aboutsummaryrefslogtreecommitdiffhomepage
path: root/Firestore
diff options
context:
space:
mode:
Diffstat (limited to 'Firestore')
-rw-r--r--Firestore/CHANGELOG.md28
-rw-r--r--Firestore/CMakeLists.txt54
-rw-r--r--Firestore/Example/Firestore.xcodeproj/project.pbxproj707
-rw-r--r--Firestore/Example/Podfile2
-rw-r--r--Firestore/Example/SwiftBuildTest/main.swift101
-rw-r--r--Firestore/Example/Tests/API/FIRCollectionReferenceTests.mm43
-rw-r--r--Firestore/Example/Tests/API/FIRDocumentReferenceTests.mm43
-rw-r--r--Firestore/Example/Tests/API/FIRDocumentSnapshotTests.mm59
-rw-r--r--Firestore/Example/Tests/API/FIRFieldPathTests.mm46
-rw-r--r--Firestore/Example/Tests/API/FIRFieldValueTests.mm46
-rw-r--r--Firestore/Example/Tests/API/FIRGeoPointTests.mm (renamed from Firestore/Example/Tests/API/FIRGeoPointTests.m)23
-rw-r--r--Firestore/Example/Tests/API/FIRQuerySnapshotTests.mm58
-rw-r--r--Firestore/Example/Tests/API/FIRQueryTests.mm85
-rw-r--r--Firestore/Example/Tests/API/FIRSnapshotMetadataTests.mm52
-rw-r--r--Firestore/Example/Tests/API/FSTAPIHelpers.h74
-rw-r--r--Firestore/Example/Tests/API/FSTAPIHelpers.mm115
-rw-r--r--Firestore/Example/Tests/Core/FSTDatabaseInfoTests.mm (renamed from Firestore/Example/Tests/Core/FSTDatabaseInfoTests.m)0
-rw-r--r--Firestore/Example/Tests/Core/FSTEventManagerTests.mm (renamed from Firestore/Example/Tests/Core/FSTEventManagerTests.m)16
-rw-r--r--Firestore/Example/Tests/Core/FSTQueryListenerTests.mm (renamed from Firestore/Example/Tests/Core/FSTQueryListenerTests.m)52
-rw-r--r--Firestore/Example/Tests/Core/FSTQueryTests.mm (renamed from Firestore/Example/Tests/Core/FSTQueryTests.m)105
-rw-r--r--Firestore/Example/Tests/Core/FSTTargetIDGeneratorTests.m94
-rw-r--r--Firestore/Example/Tests/Core/FSTTimestampTests.mm (renamed from Firestore/Example/Tests/Core/FSTTimestampTests.m)0
-rw-r--r--Firestore/Example/Tests/Core/FSTViewSnapshotTest.mm (renamed from Firestore/Example/Tests/Core/FSTViewSnapshotTest.m)2
-rw-r--r--Firestore/Example/Tests/Core/FSTViewTests.mm (renamed from Firestore/Example/Tests/Core/FSTViewTests.m)0
-rw-r--r--Firestore/Example/Tests/GoogleTest/FSTGoogleTestTests.mm3
-rw-r--r--Firestore/Example/Tests/Integration/API/FIRCursorTests.mm (renamed from Firestore/Example/Tests/Integration/API/FIRCursorTests.m)2
-rw-r--r--Firestore/Example/Tests/Integration/API/FIRDatabaseTests.mm (renamed from Firestore/Example/Tests/Integration/API/FIRDatabaseTests.m)39
-rw-r--r--Firestore/Example/Tests/Integration/API/FIRFieldsTests.mm (renamed from Firestore/Example/Tests/Integration/API/FIRFieldsTests.m)2
-rw-r--r--Firestore/Example/Tests/Integration/API/FIRListenerRegistrationTests.mm (renamed from Firestore/Example/Tests/Integration/API/FIRListenerRegistrationTests.m)33
-rw-r--r--Firestore/Example/Tests/Integration/API/FIRQueryTests.mm (renamed from Firestore/Example/Tests/Integration/API/FIRQueryTests.m)110
-rw-r--r--Firestore/Example/Tests/Integration/API/FIRServerTimestampTests.m183
-rw-r--r--Firestore/Example/Tests/Integration/API/FIRServerTimestampTests.mm318
-rw-r--r--Firestore/Example/Tests/Integration/API/FIRTypeTests.mm (renamed from Firestore/Example/Tests/Integration/API/FIRTypeTests.m)5
-rw-r--r--Firestore/Example/Tests/Integration/API/FIRValidationTests.mm (renamed from Firestore/Example/Tests/Integration/API/FIRValidationTests.m)4
-rw-r--r--Firestore/Example/Tests/Integration/API/FIRWriteBatchTests.mm (renamed from Firestore/Example/Tests/Integration/API/FIRWriteBatchTests.m)33
-rw-r--r--Firestore/Example/Tests/Integration/FSTDatastoreTests.mm (renamed from Firestore/Example/Tests/Integration/FSTDatastoreTests.m)2
-rw-r--r--Firestore/Example/Tests/Integration/FSTSmokeTests.mm (renamed from Firestore/Example/Tests/Integration/FSTSmokeTests.m)8
-rw-r--r--Firestore/Example/Tests/Integration/FSTStreamTests.mm (renamed from Firestore/Example/Tests/Integration/FSTStreamTests.m)0
-rw-r--r--Firestore/Example/Tests/Integration/FSTTransactionTests.mm (renamed from Firestore/Example/Tests/Integration/FSTTransactionTests.m)2
-rw-r--r--Firestore/Example/Tests/Local/FSTEagerGarbageCollectorTests.mm (renamed from Firestore/Example/Tests/Local/FSTEagerGarbageCollectorTests.m)12
-rw-r--r--Firestore/Example/Tests/Local/FSTLevelDBLocalStoreTests.mm (renamed from Firestore/Example/Tests/Local/FSTLevelDBLocalStoreTests.m)0
-rw-r--r--Firestore/Example/Tests/Local/FSTLevelDBMigrationsTests.mm75
-rw-r--r--Firestore/Example/Tests/Local/FSTLevelDBMutationQueueTests.mm5
-rw-r--r--Firestore/Example/Tests/Local/FSTLevelDBQueryCacheTests.mm (renamed from Firestore/Example/Tests/Local/FSTLevelDBQueryCacheTests.m)0
-rw-r--r--Firestore/Example/Tests/Local/FSTLevelDBRemoteDocumentCacheTests.mm5
-rw-r--r--Firestore/Example/Tests/Local/FSTLocalSerializerTests.mm (renamed from Firestore/Example/Tests/Local/FSTLocalSerializerTests.m)4
-rw-r--r--Firestore/Example/Tests/Local/FSTLocalStoreTests.mm (renamed from Firestore/Example/Tests/Local/FSTLocalStoreTests.m)29
-rw-r--r--Firestore/Example/Tests/Local/FSTMemoryLocalStoreTests.mm (renamed from Firestore/Example/Tests/Local/FSTMemoryLocalStoreTests.m)0
-rw-r--r--Firestore/Example/Tests/Local/FSTMemoryMutationQueueTests.mm (renamed from Firestore/Example/Tests/Local/FSTMemoryMutationQueueTests.m)0
-rw-r--r--Firestore/Example/Tests/Local/FSTMemoryQueryCacheTests.mm (renamed from Firestore/Example/Tests/Local/FSTMemoryQueryCacheTests.m)0
-rw-r--r--Firestore/Example/Tests/Local/FSTMemoryRemoteDocumentCacheTests.mm (renamed from Firestore/Example/Tests/Local/FSTMemoryRemoteDocumentCacheTests.m)0
-rw-r--r--Firestore/Example/Tests/Local/FSTMutationQueueTests.mm (renamed from Firestore/Example/Tests/Local/FSTMutationQueueTests.m)2
-rw-r--r--Firestore/Example/Tests/Local/FSTPersistenceTestHelpers.h6
-rw-r--r--Firestore/Example/Tests/Local/FSTPersistenceTestHelpers.mm (renamed from Firestore/Example/Tests/Local/FSTPersistenceTestHelpers.m)9
-rw-r--r--Firestore/Example/Tests/Local/FSTQueryCacheTests.mm (renamed from Firestore/Example/Tests/Local/FSTQueryCacheTests.m)118
-rw-r--r--Firestore/Example/Tests/Local/FSTReferenceSetTests.mm (renamed from Firestore/Example/Tests/Local/FSTReferenceSetTests.m)9
-rw-r--r--Firestore/Example/Tests/Local/FSTRemoteDocumentCacheTests.mm (renamed from Firestore/Example/Tests/Local/FSTRemoteDocumentCacheTests.m)2
-rw-r--r--Firestore/Example/Tests/Local/FSTRemoteDocumentChangeBufferTests.mm (renamed from Firestore/Example/Tests/Local/FSTRemoteDocumentChangeBufferTests.m)0
-rw-r--r--Firestore/Example/Tests/Model/FSTDatabaseIDTests.mm (renamed from Firestore/Example/Tests/Model/FSTDatabaseIDTests.m)0
-rw-r--r--Firestore/Example/Tests/Model/FSTDocumentKeyTests.mm (renamed from Firestore/Example/Tests/Model/FSTDocumentKeyTests.m)0
-rw-r--r--Firestore/Example/Tests/Model/FSTDocumentSetTests.mm (renamed from Firestore/Example/Tests/Model/FSTDocumentSetTests.m)8
-rw-r--r--Firestore/Example/Tests/Model/FSTDocumentTests.mm (renamed from Firestore/Example/Tests/Model/FSTDocumentTests.m)63
-rw-r--r--Firestore/Example/Tests/Model/FSTFieldValueTests.mm (renamed from Firestore/Example/Tests/Model/FSTFieldValueTests.m)18
-rw-r--r--Firestore/Example/Tests/Model/FSTMutationTests.mm (renamed from Firestore/Example/Tests/Model/FSTMutationTests.m)49
-rw-r--r--Firestore/Example/Tests/Model/FSTPathTests.mm (renamed from Firestore/Example/Tests/Model/FSTPathTests.m)0
-rw-r--r--Firestore/Example/Tests/Remote/FSTDatastoreTests.mm (renamed from Firestore/Example/Tests/Remote/FSTDatastoreTests.m)2
-rw-r--r--Firestore/Example/Tests/Remote/FSTRemoteEventTests.mm (renamed from Firestore/Example/Tests/Remote/FSTRemoteEventTests.m)0
-rw-r--r--Firestore/Example/Tests/Remote/FSTSerializerBetaTests.mm (renamed from Firestore/Example/Tests/Remote/FSTSerializerBetaTests.m)52
-rw-r--r--Firestore/Example/Tests/Remote/FSTWatchChange+Testing.mm (renamed from Firestore/Example/Tests/Remote/FSTWatchChange+Testing.m)0
-rw-r--r--Firestore/Example/Tests/Remote/FSTWatchChangeTests.mm (renamed from Firestore/Example/Tests/Remote/FSTWatchChangeTests.m)0
-rw-r--r--Firestore/Example/Tests/SpecTests/FSTLevelDBSpecTests.mm (renamed from Firestore/Example/Tests/SpecTests/FSTLevelDBSpecTests.m)0
-rw-r--r--Firestore/Example/Tests/SpecTests/FSTMemorySpecTests.mm (renamed from Firestore/Example/Tests/SpecTests/FSTMemorySpecTests.m)0
-rw-r--r--Firestore/Example/Tests/SpecTests/FSTMockDatastore.mm (renamed from Firestore/Example/Tests/SpecTests/FSTMockDatastore.m)0
-rw-r--r--Firestore/Example/Tests/SpecTests/FSTSpecTests.mm (renamed from Firestore/Example/Tests/SpecTests/FSTSpecTests.m)13
-rw-r--r--Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.h3
-rw-r--r--Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.mm (renamed from Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.m)14
-rw-r--r--Firestore/Example/Tests/SpecTests/json/listen_spec_test.json14
-rw-r--r--Firestore/Example/Tests/SpecTests/json/offline_spec_test.json419
-rw-r--r--Firestore/Example/Tests/Util/FSTAssertTests.mm (renamed from Firestore/Example/Tests/Util/FSTAssertTests.m)0
-rw-r--r--Firestore/Example/Tests/Util/FSTComparisonTests.m143
-rw-r--r--Firestore/Example/Tests/Util/FSTEventAccumulator.h5
-rw-r--r--Firestore/Example/Tests/Util/FSTEventAccumulator.mm (renamed from Firestore/Example/Tests/Util/FSTEventAccumulator.m)5
-rw-r--r--Firestore/Example/Tests/Util/FSTHelpers.h4
-rw-r--r--Firestore/Example/Tests/Util/FSTHelpers.mm (renamed from Firestore/Example/Tests/Util/FSTHelpers.m)22
-rw-r--r--Firestore/Example/Tests/Util/FSTIntegrationTestCase.h5
-rw-r--r--Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm39
-rw-r--r--Firestore/Example/Tests/Util/FSTTestDispatchQueue.mm (renamed from Firestore/Example/Tests/Util/FSTTestDispatchQueue.m)0
-rw-r--r--Firestore/Example/Tests/Util/XCTestCase+Await.h8
-rw-r--r--Firestore/Example/Tests/Util/XCTestCase+Await.mm (renamed from Firestore/Example/Tests/Util/XCTestCase+Await.m)12
-rw-r--r--Firestore/Port/absl/absl_attributes.h644
-rw-r--r--Firestore/Port/absl/absl_config.h306
-rw-r--r--Firestore/Port/absl/absl_endian.h342
-rw-r--r--Firestore/Port/absl/absl_integral_types.h148
-rw-r--r--Firestore/Port/absl/absl_port.h535
-rw-r--r--Firestore/Port/string_util.h66
-rw-r--r--Firestore/Source/API/FIRCollectionReference.mm21
-rw-r--r--Firestore/Source/API/FIRDocumentChange+Internal.h1
-rw-r--r--Firestore/Source/API/FIRDocumentChange.mm (renamed from Firestore/Source/API/FIRDocumentChange.m)22
-rw-r--r--Firestore/Source/API/FIRDocumentReference.mm (renamed from Firestore/Source/API/FIRDocumentReference.m)9
-rw-r--r--Firestore/Source/API/FIRDocumentSnapshot.mm (renamed from Firestore/Source/API/FIRDocumentSnapshot.m)137
-rw-r--r--Firestore/Source/API/FIRFieldPath.mm (renamed from Firestore/Source/API/FIRFieldPath.m)2
-rw-r--r--Firestore/Source/API/FIRFieldValue.mm (renamed from Firestore/Source/API/FIRFieldValue.m)0
-rw-r--r--Firestore/Source/API/FIRFirestore.mm (renamed from Firestore/Source/API/FIRFirestore.m)117
-rw-r--r--Firestore/Source/API/FIRFirestoreSettings.mm (renamed from Firestore/Source/API/FIRFirestoreSettings.m)0
-rw-r--r--Firestore/Source/API/FIRFirestoreVersion.mm (renamed from Firestore/Source/API/FIRFirestoreVersion.m)2
-rw-r--r--Firestore/Source/API/FIRGeoPoint.mm (renamed from Firestore/Source/API/FIRGeoPoint.m)17
-rw-r--r--Firestore/Source/API/FIRListenerRegistration.mm (renamed from Firestore/Source/API/FIRListenerRegistration.m)0
-rw-r--r--Firestore/Source/API/FIRQuery.mm (renamed from Firestore/Source/API/FIRQuery.m)96
-rw-r--r--Firestore/Source/API/FIRQuerySnapshot.mm (renamed from Firestore/Source/API/FIRQuerySnapshot.m)40
-rw-r--r--Firestore/Source/API/FIRSetOptions.mm (renamed from Firestore/Source/API/FIRSetOptions.m)4
-rw-r--r--Firestore/Source/API/FIRSnapshotMetadata.mm (renamed from Firestore/Source/API/FIRSnapshotMetadata.m)21
-rw-r--r--Firestore/Source/API/FIRSnapshotOptions+Internal.h38
-rw-r--r--Firestore/Source/API/FIRSnapshotOptions.mm72
-rw-r--r--Firestore/Source/API/FIRTransaction.mm (renamed from Firestore/Source/API/FIRTransaction.m)0
-rw-r--r--Firestore/Source/API/FIRWriteBatch.mm (renamed from Firestore/Source/API/FIRWriteBatch.m)6
-rw-r--r--Firestore/Source/API/FSTUserDataConverter.mm (renamed from Firestore/Source/API/FSTUserDataConverter.m)0
-rw-r--r--Firestore/Source/Auth/FSTCredentialsProvider.mm (renamed from Firestore/Source/Auth/FSTCredentialsProvider.m)0
-rw-r--r--Firestore/Source/Auth/FSTEmptyCredentialsProvider.mm (renamed from Firestore/Source/Auth/FSTEmptyCredentialsProvider.m)0
-rw-r--r--Firestore/Source/Auth/FSTUser.mm (renamed from Firestore/Source/Auth/FSTUser.m)0
-rw-r--r--Firestore/Source/Core/FSTDatabaseInfo.mm (renamed from Firestore/Source/Core/FSTDatabaseInfo.m)0
-rw-r--r--Firestore/Source/Core/FSTEventManager.h2
-rw-r--r--Firestore/Source/Core/FSTEventManager.mm (renamed from Firestore/Source/Core/FSTEventManager.m)8
-rw-r--r--Firestore/Source/Core/FSTFirestoreClient.h2
-rw-r--r--Firestore/Source/Core/FSTFirestoreClient.mm (renamed from Firestore/Source/Core/FSTFirestoreClient.m)15
-rw-r--r--Firestore/Source/Core/FSTListenSequence.h37
-rw-r--r--Firestore/Source/Core/FSTListenSequence.mm50
-rw-r--r--Firestore/Source/Core/FSTQuery.mm (renamed from Firestore/Source/Core/FSTQuery.m)26
-rw-r--r--Firestore/Source/Core/FSTSnapshotVersion.mm (renamed from Firestore/Source/Core/FSTSnapshotVersion.m)0
-rw-r--r--Firestore/Source/Core/FSTSyncEngine.h3
-rw-r--r--Firestore/Source/Core/FSTSyncEngine.mm (renamed from Firestore/Source/Core/FSTSyncEngine.m)36
-rw-r--r--Firestore/Source/Core/FSTTargetIDGenerator.h55
-rw-r--r--Firestore/Source/Core/FSTTargetIDGenerator.m105
-rw-r--r--Firestore/Source/Core/FSTTimestamp.mm (renamed from Firestore/Source/Core/FSTTimestamp.m)9
-rw-r--r--Firestore/Source/Core/FSTTransaction.mm (renamed from Firestore/Source/Core/FSTTransaction.m)32
-rw-r--r--Firestore/Source/Core/FSTTypes.h11
-rw-r--r--Firestore/Source/Core/FSTView.h7
-rw-r--r--Firestore/Source/Core/FSTView.mm (renamed from Firestore/Source/Core/FSTView.m)50
-rw-r--r--Firestore/Source/Core/FSTViewSnapshot.mm (renamed from Firestore/Source/Core/FSTViewSnapshot.m)0
-rw-r--r--Firestore/Source/Local/FSTDocumentReference.h4
-rw-r--r--Firestore/Source/Local/FSTDocumentReference.mm (renamed from Firestore/Source/Local/FSTDocumentReference.m)13
-rw-r--r--Firestore/Source/Local/FSTEagerGarbageCollector.mm (renamed from Firestore/Source/Local/FSTEagerGarbageCollector.m)0
-rw-r--r--Firestore/Source/Local/FSTLevelDB.h17
-rw-r--r--Firestore/Source/Local/FSTLevelDB.mm17
-rw-r--r--Firestore/Source/Local/FSTLevelDBKey.h12
-rw-r--r--Firestore/Source/Local/FSTLevelDBKey.mm51
-rw-r--r--Firestore/Source/Local/FSTLevelDBMigrations.h41
-rw-r--r--Firestore/Source/Local/FSTLevelDBMigrations.mm95
-rw-r--r--Firestore/Source/Local/FSTLevelDBMutationQueue.h11
-rw-r--r--Firestore/Source/Local/FSTLevelDBMutationQueue.mm6
-rw-r--r--Firestore/Source/Local/FSTLevelDBQueryCache.h17
-rw-r--r--Firestore/Source/Local/FSTLevelDBQueryCache.mm101
-rw-r--r--Firestore/Source/Local/FSTLevelDBRemoteDocumentCache.h11
-rw-r--r--Firestore/Source/Local/FSTLevelDBRemoteDocumentCache.mm4
-rw-r--r--Firestore/Source/Local/FSTLocalDocumentsView.mm (renamed from Firestore/Source/Local/FSTLocalDocumentsView.m)4
-rw-r--r--Firestore/Source/Local/FSTLocalSerializer.mm (renamed from Firestore/Source/Local/FSTLocalSerializer.m)5
-rw-r--r--Firestore/Source/Local/FSTLocalStore.mm (renamed from Firestore/Source/Local/FSTLocalStore.m)29
-rw-r--r--Firestore/Source/Local/FSTLocalViewChanges.mm (renamed from Firestore/Source/Local/FSTLocalViewChanges.m)0
-rw-r--r--Firestore/Source/Local/FSTLocalWriteResult.mm (renamed from Firestore/Source/Local/FSTLocalWriteResult.m)0
-rw-r--r--Firestore/Source/Local/FSTMemoryMutationQueue.mm (renamed from Firestore/Source/Local/FSTMemoryMutationQueue.m)7
-rw-r--r--Firestore/Source/Local/FSTMemoryPersistence.mm (renamed from Firestore/Source/Local/FSTMemoryPersistence.m)0
-rw-r--r--Firestore/Source/Local/FSTMemoryQueryCache.mm (renamed from Firestore/Source/Local/FSTMemoryQueryCache.m)9
-rw-r--r--Firestore/Source/Local/FSTMemoryRemoteDocumentCache.mm (renamed from Firestore/Source/Local/FSTMemoryRemoteDocumentCache.m)0
-rw-r--r--Firestore/Source/Local/FSTNoOpGarbageCollector.mm (renamed from Firestore/Source/Local/FSTNoOpGarbageCollector.m)0
-rw-r--r--Firestore/Source/Local/FSTQueryCache.h5
-rw-r--r--Firestore/Source/Local/FSTQueryData.h4
-rw-r--r--Firestore/Source/Local/FSTQueryData.mm (renamed from Firestore/Source/Local/FSTQueryData.m)5
-rw-r--r--Firestore/Source/Local/FSTReferenceSet.mm (renamed from Firestore/Source/Local/FSTReferenceSet.m)0
-rw-r--r--Firestore/Source/Local/FSTRemoteDocumentChangeBuffer.mm (renamed from Firestore/Source/Local/FSTRemoteDocumentChangeBuffer.m)0
-rw-r--r--Firestore/Source/Local/FSTWriteGroup.h13
-rw-r--r--Firestore/Source/Local/FSTWriteGroup.mm3
-rw-r--r--Firestore/Source/Local/FSTWriteGroupTracker.mm (renamed from Firestore/Source/Local/FSTWriteGroupTracker.m)0
-rw-r--r--Firestore/Source/Local/StringView.h16
-rw-r--r--Firestore/Source/Model/FSTDatabaseID.mm (renamed from Firestore/Source/Model/FSTDatabaseID.m)2
-rw-r--r--Firestore/Source/Model/FSTDocument.mm (renamed from Firestore/Source/Model/FSTDocument.m)0
-rw-r--r--Firestore/Source/Model/FSTDocumentDictionary.mm (renamed from Firestore/Source/Model/FSTDocumentDictionary.m)0
-rw-r--r--Firestore/Source/Model/FSTDocumentKey.mm (renamed from Firestore/Source/Model/FSTDocumentKey.m)0
-rw-r--r--Firestore/Source/Model/FSTDocumentKeySet.mm (renamed from Firestore/Source/Model/FSTDocumentKeySet.m)0
-rw-r--r--Firestore/Source/Model/FSTDocumentSet.h10
-rw-r--r--Firestore/Source/Model/FSTDocumentSet.mm (renamed from Firestore/Source/Model/FSTDocumentSet.m)10
-rw-r--r--Firestore/Source/Model/FSTDocumentVersionDictionary.mm (renamed from Firestore/Source/Model/FSTDocumentVersionDictionary.m)0
-rw-r--r--Firestore/Source/Model/FSTFieldValue.h103
-rw-r--r--Firestore/Source/Model/FSTFieldValue.mm (renamed from Firestore/Source/Model/FSTFieldValue.m)158
-rw-r--r--Firestore/Source/Model/FSTMutation.h16
-rw-r--r--Firestore/Source/Model/FSTMutation.mm (renamed from Firestore/Source/Model/FSTMutation.m)56
-rw-r--r--Firestore/Source/Model/FSTMutationBatch.mm (renamed from Firestore/Source/Model/FSTMutationBatch.m)2
-rw-r--r--Firestore/Source/Model/FSTPath.mm (renamed from Firestore/Source/Model/FSTPath.m)2
-rw-r--r--Firestore/Source/Public/FIRDocumentChange.h4
-rw-r--r--Firestore/Source/Public/FIRDocumentReference.h2
-rw-r--r--Firestore/Source/Public/FIRDocumentSnapshot.h143
-rw-r--r--Firestore/Source/Public/FIRFirestore.h17
-rw-r--r--Firestore/Source/Public/FIRQuery.h13
-rw-r--r--Firestore/Source/Public/FIRQuerySnapshot.h4
-rw-r--r--Firestore/Source/Public/FIRWriteBatch.h7
-rw-r--r--Firestore/Source/Remote/FSTBufferedWriter.mm (renamed from Firestore/Source/Remote/FSTBufferedWriter.m)0
-rw-r--r--Firestore/Source/Remote/FSTDatastore.mm (renamed from Firestore/Source/Remote/FSTDatastore.m)0
-rw-r--r--Firestore/Source/Remote/FSTExistenceFilter.mm (renamed from Firestore/Source/Remote/FSTExistenceFilter.m)0
-rw-r--r--Firestore/Source/Remote/FSTRemoteEvent.mm (renamed from Firestore/Source/Remote/FSTRemoteEvent.m)12
-rw-r--r--Firestore/Source/Remote/FSTRemoteStore.h2
-rw-r--r--Firestore/Source/Remote/FSTRemoteStore.mm (renamed from Firestore/Source/Remote/FSTRemoteStore.m)129
-rw-r--r--Firestore/Source/Remote/FSTSerializerBeta.mm (renamed from Firestore/Source/Remote/FSTSerializerBeta.m)30
-rw-r--r--Firestore/Source/Remote/FSTStream.mm (renamed from Firestore/Source/Remote/FSTStream.m)5
-rw-r--r--Firestore/Source/Remote/FSTWatchChange.mm (renamed from Firestore/Source/Remote/FSTWatchChange.m)0
-rw-r--r--Firestore/Source/Util/FSTAsyncQueryListener.mm (renamed from Firestore/Source/Util/FSTAsyncQueryListener.m)0
-rw-r--r--Firestore/Source/Util/FSTComparison.h66
-rw-r--r--Firestore/Source/Util/FSTComparison.m175
-rw-r--r--Firestore/Source/Util/FSTDispatchQueue.mm (renamed from Firestore/Source/Util/FSTDispatchQueue.m)0
-rw-r--r--Firestore/Source/Util/FSTLogger.h8
-rw-r--r--Firestore/Source/Util/FSTLogger.mm (renamed from Firestore/Source/Util/FSTLogger.m)0
-rw-r--r--Firestore/Source/Util/FSTUsageValidation.h8
-rw-r--r--Firestore/Source/Util/FSTUsageValidation.mm (renamed from Firestore/Source/Util/FSTUsageValidation.m)0
-rw-r--r--Firestore/core/CMakeLists.txt11
-rw-r--r--Firestore/core/include/firebase/firestore/geo_point.h89
-rw-r--r--Firestore/core/src/firebase/firestore/CMakeLists.txt22
-rw-r--r--Firestore/core/src/firebase/firestore/base/port.h33
-rw-r--r--Firestore/core/src/firebase/firestore/core/CMakeLists.txt20
-rw-r--r--Firestore/core/src/firebase/firestore/core/target_id_generator.cc62
-rw-r--r--Firestore/core/src/firebase/firestore/core/target_id_generator.h83
-rw-r--r--Firestore/core/src/firebase/firestore/core/types.h32
-rw-r--r--Firestore/core/src/firebase/firestore/geo_point.cc50
-rw-r--r--Firestore/core/src/firebase/firestore/immutable/CMakeLists.txt21
-rw-r--r--Firestore/core/src/firebase/firestore/immutable/array_sorted_map.cc30
-rw-r--r--Firestore/core/src/firebase/firestore/immutable/array_sorted_map.h318
-rw-r--r--Firestore/core/src/firebase/firestore/immutable/map_entry.h62
-rw-r--r--Firestore/core/src/firebase/firestore/model/CMakeLists.txt28
-rw-r--r--Firestore/core/src/firebase/firestore/model/database_id.cc40
-rw-r--r--Firestore/core/src/firebase/firestore/model/database_id.h92
-rw-r--r--Firestore/core/src/firebase/firestore/model/field_value.cc388
-rw-r--r--Firestore/core/src/firebase/firestore/model/field_value.h169
-rw-r--r--Firestore/core/src/firebase/firestore/model/timestamp.cc54
-rw-r--r--Firestore/core/src/firebase/firestore/model/timestamp.h94
-rw-r--r--Firestore/core/src/firebase/firestore/remote/CMakeLists.txt22
-rw-r--r--Firestore/core/src/firebase/firestore/remote/datastore.cc31
-rw-r--r--Firestore/core/src/firebase/firestore/remote/datastore.h40
-rw-r--r--Firestore/core/src/firebase/firestore/util/CMakeLists.txt114
-rw-r--r--Firestore/core/src/firebase/firestore/util/assert_apple.mm51
-rw-r--r--Firestore/core/src/firebase/firestore/util/assert_stdio.cc54
-rw-r--r--Firestore/core/src/firebase/firestore/util/bits.cc (renamed from Firestore/Port/bits.cc)14
-rw-r--r--Firestore/core/src/firebase/firestore/util/bits.h (renamed from Firestore/Port/bits.h)26
-rw-r--r--Firestore/core/src/firebase/firestore/util/comparison.cc112
-rw-r--r--Firestore/core/src/firebase/firestore/util/comparison.h181
-rw-r--r--Firestore/core/src/firebase/firestore/util/config.h.in35
-rw-r--r--Firestore/core/src/firebase/firestore/util/firebase_assert.h105
-rw-r--r--Firestore/core/src/firebase/firestore/util/iterator_adaptors.h812
-rw-r--r--Firestore/core/src/firebase/firestore/util/log.h63
-rw-r--r--Firestore/core/src/firebase/firestore/util/log_apple.mm123
-rw-r--r--Firestore/core/src/firebase/firestore/util/log_stdio.cc97
-rw-r--r--Firestore/core/src/firebase/firestore/util/ordered_code.cc (renamed from Firestore/Port/ordered_code.cc)247
-rw-r--r--Firestore/core/src/firebase/firestore/util/ordered_code.h (renamed from Firestore/Port/ordered_code.h)75
-rw-r--r--Firestore/core/src/firebase/firestore/util/secure_random.h16
-rw-r--r--Firestore/core/src/firebase/firestore/util/secure_random_arc4random.cc2
-rw-r--r--Firestore/core/src/firebase/firestore/util/secure_random_openssl.cc46
-rw-r--r--Firestore/core/src/firebase/firestore/util/string_apple.h52
-rw-r--r--Firestore/core/src/firebase/firestore/util/string_printf.cc102
-rw-r--r--Firestore/core/src/firebase/firestore/util/string_printf.h47
-rw-r--r--Firestore/core/src/firebase/firestore/util/string_util.cc (renamed from Firestore/Port/string_util.cc)18
-rw-r--r--Firestore/core/src/firebase/firestore/util/string_util.h72
-rw-r--r--Firestore/core/test/firebase/firestore/CMakeLists.txt21
-rw-r--r--Firestore/core/test/firebase/firestore/core/CMakeLists.txt21
-rw-r--r--Firestore/core/test/firebase/firestore/core/target_id_generator_test.cc80
-rw-r--r--Firestore/core/test/firebase/firestore/geo_point_test.cc41
-rw-r--r--Firestore/core/test/firebase/firestore/immutable/CMakeLists.txt22
-rw-r--r--Firestore/core/test/firebase/firestore/immutable/array_sorted_map_test.cc326
-rw-r--r--Firestore/core/test/firebase/firestore/model/CMakeLists.txt23
-rw-r--r--Firestore/core/test/firebase/firestore/model/database_id_test.cc47
-rw-r--r--Firestore/core/test/firebase/firestore/model/field_value_test.cc462
-rw-r--r--Firestore/core/test/firebase/firestore/model/timestamp_test.cc49
-rw-r--r--Firestore/core/test/firebase/firestore/remote/CMakeLists.txt21
-rw-r--r--Firestore/core/test/firebase/firestore/remote/datastore_test.cc28
-rw-r--r--Firestore/core/test/firebase/firestore/util/CMakeLists.txt62
-rw-r--r--Firestore/core/test/firebase/firestore/util/assert_test.cc63
-rw-r--r--Firestore/core/test/firebase/firestore/util/autoid_test.cc6
-rw-r--r--Firestore/core/test/firebase/firestore/util/bits_test.cc (renamed from Firestore/Port/bits_test.cc)56
-rw-r--r--Firestore/core/test/firebase/firestore/util/comparison_test.cc211
-rw-r--r--Firestore/core/test/firebase/firestore/util/iterator_adaptors_test.cc1277
-rw-r--r--Firestore/core/test/firebase/firestore/util/log_test.cc61
-rw-r--r--Firestore/core/test/firebase/firestore/util/ordered_code_test.cc (renamed from Firestore/Port/ordered_code_test.cc)264
-rw-r--r--Firestore/core/test/firebase/firestore/util/secure_random_test.cc27
-rw-r--r--Firestore/core/test/firebase/firestore/util/string_printf_test.cc78
-rw-r--r--Firestore/core/test/firebase/firestore/util/string_util_test.cc (renamed from Firestore/Port/string_util_test.cc)24
-rw-r--r--Firestore/third_party/Immutable/FSTArraySortedDictionary.m13
-rw-r--r--Firestore/third_party/Immutable/FSTImmutableSortedDictionary.h8
-rw-r--r--Firestore/third_party/Immutable/FSTImmutableSortedDictionary.m4
-rw-r--r--Firestore/third_party/Immutable/FSTImmutableSortedSet.h2
-rw-r--r--Firestore/third_party/Immutable/FSTImmutableSortedSet.m4
-rw-r--r--Firestore/third_party/Immutable/FSTTreeSortedDictionary.m30
-rw-r--r--Firestore/third_party/Immutable/Tests/FSTArraySortedDictionaryTests.m22
-rw-r--r--Firestore/third_party/Immutable/Tests/FSTTreeSortedDictionaryTests.m19
-rw-r--r--Firestore/third_party/abseil-cpp/CMakeLists.txt1
-rw-r--r--Firestore/third_party/abseil-cpp/absl/CMakeLists.txt7
-rw-r--r--Firestore/third_party/abseil-cpp/absl/base/CMakeLists.txt277
-rw-r--r--Firestore/third_party/abseil-cpp/absl/base/config_test.cc17
-rw-r--r--Firestore/third_party/abseil-cpp/absl/base/macros.h2
-rw-r--r--Firestore/third_party/abseil-cpp/absl/meta/CMakeLists.txt3
-rw-r--r--Firestore/third_party/abseil-cpp/absl/numeric/int128_test.cc1
-rw-r--r--Firestore/third_party/abseil-cpp/absl/strings/CMakeLists.txt1
295 files changed, 11336 insertions, 4901 deletions
diff --git a/Firestore/CHANGELOG.md b/Firestore/CHANGELOG.md
index 0c5bcdc..13b147d 100644
--- a/Firestore/CHANGELOG.md
+++ b/Firestore/CHANGELOG.md
@@ -1,8 +1,34 @@
# Unreleased
+- [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.
+
+# 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.
+- [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.
+- [changed] Snapshot listeners (with the `includeMetadataChanges` option
+ enabled) now receive an event with `snapshot.metadata.isFromCache` set to
+ `true` if the SDK loses its connection to the backend. A new event with
+ `snapshot.metadata.isFromCache` set to false will be raised once the
+ 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.
- [fixed] Fixed a crash when using path names with international characters
with persistence enabled.
-
- [fixed] Addressed race condition during the teardown of idle streams (#490).
# v0.9.3
diff --git a/Firestore/CMakeLists.txt b/Firestore/CMakeLists.txt
index 6c2a32e..b8c95c6 100644
--- a/Firestore/CMakeLists.txt
+++ b/Firestore/CMakeLists.txt
@@ -13,20 +13,54 @@
# 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}/..")
-include("${FIREBASE_SOURCE_DIR}/cmake/utils.cmake")
+set(FIREBASE_SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/..)
-find_package(GTest REQUIRED)
+# 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
+# superbuild (i.e. by invoking cmake on the directory above this).
+#
+# If you want to use this project directly in e.g. CLion, make sure you
+# configure this.
+set(FIREBASE_INSTALL_DIR ${CMAKE_INSTALL_PREFIX})
-# We use C++11
-set(CMAKE_CXX_STANDARD 11)
-set(CMAKE_CXX_STANDARD_REQUIRED ON)
-set(CMAKE_CXX_EXTENSIONS OFF)
+list(INSERT CMAKE_MODULE_PATH 0 ${FIREBASE_SOURCE_DIR}/cmake)
+include(utils)
-# Fully qualified imports, project wide
-include_directories("${FIREBASE_SOURCE_DIR}")
+# 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)
+
+if(APPLE)
+ find_package(FirebaseCore REQUIRED)
+endif()
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})
+
add_subdirectory(core)
diff --git a/Firestore/Example/Firestore.xcodeproj/project.pbxproj b/Firestore/Example/Firestore.xcodeproj/project.pbxproj
index 437b661..4a8f4fb 100644
--- a/Firestore/Example/Firestore.xcodeproj/project.pbxproj
+++ b/Firestore/Example/Firestore.xcodeproj/project.pbxproj
@@ -24,12 +24,92 @@
/* 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 */; };
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 */; };
+ 5492E03B2021401F00B64F25 /* FSTTestDispatchQueue.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0362021401E00B64F25 /* FSTTestDispatchQueue.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 */; };
+ 5492E0432021441E00B64F25 /* FSTTestDispatchQueue.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0362021401E00B64F25 /* FSTTestDispatchQueue.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 */; };
+ 5492E062202154B900B64F25 /* FSTTimestampTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E05B202154B800B64F25 /* FSTTimestampTests.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 */; };
+ 5492E066202154B900B64F25 /* FSTDatabaseInfoTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E05F202154B900B64F25 /* FSTDatabaseInfoTests.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 */; };
+ 5492E0BB2021555100B64F25 /* FSTDatabaseIDTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0B42021555100B64F25 /* FSTDatabaseIDTests.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 */; };
+ 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 */; };
54DA12A81F315EE100DD57A1 /* limbo_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA129E1F315EE100DD57A1 /* limbo_spec_test.json */; };
@@ -40,11 +120,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 */; };
@@ -57,86 +133,30 @@
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 */; };
- 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 */; };
873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */; };
+ 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 */; };
+ AB7BAB342012B519001E0872 /* geo_point_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB7BAB332012B519001E0872 /* geo_point_test.cc */; };
+ 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 */; };
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 */; };
F104BBD69BC3F0796E3A77C1 /* Pods_Firestore_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 69F6A10DBD6187489481CD76 /* Pods_Firestore_Tests.framework */; };
/* End PBXBuildFile section */
@@ -185,11 +205,95 @@
3B843E4A1F3930A400548890 /* remote_store_spec_test.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = remote_store_spec_test.json; sourceTree = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
+ 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 = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
- 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 = "<group>"; };
54764FAE1FAA21B90085E60A /* FSTGoogleTestTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = FSTGoogleTestTests.mm; path = GoogleTest/FSTGoogleTestTests.mm; sourceTree = "<group>"; };
+ 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 = "<group>"; };
+ 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 = "<group>"; };
5491BC711FB44593008B3588 /* FSTIntegrationTestCase.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTIntegrationTestCase.mm; sourceTree = "<group>"; };
+ 5492E02C20213FFB00B64F25 /* FSTLevelDBSpecTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTLevelDBSpecTests.mm; sourceTree = "<group>"; };
+ 5492E02D20213FFC00B64F25 /* FSTMockDatastore.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTMockDatastore.mm; sourceTree = "<group>"; };
+ 5492E02E20213FFC00B64F25 /* FSTSyncEngineTestDriver.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTSyncEngineTestDriver.mm; sourceTree = "<group>"; };
+ 5492E02F20213FFC00B64F25 /* FSTMemorySpecTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTMemorySpecTests.mm; sourceTree = "<group>"; };
+ 5492E03020213FFC00B64F25 /* FSTSpecTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTSpecTests.mm; sourceTree = "<group>"; };
+ 5492E0362021401E00B64F25 /* FSTTestDispatchQueue.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTTestDispatchQueue.mm; sourceTree = "<group>"; };
+ 5492E0372021401E00B64F25 /* XCTestCase+Await.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "XCTestCase+Await.mm"; sourceTree = "<group>"; };
+ 5492E0382021401E00B64F25 /* FSTAssertTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTAssertTests.mm; sourceTree = "<group>"; };
+ 5492E0392021401F00B64F25 /* FSTEventAccumulator.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTEventAccumulator.mm; sourceTree = "<group>"; };
+ 5492E03A2021401F00B64F25 /* FSTHelpers.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTHelpers.mm; sourceTree = "<group>"; };
+ 5492E045202154AA00B64F25 /* FIRCollectionReferenceTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRCollectionReferenceTests.mm; sourceTree = "<group>"; };
+ 5492E046202154AA00B64F25 /* FIRQueryTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRQueryTests.mm; sourceTree = "<group>"; };
+ 5492E047202154AA00B64F25 /* FSTAPIHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSTAPIHelpers.h; sourceTree = "<group>"; };
+ 5492E048202154AA00B64F25 /* FIRGeoPointTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRGeoPointTests.mm; sourceTree = "<group>"; };
+ 5492E049202154AA00B64F25 /* FIRDocumentReferenceTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRDocumentReferenceTests.mm; sourceTree = "<group>"; };
+ 5492E04A202154AA00B64F25 /* FIRFieldValueTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRFieldValueTests.mm; sourceTree = "<group>"; };
+ 5492E04B202154AA00B64F25 /* FIRDocumentSnapshotTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRDocumentSnapshotTests.mm; sourceTree = "<group>"; };
+ 5492E04C202154AA00B64F25 /* FIRFieldPathTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRFieldPathTests.mm; sourceTree = "<group>"; };
+ 5492E04D202154AA00B64F25 /* FIRSnapshotMetadataTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRSnapshotMetadataTests.mm; sourceTree = "<group>"; };
+ 5492E04E202154AA00B64F25 /* FSTAPIHelpers.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTAPIHelpers.mm; sourceTree = "<group>"; };
+ 5492E04F202154AA00B64F25 /* FIRQuerySnapshotTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRQuerySnapshotTests.mm; sourceTree = "<group>"; };
+ 5492E05A202154B800B64F25 /* FSTSyncEngine+Testing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "FSTSyncEngine+Testing.h"; sourceTree = "<group>"; };
+ 5492E05B202154B800B64F25 /* FSTTimestampTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTTimestampTests.mm; sourceTree = "<group>"; };
+ 5492E05C202154B800B64F25 /* FSTViewSnapshotTest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTViewSnapshotTest.mm; sourceTree = "<group>"; };
+ 5492E05D202154B900B64F25 /* FSTQueryListenerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTQueryListenerTests.mm; sourceTree = "<group>"; };
+ 5492E05E202154B900B64F25 /* FSTViewTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTViewTests.mm; sourceTree = "<group>"; };
+ 5492E05F202154B900B64F25 /* FSTDatabaseInfoTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTDatabaseInfoTests.mm; sourceTree = "<group>"; };
+ 5492E060202154B900B64F25 /* FSTEventManagerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTEventManagerTests.mm; sourceTree = "<group>"; };
+ 5492E061202154B900B64F25 /* FSTQueryTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTQueryTests.mm; sourceTree = "<group>"; };
+ 5492E069202154D500B64F25 /* FIRQueryTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRQueryTests.mm; sourceTree = "<group>"; };
+ 5492E06A202154D500B64F25 /* FIRFieldsTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRFieldsTests.mm; sourceTree = "<group>"; };
+ 5492E06B202154D500B64F25 /* FIRListenerRegistrationTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRListenerRegistrationTests.mm; sourceTree = "<group>"; };
+ 5492E06C202154D500B64F25 /* FIRDatabaseTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRDatabaseTests.mm; sourceTree = "<group>"; };
+ 5492E06D202154D600B64F25 /* FIRValidationTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRValidationTests.mm; sourceTree = "<group>"; };
+ 5492E06E202154D600B64F25 /* FIRServerTimestampTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRServerTimestampTests.mm; sourceTree = "<group>"; };
+ 5492E06F202154D600B64F25 /* FIRWriteBatchTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRWriteBatchTests.mm; sourceTree = "<group>"; };
+ 5492E070202154D600B64F25 /* FIRCursorTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRCursorTests.mm; sourceTree = "<group>"; };
+ 5492E071202154D600B64F25 /* FIRTypeTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRTypeTests.mm; sourceTree = "<group>"; };
+ 5492E07B202154EB00B64F25 /* FSTTransactionTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTTransactionTests.mm; sourceTree = "<group>"; };
+ 5492E07C202154EB00B64F25 /* FSTSmokeTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTSmokeTests.mm; sourceTree = "<group>"; };
+ 5492E07D202154EB00B64F25 /* FSTStreamTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTStreamTests.mm; sourceTree = "<group>"; };
+ 5492E07E202154EC00B64F25 /* FSTDatastoreTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTDatastoreTests.mm; sourceTree = "<group>"; };
+ 5492E0832021552A00B64F25 /* FSTLocalStoreTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTLocalStoreTests.mm; sourceTree = "<group>"; };
+ 5492E0842021552A00B64F25 /* FSTEagerGarbageCollectorTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTEagerGarbageCollectorTests.mm; sourceTree = "<group>"; };
+ 5492E0852021552A00B64F25 /* FSTRemoteDocumentCacheTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSTRemoteDocumentCacheTests.h; sourceTree = "<group>"; };
+ 5492E0862021552A00B64F25 /* FSTLevelDBMigrationsTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTLevelDBMigrationsTests.mm; sourceTree = "<group>"; };
+ 5492E0872021552A00B64F25 /* FSTLevelDBMutationQueueTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTLevelDBMutationQueueTests.mm; sourceTree = "<group>"; };
+ 5492E0882021552A00B64F25 /* FSTMemoryLocalStoreTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTMemoryLocalStoreTests.mm; sourceTree = "<group>"; };
+ 5492E0892021552A00B64F25 /* FSTQueryCacheTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTQueryCacheTests.mm; sourceTree = "<group>"; };
+ 5492E08A2021552A00B64F25 /* FSTLocalSerializerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTLocalSerializerTests.mm; sourceTree = "<group>"; };
+ 5492E08B2021552B00B64F25 /* FSTMemoryQueryCacheTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTMemoryQueryCacheTests.mm; sourceTree = "<group>"; };
+ 5492E08C2021552B00B64F25 /* FSTMemoryRemoteDocumentCacheTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTMemoryRemoteDocumentCacheTests.mm; sourceTree = "<group>"; };
+ 5492E08D2021552B00B64F25 /* FSTPersistenceTestHelpers.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTPersistenceTestHelpers.mm; sourceTree = "<group>"; };
+ 5492E08E2021552B00B64F25 /* FSTLevelDBKeyTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTLevelDBKeyTests.mm; sourceTree = "<group>"; };
+ 5492E08F2021552B00B64F25 /* FSTLevelDBLocalStoreTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTLevelDBLocalStoreTests.mm; sourceTree = "<group>"; };
+ 5492E0902021552B00B64F25 /* FSTRemoteDocumentChangeBufferTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTRemoteDocumentChangeBufferTests.mm; sourceTree = "<group>"; };
+ 5492E0912021552B00B64F25 /* FSTLocalStoreTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSTLocalStoreTests.h; sourceTree = "<group>"; };
+ 5492E0922021552B00B64F25 /* FSTLevelDBRemoteDocumentCacheTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTLevelDBRemoteDocumentCacheTests.mm; sourceTree = "<group>"; };
+ 5492E0932021552B00B64F25 /* StringViewTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = StringViewTests.mm; sourceTree = "<group>"; };
+ 5492E0942021552C00B64F25 /* FSTMutationQueueTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSTMutationQueueTests.h; sourceTree = "<group>"; };
+ 5492E0952021552C00B64F25 /* FSTQueryCacheTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSTQueryCacheTests.h; sourceTree = "<group>"; };
+ 5492E0962021552C00B64F25 /* FSTMutationQueueTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTMutationQueueTests.mm; sourceTree = "<group>"; };
+ 5492E0972021552C00B64F25 /* FSTMemoryMutationQueueTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTMemoryMutationQueueTests.mm; sourceTree = "<group>"; };
+ 5492E0982021552C00B64F25 /* FSTLevelDBQueryCacheTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTLevelDBQueryCacheTests.mm; sourceTree = "<group>"; };
+ 5492E0992021552C00B64F25 /* FSTPersistenceTestHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSTPersistenceTestHelpers.h; sourceTree = "<group>"; };
+ 5492E09A2021552C00B64F25 /* FSTReferenceSetTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTReferenceSetTests.mm; sourceTree = "<group>"; };
+ 5492E09B2021552C00B64F25 /* FSTWriteGroupTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTWriteGroupTests.mm; sourceTree = "<group>"; };
+ 5492E09C2021552D00B64F25 /* FSTRemoteDocumentCacheTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTRemoteDocumentCacheTests.mm; sourceTree = "<group>"; };
+ 5492E0B22021555000B64F25 /* FSTDocumentKeyTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTDocumentKeyTests.mm; sourceTree = "<group>"; };
+ 5492E0B32021555100B64F25 /* FSTDocumentSetTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTDocumentSetTests.mm; sourceTree = "<group>"; };
+ 5492E0B42021555100B64F25 /* FSTDatabaseIDTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTDatabaseIDTests.mm; sourceTree = "<group>"; };
+ 5492E0B52021555100B64F25 /* FSTPathTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTPathTests.mm; sourceTree = "<group>"; };
+ 5492E0B62021555100B64F25 /* FSTDocumentTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTDocumentTests.mm; sourceTree = "<group>"; };
+ 5492E0B72021555100B64F25 /* FSTMutationTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTMutationTests.mm; sourceTree = "<group>"; };
+ 5492E0B82021555100B64F25 /* FSTFieldValueTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTFieldValueTests.mm; sourceTree = "<group>"; };
+ 5492E0C02021557E00B64F25 /* FSTWatchChange+Testing.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "FSTWatchChange+Testing.mm"; sourceTree = "<group>"; };
+ 5492E0C12021557E00B64F25 /* FSTSerializerBetaTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTSerializerBetaTests.mm; sourceTree = "<group>"; };
+ 5492E0C22021557E00B64F25 /* FSTDatastoreTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTDatastoreTests.mm; sourceTree = "<group>"; };
+ 5492E0C32021557E00B64F25 /* FSTRemoteEventTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTRemoteEventTests.mm; sourceTree = "<group>"; };
+ 5492E0C42021557E00B64F25 /* FSTWatchChange+Testing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "FSTWatchChange+Testing.h"; sourceTree = "<group>"; };
+ 5492E0C52021557E00B64F25 /* FSTWatchChangeTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTWatchChangeTests.mm; sourceTree = "<group>"; };
+ 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 = "<group>"; };
54DA129C1F315EE100DD57A1 /* collection_spec_test.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = collection_spec_test.json; sourceTree = "<group>"; };
54DA129D1F315EE100DD57A1 /* existence_filter_spec_test.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = existence_filter_spec_test.json; sourceTree = "<group>"; };
54DA129E1F315EE100DD57A1 /* limbo_spec_test.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = limbo_spec_test.json; sourceTree = "<group>"; };
@@ -200,12 +304,10 @@
54DA12A31F315EE100DD57A1 /* persistence_spec_test.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = persistence_spec_test.json; sourceTree = "<group>"; };
54DA12A41F315EE100DD57A1 /* resume_token_spec_test.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = resume_token_spec_test.json; sourceTree = "<group>"; };
54DA12A51F315EE100DD57A1 /* write_spec_test.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = write_spec_test.json; sourceTree = "<group>"; };
- 54DA12B01F315F3800DD57A1 /* FIRValidationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRValidationTests.m; sourceTree = "<group>"; };
54E9281C1F33950B00C1953E /* FSTEventAccumulator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSTEventAccumulator.h; sourceTree = "<group>"; };
- 54E9281D1F33950B00C1953E /* FSTEventAccumulator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FSTEventAccumulator.m; sourceTree = "<group>"; };
54E9281E1F33950B00C1953E /* FSTIntegrationTestCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSTIntegrationTestCase.h; sourceTree = "<group>"; };
54E9282A1F339CAD00C1953E /* XCTestCase+Await.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "XCTestCase+Await.h"; sourceTree = "<group>"; };
- 54E9282B1F339CAD00C1953E /* XCTestCase+Await.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "XCTestCase+Await.m"; sourceTree = "<group>"; };
+ 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 = "<group>"; };
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; };
@@ -222,7 +324,6 @@
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 = "<group>"; };
6003F5B9195388D20070C39A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
- 61E1D8AF1FCF6AF500753285 /* StringViewTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = StringViewTests.mm; sourceTree = "<group>"; };
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 = "<group>"; };
75A6FE51C1A02DF38F62FAAD /* Pods_Firestore_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -230,12 +331,18 @@
8E002F4AD5D9B6197C940847 /* Firestore.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = Firestore.podspec; path = ../Firestore.podspec; sourceTree = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
+ AB356EF6200EA5EB0089B766 /* field_value_test.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = field_value_test.cc; sourceTree = "<group>"; };
+ AB380CF82019382300D97691 /* target_id_generator_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = target_id_generator_test.cc; sourceTree = "<group>"; };
+ 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 = "<group>"; };
+ 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 = "<group>"; };
+ 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 = "<group>"; };
+ AB71064B201FA60300344F18 /* database_id_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = database_id_test.cc; sourceTree = "<group>"; };
+ 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 = "<group>"; };
+ ABF6506B201131F8005F2C74 /* timestamp_test.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = timestamp_test.cc; sourceTree = "<group>"; };
B2FA635DF5D116A67A7441CD /* Pods_Firestore_IntegrationTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_IntegrationTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
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 = "<group>"; };
D3CC3DC5338DCAF43A211155 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = "<group>"; };
- D5B25292CED31B81FDED0411 /* FSTTestDispatchQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FSTTestDispatchQueue.m; sourceTree = "<group>"; };
D5B259DAA9149B80D6245B57 /* FSTTestDispatchQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSTTestDispatchQueue.h; sourceTree = "<group>"; };
- D5B25C0D4AADFCA3ADB883E4 /* FSTStreamTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FSTStreamTests.m; sourceTree = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
@@ -248,77 +355,11 @@
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 = "<group>"; };
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 = "<group>"; };
DE2EF0841F3D0B6E003D0CDC /* FSTTreeSortedDictionaryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FSTTreeSortedDictionaryTests.m; path = ../../third_party/Immutable/Tests/FSTTreeSortedDictionaryTests.m; sourceTree = "<group>"; };
- DE51B1631F0D48AC0013853F /* FSTEagerGarbageCollectorTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTEagerGarbageCollectorTests.m; sourceTree = "<group>"; };
- DE51B1641F0D48AC0013853F /* FSTLevelDBKeyTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTLevelDBKeyTests.mm; sourceTree = "<group>"; };
- DE51B1651F0D48AC0013853F /* FSTLevelDBLocalStoreTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTLevelDBLocalStoreTests.m; sourceTree = "<group>"; };
- DE51B1661F0D48AC0013853F /* FSTLevelDBMutationQueueTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTLevelDBMutationQueueTests.mm; sourceTree = "<group>"; };
- DE51B1671F0D48AC0013853F /* FSTLevelDBQueryCacheTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTLevelDBQueryCacheTests.m; sourceTree = "<group>"; };
- DE51B1681F0D48AC0013853F /* FSTLevelDBRemoteDocumentCacheTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTLevelDBRemoteDocumentCacheTests.mm; sourceTree = "<group>"; };
- DE51B1691F0D48AC0013853F /* FSTLocalSerializerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTLocalSerializerTests.m; sourceTree = "<group>"; };
- DE51B16A1F0D48AC0013853F /* FSTLocalStoreTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FSTLocalStoreTests.h; sourceTree = "<group>"; };
- DE51B16B1F0D48AC0013853F /* FSTLocalStoreTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTLocalStoreTests.m; sourceTree = "<group>"; };
- DE51B16C1F0D48AC0013853F /* FSTMemoryLocalStoreTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTMemoryLocalStoreTests.m; sourceTree = "<group>"; };
- DE51B16D1F0D48AC0013853F /* FSTMemoryMutationQueueTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTMemoryMutationQueueTests.m; sourceTree = "<group>"; };
- DE51B16E1F0D48AC0013853F /* FSTMemoryQueryCacheTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTMemoryQueryCacheTests.m; sourceTree = "<group>"; };
- DE51B16F1F0D48AC0013853F /* FSTMemoryRemoteDocumentCacheTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTMemoryRemoteDocumentCacheTests.m; sourceTree = "<group>"; };
- DE51B1701F0D48AC0013853F /* FSTMutationQueueTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FSTMutationQueueTests.h; sourceTree = "<group>"; };
- DE51B1711F0D48AC0013853F /* FSTMutationQueueTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTMutationQueueTests.m; sourceTree = "<group>"; };
- DE51B1721F0D48AC0013853F /* FSTPersistenceTestHelpers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FSTPersistenceTestHelpers.h; sourceTree = "<group>"; };
- DE51B1731F0D48AC0013853F /* FSTPersistenceTestHelpers.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTPersistenceTestHelpers.m; sourceTree = "<group>"; };
- DE51B1741F0D48AC0013853F /* FSTQueryCacheTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FSTQueryCacheTests.h; sourceTree = "<group>"; };
- DE51B1751F0D48AC0013853F /* FSTQueryCacheTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTQueryCacheTests.m; sourceTree = "<group>"; };
- DE51B1761F0D48AC0013853F /* FSTReferenceSetTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTReferenceSetTests.m; sourceTree = "<group>"; };
- DE51B1771F0D48AC0013853F /* FSTRemoteDocumentCacheTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FSTRemoteDocumentCacheTests.h; sourceTree = "<group>"; };
- DE51B1781F0D48AC0013853F /* FSTRemoteDocumentCacheTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTRemoteDocumentCacheTests.m; sourceTree = "<group>"; };
- DE51B1791F0D48AC0013853F /* FSTRemoteDocumentChangeBufferTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTRemoteDocumentChangeBufferTests.m; sourceTree = "<group>"; };
- DE51B17A1F0D48AC0013853F /* FSTWriteGroupTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTWriteGroupTests.mm; sourceTree = "<group>"; };
- DE51B17C1F0D48AC0013853F /* FSTDatabaseIDTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTDatabaseIDTests.m; sourceTree = "<group>"; };
- DE51B17D1F0D48AC0013853F /* FSTDocumentKeyTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTDocumentKeyTests.m; sourceTree = "<group>"; };
- DE51B17E1F0D48AC0013853F /* FSTDocumentSetTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTDocumentSetTests.m; sourceTree = "<group>"; };
- DE51B17F1F0D48AC0013853F /* FSTDocumentTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTDocumentTests.m; sourceTree = "<group>"; };
- DE51B1801F0D48AC0013853F /* FSTFieldValueTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTFieldValueTests.m; sourceTree = "<group>"; };
- DE51B1811F0D48AC0013853F /* FSTMutationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTMutationTests.m; sourceTree = "<group>"; };
- DE51B1821F0D48AC0013853F /* FSTPathTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTPathTests.m; sourceTree = "<group>"; };
- DE51B1841F0D48AC0013853F /* FIRGeoPointTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIRGeoPointTests.m; sourceTree = "<group>"; };
- DE51B1861F0D48AC0013853F /* FSTAssertTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTAssertTests.m; sourceTree = "<group>"; };
- DE51B1871F0D48AC0013853F /* FSTComparisonTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTComparisonTests.m; sourceTree = "<group>"; };
DE51B1881F0D48AC0013853F /* FSTHelpers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FSTHelpers.h; sourceTree = "<group>"; };
- DE51B1891F0D48AC0013853F /* FSTHelpers.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTHelpers.m; sourceTree = "<group>"; };
- DE51B1941F0D48AC0013853F /* FSTLevelDBSpecTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTLevelDBSpecTests.m; sourceTree = "<group>"; };
- DE51B1951F0D48AC0013853F /* FSTMemorySpecTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTMemorySpecTests.m; sourceTree = "<group>"; };
DE51B1961F0D48AC0013853F /* FSTMockDatastore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FSTMockDatastore.h; sourceTree = "<group>"; };
- DE51B1971F0D48AC0013853F /* FSTMockDatastore.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTMockDatastore.m; sourceTree = "<group>"; };
DE51B1981F0D48AC0013853F /* FSTSpecTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FSTSpecTests.h; sourceTree = "<group>"; };
- DE51B1991F0D48AC0013853F /* FSTSpecTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTSpecTests.m; sourceTree = "<group>"; };
DE51B19A1F0D48AC0013853F /* FSTSyncEngineTestDriver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FSTSyncEngineTestDriver.h; sourceTree = "<group>"; };
- DE51B19B1F0D48AC0013853F /* FSTSyncEngineTestDriver.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTSyncEngineTestDriver.m; sourceTree = "<group>"; };
DE51B1A71F0D48AC0013853F /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
- DE51B1A91F0D48AC0013853F /* FSTDatabaseInfoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTDatabaseInfoTests.m; sourceTree = "<group>"; };
- DE51B1AA1F0D48AC0013853F /* FSTEventManagerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTEventManagerTests.m; sourceTree = "<group>"; };
- DE51B1AB1F0D48AC0013853F /* FSTQueryListenerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTQueryListenerTests.m; sourceTree = "<group>"; };
- DE51B1AC1F0D48AC0013853F /* FSTQueryTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTQueryTests.m; sourceTree = "<group>"; };
- DE51B1AD1F0D48AC0013853F /* FSTSyncEngine+Testing.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FSTSyncEngine+Testing.h"; sourceTree = "<group>"; };
- DE51B1AE1F0D48AC0013853F /* FSTTargetIDGeneratorTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTTargetIDGeneratorTests.m; sourceTree = "<group>"; };
- DE51B1AF1F0D48AC0013853F /* FSTTimestampTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTTimestampTests.m; sourceTree = "<group>"; };
- DE51B1B01F0D48AC0013853F /* FSTViewSnapshotTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTViewSnapshotTest.m; sourceTree = "<group>"; };
- DE51B1B11F0D48AC0013853F /* FSTViewTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTViewTests.m; sourceTree = "<group>"; };
- DE51B1B31F0D48AC0013853F /* FSTDatastoreTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTDatastoreTests.m; sourceTree = "<group>"; };
- DE51B1B41F0D48AC0013853F /* FSTRemoteEventTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTRemoteEventTests.m; sourceTree = "<group>"; };
- DE51B1B61F0D48AC0013853F /* FSTSerializerBetaTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTSerializerBetaTests.m; sourceTree = "<group>"; };
- DE51B1B81F0D48AC0013853F /* FSTWatchChange+Testing.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FSTWatchChange+Testing.h"; sourceTree = "<group>"; };
- DE51B1B91F0D48AC0013853F /* FSTWatchChange+Testing.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "FSTWatchChange+Testing.m"; sourceTree = "<group>"; };
- DE51B1BA1F0D48AC0013853F /* FSTWatchChangeTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTWatchChangeTests.m; sourceTree = "<group>"; };
- DE51B1BD1F0D48AC0013853F /* FIRCursorTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIRCursorTests.m; sourceTree = "<group>"; };
- DE51B1BE1F0D48AC0013853F /* FIRDatabaseTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIRDatabaseTests.m; sourceTree = "<group>"; };
- DE51B1BF1F0D48AC0013853F /* FIRFieldsTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIRFieldsTests.m; sourceTree = "<group>"; };
- DE51B1C01F0D48AC0013853F /* FIRListenerRegistrationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIRListenerRegistrationTests.m; sourceTree = "<group>"; };
- DE51B1C11F0D48AC0013853F /* FIRQueryTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIRQueryTests.m; sourceTree = "<group>"; };
- DE51B1C21F0D48AC0013853F /* FIRServerTimestampTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIRServerTimestampTests.m; sourceTree = "<group>"; };
- DE51B1C31F0D48AC0013853F /* FIRTypeTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIRTypeTests.m; sourceTree = "<group>"; };
- DE51B1C41F0D48AC0013853F /* FSTDatastoreTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTDatastoreTests.m; sourceTree = "<group>"; };
- DE51B1C51F0D48AC0013853F /* FSTSmokeTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTSmokeTests.m; sourceTree = "<group>"; };
- DE51B1C61F0D48AC0013853F /* FSTTransactionTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSTTransactionTests.m; sourceTree = "<group>"; };
- DEFE0F471F1F960A0071599A /* FIRWriteBatchTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRWriteBatchTests.m; sourceTree = "<group>"; };
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 = "<group>"; };
/* End PBXFileReference section */
@@ -371,8 +412,15 @@
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 = "<group>";
@@ -380,19 +428,23 @@
54764FAC1FAA0C390085E60A /* GoogleTests */ = {
isa = PBXGroup;
children = (
- 54764FAD1FAA0C650085E60A /* Port */,
+ AB380CF7201937B800D97691 /* core */,
+ 54EB764B202277970088B8F3 /* immutable */,
+ AB356EF5200E9D1A0089B766 /* model */,
54740A561FC913EB00713A1A /* util */,
54764FAE1FAA21B90085E60A /* FSTGoogleTestTests.mm */,
+ AB7BAB332012B519001E0872 /* geo_point_test.cc */,
);
name = GoogleTests;
sourceTree = "<group>";
};
- 54764FAD1FAA0C650085E60A /* Port */ = {
+ 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 = "<group>";
};
6003F581195388D10070C39A = {
@@ -512,6 +564,26 @@
name = Pods;
sourceTree = "<group>";
};
+ AB356EF5200E9D1A0089B766 /* model */ = {
+ isa = PBXGroup;
+ children = (
+ AB71064B201FA60300344F18 /* database_id_test.cc */,
+ AB356EF6200EA5EB0089B766 /* field_value_test.cc */,
+ ABF6506B201131F8005F2C74 /* timestamp_test.cc */,
+ );
+ name = model;
+ path = ../../core/test/firebase/firestore/model;
+ sourceTree = "<group>";
+ };
+ AB380CF7201937B800D97691 /* core */ = {
+ isa = PBXGroup;
+ children = (
+ AB380CF82019382300D97691 /* target_id_generator_test.cc */,
+ );
+ name = core;
+ path = ../../core/test/firebase/firestore/core;
+ sourceTree = "<group>";
+ };
DE0761E51F2FE611003233AF /* SwiftBuildTest */ = {
isa = PBXGroup;
children = (
@@ -537,31 +609,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 = "<group>";
@@ -569,13 +642,13 @@
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 */,
+ 5492E0B42021555100B64F25 /* FSTDatabaseIDTests.mm */,
+ 5492E0B22021555000B64F25 /* FSTDocumentKeyTests.mm */,
+ 5492E0B32021555100B64F25 /* FSTDocumentSetTests.mm */,
+ 5492E0B62021555100B64F25 /* FSTDocumentTests.mm */,
+ 5492E0B82021555100B64F25 /* FSTFieldValueTests.mm */,
+ 5492E0B72021555100B64F25 /* FSTMutationTests.mm */,
+ 5492E0B52021555100B64F25 /* FSTPathTests.mm */,
);
path = Model;
sourceTree = "<group>";
@@ -583,7 +656,17 @@
DE51B1831F0D48AC0013853F /* API */ = {
isa = PBXGroup;
children = (
- DE51B1841F0D48AC0013853F /* FIRGeoPointTests.m */,
+ 5492E045202154AA00B64F25 /* FIRCollectionReferenceTests.mm */,
+ 5492E049202154AA00B64F25 /* FIRDocumentReferenceTests.mm */,
+ 5492E04B202154AA00B64F25 /* FIRDocumentSnapshotTests.mm */,
+ 5492E04C202154AA00B64F25 /* FIRFieldPathTests.mm */,
+ 5492E04A202154AA00B64F25 /* FIRFieldValueTests.mm */,
+ 5492E048202154AA00B64F25 /* FIRGeoPointTests.mm */,
+ 5492E04F202154AA00B64F25 /* FIRQuerySnapshotTests.mm */,
+ 5492E046202154AA00B64F25 /* FIRQueryTests.mm */,
+ 5492E04D202154AA00B64F25 /* FIRSnapshotMetadataTests.mm */,
+ 5492E047202154AA00B64F25 /* FSTAPIHelpers.h */,
+ 5492E04E202154AA00B64F25 /* FSTAPIHelpers.mm */,
);
path = API;
sourceTree = "<group>";
@@ -591,18 +674,17 @@
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 */,
+ 5492E0362021401E00B64F25 /* FSTTestDispatchQueue.mm */,
+ 54E9282A1F339CAD00C1953E /* XCTestCase+Await.h */,
+ 5492E0372021401E00B64F25 /* XCTestCase+Await.mm */,
);
path = Util;
sourceTree = "<group>";
@@ -610,14 +692,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;
@@ -645,15 +727,14 @@
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 */,
+ 5492E05F202154B900B64F25 /* FSTDatabaseInfoTests.mm */,
+ 5492E060202154B900B64F25 /* FSTEventManagerTests.mm */,
+ 5492E05D202154B900B64F25 /* FSTQueryListenerTests.mm */,
+ 5492E061202154B900B64F25 /* FSTQueryTests.mm */,
+ 5492E05A202154B800B64F25 /* FSTSyncEngine+Testing.h */,
+ 5492E05B202154B800B64F25 /* FSTTimestampTests.mm */,
+ 5492E05C202154B800B64F25 /* FSTViewSnapshotTest.mm */,
+ 5492E05E202154B900B64F25 /* FSTViewTests.mm */,
);
path = Core;
sourceTree = "<group>";
@@ -661,12 +742,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 = "<group>";
@@ -674,13 +755,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 = "<group>";
@@ -688,26 +768,19 @@
DE51B1BC1F0D48AC0013853F /* API */ = {
isa = PBXGroup;
children = (
- 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 */,
+ 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 = "<group>";
};
- DE51B1C71F0D48AC0013853F /* Util */ = {
- isa = PBXGroup;
- children = (
- );
- path = Util;
- sourceTree = "<group>";
- };
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -917,8 +990,7 @@
inputPaths = (
"${SRCROOT}/Pods/Target Support Files/Pods-SwiftBuildTest/Pods-SwiftBuildTest-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/BoringSSL/openssl.framework",
- "${BUILT_PRODUCTS_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework",
- "${BUILT_PRODUCTS_DIR}/GoogleToolboxForMac-f0850809/GoogleToolboxForMac.framework",
+ "${BUILT_PRODUCTS_DIR}/GoogleToolboxForMac-Defines-NSData+zlib/GoogleToolboxForMac.framework",
"${BUILT_PRODUCTS_DIR}/Protobuf/Protobuf.framework",
"${BUILT_PRODUCTS_DIR}/gRPC/GRPCClient.framework",
"${BUILT_PRODUCTS_DIR}/gRPC-Core/grpc.framework",
@@ -930,7 +1002,6 @@
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",
@@ -1160,66 +1231,86 @@
buildActionMask = 2147483647;
files = (
DE2EF0881F3D0B6E003D0CDC /* FSTTreeSortedDictionaryTests.m in Sources */,
- DE51B1FD1F0D492C0013853F /* FSTSpecTests.m in Sources */,
- DE51B2001F0D493A0013853F /* FSTComparisonTests.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 */,
- DE51B2011F0D493E0013853F /* FSTHelpers.m in Sources */,
- DE51B1F61F0D491B0013853F /* FSTSerializerBetaTests.m in Sources */,
- DE51B1F01F0D49140013853F /* FSTFieldValueTests.m 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 */,
+ 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 */,
+ 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 */,
5491BC721FB44593008B3588 /* FSTIntegrationTestCase.mm 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 */,
+ 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 */,
- DE51B1EF1F0D49140013853F /* FSTDocumentTests.m in Sources */,
- DE51B1DC1F0D490D0013853F /* FSTLocalSerializerTests.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 */,
+ 548DB927200D590300E00ABC /* assert_test.cc in Sources */,
+ 5492E0A62021552D00B64F25 /* FSTPersistenceTestHelpers.mm in Sources */,
+ 5492E066202154B900B64F25 /* FSTDatabaseInfoTests.mm in Sources */,
+ 5492E0A12021552D00B64F25 /* FSTMemoryLocalStoreTests.mm in Sources */,
+ 5436F32420008FAD006E51E3 /* string_printf_test.cc 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 */,
- 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 */,
+ 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 */,
+ 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 */,
+ 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 */,
+ 5492E0BB2021555100B64F25 /* FSTDatabaseIDTests.mm in Sources */,
+ 5492E0C82021557E00B64F25 /* FSTDatastoreTests.mm in Sources */,
+ 5492E065202154B900B64F25 /* FSTViewTests.mm in Sources */,
+ 5492E03C2021401F00B64F25 /* XCTestCase+Await.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 */,
- DE51B1E31F0D490D0013853F /* FSTPersistenceTestHelpers.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 */,
- DE51B1F21F0D49140013853F /* FSTPathTests.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 */,
+ ABF6506C201131F8005F2C74 /* timestamp_test.cc in Sources */,
+ 5492E0AE2021552D00B64F25 /* FSTLevelDBQueryCacheTests.mm in Sources */,
+ 5492E059202154AB00B64F25 /* FIRQuerySnapshotTests.mm in Sources */,
+ 5492E050202154AA00B64F25 /* FIRCollectionReferenceTests.mm 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 */,
+ 5492E062202154B900B64F25 /* FSTTimestampTests.mm in Sources */,
+ 5492E052202154AB00B64F25 /* FIRGeoPointTests.mm in Sources */,
+ 5492E0C72021557E00B64F25 /* FSTSerializerBetaTests.mm in Sources */,
+ 5492E03520213FFC00B64F25 /* FSTSpecTests.mm in Sources */,
+ 5492E03B2021401F00B64F25 /* FSTTestDispatchQueue.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;
};
@@ -1227,24 +1318,24 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- DE03B2EE1F214BAA00A30B9C /* FIRWriteBatchTests.m in Sources */,
- DE03B2F01F214BAA00A30B9C /* FIRDatabaseTests.m 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 */,
+ 5492E0432021441E00B64F25 /* FSTTestDispatchQueue.mm in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1450,7 +1541,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";
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}";
@@ -1481,7 +1574,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";
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}";
@@ -1512,6 +1607,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 = (
@@ -1550,6 +1648,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 = (
diff --git a/Firestore/Example/Podfile b/Firestore/Example/Podfile
index 89af74f..bd57e21 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.7.0'
+pod 'Firebase/Core', '4.8.2'
use_frameworks!
platform :ios, '8.0'
diff --git a/Firestore/Example/SwiftBuildTest/main.swift b/Firestore/Example/SwiftBuildTest/main.swift
index bea8b56..260735b 100644
--- a/Firestore/Example/SwiftBuildTest/main.swift
+++ b/Firestore/Example/SwiftBuildTest/main.swift
@@ -27,6 +27,8 @@ func main() {
writeDocument(at: documentRef);
+ writeDocuments(at: documentRef, database: db);
+
addDocument(to: collectionRef);
readDocument(at: documentRef);
@@ -37,6 +39,8 @@ func main() {
listenToDocuments(matching: query);
+ enableDisableNetwork(database: db);
+
types();
}
@@ -129,6 +133,47 @@ func writeDocument(at docRef: DocumentReference) {
}
}
+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
+ }
+ }
+}
+
+func writeDocuments(at docRef: DocumentReference, database db: Firestore) {
+ var batch: WriteBatch;
+
+ 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 = 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
+ if let error = error {
+ print("Uh oh! \(error)");
+ return;
+ }
+ print("Batch write callback complete!");
+ }
+ print("Batch write with completion complete!");
+}
+
func addDocument(to collectionRef: CollectionReference) {
collectionRef.addDocument(data: ["foo": 42]);
@@ -141,11 +186,20 @@ func readDocument(at docRef: DocumentReference) {
// Trailing closure syntax.
docRef.getDocument() { document, error in
if let document = document {
- // NOTE that document is nullable.
- let data = document.data();
+ // Note that both document and document.data() is nullable.
+ if let data = document.data() {
print("Read document: \(data)")
-
- // Fields are read via subscript notation.
+ }
+ 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)")
}
@@ -181,24 +235,27 @@ func readDocuments(matching query: Query) {
func listenToDocument(at docRef: DocumentReference) {
- let listener = docRef.addSnapshotListener() { document, error in
- if let error = error {
- print("Uh oh! Listen canceled: \(error)")
- return
- }
+ let listener = docRef.addSnapshotListener() { document, error in
+ if let error = error {
+ print("Uh oh! Listen canceled: \(error)")
+ return
+ }
- if let document = document {
- print("Current document: \(document.data())");
- if (document.metadata.isFromCache) {
- print("From Cache")
- } else {
- print("From Server")
- }
- }
+ if let document = document {
+ // Note that document.data() is nullable.
+ if let data : [String:Any] = document.data() {
+ print("Current document: \(data)");
+ }
+ if document.metadata.isFromCache {
+ print("From Cache")
+ } else {
+ print("From Server")
+ }
}
+ }
- // Unsubscribe.
- listener.remove();
+ // Unsubscribe.
+ listener.remove();
}
func listenToDocuments(matching query: Query) {
@@ -215,7 +272,9 @@ func listenToDocuments(matching query: Query) {
// TODO(mikelehen): Figure out how to make "for..in" syntax work
// directly on documentSet.
for document in snap.documents {
- print("Doc: ", document.data())
+ // Note that document.data() is not nullable.
+ let data : [String:Any] = document.data()
+ print("Doc: ", data)
}
}
}
@@ -258,7 +317,7 @@ func transactions() {
let balanceA = try transaction.getDocument(accA)["balance"] as! Double
let balanceB = try transaction.getDocument(accB)["balance"] as! Double
- if (balanceA < amount) {
+ if balanceA < amount {
errorPointer?.pointee = NSError(domain: "Foo", code: 123, userInfo: nil)
return nil
}
diff --git a/Firestore/Example/Tests/API/FIRCollectionReferenceTests.mm b/Firestore/Example/Tests/API/FIRCollectionReferenceTests.mm
new file mode 100644
index 0000000..547078f
--- /dev/null
+++ b/Firestore/Example/Tests/API/FIRCollectionReferenceTests.mm
@@ -0,0 +1,43 @@
+/*
+ * 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 <FirebaseFirestore/FIRCollectionReference.h>
+
+#import <XCTest/XCTest.h>
+
+#import "Firestore/Example/Tests/API/FSTAPIHelpers.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface FIRCollectionReferenceTests : XCTestCase
+@end
+
+@implementation FIRCollectionReferenceTests
+
+- (void)testEquals {
+ FIRCollectionReference *referenceFoo = FSTTestCollectionRef(@"foo");
+ FIRCollectionReference *referenceFooDup = FSTTestCollectionRef(@"foo");
+ FIRCollectionReference *referenceBar = FSTTestCollectionRef(@"bar");
+ XCTAssertEqualObjects(referenceFoo, referenceFooDup);
+ XCTAssertNotEqualObjects(referenceFoo, referenceBar);
+
+ XCTAssertEqual([referenceFoo hash], [referenceFooDup hash]);
+ XCTAssertNotEqual([referenceFoo hash], [referenceBar hash]);
+}
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Firestore/Example/Tests/API/FIRDocumentReferenceTests.mm b/Firestore/Example/Tests/API/FIRDocumentReferenceTests.mm
new file mode 100644
index 0000000..cc2b431
--- /dev/null
+++ b/Firestore/Example/Tests/API/FIRDocumentReferenceTests.mm
@@ -0,0 +1,43 @@
+/*
+ * 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 <FirebaseFirestore/FIRDocumentReference.h>
+
+#import <XCTest/XCTest.h>
+
+#import "Firestore/Example/Tests/API/FSTAPIHelpers.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface FIRDocumentReferenceTests : XCTestCase
+@end
+
+@implementation FIRDocumentReferenceTests
+
+- (void)testEquals {
+ FIRDocumentReference *referenceFoo = FSTTestDocRef(@"rooms/foo");
+ FIRDocumentReference *referenceFooDup = FSTTestDocRef(@"rooms/foo");
+ FIRDocumentReference *referenceBar = FSTTestDocRef(@"rooms/bar");
+ XCTAssertEqualObjects(referenceFoo, referenceFooDup);
+ XCTAssertNotEqualObjects(referenceFoo, referenceBar);
+
+ XCTAssertEqual([referenceFoo hash], [referenceFooDup hash]);
+ XCTAssertNotEqual([referenceFoo hash], [referenceBar hash]);
+}
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Firestore/Example/Tests/API/FIRDocumentSnapshotTests.mm b/Firestore/Example/Tests/API/FIRDocumentSnapshotTests.mm
new file mode 100644
index 0000000..677d385
--- /dev/null
+++ b/Firestore/Example/Tests/API/FIRDocumentSnapshotTests.mm
@@ -0,0 +1,59 @@
+/*
+ * 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 <FirebaseFirestore/FIRDocumentSnapshot.h>
+
+#import <XCTest/XCTest.h>
+
+#import "Firestore/Example/Tests/API/FSTAPIHelpers.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface FIRDocumentSnapshotTests : XCTestCase
+@end
+
+@implementation FIRDocumentSnapshotTests
+
+- (void)testEquals {
+ FIRDocumentSnapshot *base = FSTTestDocSnapshot(@"rooms/foo", 1, @{ @"a" : @1 }, NO, NO);
+ FIRDocumentSnapshot *baseDup = FSTTestDocSnapshot(@"rooms/foo", 1, @{ @"a" : @1 }, NO, NO);
+ FIRDocumentSnapshot *nilData = FSTTestDocSnapshot(@"rooms/foo", 1, nil, NO, NO);
+ FIRDocumentSnapshot *nilDataDup = FSTTestDocSnapshot(@"rooms/foo", 1, nil, NO, NO);
+ 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);
+ FIRDocumentSnapshot *fromCache = FSTTestDocSnapshot(@"rooms/bar", 1, @{ @"a" : @1 }, NO, YES);
+ XCTAssertEqualObjects(base, baseDup);
+ XCTAssertEqualObjects(nilData, nilDataDup);
+ XCTAssertNotEqualObjects(base, nilData);
+ XCTAssertNotEqualObjects(nilData, base);
+ XCTAssertNotEqualObjects(base, differentPath);
+ XCTAssertNotEqualObjects(base, differentData);
+ XCTAssertNotEqualObjects(base, hasMutations);
+ XCTAssertNotEqualObjects(base, fromCache);
+
+ XCTAssertEqual([base hash], [baseDup hash]);
+ XCTAssertEqual([nilData hash], [nilDataDup hash]);
+ XCTAssertNotEqual([base hash], [nilData hash]);
+ XCTAssertNotEqual([base hash], [differentPath hash]);
+ XCTAssertNotEqual([base hash], [differentData hash]);
+ XCTAssertNotEqual([base hash], [hasMutations hash]);
+ XCTAssertNotEqual([base hash], [fromCache hash]);
+}
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Firestore/Example/Tests/API/FIRFieldPathTests.mm b/Firestore/Example/Tests/API/FIRFieldPathTests.mm
new file mode 100644
index 0000000..679ea89
--- /dev/null
+++ b/Firestore/Example/Tests/API/FIRFieldPathTests.mm
@@ -0,0 +1,46 @@
+/*
+ * 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 <FirebaseFirestore/FIRFieldPath.h>
+
+#import <XCTest/XCTest.h>
+
+#import "Firestore/Source/API/FIRFieldPath+Internal.h"
+#import "Firestore/Source/Model/FSTPath.h"
+
+#import "Firestore/Example/Tests/Util/FSTHelpers.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface FIRFieldPathTests : XCTestCase
+@end
+
+@implementation FIRFieldPathTests
+
+- (void)testEquals {
+ FIRFieldPath *foo = [[FIRFieldPath alloc] initPrivate:FSTTestFieldPath(@"foo.ooo.oooo")];
+ FIRFieldPath *fooDup = [[FIRFieldPath alloc] initPrivate:FSTTestFieldPath(@"foo.ooo.oooo")];
+ FIRFieldPath *bar = [[FIRFieldPath alloc] initPrivate:FSTTestFieldPath(@"baa.aaa.aaar")];
+ XCTAssertEqualObjects(foo, fooDup);
+ XCTAssertNotEqualObjects(foo, bar);
+
+ XCTAssertEqual([foo hash], [fooDup hash]);
+ XCTAssertNotEqual([foo hash], [bar hash]);
+}
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Firestore/Example/Tests/API/FIRFieldValueTests.mm b/Firestore/Example/Tests/API/FIRFieldValueTests.mm
new file mode 100644
index 0000000..575dfee
--- /dev/null
+++ b/Firestore/Example/Tests/API/FIRFieldValueTests.mm
@@ -0,0 +1,46 @@
+/*
+ * 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 <FirebaseFirestore/FIRFieldValue.h>
+
+#import <XCTest/XCTest.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface FIRFieldValueTests : XCTestCase
+@end
+
+@implementation FIRFieldValueTests
+
+- (void)testEquals {
+ FIRFieldValue *deleted = [FIRFieldValue fieldValueForDelete];
+ FIRFieldValue *deleteDup = [FIRFieldValue fieldValueForDelete];
+ FIRFieldValue *serverTimestamp = [FIRFieldValue fieldValueForServerTimestamp];
+ FIRFieldValue *serverTimestampDup = [FIRFieldValue fieldValueForServerTimestamp];
+ XCTAssertEqualObjects(deleted, deleteDup);
+ XCTAssertNotEqualObjects(deleted, nil);
+ XCTAssertEqualObjects(serverTimestamp, serverTimestampDup);
+ XCTAssertNotEqualObjects(serverTimestamp, nil);
+ XCTAssertNotEqualObjects(deleted, serverTimestamp);
+
+ XCTAssertEqual([deleted hash], [deleteDup hash]);
+ XCTAssertEqual([serverTimestamp hash], [serverTimestamp hash]);
+ XCTAssertNotEqual([deleted hash], [serverTimestamp hash]);
+}
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Firestore/Example/Tests/API/FIRGeoPointTests.m b/Firestore/Example/Tests/API/FIRGeoPointTests.mm
index b505de0..4de80a8 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 <FirebaseFirestore/FIRGeoPoint.h>
#import <XCTest/XCTest.h>
@@ -28,16 +28,17 @@ NS_ASSUME_NONNULL_BEGIN
@implementation FIRGeoPointTests
- (void)testEquals {
- XCTAssertEqualObjects([[FIRGeoPoint alloc] initWithLatitude:0 longitude:0],
- [[FIRGeoPoint alloc] initWithLatitude:0 longitude:0]);
- XCTAssertEqualObjects([[FIRGeoPoint alloc] initWithLatitude:1.23 longitude:4.56],
- [[FIRGeoPoint alloc] initWithLatitude:1.23 longitude:4.56]);
- XCTAssertNotEqualObjects([[FIRGeoPoint alloc] initWithLatitude:0 longitude:0],
- [[FIRGeoPoint alloc] initWithLatitude:1 longitude:0]);
- XCTAssertNotEqualObjects([[FIRGeoPoint alloc] initWithLatitude:0 longitude:0],
- [[FIRGeoPoint alloc] initWithLatitude:0 longitude:1]);
- XCTAssertNotEqualObjects([[FIRGeoPoint alloc] initWithLatitude:0 longitude:0],
- [[NSObject alloc] init]);
+ FIRGeoPoint *foo = FSTTestGeoPoint(1.23, 4.56);
+ FIRGeoPoint *fooDup = FSTTestGeoPoint(1.23, 4.56);
+ FIRGeoPoint *differentLatitude = FSTTestGeoPoint(1.23, 0);
+ FIRGeoPoint *differentLongitude = FSTTestGeoPoint(0, 4.56);
+ XCTAssertEqualObjects(foo, fooDup);
+ XCTAssertNotEqualObjects(foo, differentLatitude);
+ XCTAssertNotEqualObjects(foo, differentLongitude);
+
+ XCTAssertEqual([foo hash], [fooDup hash]);
+ XCTAssertNotEqual([foo hash], [differentLatitude hash]);
+ XCTAssertNotEqual([foo hash], [differentLongitude hash]);
}
- (void)testComparison {
diff --git a/Firestore/Example/Tests/API/FIRQuerySnapshotTests.mm b/Firestore/Example/Tests/API/FIRQuerySnapshotTests.mm
new file mode 100644
index 0000000..067425a
--- /dev/null
+++ b/Firestore/Example/Tests/API/FIRQuerySnapshotTests.mm
@@ -0,0 +1,58 @@
+/*
+ * 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 <FirebaseFirestore/FIRQuerySnapshot.h>
+
+#import <XCTest/XCTest.h>
+
+#import "Firestore/Source/Model/FSTPath.h"
+
+#import "Firestore/Example/Tests/API/FSTAPIHelpers.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface FIRQuerySnapshotTests : XCTestCase
+@end
+
+@implementation FIRQuerySnapshotTests
+
+- (void)testEquals {
+ FIRQuerySnapshot *foo = FSTTestQuerySnapshot(@"foo", @{}, @{ @"a" : @{@"a" : @1} }, YES, NO);
+ FIRQuerySnapshot *fooDup = FSTTestQuerySnapshot(@"foo", @{}, @{ @"a" : @{@"a" : @1} }, YES, NO);
+ FIRQuerySnapshot *differentPath = FSTTestQuerySnapshot(@"bar", @{},
+ @{ @"a" : @{@"a" : @1} }, YES, NO);
+ FIRQuerySnapshot *differentDoc = FSTTestQuerySnapshot(@"foo",
+ @{ @"a" : @{@"b" : @1} }, @{}, YES, NO);
+ FIRQuerySnapshot *noPendingWrites = FSTTestQuerySnapshot(@"foo", @{},
+ @{ @"a" : @{@"a" : @1} }, NO, NO);
+ FIRQuerySnapshot *fromCache = FSTTestQuerySnapshot(@"foo", @{},
+ @{ @"a" : @{@"a" : @1} }, YES, YES);
+ XCTAssertEqualObjects(foo, fooDup);
+ XCTAssertNotEqualObjects(foo, differentPath);
+ XCTAssertNotEqualObjects(foo, differentDoc);
+ XCTAssertNotEqualObjects(foo, noPendingWrites);
+ XCTAssertNotEqualObjects(foo, fromCache);
+
+ XCTAssertEqual([foo hash], [fooDup hash]);
+ XCTAssertNotEqual([foo hash], [differentPath hash]);
+ XCTAssertNotEqual([foo hash], [differentDoc hash]);
+ XCTAssertNotEqual([foo hash], [noPendingWrites hash]);
+ XCTAssertNotEqual([foo hash], [fromCache hash]);
+}
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Firestore/Example/Tests/API/FIRQueryTests.mm b/Firestore/Example/Tests/API/FIRQueryTests.mm
new file mode 100644
index 0000000..83f90be
--- /dev/null
+++ b/Firestore/Example/Tests/API/FIRQueryTests.mm
@@ -0,0 +1,85 @@
+/*
+ * 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 <FirebaseFirestore/FIRQuery.h>
+
+#import <XCTest/XCTest.h>
+
+#import "Firestore/Source/API/FIRQuery+Internal.h"
+#import "Firestore/Source/Core/FSTQuery.h"
+#import "Firestore/Source/Model/FSTPath.h"
+
+#import "Firestore/Example/Tests/API/FSTAPIHelpers.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface FIRQueryTests : XCTestCase
+@end
+
+@implementation FIRQueryTests
+
+- (void)testEquals {
+ FIRFirestore *firestore = FSTTestFirestore();
+ FIRQuery *queryFoo = [FIRQuery referenceWithQuery:FSTTestQuery(@"foo") firestore:firestore];
+ FIRQuery *queryFooDup = [FIRQuery referenceWithQuery:FSTTestQuery(@"foo") firestore:firestore];
+ FIRQuery *queryBar = [FIRQuery referenceWithQuery:FSTTestQuery(@"bar") firestore:firestore];
+ XCTAssertEqualObjects(queryFoo, queryFooDup);
+ XCTAssertNotEqualObjects(queryFoo, queryBar);
+ XCTAssertEqualObjects([queryFoo queryWhereField:@"f" isEqualTo:@1],
+ [queryFoo queryWhereField:@"f" isEqualTo:@1]);
+ XCTAssertNotEqualObjects([queryFoo queryWhereField:@"f" isEqualTo:@1],
+ [queryFoo queryWhereField:@"f" isEqualTo:@2]);
+
+ XCTAssertEqual([queryFoo hash], [queryFooDup hash]);
+ XCTAssertNotEqual([queryFoo hash], [queryBar hash]);
+ XCTAssertEqual([[queryFoo queryWhereField:@"f" isEqualTo:@1] hash],
+ [[queryFoo queryWhereField:@"f" isEqualTo:@1] hash]);
+ XCTAssertNotEqual([[queryFoo queryWhereField:@"f" isEqualTo:@1] hash],
+ [[queryFoo queryWhereField:@"f" isEqualTo:@2] hash]);
+}
+
+- (void)testFilteringWithPredicate {
+ FIRFirestore *firestore = FSTTestFirestore();
+ FIRQuery *query = [FIRQuery referenceWithQuery:FSTTestQuery(@"foo") firestore:firestore];
+ FIRQuery *query1 = [query queryWhereField:@"f" isLessThanOrEqualTo:@1];
+ FIRQuery *query2 = [query queryFilteredUsingPredicate:[NSPredicate predicateWithFormat:@"f<=1"]];
+ FIRQuery *query3 =
+ [[query queryWhereField:@"f1" isLessThan:@2] queryWhereField:@"f2" isEqualTo:@3];
+ FIRQuery *query4 =
+ [query queryFilteredUsingPredicate:[NSPredicate predicateWithFormat:@"f1<2 && f2==3"]];
+ FIRQuery *query5 =
+ [[[[[query queryWhereField:@"f1" isLessThan:@2] queryWhereField:@"f2" isEqualTo:@3]
+ queryWhereField:@"f1"
+ isLessThanOrEqualTo:@"four"] queryWhereField:@"f1"
+ isGreaterThanOrEqualTo:@"five"] queryWhereField:@"f1"
+ isGreaterThan:@6];
+ FIRQuery *query6 = [query
+ queryFilteredUsingPredicate:
+ [NSPredicate predicateWithFormat:@"f1<2 && f2==3 && f1<='four' && f1>='five' && f1>6"]];
+ FIRQuery *query7 = [query
+ queryFilteredUsingPredicate:
+ [NSPredicate predicateWithFormat:@"2>f1 && 3==f2 && 'four'>=f1 && 'five'<=f1 && 6<f1"]];
+ XCTAssertEqualObjects(query1, query2);
+ XCTAssertNotEqualObjects(query2, query3);
+ XCTAssertEqualObjects(query3, query4);
+ XCTAssertNotEqualObjects(query4, query5);
+ XCTAssertEqualObjects(query5, query6);
+ XCTAssertEqualObjects(query6, query7);
+}
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Firestore/Example/Tests/API/FIRSnapshotMetadataTests.mm b/Firestore/Example/Tests/API/FIRSnapshotMetadataTests.mm
new file mode 100644
index 0000000..a4d321b
--- /dev/null
+++ b/Firestore/Example/Tests/API/FIRSnapshotMetadataTests.mm
@@ -0,0 +1,52 @@
+/*
+ * 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 <FirebaseFirestore/FIRSnapshotMetadata.h>
+
+#import <XCTest/XCTest.h>
+
+#import "Firestore/Source/API/FIRSnapshotMetadata+Internal.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface FIRSnapshotMetadataTests : XCTestCase
+@end
+
+@implementation FIRSnapshotMetadataTests
+
+- (void)testEquals {
+ FIRSnapshotMetadata *foo =
+ [FIRSnapshotMetadata snapshotMetadataWithPendingWrites:YES fromCache:YES];
+ FIRSnapshotMetadata *fooDup =
+ [FIRSnapshotMetadata snapshotMetadataWithPendingWrites:YES fromCache:YES];
+ FIRSnapshotMetadata *bar =
+ [FIRSnapshotMetadata snapshotMetadataWithPendingWrites:YES fromCache:NO];
+ FIRSnapshotMetadata *baz =
+ [FIRSnapshotMetadata snapshotMetadataWithPendingWrites:NO fromCache:YES];
+ XCTAssertEqualObjects(foo, fooDup);
+ XCTAssertNotEqualObjects(foo, bar);
+ XCTAssertNotEqualObjects(foo, baz);
+ XCTAssertNotEqualObjects(bar, baz);
+
+ XCTAssertEqual([foo hash], [fooDup hash]);
+ XCTAssertNotEqual([foo hash], [bar hash]);
+ XCTAssertNotEqual([foo hash], [baz hash]);
+ XCTAssertNotEqual([bar hash], [baz hash]);
+}
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Firestore/Example/Tests/API/FSTAPIHelpers.h b/Firestore/Example/Tests/API/FSTAPIHelpers.h
new file mode 100644
index 0000000..0729af0
--- /dev/null
+++ b/Firestore/Example/Tests/API/FSTAPIHelpers.h
@@ -0,0 +1,74 @@
+/*
+ * 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 <Foundation/Foundation.h>
+
+#import "Firestore/Example/Tests/Util/FSTHelpers.h"
+
+@class FIRCollectionReference;
+@class FIRDocumentReference;
+@class FIRDocumentSnapshot;
+@class FIRFirestore;
+@class FIRQuerySnapshot;
+
+NS_ASSUME_NONNULL_BEGIN
+
+#if __cplusplus
+extern "C" {
+#endif
+
+/** A convenience method for creating dummy singleton FIRFirestore for tests. */
+FIRFirestore *FSTTestFirestore();
+
+/** A convenience method for creating a doc snapshot for tests. */
+FIRDocumentSnapshot *FSTTestDocSnapshot(NSString *path,
+ FSTTestSnapshotVersion version,
+ NSDictionary<NSString *, id> *_Nullable data,
+ BOOL hasMutations,
+ BOOL fromCache);
+
+/** A convenience method for creating a collection reference from a path string. */
+FIRCollectionReference *FSTTestCollectionRef(NSString *path);
+
+/** A convenience method for creating a document reference from a path string. */
+FIRDocumentReference *FSTTestDocRef(NSString *path);
+
+/**
+ * A convenience method for creating a particular query snapshot for tests.
+ *
+ * @param path To be used in constructing the query.
+ * @param oldDocs Provides the prior set of documents in the QuerySnapshot. Each dictionary entry
+ * maps to a document, with the key being the document id, and the value being the document
+ * contents.
+ * @param docsToAdd Specifies data to be added into the query snapshot as of now. Each dictionary
+ * entry maps to a document, with the key being the document id, and the value being the document
+ * contents.
+ * @param hasPendingWrites Whether the query snapshot has pending writes to the server.
+ * @param fromCache Whether the query snapshot is cache result.
+ * @returns A query snapshot that consists of both sets of documents.
+ */
+FIRQuerySnapshot *FSTTestQuerySnapshot(
+ NSString *path,
+ NSDictionary<NSString *, NSDictionary<NSString *, id> *> *oldDocs,
+ NSDictionary<NSString *, NSDictionary<NSString *, id> *> *docsToAdd,
+ BOOL hasPendingWrites,
+ BOOL fromCache);
+
+#if __cplusplus
+} // extern "C"
+#endif
+
+NS_ASSUME_NONNULL_END
diff --git a/Firestore/Example/Tests/API/FSTAPIHelpers.mm b/Firestore/Example/Tests/API/FSTAPIHelpers.mm
new file mode 100644
index 0000000..da899b7
--- /dev/null
+++ b/Firestore/Example/Tests/API/FSTAPIHelpers.mm
@@ -0,0 +1,115 @@
+/*
+ * 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/API/FSTAPIHelpers.h"
+
+#import <FirebaseFirestore/FIRDocumentChange.h>
+#import <FirebaseFirestore/FIRDocumentReference.h>
+#import <FirebaseFirestore/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/FIRQuerySnapshot+Internal.h"
+#import "Firestore/Source/API/FIRSnapshotMetadata+Internal.h"
+#import "Firestore/Source/Core/FSTQuery.h"
+#import "Firestore/Source/Core/FSTViewSnapshot.h"
+#import "Firestore/Source/Model/FSTDocument.h"
+#import "Firestore/Source/Model/FSTDocumentKey.h"
+#import "Firestore/Source/Model/FSTDocumentSet.h"
+#import "Firestore/Source/Model/FSTPath.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+FIRFirestore *FSTTestFirestore() {
+ static FIRFirestore *sharedInstance = nil;
+ static dispatch_once_t onceToken;
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnonnull"
+ dispatch_once(&onceToken, ^{
+ sharedInstance = [[FIRFirestore alloc] initWithProjectID:@"abc"
+ database:@"abc"
+ persistenceKey:@"db123"
+ credentialsProvider:nil
+ workerDispatchQueue:nil
+ firebaseApp:nil];
+ });
+#pragma clang diagnostic pop
+ return sharedInstance;
+}
+
+FIRDocumentSnapshot *FSTTestDocSnapshot(NSString *path,
+ FSTTestSnapshotVersion version,
+ NSDictionary<NSString *, id> *_Nullable data,
+ BOOL hasMutations,
+ BOOL fromCache) {
+ FSTDocument *doc = data ? FSTTestDoc(path, version, data, hasMutations) : nil;
+ return [FIRDocumentSnapshot snapshotWithFirestore:FSTTestFirestore()
+ documentKey:FSTTestDocKey(path)
+ document:doc
+ fromCache:fromCache];
+}
+
+FIRCollectionReference *FSTTestCollectionRef(NSString *path) {
+ return [FIRCollectionReference referenceWithPath:FSTTestPath(path) firestore:FSTTestFirestore()];
+}
+
+FIRDocumentReference *FSTTestDocRef(NSString *path) {
+ return [FIRDocumentReference referenceWithPath:FSTTestPath(path) firestore:FSTTestFirestore()];
+}
+
+/** A convenience method for creating a query snapshots for tests. */
+FIRQuerySnapshot *FSTTestQuerySnapshot(
+ NSString *path,
+ NSDictionary<NSString *, NSDictionary<NSString *, id> *> *oldDocs,
+ NSDictionary<NSString *, NSDictionary<NSString *, id> *> *docsToAdd,
+ BOOL hasPendingWrites,
+ BOOL fromCache) {
+ FIRSnapshotMetadata *metadata =
+ [FIRSnapshotMetadata snapshotMetadataWithPendingWrites:hasPendingWrites fromCache:fromCache];
+ FSTDocumentSet *oldDocuments = FSTTestDocSet(FSTDocumentComparatorByKey, @[]);
+ for (NSString *key in oldDocs) {
+ oldDocuments = [oldDocuments
+ documentSetByAddingDocument:FSTTestDoc([NSString stringWithFormat:@"%@/%@", path, key], 1,
+ oldDocs[key], hasPendingWrites)];
+ }
+ FSTDocumentSet *newDocuments = oldDocuments;
+ NSArray<FSTDocumentViewChange *> *documentChanges = [NSArray array];
+ for (NSString *key in docsToAdd) {
+ FSTDocument *docToAdd = FSTTestDoc([NSString stringWithFormat:@"%@/%@", path, key], 1,
+ docsToAdd[key], hasPendingWrites);
+ newDocuments = [newDocuments documentSetByAddingDocument:docToAdd];
+ documentChanges = [documentChanges
+ arrayByAddingObject:[FSTDocumentViewChange
+ changeWithDocument:docToAdd
+ type:FSTDocumentViewChangeTypeAdded]];
+ }
+ FSTViewSnapshot *viewSnapshot = [[FSTViewSnapshot alloc] initWithQuery:FSTTestQuery(path)
+ documents:newDocuments
+ oldDocuments:oldDocuments
+ documentChanges:documentChanges
+ fromCache:fromCache
+ hasPendingWrites:hasPendingWrites
+ syncStateChanged:YES];
+ return [FIRQuerySnapshot snapshotWithFirestore:FSTTestFirestore()
+ originalQuery:FSTTestQuery(path)
+ snapshot:viewSnapshot
+ metadata:metadata];
+}
+
+NS_ASSUME_NONNULL_END
diff --git a/Firestore/Example/Tests/Core/FSTDatabaseInfoTests.m b/Firestore/Example/Tests/Core/FSTDatabaseInfoTests.mm
index c7cf22a..c7cf22a 100644
--- a/Firestore/Example/Tests/Core/FSTDatabaseInfoTests.m
+++ b/Firestore/Example/Tests/Core/FSTDatabaseInfoTests.mm
diff --git a/Firestore/Example/Tests/Core/FSTEventManagerTests.m b/Firestore/Example/Tests/Core/FSTEventManagerTests.mm
index 99021ce..fcde17d 100644
--- a/Firestore/Example/Tests/Core/FSTEventManagerTests.m
+++ b/Firestore/Example/Tests/Core/FSTEventManagerTests.mm
@@ -52,7 +52,7 @@ NS_ASSUME_NONNULL_BEGIN
}
- (void)testHandlesManyListenersPerQuery {
- FSTQuery *query = [FSTQuery queryWithPath:FSTTestPath(@"foo/bar")];
+ FSTQuery *query = FSTTestQuery(@"foo/bar");
FSTQueryListener *listener1 = [self noopListenerForQuery:query];
FSTQueryListener *listener2 = [self noopListenerForQuery:query];
@@ -73,7 +73,7 @@ NS_ASSUME_NONNULL_BEGIN
}
- (void)testHandlesUnlistenOnUnknownListenerGracefully {
- FSTQuery *query = [FSTQuery queryWithPath:FSTTestPath(@"foo/bar")];
+ FSTQuery *query = FSTTestQuery(@"foo/bar");
FSTQueryListener *listener = [self noopListenerForQuery:query];
FSTSyncEngine *syncEngineMock = OCMStrictClassMock([FSTSyncEngine class]);
@@ -95,8 +95,8 @@ NS_ASSUME_NONNULL_BEGIN
}
- (void)testNotifiesListenersInTheRightOrder {
- FSTQuery *query1 = [FSTQuery queryWithPath:FSTTestPath(@"foo/bar")];
- FSTQuery *query2 = [FSTQuery queryWithPath:FSTTestPath(@"bar/baz")];
+ FSTQuery *query1 = FSTTestQuery(@"foo/bar");
+ FSTQuery *query2 = FSTTestQuery(@"bar/baz");
NSMutableArray *eventOrder = [NSMutableArray array];
FSTQueryListener *listener1 = [self makeMockListenerForQuery:query1
@@ -135,15 +135,15 @@ NS_ASSUME_NONNULL_BEGIN
}
- (void)testWillForwardOnlineStateChanges {
- FSTQuery *query = [FSTQuery queryWithPath:FSTTestPath(@"foo/bar")];
+ FSTQuery *query = FSTTestQuery(@"foo/bar");
FSTQueryListener *fakeListener = OCMClassMock([FSTQueryListener class]);
NSMutableArray *events = [NSMutableArray array];
OCMStub([fakeListener query]).andReturn(query);
- OCMStub([fakeListener clientDidChangeOnlineState:FSTOnlineStateUnknown])
+ OCMStub([fakeListener applyChangedOnlineState:FSTOnlineStateUnknown])
.andDo(^(NSInvocation *invocation) {
[events addObject:@(FSTOnlineStateUnknown)];
});
- OCMStub([fakeListener clientDidChangeOnlineState:FSTOnlineStateHealthy])
+ OCMStub([fakeListener applyChangedOnlineState:FSTOnlineStateHealthy])
.andDo(^(NSInvocation *invocation) {
[events addObject:@(FSTOnlineStateHealthy)];
});
@@ -154,7 +154,7 @@ NS_ASSUME_NONNULL_BEGIN
[eventManager addListener:fakeListener];
XCTAssertEqualObjects(events, @[ @(FSTOnlineStateUnknown) ]);
- [eventManager watchStreamDidChangeOnlineState:FSTOnlineStateHealthy];
+ [eventManager applyChangedOnlineState:FSTOnlineStateHealthy];
XCTAssertEqualObjects(events, (@[ @(FSTOnlineStateUnknown), @(FSTOnlineStateHealthy) ]));
}
diff --git a/Firestore/Example/Tests/Core/FSTQueryListenerTests.m b/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm
index 1bb7a47..4856b5f 100644
--- a/Firestore/Example/Tests/Core/FSTQueryListenerTests.m
+++ b/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm
@@ -45,7 +45,7 @@ NS_ASSUME_NONNULL_BEGIN
NSMutableArray<FSTViewSnapshot *> *accum = [NSMutableArray array];
NSMutableArray<FSTViewSnapshot *> *otherAccum = [NSMutableArray array];
- FSTQuery *query = [FSTQuery queryWithPath:FSTTestPath(@"rooms")];
+ FSTQuery *query = FSTTestQuery(@"rooms");
FSTDocument *doc1 = FSTTestDoc(@"rooms/Eros", 1, @{@"name" : @"Eros"}, NO);
FSTDocument *doc2 = FSTTestDoc(@"rooms/Hades", 2, @{@"name" : @"Hades"}, NO);
FSTDocument *doc2prime =
@@ -88,7 +88,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)testRaisesErrorEvent {
NSMutableArray<NSError *> *accum = [NSMutableArray array];
- FSTQuery *query = [FSTQuery queryWithPath:FSTTestPath(@"rooms/Eros")];
+ FSTQuery *query = FSTTestQuery(@"rooms/Eros");
FSTQueryListener *listener = [self listenToQuery:query
handler:^(FSTViewSnapshot *snapshot, NSError *error) {
@@ -104,7 +104,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)testRaisesEventForEmptyCollectionAfterSync {
NSMutableArray<FSTViewSnapshot *> *accum = [NSMutableArray array];
- FSTQuery *query = [FSTQuery queryWithPath:FSTTestPath(@"rooms")];
+ FSTQuery *query = FSTTestQuery(@"rooms");
FSTQueryListener *listener = [self listenToQuery:query accumulatingSnapshots:accum];
@@ -126,7 +126,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)testMutingAsyncListenerPreventsAllSubsequentEvents {
NSMutableArray<FSTViewSnapshot *> *accum = [NSMutableArray array];
- FSTQuery *query = [FSTQuery queryWithPath:FSTTestPath(@"rooms/Eros")];
+ FSTQuery *query = FSTTestQuery(@"rooms/Eros");
FSTDocument *doc1 = FSTTestDoc(@"rooms/Eros", 3, @{@"name" : @"Eros"}, NO);
FSTDocument *doc2 = FSTTestDoc(@"rooms/Eros", 4, @{@"name" : @"Eros2"}, NO);
@@ -166,7 +166,7 @@ NS_ASSUME_NONNULL_BEGIN
NSMutableArray<FSTViewSnapshot *> *filteredAccum = [NSMutableArray array];
NSMutableArray<FSTViewSnapshot *> *fullAccum = [NSMutableArray array];
- FSTQuery *query = [FSTQuery queryWithPath:FSTTestPath(@"rooms")];
+ FSTQuery *query = FSTTestQuery(@"rooms");
FSTDocument *doc1 = FSTTestDoc(@"rooms/Eros", 1, @{@"name" : @"Eros"}, NO);
FSTDocument *doc2 = FSTTestDoc(@"rooms/Hades", 2, @{@"name" : @"Hades"}, NO);
@@ -204,7 +204,7 @@ NS_ASSUME_NONNULL_BEGIN
NSMutableArray<FSTViewSnapshot *> *filteredAccum = [NSMutableArray array];
NSMutableArray<FSTViewSnapshot *> *fullAccum = [NSMutableArray array];
- FSTQuery *query = [FSTQuery queryWithPath:FSTTestPath(@"rooms")];
+ FSTQuery *query = FSTTestQuery(@"rooms");
FSTDocument *doc1 = FSTTestDoc(@"rooms/Eros", 1, @{@"name" : @"Eros"}, YES);
FSTDocument *doc2 = FSTTestDoc(@"rooms/Hades", 2, @{@"name" : @"Hades"}, NO);
FSTDocument *doc1Prime = FSTTestDoc(@"rooms/Eros", 1, @{@"name" : @"Eros"}, NO);
@@ -253,7 +253,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)testRaisesQueryMetadataEventsOnlyWhenHasPendingWritesOnTheQueryChanges {
NSMutableArray<FSTViewSnapshot *> *fullAccum = [NSMutableArray array];
- FSTQuery *query = [FSTQuery queryWithPath:FSTTestPath(@"rooms")];
+ FSTQuery *query = FSTTestQuery(@"rooms");
FSTDocument *doc1 = FSTTestDoc(@"rooms/Eros", 1, @{@"name" : @"Eros"}, YES);
FSTDocument *doc2 = FSTTestDoc(@"rooms/Hades", 2, @{@"name" : @"Hades"}, YES);
FSTDocument *doc1Prime = FSTTestDoc(@"rooms/Eros", 1, @{@"name" : @"Eros"}, NO);
@@ -290,7 +290,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)testMetadataOnlyDocumentChangesAreFilteredOutWhenIncludeDocumentMetadataChangesIsFalse {
NSMutableArray<FSTViewSnapshot *> *filteredAccum = [NSMutableArray array];
- FSTQuery *query = [FSTQuery queryWithPath:FSTTestPath(@"rooms")];
+ FSTQuery *query = FSTTestQuery(@"rooms");
FSTDocument *doc1 = FSTTestDoc(@"rooms/Eros", 1, @{@"name" : @"Eros"}, YES);
FSTDocument *doc2 = FSTTestDoc(@"rooms/Hades", 2, @{@"name" : @"Hades"}, NO);
FSTDocument *doc1Prime = FSTTestDoc(@"rooms/Eros", 1, @{@"name" : @"Eros"}, NO);
@@ -322,7 +322,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)testWillWaitForSyncIfOnline {
NSMutableArray<FSTViewSnapshot *> *events = [NSMutableArray array];
- FSTQuery *query = [FSTQuery queryWithPath:FSTTestPath(@"rooms")];
+ FSTQuery *query = FSTTestQuery(@"rooms");
FSTDocument *doc1 = FSTTestDoc(@"rooms/Eros", 1, @{@"name" : @"Eros"}, NO);
FSTDocument *doc2 = FSTTestDoc(@"rooms/Hades", 2, @{@"name" : @"Hades"}, NO);
FSTQueryListener *listener =
@@ -340,10 +340,10 @@ NS_ASSUME_NONNULL_BEGIN
[FSTTargetChange changeWithDocuments:@[ doc1, doc2 ]
currentStatusUpdate:FSTCurrentStatusUpdateMarkCurrent]);
- [listener clientDidChangeOnlineState:FSTOnlineStateHealthy]; // no event
+ [listener applyChangedOnlineState:FSTOnlineStateHealthy]; // no event
[listener queryDidChangeViewSnapshot:snap1];
- [listener clientDidChangeOnlineState:FSTOnlineStateUnknown];
- [listener clientDidChangeOnlineState:FSTOnlineStateHealthy];
+ [listener applyChangedOnlineState:FSTOnlineStateUnknown];
+ [listener applyChangedOnlineState:FSTOnlineStateHealthy];
[listener queryDidChangeViewSnapshot:snap2];
[listener queryDidChangeViewSnapshot:snap3];
@@ -365,7 +365,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)testWillRaiseInitialEventWhenGoingOffline {
NSMutableArray<FSTViewSnapshot *> *events = [NSMutableArray array];
- FSTQuery *query = [FSTQuery queryWithPath:FSTTestPath(@"rooms")];
+ FSTQuery *query = FSTTestQuery(@"rooms");
FSTDocument *doc1 = FSTTestDoc(@"rooms/Eros", 1, @{@"name" : @"Eros"}, NO);
FSTDocument *doc2 = FSTTestDoc(@"rooms/Hades", 2, @{@"name" : @"Hades"}, NO);
FSTQueryListener *listener =
@@ -379,12 +379,12 @@ NS_ASSUME_NONNULL_BEGIN
FSTViewSnapshot *snap1 = FSTTestApplyChanges(view, @[ doc1 ], nil);
FSTViewSnapshot *snap2 = FSTTestApplyChanges(view, @[ doc2 ], nil);
- [listener clientDidChangeOnlineState:FSTOnlineStateHealthy]; // no event
- [listener queryDidChangeViewSnapshot:snap1]; // no event
- [listener clientDidChangeOnlineState:FSTOnlineStateFailed]; // event
- [listener clientDidChangeOnlineState:FSTOnlineStateUnknown]; // no event
- [listener clientDidChangeOnlineState:FSTOnlineStateFailed]; // no event
- [listener queryDidChangeViewSnapshot:snap2]; // another event
+ [listener applyChangedOnlineState:FSTOnlineStateHealthy]; // no event
+ [listener queryDidChangeViewSnapshot:snap1]; // no event
+ [listener applyChangedOnlineState:FSTOnlineStateFailed]; // event
+ [listener applyChangedOnlineState:FSTOnlineStateUnknown]; // no event
+ [listener applyChangedOnlineState:FSTOnlineStateFailed]; // no event
+ [listener queryDidChangeViewSnapshot:snap2]; // another event
FSTDocumentViewChange *change1 =
[FSTDocumentViewChange changeWithDocument:doc1 type:FSTDocumentViewChangeTypeAdded];
@@ -411,7 +411,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)testWillRaiseInitialEventWhenGoingOfflineAndThereAreNoDocs {
NSMutableArray<FSTViewSnapshot *> *events = [NSMutableArray array];
- FSTQuery *query = [FSTQuery queryWithPath:FSTTestPath(@"rooms")];
+ FSTQuery *query = FSTTestQuery(@"rooms");
FSTQueryListener *listener = [self listenToQuery:query
options:[FSTListenOptions defaultOptions]
accumulatingSnapshots:events];
@@ -419,9 +419,9 @@ NS_ASSUME_NONNULL_BEGIN
FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:[FSTDocumentKeySet keySet]];
FSTViewSnapshot *snap1 = FSTTestApplyChanges(view, @[], nil);
- [listener clientDidChangeOnlineState:FSTOnlineStateHealthy]; // no event
- [listener queryDidChangeViewSnapshot:snap1]; // no event
- [listener clientDidChangeOnlineState:FSTOnlineStateFailed]; // event
+ [listener applyChangedOnlineState:FSTOnlineStateHealthy]; // no event
+ [listener queryDidChangeViewSnapshot:snap1]; // no event
+ [listener applyChangedOnlineState:FSTOnlineStateFailed]; // event
FSTViewSnapshot *expectedSnap = [[FSTViewSnapshot alloc]
initWithQuery:query
@@ -437,7 +437,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)testWillRaiseInitialEventWhenStartingOfflineAndThereAreNoDocs {
NSMutableArray<FSTViewSnapshot *> *events = [NSMutableArray array];
- FSTQuery *query = [FSTQuery queryWithPath:FSTTestPath(@"rooms")];
+ FSTQuery *query = FSTTestQuery(@"rooms");
FSTQueryListener *listener = [self listenToQuery:query
options:[FSTListenOptions defaultOptions]
accumulatingSnapshots:events];
@@ -445,8 +445,8 @@ NS_ASSUME_NONNULL_BEGIN
FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:[FSTDocumentKeySet keySet]];
FSTViewSnapshot *snap1 = FSTTestApplyChanges(view, @[], nil);
- [listener clientDidChangeOnlineState:FSTOnlineStateFailed]; // no event
- [listener queryDidChangeViewSnapshot:snap1]; // event
+ [listener applyChangedOnlineState:FSTOnlineStateFailed]; // no event
+ [listener queryDidChangeViewSnapshot:snap1]; // event
FSTViewSnapshot *expectedSnap = [[FSTViewSnapshot alloc]
initWithQuery:query
diff --git a/Firestore/Example/Tests/Core/FSTQueryTests.m b/Firestore/Example/Tests/Core/FSTQueryTests.mm
index 1fd0e8b..3d2bd82 100644
--- a/Firestore/Example/Tests/Core/FSTQueryTests.m
+++ b/Firestore/Example/Tests/Core/FSTQueryTests.mm
@@ -61,9 +61,7 @@ NS_ASSUME_NONNULL_BEGIN
}
- (void)testOrderBy {
- FSTResourcePath *path =
- [FSTResourcePath pathWithSegments:@[ @"rooms", @"Firestore", @"messages" ]];
- FSTQuery *query = [FSTQuery queryWithPath:path];
+ FSTQuery *query = FSTTestQuery(@"rooms/Firestore/messages");
query =
[query queryByAddingSortOrder:[FSTSortOrder sortOrderWithFieldPath:FSTTestFieldPath(@"length")
ascending:NO]];
@@ -80,29 +78,25 @@ NS_ASSUME_NONNULL_BEGIN
}
- (void)testMatchesBasedOnDocumentKey {
- FSTResourcePath *queryKey =
- [FSTResourcePath pathWithSegments:@[ @"rooms", @"eros", @"messages", @"1" ]];
FSTDocument *doc1 = FSTTestDoc(@"rooms/eros/messages/1", 0, @{@"text" : @"msg1"}, NO);
FSTDocument *doc2 = FSTTestDoc(@"rooms/eros/messages/2", 0, @{@"text" : @"msg2"}, NO);
FSTDocument *doc3 = FSTTestDoc(@"rooms/other/messages/1", 0, @{@"text" : @"msg3"}, NO);
// document query
- FSTQuery *query = [FSTQuery queryWithPath:queryKey];
+ FSTQuery *query = FSTTestQuery(@"rooms/eros/messages/1");
XCTAssertTrue([query matchesDocument:doc1]);
XCTAssertFalse([query matchesDocument:doc2]);
XCTAssertFalse([query matchesDocument:doc3]);
}
- (void)testMatchesCorrectlyForShallowAncestorQuery {
- FSTResourcePath *queryPath =
- [FSTResourcePath pathWithSegments:@[ @"rooms", @"eros", @"messages" ]];
FSTDocument *doc1 = FSTTestDoc(@"rooms/eros/messages/1", 0, @{@"text" : @"msg1"}, NO);
FSTDocument *doc1Meta = FSTTestDoc(@"rooms/eros/messages/1/meta/1", 0, @{@"meta" : @"mv"}, NO);
FSTDocument *doc2 = FSTTestDoc(@"rooms/eros/messages/2", 0, @{@"text" : @"msg2"}, NO);
FSTDocument *doc3 = FSTTestDoc(@"rooms/other/messages/1", 0, @{@"text" : @"msg3"}, NO);
// shallow ancestor query
- FSTQuery *query = [FSTQuery queryWithPath:queryPath];
+ FSTQuery *query = FSTTestQuery(@"rooms/eros/messages");
XCTAssertTrue([query matchesDocument:doc1]);
XCTAssertFalse([query matchesDocument:doc1Meta]);
XCTAssertTrue([query matchesDocument:doc2]);
@@ -110,21 +104,20 @@ NS_ASSUME_NONNULL_BEGIN
}
- (void)testEmptyFieldsAreAllowedForQueries {
- FSTResourcePath *queryPath = [FSTResourcePath pathWithString:@"rooms/eros/messages"];
FSTDocument *doc1 = FSTTestDoc(@"rooms/eros/messages/1", 0, @{@"text" : @"msg1"}, NO);
FSTDocument *doc2 = FSTTestDoc(@"rooms/eros/messages/2", 0, @{}, NO);
- FSTQuery *query = [[FSTQuery queryWithPath:queryPath]
+ FSTQuery *query = [FSTTestQuery(@"rooms/eros/messages")
queryByAddingFilter:FSTTestFilter(@"text", @"==", @"msg1")];
XCTAssertTrue([query matchesDocument:doc1]);
XCTAssertFalse([query matchesDocument:doc2]);
}
- (void)testMatchesPrimitiveValuesForFilters {
- FSTQuery *query1 = [[FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"collection" ]]]
- queryByAddingFilter:FSTTestFilter(@"sort", @">=", @(2))];
- FSTQuery *query2 = [[FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"collection" ]]]
- queryByAddingFilter:FSTTestFilter(@"sort", @"<=", @(2))];
+ FSTQuery *query1 =
+ [FSTTestQuery(@"collection") queryByAddingFilter:FSTTestFilter(@"sort", @">=", @(2))];
+ FSTQuery *query2 =
+ [FSTTestQuery(@"collection") queryByAddingFilter:FSTTestFilter(@"sort", @"<=", @(2))];
FSTDocument *doc1 = FSTTestDoc(@"collection/1", 0, @{ @"sort" : @1 }, NO);
FSTDocument *doc2 = FSTTestDoc(@"collection/2", 0, @{ @"sort" : @2 }, NO);
@@ -149,7 +142,7 @@ NS_ASSUME_NONNULL_BEGIN
}
- (void)testNullFilter {
- FSTQuery *query = [[FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"collection" ]]]
+ FSTQuery *query = [FSTTestQuery(@"collection")
queryByAddingFilter:FSTTestFilter(@"sort", @"==", [NSNull null])];
FSTDocument *doc1 = FSTTestDoc(@"collection/1", 0, @{@"sort" : [NSNull null]}, NO);
FSTDocument *doc2 = FSTTestDoc(@"collection/2", 0, @{ @"sort" : @2 }, NO);
@@ -165,8 +158,8 @@ NS_ASSUME_NONNULL_BEGIN
}
- (void)testNanFilter {
- FSTQuery *query = [[FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"collection" ]]]
- queryByAddingFilter:FSTTestFilter(@"sort", @"==", @(NAN))];
+ FSTQuery *query =
+ [FSTTestQuery(@"collection") queryByAddingFilter:FSTTestFilter(@"sort", @"==", @(NAN))];
FSTDocument *doc1 = FSTTestDoc(@"collection/1", 0, @{ @"sort" : @(NAN) }, NO);
FSTDocument *doc2 = FSTTestDoc(@"collection/2", 0, @{ @"sort" : @2 }, NO);
FSTDocument *doc3 = FSTTestDoc(@"collection/2", 0, @{ @"sort" : @3.1 }, NO);
@@ -181,10 +174,10 @@ NS_ASSUME_NONNULL_BEGIN
}
- (void)testDoesNotMatchComplexObjectsForFilters {
- FSTQuery *query1 = [[FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"collection" ]]]
- queryByAddingFilter:FSTTestFilter(@"sort", @"<=", @(2))];
- FSTQuery *query2 = [[FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"collection" ]]]
- queryByAddingFilter:FSTTestFilter(@"sort", @">=", @(2))];
+ FSTQuery *query1 =
+ [FSTTestQuery(@"collection") queryByAddingFilter:FSTTestFilter(@"sort", @"<=", @(2))];
+ FSTQuery *query2 =
+ [FSTTestQuery(@"collection") queryByAddingFilter:FSTTestFilter(@"sort", @">=", @(2))];
FSTDocument *doc1 = FSTTestDoc(@"collection/1", 0, @{ @"sort" : @2 }, NO);
FSTDocument *doc2 = FSTTestDoc(@"collection/2", 0, @{ @"sort" : @[] }, NO);
@@ -212,7 +205,7 @@ NS_ASSUME_NONNULL_BEGIN
}
- (void)testDoesntRemoveComplexObjectsWithOrderBy {
- FSTQuery *query1 = [[FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"collection" ]]]
+ FSTQuery *query1 = [FSTTestQuery(@"collection")
queryByAddingSortOrder:[FSTSortOrder sortOrderWithFieldPath:FSTTestFieldPath(@"sort")
ascending:YES]];
@@ -232,9 +225,7 @@ NS_ASSUME_NONNULL_BEGIN
}
- (void)testFiltersBasedOnArrayValue {
- FSTQuery *baseQuery =
- [FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"collection" ]]];
-
+ FSTQuery *baseQuery = FSTTestQuery(@"collection");
FSTDocument *doc1 = FSTTestDoc(@"collection/doc", 0, @{ @"tags" : @[ @"foo", @1, @YES ] }, NO);
NSArray<id<FSTFilter>> *matchingFilters =
@@ -256,9 +247,7 @@ NS_ASSUME_NONNULL_BEGIN
}
- (void)testFiltersBasedOnObjectValue {
- FSTQuery *baseQuery =
- [FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"collection" ]]];
-
+ FSTQuery *baseQuery = FSTTestQuery(@"collection");
FSTDocument *doc1 =
FSTTestDoc(@"collection/doc", 0,
@{ @"tags" : @{@"foo" : @"foo", @"a" : @0, @"b" : @YES, @"c" : @(NAN)} }, NO);
@@ -310,7 +299,7 @@ NS_ASSUME_NONNULL_BEGIN
}
- (void)testSortsDocumentsInTheCorrectOrder {
- FSTQuery *query = [FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"collection" ]]];
+ FSTQuery *query = FSTTestQuery(@"collection");
query =
[query queryByAddingSortOrder:[FSTSortOrder sortOrderWithFieldPath:FSTTestFieldPath(@"sort")
ascending:YES]];
@@ -339,7 +328,7 @@ NS_ASSUME_NONNULL_BEGIN
}
- (void)testSortsDocumentsUsingMultipleFields {
- FSTQuery *query = [FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"collection" ]]];
+ FSTQuery *query = FSTTestQuery(@"collection");
query =
[query queryByAddingSortOrder:[FSTSortOrder sortOrderWithFieldPath:FSTTestFieldPath(@"sort1")
ascending:YES]];
@@ -366,7 +355,7 @@ NS_ASSUME_NONNULL_BEGIN
}
- (void)testSortsDocumentsWithDescendingToo {
- FSTQuery *query = [FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"collection" ]]];
+ FSTQuery *query = FSTTestQuery(@"collection");
query =
[query queryByAddingSortOrder:[FSTSortOrder sortOrderWithFieldPath:FSTTestFieldPath(@"sort1")
ascending:NO]];
@@ -393,40 +382,40 @@ NS_ASSUME_NONNULL_BEGIN
}
- (void)testEquality {
- FSTQuery *q11 = [FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"foo" ]]];
+ FSTQuery *q11 = FSTTestQuery(@"foo");
q11 = [q11 queryByAddingFilter:FSTTestFilter(@"i1", @"<", @(2))];
q11 = [q11 queryByAddingFilter:FSTTestFilter(@"i2", @"==", @(3))];
- FSTQuery *q12 = [FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"foo" ]]];
+ FSTQuery *q12 = FSTTestQuery(@"foo");
q12 = [q12 queryByAddingFilter:FSTTestFilter(@"i2", @"==", @(3))];
q12 = [q12 queryByAddingFilter:FSTTestFilter(@"i1", @"<", @(2))];
- FSTQuery *q21 = [FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"foo" ]]];
- FSTQuery *q22 = [FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"foo" ]]];
+ FSTQuery *q21 = FSTTestQuery(@"foo");
+ FSTQuery *q22 = FSTTestQuery(@"foo");
- FSTQuery *q31 = [FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"foo", @"bar" ]]];
- FSTQuery *q32 = [FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"foo", @"bar" ]]];
+ FSTQuery *q31 = FSTTestQuery(@"foo/bar");
+ FSTQuery *q32 = FSTTestQuery(@"foo/bar");
- FSTQuery *q41 = [FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"foo" ]]];
+ FSTQuery *q41 = FSTTestQuery(@"foo");
q41 = [q41 queryByAddingSortBy:@"foo" ascending:YES];
q41 = [q41 queryByAddingSortBy:@"bar" ascending:YES];
- FSTQuery *q42 = [FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"foo" ]]];
+ FSTQuery *q42 = FSTTestQuery(@"foo");
q42 = [q42 queryByAddingSortBy:@"foo" ascending:YES];
q42 = [q42 queryByAddingSortBy:@"bar" ascending:YES];
- FSTQuery *q43Diff = [FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"foo" ]]];
+ FSTQuery *q43Diff = FSTTestQuery(@"foo");
q43Diff = [q43Diff queryByAddingSortBy:@"bar" ascending:YES];
q43Diff = [q43Diff queryByAddingSortBy:@"foo" ascending:YES];
- FSTQuery *q51 = [FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"foo" ]]];
+ FSTQuery *q51 = FSTTestQuery(@"foo");
q51 = [q51 queryByAddingSortBy:@"foo" ascending:YES];
q51 = [q51 queryByAddingFilter:FSTTestFilter(@"foo", @">", @(2))];
- FSTQuery *q52 = [FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"foo" ]]];
+ FSTQuery *q52 = FSTTestQuery(@"foo");
q52 = [q52 queryByAddingFilter:FSTTestFilter(@"foo", @">", @(2))];
q52 = [q52 queryByAddingSortBy:@"foo" ascending:YES];
- FSTQuery *q53Diff = [FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"foo" ]]];
+ FSTQuery *q53Diff = FSTTestQuery(@"foo");
q53Diff = [q53Diff queryByAddingFilter:FSTTestFilter(@"bar", @">", @(2))];
q53Diff = [q53Diff queryByAddingSortBy:@"bar" ascending:YES];
- FSTQuery *q61 = [FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"foo" ]]];
+ FSTQuery *q61 = FSTTestQuery(@"foo");
q61 = [q61 queryBySettingLimit:10];
// XCTAssertEqualObjects(q11, q12); // TODO(klimt): not canonical yet
@@ -458,40 +447,40 @@ NS_ASSUME_NONNULL_BEGIN
}
- (void)testUniqueIds {
- FSTQuery *q11 = [FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"foo" ]]];
+ FSTQuery *q11 = FSTTestQuery(@"foo");
q11 = [q11 queryByAddingFilter:FSTTestFilter(@"i1", @"<", @(2))];
q11 = [q11 queryByAddingFilter:FSTTestFilter(@"i2", @"==", @(3))];
- FSTQuery *q12 = [FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"foo" ]]];
+ FSTQuery *q12 = FSTTestQuery(@"foo");
q12 = [q12 queryByAddingFilter:FSTTestFilter(@"i2", @"==", @(3))];
q12 = [q12 queryByAddingFilter:FSTTestFilter(@"i1", @"<", @(2))];
- FSTQuery *q21 = [FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"foo" ]]];
- FSTQuery *q22 = [FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"foo" ]]];
+ FSTQuery *q21 = FSTTestQuery(@"foo");
+ FSTQuery *q22 = FSTTestQuery(@"foo");
- FSTQuery *q31 = [FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"foo", @"bar" ]]];
- FSTQuery *q32 = [FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"foo", @"bar" ]]];
+ FSTQuery *q31 = FSTTestQuery(@"foo/bar");
+ FSTQuery *q32 = FSTTestQuery(@"foo/bar");
- FSTQuery *q41 = [FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"foo" ]]];
+ FSTQuery *q41 = FSTTestQuery(@"foo");
q41 = [q41 queryByAddingSortBy:@"foo" ascending:YES];
q41 = [q41 queryByAddingSortBy:@"bar" ascending:YES];
- FSTQuery *q42 = [FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"foo" ]]];
+ FSTQuery *q42 = FSTTestQuery(@"foo");
q42 = [q42 queryByAddingSortBy:@"foo" ascending:YES];
q42 = [q42 queryByAddingSortBy:@"bar" ascending:YES];
- FSTQuery *q43Diff = [FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"foo" ]]];
+ FSTQuery *q43Diff = FSTTestQuery(@"foo");
q43Diff = [q43Diff queryByAddingSortBy:@"bar" ascending:YES];
q43Diff = [q43Diff queryByAddingSortBy:@"foo" ascending:YES];
- FSTQuery *q51 = [FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"foo" ]]];
+ FSTQuery *q51 = FSTTestQuery(@"foo");
q51 = [q51 queryByAddingSortBy:@"foo" ascending:YES];
q51 = [q51 queryByAddingFilter:FSTTestFilter(@"foo", @">", @(2))];
- FSTQuery *q52 = [FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"foo" ]]];
+ FSTQuery *q52 = FSTTestQuery(@"foo");
q52 = [q52 queryByAddingFilter:FSTTestFilter(@"foo", @">", @(2))];
q52 = [q52 queryByAddingSortBy:@"foo" ascending:YES];
- FSTQuery *q53Diff = [FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"foo" ]]];
+ FSTQuery *q53Diff = FSTTestQuery(@"foo");
q53Diff = [q53Diff queryByAddingFilter:FSTTestFilter(@"bar", @">", @(2))];
q53Diff = [q53Diff queryByAddingSortBy:@"bar" ascending:YES];
- FSTQuery *q61 = [FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"foo" ]]];
+ FSTQuery *q61 = FSTTestQuery(@"foo");
q61 = [q61 queryBySettingLimit:10];
// XCTAssertEqual(q11.hash, q12.hash); // TODO(klimt): not canonical yet
diff --git a/Firestore/Example/Tests/Core/FSTTargetIDGeneratorTests.m b/Firestore/Example/Tests/Core/FSTTargetIDGeneratorTests.m
deleted file mode 100644
index 6f54fd1..0000000
--- 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 <XCTest/XCTest.h>
-
-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.mm
index a3765fe..a3765fe 100644
--- a/Firestore/Example/Tests/Core/FSTTimestampTests.m
+++ b/Firestore/Example/Tests/Core/FSTTimestampTests.mm
diff --git a/Firestore/Example/Tests/Core/FSTViewSnapshotTest.m b/Firestore/Example/Tests/Core/FSTViewSnapshotTest.mm
index 5d3787a..fe3e42d 100644
--- a/Firestore/Example/Tests/Core/FSTViewSnapshotTest.m
+++ b/Firestore/Example/Tests/Core/FSTViewSnapshotTest.mm
@@ -107,7 +107,7 @@ NS_ASSUME_NONNULL_BEGIN
}
- (void)testViewSnapshotConstructor {
- FSTQuery *query = [FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"a" ]]];
+ FSTQuery *query = FSTTestQuery(@"a");
FSTDocumentSet *documents = [FSTDocumentSet documentSetWithComparator:FSTDocumentComparatorByKey];
FSTDocumentSet *oldDocuments = documents;
documents = [documents documentSetByAddingDocument:FSTTestDoc(@"c/a", 1, @{}, NO)];
diff --git a/Firestore/Example/Tests/Core/FSTViewTests.m b/Firestore/Example/Tests/Core/FSTViewTests.mm
index e6c4510..e6c4510 100644
--- a/Firestore/Example/Tests/Core/FSTViewTests.m
+++ b/Firestore/Example/Tests/Core/FSTViewTests.mm
diff --git a/Firestore/Example/Tests/GoogleTest/FSTGoogleTestTests.mm b/Firestore/Example/Tests/GoogleTest/FSTGoogleTestTests.mm
index 4fc77ed..18dfaf3 100644
--- a/Firestore/Example/Tests/GoogleTest/FSTGoogleTestTests.mm
+++ b/Firestore/Example/Tests/GoogleTest/FSTGoogleTestTests.mm
@@ -17,8 +17,9 @@
#import <XCTest/XCTest.h>
#import <objc/runtime.h>
+#include <gtest/gtest.h>
+
#include "Firestore/Source/Util/FSTAssert.h"
-#include "gtest/gtest.h"
/**
* An XCTest test case that finds C++ test cases written in the GoogleTest framework, runs them, and
diff --git a/Firestore/Example/Tests/Integration/API/FIRCursorTests.m b/Firestore/Example/Tests/Integration/API/FIRCursorTests.mm
index dc9da83..3b5c38f 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 <FirebaseFirestore/FirebaseFirestore.h>
#import <XCTest/XCTest.h>
diff --git a/Firestore/Example/Tests/Integration/API/FIRDatabaseTests.m b/Firestore/Example/Tests/Integration/API/FIRDatabaseTests.mm
index 087eb01..3b6a67e 100644
--- a/Firestore/Example/Tests/Integration/API/FIRDatabaseTests.m
+++ b/Firestore/Example/Tests/Integration/API/FIRDatabaseTests.mm
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-@import FirebaseFirestore;
+#import <FirebaseFirestore/FirebaseFirestore.h>
#import <XCTest/XCTest.h>
@@ -83,6 +83,13 @@
XCTAssertFalse(result.exists);
}
+- (void)testCanRetrieveDocumentThatDoesNotExist {
+ FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
+ FIRDocumentSnapshot *result = [self readDocumentForRef:doc];
+ XCTAssertNil(result.data);
+ XCTAssertNil(result[@"foo"]);
+}
+
- (void)testCannotUpdateNonexistentDocument {
FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
@@ -603,6 +610,7 @@
} 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];
@@ -844,7 +852,7 @@
FIRFirestore *firestore = doc.firestore;
NSDictionary<NSString *, id> *data = @{@"a" : @"b"};
- [firestore.client disableNetworkWithCompletion:^(NSError *error) {
+ [firestore disableNetworkWithCompletion:^(NSError *error) {
XCTAssertNil(error);
[doc setData:data
@@ -853,7 +861,7 @@
[writeEpectation fulfill];
}];
- [firestore.client enableNetworkWithCompletion:^(NSError *error) {
+ [firestore enableNetworkWithCompletion:^(NSError *error) {
XCTAssertNil(error);
[networkExpectation fulfill];
}];
@@ -883,7 +891,7 @@
__weak FIRDocumentReference *weakDoc = doc;
- [firestore.client disableNetworkWithCompletion:^(NSError *error) {
+ [firestore disableNetworkWithCompletion:^(NSError *error) {
XCTAssertNil(error);
[doc setData:data
completion:^(NSError *_Nullable error) {
@@ -904,7 +912,7 @@
// Verify that we are reading from cache.
XCTAssertTrue(snapshot.metadata.fromCache);
XCTAssertEqualObjects(snapshot.data, data);
- [firestore.client enableNetworkWithCompletion:^(NSError *error) {
+ [firestore enableNetworkWithCompletion:^(NSError *error) {
[networkExpectation fulfill];
}];
}];
@@ -931,4 +939,25 @@
[self readSnapshotForRef:[self documentRef] requireOnline:YES];
}
+- (void)testCanDisableNetwork {
+ FIRDocumentReference *doc = [self documentRef];
+ FIRFirestore *firestore = doc.firestore;
+
+ [firestore enableNetworkWithCompletion:[self completionForExpectationWithName:@"Enable network"]];
+ [self awaitExpectations];
+ [firestore
+ enableNetworkWithCompletion:[self completionForExpectationWithName:@"Enable network again"]];
+ [self awaitExpectations];
+ [firestore
+ disableNetworkWithCompletion:[self completionForExpectationWithName:@"Disable network"]];
+ [self awaitExpectations];
+ [firestore
+ disableNetworkWithCompletion:[self
+ completionForExpectationWithName:@"Disable network again"]];
+ [self awaitExpectations];
+ [firestore
+ enableNetworkWithCompletion:[self completionForExpectationWithName:@"Final enable network"]];
+ [self awaitExpectations];
+}
+
@end
diff --git a/Firestore/Example/Tests/Integration/API/FIRFieldsTests.m b/Firestore/Example/Tests/Integration/API/FIRFieldsTests.mm
index b647f52..34bd87e 100644
--- a/Firestore/Example/Tests/Integration/API/FIRFieldsTests.m
+++ b/Firestore/Example/Tests/Integration/API/FIRFieldsTests.mm
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-@import FirebaseFirestore;
+#import <FirebaseFirestore/FirebaseFirestore.h>
#import <XCTest/XCTest.h>
diff --git a/Firestore/Example/Tests/Integration/API/FIRListenerRegistrationTests.m b/Firestore/Example/Tests/Integration/API/FIRListenerRegistrationTests.mm
index 9751844..036ab32 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 <FirebaseFirestore/FirebaseFirestore.h>
#import <XCTest/XCTest.h>
@@ -128,35 +128,4 @@
[two remove];
}
-- (void)testWatchSurvivesNetworkDisconnect {
- XCTestExpectation *testExpectiation =
- [self expectationWithDescription:@"testWatchSurvivesNetworkDisconnect"];
-
- FIRCollectionReference *collectionRef = [self collectionRef];
- FIRDocumentReference *docRef = [collectionRef documentWithAutoID];
-
- FIRFirestore *firestore = collectionRef.firestore;
-
- FIRQueryListenOptions *options = [[[FIRQueryListenOptions options]
- includeDocumentMetadataChanges:YES] includeQueryMetadataChanges:YES];
-
- [collectionRef addSnapshotListenerWithOptions:options
- listener:^(FIRQuerySnapshot *snapshot, NSError *error) {
- XCTAssertNil(error);
- if (!snapshot.empty && !snapshot.metadata.fromCache) {
- [testExpectiation fulfill];
- }
- }];
-
- [firestore.client disableNetworkWithCompletion:^(NSError *error) {
- XCTAssertNil(error);
- [docRef setData:@{@"foo" : @"bar"}];
- [firestore.client enableNetworkWithCompletion:^(NSError *error) {
- XCTAssertNil(error);
- }];
- }];
-
- [self awaitExpectations];
-}
-
@end
diff --git a/Firestore/Example/Tests/Integration/API/FIRQueryTests.m b/Firestore/Example/Tests/Integration/API/FIRQueryTests.mm
index 180b423..32d746e 100644
--- a/Firestore/Example/Tests/Integration/API/FIRQueryTests.m
+++ b/Firestore/Example/Tests/Integration/API/FIRQueryTests.mm
@@ -14,13 +14,14 @@
* limitations under the License.
*/
-@import FirebaseFirestore;
+#import <FirebaseFirestore/FirebaseFirestore.h>
#import <XCTest/XCTest.h>
-#import "Firestore/Source/Core/FSTFirestoreClient.h"
-
+#import "Firestore/Example/Tests/Util/FSTEventAccumulator.h"
#import "Firestore/Example/Tests/Util/FSTIntegrationTestCase.h"
+#import "Firestore/Source/API/FIRFirestore+Internal.h"
+#import "Firestore/Source/Core/FSTFirestoreClient.h"
@interface FIRQueryTests : FSTIntegrationTestCase
@end
@@ -111,6 +112,23 @@
XCTAssertEqualObjects(FIRQuerySnapshotGetIDs(snapshot), (@[ @"b", @"a" ]));
}
+- (void)testQueryWithPredicate {
+ FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{
+ @"a" : @{@"a" : @1},
+ @"b" : @{@"a" : @2},
+ @"c" : @{@"a" : @3}
+ }];
+
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a < 3"];
+ FIRQuery *query = [collRef queryFilteredUsingPredicate:predicate];
+ query = [query queryOrderedByFieldPath:[[FIRFieldPath alloc] initWithFields:@[ @"a" ]]
+ descending:YES];
+
+ FIRQuerySnapshot *snapshot = [self readDocumentSetForRef:query];
+
+ XCTAssertEqualObjects(FIRQuerySnapshotGetIDs(snapshot), (@[ @"b", @"a" ]));
+}
+
- (void)testFilterOnInfinity {
FIRCollectionReference *collRef = [self collectionRefWithDocuments:@{
@"a" : @{@"inf" : @(INFINITY)},
@@ -194,4 +212,90 @@
XCTAssertEqualObjects(FIRQuerySnapshotGetData(docs), (@[ testDocs[@"ab"], testDocs[@"ba"] ]));
}
+- (void)testWatchSurvivesNetworkDisconnect {
+ XCTestExpectation *testExpectiation =
+ [self expectationWithDescription:@"testWatchSurvivesNetworkDisconnect"];
+
+ FIRCollectionReference *collectionRef = [self collectionRef];
+ FIRDocumentReference *docRef = [collectionRef documentWithAutoID];
+
+ FIRFirestore *firestore = collectionRef.firestore;
+
+ FIRQueryListenOptions *options = [[[FIRQueryListenOptions options]
+ includeDocumentMetadataChanges:YES] includeQueryMetadataChanges:YES];
+
+ [collectionRef addSnapshotListenerWithOptions:options
+ listener:^(FIRQuerySnapshot *snapshot, NSError *error) {
+ XCTAssertNil(error);
+ if (!snapshot.empty && !snapshot.metadata.fromCache) {
+ [testExpectiation fulfill];
+ }
+ }];
+
+ [firestore disableNetworkWithCompletion:^(NSError *error) {
+ XCTAssertNil(error);
+ [docRef setData:@{@"foo" : @"bar"}];
+ [firestore enableNetworkWithCompletion:^(NSError *error) {
+ XCTAssertNil(error);
+ }];
+ }];
+
+ [self awaitExpectations];
+}
+
+- (void)testQueriesFireFromCacheWhenOffline {
+ NSDictionary *testDocs = @{
+ @"a" : @{@"foo" : @1},
+ };
+ FIRCollectionReference *collection = [self collectionRefWithDocuments:testDocs];
+
+ FIRQueryListenOptions *options = [[[FIRQueryListenOptions options]
+ includeDocumentMetadataChanges:YES] includeQueryMetadataChanges:YES];
+ id<FIRListenerRegistration> registration =
+ [collection addSnapshotListenerWithOptions:options
+ listener:self.eventAccumulator.valueEventHandler];
+
+ FIRQuerySnapshot *querySnap = [self.eventAccumulator awaitEventWithName:@"initial event"];
+ XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), @[ @{ @"foo" : @1 } ]);
+ XCTAssertEqual(querySnap.metadata.isFromCache, NO);
+
+ [self disableNetwork];
+ querySnap = [self.eventAccumulator awaitEventWithName:@"offline event with isFromCache=YES"];
+ XCTAssertEqual(querySnap.metadata.isFromCache, YES);
+
+ // TODO(b/70631617): There's currently a backend bug that prevents us from using a resume token
+ // right away (against hexa at least). So we sleep. :-( :-( Anything over ~10ms seems to be
+ // sufficient.
+ [NSThread sleepForTimeInterval:0.2f];
+
+ [self enableNetwork];
+ querySnap = [self.eventAccumulator awaitEventWithName:@"back online event with isFromCache=NO"];
+ XCTAssertEqual(querySnap.metadata.isFromCache, NO);
+
+ [registration remove];
+}
+
+- (void)testCanHaveMultipleMutationsWhileOffline {
+ FIRCollectionReference *col = [self collectionRef];
+
+ // set a few docs to known values
+ NSDictionary *initialDocs =
+ @{ @"doc1" : @{@"key1" : @"value1"},
+ @"doc2" : @{@"key2" : @"value2"} };
+ [self writeAllDocuments:initialDocs toCollection:col];
+
+ // go offline for the rest of this test
+ [self disableNetwork];
+
+ // apply *multiple* mutations while offline
+ [[col documentWithPath:@"doc1"] setData:@{@"key1b" : @"value1b"}];
+ [[col documentWithPath:@"doc2"] setData:@{@"key2b" : @"value2b"}];
+
+ FIRQuerySnapshot *result = [self readDocumentSetForRef:col];
+ XCTAssertEqualObjects(FIRQuerySnapshotGetData(result), (@[
+ @{@"key1b" : @"value1b"},
+ @{@"key2b" : @"value2b"},
+ ]));
+}
+
@end
diff --git a/Firestore/Example/Tests/Integration/API/FIRServerTimestampTests.m b/Firestore/Example/Tests/Integration/API/FIRServerTimestampTests.m
deleted file mode 100644
index 2ee3966..0000000
--- a/Firestore/Example/Tests/Integration/API/FIRServerTimestampTests.m
+++ /dev/null
@@ -1,183 +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 FirebaseFirestore;
-
-#import <XCTest/XCTest.h>
-
-#import "Firestore/Source/Core/FSTFirestoreClient.h"
-
-#import "Firestore/Example/Tests/Util/FSTEventAccumulator.h"
-#import "Firestore/Example/Tests/Util/FSTIntegrationTestCase.h"
-
-@interface FIRServerTimestampTests : FSTIntegrationTestCase
-@end
-
-@implementation FIRServerTimestampTests {
- // Data written in tests via set.
- NSDictionary *_setData;
-
- // Base and update data used for update tests.
- NSDictionary *_initialData;
- NSDictionary *_updateData;
-
- // A document reference to read and write to.
- FIRDocumentReference *_docRef;
-
- // Accumulator used to capture events during the test.
- FSTEventAccumulator *_accumulator;
-
- // Listener registration for a listener maintained during the course of the test.
- id<FIRListenerRegistration> _listenerRegistration;
-}
-
-- (void)setUp {
- [super setUp];
-
- // Data written in tests via set.
- _setData = @{
- @"a" : @42,
- @"when" : [FIRFieldValue fieldValueForServerTimestamp],
- @"deep" : @{@"when" : [FIRFieldValue fieldValueForServerTimestamp]}
- };
-
- // Base and update data used for update tests.
- _initialData = @{ @"a" : @42 };
- _updateData = @{
- @"when" : [FIRFieldValue fieldValueForServerTimestamp],
- @"deep" : @{@"when" : [FIRFieldValue fieldValueForServerTimestamp]}
- };
-
- _docRef = [self documentRef];
- _accumulator = [FSTEventAccumulator accumulatorForTest:self];
- _listenerRegistration = [_docRef addSnapshotListener:_accumulator.handler];
-
- // Wait for initial nil snapshot to avoid potential races.
- FIRDocumentSnapshot *initialSnapshot = [_accumulator awaitEventWithName:@"initial event"];
- XCTAssertFalse(initialSnapshot.exists);
-}
-
-- (void)tearDown {
- [_listenerRegistration remove];
-
- [super tearDown];
-}
-
-// Returns the expected data, with an arbitrary timestamp substituted in.
-- (NSDictionary *)expectedDataWithTimestamp:(id _Nullable)timestamp {
- return @{ @"a" : @42, @"when" : timestamp, @"deep" : @{@"when" : timestamp} };
-}
-
-/** Writes _initialData and waits for the corresponding snapshot. */
-- (void)writeInitialData {
- [self writeDocumentRef:_docRef data:_initialData];
- FIRDocumentSnapshot *initialDataSnap = [_accumulator awaitEventWithName:@"Initial data event."];
- XCTAssertEqualObjects(initialDataSnap.data, _initialData);
-}
-
-/** Waits for a snapshot containing _setData but with NSNull for the timestamps. */
-- (void)waitForLocalEvent {
- FIRDocumentSnapshot *localSnap = [_accumulator awaitEventWithName:@"Local event."];
- XCTAssertEqualObjects(localSnap.data, [self expectedDataWithTimestamp:[NSNull null]]);
-}
-
-/** Waits for a snapshot containing _setData but with resolved server timestamps. */
-- (void)waitForRemoteEvent {
- // server event should have a resolved timestamp; verify it.
- FIRDocumentSnapshot *remoteSnap = [_accumulator awaitEventWithName:@"Remote event"];
- XCTAssertTrue(remoteSnap.exists);
- NSDate *when = remoteSnap[@"when"];
- XCTAssertTrue([when isKindOfClass:[NSDate class]]);
- // Tolerate up to 10 seconds of clock skew between client and server.
- XCTAssertEqualWithAccuracy(when.timeIntervalSinceNow, 0, 10);
-
- // Validate the rest of the document.
- XCTAssertEqualObjects(remoteSnap.data, [self expectedDataWithTimestamp:when]);
-}
-
-- (void)runTransactionBlock:(void (^)(FIRTransaction *transaction))transactionBlock {
- XCTestExpectation *expectation = [self expectationWithDescription:@"transaction complete"];
- [_docRef.firestore runTransactionWithBlock:^id(FIRTransaction *transaction, NSError **pError) {
- transactionBlock(transaction);
- return nil;
- }
- completion:^(id result, NSError *error) {
- XCTAssertNil(error);
- [expectation fulfill];
- }];
- [self awaitExpectations];
-}
-
-- (void)testServerTimestampsWorkViaSet {
- [self writeDocumentRef:_docRef data:_setData];
- [self waitForLocalEvent];
- [self waitForRemoteEvent];
-}
-
-- (void)testServerTimestampsWorkViaUpdate {
- [self writeInitialData];
- [self updateDocumentRef:_docRef data:_updateData];
- [self waitForLocalEvent];
- [self waitForRemoteEvent];
-}
-
-- (void)testServerTimestampsWorkViaTransactionSet {
- [self runTransactionBlock:^(FIRTransaction *transaction) {
- [transaction setData:_setData forDocument:_docRef];
- }];
-
- [self waitForRemoteEvent];
-}
-
-- (void)testServerTimestampsWorkViaTransactionUpdate {
- [self writeInitialData];
- [self runTransactionBlock:^(FIRTransaction *transaction) {
- [transaction updateData:_updateData forDocument:_docRef];
- }];
- [self waitForRemoteEvent];
-}
-
-- (void)testServerTimestampsFailViaUpdateOnNonexistentDocument {
- XCTestExpectation *expectation = [self expectationWithDescription:@"update complete"];
- [_docRef updateData:_updateData
- completion:^(NSError *error) {
- XCTAssertNotNil(error);
- XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain);
- XCTAssertEqual(error.code, FIRFirestoreErrorCodeNotFound);
- [expectation fulfill];
- }];
- [self awaitExpectations];
-}
-
-- (void)testServerTimestampsFailViaTransactionUpdateOnNonexistentDocument {
- XCTestExpectation *expectation = [self expectationWithDescription:@"transaction complete"];
- [_docRef.firestore runTransactionWithBlock:^id(FIRTransaction *transaction, NSError **pError) {
- [transaction updateData:_updateData forDocument:_docRef];
- return nil;
- }
- completion:^(id result, NSError *error) {
- XCTAssertNotNil(error);
- XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain);
- // TODO(b/35201829): This should be NotFound, but right now we retry transactions on any
- // error and so this turns into Aborted instead.
- // TODO(mikelehen): Actually it's FailedPrecondition, unlike Android. What do we want???
- XCTAssertEqual(error.code, FIRFirestoreErrorCodeFailedPrecondition);
- [expectation fulfill];
- }];
- [self awaitExpectations];
-}
-
-@end
diff --git a/Firestore/Example/Tests/Integration/API/FIRServerTimestampTests.mm b/Firestore/Example/Tests/Integration/API/FIRServerTimestampTests.mm
new file mode 100644
index 0000000..916ce7e
--- /dev/null
+++ b/Firestore/Example/Tests/Integration/API/FIRServerTimestampTests.mm
@@ -0,0 +1,318 @@
+/*
+ * 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 <FirebaseFirestore/FirebaseFirestore.h>
+
+#import <XCTest/XCTest.h>
+
+#import "Firestore/Example/Tests/Util/FSTEventAccumulator.h"
+#import "Firestore/Example/Tests/Util/FSTIntegrationTestCase.h"
+#import "Firestore/Source/API/FIRFirestore+Internal.h"
+#import "Firestore/Source/Core/FSTFirestoreClient.h"
+
+@interface FIRServerTimestampTests : FSTIntegrationTestCase
+@end
+
+@implementation FIRServerTimestampTests {
+ // Data written in tests via set.
+ NSDictionary *_setData;
+
+ // Base and update data used for update tests.
+ NSDictionary *_initialData;
+ NSDictionary *_updateData;
+
+ // A document reference to read and write to.
+ FIRDocumentReference *_docRef;
+
+ // Accumulator used to capture events during the test.
+ FSTEventAccumulator *_accumulator;
+
+ // Listener registration for a listener maintained during the course of the test.
+ id<FIRListenerRegistration> _listenerRegistration;
+
+ // Snapshot options that return the previous value for pending server timestamps.
+ FIRSnapshotOptions *_returnPreviousValue;
+ FIRSnapshotOptions *_returnEstimatedValue;
+}
+
+- (void)setUp {
+ [super setUp];
+
+ _returnPreviousValue =
+ [FIRSnapshotOptions serverTimestampBehavior:FIRServerTimestampBehaviorPrevious];
+ _returnEstimatedValue =
+ [FIRSnapshotOptions serverTimestampBehavior:FIRServerTimestampBehaviorEstimate];
+
+ // Data written in tests via set.
+ _setData = @{
+ @"a" : @42,
+ @"when" : [FIRFieldValue fieldValueForServerTimestamp],
+ @"deep" : @{@"when" : [FIRFieldValue fieldValueForServerTimestamp]}
+ };
+
+ // Base and update data used for update tests.
+ _initialData = @{ @"a" : @42 };
+ _updateData = @{
+ @"when" : [FIRFieldValue fieldValueForServerTimestamp],
+ @"deep" : @{@"when" : [FIRFieldValue fieldValueForServerTimestamp]}
+ };
+
+ _docRef = [self documentRef];
+ _accumulator = [FSTEventAccumulator accumulatorForTest:self];
+ _listenerRegistration = [_docRef addSnapshotListener:_accumulator.valueEventHandler];
+
+ // Wait for initial nil snapshot to avoid potential races.
+ FIRDocumentSnapshot *initialSnapshot = [_accumulator awaitEventWithName:@"initial event"];
+ XCTAssertFalse(initialSnapshot.exists);
+}
+
+- (void)tearDown {
+ [_listenerRegistration remove];
+
+ [super tearDown];
+}
+
+#pragma mark - Test Helpers
+
+/** Returns the expected data, with the specified timestamp substituted in. */
+- (NSDictionary *)expectedDataWithTimestamp:(nullable id)timestamp {
+ return @{ @"a" : @42, @"when" : timestamp, @"deep" : @{@"when" : timestamp} };
+}
+
+/** Writes _initialData and waits for the corresponding snapshot. */
+- (void)writeInitialData {
+ [self writeDocumentRef:_docRef data:_initialData];
+ FIRDocumentSnapshot *initialDataSnap = [_accumulator awaitEventWithName:@"Initial data event."];
+ XCTAssertEqualObjects(initialDataSnap.data, _initialData);
+}
+
+/** Waits for a snapshot with local writes. */
+- (FIRDocumentSnapshot *)waitForLocalEvent {
+ FIRDocumentSnapshot *snapshot;
+ do {
+ snapshot = [_accumulator awaitEventWithName:@"Local event."];
+ } while (!snapshot.metadata.hasPendingWrites);
+ return snapshot;
+}
+
+/** Waits for a snapshot that has no pending writes */
+- (FIRDocumentSnapshot *)waitForRemoteEvent {
+ FIRDocumentSnapshot *snapshot;
+ do {
+ snapshot = [_accumulator awaitEventWithName:@"Remote event."];
+ } while (snapshot.metadata.hasPendingWrites);
+ return snapshot;
+}
+
+/** Verifies a snapshot containing _setData but with NSNull for the timestamps. */
+- (void)verifyTimestampsAreNullInSnapshot:(FIRDocumentSnapshot *)snapshot {
+ XCTAssertEqualObjects(snapshot.data, [self expectedDataWithTimestamp:[NSNull null]]);
+}
+
+/** Verifies a snapshot containing _setData but with a local estimate for the timestamps. */
+- (void)verifyTimestampsAreEstimatedInSnapshot:(FIRDocumentSnapshot *)snapshot {
+ id timestamp = [snapshot valueForField:@"when" options:_returnEstimatedValue];
+ XCTAssertTrue([timestamp isKindOfClass:[NSDate class]]);
+ XCTAssertEqualObjects([snapshot dataWithOptions:_returnEstimatedValue],
+ [self expectedDataWithTimestamp:timestamp]);
+}
+
+/**
+ * Verifies a snapshot containing _setData but using the previous field value for server
+ * timestamps.
+ */
+- (void)verifyTimestampsInSnapshot:(FIRDocumentSnapshot *)snapshot
+ fromPreviousSnapshot:(nullable FIRDocumentSnapshot *)previousSnapshot {
+ if (previousSnapshot == nil) {
+ XCTAssertEqualObjects([snapshot dataWithOptions:_returnPreviousValue],
+ [self expectedDataWithTimestamp:[NSNull null]]);
+ } else {
+ XCTAssertEqualObjects([snapshot dataWithOptions:_returnPreviousValue],
+ [self expectedDataWithTimestamp:previousSnapshot[@"when"]]);
+ }
+}
+
+/** Verifies a snapshot containing _setData but with resolved server timestamps. */
+- (void)verifySnapshotWithResolvedTimestamps:(FIRDocumentSnapshot *)snapshot {
+ XCTAssertTrue(snapshot.exists);
+ NSDate *when = snapshot[@"when"];
+ XCTAssertTrue([when isKindOfClass:[NSDate class]]);
+ // Tolerate up to 10 seconds of clock skew between client and server.
+ XCTAssertEqualWithAccuracy(when.timeIntervalSinceNow, 0, 10);
+
+ // Validate the rest of the document.
+ XCTAssertEqualObjects(snapshot.data, [self expectedDataWithTimestamp:when]);
+}
+
+/** Runs a transaction block. */
+- (void)runTransactionBlock:(void (^)(FIRTransaction *transaction))transactionBlock {
+ XCTestExpectation *expectation = [self expectationWithDescription:@"transaction complete"];
+ [_docRef.firestore runTransactionWithBlock:^id(FIRTransaction *transaction, NSError **pError) {
+ transactionBlock(transaction);
+ return nil;
+ }
+ completion:^(id result, NSError *error) {
+ XCTAssertNil(error);
+ [expectation fulfill];
+ }];
+ [self awaitExpectations];
+}
+
+#pragma mark - Test Cases
+
+- (void)testServerTimestampsWorkViaSet {
+ [self writeDocumentRef:_docRef data:_setData];
+ [self verifyTimestampsAreNullInSnapshot:[self waitForLocalEvent]];
+ [self verifySnapshotWithResolvedTimestamps:[self waitForRemoteEvent]];
+}
+
+- (void)testServerTimestampsWorkViaUpdate {
+ [self writeInitialData];
+ [self updateDocumentRef:_docRef data:_updateData];
+ [self verifyTimestampsAreNullInSnapshot:[self waitForLocalEvent]];
+ [self verifySnapshotWithResolvedTimestamps:[self waitForRemoteEvent]];
+}
+
+- (void)testServerTimestampsWithEstimatedValue {
+ [self writeDocumentRef:_docRef data:_setData];
+ [self verifyTimestampsAreEstimatedInSnapshot:[self waitForLocalEvent]];
+ [self verifySnapshotWithResolvedTimestamps:[self waitForRemoteEvent]];
+}
+
+- (void)testServerTimestampsWithPreviousValue {
+ [self writeDocumentRef:_docRef data:_setData];
+ [self verifyTimestampsInSnapshot:[self waitForLocalEvent] fromPreviousSnapshot:nil];
+ FIRDocumentSnapshot *remoteSnapshot = [self waitForRemoteEvent];
+
+ [_docRef updateData:_updateData];
+ [self verifyTimestampsInSnapshot:[self waitForLocalEvent] fromPreviousSnapshot:remoteSnapshot];
+
+ [self verifySnapshotWithResolvedTimestamps:[self waitForRemoteEvent]];
+}
+
+- (void)testServerTimestampsWithPreviousValueOfDifferentType {
+ [self writeDocumentRef:_docRef data:_setData];
+ [self verifyTimestampsInSnapshot:[self waitForLocalEvent] fromPreviousSnapshot:nil];
+ [self verifySnapshotWithResolvedTimestamps:[self waitForRemoteEvent]];
+
+ [_docRef updateData:@{@"a" : [FIRFieldValue fieldValueForServerTimestamp]}];
+ FIRDocumentSnapshot *localSnapshot = [self waitForLocalEvent];
+ XCTAssertEqualObjects([localSnapshot valueForField:@"a"], [NSNull null]);
+ XCTAssertEqualObjects([localSnapshot valueForField:@"a" options:_returnPreviousValue], @42);
+ XCTAssertTrue([[localSnapshot valueForField:@"a" options:_returnEstimatedValue]
+ isKindOfClass:[NSDate class]]);
+
+ FIRDocumentSnapshot *remoteSnapshot = [self waitForRemoteEvent];
+ XCTAssertTrue([[remoteSnapshot valueForField:@"a"] isKindOfClass:[NSDate class]]);
+ XCTAssertTrue([[remoteSnapshot valueForField:@"a" options:_returnPreviousValue]
+ isKindOfClass:[NSDate class]]);
+ XCTAssertTrue([[remoteSnapshot valueForField:@"a" options:_returnEstimatedValue]
+ isKindOfClass:[NSDate class]]);
+}
+
+- (void)testServerTimestampsWithConsecutiveUpdates {
+ [self writeDocumentRef:_docRef data:_setData];
+ [self verifyTimestampsInSnapshot:[self waitForLocalEvent] fromPreviousSnapshot:nil];
+ [self verifySnapshotWithResolvedTimestamps:[self waitForRemoteEvent]];
+
+ [self disableNetwork];
+
+ [_docRef updateData:@{@"a" : [FIRFieldValue fieldValueForServerTimestamp]}];
+ FIRDocumentSnapshot *localSnapshot = [self waitForLocalEvent];
+ XCTAssertEqualObjects([localSnapshot valueForField:@"a" options:_returnPreviousValue], @42);
+
+ [_docRef updateData:@{@"a" : [FIRFieldValue fieldValueForServerTimestamp]}];
+ localSnapshot = [self waitForLocalEvent];
+ XCTAssertEqualObjects([localSnapshot valueForField:@"a" options:_returnPreviousValue], @42);
+
+ [self enableNetwork];
+
+ FIRDocumentSnapshot *remoteSnapshot = [self waitForRemoteEvent];
+ XCTAssertTrue([[remoteSnapshot valueForField:@"a"] isKindOfClass:[NSDate class]]);
+}
+
+- (void)testServerTimestampsPreviousValueFromLocalMutation {
+ [self writeDocumentRef:_docRef data:_setData];
+ [self verifyTimestampsInSnapshot:[self waitForLocalEvent] fromPreviousSnapshot:nil];
+ [self verifySnapshotWithResolvedTimestamps:[self waitForRemoteEvent]];
+
+ [self disableNetwork];
+
+ [_docRef updateData:@{@"a" : [FIRFieldValue fieldValueForServerTimestamp]}];
+ FIRDocumentSnapshot *localSnapshot = [self waitForLocalEvent];
+ XCTAssertEqualObjects([localSnapshot valueForField:@"a" options:_returnPreviousValue], @42);
+
+ [_docRef updateData:@{ @"a" : @1337 }];
+ localSnapshot = [self waitForLocalEvent];
+ XCTAssertEqualObjects([localSnapshot valueForField:@"a"], @1337);
+
+ [_docRef updateData:@{@"a" : [FIRFieldValue fieldValueForServerTimestamp]}];
+ localSnapshot = [self waitForLocalEvent];
+ XCTAssertEqualObjects([localSnapshot valueForField:@"a" options:_returnPreviousValue], @1337);
+
+ [self enableNetwork];
+
+ FIRDocumentSnapshot *remoteSnapshot = [self waitForRemoteEvent];
+ XCTAssertTrue([[remoteSnapshot valueForField:@"a"] isKindOfClass:[NSDate class]]);
+}
+
+- (void)testServerTimestampsWorkViaTransactionSet {
+ [self runTransactionBlock:^(FIRTransaction *transaction) {
+ [transaction setData:_setData forDocument:_docRef];
+ }];
+
+ [self verifySnapshotWithResolvedTimestamps:[self waitForRemoteEvent]];
+}
+
+- (void)testServerTimestampsWorkViaTransactionUpdate {
+ [self writeInitialData];
+ [self runTransactionBlock:^(FIRTransaction *transaction) {
+ [transaction updateData:_updateData forDocument:_docRef];
+ }];
+ [self verifySnapshotWithResolvedTimestamps:[self waitForRemoteEvent]];
+}
+
+- (void)testServerTimestampsFailViaUpdateOnNonexistentDocument {
+ XCTestExpectation *expectation = [self expectationWithDescription:@"update complete"];
+ [_docRef updateData:_updateData
+ completion:^(NSError *error) {
+ XCTAssertNotNil(error);
+ XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain);
+ XCTAssertEqual(error.code, FIRFirestoreErrorCodeNotFound);
+ [expectation fulfill];
+ }];
+ [self awaitExpectations];
+}
+
+- (void)testServerTimestampsFailViaTransactionUpdateOnNonexistentDocument {
+ XCTestExpectation *expectation = [self expectationWithDescription:@"transaction complete"];
+ [_docRef.firestore runTransactionWithBlock:^id(FIRTransaction *transaction, NSError **pError) {
+ [transaction updateData:_updateData forDocument:_docRef];
+ return nil;
+ }
+ completion:^(id result, NSError *error) {
+ XCTAssertNotNil(error);
+ XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain);
+ // TODO(b/35201829): This should be NotFound, but right now we retry transactions on any
+ // error and so this turns into Aborted instead.
+ // TODO(mikelehen): Actually it's FailedPrecondition, unlike Android. What do we want???
+ XCTAssertEqual(error.code, FIRFirestoreErrorCodeFailedPrecondition);
+ [expectation fulfill];
+ }];
+ [self awaitExpectations];
+}
+
+@end
diff --git a/Firestore/Example/Tests/Integration/API/FIRTypeTests.m b/Firestore/Example/Tests/Integration/API/FIRTypeTests.mm
index 638835f..5140b90 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 <FirebaseFirestore/FirebaseFirestore.h>
#import <XCTest/XCTest.h>
@@ -40,7 +40,8 @@
- (void)testCanReadAndWriteArrayFields {
[self assertSuccessfulRoundtrip:@{
- @"array" : @[ @1, @"foo", @{@"deep" : @YES}, [NSNull null] ]
+ @"array" : @[ @1, @"foo",
+ @{ @"deep" : @YES }, [NSNull null] ]
}];
}
diff --git a/Firestore/Example/Tests/Integration/API/FIRValidationTests.m b/Firestore/Example/Tests/Integration/API/FIRValidationTests.mm
index a318c47..49e572a 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 <FirebaseFirestore/FirebaseFirestore.h>
#import <XCTest/XCTest.h>
@@ -169,7 +169,7 @@
}
- (void)testWritesWithIndirectlyNestedArraysSucceed {
- NSDictionary<NSString *, id> *data = @{ @"nested-array" : @[ @1, @{@"foo" : @[ @2 ]} ] };
+ NSDictionary<NSString *, id> *data = @{ @"nested-array" : @[ @1, @{ @"foo" : @[ @2 ] } ] };
FIRDocumentReference *ref = [self documentRef];
FIRDocumentReference *ref2 = [self documentRef];
diff --git a/Firestore/Example/Tests/Integration/API/FIRWriteBatchTests.m b/Firestore/Example/Tests/Integration/API/FIRWriteBatchTests.mm
index 562c29f..9a2fef1 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 <FirebaseFirestore/FirebaseFirestore.h>
#import <XCTest/XCTest.h>
@@ -35,6 +35,29 @@
[self awaitExpectations];
}
+- (void)testCommitWithoutCompletionHandler {
+ FIRDocumentReference *doc = [self documentRef];
+ FIRWriteBatch *batch1 = [doc.firestore batch];
+ [batch1 setData:@{@"aa" : @"bb"} forDocument:doc];
+ [batch1 commitWithCompletion:nil];
+ FIRDocumentSnapshot *snapshot1 = [self readDocumentForRef:doc];
+ XCTAssertTrue(snapshot1.exists);
+ XCTAssertEqualObjects(snapshot1.data, @{@"aa" : @"bb"});
+
+ FIRWriteBatch *batch2 = [doc.firestore batch];
+ [batch2 setData:@{@"cc" : @"dd"} forDocument:doc];
+ [batch2 commit];
+
+ // TODO(b/70631617): There's currently a backend bug that prevents us from using a resume token
+ // right away (against hexa at least). So we sleep. :-( :-( Anything over ~10ms seems to be
+ // sufficient.
+ [NSThread sleepForTimeInterval:0.2f];
+
+ FIRDocumentSnapshot *snapshot2 = [self readDocumentForRef:doc];
+ XCTAssertTrue(snapshot2.exists);
+ XCTAssertEqualObjects(snapshot2.data, @{@"cc" : @"dd"});
+}
+
- (void)testSetDocuments {
FIRDocumentReference *doc = [self documentRef];
XCTestExpectation *batchExpectation = [self expectationWithDescription:@"batch written"];
@@ -131,7 +154,7 @@
FSTEventAccumulator *accumulator = [FSTEventAccumulator accumulatorForTest:self];
[collection addSnapshotListenerWithOptions:[[FIRQueryListenOptions options]
includeQueryMetadataChanges:YES]
- listener:accumulator.handler];
+ listener:accumulator.valueEventHandler];
FIRQuerySnapshot *initialSnap = [accumulator awaitEventWithName:@"initial event"];
XCTAssertEqual(initialSnap.count, 0);
@@ -161,7 +184,7 @@
FSTEventAccumulator *accumulator = [FSTEventAccumulator accumulatorForTest:self];
[collection addSnapshotListenerWithOptions:[[FIRQueryListenOptions options]
includeQueryMetadataChanges:YES]
- listener:accumulator.handler];
+ listener:accumulator.valueEventHandler];
FIRQuerySnapshot *initialSnap = [accumulator awaitEventWithName:@"initial event"];
XCTAssertEqual(initialSnap.count, 0);
@@ -195,7 +218,7 @@
FSTEventAccumulator *accumulator = [FSTEventAccumulator accumulatorForTest:self];
[collection addSnapshotListenerWithOptions:[[FIRQueryListenOptions options]
includeQueryMetadataChanges:YES]
- listener:accumulator.handler];
+ listener:accumulator.valueEventHandler];
FIRQuerySnapshot *initialSnap = [accumulator awaitEventWithName:@"initial event"];
XCTAssertEqual(initialSnap.count, 0);
@@ -227,7 +250,7 @@
FSTEventAccumulator *accumulator = [FSTEventAccumulator accumulatorForTest:self];
[doc
addSnapshotListenerWithOptions:[[FIRDocumentListenOptions options] includeMetadataChanges:YES]
- listener:accumulator.handler];
+ listener:accumulator.valueEventHandler];
FIRDocumentSnapshot *initialSnap = [accumulator awaitEventWithName:@"initial event"];
XCTAssertFalse(initialSnap.exists);
diff --git a/Firestore/Example/Tests/Integration/FSTDatastoreTests.m b/Firestore/Example/Tests/Integration/FSTDatastoreTests.mm
index 047f059..bf56367 100644
--- a/Firestore/Example/Tests/Integration/FSTDatastoreTests.m
+++ b/Firestore/Example/Tests/Integration/FSTDatastoreTests.mm
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-@import FirebaseFirestore;
+#import <FirebaseFirestore/FirebaseFirestore.h>
#import <GRPCClient/GRPCCall+ChannelCredentials.h>
#import <GRPCClient/GRPCCall+Tests.h>
diff --git a/Firestore/Example/Tests/Integration/FSTSmokeTests.m b/Firestore/Example/Tests/Integration/FSTSmokeTests.mm
index 847474a..cb726b8 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 <FirebaseFirestore/FirebaseFirestore.h>
#import <XCTest/XCTest.h>
@@ -48,7 +48,7 @@
[self writeDocumentRef:writerRef data:data];
id<FIRListenerRegistration> listenerRegistration =
- [readerRef addSnapshotListener:self.eventAccumulator.handler];
+ [readerRef addSnapshotListener:self.eventAccumulator.valueEventHandler];
FIRDocumentSnapshot *doc = [self.eventAccumulator awaitEventWithName:@"snapshot"];
XCTAssertEqual([doc class], [FIRDocumentSnapshot class]);
@@ -62,7 +62,7 @@
[self readerAndWriterOnDocumentRef:^(NSString *path, FIRDocumentReference *readerRef,
FIRDocumentReference *writerRef) {
id<FIRListenerRegistration> listenerRegistration =
- [readerRef addSnapshotListener:self.eventAccumulator.handler];
+ [readerRef addSnapshotListener:self.eventAccumulator.valueEventHandler];
FIRDocumentSnapshot *doc1 = [self.eventAccumulator awaitEventWithName:@"null snapshot"];
XCTAssertFalse(doc1.exists);
@@ -82,7 +82,7 @@
- (void)testWillFireValueEventsForEmptyCollections {
FIRCollectionReference *collection = [self.db collectionWithPath:@"empty-collection"];
id<FIRListenerRegistration> listenerRegistration =
- [collection addSnapshotListener:self.eventAccumulator.handler];
+ [collection addSnapshotListener:self.eventAccumulator.valueEventHandler];
FIRQuerySnapshot *snap = [self.eventAccumulator awaitEventWithName:@"empty query snapshot"];
XCTAssertEqual([snap class], [FIRQuerySnapshot class]);
diff --git a/Firestore/Example/Tests/Integration/FSTStreamTests.m b/Firestore/Example/Tests/Integration/FSTStreamTests.mm
index bbdf372..bbdf372 100644
--- a/Firestore/Example/Tests/Integration/FSTStreamTests.m
+++ b/Firestore/Example/Tests/Integration/FSTStreamTests.mm
diff --git a/Firestore/Example/Tests/Integration/FSTTransactionTests.m b/Firestore/Example/Tests/Integration/FSTTransactionTests.mm
index 2e828c9..21803ea 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 <FirebaseFirestore/FirebaseFirestore.h>
#import <XCTest/XCTest.h>
#include <libkern/OSAtomic.h>
diff --git a/Firestore/Example/Tests/Local/FSTEagerGarbageCollectorTests.m b/Firestore/Example/Tests/Local/FSTEagerGarbageCollectorTests.mm
index 1dd6d62..53f0202 100644
--- a/Firestore/Example/Tests/Local/FSTEagerGarbageCollectorTests.m
+++ b/Firestore/Example/Tests/Local/FSTEagerGarbageCollectorTests.mm
@@ -35,7 +35,7 @@ NS_ASSUME_NONNULL_BEGIN
FSTReferenceSet *referenceSet = [[FSTReferenceSet alloc] init];
[gc addGarbageSource:referenceSet];
- FSTDocumentKey *key = [FSTDocumentKey keyWithPathString:@"foo/bar"];
+ FSTDocumentKey *key = FSTTestDocKey(@"foo/bar");
[referenceSet addReferenceToKey:key forID:1];
FSTAssertEqualSets([gc collectGarbage], @[]);
XCTAssertFalse([referenceSet isEmpty]);
@@ -50,9 +50,9 @@ NS_ASSUME_NONNULL_BEGIN
FSTReferenceSet *referenceSet = [[FSTReferenceSet alloc] init];
[gc addGarbageSource:referenceSet];
- FSTDocumentKey *key1 = [FSTDocumentKey keyWithPathString:@"foo/bar"];
- FSTDocumentKey *key2 = [FSTDocumentKey keyWithPathString:@"foo/baz"];
- FSTDocumentKey *key3 = [FSTDocumentKey keyWithPathString:@"foo/blah"];
+ FSTDocumentKey *key1 = FSTTestDocKey(@"foo/bar");
+ FSTDocumentKey *key2 = FSTTestDocKey(@"foo/baz");
+ FSTDocumentKey *key3 = FSTTestDocKey(@"foo/blah");
[referenceSet addReferenceToKey:key1 forID:1];
[referenceSet addReferenceToKey:key2 forID:1];
[referenceSet addReferenceToKey:key3 forID:2];
@@ -77,12 +77,12 @@ NS_ASSUME_NONNULL_BEGIN
[gc addGarbageSource:localViews];
[gc addGarbageSource:mutations];
- FSTDocumentKey *key1 = [FSTDocumentKey keyWithPathString:@"foo/bar"];
+ FSTDocumentKey *key1 = FSTTestDocKey(@"foo/bar");
[remoteTargets addReferenceToKey:key1 forID:1];
[localViews addReferenceToKey:key1 forID:1];
[mutations addReferenceToKey:key1 forID:10];
- FSTDocumentKey *key2 = [FSTDocumentKey keyWithPathString:@"foo/baz"];
+ FSTDocumentKey *key2 = FSTTestDocKey(@"foo/baz");
[mutations addReferenceToKey:key2 forID:10];
XCTAssertFalse([remoteTargets isEmpty]);
diff --git a/Firestore/Example/Tests/Local/FSTLevelDBLocalStoreTests.m b/Firestore/Example/Tests/Local/FSTLevelDBLocalStoreTests.mm
index f71f5c9..f71f5c9 100644
--- a/Firestore/Example/Tests/Local/FSTLevelDBLocalStoreTests.m
+++ b/Firestore/Example/Tests/Local/FSTLevelDBLocalStoreTests.mm
diff --git a/Firestore/Example/Tests/Local/FSTLevelDBMigrationsTests.mm b/Firestore/Example/Tests/Local/FSTLevelDBMigrationsTests.mm
new file mode 100644
index 0000000..8ef0e94
--- /dev/null
+++ b/Firestore/Example/Tests/Local/FSTLevelDBMigrationsTests.mm
@@ -0,0 +1,75 @@
+/*
+ * 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 <XCTest/XCTest.h>
+#include <leveldb/db.h>
+
+#import "Firestore/Protos/objc/firestore/local/Target.pbobjc.h"
+#import "Firestore/Source/Local/FSTLevelDBMigrations.h"
+#import "Firestore/Source/Local/FSTLevelDBQueryCache.h"
+
+#import "Firestore/Example/Tests/Local/FSTPersistenceTestHelpers.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+using leveldb::DB;
+using leveldb::Options;
+using leveldb::Status;
+
+@interface FSTLevelDBMigrationsTests : XCTestCase
+@end
+
+@implementation FSTLevelDBMigrationsTests {
+ std::shared_ptr<DB> _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");
+}
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Firestore/Example/Tests/Local/FSTLevelDBMutationQueueTests.mm b/Firestore/Example/Tests/Local/FSTLevelDBMutationQueueTests.mm
index 6c26fd9..fe79598 100644
--- a/Firestore/Example/Tests/Local/FSTLevelDBMutationQueueTests.mm
+++ b/Firestore/Example/Tests/Local/FSTLevelDBMutationQueueTests.mm
@@ -19,7 +19,6 @@
#import <XCTest/XCTest.h>
#include <leveldb/db.h>
-#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"
@@ -29,6 +28,8 @@
#import "Firestore/Example/Tests/Local/FSTMutationQueueTests.h"
#import "Firestore/Example/Tests/Local/FSTPersistenceTestHelpers.h"
+#include "Firestore/core/src/firebase/firestore/util/ordered_code.h"
+
NS_ASSUME_NONNULL_BEGIN
using leveldb::DB;
@@ -36,7 +37,7 @@ using leveldb::Slice;
using leveldb::Status;
using leveldb::WriteOptions;
using Firestore::StringView;
-using Firestore::OrderedCode;
+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";
diff --git a/Firestore/Example/Tests/Local/FSTLevelDBQueryCacheTests.m b/Firestore/Example/Tests/Local/FSTLevelDBQueryCacheTests.mm
index 929ab9e..929ab9e 100644
--- a/Firestore/Example/Tests/Local/FSTLevelDBQueryCacheTests.m
+++ b/Firestore/Example/Tests/Local/FSTLevelDBQueryCacheTests.mm
diff --git a/Firestore/Example/Tests/Local/FSTLevelDBRemoteDocumentCacheTests.mm b/Firestore/Example/Tests/Local/FSTLevelDBRemoteDocumentCacheTests.mm
index 1f84aa6..638ab2f 100644
--- a/Firestore/Example/Tests/Local/FSTLevelDBRemoteDocumentCacheTests.mm
+++ b/Firestore/Example/Tests/Local/FSTLevelDBRemoteDocumentCacheTests.mm
@@ -18,16 +18,17 @@
#include <leveldb/db.h>
-#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
index 90f9ca3..95b9b11 100644
--- a/Firestore/Example/Tests/Local/FSTLocalSerializerTests.m
+++ b/Firestore/Example/Tests/Local/FSTLocalSerializerTests.mm
@@ -70,7 +70,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)testEncodesMutationBatch {
FSTMutation *set = FSTTestSetMutation(@"foo/bar", @{ @"a" : @"b", @"num" : @1 });
FSTMutation *patch = [[FSTPatchMutation alloc]
- initWithKey:[FSTDocumentKey keyWithPathString:@"bar/baz"]
+ initWithKey:FSTTestDocKey(@"bar/baz")
fieldMask:[[FSTFieldMask alloc] initWithFields:@[ FSTTestFieldPath(@"a") ]]
value:FSTTestObjectValue(
@{ @"a" : @"b",
@@ -157,6 +157,7 @@ NS_ASSUME_NONNULL_BEGIN
FSTQueryData *queryData = [[FSTQueryData alloc] initWithQuery:query
targetID:targetID
+ listenSequenceNumber:10
purpose:FSTQueryPurposeListen
snapshotVersion:version
resumeToken:resumeToken];
@@ -166,6 +167,7 @@ NS_ASSUME_NONNULL_BEGIN
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
index 245e1c4..45d1815 100644
--- a/Firestore/Example/Tests/Local/FSTLocalStoreTests.m
+++ b/Firestore/Example/Tests/Local/FSTLocalStoreTests.mm
@@ -196,8 +196,7 @@ FSTDocumentVersionDictionary *FSTVersionDictionary(FSTMutation *mutation,
NSEnumerator<NSString *> *keyPathEnumerator = keyPaths.objectEnumerator; \
[actual enumerateKeysAndObjectsUsingBlock:^(FSTDocumentKey * actualKey, \
FSTMaybeDocument * value, BOOL * stop) { \
- FSTDocumentKey *expectedKey = \
- [FSTDocumentKey keyWithPathString:[keyPathEnumerator nextObject]]; \
+ FSTDocumentKey *expectedKey = FSTTestDocKey([keyPathEnumerator nextObject]); \
XCTAssertEqualObjects(actualKey, expectedKey); \
XCTAssertTrue([value isKindOfClass:[FSTDeletedDocument class]]); \
}]; \
@@ -213,11 +212,11 @@ FSTDocumentVersionDictionary *FSTVersionDictionary(FSTMutation *mutation,
} while (0)
/** Asserts that the given local store does not contain the given document. */
-#define FSTAssertNotContains(keyPathString) \
- do { \
- FSTDocumentKey *key = [FSTDocumentKey keyWithPathString:keyPathString]; \
- FSTMaybeDocument *actual = [self.localStore readDocument:key]; \
- XCTAssertNil(actual); \
+#define FSTAssertNotContains(keyPathString) \
+ do { \
+ FSTDocumentKey *key = FSTTestDocKey(keyPathString); \
+ FSTMaybeDocument *actual = [self.localStore readDocument:key]; \
+ XCTAssertNil(actual); \
} while (0)
- (void)testMutationBatchKeys {
@@ -261,7 +260,7 @@ FSTDocumentVersionDictionary *FSTVersionDictionary(FSTMutation *mutation,
if ([self isTestBaseClass]) return;
// Start a query that requires acks to be held.
- FSTQuery *query = [FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"foo" ]]];
+ FSTQuery *query = FSTTestQuery(@"foo");
[self allocateQuery:query];
[self writeMutation:FSTTestSetMutation(@"foo/bar", @{@"foo" : @"bar"})];
@@ -554,7 +553,7 @@ FSTDocumentVersionDictionary *FSTVersionDictionary(FSTMutation *mutation,
- (void)testCollectsGarbageAfterChangeBatch {
if ([self isTestBaseClass]) return;
- FSTQuery *query = [FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"foo" ]]];
+ FSTQuery *query = FSTTestQuery(@"foo");
[self allocateQuery:query];
FSTAssertTargetID(2);
@@ -637,7 +636,7 @@ FSTDocumentVersionDictionary *FSTVersionDictionary(FSTMutation *mutation,
- (void)testPinsDocumentsInTheLocalView {
if ([self isTestBaseClass]) return;
- FSTQuery *query = [FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"foo" ]]];
+ FSTQuery *query = FSTTestQuery(@"foo");
[self allocateQuery:query];
FSTAssertTargetID(2);
@@ -685,7 +684,7 @@ FSTDocumentVersionDictionary *FSTVersionDictionary(FSTMutation *mutation,
FSTTestSetMutation(@"foo/baz", @{@"foo" : @"baz"}),
FSTTestSetMutation(@"foo/bar/Foo/Bar", @{@"Foo" : @"Bar"})
]];
- FSTQuery *query = [FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"foo", @"bar" ]]];
+ FSTQuery *query = FSTTestQuery(@"foo/bar");
FSTDocumentDictionary *docs = [self.localStore executeQuery:query];
XCTAssertEqualObjects([docs values], @[ FSTTestDoc(@"foo/bar", 0, @{@"foo" : @"bar"}, YES) ]);
}
@@ -700,7 +699,7 @@ FSTDocumentVersionDictionary *FSTVersionDictionary(FSTMutation *mutation,
FSTTestSetMutation(@"foo/bar/Foo/Bar", @{@"Foo" : @"Bar"}),
FSTTestSetMutation(@"fooo/blah", @{@"fooo" : @"blah"})
]];
- FSTQuery *query = [FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"foo" ]]];
+ FSTQuery *query = FSTTestQuery(@"foo");
FSTDocumentDictionary *docs = [self.localStore executeQuery:query];
XCTAssertEqualObjects([docs values], (@[
FSTTestDoc(@"foo/bar", 0, @{@"foo" : @"bar"}, YES),
@@ -711,7 +710,7 @@ FSTDocumentVersionDictionary *FSTVersionDictionary(FSTMutation *mutation,
- (void)testCanExecuteMixedCollectionQueries {
if ([self isTestBaseClass]) return;
- FSTQuery *query = [FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"foo" ]]];
+ FSTQuery *query = FSTTestQuery(@"foo");
[self allocateQuery:query];
FSTAssertTargetID(2);
@@ -736,7 +735,7 @@ FSTDocumentVersionDictionary *FSTVersionDictionary(FSTMutation *mutation,
// This test only works in the absence of the FSTEagerGarbageCollector.
[self restartWithNoopGarbageCollector];
- FSTQuery *query = [FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"foo", @"bar" ]]];
+ FSTQuery *query = FSTTestQuery(@"foo/bar");
FSTQueryData *queryData = [self.localStore allocateQuery:query];
FSTBoxedTargetID *targetID = @(queryData.targetID);
NSData *resumeToken = FSTTestResumeTokenFromSnapshotVersion(1000);
@@ -770,7 +769,7 @@ FSTDocumentVersionDictionary *FSTVersionDictionary(FSTMutation *mutation,
if ([self isTestBaseClass]) return;
[self restartWithNoopGarbageCollector];
- FSTQuery *query = [FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"foo" ]]];
+ FSTQuery *query = FSTTestQuery(@"foo");
[self allocateQuery:query];
FSTAssertTargetID(2);
diff --git a/Firestore/Example/Tests/Local/FSTMemoryLocalStoreTests.m b/Firestore/Example/Tests/Local/FSTMemoryLocalStoreTests.mm
index b78239e..b78239e 100644
--- a/Firestore/Example/Tests/Local/FSTMemoryLocalStoreTests.m
+++ b/Firestore/Example/Tests/Local/FSTMemoryLocalStoreTests.mm
diff --git a/Firestore/Example/Tests/Local/FSTMemoryMutationQueueTests.m b/Firestore/Example/Tests/Local/FSTMemoryMutationQueueTests.mm
index ab7afee..ab7afee 100644
--- a/Firestore/Example/Tests/Local/FSTMemoryMutationQueueTests.m
+++ b/Firestore/Example/Tests/Local/FSTMemoryMutationQueueTests.mm
diff --git a/Firestore/Example/Tests/Local/FSTMemoryQueryCacheTests.m b/Firestore/Example/Tests/Local/FSTMemoryQueryCacheTests.mm
index fb7df6b..fb7df6b 100644
--- a/Firestore/Example/Tests/Local/FSTMemoryQueryCacheTests.m
+++ b/Firestore/Example/Tests/Local/FSTMemoryQueryCacheTests.mm
diff --git a/Firestore/Example/Tests/Local/FSTMemoryRemoteDocumentCacheTests.m b/Firestore/Example/Tests/Local/FSTMemoryRemoteDocumentCacheTests.mm
index 162eef0..162eef0 100644
--- a/Firestore/Example/Tests/Local/FSTMemoryRemoteDocumentCacheTests.m
+++ b/Firestore/Example/Tests/Local/FSTMemoryRemoteDocumentCacheTests.mm
diff --git a/Firestore/Example/Tests/Local/FSTMutationQueueTests.m b/Firestore/Example/Tests/Local/FSTMutationQueueTests.mm
index f168ac9..020a0a7 100644
--- a/Firestore/Example/Tests/Local/FSTMutationQueueTests.m
+++ b/Firestore/Example/Tests/Local/FSTMutationQueueTests.mm
@@ -301,7 +301,7 @@ NS_ASSUME_NONNULL_BEGIN
[self.persistence commitGroup:group];
NSArray<FSTMutationBatch *> *expected = @[ batches[1], batches[2], batches[4] ];
- FSTQuery *query = [FSTQuery queryWithPath:FSTTestPath(@"foo")];
+ FSTQuery *query = FSTTestQuery(@"foo");
NSArray<FSTMutationBatch *> *matches =
[self.mutationQueue allMutationBatchesAffectingQuery:query];
diff --git a/Firestore/Example/Tests/Local/FSTPersistenceTestHelpers.h b/Firestore/Example/Tests/Local/FSTPersistenceTestHelpers.h
index 936bacf..5859d4b 100644
--- a/Firestore/Example/Tests/Local/FSTPersistenceTestHelpers.h
+++ b/Firestore/Example/Tests/Local/FSTPersistenceTestHelpers.h
@@ -24,6 +24,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
index c773b12..e9e129d 100644
--- a/Firestore/Example/Tests/Local/FSTPersistenceTestHelpers.m
+++ b/Firestore/Example/Tests/Local/FSTPersistenceTestHelpers.mm
@@ -26,10 +26,9 @@ 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 +39,18 @@ NS_ASSUME_NONNULL_BEGIN
format:@"Failed to clean up leveldb path %@: %@", dir, error];
}
}
+ return dir;
+}
+
++ (FSTLevelDB *)levelDBPersistence {
+ NSString *dir = [self levelDBDir];
FSTDatabaseID *databaseID = [FSTDatabaseID databaseIDWithProject:@"p" database:@"d"];
FSTSerializerBeta *remoteSerializer = [[FSTSerializerBeta alloc] initWithDatabaseID:databaseID];
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
index 1fed440..0c6a2a4 100644
--- a/Firestore/Example/Tests/Local/FSTQueryCacheTests.m
+++ b/Firestore/Example/Tests/Local/FSTQueryCacheTests.mm
@@ -31,12 +31,18 @@ NS_ASSUME_NONNULL_BEGIN
@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 @@ NS_ASSUME_NONNULL_BEGIN
- (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];
@@ -70,20 +76,18 @@ NS_ASSUME_NONNULL_BEGIN
// Type information is currently lost in our canonicalID implementations so this currently an
// easy way to force colliding canonicalIDs
- FSTQuery *q1 = [[FSTQuery queryWithPath:FSTTestPath(@"a")]
- queryByAddingFilter:FSTTestFilter(@"foo", @"==", @(1))];
- FSTQuery *q2 = [[FSTQuery queryWithPath:FSTTestPath(@"a")]
- queryByAddingFilter:FSTTestFilter(@"foo", @"==", @"1")];
+ FSTQuery *q1 = [FSTTestQuery(@"a") queryByAddingFilter:FSTTestFilter(@"foo", @"==", @(1))];
+ 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];
XCTAssertEqualObjects([self.queryCache queryDataForQuery:q1], data1);
@@ -101,10 +105,12 @@ NS_ASSUME_NONNULL_BEGIN
- (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];
@@ -117,7 +123,7 @@ NS_ASSUME_NONNULL_BEGIN
- (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];
@@ -129,7 +135,7 @@ NS_ASSUME_NONNULL_BEGIN
- (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]);
@@ -138,11 +144,11 @@ NS_ASSUME_NONNULL_BEGIN
- (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 = [FSTDocumentKey keyWithPathString:@"rooms/foo"];
- FSTDocumentKey *key2 = [FSTDocumentKey keyWithPathString:@"rooms/bar"];
+ FSTDocumentKey *key1 = FSTTestDocKey(@"rooms/foo");
+ FSTDocumentKey *key2 = FSTTestDocKey(@"rooms/bar");
[self addMatchingKey:key1 forTargetID:rooms.targetID];
[self addMatchingKey:key2 forTargetID:rooms.targetID];
@@ -157,7 +163,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)testAddOrRemoveMatchingKeys {
if ([self isTestBaseClass]) return;
- FSTDocumentKey *key = [FSTDocumentKey keyWithPathString:@"foo/bar"];
+ FSTDocumentKey *key = FSTTestDocKey(@"foo/bar");
XCTAssertFalse([self.queryCache containsKey:key]);
@@ -177,9 +183,9 @@ NS_ASSUME_NONNULL_BEGIN
- (void)testRemoveMatchingKeysForTargetID {
if ([self isTestBaseClass]) return;
- FSTDocumentKey *key1 = [FSTDocumentKey keyWithPathString:@"foo/bar"];
- FSTDocumentKey *key2 = [FSTDocumentKey keyWithPathString:@"foo/baz"];
- FSTDocumentKey *key3 = [FSTDocumentKey keyWithPathString:@"foo/blah"];
+ FSTDocumentKey *key1 = FSTTestDocKey(@"foo/bar");
+ FSTDocumentKey *key2 = FSTTestDocKey(@"foo/baz");
+ FSTDocumentKey *key3 = FSTTestDocKey(@"foo/blah");
[self addMatchingKey:key1 forTargetID:1];
[self addMatchingKey:key2 forTargetID:1];
@@ -206,16 +212,16 @@ NS_ASSUME_NONNULL_BEGIN
[garbageCollector addGarbageSource:self.queryCache];
FSTAssertEqualSets([garbageCollector collectGarbage], @[]);
- FSTQueryData *rooms = [self queryDataWithQuery:FSTTestQuery(@"rooms") targetID:1 version:1];
- FSTDocumentKey *room1 = [FSTDocumentKey keyWithPathString:@"rooms/bar"];
- FSTDocumentKey *room2 = [FSTDocumentKey keyWithPathString:@"rooms/foo"];
+ 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];
- FSTDocumentKey *hall1 = [FSTDocumentKey keyWithPathString:@"halls/bar"];
- FSTDocumentKey *hall2 = [FSTDocumentKey keyWithPathString:@"halls/foo"];
+ FSTQueryData *halls = [self queryDataWithQuery:FSTTestQuery(@"halls")];
+ FSTDocumentKey *hall1 = FSTTestDocKey(@"halls/bar");
+ FSTDocumentKey *hall2 = FSTTestDocKey(@"halls/foo");
[self addQueryData:halls];
[self addMatchingKey:hall1 forTargetID:halls.targetID];
[self addMatchingKey:hall2 forTargetID:halls.targetID];
@@ -235,9 +241,9 @@ NS_ASSUME_NONNULL_BEGIN
- (void)testMatchingKeysForTargetID {
if ([self isTestBaseClass]) return;
- FSTDocumentKey *key1 = [FSTDocumentKey keyWithPathString:@"foo/bar"];
- FSTDocumentKey *key2 = [FSTDocumentKey keyWithPathString:@"foo/baz"];
- FSTDocumentKey *key3 = [FSTDocumentKey keyWithPathString:@"foo/blah"];
+ FSTDocumentKey *key1 = FSTTestDocKey(@"foo/bar");
+ FSTDocumentKey *key2 = FSTTestDocKey(@"foo/baz");
+ FSTDocumentKey *key3 = FSTTestDocKey(@"foo/blah");
[self addMatchingKey:key1 forTargetID:1];
[self addMatchingKey:key2 forTargetID:1];
@@ -251,6 +257,46 @@ NS_ASSUME_NONNULL_BEGIN
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;
@@ -258,17 +304,19 @@ NS_ASSUME_NONNULL_BEGIN
FSTQueryData *query1 = [[FSTQueryData alloc] initWithQuery:FSTTestQuery(@"rooms")
targetID:1
+ listenSequenceNumber:10
purpose:FSTQueryPurposeListen];
- FSTDocumentKey *key1 = [FSTDocumentKey keyWithPathString:@"rooms/bar"];
- FSTDocumentKey *key2 = [FSTDocumentKey keyWithPathString:@"rooms/foo"];
+ FSTDocumentKey *key1 = FSTTestDocKey(@"rooms/bar");
+ FSTDocumentKey *key2 = FSTTestDocKey(@"rooms/foo");
[self addQueryData:query1];
[self addMatchingKey:key1 forTargetID:1];
[self addMatchingKey:key2 forTargetID:1];
FSTQueryData *query2 = [[FSTQueryData alloc] initWithQuery:FSTTestQuery(@"halls")
targetID:2
+ listenSequenceNumber:20
purpose:FSTQueryPurposeListen];
- FSTDocumentKey *key3 = [FSTDocumentKey keyWithPathString:@"halls/foo"];
+ FSTDocumentKey *key3 = FSTTestDocKey(@"halls/foo");
[self addQueryData:query2];
[self addMatchingKey:key3 forTargetID:2];
XCTAssertEqual([self.queryCache highestTargetID], 2);
@@ -280,6 +328,7 @@ NS_ASSUME_NONNULL_BEGIN
// 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);
@@ -321,12 +370,21 @@ NS_ASSUME_NONNULL_BEGIN
* 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
index 0b852a2..802117a 100644
--- a/Firestore/Example/Tests/Local/FSTReferenceSetTests.m
+++ b/Firestore/Example/Tests/Local/FSTReferenceSetTests.mm
@@ -18,6 +18,7 @@
#import <XCTest/XCTest.h>
+#import "Firestore/Example/Tests/Util/FSTHelpers.h"
#import "Firestore/Source/Model/FSTDocumentKey.h"
NS_ASSUME_NONNULL_BEGIN
@@ -28,7 +29,7 @@ NS_ASSUME_NONNULL_BEGIN
@implementation FSTReferenceSetTests
- (void)testAddOrRemoveReferences {
- FSTDocumentKey *key = [FSTDocumentKey keyWithPathString:@"foo/bar"];
+ FSTDocumentKey *key = FSTTestDocKey(@"foo/bar");
FSTReferenceSet *referenceSet = [[FSTReferenceSet alloc] init];
XCTAssertTrue([referenceSet isEmpty]);
@@ -53,9 +54,9 @@ NS_ASSUME_NONNULL_BEGIN
}
- (void)testRemoveAllReferencesForTargetID {
- FSTDocumentKey *key1 = [FSTDocumentKey keyWithPathString:@"foo/bar"];
- FSTDocumentKey *key2 = [FSTDocumentKey keyWithPathString:@"foo/baz"];
- FSTDocumentKey *key3 = [FSTDocumentKey keyWithPathString:@"foo/blah"];
+ FSTDocumentKey *key1 = FSTTestDocKey(@"foo/bar");
+ FSTDocumentKey *key2 = FSTTestDocKey(@"foo/baz");
+ FSTDocumentKey *key3 = FSTTestDocKey(@"foo/blah");
FSTReferenceSet *referenceSet = [[FSTReferenceSet alloc] init];
[referenceSet addReferenceToKey:key1 forID:1];
diff --git a/Firestore/Example/Tests/Local/FSTRemoteDocumentCacheTests.m b/Firestore/Example/Tests/Local/FSTRemoteDocumentCacheTests.mm
index 16fe3bf..d240604 100644
--- a/Firestore/Example/Tests/Local/FSTRemoteDocumentCacheTests.m
+++ b/Firestore/Example/Tests/Local/FSTRemoteDocumentCacheTests.mm
@@ -112,7 +112,7 @@ static const int kVersion = 42;
[self setTestDocumentAtPath:@"b/2"];
[self setTestDocumentAtPath:@"c/1"];
- FSTQuery *query = [FSTQuery queryWithPath:FSTTestPath(@"b")];
+ FSTQuery *query = FSTTestQuery(@"b");
FSTDocumentDictionary *results = [self.remoteDocumentCache documentsMatchingQuery:query];
NSArray *expected =
@[ FSTTestDoc(@"b/1", kVersion, _kDocData, NO), FSTTestDoc(@"b/2", kVersion, _kDocData, NO) ];
diff --git a/Firestore/Example/Tests/Local/FSTRemoteDocumentChangeBufferTests.m b/Firestore/Example/Tests/Local/FSTRemoteDocumentChangeBufferTests.mm
index 1970779..1970779 100644
--- a/Firestore/Example/Tests/Local/FSTRemoteDocumentChangeBufferTests.m
+++ b/Firestore/Example/Tests/Local/FSTRemoteDocumentChangeBufferTests.mm
diff --git a/Firestore/Example/Tests/Model/FSTDatabaseIDTests.m b/Firestore/Example/Tests/Model/FSTDatabaseIDTests.mm
index cb1b19d..cb1b19d 100644
--- a/Firestore/Example/Tests/Model/FSTDatabaseIDTests.m
+++ b/Firestore/Example/Tests/Model/FSTDatabaseIDTests.mm
diff --git a/Firestore/Example/Tests/Model/FSTDocumentKeyTests.m b/Firestore/Example/Tests/Model/FSTDocumentKeyTests.mm
index d66ee73..d66ee73 100644
--- a/Firestore/Example/Tests/Model/FSTDocumentKeyTests.m
+++ b/Firestore/Example/Tests/Model/FSTDocumentKeyTests.mm
diff --git a/Firestore/Example/Tests/Model/FSTDocumentSetTests.m b/Firestore/Example/Tests/Model/FSTDocumentSetTests.mm
index bf6cd21..fbaa5d6 100644
--- a/Firestore/Example/Tests/Model/FSTDocumentSetTests.m
+++ b/Firestore/Example/Tests/Model/FSTDocumentSetTests.mm
@@ -79,14 +79,6 @@ NS_ASSUME_NONNULL_BEGIN
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
index e56ab34..59f526d 100644
--- a/Firestore/Example/Tests/Model/FSTDocumentTests.m
+++ b/Firestore/Example/Tests/Model/FSTDocumentTests.mm
@@ -33,20 +33,20 @@ NS_ASSUME_NONNULL_BEGIN
@implementation FSTDocumentTests
- (void)testConstructor {
- FSTDocumentKey *key = [FSTDocumentKey keyWithPathString:@"messages/first"];
+ FSTDocumentKey *key = FSTTestDocKey(@"messages/first");
FSTSnapshotVersion *version = FSTTestVersion(1);
FSTObjectValue *data = FSTTestObjectValue(@{ @"a" : @1 });
FSTDocument *doc =
[FSTDocument documentWithData:data key:key version:version hasLocalMutations:NO];
- XCTAssertEqualObjects(doc.key, [FSTDocumentKey keyWithPathString:@"messages/first"]);
+ XCTAssertEqualObjects(doc.key, FSTTestDocKey(@"messages/first"));
XCTAssertEqualObjects(doc.version, version);
XCTAssertEqualObjects(doc.data, data);
XCTAssertEqual(doc.hasLocalMutations, NO);
}
- (void)testExtractsFields {
- FSTDocumentKey *key = [FSTDocumentKey keyWithPathString:@"rooms/eros"];
+ FSTDocumentKey *key = FSTTestDocKey(@"rooms/eros");
FSTSnapshotVersion *version = FSTTestVersion(1);
FSTObjectValue *data = FSTTestObjectValue(@{
@"desc" : @"Discuss all the project related stuff",
@@ -62,38 +62,31 @@ NS_ASSUME_NONNULL_BEGIN
}
- (void)testIsEqual {
- FSTDocumentKey *key1 = [FSTDocumentKey keyWithPathString:@"messages/first"];
- FSTDocumentKey *key2 = [FSTDocumentKey keyWithPathString:@"messages/second"];
- FSTObjectValue *data1 = FSTTestObjectValue(@{ @"a" : @1 });
- FSTObjectValue *data2 = FSTTestObjectValue(@{ @"b" : @1 });
- FSTSnapshotVersion *version1 = FSTTestVersion(1);
-
- FSTDocument *doc1 =
- [FSTDocument documentWithData:data1 key:key1 version:version1 hasLocalMutations:NO];
- FSTDocument *doc2 =
- [FSTDocument documentWithData:data1 key:key1 version:version1 hasLocalMutations:NO];
-
- XCTAssertEqualObjects(doc1, doc2);
- XCTAssertEqualObjects(
- doc1, [FSTDocument documentWithData:FSTTestObjectValue(
- @{ @"a" : @1 })
- key:[FSTDocumentKey keyWithPathString:@"messages/first"]
- version:version1
- hasLocalMutations:NO]);
-
- FSTSnapshotVersion *version2 = FSTTestVersion(2);
- XCTAssertNotEqualObjects(
- doc1, [FSTDocument documentWithData:data2 key:key1 version:version1 hasLocalMutations:NO]);
- XCTAssertNotEqualObjects(
- doc1, [FSTDocument documentWithData:data1 key:key2 version:version1 hasLocalMutations:NO]);
- XCTAssertNotEqualObjects(
- doc1, [FSTDocument documentWithData:data1 key:key1 version:version2 hasLocalMutations:NO]);
- XCTAssertNotEqualObjects(
- doc1, [FSTDocument documentWithData:data1 key:key1 version:version1 hasLocalMutations:YES]);
-
- XCTAssertEqualObjects(
- [FSTDocument documentWithData:data1 key:key1 version:version1 hasLocalMutations:YES],
- [FSTDocument documentWithData:data1 key:key1 version:version1 hasLocalMutations:5]);
+ XCTAssertEqualObjects(FSTTestDoc(@"messages/first", 1,
+ @{ @"a" : @1 }, NO),
+ FSTTestDoc(@"messages/first", 1,
+ @{ @"a" : @1 }, NO));
+ XCTAssertNotEqualObjects(FSTTestDoc(@"messages/first", 1,
+ @{ @"a" : @1 }, NO),
+ FSTTestDoc(@"messages/first", 1,
+ @{ @"b" : @1 }, NO));
+ XCTAssertNotEqualObjects(FSTTestDoc(@"messages/first", 1,
+ @{ @"a" : @1 }, NO),
+ FSTTestDoc(@"messages/second", 1,
+ @{ @"b" : @1 }, NO));
+ XCTAssertNotEqualObjects(FSTTestDoc(@"messages/first", 1,
+ @{ @"a" : @1 }, NO),
+ FSTTestDoc(@"messages/first", 2,
+ @{ @"a" : @1 }, NO));
+ XCTAssertNotEqualObjects(FSTTestDoc(@"messages/first", 1,
+ @{ @"a" : @1 }, NO),
+ FSTTestDoc(@"messages/first", 1,
+ @{ @"a" : @1 }, YES));
+
+ XCTAssertEqualObjects(FSTTestDoc(@"messages/first", 1,
+ @{ @"a" : @1 }, YES),
+ FSTTestDoc(@"messages/first", 1,
+ @{ @"a" : @1 }, 5));
}
@end
diff --git a/Firestore/Example/Tests/Model/FSTFieldValueTests.m b/Firestore/Example/Tests/Model/FSTFieldValueTests.mm
index acf95f0..56b885f 100644
--- a/Firestore/Example/Tests/Model/FSTFieldValueTests.m
+++ b/Firestore/Example/Tests/Model/FSTFieldValueTests.mm
@@ -16,9 +16,9 @@
#import "Firestore/Source/Model/FSTFieldValue.h"
+#import <FirebaseFirestore/FIRGeoPoint.h>
#import <XCTest/XCTest.h>
-#import "FirebaseFirestore/FIRGeoPoint.h"
#import "Firestore/Source/API/FIRFirestore+Internal.h"
#import "Firestore/Source/API/FSTUserDataConverter.h"
#import "Firestore/Source/Core/FSTTimestamp.h"
@@ -26,6 +26,7 @@
#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"
/** Helper to wrap the values in a set of equality groups using FSTTestFieldValue(). */
@@ -39,10 +40,12 @@ NSArray *FSTWrapGroups(NSArray *groups) {
// strings that can be used instead.
if ([value isEqual:@"server-timestamp-1"]) {
wrappedValue = [FSTServerTimestampValue
- serverTimestampValueWithLocalWriteTime:FSTTestTimestamp(2016, 5, 20, 10, 20, 0)];
+ serverTimestampValueWithLocalWriteTime:FSTTestTimestamp(2016, 5, 20, 10, 20, 0)
+ previousValue:nil];
} else if ([value isEqual:@"server-timestamp-2"]) {
wrappedValue = [FSTServerTimestampValue
- serverTimestampValueWithLocalWriteTime:FSTTestTimestamp(2016, 10, 21, 15, 32, 0)];
+ serverTimestampValueWithLocalWriteTime:FSTTestTimestamp(2016, 10, 21, 15, 32, 0)
+ previousValue:nil];
} else if ([value isKindOfClass:[FSTDocumentKeyReference class]]) {
// We directly convert these here so that the databaseIDs can be different.
FSTDocumentKeyReference *reference = (FSTDocumentKeyReference *)value;
@@ -441,12 +444,15 @@ union DoubleBits {
@[
// NOTE: ServerTimestampValues can't be parsed via FSTTestFieldValue().
[FSTServerTimestampValue
- serverTimestampValueWithLocalWriteTime:[FSTTimestamp timestampWithDate:date1]],
+ serverTimestampValueWithLocalWriteTime:[FSTTimestamp timestampWithDate:date1]
+ previousValue:nil],
[FSTServerTimestampValue
- serverTimestampValueWithLocalWriteTime:[FSTTimestamp timestampWithDate:date1]]
+ serverTimestampValueWithLocalWriteTime:[FSTTimestamp timestampWithDate:date1]
+ previousValue:nil]
],
@[ [FSTServerTimestampValue
- serverTimestampValueWithLocalWriteTime:[FSTTimestamp timestampWithDate:date2]] ],
+ serverTimestampValueWithLocalWriteTime:[FSTTimestamp timestampWithDate:date2]
+ previousValue:nil] ],
@[
FSTTestFieldValue(FSTTestGeoPoint(0, 1)),
[FSTGeoPointValue geoPointValue:FSTTestGeoPoint(0, 1)]
diff --git a/Firestore/Example/Tests/Model/FSTMutationTests.m b/Firestore/Example/Tests/Model/FSTMutationTests.mm
index 678755e..47fa9b3 100644
--- a/Firestore/Example/Tests/Model/FSTMutationTests.m
+++ b/Firestore/Example/Tests/Model/FSTMutationTests.mm
@@ -42,7 +42,7 @@
FSTDocument *baseDoc = FSTTestDoc(@"collection/key", 0, docData, NO);
FSTMutation *set = FSTTestSetMutation(@"collection/key", @{@"bar" : @"bar-value"});
- FSTMaybeDocument *setDoc = [set applyTo:baseDoc localWriteTime:_timestamp];
+ FSTMaybeDocument *setDoc = [set applyTo:baseDoc baseDocument:baseDoc localWriteTime:_timestamp];
NSDictionary *expectedData = @{@"bar" : @"bar-value"};
XCTAssertEqualObjects(setDoc, FSTTestDoc(@"collection/key", 0, expectedData, YES));
@@ -54,7 +54,8 @@
FSTMutation *patch =
FSTTestPatchMutation(@"collection/key", @{@"foo.bar" : @"new-bar-value"}, nil);
- FSTMaybeDocument *patchedDoc = [patch applyTo:baseDoc localWriteTime:_timestamp];
+ FSTMaybeDocument *patchedDoc =
+ [patch applyTo:baseDoc baseDocument:baseDoc localWriteTime:_timestamp];
NSDictionary *expectedData = @{ @"foo" : @{@"bar" : @"new-bar-value"}, @"baz" : @"baz-value" };
XCTAssertEqualObjects(patchedDoc, FSTTestDoc(@"collection/key", 0, expectedData, YES));
@@ -64,13 +65,14 @@
NSDictionary *docData = @{ @"foo" : @{@"bar" : @"bar-value", @"baz" : @"baz-value"} };
FSTDocument *baseDoc = FSTTestDoc(@"collection/key", 0, docData, NO);
- FSTDocumentKey *key = [FSTDocumentKey keyWithPathString:@"collection/key"];
+ FSTDocumentKey *key = FSTTestDocKey(@"collection/key");
FSTFieldMask *mask = [[FSTFieldMask alloc] initWithFields:@[ FSTTestFieldPath(@"foo.bar") ]];
FSTMutation *patch = [[FSTPatchMutation alloc] initWithKey:key
fieldMask:mask
value:[FSTObjectValue objectValue]
precondition:[FSTPrecondition none]];
- FSTMaybeDocument *patchedDoc = [patch applyTo:baseDoc localWriteTime:_timestamp];
+ FSTMaybeDocument *patchedDoc =
+ [patch applyTo:baseDoc baseDocument:baseDoc localWriteTime:_timestamp];
NSDictionary *expectedData = @{ @"foo" : @{@"baz" : @"baz-value"} };
XCTAssertEqualObjects(patchedDoc, FSTTestDoc(@"collection/key", 0, expectedData, YES));
@@ -82,7 +84,8 @@
FSTMutation *patch =
FSTTestPatchMutation(@"collection/key", @{@"foo.bar" : @"new-bar-value"}, nil);
- FSTMaybeDocument *patchedDoc = [patch applyTo:baseDoc localWriteTime:_timestamp];
+ FSTMaybeDocument *patchedDoc =
+ [patch applyTo:baseDoc baseDocument:baseDoc localWriteTime:_timestamp];
NSDictionary *expectedData = @{ @"foo" : @{@"bar" : @"new-bar-value"}, @"baz" : @"baz-value" };
XCTAssertEqualObjects(patchedDoc, FSTTestDoc(@"collection/key", 0, expectedData, YES));
@@ -91,7 +94,8 @@
- (void)testPatchingDeletedDocumentsDoesNothing {
FSTMaybeDocument *baseDoc = FSTTestDeletedDoc(@"collection/key", 0);
FSTMutation *patch = FSTTestPatchMutation(@"collection/key", @{@"foo" : @"bar"}, nil);
- FSTMaybeDocument *patchedDoc = [patch applyTo:baseDoc localWriteTime:_timestamp];
+ FSTMaybeDocument *patchedDoc =
+ [patch applyTo:baseDoc baseDocument:baseDoc localWriteTime:_timestamp];
XCTAssertEqualObjects(patchedDoc, baseDoc);
}
@@ -100,7 +104,8 @@
FSTDocument *baseDoc = FSTTestDoc(@"collection/key", 0, docData, NO);
FSTMutation *transform = FSTTestTransformMutation(@"collection/key", @[ @"foo.bar" ]);
- FSTMaybeDocument *transformedDoc = [transform applyTo:baseDoc localWriteTime:_timestamp];
+ FSTMaybeDocument *transformedDoc =
+ [transform applyTo:baseDoc baseDocument:baseDoc localWriteTime:_timestamp];
// Server timestamps aren't parsed, so we manually insert it.
FSTObjectValue *expectedData = FSTTestObjectValue(
@@ -108,7 +113,8 @@
@"baz" : @"baz-value" });
expectedData =
[expectedData objectBySettingValue:[FSTServerTimestampValue
- serverTimestampValueWithLocalWriteTime:_timestamp]
+ serverTimestampValueWithLocalWriteTime:_timestamp
+ previousValue:nil]
forPath:FSTTestFieldPath(@"foo.bar")];
FSTDocument *expectedDoc = [FSTDocument documentWithData:expectedData
@@ -129,8 +135,10 @@
initWithVersion:FSTTestVersion(1)
transformResults:@[ [FSTTimestampValue timestampValue:_timestamp] ]];
- FSTMaybeDocument *transformedDoc =
- [transform applyTo:baseDoc localWriteTime:_timestamp mutationResult:mutationResult];
+ FSTMaybeDocument *transformedDoc = [transform applyTo:baseDoc
+ baseDocument:baseDoc
+ localWriteTime:_timestamp
+ mutationResult:mutationResult];
NSDictionary *expectedData =
@{ @"foo" : @{@"bar" : _timestamp.approximateDateValue},
@@ -143,7 +151,8 @@
FSTDocument *baseDoc = FSTTestDoc(@"collection/key", 0, docData, NO);
FSTMutation *mutation = FSTTestDeleteMutation(@"collection/key");
- FSTMaybeDocument *result = [mutation applyTo:baseDoc localWriteTime:_timestamp];
+ FSTMaybeDocument *result =
+ [mutation applyTo:baseDoc baseDocument:baseDoc localWriteTime:_timestamp];
XCTAssertEqualObjects(result, FSTTestDeletedDoc(@"collection/key", 0));
}
@@ -154,8 +163,10 @@
FSTMutation *set = FSTTestSetMutation(@"collection/key", @{@"foo" : @"new-bar"});
FSTMutationResult *mutationResult =
[[FSTMutationResult alloc] initWithVersion:FSTTestVersion(4) transformResults:nil];
- FSTMaybeDocument *setDoc =
- [set applyTo:baseDoc localWriteTime:_timestamp mutationResult:mutationResult];
+ FSTMaybeDocument *setDoc = [set applyTo:baseDoc
+ baseDocument:baseDoc
+ localWriteTime:_timestamp
+ mutationResult:mutationResult];
NSDictionary *expectedData = @{@"foo" : @"new-bar"};
XCTAssertEqualObjects(setDoc, FSTTestDoc(@"collection/key", 0, expectedData, NO));
@@ -168,8 +179,10 @@
FSTMutation *patch = FSTTestPatchMutation(@"collection/key", @{@"foo" : @"new-bar"}, nil);
FSTMutationResult *mutationResult =
[[FSTMutationResult alloc] initWithVersion:FSTTestVersion(4) transformResults:nil];
- FSTMaybeDocument *patchedDoc =
- [patch applyTo:baseDoc localWriteTime:_timestamp mutationResult:mutationResult];
+ FSTMaybeDocument *patchedDoc = [patch applyTo:baseDoc
+ baseDocument:baseDoc
+ localWriteTime:_timestamp
+ mutationResult:mutationResult];
NSDictionary *expectedData = @{@"foo" : @"new-bar"};
XCTAssertEqualObjects(patchedDoc, FSTTestDoc(@"collection/key", 0, expectedData, NO));
@@ -179,8 +192,10 @@
do { \
FSTMutationResult *mutationResult = \
[[FSTMutationResult alloc] initWithVersion:FSTTestVersion(0) transformResults:nil]; \
- FSTMaybeDocument *actual = \
- [mutation applyTo:base localWriteTime:_timestamp mutationResult:mutationResult]; \
+ FSTMaybeDocument *actual = [mutation applyTo:base \
+ baseDocument:base \
+ localWriteTime:_timestamp \
+ mutationResult:mutationResult]; \
XCTAssertEqualObjects(actual, expected); \
} while (0);
diff --git a/Firestore/Example/Tests/Model/FSTPathTests.m b/Firestore/Example/Tests/Model/FSTPathTests.mm
index b8529e5..b8529e5 100644
--- a/Firestore/Example/Tests/Model/FSTPathTests.m
+++ b/Firestore/Example/Tests/Model/FSTPathTests.mm
diff --git a/Firestore/Example/Tests/Remote/FSTDatastoreTests.m b/Firestore/Example/Tests/Remote/FSTDatastoreTests.mm
index f3cc56f..6d6e912 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 <FirebaseFirestore/FIRFirestoreErrors.h>
#import <GRPCClient/GRPCCall.h>
#import <XCTest/XCTest.h>
diff --git a/Firestore/Example/Tests/Remote/FSTRemoteEventTests.m b/Firestore/Example/Tests/Remote/FSTRemoteEventTests.mm
index a947eb4..a947eb4 100644
--- a/Firestore/Example/Tests/Remote/FSTRemoteEventTests.m
+++ b/Firestore/Example/Tests/Remote/FSTRemoteEventTests.mm
diff --git a/Firestore/Example/Tests/Remote/FSTSerializerBetaTests.m b/Firestore/Example/Tests/Remote/FSTSerializerBetaTests.mm
index 528076f..de4a07a 100644
--- a/Firestore/Example/Tests/Remote/FSTSerializerBetaTests.m
+++ b/Firestore/Example/Tests/Remote/FSTSerializerBetaTests.mm
@@ -16,12 +16,12 @@
#import "Firestore/Source/Remote/FSTSerializerBeta.h"
+#import <FirebaseFirestore/FIRFieldPath.h>
+#import <FirebaseFirestore/FIRFirestoreErrors.h>
+#import <FirebaseFirestore/FIRGeoPoint.h>
#import <GRPCClient/GRPCCall.h>
#import <XCTest/XCTest.h>
-#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"
@@ -44,6 +44,7 @@
#import "Firestore/Source/Model/FSTPath.h"
#import "Firestore/Source/Remote/FSTWatchChange.h"
+#import "Firestore/Example/Tests/API/FSTAPIHelpers.h"
#import "Firestore/Example/Tests/Util/FSTHelpers.h"
NS_ASSUME_NONNULL_BEGIN
@@ -266,7 +267,8 @@ NS_ASSUME_NONNULL_BEGIN
@"i" : @1,
@"n" : [NSNull null],
@"s" : @"foo",
- @"a" : @[ @2, @"bar", @{@"b" : @NO} ],
+ @"a" : @[ @2, @"bar",
+ @{ @"b" : @NO } ],
@"o" : @{
@"d" : @100,
@"nested" : @{@"e" : @(LLONG_MIN)},
@@ -394,20 +396,25 @@ NS_ASSUME_NONNULL_BEGIN
- (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<NSString *, NSString *> *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"});
@@ -428,7 +435,7 @@ NS_ASSUME_NONNULL_BEGIN
#pragma mark - encodedQuery
- (void)testEncodesFirstLevelKeyQueries {
- FSTQuery *q = [FSTQuery queryWithPath:FSTTestPath(@"docs/1")];
+ FSTQuery *q = FSTTestQuery(@"docs/1");
FSTQueryData *model = [self queryDataForQuery:q];
GCFSTarget *expected = [GCFSTarget message];
@@ -439,7 +446,7 @@ NS_ASSUME_NONNULL_BEGIN
}
- (void)testEncodesFirstLevelAncestorQueries {
- FSTQuery *q = [FSTQuery queryWithPath:FSTTestPath(@"messages")];
+ FSTQuery *q = FSTTestQuery(@"messages");
FSTQueryData *model = [self queryDataForQuery:q];
GCFSTarget *expected = [GCFSTarget message];
@@ -455,7 +462,7 @@ NS_ASSUME_NONNULL_BEGIN
}
- (void)testEncodesNestedAncestorQueries {
- FSTQuery *q = [FSTQuery queryWithPath:FSTTestPath(@"rooms/1/messages/10/attachments")];
+ FSTQuery *q = FSTTestQuery(@"rooms/1/messages/10/attachments");
FSTQueryData *model = [self queryDataForQuery:q];
GCFSTarget *expected = [GCFSTarget message];
@@ -471,8 +478,7 @@ NS_ASSUME_NONNULL_BEGIN
}
- (void)testEncodesSingleFiltersAtFirstLevelCollections {
- FSTQuery *q = [[FSTQuery queryWithPath:FSTTestPath(@"docs")]
- queryByAddingFilter:FSTTestFilter(@"prop", @"<", @(42))];
+ FSTQuery *q = [FSTTestQuery(@"docs") queryByAddingFilter:FSTTestFilter(@"prop", @"<", @(42))];
FSTQueryData *model = [self queryDataForQuery:q];
GCFSTarget *expected = [GCFSTarget message];
@@ -495,7 +501,7 @@ NS_ASSUME_NONNULL_BEGIN
}
- (void)testEncodesMultipleFiltersOnDeeperCollections {
- FSTQuery *q = [[[FSTQuery queryWithPath:FSTTestPath(@"rooms/1/messages/10/attachments")]
+ FSTQuery *q = [[FSTTestQuery(@"rooms/1/messages/10/attachments")
queryByAddingFilter:FSTTestFilter(@"prop", @">=", @(42))]
queryByAddingFilter:FSTTestFilter(@"author", @"==", @"dimond")];
FSTQueryData *model = [self queryDataForQuery:q];
@@ -544,10 +550,8 @@ NS_ASSUME_NONNULL_BEGIN
}
- (void)unaryFilterTestWithValue:(id)value
- expectedUnaryOperator:(GCFSStructuredQuery_UnaryFilter_Operator)
- operator{
- FSTQuery *q = [[FSTQuery queryWithPath:FSTTestPath(@"docs")]
- queryByAddingFilter:FSTTestFilter(@"prop", @"==", value)];
+ expectedUnaryOperator:(GCFSStructuredQuery_UnaryFilter_Operator)op {
+ FSTQuery *q = [FSTTestQuery(@"docs") queryByAddingFilter:FSTTestFilter(@"prop", @"==", value)];
FSTQueryData *model = [self queryDataForQuery:q];
GCFSTarget *expected = [GCFSTarget message];
@@ -560,14 +564,14 @@ NS_ASSUME_NONNULL_BEGIN
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];
}
- (void)testEncodesSortOrders {
- FSTQuery *q = [[FSTQuery queryWithPath:FSTTestPath(@"docs")]
+ FSTQuery *q = [FSTTestQuery(@"docs")
queryByAddingSortOrder:[FSTSortOrder sortOrderWithFieldPath:FSTTestFieldPath(@"prop")
ascending:YES]];
FSTQueryData *model = [self queryDataForQuery:q];
@@ -587,7 +591,7 @@ NS_ASSUME_NONNULL_BEGIN
}
- (void)testEncodesSortOrdersDescending {
- FSTQuery *q = [[FSTQuery queryWithPath:FSTTestPath(@"rooms/1/messages/10/attachments")]
+ FSTQuery *q = [FSTTestQuery(@"rooms/1/messages/10/attachments")
queryByAddingSortOrder:[FSTSortOrder sortOrderWithFieldPath:FSTTestFieldPath(@"prop")
ascending:NO]];
FSTQueryData *model = [self queryDataForQuery:q];
@@ -607,7 +611,7 @@ NS_ASSUME_NONNULL_BEGIN
}
- (void)testEncodesLimits {
- FSTQuery *q = [[FSTQuery queryWithPath:FSTTestPath(@"docs")] queryBySettingLimit:26];
+ FSTQuery *q = [FSTTestQuery(@"docs") queryBySettingLimit:26];
FSTQueryData *model = [self queryDataForQuery:q];
GCFSTarget *expected = [GCFSTarget message];
@@ -624,9 +628,10 @@ NS_ASSUME_NONNULL_BEGIN
}
- (void)testEncodesResumeTokens {
- FSTQuery *q = [FSTQuery queryWithPath:FSTTestPath(@"docs")];
+ 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 +652,7 @@ NS_ASSUME_NONNULL_BEGIN
- (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
index 6bb314d..6bb314d 100644
--- a/Firestore/Example/Tests/Remote/FSTWatchChange+Testing.m
+++ b/Firestore/Example/Tests/Remote/FSTWatchChange+Testing.mm
diff --git a/Firestore/Example/Tests/Remote/FSTWatchChangeTests.m b/Firestore/Example/Tests/Remote/FSTWatchChangeTests.mm
index df2496b..df2496b 100644
--- a/Firestore/Example/Tests/Remote/FSTWatchChangeTests.m
+++ b/Firestore/Example/Tests/Remote/FSTWatchChangeTests.mm
diff --git a/Firestore/Example/Tests/SpecTests/FSTLevelDBSpecTests.m b/Firestore/Example/Tests/SpecTests/FSTLevelDBSpecTests.mm
index a67f667..a67f667 100644
--- a/Firestore/Example/Tests/SpecTests/FSTLevelDBSpecTests.m
+++ b/Firestore/Example/Tests/SpecTests/FSTLevelDBSpecTests.mm
diff --git a/Firestore/Example/Tests/SpecTests/FSTMemorySpecTests.m b/Firestore/Example/Tests/SpecTests/FSTMemorySpecTests.mm
index 3030ab5..3030ab5 100644
--- a/Firestore/Example/Tests/SpecTests/FSTMemorySpecTests.m
+++ b/Firestore/Example/Tests/SpecTests/FSTMemorySpecTests.mm
diff --git a/Firestore/Example/Tests/SpecTests/FSTMockDatastore.m b/Firestore/Example/Tests/SpecTests/FSTMockDatastore.mm
index 9a1d719..9a1d719 100644
--- a/Firestore/Example/Tests/SpecTests/FSTMockDatastore.m
+++ b/Firestore/Example/Tests/SpecTests/FSTMockDatastore.mm
diff --git a/Firestore/Example/Tests/SpecTests/FSTSpecTests.m b/Firestore/Example/Tests/SpecTests/FSTSpecTests.mm
index 2c1b8db..3335990 100644
--- a/Firestore/Example/Tests/SpecTests/FSTSpecTests.m
+++ b/Firestore/Example/Tests/SpecTests/FSTSpecTests.mm
@@ -16,9 +16,9 @@
#import "Firestore/Example/Tests/SpecTests/FSTSpecTests.h"
+#import <FirebaseFirestore/FIRFirestoreErrors.h>
#import <GRPCClient/GRPCCall.h>
-#import "FirebaseFirestore/FIRFirestoreErrors.h"
#import "Firestore/Source/Auth/FSTUser.h"
#import "Firestore/Source/Core/FSTEventManager.h"
#import "Firestore/Source/Core/FSTQuery.h"
@@ -103,11 +103,11 @@ static NSString *const kNoIOSTag = @"no-ios";
- (nullable FSTQuery *)parseQuery:(id)querySpec {
if ([querySpec isKindOfClass:[NSString class]]) {
- return [FSTQuery queryWithPath:[FSTResourcePath pathWithString:querySpec]];
+ return FSTTestQuery(querySpec);
} else if ([querySpec isKindOfClass:[NSDictionary class]]) {
NSDictionary *queryDict = (NSDictionary *)querySpec;
NSString *path = queryDict[@"path"];
- __block FSTQuery *query = [FSTQuery queryWithPath:[FSTResourcePath pathWithString:path]];
+ __block FSTQuery *query = FSTTestQuery(path);
if (queryDict[@"limit"]) {
NSNumber *limit = queryDict[@"limit"];
query = [query queryBySettingLimit:limit.integerValue];
@@ -156,7 +156,7 @@ static NSString *const kNoIOSTag = @"no-ios";
FSTTargetID actualID = [self.driver addUserListenerWithQuery:query];
FSTTargetID expectedID = [listenSpec[0] intValue];
- XCTAssertEqual(actualID, expectedID);
+ XCTAssertEqual(actualID, expectedID, @"targetID assigned to listen");
}
- (void)doUnlisten:(NSArray *)unlistenSpec {
@@ -237,7 +237,7 @@ static NSString *const kNoIOSTag = @"no-ios";
}
} else if (watchEntity[@"doc"]) {
NSArray *docSpec = watchEntity[@"doc"];
- FSTDocumentKey *key = [FSTDocumentKey keyWithPathString:docSpec[0]];
+ FSTDocumentKey *key = FSTTestDocKey(docSpec[0]);
FSTObjectValue *value = FSTTestObjectValue(docSpec[2]);
FSTSnapshotVersion *version = [self parseVersion:docSpec[1]];
FSTMaybeDocument *doc =
@@ -249,7 +249,7 @@ static NSString *const kNoIOSTag = @"no-ios";
document:doc];
[self.driver receiveWatchChange:change snapshotVersion:[self parseVersion:watchSnapshot]];
} else if (watchEntity[@"key"]) {
- FSTDocumentKey *docKey = [FSTDocumentKey keyWithPathString:watchEntity[@"key"]];
+ FSTDocumentKey *docKey = FSTTestDocKey(watchEntity[@"key"]);
FSTWatchChange *change =
[[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[]
removedTargetIDs:watchEntity[@"removedTargets"]
@@ -503,6 +503,7 @@ static NSString *const kNoIOSTag = @"no-ios";
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 3d031bd..46601d7 100644
--- a/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.h
+++ b/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.h
@@ -17,6 +17,7 @@
#import <Foundation/Foundation.h>
#import "Firestore/Source/Core/FSTTypes.h"
+#import "Firestore/Source/Remote/FSTRemoteStore.h"
@class FSTDocumentKey;
@class FSTMutation;
@@ -76,7 +77,7 @@ typedef NSDictionary<FSTUser *, NSArray<FSTOutstandingWrite *> *> FSTOutstanding
*
* Each method on the driver injects a different event into the system.
*/
-@interface FSTSyncEngineTestDriver : NSObject
+@interface FSTSyncEngineTestDriver : NSObject <FSTOnlineStateDelegate>
/**
* Initializes the underlying FSTSyncEngine with the given local persistence implementation and
diff --git a/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.m b/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.mm
index 896a292..a4de615 100644
--- a/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.m
+++ b/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.mm
@@ -16,9 +16,9 @@
#import "Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.h"
+#import <FirebaseFirestore/FIRFirestoreErrors.h>
#import <GRPCClient/GRPCCall.h>
-#import "FirebaseFirestore/FIRFirestoreErrors.h"
#import "Firestore/Source/Auth/FSTUser.h"
#import "Firestore/Source/Core/FSTEventManager.h"
#import "Firestore/Source/Core/FSTQuery.h"
@@ -119,7 +119,7 @@ NS_ASSUME_NONNULL_BEGIN
_remoteStore.syncEngine = _syncEngine;
_eventManager = [FSTEventManager eventManagerWithSyncEngine:_syncEngine];
- _remoteStore.onlineStateDelegate = _eventManager;
+ _remoteStore.onlineStateDelegate = self;
// Set up internal event tracking for the spec tests.
NSMutableArray<FSTQueryEvent *> *events = [NSMutableArray array];
@@ -139,6 +139,16 @@ NS_ASSUME_NONNULL_BEGIN
return self;
}
+- (NSDictionary<FSTUser *, NSMutableArray<FSTOutstandingWrite *> *> *)outstandingWrites {
+ return static_cast<NSDictionary<FSTUser *, NSMutableArray<FSTOutstandingWrite *> *> *>(
+ _outstandingWrites);
+}
+
+- (void)applyChangedOnlineState:(FSTOnlineState)onlineState {
+ [self.syncEngine applyChangedOnlineState:onlineState];
+ [self.eventManager applyChangedOnlineState:onlineState];
+}
+
- (void)start {
[self.localStore start];
[self.remoteStore start];
diff --git a/Firestore/Example/Tests/SpecTests/json/listen_spec_test.json b/Firestore/Example/Tests/SpecTests/json/listen_spec_test.json
index e607710..7bfe557 100644
--- a/Firestore/Example/Tests/SpecTests/json/listen_spec_test.json
+++ b/Firestore/Example/Tests/SpecTests/json/listen_spec_test.json
@@ -1608,7 +1608,19 @@
"stateExpect": {
"activeTargets": {},
"limboDocs": []
- }
+ },
+ "expect": [
+ {
+ "query": {
+ "path": "collection",
+ "filters": [],
+ "orderBys": []
+ },
+ "errorCode": 0,
+ "fromCache": true,
+ "hasPendingWrites": false
+ }
+ ]
},
{
"enableNetwork": true,
diff --git a/Firestore/Example/Tests/SpecTests/json/offline_spec_test.json b/Firestore/Example/Tests/SpecTests/json/offline_spec_test.json
index f542a6e..6cbbc80 100644
--- a/Firestore/Example/Tests/SpecTests/json/offline_spec_test.json
+++ b/Firestore/Example/Tests/SpecTests/json/offline_spec_test.json
@@ -294,5 +294,424 @@
]
}
]
+ },
+ "Queries revert to fromCache=true when offline.": {
+ "describeName": "Offline:",
+ "itName": "Queries revert to fromCache=true when 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
+ }
+ ]
+ },
+ {
+ "watchStreamClose": {
+ "error": {
+ "code": 14,
+ "message": "Simulated Backend Error"
+ }
+ },
+ "stateExpect": {
+ "activeTargets": {
+ "2": {
+ "query": {
+ "path": "collection",
+ "filters": [],
+ "orderBys": []
+ },
+ "resumeToken": "resume-token-1000"
+ }
+ }
+ }
+ },
+ {
+ "watchStreamClose": {
+ "error": {
+ "code": 14,
+ "message": "Simulated Backend Error"
+ }
+ }
+ },
+ {
+ "watchStreamClose": {
+ "error": {
+ "code": 14,
+ "message": "Simulated Backend Error"
+ }
+ },
+ "expect": [
+ {
+ "query": {
+ "path": "collection",
+ "filters": [],
+ "orderBys": []
+ },
+ "errorCode": 0,
+ "fromCache": true,
+ "hasPendingWrites": false
+ }
+ ]
+ },
+ {
+ "watchAck": [
+ 2
+ ]
+ },
+ {
+ "watchEntity": {
+ "docs": [],
+ "targets": [
+ 2
+ ]
+ }
+ },
+ {
+ "watchCurrent": [
+ [
+ 2
+ ],
+ "resume-token-1000"
+ ],
+ "watchSnapshot": 1000,
+ "expect": [
+ {
+ "query": {
+ "path": "collection",
+ "filters": [],
+ "orderBys": []
+ },
+ "errorCode": 0,
+ "fromCache": false,
+ "hasPendingWrites": false
+ }
+ ]
+ }
+ ]
+ },
+ "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
index 0cba03f..0cba03f 100644
--- a/Firestore/Example/Tests/Util/FSTAssertTests.m
+++ b/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 5632e64..0000000
--- 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 <XCTest/XCTest.h>
-
-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: <https://en.wikipedia.org/wiki/Denormal_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/FSTEventAccumulator.h b/Firestore/Example/Tests/Util/FSTEventAccumulator.h
index ae5392c..baa501b 100644
--- a/Firestore/Example/Tests/Util/FSTEventAccumulator.h
+++ b/Firestore/Example/Tests/Util/FSTEventAccumulator.h
@@ -23,7 +23,7 @@
NS_ASSUME_NONNULL_BEGIN
-typedef void (^FSTGenericEventHandler)(id _Nullable, NSError *error);
+typedef void (^FSTValueEventHandler)(id _Nullable, NSError *_Nullable error);
@interface FSTEventAccumulator : NSObject
@@ -35,7 +35,8 @@ typedef void (^FSTGenericEventHandler)(id _Nullable, NSError *error);
- (NSArray<id> *)awaitEvents:(NSUInteger)events name:(NSString *)name;
-@property(nonatomic, strong, readonly) FSTGenericEventHandler handler;
+@property(nonatomic, strong, readonly) FSTValueEventHandler valueEventHandler;
+
@end
NS_ASSUME_NONNULL_END
diff --git a/Firestore/Example/Tests/Util/FSTEventAccumulator.m b/Firestore/Example/Tests/Util/FSTEventAccumulator.mm
index b44ec67..c4c1602 100644
--- a/Firestore/Example/Tests/Util/FSTEventAccumulator.m
+++ b/Firestore/Example/Tests/Util/FSTEventAccumulator.mm
@@ -68,9 +68,8 @@ NS_ASSUME_NONNULL_BEGIN
return events[0];
}
-// Overrides the handler property
-- (void (^)(id _Nullable, NSError *))handler {
- return ^void(id _Nullable value, NSError *error) {
+- (void (^)(id _Nullable, NSError *_Nullable))valueEventHandler {
+ return ^void(id _Nullable value, NSError *_Nullable error) {
// We can't store nil in the _events array, but these are still interesting to tests so store
// NSNull instead.
id event = value ? value : [NSNull null];
diff --git a/Firestore/Example/Tests/Util/FSTHelpers.h b/Firestore/Example/Tests/Util/FSTHelpers.h
index 91ccbcf..4dbf910 100644
--- a/Firestore/Example/Tests/Util/FSTHelpers.h
+++ b/Firestore/Example/Tests/Util/FSTHelpers.h
@@ -16,7 +16,6 @@
#import <Foundation/Foundation.h>
-#import "Firestore/Source/API/FIRDocumentReference+Internal.h"
#import "Firestore/Source/Core/FSTTypes.h"
#import "Firestore/Source/Model/FSTDocumentDictionary.h"
#import "Firestore/Source/Model/FSTDocumentKeySet.h"
@@ -33,7 +32,6 @@
@class FSTPatchMutation;
@class FSTQuery;
@class FSTRemoteEvent;
-@class FSTResourceName;
@class FSTResourcePath;
@class FSTSetMutation;
@class FSTSnapshotVersion;
@@ -145,6 +143,8 @@ NSDate *FSTTestDate(int year, int month, int day, int hour, int minute, int seco
*/
NSData *FSTTestData(int bytes, ...);
+// Note that FIRGeoPoint is a model class in addition to an API class, so we put this helper here
+// instead of FSTAPIHelpers.h
/** Creates a new GeoPoint from the latitude and longitude values */
FIRGeoPoint *FSTTestGeoPoint(double latitude, double longitude);
diff --git a/Firestore/Example/Tests/Util/FSTHelpers.m b/Firestore/Example/Tests/Util/FSTHelpers.mm
index f01bddb..64fe213 100644
--- a/Firestore/Example/Tests/Util/FSTHelpers.m
+++ b/Firestore/Example/Tests/Util/FSTHelpers.mm
@@ -16,14 +16,18 @@
#import "Firestore/Example/Tests/Util/FSTHelpers.h"
-#import "FirebaseFirestore/FIRFieldPath.h"
-#import "FirebaseFirestore/FIRGeoPoint.h"
+#include <inttypes.h>
+
+#import <FirebaseFirestore/FIRFieldPath.h>
+#import <FirebaseFirestore/FIRGeoPoint.h>
+
#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"
@@ -138,7 +142,7 @@ FSTDocument *FSTTestDoc(NSString *path,
FSTTestSnapshotVersion version,
NSDictionary<NSString *, id> *data,
BOOL hasMutations) {
- FSTDocumentKey *key = [FSTDocumentKey keyWithPathString:path];
+ FSTDocumentKey *key = FSTTestDocKey(path);
return [FSTDocument documentWithData:FSTTestObjectValue(data)
key:key
version:FSTTestVersion(version)
@@ -146,7 +150,7 @@ FSTDocument *FSTTestDoc(NSString *path,
}
FSTDeletedDocument *FSTTestDeletedDoc(NSString *path, FSTTestSnapshotVersion version) {
- FSTDocumentKey *key = [FSTDocumentKey keyWithPathString:path];
+ FSTDocumentKey *key = FSTTestDocKey(path);
return [FSTDeletedDocument documentWithKey:key version:FSTTestVersion(version)];
}
@@ -214,7 +218,7 @@ FSTSortOrder *FSTTestOrderBy(NSString *field, NSString *direction) {
}
NSComparator FSTTestDocComparator(NSString *fieldPath) {
- FSTQuery *query = [[FSTQuery queryWithPath:[FSTResourcePath pathWithSegments:@[ @"docs" ]]]
+ FSTQuery *query = [FSTTestQuery(@"docs")
queryByAddingSortOrder:[FSTSortOrder sortOrderWithFieldPath:FSTTestFieldPath(fieldPath)
ascending:YES]];
return [query comparator];
@@ -229,7 +233,7 @@ FSTDocumentSet *FSTTestDocSet(NSComparator comp, NSArray<FSTDocument *> *docs) {
}
FSTSetMutation *FSTTestSetMutation(NSString *path, NSDictionary<NSString *, id> *values) {
- return [[FSTSetMutation alloc] initWithKey:[FSTDocumentKey keyWithPathString:path]
+ return [[FSTSetMutation alloc] initWithKey:FSTTestDocKey(path)
value:FSTTestObjectValue(values)
precondition:[FSTPrecondition none]];
}
@@ -274,7 +278,7 @@ FSTTransformMutation *FSTTestTransformMutation(NSString *path,
}
FSTDeleteMutation *FSTTestDeleteMutation(NSString *path) {
- return [[FSTDeleteMutation alloc] initWithKey:[FSTDocumentKey keyWithPathString:path]
+ return [[FSTDeleteMutation alloc] initWithKey:FSTTestDocKey(path)
precondition:[FSTPrecondition none]];
}
@@ -334,12 +338,12 @@ FSTLocalViewChanges *FSTTestViewChanges(FSTQuery *query,
NSArray<NSString *> *removedKeys) {
FSTDocumentKeySet *added = [FSTDocumentKeySet keySet];
for (NSString *keyPath in addedKeys) {
- FSTDocumentKey *key = [FSTDocumentKey keyWithPathString:keyPath];
+ FSTDocumentKey *key = FSTTestDocKey(keyPath);
added = [added setByAddingObject:key];
}
FSTDocumentKeySet *removed = [FSTDocumentKeySet keySet];
for (NSString *keyPath in removedKeys) {
- FSTDocumentKey *key = [FSTDocumentKey keyWithPathString:keyPath];
+ FSTDocumentKey *key = FSTTestDocKey(keyPath);
removed = [removed setByAddingObject:key];
}
return [FSTLocalViewChanges changesForQuery:query addedKeys:added removedKeys:removed];
diff --git a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.h b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.h
index 88f9346..e1820e2 100644
--- a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.h
+++ b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.h
@@ -18,6 +18,7 @@
#import <XCTest/XCTest.h>
#import "Firestore/Example/Tests/Util/XCTestCase+Await.h"
+#import "Firestore/Source/Core/FSTTypes.h"
@class FIRCollectionReference;
@class FIRDocumentSnapshot;
@@ -82,6 +83,10 @@ extern "C" {
- (void)deleteDocumentRef:(FIRDocumentReference *)ref;
+- (void)disableNetwork;
+
+- (void)enableNetwork;
+
/**
* "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 3d30a77..ef15056 100644
--- a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm
+++ b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm
@@ -25,6 +25,7 @@
#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"
@@ -158,11 +159,7 @@ NS_ASSUME_NONNULL_BEGIN
}
- (void)shutdownFirestore:(FIRFirestore *)firestore {
- XCTestExpectation *shutdownCompletion = [self expectationWithDescription:@"shutdown"];
- [firestore shutdownWithCompletion:^(NSError *_Nullable error) {
- XCTAssertNil(error);
- [shutdownCompletion fulfill];
- }];
+ [firestore shutdownWithCompletion:[self completionForExpectationWithName:@"shutdown"]];
[self awaitExpectations];
}
@@ -261,31 +258,29 @@ NS_ASSUME_NONNULL_BEGIN
}
- (void)writeDocumentRef:(FIRDocumentReference *)ref data:(NSDictionary<NSString *, id> *)data {
- XCTestExpectation *expectation = [self expectationWithDescription:@"setData"];
- [ref setData:data
- completion:^(NSError *_Nullable error) {
- XCTAssertNil(error);
- [expectation fulfill];
- }];
+ [ref setData:data completion:[self completionForExpectationWithName:@"setData"]];
[self awaitExpectations];
}
- (void)updateDocumentRef:(FIRDocumentReference *)ref data:(NSDictionary<id, id> *)data {
- XCTestExpectation *expectation = [self expectationWithDescription:@"updateData"];
- [ref updateData:data
- completion:^(NSError *_Nullable error) {
- XCTAssertNil(error);
- [expectation fulfill];
- }];
+ [ref updateData:data completion:[self completionForExpectationWithName:@"updateData"]];
[self awaitExpectations];
}
- (void)deleteDocumentRef:(FIRDocumentReference *)ref {
- XCTestExpectation *expectation = [self expectationWithDescription:@"deleteDocument"];
- [ref deleteDocumentWithCompletion:^(NSError *_Nullable error) {
- XCTAssertNil(error);
- [expectation fulfill];
- }];
+ [ref deleteDocumentWithCompletion:[self completionForExpectationWithName:@"deleteDocument"]];
+ [self awaitExpectations];
+}
+
+- (void)disableNetwork {
+ [self.db.client
+ disableNetworkWithCompletion:[self completionForExpectationWithName:@"Disable Network."]];
+ [self awaitExpectations];
+}
+
+- (void)enableNetwork {
+ [self.db.client
+ enableNetworkWithCompletion:[self completionForExpectationWithName:@"Enable Network."]];
[self awaitExpectations];
}
diff --git a/Firestore/Example/Tests/Util/FSTTestDispatchQueue.m b/Firestore/Example/Tests/Util/FSTTestDispatchQueue.mm
index 8124cf2..8124cf2 100644
--- a/Firestore/Example/Tests/Util/FSTTestDispatchQueue.m
+++ b/Firestore/Example/Tests/Util/FSTTestDispatchQueue.mm
diff --git a/Firestore/Example/Tests/Util/XCTestCase+Await.h b/Firestore/Example/Tests/Util/XCTestCase+Await.h
index 9d575f9..1afe8c0 100644
--- a/Firestore/Example/Tests/Util/XCTestCase+Await.h
+++ b/Firestore/Example/Tests/Util/XCTestCase+Await.h
@@ -16,6 +16,8 @@
#import <XCTest/XCTest.h>
+#import "Firestore/Source/Core/FSTTypes.h"
+
@interface XCTestCase (Await)
/**
@@ -29,4 +31,10 @@
*/
- (double)defaultExpectationWaitSeconds;
+/**
+ * Returns a completion block that fulfills a newly-created expectation with the specified
+ * name.
+ */
+- (FSTVoidErrorBlock)completionForExpectationWithName:(NSString *)expectationName;
+
@end
diff --git a/Firestore/Example/Tests/Util/XCTestCase+Await.m b/Firestore/Example/Tests/Util/XCTestCase+Await.mm
index 15c67ca..a5fefc9 100644
--- a/Firestore/Example/Tests/Util/XCTestCase+Await.m
+++ b/Firestore/Example/Tests/Util/XCTestCase+Await.mm
@@ -18,7 +18,9 @@
#import <Foundation/Foundation.h>
-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)
@@ -35,4 +37,12 @@ static const double kExpectationWaitSeconds = 10.0;
return kExpectationWaitSeconds;
}
+- (FSTVoidErrorBlock)completionForExpectationWithName:(NSString *)expectationName {
+ XCTestExpectation *expectation = [self expectationWithDescription:expectationName];
+ return ^(NSError *error) {
+ XCTAssertNil(error);
+ [expectation fulfill];
+ };
+}
+
@end
diff --git a/Firestore/Port/absl/absl_attributes.h b/Firestore/Port/absl/absl_attributes.h
deleted file mode 100644
index d43930c..0000000
--- a/Firestore/Port/absl/absl_attributes.h
+++ /dev/null
@@ -1,644 +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.
- */
-
-// Various macros for C++ attributes
-// Most macros here are exposing GCC or Clang features, and are stubbed out for
-// other compilers.
-// GCC attributes documentation:
-// https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Function-Attributes.html
-// https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Variable-Attributes.html
-// https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Type-Attributes.html
-//
-// Most attributes in this file are already supported by GCC 4.7.
-// However, some of them are not supported in older version of Clang.
-// Thus, we check __has_attribute() first. If the check fails, we check if we
-// are on GCC and assume the attribute exists on GCC (which is verified on GCC
-// 4.7).
-//
-// For sanitizer-related attributes, define the following macros
-// using -D along with the given value for -fsanitize:
-// - ADDRESS_SANITIZER with -fsanitize=address (GCC 4.8+, Clang)
-// - MEMORY_SANITIZER with -fsanitize=memory (Clang)
-// - THREAD_SANITIZER with -fsanitize=thread (GCC 4.8+, Clang)
-// - UNDEFINED_BEHAVIOR_SANITIZER with -fsanitize=undefined (GCC 4.9+, Clang)
-// - CONTROL_FLOW_INTEGRITY with -fsanitize=cfi (Clang)
-// Since these are only supported by GCC and Clang now, we only check for
-// __GNUC__ (GCC or Clang) and the above macros.
-#ifndef THIRD_PARTY_ABSL_BASE_ATTRIBUTES_H_
-#define THIRD_PARTY_ABSL_BASE_ATTRIBUTES_H_
-
-// ABSL_HAVE_ATTRIBUTE is a function-like feature checking macro.
-// It's a wrapper around __has_attribute, which is defined by GCC 5+ and Clang.
-// It evaluates to a nonzero constant integer if the attribute is supported
-// or 0 if not.
-// It evaluates to zero if __has_attribute is not defined by the compiler.
-// GCC: https://gcc.gnu.org/gcc-5/changes.html
-// Clang: https://clang.llvm.org/docs/LanguageExtensions.html
-#ifdef __has_attribute
-#define ABSL_HAVE_ATTRIBUTE(x) __has_attribute(x)
-#else
-#define ABSL_HAVE_ATTRIBUTE(x) 0
-#endif
-
-// ABSL_HAVE_CPP_ATTRIBUTE is a function-like feature checking macro that
-// accepts C++11 style attributes. It's a wrapper around __has_cpp_attribute,
-// defined by ISO C++ SD-6
-// (http://en.cppreference.com/w/cpp/experimental/feature_test). If we don't
-// find __has_cpp_attribute, will evaluate to 0.
-#if defined(__cplusplus) && defined(__has_cpp_attribute)
-// NOTE: requiring __cplusplus above should not be necessary, but
-// works around https://bugs.llvm.org/show_bug.cgi?id=23435.
-#define ABSL_HAVE_CPP_ATTRIBUTE(x) __has_cpp_attribute(x)
-#else
-#define ABSL_HAVE_CPP_ATTRIBUTE(x) 0
-#endif
-
-// -----------------------------------------------------------------------------
-// Function Attributes
-// -----------------------------------------------------------------------------
-// GCC: https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html
-// Clang: https://clang.llvm.org/docs/AttributeReference.html
-
-// PRINTF_ATTRIBUTE, SCANF_ATTRIBUTE
-// Tell the compiler to do printf format std::string checking if the
-// compiler supports it; see the 'format' attribute in
-// <http://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Function-Attributes.html>.
-//
-// N.B.: As the GCC manual states, "[s]ince non-static C++ methods
-// have an implicit 'this' argument, the arguments of such methods
-// should be counted from two, not one."
-#if ABSL_HAVE_ATTRIBUTE(format) || (defined(__GNUC__) && !defined(__clang__))
-#define ABSL_PRINTF_ATTRIBUTE(string_index, first_to_check) \
- __attribute__((__format__(__printf__, string_index, first_to_check)))
-#define ABSL_SCANF_ATTRIBUTE(string_index, first_to_check) \
- __attribute__((__format__(__scanf__, string_index, first_to_check)))
-#else
-#define ABSL_PRINTF_ATTRIBUTE(string_index, first_to_check)
-#define ABSL_SCANF_ATTRIBUTE(string_index, first_to_check)
-#endif
-
-// To be deleted macros. All macros are going te be renamed with ABSL_ prefix.
-#if ABSL_HAVE_ATTRIBUTE(format) || (defined(__GNUC__) && !defined(__clang__))
-#define PRINTF_ATTRIBUTE(string_index, first_to_check) \
- __attribute__((__format__(__printf__, string_index, first_to_check)))
-#define SCANF_ATTRIBUTE(string_index, first_to_check) \
- __attribute__((__format__(__scanf__, string_index, first_to_check)))
-#else
-#define PRINTF_ATTRIBUTE(string_index, first_to_check)
-#define SCANF_ATTRIBUTE(string_index, first_to_check)
-#endif
-
-// ATTRIBUTE_ALWAYS_INLINE, ATTRIBUTE_NOINLINE
-// For functions we want to force inline or not inline.
-// Introduced in gcc 3.1.
-#if ABSL_HAVE_ATTRIBUTE(always_inline) || (defined(__GNUC__) && !defined(__clang__))
-#define ABSL_ATTRIBUTE_ALWAYS_INLINE __attribute__((always_inline))
-#define ABSL_HAVE_ATTRIBUTE_ALWAYS_INLINE 1
-#else
-#define ABSL_ATTRIBUTE_ALWAYS_INLINE
-#endif
-
-#if ABSL_HAVE_ATTRIBUTE(noinline) || (defined(__GNUC__) && !defined(__clang__))
-#define ABSL_ATTRIBUTE_NOINLINE __attribute__((noinline))
-#define ABSL_HAVE_ATTRIBUTE_NOINLINE 1
-#else
-#define ABSL_ATTRIBUTE_NOINLINE
-#endif
-
-// To be deleted macros. All macros are going te be renamed with ABSL_ prefix.
-#if ABSL_HAVE_ATTRIBUTE(always_inline) || (defined(__GNUC__) && !defined(__clang__))
-#define ATTRIBUTE_ALWAYS_INLINE __attribute__((always_inline))
-#define HAVE_ATTRIBUTE_ALWAYS_INLINE 1
-#else
-#define ATTRIBUTE_ALWAYS_INLINE
-#endif
-
-#if ABSL_HAVE_ATTRIBUTE(noinline) || (defined(__GNUC__) && !defined(__clang__))
-#define ATTRIBUTE_NOINLINE __attribute__((noinline))
-#define HAVE_ATTRIBUTE_NOINLINE 1
-#else
-#define ATTRIBUTE_NOINLINE
-#endif
-
-// ATTRIBUTE_NO_TAIL_CALL
-// Prevent the compiler from optimizing away stack frames for functions which
-// end in a call to another function.
-#if ABSL_HAVE_ATTRIBUTE(disable_tail_calls)
-#define ABSL_HAVE_ATTRIBUTE_NO_TAIL_CALL 1
-#define ABSL_ATTRIBUTE_NO_TAIL_CALL __attribute__((disable_tail_calls))
-#elif defined(__GNUC__) && !defined(__clang__)
-#define ABSL_HAVE_ATTRIBUTE_NO_TAIL_CALL 1
-#define ABSL_ATTRIBUTE_NO_TAIL_CALL __attribute__((optimize("no-optimize-sibling-calls")))
-#else
-#define ABSL_ATTRIBUTE_NO_TAIL_CALL
-#define ABSL_HAVE_ATTRIBUTE_NO_TAIL_CALL 0
-#endif
-
-// To be deleted macros. All macros are going te be renamed with ABSL_ prefix.
-#if ABSL_HAVE_ATTRIBUTE(disable_tail_calls)
-#define HAVE_ATTRIBUTE_NO_TAIL_CALL 1
-#define ATTRIBUTE_NO_TAIL_CALL __attribute__((disable_tail_calls))
-#elif defined(__GNUC__) && !defined(__clang__)
-#define HAVE_ATTRIBUTE_NO_TAIL_CALL 1
-#define ATTRIBUTE_NO_TAIL_CALL __attribute__((optimize("no-optimize-sibling-calls")))
-#else
-#define ATTRIBUTE_NO_TAIL_CALL
-#define HAVE_ATTRIBUTE_NO_TAIL_CALL 0
-#endif
-
-// ATTRIBUTE_WEAK
-// For weak functions
-#if ABSL_HAVE_ATTRIBUTE(weak) || (defined(__GNUC__) && !defined(__clang__))
-#undef ABSL_ATTRIBUTE_WEAK
-#define ABSL_ATTRIBUTE_WEAK __attribute__((weak))
-#define ABSL_HAVE_ATTRIBUTE_WEAK 1
-#else
-#define ABSL_ATTRIBUTE_WEAK
-#define ABSL_HAVE_ATTRIBUTE_WEAK 0
-#endif
-
-// To be deleted macros. All macros are going te be renamed with ABSL_ prefix.
-#if ABSL_HAVE_ATTRIBUTE(weak) || (defined(__GNUC__) && !defined(__clang__))
-#undef ATTRIBUTE_WEAK
-#define ATTRIBUTE_WEAK __attribute__((weak))
-#define HAVE_ATTRIBUTE_WEAK 1
-#else
-#define ATTRIBUTE_WEAK
-#define HAVE_ATTRIBUTE_WEAK 0
-#endif
-
-// ATTRIBUTE_NONNULL
-// Tell the compiler either that a particular function parameter
-// should be a non-null pointer, or that all pointer arguments should
-// be non-null.
-//
-// Note: As the GCC manual states, "[s]ince non-static C++ methods
-// have an implicit 'this' argument, the arguments of such methods
-// should be counted from two, not one."
-//
-// Args are indexed starting at 1.
-// For non-static class member functions, the implicit "this" argument
-// is arg 1, and the first explicit argument is arg 2.
-// For static class member functions, there is no implicit "this", and
-// the first explicit argument is arg 1.
-//
-// /* arg_a cannot be null, but arg_b can */
-// void Function(void* arg_a, void* arg_b) ATTRIBUTE_NONNULL(1);
-//
-// class C {
-// /* arg_a cannot be null, but arg_b can */
-// void Method(void* arg_a, void* arg_b) ATTRIBUTE_NONNULL(2);
-//
-// /* arg_a cannot be null, but arg_b can */
-// static void StaticMethod(void* arg_a, void* arg_b) ATTRIBUTE_NONNULL(1);
-// };
-//
-// If no arguments are provided, then all pointer arguments should be non-null.
-//
-// /* No pointer arguments may be null. */
-// void Function(void* arg_a, void* arg_b, int arg_c) ATTRIBUTE_NONNULL();
-//
-// NOTE: The GCC nonnull attribute actually accepts a list of arguments, but
-// ATTRIBUTE_NONNULL does not.
-#if ABSL_HAVE_ATTRIBUTE(nonnull) || (defined(__GNUC__) && !defined(__clang__))
-#define ABSL_ATTRIBUTE_NONNULL(arg_index) __attribute__((nonnull(arg_index)))
-#else
-#define ABSL_ATTRIBUTE_NONNULL(...)
-#endif
-
-// To be deleted macros. All macros are going te be renamed with ABSL_ prefix.
-#if ABSL_HAVE_ATTRIBUTE(nonnull) || (defined(__GNUC__) && !defined(__clang__))
-#define ATTRIBUTE_NONNULL(arg_index) __attribute__((nonnull(arg_index)))
-#else
-#define ATTRIBUTE_NONNULL(...)
-#endif
-
-// ATTRIBUTE_NORETURN
-// Tell the compiler that a given function never returns
-#if ABSL_HAVE_ATTRIBUTE(noreturn) || (defined(__GNUC__) && !defined(__clang__))
-#define ABSL_ATTRIBUTE_NORETURN __attribute__((noreturn))
-#elif defined(_MSC_VER)
-#define ABSL_ATTRIBUTE_NORETURN __declspec(noreturn)
-#else
-#define ABSL_ATTRIBUTE_NORETURN
-#endif
-
-// To be deleted macros. All macros are going te be renamed with ABSL_ prefix.
-#if ABSL_HAVE_ATTRIBUTE(noreturn) || (defined(__GNUC__) && !defined(__clang__))
-#define ATTRIBUTE_NORETURN __attribute__((noreturn))
-#elif defined(_MSC_VER)
-#define ATTRIBUTE_NORETURN __declspec(noreturn)
-#else
-#define ATTRIBUTE_NORETURN
-#endif
-
-// ATTRIBUTE_NO_SANITIZE_ADDRESS
-// Tell AddressSanitizer (or other memory testing tools) to ignore a given
-// function. Useful for cases when a function reads random locations on stack,
-// calls _exit from a cloned subprocess, deliberately accesses buffer
-// out of bounds or does other scary things with memory.
-// NOTE: GCC supports AddressSanitizer(asan) since 4.8.
-// https://gcc.gnu.org/gcc-4.8/changes.html
-#if defined(__GNUC__) && defined(ADDRESS_SANITIZER)
-#define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address))
-#else
-#define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS
-#endif
-
-// To be deleted macros. All macros are going te be renamed with ABSL_ prefix.
-#if defined(__GNUC__) && defined(ADDRESS_SANITIZER)
-#define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address))
-#else
-#define ATTRIBUTE_NO_SANITIZE_ADDRESS
-#endif
-
-// ATTRIBUTE_NO_SANITIZE_MEMORY
-// Tell MemorySanitizer to relax the handling of a given function. All "Use of
-// uninitialized value" warnings from such functions will be suppressed, and all
-// values loaded from memory will be considered fully initialized.
-// This is similar to the ADDRESS_SANITIZER attribute above, but deals with
-// initializedness rather than addressability issues.
-// NOTE: MemorySanitizer(msan) is supported by Clang but not GCC.
-#if defined(__GNUC__) && defined(MEMORY_SANITIZER)
-#define ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory))
-#else
-#define ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY
-#endif
-
-// To be deleted macros. All macros are going te be renamed with ABSL_ prefix.
-#if defined(__GNUC__) && defined(MEMORY_SANITIZER)
-#define ATTRIBUTE_NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory))
-#else
-#define ATTRIBUTE_NO_SANITIZE_MEMORY
-#endif
-
-// ATTRIBUTE_NO_SANITIZE_THREAD
-// Tell ThreadSanitizer to not instrument a given function.
-// If you are adding this attribute, please cc dynamic-tools@ on the cl.
-// NOTE: GCC supports ThreadSanitizer(tsan) since 4.8.
-// https://gcc.gnu.org/gcc-4.8/changes.html
-#if defined(__GNUC__) && defined(THREAD_SANITIZER)
-#define ABSL_ATTRIBUTE_NO_SANITIZE_THREAD __attribute__((no_sanitize_thread))
-#else
-#define ABSL_ATTRIBUTE_NO_SANITIZE_THREAD
-#endif
-
-// To be deleted macros. All macros are going te be renamed with ABSL_ prefix.
-#if defined(__GNUC__) && defined(THREAD_SANITIZER)
-#define ATTRIBUTE_NO_SANITIZE_THREAD __attribute__((no_sanitize_thread))
-#else
-#define ATTRIBUTE_NO_SANITIZE_THREAD
-#endif
-
-// ATTRIBUTE_NO_SANITIZE_UNDEFINED
-// Tell UndefinedSanitizer to ignore a given function. Useful for cases
-// where certain behavior (eg. devision by zero) is being used intentionally.
-// NOTE: GCC supports UndefinedBehaviorSanitizer(ubsan) since 4.9.
-// https://gcc.gnu.org/gcc-4.9/changes.html
-#if defined(__GNUC__) && defined(UNDEFINED_BEHAVIOR_SANITIZER)
-#define ABSL_ATTRIBUTE_NO_SANITIZE_UNDEFINED __attribute__((no_sanitize("undefined")))
-#else
-#define ABSL_ATTRIBUTE_NO_SANITIZE_UNDEFINED
-#endif
-
-// To be deleted macros. All macros are going te be renamed with ABSL_ prefix.
-#if defined(__GNUC__) && defined(UNDEFINED_BEHAVIOR_SANITIZER)
-#define ATTRIBUTE_NO_SANITIZE_UNDEFINED __attribute__((no_sanitize("undefined")))
-#else
-#define ATTRIBUTE_NO_SANITIZE_UNDEFINED
-#endif
-
-// ATTRIBUTE_NO_SANITIZE_CFI
-// Tell ControlFlowIntegrity sanitizer to not instrument a given function.
-#if defined(__GNUC__) && defined(CONTROL_FLOW_INTEGRITY)
-#define ABSL_ATTRIBUTE_NO_SANITIZE_CFI __attribute__((no_sanitize("cfi")))
-#else
-#define ABSL_ATTRIBUTE_NO_SANITIZE_CFI
-#endif
-
-// To be deleted macros. All macros are going te be renamed with ABSL_ prefix.
-#if defined(__GNUC__) && defined(CONTROL_FLOW_INTEGRITY)
-#define ATTRIBUTE_NO_SANITIZE_CFI __attribute__((no_sanitize("cfi")))
-#else
-#define ATTRIBUTE_NO_SANITIZE_CFI
-#endif
-
-// ATTRIBUTE_SECTION
-// Labeled sections are not supported on Darwin/iOS.
-#ifdef ABSL_HAVE_ATTRIBUTE_SECTION
-#error ABSL_HAVE_ATTRIBUTE_SECTION cannot be directly set
-#elif (ABSL_HAVE_ATTRIBUTE(section) || (defined(__GNUC__) && !defined(__clang__))) && \
- !(defined(__APPLE__) && defined(__MACH__))
-#define ABSL_HAVE_ATTRIBUTE_SECTION 1
-//
-// Tell the compiler/linker to put a given function into a section and define
-// "__start_ ## name" and "__stop_ ## name" symbols to bracket the section.
-// This functionality is supported by GNU linker.
-// Any function with ATTRIBUTE_SECTION must not be inlined, or it will
-// be placed into whatever section its caller is placed into.
-//
-#ifndef ABSL_ATTRIBUTE_SECTION
-#define ABSL_ATTRIBUTE_SECTION(name) __attribute__((section(#name))) __attribute__((noinline))
-#endif
-
-// To be deleted macros. All macros are going te be renamed with ABSL_ prefix.
-#ifndef ATTRIBUTE_SECTION
-#define ATTRIBUTE_SECTION(name) __attribute__((section(#name))) __attribute__((noinline))
-#endif
-
-// Tell the compiler/linker to put a given variable into a section and define
-// "__start_ ## name" and "__stop_ ## name" symbols to bracket the section.
-// This functionality is supported by GNU linker.
-#ifndef ABSL_ATTRIBUTE_SECTION_VARIABLE
-#define ABSL_ATTRIBUTE_SECTION_VARIABLE(name) __attribute__((section(#name)))
-#endif
-
-// To be deleted macros. All macros are going te be renamed with ABSL_ prefix.
-#ifndef ATTRIBUTE_SECTION_VARIABLE
-#define ATTRIBUTE_SECTION_VARIABLE(name) __attribute__((section(#name)))
-#endif
-
-//
-// Weak section declaration to be used as a global declaration
-// for ATTRIBUTE_SECTION_START|STOP(name) to compile and link
-// even without functions with ATTRIBUTE_SECTION(name).
-// DEFINE_ATTRIBUTE_SECTION should be in the exactly one file; it's
-// a no-op on ELF but not on Mach-O.
-//
-#ifndef ABSL_DECLARE_ATTRIBUTE_SECTION_VARS
-#define ABSL_DECLARE_ATTRIBUTE_SECTION_VARS(name) \
- extern char __start_##name[] ATTRIBUTE_WEAK; \
- extern char __stop_##name[] ATTRIBUTE_WEAK
-#endif
-#ifndef ABSL_DEFINE_ATTRIBUTE_SECTION_VARS
-#define ABSL_INIT_ATTRIBUTE_SECTION_VARS(name)
-#define ABSL_DEFINE_ATTRIBUTE_SECTION_VARS(name)
-#endif
-
-// To be deleted macros. All macros are going te be renamed with ABSL_ prefix.
-#ifndef DECLARE_ATTRIBUTE_SECTION_VARS
-#define DECLARE_ATTRIBUTE_SECTION_VARS(name) \
- extern char __start_##name[] ATTRIBUTE_WEAK; \
- extern char __stop_##name[] ATTRIBUTE_WEAK
-#endif
-#ifndef DEFINE_ATTRIBUTE_SECTION_VARS
-#define INIT_ATTRIBUTE_SECTION_VARS(name)
-#define DEFINE_ATTRIBUTE_SECTION_VARS(name)
-#endif
-
-//
-// Return void* pointers to start/end of a section of code with
-// functions having ATTRIBUTE_SECTION(name).
-// Returns 0 if no such functions exits.
-// One must DECLARE_ATTRIBUTE_SECTION_VARS(name) for this to compile and link.
-//
-#define ABSL_ATTRIBUTE_SECTION_START(name) (reinterpret_cast<void *>(__start_##name))
-#define ABSL_ATTRIBUTE_SECTION_STOP(name) (reinterpret_cast<void *>(__stop_##name))
-
-// To be deleted macros. All macros are going te be renamed with ABSL_ prefix.
-#define ATTRIBUTE_SECTION_START(name) (reinterpret_cast<void *>(__start_##name))
-#define ATTRIBUTE_SECTION_STOP(name) (reinterpret_cast<void *>(__stop_##name))
-
-#else // !ABSL_HAVE_ATTRIBUTE_SECTION
-
-#define ABSL_HAVE_ATTRIBUTE_SECTION 0
-
-// provide dummy definitions
-#define ABSL_ATTRIBUTE_SECTION(name)
-#define ABSL_ATTRIBUTE_SECTION_VARIABLE(name)
-#define ABSL_INIT_ATTRIBUTE_SECTION_VARS(name)
-#define ABSL_DEFINE_ATTRIBUTE_SECTION_VARS(name)
-#define ABSL_DECLARE_ATTRIBUTE_SECTION_VARS(name)
-#define ABSL_ATTRIBUTE_SECTION_START(name) (reinterpret_cast<void *>(0))
-#define ABSL_ATTRIBUTE_SECTION_STOP(name) (reinterpret_cast<void *>(0))
-
-// To be deleted macros. All macros are going te be renamed with ABSL_ prefix.
-#define ATTRIBUTE_SECTION(name)
-#define ATTRIBUTE_SECTION_VARIABLE(name)
-#define INIT_ATTRIBUTE_SECTION_VARS(name)
-#define DEFINE_ATTRIBUTE_SECTION_VARS(name)
-#define DECLARE_ATTRIBUTE_SECTION_VARS(name)
-#define ATTRIBUTE_SECTION_START(name) (reinterpret_cast<void *>(0))
-#define ATTRIBUTE_SECTION_STOP(name) (reinterpret_cast<void *>(0))
-
-#endif // ATTRIBUTE_SECTION
-
-// ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC
-// Support for aligning the stack on 32-bit x86.
-#if ABSL_HAVE_ATTRIBUTE(force_align_arg_pointer) || (defined(__GNUC__) && !defined(__clang__))
-#if defined(__i386__)
-#define ABSL_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC __attribute__((force_align_arg_pointer))
-#define ABSL_REQUIRE_STACK_ALIGN_TRAMPOLINE (0)
-#elif defined(__x86_64__)
-#define ABSL_REQUIRE_STACK_ALIGN_TRAMPOLINE (1)
-#define ABSL_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC
-#else // !__i386__ && !__x86_64
-#define ABSL_REQUIRE_STACK_ALIGN_TRAMPOLINE (0)
-#define ABSL_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC
-#endif // __i386__
-#else
-#define ABSL_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC
-#define ABSL_REQUIRE_STACK_ALIGN_TRAMPOLINE (0)
-#endif
-
-// To be deleted macros. All macros are going te be renamed with ABSL_ prefix.
-#if ABSL_HAVE_ATTRIBUTE(force_align_arg_pointer) || (defined(__GNUC__) && !defined(__clang__))
-#if defined(__i386__)
-#define ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC __attribute__((force_align_arg_pointer))
-#define REQUIRE_STACK_ALIGN_TRAMPOLINE (0)
-#elif defined(__x86_64__)
-#define REQUIRE_STACK_ALIGN_TRAMPOLINE (1)
-#define ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC
-#else // !__i386__ && !__x86_64
-#define REQUIRE_STACK_ALIGN_TRAMPOLINE (0)
-#define ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC
-#endif // __i386__
-#else
-#define ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC
-#define REQUIRE_STACK_ALIGN_TRAMPOLINE (0)
-#endif
-
-// MUST_USE_RESULT
-// Tell the compiler to warn about unused return values for functions declared
-// with this macro. The macro must appear as the very first part of a function
-// declaration or definition:
-//
-// MUST_USE_RESULT Sprocket* AllocateSprocket();
-//
-// This placement has the broadest compatibility with GCC, Clang, and MSVC, with
-// both defs and decls, and with GCC-style attributes, MSVC declspec, and C++11
-// attributes. Note: past advice was to place the macro after the argument list.
-#if ABSL_HAVE_ATTRIBUTE(warn_unused_result) || (defined(__GNUC__) && !defined(__clang__))
-#define ABSL_MUST_USE_RESULT __attribute__((warn_unused_result))
-#else
-#define ABSL_MUST_USE_RESULT
-#endif
-
-// To be deleted macros. All macros are going te be renamed with ABSL_ prefix.
-#if ABSL_HAVE_ATTRIBUTE(warn_unused_result) || (defined(__GNUC__) && !defined(__clang__))
-#define MUST_USE_RESULT __attribute__((warn_unused_result))
-#else
-#define MUST_USE_RESULT
-#endif
-
-// ATTRIBUTE_HOT, ATTRIBUTE_COLD
-// Tell GCC that a function is hot or cold. GCC can use this information to
-// improve static analysis, i.e. a conditional branch to a cold function
-// is likely to be not-taken.
-// This annotation is used for function declarations, e.g.:
-// int foo() ATTRIBUTE_HOT;
-#if ABSL_HAVE_ATTRIBUTE(hot) || (defined(__GNUC__) && !defined(__clang__))
-#define ABSL_ATTRIBUTE_HOT __attribute__((hot))
-#else
-#define ABSL_ATTRIBUTE_HOT
-#endif
-
-#if ABSL_HAVE_ATTRIBUTE(cold) || (defined(__GNUC__) && !defined(__clang__))
-#define ABSL_ATTRIBUTE_COLD __attribute__((cold))
-#else
-#define ABSL_ATTRIBUTE_COLD
-#endif
-
-// To be deleted macros. All macros are going te be renamed with ABSL_ prefix.
-#if ABSL_HAVE_ATTRIBUTE(hot) || (defined(__GNUC__) && !defined(__clang__))
-#define ATTRIBUTE_HOT __attribute__((hot))
-#else
-#define ATTRIBUTE_HOT
-#endif
-
-#if ABSL_HAVE_ATTRIBUTE(cold) || (defined(__GNUC__) && !defined(__clang__))
-#define ATTRIBUTE_COLD __attribute__((cold))
-#else
-#define ATTRIBUTE_COLD
-#endif
-
-// ABSL_XRAY_ALWAYS_INSTRUMENT, ABSL_XRAY_NEVER_INSTRUMENT, ABSL_XRAY_LOG_ARGS
-//
-// We define the ABSL_XRAY_ALWAYS_INSTRUMENT and ABSL_XRAY_NEVER_INSTRUMENT
-// macro used as an attribute to mark functions that must always or never be
-// instrumented by XRay. Currently, this is only supported in Clang/LLVM.
-//
-// For reference on the LLVM XRay instrumentation, see
-// http://llvm.org/docs/XRay.html.
-//
-// A function with the XRAY_ALWAYS_INSTRUMENT macro attribute in its declaration
-// will always get the XRay instrumentation sleds. These sleds may introduce
-// some binary size and runtime overhead and must be used sparingly.
-//
-// These attributes only take effect when the following conditions are met:
-//
-// - The file/target is built in at least C++11 mode, with a Clang compiler
-// that supports XRay attributes.
-// - The file/target is built with the -fxray-instrument flag set for the
-// Clang/LLVM compiler.
-// - The function is defined in the translation unit (the compiler honors the
-// attribute in either the definition or the declaration, and must match).
-//
-// There are cases when, even when building with XRay instrumentation, users
-// might want to control specifically which functions are instrumented for a
-// particular build using special-case lists provided to the compiler. These
-// special case lists are provided to Clang via the
-// -fxray-always-instrument=... and -fxray-never-instrument=... flags. The
-// attributes in source take precedence over these special-case lists.
-//
-// To disable the XRay attributes at build-time, users may define
-// ABSL_NO_XRAY_ATTRIBUTES. Do NOT define ABSL_NO_XRAY_ATTRIBUTES on specific
-// packages/targets, as this may lead to conflicting definitions of functions at
-// link-time.
-//
-#if ABSL_HAVE_CPP_ATTRIBUTE(clang::xray_always_instrument) && !defined(ABSL_NO_XRAY_ATTRIBUTES)
-#define ABSL_XRAY_ALWAYS_INSTRUMENT [[clang::xray_always_instrument]]
-#define ABSL_XRAY_NEVER_INSTRUMENT [[clang::xray_never_instrument]]
-#define ABSL_XRAY_LOG_ARGS(N) [[ clang::xray_always_instrument, clang::xray_log_args(N) ]]
-#else
-#define ABSL_XRAY_ALWAYS_INSTRUMENT
-#define ABSL_XRAY_NEVER_INSTRUMENT
-#define ABSL_XRAY_LOG_ARGS(N)
-#endif
-
-// -----------------------------------------------------------------------------
-// Variable Attributes
-// -----------------------------------------------------------------------------
-
-// ATTRIBUTE_UNUSED
-// Prevent the compiler from complaining about or optimizing away variables
-// that appear unused.
-#if ABSL_HAVE_ATTRIBUTE(unused) || (defined(__GNUC__) && !defined(__clang__))
-#undef ABSL_ATTRIBUTE_UNUSED
-#define ABSL_ATTRIBUTE_UNUSED __attribute__((__unused__))
-#else
-#define ABSL_ATTRIBUTE_UNUSED
-#endif
-
-// To be deleted macros. All macros are going te be renamed with ABSL_ prefix.
-#if ABSL_HAVE_ATTRIBUTE(unused) || (defined(__GNUC__) && !defined(__clang__))
-#undef ATTRIBUTE_UNUSED
-#define ATTRIBUTE_UNUSED __attribute__((__unused__))
-#else
-#define ATTRIBUTE_UNUSED
-#endif
-
-// ATTRIBUTE_INITIAL_EXEC
-// Tell the compiler to use "initial-exec" mode for a thread-local variable.
-// See http://people.redhat.com/drepper/tls.pdf for the gory details.
-#if ABSL_HAVE_ATTRIBUTE(tls_model) || (defined(__GNUC__) && !defined(__clang__))
-#define ABSL_ATTRIBUTE_INITIAL_EXEC __attribute__((tls_model("initial-exec")))
-#else
-#define ABSL_ATTRIBUTE_INITIAL_EXEC
-#endif
-
-// To be deleted macros. All macros are going te be renamed with ABSL_ prefix.
-#if ABSL_HAVE_ATTRIBUTE(tls_model) || (defined(__GNUC__) && !defined(__clang__))
-#define ATTRIBUTE_INITIAL_EXEC __attribute__((tls_model("initial-exec")))
-#else
-#define ATTRIBUTE_INITIAL_EXEC
-#endif
-
-// ATTRIBUTE_PACKED
-// Prevent the compiler from padding a structure to natural alignment
-#if ABSL_HAVE_ATTRIBUTE(packed) || (defined(__GNUC__) && !defined(__clang__))
-#define ABSL_ATTRIBUTE_PACKED __attribute__((__packed__))
-#else
-#define ABSL_ATTRIBUTE_PACKED
-#endif
-
-// To be deleted macros. All macros are going te be renamed with ABSL_ prefix.
-#if ABSL_HAVE_ATTRIBUTE(packed) || (defined(__GNUC__) && !defined(__clang__))
-#define ATTRIBUTE_PACKED __attribute__((__packed__))
-#else
-#define ATTRIBUTE_PACKED
-#endif
-
-// ABSL_CONST_INIT
-// A variable declaration annotated with the ABSL_CONST_INIT attribute will
-// not compile (on supported platforms) unless the variable has a constant
-// initializer. This is useful for variables with static and thread storage
-// duration, because it guarantees that they will not suffer from the so-called
-// "static init order fiasco".
-//
-// Sample usage:
-//
-// ABSL_CONST_INIT static MyType my_var = MakeMyType(...);
-//
-// Note that this attribute is redundant if the variable is declared constexpr.
-#if ABSL_HAVE_CPP_ATTRIBUTE(clang::require_constant_initialization)
-// NOLINTNEXTLINE(whitespace/braces) (b/36288871)
-#define ABSL_CONST_INIT [[clang::require_constant_initialization]]
-#else
-#define ABSL_CONST_INIT
-#endif // ABSL_HAVE_CPP_ATTRIBUTE(clang::require_constant_initialization)
-
-#endif // THIRD_PARTY_ABSL_BASE_ATTRIBUTES_H_
diff --git a/Firestore/Port/absl/absl_config.h b/Firestore/Port/absl/absl_config.h
deleted file mode 100644
index 70f4d86..0000000
--- a/Firestore/Port/absl/absl_config.h
+++ /dev/null
@@ -1,306 +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.
- */
-
-// Defines preprocessor macros describing the presence of "features" available.
-// This facilitates writing portable code by parameterizing the compilation
-// based on the presence or lack of a feature.
-//
-// We define a feature as some interface we wish to program to: for example,
-// some library function or system call.
-//
-// For example, suppose a programmer wants to write a program that uses the
-// 'mmap' system call. Then one might write:
-//
-// #include "absl/base/config.h"
-//
-// #ifdef ABSL_HAVE_MMAP
-// #include "sys/mman.h"
-// #endif //ABSL_HAVE_MMAP
-//
-// ...
-// #ifdef ABSL_HAVE_MMAP
-// void *ptr = mmap(...);
-// ...
-// #endif // ABSL_HAVE_MMAP
-//
-// As a special note, using feature macros from config.h to determine whether
-// to include a particular header requires violating the style guide's required
-// ordering for headers: this is permitted.
-
-#ifndef THIRD_PARTY_ABSL_BASE_CONFIG_H_
-#define THIRD_PARTY_ABSL_BASE_CONFIG_H_
-
-// Included for the __GLIBC__ macro (or similar macros on other systems).
-#include <limits.h>
-
-#ifdef __cplusplus
-// Included for __GLIBCXX__, _LIBCPP_VERSION
-#include <cstddef>
-#endif // __cplusplus
-
-// If we're using glibc, make sure we meet a minimum version requirement
-// before we proceed much further.
-//
-// We have chosen glibc 2.12 as the minimum as it was tagged for release
-// in May, 2010 and includes some functionality used in Google software
-// (for instance pthread_setname_np):
-// https://sourceware.org/ml/libc-alpha/2010-05/msg00000.html
-#ifdef __GLIBC_PREREQ
-#if !__GLIBC_PREREQ(2, 12)
-#error "Minimum required version of glibc is 2.12."
-#endif
-#endif
-
-// ABSL_HAVE_BUILTIN is a function-like feature checking macro.
-// It's a wrapper around __has_builtin, which is defined by only clang now.
-// It evaluates to 1 if the builtin is supported or 0 if not.
-// Define it to avoid an extra level of #ifdef __has_builtin check.
-// http://releases.llvm.org/3.3/tools/clang/docs/LanguageExtensions.html
-#ifdef __has_builtin
-#define ABSL_HAVE_BUILTIN(x) __has_builtin(x)
-#else
-#define ABSL_HAVE_BUILTIN(x) 0
-#endif
-
-// ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE is defined when
-// std::is_trivially_destructible<T> is supported.
-//
-// All supported compilers using libc++ have it, as does gcc >= 4.8
-// using libstdc++, as does Visual Studio.
-// https://gcc.gnu.org/onlinedocs/gcc-4.8.1/libstdc++/manual/manual/status.html#status.iso.2011
-// is the first version where std::is_trivially_destructible no longer
-// appeared as missing in the Type properties row.
-#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE
-#error ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE cannot be directly set
-#elif defined(_LIBCPP_VERSION) || \
- (!defined(__clang__) && defined(__GNUC__) && defined(__GLIBCXX__) && \
- (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) || \
- defined(_MSC_VER)
-#define ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE 1
-#endif
-
-// ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE is defined when
-// std::is_trivially_default_constructible<T> and
-// std::is_trivially_copy_constructible<T> are supported.
-//
-// ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE is defined when
-// std::is_trivially_copy_assignable<T> is supported.
-//
-// Clang with libc++ supports it, as does gcc >= 5.1 with either
-// libc++ or libstdc++, as does Visual Studio.
-// https://gcc.gnu.org/gcc-5/changes.html lists as new
-// "std::is_trivially_constructible, std::is_trivially_assignable
-// etc."
-#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE)
-#error ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE cannot be directly set
-#elif defined(ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE)
-#error ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE cannot directly set
-#elif (defined(__clang__) && defined(_LIBCPP_VERSION)) || \
- (!defined(__clang__) && defined(__GNUC__) && \
- (__GNUC__ > 5 || (__GNUC__ == 5 && __GNUC_MINOR__ >= 1)) && \
- (defined(_LIBCPP_VERSION) || defined(__GLIBCXX__))) || \
- defined(_MSC_VER)
-#define ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE 1
-#define ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE 1
-#endif
-
-// ABSL_HAVE_THREAD_LOCAL is defined when C++11's thread_local is available.
-// Clang implements thread_local keyword but Xcode did not support the
-// implementation until Xcode 8.
-#ifdef ABSL_HAVE_THREAD_LOCAL
-#error ABSL_HAVE_THREAD_LOCAL cannot be directly set
-#elif !defined(__apple_build_version__) || __apple_build_version__ >= 8000042
-#define ABSL_HAVE_THREAD_LOCAL 1
-#endif
-
-// ABSL_HAVE_INTRINSIC_INT128 is defined when the implementation provides the
-// 128 bit integral type: __int128.
-//
-// __SIZEOF_INT128__ is defined by Clang and GCC when __int128 is supported.
-// Clang on ppc64 and aarch64 are exceptions where __int128 exists but has a
-// sporadic compiler crashing bug. Nvidia's nvcc also defines __GNUC__ and
-// __SIZEOF_INT128__ but not all versions that do this support __int128. Support
-// has been tested for versions >= 7.
-#ifdef ABSL_HAVE_INTRINSIC_INT128
-#error ABSL_HAVE_INTRINSIC_INT128 cannot be directly set
-#elif (defined(__clang__) && defined(__SIZEOF_INT128__) && !defined(__ppc64__) && \
- !defined(__aarch64__)) || \
- (defined(__CUDACC__) && defined(__SIZEOF_INT128__) && __CUDACC_VER__ >= 70000) || \
- (!defined(__clang__) && !defined(__CUDACC__) && defined(__GNUC__) && \
- defined(__SIZEOF_INT128__))
-#define ABSL_HAVE_INTRINSIC_INT128 1
-#endif
-
-// Operating system-specific features.
-//
-// Currently supported operating systems and associated preprocessor
-// symbols:
-//
-// Linux and Linux-derived __linux__
-// Android __ANDROID__ (implies __linux__)
-// Linux (non-Android) __linux__ && !__ANDROID__
-// Darwin (Mac OS X and iOS) __APPLE__ && __MACH__
-// Akaros (http://akaros.org) __ros__
-// Windows _WIN32
-// NaCL __native_client__
-// AsmJS __asmjs__
-// Fuschia __Fuchsia__
-//
-// Note that since Android defines both __ANDROID__ and __linux__, one
-// may probe for either Linux or Android by simply testing for __linux__.
-//
-
-// ABSL_HAVE_MMAP is defined when the system has an mmap(2) implementation
-// as defined in POSIX.1-2001.
-#ifdef ABSL_HAVE_MMAP
-#error ABSL_HAVE_MMAP cannot be directly set
-#elif defined(__linux__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__ros__) || \
- defined(__native_client__) || defined(__asmjs__) || defined(__Fuchsia__)
-#define ABSL_HAVE_MMAP 1
-#endif
-
-// ABSL_HAS_PTHREAD_GETSCHEDPARAM is defined when the system implements the
-// pthread_(get|set)schedparam(3) 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(__MACH__)) || defined(__ros__)
-#define ABSL_HAVE_PTHREAD_GETSCHEDPARAM 1
-#endif
-
-// ABSL_HAVE_SCHED_YIELD is defined when the system implements
-// sched_yield(2) as defined in POSIX.1-2001.
-#ifdef ABSL_HAVE_SCHED_YIELD
-#error ABSL_HAVE_SCHED_YIELD cannot be directly set
-#elif defined(__linux__) || defined(__ros__) || defined(__native_client__)
-#define ABSL_HAVE_SCHED_YIELD 1
-#endif
-
-// ABSL_HAVE_SEMAPHORE_H is defined when the system supports the <semaphore.h>
-// header and sem_open(3) family of functions as standardized in POSIX.1-2001.
-//
-// Note: While Apple does have <semaphore.h> for both iOS and macOS, it is
-// explicity deprecated and will cause build failures if enabled for those
-// systems. We side-step the issue by not defining it here for Apple platforms.
-#ifdef ABSL_HAVE_SEMAPHORE_H
-#error ABSL_HAVE_SEMAPHORE_H cannot be directly set
-#elif defined(__linux__) || defined(__ros__)
-#define ABSL_HAVE_SEMAPHORE_H 1
-#endif
-
-// Library-specific features.
-#ifdef ABSL_HAVE_ALARM
-#error ABSL_HAVE_ALARM cannot be directly set
-#elif defined(__GOOGLE_GRTE_VERSION__)
-// feature tests for Google's GRTE
-#define ABSL_HAVE_ALARM 1
-#elif defined(__GLIBC__)
-// feature test for glibc
-#define ABSL_HAVE_ALARM 1
-#elif defined(_MSC_VER)
-// feature tests for Microsoft's library
-#elif defined(__native_client__)
-#else
-// other standard libraries
-#define ABSL_HAVE_ALARM 1
-#endif
-
-#if defined(_STLPORT_VERSION)
-#error "STLPort is not supported."
-#endif
-
-// -----------------------------------------------------------------------------
-// Endianness
-// -----------------------------------------------------------------------------
-// Define ABSL_IS_LITTLE_ENDIAN, ABSL_IS_BIG_ENDIAN.
-// Some compilers or system headers provide macros to specify endianness.
-// Unfortunately, there is no standard for the names of the macros or even of
-// the header files.
-// Reference: https://sourceforge.net/p/predef/wiki/Endianness/
-#if defined(ABSL_IS_BIG_ENDIAN) || defined(ABSL_IS_LITTLE_ENDIAN)
-#error "ABSL_IS_(BIG|LITTLE)_ENDIAN cannot be directly set."
-
-#elif defined(__GLIBC__) || defined(__linux__)
-// Operating systems that use the GNU C library generally provide <endian.h>
-// containing __BYTE_ORDER, __LITTLE_ENDIAN, __BIG_ENDIAN.
-#include <endian.h>
-
-#if __BYTE_ORDER == __LITTLE_ENDIAN
-#define ABSL_IS_LITTLE_ENDIAN 1
-#elif __BYTE_ORDER == __BIG_ENDIAN
-#define ABSL_IS_BIG_ENDIAN 1
-#else // __BYTE_ORDER != __LITTLE_ENDIAN && __BYTE_ORDER != __BIG_ENDIAN
-#error "Unknown endianness"
-#endif // __BYTE_ORDER
-
-#elif defined(__APPLE__) && defined(__MACH__)
-// Apple has <machine/endian.h> containing BYTE_ORDER, BIG_ENDIAN,
-// LITTLE_ENDIAN.
-#include <machine/endian.h> // NOLINT(build/include)
-
-#if BYTE_ORDER == LITTLE_ENDIAN
-#define ABSL_IS_LITTLE_ENDIAN 1
-#elif BYTE_ORDER == BIG_ENDIAN
-#define ABSL_IS_BIG_ENDIAN 1
-#else // BYTE_ORDER != LITTLE_ENDIAN && BYTE_ORDER != BIG_ENDIAN
-#error "Unknown endianness"
-#endif // BYTE_ORDER
-
-#elif defined(_WIN32)
-// Assume Windows is little-endian.
-#define ABSL_IS_LITTLE_ENDIAN 1
-
-#elif defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || defined(__THUMBEL__) || \
- defined(__AARCH64EL__) || defined(_MIPSEL) || defined(__MIPSEL) || defined(__MIPSEL__)
-#define ABSL_IS_LITTLE_ENDIAN 1
-
-#elif defined(__BIG_ENDIAN__) || defined(__ARMEB__) || defined(__THUMBEB__) || \
- defined(__AARCH64EB__) || defined(_MIPSEB) || defined(__MIPSEB) || defined(__MIPSEB__)
-#define ABSL_IS_BIG_ENDIAN 1
-
-#else
-#error "absl endian detection needs to be set up on your platform."
-#endif
-
-// ABSL_HAVE_EXCEPTIONS is defined when exceptions are enabled. Many
-// compilers support a "no exceptions" mode that disables exceptions.
-//
-// Generally, when ABSL_HAVE_EXCEPTIONS is not defined:
-//
-// - Code using `throw` and `try` may not compile.
-// - The `noexcept` specifier will still compile and behave as normal.
-// - The `noexcept` operator may still return `false`.
-//
-// For further details, consult the compiler's documentation.
-#ifdef ABSL_HAVE_EXCEPTIONS
-#error ABSL_HAVE_EXCEPTIONS cannot be directly set.
-
-#elif defined(__clang__)
-// TODO
-// Switch to using __cpp_exceptions when we no longer support versions < 3.6.
-// For details on this check, see:
-// https://goo.gl/PilDrJ
-#if defined(__EXCEPTIONS) && __has_feature(cxx_exceptions)
-#define ABSL_HAVE_EXCEPTIONS 1
-#endif // defined(__EXCEPTIONS) && __has_feature(cxx_exceptions)
-
-// Handle remaining special cases and default to exceptions being supported.
-#elif !(defined(__GNUC__) && (__GNUC__ < 5) && !defined(__EXCEPTIONS)) && \
- !(defined(__GNUC__) && (__GNUC__ >= 5) && !defined(__cpp_exceptions)) && \
- !(defined(_MSC_VER) && !defined(_CPPUNWIND))
-#define ABSL_HAVE_EXCEPTIONS 1
-#endif
-
-#endif // THIRD_PARTY_ABSL_BASE_CONFIG_H_
diff --git a/Firestore/Port/absl/absl_endian.h b/Firestore/Port/absl/absl_endian.h
deleted file mode 100644
index 2c51a27..0000000
--- a/Firestore/Port/absl/absl_endian.h
+++ /dev/null
@@ -1,342 +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 ABSL_BASE_INTERNAL_ENDIAN_H_
-#define ABSL_BASE_INTERNAL_ENDIAN_H_
-
-// The following guarantees declaration of the byte swap functions
-#ifdef _MSC_VER
-#include <stdlib.h> // NOLINT(build/include)
-#elif defined(__APPLE__) && defined(__MACH__)
-// Mac OS X / Darwin features
-#include <libkern/OSByteOrder.h>
-#elif defined(__GLIBC__)
-#include <byteswap.h> // IWYU pragma: export
-#endif
-
-#include <cstdint>
-#include "Firestore/Port/absl/absl_port.h"
-
-namespace absl {
-
-// Use compiler byte-swapping intrinsics if they are available. 32-bit
-// and 64-bit versions are available in Clang and GCC as of GCC 4.3.0.
-// The 16-bit version is available in Clang and GCC only as of GCC 4.8.0.
-// For simplicity, we enable them all only for GCC 4.8.0 or later.
-#if defined(__clang__) || \
- (defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 8) || __GNUC__ >= 5))
-inline uint64_t gbswap_64(uint64_t host_int) {
- return __builtin_bswap64(host_int);
-}
-inline uint32_t gbswap_32(uint32_t host_int) {
- return __builtin_bswap32(host_int);
-}
-inline uint16 gbswap_16(uint16 host_int) {
- return __builtin_bswap16(host_int);
-}
-
-#elif defined(_MSC_VER)
-inline uint64_t gbswap_64(uint64_t host_int) {
- return _byteswap_uint64(host_int);
-}
-inline uint32_t gbswap_32(uint32_t host_int) {
- return _byteswap_ulong(host_int);
-}
-inline uint16 gbswap_16(uint16 host_int) {
- return _byteswap_ushort(host_int);
-}
-
-#elif defined(__APPLE__) && defined(__MACH__)
-inline uint64_t gbswap_64(uint64_t host_int) {
- return OSSwapInt16(host_int);
-}
-inline uint32_t gbswap_32(uint32_t host_int) {
- return OSSwapInt32(host_int);
-}
-inline uint16 gbswap_16(uint16 host_int) {
- return OSSwapInt64(host_int);
-}
-
-#else
-inline uint64_t gbswap_64(uint64_t host_int) {
-#if defined(__GNUC__) && defined(__x86_64__) && !(defined(__APPLE__) && defined(__MACH__))
- // Adapted from /usr/include/byteswap.h. Not available on Mac.
- if (__builtin_constant_p(host_int)) {
- return __bswap_constant_64(host_int);
- } else {
- register uint64_t result;
- __asm__("bswap %0" : "=r"(result) : "0"(host_int));
- return result;
- }
-#elif defined(__GLIBC__)
- return bswap_64(host_int);
-#else
- return (((x & GG_ULONGLONG(0xFF)) << 56) | ((x & GG_ULONGLONG(0xFF00)) << 40) |
- ((x & GG_ULONGLONG(0xFF0000)) << 24) | ((x & GG_ULONGLONG(0xFF000000)) << 8) |
- ((x & GG_ULONGLONG(0xFF00000000)) >> 8) | ((x & GG_ULONGLONG(0xFF0000000000)) >> 24) |
- ((x & GG_ULONGLONG(0xFF000000000000)) >> 40) |
- ((x & GG_ULONGLONG(0xFF00000000000000)) >> 56));
-#endif // bswap_64
-}
-
-inline uint32_t gbswap_32(uint32_t host_int) {
-#if defined(__GLIBC__)
- return bswap_32(host_int);
-#else
- return (((x & 0xFF) << 24) | ((x & 0xFF00) << 8) | ((x & 0xFF0000) >> 8) |
- ((x & 0xFF000000) >> 24));
-#endif
-}
-
-inline uint16 gbswap_16(uint16 host_int) {
-#if defined(__GLIBC__)
- return bswap_16(host_int);
-#else
- return (uint16)(((x & 0xFF) << 8) | ((x & 0xFF00) >> 8)); // NOLINT
-#endif
-}
-
-#endif // intrinics available
-
-#ifdef ABSL_IS_LITTLE_ENDIAN
-
-// Definitions for ntohl etc. that don't require us to include
-// netinet/in.h. We wrap gbswap_32 and gbswap_16 in functions rather
-// than just #defining them because in debug mode, gcc doesn't
-// correctly handle the (rather involved) definitions of bswap_32.
-// gcc guarantees that inline functions are as fast as macros, so
-// this isn't a performance hit.
-inline uint16 ghtons(uint16 x) {
- return gbswap_16(x);
-}
-inline uint32_t ghtonl(uint32_t x) {
- return gbswap_32(x);
-}
-inline uint64_t ghtonll(uint64_t x) {
- return gbswap_64(x);
-}
-
-#elif defined ABSL_IS_BIG_ENDIAN
-
-// These definitions are simpler on big-endian machines
-// These are functions instead of macros to avoid self-assignment warnings
-// on calls such as "i = ghtnol(i);". This also provides type checking.
-inline uint16 ghtons(uint16 x) {
- return x;
-}
-inline uint32_t ghtonl(uint32_t x) {
- return x;
-}
-inline uint64_t ghtonll(uint64_t x) {
- return x;
-}
-
-#else
-#error \
- "Unsupported byte order: Either ABSL_IS_BIG_ENDIAN or " \
- "ABSL_IS_LITTLE_ENDIAN must be defined"
-#endif // byte order
-
-inline uint16 gntohs(uint16 x) {
- return ghtons(x);
-}
-inline uint32_t gntohl(uint32_t x) {
- return ghtonl(x);
-}
-inline uint64_t gntohll(uint64_t x) {
- return ghtonll(x);
-}
-
-// Utilities to convert numbers between the current hosts's native byte
-// order and little-endian byte order
-//
-// Load/Store methods are alignment safe
-namespace little_endian {
-// Conversion functions.
-#ifdef ABSL_IS_LITTLE_ENDIAN
-
-inline uint16 FromHost16(uint16 x) {
- return x;
-}
-inline uint16 ToHost16(uint16 x) {
- return x;
-}
-
-inline uint32_t FromHost32(uint32_t x) {
- return x;
-}
-inline uint32_t ToHost32(uint32_t x) {
- return x;
-}
-
-inline uint64_t FromHost64(uint64_t x) {
- return x;
-}
-inline uint64_t ToHost64(uint64_t x) {
- return x;
-}
-
-inline constexpr bool IsLittleEndian() {
- return true;
-}
-
-#elif defined ABSL_IS_BIG_ENDIAN
-
-inline uint16 FromHost16(uint16 x) {
- return gbswap_16(x);
-}
-inline uint16 ToHost16(uint16 x) {
- return gbswap_16(x);
-}
-
-inline uint32_t FromHost32(uint32_t x) {
- return gbswap_32(x);
-}
-inline uint32_t ToHost32(uint32_t x) {
- return gbswap_32(x);
-}
-
-inline uint64_t FromHost64(uint64_t x) {
- return gbswap_64(x);
-}
-inline uint64_t ToHost64(uint64_t x) {
- return gbswap_64(x);
-}
-
-inline constexpr bool IsLittleEndian() {
- return false;
-}
-
-#endif /* ENDIAN */
-
-// Functions to do unaligned loads and stores in little-endian order.
-inline uint16 Load16(const void *p) {
- return ToHost16(UNALIGNED_LOAD16(p));
-}
-
-inline void Store16(void *p, uint16 v) {
- UNALIGNED_STORE16(p, FromHost16(v));
-}
-
-inline uint32_t Load32(const void *p) {
- return ToHost32(UNALIGNED_LOAD32(p));
-}
-
-inline void Store32(void *p, uint32_t v) {
- UNALIGNED_STORE32(p, FromHost32(v));
-}
-
-inline uint64_t Load64(const void *p) {
- return ToHost64(UNALIGNED_LOAD64(p));
-}
-
-inline void Store64(void *p, uint64_t v) {
- UNALIGNED_STORE64(p, FromHost64(v));
-}
-
-} // namespace little_endian
-
-// Utilities to convert numbers between the current hosts's native byte
-// order and big-endian byte order (same as network byte order)
-//
-// Load/Store methods are alignment safe
-namespace big_endian {
-#ifdef ABSL_IS_LITTLE_ENDIAN
-
-inline uint16 FromHost16(uint16 x) {
- return gbswap_16(x);
-}
-inline uint16 ToHost16(uint16 x) {
- return gbswap_16(x);
-}
-
-inline uint32_t FromHost32(uint32_t x) {
- return gbswap_32(x);
-}
-inline uint32_t ToHost32(uint32_t x) {
- return gbswap_32(x);
-}
-
-inline uint64_t FromHost64(uint64_t x) {
- return gbswap_64(x);
-}
-inline uint64_t ToHost64(uint64_t x) {
- return gbswap_64(x);
-}
-
-inline constexpr bool IsLittleEndian() {
- return true;
-}
-
-#elif defined ABSL_IS_BIG_ENDIAN
-
-inline uint16 FromHost16(uint16 x) {
- return x;
-}
-inline uint16 ToHost16(uint16 x) {
- return x;
-}
-
-inline uint32_t FromHost32(uint32_t x) {
- return x;
-}
-inline uint32_t ToHost32(uint32_t x) {
- return x;
-}
-
-inline uint64_t FromHost64(uint64_t x) {
- return x;
-}
-inline uint64_t ToHost64(uint64_t x) {
- return x;
-}
-
-inline constexpr bool IsLittleEndian() {
- return false;
-}
-
-#endif /* ENDIAN */
-
-// Functions to do unaligned loads and stores in big-endian order.
-inline uint16 Load16(const void *p) {
- return ToHost16(UNALIGNED_LOAD16(p));
-}
-
-inline void Store16(void *p, uint16 v) {
- UNALIGNED_STORE16(p, FromHost16(v));
-}
-
-inline uint32_t Load32(const void *p) {
- return ToHost32(UNALIGNED_LOAD32(p));
-}
-
-inline void Store32(void *p, uint32_t v) {
- UNALIGNED_STORE32(p, FromHost32(v));
-}
-
-inline uint64_t Load64(const void *p) {
- return ToHost64(UNALIGNED_LOAD64(p));
-}
-
-inline void Store64(void *p, uint64_t v) {
- UNALIGNED_STORE64(p, FromHost64(v));
-}
-
-} // namespace big_endian
-
-} // namespace absl
-
-#endif // ABSL_BASE_INTERNAL_ENDIAN_H_
diff --git a/Firestore/Port/absl/absl_integral_types.h b/Firestore/Port/absl/absl_integral_types.h
deleted file mode 100644
index 47da9c1..0000000
--- a/Firestore/Port/absl/absl_integral_types.h
+++ /dev/null
@@ -1,148 +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.
- */
-
-// Basic integer type definitions for various platforms
-//
-// This code is compiled directly on many platforms, including client
-// platforms like Windows, Mac, and embedded systems. Before making
-// any changes here, make sure that you're not breaking any platforms.
-//
-
-#ifndef THIRD_PARTY_ABSL_BASE_INTEGRAL_TYPES_H_
-#define THIRD_PARTY_ABSL_BASE_INTEGRAL_TYPES_H_
-
-// These typedefs are also defined in base/swig/google.swig. In the
-// SWIG environment, we use those definitions and avoid duplicate
-// definitions here with an ifdef. The definitions should be the
-// same in both files, and ideally be only defined in this file.
-#ifndef SWIG
-// Standard typedefs
-// Signed integer types with width of exactly 8, 16, 32, or 64 bits
-// respectively, for use when exact sizes are required.
-typedef signed char schar;
-typedef signed char int8;
-typedef short int16;
-typedef int int32;
-#ifdef COMPILER_MSVC
-typedef __int64 int64;
-#else
-typedef long long int64;
-#endif /* COMPILER_MSVC */
-
-// NOTE: unsigned types are DANGEROUS in loops and other arithmetical
-// places. Use the signed types unless your variable represents a bit
-// pattern (eg a hash value) or you really need the extra bit. Do NOT
-// use 'unsigned' to express "this value should always be positive";
-// use assertions for this.
-
-// Unsigned integer types with width of exactly 8, 16, 32, or 64 bits
-// respectively, for use when exact sizes are required.
-typedef unsigned char uint8;
-typedef unsigned short uint16;
-typedef unsigned int uint32;
-#ifdef COMPILER_MSVC
-typedef unsigned __int64 uint64;
-#else
-typedef unsigned long long uint64;
-#endif /* COMPILER_MSVC */
-
-// A type to represent a Unicode code-point value. As of Unicode 4.0,
-// such values require up to 21 bits.
-// (For type-checking on pointers, make this explicitly signed,
-// and it should always be the signed version of whatever int32 is.)
-typedef signed int char32;
-
-// A type to represent a natural machine word (for e.g. efficiently
-// scanning through memory for checksums or index searching). Don't use
-// this for storing normal integers. Ideally this would be just
-// unsigned int, but our 64-bit architectures use the LP64 model
-// (http://en.wikipedia.org/wiki/64-bit_computing#64-bit_data_models), hence
-// their ints are only 32 bits. We want to use the same fundamental
-// type on all archs if possible to preserve *printf() compatability.
-typedef unsigned long uword_t;
-
-#endif /* SWIG */
-
-// long long macros to be used because gcc and vc++ use different suffixes,
-// and different size specifiers in format strings
-#undef GG_LONGLONG
-#undef GG_ULONGLONG
-#undef GG_LL_FORMAT
-
-#ifdef COMPILER_MSVC /* if Visual C++ */
-
-// VC++ long long suffixes
-#define GG_LONGLONG(x) x##I64
-#define GG_ULONGLONG(x) x##UI64
-
-// Length modifier in printf format std::string for int64's (e.g. within %d)
-#define GG_LL_FORMAT "I64" // As in printf("%I64d", ...)
-#define GG_LL_FORMAT_W L"I64"
-
-#else /* not Visual C++ */
-
-#define GG_LONGLONG(x) x##LL
-#define GG_ULONGLONG(x) x##ULL
-#define GG_LL_FORMAT "ll" // As in "%lld". Note that "q" is poor form also.
-#define GG_LL_FORMAT_W L"ll"
-
-#endif // COMPILER_MSVC
-
-// There are still some requirements that we build these headers in
-// C-compatibility mode. Unfortunately, -Wall doesn't like c-style
-// casts, and C doesn't know how to read braced-initialization for
-// integers.
-#if defined(__cplusplus)
-const uint8 kuint8max{0xFF};
-const uint16 kuint16max{0xFFFF};
-const uint32 kuint32max{0xFFFFFFFF};
-const uint64 kuint64max{GG_ULONGLONG(0xFFFFFFFFFFFFFFFF)};
-const int8 kint8min{~0x7F};
-const int8 kint8max{0x7F};
-const int16 kint16min{~0x7FFF};
-const int16 kint16max{0x7FFF};
-const int32 kint32min{~0x7FFFFFFF};
-const int32 kint32max{0x7FFFFFFF};
-const int64 kint64min{GG_LONGLONG(~0x7FFFFFFFFFFFFFFF)};
-const int64 kint64max{GG_LONGLONG(0x7FFFFFFFFFFFFFFF)};
-#else // not __cplusplus, this branch exists only for C-compat
-static const uint8 kuint8max = ((uint8)0xFF);
-static const uint16 kuint16max = ((uint16)0xFFFF);
-static const uint32 kuint32max = ((uint32)0xFFFFFFFF);
-static const uint64 kuint64max = ((uint64)GG_LONGLONG(0xFFFFFFFFFFFFFFFF));
-static const int8 kint8min = ((int8)~0x7F);
-static const int8 kint8max = ((int8)0x7F);
-static const int16 kint16min = ((int16)~0x7FFF);
-static const int16 kint16max = ((int16)0x7FFF);
-static const int32 kint32min = ((int32)~0x7FFFFFFF);
-static const int32 kint32max = ((int32)0x7FFFFFFF);
-static const int64 kint64min = ((int64)GG_LONGLONG(~0x7FFFFFFFFFFFFFFF));
-static const int64 kint64max = ((int64)GG_LONGLONG(0x7FFFFFFFFFFFFFFF));
-#endif // __cplusplus
-
-// The following are not real constants, but we list them so CodeSearch and
-// other tools find them, in case people are looking for the above constants
-// under different names:
-// kMaxUint8, kMaxUint16, kMaxUint32, kMaxUint64
-// kMinInt8, kMaxInt8, kMinInt16, kMaxInt16, kMinInt32, kMaxInt32,
-// kMinInt64, kMaxInt64
-
-// No object has kIllegalFprint as its Fingerprint.
-typedef uint64 Fprint;
-static const Fprint kIllegalFprint = 0;
-static const Fprint kMaxFprint = GG_ULONGLONG(0xFFFFFFFFFFFFFFFF);
-
-#endif // THIRD_PARTY_ABSL_BASE_INTEGRAL_TYPES_H_
diff --git a/Firestore/Port/absl/absl_port.h b/Firestore/Port/absl/absl_port.h
deleted file mode 100644
index eee21fc..0000000
--- a/Firestore/Port/absl/absl_port.h
+++ /dev/null
@@ -1,535 +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.
- */
-
-// Various portability macros, type definitions, and inline functions
-// This file is used for both C and C++!
-//
-// These are weird things we need to do to get this compiling on
-// random systems (and on SWIG).
-//
-// This files is structured into the following high-level categories:
-// - Platform checks (OS, Compiler, C++, Library)
-// - Feature macros
-// - Utility macros
-// - Utility functions
-// - Type alias
-// - Predefined system/language macros
-// - Predefined system/language functions
-// - Compiler attributes (__attribute__)
-// - Performance optimization (alignment, branch prediction)
-// - Obsolete
-//
-
-#ifndef THIRD_PARTY_ABSL_BASE_PORT_H_
-#define THIRD_PARTY_ABSL_BASE_PORT_H_
-
-#include <assert.h>
-#include <limits.h> // So we can set the bounds of our types
-#include <stdlib.h> // for free()
-#include <string.h> // for memcpy()
-
-#include "Firestore/Port/absl/absl_attributes.h"
-#include "Firestore/Port/absl/absl_config.h"
-#include "Firestore/Port/absl/absl_integral_types.h"
-
-#ifdef SWIG
-%include "attributes.h"
-#endif
-
-// -----------------------------------------------------------------------------
-// Operating System Check
-// -----------------------------------------------------------------------------
-
-#if defined(__CYGWIN__)
-#error "Cygwin is not supported."
-#endif
-
-// -----------------------------------------------------------------------------
-// Compiler Check
-// -----------------------------------------------------------------------------
-
-// We support MSVC++ 14.0 update 2 and later.
-#if defined(_MSC_FULL_VER) && _MSC_FULL_VER < 190023918
-#error "This package requires Visual Studio 2015 Update 2 or higher"
-#endif
-
-// We support gcc 4.7 and later.
-#if defined(__GNUC__) && !defined(__clang__)
-#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 7)
-#error "This package requires gcc 4.7 or higher"
-#endif
-#endif
-
-// We support Apple Xcode clang 4.2.1 (version 421.11.65) and later.
-// This corresponds to Apple Xcode version 4.5.
-#if defined(__apple_build_version__) && __apple_build_version__ < 4211165
-#error "This package requires __apple_build_version__ of 4211165 or higher"
-#endif
-
-// -----------------------------------------------------------------------------
-// C++ Version Check
-// -----------------------------------------------------------------------------
-
-// Enforce C++11 as the minimum. Note that Visual Studio has not
-// advanced __cplusplus despite being good enough for our purposes, so
-// so we exempt it from the check.
-#if defined(__cplusplus) && !defined(_MSC_VER) && !defined(SWIG)
-#if __cplusplus < 201103L
-#error "C++ versions less than C++11 are not supported."
-#endif
-#endif
-
-// -----------------------------------------------------------------------------
-// C++ Standard Library Check
-// -----------------------------------------------------------------------------
-
-#if defined(__cplusplus)
-#include <cstddef>
-#if defined(_STLPORT_VERSION)
-#error "STLPort is not supported."
-#endif
-#endif
-
-// -----------------------------------------------------------------------------
-// Feature Macros
-// -----------------------------------------------------------------------------
-
-// ABSL_HAVE_TLS is defined to 1 when __thread should be supported.
-// We assume __thread is supported on Linux when compiled with Clang or compiled
-// against libstdc++ with _GLIBCXX_HAVE_TLS defined.
-#ifdef ABSL_HAVE_TLS
-#error ABSL_HAVE_TLS cannot be directly set
-#elif defined(__linux__) && (defined(__clang__) || defined(_GLIBCXX_HAVE_TLS))
-#define ABSL_HAVE_TLS 1
-#endif
-
-// -----------------------------------------------------------------------------
-// Utility Macros
-// -----------------------------------------------------------------------------
-
-// ABSL_FUNC_PTR_TO_CHAR_PTR
-// On some platforms, a "function pointer" points to a function descriptor
-// rather than directly to the function itself.
-// Use ABSL_FUNC_PTR_TO_CHAR_PTR(func) to get a char-pointer to the first
-// instruction of the function func.
-#if defined(__cplusplus)
-#if (defined(__powerpc__) && !(_CALL_ELF > 1)) || defined(__ia64)
-// use opd section for function descriptors on these platforms, the function
-// address is the first word of the descriptor
-namespace absl {
-enum { kPlatformUsesOPDSections = 1 };
-} // namespace absl
-#define ABSL_FUNC_PTR_TO_CHAR_PTR(func) (reinterpret_cast<char **>(func)[0])
-#else // not PPC or IA64
-namespace absl {
-enum { kPlatformUsesOPDSections = 0 };
-} // namespace absl
-#define ABSL_FUNC_PTR_TO_CHAR_PTR(func) (reinterpret_cast<char *>(func))
-#endif // PPC or IA64
-#endif // __cplusplus
-
-// -----------------------------------------------------------------------------
-// Utility Functions
-// -----------------------------------------------------------------------------
-
-#if defined(__cplusplus)
-namespace absl {
-constexpr char PathSeparator() {
-#ifdef _WIN32
- return '\\';
-#else
- return '/';
-#endif
-}
-} // namespace absl
-#endif // __cplusplus
-
-// -----------------------------------------------------------------------------
-// Type Alias
-// -----------------------------------------------------------------------------
-
-#ifdef _MSC_VER
-// uid_t
-// MSVC doesn't have uid_t
-typedef int uid_t;
-
-// pid_t
-// Defined all over the place.
-typedef int pid_t;
-
-// ssize_t
-// VC++ doesn't understand "ssize_t". SSIZE_T is defined in <basetsd.h>.
-#include <basetsd.h>
-typedef SSIZE_T ssize_t;
-#endif // _MSC_VER
-
-// -----------------------------------------------------------------------------
-// Predefined System/Language Macros
-// -----------------------------------------------------------------------------
-
-// MAP_ANONYMOUS
-#if defined(__APPLE__) && defined(__MACH__)
-// For mmap, Linux defines both MAP_ANONYMOUS and MAP_ANON and says MAP_ANON is
-// deprecated. In Darwin, MAP_ANON is all there is.
-#if !defined MAP_ANONYMOUS
-#define MAP_ANONYMOUS MAP_ANON
-#endif // !MAP_ANONYMOUS
-#endif // __APPLE__ && __MACH__
-
-// PATH_MAX
-// You say tomato, I say atotom
-#ifdef _MSC_VER
-#define PATH_MAX MAX_PATH
-#endif
-
-// -----------------------------------------------------------------------------
-// Performance Optimization
-// -----------------------------------------------------------------------------
-
-// Alignment
-
-// CACHELINE_SIZE, CACHELINE_ALIGNED
-// Deprecated: Use ABSL_CACHELINE_SIZE, ABSL_CACHELINE_ALIGNED.
-// Note: When C++17 is available, consider using the following:
-// - std::hardware_constructive_interference_size
-// - std::hardware_destructive_interference_size
-// See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0154r1.html
-#if defined(__GNUC__)
-#if defined(__i386__) || defined(__x86_64__)
-#define CACHELINE_SIZE 64
-#define ABSL_CACHELINE_SIZE 64
-#elif defined(__powerpc64__)
-#define CACHELINE_SIZE 128
-#define ABSL_CACHELINE_SIZE 128
-#elif defined(__aarch64__)
-// We would need to read special register ctr_el0 to find out L1 dcache size.
-// This value is a good estimate based on a real aarch64 machine.
-#define CACHELINE_SIZE 64
-#define ABSL_CACHELINE_SIZE 64
-#elif defined(__arm__)
-// Cache line sizes for ARM: These values are not strictly correct since
-// cache line sizes depend on implementations, not architectures. There
-// are even implementations with cache line sizes configurable at boot
-// time.
-#if defined(__ARM_ARCH_5T__)
-#define CACHELINE_SIZE 32
-#define ABSL_CACHELINE_SIZE 32
-#elif defined(__ARM_ARCH_7A__)
-#define CACHELINE_SIZE 64
-#define ABSL_CACHELINE_SIZE 64
-#endif
-#endif
-
-#ifndef CACHELINE_SIZE
-// A reasonable default guess. Note that overestimates tend to waste more
-// space, while underestimates tend to waste more time.
-#define CACHELINE_SIZE 64
-#define ABSL_CACHELINE_SIZE 64
-#endif
-
-// On some compilers, expands to __attribute__((aligned(CACHELINE_SIZE))).
-// For compilers where this is not known to work, expands to nothing.
-//
-// No further guarantees are made here. The result of applying the macro
-// to variables and types is always implementation defined.
-//
-// WARNING: It is easy to use this attribute incorrectly, even to the point
-// of causing bugs that are difficult to diagnose, crash, etc. It does not
-// guarantee that objects are aligned to a cache line.
-//
-// Recommendations:
-//
-// 1) Consult compiler documentation; this comment is not kept in sync as
-// toolchains evolve.
-// 2) Verify your use has the intended effect. This often requires inspecting
-// the generated machine code.
-// 3) Prefer applying this attribute to individual variables. Avoid
-// applying it to types. This tends to localize the effect.
-#define CACHELINE_ALIGNED __attribute__((aligned(CACHELINE_SIZE)))
-#define ABSL_CACHELINE_ALIGNED __attribute__((aligned(ABSL_CACHELINE_SIZE)))
-
-#else // not GCC
-#define CACHELINE_SIZE 64
-#define ABSL_CACHELINE_SIZE 64
-#define CACHELINE_ALIGNED
-#define ABSL_CACHELINE_ALIGNED
-#endif
-
-// unaligned APIs
-
-// Portable handling of unaligned loads, stores, and copies.
-// On some platforms, like ARM, the copy functions can be more efficient
-// then a load and a store.
-//
-// It is possible to implement all of these these using constant-length memcpy
-// calls, which is portable and will usually be inlined into simple loads and
-// stores if the architecture supports it. However, such inlining usually
-// happens in a pass that's quite late in compilation, which means the resulting
-// loads and stores cannot participate in many other optimizations, leading to
-// overall worse code.
-
-// The unaligned API is C++ only. The declarations use C++ features
-// (namespaces, inline) which are absent or incompatible in C.
-#if defined(__cplusplus)
-
-#if defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER) || defined(MEMORY_SANITIZER)
-// Consider we have an unaligned load/store of 4 bytes from address 0x...05.
-// AddressSanitizer will treat it as a 3-byte access to the range 05:07 and
-// will miss a bug if 08 is the first unaddressable byte.
-// ThreadSanitizer will also treat this as a 3-byte access to 05:07 and will
-// miss a race between this access and some other accesses to 08.
-// MemorySanitizer will correctly propagate the shadow on unaligned stores
-// and correctly report bugs on unaligned loads, but it may not properly
-// update and report the origin of the uninitialized memory.
-// For all three tools, replacing an unaligned access with a tool-specific
-// callback solves the problem.
-
-// Make sure uint16_t/uint32_t/uint64_t are defined.
-#include <stdint.h>
-
-extern "C" {
-uint16_t __sanitizer_unaligned_load16(const void *p);
-uint32_t __sanitizer_unaligned_load32(const void *p);
-uint64_t __sanitizer_unaligned_load64(const void *p);
-void __sanitizer_unaligned_store16(void *p, uint16_t v);
-void __sanitizer_unaligned_store32(void *p, uint32_t v);
-void __sanitizer_unaligned_store64(void *p, uint64_t v);
-} // extern "C"
-
-inline uint16 UNALIGNED_LOAD16(const void *p) {
- return __sanitizer_unaligned_load16(p);
-}
-
-inline uint32 UNALIGNED_LOAD32(const void *p) {
- return __sanitizer_unaligned_load32(p);
-}
-
-inline uint64 UNALIGNED_LOAD64(const void *p) {
- return __sanitizer_unaligned_load64(p);
-}
-
-inline void UNALIGNED_STORE16(void *p, uint16 v) {
- __sanitizer_unaligned_store16(p, v);
-}
-
-inline void UNALIGNED_STORE32(void *p, uint32 v) {
- __sanitizer_unaligned_store32(p, v);
-}
-
-inline void UNALIGNED_STORE64(void *p, uint64 v) {
- __sanitizer_unaligned_store64(p, v);
-}
-
-#elif defined(__x86_64__) || defined(_M_X64) || defined(__i386) || defined(_M_IX86) || \
- defined(__ppc__) || defined(__PPC__) || defined(__ppc64__) || defined(__PPC64__)
-
-// x86 and x86-64 can perform unaligned loads/stores directly;
-// modern PowerPC hardware can also do unaligned integer loads and stores;
-// but note: the FPU still sends unaligned loads and stores to a trap handler!
-
-#define UNALIGNED_LOAD16(_p) (*reinterpret_cast<const uint16 *>(_p))
-#define UNALIGNED_LOAD32(_p) (*reinterpret_cast<const uint32 *>(_p))
-#define UNALIGNED_LOAD64(_p) (*reinterpret_cast<const uint64 *>(_p))
-
-#define UNALIGNED_STORE16(_p, _val) (*reinterpret_cast<uint16 *>(_p) = (_val))
-#define UNALIGNED_STORE32(_p, _val) (*reinterpret_cast<uint32 *>(_p) = (_val))
-#define UNALIGNED_STORE64(_p, _val) (*reinterpret_cast<uint64 *>(_p) = (_val))
-
-#elif defined(__arm__) && !defined(__ARM_ARCH_5__) && !defined(__ARM_ARCH_5T__) && \
- !defined(__ARM_ARCH_5TE__) && !defined(__ARM_ARCH_5TEJ__) && !defined(__ARM_ARCH_6__) && \
- !defined(__ARM_ARCH_6J__) && !defined(__ARM_ARCH_6K__) && !defined(__ARM_ARCH_6Z__) && \
- !defined(__ARM_ARCH_6ZK__) && !defined(__ARM_ARCH_6T2__)
-
-// ARMv7 and newer support native unaligned accesses, but only of 16-bit
-// 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
-// sub-architectures. Newer gcc (>= 4.6) set an __ARM_FEATURE_ALIGNED #define,
-// so in time, maybe we can move on to that.
-//
-// This is a mess, but there's not much we can do about it.
-//
-// To further complicate matters, only LDR instructions (single reads) are
-// allowed to be unaligned, not LDRD (two reads) or LDM (many reads). Unless we
-// explicitly tell the compiler that these accesses can be unaligned, it can and
-// will combine accesses. On armcc, the way to signal this is done by accessing
-// through the type (uint32 __packed *), but GCC has no such attribute
-// (it ignores __attribute__((packed)) on individual variables). However,
-// we can tell it that a _struct_ is unaligned, which has the same effect,
-// so we do that.
-
-namespace base {
-namespace internal {
-
-struct Unaligned16Struct {
- uint16 value;
- uint8 dummy; // To make the size non-power-of-two.
-} ATTRIBUTE_PACKED;
-
-struct Unaligned32Struct {
- uint32 value;
- uint8 dummy; // To make the size non-power-of-two.
-} ATTRIBUTE_PACKED;
-
-} // namespace internal
-} // namespace base
-
-#define UNALIGNED_LOAD16(_p) \
- ((reinterpret_cast<const ::base::internal::Unaligned16Struct *>(_p))->value)
-#define UNALIGNED_LOAD32(_p) \
- ((reinterpret_cast<const ::base::internal::Unaligned32Struct *>(_p))->value)
-
-#define UNALIGNED_STORE16(_p, _val) \
- ((reinterpret_cast< ::base::internal::Unaligned16Struct *>(_p))->value = (_val))
-#define UNALIGNED_STORE32(_p, _val) \
- ((reinterpret_cast< ::base::internal::Unaligned32Struct *>(_p))->value = (_val))
-
-inline uint64 UNALIGNED_LOAD64(const void *p) {
- uint64 t;
- memcpy(&t, p, sizeof t);
- return t;
-}
-
-inline void UNALIGNED_STORE64(void *p, uint64 v) {
- memcpy(p, &v, sizeof v);
-}
-
-#else
-
-#define NEED_ALIGNED_LOADS
-
-// These functions are provided for architectures that don't support
-// unaligned loads and stores.
-
-inline uint16 UNALIGNED_LOAD16(const void *p) {
- uint16 t;
- memcpy(&t, p, sizeof t);
- return t;
-}
-
-inline uint32 UNALIGNED_LOAD32(const void *p) {
- uint32 t;
- memcpy(&t, p, sizeof t);
- return t;
-}
-
-inline uint64 UNALIGNED_LOAD64(const void *p) {
- uint64 t;
- memcpy(&t, p, sizeof t);
- return t;
-}
-
-inline void UNALIGNED_STORE16(void *p, uint16 v) {
- memcpy(p, &v, sizeof v);
-}
-
-inline void UNALIGNED_STORE32(void *p, uint32 v) {
- memcpy(p, &v, sizeof v);
-}
-
-inline void UNALIGNED_STORE64(void *p, uint64 v) {
- memcpy(p, &v, sizeof v);
-}
-
-#endif
-
-// The UNALIGNED_LOADW and UNALIGNED_STOREW macros load and store values
-// of type uword_t.
-#ifdef _LP64
-#define UNALIGNED_LOADW(_p) UNALIGNED_LOAD64(_p)
-#define UNALIGNED_STOREW(_p, _val) UNALIGNED_STORE64(_p, _val)
-#else
-#define UNALIGNED_LOADW(_p) UNALIGNED_LOAD32(_p)
-#define UNALIGNED_STOREW(_p, _val) UNALIGNED_STORE32(_p, _val)
-#endif
-
-inline void UnalignedCopy16(const void *src, void *dst) {
- UNALIGNED_STORE16(dst, UNALIGNED_LOAD16(src));
-}
-
-inline void UnalignedCopy32(const void *src, void *dst) {
- UNALIGNED_STORE32(dst, UNALIGNED_LOAD32(src));
-}
-
-inline void UnalignedCopy64(const void *src, void *dst) {
- if (sizeof(void *) == 8) {
- UNALIGNED_STORE64(dst, UNALIGNED_LOAD64(src));
- } else {
- const char *src_char = reinterpret_cast<const char *>(src);
- char *dst_char = reinterpret_cast<char *>(dst);
-
- UNALIGNED_STORE32(dst_char, UNALIGNED_LOAD32(src_char));
- UNALIGNED_STORE32(dst_char + 4, UNALIGNED_LOAD32(src_char + 4));
- }
-}
-
-#endif // defined(__cplusplus), end of unaligned API
-
-// PREDICT_TRUE, PREDICT_FALSE
-//
-// GCC can be told that a certain branch is not likely to be taken (for
-// instance, a CHECK failure), and use that information in static analysis.
-// Giving it this information can help it optimize for the common case in
-// the absence of better information (ie. -fprofile-arcs).
-#if ABSL_HAVE_BUILTIN(__builtin_expect) || (defined(__GNUC__) && !defined(__clang__))
-#define PREDICT_FALSE(x) (__builtin_expect(x, 0))
-#define PREDICT_TRUE(x) (__builtin_expect(!!(x), 1))
-#define ABSL_PREDICT_FALSE(x) (__builtin_expect(x, 0))
-#define ABSL_PREDICT_TRUE(x) (__builtin_expect(!!(x), 1))
-#else
-#define PREDICT_FALSE(x) x
-#define PREDICT_TRUE(x) x
-#define ABSL_PREDICT_FALSE(x) x
-#define ABSL_PREDICT_TRUE(x) x
-#endif
-
-// ABSL_ASSERT
-//
-// In C++11, `assert` can't be used portably within constexpr functions.
-// ABSL_ASSERT functions as a runtime assert but works in C++11 constexpr
-// functions. Example:
-//
-// constexpr double Divide(double a, double b) {
-// return ABSL_ASSERT(b != 0), a / b;
-// }
-//
-// This macro is inspired by
-// https://akrzemi1.wordpress.com/2017/05/18/asserts-in-constexpr-functions/
-#if defined(NDEBUG)
-#define ABSL_ASSERT(expr) (false ? (void)(expr) : (void)0)
-#else
-#define ABSL_ASSERT(expr) \
- (PREDICT_TRUE((expr)) ? (void)0 : [] { assert(false && #expr); }()) // NOLINT
-#endif
-
-// -----------------------------------------------------------------------------
-// Obsolete (to be removed)
-// -----------------------------------------------------------------------------
-
-// HAS_GLOBAL_STRING
-// Some platforms have a std::string class that is different from ::std::string
-// (although the interface is the same, of course). On other platforms,
-// std::string is the same as ::std::string.
-#if defined(__cplusplus) && !defined(SWIG)
-#include <string>
-#ifndef HAS_GLOBAL_STRING
-using std::basic_string;
-using std::string;
-#endif // HAS_GLOBAL_STRING
-#endif // SWIG, __cplusplus
-
-#endif // THIRD_PARTY_ABSL_BASE_PORT_H_
diff --git a/Firestore/Port/string_util.h b/Firestore/Port/string_util.h
deleted file mode 100644
index 6e85ba9..0000000
--- 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 <string>
-
-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/Source/API/FIRCollectionReference.mm b/Firestore/Source/API/FIRCollectionReference.mm
index 92cccc6..a8de29b 100644
--- a/Firestore/Source/API/FIRCollectionReference.mm
+++ b/Firestore/Source/API/FIRCollectionReference.mm
@@ -15,6 +15,7 @@
*/
#import "FIRCollectionReference.h"
+#import "FIRFirestore.h"
#include "Firestore/core/src/firebase/firestore/util/autoid.h"
@@ -65,6 +66,26 @@ NS_ASSUME_NONNULL_BEGIN
FSTFail(@"Use FIRCollectionReference initWithPath: initializer.");
}
+// NSObject Methods
+- (BOOL)isEqual:(nullable id)other {
+ if (other == self) return YES;
+ if (![[other class] isEqual:[self class]]) return NO;
+
+ return [self isEqualToReference:other];
+}
+
+- (BOOL)isEqualToReference:(nullable FIRCollectionReference *)reference {
+ if (self == reference) return YES;
+ if (reference == nil) return NO;
+ return [self.firestore isEqual:reference.firestore] && [self.query isEqual:reference.query];
+}
+
+- (NSUInteger)hash {
+ NSUInteger hash = [self.firestore hash];
+ hash = hash * 31u + [self.query hash];
+ return hash;
+}
+
- (NSString *)collectionID {
return [self.query.path lastSegment];
}
diff --git a/Firestore/Source/API/FIRDocumentChange+Internal.h b/Firestore/Source/API/FIRDocumentChange+Internal.h
index 7e2e5c6..7c9723c 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
index 970dc90..d1d9999 100644
--- a/Firestore/Source/API/FIRDocumentChange.m
+++ b/Firestore/Source/API/FIRDocumentChange.mm
@@ -57,11 +57,11 @@ NS_ASSUME_NONNULL_BEGIN
NSUInteger index = 0;
NSMutableArray<FIRDocumentChange *> *changes = [NSMutableArray array];
for (FSTDocumentViewChange *change in snapshot.documentChanges) {
- FIRDocumentSnapshot *document =
- [FIRDocumentSnapshot snapshotWithFirestore:firestore
- documentKey:change.document.key
- document:change.document
- fromCache:snapshot.isFromCache];
+ FIRQueryDocumentSnapshot *document =
+ [FIRQueryDocumentSnapshot snapshotWithFirestore:firestore
+ documentKey:change.document.key
+ document:change.document
+ fromCache:snapshot.isFromCache];
FSTAssert(change.type == FSTDocumentViewChangeTypeAdded,
@"Invalid event type for first snapshot");
FSTAssert(!lastDocument ||
@@ -79,11 +79,11 @@ NS_ASSUME_NONNULL_BEGIN
FSTDocumentSet *indexTracker = snapshot.oldDocuments;
NSMutableArray<FIRDocumentChange *> *changes = [NSMutableArray array];
for (FSTDocumentViewChange *change in snapshot.documentChanges) {
- FIRDocumentSnapshot *document =
- [FIRDocumentSnapshot snapshotWithFirestore:firestore
- documentKey:change.document.key
- document:change.document
- fromCache:snapshot.isFromCache];
+ FIRQueryDocumentSnapshot *document =
+ [FIRQueryDocumentSnapshot snapshotWithFirestore:firestore
+ documentKey:change.document.key
+ document:change.document
+ fromCache:snapshot.isFromCache];
NSUInteger oldIndex = NSNotFound;
NSUInteger newIndex = NSNotFound;
@@ -112,7 +112,7 @@ NS_ASSUME_NONNULL_BEGIN
@implementation FIRDocumentChange
- (instancetype)initWithType:(FIRDocumentChangeType)type
- document:(FIRDocumentSnapshot *)document
+ document:(FIRQueryDocumentSnapshot *)document
oldIndex:(NSUInteger)oldIndex
newIndex:(NSUInteger)newIndex {
if (self = [super init]) {
diff --git a/Firestore/Source/API/FIRDocumentReference.m b/Firestore/Source/API/FIRDocumentReference.mm
index 1c80ea9..05253f7 100644
--- a/Firestore/Source/API/FIRDocumentReference.m
+++ b/Firestore/Source/API/FIRDocumentReference.mm
@@ -48,6 +48,8 @@ NS_ASSUME_NONNULL_BEGIN
- (instancetype)initWithIncludeMetadataChanges:(BOOL)includeMetadataChanges
NS_DESIGNATED_INITIALIZER;
+@property(nonatomic, assign, readonly) BOOL includeMetadataChanges;
+
@end
@implementation FIRDocumentListenOptions
@@ -114,7 +116,7 @@ NS_ASSUME_NONNULL_BEGIN
- (BOOL)isEqual:(nullable id)other {
if (other == self) return YES;
- if (!other || ![[other class] isEqual:[self class]]) return NO;
+ if (![[other class] isEqual:[self class]]) return NO;
return [self isEqualToReference:other];
}
@@ -122,10 +124,7 @@ NS_ASSUME_NONNULL_BEGIN
- (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
index b78472e..10709e8 100644
--- a/Firestore/Source/API/FIRDocumentSnapshot.m
+++ b/Firestore/Source/API/FIRDocumentSnapshot.mm
@@ -20,11 +20,13 @@
#import "Firestore/Source/API/FIRFieldPath+Internal.h"
#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"
#import "Firestore/Source/Model/FSTPath.h"
+#import "Firestore/Source/Util/FSTAssert.h"
#import "Firestore/Source/Util/FSTUsageValidation.h"
NS_ASSUME_NONNULL_BEGIN
@@ -49,10 +51,10 @@ NS_ASSUME_NONNULL_BEGIN
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
@@ -76,6 +78,34 @@ NS_ASSUME_NONNULL_BEGIN
return self;
}
+// NSObject Methods
+- (BOOL)isEqual:(nullable id)other {
+ if (other == self) return YES;
+ // self class could be FIRDocumentSnapshot or subtype. So we compare with base type explicitly.
+ if (![other isKindOfClass:[FIRDocumentSnapshot class]]) return NO;
+
+ return [self isEqualToSnapshot:other];
+}
+
+- (BOOL)isEqualToSnapshot:(nullable FIRDocumentSnapshot *)snapshot {
+ if (self == snapshot) return YES;
+ if (snapshot == nil) return NO;
+
+ 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 {
+ NSUInteger hash = [self.firestore hash];
+ hash = hash * 31u + [self.internalKey hash];
+ hash = hash * 31u + [self.internalDocument hash];
+ hash = hash * 31u + (self.fromCache ? 1 : 0);
+ return hash;
+}
+
@dynamic exists;
- (BOOL)exists {
@@ -99,40 +129,48 @@ NS_ASSUME_NONNULL_BEGIN
return _cachedMetadata;
}
-- (NSDictionary<NSString *, id> *)data {
- FSTDocument *document = self.internalDocument;
-
- if (!document) {
- FSTThrowInvalidUsage(
- @"NonExistentDocumentException",
- @"Document '%@' doesn't exist. "
- @"Check document.exists to make sure the document exists before calling document.data.",
- self.internalKey);
- }
+- (nullable NSDictionary<NSString *, id> *)data {
+ return [self dataWithOptions:[FIRSnapshotOptions defaultOptions]];
+}
- return [self convertedObject:[self.internalDocument data]];
+- (nullable NSDictionary<NSString *, id> *)dataWithOptions:(FIRSnapshotOptions *)options {
+ return self.internalDocument == nil
+ ? nil
+ : [self convertedObject:[self.internalDocument data]
+ options:[FSTFieldValueOptions optionsForSnapshotOptions:options]];
}
-- (nullable id)objectForKeyedSubscript:(id)key {
+- (nullable id)valueForField:(id)field {
+ return [self valueForField:field options:[FIRSnapshotOptions defaultOptions]];
+}
+
+- (nullable id)valueForField:(id)field options:(FIRSnapshotOptions *)options {
FIRFieldPath *fieldPath;
- if ([key isKindOfClass:[NSString class]]) {
- fieldPath = [FIRFieldPath pathWithDotSeparatedString:key];
- } else if ([key isKindOfClass:[FIRFieldPath class]]) {
- fieldPath = key;
+ if ([field isKindOfClass:[NSString class]]) {
+ fieldPath = [FIRFieldPath pathWithDotSeparatedString:field];
+ } else if ([field isKindOfClass:[FIRFieldPath class]]) {
+ fieldPath = field;
} else {
FSTThrowInvalidArgument(@"Subscript key must be an NSString or FIRFieldPath.");
}
FSTFieldValue *fieldValue = [[self.internalDocument data] valueForPath:fieldPath.internalValue];
- return [self convertedValue:fieldValue];
+ return fieldValue == nil
+ ? nil
+ : [self convertedValue:fieldValue
+ options:[FSTFieldValueOptions optionsForSnapshotOptions:options]];
}
-- (id)convertedValue:(FSTFieldValue *)value {
+- (nullable id)objectForKeyedSubscript:(id)key {
+ return [self valueForField:key];
+}
+
+- (id)convertedValue:(FSTFieldValue *)value options:(FSTFieldValueOptions *)options {
if ([value isKindOfClass:[FSTObjectValue class]]) {
- return [self convertedObject:(FSTObjectValue *)value];
+ return [self convertedObject:(FSTObjectValue *)value options:options];
} else if ([value isKindOfClass:[FSTArrayValue class]]) {
- return [self convertedArray:(FSTArrayValue *)value];
+ return [self convertedArray:(FSTArrayValue *)value options:options];
} else if ([value isKindOfClass:[FSTReferenceValue class]]) {
FSTReferenceValue *ref = (FSTReferenceValue *)value;
FSTDatabaseID *refDatabase = ref.databaseID;
@@ -146,30 +184,69 @@ NS_ASSUME_NONNULL_BEGIN
self.reference.path, refDatabase.projectID, refDatabase.databaseID, database.projectID,
database.databaseID);
}
- return [FIRDocumentReference referenceWithKey:ref.value firestore:self.firestore];
+ return [FIRDocumentReference referenceWithKey:[ref valueWithOptions:options]
+ firestore:self.firestore];
} else {
- return value.value;
+ return [value valueWithOptions:options];
}
}
-- (NSDictionary<NSString *, id> *)convertedObject:(FSTObjectValue *)objectValue {
+- (NSDictionary<NSString *, id> *)convertedObject:(FSTObjectValue *)objectValue
+ options:(FSTFieldValueOptions *)options {
NSMutableDictionary *result = [NSMutableDictionary dictionary];
[objectValue.internalValue
enumerateKeysAndObjectsUsingBlock:^(NSString *key, FSTFieldValue *value, BOOL *stop) {
- result[key] = [self convertedValue:value];
+ result[key] = [self convertedValue:value options:options];
}];
return result;
}
-- (NSArray<id> *)convertedArray:(FSTArrayValue *)arrayValue {
+- (NSArray<id> *)convertedArray:(FSTArrayValue *)arrayValue
+ options:(FSTFieldValueOptions *)options {
NSArray<FSTFieldValue *> *internalValue = arrayValue.internalValue;
NSMutableArray *result = [NSMutableArray arrayWithCapacity:internalValue.count];
[internalValue enumerateObjectsUsingBlock:^(id value, NSUInteger idx, BOOL *stop) {
- [result addObject:[self convertedValue:value]];
+ [result addObject:[self convertedValue:value options:options]];
}];
return result;
}
@end
+@interface FIRQueryDocumentSnapshot ()
+
+- (instancetype)initWithFirestore:(FIRFirestore *)firestore
+ documentKey:(FSTDocumentKey *)documentKey
+ document:(FSTDocument *)document
+ fromCache:(BOOL)fromCache NS_DESIGNATED_INITIALIZER;
+
+@end
+
+@implementation FIRQueryDocumentSnapshot
+
+- (instancetype)initWithFirestore:(FIRFirestore *)firestore
+ documentKey:(FSTDocumentKey *)documentKey
+ document:(FSTDocument *)document
+ fromCache:(BOOL)fromCache {
+ self = [super initWithFirestore:firestore
+ documentKey:documentKey
+ document:document
+ fromCache:fromCache];
+ return self;
+}
+
+- (NSDictionary<NSString *, id> *)data {
+ NSDictionary<NSString *, id> *data = [super data];
+ FSTAssert(data, @"Document in a QueryDocumentSnapshot should exist");
+ return data;
+}
+
+- (NSDictionary<NSString *, id> *)dataWithOptions:(FIRSnapshotOptions *)options {
+ NSDictionary<NSString *, id> *data = [super dataWithOptions:options];
+ FSTAssert(data, @"Document in a QueryDocumentSnapshot should exist");
+ return data;
+}
+
+@end
+
NS_ASSUME_NONNULL_END
diff --git a/Firestore/Source/API/FIRFieldPath.m b/Firestore/Source/API/FIRFieldPath.mm
index d0a70c0..f4e532f 100644
--- a/Firestore/Source/API/FIRFieldPath.m
+++ b/Firestore/Source/API/FIRFieldPath.mm
@@ -80,7 +80,7 @@ NS_ASSUME_NONNULL_BEGIN
return [[[self class] alloc] initPrivate:self.internalValue];
}
-- (BOOL)isEqual:(id)object {
+- (BOOL)isEqual:(nullable id)object {
if (self == object) {
return YES;
}
diff --git a/Firestore/Source/API/FIRFieldValue.m b/Firestore/Source/API/FIRFieldValue.mm
index 7ae4fb0..7ae4fb0 100644
--- a/Firestore/Source/API/FIRFieldValue.m
+++ b/Firestore/Source/API/FIRFieldValue.mm
diff --git a/Firestore/Source/API/FIRFirestore.m b/Firestore/Source/API/FIRFirestore.mm
index 7814ce1..10367bd 100644
--- a/Firestore/Source/API/FIRFirestore.m
+++ b/Firestore/Source/API/FIRFirestore.mm
@@ -41,7 +41,7 @@
NS_ASSUME_NONNULL_BEGIN
-NSString *const FIRFirestoreErrorDomain = @"FIRFirestoreErrorDomain";
+extern "C" NSString *const FIRFirestoreErrorDomain = @"FIRFirestoreErrorDomain";
@interface FIRFirestore ()
@@ -50,13 +50,17 @@ NSString *const FIRFirestoreErrorDomain = @"FIRFirestoreErrorDomain";
@property(nonatomic, strong) id<FSTCredentialsProvider> credentialsProvider;
@property(nonatomic, strong) FSTDispatchQueue *workerDispatchQueue;
-@property(nonatomic, strong) FSTFirestoreClient *client;
+// Note that `client` is updated after initialization, but marking this readwrite would generate an
+// incorrect setter (since we make the assignment to `client` inside an `@synchronized` block.
+@property(nonatomic, strong, readonly) FSTFirestoreClient *client;
@property(nonatomic, strong, readonly) FSTUserDataConverter *dataConverter;
@end
@implementation FIRFirestore {
+ // All guarded by @synchronized(self)
FIRFirestoreSettings *_settings;
+ FSTFirestoreClient *_client;
}
+ (NSMutableDictionary<NSString *, FIRFirestore *> *)instances {
@@ -154,64 +158,74 @@ NSString *const FIRFirestoreErrorDomain = @"FIRFirestoreErrorDomain";
}
- (FIRFirestoreSettings *)settings {
- // Disallow mutation of our internal settings
- return [_settings copy];
+ @synchronized(self) {
+ // Disallow mutation of our internal settings
+ return [_settings copy];
+ }
}
- (void)setSettings:(FIRFirestoreSettings *)settings {
- // As a special exception, don't throw if the same settings are passed repeatedly. This should
- // make it more friendly to create a Firestore instance.
- if (_client && ![_settings isEqual:settings]) {
- FSTThrowInvalidUsage(@"FIRIllegalStateException",
- @"Firestore instance has already been started and its settings can no "
- "longer be changed. You can only set settings before calling any "
- "other methods on a Firestore instance.");
+ @synchronized(self) {
+ // As a special exception, don't throw if the same settings are passed repeatedly. This should
+ // make it more friendly to create a Firestore instance.
+ if (_client && ![_settings isEqual:settings]) {
+ FSTThrowInvalidUsage(@"FIRIllegalStateException",
+ @"Firestore instance has already been started and its settings can no "
+ "longer be changed. You can only set settings before calling any "
+ "other methods on a Firestore instance.");
+ }
+ _settings = [settings copy];
}
- _settings = [settings copy];
}
/**
- * Ensures that the FirestoreClient is configured.
- * @return self
+ * Ensures that the FirestoreClient is configured and returns it.
*/
-- (instancetype)firestoreWithConfiguredClient {
- if (!_client) {
- // These values are validated elsewhere; this is just double-checking:
- 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];
-
- FSTDispatchQueue *userDispatchQueue = [FSTDispatchQueue queueWith:_settings.dispatchQueue];
-
- _client = [FSTFirestoreClient clientWithDatabaseInfo:databaseInfo
- usePersistence:_settings.persistenceEnabled
- credentialsProvider:_credentialsProvider
- userDispatchQueue:userDispatchQueue
- workerDispatchQueue:_workerDispatchQueue];
+- (FSTFirestoreClient *)client {
+ [self ensureClientConfigured];
+ return _client;
+}
+
+- (void)ensureClientConfigured {
+ @synchronized(self) {
+ if (!_client) {
+ // These values are validated elsewhere; this is just double-checking:
+ 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];
+
+ FSTDispatchQueue *userDispatchQueue = [FSTDispatchQueue queueWith:_settings.dispatchQueue];
+
+ _client = [FSTFirestoreClient clientWithDatabaseInfo:databaseInfo
+ usePersistence:_settings.persistenceEnabled
+ credentialsProvider:_credentialsProvider
+ userDispatchQueue:userDispatchQueue
+ workerDispatchQueue:_workerDispatchQueue];
+ }
}
- return self;
}
- (FIRCollectionReference *)collectionWithPath:(NSString *)collectionPath {
if (!collectionPath) {
FSTThrowInvalidArgument(@"Collection path cannot be nil.");
}
+ [self ensureClientConfigured];
FSTResourcePath *path = [FSTResourcePath pathWithString:collectionPath];
- return
- [FIRCollectionReference referenceWithPath:path firestore:self.firestoreWithConfiguredClient];
+ return [FIRCollectionReference referenceWithPath:path firestore:self];
}
- (FIRDocumentReference *)documentWithPath:(NSString *)documentPath {
if (!documentPath) {
FSTThrowInvalidArgument(@"Document path cannot be nil.");
}
+ [self ensureClientConfigured];
FSTResourcePath *path = [FSTResourcePath pathWithString:documentPath];
- return [FIRDocumentReference referenceWithPath:path firestore:self.firestoreWithConfiguredClient];
+ return [FIRDocumentReference referenceWithPath:path firestore:self];
}
- (void)runTransactionWithBlock:(id _Nullable (^)(FIRTransaction *, NSError **))updateBlock
@@ -241,12 +255,13 @@ NSString *const FIRFirestoreErrorDomain = @"FIRFirestoreErrorDomain";
internalCompletion(result, error);
});
};
- [self firestoreWithConfiguredClient];
[self.client transactionWithRetries:5 updateBlock:wrappedUpdate completion:completion];
}
- (FIRWriteBatch *)batch {
- return [FIRWriteBatch writeBatchWithFirestore:[self firestoreWithConfiguredClient]];
+ [self ensureClientConfigured];
+
+ return [FIRWriteBatch writeBatchWithFirestore:self];
}
- (void)runTransactionWithBlock:(id _Nullable (^)(FIRTransaction *, NSError **error))updateBlock
@@ -264,11 +279,19 @@ NSString *const FIRFirestoreErrorDomain = @"FIRFirestoreErrorDomain";
}
- (void)shutdownWithCompletion:(nullable void (^)(NSError *_Nullable error))completion {
- if (!self.client) {
+ FSTFirestoreClient *client;
+ @synchronized(self) {
+ client = _client;
+ _client = nil;
+ }
+
+ if (!client) {
+ // We should be dispatching the callback on the user dispatch queue but if the client is nil
+ // here that queue was never created.
completion(nil);
- return;
+ } else {
+ [client shutdownWithCompletion:completion];
}
- return [self.client shutdownWithCompletion:completion];
}
+ (BOOL)isLoggingEnabled {
@@ -279,6 +302,16 @@ NSString *const FIRFirestoreErrorDomain = @"FIRFirestoreErrorDomain";
FIRSetLoggerLevel(logging ? FIRLoggerLevelDebug : FIRLoggerLevelNotice);
}
+- (void)enableNetworkWithCompletion:(nullable void (^)(NSError *_Nullable error))completion {
+ [self ensureClientConfigured];
+ [self.client enableNetworkWithCompletion:completion];
+}
+
+- (void)disableNetworkWithCompletion:(nullable void (^)(NSError *_Nullable))completion {
+ [self ensureClientConfigured];
+ [self.client disableNetworkWithCompletion:completion];
+}
+
@end
NS_ASSUME_NONNULL_END
diff --git a/Firestore/Source/API/FIRFirestoreSettings.m b/Firestore/Source/API/FIRFirestoreSettings.mm
index 9677ff6..9677ff6 100644
--- a/Firestore/Source/API/FIRFirestoreSettings.m
+++ b/Firestore/Source/API/FIRFirestoreSettings.mm
diff --git a/Firestore/Source/API/FIRFirestoreVersion.m b/Firestore/Source/API/FIRFirestoreVersion.mm
index 4f8bb28..b1fe480 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
index 72e9e7d..8d89633 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 @@ NS_ASSUME_NONNULL_BEGIN
}
- (NSComparisonResult)compare:(FIRGeoPoint *)other {
- NSComparisonResult result = FSTCompareDoubles(self.latitude, other.latitude);
+ NSComparisonResult result = WrapCompare<double>(self.latitude, other.latitude);
if (result != NSOrderedSame) {
return result;
} else {
- return FSTCompareDoubles(self.longitude, other.longitude);
+ return WrapCompare<double>(self.longitude, other.longitude);
}
}
@@ -67,12 +72,12 @@ NS_ASSUME_NONNULL_BEGIN
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
index 9f4ddd5..9f4ddd5 100644
--- a/Firestore/Source/API/FIRListenerRegistration.m
+++ b/Firestore/Source/API/FIRListenerRegistration.mm
diff --git a/Firestore/Source/API/FIRQuery.m b/Firestore/Source/API/FIRQuery.mm
index 12e79c5..1bbf91e 100644
--- a/Firestore/Source/API/FIRQuery.m
+++ b/Firestore/Source/API/FIRQuery.mm
@@ -107,7 +107,7 @@ NS_ASSUME_NONNULL_BEGIN
- (BOOL)isEqual:(nullable id)other {
if (other == self) return YES;
- if (!other || ![[other class] isEqual:[self class]]) return NO;
+ if (![[other class] isEqual:[self class]]) return NO;
return [self isEqualToQuery:other];
}
@@ -115,9 +115,8 @@ NS_ASSUME_NONNULL_BEGIN
- (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 {
@@ -256,6 +255,95 @@ addSnapshotListenerInternalWithOptions:(FSTListenOptions *)internalOptions
value:value];
}
+- (FIRQuery *)queryFilteredUsingComparisonPredicate:(NSPredicate *)predicate {
+ NSComparisonPredicate *comparison = (NSComparisonPredicate *)predicate;
+ if (comparison.comparisonPredicateModifier != NSDirectPredicateModifier) {
+ FSTThrowInvalidArgument(@"Invalid query. Predicate cannot have an aggregate modifier.");
+ }
+ NSString *path;
+ id value = nil;
+ if ([comparison.leftExpression expressionType] == NSKeyPathExpressionType &&
+ [comparison.rightExpression expressionType] == NSConstantValueExpressionType) {
+ path = comparison.leftExpression.keyPath;
+ value = comparison.rightExpression.constantValue;
+ switch (comparison.predicateOperatorType) {
+ case NSEqualToPredicateOperatorType:
+ return [self queryWhereField:path isEqualTo:value];
+ case NSLessThanPredicateOperatorType:
+ return [self queryWhereField:path isLessThan:value];
+ case NSLessThanOrEqualToPredicateOperatorType:
+ return [self queryWhereField:path isLessThanOrEqualTo:value];
+ case NSGreaterThanPredicateOperatorType:
+ return [self queryWhereField:path isGreaterThan:value];
+ case NSGreaterThanOrEqualToPredicateOperatorType:
+ return [self queryWhereField:path isGreaterThanOrEqualTo:value];
+ default:; // Fallback below to throw assertion.
+ }
+ } else if ([comparison.leftExpression expressionType] == NSConstantValueExpressionType &&
+ [comparison.rightExpression expressionType] == NSKeyPathExpressionType) {
+ path = comparison.rightExpression.keyPath;
+ value = comparison.leftExpression.constantValue;
+ switch (comparison.predicateOperatorType) {
+ case NSEqualToPredicateOperatorType:
+ return [self queryWhereField:path isEqualTo:value];
+ case NSLessThanPredicateOperatorType:
+ return [self queryWhereField:path isGreaterThan:value];
+ case NSLessThanOrEqualToPredicateOperatorType:
+ return [self queryWhereField:path isGreaterThanOrEqualTo:value];
+ case NSGreaterThanPredicateOperatorType:
+ return [self queryWhereField:path isLessThan:value];
+ case NSGreaterThanOrEqualToPredicateOperatorType:
+ return [self queryWhereField:path isLessThanOrEqualTo:value];
+ default:; // Fallback below to throw assertion.
+ }
+ } else {
+ FSTThrowInvalidArgument(
+ @"Invalid query. Predicate comparisons must include a key path and a constant.");
+ }
+ // Fallback cases of unsupported comparison operator.
+ switch (comparison.predicateOperatorType) {
+ case NSCustomSelectorPredicateOperatorType:
+ FSTThrowInvalidArgument(@"Invalid query. Custom predicate filters are not supported.");
+ break;
+ default:
+ FSTThrowInvalidArgument(@"Invalid query. Operator type %lu is not supported.",
+ (unsigned long)comparison.predicateOperatorType);
+ }
+}
+
+- (FIRQuery *)queryFilteredUsingCompoundPredicate:(NSPredicate *)predicate {
+ NSCompoundPredicate *compound = (NSCompoundPredicate *)predicate;
+ if (compound.compoundPredicateType != NSAndPredicateType || compound.subpredicates.count == 0) {
+ FSTThrowInvalidArgument(@"Invalid query. Only compound queries using AND are supported.");
+ }
+ FIRQuery *query = self;
+ for (NSPredicate *pred in compound.subpredicates) {
+ query = [query queryFilteredUsingPredicate:pred];
+ }
+ return query;
+}
+
+- (FIRQuery *)queryFilteredUsingPredicate:(NSPredicate *)predicate {
+ if ([predicate isKindOfClass:[NSComparisonPredicate class]]) {
+ return [self queryFilteredUsingComparisonPredicate:predicate];
+ } else if ([predicate isKindOfClass:[NSCompoundPredicate class]]) {
+ return [self queryFilteredUsingCompoundPredicate:predicate];
+ } else if ([predicate isKindOfClass:[[NSPredicate
+ predicateWithBlock:^BOOL(id obj, NSDictionary *bindings) {
+ return true;
+ }] class]]) {
+ FSTThrowInvalidArgument(
+ @"Invalid query. Block-based predicates are not "
+ "supported. Please use predicateWithFormat to "
+ "create predicates instead.");
+ } else {
+ FSTThrowInvalidArgument(
+ @"Invalid query. Expect comparison or compound of "
+ "comparison predicate. Please use "
+ "predicateWithFormat to create predicates.");
+ }
+}
+
- (FIRQuery *)queryOrderedByField:(NSString *)field {
return
[self queryOrderedByFieldPath:[FIRFieldPath pathWithDotSeparatedString:field] descending:NO];
diff --git a/Firestore/Source/API/FIRQuerySnapshot.m b/Firestore/Source/API/FIRQuerySnapshot.mm
index 6bc6761..abee84c 100644
--- a/Firestore/Source/API/FIRQuerySnapshot.m
+++ b/Firestore/Source/API/FIRQuerySnapshot.mm
@@ -16,6 +16,7 @@
#import "Firestore/Source/API/FIRQuerySnapshot+Internal.h"
+#import "FIRFirestore.h"
#import "FIRSnapshotMetadata.h"
#import "Firestore/Source/API/FIRDocumentChange+Internal.h"
#import "Firestore/Source/API/FIRDocumentSnapshot+Internal.h"
@@ -57,7 +58,7 @@ NS_ASSUME_NONNULL_BEGIN
@implementation FIRQuerySnapshot {
// Cached value of the documents property.
- NSArray<FIRDocumentSnapshot *> *_documents;
+ NSArray<FIRQueryDocumentSnapshot *> *_documents;
// Cached value of the documentChanges property.
NSArray<FIRDocumentChange *> *_documentChanges;
@@ -76,6 +77,31 @@ NS_ASSUME_NONNULL_BEGIN
return self;
}
+// NSObject Methods
+- (BOOL)isEqual:(nullable id)other {
+ if (other == self) return YES;
+ if (![[other class] isEqual:[self class]]) return NO;
+
+ return [self isEqualToSnapshot:other];
+}
+
+- (BOOL)isEqualToSnapshot:(nullable FIRQuerySnapshot *)snapshot {
+ if (self == snapshot) return YES;
+ if (snapshot == nil) return NO;
+
+ return [self.firestore isEqual:snapshot.firestore] &&
+ [self.originalQuery isEqual:snapshot.originalQuery] &&
+ [self.snapshot isEqual:snapshot.snapshot] && [self.metadata isEqual:snapshot.metadata];
+}
+
+- (NSUInteger)hash {
+ NSUInteger hash = [self.firestore hash];
+ hash = hash * 31u + [self.originalQuery hash];
+ hash = hash * 31u + [self.snapshot hash];
+ hash = hash * 31u + [self.metadata hash];
+ return hash;
+}
+
@dynamic empty;
- (FIRQuery *)query {
@@ -93,18 +119,18 @@ NS_ASSUME_NONNULL_BEGIN
return self.snapshot.documents.count;
}
-- (NSArray<FIRDocumentSnapshot *> *)documents {
+- (NSArray<FIRQueryDocumentSnapshot *> *)documents {
if (!_documents) {
FSTDocumentSet *documentSet = self.snapshot.documents;
FIRFirestore *firestore = self.firestore;
BOOL fromCache = self.metadata.fromCache;
- NSMutableArray<FIRDocumentSnapshot *> *result = [NSMutableArray array];
+ NSMutableArray<FIRQueryDocumentSnapshot *> *result = [NSMutableArray array];
for (FSTDocument *document in documentSet.documentEnumerator) {
- [result addObject:[FIRDocumentSnapshot snapshotWithFirestore:firestore
- documentKey:document.key
- document:document
- fromCache:fromCache]];
+ [result addObject:[FIRQueryDocumentSnapshot snapshotWithFirestore:firestore
+ documentKey:document.key
+ document:document
+ fromCache:fromCache]];
}
_documents = result;
diff --git a/Firestore/Source/API/FIRSetOptions.m b/Firestore/Source/API/FIRSetOptions.mm
index 623deaa..b41086c 100644
--- a/Firestore/Source/API/FIRSetOptions.m
+++ b/Firestore/Source/API/FIRSetOptions.mm
@@ -15,7 +15,6 @@
*/
#import "Firestore/Source/API/FIRSetOptions+Internal.h"
-#import "Firestore/Source/Model/FSTMutation.h"
NS_ASSUME_NONNULL_BEGIN
@@ -40,8 +39,7 @@ NS_ASSUME_NONNULL_BEGIN
}
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
index 224015f..27747ce 100644
--- a/Firestore/Source/API/FIRSnapshotMetadata.m
+++ b/Firestore/Source/API/FIRSnapshotMetadata.mm
@@ -44,6 +44,27 @@ NS_ASSUME_NONNULL_BEGIN
return self;
}
+// NSObject Methods
+- (BOOL)isEqual:(nullable id)other {
+ if (other == self) return YES;
+ if (![[other class] isEqual:[self class]]) return NO;
+
+ return [self isEqualToMetadata:other];
+}
+
+- (BOOL)isEqualToMetadata:(nullable FIRSnapshotMetadata *)metadata {
+ if (self == metadata) return YES;
+ if (metadata == nil) return NO;
+
+ return self.pendingWrites == metadata.pendingWrites && self.fromCache == metadata.fromCache;
+}
+
+- (NSUInteger)hash {
+ NSUInteger hash = self.pendingWrites ? 1 : 0;
+ hash = hash * 31u + (self.fromCache ? 1 : 0);
+ return hash;
+}
+
@end
NS_ASSUME_NONNULL_END
diff --git a/Firestore/Source/API/FIRSnapshotOptions+Internal.h b/Firestore/Source/API/FIRSnapshotOptions+Internal.h
new file mode 100644
index 0000000..64e7dbc
--- /dev/null
+++ b/Firestore/Source/API/FIRSnapshotOptions+Internal.h
@@ -0,0 +1,38 @@
+/*
+ * 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 "FIRDocumentSnapshot.h"
+
+#import <Foundation/Foundation.h>
+
+#import "Firestore/Source/Model/FSTFieldValue.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface FIRSnapshotOptions (Internal)
+
+/** Returns a default instance of FIRSnapshotOptions that specifies no options. */
++ (instancetype)defaultOptions;
+
+/* Initializes a new instance with the specified server timestamp behavior. */
+- (instancetype)initWithServerTimestampBehavior:(FSTServerTimestampBehavior)serverTimestampBehavior;
+
+/* Returns the server timestamp behavior. Returns -1 if no behavior is specified. */
+- (FSTServerTimestampBehavior)serverTimestampBehavior;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Firestore/Source/API/FIRSnapshotOptions.mm b/Firestore/Source/API/FIRSnapshotOptions.mm
new file mode 100644
index 0000000..72ea3cc
--- /dev/null
+++ b/Firestore/Source/API/FIRSnapshotOptions.mm
@@ -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.
+ */
+
+#import "FIRDocumentSnapshot.h"
+
+#import "Firestore/Source/API/FIRSnapshotOptions+Internal.h"
+#import "Firestore/Source/Util/FSTAssert.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface FIRSnapshotOptions ()
+
+@property(nonatomic) FSTServerTimestampBehavior serverTimestampBehavior;
+
+@end
+
+@implementation FIRSnapshotOptions
+
+- (instancetype)initWithServerTimestampBehavior:
+ (FSTServerTimestampBehavior)serverTimestampBehavior {
+ self = [super init];
+
+ if (self) {
+ _serverTimestampBehavior = serverTimestampBehavior;
+ }
+
+ return self;
+}
+
++ (instancetype)defaultOptions {
+ static FIRSnapshotOptions *sharedInstance = nil;
+ static dispatch_once_t onceToken;
+
+ dispatch_once(&onceToken, ^{
+ sharedInstance =
+ [[FIRSnapshotOptions alloc] initWithServerTimestampBehavior:FSTServerTimestampBehaviorNone];
+ });
+
+ return sharedInstance;
+}
+
++ (instancetype)serverTimestampBehavior:(FIRServerTimestampBehavior)serverTimestampBehavior {
+ switch (serverTimestampBehavior) {
+ case FIRServerTimestampBehaviorEstimate:
+ return [[FIRSnapshotOptions alloc]
+ initWithServerTimestampBehavior:FSTServerTimestampBehaviorEstimate];
+ case FIRServerTimestampBehaviorPrevious:
+ return [[FIRSnapshotOptions alloc]
+ initWithServerTimestampBehavior:FSTServerTimestampBehaviorPrevious];
+ case FIRServerTimestampBehaviorNone:
+ return [FIRSnapshotOptions defaultOptions];
+ default:
+ FSTFail(@"Encountered unknown server timestamp behavior: %d", (int)serverTimestampBehavior);
+ }
+}
+
+@end
+
+NS_ASSUME_NONNULL_END \ No newline at end of file
diff --git a/Firestore/Source/API/FIRTransaction.m b/Firestore/Source/API/FIRTransaction.mm
index 5edff19..5edff19 100644
--- a/Firestore/Source/API/FIRTransaction.m
+++ b/Firestore/Source/API/FIRTransaction.mm
diff --git a/Firestore/Source/API/FIRWriteBatch.m b/Firestore/Source/API/FIRWriteBatch.mm
index b918a9a..b1cfa09 100644
--- a/Firestore/Source/API/FIRWriteBatch.m
+++ b/Firestore/Source/API/FIRWriteBatch.mm
@@ -93,7 +93,11 @@ NS_ASSUME_NONNULL_BEGIN
return self;
}
-- (void)commitWithCompletion:(void (^)(NSError *_Nullable error))completion {
+- (void)commit {
+ [self commitWithCompletion:nil];
+}
+
+- (void)commitWithCompletion:(nullable void (^)(NSError *_Nullable error))completion {
[self verifyNotCommitted];
self.committed = TRUE;
[self.firestore.client writeMutations:self.mutations completion:completion];
diff --git a/Firestore/Source/API/FSTUserDataConverter.m b/Firestore/Source/API/FSTUserDataConverter.mm
index 414aadb..414aadb 100644
--- a/Firestore/Source/API/FSTUserDataConverter.m
+++ b/Firestore/Source/API/FSTUserDataConverter.mm
diff --git a/Firestore/Source/Auth/FSTCredentialsProvider.m b/Firestore/Source/Auth/FSTCredentialsProvider.mm
index 653d7ff..653d7ff 100644
--- a/Firestore/Source/Auth/FSTCredentialsProvider.m
+++ b/Firestore/Source/Auth/FSTCredentialsProvider.mm
diff --git a/Firestore/Source/Auth/FSTEmptyCredentialsProvider.m b/Firestore/Source/Auth/FSTEmptyCredentialsProvider.mm
index e78452a..e78452a 100644
--- a/Firestore/Source/Auth/FSTEmptyCredentialsProvider.m
+++ b/Firestore/Source/Auth/FSTEmptyCredentialsProvider.mm
diff --git a/Firestore/Source/Auth/FSTUser.m b/Firestore/Source/Auth/FSTUser.mm
index 605b4e6..605b4e6 100644
--- a/Firestore/Source/Auth/FSTUser.m
+++ b/Firestore/Source/Auth/FSTUser.mm
diff --git a/Firestore/Source/Core/FSTDatabaseInfo.m b/Firestore/Source/Core/FSTDatabaseInfo.mm
index 2dbe61a..2dbe61a 100644
--- a/Firestore/Source/Core/FSTDatabaseInfo.m
+++ b/Firestore/Source/Core/FSTDatabaseInfo.mm
diff --git a/Firestore/Source/Core/FSTEventManager.h b/Firestore/Source/Core/FSTEventManager.h
index edd2a96..8eafd4b 100644
--- a/Firestore/Source/Core/FSTEventManager.h
+++ b/Firestore/Source/Core/FSTEventManager.h
@@ -62,7 +62,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)queryDidChangeViewSnapshot:(FSTViewSnapshot *)snapshot;
- (void)queryDidError:(NSError *)error;
-- (void)clientDidChangeOnlineState:(FSTOnlineState)onlineState;
+- (void)applyChangedOnlineState:(FSTOnlineState)onlineState;
@property(nonatomic, strong, readonly) FSTQuery *query;
diff --git a/Firestore/Source/Core/FSTEventManager.m b/Firestore/Source/Core/FSTEventManager.mm
index 3e1b99b..bc204a0 100644
--- a/Firestore/Source/Core/FSTEventManager.m
+++ b/Firestore/Source/Core/FSTEventManager.mm
@@ -151,7 +151,7 @@ NS_ASSUME_NONNULL_BEGIN
self.viewSnapshotHandler(nil, error);
}
-- (void)clientDidChangeOnlineState:(FSTOnlineState)onlineState {
+- (void)applyChangedOnlineState:(FSTOnlineState)onlineState {
self.onlineState = onlineState;
if (self.snapshot && !self.raisedInitialEvent &&
[self shouldRaiseInitialEventForSnapshot:self.snapshot onlineState:onlineState]) {
@@ -268,7 +268,7 @@ NS_ASSUME_NONNULL_BEGIN
}
[queryInfo.listeners addObject:listener];
- [listener clientDidChangeOnlineState:self.onlineState];
+ [listener applyChangedOnlineState:self.onlineState];
if (queryInfo.viewSnapshot) {
[listener queryDidChangeViewSnapshot:queryInfo.viewSnapshot];
@@ -321,11 +321,11 @@ NS_ASSUME_NONNULL_BEGIN
[self.queries removeObjectForKey:query];
}
-- (void)watchStreamDidChangeOnlineState:(FSTOnlineState)onlineState {
+- (void)applyChangedOnlineState:(FSTOnlineState)onlineState {
self.onlineState = onlineState;
for (FSTQueryListenersInfo *info in self.queries.objectEnumerator) {
for (FSTQueryListener *listener in info.listeners) {
- [listener clientDidChangeOnlineState:onlineState];
+ [listener applyChangedOnlineState:onlineState];
}
}
}
diff --git a/Firestore/Source/Core/FSTFirestoreClient.h b/Firestore/Source/Core/FSTFirestoreClient.h
index 6a1e11b..0ecf2f6 100644
--- a/Firestore/Source/Core/FSTFirestoreClient.h
+++ b/Firestore/Source/Core/FSTFirestoreClient.h
@@ -38,7 +38,7 @@ NS_ASSUME_NONNULL_BEGIN
* SDK architecture. It is responsible for creating the worker queue that is shared by all of the
* other components in the system.
*/
-@interface FSTFirestoreClient : NSObject
+@interface FSTFirestoreClient : NSObject <FSTOnlineStateDelegate>
/**
* Creates and returns a FSTFirestoreClient with the given parameters.
diff --git a/Firestore/Source/Core/FSTFirestoreClient.m b/Firestore/Source/Core/FSTFirestoreClient.mm
index 2e0e407..5986b5b 100644
--- a/Firestore/Source/Core/FSTFirestoreClient.m
+++ b/Firestore/Source/Core/FSTFirestoreClient.mm
@@ -172,7 +172,7 @@ NS_ASSUME_NONNULL_BEGIN
// Setup wiring for remote store.
_remoteStore.syncEngine = _syncEngine;
- _remoteStore.onlineStateDelegate = _eventManager;
+ _remoteStore.onlineStateDelegate = self;
// NOTE: RemoteStore depends on LocalStore (for persisting stream tokens, refilling mutation
// queue, etc.) so must be started after LocalStore.
@@ -187,6 +187,11 @@ NS_ASSUME_NONNULL_BEGIN
[self.syncEngine userDidChange:user];
}
+- (void)applyChangedOnlineState:(FSTOnlineState)onlineState {
+ [self.syncEngine applyChangedOnlineState:onlineState];
+ [self.eventManager applyChangedOnlineState:onlineState];
+}
+
- (void)disableNetworkWithCompletion:(nullable FSTVoidErrorBlock)completion {
[self.workerDispatchQueue dispatchAsync:^{
[self.remoteStore disableNetwork];
@@ -248,9 +253,11 @@ NS_ASSUME_NONNULL_BEGIN
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) {
diff --git a/Firestore/Source/Core/FSTListenSequence.h b/Firestore/Source/Core/FSTListenSequence.h
new file mode 100644
index 0000000..56d0e78
--- /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 <Foundation/Foundation.h>
+
+#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 0000000..6f50d35
--- /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
index 0bfd917..8c98687 100644
--- a/Firestore/Source/Core/FSTQuery.m
+++ b/Firestore/Source/Core/FSTQuery.mm
@@ -27,6 +27,13 @@ NS_ASSUME_NONNULL_BEGIN
#pragma mark - FSTRelationFilterOperator functions
+/**
+ * Returns the reverse order (i.e. Ascending => Descending) etc.
+ */
+static constexpr NSComparisonResult ReverseOrder(NSComparisonResult result) {
+ return static_cast<NSComparisonResult>(-static_cast<NSInteger>(result));
+}
+
NSString *FSTStringFromQueryRelationOperator(FSTRelationFilterOperator filterOperator) {
switch (filterOperator) {
case FSTRelationFilterOperatorLessThan:
@@ -205,7 +212,7 @@ NSString *FSTStringFromQueryRelationOperator(FSTRelationFilterOperator filterOpe
- (BOOL)isEqual:(id)other {
if (other == self) return YES;
- if (!other || ![[other class] isEqual:[self class]]) return NO;
+ if (![[other class] isEqual:[self class]]) return NO;
return [self.field isEqual:((FSTNullFilter *)other).field];
}
@@ -246,7 +253,7 @@ NSString *FSTStringFromQueryRelationOperator(FSTRelationFilterOperator filterOpe
- (BOOL)isEqual:(id)other {
if (other == self) return YES;
- if (!other || ![[other class] isEqual:[self class]]) return NO;
+ if (![[other class] isEqual:[self class]]) return NO;
return [self.field isEqual:((FSTNanFilter *)other).field];
}
@@ -287,16 +294,20 @@ NSString *FSTStringFromQueryRelationOperator(FSTRelationFilterOperator filterOpe
#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 @@ NSString *FSTStringFromQueryRelationOperator(FSTRelationFilterOperator filterOpe
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 @@ NSString *FSTStringFromQueryRelationOperator(FSTRelationFilterOperator filterOpe
}
if (!sortOrderComponent.isAscending) {
- comparison = comparison * -1;
+ comparison = ReverseOrder(comparison);
}
if (comparison != 0) {
diff --git a/Firestore/Source/Core/FSTSnapshotVersion.m b/Firestore/Source/Core/FSTSnapshotVersion.mm
index 980ae52..980ae52 100644
--- a/Firestore/Source/Core/FSTSnapshotVersion.m
+++ b/Firestore/Source/Core/FSTSnapshotVersion.mm
diff --git a/Firestore/Source/Core/FSTSyncEngine.h b/Firestore/Source/Core/FSTSyncEngine.h
index bb45196..7060155 100644
--- a/Firestore/Source/Core/FSTSyncEngine.h
+++ b/Firestore/Source/Core/FSTSyncEngine.h
@@ -100,6 +100,9 @@ NS_ASSUME_NONNULL_BEGIN
- (void)userDidChange:(FSTUser *)user;
+/** Applies an FSTOnlineState change to the sync engine and notifies any views of the change. */
+- (void)applyChangedOnlineState:(FSTOnlineState)onlineState;
+
@end
NS_ASSUME_NONNULL_END
diff --git a/Firestore/Source/Core/FSTSyncEngine.m b/Firestore/Source/Core/FSTSyncEngine.mm
index 98658e4..d82cc99 100644
--- a/Firestore/Source/Core/FSTSyncEngine.m
+++ b/Firestore/Source/Core/FSTSyncEngine.mm
@@ -22,7 +22,6 @@
#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 +40,14 @@
#import "Firestore/Source/Util/FSTDispatchQueue.h"
#import "Firestore/Source/Util/FSTLogger.h"
+#include "Firestore/core/src/firebase/firestore/core/target_id_generator.h"
+
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
/**
@@ -137,14 +142,14 @@ NS_ASSUME_NONNULL_BEGIN
NSMutableDictionary<FSTUser *, NSMutableDictionary<NSNumber *, FSTVoidErrorBlock> *>
*mutationCompletionBlocks;
-/** Used for creating the FSTTargetIDs for the listens used to resolve limbo documents. */
-@property(nonatomic, strong, readonly) FSTTargetIDGenerator *targetIdGenerator;
-
@property(nonatomic, strong) FSTUser *currentUser;
@end
-@implementation FSTSyncEngine
+@implementation FSTSyncEngine {
+ /** Used for creating the FSTTargetIDs for the listens used to resolve limbo documents. */
+ firebase::firestore::core::TargetIdGenerator _targetIdGenerator;
+}
- (instancetype)initWithLocalStore:(FSTLocalStore *)localStore
remoteStore:(FSTRemoteStore *)remoteStore
@@ -163,7 +168,8 @@ NS_ASSUME_NONNULL_BEGIN
[_limboCollector addGarbageSource:_limboDocumentRefs];
_mutationCompletionBlocks = [NSMutableDictionary dictionary];
- _targetIdGenerator = [FSTTargetIDGenerator generatorForSyncEngineStartingAfterID:0];
+ _targetIdGenerator =
+ firebase::firestore::core::TargetIdGenerator::SyncEngineTargetIdGenerator(0);
_currentUser = initialUser;
}
return self;
@@ -318,6 +324,21 @@ NS_ASSUME_NONNULL_BEGIN
[self emitNewSnapshotsWithChanges:changes remoteEvent:remoteEvent];
}
+- (void)applyChangedOnlineState:(FSTOnlineState)onlineState {
+ NSMutableArray<FSTViewSnapshot *> *newViewSnapshots = [NSMutableArray array];
+ [self.queryViewsByQuery
+ enumerateKeysAndObjectsUsingBlock:^(FSTQuery *query, FSTQueryView *queryView, BOOL *stop) {
+ FSTViewChange *viewChange = [queryView.view applyChangedOnlineState:onlineState];
+ FSTAssert(viewChange.limboChanges.count == 0,
+ @"OnlineState should not affect limbo documents.");
+ if (viewChange.snapshot) {
+ [newViewSnapshots addObject:viewChange.snapshot];
+ }
+ }];
+
+ [self.delegate handleViewSnapshots:newViewSnapshots];
+}
+
- (void)rejectListenWithTargetID:(FSTBoxedTargetID *)targetID error:(NSError *)error {
[self assertDelegateExistsForSelector:_cmd];
@@ -471,10 +492,11 @@ NS_ASSUME_NONNULL_BEGIN
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];
diff --git a/Firestore/Source/Core/FSTTargetIDGenerator.h b/Firestore/Source/Core/FSTTargetIDGenerator.h
deleted file mode 100644
index 0b230ae..0000000
--- 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 <Foundation/Foundation.h>
-
-#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 58092ec..0000000
--- 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 <libkern/OSAtomic.h>
-
-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/FSTTimestamp.m b/Firestore/Source/Core/FSTTimestamp.mm
index 6d9e314..d2b492a 100644
--- a/Firestore/Source/Core/FSTTimestamp.m
+++ b/Firestore/Source/Core/FSTTimestamp.mm
@@ -16,8 +16,11 @@
#import "Firestore/Source/Core/FSTTimestamp.h"
+#include "Firestore/core/src/firebase/firestore/util/comparison.h"
+
#import "Firestore/Source/Util/FSTAssert.h"
-#import "Firestore/Source/Util/FSTComparison.h"
+
+using firebase::firestore::util::WrapCompare;
NS_ASSUME_NONNULL_BEGIN
@@ -110,11 +113,11 @@ static const int kNanosPerSecond = 1000000000;
}
- (NSComparisonResult)compare:(FSTTimestamp *)other {
- NSComparisonResult result = FSTCompareInt64s(self.seconds, other.seconds);
+ NSComparisonResult result = WrapCompare<int64_t>(self.seconds, other.seconds);
if (result != NSOrderedSame) {
return result;
}
- return FSTCompareInt32s(self.nanos, other.nanos);
+ return WrapCompare<int32_t>(self.nanos, other.nanos);
}
@end
diff --git a/Firestore/Source/Core/FSTTransaction.m b/Firestore/Source/Core/FSTTransaction.mm
index c4c5f27..f97888a 100644
--- a/Firestore/Source/Core/FSTTransaction.m
+++ b/Firestore/Source/Core/FSTTransaction.mm
@@ -104,22 +104,22 @@ NS_ASSUME_NONNULL_BEGIN
FSTThrowInvalidUsage(@"FIRIllegalStateException",
@"All reads in a transaction must be done before any writes.");
}
- [self.datastore
- lookupDocuments:keys
- completion:^(NSArray<FSTDocument *> *_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<FSTMaybeDocument *> *_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 c10f1bf..877ec94 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;
/**
@@ -67,8 +69,8 @@ typedef void (^FSTTransactionBlock)(FSTTransaction *transaction,
typedef NS_ENUM(NSUInteger, FSTOnlineState) {
/**
* The Firestore client is in an unknown online state. This means the client is either not
- * actively trying to establish a connection or it was previously in an unknown state and is
- * trying to establish a connection.
+ * actively trying to establish a connection or it is currently trying to establish a connection,
+ * but it has not succeeded or failed yet.
*/
FSTOnlineStateUnknown,
@@ -80,9 +82,8 @@ typedef NS_ENUM(NSUInteger, FSTOnlineState) {
FSTOnlineStateHealthy,
/**
- * The client has tried to establish a connection but has failed.
- * This state is reached after either a connection attempt failed or a healthy stream was closed
- * for unexpected reasons.
+ * The client considers itself offline. It is either trying to establish a connection but
+ * failing, or it has been explicitly marked offline via a call to `disableNetwork`.
*/
FSTOnlineStateFailed
};
diff --git a/Firestore/Source/Core/FSTView.h b/Firestore/Source/Core/FSTView.h
index ed230a3..6ff77cd 100644
--- a/Firestore/Source/Core/FSTView.h
+++ b/Firestore/Source/Core/FSTView.h
@@ -16,6 +16,7 @@
#import <Foundation/Foundation.h>
+#import "Firestore/Source/Core/FSTTypes.h"
#import "Firestore/Source/Model/FSTDocumentDictionary.h"
#import "Firestore/Source/Model/FSTDocumentKeySet.h"
@@ -138,6 +139,12 @@ typedef NS_ENUM(NSInteger, FSTLimboDocumentChangeType) {
- (FSTViewChange *)applyChangesToDocuments:(FSTViewDocumentChanges *)docChanges
targetChange:(nullable FSTTargetChange *)targetChange;
+/**
+ * Applies an FSTOnlineState change to the view, potentially generating an FSTViewChange if the
+ * view's syncState changes as a result.
+ */
+- (FSTViewChange *)applyChangedOnlineState:(FSTOnlineState)onlineState;
+
@end
NS_ASSUME_NONNULL_END
diff --git a/Firestore/Source/Core/FSTView.m b/Firestore/Source/Core/FSTView.mm
index 9b44bf4..d6b4558 100644
--- a/Firestore/Source/Core/FSTView.m
+++ b/Firestore/Source/Core/FSTView.mm
@@ -94,6 +94,12 @@ NS_ASSUME_NONNULL_BEGIN
return self.type == otherChange.type && [self.key isEqual:otherChange.key];
}
+- (NSUInteger)hash {
+ NSUInteger hash = self.type;
+ hash = hash * 31u + [self.key hash];
+ return hash;
+}
+
@end
#pragma mark - FSTViewChange
@@ -306,8 +312,8 @@ static NSComparisonResult FSTCompareDocumentViewChangeTypes(FSTDocumentViewChang
}
return self.query.comparator(c1.document, c2.document);
}];
-
- NSArray<FSTLimboDocumentChange *> *limboChanges = [self applyTargetChange:targetChange];
+ [self applyTargetChange:targetChange];
+ NSArray<FSTLimboDocumentChange *> *limboChanges = [self updateLimboDocuments];
BOOL synced = self.limboDocuments.count == 0 && self.isCurrent;
FSTSyncState newSyncState = synced ? FSTSyncStateSynced : FSTSyncStateLocal;
BOOL syncStateChanged = newSyncState != self.syncState;
@@ -330,6 +336,24 @@ static NSComparisonResult FSTCompareDocumentViewChangeTypes(FSTDocumentViewChang
}
}
+- (FSTViewChange *)applyChangedOnlineState:(FSTOnlineState)onlineState {
+ if (self.isCurrent && onlineState == FSTOnlineStateFailed) {
+ // If we're offline, set `current` to NO and then call applyChanges to refresh our syncState
+ // and generate an FSTViewChange as appropriate. We are guaranteed to get a new FSTTargetChange
+ // that sets `current` back to YES once the client is back online.
+ self.current = NO;
+ return
+ [self applyChangesToDocuments:[[FSTViewDocumentChanges alloc]
+ initWithDocumentSet:self.documentSet
+ changeSet:[FSTDocumentViewChangeSet changeSet]
+ needsRefill:NO
+ mutatedKeys:self.mutatedKeys]];
+ } else {
+ // No effect, just return a no-op FSTViewChange.
+ return [[FSTViewChange alloc] initWithSnapshot:nil limboChanges:@[]];
+ }
+}
+
#pragma mark - Private methods
/** Returns whether the doc for the given key should be in limbo. */
@@ -354,10 +378,9 @@ static NSComparisonResult FSTCompareDocumentViewChangeTypes(FSTDocumentViewChang
}
/**
- * 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<FSTLimboDocumentChange *> *)applyTargetChange:(nullable FSTTargetChange *)targetChange {
+- (void)applyTargetChange:(nullable FSTTargetChange *)targetChange {
if (targetChange) {
FSTTargetMapping *targetMapping = targetChange.mapping;
if ([targetMapping isKindOfClass:[FSTResetMapping class]]) {
@@ -384,16 +407,21 @@ static NSComparisonResult FSTCompareDocumentViewChangeTypes(FSTDocumentViewChang
break;
}
}
+}
+
+/** Updates limboDocuments and returns any changes as FSTLimboDocumentChanges. */
+- (NSArray<FSTLimboDocumentChange *> *)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
index e60b785..e60b785 100644
--- a/Firestore/Source/Core/FSTViewSnapshot.m
+++ b/Firestore/Source/Core/FSTViewSnapshot.mm
diff --git a/Firestore/Source/Local/FSTDocumentReference.h b/Firestore/Source/Local/FSTDocumentReference.h
index eff60e4..04b8416 100644
--- a/Firestore/Source/Local/FSTDocumentReference.h
+++ b/Firestore/Source/Local/FSTDocumentReference.h
@@ -32,7 +32,7 @@ NS_ASSUME_NONNULL_BEGIN
@interface FSTDocumentReference : NSObject <NSCopying>
/** 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
index 1631789..4310baa 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;
@@ -34,7 +37,7 @@ NS_ASSUME_NONNULL_BEGIN
- (BOOL)isEqual:(id)other {
if (other == self) return YES;
- if (!other || ![[other class] isEqual:[self class]]) return NO;
+ if (![[other class] isEqual:[self class]]) return NO;
FSTDocumentReference *reference = (FSTDocumentReference *)other;
@@ -67,13 +70,13 @@ const NSComparator FSTDocumentReferenceComparatorByKey =
if (result != NSOrderedSame) {
return result;
}
- return FSTCompareInts(left.ID, right.ID);
+ return WrapCompare<int32_t>(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<int32_t>(left.ID, right.ID);
if (result != NSOrderedSame) {
return result;
}
diff --git a/Firestore/Source/Local/FSTEagerGarbageCollector.m b/Firestore/Source/Local/FSTEagerGarbageCollector.mm
index 77a577e..77a577e 100644
--- a/Firestore/Source/Local/FSTEagerGarbageCollector.m
+++ b/Firestore/Source/Local/FSTEagerGarbageCollector.mm
diff --git a/Firestore/Source/Local/FSTLevelDB.h b/Firestore/Source/Local/FSTLevelDB.h
index 762054b..520557a 100644
--- a/Firestore/Source/Local/FSTLevelDB.h
+++ b/Firestore/Source/Local/FSTLevelDB.h
@@ -16,16 +16,10 @@
#import <Foundation/Foundation.h>
-#import "Firestore/Source/Local/FSTPersistence.h"
-
-#ifdef __cplusplus
#include <memory>
-namespace leveldb {
-class DB;
-class Status;
-}
-#endif
+#import "Firestore/Source/Local/FSTPersistence.h"
+#include "leveldb/db.h"
@class FSTDatabaseInfo;
@class FSTLocalSerializer;
@@ -68,8 +62,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 +95,6 @@ NS_ASSUME_NONNULL_BEGIN
/** The native db pointer, allocated during start. */
@property(nonatomic, assign, readonly) std::shared_ptr<leveldb::DB> ptr;
-#endif
-
@end
NS_ASSUME_NONNULL_END
diff --git a/Firestore/Source/Local/FSTLevelDB.mm b/Firestore/Source/Local/FSTLevelDB.mm
index fb1c81a..d163ed5 100644
--- a/Firestore/Source/Local/FSTLevelDB.mm
+++ b/Firestore/Source/Local/FSTLevelDB.mm
@@ -20,6 +20,7 @@
#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"
@@ -36,6 +37,7 @@ static NSString *const kReservedPathComponent = @"firestore";
using leveldb::DB;
using leveldb::Options;
+using leveldb::ReadOptions;
using leveldb::Status;
using leveldb::WriteOptions;
@@ -50,6 +52,15 @@ using leveldb::WriteOptions;
@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]) {
@@ -72,8 +83,8 @@ using leveldb::WriteOptions;
#else
#error "local storage on tvOS"
-// TODO(mcg): Writing to NSDocumentsDirectory on tvOS will fail; we need to write to Caches
-// https://developer.apple.com/library/content/documentation/General/Conceptual/AppleTV_PG/
+ // TODO(mcg): Writing to NSDocumentsDirectory on tvOS will fail; we need to write to Caches
+ // https://developer.apple.com/library/content/documentation/General/Conceptual/AppleTV_PG/
#endif
}
@@ -115,8 +126,8 @@ using leveldb::WriteOptions;
if (!database) {
return NO;
}
-
_ptr.reset(database);
+ [FSTLevelDBMigrations runMigrationsOnDB:_ptr];
return YES;
}
diff --git a/Firestore/Source/Local/FSTLevelDBKey.h b/Firestore/Source/Local/FSTLevelDBKey.h
index 2e9b9b2..f3f4bcf 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 <Foundation/Foundation.h>
#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 c6f51b9..41aea39 100644
--- a/Firestore/Source/Local/FSTLevelDBKey.mm
+++ b/Firestore/Source/Local/FSTLevelDBKey.mm
@@ -18,18 +18,18 @@
#include <string>
-#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<FSTComponentLabel>(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<int32_t>(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 *InvalidKey(const Slice &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/Source/Local/FSTLevelDBMigrations.h b/Firestore/Source/Local/FSTLevelDBMigrations.h
new file mode 100644
index 0000000..24fb5c8
--- /dev/null
+++ b/Firestore/Source/Local/FSTLevelDBMigrations.h
@@ -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.
+ */
+
+#import <Foundation/Foundation.h>
+
+#include <memory>
+
+#include "leveldb/db.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+typedef int32_t FSTLevelDBSchemaVersion;
+
+@interface FSTLevelDBMigrations : NSObject
+
+/**
+ * Returns the current version of the schema for the given database
+ */
++ (FSTLevelDBSchemaVersion)schemaVersionForDB:(std::shared_ptr<leveldb::DB>)db;
+
+/**
+ * Runs any migrations needed to bring the given database up to the current schema version
+ */
++ (void)runMigrationsOnDB:(std::shared_ptr<leveldb::DB>)db;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Firestore/Source/Local/FSTLevelDBMigrations.mm b/Firestore/Source/Local/FSTLevelDBMigrations.mm
new file mode 100644
index 0000000..49af893
--- /dev/null
+++ b/Firestore/Source/Local/FSTLevelDBMigrations.mm
@@ -0,0 +1,95 @@
+/*
+ * 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/db.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"
+
+NS_ASSUME_NONNULL_BEGIN
+
+// Current version of the schema defined in this file.
+static FSTLevelDBSchemaVersion kSchemaVersion = 1;
+
+using leveldb::DB;
+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> 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];
+}
+
+@implementation FSTLevelDBMigrations
+
++ (FSTLevelDBSchemaVersion)schemaVersionForDB:(std::shared_ptr<DB>)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>)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
+ 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 dd2ed4f..cc05db7 100644
--- a/Firestore/Source/Local/FSTLevelDBMutationQueue.h
+++ b/Firestore/Source/Local/FSTLevelDBMutationQueue.h
@@ -16,15 +16,10 @@
#import <Foundation/Foundation.h>
-#import "Firestore/Source/Local/FSTMutationQueue.h"
-
-#ifdef __cplusplus
#include <memory>
-namespace leveldb {
-class DB;
-}
-#endif
+#import "Firestore/Source/Local/FSTMutationQueue.h"
+#include "leveldb/db.h"
@class FSTLevelDB;
@class FSTLocalSerializer;
@@ -41,7 +36,6 @@ NS_ASSUME_NONNULL_BEGIN
/** The garbage collector to notify about potential garbage keys. */
@property(nonatomic, weak, readwrite, nullable) id<FSTGarbageCollector> garbageCollector;
-#ifdef __cplusplus
/**
* Creates a new mutation queue for the given user, in the given LevelDB.
*
@@ -57,7 +51,6 @@ NS_ASSUME_NONNULL_BEGIN
* returns 0. Note that batch IDs are global.
*/
+ (FSTBatchID)loadNextBatchIDFromDB:(std::shared_ptr<leveldb::DB>)db;
-#endif
@end
diff --git a/Firestore/Source/Local/FSTLevelDBMutationQueue.mm b/Firestore/Source/Local/FSTLevelDBMutationQueue.mm
index 56a22a1..dbe58e8 100644
--- a/Firestore/Source/Local/FSTLevelDBMutationQueue.mm
+++ b/Firestore/Source/Local/FSTLevelDBMutationQueue.mm
@@ -34,12 +34,10 @@
#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/util/string_util.h"
NS_ASSUME_NONNULL_BEGIN
-using Firestore::OrderedCode;
using Firestore::StringView;
using leveldb::DB;
using leveldb::Iterator;
@@ -164,7 +162,7 @@ static ReadOptions StandardReadOptions() {
while (moreUserIDs) {
// Compute the first key after the last mutation for nextUserID.
auto userEnd = [FSTLevelDBMutationKey keyPrefixWithUserID:nextUserID];
- userEnd = Firestore::PrefixSuccessor(userEnd);
+ userEnd = firebase::firestore::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).
diff --git a/Firestore/Source/Local/FSTLevelDBQueryCache.h b/Firestore/Source/Local/FSTLevelDBQueryCache.h
index 6d5cd60..1f6fbd4 100644
--- a/Firestore/Source/Local/FSTLevelDBQueryCache.h
+++ b/Firestore/Source/Local/FSTLevelDBQueryCache.h
@@ -16,17 +16,13 @@
#import <Foundation/Foundation.h>
-#import "Firestore/Source/Local/FSTQueryCache.h"
-
-#ifdef __cplusplus
#include <memory>
-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 <FSTQueryCache>
+/**
+ * Retrieves the global singleton metadata row from the given database, if it exists.
+ */
++ (nullable FSTPBTargetGlobal *)readTargetMetadataFromDB:(std::shared_ptr<leveldb::DB>)db;
+
- (instancetype)init NS_UNAVAILABLE;
/** The garbage collector to notify about potential garbage keys. */
@property(nonatomic, weak, readwrite, nullable) id<FSTGarbageCollector> 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<leveldb::DB>)db
serializer:(FSTLocalSerializer *)serializer NS_DESIGNATED_INITIALIZER;
-#endif
@end
diff --git a/Firestore/Source/Local/FSTLevelDBQueryCache.mm b/Firestore/Source/Local/FSTLevelDBQueryCache.mm
index 13d15ee..b3f4822 100644
--- a/Firestore/Source/Local/FSTLevelDBQueryCache.mm
+++ b/Firestore/Source/Local/FSTLevelDBQueryCache.mm
@@ -22,6 +22,7 @@
#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 +30,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 +40,6 @@ using leveldb::Slice;
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 +60,29 @@ static ReadOptions GetStandardReadOptions() {
FSTSnapshotVersion *_lastRemoteSnapshotVersion;
}
++ (nullable FSTPBTargetGlobal *)readTargetMetadataFromDB:(std::shared_ptr<DB>)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>)db serializer:(FSTLocalSerializer *)serializer {
if (self = [super init]) {
FSTAssert(db, @"db must not be NULL");
@@ -84,11 +93,10 @@ static ReadOptions GetStandardReadOptions() {
}
- (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 +108,10 @@ static ReadOptions GetStandardReadOptions() {
return self.metadata.highestTargetId;
}
+- (FSTListenSequenceNumber)highestListenSequenceNumber {
+ return self.metadata.highestListenSequenceNumber;
+}
+
- (FSTSnapshotVersion *)lastRemoteSnapshotVersion {
return _lastRemoteSnapshotVersion;
}
@@ -116,7 +128,6 @@ static ReadOptions GetStandardReadOptions() {
}
- (void)addQueryData:(FSTQueryData *)queryData group:(FSTWriteGroup *)group {
- // TODO(mcg): actually populate listen sequence number
FSTTargetID targetID = queryData.targetID;
std::string key = [FSTLevelDBTargetKey keyWithTargetID:targetID];
[group setMessage:[self.serializer encodedQueryData:queryData] forKey:key];
@@ -127,9 +138,19 @@ static ReadOptions GetStandardReadOptions() {
std::string emptyBuffer;
[group setData:emptyBuffer forKey:indexKey];
+ BOOL saveMetadata = NO;
FSTPBTargetGlobal *metadata = self.metadata;
if (targetID > metadata.highestTargetId) {
metadata.highestTargetId = targetID;
+ saveMetadata = YES;
+ }
+
+ if (queryData.sequenceNumber > metadata.highestListenSequenceNumber) {
+ metadata.highestListenSequenceNumber = queryData.sequenceNumber;
+ saveMetadata = YES;
+ }
+
+ if (saveMetadata) {
[group setMessage:metadata forKey:[FSTLevelDBTargetGlobalKey key]];
}
}
@@ -148,34 +169,6 @@ static ReadOptions GetStandardReadOptions() {
}
/**
- * 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;
-}
-
-/**
* Parses the given bytes as an FSTPBTarget protocol buffer and then converts to the equivalent
* query data.
*/
@@ -197,7 +190,7 @@ static ReadOptions GetStandardReadOptions() {
// 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<Iterator> indexItererator(_db->NewIterator(GetStandardReadOptions()));
+ std::unique_ptr<Iterator> indexItererator(_db->NewIterator([FSTLevelDB standardReadOptions]));
std::string indexPrefix = [FSTLevelDBQueryTargetKey keyPrefixWithCanonicalID:canonicalID];
indexItererator->Seek(indexPrefix);
@@ -205,7 +198,7 @@ static ReadOptions GetStandardReadOptions() {
// 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<Iterator> targetIterator(_db->NewIterator(GetStandardReadOptions()));
+ std::unique_ptr<Iterator> targetIterator(_db->NewIterator([FSTLevelDB standardReadOptions]));
FSTLevelDBQueryTargetKey *rowKey = [[FSTLevelDBQueryTargetKey alloc] init];
for (; indexItererator->Valid(); indexItererator->Next()) {
@@ -277,7 +270,7 @@ static ReadOptions GetStandardReadOptions() {
- (void)removeMatchingKeysForTargetID:(FSTTargetID)targetID group:(FSTWriteGroup *)group {
std::string indexPrefix = [FSTLevelDBTargetDocumentKey keyPrefixWithTargetID:targetID];
- std::unique_ptr<Iterator> indexIterator(_db->NewIterator(GetStandardReadOptions()));
+ std::unique_ptr<Iterator> indexIterator(_db->NewIterator([FSTLevelDB standardReadOptions]));
indexIterator->Seek(indexPrefix);
FSTLevelDBTargetDocumentKey *rowKey = [[FSTLevelDBTargetDocumentKey alloc] init];
@@ -300,7 +293,7 @@ static ReadOptions GetStandardReadOptions() {
- (FSTDocumentKeySet *)matchingKeysForTargetID:(FSTTargetID)targetID {
std::string indexPrefix = [FSTLevelDBTargetDocumentKey keyPrefixWithTargetID:targetID];
- std::unique_ptr<Iterator> indexIterator(_db->NewIterator(GetStandardReadOptions()));
+ std::unique_ptr<Iterator> indexIterator(_db->NewIterator([FSTLevelDB standardReadOptions]));
indexIterator->Seek(indexPrefix);
FSTDocumentKeySet *result = [FSTDocumentKeySet keySet];
@@ -323,7 +316,7 @@ static ReadOptions GetStandardReadOptions() {
- (BOOL)containsKey:(FSTDocumentKey *)key {
std::string indexPrefix = [FSTLevelDBDocumentTargetKey keyPrefixWithResourcePath:key.path];
- std::unique_ptr<Iterator> indexIterator(_db->NewIterator(GetStandardReadOptions()));
+ std::unique_ptr<Iterator> 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 1da3cca..20942e2 100644
--- a/Firestore/Source/Local/FSTLevelDBRemoteDocumentCache.h
+++ b/Firestore/Source/Local/FSTLevelDBRemoteDocumentCache.h
@@ -16,15 +16,10 @@
#import <Foundation/Foundation.h>
-#import "Firestore/Source/Local/FSTRemoteDocumentCache.h"
-
-#ifdef __cplusplus
#include <memory>
-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<leveldb::DB>)db
serializer:(FSTLocalSerializer *)serializer NS_DESIGNATED_INITIALIZER;
-#endif
@end
diff --git a/Firestore/Source/Local/FSTLevelDBRemoteDocumentCache.mm b/Firestore/Source/Local/FSTLevelDBRemoteDocumentCache.mm
index 02f9f3e..b842cb5 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
index a6734c4..0e88958 100644
--- a/Firestore/Source/Local/FSTLocalDocumentsView.m
+++ b/Firestore/Source/Local/FSTLocalDocumentsView.mm
@@ -167,9 +167,9 @@ NS_ASSUME_NONNULL_BEGIN
BOOL *stop) {
FSTMaybeDocument *mutatedDoc = [self localDocument:remoteDocument key:key];
if ([mutatedDoc isKindOfClass:[FSTDeletedDocument class]]) {
- result = [documents dictionaryByRemovingObjectForKey:key];
+ result = [result dictionaryByRemovingObjectForKey:key];
} else if ([mutatedDoc isKindOfClass:[FSTDocument class]]) {
- result = [documents dictionaryBySettingObject:(FSTDocument *)mutatedDoc forKey:key];
+ result = [result dictionaryBySettingObject:(FSTDocument *)mutatedDoc forKey:key];
} else {
FSTFail(@"Unknown document: %@", mutatedDoc);
}
diff --git a/Firestore/Source/Local/FSTLocalSerializer.m b/Firestore/Source/Local/FSTLocalSerializer.mm
index c71e9dd..c531c77 100644
--- a/Firestore/Source/Local/FSTLocalSerializer.m
+++ b/Firestore/Source/Local/FSTLocalSerializer.mm
@@ -16,6 +16,8 @@
#import "Firestore/Source/Local/FSTLocalSerializer.h"
+#include <inttypes.h>
+
#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"
@@ -156,6 +158,7 @@
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 @@
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 @@
return [[FSTQueryData alloc] initWithQuery:query
targetID:targetID
+ listenSequenceNumber:sequenceNumber
purpose:FSTQueryPurposeListen
snapshotVersion:version
resumeToken:resumeToken];
diff --git a/Firestore/Source/Local/FSTLocalStore.m b/Firestore/Source/Local/FSTLocalStore.mm
index cde7104..fa77e37 100644
--- a/Firestore/Source/Local/FSTLocalStore.m
+++ b/Firestore/Source/Local/FSTLocalStore.mm
@@ -17,9 +17,9 @@
#import "Firestore/Source/Local/FSTLocalStore.h"
#import "Firestore/Source/Auth/FSTUser.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"
@@ -41,6 +41,8 @@
#import "Firestore/Source/Util/FSTAssert.h"
#import "Firestore/Source/Util/FSTLogger.h"
+#include "Firestore/core/src/firebase/firestore/core/target_id_generator.h"
+
NS_ASSUME_NONNULL_BEGIN
@interface FSTLocalStore ()
@@ -73,8 +75,7 @@ NS_ASSUME_NONNULL_BEGIN
/** Maps a targetID to data about its query. */
@property(nonatomic, strong) NSMutableDictionary<NSNumber *, FSTQueryData *> *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,7 +90,10 @@ NS_ASSUME_NONNULL_BEGIN
@end
-@implementation FSTLocalStore
+@implementation FSTLocalStore {
+ /** Used to generate targetIDs for queries tracked locally. */
+ firebase::firestore::core::TargetIdGenerator _targetIDGenerator;
+}
- (instancetype)initWithPersistence:(id<FSTPersistence>)persistence
garbageCollector:(id<FSTGarbageCollector>)garbageCollector
@@ -110,6 +114,9 @@ NS_ASSUME_NONNULL_BEGIN
_targetIDs = [NSMutableDictionary dictionary];
_heldBatchResults = [NSMutableArray array];
+
+ _targetIDGenerator =
+ firebase::firestore::core::TargetIdGenerator::LocalStoreTargetIdGenerator(0);
}
return self;
}
@@ -147,7 +154,10 @@ NS_ASSUME_NONNULL_BEGIN
[self.queryCache start];
FSTTargetID targetID = [self.queryCache highestTargetID];
- self.targetIDGenerator = [FSTTargetIDGenerator generatorForLocalStoreStartingAfterID:targetID];
+ _targetIDGenerator =
+ firebase::firestore::core::TargetIdGenerator::LocalStoreTargetIdGenerator(targetID);
+ FSTListenSequenceNumber sequenceNumber = [self.queryCache highestListenSequenceNumber];
+ self.listenSequence = [[FSTListenSequence alloc] initStartingAfter:sequenceNumber];
}
- (void)shutdown {
@@ -380,6 +390,7 @@ NS_ASSUME_NONNULL_BEGIN
- (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 +398,11 @@ NS_ASSUME_NONNULL_BEGIN
} 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
index 9a7f445..9a7f445 100644
--- a/Firestore/Source/Local/FSTLocalViewChanges.m
+++ b/Firestore/Source/Local/FSTLocalViewChanges.mm
diff --git a/Firestore/Source/Local/FSTLocalWriteResult.m b/Firestore/Source/Local/FSTLocalWriteResult.mm
index c1753fe..c1753fe 100644
--- a/Firestore/Source/Local/FSTLocalWriteResult.m
+++ b/Firestore/Source/Local/FSTLocalWriteResult.mm
diff --git a/Firestore/Source/Local/FSTMemoryMutationQueue.m b/Firestore/Source/Local/FSTMemoryMutationQueue.mm
index b155264..702f614 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 ()
/**
@@ -260,7 +263,7 @@ NS_ASSUME_NONNULL_BEGIN
// Find unique batchIDs referenced by all documents potentially matching the query.
__block FSTImmutableSortedSet<NSNumber *> *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
index e301820..e301820 100644
--- a/Firestore/Source/Local/FSTMemoryPersistence.m
+++ b/Firestore/Source/Local/FSTMemoryPersistence.mm
diff --git a/Firestore/Source/Local/FSTMemoryQueryCache.m b/Firestore/Source/Local/FSTMemoryQueryCache.mm
index 8d37bcb..bcab174 100644
--- a/Firestore/Source/Local/FSTMemoryQueryCache.m
+++ b/Firestore/Source/Local/FSTMemoryQueryCache.mm
@@ -34,6 +34,8 @@ NS_ASSUME_NONNULL_BEGIN
/** The highest numbered target ID encountered. */
@property(nonatomic, assign) FSTTargetID highestTargetID;
+@property(nonatomic, assign) FSTListenSequenceNumber highestListenSequenceNumber;
+
@end
@implementation FSTMemoryQueryCache {
@@ -65,6 +67,10 @@ NS_ASSUME_NONNULL_BEGIN
return _highestTargetID;
}
+- (FSTListenSequenceNumber)highestListenSequenceNumber {
+ return _highestListenSequenceNumber;
+}
+
- (FSTSnapshotVersion *)lastRemoteSnapshotVersion {
return _lastRemoteSnapshotVersion;
}
@@ -79,6 +85,9 @@ NS_ASSUME_NONNULL_BEGIN
if (queryData.targetID > self.highestTargetID) {
self.highestTargetID = queryData.targetID;
}
+ if (queryData.sequenceNumber > self.highestListenSequenceNumber) {
+ self.highestListenSequenceNumber = queryData.sequenceNumber;
+ }
}
- (void)removeQueryData:(FSTQueryData *)queryData group:(__unused FSTWriteGroup *)group {
diff --git a/Firestore/Source/Local/FSTMemoryRemoteDocumentCache.m b/Firestore/Source/Local/FSTMemoryRemoteDocumentCache.mm
index 9bbc047..9bbc047 100644
--- a/Firestore/Source/Local/FSTMemoryRemoteDocumentCache.m
+++ b/Firestore/Source/Local/FSTMemoryRemoteDocumentCache.mm
diff --git a/Firestore/Source/Local/FSTNoOpGarbageCollector.m b/Firestore/Source/Local/FSTNoOpGarbageCollector.mm
index e03b599..e03b599 100644
--- a/Firestore/Source/Local/FSTNoOpGarbageCollector.m
+++ b/Firestore/Source/Local/FSTNoOpGarbageCollector.mm
diff --git a/Firestore/Source/Local/FSTQueryCache.h b/Firestore/Source/Local/FSTQueryCache.h
index e0cf4c8..88c9df9 100644
--- a/Firestore/Source/Local/FSTQueryCache.h
+++ b/Firestore/Source/Local/FSTQueryCache.h
@@ -53,6 +53,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
* this version (e.g. for targets resumed with a resume_token) should be suppressed (buffered)
diff --git a/Firestore/Source/Local/FSTQueryData.h b/Firestore/Source/Local/FSTQueryData.h
index 048bfad..5db2de6 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
index 080f136..6bb716a 100644
--- a/Firestore/Source/Local/FSTQueryData.m
+++ b/Firestore/Source/Local/FSTQueryData.mm
@@ -25,6 +25,7 @@ NS_ASSUME_NONNULL_BEGIN
- (instancetype)initWithQuery:(FSTQuery *)query
targetID:(FSTTargetID)targetID
+ listenSequenceNumber:(FSTListenSequenceNumber)sequenceNumber
purpose:(FSTQueryPurpose)purpose
snapshotVersion:(FSTSnapshotVersion *)snapshotVersion
resumeToken:(NSData *)resumeToken {
@@ -32,6 +33,7 @@ NS_ASSUME_NONNULL_BEGIN
if (self) {
_query = query;
_targetID = targetID;
+ _sequenceNumber = sequenceNumber;
_purpose = purpose;
_snapshotVersion = snapshotVersion;
_resumeToken = [resumeToken copy];
@@ -41,9 +43,11 @@ NS_ASSUME_NONNULL_BEGIN
- (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 @@ NS_ASSUME_NONNULL_BEGIN
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
index 2acd64b..2acd64b 100644
--- a/Firestore/Source/Local/FSTReferenceSet.m
+++ b/Firestore/Source/Local/FSTReferenceSet.mm
diff --git a/Firestore/Source/Local/FSTRemoteDocumentChangeBuffer.m b/Firestore/Source/Local/FSTRemoteDocumentChangeBuffer.mm
index bca587a..bca587a 100644
--- a/Firestore/Source/Local/FSTRemoteDocumentChangeBuffer.m
+++ b/Firestore/Source/Local/FSTRemoteDocumentChangeBuffer.mm
diff --git a/Firestore/Source/Local/FSTWriteGroup.h b/Firestore/Source/Local/FSTWriteGroup.h
index 5ea0387..c21ff72 100644
--- a/Firestore/Source/Local/FSTWriteGroup.h
+++ b/Firestore/Source/Local/FSTWriteGroup.h
@@ -16,17 +16,10 @@
#import <Foundation/Foundation.h>
-#ifdef __cplusplus
#include <memory>
#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<leveldb::DB>)db;
-#endif
-
@end
NS_ASSUME_NONNULL_END
diff --git a/Firestore/Source/Local/FSTWriteGroup.mm b/Firestore/Source/Local/FSTWriteGroup.mm
index 6859d53..80d09ca 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
index 7e3bf60..7e3bf60 100644
--- a/Firestore/Source/Local/FSTWriteGroupTracker.m
+++ b/Firestore/Source/Local/FSTWriteGroupTracker.mm
diff --git a/Firestore/Source/Local/StringView.h b/Firestore/Source/Local/StringView.h
index b81b7b5..4e36cff 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 <Foundation/Foundation.h>
#include <leveldb/slice.h>
#include <string>
+#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.m b/Firestore/Source/Model/FSTDatabaseID.mm
index 4d0448a..bff5855 100644
--- a/Firestore/Source/Model/FSTDatabaseID.m
+++ b/Firestore/Source/Model/FSTDatabaseID.mm
@@ -48,7 +48,7 @@ NSString *const kDefaultDatabaseID = @"(default)";
- (BOOL)isEqual:(id)other {
if (other == self) return YES;
- if (!other || ![[other class] isEqual:[self class]]) return NO;
+ if (![[other class] isEqual:[self class]]) return NO;
return [self isEqualToDatabaseId:other];
}
diff --git a/Firestore/Source/Model/FSTDocument.m b/Firestore/Source/Model/FSTDocument.mm
index bf416e7..bf416e7 100644
--- a/Firestore/Source/Model/FSTDocument.m
+++ b/Firestore/Source/Model/FSTDocument.mm
diff --git a/Firestore/Source/Model/FSTDocumentDictionary.m b/Firestore/Source/Model/FSTDocumentDictionary.mm
index 362af54..362af54 100644
--- a/Firestore/Source/Model/FSTDocumentDictionary.m
+++ b/Firestore/Source/Model/FSTDocumentDictionary.mm
diff --git a/Firestore/Source/Model/FSTDocumentKey.m b/Firestore/Source/Model/FSTDocumentKey.mm
index a382a55..a382a55 100644
--- a/Firestore/Source/Model/FSTDocumentKey.m
+++ b/Firestore/Source/Model/FSTDocumentKey.mm
diff --git a/Firestore/Source/Model/FSTDocumentKeySet.m b/Firestore/Source/Model/FSTDocumentKeySet.mm
index f07b785..f07b785 100644
--- a/Firestore/Source/Model/FSTDocumentKeySet.m
+++ b/Firestore/Source/Model/FSTDocumentKeySet.mm
diff --git a/Firestore/Source/Model/FSTDocumentSet.h b/Firestore/Source/Model/FSTDocumentSet.h
index a7f8c4a..022e900 100644
--- a/Firestore/Source/Model/FSTDocumentSet.h
+++ b/Firestore/Source/Model/FSTDocumentSet.h
@@ -59,16 +59,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
index c4c0f49..6f44799 100644
--- a/Firestore/Source/Model/FSTDocumentSet.m
+++ b/Firestore/Source/Model/FSTDocumentSet.mm
@@ -135,16 +135,6 @@ typedef FSTImmutableSortedSet<FSTDocument *> SetType;
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
index 870e082..870e082 100644
--- a/Firestore/Source/Model/FSTDocumentVersionDictionary.m
+++ b/Firestore/Source/Model/FSTDocumentVersionDictionary.mm
diff --git a/Firestore/Source/Model/FSTFieldValue.h b/Firestore/Source/Model/FSTFieldValue.h
index 6de9793..93fd5c4 100644
--- a/Firestore/Source/Model/FSTFieldValue.h
+++ b/Firestore/Source/Model/FSTFieldValue.h
@@ -22,7 +22,9 @@
@class FSTDocumentKey;
@class FSTFieldPath;
@class FSTTimestamp;
+@class FSTFieldValueOptions;
@class FIRGeoPoint;
+@class FIRSnapshotOptions;
NS_ASSUME_NONNULL_BEGIN
@@ -40,6 +42,32 @@ typedef NS_ENUM(NSInteger, FSTTypeOrder) {
FSTTypeOrderObject,
};
+/** Defines the return value for pending server timestamps. */
+typedef NS_ENUM(NSInteger, FSTServerTimestampBehavior) {
+ FSTServerTimestampBehaviorNone,
+ FSTServerTimestampBehaviorEstimate,
+ FSTServerTimestampBehaviorPrevious
+};
+
+/** Holds properties that define field value deserialization options. */
+@interface FSTFieldValueOptions : NSObject
+
+@property(nonatomic, readonly, assign) FSTServerTimestampBehavior serverTimestampBehavior;
+
+- (instancetype)init NS_UNAVAILABLE;
+
+/**
+ * Creates an FSTFieldValueOptions instance that specifies deserialization behavior for pending
+ * server timestamps.
+ */
+- (instancetype)initWithServerTimestampBehavior:(FSTServerTimestampBehavior)serverTimestampBehavior
+ NS_DESIGNATED_INITIALIZER;
+
+/** Creates an FSTFieldValueOption instance from FIRSnapshotOptions. */
++ (instancetype)optionsForSnapshotOptions:(FIRSnapshotOptions *)value;
+
+@end
+
/**
* Abstract base class representing an immutable data value as stored in Firestore. FSTFieldValue
* represents all the different kinds of values that can be stored in fields in a document.
@@ -58,7 +86,7 @@ typedef NS_ENUM(NSInteger, FSTTypeOrder) {
* - Array
* - Object
*/
-@interface FSTFieldValue : NSObject
+@interface FSTFieldValue <__covariant T> : NSObject
/** Returns the FSTTypeOrder for this value. */
- (FSTTypeOrder)typeOrder;
@@ -69,7 +97,15 @@ typedef NS_ENUM(NSInteger, FSTTypeOrder) {
* TODO(mikelehen): This conversion should probably happen at the API level and right now `value` is
* used inappropriately in the serializer implementation, etc. We need to do some reworking.
*/
-- (id)value;
+- (T)value;
+
+/**
+ * Converts an FSTFieldValue into the value that users will see in document snapshots.
+ *
+ * Options can be provided to configure the deserialization of some field values (such as server
+ * timestamps).
+ */
+- (T)valueWithOptions:(FSTFieldValueOptions *)options;
/** Compares against another FSTFieldValue. */
- (NSComparisonResult)compare:(FSTFieldValue *)other;
@@ -79,26 +115,24 @@ typedef NS_ENUM(NSInteger, FSTTypeOrder) {
/**
* A null value stored in Firestore. The |value| of a FSTNullValue is [NSNull null].
*/
-@interface FSTNullValue : FSTFieldValue
+@interface FSTNullValue : FSTFieldValue <NSNull *>
+ (instancetype)nullValue;
-- (id)value;
@end
/**
* A boolean value stored in Firestore.
*/
-@interface FSTBooleanValue : FSTFieldValue
+@interface FSTBooleanValue : FSTFieldValue <NSNumber *>
+ (instancetype)trueValue;
+ (instancetype)falseValue;
+ (instancetype)booleanValue:(BOOL)value;
-- (NSNumber *)value;
@end
/**
* Base class inherited from by FSTIntegerValue and FSTDoubleValue. It implements proper number
* comparisons between the two types.
*/
-@interface FSTNumberValue : FSTFieldValue
+@interface FSTNumberValue : FSTFieldValue <NSNumber *>
@end
/**
@@ -106,7 +140,6 @@ typedef NS_ENUM(NSInteger, FSTTypeOrder) {
*/
@interface FSTIntegerValue : FSTNumberValue
+ (instancetype)integerValue:(int64_t)value;
-- (NSNumber *)value;
- (int64_t)internalValue;
@end
@@ -116,24 +149,21 @@ typedef NS_ENUM(NSInteger, FSTTypeOrder) {
@interface FSTDoubleValue : FSTNumberValue
+ (instancetype)doubleValue:(double)value;
+ (instancetype)nanValue;
-- (NSNumber *)value;
- (double)internalValue;
@end
/**
* A string stored in Firestore.
*/
-@interface FSTStringValue : FSTFieldValue
+@interface FSTStringValue : FSTFieldValue <NSString *>
+ (instancetype)stringValue:(NSString *)value;
-- (NSString *)value;
@end
/**
* A timestamp value stored in Firestore.
*/
-@interface FSTTimestampValue : FSTFieldValue
+@interface FSTTimestampValue : FSTFieldValue <NSDate *>
+ (instancetype)timestampValue:(FSTTimestamp *)value;
-- (NSDate *)value;
- (FSTTimestamp *)internalValue;
@end
@@ -144,46 +174,54 @@ typedef NS_ENUM(NSInteger, FSTTypeOrder) {
* - FSTServerTimestampValue instances are created as the result of applying an FSTTransformMutation
* (see [FSTTransformMutation applyTo]). They can only exist in the local view of a document.
* Therefore they do not need to be parsed or serialized.
- * - When evaluated locally (e.g. via FSTDocumentSnapshot data), they evaluate to NSNull (at least
- * for now, see b/62064202).
+ * - When evaluated locally (e.g. via FSTDocumentSnapshot data), they by default evaluate to NSNull.
+ * This behavior can be configured by passing custom FSTFieldValueOptions to `valueWithOptions:`.
* - They sort after all FSTTimestampValues. With respect to other FSTServerTimestampValues, they
* sort by their localWriteTime.
*/
-@interface FSTServerTimestampValue : FSTFieldValue
-+ (instancetype)serverTimestampValueWithLocalWriteTime:(FSTTimestamp *)localWriteTime;
-- (NSNull *)value;
+@interface FSTServerTimestampValue : FSTFieldValue <id>
++ (instancetype)serverTimestampValueWithLocalWriteTime:(FSTTimestamp *)localWriteTime
+ previousValue:(nullable FSTFieldValue *)previousValue;
+
@property(nonatomic, strong, readonly) FSTTimestamp *localWriteTime;
+@property(nonatomic, strong, readonly, nullable) FSTFieldValue *previousValue;
+
@end
/**
* A geo point value stored in Firestore.
*/
-@interface FSTGeoPointValue : FSTFieldValue
+@interface FSTGeoPointValue : FSTFieldValue <FIRGeoPoint *>
+ (instancetype)geoPointValue:(FIRGeoPoint *)value;
-- (FIRGeoPoint *)value;
+- (FIRGeoPoint *)valueWithOptions:(FSTFieldValueOptions *)options;
@end
/**
* A blob value stored in Firestore.
*/
-@interface FSTBlobValue : FSTFieldValue
+@interface FSTBlobValue : FSTFieldValue <NSData *>
+ (instancetype)blobValue:(NSData *)value;
-- (NSData *)value;
+- (NSData *)valueWithOptions:(FSTFieldValueOptions *)options;
@end
/**
* A reference value stored in Firestore.
*/
-@interface FSTReferenceValue : FSTFieldValue
+@interface FSTReferenceValue : FSTFieldValue <FSTDocumentKey *>
+ (instancetype)referenceValue:(FSTDocumentKey *)value databaseID:(FSTDatabaseID *)databaseID;
-- (FSTDocumentKey *)value;
+- (FSTDocumentKey *)valueWithOptions:(FSTFieldValueOptions *)options;
@property(nonatomic, strong, readonly) FSTDatabaseID *databaseID;
@end
/**
* A structured object value stored in Firestore.
*/
-@interface FSTObjectValue : FSTFieldValue
+// clang-format off
+@interface FSTObjectValue : FSTFieldValue < NSDictionary<NSString *, id> * >
+
+- (instancetype)init NS_UNAVAILABLE;
+// clang-format on
+
/** Returns an empty FSTObjectValue. */
+ (instancetype)objectValue;
@@ -198,9 +236,7 @@ typedef NS_ENUM(NSInteger, FSTTypeOrder) {
- (instancetype)initWithImmutableDictionary:
(FSTImmutableSortedDictionary<NSString *, FSTFieldValue *> *)value NS_DESIGNATED_INITIALIZER;
-- (instancetype)init NS_UNAVAILABLE;
-
-- (NSDictionary<NSString *, id> *)value;
+- (NSDictionary<NSString *, id> *)valueWithOptions:(FSTFieldValueOptions *)options;
- (FSTImmutableSortedDictionary<NSString *, FSTFieldValue *> *)internalValue;
/** Returns the value at the given path if it exists. Returns nil otherwise. */
@@ -222,19 +258,20 @@ typedef NS_ENUM(NSInteger, FSTTypeOrder) {
/**
* An array value stored in Firestore.
*/
-@interface FSTArrayValue : FSTFieldValue
+// clang-format off
+@interface FSTArrayValue : FSTFieldValue < NSArray <id> * >
+
+- (instancetype)init NS_UNAVAILABLE;
+// clang-format on
/**
* Initializes this instance with the given array of wrapped values.
*
* @param value An immutable array of FSTFieldValue objects. Caller is responsible for copying the
- * value or releasing all references.
+ * value or releasing all references.
*/
- (instancetype)initWithValueNoCopy:(NSArray<FSTFieldValue *> *)value NS_DESIGNATED_INITIALIZER;
-- (instancetype)init NS_UNAVAILABLE;
-
-- (NSArray<id> *)value;
- (NSArray<FSTFieldValue *> *)internalValue;
@end
diff --git a/Firestore/Source/Model/FSTFieldValue.m b/Firestore/Source/Model/FSTFieldValue.mm
index 95ad306..8ffc98e 100644
--- a/Firestore/Source/Model/FSTFieldValue.m
+++ b/Firestore/Source/Model/FSTFieldValue.mm
@@ -16,17 +16,60 @@
#import "Firestore/Source/Model/FSTFieldValue.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"
+
+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
+#pragma mark - FSTFieldValueOptions
+
+@implementation FSTFieldValueOptions
+
++ (instancetype)optionsForSnapshotOptions:(FIRSnapshotOptions *)options {
+ if (options.serverTimestampBehavior == FSTServerTimestampBehaviorNone) {
+ static FSTFieldValueOptions *defaultInstance = nil;
+ static dispatch_once_t onceToken;
+
+ dispatch_once(&onceToken, ^{
+ defaultInstance = [[FSTFieldValueOptions alloc]
+ initWithServerTimestampBehavior:FSTServerTimestampBehaviorNone];
+ });
+ return defaultInstance;
+ } else {
+ return [[FSTFieldValueOptions alloc]
+ initWithServerTimestampBehavior:options.serverTimestampBehavior];
+ }
+}
+
+- (instancetype)initWithServerTimestampBehavior:
+ (FSTServerTimestampBehavior)serverTimestampBehavior {
+ self = [super init];
+
+ if (self) {
+ _serverTimestampBehavior = serverTimestampBehavior;
+ }
+ return self;
+}
+
+@end
+
#pragma mark - FSTFieldValue
@interface FSTFieldValue ()
@@ -40,6 +83,11 @@ NS_ASSUME_NONNULL_BEGIN
}
- (id)value {
+ return [self valueWithOptions:[FSTFieldValueOptions
+ optionsForSnapshotOptions:[FIRSnapshotOptions defaultOptions]]];
+}
+
+- (id)valueWithOptions:(FSTFieldValueOptions *)options {
@throw FSTAbstractMethodException(); // NOLINT
}
@@ -89,7 +137,7 @@ NS_ASSUME_NONNULL_BEGIN
return FSTTypeOrderNull;
}
-- (id)value {
+- (id)valueWithOptions:(FSTFieldValueOptions *)options {
return [NSNull null];
}
@@ -155,7 +203,7 @@ NS_ASSUME_NONNULL_BEGIN
return FSTTypeOrderBoolean;
}
-- (id)value {
+- (id)valueWithOptions:(FSTFieldValueOptions *)options {
return self.internalValue ? @YES : @NO;
}
@@ -170,7 +218,7 @@ NS_ASSUME_NONNULL_BEGIN
- (NSComparisonResult)compare:(FSTFieldValue *)other {
if ([other isKindOfClass:[FSTBooleanValue class]]) {
- return FSTCompareBools(self.internalValue, ((FSTBooleanValue *)other).internalValue);
+ return WrapCompare<bool>(self.internalValue, ((FSTBooleanValue *)other).internalValue);
} else {
return [self defaultCompare:other];
}
@@ -193,19 +241,22 @@ NS_ASSUME_NONNULL_BEGIN
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<NSComparisonResult>(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<NSComparisonResult>(result);
}
}
}
@@ -233,7 +284,7 @@ NS_ASSUME_NONNULL_BEGIN
return self;
}
-- (id)value {
+- (id)valueWithOptions:(FSTFieldValueOptions *)options {
return @(self.internalValue);
}
@@ -285,7 +336,7 @@ NS_ASSUME_NONNULL_BEGIN
return self;
}
-- (id)value {
+- (id)valueWithOptions:(FSTFieldValueOptions *)options {
return @(self.internalValue);
}
@@ -296,11 +347,11 @@ NS_ASSUME_NONNULL_BEGIN
// 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.
@@ -309,6 +360,17 @@ NS_ASSUME_NONNULL_BEGIN
#pragma mark - FSTStringValue
+/**
+ * Specialization of Comparator for NSStrings.
+ */
+template <>
+struct Comparator<NSString *> {
+ bool operator()(NSString *left, NSString *right) const {
+ Comparator<absl::string_view> lessThan;
+ return lessThan(MakeStringView(left), MakeStringView(right));
+ }
+};
+
@interface FSTStringValue ()
@property(nonatomic, copy, readonly) NSString *internalValue;
@end
@@ -332,7 +394,7 @@ NS_ASSUME_NONNULL_BEGIN
return FSTTypeOrderString;
}
-- (id)value {
+- (id)valueWithOptions:(FSTFieldValueOptions *)options {
return self.internalValue;
}
@@ -347,7 +409,7 @@ NS_ASSUME_NONNULL_BEGIN
- (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];
}
@@ -379,7 +441,7 @@ NS_ASSUME_NONNULL_BEGIN
return FSTTypeOrderTimestamp;
}
-- (id)value {
+- (id)valueWithOptions:(FSTFieldValueOptions *)options {
// For developers, we expose Timestamps as Dates.
return self.internalValue.approximateDateValue;
}
@@ -410,14 +472,18 @@ NS_ASSUME_NONNULL_BEGIN
@implementation FSTServerTimestampValue
-+ (instancetype)serverTimestampValueWithLocalWriteTime:(FSTTimestamp *)localWriteTime {
- return [[FSTServerTimestampValue alloc] initWithLocalWriteTime:localWriteTime];
++ (instancetype)serverTimestampValueWithLocalWriteTime:(FSTTimestamp *)localWriteTime
+ previousValue:(nullable FSTFieldValue *)previousValue {
+ return [[FSTServerTimestampValue alloc] initWithLocalWriteTime:localWriteTime
+ previousValue:previousValue];
}
-- (id)initWithLocalWriteTime:(FSTTimestamp *)localWriteTime {
+- (id)initWithLocalWriteTime:(FSTTimestamp *)localWriteTime
+ previousValue:(nullable FSTFieldValue *)previousValue {
self = [super init];
if (self) {
_localWriteTime = localWriteTime;
+ _previousValue = previousValue;
}
return self;
}
@@ -426,9 +492,17 @@ NS_ASSUME_NONNULL_BEGIN
return FSTTypeOrderTimestamp;
}
-- (NSNull *)value {
- // For developers, server timestamps always evaluate to NSNull (for now, at least; b/62064202).
- return [NSNull null];
+- (id)valueWithOptions:(FSTFieldValueOptions *)options {
+ switch (options.serverTimestampBehavior) {
+ case FSTServerTimestampBehaviorNone:
+ return [NSNull null];
+ case FSTServerTimestampBehaviorEstimate:
+ return [self.localWriteTime approximateDateValue];
+ case FSTServerTimestampBehaviorPrevious:
+ return self.previousValue ? [self.previousValue valueWithOptions:options] : [NSNull null];
+ default:
+ FSTFail(@"Unexpected server timestamp option: %d", (int)options.serverTimestampBehavior);
+ }
}
- (BOOL)isEqual:(id)other {
@@ -481,7 +555,7 @@ NS_ASSUME_NONNULL_BEGIN
return FSTTypeOrderGeoPoint;
}
-- (id)value {
+- (id)valueWithOptions:(FSTFieldValueOptions *)options {
return self.internalValue;
}
@@ -506,6 +580,22 @@ NS_ASSUME_NONNULL_BEGIN
#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
@@ -529,7 +619,7 @@ NS_ASSUME_NONNULL_BEGIN
return FSTTypeOrderBlob;
}
-- (id)value {
+- (id)valueWithOptions:(FSTFieldValueOptions *)options {
return self.internalValue;
}
@@ -544,7 +634,7 @@ NS_ASSUME_NONNULL_BEGIN
- (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];
}
@@ -573,7 +663,7 @@ NS_ASSUME_NONNULL_BEGIN
return self;
}
-- (id)value {
+- (id)valueWithOptions:(FSTFieldValueOptions *)options {
return self.key;
}
@@ -614,6 +704,10 @@ NS_ASSUME_NONNULL_BEGIN
#pragma mark - FSTObjectValue
+static const NSComparator StringComparator = ^NSComparisonResult(NSString *left, NSString *right) {
+ return WrapCompare(left, right);
+};
+
@interface FSTObjectValue ()
@property(nonatomic, strong, readonly)
FSTImmutableSortedDictionary<NSString *, FSTFieldValue *> *internalValue;
@@ -627,7 +721,7 @@ NS_ASSUME_NONNULL_BEGIN
dispatch_once(&onceToken, ^{
FSTImmutableSortedDictionary<NSString *, FSTFieldValue *> *empty =
- [FSTImmutableSortedDictionary dictionaryWithComparator:FSTStringComparator];
+ [FSTImmutableSortedDictionary dictionaryWithComparator:StringComparator];
sharedEmptyInstance = [[FSTObjectValue alloc] initWithImmutableDictionary:empty];
});
return sharedEmptyInstance;
@@ -644,15 +738,15 @@ NS_ASSUME_NONNULL_BEGIN
- (id)initWithDictionary:(NSDictionary<NSString *, FSTFieldValue *> *)value {
FSTImmutableSortedDictionary<NSString *, FSTFieldValue *> *dictionary =
- [FSTImmutableSortedDictionary dictionaryWithDictionary:value comparator:FSTStringComparator];
+ [FSTImmutableSortedDictionary dictionaryWithDictionary:value comparator:StringComparator];
return [self initWithImmutableDictionary:dictionary];
}
-- (id)value {
+- (id)valueWithOptions:(FSTFieldValueOptions *)options {
NSMutableDictionary *result = [NSMutableDictionary dictionary];
[self.internalValue
enumerateKeysAndObjectsUsingBlock:^(NSString *key, FSTFieldValue *obj, BOOL *stop) {
- result[key] = [obj value];
+ result[key] = [obj valueWithOptions:options];
}];
return result;
}
@@ -698,7 +792,7 @@ NS_ASSUME_NONNULL_BEGIN
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];
}
@@ -803,7 +897,7 @@ NS_ASSUME_NONNULL_BEGIN
return [self.internalValue hash];
}
-- (id)value {
+- (id)valueWithOptions:(FSTFieldValueOptions *)options {
NSMutableArray *result = [NSMutableArray arrayWithCapacity:_internalValue.count];
[self.internalValue enumerateObjectsUsingBlock:^(FSTFieldValue *obj, NSUInteger idx, BOOL *stop) {
[result addObject:[obj value]];
@@ -826,7 +920,7 @@ NS_ASSUME_NONNULL_BEGIN
return cmp;
}
}
- return FSTCompareUIntegers(selfArray.count, otherArray.count);
+ return WrapCompare<int64_t>(selfArray.count, otherArray.count);
} else {
return [self defaultCompare:other];
}
diff --git a/Firestore/Source/Model/FSTMutation.h b/Firestore/Source/Model/FSTMutation.h
index ef7f1c8..7c5f6de 100644
--- a/Firestore/Source/Model/FSTMutation.h
+++ b/Firestore/Source/Model/FSTMutation.h
@@ -158,8 +158,10 @@ typedef NS_ENUM(NSUInteger, FSTPreconditionExists) {
* Applies this mutation to the given FSTDocument, FSTDeletedDocument or nil, if we don't have
* information about this document. Both the input and returned documents can be nil.
*
- * @param maybeDoc The document to mutate. The input document should nil if it does not currently
- * exist.
+ * @param maybeDoc The current state of the document to mutate. The input document should be nil if
+ * it does not currently exist.
+ * @param baseDoc The state of the document prior to this mutation batch. The input document should
+ * be nil if it the document did not exist.
* @param localWriteTime A timestamp indicating the local write time of the batch this mutation is
* a part of.
* @param mutationResult Optional result info from the backend. If omitted, it's assumed that
@@ -196,16 +198,18 @@ typedef NS_ENUM(NSUInteger, FSTPreconditionExists) {
* apply the transform if the prior mutation resulted in an FSTDocument (always true for an
* FSTSetMutation, but not necessarily for an FSTPatchMutation).
*/
-- (FSTMaybeDocument *_Nullable)applyTo:(FSTMaybeDocument *_Nullable)maybeDoc
+- (nullable FSTMaybeDocument *)applyTo:(nullable FSTMaybeDocument *)maybeDoc
+ baseDocument:(nullable FSTMaybeDocument *)baseDoc
localWriteTime:(FSTTimestamp *)localWriteTime
- mutationResult:(FSTMutationResult *_Nullable)mutationResult;
+ mutationResult:(nullable FSTMutationResult *)mutationResult;
/**
* A helper version of applyTo for applying mutations locally (without a mutation result from the
* backend).
*/
-- (FSTMaybeDocument *_Nullable)applyTo:(FSTMaybeDocument *_Nullable)maybeDoc
- localWriteTime:(FSTTimestamp *)localWriteTime;
+- (nullable FSTMaybeDocument *)applyTo:(nullable FSTMaybeDocument *)maybeDoc
+ baseDocument:(nullable FSTMaybeDocument *)baseDoc
+ localWriteTime:(nullable FSTTimestamp *)localWriteTime;
@property(nonatomic, strong, readonly) FSTDocumentKey *key;
diff --git a/Firestore/Source/Model/FSTMutation.m b/Firestore/Source/Model/FSTMutation.mm
index 5b47280..c249138 100644
--- a/Firestore/Source/Model/FSTMutation.m
+++ b/Firestore/Source/Model/FSTMutation.mm
@@ -97,7 +97,7 @@ NS_ASSUME_NONNULL_BEGIN
- (BOOL)isEqual:(id)other {
if (other == self) return YES;
- if (!other || ![[other class] isEqual:[self class]]) return NO;
+ if (![[other class] isEqual:[self class]]) return NO;
FSTFieldTransform *otherFieldTransform = other;
return [self.path isEqual:otherFieldTransform.path] &&
[self.transform isEqual:otherFieldTransform.transform];
@@ -236,15 +236,18 @@ NS_ASSUME_NONNULL_BEGIN
return self;
}
-- (FSTMaybeDocument *_Nullable)applyTo:(FSTMaybeDocument *_Nullable)maybeDoc
+- (nullable FSTMaybeDocument *)applyTo:(nullable FSTMaybeDocument *)maybeDoc
+ baseDocument:(nullable FSTMaybeDocument *)baseDoc
localWriteTime:(FSTTimestamp *)localWriteTime
- mutationResult:(FSTMutationResult *_Nullable)mutationResult {
+ mutationResult:(nullable FSTMutationResult *)mutationResult {
@throw FSTAbstractMethodException(); // NOLINT
}
-- (FSTMaybeDocument *_Nullable)applyTo:(FSTMaybeDocument *_Nullable)maybeDoc
- localWriteTime:(FSTTimestamp *)localWriteTime {
- return [self applyTo:maybeDoc localWriteTime:localWriteTime mutationResult:nil];
+- (nullable FSTMaybeDocument *)applyTo:(nullable FSTMaybeDocument *)maybeDoc
+ baseDocument:(nullable FSTMaybeDocument *)baseDoc
+ localWriteTime:(nullable FSTTimestamp *)localWriteTime {
+ return
+ [self applyTo:maybeDoc baseDocument:baseDoc localWriteTime:localWriteTime mutationResult:nil];
}
@end
@@ -287,9 +290,10 @@ NS_ASSUME_NONNULL_BEGIN
return result;
}
-- (FSTMaybeDocument *_Nullable)applyTo:(FSTMaybeDocument *_Nullable)maybeDoc
+- (nullable FSTMaybeDocument *)applyTo:(nullable FSTMaybeDocument *)maybeDoc
+ baseDocument:(nullable FSTMaybeDocument *)baseDoc
localWriteTime:(FSTTimestamp *)localWriteTime
- mutationResult:(FSTMutationResult *_Nullable)mutationResult {
+ mutationResult:(nullable FSTMutationResult *)mutationResult {
if (mutationResult) {
FSTAssert(!mutationResult.transformResults, @"Transform results received by FSTSetMutation.");
}
@@ -362,9 +366,10 @@ NS_ASSUME_NONNULL_BEGIN
self.key, self.fieldMask, self.value, self.precondition];
}
-- (FSTMaybeDocument *_Nullable)applyTo:(FSTMaybeDocument *_Nullable)maybeDoc
+- (nullable FSTMaybeDocument *)applyTo:(nullable FSTMaybeDocument *)maybeDoc
+ baseDocument:(nullable FSTMaybeDocument *)baseDoc
localWriteTime:(FSTTimestamp *)localWriteTime
- mutationResult:(FSTMutationResult *_Nullable)mutationResult {
+ mutationResult:(nullable FSTMutationResult *)mutationResult {
if (mutationResult) {
FSTAssert(!mutationResult.transformResults, @"Transform results received by FSTPatchMutation.");
}
@@ -451,9 +456,10 @@ NS_ASSUME_NONNULL_BEGIN
self.key, self.fieldTransforms, self.precondition];
}
-- (FSTMaybeDocument *_Nullable)applyTo:(FSTMaybeDocument *_Nullable)maybeDoc
+- (nullable FSTMaybeDocument *)applyTo:(nullable FSTMaybeDocument *)maybeDoc
+ baseDocument:(nullable FSTMaybeDocument *)baseDoc
localWriteTime:(FSTTimestamp *)localWriteTime
- mutationResult:(FSTMutationResult *_Nullable)mutationResult {
+ mutationResult:(nullable FSTMutationResult *)mutationResult {
if (mutationResult) {
FSTAssert(mutationResult.transformResults,
@"Transform results missing for FSTTransformMutation.");
@@ -473,8 +479,9 @@ NS_ASSUME_NONNULL_BEGIN
BOOL hasLocalMutations = (mutationResult == nil);
NSArray<FSTFieldValue *> *transformResults =
- mutationResult ? mutationResult.transformResults
- : [self localTransformResultsWithWriteTime:localWriteTime];
+ mutationResult
+ ? mutationResult.transformResults
+ : [self localTransformResultsWithBaseDocument:baseDoc writeTime:localWriteTime];
FSTObjectValue *newData = [self transformObject:doc.data transformResults:transformResults];
return [FSTDocument documentWithData:newData
key:doc.key
@@ -486,16 +493,26 @@ NS_ASSUME_NONNULL_BEGIN
* Creates an array of "transform results" (a transform result is a field value representing the
* result of applying a transform) for use when applying an FSTTransformMutation locally.
*
+ * @param baseDocument The document prior to applying this mutation batch.
* @param localWriteTime The local time of the transform mutation (used to generate
* FSTServerTimestampValues).
* @return The transform results array.
*/
-- (NSArray<FSTFieldValue *> *)localTransformResultsWithWriteTime:(FSTTimestamp *)localWriteTime {
+- (NSArray<FSTFieldValue *> *)localTransformResultsWithBaseDocument:
+ (FSTMaybeDocument *_Nullable)baseDocument
+ writeTime:(FSTTimestamp *)localWriteTime {
NSMutableArray<FSTFieldValue *> *transformResults = [NSMutableArray array];
for (FSTFieldTransform *fieldTransform in self.fieldTransforms) {
if ([fieldTransform.transform isKindOfClass:[FSTServerTimestampTransform class]]) {
- [transformResults addObject:[FSTServerTimestampValue
- serverTimestampValueWithLocalWriteTime:localWriteTime]];
+ FSTFieldValue *previousValue = nil;
+
+ if ([baseDocument isMemberOfClass:[FSTDocument class]]) {
+ previousValue = [((FSTDocument *)baseDocument) fieldForPath:fieldTransform.path];
+ }
+
+ [transformResults
+ addObject:[FSTServerTimestampValue serverTimestampValueWithLocalWriteTime:localWriteTime
+ previousValue:previousValue]];
} else {
FSTFail(@"Encountered unknown transform: %@", fieldTransform);
}
@@ -551,9 +568,10 @@ NS_ASSUME_NONNULL_BEGIN
stringWithFormat:@"<FSTDeleteMutation key=%@ precondition=%@>", self.key, self.precondition];
}
-- (FSTMaybeDocument *_Nullable)applyTo:(FSTMaybeDocument *_Nullable)maybeDoc
+- (nullable FSTMaybeDocument *)applyTo:(nullable FSTMaybeDocument *)maybeDoc
+ baseDocument:(nullable FSTMaybeDocument *)baseDoc
localWriteTime:(FSTTimestamp *)localWriteTime
- mutationResult:(FSTMutationResult *_Nullable)mutationResult {
+ mutationResult:(nullable FSTMutationResult *)mutationResult {
if (mutationResult) {
FSTAssert(!mutationResult.transformResults,
@"Transform results received by FSTDeleteMutation.");
diff --git a/Firestore/Source/Model/FSTMutationBatch.m b/Firestore/Source/Model/FSTMutationBatch.mm
index 3677908..01adca7 100644
--- a/Firestore/Source/Model/FSTMutationBatch.m
+++ b/Firestore/Source/Model/FSTMutationBatch.mm
@@ -71,6 +71,7 @@ const FSTBatchID kFSTBatchIDUnknown = -1;
mutationBatchResult:(FSTMutationBatchResult *_Nullable)mutationBatchResult {
FSTAssert(!maybeDoc || [maybeDoc.key isEqualToKey:documentKey],
@"applyTo: key %@ doesn't match maybeDoc key %@", documentKey, maybeDoc.key);
+ FSTMaybeDocument *baseDoc = maybeDoc;
if (mutationBatchResult) {
FSTAssert(mutationBatchResult.mutationResults.count == self.mutations.count,
@"Mismatch between mutations length (%lu) and results length (%lu)",
@@ -83,6 +84,7 @@ const FSTBatchID kFSTBatchIDUnknown = -1;
FSTMutationResult *_Nullable mutationResult = mutationBatchResult.mutationResults[i];
if ([mutation.key isEqualToKey:documentKey]) {
maybeDoc = [mutation applyTo:maybeDoc
+ baseDocument:baseDoc
localWriteTime:self.localWriteTime
mutationResult:mutationResult];
}
diff --git a/Firestore/Source/Model/FSTPath.m b/Firestore/Source/Model/FSTPath.mm
index b236107..636c322 100644
--- a/Firestore/Source/Model/FSTPath.m
+++ b/Firestore/Source/Model/FSTPath.mm
@@ -240,7 +240,7 @@ NS_ASSUME_NONNULL_BEGIN
c = *source++;
// TODO(b/37244157): Make this a user-facing exception once we finalize field escaping.
FSTAssert(c != '\0', @"Trailing escape characters not allowed in %@", fieldPath);
- // Fall through
+ // Fall through
default:
// copy into the current segment
diff --git a/Firestore/Source/Public/FIRDocumentChange.h b/Firestore/Source/Public/FIRDocumentChange.h
index 022c81b..4717067 100644
--- a/Firestore/Source/Public/FIRDocumentChange.h
+++ b/Firestore/Source/Public/FIRDocumentChange.h
@@ -18,7 +18,7 @@
NS_ASSUME_NONNULL_BEGIN
-@class FIRDocumentSnapshot;
+@class FIRQueryDocumentSnapshot;
/** An enumeration of document change types. */
typedef NS_ENUM(NSInteger, FIRDocumentChangeType) {
@@ -47,7 +47,7 @@ NS_SWIFT_NAME(DocumentChange)
@property(nonatomic, readonly) FIRDocumentChangeType type;
/** The document affected by this change. */
-@property(nonatomic, strong, readonly) FIRDocumentSnapshot *document;
+@property(nonatomic, strong, readonly) FIRQueryDocumentSnapshot *document;
/**
* The index of the changed document in the result set immediately prior to this FIRDocumentChange
diff --git a/Firestore/Source/Public/FIRDocumentReference.h b/Firestore/Source/Public/FIRDocumentReference.h
index 439e727..7fcc7a8 100644
--- a/Firestore/Source/Public/FIRDocumentReference.h
+++ b/Firestore/Source/Public/FIRDocumentReference.h
@@ -36,8 +36,6 @@ NS_SWIFT_NAME(DocumentListenOptions)
- (instancetype)init;
-@property(nonatomic, assign, readonly) BOOL includeMetadataChanges;
-
/**
* Sets the includeMetadataChanges option which controls whether metadata-only changes (i.e. only
* `FIRDocumentSnapshot.metadata` changed) should trigger snapshot events. Default is NO.
diff --git a/Firestore/Source/Public/FIRDocumentSnapshot.h b/Firestore/Source/Public/FIRDocumentSnapshot.h
index 3e67c25..6e79a7f 100644
--- a/Firestore/Source/Public/FIRDocumentSnapshot.h
+++ b/Firestore/Source/Public/FIRDocumentSnapshot.h
@@ -22,9 +22,61 @@
NS_ASSUME_NONNULL_BEGIN
/**
+ * Controls the return value for server timestamps that have not yet been set to
+ * their final value.
+ */
+typedef NS_ENUM(NSInteger, FIRServerTimestampBehavior) {
+ /**
+ * Return `NSNull` for `FieldValue.serverTimestamp()` fields that have not yet
+ * been set to their final value.
+ */
+ FIRServerTimestampBehaviorNone,
+
+ /**
+ * Return a local estimates for `FieldValue.serverTimestamp()`
+ * fields that have not yet been set to their final value. This estimate will
+ * likely differ from the final value and may cause these pending values to
+ * change once the server result becomes available.
+ */
+ FIRServerTimestampBehaviorEstimate,
+
+ /**
+ * Return the previous value for `FieldValue.serverTimestamp()` fields that
+ * have not yet been set to their final value.
+ */
+ FIRServerTimestampBehaviorPrevious
+} NS_SWIFT_NAME(ServerTimestampBehavior);
+
+/**
+ * Options that configure how data is retrieved from a `DocumentSnapshot`
+ * (e.g. the desired behavior for server timestamps that have not yet been set
+ * to their final value).
+ */
+NS_SWIFT_NAME(SnapshotOptions)
+@interface FIRSnapshotOptions : NSObject
+
+/** */
+- (instancetype)init __attribute__((unavailable("FIRSnapshotOptions cannot be created directly.")));
+
+/**
+ * If set, controls the return value for `FieldValue.serverTimestamp()`
+ * fields that have not yet been set to their final value.
+ *
+ * If omitted, `NSNull` will be returned by default.
+ *
+ * @return The created `FIRSnapshotOptions` object.
+ */
++ (instancetype)serverTimestampBehavior:(FIRServerTimestampBehavior)serverTimestampBehavior;
+
+@end
+
+/**
* A `FIRDocumentSnapshot` contains data read from a document in your Firestore database. The data
* can be extracted with the `data` property or by using subscript syntax to access a specific
* field.
+ *
+ * For a `FIRDocumentSnapshot` that points to a non-existing document, any data access will return
+ * `nil`. You can use the `exists` property to explicitly verify a documents existence.
*/
NS_SWIFT_NAME(DocumentSnapshot)
@interface FIRDocumentSnapshot : NSObject
@@ -46,21 +98,104 @@ NS_SWIFT_NAME(DocumentSnapshot)
@property(nonatomic, strong, readonly) FIRSnapshotMetadata *metadata;
/**
- * Retrieves all fields in the document as an `NSDictionary`.
+ * Retrieves all fields in the document as an `NSDictionary`. Returns `nil` if the document doesn't
+ * exist.
*
- * @return An `NSDictionary` containing all fields in the document.
+ * Server-provided timestamps that have not yet been set to their final value will be returned as
+ * `NSNull`. You can use `dataWithOptions()` to configure this behavior.
+ *
+ * @return An `NSDictionary` containing all fields in the document or `nil` if the document doesn't
+ * exist.
*/
-- (NSDictionary<NSString *, id> *)data;
+- (nullable NSDictionary<NSString *, id> *)data;
+
+/**
+ * Retrieves all fields in the document as a `Dictionary`. Returns `nil` if the document doesn't
+ * exist.
+ *
+ * @param options `SnapshotOptions` to configure how data is returned from the snapshot (e.g. the
+ * desired behavior for server timestamps that have not yet been set to their final value).
+ * @return A `Dictionary` containing all fields in the document or `nil` if the document doesn't
+ * exist.
+ */
+- (nullable NSDictionary<NSString *, id> *)dataWithOptions:(FIRSnapshotOptions *)options;
+
+/**
+ * Retrieves a specific field from the document. Returns `nil` if the document or the field doesn't
+ * exist.
+ *
+ * The timestamps that have not yet been set to their final value will be returned as `NSNull`. The
+ * can use `get(_:options:)` to configure this behavior.
+ *
+ * @param field The field to retrieve.
+ * @return The value contained in the field or `nil` if the document or field doesn't exist.
+ */
+- (nullable id)valueForField:(id)field NS_SWIFT_NAME(get(_:));
+
+/**
+ * Retrieves a specific field from the document. Returns `nil` if the document or the field doesn't
+ * exist.
+ *
+ * The timestamps that have not yet been set to their final value will be returned as `NSNull`. The
+ * can use `get(_:options:)` to configure this behavior.
+ *
+ * @param field The field to retrieve.
+ * @param options `SnapshotOptions` to configure how data is returned from the snapshot (e.g. the
+ * desired behavior for server timestamps that have not yet been set to their final value).
+ * @return The value contained in the field or `nil` if the document or field doesn't exist.
+ */
+// clang-format off
+- (nullable id)valueForField:(id)field
+ options:(FIRSnapshotOptions *)options
+ NS_SWIFT_NAME(get(_:options:));
+// clang-format on
/**
* Retrieves a specific field from the document.
*
* @param key The field to retrieve.
*
- * @return The value contained in the field or `nil` if the field doesn't exist.
+ * @return The value contained in the field or `nil` if the document or field doesn't exist.
*/
- (nullable id)objectForKeyedSubscript:(id)key;
@end
+/**
+ * A `FIRQueryDocumentSnapshot` contains data read from a document in your Firestore database as
+ * part of a query. The document is guaranteed to exist and its data can be extracted with the
+ * `data` property or by using subscript syntax to access a specific field.
+ *
+ * A `FIRQueryDocumentSnapshot` offers the same API surface as a `FIRDocumentSnapshot`. As
+ * deleted documents are not returned from queries, its `exists` property will always be true and
+ * `data:` will never return `nil`.
+ */
+NS_SWIFT_NAME(QueryDocumentSnapshot)
+@interface FIRQueryDocumentSnapshot : FIRDocumentSnapshot
+
+/** */
+- (instancetype)init
+ __attribute__((unavailable("FIRQueryDocumentSnapshot cannot be created directly.")));
+
+/**
+ * Retrieves all fields in the document as an `NSDictionary`.
+ *
+ * Server-provided timestamps that have not yet been set to their final value will be returned as
+ * `NSNull`. You can use `dataWithOptions()` to configure this behavior.
+ *
+ * @return An `NSDictionary` containing all fields in the document.
+ */
+- (NSDictionary<NSString *, id> *)data;
+
+/**
+ * Retrieves all fields in the document as a `Dictionary`.
+ *
+ * @param options `SnapshotOptions` to configure how data is returned from the snapshot (e.g. the
+ * desired behavior for server timestamps that have not yet been set to their final value).
+ * @return A `Dictionary` containing all fields in the document.
+ */
+- (NSDictionary<NSString *, id> *)dataWithOptions:(FIRSnapshotOptions *)options;
+
+@end
+
NS_ASSUME_NONNULL_END
diff --git a/Firestore/Source/Public/FIRFirestore.h b/Firestore/Source/Public/FIRFirestore.h
index 91a96a5..4c85aba 100644
--- a/Firestore/Source/Public/FIRFirestore.h
+++ b/Firestore/Source/Public/FIRFirestore.h
@@ -139,6 +139,23 @@ NS_SWIFT_NAME(Firestore)
+ (void)enableLogging:(BOOL)logging
DEPRECATED_MSG_ATTRIBUTE("Use FIRSetLoggerLevel(FIRLoggerLevelDebug) to enable logging");
+#pragma mark - Network
+
+/**
+ * Re-enables usage of the network by this Firestore instance after a prior call to
+ * `disableNetworkWithCompletion`. Completion block, if provided, will be called once network uasge
+ * has been enabled.
+ */
+- (void)enableNetworkWithCompletion:(nullable void (^)(NSError *_Nullable error))completion;
+
+/**
+ * Disables usage of the network by this Firestore instance. It can be re-enabled by via
+ * `enableNetworkWithCompletion`. While the network is disabled, any snapshot listeners or get calls
+ * will return results from cache and any write operations will be queued until the network is
+ * restored. The completion block, if provided, will be called once network usage has been disabled.
+ */
+- (void)disableNetworkWithCompletion:(nullable void (^)(NSError *_Nullable error))completion;
+
@end
NS_ASSUME_NONNULL_END
diff --git a/Firestore/Source/Public/FIRQuery.h b/Firestore/Source/Public/FIRQuery.h
index 0f3aeed..ff15ac6 100644
--- a/Firestore/Source/Public/FIRQuery.h
+++ b/Firestore/Source/Public/FIRQuery.h
@@ -256,6 +256,19 @@ NS_SWIFT_NAME(Query)
isGreaterThanOrEqualTo:(id)value NS_SWIFT_NAME(whereField(_:isGreaterThanOrEqualTo:));
// clang-format on
+/**
+ * Creates and returns a new `FIRQuery` with the additional filter that documents must
+ * satisfy the specified predicate.
+ *
+ * @param predicate The predicate the document must satisfy. Can be either comparison
+ * or compound of comparison. In particular, block-based predicate is not supported.
+ *
+ * @return The created `FIRQuery`.
+ */
+// clang-format off
+- (FIRQuery *)queryFilteredUsingPredicate:(NSPredicate *)predicate NS_SWIFT_NAME(filter(using:));
+// clang-format on
+
#pragma mark - Sorting Data
/**
* Creates and returns a new `FIRQuery` that's additionally sorted by the specified field.
diff --git a/Firestore/Source/Public/FIRQuerySnapshot.h b/Firestore/Source/Public/FIRQuerySnapshot.h
index c49a07a..1266832 100644
--- a/Firestore/Source/Public/FIRQuerySnapshot.h
+++ b/Firestore/Source/Public/FIRQuerySnapshot.h
@@ -19,8 +19,8 @@
NS_ASSUME_NONNULL_BEGIN
@class FIRDocumentChange;
-@class FIRDocumentSnapshot;
@class FIRQuery;
+@class FIRQueryDocumentSnapshot;
@class FIRSnapshotMetadata;
/**
@@ -50,7 +50,7 @@ NS_SWIFT_NAME(QuerySnapshot)
@property(nonatomic, readonly) NSInteger count;
/** An Array of the `FIRDocumentSnapshots` that make up this document set. */
-@property(nonatomic, strong, readonly) NSArray<FIRDocumentSnapshot *> *documents;
+@property(nonatomic, strong, readonly) NSArray<FIRQueryDocumentSnapshot *> *documents;
/**
* An array of the documents that changed since the last snapshot. If this is the first snapshot,
diff --git a/Firestore/Source/Public/FIRWriteBatch.h b/Firestore/Source/Public/FIRWriteBatch.h
index 5f0034c..8ff1bec 100644
--- a/Firestore/Source/Public/FIRWriteBatch.h
+++ b/Firestore/Source/Public/FIRWriteBatch.h
@@ -94,6 +94,11 @@ NS_SWIFT_NAME(WriteBatch)
/**
* Commits all of the writes in this write batch as a single atomic unit.
+ */
+- (void)commit;
+
+/**
+ * Commits all of the writes in this write batch as a single atomic unit.
*
* @param completion A block to be called once all of the writes in the batch have been
* successfully written to the backend as an atomic unit. This block will only execute
@@ -101,7 +106,7 @@ NS_SWIFT_NAME(WriteBatch)
* completion handler will not be called when the device is offline, though local
* changes will be visible immediately.
*/
-- (void)commitWithCompletion:(void (^)(NSError *_Nullable error))completion;
+- (void)commitWithCompletion:(nullable void (^)(NSError *_Nullable error))completion;
@end
diff --git a/Firestore/Source/Remote/FSTBufferedWriter.m b/Firestore/Source/Remote/FSTBufferedWriter.mm
index 47dbb21..47dbb21 100644
--- a/Firestore/Source/Remote/FSTBufferedWriter.m
+++ b/Firestore/Source/Remote/FSTBufferedWriter.mm
diff --git a/Firestore/Source/Remote/FSTDatastore.m b/Firestore/Source/Remote/FSTDatastore.mm
index 02d868c..02d868c 100644
--- a/Firestore/Source/Remote/FSTDatastore.m
+++ b/Firestore/Source/Remote/FSTDatastore.mm
diff --git a/Firestore/Source/Remote/FSTExistenceFilter.m b/Firestore/Source/Remote/FSTExistenceFilter.mm
index d5ec7b3..d5ec7b3 100644
--- a/Firestore/Source/Remote/FSTExistenceFilter.m
+++ b/Firestore/Source/Remote/FSTExistenceFilter.mm
diff --git a/Firestore/Source/Remote/FSTRemoteEvent.m b/Firestore/Source/Remote/FSTRemoteEvent.mm
index a97eb86..88999e4 100644
--- a/Firestore/Source/Remote/FSTRemoteEvent.m
+++ b/Firestore/Source/Remote/FSTRemoteEvent.mm
@@ -278,6 +278,14 @@ initWithSnapshotVersion:(FSTSnapshotVersion *)snapshotVersion
return self;
}
+- (NSDictionary<FSTBoxedTargetID *, FSTTargetChange *> *)targetChanges {
+ return static_cast<NSDictionary<FSTBoxedTargetID *, FSTTargetChange *> *>(_targetChanges);
+}
+
+- (NSDictionary<FSTDocumentKey *, FSTMaybeDocument *> *)documentUpdates {
+ return static_cast<NSDictionary<FSTDocumentKey *, FSTMaybeDocument *> *>(_documentUpdates);
+}
+
/** Adds a document update to this remote event */
- (void)addDocumentUpdate:(FSTMaybeDocument *)document {
_documentUpdates[document.key] = document;
@@ -352,6 +360,10 @@ initWithSnapshotVersion:(FSTSnapshotVersion *)snapshotVersion
return self;
}
+- (NSDictionary<FSTBoxedTargetID *, FSTExistenceFilter *> *)existenceFilters {
+ return static_cast<NSDictionary<FSTBoxedTargetID *, FSTExistenceFilter *> *>(_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 313ddb7..18331ff 100644
--- a/Firestore/Source/Remote/FSTRemoteStore.h
+++ b/Firestore/Source/Remote/FSTRemoteStore.h
@@ -83,7 +83,7 @@ NS_ASSUME_NONNULL_BEGIN
@protocol FSTOnlineStateDelegate <NSObject>
/** Called whenever the online state of the watch stream changes */
-- (void)watchStreamDidChangeOnlineState:(FSTOnlineState)onlineState;
+- (void)applyChangedOnlineState:(FSTOnlineState)onlineState;
@end
diff --git a/Firestore/Source/Remote/FSTRemoteStore.m b/Firestore/Source/Remote/FSTRemoteStore.mm
index 063e487..123df49 100644
--- a/Firestore/Source/Remote/FSTRemoteStore.m
+++ b/Firestore/Source/Remote/FSTRemoteStore.mm
@@ -16,6 +16,8 @@
#import "Firestore/Source/Remote/FSTRemoteStore.h"
+#include <inttypes.h>
+
#import "Firestore/Source/Core/FSTQuery.h"
#import "Firestore/Source/Core/FSTSnapshotVersion.h"
#import "Firestore/Source/Core/FSTTransaction.h"
@@ -160,27 +162,38 @@ static const int kOnlineAttemptsBeforeFailure = 2;
[self enableNetwork];
}
-- (void)setOnlineStateToHealthy {
- self.shouldWarnOffline = NO;
- [self updateAndNotifyAboutOnlineState:FSTOnlineStateHealthy];
-}
-
-- (void)setOnlineStateToUnknown {
- // The state is set to unknown when a healthy stream is closed (e.g. due to a token timeout) or
- // when we have no active listens and therefore there's no need to start the stream. Assuming
- // there is (possibly in the future) an active listen, then we will eventually move to state
- // Online or Failed, but we always want to make at least kOnlineAttemptsBeforeFailure attempts
- // before failing, so we reset the count here.
- self.watchStreamFailures = 0;
- [self updateAndNotifyAboutOnlineState:FSTOnlineStateUnknown];
+/**
+ * Updates our OnlineState to the new state, updating local state and notifying the
+ * onlineStateHandler as appropriate.
+ */
+- (void)updateOnlineState:(FSTOnlineState)newState {
+ // Update and broadcast the new state.
+ if (newState != self.watchStreamOnlineState) {
+ if (newState == FSTOnlineStateHealthy) {
+ // We've connected to watch at least once. Don't warn the developer about being offline going
+ // forward.
+ self.shouldWarnOffline = NO;
+ } else if (newState == FSTOnlineStateUnknown) {
+ // The state is set to unknown when a healthy stream is closed (e.g. due to a token timeout)
+ // or when we have no active listens and therefore there's no need to start the stream.
+ // Assuming there is (possibly in the future) an active listen, then we will eventually move
+ // to state Online or Failed, but we always want to make at least kOnlineAttemptsBeforeFailure
+ // attempts before failing, so we reset the count here.
+ self.watchStreamFailures = 0;
+ }
+ self.watchStreamOnlineState = newState;
+ [self.onlineStateDelegate applyChangedOnlineState:newState];
+ }
}
+/**
+ * Updates our FSTOnlineState as appropriate after the watch stream reports a failure. The first
+ * failure moves us to the 'Unknown' state. We then may allow multiple failures (based on
+ * kOnlineAttemptsBeforeFailure) before we actually transition to FSTOnlineStateFailed.
+ */
- (void)updateOnlineStateAfterFailure {
- // The first failure after we are successfully connected moves us to the 'Unknown' state. We
- // then may make multiple attempts (based on kOnlineAttemptsBeforeFailure) before we actually
- // report failure.
if (self.watchStreamOnlineState == FSTOnlineStateHealthy) {
- [self setOnlineStateToUnknown];
+ [self updateOnlineState:FSTOnlineStateUnknown];
} else {
self.watchStreamFailures++;
if (self.watchStreamFailures >= kOnlineAttemptsBeforeFailure) {
@@ -188,19 +201,11 @@ static const int kOnlineAttemptsBeforeFailure = 2;
FSTWarn(@"Could not reach Firestore backend.");
self.shouldWarnOffline = NO;
}
- [self updateAndNotifyAboutOnlineState:FSTOnlineStateFailed];
+ [self updateOnlineState:FSTOnlineStateFailed];
}
}
}
-- (void)updateAndNotifyAboutOnlineState:(FSTOnlineState)watchStreamOnlineState {
- BOOL didChange = (watchStreamOnlineState != self.watchStreamOnlineState);
- self.watchStreamOnlineState = watchStreamOnlineState;
- if (didChange) {
- [self.onlineStateDelegate watchStreamDidChangeOnlineState:watchStreamOnlineState];
- }
-}
-
#pragma mark Online/Offline state
- (BOOL)isNetworkEnabled {
@@ -210,8 +215,9 @@ static const int kOnlineAttemptsBeforeFailure = 2;
}
- (void)enableNetwork {
- FSTAssert(self.watchStream == nil, @"enableNetwork: called with non-null watchStream.");
- FSTAssert(self.writeStream == nil, @"enableNetwork: called with non-null writeStream.");
+ if ([self isNetworkEnabled]) {
+ return;
+ }
// Create new streams (but note they're not started yet).
self.watchStream = [self.datastore createWatchStream];
@@ -227,47 +233,51 @@ static const int kOnlineAttemptsBeforeFailure = 2;
[self fillWritePipeline]; // This may start the writeStream.
// We move back to the unknown state because we might not want to re-open the stream
- [self setOnlineStateToUnknown];
+ [self updateOnlineState:FSTOnlineStateUnknown];
}
- (void)disableNetwork {
- [self updateAndNotifyAboutOnlineState:FSTOnlineStateFailed];
+ [self disableNetworkInternal];
+ // Set the FSTOnlineState to failed so get()'s return from cache, etc.
+ [self updateOnlineState:FSTOnlineStateFailed];
+}
- // NOTE: We're guaranteed not to get any further events from these streams (not even a close
- // event).
- [self.watchStream stop];
- [self.writeStream stop];
+/** Disables the network, setting the FSTOnlineState to the specified targetOnlineState. */
+- (void)disableNetworkInternal {
+ if ([self isNetworkEnabled]) {
+ // NOTE: We're guaranteed not to get any further events from these streams (not even a close
+ // event).
+ [self.watchStream stop];
+ [self.writeStream stop];
- [self cleanUpWatchStreamState];
- [self cleanUpWriteStreamState];
+ [self cleanUpWatchStreamState];
+ [self cleanUpWriteStreamState];
- self.writeStream = nil;
- self.watchStream = nil;
+ self.writeStream = nil;
+ self.watchStream = nil;
+ }
}
#pragma mark Shutdown
- (void)shutdown {
FSTLog(@"FSTRemoteStore %p shutting down", (__bridge void *)self);
-
- // Don't fire initial listener callbacks on shutdown.
- self.onlineStateDelegate = nil;
-
- // For now, all shutdown logic is handled by disableNetwork(). We might expand on this in the
- // future.
- if ([self isNetworkEnabled]) {
- [self disableNetwork];
- }
+ [self disableNetworkInternal];
+ // Set the FSTOnlineState to Unknown (rather than Failed) to avoid potentially triggering
+ // spurious listener events with cached data, etc.
+ [self updateOnlineState:FSTOnlineStateUnknown];
}
- (void)userDidChange:(FSTUser *)user {
FSTLog(@"FSTRemoteStore %p changing users: %@", (__bridge void *)self, user);
-
- // 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
- // (since mutations are per-user).
- [self disableNetwork];
- [self enableNetwork];
+ 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
+ // (since mutations are per-user).
+ [self disableNetworkInternal];
+ [self updateOnlineState:FSTOnlineStateUnknown];
+ [self enableNetwork];
+ }
}
#pragma mark Watch Stream
@@ -348,7 +358,7 @@ static const int kOnlineAttemptsBeforeFailure = 2;
- (void)watchStreamDidChange:(FSTWatchChange *)change
snapshotVersion:(FSTSnapshotVersion *)snapshotVersion {
// Mark the connection as healthy because we got a message from the server.
- [self setOnlineStateToHealthy];
+ [self updateOnlineState:FSTOnlineStateHealthy];
FSTWatchTargetChange *watchTargetChange =
[change isKindOfClass:[FSTWatchTargetChange class]] ? (FSTWatchTargetChange *)change : nil;
@@ -391,7 +401,7 @@ static const int kOnlineAttemptsBeforeFailure = 2;
} else {
// We don't need to restart the watch stream because there are no active targets. The online
// state is set to unknown because there is no active attempt at establishing a connection.
- [self setOnlineStateToUnknown];
+ [self updateOnlineState:FSTOnlineStateUnknown];
}
}
@@ -460,8 +470,10 @@ static const int kOnlineAttemptsBeforeFailure = 2;
[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
@@ -475,6 +487,7 @@ static const int kOnlineAttemptsBeforeFailure = 2;
FSTQueryData *requestQueryData =
[[FSTQueryData alloc] initWithQuery:query
targetID:targetID
+ listenSequenceNumber:queryData.sequenceNumber
purpose:FSTQueryPurposeExistenceFilterMismatch];
[self sendWatchRequestWithQueryData:requestQueryData];
}
@@ -532,6 +545,8 @@ static const int kOnlineAttemptsBeforeFailure = 2;
- (void)cleanUpWriteStreamState {
self.lastBatchSeen = kFSTBatchIDUnknown;
+ FSTLog(@"Stopping write stream with %lu pending writes",
+ (unsigned long)[self.pendingWrites count]);
[self.pendingWrites removeAllObjects];
}
diff --git a/Firestore/Source/Remote/FSTSerializerBeta.m b/Firestore/Source/Remote/FSTSerializerBeta.mm
index 04785c2..cf200ca 100644
--- a/Firestore/Source/Remote/FSTSerializerBeta.m
+++ b/Firestore/Source/Remote/FSTSerializerBeta.mm
@@ -16,6 +16,8 @@
#import "Firestore/Source/Remote/FSTSerializerBeta.h"
+#include <inttypes.h>
+
#import <GRPCClient/GRPCCall.h>
#import "Firestore/Protos/objc/google/firestore/v1beta1/Common.pbobjc.h"
@@ -161,41 +163,41 @@ NS_ASSUME_NONNULL_BEGIN
#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;
@@ -438,8 +440,8 @@ NS_ASSUME_NONNULL_BEGIN
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.m b/Firestore/Source/Remote/FSTStream.mm
index 2c039be..dc7d01e 100644
--- a/Firestore/Source/Remote/FSTStream.m
+++ b/Firestore/Source/Remote/FSTStream.mm
@@ -343,9 +343,6 @@ static const NSTimeInterval kIdleTimeout = 60.0;
- (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];
@@ -542,7 +539,7 @@ static const NSTimeInterval kIdleTimeout = 60.0;
FSTWeakify(self);
[self.workerDispatchQueue dispatchAsync:^{
FSTStrongify(self);
- if (!self || ![self isStarted]) {
+ if (![self isStarted]) {
FSTLog(@"%@ Ignoring stream message from inactive stream.", NSStringFromClass([self class]));
}
diff --git a/Firestore/Source/Remote/FSTWatchChange.m b/Firestore/Source/Remote/FSTWatchChange.mm
index 926d027..926d027 100644
--- a/Firestore/Source/Remote/FSTWatchChange.m
+++ b/Firestore/Source/Remote/FSTWatchChange.mm
diff --git a/Firestore/Source/Util/FSTAsyncQueryListener.m b/Firestore/Source/Util/FSTAsyncQueryListener.mm
index d98e2dd..d98e2dd 100644
--- a/Firestore/Source/Util/FSTAsyncQueryListener.m
+++ b/Firestore/Source/Util/FSTAsyncQueryListener.mm
diff --git a/Firestore/Source/Util/FSTComparison.h b/Firestore/Source/Util/FSTComparison.h
deleted file mode 100644
index e6e57e6..0000000
--- 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 <Foundation/Foundation.h>
-
-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 9c5c3eb..0000000
--- 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.m b/Firestore/Source/Util/FSTDispatchQueue.mm
index 6ce5d74..6ce5d74 100644
--- a/Firestore/Source/Util/FSTDispatchQueue.m
+++ b/Firestore/Source/Util/FSTDispatchQueue.mm
diff --git a/Firestore/Source/Util/FSTLogger.h b/Firestore/Source/Util/FSTLogger.h
index 699570a..c4e2b85 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
index f0081e0..f0081e0 100644
--- a/Firestore/Source/Util/FSTLogger.m
+++ b/Firestore/Source/Util/FSTLogger.mm
diff --git a/Firestore/Source/Util/FSTUsageValidation.h b/Firestore/Source/Util/FSTUsageValidation.h
index 34a3d64..a80dafa 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
index 82128f4..82128f4 100644
--- a/Firestore/Source/Util/FSTUsageValidation.m
+++ b/Firestore/Source/Util/FSTUsageValidation.mm
diff --git a/Firestore/core/CMakeLists.txt b/Firestore/core/CMakeLists.txt
index c49b6db..2fc88c6 100644
--- a/Firestore/core/CMakeLists.txt
+++ b/Firestore/core/CMakeLists.txt
@@ -12,5 +12,16 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+add_subdirectory(src/firebase/firestore)
+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)
+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/geo_point.h b/Firestore/core/include/firebase/firestore/geo_point.h
new file mode 100644
index 0000000..cc366be
--- /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 0000000..3f5522c
--- /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/base/port.h b/Firestore/core/src/firebase/firestore/base/port.h
deleted file mode 100644
index 37d1041..0000000
--- 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 0000000..a62985c
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/core/CMakeLists.txt
@@ -0,0 +1,20 @@
+# 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
+ target_id_generator.cc
+ target_id_generator.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 0000000..6d23d64
--- /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<TargetId>(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 0000000..7d30cf9
--- /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 0000000..65c2b8c
--- /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 <stdint.h>
+
+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 0000000..fb01023
--- /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 <math.h>
+
+#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 0000000..e8a95cd
--- /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 0000000..48e7553
--- /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 0000000..d0210a8
--- /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 <algorithm>
+#include <array>
+#include <cassert>
+#include <functional>
+#include <memory>
+#include <utility>
+
+#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 <typename T, ArraySortedMapBase::size_type fixed_size>
+class FixedArray {
+ public:
+ using size_type = ArraySortedMapBase::size_type;
+ using array_type = std::array<T, fixed_size>;
+ using iterator = typename array_type::iterator;
+ using const_iterator = typename array_type::const_iterator;
+
+ FixedArray() {
+ }
+
+ template <typename SourceIterator>
+ 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 <typename SourceIterator>
+ void append(SourceIterator src_begin, SourceIterator src_end) {
+ size_type appending = static_cast<size_type>(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 <typename K, typename V, typename C = std::less<K>>
+class ArraySortedMap : public impl::ArraySortedMapBase {
+ public:
+ using key_comparator_type = KeyComparator<K, V, C>;
+
+ /**
+ * The type of the entries stored in the map.
+ */
+ using value_type = std::pair<K, V>;
+
+ /**
+ * The type of the fixed-size array containing entries of value_type.
+ */
+ using array_type = impl::FixedArray<value_type, kFixedSize>;
+ using const_iterator = typename array_type::const_iterator;
+
+ using array_pointer = std::shared_ptr<const array_type>;
+
+ /**
+ * 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<value_type> entries,
+ const C& comparator = C())
+ : array_(std::make_shared<array_type>(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<array_type>(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<array_type>(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<const array_type>();
+ 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 0000000..2130b5b
--- /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 <functional>
+#include <utility>
+
+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 <typename K, typename V, typename C = std::less<K>>
+struct KeyComparator {
+ typedef std::pair<K, V> 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 0000000..aee0d86
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/model/CMakeLists.txt
@@ -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.
+
+cc_library(
+ firebase_firestore_model
+ SOURCES
+ database_id.cc
+ database_id.h
+ field_value.cc
+ field_value.h
+ timestamp.cc
+ timestamp.h
+ DEPENDS
+ absl_strings
+ firebase_firestore_util
+ firebase_firestore_types
+)
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 0000000..af12d30
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/model/database_id.cc
@@ -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.
+ */
+
+#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::kDefaultDatabaseId;
+
+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());
+}
+
+bool DatabaseId::IsDefaultDatabase() {
+ return database_id_ == kDefaultDatabaseId;
+}
+
+} // 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 0000000..48c547c
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/model/database_id.h
@@ -0,0 +1,92 @@
+/*
+ * 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 <string>
+
+#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* kDefaultDatabaseId = "(default)";
+
+ /**
+ * 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();
+
+ friend bool operator<(const DatabaseId& lhs, const DatabaseId& rhs);
+
+ private:
+ const std::string project_id_;
+ const 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/field_value.cc b/Firestore/core/src/firebase/firestore/model/field_value.cc
new file mode 100644
index 0000000..570226e
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/model/field_value.cc
@@ -0,0 +1,388 @@
+/*
+ * 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 <math.h>
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#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::Long:
+ case Type::Double:
+ return rhs == Type::Long || 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::Long:
+ 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<uint8_t> tmp = value.blob_value_;
+ std::swap(blob_value_, tmp);
+ break;
+ }
+ case Type::GeoPoint:
+ geo_point_value_ = value.geo_point_value_;
+ break;
+ case Type::Array: {
+ // copy-and-swap
+ std::vector<FieldValue> tmp = value.array_value_;
+ std::swap(array_value_, tmp);
+ break;
+ }
+ case Type::Object: {
+ // copy-and-swap
+ std::map<const std::string, const FieldValue> 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::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::Long);
+ 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<uint8_t> copy(source, source + size);
+ std::swap(result.blob_value_, copy);
+ 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<FieldValue>& value) {
+ std::vector<FieldValue> copy(value);
+ return ArrayValue(std::move(copy));
+}
+
+FieldValue FieldValue::ArrayValue(std::vector<FieldValue>&& value) {
+ FieldValue result;
+ result.SwitchTo(Type::Array);
+ std::swap(result.array_value_, value);
+ return result;
+}
+
+FieldValue FieldValue::ObjectValue(
+ const std::map<const std::string, const FieldValue>& value) {
+ std::map<const std::string, const FieldValue> copy(value);
+ return ObjectValue(std::move(copy));
+}
+
+FieldValue FieldValue::ObjectValue(
+ std::map<const std::string, const FieldValue>&& 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<bool>()(lhs.boolean_value_, rhs.boolean_value_);
+ case Type::Long:
+ if (rhs.type() == Type::Long) {
+ return Comparator<int64_t>()(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<double>()(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::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::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 (&timestamp_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<uint8_t>();
+ break;
+ case Type::GeoPoint:
+ new (&geo_point_value_) GeoPoint();
+ break;
+ case Type::Array:
+ new (&array_value_) std::vector<FieldValue>();
+ break;
+ case Type::Object:
+ new (&object_value_) std::map<const std::string, const FieldValue>();
+ 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 0000000..4cd0b3d
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/model/field_value.h
@@ -0,0 +1,169 @@
+/*
+ * 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 <stdint.h>
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "Firestore/core/include/firebase/firestore/geo_point.h"
+#include "Firestore/core/src/firebase/firestore/model/timestamp.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_;
+};
+
+/**
+ * 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
+ Long, // 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() : tag_(Type::Null) {
+ }
+
+ // 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_;
+ }
+
+ /** 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();
+ static FieldValue GeoPointValue(const GeoPoint& value);
+ static FieldValue ArrayValue(const std::vector<FieldValue>& value);
+ static FieldValue ArrayValue(std::vector<FieldValue>&& value);
+ static FieldValue ObjectValue(
+ const std::map<const std::string, const FieldValue>& value);
+ static FieldValue ObjectValue(
+ std::map<const std::string, const FieldValue>&& 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_;
+ 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<uint8_t> blob_value_;
+ GeoPoint geo_point_value_;
+ std::vector<FieldValue> array_value_;
+ std::map<const std::string, const FieldValue> 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/timestamp.cc b/Firestore/core/src/firebase/firestore/model/timestamp.cc
new file mode 100644
index 0000000..b3d1597
--- /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 <time.h>
+
+#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 0000000..dd0349c
--- /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 <stdint.h>
+
+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 0000000..43320ce
--- /dev/null
+++ b/Firestore/core/src/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_library(
+ firebase_firestore_remote
+ SOURCES
+ datastore.h
+ datastore.cc
+ DEPENDS
+ grpc::grpc
+)
diff --git a/Firestore/core/src/firebase/firestore/remote/datastore.cc b/Firestore/core/src/firebase/firestore/remote/datastore.cc
new file mode 100644
index 0000000..f8a8988
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/remote/datastore.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/remote/datastore.h"
+
+namespace firebase {
+namespace firestore {
+namespace remote {
+
+Datastore::Datastore() {
+}
+
+Datastore::~Datastore() {
+}
+
+} // 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 0000000..807b75f
--- /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/util/CMakeLists.txt b/Firestore/core/src/firebase/firestore/util/CMakeLists.txt
index ce81363..3e32111 100644
--- a/Firestore/core/src/firebase/firestore/util/CMakeLists.txt
+++ b/Firestore/core/src/firebase/firestore/util/CMakeLists.txt
@@ -12,8 +12,116 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-add_library(
+# firebase_firestore_util is the interface of this module. The rest of the
+# libraries in here are an implementation detail of making this a
+# mutli-platform build.
+
+include(CheckSymbolExists)
+include(CheckIncludeFiles)
+
+cc_library(
+ firebase_firestore_util_base
+ SOURCES
+ string_printf.cc
+ string_printf.h
+ DEPENDS
+ absl_base
+)
+
+## assert and log
+
+cc_library(
+ firebase_firestore_util_stdio
+ SOURCES
+ assert_stdio.cc
+ log_stdio.cc
+ DEPENDS
+ firebase_firestore_util_base
+ absl_base
+ EXCLUDE_FROM_ALL
+)
+
+cc_library(
+ firebase_firestore_util_apple
+ SOURCES
+ assert_apple.mm
+ log_apple.mm
+ string_apple.h
+ DEPENDS
+ FirebaseCore
+ 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)
+ list(APPEND UTIL_DEPENDS firebase_firestore_util_apple)
+else()
+ list(APPEND UTIL_DEPENDS firebase_firestore_util_stdio)
+endif()
+
+
+## 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
- autoid.cc
- secure_random_arc4random.cc
+ 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_apple.mm b/Firestore/core/src/firebase/firestore/util/assert_apple.mm
new file mode 100644
index 0000000..83b76e1
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/util/assert_apple.mm
@@ -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/util/firebase_assert.h"
+
+#import <Foundation/Foundation.h>
+
+#include <string.h>
+
+#include "Firestore/core/src/firebase/firestore/util/string_apple.h"
+
+namespace firebase {
+namespace firestore {
+namespace util {
+
+void FailAssert(const char* file,
+ const char* func,
+ const int line,
+ const char* format,
+ ...) {
+ va_list args;
+ va_start(args, format);
+ NSString* description =
+ [[NSString alloc] initWithFormat:WrapNSStringNoCopy(format)
+ arguments:args];
+ va_end(args);
+ [[NSAssertionHandler currentHandler]
+ handleFailureInFunction:WrapNSStringNoCopy(func)
+ file:WrapNSStringNoCopy(file)
+ lineNumber:line
+ description:@"FIRESTORE INTERNAL ASSERTION FAILED: %@",
+ description];
+ abort();
+}
+
+} // namespace util
+} // namespace firestore
+} // namespace firebase
diff --git a/Firestore/core/src/firebase/firestore/util/assert_stdio.cc b/Firestore/core/src/firebase/firestore/util/assert_stdio.cc
new file mode 100644
index 0000000..1d2e333
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/util/assert_stdio.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 <stdarg.h>
+
+#include <stdexcept>
+#include <string>
+
+#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 {
+namespace util {
+
+void FailAssert(const char* file,
+ const char* func,
+ const int line,
+ const char* format,
+ ...) {
+ std::string message;
+ StringAppendF(&message, "ASSERT: %s(%d) %s: ", file, line, func);
+
+ va_list args;
+ va_start(args, format);
+ StringAppendV(&message, format, args);
+ va_end(args);
+
+#if ABSL_HAVE_EXCEPTIONS
+ throw std::logic_error(message);
+
+#else
+ fprintf(stderr, "%s\n", message.c_str());
+ std::terminate();
+#endif
+}
+
+} // namespace util
+} // namespace firestore
+} // namespace firebase
diff --git a/Firestore/Port/bits.cc b/Firestore/core/src/firebase/firestore/util/bits.cc
index 3e61223..0bfe4c3 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 <assert.h>
+#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
index d212bf8..185273f 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 0000000..4bef843
--- /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 <math.h>
+
+#include <limits>
+
+namespace firebase {
+namespace firestore {
+namespace util {
+
+bool Comparator<absl::string_view>::operator()(
+ const absl::string_view& left, const absl::string_view& right) const {
+ // TODO(wilhuff): truncation aware comparison
+ return left < right;
+}
+
+bool Comparator<double>::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<double>(std::numeric_limits<int64_t>::min());
+
+static constexpr double INT64_MAX_VALUE_AS_DOUBLE =
+ static_cast<double>(std::numeric_limits<int64_t>::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<int64_t>(double_value);
+ ComparisonResult cmp = Compare<int64_t>(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<double>(int64_value);
+ return Compare<double>(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<size_t>(bits) ^ static_cast<size_t>(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 0000000..6fd1e2b
--- /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 <Foundation/Foundation.h>
+#endif
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <functional>
+#include <string>
+#include <vector>
+
+#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<ComparisonResult>(-static_cast<int>(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 <typename T>
+struct Comparator {
+ // By default comparison is not defined
+};
+
+/** Compares two strings. */
+template <>
+struct Comparator<absl::string_view> {
+ bool operator()(const absl::string_view& left,
+ const absl::string_view& right) const;
+};
+
+/** Compares two bools: false < true. */
+template <>
+struct Comparator<bool> : public std::less<bool> {};
+
+/** Compares two int32_t. */
+template <>
+struct Comparator<int32_t> : public std::less<int32_t> {};
+
+/** Compares two int64_t. */
+template <>
+struct Comparator<int64_t> : public std::less<int64_t> {};
+
+/** Compares two doubles (using Firestore semantics for NaN). */
+template <>
+struct Comparator<double> {
+ bool operator()(double left, double right) const;
+};
+
+/** Compare two byte sequences. */
+// TODO(wilhuff): perhaps absl::Span<uint8_t> would be better?
+template <>
+struct Comparator<std::vector<uint8_t>>
+ : public std::less<std::vector<uint8_t>> {};
+
+/**
+ * Perform a three-way comparison between the left and right values using
+ * the appropriate Comparator for the values based on their type.
+ */
+template <typename T>
+ComparisonResult Compare(const T& left, const T& right) {
+ Comparator<T> 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<int>(lhs) == static_cast<int>(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 <typename T>
+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<NSComparisonResult>(Compare<T>(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 0000000..e7a0c03
--- /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/firebase_assert.h b/Firestore/core/src/firebase/firestore/util/firebase_assert.h
new file mode 100644
index 0000000..cff550a
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/util/firebase_assert.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.
+ */
+
+// To avoid naming-collision, this header is called firebase_assert.h instead
+// of assert.h.
+
+#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_FIREBASE_ASSERT_H_
+#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_FIREBASE_ASSERT_H_
+
+#include <stdlib.h>
+
+#include "Firestore/core/src/firebase/firestore/util/log.h"
+
+#define FIREBASE_EXPAND_STRINGIFY_(X) #X
+#define FIREBASE_EXPAND_STRINGIFY(X) FIREBASE_EXPAND_STRINGIFY_(X)
+
+// FIREBASE_ASSERT_* macros are not compiled out of release builds. They should
+// be used for assertions that need to be propagated to end-users of SDKs.
+// FIREBASE_DEV_ASSERT_* macros are compiled out of release builds, similar to
+// the C assert() macro. They should be used for internal assertions that are
+// only shown to SDK developers.
+
+// Assert condition is true, if it's false log an assert with the specified
+// expression as a string.
+#define FIREBASE_ASSERT_WITH_EXPRESSION(condition, expression) \
+ do { \
+ if (!(condition)) { \
+ firebase::firestore::util::FailAssert( \
+ __FILE__, __PRETTY_FUNCTION__, __LINE__, \
+ FIREBASE_EXPAND_STRINGIFY(expression)); \
+ } \
+ } while (0)
+
+// Assert condition is true, if it's false log an assert with the specified
+// expression as a string. Compiled out of release builds.
+#if defined(NDEBUG)
+#define FIREBASE_DEV_ASSERT_WITH_EXPRESSION(condition, expression) \
+ { (void)(condition); }
+#else
+#define FIREBASE_DEV_ASSERT_WITH_EXPRESSION(condition, expression) \
+ FIREBASE_ASSERT_WITH_EXPRESSION(condition, expression)
+#endif // defined(NDEBUG)
+
+// Custom assert() implementation that is not compiled out in release builds.
+#define FIREBASE_ASSERT(expression) \
+ FIREBASE_ASSERT_WITH_EXPRESSION(expression, expression)
+
+// Custom assert() implementation that is compiled out in release builds.
+// Compiled out of release builds.
+#define FIREBASE_DEV_ASSERT(expression) \
+ FIREBASE_DEV_ASSERT_WITH_EXPRESSION(expression, expression)
+
+// Assert condition is true otherwise display the specified expression,
+// message and abort.
+#define FIREBASE_ASSERT_MESSAGE_WITH_EXPRESSION(condition, expression, ...) \
+ do { \
+ if (!(condition)) { \
+ firebase::firestore::util::LogError( \
+ FIREBASE_EXPAND_STRINGIFY(expression)); \
+ firebase::firestore::util::FailAssert(__FILE__, __PRETTY_FUNCTION__, \
+ __LINE__, __VA_ARGS__); \
+ } \
+ } while (0)
+
+// Assert condition is true otherwise display the specified expression,
+// message and abort. Compiled out of release builds.
+#if defined(NDEBUG)
+#define FIREBASE_DEV_ASSERT_MESSAGE_WITH_EXPRESSION(condition, expression, \
+ ...) \
+ { (void)(condition); }
+#else
+#define FIREBASE_DEV_ASSERT_MESSAGE_WITH_EXPRESSION(condition, expression, \
+ ...) \
+ FIREBASE_ASSERT_MESSAGE_WITH_EXPRESSION(condition, expression, __VA_ARGS__)
+#endif // defined(NDEBUG)
+
+namespace firebase {
+namespace firestore {
+namespace util {
+
+// A no-return helper function. To raise an assertion, use Macro instead.
+void FailAssert(const char* file,
+ const char* func,
+ const int line,
+ const char* format,
+ ...);
+
+} // namespace util
+} // namespace firestore
+} // namespace firebase
+
+#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_FIREBASE_ASSERT_H_
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 0000000..042fd72
--- /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 <iterator>
+#include <memory>
+#include <type_traits>
+
+#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 <typename Iter>
+struct IsConstIter
+ : std::is_const<typename std::remove_reference<
+ typename std::iterator_traits<Iter>::reference>::type> {};
+
+template <bool Cond, typename T>
+struct AddConstIf : std::conditional<Cond, const T, T> {};
+
+// SynthIterTraits propagates the constness of the 'BaseIter' iterator
+// type to its own exported 'pointer' and 'reference' typedefs.
+template <typename BaseIter, typename Val>
+struct SynthIterTraits : std::iterator_traits<BaseIter> {
+ private:
+ static constexpr bool kIterConst = IsConstIter<BaseIter>::value;
+
+ public:
+ using value_type = typename std::remove_cv<Val>::type;
+ using pointer = typename AddConstIf<kIterConst, Val>::type*;
+ using reference = typename AddConstIf<kIterConst, Val>::type&;
+};
+
+// PointeeSynthIterTraits is similar to SynthIterTraits, but the 'Ptr'
+// parameter is a pointer-like type, and value_type is the pointee.
+template <typename BaseIter, typename Ptr>
+struct PointeeSynthIterTraits : std::iterator_traits<BaseIter> {
+ private:
+ static constexpr bool kIterConst = IsConstIter<BaseIter>::value;
+
+ public:
+ using value_type = typename std::pointer_traits<Ptr>::element_type;
+ using pointer = typename AddConstIf<kIterConst, value_type>::type*;
+ using reference = typename AddConstIf<kIterConst, value_type>::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 <typename Sub, typename Policy>
+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<Sub&>(*this);
+ }
+ const Sub& sub() const {
+ return static_cast<const Sub&>(*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 <typename It>
+struct FirstPolicy {
+ using underlying_iterator = It;
+ using adapted_traits =
+ SynthIterTraits<underlying_iterator,
+ typename std::iterator_traits<
+ underlying_iterator>::value_type::first_type>;
+ static typename adapted_traits::reference Extract(
+ const underlying_iterator& it) {
+ return it->first;
+ }
+};
+
+template <typename It>
+struct SecondPolicy {
+ using underlying_iterator = It;
+ using adapted_traits =
+ SynthIterTraits<underlying_iterator,
+ typename std::iterator_traits<
+ underlying_iterator>::value_type::second_type>;
+ static typename adapted_traits::reference Extract(
+ const underlying_iterator& it) {
+ return it->second;
+ }
+};
+
+template <typename It>
+struct SecondPtrPolicy {
+ using underlying_iterator = It;
+ using adapted_traits =
+ PointeeSynthIterTraits<underlying_iterator,
+ typename std::iterator_traits<
+ underlying_iterator>::value_type::second_type>;
+ static typename adapted_traits::reference Extract(
+ const underlying_iterator& it) {
+ return *it->second;
+ }
+};
+
+template <typename It>
+struct PtrPolicy {
+ using underlying_iterator = It;
+ using adapted_traits = PointeeSynthIterTraits<
+ underlying_iterator,
+ typename std::iterator_traits<underlying_iterator>::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<string, int> values;
+// values["foo"] = 1;
+// values["bar"] = 2;
+// for (iterator_first<hash_map<string, int>::iterator> x = values.begin();
+// x != values.end(); ++x) {
+// printf("%s", x->c_str());
+// }
+template <typename It>
+struct iterator_first
+ : internal::IteratorAdaptorBase<iterator_first<It>,
+ internal::FirstPolicy<It>> {
+ using Base = internal::IteratorAdaptorBase<iterator_first<It>,
+ internal::FirstPolicy<It>>;
+ iterator_first() {
+ }
+ iterator_first(It it) // NOLINT(runtime/explicit)
+ : Base(it) {
+ }
+ template <typename It2>
+ iterator_first(iterator_first<It2> o) // NOLINT(runtime/explicit)
+ : Base(o.base()) {
+ }
+};
+
+template <typename It>
+iterator_first<It> make_iterator_first(It it) {
+ return iterator_first<It>(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<string, int> values;
+// values["foo"] = 1;
+// values["bar"] = 2;
+// for (iterator_second<hash_map<string, int>::iterator> x = values.begin();
+// x != values.end(); ++x) {
+// int v = *x;
+// printf("%d", v);
+// }
+template <typename It>
+struct iterator_second
+ : internal::IteratorAdaptorBase<iterator_second<It>,
+ internal::SecondPolicy<It>> {
+ using Base = internal::IteratorAdaptorBase<iterator_second<It>,
+ internal::SecondPolicy<It>>;
+ iterator_second() {
+ }
+ iterator_second(It it) // NOLINT(runtime/explicit)
+ : Base(it) {
+ }
+ template <typename It2>
+ iterator_second(iterator_second<It2> o) // NOLINT(runtime/explicit)
+ : Base(o.base()) {
+ }
+};
+
+template <typename It>
+iterator_second<It> make_iterator_second(It it) {
+ return iterator_second<It>(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<iterator_second<> >
+// 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<string, linked_ptr<MyClass> > MyMap;
+// typedef iterator_second_ptr<MyMap::iterator> 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 <typename It>
+struct iterator_second_ptr
+ : internal::IteratorAdaptorBase<iterator_second_ptr<It>,
+ internal::SecondPtrPolicy<It>> {
+ using Base = internal::IteratorAdaptorBase<iterator_second_ptr<It>,
+ internal::SecondPtrPolicy<It>>;
+ iterator_second_ptr() {
+ }
+ iterator_second_ptr(It it) // NOLINT(runtime/explicit)
+ : Base(it) {
+ }
+ template <typename It2>
+ iterator_second_ptr(iterator_second_ptr<It2> o) // NOLINT(runtime/explicit)
+ : Base(o.base()) {
+ }
+};
+
+template <typename It>
+iterator_second_ptr<It> make_iterator_second_ptr(It it) {
+ return iterator_second_ptr<It>(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<linked_ptr<MyClass> > MyVector;
+// typedef iterator_ptr<MyVector::iterator> 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 <typename It, typename Ptr /* ignored */ = void>
+struct iterator_ptr : internal::IteratorAdaptorBase<iterator_ptr<It, Ptr>,
+ internal::PtrPolicy<It>> {
+ using Base = internal::IteratorAdaptorBase<iterator_ptr<It, Ptr>,
+ internal::PtrPolicy<It>>;
+ iterator_ptr() {
+ }
+ iterator_ptr(It it) // NOLINT(runtime/explicit)
+ : Base(it) {
+ }
+ template <typename It2>
+ iterator_ptr(iterator_ptr<It2> o) // NOLINT(runtime/explicit)
+ : Base(o.base()) {
+ }
+};
+
+template <typename It>
+iterator_ptr<It> make_iterator_ptr(It it) {
+ return iterator_ptr<It>(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 <typename C>
+struct container_traits {
+ private:
+ // Test for availability of C::size_type.
+ template <typename U, typename = void>
+ struct test_size_type : std::false_type {};
+ template <typename U>
+ struct test_size_type<U, absl::void_t<typename U::size_type>>
+ : std::true_type {};
+
+ // Conditional provisioning of a size_type which defaults to size_t.
+ template <bool Cond, typename U = void>
+ struct size_type_def {
+ using type = typename U::size_type;
+ };
+ template <typename U>
+ struct size_type_def<false, U> {
+ using type = size_t;
+ };
+
+ public:
+ // Determine whether C::size_type is available.
+ static const bool has_size_type = test_size_type<C>::value;
+
+ // Provide size_type as either C::size_type if available, or as size_t.
+ using size_type = typename size_type_def<has_size_type, C>::type;
+};
+
+template <typename C>
+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 <typename SubIterGenerator>
+struct ReversingIterGeneratorAdaptor {
+ using container_type = typename SubIterGenerator::container_type;
+ using iterator = std::reverse_iterator<typename SubIterGenerator::iterator>;
+ using const_iterator =
+ std::reverse_iterator<typename SubIterGenerator::const_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 <typename C,
+ typename Iter,
+ typename ConstIter,
+ typename IterGenerator = internal::IterGenerator<C>>
+class iterator_view_helper {
+ public:
+ using container_type = C;
+ using iterator = Iter;
+ using const_iterator = ConstIter;
+ using value_type = typename std::iterator_traits<iterator>::value_type;
+ using size_type = typename internal::container_traits<C>::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 <typename C,
+ typename ConstIter,
+ typename IterGenerator = internal::IterGenerator<C>>
+class const_iterator_view_helper {
+ public:
+ using container_type = C;
+ using const_iterator = ConstIter;
+ using value_type = typename std::iterator_traits<const_iterator>::value_type;
+ using size_type = typename internal::container_traits<C>::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<MyMap>::type keys = key_view(m); // ok
+ // key_view_type<const MyMap>::type const_keys = key_view(m); // ok
+ template <typename Iter>
+ const_iterator_view_helper(const iterator_view_helper<container_type,
+ Iter,
+ const_iterator,
+ IterGenerator>& 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 <typename C> key_view_t<C> key_view(C& c);
+// template <typename C> value_view_t<C> 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 <typename C>
+struct key_view_type {
+ using type = internal::iterator_view_helper<
+ C,
+ iterator_first<typename C::iterator>,
+ iterator_first<typename C::const_iterator>>;
+};
+
+template <typename C>
+struct key_view_type<const C> {
+ using type = internal::
+ const_iterator_view_helper<C, iterator_first<typename C::const_iterator>>;
+};
+
+template <typename C>
+struct value_view_type {
+ using type = internal::iterator_view_helper<
+ C,
+ iterator_second<typename C::iterator>,
+ iterator_second<typename C::const_iterator>>;
+};
+
+template <typename C>
+struct value_view_type<const C> {
+ using type = internal::const_iterator_view_helper<
+ C,
+ iterator_second<typename C::const_iterator>>;
+};
+
+// 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<int, string> 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 C>
+typename key_view_type<C>::type key_view(
+ C& map) { // NOLINT(runtime/references)
+ return typename key_view_type<C>::type(map);
+}
+
+template <typename C>
+typename key_view_type<const C>::type key_view(const C& map) {
+ return typename key_view_type<const C>::type(map);
+}
+
+template <typename C>
+typename value_view_type<C>::type value_view(
+ C& map) { // NOLINT(runtime/references)
+ return typename value_view_type<C>::type(map);
+}
+
+template <typename C>
+typename value_view_type<const C>::type value_view(const C& map) {
+ return typename value_view_type<const C>::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<K,V*>
+// or of std::vector<std::pair<K,V*>>.
+//
+// Example:
+// map<int, string*> 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 <typename C>
+struct deref_second_view_type {
+ using type = internal::iterator_view_helper<
+ C,
+ iterator_second_ptr<typename C::iterator>,
+ iterator_second_ptr<typename C::const_iterator>>;
+};
+
+template <typename C>
+struct deref_second_view_type<const C> {
+ using type = internal::const_iterator_view_helper<
+ C,
+ iterator_second_ptr<typename C::const_iterator>>;
+};
+
+template <typename C>
+typename deref_second_view_type<C>::type deref_second_view(
+ C& map) { // NOLINT(runtime/references)
+ return typename deref_second_view_type<C>::type(map);
+}
+
+template <typename C>
+typename deref_second_view_type<const C>::type deref_second_view(const C& map) {
+ return typename deref_second_view_type<const C>::type(map);
+}
+
+// Abstract container view that dereferences pointer elements.
+//
+// Example:
+// vector<string*> 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 <typename C>
+struct deref_view_type {
+ using type =
+ internal::iterator_view_helper<C,
+ iterator_ptr<typename C::iterator>,
+ iterator_ptr<typename C::const_iterator>>;
+};
+
+template <typename C>
+struct deref_view_type<const C> {
+ using type = internal::
+ const_iterator_view_helper<C, iterator_ptr<typename C::const_iterator>>;
+};
+
+template <typename C>
+typename deref_view_type<C>::type deref_view(
+ C& c) { // NOLINT(runtime/references)
+ return typename deref_view_type<C>::type(c);
+}
+
+template <typename C>
+typename deref_view_type<const C>::type deref_view(const C& c) {
+ return typename deref_view_type<const C>::type(c);
+}
+
+// Abstract container view that iterates backwards.
+//
+// Example:
+// vector<string> 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 <typename C>
+struct reversed_view_type {
+ private:
+ using policy =
+ internal::ReversingIterGeneratorAdaptor<internal::IterGenerator<C>>;
+
+ public:
+ using type = internal::iterator_view_helper<C,
+ typename policy::iterator,
+ typename policy::const_iterator,
+ policy>;
+};
+
+template <typename C>
+struct reversed_view_type<const C> {
+ private:
+ using policy =
+ internal::ReversingIterGeneratorAdaptor<internal::IterGenerator<C>>;
+
+ public:
+ using type = internal::
+ const_iterator_view_helper<C, typename policy::const_iterator, policy>;
+};
+
+template <typename C>
+typename reversed_view_type<C>::type reversed_view(
+ C& c) { // NOLINT(runtime/references)
+ return typename reversed_view_type<C>::type(c);
+}
+
+template <typename C>
+typename reversed_view_type<const C>::type reversed_view(const C& c) {
+ return typename reversed_view_type<const C>::type(c);
+}
+
+} // namespace util
+} // namespace firestore
+} // namespace firebase
+
+#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_ITERATOR_ADAPTORS_H_
diff --git a/Firestore/core/src/firebase/firestore/util/log.h b/Firestore/core/src/firebase/firestore/util/log.h
new file mode 100644
index 0000000..d0cff4d
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/util/log.h
@@ -0,0 +1,63 @@
+/*
+ * 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_UTIL_LOG_H_
+#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_LOG_H_
+
+#include <stdarg.h>
+
+namespace firebase {
+namespace firestore {
+namespace util {
+
+/// @brief Levels used when logging messages.
+enum LogLevel {
+ /// Verbose Log Level
+ kLogLevelVerbose = 0,
+ /// Debug Log Level
+ kLogLevelDebug,
+ /// Info Log Level
+ kLogLevelInfo,
+ /// Warning Log Level
+ kLogLevelWarning,
+ /// Error Log Level
+ kLogLevelError,
+};
+
+// Common log methods.
+
+// All messages at or above the specified log level value are displayed.
+void LogSetLevel(LogLevel level);
+// Get the currently set log level.
+LogLevel LogGetLevel();
+// Log a debug message to the system log.
+void LogDebug(const char* format, ...);
+// Log an info message to the system log.
+void LogInfo(const char* format, ...);
+// Log a warning to the system log.
+void LogWarning(const char* format, ...);
+// Log an error to the system log.
+void LogError(const char* format, ...);
+// Log a firebase message (implemented by the platform specific logger).
+void LogMessageV(LogLevel log_level, const char* format, va_list args);
+// Log a firebase message via LogMessageV().
+void LogMessage(LogLevel log_level, const char* format, ...);
+
+} // namespace util
+} // namespace firestore
+} // namespace firebase
+
+#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_LOG_H_
diff --git a/Firestore/core/src/firebase/firestore/util/log_apple.mm b/Firestore/core/src/firebase/firestore/util/log_apple.mm
new file mode 100644
index 0000000..cb2c58e
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/util/log_apple.mm
@@ -0,0 +1,123 @@
+/*
+ * 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.
+ */
+
+#include "Firestore/core/src/firebase/firestore/util/log.h"
+
+#import <FirebaseCore/FIRLogger.h>
+#import <Foundation/Foundation.h>
+
+#include <string>
+
+#include "Firestore/core/src/firebase/firestore/util/string_apple.h"
+
+namespace firebase {
+namespace firestore {
+namespace util {
+
+namespace {
+
+// Translates a C++ LogLevel to the equivalent Objective-C FIRLoggerLevel
+FIRLoggerLevel ToFIRLoggerLevel(LogLevel level) {
+ switch (level) {
+ case kLogLevelVerbose: // fall through
+ case kLogLevelDebug:
+ return FIRLoggerLevelDebug;
+ case kLogLevelInfo:
+ return FIRLoggerLevelInfo;
+ case kLogLevelWarning:
+ return FIRLoggerLevelWarning;
+ case kLogLevelError:
+ return FIRLoggerLevelError;
+ default:
+ // Unsupported log level. FIRSetLoggerLevel will deal with it.
+ return static_cast<FIRLoggerLevel>(-1);
+ }
+}
+
+} // namespace
+
+void LogSetLevel(LogLevel level) {
+ FIRSetLoggerLevel(ToFIRLoggerLevel(level));
+}
+
+LogLevel LogGetLevel() {
+ // We return the true log level. True log level is what the SDK used to
+ // determine whether to log instead of what parameter is used in the last call
+ // of LogSetLevel().
+ if (FIRIsLoggableLevel(FIRLoggerLevelInfo, NO)) {
+ if (FIRIsLoggableLevel(FIRLoggerLevelDebug, NO)) {
+ // FIRLoggerLevelMax is actually kLogLevelDebug right now. We do not check
+ // further.
+ return kLogLevelDebug;
+ } else {
+ return kLogLevelInfo;
+ }
+ } else {
+ if (FIRIsLoggableLevel(FIRLoggerLevelWarning, NO)) {
+ return kLogLevelWarning;
+ } else {
+ return kLogLevelError;
+ }
+ }
+}
+
+void LogDebug(const char* format, ...) {
+ va_list list;
+ va_start(list, format);
+ FIRLogBasic(FIRLoggerLevelDebug, kFIRLoggerFirestore, @"I-FST000001",
+ WrapNSStringNoCopy(format), list);
+ va_end(list);
+}
+
+void LogInfo(const char* format, ...) {
+ va_list list;
+ va_start(list, format);
+ FIRLogBasic(FIRLoggerLevelInfo, kFIRLoggerFirestore, @"I-FST000001",
+ WrapNSStringNoCopy(format), list);
+ va_end(list);
+}
+
+void LogWarning(const char* format, ...) {
+ va_list list;
+ va_start(list, format);
+ FIRLogBasic(FIRLoggerLevelWarning, kFIRLoggerFirestore, @"I-FST000001",
+ WrapNSStringNoCopy(format), list);
+ va_end(list);
+}
+
+void LogError(const char* format, ...) {
+ va_list list;
+ va_start(list, format);
+ FIRLogBasic(FIRLoggerLevelError, kFIRLoggerFirestore, @"I-FST000001",
+ WrapNSStringNoCopy(format), list);
+ va_end(list);
+}
+
+void LogMessageV(LogLevel log_level, const char* format, va_list args) {
+ FIRLogBasic(ToFIRLoggerLevel(log_level), kFIRLoggerFirestore, @"I-FST000001",
+ WrapNSStringNoCopy(format), args);
+}
+
+void LogMessage(LogLevel log_level, const char* format, ...) {
+ va_list list;
+ va_start(list, format);
+ LogMessageV(log_level, format, list);
+ va_end(list);
+}
+
+} // namespace util
+} // namespace firestore
+} // namespace firebase
diff --git a/Firestore/core/src/firebase/firestore/util/log_stdio.cc b/Firestore/core/src/firebase/firestore/util/log_stdio.cc
new file mode 100644
index 0000000..bca2dc9
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/util/log_stdio.cc
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+
+#include "Firestore/core/src/firebase/firestore/util/log.h"
+
+#include <stdio.h>
+#include <string>
+
+namespace firebase {
+namespace firestore {
+namespace util {
+
+LogLevel g_log_level = kLogLevelInfo;
+
+void LogSetLevel(LogLevel level) {
+ g_log_level = level;
+}
+
+LogLevel LogGetLevel() {
+ return g_log_level;
+}
+
+void LogDebug(const char* format, ...) {
+ va_list list;
+ va_start(list, format);
+ LogMessageV(kLogLevelDebug, format, list);
+ va_end(list);
+}
+
+void LogInfo(const char* format, ...) {
+ va_list list;
+ va_start(list, format);
+ LogMessageV(kLogLevelInfo, format, list);
+ va_end(list);
+}
+
+void LogWarning(const char* format, ...) {
+ va_list list;
+ va_start(list, format);
+ LogMessageV(kLogLevelWarning, format, list);
+ va_end(list);
+}
+
+void LogError(const char* format, ...) {
+ va_list list;
+ va_start(list, format);
+ LogMessageV(kLogLevelError, format, list);
+ va_end(list);
+}
+
+void LogMessageV(LogLevel log_level, const char* format, va_list args) {
+ if (log_level < g_log_level) {
+ return;
+ }
+ switch (log_level) {
+ case kLogLevelVerbose:
+ printf("VERBOSE: ");
+ break;
+ case kLogLevelDebug:
+ printf("DEBUG: ");
+ break;
+ case kLogLevelInfo:
+ break;
+ case kLogLevelWarning:
+ printf("WARNING: ");
+ break;
+ case kLogLevelError:
+ printf("ERROR: ");
+ break;
+ }
+ vprintf(format, args);
+ printf("\n");
+}
+
+void LogMessage(LogLevel log_level, const char* format, ...) {
+ va_list list;
+ va_start(list, format);
+ LogMessageV(log_level, format, list);
+ va_end(list);
+}
+
+} // namespace util
+} // namespace firestore
+} // namespace firebase
diff --git a/Firestore/Port/ordered_code.cc b/Firestore/core/src/firebase/firestore/util/ordered_code.cc
index 038d445..688f15c 100644
--- a/Firestore/Port/ordered_code.cc
+++ b/Firestore/core/src/firebase/firestore/util/ordered_code.cc
@@ -14,15 +14,19 @@
* limitations under the License.
*/
-#include "Firestore/Port/ordered_code.h"
+#include "Firestore/core/src/firebase/firestore/util/ordered_code.h"
-#include <assert.h>
+#include <absl/base/internal/endian.h>
+#include <absl/base/internal/unaligned_access.h>
+#include <absl/base/port.h>
-#include "Firestore/Port/bits.h"
+#include "Firestore/core/src/firebase/firestore/util/bits.h"
+#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h"
-#include "Firestore/Port/absl/absl_endian.h"
-#include "Firestore/Port/absl/absl_port.h"
-#include <leveldb/db.h> // For Slice
+#define UNALIGNED_LOAD32 ABSL_INTERNAL_UNALIGNED_LOAD32
+#define UNALIGNED_LOAD64 ABSL_INTERNAL_UNALIGNED_LOAD64
+#define UNALIGNED_STORE32 ABSL_INTERNAL_UNALIGNED_STORE32
+#define UNALIGNED_STORE64 ABSL_INTERNAL_UNALIGNED_STORE64
// We encode a string in different ways depending on whether the item
// should be in lexicographically increasing or decreasing order.
@@ -59,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(<infinity>).
-namespace Firestore {
-
-using leveldb::Slice;
+namespace firebase {
+namespace firestore {
+namespace util {
static const char kEscape1 = '\000';
static const char kNullCharacter = '\xff'; // Combined with kEscape1
@@ -73,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
@@ -154,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;
}
}
@@ -174,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;
@@ -184,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<size_t>(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<size_t>(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<size_t>(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<unsigned int>(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
@@ -238,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<char>(length);
AppendUpto9(dest, start, length + 1);
}
@@ -255,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();
@@ -278,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<size_t>(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<size_t>(start - src->data()));
return true;
} else if (next == kNullCharacter) {
if (result) {
@@ -298,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<size_t>(start - copy_start) - 1);
}
// kEscape2 kFFCharacter represents '\xff'
// kEscape2 kInfinity is an error
@@ -318,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<unsigned char>((*src)[0]);
+ const size_t len = static_cast<size_t>((*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
@@ -341,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<unsigned char>((*src)[1 + i]);
}
@@ -351,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;
@@ -360,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;
@@ -381,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;
@@ -394,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) {
@@ -500,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<uint64_t>(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<uint64_t>(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<char>(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<uint64_t>(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<size_t>(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<unsigned char>(
+ static_cast<uint64_t>((*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<size_t>(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<unsigned char>((*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<unsigned char>(
+ static_cast<uint64_t>((*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<unsigned char>(
+ static_cast<uint64_t>((*src)[2]) ^ (xor_mask & 0xff));
if (second_byte == 0xc0 && third_byte < 0x80) {
len = 10;
} else {
@@ -568,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<size_t>(
+ SignedEncodingLength(static_cast<int64_t>(x))));
- if (result) *result = x;
- src->remove_prefix(len);
+ if (result) *result = static_cast<int64_t>(x);
+ src->remove_prefix(static_cast<size_t>(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
index 7c390a5..57b84bd 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 <string>
-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 72be1bd..95b41e1 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 a76ade3..83f72b5 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 0000000..d3f6e63
--- /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 <openssl/err.h>
+#include <openssl/rand.h>
+
+namespace firebase {
+namespace firestore {
+namespace util {
+
+SecureRandom::result_type SecureRandom::operator()() {
+ result_type result;
+ int rc = RAND_bytes(reinterpret_cast<uint8_t*>(&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
new file mode 100644
index 0000000..108ade7
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/util/string_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_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 <Foundation/Foundation.h>
+
+#include "absl/strings/string_view.h"
+
+namespace firebase {
+namespace firestore {
+namespace util {
+
+// Translates a C string to the equivalent NSString without making a copy.
+inline NSString* WrapNSStringNoCopy(const char* c_str) {
+ return [[NSString alloc]
+ initWithBytesNoCopy:const_cast<void*>(static_cast<const void*>(c_str))
+ length:strlen(c_str)
+ encoding:NSUTF8StringEncoding
+ freeWhenDone:NO];
+}
+
+// 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
new file mode 100644
index 0000000..9c4e31c
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/util/string_printf.cc
@@ -0,0 +1,102 @@
+/*
+ * 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/string_printf.h"
+
+#include <stdio.h>
+
+namespace firebase {
+namespace firestore {
+namespace util {
+
+void StringAppendV(std::string* dst, const char* format, va_list ap) {
+ // First try with a small fixed size buffer
+ static const int kSpaceLength = 1024;
+ char space[kSpaceLength];
+
+ // It's possible for methods that use a va_list to invalidate
+ // the data in it upon use. The fix is to make a copy
+ // of the structure before using it and use that copy instead.
+ va_list backup_ap;
+ va_copy(backup_ap, ap);
+ int result = vsnprintf(space, kSpaceLength, format, backup_ap);
+ va_end(backup_ap);
+
+ if (result < kSpaceLength) {
+ if (result >= 0) {
+ // Normal case -- everything fit.
+ dst->append(space, static_cast<size_t>(result));
+ return;
+ }
+
+#ifdef _MSC_VER
+ // Error or MSVC running out of space. MSVC 8.0 and higher
+ // can be asked about space needed with the special idiom below:
+ va_copy(backup_ap, ap);
+ result = vsnprintf(nullptr, 0, format, backup_ap);
+ va_end(backup_ap);
+#endif
+ }
+
+ if (result < 0) {
+ // Just an error.
+ return;
+ }
+ size_t result_size = static_cast<size_t>(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;
+
+ dst->resize(target_size + 1);
+ char* buf = &(*dst)[initial_size];
+ 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 && static_cast<size_t>(result) < buf_remain) {
+ // It fit and vsnprintf copied in directly. Resize down one to
+ // remove the trailing \0.
+ dst->resize(target_size);
+ } else {
+ // Didn't fit. Leave the original string unchanged.
+ dst->resize(initial_size);
+ }
+}
+
+std::string StringPrintf(const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ std::string result;
+ StringAppendV(&result, format, ap);
+ va_end(ap);
+ return result;
+}
+
+void StringAppendF(std::string* dst, const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ StringAppendV(dst, format, ap);
+ va_end(ap);
+}
+
+} // namespace util
+} // namespace firestore
+} // namespace firebase
diff --git a/Firestore/core/src/firebase/firestore/util/string_printf.h b/Firestore/core/src/firebase/firestore/util/string_printf.h
new file mode 100644
index 0000000..10dfae9
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/util/string_printf.h
@@ -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.
+ */
+
+#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_STRING_PRINTF_H_
+#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_STRING_PRINTF_H_
+
+#include <stdarg.h>
+
+#include <string>
+
+#include "absl/base/attributes.h"
+
+namespace firebase {
+namespace firestore {
+namespace util {
+
+/** Return a C++ string. */
+std::string StringPrintf(const char* format, ...) ABSL_PRINTF_ATTRIBUTE(1, 2);
+
+/** Append result to a supplied string. */
+void StringAppendF(std::string* dst, const char* format, ...)
+ ABSL_PRINTF_ATTRIBUTE(2, 3);
+
+/**
+ * Lower-level routine that takes a va_list and appends to a specified
+ * string. All other routines are just convenience wrappers around it.
+ */
+void StringAppendV(std::string* dst, const char* format, va_list ap);
+
+} // namespace util
+} // namespace firestore
+} // namespace firebase
+
+#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
index 2587860..b7f1ed9 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 <leveldb/db.h>
+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 0000000..3de177d
--- /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 <string>
+
+#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 0000000..ed5760f
--- /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/core/CMakeLists.txt b/Firestore/core/test/firebase/firestore/core/CMakeLists.txt
new file mode 100644
index 0000000..34993aa
--- /dev/null
+++ b/Firestore/core/test/firebase/firestore/core/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_core_test
+ SOURCES
+ target_id_generator_test.cc
+ DEPENDS
+ firebase_firestore_core
+)
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 0000000..7eca335
--- /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 0000000..bd8d76f
--- /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 0000000..e7b0b6e
--- /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 0000000..3ec1e64
--- /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 <numeric>
+#include <random>
+
+#include "Firestore/core/src/firebase/firestore/util/secure_random.h"
+#include "gtest/gtest.h"
+
+namespace firebase {
+namespace firestore {
+namespace immutable {
+
+typedef ArraySortedMap<int, int> IntMap;
+constexpr IntMap::size_type kFixedSize = IntMap::kFixedSize;
+
+template <typename K, typename V>
+testing::AssertionResult NotFound(const ArraySortedMap<K, V>& 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 <typename K, typename V>
+testing::AssertionResult Found(const ArraySortedMap<K, V>& 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<int> Sequence(int start, int end, int step = 1) {
+ std::vector<int> 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<int> Sequence(int num_elements) {
+ return Sequence(0, num_elements);
+}
+
+/**
+ * Creates a copy of the given vector with contents shuffled randomly.
+ */
+std::vector<int> Shuffled(const std::vector<int>& values) {
+ std::vector<int> 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<int> Sorted(const std::vector<int>& values) {
+ std::vector<int> 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<std::pair<int, int>> Pairs(const std::vector<int>& values) {
+ std::vector<std::pair<int, int>> 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<int>& 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 <typename Container>
+std::vector<typename Container::value_type> Append(const Container& container) {
+ std::vector<typename Container::value_type> 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<int>(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<int> 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<int>(expected_size);
+ std::vector<int> to_insert = Shuffled(Sequence(n));
+ std::vector<int> 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<int> 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 0000000..0f83bf2
--- /dev/null
+++ b/Firestore/core/test/firebase/firestore/model/CMakeLists.txt
@@ -0,0 +1,23 @@
+# 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
+ field_value_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 0000000..e9c9439
--- /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::kDefaultDatabaseId);
+ EXPECT_EQ("p", id.project_id());
+ EXPECT_EQ(DatabaseId::kDefaultDatabaseId, 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/field_value_test.cc b/Firestore/core/test/firebase/firestore/model/field_value_test.cc
new file mode 100644
index 0000000..702c0f6
--- /dev/null
+++ b/Firestore/core/test/firebase/firestore/model/field_value_test.cc
@@ -0,0 +1,462 @@
+/*
+ * 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 <limits.h>
+
+#include <vector>
+
+#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<const uint8_t*>(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::Long, 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, 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<FieldValue>{});
+ std::vector<FieldValue> array{FieldValue::NullValue(),
+ FieldValue::BooleanValue(true),
+ FieldValue::BooleanValue(false)};
+ // copy the array
+ const FieldValue small = FieldValue::ArrayValue(array);
+ std::vector<FieldValue> 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<const std::string, const FieldValue>{});
+ std::map<const std::string, const FieldValue> object{
+ {"null", FieldValue::NullValue()},
+ {"true", FieldValue::TrueValue()},
+ {"false", FieldValue::FalseValue()}};
+ // copy the map
+ const FieldValue small = FieldValue::ObjectValue(object);
+ std::map<const std::string, const FieldValue> 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 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>{
+ FieldValue::TrueValue(), FieldValue::FalseValue()});
+ clone = array_value;
+ EXPECT_EQ(FieldValue::ArrayValue(std::vector<FieldValue>{
+ FieldValue::TrueValue(), FieldValue::FalseValue()}),
+ clone);
+ EXPECT_EQ(FieldValue::ArrayValue(std::vector<FieldValue>{
+ FieldValue::TrueValue(), FieldValue::FalseValue()}),
+ array_value);
+ clone = clone;
+ EXPECT_EQ(FieldValue::ArrayValue(std::vector<FieldValue>{
+ FieldValue::TrueValue(), FieldValue::FalseValue()}),
+ clone);
+ clone = null_value;
+ EXPECT_EQ(FieldValue::NullValue(), clone);
+
+ const FieldValue object_value =
+ FieldValue::ObjectValue(std::map<const std::string, const FieldValue>{
+ {"true", FieldValue::TrueValue()},
+ {"false", FieldValue::FalseValue()}});
+ clone = object_value;
+ EXPECT_EQ(
+ FieldValue::ObjectValue(std::map<const std::string, const FieldValue>{
+ {"true", FieldValue::TrueValue()},
+ {"false", FieldValue::FalseValue()}}),
+ clone);
+ EXPECT_EQ(
+ FieldValue::ObjectValue(std::map<const std::string, const FieldValue>{
+ {"true", FieldValue::TrueValue()},
+ {"false", FieldValue::FalseValue()}}),
+ object_value);
+ clone = clone;
+ EXPECT_EQ(
+ FieldValue::ObjectValue(std::map<const std::string, const FieldValue>{
+ {"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);
+
+ const 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);
+
+ const 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 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>{
+ FieldValue::TrueValue(), FieldValue::FalseValue()});
+ clone = std::move(array_value);
+ EXPECT_EQ(FieldValue::ArrayValue(std::vector<FieldValue>{
+ FieldValue::TrueValue(), FieldValue::FalseValue()}),
+ clone);
+ clone = FieldValue::NullValue();
+ EXPECT_EQ(FieldValue::NullValue(), clone);
+
+ FieldValue object_value =
+ FieldValue::ObjectValue(std::map<const std::string, const FieldValue>{
+ {"true", FieldValue::TrueValue()},
+ {"false", FieldValue::FalseValue()}});
+ clone = std::move(object_value);
+ EXPECT_EQ(
+ FieldValue::ObjectValue(std::map<const std::string, const FieldValue>{
+ {"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 FieldValue geo_point_value = FieldValue::GeoPointValue({1, 2});
+ const FieldValue array_value =
+ FieldValue::ArrayValue(std::vector<FieldValue>());
+ const FieldValue object_value =
+ FieldValue::ObjectValue(std::map<const std::string, const FieldValue>());
+ 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 < 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/timestamp_test.cc b/Firestore/core/test/firebase/firestore/model/timestamp_test.cc
new file mode 100644
index 0000000..55ee378
--- /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 <vector>
+
+#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 0000000..7d99e6f
--- /dev/null
+++ b/Firestore/core/test/firebase/firestore/remote/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_remote_test
+ SOURCES
+ datastore_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 0000000..46a19cc
--- /dev/null
+++ b/Firestore/core/test/firebase/firestore/remote/datastore_test.cc
@@ -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.
+ */
+
+#include "Firestore/core/src/firebase/firestore/remote/datastore.h"
+
+#include <grpc/grpc.h>
+#include <gtest/gtest.h>
+
+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();
+}
diff --git a/Firestore/core/test/firebase/firestore/util/CMakeLists.txt b/Firestore/core/test/firebase/firestore/util/CMakeLists.txt
index 223fa41..0bddf06 100644
--- a/Firestore/core/test/firebase/firestore/util/CMakeLists.txt
+++ b/Firestore/core/test/firebase/firestore/util/CMakeLists.txt
@@ -12,12 +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
+ 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
)
-target_link_libraries(
- firebase_firestore_util_test
- firebase_firestore_util
+
+if(APPLE)
+ cc_test(
+ firebase_firestore_util_apple_test
+ SOURCES
+ assert_test.cc
+ log_test.cc
+ DEPENDS
+ firebase_firestore_util_apple
+ )
+endif(APPLE)
+
+cc_test(
+ firebase_firestore_util_stdio_test
+ 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
new file mode 100644
index 0000000..fb15e61
--- /dev/null
+++ b/Firestore/core/test/firebase/firestore/util/assert_test.cc
@@ -0,0 +1,63 @@
+/*
+ * 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 <exception>
+
+#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h"
+#include "gtest/gtest.h"
+
+namespace firebase {
+namespace firestore {
+namespace util {
+
+namespace {
+
+void AssertWithExpression(bool condition) {
+ FIREBASE_ASSERT_WITH_EXPRESSION(condition, 1 + 2 + 3);
+}
+
+void Assert(bool condition) {
+ FIREBASE_ASSERT(condition == true);
+}
+
+void AssertMessageWithExpression(bool condition) {
+ FIREBASE_ASSERT_MESSAGE_WITH_EXPRESSION(condition, 1 + 2 + 3, "connection %s",
+ condition ? "succeeded" : "failed");
+}
+
+} // namespace
+
+TEST(Assert, WithExpression) {
+ AssertWithExpression(true);
+
+ EXPECT_ANY_THROW(AssertWithExpression(false));
+}
+
+TEST(Assert, Vanilla) {
+ Assert(true);
+
+ EXPECT_ANY_THROW(Assert(false));
+}
+
+TEST(Assert, WithMessageAndExpression) {
+ AssertMessageWithExpression(true);
+
+ EXPECT_ANY_THROW(AssertMessageWithExpression(false));
+}
+
+} // namespace util
+} // namespace firestore
+} // 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 730c683..079b990 100644
--- a/Firestore/core/test/firebase/firestore/util/autoid_test.cc
+++ b/Firestore/core/test/firebase/firestore/util/autoid_test.cc
@@ -18,15 +18,15 @@
#include <ctype.h>
-#include "gtest/gtest.h"
+#include <gtest/gtest.h>
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
index 8c3c246..cb0976b 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 <algorithm>
#include <iostream>
+#include <limits>
-#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<int>(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<int>(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<uint32>(1) << shift) + delta;
+ for (uint32_t delta = 0; delta <= 2; delta++) {
+ const uint32_t v = (static_cast<uint32_t>(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<uint32_t>::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<uint64>(1) << shift) + delta;
+ for (uint64_t delta = 0; delta <= 2; delta++) {
+ const uint64_t v = (static_cast<uint64_t>(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<uint64_t>::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 0000000..ccb3011
--- /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 <inttypes.h>
+#include <math.h>
+
+#include <limits>
+
+#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<absl::string_view>("", "a"));
+ ASSERT_ASCENDING(Compare<absl::string_view>("a", "b"));
+ ASSERT_ASCENDING(Compare<absl::string_view>("a", "aa"));
+
+ ASSERT_DESCENDING(Compare<absl::string_view>("a", ""));
+ ASSERT_DESCENDING(Compare<absl::string_view>("b", "a"));
+ ASSERT_DESCENDING(Compare<absl::string_view>("aa", "a"));
+
+ ASSERT_SAME(Compare<absl::string_view>("", ""));
+ ASSERT_SAME(Compare<absl::string_view>("", std::string()));
+ ASSERT_SAME(Compare<absl::string_view>("a", "a"));
+}
+
+TEST(Comparison, BooleanCompare) {
+ ASSERT_SAME(Compare<bool>(false, false));
+ ASSERT_SAME(Compare<bool>(true, true));
+ ASSERT_ASCENDING(Compare<bool>(false, true));
+ ASSERT_DESCENDING(Compare<bool>(true, false));
+}
+
+TEST(Comparison, DoubleCompare) {
+ ASSERT_SAME(Compare<double>(NAN, NAN));
+ ASSERT_ASCENDING(Compare<double>(NAN, 0));
+ ASSERT_DESCENDING(Compare<double>(0, NAN));
+
+ ASSERT_SAME(Compare<double>(-INFINITY, -INFINITY));
+ ASSERT_SAME(Compare<double>(INFINITY, INFINITY));
+ ASSERT_ASCENDING(Compare<double>(-INFINITY, INFINITY));
+ ASSERT_DESCENDING(Compare<double>(INFINITY, -INFINITY));
+
+ ASSERT_SAME(Compare<double>(0, 0));
+ ASSERT_SAME(Compare<double>(-0, -0));
+ ASSERT_SAME(Compare<double>(-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: <https://en.wikipedia.org/wiki/Denormal_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 0000000..4cd44cc
--- /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 <iterator>
+#include <list>
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <type_traits>
+#include <unordered_map>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+
+#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 <typename T>
+struct IsConst : std::false_type {};
+template <typename T>
+struct IsConst<const T> : std::true_type {};
+template <typename T>
+struct IsConst<T&> : IsConst<T> {};
+
+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 <typename T>
+ class InlineStorageIter : public std::iterator<std::input_iterator_tag, T> {
+ 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<std::string, int> my_container;
+ my_container values;
+ for (int i = 0; i < kCount; ++i) {
+ values[kFirst[i]] = kSecond[i];
+ }
+ for (iterator_first<my_container::iterator> it = values.begin();
+ it != values.end(); ++it) {
+ ASSERT_GT(it->length(), 0u);
+ }
+}
+
+TEST_F(IteratorAdaptorTest, IteratorPtrUniquePtr) {
+ // Tests iterator_ptr with a vector<unique_ptr<int>>.
+ typedef std::vector<std::unique_ptr<int>> my_container;
+ typedef iterator_ptr<my_container::iterator> my_iterator;
+ my_container values;
+ for (int i = 0; i < kCount; ++i) {
+ values.push_back(std::unique_ptr<int>(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<std::string, int> my_container;
+ my_container values;
+ for (int i = 0; i < kCount; ++i) {
+ values[kFirst[i]] = kSecond[i];
+ }
+ iterator_first<my_container::iterator> iter = values.begin();
+ iterator_first<my_container::const_iterator> 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<std::pair<int, int>> my_container;
+ typedef iterator_first<my_container::iterator> my_iterator;
+ typedef iterator_first<my_container::const_iterator> 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<std::string, int> my_container;
+ my_container values;
+ for (int i = 0; i < kCount; ++i) {
+ values[kFirst[i]] = kSecond[i];
+ }
+ for (iterator_second<my_container::iterator> 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<std::string, int> my_container;
+ my_container values;
+ for (int i = 0; i < kCount; ++i) {
+ values[kFirst[i]] = kSecond[i];
+ }
+ iterator_second<my_container::iterator> iter = values.begin();
+ iterator_second<my_container::const_iterator> 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<std::pair<int, int>> my_container;
+ typedef iterator_second<my_container::iterator> my_iterator;
+ typedef iterator_second<my_container::const_iterator> 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<std::string, int*> my_container;
+ my_container values;
+ for (int i = 0; i < kCount; ++i) {
+ values[kFirst[i]] = &kSecond[i];
+ }
+ iterator_second_ptr<my_container::iterator> iter = values.begin();
+ iterator_second_ptr<my_container::const_iterator> 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<int, int*> ConstMap;
+ ConstMap empty_map;
+
+ iterator_second_ptr<ConstMap::const_iterator> 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<ConstMap::const_iterator>::value_type* v1 = &*it;
+ iterator_second_ptr<ConstMap::const_iterator>::pointer v1 = &*it;
+ iterator_second_ptr<ConstMap::const_iterator>::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<int*, int> 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<std::pair<int, int*>> my_container;
+ typedef iterator_second_ptr<my_container::iterator> my_iterator;
+ typedef iterator_second_ptr<my_container::const_iterator> 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<std::string, int> my_container;
+ my_container values;
+ for (int i = 0; i < kCount; ++i) {
+ values[kFirst[i]] = kSecond[i];
+ }
+ const unordered_map<std::string, int>* cvalues = &values;
+ for (iterator_first<my_container::const_iterator> 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<std::string, int> my_pair;
+ typedef std::list<my_pair> 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<my_list::iterator> 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<std::string, int> my_pair;
+ typedef std::list<my_pair> 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<my_list::const_iterator> 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<std::pair<std::string, int>> values;
+ for (int i = 0; i < kCount; ++i) {
+ values.push_back(std::pair<std::string, int>(kFirst[i], kSecond[i]));
+ }
+ int i = 0;
+ for (iterator_second<std::vector<std::pair<std::string, int>>::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<std::string, int*> my_container;
+ typedef iterator_second_ptr<my_container::iterator> 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<std::string, std::shared_ptr<int>> my_container;
+ typedef iterator_second_ptr<my_container::iterator> 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<int*> my_container;
+ typedef iterator_ptr<my_container::iterator> 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<B*> v;
+ const std::vector<B*>& cv = v;
+ iterator_ptr<std::vector<B*>::iterator, A*> ip(v.begin());
+ iterator_ptr<std::vector<B*>::const_iterator, A*> cip(cv.begin());
+}
+
+TEST_F(IteratorAdaptorTest, IteratorPtrtConstEqNonConst) {
+ // verify that const and non-const iterators return the same reference.
+ typedef std::vector<int*> my_container;
+ typedef iterator_ptr<my_container::iterator> my_iterator;
+ typedef iterator_ptr<my_container::const_iterator> 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<std::shared_ptr<int>> my_container;
+ typedef iterator_ptr<my_container::iterator> my_iterator;
+ my_container values;
+ for (int i = 0; i < kCount; ++i) {
+ values.push_back(std::make_shared<int>(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<int*> values;
+ values.push_back(&value);
+ iterator_ptr<std::vector<int*>::iterator> iter = values.begin();
+ iterator_ptr<std::vector<int*>::const_iterator> c_iter = iter;
+ EXPECT_EQ(1, *c_iter);
+}
+
+TEST_F(IteratorAdaptorTest, IteratorFirstHasRandomAccessMethods) {
+ typedef std::vector<std::pair<std::string, int>> my_container;
+ typedef iterator_first<my_container::iterator> my_iterator;
+
+ my_container values;
+ for (int i = 0; i < kCount; ++i) {
+ values.push_back(std::pair<std::string, int>(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<std::pair<std::string, int>> my_container;
+ typedef iterator_second<my_container::iterator> my_iterator;
+
+ my_container values;
+ for (int i = 0; i < kCount; ++i) {
+ values.push_back(std::pair<std::string, int>(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<std::pair<std::string, int*>> my_container;
+ typedef iterator_second_ptr<my_container::iterator> my_iterator;
+
+ ASSERT_GE(kCount, 2);
+ int value1 = 17;
+ int value2 = 99;
+ my_container values;
+ values.push_back(std::pair<std::string, int*>(kFirst[0], &value1));
+ values.push_back(std::pair<std::string, int*>(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<int*> my_container;
+ typedef iterator_ptr<my_container::iterator> 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<std::input_iterator_tag, const int*> {
+ 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<MyInputIterator> 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<std::pair<int, int>*> first_default;
+ iterator_first<std::pair<int, int>*> first_null(nullptr);
+ ASSERT_TRUE(first_default == first_null);
+
+ iterator_second<std::pair<int, int>*> second_default;
+ iterator_second<std::pair<int, int>*> second_null(nullptr);
+ ASSERT_TRUE(second_default == second_null);
+
+ iterator_second_ptr<std::pair<int, int*>*> second_ptr_default;
+ iterator_second_ptr<std::pair<int, int*>*> second_ptr_null(nullptr);
+ ASSERT_TRUE(second_ptr_default == second_ptr_null);
+
+ iterator_ptr<int**> ptr_default;
+ iterator_ptr<int**> ptr_null(nullptr);
+ ASSERT_TRUE(ptr_default == ptr_null);
+}
+
+// Non C++11 test.
+TEST_F(IteratorAdaptorTest, ValueView) {
+ typedef unordered_map<int, std::string> MapType;
+ MapType my_map;
+ my_map[0] = "a";
+ my_map[1] = "b";
+ my_map[2] = "c";
+ const MapType c_map(my_map);
+
+ std::set<std::string> 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<int, int> 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<MapType>::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<int, std::string> pair_int_str;
+ typedef std::map<int, pair_int_str> 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<map_int_pair_int_str>::type
+ value_view_map_int_pair_int_str_type;
+
+ static_assert(
+ (std::is_same<pair_int_str,
+ value_view_map_int_pair_int_str_type::value_type>::value),
+ "value_view_value_type_");
+
+ typedef value_view_type<value_view_map_int_pair_int_str_type>::type
+ view_view_type;
+
+ static_assert((std::is_same<std::string, view_view_type::value_type>::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<std::string>(helper.begin(), helper.end()),
+ ElementsAre("a", "b", "c"));
+}
+
+TEST_F(IteratorAdaptorTest, ValueViewAndKeyViewCopy) {
+ std::map<int, std::string> my_map;
+ my_map[0] = "0";
+ my_map[1] = "1";
+ my_map[2] = "2";
+ std::set<int> keys;
+ std::set<std::string> 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<int, std::string> my_map;
+ my_map[0] = "0";
+ my_map[1] = "1";
+ my_map[2] = "2";
+ std::set<int> keys;
+ std::set<std::string> 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 <int N, typename Value, typename Key>
+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, Key> 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<int, int> 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<int> keys;
+ std::vector<std::string> 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<int, std::string> my_map;
+ my_map[0] = "a";
+ my_map[1] = "b";
+ my_map[2] = "c";
+
+ std::set<std::string> vals;
+ // iterator_view_helper defines cbegin() and cend(); we're not invoking the
+ // C++11 functions of the same name.
+ for (iterator_second<unordered_map<int, std::string>::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<int, std::string> my_map;
+ my_map[0] = "a";
+ my_map[1] = "b";
+ my_map[2] = "c";
+
+ std::set<std::string> vals;
+ const iterator_view_helper<
+ unordered_map<int, std::string>,
+ iterator_second<unordered_map<int, std::string>::iterator>,
+ iterator_second<unordered_map<int, std::string>::const_iterator>>
+ const_view = value_view(my_map);
+ for (iterator_second<unordered_map<int, std::string>::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<int, std::string> my_map;
+ my_map[0] = "a";
+ my_map[1] = "b";
+ my_map[2] = "c";
+
+ const unordered_map<int, std::string>& const_map = my_map;
+
+ std::set<std::string> vals;
+ for (iterator_second<unordered_map<int, std::string>::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<int, std::string> my_map;
+ my_map[0] = "a";
+ my_map[1] = "b";
+ my_map[2] = "c";
+
+ const unordered_map<int, std::string>& const_map = my_map;
+
+ std::set<std::string> vals;
+ // iterator_view_helper defines cbegin() and cend(); we're not invoking the
+ // C++11 functions of the same name.
+ for (iterator_second<unordered_map<int, std::string>::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<int, std::string> my_map;
+ my_map[0] = "a";
+ my_map[1] = "b";
+ my_map[2] = "c";
+
+ const unordered_map<int, std::string>& const_map = my_map;
+
+ std::set<std::string> vals;
+ const value_view_type<const unordered_map<int, std::string>>::type
+ const_view = value_view(const_map);
+ for (iterator_second<unordered_map<int, std::string>::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<int, std::string> my_map;
+ my_map[0] = "a";
+ my_map[1] = "b";
+ my_map[2] = "c";
+
+ std::set<int> vals;
+ for (iterator_first<unordered_map<int, std::string>::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<int, std::string> my_map;
+ my_map[0] = "a";
+ my_map[1] = "b";
+ my_map[2] = "c";
+
+ std::set<int> vals;
+ // iterator_view_helper defines cbegin() and cend(); we're not invoking the
+ // C++11 functions of the same name.
+ for (iterator_first<unordered_map<int, std::string>::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<int, std::string> my_map;
+ my_map[0] = "a";
+ my_map[1] = "b";
+ my_map[2] = "c";
+
+ std::set<int> vals;
+ const key_view_type<unordered_map<int, std::string>>::type const_view =
+ key_view(my_map);
+ for (iterator_first<unordered_map<int, std::string>::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<int, std::string> my_map;
+ my_map[0] = "a";
+ my_map[1] = "b";
+ my_map[2] = "c";
+
+ const unordered_map<int, std::string>& const_map = my_map;
+
+ std::set<int> vals;
+ for (iterator_first<unordered_map<int, std::string>::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<int, std::string> my_map;
+ my_map[0] = "a";
+ my_map[1] = "b";
+ my_map[2] = "c";
+
+ const unordered_map<int, std::string>& const_map = my_map;
+
+ std::set<int> vals;
+ // iterator_view_helper defines cbegin() and cend(); we're not invoking the
+ // C++11 functions of the same name.
+ for (iterator_first<unordered_map<int, std::string>::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<int, std::string> my_map;
+ my_map[0] = "a";
+ my_map[1] = "b";
+ my_map[2] = "c";
+
+ const unordered_map<int, std::string>& const_map = my_map;
+
+ std::set<int> vals;
+ const key_view_type<const unordered_map<int, std::string>>::type const_view =
+ key_view(const_map);
+ for (iterator_first<unordered_map<int, std::string>::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<int> my_set;
+ my_set.insert(1);
+ my_set.insert(0);
+ my_set.insert(2);
+
+ typedef iterator_view_helper<unordered_set<int>, unordered_set<int>::iterator,
+ unordered_set<int>::const_iterator>
+ SetView;
+ SetView set_view(my_set);
+ unordered_set<int> 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<int> my_set;
+ my_set.insert(1);
+ my_set.insert(0);
+ my_set.insert(2);
+
+ typedef iterator_view_helper<unordered_set<int>, unordered_set<int>::iterator,
+ unordered_set<int>::const_iterator>
+ SetView;
+ SetView set_view(my_set);
+ unordered_set<int> 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<int, int> M;
+ M m;
+ const M& cm = m;
+
+ typedef key_view_type<M>::type KV;
+ typedef key_view_type<const M>::type KVC;
+ typedef value_view_type<M>::type VV;
+ typedef value_view_type<const M>::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<std::string, 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());
+ 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<int, int>()), IsEmpty());
+ EXPECT_THAT(key_view(FixedSizeContainer<2, int, int>()), Not(IsEmpty()));
+}
+
+TEST_F(IteratorAdaptorTest, View_SizeIs) {
+ EXPECT_THAT(key_view(std::map<int, int>()), SizeIs(0));
+ EXPECT_THAT(key_view(FixedSizeContainer<2, int, int>()), SizeIs(2));
+}
+
+TEST_F(IteratorAdaptorTest, View_Pointwise) {
+ typedef std::map<int, std::string> MapType;
+ MapType my_map;
+ my_map[0] = "a";
+ my_map[1] = "b";
+ my_map[2] = "c";
+
+ std::vector<std::string> 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<int*> 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<int*> cc(c);
+ EXPECT_THAT(deref_view(cc), ElementsAre(2, 1));
+}
+
+TEST_F(IteratorAdaptorTest, ConstDerefView) {
+ typedef std::vector<const std::string*> 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<int, int*> 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<int, int*> cc(c);
+ EXPECT_THAT(deref_second_view(cc), ElementsAre(2, 1));
+}
+
+TEST_F(IteratorAdaptorTest, ConstDerefSecondView) {
+ typedef std::map<int, const std::string*> 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 <class T>
+std::vector<int> ToVec(const T& t) {
+ return std::vector<int>(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<int> vec(arr, arr_end);
+ const std::vector<int> 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<int*> v;
+ const std::vector<int*>& 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<int*> PtrsToMutable;
+ typedef iterator_ptr<PtrsToMutable::const_iterator> ConstIter;
+ EXPECT_TRUE((std::is_same<ConstIter::reference, const int&>::value));
+ EXPECT_TRUE(IsConst<ConstIter::reference>::value);
+
+ typedef iterator_ptr<PtrsToMutable::iterator> Iter;
+ EXPECT_TRUE((std::is_same<Iter::reference, int&>::value));
+ EXPECT_FALSE(IsConst<Iter::reference>::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<int> 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<X, int> Val;
+ InlineStorageIter<Val> iter;
+ iterator_first<InlineStorageIter<Val>> iter2(iter);
+ EXPECT_EQ(&iter2.base()->first, &*iter2);
+ EXPECT_EQ(&iter2.base()->first.d, &iter2->d);
+}
+
+TEST_F(IteratorAdaptorTest, BaseIterDanglingRefSecond) {
+ typedef std::pair<int, X> Val;
+ InlineStorageIter<Val> iter;
+ iterator_second<InlineStorageIter<Val>> 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
new file mode 100644
index 0000000..973b174
--- /dev/null
+++ b/Firestore/core/test/firebase/firestore/util/log_test.cc
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+#include "Firestore/core/src/firebase/firestore/util/log.h"
+
+#include "gtest/gtest.h"
+
+namespace firebase {
+namespace firestore {
+namespace util {
+
+// When running against the log_apple.mm implementation (backed by FIRLogger)
+// this test can fail if debug_mode gets persisted in the user defaults. Check
+// for defaults getting in your way with
+//
+// defaults read firebase_firestore_util_log_apple_test
+//
+// You can fix it with:
+//
+// defaults write firebase_firestore_util_log_apple_test
+// /google/firebase/debug_mode NO
+TEST(Log, SetAndGet) {
+ LogSetLevel(kLogLevelVerbose);
+
+ LogSetLevel(kLogLevelDebug);
+ EXPECT_EQ(kLogLevelDebug, LogGetLevel());
+
+ LogSetLevel(kLogLevelInfo);
+ EXPECT_EQ(kLogLevelInfo, LogGetLevel());
+
+ LogSetLevel(kLogLevelWarning);
+ EXPECT_EQ(kLogLevelWarning, LogGetLevel());
+
+ LogSetLevel(kLogLevelError);
+ EXPECT_EQ(kLogLevelError, LogGetLevel());
+}
+
+TEST(Log, LogAllKinds) {
+ LogDebug("test debug logging %d", 1);
+ LogInfo("test info logging %d", 2);
+ LogWarning("test warning logging %d", 3);
+ LogError("test error logging %d", 4);
+ LogMessage(kLogLevelError, "test va-args %s %c %d", "abc", ':', 123);
+}
+
+} // namespace util
+} // namespace firestore
+} // namespace firebase
diff --git a/Firestore/Port/ordered_code_test.cc b/Firestore/core/test/firebase/firestore/util/ordered_code_test.cc
index 0a339fc..fd2ce83 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 <float.h>
-// #include <stddef.h>
#include <iostream>
#include <limits>
-#include "base/logging.h"
-#include "testing/base/public/gunit.h"
-#include <leveldb/db.h>
-#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<char>(rnd->Uniform(256));
}
return x;
}
@@ -82,7 +41,7 @@ static std::string RandomString(ACMRandom* rnd, int len) {
template <typename T>
static void OCWriteIncreasing(std::string* dest, const T& val);
template <typename T>
-static bool OCReadIncreasing(Slice* src, T* result);
+static bool OCReadIncreasing(absl::string_view* src, T* result);
// Read/WriteIncreasing<std::string>
template <>
@@ -90,7 +49,8 @@ void OCWriteIncreasing<std::string>(std::string* dest, const std::string& val) {
OrderedCode::WriteString(dest, val);
}
template <>
-bool OCReadIncreasing<std::string>(Slice* src, std::string* result) {
+bool OCReadIncreasing<std::string>(absl::string_view* src,
+ std::string* result) {
return OrderedCode::ReadString(src, result);
}
@@ -100,7 +60,7 @@ void OCWriteIncreasing<uint64_t>(std::string* dest, const uint64_t& val) {
OrderedCode::WriteNumIncreasing(dest, val);
}
template <>
-bool OCReadIncreasing<uint64_t>(Slice* src, uint64_t* result) {
+bool OCReadIncreasing<uint64_t>(absl::string_view* src, uint64_t* result) {
return OrderedCode::ReadNumIncreasing(src, result);
}
@@ -112,12 +72,13 @@ void OCWriteIncreasing<int64_t>(std::string* dest, const int64_t& val) {
OrderedCode::WriteSignedNumIncreasing(dest, val);
}
template <>
-bool OCReadIncreasing<int64_t>(Slice* src, int64_t* result) {
+bool OCReadIncreasing<int64_t>(absl::string_view* src, int64_t* result) {
return OrderedCode::ReadSignedNumIncreasing(src, result);
}
template <typename T>
std::string OCWrite(T val, Direction direction) {
+ EXPECT_EQ(INCREASING, direction); // DECREASING never implemented.
std::string result;
OCWriteIncreasing<T>(&result, val);
return result;
@@ -125,11 +86,13 @@ std::string OCWrite(T val, Direction direction) {
template <typename T>
void OCWriteToString(std::string* result, T val, Direction direction) {
+ EXPECT_EQ(INCREASING, direction); // DECREASING never implemented.
OCWriteIncreasing<T>(result, val);
}
template <typename T>
-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<T>(s, val);
}
@@ -139,16 +102,16 @@ bool OCRead(Slice* s, T* val, Direction direction) {
template <typename T>
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<T>(&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<T>(&s, NULL, d));
+ EXPECT_EQ(s, a.substr(0, i));
}
- Slice s(a);
+ absl::string_view s(a);
T v;
- CHECK(OCRead<T>(&s, &v, d));
- CHECK(s.empty());
+ EXPECT_TRUE(OCRead<T>(&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<U>(&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 <typename T>
static void TestNumbers(T multiplier) {
- for (int j = 0; j < 2; ++j) {
+ for (int j = 0; j < 1; ++j) {
const Direction d = static_cast<Direction>(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<T>::max()) {
TestWriteRead(d, multiplier * (x + 1));
- } else if (multiplier < 0 && multiplier == -1) {
+ } else if (multiplier < 0 && static_cast<int64_t>(multiplier) == -1) {
TestWriteRead(d, -x - 1);
}
}
- ACMRandom rnd(301);
+ SecureRandom rnd; // Generate 32bit pseudo-random integer.
for (int bits = 1; bits <= std::numeric_limits<T>().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<T>((static_cast<uint64_t>(rnd()) << 32 |
+ static_cast<uint64_t>(rnd())) &
+ mask);
TestWriteRead(d, multiplier * x);
- T y = rnd.Next64() & mask;
+ T y = static_cast<T>((static_cast<uint64_t>(rnd()) << 32 |
+ static_cast<uint64_t>(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<T>(num, d);
std::string strplus1 = OCWrite<T>(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<T>(num, d);
std::string strplus1 = OCWrite<T>(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<size_t>(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<char>(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<char>(b0);
+ buf[start_offset + 1] = static_cast<char>(b1);
+ buf[start_offset + 2] = static_cast<char>(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<uint64_t>(1); }
+TEST(OrderedCodeUint64, EncodeDecode) {
+ TestNumbers<uint64_t>(1);
+}
-TEST(Uint64, Ordering) { TestNumberOrdering<uint64_t>(); }
+TEST(OrderedCodeUint64, Ordering) {
+ TestNumberOrdering<uint64_t>();
+}
-TEST(Int64, EncodeDecode) {
+TEST(OrderedCodeInt64, EncodeDecode) {
TestNumbers<int64_t>(1);
TestNumbers<int64_t>(-1);
}
-TEST(Int64, Ordering) { TestNumberOrdering<int64_t>(); }
+TEST(OrderedCodeInt64, Ordering) {
+ TestNumberOrdering<int64_t>();
+}
// 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 <typename T>
static void TestInvalidEncoding(Direction d, const std::string& s) {
- Slice p(s);
+ absl::string_view p(s);
EXPECT_FALSE(OCRead<T>(&p, static_cast<T*>(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<size_t>(n), non_minimal.length());
EXPECT_NE(OCWrite<uint64_t>(0, INCREASING), non_minimal);
- if (DEBUG_MODE) {
- Slice s(non_minimal);
- EXPECT_DEATH_IF_SUPPORTED(OrderedCode::ReadNumIncreasing(&s, NULL),
- "ssertion failed");
- } else {
- TestRead<uint64_t>(INCREASING, non_minimal);
- }
+
+#if defined(NDEBUG)
+ TestRead<uint64_t>(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<char>(rnd.Uniform(256)) & ~*header.rbegin()) +
+ RandomString(&rnd, n - static_cast<int>(header.length()) - 1);
+ EXPECT_EQ(static_cast<size_t>(n), non_minimal.length());
EXPECT_NE(OCWrite<int64_t>(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<int64_t>(INCREASING, non_minimal);
- }
+
+#if defined(NDEBUG)
+ TestRead<int64_t>(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<Direction>(i);
for (int len = 0; len < 256; len++) {
@@ -457,48 +433,48 @@ TEST(String, EncodeDecode) {
OCWriteToString<std::string>(&out, b, d);
std::string a2, b2, dummy;
- Slice s = out;
- Slice s2 = out;
- CHECK(OCRead<std::string>(&s, &a2, d));
- CHECK(OCRead<std::string>(&s2, NULL, d));
- CHECK_EQ(s, s2);
-
- CHECK(OCRead<std::string>(&s, &b2, d));
- CHECK(OCRead<std::string>(&s2, NULL, d));
- CHECK_EQ(s, s2);
-
- CHECK(!OCRead<std::string>(&s, &dummy, d));
- CHECK(!OCRead<std::string>(&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<std::string>(&s, &a2, d));
+ EXPECT_TRUE(OCRead<std::string>(&s2, NULL, d));
+ EXPECT_EQ(s, s2);
+
+ EXPECT_TRUE(OCRead<std::string>(&s, &b2, d));
+ EXPECT_TRUE(OCRead<std::string>(&s2, NULL, d));
+ EXPECT_EQ(s, s2);
+
+ EXPECT_TRUE(!OCRead<std::string>(&s, &dummy, d));
+ EXPECT_TRUE(!OCRead<std::string>(&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 f96f3de..0b7a51b 100644
--- a/Firestore/core/test/firebase/firestore/util/secure_random_test.cc
+++ b/Firestore/core/test/firebase/firestore/util/secure_random_test.cc
@@ -16,7 +16,7 @@
#include "Firestore/core/src/firebase/firestore/util/secure_random.h"
-#include "gtest/gtest.h"
+#include <gtest/gtest.h>
using firebase::firestore::util::SecureRandom;
@@ -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
new file mode 100644
index 0000000..085be84
--- /dev/null
+++ b/Firestore/core/test/firebase/firestore/util/string_printf_test.cc
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+#include "Firestore/core/src/firebase/firestore/util/string_printf.h"
+
+#include <gtest/gtest.h>
+
+namespace firebase {
+namespace firestore {
+namespace util {
+
+TEST(StringPrintf, Empty) {
+ EXPECT_EQ("", StringPrintf(""));
+ EXPECT_EQ("", StringPrintf("%s", std::string().c_str()));
+ EXPECT_EQ("", StringPrintf("%s", ""));
+}
+
+TEST(StringAppendFTest, Empty) {
+ std::string value("Hello");
+ const char* empty = "";
+ StringAppendF(&value, "%s", empty);
+ EXPECT_EQ("Hello", value);
+}
+
+TEST(StringAppendFTest, EmptyString) {
+ std::string value("Hello");
+ StringAppendF(&value, "%s", "");
+ EXPECT_EQ("Hello", value);
+}
+
+TEST(StringAppendFTest, String) {
+ std::string value("Hello");
+ StringAppendF(&value, " %s", "World");
+ EXPECT_EQ("Hello World", value);
+}
+
+TEST(StringAppendFTest, Int) {
+ std::string value("Hello");
+ StringAppendF(&value, " %d", 123);
+ EXPECT_EQ("Hello 123", value);
+}
+
+TEST(StringPrintf, DontOverwriteErrno) {
+ // Check that errno isn't overwritten unless we're printing
+ // something significantly larger than what people are normally
+ // printing in their badly written PLOG() statements.
+ errno = ECHILD;
+ std::string value = StringPrintf("Hello, %s!", "World");
+ EXPECT_EQ(ECHILD, errno);
+}
+
+TEST(StringPrintf, LargeBuf) {
+ // Check that the large buffer is handled correctly.
+ size_t n = 2048;
+ char* buf = new char[n + 1];
+ memset(buf, ' ', n);
+ buf[n] = 0;
+ std::string value = StringPrintf("%s", buf);
+ EXPECT_EQ(buf, value);
+ delete[] buf;
+}
+
+} // namespace util
+} // namespace firestore
+} // namespace firebase
diff --git a/Firestore/Port/string_util_test.cc b/Firestore/core/test/firebase/firestore/util/string_util_test.cc
index ac8ce56..f94596f 100644
--- a/Firestore/Port/string_util_test.cc
+++ b/Firestore/core/test/firebase/firestore/util/string_util_test.cc
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-#include "Firestore/Port/string_util.h"
+#include "Firestore/core/src/firebase/firestore/util/string_util.h"
-#include "leveldb/db.h"
+#include <gtest/gtest.h>
-#include "gtest/gtest.h"
+namespace firebase {
+namespace firestore {
+namespace util {
-using Firestore::PrefixSuccessor;
-using Firestore::ImmediateSuccessor;
-using leveldb::Slice;
-
-TEST(Util, PrefixSuccessor) {
+TEST(StringUtil, PrefixSuccessor) {
EXPECT_EQ(PrefixSuccessor("a"), "b");
EXPECT_EQ(PrefixSuccessor("aaAA"), "aaAB");
EXPECT_EQ(PrefixSuccessor("aaa\xff"), "aab");
@@ -34,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/third_party/Immutable/FSTArraySortedDictionary.m b/Firestore/third_party/Immutable/FSTArraySortedDictionary.m
index ad1d68a..e9325a7 100644
--- a/Firestore/third_party/Immutable/FSTArraySortedDictionary.m
+++ b/Firestore/third_party/Immutable/FSTArraySortedDictionary.m
@@ -142,19 +142,6 @@ NS_ASSUME_NONNULL_BEGIN
}
}
-- (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 a2264ec..cbb4da3 100644
--- a/Firestore/third_party/Immutable/FSTImmutableSortedDictionary.h
+++ b/Firestore/third_party/Immutable/FSTImmutableSortedDictionary.h
@@ -71,14 +71,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.
*
* @param key The key to return the index for.
diff --git a/Firestore/third_party/Immutable/FSTImmutableSortedDictionary.m b/Firestore/third_party/Immutable/FSTImmutableSortedDictionary.m
index 6e78961..87c21a5 100644
--- a/Firestore/third_party/Immutable/FSTImmutableSortedDictionary.m
+++ b/Firestore/third_party/Immutable/FSTImmutableSortedDictionary.m
@@ -82,10 +82,6 @@ const int kSortedDictionaryArrayToRBTreeSizeThreshold = 25;
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 d0f9906..b432f98 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 a23068e..1b63c2c 100644
--- a/Firestore/third_party/Immutable/FSTImmutableSortedSet.m
+++ b/Firestore/third_party/Immutable/FSTImmutableSortedSet.m
@@ -76,10 +76,6 @@ NS_ASSUME_NONNULL_BEGIN
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 b3e691f..ec0c483 100644
--- a/Firestore/third_party/Immutable/FSTTreeSortedDictionary.m
+++ b/Firestore/third_party/Immutable/FSTTreeSortedDictionary.m
@@ -87,36 +87,6 @@ NS_ASSUME_NONNULL_BEGIN
return nil;
}
-- (nullable id)predecessorKey:(id)key {
- NSComparisonResult cmp;
- id<FSTLLRBNode> node = self.root;
- id<FSTLLRBNode> 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<FSTLLRBNode> node = self.root;
diff --git a/Firestore/third_party/Immutable/Tests/FSTArraySortedDictionaryTests.m b/Firestore/third_party/Immutable/Tests/FSTArraySortedDictionaryTests.m
index a799686..bf17496 100644
--- a/Firestore/third_party/Immutable/Tests/FSTArraySortedDictionaryTests.m
+++ b/Firestore/third_party/Immutable/Tests/FSTArraySortedDictionaryTests.m
@@ -252,25 +252,6 @@
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)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 344efba..3e06b30 100644
--- a/Firestore/third_party/Immutable/Tests/FSTTreeSortedDictionaryTests.m
+++ b/Firestore/third_party/Immutable/Tests/FSTTreeSortedDictionaryTests.m
@@ -437,25 +437,6 @@
}
}
-- (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 6d3789e..4a23b70 100644
--- a/Firestore/third_party/abseil-cpp/CMakeLists.txt
+++ b/Firestore/third_party/abseil-cpp/CMakeLists.txt
@@ -72,7 +72,6 @@ endif()
# commented: used only for standalone test
#add_subdirectory(cctz)
#add_subdirectory(googletest)
-check_target(${ABSL_CCTZ_TARGET})
## check targets
if(BUILD_TESTING)
diff --git a/Firestore/third_party/abseil-cpp/absl/CMakeLists.txt b/Firestore/third_party/abseil-cpp/absl/CMakeLists.txt
index 689f64e..e7b5139 100644
--- a/Firestore/third_party/abseil-cpp/absl/CMakeLists.txt
+++ b/Firestore/third_party/abseil-cpp/absl/CMakeLists.txt
@@ -17,14 +17,7 @@
add_subdirectory(base)
-add_subdirectory(algorithm)
-add_subdirectory(container)
-add_subdirectory(debugging)
add_subdirectory(memory)
add_subdirectory(meta)
add_subdirectory(numeric)
add_subdirectory(strings)
-add_subdirectory(synchronization)
-add_subdirectory(time)
-add_subdirectory(types)
-add_subdirectory(utility)
diff --git a/Firestore/third_party/abseil-cpp/absl/base/CMakeLists.txt b/Firestore/third_party/abseil-cpp/absl/base/CMakeLists.txt
index 4b7b53a..cfa119a 100644
--- a/Firestore/third_party/abseil-cpp/absl/base/CMakeLists.txt
+++ b/Firestore/third_party/abseil-cpp/absl/base/CMakeLists.txt
@@ -16,8 +16,6 @@
list(APPEND BASE_PUBLIC_HEADERS
"attributes.h"
- "call_once.h"
- "casts.h"
"config.h"
"dynamic_annotations.h"
"log_severity.h"
@@ -25,50 +23,21 @@ list(APPEND BASE_PUBLIC_HEADERS
"optimization.h"
"policy_checks.h"
"port.h"
- "thread_annotations.h"
)
list(APPEND BASE_INTERNAL_HEADERS
"internal/atomic_hook.h"
- "internal/cycleclock.h"
"internal/endian.h"
- "internal/exception_testing.h"
- "internal/exception_safety_testing.h"
- "internal/identity.h"
- "internal/invoke.h"
- "internal/inline_variable.h"
- "internal/low_level_alloc.h"
- "internal/low_level_scheduling.h"
- "internal/malloc_extension.h"
- "internal/malloc_hook_c.h"
- "internal/malloc_hook.h"
- "internal/malloc_hook_invoke.h"
- "internal/per_thread_tls.h"
- "internal/pretty_function.h"
"internal/raw_logging.h"
- "internal/scheduling_mode.h"
- "internal/spinlock.h"
- "internal/spinlock_wait.h"
- "internal/sysinfo.h"
- "internal/thread_identity.h"
"internal/throw_delegate.h"
- "internal/tsan_mutex_interface.h"
"internal/unaligned_access.h"
- "internal/unscaledcycleclock.h"
)
# absl_base main library
list(APPEND BASE_SRC
- "internal/cycleclock.cc"
"internal/raw_logging.cc"
- "internal/spinlock.cc"
- "internal/sysinfo.cc"
- "internal/thread_identity.cc"
- "internal/unscaledcycleclock.cc"
- "internal/low_level_alloc.cc"
- "internal/malloc_hook.cc"
${BASE_PUBLIC_HEADERS}
${BASE_INTERNAL_HEADERS}
)
@@ -80,25 +49,10 @@ absl_library(
${BASE_SRC}
PUBLIC_LIBRARIES
absl_dynamic_annotations
- absl_spinlock_wait
EXPORT_NAME
base
)
-# malloc extension library
-set(MALLOC_EXTENSION_SRC "internal/malloc_extension.cc")
-set(MALLOC_EXTENSION_PUBLIC_LIBRARIES absl::base)
-
-absl_library(
- TARGET
- absl_malloc_extension
- SOURCES
- ${MALLOC_EXTENSION_SRC}
- PUBLIC_LIBRARIES
- ${MALLOC_EXTENSION_PUBLIC_LIBRARIES}
- EXPORT_NAME
- malloc_extension
-)
# throw delegate library
set(THROW_DELEGATE_SRC "internal/throw_delegate.cc")
@@ -116,28 +70,6 @@ absl_library(
throw_delegate
)
-if(BUILD_TESTING)
- # exception-safety testing library
- set(EXCEPTION_SAFETY_TESTING_SRC "internal/exception_safety_testing.cc")
- set(EXCEPTION_SAFETY_TESTING_PUBLIC_LIBRARIES
- ${ABSL_TEST_COMMON_LIBRARIES}
- absl::base
- absl::memory
- absl::meta
- absl::strings
- absl::types
- )
-
-absl_library(
- TARGET
- absl_base_internal_exception_safety_testing
- SOURCES
- ${EXCEPTION_SAFETY_TESTING_SRC}
- PUBLIC_LIBRARIES
- ${EXCEPTION_SAFETY_TESTING_PUBLIC_LIBRARIES}
-)
-endif()
-
# dynamic_annotations library
set(DYNAMIC_ANNOTATIONS_SRC "dynamic_annotations.cc")
@@ -150,140 +82,10 @@ absl_library(
)
-# spinlock_wait library
-set(SPINLOCK_WAIT_SRC "internal/spinlock_wait.cc")
-
-absl_library(
- TARGET
- absl_spinlock_wait
- SOURCES
- ${SPINLOCK_WAIT_SRC}
-)
-
-
-# malloc_internal library
-list(APPEND MALLOC_INTERNAL_SRC
- "internal/low_level_alloc.cc"
- "internal/malloc_hook.cc"
- "internal/malloc_hook_mmap_linux.inc"
-)
-
-absl_library(
- TARGET
- absl_malloc_internal
- SOURCES
- ${MALLOC_INTERNAL_SRC}
- PUBLIC_LIBRARIES
- absl_dynamic_annotations
-)
-
-
-
#
## TESTS
#
-# call once test
-set(CALL_ONCE_TEST_SRC "call_once_test.cc")
-set(CALL_ONCE_TEST_PUBLIC_LIBRARIES absl::base absl::synchronization)
-
-absl_test(
- TARGET
- call_once_test
- SOURCES
- ${CALL_ONCE_TEST_SRC}
- PUBLIC_LIBRARIES
- ${CALL_ONCE_TEST_PUBLIC_LIBRARIES}
-)
-
-
-# test bit_cast_test
-set(BIT_CAST_TEST_SRC "bit_cast_test.cc")
-
-absl_test(
- TARGET
- bit_cast_test
- SOURCES
- ${BIT_CAST_TEST_SRC}
-)
-
-
-# test absl_throw_delegate_test
-set(THROW_DELEGATE_TEST_SRC "throw_delegate_test.cc")
-set(THROW_DELEGATE_TEST_PUBLIC_LIBRARIES absl::base absl_throw_delegate)
-
-absl_test(
- TARGET
- throw_delegate_test
- SOURCES
- ${THROW_DELEGATE_TEST_SRC}
- PUBLIC_LIBRARIES
- ${THROW_DELEGATE_TEST_PUBLIC_LIBRARIES}
-)
-
-
-# test invoke_test
-set(INVOKE_TEST_SRC "invoke_test.cc")
-set(INVOKE_TEST_PUBLIC_LIBRARIES absl::strings)
-
-absl_test(
- TARGET
- invoke_test
- SOURCES
- ${INVOKE_TEST_SRC}
- PUBLIC_LIBRARIES
- ${INVOKE_TEST_PUBLIC_LIBRARIES}
-)
-
-
-# test inline_variable_test
-list(APPEND INLINE_VARIABLE_TEST_SRC
- "internal/inline_variable_testing.h"
- "inline_variable_test.cc"
- "inline_variable_test_a.cc"
- "inline_variable_test_b.cc"
-)
-
-set(INLINE_VARIABLE_TEST_PUBLIC_LIBRARIES absl::base)
-
-absl_test(
- TARGET
- inline_variable_test
- SOURCES
- ${INLINE_VARIABLE_TEST_SRC}
- PUBLIC_LIBRARIES
- ${INLINE_VARIABLE_TEST_PUBLIC_LIBRARIES}
-)
-
-
-# test spinlock_test_common
-set(SPINLOCK_TEST_COMMON_SRC "spinlock_test_common.cc")
-set(SPINLOCK_TEST_COMMON_PUBLIC_LIBRARIES absl::base absl::synchronization)
-
-absl_test(
- TARGET
- spinlock_test_common
- SOURCES
- ${SPINLOCK_TEST_COMMON_SRC}
- PUBLIC_LIBRARIES
- ${SPINLOCK_TEST_COMMON_PUBLIC_LIBRARIES}
-)
-
-
-# test spinlock_test
-set(SPINLOCK_TEST_SRC "spinlock_test_common.cc")
-set(SPINLOCK_TEST_PUBLIC_LIBRARIES absl::base absl::synchronization)
-
-absl_test(
- TARGET
- spinlock_test
- SOURCES
- ${SPINLOCK_TEST_SRC}
- PUBLIC_LIBRARIES
- ${SPINLOCK_TEST_PUBLIC_LIBRARIES}
-)
-
-
# test endian_test
set(ENDIAN_TEST_SRC "internal/endian_test.cc")
@@ -297,7 +99,7 @@ absl_test(
# test config_test
set(CONFIG_TEST_SRC "config_test.cc")
-set(CONFIG_TEST_PUBLIC_LIBRARIES absl::base absl::synchronization)
+set(CONFIG_TEST_PUBLIC_LIBRARIES absl::base)
absl_test(
TARGET
config_test
@@ -320,80 +122,3 @@ absl_test(
PUBLIC_LIBRARIES
${RAW_LOGGING_TEST_PUBLIC_LIBRARIES}
)
-
-
-# test sysinfo_test
-set(SYSINFO_TEST_SRC "internal/sysinfo_test.cc")
-set(SYSINFO_TEST_PUBLIC_LIBRARIES absl::base absl::synchronization)
-
-absl_test(
- TARGET
- sysinfo_test
- SOURCES
- ${SYSINFO_TEST_SRC}
- PUBLIC_LIBRARIES
- ${SYSINFO_TEST_PUBLIC_LIBRARIES}
-)
-
-
-# test low_level_alloc_test
-set(LOW_LEVEL_ALLOC_TEST_SRC "internal/low_level_alloc_test.cc")
-set(LOW_LEVEL_ALLOC_TEST_PUBLIC_LIBRARIES absl::base)
-
-absl_test(
- TARGET
- low_level_alloc_test
- SOURCES
- ${LOW_LEVEL_ALLOC_TEST_SRC}
- PUBLIC_LIBRARIES
- ${LOW_LEVEL_ALLOC_TEST_PUBLIC_LIBRARIES}
-)
-
-
-# test thread_identity_test
-set(THREAD_IDENTITY_TEST_SRC "internal/thread_identity_test.cc")
-set(THREAD_IDENTITY_TEST_PUBLIC_LIBRARIES absl::base absl::synchronization)
-
-absl_test(
- TARGET
- thread_identity_test
- SOURCES
- ${THREAD_IDENTITY_TEST_SRC}
- PUBLIC_LIBRARIES
- ${THREAD_IDENTITY_TEST_PUBLIC_LIBRARIES}
-)
-
-#test exceptions_safety_testing_test
-set(EXCEPTION_SAFETY_TESTING_TEST_SRC "exception_safety_testing_test.cc")
-set(EXCEPTION_SAFETY_TESTING_TEST_PUBLIC_LIBRARIES absl::base absl::memory absl::meta absl::strings absl::optional)
-
-absl_test(
- TARGET
- absl_exception_safety_testing_test
- SOURCES
- ${EXCEPTION_SAFETY_TESTING_TEST_SRC}
- PUBLIC_LIBRARIES
- ${EXCEPTION_SAFETY_TESTING_TEST_PUBLIC_LIBRARIES}
- PRIVATE_COMPILE_FLAGS
- ${ABSL_EXCEPTIONS_FLAG}
-)
-
-# test absl_malloc_extension_system_malloc_test
-set(MALLOC_EXTENSION_SYSTEM_MALLOC_TEST_SRC "internal/malloc_extension_test.cc")
-set(MALLOC_EXTENSION_SYSTEM_MALLOC_TEST_PUBLIC_LIBRARIES absl::base absl_malloc_extension)
-set(MALLOC_EXTENSION_SYSTEM_MALLOC_TEST_PRIVATE_COMPILE_FLAGS "-DABSL_MALLOC_EXTENSION_TEST_ALLOW_MISSING_EXTENSION=1")
-
-absl_test(
- TARGET
- absl_malloc_extension_system_malloc_test
- SOURCES
- ${MALLOC_EXTENSION_SYSTEM_MALLOC_TEST_SRC}
- PUBLIC_LIBRARIES
- ${MALLOC_EXTENSION_SYSTEM_MALLOC_TEST_PUBLIC_LIBRARIES}
- PRIVATE_COMPILE_FLAGS
- ${MALLOC_EXTENSION_SYSTEM_MALLOC_TEST_PRIVATE_COMPILE_FLAGS}
-)
-
-
-
-
diff --git a/Firestore/third_party/abseil-cpp/absl/base/config_test.cc b/Firestore/third_party/abseil-cpp/absl/base/config_test.cc
index c839712..4e6dd6a 100644
--- a/Firestore/third_party/abseil-cpp/absl/base/config_test.cc
+++ b/Firestore/third_party/abseil-cpp/absl/base/config_test.cc
@@ -17,7 +17,6 @@
#include <cstdint>
#include "gtest/gtest.h"
-#include "absl/synchronization/internal/thread_pool.h"
namespace {
@@ -41,20 +40,4 @@ TEST(ConfigTest, Endianness) {
#endif
}
-#if defined(ABSL_HAVE_THREAD_LOCAL)
-TEST(ConfigTest, ThreadLocal) {
- static thread_local int mine_mine_mine = 16;
- EXPECT_EQ(16, mine_mine_mine);
- {
- absl::synchronization_internal::ThreadPool pool(1);
- pool.Schedule([&] {
- EXPECT_EQ(16, mine_mine_mine);
- mine_mine_mine = 32;
- EXPECT_EQ(32, mine_mine_mine);
- });
- }
- EXPECT_EQ(16, mine_mine_mine);
-}
-#endif
-
} // namespace
diff --git a/Firestore/third_party/abseil-cpp/absl/base/macros.h b/Firestore/third_party/abseil-cpp/absl/base/macros.h
index 5ae0f05..c81f8c6 100644
--- a/Firestore/third_party/abseil-cpp/absl/base/macros.h
+++ b/Firestore/third_party/abseil-cpp/absl/base/macros.h
@@ -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/meta/CMakeLists.txt b/Firestore/third_party/abseil-cpp/absl/meta/CMakeLists.txt
index d56fced..a25dd61 100644
--- a/Firestore/third_party/abseil-cpp/absl/meta/CMakeLists.txt
+++ b/Firestore/third_party/abseil-cpp/absl/meta/CMakeLists.txt
@@ -44,6 +44,3 @@ absl_test(
PUBLIC_LIBRARIES
${TYPE_TRAITS_TEST_PUBLIC_LIBRARIES} absl::meta
)
-
-
-
diff --git a/Firestore/third_party/abseil-cpp/absl/numeric/int128_test.cc b/Firestore/third_party/abseil-cpp/absl/numeric/int128_test.cc
index d674cb1..b9d3647 100644
--- a/Firestore/third_party/abseil-cpp/absl/numeric/int128_test.cc
+++ b/Firestore/third_party/abseil-cpp/absl/numeric/int128_test.cc
@@ -22,7 +22,6 @@
#include <vector>
#include "gtest/gtest.h"
-#include "absl/base/internal/cycleclock.h"
#include "absl/meta/type_traits.h"
#if defined(_MSC_VER) && _MSC_VER == 1900
diff --git a/Firestore/third_party/abseil-cpp/absl/strings/CMakeLists.txt b/Firestore/third_party/abseil-cpp/absl/strings/CMakeLists.txt
index 83cb934..a31f05d 100644
--- a/Firestore/third_party/abseil-cpp/absl/strings/CMakeLists.txt
+++ b/Firestore/third_party/abseil-cpp/absl/strings/CMakeLists.txt
@@ -48,7 +48,6 @@ list(APPEND STRINGS_SRC
"ascii.cc"
"escaping.cc"
"internal/memutil.cc"
- "internal/memutil.h"
"internal/utf8.cc"
"internal/ostringstream.cc"
"match.cc"