diff options
Diffstat (limited to 'Firestore')
294 files changed, 11336 insertions, 5082 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 (×tamp_value_) Timestamp(0, 0); + break; + case Type::ServerTimestamp: + new (&server_timestamp_value_) ServerTimestamp(); + break; + case Type::String: + new (&string_value_) std::string(); + break; + case Type::Blob: + // Do not even bother to allocate a new array of size 0. + new (&blob_value_) std::vector<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..fb158fa 100644 --- a/Firestore/third_party/abseil-cpp/absl/CMakeLists.txt +++ b/Firestore/third_party/abseil-cpp/absl/CMakeLists.txt @@ -17,14 +17,5 @@ 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/strings/CMakeLists.txt b/Firestore/third_party/abseil-cpp/absl/strings/CMakeLists.txt index 83cb934..070bf4f 100644 --- a/Firestore/third_party/abseil-cpp/absl/strings/CMakeLists.txt +++ b/Firestore/third_party/abseil-cpp/absl/strings/CMakeLists.txt @@ -17,28 +17,14 @@ list(APPEND STRINGS_PUBLIC_HEADERS "ascii.h" - "escaping.h" "match.h" - "numbers.h" - "str_cat.h" "string_view.h" - "strip.h" - "str_join.h" - "str_replace.h" - "str_split.h" - "substitute.h" ) list(APPEND STRINGS_INTERNAL_HEADERS - "internal/char_map.h" "internal/memutil.h" - "internal/ostringstream.h" "internal/resize_uninitialized.h" - "internal/stl_type_traits.h" - "internal/str_join_internal.h" - "internal/str_split_internal.h" - "internal/utf8.h" ) @@ -46,18 +32,9 @@ list(APPEND STRINGS_INTERNAL_HEADERS # add string library list(APPEND STRINGS_SRC "ascii.cc" - "escaping.cc" "internal/memutil.cc" - "internal/memutil.h" - "internal/utf8.cc" - "internal/ostringstream.cc" "match.cc" - "numbers.cc" - "str_cat.cc" - "str_replace.cc" - "str_split.cc" "string_view.cc" - "substitute.cc" ${STRINGS_PUBLIC_HEADERS} ${STRINGS_INTERNAL_HEADERS} ) @@ -93,20 +70,6 @@ absl_test( ) -# test escaping_test -set(ESCAPING_TEST_SRC "escaping_test.cc") -set(ESCAPING_TEST_PUBLIC_LIBRARIES absl::strings absl::base) - -absl_test( - TARGET - escaping_test - SOURCES - ${ESCAPING_TEST_SRC} - PUBLIC_LIBRARIES - ${ESCAPING_TEST_PUBLIC_LIBRARIES} -) - - # test ascii_test set(ASCII_TEST_SRC "ascii_test.cc") set(ASCII_TEST_PUBLIC_LIBRARIES absl::strings) @@ -135,20 +98,6 @@ absl_test( ) -# test utf8_test -set(UTF8_TEST_SRC "internal/utf8_test.cc") -set(UTF8_TEST_PUBLIC_LIBRARIES absl::strings absl::base) - -absl_test( - TARGET - utf8_test - SOURCES - ${UTF8_TEST_SRC} - PUBLIC_LIBRARIES - ${UTF8_TEST_PUBLIC_LIBRARIES} -) - - # test string_view_test set(STRING_VIEW_TEST_SRC "string_view_test.cc") set(STRING_VIEW_TEST_PUBLIC_LIBRARIES absl::strings absl_throw_delegate absl::base) @@ -163,62 +112,6 @@ absl_test( ) -# test substitute_test -set(SUBSTITUTE_TEST_SRC "substitute_test.cc") -set(SUBSTITUTE_TEST_PUBLIC_LIBRARIES absl::strings absl::base) - -absl_test( - TARGET - substitute_test - SOURCES - ${SUBSTITUTE_TEST_SRC} - PUBLIC_LIBRARIES - ${SUBSTITUTE_TEST_PUBLIC_LIBRARIES} -) - - -# test str_replace_test -set(STR_REPLACE_TEST_SRC "str_replace_test.cc") -set(STR_REPLACE_TEST_PUBLIC_LIBRARIES absl::strings absl::base absl_throw_delegate) - -absl_test( - TARGET - str_replace_test - SOURCES - ${STR_REPLACE_TEST_SRC} - PUBLIC_LIBRARIES - ${STR_REPLACE_TEST_PUBLIC_LIBRARIES} -) - - -# test str_split_test -set(STR_SPLIT_TEST_SRC "str_split_test.cc") -set(STR_SPLIT_TEST_PUBLIC_LIBRARIES absl::strings absl::base absl_throw_delegate) - -absl_test( - TARGET - str_split_test - SOURCES - ${STR_SPLIT_TEST_SRC} - PUBLIC_LIBRARIES - ${STR_SPLIT_TEST_PUBLIC_LIBRARIES} -) - - -# test ostringstream_test -set(OSTRINGSTREAM_TEST_SRC "internal/ostringstream_test.cc") -set(OSTRINGSTREAM_TEST_PUBLIC_LIBRARIES absl::strings) - -absl_test( - TARGET - ostringstream_test - SOURCES - ${OSTRINGSTREAM_TEST_SRC} - PUBLIC_LIBRARIES - ${OSTRINGSTREAM_TEST_PUBLIC_LIBRARIES} -) - - # test resize_uninitialized_test set(RESIZE_UNINITIALIZED_TEST_SRC "internal/resize_uninitialized_test.cc") @@ -228,77 +121,3 @@ absl_test( SOURCES ${RESIZE_UNINITIALIZED_TEST_SRC} ) - - -# test str_join_test -set(STR_JOIN_TEST_SRC "str_join_test.cc") -set(STR_JOIN_TEST_PUBLIC_LIBRARIES absl::strings) - -absl_test( - TARGET - str_join_test - SOURCES - ${STR_JOIN_TEST_SRC} - PUBLIC_LIBRARIES - ${STR_JOIN_TEST_PUBLIC_LIBRARIES} -) - - -# test str_cat_test -set(STR_CAT_TEST_SRC "str_cat_test.cc") -set(STR_CAT_TEST_PUBLIC_LIBRARIES absl::strings) - -absl_test( - TARGET - str_cat_test - SOURCES - ${STR_CAT_TEST_SRC} - PUBLIC_LIBRARIES - ${STR_CAT_TEST_PUBLIC_LIBRARIES} -) - - -# test numbers_test -set(NUMBERS_TEST_SRC "numbers_test.cc") -set(NUMBERS_TEST_PUBLIC_LIBRARIES absl::strings) - -absl_test( - TARGET - numbers_test - SOURCES - ${NUMBERS_TEST_SRC} - PUBLIC_LIBRARIES - ${NUMBERS_TEST_PUBLIC_LIBRARIES} -) - - -# test strip_test -set(STRIP_TEST_SRC "strip_test.cc") -set(STRIP_TEST_PUBLIC_LIBRARIES absl::strings) - -absl_test( - TARGET - strip_test - SOURCES - ${STRIP_TEST_SRC} - PUBLIC_LIBRARIES - ${STRIP_TEST_PUBLIC_LIBRARIES} -) - - -# test char_map_test -set(CHAR_MAP_TEST_SRC "internal/char_map_test.cc") -set(CHAR_MAP_TEST_PUBLIC_LIBRARIES absl::strings) - -absl_test( - TARGET - char_map_test - SOURCES - ${CHAR_MAP_TEST_SRC} - PUBLIC_LIBRARIES - ${CHAR_MAP_TEST_PUBLIC_LIBRARIES} -) - - - - |