diff options
Diffstat (limited to 'Firestore')
130 files changed, 3133 insertions, 2583 deletions
diff --git a/Firestore/CHANGELOG.md b/Firestore/CHANGELOG.md index c97aa23..70bab4e 100644 --- a/Firestore/CHANGELOG.md +++ b/Firestore/CHANGELOG.md @@ -1,4 +1,9 @@ # Unreleased +- [fixed] Fixed an issue where `FirestoreSettings` would accept a concurrent + dispatch queue, but this configuration would trigger an assertion failure. + Passing a concurrent dispatch queue should now work correctly (#988). + +# v0.12.0 - [changed] Replaced the `DocumentListenOptions` object with a simple boolean. Instead of calling `addSnapshotListener(options: DocumentListenOptions.includeMetadataChanges(true))` diff --git a/Firestore/Example/Firestore/Base.lproj/LaunchScreen.storyboard b/Firestore/Example/App/iOS/Base.lproj/LaunchScreen.storyboard index 66a7681..66a7681 100644 --- a/Firestore/Example/Firestore/Base.lproj/LaunchScreen.storyboard +++ b/Firestore/Example/App/iOS/Base.lproj/LaunchScreen.storyboard diff --git a/Firestore/Example/Firestore/Base.lproj/Main.storyboard b/Firestore/Example/App/iOS/Base.lproj/Main.storyboard index d164a23..d164a23 100644 --- a/Firestore/Example/Firestore/Base.lproj/Main.storyboard +++ b/Firestore/Example/App/iOS/Base.lproj/Main.storyboard diff --git a/Firestore/Example/Firestore/FIRAppDelegate.h b/Firestore/Example/App/iOS/FIRAppDelegate.h index 1eb5040..1eb5040 100644 --- a/Firestore/Example/Firestore/FIRAppDelegate.h +++ b/Firestore/Example/App/iOS/FIRAppDelegate.h diff --git a/Firestore/Example/Firestore/FIRAppDelegate.m b/Firestore/Example/App/iOS/FIRAppDelegate.m index 12ca249..12ca249 100644 --- a/Firestore/Example/Firestore/FIRAppDelegate.m +++ b/Firestore/Example/App/iOS/FIRAppDelegate.m diff --git a/Firestore/Example/Firestore/FIRViewController.h b/Firestore/Example/App/iOS/FIRViewController.h index 64b4b74..64b4b74 100644 --- a/Firestore/Example/Firestore/FIRViewController.h +++ b/Firestore/Example/App/iOS/FIRViewController.h diff --git a/Firestore/Example/Firestore/FIRViewController.m b/Firestore/Example/App/iOS/FIRViewController.m index cdad545..cdad545 100644 --- a/Firestore/Example/Firestore/FIRViewController.m +++ b/Firestore/Example/App/iOS/FIRViewController.m diff --git a/Firestore/Example/Firestore/Firestore-Info.plist b/Firestore/Example/App/iOS/Firestore-Info.plist index 7576a0d..7576a0d 100644 --- a/Firestore/Example/Firestore/Firestore-Info.plist +++ b/Firestore/Example/App/iOS/Firestore-Info.plist diff --git a/Firestore/Example/Firestore/Images.xcassets/AppIcon.appiconset/Contents.json b/Firestore/Example/App/iOS/Images.xcassets/AppIcon.appiconset/Contents.json index d7070bc..d7070bc 100644 --- a/Firestore/Example/Firestore/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/Firestore/Example/App/iOS/Images.xcassets/AppIcon.appiconset/Contents.json diff --git a/Firestore/Example/Firestore/en.lproj/InfoPlist.strings b/Firestore/Example/App/iOS/en.lproj/InfoPlist.strings index 477b28f..477b28f 100644 --- a/Firestore/Example/Firestore/en.lproj/InfoPlist.strings +++ b/Firestore/Example/App/iOS/en.lproj/InfoPlist.strings diff --git a/Firestore/Example/Firestore/main.m b/Firestore/Example/App/iOS/main.m index 724fccf..724fccf 100644 --- a/Firestore/Example/Firestore/main.m +++ b/Firestore/Example/App/iOS/main.m diff --git a/Firestore/Example/Firestore.xcodeproj/project.pbxproj b/Firestore/Example/Firestore.xcodeproj/project.pbxproj index ba7a59a..fd6d9ba 100644 --- a/Firestore/Example/Firestore.xcodeproj/project.pbxproj +++ b/Firestore/Example/Firestore.xcodeproj/project.pbxproj @@ -25,17 +25,15 @@ /* Begin PBXBuildFile section */ 132E3E53179DE287D875F3F2 /* FSTLevelDBTransactionTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 132E36BB104830BD806351AC /* FSTLevelDBTransactionTests.mm */; }; - 347FDC6AA737A754541F7C8A /* Pods_Firestore_Tests_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8525646842C83F703237BAA4 /* Pods_Firestore_Tests_iOS.framework */; }; 3B843E4C1F3A182900548890 /* remote_store_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 3B843E4A1F3930A400548890 /* remote_store_spec_test.json */; }; - 3DE7ABABD726C80991971BE1 /* Pods_Firestore_SwiftTests_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 44B1394B81D5FCA818943A06 /* Pods_Firestore_SwiftTests_iOS.framework */; }; - 5436F32420008FAD006E51E3 /* string_printf_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 5436F32320008FAD006E51E3 /* string_printf_test.cc */; }; + 54131E9720ADE679001DF3FF /* string_format_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54131E9620ADE678001DF3FF /* string_format_test.cc */; }; 54511E8E209805F8005BD28F /* hashing_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54511E8D209805F8005BD28F /* hashing_test.cc */; }; 5467FB01203E5717009C9584 /* FIRFirestoreTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5467FAFF203E56F8009C9584 /* FIRFirestoreTests.mm */; }; 5467FB08203E6A44009C9584 /* app_testing.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5467FB07203E6A44009C9584 /* app_testing.mm */; }; + 546854AA20A36867004BDBD5 /* datastore_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 546854A820A36867004BDBD5 /* datastore_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 */; }; 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 */; }; @@ -110,6 +108,17 @@ 5492E0CA2021557E00B64F25 /* FSTWatchChangeTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E0C52021557E00B64F25 /* FSTWatchChangeTests.mm */; }; 5495EB032040E90200EBA509 /* CodableGeoPointTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5495EB022040E90200EBA509 /* CodableGeoPointTests.swift */; }; 54995F6F205B6E12004EFFA0 /* leveldb_key_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54995F6E205B6E12004EFFA0 /* leveldb_key_test.cc */; }; + 549CCA5020A36DBC00BCEB75 /* sorted_set_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA4C20A36DBB00BCEB75 /* sorted_set_test.cc */; }; + 549CCA5120A36DBC00BCEB75 /* tree_sorted_map_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA4D20A36DBB00BCEB75 /* tree_sorted_map_test.cc */; }; + 549CCA5220A36DBC00BCEB75 /* sorted_map_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA4E20A36DBB00BCEB75 /* sorted_map_test.cc */; }; + 549CCA5720A36E1F00BCEB75 /* field_mask_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA5320A36E1F00BCEB75 /* field_mask_test.cc */; }; + 549CCA5920A36E1F00BCEB75 /* precondition_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA5520A36E1F00BCEB75 /* precondition_test.cc */; }; + 54A0352620A3AED0003E0143 /* field_transform_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352320A3AEC3003E0143 /* field_transform_test.mm */; }; + 54A0352720A3AED0003E0143 /* transform_operations_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352220A3AEC3003E0143 /* transform_operations_test.mm */; }; + 54A0352A20A3B3BD003E0143 /* testutil.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352820A3B3BD003E0143 /* testutil.cc */; }; + 54A0352F20A3B3D8003E0143 /* status_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352C20A3B3D7003E0143 /* status_test.cc */; }; + 54A0353020A3B3D8003E0143 /* statusor_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352D20A3B3D7003E0143 /* statusor_test.cc */; }; + 54A0353520A3D8CB003E0143 /* iterator_adaptors_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0353420A3D8CB003E0143 /* iterator_adaptors_test.cc */; }; 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 */; }; @@ -122,6 +131,7 @@ 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 */; }; 54EB764D202277B30088B8F3 /* array_sorted_map_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54EB764C202277B30088B8F3 /* array_sorted_map_test.cc */; }; + 5D405BE298CE4692CB00790A /* Pods_Firestore_Tests_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2B50B3A0DF77100EEE887891 /* Pods_Firestore_Tests_iOS.framework */; }; 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 */; }; @@ -139,6 +149,7 @@ 7346E61D20325C6900FD6CEF /* FSTDispatchQueueTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7346E61C20325C6900FD6CEF /* FSTDispatchQueueTests.mm */; }; 73866AA12082B0A5009BB4FF /* FIRArrayTransformTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 73866A9F2082B069009BB4FF /* FIRArrayTransformTests.mm */; }; 873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */; }; + 8C82D4D3F9AB63E79CC52DC8 /* Pods_Firestore_IntegrationTests_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ECEBABC7E7B693BE808A1052 /* Pods_Firestore_IntegrationTests_iOS.framework */; }; 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 */; }; @@ -167,8 +178,9 @@ B6FB468E208F9BAB00554BA2 /* executor_libdispatch_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4689208F9B9100554BA2 /* executor_libdispatch_test.mm */; }; B6FB468F208F9BAE00554BA2 /* executor_std_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4687208F9B9100554BA2 /* executor_std_test.cc */; }; B6FB4690208F9BB300554BA2 /* executor_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4688208F9B9100554BA2 /* executor_test.cc */; }; - C4E749275AD0FBDF9F4716A8 /* Pods_SwiftBuildTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 32AD40BF6B0E849B07FFD05E /* Pods_SwiftBuildTest.framework */; }; - CF08376B68945A0BB332D0C8 /* Pods_Firestore_IntegrationTests_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B4EEE10E8E59CC91309335CA /* Pods_Firestore_IntegrationTests_iOS.framework */; }; + BF219E98F1C5A1DAEB5EEC86 /* Pods_Firestore_Example_iOS_SwiftBuildTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 379B34A1536045869826D82A /* Pods_Firestore_Example_iOS_SwiftBuildTest.framework */; }; + C1AA536F90A0A576CA2816EB /* Pods_Firestore_Example_iOS_Firestore_SwiftTests_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BB92EB03E3F92485023F64ED /* Pods_Firestore_Example_iOS_Firestore_SwiftTests_iOS.framework */; }; + C8D3CE2343E53223E6487F2C /* Pods_Firestore_Example_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5918805E993304321A05E82B /* Pods_Firestore_Example_iOS.framework */; }; 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 */; }; @@ -179,7 +191,6 @@ 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 */; }; - F06EE8EB234BBE8B7898D5EE /* Pods_Firestore_Example_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 24A6BEC38BF31BC4BF0E9DA7 /* Pods_Firestore_Example_iOS.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -235,30 +246,26 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - 04DF37A117F88A9891379ED6 /* Pods-Firestore_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Tests/Pods-Firestore_Tests.release.xcconfig"; sourceTree = "<group>"; }; - 0E5F50EF80014608B1868944 /* Pods_Firestore_Example_iOS_Firestore_SwiftTests_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_Example_iOS_Firestore_SwiftTests_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 05C3D82261C3BE976889FF09 /* Pods-Firestore_Example_iOS-SwiftBuildTest.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example_iOS-SwiftBuildTest.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example_iOS-SwiftBuildTest/Pods-Firestore_Example_iOS-SwiftBuildTest.debug.xcconfig"; sourceTree = "<group>"; }; + 11984BA0A99D7A7ABA5B0D90 /* Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS/Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS.release.xcconfig"; sourceTree = "<group>"; }; + 1277F98C20D2DF0867496976 /* Pods-Firestore_IntegrationTests_iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_IntegrationTests_iOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_IntegrationTests_iOS/Pods-Firestore_IntegrationTests_iOS.debug.xcconfig"; sourceTree = "<group>"; }; 12F4357299652983A615F886 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = "<group>"; }; 132E36BB104830BD806351AC /* FSTLevelDBTransactionTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTLevelDBTransactionTests.mm; sourceTree = "<group>"; }; - 245812330F6A31632BB4B623 /* Pods_Firestore_Example_Firestore_SwiftTests_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_Example_Firestore_SwiftTests_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 24A6BEC38BF31BC4BF0E9DA7 /* Pods_Firestore_Example_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_Example_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 32AD40BF6B0E849B07FFD05E /* Pods_SwiftBuildTest.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SwiftBuildTest.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 39F1102E452A53A1F93AAA1F /* Pods-Firestore_SwiftTests_iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_SwiftTests_iOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_SwiftTests_iOS/Pods-Firestore_SwiftTests_iOS.release.xcconfig"; sourceTree = "<group>"; }; + 2B50B3A0DF77100EEE887891 /* Pods_Firestore_Tests_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_Tests_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 379B34A1536045869826D82A /* Pods_Firestore_Example_iOS_SwiftBuildTest.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_Example_iOS_SwiftBuildTest.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3B843E4A1F3930A400548890 /* remote_store_spec_test.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = remote_store_spec_test.json; sourceTree = "<group>"; }; - 3C7CE22C50805C4A854C73A1 /* Pods-Firestore_IntegrationTests_iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_IntegrationTests_iOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_IntegrationTests_iOS/Pods-Firestore_IntegrationTests_iOS.debug.xcconfig"; sourceTree = "<group>"; }; - 3F422FFBDA6E79396E2FB594 /* Pods-Firestore_Example-Firestore_SwiftTests_iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example-Firestore_SwiftTests_iOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example-Firestore_SwiftTests_iOS/Pods-Firestore_Example-Firestore_SwiftTests_iOS.debug.xcconfig"; sourceTree = "<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>"; }; - 44B1394B81D5FCA818943A06 /* Pods_Firestore_SwiftTests_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_SwiftTests_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 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>"; }; - 54511E8D209805F8005BD28F /* hashing_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = hashing_test.cc; path = ../../core/test/firebase/firestore/util/hashing_test.cc; sourceTree = "<group>"; }; + 3C81DE3772628FE297055662 /* Pods-Firestore_Example_iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example_iOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example_iOS/Pods-Firestore_Example_iOS.debug.xcconfig"; sourceTree = "<group>"; }; + 3F0992A4B83C60841C52E960 /* Pods-Firestore_Example_iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example_iOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example_iOS/Pods-Firestore_Example_iOS.release.xcconfig"; sourceTree = "<group>"; }; + 54131E9620ADE678001DF3FF /* string_format_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = string_format_test.cc; sourceTree = "<group>"; }; + 54511E8D209805F8005BD28F /* hashing_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = hashing_test.cc; sourceTree = "<group>"; }; 5467FAFF203E56F8009C9584 /* FIRFirestoreTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRFirestoreTests.mm; sourceTree = "<group>"; }; - 5467FB06203E6A44009C9584 /* app_testing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = app_testing.h; path = ../../core/test/firebase/firestore/testutil/app_testing.h; sourceTree = "<group>"; }; - 5467FB07203E6A44009C9584 /* app_testing.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = app_testing.mm; path = ../../core/test/firebase/firestore/testutil/app_testing.mm; sourceTree = "<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>"; }; - 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>"; }; + 5467FB06203E6A44009C9584 /* app_testing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = app_testing.h; sourceTree = "<group>"; }; + 5467FB07203E6A44009C9584 /* app_testing.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = app_testing.mm; sourceTree = "<group>"; }; + 546854A820A36867004BDBD5 /* datastore_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = datastore_test.cc; sourceTree = "<group>"; }; + 54740A521FC913E500713A1A /* autoid_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = autoid_test.cc; sourceTree = "<group>"; }; + 54740A531FC913E500713A1A /* secure_random_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = secure_random_test.cc; sourceTree = "<group>"; }; + 54764FAE1FAA21B90085E60A /* FSTGoogleTestTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTGoogleTestTests.mm; sourceTree = "<group>"; }; + 548DB928200D59F600E00ABC /* comparison_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = 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>"; }; @@ -335,8 +342,22 @@ 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>"; }; 5495EB022040E90200EBA509 /* CodableGeoPointTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CodableGeoPointTests.swift; sourceTree = "<group>"; }; - 54995F6E205B6E12004EFFA0 /* leveldb_key_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = leveldb_key_test.cc; path = ../../core/test/firebase/firestore/local/leveldb_key_test.cc; 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>"; }; + 54995F6E205B6E12004EFFA0 /* leveldb_key_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = leveldb_key_test.cc; sourceTree = "<group>"; }; + 549CCA4C20A36DBB00BCEB75 /* sorted_set_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sorted_set_test.cc; sourceTree = "<group>"; }; + 549CCA4D20A36DBB00BCEB75 /* tree_sorted_map_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tree_sorted_map_test.cc; sourceTree = "<group>"; }; + 549CCA4E20A36DBB00BCEB75 /* sorted_map_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sorted_map_test.cc; sourceTree = "<group>"; }; + 549CCA4F20A36DBC00BCEB75 /* testing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = testing.h; sourceTree = "<group>"; }; + 549CCA5320A36E1F00BCEB75 /* field_mask_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = field_mask_test.cc; sourceTree = "<group>"; }; + 549CCA5520A36E1F00BCEB75 /* precondition_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = precondition_test.cc; sourceTree = "<group>"; }; + 54A0352220A3AEC3003E0143 /* transform_operations_test.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = transform_operations_test.mm; sourceTree = "<group>"; }; + 54A0352320A3AEC3003E0143 /* field_transform_test.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = field_transform_test.mm; sourceTree = "<group>"; }; + 54A0352820A3B3BD003E0143 /* testutil.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = testutil.cc; sourceTree = "<group>"; }; + 54A0352920A3B3BD003E0143 /* testutil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = testutil.h; sourceTree = "<group>"; }; + 54A0352B20A3B3D7003E0143 /* status_test_util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = status_test_util.h; sourceTree = "<group>"; }; + 54A0352C20A3B3D7003E0143 /* status_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = status_test.cc; sourceTree = "<group>"; }; + 54A0352D20A3B3D7003E0143 /* statusor_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = statusor_test.cc; sourceTree = "<group>"; }; + 54A0353420A3D8CB003E0143 /* iterator_adaptors_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = iterator_adaptors_test.cc; sourceTree = "<group>"; }; + 54C2294E1FECABAE007D065B /* log_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = log_test.cc; sourceTree = "<group>"; }; 54C9EDF12040E16300A969CD /* Firestore_SwiftTests_iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Firestore_SwiftTests_iOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 54C9EDF52040E16300A969CD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 54DA129C1F315EE100DD57A1 /* collection_spec_test.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = collection_spec_test.json; sourceTree = "<group>"; }; @@ -352,9 +373,8 @@ 54E9281C1F33950B00C1953E /* FSTEventAccumulator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSTEventAccumulator.h; 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>"; }; - 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>"; }; - 555ACD6970390DC825A8F141 /* Pods-Firestore_Example_iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example_iOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example_iOS/Pods-Firestore_Example_iOS.debug.xcconfig"; sourceTree = "<group>"; }; - 5A3E3BE5F322D66EE3D6CB65 /* Pods-Firestore_Tests_iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Tests_iOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Tests_iOS/Pods-Firestore_Tests_iOS.release.xcconfig"; sourceTree = "<group>"; }; + 54EB764C202277B30088B8F3 /* array_sorted_map_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = array_sorted_map_test.cc; sourceTree = "<group>"; }; + 5918805E993304321A05E82B /* Pods_Firestore_Example_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_Example_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 6003F58A195388D20070C39A /* Firestore_Example_iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Firestore_Example_iOS.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; }; @@ -372,25 +392,18 @@ 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>"; }; 6161B5012047140400A99DBB /* FIRFirestoreSourceTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRFirestoreSourceTests.mm; sourceTree = "<group>"; }; - 618AC3C38A174084B9420162 /* Pods-Firestore_IntegrationTests_iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_IntegrationTests_iOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_IntegrationTests_iOS/Pods-Firestore_IntegrationTests_iOS.release.xcconfig"; sourceTree = "<group>"; }; - 635C1D9B5E36BC4C12A35E70 /* Pods-Firestore_SwiftTests_iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_SwiftTests_iOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_SwiftTests_iOS/Pods-Firestore_SwiftTests_iOS.debug.xcconfig"; sourceTree = "<group>"; }; - 69F6A10DBD6187489481CD76 /* Pods_Firestore_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 69E6C311558EC77729A16CF1 /* Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS/Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS.debug.xcconfig"; sourceTree = "<group>"; }; 71719F9E1E33DC2100824A3D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; }; 7346E61C20325C6900FD6CEF /* FSTDispatchQueueTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTDispatchQueueTests.mm; sourceTree = "<group>"; }; 73866A9F2082B069009BB4FF /* FIRArrayTransformTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRArrayTransformTests.mm; sourceTree = "<group>"; }; - 75A6FE51C1A02DF38F62FAAD /* Pods_Firestore_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 80025E2E892B94823962D11D /* Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS/Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS.release.xcconfig"; sourceTree = "<group>"; }; - 8525646842C83F703237BAA4 /* Pods_Firestore_Tests_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_Tests_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 74ACEC3603BE58B57A7E8D4C /* Pods-Firestore_Example_iOS-SwiftBuildTest.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example_iOS-SwiftBuildTest.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example_iOS-SwiftBuildTest/Pods-Firestore_Example_iOS-SwiftBuildTest.release.xcconfig"; sourceTree = "<group>"; }; 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = Main.storyboard; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; }; 8E002F4AD5D9B6197C940847 /* Firestore.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = Firestore.podspec; path = ../Firestore.podspec; sourceTree = "<group>"; }; - 9837221D251B8D40E7D7B454 /* Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS/Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS.debug.xcconfig"; 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>"; }; + AB380CFC201A2EE200D97691 /* string_util_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = string_util_test.cc; sourceTree = "<group>"; }; + AB380D01201BC69F00D97691 /* bits_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = bits_test.cc; sourceTree = "<group>"; }; + AB380D03201BC6E400D97691 /* ordered_code_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ordered_code_test.cc; sourceTree = "<group>"; }; AB38D92E20235D22000A432D /* database_info_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = database_info_test.cc; sourceTree = "<group>"; }; AB38D93220239654000A432D /* user_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = user_test.cc; sourceTree = "<group>"; }; AB38D9342023966E000A432D /* credentials_provider_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = credentials_provider_test.cc; sourceTree = "<group>"; }; @@ -399,32 +412,27 @@ AB6B908520322E6D00CC290A /* maybe_document_test.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = maybe_document_test.cc; sourceTree = "<group>"; }; AB6B908720322E8800CC290A /* no_document_test.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = no_document_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>"; }; + AB7BAB332012B519001E0872 /* geo_point_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = geo_point_test.cc; sourceTree = "<group>"; }; ABA495B9202B7E79008A7851 /* snapshot_version_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = snapshot_version_test.cc; sourceTree = "<group>"; }; ABC1D7DF2023A3EF00BA84F0 /* token_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = token_test.cc; sourceTree = "<group>"; }; ABC1D7E22023CDC500BA84F0 /* firebase_credentials_provider_test.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = firebase_credentials_provider_test.mm; sourceTree = "<group>"; }; - ABF6506B201131F8005F2C74 /* timestamp_test.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = timestamp_test.cc; path = ../../core/test/firebase/firestore/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; }; - B4EEE10E8E59CC91309335CA /* Pods_Firestore_IntegrationTests_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_IntegrationTests_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + ABF6506B201131F8005F2C74 /* timestamp_test.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = timestamp_test.cc; sourceTree = "<group>"; }; + B3F5B3AAE791A5911B9EAA82 /* Pods-Firestore_Tests_iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Tests_iOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Tests_iOS/Pods-Firestore_Tests_iOS.release.xcconfig"; sourceTree = "<group>"; }; B6152AD5202A5385000E5744 /* document_key_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = document_key_test.cc; sourceTree = "<group>"; }; B65D34A7203C99090076A5E1 /* FIRTimestampTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRTimestampTest.m; sourceTree = "<group>"; }; B686F2AD2023DDB20028D6BE /* field_path_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = field_path_test.cc; sourceTree = "<group>"; }; B686F2B02024FFD70028D6BE /* resource_path_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = resource_path_test.cc; sourceTree = "<group>"; }; - B6FB467A208E9A8200554BA2 /* async_queue_test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = async_queue_test.h; path = ../../core/test/firebase/firestore/util/async_queue_test.h; sourceTree = "<group>"; }; - B6FB467B208E9A8200554BA2 /* async_queue_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = async_queue_test.cc; path = ../../core/test/firebase/firestore/util/async_queue_test.cc; sourceTree = "<group>"; }; - B6FB4680208EA0BE00554BA2 /* async_queue_libdispatch_test.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = async_queue_libdispatch_test.mm; path = ../../core/test/firebase/firestore/util/async_queue_libdispatch_test.mm; sourceTree = "<group>"; }; - B6FB4681208EA0BE00554BA2 /* async_queue_std_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = async_queue_std_test.cc; path = ../../core/test/firebase/firestore/util/async_queue_std_test.cc; sourceTree = "<group>"; }; - B6FB4686208F9B9100554BA2 /* async_tests_util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = async_tests_util.h; path = ../../core/test/firebase/firestore/util/async_tests_util.h; sourceTree = "<group>"; }; - B6FB4687208F9B9100554BA2 /* executor_std_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = executor_std_test.cc; path = ../../core/test/firebase/firestore/util/executor_std_test.cc; sourceTree = "<group>"; }; - B6FB4688208F9B9100554BA2 /* executor_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = executor_test.cc; path = ../../core/test/firebase/firestore/util/executor_test.cc; sourceTree = "<group>"; }; - B6FB4689208F9B9100554BA2 /* executor_libdispatch_test.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = executor_libdispatch_test.mm; path = ../../core/test/firebase/firestore/util/executor_libdispatch_test.mm; sourceTree = "<group>"; }; - B6FB468A208F9B9100554BA2 /* executor_test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = executor_test.h; path = ../../core/test/firebase/firestore/util/executor_test.h; sourceTree = "<group>"; }; - BE88081EE627C46349C918EF /* Pods-Firestore_Tests_iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Tests_iOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Tests_iOS/Pods-Firestore_Tests_iOS.debug.xcconfig"; sourceTree = "<group>"; }; - C1D89E5405935366C88CC3E5 /* Pods-Firestore_Example-Firestore_SwiftTests_iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example-Firestore_SwiftTests_iOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example-Firestore_SwiftTests_iOS/Pods-Firestore_Example-Firestore_SwiftTests_iOS.release.xcconfig"; sourceTree = "<group>"; }; - 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>"; }; + B6FB467A208E9A8200554BA2 /* async_queue_test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = async_queue_test.h; sourceTree = "<group>"; }; + B6FB467B208E9A8200554BA2 /* async_queue_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = async_queue_test.cc; sourceTree = "<group>"; }; + B6FB4680208EA0BE00554BA2 /* async_queue_libdispatch_test.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = async_queue_libdispatch_test.mm; sourceTree = "<group>"; }; + B6FB4681208EA0BE00554BA2 /* async_queue_std_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = async_queue_std_test.cc; sourceTree = "<group>"; }; + B6FB4686208F9B9100554BA2 /* async_tests_util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = async_tests_util.h; sourceTree = "<group>"; }; + B6FB4687208F9B9100554BA2 /* executor_std_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = executor_std_test.cc; sourceTree = "<group>"; }; + B6FB4688208F9B9100554BA2 /* executor_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = executor_test.cc; sourceTree = "<group>"; }; + B6FB4689208F9B9100554BA2 /* executor_libdispatch_test.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = executor_libdispatch_test.mm; sourceTree = "<group>"; }; + B6FB468A208F9B9100554BA2 /* executor_test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = executor_test.h; sourceTree = "<group>"; }; + BB92EB03E3F92485023F64ED /* Pods_Firestore_Example_iOS_Firestore_SwiftTests_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_Example_iOS_Firestore_SwiftTests_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D3CC3DC5338DCAF43A211155 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = "<group>"; }; - D8CA124A5F9A704EA0E12785 /* Pods-Firestore_Example_iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example_iOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example_iOS/Pods-Firestore_Example_iOS.release.xcconfig"; 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_iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Firestore_IntegrationTests_iOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; DE03B3621F215E1600A30B9C /* CAcert.pem */ = {isa = PBXFileReference; lastKnownFileType = text; path = CAcert.pem; sourceTree = "<group>"; }; DE0761E41F2FE611003233AF /* SwiftBuildTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftBuildTest.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -441,7 +449,9 @@ DE51B1981F0D48AC0013853F /* FSTSpecTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FSTSpecTests.h; sourceTree = "<group>"; }; DE51B19A1F0D48AC0013853F /* FSTSyncEngineTestDriver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FSTSyncEngineTestDriver.h; sourceTree = "<group>"; }; DE51B1A71F0D48AC0013853F /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; 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>"; }; + E592181BFD7C53C305123739 /* Pods-Firestore_Tests_iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Tests_iOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Tests_iOS/Pods-Firestore_Tests_iOS.debug.xcconfig"; sourceTree = "<group>"; }; + ECEBABC7E7B693BE808A1052 /* Pods_Firestore_IntegrationTests_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_IntegrationTests_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + F354C0FE92645B56A6C6FD44 /* Pods-Firestore_IntegrationTests_iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_IntegrationTests_iOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_IntegrationTests_iOS/Pods-Firestore_IntegrationTests_iOS.release.xcconfig"; sourceTree = "<group>"; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -449,7 +459,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 3DE7ABABD726C80991971BE1 /* Pods_Firestore_SwiftTests_iOS.framework in Frameworks */, + C1AA536F90A0A576CA2816EB /* Pods_Firestore_Example_iOS_Firestore_SwiftTests_iOS.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -460,7 +470,7 @@ 6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */, 6003F592195388D20070C39A /* UIKit.framework in Frameworks */, 6003F58E195388D20070C39A /* Foundation.framework in Frameworks */, - F06EE8EB234BBE8B7898D5EE /* Pods_Firestore_Example_iOS.framework in Frameworks */, + C8D3CE2343E53223E6487F2C /* Pods_Firestore_Example_iOS.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -471,7 +481,7 @@ 6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */, 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */, 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */, - 347FDC6AA737A754541F7C8A /* Pods_Firestore_Tests_iOS.framework in Frameworks */, + 5D405BE298CE4692CB00790A /* Pods_Firestore_Tests_iOS.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -482,7 +492,7 @@ DE03B2D41F2149D600A30B9C /* XCTest.framework in Frameworks */, DE03B2D51F2149D600A30B9C /* UIKit.framework in Frameworks */, DE03B2D61F2149D600A30B9C /* Foundation.framework in Frameworks */, - CF08376B68945A0BB332D0C8 /* Pods_Firestore_IntegrationTests_iOS.framework in Frameworks */, + 8C82D4D3F9AB63E79CC52DC8 /* Pods_Firestore_IntegrationTests_iOS.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -490,49 +500,71 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - C4E749275AD0FBDF9F4716A8 /* Pods_SwiftBuildTest.framework in Frameworks */, + BF219E98F1C5A1DAEB5EEC86 /* Pods_Firestore_Example_iOS_SwiftBuildTest.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 543B4F0520A91E4B001F506D /* App */ = { + isa = PBXGroup; + children = ( + 6003F593195388D20070C39A /* iOS */, + ); + path = App; + sourceTree = "<group>"; + }; 5467FB05203E652F009C9584 /* testutil */ = { isa = PBXGroup; children = ( 5467FB06203E6A44009C9584 /* app_testing.h */, 5467FB07203E6A44009C9584 /* app_testing.mm */, + 54A0352820A3B3BD003E0143 /* testutil.cc */, + 54A0352920A3B3BD003E0143 /* testutil.h */, + ); + path = testutil; + sourceTree = "<group>"; + }; + 546854A720A3681B004BDBD5 /* remote */ = { + isa = PBXGroup; + children = ( + 546854A820A36867004BDBD5 /* datastore_test.cc */, ); - name = testutil; + path = remote; sourceTree = "<group>"; }; 54740A561FC913EB00713A1A /* util */ = { isa = PBXGroup; children = ( - B6FB4686208F9B9100554BA2 /* async_tests_util.h */, - B6FB4689208F9B9100554BA2 /* executor_libdispatch_test.mm */, - B6FB4687208F9B9100554BA2 /* executor_std_test.cc */, - B6FB4688208F9B9100554BA2 /* executor_test.cc */, - B6FB468A208F9B9100554BA2 /* executor_test.h */, B6FB4680208EA0BE00554BA2 /* async_queue_libdispatch_test.mm */, B6FB4681208EA0BE00554BA2 /* async_queue_std_test.cc */, B6FB467B208E9A8200554BA2 /* async_queue_test.cc */, B6FB467A208E9A8200554BA2 /* async_queue_test.h */, - 548DB926200D590300E00ABC /* assert_test.cc */, + B6FB4686208F9B9100554BA2 /* async_tests_util.h */, 54740A521FC913E500713A1A /* autoid_test.cc */, AB380D01201BC69F00D97691 /* bits_test.cc */, 548DB928200D59F600E00ABC /* comparison_test.cc */, + B6FB4689208F9B9100554BA2 /* executor_libdispatch_test.mm */, + B6FB4687208F9B9100554BA2 /* executor_std_test.cc */, + B6FB4688208F9B9100554BA2 /* executor_test.cc */, + B6FB468A208F9B9100554BA2 /* executor_test.h */, + 54131E9820AE076C001DF3FF /* hard_assert_test.cc */, 54511E8D209805F8005BD28F /* hashing_test.cc */, + 54A0353420A3D8CB003E0143 /* iterator_adaptors_test.cc */, 54C2294E1FECABAE007D065B /* log_test.cc */, AB380D03201BC6E400D97691 /* ordered_code_test.cc */, 54740A531FC913E500713A1A /* secure_random_test.cc */, - 5436F32320008FAD006E51E3 /* string_printf_test.cc */, + 54A0352B20A3B3D7003E0143 /* status_test_util.h */, + 54A0352C20A3B3D7003E0143 /* status_test.cc */, + 54A0352D20A3B3D7003E0143 /* statusor_test.cc */, + 54131E9620ADE678001DF3FF /* string_format_test.cc */, AB380CFC201A2EE200D97691 /* string_util_test.cc */, ); - name = util; + path = util; sourceTree = "<group>"; }; - 54764FAC1FAA0C390085E60A /* GoogleTests */ = { + 54764FAC1FAA0C390085E60A /* CoreTests */ = { isa = PBXGroup; children = ( AB38D9312023962A000A432D /* auth */, @@ -540,13 +572,15 @@ 54EB764B202277970088B8F3 /* immutable */, 54995F70205B6E1A004EFFA0 /* local */, AB356EF5200E9D1A0089B766 /* model */, + 546854A720A3681B004BDBD5 /* remote */, 5467FB05203E652F009C9584 /* testutil */, 54740A561FC913EB00713A1A /* util */, 54764FAE1FAA21B90085E60A /* FSTGoogleTestTests.mm */, AB7BAB332012B519001E0872 /* geo_point_test.cc */, ABF6506B201131F8005F2C74 /* timestamp_test.cc */, ); - name = GoogleTests; + name = CoreTests; + path = ../core/test/firebase/firestore; sourceTree = "<group>"; }; 5495EB012040E90200EBA509 /* Codable */ = { @@ -562,7 +596,7 @@ children = ( 54995F6E205B6E12004EFFA0 /* leveldb_key_test.cc */, ); - name = local; + path = local; sourceTree = "<group>"; }; 54C9EDF22040E16300A969CD /* SwiftTests */ = { @@ -579,22 +613,26 @@ isa = PBXGroup; children = ( 54EB764C202277B30088B8F3 /* array_sorted_map_test.cc */, + 549CCA4E20A36DBB00BCEB75 /* sorted_map_test.cc */, + 549CCA4C20A36DBB00BCEB75 /* sorted_set_test.cc */, + 549CCA4F20A36DBC00BCEB75 /* testing.h */, + 549CCA4D20A36DBB00BCEB75 /* tree_sorted_map_test.cc */, ); - name = immutable; - path = ../../core/test/firebase/firestore/core/immutable; + path = immutable; sourceTree = "<group>"; }; 6003F581195388D10070C39A = { isa = PBXGroup; children = ( + 543B4F0520A91E4B001F506D /* App */, 60FF7A9C1954A5C5007DD14C /* Podspec Metadata */, - 6003F593195388D20070C39A /* Example for Firestore */, 6003F5B5195388D20070C39A /* Tests */, + 54764FAC1FAA0C390085E60A /* CoreTests */, 54C9EDF22040E16300A969CD /* SwiftTests */, DE0761E51F2FE611003233AF /* SwiftBuildTest */, 6003F58C195388D20070C39A /* Frameworks */, 6003F58B195388D20070C39A /* Products */, - A47A1BF74A48BCAEAFBCBF1E /* Pods */, + AAEA2A72CFD1FA5AD34462F7 /* Pods */, ); sourceTree = "<group>"; }; @@ -617,21 +655,16 @@ 6003F58F195388D20070C39A /* CoreGraphics.framework */, 6003F591195388D20070C39A /* UIKit.framework */, 6003F5AF195388D20070C39A /* XCTest.framework */, - 75A6FE51C1A02DF38F62FAAD /* Pods_Firestore_Example.framework */, - 69F6A10DBD6187489481CD76 /* Pods_Firestore_Tests.framework */, - B2FA635DF5D116A67A7441CD /* Pods_Firestore_IntegrationTests.framework */, - 32AD40BF6B0E849B07FFD05E /* Pods_SwiftBuildTest.framework */, - 245812330F6A31632BB4B623 /* Pods_Firestore_Example_Firestore_SwiftTests_iOS.framework */, - 24A6BEC38BF31BC4BF0E9DA7 /* Pods_Firestore_Example_iOS.framework */, - 0E5F50EF80014608B1868944 /* Pods_Firestore_Example_iOS_Firestore_SwiftTests_iOS.framework */, - 8525646842C83F703237BAA4 /* Pods_Firestore_Tests_iOS.framework */, - B4EEE10E8E59CC91309335CA /* Pods_Firestore_IntegrationTests_iOS.framework */, - 44B1394B81D5FCA818943A06 /* Pods_Firestore_SwiftTests_iOS.framework */, + 5918805E993304321A05E82B /* Pods_Firestore_Example_iOS.framework */, + BB92EB03E3F92485023F64ED /* Pods_Firestore_Example_iOS_Firestore_SwiftTests_iOS.framework */, + 379B34A1536045869826D82A /* Pods_Firestore_Example_iOS_SwiftBuildTest.framework */, + ECEBABC7E7B693BE808A1052 /* Pods_Firestore_IntegrationTests_iOS.framework */, + 2B50B3A0DF77100EEE887891 /* Pods_Firestore_Tests_iOS.framework */, ); name = Frameworks; sourceTree = "<group>"; }; - 6003F593195388D20070C39A /* Example for Firestore */ = { + 6003F593195388D20070C39A /* iOS */ = { isa = PBXGroup; children = ( 6003F59C195388D20070C39A /* FIRAppDelegate.h */, @@ -643,8 +676,7 @@ 6003F5A8195388D20070C39A /* Images.xcassets */, 6003F594195388D20070C39A /* Supporting Files */, ); - name = "Example for Firestore"; - path = Firestore; + path = iOS; sourceTree = "<group>"; }; 6003F594195388D20070C39A /* Supporting Files */ = { @@ -662,7 +694,6 @@ children = ( DE51B1831F0D48AC0013853F /* API */, DE51B1A81F0D48AC0013853F /* Core */, - 54764FAC1FAA0C390085E60A /* GoogleTests */, DE2EF06E1F3D07D7003D0CDC /* Immutable */, DE51B1BB1F0D48AC0013853F /* Integration */, DE51B1621F0D48AC0013853F /* Local */, @@ -694,29 +725,19 @@ name = "Podspec Metadata"; sourceTree = "<group>"; }; - A47A1BF74A48BCAEAFBCBF1E /* Pods */ = { + AAEA2A72CFD1FA5AD34462F7 /* Pods */ = { isa = PBXGroup; children = ( - 9EF477AD4B2B643FD320867A /* Pods-Firestore_Example.debug.xcconfig */, - 4EBC5F5ABE1FD097EFE5E224 /* Pods-Firestore_Example.release.xcconfig */, - 9D52E67EE96AA7E5D6F69748 /* Pods-Firestore_IntegrationTests.debug.xcconfig */, - DB17FEDFB80770611A935A60 /* Pods-Firestore_IntegrationTests.release.xcconfig */, - CE00BABB5A3AAB44A4C209E2 /* Pods-Firestore_Tests.debug.xcconfig */, - 04DF37A117F88A9891379ED6 /* Pods-Firestore_Tests.release.xcconfig */, - 42491D7DC8C8CD245CC22B93 /* Pods-SwiftBuildTest.debug.xcconfig */, - F23325524BEAF8D24F78AC88 /* Pods-SwiftBuildTest.release.xcconfig */, - 3F422FFBDA6E79396E2FB594 /* Pods-Firestore_Example-Firestore_SwiftTests_iOS.debug.xcconfig */, - C1D89E5405935366C88CC3E5 /* Pods-Firestore_Example-Firestore_SwiftTests_iOS.release.xcconfig */, - 555ACD6970390DC825A8F141 /* Pods-Firestore_Example_iOS.debug.xcconfig */, - D8CA124A5F9A704EA0E12785 /* Pods-Firestore_Example_iOS.release.xcconfig */, - 9837221D251B8D40E7D7B454 /* Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS.debug.xcconfig */, - 80025E2E892B94823962D11D /* Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS.release.xcconfig */, - BE88081EE627C46349C918EF /* Pods-Firestore_Tests_iOS.debug.xcconfig */, - 5A3E3BE5F322D66EE3D6CB65 /* Pods-Firestore_Tests_iOS.release.xcconfig */, - 3C7CE22C50805C4A854C73A1 /* Pods-Firestore_IntegrationTests_iOS.debug.xcconfig */, - 618AC3C38A174084B9420162 /* Pods-Firestore_IntegrationTests_iOS.release.xcconfig */, - 635C1D9B5E36BC4C12A35E70 /* Pods-Firestore_SwiftTests_iOS.debug.xcconfig */, - 39F1102E452A53A1F93AAA1F /* Pods-Firestore_SwiftTests_iOS.release.xcconfig */, + 3C81DE3772628FE297055662 /* Pods-Firestore_Example_iOS.debug.xcconfig */, + 3F0992A4B83C60841C52E960 /* Pods-Firestore_Example_iOS.release.xcconfig */, + 69E6C311558EC77729A16CF1 /* Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS.debug.xcconfig */, + 11984BA0A99D7A7ABA5B0D90 /* Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS.release.xcconfig */, + 05C3D82261C3BE976889FF09 /* Pods-Firestore_Example_iOS-SwiftBuildTest.debug.xcconfig */, + 74ACEC3603BE58B57A7E8D4C /* Pods-Firestore_Example_iOS-SwiftBuildTest.release.xcconfig */, + 1277F98C20D2DF0867496976 /* Pods-Firestore_IntegrationTests_iOS.debug.xcconfig */, + F354C0FE92645B56A6C6FD44 /* Pods-Firestore_IntegrationTests_iOS.release.xcconfig */, + E592181BFD7C53C305123739 /* Pods-Firestore_Tests_iOS.debug.xcconfig */, + B3F5B3AAE791A5911B9EAA82 /* Pods-Firestore_Tests_iOS.release.xcconfig */, ); name = Pods; sourceTree = "<group>"; @@ -724,28 +745,28 @@ AB356EF5200E9D1A0089B766 /* model */ = { isa = PBXGroup; children = ( + AB71064B201FA60300344F18 /* database_id_test.cc */, B6152AD5202A5385000E5744 /* document_key_test.cc */, AB6B908320322E4D00CC290A /* document_test.cc */, - B686F2B02024FFD70028D6BE /* resource_path_test.cc */, + 549CCA5320A36E1F00BCEB75 /* field_mask_test.cc */, B686F2AD2023DDB20028D6BE /* field_path_test.cc */, - AB71064B201FA60300344F18 /* database_id_test.cc */, AB356EF6200EA5EB0089B766 /* field_value_test.cc */, AB6B908520322E6D00CC290A /* maybe_document_test.cc */, AB6B908720322E8800CC290A /* no_document_test.cc */, + 549CCA5520A36E1F00BCEB75 /* precondition_test.cc */, + B686F2B02024FFD70028D6BE /* resource_path_test.cc */, ABA495B9202B7E79008A7851 /* snapshot_version_test.cc */, ); - name = model; - path = ../../core/test/firebase/firestore/model; + path = model; sourceTree = "<group>"; }; AB380CF7201937B800D97691 /* core */ = { isa = PBXGroup; children = ( - AB380CF82019382300D97691 /* target_id_generator_test.cc */, AB38D92E20235D22000A432D /* database_info_test.cc */, + AB380CF82019382300D97691 /* target_id_generator_test.cc */, ); - name = core; - path = ../../core/test/firebase/firestore/core; + path = core; sourceTree = "<group>"; }; AB38D9312023962A000A432D /* auth */ = { @@ -757,8 +778,7 @@ ABC1D7DF2023A3EF00BA84F0 /* token_test.cc */, AB38D93220239654000A432D /* user_test.cc */, ); - name = auth; - path = ../../core/test/firebase/firestore/auth; + path = auth; sourceTree = "<group>"; }; DE0761E51F2FE611003233AF /* SwiftBuildTest */ = { @@ -818,11 +838,13 @@ DE51B17B1F0D48AC0013853F /* Model */ = { isa = PBXGroup; children = ( + 54A0352320A3AEC3003E0143 /* field_transform_test.mm */, 5492E0B22021555000B64F25 /* FSTDocumentKeyTests.mm */, 5492E0B32021555100B64F25 /* FSTDocumentSetTests.mm */, 5492E0B62021555100B64F25 /* FSTDocumentTests.mm */, 5492E0B82021555100B64F25 /* FSTFieldValueTests.mm */, 5492E0B72021555100B64F25 /* FSTMutationTests.mm */, + 54A0352220A3AEC3003E0143 /* transform_operations_test.mm */, ); path = Model; sourceTree = "<group>"; @@ -963,11 +985,11 @@ isa = PBXNativeTarget; buildConfigurationList = 54C9EDFA2040E16300A969CD /* Build configuration list for PBXNativeTarget "Firestore_SwiftTests_iOS" */; buildPhases = ( - 6FB7F3A6D6ADAC64E4972A29 /* [CP] Check Pods Manifest.lock */, + D2D94DFA64939EF6DECDF908 /* [CP] Check Pods Manifest.lock */, 54C9EDED2040E16300A969CD /* Sources */, 54C9EDEE2040E16300A969CD /* Frameworks */, 54C9EDEF2040E16300A969CD /* Resources */, - 9E2D564AC55ADE2D52B7E951 /* [CP] Embed Pods Frameworks */, + EA424838F4A5DD7B337F57AB /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -983,10 +1005,11 @@ isa = PBXNativeTarget; buildConfigurationList = 6003F5BF195388D20070C39A /* Build configuration list for PBXNativeTarget "Firestore_Example_iOS" */; buildPhases = ( - FAB3416C6DD87D45081EC3E8 /* [CP] Check Pods Manifest.lock */, + 83F2AB95D08093BB076EE521 /* [CP] Check Pods Manifest.lock */, 6003F586195388D20070C39A /* Sources */, 6003F587195388D20070C39A /* Frameworks */, 6003F588195388D20070C39A /* Resources */, + 1EE692C7509A98D7EB03CA51 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -1001,11 +1024,11 @@ isa = PBXNativeTarget; buildConfigurationList = 6003F5C2195388D20070C39A /* Build configuration list for PBXNativeTarget "Firestore_Tests_iOS" */; buildPhases = ( - 8D94B6319191CD7344A4D1B9 /* [CP] Check Pods Manifest.lock */, + 8B469EB6DA9E6404589402E2 /* [CP] Check Pods Manifest.lock */, 6003F5AA195388D20070C39A /* Sources */, 6003F5AB195388D20070C39A /* Frameworks */, 6003F5AC195388D20070C39A /* Resources */, - BB3FE78ABF533BFC38839A0E /* [CP] Embed Pods Frameworks */, + 329C25E418360CEF62F6CB2B /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -1021,11 +1044,11 @@ isa = PBXNativeTarget; buildConfigurationList = DE03B2E61F2149D600A30B9C /* Build configuration list for PBXNativeTarget "Firestore_IntegrationTests_iOS" */; buildPhases = ( - DE03B2971F2149D600A30B9C /* [CP] Check Pods Manifest.lock */, + A827A009A65B69DC1B80EAD4 /* [CP] Check Pods Manifest.lock */, DE03B2981F2149D600A30B9C /* Sources */, DE03B2D31F2149D600A30B9C /* Frameworks */, DE03B2D81F2149D600A30B9C /* Resources */, - A677B831B09F9BD04DC6DF32 /* [CP] Embed Pods Frameworks */, + B7923D95031DB0DA112AAE9B /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -1041,11 +1064,11 @@ isa = PBXNativeTarget; buildConfigurationList = DE0761F51F2FE611003233AF /* Build configuration list for PBXNativeTarget "SwiftBuildTest" */; buildPhases = ( - 8F34C5E63ACEBD784CF82A45 /* [CP] Check Pods Manifest.lock */, + 5504F81EEBBEF943CA61D32C /* [CP] Check Pods Manifest.lock */, DE0761E01F2FE611003233AF /* Sources */, DE0761E11F2FE611003233AF /* Frameworks */, DE0761E21F2FE611003233AF /* Resources */, - 125BDFEB177CFD41D7A40928 /* [CP] Embed Pods Frameworks */, + 04404E0DCBB886A40E3C7175 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -1170,13 +1193,13 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 125BDFEB177CFD41D7A40928 /* [CP] Embed Pods Frameworks */ = { + 04404E0DCBB886A40E3C7175 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( - "${SRCROOT}/Pods/Target Support Files/Pods-SwiftBuildTest/Pods-SwiftBuildTest-frameworks.sh", + "${SRCROOT}/Pods/Target Support Files/Pods-Firestore_Example_iOS-SwiftBuildTest/Pods-Firestore_Example_iOS-SwiftBuildTest-frameworks.sh", "${BUILT_PRODUCTS_DIR}/BoringSSL/openssl.framework", "${BUILT_PRODUCTS_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework", "${BUILT_PRODUCTS_DIR}/GoogleToolboxForMac/GoogleToolboxForMac.framework", @@ -1203,28 +1226,68 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-SwiftBuildTest/Pods-SwiftBuildTest-frameworks.sh\"\n"; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Firestore_Example_iOS-SwiftBuildTest/Pods-Firestore_Example_iOS-SwiftBuildTest-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - 6FB7F3A6D6ADAC64E4972A29 /* [CP] Check Pods Manifest.lock */ = { + 1EE692C7509A98D7EB03CA51 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", + "${SRCROOT}/Pods/Target Support Files/Pods-Firestore_Example_iOS/Pods-Firestore_Example_iOS-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/BoringSSL/openssl.framework", + "${BUILT_PRODUCTS_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework", + "${BUILT_PRODUCTS_DIR}/GoogleToolboxForMac/GoogleToolboxForMac.framework", + "${BUILT_PRODUCTS_DIR}/Protobuf/Protobuf.framework", + "${BUILT_PRODUCTS_DIR}/gRPC/GRPCClient.framework", + "${BUILT_PRODUCTS_DIR}/gRPC-Core/grpc.framework", + "${BUILT_PRODUCTS_DIR}/gRPC-ProtoRPC/ProtoRPC.framework", + "${BUILT_PRODUCTS_DIR}/gRPC-RxLibrary/RxLibrary.framework", + "${BUILT_PRODUCTS_DIR}/leveldb-library/leveldb.framework", + "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework", ); - name = "[CP] Check Pods Manifest.lock"; + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/openssl.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GTMSessionFetcher.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleToolboxForMac.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Protobuf.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GRPCClient.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/grpc.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ProtoRPC.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxLibrary.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/leveldb.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Firestore_Example_iOS/Pods-Firestore_Example_iOS-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 329C25E418360CEF62F6CB2B /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${SRCROOT}/Pods/Target Support Files/Pods-Firestore_Tests_iOS/Pods-Firestore_Tests_iOS-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/leveldb-library/leveldb.framework", + "${BUILT_PRODUCTS_DIR}/GoogleTest/GoogleTest.framework", + "${BUILT_PRODUCTS_DIR}/OCMock/OCMock.framework", + ); + name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Firestore_SwiftTests_iOS-checkManifestLockResult.txt", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/leveldb.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleTest.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OCMock.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Firestore_Tests_iOS/Pods-Firestore_Tests_iOS-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - 8D94B6319191CD7344A4D1B9 /* [CP] Check Pods Manifest.lock */ = { + 5504F81EEBBEF943CA61D32C /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -1235,14 +1298,14 @@ ); name = "[CP] Check Pods Manifest.lock"; outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Firestore_Tests_iOS-checkManifestLockResult.txt", + "$(DERIVED_FILE_DIR)/Pods-Firestore_Example_iOS-SwiftBuildTest-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 8F34C5E63ACEBD784CF82A45 /* [CP] Check Pods Manifest.lock */ = { + 83F2AB95D08093BB076EE521 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -1253,126 +1316,68 @@ ); name = "[CP] Check Pods Manifest.lock"; outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-SwiftBuildTest-checkManifestLockResult.txt", + "$(DERIVED_FILE_DIR)/Pods-Firestore_Example_iOS-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 9E2D564AC55ADE2D52B7E951 /* [CP] Embed Pods Frameworks */ = { + 8B469EB6DA9E6404589402E2 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( - "${SRCROOT}/Pods/Target Support Files/Pods-Firestore_SwiftTests_iOS/Pods-Firestore_SwiftTests_iOS-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/BoringSSL/openssl.framework", - "${BUILT_PRODUCTS_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework", - "${BUILT_PRODUCTS_DIR}/GoogleToolboxForMac/GoogleToolboxForMac.framework", - "${BUILT_PRODUCTS_DIR}/Protobuf/Protobuf.framework", - "${BUILT_PRODUCTS_DIR}/gRPC/GRPCClient.framework", - "${BUILT_PRODUCTS_DIR}/gRPC-Core/grpc.framework", - "${BUILT_PRODUCTS_DIR}/gRPC-ProtoRPC/ProtoRPC.framework", - "${BUILT_PRODUCTS_DIR}/gRPC-RxLibrary/RxLibrary.framework", - "${BUILT_PRODUCTS_DIR}/leveldb-library/leveldb.framework", - "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework", + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", ); - name = "[CP] Embed Pods Frameworks"; + name = "[CP] Check Pods Manifest.lock"; outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/openssl.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GTMSessionFetcher.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleToolboxForMac.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Protobuf.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GRPCClient.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/grpc.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ProtoRPC.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxLibrary.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/leveldb.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework", + "$(DERIVED_FILE_DIR)/Pods-Firestore_Tests_iOS-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Firestore_SwiftTests_iOS/Pods-Firestore_SwiftTests_iOS-frameworks.sh\"\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - A677B831B09F9BD04DC6DF32 /* [CP] Embed Pods Frameworks */ = { + A827A009A65B69DC1B80EAD4 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( - "${SRCROOT}/Pods/Target Support Files/Pods-Firestore_IntegrationTests_iOS/Pods-Firestore_IntegrationTests_iOS-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/BoringSSL/openssl.framework", - "${BUILT_PRODUCTS_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework", - "${BUILT_PRODUCTS_DIR}/GoogleToolboxForMac/GoogleToolboxForMac.framework", - "${BUILT_PRODUCTS_DIR}/Protobuf/Protobuf.framework", - "${BUILT_PRODUCTS_DIR}/gRPC/GRPCClient.framework", - "${BUILT_PRODUCTS_DIR}/gRPC-Core/grpc.framework", - "${BUILT_PRODUCTS_DIR}/gRPC-ProtoRPC/ProtoRPC.framework", - "${BUILT_PRODUCTS_DIR}/gRPC-RxLibrary/RxLibrary.framework", - "${BUILT_PRODUCTS_DIR}/leveldb-library/leveldb.framework", - "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework", + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", ); - name = "[CP] Embed Pods Frameworks"; + name = "[CP] Check Pods Manifest.lock"; outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/openssl.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GTMSessionFetcher.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleToolboxForMac.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Protobuf.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GRPCClient.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/grpc.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ProtoRPC.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxLibrary.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/leveldb.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework", + "$(DERIVED_FILE_DIR)/Pods-Firestore_IntegrationTests_iOS-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Firestore_IntegrationTests_iOS/Pods-Firestore_IntegrationTests_iOS-frameworks.sh\"\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - BB3FE78ABF533BFC38839A0E /* [CP] Embed Pods Frameworks */ = { + B7923D95031DB0DA112AAE9B /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( - "${SRCROOT}/Pods/Target Support Files/Pods-Firestore_Tests_iOS/Pods-Firestore_Tests_iOS-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/BoringSSL/openssl.framework", - "${BUILT_PRODUCTS_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework", - "${BUILT_PRODUCTS_DIR}/GoogleToolboxForMac/GoogleToolboxForMac.framework", - "${BUILT_PRODUCTS_DIR}/Protobuf/Protobuf.framework", - "${BUILT_PRODUCTS_DIR}/gRPC/GRPCClient.framework", - "${BUILT_PRODUCTS_DIR}/gRPC-Core/grpc.framework", - "${BUILT_PRODUCTS_DIR}/gRPC-ProtoRPC/ProtoRPC.framework", - "${BUILT_PRODUCTS_DIR}/gRPC-RxLibrary/RxLibrary.framework", - "${BUILT_PRODUCTS_DIR}/leveldb-library/leveldb.framework", - "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework", - "${BUILT_PRODUCTS_DIR}/GoogleTest/GoogleTest.framework", + "${SRCROOT}/Pods/Target Support Files/Pods-Firestore_IntegrationTests_iOS/Pods-Firestore_IntegrationTests_iOS-frameworks.sh", "${BUILT_PRODUCTS_DIR}/OCMock/OCMock.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/openssl.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GTMSessionFetcher.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleToolboxForMac.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Protobuf.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GRPCClient.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/grpc.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ProtoRPC.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxLibrary.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/leveldb.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleTest.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OCMock.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Firestore_Tests_iOS/Pods-Firestore_Tests_iOS-frameworks.sh\"\n"; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Firestore_IntegrationTests_iOS/Pods-Firestore_IntegrationTests_iOS-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - DE03B2971F2149D600A30B9C /* [CP] Check Pods Manifest.lock */ = { + D2D94DFA64939EF6DECDF908 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -1383,29 +1388,47 @@ ); name = "[CP] Check Pods Manifest.lock"; outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Firestore_IntegrationTests_iOS-checkManifestLockResult.txt", + "$(DERIVED_FILE_DIR)/Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - FAB3416C6DD87D45081EC3E8 /* [CP] Check Pods Manifest.lock */ = { + EA424838F4A5DD7B337F57AB /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", + "${SRCROOT}/Pods/Target Support Files/Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS/Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/BoringSSL/openssl.framework", + "${BUILT_PRODUCTS_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework", + "${BUILT_PRODUCTS_DIR}/GoogleToolboxForMac/GoogleToolboxForMac.framework", + "${BUILT_PRODUCTS_DIR}/Protobuf/Protobuf.framework", + "${BUILT_PRODUCTS_DIR}/gRPC/GRPCClient.framework", + "${BUILT_PRODUCTS_DIR}/gRPC-Core/grpc.framework", + "${BUILT_PRODUCTS_DIR}/gRPC-ProtoRPC/ProtoRPC.framework", + "${BUILT_PRODUCTS_DIR}/gRPC-RxLibrary/RxLibrary.framework", + "${BUILT_PRODUCTS_DIR}/leveldb-library/leveldb.framework", + "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework", ); - name = "[CP] Check Pods Manifest.lock"; + name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Firestore_Example_iOS-checkManifestLockResult.txt", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/openssl.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GTMSessionFetcher.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleToolboxForMac.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Protobuf.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GRPCClient.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/grpc.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ProtoRPC.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxLibrary.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/leveldb.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS/Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -1437,6 +1460,7 @@ DE2EF0881F3D0B6E003D0CDC /* FSTTreeSortedDictionaryTests.m in Sources */, ABE6637A201FA81900ED349A /* database_id_test.cc in Sources */, 5492E0AF2021552D00B64F25 /* FSTReferenceSetTests.mm in Sources */, + 549CCA5120A36DBC00BCEB75 /* tree_sorted_map_test.cc in Sources */, 5492E09E2021552D00B64F25 /* FSTEagerGarbageCollectorTests.mm in Sources */, 5492E0C62021557E00B64F25 /* FSTWatchChange+Testing.mm in Sources */, 5492E064202154B900B64F25 /* FSTQueryListenerTests.mm in Sources */, @@ -1448,36 +1472,41 @@ B686F2B22025000D0028D6BE /* resource_path_test.cc in Sources */, 5492E0CA2021557E00B64F25 /* FSTWatchChangeTests.mm in Sources */, 5492E063202154B900B64F25 /* FSTViewSnapshotTest.mm in Sources */, + 54131E9720ADE679001DF3FF /* string_format_test.cc in Sources */, 5492E058202154AB00B64F25 /* FSTAPIHelpers.mm in Sources */, AB380CFB2019388600D97691 /* target_id_generator_test.cc in Sources */, 5492E0A82021552D00B64F25 /* FSTLevelDBLocalStoreTests.mm in Sources */, + 549CCA5920A36E1F00BCEB75 /* precondition_test.cc in Sources */, ABC1D7DE2023A05300BA84F0 /* user_test.cc in Sources */, 5491BC721FB44593008B3588 /* FSTIntegrationTestCase.mm in Sources */, ABC1D7DD2023A04F00BA84F0 /* empty_credentials_provider_test.cc in Sources */, DE2EF0861F3D0B6E003D0CDC /* FSTImmutableSortedDictionary+Testing.m in Sources */, B686F2AF2023DDEE0028D6BE /* field_path_test.cc in Sources */, + 54A0352A20A3B3BD003E0143 /* testutil.cc in Sources */, 5492E03120213FFC00B64F25 /* FSTLevelDBSpecTests.mm in Sources */, B6FB468F208F9BAE00554BA2 /* executor_std_test.cc in Sources */, 5492E0B12021552D00B64F25 /* FSTRemoteDocumentCacheTests.mm in Sources */, 5492E0BA2021555100B64F25 /* FSTDocumentSetTests.mm in Sources */, 54740A581FC914F000713A1A /* autoid_test.cc in Sources */, - 548DB927200D590300E00ABC /* assert_test.cc in Sources */, 5492E0A62021552D00B64F25 /* FSTPersistenceTestHelpers.mm in Sources */, B6FB468E208F9BAB00554BA2 /* executor_libdispatch_test.mm in Sources */, 5467FB01203E5717009C9584 /* FIRFirestoreTests.mm in Sources */, B6FB4684208EA0EC00554BA2 /* async_queue_libdispatch_test.mm in Sources */, 5492E0A12021552D00B64F25 /* FSTMemoryLocalStoreTests.mm in Sources */, - 5436F32420008FAD006E51E3 /* string_printf_test.cc in Sources */, 5492E067202154B900B64F25 /* FSTEventManagerTests.mm in Sources */, + 54A0353020A3B3D8003E0143 /* statusor_test.cc in Sources */, 5492E0BF2021555100B64F25 /* FSTFieldValueTests.mm in Sources */, + 54A0352620A3AED0003E0143 /* field_transform_test.mm in Sources */, 5492E055202154AB00B64F25 /* FIRDocumentSnapshotTests.mm in Sources */, 5492E03E2021401F00B64F25 /* FSTEventAccumulator.mm in Sources */, + 54A0352720A3AED0003E0143 /* transform_operations_test.mm in Sources */, DE2EF0851F3D0B6E003D0CDC /* FSTArraySortedDictionaryTests.m in Sources */, 5492E0AA2021552D00B64F25 /* FSTLevelDBRemoteDocumentCacheTests.mm in Sources */, 5492E0AC2021552D00B64F25 /* FSTMutationQueueTests.mm in Sources */, 5492E056202154AB00B64F25 /* FIRFieldPathTests.mm in Sources */, 5492E03220213FFC00B64F25 /* FSTMockDatastore.mm in Sources */, ABC1D7E12023A40C00BA84F0 /* token_test.cc in Sources */, + 54A0353520A3D8CB003E0143 /* iterator_adaptors_test.cc in Sources */, AB356EF7200EA5EB0089B766 /* field_value_test.cc in Sources */, AB7BAB342012B519001E0872 /* geo_point_test.cc in Sources */, 5492E0AD2021552D00B64F25 /* FSTMemoryMutationQueueTests.mm in Sources */, @@ -1485,6 +1514,7 @@ 5492E054202154AB00B64F25 /* FIRFieldValueTests.mm in Sources */, AB6B908620322E6D00CC290A /* maybe_document_test.cc in Sources */, 5492E09F2021552D00B64F25 /* FSTLevelDBMigrationsTests.mm in Sources */, + 546854AA20A36867004BDBD5 /* datastore_test.cc in Sources */, 5492E053202154AB00B64F25 /* FIRDocumentReferenceTests.mm in Sources */, 5492E09D2021552D00B64F25 /* FSTLocalStoreTests.mm in Sources */, 5492E0A32021552D00B64F25 /* FSTLocalSerializerTests.mm in Sources */, @@ -1500,12 +1530,14 @@ 54995F6F205B6E12004EFFA0 /* leveldb_key_test.cc in Sources */, 5492E065202154B900B64F25 /* FSTViewTests.mm in Sources */, B6FB467D208E9D3C00554BA2 /* async_queue_test.cc in Sources */, + 54A0352F20A3B3D8003E0143 /* status_test.cc in Sources */, 5492E03C2021401F00B64F25 /* XCTestCase+Await.mm in Sources */, B6152AD7202A53CB000E5744 /* document_key_test.cc in Sources */, 5467FB08203E6A44009C9584 /* app_testing.mm in Sources */, 54764FAF1FAA21B90085E60A /* FSTGoogleTestTests.mm in Sources */, AB380D04201BC6E400D97691 /* ordered_code_test.cc in Sources */, 5492E03F2021401F00B64F25 /* FSTHelpers.mm in Sources */, + 549CCA5220A36DBC00BCEB75 /* sorted_map_test.cc in Sources */, 5492E068202154B900B64F25 /* FSTQueryTests.mm in Sources */, 5492E0AB2021552D00B64F25 /* StringViewTests.mm in Sources */, 5492E0C92021557E00B64F25 /* FSTRemoteEventTests.mm in Sources */, @@ -1520,9 +1552,11 @@ ABA495BB202B7E80008A7851 /* snapshot_version_test.cc in Sources */, 5492E0A02021552D00B64F25 /* FSTLevelDBMutationQueueTests.mm in Sources */, 54EB764D202277B30088B8F3 /* array_sorted_map_test.cc in Sources */, + 54131E9920AE076D001DF3FF /* hard_assert_test.cc in Sources */, 5492E03420213FFC00B64F25 /* FSTMemorySpecTests.mm in Sources */, AB380D02201BC69F00D97691 /* bits_test.cc in Sources */, 548DB929200D59F600E00ABC /* comparison_test.cc in Sources */, + 549CCA5720A36E1F00BCEB75 /* field_mask_test.cc in Sources */, 5492E03D2021401F00B64F25 /* FSTAssertTests.mm in Sources */, AB38D93020236E21000A432D /* database_info_test.cc in Sources */, 5492E052202154AB00B64F25 /* FIRGeoPointTests.mm in Sources */, @@ -1531,6 +1565,7 @@ 5492E03520213FFC00B64F25 /* FSTSpecTests.mm in Sources */, 5492E057202154AB00B64F25 /* FIRSnapshotMetadataTests.mm in Sources */, 54740A571FC914BA00713A1A /* secure_random_test.cc in Sources */, + 549CCA5020A36DBC00BCEB75 /* sorted_set_test.cc in Sources */, 5492E0BE2021555100B64F25 /* FSTMutationTests.mm in Sources */, 132E3E53179DE287D875F3F2 /* FSTLevelDBTransactionTests.mm in Sources */, ); @@ -1640,7 +1675,7 @@ /* Begin XCBuildConfiguration section */ 54C9EDF82040E16300A969CD /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 635C1D9B5E36BC4C12A35E70 /* Pods-Firestore_SwiftTests_iOS.debug.xcconfig */; + baseConfigurationReference = 69E6C311558EC77729A16CF1 /* Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NONNULL = YES; @@ -1679,7 +1714,7 @@ }; 54C9EDF92040E16300A969CD /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 39F1102E452A53A1F93AAA1F /* Pods-Firestore_SwiftTests_iOS.release.xcconfig */; + baseConfigurationReference = 11984BA0A99D7A7ABA5B0D90 /* Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NONNULL = YES; @@ -1793,7 +1828,7 @@ }; 6003F5C0195388D20070C39A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 555ACD6970390DC825A8F141 /* Pods-Firestore_Example_iOS.debug.xcconfig */; + baseConfigurationReference = 3C81DE3772628FE297055662 /* Pods-Firestore_Example_iOS.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; GCC_PRECOMPILE_PREFIX_HEADER = YES; @@ -1803,8 +1838,12 @@ "\"${PODS_ROOT}/Firebase/Firebase/Firebase\"", "\"${PODS_ROOT}/leveldb-library/include\"", ); - INFOPLIST_FILE = "Firestore/Firestore-Info.plist"; + INFOPLIST_FILE = "App/iOS/Firestore-Info.plist"; MODULE_NAME = ExampleApp; + OTHER_LDFLAGS = ( + "$(inherited)", + "-all_load", + ); PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; WRAPPER_EXTENSION = app; @@ -1813,7 +1852,7 @@ }; 6003F5C1195388D20070C39A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D8CA124A5F9A704EA0E12785 /* Pods-Firestore_Example_iOS.release.xcconfig */; + baseConfigurationReference = 3F0992A4B83C60841C52E960 /* Pods-Firestore_Example_iOS.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; GCC_PRECOMPILE_PREFIX_HEADER = YES; @@ -1823,8 +1862,12 @@ "\"${PODS_ROOT}/Firebase/Firebase/Firebase\"", "\"${PODS_ROOT}/leveldb-library/include\"", ); - INFOPLIST_FILE = "Firestore/Firestore-Info.plist"; + INFOPLIST_FILE = "App/iOS/Firestore-Info.plist"; MODULE_NAME = ExampleApp; + OTHER_LDFLAGS = ( + "$(inherited)", + "-all_load", + ); PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; WRAPPER_EXTENSION = app; @@ -1833,7 +1876,7 @@ }; 6003F5C3195388D20070C39A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = BE88081EE627C46349C918EF /* Pods-Firestore_Tests_iOS.debug.xcconfig */; + baseConfigurationReference = E592181BFD7C53C305123739 /* Pods-Firestore_Tests_iOS.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; DEVELOPMENT_TEAM = EQHXZ8M8AV; @@ -1853,8 +1896,9 @@ "$(inherited)", "\"${PODS_ROOT}/../../..\"", "\"${PODS_ROOT}/../../../Firestore/third_party/abseil-cpp\"", - "\"${PODS_ROOT}/leveldb-library/include\"", + "\"${PODS_ROOT}/GoogleTest/googlemock/include\"", "\"${PODS_ROOT}/GoogleTest/googletest/include\"", + "\"${PODS_ROOT}/leveldb-library/include\"", ); INFOPLIST_FILE = "Tests/Tests-Info.plist"; PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}"; @@ -1866,7 +1910,7 @@ }; 6003F5C4195388D20070C39A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 5A3E3BE5F322D66EE3D6CB65 /* Pods-Firestore_Tests_iOS.release.xcconfig */; + baseConfigurationReference = B3F5B3AAE791A5911B9EAA82 /* Pods-Firestore_Tests_iOS.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; DEVELOPMENT_TEAM = EQHXZ8M8AV; @@ -1886,8 +1930,9 @@ "$(inherited)", "\"${PODS_ROOT}/../../..\"", "\"${PODS_ROOT}/../../../Firestore/third_party/abseil-cpp\"", - "\"${PODS_ROOT}/leveldb-library/include\"", + "\"${PODS_ROOT}/GoogleTest/googlemock/include\"", "\"${PODS_ROOT}/GoogleTest/googletest/include\"", + "\"${PODS_ROOT}/leveldb-library/include\"", ); INFOPLIST_FILE = "Tests/Tests-Info.plist"; PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}"; @@ -1899,7 +1944,7 @@ }; DE03B2E71F2149D600A30B9C /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 3C7CE22C50805C4A854C73A1 /* Pods-Firestore_IntegrationTests_iOS.debug.xcconfig */; + baseConfigurationReference = 1277F98C20D2DF0867496976 /* Pods-Firestore_IntegrationTests_iOS.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; DEVELOPMENT_TEAM = EQHXZ8M8AV; @@ -1936,7 +1981,7 @@ }; DE03B2E81F2149D600A30B9C /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 618AC3C38A174084B9420162 /* Pods-Firestore_IntegrationTests_iOS.release.xcconfig */; + baseConfigurationReference = F354C0FE92645B56A6C6FD44 /* Pods-Firestore_IntegrationTests_iOS.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; DEVELOPMENT_TEAM = EQHXZ8M8AV; @@ -1973,7 +2018,7 @@ }; DE0761F31F2FE611003233AF /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 42491D7DC8C8CD245CC22B93 /* Pods-SwiftBuildTest.debug.xcconfig */; + baseConfigurationReference = 05C3D82261C3BE976889FF09 /* Pods-Firestore_Example_iOS-SwiftBuildTest.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ANALYZER_NONNULL = YES; @@ -1986,7 +2031,7 @@ DEVELOPMENT_TEAM = EQHXZ8M8AV; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; - INFOPLIST_FILE = "Firestore/Firestore-Info.plist"; + INFOPLIST_FILE = "App/iOS/Firestore-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 10.3; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MTL_ENABLE_DEBUG_INFO = YES; @@ -2000,7 +2045,7 @@ }; DE0761F41F2FE611003233AF /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = F23325524BEAF8D24F78AC88 /* Pods-SwiftBuildTest.release.xcconfig */; + baseConfigurationReference = 74ACEC3603BE58B57A7E8D4C /* Pods-Firestore_Example_iOS-SwiftBuildTest.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ANALYZER_NONNULL = YES; @@ -2014,7 +2059,7 @@ DEVELOPMENT_TEAM = EQHXZ8M8AV; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; - INFOPLIST_FILE = "Firestore/Firestore-Info.plist"; + INFOPLIST_FILE = "App/iOS/Firestore-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 10.3; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MTL_ENABLE_DEBUG_INFO = NO; diff --git a/Firestore/Example/Tests/GoogleTest/GoogleTest.podspec b/Firestore/Example/GoogleTest.podspec index 064fc59..0ecba3d 100644 --- a/Firestore/Example/Tests/GoogleTest/GoogleTest.podspec +++ b/Firestore/Example/GoogleTest.podspec @@ -41,6 +41,8 @@ Google's C++ test framework. # (e.g. gtest.h). We don't need them because they're effectively empty: # they're compile-time hooks for third-party customization that we don't use. s.public_header_files = [ + 'googlemock/include/gmock/*.h', + 'googlemock/include/gmock/internal/*.h', 'googletest/include/gtest/*.h', 'googletest/include/gtest/internal/*.h' ] @@ -54,6 +56,9 @@ Google's C++ test framework. ] s.source_files = [ + 'googlemock/src/*.cc', + 'googlemock/include/gmock/*.h', + 'googlemock/include/gmock/internal/*.h', 'googletest/src/*.cc', 'googletest/include/gtest/*.h', 'googletest/include/gtest/internal/*.h' @@ -61,8 +66,11 @@ Google's C++ test framework. s.exclude_files = [ # A convenience wrapper for a simple command-line build. If included in - # this build, results in duplicate symbols + # this build, results in duplicate symbols. + 'googlemock/src/gmock-all.cc', 'googletest/src/gtest-all.cc', + # Both gmock and gtest define a main function but we only need one. + 'googletest/src/gtest_main.cc', ] s.library = 'c++' @@ -70,12 +78,17 @@ Google's C++ test framework. # When building this pod there are headers in googletest/src. s.pod_target_xcconfig = { 'HEADER_SEARCH_PATHS' => - '"${PODS_ROOT}/GoogleTest/googletest/include" "${PODS_ROOT}/GoogleTest/googletest"' + '"${PODS_ROOT}/GoogleTest/googlemock/include" ' + + '"${PODS_ROOT}/GoogleTest/googletest/include" ' + + '"${PODS_ROOT}/GoogleTest/googletest"' } s.prepare_command = <<-'CMD' # Remove includes of files in internal/custom sed -i.bak -e '/include.*internal\/custom/ d' \ + googlemock/include/gmock/gmock-matchers.h \ + googlemock/include/gmock/gmock-generated-actions.h \ + googlemock/include/gmock/internal/gmock-port.h \ googletest/include/gtest/gtest-printers.h \ googletest/include/gtest/internal/gtest-port.h \ googletest/src/gtest-death-test.cc \ diff --git a/Firestore/Example/Podfile b/Firestore/Example/Podfile index c1e02c8..26af4cd 100644 --- a/Firestore/Example/Podfile +++ b/Firestore/Example/Podfile @@ -2,71 +2,39 @@ #source 'sso://cpdc-internal/spec' #source 'https://github.com/CocoaPods/Specs.git' -# 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', '5.0.0' - use_frameworks! -pod 'FirebaseAuth', :path => '../../' -pod 'FirebaseCore', :path => '../../' -pod 'FirebaseFirestore', :path => '../../' - target 'Firestore_Example_iOS' do platform :ios, '8.0' - # Test targets are below to avoid problems with duplicate symbols when - # building as a static library (see comments below). -end + # 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', '5.0.1' -target 'Firestore_Tests_iOS' do - platform :ios, '8.0' + pod 'FirebaseAuth', :path => '../../' + pod 'FirebaseCore', :path => '../../' + pod 'FirebaseFirestore', :path => '../../' - pod 'leveldb-library' - pod 'OCMock' - pod 'GoogleTest', :podspec => 'Tests/GoogleTest/GoogleTest.podspec' -end + target 'Firestore_Tests_iOS' do + inherit! :search_paths -target 'Firestore_IntegrationTests_iOS' do - platform :ios, '8.0' -end + pod 'leveldb-library' + pod 'OCMock' + pod 'GoogleTest', :podspec => 'GoogleTest.podspec' + end -target 'Firestore_SwiftTests_iOS' do - platform :ios, '8.0' - pod 'FirebaseFirestoreSwift', :path => '../../' -end + target 'Firestore_IntegrationTests_iOS' do + inherit! :search_paths -target 'SwiftBuildTest' do - platform :ios, '8.0' -end + pod 'OCMock' + end -# Firestore includes both Objective-C and C++ code, and the Firestore tests -# consist of both XCTest-based tests in Objective-C and GoogleTest-based tests -# in C++. The C++ tests must resolve the classes under test at link time, so -# CocoaPods usual strategy linking Frameworks to the app and then resolving -# those classes through run-time loading does not work in all cases. -# -# If use_frameworks! is disabled above, the project will encounter a ton of -# duplicate Objective-C class warnings during test runs. Some of the tests will -# fail too because duplicate classes also get duplicate static data and this -# violates the expectations of code we depend upon. -# -# The workaround is to strip duplicate dependencies out of the example app, -# which does not need them since it doesn't do anything other than act as a -# host to the tests. This is based on the workaround posted here: -# -# https://github.com/CocoaPods/CocoaPods/issues/7155 -# -# TODO(wilhuff): Reevaluate if this is needed once we require CocoaPods 1.5.1 -# which may address this. -pre_install do |installer| - test_target = installer.aggregate_targets.find do |target| - target.name == 'Pods-Firestore_Tests_iOS' + target 'Firestore_SwiftTests_iOS' do + pod 'FirebaseFirestoreSwift', :path => '../../' end - app_target = installer.aggregate_targets.find do |target| - target.name == 'Pods-Firestore_Example_iOS' + + target 'SwiftBuildTest' do + platform :ios, '8.0' end - app_target.pod_targets = app_target.pod_targets - test_target.pod_targets end diff --git a/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm b/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm index 7ae9704..5629075 100644 --- a/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm +++ b/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm @@ -15,6 +15,7 @@ */ #import <XCTest/XCTest.h> +#include <memory> #import "Firestore/Source/Core/FSTEventManager.h" #import "Firestore/Source/Core/FSTQuery.h" @@ -23,23 +24,29 @@ #import "Firestore/Source/Model/FSTDocumentSet.h" #import "Firestore/Source/Remote/FSTRemoteEvent.h" #import "Firestore/Source/Util/FSTAsyncQueryListener.h" -#import "Firestore/Source/Util/FSTDispatchQueue.h" #import "Firestore/Example/Tests/Util/FSTHelpers.h" +#include "Firestore/core/src/firebase/firestore/util/executor_libdispatch.h" +#include "absl/memory/memory.h" + +using firebase::firestore::util::internal::ExecutorLibdispatch; using firebase::firestore::model::DocumentKeySet; NS_ASSUME_NONNULL_BEGIN @interface FSTQueryListenerTests : XCTestCase -@property(nonatomic, strong, readonly) FSTDispatchQueue *asyncQueue; @end -@implementation FSTQueryListenerTests +@implementation FSTQueryListenerTests { + std::unique_ptr<ExecutorLibdispatch> _executor; +} - (void)setUp { - _asyncQueue = [FSTDispatchQueue - queueWith:dispatch_queue_create("FSTQueryListenerTests Queue", DISPATCH_QUEUE_SERIAL)]; + // TODO(varconst): moving this test to C++, it should be possible to store Executor as a value, + // not a pointer, and initialize it in the constructor. + _executor = absl::make_unique<ExecutorLibdispatch>( + dispatch_queue_create("FSTQueryListenerTests Queue", DISPATCH_QUEUE_SERIAL)); } - (void)testRaisesCollectionEvents { @@ -131,12 +138,12 @@ NS_ASSUME_NONNULL_BEGIN FSTDocument *doc1 = FSTTestDoc("rooms/Eros", 3, @{@"name" : @"Eros"}, NO); FSTDocument *doc2 = FSTTestDoc("rooms/Eros", 4, @{@"name" : @"Eros2"}, NO); - __block FSTAsyncQueryListener *listener = [[FSTAsyncQueryListener alloc] - initWithDispatchQueue:self.asyncQueue - snapshotHandler:^(FSTViewSnapshot *snapshot, NSError *error) { - [accum addObject:snapshot]; - [listener mute]; - }]; + __block FSTAsyncQueryListener *listener = + [[FSTAsyncQueryListener alloc] initWithExecutor:_executor.get() + snapshotHandler:^(FSTViewSnapshot *snapshot, NSError *error) { + [accum addObject:snapshot]; + [listener mute]; + }]; FSTView *view = [[FSTView alloc] initWithQuery:query remoteDocuments:DocumentKeySet{}]; FSTViewSnapshot *viewSnapshot1 = FSTTestApplyChanges(view, @[ doc1 ], nil); @@ -148,9 +155,7 @@ NS_ASSUME_NONNULL_BEGIN // Drain queue XCTestExpectation *expectation = [self expectationWithDescription:@"Queue drained"]; - [self.asyncQueue dispatchAsync:^{ - [expectation fulfill]; - }]; + _executor->Execute([=] { [expectation fulfill]; }); [self waitForExpectationsWithTimeout:4.0 handler:^(NSError *_Nullable expectationError) { diff --git a/Firestore/Example/Tests/Integration/API/FIRValidationTests.mm b/Firestore/Example/Tests/Integration/API/FIRValidationTests.mm index 8af8d15..599f1b2 100644 --- a/Firestore/Example/Tests/Integration/API/FIRValidationTests.mm +++ b/Firestore/Example/Tests/Integration/API/FIRValidationTests.mm @@ -557,6 +557,13 @@ @"array_contains different than orderBy works."); } +- (void)testQueryMustNotHaveMultipleArrayContainsFilters { + FIRCollectionReference *coll = [self.db collectionWithPath:@"collection"]; + FSTAssertThrows( + [[coll queryWhereField:@"foo" arrayContains:@1] queryWhereField:@"foo" arrayContains:@2], + @"Invalid Query. Queries only support a single arrayContains filter."); +} + #pragma mark - GeoPoint Validation - (void)testInvalidGeoPointParameters { diff --git a/Firestore/core/test/firebase/firestore/model/field_transform_test.cc b/Firestore/Example/Tests/Model/field_transform_test.mm index b66aeef..a22a0f3 100644 --- a/Firestore/core/test/firebase/firestore/model/field_transform_test.cc +++ b/Firestore/Example/Tests/Model/field_transform_test.mm @@ -26,9 +26,8 @@ namespace firestore { namespace model { TEST(FieldTransform, Getter) { - FieldTransform transform(testutil::Field("foo"), - absl::make_unique<ServerTimestampTransform>( - ServerTimestampTransform::Get())); + FieldTransform transform{testutil::Field("foo"), absl::make_unique<ServerTimestampTransform>( + ServerTimestampTransform::Get())}; EXPECT_EQ(testutil::Field("foo"), transform.path()); EXPECT_EQ(ServerTimestampTransform::Get(), transform.transformation()); diff --git a/Firestore/core/test/firebase/firestore/model/transform_operations_test.cc b/Firestore/Example/Tests/Model/transform_operations_test.mm index ec0882a..247ea13 100644 --- a/Firestore/core/test/firebase/firestore/model/transform_operations_test.cc +++ b/Firestore/Example/Tests/Model/transform_operations_test.mm @@ -31,9 +31,25 @@ class DummyOperation : public TransformOperation { return Type::Test; } + FSTFieldValue* ApplyToLocalView(FSTFieldValue* /* previousValue */, + FIRTimestamp* /* localWriteTime */) const override { + return nil; + } + + FSTFieldValue* ApplyToRemoteDocument(FSTFieldValue* /* previousValue */, + FSTFieldValue* /* transformResult */) const override { + return nil; + } + bool operator==(const TransformOperation& other) const override { return this == &other; } + + NSUInteger Hash() const override { + // arbitrary number, the same as used in ObjC implementation, since all + // instances are equal. + return 37; + } }; TEST(TransformOperations, ServerTimestamp) { diff --git a/Firestore/Example/Tests/SpecTests/FSTMockDatastore.mm b/Firestore/Example/Tests/SpecTests/FSTMockDatastore.mm index dd34556..c846ec5 100644 --- a/Firestore/Example/Tests/SpecTests/FSTMockDatastore.mm +++ b/Firestore/Example/Tests/SpecTests/FSTMockDatastore.mm @@ -21,7 +21,6 @@ #import "Firestore/Source/Remote/FSTSerializerBeta.h" #import "Firestore/Source/Remote/FSTStream.h" #import "Firestore/Source/Util/FSTAssert.h" -#import "Firestore/Source/Util/FSTLogger.h" #import "Firestore/Example/Tests/Remote/FSTWatchChange+Testing.h" @@ -29,6 +28,7 @@ #include "Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.h" #include "Firestore/core/src/firebase/firestore/core/database_info.h" #include "Firestore/core/src/firebase/firestore/model/database_id.h" +#include "Firestore/core/src/firebase/firestore/util/log.h" #include "Firestore/core/src/firebase/firestore/util/string_apple.h" using firebase::firestore::auth::CredentialsProvider; @@ -117,7 +117,7 @@ NS_ASSUME_NONNULL_BEGIN } - (void)watchQuery:(FSTQueryData *)query { - FSTLog(@"watchQuery: %d: %@", query.targetID, query.query); + LOG_DEBUG("watchQuery: %s: %s", query.targetID, query.query); self.datastore.watchStreamRequestCount += 1; // Snapshot version is ignored on the wire FSTQueryData *sentQueryData = [query queryDataByReplacingSnapshotVersion:SnapshotVersion::None() @@ -127,7 +127,7 @@ NS_ASSUME_NONNULL_BEGIN } - (void)unwatchTargetID:(FSTTargetID)targetID { - FSTLog(@"unwatchTargetID: %d", targetID); + LOG_DEBUG("unwatchTargetID: %s", targetID); [self.activeTargets removeObjectForKey:@(targetID)]; } diff --git a/Firestore/Example/Tests/SpecTests/FSTSpecTests.mm b/Firestore/Example/Tests/SpecTests/FSTSpecTests.mm index 5a7cb72..7b10bd4 100644 --- a/Firestore/Example/Tests/SpecTests/FSTSpecTests.mm +++ b/Firestore/Example/Tests/SpecTests/FSTSpecTests.mm @@ -37,7 +37,6 @@ #import "Firestore/Source/Util/FSTAssert.h" #import "Firestore/Source/Util/FSTClasses.h" #import "Firestore/Source/Util/FSTDispatchQueue.h" -#import "Firestore/Source/Util/FSTLogger.h" #import "Firestore/Example/Tests/Remote/FSTWatchChange+Testing.h" #import "Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.h" @@ -46,6 +45,7 @@ #include "Firestore/core/src/firebase/firestore/auth/user.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" #include "Firestore/core/src/firebase/firestore/model/snapshot_version.h" +#include "Firestore/core/src/firebase/firestore/util/log.h" #include "Firestore/core/src/firebase/firestore/util/string_apple.h" #include "Firestore/core/test/firebase/firestore/testutil/testutil.h" @@ -620,7 +620,7 @@ static NSString *const kNoIOSTag = @"no-ios"; @try { [self setUpForSpecWithConfig:config]; for (NSDictionary *step in steps) { - FSTLog(@"Doing step %@", step); + LOG_DEBUG("Doing step %s", step); [self doStep:step]; [self validateStepExpectations:step[@"expect"]]; [self validateStateExpectations:step[@"stateExpect"]]; diff --git a/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.mm b/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.mm index 2aa0e30..40ebfb9 100644 --- a/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.mm +++ b/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.mm @@ -31,7 +31,6 @@ #import "Firestore/Source/Remote/FSTDatastore.h" #import "Firestore/Source/Remote/FSTWatchChange.h" #import "Firestore/Source/Util/FSTAssert.h" -#import "Firestore/Source/Util/FSTLogger.h" #import "Firestore/Example/Tests/Core/FSTSyncEngine+Testing.h" #import "Firestore/Example/Tests/SpecTests/FSTMockDatastore.h" @@ -41,6 +40,7 @@ #include "Firestore/core/src/firebase/firestore/core/database_info.h" #include "Firestore/core/src/firebase/firestore/model/database_id.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" +#include "Firestore/core/src/firebase/firestore/util/log.h" using firebase::firestore::auth::EmptyCredentialsProvider; using firebase::firestore::auth::HashUser; @@ -202,7 +202,7 @@ NS_ASSUME_NONNULL_BEGIN FSTAssert([actualWrite isEqual:expectedWrite], @"Mock datastore received write %@ but first outstanding mutation was %@", actualWrite, expectedWrite); - FSTLog(@"A write was sent: %@", actualWrite); + LOG_DEBUG("A write was sent: %s", actualWrite); } - (int)sentWritesCount { @@ -271,7 +271,7 @@ NS_ASSUME_NONNULL_BEGIN [[self currentOutstandingWrites] removeObjectAtIndex:0]; } - FSTLog(@"Failing a write."); + LOG_DEBUG("Failing a write."); [self.dispatchQueue dispatchSync:^{ [self.datastore failWriteWithError:error]; }]; @@ -321,11 +321,11 @@ NS_ASSUME_NONNULL_BEGIN FSTOutstandingWrite *write = [[FSTOutstandingWrite alloc] init]; write.write = mutation; [[self currentOutstandingWrites] addObject:write]; - FSTLog(@"sending a user write."); + LOG_DEBUG("sending a user write."); [self.dispatchQueue dispatchSync:^{ [self.syncEngine writeMutations:@[ mutation ] completion:^(NSError *_Nullable error) { - FSTLog(@"A callback was called with error: %@", error); + LOG_DEBUG("A callback was called with error: %s", error); write.done = YES; write.error = error; }]; diff --git a/Firestore/Example/Tests/Util/FSTDispatchQueueTests.mm b/Firestore/Example/Tests/Util/FSTDispatchQueueTests.mm index 811fa34..1f49aa4 100644 --- a/Firestore/Example/Tests/Util/FSTDispatchQueueTests.mm +++ b/Firestore/Example/Tests/Util/FSTDispatchQueueTests.mm @@ -66,9 +66,10 @@ static const FSTTimerID timerID3 = FSTTimerIDWriteStreamConnectionBackoff; XCTAssertNotNil(caught); XCTAssertEqualObjects(caught.name, NSInternalInconsistencyException); - XCTAssertTrue( - [caught.reason hasPrefix:@"FIRESTORE INTERNAL ASSERTION FAILED: " - @"dispatchAsync called when we are already running on target"]); + XCTAssertTrue([caught.reason + hasPrefix: + @"FIRESTORE INTERNAL ASSERTION FAILED: " + @"Enqueue methods cannot be called when we are already running on target executor"]); } - (void)testDispatchAsyncAllowingSameQueueActuallyAllowsSameQueue { @@ -133,9 +134,10 @@ static const FSTTimerID timerID3 = FSTTimerIDWriteStreamConnectionBackoff; XCTAssertNotNil(caught); XCTAssertEqualObjects(caught.name, NSInternalInconsistencyException); - XCTAssertTrue( - [caught.reason hasPrefix:@"FIRESTORE INTERNAL ASSERTION FAILED: " - @"dispatchSync called when we are already running on target"]); + XCTAssertTrue([caught.reason + hasPrefix: + @"FIRESTORE INTERNAL ASSERTION FAILED: " + @"Enqueue methods cannot be called when we are already running on target executor"]); } - (void)testVerifyIsCurrentQueueActuallyRequiresCurrentQueue { @@ -150,7 +152,8 @@ static const FSTTimerID timerID3 = FSTTimerIDWriteStreamConnectionBackoff; } XCTAssertNotNil(caught); XCTAssertTrue([caught.reason hasPrefix:@"FIRESTORE INTERNAL ASSERTION FAILED: " - @"We are running on the wrong dispatch queue"]); + @"Expected to be called by the executor " + @"associated with this queue"]); } - (void)testVerifyIsCurrentQueueRequiresOperationIsInProgress { @@ -165,7 +168,7 @@ static const FSTTimerID timerID3 = FSTTimerIDWriteStreamConnectionBackoff; XCTAssertNotNil(caught); XCTAssertTrue( [caught.reason hasPrefix:@"FIRESTORE INTERNAL ASSERTION FAILED: " - @"verifyIsCurrentQueue called outside enterCheckedOperation"]); + @"VerifyIsCurrentQueue called when no operation is executing"]); } - (void)testVerifyIsCurrentQueueWorksWithOperationIsInProgress { @@ -194,9 +197,10 @@ static const FSTTimerID timerID3 = FSTTimerIDWriteStreamConnectionBackoff; }]; XCTAssertNil(problem); XCTAssertNotNil(caught); - XCTAssertTrue([caught.reason - hasPrefix:@"FIRESTORE INTERNAL ASSERTION FAILED: " - @"enterCheckedOperation may not be called when an operation is in progress"]); + XCTAssertTrue( + [caught.reason hasPrefix:@"FIRESTORE INTERNAL ASSERTION FAILED: " + @"ExecuteBlocking may not be called before the previous operation " + @"finishes executing"]); } /** @@ -217,8 +221,10 @@ static const FSTTimerID timerID3 = FSTTimerIDWriteStreamConnectionBackoff; _expectation = [self expectationWithDescription:@"Expected steps"]; _expectedSteps = @[ @1, @2, @3, @4 ]; [_queue dispatchAsync:[self blockForStep:1]]; - [_queue dispatchAfterDelay:0.005 timerID:timerID1 block:[self blockForStep:4]]; - [_queue dispatchAfterDelay:0.001 timerID:timerID2 block:[self blockForStep:3]]; + [_queue dispatchAsync:^{ + [_queue dispatchAfterDelay:0.005 timerID:timerID1 block:[self blockForStep:4]]; + [_queue dispatchAfterDelay:0.001 timerID:timerID2 block:[self blockForStep:3]]; + }]; [_queue dispatchAsync:[self blockForStep:2]]; [self awaitExpectations]; @@ -244,8 +250,10 @@ static const FSTTimerID timerID3 = FSTTimerIDWriteStreamConnectionBackoff; - (void)testCanManuallyDrainAllDelayedCallbacksForTesting { [_queue dispatchAsync:[self blockForStep:1]]; - [_queue dispatchAfterDelay:20 timerID:timerID1 block:[self blockForStep:4]]; - [_queue dispatchAfterDelay:10 timerID:timerID2 block:[self blockForStep:3]]; + [_queue dispatchAsync:^{ + [_queue dispatchAfterDelay:20 timerID:timerID1 block:[self blockForStep:4]]; + [_queue dispatchAfterDelay:10 timerID:timerID2 block:[self blockForStep:3]]; + }]; [_queue dispatchAsync:[self blockForStep:2]]; [_queue runDelayedCallbacksUntil:FSTTimerIDAll]; @@ -254,9 +262,11 @@ static const FSTTimerID timerID3 = FSTTimerIDWriteStreamConnectionBackoff; - (void)testCanManuallyDrainSpecificDelayedCallbacksForTesting { [_queue dispatchAsync:[self blockForStep:1]]; - [_queue dispatchAfterDelay:20 timerID:timerID1 block:[self blockForStep:5]]; - [_queue dispatchAfterDelay:10 timerID:timerID2 block:[self blockForStep:3]]; - [_queue dispatchAfterDelay:15 timerID:timerID3 block:[self blockForStep:4]]; + [_queue dispatchAsync:^{ + [_queue dispatchAfterDelay:20 timerID:timerID1 block:[self blockForStep:5]]; + [_queue dispatchAfterDelay:10 timerID:timerID2 block:[self blockForStep:3]]; + [_queue dispatchAfterDelay:15 timerID:timerID3 block:[self blockForStep:4]]; + }]; [_queue dispatchAsync:[self blockForStep:2]]; [_queue runDelayedCallbacksUntil:timerID3]; diff --git a/Firestore/Example/Tests/Util/FSTHelpers.mm b/Firestore/Example/Tests/Util/FSTHelpers.mm index dbd19aa..bc2f005 100644 --- a/Firestore/Example/Tests/Util/FSTHelpers.mm +++ b/Firestore/Example/Tests/Util/FSTHelpers.mm @@ -250,7 +250,7 @@ FSTPatchMutation *FSTTestPatchMutation(const absl::string_view path, BOOL merge = !updateMask.empty(); __block FSTObjectValue *objectValue = [FSTObjectValue objectValue]; - __block std::vector<FieldPath> fieldMaskPaths{}; + __block std::vector<FieldPath> fieldMaskPaths; [values enumerateKeysAndObjectsUsingBlock:^(NSString *key, id value, BOOL *stop) { const FieldPath path = testutil::Field(util::MakeStringView(key)); fieldMaskPaths.push_back(path); diff --git a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm index 1817015..434b77b 100644 --- a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm +++ b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm @@ -17,7 +17,13 @@ #import "Firestore/Example/Tests/Util/FSTIntegrationTestCase.h" #import <FirebaseCore/FIRLogger.h> -#import <FirebaseFirestore/FirebaseFirestore-umbrella.h> +#import <FirebaseFirestore/FIRCollectionReference.h> +#import <FirebaseFirestore/FIRDocumentChange.h> +#import <FirebaseFirestore/FIRDocumentReference.h> +#import <FirebaseFirestore/FIRDocumentSnapshot.h> +#import <FirebaseFirestore/FIRFirestoreSettings.h> +#import <FirebaseFirestore/FIRQuerySnapshot.h> +#import <FirebaseFirestore/FIRSnapshotMetadata.h> #import <GRPCClient/GRPCCall+ChannelArg.h> #import <GRPCClient/GRPCCall+Tests.h> diff --git a/Firestore/Source/API/FIRDocumentReference.mm b/Firestore/Source/API/FIRDocumentReference.mm index da67a5b..5ad606c 100644 --- a/Firestore/Source/API/FIRDocumentReference.mm +++ b/Firestore/Source/API/FIRDocumentReference.mm @@ -280,8 +280,8 @@ addSnapshotListenerInternalWithOptions:(FSTListenOptions *)internalOptions }; FSTAsyncQueryListener *asyncListener = - [[FSTAsyncQueryListener alloc] initWithDispatchQueue:self.firestore.client.userDispatchQueue - snapshotHandler:snapshotHandler]; + [[FSTAsyncQueryListener alloc] initWithExecutor:self.firestore.client.userExecutor + snapshotHandler:snapshotHandler]; FSTQueryListener *internalListener = [firestore.client listenToQuery:query diff --git a/Firestore/Source/API/FIRDocumentSnapshot.mm b/Firestore/Source/API/FIRDocumentSnapshot.mm index 614982b..388b3b3 100644 --- a/Firestore/Source/API/FIRDocumentSnapshot.mm +++ b/Firestore/Source/API/FIRDocumentSnapshot.mm @@ -50,7 +50,7 @@ static FSTServerTimestampBehavior InternalServerTimestampBehavor( case FIRServerTimestampBehaviorPrevious: return FSTServerTimestampBehaviorPrevious; default: - FIREBASE_ASSERT_MESSAGE(false, "Unexpected server timestamp option: %ld", (long)behavior); + HARD_FAIL("Unexpected server timestamp option: %s", behavior); } } diff --git a/Firestore/Source/API/FIRFieldPath.mm b/Firestore/Source/API/FIRFieldPath.mm index 4fd0022..68f3de1 100644 --- a/Firestore/Source/API/FIRFieldPath.mm +++ b/Firestore/Source/API/FIRFieldPath.mm @@ -47,7 +47,7 @@ NS_ASSUME_NONNULL_BEGIN FSTThrowInvalidArgument(@"Invalid field path. Provided names must not be empty."); } - std::vector<std::string> field_names{}; + std::vector<std::string> field_names; field_names.reserve(fieldNames.count); for (int i = 0; i < fieldNames.count; ++i) { if (fieldNames[i].length == 0) { diff --git a/Firestore/Source/API/FIRFirestore.mm b/Firestore/Source/API/FIRFirestore.mm index fe461d6..0ed9082 100644 --- a/Firestore/Source/API/FIRFirestore.mm +++ b/Firestore/Source/API/FIRFirestore.mm @@ -34,7 +34,6 @@ #import "Firestore/Source/Core/FSTFirestoreClient.h" #import "Firestore/Source/Util/FSTAssert.h" #import "Firestore/Source/Util/FSTDispatchQueue.h" -#import "Firestore/Source/Util/FSTLogger.h" #import "Firestore/Source/Util/FSTUsageValidation.h" #include "Firestore/core/src/firebase/firestore/auth/credentials_provider.h" @@ -42,15 +41,20 @@ #include "Firestore/core/src/firebase/firestore/core/database_info.h" #include "Firestore/core/src/firebase/firestore/model/database_id.h" #include "Firestore/core/src/firebase/firestore/model/resource_path.h" +#include "Firestore/core/src/firebase/firestore/util/log.h" #include "Firestore/core/src/firebase/firestore/util/string_apple.h" #include "absl/memory/memory.h" +#include "Firestore/core/src/firebase/firestore/util/executor_libdispatch.h" + namespace util = firebase::firestore::util; using firebase::firestore::auth::CredentialsProvider; using firebase::firestore::auth::FirebaseCredentialsProvider; using firebase::firestore::core::DatabaseInfo; using firebase::firestore::model::DatabaseId; using firebase::firestore::model::ResourcePath; +using util::internal::Executor; +using util::internal::ExecutorLibdispatch; NS_ASSUME_NONNULL_BEGIN @@ -242,42 +246,43 @@ extern "C" NSString *const FIRFirestoreErrorDomain = @"FIRFirestoreErrorDomain"; FSTAssert(_settings.dispatchQueue, @"FirestoreSettings.dispatchQueue cannot be nil."); if (!_settings.timestampsInSnapshotsEnabled) { - FSTWarn( - @"The behavior for system Date objects stored in Firestore is going to change " - "AND YOUR APP MAY BREAK.\n" - "To hide this warning and ensure your app does not break, you need to add " - "the following code to your app before calling any other Cloud Firestore methods:\n" - "\n" - "let db = Firestore.firestore()\n" - "let settings = db.settings\n" - "settings.areTimestampsInSnapshotsEnabled = true\n" - "db.settings = settings\n" - "\n" - "With this change, timestamps stored in Cloud Firestore will be read back as " - "Firebase Timestamp objects instead of as system Date objects. So you will " - "also need to update code expecting a Date to instead expect a Timestamp. " - "For example:\n" - "\n" - "// old:\n" - "let date: Date = documentSnapshot.get(\"created_at\") as! Date\n" - "// new:\n" - "let timestamp: Timestamp = documentSnapshot.get(\"created_at\") as! Timestamp\n" - "let date: Date = timestamp.dateValue()\n" - "\n" - "Please audit all existing usages of Date when you enable the new behavior. In a " - "future release, the behavior will be changed to the new behavior, so if you do not " - "follow these steps, YOUR APP MAY BREAK."); + LOG_WARN( + "The behavior for system Date objects stored in Firestore is going to change " + "AND YOUR APP MAY BREAK.\n" + "To hide this warning and ensure your app does not break, you need to add " + "the following code to your app before calling any other Cloud Firestore methods:\n" + "\n" + "let db = Firestore.firestore()\n" + "let settings = db.settings\n" + "settings.areTimestampsInSnapshotsEnabled = true\n" + "db.settings = settings\n" + "\n" + "With this change, timestamps stored in Cloud Firestore will be read back as " + "Firebase Timestamp objects instead of as system Date objects. So you will " + "also need to update code expecting a Date to instead expect a Timestamp. " + "For example:\n" + "\n" + "// old:\n" + "let date: Date = documentSnapshot.get(\"created_at\") as! Date\n" + "// new:\n" + "let timestamp: Timestamp = documentSnapshot.get(\"created_at\") as! Timestamp\n" + "let date: Date = timestamp.dateValue()\n" + "\n" + "Please audit all existing usages of Date when you enable the new behavior. In a " + "future release, the behavior will be changed to the new behavior, so if you do not " + "follow these steps, YOUR APP MAY BREAK."); } const DatabaseInfo database_info(*self.databaseID, util::MakeStringView(_persistenceKey), util::MakeStringView(_settings.host), _settings.sslEnabled); - FSTDispatchQueue *userDispatchQueue = [FSTDispatchQueue queueWith:_settings.dispatchQueue]; + std::unique_ptr<Executor> userExecutor = + absl::make_unique<ExecutorLibdispatch>(_settings.dispatchQueue); _client = [FSTFirestoreClient clientWithDatabaseInfo:database_info usePersistence:_settings.persistenceEnabled credentialsProvider:_credentialsProvider.get() - userDispatchQueue:userDispatchQueue + userExecutor:std::move(userExecutor) workerDispatchQueue:_workerDispatchQueue]; } } @@ -287,6 +292,11 @@ extern "C" NSString *const FIRFirestoreErrorDomain = @"FIRFirestoreErrorDomain"; if (!collectionPath) { FSTThrowInvalidArgument(@"Collection path cannot be nil."); } + if ([collectionPath containsString:@"//"]) { + FSTThrowInvalidArgument(@"Invalid path (%@). Paths must not contain // in them.", + collectionPath); + } + [self ensureClientConfigured]; const ResourcePath path = ResourcePath::FromString(util::MakeStringView(collectionPath)); return [FIRCollectionReference referenceWithPath:path firestore:self]; @@ -296,6 +306,10 @@ extern "C" NSString *const FIRFirestoreErrorDomain = @"FIRFirestoreErrorDomain"; if (!documentPath) { FSTThrowInvalidArgument(@"Document path cannot be nil."); } + if ([documentPath containsString:@"//"]) { + FSTThrowInvalidArgument(@"Invalid path (%@). Paths must not contain // in them.", documentPath); + } + [self ensureClientConfigured]; const ResourcePath path = ResourcePath::FromString(util::MakeStringView(documentPath)); return [FIRDocumentReference referenceWithPath:path firestore:self]; diff --git a/Firestore/Source/API/FIRQuery+Internal.h b/Firestore/Source/API/FIRQuery+Internal.h index c4f9f23..e207837 100644 --- a/Firestore/Source/API/FIRQuery+Internal.h +++ b/Firestore/Source/API/FIRQuery+Internal.h @@ -35,6 +35,8 @@ NS_ASSUME_NONNULL_BEGIN * Creates and returns a new `FIRQuery` with the additional filter that documents must contain * the specified field, it must be an array, and the array must contain the provided value. * + * A query can have only one arrayContains filter. + * * @param field The name of the field containing an array to search * @param value The value that must be contained in the array * @@ -49,6 +51,8 @@ NS_ASSUME_NONNULL_BEGIN * Creates and returns a new `FIRQuery` with the additional filter that documents must contain * the specified field, it must be an array, and the array must contain the provided value. * + * A query can have only one arrayContains filter. + * * @param path The path of the field containing an array to search * @param value The value that must be contained in the array * diff --git a/Firestore/Source/API/FIRQuery.mm b/Firestore/Source/API/FIRQuery.mm index c83af9c..ad4d2aa 100644 --- a/Firestore/Source/API/FIRQuery.mm +++ b/Firestore/Source/API/FIRQuery.mm @@ -183,8 +183,8 @@ addSnapshotListenerInternalWithOptions:(FSTListenOptions *)internalOptions }; FSTAsyncQueryListener *asyncListener = - [[FSTAsyncQueryListener alloc] initWithDispatchQueue:self.firestore.client.userDispatchQueue - snapshotHandler:snapshotHandler]; + [[FSTAsyncQueryListener alloc] initWithExecutor:self.firestore.client.userExecutor + snapshotHandler:snapshotHandler]; FSTQueryListener *internalListener = [firestore.client listenToQuery:query @@ -527,6 +527,11 @@ addSnapshotListenerInternalWithOptions:(FSTListenOptions *)internalOptions if (firstOrderByField) { [self validateOrderByField:*firstOrderByField matchesInequalityField:filter.field]; } + } else if (filter.filterOperator == FSTRelationFilterOperatorArrayContains) { + if ([self.query hasArrayContainsFilter]) { + FSTThrowInvalidUsage(@"InvalidQueryException", + @"Invalid Query. Queries only support a single arrayContains filter."); + } } } diff --git a/Firestore/Source/API/FSTUserDataConverter.mm b/Firestore/Source/API/FSTUserDataConverter.mm index 6d01c75..3484539 100644 --- a/Firestore/Source/API/FSTUserDataConverter.mm +++ b/Firestore/Source/API/FSTUserDataConverter.mm @@ -268,7 +268,7 @@ typedef NS_ENUM(NSInteger, FSTUserDataSource) { } - (instancetype)contextForField:(NSString *)fieldName { - std::unique_ptr<FieldPath> path{}; + std::unique_ptr<FieldPath> path; if (_path) { path = absl::make_unique<FieldPath>(_path->Append(util::MakeString(fieldName))); } @@ -282,7 +282,7 @@ typedef NS_ENUM(NSInteger, FSTUserDataSource) { } - (instancetype)contextForFieldPath:(const FieldPath &)fieldPath { - std::unique_ptr<FieldPath> path{}; + std::unique_ptr<FieldPath> path; if (_path) { path = absl::make_unique<FieldPath>(_path->Append(fieldPath)); } @@ -428,9 +428,9 @@ typedef NS_ENUM(NSInteger, FSTUserDataSource) { std::vector<FieldTransform> convertedFieldTransform; if (fieldMask) { - __block std::vector<FieldPath> fieldMaskPaths{}; + __block std::vector<FieldPath> fieldMaskPaths; [fieldMask enumerateObjectsUsingBlock:^(id fieldPath, NSUInteger idx, BOOL *stop) { - FieldPath path{}; + FieldPath path; if ([fieldPath isKindOfClass:[NSString class]]) { path = [FIRFieldPath pathWithDotSeparatedString:fieldPath].internalValue; @@ -490,14 +490,14 @@ typedef NS_ENUM(NSInteger, FSTUserDataSource) { NSDictionary *dict = input; - __block std::vector<FieldPath> fieldMaskPaths{}; + __block std::vector<FieldPath> fieldMaskPaths; __block FSTObjectValue *updateData = [FSTObjectValue objectValue]; FSTParseContext *context = [FSTParseContext contextWithSource:FSTUserDataSourceUpdate path:absl::make_unique<FieldPath>(FieldPath::EmptyPath())]; [dict enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) { - FieldPath path{}; + FieldPath path; if ([key isKindOfClass:[NSString class]]) { path = [FIRFieldPath pathWithDotSeparatedString:key].internalValue; diff --git a/Firestore/Source/Core/FSTFirestoreClient.h b/Firestore/Source/Core/FSTFirestoreClient.h index 7285e65..94c2284 100644 --- a/Firestore/Source/Core/FSTFirestoreClient.h +++ b/Firestore/Source/Core/FSTFirestoreClient.h @@ -15,6 +15,7 @@ */ #import <Foundation/Foundation.h> +#include <memory> #import "Firestore/Source/Core/FSTTypes.h" #import "Firestore/Source/Core/FSTViewSnapshot.h" @@ -23,6 +24,7 @@ #include "Firestore/core/src/firebase/firestore/auth/credentials_provider.h" #include "Firestore/core/src/firebase/firestore/core/database_info.h" #include "Firestore/core/src/firebase/firestore/model/database_id.h" +#include "Firestore/core/src/firebase/firestore/util/executor.h" @class FIRDocumentReference; @class FIRDocumentSnapshot; @@ -50,14 +52,15 @@ NS_ASSUME_NONNULL_BEGIN /** * Creates and returns a FSTFirestoreClient with the given parameters. * - * All callbacks and events will be triggered on the provided userDispatchQueue. + * All callbacks and events will be triggered on the provided userExecutor. */ -+ (instancetype)clientWithDatabaseInfo:(const firebase::firestore::core::DatabaseInfo &)databaseInfo - usePersistence:(BOOL)usePersistence - credentialsProvider:(firebase::firestore::auth::CredentialsProvider *) - credentialsProvider // no passing ownership - userDispatchQueue:(FSTDispatchQueue *)userDispatchQueue - workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue; ++ (instancetype) +clientWithDatabaseInfo:(const firebase::firestore::core::DatabaseInfo &)databaseInfo + usePersistence:(BOOL)usePersistence + credentialsProvider:(firebase::firestore::auth::CredentialsProvider *) + credentialsProvider // no passing ownership + userExecutor:(std::unique_ptr<firebase::firestore::util::internal::Executor>)userExecutor + workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue; - (instancetype)init __attribute__((unavailable("Use static constructor method."))); @@ -111,7 +114,7 @@ NS_ASSUME_NONNULL_BEGIN * Dispatch queue for user callbacks / events. This will often be the "Main Dispatch Queue" of the * app but the developer can configure it to a different queue if they so choose. */ -@property(nonatomic, strong, readonly) FSTDispatchQueue *userDispatchQueue; +- (firebase::firestore::util::internal::Executor *)userExecutor; @end diff --git a/Firestore/Source/Core/FSTFirestoreClient.mm b/Firestore/Source/Core/FSTFirestoreClient.mm index 658cf57..da19960 100644 --- a/Firestore/Source/Core/FSTFirestoreClient.mm +++ b/Firestore/Source/Core/FSTFirestoreClient.mm @@ -18,6 +18,7 @@ #include <future> // NOLINT(build/c++11) #include <memory> +#include <utility> #import "FIRFirestoreErrors.h" #import "Firestore/Source/API/FIRDocumentReference+Internal.h" @@ -44,11 +45,11 @@ #import "Firestore/Source/Util/FSTAssert.h" #import "Firestore/Source/Util/FSTClasses.h" #import "Firestore/Source/Util/FSTDispatchQueue.h" -#import "Firestore/Source/Util/FSTLogger.h" #include "Firestore/core/src/firebase/firestore/auth/credentials_provider.h" #include "Firestore/core/src/firebase/firestore/core/database_info.h" #include "Firestore/core/src/firebase/firestore/model/database_id.h" +#include "Firestore/core/src/firebase/firestore/util/log.h" #include "Firestore/core/src/firebase/firestore/util/string_apple.h" namespace util = firebase::firestore::util; @@ -58,6 +59,8 @@ using firebase::firestore::core::DatabaseInfo; using firebase::firestore::model::DatabaseId; using firebase::firestore::model::DocumentKeySet; +using firebase::firestore::util::internal::Executor; + NS_ASSUME_NONNULL_BEGIN @interface FSTFirestoreClient () { @@ -68,7 +71,7 @@ NS_ASSUME_NONNULL_BEGIN usePersistence:(BOOL)usePersistence credentialsProvider: (CredentialsProvider *)credentialsProvider // no passing ownership - userDispatchQueue:(FSTDispatchQueue *)userDispatchQueue + userExecutor:(std::unique_ptr<Executor>)userExecutor workerDispatchQueue:(FSTDispatchQueue *)queue NS_DESIGNATED_INITIALIZER; @property(nonatomic, assign, readonly) const DatabaseInfo *databaseInfo; @@ -91,18 +94,24 @@ NS_ASSUME_NONNULL_BEGIN @end -@implementation FSTFirestoreClient +@implementation FSTFirestoreClient { + std::unique_ptr<Executor> _userExecutor; +} + +- (Executor *)userExecutor { + return _userExecutor.get(); +} + (instancetype)clientWithDatabaseInfo:(const DatabaseInfo &)databaseInfo usePersistence:(BOOL)usePersistence credentialsProvider: (CredentialsProvider *)credentialsProvider // no passing ownership - userDispatchQueue:(FSTDispatchQueue *)userDispatchQueue + userExecutor:(std::unique_ptr<Executor>)userExecutor workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue { return [[FSTFirestoreClient alloc] initWithDatabaseInfo:databaseInfo usePersistence:usePersistence credentialsProvider:credentialsProvider - userDispatchQueue:userDispatchQueue + userExecutor:std::move(userExecutor) workerDispatchQueue:workerDispatchQueue]; } @@ -110,12 +119,12 @@ NS_ASSUME_NONNULL_BEGIN usePersistence:(BOOL)usePersistence credentialsProvider: (CredentialsProvider *)credentialsProvider // no passing ownership - userDispatchQueue:(FSTDispatchQueue *)userDispatchQueue + userExecutor:(std::unique_ptr<Executor>)userExecutor workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue { if (self = [super init]) { _databaseInfo = databaseInfo; _credentialsProvider = credentialsProvider; - _userDispatchQueue = userDispatchQueue; + _userExecutor = std::move(userExecutor); _workerDispatchQueue = workerDispatchQueue; auto userPromise = std::make_shared<std::promise<User>>(); @@ -217,7 +226,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)userDidChange:(const User &)user { [self.workerDispatchQueue verifyIsCurrentQueue]; - FSTLog(@"User Changed: %s", user.uid().c_str()); + LOG_DEBUG("User Changed: %s", user.uid()); [self.syncEngine userDidChange:user]; } @@ -230,9 +239,7 @@ NS_ASSUME_NONNULL_BEGIN [self.workerDispatchQueue dispatchAsync:^{ [self.remoteStore disableNetwork]; if (completion) { - [self.userDispatchQueue dispatchAsync:^{ - completion(nil); - }]; + self->_userExecutor->Execute([=] { completion(nil); }); } }]; } @@ -241,9 +248,7 @@ NS_ASSUME_NONNULL_BEGIN [self.workerDispatchQueue dispatchAsync:^{ [self.remoteStore enableNetwork]; if (completion) { - [self.userDispatchQueue dispatchAsync:^{ - completion(nil); - }]; + self->_userExecutor->Execute([=] { completion(nil); }); } }]; } @@ -255,9 +260,7 @@ NS_ASSUME_NONNULL_BEGIN [self.remoteStore shutdown]; [self.persistence shutdown]; if (completion) { - [self.userDispatchQueue dispatchAsync:^{ - completion(nil); - }]; + self->_userExecutor->Execute([=] { completion(nil); }); } }]; } @@ -338,18 +341,14 @@ NS_ASSUME_NONNULL_BEGIN [self.workerDispatchQueue dispatchAsync:^{ if (mutations.count == 0) { if (completion) { - [self.userDispatchQueue dispatchAsync:^{ - completion(nil); - }]; + self->_userExecutor->Execute([=] { completion(nil); }); } } else { [self.syncEngine writeMutations:mutations completion:^(NSError *error) { // Dispatch the result back onto the user dispatch queue. if (completion) { - [self.userDispatchQueue dispatchAsync:^{ - completion(error); - }]; + self->_userExecutor->Execute([=] { completion(error); }); } }]; } @@ -360,17 +359,16 @@ NS_ASSUME_NONNULL_BEGIN updateBlock:(FSTTransactionBlock)updateBlock completion:(FSTVoidIDErrorBlock)completion { [self.workerDispatchQueue dispatchAsync:^{ - [self.syncEngine transactionWithRetries:retries - workerDispatchQueue:self.workerDispatchQueue - updateBlock:updateBlock - completion:^(id _Nullable result, NSError *_Nullable error) { - // Dispatch the result back onto the user dispatch queue. - if (completion) { - [self.userDispatchQueue dispatchAsync:^{ - completion(result, error); - }]; - } - }]; + [self.syncEngine + transactionWithRetries:retries + workerDispatchQueue:self.workerDispatchQueue + updateBlock:updateBlock + completion:^(id _Nullable result, NSError *_Nullable error) { + // Dispatch the result back onto the user dispatch queue. + if (completion) { + self->_userExecutor->Execute([=] { completion(result, error); }); + } + }]; }]; } diff --git a/Firestore/Source/Core/FSTQuery.h b/Firestore/Source/Core/FSTQuery.h index 572fabb..e38d3dd 100644 --- a/Firestore/Source/Core/FSTQuery.h +++ b/Firestore/Source/Core/FSTQuery.h @@ -245,6 +245,9 @@ typedef NS_ENUM(NSInteger, FSTRelationFilterOperator) { */ - (const firebase::firestore::model::FieldPath *)inequalityFilterField; +/** Returns YES if the query has an arrayContains filter already. */ +- (BOOL)hasArrayContainsFilter; + /** Returns the first field in an order-by constraint, or nullptr if none. */ - (const firebase::firestore::model::FieldPath *)firstSortOrderField; diff --git a/Firestore/Source/Core/FSTQuery.mm b/Firestore/Source/Core/FSTQuery.mm index d3961e8..13ebadb 100644 --- a/Firestore/Source/Core/FSTQuery.mm +++ b/Firestore/Source/Core/FSTQuery.mm @@ -724,6 +724,16 @@ NSString *FSTStringFromQueryRelationOperator(FSTRelationFilterOperator filterOpe return nullptr; } +- (BOOL)hasArrayContainsFilter { + for (id<FSTFilter> filter in self.filters) { + if ([filter isKindOfClass:[FSTRelationFilter class]] && + ((FSTRelationFilter *)filter).filterOperator == FSTRelationFilterOperatorArrayContains) { + return YES; + } + } + return NO; +} + - (const FieldPath *)firstSortOrderField { if (self.explicitSortOrders.count > 0) { return &self.explicitSortOrders.firstObject.field; diff --git a/Firestore/Source/Core/FSTSnapshotVersion.h b/Firestore/Source/Core/FSTSnapshotVersion.h deleted file mode 100644 index 8649d40..0000000 --- a/Firestore/Source/Core/FSTSnapshotVersion.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2017 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import <Foundation/Foundation.h> - -NS_ASSUME_NONNULL_BEGIN - -@class FIRTimestamp; - -/** - * A version of a document in Firestore. This corresponds to the version timestamp, such as - * update_time or read_time. - */ -@interface FSTSnapshotVersion : NSObject <NSCopying> - -/** Creates a new version that is smaller than all other versions. */ -+ (instancetype)noVersion; - -/** Creates a new version representing the given timestamp. */ -+ (instancetype)versionWithTimestamp:(FIRTimestamp *)timestamp; - -- (instancetype)init NS_UNAVAILABLE; - -- (NSComparisonResult)compare:(FSTSnapshotVersion *)other; - -@property(nonatomic, strong, readonly) FIRTimestamp *timestamp; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Core/FSTSnapshotVersion.mm b/Firestore/Source/Core/FSTSnapshotVersion.mm deleted file mode 100644 index 58b2be4..0000000 --- a/Firestore/Source/Core/FSTSnapshotVersion.mm +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2017 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import "Firestore/Source/Core/FSTSnapshotVersion.h" - -#import "FIRTimestamp.h" - -NS_ASSUME_NONNULL_BEGIN - -@implementation FSTSnapshotVersion - -+ (instancetype)noVersion { - static FSTSnapshotVersion *min; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - FIRTimestamp *timestamp = [[FIRTimestamp alloc] initWithSeconds:0 nanoseconds:0]; - min = [FSTSnapshotVersion versionWithTimestamp:timestamp]; - }); - return min; -} - -+ (instancetype)versionWithTimestamp:(FIRTimestamp *)timestamp { - return [[FSTSnapshotVersion alloc] initWithTimestamp:timestamp]; -} - -- (instancetype)initWithTimestamp:(FIRTimestamp *)timestamp { - self = [super init]; - if (self) { - _timestamp = timestamp; - } - return self; -} - -#pragma mark - NSObject methods - -- (BOOL)isEqual:(id)object { - if (self == object) { - return YES; - } - if (![object isKindOfClass:[FSTSnapshotVersion class]]) { - return NO; - } - return [self.timestamp isEqual:((FSTSnapshotVersion *)object).timestamp]; -} - -- (NSUInteger)hash { - return self.timestamp.hash; -} - -- (NSString *)description { - return [NSString stringWithFormat:@"<FSTSnapshotVersion: %@>", self.timestamp]; -} - -- (id)copyWithZone:(NSZone *_Nullable)zone { - // Implements NSCopying without actually copying because timestamps are immutable. - return self; -} - -#pragma mark - Public methods - -- (NSComparisonResult)compare:(FSTSnapshotVersion *)other { - return [self.timestamp compare:other.timestamp]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Core/FSTSyncEngine.mm b/Firestore/Source/Core/FSTSyncEngine.mm index ed97d6c..9d9e526 100644 --- a/Firestore/Source/Core/FSTSyncEngine.mm +++ b/Firestore/Source/Core/FSTSyncEngine.mm @@ -40,12 +40,12 @@ #import "Firestore/Source/Remote/FSTRemoteEvent.h" #import "Firestore/Source/Util/FSTAssert.h" #import "Firestore/Source/Util/FSTDispatchQueue.h" -#import "Firestore/Source/Util/FSTLogger.h" #include "Firestore/core/src/firebase/firestore/auth/user.h" #include "Firestore/core/src/firebase/firestore/core/target_id_generator.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" #include "Firestore/core/src/firebase/firestore/model/snapshot_version.h" +#include "Firestore/core/src/firebase/firestore/util/log.h" using firebase::firestore::auth::HashUser; using firebase::firestore::auth::User; @@ -470,7 +470,7 @@ static const FSTListenSequenceNumber kIrrelevantSequenceNumber = -1; break; case FSTLimboDocumentChangeTypeRemoved: - FSTLog(@"Document no longer in limbo: %s", limboChange.key.ToString().c_str()); + LOG_DEBUG("Document no longer in limbo: %s", limboChange.key.ToString()); [self.limboDocumentRefs removeReferenceToKey:limboChange.key forID:targetID]; break; @@ -485,7 +485,7 @@ static const FSTListenSequenceNumber kIrrelevantSequenceNumber = -1; DocumentKey key{limboChange.key}; if (_limboTargetsByKey.find(key) == _limboTargetsByKey.end()) { - FSTLog(@"New document in limbo: %s", key.ToString().c_str()); + LOG_DEBUG("New document in limbo: %s", key.ToString()); TargetId limboTargetID = _targetIdGenerator.NextId(); FSTQuery *query = [FSTQuery queryWithPath:key.path()]; FSTQueryData *queryData = [[FSTQueryData alloc] initWithQuery:query diff --git a/Firestore/Source/Local/FSTLevelDB.mm b/Firestore/Source/Local/FSTLevelDB.mm index bc2f2eb..6d0f8af 100644 --- a/Firestore/Source/Local/FSTLevelDB.mm +++ b/Firestore/Source/Local/FSTLevelDB.mm @@ -25,7 +25,6 @@ #import "Firestore/Source/Local/FSTLevelDBRemoteDocumentCache.h" #import "Firestore/Source/Remote/FSTSerializerBeta.h" #import "Firestore/Source/Util/FSTAssert.h" -#import "Firestore/Source/Util/FSTLogger.h" #include "Firestore/core/src/firebase/firestore/auth/user.h" #include "Firestore/core/src/firebase/firestore/core/database_info.h" diff --git a/Firestore/Source/Local/FSTLevelDBKey.mm b/Firestore/Source/Local/FSTLevelDBKey.mm index e23f5b7..1fe0396 100644 --- a/Firestore/Source/Local/FSTLevelDBKey.mm +++ b/Firestore/Source/Local/FSTLevelDBKey.mm @@ -280,7 +280,7 @@ BOOL ReadDocumentKey(Slice *contents, FSTDocumentKey *__strong *result) { Slice completeSegments = *contents; std::string segment; - std::vector<std::string> path_segments{}; + std::vector<std::string> path_segments; for (;;) { // Advance a temporary slice to avoid advancing contents into the next key component which may // not be a path segment. diff --git a/Firestore/Source/Local/FSTLocalSerializer.mm b/Firestore/Source/Local/FSTLocalSerializer.mm index 8fa1278..2c5ab4d 100644 --- a/Firestore/Source/Local/FSTLocalSerializer.mm +++ b/Firestore/Source/Local/FSTLocalSerializer.mm @@ -18,6 +18,7 @@ #include <cinttypes> +#import "FIRTimestamp.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" diff --git a/Firestore/Source/Local/FSTLocalStore.mm b/Firestore/Source/Local/FSTLocalStore.mm index 0d6a785..245dd62 100644 --- a/Firestore/Source/Local/FSTLocalStore.mm +++ b/Firestore/Source/Local/FSTLocalStore.mm @@ -37,12 +37,12 @@ #import "Firestore/Source/Model/FSTMutationBatch.h" #import "Firestore/Source/Remote/FSTRemoteEvent.h" #import "Firestore/Source/Util/FSTAssert.h" -#import "Firestore/Source/Util/FSTLogger.h" #include "Firestore/core/src/firebase/firestore/auth/user.h" #include "Firestore/core/src/firebase/firestore/core/target_id_generator.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" #include "Firestore/core/src/firebase/firestore/model/snapshot_version.h" +#include "Firestore/core/src/firebase/firestore/util/log.h" using firebase::firestore::auth::User; using firebase::firestore::core::TargetIdGenerator; @@ -324,11 +324,11 @@ NS_ASSUME_NONNULL_BEGIN SnapshotVersion{doc.version} >= SnapshotVersion{existingDoc.version}) { [self.remoteDocumentCache addEntry:doc]; } else { - FSTLog( - @"FSTLocalStore Ignoring outdated watch update for %s. " - "Current version: %s Watch version: %s", - key.ToString().c_str(), existingDoc.version.timestamp().ToString().c_str(), - doc.version.timestamp().ToString().c_str()); + LOG_DEBUG( + "FSTLocalStore Ignoring outdated watch update for %s. " + "Current version: %s Watch version: %s", + key.ToString(), existingDoc.version.timestamp().ToString(), + doc.version.timestamp().ToString()); } // The document might be garbage because it was unreferenced by everything. diff --git a/Firestore/Source/Local/FSTQueryData.mm b/Firestore/Source/Local/FSTQueryData.mm index 5087dfc..16c3b2e 100644 --- a/Firestore/Source/Local/FSTQueryData.mm +++ b/Firestore/Source/Local/FSTQueryData.mm @@ -82,7 +82,7 @@ NS_ASSUME_NONNULL_BEGIN NSUInteger result = [self.query hash]; result = result * 31 + self.targetID; result = result * 31 + self.purpose; - result = result * 31 + [self.snapshotVersion hash]; + result = result * 31 + self.snapshotVersion.Hash(); result = result * 31 + [self.resumeToken hash]; return result; } diff --git a/Firestore/Source/Model/FSTDocument.mm b/Firestore/Source/Model/FSTDocument.mm index 8ebc9ab..8c4c801 100644 --- a/Firestore/Source/Model/FSTDocument.mm +++ b/Firestore/Source/Model/FSTDocument.mm @@ -23,6 +23,7 @@ #include "Firestore/core/src/firebase/firestore/model/document_key.h" #include "Firestore/core/src/firebase/firestore/model/field_path.h" +#include "Firestore/core/src/firebase/firestore/util/hashing.h" #include "Firestore/core/src/firebase/firestore/util/string_apple.h" namespace util = firebase::firestore::util; @@ -45,7 +46,6 @@ NS_ASSUME_NONNULL_BEGIN } - (instancetype)initWithKey:(DocumentKey)key version:(SnapshotVersion)version { - FSTAssert(!!version, @"Version must not be nil."); self = [super init]; if (self) { _key = std::move(key); @@ -102,13 +102,13 @@ NS_ASSUME_NONNULL_BEGIN } FSTDocument *otherDoc = other; - return [self.key isEqual:otherDoc.key] && [self.version isEqual:otherDoc.version] && + return [self.key isEqual:otherDoc.key] && self.version == otherDoc.version && [self.data isEqual:otherDoc.data] && self.hasLocalMutations == otherDoc.hasLocalMutations; } - (NSUInteger)hash { NSUInteger result = [self.key hash]; - result = result * 31 + [self.version hash]; + result = result * 31 + self.version.Hash(); result = result * 31 + [self.data hash]; result = result * 31 + (self.hasLocalMutations ? 1 : 0); return result; @@ -142,12 +142,12 @@ NS_ASSUME_NONNULL_BEGIN } FSTDocument *otherDoc = other; - return [self.key isEqual:otherDoc.key] && [self.version isEqual:otherDoc.version]; + return [self.key isEqual:otherDoc.key] && self.version == otherDoc.version; } - (NSUInteger)hash { NSUInteger result = [self.key hash]; - result = result * 31 + [self.version hash]; + result = result * 31 + self.version.Hash(); return result; } diff --git a/Firestore/Source/Model/FSTMutation.mm b/Firestore/Source/Model/FSTMutation.mm index ee6ef9c..82a535e 100644 --- a/Firestore/Source/Model/FSTMutation.mm +++ b/Firestore/Source/Model/FSTMutation.mm @@ -400,7 +400,7 @@ serverTransformResultsWithBaseDocument:(nullable FSTMaybeDocument *)baseDocument } [transformResults - addObject:transform.applyToRemoteDocument(previousValue, serverTransformResults[i])]; + addObject:transform.ApplyToRemoteDocument(previousValue, serverTransformResults[i])]; } return transformResults; } @@ -426,7 +426,7 @@ serverTransformResultsWithBaseDocument:(nullable FSTMaybeDocument *)baseDocument previousValue = [((FSTDocument *)baseDocument) fieldForPath:fieldTransform.path()]; } - [transformResults addObject:transform.applyToLocalView(previousValue, localWriteTime)]; + [transformResults addObject:transform.ApplyToLocalView(previousValue, localWriteTime)]; } return transformResults; } diff --git a/Firestore/Source/Public/FIRDocumentReference.h b/Firestore/Source/Public/FIRDocumentReference.h index 7baa30a..216a8dc 100644 --- a/Firestore/Source/Public/FIRDocumentReference.h +++ b/Firestore/Source/Public/FIRDocumentReference.h @@ -225,7 +225,8 @@ NS_SWIFT_NAME(DocumentReference) * @param completion a block to execute once the document has been successfully read. */ // clang-format off -- (void)getDocumentWithSource:(FIRFirestoreSource)source completion:(FIRDocumentSnapshotBlock)completion +- (void)getDocumentWithSource:(FIRFirestoreSource)source + completion:(FIRDocumentSnapshotBlock)completion NS_SWIFT_NAME(getDocument(source:completion:)); // clang-format on diff --git a/Firestore/Source/Public/FIRQuery.h b/Firestore/Source/Public/FIRQuery.h index 946cf06..799abcc 100644 --- a/Firestore/Source/Public/FIRQuery.h +++ b/Firestore/Source/Public/FIRQuery.h @@ -66,7 +66,8 @@ NS_SWIFT_NAME(Query) * documentSet will be `nil` only if error is `non-nil`. */ // clang-format off -- (void)getDocumentsWithSource:(FIRFirestoreSource)source completion:(FIRQuerySnapshotBlock)completion +- (void)getDocumentsWithSource:(FIRFirestoreSource)source + completion:(FIRQuerySnapshotBlock)completion NS_SWIFT_NAME(getDocuments(source:completion:)); // clang-format on diff --git a/Firestore/Source/Remote/FSTDatastore.mm b/Firestore/Source/Remote/FSTDatastore.mm index f0852fe..d45af6b 100644 --- a/Firestore/Source/Remote/FSTDatastore.mm +++ b/Firestore/Source/Remote/FSTDatastore.mm @@ -33,7 +33,6 @@ #import "Firestore/Source/Remote/FSTStream.h" #import "Firestore/Source/Util/FSTAssert.h" #import "Firestore/Source/Util/FSTDispatchQueue.h" -#import "Firestore/Source/Util/FSTLogger.h" #import "Firestore/Protos/objc/google/firestore/v1beta1/Firestore.pbrpc.h" @@ -43,6 +42,7 @@ #include "Firestore/core/src/firebase/firestore/model/database_id.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" #include "Firestore/core/src/firebase/firestore/util/error_apple.h" +#include "Firestore/core/src/firebase/firestore/util/log.h" #include "Firestore/core/src/firebase/firestore/util/string_apple.h" namespace util = firebase::firestore::util; @@ -217,8 +217,8 @@ typedef GRPCProtoCall * (^RPCFactory)(void); /** Logs the (whitelisted) headers returned for an GRPCProtoCall RPC. */ + (void)logHeadersForRPC:(GRPCProtoCall *)rpc RPCName:(NSString *)rpcName { if ([FIRFirestore isLoggingEnabled]) { - FSTLog(@"RPC %@ returned headers (whitelisted): %@", rpcName, - [FSTDatastore extractWhiteListedHeaders:rpc.responseHeaders]); + LOG_DEBUG("RPC %s returned headers (whitelisted): %s", rpcName, + [FSTDatastore extractWhiteListedHeaders:rpc.responseHeaders]); } } @@ -239,7 +239,7 @@ typedef GRPCProtoCall * (^RPCFactory)(void); handler:^(GCFSCommitResponse *response, NSError *_Nullable error) { error = [FSTDatastore firestoreErrorForError:error]; [self.workerDispatchQueue dispatchAsync:^{ - FSTLog(@"RPC CommitRequest completed. Error: %@", error); + LOG_DEBUG("RPC CommitRequest completed. Error: %s", error); [FSTDatastore logHeadersForRPC:rpc RPCName:@"CommitRequest"]; completion(error); }]; @@ -272,7 +272,7 @@ typedef GRPCProtoCall * (^RPCFactory)(void); error = [FSTDatastore firestoreErrorForError:error]; [self.workerDispatchQueue dispatchAsync:^{ if (error) { - FSTLog(@"RPC BatchGetDocuments completed. Error: %@", error); + LOG_DEBUG("RPC BatchGetDocuments completed. Error: %s", error); [FSTDatastore logHeadersForRPC:rpc RPCName:@"BatchGetDocuments"]; completion(nil, error); return; @@ -285,7 +285,7 @@ typedef GRPCProtoCall * (^RPCFactory)(void); closure->results.insert({doc.key, doc}); } else { // Streaming response is done, call completion - FSTLog(@"RPC BatchGetDocuments completed successfully."); + LOG_DEBUG("RPC BatchGetDocuments completed successfully."); [FSTDatastore logHeadersForRPC:rpc RPCName:@"BatchGetDocuments"]; FSTAssert(!response, @"Got response after done."); NSMutableArray<FSTMaybeDocument *> *docs = diff --git a/Firestore/Source/Remote/FSTExponentialBackoff.mm b/Firestore/Source/Remote/FSTExponentialBackoff.mm index 20b50a5..2bddbf7 100644 --- a/Firestore/Source/Remote/FSTExponentialBackoff.mm +++ b/Firestore/Source/Remote/FSTExponentialBackoff.mm @@ -18,10 +18,10 @@ #include <random> -#include "Firestore/core/src/firebase/firestore/util/secure_random.h" - #import "Firestore/Source/Util/FSTDispatchQueue.h" -#import "Firestore/Source/Util/FSTLogger.h" + +#include "Firestore/core/src/firebase/firestore/util/log.h" +#include "Firestore/core/src/firebase/firestore/util/secure_random.h" using firebase::firestore::util::SecureRandom; @@ -71,8 +71,7 @@ using firebase::firestore::util::SecureRandom; // First schedule the block using the current base (which may be 0 and should be honored as such). NSTimeInterval delayWithJitter = _currentBase + [self jitterDelay]; if (_currentBase > 0) { - FSTLog(@"Backing off for %.2f seconds (base delay: %.2f seconds)", delayWithJitter, - _currentBase); + LOG_DEBUG("Backing off for %s seconds (base delay: %s seconds)", delayWithJitter, _currentBase); } self.timerCallback = diff --git a/Firestore/Source/Remote/FSTOnlineStateTracker.mm b/Firestore/Source/Remote/FSTOnlineStateTracker.mm index e782397..e512a3c 100644 --- a/Firestore/Source/Remote/FSTOnlineStateTracker.mm +++ b/Firestore/Source/Remote/FSTOnlineStateTracker.mm @@ -18,7 +18,8 @@ #import "Firestore/Source/Remote/FSTRemoteStore.h" #import "Firestore/Source/Util/FSTAssert.h" #import "Firestore/Source/Util/FSTDispatchQueue.h" -#import "Firestore/Source/Util/FSTLogger.h" + +#include "Firestore/core/src/firebase/firestore/util/log.h" NS_ASSUME_NONNULL_BEGIN @@ -83,10 +84,9 @@ static const NSTimeInterval kOnlineStateTimeout = 10; FSTAssert( self.state == FSTOnlineStateUnknown, @"Timer should be canceled if we transitioned to a different state."); - FSTLog( - @"Watch stream didn't reach Online or Offline within %f seconds. " - @"Considering " - "client offline.", + LOG_DEBUG( + "Watch stream didn't reach Online or Offline within %s seconds. " + "Considering client offline.", kOnlineStateTimeout); [self logClientOfflineWarningIfNecessary]; [self setAndBroadcastState:FSTOnlineStateOffline]; @@ -138,7 +138,7 @@ static const NSTimeInterval kOnlineStateTimeout = 10; - (void)logClientOfflineWarningIfNecessary { if (self.shouldWarnClientIsOffline) { - FSTWarn(@"Could not reach Firestore backend."); + LOG_WARN("Could not reach Firestore backend."); self.shouldWarnClientIsOffline = NO; } } diff --git a/Firestore/Source/Remote/FSTRemoteEvent.mm b/Firestore/Source/Remote/FSTRemoteEvent.mm index 438072e..ebd9b93 100644 --- a/Firestore/Source/Remote/FSTRemoteEvent.mm +++ b/Firestore/Source/Remote/FSTRemoteEvent.mm @@ -24,9 +24,10 @@ #import "Firestore/Source/Remote/FSTWatchChange.h" #import "Firestore/Source/Util/FSTAssert.h" #import "Firestore/Source/Util/FSTClasses.h" -#import "Firestore/Source/Util/FSTLogger.h" + #include "Firestore/core/src/firebase/firestore/model/document_key.h" #include "Firestore/core/src/firebase/firestore/util/hashing.h" +#include "Firestore/core/src/firebase/firestore/util/log.h" using firebase::firestore::model::DocumentKey; using firebase::firestore::model::SnapshotVersion; @@ -577,7 +578,7 @@ initWithSnapshotVersion:(SnapshotVersion)snapshotVersion } break; default: - FSTWarn(@"Unknown target watch change type: %ld", (long)targetChange.state); + LOG_WARN("Unknown target watch change type: %s", targetChange.state); } } } diff --git a/Firestore/Source/Remote/FSTRemoteStore.mm b/Firestore/Source/Remote/FSTRemoteStore.mm index 0ea4887..e0adb4e 100644 --- a/Firestore/Source/Remote/FSTRemoteStore.mm +++ b/Firestore/Source/Remote/FSTRemoteStore.mm @@ -32,11 +32,11 @@ #import "Firestore/Source/Remote/FSTStream.h" #import "Firestore/Source/Remote/FSTWatchChange.h" #import "Firestore/Source/Util/FSTAssert.h" -#import "Firestore/Source/Util/FSTLogger.h" #include "Firestore/core/src/firebase/firestore/auth/user.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" #include "Firestore/core/src/firebase/firestore/model/snapshot_version.h" +#include "Firestore/core/src/firebase/firestore/util/log.h" #include "Firestore/core/src/firebase/firestore/util/string_apple.h" namespace util = firebase::firestore::util; @@ -205,7 +205,7 @@ static const int kMaxPendingWrites = 10; #pragma mark Shutdown - (void)shutdown { - FSTLog(@"FSTRemoteStore %p shutting down", (__bridge void *)self); + LOG_DEBUG("FSTRemoteStore %s shutting down", (__bridge void *)self); [self disableNetworkInternal]; // Set the FSTOnlineState to Unknown (rather than Offline) to avoid potentially triggering // spurious listener events with cached data, etc. @@ -213,7 +213,7 @@ static const int kMaxPendingWrites = 10; } - (void)userDidChange:(const User &)user { - FSTLog(@"FSTRemoteStore %p changing users: %s", (__bridge void *)self, user.uid().c_str()); + LOG_DEBUG("FSTRemoteStore %s changing users: %s", (__bridge void *)self, user.uid()); if ([self isNetworkEnabled]) { // Tear down and re-create our network streams. This will ensure we get a fresh auth token // for the new user and re-fill the write pipeline with new mutations from the LocalStore @@ -408,7 +408,7 @@ static const int kMaxPendingWrites = 10; } if (trackedRemote.size() != static_cast<size_t>(filter.count)) { - FSTLog(@"Existence filter mismatch, resetting mapping"); + LOG_DEBUG("Existence filter mismatch, resetting mapping"); // Make sure the mismatch is exposed in the remote event [remoteEvent handleExistenceFilterMismatchForTargetID:target]; @@ -490,8 +490,7 @@ static const int kMaxPendingWrites = 10; - (void)cleanUpWriteStreamState { self.lastBatchSeen = kFSTBatchIDUnknown; - FSTLog(@"Stopping write stream with %lu pending writes", - (unsigned long)[self.pendingWrites count]); + LOG_DEBUG("Stopping write stream with %s pending writes", [self.pendingWrites count]); [self.pendingWrites removeAllObjects]; } @@ -618,8 +617,8 @@ static const int kMaxPendingWrites = 10; // stream is no longer valid. if ([FSTDatastore isPermanentWriteError:error] || [FSTDatastore isAbortedError:error]) { NSString *token = [self.writeStream.lastStreamToken base64EncodedStringWithOptions:0]; - FSTLog(@"FSTRemoteStore %p error before completed handshake; resetting stream token %@: %@", - (__bridge void *)self, token, error); + LOG_DEBUG("FSTRemoteStore %s error before completed handshake; resetting stream token %s: %s", + (__bridge void *)self, token, error); self.writeStream.lastStreamToken = nil; [self.localStore setLastStreamToken:nil]; } diff --git a/Firestore/Source/Remote/FSTSerializerBeta.mm b/Firestore/Source/Remote/FSTSerializerBeta.mm index 782e54c..f862ec3 100644 --- a/Firestore/Source/Remote/FSTSerializerBeta.mm +++ b/Firestore/Source/Remote/FSTSerializerBeta.mm @@ -683,8 +683,8 @@ NS_ASSUME_NONNULL_BEGIN [transformResults addObject:[self decodedFieldValue:result]]; } } - return [[FSTMutationResult alloc] - initWithVersion:(version ? version.value() : nil)transformResults:transformResults]; + return [[FSTMutationResult alloc] initWithVersion:std::move(version) + transformResults:transformResults]; } #pragma mark - FSTQueryData => GCFSTarget proto diff --git a/Firestore/Source/Remote/FSTStream.mm b/Firestore/Source/Remote/FSTStream.mm index f4ec675..0e45418 100644 --- a/Firestore/Source/Remote/FSTStream.mm +++ b/Firestore/Source/Remote/FSTStream.mm @@ -29,7 +29,6 @@ #import "Firestore/Source/Util/FSTAssert.h" #import "Firestore/Source/Util/FSTClasses.h" #import "Firestore/Source/Util/FSTDispatchQueue.h" -#import "Firestore/Source/Util/FSTLogger.h" #import "Firestore/Protos/objc/google/firestore/v1beta1/Firestore.pbrpc.h" @@ -38,6 +37,7 @@ #include "Firestore/core/src/firebase/firestore/model/database_id.h" #include "Firestore/core/src/firebase/firestore/model/snapshot_version.h" #include "Firestore/core/src/firebase/firestore/util/error_apple.h" +#include "Firestore/core/src/firebase/firestore/util/log.h" #include "Firestore/core/src/firebase/firestore/util/string_apple.h" namespace util = firebase::firestore::util; @@ -258,7 +258,7 @@ static const NSTimeInterval kIdleTimeout = 60.0; return; } - FSTLog(@"%@ %p start", NSStringFromClass([self class]), (__bridge void *)self); + LOG_DEBUG("%s %s start", NSStringFromClass([self class]), (__bridge void *)self); FSTAssert(self.state == FSTStreamStateInitial, @"Already started"); self.state = FSTStreamStateAuth; @@ -311,7 +311,7 @@ static const NSTimeInterval kIdleTimeout = 60.0; /** Backs off after an error. */ - (void)performBackoffWithDelegate:(id)delegate { - FSTLog(@"%@ %p backoff", NSStringFromClass([self class]), (__bridge void *)self); + LOG_DEBUG("%s %s backoff", NSStringFromClass([self class]), (__bridge void *)self); [self.workerDispatchQueue verifyIsCurrentQueue]; FSTAssert(self.state == FSTStreamStateError, @"Should only perform backoff in an error case"); @@ -381,13 +381,13 @@ static const NSTimeInterval kIdleTimeout = 60.0; // If this is an intentional close ensure we don't delay our next connection attempt. [self.backoff reset]; } else if (error != nil && error.code == FIRFirestoreErrorCodeResourceExhausted) { - FSTLog(@"%@ %p Using maximum backoff delay to prevent overloading the backend.", [self class], - (__bridge void *)self); + LOG_DEBUG("%s %s Using maximum backoff delay to prevent overloading the backend.", [self class], + (__bridge void *)self); [self.backoff resetToMax]; } if (finalState != FSTStreamStateError) { - FSTLog(@"%@ %p Performing stream teardown", [self class], (__bridge void *)self); + LOG_DEBUG("%s %s Performing stream teardown", [self class], (__bridge void *)self); [self tearDown]; } @@ -395,7 +395,7 @@ static const NSTimeInterval kIdleTimeout = 60.0; // Clean up the underlying RPC. If this close: is in response to an error, don't attempt to // call half-close to avoid secondary failures. if (finalState != FSTStreamStateError) { - FSTLog(@"%@ %p Closing stream client-side", [self class], (__bridge void *)self); + LOG_DEBUG("%s %s Closing stream client-side", [self class], (__bridge void *)self); @synchronized(self.requestsWriter) { [self.requestsWriter finishWithError:nil]; } @@ -426,7 +426,7 @@ static const NSTimeInterval kIdleTimeout = 60.0; } - (void)stop { - FSTLog(@"%@ %p stop", NSStringFromClass([self class]), (__bridge void *)self); + LOG_DEBUG("%s %s stop", NSStringFromClass([self class]), (__bridge void *)self); if ([self isStarted]) { [self closeWithFinalState:FSTStreamStateStopped error:nil]; } @@ -550,7 +550,7 @@ static const NSTimeInterval kIdleTimeout = 60.0; * Called by the stream when the underlying RPC has been closed for whatever reason. */ - (void)handleStreamClose:(nullable NSError *)error { - FSTLog(@"%@ %p close: %@", NSStringFromClass([self class]), (__bridge void *)self, error); + LOG_DEBUG("%s %s close: %s", NSStringFromClass([self class]), (__bridge void *)self, error); FSTAssert([self isStarted], @"handleStreamClose: called for non-started stream."); // In theory the stream could close cleanly, however, in our current model we never expect this @@ -577,9 +577,9 @@ static const NSTimeInterval kIdleTimeout = 60.0; if (!self.messageReceived) { self.messageReceived = YES; if ([FIRFirestore isLoggingEnabled]) { - FSTLog(@"%@ %p headers (whitelisted): %@", NSStringFromClass([self class]), - (__bridge void *)self, - [FSTDatastore extractWhiteListedHeaders:self.rpc.responseHeaders]); + LOG_DEBUG("%s %s headers (whitelisted): %s", NSStringFromClass([self class]), + (__bridge void *)self, + [FSTDatastore extractWhiteListedHeaders:self.rpc.responseHeaders]); } } NSError *error; @@ -665,7 +665,7 @@ static const NSTimeInterval kIdleTimeout = 60.0; request.addTarget = [_serializer encodedTarget:query]; request.labels = [_serializer encodedListenRequestLabelsForQueryData:query]; - FSTLog(@"FSTWatchStream %p watch: %@", (__bridge void *)self, request); + LOG_DEBUG("FSTWatchStream %s watch: %s", (__bridge void *)self, request); [self writeRequest:request]; } @@ -677,7 +677,7 @@ static const NSTimeInterval kIdleTimeout = 60.0; request.database = [_serializer encodedDatabaseID]; request.removeTarget = targetID; - FSTLog(@"FSTWatchStream %p unwatch: %@", (__bridge void *)self, request); + LOG_DEBUG("FSTWatchStream %s unwatch: %s", (__bridge void *)self, request); [self writeRequest:request]; } @@ -686,7 +686,7 @@ static const NSTimeInterval kIdleTimeout = 60.0; * watchStreamDidChange:snapshotVersion: callback. */ - (void)handleStreamMessage:(GCFSListenResponse *)proto { - FSTLog(@"FSTWatchStream %p response: %@", (__bridge void *)self, proto); + LOG_DEBUG("FSTWatchStream %s response: %s", (__bridge void *)self, proto); [self.workerDispatchQueue verifyIsCurrentQueue]; // A successful response means the stream is healthy. @@ -765,7 +765,7 @@ static const NSTimeInterval kIdleTimeout = 60.0; // TODO(dimond): Support stream resumption. We intentionally do not set the stream token on the // handshake, ignoring any stream token we might have. - FSTLog(@"FSTWriteStream %p initial request: %@", (__bridge void *)self, request); + LOG_DEBUG("FSTWriteStream %s initial request: %s", (__bridge void *)self, request); [self writeRequest:request]; } @@ -783,7 +783,7 @@ static const NSTimeInterval kIdleTimeout = 60.0; request.writesArray = protos; request.streamToken = self.lastStreamToken; - FSTLog(@"FSTWriteStream %p mutation request: %@", (__bridge void *)self, request); + LOG_DEBUG("FSTWriteStream %s mutation request: %s", (__bridge void *)self, request); [self writeRequest:request]; } @@ -792,7 +792,7 @@ static const NSTimeInterval kIdleTimeout = 60.0; * that on to the mutationResultsHandler. */ - (void)handleStreamMessage:(GCFSWriteResponse *)response { - FSTLog(@"FSTWriteStream %p response: %@", (__bridge void *)self, response); + LOG_DEBUG("FSTWriteStream %s response: %s", (__bridge void *)self, response); [self.workerDispatchQueue verifyIsCurrentQueue]; // Always capture the last stream token. diff --git a/Firestore/Source/Util/FSTAsyncQueryListener.h b/Firestore/Source/Util/FSTAsyncQueryListener.h index 4888268..06471d8 100644 --- a/Firestore/Source/Util/FSTAsyncQueryListener.h +++ b/Firestore/Source/Util/FSTAsyncQueryListener.h @@ -18,6 +18,8 @@ #import "Firestore/Source/Core/FSTViewSnapshot.h" +#include "Firestore/core/src/firebase/firestore/util/executor.h" + NS_ASSUME_NONNULL_BEGIN @class FSTDispatchQueue; @@ -28,9 +30,8 @@ NS_ASSUME_NONNULL_BEGIN */ @interface FSTAsyncQueryListener : NSObject -- (instancetype)initWithDispatchQueue:(FSTDispatchQueue *)dispatchQueue - snapshotHandler:(FSTViewSnapshotHandler)snapshotHandler - NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithExecutor:(firebase::firestore::util::internal::Executor*)executor + snapshotHandler:(FSTViewSnapshotHandler)snapshotHandler NS_DESIGNATED_INITIALIZER; - (instancetype)init NS_UNAVAILABLE; diff --git a/Firestore/Source/Util/FSTAsyncQueryListener.mm b/Firestore/Source/Util/FSTAsyncQueryListener.mm index b72ac57..81dd41f 100644 --- a/Firestore/Source/Util/FSTAsyncQueryListener.mm +++ b/Firestore/Source/Util/FSTAsyncQueryListener.mm @@ -18,16 +18,18 @@ #import "Firestore/Source/Util/FSTDispatchQueue.h" +using firebase::firestore::util::internal::Executor; + @implementation FSTAsyncQueryListener { volatile BOOL _muted; FSTViewSnapshotHandler _snapshotHandler; - FSTDispatchQueue *_dispatchQueue; + Executor *_executor; } -- (instancetype)initWithDispatchQueue:(FSTDispatchQueue *)dispatchQueue - snapshotHandler:(FSTViewSnapshotHandler)snapshotHandler { +- (instancetype)initWithExecutor:(Executor *)executor + snapshotHandler:(FSTViewSnapshotHandler)snapshotHandler { if (self = [super init]) { - _dispatchQueue = dispatchQueue; + _executor = executor; _snapshotHandler = snapshotHandler; } return self; @@ -40,11 +42,11 @@ // users just want to turn on notifications "forever" and don't want to have // to keep track of our handle to keep them going. return ^(FSTViewSnapshot *_Nullable snapshot, NSError *_Nullable error) { - [self->_dispatchQueue dispatchAsync:^{ + self->_executor->Execute([self, snapshot, error] { if (!self->_muted) { self->_snapshotHandler(snapshot, error); } - }]; + }); }; } diff --git a/Firestore/Source/Util/FSTDispatchQueue.mm b/Firestore/Source/Util/FSTDispatchQueue.mm index 0974359..01b2732 100644 --- a/Firestore/Source/Util/FSTDispatchQueue.mm +++ b/Firestore/Source/Util/FSTDispatchQueue.mm @@ -16,154 +16,66 @@ #import <Foundation/Foundation.h> -#include <atomic> +#include <memory> +#include <utility> #import "Firestore/Source/Util/FSTAssert.h" #import "Firestore/Source/Util/FSTDispatchQueue.h" -NS_ASSUME_NONNULL_BEGIN - -/** - * removeDelayedCallback is used by FSTDelayedCallback and so we pre-declare it before the rest of - * the FSTDispatchQueue private interface. - */ -@interface FSTDispatchQueue () -- (void)removeDelayedCallback:(FSTDelayedCallback *)callback; -@end +#include "Firestore/core/src/firebase/firestore/util/async_queue.h" +#include "Firestore/core/src/firebase/firestore/util/executor_libdispatch.h" +#include "absl/memory/memory.h" -#pragma mark - FSTDelayedCallback - -/** - * Represents a callback scheduled to be run in the future on an FSTDispatchQueue. - * - * It is created via [FSTDelayedCallback createAndScheduleWithQueue]. - * - * Supports cancellation (via cancel) and early execution (via skipDelay). - */ -@interface FSTDelayedCallback () +using firebase::firestore::util::AsyncQueue; +using firebase::firestore::util::DelayedOperation; +using firebase::firestore::util::TimerId; +using firebase::firestore::util::internal::Executor; +using firebase::firestore::util::internal::ExecutorLibdispatch; -@property(nonatomic, strong, readonly) FSTDispatchQueue *queue; -@property(nonatomic, assign, readonly) FSTTimerID timerID; -@property(nonatomic, assign, readonly) NSTimeInterval targetTime; -@property(nonatomic, copy) void (^callback)(); -/** YES if the callback has been run or canceled. */ -@property(nonatomic, getter=isDone) BOOL done; +NS_ASSUME_NONNULL_BEGIN -/** - * Creates and returns an FSTDelayedCallback that has been scheduled on the provided queue with the - * provided delay. - * - * @param queue The FSTDispatchQueue to run the callback on. - * @param timerID A FSTTimerID identifying the type of the delayed callback. - * @param delay The delay before the callback should be scheduled. - * @param callback The callback block to run. - * @return The created FSTDelayedCallback instance. - */ -+ (instancetype)createAndScheduleWithQueue:(FSTDispatchQueue *)queue - timerID:(FSTTimerID)timerID - delay:(NSTimeInterval)delay - callback:(void (^)(void))callback; +#pragma mark - FSTDelayedCallback -/** - * Queues the callback to run immediately (if it hasn't already been run or canceled). - */ -- (void)skipDelay; +@interface FSTDelayedCallback () { + DelayedOperation _impl; +} @end @implementation FSTDelayedCallback -- (instancetype)initWithQueue:(FSTDispatchQueue *)queue - timerID:(FSTTimerID)timerID - targetTime:(NSTimeInterval)targetTime - callback:(void (^)(void))callback { +- (instancetype)initWithImpl:(DelayedOperation &&)impl { if (self = [super init]) { - _queue = queue; - _timerID = timerID; - _targetTime = targetTime; - _callback = callback; - _done = NO; + _impl = std::move(impl); } return self; } -+ (instancetype)createAndScheduleWithQueue:(FSTDispatchQueue *)queue - timerID:(FSTTimerID)timerID - delay:(NSTimeInterval)delay - callback:(void (^)(void))callback { - NSTimeInterval targetTime = [[NSDate date] timeIntervalSince1970] + delay; - FSTDelayedCallback *delayedCallback = [[FSTDelayedCallback alloc] initWithQueue:queue - timerID:timerID - targetTime:targetTime - callback:callback]; - [delayedCallback startWithDelay:delay]; - return delayedCallback; -} - -/** - * Starts the timer. This is called immediately after construction by createAndScheduleWithQueue. - */ -- (void)startWithDelay:(NSTimeInterval)delay { - dispatch_time_t delayNs = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)); - dispatch_after(delayNs, self.queue.queue, ^{ - [self.queue enterCheckedOperation:^{ - [self delayDidElapse]; - }]; - }); -} - -- (void)skipDelay { - [self.queue dispatchAsyncAllowingSameQueue:^{ - [self delayDidElapse]; - }]; -} - - (void)cancel { - [self.queue verifyIsCurrentQueue]; - if (!self.isDone) { - // PORTING NOTE: There's no way to actually cancel the dispatched callback, but it'll be a no-op - // since we set done to YES. - [self markDone]; - } -} - -- (void)delayDidElapse { - [self.queue verifyIsCurrentQueue]; - if (!self.isDone) { - [self markDone]; - self.callback(); - } -} - -/** - * Marks this delayed callback as done, and notifies the FSTDispatchQueue that it should be removed. - */ -- (void)markDone { - self.done = YES; - [self.queue removeDelayedCallback:self]; + _impl.Cancel(); } @end #pragma mark - FSTDispatchQueue -@interface FSTDispatchQueue () -/** - * Callbacks scheduled to be queued in the future. Callbacks are automatically removed after they - * are run or canceled. - */ -@property(nonatomic, strong, readonly) NSMutableArray<FSTDelayedCallback *> *delayedCallbacks; - -- (instancetype)initWithQueue:(dispatch_queue_t)queue NS_DESIGNATED_INITIALIZER; - -@end - @implementation FSTDispatchQueue { - /** - * Flag set while an FSTDispatchQueue operation is currently executing. Used for assertion - * sanity-checks. - */ - std::atomic<bool> _operationInProgress; + std::unique_ptr<AsyncQueue> _impl; +} + ++ (TimerId)convertTimerId:(FSTTimerID)objcTimerID { + const TimerId converted = static_cast<TimerId>(objcTimerID); + switch (converted) { + case TimerId::All: + case TimerId::ListenStreamIdle: + case TimerId::ListenStreamConnectionBackoff: + case TimerId::WriteStreamIdle: + case TimerId::WriteStreamConnectionBackoff: + case TimerId::OnlineStateTimeout: + return converted; + default: + FSTAssert(false, @"Unknown value of enum FSTTimerID."); + } } + (instancetype)queueWith:(dispatch_queue_t)dispatchQueue { @@ -172,141 +84,50 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithQueue:(dispatch_queue_t)queue { if (self = [super init]) { - _operationInProgress = false; _queue = queue; - _delayedCallbacks = [NSMutableArray array]; + auto executor = absl::make_unique<ExecutorLibdispatch>(queue); + _impl = absl::make_unique<AsyncQueue>(std::move(executor)); } return self; } - (void)verifyIsCurrentQueue { - FSTAssert([self onTargetQueue], - @"We are running on the wrong dispatch queue. Expected '%@' Actual: '%@'", - [self targetQueueLabel], [self currentQueueLabel]); - FSTAssert(_operationInProgress, - @"verifyIsCurrentQueue called outside enterCheckedOperation on queue '%@'", - [self currentQueueLabel]); + _impl->VerifyIsCurrentQueue(); } - (void)enterCheckedOperation:(void (^)(void))block { - FSTAssert(!_operationInProgress, - @"enterCheckedOperation may not be called when an operation is in progress"); - @try { - _operationInProgress = true; - [self verifyIsCurrentQueue]; - block(); - } @finally { - _operationInProgress = false; - } + _impl->ExecuteBlocking([block] { block(); }); } - (void)dispatchAsync:(void (^)(void))block { - FSTAssert(![self onTargetQueue] || !_operationInProgress, - @"dispatchAsync called when we are already running on target dispatch queue '%@'", - [self targetQueueLabel]); - - dispatch_async(self.queue, ^{ - [self enterCheckedOperation:block]; - }); + _impl->Enqueue([block] { block(); }); } - (void)dispatchAsyncAllowingSameQueue:(void (^)(void))block { - dispatch_async(self.queue, ^{ - [self enterCheckedOperation:block]; - }); + _impl->EnqueueRelaxed([block] { block(); }); } - (void)dispatchSync:(void (^)(void))block { - FSTAssert(![self onTargetQueue] || !_operationInProgress, - @"dispatchSync called when we are already running on target dispatch queue '%@'", - [self targetQueueLabel]); - - dispatch_sync(self.queue, ^{ - [self enterCheckedOperation:block]; - }); + _impl->EnqueueBlocking([block] { block(); }); } - (FSTDelayedCallback *)dispatchAfterDelay:(NSTimeInterval)delay timerID:(FSTTimerID)timerID block:(void (^)(void))block { - // While not necessarily harmful, we currently don't expect to have multiple callbacks with the - // same timerID in the queue, so defensively reject them. - FSTAssert(![self containsDelayedCallbackWithTimerID:timerID], - @"Attempted to schedule multiple callbacks with id %ld", (unsigned long)timerID); - FSTDelayedCallback *delayedCallback = [FSTDelayedCallback createAndScheduleWithQueue:self - timerID:timerID - delay:delay - callback:block]; - [self.delayedCallbacks addObject:delayedCallback]; - return delayedCallback; + const AsyncQueue::Milliseconds delayMs = + std::chrono::milliseconds(static_cast<long long>(delay * 1000)); + const TimerId convertedTimerId = [FSTDispatchQueue convertTimerId:timerID]; + DelayedOperation delayed_operation = + _impl->EnqueueAfterDelay(delayMs, convertedTimerId, [block] { block(); }); + return [[FSTDelayedCallback alloc] initWithImpl:std::move(delayed_operation)]; } - (BOOL)containsDelayedCallbackWithTimerID:(FSTTimerID)timerID { - NSUInteger matchIndex = [self.delayedCallbacks - indexOfObjectPassingTest:^BOOL(FSTDelayedCallback *obj, NSUInteger idx, BOOL *stop) { - return obj.timerID == timerID; - }]; - return matchIndex != NSNotFound; + return _impl->IsScheduled([FSTDispatchQueue convertTimerId:timerID]); } - (void)runDelayedCallbacksUntil:(FSTTimerID)lastTimerID { - dispatch_semaphore_t doneSemaphore = dispatch_semaphore_create(0); - - [self dispatchAsync:^{ - FSTAssert(lastTimerID == FSTTimerIDAll || [self containsDelayedCallbackWithTimerID:lastTimerID], - @"Attempted to run callbacks until missing timer ID: %ld", - (unsigned long)lastTimerID); - - [self sortDelayedCallbacks]; - for (FSTDelayedCallback *callback in self.delayedCallbacks) { - [callback skipDelay]; - if (lastTimerID != FSTTimerIDAll && callback.timerID == lastTimerID) { - break; - } - } - - // Now that the callbacks are queued, we want to enqueue an additional item to release the - // 'done' semaphore. - [self dispatchAsyncAllowingSameQueue:^{ - dispatch_semaphore_signal(doneSemaphore); - }]; - }]; - - dispatch_semaphore_wait(doneSemaphore, DISPATCH_TIME_FOREVER); -} - -// NOTE: For performance we could store the callbacks sorted (e.g. using std::priority_queue), -// but this sort only happens in tests (if runDelayedCallbacksUntil: is called), and the size -// is guaranteed to be small since we don't allow duplicate TimerIds (of which there are only 4). -- (void)sortDelayedCallbacks { - // We want to run callbacks in the same order they'd run if they ran naturally. - [self.delayedCallbacks - sortUsingComparator:^NSComparisonResult(FSTDelayedCallback *a, FSTDelayedCallback *b) { - return a.targetTime < b.targetTime - ? NSOrderedAscending - : a.targetTime > b.targetTime ? NSOrderedDescending : NSOrderedSame; - }]; -} - -/** Called by FSTDelayedCallback when a callback is run or canceled. */ -- (void)removeDelayedCallback:(FSTDelayedCallback *)callback { - NSUInteger index = [self.delayedCallbacks indexOfObject:callback]; - FSTAssert(index != NSNotFound, @"Delayed callback not found."); - [self.delayedCallbacks removeObjectAtIndex:index]; -} - -#pragma mark - Private Methods - -- (NSString *)currentQueueLabel { - return [NSString stringWithUTF8String:dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL)]; -} - -- (NSString *)targetQueueLabel { - return [NSString stringWithUTF8String:dispatch_queue_get_label(self.queue)]; -} - -- (BOOL)onTargetQueue { - return [[self currentQueueLabel] isEqualToString:[self targetQueueLabel]]; + _impl->RunScheduledOperationsUntil([FSTDispatchQueue convertTimerId:lastTimerID]); } @end diff --git a/Firestore/Source/Util/FSTLogger.h b/Firestore/Source/Util/FSTLogger.h deleted file mode 100644 index c4e2b85..0000000 --- a/Firestore/Source/Util/FSTLogger.h +++ /dev/null @@ -1,26 +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 - -/** 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); - -NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Util/FSTLogger.mm b/Firestore/Source/Util/FSTLogger.mm deleted file mode 100644 index f0081e0..0000000 --- a/Firestore/Source/Util/FSTLogger.mm +++ /dev/null @@ -1,41 +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/FSTLogger.h" - -#import <FirebaseCore/FIRLogger.h> - -#import "Firestore/Source/API/FIRFirestore+Internal.h" - -NS_ASSUME_NONNULL_BEGIN - -void FSTLog(NSString *format, ...) { - if ([FIRFirestore isLoggingEnabled]) { - va_list args; - va_start(args, format); - FIRLogBasic(FIRLoggerLevelDebug, kFIRLoggerFirestore, @"I-FST000001", format, args); - va_end(args); - } -} - -void FSTWarn(NSString *format, ...) { - va_list args; - va_start(args, format); - FIRLogBasic(FIRLoggerLevelWarning, kFIRLoggerFirestore, @"I-FST000001", format, args); - va_end(args); -} - -NS_ASSUME_NONNULL_END diff --git a/Firestore/core/CMakeLists.txt b/Firestore/core/CMakeLists.txt index 1a0c936..c6ae0f5 100644 --- a/Firestore/core/CMakeLists.txt +++ b/Firestore/core/CMakeLists.txt @@ -20,6 +20,7 @@ add_subdirectory(src/firebase/firestore/core) add_subdirectory(src/firebase/firestore/immutable) add_subdirectory(src/firebase/firestore/local) add_subdirectory(src/firebase/firestore/model) +add_subdirectory(src/firebase/firestore/nanopb) add_subdirectory(src/firebase/firestore/remote) add_subdirectory(src/firebase/firestore/util) diff --git a/Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.mm b/Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.mm index ff2d5f7..9d5b89e 100644 --- a/Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.mm +++ b/Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.mm @@ -20,7 +20,7 @@ #import <FirebaseCore/FIRAppInternal.h> #import <FirebaseCore/FIROptionsInternal.h> -#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" #include "Firestore/core/src/firebase/firestore/util/string_apple.h" // NB: This is also defined in Firestore/Source/Public/FIRFirestoreErrors.h @@ -80,8 +80,8 @@ FirebaseCredentialsProvider::~FirebaseCredentialsProvider() { void FirebaseCredentialsProvider::GetToken(bool force_refresh, TokenListener completion) { - FIREBASE_ASSERT_MESSAGE(auth_listener_handle_, - "GetToken cannot be called after listener removed."); + HARD_ASSERT(auth_listener_handle_, + "GetToken cannot be called after listener removed."); // Take note of the current value of the userCounter so that this method can // fail if there is a user change while the request is outstanding. @@ -129,15 +129,13 @@ void FirebaseCredentialsProvider::SetUserChangeListener( UserChangeListener listener) { std::unique_lock<std::mutex> lock(contents_->mutex); if (listener) { - FIREBASE_ASSERT_MESSAGE(!user_change_listener_, - "set user_change_listener twice!"); + HARD_ASSERT(!user_change_listener_, "set user_change_listener twice!"); // Fire initial event. listener(contents_->current_user); } else { - FIREBASE_ASSERT_MESSAGE(auth_listener_handle_, - "removed user_change_listener twice!"); - FIREBASE_ASSERT_MESSAGE(user_change_listener_, - "user_change_listener removed without being set!"); + HARD_ASSERT(auth_listener_handle_, "removed user_change_listener twice!"); + HARD_ASSERT(user_change_listener_, + "user_change_listener removed without being set!"); [[NSNotificationCenter defaultCenter] removeObserver:auth_listener_handle_]; auth_listener_handle_ = nil; } diff --git a/Firestore/core/src/firebase/firestore/auth/token.h b/Firestore/core/src/firebase/firestore/auth/token.h index 56084f8..b0d7075 100644 --- a/Firestore/core/src/firebase/firestore/auth/token.h +++ b/Firestore/core/src/firebase/firestore/auth/token.h @@ -20,7 +20,7 @@ #include <string> #include "Firestore/core/src/firebase/firestore/auth/user.h" -#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" #include "absl/strings/string_view.h" namespace firebase { @@ -46,7 +46,7 @@ class Token { /** The actual raw token. */ const std::string& token() const { - FIREBASE_ASSERT(user_.is_authenticated()); + HARD_ASSERT(user_.is_authenticated()); return token_; } diff --git a/Firestore/core/src/firebase/firestore/auth/user.cc b/Firestore/core/src/firebase/firestore/auth/user.cc index 4793fed..4fc10f6 100644 --- a/Firestore/core/src/firebase/firestore/auth/user.cc +++ b/Firestore/core/src/firebase/firestore/auth/user.cc @@ -16,7 +16,7 @@ #include "Firestore/core/src/firebase/firestore/auth/user.h" -#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" namespace firebase { namespace firestore { @@ -26,7 +26,7 @@ User::User() : is_authenticated_(false) { } User::User(const absl::string_view uid) : uid_(uid), is_authenticated_(true) { - FIREBASE_ASSERT(!uid.empty()); + HARD_ASSERT(!uid.empty()); } const User& User::Unauthenticated() { diff --git a/Firestore/core/src/firebase/firestore/geo_point.cc b/Firestore/core/src/firebase/firestore/geo_point.cc index 1ed5126..393b8f2 100644 --- a/Firestore/core/src/firebase/firestore/geo_point.cc +++ b/Firestore/core/src/firebase/firestore/geo_point.cc @@ -18,9 +18,7 @@ #include <cmath> -#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" - -using std::isnan; +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" namespace firebase { namespace firestore { @@ -30,14 +28,10 @@ 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]"); + HARD_ASSERT(!std::isnan(latitude) && -90 <= latitude && latitude <= 90, + "Latitude must be in the range of [-90, 90]"); + HARD_ASSERT(!std::isnan(longitude) && -180 <= longitude && longitude <= 180, + "Latitude must be in the range of [-180, 180]"); } bool operator<(const GeoPoint& lhs, const GeoPoint& rhs) { diff --git a/Firestore/core/src/firebase/firestore/immutable/array_sorted_map.h b/Firestore/core/src/firebase/firestore/immutable/array_sorted_map.h index db53969..b1c9398 100644 --- a/Firestore/core/src/firebase/firestore/immutable/array_sorted_map.h +++ b/Firestore/core/src/firebase/firestore/immutable/array_sorted_map.h @@ -28,7 +28,7 @@ #include "Firestore/core/src/firebase/firestore/immutable/map_entry.h" #include "Firestore/core/src/firebase/firestore/immutable/sorted_map_base.h" #include "Firestore/core/src/firebase/firestore/util/comparison.h" -#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" #include "Firestore/core/src/firebase/firestore/util/range.h" namespace firebase { @@ -74,7 +74,7 @@ class FixedArray { void append(SourceIterator src_begin, SourceIterator src_end) { auto appending = static_cast<size_type>(src_end - src_begin); auto new_size = size_ + appending; - FIREBASE_ASSERT(new_size <= SortedMapBase::kFixedSize); + HARD_ASSERT(new_size <= SortedMapBase::kFixedSize); std::copy(src_begin, src_end, end()); size_ = new_size; @@ -85,7 +85,7 @@ class FixedArray { */ void append(T&& value) { size_type new_size = size_ + 1; - FIREBASE_ASSERT(new_size <= SortedMapBase::kFixedSize); + HARD_ASSERT(new_size <= SortedMapBase::kFixedSize); *end() = std::move(value); size_ = new_size; diff --git a/Firestore/core/src/firebase/firestore/immutable/llrb_node_iterator.h b/Firestore/core/src/firebase/firestore/immutable/llrb_node_iterator.h index 5011947..3092678 100644 --- a/Firestore/core/src/firebase/firestore/immutable/llrb_node_iterator.h +++ b/Firestore/core/src/firebase/firestore/immutable/llrb_node_iterator.h @@ -23,7 +23,7 @@ #include "Firestore/core/src/firebase/firestore/immutable/llrb_node.h" #include "Firestore/core/src/firebase/firestore/util/comparison.h" -#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" namespace firebase { namespace firestore { @@ -149,7 +149,7 @@ class LlrbNodeIterator { * This can only be called if `end()` is false. */ pointer get() const { - FIREBASE_ASSERT(!is_end()); + HARD_ASSERT(!is_end()); return &(stack_.top()->entry()); } @@ -162,7 +162,7 @@ class LlrbNodeIterator { } LlrbNodeIterator& operator++() { - FIREBASE_ASSERT(!is_end()); + HARD_ASSERT(!is_end()); // Pop the stack, moving the currently pointed to node to the parent. const node_type* node = stack_.top(); diff --git a/Firestore/core/src/firebase/firestore/immutable/sorted_map.h b/Firestore/core/src/firebase/firestore/immutable/sorted_map.h index 647bab0..3a50020 100644 --- a/Firestore/core/src/firebase/firestore/immutable/sorted_map.h +++ b/Firestore/core/src/firebase/firestore/immutable/sorted_map.h @@ -144,7 +144,7 @@ class SortedMap : public impl::SortedMapBase { case Tag::Tree: return tree_.empty(); } - FIREBASE_UNREACHABLE(); + UNREACHABLE(); } /** Returns the number of items in this map. */ @@ -155,7 +155,7 @@ class SortedMap : public impl::SortedMapBase { case Tag::Tree: return tree_.size(); } - FIREBASE_UNREACHABLE(); + UNREACHABLE(); } /** @@ -184,7 +184,7 @@ class SortedMap : public impl::SortedMapBase { case Tag::Tree: return SortedMap{tree_.insert(key, value)}; } - FIREBASE_UNREACHABLE(); + UNREACHABLE(); } /** @@ -205,7 +205,7 @@ class SortedMap : public impl::SortedMapBase { } return SortedMap{std::move(result)}; } - FIREBASE_UNREACHABLE(); + UNREACHABLE(); } bool contains(const K& key) const { @@ -215,7 +215,7 @@ class SortedMap : public impl::SortedMapBase { case Tag::Tree: return tree_.contains(key); } - FIREBASE_UNREACHABLE(); + UNREACHABLE(); } /** @@ -232,7 +232,7 @@ class SortedMap : public impl::SortedMapBase { case Tag::Tree: return const_iterator{tree_.find(key)}; } - FIREBASE_UNREACHABLE(); + UNREACHABLE(); } /** @@ -248,7 +248,7 @@ class SortedMap : public impl::SortedMapBase { case Tag::Tree: return tree_.find_index(key); } - FIREBASE_UNREACHABLE(); + UNREACHABLE(); } /** @@ -267,7 +267,7 @@ class SortedMap : public impl::SortedMapBase { case Tag::Tree: return const_iterator{tree_.lower_bound(key)}; } - FIREBASE_UNREACHABLE(); + UNREACHABLE(); } const_iterator min() const { @@ -277,7 +277,7 @@ class SortedMap : public impl::SortedMapBase { case Tag::Tree: return const_iterator{tree_.min()}; } - FIREBASE_UNREACHABLE(); + UNREACHABLE(); } const_iterator max() const { @@ -287,7 +287,7 @@ class SortedMap : public impl::SortedMapBase { case Tag::Tree: return const_iterator{tree_.max()}; } - FIREBASE_UNREACHABLE(); + UNREACHABLE(); } /** @@ -301,7 +301,7 @@ class SortedMap : public impl::SortedMapBase { case Tag::Tree: return const_iterator{tree_.begin()}; } - FIREBASE_UNREACHABLE(); + UNREACHABLE(); } /** @@ -314,7 +314,7 @@ class SortedMap : public impl::SortedMapBase { case Tag::Tree: return const_iterator{tree_.end()}; } - FIREBASE_UNREACHABLE(); + UNREACHABLE(); } /** @@ -359,7 +359,7 @@ class SortedMap : public impl::SortedMapBase { case Tag::Tree: return tree_.comparator(); } - FIREBASE_UNREACHABLE(); + UNREACHABLE(); } enum class Tag { diff --git a/Firestore/core/src/firebase/firestore/immutable/sorted_map_iterator.h b/Firestore/core/src/firebase/firestore/immutable/sorted_map_iterator.h index a79f9f5..960e3f6 100644 --- a/Firestore/core/src/firebase/firestore/immutable/sorted_map_iterator.h +++ b/Firestore/core/src/firebase/firestore/immutable/sorted_map_iterator.h @@ -123,7 +123,7 @@ class SortedMapIterator { case Tag::Tree: return tree_iter_.get(); } - FIREBASE_UNREACHABLE(); + UNREACHABLE(); } reference operator*() const { @@ -164,7 +164,7 @@ class SortedMapIterator { case Tag::Tree: return a.tree_iter_ == b.tree_iter_; } - FIREBASE_UNREACHABLE(); + UNREACHABLE(); } bool operator!=(const SortedMapIterator& b) const { diff --git a/Firestore/core/src/firebase/firestore/immutable/sorted_set.h b/Firestore/core/src/firebase/firestore/immutable/sorted_set.h index d78fd61..1fe91bd 100644 --- a/Firestore/core/src/firebase/firestore/immutable/sorted_set.h +++ b/Firestore/core/src/firebase/firestore/immutable/sorted_set.h @@ -23,7 +23,7 @@ #include "Firestore/core/src/firebase/firestore/immutable/sorted_map.h" #include "Firestore/core/src/firebase/firestore/immutable/sorted_map_base.h" #include "Firestore/core/src/firebase/firestore/util/comparison.h" -#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" #include "Firestore/core/src/firebase/firestore/util/hashing.h" namespace firebase { diff --git a/Firestore/core/src/firebase/firestore/local/leveldb_key.cc b/Firestore/core/src/firebase/firestore/local/leveldb_key.cc index 7febe71..3a4e512 100644 --- a/Firestore/core/src/firebase/firestore/local/leveldb_key.cc +++ b/Firestore/core/src/firebase/firestore/local/leveldb_key.cc @@ -534,7 +534,7 @@ class Writer { OrderedCode::WriteString(&dest_, value); } - std::string dest_{}; + std::string dest_; }; } // namespace diff --git a/Firestore/core/src/firebase/firestore/local/leveldb_transaction.cc b/Firestore/core/src/firebase/firestore/local/leveldb_transaction.cc index 561d1e2..da9f004 100644 --- a/Firestore/core/src/firebase/firestore/local/leveldb_transaction.cc +++ b/Firestore/core/src/firebase/firestore/local/leveldb_transaction.cc @@ -17,7 +17,7 @@ #include "Firestore/core/src/firebase/firestore/local/leveldb_transaction.h" #include "Firestore/core/src/firebase/firestore/local/leveldb_key.h" -#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" #include "Firestore/core/src/firebase/firestore/util/log.h" #include "absl/memory/memory.h" #include "leveldb/write_batch.h" @@ -71,26 +71,24 @@ void LevelDbTransaction::Iterator::UpdateCurrent() { void LevelDbTransaction::Iterator::Seek(const std::string& key) { db_iter_->Seek(key); - FIREBASE_ASSERT_MESSAGE(db_iter_->status().ok(), - "leveldb iterator reported an error: %s", - db_iter_->status().ToString().c_str()); + HARD_ASSERT(db_iter_->status().ok(), "leveldb iterator reported an error: %s", + db_iter_->status().ToString()); for (; db_iter_->Valid() && IsDeleted(db_iter_->key()); db_iter_->Next()) { } - FIREBASE_ASSERT_MESSAGE(db_iter_->status().ok(), - "leveldb iterator reported an error: %s", - db_iter_->status().ToString().c_str()); + HARD_ASSERT(db_iter_->status().ok(), "leveldb iterator reported an error: %s", + db_iter_->status().ToString()); mutations_iter_ = txn_->mutations_.lower_bound(key); UpdateCurrent(); last_version_ = txn_->version_; } absl::string_view LevelDbTransaction::Iterator::key() { - FIREBASE_ASSERT_MESSAGE(Valid(), "key() called on invalid iterator"); + HARD_ASSERT(Valid(), "key() called on invalid iterator"); return current_.first; } absl::string_view LevelDbTransaction::Iterator::value() { - FIREBASE_ASSERT_MESSAGE(Valid(), "value() called on invalid iterator"); + HARD_ASSERT(Valid(), "value() called on invalid iterator"); return current_.second; } @@ -115,13 +113,12 @@ void LevelDbTransaction::Iterator::AdvanceLDB() { do { db_iter_->Next(); } while (db_iter_->Valid() && IsDeleted(db_iter_->key())); - FIREBASE_ASSERT_MESSAGE(db_iter_->status().ok(), - "leveldb iterator reported an error: %s", - db_iter_->status().ToString().c_str()); + HARD_ASSERT(db_iter_->status().ok(), "leveldb iterator reported an error: %s", + db_iter_->status().ToString()); } void LevelDbTransaction::Iterator::Next() { - FIREBASE_ASSERT_MESSAGE(Valid(), "Next() called on invalid iterator"); + HARD_ASSERT(Valid(), "Next() called on invalid iterator"); bool advanced = SyncToTransaction(); if (!advanced && is_valid_) { if (is_mutation_) { @@ -215,14 +212,11 @@ void LevelDbTransaction::Commit() { batch.Put(it->first, it->second); } - if (util::LogGetLevel() <= util::kLogLevelDebug) { - util::LogDebug("Committing transaction: %s", ToString().c_str()); - } + LOG_DEBUG("Committing transaction: %s", ToString()); Status status = db_->Write(write_options_, &batch); - FIREBASE_ASSERT_MESSAGE(status.ok(), - "Failed to commit transaction:\n%s\n Failed: %s", - ToString().c_str(), status.ToString().c_str()); + HARD_ASSERT(status.ok(), "Failed to commit transaction:\n%s\n Failed: %s", + ToString(), status.ToString()); } std::string LevelDbTransaction::ToString() { diff --git a/Firestore/core/src/firebase/firestore/model/base_path.h b/Firestore/core/src/firebase/firestore/model/base_path.h index 58df6f0..7608829 100644 --- a/Firestore/core/src/firebase/firestore/model/base_path.h +++ b/Firestore/core/src/firebase/firestore/model/base_path.h @@ -24,7 +24,7 @@ #include <utility> #include <vector> -#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" #include "Firestore/core/src/firebase/firestore/util/hashing.h" namespace firebase { @@ -57,19 +57,18 @@ class BasePath { /** Returns i-th segment of the path. */ const std::string& operator[](const size_t i) const { - FIREBASE_ASSERT_MESSAGE(i < segments_.size(), "index %u out of range", i); + HARD_ASSERT(i < segments_.size(), "index %s out of range", i); return segments_[i]; } /** Returns the first segment of the path. */ const std::string& first_segment() const { - FIREBASE_ASSERT_MESSAGE(!empty(), - "Cannot call first_segment on empty path"); + HARD_ASSERT(!empty(), "Cannot call first_segment on empty path"); return segments_[0]; } /** Returns the last segment of the path. */ const std::string& last_segment() const { - FIREBASE_ASSERT_MESSAGE(!empty(), "Cannot call last_segment on empty path"); + HARD_ASSERT(!empty(), "Cannot call last_segment on empty path"); return segments_[size() - 1]; } @@ -117,9 +116,8 @@ class BasePath { * this path. */ T PopFirst(const size_t n = 1) const { - FIREBASE_ASSERT_MESSAGE(n <= size(), - "Cannot call PopFirst(%u) on path of length %u", n, - size()); + HARD_ASSERT(n <= size(), "Cannot call PopFirst(%s) on path of length %s", n, + size()); return T{begin() + n, end()}; } @@ -128,7 +126,7 @@ class BasePath { * this path. */ T PopLast() const { - FIREBASE_ASSERT_MESSAGE(!empty(), "Cannot call PopLast() on empty path"); + HARD_ASSERT(!empty(), "Cannot call PopLast() on empty path"); return T{begin(), end() - 1}; } diff --git a/Firestore/core/src/firebase/firestore/model/database_id.cc b/Firestore/core/src/firebase/firestore/model/database_id.cc index a756ea7..97e7d92 100644 --- a/Firestore/core/src/firebase/firestore/model/database_id.cc +++ b/Firestore/core/src/firebase/firestore/model/database_id.cc @@ -16,7 +16,7 @@ #include "Firestore/core/src/firebase/firestore/model/database_id.h" -#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" namespace firebase { namespace firestore { @@ -27,8 +27,8 @@ constexpr const char* DatabaseId::kDefault; DatabaseId::DatabaseId(const absl::string_view project_id, const absl::string_view database_id) : project_id_(project_id), database_id_(database_id) { - FIREBASE_ASSERT(!project_id.empty()); - FIREBASE_ASSERT(!database_id.empty()); + HARD_ASSERT(!project_id.empty()); + HARD_ASSERT(!database_id.empty()); } } // namespace model diff --git a/Firestore/core/src/firebase/firestore/model/document.cc b/Firestore/core/src/firebase/firestore/model/document.cc index ae59d15..7adf684 100644 --- a/Firestore/core/src/firebase/firestore/model/document.cc +++ b/Firestore/core/src/firebase/firestore/model/document.cc @@ -18,7 +18,7 @@ #include <utility> -#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" namespace firebase { namespace firestore { @@ -32,7 +32,7 @@ Document::Document(FieldValue&& data, data_(std::move(data)), has_local_mutations_(has_local_mutations) { set_type(Type::Document); - FIREBASE_ASSERT(FieldValue::Type::Object == data.type()); + HARD_ASSERT(FieldValue::Type::Object == data.type()); } bool Document::Equals(const MaybeDocument& other) const { diff --git a/Firestore/core/src/firebase/firestore/model/document_key.cc b/Firestore/core/src/firebase/firestore/model/document_key.cc index ddda4c9..7dae412 100644 --- a/Firestore/core/src/firebase/firestore/model/document_key.cc +++ b/Firestore/core/src/firebase/firestore/model/document_key.cc @@ -18,7 +18,7 @@ #include <utility> -#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" namespace firebase { namespace firestore { @@ -27,9 +27,8 @@ namespace model { namespace { void AssertValidPath(const ResourcePath& path) { - FIREBASE_ASSERT_MESSAGE(DocumentKey::IsDocumentKey(path), - "invalid document key path: %s", - path.CanonicalString().c_str()); + HARD_ASSERT(DocumentKey::IsDocumentKey(path), "invalid document key path: %s", + path.CanonicalString()); } } // namespace diff --git a/Firestore/core/src/firebase/firestore/model/field_path.cc b/Firestore/core/src/firebase/firestore/model/field_path.cc index bc0e97c..5636b53 100644 --- a/Firestore/core/src/firebase/firestore/model/field_path.cc +++ b/Firestore/core/src/firebase/firestore/model/field_path.cc @@ -19,7 +19,7 @@ #include <algorithm> #include <utility> -#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" #include "absl/strings/str_join.h" #include "absl/strings/str_replace.h" #include "absl/strings/str_split.h" @@ -71,18 +71,11 @@ FieldPath FieldPath::FromServerFormat(const absl::string_view path) { std::string segment; segment.reserve(path.size()); - // string_view doesn't have a c_str() method, because it might not be - // null-terminated. Assertions expect C strings, so construct std::string on - // the fly, so that c_str() might be called on it. - const auto to_string = [](const absl::string_view view) { - return std::string{view.data(), view.data() + view.size()}; - }; - const auto finish_segment = [&segments, &segment, &path, &to_string] { - FIREBASE_ASSERT_MESSAGE( - !segment.empty(), - "Invalid field path (%s). Paths must not be empty, begin with " - "'.', end with '.', or contain '..'", - to_string(path).c_str()); + const auto finish_segment = [&segments, &segment, &path] { + HARD_ASSERT(!segment.empty(), + "Invalid field path (%s). Paths must not be empty, begin with " + "'.', end with '.', or contain '..'", + path); // Move operation will clear segment, but capacity will remain the same // (not, strictly speaking, required by the standard, but true in practice). segments.push_back(std::move(segment)); @@ -116,9 +109,8 @@ FieldPath FieldPath::FromServerFormat(const absl::string_view path) { case '\\': // TODO(b/37244157): Make this a user-facing exception once we // finalize field escaping. - FIREBASE_ASSERT_MESSAGE(i + 1 != path.size(), - "Trailing escape characters not allowed in %s", - to_string(path).c_str()); + HARD_ASSERT(i + 1 != path.size(), + "Trailing escape characters not allowed in %s", path); ++i; segment += path[i]; break; @@ -131,8 +123,7 @@ FieldPath FieldPath::FromServerFormat(const absl::string_view path) { } finish_segment(); - FIREBASE_ASSERT_MESSAGE(!inside_backticks, "Unterminated ` in path %s", - to_string(path).c_str()); + HARD_ASSERT(!inside_backticks, "Unterminated ` in path %s", path); return FieldPath{std::move(segments)}; } diff --git a/Firestore/core/src/firebase/firestore/model/field_value.cc b/Firestore/core/src/firebase/firestore/model/field_value.cc index 35253c8..4f92c8b 100644 --- a/Firestore/core/src/firebase/firestore/model/field_value.cc +++ b/Firestore/core/src/firebase/firestore/model/field_value.cc @@ -23,7 +23,7 @@ #include <vector> #include "Firestore/core/src/firebase/firestore/util/comparison.h" -#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" using firebase::firestore::util::Comparator; @@ -130,8 +130,7 @@ FieldValue& FieldValue::operator=(const FieldValue& value) { break; } default: - FIREBASE_ASSERT_MESSAGE_WITH_EXPRESSION( - false, lhs.type(), "Unsupported type %d", value.type()); + HARD_FAIL("Unsupported type %s", value.type()); } return *this; } @@ -168,10 +167,10 @@ FieldValue& FieldValue::operator=(FieldValue&& value) { FieldValue FieldValue::Set(const FieldPath& field_path, FieldValue value) const { - FIREBASE_ASSERT_MESSAGE(type() == Type::Object, - "Cannot set field for non-object FieldValue"); - FIREBASE_ASSERT_MESSAGE(!field_path.empty(), - "Cannot set field for empty path on FieldValue"); + HARD_ASSERT(type() == Type::Object, + "Cannot set field for non-object FieldValue"); + HARD_ASSERT(!field_path.empty(), + "Cannot set field for empty path on FieldValue"); // Set the value by recursively calling on child object. const std::string& child_name = field_path.first_segment(); const ObjectValue::Map& object_map = object_value_.internal_value; @@ -195,10 +194,10 @@ FieldValue FieldValue::Set(const FieldPath& field_path, } FieldValue FieldValue::Delete(const FieldPath& field_path) const { - FIREBASE_ASSERT_MESSAGE(type() == Type::Object, - "Cannot delete field for non-object FieldValue"); - FIREBASE_ASSERT_MESSAGE(!field_path.empty(), - "Cannot delete field for empty path on FieldValue"); + HARD_ASSERT(type() == Type::Object, + "Cannot delete field for non-object FieldValue"); + HARD_ASSERT(!field_path.empty(), + "Cannot delete field for empty path on FieldValue"); // Delete the value by recursively calling on child object. const std::string& child_name = field_path.first_segment(); const ObjectValue::Map& object_map = object_value_.internal_value; @@ -223,8 +222,8 @@ FieldValue FieldValue::Delete(const FieldPath& field_path) const { } absl::optional<FieldValue> FieldValue::Get(const FieldPath& field_path) const { - FIREBASE_ASSERT_MESSAGE(type() == Type::Object, - "Cannot get field for non-object FieldValue"); + HARD_ASSERT(type() == Type::Object, + "Cannot get field for non-object FieldValue"); const FieldValue* current = this; for (const auto& path : field_path) { if (current->type() != Type::Object) { @@ -435,8 +434,7 @@ bool operator<(const FieldValue& lhs, const FieldValue& rhs) { case Type::Object: return lhs.object_value_ < rhs.object_value_; default: - FIREBASE_ASSERT_MESSAGE_WITH_EXPRESSION( - false, lhs.type(), "Unsupported type %d", lhs.type()); + HARD_FAIL("Unsupported type %s", 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; diff --git a/Firestore/core/src/firebase/firestore/model/field_value.h b/Firestore/core/src/firebase/firestore/model/field_value.h index 0f7dfc4..09c8531 100644 --- a/Firestore/core/src/firebase/firestore/model/field_value.h +++ b/Firestore/core/src/firebase/firestore/model/field_value.h @@ -28,7 +28,7 @@ #include "Firestore/core/src/firebase/firestore/model/database_id.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" #include "Firestore/core/src/firebase/firestore/model/field_path.h" -#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" #include "absl/types/optional.h" namespace firebase { @@ -109,27 +109,27 @@ class FieldValue { } bool boolean_value() const { - FIREBASE_ASSERT(tag_ == Type::Boolean); + HARD_ASSERT(tag_ == Type::Boolean); return boolean_value_; } int64_t integer_value() const { - FIREBASE_ASSERT(tag_ == Type::Integer); + HARD_ASSERT(tag_ == Type::Integer); return integer_value_; } Timestamp timestamp_value() const { - FIREBASE_ASSERT(tag_ == Type::Timestamp); + HARD_ASSERT(tag_ == Type::Timestamp); return timestamp_value_; } const std::string& string_value() const { - FIREBASE_ASSERT(tag_ == Type::String); + HARD_ASSERT(tag_ == Type::String); return string_value_; } ObjectValue object_value() const { - FIREBASE_ASSERT(tag_ == Type::Object); + HARD_ASSERT(tag_ == Type::Object); return ObjectValue{object_value_}; } diff --git a/Firestore/core/src/firebase/firestore/model/maybe_document.cc b/Firestore/core/src/firebase/firestore/model/maybe_document.cc index 4f3be1d..aa5e15a 100644 --- a/Firestore/core/src/firebase/firestore/model/maybe_document.cc +++ b/Firestore/core/src/firebase/firestore/model/maybe_document.cc @@ -18,8 +18,6 @@ #include <utility> -#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" - namespace firebase { namespace firestore { namespace model { diff --git a/Firestore/core/src/firebase/firestore/model/precondition.cc b/Firestore/core/src/firebase/firestore/model/precondition.cc index 423d5a2..179b313 100644 --- a/Firestore/core/src/firebase/firestore/model/precondition.cc +++ b/Firestore/core/src/firebase/firestore/model/precondition.cc @@ -18,7 +18,7 @@ #include "Firestore/core/src/firebase/firestore/model/maybe_document.h" #include "Firestore/core/src/firebase/firestore/model/snapshot_version.h" -#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" namespace firebase { namespace firestore { @@ -59,7 +59,7 @@ bool Precondition::IsValidFor(const MaybeDocument& maybe_doc) const { case Type::None: return true; } - FIREBASE_UNREACHABLE(); + UNREACHABLE(); } } // namespace model diff --git a/Firestore/core/src/firebase/firestore/model/precondition.h b/Firestore/core/src/firebase/firestore/model/precondition.h index b98bb45..b015100 100644 --- a/Firestore/core/src/firebase/firestore/model/precondition.h +++ b/Firestore/core/src/firebase/firestore/model/precondition.h @@ -27,7 +27,7 @@ #include "Firestore/core/src/firebase/firestore/model/maybe_document.h" #include "Firestore/core/src/firebase/firestore/model/snapshot_version.h" -#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" namespace firebase { namespace firestore { @@ -104,7 +104,7 @@ class Precondition { case Type::None: return true; } - FIREBASE_UNREACHABLE(); + UNREACHABLE(); } // For Objective-C++ hash; to be removed after migration. @@ -131,10 +131,7 @@ class Precondition { stringWithFormat:@"<Precondition update_time=%s>", update_time_.timestamp().ToString().c_str()]; } - // We only raise dev assertion here. This function is mainly used in - // logging. - FIREBASE_DEV_ASSERT_MESSAGE(false, "precondition invalid"); - return @"<Precondition invalid>"; + UNREACHABLE(); } #endif // defined(__OBJC__) diff --git a/Firestore/core/src/firebase/firestore/model/resource_path.cc b/Firestore/core/src/firebase/firestore/model/resource_path.cc index c95aa63..cdd795f 100644 --- a/Firestore/core/src/firebase/firestore/model/resource_path.cc +++ b/Firestore/core/src/firebase/firestore/model/resource_path.cc @@ -20,7 +20,7 @@ #include <utility> #include <vector> -#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" #include "absl/strings/str_join.h" #include "absl/strings/str_split.h" @@ -33,10 +33,8 @@ ResourcePath ResourcePath::FromString(const absl::string_view path) { // sequences (e.g. __id123__) and just passes them through raw (they exist // for legacy reasons and should not be used frequently). - FIREBASE_ASSERT_MESSAGE( - path.find("//") == std::string::npos, - "Invalid path (%s). Paths must not contain // in them.", - std::string{path.data(), path.data() + path.size()}.c_str()); + HARD_ASSERT(path.find("//") == std::string::npos, + "Invalid path (%s). Paths must not contain // in them.", path); // SkipEmpty because we may still have an empty segment at the beginning or // end if they had a leading or trailing slash (which we allow). diff --git a/Firestore/core/src/firebase/firestore/model/snapshot_version.h b/Firestore/core/src/firebase/firestore/model/snapshot_version.h index 7ce0985..dbecea1 100644 --- a/Firestore/core/src/firebase/firestore/model/snapshot_version.h +++ b/Firestore/core/src/firebase/firestore/model/snapshot_version.h @@ -19,11 +19,6 @@ #include "Firestore/core/include/firebase/firestore/timestamp.h" -#if defined(__OBJC__) -#import "FIRTimestamp.h" -#import "Firestore/Source/Core/FSTSnapshotVersion.h" -#endif // defined(__OBJC__) - namespace firebase { namespace firestore { namespace model { @@ -34,6 +29,11 @@ namespace model { */ class SnapshotVersion { public: +#if __OBJC__ + SnapshotVersion() { + } +#endif // __OBJC__ + explicit SnapshotVersion(const Timestamp& timestamp); const Timestamp& timestamp() const { @@ -43,26 +43,11 @@ class SnapshotVersion { /** Creates a new version that is smaller than all other versions. */ static const SnapshotVersion& None(); -#if defined(__OBJC__) - SnapshotVersion() { - } - - SnapshotVersion(FSTSnapshotVersion* version) // NOLINT(runtime/explicit) - : timestamp_{version.timestamp.seconds, version.timestamp.nanoseconds} { - } - - operator FSTSnapshotVersion*() const { - if (timestamp_ == Timestamp{}) { - return [FSTSnapshotVersion noVersion]; - } else { - return [FSTSnapshotVersion - versionWithTimestamp:[FIRTimestamp - timestampWithSeconds:timestamp_.seconds() - nanoseconds:timestamp_ - .nanoseconds()]]; - } +#if __OBJC__ + size_t Hash() const { + return std::hash<Timestamp>{}(timestamp_); } -#endif // defined(__OBJC__) +#endif // __OBJC__ private: Timestamp timestamp_; diff --git a/Firestore/core/src/firebase/firestore/model/transform_operations.h b/Firestore/core/src/firebase/firestore/model/transform_operations.h index abc8d1b..a5c5f82 100644 --- a/Firestore/core/src/firebase/firestore/model/transform_operations.h +++ b/Firestore/core/src/firebase/firestore/model/transform_operations.h @@ -17,14 +17,16 @@ #ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_TRANSFORM_OPERATIONS_H_ #define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_TRANSFORM_OPERATIONS_H_ +#if !defined(__OBJC__) +#error "This header only supports Objective-C++." +#endif // !defined(__OBJC__) + #include <utility> #include <vector> -#include "Firestore/core/include/firebase/firestore/timestamp.h" - -#if defined(__OBJC__) #import "Firestore/Source/Model/FSTFieldValue.h" -#endif + +#include "Firestore/core/include/firebase/firestore/timestamp.h" namespace firebase { namespace firestore { @@ -51,36 +53,31 @@ class TransformOperation { /** Returns the actual type. */ virtual Type type() const = 0; -// TODO(mikelehen): apply methods require FSTFieldValue which is Obj-C only. -#if defined(__OBJC__) /** * Computes the local transform result against the provided `previousValue`, * optionally using the provided localWriteTime. */ - virtual FSTFieldValue *applyToLocalView( - FSTFieldValue *previousValue, FIRTimestamp *localWriteTime) const = 0; + virtual FSTFieldValue* ApplyToLocalView( + FSTFieldValue* previousValue, FIRTimestamp* localWriteTime) const = 0; /** * Computes a final transform result after the transform has been acknowledged * by the server, potentially using the server-provided transformResult. */ - virtual FSTFieldValue *applyToRemoteDocument( - FSTFieldValue *previousValue, FSTFieldValue *transformResult) const = 0; -#endif + virtual FSTFieldValue* ApplyToRemoteDocument( + FSTFieldValue* previousValue, FSTFieldValue* transformResult) const = 0; /** Returns whether the two are equal. */ - virtual bool operator==(const TransformOperation &other) const = 0; + virtual bool operator==(const TransformOperation& other) const = 0; /** Returns whether the two are not equal. */ - bool operator!=(const TransformOperation &other) const { + bool operator!=(const TransformOperation& other) const { return !operator==(other); } -#if defined(__OBJC__) // For Objective-C++ hash; to be removed after migration. // Do NOT use in C++ code. virtual NSUInteger Hash() const = 0; -#endif // defined(__OBJC__) }; /** Transforms a value into a server-generated timestamp. */ @@ -93,33 +90,29 @@ class ServerTimestampTransform : public TransformOperation { return Type::ServerTimestamp; } -// TODO(mikelehen): apply methods require FSTFieldValue which is Obj-C only. -#if defined(__OBJC__) - FSTFieldValue *applyToLocalView(FSTFieldValue *previousValue, - FIRTimestamp *localWriteTime) const override { + FSTFieldValue* ApplyToLocalView(FSTFieldValue* previousValue, + FIRTimestamp* localWriteTime) const override { return [FSTServerTimestampValue serverTimestampValueWithLocalWriteTime:localWriteTime previousValue:previousValue]; } - FSTFieldValue *applyToRemoteDocument( - FSTFieldValue *previousValue, - FSTFieldValue *transformResult) const override { + FSTFieldValue* ApplyToRemoteDocument( + FSTFieldValue* /* previousValue */, + FSTFieldValue* transformResult) const override { return transformResult; } -#endif - bool operator==(const TransformOperation &other) const override { + bool operator==(const TransformOperation& other) const override { // All ServerTimestampTransform objects are equal. return other.type() == Type::ServerTimestamp; } - static const ServerTimestampTransform &Get() { + static const ServerTimestampTransform& Get() { static ServerTimestampTransform shared_instance; return shared_instance; } -#if defined(__OBJC__) // For Objective-C++ hash; to be removed after migration. // Do NOT use in C++ code. NSUInteger Hash() const override { @@ -127,24 +120,19 @@ class ServerTimestampTransform : public TransformOperation { // instances are equal. return 37; } -#endif // defined(__OBJC__) private: ServerTimestampTransform() { } }; -// TODO(mikelehen): ArrayTransform can only be used from Obj-C until we switch -// to using FieldValue instead of FSTFieldValue. -#if defined(__OBJC__) - /** * Transforms an array via a union or remove operation (for convenience, we use * this class for both Type::ArrayUnion and Type::ArrayRemove). */ class ArrayTransform : public TransformOperation { public: - ArrayTransform(Type type, std::vector<FSTFieldValue *> elements) + ArrayTransform(Type type, std::vector<FSTFieldValue*> elements) : type_(type), elements_(std::move(elements)) { } @@ -155,33 +143,34 @@ class ArrayTransform : public TransformOperation { return type_; } - FSTFieldValue *applyToLocalView(FSTFieldValue *previousValue, - FIRTimestamp *localWriteTime) const override { - return apply(previousValue); + FSTFieldValue* ApplyToLocalView( + FSTFieldValue* previousValue, + FIRTimestamp* /* localWriteTime */) const override { + return Apply(previousValue); } - FSTFieldValue *applyToRemoteDocument( - FSTFieldValue *previousValue, - FSTFieldValue *transformResult) const override { + FSTFieldValue* ApplyToRemoteDocument( + FSTFieldValue* previousValue, + FSTFieldValue* /* transformResult */) const override { // The server just sends null as the transform result for array operations, // so we have to calculate a result the same as we do for local // applications. - return apply(previousValue); + return Apply(previousValue); } - const std::vector<FSTFieldValue *> &elements() const { + const std::vector<FSTFieldValue*>& elements() const { return elements_; } - bool operator==(const TransformOperation &other) const override { + bool operator==(const TransformOperation& other) const override { if (other.type() != type()) { return false; } - auto array_transform = static_cast<const ArrayTransform &>(other); + auto array_transform = static_cast<const ArrayTransform&>(other); if (array_transform.elements_.size() != elements_.size()) { return false; } - for (int i = 0; i < elements_.size(); i++) { + for (size_t i = 0; i < elements_.size(); i++) { if (![array_transform.elements_[i] isEqual:elements_[i]]) { return false; } @@ -189,64 +178,60 @@ class ArrayTransform : public TransformOperation { return true; } -#if defined(__OBJC__) // For Objective-C++ hash; to be removed after migration. // Do NOT use in C++ code. NSUInteger Hash() const override { NSUInteger result = 37; result = 31 * result + (type() == Type::ArrayUnion ? 1231 : 1237); - for (FSTFieldValue *element : elements_) { + for (FSTFieldValue* element : elements_) { result = 31 * result + [element hash]; } return result; } -#endif // defined(__OBJC__) - static const std::vector<FSTFieldValue *> &Elements( - const TransformOperation &op) { - FIREBASE_ASSERT(op.type() == Type::ArrayUnion || - op.type() == Type::ArrayRemove); - return static_cast<const ArrayTransform &>(op).elements(); + static const std::vector<FSTFieldValue*>& Elements( + const TransformOperation& op) { + HARD_ASSERT(op.type() == Type::ArrayUnion || + op.type() == Type::ArrayRemove); + return static_cast<const ArrayTransform&>(op).elements(); } private: Type type_; - std::vector<FSTFieldValue *> elements_; + std::vector<FSTFieldValue*> elements_; /** * Inspects the provided value, returning a mutable copy of the internal array * if it's an FSTArrayValue and an empty mutable array if it's nil or any * other type of FSTFieldValue. */ - static NSMutableArray<FSTFieldValue *> *CoercedFieldValuesArray( - FSTFieldValue *value) { + static NSMutableArray<FSTFieldValue*>* CoercedFieldValuesArray( + FSTFieldValue* value) { if ([value isMemberOfClass:[FSTArrayValue class]]) { return [NSMutableArray - arrayWithArray:reinterpret_cast<FSTArrayValue *>(value) - .internalValue]; + arrayWithArray:reinterpret_cast<FSTArrayValue*>(value).internalValue]; } else { // coerce to empty array. return [NSMutableArray array]; } } - FSTFieldValue *apply(FSTFieldValue *previousValue) const { - NSMutableArray<FSTFieldValue *> *result = + FSTFieldValue* Apply(FSTFieldValue* previousValue) const { + NSMutableArray<FSTFieldValue*>* result = ArrayTransform::CoercedFieldValuesArray(previousValue); - for (FSTFieldValue *element : elements_) { + for (FSTFieldValue* element : elements_) { if (type_ == Type::ArrayUnion) { if (![result containsObject:element]) { [result addObject:element]; } } else { - FIREBASE_ASSERT(type_ == Type::ArrayRemove); + HARD_ASSERT(type_ == Type::ArrayRemove); [result removeObject:element]; } } return [[FSTArrayValue alloc] initWithValueNoCopy:result]; } }; -#endif } // namespace model } // namespace firestore diff --git a/Firestore/core/src/firebase/firestore/nanopb/CMakeLists.txt b/Firestore/core/src/firebase/firestore/nanopb/CMakeLists.txt new file mode 100644 index 0000000..82ffb65 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/nanopb/CMakeLists.txt @@ -0,0 +1,27 @@ +# Copyright 2018 Google +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cc_library( + firebase_firestore_nanopb + SOURCES + tag.h + reader.h + reader.cc + writer.h + writer.cc + DEPENDS + firebase_firestore_util + firebase_firestore_protos_nanopb + nanopb +) diff --git a/Firestore/core/src/firebase/firestore/nanopb/reader.cc b/Firestore/core/src/firebase/firestore/nanopb/reader.cc new file mode 100644 index 0000000..7a12900 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/nanopb/reader.cc @@ -0,0 +1,141 @@ +/* + * 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/nanopb/reader.h" + +#include "Firestore/Protos/nanopb/google/firestore/v1beta1/document.pb.h" + +namespace firebase { +namespace firestore { +namespace nanopb { + +using firebase::firestore::util::Status; +using std::int64_t; +using std::uint64_t; + +Reader Reader::Wrap(const uint8_t* bytes, size_t length) { + return Reader{pb_istream_from_buffer(bytes, length)}; +} + +Tag Reader::ReadTag() { + Tag tag; + if (!status_.ok()) return tag; + + bool eof; + if (!pb_decode_tag(&stream_, &tag.wire_type, &tag.field_number, &eof)) { + status_ = Status(FirestoreErrorCode::DataLoss, PB_GET_ERROR(&stream_)); + return tag; + } + + // nanopb code always returns a false status when setting eof. + HARD_ASSERT(!eof, "nanopb set both ok status and eof to true"); + + return tag; +} + +void Reader::ReadNanopbMessage(const pb_field_t fields[], void* dest_struct) { + if (!status_.ok()) return; + + if (!pb_decode(&stream_, fields, dest_struct)) { + status_ = Status(FirestoreErrorCode::DataLoss, PB_GET_ERROR(&stream_)); + } +} + +/** + * Note that (despite the return type) this works for bool, enum, int32, int64, + * uint32 and uint64 proto field types. + * + * Note: This is not expected to be called directly, but rather only via the + * other Decode* methods (i.e. DecodeBool, DecodeLong, etc) + * + * @return The decoded varint as a uint64_t. + */ +uint64_t Reader::ReadVarint() { + if (!status_.ok()) return 0; + + uint64_t varint_value = 0; + if (!pb_decode_varint(&stream_, &varint_value)) { + status_ = Status(FirestoreErrorCode::DataLoss, PB_GET_ERROR(&stream_)); + } + return varint_value; +} + +void Reader::ReadNull() { + uint64_t varint = ReadVarint(); + if (!status_.ok()) return; + + if (varint != google_protobuf_NullValue_NULL_VALUE) { + status_ = Status(FirestoreErrorCode::DataLoss, + "Input proto bytes cannot be parsed (invalid null value)"); + } +} + +bool Reader::ReadBool() { + uint64_t varint = ReadVarint(); + if (!status_.ok()) return false; + + switch (varint) { + case 0: + return false; + case 1: + return true; + default: + status_ = + Status(FirestoreErrorCode::DataLoss, + "Input proto bytes cannot be parsed (invalid bool value)"); + return false; + } +} + +int64_t Reader::ReadInteger() { + return ReadVarint(); +} + +std::string Reader::ReadString() { + if (!status_.ok()) return ""; + + pb_istream_t substream; + if (!pb_make_string_substream(&stream_, &substream)) { + status_ = Status(FirestoreErrorCode::DataLoss, PB_GET_ERROR(&stream_)); + pb_close_string_substream(&stream_, &substream); + return ""; + } + + std::string result(substream.bytes_left, '\0'); + if (!pb_read(&substream, reinterpret_cast<pb_byte_t*>(&result[0]), + substream.bytes_left)) { + status_ = Status(FirestoreErrorCode::DataLoss, PB_GET_ERROR(&stream_)); + pb_close_string_substream(&stream_, &substream); + return ""; + } + + // NB: future versions of nanopb read the remaining characters out of the + // substream (and return false if that fails) as an additional safety + // check within pb_close_string_substream. Unfortunately, that's not present + // in the current version (0.38). We'll make a stronger assertion and check + // to make sure there *are* no remaining characters in the substream. + HARD_ASSERT( + substream.bytes_left == 0, + "Bytes remaining in substream after supposedly reading all of them."); + + pb_close_string_substream(&stream_, &substream); + + return result; +} + +} // namespace nanopb +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/nanopb/reader.h b/Firestore/core/src/firebase/firestore/nanopb/reader.h new file mode 100644 index 0000000..930211a --- /dev/null +++ b/Firestore/core/src/firebase/firestore/nanopb/reader.h @@ -0,0 +1,175 @@ +/* + * 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_NANOPB_READER_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_NANOPB_READER_H_ + +#include <pb.h> +#include <pb_decode.h> + +#include <cstdint> +#include <functional> +#include <string> + +#include "Firestore/core/include/firebase/firestore/firestore_errors.h" +#include "Firestore/core/src/firebase/firestore/nanopb/tag.h" +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" +#include "Firestore/core/src/firebase/firestore/util/status.h" + +namespace firebase { +namespace firestore { +namespace nanopb { + +/** + * Docs TODO(rsgowman). But currently, this just wraps the underlying nanopb + * pb_istream_t. + */ +class Reader { + public: + /** + * Creates an input stream that reads from the specified bytes. Note that + * this reference must remain valid for the lifetime of this Reader. + * + * (This is roughly equivalent to the nanopb function + * pb_istream_from_buffer()) + * + * @param bytes where the input should be deserialized from. + */ + static Reader Wrap(const uint8_t* bytes, size_t length); + + /** + * Reads a message type from the input stream. + * + * This essentially wraps calls to nanopb's pb_decode_tag() method. + */ + Tag ReadTag(); + + /** + * Reads a nanopb message from the input stream. + * + * This essentially wraps calls to nanopb's pb_decode() method. If we didn't + * use `oneof`s in our protos, this would be the primary way of decoding + * messages. + */ + void ReadNanopbMessage(const pb_field_t fields[], void* dest_struct); + + void ReadNull(); + bool ReadBool(); + std::int64_t ReadInteger(); + + std::string ReadString(); + + /** + * Reads a message and its length. + * + * Analog to Writer::WriteNestedMessage(). See that methods docs for further + * details. + * + * Call this method when reading a nested message. Provide a function to read + * the message itself. + * + * @param read_message_fn Function to read the submessage. Note that this + * function is expected to check the Reader's status (via + * Reader::status().ok()) and if not ok, to return a placeholder/invalid + * value. + */ + template <typename T> + T ReadNestedMessage(const std::function<T(Reader*)>& read_message_fn); + + size_t bytes_left() const { + return stream_.bytes_left; + } + + util::Status status() const { + return status_; + } + + void set_status(util::Status status) { + status_ = status; + } + + private: + /** + * Creates a new Reader, based on the given nanopb pb_istream_t. Note that + * a shallow copy will be taken. (Non-null pointers within this struct must + * remain valid for the lifetime of this Reader.) + */ + explicit Reader(pb_istream_t stream) : stream_(stream) { + } + + /** + * Reads a "varint" from the input stream. + * + * This essentially wraps calls to nanopb's pb_decode_varint() method. + * + * Note that (despite the return type) this works for bool, enum, int32, + * int64, uint32 and uint64 proto field types. + * + * Note: This is not expected to be called direclty, but rather only via the + * other Decode* methods (i.e. DecodeBool, DecodeLong, etc) + * + * @return The decoded varint as a uint64_t. + */ + std::uint64_t ReadVarint(); + + util::Status status_ = util::Status::OK(); + + pb_istream_t stream_; +}; + +template <typename T> +T Reader::ReadNestedMessage(const std::function<T(Reader*)>& read_message_fn) { + // Implementation note: This is roughly modeled on pb_decode_delimited, + // adjusted to account for the oneof in FieldValue. + + if (!status_.ok()) return read_message_fn(this); + + pb_istream_t raw_substream; + if (!pb_make_string_substream(&stream_, &raw_substream)) { + status_ = + util::Status(FirestoreErrorCode::DataLoss, PB_GET_ERROR(&stream_)); + pb_close_string_substream(&stream_, &raw_substream); + return read_message_fn(this); + } + Reader substream(raw_substream); + + // If this fails, we *won't* return right away so that we can cleanup the + // substream (although technically, that turns out not to matter; no resource + // leaks occur if we don't do this.) + // TODO(rsgowman): Consider RAII here. (Watch out for Reader class which also + // wraps streams.) + T message = read_message_fn(&substream); + status_ = substream.status(); + + // NB: future versions of nanopb read the remaining characters out of the + // substream (and return false if that fails) as an additional safety + // check within pb_close_string_substream. Unfortunately, that's not present + // in the current version (0.38). We'll make a stronger assertion and check + // to make sure there *are* no remaining characters in the substream. + HARD_ASSERT( + substream.bytes_left() == 0, + "Bytes remaining in substream after supposedly reading all of them."); + + pb_close_string_substream(&stream_, &substream.stream_); + + return message; +} + +} // namespace nanopb +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_NANOPB_READER_H_ diff --git a/Firestore/core/src/firebase/firestore/nanopb/tag.h b/Firestore/core/src/firebase/firestore/nanopb/tag.h new file mode 100644 index 0000000..455ef0c --- /dev/null +++ b/Firestore/core/src/firebase/firestore/nanopb/tag.h @@ -0,0 +1,43 @@ +/* + * Copyright 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_NANOPB_TAG_H__ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_NANOPB_TAG_H__ + +#include <pb.h> + +namespace firebase { +namespace firestore { +namespace nanopb { + +/** + * Represents a nanopb tag. + * + * field_number is one of the field tags that nanopb generates based off of + * the proto messages. They're typically named in the format: + * <parentNameSpace>_<childNameSpace>_<message>_<field>_tag, e.g. + * google_firestore_v1beta1_Document_name_tag. + */ +struct Tag { + pb_wire_type_t wire_type; + uint32_t field_number; +}; + +} // namespace nanopb +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_NANOPB_TAG_H_ diff --git a/Firestore/core/src/firebase/firestore/nanopb/writer.cc b/Firestore/core/src/firebase/firestore/nanopb/writer.cc new file mode 100644 index 0000000..c3ffaac --- /dev/null +++ b/Firestore/core/src/firebase/firestore/nanopb/writer.cc @@ -0,0 +1,163 @@ +/* + * 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/nanopb/writer.h" + +#include "Firestore/Protos/nanopb/google/firestore/v1beta1/document.pb.h" + +namespace firebase { +namespace firestore { +namespace nanopb { + +using firebase::firestore::util::Status; +using std::int64_t; +using std::int8_t; +using std::uint64_t; + +Writer Writer::Wrap(std::vector<uint8_t>* out_bytes) { + // TODO(rsgowman): find a better home for this constant. + // A document is defined to have a max size of 1MiB - 4 bytes. + static const size_t kMaxDocumentSize = 1 * 1024 * 1024 - 4; + + // Construct a nanopb output stream. + // + // Set the max_size to be the max document size (as an upper bound; one would + // expect individual FieldValue's to be smaller than this). + // + // bytes_written is (always) initialized to 0. (NB: nanopb does not know or + // care about the underlying output vector, so where we are in the vector + // itself is irrelevant. i.e. don't use out_bytes->size()) + pb_ostream_t raw_stream = { + /*callback=*/[](pb_ostream_t* stream, const pb_byte_t* buf, + size_t count) -> bool { + auto* out_bytes = static_cast<std::vector<uint8_t>*>(stream->state); + out_bytes->insert(out_bytes->end(), buf, buf + count); + return true; + }, + /*state=*/out_bytes, + /*max_size=*/kMaxDocumentSize, + /*bytes_written=*/0, + /*errmsg=*/nullptr}; + return Writer(raw_stream); +} + +void Writer::WriteTag(Tag tag) { + if (!status_.ok()) return; + + if (!pb_encode_tag(&stream_, tag.wire_type, tag.field_number)) { + HARD_FAIL(PB_GET_ERROR(&stream_)); + } +} + +void Writer::WriteNanopbMessage(const pb_field_t fields[], + const void* src_struct) { + if (!status_.ok()) return; + + if (!pb_encode(&stream_, fields, src_struct)) { + HARD_FAIL(PB_GET_ERROR(&stream_)); + } +} + +void Writer::WriteSize(size_t size) { + return WriteVarint(size); +} + +void Writer::WriteVarint(uint64_t value) { + if (!status_.ok()) return; + + if (!pb_encode_varint(&stream_, value)) { + HARD_FAIL(PB_GET_ERROR(&stream_)); + } +} + +void Writer::WriteNull() { + return WriteVarint(google_protobuf_NullValue_NULL_VALUE); +} + +void Writer::WriteBool(bool bool_value) { + return WriteVarint(bool_value); +} + +void Writer::WriteInteger(int64_t integer_value) { + return WriteVarint(integer_value); +} + +void Writer::WriteString(const std::string& string_value) { + if (!status_.ok()) return; + + if (!pb_encode_string( + &stream_, reinterpret_cast<const pb_byte_t*>(string_value.c_str()), + string_value.length())) { + HARD_FAIL(PB_GET_ERROR(&stream_)); + } +} + +void Writer::WriteNestedMessage( + const std::function<void(Writer*)>& write_message_fn) { + if (!status_.ok()) return; + + // First calculate the message size using a non-writing substream. + Writer sizer = Writer::Sizing(); + write_message_fn(&sizer); + status_ = sizer.status(); + if (!status_.ok()) return; + size_t size = sizer.bytes_written(); + + // Write out the size to the output writer. + WriteSize(size); + if (!status_.ok()) return; + + // If this stream is itself a sizing stream, then we don't need to actually + // parse field_value a second time; just update the bytes_written via a call + // to pb_write. (If we try to write the contents into a sizing stream, it'll + // fail since sizing streams don't actually have any buffer space.) + if (stream_.callback == nullptr) { + if (!pb_write(&stream_, nullptr, size)) { + HARD_FAIL(PB_GET_ERROR(&stream_)); + } + return; + } + + // Ensure the output stream has enough space + if (stream_.bytes_written + size > stream_.max_size) { + HARD_FAIL( + "Insufficient space in the output stream to write the given message"); + } + + // Use a substream to verify that a callback doesn't write more than what it + // did the first time. (Use an initializer rather than setting fields + // individually like nanopb does. This gives us a *chance* of noticing if + // nanopb adds new fields.) + Writer writer({stream_.callback, stream_.state, + /*max_size=*/size, /*bytes_written=*/0, + /*errmsg=*/nullptr}); + write_message_fn(&writer); + status_ = writer.status(); + if (!status_.ok()) return; + + stream_.bytes_written += writer.stream_.bytes_written; + stream_.state = writer.stream_.state; + stream_.errmsg = writer.stream_.errmsg; + + if (writer.bytes_written() != size) { + // submsg size changed + HARD_FAIL("Parsing the nested message twice yielded different sizes"); + } +} + +} // namespace nanopb +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/nanopb/writer.h b/Firestore/core/src/firebase/firestore/nanopb/writer.h new file mode 100644 index 0000000..e428826 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/nanopb/writer.h @@ -0,0 +1,140 @@ +/* + * 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_NANOPB_WRITER_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_NANOPB_WRITER_H_ + +#include <pb.h> +#include <pb_encode.h> + +#include <cstdint> +#include <functional> +#include <string> +#include <vector> + +#include "Firestore/core/src/firebase/firestore/nanopb/tag.h" +#include "Firestore/core/src/firebase/firestore/util/status.h" + +namespace firebase { +namespace firestore { +namespace nanopb { + +/** + * Docs TODO(rsgowman). But currently, this just wraps the underlying nanopb + * pb_ostream_t. Also doc how to check status. + */ +class Writer { + public: + /** + * Creates an output stream that writes to the specified vector. Note that + * this vector pointer must remain valid for the lifetime of this Writer. + * + * (This is roughly equivalent to the nanopb function + * pb_ostream_from_buffer()) + * + * @param out_bytes where the output should be serialized to. + */ + static Writer Wrap(std::vector<std::uint8_t>* out_bytes); + + /** + * Creates a non-writing output stream used to calculate the size of + * the serialized output. + */ + static Writer Sizing() { + return Writer(PB_OSTREAM_SIZING); + } + + /** + * Writes a message type to the output stream. + * + * This essentially wraps calls to nanopb's pb_encode_tag() method. + */ + void WriteTag(Tag tag); + + /** + * Writes a nanopb message to the output stream. + * + * This essentially wraps calls to nanopb's `pb_encode()` method. If we didn't + * use `oneof`s in our protos, this would be the primary way of encoding + * messages. + */ + void WriteNanopbMessage(const pb_field_t fields[], const void* src_struct); + + void WriteSize(size_t size); + void WriteNull(); + void WriteBool(bool bool_value); + void WriteInteger(std::int64_t integer_value); + + void WriteString(const std::string& string_value); + + /** + * Writes a message and its length. + * + * When writing a top level message, protobuf doesn't include the length + * (since you can get that already from the length of the binary output.) But + * when writing a sub/nested message, you must include the length in the + * serialization. + * + * Call this method when writing a nested message. Provide a function to + * write the message itself. This method will calculate the size of the + * written message (using the provided function with a non-writing sizing + * stream), write out the size (and perform sanity checks), and then serialize + * the message by calling the provided function a second time. + */ + void WriteNestedMessage(const std::function<void(Writer*)>& write_message_fn); + + size_t bytes_written() const { + return stream_.bytes_written; + } + + util::Status status() const { + return status_; + } + + private: + util::Status status_ = util::Status::OK(); + + /** + * Creates a new Writer, based on the given nanopb pb_ostream_t. Note that + * a shallow copy will be taken. (Non-null pointers within this struct must + * remain valid for the lifetime of this Writer.) + */ + explicit Writer(const pb_ostream_t& stream) : stream_(stream) { + } + + /** + * Writes a "varint" to the output stream. + * + * This essentially wraps calls to nanopb's pb_encode_varint() method. + * + * Note that (despite the value parameter type) this works for bool, enum, + * int32, int64, uint32 and uint64 proto field types. + * + * Note: This is not expected to be called directly, but rather only + * via the other Write* methods (i.e. WriteBool, WriteLong, etc) + * + * @param value The value to write, represented as a uint64_t. + */ + void WriteVarint(std::uint64_t value); + + pb_ostream_t stream_; +}; + +} // namespace nanopb +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_NANOPB_WRITER_H_ diff --git a/Firestore/core/src/firebase/firestore/remote/CMakeLists.txt b/Firestore/core/src/firebase/firestore/remote/CMakeLists.txt index 7f528fb..fc51b37 100644 --- a/Firestore/core/src/firebase/firestore/remote/CMakeLists.txt +++ b/Firestore/core/src/firebase/firestore/remote/CMakeLists.txt @@ -21,7 +21,9 @@ cc_library( serializer.cc DEPENDS firebase_firestore_model + firebase_firestore_nanopb firebase_firestore_protos_nanopb + firebase_firestore_util grpc::grpc nanopb ) diff --git a/Firestore/core/src/firebase/firestore/remote/serializer.cc b/Firestore/core/src/firebase/firestore/remote/serializer.cc index 209f2b1..6ea5844 100644 --- a/Firestore/core/src/firebase/firestore/remote/serializer.cc +++ b/Firestore/core/src/firebase/firestore/remote/serializer.cc @@ -25,10 +25,19 @@ #include <utility> #include "Firestore/Protos/nanopb/google/firestore/v1beta1/document.pb.h" +#include "Firestore/Protos/nanopb/google/firestore/v1beta1/firestore.pb.h" +#include "Firestore/core/include/firebase/firestore/firestore_errors.h" #include "Firestore/core/include/firebase/firestore/timestamp.h" +#include "Firestore/core/src/firebase/firestore/model/document.h" +#include "Firestore/core/src/firebase/firestore/model/no_document.h" #include "Firestore/core/src/firebase/firestore/model/resource_path.h" +#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h" +#include "Firestore/core/src/firebase/firestore/nanopb/reader.h" +#include "Firestore/core/src/firebase/firestore/nanopb/tag.h" +#include "Firestore/core/src/firebase/firestore/nanopb/writer.h" #include "Firestore/core/src/firebase/firestore/timestamp_internal.h" -#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" +#include "absl/memory/memory.h" namespace firebase { namespace firestore { @@ -37,422 +46,30 @@ namespace remote { using firebase::Timestamp; using firebase::TimestampInternal; using firebase::firestore::model::DatabaseId; +using firebase::firestore::model::Document; using firebase::firestore::model::DocumentKey; using firebase::firestore::model::FieldValue; +using firebase::firestore::model::MaybeDocument; +using firebase::firestore::model::NoDocument; using firebase::firestore::model::ObjectValue; using firebase::firestore::model::ResourcePath; +using firebase::firestore::model::SnapshotVersion; +using firebase::firestore::nanopb::Reader; +using firebase::firestore::nanopb::Tag; +using firebase::firestore::nanopb::Writer; using firebase::firestore::util::Status; using firebase::firestore::util::StatusOr; namespace { -class Writer; +void EncodeMapValue(Writer* writer, const ObjectValue& object_value); +void EncodeObjectMap(Writer* writer, + const ObjectValue::Map& object_value_map, + uint32_t map_tag, + uint32_t key_tag, + uint32_t value_tag); -class Reader; - -void EncodeObject(Writer* writer, const ObjectValue& object_value); - -ObjectValue::Map DecodeObject(Reader* reader); - -/** - * Represents a nanopb tag. - * - * field_number is one of the field tags that nanopb generates based off of - * the proto messages. They're typically named in the format: - * <parentNameSpace>_<childNameSpace>_<message>_<field>_tag, e.g. - * google_firestore_v1beta1_Document_name_tag. - */ -struct Tag { - pb_wire_type_t wire_type; - uint32_t field_number; -}; - -/** - * Docs TODO(rsgowman). But currently, this just wraps the underlying nanopb - * pb_ostream_t. Also doc how to check status. - */ -class Writer { - public: - /** - * Creates an output stream that writes to the specified vector. Note that - * this vector pointer must remain valid for the lifetime of this Writer. - * - * (This is roughly equivalent to the nanopb function - * pb_ostream_from_buffer()) - * - * @param out_bytes where the output should be serialized to. - */ - static Writer Wrap(std::vector<uint8_t>* out_bytes); - - /** - * Creates a non-writing output stream used to calculate the size of - * the serialized output. - */ - static Writer Sizing() { - return Writer(PB_OSTREAM_SIZING); - } - - /** - * Writes a message type to the output stream. - * - * This essentially wraps calls to nanopb's pb_encode_tag() method. - */ - void WriteTag(Tag tag); - - /** - * Writes a nanopb message to the output stream. - * - * This essentially wraps calls to nanopb's `pb_encode()` method. If we didn't - * use `oneof`s in our protos, this would be the primary way of encoding - * messages. - */ - void WriteNanopbMessage(const pb_field_t fields[], const void* src_struct); - - void WriteSize(size_t size); - void WriteNull(); - void WriteBool(bool bool_value); - void WriteInteger(int64_t integer_value); - - void WriteString(const std::string& string_value); - - /** - * Writes a message and its length. - * - * When writing a top level message, protobuf doesn't include the length - * (since you can get that already from the length of the binary output.) But - * when writing a sub/nested message, you must include the length in the - * serialization. - * - * Call this method when writing a nested message. Provide a function to - * write the message itself. This method will calculate the size of the - * written message (using the provided function with a non-writing sizing - * stream), write out the size (and perform sanity checks), and then serialize - * the message by calling the provided function a second time. - */ - void WriteNestedMessage(const std::function<void(Writer*)>& write_message_fn); - - size_t bytes_written() const { - return stream_.bytes_written; - } - - Status status() const { - return status_; - } - - private: - Status status_ = Status::OK(); - - /** - * Creates a new Writer, based on the given nanopb pb_ostream_t. Note that - * a shallow copy will be taken. (Non-null pointers within this struct must - * remain valid for the lifetime of this Writer.) - */ - explicit Writer(const pb_ostream_t& stream) : stream_(stream) { - } - - /** - * Writes a "varint" to the output stream. - * - * This essentially wraps calls to nanopb's pb_encode_varint() method. - * - * Note that (despite the value parameter type) this works for bool, enum, - * int32, int64, uint32 and uint64 proto field types. - * - * Note: This is not expected to be called directly, but rather only - * via the other Write* methods (i.e. WriteBool, WriteLong, etc) - * - * @param value The value to write, represented as a uint64_t. - */ - void WriteVarint(uint64_t value); - - pb_ostream_t stream_; -}; - -/** - * Docs TODO(rsgowman). But currently, this just wraps the underlying nanopb - * pb_istream_t. - */ -class Reader { - public: - /** - * Creates an input stream that reads from the specified bytes. Note that - * this reference must remain valid for the lifetime of this Reader. - * - * (This is roughly equivalent to the nanopb function - * pb_istream_from_buffer()) - * - * @param bytes where the input should be deserialized from. - */ - static Reader Wrap(const uint8_t* bytes, size_t length); - - /** - * Reads a message type from the input stream. - * - * This essentially wraps calls to nanopb's pb_decode_tag() method. - */ - Tag ReadTag(); - - /** - * Reads a nanopb message from the input stream. - * - * This essentially wraps calls to nanopb's pb_decode() method. If we didn't - * use `oneof`s in our protos, this would be the primary way of decoding - * messages. - */ - void ReadNanopbMessage(const pb_field_t fields[], void* dest_struct); - - void ReadNull(); - bool ReadBool(); - int64_t ReadInteger(); - - std::string ReadString(); - - /** - * Reads a message and its length. - * - * Analog to Writer::WriteNestedMessage(). See that methods docs for further - * details. - * - * Call this method when reading a nested message. Provide a function to read - * the message itself. - */ - template <typename T> - T ReadNestedMessage(const std::function<T(Reader*)>& read_message_fn); - - size_t bytes_left() const { - return stream_.bytes_left; - } - - Status status() const { - return status_; - } - - void set_status(Status status) { - status_ = status; - } - - private: - /** - * Creates a new Reader, based on the given nanopb pb_istream_t. Note that - * a shallow copy will be taken. (Non-null pointers within this struct must - * remain valid for the lifetime of this Reader.) - */ - explicit Reader(pb_istream_t stream) : stream_(stream) { - } - - /** - * Reads a "varint" from the input stream. - * - * This essentially wraps calls to nanopb's pb_decode_varint() method. - * - * Note that (despite the return type) this works for bool, enum, int32, - * int64, uint32 and uint64 proto field types. - * - * Note: This is not expected to be called direclty, but rather only via the - * other Decode* methods (i.e. DecodeBool, DecodeLong, etc) - * - * @return The decoded varint as a uint64_t. - */ - uint64_t ReadVarint(); - - Status status_ = Status::OK(); - - pb_istream_t stream_; -}; - -Writer Writer::Wrap(std::vector<uint8_t>* out_bytes) { - // TODO(rsgowman): find a better home for this constant. - // A document is defined to have a max size of 1MiB - 4 bytes. - static const size_t kMaxDocumentSize = 1 * 1024 * 1024 - 4; - - // Construct a nanopb output stream. - // - // Set the max_size to be the max document size (as an upper bound; one would - // expect individual FieldValue's to be smaller than this). - // - // bytes_written is (always) initialized to 0. (NB: nanopb does not know or - // care about the underlying output vector, so where we are in the vector - // itself is irrelevant. i.e. don't use out_bytes->size()) - pb_ostream_t raw_stream = { - /*callback=*/[](pb_ostream_t* stream, const pb_byte_t* buf, - size_t count) -> bool { - auto* out_bytes = static_cast<std::vector<uint8_t>*>(stream->state); - out_bytes->insert(out_bytes->end(), buf, buf + count); - return true; - }, - /*state=*/out_bytes, - /*max_size=*/kMaxDocumentSize, - /*bytes_written=*/0, - /*errmsg=*/nullptr}; - return Writer(raw_stream); -} - -Reader Reader::Wrap(const uint8_t* bytes, size_t length) { - return Reader{pb_istream_from_buffer(bytes, length)}; -} - -// TODO(rsgowman): I've left the methods as near as possible to where they were -// before, which implies that the Writer methods are interspersed with the -// Reader methods. This should make it a bit easier to review. Refactor these to -// group the related methods together (probably within their own file rather -// than here). - -void Writer::WriteTag(Tag tag) { - if (!status_.ok()) return; - - if (!pb_encode_tag(&stream_, tag.wire_type, tag.field_number)) { - FIREBASE_ASSERT_MESSAGE(false, PB_GET_ERROR(&stream_)); - } -} - -Tag Reader::ReadTag() { - Tag tag; - if (!status_.ok()) return tag; - - bool eof; - if (!pb_decode_tag(&stream_, &tag.wire_type, &tag.field_number, &eof)) { - status_ = Status(FirestoreErrorCode::DataLoss, PB_GET_ERROR(&stream_)); - return tag; - } - - // nanopb code always returns a false status when setting eof. - FIREBASE_ASSERT_MESSAGE(!eof, "nanopb set both ok status and eof to true"); - - return tag; -} - -void Writer::WriteNanopbMessage(const pb_field_t fields[], - const void* src_struct) { - if (!status_.ok()) return; - - if (!pb_encode(&stream_, fields, src_struct)) { - FIREBASE_ASSERT_MESSAGE(false, PB_GET_ERROR(&stream_)); - } -} - -void Reader::ReadNanopbMessage(const pb_field_t fields[], void* dest_struct) { - if (!status_.ok()) return; - - if (!pb_decode(&stream_, fields, dest_struct)) { - status_ = Status(FirestoreErrorCode::DataLoss, PB_GET_ERROR(&stream_)); - } -} - -void Writer::WriteSize(size_t size) { - return WriteVarint(size); -} - -void Writer::WriteVarint(uint64_t value) { - if (!status_.ok()) return; - - if (!pb_encode_varint(&stream_, value)) { - FIREBASE_ASSERT_MESSAGE(false, PB_GET_ERROR(&stream_)); - } -} - -/** - * Note that (despite the return type) this works for bool, enum, int32, int64, - * uint32 and uint64 proto field types. - * - * Note: This is not expected to be called directly, but rather only via the - * other Decode* methods (i.e. DecodeBool, DecodeLong, etc) - * - * @return The decoded varint as a uint64_t. - */ -uint64_t Reader::ReadVarint() { - if (!status_.ok()) return 0; - - uint64_t varint_value = 0; - if (!pb_decode_varint(&stream_, &varint_value)) { - status_ = Status(FirestoreErrorCode::DataLoss, PB_GET_ERROR(&stream_)); - } - return varint_value; -} - -void Writer::WriteNull() { - return WriteVarint(google_protobuf_NullValue_NULL_VALUE); -} - -void Reader::ReadNull() { - uint64_t varint = ReadVarint(); - if (!status_.ok()) return; - - if (varint != google_protobuf_NullValue_NULL_VALUE) { - status_ = Status(FirestoreErrorCode::DataLoss, - "Input proto bytes cannot be parsed (invalid null value)"); - } -} - -void Writer::WriteBool(bool bool_value) { - return WriteVarint(bool_value); -} - -bool Reader::ReadBool() { - uint64_t varint = ReadVarint(); - if (!status_.ok()) return false; - - switch (varint) { - case 0: - return false; - case 1: - return true; - default: - status_ = - Status(FirestoreErrorCode::DataLoss, - "Input proto bytes cannot be parsed (invalid bool value)"); - return false; - } -} - -void Writer::WriteInteger(int64_t integer_value) { - return WriteVarint(integer_value); -} - -int64_t Reader::ReadInteger() { - return ReadVarint(); -} - -void Writer::WriteString(const std::string& string_value) { - if (!status_.ok()) return; - - if (!pb_encode_string( - &stream_, reinterpret_cast<const pb_byte_t*>(string_value.c_str()), - string_value.length())) { - FIREBASE_ASSERT_MESSAGE(false, PB_GET_ERROR(&stream_)); - } -} - -std::string Reader::ReadString() { - if (!status_.ok()) return ""; - - pb_istream_t substream; - if (!pb_make_string_substream(&stream_, &substream)) { - status_ = Status(FirestoreErrorCode::DataLoss, PB_GET_ERROR(&stream_)); - pb_close_string_substream(&stream_, &substream); - return ""; - } - - std::string result(substream.bytes_left, '\0'); - if (!pb_read(&substream, reinterpret_cast<pb_byte_t*>(&result[0]), - substream.bytes_left)) { - status_ = Status(FirestoreErrorCode::DataLoss, PB_GET_ERROR(&stream_)); - pb_close_string_substream(&stream_, &substream); - return ""; - } - - // NB: future versions of nanopb read the remaining characters out of the - // substream (and return false if that fails) as an additional safety - // check within pb_close_string_substream. Unfortunately, that's not present - // in the current version (0.38). We'll make a stronger assertion and check - // to make sure there *are* no remaining characters in the substream. - FIREBASE_ASSERT_MESSAGE( - substream.bytes_left == 0, - "Bytes remaining in substream after supposedly reading all of them."); - - pb_close_string_substream(&stream_, &substream); - - return result; -} +ObjectValue::Map DecodeMapValue(Reader* reader); void EncodeTimestamp(Writer* writer, const Timestamp& timestamp_value) { google_protobuf_Timestamp timestamp_proto = @@ -464,6 +81,8 @@ void EncodeTimestamp(Writer* writer, const Timestamp& timestamp_value) { } Timestamp DecodeTimestamp(Reader* reader) { + if (!reader->status().ok()) return {}; + google_protobuf_Timestamp timestamp_proto = google_protobuf_Timestamp_init_zero; reader->ReadNanopbMessage(google_protobuf_Timestamp_fields, ×tamp_proto); @@ -534,7 +153,9 @@ void EncodeFieldValueImpl(Writer* writer, const FieldValue& field_value) { case FieldValue::Type::Object: writer->WriteTag( {PB_WT_STRING, google_firestore_v1beta1_Value_map_value_tag}); - EncodeObject(writer, field_value.object_value()); + writer->WriteNestedMessage([&field_value](Writer* writer) { + EncodeMapValue(writer, field_value.object_value()); + }); break; default: @@ -544,6 +165,8 @@ void EncodeFieldValueImpl(Writer* writer, const FieldValue& field_value) { } FieldValue DecodeFieldValueImpl(Reader* reader) { + if (!reader->status().ok()) return FieldValue::NullValue(); + Tag tag = reader->ReadTag(); if (!reader->status().ok()) return FieldValue::NullValue(); @@ -580,9 +203,8 @@ FieldValue DecodeFieldValueImpl(Reader* reader) { // TODO(rsgowman): While still in development, we'll contradict the above // and assume the latter. Remove the following assertion when we're // confident that we're handling all the tags in the protos. - FIREBASE_ASSERT_MESSAGE( - false, - "Unhandled message field number (tag): %i. (Or possibly " + HARD_FAIL( + "Unhandled message field number (tag): %s. (Or possibly " "corrupt input bytes)", tag.field_number); reader->set_status(Status( @@ -606,110 +228,18 @@ FieldValue DecodeFieldValueImpl(Reader* reader) { return FieldValue::TimestampValue( reader->ReadNestedMessage<Timestamp>(DecodeTimestamp)); case google_firestore_v1beta1_Value_map_value_tag: - return FieldValue::ObjectValueFromMap(DecodeObject(reader)); + return FieldValue::ObjectValueFromMap( + reader->ReadNestedMessage<ObjectValue::Map>(DecodeMapValue)); default: // This indicates an internal error as we've already ensured that this is // a valid field_number. - FIREBASE_ASSERT_MESSAGE( - false, + HARD_FAIL( "Somehow got an unexpected field number (tag) after verifying that " "the field number was expected."); } } -void Writer::WriteNestedMessage( - const std::function<void(Writer*)>& write_message_fn) { - if (!status_.ok()) return; - - // First calculate the message size using a non-writing substream. - Writer sizer = Writer::Sizing(); - write_message_fn(&sizer); - status_ = sizer.status(); - if (!status_.ok()) return; - size_t size = sizer.bytes_written(); - - // Write out the size to the output writer. - WriteSize(size); - if (!status_.ok()) return; - - // If this stream is itself a sizing stream, then we don't need to actually - // parse field_value a second time; just update the bytes_written via a call - // to pb_write. (If we try to write the contents into a sizing stream, it'll - // fail since sizing streams don't actually have any buffer space.) - if (stream_.callback == nullptr) { - if (!pb_write(&stream_, nullptr, size)) { - FIREBASE_ASSERT_MESSAGE(false, PB_GET_ERROR(&stream_)); - } - return; - } - - // Ensure the output stream has enough space - if (stream_.bytes_written + size > stream_.max_size) { - FIREBASE_ASSERT_MESSAGE( - false, - "Insufficient space in the output stream to write the given message"); - } - - // Use a substream to verify that a callback doesn't write more than what it - // did the first time. (Use an initializer rather than setting fields - // individually like nanopb does. This gives us a *chance* of noticing if - // nanopb adds new fields.) - Writer writer({stream_.callback, stream_.state, - /*max_size=*/size, /*bytes_written=*/0, - /*errmsg=*/nullptr}); - write_message_fn(&writer); - status_ = writer.status(); - if (!status_.ok()) return; - - stream_.bytes_written += writer.stream_.bytes_written; - stream_.state = writer.stream_.state; - stream_.errmsg = writer.stream_.errmsg; - - if (writer.bytes_written() != size) { - // submsg size changed - FIREBASE_ASSERT_MESSAGE( - false, "Parsing the nested message twice yielded different sizes"); - } -} - -template <typename T> -T Reader::ReadNestedMessage(const std::function<T(Reader*)>& read_message_fn) { - // Implementation note: This is roughly modeled on pb_decode_delimited, - // adjusted to account for the oneof in FieldValue. - - if (!status_.ok()) return T(); - - pb_istream_t raw_substream; - if (!pb_make_string_substream(&stream_, &raw_substream)) { - status_ = Status(FirestoreErrorCode::DataLoss, PB_GET_ERROR(&stream_)); - pb_close_string_substream(&stream_, &raw_substream); - return T(); - } - Reader substream(raw_substream); - - // If this fails, we *won't* return right away so that we can cleanup the - // substream (although technically, that turns out not to matter; no resource - // leaks occur if we don't do this.) - // TODO(rsgowman): Consider RAII here. (Watch out for Reader class which also - // wraps streams.) - T message = read_message_fn(&substream); - status_ = substream.status(); - - // NB: future versions of nanopb read the remaining characters out of the - // substream (and return false if that fails) as an additional safety - // check within pb_close_string_substream. Unfortunately, that's not present - // in the current version (0.38). We'll make a stronger assertion and check - // to make sure there *are* no remaining characters in the substream. - FIREBASE_ASSERT_MESSAGE( - substream.bytes_left() == 0, - "Bytes remaining in substream after supposedly reading all of them."); - - pb_close_string_substream(&stream_, &substream.stream_); - - return message; -} - /** * Encodes a 'FieldsEntry' object, within a FieldValue's map_value type. * @@ -730,35 +260,38 @@ T Reader::ReadNestedMessage(const std::function<T(Reader*)>& read_message_fn) { * * @param kv The individual key/value pair to write. */ -void EncodeFieldsEntry(Writer* writer, const ObjectValue::Map::value_type& kv) { +void EncodeFieldsEntry(Writer* writer, + const ObjectValue::Map::value_type& kv, + uint32_t key_tag, + uint32_t value_tag) { // Write the key (string) - writer->WriteTag( - {PB_WT_STRING, google_firestore_v1beta1_MapValue_FieldsEntry_key_tag}); + writer->WriteTag({PB_WT_STRING, key_tag}); writer->WriteString(kv.first); // Write the value (FieldValue) - writer->WriteTag( - {PB_WT_STRING, google_firestore_v1beta1_MapValue_FieldsEntry_value_tag}); + writer->WriteTag({PB_WT_STRING, value_tag}); writer->WriteNestedMessage( [&kv](Writer* writer) { EncodeFieldValueImpl(writer, kv.second); }); } -ObjectValue::Map::value_type DecodeFieldsEntry(Reader* reader) { +ObjectValue::Map::value_type DecodeFieldsEntry(Reader* reader, + uint32_t key_tag, + uint32_t value_tag) { + if (!reader->status().ok()) return {}; + Tag tag = reader->ReadTag(); if (!reader->status().ok()) return {}; // TODO(rsgowman): figure out error handling: We can do better than a failed // assertion. - FIREBASE_ASSERT(tag.field_number == - google_firestore_v1beta1_MapValue_FieldsEntry_key_tag); - FIREBASE_ASSERT(tag.wire_type == PB_WT_STRING); + HARD_ASSERT(tag.field_number == key_tag); + HARD_ASSERT(tag.wire_type == PB_WT_STRING); std::string key = reader->ReadString(); tag = reader->ReadTag(); if (!reader->status().ok()) return {}; - FIREBASE_ASSERT(tag.field_number == - google_firestore_v1beta1_MapValue_FieldsEntry_value_tag); - FIREBASE_ASSERT(tag.wire_type == PB_WT_STRING); + HARD_ASSERT(tag.field_number == value_tag); + HARD_ASSERT(tag.wire_type == PB_WT_STRING); FieldValue value = reader->ReadNestedMessage<FieldValue>(DecodeFieldValueImpl); @@ -766,49 +299,72 @@ ObjectValue::Map::value_type DecodeFieldsEntry(Reader* reader) { return ObjectValue::Map::value_type{key, value}; } -void EncodeObject(Writer* writer, const ObjectValue& object_value) { - return writer->WriteNestedMessage([&object_value](Writer* writer) { - // Write each FieldsEntry (i.e. key-value pair.) - for (const auto& kv : object_value.internal_value) { - writer->WriteTag({PB_WT_STRING, - google_firestore_v1beta1_MapValue_FieldsEntry_key_tag}); - writer->WriteNestedMessage( - [&kv](Writer* writer) { return EncodeFieldsEntry(writer, kv); }); - } - }); +ObjectValue::Map::value_type DecodeMapValueFieldsEntry(Reader* reader) { + return DecodeFieldsEntry( + reader, google_firestore_v1beta1_MapValue_FieldsEntry_key_tag, + google_firestore_v1beta1_MapValue_FieldsEntry_value_tag); } -ObjectValue::Map DecodeObject(Reader* reader) { - if (!reader->status().ok()) return ObjectValue::Map(); - - return reader->ReadNestedMessage<ObjectValue::Map>( - [](Reader* reader) -> ObjectValue::Map { - ObjectValue::Map result; - if (!reader->status().ok()) return result; - - while (reader->bytes_left()) { - Tag tag = reader->ReadTag(); - if (!reader->status().ok()) return result; - FIREBASE_ASSERT(tag.field_number == - google_firestore_v1beta1_MapValue_fields_tag); - FIREBASE_ASSERT(tag.wire_type == PB_WT_STRING); - - ObjectValue::Map::value_type fv = - reader->ReadNestedMessage<ObjectValue::Map::value_type>( - DecodeFieldsEntry); - - // Sanity check: ensure that this key doesn't already exist in the - // map. - // TODO(rsgowman): figure out error handling: We can do better than a - // failed assertion. - if (!reader->status().ok()) return result; - FIREBASE_ASSERT(result.find(fv.first) == result.end()); - - // Add this key,fieldvalue to the results map. - result.emplace(std::move(fv)); - } - return result; - }); +ObjectValue::Map::value_type DecodeDocumentFieldsEntry(Reader* reader) { + return DecodeFieldsEntry( + reader, google_firestore_v1beta1_Document_FieldsEntry_key_tag, + google_firestore_v1beta1_Document_FieldsEntry_value_tag); +} + +void EncodeObjectMap(Writer* writer, + const ObjectValue::Map& object_value_map, + uint32_t map_tag, + uint32_t key_tag, + uint32_t value_tag) { + // Write each FieldsEntry (i.e. key-value pair.) + for (const auto& kv : object_value_map) { + writer->WriteTag({PB_WT_STRING, map_tag}); + writer->WriteNestedMessage([&kv, &key_tag, &value_tag](Writer* writer) { + return EncodeFieldsEntry(writer, kv, key_tag, value_tag); + }); + } +} + +void EncodeMapValue(Writer* writer, const ObjectValue& object_value) { + EncodeObjectMap(writer, object_value.internal_value, + google_firestore_v1beta1_MapValue_fields_tag, + google_firestore_v1beta1_MapValue_FieldsEntry_key_tag, + google_firestore_v1beta1_MapValue_FieldsEntry_value_tag); +} + +ObjectValue::Map DecodeMapValue(Reader* reader) { + ObjectValue::Map result; + if (!reader->status().ok()) return result; + + while (reader->bytes_left()) { + Tag tag = reader->ReadTag(); + if (!reader->status().ok()) return result; + // The MapValue message only has a single valid tag. + // TODO(rsgowman): figure out error handling: We can do better than a + // failed assertion. + HARD_ASSERT(tag.field_number == + google_firestore_v1beta1_MapValue_fields_tag); + HARD_ASSERT(tag.wire_type == PB_WT_STRING); + + ObjectValue::Map::value_type fv = + reader->ReadNestedMessage<ObjectValue::Map::value_type>( + DecodeMapValueFieldsEntry); + + if (!reader->status().ok()) return result; + + // Assumption: If we parse two entries for the map that have the same key, + // then the latter should overwrite the former. This does not appear to be + // explicitly called out by the docs, but seems to be in the spirit of how + // things work. (i.e. non-repeated fields explicitly follow this behaviour.) + // In any case, well behaved proto emitters shouldn't create encodings like + // this, but well behaved parsers are expected to handle these cases. + // + // https://developers.google.com/protocol-buffers/docs/encoding#optional + + // Add this key,fieldvalue to the results map. + result[fv.first] = fv.second; + } + return result; } /** @@ -849,9 +405,9 @@ bool IsValidResourceName(const ResourcePath& path) { */ ResourcePath DecodeResourceName(absl::string_view encoded) { ResourcePath resource = ResourcePath::FromString(encoded); - FIREBASE_ASSERT_MESSAGE(IsValidResourceName(resource), - "Tried to deserialize invalid key %s", - resource.CanonicalString().c_str()); + HARD_ASSERT(IsValidResourceName(resource), + "Tried to deserialize invalid key %s", + resource.CanonicalString()); return resource; } @@ -862,10 +418,9 @@ ResourcePath DecodeResourceName(absl::string_view encoded) { */ ResourcePath ExtractLocalPathFromResourceName( const ResourcePath& resource_name) { - FIREBASE_ASSERT_MESSAGE( - resource_name.size() > 4 && resource_name[4] == "documents", - "Tried to deserialize invalid key %s", - resource_name.CanonicalString().c_str()); + HARD_ASSERT(resource_name.size() > 4 && resource_name[4] == "documents", + "Tried to deserialize invalid key %s", + resource_name.CanonicalString()); return resource_name.PopFirst(5); } @@ -895,13 +450,197 @@ std::string Serializer::EncodeKey(const DocumentKey& key) const { DocumentKey Serializer::DecodeKey(absl::string_view name) const { ResourcePath resource = DecodeResourceName(name); - FIREBASE_ASSERT_MESSAGE(resource[1] == database_id_.project_id(), - "Tried to deserialize key from different project."); - FIREBASE_ASSERT_MESSAGE(resource[3] == database_id_.database_id(), - "Tried to deserialize key from different database."); + HARD_ASSERT(resource[1] == database_id_.project_id(), + "Tried to deserialize key from different project."); + HARD_ASSERT(resource[3] == database_id_.database_id(), + "Tried to deserialize key from different database."); return DocumentKey{ExtractLocalPathFromResourceName(resource)}; } +util::Status Serializer::EncodeDocument(const DocumentKey& key, + const ObjectValue& value, + std::vector<uint8_t>* out_bytes) const { + Writer writer = Writer::Wrap(out_bytes); + EncodeDocument(&writer, key, value); + return writer.status(); +} + +void Serializer::EncodeDocument(Writer* writer, + const DocumentKey& key, + const ObjectValue& object_value) const { + // Encode Document.name + writer->WriteTag({PB_WT_STRING, google_firestore_v1beta1_Document_name_tag}); + writer->WriteString(EncodeKey(key)); + + // Encode Document.fields (unless it's empty) + if (!object_value.internal_value.empty()) { + EncodeObjectMap(writer, object_value.internal_value, + google_firestore_v1beta1_Document_fields_tag, + google_firestore_v1beta1_Document_FieldsEntry_key_tag, + google_firestore_v1beta1_Document_FieldsEntry_value_tag); + } + + // Skip Document.create_time and Document.update_time, since they're + // output-only fields. +} + +util::StatusOr<std::unique_ptr<model::MaybeDocument>> +Serializer::DecodeMaybeDocument(const uint8_t* bytes, size_t length) const { + Reader reader = Reader::Wrap(bytes, length); + std::unique_ptr<MaybeDocument> maybeDoc = + DecodeBatchGetDocumentsResponse(&reader); + + if (reader.status().ok()) { + return std::move(maybeDoc); + } else { + return reader.status(); + } +} + +std::unique_ptr<MaybeDocument> Serializer::DecodeBatchGetDocumentsResponse( + Reader* reader) const { + if (!reader->status().ok()) return nullptr; + + // Initialize BatchGetDocumentsResponse fields to their default values + std::unique_ptr<MaybeDocument> found; + std::string missing; + // TODO(rsgowman): transaction + SnapshotVersion read_time = SnapshotVersion::None(); + + while (reader->bytes_left()) { + Tag tag = reader->ReadTag(); + if (!reader->status().ok()) return nullptr; + + // Ensure the tag matches the wire type + switch (tag.field_number) { + case google_firestore_v1beta1_BatchGetDocumentsResponse_found_tag: + case google_firestore_v1beta1_BatchGetDocumentsResponse_missing_tag: + case google_firestore_v1beta1_BatchGetDocumentsResponse_read_time_tag: + if (tag.wire_type != PB_WT_STRING) { + reader->set_status( + Status(FirestoreErrorCode::DataLoss, + "Input proto bytes cannot be parsed (mismatch between " + "the wiretype and the field number (tag))")); + } + break; + + case google_firestore_v1beta1_BatchGetDocumentsResponse_transaction_tag: + // TODO(rsgowman) + abort(); + + default: + reader->set_status(Status( + FirestoreErrorCode::DataLoss, + "Input proto bytes cannot be parsed (invalid field number (tag))")); + } + + if (!reader->status().ok()) return nullptr; + + switch (tag.field_number) { + case google_firestore_v1beta1_BatchGetDocumentsResponse_found_tag: + // 'found' and 'missing' are part of a oneof. The proto docs claim that + // if both are set on the wire, the last one wins. + missing = ""; + + // TODO(rsgowman): If multiple 'found' values are found, we should merge + // them (rather than using the last one.) + found = reader->ReadNestedMessage<std::unique_ptr<MaybeDocument>>( + [this](Reader* reader) -> std::unique_ptr<MaybeDocument> { + return DecodeDocument(reader); + }); + break; + + case google_firestore_v1beta1_BatchGetDocumentsResponse_missing_tag: + // 'found' and 'missing' are part of a oneof. The proto docs claim that + // if both are set on the wire, the last one wins. + found = nullptr; + + missing = reader->ReadString(); + break; + + case google_firestore_v1beta1_BatchGetDocumentsResponse_transaction_tag: + // TODO(rsgowman) + abort(); + + case google_firestore_v1beta1_BatchGetDocumentsResponse_read_time_tag: + read_time = SnapshotVersion{ + reader->ReadNestedMessage<Timestamp>(DecodeTimestamp)}; + break; + + default: + // This indicates an internal error as we've already ensured that this + // is a valid field_number. + HARD_FAIL( + "Somehow got an unexpected field number (tag) after verifying that " + "the field number was expected."); + } + } + + if (found != nullptr) { + return found; + } else if (!missing.empty()) { + return absl::make_unique<NoDocument>(DecodeKey(missing), read_time); + } else { + // Neither 'found' nor 'missing' fields were set. + // TODO(rsgowman): Handle the error case. + abort(); + } +} + +std::unique_ptr<Document> Serializer::DecodeDocument(Reader* reader) const { + if (!reader->status().ok()) return nullptr; + + std::string name; + ObjectValue::Map fields_internal; + SnapshotVersion version = SnapshotVersion::None(); + + while (reader->bytes_left()) { + Tag tag = reader->ReadTag(); + if (!reader->status().ok()) return nullptr; + HARD_ASSERT(tag.wire_type == PB_WT_STRING); + switch (tag.field_number) { + case google_firestore_v1beta1_Document_name_tag: + name = reader->ReadString(); + break; + case google_firestore_v1beta1_Document_fields_tag: { + ObjectValue::Map::value_type fv = + reader->ReadNestedMessage<ObjectValue::Map::value_type>( + DecodeDocumentFieldsEntry); + + if (!reader->status().ok()) return nullptr; + + // Assumption: For duplicates, the latter overrides the former, see + // comment on writing object map for details (DecodeMapValue). + + // Add fieldvalue to the results map. + fields_internal[fv.first] = fv.second; + break; + } + case google_firestore_v1beta1_Document_create_time_tag: + // This field is ignored by the client sdk, but we still need to extract + // it. + reader->ReadNestedMessage<Timestamp>(DecodeTimestamp); + break; + case google_firestore_v1beta1_Document_update_time_tag: + // TODO(rsgowman): Rather than overwriting, we should instead merge with + // the existing SnapshotVersion (if any). Less relevant here, since it's + // just two numbers which are both expected to be present, but if the + // proto evolves that might change. + version = SnapshotVersion{ + reader->ReadNestedMessage<Timestamp>(DecodeTimestamp)}; + break; + default: + // TODO(rsgowman): Error handling. (Invalid tags should fail to decode, + // but shouldn't cause a crash.) + abort(); + } + } + + return absl::make_unique<Document>( + FieldValue::ObjectValueFromMap(fields_internal), DecodeKey(name), version, + /*has_local_modifications=*/false); +} + } // namespace remote } // namespace firestore } // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/remote/serializer.h b/Firestore/core/src/firebase/firestore/remote/serializer.h index 86aa6e2..379069c 100644 --- a/Firestore/core/src/firebase/firestore/remote/serializer.h +++ b/Firestore/core/src/firebase/firestore/remote/serializer.h @@ -19,13 +19,18 @@ #include <cstdint> #include <cstdlib> +#include <memory> #include <string> #include <vector> #include "Firestore/core/src/firebase/firestore/model/database_id.h" +#include "Firestore/core/src/firebase/firestore/model/document.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" #include "Firestore/core/src/firebase/firestore/model/field_value.h" -#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" +#include "Firestore/core/src/firebase/firestore/model/maybe_document.h" +#include "Firestore/core/src/firebase/firestore/nanopb/reader.h" +#include "Firestore/core/src/firebase/firestore/nanopb/writer.h" +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" #include "Firestore/core/src/firebase/firestore/util/status.h" #include "Firestore/core/src/firebase/firestore/util/statusor.h" #include "absl/base/attributes.h" @@ -59,7 +64,7 @@ class Serializer { } /** - * Converts the FieldValue model passed into bytes. + * @brief Converts the FieldValue model passed into bytes. * * @param field_value the model to convert. * @param[out] out_bytes A buffer to place the output. The bytes will be @@ -111,7 +116,56 @@ class Serializer { firebase::firestore::model::DocumentKey DecodeKey( absl::string_view name) const; + /** + * @brief Converts the Document (i.e. key/value) into bytes. + * + * @param[out] out_bytes A buffer to place the output. The bytes will be + * appended to this vector. + * @return A Status, which if not ok(), indicates what went wrong. Note that + * errors during encoding generally indicate a serious/fatal error. + */ + // TODO(rsgowman): Similar to above, if we never support any output except to + // a vector, it may make sense to have Serializer own the vector and provide + // an accessor rather than asking the user to create it first. + util::Status EncodeDocument( + const firebase::firestore::model::DocumentKey& key, + const firebase::firestore::model::ObjectValue& value, + std::vector<uint8_t>* out_bytes) const; + + /** + * @brief Converts from bytes to the model Document format. + * + * @param bytes The bytes to convert. These bytes must represent a + * BatchGetDocumentsResponse. It's assumed that exactly all of the bytes will + * be used by this conversion. + * @return The model equivalent of the bytes or a Status indicating + * what went wrong. + */ + util::StatusOr<std::unique_ptr<model::MaybeDocument>> DecodeMaybeDocument( + const uint8_t* bytes, size_t length) const; + + /** + * @brief Converts from bytes to the model Document format. + * + * @param bytes The bytes to convert. These bytes must represent a + * BatchGetDocumentsResponse. It's assumed that exactly all of the bytes will + * be used by this conversion. + * @return The model equivalent of the bytes or a Status indicating + * what went wrong. + */ + util::StatusOr<std::unique_ptr<model::MaybeDocument>> DecodeMaybeDocument( + const std::vector<uint8_t>& bytes) const { + return DecodeMaybeDocument(bytes.data(), bytes.size()); + } + private: + void EncodeDocument(nanopb::Writer* writer, + const model::DocumentKey& key, + const model::ObjectValue& object_value) const; + std::unique_ptr<model::MaybeDocument> DecodeBatchGetDocumentsResponse( + nanopb::Reader* reader) const; + std::unique_ptr<model::Document> DecodeDocument(nanopb::Reader* reader) const; + const firebase::firestore::model::DatabaseId& database_id_; }; diff --git a/Firestore/core/src/firebase/firestore/timestamp.cc b/Firestore/core/src/firebase/firestore/timestamp.cc index a5f0121..c35fca1 100644 --- a/Firestore/core/src/firebase/firestore/timestamp.cc +++ b/Firestore/core/src/firebase/firestore/timestamp.cc @@ -16,7 +16,7 @@ #include "Firestore/core/include/firebase/firestore/timestamp.h" -#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" namespace firebase { @@ -58,7 +58,7 @@ Timestamp Timestamp::FromTimePoint( const auto epoch_time = time_point.time_since_epoch(); auto seconds = chr::duration_cast<chr::duration<int64_t>>(epoch_time); auto nanoseconds = chr::duration_cast<chr::nanoseconds>(epoch_time - seconds); - FIREBASE_DEV_ASSERT(nanoseconds.count() < 1 * 1000 * 1000 * 1000); + HARD_ASSERT(nanoseconds.count() < 1 * 1000 * 1000 * 1000); if (nanoseconds.count() < 0) { // Timestamp format always has a positive number of nanoseconds that is @@ -89,19 +89,17 @@ std::string Timestamp::ToString() const { } void Timestamp::ValidateBounds() const { - FIREBASE_ASSERT_MESSAGE(nanoseconds_ >= 0, - "Timestamp nanoseconds out of range: %d", - nanoseconds_); - FIREBASE_ASSERT_MESSAGE(nanoseconds_ < 1e9, - "Timestamp nanoseconds out of range: %d", - nanoseconds_); + HARD_ASSERT(nanoseconds_ >= 0, "Timestamp nanoseconds out of range: %s", + nanoseconds_); + HARD_ASSERT(nanoseconds_ < 1e9, "Timestamp nanoseconds out of range: %s", + nanoseconds_); // Midnight at the beginning of 1/1/1 is the earliest timestamp Firestore // supports. - FIREBASE_ASSERT_MESSAGE(seconds_ >= -62135596800L, - "Timestamp seconds out of range: %lld", seconds_); + HARD_ASSERT(seconds_ >= -62135596800L, "Timestamp seconds out of range: %s", + seconds_); // This will break in the year 10,000. - FIREBASE_ASSERT_MESSAGE(seconds_ < 253402300800L, - "Timestamp seconds out of range: %lld", seconds_); + HARD_ASSERT(seconds_ < 253402300800L, "Timestamp seconds out of range: %s", + seconds_); } } // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/util/CMakeLists.txt b/Firestore/core/src/firebase/firestore/util/CMakeLists.txt index b2b015b..043713f 100644 --- a/Firestore/core/src/firebase/firestore/util/CMakeLists.txt +++ b/Firestore/core/src/firebase/firestore/util/CMakeLists.txt @@ -22,10 +22,11 @@ include(CheckIncludeFiles) cc_library( firebase_firestore_util_base SOURCES - string_printf.cc - string_printf.h + string_format.cc + string_format.h DEPENDS absl_base + absl_strings ) ## assert and log @@ -33,8 +34,8 @@ cc_library( cc_library( firebase_firestore_util_log_stdio SOURCES - firebase_assert.h - assert_stdio.cc + hard_assert_stdio.cc + hard_assert.h log.h log_stdio.cc DEPENDS @@ -46,14 +47,15 @@ cc_library( cc_library( firebase_firestore_util_log_apple SOURCES - firebase_assert.h - assert_apple.mm + hard_assert.h + hard_assert_apple.mm log.h log_apple.mm string_apple.h DEPENDS FirebaseCore absl_strings + firebase_firestore_util_base EXCLUDE_FROM_ALL ) diff --git a/Firestore/core/src/firebase/firestore/util/async_queue.cc b/Firestore/core/src/firebase/firestore/util/async_queue.cc index 71f5cc5..b42dec3 100644 --- a/Firestore/core/src/firebase/firestore/util/async_queue.cc +++ b/Firestore/core/src/firebase/firestore/util/async_queue.cc @@ -18,7 +18,7 @@ #include <utility> -#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" #include "absl/memory/memory.h" namespace firebase { @@ -32,28 +32,29 @@ AsyncQueue::AsyncQueue(std::unique_ptr<Executor> executor) is_operation_in_progress_ = false; } +// TODO(varconst): assert in destructor that the queue is empty. + void AsyncQueue::VerifyIsCurrentExecutor() const { - FIREBASE_ASSERT_MESSAGE( + HARD_ASSERT( executor_->IsCurrentExecutor(), "Expected to be called by the executor associated with this queue " "(expected executor: '%s', actual executor: '%s')", - executor_->Name().c_str(), executor_->CurrentExecutorName().c_str()); + executor_->Name(), executor_->CurrentExecutorName()); } void AsyncQueue::VerifyIsCurrentQueue() const { VerifyIsCurrentExecutor(); - FIREBASE_ASSERT_MESSAGE( - is_operation_in_progress_, - "VerifyIsCurrentQueue called when no operation is executing " - "(expected executor: '%s', actual executor: '%s')", - executor_->Name().c_str(), executor_->CurrentExecutorName().c_str()); + HARD_ASSERT(is_operation_in_progress_, + "VerifyIsCurrentQueue called when no operation is executing " + "(expected executor: '%s', actual executor: '%s')", + executor_->Name(), executor_->CurrentExecutorName()); } void AsyncQueue::ExecuteBlocking(const Operation& operation) { VerifyIsCurrentExecutor(); - FIREBASE_ASSERT_MESSAGE(!is_operation_in_progress_, - "ExecuteBlocking may not be called " - "before the previous operation finishes executing"); + HARD_ASSERT(!is_operation_in_progress_, + "ExecuteBlocking may not be called " + "before the previous operation finishes executing"); is_operation_in_progress_ = true; operation(); @@ -77,9 +78,8 @@ DelayedOperation AsyncQueue::EnqueueAfterDelay(const Milliseconds delay, // While not necessarily harmful, we currently don't expect to have multiple // callbacks with the same timer_id in the queue, so defensively reject // them. - FIREBASE_ASSERT_MESSAGE( - !IsScheduled(timer_id), - "Attempted to schedule multiple operations with id %d", timer_id); + HARD_ASSERT(!IsScheduled(timer_id), + "Attempted to schedule multiple operations with id %s", timer_id); Executor::TaggedOperation tagged{static_cast<int>(timer_id), Wrap(operation)}; return executor_->Schedule(delay, std::move(tagged)); @@ -95,12 +95,11 @@ AsyncQueue::Operation AsyncQueue::Wrap(const Operation& operation) { void AsyncQueue::VerifySequentialOrder() const { // This is the inverse of `VerifyIsCurrentQueue`. - FIREBASE_ASSERT_MESSAGE( - !is_operation_in_progress_ || !executor_->IsCurrentExecutor(), - "Enforcing sequential order failed: currently executing operations " - "cannot enqueue more operations " - "(this queue's executor: '%s', current executor: '%s')", - executor_->Name().c_str(), executor_->CurrentExecutorName().c_str()); + HARD_ASSERT(!is_operation_in_progress_ || !executor_->IsCurrentExecutor(), + "Enqueue methods cannot be called when we are already running on " + "target executor " + "(this queue's executor: '%s', current executor: '%s')", + executor_->Name(), executor_->CurrentExecutorName()); } // Test-only functions @@ -115,14 +114,13 @@ bool AsyncQueue::IsScheduled(const TimerId timer_id) const { } void AsyncQueue::RunScheduledOperationsUntil(const TimerId last_timer_id) { - FIREBASE_ASSERT_MESSAGE( - !executor_->IsCurrentExecutor(), - "RunScheduledOperationsUntil must not be called on the queue"); + HARD_ASSERT(!executor_->IsCurrentExecutor(), + "RunScheduledOperationsUntil must not be called on the queue"); executor_->ExecuteBlocking([this, last_timer_id] { - FIREBASE_ASSERT_MESSAGE( + HARD_ASSERT( last_timer_id == TimerId::All || IsScheduled(last_timer_id), - "Attempted to run scheduled operations until missing timer id: %d", + "Attempted to run scheduled operations until missing timer id: %s", last_timer_id); for (auto next = executor_->PopFromSchedule(); next.has_value(); diff --git a/Firestore/core/src/firebase/firestore/util/bits.cc b/Firestore/core/src/firebase/firestore/util/bits.cc index 0bfe4c3..03d9b77 100644 --- a/Firestore/core/src/firebase/firestore/util/bits.cc +++ b/Firestore/core/src/firebase/firestore/util/bits.cc @@ -16,7 +16,7 @@ #include "Firestore/core/src/firebase/firestore/util/bits.h" -#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" namespace firebase { namespace firestore { @@ -34,7 +34,7 @@ int Bits::Log2Floor_Portable(uint32_t n) { log += shift; } } - FIREBASE_ASSERT(value == 1); + HARD_ASSERT(value == 1); return log; } diff --git a/Firestore/core/src/firebase/firestore/util/executor_libdispatch.h b/Firestore/core/src/firebase/firestore/util/executor_libdispatch.h index 85c34f8..d356a74 100644 --- a/Firestore/core/src/firebase/firestore/util/executor_libdispatch.h +++ b/Firestore/core/src/firebase/firestore/util/executor_libdispatch.h @@ -26,7 +26,6 @@ #include "dispatch/dispatch.h" #include "Firestore/core/src/firebase/firestore/util/executor.h" -#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" #include "absl/strings/string_view.h" #if !defined(__OBJC__) @@ -45,10 +44,10 @@ namespace internal { // Generic wrapper over `dispatch_async_f`, providing `dispatch_async`-like // interface: accepts an arbitrary invocable object in place of an Objective-C // block. -void DispatchAsync(const dispatch_queue_t queue, std::function<void()>&& work); +void DispatchAsync(dispatch_queue_t queue, std::function<void()>&& work); // Similar to `DispatchAsync` but wraps `dispatch_sync_f`. -void DispatchSync(const dispatch_queue_t queue, std::function<void()> work); +void DispatchSync(dispatch_queue_t queue, std::function<void()> work); class TimeSlot; @@ -56,9 +55,7 @@ class TimeSlot; // a dedicated serial dispatch queue. class ExecutorLibdispatch : public Executor { public: - ExecutorLibdispatch(); explicit ExecutorLibdispatch(dispatch_queue_t dispatch_queue); - ~ExecutorLibdispatch(); bool IsCurrentExecutor() const override; std::string CurrentExecutorName() const override; @@ -79,11 +76,6 @@ class ExecutorLibdispatch : public Executor { } private: - // GetLabel functions are guaranteed to never return a "null" string_view - // (i.e. data() != nullptr). - absl::string_view GetCurrentQueueLabel() const; - absl::string_view GetTargetQueueLabel() const; - dispatch_queue_t dispatch_queue_; // Stores non-owned pointers to `TimeSlot`s. // Invariant: if a `TimeSlot` is in `schedule_`, it's a valid pointer. diff --git a/Firestore/core/src/firebase/firestore/util/executor_libdispatch.mm b/Firestore/core/src/firebase/firestore/util/executor_libdispatch.mm index 5491fec..70d0b3a 100644 --- a/Firestore/core/src/firebase/firestore/util/executor_libdispatch.mm +++ b/Firestore/core/src/firebase/firestore/util/executor_libdispatch.mm @@ -16,6 +16,8 @@ #include "Firestore/core/src/firebase/firestore/util/executor_libdispatch.h" +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" + namespace firebase { namespace firestore { namespace util { @@ -28,13 +30,16 @@ absl::string_view StringViewFromDispatchLabel(const char* const label) { return label ? absl::string_view{label} : absl::string_view{""}; } -void RunSynchronized(const ExecutorLibdispatch* const executor, - std::function<void()>&& work) { - if (executor->IsCurrentExecutor()) { - work(); - } else { - DispatchSync(executor->dispatch_queue(), std::move(work)); - } +// GetLabel functions are guaranteed to never return a "null" string_view +// (i.e. data() != nullptr). +absl::string_view GetQueueLabel(const dispatch_queue_t queue) { + return StringViewFromDispatchLabel(dispatch_queue_get_label(queue)); +} +absl::string_view GetCurrentQueueLabel() { + // Note: dispatch_queue_get_label may return nullptr if the queue wasn't + // initialized with a label. + return StringViewFromDispatchLabel( + dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL)); } } // namespace @@ -51,15 +56,32 @@ void DispatchAsync(const dispatch_queue_t queue, std::function<void()>&& work) { }); } -void DispatchSync(const dispatch_queue_t queue, Executor::Operation work) { +void DispatchSync(const dispatch_queue_t queue, std::function<void()> work) { + HARD_ASSERT( + GetCurrentQueueLabel() != GetQueueLabel(queue), + "Calling DispatchSync on the current queue will lead to a deadlock."); + // Unlike dispatch_async_f, dispatch_sync_f blocks until the work passed to it // is done, so passing a reference to a local variable is okay. dispatch_sync_f(queue, &work, [](void* const raw_work) { - const auto unwrap = static_cast<const Executor::Operation*>(raw_work); + const auto unwrap = static_cast<std::function<void()>*>(raw_work); (*unwrap)(); }); } +namespace { + +template <typename Work> +void RunSynchronized(const ExecutorLibdispatch* const executor, Work&& work) { + if (executor->IsCurrentExecutor()) { + work(); + } else { + DispatchSync(executor->dispatch_queue(), std::forward<Work>(work)); + } +} + +} // namespace + // Represents a "busy" time slot on the schedule. // // Since libdispatch doesn't provide a way to cancel a scheduled operation, once @@ -156,8 +178,8 @@ void TimeSlot::Execute() { RemoveFromSchedule(); - FIREBASE_ASSERT_MESSAGE(tagged_.operation, - "TimeSlot contains an invalid function object"); + HARD_ASSERT(tagged_.operation, + "TimeSlot contains an invalid function object"); tagged_.operation(); } @@ -170,32 +192,15 @@ void TimeSlot::RemoveFromSchedule() { ExecutorLibdispatch::ExecutorLibdispatch(const dispatch_queue_t dispatch_queue) : dispatch_queue_{dispatch_queue} { } -ExecutorLibdispatch::ExecutorLibdispatch() - : ExecutorLibdispatch{dispatch_queue_create("com.google.firebase.firestore", - DISPATCH_QUEUE_SERIAL)} { -} - -ExecutorLibdispatch::~ExecutorLibdispatch() { - // Turn any operations that might still be in the queue into no-ops, lest - // they try to access `ExecutorLibdispatch` after it gets destroyed. Because - // the queue is serial, by the time libdispatch gets to the newly-enqueued - // work, the pending operations that might have been in progress would have - // already finished. - RunSynchronized(this, [this] { - for (auto slot : schedule_) { - slot->MarkDone(); - } - }); -} bool ExecutorLibdispatch::IsCurrentExecutor() const { - return GetCurrentQueueLabel().data() == GetTargetQueueLabel().data(); + return GetCurrentQueueLabel() == GetQueueLabel(dispatch_queue()); } std::string ExecutorLibdispatch::CurrentExecutorName() const { return GetCurrentQueueLabel().data(); } std::string ExecutorLibdispatch::Name() const { - return GetTargetQueueLabel().data(); + return GetQueueLabel(dispatch_queue()).data(); } void ExecutorLibdispatch::Execute(Operation&& operation) { @@ -243,29 +248,14 @@ void ExecutorLibdispatch::RemoveFromSchedule(const TimeSlot* const to_remove) { }); } -// GetLabel functions are guaranteed to never return a "null" string_view -// (i.e. data() != nullptr). -absl::string_view ExecutorLibdispatch::GetCurrentQueueLabel() const { - // Note: dispatch_queue_get_label may return nullptr if the queue wasn't - // initialized with a label. - return StringViewFromDispatchLabel( - dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL)); -} - -absl::string_view ExecutorLibdispatch::GetTargetQueueLabel() const { - return StringViewFromDispatchLabel( - dispatch_queue_get_label(dispatch_queue())); -} - // Test-only methods bool ExecutorLibdispatch::IsScheduled(const Tag tag) const { bool result = false; RunSynchronized(this, [this, tag, &result] { - result = std::find_if(schedule_.begin(), schedule_.end(), - [&tag](const TimeSlot* const operation) { - return *operation == tag; - }) != schedule_.end(); + result = std::any_of( + schedule_.begin(), schedule_.end(), + [&tag](const TimeSlot* const operation) { return *operation == tag; }); }); return result; } diff --git a/Firestore/core/src/firebase/firestore/util/executor_std.cc b/Firestore/core/src/firebase/firestore/util/executor_std.cc index f03a712..a87ec20 100644 --- a/Firestore/core/src/firebase/firestore/util/executor_std.cc +++ b/Firestore/core/src/firebase/firestore/util/executor_std.cc @@ -64,8 +64,7 @@ DelayedOperation ExecutorStd::Schedule(const Milliseconds delay, // While negative delay can be interpreted as a request for immediate // execution, supporting it would provide a hacky way to modify FIFO ordering // of immediate operations. - FIREBASE_ASSERT_MESSAGE(delay.count() >= 0, - "Schedule: delay cannot be negative"); + HARD_ASSERT(delay.count() >= 0, "Schedule: delay cannot be negative"); namespace chr = std::chrono; const auto now = chr::time_point_cast<Milliseconds>(chr::steady_clock::now()); diff --git a/Firestore/core/src/firebase/firestore/util/executor_std.h b/Firestore/core/src/firebase/firestore/util/executor_std.h index 58ef96f..efe60db 100644 --- a/Firestore/core/src/firebase/firestore/util/executor_std.h +++ b/Firestore/core/src/firebase/firestore/util/executor_std.h @@ -27,7 +27,7 @@ #include <utility> #include "Firestore/core/src/firebase/firestore/util/executor.h" -#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" #include "absl/types/optional.h" namespace firebase { @@ -41,7 +41,7 @@ namespace async { // the exact same time are prioritized in FIFO order. // // The main function of `Schedule` is `PopBlocking`, which sleeps until an entry -// becomes available. It correctly handles entries being asynchonously added or +// becomes available. It correctly handles entries being asynchronously added or // removed from the schedule. // // The details of time management are completely concealed within the class. @@ -181,8 +181,8 @@ class Schedule { // This function expects the mutex to be already locked. T ExtractLocked(const Iterator where) { - FIREBASE_ASSERT_MESSAGE(!scheduled_.empty(), - "Trying to pop an entry from an empty queue."); + HARD_ASSERT(!scheduled_.empty(), + "Trying to pop an entry from an empty queue."); T result = std::move(where->value); scheduled_.erase(where); @@ -220,7 +220,6 @@ class ExecutorStd : public Executor { bool IsScheduled(Tag tag) const override; absl::optional<TaggedOperation> PopFromSchedule() override; - private: using TimePoint = async::Schedule<Operation>::TimePoint; // To allow canceling operations, each scheduled operation is assigned // a monotonically increasing identifier. diff --git a/Firestore/core/src/firebase/firestore/util/firebase_assert.h b/Firestore/core/src/firebase/firestore/util/firebase_assert.h deleted file mode 100644 index 6a9c2eb..0000000 --- a/Firestore/core/src/firebase/firestore/util/firebase_assert.h +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2018 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// 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 <cstdlib> - -#include "Firestore/core/src/firebase/firestore/util/log.h" -#include "absl/base/attributes.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) - -// Assert expression is true otherwise display the specified message and -// abort. -#define FIREBASE_ASSERT_MESSAGE(expression, ...) \ - FIREBASE_ASSERT_MESSAGE_WITH_EXPRESSION(expression, expression, __VA_ARGS__) - -// Assert expression is true otherwise display the specified message and -// abort. Compiled out of release builds. -#define FIREBASE_DEV_ASSERT_MESSAGE(expression, ...) \ - FIREBASE_DEV_ASSERT_MESSAGE_WITH_EXPRESSION(expression, expression, \ - __VA_ARGS__) - -// Indicates an area of the code that cannot be reached (except possibly due to -// undefined behaviour or other similar badness). The only reasonable thing to -// do in these cases is to immediately abort. -#define FIREBASE_UNREACHABLE() abort() - -namespace firebase { -namespace firestore { -namespace util { - -// A no-return helper function. To raise an assertion, use Macro instead. -ABSL_ATTRIBUTE_NORETURN void FailAssert( - const char* file, const char* func, 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/hard_assert.h b/Firestore/core/src/firebase/firestore/util/hard_assert.h new file mode 100644 index 0000000..e60d71a --- /dev/null +++ b/Firestore/core/src/firebase/firestore/util/hard_assert.h @@ -0,0 +1,88 @@ +/* + * Copyright 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_HARD_ASSERT_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_HARD_ASSERT_H_ + +#include <string> + +#include "Firestore/core/src/firebase/firestore/util/string_format.h" + +/** + * Fails the current function if the given condition is false. + * + * Unlike assert(3) or NSAssert, this macro is never compiled out. + * + * @param condition The condition to test. + * @param format (optional) A format string suitable for util::StringFormat. + * @param ... format arguments to pass to util::StringFormat. + */ +#define HARD_ASSERT(condition, ...) \ + do { \ + if (!(condition)) { \ + std::string _message = \ + firebase::firestore::util::StringFormat(__VA_ARGS__); \ + firebase::firestore::util::internal::Fail( \ + __FILE__, __PRETTY_FUNCTION__, __LINE__, _message, #condition); \ + } \ + } while (0) + +/** + * Unconditionally fails the current function. + * + * Unlike assert(3) or NSAssert, this macro is never compiled out. + * + * @param format A format string suitable for util::StringFormat. + * @param ... format arguments to pass to util::StringFormat. + */ +#define HARD_FAIL(...) \ + do { \ + std::string _failure = \ + firebase::firestore::util::StringFormat(__VA_ARGS__); \ + firebase::firestore::util::internal::Fail(__FILE__, __PRETTY_FUNCTION__, \ + __LINE__, _failure); \ + } while (0) + +/** + * Indicates an area of the code that cannot be reached (except possibly due to + * undefined behaviour or other similar badness). The only reasonable thing to + * do in these cases is to immediately abort. + */ +#define UNREACHABLE() abort() + +namespace firebase { +namespace firestore { +namespace util { +namespace internal { + +// A no-return helper function. To raise an assertion, use Macro instead. +ABSL_ATTRIBUTE_NORETURN void Fail(const char* file, + const char* func, + int line, + const std::string& message); + +ABSL_ATTRIBUTE_NORETURN void Fail(const char* file, + const char* func, + int line, + const std::string& message, + const char* condition); + +} // namespace internal +} // namespace util +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_HARD_ASSERT_H_ diff --git a/Firestore/core/src/firebase/firestore/util/assert_apple.mm b/Firestore/core/src/firebase/firestore/util/hard_assert_apple.mm index 9b6a651..3324fe8 100644 --- a/Firestore/core/src/firebase/firestore/util/assert_apple.mm +++ b/Firestore/core/src/firebase/firestore/util/hard_assert_apple.mm @@ -14,36 +14,47 @@ * limitations under the License. */ +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" + #import <Foundation/Foundation.h> -// TODO(wilhuff): match basenames so this can move up top -#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" +#include <string> + #include "Firestore/core/src/firebase/firestore/util/string_apple.h" namespace firebase { namespace firestore { namespace util { +namespace internal { -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); +void Fail(const char* file, + const char* func, + const int line, + const std::string& message) { [[NSAssertionHandler currentHandler] handleFailureInFunction:WrapNSStringNoCopy(func) file:WrapNSStringNoCopy(file) lineNumber:line - description:@"FIRESTORE INTERNAL ASSERTION FAILED: %@", - description]; + description:@"FIRESTORE INTERNAL ASSERTION FAILED: %s", + message.c_str()]; abort(); } +void Fail(const char* file, + const char* func, + const int line, + const std::string& message, + const char* condition) { + std::string failure; + if (message.empty()) { + failure = condition; + } else { + failure = StringFormat("%s (expected %s)", message, condition); + } + Fail(file, func, line, failure); +} + +} // namespace internal } // 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/hard_assert_stdio.cc index e01e564..6c50eb7 100644 --- a/Firestore/core/src/firebase/firestore/util/assert_stdio.cc +++ b/Firestore/core/src/firebase/firestore/util/hard_assert_stdio.cc @@ -14,40 +14,51 @@ * limitations under the License. */ +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" + #include <cstdarg> #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 "Firestore/core/src/firebase/firestore/util/string_format.h" #include "absl/base/config.h" namespace firebase { namespace firestore { namespace util { +namespace internal { -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); +void Fail(const char* file, + const char* func, + const int line, + const std::string& message) { + std::string failure = + StringFormat("ASSERT: %s(%s) %s: %s", file, line, func, message); #if ABSL_HAVE_EXCEPTIONS - throw std::logic_error(message); + throw std::logic_error(failure); #else - fprintf(stderr, "%s\n", message.c_str()); + fprintf(stderr, "%s\n", failure.c_str()); std::terminate(); #endif } +void Fail(const char* file, + const char* func, + const int line, + const std::string& message, + const char* condition) { + std::string failure; + if (message.empty()) { + failure = condition; + } else { + failure = StringFormat("%s (expected %s)", message, condition); + } + Fail(file, func, line, failure); +} + +} // namespace internal } // namespace util } // namespace firestore } // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/util/log.h b/Firestore/core/src/firebase/firestore/util/log.h index 1944596..248d434 100644 --- a/Firestore/core/src/firebase/firestore/util/log.h +++ b/Firestore/core/src/firebase/firestore/util/log.h @@ -17,44 +17,65 @@ #ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_LOG_H_ #define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_LOG_H_ -#include <cstdarg> +#include <string> + +#include "Firestore/core/src/firebase/firestore/util/string_format.h" namespace firebase { namespace firestore { namespace util { -/// @brief Levels used when logging messages. +// Levels used when logging messages. enum LogLevel { - /// Verbose Log Level - kLogLevelVerbose = 0, - /// Debug Log Level + // Debug Log Level kLogLevelDebug, - /// Info Log Level - kLogLevelInfo, - /// Warning Log Level + // Warning Log Level kLogLevelWarning, - /// Error Log Level - kLogLevelError, }; -// Common log methods. +// Log a message if kLogLevelDebug is enabled. Arguments are not evaluated if +// logging is disabled. +// +// @param format A format string suitable for use with `util::StringFormat` +// @param ... C++ variadic arguments that match the format string. Not C +// varargs. +#define LOG_DEBUG(...) \ + do { \ + namespace _util = firebase::firestore::util; \ + if (_util::LogIsLoggable(_util::kLogLevelDebug)) { \ + std::string _message = _util::StringFormat(__VA_ARGS__); \ + _util::LogMessage(_util::kLogLevelDebug, _message); \ + } \ + } while (0) + +// Log a message if kLogLevelWarn is enabled (it is by default). Arguments are +// not evaluated if logging is disabled. +// +// @param format A format string suitable for use with `util::StringFormat` +// @param ... C++ variadic arguments that match the format string. Not C +// varargs. +#define LOG_WARN(...) \ + do { \ + namespace _util = firebase::firestore::util; \ + if (_util::LogIsLoggable(_util::kLogLevelWarning)) { \ + std::string _message = _util::StringFormat(__VA_ARGS__); \ + _util::LogMessage(_util::kLogLevelWarning, _message); \ + } \ + } while (0) + +// Tests to see if the given log level is loggable. +bool LogIsLoggable(LogLevel level); + +// Is debug logging enabled? +inline bool LogIsDebugEnabled() { + return LogIsLoggable(kLogLevelDebug); +} // 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, ...); + +// Log a message at the given level. +void LogMessage(LogLevel log_level, const std::string& message); } // namespace util } // namespace firestore diff --git a/Firestore/core/src/firebase/firestore/util/log_apple.mm b/Firestore/core/src/firebase/firestore/util/log_apple.mm index cb2c58e..45e4f55 100644 --- a/Firestore/core/src/firebase/firestore/util/log_apple.mm +++ b/Firestore/core/src/firebase/firestore/util/log_apple.mm @@ -19,6 +19,7 @@ #import <FirebaseCore/FIRLogger.h> #import <Foundation/Foundation.h> +#include <cstdarg> #include <string> #include "Firestore/core/src/firebase/firestore/util/string_apple.h" @@ -32,90 +33,40 @@ 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, ...) { +// Actually logs a message via FIRLogger. This must be a C varargs function +// so that we can call FIRLogBasic which takes a `va_list`. +void LogMessageV(LogLevel level, NSString* 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); -} + FIRLogBasic(ToFIRLoggerLevel(level), kFIRLoggerFirestore, @"I-FST000001", + format, 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); +} // namespace + +void LogSetLevel(LogLevel level) { + FIRSetLoggerLevel(ToFIRLoggerLevel(level)); } -void LogMessageV(LogLevel log_level, const char* format, va_list args) { - FIRLogBasic(ToFIRLoggerLevel(log_level), kFIRLoggerFirestore, @"I-FST000001", - WrapNSStringNoCopy(format), args); +bool LogIsLoggable(LogLevel level) { + return FIRIsLoggableLevel(ToFIRLoggerLevel(level), NO); } -void LogMessage(LogLevel log_level, const char* format, ...) { - va_list list; - va_start(list, format); - LogMessageV(log_level, format, list); - va_end(list); +void LogMessage(LogLevel level, const std::string& message) { + LogMessageV(level, @"%s", message.c_str()); } } // namespace util diff --git a/Firestore/core/src/firebase/firestore/util/log_stdio.cc b/Firestore/core/src/firebase/firestore/util/log_stdio.cc index b277406..6fff885 100644 --- a/Firestore/core/src/firebase/firestore/util/log_stdio.cc +++ b/Firestore/core/src/firebase/firestore/util/log_stdio.cc @@ -19,77 +19,42 @@ #include <cstdio> #include <string> +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" + namespace firebase { namespace firestore { namespace util { -LogLevel g_log_level = kLogLevelInfo; +LogLevel g_log_level = kLogLevelWarning; 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); +bool LogIsLoggable(LogLevel level) { + return level >= g_log_level; } -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) { +void LogMessage(LogLevel log_level, const std::string& message) { if (log_level < g_log_level) { return; } + + const char* level_word; + switch (log_level) { - case kLogLevelVerbose: - printf("VERBOSE: "); - break; case kLogLevelDebug: - printf("DEBUG: "); - break; - case kLogLevelInfo: + level_word = "DEBUG"; break; case kLogLevelWarning: - printf("WARNING: "); + level_word = "WARNING"; break; - case kLogLevelError: - printf("ERROR: "); + default: + UNREACHABLE(); 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); + printf("%s: %s\n", level_word, message.c_str()); } } // namespace util diff --git a/Firestore/core/src/firebase/firestore/util/ordered_code.cc b/Firestore/core/src/firebase/firestore/util/ordered_code.cc index cb53b09..03da08d 100644 --- a/Firestore/core/src/firebase/firestore/util/ordered_code.cc +++ b/Firestore/core/src/firebase/firestore/util/ordered_code.cc @@ -17,7 +17,7 @@ #include "Firestore/core/src/firebase/firestore/util/ordered_code.h" #include "Firestore/core/src/firebase/firestore/util/bits.h" -#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" #include "absl/base/internal/endian.h" #include "absl/base/internal/unaligned_access.h" #include "absl/base/port.h" @@ -99,20 +99,20 @@ inline static bool IsSpecialByte(char c) { * assertion checking). */ inline static int AdvanceIfNoSpecialBytes(uint32_t v_32, const char* p) { - FIREBASE_DEV_ASSERT(UNALIGNED_LOAD32(p) == v_32); + HARD_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] - FIREBASE_DEV_ASSERT(IsSpecialByte(p[0]) || IsSpecialByte(p[1]) || - IsSpecialByte(p[2]) || IsSpecialByte(p[3])); + HARD_ASSERT(IsSpecialByte(p[0]) || IsSpecialByte(p[1]) || + IsSpecialByte(p[2]) || IsSpecialByte(p[3])); return 0; } else { - FIREBASE_DEV_ASSERT(!IsSpecialByte(p[0])); - FIREBASE_DEV_ASSERT(!IsSpecialByte(p[1])); - FIREBASE_DEV_ASSERT(!IsSpecialByte(p[2])); - FIREBASE_DEV_ASSERT(!IsSpecialByte(p[3])); + HARD_ASSERT(!IsSpecialByte(p[0])); + HARD_ASSERT(!IsSpecialByte(p[1])); + HARD_ASSERT(!IsSpecialByte(p[2])); + HARD_ASSERT(!IsSpecialByte(p[3])); return 4; } } @@ -125,8 +125,8 @@ inline static int AdvanceIfNoSpecialBytes(uint32_t v_32, const char* p) { inline static const char* SkipToNextSpecialByte(const char* start, const char* limit) { // If these constants were ever changed, this routine needs to change - FIREBASE_DEV_ASSERT(kEscape1 == 0); - FIREBASE_DEV_ASSERT((kEscape2 & 0xff) == 255); + HARD_ASSERT(kEscape1 == 0); + HARD_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 @@ -164,8 +164,7 @@ inline static const char* SkipToNextSpecialByte(const char* start, if (IsSpecialByte(p[0])) return p; if (IsSpecialByte(p[1])) return p + 1; if (IsSpecialByte(p[2])) return p + 2; - FIREBASE_DEV_ASSERT( - IsSpecialByte(p[3])); // Last byte must be the special one + HARD_ASSERT(IsSpecialByte(p[3])); // Last byte must be the special one return p + 3; } } @@ -198,14 +197,14 @@ inline static void EncodeStringFragment(std::string* dest, p = SkipToNextSpecialByte(p, limit); if (p >= limit) break; // No more special characters that need escaping char c = *(p++); - FIREBASE_DEV_ASSERT(IsSpecialByte(c)); + HARD_ASSERT(IsSpecialByte(c)); if (c == kEscape1) { AppendBytes(dest, copy_start, static_cast<size_t>(p - copy_start) - 1); dest->push_back(kEscape1); dest->push_back(kNullCharacter); copy_start = p; } else { - FIREBASE_DEV_ASSERT(c == kEscape2); + HARD_ASSERT(c == kEscape2); AppendBytes(dest, copy_start, static_cast<size_t>(p - copy_start) - 1); dest->push_back(kEscape2); dest->push_back(kFFCharacter); @@ -302,7 +301,7 @@ inline static bool ReadStringInternal(absl::string_view* src, // 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'. - FIREBASE_DEV_ASSERT(IsSpecialByte(c)); + HARD_ASSERT(IsSpecialByte(c)); if (c == kEscape1) { if (result) { AppendBytes(result, copy_start, @@ -323,7 +322,7 @@ inline static bool ReadStringInternal(absl::string_view* src, } copy_start = start; } else { - FIREBASE_DEV_ASSERT(c == kEscape2); + HARD_ASSERT(c == kEscape2); if (result) { AppendBytes(result, copy_start, static_cast<size_t>(start - copy_start) - 1); @@ -359,7 +358,7 @@ bool OrderedCode::ReadNumIncreasing(absl::string_view* src, uint64_t* result) { // 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. - FIREBASE_DEV_ASSERT(0 == len || src->size() == 1 || (*src)[1] != '\0'); + HARD_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 @@ -557,11 +556,9 @@ void OrderedCode::WriteSignedNumIncreasing(std::string* dest, int64_t val) { }; UNALIGNED_STORE64(buf + 2, absl::ghtonll(static_cast<uint64_t>(val))); - FIREBASE_DEV_ASSERT_MESSAGE_WITH_EXPRESSION(sizeof(buf) == kMaxSigned64Length, - sizeof(buf) == kMaxSigned64Length, - "max length size mismatch"); + HARD_ASSERT(sizeof(buf) == kMaxSigned64Length, "max length size mismatch"); const size_t len = static_cast<size_t>(SignedEncodingLengthPositive(x)); - FIREBASE_DEV_ASSERT(len >= 2); + HARD_ASSERT(len >= 2); char* const begin = buf + sizeof(buf) - len; begin[0] ^= kLengthToHeaderBits[len][0]; begin[1] ^= kLengthToHeaderBits[len][1]; // ok because len >= 2 @@ -608,8 +605,8 @@ bool OrderedCode::ReadSignedNumIncreasing(absl::string_view* src, x ^= kLengthToMask[len]; // remove spurious header bits - FIREBASE_DEV_ASSERT(len == static_cast<size_t>(SignedEncodingLength( - static_cast<int64_t>(x)))); + HARD_ASSERT(len == static_cast<size_t>( + SignedEncodingLength(static_cast<int64_t>(x)))); if (result) *result = static_cast<int64_t>(x); src->remove_prefix(static_cast<size_t>(len)); diff --git a/Firestore/core/src/firebase/firestore/util/status.cc b/Firestore/core/src/firebase/firestore/util/status.cc index 662fa5d..46f3ce6 100644 --- a/Firestore/core/src/firebase/firestore/util/status.cc +++ b/Firestore/core/src/firebase/firestore/util/status.cc @@ -16,14 +16,14 @@ #include "Firestore/core/src/firebase/firestore/util/status.h" -#include "Firestore/core/src/firebase/firestore/util/string_printf.h" +#include "Firestore/core/src/firebase/firestore/util/string_format.h" namespace firebase { namespace firestore { namespace util { Status::Status(FirestoreErrorCode code, absl::string_view msg) { - FIREBASE_ASSERT(code != FirestoreErrorCode::Ok); + HARD_ASSERT(code != FirestoreErrorCode::Ok); state_ = std::unique_ptr<State>(new State); state_->code = code; state_->msg = static_cast<std::string>(msg); @@ -103,7 +103,7 @@ std::string Status::ToString() const { result = "Data loss"; break; default: - result = StringPrintf("Unknown code(%d)", static_cast<int>(code())); + result = StringFormat("Unknown code(%s)", code()); break; } result += ": "; @@ -117,7 +117,7 @@ void Status::IgnoreError() const { } std::string StatusCheckOpHelperOutOfLine(const Status& v, const char* msg) { - FIREBASE_ASSERT(!v.ok()); + HARD_ASSERT(!v.ok()); std::string r("Non-OK-status: "); r += msg; r += " status: "; diff --git a/Firestore/core/src/firebase/firestore/util/status.h b/Firestore/core/src/firebase/firestore/util/status.h index 13bf320..9121b36 100644 --- a/Firestore/core/src/firebase/firestore/util/status.h +++ b/Firestore/core/src/firebase/firestore/util/status.h @@ -23,7 +23,7 @@ #include <string> #include "Firestore/core/include/firebase/firestore/firestore_errors.h" -#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" #include "absl/base/attributes.h" #include "absl/strings/string_view.h" @@ -124,15 +124,8 @@ typedef std::function<void(const Status&)> StatusCallback; extern std::string StatusCheckOpHelperOutOfLine(const Status& v, const char* msg); -#define STATUS_CHECK_OK(val) \ - FIREBASE_ASSERT_MESSAGE_WITH_EXPRESSION( \ - val.ok(), val.ok(), StatusCheckOpHelperOutOfLine(val, #val).c_str()) - -// DEBUG only version of STATUS_CHECK_OK. Compiler still parses 'val' even in -// opt mode. -#define STATUS_DCHECK_OK(val) \ - FIREBASE_DEV_ASSERT_MESSAGE_WITH_EXPRESSION( \ - val.ok(), val.ok(), StatusCheckOpHelperOutOfLine(val, #val).c_str()) +#define STATUS_CHECK_OK(val) \ + HARD_ASSERT(val.ok(), "%s", StatusCheckOpHelperOutOfLine(val, #val)) } // namespace util } // namespace firestore diff --git a/Firestore/core/src/firebase/firestore/util/statusor.cc b/Firestore/core/src/firebase/firestore/util/statusor.cc index be1e03a..bbd5781 100644 --- a/Firestore/core/src/firebase/firestore/util/statusor.cc +++ b/Firestore/core/src/firebase/firestore/util/statusor.cc @@ -15,6 +15,7 @@ */ #include "Firestore/core/src/firebase/firestore/util/statusor.h" +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" namespace firebase { namespace firestore { @@ -24,15 +25,14 @@ namespace internal_statusor { void Helper::HandleInvalidStatusCtorArg(Status* status) { const char* kMessage = "An OK status is not a valid constructor argument to StatusOr<T>"; - FIREBASE_DEV_ASSERT_MESSAGE(false, kMessage); + HARD_FAIL("%s", kMessage); // Fall back to Internal for non-debug builds *status = Status(FirestoreErrorCode::Internal, kMessage); } void Helper::Crash(const Status& status) { - FIREBASE_ASSERT_MESSAGE( - false, "Attempting to fetch value instead of handling error ", - status.ToString().c_str()); + HARD_FAIL("Attempting to fetch value instead of handling error %s", + status.ToString()); } } // namespace internal_statusor diff --git a/Firestore/core/src/firebase/firestore/util/string_format.cc b/Firestore/core/src/firebase/firestore/util/string_format.cc new file mode 100644 index 0000000..bafdac2 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/util/string_format.cc @@ -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. + */ + +#include "Firestore/core/src/firebase/firestore/util/string_format.h" + +namespace firebase { +namespace firestore { +namespace util { +namespace internal { + +static const char* kMissing = "<missing>"; +static const char* kInvalid = "<invalid>"; + +std::string StringFormatPieces( + const char* format, std::initializer_list<absl::string_view> pieces) { + std::string result; + + const char* format_iter = format; + const char* format_end = format + strlen(format); + + auto pieces_iter = pieces.begin(); + auto pieces_end = pieces.end(); + auto append_next_piece = [&](std::string* dest) { + if (pieces_iter == pieces_end) { + dest->append(kMissing); + } else { + // Pass a piece through + dest->append(pieces_iter->data(), pieces_iter->size()); + ++pieces_iter; + } + }; + + auto append_specifier = [&](char spec) { + switch (spec) { + case '%': + // Pass through literal %. + result.push_back('%'); + break; + + case 's': { + append_next_piece(&result); + break; + } + + default: + result.append(kInvalid); + break; + } + }; + + // Iterate through the format string, advancing `format_iter` as we go. + while (true) { + const char* percent_ptr = std::find(format_iter, format_end, '%'); + + // percent either points to the next format specifier or the end of the + // format string. Either is safe to append here: + result.append(format_iter, percent_ptr); + if (percent_ptr == format_end) { + // No further pieces to format + break; + } + + // Examine the specifier: + const char* spec_ptr = percent_ptr + 1; + if (spec_ptr == format_end) { + // Incomplete specifier, treat as a literal "%" and be done. + append_specifier('%'); + break; + } + append_specifier(*spec_ptr); + + format_iter = spec_ptr + 1; + } + + return result; +} + +} // namespace internal +} // namespace util +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/util/string_format.h b/Firestore/core/src/firebase/firestore/util/string_format.h new file mode 100644 index 0000000..f8da785 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/util/string_format.h @@ -0,0 +1,156 @@ +/* + * 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_FORMAT_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_STRING_FORMAT_H_ + +#include <initializer_list> +#include <string> +#include <utility> + +#include "Firestore/core/src/firebase/firestore/util/string_apple.h" +#include "absl/base/attributes.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" + +namespace firebase { +namespace firestore { +namespace util { + +namespace internal { + +std::string StringFormatPieces(const char* format, + std::initializer_list<absl::string_view> pieces); + +/** + * Explicit ranking for formatting choices. Only useful as an implementation + * detail of `FormatArg`. + */ +template <int I> +struct FormatChoice : FormatChoice<I + 1> {}; + +template <> +struct FormatChoice<4> {}; + +} // namespace internal + +/** + * Acts as the main value parameter to StringFormat and related functions. + * + * Chooses a conversion to a text form in this order: + * * If the value is exactly of `bool` type (without implicit conversions) + * the text will the "true" or "false". + * * If the value is of type `const char*`, the text will be the value + * interpreted as a C string. To show the address of a single char or to + * show the `const char*` as an address, cast to `void*`. + * * If the value is any other pointer type, the text will be the hexidecimal + * formatting of the value as an unsigned integer. + * * Otherwise the value is interpreted as anything absl::AlphaNum accepts. + */ +class FormatArg : public absl::AlphaNum { + public: + template <typename T> + FormatArg(T&& value) // NOLINT(runtime/explicit) + : FormatArg{std::forward<T>(value), internal::FormatChoice<0>{}} { + } + +#if __OBJC__ + FormatArg(NSObject* object) // NOLINT(runtime/explicit) + : AlphaNum{MakeStringView([object description])} { + } +#endif + + private: + /** + * Creates a FormatArg from a boolean value, representing the string + * "true" or "false". + * + * This overload only applies if the type of the argument is exactly `bool`. + * No implicit conversions to bool are accepted. + */ + template <typename T, + typename = typename std::enable_if<std::is_same<bool, T>{}>::type> + FormatArg(T bool_value, internal::FormatChoice<0>) + : AlphaNum{bool_value ? "true" : "false"} { + } + + /** + * Creates a FormatArg from a character string literal. This is + * handled specially to avoid ambiguity with generic pointers, which are + * handled differently. + */ + FormatArg(std::nullptr_t, internal::FormatChoice<1>) : AlphaNum{"null"} { + } + + /** + * Creates a FormatArg from a character string literal. This is + * handled specially to avoid ambiguity with generic pointers, which are + * handled differently. + */ + FormatArg(const char* string_value, internal::FormatChoice<2>) + : AlphaNum{string_value == nullptr ? "null" : string_value} { + } + + /** + * Creates a FormatArg from an arbitrary pointer, represented as a + * hexidecimal integer literal. + */ + template <typename T> + FormatArg(T* pointer_value, internal::FormatChoice<3>) + : AlphaNum{absl::Hex{reinterpret_cast<uintptr_t>(pointer_value)}} { + } + + /** + * As a final fallback, creates a FormatArg from any type of value that + * absl::AlphaNum accepts. + */ + template <typename T> + FormatArg(T&& value, internal::FormatChoice<4>) + : AlphaNum{std::forward<T>(value)} { + } +}; + +/** + * Formats a string using a simplified printf-like formatting mechanism that + * does not rely on C varargs. + * + * The following format specifiers are recognized: + * * "%%" - A literal "%" + * * "%s" - The next parameter is copied through + * + * Note: + * * If you pass fewer arguments than the format requires, StringFormat will + * insert "<missing>". + * * If you pass more arguments than the format requires, any excess arguments + * are ignored. + * * If you use an invalid format specifier, StringFormat will insert + * <invalid>. + */ +template <typename... FA> +std::string StringFormat(const char* format, const FA&... args) { + return internal::StringFormatPieces( + format, {static_cast<const FormatArg&>(args).Piece()...}); +} + +inline std::string StringFormat() { + return {}; +} + +} // namespace util +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_STRING_FORMAT_H_ diff --git a/Firestore/core/src/firebase/firestore/util/string_printf.cc b/Firestore/core/src/firebase/firestore/util/string_printf.cc deleted file mode 100644 index c5483f4..0000000 --- a/Firestore/core/src/firebase/firestore/util/string_printf.cc +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2018 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Firestore/core/src/firebase/firestore/util/string_printf.h" - -#include <cstdio> - -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 deleted file mode 100644 index 553af66..0000000 --- a/Firestore/core/src/firebase/firestore/util/string_printf.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2018 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_STRING_PRINTF_H_ -#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_STRING_PRINTF_H_ - -#include <cstdarg> -#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/Example/Tests/GoogleTest/FSTGoogleTestTests.mm b/Firestore/core/test/firebase/firestore/FSTGoogleTestTests.mm index ec14880..c87949a 100644 --- a/Firestore/Example/Tests/GoogleTest/FSTGoogleTestTests.mm +++ b/Firestore/core/test/firebase/firestore/FSTGoogleTestTests.mm @@ -22,53 +22,60 @@ #include "gtest/gtest.h" /** - * An XCTest test case that finds C++ test cases written in the GoogleTest framework, runs them, and - * reports the results back to Xcode. This allows tests written in C++ that don't rely on XCTest to - * coexist in this project. + * An XCTest test case that finds C++ test cases written in the GoogleTest + * framework, runs them, and reports the results back to Xcode. This allows + * tests written in C++ that don't rely on XCTest to coexist in this project. * - * As an extra feature, you can run all C++ tests by focusing on the GoogleTests class. + * As an extra feature, you can run all C++ tests by focusing on the GoogleTests + * class. * - * Each GoogleTest TestCase is mapped to a dynamically generated XCTestCase class. Each GoogleTest - * TEST() is mapped to a test method on that XCTestCase. + * Each GoogleTest TestCase is mapped to a dynamically generated XCTestCase + * class. Each GoogleTest TEST() is mapped to a test method on that XCTestCase. */ @interface GoogleTests : XCTestCase @end namespace { -// A testing::TestCase named "Foo" corresponds to an XCTestCase named "FooTests". +// A testing::TestCase named "Foo" corresponds to an XCTestCase named +// "FooTests". NSString *const kTestCaseSuffix = @"Tests"; // A testing::TestInfo named "Foo" corresponds to test method named "testFoo". NSString *const kTestMethodPrefix = @"test"; -// A map of keys created by TestInfoKey to the corresponding testing::TestInfo (wrapped in an -// NSValue). The generated XCTestCase classes are discovered and instantiated by XCTest so this is -// the only means of plumbing per-test-method state into these methods. +// A map of keys created by TestInfoKey to the corresponding testing::TestInfo +// (wrapped in an NSValue). The generated XCTestCase classes are discovered and +// instantiated by XCTest so this is the only means of plumbing per-test-method +// state into these methods. NSDictionary<NSString *, NSValue *> *testInfosByKey; -// If the user focuses on GoogleTests itself, this means force all C++ tests to run. +// If the user focuses on GoogleTests itself, this means force all C++ tests to +// run. BOOL forceAllTests = NO; /** - * Loads this XCTest runner's configuration file and figures out which tests to run based on the - * contents of that configuration file. + * Loads this XCTest runner's configuration file and figures out which tests to + * run based on the contents of that configuration file. * - * @return the set of tests to run, or nil if the user asked for all tests or if there's any - * problem loading or parsing the configuration. + * @return the set of tests to run, or nil if the user asked for all tests or if + * there's any problem loading or parsing the configuration. */ NSSet<NSString *> *_Nullable LoadXCTestConfigurationTestsToRun() { - // Xcode invokes the test runner with an XCTestConfigurationFilePath environment variable set to - // the path of a configuration file containing, among other things, the set of tests to run. The - // configuration file deserializes to a non-public XCTestConfiguration class. + // Xcode invokes the test runner with an XCTestConfigurationFilePath + // environment variable set to the path of a configuration file containing, + // among other things, the set of tests to run. The configuration file + // deserializes to a non-public XCTestConfiguration class. // - // This loads that file and then reflectively pulls out the testsToRun set. Just in case any of - // these private details should change in the future and something should fail here, the mechanism - // complains but fails open. This way the worst that can happen is that users end up running more - // tests than they intend, but we never accidentally show a green run that wasn't. + // This loads that file and then reflectively pulls out the testsToRun set. + // Just in case any of these private details should change in the future and + // something should fail here, the mechanism complains but fails open. This + // way the worst that can happen is that users end up running more tests than + // they intend, but we never accidentally show a green run that wasn't. static NSString *const configEnvVar = @"XCTestConfigurationFilePath"; - NSDictionary<NSString *, NSString *> *env = [[NSProcessInfo processInfo] environment]; + NSDictionary<NSString *, NSString *> *env = + [[NSProcessInfo processInfo] environment]; NSString *filePath = [env objectForKey:configEnvVar]; if (!filePath) { NSLog(@"Missing %@ environment variable; assuming all tests", configEnvVar); @@ -77,27 +84,32 @@ NSSet<NSString *> *_Nullable LoadXCTestConfigurationTestsToRun() { id config = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath]; if (!config) { - NSLog(@"Failed to load any configuaration from %@=%@", configEnvVar, filePath); + NSLog(@"Failed to load any configuaration from %@=%@", configEnvVar, + filePath); return nil; } SEL testsToRunSelector = NSSelectorFromString(@"testsToRun"); if (![config respondsToSelector:testsToRunSelector]) { - NSLog(@"Invalid configuaration from %@=%@: missing testsToRun", configEnvVar, filePath); + NSLog(@"Invalid configuaration from %@=%@: missing testsToRun", + configEnvVar, filePath); return nil; } - // Invoke the testsToRun selector safely. This indirection is required because just calling - // -performSelector: fails to properly retain the NSSet under ARC. + // Invoke the testsToRun selector safely. This indirection is required because + // just calling -performSelector: fails to properly retain the NSSet under + // ARC. typedef NSSet<NSString *> *(*TestsToRunFunction)(id, SEL); IMP testsToRunMethod = [config methodForSelector:testsToRunSelector]; - auto testsToRunFunction = reinterpret_cast<TestsToRunFunction>(testsToRunMethod); + auto testsToRunFunction = + reinterpret_cast<TestsToRunFunction>(testsToRunMethod); return testsToRunFunction(config, testsToRunSelector); } /** - * Creates a GoogleTest filter specification, suitable for passing to the --gtest_filter flag, - * that limits GoogleTest to running the same set of tests that Xcode requested. + * Creates a GoogleTest filter specification, suitable for passing to the + * --gtest_filter flag, that limits GoogleTest to running the same set of tests + * that Xcode requested. * * Each member of the testsToRun set is mapped as follows: * @@ -106,7 +118,8 @@ NSSet<NSString *> *_Nullable LoadXCTestConfigurationTestsToRun() { * * These members are then joined with a ":" as googletest requires. * - * @see https://github.com/google/googletest/blob/master/googletest/docs/AdvancedGuide.md + * @see + * https://github.com/google/googletest/blob/master/googletest/docs/AdvancedGuide.md */ NSString *CreateTestFiltersFromTestsToRun(NSSet<NSString *> *testsToRun) { NSMutableString *result = [[NSMutableString alloc] init]; @@ -117,7 +130,8 @@ NSString *CreateTestFiltersFromTestsToRun(NSSet<NSString *> *testsToRun) { if (parts.count > 0) { NSString *className = parts[0]; if ([className hasSuffix:kTestCaseSuffix]) { - gtestCaseName = [className substringToIndex:className.length - kTestCaseSuffix.length]; + gtestCaseName = [className + substringToIndex:className.length - kTestCaseSuffix.length]; } } @@ -125,7 +139,8 @@ NSString *CreateTestFiltersFromTestsToRun(NSSet<NSString *> *testsToRun) { if (parts.count > 1) { NSString *methodName = parts[1]; if ([methodName hasPrefix:kTestMethodPrefix]) { - gtestMethodName = [methodName substringFromIndex:kTestMethodPrefix.length]; + gtestMethodName = + [methodName substringFromIndex:kTestMethodPrefix.length]; } } @@ -142,9 +157,11 @@ NSString *CreateTestFiltersFromTestsToRun(NSSet<NSString *> *testsToRun) { return result; } -/** Returns the name of the selector for the test method representing this specific test. */ +/** Returns the name of the selector for the test method representing this + * specific test. */ NSString *SelectorNameForTestInfo(const testing::TestInfo *testInfo) { - return [NSString stringWithFormat:@"%@%s", kTestMethodPrefix, testInfo->name()]; + return + [NSString stringWithFormat:@"%@%s", kTestMethodPrefix, testInfo->name()]; } /** Returns the name of the class representing the given testing::TestCase. */ @@ -153,21 +170,22 @@ NSString *ClassNameForTestCase(const testing::TestCase *testCase) { } /** - * Returns a key name for the testInfosByKey dictionary. Each (class, selector) pair corresponds - * to a unique GoogleTest result. + * Returns a key name for the testInfosByKey dictionary. Each (class, selector) + * pair corresponds to a unique GoogleTest result. */ NSString *TestInfoKey(Class testClass, SEL testSelector) { - return [NSString - stringWithFormat:@"%@.%@", NSStringFromClass(testClass), NSStringFromSelector(testSelector)]; + return [NSString stringWithFormat:@"%@.%@", NSStringFromClass(testClass), + NSStringFromSelector(testSelector)]; } /** - * Looks up the testing::TestInfo for this test method and reports on the outcome to XCTest, as if - * the test actually ran in this method. + * Looks up the testing::TestInfo for this test method and reports on the + * outcome to XCTest, as if the test actually ran in this method. * - * Note: this function is the implementation for each generated test method. It shouldn't be used - * directly. The parameter names of self and _cmd match up with the implicit parameters passed to - * any Objective-C method. Naming them this way here allows XCTAssert and friends to work. + * Note: this function is the implementation for each generated test method. It + * shouldn't be used directly. The parameter names of self and _cmd match up + * with the implicit parameters passed to any Objective-C method. Naming them + * this way here allows XCTAssert and friends to work. */ void ReportTestResult(XCTestCase *self, SEL _cmd) { NSString *testInfoKey = TestInfoKey([self class], _cmd); @@ -189,31 +207,38 @@ void ReportTestResult(XCTestCase *self, SEL _cmd) { return; } - // Test failed :-(. Record the failure such that XCode will navigate directly to the file:line. + // Test failed :-(. Record the failure such that XCode will navigate directly + // to the file:line. int parts = result->total_part_count(); for (int i = 0; i < parts; i++) { const testing::TestPartResult &part = result->GetTestPartResult(i); - [self recordFailureWithDescription:@(part.message()) - inFile:@(part.file_name() ? part.file_name() : "") - atLine:(part.line_number() > 0 ? part.line_number() : 0) - expected:YES]; + [self + recordFailureWithDescription:@(part.message()) + inFile:@(part.file_name() ? part.file_name() : "") + atLine:(part.line_number() > 0 + ? part.line_number() + : 0) + expected:YES]; } } /** - * Generates a new subclass of XCTestCase for the given GoogleTest TestCase. Each TestInfo (which - * represents an indivudal test method execution) is translated into a method on the test case. + * Generates a new subclass of XCTestCase for the given GoogleTest TestCase. + * Each TestInfo (which represents an indivudal test method execution) is + * translated into a method on the test case. * * @param The testing::TestCase of interest to translate. * @param A map of TestInfoKeys to testing::TestInfos, populated by this method. * - * @return A new Class that's a subclass of XCTestCase, that's been registered with the Objective-C - * runtime. + * @return A new Class that's a subclass of XCTestCase, that's been registered + * with the Objective-C runtime. */ -Class CreateXCTestCaseClass(const testing::TestCase *testCase, - NSMutableDictionary<NSString *, NSValue *> *infoMap) { +Class CreateXCTestCaseClass( + const testing::TestCase *testCase, + NSMutableDictionary<NSString *, NSValue *> *infoMap) { NSString *testCaseName = ClassNameForTestCase(testCase); - Class testClass = objc_allocateClassPair([XCTestCase class], [testCaseName UTF8String], 0); + Class testClass = + objc_allocateClassPair([XCTestCase class], [testCaseName UTF8String], 0); // Create a method for each TestInfo. int testInfos = testCase->total_test_count(); @@ -223,8 +248,9 @@ Class CreateXCTestCaseClass(const testing::TestCase *testCase, NSString *selectorName = SelectorNameForTestInfo(testInfo); SEL selector = sel_registerName([selectorName UTF8String]); - // Use the ReportTestResult function as the method implementation. The v@: indicates it is a - // void objective-C method; this must continue to match the signature of ReportTestResult. + // Use the ReportTestResult function as the method implementation. The v@: + // indicates it is a void objective-C method; this must continue to match + // the signature of ReportTestResult. IMP method = reinterpret_cast<IMP>(ReportTestResult); class_addMethod(testClass, selector, method, "v@:"); @@ -238,16 +264,20 @@ Class CreateXCTestCaseClass(const testing::TestCase *testCase, } /** - * Creates a test suite containing all C++ tests, used when the user starts the GoogleTests class. + * Creates a test suite containing all C++ tests, used when the user starts the + * GoogleTests class. * - * Note: normally XCTest finds all the XCTestCase classes that are registered with the run time - * and asks them to create suites for themselves. When a user focuses on the GoogleTests class, - * XCTest no longer does this so we have to force XCTest to see more tests than it would normally - * look at so that the indicators in the test navigator update properly. + * Note: normally XCTest finds all the XCTestCase classes that are registered + * with the run time and asks them to create suites for themselves. When a user + * focuses on the GoogleTests class, XCTest no longer does this so we have to + * force XCTest to see more tests than it would normally look at so that the + * indicators in the test navigator update properly. */ XCTestSuite *CreateAllTestsTestSuite() { - XCTestSuite *allTestsSuite = [[XCTestSuite alloc] initWithName:@"All GoogleTest Tests"]; - [allTestsSuite addTest:[XCTestSuite testSuiteForTestCaseClass:[GoogleTests class]]]; + XCTestSuite *allTestsSuite = + [[XCTestSuite alloc] initWithName:@"All GoogleTest Tests"]; + [allTestsSuite + addTest:[XCTestSuite testSuiteForTestCaseClass:[GoogleTests class]]]; const testing::UnitTest *master = testing::UnitTest::GetInstance(); @@ -263,8 +293,8 @@ XCTestSuite *CreateAllTestsTestSuite() { } /** - * Finds and runs googletest-based tests based on the XCTestConfiguration of the current test - * invocation. + * Finds and runs googletest-based tests based on the XCTestConfiguration of the + * current test invocation. */ void RunGoogleTestTests() { NSString *masterTestCaseName = NSStringFromClass([GoogleTests class]); @@ -276,8 +306,9 @@ void RunGoogleTestTests() { // Convert XCTest's testToRun set to the equivalent --gtest_filter flag. // - // Note that we only set forceAllTests to YES if the user specifically focused on GoogleTests. - // This prevents XCTest double-counting test cases (and failures) when a user asks for all tests. + // Note that we only set forceAllTests to YES if the user specifically focused + // on GoogleTests. This prevents XCTest double-counting test cases (and + // failures) when a user asks for all tests. NSSet<NSString *> *allTests = [NSSet setWithObject:masterTestCaseName]; NSSet<NSString *> *testsToRun = LoadXCTestConfigurationTestsToRun(); if (testsToRun) { @@ -307,10 +338,11 @@ void RunGoogleTestTests() { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-result" - // RUN_ALL_TESTS by default doesn't want you to ignore its result, but it's safe here. Test - // failures are already logged by GoogleTest itself (and then again by XCTest). Test failures are - // reported via -recordFailureWithDescription:inFile:atLine:expected: which then causes XCTest - // itself to fail the run. + // RUN_ALL_TESTS by default doesn't want you to ignore its result, but it's + // safe here. Test failures are already logged by GoogleTest itself (and then + // again by XCTest). Test failures are reported via + // -recordFailureWithDescription:inFile:atLine:expected: which then causes + // XCTest itself to fail the run. RUN_ALL_TESTS(); #pragma clang diagnostic pop } @@ -320,7 +352,8 @@ void RunGoogleTestTests() { @implementation GoogleTests + (XCTestSuite *)defaultTestSuite { - // Only return all tests beyond GoogleTests if the user is focusing on GoogleTests. + // Only return all tests beyond GoogleTests if the user is focusing on + // GoogleTests. if (forceAllTests) { return CreateAllTestsTestSuite(); } else { @@ -330,8 +363,8 @@ void RunGoogleTestTests() { } - (void)testGoogleTestsActuallyRun { - // This whole mechanism is sufficiently tricky that we should verify that the build actually - // plumbed this together correctly. + // This whole mechanism is sufficiently tricky that we should verify that the + // build actually plumbed this together correctly. const testing::UnitTest *master = testing::UnitTest::GetInstance(); XCTAssertGreaterThan(master->total_test_case_count(), 0); } @@ -339,9 +372,9 @@ void RunGoogleTestTests() { @end /** - * This class is registered as the NSPrincipalClass in the Firestore_Tests bundle's Info.plist. - * XCTest instantiates this class to perform one-time setup for the test bundle, as documented - * here: + * This class is registered as the NSPrincipalClass in the Firestore_Tests + * bundle's Info.plist. XCTest instantiates this class to perform one-time setup + * for the test bundle, as documented here: * * https://developer.apple.com/documentation/xctest/xctestobservationcenter */ diff --git a/Firestore/core/test/firebase/firestore/model/CMakeLists.txt b/Firestore/core/test/firebase/firestore/model/CMakeLists.txt index 9c94677..b38d658 100644 --- a/Firestore/core/test/firebase/firestore/model/CMakeLists.txt +++ b/Firestore/core/test/firebase/firestore/model/CMakeLists.txt @@ -20,14 +20,12 @@ cc_test( document_test.cc field_mask_test.cc field_path_test.cc - field_transform_test.cc field_value_test.cc maybe_document_test.cc no_document_test.cc precondition_test.cc resource_path_test.cc snapshot_version_test.cc - transform_operations_test.cc DEPENDS firebase_firestore_model ) diff --git a/Firestore/core/test/firebase/firestore/remote/serializer_test.cc b/Firestore/core/test/firebase/firestore/remote/serializer_test.cc index 38277e2..ba9ea47 100644 --- a/Firestore/core/test/firebase/firestore/remote/serializer_test.cc +++ b/Firestore/core/test/firebase/firestore/remote/serializer_test.cc @@ -33,23 +33,32 @@ #include <vector> #include "Firestore/Protos/cpp/google/firestore/v1beta1/document.pb.h" +#include "Firestore/Protos/cpp/google/firestore/v1beta1/firestore.pb.h" #include "Firestore/core/include/firebase/firestore/firestore_errors.h" #include "Firestore/core/include/firebase/firestore/timestamp.h" #include "Firestore/core/src/firebase/firestore/model/field_value.h" +#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h" #include "Firestore/core/src/firebase/firestore/timestamp_internal.h" #include "Firestore/core/src/firebase/firestore/util/status.h" #include "Firestore/core/src/firebase/firestore/util/statusor.h" #include "Firestore/core/test/firebase/firestore/testutil/testutil.h" +#include "absl/types/optional.h" #include "google/protobuf/stubs/common.h" #include "google/protobuf/util/message_differencer.h" #include "gtest/gtest.h" +namespace v1beta1 = google::firestore::v1beta1; using firebase::Timestamp; using firebase::TimestampInternal; using firebase::firestore::FirestoreErrorCode; using firebase::firestore::model::DatabaseId; +using firebase::firestore::model::Document; +using firebase::firestore::model::DocumentKey; using firebase::firestore::model::FieldValue; +using firebase::firestore::model::MaybeDocument; +using firebase::firestore::model::NoDocument; using firebase::firestore::model::ObjectValue; +using firebase::firestore::model::SnapshotVersion; using firebase::firestore::remote::Serializer; using firebase::firestore::testutil::Key; using firebase::firestore::util::Status; @@ -73,13 +82,14 @@ TEST(Serializer, CanLinkToNanopb) { class SerializerTest : public ::testing::Test { public: SerializerTest() : serializer(kDatabaseId) { + msg_diff.ReportDifferencesToString(&message_differences); } const DatabaseId kDatabaseId{"p", "d"}; Serializer serializer; void ExpectRoundTrip(const FieldValue& model, - const google::firestore::v1beta1::Value& proto, + const v1beta1::Value& proto, FieldValue::Type type) { // First, serialize model with our (nanopb based) serializer, then // deserialize the resulting bytes with libprotobuf and ensure the result is @@ -92,6 +102,21 @@ class SerializerTest : public ::testing::Test { ExpectDeserializationRoundTrip(model, proto, type); } + void ExpectRoundTrip(const DocumentKey& key, + const FieldValue& value, + const SnapshotVersion& update_time, + const v1beta1::BatchGetDocumentsResponse& proto) { + ExpectSerializationRoundTrip(key, value, update_time, proto); + ExpectDeserializationRoundTrip(key, value, update_time, proto); + } + + void ExpectNoDocumentDeserializationRoundTrip( + const DocumentKey& key, + const SnapshotVersion& read_time, + const v1beta1::BatchGetDocumentsResponse& proto) { + ExpectDeserializationRoundTrip(key, absl::nullopt, read_time, proto); + } + /** * Checks the status. Don't use directly; use one of the relevant macros * instead. eg: @@ -128,10 +153,10 @@ class SerializerTest : public ::testing::Test { EXPECT_EQ(status.code(), bad_status.status().code()); } - google::firestore::v1beta1::Value ValueProto(nullptr_t) { + v1beta1::Value ValueProto(nullptr_t) { std::vector<uint8_t> bytes = EncodeFieldValue(&serializer, FieldValue::NullValue()); - google::firestore::v1beta1::Value proto; + v1beta1::Value proto; bool ok = proto.ParseFromArray(bytes.data(), bytes.size()); EXPECT_TRUE(ok); return proto; @@ -145,6 +170,16 @@ class SerializerTest : public ::testing::Test { return bytes; } + std::vector<uint8_t> EncodeDocument(Serializer* serializer, + const DocumentKey& key, + const FieldValue& value) { + std::vector<uint8_t> bytes; + Status status = + serializer->EncodeDocument(key, value.object_value(), &bytes); + EXPECT_OK(status); + return bytes; + } + void Mutate(uint8_t* byte, uint8_t expected_initial_value, uint8_t new_value) { @@ -152,63 +187,84 @@ class SerializerTest : public ::testing::Test { *byte = new_value; } - google::firestore::v1beta1::Value ValueProto(bool b) { + v1beta1::Value ValueProto(bool b) { std::vector<uint8_t> bytes = EncodeFieldValue(&serializer, FieldValue::BooleanValue(b)); - google::firestore::v1beta1::Value proto; + v1beta1::Value proto; bool ok = proto.ParseFromArray(bytes.data(), bytes.size()); EXPECT_TRUE(ok); return proto; } - google::firestore::v1beta1::Value ValueProto(int64_t i) { + v1beta1::Value ValueProto(int64_t i) { std::vector<uint8_t> bytes = EncodeFieldValue(&serializer, FieldValue::IntegerValue(i)); - google::firestore::v1beta1::Value proto; + v1beta1::Value proto; bool ok = proto.ParseFromArray(bytes.data(), bytes.size()); EXPECT_TRUE(ok); return proto; } - google::firestore::v1beta1::Value ValueProto(const char* s) { + v1beta1::Value ValueProto(const char* s) { return ValueProto(std::string(s)); } - google::firestore::v1beta1::Value ValueProto(const std::string& s) { + v1beta1::Value ValueProto(const std::string& s) { std::vector<uint8_t> bytes = EncodeFieldValue(&serializer, FieldValue::StringValue(s)); - google::firestore::v1beta1::Value proto; + v1beta1::Value proto; bool ok = proto.ParseFromArray(bytes.data(), bytes.size()); EXPECT_TRUE(ok); return proto; } - google::firestore::v1beta1::Value ValueProto(const Timestamp& ts) { + v1beta1::Value ValueProto(const Timestamp& ts) { std::vector<uint8_t> bytes = EncodeFieldValue(&serializer, FieldValue::TimestampValue(ts)); - google::firestore::v1beta1::Value proto; + v1beta1::Value proto; bool ok = proto.ParseFromArray(bytes.data(), bytes.size()); EXPECT_TRUE(ok); return proto; } + /** + * Creates entries in the proto that we don't care about. + * + * We ignore create time in our serializer. We never set it, and never read it + * (other than to throw it away). But the server could (and probably does) set + * it, so we need to be able to discard it properly. The ExpectRoundTrip deals + * with this asymmetry. + * + * This method adds these ignored fields to the proto. + */ + void TouchIgnoredBatchGetDocumentsResponseFields( + v1beta1::BatchGetDocumentsResponse* proto) { + // TODO(rsgowman): This method currently assumes that this is a 'found' + // document. We (probably) will need to adjust this to work with NoDocuments + // too. + v1beta1::Document* doc_proto = proto->mutable_found(); + + google::protobuf::Timestamp* create_time_proto = + doc_proto->mutable_create_time(); + create_time_proto->set_seconds(8765); + create_time_proto->set_nanos(4321); + } + private: - void ExpectSerializationRoundTrip( - const FieldValue& model, - const google::firestore::v1beta1::Value& proto, - FieldValue::Type type) { + void ExpectSerializationRoundTrip(const FieldValue& model, + const v1beta1::Value& proto, + FieldValue::Type type) { EXPECT_EQ(type, model.type()); std::vector<uint8_t> bytes = EncodeFieldValue(&serializer, model); - google::firestore::v1beta1::Value actual_proto; + v1beta1::Value actual_proto; bool ok = actual_proto.ParseFromArray(bytes.data(), bytes.size()); EXPECT_TRUE(ok); - EXPECT_TRUE(MessageDifferencer::Equals(proto, actual_proto)); + EXPECT_TRUE(msg_diff.Compare(proto, actual_proto)) << message_differences; } - void ExpectDeserializationRoundTrip( - const FieldValue& model, - const google::firestore::v1beta1::Value& proto, - FieldValue::Type type) { + void ExpectDeserializationRoundTrip(const FieldValue& model, + const v1beta1::Value& proto, + FieldValue::Type type) { size_t size = proto.ByteSizeLong(); std::vector<uint8_t> bytes(size); bool status = proto.SerializeToArray(bytes.data(), size); @@ -220,6 +276,72 @@ class SerializerTest : public ::testing::Test { EXPECT_EQ(type, actual_model.type()); EXPECT_EQ(model, actual_model); } + + void ExpectSerializationRoundTrip( + const DocumentKey& key, + const FieldValue& value, + const SnapshotVersion& update_time, + const v1beta1::BatchGetDocumentsResponse& proto) { + std::vector<uint8_t> bytes = EncodeDocument(&serializer, key, value); + v1beta1::Document actual_proto; + bool ok = actual_proto.ParseFromArray(bytes.data(), bytes.size()); + EXPECT_TRUE(ok); + + // Note that the client can only serialize Documents (and cannot serialize + // NoDocuments) + EXPECT_TRUE(proto.has_found()); + + // Slight weirdness: When we *encode* a document for sending it to the + // backend, we don't encode the update_time (or create_time). But when we + // *decode* a document, we *do* decode the update_time (though we still + // ignore the create_time). Therefore, we'll verify the update_time + // independently, and then strip it out before comparing the rest. + EXPECT_FALSE(actual_proto.has_create_time()); + EXPECT_EQ(update_time.timestamp().seconds(), + proto.found().update_time().seconds()); + EXPECT_EQ(update_time.timestamp().nanoseconds(), + proto.found().update_time().nanos()); + v1beta1::BatchGetDocumentsResponse proto_copy{proto}; + proto_copy.mutable_found()->clear_update_time(); + proto_copy.mutable_found()->clear_create_time(); + + EXPECT_TRUE(msg_diff.Compare(proto_copy.found(), actual_proto)) + << message_differences; + } + + void ExpectDeserializationRoundTrip( + const DocumentKey& key, + const absl::optional<FieldValue> value, + const SnapshotVersion& version, // either update_time or read_time + const v1beta1::BatchGetDocumentsResponse& proto) { + size_t size = proto.ByteSizeLong(); + std::vector<uint8_t> bytes(size); + bool status = proto.SerializeToArray(bytes.data(), size); + EXPECT_TRUE(status); + StatusOr<std::unique_ptr<MaybeDocument>> actual_model_status = + serializer.DecodeMaybeDocument(bytes); + EXPECT_OK(actual_model_status); + std::unique_ptr<MaybeDocument> actual_model = + std::move(actual_model_status).ValueOrDie(); + + EXPECT_EQ(key, actual_model->key()); + EXPECT_EQ(version, actual_model->version()); + switch (actual_model->type()) { + case MaybeDocument::Type::Document: { + Document* actual_doc_model = static_cast<Document*>(actual_model.get()); + EXPECT_EQ(value, actual_doc_model->data()); + break; + } + case MaybeDocument::Type::NoDocument: + EXPECT_FALSE(value.has_value()); + break; + case MaybeDocument::Type::Unknown: + FAIL() << "We somehow created an invalid model object"; + } + } + + std::string message_differences; + MessageDifferencer msg_diff; }; TEST_F(SerializerTest, EncodesNull) { @@ -292,7 +414,7 @@ TEST_F(SerializerTest, EncodesTimestamps) { TEST_F(SerializerTest, EncodesEmptyMap) { FieldValue model = FieldValue::ObjectValueFromMap({}); - google::firestore::v1beta1::Value proto; + v1beta1::Value proto; proto.mutable_map_value(); ExpectRoundTrip(model, proto, FieldValue::Type::Object); @@ -320,20 +442,20 @@ TEST_F(SerializerTest, EncodesNestedObjects) { })}, }); - google::firestore::v1beta1::Value inner_proto; - google::protobuf::Map<std::string, google::firestore::v1beta1::Value>* - inner_fields = inner_proto.mutable_map_value()->mutable_fields(); + v1beta1::Value inner_proto; + google::protobuf::Map<std::string, v1beta1::Value>* inner_fields = + inner_proto.mutable_map_value()->mutable_fields(); (*inner_fields)["e"] = ValueProto(std::numeric_limits<int64_t>::max()); - google::firestore::v1beta1::Value middle_proto; - google::protobuf::Map<std::string, google::firestore::v1beta1::Value>* - middle_fields = middle_proto.mutable_map_value()->mutable_fields(); + v1beta1::Value middle_proto; + google::protobuf::Map<std::string, v1beta1::Value>* middle_fields = + middle_proto.mutable_map_value()->mutable_fields(); (*middle_fields)["d"] = ValueProto(int64_t{100}); (*middle_fields)["nested"] = inner_proto; - google::firestore::v1beta1::Value proto; - google::protobuf::Map<std::string, google::firestore::v1beta1::Value>* - fields = proto.mutable_map_value()->mutable_fields(); + v1beta1::Value proto; + google::protobuf::Map<std::string, v1beta1::Value>* fields = + proto.mutable_map_value()->mutable_fields(); (*fields)["b"] = ValueProto(true); (*fields)["i"] = ValueProto(int64_t{1}); (*fields)["n"] = ValueProto(nullptr); @@ -422,7 +544,7 @@ TEST_F(SerializerTest, BadTag) { std::vector<uint8_t> bytes = EncodeFieldValue(&serializer, FieldValue::NullValue()); - // The google::firestore::v1beta1::Value value_type oneof currently has tags + // The v1beta1::Value value_type oneof currently has tags // up to 18. For this test, we'll pick a tag that's unlikely to be added in // the near term but still fits within a uint8_t even when encoded. // Specifically 31. 0xf8 represents field number 31 encoded as a varint. @@ -517,5 +639,79 @@ TEST_F(SerializerTest, BadKey) { } } +TEST_F(SerializerTest, EncodesEmptyDocument) { + DocumentKey key = DocumentKey::FromPathString("path/to/the/doc"); + FieldValue empty_value = FieldValue::ObjectValueFromMap({}); + SnapshotVersion update_time = SnapshotVersion{{1234, 5678}}; + + v1beta1::BatchGetDocumentsResponse proto; + v1beta1::Document* doc_proto = proto.mutable_found(); + doc_proto->set_name(serializer.EncodeKey(key)); + doc_proto->mutable_fields(); + + google::protobuf::Timestamp* update_time_proto = + doc_proto->mutable_update_time(); + update_time_proto->set_seconds(1234); + update_time_proto->set_nanos(5678); + + TouchIgnoredBatchGetDocumentsResponseFields(&proto); + + ExpectRoundTrip(key, empty_value, update_time, proto); +} + +TEST_F(SerializerTest, EncodesNonEmptyDocument) { + DocumentKey key = DocumentKey::FromPathString("path/to/the/doc"); + FieldValue fields = FieldValue::ObjectValueFromMap({ + {"foo", FieldValue::StringValue("bar")}, + {"two", FieldValue::IntegerValue(2)}, + {"nested", FieldValue::ObjectValueFromMap({ + {"fourty-two", FieldValue::IntegerValue(42)}, + })}, + }); + SnapshotVersion update_time = SnapshotVersion{{1234, 5678}}; + + v1beta1::Value inner_proto; + google::protobuf::Map<std::string, v1beta1::Value>& inner_fields = + *inner_proto.mutable_map_value()->mutable_fields(); + inner_fields["fourty-two"] = ValueProto(int64_t{42}); + + v1beta1::BatchGetDocumentsResponse proto; + v1beta1::Document* doc_proto = proto.mutable_found(); + doc_proto->set_name(serializer.EncodeKey(key)); + google::protobuf::Map<std::string, v1beta1::Value>& m = + *doc_proto->mutable_fields(); + m["foo"] = ValueProto("bar"); + m["two"] = ValueProto(int64_t{2}); + m["nested"] = inner_proto; + + google::protobuf::Timestamp* update_time_proto = + doc_proto->mutable_update_time(); + update_time_proto->set_seconds(1234); + update_time_proto->set_nanos(5678); + + TouchIgnoredBatchGetDocumentsResponseFields(&proto); + + ExpectRoundTrip(key, fields, update_time, proto); +} + +TEST_F(SerializerTest, DecodesNoDocument) { + // We can't actually *encode* a NoDocument; the method exposed by the + // serializer requires both the document key and contents (as an ObjectValue, + // i.e. map.) The contents can be empty, but not missing. As a result, this + // test will only verify the ability to decode a NoDocument. + + DocumentKey key = DocumentKey::FromPathString("path/to/the/doc"); + SnapshotVersion read_time = + SnapshotVersion{{/*seconds=*/1234, /*nanoseconds=*/5678}}; + + v1beta1::BatchGetDocumentsResponse proto; + proto.set_missing(serializer.EncodeKey(key)); + google::protobuf::Timestamp* read_time_proto = proto.mutable_read_time(); + read_time_proto->set_seconds(read_time.timestamp().seconds()); + read_time_proto->set_nanos(read_time.timestamp().nanoseconds()); + + ExpectNoDocumentDeserializationRoundTrip(key, read_time, proto); +} + // TODO(rsgowman): Test [en|de]coding multiple protos into the same output // vector. diff --git a/Firestore/core/test/firebase/firestore/util/CMakeLists.txt b/Firestore/core/test/firebase/firestore/util/CMakeLists.txt index ea80ea2..bcb1c84 100644 --- a/Firestore/core/test/firebase/firestore/util/CMakeLists.txt +++ b/Firestore/core/test/firebase/firestore/util/CMakeLists.txt @@ -23,7 +23,7 @@ if(APPLE) cc_test( firebase_firestore_util_log_apple_test SOURCES - assert_test.cc + hard_assert_test.cc log_test.cc DEPENDS firebase_firestore_util_log_apple @@ -33,7 +33,7 @@ endif(APPLE) cc_test( firebase_firestore_util_log_stdio_test SOURCES - assert_test.cc + hard_assert_test.cc log_test.cc DEPENDS firebase_firestore_util_log_stdio @@ -129,7 +129,7 @@ cc_test( status_test.cc status_test_util.h statusor_test.cc - string_printf_test.cc + string_format_test.cc string_util_test.cc DEPENDS absl_base diff --git a/Firestore/core/test/firebase/firestore/util/async_queue_libdispatch_test.mm b/Firestore/core/test/firebase/firestore/util/async_queue_libdispatch_test.mm index 5452266..f1ff394 100644 --- a/Firestore/core/test/firebase/firestore/util/async_queue_libdispatch_test.mm +++ b/Firestore/core/test/firebase/firestore/util/async_queue_libdispatch_test.mm @@ -77,7 +77,7 @@ TEST_F(AsyncQueueTestLibdispatchOnly, } TEST_F(AsyncQueueTestLibdispatchOnly, - VerifyIsCurrentQueueRequiresBeingCalledAsync) { + VerifyIsCurrentQueueRequiresBeingCalledOnTheQueue) { ASSERT_NE(underlying_queue, dispatch_get_main_queue()); EXPECT_ANY_THROW(queue.VerifyIsCurrentQueue()); } diff --git a/Firestore/core/test/firebase/firestore/util/async_queue_test.cc b/Firestore/core/test/firebase/firestore/util/async_queue_test.cc index bcee2e3..4247584 100644 --- a/Firestore/core/test/firebase/firestore/util/async_queue_test.cc +++ b/Firestore/core/test/firebase/firestore/util/async_queue_test.cc @@ -83,16 +83,20 @@ TEST_P(AsyncQueueTest, VerifyIsCurrentQueueWorksWithOperationInProgress) { queue.EnqueueBlocking([&] { EXPECT_NO_THROW(queue.VerifyIsCurrentQueue()); }); } +// TODO(varconst): this test is inherently flaky because it can't be guaranteed +// that the enqueued asynchronous operation didn't finish before the code has +// a chance to even enqueue the next operation. Delays are chosen so that the +// test is unlikely to fail in practice. Need to revisit this. TEST_P(AsyncQueueTest, CanScheduleOperationsInTheFuture) { std::string steps; queue.Enqueue([&steps] { steps += '1'; }); queue.Enqueue([&] { - queue.EnqueueAfterDelay(AsyncQueue::Milliseconds(5), kTimerId1, [&] { + queue.EnqueueAfterDelay(AsyncQueue::Milliseconds(20), kTimerId1, [&] { steps += '4'; signal_finished(); }); - queue.EnqueueAfterDelay(AsyncQueue::Milliseconds(1), kTimerId2, + queue.EnqueueAfterDelay(AsyncQueue::Milliseconds(10), kTimerId2, [&steps] { steps += '3'; }); queue.EnqueueRelaxed([&steps] { steps += '2'; }); }); diff --git a/Firestore/core/test/firebase/firestore/util/comparison_test.cc b/Firestore/core/test/firebase/firestore/util/comparison_test.cc index a03aec8..317a830 100644 --- a/Firestore/core/test/firebase/firestore/util/comparison_test.cc +++ b/Firestore/core/test/firebase/firestore/util/comparison_test.cc @@ -16,11 +16,10 @@ #include "Firestore/core/src/firebase/firestore/util/comparison.h" -#include <cinttypes> #include <cmath> #include <limits> -#include "Firestore/core/src/firebase/firestore/util/string_printf.h" +#include "Firestore/core/src/firebase/firestore/util/string_format.h" #include "gtest/gtest.h" namespace firebase { @@ -84,47 +83,47 @@ TEST(Comparison, DoubleCompare) { 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; \ - } \ +#define ASSERT_BIT_EQUALS(expected, actual) \ + do { \ + uint64_t expectedBits = DoubleBits(expected); \ + uint64_t actualBits = DoubleBits(actual); \ + if (expectedBits != actualBits) { \ + std::string message = StringFormat( \ + "Expected <%s> to compare equal to <%s> " \ + "with bits <%s> equal to <%s>", \ + 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; \ - } \ +#define ASSERT_MIXED_SAME(doubleValue, longValue) \ + do { \ + ComparisonResult result = CompareMixedNumber(doubleValue, longValue); \ + if (result != ComparisonResult::Same) { \ + std::string message = StringFormat( \ + "Expected <%s> to compare equal to <%s>", 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; \ - } \ +#define ASSERT_MIXED_DESCENDING(doubleValue, longValue) \ + do { \ + ComparisonResult result = CompareMixedNumber(doubleValue, longValue); \ + if (result != ComparisonResult::Descending) { \ + std::string message = StringFormat( \ + "Expected <%s> to compare equal to <%s>", 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; \ - } \ +#define ASSERT_MIXED_ASCENDING(doubleValue, longValue) \ + do { \ + ComparisonResult result = CompareMixedNumber(doubleValue, longValue); \ + if (result != ComparisonResult::Ascending) { \ + std::string message = StringFormat( \ + "Expected <%s> to compare equal to <%s>", doubleValue, longValue); \ + FAIL() << message; \ + } \ } while (0); TEST(Comparison, MixedNumberCompare) { diff --git a/Firestore/core/test/firebase/firestore/util/executor_libdispatch_test.mm b/Firestore/core/test/firebase/firestore/util/executor_libdispatch_test.mm index 0167c83..330c8fc 100644 --- a/Firestore/core/test/firebase/firestore/util/executor_libdispatch_test.mm +++ b/Firestore/core/test/firebase/firestore/util/executor_libdispatch_test.mm @@ -29,7 +29,8 @@ namespace util { namespace { std::unique_ptr<internal::Executor> ExecutorFactory() { - return absl::make_unique<internal::ExecutorLibdispatch>(); + return absl::make_unique<internal::ExecutorLibdispatch>( + dispatch_queue_create("ExecutorLibdispatchTests", DISPATCH_QUEUE_SERIAL)); } } // namespace @@ -38,6 +39,36 @@ INSTANTIATE_TEST_CASE_P(ExecutorTestLibdispatch, ExecutorTest, ::testing::Values(ExecutorFactory)); +namespace internal { +class ExecutorLibdispatchOnlyTests : public TestWithTimeoutMixin, + public ::testing::Test { + public: + ExecutorLibdispatchOnlyTests() : executor{ExecutorFactory()} { + } + + std::unique_ptr<Executor> executor; +}; + +TEST_F(ExecutorLibdispatchOnlyTests, NameReturnsLabelOfTheQueue) { + EXPECT_EQ(executor->Name(), "ExecutorLibdispatchTests"); + executor->Execute([&] { + EXPECT_EQ(executor->CurrentExecutorName(), "ExecutorLibdispatchTests"); + signal_finished(); + }); + EXPECT_TRUE(WaitForTestToFinish()); +} + +TEST_F(ExecutorLibdispatchOnlyTests, + ExecuteBlockingOnTheCurrentQueueIsNotAllowed) { + EXPECT_NO_THROW(executor->ExecuteBlocking([] {})); + executor->Execute([&] { + EXPECT_ANY_THROW(executor->ExecuteBlocking([] {})); + signal_finished(); + }); + EXPECT_TRUE(WaitForTestToFinish()); +} + +} // namespace internal } // namespace util } // namespace firestore } // namespace firebase diff --git a/Firestore/core/test/firebase/firestore/util/executor_std_test.cc b/Firestore/core/test/firebase/firestore/util/executor_std_test.cc index 43cad60..59c3c32 100644 --- a/Firestore/core/test/firebase/firestore/util/executor_std_test.cc +++ b/Firestore/core/test/firebase/firestore/util/executor_std_test.cc @@ -70,7 +70,6 @@ TEST_F(ScheduleTest, PopIfDue_Delayed) { schedule.Push(2, start_time + chr::milliseconds(3)); schedule.Push(3, start_time + chr::milliseconds(1)); - EXPECT_FALSE(schedule.PopIfDue().has_value()); std::this_thread::sleep_for(chr::milliseconds(5)); EXPECT_EQ(schedule.PopIfDue().value(), 3); @@ -212,11 +211,16 @@ TEST_F(ScheduleTest, PopBlockingIsNotAffectedByIrrelevantRemovals) { EXPECT_EQ(schedule.PopBlocking(), 1); }); - while (schedule.empty()) { + // Wait (with timeout) for both values to appear in the schedule. + while (schedule.size() != 2) { + if (now() - start_time >= kTimeout) { + Abort(); + } std::this_thread::sleep_for(chr::milliseconds(1)); } const auto maybe_removed = schedule.RemoveIf([](const int v) { return v == 2; }); + ASSERT_TRUE(maybe_removed.has_value()); EXPECT_EQ(maybe_removed.value(), 2); ABORT_ON_TIMEOUT(future); } diff --git a/Firestore/core/test/firebase/firestore/util/executor_test.cc b/Firestore/core/test/firebase/firestore/util/executor_test.cc index 5cf389b..99bddce 100644 --- a/Firestore/core/test/firebase/firestore/util/executor_test.cc +++ b/Firestore/core/test/firebase/firestore/util/executor_test.cc @@ -49,6 +49,12 @@ TEST_P(ExecutorTest, Execute) { EXPECT_TRUE(WaitForTestToFinish()); } +TEST_P(ExecutorTest, ExecuteBlocking) { + bool finished = false; + executor->ExecuteBlocking([&] { finished = true; }); + EXPECT_TRUE(finished); +} + TEST_P(ExecutorTest, DestructorDoesNotBlockIfThereArePendingTasks) { const auto future = std::async(std::launch::async, [&] { auto another_executor = GetParam()(); @@ -60,15 +66,19 @@ TEST_P(ExecutorTest, DestructorDoesNotBlockIfThereArePendingTasks) { ABORT_ON_TIMEOUT(future); } +// TODO(varconst): this test is inherently flaky because it can't be guaranteed +// that the enqueued asynchronous operation didn't finish before the code has +// a chance to even enqueue the next operation. Delays are chosen so that the +// test is unlikely to fail in practice. Need to revisit this. TEST_P(ExecutorTest, CanScheduleOperationsInTheFuture) { std::string steps; executor->Execute([&steps] { steps += '1'; }); - Schedule(executor.get(), Executor::Milliseconds(5), [&] { + Schedule(executor.get(), Executor::Milliseconds(20), [&] { steps += '4'; signal_finished(); }); - Schedule(executor.get(), Executor::Milliseconds(1), + Schedule(executor.get(), Executor::Milliseconds(10), [&steps] { steps += '3'; }); executor->Execute([&steps] { steps += '2'; }); @@ -105,6 +115,104 @@ TEST_P(ExecutorTest, DelayedOperationIsValidAfterTheOperationHasRun) { EXPECT_NO_THROW(delayed_operation.Cancel()); } +TEST_P(ExecutorTest, IsCurrentExecutor) { + EXPECT_FALSE(executor->IsCurrentExecutor()); + EXPECT_NE(executor->Name(), executor->CurrentExecutorName()); + + executor->ExecuteBlocking([&] { + EXPECT_TRUE(executor->IsCurrentExecutor()); + EXPECT_EQ(executor->Name(), executor->CurrentExecutorName()); + }); + + executor->Execute([&] { + EXPECT_TRUE(executor->IsCurrentExecutor()); + EXPECT_EQ(executor->Name(), executor->CurrentExecutorName()); + }); + + Schedule(executor.get(), Executor::Milliseconds(1), [&] { + EXPECT_TRUE(executor->IsCurrentExecutor()); + EXPECT_EQ(executor->Name(), executor->CurrentExecutorName()); + signal_finished(); + }); + + EXPECT_TRUE(WaitForTestToFinish()); +} + +TEST_P(ExecutorTest, OperationsCanBeRemovedFromScheduleBeforeTheyRun) { + const Executor::Tag tag_foo = 1; + const Executor::Tag tag_bar = 2; + + // Make sure the schedule is empty. + EXPECT_FALSE(executor->IsScheduled(tag_foo)); + EXPECT_FALSE(executor->IsScheduled(tag_bar)); + EXPECT_FALSE(executor->PopFromSchedule().has_value()); + + // Add two operations to the schedule with different tags. + + // The exact delay doesn't matter as long as it's too far away to be executed + // during the test. + const auto far_away = chr::seconds(1); + executor->Schedule(far_away, {tag_foo, [] {}}); + // Scheduled operations can be distinguished by their tag. + EXPECT_TRUE(executor->IsScheduled(tag_foo)); + EXPECT_FALSE(executor->IsScheduled(tag_bar)); + + // This operation will be scheduled after the previous one (operations + // scheduled with the same delay are FIFO ordered). + executor->Schedule(far_away, {tag_bar, [] {}}); + EXPECT_TRUE(executor->IsScheduled(tag_foo)); + EXPECT_TRUE(executor->IsScheduled(tag_bar)); + + // Now pop the operations one by one without waiting for them to be executed, + // check that operations are popped in the order they are scheduled and + // preserve tags. Schedule should become empty as a result. + + auto maybe_operation = executor->PopFromSchedule(); + ASSERT_TRUE(maybe_operation.has_value()); + EXPECT_EQ(maybe_operation->tag, tag_foo); + EXPECT_FALSE(executor->IsScheduled(tag_foo)); + EXPECT_TRUE(executor->IsScheduled(tag_bar)); + + maybe_operation = executor->PopFromSchedule(); + ASSERT_TRUE(maybe_operation.has_value()); + EXPECT_EQ(maybe_operation->tag, tag_bar); + EXPECT_FALSE(executor->IsScheduled(tag_bar)); + + // Schedule should now be empty. + EXPECT_FALSE(executor->PopFromSchedule().has_value()); +} + +TEST_P(ExecutorTest, DuplicateTagsOnOperationsAreAllowed) { + const Executor::Tag tag_foo = 1; + std::string steps; + + // Add two operations with the same tag to the schedule to verify that + // duplicate tags are allowed. + + const auto far_away = chr::seconds(1); + executor->Schedule(far_away, {tag_foo, [&steps] { steps += '1'; }}); + executor->Schedule(far_away, {tag_foo, [&steps] { steps += '2'; }}); + EXPECT_TRUE(executor->IsScheduled(tag_foo)); + + auto maybe_operation = executor->PopFromSchedule(); + ASSERT_TRUE(maybe_operation.has_value()); + EXPECT_EQ(maybe_operation->tag, tag_foo); + // There's still another operation with the same tag in the schedule. + EXPECT_TRUE(executor->IsScheduled(tag_foo)); + + maybe_operation->operation(); + + maybe_operation = executor->PopFromSchedule(); + ASSERT_TRUE(maybe_operation.has_value()); + EXPECT_EQ(maybe_operation->tag, tag_foo); + EXPECT_FALSE(executor->IsScheduled(tag_foo)); + + maybe_operation->operation(); + // Despite having the same tag, the operations should have been ordered + // according to their scheduled time and preserved their identity. + EXPECT_EQ(steps, "12"); +} + } // namespace util } // namespace firestore } // namespace firebase diff --git a/Firestore/core/test/firebase/firestore/util/assert_test.cc b/Firestore/core/test/firebase/firestore/util/hard_assert_test.cc index fb15e61..fab6475 100644 --- a/Firestore/core/test/firebase/firestore/util/assert_test.cc +++ b/Firestore/core/test/firebase/firestore/util/hard_assert_test.cc @@ -16,7 +16,7 @@ #include <exception> -#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" +#include "Firestore/core/src/firebase/firestore/util/hard_assert.h" #include "gtest/gtest.h" namespace firebase { @@ -25,37 +25,26 @@ namespace util { namespace { -void AssertWithExpression(bool condition) { - FIREBASE_ASSERT_WITH_EXPRESSION(condition, 1 + 2 + 3); -} - void Assert(bool condition) { - FIREBASE_ASSERT(condition == true); + HARD_ASSERT(condition == true); } -void AssertMessageWithExpression(bool condition) { - FIREBASE_ASSERT_MESSAGE_WITH_EXPRESSION(condition, 1 + 2 + 3, "connection %s", - condition ? "succeeded" : "failed"); +void AssertWithMessage(bool condition) { + HARD_ASSERT(condition, "condition %s", condition ? "succeeded" : "failed"); } } // namespace -TEST(Assert, WithExpression) { - AssertWithExpression(true); - - EXPECT_ANY_THROW(AssertWithExpression(false)); -} - -TEST(Assert, Vanilla) { +TEST(HardAssertTest, Vanilla) { Assert(true); EXPECT_ANY_THROW(Assert(false)); } -TEST(Assert, WithMessageAndExpression) { - AssertMessageWithExpression(true); +TEST(HardAssertTest, WithMessage) { + AssertWithMessage(true); - EXPECT_ANY_THROW(AssertMessageWithExpression(false)); + EXPECT_ANY_THROW(AssertWithMessage(false)); } } // namespace util diff --git a/Firestore/core/test/firebase/firestore/util/log_test.cc b/Firestore/core/test/firebase/firestore/util/log_test.cc index 973b174..73050ea 100644 --- a/Firestore/core/test/firebase/firestore/util/log_test.cc +++ b/Firestore/core/test/firebase/firestore/util/log_test.cc @@ -33,27 +33,19 @@ namespace util { // defaults write firebase_firestore_util_log_apple_test // /google/firebase/debug_mode NO TEST(Log, SetAndGet) { - LogSetLevel(kLogLevelVerbose); + EXPECT_FALSE(LogIsDebugEnabled()); LogSetLevel(kLogLevelDebug); - EXPECT_EQ(kLogLevelDebug, LogGetLevel()); - - LogSetLevel(kLogLevelInfo); - EXPECT_EQ(kLogLevelInfo, LogGetLevel()); + EXPECT_TRUE(LogIsDebugEnabled()); LogSetLevel(kLogLevelWarning); - EXPECT_EQ(kLogLevelWarning, LogGetLevel()); - - LogSetLevel(kLogLevelError); - EXPECT_EQ(kLogLevelError, LogGetLevel()); + EXPECT_FALSE(LogIsDebugEnabled()); } 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); + LOG_DEBUG("test debug logging %s", 1); + LOG_WARN("test warning logging %s", 3); + LOG_DEBUG("test va-args %s %s %s", "abc", std::string{"def"}, 123); } } // namespace util diff --git a/Firestore/core/test/firebase/firestore/util/string_format_test.cc b/Firestore/core/test/firebase/firestore/util/string_format_test.cc new file mode 100644 index 0000000..f0ec20d --- /dev/null +++ b/Firestore/core/test/firebase/firestore/util/string_format_test.cc @@ -0,0 +1,110 @@ +/* + * Copyright 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Firestore/core/src/firebase/firestore/util/string_format.h" + +#include "absl/strings/string_view.h" +#include "gtest/gtest.h" + +namespace firebase { +namespace firestore { +namespace util { + +TEST(StringFormatTest, Empty) { + EXPECT_EQ("", StringFormat("")); + EXPECT_EQ("", StringFormat("%s", std::string().c_str())); + EXPECT_EQ("", StringFormat("%s", "")); +} + +TEST(StringFormatTest, CString) { + EXPECT_EQ("Hello World", StringFormat("Hello %s", "World")); + EXPECT_EQ("Hello World", StringFormat("%s World", "Hello")); + EXPECT_EQ("Hello World", StringFormat("Hello%sWorld", " ")); + + const char* value = "World"; + EXPECT_EQ("Hello World", StringFormat("Hello %s", value)); + + value = nullptr; + EXPECT_EQ("Hello null", StringFormat("Hello %s", value)); +} + +TEST(StringFormatTest, String) { + EXPECT_EQ("Hello World", StringFormat("Hello %s", std::string{"World"})); + + std::string value{"World"}; + EXPECT_EQ("Hello World", StringFormat("Hello %s", value)); +} + +TEST(StringFormatTest, StringView) { + EXPECT_EQ("Hello World", + StringFormat("Hello %s", absl::string_view{"World"})); + EXPECT_EQ("Hello World", + StringFormat("%s World", absl::string_view{"Hello"})); + EXPECT_EQ("Hello World", + StringFormat("Hello%sWorld", absl::string_view{" "})); +} + +TEST(StringFormatTest, Int) { + std::string value = StringFormat("Hello %s", 123); + EXPECT_EQ("Hello 123", value); +} + +TEST(StringFormatTest, Float) { + std::string value = StringFormat("Hello %s", 1.5); + EXPECT_EQ("Hello 1.5", value); +} + +TEST(StringFormatTest, Bool) { + EXPECT_EQ("Hello true", StringFormat("Hello %s", true)); + EXPECT_EQ("Hello false", StringFormat("Hello %s", false)); +} + +TEST(StringFormatTest, Pointer) { + // pointers implicitly convert to bool. Make sure this doesn't happen in + // this API. + int value = 4; + EXPECT_NE("Hello true", StringFormat("Hello %s", &value)); + EXPECT_EQ("Hello null", StringFormat("Hello %s", nullptr)); +} + +TEST(StringFormatTest, Mixed) { + EXPECT_EQ("string=World, bool=true, int=42, float=1.5", + StringFormat("string=%s, bool=%s, int=%s, float=%s", "World", true, + 42, 1.5)); + EXPECT_EQ("World%true%42%1.5", + StringFormat("%s%%%s%%%s%%%s", "World", true, 42, 1.5)); +} + +TEST(StringFormatTest, Literal) { + EXPECT_EQ("Hello %", StringFormat("Hello %%")); + EXPECT_EQ("% World", StringFormat("%% World")); +} + +TEST(StringFormatTest, Invalid) { + EXPECT_EQ("Hello <invalid>", StringFormat("Hello %@", 42)); +} + +TEST(StringFormatTest, Missing) { + EXPECT_EQ("Hello <missing>", StringFormat("Hello %s")); +} + +TEST(StringFormatTest, Excess) { + EXPECT_EQ("Hello World", StringFormat("Hello %s", "World", 42)); +} + +} // namespace util +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/test/firebase/firestore/util/string_printf_test.cc b/Firestore/core/test/firebase/firestore/util/string_printf_test.cc deleted file mode 100644 index 14cc9c8..0000000 --- a/Firestore/core/test/firebase/firestore/util/string_printf_test.cc +++ /dev/null @@ -1,78 +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. - */ - -#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 |