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