xref: /aosp_15_r20/external/webrtc/test/ios/test_support.mm (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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