• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 *  Copyright 2017 The WebRTC Project Authors. All rights reserved.
3 *
4 *  Use of this source code is governed by a BSD-style license
5 *  that can be found in the LICENSE file in the root of the source
6 *  tree. An additional intellectual property rights grant can be found
7 *  in the file PATENTS.  All contributing project authors may
8 *  be found in the AUTHORS file in the root of the source tree.
9 */
10
11#import <UIKit/UIKit.h>
12
13#include "test/ios/coverage_util_ios.h"
14#include "test/ios/test_support.h"
15#include "test/testsupport/perf_test.h"
16
17#import "sdk/objc/helpers/NSString+StdString.h"
18
19// Springboard will kill any iOS app that fails to check in after launch within
20// a given time. Starting a UIApplication before invoking TestSuite::Run
21// prevents this from happening.
22
23// InitIOSRunHook saves the TestSuite and argc/argv, then invoking
24// RunTestsFromIOSApp calls UIApplicationMain(), providing an application
25// delegate class: WebRtcUnitTestDelegate. The delegate implements
26// application:didFinishLaunchingWithOptions: to invoke the TestSuite's Run
27// method.
28
29// Since the executable isn't likely to be a real iOS UI, the delegate puts up a
30// window displaying the app name. If a bunch of apps using MainHook are being
31// run in a row, this provides an indication of which one is currently running.
32
33static int (*g_test_suite)(void) = NULL;
34static int g_argc;
35static char **g_argv;
36static bool g_write_perf_output;
37static absl::optional<std::vector<std::string>> g_metrics_to_plot;
38
39@interface UIApplication (Testing)
40- (void)_terminateWithStatus:(int)status;
41@end
42
43@interface WebRtcUnitTestDelegate : NSObject {
44  UIWindow *_window;
45}
46- (void)runTests;
47@end
48
49@implementation WebRtcUnitTestDelegate
50
51- (BOOL)application:(UIApplication *)application
52    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
53  CGRect bounds = [[UIScreen mainScreen] bounds];
54
55  _window = [[UIWindow alloc] initWithFrame:bounds];
56  [_window setBackgroundColor:[UIColor whiteColor]];
57  [_window makeKeyAndVisible];
58
59  // Add a label with the app name.
60  UILabel *label = [[UILabel alloc] initWithFrame:bounds];
61  label.text = [[NSProcessInfo processInfo] processName];
62  label.textAlignment = NSTextAlignmentCenter;
63  [_window addSubview:label];
64
65  // An NSInternalInconsistencyException is thrown if the app doesn't have a
66  // root view controller. Set an empty one here.
67  [_window setRootViewController:[[UIViewController alloc] init]];
68
69  // Queue up the test run.
70  [self performSelector:@selector(runTests) withObject:nil afterDelay:0.1];
71  return YES;
72}
73
74- (void)runTests {
75  rtc::test::ConfigureCoverageReportPath();
76
77  int exitStatus = g_test_suite();
78
79  if (g_write_perf_output) {
80    // Stores data into a proto file under the app's document directory.
81    NSString *fileName = @"perftest-output.pb";
82    NSArray<NSString*>* outputDirectories = NSSearchPathForDirectoriesInDomains(
83        NSDocumentDirectory, NSUserDomainMask, YES);
84    if ([outputDirectories count] != 0) {
85      NSString* outputPath =
86          [outputDirectories[0] stringByAppendingPathComponent:fileName];
87
88      if (!webrtc::test::WritePerfResults([NSString stdStringForString:outputPath])) {
89        exit(1);
90      }
91    }
92  }
93  if (g_metrics_to_plot) {
94    webrtc::test::PrintPlottableResults(*g_metrics_to_plot);
95  }
96
97  // If a test app is too fast, it will exit before Instruments has has a
98  // a chance to initialize and no test results will be seen.
99  // TODO(crbug.com/137010): Figure out how much time is actually needed, and
100  // sleep only to make sure that much time has elapsed since launch.
101  [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]];
102
103  // Use the hidden selector to try and cleanly take down the app (otherwise
104  // things can think the app crashed even on a zero exit status).
105  UIApplication *application = [UIApplication sharedApplication];
106  [application _terminateWithStatus:exitStatus];
107
108  exit(exitStatus);
109}
110
111@end
112namespace rtc {
113namespace test {
114
115// Note: This is not thread safe, and must be called from the same thread as
116// runTests above.
117void InitTestSuite(int (*test_suite)(void),
118                   int argc,
119                   char *argv[],
120                   bool write_perf_output,
121                   absl::optional<std::vector<std::string>> metrics_to_plot) {
122  g_test_suite = test_suite;
123  g_argc = argc;
124  g_argv = argv;
125  g_write_perf_output = write_perf_output;
126  g_metrics_to_plot = std::move(metrics_to_plot);
127}
128
129void RunTestsFromIOSApp() {
130  @autoreleasepool {
131    exit(UIApplicationMain(g_argc, g_argv, nil, @"WebRtcUnitTestDelegate"));
132  }
133}
134}  // namespace test
135}  // namespace rtc
136