1*d9f75844SAndroid Build Coastguard Worker/* 2*d9f75844SAndroid Build Coastguard Worker * Copyright 2017 The WebRTC Project Authors. All rights reserved. 3*d9f75844SAndroid Build Coastguard Worker * 4*d9f75844SAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license 5*d9f75844SAndroid Build Coastguard Worker * that can be found in the LICENSE file in the root of the source 6*d9f75844SAndroid Build Coastguard Worker * tree. An additional intellectual property rights grant can be found 7*d9f75844SAndroid Build Coastguard Worker * in the file PATENTS. All contributing project authors may 8*d9f75844SAndroid Build Coastguard Worker * be found in the AUTHORS file in the root of the source tree. 9*d9f75844SAndroid Build Coastguard Worker */ 10*d9f75844SAndroid Build Coastguard Worker 11*d9f75844SAndroid Build Coastguard Worker#import <UIKit/UIKit.h> 12*d9f75844SAndroid Build Coastguard Worker 13*d9f75844SAndroid Build Coastguard Worker#include "api/test/metrics/chrome_perf_dashboard_metrics_exporter.h" 14*d9f75844SAndroid Build Coastguard Worker#include "api/test/metrics/global_metrics_logger_and_exporter.h" 15*d9f75844SAndroid Build Coastguard Worker#include "api/test/metrics/metrics_exporter.h" 16*d9f75844SAndroid Build Coastguard Worker#include "api/test/metrics/metrics_set_proto_file_exporter.h" 17*d9f75844SAndroid Build Coastguard Worker#include "api/test/metrics/print_result_proxy_metrics_exporter.h" 18*d9f75844SAndroid Build Coastguard Worker#include "api/test/metrics/stdout_metrics_exporter.h" 19*d9f75844SAndroid Build Coastguard Worker#include "test/ios/coverage_util_ios.h" 20*d9f75844SAndroid Build Coastguard Worker#include "test/ios/google_test_runner_delegate.h" 21*d9f75844SAndroid Build Coastguard Worker#include "test/ios/test_support.h" 22*d9f75844SAndroid Build Coastguard Worker#include "test/testsupport/perf_test.h" 23*d9f75844SAndroid Build Coastguard Worker 24*d9f75844SAndroid Build Coastguard Worker#import "sdk/objc/helpers/NSString+StdString.h" 25*d9f75844SAndroid Build Coastguard Worker 26*d9f75844SAndroid Build Coastguard Worker// Springboard will kill any iOS app that fails to check in after launch within 27*d9f75844SAndroid Build Coastguard Worker// a given time. Starting a UIApplication before invoking TestSuite::Run 28*d9f75844SAndroid Build Coastguard Worker// prevents this from happening. 29*d9f75844SAndroid Build Coastguard Worker 30*d9f75844SAndroid Build Coastguard Worker// InitIOSRunHook saves the TestSuite and argc/argv, then invoking 31*d9f75844SAndroid Build Coastguard Worker// RunTestsFromIOSApp calls UIApplicationMain(), providing an application 32*d9f75844SAndroid Build Coastguard Worker// delegate class: WebRtcUnitTestDelegate. The delegate implements 33*d9f75844SAndroid Build Coastguard Worker// application:didFinishLaunchingWithOptions: to invoke the TestSuite's Run 34*d9f75844SAndroid Build Coastguard Worker// method. 35*d9f75844SAndroid Build Coastguard Worker 36*d9f75844SAndroid Build Coastguard Worker// Since the executable isn't likely to be a real iOS UI, the delegate puts up a 37*d9f75844SAndroid Build Coastguard Worker// window displaying the app name. If a bunch of apps using MainHook are being 38*d9f75844SAndroid Build Coastguard Worker// run in a row, this provides an indication of which one is currently running. 39*d9f75844SAndroid Build Coastguard Worker 40*d9f75844SAndroid Build Coastguard Worker// If enabled, runs unittests using the XCTest test runner. 41*d9f75844SAndroid Build Coastguard Workerconst char kEnableRunIOSUnittestsWithXCTest[] = "enable-run-ios-unittests-with-xctest"; 42*d9f75844SAndroid Build Coastguard Worker 43*d9f75844SAndroid Build Coastguard Workerstatic int (*g_test_suite)(void) = NULL; 44*d9f75844SAndroid Build Coastguard Workerstatic int g_argc; 45*d9f75844SAndroid Build Coastguard Workerstatic char **g_argv; 46*d9f75844SAndroid Build Coastguard Workerstatic bool g_write_perf_output; 47*d9f75844SAndroid Build Coastguard Workerstatic bool g_export_perf_results_new_api; 48*d9f75844SAndroid Build Coastguard Workerstatic std::string g_webrtc_test_metrics_output_path; 49*d9f75844SAndroid Build Coastguard Workerstatic absl::optional<bool> g_is_xctest; 50*d9f75844SAndroid Build Coastguard Workerstatic absl::optional<std::vector<std::string>> g_metrics_to_plot; 51*d9f75844SAndroid Build Coastguard Worker 52*d9f75844SAndroid Build Coastguard Worker@interface UIApplication (Testing) 53*d9f75844SAndroid Build Coastguard Worker- (void)_terminateWithStatus:(int)status; 54*d9f75844SAndroid Build Coastguard Worker@end 55*d9f75844SAndroid Build Coastguard Worker 56*d9f75844SAndroid Build Coastguard Worker@interface WebRtcUnitTestDelegate : NSObject <GoogleTestRunnerDelegate> { 57*d9f75844SAndroid Build Coastguard Worker UIWindow *_window; 58*d9f75844SAndroid Build Coastguard Worker} 59*d9f75844SAndroid Build Coastguard Worker- (void)runTests; 60*d9f75844SAndroid Build Coastguard Worker@end 61*d9f75844SAndroid Build Coastguard Worker 62*d9f75844SAndroid Build Coastguard Worker@implementation WebRtcUnitTestDelegate 63*d9f75844SAndroid Build Coastguard Worker 64*d9f75844SAndroid Build Coastguard Worker- (BOOL)application:(UIApplication *)application 65*d9f75844SAndroid Build Coastguard Worker didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 66*d9f75844SAndroid Build Coastguard Worker CGRect bounds = [[UIScreen mainScreen] bounds]; 67*d9f75844SAndroid Build Coastguard Worker 68*d9f75844SAndroid Build Coastguard Worker _window = [[UIWindow alloc] initWithFrame:bounds]; 69*d9f75844SAndroid Build Coastguard Worker [_window setBackgroundColor:[UIColor whiteColor]]; 70*d9f75844SAndroid Build Coastguard Worker [_window makeKeyAndVisible]; 71*d9f75844SAndroid Build Coastguard Worker 72*d9f75844SAndroid Build Coastguard Worker // Add a label with the app name. 73*d9f75844SAndroid Build Coastguard Worker UILabel *label = [[UILabel alloc] initWithFrame:bounds]; 74*d9f75844SAndroid Build Coastguard Worker label.text = [[NSProcessInfo processInfo] processName]; 75*d9f75844SAndroid Build Coastguard Worker label.textAlignment = NSTextAlignmentCenter; 76*d9f75844SAndroid Build Coastguard Worker [_window addSubview:label]; 77*d9f75844SAndroid Build Coastguard Worker 78*d9f75844SAndroid Build Coastguard Worker // An NSInternalInconsistencyException is thrown if the app doesn't have a 79*d9f75844SAndroid Build Coastguard Worker // root view controller. Set an empty one here. 80*d9f75844SAndroid Build Coastguard Worker [_window setRootViewController:[[UIViewController alloc] init]]; 81*d9f75844SAndroid Build Coastguard Worker 82*d9f75844SAndroid Build Coastguard Worker if (!rtc::test::ShouldRunIOSUnittestsWithXCTest()) { 83*d9f75844SAndroid Build Coastguard Worker // When running in XCTest mode, XCTest will invoke `runGoogleTest` directly. 84*d9f75844SAndroid Build Coastguard Worker // Otherwise, schedule a call to `runTests`. 85*d9f75844SAndroid Build Coastguard Worker [self performSelector:@selector(runTests) withObject:nil afterDelay:0.1]; 86*d9f75844SAndroid Build Coastguard Worker } 87*d9f75844SAndroid Build Coastguard Worker 88*d9f75844SAndroid Build Coastguard Worker return YES; 89*d9f75844SAndroid Build Coastguard Worker} 90*d9f75844SAndroid Build Coastguard Worker 91*d9f75844SAndroid Build Coastguard Worker- (BOOL)supportsRunningGoogleTests { 92*d9f75844SAndroid Build Coastguard Worker return rtc::test::ShouldRunIOSUnittestsWithXCTest(); 93*d9f75844SAndroid Build Coastguard Worker} 94*d9f75844SAndroid Build Coastguard Worker 95*d9f75844SAndroid Build Coastguard Worker- (int)runGoogleTests { 96*d9f75844SAndroid Build Coastguard Worker rtc::test::ConfigureCoverageReportPath(); 97*d9f75844SAndroid Build Coastguard Worker 98*d9f75844SAndroid Build Coastguard Worker int exitStatus = g_test_suite(); 99*d9f75844SAndroid Build Coastguard Worker 100*d9f75844SAndroid Build Coastguard Worker std::vector<std::unique_ptr<webrtc::test::MetricsExporter>> exporters; 101*d9f75844SAndroid Build Coastguard Worker if (g_export_perf_results_new_api) { 102*d9f75844SAndroid Build Coastguard Worker exporters.push_back(std::make_unique<webrtc::test::StdoutMetricsExporter>()); 103*d9f75844SAndroid Build Coastguard Worker if (g_write_perf_output) { 104*d9f75844SAndroid Build Coastguard Worker // Stores data into a proto file under the app's document directory. 105*d9f75844SAndroid Build Coastguard Worker NSString *fileName = @"perftest-output.pb"; 106*d9f75844SAndroid Build Coastguard Worker NSArray<NSString *> *outputDirectories = 107*d9f75844SAndroid Build Coastguard Worker NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 108*d9f75844SAndroid Build Coastguard Worker if ([outputDirectories count] != 0) { 109*d9f75844SAndroid Build Coastguard Worker NSString *outputPath = [outputDirectories[0] stringByAppendingPathComponent:fileName]; 110*d9f75844SAndroid Build Coastguard Worker 111*d9f75844SAndroid Build Coastguard Worker exporters.push_back(std::make_unique<webrtc::test::ChromePerfDashboardMetricsExporter>( 112*d9f75844SAndroid Build Coastguard Worker [NSString stdStringForString:outputPath])); 113*d9f75844SAndroid Build Coastguard Worker } 114*d9f75844SAndroid Build Coastguard Worker } 115*d9f75844SAndroid Build Coastguard Worker if (!g_webrtc_test_metrics_output_path.empty()) { 116*d9f75844SAndroid Build Coastguard Worker exporters.push_back(std::make_unique<webrtc::test::MetricsSetProtoFileExporter>( 117*d9f75844SAndroid Build Coastguard Worker webrtc::test::MetricsSetProtoFileExporter::Options(g_webrtc_test_metrics_output_path))); 118*d9f75844SAndroid Build Coastguard Worker } 119*d9f75844SAndroid Build Coastguard Worker } else { 120*d9f75844SAndroid Build Coastguard Worker exporters.push_back(std::make_unique<webrtc::test::PrintResultProxyMetricsExporter>()); 121*d9f75844SAndroid Build Coastguard Worker } 122*d9f75844SAndroid Build Coastguard Worker webrtc::test::ExportPerfMetric(*webrtc::test::GetGlobalMetricsLogger(), std::move(exporters)); 123*d9f75844SAndroid Build Coastguard Worker if (!g_export_perf_results_new_api) { 124*d9f75844SAndroid Build Coastguard Worker if (g_write_perf_output) { 125*d9f75844SAndroid Build Coastguard Worker // Stores data into a proto file under the app's document directory. 126*d9f75844SAndroid Build Coastguard Worker NSString *fileName = @"perftest-output.pb"; 127*d9f75844SAndroid Build Coastguard Worker NSArray<NSString *> *outputDirectories = 128*d9f75844SAndroid Build Coastguard Worker NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 129*d9f75844SAndroid Build Coastguard Worker if ([outputDirectories count] != 0) { 130*d9f75844SAndroid Build Coastguard Worker NSString *outputPath = [outputDirectories[0] stringByAppendingPathComponent:fileName]; 131*d9f75844SAndroid Build Coastguard Worker 132*d9f75844SAndroid Build Coastguard Worker if (!webrtc::test::WritePerfResults([NSString stdStringForString:outputPath])) { 133*d9f75844SAndroid Build Coastguard Worker return 1; 134*d9f75844SAndroid Build Coastguard Worker } 135*d9f75844SAndroid Build Coastguard Worker } 136*d9f75844SAndroid Build Coastguard Worker } 137*d9f75844SAndroid Build Coastguard Worker if (g_metrics_to_plot) { 138*d9f75844SAndroid Build Coastguard Worker webrtc::test::PrintPlottableResults(*g_metrics_to_plot); 139*d9f75844SAndroid Build Coastguard Worker } 140*d9f75844SAndroid Build Coastguard Worker } 141*d9f75844SAndroid Build Coastguard Worker 142*d9f75844SAndroid Build Coastguard Worker return exitStatus; 143*d9f75844SAndroid Build Coastguard Worker} 144*d9f75844SAndroid Build Coastguard Worker 145*d9f75844SAndroid Build Coastguard Worker- (void)runTests { 146*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(!rtc::test::ShouldRunIOSUnittestsWithXCTest()); 147*d9f75844SAndroid Build Coastguard Worker rtc::test::ConfigureCoverageReportPath(); 148*d9f75844SAndroid Build Coastguard Worker 149*d9f75844SAndroid Build Coastguard Worker int exitStatus = [self runGoogleTests]; 150*d9f75844SAndroid Build Coastguard Worker 151*d9f75844SAndroid Build Coastguard Worker // If a test app is too fast, it will exit before Instruments has has a 152*d9f75844SAndroid Build Coastguard Worker // a chance to initialize and no test results will be seen. 153*d9f75844SAndroid Build Coastguard Worker // TODO(crbug.com/137010): Figure out how much time is actually needed, and 154*d9f75844SAndroid Build Coastguard Worker // sleep only to make sure that much time has elapsed since launch. 155*d9f75844SAndroid Build Coastguard Worker [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]]; 156*d9f75844SAndroid Build Coastguard Worker 157*d9f75844SAndroid Build Coastguard Worker // Use the hidden selector to try and cleanly take down the app (otherwise 158*d9f75844SAndroid Build Coastguard Worker // things can think the app crashed even on a zero exit status). 159*d9f75844SAndroid Build Coastguard Worker UIApplication *application = [UIApplication sharedApplication]; 160*d9f75844SAndroid Build Coastguard Worker [application _terminateWithStatus:exitStatus]; 161*d9f75844SAndroid Build Coastguard Worker 162*d9f75844SAndroid Build Coastguard Worker exit(exitStatus); 163*d9f75844SAndroid Build Coastguard Worker} 164*d9f75844SAndroid Build Coastguard Worker 165*d9f75844SAndroid Build Coastguard Worker@end 166*d9f75844SAndroid Build Coastguard Workernamespace rtc { 167*d9f75844SAndroid Build Coastguard Workernamespace test { 168*d9f75844SAndroid Build Coastguard Worker 169*d9f75844SAndroid Build Coastguard Worker// Note: This is not thread safe, and must be called from the same thread as 170*d9f75844SAndroid Build Coastguard Worker// runTests above. 171*d9f75844SAndroid Build Coastguard Workervoid InitTestSuite(int (*test_suite)(void), 172*d9f75844SAndroid Build Coastguard Worker int argc, 173*d9f75844SAndroid Build Coastguard Worker char *argv[], 174*d9f75844SAndroid Build Coastguard Worker bool write_perf_output, 175*d9f75844SAndroid Build Coastguard Worker bool export_perf_results_new_api, 176*d9f75844SAndroid Build Coastguard Worker std::string webrtc_test_metrics_output_path, 177*d9f75844SAndroid Build Coastguard Worker absl::optional<std::vector<std::string>> metrics_to_plot) { 178*d9f75844SAndroid Build Coastguard Worker g_test_suite = test_suite; 179*d9f75844SAndroid Build Coastguard Worker g_argc = argc; 180*d9f75844SAndroid Build Coastguard Worker g_argv = argv; 181*d9f75844SAndroid Build Coastguard Worker g_write_perf_output = write_perf_output; 182*d9f75844SAndroid Build Coastguard Worker g_export_perf_results_new_api = export_perf_results_new_api; 183*d9f75844SAndroid Build Coastguard Worker g_webrtc_test_metrics_output_path = webrtc_test_metrics_output_path; 184*d9f75844SAndroid Build Coastguard Worker g_metrics_to_plot = std::move(metrics_to_plot); 185*d9f75844SAndroid Build Coastguard Worker} 186*d9f75844SAndroid Build Coastguard Worker 187*d9f75844SAndroid Build Coastguard Workervoid RunTestsFromIOSApp() { 188*d9f75844SAndroid Build Coastguard Worker @autoreleasepool { 189*d9f75844SAndroid Build Coastguard Worker exit(UIApplicationMain(g_argc, g_argv, nil, @"WebRtcUnitTestDelegate")); 190*d9f75844SAndroid Build Coastguard Worker } 191*d9f75844SAndroid Build Coastguard Worker} 192*d9f75844SAndroid Build Coastguard Worker 193*d9f75844SAndroid Build Coastguard Workerbool ShouldRunIOSUnittestsWithXCTest() { 194*d9f75844SAndroid Build Coastguard Worker if (g_is_xctest.has_value()) { 195*d9f75844SAndroid Build Coastguard Worker return g_is_xctest.value(); 196*d9f75844SAndroid Build Coastguard Worker } 197*d9f75844SAndroid Build Coastguard Worker 198*d9f75844SAndroid Build Coastguard Worker char **argv = g_argv; 199*d9f75844SAndroid Build Coastguard Worker while (*argv != nullptr) { 200*d9f75844SAndroid Build Coastguard Worker if (strstr(*argv, kEnableRunIOSUnittestsWithXCTest) != nullptr) { 201*d9f75844SAndroid Build Coastguard Worker g_is_xctest = absl::optional<bool>(true); 202*d9f75844SAndroid Build Coastguard Worker return true; 203*d9f75844SAndroid Build Coastguard Worker } 204*d9f75844SAndroid Build Coastguard Worker argv++; 205*d9f75844SAndroid Build Coastguard Worker } 206*d9f75844SAndroid Build Coastguard Worker g_is_xctest = absl::optional<bool>(false); 207*d9f75844SAndroid Build Coastguard Worker return false; 208*d9f75844SAndroid Build Coastguard Worker} 209*d9f75844SAndroid Build Coastguard Worker 210*d9f75844SAndroid Build Coastguard Worker} // namespace test 211*d9f75844SAndroid Build Coastguard Worker} // namespace rtc 212