diff options
author | leng@chromium.org <leng@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-07-13 09:21:34 +0000 |
---|---|---|
committer | leng@chromium.org <leng@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-07-13 09:21:34 +0000 |
commit | 73b3242f5b0c21820e41c6f7811a6436cd161d41 (patch) | |
tree | 875ff3b5fd4cd29d07192bb0cebeb0054bdb60d0 /base/test | |
parent | 6add19457a1cbc140992ed31eec110945a5255fa (diff) | |
download | chromium_src-73b3242f5b0c21820e41c6f7811a6436cd161d41.zip chromium_src-73b3242f5b0c21820e41c6f7811a6436cd161d41.tar.gz chromium_src-73b3242f5b0c21820e41c6f7811a6436cd161d41.tar.bz2 |
Make it possible to run gtests on iOS.
Adds MainHook object to run_all_unittests main() and run_all_perftest main(), so tests run without beng killed by the OS. (If an application does not check in within a certain amount of time after launch, it will be killed.) The object is empty for non-iOS.
Adds test_listener_ios to test_suite, which registers to be notified on test end. At test end, it runs the run loop to prevent the test run from being killed by the OS.
Disables two tests for iOS: BooleanOperators and StringPattern.
BUG=b/6753976
TEST=None
Review URL: https://chromiumcodereview.appspot.com/10690161
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@146542 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/test')
-rw-r--r-- | base/test/main_hook.cc | 9 | ||||
-rw-r--r-- | base/test/main_hook.h | 17 | ||||
-rw-r--r-- | base/test/main_hook_ios.mm | 108 | ||||
-rw-r--r-- | base/test/run_all_perftests.cc | 4 | ||||
-rw-r--r-- | base/test/run_all_unittests.cc | 4 | ||||
-rw-r--r-- | base/test/test_listener_ios.h | 17 | ||||
-rw-r--r-- | base/test/test_listener_ios.mm | 45 | ||||
-rw-r--r-- | base/test/test_suite.cc | 9 |
8 files changed, 209 insertions, 4 deletions
diff --git a/base/test/main_hook.cc b/base/test/main_hook.cc new file mode 100644 index 0000000..f1f1961 --- /dev/null +++ b/base/test/main_hook.cc @@ -0,0 +1,9 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/test/main_hook.h" + +#if !defined(OS_IOS) +MainHook::MainHook(MainType main_func, int argc, char* argv[]) {} +#endif diff --git a/base/test/main_hook.h b/base/test/main_hook.h new file mode 100644 index 0000000..46b71ce --- /dev/null +++ b/base/test/main_hook.h @@ -0,0 +1,17 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "build/build_config.h" +#include "base/basictypes.h" + +// Provides a way of running code before gtest-based tests with access to +// argv and argc. +class MainHook { + public: + typedef int (*MainType)(int, char*[]); + MainHook(MainType main_func, int argc, char* argv[]); + + private: + DISALLOW_COPY_AND_ASSIGN(MainHook); +}; diff --git a/base/test/main_hook_ios.mm b/base/test/main_hook_ios.mm new file mode 100644 index 0000000..6d7bc44 --- /dev/null +++ b/base/test/main_hook_ios.mm @@ -0,0 +1,108 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/test/main_hook.h" + +#import <UIKit/UIKit.h> + +#include "base/debug/debugger.h" +#include "base/logging.h" +#include "base/mac/scoped_nsautorelease_pool.h" +#include "base/memory/scoped_nsobject.h" + +// Springboard will kill any iOS app that fails to check in after launch within +// a given time. These two classes prevent this from happening. + +// MainHook saves the chrome main() and calls UIApplicationMain(), +// providing an application delegate class: ChromeUnitTestDelegate. The delegate +// listens for UIApplicationDidFinishLaunchingNotification. When the +// notification is received, it fires main() again to have the real work done. + +// Example usage: +// int main(int argc, char** argv) { +// MainHook hook(main, argc, argv); +// // Testing code goes here. There should be no code above MainHook. If +// // there is, it will be run twice. +// } + +// 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 MainHook::MainType g_main_func = NULL; +static int g_argc; +static char** g_argv; + +@interface UIApplication (Testing) +- (void) _terminateWithStatus:(int)status; +@end + +@interface ChromeUnitTestDelegate : NSObject { +@private + scoped_nsobject<UIWindow> window_; +} +- (void)runTests; +@end + +@implementation ChromeUnitTestDelegate + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + + CGRect bounds = [[UIScreen mainScreen] bounds]; + + // Yes, this is leaked, it's just to make what's running visible. + window_.reset([[UIWindow alloc] initWithFrame:bounds]); + [window_ makeKeyAndVisible]; + + // Add a label with the app name. + UILabel* label = [[[UILabel alloc] initWithFrame:bounds] autorelease]; + label.text = [[NSProcessInfo processInfo] processName]; + label.textAlignment = UITextAlignmentCenter; + [window_ addSubview:label]; + + // Queue up the test run. + [self performSelector:@selector(runTests) + withObject:nil + afterDelay:0.1]; + return YES; +} + +- (void)runTests { + int exitStatus = g_main_func(g_argc, g_argv); + + // 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(ios): 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]]; + window_.reset(); + + // 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 + +#pragma mark - + +MainHook::MainHook(MainType main_func, int argc, char* argv[]) { + static bool ran_hook = false; + if (!ran_hook) { + ran_hook = true; + + g_main_func = main_func; + g_argc = argc; + g_argv = argv; + + base::mac::ScopedNSAutoreleasePool pool; + int exit_status = UIApplicationMain(argc, argv, nil, + @"ChromeUnitTestDelegate"); + exit(exit_status); + } +} diff --git a/base/test/run_all_perftests.cc b/base/test/run_all_perftests.cc index 2b4c628..6d9817c 100644 --- a/base/test/run_all_perftests.cc +++ b/base/test/run_all_perftests.cc @@ -1,9 +1,11 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/test/main_hook.h" #include "base/test/perf_test_suite.h" int main(int argc, char** argv) { + MainHook hook(main, argc, argv); return base::PerfTestSuite(argc, argv).Run(); } diff --git a/base/test/run_all_unittests.cc b/base/test/run_all_unittests.cc index 8bfeb3b..969b091 100644 --- a/base/test/run_all_unittests.cc +++ b/base/test/run_all_unittests.cc @@ -1,9 +1,11 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/test/main_hook.h" #include "base/test/test_suite.h" int main(int argc, char** argv) { + MainHook hook(main, argc, argv); return base::TestSuite(argc, argv).Run(); } diff --git a/base/test/test_listener_ios.h b/base/test/test_listener_ios.h new file mode 100644 index 0000000..c312250 --- /dev/null +++ b/base/test/test_listener_ios.h @@ -0,0 +1,17 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_TEST_TEST_LISTENER_IOS_H_ +#define BASE_TEST_TEST_LISTENER_IOS_H_ + +namespace base { +namespace test_listener_ios { + +// Register an IOSRunLoopListener. +void RegisterTestEndListener(); + +} // namespace test_listener_ios +} // namespace base + +#endif // BASE_TEST_TEST_LISTENER_IOS_H_ diff --git a/base/test/test_listener_ios.mm b/base/test/test_listener_ios.mm new file mode 100644 index 0000000..12cf5bb --- /dev/null +++ b/base/test/test_listener_ios.mm @@ -0,0 +1,45 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/test/test_listener_ios.h" + +#import <Foundation/Foundation.h> + +#include "base/mac/scoped_nsautorelease_pool.h" +#include "testing/gtest/include/gtest/gtest.h" + +// The iOS watchdog timer will kill an app that doesn't spin the main event +// loop often enough. This uses a Gtest TestEventListener to spin the current +// loop after each test finishes. However, if any individual test takes too +// long, it is still possible that the app will get killed. + +namespace { + +class IOSRunLoopListener : public testing::EmptyTestEventListener { + public: + virtual void OnTestEnd(const testing::TestInfo& test_info); +}; + +void IOSRunLoopListener::OnTestEnd(const testing::TestInfo& test_info) { + base::mac::ScopedNSAutoreleasePool scoped_pool; + + // At the end of the test, spin the default loop for a moment. + NSDate* stop_date = [NSDate dateWithTimeIntervalSinceNow:0.001]; + [[NSRunLoop currentRunLoop] runUntilDate:stop_date]; +} + +} // namespace + + +namespace base { +namespace test_listener_ios { + +void RegisterTestEndListener() { + testing::TestEventListeners& listeners = + testing::UnitTest::GetInstance()->listeners(); + listeners.Append(new IOSRunLoopListener); +} + +} // namespace test_listener_ios +} // namespace base diff --git a/base/test/test_suite.cc b/base/test/test_suite.cc index 0cca8fa..05a6069b 100644 --- a/base/test/test_suite.cc +++ b/base/test/test_suite.cc @@ -25,9 +25,11 @@ #if defined(OS_MACOSX) #include "base/mac/scoped_nsautorelease_pool.h" -#if !defined(OS_IOS) +#if defined(OS_IOS) +#include "base/test/test_listener_ios.h" +#else #include "base/test/mock_chrome_application_mac.h" -#endif // !OS_IOS +#endif // OS_IOS #endif // OS_MACOSX #if defined(OS_ANDROID) @@ -234,6 +236,9 @@ int TestSuite::Run() { // Check to see if we are being run as a client process. if (!client_func.empty()) return multi_process_function_list::InvokeChildProcessTest(client_func); +#if defined(OS_IOS) + base::test_listener_ios::RegisterTestEndListener(); +#endif int result = RUN_ALL_TESTS(); // If there are failed tests, see if we should ignore the failures. |