1//
2//  GTMGoogleTestRunner.mm
3//
4//  Copyright 2013 Google Inc.
5//
6//  Licensed under the Apache License, Version 2.0 (the "License"); you may not
7//  use this file except in compliance with the License.  You may obtain a copy
8//  of the License at
9//
10//  http://www.apache.org/licenses/LICENSE-2.0
11//
12//  Unless required by applicable law or agreed to in writing, software
13//  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14//  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
15//  License for the specific language governing permissions and limitations under
16//  the License.
17//
18
19#if !defined(__has_feature) || !__has_feature(objc_arc)
20#error "This file requires ARC support."
21#endif
22
23// This is a XCTest based unit test that will run all of the GoogleTest
24// https://code.google.com/p/googletest/
25// based tests in the project, and will report results correctly via XCTest so
26// that Xcode can pick them up in it's UI.
27
28// XCTest dynamically creates one XCTest per GoogleTest.
29// GoogleTest is set up using a custom event listener (GoogleTestPrinter)
30// which knows how to log GoogleTest test results in a manner that XCTest (and
31// the Xcode IDE) understand.
32
33// Note that this does not able you to control individual tests from the Xcode
34// UI. You can only turn on/off all of the C++ tests. It does however give
35// you output that you can click on in the Xcode UI and immediately jump to a
36// test failure.
37
38// This class is not compiled as part of the standard Google Toolbox For Mac
39// project because of it's dependency on https://code.google.com/p/googletest/
40
41// To use this:
42// - Add GTMGoogleTestRunner to your test bundle sources.
43// - Add gtest-all.cc from gtest to your test bundle sources.
44// - Write some C++ tests and add them to your test bundle sources.
45// - Build and run tests. Your C++ tests should just execute.
46
47// NOTE:
48// A key difference between how GTMGoogleTestRunner runs tests versus how a
49// "standard" unit test package runs tests is that SetUpTestSuite/SetupTestCase
50// and TeardownTestSuite/TeardownTestCase are going to be called before/after
51// *every* individual test. Unfortunately this is due to restrictions in the
52// design of GoogleTest in that the only way to run individual tests is to
53// use a filter to focus on a specific test, and then "run" all the tests
54// multiple times.
55// If you have state that you need maintained across tests (not normally a
56// great idea anyhow), using SetUp*, Teardown* is not going to work for you.
57
58#import <XCTest/XCTest.h>
59#import <objc/runtime.h>
60
61#include <gtest/gtest.h>
62
63using ::testing::EmptyTestEventListener;
64using ::testing::TestCase;
65using ::testing::TestEventListener;
66using ::testing::TestEventListeners;
67using ::testing::TestInfo;
68using ::testing::TestPartResult;
69using ::testing::TestResult;
70using ::testing::UnitTest;
71
72namespace {
73
74// A gtest printer that takes care of reporting gtest results via the
75// XCTest interface. Note that a test suite in XCTest == a test case in gtest
76// and a test case in XCTest == a test in gtest.
77// This will handle fatal and non-fatal gtests properly.
78class GoogleTestPrinter : public EmptyTestEventListener {
79 public:
80  GoogleTestPrinter(XCTestCase *test_case) : test_case_(test_case) {}
81
82  virtual ~GoogleTestPrinter() {}
83
84  virtual void OnTestPartResult(const TestPartResult &test_part_result) {
85    if (!test_part_result.passed()) {
86      const char *file_name = test_part_result.file_name();
87      NSString *file = @(file_name ? file_name : "<file name unavailable>");
88      int line = test_part_result.line_number();
89      NSString *summary = @(test_part_result.summary());
90
91      // gtest likes to give multi-line summaries. These don't look good in
92      // the Xcode UI, so we clean them up.
93      NSString *oneLineSummary =
94          [summary stringByReplacingOccurrencesOfString:@"\n" withString:@" "];
95      BOOL expected = test_part_result.nonfatally_failed();
96      [test_case_ recordFailureWithDescription:oneLineSummary
97                                        inFile:file
98                                        atLine:line
99                                      expected:expected];
100    }
101  }
102
103 private:
104  XCTestCase *test_case_;
105};
106
107NSString *SelectorNameFromGTestName(NSString *testName) {
108  NSRange dot = [testName rangeOfString:@"."];
109  return [NSString stringWithFormat:@"%@::%@",
110          [testName substringToIndex:dot.location],
111          [testName substringFromIndex:dot.location + 1]];
112}
113
114}  // namespace
115
116// GTMGoogleTestRunner is a GTMTestCase that makes a sub test suite populated
117// with all of the GoogleTest unit tests.
118@interface GTMGoogleTestRunner : XCTestCase {
119  NSString *testName_;
120}
121
122// The name for a test is the GoogleTest name which is "TestCase.Test"
123- (id)initWithName:(NSString *)testName;
124@end
125
126@implementation GTMGoogleTestRunner
127
128+ (void)initGoogleTest {
129  static dispatch_once_t onceToken;
130  dispatch_once(&onceToken, ^{
131    NSArray *arguments = [NSProcessInfo processInfo].arguments;
132    int argc = (int)arguments.count;
133    char **argv = static_cast<char **>(alloca((sizeof(char *) * (argc + 1))));
134    for (int index = 0; index < argc; index++) {
135      argv[index] = const_cast<char *> ([arguments[index] UTF8String]);
136    }
137    argv[argc] = NULL;
138
139    testing::InitGoogleTest(&argc, argv);
140  });
141}
142
143+ (id)defaultTestSuite {
144  [GTMGoogleTestRunner initGoogleTest];
145  XCTestSuite *result = [[XCTestSuite alloc] initWithName:NSStringFromClass(self)];
146  UnitTest *test = UnitTest::GetInstance();
147
148  // Walk the GoogleTest tests, adding sub tests and sub suites as appropriate.
149  int total_test_case_count = test->total_test_case_count();
150  for (int i = 0; i < total_test_case_count; ++i) {
151    const TestCase *test_case = test->GetTestCase(i);
152    int total_test_count = test_case->total_test_count();
153    XCTestSuite *subSuite = [[XCTestSuite alloc] initWithName:@(test_case->name())];
154    [result addTest:subSuite];
155    for (int j = 0; j < total_test_count; ++j) {
156      const TestInfo *test_info = test_case->GetTestInfo(j);
157      NSString *testName = [NSString stringWithFormat:@"%s.%s",
158                            test_case->name(), test_info->name()];
159      XCTestCase *xcTest = [[self alloc] initWithName:testName];
160      [subSuite addTest:xcTest];
161    }
162  }
163  return result;
164}
165
166- (id)initWithName:(NSString *)testName {
167  // Xcode 6.1 started taking the testName from the selector instead of calling
168  // -name.
169  // So we will add selectors to GTMGoogleTestRunner.
170  // They should all be unique because the selectors are named cppclass.method
171  // Filed as radar 18798444.
172  Class cls = [self class];
173  NSString *selectorTestName = SelectorNameFromGTestName(testName);
174  SEL selector = sel_registerName([selectorTestName UTF8String]);
175  Method method = class_getInstanceMethod(cls, @selector(runGoogleTest));
176  IMP implementation = method_getImplementation(method);
177  const char *encoding = method_getTypeEncoding(method);
178  if (!class_addMethod(cls, selector, implementation, encoding)) {
179    // If we can't add a method, we should blow up here.
180    [NSException raise:NSInternalInconsistencyException
181                format:@"Unable to add %@ to %@.", testName, cls];
182  }
183  if ((self = [super initWithSelector:selector])) {
184    testName_ = testName;
185  }
186  return self;
187}
188
189- (NSString *)name {
190  // An XCTest name must be "-[foo bar]" or it won't be parsed properly.
191  NSRange dot = [testName_ rangeOfString:@"."];
192  return [NSString stringWithFormat:@"-[%@ %@]",
193          [testName_ substringToIndex:dot.location],
194          [testName_ substringFromIndex:dot.location + 1]];
195}
196
197- (void)runGoogleTest {
198  [GTMGoogleTestRunner initGoogleTest];
199
200  // Gets hold of the event listener list.
201  TestEventListeners& listeners = UnitTest::GetInstance()->listeners();
202
203  // Adds a listener to the end.
204  GoogleTestPrinter printer = GoogleTestPrinter(self);
205  listeners.Append(&printer);
206
207  // Remove the default printer if it exists.
208  TestEventListener *defaultListener = listeners.default_result_printer();
209  if (defaultListener) {
210    delete listeners.Release(defaultListener);
211  }
212
213  // Since there is no way of running a single GoogleTest directly, we use the
214  // filter mechanism in GoogleTest to simulate it for us.
215  ::testing::GTEST_FLAG(filter) = [testName_ UTF8String];
216
217  // Intentionally ignore return value of RUN_ALL_TESTS. We will be printing
218  // the output appropriately, and there is no reason to mark this test as
219  // "failed" if RUN_ALL_TESTS returns non-zero.
220  (void)RUN_ALL_TESTS();
221
222  // Remove the listener that we added.
223  listeners.Release(&printer);
224}
225
226@end
227