diff --git a/test/BUILD.gn b/test/BUILD.gn index d645621e79..c89085d542 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -281,6 +281,7 @@ if (is_ios) { sources = [ "ios/coverage_util_ios.h", "ios/coverage_util_ios.mm", + "ios/google_test_runner_delegate.h", "ios/test_support.h", "ios/test_support.mm", ] diff --git a/test/ios/google_test_runner_delegate.h b/test/ios/google_test_runner_delegate.h new file mode 100644 index 0000000000..f0bcfe98d1 --- /dev/null +++ b/test/ios/google_test_runner_delegate.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021 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. + */ + +#ifndef TEST_IOS_GOOGLE_TEST_RUNNER_DELEGATE_H_ +#define TEST_IOS_GOOGLE_TEST_RUNNER_DELEGATE_H_ + +// Copied from Chromium base/test/ios/google_test_runner_delegate.h +// The WebRTC test cannot depend on //base, but this protocol is required +// to run iOS Unittest, so it is a workaround for the dependency. +@protocol GoogleTestRunnerDelegate + +// Returns YES if this delegate supports running GoogleTests via a call to +// |runGoogleTests|. +@property(nonatomic, readonly, assign) BOOL supportsRunningGoogleTests; + +// Runs GoogleTests and returns the final exit code. +- (int)runGoogleTests; + +@end + +#endif // TEST_IOS_GOOGLE_TEST_RUNNER_DELEGATE_H_ diff --git a/test/ios/test_support.h b/test/ios/test_support.h index 10958572cf..cd8d2a8103 100644 --- a/test/ios/test_support.h +++ b/test/ios/test_support.h @@ -28,6 +28,9 @@ void InitTestSuite(int (*test_suite)(void), bool save_chartjson_result, absl::optional> metrics_to_plot); +// Returns true if unittests should be run by the XCTest runnner. +bool ShouldRunIOSUnittestsWithXCTest(); + } // namespace test } // namespace rtc diff --git a/test/ios/test_support.mm b/test/ios/test_support.mm index 86d2e6ce17..3da896fa08 100644 --- a/test/ios/test_support.mm +++ b/test/ios/test_support.mm @@ -11,6 +11,7 @@ #import #include "test/ios/coverage_util_ios.h" +#include "test/ios/google_test_runner_delegate.h" #include "test/ios/test_support.h" #include "test/testsupport/perf_test.h" @@ -30,17 +31,21 @@ // 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. +// If enabled, runs unittests using the XCTest test runner. +const char kEnableRunIOSUnittestsWithXCTest[] = "enable-run-ios-unittests-with-xctest"; + static int (*g_test_suite)(void) = NULL; static int g_argc; static char **g_argv; static bool g_write_perf_output; +static absl::optional g_is_xctest; static absl::optional> g_metrics_to_plot; @interface UIApplication (Testing) - (void)_terminateWithStatus:(int)status; @end -@interface WebRtcUnitTestDelegate : NSObject { +@interface WebRtcUnitTestDelegate : NSObject { UIWindow *_window; } - (void)runTests; @@ -66,12 +71,20 @@ static absl::optional> g_metrics_to_plot; // root view controller. Set an empty one here. [_window setRootViewController:[[UIViewController alloc] init]]; - // Queue up the test run. - [self performSelector:@selector(runTests) withObject:nil afterDelay:0.1]; + if (!rtc::test::ShouldRunIOSUnittestsWithXCTest()) { + // When running in XCTest mode, XCTest will invoke |runGoogleTest| directly. + // Otherwise, schedule a call to |runTests|. + [self performSelector:@selector(runTests) withObject:nil afterDelay:0.1]; + } + return YES; } -- (void)runTests { +- (BOOL)supportsRunningGoogleTests { + return rtc::test::ShouldRunIOSUnittestsWithXCTest(); +} + +- (int)runGoogleTests { rtc::test::ConfigureCoverageReportPath(); int exitStatus = g_test_suite(); @@ -79,14 +92,13 @@ static absl::optional> g_metrics_to_plot; if (g_write_perf_output) { // Stores data into a proto file under the app's document directory. NSString *fileName = @"perftest-output.pb"; - NSArray* outputDirectories = NSSearchPathForDirectoriesInDomains( - NSDocumentDirectory, NSUserDomainMask, YES); + NSArray *outputDirectories = + NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); if ([outputDirectories count] != 0) { - NSString* outputPath = - [outputDirectories[0] stringByAppendingPathComponent:fileName]; + NSString *outputPath = [outputDirectories[0] stringByAppendingPathComponent:fileName]; if (!webrtc::test::WritePerfResults([NSString stdStringForString:outputPath])) { - exit(1); + return 1; } } } @@ -94,6 +106,15 @@ static absl::optional> g_metrics_to_plot; webrtc::test::PrintPlottableResults(*g_metrics_to_plot); } + return exitStatus; +} + +- (void)runTests { + RTC_DCHECK(!rtc::test::ShouldRunIOSUnittestsWithXCTest()); + rtc::test::ConfigureCoverageReportPath(); + + int exitStatus = [self runGoogleTests]; + // 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 @@ -131,5 +152,23 @@ void RunTestsFromIOSApp() { exit(UIApplicationMain(g_argc, g_argv, nil, @"WebRtcUnitTestDelegate")); } } + +bool ShouldRunIOSUnittestsWithXCTest() { + if (g_is_xctest.has_value()) { + return g_is_xctest.value(); + } + + char **argv = g_argv; + while (*argv != nullptr) { + if (strstr(*argv, kEnableRunIOSUnittestsWithXCTest) != nullptr) { + g_is_xctest = absl::optional(true); + return true; + } + argv++; + } + g_is_xctest = absl::optional(false); + return false; +} + } // namespace test } // namespace rtc