OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "base/test/main_hook.h" | |
6 | |
7 #import <UIKit/UIKit.h> | |
8 | |
9 #include "base/debug/debugger.h" | |
10 #include "base/logging.h" | |
11 #include "base/mac/scoped_nsautorelease_pool.h" | |
12 #include "base/memory/scoped_nsobject.h" | |
13 | |
14 // Springboard will kill any iOS app that fails to check in after launch within | |
15 // a given time. These two classes prevent this from happening. | |
16 | |
17 // MainHook saves the chrome main() and calls UIApplicationMain(), | |
18 // providing an application delegate class: ChromeUnitTestDelegate. The delegate | |
19 // listens for UIApplicationDidFinishLaunchingNotification. When the | |
20 // notification is received, it fires main() again to have the real work done. | |
21 | |
22 // Example usage: | |
23 // int main(int argc, char** argv) { | |
24 // MainHook hook(main, argc, argv); | |
25 // // Testing code goes here. There should be no code above MainHook. If | |
26 // // there is, it will be run twice. | |
27 // } | |
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 | |
33 static MainHook::MainType g_main_func = NULL; | |
34 static int g_argc; | |
35 static char** g_argv; | |
36 | |
37 @interface UIApplication (Testing) | |
38 - (void) _terminateWithStatus:(int)status; | |
39 @end | |
40 | |
41 @interface ChromeUnitTestDelegate : NSObject { | |
42 @private | |
43 scoped_nsobject<UIWindow> window_; | |
44 } | |
45 - (void)runTests; | |
46 @end | |
47 | |
48 @implementation ChromeUnitTestDelegate | |
49 | |
50 - (BOOL)application:(UIApplication *)application | |
51 didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { | |
52 | |
53 CGRect bounds = [[UIScreen mainScreen] bounds]; | |
54 | |
55 // Yes, this is leaked, it's just to make what's running visible. | |
56 window_.reset([[UIWindow alloc] initWithFrame:bounds]); | |
57 [window_ makeKeyAndVisible]; | |
58 | |
59 // Add a label with the app name. | |
60 UILabel* label = [[[UILabel alloc] initWithFrame:bounds] autorelease]; | |
61 label.text = [[NSProcessInfo processInfo] processName]; | |
62 label.textAlignment = UITextAlignmentCenter; | |
63 [window_ addSubview:label]; | |
64 | |
65 // Queue up the test run. | |
66 [self performSelector:@selector(runTests) | |
67 withObject:nil | |
68 afterDelay:0.1]; | |
69 return YES; | |
70 } | |
71 | |
72 - (void)runTests { | |
73 int exitStatus = g_main_func(g_argc, g_argv); | |
74 | |
75 // If a test app is too fast, it will exit before Instruments has has a | |
76 // a chance to initialize and no test results will be seen. | |
77 // TODO(ios): crbug.com/137010 Figure out how much time is actually needed, | |
78 // and sleep only to make sure that much time has elapsed since launch. | |
79 [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]]; | |
80 window_.reset(); | |
81 | |
82 // Use the hidden selector to try and cleanly take down the app (otherwise | |
83 // things can think the app crashed even on a zero exit status). | |
84 UIApplication* application = [UIApplication sharedApplication]; | |
85 [application _terminateWithStatus:exitStatus]; | |
86 | |
87 exit(exitStatus); | |
88 } | |
89 | |
90 @end | |
91 | |
92 #pragma mark - | |
93 | |
94 MainHook::MainHook(MainType main_func, int argc, char* argv[]) { | |
95 static bool ran_hook = false; | |
96 if (!ran_hook) { | |
97 ran_hook = true; | |
98 | |
99 g_main_func = main_func; | |
100 g_argc = argc; | |
101 g_argv = argv; | |
102 | |
103 base::mac::ScopedNSAutoreleasePool pool; | |
104 int exit_status = UIApplicationMain(argc, argv, nil, | |
105 @"ChromeUnitTestDelegate"); | |
106 exit(exit_status); | |
107 } | |
108 } | |
OLD | NEW |