test: Make iOS Test Delegate Class conforms the proper protocol

Currently, the WebRtcUnitTestDelegate does not conform to the
GoogleTestRunnerDelegate protocol. So the GoogleTestRunner, designed to
run googletest based tests using XCTest, fails like this:

Test Case '-[GoogleTestRunner testRunGoogleTests]' started.
/../../base/test/ios/google_test_runner.mm:24: error: -[GoogleTestRunner testRunGoogleTests] : (([appDelegate conformsToProtocol:@protocol(GoogleTestRunnerDelegate)]) is true) failed
Test Case '-[GoogleTestRunner testRunGoogleTests]' failed (0.004 seconds).

This CL fixes this issue by implementing the GoogleTestRunnerDelegate
protocol, so googletest based tests run without error.

No-Presubmit: True
No-Try: True
Bug: webrtc:12813
Change-Id: I6564b41c3aec88a9ddf078104753ceca5e6f0ba6
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/220260
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Reviewed-by: Artem Titov <titovartem@webrtc.org>
Commit-Queue: Mirko Bonadei <mbonadei@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#34593}
diff --git a/test/BUILD.gn b/test/BUILD.gn
index d645621..c89085d 100644
--- a/test/BUILD.gn
+++ b/test/BUILD.gn
@@ -281,6 +281,7 @@
     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 0000000..f0bcfe9
--- /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 1095857..cd8d2a8 100644
--- a/test/ios/test_support.h
+++ b/test/ios/test_support.h
@@ -28,6 +28,9 @@
                    bool save_chartjson_result,
                    absl::optional<std::vector<std::string>> 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 86d2e6c..3da896f 100644
--- a/test/ios/test_support.mm
+++ b/test/ios/test_support.mm
@@ -11,6 +11,7 @@
 #import <UIKit/UIKit.h>
 
 #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<bool> g_is_xctest;
 static absl::optional<std::vector<std::string>> g_metrics_to_plot;
 
 @interface UIApplication (Testing)
 - (void)_terminateWithStatus:(int)status;
 @end
 
-@interface WebRtcUnitTestDelegate : NSObject {
+@interface WebRtcUnitTestDelegate : NSObject <GoogleTestRunnerDelegate> {
   UIWindow *_window;
 }
 - (void)runTests;
@@ -66,12 +71,20 @@
   // 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 @@
   if (g_write_perf_output) {
     // Stores data into a proto file under the app's document directory.
     NSString *fileName = @"perftest-output.pb";
-    NSArray<NSString*>* outputDirectories = NSSearchPathForDirectoriesInDomains(
-        NSDocumentDirectory, NSUserDomainMask, YES);
+    NSArray<NSString *> *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 @@
     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 @@
     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<bool>(true);
+      return true;
+    }
+    argv++;
+  }
+  g_is_xctest = absl::optional<bool>(false);
+  return false;
+}
+
 }  // namespace test
 }  // namespace rtc