| /* |
| * Copyright 2017 The WebRTC Project Authors. All rights reserved. |
| * |
| * Use of this source code is governed by a BSD-style license |
| * that can be found in the LICENSE file in the root of the source |
| * tree. An additional intellectual property rights grant can be found |
| * in the file PATENTS. All contributing project authors may |
| * be found in the AUTHORS file in the root of the source tree. |
| */ |
| |
| #import <UIKit/UIKit.h> |
| |
| #include "webrtc/test/ios/test_support.h" |
| |
| #import "webrtc/sdk/objc/Framework/Classes/Common/RTCUIApplicationStatusObserver.h" |
| |
| // Springboard will kill any iOS app that fails to check in after launch within |
| // a given time. Starting a UIApplication before invoking TestSuite::Run |
| // prevents this from happening. |
| |
| // InitIOSRunHook saves the TestSuite and argc/argv, then invoking |
| // RunTestsFromIOSApp calls UIApplicationMain(), providing an application |
| // delegate class: WebRtcUnitTestDelegate. The delegate implements |
| // application:didFinishLaunchingWithOptions: to invoke the TestSuite's Run |
| // method. |
| |
| // Since the executable isn't likely to be a real iOS UI, the delegate puts up a |
| // window displaying the app name. If a bunch of apps using MainHook are being |
| // run in a row, this provides an indication of which one is currently running. |
| |
| static int (*g_test_suite)(void) = NULL; |
| static int g_argc; |
| static char **g_argv; |
| |
| @interface UIApplication (Testing) |
| - (void)_terminateWithStatus:(int)status; |
| @end |
| |
| @interface WebRtcUnitTestDelegate : NSObject { |
| UIWindow *_window; |
| } |
| - (void)runTests; |
| @end |
| |
| @implementation WebRtcUnitTestDelegate |
| |
| - (BOOL)application:(UIApplication *)application |
| didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { |
| CGRect bounds = [[UIScreen mainScreen] bounds]; |
| |
| _window = [[UIWindow alloc] initWithFrame:bounds]; |
| [_window setBackgroundColor:[UIColor whiteColor]]; |
| [_window makeKeyAndVisible]; |
| |
| // Add a label with the app name. |
| UILabel *label = [[UILabel alloc] initWithFrame:bounds]; |
| label.text = [[NSProcessInfo processInfo] processName]; |
| label.textAlignment = NSTextAlignmentCenter; |
| [_window addSubview:label]; |
| |
| // An NSInternalInconsistencyException is thrown if the app doesn't have a |
| // root view controller. Set an empty one here. |
| [_window setRootViewController:[[UIViewController alloc] init]]; |
| |
| // We want to call `RTCUIApplicationStatusObserver sharedInstance` as early as |
| // possible in the application lifecycle to set observation properly. |
| __unused RTCUIApplicationStatusObserver *observer = |
| [RTCUIApplicationStatusObserver sharedInstance]; |
| |
| // Queue up the test run. |
| [self performSelector:@selector(runTests) withObject:nil afterDelay:0.1]; |
| return YES; |
| } |
| |
| - (void)runTests { |
| int exitStatus = g_test_suite(); |
| |
| // If a test app is too fast, it will exit before Instruments has has a |
| // a chance to initialize and no test results will be seen. |
| // TODO(crbug.com/137010): Figure out how much time is actually needed, and |
| // sleep only to make sure that much time has elapsed since launch. |
| [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]]; |
| |
| // Use the hidden selector to try and cleanly take down the app (otherwise |
| // things can think the app crashed even on a zero exit status). |
| UIApplication *application = [UIApplication sharedApplication]; |
| [application _terminateWithStatus:exitStatus]; |
| |
| exit(exitStatus); |
| } |
| |
| @end |
| namespace rtc { |
| namespace test { |
| |
| void InitTestSuite(int (*test_suite)(void), int argc, char *argv[]) { |
| g_test_suite = test_suite; |
| g_argc = argc; |
| g_argv = argv; |
| } |
| |
| void RunTestsFromIOSApp() { |
| @autoreleasepool { |
| exit(UIApplicationMain(g_argc, g_argv, nil, @"WebRtcUnitTestDelegate")); |
| } |
| } |
| } // namespace test |
| } // namespace rtc |