From 86f429b2f0061a9f16c42f83841d36813892232b Mon Sep 17 00:00:00 2001 From: Gil Date: Thu, 9 Nov 2017 09:16:20 -0800 Subject: Make FSTIntegrationTestCase Objective-C++ (#439) * Make all FIRLogger functions extern "C" * Make FSTIntegrationTestCase Objective-C++ Keep external interface to FSTIntegrationTestCase Objective-C * Add explicit dependency on Firebase/Community/Core from Tests This fixes a linkage error on a missing _FIRSetLoggerLevel symbol --- .../Example/Firestore.xcodeproj/project.pbxproj | 28 +- Firestore/Example/Podfile | 8 +- .../Example/Tests/Util/FSTIntegrationTestCase.h | 8 + .../Example/Tests/Util/FSTIntegrationTestCase.m | 319 -------------------- .../Example/Tests/Util/FSTIntegrationTestCase.mm | 320 +++++++++++++++++++++ 5 files changed, 353 insertions(+), 330 deletions(-) delete mode 100644 Firestore/Example/Tests/Util/FSTIntegrationTestCase.m create mode 100644 Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm (limited to 'Firestore') diff --git a/Firestore/Example/Firestore.xcodeproj/project.pbxproj b/Firestore/Example/Firestore.xcodeproj/project.pbxproj index 50da99b..eac3251 100644 --- a/Firestore/Example/Firestore.xcodeproj/project.pbxproj +++ b/Firestore/Example/Firestore.xcodeproj/project.pbxproj @@ -26,6 +26,8 @@ 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 */; }; + 5491BC721FB44593008B3588 /* FSTIntegrationTestCase.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5491BC711FB44593008B3588 /* FSTIntegrationTestCase.mm */; }; + 5491BC731FB44593008B3588 /* FSTIntegrationTestCase.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5491BC711FB44593008B3588 /* FSTIntegrationTestCase.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 */; }; @@ -37,8 +39,6 @@ 54DA12AE1F315EE100DD57A1 /* resume_token_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA12A41F315EE100DD57A1 /* resume_token_spec_test.json */; }; 54DA12AF1F315EE100DD57A1 /* write_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA12A51F315EE100DD57A1 /* write_spec_test.json */; }; 54DA12B11F315F3800DD57A1 /* FIRValidationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 54DA12B01F315F3800DD57A1 /* FIRValidationTests.m */; }; - 54E928221F33952900C1953E /* FSTIntegrationTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 54E9281F1F33950B00C1953E /* FSTIntegrationTestCase.m */; }; - 54E928231F33952D00C1953E /* FSTIntegrationTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 54E9281F1F33950B00C1953E /* FSTIntegrationTestCase.m */; }; 54E928241F33953300C1953E /* FSTEventAccumulator.m in Sources */ = {isa = PBXBuildFile; fileRef = 54E9281D1F33950B00C1953E /* FSTEventAccumulator.m */; }; 54E928251F33953400C1953E /* FSTEventAccumulator.m in Sources */ = {isa = PBXBuildFile; fileRef = 54E9281D1F33950B00C1953E /* FSTEventAccumulator.m */; }; 54E9282C1F339CAD00C1953E /* XCTestCase+Await.m in Sources */ = {isa = PBXBuildFile; fileRef = 54E9282B1F339CAD00C1953E /* XCTestCase+Await.m */; }; @@ -185,6 +185,7 @@ 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 = ""; }; + 5491BC711FB44593008B3588 /* FSTIntegrationTestCase.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTIntegrationTestCase.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 = ""; }; @@ -199,7 +200,6 @@ 54E9281C1F33950B00C1953E /* FSTEventAccumulator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSTEventAccumulator.h; sourceTree = ""; }; 54E9281D1F33950B00C1953E /* FSTEventAccumulator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FSTEventAccumulator.m; sourceTree = ""; }; 54E9281E1F33950B00C1953E /* FSTIntegrationTestCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSTIntegrationTestCase.h; sourceTree = ""; }; - 54E9281F1F33950B00C1953E /* FSTIntegrationTestCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FSTIntegrationTestCase.m; sourceTree = ""; }; 54E9282A1F339CAD00C1953E /* XCTestCase+Await.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "XCTestCase+Await.h"; sourceTree = ""; }; 54E9282B1F339CAD00C1953E /* XCTestCase+Await.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "XCTestCase+Await.m"; sourceTree = ""; }; 6003F58A195388D20070C39A /* Firestore_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Firestore_Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -579,7 +579,7 @@ 54E9281C1F33950B00C1953E /* FSTEventAccumulator.h */, 54E9281D1F33950B00C1953E /* FSTEventAccumulator.m */, 54E9281E1F33950B00C1953E /* FSTIntegrationTestCase.h */, - 54E9281F1F33950B00C1953E /* FSTIntegrationTestCase.m */, + 5491BC711FB44593008B3588 /* FSTIntegrationTestCase.mm */, DE51B1861F0D48AC0013853F /* FSTAssertTests.m */, DE51B1871F0D48AC0013853F /* FSTComparisonTests.m */, DE51B1881F0D48AC0013853F /* FSTHelpers.h */, @@ -903,10 +903,10 @@ inputPaths = ( "${SRCROOT}/Pods/Target Support Files/Pods-SwiftBuildTest/Pods-SwiftBuildTest-frameworks.sh", "${BUILT_PRODUCTS_DIR}/BoringSSL/openssl.framework", - "${BUILT_PRODUCTS_DIR}/FirebaseCommunity/FirebaseCommunity.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseCommunity-Auth-Core-Root/FirebaseCommunity.framework", "${BUILT_PRODUCTS_DIR}/Firestore/Firestore.framework", "${BUILT_PRODUCTS_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework", - "${BUILT_PRODUCTS_DIR}/GoogleToolboxForMac/GoogleToolboxForMac.framework", + "${BUILT_PRODUCTS_DIR}/GoogleToolboxForMac-f0850809/GoogleToolboxForMac.framework", "${BUILT_PRODUCTS_DIR}/Protobuf/Protobuf.framework", "${BUILT_PRODUCTS_DIR}/gRPC/GRPCClient.framework", "${BUILT_PRODUCTS_DIR}/gRPC-Core/grpc.framework", @@ -943,10 +943,10 @@ inputPaths = ( "${SRCROOT}/Pods/Target Support Files/Pods-Firestore_Example/Pods-Firestore_Example-frameworks.sh", "${BUILT_PRODUCTS_DIR}/BoringSSL/openssl.framework", - "${BUILT_PRODUCTS_DIR}/FirebaseCommunity/FirebaseCommunity.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseCommunity-Auth-Core-Root/FirebaseCommunity.framework", "${BUILT_PRODUCTS_DIR}/Firestore/Firestore.framework", "${BUILT_PRODUCTS_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework", - "${BUILT_PRODUCTS_DIR}/GoogleToolboxForMac/GoogleToolboxForMac.framework", + "${BUILT_PRODUCTS_DIR}/GoogleToolboxForMac-f0850809/GoogleToolboxForMac.framework", "${BUILT_PRODUCTS_DIR}/Protobuf/Protobuf.framework", "${BUILT_PRODUCTS_DIR}/gRPC/GRPCClient.framework", "${BUILT_PRODUCTS_DIR}/gRPC-Core/grpc.framework", @@ -1033,12 +1033,16 @@ ); inputPaths = ( "${SRCROOT}/Pods/Target Support Files/Pods-Firestore_Tests/Pods-Firestore_Tests-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/FirebaseCommunity-Core-Root/FirebaseCommunity.framework", + "${BUILT_PRODUCTS_DIR}/GoogleToolboxForMac-Defines-NSData+zlib/GoogleToolboxForMac.framework", "${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}/FirebaseCommunity.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleToolboxForMac.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/leveldb.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleTest.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OCMock.framework", @@ -1073,10 +1077,14 @@ ); inputPaths = ( "${SRCROOT}/Pods/Target Support Files/Pods-Firestore_IntegrationTests/Pods-Firestore_IntegrationTests-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/FirebaseCommunity-Core-Root/FirebaseCommunity.framework", + "${BUILT_PRODUCTS_DIR}/GoogleToolboxForMac-Defines-NSData+zlib/GoogleToolboxForMac.framework", "${BUILT_PRODUCTS_DIR}/OCMock/OCMock.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCommunity.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleToolboxForMac.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OCMock.framework", ); runOnlyForDeploymentPostprocessing = 0; @@ -1163,10 +1171,10 @@ DE51B2011F0D493E0013853F /* FSTHelpers.m in Sources */, DE51B1F61F0D491B0013853F /* FSTSerializerBetaTests.m in Sources */, DE51B1F01F0D49140013853F /* FSTFieldValueTests.m in Sources */, + 5491BC721FB44593008B3588 /* FSTIntegrationTestCase.mm in Sources */, DE2EF0861F3D0B6E003D0CDC /* FSTImmutableSortedDictionary+Testing.m in Sources */, DE51B1DE1F0D490D0013853F /* FSTMemoryLocalStoreTests.m in Sources */, DE51B1EC1F0D49140013853F /* FSTDatabaseIDTests.m in Sources */, - 54E928221F33952900C1953E /* FSTIntegrationTestCase.m in Sources */, DE51B1ED1F0D49140013853F /* FSTDocumentKeyTests.m in Sources */, DE51B1D41F0D48CD0013853F /* FSTViewTests.m in Sources */, DE51B1F41F0D491B0013853F /* FSTRemoteEventTests.m in Sources */, @@ -1217,7 +1225,7 @@ files = ( DE03B2EE1F214BAA00A30B9C /* FIRWriteBatchTests.m in Sources */, DE03B2F01F214BAA00A30B9C /* FIRDatabaseTests.m in Sources */, - 54E928231F33952D00C1953E /* FSTIntegrationTestCase.m in Sources */, + 5491BC731FB44593008B3588 /* FSTIntegrationTestCase.mm in Sources */, DE03B2F41F214BAA00A30B9C /* FIRServerTimestampTests.m in Sources */, DE03B2F11F214BAA00A30B9C /* FIRFieldsTests.m in Sources */, 54E9282D1F339CAD00C1953E /* XCTestCase+Await.m in Sources */, diff --git a/Firestore/Example/Podfile b/Firestore/Example/Podfile index c79495d..c5cf06a 100644 --- a/Firestore/Example/Podfile +++ b/Firestore/Example/Podfile @@ -7,13 +7,19 @@ target 'Firestore_Example' do target 'Firestore_Tests' do inherit! :search_paths + + pod 'FirebaseCommunity/Core', :path => '../../' + pod 'leveldb-library' + pod 'OCMock' pod 'GoogleTest', :podspec => 'Tests/GoogleTest/GoogleTest.podspec' - pod 'leveldb-library' end target 'Firestore_IntegrationTests' do inherit! :search_paths + + pod 'FirebaseCommunity/Core', :path => '../../' + pod 'OCMock' end end diff --git a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.h b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.h index a2c08ec..1170080 100644 --- a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.h +++ b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.h @@ -30,6 +30,10 @@ NS_ASSUME_NONNULL_BEGIN +#if __cplusplus +extern "C" { +#endif + @interface FSTIntegrationTestCase : XCTestCase /** Returns the default Firestore project ID for testing. */ @@ -96,4 +100,8 @@ NSArray *> *FIRQuerySnapshotGetData(FIRQuerySnapsho /** Converts the FIRQuerySnapshot to an NSArray containing the document IDs in order. */ NSArray *FIRQuerySnapshotGetIDs(FIRQuerySnapshot *docs); +#if __cplusplus +} // extern "C" +#endif + NS_ASSUME_NONNULL_END diff --git a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.m b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.m deleted file mode 100644 index 2e1e0a9..0000000 --- a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.m +++ /dev/null @@ -1,319 +0,0 @@ -/* - * 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 Firestore; - -#import "FSTIntegrationTestCase.h" - -#import -#import -#import - -#import "API/FIRFirestore+Internal.h" -#import "Auth/FSTEmptyCredentialsProvider.h" -#import "Local/FSTLevelDB.h" -#import "Model/FSTDatabaseID.h" -#import "Util/FSTDispatchQueue.h" -#import "Util/FSTUtil.h" - -#import "FSTEventAccumulator.h" -#import "FSTTestDispatchQueue.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface FIRFirestore (Testing) -@property(nonatomic, strong) FSTDispatchQueue *workerDispatchQueue; -@end - -@implementation FSTIntegrationTestCase { - NSMutableArray *_firestores; -} - -- (void)setUp { - [super setUp]; - - [self clearPersistence]; - - _firestores = [NSMutableArray array]; - self.db = [self firestore]; - self.eventAccumulator = [FSTEventAccumulator accumulatorForTest:self]; -} - -- (void)tearDown { - @try { - for (FIRFirestore *firestore in _firestores) { - [self shutdownFirestore:firestore]; - } - } @finally { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - [GRPCCall closeOpenConnections]; -#pragma clang diagnostic pop - _firestores = nil; - [super tearDown]; - } -} - -- (void)clearPersistence { - NSString *levelDBDir = [FSTLevelDB documentsDirectory]; - NSError *error; - if (![[NSFileManager defaultManager] removeItemAtPath:levelDBDir error:&error]) { - // file not found is okay. - XCTAssertTrue( - [error.domain isEqualToString:NSCocoaErrorDomain] && error.code == NSFileNoSuchFileError, - @"Failed to clear LevelDB Persistence: %@", error); - } -} - -- (FIRFirestore *)firestore { - return [self firestoreWithProjectID:[FSTIntegrationTestCase projectID]]; -} - -+ (NSString *)projectID { - NSString *project = [[NSProcessInfo processInfo] environment][@"PROJECT_ID"]; - if (!project) { - project = @"test-db"; - } - return project; -} - -+ (FIRFirestoreSettings *)settings { - FIRFirestoreSettings *settings = [[FIRFirestoreSettings alloc] init]; - NSString *host = [[NSProcessInfo processInfo] environment][@"DATASTORE_HOST"]; - settings.sslEnabled = YES; - if (!host) { - // If host is nil, there is no GoogleService-Info.plist. Check if a hexa integration test - // configuration is configured. The first bundle location is used by bazel builds. The - // second is used for github clones. - host = @"localhost:8081"; - settings.sslEnabled = YES; - NSString *certsPath = - [[NSBundle mainBundle] pathForResource:@"PlugIns/IntegrationTests.xctest/CAcert" - ofType:@"pem"]; - if (certsPath == nil) { - certsPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"CAcert" ofType:@"pem"]; - } - unsigned long long fileSize = - [[[NSFileManager defaultManager] attributesOfItemAtPath:certsPath error:nil] fileSize]; - - if (fileSize == 0) { - NSLog( - @"The cert is not properly configured. Make sure setup_integration_tests.py " - "has been run."); - } - [GRPCCall useTestCertsPath:certsPath testName:@"test_cert_2" forHost:host]; - } - settings.host = host; - settings.persistenceEnabled = YES; - NSLog(@"Configured integration test for %@ with SSL: %@", settings.host, - settings.sslEnabled ? @"YES" : @"NO"); - return settings; -} - -- (FIRFirestore *)firestoreWithProjectID:(NSString *)projectID { - NSString *persistenceKey = [NSString stringWithFormat:@"db%lu", (unsigned long)_firestores.count]; - - FSTTestDispatchQueue *workerDispatchQueue = [FSTTestDispatchQueue - queueWith:dispatch_queue_create("com.google.firebase.firestore", DISPATCH_QUEUE_SERIAL)]; - - FSTEmptyCredentialsProvider *credentialsProvider = [[FSTEmptyCredentialsProvider alloc] init]; - - FIRSetLoggerLevel(FIRLoggerLevelDebug); - // HACK: FIRFirestore expects a non-nil app, but for tests we cheat. - FIRApp *app = nil; - FIRFirestore *firestore = [[FIRFirestore alloc] initWithProjectID:projectID - database:kDefaultDatabaseID - persistenceKey:persistenceKey - credentialsProvider:credentialsProvider - workerDispatchQueue:workerDispatchQueue - firebaseApp:app]; - - firestore.settings = [FSTIntegrationTestCase settings]; - - [_firestores addObject:firestore]; - return firestore; -} - -- (void)waitForIdleFirestore:(FIRFirestore *)firestore { - XCTestExpectation *expectation = [self expectationWithDescription:@"idle"]; - // Note that we wait on any task that is scheduled with a delay of 60s. Currently, the idle - // timeout is the only task that uses this delay. - [((FSTTestDispatchQueue *)firestore.workerDispatchQueue) fulfillOnExecution:expectation]; - [self awaitExpectations]; -} - -- (void)shutdownFirestore:(FIRFirestore *)firestore { - XCTestExpectation *shutdownCompletion = [self expectationWithDescription:@"shutdown"]; - [firestore shutdownWithCompletion:^(NSError *_Nullable error) { - XCTAssertNil(error); - [shutdownCompletion fulfill]; - }]; - [self awaitExpectations]; -} - -- (NSString *)documentPath { - return [@"test-collection/" stringByAppendingString:[FSTUtil autoID]]; -} - -- (FIRDocumentReference *)documentRef { - return [self.db documentWithPath:[self documentPath]]; -} - -- (FIRCollectionReference *)collectionRef { - NSString *collectionName = [@"test-collection-" stringByAppendingString:[FSTUtil autoID]]; - return [self.db collectionWithPath:collectionName]; -} - -- (FIRCollectionReference *)collectionRefWithDocuments: - (NSDictionary *> *)documents { - FIRCollectionReference *collection = [self collectionRef]; - // Use a different instance to write the documents - [self writeAllDocuments:documents - toCollection:[[self firestore] collectionWithPath:collection.path]]; - return collection; -} - -- (void)writeAllDocuments:(NSDictionary *> *)documents - toCollection:(FIRCollectionReference *)collection { - [documents enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSDictionary *value, - BOOL *stop) { - FIRDocumentReference *ref = [collection documentWithPath:key]; - [self writeDocumentRef:ref data:value]; - }]; -} - -- (void)readerAndWriterOnDocumentRef:(void (^)(NSString *path, - FIRDocumentReference *readerRef, - FIRDocumentReference *writerRef))action { - FIRFirestore *reader = self.db; // for clarity - FIRFirestore *writer = [self firestore]; - - NSString *path = [self documentPath]; - FIRDocumentReference *readerRef = [reader documentWithPath:path]; - FIRDocumentReference *writerRef = [writer documentWithPath:path]; - action(path, readerRef, writerRef); -} - -- (FIRDocumentSnapshot *)readDocumentForRef:(FIRDocumentReference *)ref { - __block FIRDocumentSnapshot *result; - - XCTestExpectation *expectation = [self expectationWithDescription:@"getData"]; - [ref getDocumentWithCompletion:^(FIRDocumentSnapshot *doc, NSError *_Nullable error) { - XCTAssertNil(error); - result = doc; - [expectation fulfill]; - }]; - [self awaitExpectations]; - - return result; -} - -- (FIRQuerySnapshot *)readDocumentSetForRef:(FIRQuery *)query { - __block FIRQuerySnapshot *result; - - XCTestExpectation *expectation = [self expectationWithDescription:@"getData"]; - [query getDocumentsWithCompletion:^(FIRQuerySnapshot *documentSet, NSError *error) { - XCTAssertNil(error); - result = documentSet; - [expectation fulfill]; - }]; - [self awaitExpectations]; - - return result; -} - -- (FIRDocumentSnapshot *)readSnapshotForRef:(FIRDocumentReference *)ref - requireOnline:(BOOL)requireOnline { - __block FIRDocumentSnapshot *result; - - XCTestExpectation *expectation = [self expectationWithDescription:@"listener"]; - id listener = [ref - addSnapshotListenerWithOptions:[[FIRDocumentListenOptions options] includeMetadataChanges:YES] - listener:^(FIRDocumentSnapshot *snapshot, NSError *error) { - XCTAssertNil(error); - if (!requireOnline || !snapshot.metadata.fromCache) { - result = snapshot; - [expectation fulfill]; - } - }]; - - [self awaitExpectations]; - [listener remove]; - - return result; -} - -- (void)writeDocumentRef:(FIRDocumentReference *)ref data:(NSDictionary *)data { - XCTestExpectation *expectation = [self expectationWithDescription:@"setData"]; - [ref setData:data - completion:^(NSError *_Nullable error) { - XCTAssertNil(error); - [expectation fulfill]; - }]; - [self awaitExpectations]; -} - -- (void)updateDocumentRef:(FIRDocumentReference *)ref data:(NSDictionary *)data { - XCTestExpectation *expectation = [self expectationWithDescription:@"updateData"]; - [ref updateData:data - completion:^(NSError *_Nullable error) { - XCTAssertNil(error); - [expectation fulfill]; - }]; - [self awaitExpectations]; -} - -- (void)deleteDocumentRef:(FIRDocumentReference *)ref { - XCTestExpectation *expectation = [self expectationWithDescription:@"deleteDocument"]; - [ref deleteDocumentWithCompletion:^(NSError *_Nullable error) { - XCTAssertNil(error); - [expectation fulfill]; - }]; - [self awaitExpectations]; -} - -- (void)waitUntil:(BOOL (^)())predicate { - NSTimeInterval start = [NSDate timeIntervalSinceReferenceDate]; - double waitSeconds = [self defaultExpectationWaitSeconds]; - while (!predicate() && ([NSDate timeIntervalSinceReferenceDate] - start < waitSeconds)) { - // This waits for the next event or until the 100ms timeout is reached - [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode - beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; - } - if (!predicate()) { - XCTFail(@"Timeout"); - } -} - -NSArray *> *FIRQuerySnapshotGetData(FIRQuerySnapshot *docs) { - NSMutableArray *> *result = [NSMutableArray array]; - for (FIRDocumentSnapshot *doc in docs.documents) { - [result addObject:doc.data]; - } - return result; -} - -NSArray *FIRQuerySnapshotGetIDs(FIRQuerySnapshot *docs) { - NSMutableArray *result = [NSMutableArray array]; - for (FIRDocumentSnapshot *doc in docs.documents) { - [result addObject:doc.documentID]; - } - return result; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm new file mode 100644 index 0000000..92e4dc2 --- /dev/null +++ b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm @@ -0,0 +1,320 @@ +/* + * 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 "FSTIntegrationTestCase.h" + +#import +#import +#import +#import + +#import "API/FIRFirestore+Internal.h" +#import "Auth/FSTEmptyCredentialsProvider.h" +#import "Local/FSTLevelDB.h" +#import "Model/FSTDatabaseID.h" +#import "Util/FSTDispatchQueue.h" +#import "Util/FSTUtil.h" + +#import "FSTEventAccumulator.h" +#import "FSTTestDispatchQueue.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface FIRFirestore (Testing) +@property(nonatomic, strong) FSTDispatchQueue *workerDispatchQueue; +@end + +@implementation FSTIntegrationTestCase { + NSMutableArray *_firestores; +} + +- (void)setUp { + [super setUp]; + + [self clearPersistence]; + + _firestores = [NSMutableArray array]; + self.db = [self firestore]; + self.eventAccumulator = [FSTEventAccumulator accumulatorForTest:self]; +} + +- (void)tearDown { + @try { + for (FIRFirestore *firestore in _firestores) { + [self shutdownFirestore:firestore]; + } + } @finally { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [GRPCCall closeOpenConnections]; +#pragma clang diagnostic pop + _firestores = nil; + [super tearDown]; + } +} + +- (void)clearPersistence { + NSString *levelDBDir = [FSTLevelDB documentsDirectory]; + NSError *error; + if (![[NSFileManager defaultManager] removeItemAtPath:levelDBDir error:&error]) { + // file not found is okay. + XCTAssertTrue( + [error.domain isEqualToString:NSCocoaErrorDomain] && error.code == NSFileNoSuchFileError, + @"Failed to clear LevelDB Persistence: %@", error); + } +} + +- (FIRFirestore *)firestore { + return [self firestoreWithProjectID:[FSTIntegrationTestCase projectID]]; +} + ++ (NSString *)projectID { + NSString *project = [[NSProcessInfo processInfo] environment][@"PROJECT_ID"]; + if (!project) { + project = @"test-db"; + } + return project; +} + ++ (FIRFirestoreSettings *)settings { + FIRFirestoreSettings *settings = [[FIRFirestoreSettings alloc] init]; + NSString *host = [[NSProcessInfo processInfo] environment][@"DATASTORE_HOST"]; + settings.sslEnabled = YES; + if (!host) { + // If host is nil, there is no GoogleService-Info.plist. Check if a hexa integration test + // configuration is configured. The first bundle location is used by bazel builds. The + // second is used for github clones. + host = @"localhost:8081"; + settings.sslEnabled = YES; + NSString *certsPath = + [[NSBundle mainBundle] pathForResource:@"PlugIns/IntegrationTests.xctest/CAcert" + ofType:@"pem"]; + if (certsPath == nil) { + certsPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"CAcert" ofType:@"pem"]; + } + unsigned long long fileSize = + [[[NSFileManager defaultManager] attributesOfItemAtPath:certsPath error:nil] fileSize]; + + if (fileSize == 0) { + NSLog( + @"The cert is not properly configured. Make sure setup_integration_tests.py " + "has been run."); + } + [GRPCCall useTestCertsPath:certsPath testName:@"test_cert_2" forHost:host]; + } + settings.host = host; + settings.persistenceEnabled = YES; + NSLog(@"Configured integration test for %@ with SSL: %@", settings.host, + settings.sslEnabled ? @"YES" : @"NO"); + return settings; +} + +- (FIRFirestore *)firestoreWithProjectID:(NSString *)projectID { + NSString *persistenceKey = [NSString stringWithFormat:@"db%lu", (unsigned long)_firestores.count]; + + FSTTestDispatchQueue *workerDispatchQueue = [FSTTestDispatchQueue + queueWith:dispatch_queue_create("com.google.firebase.firestore", DISPATCH_QUEUE_SERIAL)]; + + FSTEmptyCredentialsProvider *credentialsProvider = [[FSTEmptyCredentialsProvider alloc] init]; + + FIRSetLoggerLevel(FIRLoggerLevelDebug); + // HACK: FIRFirestore expects a non-nil app, but for tests we cheat. + FIRApp *app = nil; + FIRFirestore *firestore = [[FIRFirestore alloc] initWithProjectID:projectID + database:kDefaultDatabaseID + persistenceKey:persistenceKey + credentialsProvider:credentialsProvider + workerDispatchQueue:workerDispatchQueue + firebaseApp:app]; + + firestore.settings = [FSTIntegrationTestCase settings]; + + [_firestores addObject:firestore]; + return firestore; +} + +- (void)waitForIdleFirestore:(FIRFirestore *)firestore { + XCTestExpectation *expectation = [self expectationWithDescription:@"idle"]; + // Note that we wait on any task that is scheduled with a delay of 60s. Currently, the idle + // timeout is the only task that uses this delay. + [((FSTTestDispatchQueue *)firestore.workerDispatchQueue) fulfillOnExecution:expectation]; + [self awaitExpectations]; +} + +- (void)shutdownFirestore:(FIRFirestore *)firestore { + XCTestExpectation *shutdownCompletion = [self expectationWithDescription:@"shutdown"]; + [firestore shutdownWithCompletion:^(NSError *_Nullable error) { + XCTAssertNil(error); + [shutdownCompletion fulfill]; + }]; + [self awaitExpectations]; +} + +- (NSString *)documentPath { + return [@"test-collection/" stringByAppendingString:[FSTUtil autoID]]; +} + +- (FIRDocumentReference *)documentRef { + return [self.db documentWithPath:[self documentPath]]; +} + +- (FIRCollectionReference *)collectionRef { + NSString *collectionName = [@"test-collection-" stringByAppendingString:[FSTUtil autoID]]; + return [self.db collectionWithPath:collectionName]; +} + +- (FIRCollectionReference *)collectionRefWithDocuments: + (NSDictionary *> *)documents { + FIRCollectionReference *collection = [self collectionRef]; + // Use a different instance to write the documents + [self writeAllDocuments:documents + toCollection:[[self firestore] collectionWithPath:collection.path]]; + return collection; +} + +- (void)writeAllDocuments:(NSDictionary *> *)documents + toCollection:(FIRCollectionReference *)collection { + [documents enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSDictionary *value, + BOOL *stop) { + FIRDocumentReference *ref = [collection documentWithPath:key]; + [self writeDocumentRef:ref data:value]; + }]; +} + +- (void)readerAndWriterOnDocumentRef:(void (^)(NSString *path, + FIRDocumentReference *readerRef, + FIRDocumentReference *writerRef))action { + FIRFirestore *reader = self.db; // for clarity + FIRFirestore *writer = [self firestore]; + + NSString *path = [self documentPath]; + FIRDocumentReference *readerRef = [reader documentWithPath:path]; + FIRDocumentReference *writerRef = [writer documentWithPath:path]; + action(path, readerRef, writerRef); +} + +- (FIRDocumentSnapshot *)readDocumentForRef:(FIRDocumentReference *)ref { + __block FIRDocumentSnapshot *result; + + XCTestExpectation *expectation = [self expectationWithDescription:@"getData"]; + [ref getDocumentWithCompletion:^(FIRDocumentSnapshot *doc, NSError *_Nullable error) { + XCTAssertNil(error); + result = doc; + [expectation fulfill]; + }]; + [self awaitExpectations]; + + return result; +} + +- (FIRQuerySnapshot *)readDocumentSetForRef:(FIRQuery *)query { + __block FIRQuerySnapshot *result; + + XCTestExpectation *expectation = [self expectationWithDescription:@"getData"]; + [query getDocumentsWithCompletion:^(FIRQuerySnapshot *documentSet, NSError *error) { + XCTAssertNil(error); + result = documentSet; + [expectation fulfill]; + }]; + [self awaitExpectations]; + + return result; +} + +- (FIRDocumentSnapshot *)readSnapshotForRef:(FIRDocumentReference *)ref + requireOnline:(BOOL)requireOnline { + __block FIRDocumentSnapshot *result; + + XCTestExpectation *expectation = [self expectationWithDescription:@"listener"]; + id listener = [ref + addSnapshotListenerWithOptions:[[FIRDocumentListenOptions options] includeMetadataChanges:YES] + listener:^(FIRDocumentSnapshot *snapshot, NSError *error) { + XCTAssertNil(error); + if (!requireOnline || !snapshot.metadata.fromCache) { + result = snapshot; + [expectation fulfill]; + } + }]; + + [self awaitExpectations]; + [listener remove]; + + return result; +} + +- (void)writeDocumentRef:(FIRDocumentReference *)ref data:(NSDictionary *)data { + XCTestExpectation *expectation = [self expectationWithDescription:@"setData"]; + [ref setData:data + completion:^(NSError *_Nullable error) { + XCTAssertNil(error); + [expectation fulfill]; + }]; + [self awaitExpectations]; +} + +- (void)updateDocumentRef:(FIRDocumentReference *)ref data:(NSDictionary *)data { + XCTestExpectation *expectation = [self expectationWithDescription:@"updateData"]; + [ref updateData:data + completion:^(NSError *_Nullable error) { + XCTAssertNil(error); + [expectation fulfill]; + }]; + [self awaitExpectations]; +} + +- (void)deleteDocumentRef:(FIRDocumentReference *)ref { + XCTestExpectation *expectation = [self expectationWithDescription:@"deleteDocument"]; + [ref deleteDocumentWithCompletion:^(NSError *_Nullable error) { + XCTAssertNil(error); + [expectation fulfill]; + }]; + [self awaitExpectations]; +} + +- (void)waitUntil:(BOOL (^)())predicate { + NSTimeInterval start = [NSDate timeIntervalSinceReferenceDate]; + double waitSeconds = [self defaultExpectationWaitSeconds]; + while (!predicate() && ([NSDate timeIntervalSinceReferenceDate] - start < waitSeconds)) { + // This waits for the next event or until the 100ms timeout is reached + [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode + beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; + } + if (!predicate()) { + XCTFail(@"Timeout"); + } +} + +extern "C" +NSArray *> *FIRQuerySnapshotGetData(FIRQuerySnapshot *docs) { + NSMutableArray *> *result = [NSMutableArray array]; + for (FIRDocumentSnapshot *doc in docs.documents) { + [result addObject:doc.data]; + } + return result; +} + +extern "C" +NSArray *FIRQuerySnapshotGetIDs(FIRQuerySnapshot *docs) { + NSMutableArray *result = [NSMutableArray array]; + for (FIRDocumentSnapshot *doc in docs.documents) { + [result addObject:doc.documentID]; + } + return result; +} + +@end + +NS_ASSUME_NONNULL_END -- cgit v1.2.3