From c4c7777e75bfc84ba144f13014f312230a0cc7ed Mon Sep 17 00:00:00 2001 From: Gil Date: Wed, 8 Nov 2017 14:46:50 -0800 Subject: Run GoogleTest-based C++ tests in Xcode (#420) * Use GoogleTest as a dependency of Firestore_Tests * Remove top-level leveldb-library from HEADER_SEARCH_PATHS * Add string_util_test to the project and get it to build * Implement FSTGoogleTestTests, a bridge between GoogleTest and XCTest --- .../Example/Firestore.xcodeproj/project.pbxproj | 28 +- Firestore/Example/Podfile | 1 + .../Example/Tests/GoogleTest/FSTGoogleTestTests.mm | 358 +++++++++++++++++++++ .../Example/Tests/GoogleTest/GoogleTest.podspec | 84 +++++ Firestore/Port/string_util_test.cc | 7 +- 5 files changed, 473 insertions(+), 5 deletions(-) create mode 100644 Firestore/Example/Tests/GoogleTest/FSTGoogleTestTests.mm create mode 100644 Firestore/Example/Tests/GoogleTest/GoogleTest.podspec diff --git a/Firestore/Example/Firestore.xcodeproj/project.pbxproj b/Firestore/Example/Firestore.xcodeproj/project.pbxproj index 0197deb..50da99b 100644 --- a/Firestore/Example/Firestore.xcodeproj/project.pbxproj +++ b/Firestore/Example/Firestore.xcodeproj/project.pbxproj @@ -24,6 +24,8 @@ /* Begin PBXBuildFile section */ 3B843E4C1F3A182900548890 /* remote_store_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 3B843E4A1F3930A400548890 /* remote_store_spec_test.json */; }; + 54764FAB1FAA0C320085E60A /* string_util_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54764FAA1FAA0C320085E60A /* string_util_test.cc */; }; + 54764FAF1FAA21B90085E60A /* FSTGoogleTestTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 54764FAE1FAA21B90085E60A /* FSTGoogleTestTests.mm */; }; 54DA12A61F315EE100DD57A1 /* collection_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA129C1F315EE100DD57A1 /* collection_spec_test.json */; }; 54DA12A71F315EE100DD57A1 /* existence_filter_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA129D1F315EE100DD57A1 /* existence_filter_spec_test.json */; }; 54DA12A81F315EE100DD57A1 /* limbo_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA129E1F315EE100DD57A1 /* limbo_spec_test.json */; }; @@ -181,6 +183,8 @@ 3B843E4A1F3930A400548890 /* remote_store_spec_test.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = remote_store_spec_test.json; sourceTree = ""; }; 42491D7DC8C8CD245CC22B93 /* Pods-SwiftBuildTest.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwiftBuildTest.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SwiftBuildTest/Pods-SwiftBuildTest.debug.xcconfig"; sourceTree = ""; }; 4EBC5F5ABE1FD097EFE5E224 /* Pods-Firestore_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example/Pods-Firestore_Example.release.xcconfig"; sourceTree = ""; }; + 54764FAA1FAA0C320085E60A /* string_util_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = string_util_test.cc; path = ../../Port/string_util_test.cc; sourceTree = ""; }; + 54764FAE1FAA21B90085E60A /* FSTGoogleTestTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = FSTGoogleTestTests.mm; path = GoogleTest/FSTGoogleTestTests.mm; sourceTree = ""; }; 54DA129C1F315EE100DD57A1 /* collection_spec_test.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = collection_spec_test.json; sourceTree = ""; }; 54DA129D1F315EE100DD57A1 /* existence_filter_spec_test.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = existence_filter_spec_test.json; sourceTree = ""; }; 54DA129E1F315EE100DD57A1 /* limbo_spec_test.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = limbo_spec_test.json; sourceTree = ""; }; @@ -360,6 +364,23 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 54764FAC1FAA0C390085E60A /* GoogleTests */ = { + isa = PBXGroup; + children = ( + 54764FAE1FAA21B90085E60A /* FSTGoogleTestTests.mm */, + 54764FAD1FAA0C650085E60A /* Port */, + ); + name = GoogleTests; + sourceTree = ""; + }; + 54764FAD1FAA0C650085E60A /* Port */ = { + isa = PBXGroup; + children = ( + 54764FAA1FAA0C320085E60A /* string_util_test.cc */, + ); + name = Port; + sourceTree = ""; + }; 6003F581195388D10070C39A = { isa = PBXGroup; children = ( @@ -430,6 +451,7 @@ children = ( DE51B1831F0D48AC0013853F /* API */, DE51B1A81F0D48AC0013853F /* Core */, + 54764FAC1FAA0C390085E60A /* GoogleTests */, DE2EF06E1F3D07D7003D0CDC /* Immutable */, DE51B1BB1F0D48AC0013853F /* Integration */, DE51B1621F0D48AC0013853F /* Local */, @@ -1012,11 +1034,13 @@ inputPaths = ( "${SRCROOT}/Pods/Target Support Files/Pods-Firestore_Tests/Pods-Firestore_Tests-frameworks.sh", "${BUILT_PRODUCTS_DIR}/leveldb-library/leveldb.framework", + "${BUILT_PRODUCTS_DIR}/GoogleTest/GoogleTest.framework", "${BUILT_PRODUCTS_DIR}/OCMock/OCMock.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/leveldb.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleTest.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OCMock.framework", ); runOnlyForDeploymentPostprocessing = 0; @@ -1159,6 +1183,7 @@ DE51B1F11F0D49140013853F /* FSTMutationTests.m in Sources */, DE51B1FB1F0D492C0013853F /* FSTMemorySpecTests.m in Sources */, DE51B1DB1F0D490D0013853F /* FSTLevelDBQueryCacheTests.m in Sources */, + 54764FAB1FAA0C320085E60A /* string_util_test.cc in Sources */, 54E9282C1F339CAD00C1953E /* XCTestCase+Await.m in Sources */, DE51B1DF1F0D490D0013853F /* FSTMemoryMutationQueueTests.m in Sources */, DE51B1F31F0D491B0013853F /* FSTDatastoreTests.m in Sources */, @@ -1166,6 +1191,7 @@ DE2EF0871F3D0B6E003D0CDC /* FSTImmutableSortedSet+Testing.m in Sources */, DE51B1E01F0D490D0013853F /* FSTMemoryQueryCacheTests.m in Sources */, DE51B1E91F0D490D0013853F /* FSTLevelDBMutationQueueTests.mm in Sources */, + 54764FAF1FAA21B90085E60A /* FSTGoogleTestTests.mm in Sources */, DE51B1E61F0D490D0013853F /* FSTRemoteDocumentCacheTests.m in Sources */, DE51B1D91F0D490D0013853F /* FSTEagerGarbageCollectorTests.m in Sources */, DE51B1E21F0D490D0013853F /* FSTMutationQueueTests.m in Sources */, @@ -1359,7 +1385,6 @@ HEADER_SEARCH_PATHS = ( "$(inherited)", "\"${PODS_ROOT}/Firebase/Firebase/Firebase\"", - "\"${PODS_ROOT}/leveldb-library/\"", "\"${PODS_ROOT}/leveldb-library/include\"", ); INFOPLIST_FILE = "Firestore/Firestore-Info.plist"; @@ -1380,7 +1405,6 @@ HEADER_SEARCH_PATHS = ( "$(inherited)", "\"${PODS_ROOT}/Firebase/Firebase/Firebase\"", - "\"${PODS_ROOT}/leveldb-library/\"", "\"${PODS_ROOT}/leveldb-library/include\"", ); INFOPLIST_FILE = "Firestore/Firestore-Info.plist"; diff --git a/Firestore/Example/Podfile b/Firestore/Example/Podfile index 4008af7..c79495d 100644 --- a/Firestore/Example/Podfile +++ b/Firestore/Example/Podfile @@ -8,6 +8,7 @@ target 'Firestore_Example' do target 'Firestore_Tests' do inherit! :search_paths pod 'OCMock' + pod 'GoogleTest', :podspec => 'Tests/GoogleTest/GoogleTest.podspec' pod 'leveldb-library' end diff --git a/Firestore/Example/Tests/GoogleTest/FSTGoogleTestTests.mm b/Firestore/Example/Tests/GoogleTest/FSTGoogleTestTests.mm new file mode 100644 index 0000000..731e698 --- /dev/null +++ b/Firestore/Example/Tests/GoogleTest/FSTGoogleTestTests.mm @@ -0,0 +1,358 @@ +/* + * Copyright 2017 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import +#import + +#include "Util/FSTAssert.h" +#include "gtest/gtest.h" + +/** + * An XCTest test case that finds C++ test cases written in the GoogleTest framework, runs them, and + * reports the results back to Xcode. This allows tests written in C++ that don't rely on XCTest to + * coexist in this project. + * + * As an extra feature, you can run all C++ tests by focusing on the GoogleTests class. + * + * Each GoogleTest TestCase is mapped to a dynamically generated XCTestCase class. Each GoogleTest + * TEST() is mapped to a test method on that XCTestCase. + */ +@interface GoogleTests : XCTestCase +@end + +namespace { + +// A testing::TestCase named "Foo" corresponds to an XCTestCase named "FooTests". +NSString *const kTestCaseSuffix = @"Tests"; + +// A testing::TestInfo named "Foo" corresponds to test method named "testFoo". +NSString *const kTestMethodPrefix = @"test"; + +// A map of keys created by TestInfoKey to the corresponding testing::TestInfo (wrapped in an +// NSValue). The generated XCTestCase classes are discovered and instantiated by XCTest so this is +// the only means of plumbing per-test-method state into these methods. +NSDictionary *testInfosByKey; + +// If the user focuses on GoogleTests itself, this means force all C++ tests to run. +BOOL forceAllTests = NO; + +/** + * Loads this XCTest runner's configuration file and figures out which tests to run based on the + * contents of that configuration file. + * + * @return the set of tests to run, or nil if the user asked for all tests or if there's any + * problem loading or parsing the configuration. + */ +NSSet *_Nullable LoadXCTestConfigurationTestsToRun() { + // Xcode invokes the test runner with an XCTestConfigurationFilePath environment variable set to + // the path of a configuration file containing, among other things, the set of tests to run. The + // configuration file deserializes to a non-public XCTestConfiguration class. + // + // This loads that file and then reflectively pulls out the testsToRun set. Just in case any of + // these private details should change in the future and something should fail here, the mechanism + // complains but fails open. This way the worst that can happen is that users end up running more + // tests than they intend, but we never accidentally show a green run that wasn't. + static NSString *const configEnvVar = @"XCTestConfigurationFilePath"; + + NSDictionary *env = [[NSProcessInfo processInfo] environment]; + NSString *filePath = [env objectForKey:configEnvVar]; + if (!filePath) { + NSLog(@"Missing %@ environment variable; assuming all tests", configEnvVar); + return nil; + } + + id config = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath]; + if (!config) { + NSLog(@"Failed to load any configuaration from %@=%@", configEnvVar, filePath); + return nil; + } + + SEL testsToRunSelector = NSSelectorFromString(@"testsToRun"); + if (![config respondsToSelector:testsToRunSelector]) { + NSLog(@"Invalid configuaration from %@=%@: missing testsToRun", configEnvVar, filePath); + return nil; + } + + // Invoke the testsToRun selector safely. This indirection is required because just calling + // -performSelector: fails to properly retain the NSSet under ARC. + typedef NSSet *(*TestsToRunFunction)(id, SEL); + IMP testsToRunMethod = [config methodForSelector:testsToRunSelector]; + auto testsToRunFunction = reinterpret_cast(testsToRunMethod); + return testsToRunFunction(config, testsToRunSelector); +} + +/** + * Creates a GoogleTest filter specification, suitable for passing to the --gtest_filter flag, + * that limits GoogleTest to running the same set of tests that Xcode requested. + * + * Each member of the testsToRun set is mapped as follows: + * + * * Bare class: "ClassTests" => "Class.*" + * * Class and method: "ClassTests/testMethod" => "Class.Method" + * + * These members are then joined with a ":" as googletest requires. + * + * @see https://github.com/google/googletest/blob/master/googletest/docs/AdvancedGuide.md + */ +NSString *CreateTestFiltersFromTestsToRun(NSSet *testsToRun) { + NSMutableString *result = [[NSMutableString alloc] init]; + for (NSString *spec in testsToRun) { + NSArray *parts = [spec componentsSeparatedByString:@"/"]; + + NSString *gtestCaseName = nil; + if (parts.count > 0) { + NSString *className = parts[0]; + if ([className hasSuffix:kTestCaseSuffix]) { + gtestCaseName = [className substringToIndex:className.length - kTestCaseSuffix.length]; + } + } + + NSString *gtestMethodName = nil; + if (parts.count > 1) { + NSString *methodName = parts[1]; + if ([methodName hasPrefix:kTestMethodPrefix]) { + gtestMethodName = [methodName substringFromIndex:kTestMethodPrefix.length]; + } + } + + if (gtestCaseName) { + if (result.length > 0) { + [result appendString:@":"]; + } + [result appendString:gtestCaseName]; + [result appendString:@"."]; + [result appendString:(gtestMethodName ? gtestMethodName : @"*")]; + } + } + + return result; +} + +/** Returns the name of the selector for the test method representing this specific test. */ +NSString *SelectorNameForTestInfo(const testing::TestInfo *testInfo) { + return [NSString stringWithFormat:@"%@%s", kTestMethodPrefix, testInfo->name()]; +} + +/** Returns the name of the class representing the given testing::TestCase. */ +NSString *ClassNameForTestCase(const testing::TestCase *testCase) { + return [NSString stringWithFormat:@"%s%@", testCase->name(), kTestCaseSuffix]; +} + +/** + * Returns a key name for the testInfosByKey dictionary. Each (class, selector) pair corresponds + * to a unique GoogleTest result. + */ +NSString *TestInfoKey(Class testClass, SEL testSelector) { + return [NSString + stringWithFormat:@"%@.%@", NSStringFromClass(testClass), NSStringFromSelector(testSelector)]; +} + +/** + * Looks up the testing::TestInfo for this test method and reports on the outcome to XCTest, as if + * the test actually ran in this method. + * + * Note: this function is the implementation for each generated test method. It shouldn't be used + * directly. The parameter names of self and _cmd match up with the implicit parameters passed to + * any Objective-C method. Naming them this way here allows XCTAssert and friends to work. + */ +void ReportTestResult(XCTestCase *self, SEL _cmd) { + NSString *testInfoKey = TestInfoKey([self class], _cmd); + NSValue *holder = testInfosByKey[testInfoKey]; + auto testInfo = static_cast(holder.pointerValue); + if (!testInfo) { + return; + } + + if (!testInfo->should_run()) { + // Test was filtered out by gunit; nothing to report. + return; + } + + const testing::TestResult *result = testInfo->result(); + if (result->Passed()) { + // Let XCode know that the test ran and succeeded. + XCTAssertTrue(true); + return; + } + + // Test failed :-(. Record the failure such that XCode will navigate directly to the file:line. + int parts = result->total_part_count(); + for (int i = 0; i < parts; i++) { + const testing::TestPartResult &part = result->GetTestPartResult(i); + [self recordFailureWithDescription:@(part.message()) + inFile:@(part.file_name() ? part.file_name() : "") + atLine:(part.line_number() > 0 ? part.line_number() : 0) + expected:YES]; + } +} + +/** + * Generates a new subclass of XCTestCase for the given GoogleTest TestCase. Each TestInfo (which + * represents an indivudal test method execution) is translated into a method on the test case. + * + * @param The testing::TestCase of interest to translate. + * @param A map of TestInfoKeys to testing::TestInfos, populated by this method. + * + * @return A new Class that's a subclass of XCTestCase, that's been registered with the Objective-C + * runtime. + */ +Class CreateXCTestCaseClass(const testing::TestCase *testCase, + NSMutableDictionary *infoMap) { + NSString *testCaseName = ClassNameForTestCase(testCase); + Class testClass = objc_allocateClassPair([XCTestCase class], [testCaseName UTF8String], 0); + + // Create a method for each TestInfo. + int testInfos = testCase->total_test_count(); + for (int j = 0; j < testInfos; j++) { + const testing::TestInfo *testInfo = testCase->GetTestInfo(j); + + NSString *selectorName = SelectorNameForTestInfo(testInfo); + SEL selector = sel_registerName([selectorName UTF8String]); + + // Use the ReportTestResult function as the method implementation. The v@: indicates it is a + // void objective-C method; this must continue to match the signature of ReportTestResult. + IMP method = reinterpret_cast(ReportTestResult); + class_addMethod(testClass, selector, method, "v@:"); + + NSString *infoKey = TestInfoKey(testClass, selector); + NSValue *holder = [NSValue valueWithPointer:testInfo]; + infoMap[infoKey] = holder; + } + objc_registerClassPair(testClass); + + return testClass; +} + +/** + * Creates a test suite containing all C++ tests, used when the user starts the GoogleTests class. + * + * Note: normally XCTest finds all the XCTestCase classes that are registered with the run time + * and asks them to create suites for themselves. When a user focuses on the GoogleTests class, + * XCTest no longer does this so we have to force XCTest to see more tests than it would normally + * look at so that the indicators in the test navigator update properly. + */ +XCTestSuite *CreateAllTestsTestSuite() { + XCTestSuite *allTestsSuite = [[XCTestSuite alloc] initWithName:@"All GoogleTest Tests"]; + [allTestsSuite addTest:[XCTestSuite testSuiteForTestCaseClass:[GoogleTests class]]]; + + const testing::UnitTest *master = testing::UnitTest::GetInstance(); + + int testCases = master->total_test_case_count(); + for (int i = 0; i < testCases; i++) { + const testing::TestCase *testCase = master->GetTestCase(i); + NSString *testCaseName = ClassNameForTestCase(testCase); + Class testClass = objc_getClass([testCaseName UTF8String]); + [allTestsSuite addTest:[XCTestSuite testSuiteForTestCaseClass:testClass]]; + } + + return allTestsSuite; +} + +/** + * A pseudo-constructor that dynamically generates all the XCTestCase subclasses. + * + * For background, the Objective-C runtime and XCTest initialize things in the following order: + * + * 1. Objective-C calls +load on every class in the bundle. + * 2. C++ constructors are called. + * 3. Any NSPrincipalClass in the test bundle's Info.plist is instantiated. + * 4. Objective-C calls +initialize on every class that's referenced, lazily. + * 5. XCTest calls +defaultTestSuite on every class that's a subclass of XCTestCase that matches + * its notion of which tests to run. + * + * All stages only run on all XCTestCase classes if the user runs all tests. Otherwise: + * * When a user is focusing on a test case XCTest only calls +defaultTestSuite (and triggers + * +initialize) on that specific test. + * * When a user is focusing on a test method XCTest does not call +defaultTestSuite at all + * (+initialize still runs). + * + * This means that +initialize or +defaultTestSuite on some fixed class like GoogleTests can't be + * used to bootstrap the generated classes because these steps can be skipped if the user focuses + * on the wrong thing. NSPrincipalClass would work, but requires frobbing the Info.plist, which is + * a manual step in project configuration which is error prone. + * + * Meanwhile even though __attribute__((constructor)) is a GCC and Clang extension those are the + * only compilers we care about for Objective-C so it's not that bad. + */ +__attribute__((constructor)) void RegisterGoogleTestTests() { + NSString *masterTestCaseName = NSStringFromClass([GoogleTests class]); + + // Initialize GoogleTest but don't run the tests yet. + int argc = 1; + const char *argv[] = {[masterTestCaseName UTF8String]}; + testing::InitGoogleTest(&argc, const_cast(argv)); + + // Convert XCTest's testToRun set to the equivalent --gtest_filter flag. + // + // Note that we only set forceAllTests to YES if the user specifically focused on GoogleTests. + // This prevents XCTest double-counting test cases (and failures) when a user asks for all tests. + NSSet *allTests = [NSSet setWithObject:masterTestCaseName]; + NSSet *testsToRun = LoadXCTestConfigurationTestsToRun(); + if (testsToRun) { + if ([allTests isEqual:testsToRun]) { + forceAllTests = YES; + } else { + NSString *filters = CreateTestFiltersFromTestsToRun(testsToRun); + if (filters) { + testing::GTEST_FLAG(filter) = [filters UTF8String]; + } + } + } + + // Create XCTestCases and populate the testInfosByKey map + const testing::UnitTest *master = testing::UnitTest::GetInstance(); + NSMutableDictionary *infoMap = + [NSMutableDictionary dictionaryWithCapacity:master->total_test_count()]; + + int testCases = master->total_test_case_count(); + for (int i = 0; i < testCases; i++) { + const testing::TestCase *testCase = master->GetTestCase(i); + CreateXCTestCaseClass(testCase, infoMap); + } + testInfosByKey = infoMap; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-result" + // RUN_ALL_TESTS by default doesn't want you to ignore its result, but it's safe here. Test + // failures are already logged by GoogleTest itself (and then again by XCTest). Test failures are + // reported via -recordFailureWithDescription:inFile:atLine:expected: which then causes XCTest + // itself to fail the run. + RUN_ALL_TESTS(); +#pragma clang diagnostic pop +} + +} // namespace + +@implementation GoogleTests + ++ (XCTestSuite *)defaultTestSuite { + // Only return all tests beyond GoogleTests if the user is focusing on GoogleTests. + if (forceAllTests) { + return CreateAllTestsTestSuite(); + } else { + // just run the tests that are a part of this class + return [XCTestSuite testSuiteForTestCaseClass:[self class]]; + } +} + +- (void)testGoogleTestsActuallyRun { + // This whole mechanism is sufficiently tricky that we should verify that the build actually + // plumbed this together correctly. + const testing::UnitTest *master = testing::UnitTest::GetInstance(); + XCTAssertGreaterThan(master->total_test_case_count(), 0); +} + +@end diff --git a/Firestore/Example/Tests/GoogleTest/GoogleTest.podspec b/Firestore/Example/Tests/GoogleTest/GoogleTest.podspec new file mode 100644 index 0000000..064fc59 --- /dev/null +++ b/Firestore/Example/Tests/GoogleTest/GoogleTest.podspec @@ -0,0 +1,84 @@ +# Copyright 2017 Google +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# A Private podspec for GoogleTest. Suitable only for use inside this source +# tree. + +Pod::Spec.new do |s| + s.name = 'GoogleTest' + s.version = '1.8.0' + s.summary = 'Google Test' + + s.description = <<-DESC +Google's C++ test framework. + DESC + + s.homepage = 'https://github.com/google/googletest/' + s.license = 'BSD' + s.authors = 'Google, Inc.' + + s.source = { + :git => 'https://github.com/google/googletest.git', + :tag => 'release-' + s.version.to_s + } + + s.ios.deployment_target = '8.0' + s.requires_arc = false + + # Exclude include/gtest/internal/custom files from public headers. These + # files cause problems because they have the same basenames as other headers + # (e.g. gtest.h). We don't need them because they're effectively empty: + # they're compile-time hooks for third-party customization that we don't use. + s.public_header_files = [ + 'googletest/include/gtest/*.h', + 'googletest/include/gtest/internal/*.h' + ] + s.header_mappings_dir = 'googletest/include' + + # Internal headers accessed only by the implementation. These can't be + # mentioned in source_files because header_mappings_dir will complain about + # headers outside its directory. + s.preserve_paths = [ + 'googletest/src/*.h', + ] + + s.source_files = [ + 'googletest/src/*.cc', + 'googletest/include/gtest/*.h', + 'googletest/include/gtest/internal/*.h' + ] + + s.exclude_files = [ + # A convenience wrapper for a simple command-line build. If included in + # this build, results in duplicate symbols + 'googletest/src/gtest-all.cc', + ] + + s.library = 'c++' + + # When building this pod there are headers in googletest/src. + s.pod_target_xcconfig = { + 'HEADER_SEARCH_PATHS' => + '"${PODS_ROOT}/GoogleTest/googletest/include" "${PODS_ROOT}/GoogleTest/googletest"' + } + + s.prepare_command = <<-'CMD' + # Remove includes of files in internal/custom + sed -i.bak -e '/include.*internal\/custom/ d' \ + googletest/include/gtest/gtest-printers.h \ + googletest/include/gtest/internal/gtest-port.h \ + googletest/src/gtest-death-test.cc \ + googletest/src/gtest.cc + CMD +end diff --git a/Firestore/Port/string_util_test.cc b/Firestore/Port/string_util_test.cc index ecdfb8f..bac1de3 100644 --- a/Firestore/Port/string_util_test.cc +++ b/Firestore/Port/string_util_test.cc @@ -16,8 +16,9 @@ #include "string_util.h" -#include "testing/base/public/gunit.h" -#include +#include "leveldb/db.h" + +#include "gtest/gtest.h" using Firestore::PrefixSuccessor; using Firestore::ImmediateSuccessor; @@ -27,7 +28,7 @@ TEST(Util, PrefixSuccessor) { EXPECT_EQ(PrefixSuccessor("a"), "b"); EXPECT_EQ(PrefixSuccessor("aaAA"), "aaAB"); EXPECT_EQ(PrefixSuccessor("aaa\xff"), "aab"); - EXPECT_EQ(PrefixSuccessor(string("\x00", 1)), "\x01"); + EXPECT_EQ(PrefixSuccessor(std::string("\x00", 1)), "\x01"); EXPECT_EQ(PrefixSuccessor("az\xe0"), "az\xe1"); EXPECT_EQ(PrefixSuccessor("\xff\xff\xff"), ""); EXPECT_EQ(PrefixSuccessor(""), ""); -- cgit v1.2.3