From 3440fef2ffc9d76d0282b4bee5bca80aa9cc51d2 Mon Sep 17 00:00:00 2001 From: rsgowman Date: Fri, 19 Jan 2018 13:40:53 -0500 Subject: [PATCH 1/7] Add GetOptions for controlling offline get behaviour (#655) Add option to allow the user to control where DocumentReference.getDocument() and CollectionReference.getDocuments() fetches from. By default, it fetches from the server (if possible) and falls back to the local cache. It's now possible to alternatively fetch from the local cache only, or to fetch from the server only (though in the server only case, latency compensation is still enabled). --- .../Firestore.xcodeproj/project.pbxproj | 4 + Firestore/Example/SwiftBuildTest/main.swift | 24 + .../Integration/API/FIRGetOptionsTests.m | 674 ++++++++++++++++++ .../Tests/Util/FSTIntegrationTestCase.h | 10 + .../Tests/Util/FSTIntegrationTestCase.mm | 43 +- Firestore/Source/API/FIRDocumentReference.m | 25 +- Firestore/Source/API/FIRGetOptions+Internal.h | 28 + Firestore/Source/API/FIRGetOptions.m | 36 + Firestore/Source/API/FIRQuery.m | 38 +- Firestore/Source/Core/FSTFirestoreClient.h | 20 + Firestore/Source/Core/FSTFirestoreClient.m | 64 ++ .../Source/Public/FIRDocumentReference.h | 6 + Firestore/Source/Public/FIRGetOptions.h | 69 ++ Firestore/Source/Public/FIRQuery.h | 6 + 14 files changed, 1031 insertions(+), 16 deletions(-) create mode 100644 Firestore/Example/Tests/Integration/API/FIRGetOptionsTests.m create mode 100644 Firestore/Source/API/FIRGetOptions+Internal.h create mode 100644 Firestore/Source/API/FIRGetOptions.m create mode 100644 Firestore/Source/Public/FIRGetOptions.h diff --git a/Firestore/Example/Firestore.xcodeproj/project.pbxproj b/Firestore/Example/Firestore.xcodeproj/project.pbxproj index 06f790c1449..503d62a3b81 100644 --- a/Firestore/Example/Firestore.xcodeproj/project.pbxproj +++ b/Firestore/Example/Firestore.xcodeproj/project.pbxproj @@ -59,6 +59,7 @@ 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; 6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5B8195388D20070C39A /* InfoPlist.strings */; }; + 61CC13FA2007D0C90021F5BF /* FIRGetOptionsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 61CC13F82007D0C20021F5BF /* FIRGetOptionsTests.m */; }; 61E1D8B11FCF6C5700753285 /* StringViewTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 61E1D8AF1FCF6AF500753285 /* StringViewTests.mm */; }; 6ED54761B845349D43DB6B78 /* Pods_Firestore_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 75A6FE51C1A02DF38F62FAAD /* Pods_Firestore_Example.framework */; }; 71719F9F1E33DC2100824A3D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 71719F9D1E33DC2100824A3D /* LaunchScreen.storyboard */; }; @@ -235,6 +236,7 @@ 6003F5AF195388D20070C39A /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 6003F5B7195388D20070C39A /* Tests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Tests-Info.plist"; sourceTree = ""; }; 6003F5B9195388D20070C39A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + 61CC13F82007D0C20021F5BF /* FIRGetOptionsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRGetOptionsTests.m; sourceTree = ""; }; 61E1D8AF1FCF6AF500753285 /* StringViewTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = StringViewTests.mm; sourceTree = ""; }; 69F6A10DBD6187489481CD76 /* Pods_Firestore_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 71719F9E1E33DC2100824A3D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; @@ -723,6 +725,7 @@ DE51B1BC1F0D48AC0013853F /* API */ = { isa = PBXGroup; children = ( + 61CC13F82007D0C20021F5BF /* FIRGetOptionsTests.m */, DE51B1BD1F0D48AC0013853F /* FIRCursorTests.m */, DE51B1BE1F0D48AC0013853F /* FIRDatabaseTests.m */, DE51B1BF1F0D48AC0013853F /* FIRFieldsTests.m */, @@ -1281,6 +1284,7 @@ 54E928251F33953400C1953E /* FSTEventAccumulator.m in Sources */, DE03B2ED1F214BA200A30B9C /* FSTSmokeTests.m in Sources */, DE03B2F31F214BAA00A30B9C /* FIRQueryTests.m in Sources */, + 61CC13FA2007D0C90021F5BF /* FIRGetOptionsTests.m in Sources */, DE03B35E1F21586C00A30B9C /* FSTHelpers.m in Sources */, DE03B2F51F214BAA00A30B9C /* FIRTypeTests.m in Sources */, DE03B2EF1F214BAA00A30B9C /* FIRCursorTests.m in Sources */, diff --git a/Firestore/Example/SwiftBuildTest/main.swift b/Firestore/Example/SwiftBuildTest/main.swift index 260735b1ee1..2d77669f265 100644 --- a/Firestore/Example/SwiftBuildTest/main.swift +++ b/Firestore/Example/SwiftBuildTest/main.swift @@ -32,8 +32,10 @@ func main() { addDocument(to: collectionRef); readDocument(at: documentRef); + readDocumentWithOptions(at: documentRef); readDocuments(matching: query); + readDocumentsWithOptions(matching: query); listenToDocument(at: documentRef); @@ -223,6 +225,17 @@ func readDocument(at docRef: DocumentReference) { } } +func readDocumentWithOptions(at docRef: DocumentReference) { + docRef.getDocument(options:GetOptions.defaultOptions()) { document, error in + } + docRef.getDocument(options:GetOptions(source:GetSource.default)) { document, error in + } + docRef.getDocument(options:GetOptions(source:.server)) { document, error in + } + docRef.getDocument(options:GetOptions(source:GetSource.cache)) { document, error in + } +} + func readDocuments(matching query: Query) { query.getDocuments() { querySnapshot, error in // TODO(mikelehen): Figure out how to make "for..in" syntax work @@ -233,6 +246,17 @@ func readDocuments(matching query: Query) { } } +func readDocumentsWithOptions(matching query: Query) { + query.getDocuments(options:GetOptions.defaultOptions()) { querySnapshot, error in + } + query.getDocuments(options:GetOptions.init(source:GetSource.default)) { querySnapshot, error in + } + query.getDocuments(options:GetOptions.init(source:GetSource.server)) { querySnapshot, error in + } + query.getDocuments(options:GetOptions.init(source:GetSource.cache)) { querySnapshot, error in + } +} + func listenToDocument(at docRef: DocumentReference) { let listener = docRef.addSnapshotListener() { document, error in diff --git a/Firestore/Example/Tests/Integration/API/FIRGetOptionsTests.m b/Firestore/Example/Tests/Integration/API/FIRGetOptionsTests.m new file mode 100644 index 00000000000..628a6dcb2d8 --- /dev/null +++ b/Firestore/Example/Tests/Integration/API/FIRGetOptionsTests.m @@ -0,0 +1,674 @@ +/* + * Copyright 2018 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 FirebaseFirestore; + +#import + +#import "Firestore/Example/Tests/Util/FSTIntegrationTestCase.h" +#import "Firestore/Source/API/FIRFirestore+Internal.h" +#import "Firestore/Source/Core/FSTFirestoreClient.h" + +@interface FIRGetOptionsTests : FSTIntegrationTestCase +@end + +@implementation FIRGetOptionsTests + +- (void)testGetDocumentWhileOnlineWithDefaultGetOptions { + FIRDocumentReference *doc = [self documentRef]; + + // set document to a known value + NSDictionary *initialData = @{@"key" : @"value"}; + [self writeDocumentRef:doc data:initialData]; + + // get doc and ensure that it exists, is *not* from the cache, and matches + // the initialData. + FIRDocumentSnapshot *result = [self readDocumentForRef:doc]; + XCTAssertTrue(result.exists); + XCTAssertFalse(result.metadata.fromCache); + XCTAssertFalse(result.metadata.hasPendingWrites); + XCTAssertEqualObjects(result.data, initialData); +} + +- (void)testGetCollectionWhileOnlineWithDefaultGetOptions { + FIRCollectionReference *col = [self collectionRef]; + + // set a few documents to known values + NSDictionary *> *initialDocs = @{ + @"doc1" : @{@"key1" : @"value1"}, + @"doc2" : @{@"key2" : @"value2"}, + @"doc3" : @{@"key3" : @"value3"} + }; + [self writeAllDocuments:initialDocs toCollection:col]; + + // get docs and ensure they are *not* from the cache, and match the + // initialDocs. + FIRQuerySnapshot *result = [self readDocumentSetForRef:col]; + XCTAssertFalse(result.metadata.fromCache); + XCTAssertFalse(result.metadata.hasPendingWrites); + XCTAssertEqualObjects( + FIRQuerySnapshotGetData(result), + (@[ @{@"key1" : @"value1"}, @{@"key2" : @"value2"}, @{@"key3" : @"value3"} ])); + XCTAssertEqualObjects(FIRQuerySnapshotGetDocChangesData(result), (@[ + @[ @(FIRDocumentChangeTypeAdded), @"doc1", @{@"key1" : @"value1"} ], + @[ @(FIRDocumentChangeTypeAdded), @"doc2", @{@"key2" : @"value2"} ], + @[ @(FIRDocumentChangeTypeAdded), @"doc3", @{@"key3" : @"value3"} ] + ])); +} + +- (void)testGetDocumentWhileOfflineWithDefaultGetOptions { + FIRDocumentReference *doc = [self documentRef]; + + // set document to a known value + NSDictionary *initialData = @{@"key1" : @"value1"}; + [self writeDocumentRef:doc data:initialData]; + + // go offline for the rest of this test + [self disableNetwork]; + + // update the doc (though don't wait for a server response. We're offline; so + // that ain't happening!). This allows us to further distinguished cached vs + // server responses below. + NSDictionary *newData = @{@"key2" : @"value2"}; + [doc setData:newData + completion:^(NSError *_Nullable error) { + XCTAssertTrue(false, "Because we're offline, this should never occur."); + }]; + + // get doc and ensure it exists, *is* from the cache, and matches the + // newData. + FIRDocumentSnapshot *result = [self readDocumentForRef:doc]; + XCTAssertTrue(result.exists); + XCTAssertTrue(result.metadata.fromCache); + XCTAssertTrue(result.metadata.hasPendingWrites); + XCTAssertEqualObjects(result.data, newData); +} + +- (void)testGetCollectionWhileOfflineWithDefaultGetOptions { + FIRCollectionReference *col = [self collectionRef]; + + // set a few documents to known values + NSDictionary *> *initialDocs = @{ + @"doc1" : @{@"key1" : @"value1"}, + @"doc2" : @{@"key2" : @"value2"}, + @"doc3" : @{@"key3" : @"value3"} + }; + [self writeAllDocuments:initialDocs toCollection:col]; + + // go offline for the rest of this test + [self disableNetwork]; + + // update the docs (though don't wait for a server response. We're offline; so + // that ain't happening!). This allows us to further distinguished cached vs + // server responses below. + [[col documentWithPath:@"doc2"] setData:@{@"key2b" : @"value2b"} options:FIRSetOptions.merge]; + [[col documentWithPath:@"doc3"] setData:@{@"key3b" : @"value3b"}]; + [[col documentWithPath:@"doc4"] setData:@{@"key4" : @"value4"}]; + + // get docs and ensure they *are* from the cache, and matches the updated data. + FIRQuerySnapshot *result = [self readDocumentSetForRef:col]; + XCTAssertTrue(result.metadata.fromCache); + XCTAssertTrue(result.metadata.hasPendingWrites); + XCTAssertEqualObjects(FIRQuerySnapshotGetData(result), (@[ + @{@"key1" : @"value1"}, @{@"key2" : @"value2", @"key2b" : @"value2b"}, + @{@"key3b" : @"value3b"}, @{@"key4" : @"value4"} + ])); + XCTAssertEqualObjects( + FIRQuerySnapshotGetDocChangesData(result), (@[ + @[ @(FIRDocumentChangeTypeAdded), @"doc1", @{@"key1" : @"value1"} ], + @[ @(FIRDocumentChangeTypeAdded), @"doc2", @{@"key2" : @"value2", @"key2b" : @"value2b"} ], + @[ @(FIRDocumentChangeTypeAdded), @"doc3", @{@"key3b" : @"value3b"} ], + @[ @(FIRDocumentChangeTypeAdded), @"doc4", @{@"key4" : @"value4"} ] + ])); +} + +- (void)testGetDocumentWhileOnlineCacheOnly { + FIRDocumentReference *doc = [self documentRef]; + + // set document to a known value + NSDictionary *initialData = @{@"key" : @"value"}; + [self writeDocumentRef:doc data:initialData]; + + // get doc and ensure that it exists, *is* from the cache, and matches + // the initialData. + FIRDocumentSnapshot *result = + [self readDocumentForRef:doc + options:[[FIRGetOptions alloc] initWithSource:FIRGetSourceCache]]; + XCTAssertTrue(result.exists); + XCTAssertTrue(result.metadata.fromCache); + XCTAssertFalse(result.metadata.hasPendingWrites); + XCTAssertEqualObjects(result.data, initialData); +} + +- (void)testGetCollectionWhileOnlineCacheOnly { + FIRCollectionReference *col = [self collectionRef]; + + // set a few documents to a known value + NSDictionary *> *initialDocs = @{ + @"doc1" : @{@"key1" : @"value1"}, + @"doc2" : @{@"key2" : @"value2"}, + @"doc3" : @{@"key3" : @"value3"}, + }; + [self writeAllDocuments:initialDocs toCollection:col]; + + // get docs and ensure they *are* from the cache, and matches the + // initialDocs. + FIRQuerySnapshot *result = + [self readDocumentSetForRef:col + options:[[FIRGetOptions alloc] initWithSource:FIRGetSourceCache]]; + XCTAssertTrue(result.metadata.fromCache); + XCTAssertFalse(result.metadata.hasPendingWrites); + XCTAssertEqualObjects(FIRQuerySnapshotGetData(result), (@[ + @{@"key1" : @"value1"}, + @{@"key2" : @"value2"}, + @{@"key3" : @"value3"}, + ])); + XCTAssertEqualObjects(FIRQuerySnapshotGetDocChangesData(result), (@[ + @[ @(FIRDocumentChangeTypeAdded), @"doc1", @{@"key1" : @"value1"} ], + @[ @(FIRDocumentChangeTypeAdded), @"doc2", @{@"key2" : @"value2"} ], + @[ @(FIRDocumentChangeTypeAdded), @"doc3", @{@"key3" : @"value3"} ] + ])); +} + +- (void)testGetDocumentWhileOfflineCacheOnly { + FIRDocumentReference *doc = [self documentRef]; + + // set document to a known value + NSDictionary *initialData = @{@"key1" : @"value1"}; + [self writeDocumentRef:doc data:initialData]; + + // go offline for the rest of this test + [self disableNetwork]; + + // update the doc (though don't wait for a server response. We're offline; so + // that ain't happening!). This allows us to further distinguished cached vs + // server responses below. + NSDictionary *newData = @{@"key2" : @"value2"}; + [doc setData:newData + completion:^(NSError *_Nullable error) { + XCTFail("Because we're offline, this should never occur."); + }]; + + // get doc and ensure it exists, *is* from the cache, and matches the + // newData. + FIRDocumentSnapshot *result = + [self readDocumentForRef:doc + options:[[FIRGetOptions alloc] initWithSource:FIRGetSourceCache]]; + XCTAssertTrue(result.exists); + XCTAssertTrue(result.metadata.fromCache); + XCTAssertTrue(result.metadata.hasPendingWrites); + XCTAssertEqualObjects(result.data, newData); +} + +- (void)testGetCollectionWhileOfflineCacheOnly { + FIRCollectionReference *col = [self collectionRef]; + + // set a few documents to a known value + NSDictionary *> *initialDocs = @{ + @"doc1" : @{@"key1" : @"value1"}, + @"doc2" : @{@"key2" : @"value2"}, + @"doc3" : @{@"key3" : @"value3"}, + }; + [self writeAllDocuments:initialDocs toCollection:col]; + + // go offline for the rest of this test + [self disableNetwork]; + + // update the docs (though don't wait for a server response. We're offline; so + // that ain't happening!). This allows us to further distinguished cached vs + // server responses below. + [[col documentWithPath:@"doc2"] setData:@{@"key2b" : @"value2b"} options:FIRSetOptions.merge]; + [[col documentWithPath:@"doc3"] setData:@{@"key3b" : @"value3b"}]; + [[col documentWithPath:@"doc4"] setData:@{@"key4" : @"value4"}]; + + // get docs and ensure they *are* from the cache, and matches the updated + // data. + FIRQuerySnapshot *result = + [self readDocumentSetForRef:col + options:[[FIRGetOptions alloc] initWithSource:FIRGetSourceCache]]; + XCTAssertTrue(result.metadata.fromCache); + XCTAssertTrue(result.metadata.hasPendingWrites); + XCTAssertEqualObjects(FIRQuerySnapshotGetData(result), (@[ + @{@"key1" : @"value1"}, @{@"key2" : @"value2", @"key2b" : @"value2b"}, + @{@"key3b" : @"value3b"}, @{@"key4" : @"value4"} + ])); + XCTAssertEqualObjects( + FIRQuerySnapshotGetDocChangesData(result), (@[ + @[ @(FIRDocumentChangeTypeAdded), @"doc1", @{@"key1" : @"value1"} ], + @[ @(FIRDocumentChangeTypeAdded), @"doc2", @{@"key2" : @"value2", @"key2b" : @"value2b"} ], + @[ @(FIRDocumentChangeTypeAdded), @"doc3", @{@"key3b" : @"value3b"} ], + @[ @(FIRDocumentChangeTypeAdded), @"doc4", @{@"key4" : @"value4"} ] + ])); +} + +- (void)testGetDocumentWhileOnlineServerOnly { + FIRDocumentReference *doc = [self documentRef]; + + // set document to a known value + NSDictionary *initialData = @{@"key" : @"value"}; + [self writeDocumentRef:doc data:initialData]; + + // get doc and ensure that it exists, is *not* from the cache, and matches + // the initialData. + FIRDocumentSnapshot *result = + [self readDocumentForRef:doc + options:[[FIRGetOptions alloc] initWithSource:FIRGetSourceServer]]; + XCTAssertTrue(result.exists); + XCTAssertFalse(result.metadata.fromCache); + XCTAssertFalse(result.metadata.hasPendingWrites); + XCTAssertEqualObjects(result.data, initialData); +} + +- (void)testGetCollectionWhileOnlineServerOnly { + FIRCollectionReference *col = [self collectionRef]; + + // set a few documents to a known value + NSDictionary *> *initialDocs = @{ + @"doc1" : @{@"key1" : @"value1"}, + @"doc2" : @{@"key2" : @"value2"}, + @"doc3" : @{@"key3" : @"value3"}, + }; + [self writeAllDocuments:initialDocs toCollection:col]; + + // get docs and ensure they are *not* from the cache, and matches the + // initialData. + FIRQuerySnapshot *result = + [self readDocumentSetForRef:col + options:[[FIRGetOptions alloc] initWithSource:FIRGetSourceServer]]; + XCTAssertFalse(result.metadata.fromCache); + XCTAssertFalse(result.metadata.hasPendingWrites); + XCTAssertEqualObjects(FIRQuerySnapshotGetData(result), (@[ + @{@"key1" : @"value1"}, + @{@"key2" : @"value2"}, + @{@"key3" : @"value3"}, + ])); + XCTAssertEqualObjects(FIRQuerySnapshotGetDocChangesData(result), (@[ + @[ @(FIRDocumentChangeTypeAdded), @"doc1", @{@"key1" : @"value1"} ], + @[ @(FIRDocumentChangeTypeAdded), @"doc2", @{@"key2" : @"value2"} ], + @[ @(FIRDocumentChangeTypeAdded), @"doc3", @{@"key3" : @"value3"} ] + ])); +} + +- (void)testGetDocumentWhileOfflineServerOnly { + FIRDocumentReference *doc = [self documentRef]; + + // set document to a known value + NSDictionary *initialData = @{@"key1" : @"value1"}; + [self writeDocumentRef:doc data:initialData]; + + // go offline for the rest of this test + [self disableNetwork]; + + // attempt to get doc and ensure it cannot be retreived + XCTestExpectation *failedGetDocCompletion = [self expectationWithDescription:@"failedGetDoc"]; + [doc getDocumentWithOptions:[[FIRGetOptions alloc] initWithSource:FIRGetSourceServer] + completion:^(FIRDocumentSnapshot *snapshot, NSError *error) { + XCTAssertNotNil(error); + XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); + XCTAssertEqual(error.code, FIRFirestoreErrorCodeUnavailable); + [failedGetDocCompletion fulfill]; + }]; + [self awaitExpectations]; +} + +- (void)testGetCollectionWhileOfflineServerOnly { + FIRCollectionReference *col = [self collectionRef]; + + // set a few documents to a known value + NSDictionary *> *initialDocs = @{ + @"doc1" : @{@"key1" : @"value1"}, + @"doc2" : @{@"key2" : @"value2"}, + @"doc3" : @{@"key3" : @"value3"}, + }; + [self writeAllDocuments:initialDocs toCollection:col]; + + // go offline for the rest of this test + [self disableNetwork]; + + // attempt to get docs and ensure they cannot be retreived + XCTestExpectation *failedGetDocsCompletion = [self expectationWithDescription:@"failedGetDocs"]; + [col getDocumentsWithOptions:[[FIRGetOptions alloc] initWithSource:FIRGetSourceServer] + completion:^(FIRQuerySnapshot *snapshot, NSError *error) { + XCTAssertNotNil(error); + XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); + XCTAssertEqual(error.code, FIRFirestoreErrorCodeUnavailable); + [failedGetDocsCompletion fulfill]; + }]; + [self awaitExpectations]; +} + +- (void)testGetDocumentWhileOfflineWithDifferentGetOptions { + FIRDocumentReference *doc = [self documentRef]; + + // set document to a known value + NSDictionary *initialData = @{@"key1" : @"value1"}; + [self writeDocumentRef:doc data:initialData]; + + // go offline for the rest of this test + [self disableNetwork]; + + // update the doc (though don't wait for a server response. We're offline; so + // that ain't happening!). This allows us to further distinguished cached vs + // server responses below. + NSDictionary *newData = @{@"key2" : @"value2"}; + [doc setData:newData + completion:^(NSError *_Nullable error) { + XCTAssertTrue(false, "Because we're offline, this should never occur."); + }]; + + // Create an initial listener for this query (to attempt to disrupt the gets below) and wait for + // the listener to deliver its initial snapshot before continuing. + XCTestExpectation *listenerReady = [self expectationWithDescription:@"listenerReady"]; + [doc addSnapshotListener:^(FIRDocumentSnapshot *snapshot, NSError *error) { + [listenerReady fulfill]; + }]; + [self awaitExpectations]; + + // get doc (from cache) and ensure it exists, *is* from the cache, and + // matches the newData. + FIRDocumentSnapshot *result = + [self readDocumentForRef:doc + options:[[FIRGetOptions alloc] initWithSource:FIRGetSourceCache]]; + XCTAssertTrue(result.exists); + XCTAssertTrue(result.metadata.fromCache); + XCTAssertTrue(result.metadata.hasPendingWrites); + XCTAssertEqualObjects(result.data, newData); + + // attempt to get doc (with default get options) + result = [self readDocumentForRef:doc + options:[[FIRGetOptions alloc] initWithSource:FIRGetSourceDefault]]; + XCTAssertTrue(result.exists); + XCTAssertTrue(result.metadata.fromCache); + XCTAssertTrue(result.metadata.hasPendingWrites); + XCTAssertEqualObjects(result.data, newData); + + // attempt to get doc (from the server) and ensure it cannot be retreived + XCTestExpectation *failedGetDocCompletion = [self expectationWithDescription:@"failedGetDoc"]; + [doc getDocumentWithOptions:[[FIRGetOptions alloc] initWithSource:FIRGetSourceServer] + completion:^(FIRDocumentSnapshot *snapshot, NSError *error) { + XCTAssertNotNil(error); + XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); + XCTAssertEqual(error.code, FIRFirestoreErrorCodeUnavailable); + [failedGetDocCompletion fulfill]; + }]; + [self awaitExpectations]; +} + +- (void)testGetCollectionWhileOfflineWithDifferentGetOptions { + FIRCollectionReference *col = [self collectionRef]; + + // set a few documents to a known value + NSDictionary *> *initialDocs = @{ + @"doc1" : @{@"key1" : @"value1"}, + @"doc2" : @{@"key2" : @"value2"}, + @"doc3" : @{@"key3" : @"value3"}, + }; + [self writeAllDocuments:initialDocs toCollection:col]; + + // go offline for the rest of this test + [self disableNetwork]; + + // update the docs (though don't wait for a server response. We're offline; so + // that ain't happening!). This allows us to further distinguished cached vs + // server responses below. + [[col documentWithPath:@"doc2"] setData:@{@"key2b" : @"value2b"} options:FIRSetOptions.merge]; + [[col documentWithPath:@"doc3"] setData:@{@"key3b" : @"value3b"}]; + [[col documentWithPath:@"doc4"] setData:@{@"key4" : @"value4"}]; + + // Create an initial listener for this query (to attempt to disrupt the gets + // below) and wait for the listener to deliver its initial snapshot before + // continuing. + XCTestExpectation *listenerReady = [self expectationWithDescription:@"listenerReady"]; + [col addSnapshotListener:^(FIRQuerySnapshot *snapshot, NSError *error) { + [listenerReady fulfill]; + }]; + [self awaitExpectations]; + + // get docs (from cache) and ensure they *are* from the cache, and + // matches the updated data. + FIRQuerySnapshot *result = + [self readDocumentSetForRef:col + options:[[FIRGetOptions alloc] initWithSource:FIRGetSourceCache]]; + XCTAssertTrue(result.metadata.fromCache); + XCTAssertTrue(result.metadata.hasPendingWrites); + XCTAssertEqualObjects(FIRQuerySnapshotGetData(result), (@[ + @{@"key1" : @"value1"}, @{@"key2" : @"value2", @"key2b" : @"value2b"}, + @{@"key3b" : @"value3b"}, @{@"key4" : @"value4"} + ])); + XCTAssertEqualObjects( + FIRQuerySnapshotGetDocChangesData(result), (@[ + @[ @(FIRDocumentChangeTypeAdded), @"doc1", @{@"key1" : @"value1"} ], + @[ @(FIRDocumentChangeTypeAdded), @"doc2", @{@"key2" : @"value2", @"key2b" : @"value2b"} ], + @[ @(FIRDocumentChangeTypeAdded), @"doc3", @{@"key3b" : @"value3b"} ], + @[ @(FIRDocumentChangeTypeAdded), @"doc4", @{@"key4" : @"value4"} ] + ])); + + // attempt to get docs (with default get options) + result = [self readDocumentSetForRef:col + options:[[FIRGetOptions alloc] initWithSource:FIRGetSourceDefault]]; + XCTAssertTrue(result.metadata.fromCache); + XCTAssertEqualObjects(FIRQuerySnapshotGetData(result), (@[ + @{@"key1" : @"value1"}, @{@"key2" : @"value2", @"key2b" : @"value2b"}, + @{@"key3b" : @"value3b"}, @{@"key4" : @"value4"} + ])); + XCTAssertEqualObjects( + FIRQuerySnapshotGetDocChangesData(result), (@[ + @[ @(FIRDocumentChangeTypeAdded), @"doc1", @{@"key1" : @"value1"} ], + @[ @(FIRDocumentChangeTypeAdded), @"doc2", @{@"key2" : @"value2", @"key2b" : @"value2b"} ], + @[ @(FIRDocumentChangeTypeAdded), @"doc3", @{@"key3b" : @"value3b"} ], + @[ @(FIRDocumentChangeTypeAdded), @"doc4", @{@"key4" : @"value4"} ] + ])); + + // attempt to get docs (from the server) and ensure they cannot be retreived + XCTestExpectation *failedGetDocsCompletion = [self expectationWithDescription:@"failedGetDocs"]; + [col getDocumentsWithOptions:[[FIRGetOptions alloc] initWithSource:FIRGetSourceServer] + completion:^(FIRQuerySnapshot *snapshot, NSError *error) { + XCTAssertNotNil(error); + XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); + XCTAssertEqual(error.code, FIRFirestoreErrorCodeUnavailable); + [failedGetDocsCompletion fulfill]; + }]; + [self awaitExpectations]; +} + +- (void)testGetNonExistingDocWhileOnlineWithDefaultGetOptions { + FIRDocumentReference *doc = [self documentRef]; + + // get doc and ensure that it does not exist and is *not* from the cache. + FIRDocumentSnapshot *snapshot = [self readDocumentForRef:doc]; + XCTAssertFalse(snapshot.exists); + XCTAssertFalse(snapshot.metadata.fromCache); + XCTAssertFalse(snapshot.metadata.hasPendingWrites); +} + +- (void)testGetNonExistingCollectionWhileOnlineWithDefaultGetOptions { + FIRCollectionReference *col = [self collectionRef]; + + // get collection and ensure it's empty and that it's *not* from the cache. + FIRQuerySnapshot *snapshot = [self readDocumentSetForRef:col]; + XCTAssertEqual(snapshot.count, 0); + XCTAssertEqual(snapshot.documentChanges.count, 0); + XCTAssertFalse(snapshot.metadata.fromCache); + XCTAssertFalse(snapshot.metadata.hasPendingWrites); +} + +- (void)testGetNonExistingDocWhileOfflineWithDefaultGetOptions { + FIRDocumentReference *doc = [self documentRef]; + + // go offline for the rest of this test + [self disableNetwork]; + + // attempt to get doc. Currently, this is expected to fail. In the future, we + // might consider adding support for negative cache hits so that we know + // certain documents *don't* exist. + XCTestExpectation *getNonExistingDocCompletion = + [self expectationWithDescription:@"getNonExistingDoc"]; + [doc getDocumentWithCompletion:^(FIRDocumentSnapshot *snapshot, NSError *error) { + XCTAssertNotNil(error); + XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); + XCTAssertEqual(error.code, FIRFirestoreErrorCodeUnavailable); + [getNonExistingDocCompletion fulfill]; + }]; + [self awaitExpectations]; +} + +- (void)testGetNonExistingCollectionWhileOfflineWithDefaultGetOptions { + FIRCollectionReference *col = [self collectionRef]; + + // go offline for the rest of this test + [self disableNetwork]; + + // get collection and ensure it's empty and that it *is* from the cache. + FIRQuerySnapshot *snapshot = [self readDocumentSetForRef:col]; + XCTAssertEqual(snapshot.count, 0); + XCTAssertEqual(snapshot.documentChanges.count, 0); + XCTAssertTrue(snapshot.metadata.fromCache); + XCTAssertFalse(snapshot.metadata.hasPendingWrites); +} + +- (void)testGetNonExistingDocWhileOnlineCacheOnly { + FIRDocumentReference *doc = [self documentRef]; + + // attempt to get doc. Currently, this is expected to fail. In the future, we + // might consider adding support for negative cache hits so that we know + // certain documents *don't* exist. + XCTestExpectation *getNonExistingDocCompletion = + [self expectationWithDescription:@"getNonExistingDoc"]; + [doc getDocumentWithOptions:[[FIRGetOptions alloc] initWithSource:FIRGetSourceCache] + completion:^(FIRDocumentSnapshot *snapshot, NSError *error) { + XCTAssertNotNil(error); + XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); + XCTAssertEqual(error.code, FIRFirestoreErrorCodeUnavailable); + [getNonExistingDocCompletion fulfill]; + }]; + [self awaitExpectations]; +} + +- (void)testGetNonExistingCollectionWhileOnlineCacheOnly { + FIRCollectionReference *col = [self collectionRef]; + + // get collection and ensure it's empty and that it *is* from the cache. + FIRQuerySnapshot *snapshot = + [self readDocumentSetForRef:col + options:[[FIRGetOptions alloc] initWithSource:FIRGetSourceCache]]; + XCTAssertEqual(snapshot.count, 0); + XCTAssertEqual(snapshot.documentChanges.count, 0); + XCTAssertTrue(snapshot.metadata.fromCache); + XCTAssertFalse(snapshot.metadata.hasPendingWrites); +} + +- (void)testGetNonExistingDocWhileOfflineCacheOnly { + FIRDocumentReference *doc = [self documentRef]; + + // go offline for the rest of this test + [self disableNetwork]; + + // attempt to get doc. Currently, this is expected to fail. In the future, we + // might consider adding support for negative cache hits so that we know + // certain documents *don't* exist. + XCTestExpectation *getNonExistingDocCompletion = + [self expectationWithDescription:@"getNonExistingDoc"]; + [doc getDocumentWithOptions:[[FIRGetOptions alloc] initWithSource:FIRGetSourceCache] + completion:^(FIRDocumentSnapshot *snapshot, NSError *error) { + XCTAssertNotNil(error); + XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); + XCTAssertEqual(error.code, FIRFirestoreErrorCodeUnavailable); + [getNonExistingDocCompletion fulfill]; + }]; + [self awaitExpectations]; +} + +- (void)testGetNonExistingCollectionWhileOfflineCacheOnly { + FIRCollectionReference *col = [self collectionRef]; + + // go offline for the rest of this test + [self disableNetwork]; + + // get collection and ensure it's empty and that it *is* from the cache. + FIRQuerySnapshot *snapshot = + [self readDocumentSetForRef:col + options:[[FIRGetOptions alloc] initWithSource:FIRGetSourceCache]]; + XCTAssertEqual(snapshot.count, 0); + XCTAssertEqual(snapshot.documentChanges.count, 0); + XCTAssertTrue(snapshot.metadata.fromCache); + XCTAssertFalse(snapshot.metadata.hasPendingWrites); +} + +- (void)testGetNonExistingDocWhileOnlineServerOnly { + FIRDocumentReference *doc = [self documentRef]; + + // get doc and ensure that it does not exist and is *not* from the cache. + FIRDocumentSnapshot *snapshot = + [self readDocumentForRef:doc + options:[[FIRGetOptions alloc] initWithSource:FIRGetSourceServer]]; + XCTAssertFalse(snapshot.exists); + XCTAssertFalse(snapshot.metadata.fromCache); + XCTAssertFalse(snapshot.metadata.hasPendingWrites); +} + +- (void)testGetNonExistingCollectionWhileOnlineServerOnly { + FIRCollectionReference *col = [self collectionRef]; + + // get collection and ensure that it's empty and that it's *not* from the cache. + FIRQuerySnapshot *snapshot = + [self readDocumentSetForRef:col + options:[[FIRGetOptions alloc] initWithSource:FIRGetSourceServer]]; + XCTAssertEqual(snapshot.count, 0); + XCTAssertEqual(snapshot.documentChanges.count, 0); + XCTAssertFalse(snapshot.metadata.fromCache); + XCTAssertFalse(snapshot.metadata.hasPendingWrites); +} + +- (void)testGetNonExistingDocWhileOfflineServerOnly { + FIRDocumentReference *doc = [self documentRef]; + + // go offline for the rest of this test + [self disableNetwork]; + + // attempt to get doc. Currently, this is expected to fail. In the future, we + // might consider adding support for negative cache hits so that we know + // certain documents *don't* exist. + XCTestExpectation *getNonExistingDocCompletion = + [self expectationWithDescription:@"getNonExistingDoc"]; + [doc getDocumentWithOptions:[[FIRGetOptions alloc] initWithSource:FIRGetSourceServer] + completion:^(FIRDocumentSnapshot *snapshot, NSError *error) { + XCTAssertNotNil(error); + XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); + XCTAssertEqual(error.code, FIRFirestoreErrorCodeUnavailable); + [getNonExistingDocCompletion fulfill]; + }]; + [self awaitExpectations]; +} + +- (void)testGetNonExistingCollectionWhileOfflineServerOnly { + FIRCollectionReference *col = [self collectionRef]; + + // go offline for the rest of this test + [self disableNetwork]; + + // attempt to get collection and ensure that it cannot be retreived + XCTestExpectation *failedGetDocsCompletion = [self expectationWithDescription:@"failedGetDocs"]; + [col getDocumentsWithOptions:[[FIRGetOptions alloc] initWithSource:FIRGetSourceServer] + completion:^(FIRQuerySnapshot *snapshot, NSError *error) { + XCTAssertNotNil(error); + XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); + XCTAssertEqual(error.code, FIRFirestoreErrorCodeUnavailable); + [failedGetDocsCompletion fulfill]; + }]; + [self awaitExpectations]; +} + +@end diff --git a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.h b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.h index ac5425308ce..fa12d868304 100644 --- a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.h +++ b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.h @@ -23,6 +23,7 @@ @class FIRCollectionReference; @class FIRDocumentSnapshot; @class FIRDocumentReference; +@class FIRGetOptions; @class FIRQuerySnapshot; @class FIRFirestore; @class FIRFirestoreSettings; @@ -72,8 +73,13 @@ extern "C" { - (FIRDocumentSnapshot *)readDocumentForRef:(FIRDocumentReference *)ref; +- (FIRDocumentSnapshot *)readDocumentForRef:(FIRDocumentReference *)ref + options:(FIRGetOptions *)options; + - (FIRQuerySnapshot *)readDocumentSetForRef:(FIRQuery *)query; +- (FIRQuerySnapshot *)readDocumentSetForRef:(FIRQuery *)query options:(FIRGetOptions *)options; + - (FIRDocumentSnapshot *)readSnapshotForRef:(FIRDocumentReference *)query requireOnline:(BOOL)online; @@ -105,6 +111,10 @@ NSArray *> *FIRQuerySnapshotGetData(FIRQuerySnapsho /** Converts the FIRQuerySnapshot to an NSArray containing the document IDs in order. */ NSArray *FIRQuerySnapshotGetIDs(FIRQuerySnapshot *docs); +/** Converts the FIRQuerySnapshot to an NSArray containing an NSArray containing the doc change data + * in order of { type, doc title, doc data }. */ +NSArray *> *FIRQuerySnapshotGetDocChangesData(FIRQuerySnapshot *docs); + #if __cplusplus } // extern "C" #endif diff --git a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm index ef15056c5af..ac98e877864 100644 --- a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm +++ b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm @@ -209,28 +209,39 @@ - (void)readerAndWriterOnDocumentRef:(void (^)(NSString *path, } - (FIRDocumentSnapshot *)readDocumentForRef:(FIRDocumentReference *)ref { + return [self readDocumentForRef:ref options:[FIRGetOptions defaultOptions]]; +} + +- (FIRDocumentSnapshot *)readDocumentForRef:(FIRDocumentReference *)ref + options:(FIRGetOptions *)options { __block FIRDocumentSnapshot *result; XCTestExpectation *expectation = [self expectationWithDescription:@"getData"]; - [ref getDocumentWithCompletion:^(FIRDocumentSnapshot *doc, NSError *_Nullable error) { - XCTAssertNil(error); - result = doc; - [expectation fulfill]; - }]; + [ref getDocumentWithOptions:options + completion:^(FIRDocumentSnapshot *doc, NSError *_Nullable error) { + XCTAssertNil(error); + result = doc; + [expectation fulfill]; + }]; [self awaitExpectations]; return result; } - (FIRQuerySnapshot *)readDocumentSetForRef:(FIRQuery *)query { + return [self readDocumentSetForRef:query options:[FIRGetOptions defaultOptions]]; +} + +- (FIRQuerySnapshot *)readDocumentSetForRef:(FIRQuery *)query options:(FIRGetOptions *)options { __block FIRQuerySnapshot *result; XCTestExpectation *expectation = [self expectationWithDescription:@"getData"]; - [query getDocumentsWithCompletion:^(FIRQuerySnapshot *documentSet, NSError *error) { - XCTAssertNil(error); - result = documentSet; - [expectation fulfill]; - }]; + [query getDocumentsWithOptions:options + completion:^(FIRQuerySnapshot *documentSet, NSError *error) { + XCTAssertNil(error); + result = documentSet; + [expectation fulfill]; + }]; [self awaitExpectations]; return result; @@ -314,6 +325,18 @@ - (void)waitUntil:(BOOL (^)())predicate { return result; } +extern "C" NSArray *> *FIRQuerySnapshotGetDocChangesData(FIRQuerySnapshot *docs) { + NSMutableArray *> *result = [NSMutableArray array]; + for (FIRDocumentChange *docChange in docs.documentChanges) { + NSMutableArray *docChangeData = [NSMutableArray array]; + [docChangeData addObject:@(docChange.type)]; + [docChangeData addObject:docChange.document.documentID]; + [docChangeData addObject:docChange.document.data]; + [result addObject:docChangeData]; + } + return result; +} + @end NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/API/FIRDocumentReference.m b/Firestore/Source/API/FIRDocumentReference.m index 87e66318a03..464217a4fa3 100644 --- a/Firestore/Source/API/FIRDocumentReference.m +++ b/Firestore/Source/API/FIRDocumentReference.m @@ -24,6 +24,7 @@ #import "Firestore/Source/API/FIRDocumentReference+Internal.h" #import "Firestore/Source/API/FIRDocumentSnapshot+Internal.h" #import "Firestore/Source/API/FIRFirestore+Internal.h" +#import "Firestore/Source/API/FIRGetOptions+Internal.h" #import "Firestore/Source/API/FIRListenerRegistration+Internal.h" #import "Firestore/Source/API/FIRSetOptions+Internal.h" #import "Firestore/Source/API/FSTUserDataConverter.h" @@ -209,6 +210,17 @@ - (void)deleteDocumentWithCompletion:(nullable void (^)(NSError *_Nullable error - (void)getDocumentWithCompletion:(void (^)(FIRDocumentSnapshot *_Nullable document, NSError *_Nullable error))completion { + return [self getDocumentWithOptions:[FIRGetOptions defaultOptions] completion:completion]; +} + +- (void)getDocumentWithOptions:(FIRGetOptions *)options + completion:(void (^)(FIRDocumentSnapshot *_Nullable document, + NSError *_Nullable error))completion { + if (options.source == FIRGetSourceCache) { + [self.firestore.client getDocumentFromLocalCache:self completion:completion]; + return; + } + FSTListenOptions *listenOptions = [[FSTListenOptions alloc] initWithIncludeQueryMetadataChanges:YES includeDocumentMetadataChanges:YES @@ -236,7 +248,6 @@ - (void)getDocumentWithCompletion:(void (^)(FIRDocumentSnapshot *_Nullable docum // offline. // 2) Actually call the completion handler with an error if the document doesn't exist when // you are offline. - // TODO(dimond): Use proper error domain completion(nil, [NSError errorWithDomain:FIRFirestoreErrorDomain code:FIRFirestoreErrorCodeUnavailable @@ -244,6 +255,18 @@ - (void)getDocumentWithCompletion:(void (^)(FIRDocumentSnapshot *_Nullable docum NSLocalizedDescriptionKey : @"Failed to get document because the client is offline.", }]); + } else if (snapshot.exists && snapshot.metadata.fromCache && + options.source == FIRGetSourceServer) { + completion( + nil, [NSError errorWithDomain:FIRFirestoreErrorDomain + code:FIRFirestoreErrorCodeUnavailable + userInfo:@{ + NSLocalizedDescriptionKey : + @"Failed to get document from server. (However, this " + @"document does exist in the local cache. Run again " + @"without setting FIRGetSourceServer in the FIRGetOptions to " + @"retrieve the cached document.)" + }]); } else { completion(snapshot, nil); } diff --git a/Firestore/Source/API/FIRGetOptions+Internal.h b/Firestore/Source/API/FIRGetOptions+Internal.h new file mode 100644 index 00000000000..1e8a479c837 --- /dev/null +++ b/Firestore/Source/API/FIRGetOptions+Internal.h @@ -0,0 +1,28 @@ +/* + * Copyright 2018 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 "FIRGetOptions.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface FIRGetOptions () + +/** Where getDocument[s] calls should get their data from. */ +@property(nonatomic, readonly, getter=source) FIRGetSource source; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/API/FIRGetOptions.m b/Firestore/Source/API/FIRGetOptions.m new file mode 100644 index 00000000000..b7eb5e2ece9 --- /dev/null +++ b/Firestore/Source/API/FIRGetOptions.m @@ -0,0 +1,36 @@ +/* + * Copyright 2018 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/Source/API/FIRGetOptions+Internal.h" + +NS_ASSUME_NONNULL_BEGIN + +@implementation FIRGetOptions + ++ (FIRGetOptions *)defaultOptions { + return [[FIRGetOptions alloc] initWithSource:FIRGetSourceDefault]; +} + +- (instancetype)initWithSource:(FIRGetSource)source { + if (self = [super init]) { + _source = source; + } + return self; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/API/FIRQuery.m b/Firestore/Source/API/FIRQuery.m index 2feca393948..06fa5e8ae3e 100644 --- a/Firestore/Source/API/FIRQuery.m +++ b/Firestore/Source/API/FIRQuery.m @@ -17,10 +17,12 @@ #import "FIRQuery.h" #import "FIRDocumentReference.h" +#import "FIRFirestoreErrors.h" #import "Firestore/Source/API/FIRDocumentReference+Internal.h" #import "Firestore/Source/API/FIRDocumentSnapshot+Internal.h" #import "Firestore/Source/API/FIRFieldPath+Internal.h" #import "Firestore/Source/API/FIRFirestore+Internal.h" +#import "Firestore/Source/API/FIRGetOptions+Internal.h" #import "Firestore/Source/API/FIRListenerRegistration+Internal.h" #import "Firestore/Source/API/FIRQuery+Internal.h" #import "Firestore/Source/API/FIRQuerySnapshot+Internal.h" @@ -130,9 +132,21 @@ - (NSUInteger)hash { - (void)getDocumentsWithCompletion:(void (^)(FIRQuerySnapshot *_Nullable snapshot, NSError *_Nullable error))completion { - FSTListenOptions *options = [[FSTListenOptions alloc] initWithIncludeQueryMetadataChanges:YES - includeDocumentMetadataChanges:YES - waitForSyncWhenOnline:YES]; + [self getDocumentsWithOptions:[FIRGetOptions defaultOptions] completion:completion]; +} + +- (void)getDocumentsWithOptions:(FIRGetOptions *)options + completion:(void (^)(FIRQuerySnapshot *_Nullable snapshot, + NSError *_Nullable error))completion { + if (options.source == FIRGetSourceCache) { + [self.firestore.client getDocumentsFromLocalCache:self completion:completion]; + return; + } + + FSTListenOptions *listenOptions = + [[FSTListenOptions alloc] initWithIncludeQueryMetadataChanges:YES + includeDocumentMetadataChanges:YES + waitForSyncWhenOnline:YES]; dispatch_semaphore_t registered = dispatch_semaphore_create(0); __block id listenerRegistration; @@ -147,10 +161,24 @@ - (void)getDocumentsWithCompletion:(void (^)(FIRQuerySnapshot *_Nullable snapsho dispatch_semaphore_wait(registered, DISPATCH_TIME_FOREVER); [listenerRegistration remove]; - completion(snapshot, nil); + if (snapshot.metadata.fromCache && options.source == FIRGetSourceServer) { + completion( + nil, [NSError errorWithDomain:FIRFirestoreErrorDomain + code:FIRFirestoreErrorCodeUnavailable + userInfo:@{ + NSLocalizedDescriptionKey : + @"Failed to get documents from server. (However, these " + @"documents may exist in the local cache. Run again " + @"without setting FIRGetSourceServer in the FIRGetOptions to " + @"retrieve the cached documents.)" + }]); + } else { + completion(snapshot, nil); + } }; - listenerRegistration = [self addSnapshotListenerInternalWithOptions:options listener:listener]; + listenerRegistration = + [self addSnapshotListenerInternalWithOptions:listenOptions listener:listener]; dispatch_semaphore_signal(registered); } diff --git a/Firestore/Source/Core/FSTFirestoreClient.h b/Firestore/Source/Core/FSTFirestoreClient.h index 0ecf2f6b108..d7b3304a61c 100644 --- a/Firestore/Source/Core/FSTFirestoreClient.h +++ b/Firestore/Source/Core/FSTFirestoreClient.h @@ -20,6 +20,10 @@ #import "Firestore/Source/Core/FSTViewSnapshot.h" #import "Firestore/Source/Remote/FSTRemoteStore.h" +@class FIRDocumentReference; +@class FIRDocumentSnapshot; +@class FIRQuery; +@class FIRQuerySnapshot; @class FSTDatabaseID; @class FSTDatabaseInfo; @class FSTDispatchQueue; @@ -70,6 +74,22 @@ NS_ASSUME_NONNULL_BEGIN /** Stops listening to a query previously listened to. */ - (void)removeListener:(FSTQueryListener *)listener; +/** + * Retrieves a document from the cache via the indicated completion. If the doc + * doesn't exist, an error will be sent to the completion. + */ +- (void)getDocumentFromLocalCache:(FIRDocumentReference *)doc + completion:(void (^)(FIRDocumentSnapshot *_Nullable document, + NSError *_Nullable error))completion; + +/** + * Retrieves a (possibly empty) set of documents from the cache via the + * indicated completion. + */ +- (void)getDocumentsFromLocalCache:(FIRQuery *)query + completion:(void (^)(FIRQuerySnapshot *_Nullable query, + NSError *_Nullable error))completion; + /** Write mutations. completion will be notified when it's written to the backend. */ - (void)writeMutations:(NSArray *)mutations completion:(nullable FSTVoidErrorBlock)completion; diff --git a/Firestore/Source/Core/FSTFirestoreClient.m b/Firestore/Source/Core/FSTFirestoreClient.m index fff644da62d..6e076c4ec7f 100644 --- a/Firestore/Source/Core/FSTFirestoreClient.m +++ b/Firestore/Source/Core/FSTFirestoreClient.m @@ -16,17 +16,27 @@ #import "Firestore/Source/Core/FSTFirestoreClient.h" +#import "FIRFirestoreErrors.h" +#import "Firestore/Source/API/FIRDocumentReference+Internal.h" +#import "Firestore/Source/API/FIRDocumentSnapshot+Internal.h" +#import "Firestore/Source/API/FIRQuery+Internal.h" +#import "Firestore/Source/API/FIRQuerySnapshot+Internal.h" +#import "Firestore/Source/API/FIRSnapshotMetadata+Internal.h" #import "Firestore/Source/Auth/FSTCredentialsProvider.h" #import "Firestore/Source/Core/FSTDatabaseInfo.h" #import "Firestore/Source/Core/FSTEventManager.h" +#import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Core/FSTSyncEngine.h" #import "Firestore/Source/Core/FSTTransaction.h" +#import "Firestore/Source/Core/FSTView.h" #import "Firestore/Source/Local/FSTEagerGarbageCollector.h" #import "Firestore/Source/Local/FSTLevelDB.h" #import "Firestore/Source/Local/FSTLocalSerializer.h" #import "Firestore/Source/Local/FSTLocalStore.h" #import "Firestore/Source/Local/FSTMemoryPersistence.h" #import "Firestore/Source/Local/FSTNoOpGarbageCollector.h" +#import "Firestore/Source/Model/FSTDocument.h" +#import "Firestore/Source/Model/FSTDocumentSet.h" #import "Firestore/Source/Remote/FSTDatastore.h" #import "Firestore/Source/Remote/FSTRemoteStore.h" #import "Firestore/Source/Remote/FSTSerializerBeta.h" @@ -249,6 +259,60 @@ - (void)removeListener:(FSTQueryListener *)listener { }]; } +- (void)getDocumentFromLocalCache:(FIRDocumentReference *)doc + completion:(void (^)(FIRDocumentSnapshot *_Nullable document, + NSError *_Nullable error))completion { + [self.workerDispatchQueue dispatchAsync:^{ + FSTMaybeDocument *maybeDoc = [self.localStore readDocument:doc.key]; + if (maybeDoc) { + completion([FIRDocumentSnapshot snapshotWithFirestore:doc.firestore + documentKey:doc.key + document:(FSTDocument *)maybeDoc + fromCache:YES], + nil); + } else { + completion( + nil, + [NSError errorWithDomain:FIRFirestoreErrorDomain + code:FIRFirestoreErrorCodeUnavailable + userInfo:@{ + NSLocalizedDescriptionKey : + @"Failed to get document from cache. (However, this " + @"document may exist on the server. Run again without " + @"setting FIRGetSourceCache in the FIRGetOptions to attempt to " + @"retrieve the document from the server.)", + }]); + } + }]; +} + +- (void)getDocumentsFromLocalCache:(FIRQuery *)query + completion:(void (^)(FIRQuerySnapshot *_Nullable query, + NSError *_Nullable error))completion { + [self.workerDispatchQueue dispatchAsync:^{ + + FSTDocumentDictionary *docs = [self.localStore executeQuery:query.query]; + FSTDocumentKeySet *remoteKeys = [FSTDocumentKeySet keySet]; + + FSTView *view = [[FSTView alloc] initWithQuery:query.query remoteDocuments:remoteKeys]; + FSTViewDocumentChanges *viewDocChanges = [view computeChangesWithDocuments:docs]; + FSTViewChange *viewChange = [view applyChangesToDocuments:viewDocChanges]; + FSTAssert(viewChange.limboChanges.count == 0, + @"View returned limbo documents during local-only query execution."); + + FSTViewSnapshot *snapshot = viewChange.snapshot; + FIRSnapshotMetadata *metadata = + [FIRSnapshotMetadata snapshotMetadataWithPendingWrites:snapshot.hasPendingWrites + fromCache:snapshot.fromCache]; + + completion([FIRQuerySnapshot snapshotWithFirestore:query.firestore + originalQuery:query.query + snapshot:snapshot + metadata:metadata], + nil); + }]; +} + - (void)writeMutations:(NSArray *)mutations completion:(nullable FSTVoidErrorBlock)completion { [self.workerDispatchQueue dispatchAsync:^{ diff --git a/Firestore/Source/Public/FIRDocumentReference.h b/Firestore/Source/Public/FIRDocumentReference.h index 7fcc7a873ae..fc4ffc13fad 100644 --- a/Firestore/Source/Public/FIRDocumentReference.h +++ b/Firestore/Source/Public/FIRDocumentReference.h @@ -21,6 +21,7 @@ @class FIRFirestore; @class FIRCollectionReference; @class FIRDocumentSnapshot; +@class FIRGetOptions; @class FIRSetOptions; NS_ASSUME_NONNULL_BEGIN @@ -195,6 +196,11 @@ NS_SWIFT_NAME(DocumentReference) - (void)getDocumentWithCompletion:(FIRDocumentSnapshotBlock)completion NS_SWIFT_NAME(getDocument(completion:)); +// clang-format off +- (void)getDocumentWithOptions:(FIRGetOptions *)options completion:(FIRDocumentSnapshotBlock)completion + NS_SWIFT_NAME(getDocument(options:completion:)); +// clang-format on + /** * Attaches a listener for DocumentSnapshot events. * diff --git a/Firestore/Source/Public/FIRGetOptions.h b/Firestore/Source/Public/FIRGetOptions.h new file mode 100644 index 00000000000..0b7412191d9 --- /dev/null +++ b/Firestore/Source/Public/FIRGetOptions.h @@ -0,0 +1,69 @@ +/* + * Copyright 2018 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 + +NS_ASSUME_NONNULL_BEGIN + +/** + * An options object that configures the behavior of + * `DocumentReference.getDocument()` and `CollectionReference.getDocuments()`. + * By providing a `GetOptions` object the `getDocument[s]` methods can be + * configured to fetch results only from the server, only from the local cache, + * or attempt the server and fall back to the cache (which is the default). + */ +NS_SWIFT_NAME(GetOptions) +@interface FIRGetOptions : NSObject + +/** + * Returns the default options. + * + * Equiavlent to `[[FIRGetOptions alloc] initWithSource:FIRGetSourceDefault]` in + * objective-c. + */ ++ (FIRGetOptions *)defaultOptions NS_SWIFT_NAME(defaultOptions()); + +/** + * Describes whether we should get from server or cache. + * + * Setting the FIRGetOption source to FIRGetSourceDefault, if online, causes + * Firestore to try to give a consistent (server-retrieved) snapshot, or else + * revert to the cache to provide a value. + * + * FIRGetSourceServer causes Firestore to avoid the cache (generating an error + * if a value cannot be retrieved from the server). The cache will be updated + * if the RPC succeeds. Latency compensation still occurs (implying that if the + * cache is more up to date, then it's values will be merged into the results). + * + * FIRGetSourceCache causes Firestore to immediately return a value from the + * cache, ignoring the server completely (implying that the returned value may + * be stale with respect to the value on the server.) For a single document, + * the get will fail if the document doesn't exist. + */ +typedef NS_ENUM(NSUInteger, FIRGetSource) { + FIRGetSourceDefault, + FIRGetSourceServer, + FIRGetSourceCache +} NS_SWIFT_NAME(GetSource); + +/** + * Initializes the get options with the specified source. + */ +- (instancetype)initWithSource:(FIRGetSource)source NS_SWIFT_NAME(init(source:)); + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Public/FIRQuery.h b/Firestore/Source/Public/FIRQuery.h index ff15ac6af08..0ddfe3c7150 100644 --- a/Firestore/Source/Public/FIRQuery.h +++ b/Firestore/Source/Public/FIRQuery.h @@ -20,6 +20,7 @@ @class FIRFieldPath; @class FIRFirestore; +@class FIRGetOptions; @class FIRQuerySnapshot; @class FIRDocumentSnapshot; @@ -90,6 +91,11 @@ NS_SWIFT_NAME(Query) - (void)getDocumentsWithCompletion:(FIRQuerySnapshotBlock)completion NS_SWIFT_NAME(getDocuments(completion:)); +// clang-format off +- (void)getDocumentsWithOptions:(FIRGetOptions *)options completion:(FIRQuerySnapshotBlock)completion + NS_SWIFT_NAME(getDocuments(options:completion:)); +// clang-format on + /** * Attaches a listener for QuerySnapshot events. * From 5a68eda6e1ed916d7e47f811c8e74e9eb53b7934 Mon Sep 17 00:00:00 2001 From: rsgowman Date: Thu, 1 Feb 2018 11:37:41 -0500 Subject: [PATCH 2/7] Convert FIRGetOptions.initWithSource to factory method. (#655) (#711) * Convert FIRGetOptions.initWithSource to factory method. * source -> optionsWithSource * Swift: GetOptions.source(source:) -> source(_:) --- Firestore/Example/SwiftBuildTest/main.swift | 12 ++-- .../Integration/API/FIRGetOptionsTests.m | 58 ++++++++----------- Firestore/Source/API/FIRGetOptions+Internal.h | 2 + Firestore/Source/API/FIRGetOptions.m | 6 +- Firestore/Source/Public/FIRGetOptions.h | 9 ++- 5 files changed, 42 insertions(+), 45 deletions(-) diff --git a/Firestore/Example/SwiftBuildTest/main.swift b/Firestore/Example/SwiftBuildTest/main.swift index 2d77669f265..c74c3d8b7b3 100644 --- a/Firestore/Example/SwiftBuildTest/main.swift +++ b/Firestore/Example/SwiftBuildTest/main.swift @@ -228,11 +228,11 @@ func readDocument(at docRef: DocumentReference) { func readDocumentWithOptions(at docRef: DocumentReference) { docRef.getDocument(options:GetOptions.defaultOptions()) { document, error in } - docRef.getDocument(options:GetOptions(source:GetSource.default)) { document, error in + docRef.getDocument(options:GetOptions.source(GetSource.default)) { document, error in } - docRef.getDocument(options:GetOptions(source:.server)) { document, error in + docRef.getDocument(options:GetOptions.source(.server)) { document, error in } - docRef.getDocument(options:GetOptions(source:GetSource.cache)) { document, error in + docRef.getDocument(options:GetOptions.source(GetSource.cache)) { document, error in } } @@ -249,11 +249,11 @@ func readDocuments(matching query: Query) { func readDocumentsWithOptions(matching query: Query) { query.getDocuments(options:GetOptions.defaultOptions()) { querySnapshot, error in } - query.getDocuments(options:GetOptions.init(source:GetSource.default)) { querySnapshot, error in + query.getDocuments(options:GetOptions.source(GetSource.default)) { querySnapshot, error in } - query.getDocuments(options:GetOptions.init(source:GetSource.server)) { querySnapshot, error in + query.getDocuments(options:GetOptions.source(.server)) { querySnapshot, error in } - query.getDocuments(options:GetOptions.init(source:GetSource.cache)) { querySnapshot, error in + query.getDocuments(options:GetOptions.source(GetSource.cache)) { querySnapshot, error in } } diff --git a/Firestore/Example/Tests/Integration/API/FIRGetOptionsTests.m b/Firestore/Example/Tests/Integration/API/FIRGetOptionsTests.m index 628a6dcb2d8..cf77d4ab0c5 100644 --- a/Firestore/Example/Tests/Integration/API/FIRGetOptionsTests.m +++ b/Firestore/Example/Tests/Integration/API/FIRGetOptionsTests.m @@ -145,8 +145,7 @@ - (void)testGetDocumentWhileOnlineCacheOnly { // get doc and ensure that it exists, *is* from the cache, and matches // the initialData. FIRDocumentSnapshot *result = - [self readDocumentForRef:doc - options:[[FIRGetOptions alloc] initWithSource:FIRGetSourceCache]]; + [self readDocumentForRef:doc options:[FIRGetOptions optionsWithSource:FIRGetSourceCache]]; XCTAssertTrue(result.exists); XCTAssertTrue(result.metadata.fromCache); XCTAssertFalse(result.metadata.hasPendingWrites); @@ -167,8 +166,7 @@ - (void)testGetCollectionWhileOnlineCacheOnly { // get docs and ensure they *are* from the cache, and matches the // initialDocs. FIRQuerySnapshot *result = - [self readDocumentSetForRef:col - options:[[FIRGetOptions alloc] initWithSource:FIRGetSourceCache]]; + [self readDocumentSetForRef:col options:[FIRGetOptions optionsWithSource:FIRGetSourceCache]]; XCTAssertTrue(result.metadata.fromCache); XCTAssertFalse(result.metadata.hasPendingWrites); XCTAssertEqualObjects(FIRQuerySnapshotGetData(result), (@[ @@ -205,8 +203,7 @@ - (void)testGetDocumentWhileOfflineCacheOnly { // get doc and ensure it exists, *is* from the cache, and matches the // newData. FIRDocumentSnapshot *result = - [self readDocumentForRef:doc - options:[[FIRGetOptions alloc] initWithSource:FIRGetSourceCache]]; + [self readDocumentForRef:doc options:[FIRGetOptions optionsWithSource:FIRGetSourceCache]]; XCTAssertTrue(result.exists); XCTAssertTrue(result.metadata.fromCache); XCTAssertTrue(result.metadata.hasPendingWrites); @@ -237,8 +234,7 @@ - (void)testGetCollectionWhileOfflineCacheOnly { // get docs and ensure they *are* from the cache, and matches the updated // data. FIRQuerySnapshot *result = - [self readDocumentSetForRef:col - options:[[FIRGetOptions alloc] initWithSource:FIRGetSourceCache]]; + [self readDocumentSetForRef:col options:[FIRGetOptions optionsWithSource:FIRGetSourceCache]]; XCTAssertTrue(result.metadata.fromCache); XCTAssertTrue(result.metadata.hasPendingWrites); XCTAssertEqualObjects(FIRQuerySnapshotGetData(result), (@[ @@ -264,8 +260,7 @@ - (void)testGetDocumentWhileOnlineServerOnly { // get doc and ensure that it exists, is *not* from the cache, and matches // the initialData. FIRDocumentSnapshot *result = - [self readDocumentForRef:doc - options:[[FIRGetOptions alloc] initWithSource:FIRGetSourceServer]]; + [self readDocumentForRef:doc options:[FIRGetOptions optionsWithSource:FIRGetSourceServer]]; XCTAssertTrue(result.exists); XCTAssertFalse(result.metadata.fromCache); XCTAssertFalse(result.metadata.hasPendingWrites); @@ -286,8 +281,7 @@ - (void)testGetCollectionWhileOnlineServerOnly { // get docs and ensure they are *not* from the cache, and matches the // initialData. FIRQuerySnapshot *result = - [self readDocumentSetForRef:col - options:[[FIRGetOptions alloc] initWithSource:FIRGetSourceServer]]; + [self readDocumentSetForRef:col options:[FIRGetOptions optionsWithSource:FIRGetSourceServer]]; XCTAssertFalse(result.metadata.fromCache); XCTAssertFalse(result.metadata.hasPendingWrites); XCTAssertEqualObjects(FIRQuerySnapshotGetData(result), (@[ @@ -314,7 +308,7 @@ - (void)testGetDocumentWhileOfflineServerOnly { // attempt to get doc and ensure it cannot be retreived XCTestExpectation *failedGetDocCompletion = [self expectationWithDescription:@"failedGetDoc"]; - [doc getDocumentWithOptions:[[FIRGetOptions alloc] initWithSource:FIRGetSourceServer] + [doc getDocumentWithOptions:[FIRGetOptions optionsWithSource:FIRGetSourceServer] completion:^(FIRDocumentSnapshot *snapshot, NSError *error) { XCTAssertNotNil(error); XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); @@ -340,7 +334,7 @@ - (void)testGetCollectionWhileOfflineServerOnly { // attempt to get docs and ensure they cannot be retreived XCTestExpectation *failedGetDocsCompletion = [self expectationWithDescription:@"failedGetDocs"]; - [col getDocumentsWithOptions:[[FIRGetOptions alloc] initWithSource:FIRGetSourceServer] + [col getDocumentsWithOptions:[FIRGetOptions optionsWithSource:FIRGetSourceServer] completion:^(FIRQuerySnapshot *snapshot, NSError *error) { XCTAssertNotNil(error); XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); @@ -380,16 +374,15 @@ - (void)testGetDocumentWhileOfflineWithDifferentGetOptions { // get doc (from cache) and ensure it exists, *is* from the cache, and // matches the newData. FIRDocumentSnapshot *result = - [self readDocumentForRef:doc - options:[[FIRGetOptions alloc] initWithSource:FIRGetSourceCache]]; + [self readDocumentForRef:doc options:[FIRGetOptions optionsWithSource:FIRGetSourceCache]]; XCTAssertTrue(result.exists); XCTAssertTrue(result.metadata.fromCache); XCTAssertTrue(result.metadata.hasPendingWrites); XCTAssertEqualObjects(result.data, newData); // attempt to get doc (with default get options) - result = [self readDocumentForRef:doc - options:[[FIRGetOptions alloc] initWithSource:FIRGetSourceDefault]]; + result = + [self readDocumentForRef:doc options:[FIRGetOptions optionsWithSource:FIRGetSourceDefault]]; XCTAssertTrue(result.exists); XCTAssertTrue(result.metadata.fromCache); XCTAssertTrue(result.metadata.hasPendingWrites); @@ -397,7 +390,7 @@ - (void)testGetDocumentWhileOfflineWithDifferentGetOptions { // attempt to get doc (from the server) and ensure it cannot be retreived XCTestExpectation *failedGetDocCompletion = [self expectationWithDescription:@"failedGetDoc"]; - [doc getDocumentWithOptions:[[FIRGetOptions alloc] initWithSource:FIRGetSourceServer] + [doc getDocumentWithOptions:[FIRGetOptions optionsWithSource:FIRGetSourceServer] completion:^(FIRDocumentSnapshot *snapshot, NSError *error) { XCTAssertNotNil(error); XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); @@ -440,8 +433,7 @@ - (void)testGetCollectionWhileOfflineWithDifferentGetOptions { // get docs (from cache) and ensure they *are* from the cache, and // matches the updated data. FIRQuerySnapshot *result = - [self readDocumentSetForRef:col - options:[[FIRGetOptions alloc] initWithSource:FIRGetSourceCache]]; + [self readDocumentSetForRef:col options:[FIRGetOptions optionsWithSource:FIRGetSourceCache]]; XCTAssertTrue(result.metadata.fromCache); XCTAssertTrue(result.metadata.hasPendingWrites); XCTAssertEqualObjects(FIRQuerySnapshotGetData(result), (@[ @@ -458,7 +450,7 @@ - (void)testGetCollectionWhileOfflineWithDifferentGetOptions { // attempt to get docs (with default get options) result = [self readDocumentSetForRef:col - options:[[FIRGetOptions alloc] initWithSource:FIRGetSourceDefault]]; + options:[FIRGetOptions optionsWithSource:FIRGetSourceDefault]]; XCTAssertTrue(result.metadata.fromCache); XCTAssertEqualObjects(FIRQuerySnapshotGetData(result), (@[ @{@"key1" : @"value1"}, @{@"key2" : @"value2", @"key2b" : @"value2b"}, @@ -474,7 +466,7 @@ - (void)testGetCollectionWhileOfflineWithDifferentGetOptions { // attempt to get docs (from the server) and ensure they cannot be retreived XCTestExpectation *failedGetDocsCompletion = [self expectationWithDescription:@"failedGetDocs"]; - [col getDocumentsWithOptions:[[FIRGetOptions alloc] initWithSource:FIRGetSourceServer] + [col getDocumentsWithOptions:[FIRGetOptions optionsWithSource:FIRGetSourceServer] completion:^(FIRQuerySnapshot *snapshot, NSError *error) { XCTAssertNotNil(error); XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); @@ -547,7 +539,7 @@ - (void)testGetNonExistingDocWhileOnlineCacheOnly { // certain documents *don't* exist. XCTestExpectation *getNonExistingDocCompletion = [self expectationWithDescription:@"getNonExistingDoc"]; - [doc getDocumentWithOptions:[[FIRGetOptions alloc] initWithSource:FIRGetSourceCache] + [doc getDocumentWithOptions:[FIRGetOptions optionsWithSource:FIRGetSourceCache] completion:^(FIRDocumentSnapshot *snapshot, NSError *error) { XCTAssertNotNil(error); XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); @@ -562,8 +554,7 @@ - (void)testGetNonExistingCollectionWhileOnlineCacheOnly { // get collection and ensure it's empty and that it *is* from the cache. FIRQuerySnapshot *snapshot = - [self readDocumentSetForRef:col - options:[[FIRGetOptions alloc] initWithSource:FIRGetSourceCache]]; + [self readDocumentSetForRef:col options:[FIRGetOptions optionsWithSource:FIRGetSourceCache]]; XCTAssertEqual(snapshot.count, 0); XCTAssertEqual(snapshot.documentChanges.count, 0); XCTAssertTrue(snapshot.metadata.fromCache); @@ -581,7 +572,7 @@ - (void)testGetNonExistingDocWhileOfflineCacheOnly { // certain documents *don't* exist. XCTestExpectation *getNonExistingDocCompletion = [self expectationWithDescription:@"getNonExistingDoc"]; - [doc getDocumentWithOptions:[[FIRGetOptions alloc] initWithSource:FIRGetSourceCache] + [doc getDocumentWithOptions:[FIRGetOptions optionsWithSource:FIRGetSourceCache] completion:^(FIRDocumentSnapshot *snapshot, NSError *error) { XCTAssertNotNil(error); XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); @@ -599,8 +590,7 @@ - (void)testGetNonExistingCollectionWhileOfflineCacheOnly { // get collection and ensure it's empty and that it *is* from the cache. FIRQuerySnapshot *snapshot = - [self readDocumentSetForRef:col - options:[[FIRGetOptions alloc] initWithSource:FIRGetSourceCache]]; + [self readDocumentSetForRef:col options:[FIRGetOptions optionsWithSource:FIRGetSourceCache]]; XCTAssertEqual(snapshot.count, 0); XCTAssertEqual(snapshot.documentChanges.count, 0); XCTAssertTrue(snapshot.metadata.fromCache); @@ -612,8 +602,7 @@ - (void)testGetNonExistingDocWhileOnlineServerOnly { // get doc and ensure that it does not exist and is *not* from the cache. FIRDocumentSnapshot *snapshot = - [self readDocumentForRef:doc - options:[[FIRGetOptions alloc] initWithSource:FIRGetSourceServer]]; + [self readDocumentForRef:doc options:[FIRGetOptions optionsWithSource:FIRGetSourceServer]]; XCTAssertFalse(snapshot.exists); XCTAssertFalse(snapshot.metadata.fromCache); XCTAssertFalse(snapshot.metadata.hasPendingWrites); @@ -624,8 +613,7 @@ - (void)testGetNonExistingCollectionWhileOnlineServerOnly { // get collection and ensure that it's empty and that it's *not* from the cache. FIRQuerySnapshot *snapshot = - [self readDocumentSetForRef:col - options:[[FIRGetOptions alloc] initWithSource:FIRGetSourceServer]]; + [self readDocumentSetForRef:col options:[FIRGetOptions optionsWithSource:FIRGetSourceServer]]; XCTAssertEqual(snapshot.count, 0); XCTAssertEqual(snapshot.documentChanges.count, 0); XCTAssertFalse(snapshot.metadata.fromCache); @@ -643,7 +631,7 @@ - (void)testGetNonExistingDocWhileOfflineServerOnly { // certain documents *don't* exist. XCTestExpectation *getNonExistingDocCompletion = [self expectationWithDescription:@"getNonExistingDoc"]; - [doc getDocumentWithOptions:[[FIRGetOptions alloc] initWithSource:FIRGetSourceServer] + [doc getDocumentWithOptions:[FIRGetOptions optionsWithSource:FIRGetSourceServer] completion:^(FIRDocumentSnapshot *snapshot, NSError *error) { XCTAssertNotNil(error); XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); @@ -661,7 +649,7 @@ - (void)testGetNonExistingCollectionWhileOfflineServerOnly { // attempt to get collection and ensure that it cannot be retreived XCTestExpectation *failedGetDocsCompletion = [self expectationWithDescription:@"failedGetDocs"]; - [col getDocumentsWithOptions:[[FIRGetOptions alloc] initWithSource:FIRGetSourceServer] + [col getDocumentsWithOptions:[FIRGetOptions optionsWithSource:FIRGetSourceServer] completion:^(FIRQuerySnapshot *snapshot, NSError *error) { XCTAssertNotNil(error); XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); diff --git a/Firestore/Source/API/FIRGetOptions+Internal.h b/Firestore/Source/API/FIRGetOptions+Internal.h index 1e8a479c837..da3bdc9de6b 100644 --- a/Firestore/Source/API/FIRGetOptions+Internal.h +++ b/Firestore/Source/API/FIRGetOptions+Internal.h @@ -23,6 +23,8 @@ NS_ASSUME_NONNULL_BEGIN /** Where getDocument[s] calls should get their data from. */ @property(nonatomic, readonly, getter=source) FIRGetSource source; +- (instancetype)initWithSource:(FIRGetSource)source; + @end NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/API/FIRGetOptions.m b/Firestore/Source/API/FIRGetOptions.m index b7eb5e2ece9..e43f25f837b 100644 --- a/Firestore/Source/API/FIRGetOptions.m +++ b/Firestore/Source/API/FIRGetOptions.m @@ -20,7 +20,7 @@ @implementation FIRGetOptions -+ (FIRGetOptions *)defaultOptions { ++ (instancetype)defaultOptions { return [[FIRGetOptions alloc] initWithSource:FIRGetSourceDefault]; } @@ -31,6 +31,10 @@ - (instancetype)initWithSource:(FIRGetSource)source { return self; } ++ (instancetype)optionsWithSource:(FIRGetSource)source { + return [[FIRGetOptions alloc] initWithSource:source]; +} + @end NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Public/FIRGetOptions.h b/Firestore/Source/Public/FIRGetOptions.h index 0b7412191d9..de283255ecf 100644 --- a/Firestore/Source/Public/FIRGetOptions.h +++ b/Firestore/Source/Public/FIRGetOptions.h @@ -28,13 +28,16 @@ NS_ASSUME_NONNULL_BEGIN NS_SWIFT_NAME(GetOptions) @interface FIRGetOptions : NSObject +/** */ +- (instancetype)init __attribute((unavailable("FIRGetOptions cannot be created directly."))); + /** * Returns the default options. * - * Equiavlent to `[[FIRGetOptions alloc] initWithSource:FIRGetSourceDefault]` in + * Equiavlent to `[FIRGetOptions source:FIRGetSourceDefault]` in * objective-c. */ -+ (FIRGetOptions *)defaultOptions NS_SWIFT_NAME(defaultOptions()); ++ (instancetype)defaultOptions NS_SWIFT_NAME(defaultOptions()); /** * Describes whether we should get from server or cache. @@ -62,7 +65,7 @@ typedef NS_ENUM(NSUInteger, FIRGetSource) { /** * Initializes the get options with the specified source. */ -- (instancetype)initWithSource:(FIRGetSource)source NS_SWIFT_NAME(init(source:)); ++ (instancetype)optionsWithSource:(FIRGetSource)source NS_SWIFT_NAME(source(_:)); @end From 607fe1103d80f0993a93923b2940f4be6cd2637a Mon Sep 17 00:00:00 2001 From: rsgowman Date: Tue, 27 Feb 2018 10:12:22 -0500 Subject: [PATCH 3/7] Convert GetOptions to GetSource (#826) Reworks the existing options object into a source parameter. --- Firestore/CHANGELOG.md | 4 + .../Firestore.xcodeproj/project.pbxproj | 8 +- Firestore/Example/SwiftBuildTest/main.swift | 16 +- ...{FIRGetOptionsTests.m => FIRSourceTests.m} | 182 ++++++++---------- .../Tests/Util/FSTIntegrationTestCase.h | 8 +- .../Tests/Util/FSTIntegrationTestCase.mm | 32 +-- Firestore/Source/API/FIRDocumentReference.m | 34 ++-- Firestore/Source/API/FIRGetOptions+Internal.h | 30 --- Firestore/Source/API/FIRGetOptions.m | 40 ---- Firestore/Source/API/FIRQuery.m | 33 ++-- Firestore/Source/Core/FSTFirestoreClient.m | 20 +- .../Source/Public/FIRDocumentReference.h | 19 +- Firestore/Source/Public/FIRGetOptions.h | 72 ------- Firestore/Source/Public/FIRGetSource.h | 48 +++++ Firestore/Source/Public/FIRQuery.h | 20 +- 15 files changed, 240 insertions(+), 326 deletions(-) rename Firestore/Example/Tests/Integration/API/{FIRGetOptionsTests.m => FIRSourceTests.m} (80%) delete mode 100644 Firestore/Source/API/FIRGetOptions+Internal.h delete mode 100644 Firestore/Source/API/FIRGetOptions.m delete mode 100644 Firestore/Source/Public/FIRGetOptions.h create mode 100644 Firestore/Source/Public/FIRGetSource.h diff --git a/Firestore/CHANGELOG.md b/Firestore/CHANGELOG.md index 31a477cac37..307b71b7917 100644 --- a/Firestore/CHANGELOG.md +++ b/Firestore/CHANGELOG.md @@ -1,4 +1,8 @@ # Unreleased +- [changed] Added ability to control whether DocumentReference.getDocument() and + Query.getDocuments() should fetch from server only, cache only, or attempt + server and fall back to the cache (which was the only option previously, and + is now the default.) # v0.10.0 - [changed] Removed the includeMetadataChanges property in FIRDocumentListenOptions diff --git a/Firestore/Example/Firestore.xcodeproj/project.pbxproj b/Firestore/Example/Firestore.xcodeproj/project.pbxproj index 503d62a3b81..0bfc8174617 100644 --- a/Firestore/Example/Firestore.xcodeproj/project.pbxproj +++ b/Firestore/Example/Firestore.xcodeproj/project.pbxproj @@ -59,7 +59,7 @@ 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; 6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5B8195388D20070C39A /* InfoPlist.strings */; }; - 61CC13FA2007D0C90021F5BF /* FIRGetOptionsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 61CC13F82007D0C20021F5BF /* FIRGetOptionsTests.m */; }; + 61287373203C92C5000EAD3F /* FIRSourceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 61287372203C92C5000EAD3F /* FIRSourceTests.m */; }; 61E1D8B11FCF6C5700753285 /* StringViewTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 61E1D8AF1FCF6AF500753285 /* StringViewTests.mm */; }; 6ED54761B845349D43DB6B78 /* Pods_Firestore_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 75A6FE51C1A02DF38F62FAAD /* Pods_Firestore_Example.framework */; }; 71719F9F1E33DC2100824A3D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 71719F9D1E33DC2100824A3D /* LaunchScreen.storyboard */; }; @@ -236,7 +236,7 @@ 6003F5AF195388D20070C39A /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 6003F5B7195388D20070C39A /* Tests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Tests-Info.plist"; sourceTree = ""; }; 6003F5B9195388D20070C39A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; - 61CC13F82007D0C20021F5BF /* FIRGetOptionsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRGetOptionsTests.m; sourceTree = ""; }; + 61287372203C92C5000EAD3F /* FIRSourceTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRSourceTests.m; sourceTree = ""; }; 61E1D8AF1FCF6AF500753285 /* StringViewTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = StringViewTests.mm; sourceTree = ""; }; 69F6A10DBD6187489481CD76 /* Pods_Firestore_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 71719F9E1E33DC2100824A3D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; @@ -725,7 +725,7 @@ DE51B1BC1F0D48AC0013853F /* API */ = { isa = PBXGroup; children = ( - 61CC13F82007D0C20021F5BF /* FIRGetOptionsTests.m */, + 61287372203C92C5000EAD3F /* FIRSourceTests.m */, DE51B1BD1F0D48AC0013853F /* FIRCursorTests.m */, DE51B1BE1F0D48AC0013853F /* FIRDatabaseTests.m */, DE51B1BF1F0D48AC0013853F /* FIRFieldsTests.m */, @@ -1274,6 +1274,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 61287373203C92C5000EAD3F /* FIRSourceTests.m in Sources */, DE03B2EE1F214BAA00A30B9C /* FIRWriteBatchTests.m in Sources */, DE03B2F01F214BAA00A30B9C /* FIRDatabaseTests.m in Sources */, 5491BC731FB44593008B3588 /* FSTIntegrationTestCase.mm in Sources */, @@ -1284,7 +1285,6 @@ 54E928251F33953400C1953E /* FSTEventAccumulator.m in Sources */, DE03B2ED1F214BA200A30B9C /* FSTSmokeTests.m in Sources */, DE03B2F31F214BAA00A30B9C /* FIRQueryTests.m in Sources */, - 61CC13FA2007D0C90021F5BF /* FIRGetOptionsTests.m in Sources */, DE03B35E1F21586C00A30B9C /* FSTHelpers.m in Sources */, DE03B2F51F214BAA00A30B9C /* FIRTypeTests.m in Sources */, DE03B2EF1F214BAA00A30B9C /* FIRCursorTests.m in Sources */, diff --git a/Firestore/Example/SwiftBuildTest/main.swift b/Firestore/Example/SwiftBuildTest/main.swift index c74c3d8b7b3..b2de9da8d9f 100644 --- a/Firestore/Example/SwiftBuildTest/main.swift +++ b/Firestore/Example/SwiftBuildTest/main.swift @@ -226,13 +226,11 @@ func readDocument(at docRef: DocumentReference) { } func readDocumentWithOptions(at docRef: DocumentReference) { - docRef.getDocument(options:GetOptions.defaultOptions()) { document, error in + docRef.getDocument(source:Source.default) { document, error in } - docRef.getDocument(options:GetOptions.source(GetSource.default)) { document, error in + docRef.getDocument(source:.server) { document, error in } - docRef.getDocument(options:GetOptions.source(.server)) { document, error in - } - docRef.getDocument(options:GetOptions.source(GetSource.cache)) { document, error in + docRef.getDocument(source:Source.cache) { document, error in } } @@ -247,13 +245,11 @@ func readDocuments(matching query: Query) { } func readDocumentsWithOptions(matching query: Query) { - query.getDocuments(options:GetOptions.defaultOptions()) { querySnapshot, error in - } - query.getDocuments(options:GetOptions.source(GetSource.default)) { querySnapshot, error in + query.getDocuments(source:Source.default) { querySnapshot, error in } - query.getDocuments(options:GetOptions.source(.server)) { querySnapshot, error in + query.getDocuments(source:.server) { querySnapshot, error in } - query.getDocuments(options:GetOptions.source(GetSource.cache)) { querySnapshot, error in + query.getDocuments(source:Source.cache) { querySnapshot, error in } } diff --git a/Firestore/Example/Tests/Integration/API/FIRGetOptionsTests.m b/Firestore/Example/Tests/Integration/API/FIRSourceTests.m similarity index 80% rename from Firestore/Example/Tests/Integration/API/FIRGetOptionsTests.m rename to Firestore/Example/Tests/Integration/API/FIRSourceTests.m index cf77d4ab0c5..258d4410912 100644 --- a/Firestore/Example/Tests/Integration/API/FIRGetOptionsTests.m +++ b/Firestore/Example/Tests/Integration/API/FIRSourceTests.m @@ -22,12 +22,12 @@ #import "Firestore/Source/API/FIRFirestore+Internal.h" #import "Firestore/Source/Core/FSTFirestoreClient.h" -@interface FIRGetOptionsTests : FSTIntegrationTestCase +@interface FIRGetSourceTests : FSTIntegrationTestCase @end -@implementation FIRGetOptionsTests +@implementation FIRGetSourceTests -- (void)testGetDocumentWhileOnlineWithDefaultGetOptions { +- (void)testGetDocumentWhileOnlineWithDefaultSource { FIRDocumentReference *doc = [self documentRef]; // set document to a known value @@ -43,7 +43,7 @@ - (void)testGetDocumentWhileOnlineWithDefaultGetOptions { XCTAssertEqualObjects(result.data, initialData); } -- (void)testGetCollectionWhileOnlineWithDefaultGetOptions { +- (void)testGetCollectionWhileOnlineWithDefaultSource { FIRCollectionReference *col = [self collectionRef]; // set a few documents to known values @@ -69,7 +69,7 @@ - (void)testGetCollectionWhileOnlineWithDefaultGetOptions { ])); } -- (void)testGetDocumentWhileOfflineWithDefaultGetOptions { +- (void)testGetDocumentWhileOfflineWithDefaultSource { FIRDocumentReference *doc = [self documentRef]; // set document to a known value @@ -97,7 +97,7 @@ - (void)testGetDocumentWhileOfflineWithDefaultGetOptions { XCTAssertEqualObjects(result.data, newData); } -- (void)testGetCollectionWhileOfflineWithDefaultGetOptions { +- (void)testGetCollectionWhileOfflineWithDefaultSource { FIRCollectionReference *col = [self collectionRef]; // set a few documents to known values @@ -144,8 +144,7 @@ - (void)testGetDocumentWhileOnlineCacheOnly { // get doc and ensure that it exists, *is* from the cache, and matches // the initialData. - FIRDocumentSnapshot *result = - [self readDocumentForRef:doc options:[FIRGetOptions optionsWithSource:FIRGetSourceCache]]; + FIRDocumentSnapshot *result = [self readDocumentForRef:doc source:FIRGetSourceCache]; XCTAssertTrue(result.exists); XCTAssertTrue(result.metadata.fromCache); XCTAssertFalse(result.metadata.hasPendingWrites); @@ -165,8 +164,7 @@ - (void)testGetCollectionWhileOnlineCacheOnly { // get docs and ensure they *are* from the cache, and matches the // initialDocs. - FIRQuerySnapshot *result = - [self readDocumentSetForRef:col options:[FIRGetOptions optionsWithSource:FIRGetSourceCache]]; + FIRQuerySnapshot *result = [self readDocumentSetForRef:col source:FIRGetSourceCache]; XCTAssertTrue(result.metadata.fromCache); XCTAssertFalse(result.metadata.hasPendingWrites); XCTAssertEqualObjects(FIRQuerySnapshotGetData(result), (@[ @@ -202,8 +200,7 @@ - (void)testGetDocumentWhileOfflineCacheOnly { // get doc and ensure it exists, *is* from the cache, and matches the // newData. - FIRDocumentSnapshot *result = - [self readDocumentForRef:doc options:[FIRGetOptions optionsWithSource:FIRGetSourceCache]]; + FIRDocumentSnapshot *result = [self readDocumentForRef:doc source:FIRGetSourceCache]; XCTAssertTrue(result.exists); XCTAssertTrue(result.metadata.fromCache); XCTAssertTrue(result.metadata.hasPendingWrites); @@ -233,8 +230,7 @@ - (void)testGetCollectionWhileOfflineCacheOnly { // get docs and ensure they *are* from the cache, and matches the updated // data. - FIRQuerySnapshot *result = - [self readDocumentSetForRef:col options:[FIRGetOptions optionsWithSource:FIRGetSourceCache]]; + FIRQuerySnapshot *result = [self readDocumentSetForRef:col source:FIRGetSourceCache]; XCTAssertTrue(result.metadata.fromCache); XCTAssertTrue(result.metadata.hasPendingWrites); XCTAssertEqualObjects(FIRQuerySnapshotGetData(result), (@[ @@ -259,8 +255,7 @@ - (void)testGetDocumentWhileOnlineServerOnly { // get doc and ensure that it exists, is *not* from the cache, and matches // the initialData. - FIRDocumentSnapshot *result = - [self readDocumentForRef:doc options:[FIRGetOptions optionsWithSource:FIRGetSourceServer]]; + FIRDocumentSnapshot *result = [self readDocumentForRef:doc source:FIRGetSourceServer]; XCTAssertTrue(result.exists); XCTAssertFalse(result.metadata.fromCache); XCTAssertFalse(result.metadata.hasPendingWrites); @@ -280,8 +275,7 @@ - (void)testGetCollectionWhileOnlineServerOnly { // get docs and ensure they are *not* from the cache, and matches the // initialData. - FIRQuerySnapshot *result = - [self readDocumentSetForRef:col options:[FIRGetOptions optionsWithSource:FIRGetSourceServer]]; + FIRQuerySnapshot *result = [self readDocumentSetForRef:col source:FIRGetSourceServer]; XCTAssertFalse(result.metadata.fromCache); XCTAssertFalse(result.metadata.hasPendingWrites); XCTAssertEqualObjects(FIRQuerySnapshotGetData(result), (@[ @@ -308,13 +302,13 @@ - (void)testGetDocumentWhileOfflineServerOnly { // attempt to get doc and ensure it cannot be retreived XCTestExpectation *failedGetDocCompletion = [self expectationWithDescription:@"failedGetDoc"]; - [doc getDocumentWithOptions:[FIRGetOptions optionsWithSource:FIRGetSourceServer] - completion:^(FIRDocumentSnapshot *snapshot, NSError *error) { - XCTAssertNotNil(error); - XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); - XCTAssertEqual(error.code, FIRFirestoreErrorCodeUnavailable); - [failedGetDocCompletion fulfill]; - }]; + [doc getDocumentWithSource:FIRGetSourceServer + completion:^(FIRDocumentSnapshot *snapshot, NSError *error) { + XCTAssertNotNil(error); + XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); + XCTAssertEqual(error.code, FIRFirestoreErrorCodeUnavailable); + [failedGetDocCompletion fulfill]; + }]; [self awaitExpectations]; } @@ -334,17 +328,17 @@ - (void)testGetCollectionWhileOfflineServerOnly { // attempt to get docs and ensure they cannot be retreived XCTestExpectation *failedGetDocsCompletion = [self expectationWithDescription:@"failedGetDocs"]; - [col getDocumentsWithOptions:[FIRGetOptions optionsWithSource:FIRGetSourceServer] - completion:^(FIRQuerySnapshot *snapshot, NSError *error) { - XCTAssertNotNil(error); - XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); - XCTAssertEqual(error.code, FIRFirestoreErrorCodeUnavailable); - [failedGetDocsCompletion fulfill]; - }]; + [col getDocumentsWithSource:FIRGetSourceServer + completion:^(FIRQuerySnapshot *snapshot, NSError *error) { + XCTAssertNotNil(error); + XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); + XCTAssertEqual(error.code, FIRFirestoreErrorCodeUnavailable); + [failedGetDocsCompletion fulfill]; + }]; [self awaitExpectations]; } -- (void)testGetDocumentWhileOfflineWithDifferentGetOptions { +- (void)testGetDocumentWhileOfflineWithDifferentSource { FIRDocumentReference *doc = [self documentRef]; // set document to a known value @@ -373,16 +367,14 @@ - (void)testGetDocumentWhileOfflineWithDifferentGetOptions { // get doc (from cache) and ensure it exists, *is* from the cache, and // matches the newData. - FIRDocumentSnapshot *result = - [self readDocumentForRef:doc options:[FIRGetOptions optionsWithSource:FIRGetSourceCache]]; + FIRDocumentSnapshot *result = [self readDocumentForRef:doc source:FIRGetSourceCache]; XCTAssertTrue(result.exists); XCTAssertTrue(result.metadata.fromCache); XCTAssertTrue(result.metadata.hasPendingWrites); XCTAssertEqualObjects(result.data, newData); - // attempt to get doc (with default get options) - result = - [self readDocumentForRef:doc options:[FIRGetOptions optionsWithSource:FIRGetSourceDefault]]; + // attempt to get doc (with default get source) + result = [self readDocumentForRef:doc source:FIRGetSourceDefault]; XCTAssertTrue(result.exists); XCTAssertTrue(result.metadata.fromCache); XCTAssertTrue(result.metadata.hasPendingWrites); @@ -390,17 +382,17 @@ - (void)testGetDocumentWhileOfflineWithDifferentGetOptions { // attempt to get doc (from the server) and ensure it cannot be retreived XCTestExpectation *failedGetDocCompletion = [self expectationWithDescription:@"failedGetDoc"]; - [doc getDocumentWithOptions:[FIRGetOptions optionsWithSource:FIRGetSourceServer] - completion:^(FIRDocumentSnapshot *snapshot, NSError *error) { - XCTAssertNotNil(error); - XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); - XCTAssertEqual(error.code, FIRFirestoreErrorCodeUnavailable); - [failedGetDocCompletion fulfill]; - }]; + [doc getDocumentWithSource:FIRGetSourceServer + completion:^(FIRDocumentSnapshot *snapshot, NSError *error) { + XCTAssertNotNil(error); + XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); + XCTAssertEqual(error.code, FIRFirestoreErrorCodeUnavailable); + [failedGetDocCompletion fulfill]; + }]; [self awaitExpectations]; } -- (void)testGetCollectionWhileOfflineWithDifferentGetOptions { +- (void)testGetCollectionWhileOfflineWithDifferentSource { FIRCollectionReference *col = [self collectionRef]; // set a few documents to a known value @@ -432,8 +424,7 @@ - (void)testGetCollectionWhileOfflineWithDifferentGetOptions { // get docs (from cache) and ensure they *are* from the cache, and // matches the updated data. - FIRQuerySnapshot *result = - [self readDocumentSetForRef:col options:[FIRGetOptions optionsWithSource:FIRGetSourceCache]]; + FIRQuerySnapshot *result = [self readDocumentSetForRef:col source:FIRGetSourceCache]; XCTAssertTrue(result.metadata.fromCache); XCTAssertTrue(result.metadata.hasPendingWrites); XCTAssertEqualObjects(FIRQuerySnapshotGetData(result), (@[ @@ -448,9 +439,8 @@ - (void)testGetCollectionWhileOfflineWithDifferentGetOptions { @[ @(FIRDocumentChangeTypeAdded), @"doc4", @{@"key4" : @"value4"} ] ])); - // attempt to get docs (with default get options) - result = [self readDocumentSetForRef:col - options:[FIRGetOptions optionsWithSource:FIRGetSourceDefault]]; + // attempt to get docs (with default get source) + result = [self readDocumentSetForRef:col source:FIRGetSourceDefault]; XCTAssertTrue(result.metadata.fromCache); XCTAssertEqualObjects(FIRQuerySnapshotGetData(result), (@[ @{@"key1" : @"value1"}, @{@"key2" : @"value2", @"key2b" : @"value2b"}, @@ -466,17 +456,17 @@ - (void)testGetCollectionWhileOfflineWithDifferentGetOptions { // attempt to get docs (from the server) and ensure they cannot be retreived XCTestExpectation *failedGetDocsCompletion = [self expectationWithDescription:@"failedGetDocs"]; - [col getDocumentsWithOptions:[FIRGetOptions optionsWithSource:FIRGetSourceServer] - completion:^(FIRQuerySnapshot *snapshot, NSError *error) { - XCTAssertNotNil(error); - XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); - XCTAssertEqual(error.code, FIRFirestoreErrorCodeUnavailable); - [failedGetDocsCompletion fulfill]; - }]; + [col getDocumentsWithSource:FIRGetSourceServer + completion:^(FIRQuerySnapshot *snapshot, NSError *error) { + XCTAssertNotNil(error); + XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); + XCTAssertEqual(error.code, FIRFirestoreErrorCodeUnavailable); + [failedGetDocsCompletion fulfill]; + }]; [self awaitExpectations]; } -- (void)testGetNonExistingDocWhileOnlineWithDefaultGetOptions { +- (void)testGetNonExistingDocWhileOnlineWithDefaultSource { FIRDocumentReference *doc = [self documentRef]; // get doc and ensure that it does not exist and is *not* from the cache. @@ -486,7 +476,7 @@ - (void)testGetNonExistingDocWhileOnlineWithDefaultGetOptions { XCTAssertFalse(snapshot.metadata.hasPendingWrites); } -- (void)testGetNonExistingCollectionWhileOnlineWithDefaultGetOptions { +- (void)testGetNonExistingCollectionWhileOnlineWithDefaultSource { FIRCollectionReference *col = [self collectionRef]; // get collection and ensure it's empty and that it's *not* from the cache. @@ -497,7 +487,7 @@ - (void)testGetNonExistingCollectionWhileOnlineWithDefaultGetOptions { XCTAssertFalse(snapshot.metadata.hasPendingWrites); } -- (void)testGetNonExistingDocWhileOfflineWithDefaultGetOptions { +- (void)testGetNonExistingDocWhileOfflineWithDefaultSource { FIRDocumentReference *doc = [self documentRef]; // go offline for the rest of this test @@ -517,7 +507,7 @@ - (void)testGetNonExistingDocWhileOfflineWithDefaultGetOptions { [self awaitExpectations]; } -- (void)testGetNonExistingCollectionWhileOfflineWithDefaultGetOptions { +- (void)testGetNonExistingCollectionWhileOfflineWithDefaultSource { FIRCollectionReference *col = [self collectionRef]; // go offline for the rest of this test @@ -539,13 +529,13 @@ - (void)testGetNonExistingDocWhileOnlineCacheOnly { // certain documents *don't* exist. XCTestExpectation *getNonExistingDocCompletion = [self expectationWithDescription:@"getNonExistingDoc"]; - [doc getDocumentWithOptions:[FIRGetOptions optionsWithSource:FIRGetSourceCache] - completion:^(FIRDocumentSnapshot *snapshot, NSError *error) { - XCTAssertNotNil(error); - XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); - XCTAssertEqual(error.code, FIRFirestoreErrorCodeUnavailable); - [getNonExistingDocCompletion fulfill]; - }]; + [doc getDocumentWithSource:FIRGetSourceCache + completion:^(FIRDocumentSnapshot *snapshot, NSError *error) { + XCTAssertNotNil(error); + XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); + XCTAssertEqual(error.code, FIRFirestoreErrorCodeUnavailable); + [getNonExistingDocCompletion fulfill]; + }]; [self awaitExpectations]; } @@ -553,8 +543,7 @@ - (void)testGetNonExistingCollectionWhileOnlineCacheOnly { FIRCollectionReference *col = [self collectionRef]; // get collection and ensure it's empty and that it *is* from the cache. - FIRQuerySnapshot *snapshot = - [self readDocumentSetForRef:col options:[FIRGetOptions optionsWithSource:FIRGetSourceCache]]; + FIRQuerySnapshot *snapshot = [self readDocumentSetForRef:col source:FIRGetSourceCache]; XCTAssertEqual(snapshot.count, 0); XCTAssertEqual(snapshot.documentChanges.count, 0); XCTAssertTrue(snapshot.metadata.fromCache); @@ -572,13 +561,13 @@ - (void)testGetNonExistingDocWhileOfflineCacheOnly { // certain documents *don't* exist. XCTestExpectation *getNonExistingDocCompletion = [self expectationWithDescription:@"getNonExistingDoc"]; - [doc getDocumentWithOptions:[FIRGetOptions optionsWithSource:FIRGetSourceCache] - completion:^(FIRDocumentSnapshot *snapshot, NSError *error) { - XCTAssertNotNil(error); - XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); - XCTAssertEqual(error.code, FIRFirestoreErrorCodeUnavailable); - [getNonExistingDocCompletion fulfill]; - }]; + [doc getDocumentWithSource:FIRGetSourceCache + completion:^(FIRDocumentSnapshot *snapshot, NSError *error) { + XCTAssertNotNil(error); + XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); + XCTAssertEqual(error.code, FIRFirestoreErrorCodeUnavailable); + [getNonExistingDocCompletion fulfill]; + }]; [self awaitExpectations]; } @@ -589,8 +578,7 @@ - (void)testGetNonExistingCollectionWhileOfflineCacheOnly { [self disableNetwork]; // get collection and ensure it's empty and that it *is* from the cache. - FIRQuerySnapshot *snapshot = - [self readDocumentSetForRef:col options:[FIRGetOptions optionsWithSource:FIRGetSourceCache]]; + FIRQuerySnapshot *snapshot = [self readDocumentSetForRef:col source:FIRGetSourceCache]; XCTAssertEqual(snapshot.count, 0); XCTAssertEqual(snapshot.documentChanges.count, 0); XCTAssertTrue(snapshot.metadata.fromCache); @@ -601,8 +589,7 @@ - (void)testGetNonExistingDocWhileOnlineServerOnly { FIRDocumentReference *doc = [self documentRef]; // get doc and ensure that it does not exist and is *not* from the cache. - FIRDocumentSnapshot *snapshot = - [self readDocumentForRef:doc options:[FIRGetOptions optionsWithSource:FIRGetSourceServer]]; + FIRDocumentSnapshot *snapshot = [self readDocumentForRef:doc source:FIRGetSourceServer]; XCTAssertFalse(snapshot.exists); XCTAssertFalse(snapshot.metadata.fromCache); XCTAssertFalse(snapshot.metadata.hasPendingWrites); @@ -612,8 +599,7 @@ - (void)testGetNonExistingCollectionWhileOnlineServerOnly { FIRCollectionReference *col = [self collectionRef]; // get collection and ensure that it's empty and that it's *not* from the cache. - FIRQuerySnapshot *snapshot = - [self readDocumentSetForRef:col options:[FIRGetOptions optionsWithSource:FIRGetSourceServer]]; + FIRQuerySnapshot *snapshot = [self readDocumentSetForRef:col source:FIRGetSourceServer]; XCTAssertEqual(snapshot.count, 0); XCTAssertEqual(snapshot.documentChanges.count, 0); XCTAssertFalse(snapshot.metadata.fromCache); @@ -631,13 +617,13 @@ - (void)testGetNonExistingDocWhileOfflineServerOnly { // certain documents *don't* exist. XCTestExpectation *getNonExistingDocCompletion = [self expectationWithDescription:@"getNonExistingDoc"]; - [doc getDocumentWithOptions:[FIRGetOptions optionsWithSource:FIRGetSourceServer] - completion:^(FIRDocumentSnapshot *snapshot, NSError *error) { - XCTAssertNotNil(error); - XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); - XCTAssertEqual(error.code, FIRFirestoreErrorCodeUnavailable); - [getNonExistingDocCompletion fulfill]; - }]; + [doc getDocumentWithSource:FIRGetSourceServer + completion:^(FIRDocumentSnapshot *snapshot, NSError *error) { + XCTAssertNotNil(error); + XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); + XCTAssertEqual(error.code, FIRFirestoreErrorCodeUnavailable); + [getNonExistingDocCompletion fulfill]; + }]; [self awaitExpectations]; } @@ -649,13 +635,13 @@ - (void)testGetNonExistingCollectionWhileOfflineServerOnly { // attempt to get collection and ensure that it cannot be retreived XCTestExpectation *failedGetDocsCompletion = [self expectationWithDescription:@"failedGetDocs"]; - [col getDocumentsWithOptions:[FIRGetOptions optionsWithSource:FIRGetSourceServer] - completion:^(FIRQuerySnapshot *snapshot, NSError *error) { - XCTAssertNotNil(error); - XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); - XCTAssertEqual(error.code, FIRFirestoreErrorCodeUnavailable); - [failedGetDocsCompletion fulfill]; - }]; + [col getDocumentsWithSource:FIRGetSourceServer + completion:^(FIRQuerySnapshot *snapshot, NSError *error) { + XCTAssertNotNil(error); + XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); + XCTAssertEqual(error.code, FIRFirestoreErrorCodeUnavailable); + [failedGetDocsCompletion fulfill]; + }]; [self awaitExpectations]; } diff --git a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.h b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.h index fa12d868304..6c6844907d7 100644 --- a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.h +++ b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.h @@ -20,10 +20,11 @@ #import "Firestore/Example/Tests/Util/XCTestCase+Await.h" +#import "FIRGetSource.h" + @class FIRCollectionReference; @class FIRDocumentSnapshot; @class FIRDocumentReference; -@class FIRGetOptions; @class FIRQuerySnapshot; @class FIRFirestore; @class FIRFirestoreSettings; @@ -73,12 +74,11 @@ extern "C" { - (FIRDocumentSnapshot *)readDocumentForRef:(FIRDocumentReference *)ref; -- (FIRDocumentSnapshot *)readDocumentForRef:(FIRDocumentReference *)ref - options:(FIRGetOptions *)options; +- (FIRDocumentSnapshot *)readDocumentForRef:(FIRDocumentReference *)ref source:(FIRGetSource)source; - (FIRQuerySnapshot *)readDocumentSetForRef:(FIRQuery *)query; -- (FIRQuerySnapshot *)readDocumentSetForRef:(FIRQuery *)query options:(FIRGetOptions *)options; +- (FIRQuerySnapshot *)readDocumentSetForRef:(FIRQuery *)query source:(FIRGetSource)source; - (FIRDocumentSnapshot *)readSnapshotForRef:(FIRDocumentReference *)query requireOnline:(BOOL)online; diff --git a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm index ac98e877864..60e9979a310 100644 --- a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm +++ b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm @@ -209,39 +209,39 @@ - (void)readerAndWriterOnDocumentRef:(void (^)(NSString *path, } - (FIRDocumentSnapshot *)readDocumentForRef:(FIRDocumentReference *)ref { - return [self readDocumentForRef:ref options:[FIRGetOptions defaultOptions]]; + return [self readDocumentForRef:ref source:FIRGetSourceDefault]; } - (FIRDocumentSnapshot *)readDocumentForRef:(FIRDocumentReference *)ref - options:(FIRGetOptions *)options { + source:(FIRGetSource)source { __block FIRDocumentSnapshot *result; XCTestExpectation *expectation = [self expectationWithDescription:@"getData"]; - [ref getDocumentWithOptions:options - completion:^(FIRDocumentSnapshot *doc, NSError *_Nullable error) { - XCTAssertNil(error); - result = doc; - [expectation fulfill]; - }]; + [ref getDocumentWithSource:source + completion:^(FIRDocumentSnapshot *doc, NSError *_Nullable error) { + XCTAssertNil(error); + result = doc; + [expectation fulfill]; + }]; [self awaitExpectations]; return result; } - (FIRQuerySnapshot *)readDocumentSetForRef:(FIRQuery *)query { - return [self readDocumentSetForRef:query options:[FIRGetOptions defaultOptions]]; + return [self readDocumentSetForRef:query source:FIRGetSourceDefault]; } -- (FIRQuerySnapshot *)readDocumentSetForRef:(FIRQuery *)query options:(FIRGetOptions *)options { +- (FIRQuerySnapshot *)readDocumentSetForRef:(FIRQuery *)query source:(FIRGetSource)source { __block FIRQuerySnapshot *result; XCTestExpectation *expectation = [self expectationWithDescription:@"getData"]; - [query getDocumentsWithOptions:options - completion:^(FIRQuerySnapshot *documentSet, NSError *error) { - XCTAssertNil(error); - result = documentSet; - [expectation fulfill]; - }]; + [query getDocumentsWithSource:source + completion:^(FIRQuerySnapshot *documentSet, NSError *error) { + XCTAssertNil(error); + result = documentSet; + [expectation fulfill]; + }]; [self awaitExpectations]; return result; diff --git a/Firestore/Source/API/FIRDocumentReference.m b/Firestore/Source/API/FIRDocumentReference.m index 464217a4fa3..cd7e7887d2d 100644 --- a/Firestore/Source/API/FIRDocumentReference.m +++ b/Firestore/Source/API/FIRDocumentReference.m @@ -19,12 +19,12 @@ #import #import "FIRFirestoreErrors.h" +#import "FIRGetSource.h" #import "FIRSnapshotMetadata.h" #import "Firestore/Source/API/FIRCollectionReference+Internal.h" #import "Firestore/Source/API/FIRDocumentReference+Internal.h" #import "Firestore/Source/API/FIRDocumentSnapshot+Internal.h" #import "Firestore/Source/API/FIRFirestore+Internal.h" -#import "Firestore/Source/API/FIRGetOptions+Internal.h" #import "Firestore/Source/API/FIRListenerRegistration+Internal.h" #import "Firestore/Source/API/FIRSetOptions+Internal.h" #import "Firestore/Source/API/FSTUserDataConverter.h" @@ -210,13 +210,13 @@ - (void)deleteDocumentWithCompletion:(nullable void (^)(NSError *_Nullable error - (void)getDocumentWithCompletion:(void (^)(FIRDocumentSnapshot *_Nullable document, NSError *_Nullable error))completion { - return [self getDocumentWithOptions:[FIRGetOptions defaultOptions] completion:completion]; + return [self getDocumentWithSource:FIRGetSourceDefault completion:completion]; } -- (void)getDocumentWithOptions:(FIRGetOptions *)options - completion:(void (^)(FIRDocumentSnapshot *_Nullable document, - NSError *_Nullable error))completion { - if (options.source == FIRGetSourceCache) { +- (void)getDocumentWithSource:(FIRGetSource)source + completion:(void (^)(FIRDocumentSnapshot *_Nullable document, + NSError *_Nullable error))completion { + if (source == FIRGetSourceCache) { [self.firestore.client getDocumentFromLocalCache:self completion:completion]; return; } @@ -255,18 +255,16 @@ - (void)getDocumentWithOptions:(FIRGetOptions *)options NSLocalizedDescriptionKey : @"Failed to get document because the client is offline.", }]); - } else if (snapshot.exists && snapshot.metadata.fromCache && - options.source == FIRGetSourceServer) { - completion( - nil, [NSError errorWithDomain:FIRFirestoreErrorDomain - code:FIRFirestoreErrorCodeUnavailable - userInfo:@{ - NSLocalizedDescriptionKey : - @"Failed to get document from server. (However, this " - @"document does exist in the local cache. Run again " - @"without setting FIRGetSourceServer in the FIRGetOptions to " - @"retrieve the cached document.)" - }]); + } else if (snapshot.exists && snapshot.metadata.fromCache && source == FIRGetSourceServer) { + completion(nil, [NSError errorWithDomain:FIRFirestoreErrorDomain + code:FIRFirestoreErrorCodeUnavailable + userInfo:@{ + NSLocalizedDescriptionKey : + @"Failed to get document from server. (However, this " + @"document does exist in the local cache. Run again " + @"without setting source to FIRGetSourceServer to " + @"retrieve the cached document.)" + }]); } else { completion(snapshot, nil); } diff --git a/Firestore/Source/API/FIRGetOptions+Internal.h b/Firestore/Source/API/FIRGetOptions+Internal.h deleted file mode 100644 index da3bdc9de6b..00000000000 --- a/Firestore/Source/API/FIRGetOptions+Internal.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2018 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 "FIRGetOptions.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface FIRGetOptions () - -/** Where getDocument[s] calls should get their data from. */ -@property(nonatomic, readonly, getter=source) FIRGetSource source; - -- (instancetype)initWithSource:(FIRGetSource)source; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/API/FIRGetOptions.m b/Firestore/Source/API/FIRGetOptions.m deleted file mode 100644 index e43f25f837b..00000000000 --- a/Firestore/Source/API/FIRGetOptions.m +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2018 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/Source/API/FIRGetOptions+Internal.h" - -NS_ASSUME_NONNULL_BEGIN - -@implementation FIRGetOptions - -+ (instancetype)defaultOptions { - return [[FIRGetOptions alloc] initWithSource:FIRGetSourceDefault]; -} - -- (instancetype)initWithSource:(FIRGetSource)source { - if (self = [super init]) { - _source = source; - } - return self; -} - -+ (instancetype)optionsWithSource:(FIRGetSource)source { - return [[FIRGetOptions alloc] initWithSource:source]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/API/FIRQuery.m b/Firestore/Source/API/FIRQuery.m index 06fa5e8ae3e..cb62016b60b 100644 --- a/Firestore/Source/API/FIRQuery.m +++ b/Firestore/Source/API/FIRQuery.m @@ -18,11 +18,11 @@ #import "FIRDocumentReference.h" #import "FIRFirestoreErrors.h" +#import "FIRGetSource.h" #import "Firestore/Source/API/FIRDocumentReference+Internal.h" #import "Firestore/Source/API/FIRDocumentSnapshot+Internal.h" #import "Firestore/Source/API/FIRFieldPath+Internal.h" #import "Firestore/Source/API/FIRFirestore+Internal.h" -#import "Firestore/Source/API/FIRGetOptions+Internal.h" #import "Firestore/Source/API/FIRListenerRegistration+Internal.h" #import "Firestore/Source/API/FIRQuery+Internal.h" #import "Firestore/Source/API/FIRQuerySnapshot+Internal.h" @@ -132,13 +132,13 @@ - (NSUInteger)hash { - (void)getDocumentsWithCompletion:(void (^)(FIRQuerySnapshot *_Nullable snapshot, NSError *_Nullable error))completion { - [self getDocumentsWithOptions:[FIRGetOptions defaultOptions] completion:completion]; + [self getDocumentsWithSource:FIRGetSourceDefault completion:completion]; } -- (void)getDocumentsWithOptions:(FIRGetOptions *)options - completion:(void (^)(FIRQuerySnapshot *_Nullable snapshot, - NSError *_Nullable error))completion { - if (options.source == FIRGetSourceCache) { +- (void)getDocumentsWithSource:(FIRGetSource)source + completion:(void (^)(FIRQuerySnapshot *_Nullable snapshot, + NSError *_Nullable error))completion { + if (source == FIRGetSourceCache) { [self.firestore.client getDocumentsFromLocalCache:self completion:completion]; return; } @@ -161,17 +161,16 @@ - (void)getDocumentsWithOptions:(FIRGetOptions *)options dispatch_semaphore_wait(registered, DISPATCH_TIME_FOREVER); [listenerRegistration remove]; - if (snapshot.metadata.fromCache && options.source == FIRGetSourceServer) { - completion( - nil, [NSError errorWithDomain:FIRFirestoreErrorDomain - code:FIRFirestoreErrorCodeUnavailable - userInfo:@{ - NSLocalizedDescriptionKey : - @"Failed to get documents from server. (However, these " - @"documents may exist in the local cache. Run again " - @"without setting FIRGetSourceServer in the FIRGetOptions to " - @"retrieve the cached documents.)" - }]); + if (snapshot.metadata.fromCache && source == FIRGetSourceServer) { + completion(nil, [NSError errorWithDomain:FIRFirestoreErrorDomain + code:FIRFirestoreErrorCodeUnavailable + userInfo:@{ + NSLocalizedDescriptionKey : + @"Failed to get documents from server. (However, these " + @"documents may exist in the local cache. Run again " + @"without setting source to FIRGetSourceServer to " + @"retrieve the cached documents.)" + }]); } else { completion(snapshot, nil); } diff --git a/Firestore/Source/Core/FSTFirestoreClient.m b/Firestore/Source/Core/FSTFirestoreClient.m index 6e076c4ec7f..26da254b645 100644 --- a/Firestore/Source/Core/FSTFirestoreClient.m +++ b/Firestore/Source/Core/FSTFirestoreClient.m @@ -271,17 +271,15 @@ - (void)getDocumentFromLocalCache:(FIRDocumentReference *)doc fromCache:YES], nil); } else { - completion( - nil, - [NSError errorWithDomain:FIRFirestoreErrorDomain - code:FIRFirestoreErrorCodeUnavailable - userInfo:@{ - NSLocalizedDescriptionKey : - @"Failed to get document from cache. (However, this " - @"document may exist on the server. Run again without " - @"setting FIRGetSourceCache in the FIRGetOptions to attempt to " - @"retrieve the document from the server.)", - }]); + completion(nil, [NSError errorWithDomain:FIRFirestoreErrorDomain + code:FIRFirestoreErrorCodeUnavailable + userInfo:@{ + NSLocalizedDescriptionKey : + @"Failed to get document from cache. (However, this " + @"document may exist on the server. Run again without " + @"setting source to FIRGetSourceCache to attempt to " + @"retrieve the document from the server.)", + }]); } }]; } diff --git a/Firestore/Source/Public/FIRDocumentReference.h b/Firestore/Source/Public/FIRDocumentReference.h index fc4ffc13fad..39d95b1ffe3 100644 --- a/Firestore/Source/Public/FIRDocumentReference.h +++ b/Firestore/Source/Public/FIRDocumentReference.h @@ -16,12 +16,12 @@ #import +#import "FIRGetSource.h" #import "FIRListenerRegistration.h" @class FIRFirestore; @class FIRCollectionReference; @class FIRDocumentSnapshot; -@class FIRGetOptions; @class FIRSetOptions; NS_ASSUME_NONNULL_BEGIN @@ -191,14 +191,27 @@ NS_SWIFT_NAME(DocumentReference) /** * Reads the document referenced by this `FIRDocumentReference`. * + * This method attempts to provide up-to-date data when possible by waiting for + * data from the server, but it may return cached data or fail if you are + * offline and the server cannot be reached. See the + * `getDocument(source:completion:)` method to change this behavior. + * * @param completion a block to execute once the document has been successfully read. */ - (void)getDocumentWithCompletion:(FIRDocumentSnapshotBlock)completion NS_SWIFT_NAME(getDocument(completion:)); +/** + * Reads the document referenced by this `FIRDocumentReference`. + * + * @param source indicates whether the results should be fetched from the cache + * only (`Source.cache`), the server only (`Source.server`), or to attempt + * the server and fall back to the cache (`Source.default`). + * @param completion a block to execute once the document has been successfully read. + */ // clang-format off -- (void)getDocumentWithOptions:(FIRGetOptions *)options completion:(FIRDocumentSnapshotBlock)completion - NS_SWIFT_NAME(getDocument(options:completion:)); +- (void)getDocumentWithSource:(FIRGetSource)source completion:(FIRDocumentSnapshotBlock)completion + NS_SWIFT_NAME(getDocument(source:completion:)); // clang-format on /** diff --git a/Firestore/Source/Public/FIRGetOptions.h b/Firestore/Source/Public/FIRGetOptions.h deleted file mode 100644 index de283255ecf..00000000000 --- a/Firestore/Source/Public/FIRGetOptions.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2018 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 - -NS_ASSUME_NONNULL_BEGIN - -/** - * An options object that configures the behavior of - * `DocumentReference.getDocument()` and `CollectionReference.getDocuments()`. - * By providing a `GetOptions` object the `getDocument[s]` methods can be - * configured to fetch results only from the server, only from the local cache, - * or attempt the server and fall back to the cache (which is the default). - */ -NS_SWIFT_NAME(GetOptions) -@interface FIRGetOptions : NSObject - -/** */ -- (instancetype)init __attribute((unavailable("FIRGetOptions cannot be created directly."))); - -/** - * Returns the default options. - * - * Equiavlent to `[FIRGetOptions source:FIRGetSourceDefault]` in - * objective-c. - */ -+ (instancetype)defaultOptions NS_SWIFT_NAME(defaultOptions()); - -/** - * Describes whether we should get from server or cache. - * - * Setting the FIRGetOption source to FIRGetSourceDefault, if online, causes - * Firestore to try to give a consistent (server-retrieved) snapshot, or else - * revert to the cache to provide a value. - * - * FIRGetSourceServer causes Firestore to avoid the cache (generating an error - * if a value cannot be retrieved from the server). The cache will be updated - * if the RPC succeeds. Latency compensation still occurs (implying that if the - * cache is more up to date, then it's values will be merged into the results). - * - * FIRGetSourceCache causes Firestore to immediately return a value from the - * cache, ignoring the server completely (implying that the returned value may - * be stale with respect to the value on the server.) For a single document, - * the get will fail if the document doesn't exist. - */ -typedef NS_ENUM(NSUInteger, FIRGetSource) { - FIRGetSourceDefault, - FIRGetSourceServer, - FIRGetSourceCache -} NS_SWIFT_NAME(GetSource); - -/** - * Initializes the get options with the specified source. - */ -+ (instancetype)optionsWithSource:(FIRGetSource)source NS_SWIFT_NAME(source(_:)); - -@end - -NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Public/FIRGetSource.h b/Firestore/Source/Public/FIRGetSource.h new file mode 100644 index 00000000000..0ecc6c1f0d0 --- /dev/null +++ b/Firestore/Source/Public/FIRGetSource.h @@ -0,0 +1,48 @@ +/* + * Copyright 2018 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 + +/** + * An enum that configures the behavior of `DocumentReference.getDocument()` and + * `Query.getDocuments()`. By providing a source enum the `getDocument[s]` + * methods can be configured to fetch results only from the server, only from + * the local cache, or attempt to fetch results from the server and fall back to + * the cache (which is the default). + * + * Setting the source to `Source.default` causes Firestore to try to retrieve an + * up-to-date (server-retrieved) snapshot, but fall back to returning cached + * data if the server can't be reached. + * + * Setting the source to `Source.server` causes Firestore to avoid the cache, + * generating an error if the server cannot be reached. Note that the cache will + * still be updated if the server request succeeds. Also note that + * latency-compensation still takes effect, so any pending write operations will + * be visible in the returned data (merged into the server-provided data). + * + * Setting the source to `Source.cache` causes Firestore to immediately return a + * value from the cache, ignoring the server completely (implying that the + * returned value may be stale with respect to the value on the server). If + * there is no data in the cache to satisfy the `getDocument[s]` call, + * `DocumentReference.getDocument()` will return an error and + * `QuerySnapshot.getDocuments()` will return an empty `QuerySnapshot` with no + * documents. + */ +typedef NS_ENUM(NSUInteger, FIRGetSource) { + FIRGetSourceDefault, + FIRGetSourceServer, + FIRGetSourceCache +} NS_SWIFT_NAME(Source); diff --git a/Firestore/Source/Public/FIRQuery.h b/Firestore/Source/Public/FIRQuery.h index 0ddfe3c7150..b852fffd0f3 100644 --- a/Firestore/Source/Public/FIRQuery.h +++ b/Firestore/Source/Public/FIRQuery.h @@ -16,11 +16,11 @@ #import +#import "FIRGetSource.h" #import "FIRListenerRegistration.h" @class FIRFieldPath; @class FIRFirestore; -@class FIRGetOptions; @class FIRQuerySnapshot; @class FIRDocumentSnapshot; @@ -85,15 +85,29 @@ NS_SWIFT_NAME(Query) /** * Reads the documents matching this query. * + * This method attempts to provide up-to-date data when possible by waiting for + * data from the server, but it may return cached data or fail if you are + * offline and the server cannot be reached. See the + * `getDocuments(source:completion:)` method to change this behavior. + * * @param completion a block to execute once the documents have been successfully read. * documentSet will be `nil` only if error is `non-nil`. */ - (void)getDocumentsWithCompletion:(FIRQuerySnapshotBlock)completion NS_SWIFT_NAME(getDocuments(completion:)); +/** + * Reads the documents matching this query. + * + * @param source indicates whether the results should be fetched from the cache + * only (`Source.cache`), the server only (`Source.server`), or to attempt + * the server and fall back to the cache (`Source.default`). + * @param completion a block to execute once the documents have been successfully read. + * documentSet will be `nil` only if error is `non-nil`. + */ // clang-format off -- (void)getDocumentsWithOptions:(FIRGetOptions *)options completion:(FIRQuerySnapshotBlock)completion - NS_SWIFT_NAME(getDocuments(options:completion:)); +- (void)getDocumentsWithSource:(FIRGetSource)source completion:(FIRQuerySnapshotBlock)completion + NS_SWIFT_NAME(getDocuments(source:completion:)); // clang-format on /** From 6cdbc992202d06502b0b409a793f9c7809169e58 Mon Sep 17 00:00:00 2001 From: rsgowman Date: Thu, 8 Mar 2018 10:36:11 -0500 Subject: [PATCH 4/7] merge master to firestore-api-changes (#868) * Version bump for 4.8.1 Release Updated the version numbers of pods which are being released in 4.8.1 * Update from master Picking up a few last-minute changes to CHANGELOGs and tests for 4.8.1 release. * Update Core info for M21.1 Update missing version bump for M21.1 for FirebaseCore. * Remove over-aggressive assert. closeWithFinalState: assumes delegate != nil, but that is not true if when startWithdelegate: was called we entered backoff (performBackoffWithDelegate:) and so self.delegate did not get assigned yet. We could rework the code to make the assertion hold, but per offline discussion this assert doesn't represent an invariant that we care about and so I'm just removing it. * Remove over-aggressive closeWithFinalState: delegate assert. (#656) Fixes #596. closeWithFinalState: asserted delegate != nil, but that is not true if when startWithdelegate: was called we entered backoff (performBackoffWithDelegate:) and so self.delegate did not get assigned yet. We could rework the code to make the assertion hold, but per offline discussion this assert doesn't represent an invariant that we care about maintaining and so I'm just removing it. * Increase FirebaseAuth version for M21.1 This version was missed in the M21.1 version bump PR. * Fix import formatting (#660) * Fix issue @morganchen12 discovered where we weren't properly creating FIRQueryDocumentSnapshot instances. (#662) * Validate clang-format compliance in travis (#648) * Build gRPC for Firestore C++ (#652) * Clean up quoting and other minor issues * Reorganize CMake build output Make it clearer which parts of the output pertain to external projects. * Use a consistent ordering of ExternalProject arguments * Prevent the top-level build from running in parallel This prevents spurious failures when running make -j. * Actually parse arguments in the xcodebuild function * Use ExternalProject features when available * submodule limits from CMake 3.0 * shallow clones from CMake 3.6 * git progress output from CMake 3.8 * Only build the parts of leveldb we need Skip building the tools and other libraries * Avoid installing ExternalProjects Consume build output directly so that we can build just the targets we need. Installing causes all targets to be built. This doesn't matter as much for these targets but the gRPC build includes a ton of stuff we don't need so it's worth adopting this as a general strategy. * Define an external build for grpc * Test that grpc can link successfully. * Add a FindGRPC CMake module * Actually comment ExternalProjext_GitSource * Inject infoDictionary to fix flakey tests. (#664) * Inject infoDictionary to fix flakey tests. * Remove outdated comment, update format. * Fix issue @morganchen12 discovered where we weren't properly creating FIRQueryDocumentSnapshot instances. (#662) * Travis - run tests only for changed code (#665) * Add assert_test to the Xcode build (#671) * Exclude stdio-backed assert from the Xcode build * Add assert_test to the Xcode build * Enable warnings in the CMake build (#669) * Enable warnings when building with GCC or clang * Fix warnings * Fix C++ lint errors (#668) * Misc style.sh fixes * Allow test-only to use a revision; to check your changes since master: ./scripts/style.sh test-only master * Avoid diffing deleted files * 80 columns * Fix C++ lint errors * implement FieldValue for null, boolean, and array in C++. (#637) * implement FieldValue for null and boolean. * refactoring to use union type instead of poly * refactor using union design intead of poly * refactoring to use anonymous union and fix styles * small fix * add field_value_test to the project * fix warning of cmake and fix style * Simplify integration with googletest (#672) This makes it possible to build the Firestore subproject with CLion because it no longer needs to be told where googletest is. * Add a cc_library to the CMake build (#670) * Rewrite cc_test to take named arguments Cut down on build file verbosity by having cc_test take SOURCES and DEPENDS. The separate invocation of target_link_libraries is no longer necessary. * Add a cc_library rule to parallel cc_test This cuts down on build file verbosity. * Automatically add OBJC_FLAGS to cc_libraries if applicable * Exclude platform-specific libraries from 'all' This is makes it possible to declare this kind of library unconditionally. Usage within a test or as a dependency will actually trigger building. * Restore secure_random_test.cc; clean-up comments * Fix CMake build and lint warnings in field_value.cc (#677) Fixes these cpplint warnings: Firestore/core/src/firebase/firestore/model/field_value.cc:162: Semicolon defining empty statement. Use {} instead. [whitespace/semicolon] [5] Firestore/core/src/firebase/firestore/model/field_value.cc:170: Semicolon defining empty statement. Use {} instead. [whitespace/semicolon] [5] Firestore/core/src/firebase/firestore/model/field_value.cc:126: Add #include for swap [build/include_what_you_use] [4] * Listen sequence numbers (#675) * Generate and save sequence numbers for listens * Add documentation * Fix include path * Fix unavailable comment * Review feedback * Fix build warnings by making void parameter explicit (#681) * Add platform detection logic for SecureRandom (#676) * Add CMake platform detection logic for SecureRandom Now only builds secure_random_arc4random.cc if available. Remove firebase/firestore/base/port.h. Nothing else was in that directory. * Add a SecureRandom implementation that uses OpenSSL This is usable on Linux, Windows, and Android * Properly check return from RAND_bytes * Port comparison to C++ (#678) This reimplements our comparison functions as C++ Comparators and then provides compatibility shims for interoperating with existing Objective-C usage. A few specialized comparators aren't suitable for porting but only have a single usage (e.g. CompareBytes for comparing NSData * instances). In these cases I've moved them into the caller. * Use int32_t for typeof(ID) in FSTDocumentReference * Migrate callers of FSTComparison.h to Objective-C++ * Port comparison to C++ * Migrate usages of FSTComparison.h to C++ equivalents * Remove FSTComparison * Use Comparator in FieldValue. (#686) * style.sh - output unformatted files in test-only and ignore generated config.h (#690) * Cleanup imports and isEqual (#685) * Fix headers * Fix isEqual verbosity * Fix isEqual for nullable properties * Fix nullability on FSTTestDocSnapshot * Fixing spelling error in FIRStorageErrors * Update travis to use CocoaPods 1.4.0 (#692) * add FIRUser behavior to documentation * Disable Messaging build warnings (#697) * Update FirebaseMessaging protos (#696) * Adding enable/disable property to FCM token auto Initialization * revert unrelated changes * revert unrelated changes * revert unrelated changes * Adds explicit core graphics dependency to auth (#700) * Properly publish Abseil sources as a part of the podspec (#704) * Properly include abseil sources * Exclude abseil tests * Implement the rest of FieldValue types for C++ (#687) * implement FieldValue for null and boolean. * Implement number and string FieldValue. * Implement object FieldValue. * implement timestamp FieldValue. * Implement number and string FieldValue. * implement public type `Blob` and `GeoPoint` * implement Blob FieldValue * Implement GeoPoint FieldValue * refactoring `Blob` * README - FirebaseCore source pod must be included with all source pods (#705) * Fix incorrect deprecation message (#688) * Fix incorrect deprecation message for Obj-C and Swift * Update CHANGELOG for M21.1 release (#679) * normalize string_util (#708) * refactoring string_util * port string_util to iOS * implement `TargetIdGenerator` in C++ for Firestore (#701) * implement `TargetIdGenerator` * address changes * adding unit test for auto init enable function (#710) * port TargetIdGenerator to iOS (#709) * port TargetIdGenerator to iOS * fix style * move pointer property to instance variable * TriggerTravis * normalize and port the rest of Firebase/Port code (#713) * normalize bits * normalize ordered_code * Fix b/72502745: OnlineState changes cause limbo document crash. (#470) (#714) [PORT OF https://github.com/firebase/firebase-js-sdk/pull/470] Context: I made a previous change to raise isFromCache=true events when the client goes offline. As part of this change I added an assert for "OnlineState should not affect limbo documents", which it turns out was not valid because: * When we go offline, we set the view to non-current and call View.applyChanges(). * View.applyChanges() calls applyTargetChange() even though there's no target change and it recalculates limbo documents. * When the view is not current, we consider no documents to be in limbo. * Therefore all limbo documents are removed and so applyChanges() ends up returning unexpected LimboDocumentChanges. Fix: I've modified the View logic so that we don't recalculate limbo documents (and generate LimboDocumentChanges) when the View is not current, so now my assert holds and there should be less spurious removal / re-adding of limbo documents. * travis: check for copyright in sources (#717) * Add the C++ linter to the repo (#720) * Fix a number of c++ build errors (#715) * Move -fvisibility-inlines-hidden from common_flags to cxx_flags This option isn't supported by C (and causes the build to fail under at least rodete). Manifested error was: ``` -- Looking for include file openssl/rand.h -- Looking for include file openssl/rand.h - not found CMake Error at core/src/firebase/firestore/util/CMakeLists.txt:94 (message): No implementation for SecureRandom available. -- Configuring incomplete, errors occurred! ``` which is completely misleading. :( * Fix exception include for std::logic_error Was causing compiler error on (at least) rodete. (http://en.cppreference.com/w/cpp/error/logic_error suggests that stdexcept is the correct header.) * Remove 'const' from vector contents. vectors cannot contain const objects as the contained objects are required to be assignable and copy constructable. (Not *quite* strictly true; since c++11, this requirement now depends on the operations performed on the container. But my compiler objects at any rate.) http://en.cppreference.com/w/cpp/container/vector https://stackoverflow.com/questions/8685257/why-cant-you-put-a-const-object-into-a-stl-container * Add brackets as suggested (required) by my compiler. * Add missing include For LLONG_MIN, etc. * Enable gnu extension to c++11 for firestore/util *test*. We disable them by default (in cmake/CompilerSetup.cmake) but our tests requires hexidecimal floating point, which is not supported in c++11 (though is supported in C++17, gnu++11, and others). http://en.cppreference.com/w/cpp/language/floating_literal https://bugzilla.redhat.com/show_bug.cgi?id=1321986 * Fix printf format for uint64_t http://en.cppreference.com/w/cpp/types/integer https://stackoverflow.com/questions/9225567/how-to-print-a-int64-t-type-in-c * Allow 0 length printf template strings in tests. * Get rid of a multi line comment. The backslash is apparently interpreted by the compiler cause the next line to be ignored too. In this case, the next line is (currently) a comment, and would be ignored anyways. (But that doesn't stop the compiler from yelling.) * Run ./scripts/style.sh * Version updates to 4.8.2 (#722) * Use fixed-sized types (#719) Should have caught this during review, but cpplint caught it * Import iterator_adaptors from google3 (#718) * Import iterator_adapters from google3 * Remove -Wconversion which is annoyingly hard to satisfy * Strip dependency on absl_container from iterator_adapters_test * Format and lint iterator_adaptors * More flexible copyright checking in Travis * Add changelog entry for my last PR (oops) and also add a few that we missed last release. (#724) * Add absl_strings to firebase_firestore_util_test dependencies (#725) Required to link the test on rodete. * Firestore DatabaseId in C++ (#727) * Implement DataBaseId in C++ * add database_id_test to project * fix project * address changes * fix style * Schema migrations for LevelDB (#728) * Implement schema versions * Style fixes * newlines, copyrights, assumptions * Fix nullability * Raw ptr -> shared_ptr * kVersionTableGlobal -> kVersionGlobalTable * Drop utils, move into static methods * Drop extra include * Add a few more comments * Move version constant into migrations file * formatting? * Fix comment * [FCM] Add completion handler to subscribe/unsubscribe topic actions Added functionality not exposed in public header yet. I would leave to next FCM SDK owner to expose this functionality (and follow any required review process). Bugs: 72701086 * Fix tests. * Move all Firestore Objective-C to Objective-C++ (#734) * Move all Firestore files to Objective-C++ * Update project file references * Don't use module imports from Objective-C++ * Use extern "C" for C-accessible globals * Work around more stringent type checking in Objective-C++ * NSMutableDictionary ivars aren't implicitly casted to NSDictionary * FSTMaybeDocument callback can't be passed a function that accepts FSTDocument * NSComparisonResult can't be multiplied by -1 without casting * Add a #include where needed * Avoid using C++ keywords as variables * Remove #if __cplusplus guards * Start on ArraySortedMap in C++ (#721) * Implement ArraySortedMap.remove * Implement ArraySortedMap.insert * Ensure ArraySortedMap.insert avoids copying on duplicates * Port more ArraySortedMapTests * Remove predecessorKey,Object,Document, etc (#735) This is dead code. I think it was probably useful in the RTDB because of the way it notified of changes, but we give changes with indexes in Firestore so I think we don't need it. * Align tests and integration test header search paths (#737) * fix (#739) * Increase expectation timeout to 25 seconds. (#744) I'm doing this to: 1) Experimentally see if it improves the flakiness we've been seeing in the Query Conformance Tests. 2) Insulate us from the fact that GRPC seems to take a /minimum/ of 10 seconds to reconnect (at least in some cases) after a connection failure. I've opened b/72864027 to revisit this in the future. * fix null block execution crash * Make sure Firestore/core/include is in the podspec (#748) * Skip 'update' step for external dependencies We check them out from a git tag, so this *should* be a noop. However, cmake seems to want to rebuild these dependencies every time you run make as it assumes the dependency *might* have been updated. (In practice, this isn't completely awful, as make notices the files haven't changed, so files don't actually get recompiled. But the configure step is still re-run and all the files still need to be rescanned.) Skipping the update step speeds up the build considerably. On my linux box, running: cmake .. && make -j && time make -j takes ~8.5s prior to this CL and ~6.5 afterwards. (6s is used by the test suite.) The upcoming protobuf addition would otherwise have made this much worse. (It takes a long time to ./configure.) * Add ability to build nanopb and protobuf * Add instructions for building nanopb protos Currently only supported on osx * Add generated nanopb protos. 100% machine generated (except adding of the license). * Import "well-known" protos (and generated nanopb files) For use by nanopb (which otherwise doesn't know what to do with these.) * Hook up nanopb to firestorep project Use remote/serializer placeholder class as a hook for the test to ensure nanopb headers can be found, and test can be linked. * style.sh now ignores generated nanopb files * ./style.sh * Eliminate sequencing dependencies We originally had this as it was thought that cmake would spawn n*m jobs (where n is the number of jobs it was instructed to create, ie. make -j8, and m is the number of external projects.) However, it isn't supposed to do that, and doesn't appear to be doing that right now. So we'll remove this. If the problem re-appears, we'll add these back in. (Symptom was being unable to spawn /bin/sh.) * Downgrade nanopb from 0.4.0-dev to 0.3.8. Also regenerate the protos * Minor refactoring * Ignore trailing whitespace in autogenerated nanopb files * Update abseil-cpp to a new upstream (#754) Update to bf7fc9986e20f664958fc227547fd8d2fdcf863e * Add StrCat, StrJoin and the rest of //strings from abseil-cpp (#756) From abseil-cpp version bf7fc9986e20f664958fc227547fd8d2fdcf863e * Fix xcode build errors re nanopb Involves adding PODS_ROOT/nanopb to include path (to allow include ) and Firestore/Protos/nanopb to include path (to allow include "google/api/annotations.pb.h" and similar). In both cases, this is to allow auto-generated code to function properly. * Fix firebaes typo (#755) * Implement Firestore DatabaseInfo and port both Database{Id,Info} C++ to the iOS code (#738) * implement Firestore DatabaseInfo in C++ * temporary stash changes; blocking on the massive renaming of .m to .mm * add database_info_test to project * finish port DatabaseId and fix style, modular fixing DatabaseInfo * port DatabaseInfo * remove FSTDatabase{ID,Info} and their tests from project * fix unit test * use namespace alias * use namespace alias, leftover * address more changes * refactoring to use raw pointer instead of value for property * address changes * remove self-> * fix style * remove the name suffix Alloc * fix a bug * C++ port: port FSTFieldPath and FSTResourcePath to C++ (#749) Similar to Objective-C, FieldPath and ResourcePath share most of their interface (and implementation) by deriving from BasePath (using CRTP, so that factory methods in BasePath can return an instance of the derived class). * Creating CHANGELOG for FCM (#764) * add CHANGELOG for FCM * fix the typo * avoid using initWithSuiteName which does not support iOS 7 (#765) * Updates version of Firebase Auth in Changelog (#771) * Updates version of Firebase Auth in Changelog * addresses comments * Update CHANGELOG for Firestore v0.10.1 (#768) * Explicitly specify the default constructor to FieldPath (#773) Xcode prior to 8.3 does not accept an explicitly defaulted constructor (`= default`) for the purposes of default initializing a const object. This fixes a build failure under Xcode 8.2: ``` Firestore/core/src/firebase/firestore/model/field_path.cc:144:26: error: default initialization of an object of const type 'const firebase::firestore::model::FieldPath' without a user-provided default constructor static const FieldPath empty_path; ``` * Update deprecation message for Firestore. (#758) * Update FIRFirestore.h * Provide full command to enable debugging. * Version bumps for 4.9.0 (#774) * cmake build fixes (#770) * Fix nanopb (in cmake build) Look for binaries in the src dir (since that's where we build now.) This error would be masked if a previous build had completed prior to switching nanopb to build out of src. Also, don't patch the protoc path multiple times. This could be triggered by (eg) 'make && make clean && make'. * Add resource_path.{h,cc} to the cmake build * Fix signed/unsigned int comparison warnings * Ensure FieldValue tag_ is initialized during cp/mv ctor. Otherwise, the assignment operator attempts to deallocate based on the (uninitialized) tag_ variable, posssibly leading to segfaults. * Fix tests that throw exceptions. The (previous) tests checked to ensure that an abort() occurs, but if ABSL_HAVE_EXCEPTIONS is defined on non-macos (which is currently the default) then the assertions will throw a std::logic_error rather than abort()ing. On macos, an exception is thrown too, but the exception doesn't derrive from std::exception, so ASSERT_DEATH_* doesn't catch it (hence why ASSERT_DEATH_* actually works.) To resolve this, I've switched to ASSERT_ANY_THROW. * Let Travis run for `CMake` test and `lint.sh` (#769) * Fix nanopb (in cmake build) Look for binaries in the src dir (since that's where we build now.) This error would be masked if a previous build had completed prior to switching nanopb to build out of src. Also, don't patch the protoc path multiple times. This could be triggered by (eg) 'make && make clean && make'. * Add resource_path.{h,cc} to the cmake build * Fix signed/unsigned int comparison warnings * let Travis run for `CMake` test and `lint.sh` * Ensure FieldValue tag_ is initialized during cp/mv ctor. Otherwise, the assignment operator attempts to deallocate based on the (uninitialized) tag_ variable, posssibly leading to segfaults. * address change * fix trailing space * address change * moving Firestore checks closer together * Fix tests that throw exceptions. The (previous) tests checked to ensure that an abort() occurs, but if ABSL_HAVE_EXCEPTIONS is defined on non-macos (which is currently the default) then the assertions will throw a std::logic_error rather than abort()ing. On macos, an exception is thrown too, but the exception doesn't derrive from std::exception, so ASSERT_DEATH_* doesn't catch it (hence why ASSERT_DEATH_* actually works.) To resolve this, I've switched to ASSERT_ANY_THROW. * ./scripts/lint.sh * Move FieldValue::tag_ initializer to be in class. * check Travis ulimit * check travis limit * set make -j 200 instead of unlimited * use cpu core number instead of 200 * port Firestore Auth module in C++ (#733) * Implement firestore/auth/user * add user to project and some fixes * implement firestore/auth/{credentials_provider,empty_credentials_provider} * implement firestore/auth/firebase_credentials_provider * refactoring firebase_credentials_provider and add (disabled but working) unit test * add auth test to project * address changes * small fix to style and project * fix the firebase_credentials_provider_test * fix style * address changes * revert the change to static mutex_ * remove my custom plist path * fix style * address changes * refactoring FirebaseCredentialsProvider to fix the issue w.r.t. auth global dispatch queue * add /*force_refresh=*/ tag to bool literal for style purpose * Use a shared_ptr/weak_ptr handoff on FirebaseCredentialsProvider (#778) * Revert "refactoring FirebaseCredentialsProvider to fix the issue w.r.t. auth global dispatch queue" This reverts commit 87175a4146267d403a774f138b85f8d3b532fa4b. * Use a shared_ptr/weak_ptr handoff on FirebaseCredentialsProvider This avoids any problems with callsbacks retaining pointers to objects destroyed by a C++ destructor * port Firestore SnapshotVersion in C++ (#767) * implement SnapshotVersion and test * Fix Core CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF warnings (#779) * C++ port: add C++ equivalent of FSTDocumentKey. (#762) Also move kDocumentKeyPath to the only point of usage - make it a static member variable of FieldPath. * Strawman C++ API (#763) Incomplete, and what does exist in still slightly vague. It's expected that this will change. * Move to explicit nil check. (#782) * Update FieldValue of type Reference (#775) * update FieldValue of type Reference * address change * fix bad path string literal in test * use ReferenceValue and qualified name * Port Firestore Document to C++ (#777) * implement SnapshotVersion and test * update project * implement MaybeDocument and test * move snapshot-version from core to model * fix a bug * implement Document and test * implement NoDocument * adding type tag and fix style * fix a few bugs, discovered after merging and test run. * add assert to check FieldValue type and more test for comparision. * address changes * allow moving FieldValue to construct Document. * address changes * add document tests to project * use std::less convention * make Type::Unknown static initializer * Cleaning up implicit retain for the RTDB and Storage * Fix double parsing on 32 bit devices. (#787) * Keep track of number of queries in the query cache (#776) * Implement schema versions * Style fixes * newlines, copyrights, assumptions * Fix nullability * Raw ptr -> shared_ptr * kVersionTableGlobal -> kVersionGlobalTable * Drop utils, move into static methods * Drop extra include * Add a few more comments * Move version constant into migrations file * formatting? * Fix comment * Split add and update queryData * Work on adding targetCount * More work on count * Using shared_ptr * Implement count for query cache * use quotes * Add cast * Styling * Revert year bump in copyright * Add adversarial key to migration test * Add comment * Fix style * style fix * fix flaky test (#788) * Fix off-by-one error in Auth changelog (#789) * Serialize and deserialize null (#783) * Build (grpc's) nanopb with -DPB_FIELD_16BIT We require (at least) 16 bit fields. (By default, nanopb uses 8 bit fields, ie allowing up to 256 field tags.) Also note that this patch adds this to grpc's nanopb, rather than to our nanopb. We'll need to eventually either: a) we instruct grpc to use our nanopb b) we rely on grpc's nanopb instead of using our own. (^ marked as a TODO for now.) * Add some dependant protos Imported from protobuf. Nanopb requires these to be present (though anything using libprotobuf does not, as these are already built into that.) * Add generated nanopb protos based off of newly added proto definitions * Build the nanopb protos * Serialize and deserialize null * Actually ignore events on inactive streams, rather than just logging that we're going to. (#790) * Improve documentation on auto-init property in FCM. (#792) * Fixed capitalization of init in test function. (#797) * Update README.md with code markup (#800) Add code markup to a few paths for consistency. * replacing Auth/FSTUser by C++ auth implementation (#804) * replacing Auth/FSTUser by C++ auth implementation * address changes * DispatchQueue delayed callback improvements + testing (#784) Basically a port of https://github.com/firebase/firebase-js-sdk/commit/a1e346ff93c6cbcc0a1b3b33f0fbc3a7b66e7e12 and https://github.com/firebase/firebase-js-sdk/commit/fce4168309f42aa038125f39818fbf654b65b05f * Introduces a DelayedCallback helper class in FSTDispatchQueue to encapsulate delayed callback logic. * Adds cancellation support. * Updates the idle timer in FSTStream to use new cancellation support. * Adds a FSTTimerId enum for identifying delayed operations on the queue and uses it to identify our existing backoff and idle timers. * Added containsDelayedCallback: and runDelayedCallbacksUntil: methods to FSTDispatchQueue which can be used from tests to check for the presence of a callback or to schedule them to run early. * Removes FSTTestDispatchQueue and changes idle tests to use new test methods. * Enable -Wcomma for our build; disable it for abseil. (#799) In order to use different cflags for abseil, this patch splits it out into a subspec within the pod. The cmake side of things "just works" since Firestore/CMakeLists.txt includes abseil before setting our compiler flags. * Serialize (and deserialize) bool values (#791) * Require official 1.4.0 for min CocoaPods version (#806) * Disable -Wrange-loop-analysis for abseil (#807) absl includes code like this: ``` void fn(std::initializer_list pieces) { ... for (const absl::string_view piece : pieces) total_size += piece.size(); ``` clang objects, suggesting that a reference should be used instead, i.e.: ``` for (const absl::string_view& piece : pieces) total_size += piece.size(); ``` But: a) we don't want to touch absl code b) string_views are cheap to copy (and absl recommends copying string_views rather than taking references as it may result in smaller code) c) some brief, naive benchmarking suggests there's no significant different in this case (i.e. (b) is correct.) Note that -Wrange-loop-analysis is already exlicitly enabled in our cmake build. * Delete stale Firestore instances after FIRApp is deleted. (#809) * replacing FSTGetTokenResult by C++ Token implementation (#805) * replacing Auth/FSTUser by C++ auth implementation * address changes * replacing FSTGetTokenResult by C++ Token implementation * address changes * address changes * fix another const& v.s. dispatch bug * fix more const& v.s. dispatch bug zxu123 committed * fix * passing by value in callback * Fix two stream close issues (b/73167987, b/73382103). (#810) * Fix b/73167987: Upon receiving a permanent write error when we had additional pendingWrites to send, we were restarting the stream with a new delegate and then immediately setting the delegate to nil, causing the stream to ignore all GRPC events for the stream. * Fix b/73382103: We were attempting to gracefully teardown the write stream (i.e. send an empty WriteRequest) even when the stream was already failed due to an error. This caused no harm other than log pollution, but I fixed it. * Use -[GRPCCall setResponseDispatchQueue] to dispatch GRPC callbacks directly onto the Firestore worker queue. This saves a double-dispatch and simplifies our logic. * Add stricter assertions regarding stream state now that dispatch queue / callback filter race conditions are eliminated. * [En|De]codeUnsignedVarint -> [En|De]codeVarint (#817) * [En|De]codeUnsignedVarint -> [En|De]codeVarint The 'unsigned' portion was misleading, as these varints work with both signed and unsigned integers. (The 'signed' varints also work with both signed and unsigned integers, but use zig-zag encoding so that negative numbers are encoded more efficiently. Note that 'signed' varints aren't used in the Value proto, so won't appear in the serializer class for at least the short term.) Added some docstrings to help disambiguate this. * Make FSTTimestamp into a public Firestore class (#698) - FSTTimestamp is now FIRTimestamp, under Firestore/Source/{Public,API}. This is a temporary solution; eventually, FIRTimestamp is supposed to live somewhere under Firebase; - move most internal Timestamp methods to the public header (the only exception is ISOString). * Fix implicit retain self warnings (#808) Xcode has starting warning about us implicitly retaining self references within blocks. This commit fixes it by explicitly mentioning self. No real changes are introduced here; this is effectively just making implicit behaviour explicit. * Accept FIRTimestamp where NSDate is currently accepted as a parameter (#823) * Fix trivial mem leak in the test suite (#828) Reduces noise while running valgrind so that I can see the leaks that I'm introducing. :/ * Serialize (and deserialize) int64 values (#818) (#829) * Avoid wrapping and rewrapping NSStrings when constructing DatabaseId (#833) * Avoid wrapping and rewrapping NSStrings when constructing DatabaseId * Shorten DatabaseId::kDefaultDatabaseId * Fix Firestore tests for M22 (#834) * Add FIRFirestoreTests to the Firestore Xcode project * Avoid waitForExpectations:timeout: This API was added in Xcode 8.3, but we still build production releases with Xcode 8.2. waitForExpectationsWithTimeout:handler: is available from Xcode 7.2. * Add AppForUnitTesting Add a utility for constructing a Firebase App for testing. * Handle the nil UID from FIRAuth * Avoid running CMake tests twice * Only build app_testing on Apple platforms * Revise test.sh messages * Avoid warnings about failing to override a designated initializer (#832) * address PR comments * Refactor [En|De]codeVarint to be symetric wrt tags (#837) Since we can't decode a value before knowing it's type, I've pulled the tag handling out of these methods. More context over here: https://github.com/firebase/firebase-ios-sdk/pull/829 * Fix lint warnings (#840) * Don't build firebase_firestore_testutil_apple on non-apple platforms. (#841) Was failing the cmake build on linux. * Initial Carthage distribution instructions (#821) I couldn't find a way to get the Carthage installer to install Resources, so withdrawing Firestore and Invites. The other 11 components passed testing. * Update CHANGELOG for release (#845) * improve the documentations on auto init property * adjust the comments * Add build infrastructure for Codable support in Firestore (#815) * Add Firestore_SwiftTests_iOS target to Xcode * Add FirebaseFirestoreSwift podspec * Add Firestore_SwiftTests_iOS to the Podfile * Add CodableGeoPoint and tests * Version FirebaseFirestoreSwift separately * Move -messageWasLogged to FIRTestCase This makes it accessible to other test classes. * Add third-party library version registration This will allow us to collect the version of platform libraries that developers use in conjunction with Firebase. * Fixes clang warnings for Auth (#848) * Fixes clang warnings for Auth * Addresses comments * Revert "Move -messageWasLogged to FIRTestCase" This reverts commit dfda142503e0daffeab67e996df03324e1f372b3. * Add tests for useragent Tests a variety of simple use cases. * Auto-style swift sources (#847) * Fix bash style issues * Exclude additional build output directories * Format swift files with scripts/style.sh * Reformat swift sources * Allow swiftformat 0.32.0 on travis * PR feedback * Add FirebaseCore version reporting I've also added clearing of the library names for tests to avoid the auto found versions on load. * Deflake tests * Updating RTDB Changelog for v4.1..5 * Update CHANGELOG.md * Style fixes * Change library name * Update CHANGELOG.md * Update CHANGELOG.md * Add XCode and Apple SDK versions * Update CHANGELOG for Firestore v0.10.2 (#856) * Manually fix clang-format issues * Remove unnecessary nonnull annotations * Update changelog for release (#857) * Updates changelog in preparation for release * Adds another change. * Update Core CHANGELOG for 4.0.16 (#858) * Convert cmake build to (mostly) use urls rather than git clones (#852) Exception: grpc. Due to it's use of git submodules, it's not completely trivial to convert it (though probably wouldn't be too much more work.) This helps address our build being throttled. (Maybe. This assumes github allows more fetching of tgz's than clones.) It should also speed up our build times. The downloaded tarballs are placed into ${PROJECT_BINARY_DIR}/downloads. This allows for eventual caching in travis. * Eliminate TypedValue and serialize direct from FieldValue to bytes. (#860) This will likely only apply for proto messages that use 'oneof's. (Because we need to serialize these manually.) The problem was that as we were adding additional types, TypeValue was evolving into a parallel implementation of the FieldValue union. When serializing/deserializing oneofs we need to supply a custom value to serialize and a custom function to do the work. There's no value in translating from FieldValue to TypeValue just to store as this custom object. We might as well just store the FieldValue model directly and write the custom serializer/deserializer to use the model directly. * replacing Auth by C++ auth implementation (#802) * lazy replacing FST(Firebase)CredentialsProvider by (Firebase)CredentialsProvider * lazy replacing FSTUser by User * adding error-code parameter to TokenListener * actually use const user& instead of pointer; also add an error util * add HashUser and pass into the unordered_map * use User in test * use c++ CredentialsProvider and subclass in test * fix unit test * use explicit capture in lambda instead of capture all by reference * cache currentUser explicitly when reset sync engineer test driver * objc object should be captured by value in lambda * replacing Auth/FSTUser by C++ auth implementation * address changes * replacing FSTGetTokenResult by C++ Token implementation * address changes * fix unintentional change in merging * patch the change in objc Auth up-stream * somehow, the lambda-version of set-user-change-listener does not work... fallback to block * address changes * fix another const& v.s. dispatch bug * fix more const& v.s. dispatch bug zxu123 committed * fix a bad sync line * address changes * address change * address change * fix upstream change from merge * fix upstream changes * Suggested fixes for cpp/port_auth (#846) * Get rid of MockDatastore factory This avoids the need to statically allocate (and leak) a credentials provider * Use absl::make_unique std::make_unique technically does not exist until C++14. * #include for std::move * Use std::future for the initial user * fix style * Add FieldValue.boolean_value() (#862) * style.sh (for swift) * remove forward declarations for classes that no longer exist --- .gitignore | 3 + .travis.yml | 39 +- CMakeLists.txt | 4 +- Carthage.md | 72 + .../FIRAppAssociationRegistrationUnitTests.m | 2 +- Example/Core/Tests/FIRAppTest.m | 21 + Example/Core/Tests/FIRLoggerTest.m | 8 +- Example/Core/Tests/FIROptionsTest.m | 50 +- Example/Database/Tests/Integration/FData.m | 6 +- .../Database/Tests/Integration/FRealtime.m | 1 + Example/Database/Tests/Unit/FSyncPointTests.m | 6 +- Example/Firebase.xcodeproj/project.pbxproj | 4 + .../Messaging/App/iOS/Messaging-Info.plist | 2 + .../Messaging/Tests/FIRMessagingServiceTest.m | 11 +- Example/Messaging/Tests/FIRMessagingTest.m | 13 + Example/Messaging/Tests/Info.plist | 2 + Example/Podfile | 2 +- Example/Shared/FIRSampleAppUtilities.m | 1 - .../Integration/FIRStorageIntegrationTests.m | 2 +- .../tvOSSample/tvOSSample/AppDelegate.swift | 2 - .../tvOSSample/AuthLoginViewController.swift | 27 +- .../tvOSSample/AuthViewController.swift | 6 +- .../tvOSSample/DatabaseViewController.swift | 4 +- .../tvOSSample/EmailLoginViewController.swift | 9 +- .../tvOSSample/StorageViewController.swift | 16 +- Firebase/Auth/CHANGELOG.md | 11 +- Firebase/Auth/FirebaseAuth.podspec | 1 + .../Phone/FIRPhoneAuthProvider.m | 30 +- Firebase/Auth/Source/FIRAuth.m | 46 +- .../Auth/Source/FIRAuthAPNSTokenManager.m | 8 +- .../Auth/Source/FIRAuthNotificationManager.m | 8 +- Firebase/Auth/Source/FIRAuthSerialTaskQueue.m | 6 +- Firebase/Auth/Source/FIRAuthURLPresenter.m | 28 +- Firebase/Auth/Source/FIRSecureTokenService.m | 13 +- Firebase/Auth/Source/FIRUser.m | 103 +- .../Auth/Source/Public/FIREmailAuthProvider.h | 2 +- Firebase/Auth/Source/Public/FIRUser.h | 8 +- Firebase/Core/CHANGELOG.md | 5 + Firebase/Core/FIRApp.m | 35 + Firebase/Core/FIRMutableDictionary.m | 14 +- Firebase/Core/FIRNetworkURLSession.m | 14 +- Firebase/Core/FIROptions.m | 53 +- Firebase/Core/Private/FIRAppInternal.h | 17 + Firebase/Database/CHANGELOG.md | 4 + .../Persistence/FLevelDBStorageEngine.m | 2 +- .../third_party/SocketRocket/FSRWebSocket.m | 34 +- Firebase/Messaging/CHANGELOG.md | 88 + Firebase/Messaging/FIRMessaging.m | 61 +- .../FIRMessagingContextManagerService.m | 3 + Firebase/Messaging/FIRMessagingPubSub.h | 15 +- Firebase/Messaging/FIRMessagingPubSub.m | 10 +- .../FIRMessagingRmq2PersistentStore.m | 3 + .../Messaging/FIRMessagingTopicOperation.m | 1 + Firebase/Messaging/FIRMessaging_Private.h | 2 + Firebase/Messaging/Protos/GtalkCore.pbobjc.h | 56 +- Firebase/Messaging/Protos/GtalkCore.pbobjc.m | 122 +- Firebase/Messaging/Public/FIRMessaging.h | 20 +- Firebase/Storage/CHANGELOG.md | 3 + Firebase/Storage/FIRStorageDeleteTask.m | 2 +- Firebase/Storage/FIRStorageDownloadTask.m | 6 +- Firebase/Storage/FIRStorageErrors.m | 2 +- Firebase/Storage/FIRStorageGetMetadataTask.m | 4 +- Firebase/Storage/FIRStorageMetadata.m | 5 +- Firebase/Storage/FIRStorageObservableTask.m | 1 - .../Storage/FIRStorageUpdateMetadataTask.m | 4 +- Firebase/Storage/FIRStorageUploadTask.m | 8 +- FirebaseAuth.podspec | 4 +- FirebaseCore.podspec | 4 +- FirebaseDatabase.podspec | 4 +- FirebaseFirestore.podspec | 37 +- FirebaseFirestoreSwift.podspec | 37 + FirebaseMessaging.podspec | 4 +- FirebaseStorage.podspec | 4 +- Firestore/CHANGELOG.md | 20 +- Firestore/CMakeLists.txt | 56 +- .../Firestore.xcodeproj/project.pbxproj | 1072 ++- .../xcshareddata/xcschemes/AllTests.xcscheme | 24 + .../xcschemes/Firestore_Tests.xcscheme | 56 +- Firestore/Example/Podfile | 7 +- Firestore/Example/SwiftBuildTest/main.swift | 466 +- ...Tests.m => FIRCollectionReferenceTests.mm} | 4 +- ...ceTests.m => FIRDocumentReferenceTests.mm} | 4 +- ...hotTests.m => FIRDocumentSnapshotTests.mm} | 7 +- ...RFieldPathTests.m => FIRFieldPathTests.mm} | 3 +- ...ieldValueTests.m => FIRFieldValueTests.mm} | 16 +- .../Example/Tests/API/FIRFirestoreTests.mm | 65 + ...FIRGeoPointTests.m => FIRGeoPointTests.mm} | 2 +- ...apshotTests.m => FIRQuerySnapshotTests.mm} | 3 +- .../API/{FIRQueryTests.m => FIRQueryTests.mm} | 3 +- ...ataTests.m => FIRSnapshotMetadataTests.mm} | 3 +- .../Example/Tests/API/FIRTimestampTest.m | 102 + Firestore/Example/Tests/API/FSTAPIHelpers.h | 13 +- .../API/{FSTAPIHelpers.m => FSTAPIHelpers.mm} | 12 +- .../Example/Tests/Core/FSTDatabaseInfoTests.m | 59 - ...ManagerTests.m => FSTEventManagerTests.mm} | 0 ...stenerTests.m => FSTQueryListenerTests.mm} | 0 .../{FSTQueryTests.m => FSTQueryTests.mm} | 9 +- .../Tests/Core/FSTTargetIDGeneratorTests.m | 94 - .../Example/Tests/Core/FSTTimestampTests.m | 88 - ...wSnapshotTest.m => FSTViewSnapshotTest.mm} | 0 .../Core/{FSTViewTests.m => FSTViewTests.mm} | 0 .../{FIRCursorTests.m => FIRCursorTests.mm} | 80 +- ...FIRDatabaseTests.m => FIRDatabaseTests.mm} | 9 +- .../{FIRFieldsTests.m => FIRFieldsTests.mm} | 27 +- ...ests.m => FIRListenerRegistrationTests.mm} | 2 +- .../API/{FIRQueryTests.m => FIRQueryTests.mm} | 2 +- ...tampTests.m => FIRServerTimestampTests.mm} | 2 +- .../{FIRSourceTests.m => FIRSourceTests.mm} | 2 +- .../API/{FIRTypeTests.m => FIRTypeTests.mm} | 2 +- ...alidationTests.m => FIRValidationTests.mm} | 2 +- ...riteBatchTests.m => FIRWriteBatchTests.mm} | 2 +- ...TDatastoreTests.m => FSTDatastoreTests.mm} | 37 +- .../{FSTSmokeTests.m => FSTSmokeTests.mm} | 2 +- .../{FSTStreamTests.m => FSTStreamTests.mm} | 64 +- ...nsactionTests.m => FSTTransactionTests.mm} | 4 +- ...sts.m => FSTEagerGarbageCollectorTests.mm} | 0 ...reTests.m => FSTLevelDBLocalStoreTests.mm} | 1 - .../Tests/Local/FSTLevelDBMigrationsTests.mm | 104 + .../Local/FSTLevelDBMutationQueueTests.mm | 10 +- ...heTests.m => FSTLevelDBQueryCacheTests.mm} | 0 .../FSTLevelDBRemoteDocumentCacheTests.mm | 5 +- ...izerTests.m => FSTLocalSerializerTests.mm} | 22 +- ...ocalStoreTests.m => FSTLocalStoreTests.mm} | 15 +- ...oreTests.m => FSTMemoryLocalStoreTests.mm} | 0 ...Tests.m => FSTMemoryMutationQueueTests.mm} | 8 +- ...cheTests.m => FSTMemoryQueryCacheTests.mm} | 0 ...m => FSTMemoryRemoteDocumentCacheTests.mm} | 0 ...nQueueTests.m => FSTMutationQueueTests.mm} | 16 +- .../Tests/Local/FSTPersistenceTestHelpers.h | 6 + ...Helpers.m => FSTPersistenceTestHelpers.mm} | 20 +- ...ueryCacheTests.m => FSTQueryCacheTests.mm} | 83 +- ...enceSetTests.m => FSTReferenceSetTests.mm} | 0 ...Tests.m => FSTRemoteDocumentCacheTests.mm} | 0 ... => FSTRemoteDocumentChangeBufferTests.mm} | 0 .../Example/Tests/Model/FSTDatabaseIDTests.m | 45 - ...umentKeyTests.m => FSTDocumentKeyTests.mm} | 0 ...umentSetTests.m => FSTDocumentSetTests.mm} | 8 - ...FSTDocumentTests.m => FSTDocumentTests.mm} | 0 ...ieldValueTests.m => FSTFieldValueTests.mm} | 49 +- ...FSTMutationTests.m => FSTMutationTests.mm} | 6 +- .../Model/{FSTPathTests.m => FSTPathTests.mm} | 4 + ...TDatastoreTests.m => FSTDatastoreTests.mm} | 2 +- ...oteEventTests.m => FSTRemoteEventTests.mm} | 0 ...rBetaTests.m => FSTSerializerBetaTests.mm} | 49 +- ...ge+Testing.m => FSTWatchChange+Testing.mm} | 0 ...chChangeTests.m => FSTWatchChangeTests.mm} | 0 ...elDBSpecTests.m => FSTLevelDBSpecTests.mm} | 0 ...emorySpecTests.m => FSTMemorySpecTests.mm} | 0 .../Tests/SpecTests/FSTMockDatastore.h | 2 - ...FSTMockDatastore.m => FSTMockDatastore.mm} | 62 +- .../{FSTSpecTests.m => FSTSpecTests.mm} | 22 +- .../Tests/SpecTests/FSTSyncEngineTestDriver.h | 22 +- ...estDriver.m => FSTSyncEngineTestDriver.mm} | 65 +- .../SpecTests/json/offline_spec_test.json | 254 + .../{FSTAssertTests.m => FSTAssertTests.mm} | 0 .../Example/Tests/Util/FSTComparisonTests.m | 143 - .../Tests/Util/FSTDispatchQueueTests.mm | 111 + ...ntAccumulator.m => FSTEventAccumulator.mm} | 0 Firestore/Example/Tests/Util/FSTHelpers.h | 12 +- .../Util/{FSTHelpers.m => FSTHelpers.mm} | 40 +- .../Tests/Util/FSTIntegrationTestCase.h | 7 +- .../Tests/Util/FSTIntegrationTestCase.mm | 39 +- .../Example/Tests/Util/FSTTestDispatchQueue.m | 61 - .../Example/Tests/Util/XCTestCase+Await.h | 3 +- ...XCTestCase+Await.m => XCTestCase+Await.mm} | 4 +- Firestore/Port/string_util.h | 66 - Firestore/Protos/CMakeLists.txt | 45 + .../FrameworkMaker.xcodeproj/project.pbxproj | 2 +- Firestore/Protos/README.md | 10 + Firestore/Protos/build-protos.sh | 16 +- .../firestore/local/maybe_document.pb.c | 66 + .../firestore/local/maybe_document.pb.h | 88 + .../nanopb/firestore/local/mutation.pb.c | 67 + .../nanopb/firestore/local/mutation.pb.h | 87 + .../Protos/nanopb/firestore/local/target.pb.c | 71 + .../Protos/nanopb/firestore/local/target.pb.h | 98 + .../Protos/nanopb/google/api/annotations.pb.c | 31 + .../Protos/nanopb/google/api/annotations.pb.h | 44 + Firestore/Protos/nanopb/google/api/http.pb.c | 78 + Firestore/Protos/nanopb/google/api/http.pb.h | 105 + .../google/firestore/v1beta1/common.pb.c | 81 + .../google/firestore/v1beta1/common.pb.h | 124 + .../google/firestore/v1beta1/document.pb.c | 105 + .../google/firestore/v1beta1/document.pb.h | 155 + .../google/firestore/v1beta1/firestore.pb.c | 259 + .../google/firestore/v1beta1/firestore.pb.h | 508 ++ .../google/firestore/v1beta1/query.pb.c | 124 + .../google/firestore/v1beta1/query.pb.h | 240 + .../google/firestore/v1beta1/write.pb.c | 110 + .../google/firestore/v1beta1/write.pb.h | 187 + .../Protos/nanopb/google/protobuf/any.pb.c | 36 + .../Protos/nanopb/google/protobuf/any.pb.h | 69 + .../Protos/nanopb/google/protobuf/empty.pb.c | 34 + .../Protos/nanopb/google/protobuf/empty.pb.h | 66 + .../Protos/nanopb/google/protobuf/struct.pb.c | 87 + .../Protos/nanopb/google/protobuf/struct.pb.h | 117 + .../nanopb/google/protobuf/timestamp.pb.c | 36 + .../nanopb/google/protobuf/timestamp.pb.h | 69 + .../nanopb/google/protobuf/wrappers.pb.c | 81 + .../nanopb/google/protobuf/wrappers.pb.h | 147 + .../Protos/nanopb/google/rpc/status.pb.c | 37 + .../Protos/nanopb/google/rpc/status.pb.h | 73 + .../Protos/nanopb/google/type/latlng.pb.c | 42 + .../Protos/nanopb/google/type/latlng.pb.h | 69 + .../objc/firestore/local/Target.pbobjc.h | 4 + .../objc/firestore/local/Target.pbobjc.m | 11 + .../protos/firestore/local/target.proto | 3 + .../Protos/protos/google/api/http.options | 1 + .../google/firestore/v1beta1/document.options | 1 + .../firestore/v1beta1/firestore.options | 6 + .../google/firestore/v1beta1/write.options | 1 + .../Protos/protos/google/protobuf/any.proto | 149 + .../Protos/protos/google/protobuf/empty.proto | 52 + .../protos/google/protobuf/struct.options | 1 + .../protos/google/protobuf/struct.proto | 96 + .../protos/google/protobuf/timestamp.proto | 133 + .../protos/google/protobuf/wrappers.proto | 118 + .../Source/API/FIRCollectionReference.mm | 5 +- .../Source/API/FIRDocumentChange+Internal.h | 1 + ...RDocumentChange.m => FIRDocumentChange.mm} | 0 ...entReference.m => FIRDocumentReference.mm} | 5 +- ...umentSnapshot.m => FIRDocumentSnapshot.mm} | 42 +- .../API/{FIRFieldPath.m => FIRFieldPath.mm} | 0 .../API/{FIRFieldValue.m => FIRFieldValue.mm} | 0 Firestore/Source/API/FIRFirestore+Internal.h | 18 +- .../API/{FIRFirestore.m => FIRFirestore.mm} | 106 +- ...toreSettings.m => FIRFirestoreSettings.mm} | 0 ...estoreVersion.m => FIRFirestoreVersion.mm} | 2 +- .../API/{FIRGeoPoint.m => FIRGeoPoint.mm} | 17 +- ...istration.m => FIRListenerRegistration.mm} | 0 .../Source/API/{FIRQuery.m => FIRQuery.mm} | 5 +- ...FIRQuerySnapshot.m => FIRQuerySnapshot.mm} | 12 +- .../API/{FIRSetOptions.m => FIRSetOptions.mm} | 3 +- ...pshotMetadata.m => FIRSnapshotMetadata.mm} | 5 +- ...napshotOptions.m => FIRSnapshotOptions.mm} | 0 Firestore/Source/API/FIRTimestamp+Internal.h | 35 + .../FSTTimestamp.m => API/FIRTimestamp.m} | 112 +- .../{FIRTransaction.m => FIRTransaction.mm} | 0 .../API/{FIRWriteBatch.m => FIRWriteBatch.mm} | 0 Firestore/Source/API/FSTUserDataConverter.h | 11 +- ...ataConverter.m => FSTUserDataConverter.mm} | 36 +- .../Source/Auth/FSTCredentialsProvider.h | 113 - .../Source/Auth/FSTCredentialsProvider.m | 164 - .../Source/Auth/FSTEmptyCredentialsProvider.m | 47 - Firestore/Source/Auth/FSTUser.h | 43 - Firestore/Source/Auth/FSTUser.m | 68 - Firestore/Source/Core/FSTDatabaseInfo.h | 55 - Firestore/Source/Core/FSTDatabaseInfo.m | 70 - .../{FSTEventManager.m => FSTEventManager.mm} | 0 Firestore/Source/Core/FSTFirestoreClient.h | 15 +- ...irestoreClient.m => FSTFirestoreClient.mm} | 107 +- Firestore/Source/Core/FSTListenSequence.h | 37 + Firestore/Source/Core/FSTListenSequence.mm | 50 + .../Source/Core/{FSTQuery.m => FSTQuery.mm} | 22 +- Firestore/Source/Core/FSTSnapshotVersion.h | 6 +- ...napshotVersion.m => FSTSnapshotVersion.mm} | 8 +- Firestore/Source/Core/FSTSyncEngine.h | 8 +- .../{FSTSyncEngine.m => FSTSyncEngine.mm} | 52 +- Firestore/Source/Core/FSTTargetIDGenerator.h | 55 - Firestore/Source/Core/FSTTargetIDGenerator.m | 105 - .../{FSTTransaction.m => FSTTransaction.mm} | 32 +- Firestore/Source/Core/FSTTypes.h | 2 + .../Source/Core/{FSTView.m => FSTView.mm} | 26 +- .../{FSTViewSnapshot.m => FSTViewSnapshot.mm} | 0 Firestore/Source/Local/FSTDocumentReference.h | 4 +- ...entReference.m => FSTDocumentReference.mm} | 11 +- ...ollector.m => FSTEagerGarbageCollector.mm} | 0 Firestore/Source/Local/FSTLevelDB.h | 22 +- Firestore/Source/Local/FSTLevelDB.mm | 42 +- Firestore/Source/Local/FSTLevelDBKey.h | 12 +- Firestore/Source/Local/FSTLevelDBKey.mm | 51 +- .../Local/FSTLevelDBMigrations.h} | 24 +- .../Source/Local/FSTLevelDBMigrations.mm | 131 + .../Source/Local/FSTLevelDBMutationQueue.h | 16 +- .../Source/Local/FSTLevelDBMutationQueue.mm | 18 +- Firestore/Source/Local/FSTLevelDBQueryCache.h | 17 +- .../Source/Local/FSTLevelDBQueryCache.mm | 135 +- .../Local/FSTLevelDBRemoteDocumentCache.h | 11 +- .../Local/FSTLevelDBRemoteDocumentCache.mm | 4 - ...cumentsView.m => FSTLocalDocumentsView.mm} | 0 ...ocalSerializer.m => FSTLocalSerializer.mm} | 7 +- Firestore/Source/Local/FSTLocalStore.h | 8 +- .../{FSTLocalStore.m => FSTLocalStore.mm} | 42 +- ...alViewChanges.m => FSTLocalViewChanges.mm} | 0 ...alWriteResult.m => FSTLocalWriteResult.mm} | 0 ...ationQueue.m => FSTMemoryMutationQueue.mm} | 9 +- ...yPersistence.m => FSTMemoryPersistence.mm} | 19 +- ...oryQueryCache.m => FSTMemoryQueryCache.mm} | 23 + ...ache.m => FSTMemoryRemoteDocumentCache.mm} | 0 Firestore/Source/Local/FSTMutationQueue.h | 4 +- ...Collector.m => FSTNoOpGarbageCollector.mm} | 0 Firestore/Source/Local/FSTPersistence.h | 5 +- Firestore/Source/Local/FSTQueryCache.h | 24 +- Firestore/Source/Local/FSTQueryData.h | 4 + .../Local/{FSTQueryData.m => FSTQueryData.mm} | 5 + .../{FSTReferenceSet.m => FSTReferenceSet.mm} | 0 ...fer.m => FSTRemoteDocumentChangeBuffer.mm} | 0 Firestore/Source/Local/FSTWriteGroup.h | 13 +- Firestore/Source/Local/FSTWriteGroup.mm | 3 - ...GroupTracker.m => FSTWriteGroupTracker.mm} | 0 Firestore/Source/Local/StringView.h | 16 +- Firestore/Source/Model/FSTDatabaseID.h | 48 - Firestore/Source/Model/FSTDatabaseID.m | 90 - .../Model/{FSTDocument.m => FSTDocument.mm} | 0 ...tDictionary.m => FSTDocumentDictionary.mm} | 0 .../{FSTDocumentKey.m => FSTDocumentKey.mm} | 0 ...TDocumentKeySet.m => FSTDocumentKeySet.mm} | 0 Firestore/Source/Model/FSTDocumentSet.h | 10 - .../{FSTDocumentSet.m => FSTDocumentSet.mm} | 10 - ...nary.m => FSTDocumentVersionDictionary.mm} | 0 Firestore/Source/Model/FSTFieldValue.h | 19 +- .../{FSTFieldValue.m => FSTFieldValue.mm} | 110 +- Firestore/Source/Model/FSTMutation.h | 6 +- .../Model/{FSTMutation.m => FSTMutation.mm} | 17 +- Firestore/Source/Model/FSTMutationBatch.h | 6 +- ...FSTMutationBatch.m => FSTMutationBatch.mm} | 5 +- .../Source/Model/{FSTPath.m => FSTPath.mm} | 0 Firestore/Source/Public/FIRFirestore.h | 5 +- .../FSTTimestamp.h => Public/FIRTimestamp.h} | 48 +- Firestore/Source/Public/FirebaseFirestore.h | 1 + ...TBufferedWriter.m => FSTBufferedWriter.mm} | 0 Firestore/Source/Remote/FSTDatastore.h | 26 +- .../{FSTDatastore.m => FSTDatastore.mm} | 92 +- ...xistenceFilter.m => FSTExistenceFilter.mm} | 0 .../Source/Remote/FSTExponentialBackoff.h | 20 +- .../Source/Remote/FSTExponentialBackoff.mm | 24 +- .../{FSTRemoteEvent.m => FSTRemoteEvent.mm} | 12 + Firestore/Source/Remote/FSTRemoteStore.h | 6 +- .../{FSTRemoteStore.m => FSTRemoteStore.mm} | 24 +- Firestore/Source/Remote/FSTSerializerBeta.h | 12 +- ...TSerializerBeta.m => FSTSerializerBeta.mm} | 87 +- Firestore/Source/Remote/FSTStream.h | 47 +- .../Remote/{FSTStream.m => FSTStream.mm} | 209 +- .../{FSTWatchChange.m => FSTWatchChange.mm} | 0 ...eryListener.m => FSTAsyncQueryListener.mm} | 11 +- Firestore/Source/Util/FSTComparison.h | 66 - Firestore/Source/Util/FSTComparison.m | 175 - Firestore/Source/Util/FSTDispatchQueue.h | 52 +- Firestore/Source/Util/FSTDispatchQueue.m | 80 - Firestore/Source/Util/FSTDispatchQueue.mm | 275 + Firestore/Source/Util/FSTLogger.h | 8 - .../Source/Util/{FSTLogger.m => FSTLogger.mm} | 0 Firestore/Source/Util/FSTUsageValidation.h | 8 - ...sageValidation.m => FSTUsageValidation.mm} | 0 .../Source/Codable/CodableGeoPoint.swift | 62 + .../Tests/Codable/CodableGeoPointTests.swift | 49 + Firestore/Swift/Tests/Info.plist | 22 + Firestore/core/CMakeLists.txt | 14 + .../firebase/firestore/document_reference.h | 375 + .../firebase/firestore/event_listener.h | 53 + .../include/firebase/firestore/firestore.h | 160 + .../firebase/firestore/firestore_errors.h | 115 + .../include/firebase/firestore/geo_point.h | 89 + .../src/firebase/firestore/CMakeLists.txt | 22 + .../firebase/firestore/auth/CMakeLists.txt | 52 + .../firestore/auth/credentials_provider.cc | 31 + .../firestore/auth/credentials_provider.h | 82 + .../auth/empty_credentials_provider.cc | 43 + .../auth/empty_credentials_provider.h | 37 + .../firebase_credentials_provider_apple.h | 113 + .../firebase_credentials_provider_apple.mm | 134 + .../core/src/firebase/firestore/auth/token.cc | 37 + .../core/src/firebase/firestore/auth/token.h | 87 + .../core/src/firebase/firestore/auth/user.cc | 39 + .../core/src/firebase/firestore/auth/user.h | 103 + .../core/src/firebase/firestore/base/port.h | 33 - .../firebase/firestore/core/CMakeLists.txt | 25 + .../firebase/firestore/core/database_info.cc | 36 + .../firebase/firestore/core/database_info.h | 79 + .../firestore/core/target_id_generator.cc | 62 + .../firestore/core/target_id_generator.h | 83 + .../core/src/firebase/firestore/core/types.h | 32 + .../core/src/firebase/firestore/geo_point.cc | 50 + .../firestore/immutable/CMakeLists.txt | 21 + .../firestore/immutable/array_sorted_map.cc | 30 + .../firestore/immutable/array_sorted_map.h | 318 + .../firebase/firestore/immutable/map_entry.h | 62 + .../firebase/firestore/model/CMakeLists.txt | 43 + .../src/firebase/firestore/model/base_path.h | 181 + .../firebase/firestore/model/database_id.cc | 36 + .../firebase/firestore/model/database_id.h | 111 + .../src/firebase/firestore/model/document.cc | 50 + .../src/firebase/firestore/model/document.h | 72 + .../firebase/firestore/model/document_key.cc | 54 + .../firebase/firestore/model/document_key.h | 101 + .../firebase/firestore/model/field_path.cc | 173 + .../src/firebase/firestore/model/field_path.h | 100 + .../firebase/firestore/model/field_value.cc | 429 ++ .../firebase/firestore/model/field_value.h | 193 + .../firestore/model/maybe_document.cc | 38 + .../firebase/firestore/model/maybe_document.h | 100 + .../firebase/firestore/model/no_document.cc | 32 + .../firebase/firestore/model/no_document.h | 36 + .../firebase/firestore/model/resource_path.cc | 58 + .../firebase/firestore/model/resource_path.h | 85 + .../firestore/model/snapshot_version.cc | 34 + .../firestore/model/snapshot_version.h | 74 + .../src/firebase/firestore/model/timestamp.cc | 54 + .../src/firebase/firestore/model/timestamp.h | 94 + .../firebase/firestore/remote/CMakeLists.txt | 27 + .../firebase/firestore/remote/datastore.cc} | 21 +- .../src/firebase/firestore/remote/datastore.h | 40 + .../firebase/firestore/remote/serializer.cc | 183 + .../firebase/firestore/remote/serializer.h | 107 + .../firebase/firestore/util/CMakeLists.txt | 143 +- .../firebase/firestore/util/assert_stdio.cc | 8 +- .../src/firebase/firestore/util}/bits.cc | 14 +- .../src/firebase/firestore/util}/bits.h | 26 +- .../src/firebase/firestore/util/comparison.cc | 112 + .../src/firebase/firestore/util/comparison.h | 181 + .../src/firebase/firestore/util/config.h.in | 35 + .../src/firebase/firestore/util/error_apple.h | 52 + .../firebase/firestore/util/firebase_assert.h | 19 +- .../firestore/util/iterator_adaptors.h | 812 +++ .../firebase/firestore/util}/ordered_code.cc | 239 +- .../firebase/firestore/util}/ordered_code.h | 75 +- .../firebase/firestore/util/secure_random.h | 16 + .../util/secure_random_arc4random.cc | 2 +- .../firestore/util/secure_random_openssl.cc | 46 + .../firebase/firestore/util/string_apple.h | 27 + .../firebase/firestore/util/string_printf.cc | 17 +- .../firebase/firestore/util/string_printf.h | 4 +- .../firebase/firestore/util}/string_util.cc | 18 +- .../src/firebase/firestore/util/string_util.h | 72 + .../test/firebase/firestore/CMakeLists.txt | 21 + .../firebase/firestore/auth/CMakeLists.txt | 35 + .../auth/credentials_provider_test.cc | 52 + .../auth/empty_credentials_provider_test.cc | 51 + .../firebase_credentials_provider_test.mm | 86 + .../firebase/firestore/auth/token_test.cc | 41 + .../test/firebase/firestore/auth/user_test.cc | 59 + .../firebase/firestore/core/CMakeLists.txt | 22 + .../firestore/core/database_info_test.cc | 48 + .../core/target_id_generator_test.cc | 80 + .../test/firebase/firestore/geo_point_test.cc | 41 + .../firestore/immutable/CMakeLists.txt | 22 + .../immutable/array_sorted_map_test.cc | 326 + .../firebase/firestore/model/CMakeLists.txt | 30 + .../firestore/model/database_id_test.cc | 47 + .../firestore/model/document_key_test.cc | 153 + .../firebase/firestore/model/document_test.cc | 76 + .../firestore/model/field_path_test.cc | 277 + .../firestore/model/field_value_test.cc | 507 ++ .../firestore/model/maybe_document_test.cc | 66 + .../firestore/model/no_document_test.cc | 51 + .../firestore/model/resource_path_test.cc | 105 + .../firestore/model/snapshot_version_test.cc | 56 + .../firestore/model/timestamp_test.cc | 49 + .../firebase/firestore/remote/CMakeLists.txt | 22 + .../firestore/remote/datastore_test.cc | 29 + .../firestore/remote/serializer_test.cc | 157 + .../firestore/testutil/CMakeLists.txt | 36 + .../firebase/firestore/testutil/app_testing.h | 43 + .../firestore/testutil/app_testing.mm | 48 + .../firebase/firestore/util/CMakeLists.txt | 67 +- .../firebase/firestore/util/assert_test.cc | 3 +- .../firebase/firestore/util/autoid_test.cc | 4 +- .../firebase/firestore/util}/bits_test.cc | 56 +- .../firestore/util/comparison_test.cc | 211 + .../firestore/util/iterator_adaptors_test.cc | 1277 ++++ .../test/firebase/firestore/util/log_test.cc | 2 +- .../firestore/util}/ordered_code_test.cc | 264 +- .../firestore/util/secure_random_test.cc | 25 + .../firestore/util/string_printf_test.cc | 2 +- .../firestore/util}/string_util_test.cc | 21 +- Firestore/test.sh | 19 +- .../Immutable/FSTArraySortedDictionary.m | 13 - .../Immutable/FSTImmutableSortedDictionary.h | 8 - .../Immutable/FSTImmutableSortedDictionary.m | 4 - .../Immutable/FSTImmutableSortedSet.h | 2 - .../Immutable/FSTImmutableSortedSet.m | 4 - .../Immutable/FSTTreeSortedDictionary.m | 30 - .../Tests/FSTArraySortedDictionaryTests.m | 22 - .../Tests/FSTTreeSortedDictionaryTests.m | 19 - .../third_party/abseil-cpp/CMakeLists.txt | 44 +- .../abseil-cpp/absl/CMakeLists.txt | 2 + .../abseil-cpp/absl/base/CMakeLists.txt | 2 +- .../abseil-cpp/absl/base/attributes.h | 15 + .../third_party/abseil-cpp/absl/base/config.h | 41 +- .../abseil-cpp/absl/base/internal/endian.h | 2 + .../absl/base/internal/raw_logging.cc | 7 +- .../absl/base/internal/raw_logging.h | 2 +- .../absl/base/internal/unaligned_access.h | 2 +- .../absl/base/{internal => }/log_severity.h | 11 +- .../third_party/abseil-cpp/absl/base/macros.h | 4 +- .../abseil-cpp/absl/memory/CMakeLists.txt | 52 + .../abseil-cpp/absl/memory/memory.h | 640 ++ .../abseil-cpp/absl/memory/memory_test.cc | 614 ++ .../abseil-cpp/absl/meta/type_traits.h | 66 +- .../abseil-cpp/absl/meta/type_traits_test.cc | 369 +- .../abseil-cpp/absl/numeric/CMakeLists.txt | 62 + .../abseil-cpp/absl/numeric/int128.cc | 206 + .../abseil-cpp/absl/numeric/int128.h | 632 ++ .../absl/numeric/int128_have_intrinsic.inc | 3 + .../absl/numeric/int128_no_intrinsic.inc | 3 + .../absl/numeric/int128_stream_test.cc | 666 ++ .../abseil-cpp/absl/numeric/int128_test.cc | 428 ++ .../abseil-cpp/absl/strings/CMakeLists.txt | 180 + .../abseil-cpp/absl/strings/escaping.cc | 1109 +++ .../abseil-cpp/absl/strings/escaping.h | 161 + .../abseil-cpp/absl/strings/escaping_test.cc | 640 ++ .../absl/strings/internal/char_map.h | 154 + .../absl/strings/internal/char_map_test.cc | 172 + .../strings/internal/escaping_test_common.inc | 113 + .../strings/internal/numbers_test_common.inc | 156 + .../absl/strings/internal/ostringstream.cc | 34 + .../absl/strings/internal/ostringstream.h | 87 + .../strings/internal/ostringstream_test.cc | 102 + .../absl/strings/internal/stl_type_traits.h | 213 + .../absl/strings/internal/str_join_internal.h | 309 + .../strings/internal/str_split_internal.h | 435 ++ .../abseil-cpp/absl/strings/internal/utf8.cc | 51 + .../abseil-cpp/absl/strings/internal/utf8.h | 51 + .../absl/strings/internal/utf8_test.cc | 57 + .../abseil-cpp/absl/strings/numbers.cc | 919 +++ .../abseil-cpp/absl/strings/numbers.h | 172 + .../abseil-cpp/absl/strings/numbers_test.cc | 1186 ++++ .../abseil-cpp/absl/strings/str_cat.cc | 208 + .../abseil-cpp/absl/strings/str_cat.h | 347 + .../abseil-cpp/absl/strings/str_cat_test.cc | 468 ++ .../abseil-cpp/absl/strings/str_join.h | 288 + .../abseil-cpp/absl/strings/str_join_test.cc | 473 ++ .../abseil-cpp/absl/strings/str_replace.cc | 79 + .../abseil-cpp/absl/strings/str_replace.h | 213 + .../absl/strings/str_replace_test.cc | 341 + .../abseil-cpp/absl/strings/str_split.cc | 136 + .../abseil-cpp/absl/strings/str_split.h | 511 ++ .../abseil-cpp/absl/strings/str_split_test.cc | 902 +++ .../abseil-cpp/absl/strings/string_view.h | 2 +- .../absl/strings/string_view_test.cc | 32 +- .../abseil-cpp/absl/strings/strip.h | 89 + .../abseil-cpp/absl/strings/strip_test.cc | 181 + .../abseil-cpp/absl/strings/substitute.cc | 117 + .../abseil-cpp/absl/strings/substitute.h | 664 ++ .../absl/strings/substitute_test.cc | 168 + Gemfile.lock | 20 +- README.md | 16 +- cmake/CompilerSetup.cmake | 99 + cmake/ExternalProjectFlags.cmake | 71 + cmake/FindGRPC.cmake | 142 + cmake/FindLevelDB.cmake | 2 +- cmake/FindNanopb.cmake | 34 + cmake/external/firestore.cmake | 28 +- cmake/external/googletest.cmake | 20 +- cmake/external/grpc.cmake | 90 + cmake/external/leveldb.cmake | 21 +- cmake/external/nanopb.cmake | 64 + cmake/external/protobuf.cmake | 31 + cmake/utils.cmake | 80 +- cmake/xcodebuild.cmake | 11 +- patch/FirebaseAnalytics.h | 16 + scripts/cpplint.py | 6228 +++++++++++++++++ scripts/lint.sh | 18 + scripts/style.sh | 93 +- test.sh | 3 - 555 files changed, 42653 insertions(+), 4595 deletions(-) create mode 100644 Carthage.md create mode 100644 Firebase/Messaging/CHANGELOG.md create mode 100644 FirebaseFirestoreSwift.podspec rename Firestore/Example/Tests/API/{FIRCollectionReferenceTests.m => FIRCollectionReferenceTests.mm} (96%) rename Firestore/Example/Tests/API/{FIRDocumentReferenceTests.m => FIRDocumentReferenceTests.mm} (96%) rename Firestore/Example/Tests/API/{FIRDocumentSnapshotTests.m => FIRDocumentSnapshotTests.mm} (93%) rename Firestore/Example/Tests/API/{FIRFieldPathTests.m => FIRFieldPathTests.mm} (96%) rename Firestore/Example/Tests/API/{FIRFieldValueTests.m => FIRFieldValueTests.mm} (75%) create mode 100644 Firestore/Example/Tests/API/FIRFirestoreTests.mm rename Firestore/Example/Tests/API/{FIRGeoPointTests.m => FIRGeoPointTests.mm} (98%) rename Firestore/Example/Tests/API/{FIRQuerySnapshotTests.m => FIRQuerySnapshotTests.mm} (97%) rename Firestore/Example/Tests/API/{FIRQueryTests.m => FIRQueryTests.mm} (98%) rename Firestore/Example/Tests/API/{FIRSnapshotMetadataTests.m => FIRSnapshotMetadataTests.mm} (96%) create mode 100644 Firestore/Example/Tests/API/FIRTimestampTest.m rename Firestore/Example/Tests/API/{FSTAPIHelpers.m => FSTAPIHelpers.mm} (94%) delete mode 100644 Firestore/Example/Tests/Core/FSTDatabaseInfoTests.m rename Firestore/Example/Tests/Core/{FSTEventManagerTests.m => FSTEventManagerTests.mm} (100%) rename Firestore/Example/Tests/Core/{FSTQueryListenerTests.m => FSTQueryListenerTests.mm} (100%) rename Firestore/Example/Tests/Core/{FSTQueryTests.m => FSTQueryTests.mm} (98%) delete mode 100644 Firestore/Example/Tests/Core/FSTTargetIDGeneratorTests.m delete mode 100644 Firestore/Example/Tests/Core/FSTTimestampTests.m rename Firestore/Example/Tests/Core/{FSTViewSnapshotTest.m => FSTViewSnapshotTest.mm} (100%) rename Firestore/Example/Tests/Core/{FSTViewTests.m => FSTViewTests.mm} (100%) rename Firestore/Example/Tests/Integration/API/{FIRCursorTests.m => FIRCursorTests.mm} (68%) rename Firestore/Example/Tests/Integration/API/{FIRDatabaseTests.m => FIRDatabaseTests.mm} (98%) rename Firestore/Example/Tests/Integration/API/{FIRFieldsTests.m => FIRFieldsTests.mm} (87%) rename Firestore/Example/Tests/Integration/API/{FIRListenerRegistrationTests.m => FIRListenerRegistrationTests.mm} (98%) rename Firestore/Example/Tests/Integration/API/{FIRQueryTests.m => FIRQueryTests.mm} (99%) rename Firestore/Example/Tests/Integration/API/{FIRServerTimestampTests.m => FIRServerTimestampTests.mm} (99%) rename Firestore/Example/Tests/Integration/API/{FIRSourceTests.m => FIRSourceTests.mm} (99%) rename Firestore/Example/Tests/Integration/API/{FIRTypeTests.m => FIRTypeTests.mm} (97%) rename Firestore/Example/Tests/Integration/API/{FIRValidationTests.m => FIRValidationTests.mm} (99%) rename Firestore/Example/Tests/Integration/API/{FIRWriteBatchTests.m => FIRWriteBatchTests.mm} (99%) rename Firestore/Example/Tests/Integration/{FSTDatastoreTests.m => FSTDatastoreTests.mm} (88%) rename Firestore/Example/Tests/Integration/{FSTSmokeTests.m => FSTSmokeTests.mm} (99%) rename Firestore/Example/Tests/Integration/{FSTStreamTests.m => FSTStreamTests.mm} (82%) rename Firestore/Example/Tests/Integration/{FSTTransactionTests.m => FSTTransactionTests.mm} (99%) rename Firestore/Example/Tests/Local/{FSTEagerGarbageCollectorTests.m => FSTEagerGarbageCollectorTests.mm} (100%) rename Firestore/Example/Tests/Local/{FSTLevelDBLocalStoreTests.m => FSTLevelDBLocalStoreTests.mm} (96%) create mode 100644 Firestore/Example/Tests/Local/FSTLevelDBMigrationsTests.mm rename Firestore/Example/Tests/Local/{FSTLevelDBQueryCacheTests.m => FSTLevelDBQueryCacheTests.mm} (100%) rename Firestore/Example/Tests/Local/{FSTLocalSerializerTests.m => FSTLocalSerializerTests.mm} (92%) rename Firestore/Example/Tests/Local/{FSTLocalStoreTests.m => FSTLocalStoreTests.mm} (98%) rename Firestore/Example/Tests/Local/{FSTMemoryLocalStoreTests.m => FSTMemoryLocalStoreTests.mm} (100%) rename Firestore/Example/Tests/Local/{FSTMemoryMutationQueueTests.m => FSTMemoryMutationQueueTests.mm} (87%) rename Firestore/Example/Tests/Local/{FSTMemoryQueryCacheTests.m => FSTMemoryQueryCacheTests.mm} (100%) rename Firestore/Example/Tests/Local/{FSTMemoryRemoteDocumentCacheTests.m => FSTMemoryRemoteDocumentCacheTests.mm} (100%) rename Firestore/Example/Tests/Local/{FSTMutationQueueTests.m => FSTMutationQueueTests.mm} (97%) rename Firestore/Example/Tests/Local/{FSTPersistenceTestHelpers.m => FSTPersistenceTestHelpers.mm} (83%) rename Firestore/Example/Tests/Local/{FSTQueryCacheTests.m => FSTQueryCacheTests.mm} (80%) rename Firestore/Example/Tests/Local/{FSTReferenceSetTests.m => FSTReferenceSetTests.mm} (100%) rename Firestore/Example/Tests/Local/{FSTRemoteDocumentCacheTests.m => FSTRemoteDocumentCacheTests.mm} (100%) rename Firestore/Example/Tests/Local/{FSTRemoteDocumentChangeBufferTests.m => FSTRemoteDocumentChangeBufferTests.mm} (100%) delete mode 100644 Firestore/Example/Tests/Model/FSTDatabaseIDTests.m rename Firestore/Example/Tests/Model/{FSTDocumentKeyTests.m => FSTDocumentKeyTests.mm} (100%) rename Firestore/Example/Tests/Model/{FSTDocumentSetTests.m => FSTDocumentSetTests.mm} (93%) rename Firestore/Example/Tests/Model/{FSTDocumentTests.m => FSTDocumentTests.mm} (100%) rename Firestore/Example/Tests/Model/{FSTFieldValueTests.m => FSTFieldValueTests.mm} (93%) rename Firestore/Example/Tests/Model/{FSTMutationTests.m => FSTMutationTests.mm} (98%) rename Firestore/Example/Tests/Model/{FSTPathTests.m => FSTPathTests.mm} (97%) rename Firestore/Example/Tests/Remote/{FSTDatastoreTests.m => FSTDatastoreTests.mm} (97%) rename Firestore/Example/Tests/Remote/{FSTRemoteEventTests.m => FSTRemoteEventTests.mm} (100%) rename Firestore/Example/Tests/Remote/{FSTSerializerBetaTests.m => FSTSerializerBetaTests.mm} (95%) rename Firestore/Example/Tests/Remote/{FSTWatchChange+Testing.m => FSTWatchChange+Testing.mm} (100%) rename Firestore/Example/Tests/Remote/{FSTWatchChangeTests.m => FSTWatchChangeTests.mm} (100%) rename Firestore/Example/Tests/SpecTests/{FSTLevelDBSpecTests.m => FSTLevelDBSpecTests.mm} (100%) rename Firestore/Example/Tests/SpecTests/{FSTMemorySpecTests.m => FSTMemorySpecTests.mm} (100%) rename Firestore/Example/Tests/SpecTests/{FSTMockDatastore.m => FSTMockDatastore.mm} (83%) rename Firestore/Example/Tests/SpecTests/{FSTSpecTests.m => FSTSpecTests.mm} (98%) rename Firestore/Example/Tests/SpecTests/{FSTSyncEngineTestDriver.m => FSTSyncEngineTestDriver.mm} (85%) rename Firestore/Example/Tests/Util/{FSTAssertTests.m => FSTAssertTests.mm} (100%) delete mode 100644 Firestore/Example/Tests/Util/FSTComparisonTests.m create mode 100644 Firestore/Example/Tests/Util/FSTDispatchQueueTests.mm rename Firestore/Example/Tests/Util/{FSTEventAccumulator.m => FSTEventAccumulator.mm} (100%) rename Firestore/Example/Tests/Util/{FSTHelpers.m => FSTHelpers.mm} (91%) delete mode 100644 Firestore/Example/Tests/Util/FSTTestDispatchQueue.m rename Firestore/Example/Tests/Util/{XCTestCase+Await.m => XCTestCase+Await.mm} (86%) delete mode 100644 Firestore/Port/string_util.h create mode 100644 Firestore/Protos/CMakeLists.txt create mode 100644 Firestore/Protos/nanopb/firestore/local/maybe_document.pb.c create mode 100644 Firestore/Protos/nanopb/firestore/local/maybe_document.pb.h create mode 100644 Firestore/Protos/nanopb/firestore/local/mutation.pb.c create mode 100644 Firestore/Protos/nanopb/firestore/local/mutation.pb.h create mode 100644 Firestore/Protos/nanopb/firestore/local/target.pb.c create mode 100644 Firestore/Protos/nanopb/firestore/local/target.pb.h create mode 100644 Firestore/Protos/nanopb/google/api/annotations.pb.c create mode 100644 Firestore/Protos/nanopb/google/api/annotations.pb.h create mode 100644 Firestore/Protos/nanopb/google/api/http.pb.c create mode 100644 Firestore/Protos/nanopb/google/api/http.pb.h create mode 100644 Firestore/Protos/nanopb/google/firestore/v1beta1/common.pb.c create mode 100644 Firestore/Protos/nanopb/google/firestore/v1beta1/common.pb.h create mode 100644 Firestore/Protos/nanopb/google/firestore/v1beta1/document.pb.c create mode 100644 Firestore/Protos/nanopb/google/firestore/v1beta1/document.pb.h create mode 100644 Firestore/Protos/nanopb/google/firestore/v1beta1/firestore.pb.c create mode 100644 Firestore/Protos/nanopb/google/firestore/v1beta1/firestore.pb.h create mode 100644 Firestore/Protos/nanopb/google/firestore/v1beta1/query.pb.c create mode 100644 Firestore/Protos/nanopb/google/firestore/v1beta1/query.pb.h create mode 100644 Firestore/Protos/nanopb/google/firestore/v1beta1/write.pb.c create mode 100644 Firestore/Protos/nanopb/google/firestore/v1beta1/write.pb.h create mode 100644 Firestore/Protos/nanopb/google/protobuf/any.pb.c create mode 100644 Firestore/Protos/nanopb/google/protobuf/any.pb.h create mode 100644 Firestore/Protos/nanopb/google/protobuf/empty.pb.c create mode 100644 Firestore/Protos/nanopb/google/protobuf/empty.pb.h create mode 100644 Firestore/Protos/nanopb/google/protobuf/struct.pb.c create mode 100644 Firestore/Protos/nanopb/google/protobuf/struct.pb.h create mode 100644 Firestore/Protos/nanopb/google/protobuf/timestamp.pb.c create mode 100644 Firestore/Protos/nanopb/google/protobuf/timestamp.pb.h create mode 100644 Firestore/Protos/nanopb/google/protobuf/wrappers.pb.c create mode 100644 Firestore/Protos/nanopb/google/protobuf/wrappers.pb.h create mode 100644 Firestore/Protos/nanopb/google/rpc/status.pb.c create mode 100644 Firestore/Protos/nanopb/google/rpc/status.pb.h create mode 100644 Firestore/Protos/nanopb/google/type/latlng.pb.c create mode 100644 Firestore/Protos/nanopb/google/type/latlng.pb.h create mode 100644 Firestore/Protos/protos/google/api/http.options create mode 100644 Firestore/Protos/protos/google/firestore/v1beta1/document.options create mode 100644 Firestore/Protos/protos/google/firestore/v1beta1/firestore.options create mode 100644 Firestore/Protos/protos/google/firestore/v1beta1/write.options create mode 100644 Firestore/Protos/protos/google/protobuf/any.proto create mode 100644 Firestore/Protos/protos/google/protobuf/empty.proto create mode 100644 Firestore/Protos/protos/google/protobuf/struct.options create mode 100644 Firestore/Protos/protos/google/protobuf/struct.proto create mode 100644 Firestore/Protos/protos/google/protobuf/timestamp.proto create mode 100644 Firestore/Protos/protos/google/protobuf/wrappers.proto rename Firestore/Source/API/{FIRDocumentChange.m => FIRDocumentChange.mm} (100%) rename Firestore/Source/API/{FIRDocumentReference.m => FIRDocumentReference.mm} (98%) rename Firestore/Source/API/{FIRDocumentSnapshot.m => FIRDocumentSnapshot.mm} (87%) rename Firestore/Source/API/{FIRFieldPath.m => FIRFieldPath.mm} (100%) rename Firestore/Source/API/{FIRFieldValue.m => FIRFieldValue.mm} (100%) rename Firestore/Source/API/{FIRFirestore.m => FIRFirestore.mm} (72%) rename Firestore/Source/API/{FIRFirestoreSettings.m => FIRFirestoreSettings.mm} (100%) rename Firestore/Source/API/{FIRFirestoreVersion.m => FIRFirestoreVersion.mm} (93%) rename Firestore/Source/API/{FIRGeoPoint.m => FIRGeoPoint.mm} (77%) rename Firestore/Source/API/{FIRListenerRegistration.m => FIRListenerRegistration.mm} (100%) rename Firestore/Source/API/{FIRQuery.m => FIRQuery.mm} (99%) rename Firestore/Source/API/{FIRQuerySnapshot.m => FIRQuerySnapshot.mm} (91%) rename Firestore/Source/API/{FIRSetOptions.m => FIRSetOptions.mm} (97%) rename Firestore/Source/API/{FIRSnapshotMetadata.m => FIRSnapshotMetadata.mm} (93%) rename Firestore/Source/API/{FIRSnapshotOptions.m => FIRSnapshotOptions.mm} (100%) create mode 100644 Firestore/Source/API/FIRTimestamp+Internal.h rename Firestore/Source/{Core/FSTTimestamp.m => API/FIRTimestamp.m} (52%) rename Firestore/Source/API/{FIRTransaction.m => FIRTransaction.mm} (100%) rename Firestore/Source/API/{FIRWriteBatch.m => FIRWriteBatch.mm} (100%) rename Firestore/Source/API/{FSTUserDataConverter.m => FSTUserDataConverter.mm} (94%) delete mode 100644 Firestore/Source/Auth/FSTCredentialsProvider.h delete mode 100644 Firestore/Source/Auth/FSTCredentialsProvider.m delete mode 100644 Firestore/Source/Auth/FSTEmptyCredentialsProvider.m delete mode 100644 Firestore/Source/Auth/FSTUser.h delete mode 100644 Firestore/Source/Auth/FSTUser.m delete mode 100644 Firestore/Source/Core/FSTDatabaseInfo.h delete mode 100644 Firestore/Source/Core/FSTDatabaseInfo.m rename Firestore/Source/Core/{FSTEventManager.m => FSTEventManager.mm} (100%) rename Firestore/Source/Core/{FSTFirestoreClient.m => FSTFirestoreClient.mm} (82%) create mode 100644 Firestore/Source/Core/FSTListenSequence.h create mode 100644 Firestore/Source/Core/FSTListenSequence.mm rename Firestore/Source/Core/{FSTQuery.m => FSTQuery.mm} (97%) rename Firestore/Source/Core/{FSTSnapshotVersion.m => FSTSnapshotVersion.mm} (87%) rename Firestore/Source/Core/{FSTSyncEngine.m => FSTSyncEngine.mm} (94%) delete mode 100644 Firestore/Source/Core/FSTTargetIDGenerator.h delete mode 100644 Firestore/Source/Core/FSTTargetIDGenerator.m rename Firestore/Source/Core/{FSTTransaction.m => FSTTransaction.mm} (91%) rename Firestore/Source/Core/{FSTView.m => FSTView.mm} (96%) rename Firestore/Source/Core/{FSTViewSnapshot.m => FSTViewSnapshot.mm} (100%) rename Firestore/Source/Local/{FSTDocumentReference.m => FSTDocumentReference.mm} (87%) rename Firestore/Source/Local/{FSTEagerGarbageCollector.m => FSTEagerGarbageCollector.mm} (100%) rename Firestore/{Example/Tests/Util/FSTTestDispatchQueue.h => Source/Local/FSTLevelDBMigrations.h} (53%) create mode 100644 Firestore/Source/Local/FSTLevelDBMigrations.mm rename Firestore/Source/Local/{FSTLocalDocumentsView.m => FSTLocalDocumentsView.mm} (100%) rename Firestore/Source/Local/{FSTLocalSerializer.m => FSTLocalSerializer.mm} (96%) rename Firestore/Source/Local/{FSTLocalStore.m => FSTLocalStore.mm} (93%) rename Firestore/Source/Local/{FSTLocalViewChanges.m => FSTLocalViewChanges.mm} (100%) rename Firestore/Source/Local/{FSTLocalWriteResult.m => FSTLocalWriteResult.mm} (100%) rename Firestore/Source/Local/{FSTMemoryMutationQueue.m => FSTMemoryMutationQueue.mm} (98%) rename Firestore/Source/Local/{FSTMemoryPersistence.m => FSTMemoryPersistence.mm} (88%) rename Firestore/Source/Local/{FSTMemoryQueryCache.m => FSTMemoryQueryCache.mm} (83%) rename Firestore/Source/Local/{FSTMemoryRemoteDocumentCache.m => FSTMemoryRemoteDocumentCache.mm} (100%) rename Firestore/Source/Local/{FSTNoOpGarbageCollector.m => FSTNoOpGarbageCollector.mm} (100%) rename Firestore/Source/Local/{FSTQueryData.m => FSTQueryData.mm} (91%) rename Firestore/Source/Local/{FSTReferenceSet.m => FSTReferenceSet.mm} (100%) rename Firestore/Source/Local/{FSTRemoteDocumentChangeBuffer.m => FSTRemoteDocumentChangeBuffer.mm} (100%) rename Firestore/Source/Local/{FSTWriteGroupTracker.m => FSTWriteGroupTracker.mm} (100%) delete mode 100644 Firestore/Source/Model/FSTDatabaseID.h delete mode 100644 Firestore/Source/Model/FSTDatabaseID.m rename Firestore/Source/Model/{FSTDocument.m => FSTDocument.mm} (100%) rename Firestore/Source/Model/{FSTDocumentDictionary.m => FSTDocumentDictionary.mm} (100%) rename Firestore/Source/Model/{FSTDocumentKey.m => FSTDocumentKey.mm} (100%) rename Firestore/Source/Model/{FSTDocumentKeySet.m => FSTDocumentKeySet.mm} (100%) rename Firestore/Source/Model/{FSTDocumentSet.m => FSTDocumentSet.mm} (92%) rename Firestore/Source/Model/{FSTDocumentVersionDictionary.m => FSTDocumentVersionDictionary.mm} (100%) rename Firestore/Source/Model/{FSTFieldValue.m => FSTFieldValue.mm} (85%) rename Firestore/Source/Model/{FSTMutation.m => FSTMutation.mm} (97%) rename Firestore/Source/Model/{FSTMutationBatch.m => FSTMutationBatch.mm} (98%) rename Firestore/Source/Model/{FSTPath.m => FSTPath.mm} (100%) rename Firestore/Source/{Core/FSTTimestamp.h => Public/FIRTimestamp.h} (52%) rename Firestore/Source/Remote/{FSTBufferedWriter.m => FSTBufferedWriter.mm} (100%) rename Firestore/Source/Remote/{FSTDatastore.m => FSTDatastore.mm} (81%) rename Firestore/Source/Remote/{FSTExistenceFilter.m => FSTExistenceFilter.mm} (100%) rename Firestore/Source/Remote/{FSTRemoteEvent.m => FSTRemoteEvent.mm} (97%) rename Firestore/Source/Remote/{FSTRemoteStore.m => FSTRemoteStore.mm} (97%) rename Firestore/Source/Remote/{FSTSerializerBeta.m => FSTSerializerBeta.mm} (92%) rename Firestore/Source/Remote/{FSTStream.m => FSTStream.mm} (78%) rename Firestore/Source/Remote/{FSTWatchChange.m => FSTWatchChange.mm} (100%) rename Firestore/Source/Util/{FSTAsyncQueryListener.m => FSTAsyncQueryListener.mm} (73%) delete mode 100644 Firestore/Source/Util/FSTComparison.h delete mode 100644 Firestore/Source/Util/FSTComparison.m delete mode 100644 Firestore/Source/Util/FSTDispatchQueue.m create mode 100644 Firestore/Source/Util/FSTDispatchQueue.mm rename Firestore/Source/Util/{FSTLogger.m => FSTLogger.mm} (100%) rename Firestore/Source/Util/{FSTUsageValidation.m => FSTUsageValidation.mm} (100%) create mode 100644 Firestore/Swift/Source/Codable/CodableGeoPoint.swift create mode 100644 Firestore/Swift/Tests/Codable/CodableGeoPointTests.swift create mode 100644 Firestore/Swift/Tests/Info.plist create mode 100644 Firestore/core/include/firebase/firestore/document_reference.h create mode 100644 Firestore/core/include/firebase/firestore/event_listener.h create mode 100644 Firestore/core/include/firebase/firestore/firestore.h create mode 100644 Firestore/core/include/firebase/firestore/firestore_errors.h create mode 100644 Firestore/core/include/firebase/firestore/geo_point.h create mode 100644 Firestore/core/src/firebase/firestore/CMakeLists.txt create mode 100644 Firestore/core/src/firebase/firestore/auth/CMakeLists.txt create mode 100644 Firestore/core/src/firebase/firestore/auth/credentials_provider.cc create mode 100644 Firestore/core/src/firebase/firestore/auth/credentials_provider.h create mode 100644 Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.cc create mode 100644 Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.h create mode 100644 Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.h create mode 100644 Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.mm create mode 100644 Firestore/core/src/firebase/firestore/auth/token.cc create mode 100644 Firestore/core/src/firebase/firestore/auth/token.h create mode 100644 Firestore/core/src/firebase/firestore/auth/user.cc create mode 100644 Firestore/core/src/firebase/firestore/auth/user.h delete mode 100644 Firestore/core/src/firebase/firestore/base/port.h create mode 100644 Firestore/core/src/firebase/firestore/core/CMakeLists.txt create mode 100644 Firestore/core/src/firebase/firestore/core/database_info.cc create mode 100644 Firestore/core/src/firebase/firestore/core/database_info.h create mode 100644 Firestore/core/src/firebase/firestore/core/target_id_generator.cc create mode 100644 Firestore/core/src/firebase/firestore/core/target_id_generator.h create mode 100644 Firestore/core/src/firebase/firestore/core/types.h create mode 100644 Firestore/core/src/firebase/firestore/geo_point.cc create mode 100644 Firestore/core/src/firebase/firestore/immutable/CMakeLists.txt create mode 100644 Firestore/core/src/firebase/firestore/immutable/array_sorted_map.cc create mode 100644 Firestore/core/src/firebase/firestore/immutable/array_sorted_map.h create mode 100644 Firestore/core/src/firebase/firestore/immutable/map_entry.h create mode 100644 Firestore/core/src/firebase/firestore/model/CMakeLists.txt create mode 100644 Firestore/core/src/firebase/firestore/model/base_path.h create mode 100644 Firestore/core/src/firebase/firestore/model/database_id.cc create mode 100644 Firestore/core/src/firebase/firestore/model/database_id.h create mode 100644 Firestore/core/src/firebase/firestore/model/document.cc create mode 100644 Firestore/core/src/firebase/firestore/model/document.h create mode 100644 Firestore/core/src/firebase/firestore/model/document_key.cc create mode 100644 Firestore/core/src/firebase/firestore/model/document_key.h create mode 100644 Firestore/core/src/firebase/firestore/model/field_path.cc create mode 100644 Firestore/core/src/firebase/firestore/model/field_path.h create mode 100644 Firestore/core/src/firebase/firestore/model/field_value.cc create mode 100644 Firestore/core/src/firebase/firestore/model/field_value.h create mode 100644 Firestore/core/src/firebase/firestore/model/maybe_document.cc create mode 100644 Firestore/core/src/firebase/firestore/model/maybe_document.h create mode 100644 Firestore/core/src/firebase/firestore/model/no_document.cc create mode 100644 Firestore/core/src/firebase/firestore/model/no_document.h create mode 100644 Firestore/core/src/firebase/firestore/model/resource_path.cc create mode 100644 Firestore/core/src/firebase/firestore/model/resource_path.h create mode 100644 Firestore/core/src/firebase/firestore/model/snapshot_version.cc create mode 100644 Firestore/core/src/firebase/firestore/model/snapshot_version.h create mode 100644 Firestore/core/src/firebase/firestore/model/timestamp.cc create mode 100644 Firestore/core/src/firebase/firestore/model/timestamp.h create mode 100644 Firestore/core/src/firebase/firestore/remote/CMakeLists.txt rename Firestore/{Source/Auth/FSTEmptyCredentialsProvider.h => core/src/firebase/firestore/remote/datastore.cc} (64%) create mode 100644 Firestore/core/src/firebase/firestore/remote/datastore.h create mode 100644 Firestore/core/src/firebase/firestore/remote/serializer.cc create mode 100644 Firestore/core/src/firebase/firestore/remote/serializer.h rename Firestore/{Port => core/src/firebase/firestore/util}/bits.cc (74%) rename Firestore/{Port => core/src/firebase/firestore/util}/bits.h (87%) create mode 100644 Firestore/core/src/firebase/firestore/util/comparison.cc create mode 100644 Firestore/core/src/firebase/firestore/util/comparison.h create mode 100644 Firestore/core/src/firebase/firestore/util/config.h.in create mode 100644 Firestore/core/src/firebase/firestore/util/error_apple.h create mode 100644 Firestore/core/src/firebase/firestore/util/iterator_adaptors.h rename Firestore/{Port => core/src/firebase/firestore/util}/ordered_code.cc (71%) rename Firestore/{Port => core/src/firebase/firestore/util}/ordered_code.h (64%) create mode 100644 Firestore/core/src/firebase/firestore/util/secure_random_openssl.cc rename Firestore/{Port => core/src/firebase/firestore/util}/string_util.cc (79%) create mode 100644 Firestore/core/src/firebase/firestore/util/string_util.h create mode 100644 Firestore/core/test/firebase/firestore/CMakeLists.txt create mode 100644 Firestore/core/test/firebase/firestore/auth/CMakeLists.txt create mode 100644 Firestore/core/test/firebase/firestore/auth/credentials_provider_test.cc create mode 100644 Firestore/core/test/firebase/firestore/auth/empty_credentials_provider_test.cc create mode 100644 Firestore/core/test/firebase/firestore/auth/firebase_credentials_provider_test.mm create mode 100644 Firestore/core/test/firebase/firestore/auth/token_test.cc create mode 100644 Firestore/core/test/firebase/firestore/auth/user_test.cc create mode 100644 Firestore/core/test/firebase/firestore/core/CMakeLists.txt create mode 100644 Firestore/core/test/firebase/firestore/core/database_info_test.cc create mode 100644 Firestore/core/test/firebase/firestore/core/target_id_generator_test.cc create mode 100644 Firestore/core/test/firebase/firestore/geo_point_test.cc create mode 100644 Firestore/core/test/firebase/firestore/immutable/CMakeLists.txt create mode 100644 Firestore/core/test/firebase/firestore/immutable/array_sorted_map_test.cc create mode 100644 Firestore/core/test/firebase/firestore/model/CMakeLists.txt create mode 100644 Firestore/core/test/firebase/firestore/model/database_id_test.cc create mode 100644 Firestore/core/test/firebase/firestore/model/document_key_test.cc create mode 100644 Firestore/core/test/firebase/firestore/model/document_test.cc create mode 100644 Firestore/core/test/firebase/firestore/model/field_path_test.cc create mode 100644 Firestore/core/test/firebase/firestore/model/field_value_test.cc create mode 100644 Firestore/core/test/firebase/firestore/model/maybe_document_test.cc create mode 100644 Firestore/core/test/firebase/firestore/model/no_document_test.cc create mode 100644 Firestore/core/test/firebase/firestore/model/resource_path_test.cc create mode 100644 Firestore/core/test/firebase/firestore/model/snapshot_version_test.cc create mode 100644 Firestore/core/test/firebase/firestore/model/timestamp_test.cc create mode 100644 Firestore/core/test/firebase/firestore/remote/CMakeLists.txt create mode 100644 Firestore/core/test/firebase/firestore/remote/datastore_test.cc create mode 100644 Firestore/core/test/firebase/firestore/remote/serializer_test.cc create mode 100644 Firestore/core/test/firebase/firestore/testutil/CMakeLists.txt create mode 100644 Firestore/core/test/firebase/firestore/testutil/app_testing.h create mode 100644 Firestore/core/test/firebase/firestore/testutil/app_testing.mm rename Firestore/{Port => core/test/firebase/firestore/util}/bits_test.cc (72%) create mode 100644 Firestore/core/test/firebase/firestore/util/comparison_test.cc create mode 100644 Firestore/core/test/firebase/firestore/util/iterator_adaptors_test.cc rename Firestore/{Port => core/test/firebase/firestore/util}/ordered_code_test.cc (69%) rename Firestore/{Port => core/test/firebase/firestore/util}/string_util_test.cc (70%) rename Firestore/third_party/abseil-cpp/absl/base/{internal => }/log_severity.h (80%) create mode 100644 Firestore/third_party/abseil-cpp/absl/memory/CMakeLists.txt create mode 100644 Firestore/third_party/abseil-cpp/absl/memory/memory.h create mode 100644 Firestore/third_party/abseil-cpp/absl/memory/memory_test.cc create mode 100644 Firestore/third_party/abseil-cpp/absl/numeric/CMakeLists.txt create mode 100644 Firestore/third_party/abseil-cpp/absl/numeric/int128.cc create mode 100644 Firestore/third_party/abseil-cpp/absl/numeric/int128.h create mode 100644 Firestore/third_party/abseil-cpp/absl/numeric/int128_have_intrinsic.inc create mode 100644 Firestore/third_party/abseil-cpp/absl/numeric/int128_no_intrinsic.inc create mode 100644 Firestore/third_party/abseil-cpp/absl/numeric/int128_stream_test.cc create mode 100644 Firestore/third_party/abseil-cpp/absl/numeric/int128_test.cc create mode 100644 Firestore/third_party/abseil-cpp/absl/strings/escaping.cc create mode 100644 Firestore/third_party/abseil-cpp/absl/strings/escaping.h create mode 100644 Firestore/third_party/abseil-cpp/absl/strings/escaping_test.cc create mode 100644 Firestore/third_party/abseil-cpp/absl/strings/internal/char_map.h create mode 100644 Firestore/third_party/abseil-cpp/absl/strings/internal/char_map_test.cc create mode 100644 Firestore/third_party/abseil-cpp/absl/strings/internal/escaping_test_common.inc create mode 100644 Firestore/third_party/abseil-cpp/absl/strings/internal/numbers_test_common.inc create mode 100644 Firestore/third_party/abseil-cpp/absl/strings/internal/ostringstream.cc create mode 100644 Firestore/third_party/abseil-cpp/absl/strings/internal/ostringstream.h create mode 100644 Firestore/third_party/abseil-cpp/absl/strings/internal/ostringstream_test.cc create mode 100644 Firestore/third_party/abseil-cpp/absl/strings/internal/stl_type_traits.h create mode 100644 Firestore/third_party/abseil-cpp/absl/strings/internal/str_join_internal.h create mode 100644 Firestore/third_party/abseil-cpp/absl/strings/internal/str_split_internal.h create mode 100644 Firestore/third_party/abseil-cpp/absl/strings/internal/utf8.cc create mode 100644 Firestore/third_party/abseil-cpp/absl/strings/internal/utf8.h create mode 100644 Firestore/third_party/abseil-cpp/absl/strings/internal/utf8_test.cc create mode 100644 Firestore/third_party/abseil-cpp/absl/strings/numbers.cc create mode 100644 Firestore/third_party/abseil-cpp/absl/strings/numbers.h create mode 100644 Firestore/third_party/abseil-cpp/absl/strings/numbers_test.cc create mode 100644 Firestore/third_party/abseil-cpp/absl/strings/str_cat.cc create mode 100644 Firestore/third_party/abseil-cpp/absl/strings/str_cat.h create mode 100644 Firestore/third_party/abseil-cpp/absl/strings/str_cat_test.cc create mode 100644 Firestore/third_party/abseil-cpp/absl/strings/str_join.h create mode 100644 Firestore/third_party/abseil-cpp/absl/strings/str_join_test.cc create mode 100644 Firestore/third_party/abseil-cpp/absl/strings/str_replace.cc create mode 100644 Firestore/third_party/abseil-cpp/absl/strings/str_replace.h create mode 100644 Firestore/third_party/abseil-cpp/absl/strings/str_replace_test.cc create mode 100644 Firestore/third_party/abseil-cpp/absl/strings/str_split.cc create mode 100644 Firestore/third_party/abseil-cpp/absl/strings/str_split.h create mode 100644 Firestore/third_party/abseil-cpp/absl/strings/str_split_test.cc create mode 100644 Firestore/third_party/abseil-cpp/absl/strings/strip.h create mode 100644 Firestore/third_party/abseil-cpp/absl/strings/strip_test.cc create mode 100644 Firestore/third_party/abseil-cpp/absl/strings/substitute.cc create mode 100644 Firestore/third_party/abseil-cpp/absl/strings/substitute.h create mode 100644 Firestore/third_party/abseil-cpp/absl/strings/substitute_test.cc create mode 100644 cmake/CompilerSetup.cmake create mode 100644 cmake/ExternalProjectFlags.cmake create mode 100644 cmake/FindGRPC.cmake create mode 100644 cmake/FindNanopb.cmake create mode 100644 cmake/external/grpc.cmake create mode 100644 cmake/external/nanopb.cmake create mode 100644 cmake/external/protobuf.cmake create mode 100644 scripts/cpplint.py create mode 100755 scripts/lint.sh diff --git a/.gitignore b/.gitignore index 79daac41384..7645c896691 100644 --- a/.gitignore +++ b/.gitignore @@ -58,6 +58,9 @@ Pods/ Podfile.lock *.xcworkspace +# Firestore's build configuration, as generated by CocoaPods +Firestore/core/src/firebase/firestore/util/config.h + # CMake .downloads Debug diff --git a/.travis.yml b/.travis.yml index 18b04e2e2e6..7c1c0b4d779 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,13 +12,46 @@ before_install: - gem install xcpretty - bundle exec pod install --project-directory=Example --repo-update - bundle exec pod install --project-directory=Firestore/Example --no-repo-update + - brew install clang-format + - brew install swiftformat + - brew install cmake + - brew install go # Somehow the build for Abseil requires this. + - echo "$TRAVIS_COMMIT_RANGE" + - echo "$TRAVIS_PULL_REQUEST" + - | + if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then + SKIP_FIREBASE=0 + SKIP_FIRESTORE=0 + else + git diff --name-only $TRAVIS_COMMIT_RANGE | grep -Eq '^(Firebase|Example)' + SKIP_FIREBASE="$?" + git diff --name-only $TRAVIS_COMMIT_RANGE | grep -q Firestore + SKIP_FIRESTORE="$?" + fi script: - - "! git grep -I ' $'" # Fail on trailing whitespace in non-binary files - - ./test.sh + # Fail on trailing whitespace in non-binary, non-generated-nanopb files + - "! git grep -I ' $' ':(exclude)Firestore/Protos/nanopb'" + - "! git grep -EL --name-only 'Copyright [0-9]{4}.*Google' | grep -v third_party | egrep '\\.(m|h|cc|mm|c)$'" + - ./scripts/style.sh test-only # Validate clang-format compliance + - | + if [ $SKIP_FIREBASE != 1 ]; then + ./test.sh + fi + - | + if [ $SKIP_FIRESTORE != 1 ]; then + ./scripts/lint.sh # Google C++ style compliance + fi + - | + if [ $SKIP_FIRESTORE != 1 ]; then + ./Firestore/test.sh + fi # TODO fix os_log deprecation warning in FIRLogger to remove --allow-warnings - - bundle exec pod lib lint FirebaseCore.podspec --allow-warnings + - | + if [ $SKIP_FIREBASE != 1 ]; then + bundle exec pod lib lint FirebaseCore.podspec --allow-warnings + fi # TODO - Uncomment subsequent lines once FirebaseCore source repo is in public Specs repo # - bundle exec pod lib lint FirebaseAuth.podspec diff --git a/CMakeLists.txt b/CMakeLists.txt index edcd611e029..15b68b5b5ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,7 +37,9 @@ set(FIREBASE_INSTALL_DIR ${PROJECT_BINARY_DIR}) enable_testing() include(external/FirebaseCore) - include(external/googletest) include(external/leveldb) +include(external/grpc) +include(external/protobuf) +include(external/nanopb) include(external/firestore) diff --git a/Carthage.md b/Carthage.md new file mode 100644 index 00000000000..e111205314c --- /dev/null +++ b/Carthage.md @@ -0,0 +1,72 @@ +# Firebase Carthage + +## Context + +This page introduces and provides instructions for an **experimental** Firebase +[Carthage](https://github.com/Carthage/Carthage) implementation. Based on +feedback and usage, the Firebase team may decide to make the Carthage +distribution official. + +FirebaseFirestore and FirebaseInvites are not yet supported from Carthage. +Please [let us know](https://github.com/firebase/firebase-ios-sdk/issues) if you +have suggestions about how best to distribute Carthage binaries that include +resource bundles. + +## Carthage Installation + +[Homebrew](http://brew.sh/) is one way to install Carthage. + +```bash +$ brew update +$ brew install carthage +``` + +See the +[Carthage page](https://github.com/Carthage/Carthage#installing-carthage) for +more details and additional installation methods. + +## Carthage Usage + +- Create a Cartfile with a **subset** of the following components. Note that + **FirebaseAnalytics** must always be included. +``` +binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseABTestingBinary.json" +binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseAdMobBinary.json" +binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseAnalyticsBinary.json" +binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseAuthBinary.json" +binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseCrashBinary.json" +binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseDatabaseBinary.json" +binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseDynamicLinksBinary.json" +binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseMessagingBinary.json" +binary "https://dl.google.com/dl/firebase/ios/carthage/FirebasePerformanceBinary.json" +binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseRemoteConfigBinary.json" +binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseStorageBinary.json" +``` +- Run `carthage update` +- Use Finder to open `Carthage/Build/iOS`. +- Copy the contents into the top level of your Xcode project and make sure + they're added to the right build target(s). +- Add the -ObjC flag to "Other Linker Flags". +- Make sure that the build target(s) includes your project's `GoogleService-Info.plist`. + +## Versioning + +Unlike the CocoaPods distribution, the Carthage distribution is like the +Firebase zip release in that all the Firebase components share the same version. +Mixing and matching components with different versions may cause linker errors. + +## Static Frameworks + +Note that the Firebase frameworks in the distribution include static libraries. +While it is fine to link these into apps, it will generally not work to depend +on them from wrapper dynamic frameworks. + +## Acknowledgements + +Thanks to the Firebase community for encouraging this implementation including +those who have made this the most updated +[firebase-ios-sdk](https://github.com/firebase/firebase-ios-sdk) +[issue](https://github.com/firebase/firebase-ios-sdk/issues/9). + +Thanks also to those who have already done Firebase Carthage implementations, +such as https://github.com/soheilbm/Firebase. diff --git a/Example/Core/Tests/FIRAppAssociationRegistrationUnitTests.m b/Example/Core/Tests/FIRAppAssociationRegistrationUnitTests.m index 2bc1de7f3c4..60f0651e5ee 100644 --- a/Example/Core/Tests/FIRAppAssociationRegistrationUnitTests.m +++ b/Example/Core/Tests/FIRAppAssociationRegistrationUnitTests.m @@ -34,7 +34,7 @@ /** @var gCreateNewObject @brief A block that returns a new object everytime it is called. */ -static id _Nullable (^gCreateNewObject)() = ^id _Nullable() { +static id _Nullable (^gCreateNewObject)(void) = ^id _Nullable() { return [[NSObject alloc] init]; }; diff --git a/Example/Core/Tests/FIRAppTest.m b/Example/Core/Tests/FIRAppTest.m index 846648872d6..3784a6ebdd0 100644 --- a/Example/Core/Tests/FIRAppTest.m +++ b/Example/Core/Tests/FIRAppTest.m @@ -596,6 +596,27 @@ - (void)testIsAppConfigured { XCTAssertFalse([FIRApp isDefaultAppConfigured]); } +- (void)testIllegalLibraryName { + [FIRApp registerLibrary:@"Oops>" withVersion:@"1.0.0"]; + XCTAssertTrue([[FIRApp firebaseUserAgent] isEqualToString:@""]); +} + +- (void)testIllegalLibraryVersion { + [FIRApp registerLibrary:@"LegalName" withVersion:@"1.0.0+"]; + XCTAssertTrue([[FIRApp firebaseUserAgent] isEqualToString:@""]); +} + +- (void)testSingleLibrary { + [FIRApp registerLibrary:@"LegalName" withVersion:@"1.0.0"]; + XCTAssertTrue([[FIRApp firebaseUserAgent] containsString:@"LegalName/1.0.0"]); +} + +- (void)testMultipleLibraries { + [FIRApp registerLibrary:@"LegalName" withVersion:@"1.0.0"]; + [FIRApp registerLibrary:@"LegalName2" withVersion:@"2.0.0"]; + XCTAssertTrue([[FIRApp firebaseUserAgent] containsString:@"LegalName/1.0.0 LegalName2/2.0.0"]); +} + #pragma mark - private - (NSDictionary *)expectedUserInfoWithAppName:(NSString *)name diff --git a/Example/Core/Tests/FIRLoggerTest.m b/Example/Core/Tests/FIRLoggerTest.m index 70b688af2d3..5cc74658cef 100644 --- a/Example/Core/Tests/FIRLoggerTest.m +++ b/Example/Core/Tests/FIRLoggerTest.m @@ -28,13 +28,13 @@ extern const char *kFIRLoggerCustomASLMessageFormat; -extern void FIRResetLogger(); +extern void FIRResetLogger(void); -extern aslclient getFIRLoggerClient(); +extern aslclient getFIRLoggerClient(void); -extern dispatch_queue_t getFIRClientQueue(); +extern dispatch_queue_t getFIRClientQueue(void); -extern BOOL getFIRLoggerDebugMode(); +extern BOOL getFIRLoggerDebugMode(void); // Define the message format again to make sure the format doesn't accidentally change. static NSString *const kCorrectASLMessageFormat = diff --git a/Example/Core/Tests/FIROptionsTest.m b/Example/Core/Tests/FIROptionsTest.m index d27a0e1eabb..5d66ca97950 100644 --- a/Example/Core/Tests/FIROptionsTest.m +++ b/Example/Core/Tests/FIROptionsTest.m @@ -25,7 +25,8 @@ @interface FIROptions (Test) -@property(nonatomic, readonly) NSDictionary *analyticsOptionsDictionary; +- (nullable NSDictionary *)analyticsOptionsDictionaryWithInfoDictionary: + (nullable NSDictionary *)infoDictionary; @end @@ -127,7 +128,7 @@ - (void)testInitCustomizedOptions { #pragma clang diagnostic pop } -- (void)testinitWithContentsOfFile { +- (void)testInitWithContentsOfFile { NSString *filePath = [[NSBundle mainBundle] pathForResource:@"GoogleService-Info" ofType:@"plist"]; FIROptions *options = [[FIROptions alloc] initWithContentsOfFile:filePath]; @@ -263,22 +264,20 @@ - (void)testAnalyticsConstants { } - (void)testAnalyticsOptions { - id mainBundleMock = OCMPartialMock([NSBundle mainBundle]); - // No keys anywhere. NSDictionary *optionsDictionary = nil; FIROptions *options = [[FIROptions alloc] initInternalWithOptionsDictionary:optionsDictionary]; NSDictionary *mainDictionary = nil; - OCMExpect([mainBundleMock infoDictionary]).andReturn(mainDictionary); NSDictionary *expectedAnalyticsOptions = @{}; - XCTAssertEqualObjects(options.analyticsOptionsDictionary, expectedAnalyticsOptions); + NSDictionary *analyticsOptions = [options analyticsOptionsDictionaryWithInfoDictionary:nil]; + XCTAssertEqualObjects(analyticsOptions, expectedAnalyticsOptions); optionsDictionary = @{}; options = [[FIROptions alloc] initInternalWithOptionsDictionary:optionsDictionary]; mainDictionary = @{}; - OCMExpect([mainBundleMock infoDictionary]).andReturn(mainDictionary); expectedAnalyticsOptions = @{}; - XCTAssertEqualObjects(options.analyticsOptionsDictionary, expectedAnalyticsOptions); + analyticsOptions = [options analyticsOptionsDictionaryWithInfoDictionary:mainDictionary]; + XCTAssertEqualObjects(analyticsOptions, expectedAnalyticsOptions); // Main has no keys. optionsDictionary = @{ @@ -288,9 +287,9 @@ - (void)testAnalyticsOptions { }; options = [[FIROptions alloc] initInternalWithOptionsDictionary:optionsDictionary]; mainDictionary = @{}; - OCMExpect([mainBundleMock infoDictionary]).andReturn(mainDictionary); expectedAnalyticsOptions = optionsDictionary; - XCTAssertEqualObjects(options.analyticsOptionsDictionary, expectedAnalyticsOptions); + analyticsOptions = [options analyticsOptionsDictionaryWithInfoDictionary:mainDictionary]; + XCTAssertEqualObjects(analyticsOptions, expectedAnalyticsOptions); // Main overrides all the keys. optionsDictionary = @{ @@ -304,9 +303,9 @@ - (void)testAnalyticsOptions { kFIRIsAnalyticsCollectionEnabled : @NO, kFIRIsMeasurementEnabled : @NO }; - OCMExpect([mainBundleMock infoDictionary]).andReturn(mainDictionary); expectedAnalyticsOptions = mainDictionary; - XCTAssertEqualObjects(options.analyticsOptionsDictionary, expectedAnalyticsOptions); + analyticsOptions = [options analyticsOptionsDictionaryWithInfoDictionary:mainDictionary]; + XCTAssertEqualObjects(analyticsOptions, expectedAnalyticsOptions); // Keys exist only in main. optionsDictionary = @{}; @@ -316,9 +315,9 @@ - (void)testAnalyticsOptions { kFIRIsAnalyticsCollectionEnabled : @YES, kFIRIsMeasurementEnabled : @YES }; - OCMExpect([mainBundleMock infoDictionary]).andReturn(mainDictionary); expectedAnalyticsOptions = mainDictionary; - XCTAssertEqualObjects(options.analyticsOptionsDictionary, expectedAnalyticsOptions); + analyticsOptions = [options analyticsOptionsDictionaryWithInfoDictionary:mainDictionary]; + XCTAssertEqualObjects(analyticsOptions, expectedAnalyticsOptions); // Main overrides single keys. optionsDictionary = @{ @@ -328,13 +327,13 @@ - (void)testAnalyticsOptions { }; options = [[FIROptions alloc] initInternalWithOptionsDictionary:optionsDictionary]; mainDictionary = @{ kFIRIsAnalyticsCollectionDeactivated : @NO }; - OCMExpect([mainBundleMock infoDictionary]).andReturn(mainDictionary); expectedAnalyticsOptions = @{ kFIRIsAnalyticsCollectionDeactivated : @NO, // override kFIRIsAnalyticsCollectionEnabled : @YES, kFIRIsMeasurementEnabled : @YES }; - XCTAssertEqualObjects(options.analyticsOptionsDictionary, expectedAnalyticsOptions); + analyticsOptions = [options analyticsOptionsDictionaryWithInfoDictionary:mainDictionary]; + XCTAssertEqualObjects(analyticsOptions, expectedAnalyticsOptions); optionsDictionary = @{ kFIRIsAnalyticsCollectionDeactivated : @YES, @@ -343,13 +342,13 @@ - (void)testAnalyticsOptions { }; options = [[FIROptions alloc] initInternalWithOptionsDictionary:optionsDictionary]; mainDictionary = @{ kFIRIsAnalyticsCollectionEnabled : @NO }; - OCMExpect([mainBundleMock infoDictionary]).andReturn(mainDictionary); expectedAnalyticsOptions = @{ kFIRIsAnalyticsCollectionDeactivated : @YES, kFIRIsAnalyticsCollectionEnabled : @NO, // override kFIRIsMeasurementEnabled : @YES }; - XCTAssertEqualObjects(options.analyticsOptionsDictionary, expectedAnalyticsOptions); + analyticsOptions = [options analyticsOptionsDictionaryWithInfoDictionary:mainDictionary]; + XCTAssertEqualObjects(analyticsOptions, expectedAnalyticsOptions); optionsDictionary = @{ kFIRIsAnalyticsCollectionDeactivated : @YES, @@ -358,18 +357,18 @@ - (void)testAnalyticsOptions { }; options = [[FIROptions alloc] initInternalWithOptionsDictionary:optionsDictionary]; mainDictionary = @{ kFIRIsMeasurementEnabled : @NO }; - OCMExpect([mainBundleMock infoDictionary]).andReturn(mainDictionary); expectedAnalyticsOptions = @{ kFIRIsAnalyticsCollectionDeactivated : @YES, kFIRIsAnalyticsCollectionEnabled : @YES, kFIRIsMeasurementEnabled : @NO // override }; - XCTAssertEqualObjects(options.analyticsOptionsDictionary, expectedAnalyticsOptions); + analyticsOptions = [options analyticsOptionsDictionaryWithInfoDictionary:mainDictionary]; + XCTAssertEqualObjects(analyticsOptions, expectedAnalyticsOptions); } - (void)testAnalyticsOptions_combinatorial { // Complete combinatorial test. - id mainBundleMock = OCMPartialMock([NSBundle mainBundle]); + // Possible values for the flags in the plist, where NSNull means the flag is not present. NSArray *values = @[ [NSNull null], @NO, @YES ]; @@ -398,6 +397,7 @@ - (void)testAnalyticsOptions_combinatorial { if (![optionsMeasurementEnabled isEqual:[NSNull null]]) { optionsDictionary[kFIRIsMeasurementEnabled] = optionsMeasurementEnabled; } + FIROptions *options = [[FIROptions alloc] initInternalWithOptionsDictionary:optionsDictionary]; if (![uniqueOptionsCombinations containsObject:optionsDictionary]) { @@ -415,7 +415,8 @@ - (void)testAnalyticsOptions_combinatorial { if (![mainMeasurementEnabled isEqual:[NSNull null]]) { mainDictionary[kFIRIsMeasurementEnabled] = mainMeasurementEnabled; } - OCMExpect([mainBundleMock infoDictionary]).andReturn(mainDictionary); + + // Add mainDictionary to uniqueMainCombinations if it isn't included yet. if (![uniqueMainCombinations containsObject:mainDictionary]) { [uniqueMainCombinations addObject:mainDictionary]; } @@ -427,7 +428,10 @@ - (void)testAnalyticsOptions_combinatorial { NSMutableDictionary *expectedAnalyticsOptions = [[NSMutableDictionary alloc] initWithDictionary:optionsDictionary]; [expectedAnalyticsOptions addEntriesFromDictionary:mainDictionary]; - XCTAssertEqualObjects(options.analyticsOptionsDictionary, expectedAnalyticsOptions); + + NSDictionary *analyticsOptions = + [options analyticsOptionsDictionaryWithInfoDictionary:mainDictionary]; + XCTAssertEqualObjects(analyticsOptions, expectedAnalyticsOptions); combinationCount++; } diff --git a/Example/Database/Tests/Integration/FData.m b/Example/Database/Tests/Integration/FData.m index aef15e1e280..d036f7728b4 100644 --- a/Example/Database/Tests/Integration/FData.m +++ b/Example/Database/Tests/Integration/FData.m @@ -487,7 +487,7 @@ - (void) testLargeNumericKeysDontGetTurnedIntoArrays { [[ref child:@"100003354884401"] setValue:@"alpha"]; __block BOOL ready = NO; - [ref observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) { + [ref observeSingleEventOfType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) { id val = [snapshot value]; XCTAssertTrue([val isKindOfClass:[NSDictionary class]], @"Expected a dictionary."); ready = YES; @@ -678,6 +678,8 @@ - (void) testSetAndThenListenForValueEventsAreCorrect { [self waitUntil:^BOOL{ return setDone && calls == 1; }]; + + [node removeAllObservers]; } - (void) testHasChildrenWorksCorrectly { @@ -878,6 +880,7 @@ - (void) testListenForValueAndThenWriteOnANodeWithExistingData { [self waitUntil:^BOOL{ return calls == 1; }]; + [reader removeAllObservers]; } - (void) testSetPriorityOnNonexistentNodeFails { @@ -2208,6 +2211,7 @@ - (void) testParentDeleteShadowsChildListeners { }]; WAIT_FOR(done); + [deleter removeAllObservers]; } - (void) testParentDeleteShadowsChildListenersWithNonDefaultQuery { diff --git a/Example/Database/Tests/Integration/FRealtime.m b/Example/Database/Tests/Integration/FRealtime.m index 5c7d1865b0b..5acda07e98e 100644 --- a/Example/Database/Tests/Integration/FRealtime.m +++ b/Example/Database/Tests/Integration/FRealtime.m @@ -481,6 +481,7 @@ - (void) testOnDisconnectDeepMergeTriggersOnlyOneValueEventForReaderWithQuery { WAIT_FOR(count == 2); // cleanup + [reader removeAllObservers]; [FRepoManager disposeRepos:writerCfg]; } diff --git a/Example/Database/Tests/Unit/FSyncPointTests.m b/Example/Database/Tests/Unit/FSyncPointTests.m index 797a5aa5494..de4680f30ea 100644 --- a/Example/Database/Tests/Unit/FSyncPointTests.m +++ b/Example/Database/Tests/Unit/FSyncPointTests.m @@ -86,15 +86,15 @@ - (BOOL) matches:(id)other { } - (void) fireEvent:(id)event queue:(dispatch_queue_t)queue { - [NSException raise:@"NotImplementedError" format:@"Method not implemneted."]; + [NSException raise:@"NotImplementedError" format:@"Method not implemented."]; } - (FCancelEvent *) createCancelEventFromError:(NSError *)error path:(FPath *)path { - [NSException raise:@"NotImplementedError" format:@"Method not implemneted."]; + [NSException raise:@"NotImplementedError" format:@"Method not implemented."]; return nil; } - (FIRDatabaseHandle) handle { - [NSException raise:@"NotImplementedError" format:@"Method not implemneted."]; + [NSException raise:@"NotImplementedError" format:@"Method not implemented."]; return 0; } @end diff --git a/Example/Firebase.xcodeproj/project.pbxproj b/Example/Firebase.xcodeproj/project.pbxproj index 9431ae0ff23..c8400d60752 100644 --- a/Example/Firebase.xcodeproj/project.pbxproj +++ b/Example/Firebase.xcodeproj/project.pbxproj @@ -8111,6 +8111,7 @@ CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = "$(SRCROOT)/Database/App/iOS/Database-Info.plist"; @@ -8129,6 +8130,7 @@ CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = ""; @@ -8650,6 +8652,7 @@ baseConfigurationReference = 06F3D16439F061DE9973902D /* Pods-Storage_Example_iOS.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; DEVELOPMENT_TEAM = ""; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; GCC_PRECOMPILE_PREFIX_HEADER = YES; @@ -8671,6 +8674,7 @@ baseConfigurationReference = D440FB786B320FCF836B508F /* Pods-Storage_Example_iOS.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; DEVELOPMENT_TEAM = ""; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; GCC_PRECOMPILE_PREFIX_HEADER = YES; diff --git a/Example/Messaging/App/iOS/Messaging-Info.plist b/Example/Messaging/App/iOS/Messaging-Info.plist index e42f39d270b..b43105e2da5 100644 --- a/Example/Messaging/App/iOS/Messaging-Info.plist +++ b/Example/Messaging/App/iOS/Messaging-Info.plist @@ -2,6 +2,8 @@ + FirebaseMessagingAutoInitEnabled + CFBundleDevelopmentRegion en CFBundleDisplayName diff --git a/Example/Messaging/Tests/FIRMessagingServiceTest.m b/Example/Messaging/Tests/FIRMessagingServiceTest.m index c2b0853a0ea..c8d61c27803 100644 --- a/Example/Messaging/Tests/FIRMessagingServiceTest.m +++ b/Example/Messaging/Tests/FIRMessagingServiceTest.m @@ -198,7 +198,8 @@ - (void)testSubscribeWithNoTopicPrefix { NSString *topicNameWithPrefix = [FIRMessagingPubSub addPrefixToTopic:topicName]; messaging.pubsub = mockPubSub; messaging.defaultFcmToken = @"fake-default-token"; - OCMExpect([messaging.pubsub subscribeToTopic:[OCMArg isEqual:topicNameWithPrefix]]); + OCMExpect([messaging.pubsub subscribeToTopic:[OCMArg isEqual:topicNameWithPrefix] + handler:[OCMArg any]]); [messaging subscribeToTopic:topicName]; OCMVerifyAll(mockPubSub); // Need to swap back since it's a singleton and hence will live beyond the scope of this test. @@ -213,7 +214,7 @@ - (void)testSubscribeWithTopicPrefix { NSString *topicName = @"/topics/topicWithoutPrefix"; messaging.pubsub = mockPubSub; messaging.defaultFcmToken = @"fake-default-token"; - OCMExpect([messaging.pubsub subscribeToTopic:[OCMArg isEqual:topicName]]); + OCMExpect([messaging.pubsub subscribeToTopic:[OCMArg isEqual:topicName] handler:[OCMArg any]]); [messaging subscribeToTopic:topicName]; OCMVerifyAll(mockPubSub); // Need to swap back since it's a singleton and hence will live beyond the scope of this test. @@ -229,7 +230,8 @@ - (void)testUnsubscribeWithNoTopicPrefix { NSString *topicNameWithPrefix = [FIRMessagingPubSub addPrefixToTopic:topicName]; messaging.pubsub = mockPubSub; messaging.defaultFcmToken = @"fake-default-token"; - OCMExpect([messaging.pubsub unsubscribeFromTopic:[OCMArg isEqual:topicNameWithPrefix]]); + OCMExpect([messaging.pubsub unsubscribeFromTopic:[OCMArg isEqual:topicNameWithPrefix] + handler:[OCMArg any]]); [messaging unsubscribeFromTopic:topicName]; OCMVerifyAll(mockPubSub); // Need to swap back since it's a singleton and hence will live beyond the scope of this test. @@ -244,7 +246,8 @@ - (void)testUnsubscribeWithTopicPrefix { NSString *topicName = @"/topics/topicWithPrefix"; messaging.pubsub = mockPubSub; messaging.defaultFcmToken = @"fake-default-token"; - OCMExpect([messaging.pubsub unsubscribeFromTopic:[OCMArg isEqual:topicName]]); + OCMExpect([messaging.pubsub unsubscribeFromTopic:[OCMArg isEqual:topicName] + handler:[OCMArg any]]); [messaging unsubscribeFromTopic:topicName]; OCMVerifyAll(mockPubSub); // Need to swap back since it's a singleton and hence will live beyond the scope of this test. diff --git a/Example/Messaging/Tests/FIRMessagingTest.m b/Example/Messaging/Tests/FIRMessagingTest.m index 09cdffc353b..0052e1b3d55 100644 --- a/Example/Messaging/Tests/FIRMessagingTest.m +++ b/Example/Messaging/Tests/FIRMessagingTest.m @@ -20,6 +20,7 @@ #import "FIRMessaging.h" #import "FIRMessagingInstanceIDProxy.h" +#import "FIRMessaging_Private.h" extern NSString *const kFIRMessagingFCMTokenFetchAPNSOption; @@ -28,6 +29,7 @@ @interface FIRMessaging () @property(nonatomic, readwrite, strong) NSString *defaultFcmToken; @property(nonatomic, readwrite, strong) NSData *apnsTokenData; @property(nonatomic, readwrite, strong) FIRMessagingInstanceIDProxy *instanceIDProxy; +@property(nonatomic, readwrite, strong) NSUserDefaults *messagingUserDefaults; - (instancetype)initPrivately; // Direct Channel Methods @@ -52,6 +54,8 @@ - (void)setUp { _mockMessaging = OCMPartialMock(self.messaging); _mockInstanceIDProxy = OCMPartialMock(self.messaging.instanceIDProxy); self.messaging.instanceIDProxy = _mockInstanceIDProxy; + [[NSUserDefaults standardUserDefaults] + removePersistentDomainForName:[NSBundle mainBundle].bundleIdentifier]; } - (void)tearDown { @@ -60,6 +64,15 @@ - (void)tearDown { [super tearDown]; } +- (void)testAutoInitEnableFlag { + // Should read from Info.plist + XCTAssertFalse(_messaging.isAutoInitEnabled); + + // Now set the flag should overwrite Info.plist value. + _messaging.autoInitEnabled = YES; + XCTAssertTrue(_messaging.isAutoInitEnabled); +} + #pragma mark - Direct Channel Establishment Testing // Should connect with valid token and application in foreground diff --git a/Example/Messaging/Tests/Info.plist b/Example/Messaging/Tests/Info.plist index ba72822e872..4df9372a691 100644 --- a/Example/Messaging/Tests/Info.plist +++ b/Example/Messaging/Tests/Info.plist @@ -20,5 +20,7 @@ ???? CFBundleVersion 1 + FirebaseMessagingAutoInitEnabled + diff --git a/Example/Podfile b/Example/Podfile index 9e44d40e284..dac03cf8937 100644 --- a/Example/Podfile +++ b/Example/Podfile @@ -8,7 +8,7 @@ target 'Core_Example_iOS' do # The next line is the forcing function for the Firebase pod. The Firebase # version's subspecs should depend on the component versions in their # corresponding podspec's. - pod 'Firebase/Core', '4.8.0' + pod 'Firebase/Core', '4.9.0' target 'Core_Tests_iOS' do inherit! :search_paths diff --git a/Example/Shared/FIRSampleAppUtilities.m b/Example/Shared/FIRSampleAppUtilities.m index 7a7ce3be352..d161cb99346 100644 --- a/Example/Shared/FIRSampleAppUtilities.m +++ b/Example/Shared/FIRSampleAppUtilities.m @@ -88,7 +88,6 @@ + (void)presentAlertForInvalidServiceInfoPlistFromViewController: handler:^(UIAlertAction *_Nonnull action) { NSURL *githubURL = [NSURL URLWithString:kGithubRepoURLString]; [FIRSampleAppUtilities navigateToURL:githubURL fromViewController:viewController]; - }]; [alertController addAction:viewReadmeAction]; diff --git a/Example/Storage/Tests/Integration/FIRStorageIntegrationTests.m b/Example/Storage/Tests/Integration/FIRStorageIntegrationTests.m index 703c2c62a4d..8e826c9ab15 100644 --- a/Example/Storage/Tests/Integration/FIRStorageIntegrationTests.m +++ b/Example/Storage/Tests/Integration/FIRStorageIntegrationTests.m @@ -33,7 +33,7 @@ * A sample configuration may look like: * * service firebase.storage { - * match /b/{YOUR_PROJECT_ID}.appspot.com/o { + * match /b/{bucket}/o { * ... * match /ios { * match /public/{allPaths=**} { diff --git a/Example/tvOSSample/tvOSSample/AppDelegate.swift b/Example/tvOSSample/tvOSSample/AppDelegate.swift index 9a0d05278b7..723a3c4122f 100644 --- a/Example/tvOSSample/tvOSSample/AppDelegate.swift +++ b/Example/tvOSSample/tvOSSample/AppDelegate.swift @@ -17,7 +17,6 @@ import FirebaseCore @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { - var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { @@ -26,4 +25,3 @@ class AppDelegate: UIResponder, UIApplicationDelegate { return true } } - diff --git a/Example/tvOSSample/tvOSSample/AuthLoginViewController.swift b/Example/tvOSSample/tvOSSample/AuthLoginViewController.swift index dcf72d4eb24..65e0316e048 100644 --- a/Example/tvOSSample/tvOSSample/AuthLoginViewController.swift +++ b/Example/tvOSSample/tvOSSample/AuthLoginViewController.swift @@ -16,25 +16,14 @@ import UIKit import FirebaseAuth class AuthLoginViewController: UIViewController { + override func viewDidLoad() { + super.viewDidLoad() - override func viewDidLoad() { - super.viewDidLoad() + // Do any additional setup after loading the view. + } - // Do any additional setup after loading the view. - } - - override func didReceiveMemoryWarning() { - super.didReceiveMemoryWarning() - // Dispose of any resources that can be recreated. - } - - /* - // MARK: - Navigation - - // In a storyboard-based application, you will often want to do a little preparation before navigation - override func prepare(for segue: UIStoryboardSegue, sender: Any?) { - // Get the new view controller using segue.destinationViewController. - // Pass the selected object to the new view controller. - } - */ + override func didReceiveMemoryWarning() { + super.didReceiveMemoryWarning() + // Dispose of any resources that can be recreated. + } } diff --git a/Example/tvOSSample/tvOSSample/AuthViewController.swift b/Example/tvOSSample/tvOSSample/AuthViewController.swift index 72351d31540..56276ed353b 100644 --- a/Example/tvOSSample/tvOSSample/AuthViewController.swift +++ b/Example/tvOSSample/tvOSSample/AuthViewController.swift @@ -20,13 +20,13 @@ class AuthViewController: UIViewController { // MARK: - User Interface /// A stackview containing all of the buttons to providers (Email, OAuth, etc). - @IBOutlet weak var providers: UIStackView! + @IBOutlet var providers: UIStackView! /// A stackview containing a signed in label and sign out button. - @IBOutlet weak var signedIn: UIStackView! + @IBOutlet var signedIn: UIStackView! /// A label to display the status for the signed in user. - @IBOutlet weak var signInStatus: UILabel! + @IBOutlet var signInStatus: UILabel! // MARK: - User Actions diff --git a/Example/tvOSSample/tvOSSample/DatabaseViewController.swift b/Example/tvOSSample/tvOSSample/DatabaseViewController.swift index 712c48f3954..2b710fa1645 100644 --- a/Example/tvOSSample/tvOSSample/DatabaseViewController.swift +++ b/Example/tvOSSample/tvOSSample/DatabaseViewController.swift @@ -30,7 +30,7 @@ class DatabaseViewController: UIViewController { // MARK: - Interface /// Label to display the current value. - @IBOutlet weak var currentValue: UILabel! + @IBOutlet var currentValue: UILabel! // MARK: - User Actions @@ -65,7 +65,7 @@ class DatabaseViewController: UIViewController { // Observe the current value, and update the UI every time it changes. let ref = Database.database().reference(withPath: Constants.databasePath) - ref.observe(.value) { [weak self] (snapshot) in + ref.observe(.value) { [weak self] snapshot in guard let value = snapshot.value as? Int else { print("Error grabbing value from Snapshot!") return diff --git a/Example/tvOSSample/tvOSSample/EmailLoginViewController.swift b/Example/tvOSSample/tvOSSample/EmailLoginViewController.swift index 60dfc43bfb1..9bea765c686 100644 --- a/Example/tvOSSample/tvOSSample/EmailLoginViewController.swift +++ b/Example/tvOSSample/tvOSSample/EmailLoginViewController.swift @@ -19,6 +19,7 @@ protocol EmailLoginDelegate { func emailLogin(_ controller: EmailLoginViewController, signedInAs user: User) func emailLogin(_ controller: EmailLoginViewController, failedWithError error: Error) } + class EmailLoginViewController: UIViewController { // MARK: - Public Properties @@ -27,15 +28,15 @@ class EmailLoginViewController: UIViewController { // MARK: - User Interface - @IBOutlet private weak var emailAddress: UITextField! - @IBOutlet private weak var password: UITextField! + @IBOutlet private var emailAddress: UITextField! + @IBOutlet private var password: UITextField! // MARK: - User Actions @IBAction func logInButtonHit(_ sender: UIButton) { guard let (email, password) = validatedInputs() else { return } - Auth.auth().signIn(withEmail: email, password: password) { [unowned self] (user, error) in + Auth.auth().signIn(withEmail: email, password: password) { [unowned self] user, error in guard let user = user else { print("Error signing in: \(error!)") self.delegate?.emailLogin(self, failedWithError: error!) @@ -50,7 +51,7 @@ class EmailLoginViewController: UIViewController { @IBAction func signUpButtonHit(_ sender: UIButton) { guard let (email, password) = validatedInputs() else { return } - Auth.auth().createUser(withEmail: email, password: password) { [unowned self] (user, error) in + Auth.auth().createUser(withEmail: email, password: password) { [unowned self] user, error in guard let user = user else { print("Error signing up: \(error!)") self.delegate?.emailLogin(self, failedWithError: error!) diff --git a/Example/tvOSSample/tvOSSample/StorageViewController.swift b/Example/tvOSSample/tvOSSample/StorageViewController.swift index 4416649c94c..2e91d92ae01 100644 --- a/Example/tvOSSample/tvOSSample/StorageViewController.swift +++ b/Example/tvOSSample/tvOSSample/StorageViewController.swift @@ -31,7 +31,7 @@ class StorageViewController: UIViewController { case failed(String) /// Equatable support for UIState. - static func ==(lhs: StorageViewController.UIState, rhs: StorageViewController.UIState) -> Bool { + static func == (lhs: StorageViewController.UIState, rhs: StorageViewController.UIState) -> Bool { switch (lhs, rhs) { case (.cleared, .cleared): return true case (.downloading, .downloading): return true @@ -52,16 +52,16 @@ class StorageViewController: UIViewController { // MARK: Interface /// Image view to display the downloaded image. - @IBOutlet weak var imageView: UIImageView! + @IBOutlet var imageView: UIImageView! /// The download button. - @IBOutlet weak var downloadButton: UIButton! + @IBOutlet var downloadButton: UIButton! /// The clear button. - @IBOutlet weak var clearButton: UIButton! + @IBOutlet var clearButton: UIButton! /// A visual representation of the state. - @IBOutlet weak var stateLabel: UILabel! + @IBOutlet var stateLabel: UILabel! // MARK: - User Actions @@ -72,7 +72,7 @@ class StorageViewController: UIViewController { let storage = Storage.storage() let ref = storage.reference(withPath: Constants.downloadPath) // TODO: Show progress bar here using proper API. - let task = ref.getData(maxSize: Constants.maxSize) { [unowned self] (data, error) in + let task = ref.getData(maxSize: Constants.maxSize) { [unowned self] data, error in guard let data = data else { self.state = .failed("Error downloading: \(error!.localizedDescription)") return @@ -114,7 +114,7 @@ class StorageViewController: UIViewController { stateLabel.text = "State: Downloading..." // Download complete, ensure the download button is still off and enable the clear button. - case (_, .downloaded(let image)): + case let (_, .downloaded(image)): imageView.image = image stateLabel.text = "State: Image downloaded!" @@ -124,7 +124,7 @@ class StorageViewController: UIViewController { stateLabel.text = "State: Pending download" // An error occurred. - case (_, .failed(let error)): + case let (_, .failed(error)): stateLabel.text = "State: \(error)" // For now, as the default, throw a fatal error because it's an unexpected state. This will diff --git a/Firebase/Auth/CHANGELOG.md b/Firebase/Auth/CHANGELOG.md index 176ae3b2bf7..5b53fc367f3 100644 --- a/Firebase/Auth/CHANGELOG.md +++ b/Firebase/Auth/CHANGELOG.md @@ -1,4 +1,13 @@ -# v4.4.1 +# v4.4.4 +- Addresses CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF warnings that surface in newer versions of + Xcode and CocoaPods. +- Improves FIRUser documentation with clear message explaining when Firebase Auth attempts to validate + users and what happens when an invalidated user is detected (#694) . + +# v4.4.3 +- Adds an explicit dependency on CoreGraphics from Firebase Auth. + +# v4.4.2 - Fixes bug where the FIRAuthResult object returned following a Phone Number authentication always contained a nil FIRAdditionalUserInfo object. Now the FIRAdditionalUserInfo object is never nil and its newUser field is populated correctly. diff --git a/Firebase/Auth/FirebaseAuth.podspec b/Firebase/Auth/FirebaseAuth.podspec index 8823238b894..8211bb041a3 100644 --- a/Firebase/Auth/FirebaseAuth.podspec +++ b/Firebase/Auth/FirebaseAuth.podspec @@ -45,6 +45,7 @@ Simplify your iOS development, grow your user base, and monetize more effectivel '$(inherited) ' + 'FIRAuth_VERSION=' + s.version.to_s + ' FIRAuth_MINOR_VERSION=' + s.version.to_s.split(".")[0] + "." + s.version.to_s.split(".")[1] } + s.framework = 'CoreGraphics' s.framework = 'SafariServices' s.framework = 'Security' # s.dependency 'FirebaseCommunity/Core' diff --git a/Firebase/Auth/Source/AuthProviders/Phone/FIRPhoneAuthProvider.m b/Firebase/Auth/Source/AuthProviders/Phone/FIRPhoneAuthProvider.m index 4a0120bde7c..a44a3403e59 100644 --- a/Firebase/Auth/Source/AuthProviders/Phone/FIRPhoneAuthProvider.m +++ b/Firebase/Auth/Source/AuthProviders/Phone/FIRPhoneAuthProvider.m @@ -166,11 +166,11 @@ - (void)verifyPhoneNumber:(NSString *)phoneNumber FIRAuthURLCallbackMatcher callbackMatcher = ^BOOL(NSURL *_Nullable callbackURL) { return [self isVerifyAppURL:callbackURL eventID:eventID]; }; - [_auth.authURLPresenter presentURL:reCAPTCHAURL - UIDelegate:UIDelegate - callbackMatcher:callbackMatcher - completion:^(NSURL *_Nullable callbackURL, - NSError *_Nullable error) { + [self->_auth.authURLPresenter presentURL:reCAPTCHAURL + UIDelegate:UIDelegate + callbackMatcher:callbackMatcher + completion:^(NSURL *_Nullable callbackURL, + NSError *_Nullable error) { if (error) { callBackOnMainThread(nil, error); return; @@ -185,7 +185,8 @@ - (void)verifyPhoneNumber:(NSString *)phoneNumber [[FIRSendVerificationCodeRequest alloc] initWithPhoneNumber:phoneNumber appCredential:nil reCAPTCHAToken:reCAPTCHAToken - requestConfiguration:_auth.requestConfiguration]; + requestConfiguration: + self->_auth.requestConfiguration]; [FIRAuthBackend sendVerificationCode:request callback:^(FIRSendVerificationCodeResponse *_Nullable response, NSError *_Nullable error) { @@ -361,14 +362,15 @@ - (void)verifyClientAndSendVerificationCodeToPhoneNumber:(NSString *)phoneNumber [[FIRSendVerificationCodeRequest alloc] initWithPhoneNumber:phoneNumber appCredential:appCredential reCAPTCHAToken:nil - requestConfiguration:_auth.requestConfiguration]; + requestConfiguration: + self->_auth.requestConfiguration]; [FIRAuthBackend sendVerificationCode:request callback:^(FIRSendVerificationCodeResponse *_Nullable response, NSError *_Nullable error) { if (error) { if (error.code == FIRAuthErrorCodeInvalidAppCredential) { if (retryOnInvalidAppCredential) { - [_auth.appCredentialManager clearCredential]; + [self->_auth.appCredentialManager clearCredential]; [self verifyClientAndSendVerificationCodeToPhoneNumber:phoneNumber retryOnInvalidAppCredential:NO callback:callback]; @@ -404,7 +406,7 @@ - (void)verifyClientWithCompletion:(FIRVerifyClientCallback)completion { FIRVerifyClientRequest *request = [[FIRVerifyClientRequest alloc] initWithAppToken:token.string isSandbox:token.type == FIRAuthAPNSTokenTypeSandbox - requestConfiguration:_auth.requestConfiguration]; + requestConfiguration:self->_auth.requestConfiguration]; [FIRAuthBackend verifyClient:request callback:^(FIRVerifyClientResponse *_Nullable response, NSError *_Nullable error) { if (error) { @@ -412,7 +414,7 @@ - (void)verifyClientWithCompletion:(FIRVerifyClientCallback)completion { return; } NSTimeInterval timeout = [response.suggestedTimeOutDate timeIntervalSinceNow]; - [_auth.appCredentialManager + [self->_auth.appCredentialManager didStartVerificationWithReceipt:response.receipt timeout:timeout callback:^(FIRAuthAppCredential *credential) { @@ -442,8 +444,8 @@ - (void)reCAPTCHAURLWithEventID:(NSString *)eventID completion:(FIRReCAPTCHAURLC return; } NSString *bundleID = [NSBundle mainBundle].bundleIdentifier; - NSString *clienID = _auth.app.options.clientID; - NSString *apiKey = _auth.requestConfiguration.APIKey; + NSString *clienID = self->_auth.app.options.clientID; + NSString *apiKey = self->_auth.requestConfiguration.APIKey; NSMutableDictionary *urlArguments = [[NSMutableDictionary alloc] initWithDictionary: @{ @"apiKey" : apiKey, @"authType" : kAuthTypeVerifyApp, @@ -452,8 +454,8 @@ - (void)reCAPTCHAURLWithEventID:(NSString *)eventID completion:(FIRReCAPTCHAURLC @"v" : [FIRAuthBackend authUserAgent], @"eventId" : eventID, }]; - if (_auth.requestConfiguration.languageCode) { - urlArguments[@"hl"] = _auth.requestConfiguration.languageCode; + if (self->_auth.requestConfiguration.languageCode) { + urlArguments[@"hl"] = self->_auth.requestConfiguration.languageCode; } NSString *argumentsString = [urlArguments gtm_httpArgumentsString]; NSString *URLString = diff --git a/Firebase/Auth/Source/FIRAuth.m b/Firebase/Auth/Source/FIRAuth.m index ebca6c8f5bc..0f3705f9b5e 100644 --- a/Firebase/Auth/Source/FIRAuth.m +++ b/Firebase/Auth/Source/FIRAuth.m @@ -436,7 +436,7 @@ - (instancetype)initWithAPIKey:(NSString *)APIKey appName:(NSString *)appName { NSError *error; if ([strongSelf getUser:&user error:&error]) { [strongSelf updateCurrentUser:user byForce:NO savingToDisk:NO error:&error]; - _lastNotifiedUserToken = user.rawAccessToken; + self->_lastNotifiedUserToken = user.rawAccessToken; } else { FIRLogError(kFIRLoggerAuth, @"I-AUT000001", @"Error loading saved user when starting up: %@", error); @@ -486,7 +486,7 @@ - (void)dealloc { - (FIRUser *)currentUser { __block FIRUser *result; dispatch_sync(FIRAuthGlobalWorkQueue(), ^{ - result = _currentUser; + result = self->_currentUser; }); return result; } @@ -497,7 +497,7 @@ - (void)fetchProvidersForEmail:(NSString *)email FIRCreateAuthURIRequest *request = [[FIRCreateAuthURIRequest alloc] initWithIdentifier:email continueURI:@"http://www.google.com/" - requestConfiguration:_requestConfiguration]; + requestConfiguration:self->_requestConfiguration]; [FIRAuthBackend createAuthURI:request callback:^(FIRCreateAuthURIResponse *_Nullable response, NSError *_Nullable error) { if (completion) { @@ -743,14 +743,14 @@ - (void)signInAnonymouslyAndRetrieveDataWithCompletion:(FIRAuthDataResultCallbac dispatch_async(FIRAuthGlobalWorkQueue(), ^{ FIRAuthDataResultCallback decoratedCallback = [self signInFlowAuthDataResultCallbackByDecoratingCallback:completion]; - if (_currentUser.anonymous) { + if (self->_currentUser.anonymous) { FIRAdditionalUserInfo *additionalUserInfo = [[FIRAdditionalUserInfo alloc] initWithProviderID:nil profile:nil username:nil isNewUser:NO]; FIRAuthDataResult *authDataResult = - [[FIRAuthDataResult alloc] initWithUser:_currentUser + [[FIRAuthDataResult alloc] initWithUser:self->_currentUser additionalUserInfo:additionalUserInfo]; decoratedCallback(authDataResult, nil); return; @@ -784,8 +784,8 @@ - (void)signInAnonymouslyWithCompletion:(FIRAuthResultCallback)completion { dispatch_async(FIRAuthGlobalWorkQueue(), ^{ FIRAuthResultCallback decoratedCallback = [self signInFlowAuthResultCallbackByDecoratingCallback:completion]; - if (_currentUser.anonymous) { - decoratedCallback(_currentUser, nil); + if (self->_currentUser.anonymous) { + decoratedCallback(self->_currentUser, nil); return; } [self internalSignInAnonymouslyWithCompletion:^(FIRSignUpNewUserResponse *_Nullable response, @@ -889,7 +889,7 @@ - (void)confirmPasswordResetWithCode:(NSString *)code FIRResetPasswordRequest *request = [[FIRResetPasswordRequest alloc] initWithOobCode:code newPassword:newPassword - requestConfiguration:_requestConfiguration]; + requestConfiguration:self->_requestConfiguration]; [FIRAuthBackend resetPassword:request callback:^(FIRResetPasswordResponse *_Nullable response, NSError *_Nullable error) { if (completion) { @@ -910,7 +910,7 @@ - (void)checkActionCode:(NSString *)code completion:(FIRCheckActionCodeCallBack) FIRResetPasswordRequest *request = [[FIRResetPasswordRequest alloc] initWithOobCode:code newPassword:nil - requestConfiguration:_requestConfiguration]; + requestConfiguration:self->_requestConfiguration]; [FIRAuthBackend resetPassword:request callback:^(FIRResetPasswordResponse *_Nullable response, NSError *_Nullable error) { if (completion) { @@ -951,7 +951,7 @@ - (void)verifyPasswordResetCode:(NSString *)code - (void)applyActionCode:(NSString *)code completion:(FIRApplyActionCodeCallback)completion { dispatch_async(FIRAuthGlobalWorkQueue(), ^ { FIRSetAccountInfoRequest *request = - [[FIRSetAccountInfoRequest alloc] initWithRequestConfiguration:_requestConfiguration]; + [[FIRSetAccountInfoRequest alloc] initWithRequestConfiguration:self->_requestConfiguration]; request.OOBCode = code; [FIRAuthBackend setAccountInfo:request callback:^(FIRSetAccountInfoResponse *_Nullable response, NSError *_Nullable error) { @@ -999,7 +999,8 @@ - (void)sendPasswordResetWithNullableActionCodeSettings:(nullable FIRActionCodeS FIRGetOOBConfirmationCodeRequest *request = [FIRGetOOBConfirmationCodeRequest passwordResetRequestWithEmail:email actionCodeSettings:actionCodeSettings - requestConfiguration:_requestConfiguration]; + requestConfiguration:self->_requestConfiguration + ]; [FIRAuthBackend getOOBConfirmationCode:request callback:^(FIRGetOOBConfirmationCodeResponse *_Nullable response, NSError *_Nullable error) { @@ -1015,7 +1016,7 @@ - (void)sendPasswordResetWithNullableActionCodeSettings:(nullable FIRActionCodeS - (BOOL)signOut:(NSError *_Nullable __autoreleasing *_Nullable)error { __block BOOL result = YES; dispatch_sync(FIRAuthGlobalWorkQueue(), ^{ - if (!_currentUser) { + if (!self->_currentUser) { return; } result = [self updateCurrentUser:nil byForce:NO savingToDisk:YES error:error]; @@ -1068,7 +1069,7 @@ - (FIRIDTokenDidChangeListenerHandle)addIDTokenDidChangeListener: [_listenerHandles addObject:handle]; } dispatch_async(dispatch_get_main_queue(), ^{ - listener(self, self.currentUser); + listener(self, self->_currentUser); }); return handle; } @@ -1082,7 +1083,8 @@ - (void)removeIDTokenDidChangeListener:(FIRIDTokenDidChangeListenerHandle)listen - (void)useAppLanguage { dispatch_sync(FIRAuthGlobalWorkQueue(), ^{ - _requestConfiguration.languageCode = [NSBundle mainBundle].preferredLocalizations.firstObject; + self->_requestConfiguration.languageCode = + [NSBundle mainBundle].preferredLocalizations.firstObject; }); } @@ -1092,17 +1094,17 @@ - (nullable NSString *)languageCode { - (void)setLanguageCode:(nullable NSString *)languageCode { dispatch_sync(FIRAuthGlobalWorkQueue(), ^{ - _requestConfiguration.languageCode = [languageCode copy]; + self->_requestConfiguration.languageCode = [languageCode copy]; }); } - (NSString *)additionalFrameworkMarker { - return _requestConfiguration.additionalFrameworkMarker; + return self->_requestConfiguration.additionalFrameworkMarker; } - (void)setAdditionalFrameworkMarker:(NSString *)additionalFrameworkMarker { dispatch_sync(FIRAuthGlobalWorkQueue(), ^{ - _requestConfiguration.additionalFrameworkMarker = [additionalFrameworkMarker copy]; + self->_requestConfiguration.additionalFrameworkMarker = [additionalFrameworkMarker copy]; }); } @@ -1110,7 +1112,7 @@ - (void)setAdditionalFrameworkMarker:(NSString *)additionalFrameworkMarker { - (NSData *)APNSToken { __block NSData *result = nil; dispatch_sync(FIRAuthGlobalWorkQueue(), ^{ - result = _tokenManager.token.data; + result = self->_tokenManager.token.data; }); return result; } @@ -1121,20 +1123,20 @@ - (void)setAPNSToken:(NSData *)APNSToken { - (void)setAPNSToken:(NSData *)token type:(FIRAuthAPNSTokenType)type { dispatch_sync(FIRAuthGlobalWorkQueue(), ^{ - _tokenManager.token = [[FIRAuthAPNSToken alloc] initWithData:token type:type]; + self->_tokenManager.token = [[FIRAuthAPNSToken alloc] initWithData:token type:type]; }); } - (void)handleAPNSTokenError:(NSError *)error { dispatch_sync(FIRAuthGlobalWorkQueue(), ^{ - [_tokenManager cancelWithError:error]; + [self->_tokenManager cancelWithError:error]; }); } - (BOOL)canHandleNotification:(NSDictionary *)userInfo { __block BOOL result = NO; dispatch_sync(FIRAuthGlobalWorkQueue(), ^{ - result = [_notificationManager canHandleNotification:userInfo]; + result = [self->_notificationManager canHandleNotification:userInfo]; }); return result; } @@ -1142,7 +1144,7 @@ - (BOOL)canHandleNotification:(NSDictionary *)userInfo { - (BOOL)canHandleURL:(NSURL *)URL { __block BOOL result = NO; dispatch_sync(FIRAuthGlobalWorkQueue(), ^{ - result = [_authURLPresenter canHandleURL:URL]; + result = [self->_authURLPresenter canHandleURL:URL]; }); return result; } diff --git a/Firebase/Auth/Source/FIRAuthAPNSTokenManager.m b/Firebase/Auth/Source/FIRAuthAPNSTokenManager.m index 215a391cd03..2b39aefa4f9 100644 --- a/Firebase/Auth/Source/FIRAuthAPNSTokenManager.m +++ b/Firebase/Auth/Source/FIRAuthAPNSTokenManager.m @@ -68,13 +68,13 @@ - (void)getTokenWithCallback:(FIRAuthAPNSTokenCallback)callback { _pendingCallbacks = [[NSMutableArray alloc] initWithObjects:callback, nil]; dispatch_async(dispatch_get_main_queue(), ^{ - if ([_application respondsToSelector:@selector(registerForRemoteNotifications)]) { - [_application registerForRemoteNotifications]; + if ([self->_application respondsToSelector:@selector(registerForRemoteNotifications)]) { + [self->_application registerForRemoteNotifications]; } else { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" #if TARGET_OS_IOS - [_application registerForRemoteNotificationTypes:UIRemoteNotificationTypeAlert]; + [self->_application registerForRemoteNotificationTypes:UIRemoteNotificationTypeAlert]; #endif // TARGET_OS_IOS #pragma clang diagnostic pop } @@ -83,7 +83,7 @@ - (void)getTokenWithCallback:(FIRAuthAPNSTokenCallback)callback { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_timeout * NSEC_PER_SEC)), FIRAuthGlobalWorkQueue(), ^{ // Only cancel if the pending callbacks remain the same, i.e., not triggered yet. - if (applicableCallbacks == _pendingCallbacks) { + if (applicableCallbacks == self->_pendingCallbacks) { [self callBackWithToken:nil error:nil]; } }); diff --git a/Firebase/Auth/Source/FIRAuthNotificationManager.m b/Firebase/Auth/Source/FIRAuthNotificationManager.m index b1dd34ce598..624de1067ed 100644 --- a/Firebase/Auth/Source/FIRAuthNotificationManager.m +++ b/Firebase/Auth/Source/FIRAuthNotificationManager.m @@ -104,14 +104,14 @@ - (void)checkNotificationForwardingWithCallback:(FIRAuthNotificationForwardingCa kNotificationProberKey : @"This fake notification should be forwarded to Firebase Auth." } }; - if ([_application.delegate respondsToSelector: + if ([self->_application.delegate respondsToSelector: @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:)]) { - [_application.delegate application:_application + [self->_application.delegate application:self->_application didReceiveRemoteNotification:proberNotification fetchCompletionHandler:^(UIBackgroundFetchResult result) {}]; - } else if ([_application.delegate respondsToSelector: + } else if ([self->_application.delegate respondsToSelector: @selector(application:didReceiveRemoteNotification:)]) { - [_application.delegate application:_application + [self->_application.delegate application:self->_application didReceiveRemoteNotification:proberNotification]; } else { FIRLogWarning(kFIRLoggerAuth, @"I-AUT000015", diff --git a/Firebase/Auth/Source/FIRAuthSerialTaskQueue.m b/Firebase/Auth/Source/FIRAuthSerialTaskQueue.m index 3be0f54577d..edceeecf99e 100644 --- a/Firebase/Auth/Source/FIRAuthSerialTaskQueue.m +++ b/Firebase/Auth/Source/FIRAuthSerialTaskQueue.m @@ -37,14 +37,14 @@ - (instancetype)init { - (void)enqueueTask:(FIRAuthSerialTask)task { // This dispatch queue will run tasks serially in FIFO order, as long as it's not suspended. - dispatch_async(_dispatchQueue, ^{ + dispatch_async(self->_dispatchQueue, ^{ // But as soon as a task is started, stop other tasks from running until the task calls it's // completion handler, which allows the queue to resume processing of tasks. This allows the // task to perform other asyncronous actions on other dispatch queues and "get back to us" when // all of their sub-tasks are complete. - dispatch_suspend(_dispatchQueue); + dispatch_suspend(self->_dispatchQueue); task(^{ - dispatch_resume(_dispatchQueue); + dispatch_resume(self->_dispatchQueue); }); }); } diff --git a/Firebase/Auth/Source/FIRAuthURLPresenter.m b/Firebase/Auth/Source/FIRAuthURLPresenter.m index 5526a853e23..d8e359367c8 100644 --- a/Firebase/Auth/Source/FIRAuthURLPresenter.m +++ b/Firebase/Auth/Source/FIRAuthURLPresenter.m @@ -81,17 +81,19 @@ - (void)presentURL:(NSURL *)URL _callbackMatcher = callbackMatcher; _completion = completion; dispatch_async(dispatch_get_main_queue(), ^() { - _UIDelegate = UIDelegate ?: [FIRAuthDefaultUIDelegate defaultUIDelegate]; + self->_UIDelegate = UIDelegate ?: [FIRAuthDefaultUIDelegate defaultUIDelegate]; if ([SFSafariViewController class]) { - _safariViewController = [[SFSafariViewController alloc] initWithURL:URL]; - _safariViewController.delegate = self; - [_UIDelegate presentViewController:_safariViewController animated:YES completion:nil]; + self->_safariViewController = [[SFSafariViewController alloc] initWithURL:URL]; + self->_safariViewController.delegate = self; + [self->_UIDelegate presentViewController:self->_safariViewController + animated:YES + completion:nil]; return; } else { - _webViewController = [[FIRAuthWebViewController alloc] initWithURL:URL delegate:self]; + self->_webViewController = [[FIRAuthWebViewController alloc] initWithURL:URL delegate:self]; UINavigationController *navController = - [[UINavigationController alloc] initWithRootViewController:_webViewController]; - [_UIDelegate presentViewController:navController animated:YES completion:nil]; + [[UINavigationController alloc] initWithRootViewController:self->_webViewController]; + [self->_UIDelegate presentViewController:navController animated:YES completion:nil]; } }); } @@ -108,8 +110,8 @@ - (BOOL)canHandleURL:(NSURL *)URL { - (void)safariViewControllerDidFinish:(SFSafariViewController *)controller { dispatch_async(FIRAuthGlobalWorkQueue(), ^() { - if (controller == _safariViewController) { - _safariViewController = nil; + if (controller == self->_safariViewController) { + self->_safariViewController = nil; //TODO:Ensure that the SFSafariViewController is actually removed from the screen before //invoking finishPresentationWithURL:error: [self finishPresentationWithURL:nil @@ -123,7 +125,7 @@ - (void)safariViewControllerDidFinish:(SFSafariViewController *)controller { - (BOOL)webViewController:(FIRAuthWebViewController *)webViewController canHandleURL:(NSURL *)URL { __block BOOL result = NO; dispatch_sync(FIRAuthGlobalWorkQueue(), ^() { - if (webViewController == _webViewController) { + if (webViewController == self->_webViewController) { result = [self canHandleURL:URL]; } }); @@ -132,7 +134,7 @@ - (BOOL)webViewController:(FIRAuthWebViewController *)webViewController canHandl - (void)webViewControllerDidCancel:(FIRAuthWebViewController *)webViewController { dispatch_async(FIRAuthGlobalWorkQueue(), ^() { - if (webViewController == _webViewController) { + if (webViewController == self->_webViewController) { [self finishPresentationWithURL:nil error:[FIRAuthErrorUtils webContextCancelledErrorWithMessage:nil]]; } @@ -142,7 +144,7 @@ - (void)webViewControllerDidCancel:(FIRAuthWebViewController *)webViewController - (void)webViewController:(FIRAuthWebViewController *)webViewController didFailWithError:(NSError *)error { dispatch_async(FIRAuthGlobalWorkQueue(), ^() { - if (webViewController == _webViewController) { + if (webViewController == self->_webViewController) { [self finishPresentationWithURL:nil error:error]; } }); @@ -163,7 +165,7 @@ - (void)finishPresentationWithURL:(nullable NSURL *)URL FIRAuthURLPresentationCompletion completion = _completion; _completion = nil; void (^finishBlock)(void) = ^() { - _isPresenting = NO; + self->_isPresenting = NO; completion(URL, error); }; SFSafariViewController *safariViewController = _safariViewController; diff --git a/Firebase/Auth/Source/FIRSecureTokenService.m b/Firebase/Auth/Source/FIRSecureTokenService.m index 8e37a058867..69434ff063e 100644 --- a/Firebase/Auth/Source/FIRSecureTokenService.m +++ b/Firebase/Auth/Source/FIRSecureTokenService.m @@ -107,7 +107,7 @@ - (void)fetchAccessTokenForcingRefresh:(BOOL)forceRefresh [_taskQueue enqueueTask:^(FIRAuthSerialTaskCompletionBlock complete) { if (!forceRefresh && [self hasValidAccessToken]) { complete(); - callback(_accessToken, nil, NO); + callback(self->_accessToken, nil, NO); } else { [self requestAccessToken:^(NSString *_Nullable token, NSError *_Nullable error, @@ -184,14 +184,15 @@ - (void)requestAccessToken:(FIRFetchAccessTokenCallback)callback { NSError *_Nullable error) { BOOL tokenUpdated = NO; NSString *newAccessToken = response.accessToken; - if (newAccessToken.length && ![newAccessToken isEqualToString:_accessToken]) { - _accessToken = [newAccessToken copy]; - _accessTokenExpirationDate = response.approximateExpirationDate; + if (newAccessToken.length && ![newAccessToken isEqualToString:self->_accessToken]) { + self->_accessToken = [newAccessToken copy]; + self->_accessTokenExpirationDate = response.approximateExpirationDate; tokenUpdated = YES; } NSString *newRefreshToken = response.refreshToken; - if (newRefreshToken.length && ![newRefreshToken isEqualToString:_refreshToken]) { - _refreshToken = [newRefreshToken copy]; + if (newRefreshToken.length && + ![newRefreshToken isEqualToString:self->_refreshToken]) { + self->_refreshToken = [newRefreshToken copy]; tokenUpdated = YES; } callback(newAccessToken, error, tokenUpdated); diff --git a/Firebase/Auth/Source/FIRUser.m b/Firebase/Auth/Source/FIRUser.m index 7f2316bc542..a77db75f630 100644 --- a/Firebase/Auth/Source/FIRUser.m +++ b/Firebase/Auth/Source/FIRUser.m @@ -383,7 +383,7 @@ - (void)getAccountInfoRefreshingCache:(void(^)(FIRGetAccountInfoResponseUser *_N } FIRGetAccountInfoRequest *getAccountInfoRequest = [[FIRGetAccountInfoRequest alloc] initWithAccessToken:accessToken - requestConfiguration:_auth.requestConfiguration]; + requestConfiguration:self->_auth.requestConfiguration]; [FIRAuthBackend getAccountInfo:getAccountInfoRequest callback:^(FIRGetAccountInfoResponse *_Nullable response, NSError *_Nullable error) { @@ -451,7 +451,7 @@ - (void)executeUserUpdateWithChanges:(void(^)(FIRGetAccountInfoResponseUser *, callback(error); return; } - FIRAuthRequestConfiguration *configuration = _auth.requestConfiguration; + FIRAuthRequestConfiguration *configuration = self->_auth.requestConfiguration; // Mutate setAccountInfoRequest in block: FIRSetAccountInfoRequest *setAccountInfoRequest = [[FIRSetAccountInfoRequest alloc] initWithRequestConfiguration:configuration]; @@ -513,7 +513,7 @@ - (void)setTokenService:(FIRSecureTokenService *)tokenService callback(error); return; } - _tokenService = tokenService; + self->_tokenService = tokenService; if (![self updateKeychain:&error]) { callback(error); return; @@ -558,11 +558,11 @@ - (void)updateEmail:(nullable NSString *)email return; } if (email) { - _email = email; + self->_email = email; } - if (_email && password) { - _anonymous = NO; - _hasEmailPasswordCredential = YES; + if (self->_email && password) { + self->_anonymous = NO; + self->_hasEmailPasswordCredential = YES; if (!hadEmailPasswordCredential) { // The list of providers need to be updated for the newly added email-password provider. [self internalGetTokenWithCallback:^(NSString *_Nullable accessToken, @@ -571,7 +571,7 @@ - (void)updateEmail:(nullable NSString *)email callback(error); return; } - FIRAuthRequestConfiguration *requestConfiguration = _auth.requestConfiguration; + FIRAuthRequestConfiguration *requestConfiguration = self->_auth.requestConfiguration; FIRGetAccountInfoRequest *getAccountInfoRequest = [[FIRGetAccountInfoRequest alloc] initWithAccessToken:accessToken requestConfiguration:requestConfiguration]; @@ -625,7 +625,7 @@ - (void)updatePassword:(NSString *)password updated. @param phoneAuthCredential The new phone number credential corresponding to the phone number - to be added to the firebaes account, if a phone number is already linked to the account this + to be added to the Firebase account, if a phone number is already linked to the account this new phone number will replace it. @param isLinkOperation Boolean value indicating whether or not this is a link operation. @param completion Optionally; the block invoked when the user profile change has finished. @@ -646,7 +646,7 @@ - (void)internalUpdateOrLinkPhoneNumberCredential:(FIRPhoneAuthCredential *)phon initWithVerificationID:phoneAuthCredential.verificationID verificationCode:phoneAuthCredential.verificationCode operation:operation - requestConfiguration:_auth.requestConfiguration]; + requestConfiguration:self->_auth.requestConfiguration]; request.accessToken = accessToken; [FIRAuthBackend verifyPhoneNumber:request callback:^(FIRVerifyPhoneNumberResponse *_Nullable response, @@ -664,7 +664,7 @@ - (void)internalUpdateOrLinkPhoneNumberCredential:(FIRPhoneAuthCredential *)phon completion(error); return; } - _anonymous = NO; + self->_anonymous = NO; if (![self updateKeychain:&error]) { completion(error); return; @@ -737,10 +737,11 @@ - (void)reauthenticateWithCredential:(FIRAuthCredential *)credential reauthenticateAndRetrieveDataWithCredential:(FIRAuthCredential *) credential completion:(nullable FIRAuthDataResultCallback) completion { dispatch_async(FIRAuthGlobalWorkQueue(), ^{ - [_auth internalSignInAndRetrieveDataWithCredential:credential - isReauthentication:YES - callback:^(FIRAuthDataResult *_Nullable authResult, - NSError *_Nullable error) { + [self->_auth internalSignInAndRetrieveDataWithCredential:credential + isReauthentication:YES + callback:^(FIRAuthDataResult *_Nullable + authResult, + NSError *_Nullable error) { if (error) { // If "user not found" error returned by backend, translate to user mismatch error which is // more accurate. @@ -750,7 +751,7 @@ - (void)reauthenticateWithCredential:(FIRAuthCredential *)credential callInMainThreadWithAuthDataResultAndError(completion, authResult, error); return; } - if (![authResult.user.uid isEqual:[_auth getUID]]) { + if (![authResult.user.uid isEqual:[self->_auth getUID]]) { callInMainThreadWithAuthDataResultAndError(completion, authResult, [FIRAuthErrorUtils userMismatchError]); return; @@ -766,7 +767,7 @@ - (void)reauthenticateWithCredential:(FIRAuthCredential *)credential - (nullable NSString *)refreshToken { __block NSString *result; dispatch_sync(FIRAuthGlobalWorkQueue(), ^{ - result = _tokenService.refreshToken; + result = self->_tokenService.refreshToken; }); return result; } @@ -842,7 +843,7 @@ - (void)linkWithCredential:(FIRAuthCredential *)credential - (void)linkAndRetrieveDataWithCredential:(FIRAuthCredential *)credential completion:(nullable FIRAuthDataResultCallback)completion { dispatch_async(FIRAuthGlobalWorkQueue(), ^{ - if (_providerData[credential.provider]) { + if (self->_providerData[credential.provider]) { callInMainThreadWithAuthDataResultAndError(completion, nil, [FIRAuthErrorUtils providerAlreadyLinkedError]); @@ -851,7 +852,7 @@ - (void)linkAndRetrieveDataWithCredential:(FIRAuthCredential *)credential FIRAuthDataResult *result = [[FIRAuthDataResult alloc] initWithUser:self additionalUserInfo:nil]; if ([credential isKindOfClass:[FIREmailPasswordAuthCredential class]]) { - if (_hasEmailPasswordCredential) { + if (self->_hasEmailPasswordCredential) { callInMainThreadWithAuthDataResultAndError(completion, nil, [FIRAuthErrorUtils providerAlreadyLinkedError]); @@ -887,7 +888,7 @@ - (void)linkAndRetrieveDataWithCredential:(FIRAuthCredential *)credential } #endif - [_taskQueue enqueueTask:^(FIRAuthSerialTaskCompletionBlock _Nonnull complete) { + [self->_taskQueue enqueueTask:^(FIRAuthSerialTaskCompletionBlock _Nonnull complete) { CallbackWithAuthDataResultAndError completeWithError = ^(FIRAuthDataResult *result, NSError *error) { complete(); @@ -899,7 +900,7 @@ - (void)linkAndRetrieveDataWithCredential:(FIRAuthCredential *)credential completeWithError(nil, error); return; } - FIRAuthRequestConfiguration *requestConfiguration = _auth.requestConfiguration; + FIRAuthRequestConfiguration *requestConfiguration = self->_auth.requestConfiguration; FIRVerifyAssertionRequest *request = [[FIRVerifyAssertionRequest alloc] initWithProviderID:credential.provider requestConfiguration:requestConfiguration]; @@ -917,7 +918,7 @@ - (void)linkAndRetrieveDataWithCredential:(FIRAuthCredential *)credential FIRAuthDataResult *result = [[FIRAuthDataResult alloc] initWithUser:self additionalUserInfo:additionalUserInfo]; // Update the new token and refresh user info again. - _tokenService = [[FIRSecureTokenService alloc] + self->_tokenService = [[FIRSecureTokenService alloc] initWithRequestConfiguration:requestConfiguration accessToken:response.IDToken accessTokenExpirationDate:response.approximateExpirationDate @@ -939,7 +940,7 @@ - (void)linkAndRetrieveDataWithCredential:(FIRAuthCredential *)credential completeWithError(nil, error); return; } - _anonymous = NO; + self->_anonymous = NO; [self updateWithGetAccountInfoResponse:response]; if (![self updateKeychain:&error]) { completeWithError(nil, error); @@ -967,19 +968,19 @@ - (void)unlinkFromProvider:(NSString *)provider completeAndCallbackWithError(error); return; } - FIRAuthRequestConfiguration *requestConfiguration = _auth.requestConfiguration; + FIRAuthRequestConfiguration *requestConfiguration = self->_auth.requestConfiguration; FIRSetAccountInfoRequest *setAccountInfoRequest = [[FIRSetAccountInfoRequest alloc] initWithRequestConfiguration:requestConfiguration]; setAccountInfoRequest.accessToken = accessToken; BOOL isEmailPasswordProvider = [provider isEqualToString:FIREmailAuthProviderID]; if (isEmailPasswordProvider) { - if (!_hasEmailPasswordCredential) { + if (!self->_hasEmailPasswordCredential) { completeAndCallbackWithError([FIRAuthErrorUtils noSuchProviderError]); return; } setAccountInfoRequest.deleteAttributes = @[ FIRSetAccountInfoUserAttributePassword ]; } else { - if (!_providerData[provider]) { + if (!self->_providerData[provider]) { completeAndCallbackWithError([FIRAuthErrorUtils noSuchProviderError]); return; } @@ -994,19 +995,19 @@ - (void)unlinkFromProvider:(NSString *)provider return; } if (isEmailPasswordProvider) { - _hasEmailPasswordCredential = NO; + self->_hasEmailPasswordCredential = NO; } else { // We can't just use the provider info objects in FIRSetAcccountInfoResponse because they // don't have localID and email fields. Remove the specific provider manually. - NSMutableDictionary *mutableProviderData = [_providerData mutableCopy]; + NSMutableDictionary *mutableProviderData = [self->_providerData mutableCopy]; [mutableProviderData removeObjectForKey:provider]; - _providerData = [mutableProviderData copy]; + self->_providerData = [mutableProviderData copy]; #if TARGET_OS_IOS // After successfully unlinking a phone auth provider, remove the phone number from the // cached user info. if ([provider isEqualToString:FIRPhoneAuthProviderID]) { - _phoneNumber = nil; + self->_phoneNumber = nil; } #endif } @@ -1060,7 +1061,7 @@ - (void)sendEmailVerificationWithNullableActionCodeSettings:(nullable FIRActionC callInMainThreadWithError(completion, error); return; } - FIRAuthRequestConfiguration *configuration = _auth.requestConfiguration; + FIRAuthRequestConfiguration *configuration = self->_auth.requestConfiguration; FIRGetOOBConfirmationCodeRequest *request = [FIRGetOOBConfirmationCodeRequest verifyEmailRequestWithAccessToken:accessToken actionCodeSettings:actionCodeSettings @@ -1085,15 +1086,15 @@ - (void)deleteWithCompletion:(nullable FIRUserProfileChangeCallback)completion { return; } FIRDeleteAccountRequest *deleteUserRequest = - [[FIRDeleteAccountRequest alloc] initWitLocalID:_userID + [[FIRDeleteAccountRequest alloc] initWitLocalID:self->_userID accessToken:accessToken - requestConfiguration:_auth.requestConfiguration]; + requestConfiguration:self->_auth.requestConfiguration]; [FIRAuthBackend deleteAccount:deleteUserRequest callback:^(NSError *_Nullable error) { if (error) { callInMainThreadWithError(completion, error); return; } - if (![_auth signOutByForceWithUserID:_userID error:&error]) { + if (![self->_auth signOutByForceWithUserID:self->_userID error:&error]) { callInMainThreadWithError(completion, error); return; } @@ -1167,14 +1168,14 @@ - (nullable NSString *)displayName { - (void)setDisplayName:(nullable NSString *)displayName { dispatch_sync(FIRAuthGlobalWorkQueue(), ^{ - if (_consumed) { + if (self->_consumed) { [NSException raise:NSInternalInconsistencyException format:@"%@", @"Invalid call to setDisplayName: after commitChangesWithCallback:."]; return; } - _displayNameSet = YES; - _displayName = [displayName copy]; + self->_displayNameSet = YES; + self->_displayName = [displayName copy]; }); } @@ -1184,14 +1185,14 @@ - (nullable NSURL *)photoURL { - (void)setPhotoURL:(nullable NSURL *)photoURL { dispatch_sync(FIRAuthGlobalWorkQueue(), ^{ - if (_consumed) { + if (self->_consumed) { [NSException raise:NSInternalInconsistencyException format:@"%@", @"Invalid call to setPhotoURL: after commitChangesWithCallback:."]; return; } - _photoURLSet = YES; - _photoURL = [photoURL copy]; + self->_photoURLSet = YES; + self->_photoURL = [photoURL copy]; }); } @@ -1204,24 +1205,24 @@ - (BOOL)hasUpdates { - (void)commitChangesWithCompletion:(nullable FIRUserProfileChangeCallback)completion { dispatch_sync(FIRAuthGlobalWorkQueue(), ^{ - if (_consumed) { + if (self->_consumed) { [NSException raise:NSInternalInconsistencyException format:@"%@", @"commitChangesWithCallback: should only be called once."]; return; } - _consumed = YES; + self->_consumed = YES; // Return fast if there is nothing to update: if (![self hasUpdates]) { callInMainThreadWithError(completion, nil); return; } - NSString *displayName = [_displayName copy]; - BOOL displayNameWasSet = _displayNameSet; - NSURL *photoURL = [_photoURL copy]; - BOOL photoURLWasSet = _photoURLSet; - [_user executeUserUpdateWithChanges:^(FIRGetAccountInfoResponseUser *user, - FIRSetAccountInfoRequest *request) { + NSString *displayName = [self->_displayName copy]; + BOOL displayNameWasSet = self->_displayNameSet; + NSURL *photoURL = [self->_photoURL copy]; + BOOL photoURLWasSet = self->_photoURLSet; + [self->_user executeUserUpdateWithChanges:^(FIRGetAccountInfoResponseUser *user, + FIRSetAccountInfoRequest *request) { if (photoURLWasSet) { request.photoURL = photoURL; } @@ -1235,12 +1236,12 @@ - (void)commitChangesWithCompletion:(nullable FIRUserProfileChangeCallback)compl return; } if (displayNameWasSet) { - [_user setDisplayName:displayName]; + [self->_user setDisplayName:displayName]; } if (photoURLWasSet) { - [_user setPhotoURL:photoURL]; + [self->_user setPhotoURL:photoURL]; } - if (![_user updateKeychain:&error]) { + if (![self->_user updateKeychain:&error]) { callInMainThreadWithError(completion, error); return; } diff --git a/Firebase/Auth/Source/Public/FIREmailAuthProvider.h b/Firebase/Auth/Source/Public/FIREmailAuthProvider.h index 0108d406082..99cd018f0ac 100644 --- a/Firebase/Auth/Source/Public/FIREmailAuthProvider.h +++ b/Firebase/Auth/Source/Public/FIREmailAuthProvider.h @@ -26,7 +26,7 @@ NS_ASSUME_NONNULL_BEGIN extern NSString *const FIREmailAuthProviderID NS_SWIFT_NAME(EmailAuthProviderID); /** - @brief please use `FIREmailAuthProviderID` instead. + @brief Please use `FIREmailAuthProviderID` for Objective-C or `EmailAuthProviderID` for Swift instead. */ extern NSString *const FIREmailPasswordAuthProviderID __attribute__((deprecated)); diff --git a/Firebase/Auth/Source/Public/FIRUser.h b/Firebase/Auth/Source/Public/FIRUser.h index 1bba71057a0..0cf872f0338 100644 --- a/Firebase/Auth/Source/Public/FIRUser.h +++ b/Firebase/Auth/Source/Public/FIRUser.h @@ -56,7 +56,11 @@ typedef void (^FIRSendEmailVerificationCallback)(NSError *_Nullable error) NS_SWIFT_NAME(SendEmailVerificationCallback); /** @class FIRUser - @brief Represents a user. + @brief Represents a user. Firebase Auth does not attempt to validate users + when loading them from the keychain. Invalidated users (such as those + whose passwords have been changed on another client) are automatically + logged out when an auth-dependent operation is attempted or when the + ID token is automatically refreshed. @remarks This class is thread-safe. */ NS_SWIFT_NAME(User) @@ -158,7 +162,7 @@ NS_SWIFT_NAME(User) updated. @param phoneNumberCredential The new phone number credential corresponding to the phone number - to be added to the firebaes account, if a phone number is already linked to the account this + to be added to the Firebase account, if a phone number is already linked to the account this new phone number will replace it. @param completion Optionally; the block invoked when the user profile change has finished. Invoked asynchronously on the main thread in the future. diff --git a/Firebase/Core/CHANGELOG.md b/Firebase/Core/CHANGELOG.md index 18e956d4c6a..4289cae9f5d 100644 --- a/Firebase/Core/CHANGELOG.md +++ b/Firebase/Core/CHANGELOG.md @@ -1,4 +1,9 @@ # Unreleased + +# 2018-03-06 -- v4.0.16 -- M22 +- [changed] Addresses CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF warnings that surface in newer versions of Xcode and CocoaPods. + +# 2018-01-18 -- v4.0.14 -- M21.1 - [changed] Removed AppKit dependency for community macOS build. # 2017-11-30 -- v4.0.12 -- M20.2 diff --git a/Firebase/Core/FIRApp.m b/Firebase/Core/FIRApp.m index 22c9369b33a..c2ce28e3033 100644 --- a/Firebase/Core/FIRApp.m +++ b/Firebase/Core/FIRApp.m @@ -84,6 +84,7 @@ @implementation FIRApp static NSMutableDictionary *sAllApps; static FIRApp *sDefaultApp; +static NSMutableDictionary *sLibraryVersions; + (void)configure { FIROptions *options = [FIROptions defaultOptions]; @@ -228,6 +229,8 @@ + (void)resetApps { sDefaultApp = nil; [sAllApps removeAllObjects]; sAllApps = nil; + [sLibraryVersions removeAllObjects]; + sLibraryVersions = nil; } - (void)deleteApp:(FIRAppVoidBoolCallback)completion { @@ -400,6 +403,38 @@ + (BOOL)isDefaultAppConfigured { return (sDefaultApp != nil); } ++ (void)registerLibrary:(nonnull NSString *)library withVersion:(nonnull NSString *)version { + // Create the set of characters which aren't allowed, only if this feature is used. + NSMutableCharacterSet *allowedSet = [NSMutableCharacterSet alphanumericCharacterSet]; + [allowedSet addCharactersInString:@"-_."]; + NSCharacterSet *disallowedSet = [allowedSet invertedSet]; + // Make sure the library name and version strings do not contain unexpected characters, and + // add the name/version pair to the dictionary. + if ([library rangeOfCharacterFromSet:disallowedSet].location == NSNotFound && + [version rangeOfCharacterFromSet:disallowedSet].location == NSNotFound) { + if (!sLibraryVersions) { + sLibraryVersions = [[NSMutableDictionary alloc] init]; + } + sLibraryVersions[library] = version; + } else { + FIRLogError(kFIRLoggerCore, @"I-COR000027", + @"The library name (%@) or version number (%@) contain illegal characters. " + @"Only alphanumeric, dash, underscore and period characters are allowed.", + library, version); + } +} + ++ (NSString *)firebaseUserAgent { + NSMutableArray *libraries = + [[NSMutableArray alloc] initWithCapacity:sLibraryVersions.count]; + for (NSString *libraryName in sLibraryVersions) { + [libraries + addObject:[NSString stringWithFormat:@"%@/%@", libraryName, sLibraryVersions[libraryName]]]; + } + [libraries sortUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; + return [libraries componentsJoinedByString:@" "]; +} + - (void)checkExpectedBundleID { NSArray *bundles = [FIRBundleUtil relevantBundles]; NSString *expectedBundleID = [self expectedBundleID]; diff --git a/Firebase/Core/FIRMutableDictionary.m b/Firebase/Core/FIRMutableDictionary.m index 1d6ef3af9a6..31941bc6c03 100644 --- a/Firebase/Core/FIRMutableDictionary.m +++ b/Firebase/Core/FIRMutableDictionary.m @@ -37,7 +37,7 @@ - (instancetype)init { - (NSString *)description { __block NSString *description; dispatch_sync(_queue, ^{ - description = _objects.description; + description = self->_objects.description; }); return description; } @@ -45,33 +45,33 @@ - (NSString *)description { - (id)objectForKey:(id)key { __block id object; dispatch_sync(_queue, ^{ - object = _objects[key]; + object = self->_objects[key]; }); return object; } - (void)setObject:(id)object forKey:(id)key { dispatch_async(_queue, ^{ - _objects[key] = object; + self->_objects[key] = object; }); } - (void)removeObjectForKey:(id)key { dispatch_async(_queue, ^{ - [_objects removeObjectForKey:key]; + [self->_objects removeObjectForKey:key]; }); } - (void)removeAllObjects { dispatch_async(_queue, ^{ - [_objects removeAllObjects]; + [self->_objects removeAllObjects]; }); } - (NSUInteger)count { __block NSUInteger count; dispatch_sync(_queue, ^{ - count = _objects.count; + count = self->_objects.count; }); return count; } @@ -89,7 +89,7 @@ - (void)setObject:(id)obj forKeyedSubscript:(id)key { - (NSDictionary *)dictionary { __block NSDictionary *dictionary; dispatch_sync(_queue, ^{ - dictionary = [_objects copy]; + dictionary = [self->_objects copy]; }); return dictionary; } diff --git a/Firebase/Core/FIRNetworkURLSession.m b/Firebase/Core/FIRNetworkURLSession.m index c3da6743e6d..6b5ce3af294 100644 --- a/Firebase/Core/FIRNetworkURLSession.m +++ b/Firebase/Core/FIRNetworkURLSession.m @@ -314,11 +314,11 @@ - (void)URLSession:(NSURLSession *)session if (allow) { completionHandler(NSURLSessionAuthChallengeUseCredential, credential); } else { - [_loggerDelegate + [self->_loggerDelegate firNetwork_logWithLevel:kFIRNetworkLogLevelDebug messageCode:kFIRNetworkMessageCodeURLSession007 message:@"Cancelling authentication challenge for host. Host" - context:_request.URL]; + context:self->_request.URL]; completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil); } }; @@ -344,10 +344,10 @@ - (void)URLSession:(NSURLSession *)session } if (trustError != errSecSuccess) { - [_loggerDelegate firNetwork_logWithLevel:kFIRNetworkLogLevelError - messageCode:kFIRNetworkMessageCodeURLSession008 - message:@"Cannot evaluate server trust. Error, host" - contexts:@[ @(trustError), _request.URL ]]; + [self->_loggerDelegate firNetwork_logWithLevel:kFIRNetworkLogLevelError + messageCode:kFIRNetworkMessageCodeURLSession008 + message:@"Cannot evaluate server trust. Error, host" + contexts:@[ @(trustError), self->_request.URL ]]; shouldAllow = NO; } else { // Having a trust level "unspecified" by the user is the usual result, described at @@ -651,7 +651,7 @@ - (void)callCompletionHandler:(FIRNetworkURLSessionCompletionHandler)handler if (handler) { dispatch_async(dispatch_get_main_queue(), ^{ - handler(response, data, _sessionID, error); + handler(response, data, self->_sessionID, error); }); } } diff --git a/Firebase/Core/FIROptions.m b/Firebase/Core/FIROptions.m index b60ccdd706a..e8f6600f885 100644 --- a/Firebase/Core/FIROptions.m +++ b/Firebase/Core/FIROptions.m @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +#import "Private/FIRAppInternal.h" #import "Private/FIRBundleUtil.h" #import "Private/FIRErrors.h" #import "Private/FIRLogger.h" @@ -42,7 +43,7 @@ NSString *const kFIRLibraryVersionID = @"4" // Major version (one or more digits) @"00" // Minor version (exactly 2 digits) - @"13" // Build number (exactly 2 digits) + @"15" // Build number (exactly 2 digits) @"000"; // Fixed "000" // Plist file name. NSString *const kServiceInfoFileName = @"GoogleService-Info"; @@ -62,11 +63,18 @@ @interface FIROptions () @property(nonatomic, readwrite) NSMutableDictionary *optionsDictionary; /** - * Combination of analytics options from both the main plist and the GoogleService-Info.plist. + * Calls `analyticsOptionsDictionaryWithInfoDictionary:` using [NSBundle mainBundle].infoDictionary. + * It combines analytics options from both the infoDictionary and the GoogleService-Info.plist. * Values which are present in the main plist override values from the GoogleService-Info.plist. */ @property(nonatomic, readonly) NSDictionary *analyticsOptionsDictionary; +/** + * Combination of analytics options from both the infoDictionary and the GoogleService-Info.plist. + * Values which are present in the infoDictionary override values from the GoogleService-Info.plist. + */ +- (NSDictionary *)analyticsOptionsDictionaryWithInfoDictionary:(NSDictionary *)infoDictionary; + /** * Throw exception if editing is locked when attempting to modify an option. */ @@ -101,6 +109,30 @@ + (FIROptions *)defaultOptions { #pragma mark - Private class methods ++ (void)load { + // Report FirebaseCore version for useragent string + NSRange major = NSMakeRange(0, 1); + NSRange minor = NSMakeRange(1, 2); + NSRange patch = NSMakeRange(3, 2); + [FIRApp + registerLibrary:@"fire-ios" + withVersion:[NSString stringWithFormat:@"%@.%d.%d", + [kFIRLibraryVersionID substringWithRange:major], + [[kFIRLibraryVersionID substringWithRange:minor] + intValue], + [[kFIRLibraryVersionID substringWithRange:patch] + intValue]]]; + NSDictionary *info = [[NSBundle mainBundle] infoDictionary]; + NSString *xcodeVersion = info[@"DTXcodeBuild"]; + NSString *sdkVersion = info[@"DTSDKBuild"]; + if (xcodeVersion) { + [FIRApp registerLibrary:@"xcode" withVersion:xcodeVersion]; + } + if (sdkVersion) { + [FIRApp registerLibrary:@"apple-sdk" withVersion:sdkVersion]; + } +} + + (NSDictionary *)defaultOptionsDictionary { if (sDefaultOptionsDictionary != nil) { return sDefaultOptionsDictionary; @@ -346,26 +378,29 @@ - (void)setBundleID:(NSString *)bundleID { #pragma mark - Internal instance methods -- (NSDictionary *)analyticsOptionsDictionary { +- (NSDictionary *)analyticsOptionsDictionaryWithInfoDictionary:(NSDictionary *)infoDictionary { dispatch_once(&_createAnalyticsOptionsDictionaryOnce, ^{ NSMutableDictionary *tempAnalyticsOptions = [[NSMutableDictionary alloc] init]; - NSDictionary *mainInfoDictionary = [NSBundle mainBundle].infoDictionary; NSArray *measurementKeys = @[ kFIRIsMeasurementEnabled, kFIRIsAnalyticsCollectionEnabled, kFIRIsAnalyticsCollectionDeactivated ]; for (NSString *key in measurementKeys) { - id value = mainInfoDictionary[key] ?: self.optionsDictionary[key] ?: nil; + id value = infoDictionary[key] ?: self.optionsDictionary[key] ?: nil; if (!value) { continue; } tempAnalyticsOptions[key] = value; } - _analyticsOptionsDictionary = tempAnalyticsOptions; + self->_analyticsOptionsDictionary = tempAnalyticsOptions; }); return _analyticsOptionsDictionary; } +- (NSDictionary *)analyticsOptionsDictionary { + return [self analyticsOptionsDictionaryWithInfoDictionary:[NSBundle mainBundle].infoDictionary]; +} + /** * Whether or not Measurement was enabled. Measurement is enabled unless explicitly disabled in * GoogleService-Info.plist. This uses the old plist flag IS_MEASUREMENT_ENABLED, which should still @@ -376,7 +411,7 @@ - (BOOL)isMeasurementEnabled { return NO; } NSNumber *value = self.analyticsOptionsDictionary[kFIRIsMeasurementEnabled]; - if (!value) { + if (value == nil) { return YES; // Enable Measurement by default when the key is not in the dictionary. } return [value boolValue]; @@ -387,7 +422,7 @@ - (BOOL)isAnalyticsCollectionEnabled { return NO; } NSNumber *value = self.analyticsOptionsDictionary[kFIRIsAnalyticsCollectionEnabled]; - if (!value) { + if (value == nil) { return self.isMeasurementEnabled; // Fall back to older plist flag. } return [value boolValue]; @@ -395,7 +430,7 @@ - (BOOL)isAnalyticsCollectionEnabled { - (BOOL)isAnalyticsCollectionDeactivated { NSNumber *value = self.analyticsOptionsDictionary[kFIRIsAnalyticsCollectionDeactivated]; - if (!value) { + if (value == nil) { return NO; // Analytics Collection is not deactivated when the key is not in the dictionary. } return [value boolValue]; diff --git a/Firebase/Core/Private/FIRAppInternal.h b/Firebase/Core/Private/FIRAppInternal.h index 447d1510bd4..b7cf5e88720 100644 --- a/Firebase/Core/Private/FIRAppInternal.h +++ b/Firebase/Core/Private/FIRAppInternal.h @@ -134,6 +134,23 @@ typedef NSString *_Nullable (^FIRAppGetUIDImplementation)(void); */ + (BOOL)isDefaultAppConfigured; +/** + * Registers a given third-party library with the given version number to be reported for + * analyitcs. + * + * @param library Name of the library + * @param version Version of the library + */ +// clang-format off ++ (void)registerLibrary:(NSString *)library + withVersion:(NSString *)version NS_SWIFT_NAME(registerLibrary(_:version:)); +// clang-format on + +/** + * A concatenated string representing all the third-party libraries and version numbers. + */ ++ (NSString *)firebaseUserAgent; + /** * Used by each SDK to send logs about SDK configuration status to Clearcut. */ diff --git a/Firebase/Database/CHANGELOG.md b/Firebase/Database/CHANGELOG.md index 6867ccea13c..291ec0858f3 100644 --- a/Firebase/Database/CHANGELOG.md +++ b/Firebase/Database/CHANGELOG.md @@ -1,3 +1,7 @@ +# v4.1.5 +- [fixed] Fixes loss of precision for 64 bit numbers on older 32 bit iOS devices with persistence enabled. +- [changed] Addresses CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF warnings that surface in newer versions of Xcode and CocoaPods. + # v4.1.4 - [added] Firebase Database is now community-supported on tvOS. diff --git a/Firebase/Database/Persistence/FLevelDBStorageEngine.m b/Firebase/Database/Persistence/FLevelDBStorageEngine.m index 7de9ebf9318..e49d6bc044f 100644 --- a/Firebase/Database/Persistence/FLevelDBStorageEngine.m +++ b/Firebase/Database/Persistence/FLevelDBStorageEngine.m @@ -687,7 +687,7 @@ - (id)fixDoubleParsing:(id)value { NSString *doubleString = [value stringValue]; return [NSNumber numberWithDouble:[doubleString doubleValue]]; } else { - return [NSNumber numberWithLong:[value longValue]]; + return [NSNumber numberWithLongLong:[value longLongValue]]; } } } diff --git a/Firebase/Database/third_party/SocketRocket/FSRWebSocket.m b/Firebase/Database/third_party/SocketRocket/FSRWebSocket.m index c80dbb00631..9b3dad07896 100644 --- a/Firebase/Database/third_party/SocketRocket/FSRWebSocket.m +++ b/Firebase/Database/third_party/SocketRocket/FSRWebSocket.m @@ -515,10 +515,10 @@ - (void)_readHTTPHeader; } [self _readUntilHeaderCompleteWithCallback:^(FSRWebSocket *self, NSData *data) { - CFHTTPMessageAppendBytes(_receivedHTTPHeaders, (const UInt8 *)data.bytes, data.length); + CFHTTPMessageAppendBytes(self->_receivedHTTPHeaders, (const UInt8 *)data.bytes, data.length); - if (CFHTTPMessageIsHeaderComplete(_receivedHTTPHeaders)) { - SRFastLog(@"Finished reading headers %@", CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(_receivedHTTPHeaders))); + if (CFHTTPMessageIsHeaderComplete(self->_receivedHTTPHeaders)) { + SRFastLog(@"Finished reading headers %@", CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(self->_receivedHTTPHeaders))); [self _HTTPHeadersDidFinish]; } else { [self _readHTTPHeader]; @@ -696,7 +696,7 @@ - (void)_closeWithProtocolError:(NSString *)message; // Need to shunt this on the _callbackQueue first to see if they received any messages [self _performDelegateBlock:^{ [self closeWithCode:SRStatusCodeProtocolError reason:message]; - dispatch_async(_workQueue, ^{ + dispatch_async(self->_workQueue, ^{ [self _disconnect]; }); }]; @@ -706,7 +706,7 @@ - (void)_failWithError:(NSError *)error; { dispatch_async(_workQueue, ^{ if (self.readyState != SR_CLOSED) { - _failed = YES; + self->_failed = YES; [self _performDelegateBlock:^{ if ([self.delegate respondsToSelector:@selector(webSocket:didFailWithError:)]) { [self.delegate webSocket:self didFailWithError:error]; @@ -756,7 +756,7 @@ - (void)handlePing:(NSData *)pingData; { // Need to pingpong this off _callbackQueue first to make sure messages happen in order [self _performDelegateBlock:^{ - dispatch_async(_workQueue, ^{ + dispatch_async(self->_workQueue, ^{ [self _sendFrameWithOpcode:SROpCodePong data:pingData]; }); }]; @@ -1031,7 +1031,7 @@ - (void)_readFrameContinue; [self _closeWithProtocolError:@"Client must receive unmasked data"]; } - size_t extra_bytes_needed = header.masked ? sizeof(_currentReadMaskKey) : 0; + size_t extra_bytes_needed = header.masked ? sizeof(self->_currentReadMaskKey) : 0; if (header.payload_length == 126) { extra_bytes_needed += sizeof(uint16_t); @@ -1062,7 +1062,7 @@ - (void)_readFrameContinue; if (header.masked) { - assert(mapped_size >= sizeof(_currentReadMaskOffset) + offset); + assert(mapped_size >= sizeof(self->_currentReadMaskOffset) + offset); memcpy(self->_currentReadMaskKey, ((uint8_t *)mapped_buffer) + offset, sizeof(self->_currentReadMaskKey)); } @@ -1075,12 +1075,12 @@ - (void)_readFrameContinue; - (void)_readFrameNew; { dispatch_async(_workQueue, ^{ - [_currentFrameData setLength:0]; + [self->_currentFrameData setLength:0]; - _currentFrameOpcode = 0; - _currentFrameCount = 0; - _readOpCount = 0; - _currentStringScanPosition = 0; + self->_currentFrameOpcode = 0; + self->_currentFrameCount = 0; + self->_readOpCount = 0; + self->_currentStringScanPosition = 0; [self _readFrameContinue]; }); @@ -1123,7 +1123,7 @@ - (void)_pumpWriting; if (!_failed) { [self _performDelegateBlock:^{ if ([self.delegate respondsToSelector:@selector(webSocket:didCloseWithCode:reason:wasClean:)]) { - [self.delegate webSocket:self didCloseWithCode:_closeCode reason:_closeReason wasClean:YES]; + [self.delegate webSocket:self didCloseWithCode:self->_closeCode reason:self->_closeReason wasClean:YES]; } }]; } @@ -1184,7 +1184,7 @@ - (void)_cleanupSelfReference:(NSTimer *)timer // Cleanup selfRetain in the same GCD queue as usual dispatch_async(_workQueue, ^{ - _selfRetain = nil; + self->_selfRetain = nil; }); } @@ -1525,8 +1525,8 @@ - (void)safeHandleEvent:(NSStreamEvent)eventCode stream:(NSStream *)aStream [self _scheduleCleanup]; } - if (!_sentClose && !_failed) { - _sentClose = YES; + if (!self->_sentClose && !self->_failed) { + self->_sentClose = YES; // If we get closed in this state it's probably not clean because we should be sending this when we send messages [self _performDelegateBlock:^{ if ([self.delegate respondsToSelector:@selector(webSocket:didCloseWithCode:reason:wasClean:)]) { diff --git a/Firebase/Messaging/CHANGELOG.md b/Firebase/Messaging/CHANGELOG.md new file mode 100644 index 00000000000..2f59fabc30a --- /dev/null +++ b/Firebase/Messaging/CHANGELOG.md @@ -0,0 +1,88 @@ +# 2018-02-23 -- v2.1.1 +- Improve documentation on the usage of the autoInitEnabled property. + +# 2018-02-06 -- v2.1.0 +- Added a new property autoInitEnabled to enable and disable FCM token auto generation. +- Fixed an issue where notification delivery would fail after changing language settings. + +# 2017-09-26 -- v2.0.5 +- Added swizzling of additional UNUserNotificationCenterDelegate method, for + more accurate Analytics logging. +- Fixed a swizzling issue with unimplemented UNUserNotificationCenterDelegate + methods. + +# 2017-09-26 -- v2.0.4 +- Fixed an issue where the FCM token was not associating correctly with an APNs + device token, depending on when the APNs device token was made available. +- Fixed an issue where FCM tokens for different Sender IDs were not associating + correctly with an APNs device token. +- Fixed an issue that was preventing the FCM direct channel from being + established on the first start after 24 hours of being opened. +- Clarified a log message about method swizzling being enabled. + +# 2017-09-13 -- v2.0.3 +- Moved to safer use of NSAsserts, instead of lower-level `__builtin_trap()` + method. +- Added logging of the underlying error code for an error trying to create or + open an internal database file. + +# 2017-08-25 -- v2.0.2 +- Removed old logic which was saving the SDK version to NSUserDefaults. + +# 2017-08-07 -- v2.0.1 +- Fixed an issue where setting `shouldEstablishDirectChannel` in a background + thread was triggering the Main Thread Sanitizer in Xcode 9. +- Removed some old logic related to logging. +- Added some additional logging around errors while method swizzling. + +# 2017-05-03 -- v2.0.0 +- Introduced an improved interface for Swift 3 developers +- Added new properties and methods to simplify FCM token management +- Added property, APNSToken, to simplify APNs token management +- Added new delegate method to be notified of FCM token refreshes +- Added new property, shouldEstablishDirectChannel, to simplify connecting + directly to FCM + +# 2017-03-31 -- v1.2.3 + +- Fixed an issue where custom UNNotificationCenterDelegates may not have been + swizzled (if swizzling was enabled) +- Fixed a issue iOS 8.0 and 8.1 devices using scheduled notifications +- Improvements to console logging + +# 2017-01-31 -- v1.2.2 + +- Improved topic subscription logic for more reliable subscriptions. +- Reduced memory footprint and CPU usage when subscribing to multiple topics. +- Better documentation in the public headers. +- Switched from ProtocolBuffers2 to protobuf compiler. + +# 2016-10-12 -- v1.2.1 + +- Better documentation on the public headers. + +# 2016-09-02 -- v1.2.0 + +- Support the UserNotifications framework introduced in iOS 10. +- Add a new API, -applicationReceivedRemoteMessage:, to FIRMessaging. This + allows apps to receive data messages from FCM on devices running iOS 10 and + above. + +# 2016-07-06 -- v1.1.1 + +- Move FIRMessaging related plists to ApplicationSupport directory. + +# 2016-05-04 -- v1.1.0 + +- Change flag to disable swizzling to *FirebaseAppDelegateProxyEnabled*. +- '[FIRMessaging appDidReceiveMessage:] returns FIRMessagingMessageInfo object. +- Minor bug fixes. + +# 2016-01-25 -- v1.0.2 + +- Accept topic names without /topics prefix. +- Add Swift annotations to public static accessors. + +# 2016-01-25 -- v1.0.0 + +- New Firebase messaging API. diff --git a/Firebase/Messaging/FIRMessaging.m b/Firebase/Messaging/FIRMessaging.m index 782b7798213..1cc9a6cc4e4 100644 --- a/Firebase/Messaging/FIRMessaging.m +++ b/Firebase/Messaging/FIRMessaging.m @@ -70,6 +70,12 @@ @"com.firebase.messaging.notif.fcm-token-refreshed"; #endif // defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 +NSString *const kFIRMessagingUserDefaultsKeyAutoInitEnabled = + @"com.firebase.messaging.auto-init.enabled"; // Auto Init Enabled key stored in NSUserDefaults + +static NSString *const kFIRMessagingPlistAutoInitEnabled = + @"FirebaseMessagingAutoInitEnabled"; // Auto Init Enabled key stored in Info.plist + // Copied from Apple's header in case it is missing in some cases (e.g. pre-Xcode 8 builds). #ifndef NSFoundationVersionNumber_iOS_8_x_Max #define NSFoundationVersionNumber_iOS_8_x_Max 1199 @@ -120,8 +126,8 @@ - (instancetype)initWithMessage:(FIRMessagingRemoteMessage *)message { @end -@interface FIRMessaging () - +@interface FIRMessaging () // FIRApp properties @property(nonatomic, readwrite, copy) NSString *fcmSenderID; @@ -141,6 +147,7 @@ @interface FIRMessaging () @property(nonatomic, readwrite, strong) FIRMessagingRmqManager *rmq2Manager; @property(nonatomic, readwrite, strong) FIRMessagingReceiver *receiver; @property(nonatomic, readwrite, strong) FIRMessagingSyncMessageManager *syncMessageManager; +@property(nonatomic, readwrite, strong) NSUserDefaults *messagingUserDefaults; /// Message ID's logged for analytics. This prevents us from logging the same message twice /// which can happen if the user inadvertently calls `appDidReceiveMessage` along with us @@ -166,6 +173,7 @@ - (instancetype)initPrivately { if (self) { _loggedMessageIDs = [NSMutableSet set]; _instanceIDProxy = [[FIRMessagingInstanceIDProxy alloc] init]; + _messagingUserDefaults = [NSUserDefaults standardUserDefaults]; } return self; } @@ -404,7 +412,10 @@ - (void)handleIncomingLinkIfNeededFromMessage:(NSDictionary *)message { }]; } else if ([appDelegate respondsToSelector:openURLWithOptionsSelector]) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunguarded-availability" [appDelegate application:application openURL:url options:@{}]; +#pragma clang diagnostic pop // Similarly, |application:openURL:sourceApplication:annotation:| will also always be called, due // to the default swizzling done by FIRAAppDelegateProxy in Firebase Analytics @@ -448,6 +459,33 @@ - (void)setAPNSToken:(NSData *)apnsToken type:(FIRMessagingAPNSTokenType)type { #pragma mark - FCM +- (BOOL)isAutoInitEnabled { + // Check storage + id isAutoInitEnabledObject = + [_messagingUserDefaults objectForKey:kFIRMessagingUserDefaultsKeyAutoInitEnabled]; + if (isAutoInitEnabledObject) { + return [isAutoInitEnabledObject boolValue]; + } + + // Check Info.plist + isAutoInitEnabledObject = + [[NSBundle mainBundle] objectForInfoDictionaryKey:kFIRMessagingPlistAutoInitEnabled]; + if (isAutoInitEnabledObject) { + return [isAutoInitEnabledObject boolValue]; + } + // If none of above exists, we default assume FCM auto init is enabled. + return YES; +} + +- (void)setAutoInitEnabled:(BOOL)autoInitEnabled { + BOOL isFCMAutoInitEnabled = [self isAutoInitEnabled]; + [_messagingUserDefaults setBool:autoInitEnabled + forKey:kFIRMessagingUserDefaultsKeyAutoInitEnabled]; + if (!isFCMAutoInitEnabled && autoInitEnabled) { + self.defaultFcmToken = [self.instanceIDProxy token]; + } +} + - (NSString *)FCMToken { NSString *token = self.defaultFcmToken; if (!token) { @@ -643,10 +681,15 @@ + (NSString *)normalizeTopic:(NSString *)topic { } - (void)subscribeToTopic:(NSString *)topic { + [self subscribeToTopic:topic completion:nil]; +} + +- (void)subscribeToTopic:(NSString *)topic + completion:(nullable FIRMessagingTopicOperationCompletion)completion { if (self.defaultFcmToken.length && topic.length) { NSString *normalizeTopic = [[self class ] normalizeTopic:topic]; if (normalizeTopic.length) { - [self.pubsub subscribeToTopic:normalizeTopic]; + [self.pubsub subscribeToTopic:normalizeTopic handler:completion]; } else { FIRMessagingLoggerError(kFIRMessagingMessageCodeMessaging009, @"Cannot parse topic name %@. Will not subscribe.", topic); @@ -659,10 +702,15 @@ - (void)subscribeToTopic:(NSString *)topic { } - (void)unsubscribeFromTopic:(NSString *)topic { + [self unsubscribeFromTopic:topic completion:nil]; +} + +- (void)unsubscribeFromTopic:(NSString *)topic + completion:(nullable FIRMessagingTopicOperationCompletion)completion { if (self.defaultFcmToken.length && topic.length) { NSString *normalizeTopic = [[self class] normalizeTopic:topic]; if (normalizeTopic.length) { - [self.pubsub unsubscribeFromTopic:normalizeTopic]; + [self.pubsub unsubscribeFromTopic:normalizeTopic handler:completion]; } else { FIRMessagingLoggerError(kFIRMessagingMessageCodeMessaging011, @"Cannot parse topic name %@. Will not unsubscribe.", topic); @@ -728,7 +776,10 @@ - (void)setAPNSToken:(NSData *)apnsToken error:(NSError *)error { - (void)receiver:(FIRMessagingReceiver *)receiver receivedRemoteMessage:(FIRMessagingRemoteMessage *)remoteMessage { if ([self.delegate respondsToSelector:@selector(messaging:didReceiveMessage:)]) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunguarded-availability" [self.delegate messaging:self didReceiveMessage:remoteMessage]; +#pragma pop } else if ([self.delegate respondsToSelector:@selector(applicationReceivedRemoteMessage:)]) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" @@ -781,7 +832,7 @@ - (FIRMessagingNetworkStatus)networkType { #pragma mark - Notifications - (void)didReceiveDefaultInstanceIDToken:(NSNotification *)notification { - if (![notification.object isKindOfClass:[NSString class]]) { + if (notification.object && ![notification.object isKindOfClass:[NSString class]]) { FIRMessagingLoggerDebug(kFIRMessagingMessageCodeMessaging015, @"Invalid default FCM token type %@", NSStringFromClass([notification.object class])); diff --git a/Firebase/Messaging/FIRMessagingContextManagerService.m b/Firebase/Messaging/FIRMessagingContextManagerService.m index 232587f427a..65f64ad4d6d 100644 --- a/Firebase/Messaging/FIRMessagingContextManagerService.m +++ b/Firebase/Messaging/FIRMessagingContextManagerService.m @@ -144,7 +144,10 @@ + (void)scheduleLocalNotificationForMessage:(NSDictionary *)message if ([apsDictionary[kFIRMessagingContextManagerTitleKey] length]) { // |alertTitle| is iOS 8.2+, so check if we can set it if ([notification respondsToSelector:@selector(setAlertTitle:)]) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunguarded-availability" notification.alertTitle = apsDictionary[kFIRMessagingContextManagerTitleKey]; +#pragma pop } } diff --git a/Firebase/Messaging/FIRMessagingPubSub.h b/Firebase/Messaging/FIRMessagingPubSub.h index 3a034945303..2ce8ed4210a 100644 --- a/Firebase/Messaging/FIRMessagingPubSub.h +++ b/Firebase/Messaging/FIRMessagingPubSub.h @@ -59,6 +59,7 @@ * library for a given `authorizedEntity` and "gcm" scope. * @param topic The topic to subscribe to. Should be of the form * `"/topics/"`. + * @param options Unused parameter, please pass nil or empty dictionary. * @param handler The callback handler invoked when the subscribe call * ends. In case of success, a nil error is returned. Otherwise, * an appropriate error object is returned. @@ -70,7 +71,6 @@ options:(NSDictionary *)options handler:(FIRMessagingTopicOperationCompletion)handler; - /** * Unsubscribes an app instance from a topic, stopping it from receiving * any further messages sent to that topic. @@ -81,6 +81,7 @@ * @param token The token used to subscribe to this topic. * @param topic The topic to unsubscribe from. Should be of the form * `"/topics/"`. + * @param options Unused parameter, please pass nil or empty dictionary. * @param handler The handler that is invoked once the unsubscribe call ends. * In case of success, nil error is returned. Otherwise, an * appropriate error object is returned. @@ -98,8 +99,12 @@ * as compared to the `subscribe` method above which tries once. * * @param topic The topic name to subscribe to. Should be of the form `"/topics/"`. + * @param handler The handler that is invoked once the unsubscribe call ends. + * In case of success, nil error is returned. Otherwise, an + * appropriate error object is returned. */ -- (void)subscribeToTopic:(NSString *)topic; +- (void)subscribeToTopic:(NSString *)topic + handler:(nullable FIRMessagingTopicOperationCompletion)handler; /** * Asynchronously unsubscribe from the topic. Adds to the pending list of topic operations. @@ -107,8 +112,12 @@ * as compared to the `unsubscribe` method above which tries once. * * @param topic The topic name to unsubscribe from. Should be of the form `"/topics/"`. + * @param handler The handler that is invoked once the unsubscribe call ends. + * In case of success, nil error is returned. Otherwise, an + * appropriate error object is returned. */ -- (void)unsubscribeFromTopic:(NSString *)topic; +- (void)unsubscribeFromTopic:(NSString *)topic + handler:(nullable FIRMessagingTopicOperationCompletion)handler; /** * Schedule subscriptions sync. diff --git a/Firebase/Messaging/FIRMessagingPubSub.m b/Firebase/Messaging/FIRMessagingPubSub.m index c8293e01ec0..74a529261b1 100644 --- a/Firebase/Messaging/FIRMessagingPubSub.m +++ b/Firebase/Messaging/FIRMessagingPubSub.m @@ -146,16 +146,18 @@ - (void)unsubscribeWithToken:(NSString *)token }]; } -- (void)subscribeToTopic:(NSString *)topic { +- (void)subscribeToTopic:(NSString *)topic + handler:(nullable FIRMessagingTopicOperationCompletion)handler { [self.pendingTopicUpdates addOperationForTopic:topic withAction:FIRMessagingTopicActionSubscribe - completion:nil]; + completion:handler]; } -- (void)unsubscribeFromTopic:(NSString *)topic { +- (void)unsubscribeFromTopic:(NSString *)topic + handler:(nullable FIRMessagingTopicOperationCompletion)handler { [self.pendingTopicUpdates addOperationForTopic:topic withAction:FIRMessagingTopicActionUnsubscribe - completion:nil]; + completion:handler]; } - (void)scheduleSync:(BOOL)immediately { diff --git a/Firebase/Messaging/FIRMessagingRmq2PersistentStore.m b/Firebase/Messaging/FIRMessagingRmq2PersistentStore.m index 189f366cf90..f58bd52cd04 100644 --- a/Firebase/Messaging/FIRMessagingRmq2PersistentStore.m +++ b/Firebase/Messaging/FIRMessagingRmq2PersistentStore.m @@ -104,7 +104,10 @@ // Utility to create an NSString from a sqlite3 result code NSString * _Nonnull FIRMessagingStringFromSQLiteResult(int result) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunguarded-availability" const char *errorStr = sqlite3_errstr(result); +#pragma pop NSString *errorString = [NSString stringWithFormat:@"%d - %s", result, errorStr]; return errorString; } diff --git a/Firebase/Messaging/FIRMessagingTopicOperation.m b/Firebase/Messaging/FIRMessagingTopicOperation.m index 90760eb04d2..3120240c2fb 100644 --- a/Firebase/Messaging/FIRMessagingTopicOperation.m +++ b/Firebase/Messaging/FIRMessagingTopicOperation.m @@ -82,6 +82,7 @@ - (instancetype)initWithTopic:(NSString *)topic _topic = topic; _action = action; _token = token; + _options = options; _checkinService = checkinService; _completion = completion; diff --git a/Firebase/Messaging/FIRMessaging_Private.h b/Firebase/Messaging/FIRMessaging_Private.h index 0c351797330..496fea43ada 100644 --- a/Firebase/Messaging/FIRMessaging_Private.h +++ b/Firebase/Messaging/FIRMessaging_Private.h @@ -25,6 +25,8 @@ typedef NS_ENUM(int8_t, FIRMessagingNetworkStatus) { kFIRMessagingReachabilityReachableViaWWAN, }; +FOUNDATION_EXPORT NSString *const kFIRMessagingUserDefaultsKeyAutoInitEnabled; + @interface FIRMessagingRemoteMessage () @property(nonatomic, strong) NSDictionary *appData; diff --git a/Firebase/Messaging/Protos/GtalkCore.pbobjc.h b/Firebase/Messaging/Protos/GtalkCore.pbobjc.h index d4c8c8c2b1c..46d2d9ce485 100644 --- a/Firebase/Messaging/Protos/GtalkCore.pbobjc.h +++ b/Firebase/Messaging/Protos/GtalkCore.pbobjc.h @@ -197,6 +197,9 @@ typedef GPB_ENUM(GtalkClientEvent_Type) { GtalkClientEvent_Type_DiscardedEvents = 1, GtalkClientEvent_Type_FailedConnection = 2, GtalkClientEvent_Type_SuccessfulConnection = 3, + GtalkClientEvent_Type_McsReconnectRequest = 4, + GtalkClientEvent_Type_FailedSocketCreationMcsReconnect = 5, + GtalkClientEvent_Type_McsReconnectLimited = 6, }; GPBEnumDescriptor *GtalkClientEvent_Type_EnumDescriptor(void); @@ -207,6 +210,22 @@ GPBEnumDescriptor *GtalkClientEvent_Type_EnumDescriptor(void); **/ BOOL GtalkClientEvent_Type_IsValidValue(int32_t value); +#pragma mark - Enum GtalkClientEvent_McsReconnectAction + +typedef GPB_ENUM(GtalkClientEvent_McsReconnectAction) { + GtalkClientEvent_McsReconnectAction_None = 0, + GtalkClientEvent_McsReconnectAction_NotConnected = 1, + GtalkClientEvent_McsReconnectAction_TooSoon = 2, +}; + +GPBEnumDescriptor *GtalkClientEvent_McsReconnectAction_EnumDescriptor(void); + +/** + * Checks to see if the given value is defined by the enum or was not known at + * the time this source was generated. + **/ +BOOL GtalkClientEvent_McsReconnectAction_IsValidValue(int32_t value); + #pragma mark - GtalkGtalkCoreRoot /** @@ -247,9 +266,9 @@ typedef GPB_ENUM(GtalkHeartbeatPing_FieldNumber) { @property(nonatomic, readwrite) BOOL hasStatus; -@property(nonatomic, readwrite, strong, null_resettable) GtalkCellTower *cellTower; +@property(nonatomic, readwrite, strong, null_resettable) GtalkCellTower *cellTower DEPRECATED_ATTRIBUTE; /** Test to see if @c cellTower has been set. */ -@property(nonatomic, readwrite) BOOL hasCellTower; +@property(nonatomic, readwrite) BOOL hasCellTower DEPRECATED_ATTRIBUTE; @property(nonatomic, readwrite) int32_t intervalMs; @@ -282,9 +301,9 @@ typedef GPB_ENUM(GtalkHeartbeatAck_FieldNumber) { @property(nonatomic, readwrite) BOOL hasStatus; -@property(nonatomic, readwrite, strong, null_resettable) GtalkCellTower *cellTower; +@property(nonatomic, readwrite, strong, null_resettable) GtalkCellTower *cellTower DEPRECATED_ATTRIBUTE; /** Test to see if @c cellTower has been set. */ -@property(nonatomic, readwrite) BOOL hasCellTower; +@property(nonatomic, readwrite) BOOL hasCellTower DEPRECATED_ATTRIBUTE; @property(nonatomic, readwrite) int32_t intervalMs; @@ -406,7 +425,6 @@ typedef GPB_ENUM(GtalkLoginRequest_FieldNumber) { GtalkLoginRequest_FieldNumber_DeviceId = 6, GtalkLoginRequest_FieldNumber_LastRmqId = 7, GtalkLoginRequest_FieldNumber_SettingArray = 8, - GtalkLoginRequest_FieldNumber_Compress = 9, GtalkLoginRequest_FieldNumber_ReceivedPersistentIdArray = 10, GtalkLoginRequest_FieldNumber_IncludeStreamIds = 11, GtalkLoginRequest_FieldNumber_HeartbeatStat = 13, @@ -420,6 +438,8 @@ typedef GPB_ENUM(GtalkLoginRequest_FieldNumber) { GtalkLoginRequest_FieldNumber_GcmStartTimeMs = 21, GtalkLoginRequest_FieldNumber_ClientEventArray = 22, GtalkLoginRequest_FieldNumber_OnFallback = 23, + GtalkLoginRequest_FieldNumber_NoPendingUpstream = 24, + GtalkLoginRequest_FieldNumber_ReconnectRequestId = 25, }; @interface GtalkLoginRequest : GPBMessage @@ -464,10 +484,6 @@ typedef GPB_ENUM(GtalkLoginRequest_FieldNumber) { @property(nonatomic, readonly) NSUInteger settingArray_Count; -@property(nonatomic, readwrite) int32_t compress; - -@property(nonatomic, readwrite) BOOL hasCompress; - @property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *receivedPersistentIdArray; /** The number of items in @c receivedPersistentIdArray without causing the array to be created. */ @property(nonatomic, readonly) NSUInteger receivedPersistentIdArray_Count; @@ -507,9 +523,9 @@ typedef GPB_ENUM(GtalkLoginRequest_FieldNumber) { @property(nonatomic, readwrite) BOOL hasTokenVersionInfo; -@property(nonatomic, readwrite, strong, null_resettable) GtalkCellTower *cellTower; +@property(nonatomic, readwrite, strong, null_resettable) GtalkCellTower *cellTower DEPRECATED_ATTRIBUTE; /** Test to see if @c cellTower has been set. */ -@property(nonatomic, readwrite) BOOL hasCellTower; +@property(nonatomic, readwrite) BOOL hasCellTower DEPRECATED_ATTRIBUTE; @property(nonatomic, readwrite) uint64_t gcmStartTimeMs; @@ -524,6 +540,14 @@ typedef GPB_ENUM(GtalkLoginRequest_FieldNumber) { @property(nonatomic, readwrite) BOOL onFallback; @property(nonatomic, readwrite) BOOL hasOnFallback; + +@property(nonatomic, readwrite) BOOL noPendingUpstream; + +@property(nonatomic, readwrite) BOOL hasNoPendingUpstream; + +@property(nonatomic, readwrite) int32_t reconnectRequestId; + +@property(nonatomic, readwrite) BOOL hasReconnectRequestId; @end #pragma mark - GtalkLoginResponse @@ -1242,9 +1266,9 @@ typedef GPB_ENUM(GtalkDataMessageStanza_FieldNumber) { @property(nonatomic, readwrite) BOOL hasFlags; -@property(nonatomic, readwrite, strong, null_resettable) GtalkCellTower *cellTower; +@property(nonatomic, readwrite, strong, null_resettable) GtalkCellTower *cellTower DEPRECATED_ATTRIBUTE; /** Test to see if @c cellTower has been set. */ -@property(nonatomic, readwrite) BOOL hasCellTower; +@property(nonatomic, readwrite) BOOL hasCellTower DEPRECATED_ATTRIBUTE; @property(nonatomic, readwrite) int32_t priority; @@ -1273,6 +1297,7 @@ typedef GPB_ENUM(GtalkCellTower_FieldNumber) { GtalkCellTower_FieldNumber_KnownCongestionStatus = 2, }; +DEPRECATED_ATTRIBUTE @interface GtalkCellTower : GPBMessage @@ -1297,6 +1322,7 @@ typedef GPB_ENUM(GtalkClientEvent_FieldNumber) { GtalkClientEvent_FieldNumber_TimeConnectionEndedMs = 203, GtalkClientEvent_FieldNumber_ErrorCode = 204, GtalkClientEvent_FieldNumber_TimeConnectionEstablishedMs = 300, + GtalkClientEvent_FieldNumber_McsReconnectAction = 400, }; @interface GtalkClientEvent : GPBMessage @@ -1333,6 +1359,10 @@ typedef GPB_ENUM(GtalkClientEvent_FieldNumber) { @property(nonatomic, readwrite) uint64_t timeConnectionEstablishedMs; @property(nonatomic, readwrite) BOOL hasTimeConnectionEstablishedMs; + +@property(nonatomic, readwrite) GtalkClientEvent_McsReconnectAction mcsReconnectAction; + +@property(nonatomic, readwrite) BOOL hasMcsReconnectAction; @end NS_ASSUME_NONNULL_END diff --git a/Firebase/Messaging/Protos/GtalkCore.pbobjc.m b/Firebase/Messaging/Protos/GtalkCore.pbobjc.m index f4efe22bb5a..06c91346a60 100644 --- a/Firebase/Messaging/Protos/GtalkCore.pbobjc.m +++ b/Firebase/Messaging/Protos/GtalkCore.pbobjc.m @@ -503,7 +503,6 @@ @implementation GtalkLoginRequest @dynamic hasDeviceId, deviceId; @dynamic hasLastRmqId, lastRmqId; @dynamic settingArray, settingArray_Count; -@dynamic hasCompress, compress; @dynamic receivedPersistentIdArray, receivedPersistentIdArray_Count; @dynamic hasIncludeStreamIds, includeStreamIds; @dynamic hasHeartbeatStat, heartbeatStat; @@ -517,12 +516,14 @@ @implementation GtalkLoginRequest @dynamic hasGcmStartTimeMs, gcmStartTimeMs; @dynamic clientEventArray, clientEventArray_Count; @dynamic hasOnFallback, onFallback; +@dynamic hasNoPendingUpstream, noPendingUpstream; +@dynamic hasReconnectRequestId, reconnectRequestId; typedef struct GtalkLoginRequest__storage_ { uint32_t _has_storage_[1]; - int32_t compress; GtalkLoginRequest_AuthService authService; int32_t networkType; + int32_t reconnectRequestId; NSString *id_p; NSString *domain; NSString *user; @@ -619,15 +620,6 @@ + (GPBDescriptor *)descriptor { .flags = GPBFieldRepeated, .dataType = GPBDataTypeMessage, }, - { - .name = "compress", - .dataTypeSpecific.className = NULL, - .number = GtalkLoginRequest_FieldNumber_Compress, - .hasIndex = 7, - .offset = (uint32_t)offsetof(GtalkLoginRequest__storage_, compress), - .flags = GPBFieldOptional, - .dataType = GPBDataTypeInt32, - }, { .name = "receivedPersistentIdArray", .dataTypeSpecific.className = NULL, @@ -641,8 +633,8 @@ + (GPBDescriptor *)descriptor { .name = "includeStreamIds", .dataTypeSpecific.className = NULL, .number = GtalkLoginRequest_FieldNumber_IncludeStreamIds, - .hasIndex = 8, - .offset = 9, // Stored in _has_storage_ to save space. + .hasIndex = 7, + .offset = 8, // Stored in _has_storage_ to save space. .flags = GPBFieldOptional, .dataType = GPBDataTypeBool, }, @@ -650,7 +642,7 @@ + (GPBDescriptor *)descriptor { .name = "heartbeatStat", .dataTypeSpecific.className = GPBStringifySymbol(GtalkHeartbeatStat), .number = GtalkLoginRequest_FieldNumber_HeartbeatStat, - .hasIndex = 10, + .hasIndex = 9, .offset = (uint32_t)offsetof(GtalkLoginRequest__storage_, heartbeatStat), .flags = GPBFieldOptional, .dataType = GPBDataTypeMessage, @@ -659,8 +651,8 @@ + (GPBDescriptor *)descriptor { .name = "useRmq2", .dataTypeSpecific.className = NULL, .number = GtalkLoginRequest_FieldNumber_UseRmq2, - .hasIndex = 11, - .offset = 12, // Stored in _has_storage_ to save space. + .hasIndex = 10, + .offset = 11, // Stored in _has_storage_ to save space. .flags = GPBFieldOptional, .dataType = GPBDataTypeBool, }, @@ -668,7 +660,7 @@ + (GPBDescriptor *)descriptor { .name = "accountId", .dataTypeSpecific.className = NULL, .number = GtalkLoginRequest_FieldNumber_AccountId, - .hasIndex = 13, + .hasIndex = 12, .offset = (uint32_t)offsetof(GtalkLoginRequest__storage_, accountId), .flags = GPBFieldOptional, .dataType = GPBDataTypeInt64, @@ -677,7 +669,7 @@ + (GPBDescriptor *)descriptor { .name = "authService", .dataTypeSpecific.enumDescFunc = GtalkLoginRequest_AuthService_EnumDescriptor, .number = GtalkLoginRequest_FieldNumber_AuthService, - .hasIndex = 14, + .hasIndex = 13, .offset = (uint32_t)offsetof(GtalkLoginRequest__storage_, authService), .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldHasEnumDescriptor), .dataType = GPBDataTypeEnum, @@ -686,7 +678,7 @@ + (GPBDescriptor *)descriptor { .name = "networkType", .dataTypeSpecific.className = NULL, .number = GtalkLoginRequest_FieldNumber_NetworkType, - .hasIndex = 15, + .hasIndex = 14, .offset = (uint32_t)offsetof(GtalkLoginRequest__storage_, networkType), .flags = GPBFieldOptional, .dataType = GPBDataTypeInt32, @@ -695,7 +687,7 @@ + (GPBDescriptor *)descriptor { .name = "status", .dataTypeSpecific.className = NULL, .number = GtalkLoginRequest_FieldNumber_Status, - .hasIndex = 16, + .hasIndex = 15, .offset = (uint32_t)offsetof(GtalkLoginRequest__storage_, status), .flags = GPBFieldOptional, .dataType = GPBDataTypeInt64, @@ -704,7 +696,7 @@ + (GPBDescriptor *)descriptor { .name = "tokenVersionInfo", .dataTypeSpecific.className = NULL, .number = GtalkLoginRequest_FieldNumber_TokenVersionInfo, - .hasIndex = 17, + .hasIndex = 16, .offset = (uint32_t)offsetof(GtalkLoginRequest__storage_, tokenVersionInfo), .flags = GPBFieldOptional, .dataType = GPBDataTypeString, @@ -713,7 +705,7 @@ + (GPBDescriptor *)descriptor { .name = "cellTower", .dataTypeSpecific.className = GPBStringifySymbol(GtalkCellTower), .number = GtalkLoginRequest_FieldNumber_CellTower, - .hasIndex = 18, + .hasIndex = 17, .offset = (uint32_t)offsetof(GtalkLoginRequest__storage_, cellTower), .flags = GPBFieldOptional, .dataType = GPBDataTypeMessage, @@ -722,7 +714,7 @@ + (GPBDescriptor *)descriptor { .name = "gcmStartTimeMs", .dataTypeSpecific.className = NULL, .number = GtalkLoginRequest_FieldNumber_GcmStartTimeMs, - .hasIndex = 19, + .hasIndex = 18, .offset = (uint32_t)offsetof(GtalkLoginRequest__storage_, gcmStartTimeMs), .flags = GPBFieldOptional, .dataType = GPBDataTypeUInt64, @@ -740,11 +732,29 @@ + (GPBDescriptor *)descriptor { .name = "onFallback", .dataTypeSpecific.className = NULL, .number = GtalkLoginRequest_FieldNumber_OnFallback, - .hasIndex = 20, - .offset = 21, // Stored in _has_storage_ to save space. + .hasIndex = 19, + .offset = 20, // Stored in _has_storage_ to save space. .flags = GPBFieldOptional, .dataType = GPBDataTypeBool, }, + { + .name = "noPendingUpstream", + .dataTypeSpecific.className = NULL, + .number = GtalkLoginRequest_FieldNumber_NoPendingUpstream, + .hasIndex = 21, + .offset = 22, // Stored in _has_storage_ to save space. + .flags = GPBFieldOptional, + .dataType = GPBDataTypeBool, + }, + { + .name = "reconnectRequestId", + .dataTypeSpecific.className = NULL, + .number = GtalkLoginRequest_FieldNumber_ReconnectRequestId, + .hasIndex = 23, + .offset = (uint32_t)offsetof(GtalkLoginRequest__storage_, reconnectRequestId), + .flags = GPBFieldOptional, + .dataType = GPBDataTypeInt32, + }, }; GPBDescriptor *localDescriptor = [GPBDescriptor allocDescriptorForClass:[GtalkLoginRequest class] @@ -2730,6 +2740,9 @@ + (GPBDescriptor *)descriptor { #pragma mark - GtalkCellTower +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" + @implementation GtalkCellTower @dynamic hasId_p, id_p; @@ -2782,6 +2795,8 @@ + (GPBDescriptor *)descriptor { @end +#pragma clang diagnostic pop + #pragma mark - GtalkClientEvent @implementation GtalkClientEvent @@ -2794,6 +2809,7 @@ @implementation GtalkClientEvent @dynamic hasTimeConnectionEndedMs, timeConnectionEndedMs; @dynamic hasErrorCode, errorCode; @dynamic hasTimeConnectionEstablishedMs, timeConnectionEstablishedMs; +@dynamic hasMcsReconnectAction, mcsReconnectAction; typedef struct GtalkClientEvent__storage_ { uint32_t _has_storage_[1]; @@ -2802,6 +2818,7 @@ @implementation GtalkClientEvent int32_t networkType; int32_t networkPort; int32_t errorCode; + GtalkClientEvent_McsReconnectAction mcsReconnectAction; uint64_t timeConnectionStartedMs; uint64_t timeConnectionEndedMs; uint64_t timeConnectionEstablishedMs; @@ -2885,6 +2902,15 @@ + (GPBDescriptor *)descriptor { .flags = GPBFieldOptional, .dataType = GPBDataTypeUInt64, }, + { + .name = "mcsReconnectAction", + .dataTypeSpecific.enumDescFunc = GtalkClientEvent_McsReconnectAction_EnumDescriptor, + .number = GtalkClientEvent_FieldNumber_McsReconnectAction, + .hasIndex = 8, + .offset = (uint32_t)offsetof(GtalkClientEvent__storage_, mcsReconnectAction), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldHasEnumDescriptor), + .dataType = GPBDataTypeEnum, + }, }; GPBDescriptor *localDescriptor = [GPBDescriptor allocDescriptorForClass:[GtalkClientEvent class] @@ -2909,12 +2935,17 @@ + (GPBDescriptor *)descriptor { if (!descriptor) { static const char *valueNames = "Unknown\000DiscardedEvents\000FailedConnection" - "\000SuccessfulConnection\000"; + "\000SuccessfulConnection\000McsReconnectReques" + "t\000FailedSocketCreationMcsReconnect\000McsRe" + "connectLimited\000"; static const int32_t values[] = { GtalkClientEvent_Type_Unknown, GtalkClientEvent_Type_DiscardedEvents, GtalkClientEvent_Type_FailedConnection, GtalkClientEvent_Type_SuccessfulConnection, + GtalkClientEvent_Type_McsReconnectRequest, + GtalkClientEvent_Type_FailedSocketCreationMcsReconnect, + GtalkClientEvent_Type_McsReconnectLimited, }; GPBEnumDescriptor *worker = [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol(GtalkClientEvent_Type) @@ -2935,6 +2966,45 @@ BOOL GtalkClientEvent_Type_IsValidValue(int32_t value__) { case GtalkClientEvent_Type_DiscardedEvents: case GtalkClientEvent_Type_FailedConnection: case GtalkClientEvent_Type_SuccessfulConnection: + case GtalkClientEvent_Type_McsReconnectRequest: + case GtalkClientEvent_Type_FailedSocketCreationMcsReconnect: + case GtalkClientEvent_Type_McsReconnectLimited: + return YES; + default: + return NO; + } +} + +#pragma mark - Enum GtalkClientEvent_McsReconnectAction + +GPBEnumDescriptor *GtalkClientEvent_McsReconnectAction_EnumDescriptor(void) { + static GPBEnumDescriptor *descriptor = NULL; + if (!descriptor) { + static const char *valueNames = + "None\000NotConnected\000TooSoon\000"; + static const int32_t values[] = { + GtalkClientEvent_McsReconnectAction_None, + GtalkClientEvent_McsReconnectAction_NotConnected, + GtalkClientEvent_McsReconnectAction_TooSoon, + }; + GPBEnumDescriptor *worker = + [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol(GtalkClientEvent_McsReconnectAction) + valueNames:valueNames + values:values + count:(uint32_t)(sizeof(values) / sizeof(int32_t)) + enumVerifier:GtalkClientEvent_McsReconnectAction_IsValidValue]; + if (!OSAtomicCompareAndSwapPtrBarrier(nil, worker, (void * volatile *)&descriptor)) { + [worker release]; + } + } + return descriptor; +} + +BOOL GtalkClientEvent_McsReconnectAction_IsValidValue(int32_t value__) { + switch (value__) { + case GtalkClientEvent_McsReconnectAction_None: + case GtalkClientEvent_McsReconnectAction_NotConnected: + case GtalkClientEvent_McsReconnectAction_TooSoon: return YES; default: return NO; diff --git a/Firebase/Messaging/Public/FIRMessaging.h b/Firebase/Messaging/Public/FIRMessaging.h index 7cd7b194936..31e86255eaa 100644 --- a/Firebase/Messaging/Public/FIRMessaging.h +++ b/Firebase/Messaging/Public/FIRMessaging.h @@ -289,7 +289,6 @@ NS_SWIFT_NAME(Messaging) */ @property(nonatomic, weak, nullable) id delegate; - /** * Delegate to handle remote data messages received via FCM for devices running iOS 10 or above. */ @@ -353,6 +352,25 @@ NS_SWIFT_NAME(Messaging) #pragma mark - FCM Tokens +/** + * Is Firebase Messaging token auto generation enabled? If this flag is disabled, + * Firebase Messaging will not generate token automatically for message delivery. + * + * If this flag is disabled, Firebase Messaging does not generate new tokens automatically for + * message delivery. If this flag is enabled, FCM generates a registration token on application + * start when there is no existing valid token. FCM also generates a new token when an existing + * token is deleted. + * + * This setting is persisted, and is applied on future + * invocations of your application. Once explicitly set, it overrides any + * settings in your Info.plist. + * + * By default, FCM automatic initialization is enabled. If you need to change the + * default (for example, because you want to prompt the user before getting token) + * set FirebaseMessagingAutoInitEnabled to false in your application's Info.plist. + */ +@property(nonatomic, assign, getter=isAutoInitEnabled) BOOL autoInitEnabled; + /** * The FCM token is used to identify this device so that FCM can send notifications to it. * It is associated with your APNS token when the APNS token is supplied, so that sending diff --git a/Firebase/Storage/CHANGELOG.md b/Firebase/Storage/CHANGELOG.md index 1077b9f30d9..194597a0f3b 100644 --- a/Firebase/Storage/CHANGELOG.md +++ b/Firebase/Storage/CHANGELOG.md @@ -1,3 +1,6 @@ +# v2.1.3 +- [changed] Addresses CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF warnings that surface in newer versions of Xcode and CocoaPods. + # v2.1.2 - [added] Firebase Storage is now community-supported on tvOS. diff --git a/Firebase/Storage/FIRStorageDeleteTask.m b/Firebase/Storage/FIRStorageDeleteTask.m index 738d8a5d0a1..b41f06ed9d3 100644 --- a/Firebase/Storage/FIRStorageDeleteTask.m +++ b/Firebase/Storage/FIRStorageDeleteTask.m @@ -60,7 +60,7 @@ - (void)enqueue { if (callback) { callback(self.error); } - _fetcherCompletion = nil; + self->_fetcherCompletion = nil; }; #pragma clang diangostic pop diff --git a/Firebase/Storage/FIRStorageDownloadTask.m b/Firebase/Storage/FIRStorageDownloadTask.m index c410f0593cd..91da4b72700 100644 --- a/Firebase/Storage/FIRStorageDownloadTask.m +++ b/Firebase/Storage/FIRStorageDownloadTask.m @@ -116,7 +116,7 @@ - (void)enqueueWithData:(nullable NSData *)resumeData { self.error = [FIRStorageErrors errorWithServerError:error reference:self.reference]; [self fireHandlersForStatus:FIRStorageTaskStatusFailure snapshot:self.snapshot]; [self removeAllObservers]; - _fetcherCompletion = nil; + self->_fetcherCompletion = nil; return; } @@ -124,12 +124,12 @@ - (void)enqueueWithData:(nullable NSData *)resumeData { self.state = FIRStorageTaskStateSuccess; if (data) { - _downloadData = data; + self->_downloadData = data; } [self fireHandlersForStatus:FIRStorageTaskStatusSuccess snapshot:self.snapshot]; [self removeAllObservers]; - _fetcherCompletion = nil; + self->_fetcherCompletion = nil; }; #pragma clang diagnostic pop diff --git a/Firebase/Storage/FIRStorageErrors.m b/Firebase/Storage/FIRStorageErrors.m index dd5c9033368..ecfae023799 100644 --- a/Firebase/Storage/FIRStorageErrors.m +++ b/Firebase/Storage/FIRStorageErrors.m @@ -63,7 +63,7 @@ + (NSError *)errorWithCode:(FIRStorageErrorCode)code NSString *totalString = total ? @(total).stringValue : @"unknown"; NSString *sizeString = total ? @(size).stringValue : @"unknown"; NSString *const kSizeExceededErrorFormat = - @"Attempeted to download object with size of %@ bytes, " + @"Attempted to download object with size of %@ bytes, " @"which exceeds the maximum size of %@ bytes. " @"Consider raising the maximum download size, or using " @"[FIRStorageReference writeToFile:]"; diff --git a/Firebase/Storage/FIRStorageGetMetadataTask.m b/Firebase/Storage/FIRStorageGetMetadataTask.m index 78d8a16bebb..26236520406 100644 --- a/Firebase/Storage/FIRStorageGetMetadataTask.m +++ b/Firebase/Storage/FIRStorageGetMetadataTask.m @@ -65,7 +65,7 @@ - (void)enqueue { if (callback) { callback(nil, self.error); } - _fetcherCompletion = nil; + self->_fetcherCompletion = nil; return; } @@ -90,7 +90,7 @@ - (void)enqueue { callback(nil, self.error); } } - _fetcherCompletion = nil; + self->_fetcherCompletion = nil; }; #pragma clang diagnostic pop diff --git a/Firebase/Storage/FIRStorageMetadata.m b/Firebase/Storage/FIRStorageMetadata.m index 34ac86ca5e5..ab250769cd0 100644 --- a/Firebase/Storage/FIRStorageMetadata.m +++ b/Firebase/Storage/FIRStorageMetadata.m @@ -60,8 +60,9 @@ - (instancetype)initWithDictionary:(NSDictionary *)dictionary { NSURLComponents *components = [[NSURLComponents alloc] init]; components.scheme = kFIRStorageScheme; components.host = kFIRStorageHost; - NSString *path = [FIRStorageUtils GCSEscapedString:_path]; - NSString *fullPath = [NSString stringWithFormat:kFIRStorageFullPathFormat, _bucket, path]; + NSString *path = [FIRStorageUtils GCSEscapedString:self->_path]; + NSString *fullPath = + [NSString stringWithFormat:kFIRStorageFullPathFormat, self->_bucket, path]; components.percentEncodedPath = fullPath; components.query = [NSString stringWithFormat:@"alt=media&token=%@", token]; diff --git a/Firebase/Storage/FIRStorageObservableTask.m b/Firebase/Storage/FIRStorageObservableTask.m index 7d7c61f15e5..af82fa2c2b3 100644 --- a/Firebase/Storage/FIRStorageObservableTask.m +++ b/Firebase/Storage/FIRStorageObservableTask.m @@ -206,7 +206,6 @@ - (void)fireHandlers:(NSMutableDictionary *) [handlersCopy enumerateKeysAndObjectsUsingBlock:^( NSString *_Nonnull key, FIRStorageVoidSnapshot _Nonnull handler, BOOL *_Nonnull stop) { - dispatch_async(callbackQueue, ^{ handler(snapshot); }); diff --git a/Firebase/Storage/FIRStorageUpdateMetadataTask.m b/Firebase/Storage/FIRStorageUpdateMetadataTask.m index cf1bf930607..fa5955a5d69 100644 --- a/Firebase/Storage/FIRStorageUpdateMetadataTask.m +++ b/Firebase/Storage/FIRStorageUpdateMetadataTask.m @@ -71,7 +71,7 @@ - (void)enqueue { if (callback) { callback(nil, self.error); } - _fetcherCompletion = nil; + self->_fetcherCompletion = nil; return; } @@ -96,7 +96,7 @@ - (void)enqueue { callback(nil, self.error); } } - _fetcherCompletion = nil; + self->_fetcherCompletion = nil; }; #pragma clang diagnostic pop diff --git a/Firebase/Storage/FIRStorageUploadTask.m b/Firebase/Storage/FIRStorageUploadTask.m index 0df0bf44d7e..f84c2c7dec7 100644 --- a/Firebase/Storage/FIRStorageUploadTask.m +++ b/Firebase/Storage/FIRStorageUploadTask.m @@ -117,7 +117,7 @@ - (void)enqueue { weakSelf.state = FIRStorageTaskStateProgress; weakSelf.progress.completedUnitCount = totalBytesSent; weakSelf.progress.totalUnitCount = totalBytesExpectedToSend; - weakSelf.metadata = _uploadMetadata; + weakSelf.metadata = self->_uploadMetadata; [weakSelf fireHandlersForStatus:FIRStorageTaskStatusProgress snapshot:weakSelf.snapshot]; weakSelf.state = FIRStorageTaskStateRunning; }]; @@ -137,10 +137,10 @@ - (void)enqueue { if (error) { self.state = FIRStorageTaskStateFailed; self.error = [FIRStorageErrors errorWithServerError:error reference:self.reference]; - self.metadata = _uploadMetadata; + self.metadata = self->_uploadMetadata; [self fireHandlersForStatus:FIRStorageTaskStatusFailure snapshot:self.snapshot]; [self removeAllObservers]; - _fetcherCompletion = nil; + self->_fetcherCompletion = nil; return; } @@ -166,7 +166,7 @@ - (void)enqueue { [self fireHandlersForStatus:FIRStorageTaskStatusSuccess snapshot:self.snapshot]; [self removeAllObservers]; - _fetcherCompletion = nil; + self->_fetcherCompletion = nil; }; #pragma clang diagnostic pop diff --git a/FirebaseAuth.podspec b/FirebaseAuth.podspec index 6b84f5d817d..d87d1cd3b72 100644 --- a/FirebaseAuth.podspec +++ b/FirebaseAuth.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseAuth' - s.version = '4.4.1' + s.version = '4.4.3' s.summary = 'The official iOS client for Firebase Authentication' s.description = <<-DESC @@ -21,7 +21,7 @@ supports email and password accounts, as well as several 3rd party authenticatio s.osx.deployment_target = '10.10' s.tvos.deployment_target = '10.0' - s.cocoapods_version = '>= 1.4.0.beta.2' + s.cocoapods_version = '>= 1.4.0' s.static_framework = true s.prefix_header_file = false diff --git a/FirebaseCore.podspec b/FirebaseCore.podspec index 854815ef47c..444022d9831 100644 --- a/FirebaseCore.podspec +++ b/FirebaseCore.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseCore' - s.version = '4.0.13' + s.version = '4.0.15' s.summary = 'Firebase Core for iOS' s.description = <<-DESC @@ -20,7 +20,7 @@ Firebase Core includes FIRApp and FIROptions which provide central configuration s.osx.deployment_target = '10.10' s.tvos.deployment_target = '10.0' - s.cocoapods_version = '>= 1.4.0.beta.2' + s.cocoapods_version = '>= 1.4.0' s.static_framework = true s.prefix_header_file = false diff --git a/FirebaseDatabase.podspec b/FirebaseDatabase.podspec index 6cb9af50ed6..3992c2121a1 100644 --- a/FirebaseDatabase.podspec +++ b/FirebaseDatabase.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseDatabase' - s.version = '4.1.3' + s.version = '4.1.4' s.summary = 'Firebase Open Source Libraries for iOS.' s.description = <<-DESC @@ -20,7 +20,7 @@ Simplify your iOS development, grow your user base, and monetize more effectivel s.osx.deployment_target = '10.10' s.tvos.deployment_target = '10.0' - s.cocoapods_version = '>= 1.4.0.beta.2' + s.cocoapods_version = '>= 1.4.0' s.static_framework = true s.prefix_header_file = false diff --git a/FirebaseFirestore.podspec b/FirebaseFirestore.podspec index 5a3a1d639cd..094a2ba5129 100644 --- a/FirebaseFirestore.podspec +++ b/FirebaseFirestore.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.name = 'FirebaseFirestore' - s.version = '0.9.4' + s.version = '0.10.1' s.summary = 'Google Cloud Firestore for iOS' s.description = <<-DESC @@ -24,7 +24,7 @@ Google Cloud Firestore is a NoSQL document database built for automatic scaling, s.ios.deployment_target = '7.0' - s.cocoapods_version = '>= 1.4.0.beta.2' + s.cocoapods_version = '>= 1.4.0' s.static_framework = true s.prefix_header_file = false @@ -32,9 +32,9 @@ Google Cloud Firestore is a NoSQL document database built for automatic scaling, 'Firestore/Source/**/*', 'Firestore/Port/**/*', 'Firestore/Protos/objc/**/*.[hm]', + 'Firestore/core/include/**/*.{h,cc,mm}', 'Firestore/core/src/**/*.{h,cc,mm}', 'Firestore/third_party/Immutable/*.[mh]', - 'Firestore/third_party/abseil-cpp/absl/*.{h,cc}' ] s.requires_arc = [ 'Firestore/Source/**/*', @@ -46,7 +46,9 @@ Google Cloud Firestore is a NoSQL document database built for automatic scaling, 'Firestore/third_party/Immutable/Tests/**', # Exclude alternate implementations for other platforms - 'Firestore/core/src/firebase/firestore/util/log_stdio.cc' + 'Firestore/core/src/firebase/firestore/util/assert_stdio.cc', + 'Firestore/core/src/firebase/firestore/util/log_stdio.cc', + 'Firestore/core/src/firebase/firestore/util/secure_random_openssl.cc' ] s.public_header_files = 'Firestore/Source/Public/*.h' @@ -62,7 +64,32 @@ Google Cloud Firestore is a NoSQL document database built for automatic scaling, 'GCC_PREPROCESSOR_DEFINITIONS' => 'GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=1 ', 'HEADER_SEARCH_PATHS' => '"${PODS_TARGET_SRCROOT}" ' + - '"${PODS_TARGET_SRCROOT}/Firestore/third_party/abseil-cpp"', + '"${PODS_TARGET_SRCROOT}/Firestore/third_party/abseil-cpp" ' + + '"${PODS_ROOT}/nanopb" ' + + '"${PODS_TARGET_SRCROOT}/Firestore/Protos/nanopb"', 'OTHER_CFLAGS' => '-DFIRFirestore_VERSION=' + s.version.to_s } + + s.prepare_command = <<-CMD + # Generate a version of the config.h header suitable for building with + # CocoaPods. + sed '/^#cmakedefine/ d' \ + Firestore/core/src/firebase/firestore/util/config.h.in > \ + Firestore/core/src/firebase/firestore/util/config.h + CMD + + s.subspec 'abseil-cpp' do |ss| + ss.preserve_path = [ + 'Firestore/third_party/abseil-cpp/absl' + ] + ss.source_files = [ + 'Firestore/third_party/abseil-cpp/**/*.cc' + ] + ss.exclude_files = [ + 'Firestore/third_party/abseil-cpp/**/*_test.cc', + ] + + ss.library = 'c++' + ss.compiler_flags = '$(inherited) ' + '-Wno-comma -Wno-range-loop-analysis' + end end diff --git a/FirebaseFirestoreSwift.podspec b/FirebaseFirestoreSwift.podspec new file mode 100644 index 00000000000..684307065f7 --- /dev/null +++ b/FirebaseFirestoreSwift.podspec @@ -0,0 +1,37 @@ +# +# Be sure to run `pod lib lint FirebaseFirestoreSwift.podspec' to ensure this is a +# valid spec before submitting. +# + +Pod::Spec.new do |s| + s.name = 'FirebaseFirestoreSwift' + s.version = '0.1' + s.summary = 'Google Cloud Firestore for iOS Swift Extensions' + + s.description = <<-DESC +Google Cloud Firestore is a NoSQL document database built for automatic scaling, high performance, and ease of application development. + DESC + + s.homepage = 'https://developers.google.com/' + s.license = { :type => 'Apache', :file => 'LICENSE' } + s.authors = 'Google, Inc.' + + s.source = { + :git => 'https://github.com/Firebase/firebase-ios-sdk.git', + :tag => s.version.to_s + } + + s.swift_version = '4.0' + s.ios.deployment_target = '8.0' + + s.cocoapods_version = '>= 1.4.0' + s.static_framework = true + s.prefix_header_file = false + + s.requires_arc = true + s.source_files = [ + 'Firestore/Swift/Source/**/*.swift', + ] + + s.dependency 'FirebaseFirestore', ">= 0.10.0" +end diff --git a/FirebaseMessaging.podspec b/FirebaseMessaging.podspec index b1fdb68808e..30d9185ef00 100644 --- a/FirebaseMessaging.podspec +++ b/FirebaseMessaging.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseMessaging' - s.version = '2.0.8' + s.version = '2.1.0' s.summary = 'Firebase Messaging for iOS' s.description = <<-DESC @@ -22,7 +22,7 @@ device, and it is completely free. s.ios.deployment_target = '7.0' s.osx.deployment_target = '10.10' - s.cocoapods_version = '>= 1.4.0.beta.2' + s.cocoapods_version = '>= 1.4.0' s.static_framework = true s.prefix_header_file = false diff --git a/FirebaseStorage.podspec b/FirebaseStorage.podspec index 7c3a8f6350d..ce50270c3ae 100644 --- a/FirebaseStorage.podspec +++ b/FirebaseStorage.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseStorage' - s.version = '2.1.1' + s.version = '2.1.2' s.summary = 'Firebase Storage for iOS' s.description = <<-DESC @@ -20,7 +20,7 @@ Firebase Storage provides robust, secure file uploads and downloads from Firebas s.osx.deployment_target = '10.10' s.tvos.deployment_target = '10.0' - s.cocoapods_version = '>= 1.4.0.beta.2' + s.cocoapods_version = '>= 1.4.0' s.static_framework = true s.prefix_header_file = false diff --git a/Firestore/CHANGELOG.md b/Firestore/CHANGELOG.md index 307b71b7917..cf2c067e546 100644 --- a/Firestore/CHANGELOG.md +++ b/Firestore/CHANGELOG.md @@ -4,13 +4,29 @@ server and fall back to the cache (which was the only option previously, and is now the default.) +# v0.10.2 +- [changed] When you delete a FirebaseApp, the associated Firestore instances + are now also deleted (#683). +- [fixed] Fixed race conditions in streams that could be exposed by rapidly + toggling the network from enabled to disabled and back (#772) or encountering + a failure from the server (#835). +- [fixed] Addressed warnings shown by the latest versions of Xcode and CocoaPods. + +# v0.10.1 +- [fixed] Fixed a regression in Firebase iOS release 4.8.1 that could in certain + cases result in an "OnlineState should not affect limbo documents." assertion + crash when the client loses its network connection. +- [fixed] It's now possible to pass a nil completion block to WriteBatch.commit (#745). + # v0.10.0 - [changed] Removed the includeMetadataChanges property in FIRDocumentListenOptions to avoid confusion with the factory method of the same name. - [changed] Added a commit method that takes no completion handler to FIRWriteBatch. - [feature] Queries can now be created from an NSPredicate. -- [added] Added SnapshotOptions API to control how DocumentSnapshots return unresolved +- [feature] Added SnapshotOptions API to control how DocumentSnapshots return unresolved server timestamps. +- [feature] Added `disableNetwork()` and `enableNetwork()` methods to + `Firestore` class, allowing for explicit network management. - [changed] For non-existing documents, DocumentSnapshot.data() now returns `nil` instead of throwing an exception. A non-nullable QueryDocumentSnapshot is introduced for Queries to reduce the number of nil-checks in your code. @@ -21,6 +37,8 @@ connection is restored and the query is in sync with the backend again. - [fixed] Multiple offline mutations now properly reflected in retrieved documents. Previously, only the last mutation would be visible. (#643) +- [fixed] Fixed a crash in `closeWithFinaleState:` that could be triggered by + signing out when the app didn't have a network connection. # v0.9.4 - [changed] Firestore no longer has a direct dependency on FirebaseAuth. diff --git a/Firestore/CMakeLists.txt b/Firestore/CMakeLists.txt index 9b90815da18..28ac08f857a 100644 --- a/Firestore/CMakeLists.txt +++ b/Firestore/CMakeLists.txt @@ -13,9 +13,9 @@ # limitations under the License. cmake_minimum_required(VERSION 2.8.11) -project(firestore) +project(firestore C CXX) -set(FIREBASE_SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/..") +set(FIREBASE_SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/..) # CMAKE_INSTALL_PREFIX should be passed in to this build so that it can find # outputs of the superbuild. This is handled automatically if run via the @@ -23,39 +23,49 @@ set(FIREBASE_SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/..") # # If you want to use this project directly in e.g. CLion, make sure you # configure this. -set(FIREBASE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}") +set(FIREBASE_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}) list(INSERT CMAKE_MODULE_PATH 0 ${FIREBASE_SOURCE_DIR}/cmake) include(utils) -find_package(GTest REQUIRED) +# Include GoogleTest directly in the build. +set(gtest_dir ${FIREBASE_INSTALL_DIR}/external/googletest) +add_subdirectory( + ${gtest_dir}/src/googletest + ${gtest_dir}/src/googletest-build + EXCLUDE_FROM_ALL +) + +# Set up aliases with the same names as available via FindGTest. +add_library( + GTest::GTest ALIAS gtest +) + +add_library( + GTest::Main ALIAS gtest_main +) + find_package(LevelDB REQUIRED) +find_package(GRPC REQUIRED) +find_package(Nanopb REQUIRED) if(APPLE) find_package(FirebaseCore REQUIRED) endif() -# We use C++11 -set(CMAKE_CXX_STANDARD 11) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) +enable_testing() +add_subdirectory(third_party/abseil-cpp) + +include(CompilerSetup) + +# Generated sources will be relative to the binary directory. +include_directories(${FIREBASE_INSTALL_DIR}) # Fully qualified imports, project wide -include_directories("${FIREBASE_SOURCE_DIR}") +include_directories(${FIREBASE_SOURCE_DIR}) -if(APPLE) - # CMake has no special support for Objective-C as a distinct language but enabling modules and - # other clang extensions would apply even to regular C++ sources which is nonportable. Keep these - # flags separate to avoid misuse. - set( - OBJC_FLAGS - -fobjc-arc - -fmodules - -fno-autolink - -F${FIREBASE_INSTALL_DIR}/Frameworks - ) -endif(APPLE) +# Include nanopb generated sources +include_directories(${FIREBASE_SOURCE_DIR}/Firestore/Protos/nanopb) -enable_testing() -add_subdirectory(third_party/abseil-cpp EXCLUDE_FROM_ALL) add_subdirectory(core) +add_subdirectory(Protos) diff --git a/Firestore/Example/Firestore.xcodeproj/project.pbxproj b/Firestore/Example/Firestore.xcodeproj/project.pbxproj index 0bfc8174617..6ae7255dfd5 100644 --- a/Firestore/Example/Firestore.xcodeproj/project.pbxproj +++ b/Firestore/Example/Firestore.xcodeproj/project.pbxproj @@ -13,9 +13,10 @@ buildPhases = ( ); dependencies = ( - DE0761FA1F2FEE7E003233AF /* PBXTargetDependency */, DE29E7FA1F2174DD00909613 /* PBXTargetDependency */, + 54C9EDFF2040E41900A969CD /* PBXTargetDependency */, DE29E7FC1F2174DD00909613 /* PBXTargetDependency */, + DE0761FA1F2FEE7E003233AF /* PBXTargetDependency */, ); name = AllTests; productName = AllTests; @@ -25,12 +26,88 @@ /* Begin PBXBuildFile section */ 3B843E4C1F3A182900548890 /* remote_store_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 3B843E4A1F3930A400548890 /* remote_store_spec_test.json */; }; 5436F32420008FAD006E51E3 /* string_printf_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 5436F32320008FAD006E51E3 /* string_printf_test.cc */; }; + 5467FB01203E5717009C9584 /* FIRFirestoreTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5467FAFF203E56F8009C9584 /* FIRFirestoreTests.mm */; }; + 5467FB08203E6A44009C9584 /* app_testing.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5467FB07203E6A44009C9584 /* app_testing.mm */; }; 54740A571FC914BA00713A1A /* secure_random_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54740A531FC913E500713A1A /* secure_random_test.cc */; }; 54740A581FC914F000713A1A /* autoid_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54740A521FC913E500713A1A /* autoid_test.cc */; }; - 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 */; }; + 548DB927200D590300E00ABC /* assert_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 548DB926200D590300E00ABC /* assert_test.cc */; }; + 548DB929200D59F600E00ABC /* comparison_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 548DB928200D59F600E00ABC /* comparison_test.cc */; }; 5491BC721FB44593008B3588 /* FSTIntegrationTestCase.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5491BC711FB44593008B3588 /* FSTIntegrationTestCase.mm */; }; 5491BC731FB44593008B3588 /* FSTIntegrationTestCase.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5491BC711FB44593008B3588 /* FSTIntegrationTestCase.mm */; }; + 5492E03120213FFC00B64F25 /* FSTLevelDBSpecTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E02C20213FFB00B64F25 /* FSTLevelDBSpecTests.mm */; }; + 5492E03220213FFC00B64F25 /* FSTMockDatastore.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E02D20213FFC00B64F25 /* FSTMockDatastore.mm */; }; + 5492E03320213FFC00B64F25 /* FSTSyncEngineTestDriver.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E02E20213FFC00B64F25 /* FSTSyncEngineTestDriver.mm */; }; + 5492E03420213FFC00B64F25 /* FSTMemorySpecTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E02F20213FFC00B64F25 /* FSTMemorySpecTests.mm */; }; + 5492E03520213FFC00B64F25 /* FSTSpecTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E03020213FFC00B64F25 /* FSTSpecTests.mm */; }; + 5492E03C2021401F00B64F25 /* XCTestCase+Await.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0372021401E00B64F25 /* XCTestCase+Await.mm */; }; + 5492E03D2021401F00B64F25 /* FSTAssertTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0382021401E00B64F25 /* FSTAssertTests.mm */; }; + 5492E03E2021401F00B64F25 /* FSTEventAccumulator.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0392021401F00B64F25 /* FSTEventAccumulator.mm */; }; + 5492E03F2021401F00B64F25 /* FSTHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E03A2021401F00B64F25 /* FSTHelpers.mm */; }; + 5492E041202143E700B64F25 /* FSTEventAccumulator.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0392021401F00B64F25 /* FSTEventAccumulator.mm */; }; + 5492E0422021440500B64F25 /* FSTHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E03A2021401F00B64F25 /* FSTHelpers.mm */; }; + 5492E0442021457E00B64F25 /* XCTestCase+Await.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0372021401E00B64F25 /* XCTestCase+Await.mm */; }; + 5492E050202154AA00B64F25 /* FIRCollectionReferenceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E045202154AA00B64F25 /* FIRCollectionReferenceTests.mm */; }; + 5492E051202154AA00B64F25 /* FIRQueryTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E046202154AA00B64F25 /* FIRQueryTests.mm */; }; + 5492E052202154AB00B64F25 /* FIRGeoPointTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E048202154AA00B64F25 /* FIRGeoPointTests.mm */; }; + 5492E053202154AB00B64F25 /* FIRDocumentReferenceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E049202154AA00B64F25 /* FIRDocumentReferenceTests.mm */; }; + 5492E054202154AB00B64F25 /* FIRFieldValueTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04A202154AA00B64F25 /* FIRFieldValueTests.mm */; }; + 5492E055202154AB00B64F25 /* FIRDocumentSnapshotTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04B202154AA00B64F25 /* FIRDocumentSnapshotTests.mm */; }; + 5492E056202154AB00B64F25 /* FIRFieldPathTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04C202154AA00B64F25 /* FIRFieldPathTests.mm */; }; + 5492E057202154AB00B64F25 /* FIRSnapshotMetadataTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04D202154AA00B64F25 /* FIRSnapshotMetadataTests.mm */; }; + 5492E058202154AB00B64F25 /* FSTAPIHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04E202154AA00B64F25 /* FSTAPIHelpers.mm */; }; + 5492E059202154AB00B64F25 /* FIRQuerySnapshotTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04F202154AA00B64F25 /* FIRQuerySnapshotTests.mm */; }; + 5492E063202154B900B64F25 /* FSTViewSnapshotTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E05C202154B800B64F25 /* FSTViewSnapshotTest.mm */; }; + 5492E064202154B900B64F25 /* FSTQueryListenerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E05D202154B900B64F25 /* FSTQueryListenerTests.mm */; }; + 5492E065202154B900B64F25 /* FSTViewTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E05E202154B900B64F25 /* FSTViewTests.mm */; }; + 5492E067202154B900B64F25 /* FSTEventManagerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E060202154B900B64F25 /* FSTEventManagerTests.mm */; }; + 5492E068202154B900B64F25 /* FSTQueryTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E061202154B900B64F25 /* FSTQueryTests.mm */; }; + 5492E072202154D600B64F25 /* FIRQueryTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E069202154D500B64F25 /* FIRQueryTests.mm */; }; + 5492E073202154D600B64F25 /* FIRFieldsTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E06A202154D500B64F25 /* FIRFieldsTests.mm */; }; + 5492E074202154D600B64F25 /* FIRListenerRegistrationTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E06B202154D500B64F25 /* FIRListenerRegistrationTests.mm */; }; + 5492E075202154D600B64F25 /* FIRDatabaseTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E06C202154D500B64F25 /* FIRDatabaseTests.mm */; }; + 5492E076202154D600B64F25 /* FIRValidationTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E06D202154D600B64F25 /* FIRValidationTests.mm */; }; + 5492E077202154D600B64F25 /* FIRServerTimestampTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E06E202154D600B64F25 /* FIRServerTimestampTests.mm */; }; + 5492E078202154D600B64F25 /* FIRWriteBatchTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E06F202154D600B64F25 /* FIRWriteBatchTests.mm */; }; + 5492E079202154D600B64F25 /* FIRCursorTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E070202154D600B64F25 /* FIRCursorTests.mm */; }; + 5492E07A202154D600B64F25 /* FIRTypeTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E071202154D600B64F25 /* FIRTypeTests.mm */; }; + 5492E07F202154EC00B64F25 /* FSTTransactionTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E07B202154EB00B64F25 /* FSTTransactionTests.mm */; }; + 5492E080202154EC00B64F25 /* FSTSmokeTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E07C202154EB00B64F25 /* FSTSmokeTests.mm */; }; + 5492E081202154EC00B64F25 /* FSTStreamTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E07D202154EB00B64F25 /* FSTStreamTests.mm */; }; + 5492E082202154EC00B64F25 /* FSTDatastoreTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E07E202154EC00B64F25 /* FSTDatastoreTests.mm */; }; + 5492E09D2021552D00B64F25 /* FSTLocalStoreTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0832021552A00B64F25 /* FSTLocalStoreTests.mm */; }; + 5492E09E2021552D00B64F25 /* FSTEagerGarbageCollectorTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0842021552A00B64F25 /* FSTEagerGarbageCollectorTests.mm */; }; + 5492E09F2021552D00B64F25 /* FSTLevelDBMigrationsTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0862021552A00B64F25 /* FSTLevelDBMigrationsTests.mm */; }; + 5492E0A02021552D00B64F25 /* FSTLevelDBMutationQueueTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0872021552A00B64F25 /* FSTLevelDBMutationQueueTests.mm */; }; + 5492E0A12021552D00B64F25 /* FSTMemoryLocalStoreTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0882021552A00B64F25 /* FSTMemoryLocalStoreTests.mm */; }; + 5492E0A22021552D00B64F25 /* FSTQueryCacheTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0892021552A00B64F25 /* FSTQueryCacheTests.mm */; }; + 5492E0A32021552D00B64F25 /* FSTLocalSerializerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E08A2021552A00B64F25 /* FSTLocalSerializerTests.mm */; }; + 5492E0A42021552D00B64F25 /* FSTMemoryQueryCacheTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E08B2021552B00B64F25 /* FSTMemoryQueryCacheTests.mm */; }; + 5492E0A52021552D00B64F25 /* FSTMemoryRemoteDocumentCacheTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E08C2021552B00B64F25 /* FSTMemoryRemoteDocumentCacheTests.mm */; }; + 5492E0A62021552D00B64F25 /* FSTPersistenceTestHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E08D2021552B00B64F25 /* FSTPersistenceTestHelpers.mm */; }; + 5492E0A72021552D00B64F25 /* FSTLevelDBKeyTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E08E2021552B00B64F25 /* FSTLevelDBKeyTests.mm */; }; + 5492E0A82021552D00B64F25 /* FSTLevelDBLocalStoreTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E08F2021552B00B64F25 /* FSTLevelDBLocalStoreTests.mm */; }; + 5492E0A92021552D00B64F25 /* FSTRemoteDocumentChangeBufferTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0902021552B00B64F25 /* FSTRemoteDocumentChangeBufferTests.mm */; }; + 5492E0AA2021552D00B64F25 /* FSTLevelDBRemoteDocumentCacheTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0922021552B00B64F25 /* FSTLevelDBRemoteDocumentCacheTests.mm */; }; + 5492E0AB2021552D00B64F25 /* StringViewTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0932021552B00B64F25 /* StringViewTests.mm */; }; + 5492E0AC2021552D00B64F25 /* FSTMutationQueueTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0962021552C00B64F25 /* FSTMutationQueueTests.mm */; }; + 5492E0AD2021552D00B64F25 /* FSTMemoryMutationQueueTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0972021552C00B64F25 /* FSTMemoryMutationQueueTests.mm */; }; + 5492E0AE2021552D00B64F25 /* FSTLevelDBQueryCacheTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0982021552C00B64F25 /* FSTLevelDBQueryCacheTests.mm */; }; + 5492E0AF2021552D00B64F25 /* FSTReferenceSetTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E09A2021552C00B64F25 /* FSTReferenceSetTests.mm */; }; + 5492E0B02021552D00B64F25 /* FSTWriteGroupTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E09B2021552C00B64F25 /* FSTWriteGroupTests.mm */; }; + 5492E0B12021552D00B64F25 /* FSTRemoteDocumentCacheTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E09C2021552D00B64F25 /* FSTRemoteDocumentCacheTests.mm */; }; + 5492E0B92021555100B64F25 /* FSTDocumentKeyTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0B22021555000B64F25 /* FSTDocumentKeyTests.mm */; }; + 5492E0BA2021555100B64F25 /* FSTDocumentSetTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0B32021555100B64F25 /* FSTDocumentSetTests.mm */; }; + 5492E0BC2021555100B64F25 /* FSTPathTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0B52021555100B64F25 /* FSTPathTests.mm */; }; + 5492E0BD2021555100B64F25 /* FSTDocumentTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0B62021555100B64F25 /* FSTDocumentTests.mm */; }; + 5492E0BE2021555100B64F25 /* FSTMutationTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0B72021555100B64F25 /* FSTMutationTests.mm */; }; + 5492E0BF2021555100B64F25 /* FSTFieldValueTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0B82021555100B64F25 /* FSTFieldValueTests.mm */; }; + 5492E0C62021557E00B64F25 /* FSTWatchChange+Testing.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0C02021557E00B64F25 /* FSTWatchChange+Testing.mm */; }; + 5492E0C72021557E00B64F25 /* FSTSerializerBetaTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0C12021557E00B64F25 /* FSTSerializerBetaTests.mm */; }; + 5492E0C82021557E00B64F25 /* FSTDatastoreTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0C22021557E00B64F25 /* FSTDatastoreTests.mm */; }; + 5492E0C92021557E00B64F25 /* FSTRemoteEventTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0C32021557E00B64F25 /* FSTRemoteEventTests.mm */; }; + 5492E0CA2021557E00B64F25 /* FSTWatchChangeTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0C52021557E00B64F25 /* FSTWatchChangeTests.mm */; }; + 5495EB032040E90200EBA509 /* CodableGeoPointTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5495EB022040E90200EBA509 /* CodableGeoPointTests.swift */; }; 54C2294F1FECABAE007D065B /* log_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54C2294E1FECABAE007D065B /* log_test.cc */; }; 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 */; }; @@ -42,11 +119,7 @@ 54DA12AD1F315EE100DD57A1 /* persistence_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA12A31F315EE100DD57A1 /* persistence_spec_test.json */; }; 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 */; }; - 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 */; }; - 54E9282D1F339CAD00C1953E /* XCTestCase+Await.m in Sources */ = {isa = PBXBuildFile; fileRef = 54E9282B1F339CAD00C1953E /* XCTestCase+Await.m */; }; + 54EB764D202277B30088B8F3 /* array_sorted_map_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54EB764C202277B30088B8F3 /* array_sorted_map_test.cc */; }; 6003F58E195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; 6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58F195388D20070C39A /* CoreGraphics.framework */; }; 6003F592195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; @@ -59,100 +132,65 @@ 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; 6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5B8195388D20070C39A /* InfoPlist.strings */; }; - 61287373203C92C5000EAD3F /* FIRSourceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 61287372203C92C5000EAD3F /* FIRSourceTests.m */; }; - 61E1D8B11FCF6C5700753285 /* StringViewTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 61E1D8AF1FCF6AF500753285 /* StringViewTests.mm */; }; + 6161B5032047140C00A99DBB /* FIRSourceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6161B5012047140400A99DBB /* FIRSourceTests.mm */; }; 6ED54761B845349D43DB6B78 /* Pods_Firestore_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 75A6FE51C1A02DF38F62FAAD /* Pods_Firestore_Example.framework */; }; 71719F9F1E33DC2100824A3D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 71719F9D1E33DC2100824A3D /* LaunchScreen.storyboard */; }; + 7346E61D20325C6900FD6CEF /* FSTDispatchQueueTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7346E61C20325C6900FD6CEF /* FSTDispatchQueueTests.mm */; }; 873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */; }; - AB382F7C1FE02A1F007CA955 /* FIRDocumentReferenceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = AB382F7B1FE02A1F007CA955 /* FIRDocumentReferenceTests.m */; }; - AB382F7E1FE03059007CA955 /* FIRFieldPathTests.m in Sources */ = {isa = PBXBuildFile; fileRef = AB382F7D1FE03059007CA955 /* FIRFieldPathTests.m */; }; - AB9945261FE2D71100DFC1E6 /* FIRCollectionReferenceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = AB9945251FE2D71100DFC1E6 /* FIRCollectionReferenceTests.m */; }; - AB9945281FE2DE0C00DFC1E6 /* FIRSnapshotMetadataTests.m in Sources */ = {isa = PBXBuildFile; fileRef = AB9945271FE2DE0C00DFC1E6 /* FIRSnapshotMetadataTests.m */; }; - AB99452A1FE2F9EB00DFC1E6 /* FIRDocumentSnapshotTests.m in Sources */ = {isa = PBXBuildFile; fileRef = AB9945291FE2F9EB00DFC1E6 /* FIRDocumentSnapshotTests.m */; }; - AB99452C1FE3018D00DFC1E6 /* FIRQuerySnapshotTests.m in Sources */ = {isa = PBXBuildFile; fileRef = AB99452B1FE3018D00DFC1E6 /* FIRQuerySnapshotTests.m */; }; - AB99452E1FE30AC800DFC1E6 /* FIRFieldValueTests.m in Sources */ = {isa = PBXBuildFile; fileRef = AB99452D1FE30AC800DFC1E6 /* FIRFieldValueTests.m */; }; - ABAEEF4F1FD5F8B100C966CB /* FIRQueryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = ABAEEF4E1FD5F8B100C966CB /* FIRQueryTests.m */; }; - ABF341051FE860CA00C48322 /* FSTAPIHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = ABF341021FE8593500C48322 /* FSTAPIHelpers.m */; }; + AB356EF7200EA5EB0089B766 /* field_value_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB356EF6200EA5EB0089B766 /* field_value_test.cc */; }; + AB380CFB2019388600D97691 /* target_id_generator_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380CF82019382300D97691 /* target_id_generator_test.cc */; }; + AB380CFE201A2F4500D97691 /* string_util_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380CFC201A2EE200D97691 /* string_util_test.cc */; }; + AB380D02201BC69F00D97691 /* bits_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380D01201BC69F00D97691 /* bits_test.cc */; }; + AB380D04201BC6E400D97691 /* ordered_code_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380D03201BC6E400D97691 /* ordered_code_test.cc */; }; + AB38D93020236E21000A432D /* database_info_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB38D92E20235D22000A432D /* database_info_test.cc */; }; + AB6B908420322E4D00CC290A /* document_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB6B908320322E4D00CC290A /* document_test.cc */; }; + AB6B908620322E6D00CC290A /* maybe_document_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB6B908520322E6D00CC290A /* maybe_document_test.cc */; }; + AB6B908820322E8800CC290A /* no_document_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB6B908720322E8800CC290A /* no_document_test.cc */; }; + AB7BAB342012B519001E0872 /* geo_point_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB7BAB332012B519001E0872 /* geo_point_test.cc */; }; + ABA495BB202B7E80008A7851 /* snapshot_version_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = ABA495B9202B7E79008A7851 /* snapshot_version_test.cc */; }; + ABC1D7DC2023A04B00BA84F0 /* credentials_provider_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB38D9342023966E000A432D /* credentials_provider_test.cc */; }; + ABC1D7DD2023A04F00BA84F0 /* empty_credentials_provider_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB38D93620239689000A432D /* empty_credentials_provider_test.cc */; }; + ABC1D7DE2023A05300BA84F0 /* user_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB38D93220239654000A432D /* user_test.cc */; }; + ABC1D7E12023A40C00BA84F0 /* token_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = ABC1D7DF2023A3EF00BA84F0 /* token_test.cc */; }; + ABC1D7E42024AFDE00BA84F0 /* firebase_credentials_provider_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = ABC1D7E22023CDC500BA84F0 /* firebase_credentials_provider_test.mm */; }; + ABE6637A201FA81900ED349A /* database_id_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB71064B201FA60300344F18 /* database_id_test.cc */; }; + ABF6506C201131F8005F2C74 /* timestamp_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = ABF6506B201131F8005F2C74 /* timestamp_test.cc */; }; AFE6114F0D4DAECBA7B7C089 /* Pods_Firestore_IntegrationTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B2FA635DF5D116A67A7441CD /* Pods_Firestore_IntegrationTests.framework */; }; + B6152AD7202A53CB000E5744 /* document_key_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6152AD5202A5385000E5744 /* document_key_test.cc */; }; + B65D34A9203C995B0076A5E1 /* FIRTimestampTest.m in Sources */ = {isa = PBXBuildFile; fileRef = B65D34A7203C99090076A5E1 /* FIRTimestampTest.m */; }; + B686F2AF2023DDEE0028D6BE /* field_path_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B686F2AD2023DDB20028D6BE /* field_path_test.cc */; }; + B686F2B22025000D0028D6BE /* resource_path_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B686F2B02024FFD70028D6BE /* resource_path_test.cc */; }; C4E749275AD0FBDF9F4716A8 /* Pods_SwiftBuildTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 32AD40BF6B0E849B07FFD05E /* Pods_SwiftBuildTest.framework */; }; - D5B2532E4676014F57A7EAB9 /* FSTStreamTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D5B25C0D4AADFCA3ADB883E4 /* FSTStreamTests.m */; }; - D5B25474286C9800CE42B8C2 /* FSTTestDispatchQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = D5B25292CED31B81FDED0411 /* FSTTestDispatchQueue.m */; }; - D5B259FDEE8094E8D710C5BF /* FSTTestDispatchQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = D5B25292CED31B81FDED0411 /* FSTTestDispatchQueue.m */; }; - DE03B2C91F2149D600A30B9C /* FSTTransactionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1C61F0D48AC0013853F /* FSTTransactionTests.m */; }; DE03B2D41F2149D600A30B9C /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F5AF195388D20070C39A /* XCTest.framework */; }; DE03B2D51F2149D600A30B9C /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; DE03B2D61F2149D600A30B9C /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; DE03B2D71F2149D600A30B9C /* Pods_Firestore_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 69F6A10DBD6187489481CD76 /* Pods_Firestore_Tests.framework */; }; DE03B2DD1F2149D600A30B9C /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5B8195388D20070C39A /* InfoPlist.strings */; }; - DE03B2EC1F214BA200A30B9C /* FSTDatastoreTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1C41F0D48AC0013853F /* FSTDatastoreTests.m */; }; - DE03B2ED1F214BA200A30B9C /* FSTSmokeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1C51F0D48AC0013853F /* FSTSmokeTests.m */; }; - DE03B2EE1F214BAA00A30B9C /* FIRWriteBatchTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DEFE0F471F1F960A0071599A /* FIRWriteBatchTests.m */; }; - DE03B2EF1F214BAA00A30B9C /* FIRCursorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1BD1F0D48AC0013853F /* FIRCursorTests.m */; }; - DE03B2F01F214BAA00A30B9C /* FIRDatabaseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1BE1F0D48AC0013853F /* FIRDatabaseTests.m */; }; - DE03B2F11F214BAA00A30B9C /* FIRFieldsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1BF1F0D48AC0013853F /* FIRFieldsTests.m */; }; - DE03B2F21F214BAA00A30B9C /* FIRListenerRegistrationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1C01F0D48AC0013853F /* FIRListenerRegistrationTests.m */; }; - DE03B2F31F214BAA00A30B9C /* FIRQueryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1C11F0D48AC0013853F /* FIRQueryTests.m */; }; - DE03B2F41F214BAA00A30B9C /* FIRServerTimestampTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1C21F0D48AC0013853F /* FIRServerTimestampTests.m */; }; - DE03B2F51F214BAA00A30B9C /* FIRTypeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1C31F0D48AC0013853F /* FIRTypeTests.m */; }; - DE03B35E1F21586C00A30B9C /* FSTHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1891F0D48AC0013853F /* FSTHelpers.m */; }; DE03B3631F215E1A00A30B9C /* CAcert.pem in Resources */ = {isa = PBXBuildFile; fileRef = DE03B3621F215E1600A30B9C /* CAcert.pem */; }; DE0761F81F2FE68D003233AF /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE0761F61F2FE68D003233AF /* main.swift */; }; DE2EF0851F3D0B6E003D0CDC /* FSTArraySortedDictionaryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE2EF07E1F3D0B6E003D0CDC /* FSTArraySortedDictionaryTests.m */; }; DE2EF0861F3D0B6E003D0CDC /* FSTImmutableSortedDictionary+Testing.m in Sources */ = {isa = PBXBuildFile; fileRef = DE2EF0801F3D0B6E003D0CDC /* FSTImmutableSortedDictionary+Testing.m */; }; DE2EF0871F3D0B6E003D0CDC /* FSTImmutableSortedSet+Testing.m in Sources */ = {isa = PBXBuildFile; fileRef = DE2EF0821F3D0B6E003D0CDC /* FSTImmutableSortedSet+Testing.m */; }; DE2EF0881F3D0B6E003D0CDC /* FSTTreeSortedDictionaryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE2EF0841F3D0B6E003D0CDC /* FSTTreeSortedDictionaryTests.m */; }; - DE51B1CC1F0D48C00013853F /* FIRGeoPointTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1841F0D48AC0013853F /* FIRGeoPointTests.m */; }; - DE51B1CD1F0D48CD0013853F /* FSTDatabaseInfoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1A91F0D48AC0013853F /* FSTDatabaseInfoTests.m */; }; - DE51B1CE1F0D48CD0013853F /* FSTEventManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1AA1F0D48AC0013853F /* FSTEventManagerTests.m */; }; - DE51B1CF1F0D48CD0013853F /* FSTQueryListenerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1AB1F0D48AC0013853F /* FSTQueryListenerTests.m */; }; - DE51B1D01F0D48CD0013853F /* FSTQueryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1AC1F0D48AC0013853F /* FSTQueryTests.m */; }; - DE51B1D11F0D48CD0013853F /* FSTTargetIDGeneratorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1AE1F0D48AC0013853F /* FSTTargetIDGeneratorTests.m */; }; - DE51B1D21F0D48CD0013853F /* FSTTimestampTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1AF1F0D48AC0013853F /* FSTTimestampTests.m */; }; - DE51B1D31F0D48CD0013853F /* FSTViewSnapshotTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1B01F0D48AC0013853F /* FSTViewSnapshotTest.m */; }; - DE51B1D41F0D48CD0013853F /* FSTViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1B11F0D48AC0013853F /* FSTViewTests.m */; }; - DE51B1D91F0D490D0013853F /* FSTEagerGarbageCollectorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1631F0D48AC0013853F /* FSTEagerGarbageCollectorTests.m */; }; - DE51B1DA1F0D490D0013853F /* FSTLevelDBLocalStoreTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1651F0D48AC0013853F /* FSTLevelDBLocalStoreTests.m */; }; - DE51B1DB1F0D490D0013853F /* FSTLevelDBQueryCacheTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1671F0D48AC0013853F /* FSTLevelDBQueryCacheTests.m */; }; - DE51B1DC1F0D490D0013853F /* FSTLocalSerializerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1691F0D48AC0013853F /* FSTLocalSerializerTests.m */; }; - DE51B1DD1F0D490D0013853F /* FSTLocalStoreTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B16B1F0D48AC0013853F /* FSTLocalStoreTests.m */; }; - DE51B1DE1F0D490D0013853F /* FSTMemoryLocalStoreTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B16C1F0D48AC0013853F /* FSTMemoryLocalStoreTests.m */; }; - DE51B1DF1F0D490D0013853F /* FSTMemoryMutationQueueTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B16D1F0D48AC0013853F /* FSTMemoryMutationQueueTests.m */; }; - DE51B1E01F0D490D0013853F /* FSTMemoryQueryCacheTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B16E1F0D48AC0013853F /* FSTMemoryQueryCacheTests.m */; }; - DE51B1E11F0D490D0013853F /* FSTMemoryRemoteDocumentCacheTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B16F1F0D48AC0013853F /* FSTMemoryRemoteDocumentCacheTests.m */; }; - DE51B1E21F0D490D0013853F /* FSTMutationQueueTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1711F0D48AC0013853F /* FSTMutationQueueTests.m */; }; - DE51B1E31F0D490D0013853F /* FSTPersistenceTestHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1731F0D48AC0013853F /* FSTPersistenceTestHelpers.m */; }; - DE51B1E41F0D490D0013853F /* FSTQueryCacheTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1751F0D48AC0013853F /* FSTQueryCacheTests.m */; }; - DE51B1E51F0D490D0013853F /* FSTReferenceSetTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1761F0D48AC0013853F /* FSTReferenceSetTests.m */; }; - DE51B1E61F0D490D0013853F /* FSTRemoteDocumentCacheTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1781F0D48AC0013853F /* FSTRemoteDocumentCacheTests.m */; }; - DE51B1E71F0D490D0013853F /* FSTRemoteDocumentChangeBufferTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1791F0D48AC0013853F /* FSTRemoteDocumentChangeBufferTests.m */; }; - DE51B1E81F0D490D0013853F /* FSTLevelDBKeyTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1641F0D48AC0013853F /* FSTLevelDBKeyTests.mm */; }; - DE51B1E91F0D490D0013853F /* FSTLevelDBMutationQueueTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1661F0D48AC0013853F /* FSTLevelDBMutationQueueTests.mm */; }; - DE51B1EA1F0D490D0013853F /* FSTLevelDBRemoteDocumentCacheTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1681F0D48AC0013853F /* FSTLevelDBRemoteDocumentCacheTests.mm */; }; - DE51B1EB1F0D490D0013853F /* FSTWriteGroupTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = DE51B17A1F0D48AC0013853F /* FSTWriteGroupTests.mm */; }; - DE51B1EC1F0D49140013853F /* FSTDatabaseIDTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B17C1F0D48AC0013853F /* FSTDatabaseIDTests.m */; }; - DE51B1ED1F0D49140013853F /* FSTDocumentKeyTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B17D1F0D48AC0013853F /* FSTDocumentKeyTests.m */; }; - DE51B1EE1F0D49140013853F /* FSTDocumentSetTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B17E1F0D48AC0013853F /* FSTDocumentSetTests.m */; }; - DE51B1EF1F0D49140013853F /* FSTDocumentTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B17F1F0D48AC0013853F /* FSTDocumentTests.m */; }; - DE51B1F01F0D49140013853F /* FSTFieldValueTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1801F0D48AC0013853F /* FSTFieldValueTests.m */; }; - DE51B1F11F0D49140013853F /* FSTMutationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1811F0D48AC0013853F /* FSTMutationTests.m */; }; - DE51B1F21F0D49140013853F /* FSTPathTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1821F0D48AC0013853F /* FSTPathTests.m */; }; - DE51B1F31F0D491B0013853F /* FSTDatastoreTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1B31F0D48AC0013853F /* FSTDatastoreTests.m */; }; - DE51B1F41F0D491B0013853F /* FSTRemoteEventTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1B41F0D48AC0013853F /* FSTRemoteEventTests.m */; }; - DE51B1F61F0D491B0013853F /* FSTSerializerBetaTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1B61F0D48AC0013853F /* FSTSerializerBetaTests.m */; }; - DE51B1F81F0D491F0013853F /* FSTWatchChange+Testing.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1B91F0D48AC0013853F /* FSTWatchChange+Testing.m */; }; - DE51B1F91F0D491F0013853F /* FSTWatchChangeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1BA1F0D48AC0013853F /* FSTWatchChangeTests.m */; }; - DE51B1FA1F0D492C0013853F /* FSTLevelDBSpecTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1941F0D48AC0013853F /* FSTLevelDBSpecTests.m */; }; - DE51B1FB1F0D492C0013853F /* FSTMemorySpecTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1951F0D48AC0013853F /* FSTMemorySpecTests.m */; }; - DE51B1FC1F0D492C0013853F /* FSTMockDatastore.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1971F0D48AC0013853F /* FSTMockDatastore.m */; }; - DE51B1FD1F0D492C0013853F /* FSTSpecTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1991F0D48AC0013853F /* FSTSpecTests.m */; }; - DE51B1FE1F0D492C0013853F /* FSTSyncEngineTestDriver.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B19B1F0D48AC0013853F /* FSTSyncEngineTestDriver.m */; }; - DE51B1FF1F0D493A0013853F /* FSTAssertTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1861F0D48AC0013853F /* FSTAssertTests.m */; }; - DE51B2001F0D493A0013853F /* FSTComparisonTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1871F0D48AC0013853F /* FSTComparisonTests.m */; }; - DE51B2011F0D493E0013853F /* FSTHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = DE51B1891F0D48AC0013853F /* FSTHelpers.m */; }; + DEF43C59D90C3CF3CA34DDF4 /* Pods_Firestore_Example_Firestore_SwiftTests_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 245812330F6A31632BB4B623 /* Pods_Firestore_Example_Firestore_SwiftTests_iOS.framework */; }; F104BBD69BC3F0796E3A77C1 /* Pods_Firestore_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 69F6A10DBD6187489481CD76 /* Pods_Firestore_Tests.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + 54C9EDF62040E16300A969CD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 6003F582195388D10070C39A /* Project object */; + proxyType = 1; + remoteGlobalIDString = 6003F589195388D20070C39A; + remoteInfo = Firestore_Example; + }; + 54C9EDFE2040E41900A969CD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 6003F582195388D10070C39A /* Project object */; + proxyType = 1; + remoteGlobalIDString = 54C9EDF02040E16300A969CD; + remoteInfo = Firestore_SwiftTests_iOS; + }; 6003F5B3195388D20070C39A /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 6003F582195388D10070C39A /* Project object */; @@ -193,17 +231,103 @@ /* Begin PBXFileReference section */ 04DF37A117F88A9891379ED6 /* Pods-Firestore_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Tests/Pods-Firestore_Tests.release.xcconfig"; sourceTree = ""; }; 12F4357299652983A615F886 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; + 245812330F6A31632BB4B623 /* Pods_Firestore_Example_Firestore_SwiftTests_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_Example_Firestore_SwiftTests_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 32AD40BF6B0E849B07FFD05E /* Pods_SwiftBuildTest.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SwiftBuildTest.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3B843E4A1F3930A400548890 /* remote_store_spec_test.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = remote_store_spec_test.json; sourceTree = ""; }; + 3F422FFBDA6E79396E2FB594 /* Pods-Firestore_Example-Firestore_SwiftTests_iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example-Firestore_SwiftTests_iOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example-Firestore_SwiftTests_iOS/Pods-Firestore_Example-Firestore_SwiftTests_iOS.debug.xcconfig"; 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 = ""; }; 5436F32320008FAD006E51E3 /* string_printf_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = string_printf_test.cc; path = ../../core/test/firebase/firestore/util/string_printf_test.cc; sourceTree = ""; }; + 5467FAFF203E56F8009C9584 /* FIRFirestoreTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRFirestoreTests.mm; sourceTree = ""; }; + 5467FB06203E6A44009C9584 /* app_testing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = app_testing.h; path = ../../core/test/firebase/firestore/testutil/app_testing.h; sourceTree = ""; }; + 5467FB07203E6A44009C9584 /* app_testing.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = app_testing.mm; path = ../../core/test/firebase/firestore/testutil/app_testing.mm; sourceTree = ""; }; 54740A521FC913E500713A1A /* autoid_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = autoid_test.cc; path = ../../core/test/firebase/firestore/util/autoid_test.cc; sourceTree = ""; }; 54740A531FC913E500713A1A /* secure_random_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = secure_random_test.cc; path = ../../core/test/firebase/firestore/util/secure_random_test.cc; 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 = ""; }; + 548DB926200D590300E00ABC /* assert_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = assert_test.cc; path = ../../core/test/firebase/firestore/util/assert_test.cc; sourceTree = ""; }; + 548DB928200D59F600E00ABC /* comparison_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = comparison_test.cc; path = ../../core/test/firebase/firestore/util/comparison_test.cc; sourceTree = ""; }; 5491BC711FB44593008B3588 /* FSTIntegrationTestCase.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTIntegrationTestCase.mm; sourceTree = ""; }; + 5492E02C20213FFB00B64F25 /* FSTLevelDBSpecTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTLevelDBSpecTests.mm; sourceTree = ""; }; + 5492E02D20213FFC00B64F25 /* FSTMockDatastore.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTMockDatastore.mm; sourceTree = ""; }; + 5492E02E20213FFC00B64F25 /* FSTSyncEngineTestDriver.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTSyncEngineTestDriver.mm; sourceTree = ""; }; + 5492E02F20213FFC00B64F25 /* FSTMemorySpecTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTMemorySpecTests.mm; sourceTree = ""; }; + 5492E03020213FFC00B64F25 /* FSTSpecTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTSpecTests.mm; sourceTree = ""; }; + 5492E0372021401E00B64F25 /* XCTestCase+Await.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "XCTestCase+Await.mm"; sourceTree = ""; }; + 5492E0382021401E00B64F25 /* FSTAssertTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTAssertTests.mm; sourceTree = ""; }; + 5492E0392021401F00B64F25 /* FSTEventAccumulator.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTEventAccumulator.mm; sourceTree = ""; }; + 5492E03A2021401F00B64F25 /* FSTHelpers.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTHelpers.mm; sourceTree = ""; }; + 5492E045202154AA00B64F25 /* FIRCollectionReferenceTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRCollectionReferenceTests.mm; sourceTree = ""; }; + 5492E046202154AA00B64F25 /* FIRQueryTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRQueryTests.mm; sourceTree = ""; }; + 5492E047202154AA00B64F25 /* FSTAPIHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSTAPIHelpers.h; sourceTree = ""; }; + 5492E048202154AA00B64F25 /* FIRGeoPointTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRGeoPointTests.mm; sourceTree = ""; }; + 5492E049202154AA00B64F25 /* FIRDocumentReferenceTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRDocumentReferenceTests.mm; sourceTree = ""; }; + 5492E04A202154AA00B64F25 /* FIRFieldValueTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRFieldValueTests.mm; sourceTree = ""; }; + 5492E04B202154AA00B64F25 /* FIRDocumentSnapshotTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRDocumentSnapshotTests.mm; sourceTree = ""; }; + 5492E04C202154AA00B64F25 /* FIRFieldPathTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRFieldPathTests.mm; sourceTree = ""; }; + 5492E04D202154AA00B64F25 /* FIRSnapshotMetadataTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRSnapshotMetadataTests.mm; sourceTree = ""; }; + 5492E04E202154AA00B64F25 /* FSTAPIHelpers.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTAPIHelpers.mm; sourceTree = ""; }; + 5492E04F202154AA00B64F25 /* FIRQuerySnapshotTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRQuerySnapshotTests.mm; sourceTree = ""; }; + 5492E05A202154B800B64F25 /* FSTSyncEngine+Testing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "FSTSyncEngine+Testing.h"; sourceTree = ""; }; + 5492E05C202154B800B64F25 /* FSTViewSnapshotTest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTViewSnapshotTest.mm; sourceTree = ""; }; + 5492E05D202154B900B64F25 /* FSTQueryListenerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTQueryListenerTests.mm; sourceTree = ""; }; + 5492E05E202154B900B64F25 /* FSTViewTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTViewTests.mm; sourceTree = ""; }; + 5492E060202154B900B64F25 /* FSTEventManagerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTEventManagerTests.mm; sourceTree = ""; }; + 5492E061202154B900B64F25 /* FSTQueryTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTQueryTests.mm; sourceTree = ""; }; + 5492E069202154D500B64F25 /* FIRQueryTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRQueryTests.mm; sourceTree = ""; }; + 5492E06A202154D500B64F25 /* FIRFieldsTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRFieldsTests.mm; sourceTree = ""; }; + 5492E06B202154D500B64F25 /* FIRListenerRegistrationTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRListenerRegistrationTests.mm; sourceTree = ""; }; + 5492E06C202154D500B64F25 /* FIRDatabaseTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRDatabaseTests.mm; sourceTree = ""; }; + 5492E06D202154D600B64F25 /* FIRValidationTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRValidationTests.mm; sourceTree = ""; }; + 5492E06E202154D600B64F25 /* FIRServerTimestampTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRServerTimestampTests.mm; sourceTree = ""; }; + 5492E06F202154D600B64F25 /* FIRWriteBatchTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRWriteBatchTests.mm; sourceTree = ""; }; + 5492E070202154D600B64F25 /* FIRCursorTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRCursorTests.mm; sourceTree = ""; }; + 5492E071202154D600B64F25 /* FIRTypeTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRTypeTests.mm; sourceTree = ""; }; + 5492E07B202154EB00B64F25 /* FSTTransactionTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTTransactionTests.mm; sourceTree = ""; }; + 5492E07C202154EB00B64F25 /* FSTSmokeTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTSmokeTests.mm; sourceTree = ""; }; + 5492E07D202154EB00B64F25 /* FSTStreamTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTStreamTests.mm; sourceTree = ""; }; + 5492E07E202154EC00B64F25 /* FSTDatastoreTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTDatastoreTests.mm; sourceTree = ""; }; + 5492E0832021552A00B64F25 /* FSTLocalStoreTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTLocalStoreTests.mm; sourceTree = ""; }; + 5492E0842021552A00B64F25 /* FSTEagerGarbageCollectorTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTEagerGarbageCollectorTests.mm; sourceTree = ""; }; + 5492E0852021552A00B64F25 /* FSTRemoteDocumentCacheTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSTRemoteDocumentCacheTests.h; sourceTree = ""; }; + 5492E0862021552A00B64F25 /* FSTLevelDBMigrationsTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTLevelDBMigrationsTests.mm; sourceTree = ""; }; + 5492E0872021552A00B64F25 /* FSTLevelDBMutationQueueTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTLevelDBMutationQueueTests.mm; sourceTree = ""; }; + 5492E0882021552A00B64F25 /* FSTMemoryLocalStoreTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTMemoryLocalStoreTests.mm; sourceTree = ""; }; + 5492E0892021552A00B64F25 /* FSTQueryCacheTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTQueryCacheTests.mm; sourceTree = ""; }; + 5492E08A2021552A00B64F25 /* FSTLocalSerializerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTLocalSerializerTests.mm; sourceTree = ""; }; + 5492E08B2021552B00B64F25 /* FSTMemoryQueryCacheTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTMemoryQueryCacheTests.mm; sourceTree = ""; }; + 5492E08C2021552B00B64F25 /* FSTMemoryRemoteDocumentCacheTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTMemoryRemoteDocumentCacheTests.mm; sourceTree = ""; }; + 5492E08D2021552B00B64F25 /* FSTPersistenceTestHelpers.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTPersistenceTestHelpers.mm; sourceTree = ""; }; + 5492E08E2021552B00B64F25 /* FSTLevelDBKeyTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTLevelDBKeyTests.mm; sourceTree = ""; }; + 5492E08F2021552B00B64F25 /* FSTLevelDBLocalStoreTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTLevelDBLocalStoreTests.mm; sourceTree = ""; }; + 5492E0902021552B00B64F25 /* FSTRemoteDocumentChangeBufferTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTRemoteDocumentChangeBufferTests.mm; sourceTree = ""; }; + 5492E0912021552B00B64F25 /* FSTLocalStoreTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSTLocalStoreTests.h; sourceTree = ""; }; + 5492E0922021552B00B64F25 /* FSTLevelDBRemoteDocumentCacheTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTLevelDBRemoteDocumentCacheTests.mm; sourceTree = ""; }; + 5492E0932021552B00B64F25 /* StringViewTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = StringViewTests.mm; sourceTree = ""; }; + 5492E0942021552C00B64F25 /* FSTMutationQueueTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSTMutationQueueTests.h; sourceTree = ""; }; + 5492E0952021552C00B64F25 /* FSTQueryCacheTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSTQueryCacheTests.h; sourceTree = ""; }; + 5492E0962021552C00B64F25 /* FSTMutationQueueTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTMutationQueueTests.mm; sourceTree = ""; }; + 5492E0972021552C00B64F25 /* FSTMemoryMutationQueueTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTMemoryMutationQueueTests.mm; sourceTree = ""; }; + 5492E0982021552C00B64F25 /* FSTLevelDBQueryCacheTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTLevelDBQueryCacheTests.mm; sourceTree = ""; }; + 5492E0992021552C00B64F25 /* FSTPersistenceTestHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSTPersistenceTestHelpers.h; sourceTree = ""; }; + 5492E09A2021552C00B64F25 /* FSTReferenceSetTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTReferenceSetTests.mm; sourceTree = ""; }; + 5492E09B2021552C00B64F25 /* FSTWriteGroupTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTWriteGroupTests.mm; sourceTree = ""; }; + 5492E09C2021552D00B64F25 /* FSTRemoteDocumentCacheTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTRemoteDocumentCacheTests.mm; sourceTree = ""; }; + 5492E0B22021555000B64F25 /* FSTDocumentKeyTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTDocumentKeyTests.mm; sourceTree = ""; }; + 5492E0B32021555100B64F25 /* FSTDocumentSetTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTDocumentSetTests.mm; sourceTree = ""; }; + 5492E0B52021555100B64F25 /* FSTPathTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTPathTests.mm; sourceTree = ""; }; + 5492E0B62021555100B64F25 /* FSTDocumentTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTDocumentTests.mm; sourceTree = ""; }; + 5492E0B72021555100B64F25 /* FSTMutationTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTMutationTests.mm; sourceTree = ""; }; + 5492E0B82021555100B64F25 /* FSTFieldValueTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTFieldValueTests.mm; sourceTree = ""; }; + 5492E0C02021557E00B64F25 /* FSTWatchChange+Testing.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "FSTWatchChange+Testing.mm"; sourceTree = ""; }; + 5492E0C12021557E00B64F25 /* FSTSerializerBetaTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTSerializerBetaTests.mm; sourceTree = ""; }; + 5492E0C22021557E00B64F25 /* FSTDatastoreTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTDatastoreTests.mm; sourceTree = ""; }; + 5492E0C32021557E00B64F25 /* FSTRemoteEventTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTRemoteEventTests.mm; sourceTree = ""; }; + 5492E0C42021557E00B64F25 /* FSTWatchChange+Testing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "FSTWatchChange+Testing.h"; sourceTree = ""; }; + 5492E0C52021557E00B64F25 /* FSTWatchChangeTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTWatchChangeTests.mm; sourceTree = ""; }; + 5495EB022040E90200EBA509 /* CodableGeoPointTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CodableGeoPointTests.swift; sourceTree = ""; }; 54C2294E1FECABAE007D065B /* log_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = log_test.cc; path = ../../core/test/firebase/firestore/util/log_test.cc; sourceTree = ""; }; + 54C9EDF12040E16300A969CD /* Firestore_SwiftTests_iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Firestore_SwiftTests_iOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 54C9EDF52040E16300A969CD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; 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 = ""; }; @@ -214,12 +338,10 @@ 54DA12A31F315EE100DD57A1 /* persistence_spec_test.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = persistence_spec_test.json; sourceTree = ""; }; 54DA12A41F315EE100DD57A1 /* resume_token_spec_test.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = resume_token_spec_test.json; sourceTree = ""; }; 54DA12A51F315EE100DD57A1 /* write_spec_test.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = write_spec_test.json; sourceTree = ""; }; - 54DA12B01F315F3800DD57A1 /* FIRValidationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRValidationTests.m; sourceTree = ""; }; 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 = ""; }; 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 = ""; }; + 54EB764C202277B30088B8F3 /* array_sorted_map_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = array_sorted_map_test.cc; path = ../../immutable/array_sorted_map_test.cc; sourceTree = ""; }; 6003F58A195388D20070C39A /* Firestore_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Firestore_Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 6003F58D195388D20070C39A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 6003F58F195388D20070C39A /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; @@ -236,31 +358,41 @@ 6003F5AF195388D20070C39A /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 6003F5B7195388D20070C39A /* Tests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Tests-Info.plist"; sourceTree = ""; }; 6003F5B9195388D20070C39A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; - 61287372203C92C5000EAD3F /* FIRSourceTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRSourceTests.m; sourceTree = ""; }; - 61E1D8AF1FCF6AF500753285 /* StringViewTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = StringViewTests.mm; sourceTree = ""; }; + 6161B5012047140400A99DBB /* FIRSourceTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRSourceTests.mm; sourceTree = ""; }; 69F6A10DBD6187489481CD76 /* Pods_Firestore_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 71719F9E1E33DC2100824A3D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 7346E61C20325C6900FD6CEF /* FSTDispatchQueueTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTDispatchQueueTests.mm; sourceTree = ""; }; 75A6FE51C1A02DF38F62FAAD /* Pods_Firestore_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = Main.storyboard; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 8E002F4AD5D9B6197C940847 /* Firestore.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = Firestore.podspec; path = ../Firestore.podspec; sourceTree = ""; }; 9D52E67EE96AA7E5D6F69748 /* Pods-Firestore_IntegrationTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_IntegrationTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_IntegrationTests/Pods-Firestore_IntegrationTests.debug.xcconfig"; sourceTree = ""; }; 9EF477AD4B2B643FD320867A /* Pods-Firestore_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example/Pods-Firestore_Example.debug.xcconfig"; sourceTree = ""; }; - AB382F7B1FE02A1F007CA955 /* FIRDocumentReferenceTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRDocumentReferenceTests.m; sourceTree = ""; }; - AB382F7D1FE03059007CA955 /* FIRFieldPathTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRFieldPathTests.m; sourceTree = ""; }; - AB9945251FE2D71100DFC1E6 /* FIRCollectionReferenceTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIRCollectionReferenceTests.m; sourceTree = ""; }; - AB9945271FE2DE0C00DFC1E6 /* FIRSnapshotMetadataTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIRSnapshotMetadataTests.m; sourceTree = ""; }; - AB9945291FE2F9EB00DFC1E6 /* FIRDocumentSnapshotTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIRDocumentSnapshotTests.m; sourceTree = ""; }; - AB99452B1FE3018D00DFC1E6 /* FIRQuerySnapshotTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIRQuerySnapshotTests.m; sourceTree = ""; }; - AB99452D1FE30AC800DFC1E6 /* FIRFieldValueTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIRFieldValueTests.m; sourceTree = ""; }; - ABAEEF4E1FD5F8B100C966CB /* FIRQueryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRQueryTests.m; sourceTree = ""; }; - ABF341011FE858B500C48322 /* FSTAPIHelpers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FSTAPIHelpers.h; sourceTree = ""; }; - ABF341021FE8593500C48322 /* FSTAPIHelpers.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTAPIHelpers.m; sourceTree = ""; }; + AB356EF6200EA5EB0089B766 /* field_value_test.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = field_value_test.cc; sourceTree = ""; }; + AB380CF82019382300D97691 /* target_id_generator_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = target_id_generator_test.cc; sourceTree = ""; }; + AB380CFC201A2EE200D97691 /* string_util_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = string_util_test.cc; path = ../../core/test/firebase/firestore/util/string_util_test.cc; sourceTree = ""; }; + AB380D01201BC69F00D97691 /* bits_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = bits_test.cc; path = ../../core/test/firebase/firestore/util/bits_test.cc; sourceTree = ""; }; + AB380D03201BC6E400D97691 /* ordered_code_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ordered_code_test.cc; path = ../../core/test/firebase/firestore/util/ordered_code_test.cc; sourceTree = ""; }; + AB38D92E20235D22000A432D /* database_info_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = database_info_test.cc; sourceTree = ""; }; + AB38D93220239654000A432D /* user_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = user_test.cc; sourceTree = ""; }; + AB38D9342023966E000A432D /* credentials_provider_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = credentials_provider_test.cc; sourceTree = ""; }; + AB38D93620239689000A432D /* empty_credentials_provider_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = empty_credentials_provider_test.cc; sourceTree = ""; }; + AB6B908320322E4D00CC290A /* document_test.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = document_test.cc; sourceTree = ""; }; + AB6B908520322E6D00CC290A /* maybe_document_test.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = maybe_document_test.cc; sourceTree = ""; }; + AB6B908720322E8800CC290A /* no_document_test.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = no_document_test.cc; sourceTree = ""; }; + AB71064B201FA60300344F18 /* database_id_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = database_id_test.cc; sourceTree = ""; }; + AB7BAB332012B519001E0872 /* geo_point_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = geo_point_test.cc; path = ../../core/test/firebase/firestore/geo_point_test.cc; sourceTree = ""; }; + ABA495B9202B7E79008A7851 /* snapshot_version_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = snapshot_version_test.cc; sourceTree = ""; }; + ABC1D7DF2023A3EF00BA84F0 /* token_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = token_test.cc; sourceTree = ""; }; + ABC1D7E22023CDC500BA84F0 /* firebase_credentials_provider_test.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = firebase_credentials_provider_test.mm; sourceTree = ""; }; + ABF6506B201131F8005F2C74 /* timestamp_test.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = timestamp_test.cc; sourceTree = ""; }; B2FA635DF5D116A67A7441CD /* Pods_Firestore_IntegrationTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_IntegrationTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + B6152AD5202A5385000E5744 /* document_key_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = document_key_test.cc; sourceTree = ""; }; + B65D34A7203C99090076A5E1 /* FIRTimestampTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRTimestampTest.m; sourceTree = ""; }; + B686F2AD2023DDB20028D6BE /* field_path_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = field_path_test.cc; sourceTree = ""; }; + B686F2B02024FFD70028D6BE /* resource_path_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = resource_path_test.cc; sourceTree = ""; }; + C1D89E5405935366C88CC3E5 /* Pods-Firestore_Example-Firestore_SwiftTests_iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example-Firestore_SwiftTests_iOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example-Firestore_SwiftTests_iOS/Pods-Firestore_Example-Firestore_SwiftTests_iOS.release.xcconfig"; sourceTree = ""; }; CE00BABB5A3AAB44A4C209E2 /* Pods-Firestore_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Tests/Pods-Firestore_Tests.debug.xcconfig"; sourceTree = ""; }; D3CC3DC5338DCAF43A211155 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; - D5B25292CED31B81FDED0411 /* FSTTestDispatchQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FSTTestDispatchQueue.m; sourceTree = ""; }; - D5B259DAA9149B80D6245B57 /* FSTTestDispatchQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSTTestDispatchQueue.h; sourceTree = ""; }; - D5B25C0D4AADFCA3ADB883E4 /* FSTStreamTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FSTStreamTests.m; sourceTree = ""; }; DB17FEDFB80770611A935A60 /* Pods-Firestore_IntegrationTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_IntegrationTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_IntegrationTests/Pods-Firestore_IntegrationTests.release.xcconfig"; sourceTree = ""; }; DE03B2E91F2149D600A30B9C /* Firestore_IntegrationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Firestore_IntegrationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; DE03B3621F215E1600A30B9C /* CAcert.pem */ = {isa = PBXFileReference; lastKnownFileType = text; path = CAcert.pem; sourceTree = ""; }; @@ -273,81 +405,23 @@ DE2EF0821F3D0B6E003D0CDC /* FSTImmutableSortedSet+Testing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "FSTImmutableSortedSet+Testing.m"; path = "../../third_party/Immutable/Tests/FSTImmutableSortedSet+Testing.m"; sourceTree = ""; }; DE2EF0831F3D0B6E003D0CDC /* FSTLLRBValueNode+Test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "FSTLLRBValueNode+Test.h"; path = "../../third_party/Immutable/Tests/FSTLLRBValueNode+Test.h"; sourceTree = ""; }; DE2EF0841F3D0B6E003D0CDC /* FSTTreeSortedDictionaryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FSTTreeSortedDictionaryTests.m; path = ../../third_party/Immutable/Tests/FSTTreeSortedDictionaryTests.m; sourceTree = ""; }; - DE51B1631F0D48AC0013853F /* FSTEagerGarbageCollectorTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTEagerGarbageCollectorTests.m; sourceTree = ""; }; - DE51B1641F0D48AC0013853F /* FSTLevelDBKeyTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTLevelDBKeyTests.mm; sourceTree = ""; }; - DE51B1651F0D48AC0013853F /* FSTLevelDBLocalStoreTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTLevelDBLocalStoreTests.m; sourceTree = ""; }; - DE51B1661F0D48AC0013853F /* FSTLevelDBMutationQueueTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTLevelDBMutationQueueTests.mm; sourceTree = ""; }; - DE51B1671F0D48AC0013853F /* FSTLevelDBQueryCacheTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTLevelDBQueryCacheTests.m; sourceTree = ""; }; - DE51B1681F0D48AC0013853F /* FSTLevelDBRemoteDocumentCacheTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTLevelDBRemoteDocumentCacheTests.mm; sourceTree = ""; }; - DE51B1691F0D48AC0013853F /* FSTLocalSerializerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTLocalSerializerTests.m; sourceTree = ""; }; - DE51B16A1F0D48AC0013853F /* FSTLocalStoreTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FSTLocalStoreTests.h; sourceTree = ""; }; - DE51B16B1F0D48AC0013853F /* FSTLocalStoreTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTLocalStoreTests.m; sourceTree = ""; }; - DE51B16C1F0D48AC0013853F /* FSTMemoryLocalStoreTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTMemoryLocalStoreTests.m; sourceTree = ""; }; - DE51B16D1F0D48AC0013853F /* FSTMemoryMutationQueueTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTMemoryMutationQueueTests.m; sourceTree = ""; }; - DE51B16E1F0D48AC0013853F /* FSTMemoryQueryCacheTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTMemoryQueryCacheTests.m; sourceTree = ""; }; - DE51B16F1F0D48AC0013853F /* FSTMemoryRemoteDocumentCacheTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTMemoryRemoteDocumentCacheTests.m; sourceTree = ""; }; - DE51B1701F0D48AC0013853F /* FSTMutationQueueTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FSTMutationQueueTests.h; sourceTree = ""; }; - DE51B1711F0D48AC0013853F /* FSTMutationQueueTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTMutationQueueTests.m; sourceTree = ""; }; - DE51B1721F0D48AC0013853F /* FSTPersistenceTestHelpers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FSTPersistenceTestHelpers.h; sourceTree = ""; }; - DE51B1731F0D48AC0013853F /* FSTPersistenceTestHelpers.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTPersistenceTestHelpers.m; sourceTree = ""; }; - DE51B1741F0D48AC0013853F /* FSTQueryCacheTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FSTQueryCacheTests.h; sourceTree = ""; }; - DE51B1751F0D48AC0013853F /* FSTQueryCacheTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTQueryCacheTests.m; sourceTree = ""; }; - DE51B1761F0D48AC0013853F /* FSTReferenceSetTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTReferenceSetTests.m; sourceTree = ""; }; - DE51B1771F0D48AC0013853F /* FSTRemoteDocumentCacheTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FSTRemoteDocumentCacheTests.h; sourceTree = ""; }; - DE51B1781F0D48AC0013853F /* FSTRemoteDocumentCacheTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTRemoteDocumentCacheTests.m; sourceTree = ""; }; - DE51B1791F0D48AC0013853F /* FSTRemoteDocumentChangeBufferTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTRemoteDocumentChangeBufferTests.m; sourceTree = ""; }; - DE51B17A1F0D48AC0013853F /* FSTWriteGroupTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTWriteGroupTests.mm; sourceTree = ""; }; - DE51B17C1F0D48AC0013853F /* FSTDatabaseIDTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTDatabaseIDTests.m; sourceTree = ""; }; - DE51B17D1F0D48AC0013853F /* FSTDocumentKeyTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTDocumentKeyTests.m; sourceTree = ""; }; - DE51B17E1F0D48AC0013853F /* FSTDocumentSetTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTDocumentSetTests.m; sourceTree = ""; }; - DE51B17F1F0D48AC0013853F /* FSTDocumentTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTDocumentTests.m; sourceTree = ""; }; - DE51B1801F0D48AC0013853F /* FSTFieldValueTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTFieldValueTests.m; sourceTree = ""; }; - DE51B1811F0D48AC0013853F /* FSTMutationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTMutationTests.m; sourceTree = ""; }; - DE51B1821F0D48AC0013853F /* FSTPathTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTPathTests.m; sourceTree = ""; }; - DE51B1841F0D48AC0013853F /* FIRGeoPointTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIRGeoPointTests.m; sourceTree = ""; }; - DE51B1861F0D48AC0013853F /* FSTAssertTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTAssertTests.m; sourceTree = ""; }; - DE51B1871F0D48AC0013853F /* FSTComparisonTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTComparisonTests.m; sourceTree = ""; }; DE51B1881F0D48AC0013853F /* FSTHelpers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FSTHelpers.h; sourceTree = ""; }; - DE51B1891F0D48AC0013853F /* FSTHelpers.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTHelpers.m; sourceTree = ""; }; - DE51B1941F0D48AC0013853F /* FSTLevelDBSpecTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTLevelDBSpecTests.m; sourceTree = ""; }; - DE51B1951F0D48AC0013853F /* FSTMemorySpecTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTMemorySpecTests.m; sourceTree = ""; }; DE51B1961F0D48AC0013853F /* FSTMockDatastore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FSTMockDatastore.h; sourceTree = ""; }; - DE51B1971F0D48AC0013853F /* FSTMockDatastore.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTMockDatastore.m; sourceTree = ""; }; DE51B1981F0D48AC0013853F /* FSTSpecTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FSTSpecTests.h; sourceTree = ""; }; - DE51B1991F0D48AC0013853F /* FSTSpecTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTSpecTests.m; sourceTree = ""; }; DE51B19A1F0D48AC0013853F /* FSTSyncEngineTestDriver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FSTSyncEngineTestDriver.h; sourceTree = ""; }; - DE51B19B1F0D48AC0013853F /* FSTSyncEngineTestDriver.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTSyncEngineTestDriver.m; sourceTree = ""; }; DE51B1A71F0D48AC0013853F /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; - DE51B1A91F0D48AC0013853F /* FSTDatabaseInfoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTDatabaseInfoTests.m; sourceTree = ""; }; - DE51B1AA1F0D48AC0013853F /* FSTEventManagerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTEventManagerTests.m; sourceTree = ""; }; - DE51B1AB1F0D48AC0013853F /* FSTQueryListenerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTQueryListenerTests.m; sourceTree = ""; }; - DE51B1AC1F0D48AC0013853F /* FSTQueryTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTQueryTests.m; sourceTree = ""; }; - DE51B1AD1F0D48AC0013853F /* FSTSyncEngine+Testing.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FSTSyncEngine+Testing.h"; sourceTree = ""; }; - DE51B1AE1F0D48AC0013853F /* FSTTargetIDGeneratorTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTTargetIDGeneratorTests.m; sourceTree = ""; }; - DE51B1AF1F0D48AC0013853F /* FSTTimestampTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTTimestampTests.m; sourceTree = ""; }; - DE51B1B01F0D48AC0013853F /* FSTViewSnapshotTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTViewSnapshotTest.m; sourceTree = ""; }; - DE51B1B11F0D48AC0013853F /* FSTViewTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTViewTests.m; sourceTree = ""; }; - DE51B1B31F0D48AC0013853F /* FSTDatastoreTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTDatastoreTests.m; sourceTree = ""; }; - DE51B1B41F0D48AC0013853F /* FSTRemoteEventTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTRemoteEventTests.m; sourceTree = ""; }; - DE51B1B61F0D48AC0013853F /* FSTSerializerBetaTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTSerializerBetaTests.m; sourceTree = ""; }; - DE51B1B81F0D48AC0013853F /* FSTWatchChange+Testing.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FSTWatchChange+Testing.h"; sourceTree = ""; }; - DE51B1B91F0D48AC0013853F /* FSTWatchChange+Testing.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "FSTWatchChange+Testing.m"; sourceTree = ""; }; - DE51B1BA1F0D48AC0013853F /* FSTWatchChangeTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTWatchChangeTests.m; sourceTree = ""; }; - DE51B1BD1F0D48AC0013853F /* FIRCursorTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIRCursorTests.m; sourceTree = ""; }; - DE51B1BE1F0D48AC0013853F /* FIRDatabaseTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIRDatabaseTests.m; sourceTree = ""; }; - DE51B1BF1F0D48AC0013853F /* FIRFieldsTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIRFieldsTests.m; sourceTree = ""; }; - DE51B1C01F0D48AC0013853F /* FIRListenerRegistrationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIRListenerRegistrationTests.m; sourceTree = ""; }; - DE51B1C11F0D48AC0013853F /* FIRQueryTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIRQueryTests.m; sourceTree = ""; }; - DE51B1C21F0D48AC0013853F /* FIRServerTimestampTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIRServerTimestampTests.m; sourceTree = ""; }; - DE51B1C31F0D48AC0013853F /* FIRTypeTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIRTypeTests.m; sourceTree = ""; }; - DE51B1C41F0D48AC0013853F /* FSTDatastoreTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTDatastoreTests.m; sourceTree = ""; }; - DE51B1C51F0D48AC0013853F /* FSTSmokeTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTSmokeTests.m; sourceTree = ""; }; - DE51B1C61F0D48AC0013853F /* FSTTransactionTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTTransactionTests.m; sourceTree = ""; }; - DEFE0F471F1F960A0071599A /* FIRWriteBatchTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRWriteBatchTests.m; sourceTree = ""; }; F23325524BEAF8D24F78AC88 /* Pods-SwiftBuildTest.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwiftBuildTest.release.xcconfig"; path = "Pods/Target Support Files/Pods-SwiftBuildTest/Pods-SwiftBuildTest.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 54C9EDEE2040E16300A969CD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + DEF43C59D90C3CF3CA34DDF4 /* Pods_Firestore_Example_Firestore_SwiftTests_iOS.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 6003F587195388D20070C39A /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -393,13 +467,27 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 5467FB05203E652F009C9584 /* testutil */ = { + isa = PBXGroup; + children = ( + 5467FB06203E6A44009C9584 /* app_testing.h */, + 5467FB07203E6A44009C9584 /* app_testing.mm */, + ); + name = testutil; + sourceTree = ""; + }; 54740A561FC913EB00713A1A /* util */ = { isa = PBXGroup; children = ( + 548DB926200D590300E00ABC /* assert_test.cc */, 54740A521FC913E500713A1A /* autoid_test.cc */, + AB380D01201BC69F00D97691 /* bits_test.cc */, + 548DB928200D59F600E00ABC /* comparison_test.cc */, 54C2294E1FECABAE007D065B /* log_test.cc */, + AB380D03201BC6E400D97691 /* ordered_code_test.cc */, 54740A531FC913E500713A1A /* secure_random_test.cc */, 5436F32320008FAD006E51E3 /* string_printf_test.cc */, + AB380CFC201A2EE200D97691 /* string_util_test.cc */, ); name = util; sourceTree = ""; @@ -407,19 +495,43 @@ 54764FAC1FAA0C390085E60A /* GoogleTests */ = { isa = PBXGroup; children = ( - 54764FAD1FAA0C650085E60A /* Port */, + AB38D9312023962A000A432D /* auth */, + AB380CF7201937B800D97691 /* core */, + 54EB764B202277970088B8F3 /* immutable */, + AB356EF5200E9D1A0089B766 /* model */, + 5467FB05203E652F009C9584 /* testutil */, 54740A561FC913EB00713A1A /* util */, 54764FAE1FAA21B90085E60A /* FSTGoogleTestTests.mm */, + AB7BAB332012B519001E0872 /* geo_point_test.cc */, ); name = GoogleTests; sourceTree = ""; }; - 54764FAD1FAA0C650085E60A /* Port */ = { + 5495EB012040E90200EBA509 /* Codable */ = { + isa = PBXGroup; + children = ( + 5495EB022040E90200EBA509 /* CodableGeoPointTests.swift */, + ); + path = Codable; + sourceTree = ""; + }; + 54C9EDF22040E16300A969CD /* SwiftTests */ = { + isa = PBXGroup; + children = ( + 5495EB012040E90200EBA509 /* Codable */, + 54C9EDF52040E16300A969CD /* Info.plist */, + ); + name = SwiftTests; + path = ../Swift/Tests; + sourceTree = ""; + }; + 54EB764B202277970088B8F3 /* immutable */ = { isa = PBXGroup; children = ( - 54764FAA1FAA0C320085E60A /* string_util_test.cc */, + 54EB764C202277B30088B8F3 /* array_sorted_map_test.cc */, ); - name = Port; + name = immutable; + path = ../../core/test/firebase/firestore/core/immutable; sourceTree = ""; }; 6003F581195388D10070C39A = { @@ -428,6 +540,7 @@ 60FF7A9C1954A5C5007DD14C /* Podspec Metadata */, 6003F593195388D20070C39A /* Example for Firestore */, 6003F5B5195388D20070C39A /* Tests */, + 54C9EDF22040E16300A969CD /* SwiftTests */, DE0761E51F2FE611003233AF /* SwiftBuildTest */, 6003F58C195388D20070C39A /* Frameworks */, 6003F58B195388D20070C39A /* Products */, @@ -442,6 +555,7 @@ 6003F5AE195388D20070C39A /* Firestore_Tests.xctest */, DE03B2E91F2149D600A30B9C /* Firestore_IntegrationTests.xctest */, DE0761E41F2FE611003233AF /* SwiftBuildTest.app */, + 54C9EDF12040E16300A969CD /* Firestore_SwiftTests_iOS.xctest */, ); name = Products; sourceTree = ""; @@ -457,6 +571,7 @@ 69F6A10DBD6187489481CD76 /* Pods_Firestore_Tests.framework */, B2FA635DF5D116A67A7441CD /* Pods_Firestore_IntegrationTests.framework */, 32AD40BF6B0E849B07FFD05E /* Pods_SwiftBuildTest.framework */, + 245812330F6A31632BB4B623 /* Pods_Firestore_Example_Firestore_SwiftTests_iOS.framework */, ); name = Frameworks; sourceTree = ""; @@ -535,10 +650,53 @@ 04DF37A117F88A9891379ED6 /* Pods-Firestore_Tests.release.xcconfig */, 42491D7DC8C8CD245CC22B93 /* Pods-SwiftBuildTest.debug.xcconfig */, F23325524BEAF8D24F78AC88 /* Pods-SwiftBuildTest.release.xcconfig */, + 3F422FFBDA6E79396E2FB594 /* Pods-Firestore_Example-Firestore_SwiftTests_iOS.debug.xcconfig */, + C1D89E5405935366C88CC3E5 /* Pods-Firestore_Example-Firestore_SwiftTests_iOS.release.xcconfig */, ); name = Pods; sourceTree = ""; }; + AB356EF5200E9D1A0089B766 /* model */ = { + isa = PBXGroup; + children = ( + B6152AD5202A5385000E5744 /* document_key_test.cc */, + AB6B908320322E4D00CC290A /* document_test.cc */, + B686F2B02024FFD70028D6BE /* resource_path_test.cc */, + B686F2AD2023DDB20028D6BE /* field_path_test.cc */, + AB71064B201FA60300344F18 /* database_id_test.cc */, + AB356EF6200EA5EB0089B766 /* field_value_test.cc */, + AB6B908520322E6D00CC290A /* maybe_document_test.cc */, + AB6B908720322E8800CC290A /* no_document_test.cc */, + ABA495B9202B7E79008A7851 /* snapshot_version_test.cc */, + ABF6506B201131F8005F2C74 /* timestamp_test.cc */, + ); + name = model; + path = ../../core/test/firebase/firestore/model; + sourceTree = ""; + }; + AB380CF7201937B800D97691 /* core */ = { + isa = PBXGroup; + children = ( + AB380CF82019382300D97691 /* target_id_generator_test.cc */, + AB38D92E20235D22000A432D /* database_info_test.cc */, + ); + name = core; + path = ../../core/test/firebase/firestore/core; + sourceTree = ""; + }; + AB38D9312023962A000A432D /* auth */ = { + isa = PBXGroup; + children = ( + AB38D9342023966E000A432D /* credentials_provider_test.cc */, + AB38D93620239689000A432D /* empty_credentials_provider_test.cc */, + ABC1D7E22023CDC500BA84F0 /* firebase_credentials_provider_test.mm */, + ABC1D7DF2023A3EF00BA84F0 /* token_test.cc */, + AB38D93220239654000A432D /* user_test.cc */, + ); + name = auth; + path = ../../core/test/firebase/firestore/auth; + sourceTree = ""; + }; DE0761E51F2FE611003233AF /* SwiftBuildTest */ = { isa = PBXGroup; children = ( @@ -564,31 +722,32 @@ DE51B1621F0D48AC0013853F /* Local */ = { isa = PBXGroup; children = ( - 61E1D8AF1FCF6AF500753285 /* StringViewTests.mm */, - DE51B16A1F0D48AC0013853F /* FSTLocalStoreTests.h */, - DE51B1701F0D48AC0013853F /* FSTMutationQueueTests.h */, - DE51B1721F0D48AC0013853F /* FSTPersistenceTestHelpers.h */, - DE51B1741F0D48AC0013853F /* FSTQueryCacheTests.h */, - DE51B1771F0D48AC0013853F /* FSTRemoteDocumentCacheTests.h */, - DE51B1631F0D48AC0013853F /* FSTEagerGarbageCollectorTests.m */, - DE51B1651F0D48AC0013853F /* FSTLevelDBLocalStoreTests.m */, - DE51B1671F0D48AC0013853F /* FSTLevelDBQueryCacheTests.m */, - DE51B1691F0D48AC0013853F /* FSTLocalSerializerTests.m */, - DE51B16B1F0D48AC0013853F /* FSTLocalStoreTests.m */, - DE51B16C1F0D48AC0013853F /* FSTMemoryLocalStoreTests.m */, - DE51B16D1F0D48AC0013853F /* FSTMemoryMutationQueueTests.m */, - DE51B16E1F0D48AC0013853F /* FSTMemoryQueryCacheTests.m */, - DE51B16F1F0D48AC0013853F /* FSTMemoryRemoteDocumentCacheTests.m */, - DE51B1711F0D48AC0013853F /* FSTMutationQueueTests.m */, - DE51B1731F0D48AC0013853F /* FSTPersistenceTestHelpers.m */, - DE51B1751F0D48AC0013853F /* FSTQueryCacheTests.m */, - DE51B1761F0D48AC0013853F /* FSTReferenceSetTests.m */, - DE51B1781F0D48AC0013853F /* FSTRemoteDocumentCacheTests.m */, - DE51B1791F0D48AC0013853F /* FSTRemoteDocumentChangeBufferTests.m */, - DE51B1641F0D48AC0013853F /* FSTLevelDBKeyTests.mm */, - DE51B1661F0D48AC0013853F /* FSTLevelDBMutationQueueTests.mm */, - DE51B1681F0D48AC0013853F /* FSTLevelDBRemoteDocumentCacheTests.mm */, - DE51B17A1F0D48AC0013853F /* FSTWriteGroupTests.mm */, + 5492E0842021552A00B64F25 /* FSTEagerGarbageCollectorTests.mm */, + 5492E08E2021552B00B64F25 /* FSTLevelDBKeyTests.mm */, + 5492E08F2021552B00B64F25 /* FSTLevelDBLocalStoreTests.mm */, + 5492E0862021552A00B64F25 /* FSTLevelDBMigrationsTests.mm */, + 5492E0872021552A00B64F25 /* FSTLevelDBMutationQueueTests.mm */, + 5492E0982021552C00B64F25 /* FSTLevelDBQueryCacheTests.mm */, + 5492E0922021552B00B64F25 /* FSTLevelDBRemoteDocumentCacheTests.mm */, + 5492E08A2021552A00B64F25 /* FSTLocalSerializerTests.mm */, + 5492E0912021552B00B64F25 /* FSTLocalStoreTests.h */, + 5492E0832021552A00B64F25 /* FSTLocalStoreTests.mm */, + 5492E0882021552A00B64F25 /* FSTMemoryLocalStoreTests.mm */, + 5492E0972021552C00B64F25 /* FSTMemoryMutationQueueTests.mm */, + 5492E08B2021552B00B64F25 /* FSTMemoryQueryCacheTests.mm */, + 5492E08C2021552B00B64F25 /* FSTMemoryRemoteDocumentCacheTests.mm */, + 5492E0942021552C00B64F25 /* FSTMutationQueueTests.h */, + 5492E0962021552C00B64F25 /* FSTMutationQueueTests.mm */, + 5492E0992021552C00B64F25 /* FSTPersistenceTestHelpers.h */, + 5492E08D2021552B00B64F25 /* FSTPersistenceTestHelpers.mm */, + 5492E0952021552C00B64F25 /* FSTQueryCacheTests.h */, + 5492E0892021552A00B64F25 /* FSTQueryCacheTests.mm */, + 5492E09A2021552C00B64F25 /* FSTReferenceSetTests.mm */, + 5492E0852021552A00B64F25 /* FSTRemoteDocumentCacheTests.h */, + 5492E09C2021552D00B64F25 /* FSTRemoteDocumentCacheTests.mm */, + 5492E0902021552B00B64F25 /* FSTRemoteDocumentChangeBufferTests.mm */, + 5492E09B2021552C00B64F25 /* FSTWriteGroupTests.mm */, + 5492E0932021552B00B64F25 /* StringViewTests.mm */, ); path = Local; sourceTree = ""; @@ -596,13 +755,12 @@ DE51B17B1F0D48AC0013853F /* Model */ = { isa = PBXGroup; children = ( - DE51B17C1F0D48AC0013853F /* FSTDatabaseIDTests.m */, - DE51B17D1F0D48AC0013853F /* FSTDocumentKeyTests.m */, - DE51B17E1F0D48AC0013853F /* FSTDocumentSetTests.m */, - DE51B17F1F0D48AC0013853F /* FSTDocumentTests.m */, - DE51B1801F0D48AC0013853F /* FSTFieldValueTests.m */, - DE51B1811F0D48AC0013853F /* FSTMutationTests.m */, - DE51B1821F0D48AC0013853F /* FSTPathTests.m */, + 5492E0B22021555000B64F25 /* FSTDocumentKeyTests.mm */, + 5492E0B32021555100B64F25 /* FSTDocumentSetTests.mm */, + 5492E0B62021555100B64F25 /* FSTDocumentTests.mm */, + 5492E0B82021555100B64F25 /* FSTFieldValueTests.mm */, + 5492E0B72021555100B64F25 /* FSTMutationTests.mm */, + 5492E0B52021555100B64F25 /* FSTPathTests.mm */, ); path = Model; sourceTree = ""; @@ -610,17 +768,19 @@ DE51B1831F0D48AC0013853F /* API */ = { isa = PBXGroup; children = ( - AB99452D1FE30AC800DFC1E6 /* FIRFieldValueTests.m */, - AB99452B1FE3018D00DFC1E6 /* FIRQuerySnapshotTests.m */, - AB9945291FE2F9EB00DFC1E6 /* FIRDocumentSnapshotTests.m */, - AB9945271FE2DE0C00DFC1E6 /* FIRSnapshotMetadataTests.m */, - AB9945251FE2D71100DFC1E6 /* FIRCollectionReferenceTests.m */, - AB382F7D1FE03059007CA955 /* FIRFieldPathTests.m */, - AB382F7B1FE02A1F007CA955 /* FIRDocumentReferenceTests.m */, - DE51B1841F0D48AC0013853F /* FIRGeoPointTests.m */, - ABAEEF4E1FD5F8B100C966CB /* FIRQueryTests.m */, - ABF341011FE858B500C48322 /* FSTAPIHelpers.h */, - ABF341021FE8593500C48322 /* FSTAPIHelpers.m */, + 5492E045202154AA00B64F25 /* FIRCollectionReferenceTests.mm */, + 5492E049202154AA00B64F25 /* FIRDocumentReferenceTests.mm */, + 5492E04B202154AA00B64F25 /* FIRDocumentSnapshotTests.mm */, + 5492E04C202154AA00B64F25 /* FIRFieldPathTests.mm */, + 5492E04A202154AA00B64F25 /* FIRFieldValueTests.mm */, + 5467FAFF203E56F8009C9584 /* FIRFirestoreTests.mm */, + 5492E048202154AA00B64F25 /* FIRGeoPointTests.mm */, + 5492E04F202154AA00B64F25 /* FIRQuerySnapshotTests.mm */, + 5492E046202154AA00B64F25 /* FIRQueryTests.mm */, + 5492E04D202154AA00B64F25 /* FIRSnapshotMetadataTests.mm */, + B65D34A7203C99090076A5E1 /* FIRTimestampTest.m */, + 5492E047202154AA00B64F25 /* FSTAPIHelpers.h */, + 5492E04E202154AA00B64F25 /* FSTAPIHelpers.mm */, ); path = API; sourceTree = ""; @@ -628,18 +788,16 @@ DE51B1851F0D48AC0013853F /* Util */ = { isa = PBXGroup; children = ( + 5492E0382021401E00B64F25 /* FSTAssertTests.mm */, 54E9281C1F33950B00C1953E /* FSTEventAccumulator.h */, - 54E9281D1F33950B00C1953E /* FSTEventAccumulator.m */, + 5492E0392021401F00B64F25 /* FSTEventAccumulator.mm */, + DE51B1881F0D48AC0013853F /* FSTHelpers.h */, + 5492E03A2021401F00B64F25 /* FSTHelpers.mm */, 54E9281E1F33950B00C1953E /* FSTIntegrationTestCase.h */, 5491BC711FB44593008B3588 /* FSTIntegrationTestCase.mm */, - DE51B1861F0D48AC0013853F /* FSTAssertTests.m */, - DE51B1871F0D48AC0013853F /* FSTComparisonTests.m */, - DE51B1881F0D48AC0013853F /* FSTHelpers.h */, - DE51B1891F0D48AC0013853F /* FSTHelpers.m */, 54E9282A1F339CAD00C1953E /* XCTestCase+Await.h */, - 54E9282B1F339CAD00C1953E /* XCTestCase+Await.m */, - D5B259DAA9149B80D6245B57 /* FSTTestDispatchQueue.h */, - D5B25292CED31B81FDED0411 /* FSTTestDispatchQueue.m */, + 5492E0372021401E00B64F25 /* XCTestCase+Await.mm */, + 7346E61C20325C6900FD6CEF /* FSTDispatchQueueTests.mm */, ); path = Util; sourceTree = ""; @@ -647,14 +805,14 @@ DE51B1931F0D48AC0013853F /* SpecTests */ = { isa = PBXGroup; children = ( + 5492E02C20213FFB00B64F25 /* FSTLevelDBSpecTests.mm */, + 5492E02F20213FFC00B64F25 /* FSTMemorySpecTests.mm */, DE51B1961F0D48AC0013853F /* FSTMockDatastore.h */, + 5492E02D20213FFC00B64F25 /* FSTMockDatastore.mm */, DE51B1981F0D48AC0013853F /* FSTSpecTests.h */, + 5492E03020213FFC00B64F25 /* FSTSpecTests.mm */, DE51B19A1F0D48AC0013853F /* FSTSyncEngineTestDriver.h */, - DE51B1941F0D48AC0013853F /* FSTLevelDBSpecTests.m */, - DE51B1951F0D48AC0013853F /* FSTMemorySpecTests.m */, - DE51B1971F0D48AC0013853F /* FSTMockDatastore.m */, - DE51B1991F0D48AC0013853F /* FSTSpecTests.m */, - DE51B19B1F0D48AC0013853F /* FSTSyncEngineTestDriver.m */, + 5492E02E20213FFC00B64F25 /* FSTSyncEngineTestDriver.mm */, DE51B19C1F0D48AC0013853F /* json */, ); path = SpecTests; @@ -682,15 +840,12 @@ DE51B1A81F0D48AC0013853F /* Core */ = { isa = PBXGroup; children = ( - DE51B1AD1F0D48AC0013853F /* FSTSyncEngine+Testing.h */, - DE51B1A91F0D48AC0013853F /* FSTDatabaseInfoTests.m */, - DE51B1AA1F0D48AC0013853F /* FSTEventManagerTests.m */, - DE51B1AB1F0D48AC0013853F /* FSTQueryListenerTests.m */, - DE51B1AC1F0D48AC0013853F /* FSTQueryTests.m */, - DE51B1AE1F0D48AC0013853F /* FSTTargetIDGeneratorTests.m */, - DE51B1AF1F0D48AC0013853F /* FSTTimestampTests.m */, - DE51B1B01F0D48AC0013853F /* FSTViewSnapshotTest.m */, - DE51B1B11F0D48AC0013853F /* FSTViewTests.m */, + 5492E060202154B900B64F25 /* FSTEventManagerTests.mm */, + 5492E05D202154B900B64F25 /* FSTQueryListenerTests.mm */, + 5492E061202154B900B64F25 /* FSTQueryTests.mm */, + 5492E05A202154B800B64F25 /* FSTSyncEngine+Testing.h */, + 5492E05C202154B800B64F25 /* FSTViewSnapshotTest.mm */, + 5492E05E202154B900B64F25 /* FSTViewTests.mm */, ); path = Core; sourceTree = ""; @@ -698,12 +853,12 @@ DE51B1B21F0D48AC0013853F /* Remote */ = { isa = PBXGroup; children = ( - DE51B1B31F0D48AC0013853F /* FSTDatastoreTests.m */, - DE51B1B41F0D48AC0013853F /* FSTRemoteEventTests.m */, - DE51B1B61F0D48AC0013853F /* FSTSerializerBetaTests.m */, - DE51B1B81F0D48AC0013853F /* FSTWatchChange+Testing.h */, - DE51B1B91F0D48AC0013853F /* FSTWatchChange+Testing.m */, - DE51B1BA1F0D48AC0013853F /* FSTWatchChangeTests.m */, + 5492E0C22021557E00B64F25 /* FSTDatastoreTests.mm */, + 5492E0C32021557E00B64F25 /* FSTRemoteEventTests.mm */, + 5492E0C12021557E00B64F25 /* FSTSerializerBetaTests.mm */, + 5492E0C42021557E00B64F25 /* FSTWatchChange+Testing.h */, + 5492E0C02021557E00B64F25 /* FSTWatchChange+Testing.mm */, + 5492E0C52021557E00B64F25 /* FSTWatchChangeTests.mm */, ); path = Remote; sourceTree = ""; @@ -711,13 +866,12 @@ DE51B1BB1F0D48AC0013853F /* Integration */ = { isa = PBXGroup; children = ( - DE03B3621F215E1600A30B9C /* CAcert.pem */, DE51B1BC1F0D48AC0013853F /* API */, - DE51B1C41F0D48AC0013853F /* FSTDatastoreTests.m */, - DE51B1C51F0D48AC0013853F /* FSTSmokeTests.m */, - DE51B1C61F0D48AC0013853F /* FSTTransactionTests.m */, - DE51B1C71F0D48AC0013853F /* Util */, - D5B25C0D4AADFCA3ADB883E4 /* FSTStreamTests.m */, + DE03B3621F215E1600A30B9C /* CAcert.pem */, + 5492E07E202154EC00B64F25 /* FSTDatastoreTests.mm */, + 5492E07C202154EB00B64F25 /* FSTSmokeTests.mm */, + 5492E07D202154EB00B64F25 /* FSTStreamTests.mm */, + 5492E07B202154EB00B64F25 /* FSTTransactionTests.mm */, ); path = Integration; sourceTree = ""; @@ -725,30 +879,44 @@ DE51B1BC1F0D48AC0013853F /* API */ = { isa = PBXGroup; children = ( - 61287372203C92C5000EAD3F /* FIRSourceTests.m */, - DE51B1BD1F0D48AC0013853F /* FIRCursorTests.m */, - DE51B1BE1F0D48AC0013853F /* FIRDatabaseTests.m */, - DE51B1BF1F0D48AC0013853F /* FIRFieldsTests.m */, - DE51B1C01F0D48AC0013853F /* FIRListenerRegistrationTests.m */, - DE51B1C11F0D48AC0013853F /* FIRQueryTests.m */, - DE51B1C21F0D48AC0013853F /* FIRServerTimestampTests.m */, - DE51B1C31F0D48AC0013853F /* FIRTypeTests.m */, - 54DA12B01F315F3800DD57A1 /* FIRValidationTests.m */, - DEFE0F471F1F960A0071599A /* FIRWriteBatchTests.m */, + 6161B5012047140400A99DBB /* FIRSourceTests.mm */, + 5492E070202154D600B64F25 /* FIRCursorTests.mm */, + 5492E06C202154D500B64F25 /* FIRDatabaseTests.mm */, + 5492E06A202154D500B64F25 /* FIRFieldsTests.mm */, + 5492E06B202154D500B64F25 /* FIRListenerRegistrationTests.mm */, + 5492E069202154D500B64F25 /* FIRQueryTests.mm */, + 5492E06E202154D600B64F25 /* FIRServerTimestampTests.mm */, + 5492E071202154D600B64F25 /* FIRTypeTests.mm */, + 5492E06D202154D600B64F25 /* FIRValidationTests.mm */, + 5492E06F202154D600B64F25 /* FIRWriteBatchTests.mm */, ); path = API; sourceTree = ""; }; - DE51B1C71F0D48AC0013853F /* Util */ = { - isa = PBXGroup; - children = ( - ); - path = Util; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + 54C9EDF02040E16300A969CD /* Firestore_SwiftTests_iOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 54C9EDFA2040E16300A969CD /* Build configuration list for PBXNativeTarget "Firestore_SwiftTests_iOS" */; + buildPhases = ( + 6FB7F3A6D6ADAC64E4972A29 /* [CP] Check Pods Manifest.lock */, + 54C9EDED2040E16300A969CD /* Sources */, + 54C9EDEE2040E16300A969CD /* Frameworks */, + 54C9EDEF2040E16300A969CD /* Resources */, + 9E2D564AC55ADE2D52B7E951 /* [CP] Embed Pods Frameworks */, + A650E34A01FE620F7B26F5FF /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + 54C9EDF72040E16300A969CD /* PBXTargetDependency */, + ); + name = Firestore_SwiftTests_iOS; + productName = Firestore_SwiftTests_iOS; + productReference = 54C9EDF12040E16300A969CD /* Firestore_SwiftTests_iOS.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; 6003F589195388D20070C39A /* Firestore_Example */ = { isa = PBXNativeTarget; buildConfigurationList = 6003F5BF195388D20070C39A /* Build configuration list for PBXNativeTarget "Firestore_Example" */; @@ -838,10 +1006,15 @@ isa = PBXProject; attributes = { CLASSPREFIX = FIR; - LastSwiftUpdateCheck = 0830; + LastSwiftUpdateCheck = 0920; LastUpgradeCheck = 0720; ORGANIZATIONNAME = Google; TargetAttributes = { + 54C9EDF02040E16300A969CD = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Automatic; + TestTargetID = 6003F589195388D20070C39A; + }; 6003F5AD195388D20070C39A = { DevelopmentTeam = EQHXZ8M8AV; TestTargetID = 6003F589195388D20070C39A; @@ -875,6 +1048,7 @@ targets = ( 6003F589195388D20070C39A /* Firestore_Example */, 6003F5AD195388D20070C39A /* Firestore_Tests */, + 54C9EDF02040E16300A969CD /* Firestore_SwiftTests_iOS */, DE03B2941F2149D600A30B9C /* Firestore_IntegrationTests */, DE29E7F51F2174B000909613 /* AllTests */, DE0761E31F2FE611003233AF /* SwiftBuildTest */, @@ -883,6 +1057,13 @@ /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 54C9EDEF2040E16300A969CD /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 6003F588195388D20070C39A /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -981,6 +1162,24 @@ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-SwiftBuildTest/Pods-SwiftBuildTest-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; + 6FB7F3A6D6ADAC64E4972A29 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Firestore_Example-Firestore_SwiftTests_iOS-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; 7C5123A9C345ECE100DA21BD /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -1053,6 +1252,57 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + 9E2D564AC55ADE2D52B7E951 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${SRCROOT}/Pods/Target Support Files/Pods-Firestore_Example-Firestore_SwiftTests_iOS/Pods-Firestore_Example-Firestore_SwiftTests_iOS-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/BoringSSL/openssl.framework", + "${BUILT_PRODUCTS_DIR}/GTMSessionFetcher/GTMSessionFetcher.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", + "${BUILT_PRODUCTS_DIR}/gRPC-ProtoRPC/ProtoRPC.framework", + "${BUILT_PRODUCTS_DIR}/gRPC-RxLibrary/RxLibrary.framework", + "${BUILT_PRODUCTS_DIR}/leveldb-library/leveldb.framework", + "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/openssl.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GTMSessionFetcher.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleToolboxForMac.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Protobuf.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GRPCClient.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/grpc.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ProtoRPC.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxLibrary.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/leveldb.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Firestore_Example-Firestore_SwiftTests_iOS/Pods-Firestore_Example-Firestore_SwiftTests_iOS-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + A650E34A01FE620F7B26F5FF /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Firestore_Example-Firestore_SwiftTests_iOS/Pods-Firestore_Example-Firestore_SwiftTests_iOS-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; AB3F19DA92555D3399DB07CE /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -1181,6 +1431,14 @@ /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 54C9EDED2040E16300A969CD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 5495EB032040E90200EBA509 /* CodableGeoPointTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 6003F586195388D20070C39A /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -1195,78 +1453,100 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 7346E61D20325C6900FD6CEF /* FSTDispatchQueueTests.mm in Sources */, DE2EF0881F3D0B6E003D0CDC /* FSTTreeSortedDictionaryTests.m in Sources */, - DE51B1FD1F0D492C0013853F /* FSTSpecTests.m in Sources */, - ABAEEF4F1FD5F8B100C966CB /* FIRQueryTests.m in Sources */, - DE51B2001F0D493A0013853F /* FSTComparisonTests.m in Sources */, - ABF341051FE860CA00C48322 /* FSTAPIHelpers.m in Sources */, - DE51B1CC1F0D48C00013853F /* FIRGeoPointTests.m in Sources */, - DE51B1E11F0D490D0013853F /* FSTMemoryRemoteDocumentCacheTests.m in Sources */, - DE51B1FF1F0D493A0013853F /* FSTAssertTests.m in Sources */, - DE51B1D31F0D48CD0013853F /* FSTViewSnapshotTest.m in Sources */, - DE51B1F91F0D491F0013853F /* FSTWatchChangeTests.m in Sources */, - DE51B1F81F0D491F0013853F /* FSTWatchChange+Testing.m in Sources */, - DE51B1EB1F0D490D0013853F /* FSTWriteGroupTests.mm in Sources */, + ABE6637A201FA81900ED349A /* database_id_test.cc in Sources */, + 5492E0AF2021552D00B64F25 /* FSTReferenceSetTests.mm in Sources */, + 5492E09E2021552D00B64F25 /* FSTEagerGarbageCollectorTests.mm in Sources */, + 5492E0C62021557E00B64F25 /* FSTWatchChange+Testing.mm in Sources */, + 5492E064202154B900B64F25 /* FSTQueryListenerTests.mm in Sources */, + B65D34A9203C995B0076A5E1 /* FIRTimestampTest.m in Sources */, + 5492E03320213FFC00B64F25 /* FSTSyncEngineTestDriver.mm in Sources */, + AB380CFE201A2F4500D97691 /* string_util_test.cc in Sources */, + 5492E0A42021552D00B64F25 /* FSTMemoryQueryCacheTests.mm in Sources */, + 5492E0A92021552D00B64F25 /* FSTRemoteDocumentChangeBufferTests.mm in Sources */, 54C2294F1FECABAE007D065B /* log_test.cc in Sources */, - DE51B2011F0D493E0013853F /* FSTHelpers.m in Sources */, - DE51B1F61F0D491B0013853F /* FSTSerializerBetaTests.m in Sources */, - DE51B1F01F0D49140013853F /* FSTFieldValueTests.m in Sources */, - AB9945281FE2DE0C00DFC1E6 /* FIRSnapshotMetadataTests.m in Sources */, + B686F2B22025000D0028D6BE /* resource_path_test.cc in Sources */, + 5492E0CA2021557E00B64F25 /* FSTWatchChangeTests.mm in Sources */, + 5492E063202154B900B64F25 /* FSTViewSnapshotTest.mm in Sources */, + 5492E0BC2021555100B64F25 /* FSTPathTests.mm in Sources */, + 5492E0B02021552D00B64F25 /* FSTWriteGroupTests.mm in Sources */, + 5492E058202154AB00B64F25 /* FSTAPIHelpers.mm in Sources */, + AB380CFB2019388600D97691 /* target_id_generator_test.cc in Sources */, + 5492E0A82021552D00B64F25 /* FSTLevelDBLocalStoreTests.mm in Sources */, + ABC1D7DE2023A05300BA84F0 /* user_test.cc in Sources */, 5491BC721FB44593008B3588 /* FSTIntegrationTestCase.mm in Sources */, + ABC1D7DD2023A04F00BA84F0 /* empty_credentials_provider_test.cc in Sources */, DE2EF0861F3D0B6E003D0CDC /* FSTImmutableSortedDictionary+Testing.m in Sources */, - DE51B1DE1F0D490D0013853F /* FSTMemoryLocalStoreTests.m in Sources */, - DE51B1EC1F0D49140013853F /* FSTDatabaseIDTests.m in Sources */, - DE51B1ED1F0D49140013853F /* FSTDocumentKeyTests.m in Sources */, - DE51B1D41F0D48CD0013853F /* FSTViewTests.m in Sources */, + B686F2AF2023DDEE0028D6BE /* field_path_test.cc in Sources */, + 5492E03120213FFC00B64F25 /* FSTLevelDBSpecTests.mm in Sources */, + 5492E0B12021552D00B64F25 /* FSTRemoteDocumentCacheTests.mm in Sources */, + 5492E0BA2021555100B64F25 /* FSTDocumentSetTests.mm in Sources */, 54740A581FC914F000713A1A /* autoid_test.cc in Sources */, - DE51B1F41F0D491B0013853F /* FSTRemoteEventTests.m in Sources */, - 54E928241F33953300C1953E /* FSTEventAccumulator.m in Sources */, - DE51B1D11F0D48CD0013853F /* FSTTargetIDGeneratorTests.m in Sources */, + 548DB927200D590300E00ABC /* assert_test.cc in Sources */, + 5492E0A62021552D00B64F25 /* FSTPersistenceTestHelpers.mm in Sources */, + 5467FB01203E5717009C9584 /* FIRFirestoreTests.mm in Sources */, + 5492E0A12021552D00B64F25 /* FSTMemoryLocalStoreTests.mm in Sources */, 5436F32420008FAD006E51E3 /* string_printf_test.cc in Sources */, - DE51B1EF1F0D49140013853F /* FSTDocumentTests.m in Sources */, - DE51B1DC1F0D490D0013853F /* FSTLocalSerializerTests.m in Sources */, - AB99452A1FE2F9EB00DFC1E6 /* FIRDocumentSnapshotTests.m in Sources */, - DE51B1E71F0D490D0013853F /* FSTRemoteDocumentChangeBufferTests.m in Sources */, - DE51B1E51F0D490D0013853F /* FSTReferenceSetTests.m in Sources */, - DE51B1EA1F0D490D0013853F /* FSTLevelDBRemoteDocumentCacheTests.mm in Sources */, - DE51B1D21F0D48CD0013853F /* FSTTimestampTests.m in Sources */, - DE51B1EE1F0D49140013853F /* FSTDocumentSetTests.m in Sources */, + 5492E067202154B900B64F25 /* FSTEventManagerTests.mm in Sources */, + 5492E0BF2021555100B64F25 /* FSTFieldValueTests.mm in Sources */, + 5492E055202154AB00B64F25 /* FIRDocumentSnapshotTests.mm in Sources */, + 5492E03E2021401F00B64F25 /* FSTEventAccumulator.mm in Sources */, DE2EF0851F3D0B6E003D0CDC /* FSTArraySortedDictionaryTests.m in Sources */, - 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 */, - AB99452E1FE30AC800DFC1E6 /* FIRFieldValueTests.m in Sources */, - DE51B1DF1F0D490D0013853F /* FSTMemoryMutationQueueTests.m in Sources */, - DE51B1F31F0D491B0013853F /* FSTDatastoreTests.m in Sources */, - DE51B1D01F0D48CD0013853F /* FSTQueryTests.m in Sources */, + 5492E0AA2021552D00B64F25 /* FSTLevelDBRemoteDocumentCacheTests.mm in Sources */, + 5492E0AC2021552D00B64F25 /* FSTMutationQueueTests.mm in Sources */, + 5492E056202154AB00B64F25 /* FIRFieldPathTests.mm in Sources */, + 5492E03220213FFC00B64F25 /* FSTMockDatastore.mm in Sources */, + ABC1D7E12023A40C00BA84F0 /* token_test.cc in Sources */, + AB356EF7200EA5EB0089B766 /* field_value_test.cc in Sources */, + AB7BAB342012B519001E0872 /* geo_point_test.cc in Sources */, + 5492E0AD2021552D00B64F25 /* FSTMemoryMutationQueueTests.mm in Sources */, + 5492E051202154AA00B64F25 /* FIRQueryTests.mm in Sources */, + 5492E054202154AB00B64F25 /* FIRFieldValueTests.mm in Sources */, + AB6B908620322E6D00CC290A /* maybe_document_test.cc in Sources */, + 5492E09F2021552D00B64F25 /* FSTLevelDBMigrationsTests.mm in Sources */, + 5492E053202154AB00B64F25 /* FIRDocumentReferenceTests.mm in Sources */, + 5492E09D2021552D00B64F25 /* FSTLocalStoreTests.mm in Sources */, + 5492E0A32021552D00B64F25 /* FSTLocalSerializerTests.mm in Sources */, + 5492E0A72021552D00B64F25 /* FSTLevelDBKeyTests.mm in Sources */, + 5492E0A22021552D00B64F25 /* FSTQueryCacheTests.mm in Sources */, + 5492E0A52021552D00B64F25 /* FSTMemoryRemoteDocumentCacheTests.mm in Sources */, + AB6B908820322E8800CC290A /* no_document_test.cc in Sources */, + 5492E0BD2021555100B64F25 /* FSTDocumentTests.mm in Sources */, + 5492E0B92021555100B64F25 /* FSTDocumentKeyTests.mm in Sources */, DE2EF0871F3D0B6E003D0CDC /* FSTImmutableSortedSet+Testing.m in Sources */, - DE51B1E01F0D490D0013853F /* FSTMemoryQueryCacheTests.m in Sources */, - DE51B1E91F0D490D0013853F /* FSTLevelDBMutationQueueTests.mm in Sources */, + 5492E0C82021557E00B64F25 /* FSTDatastoreTests.mm in Sources */, + 5492E065202154B900B64F25 /* FSTViewTests.mm in Sources */, + 5492E03C2021401F00B64F25 /* XCTestCase+Await.mm in Sources */, + B6152AD7202A53CB000E5744 /* document_key_test.cc in Sources */, + 5467FB08203E6A44009C9584 /* app_testing.mm in Sources */, 54764FAF1FAA21B90085E60A /* FSTGoogleTestTests.mm in Sources */, - DE51B1E61F0D490D0013853F /* FSTRemoteDocumentCacheTests.m in Sources */, - 61E1D8B11FCF6C5700753285 /* StringViewTests.mm in Sources */, - DE51B1D91F0D490D0013853F /* FSTEagerGarbageCollectorTests.m in Sources */, - DE51B1E21F0D490D0013853F /* FSTMutationQueueTests.m in Sources */, - DE51B1E81F0D490D0013853F /* FSTLevelDBKeyTests.mm in Sources */, - AB9945261FE2D71100DFC1E6 /* FIRCollectionReferenceTests.m in Sources */, - DE51B1E31F0D490D0013853F /* FSTPersistenceTestHelpers.m in Sources */, - AB382F7C1FE02A1F007CA955 /* FIRDocumentReferenceTests.m in Sources */, - DE51B1CF1F0D48CD0013853F /* FSTQueryListenerTests.m in Sources */, - DE51B1DA1F0D490D0013853F /* FSTLevelDBLocalStoreTests.m in Sources */, - DE51B1FA1F0D492C0013853F /* FSTLevelDBSpecTests.m in Sources */, - DE51B1FE1F0D492C0013853F /* FSTSyncEngineTestDriver.m in Sources */, - DE51B1FC1F0D492C0013853F /* FSTMockDatastore.m in Sources */, - DE51B1CE1F0D48CD0013853F /* FSTEventManagerTests.m in Sources */, - DE51B1E41F0D490D0013853F /* FSTQueryCacheTests.m in Sources */, - DE51B1CD1F0D48CD0013853F /* FSTDatabaseInfoTests.m in Sources */, - AB382F7E1FE03059007CA955 /* FIRFieldPathTests.m in Sources */, - DE51B1F21F0D49140013853F /* FSTPathTests.m in Sources */, - AB99452C1FE3018D00DFC1E6 /* FIRQuerySnapshotTests.m in Sources */, + AB380D04201BC6E400D97691 /* ordered_code_test.cc in Sources */, + 5492E03F2021401F00B64F25 /* FSTHelpers.mm in Sources */, + 5492E068202154B900B64F25 /* FSTQueryTests.mm in Sources */, + 5492E0AB2021552D00B64F25 /* StringViewTests.mm in Sources */, + 5492E0C92021557E00B64F25 /* FSTRemoteEventTests.mm in Sources */, + ABC1D7E42024AFDE00BA84F0 /* firebase_credentials_provider_test.mm in Sources */, + AB6B908420322E4D00CC290A /* document_test.cc in Sources */, + ABF6506C201131F8005F2C74 /* timestamp_test.cc in Sources */, + 5492E0AE2021552D00B64F25 /* FSTLevelDBQueryCacheTests.mm in Sources */, + ABC1D7DC2023A04B00BA84F0 /* credentials_provider_test.cc in Sources */, + 5492E059202154AB00B64F25 /* FIRQuerySnapshotTests.mm in Sources */, + 5492E050202154AA00B64F25 /* FIRCollectionReferenceTests.mm in Sources */, + ABA495BB202B7E80008A7851 /* snapshot_version_test.cc in Sources */, + 5492E0A02021552D00B64F25 /* FSTLevelDBMutationQueueTests.mm in Sources */, + 54EB764D202277B30088B8F3 /* array_sorted_map_test.cc in Sources */, + 5492E03420213FFC00B64F25 /* FSTMemorySpecTests.mm in Sources */, + AB380D02201BC69F00D97691 /* bits_test.cc in Sources */, + 548DB929200D59F600E00ABC /* comparison_test.cc in Sources */, + 5492E03D2021401F00B64F25 /* FSTAssertTests.mm in Sources */, + AB38D93020236E21000A432D /* database_info_test.cc in Sources */, + 5492E052202154AB00B64F25 /* FIRGeoPointTests.mm in Sources */, + 5492E0C72021557E00B64F25 /* FSTSerializerBetaTests.mm in Sources */, + 5492E03520213FFC00B64F25 /* FSTSpecTests.mm in Sources */, + 5492E057202154AB00B64F25 /* FIRSnapshotMetadataTests.mm in Sources */, 54740A571FC914BA00713A1A /* secure_random_test.cc in Sources */, - DE51B1DD1F0D490D0013853F /* FSTLocalStoreTests.m in Sources */, - D5B25474286C9800CE42B8C2 /* FSTTestDispatchQueue.m in Sources */, + 5492E0BE2021555100B64F25 /* FSTMutationTests.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1274,25 +1554,24 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 61287373203C92C5000EAD3F /* FIRSourceTests.m in Sources */, - DE03B2EE1F214BAA00A30B9C /* FIRWriteBatchTests.m in Sources */, - DE03B2F01F214BAA00A30B9C /* FIRDatabaseTests.m in Sources */, + 6161B5032047140C00A99DBB /* FIRSourceTests.mm in Sources */, + 5492E076202154D600B64F25 /* FIRValidationTests.mm in Sources */, + 5492E072202154D600B64F25 /* FIRQueryTests.mm in Sources */, 5491BC731FB44593008B3588 /* FSTIntegrationTestCase.mm in Sources */, - DE03B2F41F214BAA00A30B9C /* FIRServerTimestampTests.m in Sources */, - DE03B2F11F214BAA00A30B9C /* FIRFieldsTests.m in Sources */, - 54E9282D1F339CAD00C1953E /* XCTestCase+Await.m in Sources */, - DE03B2EC1F214BA200A30B9C /* FSTDatastoreTests.m in Sources */, - 54E928251F33953400C1953E /* FSTEventAccumulator.m in Sources */, - DE03B2ED1F214BA200A30B9C /* FSTSmokeTests.m in Sources */, - DE03B2F31F214BAA00A30B9C /* FIRQueryTests.m in Sources */, - DE03B35E1F21586C00A30B9C /* FSTHelpers.m in Sources */, - DE03B2F51F214BAA00A30B9C /* FIRTypeTests.m in Sources */, - DE03B2EF1F214BAA00A30B9C /* FIRCursorTests.m in Sources */, - DE03B2F21F214BAA00A30B9C /* FIRListenerRegistrationTests.m in Sources */, - DE03B2C91F2149D600A30B9C /* FSTTransactionTests.m in Sources */, - 54DA12B11F315F3800DD57A1 /* FIRValidationTests.m in Sources */, - D5B2532E4676014F57A7EAB9 /* FSTStreamTests.m in Sources */, - D5B259FDEE8094E8D710C5BF /* FSTTestDispatchQueue.m in Sources */, + 5492E0442021457E00B64F25 /* XCTestCase+Await.mm in Sources */, + 5492E07A202154D600B64F25 /* FIRTypeTests.mm in Sources */, + 5492E0422021440500B64F25 /* FSTHelpers.mm in Sources */, + 5492E041202143E700B64F25 /* FSTEventAccumulator.mm in Sources */, + 5492E080202154EC00B64F25 /* FSTSmokeTests.mm in Sources */, + 5492E077202154D600B64F25 /* FIRServerTimestampTests.mm in Sources */, + 5492E081202154EC00B64F25 /* FSTStreamTests.mm in Sources */, + 5492E074202154D600B64F25 /* FIRListenerRegistrationTests.mm in Sources */, + 5492E082202154EC00B64F25 /* FSTDatastoreTests.mm in Sources */, + 5492E079202154D600B64F25 /* FIRCursorTests.mm in Sources */, + 5492E073202154D600B64F25 /* FIRFieldsTests.mm in Sources */, + 5492E07F202154EC00B64F25 /* FSTTransactionTests.mm in Sources */, + 5492E075202154D600B64F25 /* FIRDatabaseTests.mm in Sources */, + 5492E078202154D600B64F25 /* FIRWriteBatchTests.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1307,6 +1586,16 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 54C9EDF72040E16300A969CD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 6003F589195388D20070C39A /* Firestore_Example */; + targetProxy = 54C9EDF62040E16300A969CD /* PBXContainerItemProxy */; + }; + 54C9EDFF2040E41900A969CD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 54C9EDF02040E16300A969CD /* Firestore_SwiftTests_iOS */; + targetProxy = 54C9EDFE2040E41900A969CD /* PBXContainerItemProxy */; + }; 6003F5B4195388D20070C39A /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 6003F589195388D20070C39A /* Firestore_Example */; @@ -1362,6 +1651,84 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + 54C9EDF82040E16300A969CD /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3F422FFBDA6E79396E2FB594 /* Pods-Firestore_Example-Firestore_SwiftTests_iOS.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + INFOPLIST_FILE = ../Swift/Tests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.2; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_BUNDLE_IDENTIFIER = "com.google.Firestore-SwiftTests-iOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Firestore_Example.app/Firestore_Example"; + }; + name = Debug; + }; + 54C9EDF92040E16300A969CD /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = C1D89E5405935366C88CC3E5 /* Pods-Firestore_Example-Firestore_SwiftTests_iOS.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + INFOPLIST_FILE = ../Swift/Tests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.2; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_BUNDLE_IDENTIFIER = "com.google.Firestore-SwiftTests-iOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Firestore_Example.app/Firestore_Example"; + }; + name = Release; + }; 6003F5BD195388D20070C39A /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -1564,6 +1931,9 @@ HEADER_SEARCH_PATHS = ( "$(inherited)", "\"${PODS_ROOT}/../../..\"", + "\"${PODS_ROOT}/../../../Firestore/third_party/abseil-cpp\"", + "\"${PODS_ROOT}/leveldb-library/include\"", + "\"${PODS_ROOT}/GoogleTest/googletest/include\"", ); INFOPLIST_FILE = "Tests/Tests-Info.plist"; OTHER_LDFLAGS = ( @@ -1602,6 +1972,9 @@ HEADER_SEARCH_PATHS = ( "$(inherited)", "\"${PODS_ROOT}/../../..\"", + "\"${PODS_ROOT}/../../../Firestore/third_party/abseil-cpp\"", + "\"${PODS_ROOT}/leveldb-library/include\"", + "\"${PODS_ROOT}/GoogleTest/googletest/include\"", ); INFOPLIST_FILE = "Tests/Tests-Info.plist"; OTHER_LDFLAGS = ( @@ -1692,6 +2065,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 54C9EDFA2040E16300A969CD /* Build configuration list for PBXNativeTarget "Firestore_SwiftTests_iOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 54C9EDF82040E16300A969CD /* Debug */, + 54C9EDF92040E16300A969CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 6003F585195388D10070C39A /* Build configuration list for PBXProject "Firestore" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/AllTests.xcscheme b/Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/AllTests.xcscheme index aacb70e3973..6cf00a9b302 100644 --- a/Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/AllTests.xcscheme +++ b/Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/AllTests.xcscheme @@ -20,6 +20,20 @@ ReferencedContainer = "container:Firestore.xcodeproj"> + + + + + + + + + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + + + + + + + + + + + + @@ -51,6 +87,15 @@ debugDocumentVersioning = "YES" debugServiceExtension = "internal" allowLocationSimulation = "YES"> + + + + @@ -60,6 +105,15 @@ savedToolIdentifier = "" useCustomWorkingDirectory = "NO" debugDocumentVersioning = "YES"> + + + + diff --git a/Firestore/Example/Podfile b/Firestore/Example/Podfile index a15446ea82e..2c38cd99885 100644 --- a/Firestore/Example/Podfile +++ b/Firestore/Example/Podfile @@ -1,7 +1,7 @@ # The next line is the forcing function for the Firebase pod. The Firebase # version's subspecs should depend on the component versions in their # corresponding podspec's. -pod 'Firebase/Core', '4.8.0' +pod 'Firebase/Core', '4.9.0' use_frameworks! platform :ios, '8.0' @@ -28,6 +28,11 @@ target 'Firestore_Example' do pod 'OCMock' end + + target 'Firestore_SwiftTests_iOS' do + pod 'FirebaseFirestore', :path => '../../' + pod 'FirebaseFirestoreSwift', :path => '../../' + end end target 'SwiftBuildTest' do diff --git a/Firestore/Example/SwiftBuildTest/main.swift b/Firestore/Example/SwiftBuildTest/main.swift index b2de9da8d9f..06d9479f44a 100644 --- a/Firestore/Example/SwiftBuildTest/main.swift +++ b/Firestore/Example/SwiftBuildTest/main.swift @@ -19,243 +19,235 @@ import Foundation import FirebaseFirestore func main() { - let db = initializeDb(); + let db = initializeDb() - let (collectionRef, documentRef) = makeRefs(database: db); + let (collectionRef, documentRef) = makeRefs(database: db) - let query = makeQuery(collection: collectionRef); + let query = makeQuery(collection: collectionRef) - writeDocument(at: documentRef); + writeDocument(at: documentRef) - writeDocuments(at: documentRef, database: db); + writeDocuments(at: documentRef, database: db) - addDocument(to: collectionRef); + addDocument(to: collectionRef) - readDocument(at: documentRef); - readDocumentWithOptions(at: documentRef); + readDocument(at: documentRef) + readDocumentWithOptions(at: documentRef) - readDocuments(matching: query); - readDocumentsWithOptions(matching: query); + readDocuments(matching: query) + readDocumentsWithOptions(matching: query) - listenToDocument(at: documentRef); + listenToDocument(at: documentRef) - listenToDocuments(matching: query); + listenToDocuments(matching: query) - enableDisableNetwork(database: db); + enableDisableNetwork(database: db) - types(); + types() } func initializeDb() -> Firestore { + // Initialize with ProjectID. + let firestore = Firestore.firestore() - // Initialize with ProjectID. - let firestore = Firestore.firestore() + // Apply settings + let settings = FirestoreSettings() + settings.host = "localhost" + settings.isPersistenceEnabled = true + firestore.settings = settings - // Apply settings - let settings = FirestoreSettings() - settings.host = "localhost" - settings.isPersistenceEnabled = true - firestore.settings = settings - - return firestore; + return firestore } func makeRefs(database db: Firestore) -> (CollectionReference, DocumentReference) { + var collectionRef = db.collection("my-collection") - var collectionRef = db.collection("my-collection") - - var documentRef: DocumentReference; - documentRef = collectionRef.document("my-doc") - // or - documentRef = db.document("my-collection/my-doc") + var documentRef: DocumentReference + documentRef = collectionRef.document("my-doc") + // or + documentRef = db.document("my-collection/my-doc") - // deeper collection (my-collection/my-doc/some/deep/collection) - collectionRef = documentRef.collection("some/deep/collection") + // deeper collection (my-collection/my-doc/some/deep/collection) + collectionRef = documentRef.collection("some/deep/collection") - // parent doc (my-collection/my-doc/some/deep) - documentRef = collectionRef.parent! + // parent doc (my-collection/my-doc/some/deep) + documentRef = collectionRef.parent! - // print paths. - print("Collection: \(collectionRef.path), document: \(documentRef.path)") + // print paths. + print("Collection: \(collectionRef.path), document: \(documentRef.path)") - return (collectionRef, documentRef); + return (collectionRef, documentRef) } func makeQuery(collection collectionRef: CollectionReference) -> Query { - - let query = collectionRef.whereField(FieldPath(["name"]), isEqualTo: "Fred") - .whereField("age", isGreaterThanOrEqualTo: 24) - .whereField(FieldPath.documentID(), isEqualTo: "fred") - .order(by: FieldPath(["age"])) - .order(by: "name", descending: true) - .limit(to: 10) - - return query; + let query = collectionRef.whereField(FieldPath(["name"]), isEqualTo: "Fred") + .whereField("age", isGreaterThanOrEqualTo: 24) + .whereField(FieldPath.documentID(), isEqualTo: "fred") + .order(by: FieldPath(["age"])) + .order(by: "name", descending: true) + .limit(to: 10) + + return query } func writeDocument(at docRef: DocumentReference) { - - let setData = [ - "foo": 42, - "bar": [ - "baz": "Hello world!" - ] - ] as [String : Any]; - - let updateData = [ - "bar.baz": 42, - FieldPath(["foobar"]) : 42 - ] as [AnyHashable : Any]; - - docRef.setData(setData) - - // Completion callback (via trailing closure syntax). - docRef.setData(setData) { error in - if let error = error { - print("Uh oh! \(error)") - return - } - - print("Set complete!") + let setData = [ + "foo": 42, + "bar": [ + "baz": "Hello world!", + ], + ] as [String: Any] + + let updateData = [ + "bar.baz": 42, + FieldPath(["foobar"]): 42, + ] as [AnyHashable: Any] + + docRef.setData(setData) + + // Completion callback (via trailing closure syntax). + docRef.setData(setData) { error in + if let error = error { + print("Uh oh! \(error)") + return } - // SetOptions - docRef.setData(setData, options:SetOptions.merge()) + print("Set complete!") + } - docRef.updateData(updateData) - docRef.delete(); + // SetOptions + docRef.setData(setData, options: SetOptions.merge()) - docRef.delete() { error in - if let error = error { - print("Uh oh! \(error)") - return - } + docRef.updateData(updateData) + docRef.delete() - print("Set complete!") + docRef.delete { error in + if let error = error { + print("Uh oh! \(error)") + return } + + print("Set complete!") + } } func enableDisableNetwork(database db: Firestore) { - // closure syntax - db.disableNetwork(completion: { (error) in - if let e = error { - print("Uh oh! \(e)") - return - } - }) - // trailing block syntax - db.enableNetwork { (error) in - if let e = error { - print("Uh oh! \(e)") - return - } + // closure syntax + db.disableNetwork(completion: { error in + if let e = error { + print("Uh oh! \(e)") + return } + }) + // trailing block syntax + db.enableNetwork { error in + if let e = error { + print("Uh oh! \(e)") + return + } + } } func writeDocuments(at docRef: DocumentReference, database db: Firestore) { - var batch: WriteBatch; + var batch: WriteBatch - batch = db.batch(); - batch.setData(["a" : "b"], forDocument:docRef); - batch.setData(["c" : "d"], forDocument:docRef); + batch = db.batch() + batch.setData(["a": "b"], forDocument: docRef) + batch.setData(["c": "d"], forDocument: docRef) // commit without completion callback. - batch.commit(); - print("Batch write without completion complete!"); + batch.commit() + print("Batch write without completion complete!") - batch = db.batch(); - batch.setData(["a" : "b"], forDocument:docRef); - batch.setData(["c" : "d"], forDocument:docRef); + batch = db.batch() + batch.setData(["a": "b"], forDocument: docRef) + batch.setData(["c": "d"], forDocument: docRef) // commit with completion callback via trailing closure syntax. - batch.commit() { error in + batch.commit { error in if let error = error { - print("Uh oh! \(error)"); - return; + print("Uh oh! \(error)") + return } - print("Batch write callback complete!"); + print("Batch write callback complete!") } - print("Batch write with completion complete!"); + print("Batch write with completion complete!") } func addDocument(to collectionRef: CollectionReference) { - - collectionRef.addDocument(data: ["foo": 42]); - //or - collectionRef.document().setData(["foo": 42]); + collectionRef.addDocument(data: ["foo": 42]) + // or + collectionRef.document().setData(["foo": 42]) } func readDocument(at docRef: DocumentReference) { - - // Trailing closure syntax. - docRef.getDocument() { document, error in - if let document = document { - // Note that both document and document.data() is nullable. - if let data = document.data() { - print("Read document: \(data)") - } - if let data = document.data(with:SnapshotOptions.serverTimestampBehavior(.estimate)) { - print("Read document: \(data)") - } - if let foo = document.get("foo") { - print("Field: \(foo)") - } - if let foo = document.get("foo", options: SnapshotOptions.serverTimestampBehavior(.previous)) { - print("Field: \(foo)") - } - // Fields can also be read via subscript notation. - if let foo = document["foo"] { - print("Field: \(foo)") - } - } else { - // TODO(mikelehen): There may be a better way to do this, but it at least demonstrates - // the swift error domain / enum codes are renamed appropriately. - if let errorCode = error.flatMap({ - ($0._domain == FirestoreErrorDomain) ? FirestoreErrorCode (rawValue: $0._code) : nil - }) { - switch errorCode { - case .unavailable: - print("Can't read document due to being offline!") - case _: - print("Failed to read.") - } - } else { - print("Unknown error!") - } + // Trailing closure syntax. + docRef.getDocument { document, error in + if let document = document { + // Note that both document and document.data() is nullable. + if let data = document.data() { + print("Read document: \(data)") + } + if let data = document.data(with: SnapshotOptions.serverTimestampBehavior(.estimate)) { + print("Read document: \(data)") + } + if let foo = document.get("foo") { + print("Field: \(foo)") + } + if let foo = document.get("foo", options: SnapshotOptions.serverTimestampBehavior(.previous)) { + print("Field: \(foo)") + } + // Fields can also be read via subscript notation. + if let foo = document["foo"] { + print("Field: \(foo)") + } + } else { + // TODO(mikelehen): There may be a better way to do this, but it at least demonstrates + // the swift error domain / enum codes are renamed appropriately. + if let errorCode = error.flatMap({ + ($0._domain == FirestoreErrorDomain) ? FirestoreErrorCode(rawValue: $0._code) : nil + }) { + switch errorCode { + case .unavailable: + print("Can't read document due to being offline!") + case _: + print("Failed to read.") } - + } else { + print("Unknown error!") + } } + } } func readDocumentWithOptions(at docRef: DocumentReference) { - docRef.getDocument(source:Source.default) { document, error in + docRef.getDocument(source: Source.default) { document, error in } - docRef.getDocument(source:.server) { document, error in + docRef.getDocument(source: .server) { document, error in } - docRef.getDocument(source:Source.cache) { document, error in + docRef.getDocument(source: Source.cache) { document, error in } } func readDocuments(matching query: Query) { - query.getDocuments() { querySnapshot, error in - // TODO(mikelehen): Figure out how to make "for..in" syntax work - // directly on documentSet. - for document in querySnapshot!.documents { - print(document.data()) - } + query.getDocuments { querySnapshot, error in + // TODO(mikelehen): Figure out how to make "for..in" syntax work + // directly on documentSet. + for document in querySnapshot!.documents { + print(document.data()) } + } } func readDocumentsWithOptions(matching query: Query) { - query.getDocuments(source:Source.default) { querySnapshot, error in + query.getDocuments(source: Source.default) { querySnapshot, error in } - query.getDocuments(source:.server) { querySnapshot, error in + query.getDocuments(source: .server) { querySnapshot, error in } - query.getDocuments(source:Source.cache) { querySnapshot, error in + query.getDocuments(source: Source.cache) { querySnapshot, error in } } func listenToDocument(at docRef: DocumentReference) { - - let listener = docRef.addSnapshotListener() { document, error in + let listener = docRef.addSnapshotListener { document, error in if let error = error { print("Uh oh! Listen canceled: \(error)") return @@ -263,8 +255,8 @@ func listenToDocument(at docRef: DocumentReference) { if let document = document { // Note that document.data() is nullable. - if let data : [String:Any] = document.data() { - print("Current document: \(data)"); + if let data: [String: Any] = document.data() { + print("Current document: \(data)") } if document.metadata.isFromCache { print("From Cache") @@ -275,100 +267,98 @@ func listenToDocument(at docRef: DocumentReference) { } // Unsubscribe. - listener.remove(); + listener.remove() } func listenToDocuments(matching query: Query) { + let listener = query.addSnapshotListener { snap, error in + if let error = error { + print("Uh oh! Listen canceled: \(error)") + return + } - let listener = query.addSnapshotListener() { snap, error in - if let error = error { - print("Uh oh! Listen canceled: \(error)") - return - } - - if let snap = snap { - print("NEW SNAPSHOT (empty=\(snap.isEmpty) count=\(snap.count)") + if let snap = snap { + print("NEW SNAPSHOT (empty=\(snap.isEmpty) count=\(snap.count)") - // TODO(mikelehen): Figure out how to make "for..in" syntax work - // directly on documentSet. - for document in snap.documents { - // Note that document.data() is not nullable. - let data : [String:Any] = document.data() - print("Doc: ", data) - } - } + // TODO(mikelehen): Figure out how to make "for..in" syntax work + // directly on documentSet. + for document in snap.documents { + // Note that document.data() is not nullable. + let data: [String: Any] = document.data() + print("Doc: ", data) + } } + } - // Unsubscribe - listener.remove(); + // Unsubscribe + listener.remove() } func listenToQueryDiffs(onQuery query: Query) { - - let listener = query.addSnapshotListener() { snap, error in - if let snap = snap { - for change in snap.documentChanges { - switch (change.type) { - case .added: - print("New document: \(change.document.data())") - case .modified: - print("Modified document: \(change.document.data())") - case .removed: - print("Removed document: \(change.document.data())") - } - } + let listener = query.addSnapshotListener { snap, error in + if let snap = snap { + for change in snap.documentChanges { + switch change.type { + case .added: + print("New document: \(change.document.data())") + case .modified: + print("Modified document: \(change.document.data())") + case .removed: + print("Removed document: \(change.document.data())") } + } } + } - // Unsubscribe - listener.remove(); + // Unsubscribe + listener.remove() } func transactions() { - let db = Firestore.firestore() - - let collectionRef = db.collection("cities") - let accA = collectionRef.document("accountA") - let accB = collectionRef.document("accountB") - let amount = 20.0 - - db.runTransaction({ (transaction, errorPointer) -> Any? in - do { - let balanceA = try transaction.getDocument(accA)["balance"] as! Double - let balanceB = try transaction.getDocument(accB)["balance"] as! Double - - if balanceA < amount { - errorPointer?.pointee = NSError(domain: "Foo", code: 123, userInfo: nil) - return nil - } - transaction.updateData(["balance": balanceA - amount], forDocument:accA) - transaction.updateData(["balance": balanceB + amount], forDocument:accB) - } catch let error as NSError { - print("Uh oh! \(error)") - } - return 0 - }) { (result, error) in - // handle result. + let db = Firestore.firestore() + + let collectionRef = db.collection("cities") + let accA = collectionRef.document("accountA") + let accB = collectionRef.document("accountB") + let amount = 20.0 + + db.runTransaction({ (transaction, errorPointer) -> Any? in + do { + let balanceA = try transaction.getDocument(accA)["balance"] as! Double + let balanceB = try transaction.getDocument(accB)["balance"] as! Double + + if balanceA < amount { + errorPointer?.pointee = NSError(domain: "Foo", code: 123, userInfo: nil) + return nil + } + transaction.updateData(["balance": balanceA - amount], forDocument: accA) + transaction.updateData(["balance": balanceB + amount], forDocument: accB) + } catch let error as NSError { + print("Uh oh! \(error)") } + return 0 + }) { result, error in + // handle result. + } } func types() { - let _: CollectionReference; - let _: DocumentChange; - let _: DocumentListenOptions; - let _: DocumentReference; - let _: DocumentSnapshot; - let _: FieldPath; - let _: FieldValue; - let _: Firestore; - let _: FirestoreSettings; - let _: GeoPoint; - let _: ListenerRegistration; - let _: QueryListenOptions; - let _: Query; - let _: QuerySnapshot; - let _: SetOptions; - let _: SnapshotMetadata; - let _: Transaction; - let _: WriteBatch; + let _: CollectionReference + let _: DocumentChange + let _: DocumentListenOptions + let _: DocumentReference + let _: DocumentSnapshot + let _: FieldPath + let _: FieldValue + let _: Firestore + let _: FirestoreSettings + let _: GeoPoint + let _: ListenerRegistration + let _: QueryListenOptions + let _: Query + let _: QuerySnapshot + let _: SetOptions + let _: SnapshotMetadata + let _: Transaction + let _: WriteBatch } diff --git a/Firestore/Example/Tests/API/FIRCollectionReferenceTests.m b/Firestore/Example/Tests/API/FIRCollectionReferenceTests.mm similarity index 96% rename from Firestore/Example/Tests/API/FIRCollectionReferenceTests.m rename to Firestore/Example/Tests/API/FIRCollectionReferenceTests.mm index 73ae38d9085..547078f3772 100644 --- a/Firestore/Example/Tests/API/FIRCollectionReferenceTests.m +++ b/Firestore/Example/Tests/API/FIRCollectionReferenceTests.mm @@ -14,9 +14,9 @@ * limitations under the License. */ -#import +#import -#import "FirebaseFirestore/FIRCollectionReference.h" +#import #import "Firestore/Example/Tests/API/FSTAPIHelpers.h" diff --git a/Firestore/Example/Tests/API/FIRDocumentReferenceTests.m b/Firestore/Example/Tests/API/FIRDocumentReferenceTests.mm similarity index 96% rename from Firestore/Example/Tests/API/FIRDocumentReferenceTests.m rename to Firestore/Example/Tests/API/FIRDocumentReferenceTests.mm index 4e301d08a76..cc2b4315153 100644 --- a/Firestore/Example/Tests/API/FIRDocumentReferenceTests.m +++ b/Firestore/Example/Tests/API/FIRDocumentReferenceTests.mm @@ -14,9 +14,9 @@ * limitations under the License. */ -#import +#import -#import "FirebaseFirestore/FIRDocumentReference.h" +#import #import "Firestore/Example/Tests/API/FSTAPIHelpers.h" diff --git a/Firestore/Example/Tests/API/FIRDocumentSnapshotTests.m b/Firestore/Example/Tests/API/FIRDocumentSnapshotTests.mm similarity index 93% rename from Firestore/Example/Tests/API/FIRDocumentSnapshotTests.m rename to Firestore/Example/Tests/API/FIRDocumentSnapshotTests.mm index e865928b383..677d38543ce 100644 --- a/Firestore/Example/Tests/API/FIRDocumentSnapshotTests.m +++ b/Firestore/Example/Tests/API/FIRDocumentSnapshotTests.mm @@ -14,9 +14,9 @@ * limitations under the License. */ -#import +#import -#import "FirebaseFirestore/FIRDocumentSnapshot.h" +#import #import "Firestore/Example/Tests/API/FSTAPIHelpers.h" @@ -30,11 +30,8 @@ @implementation FIRDocumentSnapshotTests - (void)testEquals { FIRDocumentSnapshot *base = FSTTestDocSnapshot(@"rooms/foo", 1, @{ @"a" : @1 }, NO, NO); FIRDocumentSnapshot *baseDup = FSTTestDocSnapshot(@"rooms/foo", 1, @{ @"a" : @1 }, NO, NO); -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wnonnull" FIRDocumentSnapshot *nilData = FSTTestDocSnapshot(@"rooms/foo", 1, nil, NO, NO); FIRDocumentSnapshot *nilDataDup = FSTTestDocSnapshot(@"rooms/foo", 1, nil, NO, NO); -#pragma clang diagnostic pop FIRDocumentSnapshot *differentPath = FSTTestDocSnapshot(@"rooms/bar", 1, @{ @"a" : @1 }, NO, NO); FIRDocumentSnapshot *differentData = FSTTestDocSnapshot(@"rooms/bar", 1, @{ @"b" : @1 }, NO, NO); FIRDocumentSnapshot *hasMutations = FSTTestDocSnapshot(@"rooms/bar", 1, @{ @"a" : @1 }, YES, NO); diff --git a/Firestore/Example/Tests/API/FIRFieldPathTests.m b/Firestore/Example/Tests/API/FIRFieldPathTests.mm similarity index 96% rename from Firestore/Example/Tests/API/FIRFieldPathTests.m rename to Firestore/Example/Tests/API/FIRFieldPathTests.mm index f8177c840ea..679ea89df4b 100644 --- a/Firestore/Example/Tests/API/FIRFieldPathTests.m +++ b/Firestore/Example/Tests/API/FIRFieldPathTests.mm @@ -14,9 +14,10 @@ * limitations under the License. */ +#import + #import -#import "FirebaseFirestore/FIRFieldPath.h" #import "Firestore/Source/API/FIRFieldPath+Internal.h" #import "Firestore/Source/Model/FSTPath.h" diff --git a/Firestore/Example/Tests/API/FIRFieldValueTests.m b/Firestore/Example/Tests/API/FIRFieldValueTests.mm similarity index 75% rename from Firestore/Example/Tests/API/FIRFieldValueTests.m rename to Firestore/Example/Tests/API/FIRFieldValueTests.mm index 8c9db99cb0b..575dfee4bee 100644 --- a/Firestore/Example/Tests/API/FIRFieldValueTests.m +++ b/Firestore/Example/Tests/API/FIRFieldValueTests.mm @@ -14,12 +14,10 @@ * limitations under the License. */ -@import FirebaseFirestore; +#import #import -#import "FirebaseFirestore/FIRFieldValue.h" - NS_ASSUME_NONNULL_BEGIN @interface FIRFieldValueTests : XCTestCase @@ -28,19 +26,19 @@ @interface FIRFieldValueTests : XCTestCase @implementation FIRFieldValueTests - (void)testEquals { - FIRFieldValue *delete = [FIRFieldValue fieldValueForDelete]; + FIRFieldValue *deleted = [FIRFieldValue fieldValueForDelete]; FIRFieldValue *deleteDup = [FIRFieldValue fieldValueForDelete]; FIRFieldValue *serverTimestamp = [FIRFieldValue fieldValueForServerTimestamp]; FIRFieldValue *serverTimestampDup = [FIRFieldValue fieldValueForServerTimestamp]; - XCTAssertEqualObjects(delete, deleteDup); - XCTAssertNotEqualObjects(delete, nil); + XCTAssertEqualObjects(deleted, deleteDup); + XCTAssertNotEqualObjects(deleted, nil); XCTAssertEqualObjects(serverTimestamp, serverTimestampDup); XCTAssertNotEqualObjects(serverTimestamp, nil); - XCTAssertNotEqualObjects(delete, serverTimestamp); + XCTAssertNotEqualObjects(deleted, serverTimestamp); - XCTAssertEqual([delete hash], [deleteDup hash]); + XCTAssertEqual([deleted hash], [deleteDup hash]); XCTAssertEqual([serverTimestamp hash], [serverTimestamp hash]); - XCTAssertNotEqual([delete hash], [serverTimestamp hash]); + XCTAssertNotEqual([deleted hash], [serverTimestamp hash]); } @end diff --git a/Firestore/Example/Tests/API/FIRFirestoreTests.mm b/Firestore/Example/Tests/API/FIRFirestoreTests.mm new file mode 100644 index 00000000000..7cb49b72d7c --- /dev/null +++ b/Firestore/Example/Tests/API/FIRFirestoreTests.mm @@ -0,0 +1,65 @@ +/* + * Copyright 2018 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 +#import + +#import + +#include "Firestore/core/test/firebase/firestore/testutil/app_testing.h" + +namespace testutil = firebase::firestore::testutil; + +@interface FIRFirestoreTests : XCTestCase +@end + +@implementation FIRFirestoreTests + +- (void)testDeleteApp { + // Ensure the app is set appropriately. + FIRApp *app = testutil::AppForUnitTesting(); + NSString *appName = app.name; + FIROptions *options = app.options; + + FIRFirestore *firestore = [FIRFirestore firestoreForApp:app]; + XCTAssertEqualObjects(firestore.app, app); + + // Ensure that firestoreForApp returns the same instance. + XCTAssertEqualObjects(firestore, [FIRFirestore firestoreForApp:app]); + + XCTestExpectation *defaultAppDeletedExpectation = + [self expectationWithDescription: + @"Deleting the default app should invalidate the default " + @"Firestore instance."]; + [app deleteApp:^(BOOL success) { + // Recreate the FIRApp with the same name, fetch a new Firestore instance and make sure it's + // different than the other one. + [FIRApp configureWithName:appName options:options]; + FIRApp *newApp = [FIRApp appNamed:appName]; + FIRFirestore *newInstance = [FIRFirestore firestoreForApp:newApp]; + XCTAssertNotEqualObjects(newInstance, firestore); + + [defaultAppDeletedExpectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:2 + handler:^(NSError *_Nullable error) { + XCTAssertNil(error); + }]; +} + +@end diff --git a/Firestore/Example/Tests/API/FIRGeoPointTests.m b/Firestore/Example/Tests/API/FIRGeoPointTests.mm similarity index 98% rename from Firestore/Example/Tests/API/FIRGeoPointTests.m rename to Firestore/Example/Tests/API/FIRGeoPointTests.mm index 8abda108bbb..4de80a88aee 100644 --- a/Firestore/Example/Tests/API/FIRGeoPointTests.m +++ b/Firestore/Example/Tests/API/FIRGeoPointTests.mm @@ -14,7 +14,7 @@ * limitations under the License. */ -#import "FirebaseFirestore/FIRGeoPoint.h" +#import #import diff --git a/Firestore/Example/Tests/API/FIRQuerySnapshotTests.m b/Firestore/Example/Tests/API/FIRQuerySnapshotTests.mm similarity index 97% rename from Firestore/Example/Tests/API/FIRQuerySnapshotTests.m rename to Firestore/Example/Tests/API/FIRQuerySnapshotTests.mm index 4637c497dfc..067425a5067 100644 --- a/Firestore/Example/Tests/API/FIRQuerySnapshotTests.m +++ b/Firestore/Example/Tests/API/FIRQuerySnapshotTests.mm @@ -14,9 +14,10 @@ * limitations under the License. */ +#import + #import -#import "FirebaseFirestore/FIRQuerySnapshot.h" #import "Firestore/Source/Model/FSTPath.h" #import "Firestore/Example/Tests/API/FSTAPIHelpers.h" diff --git a/Firestore/Example/Tests/API/FIRQueryTests.m b/Firestore/Example/Tests/API/FIRQueryTests.mm similarity index 98% rename from Firestore/Example/Tests/API/FIRQueryTests.m rename to Firestore/Example/Tests/API/FIRQueryTests.mm index 1b5236d21b5..83f90be1de2 100644 --- a/Firestore/Example/Tests/API/FIRQueryTests.m +++ b/Firestore/Example/Tests/API/FIRQueryTests.mm @@ -14,9 +14,10 @@ * limitations under the License. */ +#import + #import -#import "FirebaseFirestore/FIRQuery.h" #import "Firestore/Source/API/FIRQuery+Internal.h" #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Model/FSTPath.h" diff --git a/Firestore/Example/Tests/API/FIRSnapshotMetadataTests.m b/Firestore/Example/Tests/API/FIRSnapshotMetadataTests.mm similarity index 96% rename from Firestore/Example/Tests/API/FIRSnapshotMetadataTests.m rename to Firestore/Example/Tests/API/FIRSnapshotMetadataTests.mm index cf507657d6e..a4d321b40d2 100644 --- a/Firestore/Example/Tests/API/FIRSnapshotMetadataTests.m +++ b/Firestore/Example/Tests/API/FIRSnapshotMetadataTests.mm @@ -14,9 +14,10 @@ * limitations under the License. */ +#import + #import -#import "FirebaseFirestore/FIRSnapshotMetadata.h" #import "Firestore/Source/API/FIRSnapshotMetadata+Internal.h" NS_ASSUME_NONNULL_BEGIN diff --git a/Firestore/Example/Tests/API/FIRTimestampTest.m b/Firestore/Example/Tests/API/FIRTimestampTest.m new file mode 100644 index 00000000000..98ec8049b7c --- /dev/null +++ b/Firestore/Example/Tests/API/FIRTimestampTest.m @@ -0,0 +1,102 @@ +/* + * 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 "Firestore/Source/API/FIRTimestamp+Internal.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface FIRTimestampTest : XCTestCase +@end + +NSDate *TestDate(int year, int month, int day, int hour, int minute, int second) { + NSDateComponents *comps = [[NSDateComponents alloc] init]; + comps.year = year; + comps.month = month; + comps.day = day; + comps.hour = hour; + comps.minute = minute; + comps.second = second; + // Force time zone to UTC to avoid these values changing due to daylight saving. + comps.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0]; + + return [[NSCalendar currentCalendar] dateFromComponents:comps]; +} + +@implementation FIRTimestampTest + +- (void)testFromDate { + // Use an NSDate such that its fractional seconds have an exact representation to avoid losing + // precision. + NSDate *input = [NSDate dateWithTimeIntervalSinceReferenceDate:1.5]; + + FIRTimestamp *actual = [FIRTimestamp timestampWithDate:input]; + static const int64_t kSecondsFromEpochToReferenceDate = 978307200; + XCTAssertEqual(kSecondsFromEpochToReferenceDate + 1, actual.seconds); + XCTAssertEqual(actual.nanoseconds, 500000000); + + FIRTimestamp *expected = + [[FIRTimestamp alloc] initWithSeconds:(kSecondsFromEpochToReferenceDate + 1) + nanoseconds:500000000]; + XCTAssertEqualObjects(actual, expected); +} + +- (void)testSO8601String { + NSDate *date = TestDate(1912, 4, 14, 23, 40, 0); + FIRTimestamp *timestamp = + [[FIRTimestamp alloc] initWithSeconds:(int64_t)date.timeIntervalSince1970 + nanoseconds:543000000]; + XCTAssertEqualObjects(timestamp.ISO8601String, @"1912-04-14T23:40:00.543000000Z"); +} + +- (void)testISO8601String_withLowMilliseconds { + NSDate *date = TestDate(1912, 4, 14, 23, 40, 0); + FIRTimestamp *timestamp = + [[FIRTimestamp alloc] initWithSeconds:(int64_t)date.timeIntervalSince1970 + nanoseconds:7000000]; + XCTAssertEqualObjects(timestamp.ISO8601String, @"1912-04-14T23:40:00.007000000Z"); +} + +- (void)testISO8601String_withLowNanos { + FIRTimestamp *timestamp = [[FIRTimestamp alloc] initWithSeconds:0 nanoseconds:1]; + XCTAssertEqualObjects(timestamp.ISO8601String, @"1970-01-01T00:00:00.000000001Z"); +} + +- (void)testISO8601String_withNegativeSeconds { + FIRTimestamp *timestamp = [[FIRTimestamp alloc] initWithSeconds:-1 nanoseconds:999999999]; + XCTAssertEqualObjects(timestamp.ISO8601String, @"1969-12-31T23:59:59.999999999Z"); +} + +- (void)testCompare { + NSArray *timestamps = @[ + [[FIRTimestamp alloc] initWithSeconds:12344 nanoseconds:999999999], + [[FIRTimestamp alloc] initWithSeconds:12345 nanoseconds:0], + [[FIRTimestamp alloc] initWithSeconds:12345 nanoseconds:000000001], + [[FIRTimestamp alloc] initWithSeconds:12345 nanoseconds:99999999], + [[FIRTimestamp alloc] initWithSeconds:12345 nanoseconds:100000000], + [[FIRTimestamp alloc] initWithSeconds:12345 nanoseconds:100000001], + [[FIRTimestamp alloc] initWithSeconds:12346 nanoseconds:0], + ]; + for (int i = 0; i < timestamps.count - 1; ++i) { + XCTAssertEqual([timestamps[i] compare:timestamps[i + 1]], NSOrderedAscending); + XCTAssertEqual([timestamps[i + 1] compare:timestamps[i]], NSOrderedDescending); + } +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firestore/Example/Tests/API/FSTAPIHelpers.h b/Firestore/Example/Tests/API/FSTAPIHelpers.h index dcd8209c9b0..0729af0e4d9 100644 --- a/Firestore/Example/Tests/API/FSTAPIHelpers.h +++ b/Firestore/Example/Tests/API/FSTAPIHelpers.h @@ -16,13 +16,14 @@ #import -#import "FirebaseFirestore/FIRCollectionReference.h" -#import "FirebaseFirestore/FIRDocumentSnapshot.h" -#import "FirebaseFirestore/FIRFirestore.h" -#import "FirebaseFirestore/FIRQuerySnapshot.h" - #import "Firestore/Example/Tests/Util/FSTHelpers.h" +@class FIRCollectionReference; +@class FIRDocumentReference; +@class FIRDocumentSnapshot; +@class FIRFirestore; +@class FIRQuerySnapshot; + NS_ASSUME_NONNULL_BEGIN #if __cplusplus @@ -35,7 +36,7 @@ FIRFirestore *FSTTestFirestore(); /** A convenience method for creating a doc snapshot for tests. */ FIRDocumentSnapshot *FSTTestDocSnapshot(NSString *path, FSTTestSnapshotVersion version, - NSDictionary *data, + NSDictionary *_Nullable data, BOOL hasMutations, BOOL fromCache); diff --git a/Firestore/Example/Tests/API/FSTAPIHelpers.m b/Firestore/Example/Tests/API/FSTAPIHelpers.mm similarity index 94% rename from Firestore/Example/Tests/API/FSTAPIHelpers.m rename to Firestore/Example/Tests/API/FSTAPIHelpers.mm index 507e2ff72ea..d0918422e97 100644 --- a/Firestore/Example/Tests/API/FSTAPIHelpers.m +++ b/Firestore/Example/Tests/API/FSTAPIHelpers.mm @@ -16,8 +16,10 @@ #import "Firestore/Example/Tests/API/FSTAPIHelpers.h" -#import "FirebaseFirestore/FIRDocumentReference.h" -#import "FirebaseFirestore/FIRSnapshotMetadata.h" +#import +#import +#import + #import "Firestore/Source/API/FIRCollectionReference+Internal.h" #import "Firestore/Source/API/FIRDocumentReference+Internal.h" #import "Firestore/Source/API/FIRDocumentSnapshot+Internal.h" @@ -40,8 +42,8 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wnonnull" dispatch_once(&onceToken, ^{ - sharedInstance = [[FIRFirestore alloc] initWithProjectID:@"abc" - database:@"abc" + sharedInstance = [[FIRFirestore alloc] initWithProjectID:"abc" + database:"abc" persistenceKey:@"db123" credentialsProvider:nil workerDispatchQueue:nil @@ -53,7 +55,7 @@ FIRDocumentSnapshot *FSTTestDocSnapshot(NSString *path, FSTTestSnapshotVersion version, - NSDictionary *data, + NSDictionary *_Nullable data, BOOL hasMutations, BOOL fromCache) { FSTDocument *doc = data ? FSTTestDoc(path, version, data, hasMutations) : nil; diff --git a/Firestore/Example/Tests/Core/FSTDatabaseInfoTests.m b/Firestore/Example/Tests/Core/FSTDatabaseInfoTests.m deleted file mode 100644 index c7cf22aed18..00000000000 --- a/Firestore/Example/Tests/Core/FSTDatabaseInfoTests.m +++ /dev/null @@ -1,59 +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/Source/Core/FSTDatabaseInfo.h" - -#import - -#import "Firestore/Source/Model/FSTDatabaseID.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface FSTDatabaseInfoTests : XCTestCase -@end - -@implementation FSTDatabaseInfoTests - -- (void)testConstructor { - FSTDatabaseID *databaseID = [FSTDatabaseID databaseIDWithProject:@"p" database:@"d"]; - FSTDatabaseInfo *databaseInfo = [FSTDatabaseInfo databaseInfoWithDatabaseID:databaseID - persistenceKey:@"pk" - host:@"h" - sslEnabled:YES]; - XCTAssertEqualObjects(databaseInfo.databaseID.projectID, @"p"); - XCTAssertEqualObjects(databaseInfo.databaseID.databaseID, @"d"); - XCTAssertEqualObjects(databaseInfo.persistenceKey, @"pk"); - XCTAssertEqualObjects(databaseInfo.host, @"h"); - XCTAssertEqual(databaseInfo.sslEnabled, YES); -} - -- (void)testDefaultDatabase { - FSTDatabaseID *databaseID = - [FSTDatabaseID databaseIDWithProject:@"p" database:kDefaultDatabaseID]; - FSTDatabaseInfo *databaseInfo = [FSTDatabaseInfo databaseInfoWithDatabaseID:databaseID - persistenceKey:@"pk" - host:@"h" - sslEnabled:YES]; - XCTAssertEqualObjects(databaseInfo.databaseID.projectID, @"p"); - XCTAssertEqualObjects(databaseInfo.databaseID.databaseID, @"(default)"); - XCTAssertEqualObjects(databaseInfo.persistenceKey, @"pk"); - XCTAssertEqualObjects(databaseInfo.host, @"h"); - XCTAssertEqual(databaseInfo.sslEnabled, YES); -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/Firestore/Example/Tests/Core/FSTEventManagerTests.m b/Firestore/Example/Tests/Core/FSTEventManagerTests.mm similarity index 100% rename from Firestore/Example/Tests/Core/FSTEventManagerTests.m rename to Firestore/Example/Tests/Core/FSTEventManagerTests.mm diff --git a/Firestore/Example/Tests/Core/FSTQueryListenerTests.m b/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm similarity index 100% rename from Firestore/Example/Tests/Core/FSTQueryListenerTests.m rename to Firestore/Example/Tests/Core/FSTQueryListenerTests.mm diff --git a/Firestore/Example/Tests/Core/FSTQueryTests.m b/Firestore/Example/Tests/Core/FSTQueryTests.mm similarity index 98% rename from Firestore/Example/Tests/Core/FSTQueryTests.m rename to Firestore/Example/Tests/Core/FSTQueryTests.mm index 3d2bd82f0a7..c0b2cd9ac07 100644 --- a/Firestore/Example/Tests/Core/FSTQueryTests.m +++ b/Firestore/Example/Tests/Core/FSTQueryTests.mm @@ -19,13 +19,18 @@ #import #import "Firestore/Source/API/FIRFirestore+Internal.h" -#import "Firestore/Source/Model/FSTDatabaseID.h" #import "Firestore/Source/Model/FSTDocument.h" #import "Firestore/Source/Model/FSTDocumentKey.h" #import "Firestore/Source/Model/FSTPath.h" #import "Firestore/Example/Tests/Util/FSTHelpers.h" +#include "Firestore/core/src/firebase/firestore/model/database_id.h" +#include "Firestore/core/src/firebase/firestore/util/string_apple.h" + +namespace util = firebase::firestore::util; +using firebase::firestore::model::DatabaseId; + NS_ASSUME_NONNULL_BEGIN /** Convenience methods for building test queries. */ @@ -320,7 +325,7 @@ - (void)testSortsDocumentsInTheCorrectOrder { FSTTestDoc(@"collection/1", 0, @{@"sort": @"ab"}, NO), FSTTestDoc(@"collection/1", 0, @{@"sort": @"b"}, NO), FSTTestDoc(@"collection/1", 0, @{@"sort": - FSTTestRef(@"project", kDefaultDatabaseID, @"collection/id1")}, NO), + FSTTestRef("project", DatabaseId::kDefault, @"collection/id1")}, NO), ]; // clang-format on diff --git a/Firestore/Example/Tests/Core/FSTTargetIDGeneratorTests.m b/Firestore/Example/Tests/Core/FSTTargetIDGeneratorTests.m deleted file mode 100644 index 6f54fd1292a..00000000000 --- a/Firestore/Example/Tests/Core/FSTTargetIDGeneratorTests.m +++ /dev/null @@ -1,94 +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/Source/Core/FSTTargetIDGenerator.h" - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface FSTTargetIDGenerator () -- (instancetype)initWithGeneratorID:(NSInteger)generatorID startingAfterID:(FSTTargetID)after; -@end - -@interface FSTTargetIDGeneratorTests : XCTestCase -@end - -@implementation FSTTargetIDGeneratorTests - -- (void)testConstructor { - XCTAssertEqual([[[FSTTargetIDGenerator alloc] initWithGeneratorID:0 startingAfterID:0] nextID], - 2); - XCTAssertEqual([[[FSTTargetIDGenerator alloc] initWithGeneratorID:1 startingAfterID:0] nextID], - 1); - - XCTAssertEqual([[FSTTargetIDGenerator generatorForLocalStoreStartingAfterID:0] nextID], 2); - XCTAssertEqual([[FSTTargetIDGenerator generatorForSyncEngineStartingAfterID:0] nextID], 1); -} - -- (void)testSkipPast { - FSTTargetIDGenerator *gen = - [[FSTTargetIDGenerator alloc] initWithGeneratorID:1 startingAfterID:-1]; - XCTAssertEqual([gen nextID], 1); - - gen = [[FSTTargetIDGenerator alloc] initWithGeneratorID:1 startingAfterID:2]; - XCTAssertEqual([gen nextID], 3); - - gen = [[FSTTargetIDGenerator alloc] initWithGeneratorID:1 startingAfterID:4]; - XCTAssertEqual([gen nextID], 5); - - for (int i = 4; i < 12; ++i) { - FSTTargetIDGenerator *gen0 = - [[FSTTargetIDGenerator alloc] initWithGeneratorID:0 startingAfterID:i]; - FSTTargetIDGenerator *gen1 = - [[FSTTargetIDGenerator alloc] initWithGeneratorID:1 startingAfterID:i]; - XCTAssertEqual([gen0 nextID], i + 2 & ~1, @"Skip failed for index %d", i); - XCTAssertEqual([gen1 nextID], i + 1 | 1, @"Skip failed for index %d", i); - } - - gen = [[FSTTargetIDGenerator alloc] initWithGeneratorID:1 startingAfterID:12]; - XCTAssertEqual([gen nextID], 13); - - gen = [[FSTTargetIDGenerator alloc] initWithGeneratorID:0 startingAfterID:22]; - XCTAssertEqual([gen nextID], 24); -} - -- (void)testIncrement { - FSTTargetIDGenerator *gen = - [[FSTTargetIDGenerator alloc] initWithGeneratorID:0 startingAfterID:0]; - XCTAssertEqual([gen nextID], 2); - XCTAssertEqual([gen nextID], 4); - XCTAssertEqual([gen nextID], 6); - gen = [[FSTTargetIDGenerator alloc] initWithGeneratorID:0 startingAfterID:46]; - XCTAssertEqual([gen nextID], 48); - XCTAssertEqual([gen nextID], 50); - XCTAssertEqual([gen nextID], 52); - XCTAssertEqual([gen nextID], 54); - - gen = [[FSTTargetIDGenerator alloc] initWithGeneratorID:1 startingAfterID:0]; - XCTAssertEqual([gen nextID], 1); - XCTAssertEqual([gen nextID], 3); - XCTAssertEqual([gen nextID], 5); - gen = [[FSTTargetIDGenerator alloc] initWithGeneratorID:1 startingAfterID:46]; - XCTAssertEqual([gen nextID], 47); - XCTAssertEqual([gen nextID], 49); - XCTAssertEqual([gen nextID], 51); - XCTAssertEqual([gen nextID], 53); -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/Firestore/Example/Tests/Core/FSTTimestampTests.m b/Firestore/Example/Tests/Core/FSTTimestampTests.m deleted file mode 100644 index a3765fe8460..00000000000 --- a/Firestore/Example/Tests/Core/FSTTimestampTests.m +++ /dev/null @@ -1,88 +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/Source/Core/FSTTimestamp.h" - -#import - -#import "Firestore/Source/Util/FSTAssert.h" - -#import "Firestore/Example/Tests/Util/FSTHelpers.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface FSTTimestampTests : XCTestCase -@end - -@implementation FSTTimestampTests - -- (void)testFromDate { - // Very carefully construct an NSDate that won't lose precision with its milliseconds. - NSDate *input = [NSDate dateWithTimeIntervalSinceReferenceDate:1.5]; - - FSTTimestamp *actual = [FSTTimestamp timestampWithDate:input]; - static const int64_t kSecondsFromEpochToReferenceDate = 978307200; - XCTAssertEqual(kSecondsFromEpochToReferenceDate + 1, actual.seconds); - XCTAssertEqual(500000000, actual.nanos); - - FSTTimestamp *expected = - [[FSTTimestamp alloc] initWithSeconds:(kSecondsFromEpochToReferenceDate + 1) nanos:500000000]; - XCTAssertEqualObjects(expected, actual); -} - -- (void)testSO8601String { - NSDate *date = FSTTestDate(1912, 4, 14, 23, 40, 0); - FSTTimestamp *timestamp = - [[FSTTimestamp alloc] initWithSeconds:(int64_t)date.timeIntervalSince1970 nanos:543000000]; - XCTAssertEqualObjects(timestamp.ISO8601String, @"1912-04-14T23:40:00.543000000Z"); -} - -- (void)testISO8601String_withLowMilliseconds { - NSDate *date = FSTTestDate(1912, 4, 14, 23, 40, 0); - FSTTimestamp *timestamp = - [[FSTTimestamp alloc] initWithSeconds:(int64_t)date.timeIntervalSince1970 nanos:7000000]; - XCTAssertEqualObjects(timestamp.ISO8601String, @"1912-04-14T23:40:00.007000000Z"); -} - -- (void)testISO8601String_withLowNanos { - FSTTimestamp *timestamp = [[FSTTimestamp alloc] initWithSeconds:0 nanos:1]; - XCTAssertEqualObjects(timestamp.ISO8601String, @"1970-01-01T00:00:00.000000001Z"); -} - -- (void)testISO8601String_withNegativeSeconds { - FSTTimestamp *timestamp = [[FSTTimestamp alloc] initWithSeconds:-1 nanos:999999999]; - XCTAssertEqualObjects(timestamp.ISO8601String, @"1969-12-31T23:59:59.999999999Z"); -} - -- (void)testCompare { - NSArray *timestamps = @[ - [[FSTTimestamp alloc] initWithSeconds:12344 nanos:999999999], - [[FSTTimestamp alloc] initWithSeconds:12345 nanos:0], - [[FSTTimestamp alloc] initWithSeconds:12345 nanos:000000001], - [[FSTTimestamp alloc] initWithSeconds:12345 nanos:99999999], - [[FSTTimestamp alloc] initWithSeconds:12345 nanos:100000000], - [[FSTTimestamp alloc] initWithSeconds:12345 nanos:100000001], - [[FSTTimestamp alloc] initWithSeconds:12346 nanos:0], - ]; - for (int i = 0; i < timestamps.count - 1; ++i) { - XCTAssertEqual(NSOrderedAscending, [timestamps[i] compare:timestamps[i + 1]]); - XCTAssertEqual(NSOrderedDescending, [timestamps[i + 1] compare:timestamps[i]]); - } -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/Firestore/Example/Tests/Core/FSTViewSnapshotTest.m b/Firestore/Example/Tests/Core/FSTViewSnapshotTest.mm similarity index 100% rename from Firestore/Example/Tests/Core/FSTViewSnapshotTest.m rename to Firestore/Example/Tests/Core/FSTViewSnapshotTest.mm diff --git a/Firestore/Example/Tests/Core/FSTViewTests.m b/Firestore/Example/Tests/Core/FSTViewTests.mm similarity index 100% rename from Firestore/Example/Tests/Core/FSTViewTests.m rename to Firestore/Example/Tests/Core/FSTViewTests.mm diff --git a/Firestore/Example/Tests/Integration/API/FIRCursorTests.m b/Firestore/Example/Tests/Integration/API/FIRCursorTests.mm similarity index 68% rename from Firestore/Example/Tests/Integration/API/FIRCursorTests.m rename to Firestore/Example/Tests/Integration/API/FIRCursorTests.mm index dc9da83b71c..2188b8a9eba 100644 --- a/Firestore/Example/Tests/Integration/API/FIRCursorTests.m +++ b/Firestore/Example/Tests/Integration/API/FIRCursorTests.mm @@ -14,7 +14,7 @@ * limitations under the License. */ -@import FirebaseFirestore; +#import #import @@ -192,4 +192,82 @@ - (void)testCanBeUsedInDescendingQueries { XCTAssertEqualObjects(FIRQuerySnapshotGetData(snapshot), (@[ @{ @"v" : @"d", @"sort" : @3.0 } ])); } +FIRTimestamp *TimestampWithMicros(int64_t seconds, int32_t micros) { + // Firestore only supports microsecond resolution, so use a microsecond as a minimum value for + // nanoseconds. + return [FIRTimestamp timestampWithSeconds:seconds nanoseconds:micros * 1000]; +} + +- (void)testTimestampsCanBePassedToQueriesAsLimits { + FIRCollectionReference *testCollection = [self collectionRefWithDocuments:@{ + @"a" : @{@"timestamp" : TimestampWithMicros(100, 2)}, + @"b" : @{@"timestamp" : TimestampWithMicros(100, 5)}, + @"c" : @{@"timestamp" : TimestampWithMicros(100, 3)}, + @"d" : @{@"timestamp" : TimestampWithMicros(100, 1)}, + // Number of microseconds deliberately repeated. + @"e" : @{@"timestamp" : TimestampWithMicros(100, 5)}, + @"f" : @{@"timestamp" : TimestampWithMicros(100, 4)}, + }]; + FIRQuery *query = [testCollection queryOrderedByField:@"timestamp"]; + FIRQuerySnapshot *querySnapshot = + [self readDocumentSetForRef:[[query queryStartingAfterValues:@[ TimestampWithMicros(100, 2) ]] + queryEndingAtValues:@[ TimestampWithMicros(100, 5) ]]]; + XCTAssertEqualObjects(FIRQuerySnapshotGetIDs(querySnapshot), (@[ @"c", @"f", @"b", @"e" ])); +} + +- (void)testTimestampsCanBePassedToQueriesInWhereClause { + FIRTimestamp *currentTimestamp = [FIRTimestamp timestamp]; + int64_t seconds = currentTimestamp.seconds; + int32_t micros = currentTimestamp.nanoseconds / 1000; + FIRCollectionReference *testCollection = [self collectionRefWithDocuments:@{ + @"a" : @{ + @"timestamp" : TimestampWithMicros(seconds, micros + 2), + }, + @"b" : @{ + @"timestamp" : TimestampWithMicros(seconds, micros - 1), + }, + @"c" : @{ + @"timestamp" : TimestampWithMicros(seconds, micros + 3), + }, + @"d" : @{ + @"timestamp" : TimestampWithMicros(seconds, micros), + }, + @"e" : @{ + @"timestamp" : TimestampWithMicros(seconds, micros + 1), + } + }]; + + FIRQuerySnapshot *querySnapshot = [self + readDocumentSetForRef:[[testCollection queryWhereField:@"timestamp" + isGreaterThanOrEqualTo:TimestampWithMicros(seconds, micros)] + queryWhereField:@"timestamp" + isLessThan:TimestampWithMicros(seconds, micros + 3)]]; + XCTAssertEqualObjects(FIRQuerySnapshotGetIDs(querySnapshot), (@[ @"d", @"e", @"a" ])); +} + +- (void)testTimestampsAreTruncatedToMicroseconds { + FIRTimestamp *nanos = [FIRTimestamp timestampWithSeconds:0 nanoseconds:123456789]; + FIRTimestamp *micros = [FIRTimestamp timestampWithSeconds:0 nanoseconds:123456000]; + FIRTimestamp *millis = [FIRTimestamp timestampWithSeconds:0 nanoseconds:123000000]; + FIRCollectionReference *testCollection = [self collectionRefWithDocuments:@{ + @"a" : @{@"timestamp" : nanos}, + }]; + + FIRQuerySnapshot *querySnapshot = + [self readDocumentSetForRef:[testCollection queryWhereField:@"timestamp" isEqualTo:nanos]]; + XCTAssertEqualObjects(FIRQuerySnapshotGetIDs(querySnapshot), (@[ @"a" ])); + + // Because Timestamp should have been truncated to microseconds, the microsecond timestamp + // should be considered equal to the nanosecond one. + querySnapshot = + [self readDocumentSetForRef:[testCollection queryWhereField:@"timestamp" isEqualTo:micros]]; + XCTAssertEqualObjects(FIRQuerySnapshotGetIDs(querySnapshot), (@[ @"a" ])); + + // The truncation is just to the microseconds, however, so the millisecond timestamp should be + // treated as different and thus the query should return no results. + querySnapshot = + [self readDocumentSetForRef:[testCollection queryWhereField:@"timestamp" isEqualTo:millis]]; + XCTAssertEqualObjects(FIRQuerySnapshotGetIDs(querySnapshot), (@[])); +} + @end diff --git a/Firestore/Example/Tests/Integration/API/FIRDatabaseTests.m b/Firestore/Example/Tests/Integration/API/FIRDatabaseTests.mm similarity index 98% rename from Firestore/Example/Tests/Integration/API/FIRDatabaseTests.m rename to Firestore/Example/Tests/Integration/API/FIRDatabaseTests.mm index f557ee67671..751e7ff418a 100644 --- a/Firestore/Example/Tests/Integration/API/FIRDatabaseTests.m +++ b/Firestore/Example/Tests/Integration/API/FIRDatabaseTests.mm @@ -14,14 +14,14 @@ * limitations under the License. */ -@import FirebaseFirestore; +#import -#import #import #import "Firestore/Example/Tests/Util/FSTIntegrationTestCase.h" #import "Firestore/Source/API/FIRFirestore+Internal.h" #import "Firestore/Source/Core/FSTFirestoreClient.h" +#import "Firestore/Source/Util/FSTDispatchQueue.h" @interface FIRDatabaseTests : FSTIntegrationTestCase @end @@ -611,6 +611,7 @@ - (void)testQuerySnapshotEvents_forAdd { } else if (callbacks == 2) { XCTAssertEqual(docSet.count, 1); + XCTAssertTrue([docSet.documents[0] isKindOfClass:[FIRQueryDocumentSnapshot class]]); XCTAssertEqualObjects(docSet.documents[0].data, newData); XCTAssertEqual(docSet.documents[0].metadata.hasPendingWrites, YES); [changeCompletion fulfill]; @@ -926,7 +927,7 @@ - (void)testWriteStreamReconnectsAfterIdle { FIRFirestore *firestore = doc.firestore; [self writeDocumentRef:doc data:@{@"foo" : @"bar"}]; - [self waitForIdleFirestore:firestore]; + [[self queueForFirestore:firestore] runDelayedCallbacksUntil:FSTTimerIDWriteStreamIdle]; [self writeDocumentRef:doc data:@{@"foo" : @"bar"}]; } @@ -935,7 +936,7 @@ - (void)testWatchStreamReconnectsAfterIdle { FIRFirestore *firestore = doc.firestore; [self readSnapshotForRef:[self documentRef] requireOnline:YES]; - [self waitForIdleFirestore:firestore]; + [[self queueForFirestore:firestore] runDelayedCallbacksUntil:FSTTimerIDListenStreamIdle]; [self readSnapshotForRef:[self documentRef] requireOnline:YES]; } diff --git a/Firestore/Example/Tests/Integration/API/FIRFieldsTests.m b/Firestore/Example/Tests/Integration/API/FIRFieldsTests.mm similarity index 87% rename from Firestore/Example/Tests/Integration/API/FIRFieldsTests.m rename to Firestore/Example/Tests/Integration/API/FIRFieldsTests.mm index b647f526fd3..0e75b8e3e9f 100644 --- a/Firestore/Example/Tests/Integration/API/FIRFieldsTests.m +++ b/Firestore/Example/Tests/Integration/API/FIRFieldsTests.mm @@ -14,7 +14,8 @@ * limitations under the License. */ -@import FirebaseFirestore; +#import +#import #import @@ -220,4 +221,28 @@ - (void)testFieldsWithSpecialCharsCanBeUsedInOrderBy { [self awaitExpectations]; } +- (NSDictionary *)testDataWithTimestamp:(FIRTimestamp *)timestamp { + return @{ + @"timestamp" : [timestamp approximateDateValue], + @"metadata" : @{@"nestedTimestamp" : [timestamp approximateDateValue]} + }; +} + +// This test should break once the default for how timestamps are returned changes. +- (void)testThatDataContainsNativeDateType { + NSDate *date = [NSDate date]; + FIRTimestamp *timestamp = [FIRTimestamp timestampWithDate:date]; + FIRDocumentReference *doc = [self documentRef]; + [self writeDocumentRef:doc data:[self testDataWithTimestamp:timestamp]]; + + FIRDocumentSnapshot *result = [self readDocumentForRef:doc]; + NSDate *resultDate = result.data[@"timestamp"]; + XCTAssertEqualWithAccuracy([resultDate timeIntervalSince1970], [date timeIntervalSince1970], + 0.000001); + XCTAssertEqualObjects(result.data[@"timestamp"], resultDate); + NSDate *resultNestedDate = result[@"metadata.nestedTimestamp"]; + XCTAssertEqualWithAccuracy([resultNestedDate timeIntervalSince1970], [date timeIntervalSince1970], + 0.000001); +} + @end diff --git a/Firestore/Example/Tests/Integration/API/FIRListenerRegistrationTests.m b/Firestore/Example/Tests/Integration/API/FIRListenerRegistrationTests.mm similarity index 98% rename from Firestore/Example/Tests/Integration/API/FIRListenerRegistrationTests.m rename to Firestore/Example/Tests/Integration/API/FIRListenerRegistrationTests.mm index 52d73b177cb..036ab32b7a1 100644 --- a/Firestore/Example/Tests/Integration/API/FIRListenerRegistrationTests.m +++ b/Firestore/Example/Tests/Integration/API/FIRListenerRegistrationTests.mm @@ -14,7 +14,7 @@ * limitations under the License. */ -@import FirebaseFirestore; +#import #import diff --git a/Firestore/Example/Tests/Integration/API/FIRQueryTests.m b/Firestore/Example/Tests/Integration/API/FIRQueryTests.mm similarity index 99% rename from Firestore/Example/Tests/Integration/API/FIRQueryTests.m rename to Firestore/Example/Tests/Integration/API/FIRQueryTests.mm index 58f57bc9012..32d746ed024 100644 --- a/Firestore/Example/Tests/Integration/API/FIRQueryTests.m +++ b/Firestore/Example/Tests/Integration/API/FIRQueryTests.mm @@ -14,7 +14,7 @@ * limitations under the License. */ -@import FirebaseFirestore; +#import #import diff --git a/Firestore/Example/Tests/Integration/API/FIRServerTimestampTests.m b/Firestore/Example/Tests/Integration/API/FIRServerTimestampTests.mm similarity index 99% rename from Firestore/Example/Tests/Integration/API/FIRServerTimestampTests.m rename to Firestore/Example/Tests/Integration/API/FIRServerTimestampTests.mm index cc0ab29f657..916ce7e5196 100644 --- a/Firestore/Example/Tests/Integration/API/FIRServerTimestampTests.m +++ b/Firestore/Example/Tests/Integration/API/FIRServerTimestampTests.mm @@ -14,7 +14,7 @@ * limitations under the License. */ -@import FirebaseFirestore; +#import #import diff --git a/Firestore/Example/Tests/Integration/API/FIRSourceTests.m b/Firestore/Example/Tests/Integration/API/FIRSourceTests.mm similarity index 99% rename from Firestore/Example/Tests/Integration/API/FIRSourceTests.m rename to Firestore/Example/Tests/Integration/API/FIRSourceTests.mm index 258d4410912..f3649c9371b 100644 --- a/Firestore/Example/Tests/Integration/API/FIRSourceTests.m +++ b/Firestore/Example/Tests/Integration/API/FIRSourceTests.mm @@ -14,7 +14,7 @@ * limitations under the License. */ -@import FirebaseFirestore; +#import #import diff --git a/Firestore/Example/Tests/Integration/API/FIRTypeTests.m b/Firestore/Example/Tests/Integration/API/FIRTypeTests.mm similarity index 97% rename from Firestore/Example/Tests/Integration/API/FIRTypeTests.m rename to Firestore/Example/Tests/Integration/API/FIRTypeTests.mm index 1874f009709..5140b906795 100644 --- a/Firestore/Example/Tests/Integration/API/FIRTypeTests.m +++ b/Firestore/Example/Tests/Integration/API/FIRTypeTests.mm @@ -14,7 +14,7 @@ * limitations under the License. */ -@import FirebaseFirestore; +#import #import diff --git a/Firestore/Example/Tests/Integration/API/FIRValidationTests.m b/Firestore/Example/Tests/Integration/API/FIRValidationTests.mm similarity index 99% rename from Firestore/Example/Tests/Integration/API/FIRValidationTests.m rename to Firestore/Example/Tests/Integration/API/FIRValidationTests.mm index 8b760c95fa5..49e572a7c4c 100644 --- a/Firestore/Example/Tests/Integration/API/FIRValidationTests.m +++ b/Firestore/Example/Tests/Integration/API/FIRValidationTests.mm @@ -14,7 +14,7 @@ * limitations under the License. */ -@import FirebaseFirestore; +#import #import diff --git a/Firestore/Example/Tests/Integration/API/FIRWriteBatchTests.m b/Firestore/Example/Tests/Integration/API/FIRWriteBatchTests.mm similarity index 99% rename from Firestore/Example/Tests/Integration/API/FIRWriteBatchTests.m rename to Firestore/Example/Tests/Integration/API/FIRWriteBatchTests.mm index 5e7f6d7bc9f..9a2fef194b4 100644 --- a/Firestore/Example/Tests/Integration/API/FIRWriteBatchTests.m +++ b/Firestore/Example/Tests/Integration/API/FIRWriteBatchTests.mm @@ -14,7 +14,7 @@ * limitations under the License. */ -@import FirebaseFirestore; +#import #import diff --git a/Firestore/Example/Tests/Integration/FSTDatastoreTests.m b/Firestore/Example/Tests/Integration/FSTDatastoreTests.mm similarity index 88% rename from Firestore/Example/Tests/Integration/FSTDatastoreTests.m rename to Firestore/Example/Tests/Integration/FSTDatastoreTests.mm index 047f0596bc3..4323ccdd02f 100644 --- a/Firestore/Example/Tests/Integration/FSTDatastoreTests.m +++ b/Firestore/Example/Tests/Integration/FSTDatastoreTests.mm @@ -14,22 +14,19 @@ * limitations under the License. */ -@import FirebaseFirestore; +#import +#import #import #import #import #import "Firestore/Source/API/FIRDocumentReference+Internal.h" #import "Firestore/Source/API/FSTUserDataConverter.h" -#import "Firestore/Source/Auth/FSTEmptyCredentialsProvider.h" -#import "Firestore/Source/Core/FSTDatabaseInfo.h" #import "Firestore/Source/Core/FSTFirestoreClient.h" #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Core/FSTSnapshotVersion.h" -#import "Firestore/Source/Core/FSTTimestamp.h" #import "Firestore/Source/Local/FSTQueryData.h" -#import "Firestore/Source/Model/FSTDatabaseID.h" #import "Firestore/Source/Model/FSTDocumentKey.h" #import "Firestore/Source/Model/FSTFieldValue.h" #import "Firestore/Source/Model/FSTMutation.h" @@ -43,6 +40,16 @@ #import "Firestore/Example/Tests/Util/FSTIntegrationTestCase.h" +#include "Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.h" +#include "Firestore/core/src/firebase/firestore/core/database_info.h" +#include "Firestore/core/src/firebase/firestore/model/database_id.h" +#include "Firestore/core/src/firebase/firestore/util/string_apple.h" + +namespace util = firebase::firestore::util; +using firebase::firestore::auth::EmptyCredentialsProvider; +using firebase::firestore::core::DatabaseInfo; +using firebase::firestore::model::DatabaseId; + NS_ASSUME_NONNULL_BEGIN @interface FSTRemoteStore (Tests) @@ -135,8 +142,9 @@ @interface FSTDatastoreTests : XCTestCase @implementation FSTDatastoreTests { FSTDispatchQueue *_testWorkerQueue; FSTLocalStore *_localStore; - id _credentials; + EmptyCredentialsProvider _credentials; + DatabaseInfo _databaseInfo; FSTDatastore *_datastore; FSTRemoteStore *_remoteStore; } @@ -154,23 +162,18 @@ - (void)setUp { [GRPCCall useInsecureConnectionsForHost:settings.host]; } - FSTDatabaseID *databaseID = - [FSTDatabaseID databaseIDWithProject:projectID database:kDefaultDatabaseID]; + DatabaseId database_id(util::MakeStringView(projectID), DatabaseId::kDefault); - FSTDatabaseInfo *databaseInfo = [FSTDatabaseInfo databaseInfoWithDatabaseID:databaseID - persistenceKey:@"test-key" - host:settings.host - sslEnabled:settings.sslEnabled]; + _databaseInfo = DatabaseInfo(database_id, "test-key", util::MakeStringView(settings.host), + settings.sslEnabled); _testWorkerQueue = [FSTDispatchQueue queueWith:dispatch_queue_create("com.google.firestore.FSTDatastoreTestsWorkerQueue", DISPATCH_QUEUE_SERIAL)]; - _credentials = [[FSTEmptyCredentialsProvider alloc] init]; - - _datastore = [FSTDatastore datastoreWithDatabase:databaseInfo + _datastore = [FSTDatastore datastoreWithDatabase:&_databaseInfo workerDispatchQueue:_testWorkerQueue - credentials:_credentials]; + credentials:&_credentials]; _remoteStore = [FSTRemoteStore remoteStoreWithLocalStore:_localStore datastore:_datastore]; @@ -210,7 +213,7 @@ - (void)testStreamingWrite { FSTSetMutation *mutation = [self setMutation]; FSTMutationBatch *batch = [[FSTMutationBatch alloc] initWithBatchID:23 - localWriteTime:[FSTTimestamp timestamp] + localWriteTime:[FIRTimestamp timestamp] mutations:@[ mutation ]]; [_testWorkerQueue dispatchAsync:^{ [_remoteStore commitBatch:batch]; diff --git a/Firestore/Example/Tests/Integration/FSTSmokeTests.m b/Firestore/Example/Tests/Integration/FSTSmokeTests.mm similarity index 99% rename from Firestore/Example/Tests/Integration/FSTSmokeTests.m rename to Firestore/Example/Tests/Integration/FSTSmokeTests.mm index ad75e502317..cb726b86ad5 100644 --- a/Firestore/Example/Tests/Integration/FSTSmokeTests.m +++ b/Firestore/Example/Tests/Integration/FSTSmokeTests.mm @@ -14,7 +14,7 @@ * limitations under the License. */ -@import FirebaseFirestore; +#import #import diff --git a/Firestore/Example/Tests/Integration/FSTStreamTests.m b/Firestore/Example/Tests/Integration/FSTStreamTests.mm similarity index 82% rename from Firestore/Example/Tests/Integration/FSTStreamTests.m rename to Firestore/Example/Tests/Integration/FSTStreamTests.mm index bbdf372bed2..655036816ac 100644 --- a/Firestore/Example/Tests/Integration/FSTStreamTests.m +++ b/Firestore/Example/Tests/Integration/FSTStreamTests.mm @@ -16,21 +16,29 @@ #import +#import + #import #import "Firestore/Example/Tests/Util/FSTHelpers.h" #import "Firestore/Example/Tests/Util/FSTIntegrationTestCase.h" -#import "Firestore/Example/Tests/Util/FSTTestDispatchQueue.h" -#import "Firestore/Source/Auth/FSTEmptyCredentialsProvider.h" -#import "Firestore/Source/Core/FSTDatabaseInfo.h" -#import "Firestore/Source/Model/FSTDatabaseID.h" #import "Firestore/Source/Remote/FSTDatastore.h" #import "Firestore/Source/Remote/FSTStream.h" #import "Firestore/Source/Util/FSTAssert.h" +#include "Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.h" +#include "Firestore/core/src/firebase/firestore/core/database_info.h" +#include "Firestore/core/src/firebase/firestore/model/database_id.h" +#include "Firestore/core/src/firebase/firestore/util/string_apple.h" + +namespace util = firebase::firestore::util; +using firebase::firestore::auth::EmptyCredentialsProvider; +using firebase::firestore::core::DatabaseInfo; +using firebase::firestore::model::DatabaseId; + /** Exposes otherwise private methods for testing. */ @interface FSTStream (Testing) -- (void)writesFinishedWithError:(NSError *_Nullable)error; +@property(nonatomic, strong, readwrite) id callbackFilter; @end /** @@ -127,9 +135,9 @@ @interface FSTStreamTests : XCTestCase @implementation FSTStreamTests { dispatch_queue_t _testQueue; - FSTTestDispatchQueue *_workerDispatchQueue; - FSTDatabaseInfo *_databaseInfo; - FSTEmptyCredentialsProvider *_credentials; + FSTDispatchQueue *_workerDispatchQueue; + DatabaseInfo _databaseInfo; + EmptyCredentialsProvider _credentials; FSTStreamStatusDelegate *_delegate; /** Single mutation to send to the write stream. */ @@ -140,18 +148,14 @@ - (void)setUp { [super setUp]; FIRFirestoreSettings *settings = [FSTIntegrationTestCase settings]; - FSTDatabaseID *databaseID = - [FSTDatabaseID databaseIDWithProject:[FSTIntegrationTestCase projectID] - database:kDefaultDatabaseID]; + DatabaseId database_id(util::MakeStringView([FSTIntegrationTestCase projectID]), + DatabaseId::kDefault); _testQueue = dispatch_queue_create("FSTStreamTestWorkerQueue", DISPATCH_QUEUE_SERIAL); - _workerDispatchQueue = [[FSTTestDispatchQueue alloc] initWithQueue:_testQueue]; + _workerDispatchQueue = [[FSTDispatchQueue alloc] initWithQueue:_testQueue]; - _databaseInfo = [FSTDatabaseInfo databaseInfoWithDatabaseID:databaseID - persistenceKey:@"test-key" - host:settings.host - sslEnabled:settings.sslEnabled]; - _credentials = [[FSTEmptyCredentialsProvider alloc] init]; + _databaseInfo = DatabaseInfo(database_id, "test-key", util::MakeStringView(settings.host), + settings.sslEnabled); _delegate = [[FSTStreamStatusDelegate alloc] initWithTestCase:self queue:_workerDispatchQueue]; @@ -159,16 +163,16 @@ - (void)setUp { } - (FSTWriteStream *)setUpWriteStream { - FSTDatastore *datastore = [[FSTDatastore alloc] initWithDatabaseInfo:_databaseInfo + FSTDatastore *datastore = [[FSTDatastore alloc] initWithDatabaseInfo:&_databaseInfo workerDispatchQueue:_workerDispatchQueue - credentials:_credentials]; + credentials:&_credentials]; return [datastore createWriteStream]; } - (FSTWatchStream *)setUpWatchStream { - FSTDatastore *datastore = [[FSTDatastore alloc] initWithDatabaseInfo:_databaseInfo + FSTDatastore *datastore = [[FSTDatastore alloc] initWithDatabaseInfo:&_databaseInfo workerDispatchQueue:_workerDispatchQueue - credentials:_credentials]; + credentials:&_credentials]; return [datastore createWatchStream]; } @@ -200,7 +204,9 @@ - (void)testWatchStreamStopBeforeHandshake { }]; // Simulate a final callback from GRPC - [watchStream writesFinishedWithError:nil]; + [_workerDispatchQueue dispatchAsync:^{ + [watchStream.callbackFilter writesFinishedWithError:nil]; + }]; [self verifyDelegateObservedStates:@[ @"watchStreamDidOpen" ]]; } @@ -222,7 +228,9 @@ - (void)testWriteStreamStopBeforeHandshake { }]; // Simulate a final callback from GRPC - [writeStream writesFinishedWithError:nil]; + [_workerDispatchQueue dispatchAsync:^{ + [writeStream.callbackFilter writesFinishedWithError:nil]; + }]; [self verifyDelegateObservedStates:@[ @"writeStreamDidOpen" ]]; } @@ -269,10 +277,14 @@ - (void)testStreamClosesWhenIdle { [writeStream writeHandshake]; }]; - [_delegate awaitNotificationFromBlock:^{ + [_workerDispatchQueue dispatchAsync:^{ [writeStream markIdle]; + XCTAssertTrue( + [_workerDispatchQueue containsDelayedCallbackWithTimerID:FSTTimerIDWriteStreamIdle]); }]; + [_workerDispatchQueue runDelayedCallbacksUntil:FSTTimerIDWriteStreamIdle]; + dispatch_sync(_testQueue, ^{ XCTAssertFalse([writeStream isOpen]); }); @@ -296,7 +308,11 @@ - (void)testStreamCancelsIdleOnWrite { // Mark the stream idle, but immediately cancel the idle timer by issuing another write. [_delegate awaitNotificationFromBlock:^{ [writeStream markIdle]; + XCTAssertTrue( + [_workerDispatchQueue containsDelayedCallbackWithTimerID:FSTTimerIDWriteStreamIdle]); [writeStream writeMutations:_mutations]; + XCTAssertFalse( + [_workerDispatchQueue containsDelayedCallbackWithTimerID:FSTTimerIDWriteStreamIdle]); }]; dispatch_sync(_testQueue, ^{ diff --git a/Firestore/Example/Tests/Integration/FSTTransactionTests.m b/Firestore/Example/Tests/Integration/FSTTransactionTests.mm similarity index 99% rename from Firestore/Example/Tests/Integration/FSTTransactionTests.m rename to Firestore/Example/Tests/Integration/FSTTransactionTests.mm index 2e828c92fb1..2464b0c5624 100644 --- a/Firestore/Example/Tests/Integration/FSTTransactionTests.m +++ b/Firestore/Example/Tests/Integration/FSTTransactionTests.mm @@ -14,7 +14,7 @@ * limitations under the License. */ -@import FirebaseFirestore; +#import #import #include @@ -274,7 +274,6 @@ - (void)testIncrementTransactionally { double newCount = ((NSNumber *)snapshot[@"count"]).doubleValue + 1.0; [transaction setData:@{ @"count" : @(newCount) } forDocument:doc]; return @YES; - } completion:^(id _Nullable result, NSError *_Nullable error) { [expectation fulfill]; @@ -316,7 +315,6 @@ - (void)testUpdateTransactionally { double newCount = ((NSNumber *)snapshot[@"count"]).doubleValue + 1.0; [transaction updateData:@{ @"count" : @(newCount) } forDocument:doc]; return @YES; - } completion:^(id _Nullable result, NSError *_Nullable error) { [expectation fulfill]; diff --git a/Firestore/Example/Tests/Local/FSTEagerGarbageCollectorTests.m b/Firestore/Example/Tests/Local/FSTEagerGarbageCollectorTests.mm similarity index 100% rename from Firestore/Example/Tests/Local/FSTEagerGarbageCollectorTests.m rename to Firestore/Example/Tests/Local/FSTEagerGarbageCollectorTests.mm diff --git a/Firestore/Example/Tests/Local/FSTLevelDBLocalStoreTests.m b/Firestore/Example/Tests/Local/FSTLevelDBLocalStoreTests.mm similarity index 96% rename from Firestore/Example/Tests/Local/FSTLevelDBLocalStoreTests.m rename to Firestore/Example/Tests/Local/FSTLevelDBLocalStoreTests.mm index f71f5c96cbf..97e3c5b07b7 100644 --- a/Firestore/Example/Tests/Local/FSTLevelDBLocalStoreTests.m +++ b/Firestore/Example/Tests/Local/FSTLevelDBLocalStoreTests.mm @@ -18,7 +18,6 @@ #import -#import "Firestore/Source/Auth/FSTUser.h" #import "Firestore/Source/Local/FSTLevelDB.h" #import "Firestore/Example/Tests/Local/FSTLocalStoreTests.h" diff --git a/Firestore/Example/Tests/Local/FSTLevelDBMigrationsTests.mm b/Firestore/Example/Tests/Local/FSTLevelDBMigrationsTests.mm new file mode 100644 index 00000000000..3559d5dd724 --- /dev/null +++ b/Firestore/Example/Tests/Local/FSTLevelDBMigrationsTests.mm @@ -0,0 +1,104 @@ +/* + * Copyright 2018 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 +#include + +#import "Firestore/Protos/objc/firestore/local/Target.pbobjc.h" +#import "Firestore/Source/Local/FSTLevelDBKey.h" +#import "Firestore/Source/Local/FSTLevelDBMigrations.h" +#import "Firestore/Source/Local/FSTLevelDBQueryCache.h" +#import "Firestore/Source/Local/FSTWriteGroup.h" + +#include "Firestore/core/src/firebase/firestore/util/ordered_code.h" + +#import "Firestore/Example/Tests/Local/FSTPersistenceTestHelpers.h" + +NS_ASSUME_NONNULL_BEGIN + +using firebase::firestore::util::OrderedCode; +using leveldb::DB; +using leveldb::Options; +using leveldb::Status; + +@interface FSTLevelDBMigrationsTests : XCTestCase +@end + +@implementation FSTLevelDBMigrationsTests { + std::shared_ptr _db; +} + +- (void)setUp { + Options options; + options.error_if_exists = true; + options.create_if_missing = true; + + NSString *dir = [FSTPersistenceTestHelpers levelDBDir]; + DB *db; + Status status = DB::Open(options, [dir UTF8String], &db); + XCTAssert(status.ok(), @"Failed to create db: %s", status.ToString().c_str()); + _db.reset(db); +} + +- (void)tearDown { + _db.reset(); +} + +- (void)testAddsTargetGlobal { + FSTPBTargetGlobal *metadata = [FSTLevelDBQueryCache readTargetMetadataFromDB:_db]; + XCTAssertNil(metadata, @"Not expecting metadata yet, we should have an empty db"); + [FSTLevelDBMigrations runMigrationsOnDB:_db]; + metadata = [FSTLevelDBQueryCache readTargetMetadataFromDB:_db]; + XCTAssertNotNil(metadata, @"Migrations should have added the metadata"); +} + +- (void)testSetsVersionNumber { + FSTLevelDBSchemaVersion initial = [FSTLevelDBMigrations schemaVersionForDB:_db]; + XCTAssertEqual(0, initial, "No version should be equivalent to 0"); + + // Pick an arbitrary high migration number and migrate to it. + [FSTLevelDBMigrations runMigrationsOnDB:_db]; + FSTLevelDBSchemaVersion actual = [FSTLevelDBMigrations schemaVersionForDB:_db]; + XCTAssertGreaterThan(actual, 0, @"Expected to migrate to a schema version > 0"); +} + +- (void)testCountsQueries { + NSUInteger expected = 50; + FSTWriteGroup *group = [FSTWriteGroup groupWithAction:@"Setup"]; + for (int i = 0; i < expected; i++) { + std::string key = [FSTLevelDBTargetKey keyWithTargetID:i]; + [group setData:"dummy" forKey:key]; + } + // Add a dummy entry after the targets to make sure the iteration is correctly bounded. + // Use a table that would sort logically right after that table 'target'. + std::string dummyKey; + // Magic number that indicates a table name follows. Needed to mimic the prefix to the target + // table. + OrderedCode::WriteSignedNumIncreasing(&dummyKey, 5); + OrderedCode::WriteString(&dummyKey, "targetA"); + [group setData:"dummy" forKey:dummyKey]; + + Status status = [group writeToDB:_db]; + XCTAssertTrue(status.ok(), @"Failed to write targets"); + + [FSTLevelDBMigrations runMigrationsOnDB:_db]; + FSTPBTargetGlobal *metadata = [FSTLevelDBQueryCache readTargetMetadataFromDB:_db]; + XCTAssertEqual(expected, metadata.targetCount, @"Failed to count all of the targets we added"); +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firestore/Example/Tests/Local/FSTLevelDBMutationQueueTests.mm b/Firestore/Example/Tests/Local/FSTLevelDBMutationQueueTests.mm index 6c26fd9af49..2e240553e21 100644 --- a/Firestore/Example/Tests/Local/FSTLevelDBMutationQueueTests.mm +++ b/Firestore/Example/Tests/Local/FSTLevelDBMutationQueueTests.mm @@ -19,9 +19,7 @@ #import #include -#include "Firestore/Port/ordered_code.h" #import "Firestore/Protos/objc/firestore/local/Mutation.pbobjc.h" -#import "Firestore/Source/Auth/FSTUser.h" #import "Firestore/Source/Local/FSTLevelDB.h" #import "Firestore/Source/Local/FSTLevelDBKey.h" #import "Firestore/Source/Local/FSTWriteGroup.h" @@ -29,6 +27,9 @@ #import "Firestore/Example/Tests/Local/FSTMutationQueueTests.h" #import "Firestore/Example/Tests/Local/FSTPersistenceTestHelpers.h" +#include "Firestore/core/src/firebase/firestore/auth/user.h" +#include "Firestore/core/src/firebase/firestore/util/ordered_code.h" + NS_ASSUME_NONNULL_BEGIN using leveldb::DB; @@ -36,7 +37,8 @@ using leveldb::Status; using leveldb::WriteOptions; using Firestore::StringView; -using Firestore::OrderedCode; +using firebase::firestore::auth::User; +using firebase::firestore::util::OrderedCode; // A dummy mutation value, useful for testing code that's known to examine only mutation keys. static const char *kDummy = "1"; @@ -68,7 +70,7 @@ @implementation FSTLevelDBMutationQueueTests { - (void)setUp { [super setUp]; _db = [FSTPersistenceTestHelpers levelDBPersistence]; - self.mutationQueue = [_db mutationQueueForUser:[[FSTUser alloc] initWithUID:@"user"]]; + self.mutationQueue = [_db mutationQueueForUser:User("user")]; self.persistence = _db; FSTWriteGroup *group = [self.persistence startGroupWithAction:@"Start MutationQueue"]; diff --git a/Firestore/Example/Tests/Local/FSTLevelDBQueryCacheTests.m b/Firestore/Example/Tests/Local/FSTLevelDBQueryCacheTests.mm similarity index 100% rename from Firestore/Example/Tests/Local/FSTLevelDBQueryCacheTests.m rename to Firestore/Example/Tests/Local/FSTLevelDBQueryCacheTests.mm diff --git a/Firestore/Example/Tests/Local/FSTLevelDBRemoteDocumentCacheTests.mm b/Firestore/Example/Tests/Local/FSTLevelDBRemoteDocumentCacheTests.mm index 1f84aa686ac..638ab2f3675 100644 --- a/Firestore/Example/Tests/Local/FSTLevelDBRemoteDocumentCacheTests.mm +++ b/Firestore/Example/Tests/Local/FSTLevelDBRemoteDocumentCacheTests.mm @@ -18,16 +18,17 @@ #include -#include "Firestore/Port/ordered_code.h" #import "Firestore/Source/Local/FSTLevelDB.h" #import "Firestore/Source/Local/FSTLevelDBKey.h" #import "Firestore/Example/Tests/Local/FSTPersistenceTestHelpers.h" +#include "Firestore/core/src/firebase/firestore/util/ordered_code.h" + NS_ASSUME_NONNULL_BEGIN using leveldb::WriteOptions; -using Firestore::OrderedCode; +using firebase::firestore::util::OrderedCode; // A dummy document value, useful for testing code that's known to examine only document keys. static const char *kDummy = "1"; diff --git a/Firestore/Example/Tests/Local/FSTLocalSerializerTests.m b/Firestore/Example/Tests/Local/FSTLocalSerializerTests.mm similarity index 92% rename from Firestore/Example/Tests/Local/FSTLocalSerializerTests.m rename to Firestore/Example/Tests/Local/FSTLocalSerializerTests.mm index 27c3dc35fe9..1793b23546a 100644 --- a/Firestore/Example/Tests/Local/FSTLocalSerializerTests.m +++ b/Firestore/Example/Tests/Local/FSTLocalSerializerTests.mm @@ -16,6 +16,7 @@ #import "Firestore/Source/Local/FSTLocalSerializer.h" +#import #import #import "Firestore/Protos/objc/firestore/local/MaybeDocument.pbobjc.h" @@ -29,9 +30,7 @@ #import "Firestore/Protos/objc/google/type/Latlng.pbobjc.h" #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Core/FSTSnapshotVersion.h" -#import "Firestore/Source/Core/FSTTimestamp.h" #import "Firestore/Source/Local/FSTQueryData.h" -#import "Firestore/Source/Model/FSTDatabaseID.h" #import "Firestore/Source/Model/FSTDocument.h" #import "Firestore/Source/Model/FSTDocumentKey.h" #import "Firestore/Source/Model/FSTFieldValue.h" @@ -42,6 +41,11 @@ #import "Firestore/Example/Tests/Util/FSTHelpers.h" +#include "Firestore/core/src/firebase/firestore/model/database_id.h" +#include "Firestore/core/src/firebase/firestore/util/string_apple.h" + +using firebase::firestore::model::DatabaseId; + NS_ASSUME_NONNULL_BEGIN @interface FSTSerializerBeta (Test) @@ -52,7 +56,9 @@ - (GCFSValue *)encodedInteger:(int64_t)value; - (GCFSValue *)encodedString:(NSString *)value; @end -@interface FSTLocalSerializerTests : XCTestCase +@interface FSTLocalSerializerTests : XCTestCase { + DatabaseId _databaseId; +} @property(nonatomic, strong) FSTLocalSerializer *serializer; @property(nonatomic, strong) FSTSerializerBeta *remoteSerializer; @@ -62,8 +68,8 @@ @interface FSTLocalSerializerTests : XCTestCase @implementation FSTLocalSerializerTests - (void)setUp { - FSTDatabaseID *databaseID = [FSTDatabaseID databaseIDWithProject:@"p" database:@"d"]; - self.remoteSerializer = [[FSTSerializerBeta alloc] initWithDatabaseID:databaseID]; + _databaseId = DatabaseId("p", "d"); + self.remoteSerializer = [[FSTSerializerBeta alloc] initWithDatabaseID:&_databaseId]; self.serializer = [[FSTLocalSerializer alloc] initWithRemoteSerializer:self.remoteSerializer]; } @@ -77,7 +83,7 @@ - (void)testEncodesMutationBatch { @"num" : @1 }) precondition:[FSTPrecondition preconditionWithExists:YES]]; FSTMutation *del = FSTTestDeleteMutation(@"baz/quux"); - FSTTimestamp *writeTime = [FSTTimestamp timestamp]; + FIRTimestamp *writeTime = [FIRTimestamp timestamp]; FSTMutationBatch *model = [[FSTMutationBatch alloc] initWithBatchID:42 localWriteTime:writeTime mutations:@[ set, patch, del ]]; @@ -103,7 +109,7 @@ - (void)testEncodesMutationBatch { GPBTimestamp *writeTimeProto = [GPBTimestamp message]; writeTimeProto.seconds = writeTime.seconds; - writeTimeProto.nanos = writeTime.nanos; + writeTimeProto.nanos = writeTime.nanoseconds; FSTPBWriteBatch *batchProto = [FSTPBWriteBatch message]; batchProto.batchId = 42; @@ -157,6 +163,7 @@ - (void)testEncodesQueryData { FSTQueryData *queryData = [[FSTQueryData alloc] initWithQuery:query targetID:targetID + listenSequenceNumber:10 purpose:FSTQueryPurposeListen snapshotVersion:version resumeToken:resumeToken]; @@ -166,6 +173,7 @@ - (void)testEncodesQueryData { FSTPBTarget *expected = [FSTPBTarget message]; expected.targetId = targetID; + expected.lastListenSequenceNumber = 10; expected.snapshotVersion.nanos = 1039000; expected.resumeToken = [resumeToken copy]; expected.query.parent = queryTarget.parent; diff --git a/Firestore/Example/Tests/Local/FSTLocalStoreTests.m b/Firestore/Example/Tests/Local/FSTLocalStoreTests.mm similarity index 98% rename from Firestore/Example/Tests/Local/FSTLocalStoreTests.m rename to Firestore/Example/Tests/Local/FSTLocalStoreTests.mm index 45d1815065f..393f77b280c 100644 --- a/Firestore/Example/Tests/Local/FSTLocalStoreTests.m +++ b/Firestore/Example/Tests/Local/FSTLocalStoreTests.mm @@ -16,11 +16,10 @@ #import "Firestore/Source/Local/FSTLocalStore.h" +#import #import -#import "Firestore/Source/Auth/FSTUser.h" #import "Firestore/Source/Core/FSTQuery.h" -#import "Firestore/Source/Core/FSTTimestamp.h" #import "Firestore/Source/Local/FSTEagerGarbageCollector.h" #import "Firestore/Source/Local/FSTLocalWriteResult.h" #import "Firestore/Source/Local/FSTNoOpGarbageCollector.h" @@ -42,6 +41,10 @@ #import "Firestore/third_party/Immutable/Tests/FSTImmutableSortedDictionary+Testing.h" #import "Firestore/third_party/Immutable/Tests/FSTImmutableSortedSet+Testing.h" +#include "Firestore/core/src/firebase/firestore/auth/user.h" + +using firebase::firestore::auth::User; + NS_ASSUME_NONNULL_BEGIN /** Creates a document version dictionary mapping the document in @a mutation to @a version. */ @@ -77,7 +80,7 @@ - (void)setUp { id garbageCollector = [[FSTEagerGarbageCollector alloc] init]; self.localStore = [[FSTLocalStore alloc] initWithPersistence:persistence garbageCollector:garbageCollector - initialUser:[FSTUser unauthenticatedUser]]; + initialUser:User::Unauthenticated()]; [self.localStore start]; _batches = [NSMutableArray array]; @@ -112,7 +115,7 @@ - (void)restartWithNoopGarbageCollector { id garbageCollector = [[FSTNoOpGarbageCollector alloc] init]; self.localStore = [[FSTLocalStore alloc] initWithPersistence:self.localStorePersistence garbageCollector:garbageCollector - initialUser:[FSTUser unauthenticatedUser]]; + initialUser:User::Unauthenticated()]; [self.localStore start]; } @@ -124,7 +127,7 @@ - (void)writeMutations:(NSArray *)mutations { FSTLocalWriteResult *result = [self.localStore locallyWriteMutations:mutations]; XCTAssertNotNil(result); [self.batches addObject:[[FSTMutationBatch alloc] initWithBatchID:result.batchID - localWriteTime:[FSTTimestamp timestamp] + localWriteTime:[FIRTimestamp timestamp] mutations:mutations]]; self.lastChanges = result.changes; } @@ -225,7 +228,7 @@ - (void)testMutationBatchKeys { FSTMutation *set1 = FSTTestSetMutation(@"foo/bar", @{@"foo" : @"bar"}); FSTMutation *set2 = FSTTestSetMutation(@"bar/baz", @{@"bar" : @"baz"}); FSTMutationBatch *batch = [[FSTMutationBatch alloc] initWithBatchID:1 - localWriteTime:[FSTTimestamp timestamp] + localWriteTime:[FIRTimestamp timestamp] mutations:@[ set1, set2 ]]; FSTDocumentKeySet *keys = [batch keys]; XCTAssertEqual(keys.count, 2); diff --git a/Firestore/Example/Tests/Local/FSTMemoryLocalStoreTests.m b/Firestore/Example/Tests/Local/FSTMemoryLocalStoreTests.mm similarity index 100% rename from Firestore/Example/Tests/Local/FSTMemoryLocalStoreTests.m rename to Firestore/Example/Tests/Local/FSTMemoryLocalStoreTests.mm diff --git a/Firestore/Example/Tests/Local/FSTMemoryMutationQueueTests.m b/Firestore/Example/Tests/Local/FSTMemoryMutationQueueTests.mm similarity index 87% rename from Firestore/Example/Tests/Local/FSTMemoryMutationQueueTests.m rename to Firestore/Example/Tests/Local/FSTMemoryMutationQueueTests.mm index ab7afee2ccf..55670780dc5 100644 --- a/Firestore/Example/Tests/Local/FSTMemoryMutationQueueTests.m +++ b/Firestore/Example/Tests/Local/FSTMemoryMutationQueueTests.mm @@ -16,12 +16,15 @@ #import "Firestore/Source/Local/FSTMemoryMutationQueue.h" -#import "Firestore/Source/Auth/FSTUser.h" #import "Firestore/Source/Local/FSTMemoryPersistence.h" #import "Firestore/Example/Tests/Local/FSTMutationQueueTests.h" #import "Firestore/Example/Tests/Local/FSTPersistenceTestHelpers.h" +#include "Firestore/core/src/firebase/firestore/auth/user.h" + +using firebase::firestore::auth::User; + @interface FSTMemoryMutationQueueTests : FSTMutationQueueTests @end @@ -35,8 +38,7 @@ - (void)setUp { [super setUp]; self.persistence = [FSTPersistenceTestHelpers memoryPersistence]; - self.mutationQueue = - [self.persistence mutationQueueForUser:[[FSTUser alloc] initWithUID:@"user"]]; + self.mutationQueue = [self.persistence mutationQueueForUser:User("user")]; } @end diff --git a/Firestore/Example/Tests/Local/FSTMemoryQueryCacheTests.m b/Firestore/Example/Tests/Local/FSTMemoryQueryCacheTests.mm similarity index 100% rename from Firestore/Example/Tests/Local/FSTMemoryQueryCacheTests.m rename to Firestore/Example/Tests/Local/FSTMemoryQueryCacheTests.mm diff --git a/Firestore/Example/Tests/Local/FSTMemoryRemoteDocumentCacheTests.m b/Firestore/Example/Tests/Local/FSTMemoryRemoteDocumentCacheTests.mm similarity index 100% rename from Firestore/Example/Tests/Local/FSTMemoryRemoteDocumentCacheTests.m rename to Firestore/Example/Tests/Local/FSTMemoryRemoteDocumentCacheTests.mm diff --git a/Firestore/Example/Tests/Local/FSTMutationQueueTests.m b/Firestore/Example/Tests/Local/FSTMutationQueueTests.mm similarity index 97% rename from Firestore/Example/Tests/Local/FSTMutationQueueTests.m rename to Firestore/Example/Tests/Local/FSTMutationQueueTests.mm index 020a0a7425a..7d305d0ca2a 100644 --- a/Firestore/Example/Tests/Local/FSTMutationQueueTests.m +++ b/Firestore/Example/Tests/Local/FSTMutationQueueTests.mm @@ -16,9 +16,8 @@ #import "Firestore/Example/Tests/Local/FSTMutationQueueTests.h" -#import "Firestore/Source/Auth/FSTUser.h" +#import #import "Firestore/Source/Core/FSTQuery.h" -#import "Firestore/Source/Core/FSTTimestamp.h" #import "Firestore/Source/Local/FSTEagerGarbageCollector.h" #import "Firestore/Source/Local/FSTMutationQueue.h" #import "Firestore/Source/Local/FSTPersistence.h" @@ -28,6 +27,10 @@ #import "Firestore/Example/Tests/Util/FSTHelpers.h" +#include "Firestore/core/src/firebase/firestore/auth/user.h" + +using firebase::firestore::auth::User; + NS_ASSUME_NONNULL_BEGIN @implementation FSTMutationQueueTests @@ -127,8 +130,7 @@ - (void)testHighestAcknowledgedBatchIDNeverExceedsNextBatchID { // Restart the queue so that nextBatchID will be reset. [self.mutationQueue shutdown]; - self.mutationQueue = - [self.persistence mutationQueueForUser:[[FSTUser alloc] initWithUID:@"user"]]; + self.mutationQueue = [self.persistence mutationQueueForUser:User("user")]; FSTWriteGroup *group = [self.persistence startGroupWithAction:@"Start MutationQueue"]; [self.mutationQueue startWithGroup:group]; @@ -256,7 +258,7 @@ - (void)testAllMutationBatchesAffectingDocumentKey { FSTWriteGroup *group = [self.persistence startGroupWithAction:@"New mutation batch"]; for (FSTMutation *mutation in mutations) { FSTMutationBatch *batch = - [self.mutationQueue addMutationBatchWithWriteTime:[FSTTimestamp timestamp] + [self.mutationQueue addMutationBatchWithWriteTime:[FIRTimestamp timestamp] mutations:@[ mutation ] group:group]; [batches addObject:batch]; @@ -293,7 +295,7 @@ - (void)testAllMutationBatchesAffectingQuery { FSTWriteGroup *group = [self.persistence startGroupWithAction:@"New mutation batch"]; for (FSTMutation *mutation in mutations) { FSTMutationBatch *batch = - [self.mutationQueue addMutationBatchWithWriteTime:[FSTTimestamp timestamp] + [self.mutationQueue addMutationBatchWithWriteTime:[FIRTimestamp timestamp] mutations:@[ mutation ] group:group]; [batches addObject:batch]; @@ -438,7 +440,7 @@ - (FSTMutationBatch *)addMutationBatchWithKey:(NSString *)key { FSTWriteGroup *group = [self.persistence startGroupWithAction:@"New mutation batch"]; FSTMutationBatch *batch = - [self.mutationQueue addMutationBatchWithWriteTime:[FSTTimestamp timestamp] + [self.mutationQueue addMutationBatchWithWriteTime:[FIRTimestamp timestamp] mutations:@[ mutation ] group:group]; [self.persistence commitGroup:group]; diff --git a/Firestore/Example/Tests/Local/FSTPersistenceTestHelpers.h b/Firestore/Example/Tests/Local/FSTPersistenceTestHelpers.h index 936bacf9182..5859d4bfdbb 100644 --- a/Firestore/Example/Tests/Local/FSTPersistenceTestHelpers.h +++ b/Firestore/Example/Tests/Local/FSTPersistenceTestHelpers.h @@ -23,6 +23,12 @@ NS_ASSUME_NONNULL_BEGIN @interface FSTPersistenceTestHelpers : NSObject +/** + * @return The directory where a leveldb instance can store data files. Any files that existed + * there will be deleted first. + */ ++ (NSString *)levelDBDir; + /** * Creates and starts a new FSTLevelDB instance for testing, destroying any previous contents * if they existed. diff --git a/Firestore/Example/Tests/Local/FSTPersistenceTestHelpers.m b/Firestore/Example/Tests/Local/FSTPersistenceTestHelpers.mm similarity index 83% rename from Firestore/Example/Tests/Local/FSTPersistenceTestHelpers.m rename to Firestore/Example/Tests/Local/FSTPersistenceTestHelpers.mm index c773b1258ad..b59a062cd7e 100644 --- a/Firestore/Example/Tests/Local/FSTPersistenceTestHelpers.m +++ b/Firestore/Example/Tests/Local/FSTPersistenceTestHelpers.mm @@ -19,17 +19,20 @@ #import "Firestore/Source/Local/FSTLevelDB.h" #import "Firestore/Source/Local/FSTLocalSerializer.h" #import "Firestore/Source/Local/FSTMemoryPersistence.h" -#import "Firestore/Source/Model/FSTDatabaseID.h" #import "Firestore/Source/Remote/FSTSerializerBeta.h" +#include "Firestore/core/src/firebase/firestore/model/database_id.h" +#include "Firestore/core/src/firebase/firestore/util/string_apple.h" + +using firebase::firestore::model::DatabaseId; + NS_ASSUME_NONNULL_BEGIN @implementation FSTPersistenceTestHelpers -+ (FSTLevelDB *)levelDBPersistence { ++ (NSString *)levelDBDir { NSError *error; NSFileManager *files = [NSFileManager defaultManager]; - NSString *dir = [NSTemporaryDirectory() stringByAppendingPathComponent:@"FSTPersistenceTestHelpers"]; if ([files fileExistsAtPath:dir]) { @@ -40,12 +43,19 @@ + (FSTLevelDB *)levelDBPersistence { format:@"Failed to clean up leveldb path %@: %@", dir, error]; } } + return dir; +} + ++ (FSTLevelDB *)levelDBPersistence { + // This owns the DatabaseIds since we do not have FirestoreClient instance to own them. + static DatabaseId database_id{"p", "d"}; - FSTDatabaseID *databaseID = [FSTDatabaseID databaseIDWithProject:@"p" database:@"d"]; - FSTSerializerBeta *remoteSerializer = [[FSTSerializerBeta alloc] initWithDatabaseID:databaseID]; + NSString *dir = [self levelDBDir]; + FSTSerializerBeta *remoteSerializer = [[FSTSerializerBeta alloc] initWithDatabaseID:&database_id]; FSTLocalSerializer *serializer = [[FSTLocalSerializer alloc] initWithRemoteSerializer:remoteSerializer]; FSTLevelDB *db = [[FSTLevelDB alloc] initWithDirectory:dir serializer:serializer]; + NSError *error; BOOL success = [db start:&error]; if (!success) { [NSException raise:NSInternalInconsistencyException diff --git a/Firestore/Example/Tests/Local/FSTQueryCacheTests.m b/Firestore/Example/Tests/Local/FSTQueryCacheTests.mm similarity index 80% rename from Firestore/Example/Tests/Local/FSTQueryCacheTests.m rename to Firestore/Example/Tests/Local/FSTQueryCacheTests.mm index 0b80bd9472e..5cdfd668a4b 100644 --- a/Firestore/Example/Tests/Local/FSTQueryCacheTests.m +++ b/Firestore/Example/Tests/Local/FSTQueryCacheTests.mm @@ -31,12 +31,18 @@ @implementation FSTQueryCacheTests { FSTQuery *_queryRooms; + FSTListenSequenceNumber _previousSequenceNumber; + FSTTargetID _previousTargetID; + FSTTestSnapshotVersion _previousSnapshotVersion; } - (void)setUp { [super setUp]; _queryRooms = FSTTestQuery(@"rooms"); + _previousSequenceNumber = 1000; + _previousTargetID = 500; + _previousSnapshotVersion = 100; } /** @@ -56,7 +62,7 @@ - (void)testReadQueryNotInCache { - (void)testSetAndReadAQuery { if ([self isTestBaseClass]) return; - FSTQueryData *queryData = [self queryDataWithQuery:_queryRooms targetID:1 version:1]; + FSTQueryData *queryData = [self queryDataWithQuery:_queryRooms]; [self addQueryData:queryData]; FSTQueryData *result = [self.queryCache queryDataForQuery:_queryRooms]; @@ -74,15 +80,16 @@ - (void)testCanonicalIDCollision { FSTQuery *q2 = [FSTTestQuery(@"a") queryByAddingFilter:FSTTestFilter(@"foo", @"==", @"1")]; XCTAssertEqualObjects(q1.canonicalID, q2.canonicalID); - FSTQueryData *data1 = [self queryDataWithQuery:q1 targetID:1 version:1]; + FSTQueryData *data1 = [self queryDataWithQuery:q1]; [self addQueryData:data1]; // Using the other query should not return the query cache entry despite equal canonicalIDs. XCTAssertNil([self.queryCache queryDataForQuery:q2]); XCTAssertEqualObjects([self.queryCache queryDataForQuery:q1], data1); - FSTQueryData *data2 = [self queryDataWithQuery:q2 targetID:2 version:1]; + FSTQueryData *data2 = [self queryDataWithQuery:q2]; [self addQueryData:data2]; + XCTAssertEqual([self.queryCache count], 2); XCTAssertEqualObjects([self.queryCache queryDataForQuery:q1], data1); XCTAssertEqualObjects([self.queryCache queryDataForQuery:q2], data2); @@ -90,19 +97,23 @@ - (void)testCanonicalIDCollision { [self removeQueryData:data1]; XCTAssertNil([self.queryCache queryDataForQuery:q1]); XCTAssertEqualObjects([self.queryCache queryDataForQuery:q2], data2); + XCTAssertEqual([self.queryCache count], 1); [self removeQueryData:data2]; XCTAssertNil([self.queryCache queryDataForQuery:q1]); XCTAssertNil([self.queryCache queryDataForQuery:q2]); + XCTAssertEqual([self.queryCache count], 0); } - (void)testSetQueryToNewValue { if ([self isTestBaseClass]) return; - FSTQueryData *queryData1 = [self queryDataWithQuery:_queryRooms targetID:1 version:1]; + FSTQueryData *queryData1 = + [self queryDataWithQuery:_queryRooms targetID:1 listenSequenceNumber:10 version:1]; [self addQueryData:queryData1]; - FSTQueryData *queryData2 = [self queryDataWithQuery:_queryRooms targetID:1 version:2]; + FSTQueryData *queryData2 = + [self queryDataWithQuery:_queryRooms targetID:1 listenSequenceNumber:10 version:2]; [self addQueryData:queryData2]; FSTQueryData *result = [self.queryCache queryDataForQuery:_queryRooms]; @@ -115,7 +126,7 @@ - (void)testSetQueryToNewValue { - (void)testRemoveQuery { if ([self isTestBaseClass]) return; - FSTQueryData *queryData1 = [self queryDataWithQuery:_queryRooms targetID:1 version:1]; + FSTQueryData *queryData1 = [self queryDataWithQuery:_queryRooms]; [self addQueryData:queryData1]; [self removeQueryData:queryData1]; @@ -127,7 +138,7 @@ - (void)testRemoveQuery { - (void)testRemoveNonExistentQuery { if ([self isTestBaseClass]) return; - FSTQueryData *queryData = [self queryDataWithQuery:_queryRooms targetID:1 version:1]; + FSTQueryData *queryData = [self queryDataWithQuery:_queryRooms]; // no-op, but make sure it doesn't throw. XCTAssertNoThrow([self removeQueryData:queryData]); @@ -136,7 +147,7 @@ - (void)testRemoveNonExistentQuery { - (void)testRemoveQueryRemovesMatchingKeysToo { if ([self isTestBaseClass]) return; - FSTQueryData *rooms = [self queryDataWithQuery:_queryRooms targetID:1 version:1]; + FSTQueryData *rooms = [self queryDataWithQuery:_queryRooms]; [self addQueryData:rooms]; FSTDocumentKey *key1 = FSTTestDocKey(@"rooms/foo"); @@ -204,14 +215,14 @@ - (void)testRemoveEmitsGarbageEvents { [garbageCollector addGarbageSource:self.queryCache]; FSTAssertEqualSets([garbageCollector collectGarbage], @[]); - FSTQueryData *rooms = [self queryDataWithQuery:FSTTestQuery(@"rooms") targetID:1 version:1]; + FSTQueryData *rooms = [self queryDataWithQuery:FSTTestQuery(@"rooms")]; FSTDocumentKey *room1 = FSTTestDocKey(@"rooms/bar"); FSTDocumentKey *room2 = FSTTestDocKey(@"rooms/foo"); [self addQueryData:rooms]; [self addMatchingKey:room1 forTargetID:rooms.targetID]; [self addMatchingKey:room2 forTargetID:rooms.targetID]; - FSTQueryData *halls = [self queryDataWithQuery:FSTTestQuery(@"halls") targetID:2 version:1]; + FSTQueryData *halls = [self queryDataWithQuery:FSTTestQuery(@"halls")]; FSTDocumentKey *hall1 = FSTTestDocKey(@"halls/bar"); FSTDocumentKey *hall2 = FSTTestDocKey(@"halls/foo"); [self addQueryData:halls]; @@ -249,6 +260,46 @@ - (void)testMatchingKeysForTargetID { FSTAssertEqualSets([self.queryCache matchingKeysForTargetID:2], (@[ key1, key3 ])); } +- (void)testHighestListenSequenceNumber { + if ([self isTestBaseClass]) return; + + FSTQueryData *query1 = [[FSTQueryData alloc] initWithQuery:FSTTestQuery(@"rooms") + targetID:1 + listenSequenceNumber:10 + purpose:FSTQueryPurposeListen]; + [self addQueryData:query1]; + FSTQueryData *query2 = [[FSTQueryData alloc] initWithQuery:FSTTestQuery(@"halls") + targetID:2 + listenSequenceNumber:20 + purpose:FSTQueryPurposeListen]; + [self addQueryData:query2]; + XCTAssertEqual([self.queryCache highestListenSequenceNumber], 20); + + // TargetIDs never come down. + [self removeQueryData:query2]; + XCTAssertEqual([self.queryCache highestListenSequenceNumber], 20); + + // A query with an empty result set still counts. + FSTQueryData *query3 = [[FSTQueryData alloc] initWithQuery:FSTTestQuery(@"garages") + targetID:42 + listenSequenceNumber:100 + purpose:FSTQueryPurposeListen]; + [self addQueryData:query3]; + XCTAssertEqual([self.queryCache highestListenSequenceNumber], 100); + + [self removeQueryData:query1]; + XCTAssertEqual([self.queryCache highestListenSequenceNumber], 100); + + [self removeQueryData:query3]; + XCTAssertEqual([self.queryCache highestListenSequenceNumber], 100); + + // Verify that the highestTargetID even survives restarts. + [self.queryCache shutdown]; + self.queryCache = [self.persistence queryCache]; + [self.queryCache start]; + XCTAssertEqual([self.queryCache highestListenSequenceNumber], 100); +} + - (void)testHighestTargetID { if ([self isTestBaseClass]) return; @@ -256,6 +307,7 @@ - (void)testHighestTargetID { FSTQueryData *query1 = [[FSTQueryData alloc] initWithQuery:FSTTestQuery(@"rooms") targetID:1 + listenSequenceNumber:10 purpose:FSTQueryPurposeListen]; FSTDocumentKey *key1 = FSTTestDocKey(@"rooms/bar"); FSTDocumentKey *key2 = FSTTestDocKey(@"rooms/foo"); @@ -265,6 +317,7 @@ - (void)testHighestTargetID { FSTQueryData *query2 = [[FSTQueryData alloc] initWithQuery:FSTTestQuery(@"halls") targetID:2 + listenSequenceNumber:20 purpose:FSTQueryPurposeListen]; FSTDocumentKey *key3 = FSTTestDocKey(@"halls/foo"); [self addQueryData:query2]; @@ -278,6 +331,7 @@ - (void)testHighestTargetID { // A query with an empty result set still counts. FSTQueryData *query3 = [[FSTQueryData alloc] initWithQuery:FSTTestQuery(@"garages") targetID:42 + listenSequenceNumber:100 purpose:FSTQueryPurposeListen]; [self addQueryData:query3]; XCTAssertEqual([self.queryCache highestTargetID], 42); @@ -319,12 +373,21 @@ - (void)testLastRemoteSnapshotVersion { * Creates a new FSTQueryData object from the given parameters, synthesizing a resume token from * the snapshot version. */ +- (FSTQueryData *)queryDataWithQuery:(FSTQuery *)query { + return [self queryDataWithQuery:query + targetID:++_previousTargetID + listenSequenceNumber:++_previousSequenceNumber + version:++_previousSnapshotVersion]; +} + - (FSTQueryData *)queryDataWithQuery:(FSTQuery *)query targetID:(FSTTargetID)targetID + listenSequenceNumber:(FSTListenSequenceNumber)sequenceNumber version:(FSTTestSnapshotVersion)version { NSData *resumeToken = FSTTestResumeTokenFromSnapshotVersion(version); return [[FSTQueryData alloc] initWithQuery:query targetID:targetID + listenSequenceNumber:sequenceNumber purpose:FSTQueryPurposeListen snapshotVersion:FSTTestVersion(version) resumeToken:resumeToken]; diff --git a/Firestore/Example/Tests/Local/FSTReferenceSetTests.m b/Firestore/Example/Tests/Local/FSTReferenceSetTests.mm similarity index 100% rename from Firestore/Example/Tests/Local/FSTReferenceSetTests.m rename to Firestore/Example/Tests/Local/FSTReferenceSetTests.mm diff --git a/Firestore/Example/Tests/Local/FSTRemoteDocumentCacheTests.m b/Firestore/Example/Tests/Local/FSTRemoteDocumentCacheTests.mm similarity index 100% rename from Firestore/Example/Tests/Local/FSTRemoteDocumentCacheTests.m rename to Firestore/Example/Tests/Local/FSTRemoteDocumentCacheTests.mm diff --git a/Firestore/Example/Tests/Local/FSTRemoteDocumentChangeBufferTests.m b/Firestore/Example/Tests/Local/FSTRemoteDocumentChangeBufferTests.mm similarity index 100% rename from Firestore/Example/Tests/Local/FSTRemoteDocumentChangeBufferTests.m rename to Firestore/Example/Tests/Local/FSTRemoteDocumentChangeBufferTests.mm diff --git a/Firestore/Example/Tests/Model/FSTDatabaseIDTests.m b/Firestore/Example/Tests/Model/FSTDatabaseIDTests.m deleted file mode 100644 index cb1b19d0c46..00000000000 --- a/Firestore/Example/Tests/Model/FSTDatabaseIDTests.m +++ /dev/null @@ -1,45 +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/Source/Model/FSTDatabaseID.h" - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface FSTDatabaseIDTests : XCTestCase -@end - -@implementation FSTDatabaseIDTests - -- (void)testConstructor { - FSTDatabaseID *databaseID = [FSTDatabaseID databaseIDWithProject:@"p" database:@"d"]; - XCTAssertEqualObjects(databaseID.projectID, @"p"); - XCTAssertEqualObjects(databaseID.databaseID, @"d"); - XCTAssertFalse([databaseID isDefaultDatabase]); -} - -- (void)testDefaultDatabase { - FSTDatabaseID *databaseID = - [FSTDatabaseID databaseIDWithProject:@"p" database:kDefaultDatabaseID]; - XCTAssertEqualObjects(databaseID.projectID, @"p"); - XCTAssertEqualObjects(databaseID.databaseID, @"(default)"); - XCTAssertTrue([databaseID isDefaultDatabase]); -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/Firestore/Example/Tests/Model/FSTDocumentKeyTests.m b/Firestore/Example/Tests/Model/FSTDocumentKeyTests.mm similarity index 100% rename from Firestore/Example/Tests/Model/FSTDocumentKeyTests.m rename to Firestore/Example/Tests/Model/FSTDocumentKeyTests.mm diff --git a/Firestore/Example/Tests/Model/FSTDocumentSetTests.m b/Firestore/Example/Tests/Model/FSTDocumentSetTests.mm similarity index 93% rename from Firestore/Example/Tests/Model/FSTDocumentSetTests.m rename to Firestore/Example/Tests/Model/FSTDocumentSetTests.mm index bf6cd2183a7..fbaa5d6fd87 100644 --- a/Firestore/Example/Tests/Model/FSTDocumentSetTests.m +++ b/Firestore/Example/Tests/Model/FSTDocumentSetTests.mm @@ -79,14 +79,6 @@ - (void)testKeepsDocumentsInTheRightOrder { XCTAssertEqualObjects([[set documentEnumerator] allObjects], (@[ _doc3, _doc1, _doc2 ])); } -- (void)testPredecessorDocumentForKey { - FSTDocumentSet *set = FSTTestDocSet(_comp, @[ _doc1, _doc2, _doc3 ]); - - XCTAssertNil([set predecessorDocumentForKey:_doc3.key]); - XCTAssertEqualObjects([set predecessorDocumentForKey:_doc1.key], _doc3); - XCTAssertEqualObjects([set predecessorDocumentForKey:_doc2.key], _doc1); -} - - (void)testDeletes { FSTDocumentSet *set = FSTTestDocSet(_comp, @[ _doc1, _doc2, _doc3 ]); diff --git a/Firestore/Example/Tests/Model/FSTDocumentTests.m b/Firestore/Example/Tests/Model/FSTDocumentTests.mm similarity index 100% rename from Firestore/Example/Tests/Model/FSTDocumentTests.m rename to Firestore/Example/Tests/Model/FSTDocumentTests.mm diff --git a/Firestore/Example/Tests/Model/FSTFieldValueTests.m b/Firestore/Example/Tests/Model/FSTFieldValueTests.mm similarity index 93% rename from Firestore/Example/Tests/Model/FSTFieldValueTests.m rename to Firestore/Example/Tests/Model/FSTFieldValueTests.mm index 785fc6bac14..03dfa66573f 100644 --- a/Firestore/Example/Tests/Model/FSTFieldValueTests.m +++ b/Firestore/Example/Tests/Model/FSTFieldValueTests.mm @@ -16,19 +16,24 @@ #import "Firestore/Source/Model/FSTFieldValue.h" +#import +#import #import -#import "FirebaseFirestore/FIRGeoPoint.h" #import "Firestore/Source/API/FIRFirestore+Internal.h" #import "Firestore/Source/API/FSTUserDataConverter.h" -#import "Firestore/Source/Core/FSTTimestamp.h" -#import "Firestore/Source/Model/FSTDatabaseID.h" #import "Firestore/Source/Model/FSTFieldValue.h" #import "Firestore/Source/Model/FSTPath.h" #import "Firestore/Example/Tests/API/FSTAPIHelpers.h" #import "Firestore/Example/Tests/Util/FSTHelpers.h" +#include "Firestore/core/src/firebase/firestore/model/database_id.h" +#include "Firestore/core/src/firebase/firestore/util/string_apple.h" + +namespace util = firebase::firestore::util; +using firebase::firestore::model::DatabaseId; + /** Helper to wrap the values in a set of equality groups using FSTTestFieldValue(). */ NSArray *FSTWrapGroups(NSArray *groups) { NSMutableArray *wrapped = [NSMutableArray array]; @@ -224,7 +229,7 @@ - (void)testWrapDates { XCTAssertEqualObjects([wrapped value], value); XCTAssertEqualObjects(((FSTTimestampValue *)wrapped).internalValue, - [FSTTimestamp timestampWithDate:value]); + [FIRTimestamp timestampWithDate:value]); } } @@ -249,14 +254,15 @@ - (void)testWrapBlobs { - (void)testWrapResourceNames { NSArray *values = @[ - FSTTestRef(@"project", kDefaultDatabaseID, @"foo/bar"), - FSTTestRef(@"project", kDefaultDatabaseID, @"foo/baz") + FSTTestRef("project", DatabaseId::kDefault, @"foo/bar"), + FSTTestRef("project", DatabaseId::kDefault, @"foo/baz") ]; for (FSTDocumentKeyReference *value in values) { FSTFieldValue *wrapped = FSTTestFieldValue(value); XCTAssertEqualObjects([wrapped class], [FSTReferenceValue class]); XCTAssertEqualObjects([wrapped value], value.key); - XCTAssertEqualObjects(((FSTDatabaseID *)wrapped).databaseID, value.databaseID); + XCTAssertTrue(*((FSTReferenceValue *)wrapped).databaseID == + *(const DatabaseId *)(value.databaseID)); } } @@ -417,6 +423,7 @@ - (void)testArrays { } - (void)testValueEquality { + DatabaseId database_id = DatabaseId("project", DatabaseId::kDefault); NSArray *groups = @[ @[ FSTTestFieldValue(@YES), [FSTBooleanValue booleanValue:YES] ], @[ FSTTestFieldValue(@NO), [FSTBooleanValue booleanValue:NO] ], @@ -438,20 +445,20 @@ - (void)testValueEquality { @[ FSTTestFieldValue(@"\u00e9a") ], // latin small letter e with acute accent @[ FSTTestFieldValue(date1), - [FSTTimestampValue timestampValue:[FSTTimestamp timestampWithDate:date1]] + [FSTTimestampValue timestampValue:[FIRTimestamp timestampWithDate:date1]] ], @[ FSTTestFieldValue(date2) ], @[ // NOTE: ServerTimestampValues can't be parsed via FSTTestFieldValue(). [FSTServerTimestampValue - serverTimestampValueWithLocalWriteTime:[FSTTimestamp timestampWithDate:date1] + serverTimestampValueWithLocalWriteTime:[FIRTimestamp timestampWithDate:date1] previousValue:nil], [FSTServerTimestampValue - serverTimestampValueWithLocalWriteTime:[FSTTimestamp timestampWithDate:date1] + serverTimestampValueWithLocalWriteTime:[FIRTimestamp timestampWithDate:date1] previousValue:nil] ], @[ [FSTServerTimestampValue - serverTimestampValueWithLocalWriteTime:[FSTTimestamp timestampWithDate:date2] + serverTimestampValueWithLocalWriteTime:[FIRTimestamp timestampWithDate:date2] previousValue:nil] ], @[ FSTTestFieldValue(FSTTestGeoPoint(0, 1)), @@ -459,12 +466,10 @@ - (void)testValueEquality { ], @[ FSTTestFieldValue(FSTTestGeoPoint(1, 0)) ], @[ - [FSTReferenceValue referenceValue:FSTTestDocKey(@"coll/doc1") - databaseID:[FSTDatabaseID databaseIDWithProject:@"project" - database:kDefaultDatabaseID]], - FSTTestFieldValue(FSTTestRef(@"project", kDefaultDatabaseID, @"coll/doc1")) + [FSTReferenceValue referenceValue:FSTTestDocKey(@"coll/doc1") databaseID:&database_id], + FSTTestFieldValue(FSTTestRef("project", DatabaseId::kDefault, @"coll/doc1")) ], - @[ FSTTestRef(@"project", @"(default)", @"coll/doc2") ], + @[ FSTTestRef("project", "(default)", @"coll/doc2") ], @[ FSTTestFieldValue(@[ @"foo", @"bar" ]), FSTTestFieldValue(@[ @"foo", @"bar" ]) ], @[ FSTTestFieldValue(@[ @"foo", @"bar", @"baz" ]) ], @[ FSTTestFieldValue(@[ @"foo" ]) ], @[ @@ -525,9 +530,9 @@ - (void)testValueOrdering { @[ FSTTestData(0, 1, 2, 4, 3, -1) ], @[ FSTTestData(255, -1) ], // resource names - @[ FSTTestRef(@"p1", @"d1", @"c1/doc1") ], @[ FSTTestRef(@"p1", @"d1", @"c1/doc2") ], - @[ FSTTestRef(@"p1", @"d1", @"c10/doc1") ], @[ FSTTestRef(@"p1", @"d1", @"c2/doc1") ], - @[ FSTTestRef(@"p1", @"d2", @"c1/doc1") ], @[ FSTTestRef(@"p2", @"d1", @"c1/doc1") ], + @[ FSTTestRef("p1", "d1", @"c1/doc1") ], @[ FSTTestRef("p1", "d1", @"c1/doc2") ], + @[ FSTTestRef("p1", "d1", @"c10/doc1") ], @[ FSTTestRef("p1", "d1", @"c2/doc1") ], + @[ FSTTestRef("p1", "d2", @"c1/doc1") ], @[ FSTTestRef("p2", "d1", @"c1/doc1") ], // Geo points @[ FSTTestGeoPoint(-90, -180) ], @[ FSTTestGeoPoint(-90, 0) ], @[ FSTTestGeoPoint(-90, 180) ], @@ -568,14 +573,12 @@ - (void)testValue { { XCTAssertTrue([output[@"array"][1] isKindOfClass:[NSDate class]]); NSDate *actual = output[@"array"][1]; - XCTAssertEqualWithAccuracy(date.timeIntervalSince1970, actual.timeIntervalSince1970, - 0.000000001); + XCTAssertEqualWithAccuracy(date.timeIntervalSince1970, actual.timeIntervalSince1970, 0.000001); } { XCTAssertTrue([output[@"obj"][@"date"] isKindOfClass:[NSDate class]]); NSDate *actual = output[@"obj"][@"date"]; - XCTAssertEqualWithAccuracy(date.timeIntervalSince1970, actual.timeIntervalSince1970, - 0.000000001); + XCTAssertEqualWithAccuracy(date.timeIntervalSince1970, actual.timeIntervalSince1970, 0.000001); } } diff --git a/Firestore/Example/Tests/Model/FSTMutationTests.m b/Firestore/Example/Tests/Model/FSTMutationTests.mm similarity index 98% rename from Firestore/Example/Tests/Model/FSTMutationTests.m rename to Firestore/Example/Tests/Model/FSTMutationTests.mm index 47fa9b31e3a..1c1375cdd09 100644 --- a/Firestore/Example/Tests/Model/FSTMutationTests.m +++ b/Firestore/Example/Tests/Model/FSTMutationTests.mm @@ -16,9 +16,9 @@ #import "Firestore/Source/Model/FSTMutation.h" +#import #import -#import "Firestore/Source/Core/FSTTimestamp.h" #import "Firestore/Source/Model/FSTDocument.h" #import "Firestore/Source/Model/FSTDocumentKey.h" #import "Firestore/Source/Model/FSTFieldValue.h" @@ -30,11 +30,11 @@ @interface FSTMutationTests : XCTestCase @end @implementation FSTMutationTests { - FSTTimestamp *_timestamp; + FIRTimestamp *_timestamp; } - (void)setUp { - _timestamp = [FSTTimestamp timestamp]; + _timestamp = [FIRTimestamp timestamp]; } - (void)testAppliesSetsToDocuments { diff --git a/Firestore/Example/Tests/Model/FSTPathTests.m b/Firestore/Example/Tests/Model/FSTPathTests.mm similarity index 97% rename from Firestore/Example/Tests/Model/FSTPathTests.m rename to Firestore/Example/Tests/Model/FSTPathTests.mm index b8529e59691..68bcc44c0b1 100644 --- a/Firestore/Example/Tests/Model/FSTPathTests.m +++ b/Firestore/Example/Tests/Model/FSTPathTests.mm @@ -173,6 +173,10 @@ - (void)testCanonicalString { ASSERT_ROUND_TRIP(@"`.foo\\\\`", 1); ASSERT_ROUND_TRIP(@"`.foo\\\\`.`.foo`", 2); ASSERT_ROUND_TRIP(@"foo.`\\``.bar", 3); + + FSTFieldPath *path = [FSTFieldPath pathWithServerFormat:@"foo\\.bar"]; + XCTAssertEqualObjects([path canonicalString], @"`foo.bar`"); + XCTAssertEqual(path.length, 1); } #undef ASSERT_ROUND_TRIP diff --git a/Firestore/Example/Tests/Remote/FSTDatastoreTests.m b/Firestore/Example/Tests/Remote/FSTDatastoreTests.mm similarity index 97% rename from Firestore/Example/Tests/Remote/FSTDatastoreTests.m rename to Firestore/Example/Tests/Remote/FSTDatastoreTests.mm index f3cc56f2d06..6d6e9122cbd 100644 --- a/Firestore/Example/Tests/Remote/FSTDatastoreTests.m +++ b/Firestore/Example/Tests/Remote/FSTDatastoreTests.mm @@ -14,9 +14,9 @@ * limitations under the License. */ -#import "FirebaseFirestore/FIRFirestoreErrors.h" #import "Firestore/Source/Remote/FSTDatastore.h" +#import #import #import diff --git a/Firestore/Example/Tests/Remote/FSTRemoteEventTests.m b/Firestore/Example/Tests/Remote/FSTRemoteEventTests.mm similarity index 100% rename from Firestore/Example/Tests/Remote/FSTRemoteEventTests.m rename to Firestore/Example/Tests/Remote/FSTRemoteEventTests.mm diff --git a/Firestore/Example/Tests/Remote/FSTSerializerBetaTests.m b/Firestore/Example/Tests/Remote/FSTSerializerBetaTests.mm similarity index 95% rename from Firestore/Example/Tests/Remote/FSTSerializerBetaTests.m rename to Firestore/Example/Tests/Remote/FSTSerializerBetaTests.mm index 61847b0e197..fc4060b7308 100644 --- a/Firestore/Example/Tests/Remote/FSTSerializerBetaTests.m +++ b/Firestore/Example/Tests/Remote/FSTSerializerBetaTests.mm @@ -16,12 +16,13 @@ #import "Firestore/Source/Remote/FSTSerializerBeta.h" +#import +#import +#import +#import #import #import -#import "FirebaseFirestore/FIRFieldPath.h" -#import "FirebaseFirestore/FIRFirestoreErrors.h" -#import "FirebaseFirestore/FIRGeoPoint.h" #import "Firestore/Protos/objc/firestore/local/MaybeDocument.pbobjc.h" #import "Firestore/Protos/objc/firestore/local/Mutation.pbobjc.h" #import "Firestore/Protos/objc/google/firestore/v1beta1/Common.pbobjc.h" @@ -33,9 +34,7 @@ #import "Firestore/Protos/objc/google/type/Latlng.pbobjc.h" #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Core/FSTSnapshotVersion.h" -#import "Firestore/Source/Core/FSTTimestamp.h" #import "Firestore/Source/Local/FSTQueryData.h" -#import "Firestore/Source/Model/FSTDatabaseID.h" #import "Firestore/Source/Model/FSTDocument.h" #import "Firestore/Source/Model/FSTDocumentKey.h" #import "Firestore/Source/Model/FSTFieldValue.h" @@ -47,6 +46,12 @@ #import "Firestore/Example/Tests/API/FSTAPIHelpers.h" #import "Firestore/Example/Tests/Util/FSTHelpers.h" +#include "Firestore/core/src/firebase/firestore/model/database_id.h" +#include "Firestore/core/src/firebase/firestore/util/string_apple.h" + +namespace util = firebase::firestore::util; +using firebase::firestore::model::DatabaseId; + NS_ASSUME_NONNULL_BEGIN @interface FSTSerializerBeta (Test) @@ -79,15 +84,18 @@ + (instancetype)messageWithProperty:(NSString *)property ascending:(BOOL)ascendi } @end -@interface FSTSerializerBetaTests : XCTestCase +@interface FSTSerializerBetaTests : XCTestCase { + DatabaseId _databaseId; +} + @property(nonatomic, strong) FSTSerializerBeta *serializer; @end @implementation FSTSerializerBetaTests - (void)setUp { - FSTDatabaseID *databaseID = [FSTDatabaseID databaseIDWithProject:@"p" database:@"d"]; - self.serializer = [[FSTSerializerBeta alloc] initWithDatabaseID:databaseID]; + _databaseId = DatabaseId("p", "d"); + self.serializer = [[FSTSerializerBeta alloc] initWithDatabaseID:&_databaseId]; } - (void)testEncodesNull { @@ -231,7 +239,8 @@ - (void)testEncodesBlobs { } - (void)testEncodesResourceNames { - FSTDocumentKeyReference *reference = FSTTestRef(@"project", kDefaultDatabaseID, @"foo/bar"); + FSTDocumentKeyReference *reference = FSTTestRef("project", DatabaseId::kDefault, @"foo/bar"); + _databaseId = DatabaseId("project", DatabaseId::kDefault); GCFSValue *proto = [GCFSValue message]; proto.referenceValue = @"projects/project/databases/(default)/documents/foo/bar"; @@ -370,7 +379,7 @@ - (void)testEncodesSetMutationWithPrecondition { GCFSWrite *proto = [GCFSWrite message]; proto.update = [self.serializer encodedDocumentWithFields:mutation.value key:mutation.key]; proto.currentDocument.updateTime = - [self.serializer encodedTimestamp:[[FSTTimestamp alloc] initWithSeconds:0 nanos:4000]]; + [self.serializer encodedTimestamp:[[FIRTimestamp alloc] initWithSeconds:0 nanoseconds:4000]]; [self assertRoundTripForMutation:mutation proto:proto]; } @@ -396,20 +405,25 @@ - (void)testRoundTripSpecialFieldNames { - (void)testEncodesListenRequestLabels { FSTQuery *query = FSTTestQuery(@"collection/key"); - FSTQueryData *queryData = - [[FSTQueryData alloc] initWithQuery:query targetID:2 purpose:FSTQueryPurposeListen]; + FSTQueryData *queryData = [[FSTQueryData alloc] initWithQuery:query + targetID:2 + listenSequenceNumber:3 + purpose:FSTQueryPurposeListen]; NSDictionary *result = [self.serializer encodedListenRequestLabelsForQueryData:queryData]; XCTAssertNil(result); - queryData = - [[FSTQueryData alloc] initWithQuery:query targetID:2 purpose:FSTQueryPurposeLimboResolution]; + queryData = [[FSTQueryData alloc] initWithQuery:query + targetID:2 + listenSequenceNumber:3 + purpose:FSTQueryPurposeLimboResolution]; result = [self.serializer encodedListenRequestLabelsForQueryData:queryData]; XCTAssertEqualObjects(result, @{@"goog-listen-tags" : @"limbo-document"}); queryData = [[FSTQueryData alloc] initWithQuery:query targetID:2 + listenSequenceNumber:3 purpose:FSTQueryPurposeExistenceFilterMismatch]; result = [self.serializer encodedListenRequestLabelsForQueryData:queryData]; XCTAssertEqualObjects(result, @{@"goog-listen-tags" : @"existence-filter-mismatch"}); @@ -545,8 +559,7 @@ - (void)testEncodesNanFilter { } - (void)unaryFilterTestWithValue:(id)value - expectedUnaryOperator:(GCFSStructuredQuery_UnaryFilter_Operator) - operator{ + expectedUnaryOperator:(GCFSStructuredQuery_UnaryFilter_Operator)op { FSTQuery *q = [FSTTestQuery(@"docs") queryByAddingFilter:FSTTestFilter(@"prop", @"==", value)]; FSTQueryData *model = [self queryDataForQuery:q]; @@ -560,7 +573,7 @@ - (void)unaryFilterTestWithValue:(id)value GCFSStructuredQuery_UnaryFilter *filter = expected.query.structuredQuery.where.unaryFilter; filter.field.fieldPath = @"prop"; - filter.op = operator; + filter.op = op; expected.targetId = 1; [self assertRoundTripForQueryData:model proto:expected]; @@ -627,6 +640,7 @@ - (void)testEncodesResumeTokens { FSTQuery *q = FSTTestQuery(@"docs"); FSTQueryData *model = [[FSTQueryData alloc] initWithQuery:q targetID:1 + listenSequenceNumber:0 purpose:FSTQueryPurposeListen snapshotVersion:[FSTSnapshotVersion noVersion] resumeToken:FSTTestData(1, 2, 3, -1)]; @@ -647,6 +661,7 @@ - (void)testEncodesResumeTokens { - (FSTQueryData *)queryDataForQuery:(FSTQuery *)query { return [[FSTQueryData alloc] initWithQuery:query targetID:1 + listenSequenceNumber:0 purpose:FSTQueryPurposeListen snapshotVersion:[FSTSnapshotVersion noVersion] resumeToken:[NSData data]]; diff --git a/Firestore/Example/Tests/Remote/FSTWatchChange+Testing.m b/Firestore/Example/Tests/Remote/FSTWatchChange+Testing.mm similarity index 100% rename from Firestore/Example/Tests/Remote/FSTWatchChange+Testing.m rename to Firestore/Example/Tests/Remote/FSTWatchChange+Testing.mm diff --git a/Firestore/Example/Tests/Remote/FSTWatchChangeTests.m b/Firestore/Example/Tests/Remote/FSTWatchChangeTests.mm similarity index 100% rename from Firestore/Example/Tests/Remote/FSTWatchChangeTests.m rename to Firestore/Example/Tests/Remote/FSTWatchChangeTests.mm diff --git a/Firestore/Example/Tests/SpecTests/FSTLevelDBSpecTests.m b/Firestore/Example/Tests/SpecTests/FSTLevelDBSpecTests.mm similarity index 100% rename from Firestore/Example/Tests/SpecTests/FSTLevelDBSpecTests.m rename to Firestore/Example/Tests/SpecTests/FSTLevelDBSpecTests.mm diff --git a/Firestore/Example/Tests/SpecTests/FSTMemorySpecTests.m b/Firestore/Example/Tests/SpecTests/FSTMemorySpecTests.mm similarity index 100% rename from Firestore/Example/Tests/SpecTests/FSTMemorySpecTests.m rename to Firestore/Example/Tests/SpecTests/FSTMemorySpecTests.mm diff --git a/Firestore/Example/Tests/SpecTests/FSTMockDatastore.h b/Firestore/Example/Tests/SpecTests/FSTMockDatastore.h index 5420c2e635b..e1ea2fb3332 100644 --- a/Firestore/Example/Tests/SpecTests/FSTMockDatastore.h +++ b/Firestore/Example/Tests/SpecTests/FSTMockDatastore.h @@ -34,8 +34,6 @@ NS_ASSUME_NONNULL_BEGIN */ @property(nonatomic) int writeStreamRequestCount; -+ (instancetype)mockDatastoreWithWorkerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue; - #pragma mark - Watch Stream manipulation. /** Injects an Added WatchChange containing the given targetIDs. */ diff --git a/Firestore/Example/Tests/SpecTests/FSTMockDatastore.m b/Firestore/Example/Tests/SpecTests/FSTMockDatastore.mm similarity index 83% rename from Firestore/Example/Tests/SpecTests/FSTMockDatastore.m rename to Firestore/Example/Tests/SpecTests/FSTMockDatastore.mm index 9a1d719ef4e..6715b24e8a3 100644 --- a/Firestore/Example/Tests/SpecTests/FSTMockDatastore.m +++ b/Firestore/Example/Tests/SpecTests/FSTMockDatastore.mm @@ -16,11 +16,8 @@ #import "Firestore/Example/Tests/SpecTests/FSTMockDatastore.h" -#import "Firestore/Source/Auth/FSTEmptyCredentialsProvider.h" -#import "Firestore/Source/Core/FSTDatabaseInfo.h" #import "Firestore/Source/Core/FSTSnapshotVersion.h" #import "Firestore/Source/Local/FSTQueryData.h" -#import "Firestore/Source/Model/FSTDatabaseID.h" #import "Firestore/Source/Model/FSTMutation.h" #import "Firestore/Source/Remote/FSTSerializerBeta.h" #import "Firestore/Source/Remote/FSTStream.h" @@ -29,6 +26,17 @@ #import "Firestore/Example/Tests/Remote/FSTWatchChange+Testing.h" +#include "Firestore/core/src/firebase/firestore/auth/credentials_provider.h" +#include "Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.h" +#include "Firestore/core/src/firebase/firestore/core/database_info.h" +#include "Firestore/core/src/firebase/firestore/model/database_id.h" +#include "Firestore/core/src/firebase/firestore/util/string_apple.h" + +using firebase::firestore::auth::CredentialsProvider; +using firebase::firestore::auth::EmptyCredentialsProvider; +using firebase::firestore::core::DatabaseInfo; +using firebase::firestore::model::DatabaseId; + @class GRPCProtoCall; NS_ASSUME_NONNULL_BEGIN @@ -39,17 +47,17 @@ @interface FSTMockWatchStream : FSTWatchStream - (instancetype)initWithDatastore:(FSTMockDatastore *)datastore workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue - credentials:(id)credentials + credentials:(CredentialsProvider *)credentials serializer:(FSTSerializerBeta *)serializer NS_DESIGNATED_INITIALIZER; -- (instancetype)initWithDatabase:(FSTDatabaseInfo *)database +- (instancetype)initWithDatabase:(const DatabaseInfo *)database workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue - credentials:(id)credentials + credentials:(CredentialsProvider *)credentials serializer:(FSTSerializerBeta *)serializer NS_UNAVAILABLE; -- (instancetype)initWithDatabase:(FSTDatabaseInfo *)database +- (instancetype)initWithDatabase:(const DatabaseInfo *)database workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue - credentials:(id)credentials + credentials:(CredentialsProvider *)credentials responseMessageClass:(Class)responseMessageClass NS_UNAVAILABLE; @property(nonatomic, assign) BOOL open; @@ -64,7 +72,7 @@ @implementation FSTMockWatchStream - (instancetype)initWithDatastore:(FSTMockDatastore *)datastore workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue - credentials:(id)credentials + credentials:(CredentialsProvider *)credentials serializer:(FSTSerializerBeta *)serializer { self = [super initWithDatabase:datastore.databaseInfo workerDispatchQueue:workerDispatchQueue @@ -165,17 +173,17 @@ @interface FSTMockWriteStream : FSTWriteStream - (instancetype)initWithDatastore:(FSTMockDatastore *)datastore workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue - credentials:(id)credentials + credentials:(CredentialsProvider *)credentials serializer:(FSTSerializerBeta *)serializer NS_DESIGNATED_INITIALIZER; -- (instancetype)initWithDatabase:(FSTDatabaseInfo *)database +- (instancetype)initWithDatabase:(const DatabaseInfo *)database workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue - credentials:(id)credentials + credentials:(CredentialsProvider *)credentials serializer:(FSTSerializerBeta *)serializer NS_UNAVAILABLE; -- (instancetype)initWithDatabase:(FSTDatabaseInfo *)database +- (instancetype)initWithDatabase:(const DatabaseInfo *)database workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue - credentials:(id)credentials + credentials:(CredentialsProvider *)credentials responseMessageClass:(Class)responseMessageClass NS_UNAVAILABLE; @property(nonatomic, strong, readonly) FSTMockDatastore *datastore; @@ -188,7 +196,7 @@ @implementation FSTMockWriteStream - (instancetype)initWithDatastore:(FSTMockDatastore *)datastore workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue - credentials:(id)credentials + credentials:(CredentialsProvider *)credentials serializer:(FSTSerializerBeta *)serializer { self = [super initWithDatabase:datastore.databaseInfo workerDispatchQueue:workerDispatchQueue @@ -274,47 +282,33 @@ @interface FSTMockDatastore () /** Properties implemented in FSTDatastore that are nonpublic. */ @property(nonatomic, strong, readonly) FSTDispatchQueue *workerDispatchQueue; -@property(nonatomic, strong, readonly) id credentials; +@property(nonatomic, assign, readonly) CredentialsProvider *credentials; @end @implementation FSTMockDatastore -+ (instancetype)mockDatastoreWithWorkerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue { - FSTDatabaseID *databaseID = [FSTDatabaseID databaseIDWithProject:@"project" database:@"database"]; - FSTDatabaseInfo *databaseInfo = [FSTDatabaseInfo databaseInfoWithDatabaseID:databaseID - persistenceKey:@"persistence" - host:@"host" - sslEnabled:NO]; - - FSTEmptyCredentialsProvider *credentials = [[FSTEmptyCredentialsProvider alloc] init]; - - return [[FSTMockDatastore alloc] initWithDatabaseInfo:databaseInfo - workerDispatchQueue:workerDispatchQueue - credentials:credentials]; -} - #pragma mark - Overridden FSTDatastore methods. - (FSTWatchStream *)createWatchStream { - FSTAssert(self.databaseInfo, @"DatabaseInfo must not be nil"); + // FSTAssert(self.databaseInfo, @"DatabaseInfo must not be nil"); self.watchStream = [[FSTMockWatchStream alloc] initWithDatastore:self workerDispatchQueue:self.workerDispatchQueue credentials:self.credentials serializer:[[FSTSerializerBeta alloc] - initWithDatabaseID:self.databaseInfo.databaseID]]; + initWithDatabaseID:&self.databaseInfo->database_id()]]; return self.watchStream; } - (FSTWriteStream *)createWriteStream { - FSTAssert(self.databaseInfo, @"DatabaseInfo must not be nil"); + // FSTAssert(self.databaseInfo, @"DatabaseInfo must not be nil"); self.writeStream = [[FSTMockWriteStream alloc] initWithDatastore:self workerDispatchQueue:self.workerDispatchQueue credentials:self.credentials serializer:[[FSTSerializerBeta alloc] - initWithDatabaseID:self.databaseInfo.databaseID]]; + initWithDatabaseID:&self.databaseInfo->database_id()]]; return self.writeStream; } diff --git a/Firestore/Example/Tests/SpecTests/FSTSpecTests.m b/Firestore/Example/Tests/SpecTests/FSTSpecTests.mm similarity index 98% rename from Firestore/Example/Tests/SpecTests/FSTSpecTests.m rename to Firestore/Example/Tests/SpecTests/FSTSpecTests.mm index 3abcb4860a9..43b2a5f1e5c 100644 --- a/Firestore/Example/Tests/SpecTests/FSTSpecTests.m +++ b/Firestore/Example/Tests/SpecTests/FSTSpecTests.mm @@ -16,10 +16,9 @@ #import "Firestore/Example/Tests/SpecTests/FSTSpecTests.h" +#import #import -#import "FirebaseFirestore/FIRFirestoreErrors.h" -#import "Firestore/Source/Auth/FSTUser.h" #import "Firestore/Source/Core/FSTEventManager.h" #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Core/FSTSnapshotVersion.h" @@ -43,6 +42,12 @@ #import "Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.h" #import "Firestore/Example/Tests/Util/FSTHelpers.h" +#include "Firestore/core/src/firebase/firestore/auth/user.h" +#include "Firestore/core/src/firebase/firestore/util/string_apple.h" + +namespace util = firebase::firestore::util; +using firebase::firestore::auth::User; + NS_ASSUME_NONNULL_BEGIN // Disables all other tests; useful for debugging. Multiple tests can have this tag and they'll all @@ -327,15 +332,17 @@ - (void)doEnableNetwork { } - (void)doChangeUser:(id)UID { - FSTUser *user = [UID isEqual:[NSNull null]] ? [FSTUser unauthenticatedUser] - : [[FSTUser alloc] initWithUID:UID]; - [self.driver changeUser:user]; + if ([UID isEqual:[NSNull null]]) { + UID = nil; + } + [self.driver changeUser:User::FromUid(UID)]; } - (void)doRestart { // Any outstanding user writes should be automatically re-sent, so we want to preserve them // when re-creating the driver. - FSTOutstandingWriteQueues *outstandingWrites = self.driver.outstandingWrites; + FSTOutstandingWriteQueues outstandingWrites = self.driver.outstandingWrites; + User currentUser = self.driver.currentUser; [self.driver shutdown]; @@ -347,7 +354,7 @@ - (void)doRestart { self.driver = [[FSTSyncEngineTestDriver alloc] initWithPersistence:self.driverPersistence garbageCollector:self.garbageCollector - initialUser:self.driver.currentUser + initialUser:currentUser outstandingWrites:outstandingWrites]; [self.driver start]; } @@ -503,6 +510,7 @@ - (void)validateStateExpectations:(nullable NSDictionary *)expected { expectedActiveTargets[@(targetID)] = [[FSTQueryData alloc] initWithQuery:query targetID:targetID + listenSequenceNumber:0 purpose:FSTQueryPurposeListen snapshotVersion:[FSTSnapshotVersion noVersion] resumeToken:resumeToken]; diff --git a/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.h b/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.h index 7cb27262027..466a34710b0 100644 --- a/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.h +++ b/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.h @@ -14,10 +14,14 @@ * limitations under the License. */ -#import #import +#include + #import "Firestore/Source/Core/FSTTypes.h" +#import "Firestore/Source/Remote/FSTRemoteStore.h" + +#include "Firestore/core/src/firebase/firestore/auth/user.h" @class FSTDocumentKey; @class FSTMutation; @@ -25,7 +29,6 @@ @class FSTQuery; @class FSTQueryData; @class FSTSnapshotVersion; -@class FSTUser; @class FSTViewSnapshot; @class FSTWatchChange; @protocol FSTGarbageCollector; @@ -54,7 +57,10 @@ NS_ASSUME_NONNULL_BEGIN @end /** Mapping of user => array of FSTMutations for that user. */ -typedef NSDictionary *> FSTOutstandingWriteQueues; +typedef std::unordered_map *, + firebase::firestore::auth::HashUser> + FSTOutstandingWriteQueues; /** * A test driver for FSTSyncEngine that allows simulated event delivery and capture. As much as @@ -93,8 +99,8 @@ typedef NSDictionary *> FSTOutstanding */ - (instancetype)initWithPersistence:(id)persistence garbageCollector:(id)garbageCollector - initialUser:(FSTUser *)initialUser - outstandingWrites:(FSTOutstandingWriteQueues *)outstandingWrites + initialUser:(const firebase::firestore::auth::User &)initialUser + outstandingWrites:(const FSTOutstandingWriteQueues &)outstandingWrites NS_DESIGNATED_INITIALIZER; - (instancetype)init NS_UNAVAILABLE; @@ -222,7 +228,7 @@ typedef NSDictionary *> FSTOutstanding * each user, so future receiveWriteAck/Error operations will validate the write sent to the mock * datastore matches the next outstanding write for that user. */ -- (void)changeUser:(FSTUser *)user; +- (void)changeUser:(const firebase::firestore::auth::User &)user; /** * Returns all query events generated by the FSTSyncEngine in response to the event injection @@ -246,10 +252,10 @@ typedef NSDictionary *> FSTOutstanding * sentWritesCount, but not necessarily, since the FSTRemoteStore limits the number of * outstanding writes to the backend at a given time. */ -@property(nonatomic, strong, readonly) FSTOutstandingWriteQueues *outstandingWrites; +@property(nonatomic, assign, readonly) const FSTOutstandingWriteQueues &outstandingWrites; /** The current user for the FSTSyncEngine; determines which mutation queue is active. */ -@property(nonatomic, strong, readonly) FSTUser *currentUser; +@property(nonatomic, assign, readonly) const firebase::firestore::auth::User ¤tUser; /** The current set of documents in limbo. */ @property(nonatomic, strong, readonly) diff --git a/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.m b/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.mm similarity index 85% rename from Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.m rename to Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.mm index da6393321a3..07dc84f8d6b 100644 --- a/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.m +++ b/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.mm @@ -16,10 +16,11 @@ #import "Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.h" +#include + +#import #import -#import "FirebaseFirestore/FIRFirestoreErrors.h" -#import "Firestore/Source/Auth/FSTUser.h" #import "Firestore/Source/Core/FSTEventManager.h" #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Core/FSTSnapshotVersion.h" @@ -36,6 +37,17 @@ #import "Firestore/Example/Tests/Core/FSTSyncEngine+Testing.h" #import "Firestore/Example/Tests/SpecTests/FSTMockDatastore.h" +#include "Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.h" +#include "Firestore/core/src/firebase/firestore/auth/user.h" +#include "Firestore/core/src/firebase/firestore/core/database_info.h" +#include "Firestore/core/src/firebase/firestore/model/database_id.h" + +using firebase::firestore::auth::EmptyCredentialsProvider; +using firebase::firestore::auth::HashUser; +using firebase::firestore::auth::User; +using firebase::firestore::core::DatabaseInfo; +using firebase::firestore::model::DatabaseId; + NS_ASSUME_NONNULL_BEGIN @implementation FSTQueryEvent @@ -71,45 +83,48 @@ @interface FSTSyncEngineTestDriver () @property(nonatomic, strong, readonly) NSMutableDictionary *queryListeners; -#pragma mark - Other data structures. -@property(nonatomic, strong, readwrite) FSTUser *currentUser; - @end @implementation FSTSyncEngineTestDriver { // ivar is declared as mutable. - NSMutableDictionary *> *_outstandingWrites; + std::unordered_map *, HashUser> _outstandingWrites; + + DatabaseInfo _databaseInfo; + User _currentUser; + EmptyCredentialsProvider _credentialProvider; } - (instancetype)initWithPersistence:(id)persistence garbageCollector:(id)garbageCollector { return [self initWithPersistence:persistence garbageCollector:garbageCollector - initialUser:[FSTUser unauthenticatedUser] - outstandingWrites:@{}]; + initialUser:User::Unauthenticated() + outstandingWrites:{}]; } - (instancetype)initWithPersistence:(id)persistence garbageCollector:(id)garbageCollector - initialUser:(FSTUser *)initialUser - outstandingWrites:(FSTOutstandingWriteQueues *)outstandingWrites { + initialUser:(const User &)initialUser + outstandingWrites:(const FSTOutstandingWriteQueues &)outstandingWrites { if (self = [super init]) { - // Create mutable copy of outstandingWrites. - _outstandingWrites = [NSMutableDictionary dictionary]; - [outstandingWrites enumerateKeysAndObjectsUsingBlock:^( - FSTUser *user, NSArray *writes, BOOL *stop) { - _outstandingWrites[user] = [writes mutableCopy]; - }]; + // Do a deep copy. + for (const auto &pair : outstandingWrites) { + _outstandingWrites[pair.first] = [pair.second mutableCopy]; + } _events = [NSMutableArray array]; + _databaseInfo = {DatabaseId{"project", "database"}, "persistence", "host", false}; + // Set up the sync engine and various stores. dispatch_queue_t mainQueue = dispatch_get_main_queue(); FSTDispatchQueue *dispatchQueue = [FSTDispatchQueue queueWith:mainQueue]; _localStore = [[FSTLocalStore alloc] initWithPersistence:persistence garbageCollector:garbageCollector initialUser:initialUser]; - _datastore = [FSTMockDatastore mockDatastoreWithWorkerDispatchQueue:dispatchQueue]; + _datastore = [[FSTMockDatastore alloc] initWithDatabaseInfo:&_databaseInfo + workerDispatchQueue:dispatchQueue + credentials:&_credentialProvider]; _remoteStore = [FSTRemoteStore remoteStoreWithLocalStore:_localStore datastore:_datastore]; @@ -139,6 +154,14 @@ - (instancetype)initWithPersistence:(id)persistence return self; } +- (const FSTOutstandingWriteQueues &)outstandingWrites { + return _outstandingWrites; +} + +- (const User &)currentUser { + return _currentUser; +} + - (void)applyChangedOnlineState:(FSTOnlineState)onlineState { [self.syncEngine applyChangedOnlineState:onlineState]; [self.eventManager applyChangedOnlineState:onlineState]; @@ -195,8 +218,8 @@ - (void)enableNetwork { [self.remoteStore enableNetwork]; } -- (void)changeUser:(FSTUser *)user { - self.currentUser = user; +- (void)changeUser:(const User &)user { + _currentUser = user; [self.syncEngine userDidChange:user]; } @@ -304,10 +327,10 @@ - (void)receiveWatchStreamError:(int)errorCode userInfo:(NSDictionary *)currentOutstandingWrites { - NSMutableArray *writes = _outstandingWrites[self.currentUser]; + NSMutableArray *writes = _outstandingWrites[_currentUser]; if (!writes) { writes = [NSMutableArray array]; - _outstandingWrites[self.currentUser] = writes; + _outstandingWrites[_currentUser] = writes; } return writes; } diff --git a/Firestore/Example/Tests/SpecTests/json/offline_spec_test.json b/Firestore/Example/Tests/SpecTests/json/offline_spec_test.json index 3981cec728f..6cbbc80ee29 100644 --- a/Firestore/Example/Tests/SpecTests/json/offline_spec_test.json +++ b/Firestore/Example/Tests/SpecTests/json/offline_spec_test.json @@ -459,5 +459,259 @@ ] } ] + }, + "Queries with limbo documents handle going offline.": { + "describeName": "Offline:", + "itName": "Queries with limbo documents handle going offline.", + "tags": [], + "config": { + "useGarbageCollection": true + }, + "steps": [ + { + "userListen": [ + 2, + { + "path": "collection", + "filters": [], + "orderBys": [] + } + ], + "stateExpect": { + "activeTargets": { + "2": { + "query": { + "path": "collection", + "filters": [], + "orderBys": [] + }, + "resumeToken": "" + } + } + } + }, + { + "watchAck": [ + 2 + ] + }, + { + "watchEntity": { + "docs": [ + [ + "collection/a", + 1000, + { + "key": "a" + } + ] + ], + "targets": [ + 2 + ] + } + }, + { + "watchCurrent": [ + [ + 2 + ], + "resume-token-1000" + ], + "watchSnapshot": 1000, + "expect": [ + { + "query": { + "path": "collection", + "filters": [], + "orderBys": [] + }, + "added": [ + [ + "collection/a", + 1000, + { + "key": "a" + } + ] + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false + } + ] + }, + { + "watchReset": [ + 2 + ] + }, + { + "watchCurrent": [ + [ + 2 + ], + "resume-token-1001" + ], + "watchSnapshot": 1001, + "stateExpect": { + "limboDocs": [ + "collection/a" + ], + "activeTargets": { + "1": { + "query": { + "path": "collection/a", + "filters": [], + "orderBys": [] + }, + "resumeToken": "" + }, + "2": { + "query": { + "path": "collection", + "filters": [], + "orderBys": [] + }, + "resumeToken": "" + } + } + }, + "expect": [ + { + "query": { + "path": "collection", + "filters": [], + "orderBys": [] + }, + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false + } + ] + }, + { + "watchStreamClose": { + "error": { + "code": 14, + "message": "Simulated Backend Error" + } + }, + "stateExpect": { + "activeTargets": { + "1": { + "query": { + "path": "collection/a", + "filters": [], + "orderBys": [] + }, + "resumeToken": "" + }, + "2": { + "query": { + "path": "collection", + "filters": [], + "orderBys": [] + }, + "resumeToken": "resume-token-1001" + } + } + } + }, + { + "watchStreamClose": { + "error": { + "code": 14, + "message": "Simulated Backend Error" + } + } + }, + { + "watchStreamClose": { + "error": { + "code": 14, + "message": "Simulated Backend Error" + } + } + }, + { + "watchAck": [ + 2 + ] + }, + { + "watchEntity": { + "docs": [], + "targets": [ + 2 + ] + } + }, + { + "watchCurrent": [ + [ + 2 + ], + "resume-token-1001" + ], + "watchSnapshot": 1001 + }, + { + "watchAck": [ + 1 + ] + }, + { + "watchEntity": { + "docs": [], + "targets": [ + 1 + ] + } + }, + { + "watchCurrent": [ + [ + 1 + ], + "resume-token-1001" + ], + "watchSnapshot": 1001, + "expect": [ + { + "query": { + "path": "collection", + "filters": [], + "orderBys": [] + }, + "removed": [ + [ + "collection/a", + 1000, + { + "key": "a" + } + ] + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false + } + ], + "stateExpect": { + "limboDocs": [], + "activeTargets": { + "2": { + "query": { + "path": "collection", + "filters": [], + "orderBys": [] + }, + "resumeToken": "resume-token-1001" + } + } + } + } + ] } } diff --git a/Firestore/Example/Tests/Util/FSTAssertTests.m b/Firestore/Example/Tests/Util/FSTAssertTests.mm similarity index 100% rename from Firestore/Example/Tests/Util/FSTAssertTests.m rename to Firestore/Example/Tests/Util/FSTAssertTests.mm diff --git a/Firestore/Example/Tests/Util/FSTComparisonTests.m b/Firestore/Example/Tests/Util/FSTComparisonTests.m deleted file mode 100644 index 5632e643b0b..00000000000 --- a/Firestore/Example/Tests/Util/FSTComparisonTests.m +++ /dev/null @@ -1,143 +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/Source/Util/FSTComparison.h" - -#import - -union DoubleBits { - double d; - uint64_t bits; -}; - -#define ASSERT_BIT_EQUALS(expected, actual) \ - do { \ - union DoubleBits expectedBits = {.d = expected}; \ - union DoubleBits actualBits = {.d = expected}; \ - if (expectedBits.bits != actualBits.bits) { \ - XCTFail(@"Expected <%f> to compare equal to <%f> with bits <%llX> equal to <%llX>", actual, \ - expected, actualBits.bits, expectedBits.bits); \ - } \ - } while (0); - -#define ASSERT_ORDERED_SAME(doubleValue, longValue) \ - do { \ - NSComparisonResult result = FSTCompareMixed(doubleValue, longValue); \ - if (result != NSOrderedSame) { \ - XCTFail(@"Expected <%f> to compare equal to <%lld>", doubleValue, longValue); \ - } \ - } while (0); - -#define ASSERT_ORDERED_DESCENDING(doubleValue, longValue) \ - do { \ - NSComparisonResult result = FSTCompareMixed(doubleValue, longValue); \ - if (result != NSOrderedDescending) { \ - XCTFail(@"Expected <%f> to compare equal to <%lld>", doubleValue, longValue); \ - } \ - } while (0); - -#define ASSERT_ORDERED_ASCENDING(doubleValue, longValue) \ - do { \ - NSComparisonResult result = FSTCompareMixed(doubleValue, longValue); \ - if (result != NSOrderedAscending) { \ - XCTFail(@"Expected <%f> to compare equal to <%lld>", doubleValue, longValue); \ - } \ - } while (0); - -@interface FSTComparisonTests : XCTestCase -@end - -@implementation FSTComparisonTests - -- (void)testMixedComparison { - // Infinities - ASSERT_ORDERED_ASCENDING(-INFINITY, LLONG_MIN); - ASSERT_ORDERED_ASCENDING(-INFINITY, LLONG_MAX); - ASSERT_ORDERED_ASCENDING(-INFINITY, 0LL); - - ASSERT_ORDERED_DESCENDING(INFINITY, LLONG_MIN); - ASSERT_ORDERED_DESCENDING(INFINITY, LLONG_MAX); - ASSERT_ORDERED_DESCENDING(INFINITY, 0LL); - - // NaN - ASSERT_ORDERED_ASCENDING(NAN, LLONG_MIN); - ASSERT_ORDERED_ASCENDING(NAN, LLONG_MAX); - ASSERT_ORDERED_ASCENDING(NAN, 0LL); - - // Large values (note DBL_MIN is positive and near zero). - ASSERT_ORDERED_ASCENDING(-DBL_MAX, LLONG_MIN); - - // Tests around LLONG_MIN - ASSERT_BIT_EQUALS((double)LLONG_MIN, -0x1.0p63); - ASSERT_ORDERED_SAME(-0x1.0p63, LLONG_MIN); - ASSERT_ORDERED_ASCENDING(-0x1.0p63, LLONG_MIN + 1); - - XCTAssertLessThan(-0x1.0000000000001p63, -0x1.0p63); - ASSERT_ORDERED_ASCENDING(-0x1.0000000000001p63, LLONG_MIN); - ASSERT_ORDERED_DESCENDING(-0x1.FFFFFFFFFFFFFp62, LLONG_MIN); - - // Tests around LLONG_MAX - // Note LLONG_MAX cannot be exactly represented by a double, so the system rounds it to the - // nearest double, which is 2^63. This number, in turn is larger than the maximum representable - // as a long. - ASSERT_BIT_EQUALS(0x1.0p63, (double)LLONG_MAX); - ASSERT_ORDERED_DESCENDING(0x1.0p63, LLONG_MAX); - - // The largest value with an exactly long representation - XCTAssertEqual((long)0x1.FFFFFFFFFFFFFp62, 0x7FFFFFFFFFFFFC00LL); - ASSERT_ORDERED_SAME(0x1.FFFFFFFFFFFFFp62, 0x7FFFFFFFFFFFFC00LL); - - ASSERT_ORDERED_DESCENDING(0x1.FFFFFFFFFFFFFp62, 0x7FFFFFFFFFFFFB00LL); - ASSERT_ORDERED_DESCENDING(0x1.FFFFFFFFFFFFFp62, 0x7FFFFFFFFFFFFBFFLL); - ASSERT_ORDERED_ASCENDING(0x1.FFFFFFFFFFFFFp62, 0x7FFFFFFFFFFFFC01LL); - ASSERT_ORDERED_ASCENDING(0x1.FFFFFFFFFFFFFp62, 0x7FFFFFFFFFFFFD00LL); - - ASSERT_ORDERED_ASCENDING(0x1.FFFFFFFFFFFFEp62, 0x7FFFFFFFFFFFFC00LL); - - // Tests around MAX_SAFE_INTEGER - ASSERT_ORDERED_SAME(0x1.FFFFFFFFFFFFFp52, 0x1FFFFFFFFFFFFFLL); - ASSERT_ORDERED_DESCENDING(0x1.FFFFFFFFFFFFFp52, 0x1FFFFFFFFFFFFELL); - ASSERT_ORDERED_ASCENDING(0x1.FFFFFFFFFFFFEp52, 0x1FFFFFFFFFFFFFLL); - ASSERT_ORDERED_ASCENDING(0x1.FFFFFFFFFFFFFp52, 0x20000000000000LL); - - // Tests around MIN_SAFE_INTEGER - ASSERT_ORDERED_SAME(-0x1.FFFFFFFFFFFFFp52, -0x1FFFFFFFFFFFFFLL); - ASSERT_ORDERED_ASCENDING(-0x1.FFFFFFFFFFFFFp52, -0x1FFFFFFFFFFFFELL); - ASSERT_ORDERED_DESCENDING(-0x1.FFFFFFFFFFFFEp52, -0x1FFFFFFFFFFFFFLL); - ASSERT_ORDERED_DESCENDING(-0x1.FFFFFFFFFFFFFp52, -0x20000000000000LL); - - // Tests around zero. - ASSERT_ORDERED_SAME(-0.0, 0LL); - ASSERT_ORDERED_SAME(0.0, 0LL); - - // The smallest representable positive value should be greater than zero - ASSERT_ORDERED_DESCENDING(DBL_MIN, 0LL); - ASSERT_ORDERED_ASCENDING(-DBL_MIN, 0LL); - - // Note that 0x1.0p-1074 is a hex floating point literal representing the minimum subnormal - // number: . - double minSubNormal = 0x1.0p-1074; - ASSERT_ORDERED_DESCENDING(minSubNormal, 0LL); - ASSERT_ORDERED_ASCENDING(-minSubNormal, 0LL); - - // Other sanity checks - ASSERT_ORDERED_ASCENDING(0.5, 1LL); - ASSERT_ORDERED_DESCENDING(0.5, 0LL); - ASSERT_ORDERED_ASCENDING(1.5, 2LL); - ASSERT_ORDERED_DESCENDING(1.5, 1LL); -} - -@end diff --git a/Firestore/Example/Tests/Util/FSTDispatchQueueTests.mm b/Firestore/Example/Tests/Util/FSTDispatchQueueTests.mm new file mode 100644 index 00000000000..9f5b52d45b9 --- /dev/null +++ b/Firestore/Example/Tests/Util/FSTDispatchQueueTests.mm @@ -0,0 +1,111 @@ +/* + * Copyright 2018 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/Source/Util/FSTDispatchQueue.h" + +#import + +#import "Firestore/Example/Tests/Util/XCTestCase+Await.h" + +// In these generic tests the specific TimerIDs don't matter. +static const FSTTimerID timerID1 = FSTTimerIDListenStreamConnection; +static const FSTTimerID timerID2 = FSTTimerIDListenStreamIdle; +static const FSTTimerID timerID3 = FSTTimerIDWriteStreamConnection; + +@interface FSTDispatchQueueTests : XCTestCase +@end + +@implementation FSTDispatchQueueTests { + FSTDispatchQueue *_queue; + NSMutableArray *_completedSteps; + NSArray *_expectedSteps; + XCTestExpectation *_expectation; +} + +- (void)setUp { + [super setUp]; + dispatch_queue_t dispatch_queue = + dispatch_queue_create("FSTDispatchQueueTests", DISPATCH_QUEUE_SERIAL); + _queue = [[FSTDispatchQueue alloc] initWithQueue:dispatch_queue]; + _completedSteps = [NSMutableArray array]; + _expectedSteps = nil; +} + +/** + * Helper to return a block that adds @(n) to _completedSteps when run and fulfils _expectation if + * the _completedSteps match the _expectedSteps. + */ +- (void (^)())blockForStep:(int)n { + return ^void() { + [self->_completedSteps addObject:@(n)]; + if (self->_expectedSteps && self->_completedSteps.count >= self->_expectedSteps.count) { + XCTAssertEqualObjects(self->_completedSteps, self->_expectedSteps); + [self->_expectation fulfill]; + } + }; +} + +- (void)testCanScheduleCallbacksInTheFuture { + _expectation = [self expectationWithDescription:@"Expected steps"]; + _expectedSteps = @[ @1, @2, @3, @4 ]; + [_queue dispatchAsync:[self blockForStep:1]]; + [_queue dispatchAfterDelay:0.005 timerID:timerID1 block:[self blockForStep:4]]; + [_queue dispatchAfterDelay:0.001 timerID:timerID2 block:[self blockForStep:3]]; + [_queue dispatchAsync:[self blockForStep:2]]; + + [self awaitExpectations]; +} + +- (void)testCanCancelDelayedCallbacks { + _expectation = [self expectationWithDescription:@"Expected steps"]; + _expectedSteps = @[ @1, @3 ]; + // Queue everything from the queue to ensure nothing completes before we cancel. + [_queue dispatchAsync:^{ + [_queue dispatchAsyncAllowingSameQueue:[self blockForStep:1]]; + FSTDelayedCallback *step2Timer = + [_queue dispatchAfterDelay:.001 timerID:timerID1 block:[self blockForStep:2]]; + [_queue dispatchAfterDelay:.005 timerID:timerID2 block:[self blockForStep:3]]; + + XCTAssertTrue([_queue containsDelayedCallbackWithTimerID:timerID1]); + [step2Timer cancel]; + XCTAssertFalse([_queue containsDelayedCallbackWithTimerID:timerID1]); + }]; + + [self awaitExpectations]; +} + +- (void)testCanManuallyDrainAllDelayedCallbacksForTesting { + [_queue dispatchAsync:[self blockForStep:1]]; + [_queue dispatchAfterDelay:20 timerID:timerID1 block:[self blockForStep:4]]; + [_queue dispatchAfterDelay:10 timerID:timerID2 block:[self blockForStep:3]]; + [_queue dispatchAsync:[self blockForStep:2]]; + + [_queue runDelayedCallbacksUntil:FSTTimerIDAll]; + XCTAssertEqualObjects(_completedSteps, (@[ @1, @2, @3, @4 ])); +} + +- (void)testCanManuallyDrainSpecificDelayedCallbacksForTesting { + [_queue dispatchAsync:[self blockForStep:1]]; + [_queue dispatchAfterDelay:20 timerID:timerID1 block:[self blockForStep:5]]; + [_queue dispatchAfterDelay:10 timerID:timerID2 block:[self blockForStep:3]]; + [_queue dispatchAfterDelay:15 timerID:timerID3 block:[self blockForStep:4]]; + [_queue dispatchAsync:[self blockForStep:2]]; + + [_queue runDelayedCallbacksUntil:timerID3]; + XCTAssertEqualObjects(_completedSteps, (@[ @1, @2, @3, @4 ])); +} + +@end diff --git a/Firestore/Example/Tests/Util/FSTEventAccumulator.m b/Firestore/Example/Tests/Util/FSTEventAccumulator.mm similarity index 100% rename from Firestore/Example/Tests/Util/FSTEventAccumulator.m rename to Firestore/Example/Tests/Util/FSTEventAccumulator.mm diff --git a/Firestore/Example/Tests/Util/FSTHelpers.h b/Firestore/Example/Tests/Util/FSTHelpers.h index 4dbf91011d6..cc9f2ec4816 100644 --- a/Firestore/Example/Tests/Util/FSTHelpers.h +++ b/Firestore/Example/Tests/Util/FSTHelpers.h @@ -20,6 +20,8 @@ #import "Firestore/Source/Model/FSTDocumentDictionary.h" #import "Firestore/Source/Model/FSTDocumentKeySet.h" +#include "absl/strings/string_view.h" + @class FIRGeoPoint; @class FSTDeleteMutation; @class FSTDeletedDocument; @@ -37,7 +39,7 @@ @class FSTSnapshotVersion; @class FSTSortOrder; @class FSTTargetChange; -@class FSTTimestamp; +@class FIRTimestamp; @class FSTTransformMutation; @class FSTView; @class FSTViewSnapshot; @@ -132,8 +134,8 @@ extern "C" { XCTAssertTrue(__didThrow, ##__VA_ARGS__); \ }) -/** Creates a new FSTTimestamp from components. Note that year, month, and day are all one-based. */ -FSTTimestamp *FSTTestTimestamp(int year, int month, int day, int hour, int minute, int second); +/** Creates a new FIRTimestamp from components. Note that year, month, and day are all one-based. */ +FIRTimestamp *FSTTestTimestamp(int year, int month, int day, int hour, int minute, int second); /** Creates a new NSDate from components. Note that year, month, and day are all one-based. */ NSDate *FSTTestDate(int year, int month, int day, int hour, int minute, int second); @@ -190,7 +192,9 @@ FSTResourcePath *FSTTestPath(NSString *path); /** * A convenience method for creating a document reference from a path string. */ -FSTDocumentKeyReference *FSTTestRef(NSString *projectID, NSString *databaseID, NSString *path); +FSTDocumentKeyReference *FSTTestRef(const absl::string_view projectID, + const absl::string_view databaseID, + NSString *path); /** A convenience method for creating a query for the given path (without any other filters). */ FSTQuery *FSTTestQuery(NSString *path); diff --git a/Firestore/Example/Tests/Util/FSTHelpers.m b/Firestore/Example/Tests/Util/FSTHelpers.mm similarity index 91% rename from Firestore/Example/Tests/Util/FSTHelpers.m rename to Firestore/Example/Tests/Util/FSTHelpers.mm index f2b3605e550..649486acf4e 100644 --- a/Firestore/Example/Tests/Util/FSTHelpers.m +++ b/Firestore/Example/Tests/Util/FSTHelpers.mm @@ -16,18 +16,21 @@ #import "Firestore/Example/Tests/Util/FSTHelpers.h" -#import "FirebaseFirestore/FIRFieldPath.h" -#import "FirebaseFirestore/FIRGeoPoint.h" +#include +#include + +#import +#import +#import + #import "Firestore/Source/API/FIRFieldPath+Internal.h" #import "Firestore/Source/API/FSTUserDataConverter.h" #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Core/FSTSnapshotVersion.h" -#import "Firestore/Source/Core/FSTTimestamp.h" #import "Firestore/Source/Core/FSTView.h" #import "Firestore/Source/Core/FSTViewSnapshot.h" #import "Firestore/Source/Local/FSTLocalViewChanges.h" #import "Firestore/Source/Local/FSTQueryData.h" -#import "Firestore/Source/Model/FSTDatabaseID.h" #import "Firestore/Source/Model/FSTDocument.h" #import "Firestore/Source/Model/FSTDocumentKey.h" #import "Firestore/Source/Model/FSTDocumentSet.h" @@ -38,6 +41,12 @@ #import "Firestore/Source/Remote/FSTWatchChange.h" #import "Firestore/Source/Util/FSTAssert.h" +#include "Firestore/core/src/firebase/firestore/model/database_id.h" +#include "Firestore/core/src/firebase/firestore/util/string_apple.h" + +namespace util = firebase::firestore::util; +using firebase::firestore::model::DatabaseId; + NS_ASSUME_NONNULL_BEGIN /** A string sentinel that can be used with FSTTestPatchMutation() to mark a field for deletion. */ @@ -46,9 +55,9 @@ static const int kMicrosPerSec = 1000000; static const int kMillisPerSec = 1000; -FSTTimestamp *FSTTestTimestamp(int year, int month, int day, int hour, int minute, int second) { +FIRTimestamp *FSTTestTimestamp(int year, int month, int day, int hour, int minute, int second) { NSDate *date = FSTTestDate(year, month, day, hour, minute, second); - return [FSTTimestamp timestampWithDate:date]; + return [FIRTimestamp timestampWithDate:date]; } NSDate *FSTTestDate(int year, int month, int day, int hour, int minute, int second) { @@ -97,10 +106,10 @@ } FSTFieldValue *FSTTestFieldValue(id _Nullable value) { - FSTDatabaseID *databaseID = - [FSTDatabaseID databaseIDWithProject:@"project" database:kDefaultDatabaseID]; + // This owns the DatabaseIds since we do not have FirestoreClient instance to own them. + static DatabaseId database_id{"project", DatabaseId::kDefault}; FSTUserDataConverter *converter = - [[FSTUserDataConverter alloc] initWithDatabaseID:databaseID + [[FSTUserDataConverter alloc] initWithDatabaseID:&database_id preConverter:^id _Nullable(id _Nullable input) { return input; }]; @@ -131,7 +140,7 @@ int64_t seconds = versionMicroseconds / kMicrosPerSec; int32_t nanos = (int32_t)(versionMicroseconds % kMicrosPerSec) * kMillisPerSec; - FSTTimestamp *timestamp = [[FSTTimestamp alloc] initWithSeconds:seconds nanos:nanos]; + FIRTimestamp *timestamp = [[FIRTimestamp alloc] initWithSeconds:seconds nanoseconds:nanos]; return [FSTSnapshotVersion versionWithTimestamp:timestamp]; } @@ -163,9 +172,14 @@ return [FSTResourcePath pathWithSegments:FSTTestSplitPath(path)]; } -FSTDocumentKeyReference *FSTTestRef(NSString *projectID, NSString *database, NSString *path) { - FSTDatabaseID *databaseID = [FSTDatabaseID databaseIDWithProject:projectID database:database]; - return [[FSTDocumentKeyReference alloc] initWithKey:FSTTestDocKey(path) databaseID:databaseID]; +FSTDocumentKeyReference *FSTTestRef(const absl::string_view projectID, + const absl::string_view database, + NSString *path) { + // This owns the DatabaseIds since we do not have FirestoreClient instance to own them. + static std::list database_ids; + database_ids.emplace_back(projectID, database); + return [[FSTDocumentKeyReference alloc] initWithKey:FSTTestDocKey(path) + databaseID:&database_ids.back()]; } FSTQuery *FSTTestQuery(NSString *path) { diff --git a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.h b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.h index 6c6844907d7..ddff759c38b 100644 --- a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.h +++ b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.h @@ -14,11 +14,11 @@ * limitations under the License. */ -#import #import #import #import "Firestore/Example/Tests/Util/XCTestCase+Await.h" +#import "Firestore/Source/Core/FSTTypes.h" #import "FIRGetSource.h" @@ -30,6 +30,7 @@ @class FIRFirestoreSettings; @class FIRQuery; @class FSTEventAccumulator; +@class FSTDispatchQueue; NS_ASSUME_NONNULL_BEGIN @@ -63,8 +64,6 @@ extern "C" { - (FIRCollectionReference *)collectionRefWithDocuments: (NSDictionary *> *)documents; -- (void)waitForIdleFirestore:(FIRFirestore *)firestore; - - (void)writeAllDocuments:(NSDictionary *> *)documents toCollection:(FIRCollectionReference *)collection; @@ -93,6 +92,8 @@ extern "C" { - (void)enableNetwork; +- (FSTDispatchQueue *)queueForFirestore:(FIRFirestore *)firestore; + /** * "Blocks" the current thread/run loop until the block returns YES. * Should only be called on the main thread. diff --git a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm index 60e9979a310..333b4bc6129 100644 --- a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm +++ b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm @@ -16,23 +16,31 @@ #import "Firestore/Example/Tests/Util/FSTIntegrationTestCase.h" +#include +#include + #import #import #import #import +#include "Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.h" +#include "Firestore/core/src/firebase/firestore/model/database_id.h" #include "Firestore/core/src/firebase/firestore/util/autoid.h" +#include "Firestore/core/src/firebase/firestore/util/string_apple.h" +#include "absl/memory/memory.h" #import "Firestore/Source/API/FIRFirestore+Internal.h" -#import "Firestore/Source/Auth/FSTEmptyCredentialsProvider.h" #import "Firestore/Source/Core/FSTFirestoreClient.h" #import "Firestore/Source/Local/FSTLevelDB.h" -#import "Firestore/Source/Model/FSTDatabaseID.h" #import "Firestore/Source/Util/FSTDispatchQueue.h" #import "Firestore/Example/Tests/Util/FSTEventAccumulator.h" -#import "Firestore/Example/Tests/Util/FSTTestDispatchQueue.h" +namespace util = firebase::firestore::util; +using firebase::firestore::auth::CredentialsProvider; +using firebase::firestore::auth::EmptyCredentialsProvider; +using firebase::firestore::model::DatabaseId; using firebase::firestore::util::CreateAutoId; NS_ASSUME_NONNULL_BEGIN @@ -129,18 +137,19 @@ + (FIRFirestoreSettings *)settings { - (FIRFirestore *)firestoreWithProjectID:(NSString *)projectID { NSString *persistenceKey = [NSString stringWithFormat:@"db%lu", (unsigned long)_firestores.count]; - FSTTestDispatchQueue *workerDispatchQueue = [FSTTestDispatchQueue + FSTDispatchQueue *workerDispatchQueue = [FSTDispatchQueue 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 + std::unique_ptr credentials_provider = + absl::make_unique(); + + FIRFirestore *firestore = [[FIRFirestore alloc] initWithProjectID:util::MakeStringView(projectID) + database:DatabaseId::kDefault persistenceKey:persistenceKey - credentialsProvider:credentialsProvider + credentialsProvider:std::move(credentials_provider) workerDispatchQueue:workerDispatchQueue firebaseApp:app]; @@ -150,14 +159,6 @@ - (FIRFirestore *)firestoreWithProjectID:(NSString *)projectID { 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 { [firestore shutdownWithCompletion:[self completionForExpectationWithName:@"shutdown"]]; [self awaitExpectations]; @@ -295,6 +296,10 @@ - (void)enableNetwork { [self awaitExpectations]; } +- (FSTDispatchQueue *)queueForFirestore:(FIRFirestore *)firestore { + return firestore.workerDispatchQueue; +} + - (void)waitUntil:(BOOL (^)())predicate { NSTimeInterval start = [NSDate timeIntervalSinceReferenceDate]; double waitSeconds = [self defaultExpectationWaitSeconds]; diff --git a/Firestore/Example/Tests/Util/FSTTestDispatchQueue.m b/Firestore/Example/Tests/Util/FSTTestDispatchQueue.m deleted file mode 100644 index 8124cf25fc2..00000000000 --- a/Firestore/Example/Tests/Util/FSTTestDispatchQueue.m +++ /dev/null @@ -1,61 +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/Example/Tests/Util/FSTTestDispatchQueue.h" - -#import - -#import "Firestore/Source/Util/FSTAssert.h" - -@interface FSTTestDispatchQueue () - -@property(nonatomic, weak) XCTestExpectation* expectation; - -@end - -@implementation FSTTestDispatchQueue - -/** The delay used by the idle timeout */ -static const NSTimeInterval kIdleDispatchDelay = 60.0; - -/** The maximum delay we use in a test run. */ -static const NSTimeInterval kTestDispatchDelay = 1.0; - -+ (instancetype)queueWith:(dispatch_queue_t)dispatchQueue { - return [[FSTTestDispatchQueue alloc] initWithQueue:dispatchQueue]; -} - -- (instancetype)initWithQueue:(dispatch_queue_t)dispatchQueue { - return (self = [super initWithQueue:dispatchQueue]); -} - -- (void)dispatchAfterDelay:(NSTimeInterval)delay block:(void (^)(void))block { - [super dispatchAfterDelay:MIN(delay, kTestDispatchDelay) - block:^() { - block(); - if (delay == kIdleDispatchDelay) { - [_expectation fulfill]; - _expectation = nil; - } - }]; -} - -- (void)fulfillOnExecution:(XCTestExpectation*)expectation { - FSTAssert(_expectation == nil, @"Previous expectation still active"); - _expectation = expectation; -} - -@end diff --git a/Firestore/Example/Tests/Util/XCTestCase+Await.h b/Firestore/Example/Tests/Util/XCTestCase+Await.h index 7a8feb847ad..1afe8c0209f 100644 --- a/Firestore/Example/Tests/Util/XCTestCase+Await.h +++ b/Firestore/Example/Tests/Util/XCTestCase+Await.h @@ -14,9 +14,10 @@ * limitations under the License. */ -#import #import +#import "Firestore/Source/Core/FSTTypes.h" + @interface XCTestCase (Await) /** diff --git a/Firestore/Example/Tests/Util/XCTestCase+Await.m b/Firestore/Example/Tests/Util/XCTestCase+Await.mm similarity index 86% rename from Firestore/Example/Tests/Util/XCTestCase+Await.m rename to Firestore/Example/Tests/Util/XCTestCase+Await.mm index 7f4356c3a41..a5fefc9f5aa 100644 --- a/Firestore/Example/Tests/Util/XCTestCase+Await.m +++ b/Firestore/Example/Tests/Util/XCTestCase+Await.mm @@ -18,7 +18,9 @@ #import -static const double kExpectationWaitSeconds = 10.0; +// TODO(b/72864027): Reduce this to 10 seconds again once we've resolved issues with Query +// Conformance Tests flakiness or gotten answers from GRPC about reconnect delays. +static const double kExpectationWaitSeconds = 25.0; @implementation XCTestCase (Await) diff --git a/Firestore/Port/string_util.h b/Firestore/Port/string_util.h deleted file mode 100644 index 6e85ba9a655..00000000000 --- a/Firestore/Port/string_util.h +++ /dev/null @@ -1,66 +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. - */ - -// Useful string functions and so forth. This is a grab-bag file. -// -// These functions work fine for UTF-8 strings as long as you can -// consider them to be just byte strings. For example, due to the -// design of UTF-8 you do not need to worry about accidental matches, -// as long as all your inputs are valid UTF-8 (use \uHHHH, not \xHH or \oOOO). - -#ifndef IPHONE_FIRESTORE_PORT_STRING_UTIL_H_ -#define IPHONE_FIRESTORE_PORT_STRING_UTIL_H_ - -#include - -namespace leveldb { -class Slice; -} - -namespace Firestore { - -// Returns the smallest lexicographically larger string of equal or smaller -// length. Returns an empty string if there is no such successor (if the input -// is empty or consists entirely of 0xff bytes). -// Useful for calculating the smallest lexicographically larger string -// that will not be prefixed by the input string. -// -// Examples: -// "a" -> "b", "aaa" -> "aab", "aa\xff" -> "ab", "\xff" -> "", "" -> "" -std::string PrefixSuccessor(leveldb::Slice prefix); - -// Returns the immediate lexicographically-following string. This is useful to -// turn an inclusive range into something that can be used with Bigtable's -// SetLimitRow(): -// -// // Inclusive range [min_element, max_element]. -// string min_element = ...; -// string max_element = ...; -// -// // Equivalent range [range_start, range_end). -// string range_start = min_element; -// string range_end = ImmediateSuccessor(max_element); -// -// WARNING: Returns the input string with a '\0' appended; if you call c_str() -// on the result, it will compare equal to s. -// -// WARNING: Transforms "" -> "\0"; this doesn't account for Bigtable's special -// treatment of "" as infinity. -std::string ImmediateSuccessor(leveldb::Slice s); - -} // namespace Firestore - -#endif // IPHONE_FIRESTORE_PORT_STRING_UTIL_H_ diff --git a/Firestore/Protos/CMakeLists.txt b/Firestore/Protos/CMakeLists.txt new file mode 100644 index 00000000000..f20f702f6a5 --- /dev/null +++ b/Firestore/Protos/CMakeLists.txt @@ -0,0 +1,45 @@ +cc_library( + firebase_firestore_protos_nanopb + SOURCES + nanopb/firestore/local/maybe_document.pb.c + nanopb/firestore/local/maybe_document.pb.h + nanopb/firestore/local/mutation.pb.c + nanopb/firestore/local/mutation.pb.h + nanopb/firestore/local/target.pb.c + nanopb/firestore/local/target.pb.h + nanopb/google/api/annotations.pb.c + nanopb/google/api/annotations.pb.h + nanopb/google/api/http.pb.c + nanopb/google/api/http.pb.h + nanopb/google/firestore/v1beta1/common.pb.c + nanopb/google/firestore/v1beta1/common.pb.h + nanopb/google/firestore/v1beta1/document.pb.c + nanopb/google/firestore/v1beta1/document.pb.h + nanopb/google/firestore/v1beta1/firestore.pb.c + nanopb/google/firestore/v1beta1/firestore.pb.h + nanopb/google/firestore/v1beta1/query.pb.c + nanopb/google/firestore/v1beta1/query.pb.h + nanopb/google/firestore/v1beta1/write.pb.c + nanopb/google/firestore/v1beta1/write.pb.h + nanopb/google/protobuf/any.pb.c + nanopb/google/protobuf/any.pb.h + nanopb/google/protobuf/empty.pb.c + nanopb/google/protobuf/empty.pb.h + nanopb/google/protobuf/struct.pb.c + nanopb/google/protobuf/struct.pb.h + nanopb/google/protobuf/timestamp.pb.c + nanopb/google/protobuf/timestamp.pb.h + nanopb/google/protobuf/wrappers.pb.c + nanopb/google/protobuf/wrappers.pb.h + nanopb/google/rpc/status.pb.c + nanopb/google/rpc/status.pb.h + nanopb/google/type/latlng.pb.c + nanopb/google/type/latlng.pb.h + DEPENDS + nanopb +) + +target_compile_definitions( + firebase_firestore_protos_nanopb PUBLIC + -DPB_FIELD_16BIT +) diff --git a/Firestore/Protos/FrameworkMaker.xcodeproj/project.pbxproj b/Firestore/Protos/FrameworkMaker.xcodeproj/project.pbxproj index 51a61b81eae..2efcb210d67 100644 --- a/Firestore/Protos/FrameworkMaker.xcodeproj/project.pbxproj +++ b/Firestore/Protos/FrameworkMaker.xcodeproj/project.pbxproj @@ -201,7 +201,7 @@ ); inputPaths = ( "${SRCROOT}/Pods/Target Support Files/Pods-FrameworkMaker_iOS/Pods-FrameworkMaker_iOS-resources.sh", - "$PODS_CONFIGURATION_BUILD_DIR/gRPC/gRPCCertificates.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/gRPC/gRPCCertificates.bundle", ); name = "[CP] Copy Pods Resources"; outputPaths = ( diff --git a/Firestore/Protos/README.md b/Firestore/Protos/README.md index cb6d90e9185..6137ff6de23 100644 --- a/Firestore/Protos/README.md +++ b/Firestore/Protos/README.md @@ -1,5 +1,15 @@ ## Usage +First, build protobuf and nanopb +``` +cd firebase-ios-sdk +mkdir -p build +cd build +cmake .. +make -j protobuf nanopb +``` + +Next, build the protos: ``` cd firebase-ios-sdk/Firestore/Protos ./build-protos.sh diff --git a/Firestore/Protos/build-protos.sh b/Firestore/Protos/build-protos.sh index 4cfb12ed07e..a535f16cfbc 100755 --- a/Firestore/Protos/build-protos.sh +++ b/Firestore/Protos/build-protos.sh @@ -20,7 +20,21 @@ rm Podfile.lock pod update # Generate the objective C files from the protos. -./Pods/!ProtoCompiler/protoc --plugin=protoc-gen-grpc=Pods/\!ProtoCompiler-gRPCPlugin/grpc_objective_c_plugin -I protos --objc_out=objc --grpc_out=objc `find protos -name *.proto -print | xargs` +./Pods/!ProtoCompiler/protoc \ + --plugin=protoc-gen-grpc=Pods/\!ProtoCompiler-gRPCPlugin/grpc_objective_c_plugin \ + --plugin=../../build/external/nanopb/src/nanopb/generator/protoc-gen-nanopb \ + -I protos --objc_out=objc --grpc_out=objc \ + --nanopb_out="--options-file=protos/%s.options:nanopb" \ + `find protos -name *.proto -print | xargs` + +# Remove "well-known" protos from objc. (We get these for free. We only need +# them for nanopb.) +rm -rf objc/google/protobuf/ + +# If a proto uses a field named 'delete', nanopb happily uses that in the +# message definition. Works fine for C; not so much for C++. Rename uses of this +# to delete_ (which is how protoc does it for c++ files.) +perl -i -pe 's/\bdelete\b/delete_/g' `find nanopb -type f` # CocoaPods does not like paths in library imports, flatten them. diff --git a/Firestore/Protos/nanopb/firestore/local/maybe_document.pb.c b/Firestore/Protos/nanopb/firestore/local/maybe_document.pb.c new file mode 100644 index 00000000000..7cd40359602 --- /dev/null +++ b/Firestore/Protos/nanopb/firestore/local/maybe_document.pb.c @@ -0,0 +1,66 @@ +/* + * Copyright 2018 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. + */ + +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.3.8 at Fri Feb 2 17:48:02 2018. */ + +#include "maybe_document.pb.h" + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + + + +const pb_field_t firestore_client_NoDocument_fields[3] = { + PB_FIELD( 1, STRING , SINGULAR, CALLBACK, FIRST, firestore_client_NoDocument, name, name, 0), + PB_FIELD( 2, MESSAGE , SINGULAR, STATIC , OTHER, firestore_client_NoDocument, read_time, name, &google_protobuf_Timestamp_fields), + PB_LAST_FIELD +}; + +const pb_field_t firestore_client_MaybeDocument_fields[3] = { + PB_ONEOF_FIELD(document_type, 1, MESSAGE , ONEOF, STATIC , FIRST, firestore_client_MaybeDocument, no_document, no_document, &firestore_client_NoDocument_fields), + PB_ONEOF_FIELD(document_type, 2, MESSAGE , ONEOF, STATIC , UNION, firestore_client_MaybeDocument, document, document, &google_firestore_v1beta1_Document_fields), + PB_LAST_FIELD +}; + + +/* Check that field information fits in pb_field_t */ +#if !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_32BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in 8 or 16 bit + * field descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(firestore_client_NoDocument, read_time) < 65536 && pb_membersize(firestore_client_MaybeDocument, document_type.no_document) < 65536 && pb_membersize(firestore_client_MaybeDocument, document_type.document) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_firestore_client_NoDocument_firestore_client_MaybeDocument) +#endif + +#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_16BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in the default + * 8 bit descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(firestore_client_NoDocument, read_time) < 256 && pb_membersize(firestore_client_MaybeDocument, document_type.no_document) < 256 && pb_membersize(firestore_client_MaybeDocument, document_type.document) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_firestore_client_NoDocument_firestore_client_MaybeDocument) +#endif + + +/* @@protoc_insertion_point(eof) */ diff --git a/Firestore/Protos/nanopb/firestore/local/maybe_document.pb.h b/Firestore/Protos/nanopb/firestore/local/maybe_document.pb.h new file mode 100644 index 00000000000..b159cd11905 --- /dev/null +++ b/Firestore/Protos/nanopb/firestore/local/maybe_document.pb.h @@ -0,0 +1,88 @@ +/* + * Copyright 2018 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. + */ + +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.3.8 at Fri Feb 2 17:48:02 2018. */ + +#ifndef PB_FIRESTORE_CLIENT_MAYBE_DOCUMENT_PB_H_INCLUDED +#define PB_FIRESTORE_CLIENT_MAYBE_DOCUMENT_PB_H_INCLUDED +#include + +#include "google/firestore/v1beta1/document.pb.h" + +#include "google/protobuf/timestamp.pb.h" + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Struct definitions */ +typedef struct _firestore_client_NoDocument { + pb_callback_t name; + google_protobuf_Timestamp read_time; +/* @@protoc_insertion_point(struct:firestore_client_NoDocument) */ +} firestore_client_NoDocument; + +typedef struct _firestore_client_MaybeDocument { + pb_size_t which_document_type; + union { + firestore_client_NoDocument no_document; + google_firestore_v1beta1_Document document; + } document_type; +/* @@protoc_insertion_point(struct:firestore_client_MaybeDocument) */ +} firestore_client_MaybeDocument; + +/* Default values for struct fields */ + +/* Initializer values for message structs */ +#define firestore_client_NoDocument_init_default {{{NULL}, NULL}, google_protobuf_Timestamp_init_default} +#define firestore_client_MaybeDocument_init_default {0, {firestore_client_NoDocument_init_default}} +#define firestore_client_NoDocument_init_zero {{{NULL}, NULL}, google_protobuf_Timestamp_init_zero} +#define firestore_client_MaybeDocument_init_zero {0, {firestore_client_NoDocument_init_zero}} + +/* Field tags (for use in manual encoding/decoding) */ +#define firestore_client_NoDocument_name_tag 1 +#define firestore_client_NoDocument_read_time_tag 2 +#define firestore_client_MaybeDocument_no_document_tag 1 +#define firestore_client_MaybeDocument_document_tag 2 + +/* Struct field encoding specification for nanopb */ +extern const pb_field_t firestore_client_NoDocument_fields[3]; +extern const pb_field_t firestore_client_MaybeDocument_fields[3]; + +/* Maximum encoded size of messages (where known) */ +/* firestore_client_NoDocument_size depends on runtime parameters */ +/* firestore_client_MaybeDocument_size depends on runtime parameters */ + +/* Message IDs (where set with "msgid" option) */ +#ifdef PB_MSGID + +#define MAYBE_DOCUMENT_MESSAGES \ + + +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif +/* @@protoc_insertion_point(eof) */ + +#endif diff --git a/Firestore/Protos/nanopb/firestore/local/mutation.pb.c b/Firestore/Protos/nanopb/firestore/local/mutation.pb.c new file mode 100644 index 00000000000..7dedb14ed96 --- /dev/null +++ b/Firestore/Protos/nanopb/firestore/local/mutation.pb.c @@ -0,0 +1,67 @@ +/* + * Copyright 2018 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. + */ + +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.3.8 at Fri Feb 2 17:48:02 2018. */ + +#include "mutation.pb.h" + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + + + +const pb_field_t firestore_client_MutationQueue_fields[3] = { + PB_FIELD( 1, INT32 , SINGULAR, STATIC , FIRST, firestore_client_MutationQueue, last_acknowledged_batch_id, last_acknowledged_batch_id, 0), + PB_FIELD( 2, BYTES , SINGULAR, CALLBACK, OTHER, firestore_client_MutationQueue, last_stream_token, last_acknowledged_batch_id, 0), + PB_LAST_FIELD +}; + +const pb_field_t firestore_client_WriteBatch_fields[4] = { + PB_FIELD( 1, INT32 , SINGULAR, STATIC , FIRST, firestore_client_WriteBatch, batch_id, batch_id, 0), + PB_FIELD( 2, MESSAGE , REPEATED, CALLBACK, OTHER, firestore_client_WriteBatch, writes, batch_id, &google_firestore_v1beta1_Write_fields), + PB_FIELD( 3, MESSAGE , SINGULAR, STATIC , OTHER, firestore_client_WriteBatch, local_write_time, writes, &google_protobuf_Timestamp_fields), + PB_LAST_FIELD +}; + + +/* Check that field information fits in pb_field_t */ +#if !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_32BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in 8 or 16 bit + * field descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(firestore_client_WriteBatch, local_write_time) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_firestore_client_MutationQueue_firestore_client_WriteBatch) +#endif + +#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_16BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in the default + * 8 bit descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(firestore_client_WriteBatch, local_write_time) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_firestore_client_MutationQueue_firestore_client_WriteBatch) +#endif + + +/* @@protoc_insertion_point(eof) */ diff --git a/Firestore/Protos/nanopb/firestore/local/mutation.pb.h b/Firestore/Protos/nanopb/firestore/local/mutation.pb.h new file mode 100644 index 00000000000..537d0cd653f --- /dev/null +++ b/Firestore/Protos/nanopb/firestore/local/mutation.pb.h @@ -0,0 +1,87 @@ +/* + * Copyright 2018 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. + */ + +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.3.8 at Fri Feb 2 17:48:02 2018. */ + +#ifndef PB_FIRESTORE_CLIENT_MUTATION_PB_H_INCLUDED +#define PB_FIRESTORE_CLIENT_MUTATION_PB_H_INCLUDED +#include + +#include "google/firestore/v1beta1/write.pb.h" + +#include "google/protobuf/timestamp.pb.h" + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Struct definitions */ +typedef struct _firestore_client_MutationQueue { + int32_t last_acknowledged_batch_id; + pb_callback_t last_stream_token; +/* @@protoc_insertion_point(struct:firestore_client_MutationQueue) */ +} firestore_client_MutationQueue; + +typedef struct _firestore_client_WriteBatch { + int32_t batch_id; + pb_callback_t writes; + google_protobuf_Timestamp local_write_time; +/* @@protoc_insertion_point(struct:firestore_client_WriteBatch) */ +} firestore_client_WriteBatch; + +/* Default values for struct fields */ + +/* Initializer values for message structs */ +#define firestore_client_MutationQueue_init_default {0, {{NULL}, NULL}} +#define firestore_client_WriteBatch_init_default {0, {{NULL}, NULL}, google_protobuf_Timestamp_init_default} +#define firestore_client_MutationQueue_init_zero {0, {{NULL}, NULL}} +#define firestore_client_WriteBatch_init_zero {0, {{NULL}, NULL}, google_protobuf_Timestamp_init_zero} + +/* Field tags (for use in manual encoding/decoding) */ +#define firestore_client_MutationQueue_last_acknowledged_batch_id_tag 1 +#define firestore_client_MutationQueue_last_stream_token_tag 2 +#define firestore_client_WriteBatch_batch_id_tag 1 +#define firestore_client_WriteBatch_writes_tag 2 +#define firestore_client_WriteBatch_local_write_time_tag 3 + +/* Struct field encoding specification for nanopb */ +extern const pb_field_t firestore_client_MutationQueue_fields[3]; +extern const pb_field_t firestore_client_WriteBatch_fields[4]; + +/* Maximum encoded size of messages (where known) */ +/* firestore_client_MutationQueue_size depends on runtime parameters */ +/* firestore_client_WriteBatch_size depends on runtime parameters */ + +/* Message IDs (where set with "msgid" option) */ +#ifdef PB_MSGID + +#define MUTATION_MESSAGES \ + + +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif +/* @@protoc_insertion_point(eof) */ + +#endif diff --git a/Firestore/Protos/nanopb/firestore/local/target.pb.c b/Firestore/Protos/nanopb/firestore/local/target.pb.c new file mode 100644 index 00000000000..2805ceb7460 --- /dev/null +++ b/Firestore/Protos/nanopb/firestore/local/target.pb.c @@ -0,0 +1,71 @@ +/* + * Copyright 2018 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. + */ + +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.3.8 at Fri Feb 2 17:48:02 2018. */ + +#include "target.pb.h" + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + + + +const pb_field_t firestore_client_Target_fields[7] = { + PB_FIELD( 1, INT32 , SINGULAR, STATIC , FIRST, firestore_client_Target, target_id, target_id, 0), + PB_FIELD( 2, MESSAGE , SINGULAR, STATIC , OTHER, firestore_client_Target, snapshot_version, target_id, &google_protobuf_Timestamp_fields), + PB_FIELD( 3, BYTES , SINGULAR, CALLBACK, OTHER, firestore_client_Target, resume_token, snapshot_version, 0), + PB_FIELD( 4, INT64 , SINGULAR, STATIC , OTHER, firestore_client_Target, last_listen_sequence_number, resume_token, 0), + PB_ONEOF_FIELD(target_type, 5, MESSAGE , ONEOF, STATIC , OTHER, firestore_client_Target, query, last_listen_sequence_number, &google_firestore_v1beta1_Target_QueryTarget_fields), + PB_ONEOF_FIELD(target_type, 6, MESSAGE , ONEOF, STATIC , UNION, firestore_client_Target, documents, last_listen_sequence_number, &google_firestore_v1beta1_Target_DocumentsTarget_fields), + PB_LAST_FIELD +}; + +const pb_field_t firestore_client_TargetGlobal_fields[4] = { + PB_FIELD( 1, INT32 , SINGULAR, STATIC , FIRST, firestore_client_TargetGlobal, highest_target_id, highest_target_id, 0), + PB_FIELD( 2, INT64 , SINGULAR, STATIC , OTHER, firestore_client_TargetGlobal, highest_listen_sequence_number, highest_target_id, 0), + PB_FIELD( 3, MESSAGE , SINGULAR, STATIC , OTHER, firestore_client_TargetGlobal, last_remote_snapshot_version, highest_listen_sequence_number, &google_protobuf_Timestamp_fields), + PB_LAST_FIELD +}; + + +/* Check that field information fits in pb_field_t */ +#if !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_32BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in 8 or 16 bit + * field descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(firestore_client_Target, target_type.query) < 65536 && pb_membersize(firestore_client_Target, target_type.documents) < 65536 && pb_membersize(firestore_client_Target, snapshot_version) < 65536 && pb_membersize(firestore_client_TargetGlobal, last_remote_snapshot_version) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_firestore_client_Target_firestore_client_TargetGlobal) +#endif + +#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_16BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in the default + * 8 bit descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(firestore_client_Target, target_type.query) < 256 && pb_membersize(firestore_client_Target, target_type.documents) < 256 && pb_membersize(firestore_client_Target, snapshot_version) < 256 && pb_membersize(firestore_client_TargetGlobal, last_remote_snapshot_version) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_firestore_client_Target_firestore_client_TargetGlobal) +#endif + + +/* @@protoc_insertion_point(eof) */ diff --git a/Firestore/Protos/nanopb/firestore/local/target.pb.h b/Firestore/Protos/nanopb/firestore/local/target.pb.h new file mode 100644 index 00000000000..8cc146fd1fc --- /dev/null +++ b/Firestore/Protos/nanopb/firestore/local/target.pb.h @@ -0,0 +1,98 @@ +/* + * Copyright 2018 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. + */ + +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.3.8 at Fri Feb 2 17:48:02 2018. */ + +#ifndef PB_FIRESTORE_CLIENT_TARGET_PB_H_INCLUDED +#define PB_FIRESTORE_CLIENT_TARGET_PB_H_INCLUDED +#include + +#include "google/firestore/v1beta1/firestore.pb.h" + +#include "google/protobuf/timestamp.pb.h" + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Struct definitions */ +typedef struct _firestore_client_Target { + int32_t target_id; + google_protobuf_Timestamp snapshot_version; + pb_callback_t resume_token; + int64_t last_listen_sequence_number; + pb_size_t which_target_type; + union { + google_firestore_v1beta1_Target_QueryTarget query; + google_firestore_v1beta1_Target_DocumentsTarget documents; + } target_type; +/* @@protoc_insertion_point(struct:firestore_client_Target) */ +} firestore_client_Target; + +typedef struct _firestore_client_TargetGlobal { + int32_t highest_target_id; + int64_t highest_listen_sequence_number; + google_protobuf_Timestamp last_remote_snapshot_version; +/* @@protoc_insertion_point(struct:firestore_client_TargetGlobal) */ +} firestore_client_TargetGlobal; + +/* Default values for struct fields */ + +/* Initializer values for message structs */ +#define firestore_client_Target_init_default {0, google_protobuf_Timestamp_init_default, {{NULL}, NULL}, 0, 0, {google_firestore_v1beta1_Target_QueryTarget_init_default}} +#define firestore_client_TargetGlobal_init_default {0, 0, google_protobuf_Timestamp_init_default} +#define firestore_client_Target_init_zero {0, google_protobuf_Timestamp_init_zero, {{NULL}, NULL}, 0, 0, {google_firestore_v1beta1_Target_QueryTarget_init_zero}} +#define firestore_client_TargetGlobal_init_zero {0, 0, google_protobuf_Timestamp_init_zero} + +/* Field tags (for use in manual encoding/decoding) */ +#define firestore_client_Target_query_tag 5 +#define firestore_client_Target_documents_tag 6 +#define firestore_client_Target_target_id_tag 1 +#define firestore_client_Target_snapshot_version_tag 2 +#define firestore_client_Target_resume_token_tag 3 +#define firestore_client_Target_last_listen_sequence_number_tag 4 +#define firestore_client_TargetGlobal_highest_target_id_tag 1 +#define firestore_client_TargetGlobal_highest_listen_sequence_number_tag 2 +#define firestore_client_TargetGlobal_last_remote_snapshot_version_tag 3 + +/* Struct field encoding specification for nanopb */ +extern const pb_field_t firestore_client_Target_fields[7]; +extern const pb_field_t firestore_client_TargetGlobal_fields[4]; + +/* Maximum encoded size of messages (where known) */ +/* firestore_client_Target_size depends on runtime parameters */ +#define firestore_client_TargetGlobal_size 46 + +/* Message IDs (where set with "msgid" option) */ +#ifdef PB_MSGID + +#define TARGET_MESSAGES \ + + +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif +/* @@protoc_insertion_point(eof) */ + +#endif diff --git a/Firestore/Protos/nanopb/google/api/annotations.pb.c b/Firestore/Protos/nanopb/google/api/annotations.pb.c new file mode 100644 index 00000000000..6da5206772b --- /dev/null +++ b/Firestore/Protos/nanopb/google/api/annotations.pb.c @@ -0,0 +1,31 @@ +/* + * Copyright 2018 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. + */ + +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.3.8 at Fri Feb 2 17:48:02 2018. */ + +#include "annotations.pb.h" + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + + + + + +/* @@protoc_insertion_point(eof) */ diff --git a/Firestore/Protos/nanopb/google/api/annotations.pb.h b/Firestore/Protos/nanopb/google/api/annotations.pb.h new file mode 100644 index 00000000000..33c9ba8200e --- /dev/null +++ b/Firestore/Protos/nanopb/google/api/annotations.pb.h @@ -0,0 +1,44 @@ +/* + * Copyright 2018 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. + */ + +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.3.8 at Fri Feb 2 17:48:02 2018. */ + +#ifndef PB_GOOGLE_API_ANNOTATIONS_PB_H_INCLUDED +#define PB_GOOGLE_API_ANNOTATIONS_PB_H_INCLUDED +#include + +#include "google/api/http.pb.h" + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Extensions */ +/* Extension field google_api_http was skipped because only "optional" + type of extension fields is currently supported. */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif +/* @@protoc_insertion_point(eof) */ + +#endif diff --git a/Firestore/Protos/nanopb/google/api/http.pb.c b/Firestore/Protos/nanopb/google/api/http.pb.c new file mode 100644 index 00000000000..5219163c059 --- /dev/null +++ b/Firestore/Protos/nanopb/google/api/http.pb.c @@ -0,0 +1,78 @@ +/* + * Copyright 2018 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. + */ + +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.3.8 at Fri Feb 2 17:48:02 2018. */ + +#include "http.pb.h" + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + + + +const pb_field_t google_api_Http_fields[2] = { + PB_FIELD( 1, MESSAGE , REPEATED, CALLBACK, FIRST, google_api_Http, rules, rules, &google_api_HttpRule_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_api_HttpRule_fields[10] = { + PB_FIELD( 1, STRING , SINGULAR, CALLBACK, FIRST, google_api_HttpRule, selector, selector, 0), + PB_FIELD( 2, STRING , SINGULAR, CALLBACK, OTHER, google_api_HttpRule, get, selector, 0), + PB_FIELD( 3, STRING , SINGULAR, CALLBACK, OTHER, google_api_HttpRule, put, get, 0), + PB_FIELD( 4, STRING , SINGULAR, CALLBACK, OTHER, google_api_HttpRule, post, put, 0), + PB_FIELD( 5, STRING , SINGULAR, CALLBACK, OTHER, google_api_HttpRule, delete_, post, 0), + PB_FIELD( 6, STRING , SINGULAR, CALLBACK, OTHER, google_api_HttpRule, patch, delete_, 0), + PB_FIELD( 7, STRING , SINGULAR, CALLBACK, OTHER, google_api_HttpRule, body, patch, 0), + PB_FIELD( 8, MESSAGE , SINGULAR, STATIC , OTHER, google_api_HttpRule, custom, body, &google_api_CustomHttpPattern_fields), + PB_FIELD( 11, MESSAGE , REPEATED, CALLBACK, OTHER, google_api_HttpRule, additional_bindings, custom, &google_api_HttpRule_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_api_CustomHttpPattern_fields[3] = { + PB_FIELD( 1, STRING , SINGULAR, CALLBACK, FIRST, google_api_CustomHttpPattern, kind, kind, 0), + PB_FIELD( 2, STRING , SINGULAR, CALLBACK, OTHER, google_api_CustomHttpPattern, path, kind, 0), + PB_LAST_FIELD +}; + + +/* Check that field information fits in pb_field_t */ +#if !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_32BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in 8 or 16 bit + * field descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(google_api_HttpRule, custom) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_google_api_Http_google_api_HttpRule_google_api_CustomHttpPattern) +#endif + +#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_16BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in the default + * 8 bit descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(google_api_HttpRule, custom) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_google_api_Http_google_api_HttpRule_google_api_CustomHttpPattern) +#endif + + +/* @@protoc_insertion_point(eof) */ diff --git a/Firestore/Protos/nanopb/google/api/http.pb.h b/Firestore/Protos/nanopb/google/api/http.pb.h new file mode 100644 index 00000000000..e29758c0336 --- /dev/null +++ b/Firestore/Protos/nanopb/google/api/http.pb.h @@ -0,0 +1,105 @@ +/* + * Copyright 2018 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. + */ + +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.3.8 at Fri Feb 2 17:48:02 2018. */ + +#ifndef PB_GOOGLE_API_HTTP_PB_H_INCLUDED +#define PB_GOOGLE_API_HTTP_PB_H_INCLUDED +#include + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Struct definitions */ +typedef struct _google_api_CustomHttpPattern { + pb_callback_t kind; + pb_callback_t path; +/* @@protoc_insertion_point(struct:google_api_CustomHttpPattern) */ +} google_api_CustomHttpPattern; + +typedef struct _google_api_Http { + pb_callback_t rules; +/* @@protoc_insertion_point(struct:google_api_Http) */ +} google_api_Http; + +typedef struct _google_api_HttpRule { + pb_callback_t selector; + pb_callback_t get; + pb_callback_t put; + pb_callback_t post; + pb_callback_t delete_; + pb_callback_t patch; + pb_callback_t body; + google_api_CustomHttpPattern custom; + pb_callback_t additional_bindings; +/* @@protoc_insertion_point(struct:google_api_HttpRule) */ +} google_api_HttpRule; + +/* Default values for struct fields */ + +/* Initializer values for message structs */ +#define google_api_Http_init_default {{{NULL}, NULL}} +#define google_api_HttpRule_init_default {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, google_api_CustomHttpPattern_init_default, {{NULL}, NULL}} +#define google_api_CustomHttpPattern_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define google_api_Http_init_zero {{{NULL}, NULL}} +#define google_api_HttpRule_init_zero {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, google_api_CustomHttpPattern_init_zero, {{NULL}, NULL}} +#define google_api_CustomHttpPattern_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} + +/* Field tags (for use in manual encoding/decoding) */ +#define google_api_CustomHttpPattern_kind_tag 1 +#define google_api_CustomHttpPattern_path_tag 2 +#define google_api_Http_rules_tag 1 +#define google_api_HttpRule_selector_tag 1 +#define google_api_HttpRule_get_tag 2 +#define google_api_HttpRule_put_tag 3 +#define google_api_HttpRule_post_tag 4 +#define google_api_HttpRule_delete_tag 5 +#define google_api_HttpRule_patch_tag 6 +#define google_api_HttpRule_custom_tag 8 +#define google_api_HttpRule_body_tag 7 +#define google_api_HttpRule_additional_bindings_tag 11 + +/* Struct field encoding specification for nanopb */ +extern const pb_field_t google_api_Http_fields[2]; +extern const pb_field_t google_api_HttpRule_fields[10]; +extern const pb_field_t google_api_CustomHttpPattern_fields[3]; + +/* Maximum encoded size of messages (where known) */ +/* google_api_Http_size depends on runtime parameters */ +/* google_api_HttpRule_size depends on runtime parameters */ +/* google_api_CustomHttpPattern_size depends on runtime parameters */ + +/* Message IDs (where set with "msgid" option) */ +#ifdef PB_MSGID + +#define HTTP_MESSAGES \ + + +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif +/* @@protoc_insertion_point(eof) */ + +#endif diff --git a/Firestore/Protos/nanopb/google/firestore/v1beta1/common.pb.c b/Firestore/Protos/nanopb/google/firestore/v1beta1/common.pb.c new file mode 100644 index 00000000000..de2cf654c49 --- /dev/null +++ b/Firestore/Protos/nanopb/google/firestore/v1beta1/common.pb.c @@ -0,0 +1,81 @@ +/* + * Copyright 2018 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. + */ + +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.3.8 at Fri Feb 2 17:48:02 2018. */ + +#include "common.pb.h" + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + + + +const pb_field_t google_firestore_v1beta1_DocumentMask_fields[2] = { + PB_FIELD( 1, STRING , REPEATED, CALLBACK, FIRST, google_firestore_v1beta1_DocumentMask, field_paths, field_paths, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_Precondition_fields[3] = { + PB_ONEOF_FIELD(condition_type, 1, BOOL , ONEOF, STATIC , FIRST, google_firestore_v1beta1_Precondition, exists, exists, 0), + PB_ONEOF_FIELD(condition_type, 2, MESSAGE , ONEOF, STATIC , UNION, google_firestore_v1beta1_Precondition, update_time, update_time, &google_protobuf_Timestamp_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_TransactionOptions_fields[3] = { + PB_ONEOF_FIELD(mode, 2, MESSAGE , ONEOF, STATIC , FIRST, google_firestore_v1beta1_TransactionOptions, read_only, read_only, &google_firestore_v1beta1_TransactionOptions_ReadOnly_fields), + PB_ONEOF_FIELD(mode, 3, MESSAGE , ONEOF, STATIC , UNION, google_firestore_v1beta1_TransactionOptions, read_write, read_write, &google_firestore_v1beta1_TransactionOptions_ReadWrite_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_TransactionOptions_ReadWrite_fields[2] = { + PB_FIELD( 1, BYTES , SINGULAR, CALLBACK, FIRST, google_firestore_v1beta1_TransactionOptions_ReadWrite, retry_transaction, retry_transaction, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_TransactionOptions_ReadOnly_fields[2] = { + PB_ONEOF_FIELD(consistency_selector, 2, MESSAGE , ONEOF, STATIC , FIRST, google_firestore_v1beta1_TransactionOptions_ReadOnly, read_time, read_time, &google_protobuf_Timestamp_fields), + PB_LAST_FIELD +}; + + +/* Check that field information fits in pb_field_t */ +#if !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_32BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in 8 or 16 bit + * field descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(google_firestore_v1beta1_Precondition, condition_type.update_time) < 65536 && pb_membersize(google_firestore_v1beta1_TransactionOptions, mode.read_only) < 65536 && pb_membersize(google_firestore_v1beta1_TransactionOptions, mode.read_write) < 65536 && pb_membersize(google_firestore_v1beta1_TransactionOptions_ReadOnly, consistency_selector.read_time) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_google_firestore_v1beta1_DocumentMask_google_firestore_v1beta1_Precondition_google_firestore_v1beta1_TransactionOptions_google_firestore_v1beta1_TransactionOptions_ReadWrite_google_firestore_v1beta1_TransactionOptions_ReadOnly) +#endif + +#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_16BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in the default + * 8 bit descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(google_firestore_v1beta1_Precondition, condition_type.update_time) < 256 && pb_membersize(google_firestore_v1beta1_TransactionOptions, mode.read_only) < 256 && pb_membersize(google_firestore_v1beta1_TransactionOptions, mode.read_write) < 256 && pb_membersize(google_firestore_v1beta1_TransactionOptions_ReadOnly, consistency_selector.read_time) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_google_firestore_v1beta1_DocumentMask_google_firestore_v1beta1_Precondition_google_firestore_v1beta1_TransactionOptions_google_firestore_v1beta1_TransactionOptions_ReadWrite_google_firestore_v1beta1_TransactionOptions_ReadOnly) +#endif + + +/* @@protoc_insertion_point(eof) */ diff --git a/Firestore/Protos/nanopb/google/firestore/v1beta1/common.pb.h b/Firestore/Protos/nanopb/google/firestore/v1beta1/common.pb.h new file mode 100644 index 00000000000..277d9b82612 --- /dev/null +++ b/Firestore/Protos/nanopb/google/firestore/v1beta1/common.pb.h @@ -0,0 +1,124 @@ +/* + * Copyright 2018 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. + */ + +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.3.8 at Fri Feb 2 17:48:02 2018. */ + +#ifndef PB_GOOGLE_FIRESTORE_V1BETA1_COMMON_PB_H_INCLUDED +#define PB_GOOGLE_FIRESTORE_V1BETA1_COMMON_PB_H_INCLUDED +#include + +#include "google/api/annotations.pb.h" + +#include "google/protobuf/timestamp.pb.h" + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Struct definitions */ +typedef struct _google_firestore_v1beta1_DocumentMask { + pb_callback_t field_paths; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_DocumentMask) */ +} google_firestore_v1beta1_DocumentMask; + +typedef struct _google_firestore_v1beta1_TransactionOptions_ReadWrite { + pb_callback_t retry_transaction; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_TransactionOptions_ReadWrite) */ +} google_firestore_v1beta1_TransactionOptions_ReadWrite; + +typedef struct _google_firestore_v1beta1_Precondition { + pb_size_t which_condition_type; + union { + bool exists; + google_protobuf_Timestamp update_time; + } condition_type; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_Precondition) */ +} google_firestore_v1beta1_Precondition; + +typedef struct _google_firestore_v1beta1_TransactionOptions_ReadOnly { + pb_size_t which_consistency_selector; + union { + google_protobuf_Timestamp read_time; + } consistency_selector; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_TransactionOptions_ReadOnly) */ +} google_firestore_v1beta1_TransactionOptions_ReadOnly; + +typedef struct _google_firestore_v1beta1_TransactionOptions { + pb_size_t which_mode; + union { + google_firestore_v1beta1_TransactionOptions_ReadOnly read_only; + google_firestore_v1beta1_TransactionOptions_ReadWrite read_write; + } mode; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_TransactionOptions) */ +} google_firestore_v1beta1_TransactionOptions; + +/* Default values for struct fields */ + +/* Initializer values for message structs */ +#define google_firestore_v1beta1_DocumentMask_init_default {{{NULL}, NULL}} +#define google_firestore_v1beta1_Precondition_init_default {0, {0}} +#define google_firestore_v1beta1_TransactionOptions_init_default {0, {google_firestore_v1beta1_TransactionOptions_ReadOnly_init_default}} +#define google_firestore_v1beta1_TransactionOptions_ReadWrite_init_default {{{NULL}, NULL}} +#define google_firestore_v1beta1_TransactionOptions_ReadOnly_init_default {0, {google_protobuf_Timestamp_init_default}} +#define google_firestore_v1beta1_DocumentMask_init_zero {{{NULL}, NULL}} +#define google_firestore_v1beta1_Precondition_init_zero {0, {0}} +#define google_firestore_v1beta1_TransactionOptions_init_zero {0, {google_firestore_v1beta1_TransactionOptions_ReadOnly_init_zero}} +#define google_firestore_v1beta1_TransactionOptions_ReadWrite_init_zero {{{NULL}, NULL}} +#define google_firestore_v1beta1_TransactionOptions_ReadOnly_init_zero {0, {google_protobuf_Timestamp_init_zero}} + +/* Field tags (for use in manual encoding/decoding) */ +#define google_firestore_v1beta1_DocumentMask_field_paths_tag 1 +#define google_firestore_v1beta1_TransactionOptions_ReadWrite_retry_transaction_tag 1 +#define google_firestore_v1beta1_Precondition_exists_tag 1 +#define google_firestore_v1beta1_Precondition_update_time_tag 2 +#define google_firestore_v1beta1_TransactionOptions_ReadOnly_read_time_tag 2 +#define google_firestore_v1beta1_TransactionOptions_read_only_tag 2 +#define google_firestore_v1beta1_TransactionOptions_read_write_tag 3 + +/* Struct field encoding specification for nanopb */ +extern const pb_field_t google_firestore_v1beta1_DocumentMask_fields[2]; +extern const pb_field_t google_firestore_v1beta1_Precondition_fields[3]; +extern const pb_field_t google_firestore_v1beta1_TransactionOptions_fields[3]; +extern const pb_field_t google_firestore_v1beta1_TransactionOptions_ReadWrite_fields[2]; +extern const pb_field_t google_firestore_v1beta1_TransactionOptions_ReadOnly_fields[2]; + +/* Maximum encoded size of messages (where known) */ +/* google_firestore_v1beta1_DocumentMask_size depends on runtime parameters */ +#define google_firestore_v1beta1_Precondition_size 24 +/* google_firestore_v1beta1_TransactionOptions_size depends on runtime parameters */ +/* google_firestore_v1beta1_TransactionOptions_ReadWrite_size depends on runtime parameters */ +#define google_firestore_v1beta1_TransactionOptions_ReadOnly_size 24 + +/* Message IDs (where set with "msgid" option) */ +#ifdef PB_MSGID + +#define COMMON_MESSAGES \ + + +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif +/* @@protoc_insertion_point(eof) */ + +#endif diff --git a/Firestore/Protos/nanopb/google/firestore/v1beta1/document.pb.c b/Firestore/Protos/nanopb/google/firestore/v1beta1/document.pb.c new file mode 100644 index 00000000000..862c884f199 --- /dev/null +++ b/Firestore/Protos/nanopb/google/firestore/v1beta1/document.pb.c @@ -0,0 +1,105 @@ +/* + * Copyright 2018 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. + */ + +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.3.8 at Fri Feb 2 17:48:02 2018. */ + +#include "document.pb.h" + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + + + +const pb_field_t google_firestore_v1beta1_Document_fields[5] = { + PB_FIELD( 1, STRING , SINGULAR, CALLBACK, FIRST, google_firestore_v1beta1_Document, name, name, 0), + PB_FIELD( 2, MESSAGE , REPEATED, CALLBACK, OTHER, google_firestore_v1beta1_Document, fields, name, &google_firestore_v1beta1_Document_FieldsEntry_fields), + PB_FIELD( 3, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_Document, create_time, fields, &google_protobuf_Timestamp_fields), + PB_FIELD( 4, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_Document, update_time, create_time, &google_protobuf_Timestamp_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_Document_FieldsEntry_fields[3] = { + PB_FIELD( 1, STRING , SINGULAR, CALLBACK, FIRST, google_firestore_v1beta1_Document_FieldsEntry, key, key, 0), + PB_FIELD( 2, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_Document_FieldsEntry, value, key, &google_firestore_v1beta1_Value_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_Value_fields[12] = { + PB_FIELD( 1, BOOL , SINGULAR, STATIC , FIRST, google_firestore_v1beta1_Value, boolean_value, boolean_value, 0), + PB_FIELD( 2, INT64 , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_Value, integer_value, boolean_value, 0), + PB_FIELD( 3, DOUBLE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_Value, double_value, integer_value, 0), + PB_FIELD( 5, STRING , SINGULAR, CALLBACK, OTHER, google_firestore_v1beta1_Value, reference_value, double_value, 0), + PB_FIELD( 6, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_Value, map_value, reference_value, &google_firestore_v1beta1_MapValue_fields), + PB_FIELD( 8, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_Value, geo_point_value, map_value, &google_type_LatLng_fields), + PB_FIELD( 9, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_Value, array_value, geo_point_value, &google_firestore_v1beta1_ArrayValue_fields), + PB_FIELD( 10, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_Value, timestamp_value, array_value, &google_protobuf_Timestamp_fields), + PB_FIELD( 11, UENUM , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_Value, null_value, timestamp_value, 0), + PB_FIELD( 17, STRING , SINGULAR, CALLBACK, OTHER, google_firestore_v1beta1_Value, string_value, null_value, 0), + PB_FIELD( 18, BYTES , SINGULAR, CALLBACK, OTHER, google_firestore_v1beta1_Value, bytes_value, string_value, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_ArrayValue_fields[2] = { + PB_FIELD( 1, MESSAGE , REPEATED, CALLBACK, FIRST, google_firestore_v1beta1_ArrayValue, values, values, &google_firestore_v1beta1_Value_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_MapValue_fields[2] = { + PB_FIELD( 1, MESSAGE , REPEATED, CALLBACK, FIRST, google_firestore_v1beta1_MapValue, fields, fields, &google_firestore_v1beta1_MapValue_FieldsEntry_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_MapValue_FieldsEntry_fields[3] = { + PB_FIELD( 1, STRING , SINGULAR, CALLBACK, FIRST, google_firestore_v1beta1_MapValue_FieldsEntry, key, key, 0), + PB_FIELD( 2, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_MapValue_FieldsEntry, value, key, &google_firestore_v1beta1_Value_fields), + PB_LAST_FIELD +}; + + +/* Check that field information fits in pb_field_t */ +#if !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_32BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in 8 or 16 bit + * field descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(google_firestore_v1beta1_Document, create_time) < 65536 && pb_membersize(google_firestore_v1beta1_Document, update_time) < 65536 && pb_membersize(google_firestore_v1beta1_Document_FieldsEntry, value) < 65536 && pb_membersize(google_firestore_v1beta1_Value, timestamp_value) < 65536 && pb_membersize(google_firestore_v1beta1_Value, geo_point_value) < 65536 && pb_membersize(google_firestore_v1beta1_Value, array_value) < 65536 && pb_membersize(google_firestore_v1beta1_Value, map_value) < 65536 && pb_membersize(google_firestore_v1beta1_MapValue_FieldsEntry, value) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_google_firestore_v1beta1_Document_google_firestore_v1beta1_Document_FieldsEntry_google_firestore_v1beta1_Value_google_firestore_v1beta1_ArrayValue_google_firestore_v1beta1_MapValue_google_firestore_v1beta1_MapValue_FieldsEntry) +#endif + +#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_16BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in the default + * 8 bit descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(google_firestore_v1beta1_Document, create_time) < 256 && pb_membersize(google_firestore_v1beta1_Document, update_time) < 256 && pb_membersize(google_firestore_v1beta1_Document_FieldsEntry, value) < 256 && pb_membersize(google_firestore_v1beta1_Value, timestamp_value) < 256 && pb_membersize(google_firestore_v1beta1_Value, geo_point_value) < 256 && pb_membersize(google_firestore_v1beta1_Value, array_value) < 256 && pb_membersize(google_firestore_v1beta1_Value, map_value) < 256 && pb_membersize(google_firestore_v1beta1_MapValue_FieldsEntry, value) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_google_firestore_v1beta1_Document_google_firestore_v1beta1_Document_FieldsEntry_google_firestore_v1beta1_Value_google_firestore_v1beta1_ArrayValue_google_firestore_v1beta1_MapValue_google_firestore_v1beta1_MapValue_FieldsEntry) +#endif + + +/* On some platforms (such as AVR), double is really float. + * These are not directly supported by nanopb, but see example_avr_double. + * To get rid of this error, remove any double fields from your .proto. + */ +PB_STATIC_ASSERT(sizeof(double) == 8, DOUBLE_MUST_BE_8_BYTES) + +/* @@protoc_insertion_point(eof) */ diff --git a/Firestore/Protos/nanopb/google/firestore/v1beta1/document.pb.h b/Firestore/Protos/nanopb/google/firestore/v1beta1/document.pb.h new file mode 100644 index 00000000000..180c1af0cf4 --- /dev/null +++ b/Firestore/Protos/nanopb/google/firestore/v1beta1/document.pb.h @@ -0,0 +1,155 @@ +/* + * Copyright 2018 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. + */ + +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.3.8 at Fri Feb 2 17:48:02 2018. */ + +#ifndef PB_GOOGLE_FIRESTORE_V1BETA1_DOCUMENT_PB_H_INCLUDED +#define PB_GOOGLE_FIRESTORE_V1BETA1_DOCUMENT_PB_H_INCLUDED +#include + +#include "google/api/annotations.pb.h" + +#include "google/protobuf/struct.pb.h" + +#include "google/protobuf/timestamp.pb.h" + +#include "google/type/latlng.pb.h" + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Struct definitions */ +typedef struct _google_firestore_v1beta1_ArrayValue { + pb_callback_t values; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_ArrayValue) */ +} google_firestore_v1beta1_ArrayValue; + +typedef struct _google_firestore_v1beta1_MapValue { + pb_callback_t fields; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_MapValue) */ +} google_firestore_v1beta1_MapValue; + +typedef struct _google_firestore_v1beta1_Document { + pb_callback_t name; + pb_callback_t fields; + google_protobuf_Timestamp create_time; + google_protobuf_Timestamp update_time; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_Document) */ +} google_firestore_v1beta1_Document; + +typedef struct _google_firestore_v1beta1_Value { + bool boolean_value; + int64_t integer_value; + double double_value; + pb_callback_t reference_value; + google_firestore_v1beta1_MapValue map_value; + google_type_LatLng geo_point_value; + google_firestore_v1beta1_ArrayValue array_value; + google_protobuf_Timestamp timestamp_value; + google_protobuf_NullValue null_value; + pb_callback_t string_value; + pb_callback_t bytes_value; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_Value) */ +} google_firestore_v1beta1_Value; + +typedef struct _google_firestore_v1beta1_Document_FieldsEntry { + pb_callback_t key; + google_firestore_v1beta1_Value value; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_Document_FieldsEntry) */ +} google_firestore_v1beta1_Document_FieldsEntry; + +typedef struct _google_firestore_v1beta1_MapValue_FieldsEntry { + pb_callback_t key; + google_firestore_v1beta1_Value value; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_MapValue_FieldsEntry) */ +} google_firestore_v1beta1_MapValue_FieldsEntry; + +/* Default values for struct fields */ + +/* Initializer values for message structs */ +#define google_firestore_v1beta1_Document_init_default {{{NULL}, NULL}, {{NULL}, NULL}, google_protobuf_Timestamp_init_default, google_protobuf_Timestamp_init_default} +#define google_firestore_v1beta1_Document_FieldsEntry_init_default {{{NULL}, NULL}, google_firestore_v1beta1_Value_init_default} +#define google_firestore_v1beta1_Value_init_default {0, 0, 0, {{NULL}, NULL}, google_firestore_v1beta1_MapValue_init_default, google_type_LatLng_init_default, google_firestore_v1beta1_ArrayValue_init_default, google_protobuf_Timestamp_init_default, (google_protobuf_NullValue)0, {{NULL}, NULL}, {{NULL}, NULL}} +#define google_firestore_v1beta1_ArrayValue_init_default {{{NULL}, NULL}} +#define google_firestore_v1beta1_MapValue_init_default {{{NULL}, NULL}} +#define google_firestore_v1beta1_MapValue_FieldsEntry_init_default {{{NULL}, NULL}, google_firestore_v1beta1_Value_init_default} +#define google_firestore_v1beta1_Document_init_zero {{{NULL}, NULL}, {{NULL}, NULL}, google_protobuf_Timestamp_init_zero, google_protobuf_Timestamp_init_zero} +#define google_firestore_v1beta1_Document_FieldsEntry_init_zero {{{NULL}, NULL}, google_firestore_v1beta1_Value_init_zero} +#define google_firestore_v1beta1_Value_init_zero {0, 0, 0, {{NULL}, NULL}, google_firestore_v1beta1_MapValue_init_zero, google_type_LatLng_init_zero, google_firestore_v1beta1_ArrayValue_init_zero, google_protobuf_Timestamp_init_zero, (google_protobuf_NullValue)0, {{NULL}, NULL}, {{NULL}, NULL}} +#define google_firestore_v1beta1_ArrayValue_init_zero {{{NULL}, NULL}} +#define google_firestore_v1beta1_MapValue_init_zero {{{NULL}, NULL}} +#define google_firestore_v1beta1_MapValue_FieldsEntry_init_zero {{{NULL}, NULL}, google_firestore_v1beta1_Value_init_zero} + +/* Field tags (for use in manual encoding/decoding) */ +#define google_firestore_v1beta1_ArrayValue_values_tag 1 +#define google_firestore_v1beta1_MapValue_fields_tag 1 +#define google_firestore_v1beta1_Document_name_tag 1 +#define google_firestore_v1beta1_Document_fields_tag 2 +#define google_firestore_v1beta1_Document_create_time_tag 3 +#define google_firestore_v1beta1_Document_update_time_tag 4 +#define google_firestore_v1beta1_Value_null_value_tag 11 +#define google_firestore_v1beta1_Value_boolean_value_tag 1 +#define google_firestore_v1beta1_Value_integer_value_tag 2 +#define google_firestore_v1beta1_Value_double_value_tag 3 +#define google_firestore_v1beta1_Value_timestamp_value_tag 10 +#define google_firestore_v1beta1_Value_string_value_tag 17 +#define google_firestore_v1beta1_Value_bytes_value_tag 18 +#define google_firestore_v1beta1_Value_reference_value_tag 5 +#define google_firestore_v1beta1_Value_geo_point_value_tag 8 +#define google_firestore_v1beta1_Value_array_value_tag 9 +#define google_firestore_v1beta1_Value_map_value_tag 6 +#define google_firestore_v1beta1_Document_FieldsEntry_key_tag 1 +#define google_firestore_v1beta1_Document_FieldsEntry_value_tag 2 +#define google_firestore_v1beta1_MapValue_FieldsEntry_key_tag 1 +#define google_firestore_v1beta1_MapValue_FieldsEntry_value_tag 2 + +/* Struct field encoding specification for nanopb */ +extern const pb_field_t google_firestore_v1beta1_Document_fields[5]; +extern const pb_field_t google_firestore_v1beta1_Document_FieldsEntry_fields[3]; +extern const pb_field_t google_firestore_v1beta1_Value_fields[12]; +extern const pb_field_t google_firestore_v1beta1_ArrayValue_fields[2]; +extern const pb_field_t google_firestore_v1beta1_MapValue_fields[2]; +extern const pb_field_t google_firestore_v1beta1_MapValue_FieldsEntry_fields[3]; + +/* Maximum encoded size of messages (where known) */ +/* google_firestore_v1beta1_Document_size depends on runtime parameters */ +/* google_firestore_v1beta1_Document_FieldsEntry_size depends on runtime parameters */ +/* google_firestore_v1beta1_Value_size depends on runtime parameters */ +/* google_firestore_v1beta1_ArrayValue_size depends on runtime parameters */ +/* google_firestore_v1beta1_MapValue_size depends on runtime parameters */ +/* google_firestore_v1beta1_MapValue_FieldsEntry_size depends on runtime parameters */ + +/* Message IDs (where set with "msgid" option) */ +#ifdef PB_MSGID + +#define DOCUMENT_MESSAGES \ + + +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif +/* @@protoc_insertion_point(eof) */ + +#endif diff --git a/Firestore/Protos/nanopb/google/firestore/v1beta1/firestore.pb.c b/Firestore/Protos/nanopb/google/firestore/v1beta1/firestore.pb.c new file mode 100644 index 00000000000..bc8eca9f048 --- /dev/null +++ b/Firestore/Protos/nanopb/google/firestore/v1beta1/firestore.pb.c @@ -0,0 +1,259 @@ +/* + * Copyright 2018 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. + */ + +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.3.8 at Fri Feb 2 17:48:02 2018. */ + +#include "firestore.pb.h" + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + + + +const pb_field_t google_firestore_v1beta1_GetDocumentRequest_fields[5] = { + PB_FIELD( 1, STRING , SINGULAR, CALLBACK, FIRST, google_firestore_v1beta1_GetDocumentRequest, name, name, 0), + PB_FIELD( 2, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_GetDocumentRequest, mask, name, &google_firestore_v1beta1_DocumentMask_fields), + PB_FIELD( 3, BYTES , SINGULAR, CALLBACK, OTHER, google_firestore_v1beta1_GetDocumentRequest, transaction, mask, 0), + PB_FIELD( 5, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_GetDocumentRequest, read_time, transaction, &google_protobuf_Timestamp_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_ListDocumentsRequest_fields[10] = { + PB_FIELD( 1, STRING , SINGULAR, CALLBACK, FIRST, google_firestore_v1beta1_ListDocumentsRequest, parent, parent, 0), + PB_FIELD( 2, STRING , SINGULAR, CALLBACK, OTHER, google_firestore_v1beta1_ListDocumentsRequest, collection_id, parent, 0), + PB_FIELD( 3, INT32 , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_ListDocumentsRequest, page_size, collection_id, 0), + PB_FIELD( 4, STRING , SINGULAR, CALLBACK, OTHER, google_firestore_v1beta1_ListDocumentsRequest, page_token, page_size, 0), + PB_FIELD( 6, STRING , SINGULAR, CALLBACK, OTHER, google_firestore_v1beta1_ListDocumentsRequest, order_by, page_token, 0), + PB_FIELD( 7, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_ListDocumentsRequest, mask, order_by, &google_firestore_v1beta1_DocumentMask_fields), + PB_FIELD( 8, BYTES , SINGULAR, CALLBACK, OTHER, google_firestore_v1beta1_ListDocumentsRequest, transaction, mask, 0), + PB_FIELD( 10, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_ListDocumentsRequest, read_time, transaction, &google_protobuf_Timestamp_fields), + PB_FIELD( 12, BOOL , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_ListDocumentsRequest, show_missing, read_time, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_ListDocumentsResponse_fields[3] = { + PB_FIELD( 1, MESSAGE , REPEATED, CALLBACK, FIRST, google_firestore_v1beta1_ListDocumentsResponse, documents, documents, &google_firestore_v1beta1_Document_fields), + PB_FIELD( 2, STRING , SINGULAR, CALLBACK, OTHER, google_firestore_v1beta1_ListDocumentsResponse, next_page_token, documents, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_CreateDocumentRequest_fields[6] = { + PB_FIELD( 1, STRING , SINGULAR, CALLBACK, FIRST, google_firestore_v1beta1_CreateDocumentRequest, parent, parent, 0), + PB_FIELD( 2, STRING , SINGULAR, CALLBACK, OTHER, google_firestore_v1beta1_CreateDocumentRequest, collection_id, parent, 0), + PB_FIELD( 3, STRING , SINGULAR, CALLBACK, OTHER, google_firestore_v1beta1_CreateDocumentRequest, document_id, collection_id, 0), + PB_FIELD( 4, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_CreateDocumentRequest, document, document_id, &google_firestore_v1beta1_Document_fields), + PB_FIELD( 5, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_CreateDocumentRequest, mask, document, &google_firestore_v1beta1_DocumentMask_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_UpdateDocumentRequest_fields[5] = { + PB_FIELD( 1, MESSAGE , SINGULAR, STATIC , FIRST, google_firestore_v1beta1_UpdateDocumentRequest, document, document, &google_firestore_v1beta1_Document_fields), + PB_FIELD( 2, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_UpdateDocumentRequest, update_mask, document, &google_firestore_v1beta1_DocumentMask_fields), + PB_FIELD( 3, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_UpdateDocumentRequest, mask, update_mask, &google_firestore_v1beta1_DocumentMask_fields), + PB_FIELD( 4, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_UpdateDocumentRequest, current_document, mask, &google_firestore_v1beta1_Precondition_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_DeleteDocumentRequest_fields[3] = { + PB_FIELD( 1, STRING , SINGULAR, CALLBACK, FIRST, google_firestore_v1beta1_DeleteDocumentRequest, name, name, 0), + PB_FIELD( 2, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_DeleteDocumentRequest, current_document, name, &google_firestore_v1beta1_Precondition_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_BatchGetDocumentsRequest_fields[7] = { + PB_FIELD( 1, STRING , SINGULAR, CALLBACK, FIRST, google_firestore_v1beta1_BatchGetDocumentsRequest, database, database, 0), + PB_FIELD( 2, STRING , REPEATED, CALLBACK, OTHER, google_firestore_v1beta1_BatchGetDocumentsRequest, documents, database, 0), + PB_FIELD( 3, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_BatchGetDocumentsRequest, mask, documents, &google_firestore_v1beta1_DocumentMask_fields), + PB_FIELD( 4, BYTES , SINGULAR, CALLBACK, OTHER, google_firestore_v1beta1_BatchGetDocumentsRequest, transaction, mask, 0), + PB_FIELD( 5, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_BatchGetDocumentsRequest, new_transaction, transaction, &google_firestore_v1beta1_TransactionOptions_fields), + PB_FIELD( 7, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_BatchGetDocumentsRequest, read_time, new_transaction, &google_protobuf_Timestamp_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_BatchGetDocumentsResponse_fields[5] = { + PB_FIELD( 1, MESSAGE , SINGULAR, STATIC , FIRST, google_firestore_v1beta1_BatchGetDocumentsResponse, found, found, &google_firestore_v1beta1_Document_fields), + PB_FIELD( 2, STRING , SINGULAR, CALLBACK, OTHER, google_firestore_v1beta1_BatchGetDocumentsResponse, missing, found, 0), + PB_FIELD( 3, BYTES , SINGULAR, CALLBACK, OTHER, google_firestore_v1beta1_BatchGetDocumentsResponse, transaction, missing, 0), + PB_FIELD( 4, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_BatchGetDocumentsResponse, read_time, transaction, &google_protobuf_Timestamp_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_BeginTransactionRequest_fields[3] = { + PB_FIELD( 1, STRING , SINGULAR, CALLBACK, FIRST, google_firestore_v1beta1_BeginTransactionRequest, database, database, 0), + PB_FIELD( 2, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_BeginTransactionRequest, options, database, &google_firestore_v1beta1_TransactionOptions_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_BeginTransactionResponse_fields[2] = { + PB_FIELD( 1, BYTES , SINGULAR, CALLBACK, FIRST, google_firestore_v1beta1_BeginTransactionResponse, transaction, transaction, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_CommitRequest_fields[4] = { + PB_FIELD( 1, STRING , SINGULAR, CALLBACK, FIRST, google_firestore_v1beta1_CommitRequest, database, database, 0), + PB_FIELD( 2, MESSAGE , REPEATED, CALLBACK, OTHER, google_firestore_v1beta1_CommitRequest, writes, database, &google_firestore_v1beta1_Write_fields), + PB_FIELD( 3, BYTES , SINGULAR, CALLBACK, OTHER, google_firestore_v1beta1_CommitRequest, transaction, writes, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_CommitResponse_fields[3] = { + PB_FIELD( 1, MESSAGE , REPEATED, CALLBACK, FIRST, google_firestore_v1beta1_CommitResponse, write_results, write_results, &google_firestore_v1beta1_WriteResult_fields), + PB_FIELD( 2, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_CommitResponse, commit_time, write_results, &google_protobuf_Timestamp_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_RollbackRequest_fields[3] = { + PB_FIELD( 1, STRING , SINGULAR, CALLBACK, FIRST, google_firestore_v1beta1_RollbackRequest, database, database, 0), + PB_FIELD( 2, BYTES , SINGULAR, CALLBACK, OTHER, google_firestore_v1beta1_RollbackRequest, transaction, database, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_RunQueryRequest_fields[6] = { + PB_FIELD( 1, STRING , SINGULAR, CALLBACK, FIRST, google_firestore_v1beta1_RunQueryRequest, parent, parent, 0), + PB_ONEOF_FIELD(query_type, 2, MESSAGE , ONEOF, STATIC , OTHER, google_firestore_v1beta1_RunQueryRequest, structured_query, parent, &google_firestore_v1beta1_StructuredQuery_fields), + PB_FIELD( 5, BYTES , SINGULAR, CALLBACK, OTHER, google_firestore_v1beta1_RunQueryRequest, transaction, query_type.structured_query, 0), + PB_FIELD( 6, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_RunQueryRequest, new_transaction, transaction, &google_firestore_v1beta1_TransactionOptions_fields), + PB_FIELD( 7, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_RunQueryRequest, read_time, new_transaction, &google_protobuf_Timestamp_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_RunQueryResponse_fields[5] = { + PB_FIELD( 1, MESSAGE , SINGULAR, STATIC , FIRST, google_firestore_v1beta1_RunQueryResponse, document, document, &google_firestore_v1beta1_Document_fields), + PB_FIELD( 2, BYTES , SINGULAR, CALLBACK, OTHER, google_firestore_v1beta1_RunQueryResponse, transaction, document, 0), + PB_FIELD( 3, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_RunQueryResponse, read_time, transaction, &google_protobuf_Timestamp_fields), + PB_FIELD( 4, INT32 , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_RunQueryResponse, skipped_results, read_time, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_WriteRequest_fields[6] = { + PB_FIELD( 1, STRING , SINGULAR, CALLBACK, FIRST, google_firestore_v1beta1_WriteRequest, database, database, 0), + PB_FIELD( 2, STRING , SINGULAR, CALLBACK, OTHER, google_firestore_v1beta1_WriteRequest, stream_id, database, 0), + PB_FIELD( 3, MESSAGE , REPEATED, CALLBACK, OTHER, google_firestore_v1beta1_WriteRequest, writes, stream_id, &google_firestore_v1beta1_Write_fields), + PB_FIELD( 4, BYTES , SINGULAR, CALLBACK, OTHER, google_firestore_v1beta1_WriteRequest, stream_token, writes, 0), + PB_FIELD( 5, MESSAGE , REPEATED, CALLBACK, OTHER, google_firestore_v1beta1_WriteRequest, labels, stream_token, &google_firestore_v1beta1_WriteRequest_LabelsEntry_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_WriteRequest_LabelsEntry_fields[3] = { + PB_FIELD( 1, STRING , SINGULAR, CALLBACK, FIRST, google_firestore_v1beta1_WriteRequest_LabelsEntry, key, key, 0), + PB_FIELD( 2, STRING , SINGULAR, CALLBACK, OTHER, google_firestore_v1beta1_WriteRequest_LabelsEntry, value, key, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_WriteResponse_fields[5] = { + PB_FIELD( 1, STRING , SINGULAR, CALLBACK, FIRST, google_firestore_v1beta1_WriteResponse, stream_id, stream_id, 0), + PB_FIELD( 2, BYTES , SINGULAR, CALLBACK, OTHER, google_firestore_v1beta1_WriteResponse, stream_token, stream_id, 0), + PB_FIELD( 3, MESSAGE , REPEATED, CALLBACK, OTHER, google_firestore_v1beta1_WriteResponse, write_results, stream_token, &google_firestore_v1beta1_WriteResult_fields), + PB_FIELD( 4, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_WriteResponse, commit_time, write_results, &google_protobuf_Timestamp_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_ListenRequest_fields[5] = { + PB_FIELD( 1, STRING , SINGULAR, CALLBACK, FIRST, google_firestore_v1beta1_ListenRequest, database, database, 0), + PB_ONEOF_FIELD(target_change, 2, MESSAGE , ONEOF, STATIC , OTHER, google_firestore_v1beta1_ListenRequest, add_target, database, &google_firestore_v1beta1_Target_fields), + PB_ONEOF_FIELD(target_change, 3, INT32 , ONEOF, STATIC , UNION, google_firestore_v1beta1_ListenRequest, remove_target, database, 0), + PB_FIELD( 4, MESSAGE , REPEATED, CALLBACK, OTHER, google_firestore_v1beta1_ListenRequest, labels, target_change.remove_target, &google_firestore_v1beta1_ListenRequest_LabelsEntry_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_ListenRequest_LabelsEntry_fields[3] = { + PB_FIELD( 1, STRING , SINGULAR, CALLBACK, FIRST, google_firestore_v1beta1_ListenRequest_LabelsEntry, key, key, 0), + PB_FIELD( 2, STRING , SINGULAR, CALLBACK, OTHER, google_firestore_v1beta1_ListenRequest_LabelsEntry, value, key, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_ListenResponse_fields[6] = { + PB_ONEOF_FIELD(response_type, 2, MESSAGE , ONEOF, STATIC , FIRST, google_firestore_v1beta1_ListenResponse, target_change, target_change, &google_firestore_v1beta1_TargetChange_fields), + PB_ONEOF_FIELD(response_type, 3, MESSAGE , ONEOF, STATIC , UNION, google_firestore_v1beta1_ListenResponse, document_change, document_change, &google_firestore_v1beta1_DocumentChange_fields), + PB_ONEOF_FIELD(response_type, 4, MESSAGE , ONEOF, STATIC , UNION, google_firestore_v1beta1_ListenResponse, document_delete, document_delete, &google_firestore_v1beta1_DocumentDelete_fields), + PB_ONEOF_FIELD(response_type, 5, MESSAGE , ONEOF, STATIC , UNION, google_firestore_v1beta1_ListenResponse, filter, filter, &google_firestore_v1beta1_ExistenceFilter_fields), + PB_ONEOF_FIELD(response_type, 6, MESSAGE , ONEOF, STATIC , UNION, google_firestore_v1beta1_ListenResponse, document_remove, document_remove, &google_firestore_v1beta1_DocumentRemove_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_Target_fields[7] = { + PB_ONEOF_FIELD(target_type, 2, MESSAGE , ONEOF, STATIC , FIRST, google_firestore_v1beta1_Target, query, query, &google_firestore_v1beta1_Target_QueryTarget_fields), + PB_ONEOF_FIELD(target_type, 3, MESSAGE , ONEOF, STATIC , UNION, google_firestore_v1beta1_Target, documents, documents, &google_firestore_v1beta1_Target_DocumentsTarget_fields), + PB_FIELD( 4, BYTES , SINGULAR, CALLBACK, OTHER, google_firestore_v1beta1_Target, resume_token, target_type.documents, 0), + PB_FIELD( 5, INT32 , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_Target, target_id, resume_token, 0), + PB_FIELD( 6, BOOL , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_Target, once, target_id, 0), + PB_FIELD( 11, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_Target, read_time, once, &google_protobuf_Timestamp_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_Target_DocumentsTarget_fields[2] = { + PB_FIELD( 2, STRING , REPEATED, CALLBACK, FIRST, google_firestore_v1beta1_Target_DocumentsTarget, documents, documents, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_Target_QueryTarget_fields[3] = { + PB_FIELD( 1, STRING , SINGULAR, CALLBACK, FIRST, google_firestore_v1beta1_Target_QueryTarget, parent, parent, 0), + PB_ONEOF_FIELD(query_type, 2, MESSAGE , ONEOF, STATIC , OTHER, google_firestore_v1beta1_Target_QueryTarget, structured_query, parent, &google_firestore_v1beta1_StructuredQuery_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_TargetChange_fields[6] = { + PB_FIELD( 1, UENUM , SINGULAR, STATIC , FIRST, google_firestore_v1beta1_TargetChange, target_change_type, target_change_type, 0), + PB_FIELD( 2, INT32 , REPEATED, CALLBACK, OTHER, google_firestore_v1beta1_TargetChange, target_ids, target_change_type, 0), + PB_FIELD( 3, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_TargetChange, cause, target_ids, &google_rpc_Status_fields), + PB_FIELD( 4, BYTES , SINGULAR, CALLBACK, OTHER, google_firestore_v1beta1_TargetChange, resume_token, cause, 0), + PB_FIELD( 6, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_TargetChange, read_time, resume_token, &google_protobuf_Timestamp_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_ListCollectionIdsRequest_fields[4] = { + PB_FIELD( 1, STRING , SINGULAR, CALLBACK, FIRST, google_firestore_v1beta1_ListCollectionIdsRequest, parent, parent, 0), + PB_FIELD( 2, INT32 , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_ListCollectionIdsRequest, page_size, parent, 0), + PB_FIELD( 3, STRING , SINGULAR, CALLBACK, OTHER, google_firestore_v1beta1_ListCollectionIdsRequest, page_token, page_size, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_ListCollectionIdsResponse_fields[3] = { + PB_FIELD( 1, STRING , REPEATED, CALLBACK, FIRST, google_firestore_v1beta1_ListCollectionIdsResponse, collection_ids, collection_ids, 0), + PB_FIELD( 2, STRING , SINGULAR, CALLBACK, OTHER, google_firestore_v1beta1_ListCollectionIdsResponse, next_page_token, collection_ids, 0), + PB_LAST_FIELD +}; + + + +/* Check that field information fits in pb_field_t */ +#if !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_32BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in 8 or 16 bit + * field descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(google_firestore_v1beta1_GetDocumentRequest, mask) < 65536 && pb_membersize(google_firestore_v1beta1_GetDocumentRequest, read_time) < 65536 && pb_membersize(google_firestore_v1beta1_ListDocumentsRequest, mask) < 65536 && pb_membersize(google_firestore_v1beta1_ListDocumentsRequest, read_time) < 65536 && pb_membersize(google_firestore_v1beta1_CreateDocumentRequest, document) < 65536 && pb_membersize(google_firestore_v1beta1_CreateDocumentRequest, mask) < 65536 && pb_membersize(google_firestore_v1beta1_UpdateDocumentRequest, document) < 65536 && pb_membersize(google_firestore_v1beta1_UpdateDocumentRequest, update_mask) < 65536 && pb_membersize(google_firestore_v1beta1_UpdateDocumentRequest, mask) < 65536 && pb_membersize(google_firestore_v1beta1_UpdateDocumentRequest, current_document) < 65536 && pb_membersize(google_firestore_v1beta1_DeleteDocumentRequest, current_document) < 65536 && pb_membersize(google_firestore_v1beta1_BatchGetDocumentsRequest, mask) < 65536 && pb_membersize(google_firestore_v1beta1_BatchGetDocumentsRequest, new_transaction) < 65536 && pb_membersize(google_firestore_v1beta1_BatchGetDocumentsRequest, read_time) < 65536 && pb_membersize(google_firestore_v1beta1_BatchGetDocumentsResponse, found) < 65536 && pb_membersize(google_firestore_v1beta1_BatchGetDocumentsResponse, read_time) < 65536 && pb_membersize(google_firestore_v1beta1_BeginTransactionRequest, options) < 65536 && pb_membersize(google_firestore_v1beta1_CommitResponse, commit_time) < 65536 && pb_membersize(google_firestore_v1beta1_RunQueryRequest, query_type.structured_query) < 65536 && pb_membersize(google_firestore_v1beta1_RunQueryRequest, new_transaction) < 65536 && pb_membersize(google_firestore_v1beta1_RunQueryRequest, read_time) < 65536 && pb_membersize(google_firestore_v1beta1_RunQueryResponse, document) < 65536 && pb_membersize(google_firestore_v1beta1_RunQueryResponse, read_time) < 65536 && pb_membersize(google_firestore_v1beta1_WriteResponse, commit_time) < 65536 && pb_membersize(google_firestore_v1beta1_ListenRequest, target_change.add_target) < 65536 && pb_membersize(google_firestore_v1beta1_ListenResponse, response_type.target_change) < 65536 && pb_membersize(google_firestore_v1beta1_ListenResponse, response_type.document_change) < 65536 && pb_membersize(google_firestore_v1beta1_ListenResponse, response_type.document_delete) < 65536 && pb_membersize(google_firestore_v1beta1_ListenResponse, response_type.filter) < 65536 && pb_membersize(google_firestore_v1beta1_ListenResponse, response_type.document_remove) < 65536 && pb_membersize(google_firestore_v1beta1_Target, target_type.query) < 65536 && pb_membersize(google_firestore_v1beta1_Target, target_type.documents) < 65536 && pb_membersize(google_firestore_v1beta1_Target, read_time) < 65536 && pb_membersize(google_firestore_v1beta1_Target_QueryTarget, query_type.structured_query) < 65536 && pb_membersize(google_firestore_v1beta1_TargetChange, cause) < 65536 && pb_membersize(google_firestore_v1beta1_TargetChange, read_time) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_google_firestore_v1beta1_GetDocumentRequest_google_firestore_v1beta1_ListDocumentsRequest_google_firestore_v1beta1_ListDocumentsResponse_google_firestore_v1beta1_CreateDocumentRequest_google_firestore_v1beta1_UpdateDocumentRequest_google_firestore_v1beta1_DeleteDocumentRequest_google_firestore_v1beta1_BatchGetDocumentsRequest_google_firestore_v1beta1_BatchGetDocumentsResponse_google_firestore_v1beta1_BeginTransactionRequest_google_firestore_v1beta1_BeginTransactionResponse_google_firestore_v1beta1_CommitRequest_google_firestore_v1beta1_CommitResponse_google_firestore_v1beta1_RollbackRequest_google_firestore_v1beta1_RunQueryRequest_google_firestore_v1beta1_RunQueryResponse_google_firestore_v1beta1_WriteRequest_google_firestore_v1beta1_WriteRequest_LabelsEntry_google_firestore_v1beta1_WriteResponse_google_firestore_v1beta1_ListenRequest_google_firestore_v1beta1_ListenRequest_LabelsEntry_google_firestore_v1beta1_ListenResponse_google_firestore_v1beta1_Target_google_firestore_v1beta1_Target_DocumentsTarget_google_firestore_v1beta1_Target_QueryTarget_google_firestore_v1beta1_TargetChange_google_firestore_v1beta1_ListCollectionIdsRequest_google_firestore_v1beta1_ListCollectionIdsResponse) +#endif + +#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_16BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in the default + * 8 bit descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(google_firestore_v1beta1_GetDocumentRequest, mask) < 256 && pb_membersize(google_firestore_v1beta1_GetDocumentRequest, read_time) < 256 && pb_membersize(google_firestore_v1beta1_ListDocumentsRequest, mask) < 256 && pb_membersize(google_firestore_v1beta1_ListDocumentsRequest, read_time) < 256 && pb_membersize(google_firestore_v1beta1_CreateDocumentRequest, document) < 256 && pb_membersize(google_firestore_v1beta1_CreateDocumentRequest, mask) < 256 && pb_membersize(google_firestore_v1beta1_UpdateDocumentRequest, document) < 256 && pb_membersize(google_firestore_v1beta1_UpdateDocumentRequest, update_mask) < 256 && pb_membersize(google_firestore_v1beta1_UpdateDocumentRequest, mask) < 256 && pb_membersize(google_firestore_v1beta1_UpdateDocumentRequest, current_document) < 256 && pb_membersize(google_firestore_v1beta1_DeleteDocumentRequest, current_document) < 256 && pb_membersize(google_firestore_v1beta1_BatchGetDocumentsRequest, mask) < 256 && pb_membersize(google_firestore_v1beta1_BatchGetDocumentsRequest, new_transaction) < 256 && pb_membersize(google_firestore_v1beta1_BatchGetDocumentsRequest, read_time) < 256 && pb_membersize(google_firestore_v1beta1_BatchGetDocumentsResponse, found) < 256 && pb_membersize(google_firestore_v1beta1_BatchGetDocumentsResponse, read_time) < 256 && pb_membersize(google_firestore_v1beta1_BeginTransactionRequest, options) < 256 && pb_membersize(google_firestore_v1beta1_CommitResponse, commit_time) < 256 && pb_membersize(google_firestore_v1beta1_RunQueryRequest, query_type.structured_query) < 256 && pb_membersize(google_firestore_v1beta1_RunQueryRequest, new_transaction) < 256 && pb_membersize(google_firestore_v1beta1_RunQueryRequest, read_time) < 256 && pb_membersize(google_firestore_v1beta1_RunQueryResponse, document) < 256 && pb_membersize(google_firestore_v1beta1_RunQueryResponse, read_time) < 256 && pb_membersize(google_firestore_v1beta1_WriteResponse, commit_time) < 256 && pb_membersize(google_firestore_v1beta1_ListenRequest, target_change.add_target) < 256 && pb_membersize(google_firestore_v1beta1_ListenResponse, response_type.target_change) < 256 && pb_membersize(google_firestore_v1beta1_ListenResponse, response_type.document_change) < 256 && pb_membersize(google_firestore_v1beta1_ListenResponse, response_type.document_delete) < 256 && pb_membersize(google_firestore_v1beta1_ListenResponse, response_type.filter) < 256 && pb_membersize(google_firestore_v1beta1_ListenResponse, response_type.document_remove) < 256 && pb_membersize(google_firestore_v1beta1_Target, target_type.query) < 256 && pb_membersize(google_firestore_v1beta1_Target, target_type.documents) < 256 && pb_membersize(google_firestore_v1beta1_Target, read_time) < 256 && pb_membersize(google_firestore_v1beta1_Target_QueryTarget, query_type.structured_query) < 256 && pb_membersize(google_firestore_v1beta1_TargetChange, cause) < 256 && pb_membersize(google_firestore_v1beta1_TargetChange, read_time) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_google_firestore_v1beta1_GetDocumentRequest_google_firestore_v1beta1_ListDocumentsRequest_google_firestore_v1beta1_ListDocumentsResponse_google_firestore_v1beta1_CreateDocumentRequest_google_firestore_v1beta1_UpdateDocumentRequest_google_firestore_v1beta1_DeleteDocumentRequest_google_firestore_v1beta1_BatchGetDocumentsRequest_google_firestore_v1beta1_BatchGetDocumentsResponse_google_firestore_v1beta1_BeginTransactionRequest_google_firestore_v1beta1_BeginTransactionResponse_google_firestore_v1beta1_CommitRequest_google_firestore_v1beta1_CommitResponse_google_firestore_v1beta1_RollbackRequest_google_firestore_v1beta1_RunQueryRequest_google_firestore_v1beta1_RunQueryResponse_google_firestore_v1beta1_WriteRequest_google_firestore_v1beta1_WriteRequest_LabelsEntry_google_firestore_v1beta1_WriteResponse_google_firestore_v1beta1_ListenRequest_google_firestore_v1beta1_ListenRequest_LabelsEntry_google_firestore_v1beta1_ListenResponse_google_firestore_v1beta1_Target_google_firestore_v1beta1_Target_DocumentsTarget_google_firestore_v1beta1_Target_QueryTarget_google_firestore_v1beta1_TargetChange_google_firestore_v1beta1_ListCollectionIdsRequest_google_firestore_v1beta1_ListCollectionIdsResponse) +#endif + + +/* @@protoc_insertion_point(eof) */ diff --git a/Firestore/Protos/nanopb/google/firestore/v1beta1/firestore.pb.h b/Firestore/Protos/nanopb/google/firestore/v1beta1/firestore.pb.h new file mode 100644 index 00000000000..5bfbcf8602c --- /dev/null +++ b/Firestore/Protos/nanopb/google/firestore/v1beta1/firestore.pb.h @@ -0,0 +1,508 @@ +/* + * Copyright 2018 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. + */ + +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.3.8 at Fri Feb 2 17:48:02 2018. */ + +#ifndef PB_GOOGLE_FIRESTORE_V1BETA1_FIRESTORE_PB_H_INCLUDED +#define PB_GOOGLE_FIRESTORE_V1BETA1_FIRESTORE_PB_H_INCLUDED +#include + +#include "google/api/annotations.pb.h" + +#include "google/firestore/v1beta1/common.pb.h" + +#include "google/firestore/v1beta1/document.pb.h" + +#include "google/firestore/v1beta1/query.pb.h" + +#include "google/firestore/v1beta1/write.pb.h" + +#include "google/protobuf/empty.pb.h" + +#include "google/protobuf/timestamp.pb.h" + +#include "google/rpc/status.pb.h" + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Enum definitions */ +typedef enum _google_firestore_v1beta1_TargetChange_TargetChangeType { + google_firestore_v1beta1_TargetChange_TargetChangeType_NO_CHANGE = 0, + google_firestore_v1beta1_TargetChange_TargetChangeType_ADD = 1, + google_firestore_v1beta1_TargetChange_TargetChangeType_REMOVE = 2, + google_firestore_v1beta1_TargetChange_TargetChangeType_CURRENT = 3, + google_firestore_v1beta1_TargetChange_TargetChangeType_RESET = 4 +} google_firestore_v1beta1_TargetChange_TargetChangeType; +#define _google_firestore_v1beta1_TargetChange_TargetChangeType_MIN google_firestore_v1beta1_TargetChange_TargetChangeType_NO_CHANGE +#define _google_firestore_v1beta1_TargetChange_TargetChangeType_MAX google_firestore_v1beta1_TargetChange_TargetChangeType_RESET +#define _google_firestore_v1beta1_TargetChange_TargetChangeType_ARRAYSIZE ((google_firestore_v1beta1_TargetChange_TargetChangeType)(google_firestore_v1beta1_TargetChange_TargetChangeType_RESET+1)) + +/* Struct definitions */ +typedef struct _google_firestore_v1beta1_BeginTransactionResponse { + pb_callback_t transaction; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_BeginTransactionResponse) */ +} google_firestore_v1beta1_BeginTransactionResponse; + +typedef struct _google_firestore_v1beta1_CommitRequest { + pb_callback_t database; + pb_callback_t writes; + pb_callback_t transaction; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_CommitRequest) */ +} google_firestore_v1beta1_CommitRequest; + +typedef struct _google_firestore_v1beta1_ListCollectionIdsResponse { + pb_callback_t collection_ids; + pb_callback_t next_page_token; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_ListCollectionIdsResponse) */ +} google_firestore_v1beta1_ListCollectionIdsResponse; + +typedef struct _google_firestore_v1beta1_ListDocumentsResponse { + pb_callback_t documents; + pb_callback_t next_page_token; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_ListDocumentsResponse) */ +} google_firestore_v1beta1_ListDocumentsResponse; + +typedef struct _google_firestore_v1beta1_ListenRequest_LabelsEntry { + pb_callback_t key; + pb_callback_t value; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_ListenRequest_LabelsEntry) */ +} google_firestore_v1beta1_ListenRequest_LabelsEntry; + +typedef struct _google_firestore_v1beta1_RollbackRequest { + pb_callback_t database; + pb_callback_t transaction; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_RollbackRequest) */ +} google_firestore_v1beta1_RollbackRequest; + +typedef struct _google_firestore_v1beta1_Target_DocumentsTarget { + pb_callback_t documents; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_Target_DocumentsTarget) */ +} google_firestore_v1beta1_Target_DocumentsTarget; + +typedef struct _google_firestore_v1beta1_WriteRequest { + pb_callback_t database; + pb_callback_t stream_id; + pb_callback_t writes; + pb_callback_t stream_token; + pb_callback_t labels; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_WriteRequest) */ +} google_firestore_v1beta1_WriteRequest; + +typedef struct _google_firestore_v1beta1_WriteRequest_LabelsEntry { + pb_callback_t key; + pb_callback_t value; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_WriteRequest_LabelsEntry) */ +} google_firestore_v1beta1_WriteRequest_LabelsEntry; + +typedef struct _google_firestore_v1beta1_BatchGetDocumentsRequest { + pb_callback_t database; + pb_callback_t documents; + google_firestore_v1beta1_DocumentMask mask; + pb_callback_t transaction; + google_firestore_v1beta1_TransactionOptions new_transaction; + google_protobuf_Timestamp read_time; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_BatchGetDocumentsRequest) */ +} google_firestore_v1beta1_BatchGetDocumentsRequest; + +typedef struct _google_firestore_v1beta1_BatchGetDocumentsResponse { + google_firestore_v1beta1_Document found; + pb_callback_t missing; + pb_callback_t transaction; + google_protobuf_Timestamp read_time; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_BatchGetDocumentsResponse) */ +} google_firestore_v1beta1_BatchGetDocumentsResponse; + +typedef struct _google_firestore_v1beta1_BeginTransactionRequest { + pb_callback_t database; + google_firestore_v1beta1_TransactionOptions options; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_BeginTransactionRequest) */ +} google_firestore_v1beta1_BeginTransactionRequest; + +typedef struct _google_firestore_v1beta1_CommitResponse { + pb_callback_t write_results; + google_protobuf_Timestamp commit_time; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_CommitResponse) */ +} google_firestore_v1beta1_CommitResponse; + +typedef struct _google_firestore_v1beta1_CreateDocumentRequest { + pb_callback_t parent; + pb_callback_t collection_id; + pb_callback_t document_id; + google_firestore_v1beta1_Document document; + google_firestore_v1beta1_DocumentMask mask; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_CreateDocumentRequest) */ +} google_firestore_v1beta1_CreateDocumentRequest; + +typedef struct _google_firestore_v1beta1_DeleteDocumentRequest { + pb_callback_t name; + google_firestore_v1beta1_Precondition current_document; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_DeleteDocumentRequest) */ +} google_firestore_v1beta1_DeleteDocumentRequest; + +typedef struct _google_firestore_v1beta1_GetDocumentRequest { + pb_callback_t name; + google_firestore_v1beta1_DocumentMask mask; + pb_callback_t transaction; + google_protobuf_Timestamp read_time; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_GetDocumentRequest) */ +} google_firestore_v1beta1_GetDocumentRequest; + +typedef struct _google_firestore_v1beta1_ListCollectionIdsRequest { + pb_callback_t parent; + int32_t page_size; + pb_callback_t page_token; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_ListCollectionIdsRequest) */ +} google_firestore_v1beta1_ListCollectionIdsRequest; + +typedef struct _google_firestore_v1beta1_ListDocumentsRequest { + pb_callback_t parent; + pb_callback_t collection_id; + int32_t page_size; + pb_callback_t page_token; + pb_callback_t order_by; + google_firestore_v1beta1_DocumentMask mask; + pb_callback_t transaction; + google_protobuf_Timestamp read_time; + bool show_missing; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_ListDocumentsRequest) */ +} google_firestore_v1beta1_ListDocumentsRequest; + +typedef struct _google_firestore_v1beta1_RunQueryRequest { + pb_callback_t parent; + pb_size_t which_query_type; + union { + google_firestore_v1beta1_StructuredQuery structured_query; + } query_type; + pb_callback_t transaction; + google_firestore_v1beta1_TransactionOptions new_transaction; + google_protobuf_Timestamp read_time; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_RunQueryRequest) */ +} google_firestore_v1beta1_RunQueryRequest; + +typedef struct _google_firestore_v1beta1_RunQueryResponse { + google_firestore_v1beta1_Document document; + pb_callback_t transaction; + google_protobuf_Timestamp read_time; + int32_t skipped_results; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_RunQueryResponse) */ +} google_firestore_v1beta1_RunQueryResponse; + +typedef struct _google_firestore_v1beta1_TargetChange { + google_firestore_v1beta1_TargetChange_TargetChangeType target_change_type; + pb_callback_t target_ids; + google_rpc_Status cause; + pb_callback_t resume_token; + google_protobuf_Timestamp read_time; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_TargetChange) */ +} google_firestore_v1beta1_TargetChange; + +typedef struct _google_firestore_v1beta1_Target_QueryTarget { + pb_callback_t parent; + pb_size_t which_query_type; + union { + google_firestore_v1beta1_StructuredQuery structured_query; + } query_type; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_Target_QueryTarget) */ +} google_firestore_v1beta1_Target_QueryTarget; + +typedef struct _google_firestore_v1beta1_UpdateDocumentRequest { + google_firestore_v1beta1_Document document; + google_firestore_v1beta1_DocumentMask update_mask; + google_firestore_v1beta1_DocumentMask mask; + google_firestore_v1beta1_Precondition current_document; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_UpdateDocumentRequest) */ +} google_firestore_v1beta1_UpdateDocumentRequest; + +typedef struct _google_firestore_v1beta1_WriteResponse { + pb_callback_t stream_id; + pb_callback_t stream_token; + pb_callback_t write_results; + google_protobuf_Timestamp commit_time; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_WriteResponse) */ +} google_firestore_v1beta1_WriteResponse; + +typedef struct _google_firestore_v1beta1_ListenResponse { + pb_size_t which_response_type; + union { + google_firestore_v1beta1_TargetChange target_change; + google_firestore_v1beta1_DocumentChange document_change; + google_firestore_v1beta1_DocumentDelete document_delete; + google_firestore_v1beta1_ExistenceFilter filter; + google_firestore_v1beta1_DocumentRemove document_remove; + } response_type; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_ListenResponse) */ +} google_firestore_v1beta1_ListenResponse; + +typedef struct _google_firestore_v1beta1_Target { + pb_size_t which_target_type; + union { + google_firestore_v1beta1_Target_QueryTarget query; + google_firestore_v1beta1_Target_DocumentsTarget documents; + } target_type; + pb_callback_t resume_token; + int32_t target_id; + bool once; + google_protobuf_Timestamp read_time; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_Target) */ +} google_firestore_v1beta1_Target; + +typedef struct _google_firestore_v1beta1_ListenRequest { + pb_callback_t database; + pb_size_t which_target_change; + union { + google_firestore_v1beta1_Target add_target; + int32_t remove_target; + } target_change; + pb_callback_t labels; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_ListenRequest) */ +} google_firestore_v1beta1_ListenRequest; + +/* Default values for struct fields */ + +/* Initializer values for message structs */ +#define google_firestore_v1beta1_GetDocumentRequest_init_default {{{NULL}, NULL}, google_firestore_v1beta1_DocumentMask_init_default, {{NULL}, NULL}, google_protobuf_Timestamp_init_default} +#define google_firestore_v1beta1_ListDocumentsRequest_init_default {{{NULL}, NULL}, {{NULL}, NULL}, 0, {{NULL}, NULL}, {{NULL}, NULL}, google_firestore_v1beta1_DocumentMask_init_default, {{NULL}, NULL}, google_protobuf_Timestamp_init_default, 0} +#define google_firestore_v1beta1_ListDocumentsResponse_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define google_firestore_v1beta1_CreateDocumentRequest_init_default {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, google_firestore_v1beta1_Document_init_default, google_firestore_v1beta1_DocumentMask_init_default} +#define google_firestore_v1beta1_UpdateDocumentRequest_init_default {google_firestore_v1beta1_Document_init_default, google_firestore_v1beta1_DocumentMask_init_default, google_firestore_v1beta1_DocumentMask_init_default, google_firestore_v1beta1_Precondition_init_default} +#define google_firestore_v1beta1_DeleteDocumentRequest_init_default {{{NULL}, NULL}, google_firestore_v1beta1_Precondition_init_default} +#define google_firestore_v1beta1_BatchGetDocumentsRequest_init_default {{{NULL}, NULL}, {{NULL}, NULL}, google_firestore_v1beta1_DocumentMask_init_default, {{NULL}, NULL}, google_firestore_v1beta1_TransactionOptions_init_default, google_protobuf_Timestamp_init_default} +#define google_firestore_v1beta1_BatchGetDocumentsResponse_init_default {google_firestore_v1beta1_Document_init_default, {{NULL}, NULL}, {{NULL}, NULL}, google_protobuf_Timestamp_init_default} +#define google_firestore_v1beta1_BeginTransactionRequest_init_default {{{NULL}, NULL}, google_firestore_v1beta1_TransactionOptions_init_default} +#define google_firestore_v1beta1_BeginTransactionResponse_init_default {{{NULL}, NULL}} +#define google_firestore_v1beta1_CommitRequest_init_default {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}} +#define google_firestore_v1beta1_CommitResponse_init_default {{{NULL}, NULL}, google_protobuf_Timestamp_init_default} +#define google_firestore_v1beta1_RollbackRequest_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define google_firestore_v1beta1_RunQueryRequest_init_default {{{NULL}, NULL}, 0, {google_firestore_v1beta1_StructuredQuery_init_default}, {{NULL}, NULL}, google_firestore_v1beta1_TransactionOptions_init_default, google_protobuf_Timestamp_init_default} +#define google_firestore_v1beta1_RunQueryResponse_init_default {google_firestore_v1beta1_Document_init_default, {{NULL}, NULL}, google_protobuf_Timestamp_init_default, 0} +#define google_firestore_v1beta1_WriteRequest_init_default {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}} +#define google_firestore_v1beta1_WriteRequest_LabelsEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define google_firestore_v1beta1_WriteResponse_init_default {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, google_protobuf_Timestamp_init_default} +#define google_firestore_v1beta1_ListenRequest_init_default {{{NULL}, NULL}, 0, {google_firestore_v1beta1_Target_init_default}, {{NULL}, NULL}} +#define google_firestore_v1beta1_ListenRequest_LabelsEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define google_firestore_v1beta1_ListenResponse_init_default {0, {google_firestore_v1beta1_TargetChange_init_default}} +#define google_firestore_v1beta1_Target_init_default {0, {google_firestore_v1beta1_Target_QueryTarget_init_default}, {{NULL}, NULL}, 0, 0, google_protobuf_Timestamp_init_default} +#define google_firestore_v1beta1_Target_DocumentsTarget_init_default {{{NULL}, NULL}} +#define google_firestore_v1beta1_Target_QueryTarget_init_default {{{NULL}, NULL}, 0, {google_firestore_v1beta1_StructuredQuery_init_default}} +#define google_firestore_v1beta1_TargetChange_init_default {(google_firestore_v1beta1_TargetChange_TargetChangeType)0, {{NULL}, NULL}, google_rpc_Status_init_default, {{NULL}, NULL}, google_protobuf_Timestamp_init_default} +#define google_firestore_v1beta1_ListCollectionIdsRequest_init_default {{{NULL}, NULL}, 0, {{NULL}, NULL}} +#define google_firestore_v1beta1_ListCollectionIdsResponse_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define google_firestore_v1beta1_GetDocumentRequest_init_zero {{{NULL}, NULL}, google_firestore_v1beta1_DocumentMask_init_zero, {{NULL}, NULL}, google_protobuf_Timestamp_init_zero} +#define google_firestore_v1beta1_ListDocumentsRequest_init_zero {{{NULL}, NULL}, {{NULL}, NULL}, 0, {{NULL}, NULL}, {{NULL}, NULL}, google_firestore_v1beta1_DocumentMask_init_zero, {{NULL}, NULL}, google_protobuf_Timestamp_init_zero, 0} +#define google_firestore_v1beta1_ListDocumentsResponse_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define google_firestore_v1beta1_CreateDocumentRequest_init_zero {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, google_firestore_v1beta1_Document_init_zero, google_firestore_v1beta1_DocumentMask_init_zero} +#define google_firestore_v1beta1_UpdateDocumentRequest_init_zero {google_firestore_v1beta1_Document_init_zero, google_firestore_v1beta1_DocumentMask_init_zero, google_firestore_v1beta1_DocumentMask_init_zero, google_firestore_v1beta1_Precondition_init_zero} +#define google_firestore_v1beta1_DeleteDocumentRequest_init_zero {{{NULL}, NULL}, google_firestore_v1beta1_Precondition_init_zero} +#define google_firestore_v1beta1_BatchGetDocumentsRequest_init_zero {{{NULL}, NULL}, {{NULL}, NULL}, google_firestore_v1beta1_DocumentMask_init_zero, {{NULL}, NULL}, google_firestore_v1beta1_TransactionOptions_init_zero, google_protobuf_Timestamp_init_zero} +#define google_firestore_v1beta1_BatchGetDocumentsResponse_init_zero {google_firestore_v1beta1_Document_init_zero, {{NULL}, NULL}, {{NULL}, NULL}, google_protobuf_Timestamp_init_zero} +#define google_firestore_v1beta1_BeginTransactionRequest_init_zero {{{NULL}, NULL}, google_firestore_v1beta1_TransactionOptions_init_zero} +#define google_firestore_v1beta1_BeginTransactionResponse_init_zero {{{NULL}, NULL}} +#define google_firestore_v1beta1_CommitRequest_init_zero {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}} +#define google_firestore_v1beta1_CommitResponse_init_zero {{{NULL}, NULL}, google_protobuf_Timestamp_init_zero} +#define google_firestore_v1beta1_RollbackRequest_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define google_firestore_v1beta1_RunQueryRequest_init_zero {{{NULL}, NULL}, 0, {google_firestore_v1beta1_StructuredQuery_init_zero}, {{NULL}, NULL}, google_firestore_v1beta1_TransactionOptions_init_zero, google_protobuf_Timestamp_init_zero} +#define google_firestore_v1beta1_RunQueryResponse_init_zero {google_firestore_v1beta1_Document_init_zero, {{NULL}, NULL}, google_protobuf_Timestamp_init_zero, 0} +#define google_firestore_v1beta1_WriteRequest_init_zero {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}} +#define google_firestore_v1beta1_WriteRequest_LabelsEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define google_firestore_v1beta1_WriteResponse_init_zero {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, google_protobuf_Timestamp_init_zero} +#define google_firestore_v1beta1_ListenRequest_init_zero {{{NULL}, NULL}, 0, {google_firestore_v1beta1_Target_init_zero}, {{NULL}, NULL}} +#define google_firestore_v1beta1_ListenRequest_LabelsEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define google_firestore_v1beta1_ListenResponse_init_zero {0, {google_firestore_v1beta1_TargetChange_init_zero}} +#define google_firestore_v1beta1_Target_init_zero {0, {google_firestore_v1beta1_Target_QueryTarget_init_zero}, {{NULL}, NULL}, 0, 0, google_protobuf_Timestamp_init_zero} +#define google_firestore_v1beta1_Target_DocumentsTarget_init_zero {{{NULL}, NULL}} +#define google_firestore_v1beta1_Target_QueryTarget_init_zero {{{NULL}, NULL}, 0, {google_firestore_v1beta1_StructuredQuery_init_zero}} +#define google_firestore_v1beta1_TargetChange_init_zero {(google_firestore_v1beta1_TargetChange_TargetChangeType)0, {{NULL}, NULL}, google_rpc_Status_init_zero, {{NULL}, NULL}, google_protobuf_Timestamp_init_zero} +#define google_firestore_v1beta1_ListCollectionIdsRequest_init_zero {{{NULL}, NULL}, 0, {{NULL}, NULL}} +#define google_firestore_v1beta1_ListCollectionIdsResponse_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} + +/* Field tags (for use in manual encoding/decoding) */ +#define google_firestore_v1beta1_BeginTransactionResponse_transaction_tag 1 +#define google_firestore_v1beta1_CommitRequest_database_tag 1 +#define google_firestore_v1beta1_CommitRequest_writes_tag 2 +#define google_firestore_v1beta1_CommitRequest_transaction_tag 3 +#define google_firestore_v1beta1_ListCollectionIdsResponse_collection_ids_tag 1 +#define google_firestore_v1beta1_ListCollectionIdsResponse_next_page_token_tag 2 +#define google_firestore_v1beta1_ListDocumentsResponse_documents_tag 1 +#define google_firestore_v1beta1_ListDocumentsResponse_next_page_token_tag 2 +#define google_firestore_v1beta1_ListenRequest_LabelsEntry_key_tag 1 +#define google_firestore_v1beta1_ListenRequest_LabelsEntry_value_tag 2 +#define google_firestore_v1beta1_RollbackRequest_database_tag 1 +#define google_firestore_v1beta1_RollbackRequest_transaction_tag 2 +#define google_firestore_v1beta1_Target_DocumentsTarget_documents_tag 2 +#define google_firestore_v1beta1_WriteRequest_database_tag 1 +#define google_firestore_v1beta1_WriteRequest_stream_id_tag 2 +#define google_firestore_v1beta1_WriteRequest_writes_tag 3 +#define google_firestore_v1beta1_WriteRequest_stream_token_tag 4 +#define google_firestore_v1beta1_WriteRequest_labels_tag 5 +#define google_firestore_v1beta1_WriteRequest_LabelsEntry_key_tag 1 +#define google_firestore_v1beta1_WriteRequest_LabelsEntry_value_tag 2 +#define google_firestore_v1beta1_BatchGetDocumentsRequest_database_tag 1 +#define google_firestore_v1beta1_BatchGetDocumentsRequest_documents_tag 2 +#define google_firestore_v1beta1_BatchGetDocumentsRequest_mask_tag 3 +#define google_firestore_v1beta1_BatchGetDocumentsRequest_transaction_tag 4 +#define google_firestore_v1beta1_BatchGetDocumentsRequest_new_transaction_tag 5 +#define google_firestore_v1beta1_BatchGetDocumentsRequest_read_time_tag 7 +#define google_firestore_v1beta1_BatchGetDocumentsResponse_found_tag 1 +#define google_firestore_v1beta1_BatchGetDocumentsResponse_missing_tag 2 +#define google_firestore_v1beta1_BatchGetDocumentsResponse_transaction_tag 3 +#define google_firestore_v1beta1_BatchGetDocumentsResponse_read_time_tag 4 +#define google_firestore_v1beta1_BeginTransactionRequest_database_tag 1 +#define google_firestore_v1beta1_BeginTransactionRequest_options_tag 2 +#define google_firestore_v1beta1_CommitResponse_write_results_tag 1 +#define google_firestore_v1beta1_CommitResponse_commit_time_tag 2 +#define google_firestore_v1beta1_CreateDocumentRequest_parent_tag 1 +#define google_firestore_v1beta1_CreateDocumentRequest_collection_id_tag 2 +#define google_firestore_v1beta1_CreateDocumentRequest_document_id_tag 3 +#define google_firestore_v1beta1_CreateDocumentRequest_document_tag 4 +#define google_firestore_v1beta1_CreateDocumentRequest_mask_tag 5 +#define google_firestore_v1beta1_DeleteDocumentRequest_name_tag 1 +#define google_firestore_v1beta1_DeleteDocumentRequest_current_document_tag 2 +#define google_firestore_v1beta1_GetDocumentRequest_name_tag 1 +#define google_firestore_v1beta1_GetDocumentRequest_mask_tag 2 +#define google_firestore_v1beta1_GetDocumentRequest_transaction_tag 3 +#define google_firestore_v1beta1_GetDocumentRequest_read_time_tag 5 +#define google_firestore_v1beta1_ListCollectionIdsRequest_parent_tag 1 +#define google_firestore_v1beta1_ListCollectionIdsRequest_page_size_tag 2 +#define google_firestore_v1beta1_ListCollectionIdsRequest_page_token_tag 3 +#define google_firestore_v1beta1_ListDocumentsRequest_parent_tag 1 +#define google_firestore_v1beta1_ListDocumentsRequest_collection_id_tag 2 +#define google_firestore_v1beta1_ListDocumentsRequest_page_size_tag 3 +#define google_firestore_v1beta1_ListDocumentsRequest_page_token_tag 4 +#define google_firestore_v1beta1_ListDocumentsRequest_order_by_tag 6 +#define google_firestore_v1beta1_ListDocumentsRequest_mask_tag 7 +#define google_firestore_v1beta1_ListDocumentsRequest_transaction_tag 8 +#define google_firestore_v1beta1_ListDocumentsRequest_read_time_tag 10 +#define google_firestore_v1beta1_ListDocumentsRequest_show_missing_tag 12 +#define google_firestore_v1beta1_RunQueryRequest_structured_query_tag 2 +#define google_firestore_v1beta1_RunQueryRequest_parent_tag 1 +#define google_firestore_v1beta1_RunQueryRequest_transaction_tag 5 +#define google_firestore_v1beta1_RunQueryRequest_new_transaction_tag 6 +#define google_firestore_v1beta1_RunQueryRequest_read_time_tag 7 +#define google_firestore_v1beta1_RunQueryResponse_transaction_tag 2 +#define google_firestore_v1beta1_RunQueryResponse_document_tag 1 +#define google_firestore_v1beta1_RunQueryResponse_read_time_tag 3 +#define google_firestore_v1beta1_RunQueryResponse_skipped_results_tag 4 +#define google_firestore_v1beta1_TargetChange_target_change_type_tag 1 +#define google_firestore_v1beta1_TargetChange_target_ids_tag 2 +#define google_firestore_v1beta1_TargetChange_cause_tag 3 +#define google_firestore_v1beta1_TargetChange_resume_token_tag 4 +#define google_firestore_v1beta1_TargetChange_read_time_tag 6 +#define google_firestore_v1beta1_Target_QueryTarget_structured_query_tag 2 +#define google_firestore_v1beta1_Target_QueryTarget_parent_tag 1 +#define google_firestore_v1beta1_UpdateDocumentRequest_document_tag 1 +#define google_firestore_v1beta1_UpdateDocumentRequest_update_mask_tag 2 +#define google_firestore_v1beta1_UpdateDocumentRequest_mask_tag 3 +#define google_firestore_v1beta1_UpdateDocumentRequest_current_document_tag 4 +#define google_firestore_v1beta1_WriteResponse_stream_id_tag 1 +#define google_firestore_v1beta1_WriteResponse_stream_token_tag 2 +#define google_firestore_v1beta1_WriteResponse_write_results_tag 3 +#define google_firestore_v1beta1_WriteResponse_commit_time_tag 4 +#define google_firestore_v1beta1_ListenResponse_target_change_tag 2 +#define google_firestore_v1beta1_ListenResponse_document_change_tag 3 +#define google_firestore_v1beta1_ListenResponse_document_delete_tag 4 +#define google_firestore_v1beta1_ListenResponse_filter_tag 5 +#define google_firestore_v1beta1_ListenResponse_document_remove_tag 6 +#define google_firestore_v1beta1_Target_query_tag 2 +#define google_firestore_v1beta1_Target_documents_tag 3 +#define google_firestore_v1beta1_Target_resume_token_tag 4 +#define google_firestore_v1beta1_Target_read_time_tag 11 +#define google_firestore_v1beta1_Target_target_id_tag 5 +#define google_firestore_v1beta1_Target_once_tag 6 +#define google_firestore_v1beta1_ListenRequest_add_target_tag 2 +#define google_firestore_v1beta1_ListenRequest_remove_target_tag 3 +#define google_firestore_v1beta1_ListenRequest_database_tag 1 +#define google_firestore_v1beta1_ListenRequest_labels_tag 4 + +/* Struct field encoding specification for nanopb */ +extern const pb_field_t google_firestore_v1beta1_GetDocumentRequest_fields[5]; +extern const pb_field_t google_firestore_v1beta1_ListDocumentsRequest_fields[10]; +extern const pb_field_t google_firestore_v1beta1_ListDocumentsResponse_fields[3]; +extern const pb_field_t google_firestore_v1beta1_CreateDocumentRequest_fields[6]; +extern const pb_field_t google_firestore_v1beta1_UpdateDocumentRequest_fields[5]; +extern const pb_field_t google_firestore_v1beta1_DeleteDocumentRequest_fields[3]; +extern const pb_field_t google_firestore_v1beta1_BatchGetDocumentsRequest_fields[7]; +extern const pb_field_t google_firestore_v1beta1_BatchGetDocumentsResponse_fields[5]; +extern const pb_field_t google_firestore_v1beta1_BeginTransactionRequest_fields[3]; +extern const pb_field_t google_firestore_v1beta1_BeginTransactionResponse_fields[2]; +extern const pb_field_t google_firestore_v1beta1_CommitRequest_fields[4]; +extern const pb_field_t google_firestore_v1beta1_CommitResponse_fields[3]; +extern const pb_field_t google_firestore_v1beta1_RollbackRequest_fields[3]; +extern const pb_field_t google_firestore_v1beta1_RunQueryRequest_fields[6]; +extern const pb_field_t google_firestore_v1beta1_RunQueryResponse_fields[5]; +extern const pb_field_t google_firestore_v1beta1_WriteRequest_fields[6]; +extern const pb_field_t google_firestore_v1beta1_WriteRequest_LabelsEntry_fields[3]; +extern const pb_field_t google_firestore_v1beta1_WriteResponse_fields[5]; +extern const pb_field_t google_firestore_v1beta1_ListenRequest_fields[5]; +extern const pb_field_t google_firestore_v1beta1_ListenRequest_LabelsEntry_fields[3]; +extern const pb_field_t google_firestore_v1beta1_ListenResponse_fields[6]; +extern const pb_field_t google_firestore_v1beta1_Target_fields[7]; +extern const pb_field_t google_firestore_v1beta1_Target_DocumentsTarget_fields[2]; +extern const pb_field_t google_firestore_v1beta1_Target_QueryTarget_fields[3]; +extern const pb_field_t google_firestore_v1beta1_TargetChange_fields[6]; +extern const pb_field_t google_firestore_v1beta1_ListCollectionIdsRequest_fields[4]; +extern const pb_field_t google_firestore_v1beta1_ListCollectionIdsResponse_fields[3]; + +/* Maximum encoded size of messages (where known) */ +/* google_firestore_v1beta1_GetDocumentRequest_size depends on runtime parameters */ +/* google_firestore_v1beta1_ListDocumentsRequest_size depends on runtime parameters */ +/* google_firestore_v1beta1_ListDocumentsResponse_size depends on runtime parameters */ +/* google_firestore_v1beta1_CreateDocumentRequest_size depends on runtime parameters */ +#define google_firestore_v1beta1_UpdateDocumentRequest_size (44 + google_firestore_v1beta1_Document_size + google_firestore_v1beta1_DocumentMask_size + google_firestore_v1beta1_DocumentMask_size) +/* google_firestore_v1beta1_DeleteDocumentRequest_size depends on runtime parameters */ +/* google_firestore_v1beta1_BatchGetDocumentsRequest_size depends on runtime parameters */ +/* google_firestore_v1beta1_BatchGetDocumentsResponse_size depends on runtime parameters */ +/* google_firestore_v1beta1_BeginTransactionRequest_size depends on runtime parameters */ +/* google_firestore_v1beta1_BeginTransactionResponse_size depends on runtime parameters */ +/* google_firestore_v1beta1_CommitRequest_size depends on runtime parameters */ +/* google_firestore_v1beta1_CommitResponse_size depends on runtime parameters */ +/* google_firestore_v1beta1_RollbackRequest_size depends on runtime parameters */ +/* google_firestore_v1beta1_RunQueryRequest_size depends on runtime parameters */ +/* google_firestore_v1beta1_RunQueryResponse_size depends on runtime parameters */ +/* google_firestore_v1beta1_WriteRequest_size depends on runtime parameters */ +/* google_firestore_v1beta1_WriteRequest_LabelsEntry_size depends on runtime parameters */ +/* google_firestore_v1beta1_WriteResponse_size depends on runtime parameters */ +/* google_firestore_v1beta1_ListenRequest_size depends on runtime parameters */ +/* google_firestore_v1beta1_ListenRequest_LabelsEntry_size depends on runtime parameters */ +/* google_firestore_v1beta1_ListenResponse_size depends on runtime parameters */ +/* google_firestore_v1beta1_Target_size depends on runtime parameters */ +/* google_firestore_v1beta1_Target_DocumentsTarget_size depends on runtime parameters */ +/* google_firestore_v1beta1_Target_QueryTarget_size depends on runtime parameters */ +/* google_firestore_v1beta1_TargetChange_size depends on runtime parameters */ +/* google_firestore_v1beta1_ListCollectionIdsRequest_size depends on runtime parameters */ +/* google_firestore_v1beta1_ListCollectionIdsResponse_size depends on runtime parameters */ + +/* Message IDs (where set with "msgid" option) */ +#ifdef PB_MSGID + +#define FIRESTORE_MESSAGES \ + + +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif +/* @@protoc_insertion_point(eof) */ + +#endif diff --git a/Firestore/Protos/nanopb/google/firestore/v1beta1/query.pb.c b/Firestore/Protos/nanopb/google/firestore/v1beta1/query.pb.c new file mode 100644 index 00000000000..4e68490dda9 --- /dev/null +++ b/Firestore/Protos/nanopb/google/firestore/v1beta1/query.pb.c @@ -0,0 +1,124 @@ +/* + * Copyright 2018 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. + */ + +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.3.8 at Fri Feb 2 17:48:02 2018. */ + +#include "query.pb.h" + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + + + +const pb_field_t google_firestore_v1beta1_StructuredQuery_fields[9] = { + PB_FIELD( 1, MESSAGE , SINGULAR, STATIC , FIRST, google_firestore_v1beta1_StructuredQuery, select, select, &google_firestore_v1beta1_StructuredQuery_Projection_fields), + PB_FIELD( 2, MESSAGE , REPEATED, CALLBACK, OTHER, google_firestore_v1beta1_StructuredQuery, from, select, &google_firestore_v1beta1_StructuredQuery_CollectionSelector_fields), + PB_FIELD( 3, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_StructuredQuery, where, from, &google_firestore_v1beta1_StructuredQuery_Filter_fields), + PB_FIELD( 4, MESSAGE , REPEATED, CALLBACK, OTHER, google_firestore_v1beta1_StructuredQuery, order_by, where, &google_firestore_v1beta1_StructuredQuery_Order_fields), + PB_FIELD( 5, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_StructuredQuery, limit, order_by, &google_protobuf_Int32Value_fields), + PB_FIELD( 6, INT32 , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_StructuredQuery, offset, limit, 0), + PB_FIELD( 7, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_StructuredQuery, start_at, offset, &google_firestore_v1beta1_Cursor_fields), + PB_FIELD( 8, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_StructuredQuery, end_at, start_at, &google_firestore_v1beta1_Cursor_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_StructuredQuery_CollectionSelector_fields[3] = { + PB_FIELD( 2, STRING , SINGULAR, CALLBACK, FIRST, google_firestore_v1beta1_StructuredQuery_CollectionSelector, collection_id, collection_id, 0), + PB_FIELD( 3, BOOL , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_StructuredQuery_CollectionSelector, all_descendants, collection_id, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_StructuredQuery_Filter_fields[4] = { + PB_ONEOF_FIELD(filter_type, 1, MESSAGE , ONEOF, STATIC , FIRST, google_firestore_v1beta1_StructuredQuery_Filter, composite_filter, composite_filter, &google_firestore_v1beta1_StructuredQuery_CompositeFilter_fields), + PB_ONEOF_FIELD(filter_type, 2, MESSAGE , ONEOF, STATIC , UNION, google_firestore_v1beta1_StructuredQuery_Filter, field_filter, field_filter, &google_firestore_v1beta1_StructuredQuery_FieldFilter_fields), + PB_ONEOF_FIELD(filter_type, 3, MESSAGE , ONEOF, STATIC , UNION, google_firestore_v1beta1_StructuredQuery_Filter, unary_filter, unary_filter, &google_firestore_v1beta1_StructuredQuery_UnaryFilter_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_StructuredQuery_CompositeFilter_fields[3] = { + PB_FIELD( 1, UENUM , SINGULAR, STATIC , FIRST, google_firestore_v1beta1_StructuredQuery_CompositeFilter, op, op, 0), + PB_FIELD( 2, MESSAGE , REPEATED, CALLBACK, OTHER, google_firestore_v1beta1_StructuredQuery_CompositeFilter, filters, op, &google_firestore_v1beta1_StructuredQuery_Filter_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_StructuredQuery_FieldFilter_fields[4] = { + PB_FIELD( 1, MESSAGE , SINGULAR, STATIC , FIRST, google_firestore_v1beta1_StructuredQuery_FieldFilter, field, field, &google_firestore_v1beta1_StructuredQuery_FieldReference_fields), + PB_FIELD( 2, UENUM , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_StructuredQuery_FieldFilter, op, field, 0), + PB_FIELD( 3, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_StructuredQuery_FieldFilter, value, op, &google_firestore_v1beta1_Value_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_StructuredQuery_UnaryFilter_fields[3] = { + PB_FIELD( 1, UENUM , SINGULAR, STATIC , FIRST, google_firestore_v1beta1_StructuredQuery_UnaryFilter, op, op, 0), + PB_ONEOF_FIELD(operand_type, 2, MESSAGE , ONEOF, STATIC , OTHER, google_firestore_v1beta1_StructuredQuery_UnaryFilter, field, op, &google_firestore_v1beta1_StructuredQuery_FieldReference_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_StructuredQuery_Order_fields[3] = { + PB_FIELD( 1, MESSAGE , SINGULAR, STATIC , FIRST, google_firestore_v1beta1_StructuredQuery_Order, field, field, &google_firestore_v1beta1_StructuredQuery_FieldReference_fields), + PB_FIELD( 2, UENUM , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_StructuredQuery_Order, direction, field, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_StructuredQuery_FieldReference_fields[2] = { + PB_FIELD( 2, STRING , SINGULAR, CALLBACK, FIRST, google_firestore_v1beta1_StructuredQuery_FieldReference, field_path, field_path, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_StructuredQuery_Projection_fields[2] = { + PB_FIELD( 2, MESSAGE , REPEATED, CALLBACK, FIRST, google_firestore_v1beta1_StructuredQuery_Projection, fields, fields, &google_firestore_v1beta1_StructuredQuery_FieldReference_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_Cursor_fields[3] = { + PB_FIELD( 1, MESSAGE , REPEATED, CALLBACK, FIRST, google_firestore_v1beta1_Cursor, values, values, &google_firestore_v1beta1_Value_fields), + PB_FIELD( 2, BOOL , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_Cursor, before, values, 0), + PB_LAST_FIELD +}; + + + + + + +/* Check that field information fits in pb_field_t */ +#if !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_32BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in 8 or 16 bit + * field descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(google_firestore_v1beta1_StructuredQuery, select) < 65536 && pb_membersize(google_firestore_v1beta1_StructuredQuery, where) < 65536 && pb_membersize(google_firestore_v1beta1_StructuredQuery, start_at) < 65536 && pb_membersize(google_firestore_v1beta1_StructuredQuery, end_at) < 65536 && pb_membersize(google_firestore_v1beta1_StructuredQuery, limit) < 65536 && pb_membersize(google_firestore_v1beta1_StructuredQuery_Filter, filter_type.composite_filter) < 65536 && pb_membersize(google_firestore_v1beta1_StructuredQuery_Filter, filter_type.field_filter) < 65536 && pb_membersize(google_firestore_v1beta1_StructuredQuery_Filter, filter_type.unary_filter) < 65536 && pb_membersize(google_firestore_v1beta1_StructuredQuery_FieldFilter, field) < 65536 && pb_membersize(google_firestore_v1beta1_StructuredQuery_FieldFilter, value) < 65536 && pb_membersize(google_firestore_v1beta1_StructuredQuery_UnaryFilter, operand_type.field) < 65536 && pb_membersize(google_firestore_v1beta1_StructuredQuery_Order, field) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_google_firestore_v1beta1_StructuredQuery_google_firestore_v1beta1_StructuredQuery_CollectionSelector_google_firestore_v1beta1_StructuredQuery_Filter_google_firestore_v1beta1_StructuredQuery_CompositeFilter_google_firestore_v1beta1_StructuredQuery_FieldFilter_google_firestore_v1beta1_StructuredQuery_UnaryFilter_google_firestore_v1beta1_StructuredQuery_Order_google_firestore_v1beta1_StructuredQuery_FieldReference_google_firestore_v1beta1_StructuredQuery_Projection_google_firestore_v1beta1_Cursor) +#endif + +#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_16BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in the default + * 8 bit descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(google_firestore_v1beta1_StructuredQuery, select) < 256 && pb_membersize(google_firestore_v1beta1_StructuredQuery, where) < 256 && pb_membersize(google_firestore_v1beta1_StructuredQuery, start_at) < 256 && pb_membersize(google_firestore_v1beta1_StructuredQuery, end_at) < 256 && pb_membersize(google_firestore_v1beta1_StructuredQuery, limit) < 256 && pb_membersize(google_firestore_v1beta1_StructuredQuery_Filter, filter_type.composite_filter) < 256 && pb_membersize(google_firestore_v1beta1_StructuredQuery_Filter, filter_type.field_filter) < 256 && pb_membersize(google_firestore_v1beta1_StructuredQuery_Filter, filter_type.unary_filter) < 256 && pb_membersize(google_firestore_v1beta1_StructuredQuery_FieldFilter, field) < 256 && pb_membersize(google_firestore_v1beta1_StructuredQuery_FieldFilter, value) < 256 && pb_membersize(google_firestore_v1beta1_StructuredQuery_UnaryFilter, operand_type.field) < 256 && pb_membersize(google_firestore_v1beta1_StructuredQuery_Order, field) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_google_firestore_v1beta1_StructuredQuery_google_firestore_v1beta1_StructuredQuery_CollectionSelector_google_firestore_v1beta1_StructuredQuery_Filter_google_firestore_v1beta1_StructuredQuery_CompositeFilter_google_firestore_v1beta1_StructuredQuery_FieldFilter_google_firestore_v1beta1_StructuredQuery_UnaryFilter_google_firestore_v1beta1_StructuredQuery_Order_google_firestore_v1beta1_StructuredQuery_FieldReference_google_firestore_v1beta1_StructuredQuery_Projection_google_firestore_v1beta1_Cursor) +#endif + + +/* @@protoc_insertion_point(eof) */ diff --git a/Firestore/Protos/nanopb/google/firestore/v1beta1/query.pb.h b/Firestore/Protos/nanopb/google/firestore/v1beta1/query.pb.h new file mode 100644 index 00000000000..faad2a206ab --- /dev/null +++ b/Firestore/Protos/nanopb/google/firestore/v1beta1/query.pb.h @@ -0,0 +1,240 @@ +/* + * Copyright 2018 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. + */ + +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.3.8 at Fri Feb 2 17:48:02 2018. */ + +#ifndef PB_GOOGLE_FIRESTORE_V1BETA1_QUERY_PB_H_INCLUDED +#define PB_GOOGLE_FIRESTORE_V1BETA1_QUERY_PB_H_INCLUDED +#include + +#include "google/api/annotations.pb.h" + +#include "google/firestore/v1beta1/document.pb.h" + +#include "google/protobuf/wrappers.pb.h" + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Enum definitions */ +typedef enum _google_firestore_v1beta1_StructuredQuery_Direction { + google_firestore_v1beta1_StructuredQuery_Direction_DIRECTION_UNSPECIFIED = 0, + google_firestore_v1beta1_StructuredQuery_Direction_ASCENDING = 1, + google_firestore_v1beta1_StructuredQuery_Direction_DESCENDING = 2 +} google_firestore_v1beta1_StructuredQuery_Direction; +#define _google_firestore_v1beta1_StructuredQuery_Direction_MIN google_firestore_v1beta1_StructuredQuery_Direction_DIRECTION_UNSPECIFIED +#define _google_firestore_v1beta1_StructuredQuery_Direction_MAX google_firestore_v1beta1_StructuredQuery_Direction_DESCENDING +#define _google_firestore_v1beta1_StructuredQuery_Direction_ARRAYSIZE ((google_firestore_v1beta1_StructuredQuery_Direction)(google_firestore_v1beta1_StructuredQuery_Direction_DESCENDING+1)) + +typedef enum _google_firestore_v1beta1_StructuredQuery_CompositeFilter_Operator { + google_firestore_v1beta1_StructuredQuery_CompositeFilter_Operator_OPERATOR_UNSPECIFIED = 0, + google_firestore_v1beta1_StructuredQuery_CompositeFilter_Operator_AND = 1 +} google_firestore_v1beta1_StructuredQuery_CompositeFilter_Operator; +#define _google_firestore_v1beta1_StructuredQuery_CompositeFilter_Operator_MIN google_firestore_v1beta1_StructuredQuery_CompositeFilter_Operator_OPERATOR_UNSPECIFIED +#define _google_firestore_v1beta1_StructuredQuery_CompositeFilter_Operator_MAX google_firestore_v1beta1_StructuredQuery_CompositeFilter_Operator_AND +#define _google_firestore_v1beta1_StructuredQuery_CompositeFilter_Operator_ARRAYSIZE ((google_firestore_v1beta1_StructuredQuery_CompositeFilter_Operator)(google_firestore_v1beta1_StructuredQuery_CompositeFilter_Operator_AND+1)) + +typedef enum _google_firestore_v1beta1_StructuredQuery_FieldFilter_Operator { + google_firestore_v1beta1_StructuredQuery_FieldFilter_Operator_OPERATOR_UNSPECIFIED = 0, + google_firestore_v1beta1_StructuredQuery_FieldFilter_Operator_LESS_THAN = 1, + google_firestore_v1beta1_StructuredQuery_FieldFilter_Operator_LESS_THAN_OR_EQUAL = 2, + google_firestore_v1beta1_StructuredQuery_FieldFilter_Operator_GREATER_THAN = 3, + google_firestore_v1beta1_StructuredQuery_FieldFilter_Operator_GREATER_THAN_OR_EQUAL = 4, + google_firestore_v1beta1_StructuredQuery_FieldFilter_Operator_EQUAL = 5 +} google_firestore_v1beta1_StructuredQuery_FieldFilter_Operator; +#define _google_firestore_v1beta1_StructuredQuery_FieldFilter_Operator_MIN google_firestore_v1beta1_StructuredQuery_FieldFilter_Operator_OPERATOR_UNSPECIFIED +#define _google_firestore_v1beta1_StructuredQuery_FieldFilter_Operator_MAX google_firestore_v1beta1_StructuredQuery_FieldFilter_Operator_EQUAL +#define _google_firestore_v1beta1_StructuredQuery_FieldFilter_Operator_ARRAYSIZE ((google_firestore_v1beta1_StructuredQuery_FieldFilter_Operator)(google_firestore_v1beta1_StructuredQuery_FieldFilter_Operator_EQUAL+1)) + +typedef enum _google_firestore_v1beta1_StructuredQuery_UnaryFilter_Operator { + google_firestore_v1beta1_StructuredQuery_UnaryFilter_Operator_OPERATOR_UNSPECIFIED = 0, + google_firestore_v1beta1_StructuredQuery_UnaryFilter_Operator_IS_NAN = 2, + google_firestore_v1beta1_StructuredQuery_UnaryFilter_Operator_IS_NULL = 3 +} google_firestore_v1beta1_StructuredQuery_UnaryFilter_Operator; +#define _google_firestore_v1beta1_StructuredQuery_UnaryFilter_Operator_MIN google_firestore_v1beta1_StructuredQuery_UnaryFilter_Operator_OPERATOR_UNSPECIFIED +#define _google_firestore_v1beta1_StructuredQuery_UnaryFilter_Operator_MAX google_firestore_v1beta1_StructuredQuery_UnaryFilter_Operator_IS_NULL +#define _google_firestore_v1beta1_StructuredQuery_UnaryFilter_Operator_ARRAYSIZE ((google_firestore_v1beta1_StructuredQuery_UnaryFilter_Operator)(google_firestore_v1beta1_StructuredQuery_UnaryFilter_Operator_IS_NULL+1)) + +/* Struct definitions */ +typedef struct _google_firestore_v1beta1_StructuredQuery_FieldReference { + pb_callback_t field_path; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_StructuredQuery_FieldReference) */ +} google_firestore_v1beta1_StructuredQuery_FieldReference; + +typedef struct _google_firestore_v1beta1_StructuredQuery_Projection { + pb_callback_t fields; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_StructuredQuery_Projection) */ +} google_firestore_v1beta1_StructuredQuery_Projection; + +typedef struct _google_firestore_v1beta1_Cursor { + pb_callback_t values; + bool before; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_Cursor) */ +} google_firestore_v1beta1_Cursor; + +typedef struct _google_firestore_v1beta1_StructuredQuery_CollectionSelector { + pb_callback_t collection_id; + bool all_descendants; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_StructuredQuery_CollectionSelector) */ +} google_firestore_v1beta1_StructuredQuery_CollectionSelector; + +typedef struct _google_firestore_v1beta1_StructuredQuery_CompositeFilter { + google_firestore_v1beta1_StructuredQuery_CompositeFilter_Operator op; + pb_callback_t filters; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_StructuredQuery_CompositeFilter) */ +} google_firestore_v1beta1_StructuredQuery_CompositeFilter; + +typedef struct _google_firestore_v1beta1_StructuredQuery_FieldFilter { + google_firestore_v1beta1_StructuredQuery_FieldReference field; + google_firestore_v1beta1_StructuredQuery_FieldFilter_Operator op; + google_firestore_v1beta1_Value value; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_StructuredQuery_FieldFilter) */ +} google_firestore_v1beta1_StructuredQuery_FieldFilter; + +typedef struct _google_firestore_v1beta1_StructuredQuery_Order { + google_firestore_v1beta1_StructuredQuery_FieldReference field; + google_firestore_v1beta1_StructuredQuery_Direction direction; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_StructuredQuery_Order) */ +} google_firestore_v1beta1_StructuredQuery_Order; + +typedef struct _google_firestore_v1beta1_StructuredQuery_UnaryFilter { + google_firestore_v1beta1_StructuredQuery_UnaryFilter_Operator op; + pb_size_t which_operand_type; + union { + google_firestore_v1beta1_StructuredQuery_FieldReference field; + } operand_type; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_StructuredQuery_UnaryFilter) */ +} google_firestore_v1beta1_StructuredQuery_UnaryFilter; + +typedef struct _google_firestore_v1beta1_StructuredQuery_Filter { + pb_size_t which_filter_type; + union { + google_firestore_v1beta1_StructuredQuery_CompositeFilter composite_filter; + google_firestore_v1beta1_StructuredQuery_FieldFilter field_filter; + google_firestore_v1beta1_StructuredQuery_UnaryFilter unary_filter; + } filter_type; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_StructuredQuery_Filter) */ +} google_firestore_v1beta1_StructuredQuery_Filter; + +typedef struct _google_firestore_v1beta1_StructuredQuery { + google_firestore_v1beta1_StructuredQuery_Projection select; + pb_callback_t from; + google_firestore_v1beta1_StructuredQuery_Filter where; + pb_callback_t order_by; + google_protobuf_Int32Value limit; + int32_t offset; + google_firestore_v1beta1_Cursor start_at; + google_firestore_v1beta1_Cursor end_at; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_StructuredQuery) */ +} google_firestore_v1beta1_StructuredQuery; + +/* Default values for struct fields */ + +/* Initializer values for message structs */ +#define google_firestore_v1beta1_StructuredQuery_init_default {google_firestore_v1beta1_StructuredQuery_Projection_init_default, {{NULL}, NULL}, google_firestore_v1beta1_StructuredQuery_Filter_init_default, {{NULL}, NULL}, google_protobuf_Int32Value_init_default, 0, google_firestore_v1beta1_Cursor_init_default, google_firestore_v1beta1_Cursor_init_default} +#define google_firestore_v1beta1_StructuredQuery_CollectionSelector_init_default {{{NULL}, NULL}, 0} +#define google_firestore_v1beta1_StructuredQuery_Filter_init_default {0, {google_firestore_v1beta1_StructuredQuery_CompositeFilter_init_default}} +#define google_firestore_v1beta1_StructuredQuery_CompositeFilter_init_default {(google_firestore_v1beta1_StructuredQuery_CompositeFilter_Operator)0, {{NULL}, NULL}} +#define google_firestore_v1beta1_StructuredQuery_FieldFilter_init_default {google_firestore_v1beta1_StructuredQuery_FieldReference_init_default, (google_firestore_v1beta1_StructuredQuery_FieldFilter_Operator)0, google_firestore_v1beta1_Value_init_default} +#define google_firestore_v1beta1_StructuredQuery_UnaryFilter_init_default {(google_firestore_v1beta1_StructuredQuery_UnaryFilter_Operator)0, 0, {google_firestore_v1beta1_StructuredQuery_FieldReference_init_default}} +#define google_firestore_v1beta1_StructuredQuery_Order_init_default {google_firestore_v1beta1_StructuredQuery_FieldReference_init_default, (google_firestore_v1beta1_StructuredQuery_Direction)0} +#define google_firestore_v1beta1_StructuredQuery_FieldReference_init_default {{{NULL}, NULL}} +#define google_firestore_v1beta1_StructuredQuery_Projection_init_default {{{NULL}, NULL}} +#define google_firestore_v1beta1_Cursor_init_default {{{NULL}, NULL}, 0} +#define google_firestore_v1beta1_StructuredQuery_init_zero {google_firestore_v1beta1_StructuredQuery_Projection_init_zero, {{NULL}, NULL}, google_firestore_v1beta1_StructuredQuery_Filter_init_zero, {{NULL}, NULL}, google_protobuf_Int32Value_init_zero, 0, google_firestore_v1beta1_Cursor_init_zero, google_firestore_v1beta1_Cursor_init_zero} +#define google_firestore_v1beta1_StructuredQuery_CollectionSelector_init_zero {{{NULL}, NULL}, 0} +#define google_firestore_v1beta1_StructuredQuery_Filter_init_zero {0, {google_firestore_v1beta1_StructuredQuery_CompositeFilter_init_zero}} +#define google_firestore_v1beta1_StructuredQuery_CompositeFilter_init_zero {(google_firestore_v1beta1_StructuredQuery_CompositeFilter_Operator)0, {{NULL}, NULL}} +#define google_firestore_v1beta1_StructuredQuery_FieldFilter_init_zero {google_firestore_v1beta1_StructuredQuery_FieldReference_init_zero, (google_firestore_v1beta1_StructuredQuery_FieldFilter_Operator)0, google_firestore_v1beta1_Value_init_zero} +#define google_firestore_v1beta1_StructuredQuery_UnaryFilter_init_zero {(google_firestore_v1beta1_StructuredQuery_UnaryFilter_Operator)0, 0, {google_firestore_v1beta1_StructuredQuery_FieldReference_init_zero}} +#define google_firestore_v1beta1_StructuredQuery_Order_init_zero {google_firestore_v1beta1_StructuredQuery_FieldReference_init_zero, (google_firestore_v1beta1_StructuredQuery_Direction)0} +#define google_firestore_v1beta1_StructuredQuery_FieldReference_init_zero {{{NULL}, NULL}} +#define google_firestore_v1beta1_StructuredQuery_Projection_init_zero {{{NULL}, NULL}} +#define google_firestore_v1beta1_Cursor_init_zero {{{NULL}, NULL}, 0} + +/* Field tags (for use in manual encoding/decoding) */ +#define google_firestore_v1beta1_StructuredQuery_FieldReference_field_path_tag 2 +#define google_firestore_v1beta1_StructuredQuery_Projection_fields_tag 2 +#define google_firestore_v1beta1_Cursor_values_tag 1 +#define google_firestore_v1beta1_Cursor_before_tag 2 +#define google_firestore_v1beta1_StructuredQuery_CollectionSelector_collection_id_tag 2 +#define google_firestore_v1beta1_StructuredQuery_CollectionSelector_all_descendants_tag 3 +#define google_firestore_v1beta1_StructuredQuery_CompositeFilter_op_tag 1 +#define google_firestore_v1beta1_StructuredQuery_CompositeFilter_filters_tag 2 +#define google_firestore_v1beta1_StructuredQuery_FieldFilter_field_tag 1 +#define google_firestore_v1beta1_StructuredQuery_FieldFilter_op_tag 2 +#define google_firestore_v1beta1_StructuredQuery_FieldFilter_value_tag 3 +#define google_firestore_v1beta1_StructuredQuery_Order_field_tag 1 +#define google_firestore_v1beta1_StructuredQuery_Order_direction_tag 2 +#define google_firestore_v1beta1_StructuredQuery_UnaryFilter_field_tag 2 +#define google_firestore_v1beta1_StructuredQuery_UnaryFilter_op_tag 1 +#define google_firestore_v1beta1_StructuredQuery_Filter_composite_filter_tag 1 +#define google_firestore_v1beta1_StructuredQuery_Filter_field_filter_tag 2 +#define google_firestore_v1beta1_StructuredQuery_Filter_unary_filter_tag 3 +#define google_firestore_v1beta1_StructuredQuery_select_tag 1 +#define google_firestore_v1beta1_StructuredQuery_from_tag 2 +#define google_firestore_v1beta1_StructuredQuery_where_tag 3 +#define google_firestore_v1beta1_StructuredQuery_order_by_tag 4 +#define google_firestore_v1beta1_StructuredQuery_start_at_tag 7 +#define google_firestore_v1beta1_StructuredQuery_end_at_tag 8 +#define google_firestore_v1beta1_StructuredQuery_offset_tag 6 +#define google_firestore_v1beta1_StructuredQuery_limit_tag 5 + +/* Struct field encoding specification for nanopb */ +extern const pb_field_t google_firestore_v1beta1_StructuredQuery_fields[9]; +extern const pb_field_t google_firestore_v1beta1_StructuredQuery_CollectionSelector_fields[3]; +extern const pb_field_t google_firestore_v1beta1_StructuredQuery_Filter_fields[4]; +extern const pb_field_t google_firestore_v1beta1_StructuredQuery_CompositeFilter_fields[3]; +extern const pb_field_t google_firestore_v1beta1_StructuredQuery_FieldFilter_fields[4]; +extern const pb_field_t google_firestore_v1beta1_StructuredQuery_UnaryFilter_fields[3]; +extern const pb_field_t google_firestore_v1beta1_StructuredQuery_Order_fields[3]; +extern const pb_field_t google_firestore_v1beta1_StructuredQuery_FieldReference_fields[2]; +extern const pb_field_t google_firestore_v1beta1_StructuredQuery_Projection_fields[2]; +extern const pb_field_t google_firestore_v1beta1_Cursor_fields[3]; + +/* Maximum encoded size of messages (where known) */ +/* google_firestore_v1beta1_StructuredQuery_size depends on runtime parameters */ +/* google_firestore_v1beta1_StructuredQuery_CollectionSelector_size depends on runtime parameters */ +/* google_firestore_v1beta1_StructuredQuery_Filter_size depends on runtime parameters */ +/* google_firestore_v1beta1_StructuredQuery_CompositeFilter_size depends on runtime parameters */ +#define google_firestore_v1beta1_StructuredQuery_FieldFilter_size (14 + google_firestore_v1beta1_StructuredQuery_FieldReference_size + google_firestore_v1beta1_Value_size) +/* google_firestore_v1beta1_StructuredQuery_UnaryFilter_size depends on runtime parameters */ +#define google_firestore_v1beta1_StructuredQuery_Order_size (8 + google_firestore_v1beta1_StructuredQuery_FieldReference_size) +/* google_firestore_v1beta1_StructuredQuery_FieldReference_size depends on runtime parameters */ +/* google_firestore_v1beta1_StructuredQuery_Projection_size depends on runtime parameters */ +/* google_firestore_v1beta1_Cursor_size depends on runtime parameters */ + +/* Message IDs (where set with "msgid" option) */ +#ifdef PB_MSGID + +#define QUERY_MESSAGES \ + + +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif +/* @@protoc_insertion_point(eof) */ + +#endif diff --git a/Firestore/Protos/nanopb/google/firestore/v1beta1/write.pb.c b/Firestore/Protos/nanopb/google/firestore/v1beta1/write.pb.c new file mode 100644 index 00000000000..462eca68f78 --- /dev/null +++ b/Firestore/Protos/nanopb/google/firestore/v1beta1/write.pb.c @@ -0,0 +1,110 @@ +/* + * Copyright 2018 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. + */ + +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.3.8 at Fri Feb 2 17:48:02 2018. */ + +#include "write.pb.h" + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + + + +const pb_field_t google_firestore_v1beta1_Write_fields[7] = { + PB_FIELD( 1, MESSAGE , SINGULAR, STATIC , FIRST, google_firestore_v1beta1_Write, update, update, &google_firestore_v1beta1_Document_fields), + PB_FIELD( 2, STRING , SINGULAR, CALLBACK, OTHER, google_firestore_v1beta1_Write, delete_, update, 0), + PB_FIELD( 3, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_Write, update_mask, delete_, &google_firestore_v1beta1_DocumentMask_fields), + PB_FIELD( 4, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_Write, current_document, update_mask, &google_firestore_v1beta1_Precondition_fields), + PB_FIELD( 5, STRING , SINGULAR, CALLBACK, OTHER, google_firestore_v1beta1_Write, verify, current_document, 0), + PB_FIELD( 6, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_Write, transform, verify, &google_firestore_v1beta1_DocumentTransform_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_DocumentTransform_fields[3] = { + PB_FIELD( 1, STRING , SINGULAR, CALLBACK, FIRST, google_firestore_v1beta1_DocumentTransform, document, document, 0), + PB_FIELD( 2, MESSAGE , REPEATED, CALLBACK, OTHER, google_firestore_v1beta1_DocumentTransform, field_transforms, document, &google_firestore_v1beta1_DocumentTransform_FieldTransform_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_DocumentTransform_FieldTransform_fields[3] = { + PB_FIELD( 1, STRING , SINGULAR, CALLBACK, FIRST, google_firestore_v1beta1_DocumentTransform_FieldTransform, field_path, field_path, 0), + PB_ONEOF_FIELD(transform_type, 2, ENUM , ONEOF, STATIC , OTHER, google_firestore_v1beta1_DocumentTransform_FieldTransform, set_to_server_value, field_path, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_WriteResult_fields[3] = { + PB_FIELD( 1, MESSAGE , SINGULAR, STATIC , FIRST, google_firestore_v1beta1_WriteResult, update_time, update_time, &google_protobuf_Timestamp_fields), + PB_FIELD( 2, MESSAGE , REPEATED, CALLBACK, OTHER, google_firestore_v1beta1_WriteResult, transform_results, update_time, &google_firestore_v1beta1_Value_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_DocumentChange_fields[4] = { + PB_FIELD( 1, MESSAGE , SINGULAR, STATIC , FIRST, google_firestore_v1beta1_DocumentChange, document, document, &google_firestore_v1beta1_Document_fields), + PB_FIELD( 5, INT32 , REPEATED, CALLBACK, OTHER, google_firestore_v1beta1_DocumentChange, target_ids, document, 0), + PB_FIELD( 6, INT32 , REPEATED, CALLBACK, OTHER, google_firestore_v1beta1_DocumentChange, removed_target_ids, target_ids, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_DocumentDelete_fields[4] = { + PB_FIELD( 1, STRING , SINGULAR, CALLBACK, FIRST, google_firestore_v1beta1_DocumentDelete, document, document, 0), + PB_FIELD( 4, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_DocumentDelete, read_time, document, &google_protobuf_Timestamp_fields), + PB_FIELD( 6, INT32 , REPEATED, CALLBACK, OTHER, google_firestore_v1beta1_DocumentDelete, removed_target_ids, read_time, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_DocumentRemove_fields[4] = { + PB_FIELD( 1, STRING , SINGULAR, CALLBACK, FIRST, google_firestore_v1beta1_DocumentRemove, document, document, 0), + PB_FIELD( 2, INT32 , REPEATED, CALLBACK, OTHER, google_firestore_v1beta1_DocumentRemove, removed_target_ids, document, 0), + PB_FIELD( 4, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_DocumentRemove, read_time, removed_target_ids, &google_protobuf_Timestamp_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_firestore_v1beta1_ExistenceFilter_fields[3] = { + PB_FIELD( 1, INT32 , SINGULAR, STATIC , FIRST, google_firestore_v1beta1_ExistenceFilter, target_id, target_id, 0), + PB_FIELD( 2, INT32 , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_ExistenceFilter, count, target_id, 0), + PB_LAST_FIELD +}; + + + +/* Check that field information fits in pb_field_t */ +#if !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_32BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in 8 or 16 bit + * field descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(google_firestore_v1beta1_Write, update) < 65536 && pb_membersize(google_firestore_v1beta1_Write, transform) < 65536 && pb_membersize(google_firestore_v1beta1_Write, update_mask) < 65536 && pb_membersize(google_firestore_v1beta1_Write, current_document) < 65536 && pb_membersize(google_firestore_v1beta1_WriteResult, update_time) < 65536 && pb_membersize(google_firestore_v1beta1_DocumentChange, document) < 65536 && pb_membersize(google_firestore_v1beta1_DocumentDelete, read_time) < 65536 && pb_membersize(google_firestore_v1beta1_DocumentRemove, read_time) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_google_firestore_v1beta1_Write_google_firestore_v1beta1_DocumentTransform_google_firestore_v1beta1_DocumentTransform_FieldTransform_google_firestore_v1beta1_WriteResult_google_firestore_v1beta1_DocumentChange_google_firestore_v1beta1_DocumentDelete_google_firestore_v1beta1_DocumentRemove_google_firestore_v1beta1_ExistenceFilter) +#endif + +#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_16BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in the default + * 8 bit descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(google_firestore_v1beta1_Write, update) < 256 && pb_membersize(google_firestore_v1beta1_Write, transform) < 256 && pb_membersize(google_firestore_v1beta1_Write, update_mask) < 256 && pb_membersize(google_firestore_v1beta1_Write, current_document) < 256 && pb_membersize(google_firestore_v1beta1_WriteResult, update_time) < 256 && pb_membersize(google_firestore_v1beta1_DocumentChange, document) < 256 && pb_membersize(google_firestore_v1beta1_DocumentDelete, read_time) < 256 && pb_membersize(google_firestore_v1beta1_DocumentRemove, read_time) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_google_firestore_v1beta1_Write_google_firestore_v1beta1_DocumentTransform_google_firestore_v1beta1_DocumentTransform_FieldTransform_google_firestore_v1beta1_WriteResult_google_firestore_v1beta1_DocumentChange_google_firestore_v1beta1_DocumentDelete_google_firestore_v1beta1_DocumentRemove_google_firestore_v1beta1_ExistenceFilter) +#endif + + +/* @@protoc_insertion_point(eof) */ diff --git a/Firestore/Protos/nanopb/google/firestore/v1beta1/write.pb.h b/Firestore/Protos/nanopb/google/firestore/v1beta1/write.pb.h new file mode 100644 index 00000000000..a44b6d4a670 --- /dev/null +++ b/Firestore/Protos/nanopb/google/firestore/v1beta1/write.pb.h @@ -0,0 +1,187 @@ +/* + * Copyright 2018 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. + */ + +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.3.8 at Fri Feb 2 17:48:02 2018. */ + +#ifndef PB_GOOGLE_FIRESTORE_V1BETA1_WRITE_PB_H_INCLUDED +#define PB_GOOGLE_FIRESTORE_V1BETA1_WRITE_PB_H_INCLUDED +#include + +#include "google/api/annotations.pb.h" + +#include "google/firestore/v1beta1/common.pb.h" + +#include "google/firestore/v1beta1/document.pb.h" + +#include "google/protobuf/timestamp.pb.h" + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Enum definitions */ +typedef enum _google_firestore_v1beta1_DocumentTransform_FieldTransform_ServerValue { + google_firestore_v1beta1_DocumentTransform_FieldTransform_ServerValue_SERVER_VALUE_UNSPECIFIED = 0, + google_firestore_v1beta1_DocumentTransform_FieldTransform_ServerValue_REQUEST_TIME = 1 +} google_firestore_v1beta1_DocumentTransform_FieldTransform_ServerValue; +#define _google_firestore_v1beta1_DocumentTransform_FieldTransform_ServerValue_MIN google_firestore_v1beta1_DocumentTransform_FieldTransform_ServerValue_SERVER_VALUE_UNSPECIFIED +#define _google_firestore_v1beta1_DocumentTransform_FieldTransform_ServerValue_MAX google_firestore_v1beta1_DocumentTransform_FieldTransform_ServerValue_REQUEST_TIME +#define _google_firestore_v1beta1_DocumentTransform_FieldTransform_ServerValue_ARRAYSIZE ((google_firestore_v1beta1_DocumentTransform_FieldTransform_ServerValue)(google_firestore_v1beta1_DocumentTransform_FieldTransform_ServerValue_REQUEST_TIME+1)) + +/* Struct definitions */ +typedef struct _google_firestore_v1beta1_DocumentTransform { + pb_callback_t document; + pb_callback_t field_transforms; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_DocumentTransform) */ +} google_firestore_v1beta1_DocumentTransform; + +typedef struct _google_firestore_v1beta1_DocumentChange { + google_firestore_v1beta1_Document document; + pb_callback_t target_ids; + pb_callback_t removed_target_ids; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_DocumentChange) */ +} google_firestore_v1beta1_DocumentChange; + +typedef struct _google_firestore_v1beta1_DocumentDelete { + pb_callback_t document; + google_protobuf_Timestamp read_time; + pb_callback_t removed_target_ids; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_DocumentDelete) */ +} google_firestore_v1beta1_DocumentDelete; + +typedef struct _google_firestore_v1beta1_DocumentRemove { + pb_callback_t document; + pb_callback_t removed_target_ids; + google_protobuf_Timestamp read_time; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_DocumentRemove) */ +} google_firestore_v1beta1_DocumentRemove; + +typedef struct _google_firestore_v1beta1_DocumentTransform_FieldTransform { + pb_callback_t field_path; + pb_size_t which_transform_type; + union { + google_firestore_v1beta1_DocumentTransform_FieldTransform_ServerValue set_to_server_value; + } transform_type; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_DocumentTransform_FieldTransform) */ +} google_firestore_v1beta1_DocumentTransform_FieldTransform; + +typedef struct _google_firestore_v1beta1_ExistenceFilter { + int32_t target_id; + int32_t count; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_ExistenceFilter) */ +} google_firestore_v1beta1_ExistenceFilter; + +typedef struct _google_firestore_v1beta1_Write { + google_firestore_v1beta1_Document update; + pb_callback_t delete_; + google_firestore_v1beta1_DocumentMask update_mask; + google_firestore_v1beta1_Precondition current_document; + pb_callback_t verify; + google_firestore_v1beta1_DocumentTransform transform; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_Write) */ +} google_firestore_v1beta1_Write; + +typedef struct _google_firestore_v1beta1_WriteResult { + google_protobuf_Timestamp update_time; + pb_callback_t transform_results; +/* @@protoc_insertion_point(struct:google_firestore_v1beta1_WriteResult) */ +} google_firestore_v1beta1_WriteResult; + +/* Default values for struct fields */ + +/* Initializer values for message structs */ +#define google_firestore_v1beta1_Write_init_default {google_firestore_v1beta1_Document_init_default, {{NULL}, NULL}, google_firestore_v1beta1_DocumentMask_init_default, google_firestore_v1beta1_Precondition_init_default, {{NULL}, NULL}, google_firestore_v1beta1_DocumentTransform_init_default} +#define google_firestore_v1beta1_DocumentTransform_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define google_firestore_v1beta1_DocumentTransform_FieldTransform_init_default {{{NULL}, NULL}, 0, {(google_firestore_v1beta1_DocumentTransform_FieldTransform_ServerValue)0}} +#define google_firestore_v1beta1_WriteResult_init_default {google_protobuf_Timestamp_init_default, {{NULL}, NULL}} +#define google_firestore_v1beta1_DocumentChange_init_default {google_firestore_v1beta1_Document_init_default, {{NULL}, NULL}, {{NULL}, NULL}} +#define google_firestore_v1beta1_DocumentDelete_init_default {{{NULL}, NULL}, google_protobuf_Timestamp_init_default, {{NULL}, NULL}} +#define google_firestore_v1beta1_DocumentRemove_init_default {{{NULL}, NULL}, {{NULL}, NULL}, google_protobuf_Timestamp_init_default} +#define google_firestore_v1beta1_ExistenceFilter_init_default {0, 0} +#define google_firestore_v1beta1_Write_init_zero {google_firestore_v1beta1_Document_init_zero, {{NULL}, NULL}, google_firestore_v1beta1_DocumentMask_init_zero, google_firestore_v1beta1_Precondition_init_zero, {{NULL}, NULL}, google_firestore_v1beta1_DocumentTransform_init_zero} +#define google_firestore_v1beta1_DocumentTransform_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define google_firestore_v1beta1_DocumentTransform_FieldTransform_init_zero {{{NULL}, NULL}, 0, {(google_firestore_v1beta1_DocumentTransform_FieldTransform_ServerValue)0}} +#define google_firestore_v1beta1_WriteResult_init_zero {google_protobuf_Timestamp_init_zero, {{NULL}, NULL}} +#define google_firestore_v1beta1_DocumentChange_init_zero {google_firestore_v1beta1_Document_init_zero, {{NULL}, NULL}, {{NULL}, NULL}} +#define google_firestore_v1beta1_DocumentDelete_init_zero {{{NULL}, NULL}, google_protobuf_Timestamp_init_zero, {{NULL}, NULL}} +#define google_firestore_v1beta1_DocumentRemove_init_zero {{{NULL}, NULL}, {{NULL}, NULL}, google_protobuf_Timestamp_init_zero} +#define google_firestore_v1beta1_ExistenceFilter_init_zero {0, 0} + +/* Field tags (for use in manual encoding/decoding) */ +#define google_firestore_v1beta1_DocumentTransform_document_tag 1 +#define google_firestore_v1beta1_DocumentTransform_field_transforms_tag 2 +#define google_firestore_v1beta1_DocumentChange_document_tag 1 +#define google_firestore_v1beta1_DocumentChange_target_ids_tag 5 +#define google_firestore_v1beta1_DocumentChange_removed_target_ids_tag 6 +#define google_firestore_v1beta1_DocumentDelete_document_tag 1 +#define google_firestore_v1beta1_DocumentDelete_removed_target_ids_tag 6 +#define google_firestore_v1beta1_DocumentDelete_read_time_tag 4 +#define google_firestore_v1beta1_DocumentRemove_document_tag 1 +#define google_firestore_v1beta1_DocumentRemove_removed_target_ids_tag 2 +#define google_firestore_v1beta1_DocumentRemove_read_time_tag 4 +#define google_firestore_v1beta1_DocumentTransform_FieldTransform_set_to_server_value_tag 2 +#define google_firestore_v1beta1_DocumentTransform_FieldTransform_field_path_tag 1 +#define google_firestore_v1beta1_ExistenceFilter_target_id_tag 1 +#define google_firestore_v1beta1_ExistenceFilter_count_tag 2 +#define google_firestore_v1beta1_Write_update_tag 1 +#define google_firestore_v1beta1_Write_delete_tag 2 +#define google_firestore_v1beta1_Write_verify_tag 5 +#define google_firestore_v1beta1_Write_transform_tag 6 +#define google_firestore_v1beta1_Write_update_mask_tag 3 +#define google_firestore_v1beta1_Write_current_document_tag 4 +#define google_firestore_v1beta1_WriteResult_update_time_tag 1 +#define google_firestore_v1beta1_WriteResult_transform_results_tag 2 + +/* Struct field encoding specification for nanopb */ +extern const pb_field_t google_firestore_v1beta1_Write_fields[7]; +extern const pb_field_t google_firestore_v1beta1_DocumentTransform_fields[3]; +extern const pb_field_t google_firestore_v1beta1_DocumentTransform_FieldTransform_fields[3]; +extern const pb_field_t google_firestore_v1beta1_WriteResult_fields[3]; +extern const pb_field_t google_firestore_v1beta1_DocumentChange_fields[4]; +extern const pb_field_t google_firestore_v1beta1_DocumentDelete_fields[4]; +extern const pb_field_t google_firestore_v1beta1_DocumentRemove_fields[4]; +extern const pb_field_t google_firestore_v1beta1_ExistenceFilter_fields[3]; + +/* Maximum encoded size of messages (where known) */ +/* google_firestore_v1beta1_Write_size depends on runtime parameters */ +/* google_firestore_v1beta1_DocumentTransform_size depends on runtime parameters */ +/* google_firestore_v1beta1_DocumentTransform_FieldTransform_size depends on runtime parameters */ +/* google_firestore_v1beta1_WriteResult_size depends on runtime parameters */ +/* google_firestore_v1beta1_DocumentChange_size depends on runtime parameters */ +/* google_firestore_v1beta1_DocumentDelete_size depends on runtime parameters */ +/* google_firestore_v1beta1_DocumentRemove_size depends on runtime parameters */ +#define google_firestore_v1beta1_ExistenceFilter_size 22 + +/* Message IDs (where set with "msgid" option) */ +#ifdef PB_MSGID + +#define WRITE_MESSAGES \ + + +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif +/* @@protoc_insertion_point(eof) */ + +#endif diff --git a/Firestore/Protos/nanopb/google/protobuf/any.pb.c b/Firestore/Protos/nanopb/google/protobuf/any.pb.c new file mode 100644 index 00000000000..b28d0ba4a2f --- /dev/null +++ b/Firestore/Protos/nanopb/google/protobuf/any.pb.c @@ -0,0 +1,36 @@ +/* + * Copyright 2018 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. + */ + +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.3.8 at Mon Feb 12 11:03:06 2018. */ + +#include "any.pb.h" + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + + + +const pb_field_t google_protobuf_Any_fields[3] = { + PB_FIELD( 1, STRING , SINGULAR, CALLBACK, FIRST, google_protobuf_Any, type_url, type_url, 0), + PB_FIELD( 2, BYTES , SINGULAR, CALLBACK, OTHER, google_protobuf_Any, value, type_url, 0), + PB_LAST_FIELD +}; + + +/* @@protoc_insertion_point(eof) */ diff --git a/Firestore/Protos/nanopb/google/protobuf/any.pb.h b/Firestore/Protos/nanopb/google/protobuf/any.pb.h new file mode 100644 index 00000000000..10a722e226e --- /dev/null +++ b/Firestore/Protos/nanopb/google/protobuf/any.pb.h @@ -0,0 +1,69 @@ +/* + * Copyright 2018 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. + */ + +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.3.8 at Mon Feb 12 11:03:06 2018. */ + +#ifndef PB_GOOGLE_PROTOBUF_ANY_PB_H_INCLUDED +#define PB_GOOGLE_PROTOBUF_ANY_PB_H_INCLUDED +#include + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Struct definitions */ +typedef struct _google_protobuf_Any { + pb_callback_t type_url; + pb_callback_t value; +/* @@protoc_insertion_point(struct:google_protobuf_Any) */ +} google_protobuf_Any; + +/* Default values for struct fields */ + +/* Initializer values for message structs */ +#define google_protobuf_Any_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define google_protobuf_Any_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} + +/* Field tags (for use in manual encoding/decoding) */ +#define google_protobuf_Any_type_url_tag 1 +#define google_protobuf_Any_value_tag 2 + +/* Struct field encoding specification for nanopb */ +extern const pb_field_t google_protobuf_Any_fields[3]; + +/* Maximum encoded size of messages (where known) */ +/* google_protobuf_Any_size depends on runtime parameters */ + +/* Message IDs (where set with "msgid" option) */ +#ifdef PB_MSGID + +#define ANY_MESSAGES \ + + +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif +/* @@protoc_insertion_point(eof) */ + +#endif diff --git a/Firestore/Protos/nanopb/google/protobuf/empty.pb.c b/Firestore/Protos/nanopb/google/protobuf/empty.pb.c new file mode 100644 index 00000000000..050af9c633a --- /dev/null +++ b/Firestore/Protos/nanopb/google/protobuf/empty.pb.c @@ -0,0 +1,34 @@ +/* + * Copyright 2018 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. + */ + +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.3.8 at Mon Feb 12 11:03:06 2018. */ + +#include "empty.pb.h" + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + + + +const pb_field_t google_protobuf_Empty_fields[1] = { + PB_LAST_FIELD +}; + + +/* @@protoc_insertion_point(eof) */ diff --git a/Firestore/Protos/nanopb/google/protobuf/empty.pb.h b/Firestore/Protos/nanopb/google/protobuf/empty.pb.h new file mode 100644 index 00000000000..466e1fd1491 --- /dev/null +++ b/Firestore/Protos/nanopb/google/protobuf/empty.pb.h @@ -0,0 +1,66 @@ +/* + * Copyright 2018 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. + */ + +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.3.8 at Mon Feb 12 11:03:06 2018. */ + +#ifndef PB_GOOGLE_PROTOBUF_EMPTY_PB_H_INCLUDED +#define PB_GOOGLE_PROTOBUF_EMPTY_PB_H_INCLUDED +#include + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Struct definitions */ +typedef struct _google_protobuf_Empty { + char dummy_field; +/* @@protoc_insertion_point(struct:google_protobuf_Empty) */ +} google_protobuf_Empty; + +/* Default values for struct fields */ + +/* Initializer values for message structs */ +#define google_protobuf_Empty_init_default {0} +#define google_protobuf_Empty_init_zero {0} + +/* Field tags (for use in manual encoding/decoding) */ + +/* Struct field encoding specification for nanopb */ +extern const pb_field_t google_protobuf_Empty_fields[1]; + +/* Maximum encoded size of messages (where known) */ +#define google_protobuf_Empty_size 0 + +/* Message IDs (where set with "msgid" option) */ +#ifdef PB_MSGID + +#define EMPTY_MESSAGES \ + + +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif +/* @@protoc_insertion_point(eof) */ + +#endif diff --git a/Firestore/Protos/nanopb/google/protobuf/struct.pb.c b/Firestore/Protos/nanopb/google/protobuf/struct.pb.c new file mode 100644 index 00000000000..2826aab73e7 --- /dev/null +++ b/Firestore/Protos/nanopb/google/protobuf/struct.pb.c @@ -0,0 +1,87 @@ +/* + * Copyright 2018 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. + */ + +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.3.8 at Fri Feb 2 17:48:02 2018. */ + +#include "struct.pb.h" + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + + + +const pb_field_t google_protobuf_Struct_fields[2] = { + PB_FIELD( 1, MESSAGE , REPEATED, CALLBACK, FIRST, google_protobuf_Struct, fields, fields, &google_protobuf_Struct_FieldsEntry_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_protobuf_Struct_FieldsEntry_fields[3] = { + PB_FIELD( 1, STRING , SINGULAR, CALLBACK, FIRST, google_protobuf_Struct_FieldsEntry, key, key, 0), + PB_FIELD( 2, MESSAGE , SINGULAR, STATIC , OTHER, google_protobuf_Struct_FieldsEntry, value, key, &google_protobuf_Value_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_protobuf_Value_fields[7] = { + PB_FIELD( 1, UENUM , SINGULAR, STATIC , FIRST, google_protobuf_Value, null_value, null_value, 0), + PB_FIELD( 2, DOUBLE , SINGULAR, STATIC , OTHER, google_protobuf_Value, number_value, null_value, 0), + PB_FIELD( 3, STRING , SINGULAR, CALLBACK, OTHER, google_protobuf_Value, string_value, number_value, 0), + PB_FIELD( 4, BOOL , SINGULAR, STATIC , OTHER, google_protobuf_Value, bool_value, string_value, 0), + PB_FIELD( 5, MESSAGE , SINGULAR, STATIC , OTHER, google_protobuf_Value, struct_value, bool_value, &google_protobuf_Struct_fields), + PB_FIELD( 6, MESSAGE , SINGULAR, STATIC , OTHER, google_protobuf_Value, list_value, struct_value, &google_protobuf_ListValue_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_protobuf_ListValue_fields[2] = { + PB_FIELD( 1, MESSAGE , REPEATED, CALLBACK, FIRST, google_protobuf_ListValue, values, values, &google_protobuf_Value_fields), + PB_LAST_FIELD +}; + + + +/* Check that field information fits in pb_field_t */ +#if !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_32BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in 8 or 16 bit + * field descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(google_protobuf_Struct_FieldsEntry, value) < 65536 && pb_membersize(google_protobuf_Value, struct_value) < 65536 && pb_membersize(google_protobuf_Value, list_value) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_google_protobuf_Struct_google_protobuf_Struct_FieldsEntry_google_protobuf_Value_google_protobuf_ListValue) +#endif + +#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_16BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in the default + * 8 bit descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(google_protobuf_Struct_FieldsEntry, value) < 256 && pb_membersize(google_protobuf_Value, struct_value) < 256 && pb_membersize(google_protobuf_Value, list_value) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_google_protobuf_Struct_google_protobuf_Struct_FieldsEntry_google_protobuf_Value_google_protobuf_ListValue) +#endif + + +/* On some platforms (such as AVR), double is really float. + * These are not directly supported by nanopb, but see example_avr_double. + * To get rid of this error, remove any double fields from your .proto. + */ +PB_STATIC_ASSERT(sizeof(double) == 8, DOUBLE_MUST_BE_8_BYTES) + +/* @@protoc_insertion_point(eof) */ diff --git a/Firestore/Protos/nanopb/google/protobuf/struct.pb.h b/Firestore/Protos/nanopb/google/protobuf/struct.pb.h new file mode 100644 index 00000000000..b325b6087fc --- /dev/null +++ b/Firestore/Protos/nanopb/google/protobuf/struct.pb.h @@ -0,0 +1,117 @@ +/* + * Copyright 2018 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. + */ + +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.3.8 at Fri Feb 2 17:48:02 2018. */ + +#ifndef PB_GOOGLE_PROTOBUF_STRUCT_PB_H_INCLUDED +#define PB_GOOGLE_PROTOBUF_STRUCT_PB_H_INCLUDED +#include + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Enum definitions */ +typedef enum _google_protobuf_NullValue { + google_protobuf_NullValue_NULL_VALUE = 0 +} google_protobuf_NullValue; +#define _google_protobuf_NullValue_MIN google_protobuf_NullValue_NULL_VALUE +#define _google_protobuf_NullValue_MAX google_protobuf_NullValue_NULL_VALUE +#define _google_protobuf_NullValue_ARRAYSIZE ((google_protobuf_NullValue)(google_protobuf_NullValue_NULL_VALUE+1)) + +/* Struct definitions */ +typedef struct _google_protobuf_ListValue { + pb_callback_t values; +/* @@protoc_insertion_point(struct:google_protobuf_ListValue) */ +} google_protobuf_ListValue; + +typedef struct _google_protobuf_Struct { + pb_callback_t fields; +/* @@protoc_insertion_point(struct:google_protobuf_Struct) */ +} google_protobuf_Struct; + +typedef struct _google_protobuf_Value { + google_protobuf_NullValue null_value; + double number_value; + pb_callback_t string_value; + bool bool_value; + google_protobuf_Struct struct_value; + google_protobuf_ListValue list_value; +/* @@protoc_insertion_point(struct:google_protobuf_Value) */ +} google_protobuf_Value; + +typedef struct _google_protobuf_Struct_FieldsEntry { + pb_callback_t key; + google_protobuf_Value value; +/* @@protoc_insertion_point(struct:google_protobuf_Struct_FieldsEntry) */ +} google_protobuf_Struct_FieldsEntry; + +/* Default values for struct fields */ + +/* Initializer values for message structs */ +#define google_protobuf_Struct_init_default {{{NULL}, NULL}} +#define google_protobuf_Struct_FieldsEntry_init_default {{{NULL}, NULL}, google_protobuf_Value_init_default} +#define google_protobuf_Value_init_default {(google_protobuf_NullValue)0, 0, {{NULL}, NULL}, 0, google_protobuf_Struct_init_default, google_protobuf_ListValue_init_default} +#define google_protobuf_ListValue_init_default {{{NULL}, NULL}} +#define google_protobuf_Struct_init_zero {{{NULL}, NULL}} +#define google_protobuf_Struct_FieldsEntry_init_zero {{{NULL}, NULL}, google_protobuf_Value_init_zero} +#define google_protobuf_Value_init_zero {(google_protobuf_NullValue)0, 0, {{NULL}, NULL}, 0, google_protobuf_Struct_init_zero, google_protobuf_ListValue_init_zero} +#define google_protobuf_ListValue_init_zero {{{NULL}, NULL}} + +/* Field tags (for use in manual encoding/decoding) */ +#define google_protobuf_ListValue_values_tag 1 +#define google_protobuf_Struct_fields_tag 1 +#define google_protobuf_Value_null_value_tag 1 +#define google_protobuf_Value_number_value_tag 2 +#define google_protobuf_Value_string_value_tag 3 +#define google_protobuf_Value_bool_value_tag 4 +#define google_protobuf_Value_struct_value_tag 5 +#define google_protobuf_Value_list_value_tag 6 +#define google_protobuf_Struct_FieldsEntry_key_tag 1 +#define google_protobuf_Struct_FieldsEntry_value_tag 2 + +/* Struct field encoding specification for nanopb */ +extern const pb_field_t google_protobuf_Struct_fields[2]; +extern const pb_field_t google_protobuf_Struct_FieldsEntry_fields[3]; +extern const pb_field_t google_protobuf_Value_fields[7]; +extern const pb_field_t google_protobuf_ListValue_fields[2]; + +/* Maximum encoded size of messages (where known) */ +/* google_protobuf_Struct_size depends on runtime parameters */ +/* google_protobuf_Struct_FieldsEntry_size depends on runtime parameters */ +/* google_protobuf_Value_size depends on runtime parameters */ +/* google_protobuf_ListValue_size depends on runtime parameters */ + +/* Message IDs (where set with "msgid" option) */ +#ifdef PB_MSGID + +#define STRUCT_MESSAGES \ + + +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif +/* @@protoc_insertion_point(eof) */ + +#endif diff --git a/Firestore/Protos/nanopb/google/protobuf/timestamp.pb.c b/Firestore/Protos/nanopb/google/protobuf/timestamp.pb.c new file mode 100644 index 00000000000..4f03c192efd --- /dev/null +++ b/Firestore/Protos/nanopb/google/protobuf/timestamp.pb.c @@ -0,0 +1,36 @@ +/* + * Copyright 2018 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. + */ + +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.3.8 at Fri Feb 2 17:48:02 2018. */ + +#include "timestamp.pb.h" + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + + + +const pb_field_t google_protobuf_Timestamp_fields[3] = { + PB_FIELD( 1, INT64 , SINGULAR, STATIC , FIRST, google_protobuf_Timestamp, seconds, seconds, 0), + PB_FIELD( 2, INT32 , SINGULAR, STATIC , OTHER, google_protobuf_Timestamp, nanos, seconds, 0), + PB_LAST_FIELD +}; + + +/* @@protoc_insertion_point(eof) */ diff --git a/Firestore/Protos/nanopb/google/protobuf/timestamp.pb.h b/Firestore/Protos/nanopb/google/protobuf/timestamp.pb.h new file mode 100644 index 00000000000..d7be9779d21 --- /dev/null +++ b/Firestore/Protos/nanopb/google/protobuf/timestamp.pb.h @@ -0,0 +1,69 @@ +/* + * Copyright 2018 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. + */ + +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.3.8 at Fri Feb 2 17:48:02 2018. */ + +#ifndef PB_GOOGLE_PROTOBUF_TIMESTAMP_PB_H_INCLUDED +#define PB_GOOGLE_PROTOBUF_TIMESTAMP_PB_H_INCLUDED +#include + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Struct definitions */ +typedef struct _google_protobuf_Timestamp { + int64_t seconds; + int32_t nanos; +/* @@protoc_insertion_point(struct:google_protobuf_Timestamp) */ +} google_protobuf_Timestamp; + +/* Default values for struct fields */ + +/* Initializer values for message structs */ +#define google_protobuf_Timestamp_init_default {0, 0} +#define google_protobuf_Timestamp_init_zero {0, 0} + +/* Field tags (for use in manual encoding/decoding) */ +#define google_protobuf_Timestamp_seconds_tag 1 +#define google_protobuf_Timestamp_nanos_tag 2 + +/* Struct field encoding specification for nanopb */ +extern const pb_field_t google_protobuf_Timestamp_fields[3]; + +/* Maximum encoded size of messages (where known) */ +#define google_protobuf_Timestamp_size 22 + +/* Message IDs (where set with "msgid" option) */ +#ifdef PB_MSGID + +#define TIMESTAMP_MESSAGES \ + + +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif +/* @@protoc_insertion_point(eof) */ + +#endif diff --git a/Firestore/Protos/nanopb/google/protobuf/wrappers.pb.c b/Firestore/Protos/nanopb/google/protobuf/wrappers.pb.c new file mode 100644 index 00000000000..41ab3c6a755 --- /dev/null +++ b/Firestore/Protos/nanopb/google/protobuf/wrappers.pb.c @@ -0,0 +1,81 @@ +/* + * Copyright 2018 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. + */ + +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.3.8 at Mon Feb 12 11:03:06 2018. */ + +#include "wrappers.pb.h" + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + + + +const pb_field_t google_protobuf_DoubleValue_fields[2] = { + PB_FIELD( 1, DOUBLE , SINGULAR, STATIC , FIRST, google_protobuf_DoubleValue, value, value, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_protobuf_FloatValue_fields[2] = { + PB_FIELD( 1, FLOAT , SINGULAR, STATIC , FIRST, google_protobuf_FloatValue, value, value, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_protobuf_Int64Value_fields[2] = { + PB_FIELD( 1, INT64 , SINGULAR, STATIC , FIRST, google_protobuf_Int64Value, value, value, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_protobuf_UInt64Value_fields[2] = { + PB_FIELD( 1, UINT64 , SINGULAR, STATIC , FIRST, google_protobuf_UInt64Value, value, value, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_protobuf_Int32Value_fields[2] = { + PB_FIELD( 1, INT32 , SINGULAR, STATIC , FIRST, google_protobuf_Int32Value, value, value, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_protobuf_UInt32Value_fields[2] = { + PB_FIELD( 1, UINT32 , SINGULAR, STATIC , FIRST, google_protobuf_UInt32Value, value, value, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_protobuf_BoolValue_fields[2] = { + PB_FIELD( 1, BOOL , SINGULAR, STATIC , FIRST, google_protobuf_BoolValue, value, value, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_protobuf_StringValue_fields[2] = { + PB_FIELD( 1, STRING , SINGULAR, CALLBACK, FIRST, google_protobuf_StringValue, value, value, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_protobuf_BytesValue_fields[2] = { + PB_FIELD( 1, BYTES , SINGULAR, CALLBACK, FIRST, google_protobuf_BytesValue, value, value, 0), + PB_LAST_FIELD +}; + + +/* On some platforms (such as AVR), double is really float. + * These are not directly supported by nanopb, but see example_avr_double. + * To get rid of this error, remove any double fields from your .proto. + */ +PB_STATIC_ASSERT(sizeof(double) == 8, DOUBLE_MUST_BE_8_BYTES) + +/* @@protoc_insertion_point(eof) */ diff --git a/Firestore/Protos/nanopb/google/protobuf/wrappers.pb.h b/Firestore/Protos/nanopb/google/protobuf/wrappers.pb.h new file mode 100644 index 00000000000..0e98785f681 --- /dev/null +++ b/Firestore/Protos/nanopb/google/protobuf/wrappers.pb.h @@ -0,0 +1,147 @@ +/* + * Copyright 2018 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. + */ + +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.3.8 at Mon Feb 12 11:03:06 2018. */ + +#ifndef PB_GOOGLE_PROTOBUF_WRAPPERS_PB_H_INCLUDED +#define PB_GOOGLE_PROTOBUF_WRAPPERS_PB_H_INCLUDED +#include + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Struct definitions */ +typedef struct _google_protobuf_BytesValue { + pb_callback_t value; +/* @@protoc_insertion_point(struct:google_protobuf_BytesValue) */ +} google_protobuf_BytesValue; + +typedef struct _google_protobuf_StringValue { + pb_callback_t value; +/* @@protoc_insertion_point(struct:google_protobuf_StringValue) */ +} google_protobuf_StringValue; + +typedef struct _google_protobuf_BoolValue { + bool value; +/* @@protoc_insertion_point(struct:google_protobuf_BoolValue) */ +} google_protobuf_BoolValue; + +typedef struct _google_protobuf_DoubleValue { + double value; +/* @@protoc_insertion_point(struct:google_protobuf_DoubleValue) */ +} google_protobuf_DoubleValue; + +typedef struct _google_protobuf_FloatValue { + float value; +/* @@protoc_insertion_point(struct:google_protobuf_FloatValue) */ +} google_protobuf_FloatValue; + +typedef struct _google_protobuf_Int32Value { + int32_t value; +/* @@protoc_insertion_point(struct:google_protobuf_Int32Value) */ +} google_protobuf_Int32Value; + +typedef struct _google_protobuf_Int64Value { + int64_t value; +/* @@protoc_insertion_point(struct:google_protobuf_Int64Value) */ +} google_protobuf_Int64Value; + +typedef struct _google_protobuf_UInt32Value { + uint32_t value; +/* @@protoc_insertion_point(struct:google_protobuf_UInt32Value) */ +} google_protobuf_UInt32Value; + +typedef struct _google_protobuf_UInt64Value { + uint64_t value; +/* @@protoc_insertion_point(struct:google_protobuf_UInt64Value) */ +} google_protobuf_UInt64Value; + +/* Default values for struct fields */ + +/* Initializer values for message structs */ +#define google_protobuf_DoubleValue_init_default {0} +#define google_protobuf_FloatValue_init_default {0} +#define google_protobuf_Int64Value_init_default {0} +#define google_protobuf_UInt64Value_init_default {0} +#define google_protobuf_Int32Value_init_default {0} +#define google_protobuf_UInt32Value_init_default {0} +#define google_protobuf_BoolValue_init_default {0} +#define google_protobuf_StringValue_init_default {{{NULL}, NULL}} +#define google_protobuf_BytesValue_init_default {{{NULL}, NULL}} +#define google_protobuf_DoubleValue_init_zero {0} +#define google_protobuf_FloatValue_init_zero {0} +#define google_protobuf_Int64Value_init_zero {0} +#define google_protobuf_UInt64Value_init_zero {0} +#define google_protobuf_Int32Value_init_zero {0} +#define google_protobuf_UInt32Value_init_zero {0} +#define google_protobuf_BoolValue_init_zero {0} +#define google_protobuf_StringValue_init_zero {{{NULL}, NULL}} +#define google_protobuf_BytesValue_init_zero {{{NULL}, NULL}} + +/* Field tags (for use in manual encoding/decoding) */ +#define google_protobuf_BytesValue_value_tag 1 +#define google_protobuf_StringValue_value_tag 1 +#define google_protobuf_BoolValue_value_tag 1 +#define google_protobuf_DoubleValue_value_tag 1 +#define google_protobuf_FloatValue_value_tag 1 +#define google_protobuf_Int32Value_value_tag 1 +#define google_protobuf_Int64Value_value_tag 1 +#define google_protobuf_UInt32Value_value_tag 1 +#define google_protobuf_UInt64Value_value_tag 1 + +/* Struct field encoding specification for nanopb */ +extern const pb_field_t google_protobuf_DoubleValue_fields[2]; +extern const pb_field_t google_protobuf_FloatValue_fields[2]; +extern const pb_field_t google_protobuf_Int64Value_fields[2]; +extern const pb_field_t google_protobuf_UInt64Value_fields[2]; +extern const pb_field_t google_protobuf_Int32Value_fields[2]; +extern const pb_field_t google_protobuf_UInt32Value_fields[2]; +extern const pb_field_t google_protobuf_BoolValue_fields[2]; +extern const pb_field_t google_protobuf_StringValue_fields[2]; +extern const pb_field_t google_protobuf_BytesValue_fields[2]; + +/* Maximum encoded size of messages (where known) */ +#define google_protobuf_DoubleValue_size 9 +#define google_protobuf_FloatValue_size 5 +#define google_protobuf_Int64Value_size 11 +#define google_protobuf_UInt64Value_size 11 +#define google_protobuf_Int32Value_size 11 +#define google_protobuf_UInt32Value_size 6 +#define google_protobuf_BoolValue_size 2 +/* google_protobuf_StringValue_size depends on runtime parameters */ +/* google_protobuf_BytesValue_size depends on runtime parameters */ + +/* Message IDs (where set with "msgid" option) */ +#ifdef PB_MSGID + +#define WRAPPERS_MESSAGES \ + + +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif +/* @@protoc_insertion_point(eof) */ + +#endif diff --git a/Firestore/Protos/nanopb/google/rpc/status.pb.c b/Firestore/Protos/nanopb/google/rpc/status.pb.c new file mode 100644 index 00000000000..dbdcccee5dd --- /dev/null +++ b/Firestore/Protos/nanopb/google/rpc/status.pb.c @@ -0,0 +1,37 @@ +/* + * Copyright 2018 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. + */ + +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.3.8 at Fri Feb 2 17:48:02 2018. */ + +#include "status.pb.h" + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + + + +const pb_field_t google_rpc_Status_fields[4] = { + PB_FIELD( 1, INT32 , SINGULAR, STATIC , FIRST, google_rpc_Status, code, code, 0), + PB_FIELD( 2, STRING , SINGULAR, CALLBACK, OTHER, google_rpc_Status, message, code, 0), + PB_FIELD( 3, MESSAGE , REPEATED, CALLBACK, OTHER, google_rpc_Status, details, message, &google_protobuf_Any_fields), + PB_LAST_FIELD +}; + + +/* @@protoc_insertion_point(eof) */ diff --git a/Firestore/Protos/nanopb/google/rpc/status.pb.h b/Firestore/Protos/nanopb/google/rpc/status.pb.h new file mode 100644 index 00000000000..afcbab94ee4 --- /dev/null +++ b/Firestore/Protos/nanopb/google/rpc/status.pb.h @@ -0,0 +1,73 @@ +/* + * Copyright 2018 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. + */ + +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.3.8 at Fri Feb 2 17:48:02 2018. */ + +#ifndef PB_GOOGLE_RPC_STATUS_PB_H_INCLUDED +#define PB_GOOGLE_RPC_STATUS_PB_H_INCLUDED +#include + +#include "google/protobuf/any.pb.h" + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Struct definitions */ +typedef struct _google_rpc_Status { + int32_t code; + pb_callback_t message; + pb_callback_t details; +/* @@protoc_insertion_point(struct:google_rpc_Status) */ +} google_rpc_Status; + +/* Default values for struct fields */ + +/* Initializer values for message structs */ +#define google_rpc_Status_init_default {0, {{NULL}, NULL}, {{NULL}, NULL}} +#define google_rpc_Status_init_zero {0, {{NULL}, NULL}, {{NULL}, NULL}} + +/* Field tags (for use in manual encoding/decoding) */ +#define google_rpc_Status_code_tag 1 +#define google_rpc_Status_message_tag 2 +#define google_rpc_Status_details_tag 3 + +/* Struct field encoding specification for nanopb */ +extern const pb_field_t google_rpc_Status_fields[4]; + +/* Maximum encoded size of messages (where known) */ +/* google_rpc_Status_size depends on runtime parameters */ + +/* Message IDs (where set with "msgid" option) */ +#ifdef PB_MSGID + +#define STATUS_MESSAGES \ + + +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif +/* @@protoc_insertion_point(eof) */ + +#endif diff --git a/Firestore/Protos/nanopb/google/type/latlng.pb.c b/Firestore/Protos/nanopb/google/type/latlng.pb.c new file mode 100644 index 00000000000..b5f6424bcd5 --- /dev/null +++ b/Firestore/Protos/nanopb/google/type/latlng.pb.c @@ -0,0 +1,42 @@ +/* + * Copyright 2018 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. + */ + +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.3.8 at Fri Feb 2 17:48:02 2018. */ + +#include "latlng.pb.h" + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + + + +const pb_field_t google_type_LatLng_fields[3] = { + PB_FIELD( 1, DOUBLE , SINGULAR, STATIC , FIRST, google_type_LatLng, latitude, latitude, 0), + PB_FIELD( 2, DOUBLE , SINGULAR, STATIC , OTHER, google_type_LatLng, longitude, latitude, 0), + PB_LAST_FIELD +}; + + +/* On some platforms (such as AVR), double is really float. + * These are not directly supported by nanopb, but see example_avr_double. + * To get rid of this error, remove any double fields from your .proto. + */ +PB_STATIC_ASSERT(sizeof(double) == 8, DOUBLE_MUST_BE_8_BYTES) + +/* @@protoc_insertion_point(eof) */ diff --git a/Firestore/Protos/nanopb/google/type/latlng.pb.h b/Firestore/Protos/nanopb/google/type/latlng.pb.h new file mode 100644 index 00000000000..fa5703bf6bd --- /dev/null +++ b/Firestore/Protos/nanopb/google/type/latlng.pb.h @@ -0,0 +1,69 @@ +/* + * Copyright 2018 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. + */ + +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.3.8 at Fri Feb 2 17:48:02 2018. */ + +#ifndef PB_GOOGLE_TYPE_LATLNG_PB_H_INCLUDED +#define PB_GOOGLE_TYPE_LATLNG_PB_H_INCLUDED +#include + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Struct definitions */ +typedef struct _google_type_LatLng { + double latitude; + double longitude; +/* @@protoc_insertion_point(struct:google_type_LatLng) */ +} google_type_LatLng; + +/* Default values for struct fields */ + +/* Initializer values for message structs */ +#define google_type_LatLng_init_default {0, 0} +#define google_type_LatLng_init_zero {0, 0} + +/* Field tags (for use in manual encoding/decoding) */ +#define google_type_LatLng_latitude_tag 1 +#define google_type_LatLng_longitude_tag 2 + +/* Struct field encoding specification for nanopb */ +extern const pb_field_t google_type_LatLng_fields[3]; + +/* Maximum encoded size of messages (where known) */ +#define google_type_LatLng_size 18 + +/* Message IDs (where set with "msgid" option) */ +#ifdef PB_MSGID + +#define LATLNG_MESSAGES \ + + +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif +/* @@protoc_insertion_point(eof) */ + +#endif diff --git a/Firestore/Protos/objc/firestore/local/Target.pbobjc.h b/Firestore/Protos/objc/firestore/local/Target.pbobjc.h index d8bf49c3ddf..0672a6e4482 100644 --- a/Firestore/Protos/objc/firestore/local/Target.pbobjc.h +++ b/Firestore/Protos/objc/firestore/local/Target.pbobjc.h @@ -160,6 +160,7 @@ typedef GPB_ENUM(FSTPBTargetGlobal_FieldNumber) { FSTPBTargetGlobal_FieldNumber_HighestTargetId = 1, FSTPBTargetGlobal_FieldNumber_HighestListenSequenceNumber = 2, FSTPBTargetGlobal_FieldNumber_LastRemoteSnapshotVersion = 3, + FSTPBTargetGlobal_FieldNumber_TargetCount = 4, }; /** @@ -197,6 +198,9 @@ typedef GPB_ENUM(FSTPBTargetGlobal_FieldNumber) { /** Test to see if @c lastRemoteSnapshotVersion has been set. */ @property(nonatomic, readwrite) BOOL hasLastRemoteSnapshotVersion; +/** On platforms that need it, holds the number of targets persisted. */ +@property(nonatomic, readwrite) int32_t targetCount; + @end NS_ASSUME_NONNULL_END diff --git a/Firestore/Protos/objc/firestore/local/Target.pbobjc.m b/Firestore/Protos/objc/firestore/local/Target.pbobjc.m index 6f6ccf2ad27..567c86d8474 100644 --- a/Firestore/Protos/objc/firestore/local/Target.pbobjc.m +++ b/Firestore/Protos/objc/firestore/local/Target.pbobjc.m @@ -183,10 +183,12 @@ @implementation FSTPBTargetGlobal @dynamic highestTargetId; @dynamic highestListenSequenceNumber; @dynamic hasLastRemoteSnapshotVersion, lastRemoteSnapshotVersion; +@dynamic targetCount; typedef struct FSTPBTargetGlobal__storage_ { uint32_t _has_storage_[1]; int32_t highestTargetId; + int32_t targetCount; GPBTimestamp *lastRemoteSnapshotVersion; int64_t highestListenSequenceNumber; } FSTPBTargetGlobal__storage_; @@ -224,6 +226,15 @@ + (GPBDescriptor *)descriptor { .flags = GPBFieldOptional, .dataType = GPBDataTypeMessage, }, + { + .name = "targetCount", + .dataTypeSpecific.className = NULL, + .number = FSTPBTargetGlobal_FieldNumber_TargetCount, + .hasIndex = 3, + .offset = (uint32_t)offsetof(FSTPBTargetGlobal__storage_, targetCount), + .flags = GPBFieldOptional, + .dataType = GPBDataTypeInt32, + }, }; GPBDescriptor *localDescriptor = [GPBDescriptor allocDescriptorForClass:[FSTPBTargetGlobal class] diff --git a/Firestore/Protos/protos/firestore/local/target.proto b/Firestore/Protos/protos/firestore/local/target.proto index 7f34515edca..7f0a886c4a8 100644 --- a/Firestore/Protos/protos/firestore/local/target.proto +++ b/Firestore/Protos/protos/firestore/local/target.proto @@ -87,4 +87,7 @@ message TargetGlobal { // This is updated whenever our we get a TargetChange with a read_time and // empty target_ids. google.protobuf.Timestamp last_remote_snapshot_version = 3; + + // On platforms that need it, holds the number of targets persisted. + int32 target_count = 4; } diff --git a/Firestore/Protos/protos/google/api/http.options b/Firestore/Protos/protos/google/api/http.options new file mode 100644 index 00000000000..6ddde53b458 --- /dev/null +++ b/Firestore/Protos/protos/google/api/http.options @@ -0,0 +1 @@ +google.api.HttpRule.pattern no_unions:true diff --git a/Firestore/Protos/protos/google/firestore/v1beta1/document.options b/Firestore/Protos/protos/google/firestore/v1beta1/document.options new file mode 100644 index 00000000000..e671c4364d1 --- /dev/null +++ b/Firestore/Protos/protos/google/firestore/v1beta1/document.options @@ -0,0 +1 @@ +google.firestore.v1beta1.Value.value_type no_unions:true diff --git a/Firestore/Protos/protos/google/firestore/v1beta1/firestore.options b/Firestore/Protos/protos/google/firestore/v1beta1/firestore.options new file mode 100644 index 00000000000..71a05d4e83d --- /dev/null +++ b/Firestore/Protos/protos/google/firestore/v1beta1/firestore.options @@ -0,0 +1,6 @@ +google.firestore.v1beta1.GetDocumentRequest.consistency_selector no_unions:true +google.firestore.v1beta1.ListDocumentsRequest.consistency_selector no_unions:true +google.firestore.v1beta1.RunQueryRequest.consistency_selector no_unions:true +google.firestore.v1beta1.BatchGetDocumentsRequest.consistency_selector no_unions:true +google.firestore.v1beta1.BatchGetDocumentsResponse.result no_unions:true +google.firestore.v1beta1.Target.resume_type no_unions:true diff --git a/Firestore/Protos/protos/google/firestore/v1beta1/write.options b/Firestore/Protos/protos/google/firestore/v1beta1/write.options new file mode 100644 index 00000000000..41c6a34f2a0 --- /dev/null +++ b/Firestore/Protos/protos/google/firestore/v1beta1/write.options @@ -0,0 +1 @@ +google.firestore.v1beta1.Write.operation no_unions:true diff --git a/Firestore/Protos/protos/google/protobuf/any.proto b/Firestore/Protos/protos/google/protobuf/any.proto new file mode 100644 index 00000000000..c7486676231 --- /dev/null +++ b/Firestore/Protos/protos/google/protobuf/any.proto @@ -0,0 +1,149 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option go_package = "github.com/golang/protobuf/ptypes/any"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "AnyProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; + +// `Any` contains an arbitrary serialized protocol buffer message along with a +// URL that describes the type of the serialized message. +// +// Protobuf library provides support to pack/unpack Any values in the form +// of utility functions or additional generated methods of the Any type. +// +// Example 1: Pack and unpack a message in C++. +// +// Foo foo = ...; +// Any any; +// any.PackFrom(foo); +// ... +// if (any.UnpackTo(&foo)) { +// ... +// } +// +// Example 2: Pack and unpack a message in Java. +// +// Foo foo = ...; +// Any any = Any.pack(foo); +// ... +// if (any.is(Foo.class)) { +// foo = any.unpack(Foo.class); +// } +// +// Example 3: Pack and unpack a message in Python. +// +// foo = Foo(...) +// any = Any() +// any.Pack(foo) +// ... +// if any.Is(Foo.DESCRIPTOR): +// any.Unpack(foo) +// ... +// +// Example 4: Pack and unpack a message in Go +// +// foo := &pb.Foo{...} +// any, err := ptypes.MarshalAny(foo) +// ... +// foo := &pb.Foo{} +// if err := ptypes.UnmarshalAny(any, foo); err != nil { +// ... +// } +// +// The pack methods provided by protobuf library will by default use +// 'type.googleapis.com/full.type.name' as the type URL and the unpack +// methods only use the fully qualified type name after the last '/' +// in the type URL, for example "foo.bar.com/x/y.z" will yield type +// name "y.z". +// +// +// JSON +// ==== +// The JSON representation of an `Any` value uses the regular +// representation of the deserialized, embedded message, with an +// additional field `@type` which contains the type URL. Example: +// +// package google.profile; +// message Person { +// string first_name = 1; +// string last_name = 2; +// } +// +// { +// "@type": "type.googleapis.com/google.profile.Person", +// "firstName": , +// "lastName": +// } +// +// If the embedded message type is well-known and has a custom JSON +// representation, that representation will be embedded adding a field +// `value` which holds the custom JSON in addition to the `@type` +// field. Example (for message [google.protobuf.Duration][]): +// +// { +// "@type": "type.googleapis.com/google.protobuf.Duration", +// "value": "1.212s" +// } +// +message Any { + // A URL/resource name whose content describes the type of the + // serialized protocol buffer message. + // + // For URLs which use the scheme `http`, `https`, or no scheme, the + // following restrictions and interpretations apply: + // + // * If no scheme is provided, `https` is assumed. + // * The last segment of the URL's path must represent the fully + // qualified name of the type (as in `path/google.protobuf.Duration`). + // The name should be in a canonical form (e.g., leading "." is + // not accepted). + // * An HTTP GET on the URL must yield a [google.protobuf.Type][] + // value in binary format, or produce an error. + // * Applications are allowed to cache lookup results based on the + // URL, or have them precompiled into a binary to avoid any + // lookup. Therefore, binary compatibility needs to be preserved + // on changes to types. (Use versioned type names to manage + // breaking changes.) + // + // Schemes other than `http`, `https` (or the empty scheme) might be + // used with implementation specific semantics. + // + string type_url = 1; + + // Must be a valid serialized protocol buffer of the above specified type. + bytes value = 2; +} diff --git a/Firestore/Protos/protos/google/protobuf/empty.proto b/Firestore/Protos/protos/google/protobuf/empty.proto new file mode 100644 index 00000000000..03cacd23308 --- /dev/null +++ b/Firestore/Protos/protos/google/protobuf/empty.proto @@ -0,0 +1,52 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option go_package = "github.com/golang/protobuf/ptypes/empty"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "EmptyProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; +option cc_enable_arenas = true; + +// A generic empty message that you can re-use to avoid defining duplicated +// empty messages in your APIs. A typical example is to use it as the request +// or the response type of an API method. For instance: +// +// service Foo { +// rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); +// } +// +// The JSON representation for `Empty` is empty JSON object `{}`. +message Empty {} diff --git a/Firestore/Protos/protos/google/protobuf/struct.options b/Firestore/Protos/protos/google/protobuf/struct.options new file mode 100644 index 00000000000..56fa2d01581 --- /dev/null +++ b/Firestore/Protos/protos/google/protobuf/struct.options @@ -0,0 +1 @@ +google.protobuf.Value.kind no_unions:true diff --git a/Firestore/Protos/protos/google/protobuf/struct.proto b/Firestore/Protos/protos/google/protobuf/struct.proto new file mode 100644 index 00000000000..7d7808e7fbb --- /dev/null +++ b/Firestore/Protos/protos/google/protobuf/struct.proto @@ -0,0 +1,96 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option cc_enable_arenas = true; +option go_package = "github.com/golang/protobuf/ptypes/struct;structpb"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "StructProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; + + +// `Struct` represents a structured data value, consisting of fields +// which map to dynamically typed values. In some languages, `Struct` +// might be supported by a native representation. For example, in +// scripting languages like JS a struct is represented as an +// object. The details of that representation are described together +// with the proto support for the language. +// +// The JSON representation for `Struct` is JSON object. +message Struct { + // Unordered map of dynamically typed values. + map fields = 1; +} + +// `Value` represents a dynamically typed value which can be either +// null, a number, a string, a boolean, a recursive struct value, or a +// list of values. A producer of value is expected to set one of that +// variants, absence of any variant indicates an error. +// +// The JSON representation for `Value` is JSON value. +message Value { + // The kind of value. + oneof kind { + // Represents a null value. + NullValue null_value = 1; + // Represents a double value. + double number_value = 2; + // Represents a string value. + string string_value = 3; + // Represents a boolean value. + bool bool_value = 4; + // Represents a structured value. + Struct struct_value = 5; + // Represents a repeated `Value`. + ListValue list_value = 6; + } +} + +// `NullValue` is a singleton enumeration to represent the null value for the +// `Value` type union. +// +// The JSON representation for `NullValue` is JSON `null`. +enum NullValue { + // Null value. + NULL_VALUE = 0; +} + +// `ListValue` is a wrapper around a repeated field of values. +// +// The JSON representation for `ListValue` is JSON array. +message ListValue { + // Repeated field of dynamically typed values. + repeated Value values = 1; +} diff --git a/Firestore/Protos/protos/google/protobuf/timestamp.proto b/Firestore/Protos/protos/google/protobuf/timestamp.proto new file mode 100644 index 00000000000..b7cbd17502f --- /dev/null +++ b/Firestore/Protos/protos/google/protobuf/timestamp.proto @@ -0,0 +1,133 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option cc_enable_arenas = true; +option go_package = "github.com/golang/protobuf/ptypes/timestamp"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "TimestampProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; + +// A Timestamp represents a point in time independent of any time zone +// or calendar, represented as seconds and fractions of seconds at +// nanosecond resolution in UTC Epoch time. It is encoded using the +// Proleptic Gregorian Calendar which extends the Gregorian calendar +// backwards to year one. It is encoded assuming all minutes are 60 +// seconds long, i.e. leap seconds are "smeared" so that no leap second +// table is needed for interpretation. Range is from +// 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. +// By restricting to that range, we ensure that we can convert to +// and from RFC 3339 date strings. +// See [https://www.ietf.org/rfc/rfc3339.txt](https://www.ietf.org/rfc/rfc3339.txt). +// +// # Examples +// +// Example 1: Compute Timestamp from POSIX `time()`. +// +// Timestamp timestamp; +// timestamp.set_seconds(time(NULL)); +// timestamp.set_nanos(0); +// +// Example 2: Compute Timestamp from POSIX `gettimeofday()`. +// +// struct timeval tv; +// gettimeofday(&tv, NULL); +// +// Timestamp timestamp; +// timestamp.set_seconds(tv.tv_sec); +// timestamp.set_nanos(tv.tv_usec * 1000); +// +// Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`. +// +// FILETIME ft; +// GetSystemTimeAsFileTime(&ft); +// UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime; +// +// // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z +// // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z. +// Timestamp timestamp; +// timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL)); +// timestamp.set_nanos((INT32) ((ticks % 10000000) * 100)); +// +// Example 4: Compute Timestamp from Java `System.currentTimeMillis()`. +// +// long millis = System.currentTimeMillis(); +// +// Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000) +// .setNanos((int) ((millis % 1000) * 1000000)).build(); +// +// +// Example 5: Compute Timestamp from current time in Python. +// +// timestamp = Timestamp() +// timestamp.GetCurrentTime() +// +// # JSON Mapping +// +// In JSON format, the Timestamp type is encoded as a string in the +// [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the +// format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" +// where {year} is always expressed using four digits while {month}, {day}, +// {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional +// seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution), +// are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone +// is required, though only UTC (as indicated by "Z") is presently supported. +// +// For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past +// 01:30 UTC on January 15, 2017. +// +// In JavaScript, one can convert a Date object to this format using the +// standard [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString] +// method. In Python, a standard `datetime.datetime` object can be converted +// to this format using [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) +// with the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one +// can use the Joda Time's [`ISODateTimeFormat.dateTime()`]( +// http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime()) +// to obtain a formatter capable of generating timestamps in this format. +// +// +message Timestamp { + + // Represents seconds of UTC time since Unix epoch + // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to + // 9999-12-31T23:59:59Z inclusive. + int64 seconds = 1; + + // Non-negative fractions of a second at nanosecond resolution. Negative + // second values with fractions must still have non-negative nanos values + // that count forward in time. Must be from 0 to 999,999,999 + // inclusive. + int32 nanos = 2; +} diff --git a/Firestore/Protos/protos/google/protobuf/wrappers.proto b/Firestore/Protos/protos/google/protobuf/wrappers.proto new file mode 100644 index 00000000000..01947639ac4 --- /dev/null +++ b/Firestore/Protos/protos/google/protobuf/wrappers.proto @@ -0,0 +1,118 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Wrappers for primitive (non-message) types. These types are useful +// for embedding primitives in the `google.protobuf.Any` type and for places +// where we need to distinguish between the absence of a primitive +// typed field and its default value. + +syntax = "proto3"; + +package google.protobuf; + +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option cc_enable_arenas = true; +option go_package = "github.com/golang/protobuf/ptypes/wrappers"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "WrappersProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; + +// Wrapper message for `double`. +// +// The JSON representation for `DoubleValue` is JSON number. +message DoubleValue { + // The double value. + double value = 1; +} + +// Wrapper message for `float`. +// +// The JSON representation for `FloatValue` is JSON number. +message FloatValue { + // The float value. + float value = 1; +} + +// Wrapper message for `int64`. +// +// The JSON representation for `Int64Value` is JSON string. +message Int64Value { + // The int64 value. + int64 value = 1; +} + +// Wrapper message for `uint64`. +// +// The JSON representation for `UInt64Value` is JSON string. +message UInt64Value { + // The uint64 value. + uint64 value = 1; +} + +// Wrapper message for `int32`. +// +// The JSON representation for `Int32Value` is JSON number. +message Int32Value { + // The int32 value. + int32 value = 1; +} + +// Wrapper message for `uint32`. +// +// The JSON representation for `UInt32Value` is JSON number. +message UInt32Value { + // The uint32 value. + uint32 value = 1; +} + +// Wrapper message for `bool`. +// +// The JSON representation for `BoolValue` is JSON `true` and `false`. +message BoolValue { + // The bool value. + bool value = 1; +} + +// Wrapper message for `string`. +// +// The JSON representation for `StringValue` is JSON string. +message StringValue { + // The string value. + string value = 1; +} + +// Wrapper message for `bytes`. +// +// The JSON representation for `BytesValue` is JSON string. +message BytesValue { + // The bytes value. + bytes value = 1; +} diff --git a/Firestore/Source/API/FIRCollectionReference.mm b/Firestore/Source/API/FIRCollectionReference.mm index 70a14c2c103..a8de29b4089 100644 --- a/Firestore/Source/API/FIRCollectionReference.mm +++ b/Firestore/Source/API/FIRCollectionReference.mm @@ -77,10 +77,7 @@ - (BOOL)isEqual:(nullable id)other { - (BOOL)isEqualToReference:(nullable FIRCollectionReference *)reference { if (self == reference) return YES; if (reference == nil) return NO; - if (self.firestore != reference.firestore && ![self.firestore isEqual:reference.firestore]) - return NO; - if (self.query != reference.query && ![self.query isEqual:reference.query]) return NO; - return YES; + return [self.firestore isEqual:reference.firestore] && [self.query isEqual:reference.query]; } - (NSUInteger)hash { diff --git a/Firestore/Source/API/FIRDocumentChange+Internal.h b/Firestore/Source/API/FIRDocumentChange+Internal.h index 7e2e5c61d21..7c9723ce789 100644 --- a/Firestore/Source/API/FIRDocumentChange+Internal.h +++ b/Firestore/Source/API/FIRDocumentChange+Internal.h @@ -16,6 +16,7 @@ #import "FIRDocumentChange.h" +@class FIRFirestore; @class FSTViewSnapshot; NS_ASSUME_NONNULL_BEGIN diff --git a/Firestore/Source/API/FIRDocumentChange.m b/Firestore/Source/API/FIRDocumentChange.mm similarity index 100% rename from Firestore/Source/API/FIRDocumentChange.m rename to Firestore/Source/API/FIRDocumentChange.mm diff --git a/Firestore/Source/API/FIRDocumentReference.m b/Firestore/Source/API/FIRDocumentReference.mm similarity index 98% rename from Firestore/Source/API/FIRDocumentReference.m rename to Firestore/Source/API/FIRDocumentReference.mm index cd7e7887d2d..90109c46c3b 100644 --- a/Firestore/Source/API/FIRDocumentReference.m +++ b/Firestore/Source/API/FIRDocumentReference.mm @@ -125,10 +125,7 @@ - (BOOL)isEqual:(nullable id)other { - (BOOL)isEqualToReference:(nullable FIRDocumentReference *)reference { if (self == reference) return YES; if (reference == nil) return NO; - if (self.firestore != reference.firestore && ![self.firestore isEqual:reference.firestore]) - return NO; - if (self.key != reference.key && ![self.key isEqualToKey:reference.key]) return NO; - return YES; + return [self.firestore isEqual:reference.firestore] && [self.key isEqualToKey:reference.key]; } - (NSUInteger)hash { diff --git a/Firestore/Source/API/FIRDocumentSnapshot.m b/Firestore/Source/API/FIRDocumentSnapshot.mm similarity index 87% rename from Firestore/Source/API/FIRDocumentSnapshot.m rename to Firestore/Source/API/FIRDocumentSnapshot.mm index 358ddac1547..ddf4cca367f 100644 --- a/Firestore/Source/API/FIRDocumentSnapshot.m +++ b/Firestore/Source/API/FIRDocumentSnapshot.mm @@ -21,7 +21,6 @@ #import "Firestore/Source/API/FIRFirestore+Internal.h" #import "Firestore/Source/API/FIRSnapshotMetadata+Internal.h" #import "Firestore/Source/API/FIRSnapshotOptions+Internal.h" -#import "Firestore/Source/Model/FSTDatabaseID.h" #import "Firestore/Source/Model/FSTDocument.h" #import "Firestore/Source/Model/FSTDocumentKey.h" #import "Firestore/Source/Model/FSTFieldValue.h" @@ -29,6 +28,12 @@ #import "Firestore/Source/Util/FSTAssert.h" #import "Firestore/Source/Util/FSTUsageValidation.h" +#include "Firestore/core/src/firebase/firestore/model/database_id.h" +#include "Firestore/core/src/firebase/firestore/util/string_apple.h" + +namespace util = firebase::firestore::util; +using firebase::firestore::model::DatabaseId; + NS_ASSUME_NONNULL_BEGIN @interface FIRDocumentSnapshot () @@ -51,10 +56,10 @@ + (instancetype)snapshotWithFirestore:(FIRFirestore *)firestore documentKey:(FSTDocumentKey *)documentKey document:(nullable FSTDocument *)document fromCache:(BOOL)fromCache { - return [[FIRDocumentSnapshot alloc] initWithFirestore:firestore - documentKey:documentKey - document:document - fromCache:fromCache]; + return [[[self class] alloc] initWithFirestore:firestore + documentKey:documentKey + document:document + fromCache:fromCache]; } @end @@ -90,15 +95,12 @@ - (BOOL)isEqual:(nullable id)other { - (BOOL)isEqualToSnapshot:(nullable FIRDocumentSnapshot *)snapshot { if (self == snapshot) return YES; if (snapshot == nil) return NO; - if (self.firestore != snapshot.firestore && ![self.firestore isEqual:snapshot.firestore]) - return NO; - if (self.internalKey != snapshot.internalKey && ![self.internalKey isEqual:snapshot.internalKey]) - return NO; - if (self.internalDocument != snapshot.internalDocument && - ![self.internalDocument isEqual:snapshot.internalDocument]) - return NO; - if (self.fromCache != snapshot.fromCache) return NO; - return YES; + + return [self.firestore isEqual:snapshot.firestore] && + [self.internalKey isEqual:snapshot.internalKey] && + (self.internalDocument == snapshot.internalDocument || + [self.internalDocument isEqual:snapshot.internalDocument]) && + self.fromCache == snapshot.fromCache; } - (NSUInteger)hash { @@ -176,16 +178,18 @@ - (id)convertedValue:(FSTFieldValue *)value options:(FSTFieldValueOptions *)opti return [self convertedArray:(FSTArrayValue *)value options:options]; } else if ([value isKindOfClass:[FSTReferenceValue class]]) { FSTReferenceValue *ref = (FSTReferenceValue *)value; - FSTDatabaseID *refDatabase = ref.databaseID; - FSTDatabaseID *database = self.firestore.databaseID; - if (![refDatabase isEqualToDatabaseId:database]) { + const DatabaseId *refDatabase = ref.databaseID; + const DatabaseId *database = self.firestore.databaseID; + if (*refDatabase != *database) { // TODO(b/32073923): Log this as a proper warning. NSLog( @"WARNING: Document %@ contains a document reference within a different database " "(%@/%@) which is not supported. It will be treated as a reference within the " "current database (%@/%@) instead.", - self.reference.path, refDatabase.projectID, refDatabase.databaseID, database.projectID, - database.databaseID); + self.reference.path, util::WrapNSStringNoCopy(refDatabase->project_id()), + util::WrapNSStringNoCopy(refDatabase->database_id()), + util::WrapNSStringNoCopy(database->project_id()), + util::WrapNSStringNoCopy(database->database_id())); } return [FIRDocumentReference referenceWithKey:[ref valueWithOptions:options] firestore:self.firestore]; diff --git a/Firestore/Source/API/FIRFieldPath.m b/Firestore/Source/API/FIRFieldPath.mm similarity index 100% rename from Firestore/Source/API/FIRFieldPath.m rename to Firestore/Source/API/FIRFieldPath.mm diff --git a/Firestore/Source/API/FIRFieldValue.m b/Firestore/Source/API/FIRFieldValue.mm similarity index 100% rename from Firestore/Source/API/FIRFieldValue.m rename to Firestore/Source/API/FIRFieldValue.mm diff --git a/Firestore/Source/API/FIRFirestore+Internal.h b/Firestore/Source/API/FIRFirestore+Internal.h index c2e995a9481..a52533ded19 100644 --- a/Firestore/Source/API/FIRFirestore+Internal.h +++ b/Firestore/Source/API/FIRFirestore+Internal.h @@ -16,13 +16,17 @@ #import "FIRFirestore.h" +#include + +#include "Firestore/core/src/firebase/firestore/auth/credentials_provider.h" +#include "Firestore/core/src/firebase/firestore/model/database_id.h" +#include "absl/strings/string_view.h" + NS_ASSUME_NONNULL_BEGIN -@class FSTDatabaseID; @class FSTDispatchQueue; @class FSTFirestoreClient; @class FSTUserDataConverter; -@protocol FSTCredentialsProvider; @interface FIRFirestore (/* Init */) @@ -30,10 +34,11 @@ NS_ASSUME_NONNULL_BEGIN * Initializes a Firestore object with all the required parameters directly. This exists so that * tests can create FIRFirestore objects without needing FIRApp. */ -- (instancetype)initWithProjectID:(NSString *)projectID - database:(NSString *)database +- (instancetype)initWithProjectID:(const absl::string_view)projectID + database:(const absl::string_view)database persistenceKey:(NSString *)persistenceKey - credentialsProvider:(id)credentialsProvider + credentialsProvider:(std::unique_ptr) + credentialsProvider workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue firebaseApp:(FIRApp *)app; @@ -54,7 +59,8 @@ NS_ASSUME_NONNULL_BEGIN - (void)shutdownWithCompletion:(nullable void (^)(NSError *_Nullable error))completion NS_SWIFT_NAME(shutdown(completion:)); -@property(nonatomic, strong, readonly) FSTDatabaseID *databaseID; +// FIRFirestore ownes the DatabaseId instance. +@property(nonatomic, assign, readonly) const firebase::firestore::model::DatabaseId *databaseID; @property(nonatomic, strong, readonly) FSTFirestoreClient *client; @property(nonatomic, strong, readonly) FSTUserDataConverter *dataConverter; diff --git a/Firestore/Source/API/FIRFirestore.m b/Firestore/Source/API/FIRFirestore.mm similarity index 72% rename from Firestore/Source/API/FIRFirestore.m rename to Firestore/Source/API/FIRFirestore.mm index 9df37112cd9..eff0605234a 100644 --- a/Firestore/Source/API/FIRFirestore.m +++ b/Firestore/Source/API/FIRFirestore.mm @@ -16,7 +16,11 @@ #import "FIRFirestore.h" +#include +#include + #import +#import #import #import @@ -28,10 +32,7 @@ #import "Firestore/Source/API/FIRWriteBatch+Internal.h" #import "Firestore/Source/API/FSTUserDataConverter.h" -#import "Firestore/Source/Auth/FSTCredentialsProvider.h" -#import "Firestore/Source/Core/FSTDatabaseInfo.h" #import "Firestore/Source/Core/FSTFirestoreClient.h" -#import "Firestore/Source/Model/FSTDatabaseID.h" #import "Firestore/Source/Model/FSTDocumentKey.h" #import "Firestore/Source/Model/FSTPath.h" #import "Firestore/Source/Util/FSTAssert.h" @@ -39,15 +40,30 @@ #import "Firestore/Source/Util/FSTLogger.h" #import "Firestore/Source/Util/FSTUsageValidation.h" +#include "Firestore/core/src/firebase/firestore/auth/credentials_provider.h" +#include "Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.h" +#include "Firestore/core/src/firebase/firestore/core/database_info.h" +#include "Firestore/core/src/firebase/firestore/model/database_id.h" +#include "Firestore/core/src/firebase/firestore/util/string_apple.h" +#include "absl/memory/memory.h" + +namespace util = firebase::firestore::util; +using firebase::firestore::auth::CredentialsProvider; +using firebase::firestore::auth::FirebaseCredentialsProvider; +using firebase::firestore::core::DatabaseInfo; +using firebase::firestore::model::DatabaseId; + NS_ASSUME_NONNULL_BEGIN -NSString *const FIRFirestoreErrorDomain = @"FIRFirestoreErrorDomain"; +extern "C" NSString *const FIRFirestoreErrorDomain = @"FIRFirestoreErrorDomain"; -@interface FIRFirestore () +@interface FIRFirestore () { + /** The actual owned DatabaseId instance is allocated in FIRFirestore. */ + DatabaseId _databaseID; + std::unique_ptr _credentialsProvider; +} -@property(nonatomic, strong) FSTDatabaseID *databaseID; @property(nonatomic, strong) NSString *persistenceKey; -@property(nonatomic, strong) id credentialsProvider; @property(nonatomic, strong) FSTDispatchQueue *workerDispatchQueue; // Note that `client` is updated after initialization, but marking this readwrite would generate an @@ -72,6 +88,36 @@ @implementation FIRFirestore { return instances; } ++ (void)initialize { + NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; + [center addObserverForName:kFIRAppDeleteNotification + object:nil + queue:nil + usingBlock:^(NSNotification *_Nonnull note) { + NSString *appName = note.userInfo[kFIRAppNameKey]; + if (appName == nil) return; + + NSMutableDictionary *instances = [self instances]; + @synchronized(instances) { + // Since the key for instances isn't just the app name, iterate over all the + // keys to get the one(s) we have to delete. There could be multiple in case + // the user calls firestoreForApp:database:. + NSMutableArray *keysToDelete = [[NSMutableArray alloc] init]; + NSString *keyPrefix = [NSString stringWithFormat:@"%@|", appName]; + for (NSString *key in instances.allKeys) { + if ([key hasPrefix:keyPrefix]) { + [keysToDelete addObject:key]; + } + } + + // Loop through the keys found and delete them from the stored instances. + for (NSString *key in keysToDelete) { + [instances removeObjectForKey:key]; + } + } + }]; +} + + (instancetype)firestore { FIRApp *app = [FIRApp defaultApp]; if (!app) { @@ -79,11 +125,11 @@ + (instancetype)firestore { @"Failed to get FirebaseApp instance. Please call FirebaseApp.configure() " @"before using Firestore"); } - return [self firestoreForApp:app database:kDefaultDatabaseID]; + return [self firestoreForApp:app database:util::WrapNSStringNoCopy(DatabaseId::kDefault)]; } + (instancetype)firestoreForApp:(FIRApp *)app { - return [self firestoreForApp:app database:kDefaultDatabaseID]; + return [self firestoreForApp:app database:util::WrapNSStringNoCopy(DatabaseId::kDefault)]; } // TODO(b/62410906): make this public @@ -97,8 +143,11 @@ + (instancetype)firestoreForApp:(FIRApp *)app database:(NSString *)database { FSTThrowInvalidArgument( @"database identifier may not be nil. Use '%@' if you want the default " "database", - kDefaultDatabaseID); + util::WrapNSStringNoCopy(DatabaseId::kDefault)); } + + // Note: If the key format changes, please change the code that detects FIRApps being deleted + // contained in +initialize. It checks for the app's name followed by a | character. NSString *key = [NSString stringWithFormat:@"%@|%@", app.name, database]; NSMutableDictionary *instances = self.instances; @@ -111,15 +160,15 @@ + (instancetype)firestoreForApp:(FIRApp *)app database:(NSString *)database { FSTDispatchQueue *workerDispatchQueue = [FSTDispatchQueue queueWith:dispatch_queue_create("com.google.firebase.firestore", DISPATCH_QUEUE_SERIAL)]; - id credentialsProvider; - credentialsProvider = [[FSTFirebaseCredentialsProvider alloc] initWithApp:app]; + std::unique_ptr credentials_provider = + absl::make_unique(app); NSString *persistenceKey = app.name; - firestore = [[FIRFirestore alloc] initWithProjectID:projectID - database:database + firestore = [[FIRFirestore alloc] initWithProjectID:util::MakeStringView(projectID) + database:util::MakeStringView(database) persistenceKey:persistenceKey - credentialsProvider:credentialsProvider + credentialsProvider:std::move(credentials_provider) workerDispatchQueue:workerDispatchQueue firebaseApp:app]; instances[key] = firestore; @@ -129,14 +178,14 @@ + (instancetype)firestoreForApp:(FIRApp *)app database:(NSString *)database { } } -- (instancetype)initWithProjectID:(NSString *)projectID - database:(NSString *)database +- (instancetype)initWithProjectID:(const absl::string_view)projectID + database:(const absl::string_view)database persistenceKey:(NSString *)persistenceKey - credentialsProvider:(id)credentialsProvider + credentialsProvider:(std::unique_ptr)credentialsProvider workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue firebaseApp:(FIRApp *)app { if (self = [super init]) { - _databaseID = [FSTDatabaseID databaseIDWithProject:projectID database:database]; + _databaseID = DatabaseId(projectID, database); FSTPreConverterBlock block = ^id _Nullable(id _Nullable input) { if ([input isKindOfClass:[FIRDocumentReference class]]) { FIRDocumentReference *documentReference = (FIRDocumentReference *)input; @@ -147,9 +196,9 @@ - (instancetype)initWithProjectID:(NSString *)projectID } }; _dataConverter = - [[FSTUserDataConverter alloc] initWithDatabaseID:_databaseID preConverter:block]; + [[FSTUserDataConverter alloc] initWithDatabaseID:&_databaseID preConverter:block]; _persistenceKey = persistenceKey; - _credentialsProvider = credentialsProvider; + _credentialsProvider = std::move(credentialsProvider); _workerDispatchQueue = workerDispatchQueue; _app = app; _settings = [[FIRFirestoreSettings alloc] init]; @@ -193,17 +242,14 @@ - (void)ensureClientConfigured { FSTAssert(_settings.host, @"FirestoreSettings.host cannot be nil."); FSTAssert(_settings.dispatchQueue, @"FirestoreSettings.dispatchQueue cannot be nil."); - FSTDatabaseInfo *databaseInfo = - [FSTDatabaseInfo databaseInfoWithDatabaseID:_databaseID - persistenceKey:_persistenceKey - host:_settings.host - sslEnabled:_settings.sslEnabled]; + const DatabaseInfo database_info(*self.databaseID, util::MakeStringView(_persistenceKey), + util::MakeStringView(_settings.host), _settings.sslEnabled); FSTDispatchQueue *userDispatchQueue = [FSTDispatchQueue queueWith:_settings.dispatchQueue]; - _client = [FSTFirestoreClient clientWithDatabaseInfo:databaseInfo + _client = [FSTFirestoreClient clientWithDatabaseInfo:database_info usePersistence:_settings.persistenceEnabled - credentialsProvider:_credentialsProvider + credentialsProvider:_credentialsProvider.get() userDispatchQueue:userDispatchQueue workerDispatchQueue:_workerDispatchQueue]; } @@ -312,6 +358,10 @@ - (void)disableNetworkWithCompletion:(nullable void (^)(NSError *_Nullable))comp [self.client disableNetworkWithCompletion:completion]; } +- (const DatabaseId *)databaseID { + return &_databaseID; +} + @end NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/API/FIRFirestoreSettings.m b/Firestore/Source/API/FIRFirestoreSettings.mm similarity index 100% rename from Firestore/Source/API/FIRFirestoreSettings.m rename to Firestore/Source/API/FIRFirestoreSettings.mm diff --git a/Firestore/Source/API/FIRFirestoreVersion.m b/Firestore/Source/API/FIRFirestoreVersion.mm similarity index 93% rename from Firestore/Source/API/FIRFirestoreVersion.m rename to Firestore/Source/API/FIRFirestoreVersion.mm index 4f8bb28c7df..b1fe480cd5e 100644 --- a/Firestore/Source/API/FIRFirestoreVersion.m +++ b/Firestore/Source/API/FIRFirestoreVersion.mm @@ -25,5 +25,5 @@ #define STR(x) STR_EXPAND(x) #define STR_EXPAND(x) #x -const unsigned char *const FirebaseFirestoreVersionString = +extern "C" const unsigned char *const FirebaseFirestoreVersionString = (const unsigned char *const)STR(FIRFirestore_VERSION); diff --git a/Firestore/Source/API/FIRGeoPoint.m b/Firestore/Source/API/FIRGeoPoint.mm similarity index 77% rename from Firestore/Source/API/FIRGeoPoint.m rename to Firestore/Source/API/FIRGeoPoint.mm index 72e9e7da770..8d896330279 100644 --- a/Firestore/Source/API/FIRGeoPoint.m +++ b/Firestore/Source/API/FIRGeoPoint.mm @@ -16,9 +16,14 @@ #import "Firestore/Source/API/FIRGeoPoint+Internal.h" -#import "Firestore/Source/Util/FSTComparison.h" +#import "Firestore/core/src/firebase/firestore/util/comparison.h" + #import "Firestore/Source/Util/FSTUsageValidation.h" +using firebase::firestore::util::DoubleBitwiseEquals; +using firebase::firestore::util::DoubleBitwiseHash; +using firebase::firestore::util::WrapCompare; + NS_ASSUME_NONNULL_BEGIN @implementation FIRGeoPoint @@ -45,11 +50,11 @@ - (instancetype)initWithLatitude:(double)latitude longitude:(double)longitude { } - (NSComparisonResult)compare:(FIRGeoPoint *)other { - NSComparisonResult result = FSTCompareDoubles(self.latitude, other.latitude); + NSComparisonResult result = WrapCompare(self.latitude, other.latitude); if (result != NSOrderedSame) { return result; } else { - return FSTCompareDoubles(self.longitude, other.longitude); + return WrapCompare(self.longitude, other.longitude); } } @@ -67,12 +72,12 @@ - (BOOL)isEqual:(id)other { return NO; } FIRGeoPoint *otherGeoPoint = (FIRGeoPoint *)other; - return FSTDoubleBitwiseEquals(self.latitude, otherGeoPoint.latitude) && - FSTDoubleBitwiseEquals(self.longitude, otherGeoPoint.longitude); + return DoubleBitwiseEquals(self.latitude, otherGeoPoint.latitude) && + DoubleBitwiseEquals(self.longitude, otherGeoPoint.longitude); } - (NSUInteger)hash { - return 31 * FSTDoubleBitwiseHash(self.latitude) + FSTDoubleBitwiseHash(self.longitude); + return 31 * DoubleBitwiseHash(self.latitude) + DoubleBitwiseHash(self.longitude); } /** Implements NSCopying without actually copying because geopoints are immutable. */ diff --git a/Firestore/Source/API/FIRListenerRegistration.m b/Firestore/Source/API/FIRListenerRegistration.mm similarity index 100% rename from Firestore/Source/API/FIRListenerRegistration.m rename to Firestore/Source/API/FIRListenerRegistration.mm diff --git a/Firestore/Source/API/FIRQuery.m b/Firestore/Source/API/FIRQuery.mm similarity index 99% rename from Firestore/Source/API/FIRQuery.m rename to Firestore/Source/API/FIRQuery.mm index cb62016b60b..af2df4cb4e1 100644 --- a/Firestore/Source/API/FIRQuery.m +++ b/Firestore/Source/API/FIRQuery.mm @@ -117,9 +117,8 @@ - (BOOL)isEqual:(nullable id)other { - (BOOL)isEqualToQuery:(nullable FIRQuery *)query { if (self == query) return YES; if (query == nil) return NO; - if (self.firestore != query.firestore && ![self.firestore isEqual:query.firestore]) return NO; - if (self.query != query.query && ![self.query isEqual:query.query]) return NO; - return YES; + + return [self.firestore isEqual:query.firestore] && [self.query isEqual:query.query]; } - (NSUInteger)hash { diff --git a/Firestore/Source/API/FIRQuerySnapshot.m b/Firestore/Source/API/FIRQuerySnapshot.mm similarity index 91% rename from Firestore/Source/API/FIRQuerySnapshot.m rename to Firestore/Source/API/FIRQuerySnapshot.mm index 177e461b176..abee84c0402 100644 --- a/Firestore/Source/API/FIRQuerySnapshot.m +++ b/Firestore/Source/API/FIRQuerySnapshot.mm @@ -88,14 +88,10 @@ - (BOOL)isEqual:(nullable id)other { - (BOOL)isEqualToSnapshot:(nullable FIRQuerySnapshot *)snapshot { if (self == snapshot) return YES; if (snapshot == nil) return NO; - if (self.firestore != snapshot.firestore && ![self.firestore isEqual:snapshot.firestore]) - return NO; - if (self.originalQuery != snapshot.originalQuery && - ![self.originalQuery isEqual:snapshot.originalQuery]) - return NO; - if (self.snapshot != snapshot.snapshot && ![self.snapshot isEqual:snapshot.snapshot]) return NO; - if (self.metadata != snapshot.metadata && ![self.metadata isEqual:snapshot.metadata]) return NO; - return YES; + + return [self.firestore isEqual:snapshot.firestore] && + [self.originalQuery isEqual:snapshot.originalQuery] && + [self.snapshot isEqual:snapshot.snapshot] && [self.metadata isEqual:snapshot.metadata]; } - (NSUInteger)hash { diff --git a/Firestore/Source/API/FIRSetOptions.m b/Firestore/Source/API/FIRSetOptions.mm similarity index 97% rename from Firestore/Source/API/FIRSetOptions.m rename to Firestore/Source/API/FIRSetOptions.mm index 743bcc77260..b41086c01b3 100644 --- a/Firestore/Source/API/FIRSetOptions.m +++ b/Firestore/Source/API/FIRSetOptions.mm @@ -39,8 +39,7 @@ - (BOOL)isEqual:(id)other { } FIRSetOptions *otherOptions = (FIRSetOptions *)other; - - return otherOptions.merge != self.merge; + return otherOptions.merge == self.merge; } - (NSUInteger)hash { diff --git a/Firestore/Source/API/FIRSnapshotMetadata.m b/Firestore/Source/API/FIRSnapshotMetadata.mm similarity index 93% rename from Firestore/Source/API/FIRSnapshotMetadata.m rename to Firestore/Source/API/FIRSnapshotMetadata.mm index d957a8d99ff..27747cee260 100644 --- a/Firestore/Source/API/FIRSnapshotMetadata.m +++ b/Firestore/Source/API/FIRSnapshotMetadata.mm @@ -55,9 +55,8 @@ - (BOOL)isEqual:(nullable id)other { - (BOOL)isEqualToMetadata:(nullable FIRSnapshotMetadata *)metadata { if (self == metadata) return YES; if (metadata == nil) return NO; - if (self.pendingWrites != metadata.pendingWrites) return NO; - if (self.fromCache != metadata.fromCache) return NO; - return YES; + + return self.pendingWrites == metadata.pendingWrites && self.fromCache == metadata.fromCache; } - (NSUInteger)hash { diff --git a/Firestore/Source/API/FIRSnapshotOptions.m b/Firestore/Source/API/FIRSnapshotOptions.mm similarity index 100% rename from Firestore/Source/API/FIRSnapshotOptions.m rename to Firestore/Source/API/FIRSnapshotOptions.mm diff --git a/Firestore/Source/API/FIRTimestamp+Internal.h b/Firestore/Source/API/FIRTimestamp+Internal.h new file mode 100644 index 00000000000..48e38b2e0c2 --- /dev/null +++ b/Firestore/Source/API/FIRTimestamp+Internal.h @@ -0,0 +1,35 @@ +/* + * Copyright 2018 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 "FIRTimestamp.h" + +NS_ASSUME_NONNULL_BEGIN + +/** Internal FIRTimestamp API we don't want exposed in our public header files. */ +@interface FIRTimestamp (Internal) + +/** + * Converts the given date to an ISO 8601 timestamp string, useful for rendering in JSON. + * + * ISO 8601 dates times in UTC look like this: "1912-04-14T23:40:00.000000000Z". + * + * @see http://www.ecma-international.org/ecma-262/6.0/#sec-date-time-string-format + */ +- (NSString *)ISO8601String; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Core/FSTTimestamp.m b/Firestore/Source/API/FIRTimestamp.m similarity index 52% rename from Firestore/Source/Core/FSTTimestamp.m rename to Firestore/Source/API/FIRTimestamp.m index 6d9e3141b12..489b921c6aa 100644 --- a/Firestore/Source/Core/FSTTimestamp.m +++ b/Firestore/Source/API/FIRTimestamp.m @@ -14,23 +14,36 @@ * limitations under the License. */ -#import "Firestore/Source/Core/FSTTimestamp.h" - -#import "Firestore/Source/Util/FSTAssert.h" -#import "Firestore/Source/Util/FSTComparison.h" +#import "Firestore/Source/API/FIRTimestamp+Internal.h" NS_ASSUME_NONNULL_BEGIN static const int kNanosPerSecond = 1000000000; -@implementation FSTTimestamp +@implementation FIRTimestamp (Internal) -#pragma mark - Constructors +#pragma mark - Internal public methods -+ (instancetype)timestamp { - return [FSTTimestamp timestampWithDate:[NSDate date]]; +- (NSString *)ISO8601String { + NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; + formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss"; + formatter.timeZone = [NSTimeZone timeZoneWithName:@"UTC"]; + NSDate *secondsDate = [NSDate dateWithTimeIntervalSince1970:self.seconds]; + NSString *secondsString = [formatter stringFromDate:secondsDate]; + if (secondsString.length != 19) { + [NSException raise:@"Invalid ISO string" format:@"Invalid ISO string: %@", secondsString]; + } + + NSString *nanosString = [NSString stringWithFormat:@"%09d", self.nanoseconds]; + return [NSString stringWithFormat:@"%@.%@Z", secondsString, nanosString]; } +@end + +@implementation FIRTimestamp + +#pragma mark - Constructors + + (instancetype)timestampWithDate:(NSDate *)date { double secondsDouble; double fraction = modf(date.timeIntervalSince1970, &secondsDouble); @@ -41,21 +54,41 @@ + (instancetype)timestampWithDate:(NSDate *)date { } int64_t seconds = (int64_t)secondsDouble; int32_t nanos = (int32_t)(fraction * kNanosPerSecond); - return [[FSTTimestamp alloc] initWithSeconds:seconds nanos:nanos]; + return [[FIRTimestamp alloc] initWithSeconds:seconds nanoseconds:nanos]; +} + ++ (instancetype)timestampWithSeconds:(int64_t)seconds nanoseconds:(int32_t)nanoseconds { + return [[FIRTimestamp alloc] initWithSeconds:seconds nanoseconds:nanoseconds]; } -- (instancetype)initWithSeconds:(int64_t)seconds nanos:(int32_t)nanos { ++ (instancetype)timestamp { + return [FIRTimestamp timestampWithDate:[NSDate date]]; +} + +- (instancetype)initWithSeconds:(int64_t)seconds nanoseconds:(int32_t)nanoseconds { self = [super init]; if (self) { - FSTAssert(nanos >= 0, @"timestamp nanoseconds out of range: %d", nanos); - FSTAssert(nanos < 1e9, @"timestamp nanoseconds out of range: %d", nanos); - // Midnight at the beginning of 1/1/1 is the earliest timestamp Firestore supports. - FSTAssert(seconds >= -62135596800L, @"timestamp seconds out of range: %lld", seconds); + if (nanoseconds < 0) { + [NSException raise:@"Invalid timestamp" + format:@"Timestamp nanoseconds out of range: %d", nanoseconds]; + } + if (nanoseconds >= 1e9) { + [NSException raise:@"Invalid timestamp" + format:@"Timestamp nanoseconds out of range: %d", nanoseconds]; + } + // Midnight at the beginning of 1/1/1 is the earliest timestamp supported. + if (seconds < -62135596800L) { + [NSException raise:@"Invalid timestamp" + format:@"Timestamp seconds out of range: %lld", seconds]; + } // This will break in the year 10,000. - FSTAssert(seconds < 253402300800L, @"timestamp seconds out of range: %lld", seconds); + if (seconds >= 253402300800L) { + [NSException raise:@"Invalid timestamp" + format:@"Timestamp seconds out of range: %lld", seconds]; + } _seconds = seconds; - _nanos = nanos; + _nanoseconds = nanoseconds; } return self; } @@ -66,19 +99,19 @@ - (BOOL)isEqual:(id)object { if (self == object) { return YES; } - if (![object isKindOfClass:[FSTTimestamp class]]) { + if (![object isKindOfClass:[FIRTimestamp class]]) { return NO; } - return [self isEqualToTimestamp:(FSTTimestamp *)object]; + return [self isEqualToTimestamp:(FIRTimestamp *)object]; } - (NSUInteger)hash { - return (NSUInteger)((self.seconds >> 32) ^ self.seconds ^ self.nanos); + return (NSUInteger)((self.seconds >> 32) ^ self.seconds ^ self.nanoseconds); } - (NSString *)description { - return [NSString - stringWithFormat:@"", self.seconds, self.nanos]; + return [NSString stringWithFormat:@"FIRTimestamp: seconds=%lld nanoseconds=%d>", self.seconds, + self.nanoseconds]; } /** Implements NSCopying without actually copying because timestamps are immutable. */ @@ -89,32 +122,29 @@ - (id)copyWithZone:(NSZone *_Nullable)zone { #pragma mark - Public methods - (NSDate *)approximateDateValue { - NSTimeInterval interval = (NSTimeInterval)self.seconds + ((NSTimeInterval)self.nanos) / 1e9; + NSTimeInterval interval = (NSTimeInterval)self.seconds + ((NSTimeInterval)self.nanoseconds) / 1e9; return [NSDate dateWithTimeIntervalSince1970:interval]; } -- (BOOL)isEqualToTimestamp:(FSTTimestamp *)other { - return [self compare:other] == NSOrderedSame; -} - -- (NSString *)ISO8601String { - NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; - formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss"; - formatter.timeZone = [NSTimeZone timeZoneWithName:@"UTC"]; - NSDate *secondsDate = [NSDate dateWithTimeIntervalSince1970:self.seconds]; - NSString *secondsString = [formatter stringFromDate:secondsDate]; - FSTAssert(secondsString.length == 19, @"Invalid ISO string: %@", secondsString); +- (NSComparisonResult)compare:(FIRTimestamp *)other { + if (self.seconds < other.seconds) { + return NSOrderedAscending; + } else if (self.seconds > other.seconds) { + return NSOrderedDescending; + } - NSString *nanosString = [NSString stringWithFormat:@"%09d", self.nanos]; - return [NSString stringWithFormat:@"%@.%@Z", secondsString, nanosString]; + if (self.nanoseconds < other.nanoseconds) { + return NSOrderedAscending; + } else if (self.nanoseconds > other.nanoseconds) { + return NSOrderedDescending; + } + return NSOrderedSame; } -- (NSComparisonResult)compare:(FSTTimestamp *)other { - NSComparisonResult result = FSTCompareInt64s(self.seconds, other.seconds); - if (result != NSOrderedSame) { - return result; - } - return FSTCompareInt32s(self.nanos, other.nanos); +#pragma mark - Private methods + +- (BOOL)isEqualToTimestamp:(FIRTimestamp *)other { + return [self compare:other] == NSOrderedSame; } @end diff --git a/Firestore/Source/API/FIRTransaction.m b/Firestore/Source/API/FIRTransaction.mm similarity index 100% rename from Firestore/Source/API/FIRTransaction.m rename to Firestore/Source/API/FIRTransaction.mm diff --git a/Firestore/Source/API/FIRWriteBatch.m b/Firestore/Source/API/FIRWriteBatch.mm similarity index 100% rename from Firestore/Source/API/FIRWriteBatch.m rename to Firestore/Source/API/FIRWriteBatch.mm diff --git a/Firestore/Source/API/FSTUserDataConverter.h b/Firestore/Source/API/FSTUserDataConverter.h index 2c52ad8be57..10588486d18 100644 --- a/Firestore/Source/API/FSTUserDataConverter.h +++ b/Firestore/Source/API/FSTUserDataConverter.h @@ -16,8 +16,9 @@ #import +#include "Firestore/core/src/firebase/firestore/model/database_id.h" + @class FIRSetOptions; -@class FSTDatabaseID; @class FSTDocumentKey; @class FSTObjectValue; @class FSTFieldMask; @@ -87,10 +88,12 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)init NS_UNAVAILABLE; - (instancetype)initWithKey:(FSTDocumentKey *)key - databaseID:(FSTDatabaseID *)databaseID NS_DESIGNATED_INITIALIZER; + databaseID:(const firebase::firestore::model::DatabaseId *)databaseID + NS_DESIGNATED_INITIALIZER; @property(nonatomic, strong, readonly) FSTDocumentKey *key; -@property(nonatomic, strong, readonly) FSTDatabaseID *databaseID; +// Does not own the DatabaseId instance. +@property(nonatomic, assign, readonly) const firebase::firestore::model::DatabaseId *databaseID; @end @@ -107,7 +110,7 @@ typedef id _Nullable (^FSTPreConverterBlock)(id _Nullable); @interface FSTUserDataConverter : NSObject - (instancetype)init NS_UNAVAILABLE; -- (instancetype)initWithDatabaseID:(FSTDatabaseID *)databaseID +- (instancetype)initWithDatabaseID:(const firebase::firestore::model::DatabaseId *)databaseID preConverter:(FSTPreConverterBlock)preConverter NS_DESIGNATED_INITIALIZER; /** Parse document data from a non-merge setData call.*/ diff --git a/Firestore/Source/API/FSTUserDataConverter.m b/Firestore/Source/API/FSTUserDataConverter.mm similarity index 94% rename from Firestore/Source/API/FSTUserDataConverter.m rename to Firestore/Source/API/FSTUserDataConverter.mm index 414aadbe3ca..5f89b8ef986 100644 --- a/Firestore/Source/API/FSTUserDataConverter.m +++ b/Firestore/Source/API/FSTUserDataConverter.mm @@ -16,14 +16,14 @@ #import "Firestore/Source/API/FSTUserDataConverter.h" +#import "FIRTimestamp.h" + #import "FIRGeoPoint.h" #import "Firestore/Source/API/FIRDocumentReference+Internal.h" #import "Firestore/Source/API/FIRFieldPath+Internal.h" #import "Firestore/Source/API/FIRFieldValue+Internal.h" #import "Firestore/Source/API/FIRFirestore+Internal.h" #import "Firestore/Source/API/FIRSetOptions+Internal.h" -#import "Firestore/Source/Core/FSTTimestamp.h" -#import "Firestore/Source/Model/FSTDatabaseID.h" #import "Firestore/Source/Model/FSTDocumentKey.h" #import "Firestore/Source/Model/FSTFieldValue.h" #import "Firestore/Source/Model/FSTMutation.h" @@ -31,6 +31,12 @@ #import "Firestore/Source/Util/FSTAssert.h" #import "Firestore/Source/Util/FSTUsageValidation.h" +#include "Firestore/core/src/firebase/firestore/model/database_id.h" +#include "Firestore/core/src/firebase/firestore/util/string_apple.h" + +namespace util = firebase::firestore::util; +using firebase::firestore::model::DatabaseId; + NS_ASSUME_NONNULL_BEGIN static NSString *const RESERVED_FIELD_DESIGNATOR = @"__"; @@ -271,7 +277,7 @@ - (void)validatePathSegment:(NSString *)segment { @implementation FSTDocumentKeyReference -- (instancetype)initWithKey:(FSTDocumentKey *)key databaseID:(FSTDatabaseID *)databaseID { +- (instancetype)initWithKey:(FSTDocumentKey *)key databaseID:(const DatabaseId *)databaseID { self = [super init]; if (self) { _key = key; @@ -285,13 +291,14 @@ - (instancetype)initWithKey:(FSTDocumentKey *)key databaseID:(FSTDatabaseID *)da #pragma mark - FSTUserDataConverter @interface FSTUserDataConverter () -@property(strong, nonatomic, readonly) FSTDatabaseID *databaseID; +// Does not own the DatabaseId instance. +@property(assign, nonatomic, readonly) const DatabaseId *databaseID; @property(strong, nonatomic, readonly) FSTPreConverterBlock preConverter; @end @implementation FSTUserDataConverter -- (instancetype)initWithDatabaseID:(FSTDatabaseID *)databaseID +- (instancetype)initWithDatabaseID:(const DatabaseId *)databaseID preConverter:(FSTPreConverterBlock)preConverter { self = [super init]; if (self) { @@ -530,7 +537,14 @@ - (nullable FSTFieldValue *)parseScalarValue:(nullable id)input context:(FSTPars return [FSTStringValue stringValue:input]; } else if ([input isKindOfClass:[NSDate class]]) { - return [FSTTimestampValue timestampValue:[FSTTimestamp timestampWithDate:input]]; + return [FSTTimestampValue timestampValue:[FIRTimestamp timestampWithDate:input]]; + + } else if ([input isKindOfClass:[FIRTimestamp class]]) { + FIRTimestamp *originalTimestamp = (FIRTimestamp *)input; + FIRTimestamp *truncatedTimestamp = + [FIRTimestamp timestampWithSeconds:originalTimestamp.seconds + nanoseconds:originalTimestamp.nanoseconds / 1000 * 1000]; + return [FSTTimestampValue timestampValue:truncatedTimestamp]; } else if ([input isKindOfClass:[FIRGeoPoint class]]) { return [FSTGeoPointValue geoPointValue:input]; @@ -540,12 +554,14 @@ - (nullable FSTFieldValue *)parseScalarValue:(nullable id)input context:(FSTPars } else if ([input isKindOfClass:[FSTDocumentKeyReference class]]) { FSTDocumentKeyReference *reference = input; - if (![reference.databaseID isEqual:self.databaseID]) { - FSTDatabaseID *other = reference.databaseID; + if (*reference.databaseID != *self.databaseID) { + const DatabaseId *other = reference.databaseID; FSTThrowInvalidArgument( @"Document Reference is for database %@/%@ but should be for database %@/%@%@", - other.projectID, other.databaseID, self.databaseID.projectID, self.databaseID.databaseID, - [context fieldDescription]); + util::WrapNSStringNoCopy(other->project_id()), + util::WrapNSStringNoCopy(other->database_id()), + util::WrapNSStringNoCopy(self.databaseID->project_id()), + util::WrapNSStringNoCopy(self.databaseID->database_id()), [context fieldDescription]); } return [FSTReferenceValue referenceValue:reference.key databaseID:self.databaseID]; diff --git a/Firestore/Source/Auth/FSTCredentialsProvider.h b/Firestore/Source/Auth/FSTCredentialsProvider.h deleted file mode 100644 index 92d5fdc77b7..00000000000 --- a/Firestore/Source/Auth/FSTCredentialsProvider.h +++ /dev/null @@ -1,113 +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 - -NS_ASSUME_NONNULL_BEGIN - -@class FIRApp; -@class FSTDispatchQueue; -@class FSTUser; - -#pragma mark - FSTGetTokenResult - -/** - * The current FSTUser and the authentication token provided by the underlying authentication - * mechanism. This is the result of calling -[FSTCredentialsProvider getTokenForcingRefresh]. - * - * ## Portability notes: no TokenType on iOS - * - * The TypeScript client supports 1st party Oauth tokens (for the Firebase Console to auth as the - * developer) and OAuth2 tokens for the node.js sdk to auth with a service account. We don't have - * plans to support either case on mobile so there's no TokenType here. - */ -// TODO(mcg): Rename FSTToken, change parameter order to line up with the other platforms. -@interface FSTGetTokenResult : NSObject - -- (instancetype)init NS_UNAVAILABLE; -- (instancetype)initWithUser:(FSTUser *)user - token:(NSString *_Nullable)token NS_DESIGNATED_INITIALIZER; - -/** The user with which the token is associated (used for persisting user state on disk, etc.). */ -@property(nonatomic, nonnull, readonly) FSTUser *user; - -/** The actual raw token. */ -@property(nonatomic, copy, nullable, readonly) NSString *token; - -@end - -#pragma mark - Typedefs - -/** - * `FSTVoidTokenErrorBlock` is a block that gets a token or an error. - * - * @param token An auth token as a string. - * @param error The error if one occurred, or else `nil`. - */ -typedef void (^FSTVoidGetTokenResultBlock)(FSTGetTokenResult *_Nullable token, - NSError *_Nullable error); - -/** Listener block notified with an FSTUser. */ -typedef void (^FSTVoidUserBlock)(FSTUser *user); - -#pragma mark - FSTCredentialsProvider - -/** Provides methods for getting the uid and token for the current user and listen for changes. */ -@protocol FSTCredentialsProvider - -/** Requests token for the current user, optionally forcing a refreshed token to be fetched. */ -- (void)getTokenForcingRefresh:(BOOL)forceRefresh completion:(FSTVoidGetTokenResultBlock)completion; - -/** - * A listener to be notified of user changes (sign-in / sign-out). It is immediately called once - * with the initial user. - * - * Note that this block will be called back on an arbitrary thread that is not the normal Firestore - * worker thread. - */ -@property(nonatomic, copy, nullable, readwrite) FSTVoidUserBlock userChangeListener; - -@end - -#pragma mark - FSTFirebaseCredentialsProvider - -/** - * `FSTFirebaseCredentialsProvider` uses Firebase Auth via `FIRApp` to get an auth token. - * - * NOTE: To simplify the implementation, it requires that you set `userChangeListener` with a - * non-`nil` value no more than once and don't call `getTokenForcingRefresh:` after setting - * it to `nil`. - * - * This class must be implemented in a thread-safe manner since it is accessed from the thread - * backing our internal worker queue and the callbacks from FIRAuth will be executed on an - * arbitrary different thread. - */ -@interface FSTFirebaseCredentialsProvider : NSObject - -/** - * Initializes a new FSTFirebaseCredentialsProvider. - * - * @param app The Firebase app from which to get credentials. - * - * @return A new instance of FSTFirebaseCredentialsProvider. - */ -- (instancetype)initWithApp:(FIRApp *)app NS_DESIGNATED_INITIALIZER; - -- (id)init NS_UNAVAILABLE; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Auth/FSTCredentialsProvider.m b/Firestore/Source/Auth/FSTCredentialsProvider.m deleted file mode 100644 index 653d7ff2c45..00000000000 --- a/Firestore/Source/Auth/FSTCredentialsProvider.m +++ /dev/null @@ -1,164 +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/Source/Auth/FSTCredentialsProvider.h" - -#import -#import -#import - -#import "FIRFirestoreErrors.h" -#import "Firestore/Source/Auth/FSTUser.h" -#import "Firestore/Source/Util/FSTAssert.h" -#import "Firestore/Source/Util/FSTClasses.h" -#import "Firestore/Source/Util/FSTDispatchQueue.h" - -NS_ASSUME_NONNULL_BEGIN - -#pragma mark - FSTGetTokenResult - -@implementation FSTGetTokenResult -- (instancetype)initWithUser:(FSTUser *)user token:(NSString *_Nullable)token { - if (self = [super init]) { - _user = user; - _token = token; - } - return self; -} -@end - -#pragma mark - FSTFirebaseCredentialsProvider -@interface FSTFirebaseCredentialsProvider () - -@property(nonatomic, strong, readonly) FIRApp *app; - -/** Handle used to stop receiving auth changes once userChangeListener is removed. */ -@property(nonatomic, strong, nullable, readwrite) id authListenerHandle; - -/** The current user as reported to us via our AuthStateDidChangeListener. */ -@property(nonatomic, strong, nonnull, readwrite) FSTUser *currentUser; - -/** - * Counter used to detect if the user changed while a -getTokenForcingRefresh: request was - * outstanding. - */ -@property(nonatomic, assign, readwrite) int userCounter; - -@end - -@implementation FSTFirebaseCredentialsProvider { - FSTVoidUserBlock _userChangeListener; -} - -- (instancetype)initWithApp:(FIRApp *)app { - self = [super init]; - if (self) { - _app = app; - _currentUser = [[FSTUser alloc] initWithUID:[self.app getUID]]; - _userCounter = 0; - - // Register for user changes so that we can internally track the current user. - FSTWeakify(self); - _authListenerHandle = [[NSNotificationCenter defaultCenter] - addObserverForName:FIRAuthStateDidChangeInternalNotification - object:nil - queue:nil - usingBlock:^(NSNotification *notification) { - FSTStrongify(self); - if (self) { - @synchronized(self) { - NSDictionary *userInfo = notification.userInfo; - - // ensure we're only notifiying for the current app. - FIRApp *notifiedApp = - userInfo[FIRAuthStateDidChangeInternalNotificationAppKey]; - if (![self.app isEqual:notifiedApp]) { - return; - } - - NSString *userID = userInfo[FIRAuthStateDidChangeInternalNotificationUIDKey]; - FSTUser *newUser = [[FSTUser alloc] initWithUID:userID]; - if (![newUser isEqual:self.currentUser]) { - self.currentUser = newUser; - self.userCounter++; - FSTVoidUserBlock listenerBlock = self.userChangeListener; - if (listenerBlock) { - listenerBlock(self.currentUser); - } - } - } - } - }]; - } - return self; -} - -- (void)getTokenForcingRefresh:(BOOL)forceRefresh - completion:(FSTVoidGetTokenResultBlock)completion { - FSTAssert(self.authListenerHandle, @"getToken cannot be called after listener removed."); - - // Take note of the current value of the userCounter so that this method can fail (with a - // FIRFirestoreErrorCodeAborted error) if there is a user change while the request is outstanding. - int initialUserCounter = self.userCounter; - - void (^getTokenCallback)(NSString *, NSError *) = - ^(NSString *_Nullable token, NSError *_Nullable error) { - @synchronized(self) { - if (initialUserCounter != self.userCounter) { - // Cancel the request since the user changed while the request was outstanding so the - // response is likely for a previous user (which user, we can't be sure). - NSDictionary *errorInfo = @{@"details" : @"getToken aborted due to user change."}; - NSError *cancelError = [NSError errorWithDomain:FIRFirestoreErrorDomain - code:FIRFirestoreErrorCodeAborted - userInfo:errorInfo]; - completion(nil, cancelError); - } else { - FSTGetTokenResult *result = - [[FSTGetTokenResult alloc] initWithUser:self.currentUser token:token]; - completion(result, error); - } - }; - }; - - [self.app getTokenForcingRefresh:forceRefresh withCallback:getTokenCallback]; -} - -- (void)setUserChangeListener:(nullable FSTVoidUserBlock)block { - @synchronized(self) { - if (block) { - FSTAssert(!_userChangeListener, @"UserChangeListener set twice!"); - - // Fire initial event. - block(self.currentUser); - } else { - FSTAssert(self.authListenerHandle, @"UserChangeListener removed twice!"); - FSTAssert(_userChangeListener, @"UserChangeListener removed without being set!"); - [[NSNotificationCenter defaultCenter] removeObserver:self.authListenerHandle]; - self.authListenerHandle = nil; - } - _userChangeListener = block; - } -} - -- (nullable FSTVoidUserBlock)userChangeListener { - @synchronized(self) { - return _userChangeListener; - } -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Auth/FSTEmptyCredentialsProvider.m b/Firestore/Source/Auth/FSTEmptyCredentialsProvider.m deleted file mode 100644 index e78452a9df6..00000000000 --- a/Firestore/Source/Auth/FSTEmptyCredentialsProvider.m +++ /dev/null @@ -1,47 +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/Source/Auth/FSTEmptyCredentialsProvider.h" - -#import "Firestore/Source/Auth/FSTUser.h" -#import "Firestore/Source/Util/FSTAssert.h" -#import "Firestore/Source/Util/FSTDispatchQueue.h" - -NS_ASSUME_NONNULL_BEGIN - -@implementation FSTEmptyCredentialsProvider - -- (void)getTokenForcingRefresh:(BOOL)forceRefresh - completion:(FSTVoidGetTokenResultBlock)completion { - completion(nil, nil); -} - -- (void)setUserChangeListener:(nullable FSTVoidUserBlock)block { - // Since the user never changes, we just need to fire the initial event and don't need to hang - // onto the block. - if (block) { - block([FSTUser unauthenticatedUser]); - } -} - -- (nullable FSTVoidUserBlock)userChangeListener { - // TODO(mikelehen): Implementation omitted for convenience since it's not actually required. - FSTFail(@"Not implemented."); -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Auth/FSTUser.h b/Firestore/Source/Auth/FSTUser.h deleted file mode 100644 index 68ecc4c2801..00000000000 --- a/Firestore/Source/Auth/FSTUser.h +++ /dev/null @@ -1,43 +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 - -NS_ASSUME_NONNULL_BEGIN - -/** - * Simple wrapper around a nullable UID. Mostly exists to make code more readable and for use as - * a key in dictionaries (since keys cannot be nil). - */ -@interface FSTUser : NSObject - -/** Returns an FSTUser with a nil UID. */ -+ (instancetype)unauthenticatedUser; - -// Porting note: no GOOGLE_CREDENTIALS or FIRST_PARTY equivalent on iOS, see FSTGetTokenResult for -// more details. - -/** Initializes an FSTUser with the given UID. */ -- (instancetype)initWithUID:(NSString *_Nullable)UID NS_DESIGNATED_INITIALIZER; -- (instancetype)init NS_UNAVAILABLE; - -@property(nonatomic, copy, nullable, readonly) NSString *UID; - -@property(nonatomic, assign, readonly, getter=isUnauthenticated) BOOL unauthenticated; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Auth/FSTUser.m b/Firestore/Source/Auth/FSTUser.m deleted file mode 100644 index 605b4e65c04..00000000000 --- a/Firestore/Source/Auth/FSTUser.m +++ /dev/null @@ -1,68 +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/Source/Auth/FSTUser.h" - -NS_ASSUME_NONNULL_BEGIN - -#pragma mark - FSTUser - -@implementation FSTUser - -@dynamic unauthenticated; - -+ (instancetype)unauthenticatedUser { - return [[FSTUser alloc] initWithUID:nil]; -} - -- (instancetype)initWithUID:(NSString *_Nullable)UID { - if (self = [super init]) { - _UID = UID; - } - return self; -} - -- (BOOL)isEqual:(id)object { - if (self == object) { - return YES; - } else if (![object isKindOfClass:[FSTUser class]]) { - return NO; - } else { - FSTUser *other = object; - return (self.isUnauthenticated && other.isUnauthenticated) || - [self.UID isEqualToString:other.UID]; - } -} - -- (NSUInteger)hash { - return [self.UID hash]; -} - -- (id)copyWithZone:(nullable NSZone *)zone { - return self; // since FSTUser objects are immutable -} - -- (NSString *)description { - return [NSString stringWithFormat:@"", self.UID]; -} - -- (BOOL)isUnauthenticated { - return self.UID == nil; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Core/FSTDatabaseInfo.h b/Firestore/Source/Core/FSTDatabaseInfo.h deleted file mode 100644 index fae884fe21b..00000000000 --- a/Firestore/Source/Core/FSTDatabaseInfo.h +++ /dev/null @@ -1,55 +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 - -@class FSTDatabaseID; - -NS_ASSUME_NONNULL_BEGIN - -/** FSTDatabaseInfo contains data about the database. */ -@interface FSTDatabaseInfo : NSObject - -/** - * Creates and returns a new FSTDatabaseInfo. - * - * @param databaseID The project/database to use. - * @param persistenceKey A unique identifier for this Firestore's local storage. Usually derived - * from -[FIRApp appName]. - * @param host The hostname of the datastore backend. - * @param sslEnabled Whether to use SSL when connecting. - * @return A new instance of FSTDatabaseInfo. - */ -+ (instancetype)databaseInfoWithDatabaseID:(FSTDatabaseID *)databaseID - persistenceKey:(NSString *)persistenceKey - host:(NSString *)host - sslEnabled:(BOOL)sslEnabled; - -/** The database info. */ -@property(nonatomic, strong, readonly) FSTDatabaseID *databaseID; - -/** The application name, taken from FIRApp. */ -@property(nonatomic, copy, readonly) NSString *persistenceKey; - -/** The hostname of the backend. */ -@property(nonatomic, copy, readonly) NSString *host; - -/** Whether to use SSL when connecting. */ -@property(nonatomic, readonly, getter=isSSLEnabled) BOOL sslEnabled; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Core/FSTDatabaseInfo.m b/Firestore/Source/Core/FSTDatabaseInfo.m deleted file mode 100644 index 2dbe61a217e..00000000000 --- a/Firestore/Source/Core/FSTDatabaseInfo.m +++ /dev/null @@ -1,70 +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/Source/Core/FSTDatabaseInfo.h" - -#import "Firestore/Source/Model/FSTDatabaseID.h" - -NS_ASSUME_NONNULL_BEGIN - -#pragma mark - FSTDatabaseInfo - -@implementation FSTDatabaseInfo - -#pragma mark - Constructors - -+ (instancetype)databaseInfoWithDatabaseID:(FSTDatabaseID *)databaseID - persistenceKey:(NSString *)persistenceKey - host:(NSString *)host - sslEnabled:(BOOL)sslEnabled { - return [[FSTDatabaseInfo alloc] initWithDatabaseID:databaseID - persistenceKey:persistenceKey - host:host - sslEnabled:sslEnabled]; -} - -/** - * Designated initializer. - * - * @param databaseID The database in the datastore. - * @param persistenceKey A unique identifier for this Firestore's local storage. Usually derived - * from -[FIRApp appName]. - * @param host The Firestore server hostname. - * @param sslEnabled Whether to use SSL when connecting. - */ -- (instancetype)initWithDatabaseID:(FSTDatabaseID *)databaseID - persistenceKey:(NSString *)persistenceKey - host:(NSString *)host - sslEnabled:(BOOL)sslEnabled { - if (self = [super init]) { - _databaseID = databaseID; - _persistenceKey = [persistenceKey copy]; - _host = [host copy]; - _sslEnabled = sslEnabled; - } - return self; -} - -#pragma mark - NSObject methods - -- (NSString *)description { - return [NSString - stringWithFormat:@"", self.databaseID, self.host]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Core/FSTEventManager.m b/Firestore/Source/Core/FSTEventManager.mm similarity index 100% rename from Firestore/Source/Core/FSTEventManager.m rename to Firestore/Source/Core/FSTEventManager.mm diff --git a/Firestore/Source/Core/FSTFirestoreClient.h b/Firestore/Source/Core/FSTFirestoreClient.h index d7b3304a61c..736d9561b72 100644 --- a/Firestore/Source/Core/FSTFirestoreClient.h +++ b/Firestore/Source/Core/FSTFirestoreClient.h @@ -20,12 +20,14 @@ #import "Firestore/Source/Core/FSTViewSnapshot.h" #import "Firestore/Source/Remote/FSTRemoteStore.h" +#include "Firestore/core/src/firebase/firestore/auth/credentials_provider.h" +#include "Firestore/core/src/firebase/firestore/core/database_info.h" +#include "Firestore/core/src/firebase/firestore/model/database_id.h" + @class FIRDocumentReference; @class FIRDocumentSnapshot; @class FIRQuery; @class FIRQuerySnapshot; -@class FSTDatabaseID; -@class FSTDatabaseInfo; @class FSTDispatchQueue; @class FSTDocument; @class FSTListenOptions; @@ -33,7 +35,6 @@ @class FSTQuery; @class FSTQueryListener; @class FSTTransaction; -@protocol FSTCredentialsProvider; NS_ASSUME_NONNULL_BEGIN @@ -49,9 +50,10 @@ NS_ASSUME_NONNULL_BEGIN * * All callbacks and events will be triggered on the provided userDispatchQueue. */ -+ (instancetype)clientWithDatabaseInfo:(FSTDatabaseInfo *)databaseInfo ++ (instancetype)clientWithDatabaseInfo:(const firebase::firestore::core::DatabaseInfo &)databaseInfo usePersistence:(BOOL)usePersistence - credentialsProvider:(id)credentialsProvider + credentialsProvider:(firebase::firestore::auth::CredentialsProvider *) + credentialsProvider // no passing ownership userDispatchQueue:(FSTDispatchQueue *)userDispatchQueue workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue; @@ -100,7 +102,8 @@ NS_ASSUME_NONNULL_BEGIN completion:(FSTVoidIDErrorBlock)completion; /** The database ID of the databaseInfo this client was initialized with. */ -@property(nonatomic, strong, readonly) FSTDatabaseID *databaseID; +// Ownes a DatabaseInfo instance, which contains the id here. +@property(nonatomic, assign, readonly) const firebase::firestore::model::DatabaseId *databaseID; /** * Dispatch queue for user callbacks / events. This will often be the "Main Dispatch Queue" of the diff --git a/Firestore/Source/Core/FSTFirestoreClient.m b/Firestore/Source/Core/FSTFirestoreClient.mm similarity index 82% rename from Firestore/Source/Core/FSTFirestoreClient.m rename to Firestore/Source/Core/FSTFirestoreClient.mm index 26da254b645..2c7c85f42a3 100644 --- a/Firestore/Source/Core/FSTFirestoreClient.m +++ b/Firestore/Source/Core/FSTFirestoreClient.mm @@ -16,14 +16,14 @@ #import "Firestore/Source/Core/FSTFirestoreClient.h" +#import + #import "FIRFirestoreErrors.h" #import "Firestore/Source/API/FIRDocumentReference+Internal.h" #import "Firestore/Source/API/FIRDocumentSnapshot+Internal.h" #import "Firestore/Source/API/FIRQuery+Internal.h" #import "Firestore/Source/API/FIRQuerySnapshot+Internal.h" #import "Firestore/Source/API/FIRSnapshotMetadata+Internal.h" -#import "Firestore/Source/Auth/FSTCredentialsProvider.h" -#import "Firestore/Source/Core/FSTDatabaseInfo.h" #import "Firestore/Source/Core/FSTEventManager.h" #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Core/FSTSyncEngine.h" @@ -45,16 +45,31 @@ #import "Firestore/Source/Util/FSTDispatchQueue.h" #import "Firestore/Source/Util/FSTLogger.h" +#include "Firestore/core/src/firebase/firestore/auth/credentials_provider.h" +#include "Firestore/core/src/firebase/firestore/core/database_info.h" +#include "Firestore/core/src/firebase/firestore/model/database_id.h" +#include "Firestore/core/src/firebase/firestore/util/string_apple.h" + +namespace util = firebase::firestore::util; +using firebase::firestore::auth::CredentialsProvider; +using firebase::firestore::auth::User; +using firebase::firestore::core::DatabaseInfo; +using firebase::firestore::model::DatabaseId; + NS_ASSUME_NONNULL_BEGIN -@interface FSTFirestoreClient () -- (instancetype)initWithDatabaseInfo:(FSTDatabaseInfo *)databaseInfo +@interface FSTFirestoreClient () { + DatabaseInfo _databaseInfo; +} + +- (instancetype)initWithDatabaseInfo:(const DatabaseInfo &)databaseInfo usePersistence:(BOOL)usePersistence - credentialsProvider:(id)credentialsProvider + credentialsProvider: + (CredentialsProvider *)credentialsProvider // no passing ownership userDispatchQueue:(FSTDispatchQueue *)userDispatchQueue workerDispatchQueue:(FSTDispatchQueue *)queue NS_DESIGNATED_INITIALIZER; -@property(nonatomic, strong, readonly) FSTDatabaseInfo *databaseInfo; +@property(nonatomic, assign, readonly) const DatabaseInfo *databaseInfo; @property(nonatomic, strong, readonly) FSTEventManager *eventManager; @property(nonatomic, strong, readonly) id persistence; @property(nonatomic, strong, readonly) FSTSyncEngine *syncEngine; @@ -69,15 +84,17 @@ - (instancetype)initWithDatabaseInfo:(FSTDatabaseInfo *)databaseInfo */ @property(nonatomic, strong, readonly) FSTDispatchQueue *workerDispatchQueue; -@property(nonatomic, strong, readonly) id credentialsProvider; +// Does not own the CredentialsProvider instance. +@property(nonatomic, assign, readonly) CredentialsProvider *credentialsProvider; @end @implementation FSTFirestoreClient -+ (instancetype)clientWithDatabaseInfo:(FSTDatabaseInfo *)databaseInfo ++ (instancetype)clientWithDatabaseInfo:(const DatabaseInfo &)databaseInfo usePersistence:(BOOL)usePersistence - credentialsProvider:(id)credentialsProvider + credentialsProvider: + (CredentialsProvider *)credentialsProvider // no passing ownership userDispatchQueue:(FSTDispatchQueue *)userDispatchQueue workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue { return [[FSTFirestoreClient alloc] initWithDatabaseInfo:databaseInfo @@ -87,9 +104,10 @@ + (instancetype)clientWithDatabaseInfo:(FSTDatabaseInfo *)databaseInfo workerDispatchQueue:workerDispatchQueue]; } -- (instancetype)initWithDatabaseInfo:(FSTDatabaseInfo *)databaseInfo +- (instancetype)initWithDatabaseInfo:(const DatabaseInfo &)databaseInfo usePersistence:(BOOL)usePersistence - credentialsProvider:(id)credentialsProvider + credentialsProvider: + (CredentialsProvider *)credentialsProvider // no passing ownership userDispatchQueue:(FSTDispatchQueue *)userDispatchQueue workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue { if (self = [super init]) { @@ -98,36 +116,38 @@ - (instancetype)initWithDatabaseInfo:(FSTDatabaseInfo *)databaseInfo _userDispatchQueue = userDispatchQueue; _workerDispatchQueue = workerDispatchQueue; - dispatch_semaphore_t initialUserAvailable = dispatch_semaphore_create(0); - __block FSTUser *initialUser; - FSTWeakify(self); - _credentialsProvider.userChangeListener = ^(FSTUser *user) { - FSTStrongify(self); - if (self) { - if (!initialUser) { - initialUser = user; - dispatch_semaphore_signal(initialUserAvailable); - } else { - [workerDispatchQueue dispatchAsync:^{ - [self userDidChange:user]; - }]; - } + auto userPromise = std::make_shared>(); + + __weak typeof(self) weakSelf = self; + auto userChangeListener = [initialized = false, userPromise, weakSelf, + workerDispatchQueue](User user) mutable { + typeof(self) strongSelf = weakSelf; + if (!strongSelf) return; + + if (!initialized) { + initialized = true; + userPromise->set_value(user); + } else { + [workerDispatchQueue dispatchAsync:^{ + [strongSelf userDidChange:user]; + }]; } }; + _credentialsProvider->SetUserChangeListener(userChangeListener); + // Defer initialization until we get the current user from the userChangeListener. This is // guaranteed to be synchronously dispatched onto our worker queue, so we will be initialized // before any subsequently queued work runs. [_workerDispatchQueue dispatchAsync:^{ - dispatch_semaphore_wait(initialUserAvailable, DISPATCH_TIME_FOREVER); - - [self initializeWithUser:initialUser usePersistence:usePersistence]; + User user = userPromise->get_future().get(); + [self initializeWithUser:user usePersistence:usePersistence]; }]; } return self; } -- (void)initializeWithUser:(FSTUser *)user usePersistence:(BOOL)usePersistence { +- (void)initializeWithUser:(const User &)user usePersistence:(BOOL)usePersistence { // Do all of our initialization on our own dispatch queue. [self.workerDispatchQueue verifyIsCurrentQueue]; @@ -140,11 +160,11 @@ - (void)initializeWithUser:(FSTUser *)user usePersistence:(BOOL)usePersistence { // enabled. garbageCollector = [[FSTNoOpGarbageCollector alloc] init]; - NSString *dir = [FSTLevelDB storageDirectoryForDatabaseInfo:self.databaseInfo + NSString *dir = [FSTLevelDB storageDirectoryForDatabaseInfo:*self.databaseInfo documentsDirectory:[FSTLevelDB documentsDirectory]]; FSTSerializerBeta *remoteSerializer = - [[FSTSerializerBeta alloc] initWithDatabaseID:self.databaseInfo.databaseID]; + [[FSTSerializerBeta alloc] initWithDatabaseID:&self.databaseInfo->database_id()]; FSTLocalSerializer *serializer = [[FSTLocalSerializer alloc] initWithRemoteSerializer:remoteSerializer]; @@ -169,7 +189,7 @@ - (void)initializeWithUser:(FSTUser *)user usePersistence:(BOOL)usePersistence { FSTDatastore *datastore = [FSTDatastore datastoreWithDatabase:self.databaseInfo workerDispatchQueue:self.workerDispatchQueue - credentials:self.credentialsProvider]; + credentials:_credentialsProvider]; _remoteStore = [FSTRemoteStore remoteStoreWithLocalStore:_localStore datastore:datastore]; @@ -190,10 +210,10 @@ - (void)initializeWithUser:(FSTUser *)user usePersistence:(BOOL)usePersistence { [_remoteStore start]; } -- (void)userDidChange:(FSTUser *)user { +- (void)userDidChange:(const User &)user { [self.workerDispatchQueue verifyIsCurrentQueue]; - FSTLog(@"User Changed: %@", user); + FSTLog(@"User Changed: %@", util::WrapNSStringNoCopy(user.uid())); [self.syncEngine userDidChange:user]; } @@ -226,7 +246,7 @@ - (void)enableNetworkWithCompletion:(nullable FSTVoidErrorBlock)completion { - (void)shutdownWithCompletion:(nullable FSTVoidErrorBlock)completion { [self.workerDispatchQueue dispatchAsync:^{ - self.credentialsProvider.userChangeListener = nil; + self->_credentialsProvider->SetUserChangeListener(nullptr); [self.remoteStore shutdown]; [self.localStore shutdown]; @@ -315,9 +335,11 @@ - (void)writeMutations:(NSArray *)mutations completion:(nullable FSTVoidErrorBlock)completion { [self.workerDispatchQueue dispatchAsync:^{ if (mutations.count == 0) { - [self.userDispatchQueue dispatchAsync:^{ - completion(nil); - }]; + if (completion) { + [self.userDispatchQueue dispatchAsync:^{ + completion(nil); + }]; + } } else { [self.syncEngine writeMutations:mutations completion:^(NSError *error) { @@ -347,12 +369,15 @@ - (void)transactionWithRetries:(int)retries }]; } }]; - }]; } -- (FSTDatabaseID *)databaseID { - return self.databaseInfo.databaseID; +- (const DatabaseInfo *)databaseInfo { + return &_databaseInfo; +} + +- (const DatabaseId *)databaseID { + return &_databaseInfo.database_id(); } @end diff --git a/Firestore/Source/Core/FSTListenSequence.h b/Firestore/Source/Core/FSTListenSequence.h new file mode 100644 index 00000000000..56d0e783502 --- /dev/null +++ b/Firestore/Source/Core/FSTListenSequence.h @@ -0,0 +1,37 @@ +/* + * Copyright 2018 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 "FSTTypes.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * FSTListenSequence is a monotonic sequence. It is initialized with a minimum value to + * exceed. All subsequent calls to next will return increasing values. + */ +@interface FSTListenSequence : NSObject + +- (instancetype)initStartingAfter:(FSTListenSequenceNumber)after NS_DESIGNATED_INITIALIZER; + +- (id)init NS_UNAVAILABLE; + +- (FSTListenSequenceNumber)next; + +@end + +NS_ASSUME_NONNULL_END \ No newline at end of file diff --git a/Firestore/Source/Core/FSTListenSequence.mm b/Firestore/Source/Core/FSTListenSequence.mm new file mode 100644 index 00000000000..6f50d356545 --- /dev/null +++ b/Firestore/Source/Core/FSTListenSequence.mm @@ -0,0 +1,50 @@ +/* + * Copyright 2018 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 "FSTListenSequence.h" + +NS_ASSUME_NONNULL_BEGIN + +#pragma mark - FSTListenSequence + +@interface FSTListenSequence () { + FSTListenSequenceNumber _previousSequenceNumber; +} + +@end + +@implementation FSTListenSequence + +#pragma mark - Constructors + +- (instancetype)initStartingAfter:(FSTListenSequenceNumber)after { + self = [super init]; + if (self) { + _previousSequenceNumber = after; + } + return self; +} + +#pragma mark - Public methods + +- (FSTListenSequenceNumber)next { + _previousSequenceNumber++; + return _previousSequenceNumber; +} + +@end + +NS_ASSUME_NONNULL_END \ No newline at end of file diff --git a/Firestore/Source/Core/FSTQuery.m b/Firestore/Source/Core/FSTQuery.mm similarity index 97% rename from Firestore/Source/Core/FSTQuery.m rename to Firestore/Source/Core/FSTQuery.mm index 13657f73ff3..8c986872537 100644 --- a/Firestore/Source/Core/FSTQuery.m +++ b/Firestore/Source/Core/FSTQuery.mm @@ -27,6 +27,13 @@ #pragma mark - FSTRelationFilterOperator functions +/** + * Returns the reverse order (i.e. Ascending => Descending) etc. + */ +static constexpr NSComparisonResult ReverseOrder(NSComparisonResult result) { + return static_cast(-static_cast(result)); +} + NSString *FSTStringFromQueryRelationOperator(FSTRelationFilterOperator filterOperator) { switch (filterOperator) { case FSTRelationFilterOperatorLessThan: @@ -287,16 +294,20 @@ - (instancetype)initWithFieldPath:(FSTFieldPath *)fieldPath ascending:(BOOL)asce #pragma mark - Public methods - (NSComparisonResult)compareDocument:(FSTDocument *)document1 toDocument:(FSTDocument *)document2 { - int modifier = self.isAscending ? 1 : -1; + NSComparisonResult result; if ([self.field isEqual:[FSTFieldPath keyFieldPath]]) { - return (NSComparisonResult)(modifier * FSTDocumentKeyComparator(document1.key, document2.key)); + result = FSTDocumentKeyComparator(document1.key, document2.key); } else { FSTFieldValue *value1 = [document1 fieldForPath:self.field]; FSTFieldValue *value2 = [document2 fieldForPath:self.field]; FSTAssert(value1 != nil && value2 != nil, @"Trying to compare documents on fields that don't exist."); - return modifier * [value1 compare:value2]; + result = [value1 compare:value2]; + } + if (!self.isAscending) { + result = ReverseOrder(result); } + return result; } - (NSString *)canonicalID { @@ -377,7 +388,8 @@ - (BOOL)sortsBeforeDocument:(FSTDocument *)document if ([sortOrderComponent.field isEqual:[FSTFieldPath keyFieldPath]]) { FSTAssert([fieldValue isKindOfClass:[FSTReferenceValue class]], @"FSTBound has a non-key value where the key path is being used %@", fieldValue); - comparison = [fieldValue.value compare:document.key]; + FSTReferenceValue *refValue = (FSTReferenceValue *)fieldValue; + comparison = [refValue.value compare:document.key]; } else { FSTFieldValue *docValue = [document fieldForPath:sortOrderComponent.field]; FSTAssert(docValue != nil, @"Field should exist since document matched the orderBy already."); @@ -385,7 +397,7 @@ - (BOOL)sortsBeforeDocument:(FSTDocument *)document } if (!sortOrderComponent.isAscending) { - comparison = comparison * -1; + comparison = ReverseOrder(comparison); } if (comparison != 0) { diff --git a/Firestore/Source/Core/FSTSnapshotVersion.h b/Firestore/Source/Core/FSTSnapshotVersion.h index b72e4a25ff2..8649d4030ca 100644 --- a/Firestore/Source/Core/FSTSnapshotVersion.h +++ b/Firestore/Source/Core/FSTSnapshotVersion.h @@ -18,7 +18,7 @@ NS_ASSUME_NONNULL_BEGIN -@class FSTTimestamp; +@class FIRTimestamp; /** * A version of a document in Firestore. This corresponds to the version timestamp, such as @@ -30,13 +30,13 @@ NS_ASSUME_NONNULL_BEGIN + (instancetype)noVersion; /** Creates a new version representing the given timestamp. */ -+ (instancetype)versionWithTimestamp:(FSTTimestamp *)timestamp; ++ (instancetype)versionWithTimestamp:(FIRTimestamp *)timestamp; - (instancetype)init NS_UNAVAILABLE; - (NSComparisonResult)compare:(FSTSnapshotVersion *)other; -@property(nonatomic, strong, readonly) FSTTimestamp *timestamp; +@property(nonatomic, strong, readonly) FIRTimestamp *timestamp; @end diff --git a/Firestore/Source/Core/FSTSnapshotVersion.m b/Firestore/Source/Core/FSTSnapshotVersion.mm similarity index 87% rename from Firestore/Source/Core/FSTSnapshotVersion.m rename to Firestore/Source/Core/FSTSnapshotVersion.mm index 980ae528ec8..58b2be4ec4a 100644 --- a/Firestore/Source/Core/FSTSnapshotVersion.m +++ b/Firestore/Source/Core/FSTSnapshotVersion.mm @@ -16,7 +16,7 @@ #import "Firestore/Source/Core/FSTSnapshotVersion.h" -#import "Firestore/Source/Core/FSTTimestamp.h" +#import "FIRTimestamp.h" NS_ASSUME_NONNULL_BEGIN @@ -26,17 +26,17 @@ + (instancetype)noVersion { static FSTSnapshotVersion *min; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - FSTTimestamp *timestamp = [[FSTTimestamp alloc] initWithSeconds:0 nanos:0]; + FIRTimestamp *timestamp = [[FIRTimestamp alloc] initWithSeconds:0 nanoseconds:0]; min = [FSTSnapshotVersion versionWithTimestamp:timestamp]; }); return min; } -+ (instancetype)versionWithTimestamp:(FSTTimestamp *)timestamp { ++ (instancetype)versionWithTimestamp:(FIRTimestamp *)timestamp { return [[FSTSnapshotVersion alloc] initWithTimestamp:timestamp]; } -- (instancetype)initWithTimestamp:(FSTTimestamp *)timestamp { +- (instancetype)initWithTimestamp:(FIRTimestamp *)timestamp { self = [super init]; if (self) { _timestamp = timestamp; diff --git a/Firestore/Source/Core/FSTSyncEngine.h b/Firestore/Source/Core/FSTSyncEngine.h index 7060155e97f..3b28e363720 100644 --- a/Firestore/Source/Core/FSTSyncEngine.h +++ b/Firestore/Source/Core/FSTSyncEngine.h @@ -19,13 +19,14 @@ #import "Firestore/Source/Core/FSTTypes.h" #import "Firestore/Source/Remote/FSTRemoteStore.h" +#include "Firestore/core/src/firebase/firestore/auth/user.h" + @class FSTDispatchQueue; @class FSTLocalStore; @class FSTMutation; @class FSTQuery; @class FSTRemoteEvent; @class FSTRemoteStore; -@class FSTUser; @class FSTViewSnapshot; NS_ASSUME_NONNULL_BEGIN @@ -57,7 +58,8 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)init NS_UNAVAILABLE; - (instancetype)initWithLocalStore:(FSTLocalStore *)localStore remoteStore:(FSTRemoteStore *)remoteStore - initialUser:(FSTUser *)user NS_DESIGNATED_INITIALIZER; + initialUser:(const firebase::firestore::auth::User &)user + NS_DESIGNATED_INITIALIZER; /** * A delegate to be notified when queries being listened to produce new view snapshots or @@ -98,7 +100,7 @@ NS_ASSUME_NONNULL_BEGIN updateBlock:(FSTTransactionBlock)updateBlock completion:(FSTVoidIDErrorBlock)completion; -- (void)userDidChange:(FSTUser *)user; +- (void)userDidChange:(const firebase::firestore::auth::User &)user; /** Applies an FSTOnlineState change to the sync engine and notifies any views of the change. */ - (void)applyChangedOnlineState:(FSTOnlineState)onlineState; diff --git a/Firestore/Source/Core/FSTSyncEngine.m b/Firestore/Source/Core/FSTSyncEngine.mm similarity index 94% rename from Firestore/Source/Core/FSTSyncEngine.m rename to Firestore/Source/Core/FSTSyncEngine.mm index 27ab73e8154..61fac7d2723 100644 --- a/Firestore/Source/Core/FSTSyncEngine.m +++ b/Firestore/Source/Core/FSTSyncEngine.mm @@ -16,13 +16,13 @@ #import "Firestore/Source/Core/FSTSyncEngine.h" +#include + #import #import "FIRFirestoreErrors.h" -#import "Firestore/Source/Auth/FSTUser.h" #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Core/FSTSnapshotVersion.h" -#import "Firestore/Source/Core/FSTTargetIDGenerator.h" #import "Firestore/Source/Core/FSTTransaction.h" #import "Firestore/Source/Core/FSTView.h" #import "Firestore/Source/Core/FSTViewSnapshot.h" @@ -41,8 +41,19 @@ #import "Firestore/Source/Util/FSTDispatchQueue.h" #import "Firestore/Source/Util/FSTLogger.h" +#include "Firestore/core/src/firebase/firestore/auth/user.h" +#include "Firestore/core/src/firebase/firestore/core/target_id_generator.h" + +using firebase::firestore::auth::HashUser; +using firebase::firestore::auth::User; +using firebase::firestore::core::TargetIdGenerator; + NS_ASSUME_NONNULL_BEGIN +// Limbo documents don't use persistence, and are eagerly GC'd. So, listens for them don't need +// real sequence numbers. +static const FSTListenSequenceNumber kIrrelevantSequenceNumber = -1; + #pragma mark - FSTQueryView /** @@ -132,23 +143,22 @@ @interface FSTSyncEngine () /** The garbage collector used to collect documents that are no longer in limbo. */ @property(nonatomic, strong, readonly) FSTEagerGarbageCollector *limboCollector; -/** Stores user completion blocks, indexed by user and FSTBatchID. */ -@property(nonatomic, strong) - NSMutableDictionary *> - *mutationCompletionBlocks; - -/** Used for creating the FSTTargetIDs for the listens used to resolve limbo documents. */ -@property(nonatomic, strong, readonly) FSTTargetIDGenerator *targetIdGenerator; +@end -@property(nonatomic, strong) FSTUser *currentUser; +@implementation FSTSyncEngine { + /** Used for creating the FSTTargetIDs for the listens used to resolve limbo documents. */ + TargetIdGenerator _targetIdGenerator; -@end + /** Stores user completion blocks, indexed by user and FSTBatchID. */ + std::unordered_map *, HashUser> + _mutationCompletionBlocks; -@implementation FSTSyncEngine + User _currentUser; +} - (instancetype)initWithLocalStore:(FSTLocalStore *)localStore remoteStore:(FSTRemoteStore *)remoteStore - initialUser:(FSTUser *)initialUser { + initialUser:(const User &)initialUser { if (self = [super init]) { _localStore = localStore; _remoteStore = remoteStore; @@ -162,8 +172,7 @@ - (instancetype)initWithLocalStore:(FSTLocalStore *)localStore _limboDocumentRefs = [[FSTReferenceSet alloc] init]; [_limboCollector addGarbageSource:_limboDocumentRefs]; - _mutationCompletionBlocks = [NSMutableDictionary dictionary]; - _targetIdGenerator = [FSTTargetIDGenerator generatorForSyncEngineStartingAfterID:0]; + _targetIdGenerator = TargetIdGenerator::SyncEngineTargetIdGenerator(0); _currentUser = initialUser; } return self; @@ -220,10 +229,10 @@ - (void)writeMutations:(NSArray *)mutations - (void)addMutationCompletionBlock:(FSTVoidErrorBlock)completion batchID:(FSTBatchID)batchID { NSMutableDictionary *completionBlocks = - self.mutationCompletionBlocks[self.currentUser]; + _mutationCompletionBlocks[_currentUser]; if (!completionBlocks) { completionBlocks = [NSMutableDictionary dictionary]; - self.mutationCompletionBlocks[self.currentUser] = completionBlocks; + _mutationCompletionBlocks[_currentUser] = completionBlocks; } [completionBlocks setObject:completion forKey:@(batchID)]; } @@ -393,7 +402,7 @@ - (void)rejectFailedWriteWithBatchID:(FSTBatchID)batchID error:(NSError *)error - (void)processUserCallbacksForBatchID:(FSTBatchID)batchID error:(NSError *_Nullable)error { NSMutableDictionary *completionBlocks = - self.mutationCompletionBlocks[self.currentUser]; + _mutationCompletionBlocks[_currentUser]; // NOTE: Mutations restored from persistence won't have completion blocks, so it's okay for // this (or the completion below) to be nil. @@ -486,10 +495,11 @@ - (void)trackLimboChange:(FSTLimboDocumentChange *)limboChange { if (!self.limboTargetsByKey[key]) { FSTLog(@"New document in limbo: %@", key); - FSTTargetID limboTargetID = [self.targetIdGenerator nextID]; + FSTTargetID limboTargetID = _targetIdGenerator.NextId(); FSTQuery *query = [FSTQuery queryWithPath:key.path]; FSTQueryData *queryData = [[FSTQueryData alloc] initWithQuery:query targetID:limboTargetID + listenSequenceNumber:kIrrelevantSequenceNumber purpose:FSTQueryPurposeLimboResolution]; self.limboKeysByTarget[@(limboTargetID)] = key; [self.remoteStore listenToTargetWithQueryData:queryData]; @@ -519,8 +529,8 @@ - (void)garbageCollectLimboDocuments { return [self.limboTargetsByKey copy]; } -- (void)userDidChange:(FSTUser *)user { - self.currentUser = user; +- (void)userDidChange:(const User &)user { + _currentUser = user; // Notify local store and emit any resulting events from swapping out the mutation queue. FSTMaybeDocumentDictionary *changes = [self.localStore userDidChange:user]; diff --git a/Firestore/Source/Core/FSTTargetIDGenerator.h b/Firestore/Source/Core/FSTTargetIDGenerator.h deleted file mode 100644 index 0b230aefc33..00000000000 --- a/Firestore/Source/Core/FSTTargetIDGenerator.h +++ /dev/null @@ -1,55 +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 - -#import "Firestore/Source/Core/FSTTypes.h" - -NS_ASSUME_NONNULL_BEGIN - -/** - * FSTTargetIDGenerator generates monotonically increasing integer IDs. There are separate - * generators for different scopes. While these generators will operate independently of each - * other, they are scoped, such that no two generators will ever produce the same ID. This is - * useful, because sometimes the backend may group IDs from separate parts of the client into the - * same ID space. - */ -@interface FSTTargetIDGenerator : NSObject - -/** - * Creates and returns the FSTTargetIDGenerator for the local store. - * - * @param after An ID to start at. Every call to nextID will return an ID > @a after. - * @return A shared instance of FSTTargetIDGenerator. - */ -+ (instancetype)generatorForLocalStoreStartingAfterID:(FSTTargetID)after; - -/** - * Creates and returns the FSTTargetIDGenerator for the sync engine. - * - * @param after An ID to start at. Every call to nextID will return an ID > @a after. - * @return A shared instance of FSTTargetIDGenerator. - */ -+ (instancetype)generatorForSyncEngineStartingAfterID:(FSTTargetID)after; - -- (id)init __attribute__((unavailable("Use a static constructor method."))); - -/** Returns the next ID in the sequence. */ -- (FSTTargetID)nextID; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Core/FSTTargetIDGenerator.m b/Firestore/Source/Core/FSTTargetIDGenerator.m deleted file mode 100644 index 58092ece505..00000000000 --- a/Firestore/Source/Core/FSTTargetIDGenerator.m +++ /dev/null @@ -1,105 +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/Source/Core/FSTTargetIDGenerator.h" - -#import - -NS_ASSUME_NONNULL_BEGIN - -#pragma mark - FSTTargetIDGenerator - -static const int kReservedBits = 1; - -/** FSTTargetIDGeneratorID is the set of all valid generators. */ -typedef NS_ENUM(NSInteger, FSTTargetIDGeneratorID) { - FSTTargetIDGeneratorIDLocalStore = 0, - FSTTargetIDGeneratorIDSyncEngine = 1 -}; - -@interface FSTTargetIDGenerator () { - // This is volatile so it can be used with OSAtomicAdd32. - volatile FSTTargetID _previousID; -} - -/** - * Initializes the generator. - * - * @param generatorID A unique ID indicating which generator this is. - * @param after Every call to nextID will return a number > @a after. - */ -- (instancetype)initWithGeneratorID:(FSTTargetIDGeneratorID)generatorID - startingAfterID:(FSTTargetID)after NS_DESIGNATED_INITIALIZER; - -// This is typed as FSTTargetID because we need to do bitwise operations with them together. -@property(nonatomic, assign) FSTTargetID generatorID; -@end - -@implementation FSTTargetIDGenerator - -#pragma mark - Constructors - -- (instancetype)initWithGeneratorID:(FSTTargetIDGeneratorID)generatorID - startingAfterID:(FSTTargetID)after { - self = [super init]; - if (self) { - _generatorID = generatorID; - - // Replace the generator part of |after| with this generator's ID. - FSTTargetID afterWithoutGenerator = (after >> kReservedBits) << kReservedBits; - FSTTargetID afterGenerator = after - afterWithoutGenerator; - if (afterGenerator >= _generatorID) { - // For example, if: - // self.generatorID = 0b0000 - // after = 0b1011 - // afterGenerator = 0b0001 - // Then: - // previous = 0b1010 - // next = 0b1100 - _previousID = afterWithoutGenerator | self.generatorID; - } else { - // For example, if: - // self.generatorID = 0b0001 - // after = 0b1010 - // afterGenerator = 0b0000 - // Then: - // previous = 0b1001 - // next = 0b1011 - _previousID = (afterWithoutGenerator | self.generatorID) - (1 << kReservedBits); - } - } - return self; -} - -+ (instancetype)generatorForLocalStoreStartingAfterID:(FSTTargetID)after { - return [[FSTTargetIDGenerator alloc] initWithGeneratorID:FSTTargetIDGeneratorIDLocalStore - startingAfterID:after]; -} - -+ (instancetype)generatorForSyncEngineStartingAfterID:(FSTTargetID)after { - return [[FSTTargetIDGenerator alloc] initWithGeneratorID:FSTTargetIDGeneratorIDSyncEngine - startingAfterID:after]; -} - -#pragma mark - Public methods - -- (FSTTargetID)nextID { - return OSAtomicAdd32(1 << kReservedBits, &_previousID); -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Core/FSTTransaction.m b/Firestore/Source/Core/FSTTransaction.mm similarity index 91% rename from Firestore/Source/Core/FSTTransaction.m rename to Firestore/Source/Core/FSTTransaction.mm index c4c5f27846c..f97888a7205 100644 --- a/Firestore/Source/Core/FSTTransaction.m +++ b/Firestore/Source/Core/FSTTransaction.mm @@ -104,22 +104,22 @@ - (void)lookupDocumentsForKeys:(NSArray *)keys FSTThrowInvalidUsage(@"FIRIllegalStateException", @"All reads in a transaction must be done before any writes."); } - [self.datastore - lookupDocuments:keys - completion:^(NSArray *_Nullable documents, NSError *_Nullable error) { - if (error) { - completion(nil, error); - return; - } - for (FSTMaybeDocument *doc in documents) { - NSError *recordError = nil; - if (![self recordVersionForDocument:doc error:&recordError]) { - completion(nil, recordError); - return; - } - } - completion(documents, nil); - }]; + [self.datastore lookupDocuments:keys + completion:^(NSArray *_Nullable documents, + NSError *_Nullable error) { + if (error) { + completion(nil, error); + return; + } + for (FSTMaybeDocument *doc in documents) { + NSError *recordError = nil; + if (![self recordVersionForDocument:doc error:&recordError]) { + completion(nil, recordError); + return; + } + } + completion(documents, nil); + }]; } /** Stores mutations to be written when commitWithCompletion is called. */ diff --git a/Firestore/Source/Core/FSTTypes.h b/Firestore/Source/Core/FSTTypes.h index b47bd0bc026..877ec944a72 100644 --- a/Firestore/Source/Core/FSTTypes.h +++ b/Firestore/Source/Core/FSTTypes.h @@ -26,6 +26,8 @@ typedef int32_t FSTBatchID; typedef int32_t FSTTargetID; +typedef int64_t FSTListenSequenceNumber; + typedef NSNumber FSTBoxedTargetID; /** diff --git a/Firestore/Source/Core/FSTView.m b/Firestore/Source/Core/FSTView.mm similarity index 96% rename from Firestore/Source/Core/FSTView.m rename to Firestore/Source/Core/FSTView.mm index 78019c60546..d6b4558ac3d 100644 --- a/Firestore/Source/Core/FSTView.m +++ b/Firestore/Source/Core/FSTView.mm @@ -312,8 +312,8 @@ - (FSTViewChange *)applyChangesToDocuments:(FSTViewDocumentChanges *)docChanges } return self.query.comparator(c1.document, c2.document); }]; - - NSArray *limboChanges = [self applyTargetChange:targetChange]; + [self applyTargetChange:targetChange]; + NSArray *limboChanges = [self updateLimboDocuments]; BOOL synced = self.limboDocuments.count == 0 && self.isCurrent; FSTSyncState newSyncState = synced ? FSTSyncStateSynced : FSTSyncStateLocal; BOOL syncStateChanged = newSyncState != self.syncState; @@ -378,10 +378,9 @@ - (BOOL)shouldBeLimboDocumentKey:(FSTDocumentKey *)key { } /** - * Updates syncedDocuments, isAcked, and limbo docs based on the given change. - * @return the list of changes to which docs are in limbo. + * Updates syncedDocuments and current based on the given change. */ -- (NSArray *)applyTargetChange:(nullable FSTTargetChange *)targetChange { +- (void)applyTargetChange:(nullable FSTTargetChange *)targetChange { if (targetChange) { FSTTargetMapping *targetMapping = targetChange.mapping; if ([targetMapping isKindOfClass:[FSTResetMapping class]]) { @@ -408,16 +407,21 @@ - (BOOL)shouldBeLimboDocumentKey:(FSTDocumentKey *)key { break; } } +} + +/** Updates limboDocuments and returns any changes as FSTLimboDocumentChanges. */ +- (NSArray *)updateLimboDocuments { + // We can only determine limbo documents when we're in-sync with the server. + if (!self.isCurrent) { + return @[]; + } - // Recompute the set of limbo docs. // TODO(klimt): Do this incrementally so that it's not quadratic when updating many documents. FSTDocumentKeySet *oldLimboDocuments = self.limboDocuments; self.limboDocuments = [FSTDocumentKeySet keySet]; - if (self.isCurrent) { - for (FSTDocument *doc in self.documentSet.documentEnumerator) { - if ([self shouldBeLimboDocumentKey:doc.key]) { - self.limboDocuments = [self.limboDocuments setByAddingObject:doc.key]; - } + for (FSTDocument *doc in self.documentSet.documentEnumerator) { + if ([self shouldBeLimboDocumentKey:doc.key]) { + self.limboDocuments = [self.limboDocuments setByAddingObject:doc.key]; } } diff --git a/Firestore/Source/Core/FSTViewSnapshot.m b/Firestore/Source/Core/FSTViewSnapshot.mm similarity index 100% rename from Firestore/Source/Core/FSTViewSnapshot.m rename to Firestore/Source/Core/FSTViewSnapshot.mm diff --git a/Firestore/Source/Local/FSTDocumentReference.h b/Firestore/Source/Local/FSTDocumentReference.h index eff60e4e482..04b8416295f 100644 --- a/Firestore/Source/Local/FSTDocumentReference.h +++ b/Firestore/Source/Local/FSTDocumentReference.h @@ -32,7 +32,7 @@ NS_ASSUME_NONNULL_BEGIN @interface FSTDocumentReference : NSObject /** Initializes the document reference with the given key and ID. */ -- (instancetype)initWithKey:(FSTDocumentKey *)key ID:(int)ID NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithKey:(FSTDocumentKey *)key ID:(int32_t)ID NS_DESIGNATED_INITIALIZER; - (instancetype)init NS_UNAVAILABLE; @@ -43,7 +43,7 @@ NS_ASSUME_NONNULL_BEGIN * The targetID of a referring target or the batchID of a referring mutation batch. (Which this * is depends upon which FSTReferenceSet this reference is a part of.) */ -@property(nonatomic, assign, readonly) int ID; +@property(nonatomic, assign, readonly) int32_t ID; @end diff --git a/Firestore/Source/Local/FSTDocumentReference.m b/Firestore/Source/Local/FSTDocumentReference.mm similarity index 87% rename from Firestore/Source/Local/FSTDocumentReference.m rename to Firestore/Source/Local/FSTDocumentReference.mm index 25a5935cee5..4310baafac1 100644 --- a/Firestore/Source/Local/FSTDocumentReference.m +++ b/Firestore/Source/Local/FSTDocumentReference.mm @@ -16,14 +16,17 @@ #import "Firestore/Source/Local/FSTDocumentReference.h" +#include "Firestore/core/src/firebase/firestore/util/comparison.h" + #import "Firestore/Source/Model/FSTDocumentKey.h" -#import "Firestore/Source/Util/FSTComparison.h" + +using firebase::firestore::util::WrapCompare; NS_ASSUME_NONNULL_BEGIN @implementation FSTDocumentReference -- (instancetype)initWithKey:(FSTDocumentKey *)key ID:(int)ID { +- (instancetype)initWithKey:(FSTDocumentKey *)key ID:(int32_t)ID { self = [super init]; if (self) { _key = key; @@ -67,13 +70,13 @@ - (id)copyWithZone:(nullable NSZone *)zone { if (result != NSOrderedSame) { return result; } - return FSTCompareInts(left.ID, right.ID); + return WrapCompare(left.ID, right.ID); }; /** Sorts document references by ID then key. */ const NSComparator FSTDocumentReferenceComparatorByID = ^NSComparisonResult(FSTDocumentReference *left, FSTDocumentReference *right) { - NSComparisonResult result = FSTCompareInts(left.ID, right.ID); + NSComparisonResult result = WrapCompare(left.ID, right.ID); if (result != NSOrderedSame) { return result; } diff --git a/Firestore/Source/Local/FSTEagerGarbageCollector.m b/Firestore/Source/Local/FSTEagerGarbageCollector.mm similarity index 100% rename from Firestore/Source/Local/FSTEagerGarbageCollector.m rename to Firestore/Source/Local/FSTEagerGarbageCollector.mm diff --git a/Firestore/Source/Local/FSTLevelDB.h b/Firestore/Source/Local/FSTLevelDB.h index 762054b0177..77abb3d4005 100644 --- a/Firestore/Source/Local/FSTLevelDB.h +++ b/Firestore/Source/Local/FSTLevelDB.h @@ -16,18 +16,12 @@ #import -#import "Firestore/Source/Local/FSTPersistence.h" - -#ifdef __cplusplus #include -namespace leveldb { -class DB; -class Status; -} -#endif +#import "Firestore/Source/Local/FSTPersistence.h" +#include "Firestore/core/src/firebase/firestore/core/database_info.h" +#include "leveldb/db.h" -@class FSTDatabaseInfo; @class FSTLocalSerializer; NS_ASSUME_NONNULL_BEGIN @@ -56,7 +50,8 @@ NS_ASSUME_NONNULL_BEGIN * will be created. Usually just +[FSTLevelDB documentsDir]. * @return A storage directory unique to the instance identified by databaseInfo. */ -+ (NSString *)storageDirectoryForDatabaseInfo:(FSTDatabaseInfo *)databaseInfo ++ (NSString *)storageDirectoryForDatabaseInfo: + (const firebase::firestore::core::DatabaseInfo &)databaseInfo documentsDirectory:(NSString *)documentsDirectory; /** @@ -68,8 +63,11 @@ NS_ASSUME_NONNULL_BEGIN */ - (BOOL)start:(NSError **)error; -#ifdef __cplusplus // What follows is the Objective-C++ extension to the API. +/** + * @return A standard set of read options + */ ++ (const leveldb::ReadOptions)standardReadOptions; /** * Creates an NSError based on the given status if the status is not ok. @@ -98,8 +96,6 @@ NS_ASSUME_NONNULL_BEGIN /** The native db pointer, allocated during start. */ @property(nonatomic, assign, readonly) std::shared_ptr ptr; -#endif - @end NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Local/FSTLevelDB.mm b/Firestore/Source/Local/FSTLevelDB.mm index 83b932cb57d..9d3c35e0df8 100644 --- a/Firestore/Source/Local/FSTLevelDB.mm +++ b/Firestore/Source/Local/FSTLevelDB.mm @@ -19,23 +19,33 @@ #include #import "FIRFirestoreErrors.h" -#import "Firestore/Source/Core/FSTDatabaseInfo.h" +#import "Firestore/Source/Local/FSTLevelDBMigrations.h" #import "Firestore/Source/Local/FSTLevelDBMutationQueue.h" #import "Firestore/Source/Local/FSTLevelDBQueryCache.h" #import "Firestore/Source/Local/FSTLevelDBRemoteDocumentCache.h" #import "Firestore/Source/Local/FSTWriteGroup.h" #import "Firestore/Source/Local/FSTWriteGroupTracker.h" -#import "Firestore/Source/Model/FSTDatabaseID.h" #import "Firestore/Source/Remote/FSTSerializerBeta.h" #import "Firestore/Source/Util/FSTAssert.h" #import "Firestore/Source/Util/FSTLogger.h" +#include "Firestore/core/src/firebase/firestore/auth/user.h" +#include "Firestore/core/src/firebase/firestore/core/database_info.h" +#include "Firestore/core/src/firebase/firestore/model/database_id.h" +#include "Firestore/core/src/firebase/firestore/util/string_apple.h" + +namespace util = firebase::firestore::util; +using firebase::firestore::auth::User; +using firebase::firestore::core::DatabaseInfo; +using firebase::firestore::model::DatabaseId; + NS_ASSUME_NONNULL_BEGIN static NSString *const kReservedPathComponent = @"firestore"; using leveldb::DB; using leveldb::Options; +using leveldb::ReadOptions; using leveldb::Status; using leveldb::WriteOptions; @@ -50,6 +60,15 @@ @interface FSTLevelDB () @implementation FSTLevelDB +/** + * For now this is paranoid, but perhaps disable that in production builds. + */ ++ (const ReadOptions)standardReadOptions { + ReadOptions options; + options.verify_checksums = true; + return options; +} + - (instancetype)initWithDirectory:(NSString *)directory serializer:(FSTLocalSerializer *)serializer { if (self = [super init]) { @@ -78,7 +97,7 @@ + (NSString *)documentsDirectory { #endif } -+ (NSString *)storageDirectoryForDatabaseInfo:(FSTDatabaseInfo *)databaseInfo ++ (NSString *)storageDirectoryForDatabaseInfo:(const DatabaseInfo &)databaseInfo documentsDirectory:(NSString *)documentsDirectory { // Use two different path formats: // @@ -88,11 +107,14 @@ + (NSString *)storageDirectoryForDatabaseInfo:(FSTDatabaseInfo *)databaseInfo // projectIDs are DNS-compatible names and cannot contain dots so there's // no danger of collisions. NSString *directory = documentsDirectory; - directory = [directory stringByAppendingPathComponent:databaseInfo.persistenceKey]; - - NSString *segment = databaseInfo.databaseID.projectID; - if (![databaseInfo.databaseID isDefaultDatabase]) { - segment = [NSString stringWithFormat:@"%@.%@", segment, databaseInfo.databaseID.databaseID]; + directory = [directory + stringByAppendingPathComponent:util::WrapNSStringNoCopy(databaseInfo.persistence_key())]; + + NSString *segment = util::WrapNSStringNoCopy(databaseInfo.database_id().project_id()); + if (!databaseInfo.database_id().IsDefaultDatabase()) { + segment = [NSString + stringWithFormat:@"%@.%@", segment, + util::WrapNSStringNoCopy(databaseInfo.database_id().database_id())]; } directory = [directory stringByAppendingPathComponent:segment]; @@ -115,8 +137,8 @@ - (BOOL)start:(NSError **)error { if (!database) { return NO; } - _ptr.reset(database); + [FSTLevelDBMigrations runMigrationsOnDB:_ptr]; return YES; } @@ -178,7 +200,7 @@ - (nullable DB *)createDBWithDirectory:(NSString *)directory error:(NSError **)e #pragma mark - Persistence Factory methods -- (id)mutationQueueForUser:(FSTUser *)user { +- (id)mutationQueueForUser:(const User &)user { return [FSTLevelDBMutationQueue mutationQueueWithUser:user db:_ptr serializer:self.serializer]; } diff --git a/Firestore/Source/Local/FSTLevelDBKey.h b/Firestore/Source/Local/FSTLevelDBKey.h index 2e9b9b2a00f..f3f4bcf4dbd 100644 --- a/Firestore/Source/Local/FSTLevelDBKey.h +++ b/Firestore/Source/Local/FSTLevelDBKey.h @@ -14,10 +14,6 @@ * limitations under the License. */ -#ifndef __cplusplus -#error "FSTLevelDBKey is Objective-C++ and can only be included from .mm files" -#endif - #import #import "Firestore/Source/Core/FSTTypes.h" @@ -82,6 +78,14 @@ NS_ASSUME_NONNULL_BEGIN @end +/** A key to a singleton row storing the version of the schema. */ +@interface FSTLevelDBVersionKey : NSObject + +/** Returns the key pointing to the singleton row storing the schema version. */ ++ (std::string)key; + +@end + /** A key in the mutations table. */ @interface FSTLevelDBMutationKey : NSObject diff --git a/Firestore/Source/Local/FSTLevelDBKey.mm b/Firestore/Source/Local/FSTLevelDBKey.mm index c6f51b9b914..41aea39d1cd 100644 --- a/Firestore/Source/Local/FSTLevelDBKey.mm +++ b/Firestore/Source/Local/FSTLevelDBKey.mm @@ -18,18 +18,18 @@ #include -#include "Firestore/Port/ordered_code.h" -#include "Firestore/Port/string_util.h" #import "Firestore/Source/Model/FSTDocumentKey.h" #import "Firestore/Source/Model/FSTPath.h" +#include "Firestore/core/src/firebase/firestore/util/ordered_code.h" + NS_ASSUME_NONNULL_BEGIN -using Firestore::OrderedCode; -using Firestore::PrefixSuccessor; +using firebase::firestore::util::OrderedCode; using Firestore::StringView; using leveldb::Slice; +static const char *kVersionGlobalTable = "version"; static const char *kMutationsTable = "mutation"; static const char *kDocumentMutationsTable = "document_mutation"; static const char *kMutationQueuesTable = "mutation_queue"; @@ -111,11 +111,11 @@ void WriteComponentLabel(std::string *dest, FSTComponentLabel label) { */ BOOL ReadComponentLabel(leveldb::Slice *contents, FSTComponentLabel *label) { int64_t rawResult = 0; - Slice tmp = *contents; + absl::string_view tmp(contents->data(), contents->size()); if (OrderedCode::ReadSignedNumIncreasing(&tmp, &rawResult)) { if (rawResult >= FSTComponentLabelTerminator && rawResult <= FSTComponentLabelUnknown) { *label = static_cast(rawResult); - *contents = tmp; + *contents = leveldb::Slice(tmp.data(), tmp.size()); return YES; } } @@ -130,9 +130,9 @@ BOOL ReadComponentLabel(leveldb::Slice *contents, FSTComponentLabel *label) { * * If the read is successful, returns YES and contents will be updated to the next unread byte. */ -BOOL ReadComponentLabelMatching(Slice *contents, FSTComponentLabel expectedLabel) { +BOOL ReadComponentLabelMatching(absl::string_view *contents, FSTComponentLabel expectedLabel) { int64_t rawResult = 0; - Slice tmp = *contents; + absl::string_view tmp = *contents; if (OrderedCode::ReadSignedNumIncreasing(&tmp, &rawResult)) { if (rawResult == expectedLabel) { *contents = tmp; @@ -154,10 +154,10 @@ BOOL ReadComponentLabelMatching(Slice *contents, FSTComponentLabel expectedLabel */ BOOL ReadInt32(Slice *contents, int32_t *result) { int64_t rawResult = 0; - Slice tmp = *contents; + absl::string_view tmp(contents->data(), contents->size()); if (OrderedCode::ReadSignedNumIncreasing(&tmp, &rawResult)) { if (rawResult >= INT32_MIN && rawResult <= INT32_MAX) { - *contents = tmp; + *contents = leveldb::Slice(tmp.data(), tmp.size()); *result = static_cast(rawResult); return YES; } @@ -182,10 +182,11 @@ void WriteLabeledInt32(std::string *dest, FSTComponentLabel label, int32_t value * value will be set to the decoded integer value. */ BOOL ReadLabeledInt32(Slice *contents, FSTComponentLabel expectedLabel, int32_t *value) { - Slice tmp = *contents; + absl::string_view tmp(contents->data(), contents->size()); if (ReadComponentLabelMatching(&tmp, expectedLabel)) { - if (ReadInt32(&tmp, value)) { - *contents = tmp; + Slice tmpSlice = leveldb::Slice(tmp.data(), tmp.size()); + if (ReadInt32(&tmpSlice, value)) { + *contents = tmpSlice; return YES; } } @@ -209,10 +210,10 @@ void WriteLabeledString(std::string *dest, FSTComponentLabel label, StringView v * value will be set to the decoded string value. */ BOOL ReadLabeledString(Slice *contents, FSTComponentLabel expectedLabel, std::string *value) { - Slice tmp = *contents; + absl::string_view tmp(contents->data(), contents->size()); if (ReadComponentLabelMatching(&tmp, expectedLabel)) { if (OrderedCode::ReadString(&tmp, value)) { - *contents = tmp; + *contents = leveldb::Slice(tmp.data(), tmp.size()); return YES; } } @@ -274,7 +275,7 @@ BOOL ReadDocumentKey(Slice *contents, FSTDocumentKey *__strong *result) { for (;;) { // Advance a temporary slice to avoid advancing contents into the next key component which may // not be a path segment. - Slice readPosition = completeSegments; + absl::string_view readPosition(completeSegments.data(), completeSegments.size()); if (!ReadComponentLabelMatching(&readPosition, FSTComponentLabelPathSegment)) { break; } @@ -286,7 +287,7 @@ BOOL ReadDocumentKey(Slice *contents, FSTDocumentKey *__strong *result) { [pathSegments addObject:pathSegment]; segment.clear(); - completeSegments = readPosition; + completeSegments = leveldb::Slice(readPosition.data(), readPosition.size()); } FSTResourcePath *path = [FSTResourcePath pathWithSegments:pathSegments]; @@ -306,7 +307,10 @@ inline void WriteTerminator(std::string *dest) { } inline BOOL ReadTerminator(Slice *contents) { - return ReadComponentLabelMatching(contents, FSTComponentLabelTerminator); + absl::string_view tmp(contents->data(), contents->size()); + BOOL result = ReadComponentLabelMatching(&tmp, FSTComponentLabelTerminator); + *contents = leveldb::Slice(tmp.data(), tmp.size()); + return result; } inline void WriteTableName(std::string *dest, const char *tableName) { @@ -445,6 +449,17 @@ + (NSString *)descriptionForKey:(StringView)key { @end +@implementation FSTLevelDBVersionKey + ++ (std::string)key { + std::string result; + WriteTableName(&result, kVersionGlobalTable); + WriteTerminator(&result); + return result; +} + +@end + @implementation FSTLevelDBMutationKey { std::string _userID; } diff --git a/Firestore/Example/Tests/Util/FSTTestDispatchQueue.h b/Firestore/Source/Local/FSTLevelDBMigrations.h similarity index 53% rename from Firestore/Example/Tests/Util/FSTTestDispatchQueue.h rename to Firestore/Source/Local/FSTLevelDBMigrations.h index 7ecbbaf073d..24fb5c8ecd3 100644 --- a/Firestore/Example/Tests/Util/FSTTestDispatchQueue.h +++ b/Firestore/Source/Local/FSTLevelDBMigrations.h @@ -1,5 +1,5 @@ /* - * Copyright 2017 Google + * Copyright 2018 Google * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,25 +14,27 @@ * limitations under the License. */ -#import "Firestore/Source/Util/FSTDispatchQueue.h" +#import -@class XCTestExpectation; +#include + +#include "leveldb/db.h" NS_ASSUME_NONNULL_BEGIN +typedef int32_t FSTLevelDBSchemaVersion; + +@interface FSTLevelDBMigrations : NSObject + /** - * Dispatch queue used in the integration tests that caps delayed executions at 1.0 seconds. + * Returns the current version of the schema for the given database */ -@interface FSTTestDispatchQueue : FSTDispatchQueue - -/** Creates and returns an FSTTestDispatchQueue wrapping the specified dispatch_queue_t. */ -+ (instancetype)queueWith:(dispatch_queue_t)dispatchQueue; ++ (FSTLevelDBSchemaVersion)schemaVersionForDB:(std::shared_ptr)db; /** - * Registers a test expectation that is fulfilled when the next delayed callback finished - * executing. + * Runs any migrations needed to bring the given database up to the current schema version */ -- (void)fulfillOnExecution:(XCTestExpectation *)expectation; ++ (void)runMigrationsOnDB:(std::shared_ptr)db; @end diff --git a/Firestore/Source/Local/FSTLevelDBMigrations.mm b/Firestore/Source/Local/FSTLevelDBMigrations.mm new file mode 100644 index 00000000000..7595c530956 --- /dev/null +++ b/Firestore/Source/Local/FSTLevelDBMigrations.mm @@ -0,0 +1,131 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/Source/Local/FSTLevelDBMigrations.h" + +#include "leveldb/write_batch.h" + +#import "Firestore/Protos/objc/firestore/local/Target.pbobjc.h" +#import "Firestore/Source/Local/FSTLevelDB.h" +#import "Firestore/Source/Local/FSTLevelDBKey.h" +#import "Firestore/Source/Local/FSTLevelDBQueryCache.h" +#import "Firestore/Source/Local/FSTWriteGroup.h" +#import "Firestore/Source/Util/FSTAssert.h" + +NS_ASSUME_NONNULL_BEGIN + +// Current version of the schema defined in this file. +static FSTLevelDBSchemaVersion kSchemaVersion = 2; + +using leveldb::DB; +using leveldb::Iterator; +using leveldb::Status; +using leveldb::Slice; +using leveldb::WriteOptions; + +/** + * Ensures that the global singleton target metadata row exists in LevelDB. + * @param db The db in which to require the row. + */ +static void EnsureTargetGlobal(std::shared_ptr db, FSTWriteGroup *group) { + FSTPBTargetGlobal *targetGlobal = [FSTLevelDBQueryCache readTargetMetadataFromDB:db]; + if (!targetGlobal) { + [group setMessage:[FSTPBTargetGlobal message] forKey:[FSTLevelDBTargetGlobalKey key]]; + } +} + +/** + * Save the given version number as the current version of the schema of the database. + * @param version The version to save + * @param group The transaction in which to save the new version number + */ +static void SaveVersion(FSTLevelDBSchemaVersion version, FSTWriteGroup *group) { + std::string key = [FSTLevelDBVersionKey key]; + std::string version_string = std::to_string(version); + [group setData:version_string forKey:key]; +} + +/** + * This function counts the number of targets that currently exist in the given db. It + * then reads the target global row, adds the count to the metadata from that row, and writes + * the metadata back. + * + * It assumes the metadata has already been written and is able to be read in this transaction. + */ +static void AddTargetCount(std::shared_ptr db, FSTWriteGroup *group) { + std::unique_ptr it(db->NewIterator([FSTLevelDB standardReadOptions])); + Slice start_key = [FSTLevelDBTargetKey keyPrefix]; + it->Seek(start_key); + + int32_t count = 0; + while (it->Valid() && it->key().starts_with(start_key)) { + count++; + it->Next(); + } + + FSTPBTargetGlobal *targetGlobal = [FSTLevelDBQueryCache readTargetMetadataFromDB:db]; + FSTCAssert(targetGlobal != nil, + @"We should have a metadata row as it was added in an earlier migration"); + targetGlobal.targetCount = count; + [group setMessage:targetGlobal forKey:[FSTLevelDBTargetGlobalKey key]]; +} + +@implementation FSTLevelDBMigrations + ++ (FSTLevelDBSchemaVersion)schemaVersionForDB:(std::shared_ptr)db { + std::string key = [FSTLevelDBVersionKey key]; + std::string version_string; + Status status = db->Get([FSTLevelDB standardReadOptions], key, &version_string); + if (status.IsNotFound()) { + return 0; + } else { + return stoi(version_string); + } +} + ++ (void)runMigrationsOnDB:(std::shared_ptr)db { + FSTWriteGroup *group = [FSTWriteGroup groupWithAction:@"Migrations"]; + FSTLevelDBSchemaVersion currentVersion = [self schemaVersionForDB:db]; + // Each case in this switch statement intentionally falls through. This lets us + // start at the current schema version and apply any migrations that have not yet + // been applied, to bring us up to current, as defined by the kSchemaVersion constant. + switch (currentVersion) { + case 0: + EnsureTargetGlobal(db, group); + // Fallthrough + case 1: + // We need to make sure we have metadata, since we're going to read and modify it + // in this migration. Commit the current transaction and start a new one. Since we're + // committing, we need to save a version. It's safe to save this one, if we crash + // after saving we'll resume from this step when we try to migrate. + SaveVersion(1, group); + [group writeToDB:db]; + group = [FSTWriteGroup groupWithAction:@"Migrations"]; + AddTargetCount(db, group); + // Fallthrough + default: + if (currentVersion < kSchemaVersion) { + SaveVersion(kSchemaVersion, group); + } + } + if (!group.isEmpty) { + [group writeToDB:db]; + } +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Local/FSTLevelDBMutationQueue.h b/Firestore/Source/Local/FSTLevelDBMutationQueue.h index dd2ed4f7552..3f1bd5158ca 100644 --- a/Firestore/Source/Local/FSTLevelDBMutationQueue.h +++ b/Firestore/Source/Local/FSTLevelDBMutationQueue.h @@ -16,19 +16,15 @@ #import -#import "Firestore/Source/Local/FSTMutationQueue.h" - -#ifdef __cplusplus #include -namespace leveldb { -class DB; -} -#endif +#import "Firestore/Source/Local/FSTMutationQueue.h" + +#include "Firestore/core/src/firebase/firestore/auth/user.h" +#include "leveldb/db.h" @class FSTLevelDB; @class FSTLocalSerializer; -@class FSTUser; @protocol FSTGarbageCollector; NS_ASSUME_NONNULL_BEGIN @@ -41,14 +37,13 @@ NS_ASSUME_NONNULL_BEGIN /** The garbage collector to notify about potential garbage keys. */ @property(nonatomic, weak, readwrite, nullable) id garbageCollector; -#ifdef __cplusplus /** * Creates a new mutation queue for the given user, in the given LevelDB. * * @param user The user for which to create a mutation queue. * @param db The LevelDB in which to create the queue. */ -+ (instancetype)mutationQueueWithUser:(FSTUser *)user ++ (instancetype)mutationQueueWithUser:(const firebase::firestore::auth::User &)user db:(std::shared_ptr)db serializer:(FSTLocalSerializer *)serializer; @@ -57,7 +52,6 @@ NS_ASSUME_NONNULL_BEGIN * returns 0. Note that batch IDs are global. */ + (FSTBatchID)loadNextBatchIDFromDB:(std::shared_ptr)db; -#endif @end diff --git a/Firestore/Source/Local/FSTLevelDBMutationQueue.mm b/Firestore/Source/Local/FSTLevelDBMutationQueue.mm index 56a22a1c01a..248ef9a67d8 100644 --- a/Firestore/Source/Local/FSTLevelDBMutationQueue.mm +++ b/Firestore/Source/Local/FSTLevelDBMutationQueue.mm @@ -22,7 +22,6 @@ #include #import "Firestore/Protos/objc/firestore/local/Mutation.pbobjc.h" -#import "Firestore/Source/Auth/FSTUser.h" #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Local/FSTLevelDB.h" #import "Firestore/Source/Local/FSTLevelDBKey.h" @@ -34,13 +33,15 @@ #import "Firestore/Source/Model/FSTPath.h" #import "Firestore/Source/Util/FSTAssert.h" -#include "Firestore/Port/ordered_code.h" -#include "Firestore/Port/string_util.h" +#include "Firestore/core/src/firebase/firestore/auth/user.h" +#include "Firestore/core/src/firebase/firestore/util/string_apple.h" +#include "Firestore/core/src/firebase/firestore/util/string_util.h" NS_ASSUME_NONNULL_BEGIN -using Firestore::OrderedCode; +namespace util = firebase::firestore::util; using Firestore::StringView; +using firebase::firestore::auth::User; using leveldb::DB; using leveldb::Iterator; using leveldb::ReadOptions; @@ -90,11 +91,10 @@ @implementation FSTLevelDBMutationQueue { std::shared_ptr _db; } -+ (instancetype)mutationQueueWithUser:(FSTUser *)user ++ (instancetype)mutationQueueWithUser:(const User &)user db:(std::shared_ptr)db serializer:(FSTLocalSerializer *)serializer { - FSTAssert(![user.UID isEqual:@""], @"UserID must not be an empty string."); - NSString *userID = user.isUnauthenticated ? @"" : user.UID; + NSString *userID = user.is_authenticated() ? util::WrapNSStringNoCopy(user.uid()) : @""; return [[FSTLevelDBMutationQueue alloc] initWithUserID:userID db:db serializer:serializer]; } @@ -164,7 +164,7 @@ + (FSTBatchID)loadNextBatchIDFromDB:(std::shared_ptr)db { while (moreUserIDs) { // Compute the first key after the last mutation for nextUserID. auto userEnd = [FSTLevelDBMutationKey keyPrefixWithUserID:nextUserID]; - userEnd = Firestore::PrefixSuccessor(userEnd); + userEnd = util::PrefixSuccessor(userEnd); // Seek to that key with the intent of finding the boundary between nextUserID's mutations // and the one after that (if any). @@ -269,7 +269,7 @@ - (nullable FSTPBMutationQueue *)metadataForKey:(const std::string &)key { } } -- (FSTMutationBatch *)addMutationBatchWithWriteTime:(FSTTimestamp *)localWriteTime +- (FSTMutationBatch *)addMutationBatchWithWriteTime:(FIRTimestamp *)localWriteTime mutations:(NSArray *)mutations group:(FSTWriteGroup *)group { FSTBatchID batchID = self.nextBatchID; diff --git a/Firestore/Source/Local/FSTLevelDBQueryCache.h b/Firestore/Source/Local/FSTLevelDBQueryCache.h index 6d5cd60ad11..1f6fbd4c030 100644 --- a/Firestore/Source/Local/FSTLevelDBQueryCache.h +++ b/Firestore/Source/Local/FSTLevelDBQueryCache.h @@ -16,17 +16,13 @@ #import -#import "Firestore/Source/Local/FSTQueryCache.h" - -#ifdef __cplusplus #include -namespace leveldb { -class DB; -} -#endif +#import "Firestore/Source/Local/FSTQueryCache.h" +#include "leveldb/db.h" @class FSTLocalSerializer; +@class FSTPBTargetGlobal; @protocol FSTGarbageCollector; NS_ASSUME_NONNULL_BEGIN @@ -34,12 +30,16 @@ NS_ASSUME_NONNULL_BEGIN /** Cached Queries backed by LevelDB. */ @interface FSTLevelDBQueryCache : NSObject +/** + * Retrieves the global singleton metadata row from the given database, if it exists. + */ ++ (nullable FSTPBTargetGlobal *)readTargetMetadataFromDB:(std::shared_ptr)db; + - (instancetype)init NS_UNAVAILABLE; /** The garbage collector to notify about potential garbage keys. */ @property(nonatomic, weak, readwrite, nullable) id garbageCollector; -#ifdef __cplusplus /** * Creates a new query cache in the given LevelDB. * @@ -47,7 +47,6 @@ NS_ASSUME_NONNULL_BEGIN */ - (instancetype)initWithDB:(std::shared_ptr)db serializer:(FSTLocalSerializer *)serializer NS_DESIGNATED_INITIALIZER; -#endif @end diff --git a/Firestore/Source/Local/FSTLevelDBQueryCache.mm b/Firestore/Source/Local/FSTLevelDBQueryCache.mm index 13d15eed048..fe1bf195d5d 100644 --- a/Firestore/Source/Local/FSTLevelDBQueryCache.mm +++ b/Firestore/Source/Local/FSTLevelDBQueryCache.mm @@ -18,10 +18,10 @@ #include #include -#include #import "Firestore/Protos/objc/firestore/local/Target.pbobjc.h" #import "Firestore/Source/Core/FSTQuery.h" +#import "Firestore/Source/Local/FSTLevelDB.h" #import "Firestore/Source/Local/FSTLevelDBKey.h" #import "Firestore/Source/Local/FSTLocalSerializer.h" #import "Firestore/Source/Local/FSTQueryData.h" @@ -29,12 +29,8 @@ #import "Firestore/Source/Model/FSTDocumentKey.h" #import "Firestore/Source/Util/FSTAssert.h" -#include "Firestore/Port/ordered_code.h" -#include "Firestore/Port/string_util.h" - NS_ASSUME_NONNULL_BEGIN -using Firestore::OrderedCode; using Firestore::StringView; using leveldb::DB; using leveldb::Iterator; @@ -43,17 +39,6 @@ using leveldb::Status; using leveldb::WriteOptions; -/** - * Returns a standard set of read options. - * - * For now this is paranoid, but perhaps disable that in production builds. - */ -static ReadOptions GetStandardReadOptions() { - ReadOptions options; - options.verify_checksums = true; - return options; -} - @interface FSTLevelDBQueryCache () /** A write-through cached copy of the metadata for the query cache. */ @@ -74,6 +59,29 @@ @implementation FSTLevelDBQueryCache { FSTSnapshotVersion *_lastRemoteSnapshotVersion; } ++ (nullable FSTPBTargetGlobal *)readTargetMetadataFromDB:(std::shared_ptr)db { + std::string key = [FSTLevelDBTargetGlobalKey key]; + std::string value; + Status status = db->Get([FSTLevelDB standardReadOptions], key, &value); + if (status.IsNotFound()) { + return nil; + } else if (!status.ok()) { + FSTFail(@"metadataForKey: failed loading key %s with status: %s", key.c_str(), + status.ToString().c_str()); + } + + NSData *data = + [[NSData alloc] initWithBytesNoCopy:(void *)value.data() length:value.size() freeWhenDone:NO]; + + NSError *error; + FSTPBTargetGlobal *proto = [FSTPBTargetGlobal parseFromData:data error:&error]; + if (!proto) { + FSTFail(@"FSTPBTargetGlobal failed to parse: %@", error); + } + + return proto; +} + - (instancetype)initWithDB:(std::shared_ptr)db serializer:(FSTLocalSerializer *)serializer { if (self = [super init]) { FSTAssert(db, @"db must not be NULL"); @@ -84,11 +92,10 @@ - (instancetype)initWithDB:(std::shared_ptr)db serializer:(FSTLocalSerialize } - (void)start { - std::string key = [FSTLevelDBTargetGlobalKey key]; - FSTPBTargetGlobal *metadata = [self metadataForKey:key]; - if (!metadata) { - metadata = [FSTPBTargetGlobal message]; - } + FSTPBTargetGlobal *metadata = [FSTLevelDBQueryCache readTargetMetadataFromDB:_db]; + FSTAssert( + metadata != nil, + @"Found nil metadata, expected schema to be at version 0 which ensures metadata existence"); _lastRemoteSnapshotVersion = [self.serializer decodedVersion:metadata.lastRemoteSnapshotVersion]; self.metadata = metadata; @@ -100,6 +107,10 @@ - (FSTTargetID)highestTargetID { return self.metadata.highestTargetId; } +- (FSTListenSequenceNumber)highestListenSequenceNumber { + return self.metadata.highestListenSequenceNumber; +} + - (FSTSnapshotVersion *)lastRemoteSnapshotVersion { return _lastRemoteSnapshotVersion; } @@ -115,22 +126,50 @@ - (void)shutdown { _db.reset(); } -- (void)addQueryData:(FSTQueryData *)queryData group:(FSTWriteGroup *)group { - // TODO(mcg): actually populate listen sequence number +- (void)saveQueryData:(FSTQueryData *)queryData group:(FSTWriteGroup *)group { FSTTargetID targetID = queryData.targetID; std::string key = [FSTLevelDBTargetKey keyWithTargetID:targetID]; [group setMessage:[self.serializer encodedQueryData:queryData] forKey:key]; +} + +- (void)saveMetadataInGroup:(FSTWriteGroup *)group { + [group setMessage:self.metadata forKey:[FSTLevelDBTargetGlobalKey key]]; +} + +- (BOOL)updateMetadataForQueryData:(FSTQueryData *)queryData { + BOOL updatedMetadata = NO; + + if (queryData.targetID > self.metadata.highestTargetId) { + self.metadata.highestTargetId = queryData.targetID; + updatedMetadata = YES; + } + + if (queryData.sequenceNumber > self.metadata.highestListenSequenceNumber) { + self.metadata.highestListenSequenceNumber = queryData.sequenceNumber; + updatedMetadata = YES; + } + return updatedMetadata; +} + +- (void)addQueryData:(FSTQueryData *)queryData group:(FSTWriteGroup *)group { + [self saveQueryData:queryData group:group]; NSString *canonicalID = queryData.query.canonicalID; std::string indexKey = - [FSTLevelDBQueryTargetKey keyWithCanonicalID:canonicalID targetID:targetID]; + [FSTLevelDBQueryTargetKey keyWithCanonicalID:canonicalID targetID:queryData.targetID]; std::string emptyBuffer; [group setData:emptyBuffer forKey:indexKey]; - FSTPBTargetGlobal *metadata = self.metadata; - if (targetID > metadata.highestTargetId) { - metadata.highestTargetId = targetID; - [group setMessage:metadata forKey:[FSTLevelDBTargetGlobalKey key]]; + self.metadata.targetCount += 1; + [self updateMetadataForQueryData:queryData]; + [self saveMetadataInGroup:group]; +} + +- (void)updateQueryData:(FSTQueryData *)queryData group:(FSTWriteGroup *)group { + [self saveQueryData:queryData group:group]; + + if ([self updateMetadataForQueryData:queryData]) { + [self saveMetadataInGroup:group]; } } @@ -145,34 +184,12 @@ - (void)removeQueryData:(FSTQueryData *)queryData group:(FSTWriteGroup *)group { std::string indexKey = [FSTLevelDBQueryTargetKey keyWithCanonicalID:queryData.query.canonicalID targetID:targetID]; [group removeMessageForKey:indexKey]; + self.metadata.targetCount -= 1; + [self saveMetadataInGroup:group]; } -/** - * Looks up the query global metadata associated with the given key. - * - * @return the parsed protocol buffer message or nil if the row referenced by the given key does - * not exist. - */ -- (nullable FSTPBTargetGlobal *)metadataForKey:(const std::string &)key { - std::string value; - Status status = _db->Get(GetStandardReadOptions(), key, &value); - if (status.IsNotFound()) { - return nil; - } else if (!status.ok()) { - FSTFail(@"metadataForKey: failed loading key %s with status: %s", key.c_str(), - status.ToString().c_str()); - } - - NSData *data = - [[NSData alloc] initWithBytesNoCopy:(void *)value.data() length:value.size() freeWhenDone:NO]; - - NSError *error; - FSTPBTargetGlobal *proto = [FSTPBTargetGlobal parseFromData:data error:&error]; - if (!proto) { - FSTFail(@"FSTPBTargetGlobal failed to parse: %@", error); - } - - return proto; +- (int32_t)count { + return self.metadata.targetCount; } /** @@ -197,7 +214,7 @@ - (nullable FSTQueryData *)queryDataForQuery:(FSTQuery *)query { // Note that this is a scan rather than a get because canonicalIDs are not required to be unique // per target. Slice canonicalID = StringView(query.canonicalID); - std::unique_ptr indexItererator(_db->NewIterator(GetStandardReadOptions())); + std::unique_ptr indexItererator(_db->NewIterator([FSTLevelDB standardReadOptions])); std::string indexPrefix = [FSTLevelDBQueryTargetKey keyPrefixWithCanonicalID:canonicalID]; indexItererator->Seek(indexPrefix); @@ -205,7 +222,7 @@ - (nullable FSTQueryData *)queryDataForQuery:(FSTQuery *)query { // unique and ordered, so when scanning a table prefixed by exactly one canonicalID, all the // targetIDs will be unique and in order. std::string targetPrefix = [FSTLevelDBTargetKey keyPrefix]; - std::unique_ptr targetIterator(_db->NewIterator(GetStandardReadOptions())); + std::unique_ptr targetIterator(_db->NewIterator([FSTLevelDB standardReadOptions])); FSTLevelDBQueryTargetKey *rowKey = [[FSTLevelDBQueryTargetKey alloc] init]; for (; indexItererator->Valid(); indexItererator->Next()) { @@ -277,7 +294,7 @@ - (void)removeMatchingKeys:(FSTDocumentKeySet *)keys - (void)removeMatchingKeysForTargetID:(FSTTargetID)targetID group:(FSTWriteGroup *)group { std::string indexPrefix = [FSTLevelDBTargetDocumentKey keyPrefixWithTargetID:targetID]; - std::unique_ptr indexIterator(_db->NewIterator(GetStandardReadOptions())); + std::unique_ptr indexIterator(_db->NewIterator([FSTLevelDB standardReadOptions])); indexIterator->Seek(indexPrefix); FSTLevelDBTargetDocumentKey *rowKey = [[FSTLevelDBTargetDocumentKey alloc] init]; @@ -300,7 +317,7 @@ - (void)removeMatchingKeysForTargetID:(FSTTargetID)targetID group:(FSTWriteGroup - (FSTDocumentKeySet *)matchingKeysForTargetID:(FSTTargetID)targetID { std::string indexPrefix = [FSTLevelDBTargetDocumentKey keyPrefixWithTargetID:targetID]; - std::unique_ptr indexIterator(_db->NewIterator(GetStandardReadOptions())); + std::unique_ptr indexIterator(_db->NewIterator([FSTLevelDB standardReadOptions])); indexIterator->Seek(indexPrefix); FSTDocumentKeySet *result = [FSTDocumentKeySet keySet]; @@ -323,7 +340,7 @@ - (FSTDocumentKeySet *)matchingKeysForTargetID:(FSTTargetID)targetID { - (BOOL)containsKey:(FSTDocumentKey *)key { std::string indexPrefix = [FSTLevelDBDocumentTargetKey keyPrefixWithResourcePath:key.path]; - std::unique_ptr indexIterator(_db->NewIterator(GetStandardReadOptions())); + std::unique_ptr indexIterator(_db->NewIterator([FSTLevelDB standardReadOptions])); indexIterator->Seek(indexPrefix); if (indexIterator->Valid()) { diff --git a/Firestore/Source/Local/FSTLevelDBRemoteDocumentCache.h b/Firestore/Source/Local/FSTLevelDBRemoteDocumentCache.h index 1da3cca7db0..20942e27dad 100644 --- a/Firestore/Source/Local/FSTLevelDBRemoteDocumentCache.h +++ b/Firestore/Source/Local/FSTLevelDBRemoteDocumentCache.h @@ -16,15 +16,10 @@ #import -#import "Firestore/Source/Local/FSTRemoteDocumentCache.h" - -#ifdef __cplusplus #include -namespace leveldb { -class DB; -} -#endif +#import "Firestore/Source/Local/FSTRemoteDocumentCache.h" +#include "leveldb/db.h" @class FSTLocalSerializer; @@ -35,7 +30,6 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)init NS_UNAVAILABLE; -#ifdef __cplusplus /** * Creates a new remote documents cache in the given leveldb. * @@ -43,7 +37,6 @@ NS_ASSUME_NONNULL_BEGIN */ - (instancetype)initWithDB:(std::shared_ptr)db serializer:(FSTLocalSerializer *)serializer NS_DESIGNATED_INITIALIZER; -#endif @end diff --git a/Firestore/Source/Local/FSTLevelDBRemoteDocumentCache.mm b/Firestore/Source/Local/FSTLevelDBRemoteDocumentCache.mm index 02f9f3eb275..b842cb5a2b0 100644 --- a/Firestore/Source/Local/FSTLevelDBRemoteDocumentCache.mm +++ b/Firestore/Source/Local/FSTLevelDBRemoteDocumentCache.mm @@ -32,12 +32,8 @@ #import "Firestore/Source/Model/FSTPath.h" #import "Firestore/Source/Util/FSTAssert.h" -#include "Firestore/Port/ordered_code.h" -#include "Firestore/Port/string_util.h" - NS_ASSUME_NONNULL_BEGIN -using Firestore::OrderedCode; using leveldb::DB; using leveldb::Iterator; using leveldb::ReadOptions; diff --git a/Firestore/Source/Local/FSTLocalDocumentsView.m b/Firestore/Source/Local/FSTLocalDocumentsView.mm similarity index 100% rename from Firestore/Source/Local/FSTLocalDocumentsView.m rename to Firestore/Source/Local/FSTLocalDocumentsView.mm diff --git a/Firestore/Source/Local/FSTLocalSerializer.m b/Firestore/Source/Local/FSTLocalSerializer.mm similarity index 96% rename from Firestore/Source/Local/FSTLocalSerializer.m rename to Firestore/Source/Local/FSTLocalSerializer.mm index c71e9dd4272..ec70ca014d0 100644 --- a/Firestore/Source/Local/FSTLocalSerializer.m +++ b/Firestore/Source/Local/FSTLocalSerializer.mm @@ -16,6 +16,8 @@ #import "Firestore/Source/Local/FSTLocalSerializer.h" +#include + #import "Firestore/Protos/objc/firestore/local/MaybeDocument.pbobjc.h" #import "Firestore/Protos/objc/firestore/local/Mutation.pbobjc.h" #import "Firestore/Protos/objc/firestore/local/Target.pbobjc.h" @@ -140,7 +142,7 @@ - (FSTMutationBatch *)decodedMutationBatch:(FSTPBWriteBatch *)batch { [mutations addObject:[remoteSerializer decodedMutation:write]]; } - FSTTimestamp *localWriteTime = [remoteSerializer decodedTimestamp:batch.localWriteTime]; + FIRTimestamp *localWriteTime = [remoteSerializer decodedTimestamp:batch.localWriteTime]; return [[FSTMutationBatch alloc] initWithBatchID:batchID localWriteTime:localWriteTime @@ -156,6 +158,7 @@ - (FSTPBTarget *)encodedQueryData:(FSTQueryData *)queryData { FSTPBTarget *proto = [FSTPBTarget message]; proto.targetId = queryData.targetID; + proto.lastListenSequenceNumber = queryData.sequenceNumber; proto.snapshotVersion = [remoteSerializer encodedVersion:queryData.snapshotVersion]; proto.resumeToken = queryData.resumeToken; @@ -173,6 +176,7 @@ - (FSTQueryData *)decodedQueryData:(FSTPBTarget *)target { FSTSerializerBeta *remoteSerializer = self.remoteSerializer; FSTTargetID targetID = target.targetId; + FSTListenSequenceNumber sequenceNumber = target.lastListenSequenceNumber; FSTSnapshotVersion *version = [remoteSerializer decodedVersion:target.snapshotVersion]; NSData *resumeToken = target.resumeToken; @@ -192,6 +196,7 @@ - (FSTQueryData *)decodedQueryData:(FSTPBTarget *)target { return [[FSTQueryData alloc] initWithQuery:query targetID:targetID + listenSequenceNumber:sequenceNumber purpose:FSTQueryPurposeListen snapshotVersion:version resumeToken:resumeToken]; diff --git a/Firestore/Source/Local/FSTLocalStore.h b/Firestore/Source/Local/FSTLocalStore.h index 19803ac0e0d..4ec23fd1527 100644 --- a/Firestore/Source/Local/FSTLocalStore.h +++ b/Firestore/Source/Local/FSTLocalStore.h @@ -21,6 +21,8 @@ #import "Firestore/Source/Model/FSTDocumentKeySet.h" #import "Firestore/Source/Model/FSTDocumentVersionDictionary.h" +#include "Firestore/core/src/firebase/firestore/auth/user.h" + @class FSTLocalViewChanges; @class FSTLocalWriteResult; @class FSTMutation; @@ -29,7 +31,6 @@ @class FSTQuery; @class FSTQueryData; @class FSTRemoteEvent; -@class FSTUser; @protocol FSTPersistence; @protocol FSTGarbageCollector; @@ -80,7 +81,8 @@ NS_ASSUME_NONNULL_BEGIN /** Creates a new instance of the FSTLocalStore with its required dependencies as parameters. */ - (instancetype)initWithPersistence:(id)persistence garbageCollector:(id)garbageCollector - initialUser:(FSTUser *)initialUser NS_DESIGNATED_INITIALIZER; + initialUser:(const firebase::firestore::auth::User &)initialUser + NS_DESIGNATED_INITIALIZER; - (instancetype)init NS_UNAVAILABLE; @@ -96,7 +98,7 @@ NS_ASSUME_NONNULL_BEGIN * In response the local store switches the mutation queue to the new user and returns any * resulting document changes. */ -- (FSTMaybeDocumentDictionary *)userDidChange:(FSTUser *)user; +- (FSTMaybeDocumentDictionary *)userDidChange:(const firebase::firestore::auth::User &)user; /** Accepts locally generated Mutations and commits them to storage. */ - (FSTLocalWriteResult *)locallyWriteMutations:(NSArray *)mutations; diff --git a/Firestore/Source/Local/FSTLocalStore.m b/Firestore/Source/Local/FSTLocalStore.mm similarity index 93% rename from Firestore/Source/Local/FSTLocalStore.m rename to Firestore/Source/Local/FSTLocalStore.mm index cde7104e222..a5b85cdc729 100644 --- a/Firestore/Source/Local/FSTLocalStore.m +++ b/Firestore/Source/Local/FSTLocalStore.mm @@ -16,11 +16,10 @@ #import "Firestore/Source/Local/FSTLocalStore.h" -#import "Firestore/Source/Auth/FSTUser.h" +#import "FIRTimestamp.h" +#import "Firestore/Source/Core/FSTListenSequence.h" #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Core/FSTSnapshotVersion.h" -#import "Firestore/Source/Core/FSTTargetIDGenerator.h" -#import "Firestore/Source/Core/FSTTimestamp.h" #import "Firestore/Source/Local/FSTGarbageCollector.h" #import "Firestore/Source/Local/FSTLocalDocumentsView.h" #import "Firestore/Source/Local/FSTLocalViewChanges.h" @@ -41,6 +40,12 @@ #import "Firestore/Source/Util/FSTAssert.h" #import "Firestore/Source/Util/FSTLogger.h" +#include "Firestore/core/src/firebase/firestore/auth/user.h" +#include "Firestore/core/src/firebase/firestore/core/target_id_generator.h" + +using firebase::firestore::auth::User; +using firebase::firestore::core::TargetIdGenerator; + NS_ASSUME_NONNULL_BEGIN @interface FSTLocalStore () @@ -73,8 +78,7 @@ @interface FSTLocalStore () /** Maps a targetID to data about its query. */ @property(nonatomic, strong) NSMutableDictionary *targetIDs; -/** Used to generate targetIDs for queries tracked locally. */ -@property(nonatomic, strong) FSTTargetIDGenerator *targetIDGenerator; +@property(nonatomic, strong) FSTListenSequence *listenSequence; /** * A heldBatchResult is a mutation batch result (from a write acknowledgement) that arrived before @@ -89,11 +93,14 @@ @interface FSTLocalStore () @end -@implementation FSTLocalStore +@implementation FSTLocalStore { + /** Used to generate targetIDs for queries tracked locally. */ + TargetIdGenerator _targetIDGenerator; +} - (instancetype)initWithPersistence:(id)persistence garbageCollector:(id)garbageCollector - initialUser:(FSTUser *)initialUser { + initialUser:(const User &)initialUser { if (self = [super init]) { _persistence = persistence; _mutationQueue = [persistence mutationQueueForUser:initialUser]; @@ -110,6 +117,8 @@ - (instancetype)initWithPersistence:(id)persistence _targetIDs = [NSMutableDictionary dictionary]; _heldBatchResults = [NSMutableArray array]; + + _targetIDGenerator = TargetIdGenerator::LocalStoreTargetIdGenerator(0); } return self; } @@ -147,7 +156,9 @@ - (void)startQueryCache { [self.queryCache start]; FSTTargetID targetID = [self.queryCache highestTargetID]; - self.targetIDGenerator = [FSTTargetIDGenerator generatorForLocalStoreStartingAfterID:targetID]; + _targetIDGenerator = TargetIdGenerator::LocalStoreTargetIdGenerator(targetID); + FSTListenSequenceNumber sequenceNumber = [self.queryCache highestListenSequenceNumber]; + self.listenSequence = [[FSTListenSequence alloc] initStartingAfter:sequenceNumber]; } - (void)shutdown { @@ -156,7 +167,7 @@ - (void)shutdown { [self.queryCache shutdown]; } -- (FSTMaybeDocumentDictionary *)userDidChange:(FSTUser *)user { +- (FSTMaybeDocumentDictionary *)userDidChange:(const User &)user { // Swap out the mutation queue, grabbing the pending mutation batches before and after. NSArray *oldBatches = [self.mutationQueue allMutationBatches]; @@ -190,7 +201,7 @@ - (FSTMaybeDocumentDictionary *)userDidChange:(FSTUser *)user { - (FSTLocalWriteResult *)locallyWriteMutations:(NSArray *)mutations { FSTWriteGroup *group = [self.persistence startGroupWithAction:@"Locally write mutations"]; - FSTTimestamp *localWriteTime = [FSTTimestamp timestamp]; + FIRTimestamp *localWriteTime = [FIRTimestamp timestamp]; FSTMutationBatch *batch = [self.mutationQueue addMutationBatchWithWriteTime:localWriteTime mutations:mutations group:group]; @@ -302,7 +313,7 @@ - (FSTMaybeDocumentDictionary *)applyRemoteEvent:(FSTRemoteEvent *)remoteEvent { queryData = [queryData queryDataByReplacingSnapshotVersion:change.snapshotVersion resumeToken:resumeToken]; self.targetIDs[targetIDNumber] = queryData; - [self.queryCache addQueryData:queryData group:group]; + [self.queryCache updateQueryData:queryData group:group]; } }]; @@ -380,6 +391,7 @@ - (nullable FSTMaybeDocument *)readDocument:(FSTDocumentKey *)key { - (FSTQueryData *)allocateQuery:(FSTQuery *)query { FSTQueryData *cached = [self.queryCache queryDataForQuery:query]; FSTTargetID targetID; + FSTListenSequenceNumber sequenceNumber = [self.listenSequence next]; if (cached) { // This query has been listened to previously, so reuse the previous targetID. // TODO(mcg): freshen last accessed date? @@ -387,9 +399,11 @@ - (FSTQueryData *)allocateQuery:(FSTQuery *)query { } else { FSTWriteGroup *group = [self.persistence startGroupWithAction:@"Allocate query"]; - targetID = [self.targetIDGenerator nextID]; - cached = - [[FSTQueryData alloc] initWithQuery:query targetID:targetID purpose:FSTQueryPurposeListen]; + targetID = _targetIDGenerator.NextId(); + cached = [[FSTQueryData alloc] initWithQuery:query + targetID:targetID + listenSequenceNumber:sequenceNumber + purpose:FSTQueryPurposeListen]; [self.queryCache addQueryData:cached group:group]; [self.persistence commitGroup:group]; diff --git a/Firestore/Source/Local/FSTLocalViewChanges.m b/Firestore/Source/Local/FSTLocalViewChanges.mm similarity index 100% rename from Firestore/Source/Local/FSTLocalViewChanges.m rename to Firestore/Source/Local/FSTLocalViewChanges.mm diff --git a/Firestore/Source/Local/FSTLocalWriteResult.m b/Firestore/Source/Local/FSTLocalWriteResult.mm similarity index 100% rename from Firestore/Source/Local/FSTLocalWriteResult.m rename to Firestore/Source/Local/FSTLocalWriteResult.mm diff --git a/Firestore/Source/Local/FSTMemoryMutationQueue.m b/Firestore/Source/Local/FSTMemoryMutationQueue.mm similarity index 98% rename from Firestore/Source/Local/FSTMemoryMutationQueue.m rename to Firestore/Source/Local/FSTMemoryMutationQueue.mm index b155264b9e6..2a6a1cc4f28 100644 --- a/Firestore/Source/Local/FSTMemoryMutationQueue.m +++ b/Firestore/Source/Local/FSTMemoryMutationQueue.mm @@ -23,10 +23,13 @@ #import "Firestore/Source/Model/FSTMutationBatch.h" #import "Firestore/Source/Model/FSTPath.h" #import "Firestore/Source/Util/FSTAssert.h" -#import "Firestore/Source/Util/FSTComparison.h" NS_ASSUME_NONNULL_BEGIN +static const NSComparator NumberComparator = ^NSComparisonResult(NSNumber *left, NSNumber *right) { + return [left compare:right]; +}; + @interface FSTMemoryMutationQueue () /** @@ -136,7 +139,7 @@ - (void)setLastStreamToken:(nullable NSData *)streamToken group:(__unused FSTWri self.lastStreamToken = streamToken; } -- (FSTMutationBatch *)addMutationBatchWithWriteTime:(FSTTimestamp *)localWriteTime +- (FSTMutationBatch *)addMutationBatchWithWriteTime:(FIRTimestamp *)localWriteTime mutations:(NSArray *)mutations group:(FSTWriteGroup *)group { FSTAssert(mutations.count > 0, @"Mutation batches should not be empty"); @@ -260,7 +263,7 @@ - (nullable FSTMutationBatch *)nextMutationBatchAfterBatchID:(FSTBatchID)batchID // Find unique batchIDs referenced by all documents potentially matching the query. __block FSTImmutableSortedSet *uniqueBatchIDs = - [FSTImmutableSortedSet setWithComparator:FSTNumberComparator]; + [FSTImmutableSortedSet setWithComparator:NumberComparator]; FSTDocumentReferenceBlock block = ^(FSTDocumentReference *reference, BOOL *stop) { FSTResourcePath *rowKeyPath = reference.key.path; if (![prefix isPrefixOfPath:rowKeyPath]) { diff --git a/Firestore/Source/Local/FSTMemoryPersistence.m b/Firestore/Source/Local/FSTMemoryPersistence.mm similarity index 88% rename from Firestore/Source/Local/FSTMemoryPersistence.m rename to Firestore/Source/Local/FSTMemoryPersistence.mm index e30182055e4..ba71f9cebf1 100644 --- a/Firestore/Source/Local/FSTMemoryPersistence.m +++ b/Firestore/Source/Local/FSTMemoryPersistence.mm @@ -16,7 +16,8 @@ #import "Firestore/Source/Local/FSTMemoryPersistence.h" -#import "Firestore/Source/Auth/FSTUser.h" +#include + #import "Firestore/Source/Local/FSTMemoryMutationQueue.h" #import "Firestore/Source/Local/FSTMemoryQueryCache.h" #import "Firestore/Source/Local/FSTMemoryRemoteDocumentCache.h" @@ -24,12 +25,15 @@ #import "Firestore/Source/Local/FSTWriteGroupTracker.h" #import "Firestore/Source/Util/FSTAssert.h" +#include "Firestore/core/src/firebase/firestore/auth/user.h" + +using firebase::firestore::auth::HashUser; +using firebase::firestore::auth::User; + NS_ASSUME_NONNULL_BEGIN @interface FSTMemoryPersistence () @property(nonatomic, strong, nonnull) FSTWriteGroupTracker *writeGroupTracker; -@property(nonatomic, strong, nonnull) - NSMutableDictionary> *mutationQueues; @property(nonatomic, assign, getter=isStarted) BOOL started; @end @@ -46,6 +50,8 @@ @implementation FSTMemoryPersistence { /** The FSTRemoteDocumentCache representing the persisted cache of remote documents. */ FSTMemoryRemoteDocumentCache *_remoteDocumentCache; + + std::unordered_map, HashUser> _mutationQueues; } + (instancetype)persistence { @@ -57,7 +63,6 @@ - (instancetype)init { _writeGroupTracker = [FSTWriteGroupTracker tracker]; _queryCache = [[FSTMemoryQueryCache alloc] init]; _remoteDocumentCache = [[FSTMemoryRemoteDocumentCache alloc] init]; - _mutationQueues = [NSMutableDictionary dictionary]; } return self; } @@ -75,11 +80,11 @@ - (void)shutdown { self.started = NO; } -- (id)mutationQueueForUser:(FSTUser *)user { - id queue = self.mutationQueues[user]; +- (id)mutationQueueForUser:(const User &)user { + id queue = _mutationQueues[user]; if (!queue) { queue = [FSTMemoryMutationQueue mutationQueue]; - self.mutationQueues[user] = queue; + _mutationQueues[user] = queue; } return queue; } diff --git a/Firestore/Source/Local/FSTMemoryQueryCache.m b/Firestore/Source/Local/FSTMemoryQueryCache.mm similarity index 83% rename from Firestore/Source/Local/FSTMemoryQueryCache.m rename to Firestore/Source/Local/FSTMemoryQueryCache.mm index 8d37bcbbc9f..56d5699c6c2 100644 --- a/Firestore/Source/Local/FSTMemoryQueryCache.m +++ b/Firestore/Source/Local/FSTMemoryQueryCache.mm @@ -34,6 +34,8 @@ @interface FSTMemoryQueryCache () /** The highest numbered target ID encountered. */ @property(nonatomic, assign) FSTTargetID highestTargetID; +@property(nonatomic, assign) FSTListenSequenceNumber highestListenSequenceNumber; + @end @implementation FSTMemoryQueryCache { @@ -65,6 +67,10 @@ - (FSTTargetID)highestTargetID { return _highestTargetID; } +- (FSTListenSequenceNumber)highestListenSequenceNumber { + return _highestListenSequenceNumber; +} + - (FSTSnapshotVersion *)lastRemoteSnapshotVersion { return _lastRemoteSnapshotVersion; } @@ -79,6 +85,23 @@ - (void)addQueryData:(FSTQueryData *)queryData group:(__unused FSTWriteGroup *)g if (queryData.targetID > self.highestTargetID) { self.highestTargetID = queryData.targetID; } + if (queryData.sequenceNumber > self.highestListenSequenceNumber) { + self.highestListenSequenceNumber = queryData.sequenceNumber; + } +} + +- (void)updateQueryData:(FSTQueryData *)queryData group:(FSTWriteGroup *)group { + self.queries[queryData.query] = queryData; + if (queryData.targetID > self.highestTargetID) { + self.highestTargetID = queryData.targetID; + } + if (queryData.sequenceNumber > self.highestListenSequenceNumber) { + self.highestListenSequenceNumber = queryData.sequenceNumber; + } +} + +- (int32_t)count { + return (int32_t)[self.queries count]; } - (void)removeQueryData:(FSTQueryData *)queryData group:(__unused FSTWriteGroup *)group { diff --git a/Firestore/Source/Local/FSTMemoryRemoteDocumentCache.m b/Firestore/Source/Local/FSTMemoryRemoteDocumentCache.mm similarity index 100% rename from Firestore/Source/Local/FSTMemoryRemoteDocumentCache.m rename to Firestore/Source/Local/FSTMemoryRemoteDocumentCache.mm diff --git a/Firestore/Source/Local/FSTMutationQueue.h b/Firestore/Source/Local/FSTMutationQueue.h index a1eddd4ae15..12f32842428 100644 --- a/Firestore/Source/Local/FSTMutationQueue.h +++ b/Firestore/Source/Local/FSTMutationQueue.h @@ -23,7 +23,7 @@ @class FSTMutation; @class FSTMutationBatch; @class FSTQuery; -@class FSTTimestamp; +@class FIRTimestamp; @class FSTWriteGroup; NS_ASSUME_NONNULL_BEGIN @@ -75,7 +75,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)setLastStreamToken:(nullable NSData *)streamToken group:(FSTWriteGroup *)group; /** Creates a new mutation batch and adds it to this mutation queue. */ -- (FSTMutationBatch *)addMutationBatchWithWriteTime:(FSTTimestamp *)localWriteTime +- (FSTMutationBatch *)addMutationBatchWithWriteTime:(FIRTimestamp *)localWriteTime mutations:(NSArray *)mutations group:(FSTWriteGroup *)group; diff --git a/Firestore/Source/Local/FSTNoOpGarbageCollector.m b/Firestore/Source/Local/FSTNoOpGarbageCollector.mm similarity index 100% rename from Firestore/Source/Local/FSTNoOpGarbageCollector.m rename to Firestore/Source/Local/FSTNoOpGarbageCollector.mm diff --git a/Firestore/Source/Local/FSTPersistence.h b/Firestore/Source/Local/FSTPersistence.h index cf07a9e5689..30eb21121c3 100644 --- a/Firestore/Source/Local/FSTPersistence.h +++ b/Firestore/Source/Local/FSTPersistence.h @@ -16,7 +16,8 @@ #import -@class FSTUser; +#include "Firestore/core/src/firebase/firestore/auth/user.h" + @class FSTWriteGroup; @protocol FSTMutationQueue; @protocol FSTQueryCache; @@ -75,7 +76,7 @@ NS_ASSUME_NONNULL_BEGIN * implementation to the extent possible (e.g. in the case of uid switching from * sally=>jack=>sally, sally's mutation queue will be preserved). */ -- (id)mutationQueueForUser:(FSTUser *)user; +- (id)mutationQueueForUser:(const firebase::firestore::auth::User &)user; /** Creates an FSTQueryCache representing the persisted cache of queries. */ - (id)queryCache; diff --git a/Firestore/Source/Local/FSTQueryCache.h b/Firestore/Source/Local/FSTQueryCache.h index e0cf4c82b43..5c43de4a9c3 100644 --- a/Firestore/Source/Local/FSTQueryCache.h +++ b/Firestore/Source/Local/FSTQueryCache.h @@ -52,6 +52,11 @@ NS_ASSUME_NONNULL_BEGIN */ - (FSTTargetID)highestTargetID; +/** + * Returns the highest listen sequence number of any query seen by the cache. + */ +- (FSTListenSequenceNumber)highestListenSequenceNumber; + /** * A global snapshot version representing the last consistent snapshot we received from the * backend. This is monotonically increasing and any snapshots received from the backend prior to @@ -73,18 +78,29 @@ NS_ASSUME_NONNULL_BEGIN group:(FSTWriteGroup *)group; /** - * Adds or replaces an entry in the cache. + * Adds an entry in the cache. * - * The cache key is extracted from `queryData.query`. If there is already a cache entry for the - * key, it will be replaced. + * The cache key is extracted from `queryData.query`. The key must not already exist in the cache. * - * @param queryData An FSTQueryData instance to put in the cache. + * @param queryData A new FSTQueryData instance to put in the cache. */ - (void)addQueryData:(FSTQueryData *)queryData group:(FSTWriteGroup *)group; +/** + * Updates an entry in the cache. + * + * The cache key is extracted from `queryData.query`. The entry must already exist in the cache, + * and it will be replaced. + * @param queryData An FSTQueryData instance to replace an existing entry in the cache + */ +- (void)updateQueryData:(FSTQueryData *)queryData group:(FSTWriteGroup *)group; + /** Removes the cached entry for the given query data (no-op if no entry exists). */ - (void)removeQueryData:(FSTQueryData *)queryData group:(FSTWriteGroup *)group; +/** Returns the number of targets cached. */ +- (int32_t)count; + /** * Looks up an FSTQueryData entry in the cache. * diff --git a/Firestore/Source/Local/FSTQueryData.h b/Firestore/Source/Local/FSTQueryData.h index 048bfad60bd..5db2de63432 100644 --- a/Firestore/Source/Local/FSTQueryData.h +++ b/Firestore/Source/Local/FSTQueryData.h @@ -40,6 +40,7 @@ typedef NS_ENUM(NSInteger, FSTQueryPurpose) { - (instancetype)initWithQuery:(FSTQuery *)query targetID:(FSTTargetID)targetID + listenSequenceNumber:(FSTListenSequenceNumber)sequenceNumber purpose:(FSTQueryPurpose)purpose snapshotVersion:(FSTSnapshotVersion *)snapshotVersion resumeToken:(NSData *)resumeToken NS_DESIGNATED_INITIALIZER; @@ -47,6 +48,7 @@ typedef NS_ENUM(NSInteger, FSTQueryPurpose) { /** Convenience initializer for use when creating an FSTQueryData for the first time. */ - (instancetype)initWithQuery:(FSTQuery *)query targetID:(FSTTargetID)targetID + listenSequenceNumber:(FSTListenSequenceNumber)sequenceNumber purpose:(FSTQueryPurpose)purpose; - (instancetype)init NS_UNAVAILABLE; @@ -64,6 +66,8 @@ typedef NS_ENUM(NSInteger, FSTQueryPurpose) { */ @property(nonatomic, assign, readonly) FSTTargetID targetID; +@property(nonatomic, assign, readonly) FSTListenSequenceNumber sequenceNumber; + /** The purpose of the query. */ @property(nonatomic, assign, readonly) FSTQueryPurpose purpose; diff --git a/Firestore/Source/Local/FSTQueryData.m b/Firestore/Source/Local/FSTQueryData.mm similarity index 91% rename from Firestore/Source/Local/FSTQueryData.m rename to Firestore/Source/Local/FSTQueryData.mm index 080f136cb31..6bb716aecbc 100644 --- a/Firestore/Source/Local/FSTQueryData.m +++ b/Firestore/Source/Local/FSTQueryData.mm @@ -25,6 +25,7 @@ @implementation FSTQueryData - (instancetype)initWithQuery:(FSTQuery *)query targetID:(FSTTargetID)targetID + listenSequenceNumber:(FSTListenSequenceNumber)sequenceNumber purpose:(FSTQueryPurpose)purpose snapshotVersion:(FSTSnapshotVersion *)snapshotVersion resumeToken:(NSData *)resumeToken { @@ -32,6 +33,7 @@ - (instancetype)initWithQuery:(FSTQuery *)query if (self) { _query = query; _targetID = targetID; + _sequenceNumber = sequenceNumber; _purpose = purpose; _snapshotVersion = snapshotVersion; _resumeToken = [resumeToken copy]; @@ -41,9 +43,11 @@ - (instancetype)initWithQuery:(FSTQuery *)query - (instancetype)initWithQuery:(FSTQuery *)query targetID:(FSTTargetID)targetID + listenSequenceNumber:(FSTListenSequenceNumber)sequenceNumber purpose:(FSTQueryPurpose)purpose { return [self initWithQuery:query targetID:targetID + listenSequenceNumber:sequenceNumber purpose:purpose snapshotVersion:[FSTSnapshotVersion noVersion] resumeToken:[NSData data]]; @@ -83,6 +87,7 @@ - (instancetype)queryDataByReplacingSnapshotVersion:(FSTSnapshotVersion *)snapsh resumeToken:(NSData *)resumeToken { return [[FSTQueryData alloc] initWithQuery:self.query targetID:self.targetID + listenSequenceNumber:self.sequenceNumber purpose:self.purpose snapshotVersion:snapshotVersion resumeToken:resumeToken]; diff --git a/Firestore/Source/Local/FSTReferenceSet.m b/Firestore/Source/Local/FSTReferenceSet.mm similarity index 100% rename from Firestore/Source/Local/FSTReferenceSet.m rename to Firestore/Source/Local/FSTReferenceSet.mm diff --git a/Firestore/Source/Local/FSTRemoteDocumentChangeBuffer.m b/Firestore/Source/Local/FSTRemoteDocumentChangeBuffer.mm similarity index 100% rename from Firestore/Source/Local/FSTRemoteDocumentChangeBuffer.m rename to Firestore/Source/Local/FSTRemoteDocumentChangeBuffer.mm diff --git a/Firestore/Source/Local/FSTWriteGroup.h b/Firestore/Source/Local/FSTWriteGroup.h index 5ea03871d2b..c21ff72b23a 100644 --- a/Firestore/Source/Local/FSTWriteGroup.h +++ b/Firestore/Source/Local/FSTWriteGroup.h @@ -16,17 +16,10 @@ #import -#ifdef __cplusplus #include #include "Firestore/Source/Local/StringView.h" - -namespace leveldb { -class DB; -class Status; -} - -#endif +#include "leveldb/db.h" NS_ASSUME_NONNULL_BEGIN @@ -61,8 +54,6 @@ NS_ASSUME_NONNULL_BEGIN /** Returns YES if the write group has no messages in it. */ - (BOOL)isEmpty; -#ifdef __cplusplus - /** * Marks the given key for deletion. * @@ -90,8 +81,6 @@ NS_ASSUME_NONNULL_BEGIN /** Writes the contents to the given LevelDB. */ - (leveldb::Status)writeToDB:(std::shared_ptr)db; -#endif - @end NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Local/FSTWriteGroup.mm b/Firestore/Source/Local/FSTWriteGroup.mm index 6859d5303ee..80d09ca2ffa 100644 --- a/Firestore/Source/Local/FSTWriteGroup.mm +++ b/Firestore/Source/Local/FSTWriteGroup.mm @@ -23,9 +23,6 @@ #import "Firestore/Source/Local/FSTLevelDBKey.h" #import "Firestore/Source/Util/FSTAssert.h" -#include "Firestore/Port/ordered_code.h" - -using Firestore::OrderedCode; using Firestore::StringView; using leveldb::DB; using leveldb::Slice; diff --git a/Firestore/Source/Local/FSTWriteGroupTracker.m b/Firestore/Source/Local/FSTWriteGroupTracker.mm similarity index 100% rename from Firestore/Source/Local/FSTWriteGroupTracker.m rename to Firestore/Source/Local/FSTWriteGroupTracker.mm diff --git a/Firestore/Source/Local/StringView.h b/Firestore/Source/Local/StringView.h index b81b7b5f0fc..4e36cff1800 100644 --- a/Firestore/Source/Local/StringView.h +++ b/Firestore/Source/Local/StringView.h @@ -17,14 +17,11 @@ #ifndef IPHONE_FIRESTORE_SOURCE_LOCAL_STRING_VIEW_H_ #define IPHONE_FIRESTORE_SOURCE_LOCAL_STRING_VIEW_H_ -#ifndef __cplusplus -#error "StringView is Objective-C++ and can only be included from .mm files" -#endif - #import #include #include +#include "absl/strings/string_view.h" namespace Firestore { @@ -64,6 +61,10 @@ class StringView { StringView(leveldb::Slice slice) : data_(slice.data()), size_(slice.size()) { } + // Creates a StringView from the absl::string_view. + StringView(absl::string_view s) : data_(s.data()), size_(s.size()) { + } + // Creates a StringView from the given std::string. The string must be an // lvalue for the lifetime requirements to be satisfied. StringView(const std::string &str) : data_(str.data()), size_(str.size()) { @@ -76,6 +77,13 @@ class StringView { return leveldb::Slice(data_, size_); } + // Converts this StringView to a absl::string_view, which is an equivalent (and more + // functional) type. The returned string_view has the same lifetime as this + // StringView. + operator absl::string_view() { + return absl::string_view(data_, size_); + } + private: const char *data_; const size_t size_; diff --git a/Firestore/Source/Model/FSTDatabaseID.h b/Firestore/Source/Model/FSTDatabaseID.h deleted file mode 100644 index 442e7645737..00000000000 --- a/Firestore/Source/Model/FSTDatabaseID.h +++ /dev/null @@ -1,48 +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 - -NS_ASSUME_NONNULL_BEGIN - -/** FSTDatabaseID represents a particular database in the datastore. */ -@interface FSTDatabaseID : NSObject - -/** - * Creates and returns a new FSTDatabaseID. - * @param projectID The project for the database. - * @param databaseID The database in the project to use. - * @return A new instance of FSTDatabaseID. - */ -+ (instancetype)databaseIDWithProject:(NSString *)projectID database:(NSString *)databaseID; - -/** The project. */ -@property(nonatomic, copy, readonly) NSString *projectID; - -/** The database. */ -@property(nonatomic, copy, readonly) NSString *databaseID; - -/** Whether this is the default database of the project. */ -- (BOOL)isDefaultDatabase; - -- (NSComparisonResult)compare:(FSTDatabaseID *)other; -- (BOOL)isEqualToDatabaseId:(FSTDatabaseID *)databaseID; - -@end - -extern NSString *const kDefaultDatabaseID; - -NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Model/FSTDatabaseID.m b/Firestore/Source/Model/FSTDatabaseID.m deleted file mode 100644 index bff5855c145..00000000000 --- a/Firestore/Source/Model/FSTDatabaseID.m +++ /dev/null @@ -1,90 +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/Source/Model/FSTDatabaseID.h" - -#import "Firestore/Source/Util/FSTAssert.h" - -NS_ASSUME_NONNULL_BEGIN - -/** The default name for "unset" database ID in resource names. */ -NSString *const kDefaultDatabaseID = @"(default)"; - -#pragma mark - FSTDatabaseID - -@implementation FSTDatabaseID - -+ (instancetype)databaseIDWithProject:(NSString *)projectID database:(NSString *)databaseID { - return [[FSTDatabaseID alloc] initWithProject:projectID database:databaseID]; -} - -/** - * Designated initializer. - * - * @param projectID The project for the database. - * @param databaseID The database in the datastore. - */ -- (instancetype)initWithProject:(NSString *)projectID database:(NSString *)databaseID { - if (self = [super init]) { - FSTAssert(databaseID, @"databaseID cannot be nil"); - _projectID = [projectID copy]; - _databaseID = [databaseID copy]; - } - return self; -} - -- (BOOL)isEqual:(id)other { - if (other == self) return YES; - if (![[other class] isEqual:[self class]]) return NO; - - return [self isEqualToDatabaseId:other]; -} - -- (NSUInteger)hash { - NSUInteger hash = [self.projectID hash]; - hash = hash * 31u + [self.databaseID hash]; - return hash; -} - -- (NSString *)description { - return [NSString - stringWithFormat:@"", self.projectID, self.databaseID]; -} - -- (NSComparisonResult)compare:(FSTDatabaseID *)other { - NSComparisonResult cmp = [self.projectID compare:other.projectID]; - return cmp == NSOrderedSame ? [self.databaseID compare:other.databaseID] : cmp; -} - -- (BOOL)isDefaultDatabase { - return [self.databaseID isEqualToString:kDefaultDatabaseID]; -} - -- (BOOL)isEqualToDatabaseId:(FSTDatabaseID *)databaseID { - if (self == databaseID) return YES; - if (databaseID == nil) return NO; - if (self.projectID != databaseID.projectID && - ![self.projectID isEqualToString:databaseID.projectID]) - return NO; - if (self.databaseID != databaseID.databaseID && - ![self.databaseID isEqualToString:databaseID.databaseID]) - return NO; - return YES; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Model/FSTDocument.m b/Firestore/Source/Model/FSTDocument.mm similarity index 100% rename from Firestore/Source/Model/FSTDocument.m rename to Firestore/Source/Model/FSTDocument.mm diff --git a/Firestore/Source/Model/FSTDocumentDictionary.m b/Firestore/Source/Model/FSTDocumentDictionary.mm similarity index 100% rename from Firestore/Source/Model/FSTDocumentDictionary.m rename to Firestore/Source/Model/FSTDocumentDictionary.mm diff --git a/Firestore/Source/Model/FSTDocumentKey.m b/Firestore/Source/Model/FSTDocumentKey.mm similarity index 100% rename from Firestore/Source/Model/FSTDocumentKey.m rename to Firestore/Source/Model/FSTDocumentKey.mm diff --git a/Firestore/Source/Model/FSTDocumentKeySet.m b/Firestore/Source/Model/FSTDocumentKeySet.mm similarity index 100% rename from Firestore/Source/Model/FSTDocumentKeySet.m rename to Firestore/Source/Model/FSTDocumentKeySet.mm diff --git a/Firestore/Source/Model/FSTDocumentSet.h b/Firestore/Source/Model/FSTDocumentSet.h index a7f8c4a2edf..022e900a0d5 100644 --- a/Firestore/Source/Model/FSTDocumentSet.h +++ b/Firestore/Source/Model/FSTDocumentSet.h @@ -58,16 +58,6 @@ NS_ASSUME_NONNULL_BEGIN */ - (FSTDocument *_Nullable)lastDocument; -/** - * Returns the document previous to the document associated with the given key in the set according - * to its built in ordering. Returns nil if the document associated with the given key is the - * first document. - * - * @param key A key that must be present in the DocumentSet. - * @throws NSInvalidArgumentException if key is not present. - */ -- (FSTDocument *_Nullable)predecessorDocumentForKey:(FSTDocumentKey *)key; - /** * Returns the index of the document with the provided key in the document set. Returns NSNotFound * if the key is not present. diff --git a/Firestore/Source/Model/FSTDocumentSet.m b/Firestore/Source/Model/FSTDocumentSet.mm similarity index 92% rename from Firestore/Source/Model/FSTDocumentSet.m rename to Firestore/Source/Model/FSTDocumentSet.mm index c4c0f49747f..6f44799cbdc 100644 --- a/Firestore/Source/Model/FSTDocumentSet.m +++ b/Firestore/Source/Model/FSTDocumentSet.mm @@ -135,16 +135,6 @@ - (FSTDocument *_Nullable)lastDocument { return [self.sortedSet lastObject]; } -- (FSTDocument *_Nullable)predecessorDocumentForKey:(FSTDocumentKey *)key { - FSTDocument *doc = [self.index objectForKey:key]; - if (!doc) { - @throw [NSException exceptionWithName:NSInvalidArgumentException - reason:[NSString stringWithFormat:@"Key %@ does not exist", key] - userInfo:nil]; - } - return [self.sortedSet predecessorObject:doc]; -} - - (NSUInteger)indexOfKey:(FSTDocumentKey *)key { FSTDocument *doc = [self.index objectForKey:key]; return doc ? [self.sortedSet indexOfObject:doc] : NSNotFound; diff --git a/Firestore/Source/Model/FSTDocumentVersionDictionary.m b/Firestore/Source/Model/FSTDocumentVersionDictionary.mm similarity index 100% rename from Firestore/Source/Model/FSTDocumentVersionDictionary.m rename to Firestore/Source/Model/FSTDocumentVersionDictionary.mm diff --git a/Firestore/Source/Model/FSTFieldValue.h b/Firestore/Source/Model/FSTFieldValue.h index 93fd5c4e9de..be8ba4548ca 100644 --- a/Firestore/Source/Model/FSTFieldValue.h +++ b/Firestore/Source/Model/FSTFieldValue.h @@ -18,10 +18,11 @@ #import "Firestore/third_party/Immutable/FSTImmutableSortedDictionary.h" -@class FSTDatabaseID; +#include "Firestore/core/src/firebase/firestore/model/database_id.h" + @class FSTDocumentKey; @class FSTFieldPath; -@class FSTTimestamp; +@class FIRTimestamp; @class FSTFieldValueOptions; @class FIRGeoPoint; @class FIRSnapshotOptions; @@ -163,8 +164,8 @@ typedef NS_ENUM(NSInteger, FSTServerTimestampBehavior) { * A timestamp value stored in Firestore. */ @interface FSTTimestampValue : FSTFieldValue -+ (instancetype)timestampValue:(FSTTimestamp *)value; -- (FSTTimestamp *)internalValue; ++ (instancetype)timestampValue:(FIRTimestamp *)value; +- (FIRTimestamp *)internalValue; @end /** @@ -180,10 +181,10 @@ typedef NS_ENUM(NSInteger, FSTServerTimestampBehavior) { * sort by their localWriteTime. */ @interface FSTServerTimestampValue : FSTFieldValue -+ (instancetype)serverTimestampValueWithLocalWriteTime:(FSTTimestamp *)localWriteTime ++ (instancetype)serverTimestampValueWithLocalWriteTime:(FIRTimestamp *)localWriteTime previousValue:(nullable FSTFieldValue *)previousValue; -@property(nonatomic, strong, readonly) FSTTimestamp *localWriteTime; +@property(nonatomic, strong, readonly) FIRTimestamp *localWriteTime; @property(nonatomic, strong, readonly, nullable) FSTFieldValue *previousValue; @end @@ -208,9 +209,11 @@ typedef NS_ENUM(NSInteger, FSTServerTimestampBehavior) { * A reference value stored in Firestore. */ @interface FSTReferenceValue : FSTFieldValue -+ (instancetype)referenceValue:(FSTDocumentKey *)value databaseID:(FSTDatabaseID *)databaseID; ++ (instancetype)referenceValue:(FSTDocumentKey *)value + databaseID:(const firebase::firestore::model::DatabaseId *)databaseID; - (FSTDocumentKey *)valueWithOptions:(FSTFieldValueOptions *)options; -@property(nonatomic, strong, readonly) FSTDatabaseID *databaseID; +// Does not own this DatabaseId. +@property(nonatomic, assign, readonly) const firebase::firestore::model::DatabaseId *databaseID; @end /** diff --git a/Firestore/Source/Model/FSTFieldValue.m b/Firestore/Source/Model/FSTFieldValue.mm similarity index 85% rename from Firestore/Source/Model/FSTFieldValue.m rename to Firestore/Source/Model/FSTFieldValue.mm index a6326a777de..5ef64e134ff 100644 --- a/Firestore/Source/Model/FSTFieldValue.m +++ b/Firestore/Source/Model/FSTFieldValue.mm @@ -16,15 +16,30 @@ #import "Firestore/Source/Model/FSTFieldValue.h" +#import "FIRTimestamp.h" + +#include "Firestore/core/src/firebase/firestore/util/comparison.h" +#include "Firestore/core/src/firebase/firestore/util/string_apple.h" + #import "Firestore/Source/API/FIRGeoPoint+Internal.h" #import "Firestore/Source/API/FIRSnapshotOptions+Internal.h" -#import "Firestore/Source/Core/FSTTimestamp.h" -#import "Firestore/Source/Model/FSTDatabaseID.h" #import "Firestore/Source/Model/FSTDocumentKey.h" #import "Firestore/Source/Model/FSTPath.h" #import "Firestore/Source/Util/FSTAssert.h" #import "Firestore/Source/Util/FSTClasses.h" -#import "Firestore/Source/Util/FSTComparison.h" + +#include "Firestore/core/src/firebase/firestore/model/database_id.h" +#include "Firestore/core/src/firebase/firestore/util/string_apple.h" + +namespace util = firebase::firestore::util; +using firebase::firestore::model::DatabaseId; +using firebase::firestore::util::Comparator; +using firebase::firestore::util::CompareMixedNumber; +using firebase::firestore::util::DoubleBitwiseEquals; +using firebase::firestore::util::DoubleBitwiseHash; +using firebase::firestore::util::MakeStringView; +using firebase::firestore::util::ReverseOrder; +using firebase::firestore::util::WrapCompare; NS_ASSUME_NONNULL_BEGIN @@ -208,7 +223,7 @@ - (NSUInteger)hash { - (NSComparisonResult)compare:(FSTFieldValue *)other { if ([other isKindOfClass:[FSTBooleanValue class]]) { - return FSTCompareBools(self.internalValue, ((FSTBooleanValue *)other).internalValue); + return WrapCompare(self.internalValue, ((FSTBooleanValue *)other).internalValue); } else { return [self defaultCompare:other]; } @@ -231,19 +246,22 @@ - (NSComparisonResult)compare:(FSTFieldValue *)other { if ([self isKindOfClass:[FSTDoubleValue class]]) { double thisDouble = ((FSTDoubleValue *)self).internalValue; if ([other isKindOfClass:[FSTDoubleValue class]]) { - return FSTCompareDoubles(thisDouble, ((FSTDoubleValue *)other).internalValue); + return WrapCompare(thisDouble, ((FSTDoubleValue *)other).internalValue); } else { FSTAssert([other isKindOfClass:[FSTIntegerValue class]], @"Unknown number value: %@", other); - return FSTCompareMixed(thisDouble, ((FSTIntegerValue *)other).internalValue); + auto result = CompareMixedNumber(thisDouble, ((FSTIntegerValue *)other).internalValue); + return static_cast(result); } } else { int64_t thisInt = ((FSTIntegerValue *)self).internalValue; if ([other isKindOfClass:[FSTIntegerValue class]]) { - return FSTCompareInt64s(thisInt, ((FSTIntegerValue *)other).internalValue); + return WrapCompare(thisInt, ((FSTIntegerValue *)other).internalValue); } else { FSTAssert([other isKindOfClass:[FSTDoubleValue class]], @"Unknown number value: %@", other); - return -1 * FSTCompareMixed(((FSTDoubleValue *)other).internalValue, thisInt); + double otherDouble = ((FSTDoubleValue *)other).internalValue; + auto result = ReverseOrder(CompareMixedNumber(otherDouble, thisInt)); + return static_cast(result); } } } @@ -334,11 +352,11 @@ - (BOOL)isEqual:(id)other { // NOTE: isEqual: should compare NaN equal to itself and -0.0 not equal to 0.0. return [other isKindOfClass:[FSTDoubleValue class]] && - FSTDoubleBitwiseEquals(self.internalValue, ((FSTDoubleValue *)other).internalValue); + DoubleBitwiseEquals(self.internalValue, ((FSTDoubleValue *)other).internalValue); } - (NSUInteger)hash { - return FSTDoubleBitwiseHash(self.internalValue); + return DoubleBitwiseHash(self.internalValue); } // NOTE: compare: is implemented in NumberValue. @@ -347,6 +365,17 @@ - (NSUInteger)hash { #pragma mark - FSTStringValue +/** + * Specialization of Comparator for NSStrings. + */ +template <> +struct Comparator { + bool operator()(NSString *left, NSString *right) const { + Comparator lessThan; + return lessThan(MakeStringView(left), MakeStringView(right)); + } +}; + @interface FSTStringValue () @property(nonatomic, copy, readonly) NSString *internalValue; @end @@ -385,7 +414,7 @@ - (NSUInteger)hash { - (NSComparisonResult)compare:(FSTFieldValue *)other { if ([other isKindOfClass:[FSTStringValue class]]) { - return FSTCompareStrings(self.internalValue, ((FSTStringValue *)other).internalValue); + return WrapCompare(self.internalValue, ((FSTStringValue *)other).internalValue); } else { return [self defaultCompare:other]; } @@ -396,19 +425,19 @@ - (NSComparisonResult)compare:(FSTFieldValue *)other { #pragma mark - FSTTimestampValue @interface FSTTimestampValue () -@property(nonatomic, strong, readonly) FSTTimestamp *internalValue; +@property(nonatomic, strong, readonly) FIRTimestamp *internalValue; @end @implementation FSTTimestampValue -+ (instancetype)timestampValue:(FSTTimestamp *)value { ++ (instancetype)timestampValue:(FIRTimestamp *)value { return [[FSTTimestampValue alloc] initWithValue:value]; } -- (id)initWithValue:(FSTTimestamp *)value { +- (id)initWithValue:(FIRTimestamp *)value { self = [super init]; if (self) { - _internalValue = value; // FSTTimestamp is immutable. + _internalValue = value; // FIRTimestamp is immutable. } return self; } @@ -448,13 +477,13 @@ - (NSComparisonResult)compare:(FSTFieldValue *)other { @implementation FSTServerTimestampValue -+ (instancetype)serverTimestampValueWithLocalWriteTime:(FSTTimestamp *)localWriteTime ++ (instancetype)serverTimestampValueWithLocalWriteTime:(FIRTimestamp *)localWriteTime previousValue:(nullable FSTFieldValue *)previousValue { return [[FSTServerTimestampValue alloc] initWithLocalWriteTime:localWriteTime previousValue:previousValue]; } -- (id)initWithLocalWriteTime:(FSTTimestamp *)localWriteTime +- (id)initWithLocalWriteTime:(FIRTimestamp *)localWriteTime previousValue:(nullable FSTFieldValue *)previousValue { self = [super init]; if (self) { @@ -556,6 +585,22 @@ - (NSComparisonResult)compare:(FSTFieldValue *)other { #pragma mark - FSTBlobValue +static NSComparisonResult CompareBytes(NSData *left, NSData *right) { + NSUInteger minLength = MIN(left.length, right.length); + int result = memcmp(left.bytes, right.bytes, minLength); + if (result < 0) { + return NSOrderedAscending; + } else if (result > 0) { + return NSOrderedDescending; + } else if (left.length < right.length) { + return NSOrderedAscending; + } else if (left.length > right.length) { + return NSOrderedDescending; + } else { + return NSOrderedSame; + } +} + @interface FSTBlobValue () @property(nonatomic, copy, readonly) NSData *internalValue; @end @@ -594,7 +639,7 @@ - (NSUInteger)hash { - (NSComparisonResult)compare:(FSTFieldValue *)other { if ([other isKindOfClass:[FSTBlobValue class]]) { - return FSTCompareBytes(self.internalValue, ((FSTBlobValue *)other).internalValue); + return CompareBytes(self.internalValue, ((FSTBlobValue *)other).internalValue); } else { return [self defaultCompare:other]; } @@ -610,11 +655,11 @@ @interface FSTReferenceValue () @implementation FSTReferenceValue -+ (instancetype)referenceValue:(FSTDocumentKey *)value databaseID:(FSTDatabaseID *)databaseID { ++ (instancetype)referenceValue:(FSTDocumentKey *)value databaseID:(const DatabaseId *)databaseID { return [[FSTReferenceValue alloc] initWithValue:value databaseID:databaseID]; } -- (id)initWithValue:(FSTDocumentKey *)value databaseID:(FSTDatabaseID *)databaseID { +- (id)initWithValue:(FSTDocumentKey *)value databaseID:(const DatabaseId *)databaseID { self = [super init]; if (self) { _key = value; @@ -640,12 +685,11 @@ - (BOOL)isEqual:(id)other { } FSTReferenceValue *otherRef = (FSTReferenceValue *)other; - return [self.key isEqualToKey:otherRef.key] && - [self.databaseID isEqualToDatabaseId:otherRef.databaseID]; + return [self.key isEqualToKey:otherRef.key] && *self.databaseID == *otherRef.databaseID; } - (NSUInteger)hash { - NSUInteger result = [self.databaseID hash]; + NSUInteger result = self.databaseID->Hash(); result = 31 * result + [self.key hash]; return result; } @@ -653,7 +697,13 @@ - (NSUInteger)hash { - (NSComparisonResult)compare:(FSTFieldValue *)other { if ([other isKindOfClass:[FSTReferenceValue class]]) { FSTReferenceValue *ref = (FSTReferenceValue *)other; - NSComparisonResult cmp = [self.databaseID compare:ref.databaseID]; + NSComparisonResult cmp = [util::WrapNSStringNoCopy(self.databaseID->project_id()) + compare:util::WrapNSStringNoCopy(ref.databaseID->project_id())]; + if (cmp != NSOrderedSame) { + return cmp; + } + cmp = [util::WrapNSStringNoCopy(self.databaseID->database_id()) + compare:util::WrapNSStringNoCopy(ref.databaseID->database_id())]; return cmp != NSOrderedSame ? cmp : [self.key compare:ref.key]; } else { return [self defaultCompare:other]; @@ -664,6 +714,10 @@ - (NSComparisonResult)compare:(FSTFieldValue *)other { #pragma mark - FSTObjectValue +static const NSComparator StringComparator = ^NSComparisonResult(NSString *left, NSString *right) { + return WrapCompare(left, right); +}; + @interface FSTObjectValue () @property(nonatomic, strong, readonly) FSTImmutableSortedDictionary *internalValue; @@ -677,7 +731,7 @@ + (instancetype)objectValue { dispatch_once(&onceToken, ^{ FSTImmutableSortedDictionary *empty = - [FSTImmutableSortedDictionary dictionaryWithComparator:FSTStringComparator]; + [FSTImmutableSortedDictionary dictionaryWithComparator:StringComparator]; sharedEmptyInstance = [[FSTObjectValue alloc] initWithImmutableDictionary:empty]; }); return sharedEmptyInstance; @@ -694,7 +748,7 @@ - (instancetype)initWithImmutableDictionary: - (id)initWithDictionary:(NSDictionary *)value { FSTImmutableSortedDictionary *dictionary = - [FSTImmutableSortedDictionary dictionaryWithDictionary:value comparator:FSTStringComparator]; + [FSTImmutableSortedDictionary dictionaryWithDictionary:value comparator:StringComparator]; return [self initWithImmutableDictionary:dictionary]; } @@ -748,7 +802,7 @@ - (NSComparisonResult)compare:(FSTFieldValue *)other { key2 = [enumerator2 nextObject]; } // Only equal if both enumerators are exhausted. - return FSTCompareBools(key1 != nil, key2 != nil); + return WrapCompare(key1 != nil, key2 != nil); } else { return [self defaultCompare:other]; } @@ -876,7 +930,7 @@ - (NSComparisonResult)compare:(FSTFieldValue *)other { return cmp; } } - return FSTCompareUIntegers(selfArray.count, otherArray.count); + return WrapCompare(selfArray.count, otherArray.count); } else { return [self defaultCompare:other]; } diff --git a/Firestore/Source/Model/FSTMutation.h b/Firestore/Source/Model/FSTMutation.h index 7c5f6de1705..72f6a25382e 100644 --- a/Firestore/Source/Model/FSTMutation.h +++ b/Firestore/Source/Model/FSTMutation.h @@ -23,7 +23,7 @@ @class FSTMaybeDocument; @class FSTObjectValue; @class FSTSnapshotVersion; -@class FSTTimestamp; +@class FIRTimestamp; NS_ASSUME_NONNULL_BEGIN @@ -200,7 +200,7 @@ typedef NS_ENUM(NSUInteger, FSTPreconditionExists) { */ - (nullable FSTMaybeDocument *)applyTo:(nullable FSTMaybeDocument *)maybeDoc baseDocument:(nullable FSTMaybeDocument *)baseDoc - localWriteTime:(FSTTimestamp *)localWriteTime + localWriteTime:(FIRTimestamp *)localWriteTime mutationResult:(nullable FSTMutationResult *)mutationResult; /** @@ -209,7 +209,7 @@ typedef NS_ENUM(NSUInteger, FSTPreconditionExists) { */ - (nullable FSTMaybeDocument *)applyTo:(nullable FSTMaybeDocument *)maybeDoc baseDocument:(nullable FSTMaybeDocument *)baseDoc - localWriteTime:(nullable FSTTimestamp *)localWriteTime; + localWriteTime:(nullable FIRTimestamp *)localWriteTime; @property(nonatomic, strong, readonly) FSTDocumentKey *key; diff --git a/Firestore/Source/Model/FSTMutation.m b/Firestore/Source/Model/FSTMutation.mm similarity index 97% rename from Firestore/Source/Model/FSTMutation.m rename to Firestore/Source/Model/FSTMutation.mm index c2491387e1e..e70264488ab 100644 --- a/Firestore/Source/Model/FSTMutation.m +++ b/Firestore/Source/Model/FSTMutation.mm @@ -16,8 +16,9 @@ #import "Firestore/Source/Model/FSTMutation.h" +#import "FIRTimestamp.h" + #import "Firestore/Source/Core/FSTSnapshotVersion.h" -#import "Firestore/Source/Core/FSTTimestamp.h" #import "Firestore/Source/Model/FSTDocument.h" #import "Firestore/Source/Model/FSTDocumentKey.h" #import "Firestore/Source/Model/FSTFieldValue.h" @@ -238,14 +239,14 @@ - (instancetype)initWithKey:(FSTDocumentKey *)key precondition:(FSTPrecondition - (nullable FSTMaybeDocument *)applyTo:(nullable FSTMaybeDocument *)maybeDoc baseDocument:(nullable FSTMaybeDocument *)baseDoc - localWriteTime:(FSTTimestamp *)localWriteTime + localWriteTime:(FIRTimestamp *)localWriteTime mutationResult:(nullable FSTMutationResult *)mutationResult { @throw FSTAbstractMethodException(); // NOLINT } - (nullable FSTMaybeDocument *)applyTo:(nullable FSTMaybeDocument *)maybeDoc baseDocument:(nullable FSTMaybeDocument *)baseDoc - localWriteTime:(nullable FSTTimestamp *)localWriteTime { + localWriteTime:(nullable FIRTimestamp *)localWriteTime { return [self applyTo:maybeDoc baseDocument:baseDoc localWriteTime:localWriteTime mutationResult:nil]; } @@ -292,7 +293,7 @@ - (NSUInteger)hash { - (nullable FSTMaybeDocument *)applyTo:(nullable FSTMaybeDocument *)maybeDoc baseDocument:(nullable FSTMaybeDocument *)baseDoc - localWriteTime:(FSTTimestamp *)localWriteTime + localWriteTime:(FIRTimestamp *)localWriteTime mutationResult:(nullable FSTMutationResult *)mutationResult { if (mutationResult) { FSTAssert(!mutationResult.transformResults, @"Transform results received by FSTSetMutation."); @@ -368,7 +369,7 @@ - (NSString *)description { - (nullable FSTMaybeDocument *)applyTo:(nullable FSTMaybeDocument *)maybeDoc baseDocument:(nullable FSTMaybeDocument *)baseDoc - localWriteTime:(FSTTimestamp *)localWriteTime + localWriteTime:(FIRTimestamp *)localWriteTime mutationResult:(nullable FSTMutationResult *)mutationResult { if (mutationResult) { FSTAssert(!mutationResult.transformResults, @"Transform results received by FSTPatchMutation."); @@ -458,7 +459,7 @@ - (NSString *)description { - (nullable FSTMaybeDocument *)applyTo:(nullable FSTMaybeDocument *)maybeDoc baseDocument:(nullable FSTMaybeDocument *)baseDoc - localWriteTime:(FSTTimestamp *)localWriteTime + localWriteTime:(FIRTimestamp *)localWriteTime mutationResult:(nullable FSTMutationResult *)mutationResult { if (mutationResult) { FSTAssert(mutationResult.transformResults, @@ -500,7 +501,7 @@ - (nullable FSTMaybeDocument *)applyTo:(nullable FSTMaybeDocument *)maybeDoc */ - (NSArray *)localTransformResultsWithBaseDocument: (FSTMaybeDocument *_Nullable)baseDocument - writeTime:(FSTTimestamp *)localWriteTime { + writeTime:(FIRTimestamp *)localWriteTime { NSMutableArray *transformResults = [NSMutableArray array]; for (FSTFieldTransform *fieldTransform in self.fieldTransforms) { if ([fieldTransform.transform isKindOfClass:[FSTServerTimestampTransform class]]) { @@ -570,7 +571,7 @@ - (NSString *)description { - (nullable FSTMaybeDocument *)applyTo:(nullable FSTMaybeDocument *)maybeDoc baseDocument:(nullable FSTMaybeDocument *)baseDoc - localWriteTime:(FSTTimestamp *)localWriteTime + localWriteTime:(FIRTimestamp *)localWriteTime mutationResult:(nullable FSTMutationResult *)mutationResult { if (mutationResult) { FSTAssert(!mutationResult.transformResults, diff --git a/Firestore/Source/Model/FSTMutationBatch.h b/Firestore/Source/Model/FSTMutationBatch.h index 145adfafe3d..1de79feeb4c 100644 --- a/Firestore/Source/Model/FSTMutationBatch.h +++ b/Firestore/Source/Model/FSTMutationBatch.h @@ -21,7 +21,7 @@ #import "Firestore/Source/Model/FSTDocumentVersionDictionary.h" @class FSTMutation; -@class FSTTimestamp; +@class FIRTimestamp; @class FSTMutationResult; @class FSTMutationBatchResult; @class FSTSnapshotVersion; @@ -45,7 +45,7 @@ extern const FSTBatchID kFSTBatchIDUnknown; /** Initializes a mutation batch with the given batchID, localWriteTime, and mutations. */ - (instancetype)initWithBatchID:(FSTBatchID)batchID - localWriteTime:(FSTTimestamp *)localWriteTime + localWriteTime:(FIRTimestamp *)localWriteTime mutations:(NSArray *)mutations NS_DESIGNATED_INITIALIZER; - (id)init NS_UNAVAILABLE; @@ -86,7 +86,7 @@ extern const FSTBatchID kFSTBatchIDUnknown; - (FSTDocumentKeySet *)keys; @property(nonatomic, assign, readonly) FSTBatchID batchID; -@property(nonatomic, strong, readonly) FSTTimestamp *localWriteTime; +@property(nonatomic, strong, readonly) FIRTimestamp *localWriteTime; @property(nonatomic, strong, readonly) NSArray *mutations; @end diff --git a/Firestore/Source/Model/FSTMutationBatch.m b/Firestore/Source/Model/FSTMutationBatch.mm similarity index 98% rename from Firestore/Source/Model/FSTMutationBatch.m rename to Firestore/Source/Model/FSTMutationBatch.mm index 01adca795e6..07aadbb2fc9 100644 --- a/Firestore/Source/Model/FSTMutationBatch.m +++ b/Firestore/Source/Model/FSTMutationBatch.mm @@ -16,8 +16,9 @@ #import "Firestore/Source/Model/FSTMutationBatch.h" +#import "FIRTimestamp.h" + #import "Firestore/Source/Core/FSTSnapshotVersion.h" -#import "Firestore/Source/Core/FSTTimestamp.h" #import "Firestore/Source/Model/FSTDocument.h" #import "Firestore/Source/Model/FSTDocumentKey.h" #import "Firestore/Source/Model/FSTMutation.h" @@ -30,7 +31,7 @@ @implementation FSTMutationBatch - (instancetype)initWithBatchID:(FSTBatchID)batchID - localWriteTime:(FSTTimestamp *)localWriteTime + localWriteTime:(FIRTimestamp *)localWriteTime mutations:(NSArray *)mutations { self = [super init]; if (self) { diff --git a/Firestore/Source/Model/FSTPath.m b/Firestore/Source/Model/FSTPath.mm similarity index 100% rename from Firestore/Source/Model/FSTPath.m rename to Firestore/Source/Model/FSTPath.mm diff --git a/Firestore/Source/Public/FIRFirestore.h b/Firestore/Source/Public/FIRFirestore.h index 4c85aba6d9b..5e2b40fbd8e 100644 --- a/Firestore/Source/Public/FIRFirestore.h +++ b/Firestore/Source/Public/FIRFirestore.h @@ -136,8 +136,9 @@ NS_SWIFT_NAME(Firestore) #pragma mark - Logging /** Enables or disables logging from the Firestore client. */ -+ (void)enableLogging:(BOOL)logging - DEPRECATED_MSG_ATTRIBUTE("Use FIRSetLoggerLevel(FIRLoggerLevelDebug) to enable logging"); ++ (void)enableLogging:(BOOL)logging DEPRECATED_MSG_ATTRIBUTE( + "Use FirebaseConfiguration.shared.setLoggerLevel(.debug) to enable " + "logging."); #pragma mark - Network diff --git a/Firestore/Source/Core/FSTTimestamp.h b/Firestore/Source/Public/FIRTimestamp.h similarity index 52% rename from Firestore/Source/Core/FSTTimestamp.h rename to Firestore/Source/Public/FIRTimestamp.h index f86779d9593..d0a77f37366 100644 --- a/Firestore/Source/Core/FSTTimestamp.h +++ b/Firestore/Source/Public/FIRTimestamp.h @@ -1,5 +1,5 @@ /* - * Copyright 2017 Google + * Copyright 2018 Google * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,40 +19,50 @@ NS_ASSUME_NONNULL_BEGIN /** - * An FSTTimestamp represents an absolute time from the backend at up to nanosecond precision. - * An FSTTimestamp is represented in terms of UTC and does not have an associated timezone. + * A Timestamp represents a point in time independent of any time zone or calendar, represented as + * seconds and fractions of seconds at nanosecond resolution in UTC Epoch time. It is encoded using + * the Proleptic Gregorian Calendar which extends the Gregorian calendar backwards to year one. It + * is encoded assuming all minutes are 60 seconds long, i.e. leap seconds are "smeared" so that no + * leap second table is needed for interpretation. Range is from 0001-01-01T00:00:00Z to + * 9999-12-31T23:59:59.999999999Z. By restricting to that range, we ensure that we can convert to + * and from RFC 3339 date strings. + * + * @see https://github.com/google/protobuf/blob/master/src/google/protobuf/timestamp.proto for the + * reference timestamp definition. */ -@interface FSTTimestamp : NSObject +NS_SWIFT_NAME(Timestamp) +@interface FIRTimestamp : NSObject +/** */ - (instancetype)init NS_UNAVAILABLE; /** * Creates a new timestamp. * * @param seconds the number of seconds since epoch. - * @param nanos the number of nanoseconds after the seconds. + * @param nanoseconds the number of nanoseconds after the seconds. */ -- (instancetype)initWithSeconds:(int64_t)seconds nanos:(int32_t)nanos NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithSeconds:(int64_t)seconds + nanoseconds:(int32_t)nanoseconds NS_DESIGNATED_INITIALIZER; -/** Creates a new timestamp with the current date / time. */ -+ (instancetype)timestamp; +/** + * Creates a new timestamp. + * + * @param seconds the number of seconds since epoch. + * @param nanoseconds the number of nanoseconds after the seconds. + */ ++ (instancetype)timestampWithSeconds:(int64_t)seconds nanoseconds:(int32_t)nanoseconds; /** Creates a new timestamp from the given date. */ + (instancetype)timestampWithDate:(NSDate *)date; +/** Creates a new timestamp with the current date / time. */ ++ (instancetype)timestamp; + /** Returns a new NSDate corresponding to this timestamp. This may lose precision. */ - (NSDate *)approximateDateValue; -/** - * Converts the given date to a an ISO 8601 timestamp string, useful for rendering in JSON. - * - * ISO 8601 dates times in UTC look like this: "1912-04-14T23:40:00.000000000Z". - * - * @see http://www.ecma-international.org/ecma-262/6.0/#sec-date-time-string-format - */ -- (NSString *)ISO8601String; - -- (NSComparisonResult)compare:(FSTTimestamp *)other; +- (NSComparisonResult)compare:(FIRTimestamp *)other; /** * Represents seconds of UTC time since Unix epoch 1970-01-01T00:00:00Z. @@ -65,7 +75,7 @@ NS_ASSUME_NONNULL_BEGIN * fractions must still have non-negative nanos values that count forward in time. * Must be from 0 to 999,999,999 inclusive. */ -@property(nonatomic, assign, readonly) int32_t nanos; +@property(nonatomic, assign, readonly) int32_t nanoseconds; @end diff --git a/Firestore/Source/Public/FirebaseFirestore.h b/Firestore/Source/Public/FirebaseFirestore.h index ff110fd1e41..36f9fb72fff 100644 --- a/Firestore/Source/Public/FirebaseFirestore.h +++ b/Firestore/Source/Public/FirebaseFirestore.h @@ -29,5 +29,6 @@ #import "FIRQuerySnapshot.h" #import "FIRSetOptions.h" #import "FIRSnapshotMetadata.h" +#import "FIRTimestamp.h" #import "FIRTransaction.h" #import "FIRWriteBatch.h" diff --git a/Firestore/Source/Remote/FSTBufferedWriter.m b/Firestore/Source/Remote/FSTBufferedWriter.mm similarity index 100% rename from Firestore/Source/Remote/FSTBufferedWriter.m rename to Firestore/Source/Remote/FSTBufferedWriter.mm diff --git a/Firestore/Source/Remote/FSTDatastore.h b/Firestore/Source/Remote/FSTDatastore.h index 13d9eda64f7..7b8274c666e 100644 --- a/Firestore/Source/Remote/FSTDatastore.h +++ b/Firestore/Source/Remote/FSTDatastore.h @@ -18,7 +18,11 @@ #import "Firestore/Source/Core/FSTTypes.h" -@class FSTDatabaseInfo; +#include "Firestore/core/src/firebase/firestore/auth/credentials_provider.h" +#include "Firestore/core/src/firebase/firestore/core/database_info.h" +#include "Firestore/core/src/firebase/firestore/model/database_id.h" +#include "absl/strings/string_view.h" + @class FSTDocumentKey; @class FSTDispatchQueue; @class FSTMutation; @@ -32,9 +36,6 @@ @class GRPCCall; @class GRXWriter; -@protocol FSTCredentialsProvider; -@class FSTDatabaseID; - NS_ASSUME_NONNULL_BEGIN /** @@ -52,15 +53,17 @@ NS_ASSUME_NONNULL_BEGIN @interface FSTDatastore : NSObject /** Creates a new Datastore instance with the given database info. */ -+ (instancetype)datastoreWithDatabase:(FSTDatabaseInfo *)database ++ (instancetype)datastoreWithDatabase:(const firebase::firestore::core::DatabaseInfo *)databaseInfo workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue - credentials:(id)credentials; + credentials:(firebase::firestore::auth::CredentialsProvider *) + credentials; // no passing ownership - (instancetype)init __attribute__((unavailable("Use a static constructor method."))); -- (instancetype)initWithDatabaseInfo:(FSTDatabaseInfo *)databaseInfo +- (instancetype)initWithDatabaseInfo:(const firebase::firestore::core::DatabaseInfo *)databaseInfo workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue - credentials:(id)credentials + credentials:(firebase::firestore::auth::CredentialsProvider *) + credentials // no passing ownership NS_DESIGNATED_INITIALIZER; /** @@ -81,8 +84,8 @@ NS_ASSUME_NONNULL_BEGIN /** Adds headers to the RPC including any OAuth access token if provided .*/ + (void)prepareHeadersForRPC:(GRPCCall *)rpc - databaseID:(FSTDatabaseID *)databaseID - token:(nullable NSString *)token; + databaseID:(const firebase::firestore::model::DatabaseId *)databaseID + token:(const absl::string_view)token; /** Looks up a list of documents in datastore. */ - (void)lookupDocuments:(NSArray *)keys @@ -99,7 +102,8 @@ NS_ASSUME_NONNULL_BEGIN - (FSTWriteStream *)createWriteStream; /** The name of the database and the backend. */ -@property(nonatomic, strong, readonly) FSTDatabaseInfo *databaseInfo; +// Does not own this DatabaseInfo. +@property(nonatomic, assign, readonly) const firebase::firestore::core::DatabaseInfo *databaseInfo; @end diff --git a/Firestore/Source/Remote/FSTDatastore.m b/Firestore/Source/Remote/FSTDatastore.mm similarity index 81% rename from Firestore/Source/Remote/FSTDatastore.m rename to Firestore/Source/Remote/FSTDatastore.mm index 02d868c1900..cb4516ef9c2 100644 --- a/Firestore/Source/Remote/FSTDatastore.m +++ b/Firestore/Source/Remote/FSTDatastore.mm @@ -22,10 +22,7 @@ #import "FIRFirestoreErrors.h" #import "Firestore/Source/API/FIRFirestore+Internal.h" #import "Firestore/Source/API/FIRFirestoreVersion.h" -#import "Firestore/Source/Auth/FSTCredentialsProvider.h" -#import "Firestore/Source/Core/FSTDatabaseInfo.h" #import "Firestore/Source/Local/FSTLocalStore.h" -#import "Firestore/Source/Model/FSTDatabaseID.h" #import "Firestore/Source/Model/FSTDocument.h" #import "Firestore/Source/Model/FSTDocumentKey.h" #import "Firestore/Source/Model/FSTMutation.h" @@ -37,6 +34,19 @@ #import "Firestore/Protos/objc/google/firestore/v1beta1/Firestore.pbrpc.h" +#include "Firestore/core/src/firebase/firestore/auth/credentials_provider.h" +#include "Firestore/core/src/firebase/firestore/auth/token.h" +#include "Firestore/core/src/firebase/firestore/core/database_info.h" +#include "Firestore/core/src/firebase/firestore/model/database_id.h" +#include "Firestore/core/src/firebase/firestore/util/error_apple.h" +#include "Firestore/core/src/firebase/firestore/util/string_apple.h" + +namespace util = firebase::firestore::util; +using firebase::firestore::auth::CredentialsProvider; +using firebase::firestore::auth::Token; +using firebase::firestore::core::DatabaseInfo; +using firebase::firestore::model::DatabaseId; + NS_ASSUME_NONNULL_BEGIN // GRPC does not publicly declare a means of disabling SSL, which we need for testing. Firestore @@ -62,8 +72,11 @@ @interface FSTDatastore () @property(nonatomic, strong, readonly) FSTDispatchQueue *workerDispatchQueue; -/** An object for getting an auth token before each request. */ -@property(nonatomic, strong, readonly) id credentials; +/** + * An object for getting an auth token before each request. Does not own the CredentialsProvider + * instance. + */ +@property(nonatomic, assign, readonly) CredentialsProvider *credentials; @property(nonatomic, strong, readonly) FSTSerializerBeta *serializer; @@ -71,33 +84,37 @@ @interface FSTDatastore () @implementation FSTDatastore -+ (instancetype)datastoreWithDatabase:(FSTDatabaseInfo *)databaseInfo ++ (instancetype)datastoreWithDatabase:(const DatabaseInfo *)databaseInfo workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue - credentials:(id)credentials { + credentials:(CredentialsProvider *)credentials { return [[FSTDatastore alloc] initWithDatabaseInfo:databaseInfo workerDispatchQueue:workerDispatchQueue credentials:credentials]; } -- (instancetype)initWithDatabaseInfo:(FSTDatabaseInfo *)databaseInfo +- (instancetype)initWithDatabaseInfo:(const DatabaseInfo *)databaseInfo workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue - credentials:(id)credentials { + credentials:(CredentialsProvider *)credentials { if (self = [super init]) { _databaseInfo = databaseInfo; - if (!databaseInfo.isSSLEnabled) { - GRPCHost *hostConfig = [GRPCHost hostWithAddress:databaseInfo.host]; + NSString *host = util::WrapNSStringNoCopy(databaseInfo->host()); + if (!databaseInfo->ssl_enabled()) { + GRPCHost *hostConfig = [GRPCHost hostWithAddress:host]; hostConfig.secure = NO; } - _service = [GCFSFirestore serviceWithHost:databaseInfo.host]; + _service = [GCFSFirestore serviceWithHost:host]; _workerDispatchQueue = workerDispatchQueue; _credentials = credentials; - _serializer = [[FSTSerializerBeta alloc] initWithDatabaseID:databaseInfo.databaseID]; + _serializer = [[FSTSerializerBeta alloc] initWithDatabaseID:&databaseInfo->database_id()]; } return self; } - (NSString *)description { - return [NSString stringWithFormat:@"", self.databaseInfo]; + return [NSString + stringWithFormat:@">", + util::WrapNSStringNoCopy(self.databaseInfo->database_id().database_id()), + util::WrapNSStringNoCopy(self.databaseInfo->host())]; } /** @@ -168,9 +185,10 @@ + (NSString *)googAPIClientHeaderValue { } /** Returns the string to be used as google-cloud-resource-prefix header value. */ -+ (NSString *)googleCloudResourcePrefixForDatabaseID:(FSTDatabaseID *)databaseID { - return [NSString - stringWithFormat:@"projects/%@/databases/%@", databaseID.projectID, databaseID.databaseID]; ++ (NSString *)googleCloudResourcePrefixForDatabaseID:(const DatabaseId *)databaseID { + return [NSString stringWithFormat:@"projects/%@/databases/%@", + util::WrapNSStringNoCopy(databaseID->project_id()), + util::WrapNSStringNoCopy(databaseID->database_id())]; } /** * Takes a dictionary of (HTTP) response headers and returns the set of whitelisted headers @@ -288,22 +306,24 @@ - (void)invokeRPCWithFactory:(GRPCProtoCall * (^)(void))rpcFactory errorHandler:(FSTVoidErrorBlock)errorHandler { // TODO(mikelehen): We should force a refresh if the previous RPC failed due to an expired token, // but I'm not sure how to detect that right now. http://b/32762461 - [self.credentials - getTokenForcingRefresh:NO - completion:^(FSTGetTokenResult *_Nullable result, NSError *_Nullable error) { - error = [FSTDatastore firestoreErrorForError:error]; - [self.workerDispatchQueue dispatchAsyncAllowingSameQueue:^{ - if (error) { - errorHandler(error); - } else { - GRPCProtoCall *rpc = rpcFactory(); - [FSTDatastore prepareHeadersForRPC:rpc - databaseID:self.databaseInfo.databaseID - token:result.token]; - [rpc start]; - } - }]; - }]; + _credentials->GetToken( + /*force_refresh=*/false, + [self, rpcFactory, errorHandler](Token result, const int64_t error_code, + const absl::string_view error_msg) { + NSError *error = util::WrapNSError(error_code, error_msg); + [self.workerDispatchQueue dispatchAsyncAllowingSameQueue:^{ + if (error) { + errorHandler(error); + } else { + GRPCProtoCall *rpc = rpcFactory(); + [FSTDatastore + prepareHeadersForRPC:rpc + databaseID:&self.databaseInfo->database_id() + token:(result.is_valid() ? result.token() : absl::string_view())]; + [rpc start]; + } + }]; + }); } - (FSTWatchStream *)createWatchStream { @@ -322,9 +342,9 @@ - (FSTWriteStream *)createWriteStream { /** Adds headers to the RPC including any OAuth access token if provided .*/ + (void)prepareHeadersForRPC:(GRPCCall *)rpc - databaseID:(FSTDatabaseID *)databaseID - token:(nullable NSString *)token { - rpc.oauth2AccessToken = token; + databaseID:(const DatabaseId *)databaseID + token:(const absl::string_view)token { + rpc.oauth2AccessToken = token.data() == nullptr ? nil : util::WrapNSString(token); rpc.requestHeaders[kXGoogAPIClientHeader] = [FSTDatastore googAPIClientHeaderValue]; // This header is used to improve routing and project isolation by the backend. rpc.requestHeaders[kGoogleCloudResourcePrefix] = diff --git a/Firestore/Source/Remote/FSTExistenceFilter.m b/Firestore/Source/Remote/FSTExistenceFilter.mm similarity index 100% rename from Firestore/Source/Remote/FSTExistenceFilter.m rename to Firestore/Source/Remote/FSTExistenceFilter.mm diff --git a/Firestore/Source/Remote/FSTExponentialBackoff.h b/Firestore/Source/Remote/FSTExponentialBackoff.h index 674b1ac0b44..03558eefe52 100644 --- a/Firestore/Source/Remote/FSTExponentialBackoff.h +++ b/Firestore/Source/Remote/FSTExponentialBackoff.h @@ -16,7 +16,7 @@ #import -@class FSTDispatchQueue; +#import "Firestore/Source/Util/FSTDispatchQueue.h" NS_ASSUME_NONNULL_BEGIN @@ -29,7 +29,7 @@ NS_ASSUME_NONNULL_BEGIN @interface FSTExponentialBackoff : NSObject /** - * Creates and returns a helper for running delayed tasks following an exponential backoff curve + * Initializes a helper for running delayed tasks following an exponential backoff curve * between attempts. * * Each delay is made up of a "base" delay which follows the exponential backoff curve, and a @@ -37,6 +37,7 @@ NS_ASSUME_NONNULL_BEGIN * accidentally synchronizing their delays causing spikes of load to the backend. * * @param dispatchQueue The dispatch queue to run tasks on. + * @param timerID The ID to use when scheduling backoff operations on the FSTDispatchQueue. * @param initialDelay The initial delay (used as the base delay on the first retry attempt). * Note that jitter will still be applied, so the actual delay could be as little as * 0.5*initialDelay. @@ -45,13 +46,13 @@ NS_ASSUME_NONNULL_BEGIN * @param maxDelay The maximum base delay after which no further backoff is performed. Note that * jitter will still be applied, so the actual delay could be as much as 1.5*maxDelay. */ -+ (instancetype)exponentialBackoffWithDispatchQueue:(FSTDispatchQueue *)dispatchQueue - initialDelay:(NSTimeInterval)initialDelay - backoffFactor:(double)backoffFactor - maxDelay:(NSTimeInterval)maxDelay; +- (instancetype)initWithDispatchQueue:(FSTDispatchQueue *)dispatchQueue + timerID:(FSTTimerID)timerID + initialDelay:(NSTimeInterval)initialDelay + backoffFactor:(double)backoffFactor + maxDelay:(NSTimeInterval)maxDelay NS_DESIGNATED_INITIALIZER; -- (instancetype)init - __attribute__((unavailable("Use exponentialBackoffWithDispatchQueue constructor method."))); +- (instancetype)init NS_UNAVAILABLE; /** * Resets the backoff delay. @@ -68,7 +69,8 @@ NS_ASSUME_NONNULL_BEGIN - (void)resetToMax; /** - * Waits for currentDelay seconds, increases the delay and runs the specified block. + * Waits for currentDelay seconds, increases the delay and runs the specified block. If there was + * a pending block waiting to be run already, it will be canceled. * * @param block The block to run. */ diff --git a/Firestore/Source/Remote/FSTExponentialBackoff.mm b/Firestore/Source/Remote/FSTExponentialBackoff.mm index 807735707da..dddf164b3d4 100644 --- a/Firestore/Source/Remote/FSTExponentialBackoff.mm +++ b/Firestore/Source/Remote/FSTExponentialBackoff.mm @@ -26,16 +26,14 @@ using firebase::firestore::util::SecureRandom; @interface FSTExponentialBackoff () -- (instancetype)initWithDispatchQueue:(FSTDispatchQueue *)dispatchQueue - initialDelay:(NSTimeInterval)initialDelay - backoffFactor:(double)backoffFactor - maxDelay:(NSTimeInterval)maxDelay NS_DESIGNATED_INITIALIZER; @property(nonatomic, strong) FSTDispatchQueue *dispatchQueue; +@property(nonatomic, assign, readonly) FSTTimerID timerID; @property(nonatomic) double backoffFactor; @property(nonatomic) NSTimeInterval initialDelay; @property(nonatomic) NSTimeInterval maxDelay; @property(nonatomic) NSTimeInterval currentBase; +@property(nonatomic, strong, nullable) FSTDelayedCallback *timerCallback; @end @implementation FSTExponentialBackoff { @@ -43,11 +41,13 @@ @implementation FSTExponentialBackoff { } - (instancetype)initWithDispatchQueue:(FSTDispatchQueue *)dispatchQueue + timerID:(FSTTimerID)timerID initialDelay:(NSTimeInterval)initialDelay backoffFactor:(double)backoffFactor maxDelay:(NSTimeInterval)maxDelay { if (self = [super init]) { _dispatchQueue = dispatchQueue; + _timerID = timerID; _initialDelay = initialDelay; _backoffFactor = backoffFactor; _maxDelay = maxDelay; @@ -57,16 +57,6 @@ - (instancetype)initWithDispatchQueue:(FSTDispatchQueue *)dispatchQueue return self; } -+ (instancetype)exponentialBackoffWithDispatchQueue:(FSTDispatchQueue *)dispatchQueue - initialDelay:(NSTimeInterval)initialDelay - backoffFactor:(double)backoffFactor - maxDelay:(NSTimeInterval)maxDelay { - return [[FSTExponentialBackoff alloc] initWithDispatchQueue:dispatchQueue - initialDelay:initialDelay - backoffFactor:backoffFactor - maxDelay:maxDelay]; -} - - (void)reset { _currentBase = 0; } @@ -76,6 +66,9 @@ - (void)resetToMax { } - (void)backoffAndRunBlock:(void (^)(void))block { + if (self.timerCallback) { + [self.timerCallback cancel]; + } // First schedule the block using the current base (which may be 0 and should be honored as such). NSTimeInterval delayWithJitter = _currentBase + [self jitterDelay]; if (_currentBase > 0) { @@ -83,7 +76,8 @@ - (void)backoffAndRunBlock:(void (^)(void))block { _currentBase); } - [self.dispatchQueue dispatchAfterDelay:delayWithJitter block:block]; + self.timerCallback = + [self.dispatchQueue dispatchAfterDelay:delayWithJitter timerID:self.timerID block:block]; // Apply backoff factor to determine next delay and ensure it is within bounds. _currentBase *= _backoffFactor; diff --git a/Firestore/Source/Remote/FSTRemoteEvent.m b/Firestore/Source/Remote/FSTRemoteEvent.mm similarity index 97% rename from Firestore/Source/Remote/FSTRemoteEvent.m rename to Firestore/Source/Remote/FSTRemoteEvent.mm index a97eb86680f..88999e41d0e 100644 --- a/Firestore/Source/Remote/FSTRemoteEvent.m +++ b/Firestore/Source/Remote/FSTRemoteEvent.mm @@ -278,6 +278,14 @@ @implementation FSTRemoteEvent return self; } +- (NSDictionary *)targetChanges { + return static_cast *>(_targetChanges); +} + +- (NSDictionary *)documentUpdates { + return static_cast *>(_documentUpdates); +} + /** Adds a document update to this remote event */ - (void)addDocumentUpdate:(FSTMaybeDocument *)document { _documentUpdates[document.key] = document; @@ -352,6 +360,10 @@ @implementation FSTWatchChangeAggregator { return self; } +- (NSDictionary *)existenceFilters { + return static_cast *>(_existenceFilters); +} + - (FSTTargetChange *)targetChangeForTargetID:(FSTBoxedTargetID *)targetID { FSTTargetChange *change = self.targetChanges[targetID]; if (!change) { diff --git a/Firestore/Source/Remote/FSTRemoteStore.h b/Firestore/Source/Remote/FSTRemoteStore.h index 18331fff69b..4ea93793748 100644 --- a/Firestore/Source/Remote/FSTRemoteStore.h +++ b/Firestore/Source/Remote/FSTRemoteStore.h @@ -19,7 +19,8 @@ #import "Firestore/Source/Core/FSTTypes.h" #import "Firestore/Source/Model/FSTDocumentVersionDictionary.h" -@class FSTDatabaseInfo; +#include "Firestore/core/src/firebase/firestore/auth/user.h" + @class FSTDatastore; @class FSTDocumentKey; @class FSTLocalStore; @@ -29,7 +30,6 @@ @class FSTQueryData; @class FSTRemoteEvent; @class FSTTransaction; -@class FSTUser; NS_ASSUME_NONNULL_BEGIN @@ -122,7 +122,7 @@ NS_ASSUME_NONNULL_BEGIN * In response the remote store tears down streams and clears up any tracked operations that should * not persist across users. Restarts the streams if appropriate. */ -- (void)userDidChange:(FSTUser *)user; +- (void)userDidChange:(const firebase::firestore::auth::User &)user; /** Listens to the target identified by the given FSTQueryData. */ - (void)listenToTargetWithQueryData:(FSTQueryData *)queryData; diff --git a/Firestore/Source/Remote/FSTRemoteStore.m b/Firestore/Source/Remote/FSTRemoteStore.mm similarity index 97% rename from Firestore/Source/Remote/FSTRemoteStore.m rename to Firestore/Source/Remote/FSTRemoteStore.mm index a0c5059440d..b2e401341a4 100644 --- a/Firestore/Source/Remote/FSTRemoteStore.m +++ b/Firestore/Source/Remote/FSTRemoteStore.mm @@ -16,6 +16,8 @@ #import "Firestore/Source/Remote/FSTRemoteStore.h" +#include + #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Core/FSTSnapshotVersion.h" #import "Firestore/Source/Core/FSTTransaction.h" @@ -33,6 +35,12 @@ #import "Firestore/Source/Util/FSTAssert.h" #import "Firestore/Source/Util/FSTLogger.h" +#include "Firestore/core/src/firebase/firestore/auth/user.h" +#include "Firestore/core/src/firebase/firestore/util/string_apple.h" + +namespace util = firebase::firestore::util; +using firebase::firestore::auth::User; + NS_ASSUME_NONNULL_BEGIN /** @@ -266,8 +274,9 @@ - (void)shutdown { [self updateOnlineState:FSTOnlineStateUnknown]; } -- (void)userDidChange:(FSTUser *)user { - FSTLog(@"FSTRemoteStore %p changing users: %@", (__bridge void *)self, user); +- (void)userDidChange:(const User &)user { + FSTLog(@"FSTRemoteStore %p changing users: %@", (__bridge void *)self, + util::WrapNSStringNoCopy(user.uid())); if ([self isNetworkEnabled]) { // Tear down and re-create our network streams. This will ensure we get a fresh auth token // for the new user and re-fill the write pipeline with new mutations from the LocalStore @@ -468,8 +477,10 @@ - (void)processBatchedWatchChanges:(NSArray *)changes [remoteEvent handleExistenceFilterMismatchForTargetID:target]; // Clear the resume token for the query, since we're in a known mismatch state. - queryData = - [[FSTQueryData alloc] initWithQuery:query targetID:targetID purpose:queryData.purpose]; + queryData = [[FSTQueryData alloc] initWithQuery:query + targetID:targetID + listenSequenceNumber:queryData.sequenceNumber + purpose:queryData.purpose]; self.listenTargets[target] = queryData; // Cause a hard reset by unwatching and rewatching immediately, but deliberately don't @@ -483,6 +494,7 @@ - (void)processBatchedWatchChanges:(NSArray *)changes FSTQueryData *requestQueryData = [[FSTQueryData alloc] initWithQuery:query targetID:targetID + listenSequenceNumber:queryData.sequenceNumber purpose:FSTQueryPurposeExistenceFilterMismatch]; [self sendWatchRequestWithQueryData:requestQueryData]; } @@ -495,10 +507,10 @@ - (void)processBatchedWatchChanges:(NSArray *)changes FSTBoxedTargetID *target, FSTTargetChange *change, BOOL *stop) { NSData *resumeToken = change.resumeToken; if (resumeToken.length > 0) { - FSTQueryData *queryData = _listenTargets[target]; + FSTQueryData *queryData = self->_listenTargets[target]; // A watched target might have been removed already. if (queryData) { - _listenTargets[target] = + self->_listenTargets[target] = [queryData queryDataByReplacingSnapshotVersion:change.snapshotVersion resumeToken:resumeToken]; } diff --git a/Firestore/Source/Remote/FSTSerializerBeta.h b/Firestore/Source/Remote/FSTSerializerBeta.h index 973f8665210..0f1c3aeccf6 100644 --- a/Firestore/Source/Remote/FSTSerializerBeta.h +++ b/Firestore/Source/Remote/FSTSerializerBeta.h @@ -16,7 +16,8 @@ #import -@class FSTDatabaseID; +#include "Firestore/core/src/firebase/firestore/model/database_id.h" + @class FSTDocumentKey; @class FSTFieldValue; @class FSTMaybeDocument; @@ -27,7 +28,7 @@ @class FSTQuery; @class FSTQueryData; @class FSTSnapshotVersion; -@class FSTTimestamp; +@class FIRTimestamp; @class FSTWatchChange; @class GCFSBatchGetDocumentsResponse; @@ -57,10 +58,11 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)init NS_UNAVAILABLE; -- (instancetype)initWithDatabaseID:(FSTDatabaseID *)databaseID NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithDatabaseID:(const firebase::firestore::model::DatabaseId *)databaseID + NS_DESIGNATED_INITIALIZER; -- (GPBTimestamp *)encodedTimestamp:(FSTTimestamp *)timestamp; -- (FSTTimestamp *)decodedTimestamp:(GPBTimestamp *)timestamp; +- (GPBTimestamp *)encodedTimestamp:(FIRTimestamp *)timestamp; +- (FIRTimestamp *)decodedTimestamp:(GPBTimestamp *)timestamp; - (GPBTimestamp *)encodedVersion:(FSTSnapshotVersion *)version; - (FSTSnapshotVersion *)decodedVersion:(GPBTimestamp *)version; diff --git a/Firestore/Source/Remote/FSTSerializerBeta.m b/Firestore/Source/Remote/FSTSerializerBeta.mm similarity index 92% rename from Firestore/Source/Remote/FSTSerializerBeta.m rename to Firestore/Source/Remote/FSTSerializerBeta.mm index 04785c263e9..ceb05010740 100644 --- a/Firestore/Source/Remote/FSTSerializerBeta.m +++ b/Firestore/Source/Remote/FSTSerializerBeta.mm @@ -16,7 +16,10 @@ #import "Firestore/Source/Remote/FSTSerializerBeta.h" +#include + #import +#import "FIRTimestamp.h" #import "Firestore/Protos/objc/google/firestore/v1beta1/Common.pbobjc.h" #import "Firestore/Protos/objc/google/firestore/v1beta1/Document.pbobjc.h" @@ -30,9 +33,7 @@ #import "FIRGeoPoint.h" #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Core/FSTSnapshotVersion.h" -#import "Firestore/Source/Core/FSTTimestamp.h" #import "Firestore/Source/Local/FSTQueryData.h" -#import "Firestore/Source/Model/FSTDatabaseID.h" #import "Firestore/Source/Model/FSTDocument.h" #import "Firestore/Source/Model/FSTDocumentKey.h" #import "Firestore/Source/Model/FSTFieldValue.h" @@ -43,15 +44,22 @@ #import "Firestore/Source/Remote/FSTWatchChange.h" #import "Firestore/Source/Util/FSTAssert.h" +#include "Firestore/core/src/firebase/firestore/model/database_id.h" +#include "Firestore/core/src/firebase/firestore/util/string_apple.h" + +namespace util = firebase::firestore::util; +using firebase::firestore::model::DatabaseId; + NS_ASSUME_NONNULL_BEGIN @interface FSTSerializerBeta () -@property(nonatomic, strong, readonly) FSTDatabaseID *databaseID; +// Does not own this DatabaseId. +@property(nonatomic, assign, readonly) const DatabaseId *databaseID; @end @implementation FSTSerializerBeta -- (instancetype)initWithDatabaseID:(FSTDatabaseID *)databaseID { +- (instancetype)initWithDatabaseID:(const DatabaseId *)databaseID { self = [super init]; if (self) { _databaseID = databaseID; @@ -61,15 +69,15 @@ - (instancetype)initWithDatabaseID:(FSTDatabaseID *)databaseID { #pragma mark - FSTSnapshotVersion <=> GPBTimestamp -- (GPBTimestamp *)encodedTimestamp:(FSTTimestamp *)timestamp { +- (GPBTimestamp *)encodedTimestamp:(FIRTimestamp *)timestamp { GPBTimestamp *result = [GPBTimestamp message]; result.seconds = timestamp.seconds; - result.nanos = timestamp.nanos; + result.nanos = timestamp.nanoseconds; return result; } -- (FSTTimestamp *)decodedTimestamp:(GPBTimestamp *)timestamp { - return [[FSTTimestamp alloc] initWithSeconds:timestamp.seconds nanos:timestamp.nanos]; +- (FIRTimestamp *)decodedTimestamp:(GPBTimestamp *)timestamp { + return [[FIRTimestamp alloc] initWithSeconds:timestamp.seconds nanoseconds:timestamp.nanos]; } - (GPBTimestamp *)encodedVersion:(FSTSnapshotVersion *)version { @@ -101,14 +109,16 @@ - (NSString *)encodedDocumentKey:(FSTDocumentKey *)key { - (FSTDocumentKey *)decodedDocumentKey:(NSString *)name { FSTResourcePath *path = [self decodedResourcePathWithDatabaseID:name]; - FSTAssert([[path segmentAtIndex:1] isEqualToString:self.databaseID.projectID], + FSTAssert([[path segmentAtIndex:1] + isEqualToString:util::WrapNSStringNoCopy(self.databaseID->project_id())], @"Tried to deserialize key from different project."); - FSTAssert([[path segmentAtIndex:3] isEqualToString:self.databaseID.databaseID], + FSTAssert([[path segmentAtIndex:3] + isEqualToString:util::WrapNSStringNoCopy(self.databaseID->database_id())], @"Tried to deserialize key from different datbase."); return [FSTDocumentKey keyWithPath:[self localResourcePathForQualifiedResourcePath:path]]; } -- (NSString *)encodedResourcePathForDatabaseID:(FSTDatabaseID *)databaseID +- (NSString *)encodedResourcePathForDatabaseID:(const DatabaseId *)databaseID path:(FSTResourcePath *)path { return [[[[self encodedResourcePathForDatabaseID:databaseID] pathByAppendingSegment:@"documents"] pathByAppendingPath:path] canonicalString]; @@ -137,9 +147,11 @@ - (FSTResourcePath *)decodedQueryPath:(NSString *)name { } } -- (FSTResourcePath *)encodedResourcePathForDatabaseID:(FSTDatabaseID *)databaseID { - return [FSTResourcePath - pathWithSegments:@[ @"projects", databaseID.projectID, @"databases", databaseID.databaseID ]]; +- (FSTResourcePath *)encodedResourcePathForDatabaseID:(const DatabaseId *)databaseID { + return [FSTResourcePath pathWithSegments:@[ + @"projects", util::WrapNSStringNoCopy(databaseID->project_id()), @"databases", + util::WrapNSStringNoCopy(databaseID->database_id()) + ]]; } - (FSTResourcePath *)localResourcePathForQualifiedResourcePath:(FSTResourcePath *)resourceName { @@ -161,41 +173,41 @@ - (NSString *)encodedDatabaseID { #pragma mark - FSTFieldValue <=> Value proto - (GCFSValue *)encodedFieldValue:(FSTFieldValue *)fieldValue { - Class class = [fieldValue class]; - if (class == [FSTNullValue class]) { + Class fieldClass = [fieldValue class]; + if (fieldClass == [FSTNullValue class]) { return [self encodedNull]; - } else if (class == [FSTBooleanValue class]) { + } else if (fieldClass == [FSTBooleanValue class]) { return [self encodedBool:[[fieldValue value] boolValue]]; - } else if (class == [FSTIntegerValue class]) { + } else if (fieldClass == [FSTIntegerValue class]) { return [self encodedInteger:[[fieldValue value] longLongValue]]; - } else if (class == [FSTDoubleValue class]) { + } else if (fieldClass == [FSTDoubleValue class]) { return [self encodedDouble:[[fieldValue value] doubleValue]]; - } else if (class == [FSTStringValue class]) { + } else if (fieldClass == [FSTStringValue class]) { return [self encodedString:[fieldValue value]]; - } else if (class == [FSTTimestampValue class]) { + } else if (fieldClass == [FSTTimestampValue class]) { return [self encodedTimestampValue:((FSTTimestampValue *)fieldValue).internalValue]; - } else if (class == [FSTGeoPointValue class]) { + } else if (fieldClass == [FSTGeoPointValue class]) { return [self encodedGeoPointValue:[fieldValue value]]; - } else if (class == [FSTBlobValue class]) { + } else if (fieldClass == [FSTBlobValue class]) { return [self encodedBlobValue:[fieldValue value]]; - } else if (class == [FSTReferenceValue class]) { + } else if (fieldClass == [FSTReferenceValue class]) { FSTReferenceValue *ref = (FSTReferenceValue *)fieldValue; return [self encodedReferenceValueForDatabaseID:[ref databaseID] key:[ref value]]; - } else if (class == [FSTObjectValue class]) { + } else if (fieldClass == [FSTObjectValue class]) { GCFSValue *result = [GCFSValue message]; result.mapValue = [self encodedMapValue:(FSTObjectValue *)fieldValue]; return result; - } else if (class == [FSTArrayValue class]) { + } else if (fieldClass == [FSTArrayValue class]) { GCFSValue *result = [GCFSValue message]; result.arrayValue = [self encodedArrayValue:(FSTArrayValue *)fieldValue]; return result; @@ -275,7 +287,7 @@ - (GCFSValue *)encodedString:(NSString *)value { return result; } -- (GCFSValue *)encodedTimestampValue:(FSTTimestamp *)value { +- (GCFSValue *)encodedTimestampValue:(FIRTimestamp *)value { GCFSValue *result = [GCFSValue message]; result.timestampValue = [self encodedTimestamp:value]; return result; @@ -293,8 +305,13 @@ - (GCFSValue *)encodedBlobValue:(NSData *)value { return result; } -- (GCFSValue *)encodedReferenceValueForDatabaseID:(FSTDatabaseID *)databaseID +- (GCFSValue *)encodedReferenceValueForDatabaseID:(const DatabaseId *)databaseID key:(FSTDocumentKey *)key { + FSTAssert(*databaseID == *self.databaseID, @"Database %@:%@ cannot encode reference from %@:%@", + util::WrapNSStringNoCopy(self.databaseID->project_id()), + util::WrapNSStringNoCopy(self.databaseID->database_id()), + util::WrapNSStringNoCopy(databaseID->project_id()), + util::WrapNSStringNoCopy(databaseID->database_id())); GCFSValue *result = [GCFSValue message]; result.referenceValue = [self encodedResourcePathForDatabaseID:databaseID path:key.path]; return result; @@ -304,10 +321,16 @@ - (FSTReferenceValue *)decodedReferenceValue:(NSString *)resourceName { FSTResourcePath *path = [self decodedResourcePathWithDatabaseID:resourceName]; NSString *project = [path segmentAtIndex:1]; NSString *database = [path segmentAtIndex:3]; - FSTDatabaseID *databaseID = [FSTDatabaseID databaseIDWithProject:project database:database]; FSTDocumentKey *key = [FSTDocumentKey keyWithPath:[self localResourcePathForQualifiedResourcePath:path]]; - return [FSTReferenceValue referenceValue:key databaseID:databaseID]; + + const DatabaseId database_id(util::MakeStringView(project), util::MakeStringView(database)); + FSTAssert(database_id == *self.databaseID, @"Database %@:%@ cannot encode reference from %@:%@", + util::WrapNSStringNoCopy(self.databaseID->project_id()), + util::WrapNSStringNoCopy(self.databaseID->database_id()), + util::WrapNSStringNoCopy(database_id.project_id()), + util::WrapNSStringNoCopy(database_id.database_id())); + return [FSTReferenceValue referenceValue:key databaseID:self.databaseID]; } - (GCFSArrayValue *)encodedArrayValue:(FSTArrayValue *)arrayValue { @@ -438,8 +461,8 @@ - (GCFSWrite *)encodedMutation:(FSTMutation *)mutation { proto.currentDocument.exists = YES; } else if (mutationClass == [FSTDeleteMutation class]) { - FSTDeleteMutation *delete = (FSTDeleteMutation *)mutation; - proto.delete_p = [self encodedDocumentKey:delete.key]; + FSTDeleteMutation *deleteMutation = (FSTDeleteMutation *)mutation; + proto.delete_p = [self encodedDocumentKey:deleteMutation.key]; } else { FSTFail(@"Unknown mutation type %@", NSStringFromClass(mutationClass)); diff --git a/Firestore/Source/Remote/FSTStream.h b/Firestore/Source/Remote/FSTStream.h index 546aa3d155d..e48f1da0fc1 100644 --- a/Firestore/Source/Remote/FSTStream.h +++ b/Firestore/Source/Remote/FSTStream.h @@ -17,8 +17,11 @@ #import #import "Firestore/Source/Core/FSTTypes.h" +#import "Firestore/Source/Util/FSTDispatchQueue.h" + +#include "Firestore/core/src/firebase/firestore/auth/credentials_provider.h" +#include "Firestore/core/src/firebase/firestore/core/database_info.h" -@class FSTDatabaseInfo; @class FSTDocumentKey; @class FSTDispatchQueue; @class FSTMutation; @@ -32,7 +35,6 @@ @class GRPCCall; @class GRXWriter; -@protocol FSTCredentialsProvider; @protocol FSTWatchStreamDelegate; @protocol FSTWriteStreamDelegate; @@ -45,7 +47,7 @@ NS_ASSUME_NONNULL_BEGIN * * - Restarting a stream is allowed (after failure) * - Exponential backoff on failure (independent of the underlying channel) - * - Authentication via FSTCredentialsProvider + * - Authentication via CredentialsProvider * - Dispatching all callbacks into the shared worker queue * * Subclasses of FSTStream implement serialization of models to and from bytes (via protocol @@ -88,9 +90,11 @@ NS_ASSUME_NONNULL_BEGIN */ @interface FSTStream <__covariant FSTStreamDelegate> : NSObject -- (instancetype)initWithDatabase:(FSTDatabaseInfo *)database +- (instancetype)initWithDatabase:(const firebase::firestore::core::DatabaseInfo *)database workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue - credentials:(id)credentials + connectionTimerID:(FSTTimerID)connectionTimerID + idleTimerID:(FSTTimerID)idleTimerID + credentials:(firebase::firestore::auth::CredentialsProvider *)credentials // no passing ownership responseMessageClass:(Class)responseMessageClass NS_DESIGNATED_INITIALIZER; - (instancetype)init NS_UNAVAILABLE; @@ -141,8 +145,13 @@ NS_ASSUME_NONNULL_BEGIN - (void)stop; /** - * Initializes the idle timer. If no write takes place within one minute, the GRPC stream will be - * closed. + * Marks this stream as idle. If no further actions are performed on the stream for one minute, the + * stream will automatically close itself and notify the stream's close handler. The stream will + * then be in a non-started state, requiring the caller to start the stream again before further + * use. + * + * Only streams that are in state 'Open' can be marked idle, as all other states imply pending + * network operations. */ - (void)markIdle; @@ -197,14 +206,18 @@ NS_ASSUME_NONNULL_BEGIN /** * Initializes the watch stream with its dependencies. */ -- (instancetype)initWithDatabase:(FSTDatabaseInfo *)database +- (instancetype)initWithDatabase:(const firebase::firestore::core::DatabaseInfo *)database workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue - credentials:(id)credentials + credentials:(firebase::firestore::auth::CredentialsProvider *) + credentials // no passsing ownership serializer:(FSTSerializerBeta *)serializer NS_DESIGNATED_INITIALIZER; -- (instancetype)initWithDatabase:(FSTDatabaseInfo *)database +- (instancetype)initWithDatabase:(const firebase::firestore::core::DatabaseInfo *)database workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue - credentials:(id)credentials + connectionTimerID:(FSTTimerID)connectionTimerID + idleTimerID:(FSTTimerID)idleTimerID + credentials:(firebase::firestore::auth::CredentialsProvider *) + credentials // no passing ownership responseMessageClass:(Class)responseMessageClass NS_UNAVAILABLE; - (instancetype)init NS_UNAVAILABLE; @@ -271,14 +284,18 @@ NS_ASSUME_NONNULL_BEGIN /** * Initializes the write stream with its dependencies. */ -- (instancetype)initWithDatabase:(FSTDatabaseInfo *)database +- (instancetype)initWithDatabase:(const firebase::firestore::core::DatabaseInfo *)database workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue - credentials:(id)credentials + credentials:(firebase::firestore::auth::CredentialsProvider *) + credentials // no passing ownership serializer:(FSTSerializerBeta *)serializer; -- (instancetype)initWithDatabase:(FSTDatabaseInfo *)database +- (instancetype)initWithDatabase:(const firebase::firestore::core::DatabaseInfo *)database workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue - credentials:(id)credentials + connectionTimerID:(FSTTimerID)connectionTimerID + idleTimerID:(FSTTimerID)idleTimerID + credentials:(firebase::firestore::auth::CredentialsProvider *) + credentials // no passing ownership responseMessageClass:(Class)responseMessageClass NS_UNAVAILABLE; - (instancetype)init NS_UNAVAILABLE; diff --git a/Firestore/Source/Remote/FSTStream.m b/Firestore/Source/Remote/FSTStream.mm similarity index 78% rename from Firestore/Source/Remote/FSTStream.m rename to Firestore/Source/Remote/FSTStream.mm index 5719ec85167..6bec3ad90cf 100644 --- a/Firestore/Source/Remote/FSTStream.m +++ b/Firestore/Source/Remote/FSTStream.mm @@ -21,10 +21,7 @@ #import "FIRFirestoreErrors.h" #import "Firestore/Source/API/FIRFirestore+Internal.h" -#import "Firestore/Source/Auth/FSTCredentialsProvider.h" -#import "Firestore/Source/Core/FSTDatabaseInfo.h" #import "Firestore/Source/Local/FSTQueryData.h" -#import "Firestore/Source/Model/FSTDatabaseID.h" #import "Firestore/Source/Model/FSTMutation.h" #import "Firestore/Source/Remote/FSTBufferedWriter.h" #import "Firestore/Source/Remote/FSTExponentialBackoff.h" @@ -37,6 +34,18 @@ #import "Firestore/Protos/objc/google/firestore/v1beta1/Firestore.pbrpc.h" +#include "Firestore/core/src/firebase/firestore/auth/token.h" +#include "Firestore/core/src/firebase/firestore/core/database_info.h" +#include "Firestore/core/src/firebase/firestore/model/database_id.h" +#include "Firestore/core/src/firebase/firestore/util/error_apple.h" +#include "Firestore/core/src/firebase/firestore/util/string_apple.h" + +namespace util = firebase::firestore::util; +using firebase::firestore::auth::CredentialsProvider; +using firebase::firestore::auth::Token; +using firebase::firestore::core::DatabaseInfo; +using firebase::firestore::model::DatabaseId; + /** * Initial backoff time in seconds after an error. * Set to 1s according to https://cloud.google.com/apis/design/errors. @@ -93,30 +102,32 @@ @interface FSTWatchStream () /** * Initializes the watch stream with its dependencies. */ -- (instancetype)initWithDatabase:(FSTDatabaseInfo *)database +- (instancetype)initWithDatabase:(const DatabaseInfo *)database workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue - credentials:(id)credentials + credentials:(CredentialsProvider *)credentials serializer:(FSTSerializerBeta *)serializer NS_DESIGNATED_INITIALIZER; -- (instancetype)initWithDatabase:(FSTDatabaseInfo *)database +- (instancetype)initWithDatabase:(const DatabaseInfo *)database workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue - credentials:(id)credentials + credentials:(CredentialsProvider *)credentials responseMessageClass:(Class)responseMessageClass NS_UNAVAILABLE; @end @interface FSTStream () -@property(nonatomic, getter=isIdle) BOOL idle; +@property(nonatomic, assign, readonly) FSTTimerID idleTimerID; +@property(nonatomic, strong, nullable) FSTDelayedCallback *idleTimerCallback; @property(nonatomic, weak, readwrite, nullable) id delegate; @end @interface FSTStream () -@property(nonatomic, strong, readonly) FSTDatabaseInfo *databaseInfo; +// Does not own this DatabaseInfo. +@property(nonatomic, assign, readonly) const DatabaseInfo *databaseInfo; @property(nonatomic, strong, readonly) FSTDispatchQueue *workerDispatchQueue; -@property(nonatomic, strong, readonly) id credentials; +@property(nonatomic, assign, readonly) CredentialsProvider *credentials; @property(nonatomic, unsafe_unretained, readonly) Class responseMessageClass; @property(nonatomic, strong, readonly) FSTExponentialBackoff *backoff; @@ -142,7 +153,12 @@ @interface FSTStream () #pragma mark - FSTCallbackFilter -/** Filter class that allows disabling of GRPC callbacks. */ +/** + * Implements callbacks from gRPC via the GRXWriteable protocol. This is separate from the main + * FSTStream to allow the stream to be stopped externally (either by the user or via idle timer) + * and be able to completely prevent any subsequent events from gRPC from calling back into the + * FSTSTream. + */ @interface FSTCallbackFilter : NSObject - (instancetype)initWithStream:(FSTStream *)stream NS_DESIGNATED_INITIALIZER; @@ -194,20 +210,24 @@ @implementation FSTStream /** The time a stream stays open after it is marked idle. */ static const NSTimeInterval kIdleTimeout = 60.0; -- (instancetype)initWithDatabase:(FSTDatabaseInfo *)database +- (instancetype)initWithDatabase:(const DatabaseInfo *)database workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue - credentials:(id)credentials + connectionTimerID:(FSTTimerID)connectionTimerID + idleTimerID:(FSTTimerID)idleTimerID + credentials:(CredentialsProvider *)credentials responseMessageClass:(Class)responseMessageClass { if (self = [super init]) { _databaseInfo = database; _workerDispatchQueue = workerDispatchQueue; + _idleTimerID = idleTimerID; _credentials = credentials; _responseMessageClass = responseMessageClass; - _backoff = [FSTExponentialBackoff exponentialBackoffWithDispatchQueue:workerDispatchQueue - initialDelay:kBackoffInitialDelay - backoffFactor:kBackoffFactor - maxDelay:kBackoffMaxDelay]; + _backoff = [[FSTExponentialBackoff alloc] initWithDispatchQueue:workerDispatchQueue + timerID:connectionTimerID + initialDelay:kBackoffInitialDelay + backoffFactor:kBackoffFactor + maxDelay:kBackoffMaxDelay]; _state = FSTStreamStateInitial; } return self; @@ -244,24 +264,24 @@ - (void)startWithDelegate:(id)delegate { FSTAssert(_delegate == nil, @"Delegate must be nil"); _delegate = delegate; - [self.credentials - getTokenForcingRefresh:NO - completion:^(FSTGetTokenResult *_Nullable result, NSError *_Nullable error) { - error = [FSTDatastore firestoreErrorForError:error]; - [self.workerDispatchQueue dispatchAsyncAllowingSameQueue:^{ - [self resumeStartWithToken:result error:error]; - }]; - }]; + _credentials->GetToken( + /*force_refresh=*/false, + [self](Token result, const int64_t error_code, const absl::string_view error_msg) { + NSError *error = util::WrapNSError(error_code, error_msg); + [self.workerDispatchQueue dispatchAsyncAllowingSameQueue:^{ + [self resumeStartWithToken:result error:error]; + }]; + }); } /** Add an access token to our RPC, after obtaining one from the credentials provider. */ -- (void)resumeStartWithToken:(FSTGetTokenResult *)token error:(NSError *)error { +- (void)resumeStartWithToken:(const Token &)token error:(NSError *)error { + [self.workerDispatchQueue verifyIsCurrentQueue]; + if (self.state == FSTStreamStateStopped) { // Streams can be stopped while waiting for authorization. return; } - - [self.workerDispatchQueue verifyIsCurrentQueue]; FSTAssert(self.state == FSTStreamStateAuth, @"State should still be auth (was %ld)", (long)self.state); @@ -275,9 +295,11 @@ - (void)resumeStartWithToken:(FSTGetTokenResult *)token error:(NSError *)error { self.requestsWriter = [[FSTBufferedWriter alloc] init]; _rpc = [self createRPCWithRequestsWriter:self.requestsWriter]; + [_rpc setResponseDispatchQueue:self.workerDispatchQueue.queue]; + [FSTDatastore prepareHeadersForRPC:_rpc - databaseID:self.databaseInfo.databaseID - token:token.token]; + databaseID:&self.databaseInfo->database_id() + token:(token.is_valid() ? token.token() : absl::string_view())]; FSTAssert(_callbackFilter == nil, @"GRX Filter must be nil"); _callbackFilter = [[FSTCallbackFilter alloc] initWithStream:self]; [_rpc startWithWriteable:_callbackFilter]; @@ -343,9 +365,6 @@ - (void)tearDown { - (void)closeWithFinalState:(FSTStreamState)finalState error:(nullable NSError *)error { FSTAssert(finalState == FSTStreamStateError || error == nil, @"Can't provide an error when not in an error state."); - FSTAssert(self.delegate, - @"closeWithFinalState should only be called for a started stream that has an active " - @"delegate."); [self.workerDispatchQueue verifyIsCurrentQueue]; [self cancelIdleCheck]; @@ -359,7 +378,10 @@ - (void)closeWithFinalState:(FSTStreamState)finalState error:(nullable NSError * [self.backoff resetToMax]; } - [self tearDown]; + if (finalState != FSTStreamStateError) { + FSTLog(@"%@ %p Performing stream teardown", [self class], (__bridge void *)self); + [self tearDown]; + } if (self.requestsWriter) { // Clean up the underlying RPC. If this close: is in response to an error, don't attempt to @@ -390,8 +412,9 @@ - (void)closeWithFinalState:(FSTStreamState)finalState error:(nullable NSError * [self notifyStreamInterruptedWithError:error]; } - // Clear the delegates to avoid any possible bleed through of events from GRPC. - _delegate = nil; + // PORTING NOTE: notifyStreamInterruptedWithError may have restarted the stream with a new + // delegate so we do /not/ want to clear the delegate here. And since we've already suppressed + // callbacks via our callbackFilter, there is no worry about bleed through of events from GRPC. } - (void)stop { @@ -414,7 +437,7 @@ - (void)inhibitBackoff { /** Called by the idle timer when the stream should close due to inactivity. */ - (void)handleIdleCloseTimer { [self.workerDispatchQueue verifyIsCurrentQueue]; - if (self.state == FSTStreamStateOpen && [self isIdle]) { + if ([self isOpen]) { // When timing out an idle stream there's no reason to force the stream into backoff when // it restarts so set the stream state to Initial instead of Error. [self closeWithFinalState:FSTStreamStateInitial error:nil]; @@ -423,18 +446,23 @@ - (void)handleIdleCloseTimer { - (void)markIdle { [self.workerDispatchQueue verifyIsCurrentQueue]; - if (self.state == FSTStreamStateOpen) { - self.idle = YES; - [self.workerDispatchQueue dispatchAfterDelay:kIdleTimeout - block:^() { - [self handleIdleCloseTimer]; - }]; + // Starts the idle timer if we are in state 'Open' and are not yet already running a timer (in + // which case the previous idle timeout still applies). + if ([self isOpen] && !self.idleTimerCallback) { + self.idleTimerCallback = [self.workerDispatchQueue dispatchAfterDelay:kIdleTimeout + timerID:self.idleTimerID + block:^() { + [self handleIdleCloseTimer]; + }]; } } - (void)cancelIdleCheck { [self.workerDispatchQueue verifyIsCurrentQueue]; - self.idle = NO; + if (self.idleTimerCallback) { + [self.idleTimerCallback cancel]; + self.idleTimerCallback = nil; + } } /** @@ -515,11 +543,7 @@ - (void)handleStreamMessage:(id)value { */ - (void)handleStreamClose:(nullable NSError *)error { FSTLog(@"%@ %p close: %@", NSStringFromClass([self class]), (__bridge void *)self, error); - - if (![self isStarted]) { // The stream could have already been closed by the idle close timer. - FSTLog(@"%@ Ignoring server close for already closed stream.", NSStringFromClass([self class])); - return; - } + FSTAssert([self isStarted], @"handleStreamClose: called for non-started stream."); // In theory the stream could close cleanly, however, in our current model we never expect this // to happen because if we stop a stream ourselves, this callback will never be called. To @@ -532,55 +556,50 @@ - (void)handleStreamClose:(nullable NSError *)error { // The GRXWriteable implementation defines the receive side of the RPC stream. /** - * Called by GRPC when it publishes a value. It is called from GRPC's own queue so we immediately - * redispatch back onto our own worker queue. + * Called by GRPC when it publishes a value. + * + * GRPC must be configured to use our worker queue by calling + * `[call setResponseDispatchQueue:self.workerDispatchQueue.queue]` on the GRPCCall before starting + * the RPC. */ -- (void)writeValue:(id)value __used { - // TODO(mcg): remove the double-dispatch once GRPCCall at head is released. - // Once released we can set the responseDispatchQueue property on the GRPCCall and then this - // method can call handleStreamMessage directly. - FSTWeakify(self); - [self.workerDispatchQueue dispatchAsync:^{ - FSTStrongify(self); - if (![self isStarted]) { - FSTLog(@"%@ Ignoring stream message from inactive stream.", NSStringFromClass([self class])); - } - - if (!self.messageReceived) { - self.messageReceived = YES; - if ([FIRFirestore isLoggingEnabled]) { - FSTLog(@"%@ %p headers (whitelisted): %@", NSStringFromClass([self class]), - (__bridge void *)self, - [FSTDatastore extractWhiteListedHeaders:self.rpc.responseHeaders]); - } - } - NSError *error; - id proto = [self parseProto:self.responseMessageClass data:value error:&error]; - if (proto) { - [self handleStreamMessage:proto]; - } else { - [_rpc finishWithError:error]; +- (void)writeValue:(id)value { + [self.workerDispatchQueue verifyIsCurrentQueue]; + FSTAssert([self isStarted], @"writeValue: called for stopped stream."); + + if (!self.messageReceived) { + self.messageReceived = YES; + if ([FIRFirestore isLoggingEnabled]) { + FSTLog(@"%@ %p headers (whitelisted): %@", NSStringFromClass([self class]), + (__bridge void *)self, + [FSTDatastore extractWhiteListedHeaders:self.rpc.responseHeaders]); } - }]; + } + NSError *error; + id proto = [self parseProto:self.responseMessageClass data:value error:&error]; + if (proto) { + [self handleStreamMessage:proto]; + } else { + [_rpc finishWithError:error]; + } } /** * Called by GRPC when it closed the stream with an error representing the final state of the * stream. * - * Do not call directly, since it dispatches via the worker queue. Call handleStreamClose to - * directly inform stream-specific logic, or call stop to tear down the stream. + * GRPC must be configured to use our worker queue by calling + * `[call setResponseDispatchQueue:self.workerDispatchQueue.queue]` on the GRPCCall before starting + * the RPC. + * + * Do not call directly. Call handleStreamClose to directly inform stream-specific logic, or call + * stop to tear down the stream. */ - (void)writesFinishedWithError:(nullable NSError *)error __used { error = [FSTDatastore firestoreErrorForError:error]; - FSTWeakify(self); - [self.workerDispatchQueue dispatchAsync:^{ - FSTStrongify(self); - if (!self || self.state == FSTStreamStateStopped) { - return; - } - [self handleStreamClose:error]; - }]; + [self.workerDispatchQueue verifyIsCurrentQueue]; + FSTAssert([self isStarted], @"writesFinishedWithError: called for stopped stream."); + + [self handleStreamClose:error]; } @end @@ -595,12 +614,14 @@ @interface FSTWatchStream () @implementation FSTWatchStream -- (instancetype)initWithDatabase:(FSTDatabaseInfo *)database +- (instancetype)initWithDatabase:(const DatabaseInfo *)database workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue - credentials:(id)credentials + credentials:(CredentialsProvider *)credentials serializer:(FSTSerializerBeta *)serializer { self = [super initWithDatabase:database workerDispatchQueue:workerDispatchQueue + connectionTimerID:FSTTimerIDListenStreamConnection + idleTimerID:FSTTimerIDListenStreamIdle credentials:credentials responseMessageClass:[GCFSListenResponse class]]; if (self) { @@ -610,7 +631,7 @@ - (instancetype)initWithDatabase:(FSTDatabaseInfo *)database } - (GRPCCall *)createRPCWithRequestsWriter:(GRXWriter *)requestsWriter { - return [[GRPCCall alloc] initWithHost:self.databaseInfo.host + return [[GRPCCall alloc] initWithHost:util::WrapNSStringNoCopy(self.databaseInfo->host()) path:@"/google.firestore.v1beta1.Firestore/Listen" requestsWriter:requestsWriter]; } @@ -678,12 +699,14 @@ @interface FSTWriteStream () @implementation FSTWriteStream -- (instancetype)initWithDatabase:(FSTDatabaseInfo *)database +- (instancetype)initWithDatabase:(const DatabaseInfo *)database workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue - credentials:(id)credentials + credentials:(CredentialsProvider *)credentials serializer:(FSTSerializerBeta *)serializer { self = [super initWithDatabase:database workerDispatchQueue:workerDispatchQueue + connectionTimerID:FSTTimerIDWriteStreamConnection + idleTimerID:FSTTimerIDWriteStreamIdle credentials:credentials responseMessageClass:[GCFSWriteResponse class]]; if (self) { @@ -693,7 +716,7 @@ - (instancetype)initWithDatabase:(FSTDatabaseInfo *)database } - (GRPCCall *)createRPCWithRequestsWriter:(GRXWriter *)requestsWriter { - return [[GRPCCall alloc] initWithHost:self.databaseInfo.host + return [[GRPCCall alloc] initWithHost:util::WrapNSStringNoCopy(self.databaseInfo->host()) path:@"/google.firestore.v1beta1.Firestore/Write" requestsWriter:requestsWriter]; } diff --git a/Firestore/Source/Remote/FSTWatchChange.m b/Firestore/Source/Remote/FSTWatchChange.mm similarity index 100% rename from Firestore/Source/Remote/FSTWatchChange.m rename to Firestore/Source/Remote/FSTWatchChange.mm diff --git a/Firestore/Source/Util/FSTAsyncQueryListener.m b/Firestore/Source/Util/FSTAsyncQueryListener.mm similarity index 73% rename from Firestore/Source/Util/FSTAsyncQueryListener.m rename to Firestore/Source/Util/FSTAsyncQueryListener.mm index d98e2dd99fd..b72ac5757d1 100644 --- a/Firestore/Source/Util/FSTAsyncQueryListener.m +++ b/Firestore/Source/Util/FSTAsyncQueryListener.mm @@ -34,10 +34,15 @@ - (instancetype)initWithDispatchQueue:(FSTDispatchQueue *)dispatchQueue } - (FSTViewSnapshotHandler)asyncSnapshotHandler { + // Retain `self` strongly in resulting snapshot handler so that even if the + // user releases the `FSTAsyncQueryListener` we'll continue to deliver + // events. This is done specifically to facilitate the common case where + // users just want to turn on notifications "forever" and don't want to have + // to keep track of our handle to keep them going. return ^(FSTViewSnapshot *_Nullable snapshot, NSError *_Nullable error) { - [_dispatchQueue dispatchAsync:^{ - if (!_muted) { - _snapshotHandler(snapshot, error); + [self->_dispatchQueue dispatchAsync:^{ + if (!self->_muted) { + self->_snapshotHandler(snapshot, error); } }]; }; diff --git a/Firestore/Source/Util/FSTComparison.h b/Firestore/Source/Util/FSTComparison.h deleted file mode 100644 index e6e57e6aa34..00000000000 --- a/Firestore/Source/Util/FSTComparison.h +++ /dev/null @@ -1,66 +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 - -NS_ASSUME_NONNULL_BEGIN - -/** Compares two NSStrings. */ -NSComparisonResult FSTCompareStrings(NSString *left, NSString *right); - -/** Compares two BOOLs. */ -NSComparisonResult FSTCompareBools(BOOL left, BOOL right); - -/** Compares two integers. */ -NSComparisonResult FSTCompareInts(int left, int right); - -/** Compares two int32_t. */ -NSComparisonResult FSTCompareInt32s(int32_t left, int32_t right); - -/** Compares two int64_t. */ -NSComparisonResult FSTCompareInt64s(int64_t left, int64_t right); - -/** Compares two NSUIntegers. */ -NSComparisonResult FSTCompareUIntegers(NSUInteger left, NSUInteger right); - -/** Compares two doubles (using Firestore semantics for NaN). */ -NSComparisonResult FSTCompareDoubles(double left, double right); - -/** Compares a double and an int64_t. */ -NSComparisonResult FSTCompareMixed(double doubleValue, int64_t longValue); - -/** Compare two NSData byte sequences. */ -NSComparisonResult FSTCompareBytes(NSData *left, NSData *right); - -/** A simple NSComparator for comparing NSNumber instances. */ -extern const NSComparator FSTNumberComparator; - -/** A simple NSComparator for comparing NSString instances. */ -extern const NSComparator FSTStringComparator; - -/** - * Compares the bitwise representation of two doubles, but normalizes NaN values. This is - * similar to what the backend and android clients do, including comparing -0.0 as not equal to 0.0. - */ -BOOL FSTDoubleBitwiseEquals(double left, double right); - -/** - * Computes a bitwise hash of a double, but normalizes NaN values, suitable for use when using - * FSTDoublesAreBitwiseEqual for equality. - */ -NSUInteger FSTDoubleBitwiseHash(double d); - -NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Util/FSTComparison.m b/Firestore/Source/Util/FSTComparison.m deleted file mode 100644 index 9c5c3ebc876..00000000000 --- a/Firestore/Source/Util/FSTComparison.m +++ /dev/null @@ -1,175 +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/Source/Util/FSTComparison.h" - -NS_ASSUME_NONNULL_BEGIN - -union DoubleBits { - double d; - uint64_t bits; -}; - -const NSComparator FSTNumberComparator = ^NSComparisonResult(NSNumber *left, NSNumber *right) { - return [left compare:right]; -}; - -const NSComparator FSTStringComparator = ^NSComparisonResult(NSString *left, NSString *right) { - return FSTCompareStrings(left, right); -}; - -NSComparisonResult FSTCompareStrings(NSString *left, NSString *right) { - // NOTE: NSLiteralSearch is necessary to compare the raw character codes. By default, - // precomposed characters are considered equivalent to their decomposed equivalents. - return [left compare:right options:NSLiteralSearch]; -} - -NSComparisonResult FSTCompareBools(BOOL left, BOOL right) { - if (!left) { - return right ? NSOrderedAscending : NSOrderedSame; - } else { - return right ? NSOrderedSame : NSOrderedDescending; - } -} - -NSComparisonResult FSTCompareInts(int left, int right) { - if (left > right) { - return NSOrderedDescending; - } - if (right > left) { - return NSOrderedAscending; - } - return NSOrderedSame; -} - -NSComparisonResult FSTCompareInt32s(int32_t left, int32_t right) { - if (left > right) { - return NSOrderedDescending; - } - if (right > left) { - return NSOrderedAscending; - } - return NSOrderedSame; -} - -NSComparisonResult FSTCompareInt64s(int64_t left, int64_t right) { - if (left > right) { - return NSOrderedDescending; - } - if (right > left) { - return NSOrderedAscending; - } - return NSOrderedSame; -} - -NSComparisonResult FSTCompareUIntegers(NSUInteger left, NSUInteger right) { - if (left > right) { - return NSOrderedDescending; - } - if (right > left) { - return NSOrderedAscending; - } - return NSOrderedSame; -} - -NSComparisonResult FSTCompareDoubles(double left, double right) { - // NaN sorts equal to itself and before any other number. - if (left < right) { - return NSOrderedAscending; - } else if (left > right) { - return NSOrderedDescending; - } else if (left == right) { - return NSOrderedSame; - } else { - // One or both left and right is NaN. - if (isnan(left)) { - return isnan(right) ? NSOrderedSame : NSOrderedAscending; - } else { - return NSOrderedDescending; - } - } -} - -static const double LONG_MIN_VALUE_AS_DOUBLE = (double)LLONG_MIN; -static const double LONG_MAX_VALUE_AS_DOUBLE = (double)LLONG_MAX; - -NSComparisonResult FSTCompareMixed(double doubleValue, int64_t longValue) { - // LLONG_MIN has an exact representation as double, so to check for a value outside the range - // representable by long, we have to check for strictly less than LLONG_MIN. Note that this also - // handles negative infinity. - if (doubleValue < LONG_MIN_VALUE_AS_DOUBLE) { - return NSOrderedAscending; - } - - // LLONG_MAX has no exact representation as double (casting as we've done makes 2^63, which is - // larger than LLONG_MAX), so consider any value greater than or equal to the threshold to be out - // of range. This also handles positive infinity. - if (doubleValue >= LONG_MAX_VALUE_AS_DOUBLE) { - return NSOrderedDescending; - } - - // In Firestore NaN is defined to compare before all other numbers. - if (isnan(doubleValue)) { - return NSOrderedAscending; - } - - int64_t doubleAsLong = (int64_t)doubleValue; - NSComparisonResult cmp = FSTCompareInt64s(doubleAsLong, longValue); - if (cmp != NSOrderedSame) { - return cmp; - } - - // At this point the long representations are equal but this could be due to rounding. - double longAsDouble = (double)longValue; - return FSTCompareDoubles(doubleValue, longAsDouble); -} - -NSComparisonResult FSTCompareBytes(NSData *left, NSData *right) { - NSUInteger minLength = MIN(left.length, right.length); - int result = memcmp(left.bytes, right.bytes, minLength); - if (result < 0) { - return NSOrderedAscending; - } else if (result > 0) { - return NSOrderedDescending; - } else if (left.length < right.length) { - return NSOrderedAscending; - } else if (left.length > right.length) { - return NSOrderedDescending; - } else { - return NSOrderedSame; - } -} - -/** Helper to normalize a double and then return the raw bits as a uint64_t. */ -uint64_t FSTDoubleBits(double d) { - if (isnan(d)) { - d = NAN; - } - union DoubleBits converter = {.d = d}; - return converter.bits; -} - -BOOL FSTDoubleBitwiseEquals(double left, double right) { - return FSTDoubleBits(left) == FSTDoubleBits(right); -} - -NSUInteger FSTDoubleBitwiseHash(double d) { - uint64_t bits = FSTDoubleBits(d); - // Note that x ^ (x >> 32) works fine for both 32 and 64 bit definitions of NSUInteger - return (((NSUInteger)bits) ^ (NSUInteger)(bits >> 32)); -} - -NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Util/FSTDispatchQueue.h b/Firestore/Source/Util/FSTDispatchQueue.h index fe8788708ad..9b28c9cdb48 100644 --- a/Firestore/Source/Util/FSTDispatchQueue.h +++ b/Firestore/Source/Util/FSTDispatchQueue.h @@ -18,6 +18,34 @@ NS_ASSUME_NONNULL_BEGIN +/** + * Well-known "timer" IDs used when scheduling delayed callbacks on the FSTDispatchQueue. These IDs + * can then be used from tests to check for the presence of callbacks or to run them early. + */ +typedef NS_ENUM(NSInteger, FSTTimerID) { + FSTTimerIDAll, // Sentinel value to be used with runDelayedCallbacksUntil: to run all blocks. + FSTTimerIDListenStreamIdle, + FSTTimerIDListenStreamConnection, + FSTTimerIDWriteStreamIdle, + FSTTimerIDWriteStreamConnection +}; + +/** + * Handle to a callback scheduled via [FSTDispatchQueue dispatchAfterDelay:]. Supports cancellation + * via the cancel method. + */ +@interface FSTDelayedCallback : NSObject + +/** + * Cancels the callback if it hasn't already been executed or canceled. + * + * As long as the callback has not yet been run, calling cancel() (from a callback already running + * on the dispatch queue) provides a guarantee that the operation will not be run. + */ +- (void)cancel; + +@end + @interface FSTDispatchQueue : NSObject /** Creates and returns an FSTDispatchQueue wrapping the specified dispatch_queue_t. */ @@ -56,12 +84,32 @@ NS_ASSUME_NONNULL_BEGIN * Schedules a callback after the specified delay. * * Unlike dispatchAsync: this method does not require you to dispatch to a different queue than - * the current one (thus it is equivalent to a raw dispatch_after()). + * the current one. + * + * The returned FSTDelayedCallback handle can be used to cancel the callback prior to its running. * * @param block The block to run. * @param delay The delay (in seconds) after which to run the block. + * @param timerID An FSTTimerID that can be used from tests to check for the presence of this + * callback or to schedule it to run early. + * @return A FSTDelayedCallback instance that can be used for cancellation. + */ +- (FSTDelayedCallback *)dispatchAfterDelay:(NSTimeInterval)delay + timerID:(FSTTimerID)timerID + block:(void (^)(void))block; + +/** + * For Tests: Determine if a delayed callback with a particular FSTTimerID exists. + */ +- (BOOL)containsDelayedCallbackWithTimerID:(FSTTimerID)timerID; + +/** + * For Tests: Runs delayed callbacks early, blocking until completion. + * + * @param lastTimerID Only delayed callbacks up to and including one that was scheduled using this + * FSTTimerID will be run. Method throws if no matching callback exists. */ -- (void)dispatchAfterDelay:(NSTimeInterval)delay block:(void (^)(void))block; +- (void)runDelayedCallbacksUntil:(FSTTimerID)lastTimerID; /** The underlying wrapped dispatch_queue_t */ @property(nonatomic, strong, readonly) dispatch_queue_t queue; diff --git a/Firestore/Source/Util/FSTDispatchQueue.m b/Firestore/Source/Util/FSTDispatchQueue.m deleted file mode 100644 index 6ce5d74b089..00000000000 --- a/Firestore/Source/Util/FSTDispatchQueue.m +++ /dev/null @@ -1,80 +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 - -#import "Firestore/Source/Util/FSTAssert.h" -#import "Firestore/Source/Util/FSTDispatchQueue.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface FSTDispatchQueue () -- (instancetype)initWithQueue:(dispatch_queue_t)queue NS_DESIGNATED_INITIALIZER; -@end - -@implementation FSTDispatchQueue - -+ (instancetype)queueWith:(dispatch_queue_t)dispatchQueue { - return [[FSTDispatchQueue alloc] initWithQueue:dispatchQueue]; -} - -- (instancetype)initWithQueue:(dispatch_queue_t)queue { - if (self = [super init]) { - _queue = queue; - } - return self; -} - -- (void)verifyIsCurrentQueue { - FSTAssert([self onTargetQueue], - @"We are running on the wrong dispatch queue. Expected '%@' Actual: '%@'", - [self targetQueueLabel], [self currentQueueLabel]); -} - -- (void)dispatchAsync:(void (^)(void))block { - FSTAssert(![self onTargetQueue], - @"dispatchAsync called when we are already running on target dispatch queue '%@'", - [self targetQueueLabel]); - - dispatch_async(self.queue, block); -} - -- (void)dispatchAsyncAllowingSameQueue:(void (^)(void))block { - dispatch_async(self.queue, block); -} - -- (void)dispatchAfterDelay:(NSTimeInterval)delay block:(void (^)(void))block { - dispatch_time_t delayNs = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)); - dispatch_after(delayNs, self.queue, block); -} - -#pragma mark - Private Methods - -- (NSString *)currentQueueLabel { - return [NSString stringWithUTF8String:dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL)]; -} - -- (NSString *)targetQueueLabel { - return [NSString stringWithUTF8String:dispatch_queue_get_label(self.queue)]; -} - -- (BOOL)onTargetQueue { - return [[self currentQueueLabel] isEqualToString:[self targetQueueLabel]]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Util/FSTDispatchQueue.mm b/Firestore/Source/Util/FSTDispatchQueue.mm new file mode 100644 index 00000000000..5bd7f276b21 --- /dev/null +++ b/Firestore/Source/Util/FSTDispatchQueue.mm @@ -0,0 +1,275 @@ +/* + * 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 "Firestore/Source/Util/FSTAssert.h" +#import "Firestore/Source/Util/FSTDispatchQueue.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * removeDelayedCallback is used by FSTDelayedCallback and so we pre-declare it before the rest of + * the FSTDispatchQueue private interface. + */ +@interface FSTDispatchQueue () +- (void)removeDelayedCallback:(FSTDelayedCallback *)callback; +@end + +#pragma mark - FSTDelayedCallback + +/** + * Represents a callback scheduled to be run in the future on an FSTDispatchQueue. + * + * It is created via [FSTDelayedCallback createAndScheduleWithQueue]. + * + * Supports cancellation (via cancel) and early execution (via skipDelay). + */ +@interface FSTDelayedCallback () + +@property(nonatomic, strong, readonly) FSTDispatchQueue *queue; +@property(nonatomic, assign, readonly) FSTTimerID timerID; +@property(nonatomic, assign, readonly) NSTimeInterval targetTime; +@property(nonatomic, copy) void (^callback)(); +/** YES if the callback has been run or canceled. */ +@property(nonatomic, getter=isDone) BOOL done; + +/** + * Creates and returns an FSTDelayedCallback that has been scheduled on the provided queue with the + * provided delay. + * + * @param queue The FSTDispatchQueue to run the callback on. + * @param timerID A FSTTimerID identifying the type of the delayed callback. + * @param delay The delay before the callback should be scheduled. + * @param callback The callback block to run. + * @return The created FSTDelayedCallback instance. + */ ++ (instancetype)createAndScheduleWithQueue:(FSTDispatchQueue *)queue + timerID:(FSTTimerID)timerID + delay:(NSTimeInterval)delay + callback:(void (^)(void))callback; + +/** + * Queues the callback to run immediately (if it hasn't already been run or canceled). + */ +- (void)skipDelay; + +@end + +@implementation FSTDelayedCallback + +- (instancetype)initWithQueue:(FSTDispatchQueue *)queue + timerID:(FSTTimerID)timerID + targetTime:(NSTimeInterval)targetTime + callback:(void (^)(void))callback { + if (self = [super init]) { + _queue = queue; + _timerID = timerID; + _targetTime = targetTime; + _callback = callback; + _done = NO; + } + return self; +} + ++ (instancetype)createAndScheduleWithQueue:(FSTDispatchQueue *)queue + timerID:(FSTTimerID)timerID + delay:(NSTimeInterval)delay + callback:(void (^)(void))callback { + NSTimeInterval targetTime = [[NSDate date] timeIntervalSince1970] + delay; + FSTDelayedCallback *delayedCallback = [[FSTDelayedCallback alloc] initWithQueue:queue + timerID:timerID + targetTime:targetTime + callback:callback]; + [delayedCallback startWithDelay:delay]; + return delayedCallback; +} + +/** + * Starts the timer. This is called immediately after construction by createAndScheduleWithQueue. + */ +- (void)startWithDelay:(NSTimeInterval)delay { + dispatch_time_t delayNs = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)); + dispatch_after(delayNs, self.queue.queue, ^{ + [self delayDidElapse]; + }); +} + +- (void)skipDelay { + [self.queue dispatchAsyncAllowingSameQueue:^{ + [self delayDidElapse]; + }]; +} + +- (void)cancel { + [self.queue verifyIsCurrentQueue]; + if (!self.isDone) { + // PORTING NOTE: There's no way to actually cancel the dispatched callback, but it'll be a no-op + // since we set done to YES. + [self markDone]; + } +} + +- (void)delayDidElapse { + [self.queue verifyIsCurrentQueue]; + if (!self.isDone) { + [self markDone]; + self.callback(); + } +} + +/** + * Marks this delayed callback as done, and notifies the FSTDispatchQueue that it should be removed. + */ +- (void)markDone { + self.done = YES; + [self.queue removeDelayedCallback:self]; +} + +@end + +#pragma mark - FSTDispatchQueue + +@interface FSTDispatchQueue () + +/** + * Callbacks scheduled to be queued in the future. Callbacks are automatically removed after they + * are run or canceled. + */ +@property(nonatomic, strong, readonly) NSMutableArray *delayedCallbacks; + +- (instancetype)initWithQueue:(dispatch_queue_t)queue NS_DESIGNATED_INITIALIZER; + +@end + +@implementation FSTDispatchQueue + ++ (instancetype)queueWith:(dispatch_queue_t)dispatchQueue { + return [[FSTDispatchQueue alloc] initWithQueue:dispatchQueue]; +} + +- (instancetype)initWithQueue:(dispatch_queue_t)queue { + if (self = [super init]) { + _queue = queue; + _delayedCallbacks = [NSMutableArray array]; + } + return self; +} + +- (void)verifyIsCurrentQueue { + FSTAssert([self onTargetQueue], + @"We are running on the wrong dispatch queue. Expected '%@' Actual: '%@'", + [self targetQueueLabel], [self currentQueueLabel]); +} + +- (void)dispatchAsync:(void (^)(void))block { + FSTAssert(![self onTargetQueue], + @"dispatchAsync called when we are already running on target dispatch queue '%@'", + [self targetQueueLabel]); + + dispatch_async(self.queue, block); +} + +- (void)dispatchAsyncAllowingSameQueue:(void (^)(void))block { + dispatch_async(self.queue, block); +} + +- (FSTDelayedCallback *)dispatchAfterDelay:(NSTimeInterval)delay + timerID:(FSTTimerID)timerID + block:(void (^)(void))block { + // While not necessarily harmful, we currently don't expect to have multiple callbacks with the + // same timerID in the queue, so defensively reject them. + FSTAssert(![self containsDelayedCallbackWithTimerID:timerID], + @"Attempted to schedule multiple callbacks with id %ld", (unsigned long)timerID); + FSTDelayedCallback *delayedCallback = [FSTDelayedCallback createAndScheduleWithQueue:self + timerID:timerID + delay:delay + callback:block]; + [self.delayedCallbacks addObject:delayedCallback]; + return delayedCallback; +} + +- (BOOL)containsDelayedCallbackWithTimerID:(FSTTimerID)timerID { + NSUInteger matchIndex = [self.delayedCallbacks + indexOfObjectPassingTest:^BOOL(FSTDelayedCallback *obj, NSUInteger idx, BOOL *stop) { + return obj.timerID == timerID; + }]; + return matchIndex != NSNotFound; +} + +- (void)runDelayedCallbacksUntil:(FSTTimerID)lastTimerID { + dispatch_semaphore_t doneSemaphore = dispatch_semaphore_create(0); + + [self dispatchAsync:^{ + FSTAssert(lastTimerID == FSTTimerIDAll || [self containsDelayedCallbackWithTimerID:lastTimerID], + @"Attempted to run callbacks until missing timer ID: %ld", + (unsigned long)lastTimerID); + + [self sortDelayedCallbacks]; + for (FSTDelayedCallback *callback in self.delayedCallbacks) { + [callback skipDelay]; + if (lastTimerID != FSTTimerIDAll && callback.timerID == lastTimerID) { + break; + } + } + + // Now that the callbacks are queued, we want to enqueue an additional item to release the + // 'done' semaphore. + [self dispatchAsyncAllowingSameQueue:^{ + dispatch_semaphore_signal(doneSemaphore); + }]; + }]; + + dispatch_semaphore_wait(doneSemaphore, DISPATCH_TIME_FOREVER); +} + +// NOTE: For performance we could store the callbacks sorted (e.g. using std::priority_queue), +// but this sort only happens in tests (if runDelayedCallbacksUntil: is called), and the size +// is guaranteed to be small since we don't allow duplicate TimerIds (of which there are only 4). +- (void)sortDelayedCallbacks { + // We want to run callbacks in the same order they'd run if they ran naturally. + [self.delayedCallbacks + sortUsingComparator:^NSComparisonResult(FSTDelayedCallback *a, FSTDelayedCallback *b) { + return a.targetTime < b.targetTime + ? NSOrderedAscending + : a.targetTime > b.targetTime ? NSOrderedDescending : NSOrderedSame; + }]; +} + +/** Called by FSTDelayedCallback when a callback is run or canceled. */ +- (void)removeDelayedCallback:(FSTDelayedCallback *)callback { + NSUInteger index = [self.delayedCallbacks indexOfObject:callback]; + FSTAssert(index != NSNotFound, @"Delayed callback not found."); + [self.delayedCallbacks removeObjectAtIndex:index]; +} + +#pragma mark - Private Methods + +- (NSString *)currentQueueLabel { + return [NSString stringWithUTF8String:dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL)]; +} + +- (NSString *)targetQueueLabel { + return [NSString stringWithUTF8String:dispatch_queue_get_label(self.queue)]; +} + +- (BOOL)onTargetQueue { + return [[self currentQueueLabel] isEqualToString:[self targetQueueLabel]]; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Util/FSTLogger.h b/Firestore/Source/Util/FSTLogger.h index 699570a274f..c4e2b85a0d8 100644 --- a/Firestore/Source/Util/FSTLogger.h +++ b/Firestore/Source/Util/FSTLogger.h @@ -18,17 +18,9 @@ NS_ASSUME_NONNULL_BEGIN -#ifdef __cplusplus -extern "C" { -#endif - /** Logs to NSLog if [FIRFirestore isLoggingEnabled] is YES. */ void FSTLog(NSString *format, ...) NS_FORMAT_FUNCTION(1, 2); void FSTWarn(NSString *format, ...) NS_FORMAT_FUNCTION(1, 2); -#ifdef __cplusplus -} // extern "C" -#endif - NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Util/FSTLogger.m b/Firestore/Source/Util/FSTLogger.mm similarity index 100% rename from Firestore/Source/Util/FSTLogger.m rename to Firestore/Source/Util/FSTLogger.mm diff --git a/Firestore/Source/Util/FSTUsageValidation.h b/Firestore/Source/Util/FSTUsageValidation.h index 34a3d64cb08..a80dafa19b7 100644 --- a/Firestore/Source/Util/FSTUsageValidation.h +++ b/Firestore/Source/Util/FSTUsageValidation.h @@ -18,10 +18,6 @@ NS_ASSUME_NONNULL_BEGIN -#if __cplusplus -extern "C" { -#endif - /** Helper for creating a general exception for invalid usage of an API. */ NSException *FSTInvalidUsage(NSString *exceptionName, NSString *format, ...); @@ -46,8 +42,4 @@ NSException *FSTInvalidUsage(NSString *exceptionName, NSString *format, ...); @throw FSTInvalidUsage(@"FIRInvalidArgumentException", format, ##__VA_ARGS__); \ } while (0) -#if __cplusplus -} // extern "C" -#endif - NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Util/FSTUsageValidation.m b/Firestore/Source/Util/FSTUsageValidation.mm similarity index 100% rename from Firestore/Source/Util/FSTUsageValidation.m rename to Firestore/Source/Util/FSTUsageValidation.mm diff --git a/Firestore/Swift/Source/Codable/CodableGeoPoint.swift b/Firestore/Swift/Source/Codable/CodableGeoPoint.swift new file mode 100644 index 00000000000..fa563403c8d --- /dev/null +++ b/Firestore/Swift/Source/Codable/CodableGeoPoint.swift @@ -0,0 +1,62 @@ +/* + * Copyright 2018 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 FirebaseFirestore + +/** + * A protocol describing the encodable properties of a GeoPoint. + * + * Note: this protocol exists as a workaround for the Swift compiler: if the GeoPoint class was + * extended directly to conform to Codable, the methods implementing the protcol would be need to be + * marked required but that can't be done in an extension. Declaring the extension on the protocol + * sidesteps this issue. + */ +fileprivate protocol CodableGeoPoint: Codable { + var latitude: Double { get } + var longitude: Double { get } + + init(latitude: Double, longitude: Double) +} + +/** The keys in a GeoPoint. Must match the properties of CodableGeoPoint. */ +fileprivate enum GeoPointKeys: String, CodingKey { + case latitude + case longitude +} + +/** + * An extension of GeoPoint that implements the behavior of the Codable protocol. + * + * Note: this is implemented manually here because the Swift compiler can't synthesize these methods + * when declaring an extension to conform to Codable. + */ +extension CodableGeoPoint { + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: GeoPointKeys.self) + let latitude = try container.decode(Double.self, forKey: .latitude) + let longitude = try container.decode(Double.self, forKey: .longitude) + self.init(latitude: latitude, longitude: longitude) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: GeoPointKeys.self) + try container.encode(latitude, forKey: .latitude) + try container.encode(longitude, forKey: .longitude) + } +} + +/** Extends GeoPoint to conform to Codable. */ +extension GeoPoint: CodableGeoPoint {} diff --git a/Firestore/Swift/Tests/Codable/CodableGeoPointTests.swift b/Firestore/Swift/Tests/Codable/CodableGeoPointTests.swift new file mode 100644 index 00000000000..6b1dce432a0 --- /dev/null +++ b/Firestore/Swift/Tests/Codable/CodableGeoPointTests.swift @@ -0,0 +1,49 @@ +/* + * Copyright 2018 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 FirebaseFirestore +import FirebaseFirestoreSwift +import Foundation +import XCTest + +class CodableGeoPointTests: XCTestCase { + func testGeoPointEncodes() { + let geoPoint = GeoPoint(latitude: 37.77493, longitude: -122.41942) + + let jsonData = try! JSONEncoder().encode(geoPoint) + let json = String(data: jsonData, encoding: .utf8)! + + // The ordering of attributes in the JSON output is not guaranteed, nor is the rounding of + // the values so just verify that each required property is present and that the value + // starts as expected. + XCTAssert(json.contains("\"latitude\":37.")) + XCTAssert(json.contains("\"longitude\":-122.")) + } + + func testGeoPointDecodes() { + let json = """ + { + "latitude": 37.77493, + "longitude": -122.41942 + } + """ + let jsonData: Data = json.data(using: .utf8)! + + let geoPoint = try! JSONDecoder().decode(GeoPoint.self, from: jsonData) + XCTAssertEqual(37.77493, geoPoint.latitude, accuracy: 0.0001) + XCTAssertEqual(-122.41942, geoPoint.longitude, accuracy: 0.0001) + } +} diff --git a/Firestore/Swift/Tests/Info.plist b/Firestore/Swift/Tests/Info.plist new file mode 100644 index 00000000000..6c40a6cd0c4 --- /dev/null +++ b/Firestore/Swift/Tests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/Firestore/core/CMakeLists.txt b/Firestore/core/CMakeLists.txt index c49b6dbe337..5bf8c5ec5fd 100644 --- a/Firestore/core/CMakeLists.txt +++ b/Firestore/core/CMakeLists.txt @@ -12,5 +12,19 @@ # See the License for the specific language governing permissions and # limitations under the License. +add_subdirectory(src/firebase/firestore) +add_subdirectory(src/firebase/firestore/auth) +add_subdirectory(src/firebase/firestore/core) +add_subdirectory(src/firebase/firestore/immutable) +add_subdirectory(src/firebase/firestore/model) +add_subdirectory(src/firebase/firestore/remote) add_subdirectory(src/firebase/firestore/util) + +add_subdirectory(test/firebase/firestore/testutil) +add_subdirectory(test/firebase/firestore) +add_subdirectory(test/firebase/firestore/auth) +add_subdirectory(test/firebase/firestore/core) +add_subdirectory(test/firebase/firestore/immutable) +add_subdirectory(test/firebase/firestore/model) +add_subdirectory(test/firebase/firestore/remote) add_subdirectory(test/firebase/firestore/util) diff --git a/Firestore/core/include/firebase/firestore/document_reference.h b/Firestore/core/include/firebase/firestore/document_reference.h new file mode 100644 index 00000000000..58310b5cc15 --- /dev/null +++ b/Firestore/core/include/firebase/firestore/document_reference.h @@ -0,0 +1,375 @@ +/* + * Copyright 2018 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. + */ + +// TODO(rsgowman): This file isn't intended to be used just yet. It's just an +// outline of what the API might eventually look like. Most of this was +// shamelessly stolen and modified from rtdb's header file, melded with the +// (java) firestore api. + +#ifndef FIRESTORE_CORE_INCLUDE_FIREBASE_FIRESTORE_DOCUMENT_REFERENCE_H_ +#define FIRESTORE_CORE_INCLUDE_FIREBASE_FIRESTORE_DOCUMENT_REFERENCE_H_ + +#include +#include + +#if defined(FIREBASE_USE_STD_FUNCTION) +#include +#endif + +// TODO(rsgowman): Note that RTDB uses: +// #if defined(FIREBASE_USE_MOVE_OPERATORS) || defined(DOXYGEN +// to protect move operators from older compilers. But all our supported +// compilers support this, so we've skipped the #if guard. This TODO comment is +// here so we don't forget to mention this during the API review, and should be +// removed once this note has migrated to the API review doc. + +// TODO(rsgowman): replace these forward decl's with appropriate includes (once +// they exist) +namespace firebase { +class App; +template +class Future; +} // namespace firebase + +namespace firebase { +namespace firestore { + +// TODO(rsgowman): replace these forward decl's with appropriate includes (once +// they exist) +class FieldValue; +class DocumentSnapshot; +class Firestore; +class Error; +template +class EventListener; +class ListenerRegistration; +class CollectionReference; +class DocumentListenOptions; +// TODO(rsgowman): not quite a forward decl, but required to make the default +// parameter to Set() "compile". +class SetOptions { + public: + SetOptions(); +}; + +// TODO(rsgowman): move this into the FieldValue header +#ifdef STLPORT +using MapFieldValue = std::tr1::unordered_map; +#else +using MapFieldValue = std::unordered_map; +#endif + +/** + * A DocumentReference refers to a document location in a Firestore database and + * can be used to write, read, or listen to the location. There may or may not + * exist a document at the referenced location. A DocumentReference can also be + * used to create a CollectionReference to a subcollection. + * + * Create a DocumentReference via Firebase::Document(const string& path). + * + * Subclassing Note: Firestore classes are not meant to be subclassed except for + * use in test mocks. Subclassing is not supported in production code and new + * SDK releases may break code that does so. + */ +class DocumentReference { + public: + /** + * @brief Default constructor. This creates an invalid DocumentReference. + * Attempting to perform any operations on this reference will fail (and cause + * a crash) unless a valid DocumentReference has been assigned to it. + */ + DocumentReference(); + + /** + * @brief Copy constructor. It's totally okay (and efficient) to copy + * DocumentReference instances, as they simply point to the same location in + * the database. + * + * @param[in] reference DocumentReference to copy from. + */ + DocumentReference(const DocumentReference& reference); + + /** + * @brief Move constructor. Moving is an efficient operation for + * DocumentReference instances. + * + * @param[in] reference DocumentReference to move data from. + */ + DocumentReference(DocumentReference&& reference); + + virtual ~DocumentReference(); + + /** + * @brief Copy assignment operator. It's totally okay (and efficient) to copy + * DocumentReference instances, as they simply point to the same location in + * the database. + * + * @param[in] reference DocumentReference to copy from. + * + * @returns Reference to the destination DocumentReference. + */ + DocumentReference& operator=(const DocumentReference& reference); + + /** + * @brief Move assignment operator. Moving is an efficient operation for + * DocumentReference instances. + * + * @param[in] reference DocumentReference to move data from. + * + * @returns Reference to the destination DocumentReference. + */ + DocumentReference& operator=(DocumentReference&& reference); + + /** + * @brief Returns the Firestore instance associated with this document + * reference. + * + * The pointer will remain valid indefinitely. + * + * @returns Firebase Firestore instance that this DocumentReference refers to. + */ + virtual const Firestore* firestore() const; + + /** + * @brief Returns the Firestore instance associated with this document + * reference. + * + * The pointer will remain valid indefinitely. + * + * @returns Firebase Firestore instance that this DocumentReference refers to. + */ + virtual Firestore* firestore(); + + /** + * @brief Returns the string id of this document location. + * + * The pointer is only valid while the DocumentReference remains in memory. + * + * @returns String id of this document location, which will remain valid in + * memory until the DocumentReference itself goes away. + */ + virtual const char* id() const; + + /** + * @brief Returns the string id of this document location. + * + * @returns String id of this document location. + */ + virtual std::string id_string() const; + + /** + * @brief Returns the path of this document (relative to the root of the + * database) as a slash-separated string. + * + * The pointer is only valid while the DocumentReference remains in memory. + * + * @returns String path of this document location, which will remain valid in + * memory until the DocumentReference itself goes away. + */ + virtual const char* path() const; + + /** + * @brief Returns the path of this document (relative to the root of the + * database) as a slash-separated string. + * + * @returns String path of this document location. + */ + virtual std::string path_string() const; + + /** + * @brief Returns a CollectionReference to the collection that contains this + * document. + */ + virtual CollectionReference get_parent() const; + + /** + * @brief Returns a CollectionReference instance that refers to the + * subcollection at the specified path relative to this document. + * + * @param[in] collection_path A slash-separated relative path to a + * subcollection. The pointer only needs to be valid during this call. + * + * @return The CollectionReference instance. + */ + virtual CollectionReference Collection(const char* collection_path) const; + + /** + * @brief Returns a CollectionReference instance that refers to the + * subcollection at the specified path relative to this document. + * + * @param[in] collection_path A slash-separated relative path to a + * subcollection. + * + * @return The CollectionReference instance. + */ + virtual CollectionReference Collection( + const std::string& collection_path) const; + + /** + * @brief Reads the document referenced by this DocumentReference. + * + * @return A Future that will be resolved with the contents of the Document at + * this DocumentReference. + */ + virtual Future Get() const; + + /** + * @brief Writes to the document referred to by this DocumentReference. + * + * If the document does not yet exist, it will be created. If you pass + * SetOptions, the provided data can be merged into an existing document. + * + * @param[in] data A map of the fields and values for the document. + * @param[in] options An object to configure the set behavior. + * + * @return A Future that will be resolved when the write finishes. + */ + virtual Future Set(const MapFieldValue& data, + const SetOptions& options = SetOptions()); + + /** + * @brief Updates fields in the document referred to by this + * DocumentReference. + * + * If no document exists yet, the update will fail. + * + * @param[in] data A map of field / value pairs to update. Fields can contain + * dots to reference nested fields within the document. + * + * @return A Future that will be resolved when the write finishes. + */ + virtual Future Update(const MapFieldValue& data); + + /** + * @brief Removes the document referred to by this DocumentReference. + * + * @return A Task that will be resolved when the delete completes. + */ + virtual Future Delete(); + + /** + * @brief Starts listening to the document referenced by this + * DocumentReference. + * + * @param[in] listener The event listener that will be called with the + * snapshots, which must remain in memory until you remove the listener from + * this DocumentReference. (Ownership is not transferred; you are responsible + * for making sure that listener is valid as long as this DocumentReference is + * valid and the listener is registered.) + * + * @return A registration object that can be used to remove the listener. + */ + virtual ListenerRegistration AddSnapshotListener( + EventListener* listener); + + /** + * @brief Starts listening to the document referenced by this + * DocumentReference. + * + * @param[in] options The options to use for this listen. + * @param[in] listener The event listener that will be called with the + * snapshots, which must remain in memory until you remove the listener from + * this DocumentReference. (Ownership is not transferred; you are responsible + * for making sure that listener is valid as long as this DocumentReference is + * valid and the listener is registered.) + * + * @return A registration object that can be used to remove the listener. + */ + virtual ListenerRegistration AddSnapshotListener( + const DocumentListenOptions& options, + EventListener* listener); + +#if defined(FIREBASE_USE_STD_FUNCTION) || defined(DOXYGEN) + /** + * @brief Starts listening to the document referenced by this + * DocumentReference. + * + * @param[in] callback function or lambda to call. When this function is + * called, exactly one of the parameters will be non-null. + * + * @return A registration object that can be used to remove the listener. + * + * @note This method is not available when using STLPort on Android, as + * std::function is not supported on STLPort. + */ + virtual ListenerRegistration AddSnapshotListener( + std::function callback); + + /** + * @brief Starts listening to the document referenced by this + * DocumentReference. + * + * @param[in] options The options to use for this listen. + * @param[in] callback function or lambda to call. When this function is + * called, exactly one of the parameters will be non-null. + * + * @return A registration object that can be used to remove the listener. + * + * @note This method is not available when using STLPort on Android, as + * std::function is not supported on STLPort. + */ + virtual ListenerRegistration AddSnapshotListener( + const DocumentListenOptions& options, + std::function callback); +#endif // defined(FIREBASE_USE_STD_FUNCTION) || defined(DOXYGEN) +}; + +// TODO(rsgowman): probably define and inline here. +bool operator==(const DocumentReference& lhs, const DocumentReference& rhs); + +inline bool operator!=(const DocumentReference& lhs, + const DocumentReference& rhs) { + return !(lhs == rhs); +} + +// TODO(rsgowman): probably define and inline here. +bool operator<(const DocumentReference& lhs, const DocumentReference& rhs); + +inline bool operator>(const DocumentReference& lhs, + const DocumentReference& rhs) { + return rhs < lhs; +} + +inline bool operator<=(const DocumentReference& lhs, + const DocumentReference& rhs) { + return !(lhs > rhs); +} + +inline bool operator>=(const DocumentReference& lhs, + const DocumentReference& rhs) { + return !(lhs < rhs); +} + +} // namespace firestore +} // namespace firebase + +namespace std { +// TODO(rsgowman): NB that specialization of std::hash deviates from the Google +// C++ style guide. But we think this is probably ok in this case since: +// a) It's the standard way of doing this outside of Google (as the style guide +// itself points out), and +// b) This has a straightfoward hash function anyway (just hash the path) so I +// don't think the concerns in the style guide are going to bite us. +// +// Raise this concern during the API review. +template <> +struct hash { + std::size_t operator()( + const firebase::firestore::DocumentReference& doc_ref) const; +}; +} // namespace std + +#endif // FIRESTORE_CORE_INCLUDE_FIREBASE_FIRESTORE_DOCUMENT_REFERENCE_H_ diff --git a/Firestore/core/include/firebase/firestore/event_listener.h b/Firestore/core/include/firebase/firestore/event_listener.h new file mode 100644 index 00000000000..6c9442879fa --- /dev/null +++ b/Firestore/core/include/firebase/firestore/event_listener.h @@ -0,0 +1,53 @@ +/* + * Copyright 2018 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. + */ + +// TODO(rsgowman): This file isn't intended to be used just yet. It's just an +// outline of what the API might eventually look like. Most of this was +// shamelessly stolen and modified from rtdb's header file, melded with the +// (java) firestore api. + +#ifndef FIRESTORE_CORE_INCLUDE_FIREBASE_FIRESTORE_EVENT_LISTENER_H_ +#define FIRESTORE_CORE_INCLUDE_FIREBASE_FIRESTORE_EVENT_LISTENER_H_ + +namespace firebase { +namespace firestore { + +// TODO(rsgowman): replace these forward decl's with appropriate includes (once +// they exist) +class Error; + +/** + * @brief An interface for event listeners. + */ +template +class EventListener { + public: + /** + * @brief OnEvent will be called with the new value or the error if an error + * occurred. + * + * It's guaranteed that exactly one of value or error will be non-null. + * + * @param value The value of the event. null if there was an error. + * @param error The error if there was error. null otherwise. + */ + void OnEvent(const T* value, const Error* error); +}; + +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_INCLUDE_FIREBASE_FIRESTORE_EVENT_LISTENER_H_ diff --git a/Firestore/core/include/firebase/firestore/firestore.h b/Firestore/core/include/firebase/firestore/firestore.h new file mode 100644 index 00000000000..793fdd038f2 --- /dev/null +++ b/Firestore/core/include/firebase/firestore/firestore.h @@ -0,0 +1,160 @@ +/* + * Copyright 2018 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. + */ + +// TODO(rsgowman): This file isn't intended to be used just yet. It's just an +// outline of what the API might eventually look like. Most of this was +// shamelessly stolen and modified from rtdb's header file, melded with the +// firestore api. + +#ifndef FIRESTORE_CORE_INCLUDE_FIREBASE_FIRESTORE_FIRESTORE_H_ +#define FIRESTORE_CORE_INCLUDE_FIREBASE_FIRESTORE_FIRESTORE_H_ + +#include + +// TODO(rsgowman): replace these forward decl's with appropriate includes (once +// they exist) +namespace firebase { +class App; +class InitResult; +} // namespace firebase + +namespace firebase { +namespace firestore { + +// TODO(rsgowman): replace these forward decl's with appropriate includes (once +// they exist) +class DocumentReference; +class CollectionReference; +class Settings; + +/** + * @brief Entry point for the Firebase Firestore C++ SDK. + * + * To use the SDK, call firebase::firestore::Firestore::GetInstance() to obtain + * an instance of Firestore, then use Collection() or Document() to obtain + * references to child paths within the database. From there you can set data + * via CollectionReference::Add() and DocumentReference::Set(), or get data via + * CollectionReference::Get() and DocumentReference::Get(), attach listeners, + * and more. + * + * Subclassing Note: Firestore classes are not meant to be subclassed except for + * use in test mocks. Subclassing is not supported in production code and new + * SDK releases may break code that does so. + */ +class Firestore { + public: + /** + * @brief Returns an instance of Firestore corresponding to the given App. + * + * Firebase Firestore uses firebase::App to communicate with Firebase + * Authentication to authenticate users to the Firestore server backend. + * + * If you call GetInstance() multiple times with the same App, you will get + * the same instance of App. + * + * @param[in] app Your instance of firebase::App. Firebase Firestore will use + * this to communicate with Firebase Authentication. + * @param[out] init_result_out Optional: If provided, write the init result + * here. Will be set to kInitResultSuccess if initialization succeeded, or + * kInitResultFailedMissingDependency on Android if Google Play services is + * not available on the current device. + * + * @returns An instance of Firestore corresponding to the given App. + */ + static Firestore* GetInstance(::firebase::App* app, + InitResult* init_result_out = nullptr); + + static Firestore* GetInstance(InitResult* init_result_out = nullptr); + + /** + * @brief Destructor for the Firestore object. + * + * When deleted, this instance will be removed from the cache of Firestore + * objects. If you call GetInstance() in the future with the same App, a new + * Firestore instance will be created. + */ + virtual ~Firestore(); + + /** + * @brief Returns the firebase::App that this Firestore was created with. + * + * @returns The firebase::App this Firestore was created with. + */ + virtual const App* app() const; + + /** + * @brief Returns the firebase::App that this Firestore was created with. + * + * @returns The firebase::App this Firestore was created with. + */ + virtual App* app(); + + /** + * @brief Returns a CollectionReference instance that refers to the + * collection at the specified path within the database. + * + * @param[in] collection_path A slash-separated path to a collection. + * + * @return The CollectionReference instance. + */ + virtual CollectionReference Collection(const char* collection_path) const; + + /** + * @brief Returns a CollectionReference instance that refers to the + * collection at the specified path within the database. + * + * @param[in] collection_path A slash-separated path to a collection. + * + * @return The CollectionReference instance. + */ + virtual CollectionReference Collection( + const std::string& collection_path) const; + + /** + * @brief Returns a DocumentReference instance that refers to the document at + * the specified path within the database. + * + * @param[in] document_path A slash-separated path to a document. + * @return The DocumentReference instance. + */ + virtual DocumentReference Document(const char* document_path) const; + + /** + * @brief Returns a DocumentReference instance that refers to the document at + * the specified path within the database. + * + * @param[in] document_path A slash-separated path to a document. + * + * @return The DocumentReference instance. + */ + virtual DocumentReference Document(const std::string& document_path) const; + + /** Returns the settings used by this Firestore object. */ + virtual Settings settings() const; + + /** Sets any custom settings used to configure this Firestore object. */ + virtual void set_settings(const Settings& settings); + + // TODO(rsgowman): batch(), runTransaction() + + /** Globally enables / disables Firestore logging for the SDK. */ + static void set_logging_enabled(bool logging_enabled); +}; + +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_INCLUDE_FIREBASE_FIRESTORE_FIRESTORE_H_ diff --git a/Firestore/core/include/firebase/firestore/firestore_errors.h b/Firestore/core/include/firebase/firestore/firestore_errors.h new file mode 100644 index 00000000000..7a0ff7c3c79 --- /dev/null +++ b/Firestore/core/include/firebase/firestore/firestore_errors.h @@ -0,0 +1,115 @@ +/* + * Copyright 2018 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. + */ + +#ifndef FIRESTORE_CORE_INCLUDE_FIREBASE_FIRESTORE_FIRESTORE_ERRORS_H_ +#define FIRESTORE_CORE_INCLUDE_FIREBASE_FIRESTORE_FIRESTORE_ERRORS_H_ + +namespace firebase { +namespace firestore { + +/** + * Error codes used by Cloud Firestore. + * + * The codes are in sync with the Firestore iOS client SDK header file + * FIRFirestoreErrors.h. + */ +enum FirestoreErrorCode { + /** + * The operation completed successfully. NSError objects will never have a + * code with this value. + */ + Ok = 0, + + /** The operation was cancelled (typically by the caller). */ + Cancelled = 1, + + /** Unknown error or an error from a different error domain. */ + Unknown = 2, + + /** + * Client specified an invalid argument. Note that this differs from + * FailedPrecondition. InvalidArgument indicates arguments that are + * problematic regardless of the state of the system (e.g., an invalid field + * name). + */ + InvalidArgument = 3, + + /** + * Deadline expired before operation could complete. For operations that + * change the state of the system, this error may be returned even if the + * operation has completed successfully. For example, a successful response + * from a server could have been delayed long enough for the deadline to + * expire. + */ + DeadlineExceeded = 4, + + /** Some requested document was not found. */ + NotFound = 5, + + /** Some document that we attempted to create already exists. */ + AlreadyExists = 6, + + /** The caller does not have permission to execute the specified operation. */ + PermissionDenied = 7, + + /** + * Some resource has been exhausted, perhaps a per-user quota, or perhaps the + * entire file system is out of space. + */ + ResourceExhausted = 8, + + /** + * Operation was rejected because the system is not in a state required for + * the operation's execution. + */ + FailedPrecondition = 9, + + /** + * The operation was aborted, typically due to a concurrency issue like + * transaction aborts, etc. + */ + Aborted = 10, + + /** Operation was attempted past the valid range. */ + OutOfRange = 11, + + /** Operation is not implemented or not supported/enabled. */ + Unimplemented = 12, + + /** + * Internal errors. Means some invariants expected by underlying system has + * been broken. If you see one of these errors, something is very broken. + */ + Internal = 13, + + /** + * The service is currently unavailable. This is a most likely a transient + * condition and may be corrected by retrying with a backoff. + */ + Unavailable = 14, + + /** Unrecoverable data loss or corruption. */ + DataLoss = 15, + + /** The request does not have valid authentication credentials for the + operation. */ + Unauthenticated = 16 +}; + +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_INCLUDE_FIREBASE_FIRESTORE_FIRESTORE_ERRORS_H_ diff --git a/Firestore/core/include/firebase/firestore/geo_point.h b/Firestore/core/include/firebase/firestore/geo_point.h new file mode 100644 index 00000000000..cc366be3125 --- /dev/null +++ b/Firestore/core/include/firebase/firestore/geo_point.h @@ -0,0 +1,89 @@ +/* + * Copyright 2018 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. + */ + +#ifndef FIRESTORE_CORE_INCLUDE_FIREBASE_FIRESTORE_GEO_POINT_H_ +#define FIRESTORE_CORE_INCLUDE_FIREBASE_FIRESTORE_GEO_POINT_H_ + +namespace firebase { +namespace firestore { + +/** + * An immutable object representing a geographical point in Firestore. The point + * is represented as a latitude/longitude pair. + * + * Latitude values are in the range of [-90, 90]. + * Longitude values are in the range of [-180, 180]. + */ +class GeoPoint { + public: + /** + * Creates a `GeoPoint` with both latitude and longitude being 0. + */ + GeoPoint(); + + /** + * Creates a `GeoPoint` from the provided latitude and longitude degrees. + * + * @param latitude The latitude as number between -90 and 90. + * @param longitude The longitude as number between -180 and 180. + */ + GeoPoint(double latitude, double longitude); + + GeoPoint(const GeoPoint& other) = default; + GeoPoint(GeoPoint&& other) = default; + GeoPoint& operator=(const GeoPoint& other) = default; + GeoPoint& operator=(GeoPoint&& other) = default; + + double latitude() const { + return latitude_; + } + + double longitude() const { + return longitude_; + } + + private: + double latitude_; + double longitude_; +}; + +/** Compares against another GeoPoint. */ +bool operator<(const GeoPoint& lhs, const GeoPoint& rhs); + +inline bool operator>(const GeoPoint& lhs, const GeoPoint& rhs) { + return rhs < lhs; +} + +inline bool operator>=(const GeoPoint& lhs, const GeoPoint& rhs) { + return !(lhs < rhs); +} + +inline bool operator<=(const GeoPoint& lhs, const GeoPoint& rhs) { + return !(lhs > rhs); +} + +inline bool operator!=(const GeoPoint& lhs, const GeoPoint& rhs) { + return lhs < rhs || lhs > rhs; +} + +inline bool operator==(const GeoPoint& lhs, const GeoPoint& rhs) { + return !(lhs != rhs); +} + +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_INCLUDE_FIREBASE_FIRESTORE_GEO_POINT_H_ diff --git a/Firestore/core/src/firebase/firestore/CMakeLists.txt b/Firestore/core/src/firebase/firestore/CMakeLists.txt new file mode 100644 index 00000000000..3f5522c7cfc --- /dev/null +++ b/Firestore/core/src/firebase/firestore/CMakeLists.txt @@ -0,0 +1,22 @@ +# Copyright 2018 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. + +# Public types to be used both internally and externally. +cc_library( + firebase_firestore_types + SOURCES + geo_point.cc + DEPENDS + firebase_firestore_util +) diff --git a/Firestore/core/src/firebase/firestore/auth/CMakeLists.txt b/Firestore/core/src/firebase/firestore/auth/CMakeLists.txt new file mode 100644 index 00000000000..2241fae2a8c --- /dev/null +++ b/Firestore/core/src/firebase/firestore/auth/CMakeLists.txt @@ -0,0 +1,52 @@ +# Copyright 2018 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. + +cc_library( + firebase_firestore_auth_base + SOURCES + credentials_provider.cc + credentials_provider.h + token.cc + token.h + user.cc + user.h + DEPENDS + absl_strings + firebase_firestore_util +) + +cc_library( + firebase_firestore_auth_apple + SOURCES + firebase_credentials_provider_apple.h + firebase_credentials_provider_apple.mm + DEPENDS + FirebaseCore + firebase_firestore_auth_base + EXCLUDE_FROM_ALL +) + +if(APPLE) + list(APPEND AUTH_DEPENDS firebase_firestore_auth_apple) +endif() + +cc_library( + firebase_firestore_auth + SOURCES + empty_credentials_provider.cc + empty_credentials_provider.h + DEPENDS + ${AUTH_DEPENDS} + firebase_firestore_auth_base +) diff --git a/Firestore/core/src/firebase/firestore/auth/credentials_provider.cc b/Firestore/core/src/firebase/firestore/auth/credentials_provider.cc new file mode 100644 index 00000000000..03019441341 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/auth/credentials_provider.cc @@ -0,0 +1,31 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/core/src/firebase/firestore/auth/credentials_provider.h" + +namespace firebase { +namespace firestore { +namespace auth { + +CredentialsProvider::CredentialsProvider() : user_change_listener_(nullptr) { +} + +CredentialsProvider::~CredentialsProvider() { +} + +} // namespace auth +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/auth/credentials_provider.h b/Firestore/core/src/firebase/firestore/auth/credentials_provider.h new file mode 100644 index 00000000000..b9a8a24a86a --- /dev/null +++ b/Firestore/core/src/firebase/firestore/auth/credentials_provider.h @@ -0,0 +1,82 @@ +/* + * Copyright 2018 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. + */ + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_AUTH_CREDENTIALS_PROVIDER_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_AUTH_CREDENTIALS_PROVIDER_H_ + +#include +#include + +#include "Firestore/core/include/firebase/firestore/firestore_errors.h" +#include "Firestore/core/src/firebase/firestore/auth/token.h" +#include "Firestore/core/src/firebase/firestore/auth/user.h" +#include "absl/strings/string_view.h" + +namespace firebase { +namespace firestore { +namespace auth { + +// `TokenErrorListener` is a listener that gets a token or an error. +// token: An auth token as a string, or nullptr if error occurred. +// error_code: The error code if one occurred, or else FirestoreErrorCode::Ok. +// error_msg: The error if one occurred, or else nullptr. +typedef std::function + TokenListener; + +// Listener notified with a User change. +typedef std::function UserChangeListener; + +/** + * Provides methods for getting the uid and token for the current user and + * listen for changes. + */ +class CredentialsProvider { + public: + CredentialsProvider(); + + virtual ~CredentialsProvider(); + + /** + * Requests token for the current user, optionally forcing a refreshed token + * to be fetched. + */ + virtual void GetToken(bool force_refresh, TokenListener completion) = 0; + + /** + * Sets the listener to be notified of user changes (sign-in / sign-out). It + * is immediately called once with the initial user. + * + * Call with nullptr to remove previous listener. + */ + virtual void SetUserChangeListener(UserChangeListener listener) = 0; + + protected: + /** + * A listener to be notified of user changes (sign-in / sign-out). It is + * immediately called once with the initial user. + * + * Note that this block will be called back on an arbitrary thread that is not + * the normal Firestore worker thread. + */ + UserChangeListener user_change_listener_; +}; + +} // namespace auth +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_AUTH_CREDENTIALS_PROVIDER_H_ diff --git a/Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.cc b/Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.cc new file mode 100644 index 00000000000..0fa70c0540f --- /dev/null +++ b/Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.cc @@ -0,0 +1,43 @@ +/* + * Copyright 2018 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. + */ + +#define UNUSED(x) (void)(x) + +#include "Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.h" + +namespace firebase { +namespace firestore { +namespace auth { + +void EmptyCredentialsProvider::GetToken(bool force_refresh, + TokenListener completion) { + UNUSED(force_refresh); + if (completion) { + // Invalid token will force the GRPC fallback to use default settings. + completion(Token::Invalid(), FirestoreErrorCode::Ok, ""); + } +} + +void EmptyCredentialsProvider::SetUserChangeListener( + UserChangeListener listener) { + if (listener) { + listener(User::Unauthenticated()); + } +} + +} // namespace auth +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.h b/Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.h new file mode 100644 index 00000000000..55b3cc62c09 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.h @@ -0,0 +1,37 @@ +/* + * Copyright 2018 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. + */ + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_AUTH_EMPTY_CREDENTIALS_PROVIDER_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_AUTH_EMPTY_CREDENTIALS_PROVIDER_H_ + +#include "Firestore/core/src/firebase/firestore/auth/credentials_provider.h" + +namespace firebase { +namespace firestore { +namespace auth { + +/** `EmptyCredentialsProvider` always yields an empty token. */ +class EmptyCredentialsProvider : public CredentialsProvider { + public: + void GetToken(bool force_refresh, TokenListener completion) override; + void SetUserChangeListener(UserChangeListener listener) override; +}; + +} // namespace auth +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_AUTH_EMPTY_CREDENTIALS_PROVIDER_H_ diff --git a/Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.h b/Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.h new file mode 100644 index 00000000000..66c3c87f955 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.h @@ -0,0 +1,113 @@ +/* + * Copyright 2018 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. + */ + +// Right now, FirebaseCredentialsProvider only support APPLE build. +#if !defined(__OBJC__) +#error "This header only supports Objective-C++." +#endif // !defined(__OBJC__) + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_AUTH_FIREBASE_CREDENTIALS_PROVIDER_APPLE_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_AUTH_FIREBASE_CREDENTIALS_PROVIDER_APPLE_H_ + +#import + +#include +#include // NOLINT(build/c++11) +#include + +#include "Firestore/core/src/firebase/firestore/auth/credentials_provider.h" +#include "Firestore/core/src/firebase/firestore/auth/user.h" +#include "absl/strings/string_view.h" + +@class FIRApp; + +namespace firebase { +namespace firestore { +namespace auth { + +/** + * `FirebaseCredentialsProvider` uses Firebase Auth via `FIRApp` to get an auth + * token. + * + * NOTE: To simplify the implementation, it requires that you set + * `userChangeListener` with a non-`nil` value no more than once and don't call + * `getTokenForcingRefresh:` after setting it to `nil`. + * + * This class must be implemented in a thread-safe manner since it is accessed + * from the thread backing our internal worker queue and the callbacks from + * FIRAuth will be executed on an arbitrary different thread. + * + * For non-Apple desktop build, this is right now just a stub. + */ +class FirebaseCredentialsProvider : public CredentialsProvider { + public: + // TODO(zxu123): Provide a ctor to accept the C++ Firebase Games App, which + // deals all platforms. Right now, only works for FIRApp*. + /** + * Initializes a new FirebaseCredentialsProvider. + * + * @param app The Firebase app from which to get credentials. + */ + explicit FirebaseCredentialsProvider(FIRApp* app); + + ~FirebaseCredentialsProvider() override; + + void GetToken(bool force_refresh, TokenListener completion) override; + + void SetUserChangeListener(UserChangeListener listener) override; + + private: + /** + * Most contents of the FirebaseCredentialProvider are kept in this + * Contents object and pointed to with a shared pointer. Callbacks + * registered with FirebaseAuth use weak pointers to the Contents to + * avoid races between notifications arriving and C++ object destruction. + */ + struct Contents { + Contents(FIRApp* app, User&& user) + : app(app), current_user(std::move(user)), mutex() { + } + + const FIRApp* app; + + /** + * The current user as reported to us via our AuthStateDidChangeListener. + */ + User current_user; + + /** + * Counter used to detect if the user changed while a + * -getTokenForcingRefresh: request was outstanding. + */ + int user_counter = 0; + + std::mutex mutex; + }; + + /** + * Handle used to stop receiving auth changes once userChangeListener is + * removed. + */ + id auth_listener_handle_; + + std::shared_ptr contents_; +}; + +} // namespace auth +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_AUTH_FIREBASE_CREDENTIALS_PROVIDER_APPLE_H_ diff --git a/Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.mm b/Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.mm new file mode 100644 index 00000000000..1babe82d206 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.mm @@ -0,0 +1,134 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.h" + +#import +#import +#import + +#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" +#include "Firestore/core/src/firebase/firestore/util/string_apple.h" + +namespace firebase { +namespace firestore { +namespace auth { + +FirebaseCredentialsProvider::FirebaseCredentialsProvider(FIRApp* app) + : contents_(std::make_shared(app, User::FromUid([app getUID]))) { + std::weak_ptr weak_contents = contents_; + + auth_listener_handle_ = [[NSNotificationCenter defaultCenter] + addObserverForName:FIRAuthStateDidChangeInternalNotification + object:nil + queue:nil + usingBlock:^(NSNotification* notification) { + std::shared_ptr contents = weak_contents.lock(); + if (!contents) { + return; + } + + std::unique_lock lock(contents->mutex); + NSDictionary* user_info = notification.userInfo; + + // ensure we're only notifiying for the current app. + FIRApp* notified_app = + user_info[FIRAuthStateDidChangeInternalNotificationAppKey]; + if (![contents->app isEqual:notified_app]) { + return; + } + + NSString* user_id = + user_info[FIRAuthStateDidChangeInternalNotificationUIDKey]; + User new_user = User::FromUid(user_id); + if (new_user != contents->current_user) { + contents->current_user = new_user; + contents->user_counter++; + UserChangeListener listener = user_change_listener_; + if (listener) { + listener(contents->current_user); + } + } + }]; +} + +FirebaseCredentialsProvider::~FirebaseCredentialsProvider() { + if (auth_listener_handle_) { + // Even though iOS 9 (and later) and macOS 10.11 (and later) keep a weak + // reference to the observer so we could avoid this removeObserver call, we + // still support iOS 8 which requires it. + [[NSNotificationCenter defaultCenter] removeObserver:auth_listener_handle_]; + } +} + +void FirebaseCredentialsProvider::GetToken(bool force_refresh, + TokenListener completion) { + FIREBASE_ASSERT_MESSAGE(auth_listener_handle_, + "GetToken cannot be called after listener removed."); + + // Take note of the current value of the userCounter so that this method can + // fail if there is a user change while the request is outstanding. + int initial_user_counter = contents_->user_counter; + + std::weak_ptr weak_contents = contents_; + void (^get_token_callback)(NSString*, NSError*) = ^( + NSString* _Nullable token, NSError* _Nullable error) { + std::shared_ptr contents = weak_contents.lock(); + if (!contents) { + return; + } + + std::unique_lock lock(contents->mutex); + if (initial_user_counter != contents->user_counter) { + // Cancel the request since the user changed while the request was + // outstanding so the response is likely for a previous user (which + // user, we can't be sure). + completion(Token::Invalid(), FirestoreErrorCode::Aborted, + "getToken aborted due to user change."); + } else { + completion( + Token{util::MakeStringView(token), contents->current_user}, + error == nil ? FirestoreErrorCode::Ok : error.code, + error == nil ? "" : util::MakeStringView(error.localizedDescription)); + } + }; + + [contents_->app getTokenForcingRefresh:force_refresh + withCallback:get_token_callback]; +} + +void FirebaseCredentialsProvider::SetUserChangeListener( + UserChangeListener listener) { + std::unique_lock lock(contents_->mutex); + if (listener) { + FIREBASE_ASSERT_MESSAGE(!user_change_listener_, + "set user_change_listener twice!"); + // Fire initial event. + listener(contents_->current_user); + } else { + FIREBASE_ASSERT_MESSAGE(auth_listener_handle_, + "removed user_change_listener twice!"); + FIREBASE_ASSERT_MESSAGE(user_change_listener_, + "user_change_listener removed without being set!"); + [[NSNotificationCenter defaultCenter] removeObserver:auth_listener_handle_]; + auth_listener_handle_ = nil; + } + user_change_listener_ = listener; +} + +} // namespace auth +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/auth/token.cc b/Firestore/core/src/firebase/firestore/auth/token.cc new file mode 100644 index 00000000000..4ee1b698e85 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/auth/token.cc @@ -0,0 +1,37 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/core/src/firebase/firestore/auth/token.h" + +namespace firebase { +namespace firestore { +namespace auth { + +Token::Token(const absl::string_view token, const User& user) + : token_(token), user_(user), is_valid_(true) { +} + +Token::Token() : token_(), user_(User::Unauthenticated()), is_valid_(false) { +} + +const Token& Token::Invalid() { + static const Token kInvalidToken; + return kInvalidToken; +} + +} // namespace auth +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/auth/token.h b/Firestore/core/src/firebase/firestore/auth/token.h new file mode 100644 index 00000000000..ff8d2f02f6e --- /dev/null +++ b/Firestore/core/src/firebase/firestore/auth/token.h @@ -0,0 +1,87 @@ +/* + * Copyright 2018 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. + */ + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_AUTH_TOKEN_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_AUTH_TOKEN_H_ + +#include + +#include "Firestore/core/src/firebase/firestore/auth/user.h" +#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" +#include "absl/strings/string_view.h" + +namespace firebase { +namespace firestore { +namespace auth { + +/** + * The current User and the authentication token provided by the underlying + * authentication mechanism. This is the result of calling + * CredentialsProvider::GetToken(). + * + * ## Portability notes: no TokenType on iOS + * + * The TypeScript client supports 1st party Oauth tokens (for the Firebase + * Console to auth as the developer) and OAuth2 tokens for the node.js sdk to + * auth with a service account. We don't have plans to support either case on + * mobile so there's no TokenType here. + */ +// TODO(zxu123): Make this support token-type for desktop workflow. +class Token { + public: + Token(const absl::string_view token, const User& user); + + /** The actual raw token. */ + const std::string& token() const { + FIREBASE_ASSERT(is_valid_); + return token_; + } + + /** + * The user with which the token is associated (used for persisting user + * state on disk, etc.). + */ + const User& user() const { + return user_; + } + + /** + * Whether the token is a valid one. + * + * ## Portability notes: Invalid token is the equivalent of nil in the iOS + * token implementation. We use value instead of pointer for Token instance in + * the C++ migration. + */ + bool is_valid() const { + return is_valid_; + } + + /** Returns an invalid token. */ + static const Token& Invalid(); + + private: + Token(); + + const std::string token_; + const User user_; + const bool is_valid_; +}; + +} // namespace auth +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_AUTH_TOKEN_H_ diff --git a/Firestore/core/src/firebase/firestore/auth/user.cc b/Firestore/core/src/firebase/firestore/auth/user.cc new file mode 100644 index 00000000000..f442d7b3d3d --- /dev/null +++ b/Firestore/core/src/firebase/firestore/auth/user.cc @@ -0,0 +1,39 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/core/src/firebase/firestore/auth/user.h" + +#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" + +namespace firebase { +namespace firestore { +namespace auth { + +User::User() : uid_(), is_authenticated_(false) { +} + +User::User(const absl::string_view uid) : uid_(uid), is_authenticated_(true) { + FIREBASE_ASSERT(!uid.empty()); +} + +const User& User::Unauthenticated() { + static const User kUnauthenticated; + return kUnauthenticated; +} + +} // namespace auth +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/auth/user.h b/Firestore/core/src/firebase/firestore/auth/user.h new file mode 100644 index 00000000000..3918c61c581 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/auth/user.h @@ -0,0 +1,103 @@ +/* + * Copyright 2018 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. + */ + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_AUTH_USER_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_AUTH_USER_H_ + +#if defined(__OBJC__) +#import +#include "Firestore/core/src/firebase/firestore/util/string_apple.h" +#endif // defined(__OBJC__) + +#include + +#include "absl/strings/string_view.h" + +namespace firebase { +namespace firestore { +namespace auth { + +/** + * Simple wrapper around a nullable UID. Mostly exists to make code more + * readable and for compatibility with other clients where map keys cannot be + * null. + */ +class User { + public: + /** Construct an unauthenticated user. */ + User(); + + /** Construct an authenticated user with the given UID. */ + explicit User(const absl::string_view uid); + + const std::string& uid() const { + return uid_; + } + + // PORTING NOTE: Here use more clear naming is_authenticated() instead of + // is_unauthenticated(). + bool is_authenticated() const { + return is_authenticated_; + } + + /** Returns an unauthenticated instance. */ + static const User& Unauthenticated(); + +#if defined(__OBJC__) + /** + * Returns an authenticated user if uid is non-nil, otherwise an + * unauthenticated user. + */ + static User FromUid(NSString* _Nullable uid) { + if (uid == nil) { + return Unauthenticated(); + } else { + return User(util::MakeStringView(uid)); + } + } +#endif // defined(__OBJC__) + + User& operator=(const User& other) = default; + + friend bool operator==(const User& lhs, const User& rhs); + + private: + std::string uid_; + bool is_authenticated_; +}; + +inline bool operator==(const User& lhs, const User& rhs) { + return lhs.is_authenticated_ == rhs.is_authenticated_ && + (!lhs.is_authenticated_ || lhs.uid_ == rhs.uid_); +} + +inline bool operator!=(const User& lhs, const User& rhs) { + return !(lhs == rhs); +} + +// Specializations of std::hash is prohibited. We define a hash function to be +// passed through manually. +struct HashUser { + inline int64_t operator()(const User& user) const { + return std::hash{}(user.uid()); + } +}; + +} // namespace auth +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_AUTH_USER_H_ diff --git a/Firestore/core/src/firebase/firestore/base/port.h b/Firestore/core/src/firebase/firestore/base/port.h deleted file mode 100644 index 5e3959d5302..00000000000 --- a/Firestore/core/src/firebase/firestore/base/port.h +++ /dev/null @@ -1,33 +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. - */ - -#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_BASE_PORT_H_ -#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_BASE_PORT_H_ - -#if defined(__APPLE__) -// On Apple platforms we support building via CocoaPods without CMake. When -// building this way we can't test the presence of features so predefine all -// the platform-support feature macros to their expected values. - -// All supported Apple platforms have arc4random(3). -#define HAVE_ARC4RANDOM 1 - -#else - -#error "Unknown platform." -#endif // defined(__APPLE__) - -#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_BASE_PORT_H_ diff --git a/Firestore/core/src/firebase/firestore/core/CMakeLists.txt b/Firestore/core/src/firebase/firestore/core/CMakeLists.txt new file mode 100644 index 00000000000..cf3cafec2da --- /dev/null +++ b/Firestore/core/src/firebase/firestore/core/CMakeLists.txt @@ -0,0 +1,25 @@ +# Copyright 2018 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. + +cc_library( + firebase_firestore_core + SOURCES + database_info.cc + database_info.h + target_id_generator.cc + target_id_generator.h + DEPENDS + absl_strings + firebase_firestore_model +) diff --git a/Firestore/core/src/firebase/firestore/core/database_info.cc b/Firestore/core/src/firebase/firestore/core/database_info.cc new file mode 100644 index 00000000000..b83da37a2f1 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/core/database_info.cc @@ -0,0 +1,36 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/core/src/firebase/firestore/core/database_info.h" + +namespace firebase { +namespace firestore { +namespace core { + +DatabaseInfo::DatabaseInfo( + const firebase::firestore::model::DatabaseId& database_id, + const absl::string_view persistence_key, + const absl::string_view host, + bool ssl_enabled) + : database_id_(database_id), + persistence_key_(persistence_key), + host_(host), + ssl_enabled_(ssl_enabled) { +} + +} // namespace core +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/core/database_info.h b/Firestore/core/src/firebase/firestore/core/database_info.h new file mode 100644 index 00000000000..2e1303e9e09 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/core/database_info.h @@ -0,0 +1,79 @@ +/* + * Copyright 2018 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. + */ + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_CORE_DATABASE_INFO_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_CORE_DATABASE_INFO_H_ + +#include + +#include "Firestore/core/src/firebase/firestore/model/database_id.h" +#include "absl/strings/string_view.h" + +namespace firebase { +namespace firestore { +namespace core { + +/** DatabaseInfo contains data about the database. */ +class DatabaseInfo { + public: +#if defined(__OBJC__) + // For objective-c++ initialization; to be removed after migration. + // Do NOT use in C++ code. + DatabaseInfo() = default; +#endif // defined(__OBJC__) + + /** + * Creates a new DatabaseInfo. + * + * @param database_id The project/database to use. + * @param persistence_key A unique identifier for this Firestore's local + * storage. Usually derived from -[FIRApp appName]. + * @param host The hostname of the datastore backend. + * @param ssl_enabled Whether to use SSL when connecting. + */ + DatabaseInfo(const firebase::firestore::model::DatabaseId& database_id, + const absl::string_view persistence_key, + const absl::string_view host, + bool ssl_enabled); + + const firebase::firestore::model::DatabaseId& database_id() const { + return database_id_; + } + + const std::string& persistence_key() const { + return persistence_key_; + } + + const std::string& host() const { + return host_; + } + + bool ssl_enabled() const { + return ssl_enabled_; + } + + private: + firebase::firestore::model::DatabaseId database_id_; + std::string persistence_key_; + std::string host_; + bool ssl_enabled_; +}; + +} // namespace core +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_CORE_DATABASE_INFO_H_ diff --git a/Firestore/core/src/firebase/firestore/core/target_id_generator.cc b/Firestore/core/src/firebase/firestore/core/target_id_generator.cc new file mode 100644 index 00000000000..6d23d643913 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/core/target_id_generator.cc @@ -0,0 +1,62 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/core/src/firebase/firestore/core/target_id_generator.h" + +namespace firebase { +namespace firestore { +namespace core { + +TargetIdGenerator::TargetIdGenerator(const TargetIdGenerator& value) + : generator_id_(value.generator_id_), previous_id_(value.previous_id_) { +} + +TargetIdGenerator::TargetIdGenerator(TargetIdGeneratorId generator_id, + TargetId after) + : generator_id_(generator_id) { + const TargetId after_without_generator = (after >> kReservedBits) + << kReservedBits; + const TargetId after_generator = after - after_without_generator; + const TargetId generator = static_cast(generator_id); + if (after_generator >= generator) { + // For example, if: + // self.generatorID = 0b0000 + // after = 0b1011 + // afterGenerator = 0b0001 + // Then: + // previous = 0b1010 + // next = 0b1100 + previous_id_ = after_without_generator | generator; + } else { + // For example, if: + // self.generatorID = 0b0001 + // after = 0b1010 + // afterGenerator = 0b0000 + // Then: + // previous = 0b1001 + // next = 0b1011 + previous_id_ = (after_without_generator | generator) - (1 << kReservedBits); + } +} + +TargetId TargetIdGenerator::NextId() { + previous_id_ += 1 << kReservedBits; + return previous_id_; +} + +} // namespace core +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/core/target_id_generator.h b/Firestore/core/src/firebase/firestore/core/target_id_generator.h new file mode 100644 index 00000000000..7d30cf953a3 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/core/target_id_generator.h @@ -0,0 +1,83 @@ +/* + * Copyright 2018 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. + */ + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_CORE_TARGET_ID_GENERATOR_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_CORE_TARGET_ID_GENERATOR_H_ + +#include "Firestore/core/src/firebase/firestore/core/types.h" + +namespace firebase { +namespace firestore { +namespace core { + +/** The set of all valid generators. */ +enum class TargetIdGeneratorId { LocalStore = 0, SyncEngine = 1 }; + +/** + * Generates monotonically increasing integer IDs. There are separate generators + * for different scopes. While these generators will operate independently of + * each other, they are scoped, such that no two generators will ever produce + * the same ID. This is useful, because sometimes the backend may group IDs from + * separate parts of the client into the same ID space. + * + * Not thread-safe. + */ +class TargetIdGenerator { + public: + // Makes Objective-C++ code happy to provide a default ctor. + TargetIdGenerator() = default; + + TargetIdGenerator(const TargetIdGenerator& value); + + /** + * Creates and returns the TargetIdGenerator for the local store. + * + * @param after An ID to start at. Every call to NextId returns a larger id. + * @return An instance of TargetIdGenerator. + */ + static TargetIdGenerator LocalStoreTargetIdGenerator(TargetId after) { + return TargetIdGenerator(TargetIdGeneratorId::LocalStore, after); + } + + /** + * Creates and returns the TargetIdGenerator for the sync engine. + * + * @param after An ID to start at. Every call to NextId returns a larger id. + * @return An instance of TargetIdGenerator. + */ + static TargetIdGenerator SyncEngineTargetIdGenerator(TargetId after) { + return TargetIdGenerator(TargetIdGeneratorId::SyncEngine, after); + } + + TargetIdGeneratorId generator_id() { + return generator_id_; + } + + TargetId NextId(); + + private: + TargetIdGenerator(TargetIdGeneratorId generator_id, TargetId after); + TargetIdGeneratorId generator_id_; + TargetId previous_id_; + + static const int kReservedBits = 1; +}; + +} // namespace core +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_CORE_TARGET_ID_GENERATOR_H_ diff --git a/Firestore/core/src/firebase/firestore/core/types.h b/Firestore/core/src/firebase/firestore/core/types.h new file mode 100644 index 00000000000..65c2b8ca1e1 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/core/types.h @@ -0,0 +1,32 @@ +/* + * Copyright 2018 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. + */ + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_CORE_TYPES_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_CORE_TYPES_H_ + +#include + +namespace firebase { +namespace firestore { +namespace core { + +typedef int32_t TargetId; + +} // namespace core +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_CORE_TYPES_H_ diff --git a/Firestore/core/src/firebase/firestore/geo_point.cc b/Firestore/core/src/firebase/firestore/geo_point.cc new file mode 100644 index 00000000000..fb010233c17 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/geo_point.cc @@ -0,0 +1,50 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/core/include/firebase/firestore/geo_point.h" + +#include + +#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" + +namespace firebase { +namespace firestore { + +GeoPoint::GeoPoint() : GeoPoint(0, 0) { +} + +GeoPoint::GeoPoint(double latitude, double longitude) + : latitude_(latitude), longitude_(longitude) { + FIREBASE_ASSERT_MESSAGE_WITH_EXPRESSION( + !isnan(latitude) && -90 <= latitude && latitude <= 90, + -90 <= latitude && latitude <= 90, + "Latitude must be in the range of [-90, 90]"); + FIREBASE_ASSERT_MESSAGE_WITH_EXPRESSION( + !isnan(longitude) && -180 <= longitude && longitude <= 180, + -180 <= longitude && longitude <= 180, + "Latitude must be in the range of [-180, 180]"); +} + +bool operator<(const GeoPoint& lhs, const GeoPoint& rhs) { + if (lhs.latitude() == rhs.latitude()) { + return lhs.longitude() < rhs.longitude(); + } else { + return lhs.latitude() < rhs.latitude(); + } +} + +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/immutable/CMakeLists.txt b/Firestore/core/src/firebase/firestore/immutable/CMakeLists.txt new file mode 100644 index 00000000000..e8a95cd7e1b --- /dev/null +++ b/Firestore/core/src/firebase/firestore/immutable/CMakeLists.txt @@ -0,0 +1,21 @@ +# Copyright 2018 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. + +cc_library( + firebase_firestore_immutable + SOURCES + array_sorted_map.cc + array_sorted_map.h + map_entry.h +) diff --git a/Firestore/core/src/firebase/firestore/immutable/array_sorted_map.cc b/Firestore/core/src/firebase/firestore/immutable/array_sorted_map.cc new file mode 100644 index 00000000000..48e7553d479 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/immutable/array_sorted_map.cc @@ -0,0 +1,30 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/core/src/firebase/firestore/immutable/array_sorted_map.h" + +namespace firebase { +namespace firestore { +namespace immutable { +namespace impl { + +// Define external storage for constants: +constexpr ArraySortedMapBase::size_type ArraySortedMapBase::kFixedSize; + +} // namespace impl +} // namespace immutable +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/immutable/array_sorted_map.h b/Firestore/core/src/firebase/firestore/immutable/array_sorted_map.h new file mode 100644 index 00000000000..d0210a8bf0b --- /dev/null +++ b/Firestore/core/src/firebase/firestore/immutable/array_sorted_map.h @@ -0,0 +1,318 @@ +/* + * Copyright 2018 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. + */ + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_IMMUTABLE_ARRAY_SORTED_MAP_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_IMMUTABLE_ARRAY_SORTED_MAP_H_ + +#include +#include +#include +#include +#include +#include + +#include "Firestore/core/src/firebase/firestore/immutable/map_entry.h" + +namespace firebase { +namespace firestore { +namespace immutable { + +namespace impl { + +/** + * A base class for implementing ArraySortedMap, containing types and constants + * that don't depend upon the template parameters to the main class. + * + * Note that this exists as a base class rather than as just a namespace in + * order to make it possible for users of ArraySortedMap to avoid needing to + * declare storage for each instantiation of the template. + */ +class ArraySortedMapBase { + public: + /** + * The type of size() methods on immutable collections. Note that this is not + * size_t specifically to save space in the TreeSortedMap implementation. + */ + using size_type = uint32_t; + + /** + * The maximum size of an ArraySortedMap. + * + * This is the size threshold where we use a tree backed sorted map instead of + * an array backed sorted map. This is a more or less arbitrary chosen value, + * that was chosen to be large enough to fit most of object kind of Firebase + * data, but small enough to not notice degradation in performance for + * inserting and lookups. Feel free to empirically determine this constant, + * but don't expect much gain in real world performance. + */ + // TODO(wilhuff): actually use this for switching implementations. + static constexpr size_type kFixedSize = 25; +}; + +/** + * A bounded-size array that allocates its contents directly in itself. This + * saves a heap allocation when compared with std::vector (though std::vector + * can resize itself while FixedArray cannot). + * + * Unlike std::array, FixedArray keeps track of its size and grows up to the + * fixed_size limit. Inserting more elements than fixed_size will trigger an + * assertion failure. + * + * ArraySortedMap does not actually contain its array: it contains a shared_ptr + * to a FixedArray. + * + * @tparam T The type of an element in the array. + * @tparam fixed_size the fixed size to use in creating the FixedArray. + */ +template +class FixedArray { + public: + using size_type = ArraySortedMapBase::size_type; + using array_type = std::array; + using iterator = typename array_type::iterator; + using const_iterator = typename array_type::const_iterator; + + FixedArray() { + } + + template + FixedArray(SourceIterator src_begin, SourceIterator src_end) { + append(src_begin, src_end); + } + + /** + * Appends to this array, copying from the given src_begin up to but not + * including the src_end. + */ + template + void append(SourceIterator src_begin, SourceIterator src_end) { + size_type appending = static_cast(src_end - src_begin); + size_type new_size = size_ + appending; + assert(new_size <= fixed_size); + + std::copy(src_begin, src_end, end()); + size_ = new_size; + } + + /** + * Appends a single value to the array. + */ + void append(T&& value) { + size_type new_size = size_ + 1; + assert(new_size <= fixed_size); + + *end() = std::move(value); + size_ = new_size; + } + + const_iterator begin() const { + return contents_.begin(); + } + + const_iterator end() const { + return begin() + size_; + } + + size_type size() const { + return size_; + } + + private: + iterator begin() { + return contents_.begin(); + } + + iterator end() { + return begin() + size_; + } + + array_type contents_; + size_type size_ = 0; +}; + +} // namespace impl + +/** + * ArraySortedMap is a value type containing a map. It is immutable, but has + * methods to efficiently create new maps that are mutations of it. + */ +template > +class ArraySortedMap : public impl::ArraySortedMapBase { + public: + using key_comparator_type = KeyComparator; + + /** + * The type of the entries stored in the map. + */ + using value_type = std::pair; + + /** + * The type of the fixed-size array containing entries of value_type. + */ + using array_type = impl::FixedArray; + using const_iterator = typename array_type::const_iterator; + + using array_pointer = std::shared_ptr; + + /** + * Creates an empty ArraySortedMap. + */ + explicit ArraySortedMap(const C& comparator = C()) + : array_(EmptyArray()), key_comparator_(comparator) { + } + + /** + * Creates an ArraySortedMap containing the given entries. + */ + ArraySortedMap(std::initializer_list entries, + const C& comparator = C()) + : array_(std::make_shared(entries.begin(), entries.end())), + key_comparator_(comparator) { + } + + /** + * Creates a new map identical to this one, but with a key-value pair added or + * updated. + * + * @param key The key to insert/update. + * @param value The value to associate with the key. + * @return A new dictionary with the added/updated value. + */ + ArraySortedMap insert(const K& key, const V& value) const { + const_iterator current_end = end(); + const_iterator pos = LowerBound(key); + bool replacing_entry = false; + + if (pos != current_end) { + // LowerBound found an entry where pos->first >= pair.first. Reversing the + // argument order here tests pair.first < pos->first. + replacing_entry = !key_comparator_(key, *pos); + if (replacing_entry && value == pos->second) { + return *this; + } + } + + // Copy the segment before the found position. If not found, this is + // everything. + auto copy = std::make_shared(begin(), pos); + + // Copy the value to be inserted. + copy->append(value_type(key, value)); + + if (replacing_entry) { + // Skip the thing at pos because it compares the same as the pair above. + copy->append(pos + 1, current_end); + } else { + copy->append(pos, current_end); + } + return wrap(copy); + } + + /** + * Creates a new map identical to this one, but with a key removed from it. + * + * @param key The key to remove. + * @return A new dictionary without that value. + */ + ArraySortedMap erase(const K& key) const { + const_iterator current_end = end(); + const_iterator pos = find(key); + if (pos == current_end) { + return *this; + } else if (size() <= 1) { + // If the key was found and it's the last entry, removing it would make + // the result empty. + return wrap(EmptyArray()); + } else { + auto copy = std::make_shared(begin(), pos); + copy->append(pos + 1, current_end); + return wrap(copy); + } + } + + /** + * Finds a value in the map. + * + * @param key The key to look up. + * @return An iterator pointing to the entry containing the key, or end() if + * not found. + */ + const_iterator find(const K& key) const { + const_iterator not_found = end(); + const_iterator lower_bound = LowerBound(key); + if (lower_bound != not_found && !key_comparator_(key, *lower_bound)) { + return lower_bound; + } else { + return not_found; + } + } + + // TODO(wilhuff): indexof + + /** Returns true if the map contains no elements. */ + bool empty() const { + return size() == 0; + } + + /** Returns the number of items in this map. */ + size_type size() const { + return array_->size(); + } + + /** + * Returns an iterator pointing to the first entry in the map. If there are + * no entries in the map, begin() == end(). + */ + const_iterator begin() const { + return array_->begin(); + } + + /** + * Returns an iterator pointing past the last entry in the map. + */ + const_iterator end() const { + return array_->end(); + } + + private: + static array_pointer EmptyArray() { + static const array_pointer kEmptyArray = + std::make_shared(); + return kEmptyArray; + } + + ArraySortedMap(const array_pointer& array, + const key_comparator_type& key_comparator) noexcept + : array_(array), key_comparator_(key_comparator) { + } + + ArraySortedMap wrap(const array_pointer& array) const noexcept { + return ArraySortedMap(array, key_comparator_); + } + + const_iterator LowerBound(const K& key) const { + return std::lower_bound(begin(), end(), key, key_comparator_); + } + + array_pointer array_; + key_comparator_type key_comparator_; +}; + +} // namespace immutable +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_IMMUTABLE_ARRAY_SORTED_MAP_H_ diff --git a/Firestore/core/src/firebase/firestore/immutable/map_entry.h b/Firestore/core/src/firebase/firestore/immutable/map_entry.h new file mode 100644 index 00000000000..2130b5b4945 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/immutable/map_entry.h @@ -0,0 +1,62 @@ +/* + * Copyright 2018 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. + */ + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_IMMUTABLE_MAP_ENTRY_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_IMMUTABLE_MAP_ENTRY_H_ + +#include +#include + +namespace firebase { +namespace firestore { +namespace immutable { + +/** + * Compares two keys out of a map entry. + * + * @tparam K The type of the first value in the pair. + * @tparam V The type of the second value in the pair. + * @tparam C The comparator for use for values of type K + */ +template > +struct KeyComparator { + typedef std::pair pair_type; + + explicit KeyComparator(const C& comparator = C()) + : key_comparator_(comparator) { + } + + bool operator()(const K& lhs, const pair_type& rhs) const noexcept { + return key_comparator_(lhs, rhs.first); + } + + bool operator()(const pair_type& lhs, const K& rhs) const noexcept { + return key_comparator_(lhs.first, rhs); + } + + bool operator()(const pair_type& lhs, const pair_type& rhs) const noexcept { + return key_comparator_(lhs.first, rhs.first); + } + + private: + C key_comparator_; +}; + +} // namespace immutable +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_IMMUTABLE_MAP_ENTRY_H_ diff --git a/Firestore/core/src/firebase/firestore/model/CMakeLists.txt b/Firestore/core/src/firebase/firestore/model/CMakeLists.txt new file mode 100644 index 00000000000..1b0e6a45910 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/model/CMakeLists.txt @@ -0,0 +1,43 @@ +# Copyright 2018 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. + +cc_library( + firebase_firestore_model + SOURCES + base_path.h + database_id.cc + database_id.h + document.cc + document.h + document_key.cc + document_key.h + field_path.cc + field_path.h + field_value.cc + field_value.h + maybe_document.cc + maybe_document.h + no_document.cc + no_document.h + resource_path.cc + resource_path.h + snapshot_version.cc + snapshot_version.h + timestamp.cc + timestamp.h + DEPENDS + absl_strings + firebase_firestore_util + firebase_firestore_types +) diff --git a/Firestore/core/src/firebase/firestore/model/base_path.h b/Firestore/core/src/firebase/firestore/model/base_path.h new file mode 100644 index 00000000000..accce27d077 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/model/base_path.h @@ -0,0 +1,181 @@ +/* + * Copyright 2018 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. + */ + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_BASE_PATH_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_BASE_PATH_H_ + +#include +#include +#include +#include +#include +#include + +#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" + +namespace firebase { +namespace firestore { +namespace model { +namespace impl { + +/** + * BasePath represents a path sequence in the Firestore database. It is composed + * of an ordered sequence of string segments. + * + * BasePath is reassignable and movable. Apart from those, all other mutating + * operations return new independent instances. + * + * ## Subclassing Notes + * + * BasePath is strictly meant as a base class for concrete implementations. It + * doesn't contain a single virtual method, can't be instantiated, and should + * never be used in any polymorphic way. BasePath is templated to allow static + * factory methods to return objects of the derived class (the expected + * inheritance would use CRTP: struct Derived : BasePath). + */ +template +class BasePath { + protected: + using SegmentsT = std::vector; + + public: + using const_iterator = SegmentsT::const_iterator; + + /** Returns i-th segment of the path. */ + const std::string& operator[](const size_t i) const { + FIREBASE_ASSERT_MESSAGE(i < segments_.size(), "index %u out of range", i); + return segments_[i]; + } + + /** Returns the first segment of the path. */ + const std::string& first_segment() const { + FIREBASE_ASSERT_MESSAGE(!empty(), + "Cannot call first_segment on empty path"); + return segments_[0]; + } + /** Returns the last segment of the path. */ + const std::string& last_segment() const { + FIREBASE_ASSERT_MESSAGE(!empty(), "Cannot call last_segment on empty path"); + return segments_[size() - 1]; + } + + size_t size() const { + return segments_.size(); + } + bool empty() const { + return segments_.empty(); + } + + const_iterator begin() const { + return segments_.begin(); + } + const_iterator end() const { + return segments_.end(); + } + + /** + * Returns a new path which is the result of concatenating this path with an + * additional segment. + */ + T Append(const std::string& segment) const { + auto appended = segments_; + appended.push_back(segment); + return T{std::move(appended)}; + } + T Append(std::string&& segment) const { + auto appended = segments_; + appended.push_back(std::move(segment)); + return T{std::move(appended)}; + } + + /** + * Returns a new path which is the result of concatenating this path with an + * another path. + */ + T Append(const T& path) const { + auto appended = segments_; + appended.insert(appended.end(), path.begin(), path.end()); + return T{std::move(appended)}; + } + + /** + * Returns a new path which is the result of omitting the first n segments of + * this path. + */ + T PopFirst(const size_t n = 1) const { + FIREBASE_ASSERT_MESSAGE(n <= size(), + "Cannot call PopFirst(%u) on path of length %u", n, + size()); + return T{begin() + n, end()}; + } + + /** + * Returns a new path which is the result of omitting the last segment of + * this path. + */ + T PopLast() const { + FIREBASE_ASSERT_MESSAGE(!empty(), "Cannot call PopLast() on empty path"); + return T{begin(), end() - 1}; + } + + /** + * Returns true if this path is a prefix of the given path. + * + * Empty path is a prefix of any path. Any path is a prefix of itself. + */ + bool IsPrefixOf(const T& rhs) const { + return size() <= rhs.size() && std::equal(begin(), end(), rhs.begin()); + } + + bool operator==(const BasePath& rhs) const { + return segments_ == rhs.segments_; + } + bool operator!=(const BasePath& rhs) const { + return segments_ != rhs.segments_; + } + bool operator<(const BasePath& rhs) const { + return segments_ < rhs.segments_; + } + bool operator>(const BasePath& rhs) const { + return segments_ > rhs.segments_; + } + bool operator<=(const BasePath& rhs) const { + return segments_ <= rhs.segments_; + } + bool operator>=(const BasePath& rhs) const { + return segments_ >= rhs.segments_; + } + + protected: + BasePath() = default; + template + BasePath(const IterT begin, const IterT end) : segments_{begin, end} { + } + BasePath(std::initializer_list list) : segments_{list} { + } + explicit BasePath(SegmentsT&& segments) : segments_{std::move(segments)} { + } + + private: + SegmentsT segments_; +}; + +} // namespace impl +} // namespace model +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_BASE_PATH_H_ diff --git a/Firestore/core/src/firebase/firestore/model/database_id.cc b/Firestore/core/src/firebase/firestore/model/database_id.cc new file mode 100644 index 00000000000..a756ea7000d --- /dev/null +++ b/Firestore/core/src/firebase/firestore/model/database_id.cc @@ -0,0 +1,36 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/core/src/firebase/firestore/model/database_id.h" + +#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" + +namespace firebase { +namespace firestore { +namespace model { + +constexpr const char* DatabaseId::kDefault; + +DatabaseId::DatabaseId(const absl::string_view project_id, + const absl::string_view database_id) + : project_id_(project_id), database_id_(database_id) { + FIREBASE_ASSERT(!project_id.empty()); + FIREBASE_ASSERT(!database_id.empty()); +} + +} // namespace model +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/model/database_id.h b/Firestore/core/src/firebase/firestore/model/database_id.h new file mode 100644 index 00000000000..01ccf7633eb --- /dev/null +++ b/Firestore/core/src/firebase/firestore/model/database_id.h @@ -0,0 +1,111 @@ +/* + * Copyright 2018 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. + */ + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_DATABASE_ID_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_DATABASE_ID_H_ + +#include + +#include + +#include "absl/strings/string_view.h" + +namespace firebase { +namespace firestore { +namespace model { + +/** A DatabaseId represents a particular database in the Firestore. */ +class DatabaseId { + public: + /** The default name for "unset" database ID in resource names. */ + static constexpr const char* kDefault = "(default)"; + +#if defined(__OBJC__) + // For objective-c++ initialization; to be removed after migration. + // Do NOT use in C++ code. + DatabaseId() = default; +#endif // defined(__OBJC__) + + /** + * Creates and returns a new DatabaseId. + * + * @param project_id The project for the database. + * @param database_id The database in the project to use. + */ + DatabaseId(const absl::string_view project_id, + const absl::string_view database_id); + + const std::string& project_id() const { + return project_id_; + } + + const std::string& database_id() const { + return database_id_; + } + + /** Whether this is the default database of the project. */ + bool IsDefaultDatabase() const { + return database_id_ == kDefault; + } + +#if defined(__OBJC__) + // For objective-c++ hash; to be removed after migration. + // Do NOT use in C++ code. + uint64_t Hash() const { + std::hash hash_fn; + return hash_fn(project_id_) * 31u + hash_fn(database_id_); + } +#endif // defined(__OBJC__) + + friend bool operator<(const DatabaseId& lhs, const DatabaseId& rhs); + + private: + std::string project_id_; + std::string database_id_; +}; + +/** Compares against another DatabaseId. */ +inline bool operator<(const DatabaseId& lhs, const DatabaseId& rhs) { + return lhs.project_id_ < rhs.project_id_ || + (lhs.project_id_ == rhs.project_id_ && + lhs.database_id_ < rhs.database_id_); +} + +inline bool operator>(const DatabaseId& lhs, const DatabaseId& rhs) { + return rhs < lhs; +} + +inline bool operator>=(const DatabaseId& lhs, const DatabaseId& rhs) { + return !(lhs < rhs); +} + +inline bool operator<=(const DatabaseId& lhs, const DatabaseId& rhs) { + return !(lhs > rhs); +} + +inline bool operator!=(const DatabaseId& lhs, const DatabaseId& rhs) { + return lhs < rhs || lhs > rhs; +} + +inline bool operator==(const DatabaseId& lhs, const DatabaseId& rhs) { + return !(lhs != rhs); +} + +} // namespace model +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_DATABASE_ID_H_ diff --git a/Firestore/core/src/firebase/firestore/model/document.cc b/Firestore/core/src/firebase/firestore/model/document.cc new file mode 100644 index 00000000000..16548cdf209 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/model/document.cc @@ -0,0 +1,50 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/core/src/firebase/firestore/model/document.h" + +#include + +#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" + +namespace firebase { +namespace firestore { +namespace model { + +Document::Document(FieldValue&& data, + DocumentKey key, + SnapshotVersion version, + bool has_local_mutations) + : MaybeDocument(std::move(key), std::move(version)), + data_(std::move(data)), + has_local_mutations_(has_local_mutations) { + set_type(Type::Document); + FIREBASE_ASSERT(FieldValue::Type::Object == data.type()); +} + +bool Document::Equals(const MaybeDocument& other) const { + if (other.type() != Type::Document) { + return false; + } + const Document& other_doc = static_cast(other); + return MaybeDocument::Equals(other) && + has_local_mutations_ == other_doc.has_local_mutations_ && + data_ == other_doc.data_; +} + +} // namespace model +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/model/document.h b/Firestore/core/src/firebase/firestore/model/document.h new file mode 100644 index 00000000000..50a7b901c8c --- /dev/null +++ b/Firestore/core/src/firebase/firestore/model/document.h @@ -0,0 +1,72 @@ +/* + * Copyright 2018 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. + */ + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_DOCUMENT_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_DOCUMENT_H_ + +#include "Firestore/core/src/firebase/firestore/model/field_value.h" +#include "Firestore/core/src/firebase/firestore/model/maybe_document.h" + +namespace firebase { +namespace firestore { +namespace model { + +/** + * Represents a document in Firestore with a key, version, data and whether the + * data has local mutations applied to it. + */ +class Document : public MaybeDocument { + public: + /** + * Construct a document. FieldValue must be passed by rvalue. + */ + Document(FieldValue&& data, + DocumentKey key, + SnapshotVersion version, + bool has_local_mutations); + + const FieldValue& data() const { + return data_; + } + + bool has_local_mutations() const { + return has_local_mutations_; + } + + protected: + bool Equals(const MaybeDocument& other) const override; + + private: + FieldValue data_; // This is of type Object. + bool has_local_mutations_; +}; + +/** Compares against another Document. */ +inline bool operator==(const Document& lhs, const Document& rhs) { + return lhs.version() == rhs.version() && lhs.key() == rhs.key() && + lhs.has_local_mutations() == rhs.has_local_mutations() && + lhs.data() == rhs.data(); +} + +inline bool operator!=(const Document& lhs, const Document& rhs) { + return !(lhs == rhs); +} + +} // namespace model +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_DOCUMENT_H_ diff --git a/Firestore/core/src/firebase/firestore/model/document_key.cc b/Firestore/core/src/firebase/firestore/model/document_key.cc new file mode 100644 index 00000000000..ddda4c9d73d --- /dev/null +++ b/Firestore/core/src/firebase/firestore/model/document_key.cc @@ -0,0 +1,54 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/core/src/firebase/firestore/model/document_key.h" + +#include + +#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" + +namespace firebase { +namespace firestore { +namespace model { + +namespace { + +void AssertValidPath(const ResourcePath& path) { + FIREBASE_ASSERT_MESSAGE(DocumentKey::IsDocumentKey(path), + "invalid document key path: %s", + path.CanonicalString().c_str()); +} + +} // namespace + +DocumentKey::DocumentKey(const ResourcePath& path) + : path_{std::make_shared(path)} { + AssertValidPath(*path_); +} + +DocumentKey::DocumentKey(ResourcePath&& path) + : path_{std::make_shared(std::move(path))} { + AssertValidPath(*path_); +} + +const DocumentKey& DocumentKey::Empty() { + static const DocumentKey empty; + return empty; +} + +} // namespace model +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/model/document_key.h b/Firestore/core/src/firebase/firestore/model/document_key.h new file mode 100644 index 00000000000..6eb1e181745 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/model/document_key.h @@ -0,0 +1,101 @@ +/* + * Copyright 2018 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. + */ + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_DOCUMENT_KEY_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_DOCUMENT_KEY_H_ + +#include +#include +#include + +#include "Firestore/core/src/firebase/firestore/model/resource_path.h" +#include "absl/strings/string_view.h" + +namespace firebase { +namespace firestore { +namespace model { + +/** + * DocumentKey represents the location of a document in the Firestore database. + */ +class DocumentKey { + public: + /** Creates a "blank" document key not associated with any document. */ + DocumentKey() : path_{std::make_shared()} { + } + + /** Creates a new document key containing a copy of the given path. */ + explicit DocumentKey(const ResourcePath& path); + + /** Creates a new document key, taking ownership of the given path. */ + explicit DocumentKey(ResourcePath&& path); + + /** + * Creates and returns a new document key using '/' to split the string into + * segments. + */ + static DocumentKey FromPathString(const absl::string_view path) { + return DocumentKey{ResourcePath::FromString(path)}; + } + + /** Creates and returns a new document key with the given segments. */ + static DocumentKey FromSegments(std::initializer_list list) { + return DocumentKey{ResourcePath{list}}; + } + + /** Returns a shared instance of an empty document key. */ + static const DocumentKey& Empty(); + + /** Returns true iff the given path is a path to a document. */ + static bool IsDocumentKey(const ResourcePath& path) { + return path.size() % 2 == 0; + } + + /** The path to the document. */ + const ResourcePath& path() const { + return path_ ? *path_ : Empty().path(); + } + + private: + // This is an optimization to make passing DocumentKey around cheaper (it's + // copied often). + std::shared_ptr path_; +}; + +inline bool operator==(const DocumentKey& lhs, const DocumentKey& rhs) { + return lhs.path() == rhs.path(); +} +inline bool operator!=(const DocumentKey& lhs, const DocumentKey& rhs) { + return lhs.path() != rhs.path(); +} +inline bool operator<(const DocumentKey& lhs, const DocumentKey& rhs) { + return lhs.path() < rhs.path(); +} +inline bool operator<=(const DocumentKey& lhs, const DocumentKey& rhs) { + return lhs.path() <= rhs.path(); +} +inline bool operator>(const DocumentKey& lhs, const DocumentKey& rhs) { + return lhs.path() > rhs.path(); +} +inline bool operator>=(const DocumentKey& lhs, const DocumentKey& rhs) { + return lhs.path() >= rhs.path(); +} + +} // namespace model +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_DOCUMENT_KEY_H_ diff --git a/Firestore/core/src/firebase/firestore/model/field_path.cc b/Firestore/core/src/firebase/firestore/model/field_path.cc new file mode 100644 index 00000000000..bc0e97cfb40 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/model/field_path.cc @@ -0,0 +1,173 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/core/src/firebase/firestore/model/field_path.h" + +#include +#include + +#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" +#include "absl/strings/str_join.h" +#include "absl/strings/str_replace.h" +#include "absl/strings/str_split.h" + +namespace firebase { +namespace firestore { +namespace model { + +namespace { + +/** + * True if the string could be used as a segment in a field path without + * escaping. Valid identifies follow the regex [a-zA-Z_][a-zA-Z0-9_]* + */ +bool IsValidIdentifier(const std::string& segment) { + if (segment.empty()) { + return false; + } + + // Note: strictly speaking, only digits are guaranteed by the Standard to + // be a contiguous range, while alphabetic characters may have gaps. Ignoring + // this peculiarity, because it doesn't affect the platforms that Firestore + // supports. + const unsigned char first = segment.front(); + if (first != '_' && (first < 'a' || first > 'z') && + (first < 'A' || first > 'Z')) { + return false; + } + for (size_t i = 1; i != segment.size(); ++i) { + const unsigned char c = segment[i]; + if (c != '_' && (c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && + (c < '0' || c > '9')) { + return false; + } + } + + return true; +} + +} // namespace + +FieldPath FieldPath::FromServerFormat(const absl::string_view path) { + // TODO(b/37244157): Once we move to v1beta1, we should make this more + // strict. Right now, it allows non-identifier path components, even if they + // aren't escaped. Technically, this will mangle paths with backticks in + // them used in v1alpha1, but that's fine. + + SegmentsT segments; + std::string segment; + segment.reserve(path.size()); + + // string_view doesn't have a c_str() method, because it might not be + // null-terminated. Assertions expect C strings, so construct std::string on + // the fly, so that c_str() might be called on it. + const auto to_string = [](const absl::string_view view) { + return std::string{view.data(), view.data() + view.size()}; + }; + const auto finish_segment = [&segments, &segment, &path, &to_string] { + FIREBASE_ASSERT_MESSAGE( + !segment.empty(), + "Invalid field path (%s). Paths must not be empty, begin with " + "'.', end with '.', or contain '..'", + to_string(path).c_str()); + // Move operation will clear segment, but capacity will remain the same + // (not, strictly speaking, required by the standard, but true in practice). + segments.push_back(std::move(segment)); + }; + + // Inside backticks, dots are treated literally. + bool inside_backticks = false; + size_t i = 0; + while (i < path.size()) { + const char c = path[i]; + // std::string (and string_view) may contain embedded nulls. For full + // compatibility with Objective C behavior, finish upon encountering the + // first terminating null. + if (c == '\0') { + break; + } + + switch (c) { + case '.': + if (!inside_backticks) { + finish_segment(); + } else { + segment += c; + } + break; + + case '`': + inside_backticks = !inside_backticks; + break; + + case '\\': + // TODO(b/37244157): Make this a user-facing exception once we + // finalize field escaping. + FIREBASE_ASSERT_MESSAGE(i + 1 != path.size(), + "Trailing escape characters not allowed in %s", + to_string(path).c_str()); + ++i; + segment += path[i]; + break; + + default: + segment += c; + break; + } + ++i; + } + finish_segment(); + + FIREBASE_ASSERT_MESSAGE(!inside_backticks, "Unterminated ` in path %s", + to_string(path).c_str()); + + return FieldPath{std::move(segments)}; +} + +const FieldPath& FieldPath::EmptyPath() { + static const FieldPath empty_path; + return empty_path; +} + +const FieldPath& FieldPath::KeyFieldPath() { + static const FieldPath key_field_path{FieldPath::kDocumentKeyPath}; + return key_field_path; +} + +bool FieldPath::IsKeyFieldPath() const { + return size() == 1 && first_segment() == FieldPath::kDocumentKeyPath; +} + +std::string FieldPath::CanonicalString() const { + const auto escaped_segment = [](const std::string& segment) { + auto escaped = absl::StrReplaceAll(segment, {{"\\", "\\\\"}, {"`", "\\`"}}); + const bool needs_escaping = !IsValidIdentifier(escaped); + if (needs_escaping) { + escaped.insert(escaped.begin(), '`'); + escaped.push_back('`'); + } + return escaped; + }; + return absl::StrJoin( + begin(), end(), ".", + [escaped_segment](std::string* out, const std::string& segment) { + out->append(escaped_segment(segment)); + }); +} + +} // namespace model +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/model/field_path.h b/Firestore/core/src/firebase/firestore/model/field_path.h new file mode 100644 index 00000000000..fdf49181d92 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/model/field_path.h @@ -0,0 +1,100 @@ +/* + * Copyright 2018 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. + */ + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_FIELD_PATH_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_FIELD_PATH_H_ + +#include +#include +#include + +#include "Firestore/core/src/firebase/firestore/model/base_path.h" +#include "absl/strings/string_view.h" + +namespace firebase { +namespace firestore { +namespace model { + +/** + * A dot-separated path for navigating sub-objects within a document. + * + * Immutable; all instances are fully independent. + */ +class FieldPath : public impl::BasePath { + public: + /** The field path string that represents the document's key. */ + static constexpr const char* kDocumentKeyPath = "__name__"; + + // Note: Xcode 8.2 requires explicit specification of the constructor. + FieldPath() : impl::BasePath() { + } + + /** Constructs the path from segments. */ + template + FieldPath(const IterT begin, const IterT end) : BasePath{begin, end} { + } + FieldPath(std::initializer_list list) : BasePath{list} { + } + + /** + * Creates and returns a new path from the server formatted field-path string, + * where path segments are separated by a dot "." and optionally encoded using + * backticks. + */ + static FieldPath FromServerFormat(absl::string_view path); + /** Returns a field path that represents an empty path. */ + static const FieldPath& EmptyPath(); + /** Returns a field path that represents a document key. */ + static const FieldPath& KeyFieldPath(); + + /** Returns a standardized string representation of this path. */ + std::string CanonicalString() const; + /** True if this FieldPath represents a document key. */ + bool IsKeyFieldPath() const; + + bool operator==(const FieldPath& rhs) const { + return BasePath::operator==(rhs); + } + bool operator!=(const FieldPath& rhs) const { + return BasePath::operator!=(rhs); + } + bool operator<(const FieldPath& rhs) const { + return BasePath::operator<(rhs); + } + bool operator>(const FieldPath& rhs) const { + return BasePath::operator>(rhs); + } + bool operator<=(const FieldPath& rhs) const { + return BasePath::operator<=(rhs); + } + bool operator>=(const FieldPath& rhs) const { + return BasePath::operator>=(rhs); + } + + private: + explicit FieldPath(SegmentsT&& segments) : BasePath{std::move(segments)} { + } + + // So that methods of base can construct FieldPath using the private + // constructor. + friend class BasePath; +}; + +} // namespace model +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_FIELD_PATH_H_ diff --git a/Firestore/core/src/firebase/firestore/model/field_value.cc b/Firestore/core/src/firebase/firestore/model/field_value.cc new file mode 100644 index 00000000000..03cf1d4a8a2 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/model/field_value.cc @@ -0,0 +1,429 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/core/src/firebase/firestore/model/field_value.h" + +#include + +#include +#include +#include +#include + +#include "Firestore/core/src/firebase/firestore/util/comparison.h" +#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" + +using firebase::firestore::util::Comparator; + +namespace firebase { +namespace firestore { +namespace model { + +using Type = FieldValue::Type; +using firebase::firestore::util::ComparisonResult; + +namespace { +/** + * This deviates from the other platforms that define TypeOrder. Since + * we already define Type for union types, we use it together with this + * function to achive the equivalent order of types i.e. + * i) if two types are comparable, then they are of equal order; + * ii) otherwise, their order is the same as the order of their Type. + */ +bool Comparable(Type lhs, Type rhs) { + switch (lhs) { + case Type::Integer: + case Type::Double: + return rhs == Type::Integer || rhs == Type::Double; + case Type::Timestamp: + case Type::ServerTimestamp: + return rhs == Type::Timestamp || rhs == Type::ServerTimestamp; + default: + return lhs == rhs; + } +} + +} // namespace + +FieldValue::FieldValue(const FieldValue& value) { + *this = value; +} + +FieldValue::FieldValue(FieldValue&& value) { + *this = std::move(value); +} + +FieldValue::~FieldValue() { + SwitchTo(Type::Null); +} + +FieldValue& FieldValue::operator=(const FieldValue& value) { + SwitchTo(value.tag_); + switch (tag_) { + case Type::Null: + break; + case Type::Boolean: + boolean_value_ = value.boolean_value_; + break; + case Type::Integer: + integer_value_ = value.integer_value_; + break; + case Type::Double: + double_value_ = value.double_value_; + break; + case Type::Timestamp: + timestamp_value_ = value.timestamp_value_; + break; + case Type::ServerTimestamp: + server_timestamp_value_ = value.server_timestamp_value_; + break; + case Type::String: + string_value_ = value.string_value_; + break; + case Type::Blob: { + // copy-and-swap + std::vector tmp = value.blob_value_; + std::swap(blob_value_, tmp); + break; + } + case Type::Reference: + reference_value_ = value.reference_value_; + break; + case Type::GeoPoint: + geo_point_value_ = value.geo_point_value_; + break; + case Type::Array: { + // copy-and-swap + std::vector tmp = value.array_value_; + std::swap(array_value_, tmp); + break; + } + case Type::Object: { + // copy-and-swap + std::map tmp = value.object_value_; + std::swap(object_value_, tmp); + break; + } + default: + FIREBASE_ASSERT_MESSAGE_WITH_EXPRESSION( + false, lhs.type(), "Unsupported type %d", value.type()); + } + return *this; +} + +FieldValue& FieldValue::operator=(FieldValue&& value) { + switch (value.tag_) { + case Type::String: + SwitchTo(Type::String); + string_value_.swap(value.string_value_); + return *this; + case Type::Blob: + SwitchTo(Type::Blob); + std::swap(blob_value_, value.blob_value_); + return *this; + case Type::Reference: + SwitchTo(Type::Reference); + std::swap(reference_value_.reference, value.reference_value_.reference); + reference_value_.database_id = value.reference_value_.database_id; + return *this; + case Type::Array: + SwitchTo(Type::Array); + std::swap(array_value_, value.array_value_); + return *this; + case Type::Object: + SwitchTo(Type::Object); + std::swap(object_value_, value.object_value_); + return *this; + default: + // We just copy over POD union types. + return *this = value; + } +} + +const FieldValue& FieldValue::NullValue() { + static const FieldValue kNullInstance; + return kNullInstance; +} + +const FieldValue& FieldValue::TrueValue() { + static const FieldValue kTrueInstance(true); + return kTrueInstance; +} + +const FieldValue& FieldValue::FalseValue() { + static const FieldValue kFalseInstance(false); + return kFalseInstance; +} + +const FieldValue& FieldValue::BooleanValue(bool value) { + return value ? TrueValue() : FalseValue(); +} + +const FieldValue& FieldValue::NanValue() { + static const FieldValue kNanInstance = FieldValue::DoubleValue(NAN); + return kNanInstance; +} + +FieldValue FieldValue::IntegerValue(int64_t value) { + FieldValue result; + result.SwitchTo(Type::Integer); + result.integer_value_ = value; + return result; +} + +FieldValue FieldValue::DoubleValue(double value) { + FieldValue result; + result.SwitchTo(Type::Double); + result.double_value_ = value; + return result; +} + +FieldValue FieldValue::TimestampValue(const Timestamp& value) { + FieldValue result; + result.SwitchTo(Type::Timestamp); + result.timestamp_value_ = value; + return result; +} + +FieldValue FieldValue::ServerTimestampValue(const Timestamp& local_write_time, + const Timestamp& previous_value) { + FieldValue result; + result.SwitchTo(Type::ServerTimestamp); + result.server_timestamp_value_.local_write_time = local_write_time; + result.server_timestamp_value_.previous_value = previous_value; + result.server_timestamp_value_.has_previous_value_ = true; + return result; +} + +FieldValue FieldValue::ServerTimestampValue(const Timestamp& local_write_time) { + FieldValue result; + result.SwitchTo(Type::ServerTimestamp); + result.server_timestamp_value_.local_write_time = local_write_time; + result.server_timestamp_value_.has_previous_value_ = false; + return result; +} + +FieldValue FieldValue::StringValue(const char* value) { + std::string copy(value); + return StringValue(std::move(copy)); +} + +FieldValue FieldValue::StringValue(const std::string& value) { + std::string copy(value); + return StringValue(std::move(copy)); +} + +FieldValue FieldValue::StringValue(std::string&& value) { + FieldValue result; + result.SwitchTo(Type::String); + result.string_value_.swap(value); + return result; +} + +FieldValue FieldValue::BlobValue(const uint8_t* source, size_t size) { + FieldValue result; + result.SwitchTo(Type::Blob); + std::vector copy(source, source + size); + std::swap(result.blob_value_, copy); + return result; +} + +// Does NOT pass ownership of database_id. +FieldValue FieldValue::ReferenceValue(const DocumentKey& value, + const DatabaseId* database_id) { + FieldValue result; + result.SwitchTo(Type::Reference); + result.reference_value_.reference = value; + result.reference_value_.database_id = database_id; + return result; +} + +// Does NOT pass ownership of database_id. +FieldValue FieldValue::ReferenceValue(DocumentKey&& value, + const DatabaseId* database_id) { + FieldValue result; + result.SwitchTo(Type::Reference); + std::swap(result.reference_value_.reference, value); + result.reference_value_.database_id = database_id; + return result; +} + +FieldValue FieldValue::GeoPointValue(const GeoPoint& value) { + FieldValue result; + result.SwitchTo(Type::GeoPoint); + result.geo_point_value_ = value; + return result; +} + +FieldValue FieldValue::ArrayValue(const std::vector& value) { + std::vector copy(value); + return ArrayValue(std::move(copy)); +} + +FieldValue FieldValue::ArrayValue(std::vector&& value) { + FieldValue result; + result.SwitchTo(Type::Array); + std::swap(result.array_value_, value); + return result; +} + +FieldValue FieldValue::ObjectValue( + const std::map& value) { + std::map copy(value); + return ObjectValue(std::move(copy)); +} + +FieldValue FieldValue::ObjectValue( + std::map&& value) { + FieldValue result; + result.SwitchTo(Type::Object); + std::swap(result.object_value_, value); + return result; +} + +bool operator<(const FieldValue& lhs, const FieldValue& rhs) { + if (!Comparable(lhs.type(), rhs.type())) { + return lhs.type() < rhs.type(); + } + + switch (lhs.type()) { + case Type::Null: + return false; + case Type::Boolean: + return Comparator()(lhs.boolean_value_, rhs.boolean_value_); + case Type::Integer: + if (rhs.type() == Type::Integer) { + return Comparator()(lhs.integer_value_, rhs.integer_value_); + } else { + return util::CompareMixedNumber(rhs.double_value_, + lhs.integer_value_) == + ComparisonResult::Descending; + } + case Type::Double: + if (rhs.type() == Type::Double) { + return Comparator()(lhs.double_value_, rhs.double_value_); + } else { + return util::CompareMixedNumber(lhs.double_value_, + rhs.integer_value_) == + ComparisonResult::Ascending; + } + case Type::Timestamp: + if (rhs.type() == Type::Timestamp) { + return lhs.timestamp_value_ < rhs.timestamp_value_; + } else { + return true; + } + case Type::ServerTimestamp: + if (rhs.type() == Type::ServerTimestamp) { + return lhs.server_timestamp_value_.local_write_time < + rhs.server_timestamp_value_.local_write_time; + } else { + return false; + } + case Type::String: + return lhs.string_value_.compare(rhs.string_value_) < 0; + case Type::Blob: + return lhs.blob_value_ < rhs.blob_value_; + case Type::Reference: + return *lhs.reference_value_.database_id < + *rhs.reference_value_.database_id || + (*lhs.reference_value_.database_id == + *rhs.reference_value_.database_id && + lhs.reference_value_.reference < rhs.reference_value_.reference); + case Type::GeoPoint: + return lhs.geo_point_value_ < rhs.geo_point_value_; + case Type::Array: + return lhs.array_value_ < rhs.array_value_; + case Type::Object: + return lhs.object_value_ < rhs.object_value_; + default: + FIREBASE_ASSERT_MESSAGE_WITH_EXPRESSION( + false, lhs.type(), "Unsupported type %d", lhs.type()); + // return false if assertion does not abort the program. We will say + // each unsupported type takes only one value thus everything is equal. + return false; + } +} + +void FieldValue::SwitchTo(const Type type) { + if (tag_ == type) { + return; + } + // Not same type. Destruct old type first and then initialize new type. + // Must call destructor explicitly for any non-POD type. + switch (tag_) { + case Type::Timestamp: + timestamp_value_.~Timestamp(); + break; + case Type::ServerTimestamp: + server_timestamp_value_.~ServerTimestamp(); + break; + case Type::String: + string_value_.~basic_string(); + break; + case Type::Blob: + blob_value_.~vector(); + break; + case Type::Reference: + reference_value_.~ReferenceValue(); + break; + case Type::GeoPoint: + geo_point_value_.~GeoPoint(); + break; + case Type::Array: + array_value_.~vector(); + break; + case Type::Object: + object_value_.~map(); + break; + default: {} // The other types where there is nothing to worry about. + } + tag_ = type; + // Must call constructor explicitly for any non-POD type to initialize. + switch (tag_) { + case Type::Timestamp: + new (×tamp_value_) Timestamp(0, 0); + break; + case Type::ServerTimestamp: + new (&server_timestamp_value_) ServerTimestamp(); + break; + case Type::String: + new (&string_value_) std::string(); + break; + case Type::Blob: + // Do not even bother to allocate a new array of size 0. + new (&blob_value_) std::vector(); + break; + case Type::Reference: + // Qualified name to avoid conflict with the member function of same name. + new (&reference_value_) firebase::firestore::model::ReferenceValue(); + break; + case Type::GeoPoint: + new (&geo_point_value_) GeoPoint(); + break; + case Type::Array: + new (&array_value_) std::vector(); + break; + case Type::Object: + new (&object_value_) std::map(); + break; + default: {} // The other types where there is nothing to worry about. + } +} + +} // namespace model +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/model/field_value.h b/Firestore/core/src/firebase/firestore/model/field_value.h new file mode 100644 index 00000000000..72b9481ee10 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/model/field_value.h @@ -0,0 +1,193 @@ +/* + * Copyright 2018 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. + */ + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_FIELD_VALUE_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_FIELD_VALUE_H_ + +#include + +#include +#include +#include +#include + +#include "Firestore/core/include/firebase/firestore/geo_point.h" +#include "Firestore/core/src/firebase/firestore/model/database_id.h" +#include "Firestore/core/src/firebase/firestore/model/document_key.h" +#include "Firestore/core/src/firebase/firestore/model/timestamp.h" +#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" + +namespace firebase { +namespace firestore { +namespace model { + +struct ServerTimestamp { + Timestamp local_write_time; + Timestamp previous_value; + // TODO(zxu123): adopt absl::optional once abseil is ported. + bool has_previous_value_; +}; + +struct ReferenceValue { + DocumentKey reference; + // Does not own the DatabaseId instance. + const DatabaseId* database_id; +}; + +/** + * tagged-union class representing an immutable data value as stored in + * Firestore. FieldValue represents all the different kinds of values + * that can be stored in fields in a document. + */ +class FieldValue { + public: + /** + * All the different kinds of values that can be stored in fields in + * a document. The types of the same comparison order should be defined + * together as a group. The order of each group is defined by the Firestore + * backend and is available at: + * https://firebase.google.com/docs/firestore/manage-data/data-types + */ + enum class Type { + Null, // Null + Boolean, // Boolean + Integer, // Number type starts here + Double, + Timestamp, // Timestamp type starts here + ServerTimestamp, + String, // String + Blob, // Blob + Reference, // Reference + GeoPoint, // GeoPoint + Array, // Array + Object, // Object + // New enum should not always been added at the tail. Add it to the correct + // position instead, see the doc comment above. + }; + + FieldValue() { + } + + // Do not inline these ctor/dtor below, which contain call to non-trivial + // operator=. + FieldValue(const FieldValue& value); + FieldValue(FieldValue&& value); + + ~FieldValue(); + + FieldValue& operator=(const FieldValue& value); + FieldValue& operator=(FieldValue&& value); + + /** Returns the true type for this value. */ + Type type() const { + return tag_; + } + + bool boolean_value() const { + FIREBASE_ASSERT(tag_ == Type::Boolean); + return boolean_value_; + } + + int64_t integer_value() const { + FIREBASE_ASSERT(tag_ == Type::Integer); + return integer_value_; + } + + /** factory methods. */ + static const FieldValue& NullValue(); + static const FieldValue& TrueValue(); + static const FieldValue& FalseValue(); + static const FieldValue& BooleanValue(bool value); + static const FieldValue& NanValue(); + static FieldValue IntegerValue(int64_t value); + static FieldValue DoubleValue(double value); + static FieldValue TimestampValue(const Timestamp& value); + static FieldValue ServerTimestampValue(const Timestamp& local_write_time, + const Timestamp& previous_value); + static FieldValue ServerTimestampValue(const Timestamp& local_write_time); + static FieldValue StringValue(const char* value); + static FieldValue StringValue(const std::string& value); + static FieldValue StringValue(std::string&& value); + static FieldValue BlobValue(const uint8_t* source, size_t size); + static FieldValue ReferenceValue(const DocumentKey& value, + const DatabaseId* database_id); + static FieldValue ReferenceValue(DocumentKey&& value, + const DatabaseId* database_id); + static FieldValue GeoPointValue(const GeoPoint& value); + static FieldValue ArrayValue(const std::vector& value); + static FieldValue ArrayValue(std::vector&& value); + static FieldValue ObjectValue( + const std::map& value); + static FieldValue ObjectValue( + std::map&& value); + + friend bool operator<(const FieldValue& lhs, const FieldValue& rhs); + + private: + explicit FieldValue(bool value) : tag_(Type::Boolean), boolean_value_(value) { + } + + /** + * Switch to the specified type, if different from the current type. + */ + void SwitchTo(const Type type); + + Type tag_ = Type::Null; + union { + // There is no null type as tag_ alone is enough for Null FieldValue. + bool boolean_value_; + int64_t integer_value_; + double double_value_; + Timestamp timestamp_value_; + ServerTimestamp server_timestamp_value_; + std::string string_value_; + std::vector blob_value_; + // Qualified name to avoid conflict with the member function of same name. + firebase::firestore::model::ReferenceValue reference_value_; + GeoPoint geo_point_value_; + std::vector array_value_; + std::map object_value_; + }; +}; + +/** Compares against another FieldValue. */ +bool operator<(const FieldValue& lhs, const FieldValue& rhs); + +inline bool operator>(const FieldValue& lhs, const FieldValue& rhs) { + return rhs < lhs; +} + +inline bool operator>=(const FieldValue& lhs, const FieldValue& rhs) { + return !(lhs < rhs); +} + +inline bool operator<=(const FieldValue& lhs, const FieldValue& rhs) { + return !(lhs > rhs); +} + +inline bool operator!=(const FieldValue& lhs, const FieldValue& rhs) { + return lhs < rhs || lhs > rhs; +} + +inline bool operator==(const FieldValue& lhs, const FieldValue& rhs) { + return !(lhs != rhs); +} + +} // namespace model +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_FIELD_VALUE_H_ diff --git a/Firestore/core/src/firebase/firestore/model/maybe_document.cc b/Firestore/core/src/firebase/firestore/model/maybe_document.cc new file mode 100644 index 00000000000..4f3be1daeaa --- /dev/null +++ b/Firestore/core/src/firebase/firestore/model/maybe_document.cc @@ -0,0 +1,38 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/core/src/firebase/firestore/model/maybe_document.h" + +#include + +#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" + +namespace firebase { +namespace firestore { +namespace model { + +MaybeDocument::MaybeDocument(DocumentKey key, SnapshotVersion version) + : key_(std::move(key)), version_(std::move(version)) { +} + +bool MaybeDocument::Equals(const MaybeDocument& other) const { + return type_ == other.type_ && version_ == other.version_ && + key_ == other.key_; +} + +} // namespace model +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/model/maybe_document.h b/Firestore/core/src/firebase/firestore/model/maybe_document.h new file mode 100644 index 00000000000..71bd3ef0780 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/model/maybe_document.h @@ -0,0 +1,100 @@ +/* + * Copyright 2018 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. + */ + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_MAYBE_DOCUMENT_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_MAYBE_DOCUMENT_H_ + +#include + +#include "Firestore/core/src/firebase/firestore/model/document_key.h" +#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h" + +namespace firebase { +namespace firestore { +namespace model { + +/** + * The result of a lookup for a given path may be an existing document or a + * tombstone that marks the path deleted. + */ +class MaybeDocument { + public: + /** + * All the different kinds of documents, including MaybeDocument and its + * subclasses. This is used to provide RTTI for documents. + */ + enum class Type { + Unknown, + Document, + NoDocument, + }; + + MaybeDocument(DocumentKey key, SnapshotVersion version); + + /** The runtime type of this document. */ + Type type() const { + return type_; + } + + /** The key for this document. */ + const DocumentKey& key() const { + return key_; + } + + /** + * Returns the version of this document if it exists or a version at which + * this document was guaranteed to not exist. + */ + const SnapshotVersion& version() const { + return version_; + } + + protected: + // Only allow subclass to set their types. + void set_type(Type type) { + type_ = type; + } + + virtual bool Equals(const MaybeDocument& other) const; + + friend bool operator==(const MaybeDocument& lhs, const MaybeDocument& rhs); + + private: + Type type_ = Type::Unknown; + DocumentKey key_; + SnapshotVersion version_; +}; + +inline bool operator==(const MaybeDocument& lhs, const MaybeDocument& rhs) { + return lhs.Equals(rhs); +} + +inline bool operator!=(const MaybeDocument& lhs, const MaybeDocument& rhs) { + return !(lhs == rhs); +} + +/** Compares against another MaybeDocument by keys only. */ +struct DocumentKeyComparator : public std::less { + bool operator()(const MaybeDocument& lhs, const MaybeDocument& rhs) const { + return lhs.key() < rhs.key(); + } +}; + +} // namespace model +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_MAYBE_DOCUMENT_H_ diff --git a/Firestore/core/src/firebase/firestore/model/no_document.cc b/Firestore/core/src/firebase/firestore/model/no_document.cc new file mode 100644 index 00000000000..98cb428843b --- /dev/null +++ b/Firestore/core/src/firebase/firestore/model/no_document.cc @@ -0,0 +1,32 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/core/src/firebase/firestore/model/no_document.h" + +#include + +namespace firebase { +namespace firestore { +namespace model { + +NoDocument::NoDocument(DocumentKey key, SnapshotVersion version) + : MaybeDocument(std::move(key), std::move(version)) { + set_type(Type::NoDocument); +} + +} // namespace model +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/model/no_document.h b/Firestore/core/src/firebase/firestore/model/no_document.h new file mode 100644 index 00000000000..7cfd47c66cc --- /dev/null +++ b/Firestore/core/src/firebase/firestore/model/no_document.h @@ -0,0 +1,36 @@ +/* + * Copyright 2018 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. + */ + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_NO_DOCUMENT_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_NO_DOCUMENT_H_ + +#include "Firestore/core/src/firebase/firestore/model/maybe_document.h" + +namespace firebase { +namespace firestore { +namespace model { + +/** Represents that no documents exists for the key at the given version. */ +class NoDocument : public MaybeDocument { + public: + NoDocument(DocumentKey key, SnapshotVersion version); +}; + +} // namespace model +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_NO_DOCUMENT_H_ diff --git a/Firestore/core/src/firebase/firestore/model/resource_path.cc b/Firestore/core/src/firebase/firestore/model/resource_path.cc new file mode 100644 index 00000000000..c95aa63c578 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/model/resource_path.cc @@ -0,0 +1,58 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/core/src/firebase/firestore/model/resource_path.h" + +#include +#include +#include + +#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" +#include "absl/strings/str_join.h" +#include "absl/strings/str_split.h" + +namespace firebase { +namespace firestore { +namespace model { + +ResourcePath ResourcePath::FromString(const absl::string_view path) { + // NOTE: The client is ignorant of any path segments containing escape + // sequences (e.g. __id123__) and just passes them through raw (they exist + // for legacy reasons and should not be used frequently). + + FIREBASE_ASSERT_MESSAGE( + path.find("//") == std::string::npos, + "Invalid path (%s). Paths must not contain // in them.", + std::string{path.data(), path.data() + path.size()}.c_str()); + + // SkipEmpty because we may still have an empty segment at the beginning or + // end if they had a leading or trailing slash (which we allow). + std::vector segments = + absl::StrSplit(path, '/', absl::SkipEmpty()); + return ResourcePath{std::move(segments)}; +} + +std::string ResourcePath::CanonicalString() const { + // NOTE: The client is ignorant of any path segments containing escape + // sequences (e.g. __id123__) and just passes them through raw (they exist + // for legacy reasons and should not be used frequently). + + return absl::StrJoin(begin(), end(), "/"); +} + +} // namespace model +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/model/resource_path.h b/Firestore/core/src/firebase/firestore/model/resource_path.h new file mode 100644 index 00000000000..53c195149c4 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/model/resource_path.h @@ -0,0 +1,85 @@ +/* + * Copyright 2018 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. + */ + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_RESOURCE_PATH_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_RESOURCE_PATH_H_ + +#include +#include +#include + +#include "Firestore/core/src/firebase/firestore/model/base_path.h" +#include "absl/strings/string_view.h" + +namespace firebase { +namespace firestore { +namespace model { + +/** + * A slash-separated path for navigating resources (documents and collections) + * within Firestore. Immutable; all instances are fully independent. + */ +class ResourcePath : public impl::BasePath { + public: + ResourcePath() = default; + /** Constructs the path from segments. */ + template + ResourcePath(const IterT begin, const IterT end) : BasePath{begin, end} { + } + ResourcePath(std::initializer_list list) : BasePath{list} { + } + /** + * Creates and returns a new path from the given resource-path string, where + * the path segments are separated by a slash "/". + */ + static ResourcePath FromString(absl::string_view path); + + /** Returns a standardized string representation of this path. */ + std::string CanonicalString() const; + + bool operator==(const ResourcePath& rhs) const { + return BasePath::operator==(rhs); + } + bool operator!=(const ResourcePath& rhs) const { + return BasePath::operator!=(rhs); + } + bool operator<(const ResourcePath& rhs) const { + return BasePath::operator<(rhs); + } + bool operator>(const ResourcePath& rhs) const { + return BasePath::operator>(rhs); + } + bool operator<=(const ResourcePath& rhs) const { + return BasePath::operator<=(rhs); + } + bool operator>=(const ResourcePath& rhs) const { + return BasePath::operator>=(rhs); + } + + private: + explicit ResourcePath(SegmentsT&& segments) : BasePath{std::move(segments)} { + } + + // So that methods of base can construct ResourcePath using the private + // constructor. + friend class BasePath; +}; + +} // namespace model +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_RESOURCE_PATH_H_ diff --git a/Firestore/core/src/firebase/firestore/model/snapshot_version.cc b/Firestore/core/src/firebase/firestore/model/snapshot_version.cc new file mode 100644 index 00000000000..114e313f4cc --- /dev/null +++ b/Firestore/core/src/firebase/firestore/model/snapshot_version.cc @@ -0,0 +1,34 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h" + +namespace firebase { +namespace firestore { +namespace model { + +SnapshotVersion::SnapshotVersion(const Timestamp& timestamp) + : timestamp_(timestamp) { +} + +const SnapshotVersion& SnapshotVersion::None() { + static const SnapshotVersion kNone(Timestamp{}); + return kNone; +} + +} // namespace model +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/model/snapshot_version.h b/Firestore/core/src/firebase/firestore/model/snapshot_version.h new file mode 100644 index 00000000000..70f6f4a12ec --- /dev/null +++ b/Firestore/core/src/firebase/firestore/model/snapshot_version.h @@ -0,0 +1,74 @@ +/* + * Copyright 2018 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. + */ + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_SNAPSHOT_VERSION_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_SNAPSHOT_VERSION_H_ + +#include "Firestore/core/src/firebase/firestore/model/timestamp.h" + +namespace firebase { +namespace firestore { +namespace model { + +/** + * A version of a document in Firestore. This corresponds to the version + * timestamp, such as update_time or read_time. + */ +class SnapshotVersion { + public: + explicit SnapshotVersion(const Timestamp& timestamp); + + const Timestamp& timestamp() const { + return timestamp_; + } + + /** Creates a new version that is smaller than all other versions. */ + static const SnapshotVersion& None(); + + private: + Timestamp timestamp_; +}; + +/** Compares against another SnapshotVersion. */ +inline bool operator<(const SnapshotVersion& lhs, const SnapshotVersion& rhs) { + return lhs.timestamp() < rhs.timestamp(); +} + +inline bool operator>(const SnapshotVersion& lhs, const SnapshotVersion& rhs) { + return lhs.timestamp() > rhs.timestamp(); +} + +inline bool operator>=(const SnapshotVersion& lhs, const SnapshotVersion& rhs) { + return lhs.timestamp() >= rhs.timestamp(); +} + +inline bool operator<=(const SnapshotVersion& lhs, const SnapshotVersion& rhs) { + return lhs.timestamp() <= rhs.timestamp(); +} + +inline bool operator!=(const SnapshotVersion& lhs, const SnapshotVersion& rhs) { + return lhs.timestamp() != rhs.timestamp(); +} + +inline bool operator==(const SnapshotVersion& lhs, const SnapshotVersion& rhs) { + return lhs.timestamp() == rhs.timestamp(); +} + +} // namespace model +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_SNAPSHOT_VERSION_H_ diff --git a/Firestore/core/src/firebase/firestore/model/timestamp.cc b/Firestore/core/src/firebase/firestore/model/timestamp.cc new file mode 100644 index 00000000000..b3d1597b2ca --- /dev/null +++ b/Firestore/core/src/firebase/firestore/model/timestamp.cc @@ -0,0 +1,54 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/core/src/firebase/firestore/model/timestamp.h" + +#include + +#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" + +namespace firebase { +namespace firestore { +namespace model { + +Timestamp::Timestamp(int64_t seconds, int32_t nanos) + : seconds_(seconds), nanos_(nanos) { + FIREBASE_ASSERT_MESSAGE_WITH_EXPRESSION( + nanos >= 0, nanos >= 0, "timestamp nanoseconds out of range: %d", nanos); + FIREBASE_ASSERT_MESSAGE_WITH_EXPRESSION( + nanos < 1e9, nanos < 1e9, "timestamp nanoseconds out of range: %d", + nanos); + // Midnight at the beginning of 1/1/1 is the earliest timestamp Firestore + // supports. + FIREBASE_ASSERT_MESSAGE_WITH_EXPRESSION( + seconds >= -62135596800L, seconds >= -62135596800L, + "timestamp seconds out of range: %lld", seconds); + // This will break in the year 10,000. + FIREBASE_ASSERT_MESSAGE_WITH_EXPRESSION( + seconds < 253402300800L, seconds < 253402300800L, + "timestamp seconds out of range: %lld", seconds); +} + +Timestamp::Timestamp() : seconds_(0), nanos_(0) { +} + +Timestamp Timestamp::Now() { + return Timestamp(time(nullptr), 0); +} + +} // namespace model +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/model/timestamp.h b/Firestore/core/src/firebase/firestore/model/timestamp.h new file mode 100644 index 00000000000..dd0349c962b --- /dev/null +++ b/Firestore/core/src/firebase/firestore/model/timestamp.h @@ -0,0 +1,94 @@ +/* + * Copyright 2018 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. + */ + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_TIMESTAMP_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_TIMESTAMP_H_ + +#include + +namespace firebase { +namespace firestore { +namespace model { + +/** + * A Timestamp represents an absolute time from the backend at up to nanosecond + * precision. A Timestamp is always UTC. + */ +class Timestamp { + public: + /** + * Creates a new timestamp with seconds and nanos set to 0. + * + * PORTING NOTE: This does NOT set to current timestamp by default. To get the + * current timestamp, call Timestamp::Now(). + */ + Timestamp(); + + /** + * Creates a new timestamp. + * + * @param seconds the number of seconds since epoch. + * @param nanos the number of nanoseconds after the seconds. + */ + Timestamp(int64_t seconds, int32_t nanos); + + /** Returns a timestamp with the current date / time. */ + static Timestamp Now(); + + int64_t seconds() const { + return seconds_; + } + + int32_t nanos() const { + return nanos_; + } + + private: + int64_t seconds_; + int32_t nanos_; +}; + +/** Compares against another Timestamp. */ +inline bool operator<(const Timestamp& lhs, const Timestamp& rhs) { + return lhs.seconds() < rhs.seconds() || + (lhs.seconds() == rhs.seconds() && lhs.nanos() < rhs.nanos()); +} + +inline bool operator>(const Timestamp& lhs, const Timestamp& rhs) { + return rhs < lhs; +} + +inline bool operator>=(const Timestamp& lhs, const Timestamp& rhs) { + return !(lhs < rhs); +} + +inline bool operator<=(const Timestamp& lhs, const Timestamp& rhs) { + return !(lhs > rhs); +} + +inline bool operator!=(const Timestamp& lhs, const Timestamp& rhs) { + return lhs < rhs || lhs > rhs; +} + +inline bool operator==(const Timestamp& lhs, const Timestamp& rhs) { + return !(lhs != rhs); +} + +} // namespace model +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_TIMESTAMP_H_ diff --git a/Firestore/core/src/firebase/firestore/remote/CMakeLists.txt b/Firestore/core/src/firebase/firestore/remote/CMakeLists.txt new file mode 100644 index 00000000000..7f528fb3e72 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/remote/CMakeLists.txt @@ -0,0 +1,27 @@ +# Copyright 2018 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. + +cc_library( + firebase_firestore_remote + SOURCES + datastore.h + datastore.cc + serializer.h + serializer.cc + DEPENDS + firebase_firestore_model + firebase_firestore_protos_nanopb + grpc::grpc + nanopb +) diff --git a/Firestore/Source/Auth/FSTEmptyCredentialsProvider.h b/Firestore/core/src/firebase/firestore/remote/datastore.cc similarity index 64% rename from Firestore/Source/Auth/FSTEmptyCredentialsProvider.h rename to Firestore/core/src/firebase/firestore/remote/datastore.cc index f80536385aa..f8a8988f6e4 100644 --- a/Firestore/Source/Auth/FSTEmptyCredentialsProvider.h +++ b/Firestore/core/src/firebase/firestore/remote/datastore.cc @@ -1,5 +1,5 @@ /* - * Copyright 2017 Google + * Copyright 2018 Google * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,15 +14,18 @@ * limitations under the License. */ -#import +#include "Firestore/core/src/firebase/firestore/remote/datastore.h" -#import "Firestore/Source/Auth/FSTCredentialsProvider.h" +namespace firebase { +namespace firestore { +namespace remote { -NS_ASSUME_NONNULL_BEGIN +Datastore::Datastore() { +} -/** `FSTEmptyCredentialsProvider` always yields an empty token. */ -@interface FSTEmptyCredentialsProvider : NSObject +Datastore::~Datastore() { +} -@end - -NS_ASSUME_NONNULL_END +} // namespace remote +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/remote/datastore.h b/Firestore/core/src/firebase/firestore/remote/datastore.h new file mode 100644 index 00000000000..807b75f6d16 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/remote/datastore.h @@ -0,0 +1,40 @@ +/* + * Copyright 2018 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. + */ + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_REMOTE_DATASTORE_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_REMOTE_DATASTORE_H_ + +namespace firebase { +namespace firestore { +namespace remote { + +class Datastore { + public: + Datastore(); + ~Datastore(); + + Datastore(const Datastore& other) = delete; + Datastore(Datastore&& other) = delete; + + Datastore& operator=(const Datastore& other) = delete; + Datastore& operator=(Datastore&& other) = delete; +}; + +} // namespace remote +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_REMOTE_DATASTORE_H_ diff --git a/Firestore/core/src/firebase/firestore/remote/serializer.cc b/Firestore/core/src/firebase/firestore/remote/serializer.cc new file mode 100644 index 00000000000..79ed98d2b4e --- /dev/null +++ b/Firestore/core/src/firebase/firestore/remote/serializer.cc @@ -0,0 +1,183 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/core/src/firebase/firestore/remote/serializer.h" + +#include +#include + +namespace firebase { +namespace firestore { +namespace remote { + +namespace { + +/** + * Note that (despite the value parameter type) this works for bool, enum, + * int32, int64, uint32 and uint64 proto field types. + * + * Note: This is not expected to be called direclty, but rather only via the + * other Encode* methods (i.e. EncodeBool, EncodeLong, etc) + * + * @param value The value to encode, represented as a uint64_t. + */ +void EncodeVarint(pb_ostream_t* stream, uint64_t value) { + bool status = pb_encode_varint(stream, value); + if (!status) { + // TODO(rsgowman): figure out error handling + abort(); + } +} + +/** + * Note that (despite the return type) this works for bool, enum, int32, int64, + * uint32 and uint64 proto field types. + * + * Note: This is not expected to be called direclty, but rather only via the + * other Decode* methods (i.e. DecodeBool, DecodeLong, etc) + * + * @return The decoded varint as a uint64_t. + */ +uint64_t DecodeVarint(pb_istream_t* stream) { + uint64_t varint_value; + bool status = pb_decode_varint(stream, &varint_value); + if (!status) { + // TODO(rsgowman): figure out error handling + abort(); + } + return varint_value; +} + +void EncodeNull(pb_ostream_t* stream) { + return EncodeVarint(stream, google_protobuf_NullValue_NULL_VALUE); +} + +void DecodeNull(pb_istream_t* stream) { + uint64_t varint = DecodeVarint(stream); + if (varint != google_protobuf_NullValue_NULL_VALUE) { + // TODO(rsgowman): figure out error handling + abort(); + } +} + +void EncodeBool(pb_ostream_t* stream, bool bool_value) { + return EncodeVarint(stream, bool_value); +} + +bool DecodeBool(pb_istream_t* stream) { + uint64_t varint = DecodeVarint(stream); + switch (varint) { + case 0: + return false; + case 1: + return true; + default: + // TODO(rsgowman): figure out error handling + abort(); + } +} + +void EncodeInteger(pb_ostream_t* stream, int64_t integer_value) { + return EncodeVarint(stream, integer_value); +} + +int64_t DecodeInteger(pb_istream_t* stream) { + return DecodeVarint(stream); +} + +} // namespace + +using firebase::firestore::model::FieldValue; + +void Serializer::EncodeFieldValue(const FieldValue& field_value, + std::vector* out_bytes) { + // TODO(rsgowman): how large should the output buffer be? Do some + // investigation to see if we can get nanopb to tell us how much space it's + // going to need. + uint8_t buf[1024]; + pb_ostream_t stream = pb_ostream_from_buffer(buf, sizeof(buf)); + + // TODO(rsgowman): some refactoring is in order... but will wait until after a + // non-varint, non-fixed-size (i.e. string) type is present before doing so. + bool status = false; + switch (field_value.type()) { + case FieldValue::Type::Null: + status = pb_encode_tag(&stream, PB_WT_VARINT, + google_firestore_v1beta1_Value_null_value_tag); + if (!status) { + // TODO(rsgowman): figure out error handling + abort(); + } + EncodeNull(&stream); + break; + + case FieldValue::Type::Boolean: + status = pb_encode_tag(&stream, PB_WT_VARINT, + google_firestore_v1beta1_Value_boolean_value_tag); + if (!status) { + // TODO(rsgowman): figure out error handling + abort(); + } + EncodeBool(&stream, field_value.boolean_value()); + break; + + case FieldValue::Type::Integer: + status = pb_encode_tag(&stream, PB_WT_VARINT, + google_firestore_v1beta1_Value_integer_value_tag); + if (!status) { + // TODO(rsgowman): figure out error handling + abort(); + } + EncodeInteger(&stream, field_value.integer_value()); + break; + + default: + // TODO(rsgowman): implement the other types + abort(); + } + + out_bytes->insert(out_bytes->end(), buf, buf + stream.bytes_written); +} + +FieldValue Serializer::DecodeFieldValue(const uint8_t* bytes, size_t length) { + pb_istream_t stream = pb_istream_from_buffer(bytes, length); + pb_wire_type_t wire_type; + uint32_t tag; + bool eof; + bool status = pb_decode_tag(&stream, &wire_type, &tag, &eof); + if (!status || wire_type != PB_WT_VARINT) { + // TODO(rsgowman): figure out error handling + abort(); + } + + switch (tag) { + case google_firestore_v1beta1_Value_null_value_tag: + DecodeNull(&stream); + return FieldValue::NullValue(); + case google_firestore_v1beta1_Value_boolean_value_tag: + return FieldValue::BooleanValue(DecodeBool(&stream)); + case google_firestore_v1beta1_Value_integer_value_tag: + return FieldValue::IntegerValue(DecodeInteger(&stream)); + + default: + // TODO(rsgowman): figure out error handling + abort(); + } +} + +} // namespace remote +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/remote/serializer.h b/Firestore/core/src/firebase/firestore/remote/serializer.h new file mode 100644 index 00000000000..635ffc3af09 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/remote/serializer.h @@ -0,0 +1,107 @@ +/* + * Copyright 2018 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. + */ + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_REMOTE_SERIALIZER_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_REMOTE_SERIALIZER_H_ + +#include +#include +#include + +#include "Firestore/Protos/nanopb/google/firestore/v1beta1/document.pb.h" +#include "Firestore/core/src/firebase/firestore/model/field_value.h" +#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" + +namespace firebase { +namespace firestore { +namespace remote { + +/** + * @brief Converts internal model objects to their equivalent protocol buffer + * form, and protocol buffer objects to their equivalent bytes. + * + * Methods starting with "Encode" convert from a model object to a protocol + * buffer (or directly to bytes in cases where the proto uses a 'oneof', due to + * limitations in nanopb), and methods starting with "Decode" convert from a + * protocol buffer to a model object (or from bytes directly to a model + * objects.) + */ +// TODO(rsgowman): Original docs also has this: "Throws an exception if a +// protocol buffer is missing a critical field or has a value we can't +// interpret." Adjust for C++. +class Serializer { + public: + Serializer() { + } + // TODO(rsgowman): We eventually need the DatabaseId, but can't add it just + // yet since it's not used yet (which travis complains about). So for now, + // we'll create a parameterless ctor (above) that likely won't exist in the + // final version of this class. + ///** + // * @param database_id Must remain valid for the lifetime of this Serializer + // * object. + // */ + // explicit Serializer(const firebase::firestore::model::DatabaseId& + // database_id) + // : database_id_(database_id) { + //} + + /** + * Converts the FieldValue model passed into bytes. + * + * @param field_value the model to convert. + * @param[out] out_bytes A buffer to place the output. The bytes will be + * appended to this vector. + */ + // TODO(rsgowman): error handling, incl return code. + static void EncodeFieldValue( + const firebase::firestore::model::FieldValue& field_value, + std::vector* out_bytes); + + /** + * @brief Converts from bytes to the model FieldValue format. + * + * @param bytes The bytes to convert. It's assumed that exactly all of the + * bytes will be used by this conversion. + * @return The model equivalent of the bytes. + */ + // TODO(rsgowman): error handling. + static firebase::firestore::model::FieldValue DecodeFieldValue( + const uint8_t* bytes, size_t length); + + /** + * @brief Converts from bytes to the model FieldValue format. + * + * @param bytes The bytes to convert. It's assumed that exactly all of the + * bytes will be used by this conversion. + * @return The model equivalent of the bytes. + */ + // TODO(rsgowman): error handling. + static firebase::firestore::model::FieldValue DecodeFieldValue( + const std::vector& bytes) { + return DecodeFieldValue(bytes.data(), bytes.size()); + } + + private: + // TODO(rsgowman): We don't need the database_id_ yet (but will eventually). + // const firebase::firestore::model::DatabaseId& database_id_; +}; + +} // namespace remote +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_REMOTE_SERIALIZER_H_ diff --git a/Firestore/core/src/firebase/firestore/util/CMakeLists.txt b/Firestore/core/src/firebase/firestore/util/CMakeLists.txt index 3028a956e8b..3e32111a2f6 100644 --- a/Firestore/core/src/firebase/firestore/util/CMakeLists.txt +++ b/Firestore/core/src/firebase/firestore/util/CMakeLists.txt @@ -16,69 +16,112 @@ # libraries in here are an implementation detail of making this a # mutli-platform build. -add_library( - firebase_firestore_util_base - secure_random_arc4random.cc - string_printf.cc -) -target_link_libraries( +include(CheckSymbolExists) +include(CheckIncludeFiles) + +cc_library( firebase_firestore_util_base - PUBLIC - absl_base + SOURCES + string_printf.cc + string_printf.h + DEPENDS + absl_base ) -# stdio-dependent bits can be built and tested everywhere -add_library( - firebase_firestore_util_stdio - assert_stdio.cc - log_stdio.cc -) -target_link_libraries( +## assert and log + +cc_library( firebase_firestore_util_stdio - PUBLIC - firebase_firestore_util_base + SOURCES + assert_stdio.cc + log_stdio.cc + DEPENDS + firebase_firestore_util_base + absl_base + EXCLUDE_FROM_ALL ) -# apple-dependent bits can only built and tested on apple plaforms -if(APPLE) - add_library( - firebase_firestore_util_apple +cc_library( + firebase_firestore_util_apple + SOURCES assert_apple.mm log_apple.mm - ) - target_compile_options( - firebase_firestore_util_apple - PRIVATE - ${OBJC_FLAGS} - ) - target_link_libraries( - firebase_firestore_util_apple - PUBLIC + string_apple.h + DEPENDS FirebaseCore - ) -endif(APPLE) - -add_library( - firebase_firestore_util - autoid.cc + absl_strings + EXCLUDE_FROM_ALL ) # Export a dependency on the correct logging library for this platform. All # buildable libraries are built and tested but only the best fit is exported. if(APPLE) - target_link_libraries( - firebase_firestore_util - PUBLIC - firebase_firestore_util_apple - firebase_firestore_util_base - ) + list(APPEND UTIL_DEPENDS firebase_firestore_util_apple) +else() + list(APPEND UTIL_DEPENDS firebase_firestore_util_stdio) +endif() -else(NOT APPLE) - target_link_libraries( - firebase_firestore_util - PUBLIC - firebase_firestore_util_stdio - firebase_firestore_util_base - ) -endif(APPLE) +## secure_random + +check_symbol_exists(arc4random stdlib.h HAVE_ARC4RANDOM) +cc_library( + firebase_firestore_util_arc4random + SOURCES + secure_random_arc4random.cc +) + +get_target_property( + CMAKE_REQUIRED_INCLUDES + OpenSSL::Crypto INTERFACE_INCLUDE_DIRECTORIES +) +check_include_files(openssl/rand.h HAVE_OPENSSL_RAND_H) +cc_library( + firebase_firestore_util_openssl + SOURCES + secure_random_openssl.cc + DEPENDS + OpenSSL::Crypto +) + +if(HAVE_ARC4RANDOM) + list(APPEND UTIL_DEPENDS firebase_firestore_util_arc4random) + +elseif(HAVE_OPENSSL_RAND_H) + list(APPEND UTIL_DEPENDS firebase_firestore_util_openssl) + +else() + message(FATAL_ERROR "No implementation for SecureRandom available.") + +endif() + + +## main library +configure_file( + config.h.in + config.h +) + +cc_library( + firebase_firestore_util + SOURCES + autoid.cc + autoid.h + bits.cc + bits.h + comparison.cc + comparison.h + config.h + firebase_assert.h + iterator_adaptors.h + log.h + ordered_code.cc + ordered_code.h + secure_random.h + string_util.cc + string_util.h + DEPENDS + ${UTIL_DEPENDS} + firebase_firestore_util_base + absl_base +) diff --git a/Firestore/core/src/firebase/firestore/util/assert_stdio.cc b/Firestore/core/src/firebase/firestore/util/assert_stdio.cc index 5476e6522fd..1d2e3332b71 100644 --- a/Firestore/core/src/firebase/firestore/util/assert_stdio.cc +++ b/Firestore/core/src/firebase/firestore/util/assert_stdio.cc @@ -14,16 +14,14 @@ * limitations under the License. */ -#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" - #include -#include +#include #include -#include - +#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" #include "Firestore/core/src/firebase/firestore/util/string_printf.h" +#include "absl/base/config.h" namespace firebase { namespace firestore { diff --git a/Firestore/Port/bits.cc b/Firestore/core/src/firebase/firestore/util/bits.cc similarity index 74% rename from Firestore/Port/bits.cc rename to Firestore/core/src/firebase/firestore/util/bits.cc index 3e61223cd6a..0bfe4c3f3e4 100644 --- a/Firestore/Port/bits.cc +++ b/Firestore/core/src/firebase/firestore/util/bits.cc @@ -14,11 +14,13 @@ * limitations under the License. */ -#include "Firestore/Port/bits.h" +#include "Firestore/core/src/firebase/firestore/util/bits.h" -#include +#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" -namespace Firestore { +namespace firebase { +namespace firestore { +namespace util { int Bits::Log2Floor_Portable(uint32_t n) { if (n == 0) return -1; @@ -32,8 +34,10 @@ int Bits::Log2Floor_Portable(uint32_t n) { log += shift; } } - assert(value == 1); + FIREBASE_ASSERT(value == 1); return log; } -} // namespace Firestore +} // namespace util +} // namespace firestore +} // namespace firebase diff --git a/Firestore/Port/bits.h b/Firestore/core/src/firebase/firestore/util/bits.h similarity index 87% rename from Firestore/Port/bits.h rename to Firestore/core/src/firebase/firestore/util/bits.h index d212bf8afa1..185273fb8e8 100644 --- a/Firestore/Port/bits.h +++ b/Firestore/core/src/firebase/firestore/util/bits.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef IPHONE_FIRESTORE_PORT_BITS_H_ -#define IPHONE_FIRESTORE_PORT_BITS_H_ +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_BITS_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_BITS_H_ // Various bit-twiddling functions, all of which are static members of the Bits // class (making it effectively a namespace). Operands are unsigned integers. @@ -27,16 +27,20 @@ class Bits_Port32_Test; class Bits_Port64_Test; -namespace Firestore { +namespace firebase { +namespace firestore { +namespace util { class Bits { public: - // Return floor(log2(n)) for positive integer n. Returns -1 iff n == 0. + /** Return floor(log2(n)) for positive integer n. Returns -1 iff n == 0. */ static int Log2Floor(uint32_t n); static int Log2Floor64(uint64_t n); - // Potentially faster version of Log2Floor() that returns an - // undefined value if n == 0 + /** + * Potentially faster version of Log2Floor() that returns an + * undefined value if n == 0. + */ static int Log2FloorNonZero(uint32_t n); static int Log2FloorNonZero64(uint64_t n); @@ -51,8 +55,8 @@ class Bits { void operator=(Bits const&) = delete; // Allow tests to call _Portable variants directly. - friend class ::Bits_Port32_Test; - friend class ::Bits_Port64_Test; + friend class Bits_Port32_Test; + friend class Bits_Port64_Test; }; // ------------------------------------------------------------------------ @@ -155,6 +159,8 @@ inline int Bits::Log2FloorNonZero64_Portable(uint64_t n) { } } -} // namespace Firestore +} // namespace util +} // namespace firestore +} // namespace firebase -#endif // IPHONE_FIRESTORE_PORT_BITS_H_ +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_BITS_H_ diff --git a/Firestore/core/src/firebase/firestore/util/comparison.cc b/Firestore/core/src/firebase/firestore/util/comparison.cc new file mode 100644 index 00000000000..4bef84392c3 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/util/comparison.cc @@ -0,0 +1,112 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/core/src/firebase/firestore/util/comparison.h" + +#include + +#include + +namespace firebase { +namespace firestore { +namespace util { + +bool Comparator::operator()( + const absl::string_view& left, const absl::string_view& right) const { + // TODO(wilhuff): truncation aware comparison + return left < right; +} + +bool Comparator::operator()(double left, double right) const { + // NaN sorts equal to itself and before any other number. + if (left < right) { + return true; + } else if (left >= right) { + return false; + } else { + // One or both left and right is NaN. + return isnan(left) && !isnan(right); + } +} + +static constexpr double INT64_MIN_VALUE_AS_DOUBLE = + static_cast(std::numeric_limits::min()); + +static constexpr double INT64_MAX_VALUE_AS_DOUBLE = + static_cast(std::numeric_limits::max()); + +ComparisonResult CompareMixedNumber(double double_value, int64_t int64_value) { + // LLONG_MIN has an exact representation as double, so to check for a value + // outside the range representable by long, we have to check for strictly less + // than LLONG_MIN. Note that this also handles negative infinity. + if (double_value < INT64_MIN_VALUE_AS_DOUBLE) { + return ComparisonResult::Ascending; + } + + // LLONG_MAX has no exact representation as double (casting as we've done + // makes 2^63, which is larger than LLONG_MAX), so consider any value greater + // than or equal to the threshold to be out of range. This also handles + // positive infinity. + if (double_value >= INT64_MAX_VALUE_AS_DOUBLE) { + return ComparisonResult::Descending; + } + + // In Firestore NaN is defined to compare before all other numbers. + if (isnan(double_value)) { + return ComparisonResult::Ascending; + } + + auto double_as_int64 = static_cast(double_value); + ComparisonResult cmp = Compare(double_as_int64, int64_value); + if (cmp != ComparisonResult::Same) { + return cmp; + } + + // At this point the long representations are equal but this could be due to + // rounding. + double int64_as_double = static_cast(int64_value); + return Compare(double_value, int64_as_double); +} + +/** Helper to normalize a double and then return the raw bits as a uint64_t. */ +uint64_t DoubleBits(double d) { + if (isnan(d)) { + d = NAN; + } + + // Unlike C, C++ does not define type punning through a union type. + + // TODO(wilhuff): replace with absl::bit_cast + static_assert(sizeof(double) == sizeof(uint64_t), "doubles must be 8 bytes"); + uint64_t bits; + memcpy(&bits, &d, sizeof(bits)); + return bits; +} + +bool DoubleBitwiseEquals(double left, double right) { + return DoubleBits(left) == DoubleBits(right); +} + +size_t DoubleBitwiseHash(double d) { + uint64_t bits = DoubleBits(d); + // Note that x ^ (x >> 32) works fine for both 32 and 64 bit definitions of + // size_t + return static_cast(bits) ^ static_cast(bits >> 32); +} + +} // namespace util +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/util/comparison.h b/Firestore/core/src/firebase/firestore/util/comparison.h new file mode 100644 index 00000000000..6fd1e2b5281 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/util/comparison.h @@ -0,0 +1,181 @@ +/* + * Copyright 2018 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. + */ + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_COMPARISON_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_COMPARISON_H_ + +#if __OBJC__ +#import +#endif + +#include +#include + +#include +#include +#include + +#include "Firestore/core/src/firebase/firestore/util/string_apple.h" +#include "absl/strings/string_view.h" + +namespace firebase { +namespace firestore { +namespace util { + +/** + * An enumeration describing the result of a three-way comparison among + * strongly-ordered values (i.e. where comparison between values always yields + * less-than, equal-to, or greater-than). + * + * This is equivalent to: + * + * * NSComparisonResult from the iOS/macOS Foundation framework. + * * std::strong_ordering from C++20 + * + * The values of the constants are specifically chosen so as to make casting + * between this type and NSComparisonResult possible. + */ +enum class ComparisonResult { + /** The left hand side was less than the right. */ + Ascending = -1, + + /** The left hand side was equal to the right. */ + Same = 0, + + /** The left hand side was greater than the right. */ + Descending = 1 +}; + +/** + * Returns the reverse order (i.e. Ascending => Descending) etc. + */ +constexpr ComparisonResult ReverseOrder(ComparisonResult result) { + return static_cast(-static_cast(result)); +} + +/** + * A generalized comparator for types in Firestore, with ordering defined + * according to Firestore's semantics. This is useful as argument to e.g. + * std::sort. + * + * Comparators are only defined for the limited set of types for which + * Firestore defines an ordering. + */ +template +struct Comparator { + // By default comparison is not defined +}; + +/** Compares two strings. */ +template <> +struct Comparator { + bool operator()(const absl::string_view& left, + const absl::string_view& right) const; +}; + +/** Compares two bools: false < true. */ +template <> +struct Comparator : public std::less {}; + +/** Compares two int32_t. */ +template <> +struct Comparator : public std::less {}; + +/** Compares two int64_t. */ +template <> +struct Comparator : public std::less {}; + +/** Compares two doubles (using Firestore semantics for NaN). */ +template <> +struct Comparator { + bool operator()(double left, double right) const; +}; + +/** Compare two byte sequences. */ +// TODO(wilhuff): perhaps absl::Span would be better? +template <> +struct Comparator> + : public std::less> {}; + +/** + * Perform a three-way comparison between the left and right values using + * the appropriate Comparator for the values based on their type. + */ +template +ComparisonResult Compare(const T& left, const T& right) { + Comparator less_than; + if (less_than(left, right)) { + return ComparisonResult::Ascending; + } else if (less_than(right, left)) { + return ComparisonResult::Descending; + } else { + return ComparisonResult::Same; + } +} + +#if __OBJC__ +/** + * Returns true if the given ComparisonResult and NSComparisonResult have the + * same integer values (at compile time). + */ +constexpr bool EqualValue(ComparisonResult lhs, NSComparisonResult rhs) { + return static_cast(lhs) == static_cast(rhs); +} + +/** + * Performs a three-way comparison, identically to Compare, but converts the + * result to an NSComparisonResult. + * + * This function exists for interoperation with Objective-C++ and should + * eventually be removed. + */ +template +inline NSComparisonResult WrapCompare(const T& left, const T& right) { + static_assert(EqualValue(ComparisonResult::Ascending, NSOrderedAscending), + "Ascending invalid"); + static_assert(EqualValue(ComparisonResult::Same, NSOrderedSame), + "Same invalid"); + static_assert(EqualValue(ComparisonResult::Descending, NSOrderedDescending), + "Descending invalid"); + + return static_cast(Compare(left, right)); +} +#endif + +/** Compares a double and an int64_t. */ +ComparisonResult CompareMixedNumber(double doubleValue, int64_t longValue); + +/** Normalizes a double and then return the raw bits as a uint64_t. */ +uint64_t DoubleBits(double d); + +/** + * Compares the bitwise representation of two doubles, but normalizes NaN + * values. This is similar to what the backend and android clients do, including + * comparing -0.0 as not equal to 0.0. + */ +bool DoubleBitwiseEquals(double left, double right); + +/** + * Computes a bitwise hash of a double, but normalizes NaN values, suitable for + * use when using FSTDoublesAreBitwiseEqual for equality. + */ +size_t DoubleBitwiseHash(double d); + +} // namespace util +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_COMPARISON_H_ diff --git a/Firestore/core/src/firebase/firestore/util/config.h.in b/Firestore/core/src/firebase/firestore/util/config.h.in new file mode 100644 index 00000000000..e7a0c039c56 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/util/config.h.in @@ -0,0 +1,35 @@ +/* + * Copyright 2018 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. + */ + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_CONFIG_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_CONFIG_H_ + +// This header defines macros for all available platform configuration values. +// When building with CMake, it will substitute the lines marked with +// cmakedefine with values corresponding to the local configuration. +// +// On Apple platforms we support building via CocoaPods without CMake. When +// building this way we can't test the presence of features before building so +// predefine all the platform-support feature macros to their expected values. + +#cmakedefine HAVE_ARC4RANDOM 1 +#if COCOAPODS +# define HAVE_ARC4RANDOM 1 +#endif + +#cmakedefine HAVE_OPENSSL_RAND_H 1 + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_CONFIG_H_ diff --git a/Firestore/core/src/firebase/firestore/util/error_apple.h b/Firestore/core/src/firebase/firestore/util/error_apple.h new file mode 100644 index 00000000000..e31cfd6df9c --- /dev/null +++ b/Firestore/core/src/firebase/firestore/util/error_apple.h @@ -0,0 +1,52 @@ +/* + * Copyright 2018 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. + */ + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_ERROR_APPLE_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_ERROR_APPLE_H_ + +// Everything in this header exists for compatibility with Objective-C. +#if __OBJC__ + +#import + +#include "Firestore/Source/Public/FIRFirestoreErrors.h" // for FIRFirestoreErrorDomain +#include "Firestore/core/include/firebase/firestore/firestore_errors.h" +#include "Firestore/core/src/firebase/firestore/util/string_apple.h" +#include "absl/strings/string_view.h" + +namespace firebase { +namespace firestore { +namespace util { + +// Translates a set of error_code and error_msg to an NSError. +inline NSError* WrapNSError(const int64_t error_code, + const absl::string_view error_msg) { + if (error_code == FirestoreErrorCode::Ok) { + return nil; + } + return [NSError + errorWithDomain:FIRFirestoreErrorDomain + code:error_code + userInfo:@{NSLocalizedDescriptionKey : WrapNSString(error_msg)}]; +} + +} // namespace util +} // namespace firestore +} // namespace firebase + +#endif // __OBJC__ + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_ERROR_APPLE_H_ diff --git a/Firestore/core/src/firebase/firestore/util/firebase_assert.h b/Firestore/core/src/firebase/firestore/util/firebase_assert.h index da018645d09..76768e62b26 100644 --- a/Firestore/core/src/firebase/firestore/util/firebase_assert.h +++ b/Firestore/core/src/firebase/firestore/util/firebase_assert.h @@ -52,7 +52,7 @@ #else #define FIREBASE_DEV_ASSERT_WITH_EXPRESSION(condition, expression) \ FIREBASE_ASSERT_WITH_EXPRESSION(condition, expression) -#endif // !defined(NDEBUG) +#endif // defined(NDEBUG) // Custom assert() implementation that is not compiled out in release builds. #define FIREBASE_ASSERT(expression) \ @@ -75,6 +75,10 @@ } \ } while (0) +// Assert with custom message that is not compiled out in release builds. +#define FIREBASE_ASSERT_MESSAGE(expression, ...) \ + FIREBASE_ASSERT_MESSAGE_WITH_EXPRESSION(expression, expression, __VA_ARGS__) + // Assert condition is true otherwise display the specified expression, // message and abort. Compiled out of release builds. #if defined(NDEBUG) @@ -85,7 +89,18 @@ #define FIREBASE_DEV_ASSERT_MESSAGE_WITH_EXPRESSION(condition, expression, \ ...) \ FIREBASE_ASSERT_MESSAGE_WITH_EXPRESSION(condition, expression, __VA_ARGS__) -#endif // !defined(NDEBUG) +#endif // defined(NDEBUG) + +// Assert expression is true otherwise display the specified message and +// abort. +#define FIREBASE_ASSERT_MESSAGE(expression, ...) \ + FIREBASE_ASSERT_MESSAGE_WITH_EXPRESSION(expression, expression, __VA_ARGS__) + +// Assert expression is true otherwise display the specified message and +// abort. Compiled out of release builds. +#define FIREBASE_DEV_ASSERT_MESSAGE(expression, ...) \ + FIREBASE_DEV_ASSERT_MESSAGE_WITH_EXPRESSION(expression, expression, \ + __VA_ARGS__) namespace firebase { namespace firestore { diff --git a/Firestore/core/src/firebase/firestore/util/iterator_adaptors.h b/Firestore/core/src/firebase/firestore/util/iterator_adaptors.h new file mode 100644 index 00000000000..042fd720f23 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/util/iterator_adaptors.h @@ -0,0 +1,812 @@ +/* + * Copyright 2005, 2018 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. + */ + +// Provides some iterator adaptors and views. + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_ITERATOR_ADAPTORS_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_ITERATOR_ADAPTORS_H_ + +#include +#include +#include + +#include "absl/base/port.h" +#include "absl/meta/type_traits.h" + +namespace firebase { +namespace firestore { +namespace util { + +namespace internal { + +// value == true if Iter prohibits modification of its pointees. +template +struct IsConstIter + : std::is_const::reference>::type> {}; + +template +struct AddConstIf : std::conditional {}; + +// SynthIterTraits propagates the constness of the 'BaseIter' iterator +// type to its own exported 'pointer' and 'reference' typedefs. +template +struct SynthIterTraits : std::iterator_traits { + private: + static constexpr bool kIterConst = IsConstIter::value; + + public: + using value_type = typename std::remove_cv::type; + using pointer = typename AddConstIf::type*; + using reference = typename AddConstIf::type&; +}; + +// PointeeSynthIterTraits is similar to SynthIterTraits, but the 'Ptr' +// parameter is a pointer-like type, and value_type is the pointee. +template +struct PointeeSynthIterTraits : std::iterator_traits { + private: + static constexpr bool kIterConst = IsConstIter::value; + + public: + using value_type = typename std::pointer_traits::element_type; + using pointer = typename AddConstIf::type*; + using reference = typename AddConstIf::type&; +}; + +// CRTP base class for generating iterator adaptors. +// 'Sub' is the derived type, and 'Policy' encodes +// all of the behavior for the adaptor. +// Policy requirements: +// - type 'underlying_iterator': the underlying iterator type. +// - type 'adapted_traits': the traits of the adaptor. +// - static 'Extract(underlying_iterator)': convert iterator to reference. +// +template +class IteratorAdaptorBase { + private: + // Everything needed from the Policy type is expressed in this section. + using Iterator = typename Policy::underlying_iterator; + using OutTraits = typename Policy::adapted_traits; + static typename OutTraits::reference Extract(const Iterator& it) { + return Policy::Extract(it); + } + + public: + using iterator_category = typename OutTraits::iterator_category; + using value_type = typename OutTraits::value_type; + using pointer = typename OutTraits::pointer; + using reference = typename OutTraits::reference; + using difference_type = typename OutTraits::difference_type; + + IteratorAdaptorBase() : it_() { + } + // NOLINTNEXTLINE(runtime/explicit) + IteratorAdaptorBase(Iterator it) : it_(it) { + } + + Sub& sub() { + return static_cast(*this); + } + const Sub& sub() const { + return static_cast(*this); + } + + const Iterator& base() const { + return it_; + } + + reference get() const { + return Extract(base()); + } + reference operator*() const { + return get(); + } + pointer operator->() const { + return &get(); + } + reference operator[](difference_type d) const { + return *(sub() + d); + } + + Sub& operator++() { + ++it_; + return sub(); + } + Sub& operator--() { + --it_; + return sub(); + } + Sub operator++(int /*unused*/) { + return it_++; + } + Sub operator--(int /*unused*/) { + return it_--; + } + + Sub& operator+=(difference_type d) { + it_ += d; + return sub(); + } + Sub& operator-=(difference_type d) { + it_ -= d; + return sub(); + } + + bool operator==(Sub b) const { + return base() == b.base(); + } + bool operator!=(Sub b) const { + return base() != b.base(); + } + // These shouldn't be necessary, as implicit conversion from 'Iterator' + // should be enough to make such comparisons work. + bool operator==(Iterator b) const { + return *this == Sub(b); + } + bool operator!=(Iterator b) const { + return *this != Sub(b); + } + + friend Sub operator+(Sub it, difference_type d) { + return it.base() + d; + } + friend Sub operator+(difference_type d, Sub it) { + return it + d; + } + friend Sub operator-(Sub it, difference_type d) { + return it.base() - d; + } + friend difference_type operator-(Sub a, Sub b) { + return a.base() - b.base(); + } + + friend bool operator<(Sub a, Sub b) { + return a.base() < b.base(); + } + friend bool operator>(Sub a, Sub b) { + return a.base() > b.base(); + } + friend bool operator<=(Sub a, Sub b) { + return a.base() <= b.base(); + } + friend bool operator>=(Sub a, Sub b) { + return a.base() >= b.base(); + } + + private: + Iterator it_; +}; + +template +struct FirstPolicy { + using underlying_iterator = It; + using adapted_traits = + SynthIterTraits::value_type::first_type>; + static typename adapted_traits::reference Extract( + const underlying_iterator& it) { + return it->first; + } +}; + +template +struct SecondPolicy { + using underlying_iterator = It; + using adapted_traits = + SynthIterTraits::value_type::second_type>; + static typename adapted_traits::reference Extract( + const underlying_iterator& it) { + return it->second; + } +}; + +template +struct SecondPtrPolicy { + using underlying_iterator = It; + using adapted_traits = + PointeeSynthIterTraits::value_type::second_type>; + static typename adapted_traits::reference Extract( + const underlying_iterator& it) { + return *it->second; + } +}; + +template +struct PtrPolicy { + using underlying_iterator = It; + using adapted_traits = PointeeSynthIterTraits< + underlying_iterator, + typename std::iterator_traits::value_type>; + static typename adapted_traits::reference Extract( + const underlying_iterator& it) { + return **it; + } +}; + +} // namespace internal + +// In both iterator adaptors, iterator_first<> and iterator_second<>, +// we build a new iterator based on a parameterized iterator type, "It". +// The value type, "Val" is determined by "It::value_type::first" or +// "It::value_type::second", respectively. + +// iterator_first<> adapts an iterator to return the first value of a pair. +// It is equivalent to calling it->first on every value. +// Example: +// +// hash_map values; +// values["foo"] = 1; +// values["bar"] = 2; +// for (iterator_first::iterator> x = values.begin(); +// x != values.end(); ++x) { +// printf("%s", x->c_str()); +// } +template +struct iterator_first + : internal::IteratorAdaptorBase, + internal::FirstPolicy> { + using Base = internal::IteratorAdaptorBase, + internal::FirstPolicy>; + iterator_first() { + } + iterator_first(It it) // NOLINT(runtime/explicit) + : Base(it) { + } + template + iterator_first(iterator_first o) // NOLINT(runtime/explicit) + : Base(o.base()) { + } +}; + +template +iterator_first make_iterator_first(It it) { + return iterator_first(it); +} + +// iterator_second<> adapts an iterator to return the second value of a pair. +// It is equivalent to calling it->second on every value. +// Example: +// +// hash_map values; +// values["foo"] = 1; +// values["bar"] = 2; +// for (iterator_second::iterator> x = values.begin(); +// x != values.end(); ++x) { +// int v = *x; +// printf("%d", v); +// } +template +struct iterator_second + : internal::IteratorAdaptorBase, + internal::SecondPolicy> { + using Base = internal::IteratorAdaptorBase, + internal::SecondPolicy>; + iterator_second() { + } + iterator_second(It it) // NOLINT(runtime/explicit) + : Base(it) { + } + template + iterator_second(iterator_second o) // NOLINT(runtime/explicit) + : Base(o.base()) { + } +}; + +template +iterator_second make_iterator_second(It it) { + return iterator_second(it); +} + +// iterator_second_ptr<> adapts an iterator to return the dereferenced second +// value of a pair. +// It is equivalent to calling *it->second on every value. +// The same result can be achieved by composition +// iterator_ptr > +// Can be used with maps where values are regular pointers or pointers wrapped +// into linked_ptr. This iterator adaptor can be used by classes to give their +// clients access to some of their internal data without exposing too much of +// it. +// +// Example: +// class MyClass { +// public: +// MyClass(const string& s); +// string DebugString() const; +// }; +// typedef hash_map > MyMap; +// typedef iterator_second_ptr MyMapValuesIterator; +// MyMap values; +// values["foo"].reset(new MyClass("foo")); +// values["bar"].reset(new MyClass("bar")); +// for (MyMapValuesIterator it = values.begin(); it != values.end(); ++it) { +// printf("%s", it->DebugString().c_str()); +// } +template +struct iterator_second_ptr + : internal::IteratorAdaptorBase, + internal::SecondPtrPolicy> { + using Base = internal::IteratorAdaptorBase, + internal::SecondPtrPolicy>; + iterator_second_ptr() { + } + iterator_second_ptr(It it) // NOLINT(runtime/explicit) + : Base(it) { + } + template + iterator_second_ptr(iterator_second_ptr o) // NOLINT(runtime/explicit) + : Base(o.base()) { + } +}; + +template +iterator_second_ptr make_iterator_second_ptr(It it) { + return iterator_second_ptr(it); +} + +// iterator_ptr<> adapts an iterator to return the dereferenced value. +// With this adaptor you can write *it instead of **it, or it->something instead +// of (*it)->something. +// Can be used with vectors and lists where values are regular pointers +// or pointers wrapped into linked_ptr. This iterator adaptor can be used by +// classes to give their clients access to some of their internal data without +// exposing too much of it. +// +// Example: +// class MyClass { +// public: +// MyClass(const string& s); +// string DebugString() const; +// }; +// typedef vector > MyVector; +// typedef iterator_ptr DereferencingIterator; +// MyVector values; +// values.push_back(make_linked_ptr(new MyClass("foo"))); +// values.push_back(make_linked_ptr(new MyClass("bar"))); +// for (DereferencingIterator it = values.begin(); it != values.end(); ++it) { +// printf("%s", it->DebugString().c_str()); +// } +// +// Without iterator_ptr you would have to do (*it)->DebugString() +template +struct iterator_ptr : internal::IteratorAdaptorBase, + internal::PtrPolicy> { + using Base = internal::IteratorAdaptorBase, + internal::PtrPolicy>; + iterator_ptr() { + } + iterator_ptr(It it) // NOLINT(runtime/explicit) + : Base(it) { + } + template + iterator_ptr(iterator_ptr o) // NOLINT(runtime/explicit) + : Base(o.base()) { + } +}; + +template +iterator_ptr make_iterator_ptr(It it) { + return iterator_ptr(it); +} + +namespace internal { + +// Template that uses SFINAE to inspect Container abilities: +// . Set has_size_type true, iff T::size_type is defined +// . Define size_type as T::size_type if defined, or size_t otherwise +template +struct container_traits { + private: + // Test for availability of C::size_type. + template + struct test_size_type : std::false_type {}; + template + struct test_size_type> + : std::true_type {}; + + // Conditional provisioning of a size_type which defaults to size_t. + template + struct size_type_def { + using type = typename U::size_type; + }; + template + struct size_type_def { + using type = size_t; + }; + + public: + // Determine whether C::size_type is available. + static const bool has_size_type = test_size_type::value; + + // Provide size_type as either C::size_type if available, or as size_t. + using size_type = typename size_type_def::type; +}; + +template +struct IterGenerator { + using container_type = C; + using iterator = typename C::iterator; + using const_iterator = typename C::const_iterator; + + static iterator begin(container_type& c) { // NOLINT(runtime/references) + return c.begin(); + } + static iterator end(container_type& c) { // NOLINT(runtime/references) + return c.end(); + } + static const_iterator begin(const container_type& c) { + return c.begin(); + } + static const_iterator end(const container_type& c) { + return c.end(); + } +}; + +template +struct ReversingIterGeneratorAdaptor { + using container_type = typename SubIterGenerator::container_type; + using iterator = std::reverse_iterator; + using const_iterator = + std::reverse_iterator; + + static iterator begin(container_type& c) { // NOLINT(runtime/references) + return iterator(SubIterGenerator::end(c)); + } + static iterator end(container_type& c) { // NOLINT(runtime/references) + return iterator(SubIterGenerator::begin(c)); + } + static const_iterator begin(const container_type& c) { + return const_iterator(SubIterGenerator::end(c)); + } + static const_iterator end(const container_type& c) { + return const_iterator(SubIterGenerator::begin(c)); + } +}; + +// C: the container type +// Iter: the type of mutable iterator to generate +// ConstIter: the type of constant iterator to generate +// IterGenerator: a policy type that returns native iterators from a C +template > +class iterator_view_helper { + public: + using container_type = C; + using iterator = Iter; + using const_iterator = ConstIter; + using value_type = typename std::iterator_traits::value_type; + using size_type = typename internal::container_traits::size_type; + + explicit iterator_view_helper( + container_type& c) // NOLINT(runtime/references) + : c_(&c) { + } + + iterator begin() { + return iterator(IterGenerator::begin(container())); + } + iterator end() { + return iterator(IterGenerator::end(container())); + } + const_iterator begin() const { + return const_iterator(IterGenerator::begin(container())); + } + const_iterator end() const { + return const_iterator(IterGenerator::end(container())); + } + const_iterator cbegin() const { + return begin(); + } + const_iterator cend() const { + return end(); + } + const container_type& container() const { + return *c_; + } + container_type& container() { + return *c_; + } + + bool empty() const { + return begin() == end(); + } + size_type size() const { + return c_->size(); + } + + private: + container_type* c_; +}; + +template > +class const_iterator_view_helper { + public: + using container_type = C; + using const_iterator = ConstIter; + using value_type = typename std::iterator_traits::value_type; + using size_type = typename internal::container_traits::size_type; + + explicit const_iterator_view_helper(const container_type& c) : c_(&c) { + } + + // Allow implicit conversion from the corresponding iterator_view_helper. + // Erring on the side of constness should be allowed. E.g.: + // MyMap m; + // key_view_type::type keys = key_view(m); // ok + // key_view_type::type const_keys = key_view(m); // ok + template + const_iterator_view_helper(const iterator_view_helper& v) + : c_(&v.container()) { + } + + const_iterator begin() const { + return const_iterator(IterGenerator::begin(container())); + } + const_iterator end() const { + return const_iterator(IterGenerator::end(container())); + } + const_iterator cbegin() const { + return begin(); + } + const_iterator cend() const { + return end(); + } + const container_type& container() const { + return *c_; + } + + bool empty() const { + return begin() == end(); + } + size_type size() const { + return c_->size(); + } + + private: + const container_type* c_; +}; + +} // namespace internal + +// Note: The views like value_view, key_view should be in gtl namespace. +// Currently there are lot of callers that reference the methods in the global +// namespace. +// +// Traits to provide a typedef abstraction for the return value +// of the key_view() and value_view() functions, such that +// they can be declared as: +// +// template key_view_t key_view(C& c); +// template value_view_t value_view(C& c); +// +// This abstraction allows callers of these functions to use readable +// type names, and allows the maintainers of iterator_adaptors.h to +// change the return types if needed without updating callers. + +template +struct key_view_type { + using type = internal::iterator_view_helper< + C, + iterator_first, + iterator_first>; +}; + +template +struct key_view_type { + using type = internal:: + const_iterator_view_helper>; +}; + +template +struct value_view_type { + using type = internal::iterator_view_helper< + C, + iterator_second, + iterator_second>; +}; + +template +struct value_view_type { + using type = internal::const_iterator_view_helper< + C, + iterator_second>; +}; + +// The key_view and value_view functions provide pretty ways to iterate either +// the keys or the values of a map using range based for loops. +// +// Example: +// hash_map my_map; +// ... +// for (string val : value_view(my_map)) { +// ... +// } +// +// Note: If you pass a temporary container to key_view or value_view, be careful +// that the temporary container outlives the wrapper view to avoid dangling +// references. +// This is fine: PublishAll(value_view(Make()); +// This is not: for (const auto& v : value_view(Make())) Publish(v); + +template +typename key_view_type::type key_view( + C& map) { // NOLINT(runtime/references) + return typename key_view_type::type(map); +} + +template +typename key_view_type::type key_view(const C& map) { + return typename key_view_type::type(map); +} + +template +typename value_view_type::type value_view( + C& map) { // NOLINT(runtime/references) + return typename value_view_type::type(map); +} + +template +typename value_view_type::type value_view(const C& map) { + return typename value_view_type::type(map); +} + +// Abstract container view that dereferences the pointer-like .second member +// of a container's std::pair elements, such as the elements of std::map +// or of std::vector>. +// +// Example: +// map elements; +// for (const string& element : deref_second_view(elements)) { +// ... +// } +// +// Note: If you pass a temporary container to deref_second_view, be careful that +// the temporary container outlives the deref_second_view to avoid dangling +// references. +// This is fine: PublishAll(deref_second_view(Make()); +// This is not: for (const auto& v : deref_second_view(Make())) { +// Publish(v); +// } + +template +struct deref_second_view_type { + using type = internal::iterator_view_helper< + C, + iterator_second_ptr, + iterator_second_ptr>; +}; + +template +struct deref_second_view_type { + using type = internal::const_iterator_view_helper< + C, + iterator_second_ptr>; +}; + +template +typename deref_second_view_type::type deref_second_view( + C& map) { // NOLINT(runtime/references) + return typename deref_second_view_type::type(map); +} + +template +typename deref_second_view_type::type deref_second_view(const C& map) { + return typename deref_second_view_type::type(map); +} + +// Abstract container view that dereferences pointer elements. +// +// Example: +// vector elements; +// for (const string& element : deref_view(elements)) { +// ... +// } +// +// Note: If you pass a temporary container to deref_view, be careful that the +// temporary container outlives the deref_view to avoid dangling references. +// This is fine: PublishAll(deref_view(Make()); +// This is not: for (const auto& v : deref_view(Make())) { Publish(v); } + +template +struct deref_view_type { + using type = + internal::iterator_view_helper, + iterator_ptr>; +}; + +template +struct deref_view_type { + using type = internal:: + const_iterator_view_helper>; +}; + +template +typename deref_view_type::type deref_view( + C& c) { // NOLINT(runtime/references) + return typename deref_view_type::type(c); +} + +template +typename deref_view_type::type deref_view(const C& c) { + return typename deref_view_type::type(c); +} + +// Abstract container view that iterates backwards. +// +// Example: +// vector elements; +// for (const string& element : reversed_view(elements)) { +// ... +// } +// +// Note: If you pass a temporary container to reversed_view_type, be careful +// that the temporary container outlives the reversed_view to avoid dangling +// references. This is fine: PublishAll(reversed_view(Make()); +// This is not: for (const auto& v : reversed_view(Make())) { Publish(v); } + +template +struct reversed_view_type { + private: + using policy = + internal::ReversingIterGeneratorAdaptor>; + + public: + using type = internal::iterator_view_helper; +}; + +template +struct reversed_view_type { + private: + using policy = + internal::ReversingIterGeneratorAdaptor>; + + public: + using type = internal:: + const_iterator_view_helper; +}; + +template +typename reversed_view_type::type reversed_view( + C& c) { // NOLINT(runtime/references) + return typename reversed_view_type::type(c); +} + +template +typename reversed_view_type::type reversed_view(const C& c) { + return typename reversed_view_type::type(c); +} + +} // namespace util +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_ITERATOR_ADAPTORS_H_ diff --git a/Firestore/Port/ordered_code.cc b/Firestore/core/src/firebase/firestore/util/ordered_code.cc similarity index 71% rename from Firestore/Port/ordered_code.cc rename to Firestore/core/src/firebase/firestore/util/ordered_code.cc index 05a1569eb3d..688f15c4577 100644 --- a/Firestore/Port/ordered_code.cc +++ b/Firestore/core/src/firebase/firestore/util/ordered_code.cc @@ -14,16 +14,14 @@ * limitations under the License. */ -#include "Firestore/Port/ordered_code.h" - -#include +#include "Firestore/core/src/firebase/firestore/util/ordered_code.h" #include #include #include -#include // For Slice -#include "Firestore/Port/bits.h" +#include "Firestore/core/src/firebase/firestore/util/bits.h" +#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" #define UNALIGNED_LOAD32 ABSL_INTERNAL_UNALIGNED_LOAD32 #define UNALIGNED_LOAD64 ABSL_INTERNAL_UNALIGNED_LOAD64 @@ -65,9 +63,9 @@ // turn is the same as the ordering of the encodings of those two // characters. Moreover, for every finite string x, F(x) < F(). -namespace Firestore { - -using leveldb::Slice; +namespace firebase { +namespace firestore { +namespace util { static const char kEscape1 = '\000'; static const char kNullCharacter = '\xff'; // Combined with kEscape1 @@ -79,50 +77,57 @@ static const char kFFCharacter = '\000'; // Combined with kEscape2 static const char kEscape1_Separator[2] = {kEscape1, kSeparator}; -// Append to "*dest" the "len" bytes starting from "*src". +/** Append to "*dest" the "len" bytes starting from "*src". */ inline static void AppendBytes(std::string* dest, const char* src, size_t len) { dest->append(src, len); } -inline bool IsSpecialByte(char c) { return ((unsigned char)(c + 1)) < 2; } +inline static bool IsSpecialByte(char c) { + return ((unsigned char)(c + 1)) < 2; +} -// Returns 0 if one or more of the bytes in the specified uint32 value -// are the special values 0 or 255, and returns 4 otherwise. The -// result of this routine can be added to "p" to either advance past -// the next 4 bytes if they do not contain a special byte, or to -// remain on this set of four bytes if they contain the next special -// byte occurrence. -// -// REQUIRES: v is the value of loading the next 4 bytes from "*p" (we -// pass in v rather than loading it because in some cases, the client -// may already have the value in a register: "p" is just used for -// assertion checking). -inline int AdvanceIfNoSpecialBytes(uint32_t v_32, const char* p) { - assert(UNALIGNED_LOAD32(p) == v_32); +/** + * Returns 0 if one or more of the bytes in the specified uint32 value + * are the special values 0 or 255, and returns 4 otherwise. The + * result of this routine can be added to "p" to either advance past + * the next 4 bytes if they do not contain a special byte, or to + * remain on this set of four bytes if they contain the next special + * byte occurrence. + * + * REQUIRES: v_32 is the value of loading the next 4 bytes from "*p" (we + * pass in v_32 rather than loading it because in some cases, the client + * may already have the value in a register: "p" is just used for + * assertion checking). + */ +inline static int AdvanceIfNoSpecialBytes(uint32_t v_32, const char* p) { + FIREBASE_ASSERT(UNALIGNED_LOAD32(p) == v_32); // See comments in SkipToNextSpecialByte if you wish to // understand this expression (which checks for the occurrence // of the special byte values 0 or 255 in any of the bytes of v_32). if ((v_32 - 0x01010101u) & ~(v_32 + 0x01010101u) & 0x80808080u) { // Special byte is in p[0..3] - assert(IsSpecialByte(p[0]) || IsSpecialByte(p[1]) || IsSpecialByte(p[2]) || - IsSpecialByte(p[3])); + FIREBASE_ASSERT(IsSpecialByte(p[0]) || IsSpecialByte(p[1]) || + IsSpecialByte(p[2]) || IsSpecialByte(p[3])); return 0; } else { - assert(!IsSpecialByte(p[0])); - assert(!IsSpecialByte(p[1])); - assert(!IsSpecialByte(p[2])); - assert(!IsSpecialByte(p[3])); + FIREBASE_ASSERT(!IsSpecialByte(p[0])); + FIREBASE_ASSERT(!IsSpecialByte(p[1])); + FIREBASE_ASSERT(!IsSpecialByte(p[2])); + FIREBASE_ASSERT(!IsSpecialByte(p[3])); return 4; } } -// Return a pointer to the first byte in the range "[start..limit)" -// whose value is 0 or 255 (kEscape1 or kEscape2). If no such byte -// exists in the range, returns "limit". -inline const char* SkipToNextSpecialByte(const char* start, const char* limit) { +/** + * Return a pointer to the first byte in the range "[start..limit)" + * whose value is 0 or 255 (kEscape1 or kEscape2). If no such byte + * exists in the range, returns "limit". + */ +inline static const char* SkipToNextSpecialByte(const char* start, + const char* limit) { // If these constants were ever changed, this routine needs to change - assert(kEscape1 == 0); - assert((kEscape2 & 0xffu) == 255u); + FIREBASE_ASSERT(kEscape1 == 0); + FIREBASE_ASSERT((kEscape2 & 0xff) == 255); const char* p = start; while (p + 8 <= limit) { // Find out if any of the next 8 bytes are either 0 or 255 (our @@ -160,7 +165,8 @@ inline const char* SkipToNextSpecialByte(const char* start, const char* limit) { if (IsSpecialByte(p[0])) return p; if (IsSpecialByte(p[1])) return p + 1; if (IsSpecialByte(p[2])) return p + 2; - assert(IsSpecialByte(p[3])); // Last byte must be the special one + FIREBASE_ASSERT( + IsSpecialByte(p[3])); // Last byte must be the special one return p + 3; } } @@ -180,9 +186,12 @@ const char* OrderedCode::TEST_SkipToNextSpecialByte(const char* start, return SkipToNextSpecialByte(start, limit); } -// Helper routine to encode "s" and append to "*dest", escaping special -// characters. -inline static void EncodeStringFragment(std::string* dest, Slice s) { +/** + * Helper routine to encode "s" and append to "*dest", escaping special + * characters. + */ +inline static void EncodeStringFragment(std::string* dest, + absl::string_view s) { const char* p = s.data(); const char* limit = p + s.size(); const char* copy_start = p; @@ -190,44 +199,49 @@ inline static void EncodeStringFragment(std::string* dest, Slice s) { p = SkipToNextSpecialByte(p, limit); if (p >= limit) break; // No more special characters that need escaping char c = *(p++); - assert(IsSpecialByte(c)); + FIREBASE_ASSERT(IsSpecialByte(c)); if (c == kEscape1) { - AppendBytes(dest, copy_start, p - copy_start - 1); + AppendBytes(dest, copy_start, static_cast(p - copy_start) - 1); dest->push_back(kEscape1); dest->push_back(kNullCharacter); copy_start = p; } else { - assert(c == kEscape2); - AppendBytes(dest, copy_start, p - copy_start - 1); + FIREBASE_ASSERT(c == kEscape2); + AppendBytes(dest, copy_start, static_cast(p - copy_start) - 1); dest->push_back(kEscape2); dest->push_back(kFFCharacter); copy_start = p; } } if (p > copy_start) { - AppendBytes(dest, copy_start, p - copy_start); + AppendBytes(dest, copy_start, static_cast(p - copy_start)); } } -void OrderedCode::WriteString(std::string* dest, Slice s) { +void OrderedCode::WriteString(std::string* dest, absl::string_view s) { EncodeStringFragment(dest, s); AppendBytes(dest, kEscape1_Separator, 2); } -// Return number of bytes needed to encode the non-length portion -// of val in ordered coding. Returns number in range [0,8]. +/** + * Return number of bytes needed to encode the non-length portion + * of val in ordered coding. Returns number in range [0,8]. + */ static inline unsigned int OrderedNumLength(uint64_t val) { const int lg = Bits::Log2Floor64(val); // -1 if val==0 return static_cast(lg + 1 + 7) / 8; } -// Append n bytes from src to *dst. -// REQUIRES: n <= 9 -// REQUIRES: src[0..8] are readable bytes (even if n is smaller) -// -// If we use string::append() instead of this routine, it increases the -// runtime of WriteNumIncreasing from ~9ns to ~13ns. -static inline void AppendUpto9(std::string* dst, const char* src, +/** + * Append n bytes from src to *dst. + * REQUIRES: n <= 9 + * REQUIRES: src[0..8] are readable bytes (even if n is smaller) + * + * If we use string::append() instead of this routine, it increases the + * runtime of WriteNumIncreasing from ~9ns to ~13ns. + */ +static inline void AppendUpto9(std::string* dst, + const char* src, unsigned int n) { dst->append(src, 9); // Fixed-length append const size_t extra = 9 - n; // How many extra bytes we added @@ -244,10 +258,11 @@ void OrderedCode::WriteNumIncreasing(std::string* dest, uint64_t val) { // call on *dest. char buf[17]; - UNALIGNED_STORE64(buf + 1, absl::ghtonll(val)); // buf[0] may be needed for length + UNALIGNED_STORE64(buf + 1, + absl::ghtonll(val)); // buf[0] may be needed for length const unsigned int length = OrderedNumLength(val); char* start = buf + 9 - length - 1; - *start = length; + *start = static_cast(length); AppendUpto9(dest, start, length + 1); } @@ -261,15 +276,19 @@ void OrderedCode::WriteInfinity(std::string* dest) { WriteInfinityInternal(dest); } -void OrderedCode::WriteTrailingString(std::string* dest, Slice str) { +void OrderedCode::WriteTrailingString(std::string* dest, + absl::string_view str) { dest->append(str.data(), str.size()); } -// Parse the encoding of a string previously encoded with or without -// inversion. If parse succeeds, return true, consume encoding from -// "*src", and if result != NULL append the decoded string to "*result". -// Otherwise, return false and leave both undefined. -inline static bool ReadStringInternal(Slice* src, std::string* result) { +/** + * Parse the encoding of a string previously encoded with or without + * inversion. If parse succeeds, return true, consume encoding from + * "*src", and if result != NULL append the decoded string to "*result". + * Otherwise, return false and leave both undefined. + */ +inline static bool ReadStringInternal(absl::string_view* src, + std::string* result) { const char* start = src->data(); const char* string_limit = src->data() + src->size(); @@ -284,16 +303,17 @@ inline static bool ReadStringInternal(Slice* src, std::string* result) { // If inversion is required, instead of inverting 'c', we invert the // character constants to which 'c' is compared. We get the same // behavior but save the runtime cost of inverting 'c'. - assert(IsSpecialByte(c)); + FIREBASE_ASSERT(IsSpecialByte(c)); if (c == kEscape1) { if (result) { - AppendBytes(result, copy_start, start - copy_start - 1); + AppendBytes(result, copy_start, + static_cast(start - copy_start) - 1); } // kEscape1 kSeparator ends component // kEscape1 kNullCharacter represents '\0' const char next = *(start++); if (next == kSeparator) { - src->remove_prefix(start - src->data()); + src->remove_prefix(static_cast(start - src->data())); return true; } else if (next == kNullCharacter) { if (result) { @@ -304,9 +324,10 @@ inline static bool ReadStringInternal(Slice* src, std::string* result) { } copy_start = start; } else { - assert(c == kEscape2); + FIREBASE_ASSERT(c == kEscape2); if (result) { - AppendBytes(result, copy_start, start - copy_start - 1); + AppendBytes(result, copy_start, + static_cast(start - copy_start) - 1); } // kEscape2 kFFCharacter represents '\xff' // kEscape2 kInfinity is an error @@ -324,22 +345,22 @@ inline static bool ReadStringInternal(Slice* src, std::string* result) { return false; } -bool OrderedCode::ReadString(Slice* src, std::string* result) { +bool OrderedCode::ReadString(absl::string_view* src, std::string* result) { return ReadStringInternal(src, result); } -bool OrderedCode::ReadNumIncreasing(Slice* src, uint64_t* result) { +bool OrderedCode::ReadNumIncreasing(absl::string_view* src, uint64_t* result) { if (src->empty()) { return false; // Not enough bytes } // Decode length byte - const int len = static_cast((*src)[0]); + const size_t len = static_cast((*src)[0]); // If len > 0 and src is longer than 1, the first byte of "payload" // must be non-zero (otherwise the encoding is not minimal). // In opt mode, we don't enforce that encodings must be minimal. - assert(0 == len || src->size() == 1 || (*src)[1] != '\0'); + FIREBASE_ASSERT(0 == len || src->size() == 1 || (*src)[1] != '\0'); if (len + 1 > src->size() || len > 8) { return false; // Not enough bytes or too many bytes @@ -347,7 +368,7 @@ bool OrderedCode::ReadNumIncreasing(Slice* src, uint64_t* result) { if (result) { uint64_t tmp = 0; - for (int i = 0; i < len; i++) { + for (size_t i = 0; i < len; i++) { tmp <<= 8; tmp |= static_cast((*src)[1 + i]); } @@ -357,7 +378,7 @@ bool OrderedCode::ReadNumIncreasing(Slice* src, uint64_t* result) { return true; } -inline static bool ReadInfinityInternal(Slice* src) { +inline static bool ReadInfinityInternal(absl::string_view* src) { if (src->size() >= 2 && ((*src)[0] == kEscape2) && ((*src)[1] == kInfinity)) { src->remove_prefix(2); return true; @@ -366,9 +387,12 @@ inline static bool ReadInfinityInternal(Slice* src) { } } -bool OrderedCode::ReadInfinity(Slice* src) { return ReadInfinityInternal(src); } +bool OrderedCode::ReadInfinity(absl::string_view* src) { + return ReadInfinityInternal(src); +} -inline static bool ReadStringOrInfinityInternal(Slice* src, std::string* result, +inline static bool ReadStringOrInfinityInternal(absl::string_view* src, + std::string* result, bool* inf) { if (ReadInfinityInternal(src)) { if (inf) *inf = true; @@ -387,12 +411,14 @@ inline static bool ReadStringOrInfinityInternal(Slice* src, std::string* result, } } -bool OrderedCode::ReadStringOrInfinity(Slice* src, std::string* result, +bool OrderedCode::ReadStringOrInfinity(absl::string_view* src, + std::string* result, bool* inf) { return ReadStringOrInfinityInternal(src, result, inf); } -bool OrderedCode::ReadTrailingString(Slice* src, std::string* result) { +bool OrderedCode::ReadTrailingString(absl::string_view* src, + std::string* result) { if (result) result->assign(src->data(), src->size()); src->remove_prefix(src->size()); return true; @@ -400,7 +426,7 @@ bool OrderedCode::ReadTrailingString(Slice* src, std::string* result) { void OrderedCode::TEST_Corrupt(std::string* str, int k) { int seen_seps = 0; - for (int i = 0; i < str->size() - 1; i++) { + for (size_t i = 0; i < str->size() - 1; i++) { if ((*str)[i] == kEscape1 && (*str)[i + 1] == kSeparator) { seen_seps++; if (seen_seps == k) { @@ -506,61 +532,70 @@ static const int8_t kBitsToLength[1 + 63] = { 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 10}; -// Calculates the encoding length in bytes of the signed number n. +/** Calculates the encoding length in bytes of the signed number n. */ static inline int SignedEncodingLength(int64_t n) { - return kBitsToLength[Bits::Log2Floor64(n < 0 ? ~n : n) + 1]; + return kBitsToLength[Bits::Log2Floor64( + static_cast(n < 0 ? ~n : n)) + + 1]; } -// Slightly faster version for n > 0. +/** Slightly faster version for n > 0. */ static inline int SignedEncodingLengthPositive(int64_t n) { - return kBitsToLength[Bits::Log2FloorNonZero64(n) + 1]; + return kBitsToLength[Bits::Log2FloorNonZero64(static_cast(n)) + 1]; } void OrderedCode::WriteSignedNumIncreasing(std::string* dest, int64_t val) { - const uint64_t x = val < 0 ? ~val : val; + const int64_t x = val < 0 ? ~val : val; if (x < 64) { // fast path for encoding length == 1 - *dest += kLengthToHeaderBits[1][0] ^ val; + *dest += static_cast(kLengthToHeaderBits[1][0] ^ val); return; } // buf = val in network byte order, sign extended to 10 bytes const char sign_byte = val < 0 ? '\xff' : '\0'; char buf[10] = { - sign_byte, sign_byte, + sign_byte, + sign_byte, }; - UNALIGNED_STORE64(buf + 2, absl::ghtonll(val)); + UNALIGNED_STORE64(buf + 2, absl::ghtonll(static_cast(val))); - static_assert(sizeof(buf) == kMaxSigned64Length, "max length size mismatch"); - const int len = SignedEncodingLengthPositive(x); - assert(len >= 2); + FIREBASE_ASSERT_MESSAGE_WITH_EXPRESSION(sizeof(buf) == kMaxSigned64Length, + sizeof(buf) == kMaxSigned64Length, + "max length size mismatch"); + const size_t len = static_cast(SignedEncodingLengthPositive(x)); + FIREBASE_ASSERT(len >= 2); char* const begin = buf + sizeof(buf) - len; begin[0] ^= kLengthToHeaderBits[len][0]; begin[1] ^= kLengthToHeaderBits[len][1]; // ok because len >= 2 dest->append(begin, len); } -bool OrderedCode::ReadSignedNumIncreasing(Slice* src, int64_t* result) { +bool OrderedCode::ReadSignedNumIncreasing(absl::string_view* src, + int64_t* result) { if (src->empty()) return false; const uint64_t xor_mask = (!((*src)[0] & 0x80)) ? ~0ULL : 0ULL; - const unsigned char first_byte = (*src)[0] ^ (xor_mask & 0xff); + const unsigned char first_byte = static_cast( + static_cast((*src)[0]) ^ (xor_mask & 0xff)); // now calculate and test length, and set x to raw (unmasked) result - int len; + size_t len; uint64_t x; if (first_byte != 0xff) { - len = 7 - Bits::Log2FloorNonZero(first_byte ^ 0xff); + len = static_cast(7 - Bits::Log2FloorNonZero(first_byte ^ 0xff)); if (src->size() < len) return false; x = xor_mask; // sign extend using xor_mask - for (int i = 0; i < len; ++i) + for (size_t i = 0; i < len; ++i) x = (x << 8) | static_cast((*src)[i]); } else { len = 8; if (src->size() < len) return false; - const unsigned char second_byte = (*src)[1] ^ (xor_mask & 0xff); + const unsigned char second_byte = static_cast( + static_cast((*src)[1]) ^ (xor_mask & 0xff)); if (second_byte >= 0x80) { if (second_byte < 0xc0) { len = 9; } else { - const unsigned char third_byte = (*src)[2] ^ (xor_mask & 0xff); + const unsigned char third_byte = static_cast( + static_cast((*src)[2]) ^ (xor_mask & 0xff)); if (second_byte == 0xc0 && third_byte < 0x80) { len = 10; } else { @@ -574,12 +609,14 @@ bool OrderedCode::ReadSignedNumIncreasing(Slice* src, int64_t* result) { x ^= kLengthToMask[len]; // remove spurious header bits - assert(len == SignedEncodingLength(x)); + FIREBASE_ASSERT(len == static_cast( + SignedEncodingLength(static_cast(x)))); - if (result) *result = x; - src->remove_prefix(len); + if (result) *result = static_cast(x); + src->remove_prefix(static_cast(len)); return true; } -} // namespace Firestore - +} // namespace util +} // namespace firestore +} // namespace firebase diff --git a/Firestore/Port/ordered_code.h b/Firestore/core/src/firebase/firestore/util/ordered_code.h similarity index 64% rename from Firestore/Port/ordered_code.h rename to Firestore/core/src/firebase/firestore/util/ordered_code.h index 7c390a5001f..57b84bd09ce 100644 --- a/Firestore/Port/ordered_code.h +++ b/Firestore/core/src/firebase/firestore/util/ordered_code.h @@ -36,16 +36,16 @@ // This module is often useful when generating multi-part sstable // keys that have to be ordered in a particular fashion. -#ifndef IPHONE_FIRESTORE_PORT_ORDERED_CODE_H_ -#define IPHONE_FIRESTORE_PORT_ORDERED_CODE_H_ +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_ORDERED_CODE_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_ORDERED_CODE_H_ #include -namespace leveldb { -class Slice; -} +#include "absl/strings/string_view.h" -namespace Firestore { +namespace firebase { +namespace firestore { +namespace util { class OrderedCode { public: @@ -60,19 +60,23 @@ class OrderedCode { // is not called WriteStringIncreasing() for convenience and backward // compatibility. - static void WriteString(std::string* dest, leveldb::Slice str); + static void WriteString(std::string* dest, absl::string_view str); static void WriteNumIncreasing(std::string* dest, uint64_t num); static void WriteSignedNumIncreasing(std::string* dest, int64_t num); - // Creates an encoding for the "infinite string", a value considered to - // be lexicographically after any real string. Note that in the case of - // WriteInfinityDecreasing(), this would come before any real string as - // the ordering puts lexicographically greater values first. + /** + * Creates an encoding for the "infinite string", a value considered to + * be lexicographically after any real string. Note that in the case of + * WriteInfinityDecreasing(), this would come before any real string as + * the ordering puts lexicographically greater values first. + */ static void WriteInfinity(std::string* dest); - // Special string append that can only be used at the tail end of - // an encoded string -- blindly appends "str" to "*dest". - static void WriteTrailingString(std::string* dest, leveldb::Slice str); + /** + * Special string append that can only be used at the tail end of + * an encoded string -- blindly appends "str" to "*dest". + */ + static void WriteTrailingString(std::string* dest, absl::string_view str); // ------------------------------------------------------------------- // Decoding routines: these extract an item earlier encoded using @@ -83,26 +87,33 @@ class OrderedCode { // "*result". Returns true if the next item was read successfully, false // otherwise. - static bool ReadString(leveldb::Slice* src, std::string* result); - static bool ReadNumIncreasing(leveldb::Slice* src, uint64_t* result); - static bool ReadSignedNumIncreasing(leveldb::Slice* src, int64_t* result); + static bool ReadString(absl::string_view* src, std::string* result); + static bool ReadNumIncreasing(absl::string_view* src, uint64_t* result); + static bool ReadSignedNumIncreasing(absl::string_view* src, int64_t* result); - static bool ReadInfinity(leveldb::Slice* src); - static bool ReadTrailingString(leveldb::Slice* src, std::string* result); + static bool ReadInfinity(absl::string_view* src); + static bool ReadTrailingString(absl::string_view* src, std::string* result); - // REQUIRES: next item was encoded by WriteInfinity() or WriteString() - static bool ReadStringOrInfinity(leveldb::Slice* src, std::string* result, bool* inf); + /** REQUIRES: next item was encoded by WriteInfinity() or WriteString(). */ + static bool ReadStringOrInfinity(absl::string_view* src, + std::string* result, + bool* inf); - // Helper for testing: corrupt "*str" by changing the kth item separator - // in the string. + /** + * Helper for testing: corrupt "*str" by changing the kth item separator + * in the string. + */ static void TEST_Corrupt(std::string* str, int k); - // Helper for testing. - // SkipToNextSpecialByte is an internal routine defined in the .cc file - // with the following semantics. Return a pointer to the first byte - // in the range "[start..limit)" whose value is 0 or 255. If no such - // byte exists in the range, returns "limit". - static const char* TEST_SkipToNextSpecialByte(const char* start, const char* limit); + /** + * Helper for testing. + * SkipToNextSpecialByte is an internal routine defined in the .cc file + * with the following semantics. Return a pointer to the first byte + * in the range "[start..limit)" whose value is 0 or 255. If no such + * byte exists in the range, returns "limit". + */ + static const char* TEST_SkipToNextSpecialByte(const char* start, + const char* limit); // Not an instantiable class, but the class exists to make it easy to // use with a single using statement. @@ -111,6 +122,8 @@ class OrderedCode { OrderedCode& operator=(const OrderedCode&) = delete; }; -} // namespace Firestore +} // namespace util +} // namespace firestore +} // namespace firebase -#endif // IPHONE_FIRESTORE_PORT_ORDERED_CODE_H_ +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_ORDERED_CODE_H_ diff --git a/Firestore/core/src/firebase/firestore/util/secure_random.h b/Firestore/core/src/firebase/firestore/util/secure_random.h index 72be1bd30d9..95b41e14a2d 100644 --- a/Firestore/core/src/firebase/firestore/util/secure_random.h +++ b/Firestore/core/src/firebase/firestore/util/secure_random.h @@ -50,6 +50,22 @@ class SecureRandom { } result_type operator()(); + + /** Returns a uniformly distributed pseudorandom integer in [0, n). */ + inline result_type Uniform(result_type n) { + // Divides the range into buckets of size n plus leftovers. + const result_type rem = (max() - min()) % n + min() + 1; + result_type rnd; + // Generates random number until the number falls into a bucket. + do { + rnd = (*this)(); + } while (rnd < rem); + return rnd % n; + } + + inline bool OneIn(result_type n) { + return Uniform(n) == 0; + } }; } // namespace util diff --git a/Firestore/core/src/firebase/firestore/util/secure_random_arc4random.cc b/Firestore/core/src/firebase/firestore/util/secure_random_arc4random.cc index a76ade39757..83f72b54bff 100644 --- a/Firestore/core/src/firebase/firestore/util/secure_random_arc4random.cc +++ b/Firestore/core/src/firebase/firestore/util/secure_random_arc4random.cc @@ -16,7 +16,7 @@ #include "Firestore/core/src/firebase/firestore/util/secure_random.h" -#include "Firestore/core/src/firebase/firestore/base/port.h" +#include "Firestore/core/src/firebase/firestore/util/config.h" #if HAVE_ARC4RANDOM diff --git a/Firestore/core/src/firebase/firestore/util/secure_random_openssl.cc b/Firestore/core/src/firebase/firestore/util/secure_random_openssl.cc new file mode 100644 index 00000000000..d3f6e63e7e8 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/util/secure_random_openssl.cc @@ -0,0 +1,46 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/core/src/firebase/firestore/util/secure_random.h" + +#include "Firestore/core/src/firebase/firestore/util/config.h" + +#if HAVE_OPENSSL_RAND_H + +#include +#include + +namespace firebase { +namespace firestore { +namespace util { + +SecureRandom::result_type SecureRandom::operator()() { + result_type result; + int rc = RAND_bytes(reinterpret_cast(&result), sizeof(result)); + if (rc <= 0) { + // OpenSSL's RAND_bytes can fail if there's not enough entropy. BoringSSL + // won't fail this way. + ERR_print_errors_fp(stderr); + abort(); + } + return result; +} + +} // namespace util +} // namespace firestore +} // namespace firebase + +#endif // HAVE_OPENSSL_RAND_H diff --git a/Firestore/core/src/firebase/firestore/util/string_apple.h b/Firestore/core/src/firebase/firestore/util/string_apple.h index e1be8c331c3..3f6b814da69 100644 --- a/Firestore/core/src/firebase/firestore/util/string_apple.h +++ b/Firestore/core/src/firebase/firestore/util/string_apple.h @@ -17,8 +17,13 @@ #ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_STRING_APPLE_H_ #define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_STRING_APPLE_H_ +// Everything in this header exists for compatibility with Objective-C. +#if __OBJC__ + #import +#include "absl/strings/string_view.h" + namespace firebase { namespace firestore { namespace util { @@ -32,8 +37,30 @@ inline NSString* WrapNSStringNoCopy(const char* c_str) { freeWhenDone:NO]; } +// Translates a string_view to the equivalent NSString without making a copy. +inline NSString* WrapNSStringNoCopy(const absl::string_view str) { + return WrapNSStringNoCopy(str.data()); +} + +// Translates a string_view string to the equivalent NSString by making a copy. +inline NSString* WrapNSString(const absl::string_view str) { + return [[NSString alloc] + initWithBytes:const_cast(static_cast(str.data())) + length:str.length() + encoding:NSUTF8StringEncoding]; +} + +// Creates an absl::string_view wrapper for the contents of the given +// NSString. +inline absl::string_view MakeStringView(NSString* str) { + return absl::string_view( + [str UTF8String], [str lengthOfBytesUsingEncoding:NSUTF8StringEncoding]); +} + } // namespace util } // namespace firestore } // namespace firebase +#endif // __OBJC__ + #endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_STRING_APPLE_H_ diff --git a/Firestore/core/src/firebase/firestore/util/string_printf.cc b/Firestore/core/src/firebase/firestore/util/string_printf.cc index 60cc5644f14..9c4e31cffdc 100644 --- a/Firestore/core/src/firebase/firestore/util/string_printf.cc +++ b/Firestore/core/src/firebase/firestore/util/string_printf.cc @@ -38,7 +38,7 @@ void StringAppendV(std::string* dst, const char* format, va_list ap) { if (result < kSpaceLength) { if (result >= 0) { // Normal case -- everything fit. - dst->append(space, result); + dst->append(space, static_cast(result)); return; } @@ -49,28 +49,29 @@ void StringAppendV(std::string* dst, const char* format, va_list ap) { result = vsnprintf(nullptr, 0, format, backup_ap); va_end(backup_ap); #endif + } - if (result < 0) { - // Just an error. - return; - } + if (result < 0) { + // Just an error. + return; } + size_t result_size = static_cast(result); // Increase the buffer size to the size requested by vsnprintf, // plus one for the closing \0. size_t initial_size = dst->size(); - size_t target_size = initial_size + result; + size_t target_size = initial_size + result_size; dst->resize(target_size + 1); char* buf = &(*dst)[initial_size]; - int buf_remain = result + 1; + size_t buf_remain = result_size + 1; // Restore the va_list before we use it again va_copy(backup_ap, ap); result = vsnprintf(buf, buf_remain, format, backup_ap); va_end(backup_ap); - if (result >= 0 && result < buf_remain) { + if (result >= 0 && static_cast(result) < buf_remain) { // It fit and vsnprintf copied in directly. Resize down one to // remove the trailing \0. dst->resize(target_size); diff --git a/Firestore/core/src/firebase/firestore/util/string_printf.h b/Firestore/core/src/firebase/firestore/util/string_printf.h index d15296ec696..10dfae986b4 100644 --- a/Firestore/core/src/firebase/firestore/util/string_printf.h +++ b/Firestore/core/src/firebase/firestore/util/string_printf.h @@ -21,7 +21,7 @@ #include -#include +#include "absl/base/attributes.h" namespace firebase { namespace firestore { @@ -44,4 +44,4 @@ void StringAppendV(std::string* dst, const char* format, va_list ap); } // namespace firestore } // namespace firebase -#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_STRING_FORMAT_H_ +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_STRING_PRINTF_H_ diff --git a/Firestore/Port/string_util.cc b/Firestore/core/src/firebase/firestore/util/string_util.cc similarity index 79% rename from Firestore/Port/string_util.cc rename to Firestore/core/src/firebase/firestore/util/string_util.cc index 2587860fa02..b7f1ed9517b 100644 --- a/Firestore/Port/string_util.cc +++ b/Firestore/core/src/firebase/firestore/util/string_util.cc @@ -14,19 +14,19 @@ * limitations under the License. */ -#include "Firestore/Port/string_util.h" +#include "Firestore/core/src/firebase/firestore/util/string_util.h" -#include +namespace firebase { +namespace firestore { +namespace util { -namespace Firestore { - -std::string PrefixSuccessor(leveldb::Slice prefix) { +std::string PrefixSuccessor(absl::string_view prefix) { // We can increment the last character in the string and be done // unless that character is 255 (0xff), in which case we have to erase the // last character and increment the previous character, unless that // is 255, etc. If the string is empty or consists entirely of // 255's, we just return the empty string. - std::string limit(prefix.data(), prefix.size()); + std::string limit(prefix); while (!limit.empty()) { size_t index = limit.length() - 1; if (limit[index] == '\xff') { // char literal avoids signed/unsigned. @@ -39,7 +39,7 @@ std::string PrefixSuccessor(leveldb::Slice prefix) { return limit; } -std::string ImmediateSuccessor(leveldb::Slice s) { +std::string ImmediateSuccessor(absl::string_view s) { // Return the input string, with an additional NUL byte appended. std::string out; out.reserve(s.size() + 1); @@ -48,4 +48,6 @@ std::string ImmediateSuccessor(leveldb::Slice s) { return out; } -} // namespace Firestore +} // namespace util +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/util/string_util.h b/Firestore/core/src/firebase/firestore/util/string_util.h new file mode 100644 index 00000000000..3de177dddd8 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/util/string_util.h @@ -0,0 +1,72 @@ +/* + * 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. + */ + +// Useful string functions and so forth. This is a grab-bag file. +// +// These functions work fine for UTF-8 strings as long as you can +// consider them to be just byte strings. For example, due to the +// design of UTF-8 you do not need to worry about accidental matches, +// as long as all your inputs are valid UTF-8 (use \uHHHH, not \xHH or \oOOO). + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_STRING_UTIL_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_STRING_UTIL_H_ + +#include + +#include "absl/strings/string_view.h" + +namespace firebase { +namespace firestore { +namespace util { + +/* + * Returns the smallest lexicographically larger string of equal or smaller + * length. Returns an empty string if there is no such successor (if the input + * is empty or consists entirely of 0xff bytes). + * Useful for calculating the smallest lexicographically larger string + * that will not be prefixed by the input string. + * + * Examples: + * "a" -> "b", "aaa" -> "aab", "aa\xff" -> "ab", "\xff" -> "", "" -> "" + */ +std::string PrefixSuccessor(absl::string_view prefix); + +/* + * Returns the immediate lexicographically-following string. This is useful to + * turn an inclusive range into something that can be used with Bigtable's + * SetLimitRow(): + * + * // Inclusive range [min_element, max_element]. + * string min_element = ...; + * string max_element = ...; + * + * // Equivalent range [range_start, range_end). + * string range_start = min_element; + * string range_end = ImmediateSuccessor(max_element); + * + * WARNING: Returns the input string with a '\0' appended; if you call c_str() + * on the result, it will compare equal to s. + * + * WARNING: Transforms "" -> "\0"; this doesn't account for Bigtable's special + * treatment of "" as infinity. + */ +std::string ImmediateSuccessor(absl::string_view s); + +} // namespace util +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_STRING_UTIL_H_ diff --git a/Firestore/core/test/firebase/firestore/CMakeLists.txt b/Firestore/core/test/firebase/firestore/CMakeLists.txt new file mode 100644 index 00000000000..ed5760f3235 --- /dev/null +++ b/Firestore/core/test/firebase/firestore/CMakeLists.txt @@ -0,0 +1,21 @@ +# Copyright 2018 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. + +cc_test( + firebase_firestore_types_test + SOURCES + geo_point_test.cc + DEPENDS + firebase_firestore_types +) diff --git a/Firestore/core/test/firebase/firestore/auth/CMakeLists.txt b/Firestore/core/test/firebase/firestore/auth/CMakeLists.txt new file mode 100644 index 00000000000..a6aa2ef71cc --- /dev/null +++ b/Firestore/core/test/firebase/firestore/auth/CMakeLists.txt @@ -0,0 +1,35 @@ +# Copyright 2018 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. + +cc_test( + firebase_firestore_auth_test + SOURCES + credentials_provider_test.cc + empty_credentials_provider_test.cc + token_test.cc + user_test.cc + DEPENDS + firebase_firestore_auth +) + +if(APPLE) + cc_test( + firebase_firestore_auth_apple_test + SOURCES + firebase_credentials_provider_test.mm + DEPENDS + firebase_firestore_auth_apple + firebase_firestore_testutil + ) +endif(APPLE) diff --git a/Firestore/core/test/firebase/firestore/auth/credentials_provider_test.cc b/Firestore/core/test/firebase/firestore/auth/credentials_provider_test.cc new file mode 100644 index 00000000000..6895061b16d --- /dev/null +++ b/Firestore/core/test/firebase/firestore/auth/credentials_provider_test.cc @@ -0,0 +1,52 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/core/src/firebase/firestore/auth/credentials_provider.h" + +#include "gtest/gtest.h" + +namespace firebase { +namespace firestore { +namespace auth { + +#define UNUSED(x) (void)(x) + +TEST(CredentialsProvider, Typedef) { + TokenListener token_listener = [](Token token, const int64_t error_code, + const absl::string_view error_msg) { + UNUSED(token); + UNUSED(error_code); + UNUSED(error_msg); + }; + EXPECT_NE(nullptr, token_listener); + EXPECT_TRUE(token_listener); + + token_listener = nullptr; + EXPECT_EQ(nullptr, token_listener); + EXPECT_FALSE(token_listener); + + UserChangeListener user_change_listener = [](User user) { UNUSED(user); }; + EXPECT_NE(nullptr, user_change_listener); + EXPECT_TRUE(user_change_listener); + + user_change_listener = nullptr; + EXPECT_EQ(nullptr, user_change_listener); + EXPECT_FALSE(user_change_listener); +} + +} // namespace auth +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/test/firebase/firestore/auth/empty_credentials_provider_test.cc b/Firestore/core/test/firebase/firestore/auth/empty_credentials_provider_test.cc new file mode 100644 index 00000000000..3b487f38afd --- /dev/null +++ b/Firestore/core/test/firebase/firestore/auth/empty_credentials_provider_test.cc @@ -0,0 +1,51 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.h" + +#include "gtest/gtest.h" + +namespace firebase { +namespace firestore { +namespace auth { + +TEST(EmptyCredentialsProvider, GetToken) { + EmptyCredentialsProvider credentials_provider; + credentials_provider.GetToken( + /*force_refresh=*/true, [](Token token, const int64_t error_code, + const absl::string_view error_msg) { + EXPECT_FALSE(token.is_valid()); + const User& user = token.user(); + EXPECT_EQ("", user.uid()); + EXPECT_FALSE(user.is_authenticated()); + EXPECT_EQ(FirestoreErrorCode::Ok, error_code); + EXPECT_EQ("", error_msg); + }); +} + +TEST(EmptyCredentialsProvider, SetListener) { + EmptyCredentialsProvider credentials_provider; + credentials_provider.SetUserChangeListener([](User user) { + EXPECT_EQ("", user.uid()); + EXPECT_FALSE(user.is_authenticated()); + }); + + credentials_provider.SetUserChangeListener(nullptr); +} + +} // namespace auth +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/test/firebase/firestore/auth/firebase_credentials_provider_test.mm b/Firestore/core/test/firebase/firestore/auth/firebase_credentials_provider_test.mm new file mode 100644 index 00000000000..3660d53f9f3 --- /dev/null +++ b/Firestore/core/test/firebase/firestore/auth/firebase_credentials_provider_test.mm @@ -0,0 +1,86 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.h" + +#import +#import +#import + +#include "Firestore/core/src/firebase/firestore/util/string_apple.h" +#include "Firestore/core/test/firebase/firestore/testutil/app_testing.h" + +#include "gtest/gtest.h" + +namespace firebase { +namespace firestore { +namespace auth { + +FIRApp* AppWithFakeUid(NSString* _Nullable uid) { + FIRApp* app = testutil::AppForUnitTesting(); + app.getUIDImplementation = ^NSString* { + return uid; + }; + return app; +} + +TEST(FirebaseCredentialsProviderTest, GetTokenUnauthenticated) { + FIRApp* app = AppWithFakeUid(nil); + + FirebaseCredentialsProvider credentials_provider(app); + credentials_provider.GetToken( + /*force_refresh=*/true, [](Token token, const int64_t error_code, + const absl::string_view error_msg) { + EXPECT_EQ("", token.token()); + const User& user = token.user(); + EXPECT_EQ("", user.uid()); + EXPECT_FALSE(user.is_authenticated()); + EXPECT_EQ(FirestoreErrorCode::Ok, error_code) << error_code; + EXPECT_EQ("", error_msg) << error_msg; + }); +} + +TEST(FirebaseCredentialsProviderTest, GetToken) { + FIRApp* app = AppWithFakeUid(@"fake uid"); + + FirebaseCredentialsProvider credentials_provider(app); + credentials_provider.GetToken( + /*force_refresh=*/true, [](Token token, const int64_t error_code, + const absl::string_view error_msg) { + EXPECT_EQ("", token.token()); + const User& user = token.user(); + EXPECT_EQ("fake uid", user.uid()); + EXPECT_TRUE(user.is_authenticated()); + EXPECT_EQ(FirestoreErrorCode::Ok, error_code) << error_code; + EXPECT_EQ("", error_msg) << error_msg; + }); +} + +TEST(FirebaseCredentialsProviderTest, SetListener) { + FIRApp* app = AppWithFakeUid(@"fake uid"); + + FirebaseCredentialsProvider credentials_provider(app); + credentials_provider.SetUserChangeListener([](User user) { + EXPECT_EQ("fake uid", user.uid()); + EXPECT_TRUE(user.is_authenticated()); + }); + + credentials_provider.SetUserChangeListener(nullptr); +} + +} // namespace auth +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/test/firebase/firestore/auth/token_test.cc b/Firestore/core/test/firebase/firestore/auth/token_test.cc new file mode 100644 index 00000000000..8f784d65197 --- /dev/null +++ b/Firestore/core/test/firebase/firestore/auth/token_test.cc @@ -0,0 +1,41 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/core/src/firebase/firestore/auth/token.h" + +#include "gtest/gtest.h" + +namespace firebase { +namespace firestore { +namespace auth { + +TEST(Token, Getter) { + Token token("token", User("abc")); + EXPECT_EQ("token", token.token()); + EXPECT_EQ(User("abc"), token.user()); + EXPECT_TRUE(token.is_valid()); +} + +TEST(Token, InvalidToken) { + const Token& token = Token::Invalid(); + EXPECT_ANY_THROW(token.token()); + EXPECT_EQ(User::Unauthenticated(), token.user()); + EXPECT_FALSE(token.is_valid()); +} + +} // namespace auth +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/test/firebase/firestore/auth/user_test.cc b/Firestore/core/test/firebase/firestore/auth/user_test.cc new file mode 100644 index 00000000000..7277283531b --- /dev/null +++ b/Firestore/core/test/firebase/firestore/auth/user_test.cc @@ -0,0 +1,59 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/core/src/firebase/firestore/auth/user.h" + +#include "gtest/gtest.h" + +namespace firebase { +namespace firestore { +namespace auth { + +TEST(User, Getter) { + User anonymous; + EXPECT_EQ("", anonymous.uid()); + EXPECT_FALSE(anonymous.is_authenticated()); + + User signin("abc"); + EXPECT_EQ("abc", signin.uid()); + EXPECT_TRUE(signin.is_authenticated()); + + User copy; + copy = signin; + EXPECT_EQ(signin, copy); +} + +TEST(User, Unauthenticated) { + User unauthenticated = User::Unauthenticated(); + EXPECT_EQ("", unauthenticated.uid()); + EXPECT_FALSE(unauthenticated.is_authenticated()); +} + +TEST(User, Comparison) { + EXPECT_EQ(User(), User()); + EXPECT_EQ(User("abc"), User("abc")); + EXPECT_NE(User(), User("abc")); + EXPECT_NE(User("abc"), User("xyz")); +} + +TEST(User, Hash) { + const HashUser hash; + EXPECT_EQ(hash(User("abc")), hash(User("abc"))); +} + +} // namespace auth +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/test/firebase/firestore/core/CMakeLists.txt b/Firestore/core/test/firebase/firestore/core/CMakeLists.txt new file mode 100644 index 00000000000..5b4c55aea70 --- /dev/null +++ b/Firestore/core/test/firebase/firestore/core/CMakeLists.txt @@ -0,0 +1,22 @@ +# Copyright 2018 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. + +cc_test( + firebase_firestore_core_test + SOURCES + database_info_test.cc + target_id_generator_test.cc + DEPENDS + firebase_firestore_core +) diff --git a/Firestore/core/test/firebase/firestore/core/database_info_test.cc b/Firestore/core/test/firebase/firestore/core/database_info_test.cc new file mode 100644 index 00000000000..4c9691cd243 --- /dev/null +++ b/Firestore/core/test/firebase/firestore/core/database_info_test.cc @@ -0,0 +1,48 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/core/src/firebase/firestore/core/database_info.h" + +#include "gtest/gtest.h" + +namespace firebase { +namespace firestore { +namespace core { + +using firebase::firestore::model::DatabaseId; + +TEST(DatabaseInfo, Getter) { + DatabaseInfo info(DatabaseId("project id", "database id"), "key", + "http://host", true); + EXPECT_EQ(DatabaseId("project id", "database id"), info.database_id()); + EXPECT_EQ("key", info.persistence_key()); + EXPECT_EQ("http://host", info.host()); + EXPECT_TRUE(info.ssl_enabled()); +} + +TEST(DatabaseInfo, DefaultDatabase) { + DatabaseInfo info(DatabaseId("project id", DatabaseId::kDefault), "key", + "http://host", false); + EXPECT_EQ("project id", info.database_id().project_id()); + EXPECT_EQ("(default)", info.database_id().database_id()); + EXPECT_EQ("key", info.persistence_key()); + EXPECT_EQ("http://host", info.host()); + EXPECT_FALSE(info.ssl_enabled()); +} + +} // namespace core +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/test/firebase/firestore/core/target_id_generator_test.cc b/Firestore/core/test/firebase/firestore/core/target_id_generator_test.cc new file mode 100644 index 00000000000..7eca335f1f4 --- /dev/null +++ b/Firestore/core/test/firebase/firestore/core/target_id_generator_test.cc @@ -0,0 +1,80 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/core/src/firebase/firestore/core/target_id_generator.h" + +#include "gtest/gtest.h" + +namespace firebase { +namespace firestore { +namespace core { + +TEST(TargetIdGenerator, Constructor) { + TargetIdGenerator local_store_generator = + TargetIdGenerator::LocalStoreTargetIdGenerator(0); + TargetIdGenerator sync_engine_generator = + TargetIdGenerator::SyncEngineTargetIdGenerator(0); + EXPECT_EQ(TargetIdGeneratorId::LocalStore, + local_store_generator.generator_id()); + EXPECT_EQ(2, local_store_generator.NextId()); + EXPECT_EQ(TargetIdGeneratorId::SyncEngine, + sync_engine_generator.generator_id()); + EXPECT_EQ(1, sync_engine_generator.NextId()); +} + +TEST(TargetIdGenerator, SkipPast) { + EXPECT_EQ(1, TargetIdGenerator::SyncEngineTargetIdGenerator(-1).NextId()); + EXPECT_EQ(3, TargetIdGenerator::SyncEngineTargetIdGenerator(2).NextId()); + EXPECT_EQ(5, TargetIdGenerator::SyncEngineTargetIdGenerator(4).NextId()); + + for (int i = 4; i < 12; ++i) { + TargetIdGenerator a = TargetIdGenerator::LocalStoreTargetIdGenerator(i); + TargetIdGenerator b = TargetIdGenerator::SyncEngineTargetIdGenerator(i); + EXPECT_EQ((i + 2) & ~1, a.NextId()); + EXPECT_EQ((i + 1) | 1, b.NextId()); + } + + EXPECT_EQ(13, TargetIdGenerator::SyncEngineTargetIdGenerator(12).NextId()); + EXPECT_EQ(24, TargetIdGenerator::LocalStoreTargetIdGenerator(22).NextId()); +} + +TEST(TargetIdGenerator, Increment) { + TargetIdGenerator a = TargetIdGenerator::LocalStoreTargetIdGenerator(0); + EXPECT_EQ(2, a.NextId()); + EXPECT_EQ(4, a.NextId()); + EXPECT_EQ(6, a.NextId()); + + TargetIdGenerator b = TargetIdGenerator::LocalStoreTargetIdGenerator(46); + EXPECT_EQ(48, b.NextId()); + EXPECT_EQ(50, b.NextId()); + EXPECT_EQ(52, b.NextId()); + EXPECT_EQ(54, b.NextId()); + + TargetIdGenerator c = TargetIdGenerator::SyncEngineTargetIdGenerator(0); + EXPECT_EQ(1, c.NextId()); + EXPECT_EQ(3, c.NextId()); + EXPECT_EQ(5, c.NextId()); + + TargetIdGenerator d = TargetIdGenerator::SyncEngineTargetIdGenerator(46); + EXPECT_EQ(47, d.NextId()); + EXPECT_EQ(49, d.NextId()); + EXPECT_EQ(51, d.NextId()); + EXPECT_EQ(53, d.NextId()); +} + +} // namespace core +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/test/firebase/firestore/geo_point_test.cc b/Firestore/core/test/firebase/firestore/geo_point_test.cc new file mode 100644 index 00000000000..bd8d76f33d6 --- /dev/null +++ b/Firestore/core/test/firebase/firestore/geo_point_test.cc @@ -0,0 +1,41 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/core/include/firebase/firestore/geo_point.h" + +#include "gtest/gtest.h" + +namespace firebase { +namespace firestore { + +TEST(GeoPoint, Getter) { + const GeoPoint zero; + EXPECT_EQ(0, zero.latitude()); + EXPECT_EQ(0, zero.longitude()); + + const GeoPoint point{12, 34}; + EXPECT_EQ(12, point.latitude()); + EXPECT_EQ(34, point.longitude()); +} + +TEST(GeoPoint, Comparison) { + EXPECT_EQ(GeoPoint(12, 34), GeoPoint(12, 34)); + EXPECT_LT(GeoPoint(12, 34), GeoPoint(34, 12)); + EXPECT_LT(GeoPoint(12, 34), GeoPoint(12, 56)); +} + +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/test/firebase/firestore/immutable/CMakeLists.txt b/Firestore/core/test/firebase/firestore/immutable/CMakeLists.txt new file mode 100644 index 00000000000..e7b0b6e0b56 --- /dev/null +++ b/Firestore/core/test/firebase/firestore/immutable/CMakeLists.txt @@ -0,0 +1,22 @@ +# Copyright 2018 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. + +cc_test( + firebase_firestore_immutable_test + SOURCES + array_sorted_map_test.cc + DEPENDS + firebase_firestore_immutable + firebase_firestore_util +) diff --git a/Firestore/core/test/firebase/firestore/immutable/array_sorted_map_test.cc b/Firestore/core/test/firebase/firestore/immutable/array_sorted_map_test.cc new file mode 100644 index 00000000000..3ec1e64b6a6 --- /dev/null +++ b/Firestore/core/test/firebase/firestore/immutable/array_sorted_map_test.cc @@ -0,0 +1,326 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/core/src/firebase/firestore/immutable/array_sorted_map.h" + +#include +#include + +#include "Firestore/core/src/firebase/firestore/util/secure_random.h" +#include "gtest/gtest.h" + +namespace firebase { +namespace firestore { +namespace immutable { + +typedef ArraySortedMap IntMap; +constexpr IntMap::size_type kFixedSize = IntMap::kFixedSize; + +template +testing::AssertionResult NotFound(const ArraySortedMap& set, + const K& key) { + auto found = set.find(key); + if (found == set.end()) { + return testing::AssertionSuccess(); + } else { + return testing::AssertionFailure() + << "Should not have found (" << found->first << ", " << found->second + << ") @ " << found; + } +} + +template +testing::AssertionResult Found(const ArraySortedMap& map, + const K& key, + const V& expected) { + auto found = map.find(key); + if (found == map.end()) { + return testing::AssertionFailure() << "Did not find key " << key; + } + if (found->second == expected) { + return testing::AssertionSuccess(); + } else { + return testing::AssertionFailure() << "Found entry was (" << found->first + << ", " << found->second << ")"; + } +} + +/** + * Creates a vector containing a sequence of integers from the given starting + * element up to, but not including, the given end element, with values + * incremented by the given step. + * + * If step is negative the sequence is in descending order (but still starting + * at start and ending before end). + */ +std::vector Sequence(int start, int end, int step = 1) { + std::vector result; + if (step > 0) { + for (int i = start; i < end; i += step) { + result.push_back(i); + } + } else { + for (int i = start; i > end; i += step) { + result.push_back(i); + } + } + return result; +} + +/** + * Creates a vector containing a sequence of integers with the given number of + * elements, from zero up to, but not including the given value. + */ +std::vector Sequence(int num_elements) { + return Sequence(0, num_elements); +} + +/** + * Creates a copy of the given vector with contents shuffled randomly. + */ +std::vector Shuffled(const std::vector& values) { + std::vector result(values); + util::SecureRandom rng; + std::shuffle(result.begin(), result.end(), rng); + return result; +} + +/** + * Creates a copy of the given vector with contents sorted. + */ +std::vector Sorted(const std::vector& values) { + std::vector result(values); + std::sort(result.begin(), result.end()); + return result; +} + +/** + * Creates a vector of pairs where each pair has the same first and second + * corresponding to an element in the given vector. + */ +std::vector> Pairs(const std::vector& values) { + std::vector> result; + for (auto&& value : values) { + result.emplace_back(value, value); + } + return result; +} + +/** + * Creates an ArraySortedMap by inserting a pair for each value in the vector. + * Each pair will have the same key and value. + */ +IntMap ToMap(const std::vector& values) { + IntMap result; + for (auto&& value : values) { + result = result.insert(value, value); + } + return result; +} + +/** + * Appends the contents of the given container to a new vector. + */ +template +std::vector Append(const Container& container) { + std::vector result; + result.insert(result.begin(), container.begin(), container.end()); + return result; +} + +// TODO(wilhuff): ReverseTraversal + +#define ASSERT_SEQ_EQ(x, y) ASSERT_EQ((x), Append(y)); +#define EXPECT_SEQ_EQ(x, y) EXPECT_EQ((x), Append(y)); + +TEST(ArraySortedMap, SearchForSpecificKey) { + IntMap map{{1, 3}, {2, 4}}; + + ASSERT_TRUE(Found(map, 1, 3)); + ASSERT_TRUE(Found(map, 2, 4)); + ASSERT_TRUE(NotFound(map, 3)); +} + +TEST(ArraySortedMap, RemoveKeyValuePair) { + IntMap map{{1, 3}, {2, 4}}; + + IntMap new_map = map.erase(1); + ASSERT_TRUE(Found(new_map, 2, 4)); + ASSERT_TRUE(NotFound(new_map, 1)); + + // Make sure the original one is not mutated + ASSERT_TRUE(Found(map, 1, 3)); + ASSERT_TRUE(Found(map, 2, 4)); +} + +TEST(ArraySortedMap, MoreRemovals) { + IntMap map = IntMap() + .insert(1, 1) + .insert(50, 50) + .insert(3, 3) + .insert(4, 4) + .insert(7, 7) + .insert(9, 9) + .insert(1, 20) + .insert(18, 18) + .insert(3, 2) + .insert(4, 71) + .insert(7, 42) + .insert(9, 88); + + ASSERT_TRUE(Found(map, 7, 42)); + ASSERT_TRUE(Found(map, 3, 2)); + ASSERT_TRUE(Found(map, 1, 20)); + + IntMap s1 = map.erase(7); + IntMap s2 = map.erase(3); + IntMap s3 = map.erase(1); + + ASSERT_TRUE(NotFound(s1, 7)); + ASSERT_TRUE(Found(s1, 3, 2)); + ASSERT_TRUE(Found(s1, 1, 20)); + + ASSERT_TRUE(Found(s2, 7, 42)); + ASSERT_TRUE(NotFound(s2, 3)); + ASSERT_TRUE(Found(s2, 1, 20)); + + ASSERT_TRUE(Found(s3, 7, 42)); + ASSERT_TRUE(Found(s3, 3, 2)); + ASSERT_TRUE(NotFound(s3, 1)); +} + +TEST(ArraySortedMap, RemovesMiddle) { + IntMap map{{1, 1}, {2, 2}, {3, 3}}; + ASSERT_TRUE(Found(map, 1, 1)); + ASSERT_TRUE(Found(map, 2, 2)); + ASSERT_TRUE(Found(map, 3, 3)); + + IntMap s1 = map.erase(2); + ASSERT_TRUE(Found(s1, 1, 1)); + ASSERT_TRUE(NotFound(s1, 2)); + ASSERT_TRUE(Found(s1, 3, 3)); +} + +TEST(ArraySortedMap, Increasing) { + auto total = static_cast(kFixedSize); + IntMap map; + + for (int i = 0; i < total; i++) { + map = map.insert(i, i); + } + ASSERT_EQ(kFixedSize, map.size()); + + for (int i = 0; i < total; i++) { + map = map.erase(i); + } + ASSERT_EQ(0u, map.size()); +} + +TEST(ArraySortedMap, Override) { + IntMap map = IntMap().insert(10, 10).insert(10, 8); + + ASSERT_TRUE(Found(map, 10, 8)); + ASSERT_FALSE(Found(map, 10, 10)); +} + +TEST(ArraySortedMap, ChecksSize) { + std::vector to_insert = Sequence(kFixedSize); + IntMap map = ToMap(to_insert); + + // Replacing an existing entry should not hit increase size + map = map.insert(5, 10); + + int next = kFixedSize; + ASSERT_DEATH_IF_SUPPORTED(map.insert(next, next), "new_size <= fixed_size"); +} + +TEST(ArraySortedMap, Empty) { + IntMap map = IntMap().insert(10, 10).erase(10); + EXPECT_TRUE(map.empty()); + EXPECT_EQ(0u, map.size()); + EXPECT_TRUE(NotFound(map, 1)); + EXPECT_TRUE(NotFound(map, 10)); +} + +TEST(ArraySortedMap, EmptyGet) { + IntMap map; + EXPECT_TRUE(NotFound(map, 10)); +} + +TEST(ArraySortedMap, EmptySize) { + IntMap map; + EXPECT_TRUE(map.empty()); + EXPECT_EQ(0u, map.size()); +} + +TEST(ArraySortedMap, EmptyRemoval) { + IntMap map; + IntMap new_map = map.erase(1); + EXPECT_TRUE(new_map.empty()); + EXPECT_EQ(0u, new_map.size()); + EXPECT_TRUE(NotFound(new_map, 1)); +} + +TEST(ArraySortedMap, InsertionAndRemovalOfMaxItems) { + auto expected_size = kFixedSize; + int n = static_cast(expected_size); + std::vector to_insert = Shuffled(Sequence(n)); + std::vector to_remove = Shuffled(to_insert); + + // Add them to the map + IntMap map = ToMap(to_insert); + ASSERT_EQ(expected_size, map.size()) + << "Check if all N objects are in the map"; + + // check the order is correct + ASSERT_SEQ_EQ(Pairs(Sorted(to_insert)), map); + + for (int i : to_remove) { + map = map.erase(i); + } + ASSERT_EQ(0u, map.size()) << "Check we removed all of the items"; +} + +TEST(ArraySortedMap, BalanceProblem) { + std::vector to_insert{1, 7, 8, 5, 2, 6, 4, 0, 3}; + + IntMap map = ToMap(to_insert); + ASSERT_SEQ_EQ(Pairs(Sorted(to_insert)), map); +} + +// TODO(wilhuff): Iterators + +// TODO(wilhuff): IndexOf + +TEST(ArraySortedMap, AvoidsCopying) { + IntMap map = IntMap().insert(10, 20); + auto found = map.find(10); + ASSERT_NE(found, map.end()); + EXPECT_EQ(20, found->second); + + // Verify that inserting something with equal keys and values just returns + // the same underlying array. + IntMap duped = map.insert(10, 20); + auto duped_found = duped.find(10); + + // If everything worked correctly, the backing array should not have been + // copied and the pointer to the entry with 10 as key should be the same. + EXPECT_EQ(found, duped_found); +} + +} // namespace immutable +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/test/firebase/firestore/model/CMakeLists.txt b/Firestore/core/test/firebase/firestore/model/CMakeLists.txt new file mode 100644 index 00000000000..0d581bcceae --- /dev/null +++ b/Firestore/core/test/firebase/firestore/model/CMakeLists.txt @@ -0,0 +1,30 @@ +# Copyright 2018 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. + +cc_test( + firebase_firestore_model_test + SOURCES + database_id_test.cc + document_key_test.cc + document_test.cc + field_path_test.cc + field_value_test.cc + maybe_document_test.cc + no_document_test.cc + resource_path_test.cc + snapshot_version_test.cc + timestamp_test.cc + DEPENDS + firebase_firestore_model +) diff --git a/Firestore/core/test/firebase/firestore/model/database_id_test.cc b/Firestore/core/test/firebase/firestore/model/database_id_test.cc new file mode 100644 index 00000000000..c00415aa2dd --- /dev/null +++ b/Firestore/core/test/firebase/firestore/model/database_id_test.cc @@ -0,0 +1,47 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/core/src/firebase/firestore/model/database_id.h" + +#include "gtest/gtest.h" + +namespace firebase { +namespace firestore { +namespace model { + +TEST(DatabaseId, Constructor) { + DatabaseId id("p", "d"); + EXPECT_EQ("p", id.project_id()); + EXPECT_EQ("d", id.database_id()); + EXPECT_FALSE(id.IsDefaultDatabase()); +} + +TEST(DatabaseId, DefaultDb) { + DatabaseId id("p", DatabaseId::kDefault); + EXPECT_EQ("p", id.project_id()); + EXPECT_EQ("(default)", id.database_id()); + EXPECT_TRUE(id.IsDefaultDatabase()); +} + +TEST(DatabaseId, Comparison) { + EXPECT_LT(DatabaseId("a", "b"), DatabaseId("b", "a")); + EXPECT_LT(DatabaseId("a", "b"), DatabaseId("a", "c")); + EXPECT_EQ(DatabaseId("a", "b"), DatabaseId("a", "b")); +} + +} // namespace model +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/test/firebase/firestore/model/document_key_test.cc b/Firestore/core/test/firebase/firestore/model/document_key_test.cc new file mode 100644 index 00000000000..0e0df2dab9d --- /dev/null +++ b/Firestore/core/test/firebase/firestore/model/document_key_test.cc @@ -0,0 +1,153 @@ +/* + * Copyright 2018 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. + */ + +#include +#include +#include +#include + +#include "Firestore/core/src/firebase/firestore/model/document_key.h" +#include "Firestore/core/src/firebase/firestore/model/resource_path.h" +#include "gtest/gtest.h" + +namespace firebase { +namespace firestore { +namespace model { + +TEST(DocumentKey, Constructor_Empty) { + const DocumentKey default_key; + EXPECT_TRUE(default_key.path().empty()); + + const auto& empty_key = DocumentKey::Empty(); + const auto& another_empty_key = DocumentKey::Empty(); + EXPECT_EQ(default_key, empty_key); + EXPECT_EQ(empty_key, another_empty_key); + EXPECT_EQ(&empty_key, &another_empty_key); +} + +TEST(DocumentKey, Constructor_FromPath) { + ResourcePath path{"rooms", "firestore", "messages", "1"}; + const DocumentKey key_from_path_copy{path}; + // path shouldn't have been moved from. + EXPECT_FALSE(path.empty()); + EXPECT_EQ(key_from_path_copy.path(), path); + + const DocumentKey key_from_moved_path{std::move(path)}; + EXPECT_TRUE(path.empty()); + EXPECT_FALSE(key_from_moved_path.path().empty()); + EXPECT_EQ(key_from_path_copy.path(), key_from_moved_path.path()); +} + +TEST(DocumentKey, CopyAndMove) { + DocumentKey key({"rooms", "firestore", "messages", "1"}); + const std::string path_string = "rooms/firestore/messages/1"; + EXPECT_EQ(path_string, key.path().CanonicalString()); + + DocumentKey copied = key; + EXPECT_EQ(path_string, copied.path().CanonicalString()); + EXPECT_EQ(key, copied); + + const DocumentKey moved = std::move(key); + EXPECT_EQ(path_string, moved.path().CanonicalString()); + EXPECT_NE(key, moved); + EXPECT_TRUE(key.path().empty()); + + // Reassignment. + + key = copied; + EXPECT_EQ(copied, key); + EXPECT_EQ(path_string, key.path().CanonicalString()); + + key = {}; + EXPECT_TRUE(key.path().empty()); + key = std::move(copied); + EXPECT_NE(copied, key); + EXPECT_TRUE(copied.path().empty()); + EXPECT_EQ(path_string, key.path().CanonicalString()); +} + +TEST(DocumentKey, Constructor_StaticFactory) { + const auto key_from_segments = + DocumentKey::FromSegments({"rooms", "firestore", "messages", "1"}); + const std::string path_string = "rooms/firestore/messages/1"; + const auto key_from_string = DocumentKey::FromPathString(path_string); + EXPECT_EQ(path_string, key_from_string.path().CanonicalString()); + EXPECT_EQ(path_string, key_from_segments.path().CanonicalString()); + EXPECT_EQ(key_from_segments, key_from_string); + + const auto from_empty_path = DocumentKey::FromPathString(""); + EXPECT_EQ(from_empty_path, DocumentKey{}); +} + +TEST(DocumentKey, Constructor_BadArguments) { + ASSERT_ANY_THROW(DocumentKey(ResourcePath{"foo"})); + ASSERT_ANY_THROW(DocumentKey(ResourcePath{"foo", "bar", "baz"})); + + ASSERT_ANY_THROW(DocumentKey::FromSegments({"foo"})); + ASSERT_ANY_THROW(DocumentKey::FromSegments({"foo", "bar", "baz"})); + + ASSERT_ANY_THROW(DocumentKey::FromPathString("invalid")); + ASSERT_ANY_THROW(DocumentKey::FromPathString("invalid//string")); + ASSERT_ANY_THROW(DocumentKey::FromPathString("invalid/key/path")); +} + +TEST(DocumentKey, IsDocumentKey) { + EXPECT_TRUE(DocumentKey::IsDocumentKey({})); + EXPECT_FALSE(DocumentKey::IsDocumentKey({"foo"})); + EXPECT_TRUE(DocumentKey::IsDocumentKey({"foo", "bar"})); + EXPECT_FALSE(DocumentKey::IsDocumentKey({"foo", "bar", "baz"})); +} + +TEST(DocumentKey, Comparison) { + const DocumentKey abcd({"a", "b", "c", "d"}); + const DocumentKey abcd_too({"a", "b", "c", "d"}); + const DocumentKey xyzw({"x", "y", "z", "w"}); + EXPECT_EQ(abcd, abcd_too); + EXPECT_NE(abcd, xyzw); + + const DocumentKey empty; + const DocumentKey a({"a", "a"}); + const DocumentKey b({"b", "b"}); + const DocumentKey ab({"a", "a", "b", "b"}); + + EXPECT_FALSE(empty < empty); + EXPECT_TRUE(empty <= empty); + EXPECT_TRUE(empty < a); + EXPECT_TRUE(empty <= a); + EXPECT_TRUE(a > empty); + EXPECT_TRUE(a >= empty); + + EXPECT_FALSE(a < a); + EXPECT_TRUE(a <= a); + EXPECT_FALSE(a > a); + EXPECT_TRUE(a >= a); + EXPECT_TRUE(a == a); + EXPECT_FALSE(a != a); + + EXPECT_TRUE(a < b); + EXPECT_TRUE(a <= b); + EXPECT_TRUE(b > a); + EXPECT_TRUE(b >= a); + + EXPECT_TRUE(a < ab); + EXPECT_TRUE(a <= ab); + EXPECT_TRUE(ab > a); + EXPECT_TRUE(ab >= a); +} + +} // namespace model +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/test/firebase/firestore/model/document_test.cc b/Firestore/core/test/firebase/firestore/model/document_test.cc new file mode 100644 index 00000000000..6b72360a062 --- /dev/null +++ b/Firestore/core/test/firebase/firestore/model/document_test.cc @@ -0,0 +1,76 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/core/src/firebase/firestore/model/document.h" + +#include "absl/strings/string_view.h" +#include "gtest/gtest.h" + +namespace firebase { +namespace firestore { +namespace model { + +namespace { + +inline Document MakeDocument(const absl::string_view data, + const absl::string_view path, + const Timestamp& timestamp, + bool has_local_mutations) { + return Document(FieldValue::ObjectValue( + {{"field", FieldValue::StringValue(data.data())}}), + DocumentKey::FromPathString(path.data()), + SnapshotVersion(timestamp), has_local_mutations); +} + +} // anonymous namespace + +TEST(Document, Getter) { + const Document& doc = + MakeDocument("foo", "i/am/a/path", Timestamp(123, 456), true); + EXPECT_EQ(MaybeDocument::Type::Document, doc.type()); + EXPECT_EQ( + FieldValue::ObjectValue({{"field", FieldValue::StringValue("foo")}}), + doc.data()); + EXPECT_EQ(DocumentKey::FromPathString("i/am/a/path"), doc.key()); + EXPECT_EQ(SnapshotVersion(Timestamp(123, 456)), doc.version()); + EXPECT_TRUE(doc.has_local_mutations()); +} + +TEST(Document, Comparison) { + EXPECT_EQ(MakeDocument("foo", "i/am/a/path", Timestamp(123, 456), true), + MakeDocument("foo", "i/am/a/path", Timestamp(123, 456), true)); + EXPECT_NE(MakeDocument("foo", "i/am/a/path", Timestamp(123, 456), true), + MakeDocument("bar", "i/am/a/path", Timestamp(123, 456), true)); + EXPECT_NE( + MakeDocument("foo", "i/am/a/path", Timestamp(123, 456), true), + MakeDocument("foo", "i/am/another/path", Timestamp(123, 456), true)); + EXPECT_NE(MakeDocument("foo", "i/am/a/path", Timestamp(123, 456), true), + MakeDocument("foo", "i/am/a/path", Timestamp(456, 123), true)); + EXPECT_NE(MakeDocument("foo", "i/am/a/path", Timestamp(123, 456), true), + MakeDocument("foo", "i/am/a/path", Timestamp(123, 456), false)); + + // Document and MaybeDocument will not equal. In particular, Document and + // NoDocument will not equal, which I won't test here. + EXPECT_NE(Document(FieldValue::ObjectValue({}), + DocumentKey::FromPathString("same/path"), + SnapshotVersion(Timestamp()), false), + MaybeDocument(DocumentKey::FromPathString("same/path"), + SnapshotVersion(Timestamp()))); +} + +} // namespace model +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/test/firebase/firestore/model/field_path_test.cc b/Firestore/core/test/firebase/firestore/model/field_path_test.cc new file mode 100644 index 00000000000..a5ae3b20099 --- /dev/null +++ b/Firestore/core/test/firebase/firestore/model/field_path_test.cc @@ -0,0 +1,277 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/core/src/firebase/firestore/model/field_path.h" + +#include +#include +#include + +#include "gtest/gtest.h" + +namespace firebase { +namespace firestore { +namespace model { + +TEST(FieldPath, Constructors) { + const FieldPath empty_path; + EXPECT_TRUE(empty_path.empty()); + EXPECT_EQ(0u, empty_path.size()); + EXPECT_TRUE(empty_path.begin() == empty_path.end()); + + const FieldPath path_from_list = {"rooms", "Eros", "messages"}; + EXPECT_FALSE(path_from_list.empty()); + EXPECT_EQ(3u, path_from_list.size()); + EXPECT_TRUE(path_from_list.begin() + 3 == path_from_list.end()); + + std::vector segments{"rooms", "Eros", "messages"}; + const FieldPath path_from_segments{segments.begin(), segments.end()}; + EXPECT_FALSE(path_from_segments.empty()); + EXPECT_EQ(3u, path_from_segments.size()); + EXPECT_TRUE(path_from_segments.begin() + 3 == path_from_segments.end()); + + FieldPath copied = path_from_list; + EXPECT_EQ(path_from_list, copied); + const FieldPath moved = std::move(copied); + EXPECT_EQ(path_from_list, moved); + EXPECT_NE(copied, moved); + EXPECT_EQ(empty_path, copied); +} + +TEST(FieldPath, Indexing) { + const FieldPath path{"rooms", "Eros", "messages"}; + + EXPECT_EQ(path.first_segment(), "rooms"); + EXPECT_EQ(path[0], "rooms"); + + EXPECT_EQ(path[1], "Eros"); + + EXPECT_EQ(path[2], "messages"); + EXPECT_EQ(path.last_segment(), "messages"); +} + +TEST(FieldPath, PopFirst) { + const FieldPath abc{"rooms", "Eros", "messages"}; + const FieldPath bc{"Eros", "messages"}; + const FieldPath c{"messages"}; + const FieldPath empty; + const FieldPath abc_dupl{"rooms", "Eros", "messages"}; + + EXPECT_NE(empty, c); + EXPECT_NE(c, bc); + EXPECT_NE(bc, abc); + + EXPECT_EQ(bc, abc.PopFirst()); + EXPECT_EQ(c, abc.PopFirst(2)); + EXPECT_EQ(empty, abc.PopFirst(3)); + EXPECT_EQ(abc_dupl, abc); +} + +TEST(FieldPath, PopLast) { + const FieldPath abc{"rooms", "Eros", "messages"}; + const FieldPath ab{"rooms", "Eros"}; + const FieldPath a{"rooms"}; + const FieldPath empty; + const FieldPath abc_dupl{"rooms", "Eros", "messages"}; + + EXPECT_EQ(ab, abc.PopLast()); + EXPECT_EQ(a, abc.PopLast().PopLast()); + EXPECT_EQ(empty, abc.PopLast().PopLast().PopLast()); +} + +TEST(FieldPath, Concatenation) { + const FieldPath path; + const FieldPath a{"rooms"}; + const FieldPath ab{"rooms", "Eros"}; + const FieldPath abc{"rooms", "Eros", "messages"}; + + EXPECT_EQ(a, path.Append("rooms")); + EXPECT_EQ(ab, path.Append("rooms").Append("Eros")); + EXPECT_EQ(abc, path.Append("rooms").Append("Eros").Append("messages")); + EXPECT_EQ(abc, path.Append(FieldPath{"rooms", "Eros", "messages"})); + EXPECT_EQ(abc, path.Append({"rooms", "Eros", "messages"})); + + const FieldPath bcd{"Eros", "messages", "this_week"}; + EXPECT_EQ(bcd, abc.PopFirst().Append("this_week")); +} + +TEST(FieldPath, Comparison) { + const FieldPath abc{"a", "b", "c"}; + const FieldPath abc2{"a", "b", "c"}; + const FieldPath xyz{"x", "y", "z"}; + EXPECT_EQ(abc, abc2); + EXPECT_NE(abc, xyz); + + const FieldPath empty; + const FieldPath a{"a"}; + const FieldPath b{"b"}; + const FieldPath ab{"a", "b"}; + + EXPECT_TRUE(empty < a); + EXPECT_TRUE(a < b); + EXPECT_TRUE(a < ab); + + EXPECT_TRUE(a > empty); + EXPECT_TRUE(b > a); + EXPECT_TRUE(ab > a); +} + +TEST(FieldPath, IsPrefixOf) { + const FieldPath empty; + const FieldPath a{"a"}; + const FieldPath ab{"a", "b"}; + const FieldPath abc{"a", "b", "c"}; + const FieldPath b{"b"}; + const FieldPath ba{"b", "a"}; + + EXPECT_TRUE(empty.IsPrefixOf(empty)); + EXPECT_TRUE(empty.IsPrefixOf(a)); + EXPECT_TRUE(empty.IsPrefixOf(ab)); + EXPECT_TRUE(empty.IsPrefixOf(abc)); + EXPECT_TRUE(empty.IsPrefixOf(b)); + EXPECT_TRUE(empty.IsPrefixOf(ba)); + + EXPECT_FALSE(a.IsPrefixOf(empty)); + EXPECT_TRUE(a.IsPrefixOf(a)); + EXPECT_TRUE(a.IsPrefixOf(ab)); + EXPECT_TRUE(a.IsPrefixOf(abc)); + EXPECT_FALSE(a.IsPrefixOf(b)); + EXPECT_FALSE(a.IsPrefixOf(ba)); + + EXPECT_FALSE(ab.IsPrefixOf(empty)); + EXPECT_FALSE(ab.IsPrefixOf(a)); + EXPECT_TRUE(ab.IsPrefixOf(ab)); + EXPECT_TRUE(ab.IsPrefixOf(abc)); + EXPECT_FALSE(ab.IsPrefixOf(b)); + EXPECT_FALSE(ab.IsPrefixOf(ba)); + + EXPECT_FALSE(abc.IsPrefixOf(empty)); + EXPECT_FALSE(abc.IsPrefixOf(a)); + EXPECT_FALSE(abc.IsPrefixOf(ab)); + EXPECT_TRUE(abc.IsPrefixOf(abc)); + EXPECT_FALSE(abc.IsPrefixOf(b)); + EXPECT_FALSE(abc.IsPrefixOf(ba)); +} + +TEST(FieldPath, AccessFailures) { + const FieldPath path; + ASSERT_ANY_THROW(path.first_segment()); + ASSERT_ANY_THROW(path.last_segment()); + ASSERT_ANY_THROW(path[0]); + ASSERT_ANY_THROW(path[1]); + ASSERT_ANY_THROW(path.PopFirst()); + ASSERT_ANY_THROW(path.PopFirst(2)); + ASSERT_ANY_THROW(path.PopLast()); +} + +TEST(FieldPath, Parsing) { + const auto parse = [](const std::pair expected) { + const auto path = FieldPath::FromServerFormat(expected.first); + return std::make_pair(path.CanonicalString(), path.size()); + }; + const auto make_expected = [](const std::string& str, const size_t size) { + return std::make_pair(str, size); + }; + + auto expected = make_expected("foo", 1); + EXPECT_EQ(expected, parse(expected)); + expected = make_expected("foo.bar", 2); + EXPECT_EQ(expected, parse(expected)); + expected = make_expected("foo.bar.baz", 3); + EXPECT_EQ(expected, parse(expected)); + expected = make_expected(R"(`.foo\\`)", 1); + EXPECT_EQ(expected, parse(expected)); + expected = make_expected(R"(`.foo\\`.`.foo`)", 2); + EXPECT_EQ(expected, parse(expected)); + expected = make_expected(R"(foo.`\``.bar)", 3); + EXPECT_EQ(expected, parse(expected)); + + const auto path_with_dot = FieldPath::FromServerFormat(R"(foo\.bar)"); + EXPECT_EQ(path_with_dot.CanonicalString(), "`foo.bar`"); + EXPECT_EQ(path_with_dot.size(), 1u); +} + +// This is a special case in C++: std::string may contain embedded nulls. To +// fully mimic behavior of Objective-C code, parsing must terminate upon +// encountering the first null terminator in the string. +TEST(FieldPath, ParseEmbeddedNull) { + std::string str{"foo"}; + str += '\0'; + str += ".bar"; + + const auto path = FieldPath::FromServerFormat(str); + EXPECT_EQ(path.size(), 1u); + EXPECT_EQ(path.CanonicalString(), "foo"); +} + +TEST(FieldPath, ParseFailures) { + ASSERT_ANY_THROW(FieldPath::FromServerFormat("")); + ASSERT_ANY_THROW(FieldPath::FromServerFormat(".")); + ASSERT_ANY_THROW(FieldPath::FromServerFormat("..")); + ASSERT_ANY_THROW(FieldPath::FromServerFormat("foo.")); + ASSERT_ANY_THROW(FieldPath::FromServerFormat(".bar")); + ASSERT_ANY_THROW(FieldPath::FromServerFormat("foo..bar")); + ASSERT_ANY_THROW(FieldPath::FromServerFormat(R"(foo\)")); + ASSERT_ANY_THROW(FieldPath::FromServerFormat(R"(foo.\)")); + ASSERT_ANY_THROW(FieldPath::FromServerFormat("foo`")); + ASSERT_ANY_THROW(FieldPath::FromServerFormat("foo```")); + ASSERT_ANY_THROW(FieldPath::FromServerFormat("`foo")); +} + +TEST(FieldPath, CanonicalStringOfSubstring) { + const auto path = FieldPath::FromServerFormat("foo.bar.baz"); + EXPECT_EQ(path.CanonicalString(), "foo.bar.baz"); + EXPECT_EQ(path.PopFirst().CanonicalString(), "bar.baz"); + EXPECT_EQ(path.PopLast().CanonicalString(), "foo.bar"); + EXPECT_EQ(path.PopFirst().PopLast().CanonicalString(), "bar"); + EXPECT_EQ(path.PopFirst().PopLast().CanonicalString(), "bar"); + EXPECT_EQ(path.PopLast().PopFirst().PopLast().CanonicalString(), ""); +} + +TEST(FieldPath, CanonicalStringEscaping) { + // Should be escaped + EXPECT_EQ(FieldPath::FromServerFormat("1").CanonicalString(), "`1`"); + EXPECT_EQ(FieldPath::FromServerFormat("1ab").CanonicalString(), "`1ab`"); + EXPECT_EQ(FieldPath::FromServerFormat("ab!").CanonicalString(), "`ab!`"); + EXPECT_EQ(FieldPath::FromServerFormat("/ab").CanonicalString(), "`/ab`"); + EXPECT_EQ(FieldPath::FromServerFormat("a#b").CanonicalString(), "`a#b`"); + + // Should not be escaped + EXPECT_EQ(FieldPath::FromServerFormat("_ab").CanonicalString(), "_ab"); + EXPECT_EQ(FieldPath::FromServerFormat("a1").CanonicalString(), "a1"); + EXPECT_EQ(FieldPath::FromServerFormat("a_").CanonicalString(), "a_"); +} + +TEST(FieldPath, EmptyPath) { + const auto& empty_path = FieldPath::EmptyPath(); + EXPECT_EQ(empty_path, FieldPath{empty_path}); + EXPECT_EQ(empty_path, FieldPath{}); + EXPECT_EQ(&empty_path, &FieldPath::EmptyPath()); +} + +TEST(FieldPath, KeyFieldPath) { + const auto& key_field_path = FieldPath::KeyFieldPath(); + EXPECT_EQ(key_field_path, FieldPath{key_field_path}); + EXPECT_EQ(key_field_path, + FieldPath::FromServerFormat(key_field_path.CanonicalString())); + EXPECT_EQ(&key_field_path, &FieldPath::KeyFieldPath()); + EXPECT_NE(key_field_path, FieldPath::FromServerFormat( + key_field_path.CanonicalString().substr(1))); +} + +} // namespace model +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/test/firebase/firestore/model/field_value_test.cc b/Firestore/core/test/firebase/firestore/model/field_value_test.cc new file mode 100644 index 00000000000..5a64d59406e --- /dev/null +++ b/Firestore/core/test/firebase/firestore/model/field_value_test.cc @@ -0,0 +1,507 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/core/src/firebase/firestore/model/field_value.h" + +#include + +#include + +#include "gtest/gtest.h" + +namespace firebase { +namespace firestore { +namespace model { + +using Type = FieldValue::Type; + +namespace { + +const uint8_t* Bytes(const char* value) { + return reinterpret_cast(value); +} + +} // namespace + +TEST(FieldValue, NullType) { + const FieldValue value = FieldValue::NullValue(); + EXPECT_EQ(Type::Null, value.type()); + EXPECT_FALSE(value < value); +} + +TEST(FieldValue, BooleanType) { + const FieldValue true_value = FieldValue::BooleanValue(true); + const FieldValue false_value = FieldValue::BooleanValue(false); + EXPECT_EQ(Type::Boolean, true_value.type()); + EXPECT_FALSE(true_value < true_value); + EXPECT_FALSE(true_value < false_value); + EXPECT_FALSE(false_value < false_value); + EXPECT_TRUE(false_value < true_value); +} + +TEST(FieldValue, NumberType) { + const FieldValue nan_value = FieldValue::NanValue(); + const FieldValue integer_value = FieldValue::IntegerValue(10L); + const FieldValue double_value = FieldValue::DoubleValue(10.1); + EXPECT_EQ(Type::Double, nan_value.type()); + EXPECT_EQ(Type::Integer, integer_value.type()); + EXPECT_EQ(Type::Double, double_value.type()); + EXPECT_TRUE(nan_value < integer_value); + EXPECT_TRUE(nan_value < double_value); + EXPECT_FALSE(nan_value < nan_value); + EXPECT_FALSE(integer_value < nan_value); + EXPECT_FALSE(integer_value < nan_value); + EXPECT_TRUE(integer_value < double_value); // 10 < 10.1 + EXPECT_FALSE(double_value < integer_value); + EXPECT_FALSE(integer_value < integer_value); + EXPECT_FALSE(double_value < double_value); + + // Number comparison craziness + // Integers + EXPECT_TRUE(FieldValue::IntegerValue(1L) < FieldValue::IntegerValue(2L)); + EXPECT_FALSE(FieldValue::IntegerValue(1L) < FieldValue::IntegerValue(1L)); + EXPECT_FALSE(FieldValue::IntegerValue(2L) < FieldValue::IntegerValue(1L)); + // Doubles + EXPECT_TRUE(FieldValue::DoubleValue(1.0) < FieldValue::DoubleValue(2.0)); + EXPECT_FALSE(FieldValue::DoubleValue(1.0) < FieldValue::DoubleValue(1.0)); + EXPECT_FALSE(FieldValue::DoubleValue(2.0) < FieldValue::DoubleValue(1.0)); + EXPECT_TRUE(FieldValue::NanValue() < FieldValue::DoubleValue(1.0)); + EXPECT_FALSE(FieldValue::NanValue() < FieldValue::NanValue()); + EXPECT_FALSE(FieldValue::DoubleValue(1.0) < FieldValue::NanValue()); + // Mixed + EXPECT_TRUE(FieldValue::DoubleValue(-1e20) < + FieldValue::IntegerValue(LLONG_MIN)); + EXPECT_FALSE(FieldValue::DoubleValue(1e20) < + FieldValue::IntegerValue(LLONG_MAX)); + EXPECT_TRUE(FieldValue::DoubleValue(1.234) < FieldValue::IntegerValue(2L)); + EXPECT_FALSE(FieldValue::DoubleValue(2.345) < FieldValue::IntegerValue(1L)); + EXPECT_FALSE(FieldValue::DoubleValue(1.0) < FieldValue::IntegerValue(1L)); + EXPECT_FALSE(FieldValue::DoubleValue(1.234) < FieldValue::IntegerValue(1L)); + EXPECT_FALSE(FieldValue::IntegerValue(LLONG_MIN) < + FieldValue::DoubleValue(-1e20)); + EXPECT_TRUE(FieldValue::IntegerValue(LLONG_MAX) < + FieldValue::DoubleValue(1e20)); + EXPECT_FALSE(FieldValue::IntegerValue(1) < FieldValue::DoubleValue(1.0)); + EXPECT_TRUE(FieldValue::IntegerValue(1) < FieldValue::DoubleValue(1.234)); +} + +TEST(FieldValue, TimestampType) { + const FieldValue o = FieldValue::TimestampValue(Timestamp()); + const FieldValue a = FieldValue::TimestampValue({100, 0}); + const FieldValue b = FieldValue::TimestampValue({200, 0}); + EXPECT_EQ(Type::Timestamp, a.type()); + EXPECT_TRUE(o < a); + EXPECT_TRUE(a < b); + EXPECT_FALSE(a < a); + const FieldValue c = FieldValue::ServerTimestampValue({100, 0}); + const FieldValue d = FieldValue::ServerTimestampValue({200, 0}, {300, 0}); + EXPECT_EQ(Type::ServerTimestamp, c.type()); + EXPECT_EQ(Type::ServerTimestamp, d.type()); + EXPECT_TRUE(c < d); + EXPECT_FALSE(c < c); + // Mixed + EXPECT_TRUE(o < c); + EXPECT_TRUE(a < c); + EXPECT_TRUE(b < c); + EXPECT_TRUE(b < d); + EXPECT_FALSE(c < o); + EXPECT_FALSE(c < a); + EXPECT_FALSE(c < b); + EXPECT_FALSE(d < b); +} + +TEST(FieldValue, StringType) { + const FieldValue a = FieldValue::StringValue("abc"); + std::string xyz("xyz"); + const FieldValue b = FieldValue::StringValue(xyz); + const FieldValue c = FieldValue::StringValue(std::move(xyz)); + EXPECT_EQ(Type::String, a.type()); + EXPECT_EQ(Type::String, b.type()); + EXPECT_EQ(Type::String, c.type()); + EXPECT_TRUE(a < b); + EXPECT_FALSE(a < a); +} + +TEST(FieldValue, BlobType) { + const FieldValue a = FieldValue::BlobValue(Bytes("abc"), 4); + const FieldValue b = FieldValue::BlobValue(Bytes("def"), 4); + EXPECT_EQ(Type::Blob, a.type()); + EXPECT_EQ(Type::Blob, b.type()); + EXPECT_TRUE(a < b); + EXPECT_FALSE(a < a); +} + +TEST(FieldValue, ReferenceType) { + const DatabaseId id("project", "database"); + const FieldValue a = + FieldValue::ReferenceValue(DocumentKey::FromPathString("root/abc"), &id); + DocumentKey key = DocumentKey::FromPathString("root/def"); + const FieldValue b = FieldValue::ReferenceValue(key, &id); + const FieldValue c = FieldValue::ReferenceValue(std::move(key), &id); + EXPECT_EQ(Type::Reference, a.type()); + EXPECT_EQ(Type::Reference, b.type()); + EXPECT_EQ(Type::Reference, c.type()); + EXPECT_TRUE(a < b); + EXPECT_FALSE(a < a); +} + +TEST(FieldValue, GeoPointType) { + const FieldValue a = FieldValue::GeoPointValue({1, 2}); + const FieldValue b = FieldValue::GeoPointValue({3, 4}); + EXPECT_EQ(Type::GeoPoint, a.type()); + EXPECT_EQ(Type::GeoPoint, b.type()); + EXPECT_TRUE(a < b); + EXPECT_FALSE(a < a); +} + +TEST(FieldValue, ArrayType) { + const FieldValue empty = FieldValue::ArrayValue(std::vector{}); + std::vector array{FieldValue::NullValue(), + FieldValue::BooleanValue(true), + FieldValue::BooleanValue(false)}; + // copy the array + const FieldValue small = FieldValue::ArrayValue(array); + std::vector another_array{FieldValue::BooleanValue(true), + FieldValue::BooleanValue(false)}; + // move the array + const FieldValue large = FieldValue::ArrayValue(std::move(another_array)); + EXPECT_EQ(Type::Array, empty.type()); + EXPECT_EQ(Type::Array, small.type()); + EXPECT_EQ(Type::Array, large.type()); + EXPECT_TRUE(empty < small); + EXPECT_FALSE(small < empty); + EXPECT_FALSE(small < small); + EXPECT_TRUE(small < large); + EXPECT_FALSE(large < small); +} + +TEST(FieldValue, ObjectType) { + const FieldValue empty = + FieldValue::ObjectValue(std::map{}); + std::map object{ + {"null", FieldValue::NullValue()}, + {"true", FieldValue::TrueValue()}, + {"false", FieldValue::FalseValue()}}; + // copy the map + const FieldValue small = FieldValue::ObjectValue(object); + std::map another_object{ + {"null", FieldValue::NullValue()}, {"true", FieldValue::FalseValue()}}; + // move the array + const FieldValue large = FieldValue::ObjectValue(std::move(another_object)); + EXPECT_EQ(Type::Object, empty.type()); + EXPECT_EQ(Type::Object, small.type()); + EXPECT_EQ(Type::Object, large.type()); + EXPECT_TRUE(empty < small); + EXPECT_FALSE(small < empty); + EXPECT_FALSE(small < small); + EXPECT_TRUE(small < large); + EXPECT_FALSE(large < small); +} + +TEST(FieldValue, Copy) { + FieldValue clone = FieldValue::TrueValue(); + const FieldValue null_value = FieldValue::NullValue(); + clone = null_value; + EXPECT_EQ(FieldValue::NullValue(), clone); + EXPECT_EQ(FieldValue::NullValue(), null_value); + clone = clone; + EXPECT_EQ(FieldValue::NullValue(), clone); + + const FieldValue true_value = FieldValue::TrueValue(); + clone = true_value; + EXPECT_EQ(FieldValue::TrueValue(), clone); + EXPECT_EQ(FieldValue::TrueValue(), true_value); + clone = clone; + EXPECT_EQ(FieldValue::TrueValue(), clone); + clone = null_value; + EXPECT_EQ(FieldValue::NullValue(), clone); + + const FieldValue nan_value = FieldValue::NanValue(); + clone = nan_value; + EXPECT_EQ(FieldValue::NanValue(), clone); + EXPECT_EQ(FieldValue::NanValue(), nan_value); + clone = clone; + EXPECT_EQ(FieldValue::NanValue(), clone); + clone = null_value; + EXPECT_EQ(FieldValue::NullValue(), clone); + + const FieldValue integer_value = FieldValue::IntegerValue(1L); + clone = integer_value; + EXPECT_EQ(FieldValue::IntegerValue(1L), clone); + EXPECT_EQ(FieldValue::IntegerValue(1L), integer_value); + clone = clone; + EXPECT_EQ(FieldValue::IntegerValue(1L), clone); + clone = null_value; + EXPECT_EQ(FieldValue::NullValue(), clone); + + const FieldValue double_value = FieldValue::DoubleValue(1.0); + clone = double_value; + EXPECT_EQ(FieldValue::DoubleValue(1.0), clone); + EXPECT_EQ(FieldValue::DoubleValue(1.0), double_value); + clone = clone; + EXPECT_EQ(FieldValue::DoubleValue(1.0), clone); + clone = null_value; + EXPECT_EQ(FieldValue::NullValue(), clone); + + const FieldValue timestamp_value = FieldValue::TimestampValue({100, 200}); + clone = timestamp_value; + EXPECT_EQ(FieldValue::TimestampValue({100, 200}), clone); + EXPECT_EQ(FieldValue::TimestampValue({100, 200}), timestamp_value); + clone = clone; + EXPECT_EQ(FieldValue::TimestampValue({100, 200}), clone); + clone = null_value; + EXPECT_EQ(FieldValue::NullValue(), clone); + + const FieldValue server_timestamp_value = + FieldValue::ServerTimestampValue({1, 2}, {3, 4}); + clone = server_timestamp_value; + EXPECT_EQ(FieldValue::ServerTimestampValue({1, 2}, {3, 4}), clone); + EXPECT_EQ(FieldValue::ServerTimestampValue({1, 2}, {3, 4}), + server_timestamp_value); + clone = clone; + EXPECT_EQ(FieldValue::ServerTimestampValue({1, 2}, {3, 4}), clone); + clone = null_value; + EXPECT_EQ(FieldValue::NullValue(), clone); + + const FieldValue string_value = FieldValue::StringValue("abc"); + clone = string_value; + EXPECT_EQ(FieldValue::StringValue("abc"), clone); + EXPECT_EQ(FieldValue::StringValue("abc"), string_value); + clone = clone; + EXPECT_EQ(FieldValue::StringValue("abc"), clone); + clone = null_value; + EXPECT_EQ(FieldValue::NullValue(), clone); + + const FieldValue blob_value = FieldValue::BlobValue(Bytes("abc"), 4); + clone = blob_value; + EXPECT_EQ(FieldValue::BlobValue(Bytes("abc"), 4), clone); + EXPECT_EQ(FieldValue::BlobValue(Bytes("abc"), 4), blob_value); + clone = clone; + EXPECT_EQ(FieldValue::BlobValue(Bytes("abc"), 4), clone); + clone = null_value; + EXPECT_EQ(FieldValue::NullValue(), clone); + + const DatabaseId database_id("project", "database"); + const FieldValue reference_value = FieldValue::ReferenceValue( + DocumentKey::FromPathString("root/abc"), &database_id); + clone = reference_value; + EXPECT_EQ(FieldValue::ReferenceValue(DocumentKey::FromPathString("root/abc"), + &database_id), + clone); + EXPECT_EQ(FieldValue::ReferenceValue(DocumentKey::FromPathString("root/abc"), + &database_id), + reference_value); + clone = clone; + EXPECT_EQ(FieldValue::ReferenceValue(DocumentKey::FromPathString("root/abc"), + &database_id), + clone); + clone = null_value; + EXPECT_EQ(FieldValue::NullValue(), clone); + + const FieldValue geo_point_value = FieldValue::GeoPointValue({1, 2}); + clone = geo_point_value; + EXPECT_EQ(FieldValue::GeoPointValue({1, 2}), clone); + EXPECT_EQ(FieldValue::GeoPointValue({1, 2}), geo_point_value); + clone = clone; + EXPECT_EQ(FieldValue::GeoPointValue({1, 2}), clone); + clone = null_value; + EXPECT_EQ(FieldValue::NullValue(), clone); + + const FieldValue array_value = FieldValue::ArrayValue(std::vector{ + FieldValue::TrueValue(), FieldValue::FalseValue()}); + clone = array_value; + EXPECT_EQ(FieldValue::ArrayValue(std::vector{ + FieldValue::TrueValue(), FieldValue::FalseValue()}), + clone); + EXPECT_EQ(FieldValue::ArrayValue(std::vector{ + FieldValue::TrueValue(), FieldValue::FalseValue()}), + array_value); + clone = clone; + EXPECT_EQ(FieldValue::ArrayValue(std::vector{ + FieldValue::TrueValue(), FieldValue::FalseValue()}), + clone); + clone = null_value; + EXPECT_EQ(FieldValue::NullValue(), clone); + + const FieldValue object_value = + FieldValue::ObjectValue(std::map{ + {"true", FieldValue::TrueValue()}, + {"false", FieldValue::FalseValue()}}); + clone = object_value; + EXPECT_EQ( + FieldValue::ObjectValue(std::map{ + {"true", FieldValue::TrueValue()}, + {"false", FieldValue::FalseValue()}}), + clone); + EXPECT_EQ( + FieldValue::ObjectValue(std::map{ + {"true", FieldValue::TrueValue()}, + {"false", FieldValue::FalseValue()}}), + object_value); + clone = clone; + EXPECT_EQ( + FieldValue::ObjectValue(std::map{ + {"true", FieldValue::TrueValue()}, + {"false", FieldValue::FalseValue()}}), + clone); + clone = null_value; + EXPECT_EQ(FieldValue::NullValue(), clone); +} + +TEST(FieldValue, Move) { + FieldValue clone = FieldValue::TrueValue(); + + FieldValue null_value = FieldValue::NullValue(); + clone = std::move(null_value); + EXPECT_EQ(FieldValue::NullValue(), clone); + + FieldValue true_value = FieldValue::TrueValue(); + clone = std::move(true_value); + EXPECT_EQ(FieldValue::TrueValue(), clone); + clone = FieldValue::NullValue(); + EXPECT_EQ(FieldValue::NullValue(), clone); + + FieldValue nan_value = FieldValue::NanValue(); + clone = std::move(nan_value); + EXPECT_EQ(FieldValue::NanValue(), clone); + clone = FieldValue::NullValue(); + EXPECT_EQ(FieldValue::NullValue(), clone); + + FieldValue integer_value = FieldValue::IntegerValue(1L); + clone = std::move(integer_value); + EXPECT_EQ(FieldValue::IntegerValue(1L), clone); + clone = FieldValue::NullValue(); + EXPECT_EQ(FieldValue::NullValue(), clone); + + FieldValue double_value = FieldValue::DoubleValue(1.0); + clone = std::move(double_value); + EXPECT_EQ(FieldValue::DoubleValue(1.0), clone); + clone = FieldValue::NullValue(); + EXPECT_EQ(FieldValue::NullValue(), clone); + + FieldValue timestamp_value = FieldValue::TimestampValue({100, 200}); + clone = std::move(timestamp_value); + EXPECT_EQ(FieldValue::TimestampValue({100, 200}), clone); + clone = FieldValue::NullValue(); + EXPECT_EQ(FieldValue::NullValue(), clone); + + FieldValue string_value = FieldValue::StringValue("abc"); + clone = std::move(string_value); + EXPECT_EQ(FieldValue::StringValue("abc"), clone); + clone = FieldValue::NullValue(); + EXPECT_EQ(FieldValue::NullValue(), clone); + + FieldValue blob_value = FieldValue::BlobValue(Bytes("abc"), 4); + clone = std::move(blob_value); + EXPECT_EQ(FieldValue::BlobValue(Bytes("abc"), 4), clone); + clone = FieldValue::NullValue(); + EXPECT_EQ(FieldValue::NullValue(), clone); + + const DatabaseId database_id("project", "database"); + FieldValue reference_value = FieldValue::ReferenceValue( + DocumentKey::FromPathString("root/abc"), &database_id); + clone = std::move(reference_value); + EXPECT_EQ(FieldValue::ReferenceValue(DocumentKey::FromPathString("root/abc"), + &database_id), + clone); + clone = null_value; + EXPECT_EQ(FieldValue::NullValue(), clone); + + FieldValue geo_point_value = FieldValue::GeoPointValue({1, 2}); + clone = std::move(geo_point_value); + EXPECT_EQ(FieldValue::GeoPointValue({1, 2}), clone); + clone = null_value; + EXPECT_EQ(FieldValue::NullValue(), clone); + + FieldValue array_value = FieldValue::ArrayValue(std::vector{ + FieldValue::TrueValue(), FieldValue::FalseValue()}); + clone = std::move(array_value); + EXPECT_EQ(FieldValue::ArrayValue(std::vector{ + FieldValue::TrueValue(), FieldValue::FalseValue()}), + clone); + clone = FieldValue::NullValue(); + EXPECT_EQ(FieldValue::NullValue(), clone); + + FieldValue object_value = + FieldValue::ObjectValue(std::map{ + {"true", FieldValue::TrueValue()}, + {"false", FieldValue::FalseValue()}}); + clone = std::move(object_value); + EXPECT_EQ( + FieldValue::ObjectValue(std::map{ + {"true", FieldValue::TrueValue()}, + {"false", FieldValue::FalseValue()}}), + clone); + clone = FieldValue::NullValue(); + EXPECT_EQ(FieldValue::NullValue(), clone); +} + +TEST(FieldValue, CompareMixedType) { + const FieldValue null_value = FieldValue::NullValue(); + const FieldValue true_value = FieldValue::TrueValue(); + const FieldValue number_value = FieldValue::NanValue(); + const FieldValue timestamp_value = FieldValue::TimestampValue({100, 200}); + const FieldValue string_value = FieldValue::StringValue("abc"); + const FieldValue blob_value = FieldValue::BlobValue(Bytes("abc"), 4); + const DatabaseId database_id("project", "database"); + const FieldValue reference_value = FieldValue::ReferenceValue( + DocumentKey::FromPathString("root/abc"), &database_id); + const FieldValue geo_point_value = FieldValue::GeoPointValue({1, 2}); + const FieldValue array_value = + FieldValue::ArrayValue(std::vector()); + const FieldValue object_value = + FieldValue::ObjectValue(std::map()); + EXPECT_TRUE(null_value < true_value); + EXPECT_TRUE(true_value < number_value); + EXPECT_TRUE(number_value < timestamp_value); + EXPECT_TRUE(timestamp_value < string_value); + EXPECT_TRUE(string_value < blob_value); + EXPECT_TRUE(blob_value < reference_value); + EXPECT_TRUE(reference_value < geo_point_value); + EXPECT_TRUE(geo_point_value < array_value); + EXPECT_TRUE(array_value < object_value); +} + +TEST(FieldValue, CompareWithOperator) { + const FieldValue small = FieldValue::NullValue(); + const FieldValue large = FieldValue::TrueValue(); + + EXPECT_TRUE(small < large); + EXPECT_FALSE(small < small); + EXPECT_FALSE(large < small); + + EXPECT_TRUE(large > small); + EXPECT_FALSE(small > small); + EXPECT_FALSE(small > large); + + EXPECT_TRUE(large >= small); + EXPECT_TRUE(small >= small); + EXPECT_FALSE(small >= large); + + EXPECT_TRUE(small <= large); + EXPECT_TRUE(small <= small); + EXPECT_FALSE(large <= small); + + EXPECT_TRUE(small != large); + EXPECT_FALSE(small != small); + + EXPECT_TRUE(small == small); + EXPECT_FALSE(small == large); +} + +} // namespace model +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/test/firebase/firestore/model/maybe_document_test.cc b/Firestore/core/test/firebase/firestore/model/maybe_document_test.cc new file mode 100644 index 00000000000..70ae319624f --- /dev/null +++ b/Firestore/core/test/firebase/firestore/model/maybe_document_test.cc @@ -0,0 +1,66 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/core/src/firebase/firestore/model/maybe_document.h" + +#include "absl/strings/string_view.h" +#include "gtest/gtest.h" + +namespace firebase { +namespace firestore { +namespace model { + +namespace { + +inline MaybeDocument MakeMaybeDocument(const absl::string_view path, + const Timestamp& timestamp) { + return MaybeDocument(DocumentKey::FromPathString(path.data()), + SnapshotVersion(timestamp)); +} + +inline bool operator<(const MaybeDocument& lhs, const MaybeDocument& rhs) { + static const DocumentKeyComparator less; + return less(lhs, rhs); +} + +} // anonymous namespace + +TEST(MaybeDocument, Getter) { + const MaybeDocument& doc = + MakeMaybeDocument("i/am/a/path", Timestamp(123, 456)); + EXPECT_EQ(MaybeDocument::Type::Unknown, doc.type()); + EXPECT_EQ(DocumentKey::FromPathString("i/am/a/path"), doc.key()); + EXPECT_EQ(SnapshotVersion(Timestamp(123, 456)), doc.version()); +} + +TEST(MaybeDocument, Comparison) { + EXPECT_TRUE(MakeMaybeDocument("root/123", Timestamp(456, 123)) < + MakeMaybeDocument("root/456", Timestamp(123, 456))); + // MaybeDocument comparison is purely key-based. + EXPECT_FALSE(MakeMaybeDocument("root/123", Timestamp(111, 111)) < + MakeMaybeDocument("root/123", Timestamp(222, 222))); + + EXPECT_EQ(MakeMaybeDocument("root/123", Timestamp(456, 123)), + MakeMaybeDocument("root/123", Timestamp(456, 123))); + EXPECT_NE(MakeMaybeDocument("root/123", Timestamp(456, 123)), + MakeMaybeDocument("root/456", Timestamp(456, 123))); + EXPECT_NE(MakeMaybeDocument("root/123", Timestamp(456, 123)), + MakeMaybeDocument("root/123", Timestamp(123, 456))); +} + +} // namespace model +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/test/firebase/firestore/model/no_document_test.cc b/Firestore/core/test/firebase/firestore/model/no_document_test.cc new file mode 100644 index 00000000000..825820f068c --- /dev/null +++ b/Firestore/core/test/firebase/firestore/model/no_document_test.cc @@ -0,0 +1,51 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/core/src/firebase/firestore/model/no_document.h" + +#include "absl/strings/string_view.h" +#include "gtest/gtest.h" + +namespace firebase { +namespace firestore { +namespace model { + +namespace { + +inline NoDocument MakeNoDocument(const absl::string_view path, + const Timestamp& timestamp) { + return NoDocument(DocumentKey::FromPathString(path.data()), + SnapshotVersion(timestamp)); +} + +} // anonymous namespace + +TEST(NoDocument, Getter) { + const NoDocument& doc = MakeNoDocument("i/am/a/path", Timestamp(123, 456)); + EXPECT_EQ(MaybeDocument::Type::NoDocument, doc.type()); + EXPECT_EQ(DocumentKey::FromPathString("i/am/a/path"), doc.key()); + EXPECT_EQ(SnapshotVersion(Timestamp(123, 456)), doc.version()); + + // NoDocument and MaybeDocument will not equal. + EXPECT_NE(NoDocument(DocumentKey::FromPathString("same/path"), + SnapshotVersion(Timestamp())), + MaybeDocument(DocumentKey::FromPathString("same/path"), + SnapshotVersion(Timestamp()))); +} + +} // namespace model +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/test/firebase/firestore/model/resource_path_test.cc b/Firestore/core/test/firebase/firestore/model/resource_path_test.cc new file mode 100644 index 00000000000..637e78efa42 --- /dev/null +++ b/Firestore/core/test/firebase/firestore/model/resource_path_test.cc @@ -0,0 +1,105 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/core/src/firebase/firestore/model/resource_path.h" + +#include +#include +#include + +#include "gtest/gtest.h" + +namespace firebase { +namespace firestore { +namespace model { + +TEST(ResourcePath, Constructor) { + const ResourcePath empty_path; + EXPECT_TRUE(empty_path.empty()); + EXPECT_EQ(0u, empty_path.size()); + EXPECT_TRUE(empty_path.begin() == empty_path.end()); + + const ResourcePath path_from_list{{"rooms", "Eros", "messages"}}; + EXPECT_FALSE(path_from_list.empty()); + EXPECT_EQ(3u, path_from_list.size()); + EXPECT_TRUE(path_from_list.begin() + 3 == path_from_list.end()); + + std::vector segments{"rooms", "Eros", "messages"}; + const ResourcePath path_from_segments{segments.begin(), segments.end()}; + EXPECT_FALSE(path_from_segments.empty()); + EXPECT_EQ(3u, path_from_segments.size()); + EXPECT_TRUE(path_from_segments.begin() + 3 == path_from_segments.end()); + + ResourcePath copied = path_from_list; + EXPECT_EQ(path_from_list, copied); + const ResourcePath moved = std::move(copied); + EXPECT_EQ(path_from_list, moved); + EXPECT_NE(copied, moved); + EXPECT_EQ(empty_path, copied); +} + +TEST(ResourcePath, Comparison) { + const ResourcePath abc{"a", "b", "c"}; + const ResourcePath abc2{"a", "b", "c"}; + const ResourcePath xyz{"x", "y", "z"}; + EXPECT_EQ(abc, abc2); + EXPECT_NE(abc, xyz); + + const ResourcePath empty; + const ResourcePath a{"a"}; + const ResourcePath b{"b"}; + const ResourcePath ab{"a", "b"}; + + EXPECT_TRUE(empty < a); + EXPECT_TRUE(a < b); + EXPECT_TRUE(a < ab); + + EXPECT_TRUE(a > empty); + EXPECT_TRUE(b > a); + EXPECT_TRUE(ab > a); +} + +TEST(ResourcePath, Parsing) { + const auto parse = [](const std::pair expected) { + const auto path = ResourcePath::FromString(expected.first); + return std::make_pair(path.CanonicalString(), path.size()); + }; + const auto make_expected = [](const std::string& str, const size_t size) { + return std::make_pair(str, size); + }; + + auto expected = make_expected("", 0); + EXPECT_EQ(expected, parse(expected)); + expected = make_expected("foo", 1); + EXPECT_EQ(expected, parse(expected)); + expected = make_expected("foo/bar", 2); + EXPECT_EQ(expected, parse(expected)); + expected = make_expected("foo/bar/baz", 3); + EXPECT_EQ(expected, parse(expected)); + expected = make_expected(R"(foo/__!?#@..`..\`/baz)", 3); + EXPECT_EQ(expected, parse(expected)); + + EXPECT_EQ(ResourcePath::FromString("/foo/").CanonicalString(), "foo"); +} + +TEST(ResourcePath, ParseFailures) { + ASSERT_ANY_THROW(ResourcePath::FromString("//")); + ASSERT_ANY_THROW(ResourcePath::FromString("foo//bar")); +} + +} // namespace model +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/test/firebase/firestore/model/snapshot_version_test.cc b/Firestore/core/test/firebase/firestore/model/snapshot_version_test.cc new file mode 100644 index 00000000000..e359f8449a7 --- /dev/null +++ b/Firestore/core/test/firebase/firestore/model/snapshot_version_test.cc @@ -0,0 +1,56 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h" + +#include "gtest/gtest.h" + +namespace firebase { +namespace firestore { +namespace model { + +TEST(SnapshotVersion, Getter) { + SnapshotVersion version(Timestamp(123, 456)); + EXPECT_EQ(Timestamp(123, 456), version.timestamp()); + + const SnapshotVersion& no_version = SnapshotVersion::None(); + EXPECT_EQ(Timestamp(), no_version.timestamp()); +} + +TEST(SnapshotVersion, Comparison) { + EXPECT_LT(SnapshotVersion::None(), SnapshotVersion(Timestamp(123, 456))); + + EXPECT_LT(SnapshotVersion(Timestamp(123, 456)), + SnapshotVersion(Timestamp(456, 123))); + EXPECT_GT(SnapshotVersion(Timestamp(456, 123)), + SnapshotVersion(Timestamp(123, 456))); + EXPECT_LE(SnapshotVersion(Timestamp(123, 456)), + SnapshotVersion(Timestamp(456, 123))); + EXPECT_LE(SnapshotVersion(Timestamp(123, 456)), + SnapshotVersion(Timestamp(123, 456))); + EXPECT_GE(SnapshotVersion(Timestamp(456, 123)), + SnapshotVersion(Timestamp(123, 456))); + EXPECT_GE(SnapshotVersion(Timestamp(123, 456)), + SnapshotVersion(Timestamp(123, 456))); + EXPECT_EQ(SnapshotVersion(Timestamp(123, 456)), + SnapshotVersion(Timestamp(123, 456))); + EXPECT_NE(SnapshotVersion(Timestamp(123, 456)), + SnapshotVersion(Timestamp(456, 123))); +} + +} // namespace model +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/test/firebase/firestore/model/timestamp_test.cc b/Firestore/core/test/firebase/firestore/model/timestamp_test.cc new file mode 100644 index 00000000000..55ee3783aa7 --- /dev/null +++ b/Firestore/core/test/firebase/firestore/model/timestamp_test.cc @@ -0,0 +1,49 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/core/src/firebase/firestore/model/timestamp.h" + +#include + +#include "gtest/gtest.h" + +namespace firebase { +namespace firestore { +namespace model { + +TEST(Timestamp, Getter) { + const Timestamp timestamp_zero; + EXPECT_EQ(0, timestamp_zero.seconds()); + EXPECT_EQ(0, timestamp_zero.nanos()); + + const Timestamp timestamp(100, 200); + EXPECT_EQ(100, timestamp.seconds()); + EXPECT_EQ(200, timestamp.nanos()); + + const Timestamp timestamp_now = Timestamp::Now(); + EXPECT_LT(0, timestamp_now.seconds()); + EXPECT_LE(0, timestamp_now.nanos()); +} + +TEST(Timestamp, Comparison) { + EXPECT_TRUE(Timestamp() < Timestamp(1, 2)); + EXPECT_TRUE(Timestamp(1, 2) < Timestamp(2, 1)); + EXPECT_TRUE(Timestamp(2, 1) < Timestamp(2, 2)); +} + +} // namespace model +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/test/firebase/firestore/remote/CMakeLists.txt b/Firestore/core/test/firebase/firestore/remote/CMakeLists.txt new file mode 100644 index 00000000000..d42b107b538 --- /dev/null +++ b/Firestore/core/test/firebase/firestore/remote/CMakeLists.txt @@ -0,0 +1,22 @@ +# Copyright 2018 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. + +cc_test( + firebase_firestore_remote_test + SOURCES + datastore_test.cc + serializer_test.cc + DEPENDS + firebase_firestore_remote +) diff --git a/Firestore/core/test/firebase/firestore/remote/datastore_test.cc b/Firestore/core/test/firebase/firestore/remote/datastore_test.cc new file mode 100644 index 00000000000..42a9a8c7670 --- /dev/null +++ b/Firestore/core/test/firebase/firestore/remote/datastore_test.cc @@ -0,0 +1,29 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/core/src/firebase/firestore/remote/datastore.h" + +#include +#include + +TEST(Datastore, CanLinkToGrpc) { + // This test doesn't actually do anything interesting as far as actually + // using gRPC is concerned but that it can run at all is proof that all the + // libraries required for gRPC to work are actually linked correctly into the + // test. + grpc_init(); + grpc_shutdown(); +} diff --git a/Firestore/core/test/firebase/firestore/remote/serializer_test.cc b/Firestore/core/test/firebase/firestore/remote/serializer_test.cc new file mode 100644 index 00000000000..9ce60f572ca --- /dev/null +++ b/Firestore/core/test/firebase/firestore/remote/serializer_test.cc @@ -0,0 +1,157 @@ +/* + * Copyright 2018 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. + */ + +/* NB: proto bytes were created via: + echo 'TEXT_FORMAT_PROTO' \ + | ./build/external/protobuf/src/protobuf-build/src/protoc \ + -I./Firestore/Protos/protos \ + -I./build/external/protobuf/src/protobuf/src \ + --encode=google.firestore.v1beta1.Value \ + google/firestore/v1beta1/document.proto \ + | hexdump -C + * where TEXT_FORMAT_PROTO is the text format of the protobuf. (go/textformat). + * + * Examples: + * - For null, TEXT_FORMAT_PROTO would be 'null_value: NULL_VALUE' and would + * yield the bytes: { 0x58, 0x00 }. + * - For true, TEXT_FORMAT_PROTO would be 'boolean_value: true' and would yield + * the bytes { 0x08, 0x01 }. + * + * All uses are documented below, so search for TEXT_FORMAT_PROTO to find more + * examples. + */ + +#include "Firestore/core/src/firebase/firestore/remote/serializer.h" + +#include +#include +#include +#include + +#include "Firestore/core/src/firebase/firestore/model/field_value.h" +#include "gtest/gtest.h" + +using firebase::firestore::model::FieldValue; +using firebase::firestore::remote::Serializer; + +TEST(Serializer, CanLinkToNanopb) { + // This test doesn't actually do anything interesting as far as actually using + // nanopb is concerned but that it can run at all is proof that all the + // libraries required for nanopb to work are actually linked correctly into + // the test. + pb_ostream_from_buffer(NULL, 0); +} + +// Fixture for running serializer tests. +class SerializerTest : public ::testing::Test { + public: + SerializerTest() : serializer(/*DatabaseId("p", "d")*/) { + } + Serializer serializer; + + void ExpectRoundTrip(const FieldValue& model, + const std::vector& bytes, + FieldValue::Type type) { + EXPECT_EQ(type, model.type()); + std::vector actual_bytes; + serializer.EncodeFieldValue(model, &actual_bytes); + EXPECT_EQ(bytes, actual_bytes); + FieldValue actual_model = serializer.DecodeFieldValue(bytes); + EXPECT_EQ(type, actual_model.type()); + EXPECT_EQ(model, actual_model); + } +}; + +TEST_F(SerializerTest, EncodesNullModelToBytes) { + FieldValue model = FieldValue::NullValue(); + + // TEXT_FORMAT_PROTO: 'null_value: NULL_VALUE' + std::vector bytes{0x58, 0x00}; + ExpectRoundTrip(model, bytes, FieldValue::Type::Null); +} + +TEST_F(SerializerTest, EncodesBoolModelToBytes) { + struct TestCase { + bool value; + std::vector bytes; + }; + + std::vector cases{// TEXT_FORMAT_PROTO: 'boolean_value: true' + {true, {0x08, 0x01}}, + // TEXT_FORMAT_PROTO: 'boolean_value: false' + {false, {0x08, 0x00}}}; + + for (const TestCase& test : cases) { + FieldValue model = FieldValue::BooleanValue(test.value); + ExpectRoundTrip(model, test.bytes, FieldValue::Type::Boolean); + } +} + +TEST_F(SerializerTest, EncodesIntegersModelToBytes) { + // NB: because we're calculating the bytes via protoc (instead of + // libprotobuf) we have to look at values from numeric_limits ahead of + // time. So these test cases make the following assumptions about + // numeric_limits: (linking libprotobuf is starting to sound like a better + // idea. :) + // -9223372036854775808 + // == -8000000000000000 + // == numeric_limits::min() + // 9223372036854775807 + // == 7FFFFFFFFFFFFFFF + // == numeric_limits::max() + // + // For now, lets at least verify these values: + EXPECT_EQ(-9223372036854775807 - 1, std::numeric_limits::min()); + EXPECT_EQ(9223372036854775807, std::numeric_limits::max()); + + struct TestCase { + int64_t value; + std::vector bytes; + }; + + std::vector cases{ + // TEXT_FORMAT_PROTO: 'integer_value: 0' + TestCase{0, {0x10, 0x00}}, + // TEXT_FORMAT_PROTO: 'integer_value: 1' + TestCase{1, {0x10, 0x01}}, + // TEXT_FORMAT_PROTO: 'integer_value: -1' + TestCase{ + -1, + {0x10, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01}}, + // TEXT_FORMAT_PROTO: 'integer_value: 100' + TestCase{100, {0x10, 0x64}}, + // TEXT_FORMAT_PROTO: 'integer_value: -100' + TestCase{ + -100, + {0x10, 0x9c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01}}, + // TEXT_FORMAT_PROTO: 'integer_value: -9223372036854775808' + TestCase{ + -9223372036854775807 - 1, + {0x10, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x01}}, + // TEXT_FORMAT_PROTO: 'integer_value: 9223372036854775807' + TestCase{9223372036854775807, + {0x10, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f}}}; + + for (const TestCase& test : cases) { + FieldValue model = FieldValue::IntegerValue(test.value); + ExpectRoundTrip(model, test.bytes, FieldValue::Type::Integer); + } +} + +// TODO(rsgowman): Test [en|de]coding multiple protos into the same output +// vector. + +// TODO(rsgowman): Death test for decoding invalid bytes. diff --git a/Firestore/core/test/firebase/firestore/testutil/CMakeLists.txt b/Firestore/core/test/firebase/firestore/testutil/CMakeLists.txt new file mode 100644 index 00000000000..14308a596fe --- /dev/null +++ b/Firestore/core/test/firebase/firestore/testutil/CMakeLists.txt @@ -0,0 +1,36 @@ +# Copyright 2018 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. + +cc_library( + firebase_firestore_testutil_apple + SOURCES + app_testing.h + app_testing.mm + DEPENDS + FirebaseCore + absl_strings + EXCLUDE_FROM_ALL +) + +if(APPLE) + list(APPEND TESTUTIL_DEPENDS firebase_firestore_testutil_apple) +endif() + +add_library( + firebase_firestore_testutil INTERFACE +) +target_link_libraries( + firebase_firestore_testutil INTERFACE + ${TESTUTIL_DEPENDS} +) diff --git a/Firestore/core/test/firebase/firestore/testutil/app_testing.h b/Firestore/core/test/firebase/firestore/testutil/app_testing.h new file mode 100644 index 00000000000..d18ba4fecdb --- /dev/null +++ b/Firestore/core/test/firebase/firestore/testutil/app_testing.h @@ -0,0 +1,43 @@ +/* + * Copyright 2018 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. + */ + +#ifndef FIRESTORE_CORE_TEST_FIREBASE_FIRESTORE_TESTUTIL_APP_TESTING_H_ +#define FIRESTORE_CORE_TEST_FIREBASE_FIRESTORE_TESTUTIL_APP_TESTING_H_ + +#include "absl/strings/string_view.h" + +#if __OBJC__ + +@class FIRApp; + +namespace firebase { +namespace firestore { +namespace testutil { + +/** Creates a set of default Firebase Options for testing. */ +FIROptions* OptionsForUnitTesting( + const absl::string_view project_id = "project_id"); + +/** Creates a new Firebase App for testing. */ +FIRApp* AppForUnitTesting(const absl::string_view project_id = "project_id"); + +} // namespace testutil +} // namespace firestore +} // namespace firebase + +#endif // __OBJC__ + +#endif // FIRESTORE_CORE_TEST_FIREBASE_FIRESTORE_TESTUTIL_APP_TESTING_H_ diff --git a/Firestore/core/test/firebase/firestore/testutil/app_testing.mm b/Firestore/core/test/firebase/firestore/testutil/app_testing.mm new file mode 100644 index 00000000000..21db0db1d62 --- /dev/null +++ b/Firestore/core/test/firebase/firestore/testutil/app_testing.mm @@ -0,0 +1,48 @@ +/* + * Copyright 2018 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 "Firestore/core/src/firebase/firestore/util/string_apple.h" +#include "Firestore/core/test/firebase/firestore/testutil/app_testing.h" + +namespace firebase { +namespace firestore { +namespace testutil { + +FIROptions* OptionsForUnitTesting(const absl::string_view project_id) { + FIROptions* options = + [[FIROptions alloc] initWithGoogleAppID:@"1:123:ios:123ab" + GCMSenderID:@"gcm_sender_id"]; + options.projectID = util::WrapNSString(project_id); + return options; +} + +FIRApp* AppForUnitTesting(const absl::string_view project_id) { + static int counter = 0; + + NSString* appName = + [NSString stringWithFormat:@"app_for_unit_testing_%d", counter++]; + FIROptions* options = OptionsForUnitTesting(project_id); + [FIRApp configureWithName:appName options:options]; + + return [FIRApp appNamed:appName]; +} + +} // namespace testutil +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/test/firebase/firestore/util/CMakeLists.txt b/Firestore/core/test/firebase/firestore/util/CMakeLists.txt index e51bb51484a..0bddf069639 100644 --- a/Firestore/core/test/firebase/firestore/util/CMakeLists.txt +++ b/Firestore/core/test/firebase/firestore/util/CMakeLists.txt @@ -12,35 +12,64 @@ # See the License for the specific language governing permissions and # limitations under the License. +set(CMAKE_CXX_EXTENSIONS ON) + +# Required to allow 0 length printf style strings for testing purposes. +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-format-zero-length") + +if(HAVE_ARC4RANDOM) + cc_test( + firebase_firestore_util_arc4random_test + SOURCES + secure_random_test.cc + DEPENDS + firebase_firestore_util_arc4random + ) +endif() + +if(HAVE_OPENSSL_RAND_H) + cc_test( + firebase_firestore_util_openssl_test + SOURCES + secure_random_test.cc + DEPENDS + firebase_firestore_util_openssl + ) +endif() + cc_test( firebase_firestore_util_test - autoid_test.cc - secure_random_test.cc - string_printf_test.cc -) -target_link_libraries( - firebase_firestore_util_test - firebase_firestore_util + SOURCES + autoid_test.cc + bits_test.cc + comparison_test.cc + iterator_adaptors_test.cc + ordered_code_test.cc + string_printf_test.cc + string_util_test.cc + DEPENDS + absl_base + absl_strings + firebase_firestore_util + gmock ) if(APPLE) cc_test( firebase_firestore_util_apple_test - assert_test.cc - log_test.cc - ) - target_link_libraries( - firebase_firestore_util_apple_test - firebase_firestore_util_apple + SOURCES + assert_test.cc + log_test.cc + DEPENDS + firebase_firestore_util_apple ) endif(APPLE) cc_test( firebase_firestore_util_stdio_test - assert_test.cc - log_test.cc -) -target_link_libraries( - firebase_firestore_util_stdio_test - firebase_firestore_util_stdio + SOURCES + assert_test.cc + log_test.cc + DEPENDS + firebase_firestore_util_stdio ) diff --git a/Firestore/core/test/firebase/firestore/util/assert_test.cc b/Firestore/core/test/firebase/firestore/util/assert_test.cc index 7c4946227fd..fb15e618854 100644 --- a/Firestore/core/test/firebase/firestore/util/assert_test.cc +++ b/Firestore/core/test/firebase/firestore/util/assert_test.cc @@ -14,10 +14,9 @@ * limitations under the License. */ -#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" - #include +#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" #include "gtest/gtest.h" namespace firebase { diff --git a/Firestore/core/test/firebase/firestore/util/autoid_test.cc b/Firestore/core/test/firebase/firestore/util/autoid_test.cc index e55a8e92dbd..079b9909852 100644 --- a/Firestore/core/test/firebase/firestore/util/autoid_test.cc +++ b/Firestore/core/test/firebase/firestore/util/autoid_test.cc @@ -25,8 +25,8 @@ using firebase::firestore::util::CreateAutoId; TEST(AutoId, IsSane) { for (int i = 0; i < 50; i++) { std::string auto_id = CreateAutoId(); - EXPECT_EQ(20, auto_id.length()); - for (int pos = 0; pos < 20; pos++) { + EXPECT_EQ(20u, auto_id.length()); + for (size_t pos = 0; pos < 20; pos++) { char c = auto_id[pos]; EXPECT_TRUE(isalpha(c) || isdigit(c)) << "Should be printable ascii character: '" << c << "' in \"" diff --git a/Firestore/Port/bits_test.cc b/Firestore/core/test/firebase/firestore/util/bits_test.cc similarity index 72% rename from Firestore/Port/bits_test.cc rename to Firestore/core/test/firebase/firestore/util/bits_test.cc index 8c3c2467dc7..cb0976bdaef 100644 --- a/Firestore/Port/bits_test.cc +++ b/Firestore/core/test/firebase/firestore/util/bits_test.cc @@ -14,24 +14,24 @@ * limitations under the License. */ -#include "Firestore/Port/bits.h" +#include "Firestore/core/src/firebase/firestore/util/bits.h" +#include #include +#include -#include "base/commandlineflags.h" -#include "testing/base/public/gunit.h" -#include "util/random/mt_random.h" +#include "Firestore/core/src/firebase/firestore/util/secure_random.h" +#include "gtest/gtest.h" -using Firestore::Bits; +namespace firebase { +namespace firestore { +namespace util { -DEFINE_int32(num_iterations, 10000, "Number of test iterations to run."); +const int kNumIterations = 10000; // "Number of test iterations to run. class BitsTest : public testing::Test { - public: - BitsTest() : random_(testing::FLAGS_gunit_random_seed) {} - protected: - MTRandom random_; + SecureRandom random_; }; TEST_F(BitsTest, Log2EdgeCases) { @@ -41,7 +41,7 @@ TEST_F(BitsTest, Log2EdgeCases) { EXPECT_EQ(-1, Bits::Log2Floor64(0)); for (int i = 0; i < 32; i++) { - uint32 n = 1U << i; + uint32_t n = 1U << i; EXPECT_EQ(i, Bits::Log2Floor(n)); EXPECT_EQ(i, Bits::Log2FloorNonZero(n)); if (n > 2) { @@ -53,7 +53,7 @@ TEST_F(BitsTest, Log2EdgeCases) { } for (int i = 0; i < 64; i++) { - uint64 n = 1ULL << i; + uint64_t n = 1ULL << i; EXPECT_EQ(i, Bits::Log2Floor64(n)); EXPECT_EQ(i, Bits::Log2FloorNonZero64(n)); if (n > 2) { @@ -68,11 +68,11 @@ TEST_F(BitsTest, Log2EdgeCases) { TEST_F(BitsTest, Log2Random) { std::cout << "TestLog2Random" << std::endl; - for (int i = 0; i < FLAGS_num_iterations; i++) { + for (int i = 0; i < kNumIterations; i++) { int maxbit = -1; - uint32 n = 0; - while (!random_.OneIn(32)) { - int bit = random_.Uniform(32); + uint32_t n = 0; + while (!random_.OneIn(32u)) { + int bit = static_cast(random_.Uniform(32u)); n |= (1U << bit); maxbit = std::max(bit, maxbit); } @@ -86,11 +86,11 @@ TEST_F(BitsTest, Log2Random) { TEST_F(BitsTest, Log2Random64) { std::cout << "TestLog2Random64" << std::endl; - for (int i = 0; i < FLAGS_num_iterations; i++) { + for (int i = 0; i < kNumIterations; i++) { int maxbit = -1; - uint64 n = 0; - while (!random_.OneIn(64)) { - int bit = random_.Uniform(64); + uint64_t n = 0; + while (!random_.OneIn(64u)) { + int bit = static_cast(random_.Uniform(64u)); n |= (1ULL << bit); maxbit = std::max(bit, maxbit); } @@ -103,8 +103,8 @@ TEST_F(BitsTest, Log2Random64) { TEST(Bits, Port32) { for (int shift = 0; shift < 32; shift++) { - for (int delta = -1; delta <= +1; delta++) { - const uint32 v = (static_cast(1) << shift) + delta; + for (uint32_t delta = 0; delta <= 2; delta++) { + const uint32_t v = (static_cast(1) << shift) - 1 + delta; EXPECT_EQ(Bits::Log2Floor_Portable(v), Bits::Log2Floor(v)) << v; if (v != 0) { EXPECT_EQ(Bits::Log2FloorNonZero_Portable(v), Bits::Log2FloorNonZero(v)) @@ -112,7 +112,7 @@ TEST(Bits, Port32) { } } } - static const uint32 M32 = kuint32max; + static const uint32_t M32 = std::numeric_limits::max(); EXPECT_EQ(Bits::Log2Floor_Portable(M32), Bits::Log2Floor(M32)) << M32; EXPECT_EQ(Bits::Log2FloorNonZero_Portable(M32), Bits::Log2FloorNonZero(M32)) << M32; @@ -120,8 +120,8 @@ TEST(Bits, Port32) { TEST(Bits, Port64) { for (int shift = 0; shift < 64; shift++) { - for (int delta = -1; delta <= +1; delta++) { - const uint64 v = (static_cast(1) << shift) + delta; + for (uint64_t delta = 0; delta <= 2; delta++) { + const uint64_t v = (static_cast(1) << shift) - 1 + delta; EXPECT_EQ(Bits::Log2Floor64_Portable(v), Bits::Log2Floor64(v)) << v; if (v != 0) { EXPECT_EQ(Bits::Log2FloorNonZero64_Portable(v), @@ -130,9 +130,13 @@ TEST(Bits, Port64) { } } } - static const uint64 M64 = kuint64max; + static const uint64_t M64 = std::numeric_limits::max(); EXPECT_EQ(Bits::Log2Floor64_Portable(M64), Bits::Log2Floor64(M64)) << M64; EXPECT_EQ(Bits::Log2FloorNonZero64_Portable(M64), Bits::Log2FloorNonZero64(M64)) << M64; } + +} // namespace util +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/test/firebase/firestore/util/comparison_test.cc b/Firestore/core/test/firebase/firestore/util/comparison_test.cc new file mode 100644 index 00000000000..ccb30114a6b --- /dev/null +++ b/Firestore/core/test/firebase/firestore/util/comparison_test.cc @@ -0,0 +1,211 @@ +/* + * Copyright 2018 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. + */ + +#include "Firestore/core/src/firebase/firestore/util/comparison.h" + +#include +#include + +#include + +#include "Firestore/core/src/firebase/firestore/util/string_printf.h" +#include "gtest/gtest.h" + +namespace firebase { +namespace firestore { +namespace util { + +#define ASSERT_SAME(comparison) \ + do { \ + ASSERT_EQ(ComparisonResult::Same, comparison); \ + } while (0) + +#define ASSERT_ASCENDING(comparison) \ + do { \ + ASSERT_EQ(ComparisonResult::Ascending, comparison); \ + } while (0) + +#define ASSERT_DESCENDING(comparison) \ + do { \ + ASSERT_EQ(ComparisonResult::Descending, comparison); \ + } while (0) + +TEST(Comparison, ReverseOrder) { + ASSERT_ASCENDING(ReverseOrder(ComparisonResult::Descending)); + ASSERT_DESCENDING(ReverseOrder(ComparisonResult::Ascending)); + ASSERT_SAME(ReverseOrder(ComparisonResult::Same)); +} + +TEST(Comparison, StringCompare) { + ASSERT_ASCENDING(Compare("", "a")); + ASSERT_ASCENDING(Compare("a", "b")); + ASSERT_ASCENDING(Compare("a", "aa")); + + ASSERT_DESCENDING(Compare("a", "")); + ASSERT_DESCENDING(Compare("b", "a")); + ASSERT_DESCENDING(Compare("aa", "a")); + + ASSERT_SAME(Compare("", "")); + ASSERT_SAME(Compare("", std::string())); + ASSERT_SAME(Compare("a", "a")); +} + +TEST(Comparison, BooleanCompare) { + ASSERT_SAME(Compare(false, false)); + ASSERT_SAME(Compare(true, true)); + ASSERT_ASCENDING(Compare(false, true)); + ASSERT_DESCENDING(Compare(true, false)); +} + +TEST(Comparison, DoubleCompare) { + ASSERT_SAME(Compare(NAN, NAN)); + ASSERT_ASCENDING(Compare(NAN, 0)); + ASSERT_DESCENDING(Compare(0, NAN)); + + ASSERT_SAME(Compare(-INFINITY, -INFINITY)); + ASSERT_SAME(Compare(INFINITY, INFINITY)); + ASSERT_ASCENDING(Compare(-INFINITY, INFINITY)); + ASSERT_DESCENDING(Compare(INFINITY, -INFINITY)); + + ASSERT_SAME(Compare(0, 0)); + ASSERT_SAME(Compare(-0, -0)); + ASSERT_SAME(Compare(-0, 0)); +} + +#define ASSERT_BIT_EQUALS(expected, actual) \ + do { \ + uint64_t expectedBits = DoubleBits(expected); \ + uint64_t actualBits = DoubleBits(actual); \ + if (expectedBits != actualBits) { \ + std::string message = StringPrintf( \ + "Expected <%f> to compare equal to <%f> " \ + "with bits <%" PRIu64 "> equal to <%" PRIu64 ">", \ + actual, expected, actualBits, expectedBits); \ + FAIL() << message; \ + } \ + } while (0); + +#define ASSERT_MIXED_SAME(doubleValue, longValue) \ + do { \ + ComparisonResult result = CompareMixedNumber(doubleValue, longValue); \ + if (result != ComparisonResult::Same) { \ + std::string message = StringPrintf( \ + "Expected <%f> to compare equal to <%lld>", doubleValue, longValue); \ + FAIL() << message; \ + } \ + } while (0); + +#define ASSERT_MIXED_DESCENDING(doubleValue, longValue) \ + do { \ + ComparisonResult result = CompareMixedNumber(doubleValue, longValue); \ + if (result != ComparisonResult::Descending) { \ + std::string message = StringPrintf( \ + "Expected <%f> to compare equal to <%lld>", doubleValue, longValue); \ + FAIL() << message; \ + } \ + } while (0); + +#define ASSERT_MIXED_ASCENDING(doubleValue, longValue) \ + do { \ + ComparisonResult result = CompareMixedNumber(doubleValue, longValue); \ + if (result != ComparisonResult::Ascending) { \ + std::string message = StringPrintf( \ + "Expected <%f> to compare equal to <%lld>", doubleValue, longValue); \ + FAIL() << message; \ + } \ + } while (0); + +TEST(Comparison, MixedNumberCompare) { + // Infinities + ASSERT_MIXED_ASCENDING(-INFINITY, LLONG_MIN); + ASSERT_MIXED_ASCENDING(-INFINITY, LLONG_MAX); + ASSERT_MIXED_ASCENDING(-INFINITY, 0LL); + + ASSERT_MIXED_DESCENDING(INFINITY, LLONG_MIN); + ASSERT_MIXED_DESCENDING(INFINITY, LLONG_MAX); + ASSERT_MIXED_DESCENDING(INFINITY, 0LL); + + // NaN + ASSERT_MIXED_ASCENDING(NAN, LLONG_MIN); + ASSERT_MIXED_ASCENDING(NAN, LLONG_MAX); + ASSERT_MIXED_ASCENDING(NAN, 0LL); + + // Large values (note DBL_MIN is positive and near zero). + ASSERT_MIXED_ASCENDING(-DBL_MAX, LLONG_MIN); + + // Tests around LLONG_MIN + ASSERT_BIT_EQUALS((double)LLONG_MIN, -0x1.0p63); + ASSERT_MIXED_SAME(-0x1.0p63, LLONG_MIN); + ASSERT_MIXED_ASCENDING(-0x1.0p63, LLONG_MIN + 1); + + ASSERT_LT(-0x1.0000000000001p63, -0x1.0p63); + ASSERT_MIXED_ASCENDING(-0x1.0000000000001p63, LLONG_MIN); + ASSERT_MIXED_DESCENDING(-0x1.FFFFFFFFFFFFFp62, LLONG_MIN); + + // Tests around LLONG_MAX + // Note LLONG_MAX cannot be exactly represented by a double, so the system + // rounds it to the nearest double, which is 2^63. This number, in turn is + // larger than the maximum representable as a long. + ASSERT_BIT_EQUALS(0x1.0p63, (double)LLONG_MAX); + ASSERT_MIXED_DESCENDING(0x1.0p63, LLONG_MAX); + + // The largest value with an exactly long representation + ASSERT_EQ((int64_t)0x1.FFFFFFFFFFFFFp62, 0x7FFFFFFFFFFFFC00LL); + ASSERT_MIXED_SAME(0x1.FFFFFFFFFFFFFp62, 0x7FFFFFFFFFFFFC00LL); + + ASSERT_MIXED_DESCENDING(0x1.FFFFFFFFFFFFFp62, 0x7FFFFFFFFFFFFB00LL); + ASSERT_MIXED_DESCENDING(0x1.FFFFFFFFFFFFFp62, 0x7FFFFFFFFFFFFBFFLL); + ASSERT_MIXED_ASCENDING(0x1.FFFFFFFFFFFFFp62, 0x7FFFFFFFFFFFFC01LL); + ASSERT_MIXED_ASCENDING(0x1.FFFFFFFFFFFFFp62, 0x7FFFFFFFFFFFFD00LL); + + ASSERT_MIXED_ASCENDING(0x1.FFFFFFFFFFFFEp62, 0x7FFFFFFFFFFFFC00LL); + + // Tests around MAX_SAFE_INTEGER + ASSERT_MIXED_SAME(0x1.FFFFFFFFFFFFFp52, 0x1FFFFFFFFFFFFFLL); + ASSERT_MIXED_DESCENDING(0x1.FFFFFFFFFFFFFp52, 0x1FFFFFFFFFFFFELL); + ASSERT_MIXED_ASCENDING(0x1.FFFFFFFFFFFFEp52, 0x1FFFFFFFFFFFFFLL); + ASSERT_MIXED_ASCENDING(0x1.FFFFFFFFFFFFFp52, 0x20000000000000LL); + + // Tests around MIN_SAFE_INTEGER + ASSERT_MIXED_SAME(-0x1.FFFFFFFFFFFFFp52, -0x1FFFFFFFFFFFFFLL); + ASSERT_MIXED_ASCENDING(-0x1.FFFFFFFFFFFFFp52, -0x1FFFFFFFFFFFFELL); + ASSERT_MIXED_DESCENDING(-0x1.FFFFFFFFFFFFEp52, -0x1FFFFFFFFFFFFFLL); + ASSERT_MIXED_DESCENDING(-0x1.FFFFFFFFFFFFFp52, -0x20000000000000LL); + + // Tests around zero. + ASSERT_MIXED_SAME(-0.0, 0LL); + ASSERT_MIXED_SAME(0.0, 0LL); + + // The smallest representable positive value should be greater than zero + ASSERT_MIXED_DESCENDING(DBL_MIN, 0LL); + ASSERT_MIXED_ASCENDING(-DBL_MIN, 0LL); + + // Note that 0x1.0p-1074 is a hex floating point literal representing the + // minimum subnormal number: . + double minSubNormal = 0x1.0p-1074; + ASSERT_MIXED_DESCENDING(minSubNormal, 0LL); + ASSERT_MIXED_ASCENDING(-minSubNormal, 0LL); + + // Other sanity checks + ASSERT_MIXED_ASCENDING(0.5, 1LL); + ASSERT_MIXED_DESCENDING(0.5, 0LL); + ASSERT_MIXED_ASCENDING(1.5, 2LL); + ASSERT_MIXED_DESCENDING(1.5, 1LL); +} + +} // namespace util +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/test/firebase/firestore/util/iterator_adaptors_test.cc b/Firestore/core/test/firebase/firestore/util/iterator_adaptors_test.cc new file mode 100644 index 00000000000..4cd44ccea32 --- /dev/null +++ b/Firestore/core/test/firebase/firestore/util/iterator_adaptors_test.cc @@ -0,0 +1,1277 @@ +/* + * Copyright 2005, 2018 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. + */ + +#include "Firestore/core/src/firebase/firestore/util/iterator_adaptors.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/base/macros.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using std::unordered_map; +using std::unordered_set; + +using firebase::firestore::util::deref_second_view; +using firebase::firestore::util::deref_view; +using firebase::firestore::util::iterator_first; +using firebase::firestore::util::iterator_ptr; +using firebase::firestore::util::iterator_second; +using firebase::firestore::util::iterator_second_ptr; +using firebase::firestore::util::key_view; +using firebase::firestore::util::key_view_type; +using firebase::firestore::util::make_iterator_first; +using firebase::firestore::util::make_iterator_ptr; +using firebase::firestore::util::make_iterator_second; +using firebase::firestore::util::make_iterator_second_ptr; +using firebase::firestore::util::value_view; +using firebase::firestore::util::value_view_type; +using testing::ElementsAre; +using testing::Eq; +using testing::IsEmpty; +using testing::Not; +using testing::Pair; +using testing::Pointwise; +using testing::SizeIs; + +namespace { + +const char* kFirst[] = {"foo", "bar"}; +int kSecond[] = {1, 2}; +const int kCount = ABSL_ARRAYSIZE(kFirst); + +template +struct IsConst : std::false_type {}; +template +struct IsConst : std::true_type {}; +template +struct IsConst : IsConst {}; + +class IteratorAdaptorTest : public testing::Test { + protected: + // Objects declared here can be used by all tests in the test case for Foo. + + virtual void SetUp() { + ASSERT_EQ(ABSL_ARRAYSIZE(kFirst), ABSL_ARRAYSIZE(kSecond)); + } + + virtual void TearDown() { + } + + template + class InlineStorageIter : public std::iterator { + public: + T* operator->() const { + return get(); + } + T& operator*() const { + return *get(); + } + + private: + T* get() const { + return &v_; + } + mutable T v_; + }; + + struct X { + int d; + }; +}; + +TEST_F(IteratorAdaptorTest, HashMapFirst) { + // Adapts an iterator to return the first value of a unordered_map::iterator. + typedef unordered_map my_container; + my_container values; + for (int i = 0; i < kCount; ++i) { + values[kFirst[i]] = kSecond[i]; + } + for (iterator_first it = values.begin(); + it != values.end(); ++it) { + ASSERT_GT(it->length(), 0u); + } +} + +TEST_F(IteratorAdaptorTest, IteratorPtrUniquePtr) { + // Tests iterator_ptr with a vector>. + typedef std::vector> my_container; + typedef iterator_ptr my_iterator; + my_container values; + for (int i = 0; i < kCount; ++i) { + values.push_back(std::unique_ptr(new int(kSecond[i]))); + } + int i = 0; + for (my_iterator it = values.begin(); it != values.end(); ++it, ++i) { + int v = *it; + *it = v; + ASSERT_EQ(v, kSecond[i]); + } +} + +TEST_F(IteratorAdaptorTest, IteratorFirstConvertsToConst) { + // Adapts an iterator to return the first value of a unordered_map::iterator. + typedef unordered_map my_container; + my_container values; + for (int i = 0; i < kCount; ++i) { + values[kFirst[i]] = kSecond[i]; + } + iterator_first iter = values.begin(); + iterator_first c_iter = iter; + for (; c_iter != values.end(); ++c_iter) { + ASSERT_GT(c_iter->length(), 0u); + } +} + +TEST_F(IteratorAdaptorTest, IteratorFirstConstEqNonConst) { + // verify that const and non-const iterators return the same reference. + typedef std::vector> my_container; + typedef iterator_first my_iterator; + typedef iterator_first my_const_iterator; + my_container values; + for (int i = 0; i < kCount; ++i) { + values.push_back(std::make_pair(i, i + 1)); + } + my_iterator iter1 = values.begin(); + const my_iterator iter2 = iter1; + my_const_iterator c_iter1 = iter1; + const my_const_iterator c_iter2 = c_iter1; + for (int i = 0; i < kCount; ++i) { + int& v1 = iter1[i]; + int& v2 = iter2[i]; + EXPECT_EQ(&v1, &values[i].first); + EXPECT_EQ(&v1, &v2); + const int& cv1 = c_iter1[i]; + const int& cv2 = c_iter2[i]; + EXPECT_EQ(&cv1, &values[i].first); + EXPECT_EQ(&cv1, &cv2); + } +} + +TEST_F(IteratorAdaptorTest, HashMapSecond) { + // Adapts an iterator to return the second value of a unordered_map::iterator. + typedef unordered_map my_container; + my_container values; + for (int i = 0; i < kCount; ++i) { + values[kFirst[i]] = kSecond[i]; + } + for (iterator_second it = values.begin(); + it != values.end(); ++it) { + int v = *it; + ASSERT_GT(v, 0); + } +} + +TEST_F(IteratorAdaptorTest, IteratorSecondConvertsToConst) { + // Adapts an iterator to return the first value of a unordered_map::iterator. + typedef unordered_map my_container; + my_container values; + for (int i = 0; i < kCount; ++i) { + values[kFirst[i]] = kSecond[i]; + } + iterator_second iter = values.begin(); + iterator_second c_iter = iter; + for (; c_iter != values.end(); ++c_iter) { + int v = *c_iter; + ASSERT_GT(v, 0); + } +} + +TEST_F(IteratorAdaptorTest, IteratorSecondConstEqNonConst) { + // verify that const and non-const iterators return the same reference. + typedef std::vector> my_container; + typedef iterator_second my_iterator; + typedef iterator_second my_const_iterator; + my_container values; + for (int i = 0; i < kCount; ++i) { + values.push_back(std::make_pair(i, i + 1)); + } + my_iterator iter1 = values.begin(); + const my_iterator iter2 = iter1; + my_const_iterator c_iter1 = iter1; + const my_const_iterator c_iter2 = c_iter1; + for (int i = 0; i < kCount; ++i) { + int& v1 = iter1[i]; + int& v2 = iter2[i]; + EXPECT_EQ(&v1, &values[i].second); + EXPECT_EQ(&v1, &v2); + const int& cv1 = c_iter1[i]; + const int& cv2 = c_iter2[i]; + EXPECT_EQ(&cv1, &values[i].second); + EXPECT_EQ(&cv1, &cv2); + } +} + +TEST_F(IteratorAdaptorTest, IteratorSecondPtrConvertsToConst) { + // Adapts an iterator to return the first value of a unordered_map::iterator. + typedef unordered_map my_container; + my_container values; + for (int i = 0; i < kCount; ++i) { + values[kFirst[i]] = &kSecond[i]; + } + iterator_second_ptr iter = values.begin(); + iterator_second_ptr c_iter = iter; + for (; c_iter != values.end(); ++c_iter) { + int v = *c_iter; + ASSERT_GT(v, 0); + } +} + +TEST_F(IteratorAdaptorTest, IteratorSecondPtrConstMap) { + typedef const std::map ConstMap; + ConstMap empty_map; + + iterator_second_ptr it(empty_map.begin()); + ASSERT_TRUE(it == make_iterator_second_ptr(empty_map.end())); + if ((false)) { + // Just checking syntax/compilation/type-checking. + // iterator_second_ptr::value_type* v1 = &*it; + iterator_second_ptr::pointer v1 = &*it; + iterator_second_ptr::pointer v2 = + &*it.operator->(); + if (&v1 != &v2) v1 = v2; + } +} + +TEST_F(IteratorAdaptorTest, IteratorPtrConst) { + // This is a regression test for a const-related bug that bit CL 47984515, + // where a client created an iterator whose value type was "T* const". + std::map m; + make_iterator_ptr(make_iterator_first(m.begin())); +} + +TEST_F(IteratorAdaptorTest, IteratorSecondPtrConstEqNonConst) { + // verify that const and non-const iterators return the same reference. + typedef std::vector> my_container; + typedef iterator_second_ptr my_iterator; + typedef iterator_second_ptr my_const_iterator; + my_container values; + int ivalues[kCount]; + for (int i = 0; i < kCount; ++i) { + ivalues[i] = i; + values.push_back(std::make_pair(i, &ivalues[i])); + } + my_iterator iter1 = values.begin(); + const my_iterator iter2 = iter1; + my_const_iterator c_iter1 = iter1; + const my_const_iterator c_iter2 = c_iter1; + for (int i = 0; i < kCount; ++i) { + int& v1 = iter1[i]; + int& v2 = iter2[i]; + EXPECT_EQ(&v1, &ivalues[i]); + EXPECT_EQ(&v1, &v2); + const int& cv1 = c_iter1[i]; + const int& cv2 = c_iter2[i]; + EXPECT_EQ(&cv1, &ivalues[i]); + EXPECT_EQ(&cv1, &cv2); + } +} + +TEST_F(IteratorAdaptorTest, HashMapFirstConst) { + // Adapts an iterator to return the first value of a + // unordered_map::const_iterator. + typedef unordered_map my_container; + my_container values; + for (int i = 0; i < kCount; ++i) { + values[kFirst[i]] = kSecond[i]; + } + const unordered_map* cvalues = &values; + for (iterator_first it = cvalues->begin(); + it != cvalues->end(); ++it) { + ASSERT_GT(it->length(), 0u); + } +} + +TEST_F(IteratorAdaptorTest, ListFirst) { + // Adapts an iterator to return the first value of a list::iterator. + typedef std::pair my_pair; + typedef std::list my_list; + my_list values; + for (int i = 0; i < kCount; ++i) { + values.push_back(my_pair(kFirst[i], kSecond[i])); + } + int i = 0; + for (iterator_first it = values.begin(); + it != values.end(); ++it) { + ASSERT_EQ(*it, kFirst[i++]); + } +} + +TEST_F(IteratorAdaptorTest, ListSecondConst) { + // Adapts an iterator to return the second value from a list::const_iterator. + typedef std::pair my_pair; + typedef std::list my_list; + my_list values; + for (int i = 0; i < kCount; ++i) { + values.push_back(my_pair(kFirst[i], kSecond[i])); + } + int i = 0; + const my_list* cvalues = &values; + for (iterator_second it = cvalues->begin(); + it != cvalues->end(); ++it) { + ASSERT_EQ(*it, kSecond[i++]); + } +} + +TEST_F(IteratorAdaptorTest, VectorSecond) { + // Adapts an iterator to return the second value of a vector::iterator. + std::vector> values; + for (int i = 0; i < kCount; ++i) { + values.push_back(std::pair(kFirst[i], kSecond[i])); + } + int i = 0; + for (iterator_second>::iterator> it = + values.begin(); + it != values.end(); ++it) { + ASSERT_EQ(*it, kSecond[i++]); + } +} + +// Tests iterator_second_ptr with a map where values are regular pointers. +TEST_F(IteratorAdaptorTest, HashMapSecondPtr) { + typedef unordered_map my_container; + typedef iterator_second_ptr my_iterator; + my_container values; + for (int i = 0; i < kCount; ++i) { + values[kFirst[i]] = kSecond + i; + } + for (my_iterator it = values.begin(); it != values.end(); ++it) { + int v = *it; + + // Make sure the iterator reference type is assignable ("int&" and not + // "const int&"). If it isn't, this becomes a compile-time error. + *it = v; + + ASSERT_GT(v, 0); + } +} + +// Tests iterator_second_ptr with a map where values are wrapped into +// linked_ptr. +TEST_F(IteratorAdaptorTest, HashMapSecondPtrLinkedPtr) { + typedef unordered_map> my_container; + typedef iterator_second_ptr my_iterator; + my_container values; + for (int i = 0; i < kCount; ++i) { + values[kFirst[i]].reset(new int(kSecond[i])); + } + for (my_iterator it = values.begin(); it != values.end(); ++it) { + ASSERT_EQ(&*it, it.operator->()); + int v = *it; + *it = v; + ASSERT_GT(v, 0); + } +} + +// Tests iterator_ptr with a vector where values are regular pointers. +TEST_F(IteratorAdaptorTest, IteratorPtrPtr) { + typedef std::vector my_container; + typedef iterator_ptr my_iterator; + my_container values; + for (int i = 0; i < kCount; ++i) { + values.push_back(kSecond + i); + } + int i = 0; + for (my_iterator it = values.begin(); it != values.end(); ++it, ++i) { + int v = *it; + *it = v; + ASSERT_EQ(v, kSecond[i]); + } +} + +TEST_F(IteratorAdaptorTest, IteratorPtrExplicitPtrType) { + struct A {}; + struct B : A {}; + std::vector v; + const std::vector& cv = v; + iterator_ptr::iterator, A*> ip(v.begin()); + iterator_ptr::const_iterator, A*> cip(cv.begin()); +} + +TEST_F(IteratorAdaptorTest, IteratorPtrtConstEqNonConst) { + // verify that const and non-const iterators return the same reference. + typedef std::vector my_container; + typedef iterator_ptr my_iterator; + typedef iterator_ptr my_const_iterator; + my_container values; + + for (int i = 0; i < kCount; ++i) { + values.push_back(kSecond + i); + } + my_iterator iter1 = values.begin(); + const my_iterator iter2 = iter1; + my_const_iterator c_iter1 = iter1; + const my_const_iterator c_iter2 = iter1; + for (int i = 0; i < kCount; ++i) { + int& v1 = iter1[i]; + int& v2 = iter2[i]; + EXPECT_EQ(&v1, kSecond + i); + EXPECT_EQ(&v1, &v2); + const int& cv1 = c_iter1[i]; + const int& cv2 = c_iter2[i]; + EXPECT_EQ(&cv1, kSecond + i); + EXPECT_EQ(&cv1, &cv2); + } +} + +// Tests iterator_ptr with a vector where values are wrapped into +// std::shared_ptr. +TEST_F(IteratorAdaptorTest, IteratorPtrLinkedPtr) { + typedef std::vector> my_container; + typedef iterator_ptr my_iterator; + my_container values; + for (int i = 0; i < kCount; ++i) { + values.push_back(std::make_shared(kSecond[i])); + } + int i = 0; + for (my_iterator it = values.begin(); it != values.end(); ++it, ++i) { + ASSERT_EQ(&*it, it.operator->()); + int v = *it; + *it = v; + ASSERT_EQ(v, kSecond[i]); + } +} + +TEST_F(IteratorAdaptorTest, IteratorPtrConvertsToConst) { + int value = 1; + std::vector values; + values.push_back(&value); + iterator_ptr::iterator> iter = values.begin(); + iterator_ptr::const_iterator> c_iter = iter; + EXPECT_EQ(1, *c_iter); +} + +TEST_F(IteratorAdaptorTest, IteratorFirstHasRandomAccessMethods) { + typedef std::vector> my_container; + typedef iterator_first my_iterator; + + my_container values; + for (int i = 0; i < kCount; ++i) { + values.push_back(std::pair(kFirst[i], kSecond[i])); + } + + my_iterator it1 = values.begin(), it2 = values.end(); + + EXPECT_EQ(kCount, it2 - it1); + EXPECT_TRUE(it1 < it2); + it1 += kCount; + EXPECT_TRUE(it1 == it2); + it1 -= kCount; + EXPECT_EQ(kFirst[0], *it1); + EXPECT_EQ(kFirst[1], *(it1 + 1)); + EXPECT_TRUE(it1 == it2 - kCount); + EXPECT_TRUE(kCount + it1 == it2); + EXPECT_EQ(kFirst[1], it1[1]); + it2[-1] = "baz"; + EXPECT_EQ("baz", values[kCount - 1].first); +} + +TEST_F(IteratorAdaptorTest, IteratorSecondHasRandomAccessMethods) { + typedef std::vector> my_container; + typedef iterator_second my_iterator; + + my_container values; + for (int i = 0; i < kCount; ++i) { + values.push_back(std::pair(kFirst[i], kSecond[i])); + } + + my_iterator it1 = values.begin(), it2 = values.end(); + + EXPECT_EQ(kCount, it2 - it1); + EXPECT_TRUE(it1 < it2); + it1 += kCount; + EXPECT_TRUE(it1 == it2); + it1 -= kCount; + EXPECT_EQ(kSecond[0], *it1); + EXPECT_EQ(kSecond[1], *(it1 + 1)); + EXPECT_TRUE(it1 == it2 - kCount); + EXPECT_TRUE(kCount + it1 == it2); + EXPECT_EQ(kSecond[1], it1[1]); + it2[-1] = 99; + EXPECT_EQ(99, values[kCount - 1].second); +} + +TEST_F(IteratorAdaptorTest, IteratorSecondPtrHasRandomAccessMethods) { + typedef std::vector> my_container; + typedef iterator_second_ptr my_iterator; + + ASSERT_GE(kCount, 2); + int value1 = 17; + int value2 = 99; + my_container values; + values.push_back(std::pair(kFirst[0], &value1)); + values.push_back(std::pair(kFirst[1], &value2)); + + my_iterator it1 = values.begin(), it2 = values.end(); + + EXPECT_EQ(2, it2 - it1); + EXPECT_TRUE(it1 < it2); + it1 += 2; + EXPECT_TRUE(it1 == it2); + it1 -= 2; + EXPECT_EQ(17, *it1); + EXPECT_EQ(99, *(it1 + 1)); + EXPECT_TRUE(it1 == it2 - 2); + EXPECT_TRUE(2 + it1 == it2); + EXPECT_EQ(99, it1[1]); + it2[-1] = 88; + EXPECT_EQ(88, value2); +} + +TEST_F(IteratorAdaptorTest, IteratorPtrHasRandomAccessMethods) { + typedef std::vector my_container; + typedef iterator_ptr my_iterator; + + int value1 = 17; + int value2 = 99; + my_container values; + values.push_back(&value1); + values.push_back(&value2); + + my_iterator it1 = values.begin(), it2 = values.end(); + + EXPECT_EQ(2, it2 - it1); + EXPECT_TRUE(it1 < it2); + it1 += 2; + EXPECT_TRUE(it1 == it2); + it1 -= 2; + EXPECT_EQ(17, *it1); + EXPECT_EQ(99, *(it1 + 1)); + EXPECT_TRUE(it1 == it2 - 2); + EXPECT_TRUE(2 + it1 == it2); + EXPECT_EQ(99, it1[1]); + it2[-1] = 88; + EXPECT_EQ(88, value2); +} + +class MyInputIterator + : public std::iterator { + public: + explicit MyInputIterator(int* x) : x_(x) { + } + const int* operator*() const { + return x_; + } + MyInputIterator& operator++() { + ++*x_; + return *this; + } + + private: + int* x_; +}; + +TEST_F(IteratorAdaptorTest, IteratorPtrCanWrapInputIterator) { + int x = 0; + MyInputIterator it(&x); + iterator_ptr it1(it); + + EXPECT_EQ(0, *it1); + ++it1; + EXPECT_EQ(1, *it1); + ++it1; + EXPECT_EQ(2, *it1); + ++it1; +} + +// Tests that a default-constructed adaptor is equal to an adaptor explicitly +// constructed with a default underlying iterator. +TEST_F(IteratorAdaptorTest, DefaultAdaptorConstructorUsesDefaultValue) { + iterator_first*> first_default; + iterator_first*> first_null(nullptr); + ASSERT_TRUE(first_default == first_null); + + iterator_second*> second_default; + iterator_second*> second_null(nullptr); + ASSERT_TRUE(second_default == second_null); + + iterator_second_ptr*> second_ptr_default; + iterator_second_ptr*> second_ptr_null(nullptr); + ASSERT_TRUE(second_ptr_default == second_ptr_null); + + iterator_ptr ptr_default; + iterator_ptr ptr_null(nullptr); + ASSERT_TRUE(ptr_default == ptr_null); +} + +// Non C++11 test. +TEST_F(IteratorAdaptorTest, ValueView) { + typedef unordered_map MapType; + MapType my_map; + my_map[0] = "a"; + my_map[1] = "b"; + my_map[2] = "c"; + const MapType c_map(my_map); + + std::set vals; + auto view = value_view(c_map); + std::copy(view.begin(), view.end(), inserter(vals, vals.end())); + + EXPECT_THAT(vals, ElementsAre("a", "b", "c")); +} + +TEST_F(IteratorAdaptorTest, ValueView_Modify) { + typedef std::map MapType; + MapType my_map; + my_map[0] = 0; + my_map[1] = 1; + my_map[2] = 2; + EXPECT_THAT(my_map, ElementsAre(Pair(0, 0), Pair(1, 1), Pair(2, 2))); + + value_view_type::type vv = value_view(my_map); + std::replace(vv.begin(), vv.end(), 2, 3); + std::replace(vv.begin(), vv.end(), 1, 2); + + EXPECT_THAT(my_map, ElementsAre(Pair(0, 0), Pair(1, 2), Pair(2, 3))); +} + +TEST_F(IteratorAdaptorTest, ValueViewOfValueView) { + typedef std::pair pair_int_str; + typedef std::map map_int_pair_int_str; + map_int_pair_int_str my_map; + my_map[0] = std::make_pair(1, std::string("a")); + my_map[2] = std::make_pair(3, std::string("b")); + my_map[4] = std::make_pair(5, std::string("c")); + + // This is basically typechecking of the generated views. So we generate the + // types and have the compiler verify the generated template instantiation. + typedef value_view_type::type + value_view_map_int_pair_int_str_type; + + static_assert( + (std::is_same::value), + "value_view_value_type_"); + + typedef value_view_type::type + view_view_type; + + static_assert((std::is_same::value), + "view_view_type_"); + + value_view_map_int_pair_int_str_type vv = value_view(my_map); + view_view_type helper = value_view(vv); + + EXPECT_THAT(std::set(helper.begin(), helper.end()), + ElementsAre("a", "b", "c")); +} + +TEST_F(IteratorAdaptorTest, ValueViewAndKeyViewCopy) { + std::map my_map; + my_map[0] = "0"; + my_map[1] = "1"; + my_map[2] = "2"; + std::set keys; + std::set vals; + + auto kv = key_view(my_map); + std::copy(kv.begin(), kv.end(), inserter(keys, keys.end())); + + auto vv = value_view(my_map); + std::copy(vv.begin(), vv.end(), inserter(vals, vals.end())); + EXPECT_THAT(keys, ElementsAre(0, 1, 2)); + EXPECT_THAT(vals, ElementsAre("0", "1", "2")); +} + +TEST_F(IteratorAdaptorTest, ValueViewAndKeyViewRangeBasedLoop) { + std::map my_map; + my_map[0] = "0"; + my_map[1] = "1"; + my_map[2] = "2"; + std::set keys; + std::set vals; + for (auto key : key_view(my_map)) { + keys.insert(key); + } + for (auto val : value_view(my_map)) { + vals.insert(val); + } + EXPECT_THAT(keys, ElementsAre(0, 1, 2)); + EXPECT_THAT(vals, ElementsAre("0", "1", "2")); +} + +template +class FixedSizeContainer { + public: + // NOTE: the container does on purpose not define: + // reference, const_reference, pointer, const_pointer, size_type, + // difference_type, empty(). + typedef std::pair value_type; + typedef value_type* iterator; + typedef const value_type* const_iterator; + + FixedSizeContainer() { + } + const_iterator begin() const { + return &values[0]; + } + iterator begin() { + return &values[0]; + } + const_iterator end() const { + return &values[N]; + } + iterator end() { + return &values[N]; + } + value_type at(int n) const { + return values[n]; + } + value_type& operator[](int n) { + return values[n]; + } + int size() const { + return N; + } + + private: + static constexpr int kAllocatedSize = N ? N : 1; + value_type values[kAllocatedSize]; + // NOTE: the container does on purpose not define: + // reference, const_reference, pointer, const_pointer, size_type, + // difference_type, empty(). +}; + +TEST_F(IteratorAdaptorTest, ProvidesEmpty) { + { + FixedSizeContainer<0, int, int> container0; + EXPECT_TRUE(value_view(container0).empty()); + FixedSizeContainer<1, int, int> container1; + EXPECT_FALSE(value_view(container1).empty()); + } + { + std::map container; + EXPECT_TRUE(value_view(container).empty()); + container.insert(std::make_pair(0, 0)); + EXPECT_FALSE(value_view(container).empty()); + } +} + +TEST_F(IteratorAdaptorTest, ValueViewWithPoorlyTypedHomeGrownContainer) { + FixedSizeContainer<3, int, std::string> container; + container[0] = std::make_pair(0, std::string("0")); + container[1] = std::make_pair(1, std::string("1")); + container[2] = std::make_pair(2, std::string("2")); + EXPECT_EQ(3, container.size()); + EXPECT_EQ(container.at(0), std::make_pair(0, std::string("0"))); + EXPECT_EQ(container.at(1), std::make_pair(1, std::string("1"))); + EXPECT_EQ(container.at(2), std::make_pair(2, std::string("2"))); + std::vector keys; + std::vector vals; + + auto kv = key_view(container); + std::copy(kv.begin(), kv.end(), back_inserter(keys)); + auto vv = value_view(container); + std::copy(vv.begin(), vv.end(), back_inserter(vals)); + EXPECT_THAT(keys, ElementsAre(0, 1, 2)); + EXPECT_THAT(vals, ElementsAre("0", "1", "2")); +} + +TEST_F(IteratorAdaptorTest, ValueViewConstIterators) { + unordered_map my_map; + my_map[0] = "a"; + my_map[1] = "b"; + my_map[2] = "c"; + + std::set vals; + // iterator_view_helper defines cbegin() and cend(); we're not invoking the + // C++11 functions of the same name. + for (iterator_second::const_iterator> it = + value_view(my_map).cbegin(); + it != value_view(my_map).cend(); ++it) { + vals.insert(*it); + } + + EXPECT_TRUE(vals.find("a") != vals.end()); + EXPECT_TRUE(vals.find("b") != vals.end()); + EXPECT_TRUE(vals.find("c") != vals.end()); +} + +TEST_F(IteratorAdaptorTest, ValueViewInConstContext) { + using firebase::firestore::util::internal::iterator_view_helper; + unordered_map my_map; + my_map[0] = "a"; + my_map[1] = "b"; + my_map[2] = "c"; + + std::set vals; + const iterator_view_helper< + unordered_map, + iterator_second::iterator>, + iterator_second::const_iterator>> + const_view = value_view(my_map); + for (iterator_second::const_iterator> it = + const_view.begin(); + it != const_view.end(); ++it) { + vals.insert(*it); + } + + EXPECT_TRUE(vals.find("a") != vals.end()); + EXPECT_TRUE(vals.find("b") != vals.end()); + EXPECT_TRUE(vals.find("c") != vals.end()); +} + +TEST_F(IteratorAdaptorTest, ConstValueView) { + unordered_map my_map; + my_map[0] = "a"; + my_map[1] = "b"; + my_map[2] = "c"; + + const unordered_map& const_map = my_map; + + std::set vals; + for (iterator_second::const_iterator> it = + value_view(const_map).begin(); + it != value_view(const_map).end(); ++it) { + vals.insert(*it); + } + + EXPECT_TRUE(vals.find("a") != vals.end()); + EXPECT_TRUE(vals.find("b") != vals.end()); + EXPECT_TRUE(vals.find("c") != vals.end()); +} + +TEST_F(IteratorAdaptorTest, ConstValueViewConstIterators) { + unordered_map my_map; + my_map[0] = "a"; + my_map[1] = "b"; + my_map[2] = "c"; + + const unordered_map& const_map = my_map; + + std::set vals; + // iterator_view_helper defines cbegin() and cend(); we're not invoking the + // C++11 functions of the same name. + for (iterator_second::const_iterator> it = + value_view(const_map).cbegin(); + it != value_view(const_map).cend(); ++it) { + vals.insert(*it); + } + + EXPECT_TRUE(vals.find("a") != vals.end()); + EXPECT_TRUE(vals.find("b") != vals.end()); + EXPECT_TRUE(vals.find("c") != vals.end()); +} + +TEST_F(IteratorAdaptorTest, ConstValueViewInConstContext) { + unordered_map my_map; + my_map[0] = "a"; + my_map[1] = "b"; + my_map[2] = "c"; + + const unordered_map& const_map = my_map; + + std::set vals; + const value_view_type>::type + const_view = value_view(const_map); + for (iterator_second::const_iterator> it = + const_view.begin(); + it != const_view.end(); ++it) { + vals.insert(*it); + } + + EXPECT_TRUE(vals.find("a") != vals.end()); + EXPECT_TRUE(vals.find("b") != vals.end()); + EXPECT_TRUE(vals.find("c") != vals.end()); +} + +TEST_F(IteratorAdaptorTest, KeyView) { + unordered_map my_map; + my_map[0] = "a"; + my_map[1] = "b"; + my_map[2] = "c"; + + std::set vals; + for (iterator_first::iterator> it = + key_view(my_map).begin(); + it != key_view(my_map).end(); ++it) { + vals.insert(*it); + } + + EXPECT_TRUE(vals.find(0) != vals.end()); + EXPECT_TRUE(vals.find(1) != vals.end()); + EXPECT_TRUE(vals.find(2) != vals.end()); +} + +TEST_F(IteratorAdaptorTest, KeyViewConstIterators) { + unordered_map my_map; + my_map[0] = "a"; + my_map[1] = "b"; + my_map[2] = "c"; + + std::set vals; + // iterator_view_helper defines cbegin() and cend(); we're not invoking the + // C++11 functions of the same name. + for (iterator_first::const_iterator> it = + key_view(my_map).cbegin(); + it != key_view(my_map).cend(); ++it) { + vals.insert(*it); + } + + EXPECT_TRUE(vals.find(0) != vals.end()); + EXPECT_TRUE(vals.find(1) != vals.end()); + EXPECT_TRUE(vals.find(2) != vals.end()); +} + +TEST_F(IteratorAdaptorTest, KeyViewInConstContext) { + unordered_map my_map; + my_map[0] = "a"; + my_map[1] = "b"; + my_map[2] = "c"; + + std::set vals; + const key_view_type>::type const_view = + key_view(my_map); + for (iterator_first::const_iterator> it = + const_view.begin(); + it != const_view.end(); ++it) { + vals.insert(*it); + } + + EXPECT_TRUE(vals.find(0) != vals.end()); + EXPECT_TRUE(vals.find(1) != vals.end()); + EXPECT_TRUE(vals.find(2) != vals.end()); +} + +TEST_F(IteratorAdaptorTest, ConstKeyView) { + unordered_map my_map; + my_map[0] = "a"; + my_map[1] = "b"; + my_map[2] = "c"; + + const unordered_map& const_map = my_map; + + std::set vals; + for (iterator_first::const_iterator> it = + key_view(const_map).begin(); + it != key_view(const_map).end(); ++it) { + vals.insert(*it); + } + + EXPECT_TRUE(vals.find(0) != vals.end()); + EXPECT_TRUE(vals.find(1) != vals.end()); + EXPECT_TRUE(vals.find(2) != vals.end()); +} + +TEST_F(IteratorAdaptorTest, ConstKeyViewConstIterators) { + unordered_map my_map; + my_map[0] = "a"; + my_map[1] = "b"; + my_map[2] = "c"; + + const unordered_map& const_map = my_map; + + std::set vals; + // iterator_view_helper defines cbegin() and cend(); we're not invoking the + // C++11 functions of the same name. + for (iterator_first::const_iterator> it = + key_view(const_map).cbegin(); + it != key_view(const_map).cend(); ++it) { + vals.insert(*it); + } + + EXPECT_TRUE(vals.find(0) != vals.end()); + EXPECT_TRUE(vals.find(1) != vals.end()); + EXPECT_TRUE(vals.find(2) != vals.end()); +} + +TEST_F(IteratorAdaptorTest, ConstKeyViewInConstContext) { + unordered_map my_map; + my_map[0] = "a"; + my_map[1] = "b"; + my_map[2] = "c"; + + const unordered_map& const_map = my_map; + + std::set vals; + const key_view_type>::type const_view = + key_view(const_map); + for (iterator_first::const_iterator> it = + const_view.begin(); + it != const_view.end(); ++it) { + vals.insert(*it); + } + + EXPECT_TRUE(vals.find(0) != vals.end()); + EXPECT_TRUE(vals.find(1) != vals.end()); + EXPECT_TRUE(vals.find(2) != vals.end()); +} + +TEST_F(IteratorAdaptorTest, IteratorViewHelperDefinesIterator) { + using firebase::firestore::util::internal::iterator_view_helper; + unordered_set my_set; + my_set.insert(1); + my_set.insert(0); + my_set.insert(2); + + typedef iterator_view_helper, unordered_set::iterator, + unordered_set::const_iterator> + SetView; + SetView set_view(my_set); + unordered_set vals; + for (SetView::iterator it = set_view.begin(); it != set_view.end(); ++it) { + vals.insert(*it); + } + + EXPECT_TRUE(vals.find(0) != vals.end()); + EXPECT_TRUE(vals.find(1) != vals.end()); + EXPECT_TRUE(vals.find(2) != vals.end()); +} + +TEST_F(IteratorAdaptorTest, IteratorViewHelperDefinesConstIterator) { + using firebase::firestore::util::internal::iterator_view_helper; + unordered_set my_set; + my_set.insert(1); + my_set.insert(0); + my_set.insert(2); + + typedef iterator_view_helper, unordered_set::iterator, + unordered_set::const_iterator> + SetView; + SetView set_view(my_set); + unordered_set vals; + for (SetView::const_iterator it = set_view.begin(); it != set_view.end(); + ++it) { + vals.insert(*it); + } + + EXPECT_TRUE(vals.find(0) != vals.end()); + EXPECT_TRUE(vals.find(1) != vals.end()); + EXPECT_TRUE(vals.find(2) != vals.end()); +} + +TEST_F(IteratorAdaptorTest, ViewTypeParameterConstVsNonConst) { + typedef unordered_map M; + M m; + const M& cm = m; + + typedef key_view_type::type KV; + typedef key_view_type::type KVC; + typedef value_view_type::type VV; + typedef value_view_type::type VVC; + + // key_view: + KV ABSL_ATTRIBUTE_UNUSED kv1 = key_view(m); // lvalue + KVC ABSL_ATTRIBUTE_UNUSED kv2 = key_view(m); // conversion to const + KVC ABSL_ATTRIBUTE_UNUSED kv3 = key_view(cm); // const from const lvalue + KVC ABSL_ATTRIBUTE_UNUSED kv4 = key_view(M()); // const from rvalue + // Direct initialization (without key_view function) + KV ABSL_ATTRIBUTE_UNUSED kv5(m); + KVC ABSL_ATTRIBUTE_UNUSED kv6(m); + KVC ABSL_ATTRIBUTE_UNUSED kv7(cm); + KVC ABSL_ATTRIBUTE_UNUSED kv8((M())); + + // value_view: + VV ABSL_ATTRIBUTE_UNUSED vv1 = value_view(m); // lvalue + VVC ABSL_ATTRIBUTE_UNUSED vv2 = value_view(m); // conversion to const + VVC ABSL_ATTRIBUTE_UNUSED vv3 = value_view(cm); // const from const lvalue + VVC ABSL_ATTRIBUTE_UNUSED vv4 = value_view(M()); // const from rvalue + // Direct initialization (without value_view function) + VV ABSL_ATTRIBUTE_UNUSED vv5(m); + VVC ABSL_ATTRIBUTE_UNUSED vv6(m); + VVC ABSL_ATTRIBUTE_UNUSED vv7(cm); + VVC ABSL_ATTRIBUTE_UNUSED vv8((M())); +} + +TEST_F(IteratorAdaptorTest, EmptyAndSize) { + { + FixedSizeContainer<0, int, std::string*> container; + EXPECT_TRUE(key_view(container).empty()); + EXPECT_TRUE(value_view(container).empty()); + EXPECT_EQ(0u, key_view(container).size()); + EXPECT_EQ(0u, value_view(container).size()); + } + { + FixedSizeContainer<2, int, std::string*> container; + EXPECT_FALSE(key_view(container).empty()); + EXPECT_FALSE(value_view(container).empty()); + EXPECT_EQ(2u, key_view(container).size()); + EXPECT_EQ(2u, value_view(container).size()); + } + { + std::map container; + EXPECT_TRUE(key_view(container).empty()); + EXPECT_TRUE(value_view(container).empty()); + EXPECT_EQ(0u, key_view(container).size()); + EXPECT_EQ(0u, value_view(container).size()); + std::string s0 = "s0"; + std::string s1 = "s1"; + container.insert(std::make_pair("0", &s0)); + container.insert(std::make_pair("1", &s0)); + EXPECT_FALSE(key_view(container).empty()); + EXPECT_FALSE(value_view(container).empty()); + EXPECT_EQ(2u, key_view(container).size()); + EXPECT_EQ(2u, value_view(container).size()); + } +} + +TEST_F(IteratorAdaptorTest, View_IsEmpty) { + EXPECT_THAT(key_view(std::map()), IsEmpty()); + EXPECT_THAT(key_view(FixedSizeContainer<2, int, int>()), Not(IsEmpty())); +} + +TEST_F(IteratorAdaptorTest, View_SizeIs) { + EXPECT_THAT(key_view(std::map()), SizeIs(0)); + EXPECT_THAT(key_view(FixedSizeContainer<2, int, int>()), SizeIs(2)); +} + +TEST_F(IteratorAdaptorTest, View_Pointwise) { + typedef std::map MapType; + MapType my_map; + my_map[0] = "a"; + my_map[1] = "b"; + my_map[2] = "c"; + + std::vector expected; + expected.push_back("a"); + expected.push_back("b"); + expected.push_back("c"); + + EXPECT_THAT(value_view(my_map), Pointwise(Eq(), expected)); +} + +TEST_F(IteratorAdaptorTest, DerefView) { + typedef std::vector ContainerType; + int v0 = 0; + int v1 = 1; + ContainerType c; + c.push_back(&v0); + c.push_back(&v1); + EXPECT_THAT(deref_view(c), ElementsAre(0, 1)); + *deref_view(c).begin() = 2; + EXPECT_THAT(v0, 2); + EXPECT_THAT(deref_view(c), ElementsAre(2, 1)); + const std::vector cc(c); + EXPECT_THAT(deref_view(cc), ElementsAre(2, 1)); +} + +TEST_F(IteratorAdaptorTest, ConstDerefView) { + typedef std::vector ContainerType; + const std::string s0 = "0"; + const std::string s1 = "1"; + ContainerType c; + c.push_back(&s0); + c.push_back(&s1); + EXPECT_THAT(deref_view(c), ElementsAre("0", "1")); +} + +TEST_F(IteratorAdaptorTest, DerefSecondView) { + typedef std::map ContainerType; + int v0 = 0; + int v1 = 1; + ContainerType c; + c.insert({10, &v0}); + c.insert({11, &v1}); + EXPECT_THAT(deref_second_view(c), ElementsAre(0, 1)); + *deref_second_view(c).begin() = 2; + EXPECT_THAT(v0, 2); + EXPECT_THAT(deref_second_view(c), ElementsAre(2, 1)); + const std::map cc(c); + EXPECT_THAT(deref_second_view(cc), ElementsAre(2, 1)); +} + +TEST_F(IteratorAdaptorTest, ConstDerefSecondView) { + typedef std::map ContainerType; + const std::string s0 = "0"; + const std::string s1 = "1"; + ContainerType c; + c.insert({10, &s0}); + c.insert({11, &s1}); + EXPECT_THAT(deref_second_view(c), ElementsAre("0", "1")); +} + +namespace { +template +std::vector ToVec(const T& t) { + return std::vector(t.begin(), t.end()); +} +} // namespace + +TEST_F(IteratorAdaptorTest, ReverseView) { + using firebase::firestore::util::reversed_view; + + int arr[] = {0, 1, 2, 3, 4, 5, 6}; + int* arr_end = arr + sizeof(arr) / sizeof(arr[0]); + std::vector vec(arr, arr_end); + const std::vector cvec(arr, arr_end); + + EXPECT_THAT(ToVec(reversed_view(vec)), ElementsAre(6, 5, 4, 3, 2, 1, 0)); + EXPECT_THAT(ToVec(reversed_view(cvec)), ElementsAre(6, 5, 4, 3, 2, 1, 0)); +} + +TEST_F(IteratorAdaptorTest, IteratorPtrConstConversions) { + // Users depend on this. It has to keep working. + std::vector v; + const std::vector& cv = v; + EXPECT_TRUE(make_iterator_ptr(cv.end()) == make_iterator_ptr(v.end())); + EXPECT_FALSE(make_iterator_ptr(cv.end()) != make_iterator_ptr(v.end())); + // EXPECT_TRUE(make_iterator_ptr(v.end()) == make_iterator_ptr(cv.end())); + // EXPECT_FALSE(make_iterator_ptr(v.end()) != make_iterator_ptr(cv.end())); +} + +TEST_F(IteratorAdaptorTest, IteratorPtrDeepConst) { + typedef std::vector PtrsToMutable; + typedef iterator_ptr ConstIter; + EXPECT_TRUE((std::is_same::value)); + EXPECT_TRUE(IsConst::value); + + typedef iterator_ptr Iter; + EXPECT_TRUE((std::is_same::value)); + EXPECT_FALSE(IsConst::value); +} + +TEST_F(IteratorAdaptorTest, ReverseViewCxx11) { + using firebase::firestore::util::reversed_view; + + int arr[] = {0, 1, 2, 3, 4, 5, 6}; + int* arr_end = arr + sizeof(arr) / sizeof(arr[0]); + std::vector vec(arr, arr_end); + + // Try updates and demonstrate this work with C++11 for loops. + for (auto& i : reversed_view(vec)) ++i; + EXPECT_THAT(vec, ElementsAre(1, 2, 3, 4, 5, 6, 7)); +} + +TEST_F(IteratorAdaptorTest, BaseIterDanglingRefFirst) { + // Some iterators will hold 'on-board storage' for a synthesized value. + // We must take care not to pull our adapted reference from + // a temporary copy of the base iterator. See b/15113033. + typedef std::pair Val; + InlineStorageIter iter; + iterator_first> iter2(iter); + EXPECT_EQ(&iter2.base()->first, &*iter2); + EXPECT_EQ(&iter2.base()->first.d, &iter2->d); +} + +TEST_F(IteratorAdaptorTest, BaseIterDanglingRefSecond) { + typedef std::pair Val; + InlineStorageIter iter; + iterator_second> iter2(iter); + EXPECT_EQ(&iter2.base()->second, &*iter2); + EXPECT_EQ(&iter2.base()->second.d, &iter2->d); +} + +} // namespace diff --git a/Firestore/core/test/firebase/firestore/util/log_test.cc b/Firestore/core/test/firebase/firestore/util/log_test.cc index 46cbc4e6275..973b1749dbf 100644 --- a/Firestore/core/test/firebase/firestore/util/log_test.cc +++ b/Firestore/core/test/firebase/firestore/util/log_test.cc @@ -30,7 +30,7 @@ namespace util { // // You can fix it with: // -// defaults write firebase_firestore_util_log_apple_test \ +// defaults write firebase_firestore_util_log_apple_test // /google/firebase/debug_mode NO TEST(Log, SetAndGet) { LogSetLevel(kLogLevelVerbose); diff --git a/Firestore/Port/ordered_code_test.cc b/Firestore/core/test/firebase/firestore/util/ordered_code_test.cc similarity index 69% rename from Firestore/Port/ordered_code_test.cc rename to Firestore/core/test/firebase/firestore/util/ordered_code_test.cc index 0a339fc2301..fd2ce83c7a3 100644 --- a/Firestore/Port/ordered_code_test.cc +++ b/Firestore/core/test/firebase/firestore/util/ordered_code_test.cc @@ -14,63 +14,22 @@ * limitations under the License. */ -#include "Firestore/Port/ordered_code.h" +#include "Firestore/core/src/firebase/firestore/util/ordered_code.h" -// #include -// #include #include #include -#include "base/logging.h" -#include "testing/base/public/gunit.h" -#include -#include "util/random/acmrandom.h" - -using Firestore::OrderedCode; -using leveldb::Slice; - -// Make Slices writeable to ostream, making all the CHECKs happy below. -namespace { -void WritePadding(std::ostream& o, size_t pad) { - char fill_buf[32]; - memset(fill_buf, o.fill(), sizeof(fill_buf)); - while (pad) { - size_t n = std::min(pad, sizeof(fill_buf)); - o.write(fill_buf, n); - pad -= n; - } -} -} // namespace - -namespace leveldb { - -std::ostream& operator<<(std::ostream& o, const Slice slice) { - std::ostream::sentry sentry(o); - if (sentry) { - size_t lpad = 0; - size_t rpad = 0; - if (o.width() > slice.size()) { - size_t pad = o.width() - slice.size(); - if ((o.flags() & o.adjustfield) == o.left) { - rpad = pad; - } else { - lpad = pad; - } - } - if (lpad) WritePadding(o, lpad); - o.write(slice.data(), slice.size()); - if (rpad) WritePadding(o, rpad); - o.width(0); - } - return o; -} +#include "Firestore/core/src/firebase/firestore/util/secure_random.h" +#include "gtest/gtest.h" -} // namespace leveldb +namespace firebase { +namespace firestore { +namespace util { -static std::string RandomString(ACMRandom* rnd, int len) { +static std::string RandomString(SecureRandom* rnd, int len) { std::string x; for (int i = 0; i < len; i++) { - x += rnd->Uniform(256); + x += static_cast(rnd->Uniform(256)); } return x; } @@ -82,7 +41,7 @@ static std::string RandomString(ACMRandom* rnd, int len) { template static void OCWriteIncreasing(std::string* dest, const T& val); template -static bool OCReadIncreasing(Slice* src, T* result); +static bool OCReadIncreasing(absl::string_view* src, T* result); // Read/WriteIncreasing template <> @@ -90,7 +49,8 @@ void OCWriteIncreasing(std::string* dest, const std::string& val) { OrderedCode::WriteString(dest, val); } template <> -bool OCReadIncreasing(Slice* src, std::string* result) { +bool OCReadIncreasing(absl::string_view* src, + std::string* result) { return OrderedCode::ReadString(src, result); } @@ -100,7 +60,7 @@ void OCWriteIncreasing(std::string* dest, const uint64_t& val) { OrderedCode::WriteNumIncreasing(dest, val); } template <> -bool OCReadIncreasing(Slice* src, uint64_t* result) { +bool OCReadIncreasing(absl::string_view* src, uint64_t* result) { return OrderedCode::ReadNumIncreasing(src, result); } @@ -112,12 +72,13 @@ void OCWriteIncreasing(std::string* dest, const int64_t& val) { OrderedCode::WriteSignedNumIncreasing(dest, val); } template <> -bool OCReadIncreasing(Slice* src, int64_t* result) { +bool OCReadIncreasing(absl::string_view* src, int64_t* result) { return OrderedCode::ReadSignedNumIncreasing(src, result); } template std::string OCWrite(T val, Direction direction) { + EXPECT_EQ(INCREASING, direction); // DECREASING never implemented. std::string result; OCWriteIncreasing(&result, val); return result; @@ -125,11 +86,13 @@ std::string OCWrite(T val, Direction direction) { template void OCWriteToString(std::string* result, T val, Direction direction) { + EXPECT_EQ(INCREASING, direction); // DECREASING never implemented. OCWriteIncreasing(result, val); } template -bool OCRead(Slice* s, T* val, Direction direction) { +bool OCRead(absl::string_view* s, T* val, Direction direction) { + EXPECT_EQ(INCREASING, direction); // DECREASING never implemented. return OCReadIncreasing(s, val); } @@ -139,16 +102,16 @@ bool OCRead(Slice* s, T* val, Direction direction) { template static T TestRead(Direction d, const std::string& a) { // gracefully reject any proper prefix of an encoding - for (int i = 0; i < a.size() - 1; ++i) { - Slice s(a.data(), i); - CHECK(!OCRead(&s, NULL, d)); - CHECK_EQ(s, a.substr(0, i)); + for (size_t i = 0; i < a.size() - 1; ++i) { + absl::string_view s(a.data(), i); + EXPECT_TRUE(!OCRead(&s, NULL, d)); + EXPECT_EQ(s, a.substr(0, i)); } - Slice s(a); + absl::string_view s(a); T v; - CHECK(OCRead(&s, &v, d)); - CHECK(s.empty()); + EXPECT_TRUE(OCRead(&s, &v, d)); + EXPECT_TRUE(s.empty()); return v; } @@ -166,12 +129,13 @@ static void TestWriteAppends(Direction d, T first, U second) { std::string encoded_first_only = encoded; OCWriteToString(&encoded, second, d); EXPECT_NE(encoded, encoded_first_only); - EXPECT_TRUE(Slice(encoded).starts_with(encoded_first_only)); + EXPECT_EQ(absl::string_view(encoded).substr(0, encoded_first_only.length()), + encoded_first_only); } template static void TestNumbers(T multiplier) { - for (int j = 0; j < 2; ++j) { + for (int j = 0; j < 1; ++j) { const Direction d = static_cast(j); // first test powers of 2 (and nearby numbers) @@ -180,19 +144,23 @@ static void TestNumbers(T multiplier) { TestWriteRead(d, multiplier * x); if (x != std::numeric_limits::max()) { TestWriteRead(d, multiplier * (x + 1)); - } else if (multiplier < 0 && multiplier == -1) { + } else if (multiplier < 0 && static_cast(multiplier) == -1) { TestWriteRead(d, -x - 1); } } - ACMRandom rnd(301); + SecureRandom rnd; // Generate 32bit pseudo-random integer. for (int bits = 1; bits <= std::numeric_limits().digits; ++bits) { // test random non-negative numbers with given number of significant bits const uint64_t mask = (~0ULL) >> (64 - bits); for (int i = 0; i < 1000; i++) { - T x = rnd.Next64() & mask; + T x = static_cast((static_cast(rnd()) << 32 | + static_cast(rnd())) & + mask); TestWriteRead(d, multiplier * x); - T y = rnd.Next64() & mask; + T y = static_cast((static_cast(rnd()) << 32 | + static_cast(rnd())) & + mask); TestWriteAppends(d, multiplier * x, multiplier * y); } } @@ -200,7 +168,8 @@ static void TestNumbers(T multiplier) { } // Return true iff 'a' is "before" 'b' according to 'direction' -static bool CompareStrings(const std::string& a, const std::string& b, +static bool CompareStrings(const std::string& a, + const std::string& b, Direction d) { return (INCREASING == d) ? (a < b) : (b < a); } @@ -216,12 +185,12 @@ static void TestNumberOrdering() { std::string str = OCWrite(num, d); std::string strplus1 = OCWrite(num + 1, d); - CHECK(CompareStrings(strminus1, str, d)); - CHECK(CompareStrings(str, strplus1, d)); + EXPECT_TRUE(CompareStrings(strminus1, str, d)); + EXPECT_TRUE(CompareStrings(str, strplus1, d)); // Compare 'str' with 'laststr'. When we approach 0, 'laststr' is // not necessarily before 'strminus1'. - CHECK(CompareStrings(laststr, str, d)); + EXPECT_TRUE(CompareStrings(laststr, str, d)); laststr = str; } @@ -234,46 +203,46 @@ static void TestNumberOrdering() { std::string str = OCWrite(num, d); std::string strplus1 = OCWrite(num + 1, d); - CHECK(CompareStrings(strminus1, str, d)); - CHECK(CompareStrings(str, strplus1, d)); + EXPECT_TRUE(CompareStrings(strminus1, str, d)); + EXPECT_TRUE(CompareStrings(str, strplus1, d)); // Compare 'str' with 'laststr'. - CHECK(CompareStrings(laststr, str, d)); + EXPECT_TRUE(CompareStrings(laststr, str, d)); laststr = str; } } // Helper routine for testing TEST_SkipToNextSpecialByte -static int FindSpecial(const std::string& x) { +static size_t FindSpecial(const std::string& x) { const char* p = x.data(); const char* limit = p + x.size(); const char* result = OrderedCode::TEST_SkipToNextSpecialByte(p, limit); - return result - p; + return static_cast(result - p); } TEST(OrderedCode, SkipToNextSpecialByte) { - for (int len = 0; len < 256; len++) { - ACMRandom rnd(301); + for (size_t len = 0; len < 256; len++) { + SecureRandom rnd; std::string x; while (x.size() < len) { - char c = 1 + rnd.Uniform(254); + char c = 1 + static_cast(rnd.Uniform(254)); ASSERT_NE(c, 0); ASSERT_NE(c, 255); x += c; // No 0 bytes, no 255 bytes } EXPECT_EQ(FindSpecial(x), x.size()); - for (int special_pos = 0; special_pos < len; special_pos++) { + for (size_t special_pos = 0; special_pos < len; special_pos++) { for (int special_test = 0; special_test < 2; special_test++) { - const char special_byte = (special_test == 0) ? 0 : 255; + const char special_byte = (special_test == 0) ? 0 : '\xff'; std::string y = x; y[special_pos] = special_byte; EXPECT_EQ(FindSpecial(y), special_pos); if (special_pos < 16) { // Add some special bytes after the one at special_pos to make sure // we still return the earliest special byte in the string - for (int rest = special_pos + 1; rest < len; rest++) { + for (size_t rest = special_pos + 1; rest < len; rest++) { if (rnd.OneIn(3)) { - y[rest] = rnd.OneIn(2) ? 0 : 255; + y[rest] = rnd.OneIn(2) ? 0 : '\xff'; EXPECT_EQ(FindSpecial(y), special_pos); } } @@ -297,9 +266,9 @@ TEST(OrderedCode, ExhaustiveFindSpecial) { for (int b0 = 0; b0 < 256; b0++) { for (int b1 = 0; b1 < 256; b1++) { for (int b2 = 0; b2 < 256; b2++) { - buf[start_offset + 0] = b0; - buf[start_offset + 1] = b1; - buf[start_offset + 2] = b2; + buf[start_offset + 0] = static_cast(b0); + buf[start_offset + 1] = static_cast(b1); + buf[start_offset + 2] = static_cast(b2); char* expected; if (b0 == 0 || b0 == 255) { expected = &buf[start_offset]; @@ -320,16 +289,22 @@ TEST(OrderedCode, ExhaustiveFindSpecial) { EXPECT_EQ(count, 256 * 256 * 256 * 2); } -TEST(Uint64, EncodeDecode) { TestNumbers(1); } +TEST(OrderedCodeUint64, EncodeDecode) { + TestNumbers(1); +} -TEST(Uint64, Ordering) { TestNumberOrdering(); } +TEST(OrderedCodeUint64, Ordering) { + TestNumberOrdering(); +} -TEST(Int64, EncodeDecode) { +TEST(OrderedCodeInt64, EncodeDecode) { TestNumbers(1); TestNumbers(-1); } -TEST(Int64, Ordering) { TestNumberOrdering(); } +TEST(OrderedCodeInt64, Ordering) { + TestNumberOrdering(); +} // Returns the bitwise complement of s. static inline std::string StrNot(const std::string& s) { @@ -340,7 +315,7 @@ static inline std::string StrNot(const std::string& s) { template static void TestInvalidEncoding(Direction d, const std::string& s) { - Slice p(s); + absl::string_view p(s); EXPECT_FALSE(OCRead(&p, static_cast(NULL), d)); EXPECT_EQ(s, p); } @@ -363,22 +338,22 @@ TEST(OrderedCodeInvalidEncodingsTest, NonCanonical) { // and thus should be avoided to not mess up the string ordering of // encodings. - ACMRandom rnd(301); + SecureRandom rnd; for (int n = 2; n <= 9; ++n) { // The zero in non_minimal[1] is "redundant". std::string non_minimal = std::string(1, n - 1) + std::string(1, 0) + RandomString(&rnd, n - 2); - EXPECT_EQ(n, non_minimal.length()); + EXPECT_EQ(static_cast(n), non_minimal.length()); EXPECT_NE(OCWrite(0, INCREASING), non_minimal); - if (DEBUG_MODE) { - Slice s(non_minimal); - EXPECT_DEATH_IF_SUPPORTED(OrderedCode::ReadNumIncreasing(&s, NULL), - "ssertion failed"); - } else { - TestRead(INCREASING, non_minimal); - } + +#if defined(NDEBUG) + TestRead(INCREASING, non_minimal); +#else // defined(NDEBUG) + absl::string_view s(non_minimal); + EXPECT_ANY_THROW(OrderedCode::ReadNumIncreasing(&s, NULL)); +#endif // defined(NDEBUG) } for (int n = 2; n <= 10; ++n) { @@ -387,31 +362,32 @@ TEST(OrderedCodeInvalidEncodingsTest, NonCanonical) { std::string(n / 8, 0xff) + std::string(1, 0xff << (8 - (n % 8))); // There are more than 7 zero bits between header bits and "payload". std::string non_minimal = - header + std::string(1, rnd.Uniform(256) & ~*header.rbegin()) + - RandomString(&rnd, n - header.length() - 1); - EXPECT_EQ(n, non_minimal.length()); + header + + std::string(1, + static_cast(rnd.Uniform(256)) & ~*header.rbegin()) + + RandomString(&rnd, n - static_cast(header.length()) - 1); + EXPECT_EQ(static_cast(n), non_minimal.length()); EXPECT_NE(OCWrite(0, INCREASING), non_minimal); - if (DEBUG_MODE) { - Slice s(non_minimal); - EXPECT_DEATH_IF_SUPPORTED(OrderedCode::ReadSignedNumIncreasing(&s, NULL), - "ssertion failed") - << n; - s = non_minimal; - } else { - TestRead(INCREASING, non_minimal); - } + +#if defined(NDEBUG) + TestRead(INCREASING, non_minimal); +#else // defined(NDEBUG) + absl::string_view s(non_minimal); + EXPECT_ANY_THROW(OrderedCode::ReadSignedNumIncreasing(&s, NULL)); + s = non_minimal; +#endif // defined(NDEBUG) } } // --------------------------------------------------------------------- // Strings -TEST(String, Infinity) { +TEST(OrderedCodeString, Infinity) { const std::string value("\xff\xff foo"); bool is_inf; std::string encoding, parsed; - Slice s; + absl::string_view s; // Check encoding/decoding of "infinity" for ascending order encoding.clear(); @@ -419,11 +395,11 @@ TEST(String, Infinity) { encoding.push_back('a'); s = encoding; EXPECT_TRUE(OrderedCode::ReadInfinity(&s)); - EXPECT_EQ(1, s.size()); + EXPECT_EQ(1u, s.size()); s = encoding; is_inf = false; EXPECT_TRUE(OrderedCode::ReadStringOrInfinity(&s, NULL, &is_inf)); - EXPECT_EQ(1, s.size()); + EXPECT_EQ(1u, s.size()); EXPECT_TRUE(is_inf); // Check ReadStringOrInfinity() can parse ordinary strings @@ -434,14 +410,14 @@ TEST(String, Infinity) { is_inf = false; parsed.clear(); EXPECT_TRUE(OrderedCode::ReadStringOrInfinity(&s, &parsed, &is_inf)); - EXPECT_EQ(1, s.size()); + EXPECT_EQ(1u, s.size()); EXPECT_FALSE(is_inf); EXPECT_EQ(value, parsed); } -TEST(String, EncodeDecode) { - ACMRandom rnd(301); - for (int i = 0; i < 2; ++i) { +TEST(OrderedCodeString, EncodeDecode) { + SecureRandom rnd; + for (int i = 0; i < 1; ++i) { const Direction d = static_cast(i); for (int len = 0; len < 256; len++) { @@ -457,48 +433,48 @@ TEST(String, EncodeDecode) { OCWriteToString(&out, b, d); std::string a2, b2, dummy; - Slice s = out; - Slice s2 = out; - CHECK(OCRead(&s, &a2, d)); - CHECK(OCRead(&s2, NULL, d)); - CHECK_EQ(s, s2); - - CHECK(OCRead(&s, &b2, d)); - CHECK(OCRead(&s2, NULL, d)); - CHECK_EQ(s, s2); - - CHECK(!OCRead(&s, &dummy, d)); - CHECK(!OCRead(&s2, NULL, d)); - CHECK_EQ(a, a2); - CHECK_EQ(b, b2); - CHECK(s.empty()); - CHECK(s2.empty()); + absl::string_view s = out; + absl::string_view s2 = out; + EXPECT_TRUE(OCRead(&s, &a2, d)); + EXPECT_TRUE(OCRead(&s2, NULL, d)); + EXPECT_EQ(s, s2); + + EXPECT_TRUE(OCRead(&s, &b2, d)); + EXPECT_TRUE(OCRead(&s2, NULL, d)); + EXPECT_EQ(s, s2); + + EXPECT_TRUE(!OCRead(&s, &dummy, d)); + EXPECT_TRUE(!OCRead(&s2, NULL, d)); + EXPECT_EQ(a, a2); + EXPECT_EQ(b, b2); + EXPECT_TRUE(s.empty()); + EXPECT_TRUE(s2.empty()); } } } } // 'str' is a static C-style string that may contain '\0' -#define STATIC_STR(str) Slice((str), sizeof(str) - 1) +#define STATIC_STR(str) absl::string_view((str), sizeof(str) - 1) -static std::string EncodeStringIncreasing(Slice value) { +static std::string EncodeStringIncreasing(absl::string_view value) { std::string encoded; OrderedCode::WriteString(&encoded, value); return encoded; } -TEST(String, Increasing) { +TEST(OrderedCodeString, Increasing) { // Here are a series of strings in non-decreasing order, including // consecutive strings such that the second one is equal to, a proper // prefix of, or has the same length as the first one. Most also contain // the special escaping characters '\x00' and '\xff'. - ASSERT_EQ(EncodeStringIncreasing(STATIC_STR("")), + EXPECT_EQ(EncodeStringIncreasing(STATIC_STR("")), EncodeStringIncreasing(STATIC_STR(""))); ASSERT_LT(EncodeStringIncreasing(STATIC_STR("")), EncodeStringIncreasing(STATIC_STR("\x00"))); - ASSERT_EQ(EncodeStringIncreasing(STATIC_STR("\x00")), + EXPECT_EQ(EncodeStringIncreasing(STATIC_STR("\x00")), EncodeStringIncreasing(STATIC_STR("\x00"))); ASSERT_LT(EncodeStringIncreasing(STATIC_STR("\x00")), @@ -507,7 +483,7 @@ TEST(String, Increasing) { ASSERT_LT(EncodeStringIncreasing(STATIC_STR("\x01")), EncodeStringIncreasing(STATIC_STR("a"))); - ASSERT_EQ(EncodeStringIncreasing(STATIC_STR("a")), + EXPECT_EQ(EncodeStringIncreasing(STATIC_STR("a")), EncodeStringIncreasing(STATIC_STR("a"))); ASSERT_LT(EncodeStringIncreasing(STATIC_STR("a")), @@ -526,3 +502,7 @@ TEST(String, Increasing) { OrderedCode::WriteInfinity(&infinity); ASSERT_LT(EncodeStringIncreasing(std::string(1 << 20, '\xff')), infinity); } + +} // namespace util +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/test/firebase/firestore/util/secure_random_test.cc b/Firestore/core/test/firebase/firestore/util/secure_random_test.cc index ee2ae0067c7..0b7a51b907a 100644 --- a/Firestore/core/test/firebase/firestore/util/secure_random_test.cc +++ b/Firestore/core/test/firebase/firestore/util/secure_random_test.cc @@ -30,3 +30,28 @@ TEST(SecureRandomTest, ResultsAreBounded) { EXPECT_LE(value, rng.max()); } } + +TEST(SecureRandomTest, Uniform) { + SecureRandom rng; + int count[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + for (int i = 0; i < 1000; i++) { + count[rng.Uniform(10)]++; + } + for (int i = 0; i < 10; i++) { + // Practically, each count should be close to 100. + EXPECT_LT(50, count[i]) << count[i]; + } +} + +TEST(SecureRandomTest, OneIn) { + SecureRandom rng; + int count = 0; + + for (int i = 0; i < 1000; i++) { + if (rng.OneIn(10)) count++; + } + // Practically, count should be close to 100. + EXPECT_LT(50, count) << count; + EXPECT_GT(150, count) << count; +} diff --git a/Firestore/core/test/firebase/firestore/util/string_printf_test.cc b/Firestore/core/test/firebase/firestore/util/string_printf_test.cc index 76f7cde5c33..085be84d7ad 100644 --- a/Firestore/core/test/firebase/firestore/util/string_printf_test.cc +++ b/Firestore/core/test/firebase/firestore/util/string_printf_test.cc @@ -64,7 +64,7 @@ TEST(StringPrintf, DontOverwriteErrno) { TEST(StringPrintf, LargeBuf) { // Check that the large buffer is handled correctly. - int n = 2048; + size_t n = 2048; char* buf = new char[n + 1]; memset(buf, ' ', n); buf[n] = 0; diff --git a/Firestore/Port/string_util_test.cc b/Firestore/core/test/firebase/firestore/util/string_util_test.cc similarity index 70% rename from Firestore/Port/string_util_test.cc rename to Firestore/core/test/firebase/firestore/util/string_util_test.cc index 331f96e27c5..f94596fad08 100644 --- a/Firestore/Port/string_util_test.cc +++ b/Firestore/core/test/firebase/firestore/util/string_util_test.cc @@ -14,16 +14,15 @@ * limitations under the License. */ -#include "Firestore/Port/string_util.h" +#include "Firestore/core/src/firebase/firestore/util/string_util.h" #include -#include -using Firestore::PrefixSuccessor; -using Firestore::ImmediateSuccessor; -using leveldb::Slice; +namespace firebase { +namespace firestore { +namespace util { -TEST(Util, PrefixSuccessor) { +TEST(StringUtil, PrefixSuccessor) { EXPECT_EQ(PrefixSuccessor("a"), "b"); EXPECT_EQ(PrefixSuccessor("aaAA"), "aaAB"); EXPECT_EQ(PrefixSuccessor("aaa\xff"), "aab"); @@ -33,7 +32,11 @@ TEST(Util, PrefixSuccessor) { EXPECT_EQ(PrefixSuccessor(""), ""); } -TEST(Util, ImmediateSuccessor) { - EXPECT_EQ(ImmediateSuccessor("hello"), Slice("hello\0", 6)); - EXPECT_EQ(ImmediateSuccessor(""), Slice("\0", 1)); +TEST(StringUtil, ImmediateSuccessor) { + EXPECT_EQ(ImmediateSuccessor("hello"), std::string("hello\0", 6)); + EXPECT_EQ(ImmediateSuccessor(""), std::string("\0", 1)); } + +} // namespace util +} // namespace firestore +} // namespace firebase diff --git a/Firestore/test.sh b/Firestore/test.sh index 7e26e3f5706..7be70d060a8 100755 --- a/Firestore/test.sh +++ b/Firestore/test.sh @@ -38,6 +38,19 @@ test_iOS() { | xcpretty } +test_CMake() { + echo "cpu core: $(sysctl -n hw.ncpu)" + echo "prepare cmake build" && \ + mkdir -p build && \ + cd build && \ + cmake .. || \ + exit 1 + + echo "cmake build and test" && \ + make -j $(sysctl -n hw.ncpu) all || \ + exit 2 +} + test_iOS; RESULT=$? if [[ $RESULT == 65 ]]; then echo "xcodebuild exited with 65, retrying" @@ -46,4 +59,8 @@ if [[ $RESULT == 65 ]]; then test_iOS; RESULT=$? fi -exit $RESULT +if [ $RESULT != 0 ]; then exit $RESULT; fi + +test_CMake; RESULT=$? + +if [ $RESULT != 0 ]; then exit $RESULT; fi diff --git a/Firestore/third_party/Immutable/FSTArraySortedDictionary.m b/Firestore/third_party/Immutable/FSTArraySortedDictionary.m index ad1d68af305..e9325a7c444 100644 --- a/Firestore/third_party/Immutable/FSTArraySortedDictionary.m +++ b/Firestore/third_party/Immutable/FSTArraySortedDictionary.m @@ -142,19 +142,6 @@ - (nullable id)objectForKey:(id)key { } } -- (nullable id)predecessorKey:(id)key { - NSInteger pos = [self findKey:key]; - if (pos == NSNotFound) { - [NSException raise:NSInternalInconsistencyException - format:@"Can't get predecessor key for non-existent key"]; - return nil; - } else if (pos == 0) { - return nil; - } else { - return self.keys[pos - 1]; - } -} - - (NSUInteger)indexOfKey:(id)key { return [self findKey:key]; } diff --git a/Firestore/third_party/Immutable/FSTImmutableSortedDictionary.h b/Firestore/third_party/Immutable/FSTImmutableSortedDictionary.h index a2264ec3412..cbb4da33b81 100644 --- a/Firestore/third_party/Immutable/FSTImmutableSortedDictionary.h +++ b/Firestore/third_party/Immutable/FSTImmutableSortedDictionary.h @@ -70,14 +70,6 @@ extern const int kSortedDictionaryArrayToRBTreeSizeThreshold; */ - (ValueType)objectForKeyedSubscript:(KeyType)key; -/** - * Gets the key before the given key in sorted order. - * - * @param key The key to look before. - * @return The key before the given one. - */ -- (nullable KeyType)predecessorKey:(KeyType)key; - /** * Returns the index of the key or NSNotFound if the key is not found. * diff --git a/Firestore/third_party/Immutable/FSTImmutableSortedDictionary.m b/Firestore/third_party/Immutable/FSTImmutableSortedDictionary.m index 6e7896104ca..87c21a59018 100644 --- a/Firestore/third_party/Immutable/FSTImmutableSortedDictionary.m +++ b/Firestore/third_party/Immutable/FSTImmutableSortedDictionary.m @@ -82,10 +82,6 @@ - (id)objectForKeyedSubscript:(id)key { return [self objectForKey:key]; } -- (nullable id)predecessorKey:(id)key { - @throw FSTAbstractMethodException(); // NOLINT -} - - (NSUInteger)indexOfKey:(id)key { @throw FSTAbstractMethodException(); // NOLINT } diff --git a/Firestore/third_party/Immutable/FSTImmutableSortedSet.h b/Firestore/third_party/Immutable/FSTImmutableSortedSet.h index d0f9906d54d..b432f98f57a 100644 --- a/Firestore/third_party/Immutable/FSTImmutableSortedSet.h +++ b/Firestore/third_party/Immutable/FSTImmutableSortedSet.h @@ -23,8 +23,6 @@ NS_ASSUME_NONNULL_BEGIN - (NSUInteger)count; - (BOOL)isEmpty; -- (KeyType)predecessorObject:(KeyType)entry; - /** * Returns the index of the object or NSNotFound if the object is not found. * diff --git a/Firestore/third_party/Immutable/FSTImmutableSortedSet.m b/Firestore/third_party/Immutable/FSTImmutableSortedSet.m index a23068e13a3..1b63c2c045c 100644 --- a/Firestore/third_party/Immutable/FSTImmutableSortedSet.m +++ b/Firestore/third_party/Immutable/FSTImmutableSortedSet.m @@ -76,10 +76,6 @@ - (id)lastObject { return [self.dictionary maxKey]; } -- (id)predecessorObject:(id)entry { - return [self.dictionary predecessorKey:entry]; -} - - (NSUInteger)indexOfObject:(id)object { return [self.dictionary indexOfKey:object]; } diff --git a/Firestore/third_party/Immutable/FSTTreeSortedDictionary.m b/Firestore/third_party/Immutable/FSTTreeSortedDictionary.m index b3e691f39fe..ec0c483f775 100644 --- a/Firestore/third_party/Immutable/FSTTreeSortedDictionary.m +++ b/Firestore/third_party/Immutable/FSTTreeSortedDictionary.m @@ -87,36 +87,6 @@ - (nullable id)objectForKey:(id)key { return nil; } -- (nullable id)predecessorKey:(id)key { - NSComparisonResult cmp; - id node = self.root; - id rightParent = nil; - while (![node isEmpty]) { - cmp = self.comparator(key, node.key); - if (cmp == NSOrderedSame) { - if (![node.left isEmpty]) { - node = node.left; - while (![node.right isEmpty]) { - node = node.right; - } - return node.key; - } else if (rightParent != nil) { - return rightParent.key; - } else { - return nil; - } - } else if (cmp == NSOrderedAscending) { - node = node.left; - } else if (cmp == NSOrderedDescending) { - rightParent = node; - node = node.right; - } - } - @throw [NSException exceptionWithName:@"NonexistentKey" - reason:@"getPredecessorKey called with nonexistent key." - userInfo:@{@"key" : [key description]}]; -} - - (NSUInteger)indexOfKey:(id)key { NSUInteger prunedNodes = 0; id node = self.root; diff --git a/Firestore/third_party/Immutable/Tests/FSTArraySortedDictionaryTests.m b/Firestore/third_party/Immutable/Tests/FSTArraySortedDictionaryTests.m index a7996862123..bf17496c90d 100644 --- a/Firestore/third_party/Immutable/Tests/FSTArraySortedDictionaryTests.m +++ b/Firestore/third_party/Immutable/Tests/FSTArraySortedDictionaryTests.m @@ -252,25 +252,6 @@ - (void)testBalanceProblem { XCTAssertEqual((int)next, (int)toInsert.count, @"Check we traversed all of the items"); } -- (void)testPredecessorKey { - FSTArraySortedDictionary *map = - [[FSTArraySortedDictionary alloc] initWithComparator:[self defaultComparator]]; - map = [map dictionaryBySettingObject:@1 forKey:@1]; - map = [map dictionaryBySettingObject:@50 forKey:@50]; - map = [map dictionaryBySettingObject:@3 forKey:@3]; - map = [map dictionaryBySettingObject:@4 forKey:@4]; - map = [map dictionaryBySettingObject:@7 forKey:@7]; - map = [map dictionaryBySettingObject:@9 forKey:@9]; - - XCTAssertNil([map predecessorKey:@1], @"First object doesn't have a predecessor"); - XCTAssertEqualObjects([map predecessorKey:@3], @1, @"@1"); - XCTAssertEqualObjects([map predecessorKey:@4], @3, @"@3"); - XCTAssertEqualObjects([map predecessorKey:@7], @4, @"@4"); - XCTAssertEqualObjects([map predecessorKey:@9], @7, @"@7"); - XCTAssertEqualObjects([map predecessorKey:@50], @9, @"@9"); - XCTAssertThrows([map predecessorKey:@777], @"Expect exception about nonexistent key"); -} - // This is a macro instead of a method so that the failures show on the proper lines. #define ASSERT_ENUMERATOR(enumerator, start, end, step) \ do { \ @@ -288,15 +269,12 @@ - (void)testPredecessorKey { - (void)testEnumerator { NSUInteger n = 100; NSMutableArray *toInsert = [NSMutableArray arrayWithCapacity:n]; - NSMutableArray *toRemove = [NSMutableArray arrayWithCapacity:n]; for (int i = 0; i < n; i++) { [toInsert addObject:@(i)]; - [toRemove addObject:@(i)]; } [self shuffleArray:toInsert]; - [self shuffleArray:toRemove]; FSTArraySortedDictionary *map = [[FSTArraySortedDictionary alloc] initWithComparator:self.defaultComparator]; diff --git a/Firestore/third_party/Immutable/Tests/FSTTreeSortedDictionaryTests.m b/Firestore/third_party/Immutable/Tests/FSTTreeSortedDictionaryTests.m index 344efba2abf..3e06b303724 100644 --- a/Firestore/third_party/Immutable/Tests/FSTTreeSortedDictionaryTests.m +++ b/Firestore/third_party/Immutable/Tests/FSTTreeSortedDictionaryTests.m @@ -437,25 +437,6 @@ - (void)testBalanceProblem { } } -- (void)testPredecessorKey { - FSTTreeSortedDictionary *map = - [[FSTTreeSortedDictionary alloc] initWithComparator:[self defaultComparator]]; - map = [map dictionaryBySettingObject:@1 forKey:@1]; - map = [map dictionaryBySettingObject:@50 forKey:@50]; - map = [map dictionaryBySettingObject:@3 forKey:@3]; - map = [map dictionaryBySettingObject:@4 forKey:@4]; - map = [map dictionaryBySettingObject:@7 forKey:@7]; - map = [map dictionaryBySettingObject:@9 forKey:@9]; - - XCTAssertNil([map predecessorKey:@1], @"First object doesn't have a predecessor"); - XCTAssertEqualObjects([map predecessorKey:@3], @1, @"@1"); - XCTAssertEqualObjects([map predecessorKey:@4], @3, @"@3"); - XCTAssertEqualObjects([map predecessorKey:@7], @4, @"@4"); - XCTAssertEqualObjects([map predecessorKey:@9], @7, @"@7"); - XCTAssertEqualObjects([map predecessorKey:@50], @9, @"@9"); - XCTAssertThrows([map predecessorKey:@777], @"Expect exception about nonexistent key"); -} - // This is a macro instead of a method so that the failures show on the proper lines. #define ASSERT_ENUMERATOR(enumerator, start, end, step) \ do { \ diff --git a/Firestore/third_party/abseil-cpp/CMakeLists.txt b/Firestore/third_party/abseil-cpp/CMakeLists.txt index b25a0065e86..4a23b70a10f 100644 --- a/Firestore/third_party/abseil-cpp/CMakeLists.txt +++ b/Firestore/third_party/abseil-cpp/CMakeLists.txt @@ -26,7 +26,17 @@ include(AbseilHelpers) # config options -set(ABSL_STD_CXX_FLAG "-std=c++11" CACHE STRING "c++ std flag (default: c++11)") +if (MSVC) + # /wd4005 macro-redefinition + # /wd4068 unknown pragma + # /wd4244 conversion from 'type1' to 'type2' + # /wd4267 conversion from 'size_t' to 'type2' + # /wd4800 force value to bool 'true' or 'false' (performance warning) + add_compile_options(/W3 /WX /wd4005 /wd4068 /wd4244 /wd4267 /wd4800) + add_definitions(/DNOMINMAX /DWIN32_LEAN_AND_MEAN=1 /D_CRT_SECURE_NO_WARNINGS /D_SCL_SECURE_NO_WARNINGS) +else() + set(ABSL_STD_CXX_FLAG "-std=c++11" CACHE STRING "c++ std flag (default: c++11)") +endif() @@ -48,29 +58,33 @@ list(APPEND ABSL_COMMON_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}) set(CMAKE_CXX_FLAGS "${ABSL_STD_CXX_FLAG} ${CMAKE_CXX_FLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_WARNING_VLA} ${CMAKE_CXX_FLAGS} ") +# -fexceptions +set(ABSL_EXCEPTIONS_FLAG "${CMAKE_CXX_EXCEPTIONS}") # find dependencies ## pthread find_package(Threads REQUIRED) +if(NOT ABSL_CCTZ_TARGET) + set(ABSL_CCTZ_TARGET cctz) +endif() + # commented: used only for standalone test #add_subdirectory(cctz) #add_subdirectory(googletest) ## check targets -check_target(GTest::GTest) -check_target(GTest::Main) - -# -fexceptions -set(ABSL_EXCEPTIONS_FLAG "${CMAKE_CXX_EXCEPTIONS}") - -# fix stuff -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FIX_MSVC} ${CMAKE_CXX_FLAGS}") - -list(APPEND ABSL_TEST_COMMON_LIBRARIES - GTest::Main - GTest::GTest - ${CMAKE_THREAD_LIBS_INIT} -) +if(BUILD_TESTING) + check_target(gtest) + check_target(gtest_main) + check_target(gmock) + + list(APPEND ABSL_TEST_COMMON_LIBRARIES + gtest_main + gtest + gmock + ${CMAKE_THREAD_LIBS_INIT} + ) +endif() add_subdirectory(absl) diff --git a/Firestore/third_party/abseil-cpp/absl/CMakeLists.txt b/Firestore/third_party/abseil-cpp/absl/CMakeLists.txt index fb158fa7d1e..e7b5139091c 100644 --- a/Firestore/third_party/abseil-cpp/absl/CMakeLists.txt +++ b/Firestore/third_party/abseil-cpp/absl/CMakeLists.txt @@ -17,5 +17,7 @@ add_subdirectory(base) +add_subdirectory(memory) add_subdirectory(meta) +add_subdirectory(numeric) add_subdirectory(strings) diff --git a/Firestore/third_party/abseil-cpp/absl/base/CMakeLists.txt b/Firestore/third_party/abseil-cpp/absl/base/CMakeLists.txt index 08659c49352..cfa119af3b8 100644 --- a/Firestore/third_party/abseil-cpp/absl/base/CMakeLists.txt +++ b/Firestore/third_party/abseil-cpp/absl/base/CMakeLists.txt @@ -18,6 +18,7 @@ list(APPEND BASE_PUBLIC_HEADERS "attributes.h" "config.h" "dynamic_annotations.h" + "log_severity.h" "macros.h" "optimization.h" "policy_checks.h" @@ -28,7 +29,6 @@ list(APPEND BASE_PUBLIC_HEADERS list(APPEND BASE_INTERNAL_HEADERS "internal/atomic_hook.h" "internal/endian.h" - "internal/log_severity.h" "internal/raw_logging.h" "internal/throw_delegate.h" "internal/unaligned_access.h" diff --git a/Firestore/third_party/abseil-cpp/absl/base/attributes.h b/Firestore/third_party/abseil-cpp/absl/base/attributes.h index 6f3cfe4cbda..4e1fc8b550d 100644 --- a/Firestore/third_party/abseil-cpp/absl/base/attributes.h +++ b/Firestore/third_party/abseil-cpp/absl/base/attributes.h @@ -281,6 +281,18 @@ #define ABSL_ATTRIBUTE_NO_SANITIZE_CFI #endif +// ABSL_ATTRIBUTE_RETURNS_NONNULL +// +// Tells the compiler that a particular function never returns a null pointer. +#if ABSL_HAVE_ATTRIBUTE(returns_nonnull) || \ + (defined(__GNUC__) && \ + (__GNUC__ > 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)) && \ + !defined(__clang__)) +#define ABSL_ATTRIBUTE_RETURNS_NONNULL __attribute__((returns_nonnull)) +#else +#define ABSL_ATTRIBUTE_RETURNS_NONNULL +#endif + // ABSL_HAVE_ATTRIBUTE_SECTION // // Indicates whether labeled sections are supported. Labeled sections are not @@ -305,6 +317,7 @@ __attribute__((section(#name))) __attribute__((noinline)) #endif + // ABSL_ATTRIBUTE_SECTION_VARIABLE // // Tells the compiler/linker to put a given variable into a section and define @@ -344,6 +357,7 @@ (reinterpret_cast(__start_##name)) #define ABSL_ATTRIBUTE_SECTION_STOP(name) \ (reinterpret_cast(__stop_##name)) + #else // !ABSL_HAVE_ATTRIBUTE_SECTION #define ABSL_HAVE_ATTRIBUTE_SECTION 0 @@ -356,6 +370,7 @@ #define ABSL_DECLARE_ATTRIBUTE_SECTION_VARS(name) #define ABSL_ATTRIBUTE_SECTION_START(name) (reinterpret_cast(0)) #define ABSL_ATTRIBUTE_SECTION_STOP(name) (reinterpret_cast(0)) + #endif // ABSL_ATTRIBUTE_SECTION // ABSL_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC diff --git a/Firestore/third_party/abseil-cpp/absl/base/config.h b/Firestore/third_party/abseil-cpp/absl/base/config.h index 8a44c06807f..6703d0eac71 100644 --- a/Firestore/third_party/abseil-cpp/absl/base/config.h +++ b/Firestore/third_party/abseil-cpp/absl/base/config.h @@ -138,9 +138,10 @@ // supported. #ifdef ABSL_HAVE_THREAD_LOCAL #error ABSL_HAVE_THREAD_LOCAL cannot be directly set -#elif !defined(__apple_build_version__) || \ - ((__apple_build_version__ >= 8000042) && \ - !(TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0)) +#elif (!defined(__apple_build_version__) || \ + (__apple_build_version__ >= 8000042)) && \ + !(defined(__APPLE__) && TARGET_OS_IPHONE && \ + __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0) // Notes: Xcode's clang did not support `thread_local` until version // 8, and even then not for all iOS < 9.0. #define ABSL_HAVE_THREAD_LOCAL 1 @@ -181,21 +182,21 @@ // __SIZEOF_INT128__ but not all versions actually support __int128. #ifdef ABSL_HAVE_INTRINSIC_INT128 #error ABSL_HAVE_INTRINSIC_INT128 cannot be directly set -#elif (defined(__clang__) && defined(__SIZEOF_INT128__) && \ - !defined(__aarch64__)) || \ - (defined(__CUDACC__) && defined(__SIZEOF_INT128__) && \ - __CUDACC_VER_MAJOR__ >= 9) || \ - (!defined(__clang__) && !defined(__CUDACC__) && defined(__GNUC__) && \ - defined(__SIZEOF_INT128__)) +#elif defined(__SIZEOF_INT128__) +#if (defined(__clang__) && !defined(__aarch64__)) || \ + (defined(__CUDACC__) && __CUDACC_VER_MAJOR__ >= 9) || \ + (!defined(__clang__) && !defined(__CUDACC__) && defined(__GNUC__)) #define ABSL_HAVE_INTRINSIC_INT128 1 +#elif defined(__CUDACC__) // __CUDACC_VER__ is a full version number before CUDA 9, and is defined to a -// std::string explaining that it has been removed starting with CUDA 9. We can't -// compare both variants in a single boolean expression because there is no -// short-circuiting in the preprocessor. -#elif defined(__CUDACC__) && defined(__SIZEOF_INT128__) && \ - __CUDACC_VER__ >= 7000 +// std::string explaining that it has been removed starting with CUDA 9. We use +// nested #ifs because there is no short-circuiting in the preprocessor. +// NOTE: `__CUDACC__` could be undefined while `__CUDACC_VER__` is defined. +#if __CUDACC_VER__ >= 70000 #define ABSL_HAVE_INTRINSIC_INT128 1 -#endif +#endif // __CUDACC_VER__ >= 70000 +#endif // defined(__CUDACC__) +#endif // ABSL_HAVE_INTRINSIC_INT128 // ABSL_HAVE_EXCEPTIONS // @@ -243,7 +244,7 @@ // Windows _WIN32 // NaCL __native_client__ // AsmJS __asmjs__ -// Fuschia __Fuchsia__ +// Fuchsia __Fuchsia__ // // Note that since Android defines both __ANDROID__ and __linux__, one // may probe for either Linux or Android by simply testing for __linux__. @@ -254,8 +255,9 @@ // POSIX.1-2001. #ifdef ABSL_HAVE_MMAP #error ABSL_HAVE_MMAP cannot be directly set -#elif defined(__linux__) || defined(__APPLE__) || defined(__ros__) || \ - defined(__native_client__) || defined(__asmjs__) || defined(__Fuchsia__) +#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ + defined(__ros__) || defined(__native_client__) || defined(__asmjs__) || \ + defined(__Fuchsia__) #define ABSL_HAVE_MMAP 1 #endif @@ -265,7 +267,8 @@ // functions as defined in POSIX.1-2001. #ifdef ABSL_HAVE_PTHREAD_GETSCHEDPARAM #error ABSL_HAVE_PTHREAD_GETSCHEDPARAM cannot be directly set -#elif defined(__linux__) || defined(__APPLE__) || defined(__ros__) +#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ + defined(__ros__) #define ABSL_HAVE_PTHREAD_GETSCHEDPARAM 1 #endif diff --git a/Firestore/third_party/abseil-cpp/absl/base/internal/endian.h b/Firestore/third_party/abseil-cpp/absl/base/internal/endian.h index 602129eed20..edc10f10a5a 100644 --- a/Firestore/third_party/abseil-cpp/absl/base/internal/endian.h +++ b/Firestore/third_party/abseil-cpp/absl/base/internal/endian.h @@ -22,6 +22,8 @@ #elif defined(__APPLE__) // Mac OS X / Darwin features #include +#elif defined(__FreeBSD__) +#include #elif defined(__GLIBC__) #include // IWYU pragma: export #endif diff --git a/Firestore/third_party/abseil-cpp/absl/base/internal/raw_logging.cc b/Firestore/third_party/abseil-cpp/absl/base/internal/raw_logging.cc index 1b849abfcea..86e34d458f4 100644 --- a/Firestore/third_party/abseil-cpp/absl/base/internal/raw_logging.cc +++ b/Firestore/third_party/abseil-cpp/absl/base/internal/raw_logging.cc @@ -22,7 +22,7 @@ #include "absl/base/config.h" #include "absl/base/internal/atomic_hook.h" -#include "absl/base/internal/log_severity.h" +#include "absl/base/log_severity.h" // We know how to perform low-level writes to stderr in POSIX and Windows. For // these platforms, we define the token ABSL_LOW_LEVEL_WRITE_SUPPORTED. @@ -34,7 +34,8 @@ // // This preprocessor token is also defined in raw_io.cc. If you need to copy // this, consider moving both to config.h instead. -#if defined(__linux__) || defined(__APPLE__) || defined(__Fuchsia__) +#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ + defined(__Fuchsia__) #include @@ -47,7 +48,7 @@ // ABSL_HAVE_SYSCALL_WRITE is defined when the platform provides the syscall // syscall(SYS_write, /*int*/ fd, /*char* */ buf, /*size_t*/ len); // for low level operations that want to avoid libc. -#if defined(__linux__) && !defined(__ANDROID__) +#if (defined(__linux__) || defined(__FreeBSD__)) && !defined(__ANDROID__) #include #define ABSL_HAVE_SYSCALL_WRITE 1 #define ABSL_LOW_LEVEL_WRITE_SUPPORTED 1 diff --git a/Firestore/third_party/abseil-cpp/absl/base/internal/raw_logging.h b/Firestore/third_party/abseil-cpp/absl/base/internal/raw_logging.h index 568d2afce10..1b2a44b7a7d 100644 --- a/Firestore/third_party/abseil-cpp/absl/base/internal/raw_logging.h +++ b/Firestore/third_party/abseil-cpp/absl/base/internal/raw_logging.h @@ -20,7 +20,7 @@ #define ABSL_BASE_INTERNAL_RAW_LOGGING_H_ #include "absl/base/attributes.h" -#include "absl/base/internal/log_severity.h" +#include "absl/base/log_severity.h" #include "absl/base/macros.h" #include "absl/base/port.h" diff --git a/Firestore/third_party/abseil-cpp/absl/base/internal/unaligned_access.h b/Firestore/third_party/abseil-cpp/absl/base/internal/unaligned_access.h index ea30829b0e1..c5724362a4e 100644 --- a/Firestore/third_party/abseil-cpp/absl/base/internal/unaligned_access.h +++ b/Firestore/third_party/abseil-cpp/absl/base/internal/unaligned_access.h @@ -142,7 +142,7 @@ inline void UnalignedStore64(void *p, uint64_t v) { // and 32-bit values (not 64-bit); older versions either raise a fatal signal, // do an unaligned read and rotate the words around a bit, or do the reads very // slowly (trip through kernel mode). There's no simple #define that says just -// “ARMv7 or higher”, so we have to filter away all ARMv5 and ARMv6 +// "ARMv7 or higher", so we have to filter away all ARMv5 and ARMv6 // sub-architectures. Newer gcc (>= 4.6) set an __ARM_FEATURE_ALIGNED #define, // so in time, maybe we can move on to that. // diff --git a/Firestore/third_party/abseil-cpp/absl/base/internal/log_severity.h b/Firestore/third_party/abseil-cpp/absl/base/log_severity.h similarity index 80% rename from Firestore/third_party/abseil-cpp/absl/base/internal/log_severity.h rename to Firestore/third_party/abseil-cpp/absl/base/log_severity.h index deaf6a57892..e146bcb05a4 100644 --- a/Firestore/third_party/abseil-cpp/absl/base/internal/log_severity.h +++ b/Firestore/third_party/abseil-cpp/absl/base/log_severity.h @@ -16,6 +16,8 @@ #ifndef ABSL_BASE_INTERNAL_LOG_SEVERITY_H_ #define ABSL_BASE_INTERNAL_LOG_SEVERITY_H_ +#include + #include "absl/base/attributes.h" namespace absl { @@ -27,6 +29,13 @@ enum class LogSeverity : int { kFatal = 3, }; +// Returns an iterable of all standard `absl::LogSeverity` values, ordered from +// least to most severe. +constexpr std::array LogSeverities() { + return {{absl::LogSeverity::kInfo, absl::LogSeverity::kWarning, + absl::LogSeverity::kError, absl::LogSeverity::kFatal}}; +} + constexpr const char* LogSeverityName(absl::LogSeverity s) { return s == absl::LogSeverity::kInfo ? "INFO" @@ -37,7 +46,7 @@ constexpr const char* LogSeverityName(absl::LogSeverity s) { : s == absl::LogSeverity::kFatal ? "FATAL" : "UNKNOWN"; } -// Note that out-of-range large severities normalize to kError, not kFatal. +// Note that out-of-range severities normalize to kInfo or kError, never kFatal. constexpr absl::LogSeverity NormalizeLogSeverity(absl::LogSeverity s) { return s < absl::LogSeverity::kInfo ? absl::LogSeverity::kInfo diff --git a/Firestore/third_party/abseil-cpp/absl/base/macros.h b/Firestore/third_party/abseil-cpp/absl/base/macros.h index d4140872773..c81f8c6d7ea 100644 --- a/Firestore/third_party/abseil-cpp/absl/base/macros.h +++ b/Firestore/third_party/abseil-cpp/absl/base/macros.h @@ -146,7 +146,7 @@ enum LinkerInitialized { // Every usage of a deprecated entity will trigger a warning when compiled with // clang's `-Wdeprecated-declarations` option. This option is turned off by // default, but the warnings will be reported by clang-tidy. -#if defined(__clang__) && __cplusplus >= 201103L && defined(__has_warning) +#if defined(__clang__) && __cplusplus >= 201103L #define ABSL_DEPRECATED(message) __attribute__((deprecated(message))) #endif @@ -196,7 +196,7 @@ enum LinkerInitialized { #define ABSL_ASSERT(expr) (false ? (void)(expr) : (void)0) #else #define ABSL_ASSERT(expr) \ - (ABSL_PREDICT_TRUE((expr)) ? (void)0 : [] { assert(false && #expr); }()) + (void) (ABSL_PREDICT_TRUE((expr)) ? (void)0 : [] { assert(false && #expr); }()) #endif #endif // ABSL_BASE_MACROS_H_ diff --git a/Firestore/third_party/abseil-cpp/absl/memory/CMakeLists.txt b/Firestore/third_party/abseil-cpp/absl/memory/CMakeLists.txt new file mode 100644 index 00000000000..21bfc87ed6e --- /dev/null +++ b/Firestore/third_party/abseil-cpp/absl/memory/CMakeLists.txt @@ -0,0 +1,52 @@ +# +# Copyright 2017 The Abseil Authors. +# +# 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. +# + +list(APPEND MEMORY_PUBLIC_HEADERS + "memory.h" +) + + +absl_header_library( + TARGET + absl_memory + EXPORT_NAME + memory +) + +# +## TESTS +# + +# test memory_test +list(APPEND MEMORY_TEST_SRC + "memory_test.cc" + ${MEMORY_PUBLIC_HEADERS} +) +set(MEMORY_TEST_PUBLIC_LIBRARIES absl::base absl::memory) + + + +absl_test( + TARGET + memory_test + SOURCES + ${MEMORY_TEST_SRC} + PUBLIC_LIBRARIES + ${MEMORY_TEST_PUBLIC_LIBRARIES} +) + + + diff --git a/Firestore/third_party/abseil-cpp/absl/memory/memory.h b/Firestore/third_party/abseil-cpp/absl/memory/memory.h new file mode 100644 index 00000000000..2220ee4e412 --- /dev/null +++ b/Firestore/third_party/abseil-cpp/absl/memory/memory.h @@ -0,0 +1,640 @@ +// Copyright 2017 The Abseil Authors. +// +// 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. +// +// ----------------------------------------------------------------------------- +// File: memory.h +// ----------------------------------------------------------------------------- +// +// This header file contains utility functions for managing the creation and +// conversion of smart pointers. This file is an extension to the C++ +// standard library header file. + +#ifndef ABSL_MEMORY_MEMORY_H_ +#define ABSL_MEMORY_MEMORY_H_ + +#include +#include +#include +#include +#include +#include + +#include "absl/base/macros.h" +#include "absl/meta/type_traits.h" + +namespace absl { + +// ----------------------------------------------------------------------------- +// Function Template: WrapUnique() +// ----------------------------------------------------------------------------- +// +// Adopts ownership from a raw pointer and transfers it to the returned +// `std::unique_ptr`, whose type is deduced. +// +// Example: +// X* NewX(int, int); +// auto x = WrapUnique(NewX(1, 2)); // 'x' is std::unique_ptr. +// +// `absl::WrapUnique` is useful for capturing the output of a raw pointer +// factory. However, prefer 'absl::make_unique(args...) over +// 'absl::WrapUnique(new T(args...))'. +// +// auto x = WrapUnique(new X(1, 2)); // works, but nonideal. +// auto x = make_unique(1, 2); // safer, standard, avoids raw 'new'. +// +// Note that `absl::WrapUnique(p)` is valid only if `delete p` is a valid +// expression. In particular, `absl::WrapUnique()` cannot wrap pointers to +// arrays, functions or void, and it must not be used to capture pointers +// obtained from array-new expressions (even though that would compile!). +template +std::unique_ptr WrapUnique(T* ptr) { + static_assert(!std::is_array::value, "array types are unsupported"); + static_assert(std::is_object::value, "non-object types are unsupported"); + return std::unique_ptr(ptr); +} + +namespace memory_internal { + +// Traits to select proper overload and return type for `absl::make_unique<>`. +template +struct MakeUniqueResult { + using scalar = std::unique_ptr; +}; +template +struct MakeUniqueResult { + using array = std::unique_ptr; +}; +template +struct MakeUniqueResult { + using invalid = void; +}; + +} // namespace memory_internal + +#if __cplusplus >= 201402L || defined(_MSC_VER) +using std::make_unique; +#else +// ----------------------------------------------------------------------------- +// Function Template: make_unique() +// ----------------------------------------------------------------------------- +// +// Creates a `std::unique_ptr<>`, while avoiding issues creating temporaries +// during the construction process. `absl::make_unique<>` also avoids redundant +// type declarations, by avoiding the need to explicitly use the `new` operator. +// +// This implementation of `absl::make_unique<>` is designed for C++11 code and +// will be replaced in C++14 by the equivalent `std::make_unique<>` abstraction. +// `absl::make_unique<>` is designed to be 100% compatible with +// `std::make_unique<>` so that the eventual migration will involve a simple +// rename operation. +// +// For more background on why `std::unique_ptr(new T(a,b))` is problematic, +// see Herb Sutter's explanation on +// (Exception-Safe Function Calls)[http://herbsutter.com/gotw/_102/]. +// (In general, reviewers should treat `new T(a,b)` with scrutiny.) +// +// Example usage: +// +// auto p = make_unique(args...); // 'p' is a std::unique_ptr +// auto pa = make_unique(5); // 'pa' is a std::unique_ptr +// +// Three overloads of `absl::make_unique` are required: +// +// - For non-array T: +// +// Allocates a T with `new T(std::forward args...)`, +// forwarding all `args` to T's constructor. +// Returns a `std::unique_ptr` owning that object. +// +// - For an array of unknown bounds T[]: +// +// `absl::make_unique<>` will allocate an array T of type U[] with +// `new U[n]()` and return a `std::unique_ptr` owning that array. +// +// Note that 'U[n]()' is different from 'U[n]', and elements will be +// value-initialized. Note as well that `std::unique_ptr` will perform its +// own destruction of the array elements upon leaving scope, even though +// the array [] does not have a default destructor. +// +// NOTE: an array of unknown bounds T[] may still be (and often will be) +// initialized to have a size, and will still use this overload. E.g: +// +// auto my_array = absl::make_unique(10); +// +// - For an array of known bounds T[N]: +// +// `absl::make_unique<>` is deleted (like with `std::make_unique<>`) as +// this overload is not useful. +// +// NOTE: an array of known bounds T[N] is not considered a useful +// construction, and may cause undefined behavior in templates. E.g: +// +// auto my_array = absl::make_unique(); +// +// In those cases, of course, you can still use the overload above and +// simply initialize it to its desired size: +// +// auto my_array = absl::make_unique(10); + +// `absl::make_unique` overload for non-array types. +template +typename memory_internal::MakeUniqueResult::scalar make_unique( + Args&&... args) { + return std::unique_ptr(new T(std::forward(args)...)); +} + +// `absl::make_unique` overload for an array T[] of unknown bounds. +// The array allocation needs to use the `new T[size]` form and cannot take +// element constructor arguments. The `std::unique_ptr` will manage destructing +// these array elements. +template +typename memory_internal::MakeUniqueResult::array make_unique(size_t n) { + return std::unique_ptr(new typename absl::remove_extent_t[n]()); +} + +// `absl::make_unique` overload for an array T[N] of known bounds. +// This construction will be rejected. +template +typename memory_internal::MakeUniqueResult::invalid make_unique( + Args&&... /* args */) = delete; +#endif + +// ----------------------------------------------------------------------------- +// Function Template: RawPtr() +// ----------------------------------------------------------------------------- +// +// Extracts the raw pointer from a pointer-like value `ptr`. `absl::RawPtr` is +// useful within templates that need to handle a complement of raw pointers, +// `std::nullptr_t`, and smart pointers. +template +auto RawPtr(T&& ptr) -> decltype(&*ptr) { + // ptr is a forwarding reference to support Ts with non-const operators. + return (ptr != nullptr) ? &*ptr : nullptr; +} +inline std::nullptr_t RawPtr(std::nullptr_t) { return nullptr; } + +// ----------------------------------------------------------------------------- +// Function Template: ShareUniquePtr() +// ----------------------------------------------------------------------------- +// +// Adopts a `std::unique_ptr` rvalue and returns a `std::shared_ptr` of deduced +// type. Ownership (if any) of the held value is transferred to the returned +// shared pointer. +// +// Example: +// +// auto up = absl::make_unique(10); +// auto sp = absl::ShareUniquePtr(std::move(up)); // shared_ptr +// CHECK_EQ(*sp, 10); +// CHECK(up == nullptr); +// +// Note that this conversion is correct even when T is an array type, and more +// generally it works for *any* deleter of the `unique_ptr` (single-object +// deleter, array deleter, or any custom deleter), since the deleter is adopted +// by the shared pointer as well. The deleter is copied (unless it is a +// reference). +// +// Implements the resolution of [LWG 2415](http://wg21.link/lwg2415), by which a +// null shared pointer does not attempt to call the deleter. +template +std::shared_ptr ShareUniquePtr(std::unique_ptr&& ptr) { + return ptr ? std::shared_ptr(std::move(ptr)) : std::shared_ptr(); +} + +// ----------------------------------------------------------------------------- +// Function Template: WeakenPtr() +// ----------------------------------------------------------------------------- +// +// Creates a weak pointer associated with a given shared pointer. The returned +// value is a `std::weak_ptr` of deduced type. +// +// Example: +// +// auto sp = std::make_shared(10); +// auto wp = absl::WeakenPtr(sp); +// CHECK_EQ(sp.get(), wp.lock().get()); +// sp.reset(); +// CHECK(wp.lock() == nullptr); +// +template +std::weak_ptr WeakenPtr(const std::shared_ptr& ptr) { + return std::weak_ptr(ptr); +} + +namespace memory_internal { + +// ExtractOr::type evaluates to E if possible. Otherwise, D. +template