diff --git a/Firestore/Example/Firestore.xcodeproj/project.pbxproj b/Firestore/Example/Firestore.xcodeproj/project.pbxproj index 85058967eb0..27fc04c3c40 100644 --- a/Firestore/Example/Firestore.xcodeproj/project.pbxproj +++ b/Firestore/Example/Firestore.xcodeproj/project.pbxproj @@ -245,7 +245,6 @@ 264AAB492E24318C5EEB0649 /* memory_lru_garbage_collector_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9765D47FA12FA283F4EFAD02 /* memory_lru_garbage_collector_test.cc */; }; 26777815544F549DD18D87AF /* message_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = CE37875365497FFA8687B745 /* message_test.cc */; }; 268FC3360157A2DCAF89F92D /* snapshot_version_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = ABA495B9202B7E79008A7851 /* snapshot_version_test.cc */; }; - 2695C8DE8DCAD5D6E9C9407C /* md5_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4007021A5284243AAE77292A /* md5_testing.cc */; }; 26B52236C9D049847042E1BD /* FSTMockDatastore.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E02D20213FFC00B64F25 /* FSTMockDatastore.mm */; }; 26B6F5D7571279F4FC06581A /* leveldb_query_engine_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = DB1F1E1B1ED15E8D042144B1 /* leveldb_query_engine_test.cc */; }; 26C4E52128C8E7B5B96BECC4 /* defer_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8ABAC2E0402213D837F73DC3 /* defer_test.cc */; }; @@ -379,6 +378,7 @@ 3FFFC1FE083D8BE9C4D9A148 /* string_util_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380CFC201A2EE200D97691 /* string_util_test.cc */; }; 40431BF2A368D0C891229F6E /* FSTMemorySpecTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E02F20213FFC00B64F25 /* FSTMemorySpecTests.mm */; }; 409C0F2BFC2E1BECFFAC4D32 /* testutil.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352820A3B3BD003E0143 /* testutil.cc */; }; + 416B653E930A609FA121C5E3 /* md5_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = 651321349CA7230B0E4377DA /* md5_testing.cc */; }; 4173B61CB74EB4CD1D89EE68 /* latlng.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE9220B89AAC00B5BCE7 /* latlng.pb.cc */; }; 4194B7BB8B0352E1AC5D69B9 /* precondition_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA5520A36E1F00BCEB75 /* precondition_test.cc */; }; 41EAC526C543064B8F3F7EDA /* field_path_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B686F2AD2023DDB20028D6BE /* field_path_test.cc */; }; @@ -497,6 +497,7 @@ 544129DD21C2DDC800EFB9CC /* document.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D821C2DDC800EFB9CC /* document.pb.cc */; }; 544129DE21C2DDC800EFB9CC /* write.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D921C2DDC800EFB9CC /* write.pb.cc */; }; 54511E8E209805F8005BD28F /* hashing_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54511E8D209805F8005BD28F /* hashing_test.cc */; }; + 545D42F58BF326180B845350 /* md5_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = 651321349CA7230B0E4377DA /* md5_testing.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 */; }; 546877D52248206A005E3DE0 /* collection_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA129C1F315EE100DD57A1 /* collection_spec_test.json */; }; @@ -611,6 +612,7 @@ 55427A6CFFB22E069DCC0CC4 /* target_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 526D755F65AC676234F57125 /* target_test.cc */; }; 555161D6DB2DDC8B57F72A70 /* comparison_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 548DB928200D59F600E00ABC /* comparison_test.cc */; }; 5556B648B9B1C2F79A706B4F /* common.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D221C2DDC800EFB9CC /* common.pb.cc */; }; + 55CF4295DAB9B5E7D4DA3D67 /* Validation_BloomFilterTest_MD5_1_1_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = 0DB24C79879C8290D46F106D /* Validation_BloomFilterTest_MD5_1_1_membership_test_result.json */; }; 55E84644D385A70E607A0F91 /* leveldb_local_store_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 5FF903AEFA7A3284660FA4C5 /* leveldb_local_store_test.cc */; }; 568EC1C0F68A7B95E57C8C6C /* leveldb_key_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54995F6E205B6E12004EFFA0 /* leveldb_key_test.cc */; }; 56D85436D3C864B804851B15 /* string_format_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9CFD366B783AE27B9E79EE7A /* string_format_apple_test.mm */; }; @@ -965,13 +967,12 @@ 925BE64990449E93242A00A2 /* memory_mutation_queue_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 74FBEFA4FE4B12C435011763 /* memory_mutation_queue_test.cc */; }; 92D7081085679497DC112EDB /* persistence_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9113B6F513D0473AEABBAF1F /* persistence_testing.cc */; }; 92EFF0CC2993B43CBC7A61FF /* grpc_streaming_reader_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6D964922154AB8F00EB9CFB /* grpc_streaming_reader_test.cc */; }; - 937265DEC2441FA018104C27 /* md5_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4007021A5284243AAE77292A /* md5_testing.cc */; }; 9382BE7190E7750EE7CCCE7C /* write_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA12A51F315EE100DD57A1 /* write_spec_test.json */; }; 938F2AF6EC5CD0B839300DB0 /* query.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D621C2DDC800EFB9CC /* query.pb.cc */; }; 939C898FE9D129F6A2EA259C /* FSTHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E03A2021401F00B64F25 /* FSTHelpers.mm */; }; 93C8F772F4DC5A985FA3D815 /* task_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 899FC22684B0F7BEEAE13527 /* task_test.cc */; }; 93E5620E3884A431A14500B0 /* document_key_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6152AD5202A5385000E5744 /* document_key_test.cc */; }; - 941B56B83A1C5771990E14DE /* Validation_BloomFilterTest_MD5_500_0001_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = 1C46787BE2535DD3B1D5CC71 /* Validation_BloomFilterTest_MD5_500_0001_membership_test_result.json */; }; + 9484B1C8E9447D397C54B100 /* md5_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = 651321349CA7230B0E4377DA /* md5_testing.cc */; }; 94854FAEAEA75A1AC77A0515 /* memory_bundle_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB4AB1388538CD3CB19EB028 /* memory_bundle_cache_test.cc */; }; 94BBB23B93E449D03FA34F87 /* mutation_queue_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 3068AA9DFBBA86C1FE2A946E /* mutation_queue_test.cc */; }; 951F06B3CCE4EE1568A75B30 /* Validation_BloomFilterTest_MD5_50000_0001_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = 962CB6A4BAABD47CFAB512B5 /* Validation_BloomFilterTest_MD5_50000_0001_membership_test_result.json */; }; @@ -1082,6 +1083,7 @@ AB38D93020236E21000A432D /* database_info_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB38D92E20235D22000A432D /* database_info_test.cc */; }; AB6B908420322E4D00CC290A /* document_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB6B908320322E4D00CC290A /* document_test.cc */; }; AB6D588EB21A2C8D40CEB408 /* byte_stream_cpp_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 01D10113ECC5B446DB35E96D /* byte_stream_cpp_test.cc */; }; + AB6FE7D0D8EDD7369E94C0A4 /* md5_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = 651321349CA7230B0E4377DA /* md5_testing.cc */; }; AB7BAB342012B519001E0872 /* geo_point_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB7BAB332012B519001E0872 /* geo_point_test.cc */; }; AB8209455BAA17850D5E196D /* http.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE9720B89AAC00B5BCE7 /* http.pb.cc */; }; AB9FF792C60FC581909EF381 /* recovery_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 9C1AFCC9E616EC33D6E169CF /* recovery_spec_test.json */; }; @@ -1188,7 +1190,6 @@ BB1A6F7D8F06E74FB6E525C5 /* document_key_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6152AD5202A5385000E5744 /* document_key_test.cc */; }; BB3F35B1510FE5449E50EC8A /* bundle_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F7FC06E0A47D393DE1759AE1 /* bundle_cache_test.cc */; }; BB894A81FDF56EEC19CC29F8 /* FIRQuerySnapshotTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04F202154AA00B64F25 /* FIRQuerySnapshotTests.mm */; }; - BBC5E4D41F51A8BB3F32A627 /* md5_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4007021A5284243AAE77292A /* md5_testing.cc */; }; BBDFE0000C4D7E529E296ED4 /* mutation.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE8220B89AAC00B5BCE7 /* mutation.pb.cc */; }; BC0C98A9201E8F98B9A176A9 /* FIRWriteBatchTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E06F202154D600B64F25 /* FIRWriteBatchTests.mm */; }; BC0EF4474F799EFB1849FE26 /* Validation_BloomFilterTest_MD5_500_01_bloom_filter_proto.json in Resources */ = {isa = PBXBuildFile; fileRef = 3F8374AFD04A9FF22693AA19 /* Validation_BloomFilterTest_MD5_500_01_bloom_filter_proto.json */; }; @@ -1313,7 +1314,6 @@ D5B252EE3F4037405DB1ECE3 /* FIRNumericTransformTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = D5B25E7E7D6873CBA4571841 /* FIRNumericTransformTests.mm */; }; D5B25CBF07F65E885C9D68AB /* perf_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = D5B2593BCB52957D62F1C9D3 /* perf_spec_test.json */; }; D5E9954FC1C5ABBC7A180B33 /* FSTSpecTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E03020213FFC00B64F25 /* FSTSpecTests.mm */; }; - D609BD9C3CB32EDFF46D7817 /* md5_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4007021A5284243AAE77292A /* md5_testing.cc */; }; D6486C7FFA8BE6F9C7D2F4C4 /* filesystem_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F51859B394D01C0C507282F1 /* filesystem_test.cc */; }; D658E6DA5A218E08810E1688 /* byte_string_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 5342CDDB137B4E93E2E85CCA /* byte_string_test.cc */; }; D6962E598CEDABA312D87760 /* bundle_reader_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 6ECAF7DE28A19C69DF386D88 /* bundle_reader_test.cc */; }; @@ -1334,6 +1334,7 @@ D98A0B6007E271E32299C79D /* FIRGeoPointTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E048202154AA00B64F25 /* FIRGeoPointTests.mm */; }; D9DA467E7903412DC6AECDE4 /* grpc_connection_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6D9649021544D4F00EB9CFB /* grpc_connection_test.cc */; }; D9EF7FC0E3F8646B272B427E /* FSTAPIHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04E202154AA00B64F25 /* FSTAPIHelpers.mm */; }; + DA179A5E064DDFE734DC205E /* md5_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = 651321349CA7230B0E4377DA /* md5_testing.cc */; }; DA1D665B12AA1062DCDEA6BD /* async_queue_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB467B208E9A8200554BA2 /* async_queue_test.cc */; }; DA4303684707606318E1914D /* target_id_generator_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380CF82019382300D97691 /* target_id_generator_test.cc */; }; DABB9FB61B1733F985CBF713 /* executor_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4688208F9B9100554BA2 /* executor_test.cc */; }; @@ -1372,7 +1373,6 @@ DF7ABEB48A650117CBEBCD26 /* object_value_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 214877F52A705012D6720CA0 /* object_value_test.cc */; }; DF96816EC67F9B8DF19B0CFD /* document_overlay_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = FFCA39825D9678A03D1845D0 /* document_overlay_cache_test.cc */; }; DF983A9C1FBF758AF3AF110D /* aggregation_result.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = D872D754B8AD88E28AF28B28 /* aggregation_result.pb.cc */; }; - E0008075F6D28D0D8BC46407 /* md5_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4007021A5284243AAE77292A /* md5_testing.cc */; }; E0239CE5666437FA1F353C41 /* Validation_BloomFilterTest_MD5_50000_01_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = 2CF440979CE992E038A54427 /* Validation_BloomFilterTest_MD5_50000_01_membership_test_result.json */; }; E04607A1E2964684184E8AEA /* index_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 8C7278B604B8799F074F4E8C /* index_spec_test.json */; }; E08297B35E12106105F448EB /* ordered_code_benchmark.cc in Sources */ = {isa = PBXBuildFile; fileRef = 0473AFFF5567E667A125347B /* ordered_code_benchmark.cc */; }; @@ -1415,6 +1415,7 @@ E6B0FD6D70A7FFC14719B503 /* Validation_BloomFilterTest_MD5_500_1_bloom_filter_proto.json in Resources */ = {isa = PBXBuildFile; fileRef = 435B519FF670450EF8ECD6B4 /* Validation_BloomFilterTest_MD5_500_1_bloom_filter_proto.json */; }; E6B825EE85BF20B88AF3E3CD /* memory_index_manager_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = DB5A1E760451189DA36028B3 /* memory_index_manager_test.cc */; }; E6F8EB02A0E499F25160BB40 /* FIRFieldPathTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04C202154AA00B64F25 /* FIRFieldPathTests.mm */; }; + E748D5354E230ABA231F4720 /* Validation_BloomFilterTest_MD5_500_0001_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = 1C46787BE2535DD3B1D5CC71 /* Validation_BloomFilterTest_MD5_500_0001_membership_test_result.json */; }; E764F0F389E7119220EB212C /* target_id_generator_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380CF82019382300D97691 /* target_id_generator_test.cc */; }; E7CE4B1ECD008983FAB90F44 /* string_format_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54131E9620ADE678001DF3FF /* string_format_test.cc */; }; E7D415B8717701B952C344E5 /* executor_std_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4687208F9B9100554BA2 /* executor_std_test.cc */; }; @@ -1454,7 +1455,7 @@ ED420D8F49DA5C41EEF93913 /* FIRSnapshotMetadataTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04D202154AA00B64F25 /* FIRSnapshotMetadataTests.mm */; }; ED4E2AC80CAF2A8FDDAC3DEE /* field_mask_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA5320A36E1F00BCEB75 /* field_mask_test.cc */; }; ED9DF1EB20025227B38736EC /* message_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = CE37875365497FFA8687B745 /* message_test.cc */; }; - EE2404A60CE148E22EF44851 /* Validation_BloomFilterTest_MD5_1_1_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = 0DB24C79879C8290D46F106D /* Validation_BloomFilterTest_MD5_1_1_membership_test_result.json */; }; + EE3512AC48C1E4B8A190FBB3 /* md5_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = 651321349CA7230B0E4377DA /* md5_testing.cc */; }; EE470CC3C8FBCDA5F70A8466 /* local_store_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 307FF03D0297024D59348EBD /* local_store_test.cc */; }; EE6DBFB0874A50578CE97A7F /* leveldb_remote_document_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 0840319686A223CC4AD3FAB1 /* leveldb_remote_document_cache_test.cc */; }; EECC1EC64CA963A8376FA55C /* persistence_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9113B6F513D0473AEABBAF1F /* persistence_testing.cc */; }; @@ -1522,7 +1523,6 @@ FABE084FA7DA6E216A41EE80 /* status_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352C20A3B3D7003E0143 /* status_test.cc */; }; FAD97B82766AEC29B7B5A1B7 /* index_manager_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AE4A9E38D65688EE000EE2A1 /* index_manager_test.cc */; }; FAE5DA6ED3E1842DC21453EE /* fake_target_metadata_provider.cc in Sources */ = {isa = PBXBuildFile; fileRef = 71140E5D09C6E76F7C71B2FC /* fake_target_metadata_provider.cc */; }; - FB17BF00D4D6FA1A7E710250 /* md5_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4007021A5284243AAE77292A /* md5_testing.cc */; }; FB2111D9205822CC8E7368C2 /* FIRDocumentReferenceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E049202154AA00B64F25 /* FIRDocumentReferenceTests.mm */; }; FB2D5208A6B5816A7244D77A /* query_engine_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B8A853940305237AFDA8050B /* query_engine_test.cc */; }; FB3D9E01547436163C456A3C /* message_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = CE37875365497FFA8687B745 /* message_test.cc */; }; @@ -1662,10 +1662,10 @@ 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 = ""; }; 3F8374AFD04A9FF22693AA19 /* Validation_BloomFilterTest_MD5_500_01_bloom_filter_proto.json */ = {isa = PBXFileReference; includeInIndex = 1; name = Validation_BloomFilterTest_MD5_500_01_bloom_filter_proto.json; path = bloom_filter_golden_test_data/Validation_BloomFilterTest_MD5_500_01_bloom_filter_proto.json; sourceTree = ""; }; 3FBAA6F05C0B46A522E3B5A7 /* bundle_cache_test.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = bundle_cache_test.h; sourceTree = ""; }; - 4007021A5284243AAE77292A /* md5_testing.cc */ = {isa = PBXFileReference; includeInIndex = 1; path = md5_testing.cc; sourceTree = ""; }; 403DBF6EFB541DFD01582AA3 /* path_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = path_test.cc; sourceTree = ""; }; 40F9D09063A07F710811A84F /* value_util_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = value_util_test.cc; sourceTree = ""; }; 4132F30044D5DF1FB15B2A9D /* fake_credentials_provider.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = fake_credentials_provider.h; sourceTree = ""; }; + 4196784E9EDA47C4E68E3A20 /* md5_testing.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = md5_testing.h; sourceTree = ""; }; 432C71959255C5DBDF522F52 /* byte_stream_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = byte_stream_test.cc; sourceTree = ""; }; 4334F87873015E3763954578 /* status_testing.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = status_testing.h; sourceTree = ""; }; 435B519FF670450EF8ECD6B4 /* Validation_BloomFilterTest_MD5_500_1_bloom_filter_proto.json */ = {isa = PBXFileReference; includeInIndex = 1; name = Validation_BloomFilterTest_MD5_500_1_bloom_filter_proto.json; path = bloom_filter_golden_test_data/Validation_BloomFilterTest_MD5_500_1_bloom_filter_proto.json; sourceTree = ""; }; @@ -1824,6 +1824,7 @@ 62E103B28B48A81D682A0DE9 /* Pods_Firestore_Example_tvOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_Example_tvOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 63136A2371C0C013EC7A540C /* target_index_matcher_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = target_index_matcher_test.cc; sourceTree = ""; }; 64AA92CFA356A2360F3C5646 /* filesystem_testing.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = filesystem_testing.h; sourceTree = ""; }; + 651321349CA7230B0E4377DA /* md5_testing.cc */ = {isa = PBXFileReference; includeInIndex = 1; path = md5_testing.cc; sourceTree = ""; }; 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 = ""; }; 6AE927CDFC7A72BF825BE4CB /* Pods-Firestore_Tests_tvOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Tests_tvOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Tests_tvOS/Pods-Firestore_Tests_tvOS.release.xcconfig"; sourceTree = ""; }; 6E8302DE210222ED003E1EA3 /* FSTFuzzTestFieldPath.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FSTFuzzTestFieldPath.h; sourceTree = ""; }; @@ -1963,7 +1964,6 @@ CF39535F2C41AB0006FA6C0E /* create_noop_connectivity_monitor.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = create_noop_connectivity_monitor.cc; sourceTree = ""; }; CF39ECA1293D21A0A2AB2626 /* FIRTransactionOptionsTests.mm */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRTransactionOptionsTests.mm; sourceTree = ""; }; D0A6E9136804A41CEC9D55D4 /* delayed_constructor_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = delayed_constructor_test.cc; sourceTree = ""; }; - D225CE12C489975D3D758414 /* md5_testing.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = md5_testing.h; sourceTree = ""; }; D3CC3DC5338DCAF43A211155 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; D5B2593BCB52957D62F1C9D3 /* perf_spec_test.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = perf_spec_test.json; sourceTree = ""; }; D5B25E7E7D6873CBA4571841 /* FIRNumericTransformTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRNumericTransformTests.mm; sourceTree = ""; }; @@ -2236,8 +2236,8 @@ CD422AF3E4515FB8E9BE67A0 /* equals_tester.h */, BA02DA2FCD0001CFC6EB08DA /* filesystem_testing.cc */, 64AA92CFA356A2360F3C5646 /* filesystem_testing.h */, - 4007021A5284243AAE77292A /* md5_testing.cc */, - D225CE12C489975D3D758414 /* md5_testing.h */, + 651321349CA7230B0E4377DA /* md5_testing.cc */, + 4196784E9EDA47C4E68E3A20 /* md5_testing.h */, 3CAA33F964042646FDDAF9F9 /* status_testing.cc */, 4334F87873015E3763954578 /* status_testing.h */, 54A0352820A3B3BD003E0143 /* testutil.cc */, @@ -3410,7 +3410,7 @@ 67439E2FC35308F50C0643D9 /* Validation_BloomFilterTest_MD5_1_01_bloom_filter_proto.json in Resources */, E9ABBFCEC5A3D2AAEEC961B5 /* Validation_BloomFilterTest_MD5_1_01_membership_test_result.json in Resources */, 9D6CE441655A093BB2EC7693 /* Validation_BloomFilterTest_MD5_1_1_bloom_filter_proto.json in Resources */, - EE2404A60CE148E22EF44851 /* Validation_BloomFilterTest_MD5_1_1_membership_test_result.json in Resources */, + 55CF4295DAB9B5E7D4DA3D67 /* Validation_BloomFilterTest_MD5_1_1_membership_test_result.json in Resources */, 7F7D236E5DF9EBD84E831DAC /* Validation_BloomFilterTest_MD5_50000_0001_bloom_filter_proto.json in Resources */, 951F06B3CCE4EE1568A75B30 /* Validation_BloomFilterTest_MD5_50000_0001_membership_test_result.json in Resources */, 50ED640EA94CEC891C61A914 /* Validation_BloomFilterTest_MD5_50000_01_bloom_filter_proto.json in Resources */, @@ -3424,7 +3424,7 @@ 83A4D0CFF283CBD70EB11B2D /* Validation_BloomFilterTest_MD5_5000_1_bloom_filter_proto.json in Resources */, 0908F0C7C15A1974CF3D20C3 /* Validation_BloomFilterTest_MD5_5000_1_membership_test_result.json in Resources */, A31AC114D37771C1E430CDC4 /* Validation_BloomFilterTest_MD5_500_0001_bloom_filter_proto.json in Resources */, - 941B56B83A1C5771990E14DE /* Validation_BloomFilterTest_MD5_500_0001_membership_test_result.json in Resources */, + E748D5354E230ABA231F4720 /* Validation_BloomFilterTest_MD5_500_0001_membership_test_result.json in Resources */, E8805A3A1DFC1E4EDA65524F /* Validation_BloomFilterTest_MD5_500_01_bloom_filter_proto.json in Resources */, 2374EA106F4F320120852E81 /* Validation_BloomFilterTest_MD5_500_01_membership_test_result.json in Resources */, 012498C63EE01921671FA0B6 /* Validation_BloomFilterTest_MD5_500_1_bloom_filter_proto.json in Resources */, @@ -4108,7 +4108,7 @@ 3F6C9F8A993CF4B0CD51E7F0 /* lru_garbage_collector_test.cc in Sources */, 12158DFCEE09D24B7988A340 /* maybe_document.pb.cc in Sources */, 52BC453A24D8335C5A57C775 /* md5_test.cc in Sources */, - FB17BF00D4D6FA1A7E710250 /* md5_testing.cc in Sources */, + DA179A5E064DDFE734DC205E /* md5_testing.cc in Sources */, FA43BA0195DA90CE29B29D36 /* memory_bundle_cache_test.cc in Sources */, 8F2055702DB5EE8DA4BACD7C /* memory_document_overlay_cache_test.cc in Sources */, CFF1EBC60A00BA5109893C6E /* memory_index_manager_test.cc in Sources */, @@ -4319,7 +4319,7 @@ 1F56F51EB6DF0951B1F4F85B /* lru_garbage_collector_test.cc in Sources */, 88FD82A1FC5FEC5D56B481D8 /* maybe_document.pb.cc in Sources */, 88C1B719AC0AABE9EEEA71BA /* md5_test.cc in Sources */, - D609BD9C3CB32EDFF46D7817 /* md5_testing.cc in Sources */, + 9484B1C8E9447D397C54B100 /* md5_testing.cc in Sources */, 9611A0FAA2E10A6B1C1AC2EA /* memory_bundle_cache_test.cc in Sources */, 75C6CECF607CA94F56260BAB /* memory_document_overlay_cache_test.cc in Sources */, 3987A3E8534BAA496D966735 /* memory_index_manager_test.cc in Sources */, @@ -4547,7 +4547,7 @@ 913F6E57AF18F84C5ECFD414 /* lru_garbage_collector_test.cc in Sources */, 6F511ABFD023AEB81F92DB12 /* maybe_document.pb.cc in Sources */, 861EA75409AB15BADCD5793F /* md5_test.cc in Sources */, - E0008075F6D28D0D8BC46407 /* md5_testing.cc in Sources */, + 545D42F58BF326180B845350 /* md5_testing.cc in Sources */, FF6333B8BD9732C068157221 /* memory_bundle_cache_test.cc in Sources */, 5F6FD840AC2D729B50991CCB /* memory_document_overlay_cache_test.cc in Sources */, E6B825EE85BF20B88AF3E3CD /* memory_index_manager_test.cc in Sources */, @@ -4775,7 +4775,7 @@ 95CE3F5265B9BB7297EE5A6B /* lru_garbage_collector_test.cc in Sources */, C19214F5B43AA745A7FC2FC1 /* maybe_document.pb.cc in Sources */, 82473F290CC7D9579D64A175 /* md5_test.cc in Sources */, - 937265DEC2441FA018104C27 /* md5_testing.cc in Sources */, + 416B653E930A609FA121C5E3 /* md5_testing.cc in Sources */, 94854FAEAEA75A1AC77A0515 /* memory_bundle_cache_test.cc in Sources */, 053C11420E49AE1A77E21C20 /* memory_document_overlay_cache_test.cc in Sources */, 4D8367018652104A8803E8DB /* memory_index_manager_test.cc in Sources */, @@ -4996,7 +4996,7 @@ 1290FA77A922B76503AE407C /* lru_garbage_collector_test.cc in Sources */, 618BBEA720B89AAC00B5BCE7 /* maybe_document.pb.cc in Sources */, 4EC3518D09A8C37A28763052 /* md5_test.cc in Sources */, - BBC5E4D41F51A8BB3F32A627 /* md5_testing.cc in Sources */, + AB6FE7D0D8EDD7369E94C0A4 /* md5_testing.cc in Sources */, A0E1C7F5C7093A498F65C5CF /* memory_bundle_cache_test.cc in Sources */, E56EEC9DAC455E2BE77D110A /* memory_document_overlay_cache_test.cc in Sources */, 3B47CC43DBA24434E215B8ED /* memory_index_manager_test.cc in Sources */, @@ -5243,7 +5243,7 @@ 4DF18D15AC926FB7A4888313 /* lru_garbage_collector_test.cc in Sources */, 12E04A12ABD5533B616D552A /* maybe_document.pb.cc in Sources */, 816375E636FD0EAECFA1E764 /* md5_test.cc in Sources */, - 2695C8DE8DCAD5D6E9C9407C /* md5_testing.cc in Sources */, + EE3512AC48C1E4B8A190FBB3 /* md5_testing.cc in Sources */, 479A392EAB42453D49435D28 /* memory_bundle_cache_test.cc in Sources */, 5CEB0E83DA68652927D2CF07 /* memory_document_overlay_cache_test.cc in Sources */, 90FE088B8FD9EC06EEED1F39 /* memory_index_manager_test.cc in Sources */, diff --git a/Firestore/Example/Tests/SpecTests/FSTSpecTests.mm b/Firestore/Example/Tests/SpecTests/FSTSpecTests.mm index 3f8a563f1e6..7ab419f357d 100644 --- a/Firestore/Example/Tests/SpecTests/FSTSpecTests.mm +++ b/Firestore/Example/Tests/SpecTests/FSTSpecTests.mm @@ -56,6 +56,7 @@ #include "Firestore/core/src/model/types.h" #include "Firestore/core/src/nanopb/message.h" #include "Firestore/core/src/nanopb/nanopb_util.h" +#include "Firestore/core/src/remote/bloom_filter.h" #include "Firestore/core/src/remote/existence_filter.h" #include "Firestore/core/src/remote/serializer.h" #include "Firestore/core/src/remote/watch_change.h" @@ -71,6 +72,7 @@ #include "Firestore/core/src/util/to_string.h" #include "Firestore/core/test/unit/testutil/testutil.h" #include "absl/memory/memory.h" +#include "absl/strings/escaping.h" #include "absl/types/optional.h" namespace objc = firebase::firestore::objc; @@ -98,6 +100,7 @@ using firebase::firestore::nanopb::ByteString; using firebase::firestore::nanopb::MakeByteString; using firebase::firestore::nanopb::Message; +using firebase::firestore::remote::BloomFilter; using firebase::firestore::remote::DocumentWatchChange; using firebase::firestore::remote::ExistenceFilter; using firebase::firestore::remote::ExistenceFilterWatchChange; @@ -115,6 +118,7 @@ using firebase::firestore::util::MakeStringPtr; using firebase::firestore::util::Path; using firebase::firestore::util::Status; +using firebase::firestore::util::StatusOr; using firebase::firestore::util::TimerId; using firebase::firestore::util::ToString; using firebase::firestore::util::WrapCompare; @@ -325,6 +329,13 @@ - (SnapshotVersion)parseVersion:(NSNumber *_Nullable)version { return Version(version.longLongValue); } +- (absl::optional)parseBloomFilter:(NSDictionary *_Nullable)bloomFilterProto { + // TODO(Mila): None of the ported spec tests actually has the bloom filter json string, so hard + // code a null value for now. Actual parsing code will be written in the next PR, where we can + // validate the parsing result. + return absl::nullopt; +} + - (DocumentViewChange)parseChange:(NSDictionary *)jsonDoc ofType:(DocumentViewChange::Type)type { NSNumber *version = jsonDoc[@"version"]; NSDictionary *options = jsonDoc[@"options"]; @@ -463,14 +474,17 @@ - (void)doWatchEntity:(NSDictionary *)watchEntity { } } -- (void)doWatchFilter:(NSArray *)watchFilter { - NSArray *targets = watchFilter[0]; +- (void)doWatchFilter:(NSDictionary *)watchFilter { + NSArray *targets = watchFilter[@"targetIds"]; HARD_ASSERT(targets.count == 1, "ExistenceFilters currently support exactly one target only."); - int keyCount = watchFilter.count == 0 ? 0 : (int)watchFilter.count - 1; + NSArray *keys = watchFilter[@"keys"]; + int keyCount = keys ? (int)keys.count : 0; + + absl::optional bloomFilter = [self parseBloomFilter:watchFilter[@"bloomFilter"]]; - ExistenceFilter filter{keyCount}; - ExistenceFilterWatchChange change{filter, targets[0].intValue}; + ExistenceFilter filter{keyCount, std::move(bloomFilter)}; + ExistenceFilterWatchChange change{std::move(filter), targets[0].intValue}; [self.driver receiveWatchChange:change snapshotVersion:SnapshotVersion::None()]; } diff --git a/Firestore/Example/Tests/SpecTests/json/existence_filter_spec_test.json b/Firestore/Example/Tests/SpecTests/json/existence_filter_spec_test.json index 1004cd567dc..58369d84959 100644 --- a/Firestore/Example/Tests/SpecTests/json/existence_filter_spec_test.json +++ b/Firestore/Example/Tests/SpecTests/json/existence_filter_spec_test.json @@ -128,12 +128,14 @@ ] }, { - "watchFilter": [ - [ - 2 + "watchFilter": { + "keys": [ + "collection/1" ], - "collection/1" - ] + "targetIds": [ + 2 + ] + } }, { "watchSnapshot": { @@ -358,13 +360,15 @@ ] }, { - "watchFilter": [ - [ - 2 + "watchFilter": { + "keys": [ + "collection/1", + "collection/2" ], - "collection/1", - "collection/2" - ] + "targetIds": [ + 2 + ] + } }, { "watchEntity": { @@ -712,11 +716,13 @@ } }, { - "watchFilter": [ - [ + "watchFilter": { + "keys": [ + ], + "targetIds": [ 2 ] - ] + } }, { "watchRemove": { @@ -889,12 +895,14 @@ ] }, { - "watchFilter": [ - [ - 2 + "watchFilter": { + "keys": [ + "collection/1" ], - "collection/1" - ] + "targetIds": [ + 2 + ] + } }, { "watchSnapshot": { @@ -1178,12 +1186,14 @@ ] }, { - "watchFilter": [ - [ - 2 + "watchFilter": { + "keys": [ + "collection/1" ], - "collection/1" - ] + "targetIds": [ + 2 + ] + } }, { "watchSnapshot": { @@ -1287,12 +1297,14 @@ } }, { - "watchFilter": [ - [ - 2 + "watchFilter": { + "keys": [ + "collection/1" ], - "collection/1" - ] + "targetIds": [ + 2 + ] + } }, { "watchSnapshot": { @@ -1458,12 +1470,14 @@ ] }, { - "watchFilter": [ - [ - 2 + "watchFilter": { + "keys": [ + "collection/1" ], - "collection/1" - ] + "targetIds": [ + 2 + ] + } }, { "watchSnapshot": { @@ -1809,12 +1823,14 @@ ] }, { - "watchFilter": [ - [ - 2 + "watchFilter": { + "keys": [ + "collection/1" ], - "collection/1" - ] + "targetIds": [ + 2 + ] + } }, { "watchSnapshot": { @@ -2108,11 +2124,13 @@ ] }, { - "watchFilter": [ - [ + "watchFilter": { + "keys": [ + ], + "targetIds": [ 2 ] - ] + } }, { "watchSnapshot": { @@ -2223,12 +2241,14 @@ ] }, { - "watchFilter": [ - [ - 2 + "watchFilter": { + "keys": [ + "collection/1" ], - "collection/1" - ] + "targetIds": [ + 2 + ] + } }, { "watchSnapshot": { diff --git a/Firestore/Example/Tests/SpecTests/json/limbo_spec_test.json b/Firestore/Example/Tests/SpecTests/json/limbo_spec_test.json index 6edf4b3589a..bb1fd9d7266 100644 --- a/Firestore/Example/Tests/SpecTests/json/limbo_spec_test.json +++ b/Firestore/Example/Tests/SpecTests/json/limbo_spec_test.json @@ -3409,11 +3409,13 @@ ] }, { - "watchFilter": [ - [ + "watchFilter": { + "keys": [ + ], + "targetIds": [ 1 ] - ] + } }, { "watchCurrent": [ @@ -8081,15 +8083,16 @@ } }, { - "watchFilter": [ - [ - 2 + "watchFilter": { + "keys": [ + "collection/b1", + "collection/b2", + "collection/b3" ], - "collection/b1", - "collection/b2", - "collection/b3" - ] - + "targetIds": [ + 2 + ] + } }, { "watchSnapshot": { diff --git a/Firestore/Example/Tests/SpecTests/json/limit_spec_test.json b/Firestore/Example/Tests/SpecTests/json/limit_spec_test.json index 521e8e41b42..3b226ee1061 100644 --- a/Firestore/Example/Tests/SpecTests/json/limit_spec_test.json +++ b/Firestore/Example/Tests/SpecTests/json/limit_spec_test.json @@ -5464,12 +5464,14 @@ } }, { - "watchFilter": [ - [ - 2 + "watchFilter": { + "keys": [ + "collection/b" ], - "collection/b" - ] + "targetIds": [ + 2 + ] + } }, { "watchSnapshot": { diff --git a/Firestore/Protos/nanopb/google/firestore/v1/bloom_filter.nanopb.cc b/Firestore/Protos/nanopb/google/firestore/v1/bloom_filter.nanopb.cc index 2050bc400fd..45da032ef97 100644 --- a/Firestore/Protos/nanopb/google/firestore/v1/bloom_filter.nanopb.cc +++ b/Firestore/Protos/nanopb/google/firestore/v1/bloom_filter.nanopb.cc @@ -44,7 +44,7 @@ const pb_field_t google_firestore_v1_BitSequence_fields[3] = { }; const pb_field_t google_firestore_v1_BloomFilter_fields[3] = { - PB_FIELD( 1, MESSAGE , SINGULAR, STATIC , FIRST, google_firestore_v1_BloomFilter, bits, bits, &google_firestore_v1_BitSequence_fields), + PB_FIELD( 1, MESSAGE , OPTIONAL, STATIC , FIRST, google_firestore_v1_BloomFilter, bits, bits, &google_firestore_v1_BitSequence_fields), PB_FIELD( 2, INT32 , SINGULAR, STATIC , OTHER, google_firestore_v1_BloomFilter, hash_count, bits, 0), PB_LAST_FIELD }; @@ -96,7 +96,9 @@ std::string google_firestore_v1_BloomFilter::ToString(int indent) const { std::string tostring_header = PrintHeader(indent, "BloomFilter", this); std::string tostring_result; - tostring_result += PrintMessageField("bits ", bits, indent + 1, false); + if (has_bits) { + tostring_result += PrintMessageField("bits ", bits, indent + 1, true); + } tostring_result += PrintPrimitiveField("hash_count: ", hash_count, indent + 1, false); diff --git a/Firestore/Protos/nanopb/google/firestore/v1/bloom_filter.nanopb.h b/Firestore/Protos/nanopb/google/firestore/v1/bloom_filter.nanopb.h index e0347c466a5..925d70db281 100644 --- a/Firestore/Protos/nanopb/google/firestore/v1/bloom_filter.nanopb.h +++ b/Firestore/Protos/nanopb/google/firestore/v1/bloom_filter.nanopb.h @@ -42,6 +42,7 @@ typedef struct _google_firestore_v1_BitSequence { } google_firestore_v1_BitSequence; typedef struct _google_firestore_v1_BloomFilter { + bool has_bits; google_firestore_v1_BitSequence bits; int32_t hash_count; @@ -53,9 +54,9 @@ typedef struct _google_firestore_v1_BloomFilter { /* Initializer values for message structs */ #define google_firestore_v1_BitSequence_init_default {NULL, 0} -#define google_firestore_v1_BloomFilter_init_default {google_firestore_v1_BitSequence_init_default, 0} +#define google_firestore_v1_BloomFilter_init_default {false, google_firestore_v1_BitSequence_init_default, 0} #define google_firestore_v1_BitSequence_init_zero {NULL, 0} -#define google_firestore_v1_BloomFilter_init_zero {google_firestore_v1_BitSequence_init_zero, 0} +#define google_firestore_v1_BloomFilter_init_zero {false, google_firestore_v1_BitSequence_init_zero, 0} /* Field tags (for use in manual encoding/decoding) */ #define google_firestore_v1_BitSequence_bitmap_tag 1 diff --git a/Firestore/Protos/nanopb/google/firestore/v1/write.nanopb.cc b/Firestore/Protos/nanopb/google/firestore/v1/write.nanopb.cc index 56275531bd4..407c3d9b38a 100644 --- a/Firestore/Protos/nanopb/google/firestore/v1/write.nanopb.cc +++ b/Firestore/Protos/nanopb/google/firestore/v1/write.nanopb.cc @@ -95,7 +95,7 @@ const pb_field_t google_firestore_v1_DocumentRemove_fields[4] = { const pb_field_t google_firestore_v1_ExistenceFilter_fields[4] = { PB_FIELD( 1, INT32 , SINGULAR, STATIC , FIRST, google_firestore_v1_ExistenceFilter, target_id, target_id, 0), PB_FIELD( 2, INT32 , SINGULAR, STATIC , OTHER, google_firestore_v1_ExistenceFilter, count, target_id, 0), - PB_FIELD( 3, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1_ExistenceFilter, unchanged_names, count, &google_firestore_v1_BloomFilter_fields), + PB_FIELD( 3, MESSAGE , OPTIONAL, STATIC , OTHER, google_firestore_v1_ExistenceFilter, unchanged_names, count, &google_firestore_v1_BloomFilter_fields), PB_LAST_FIELD }; @@ -317,8 +317,10 @@ std::string google_firestore_v1_ExistenceFilter::ToString(int indent) const { target_id, indent + 1, false); tostring_result += PrintPrimitiveField("count: ", count, indent + 1, false); - tostring_result += PrintMessageField("unchanged_names ", - unchanged_names, indent + 1, false); + if (has_unchanged_names) { + tostring_result += PrintMessageField("unchanged_names ", + unchanged_names, indent + 1, true); + } std::string tostring_tail = PrintTail(indent); return tostring_header + tostring_result + tostring_tail; diff --git a/Firestore/Protos/nanopb/google/firestore/v1/write.nanopb.h b/Firestore/Protos/nanopb/google/firestore/v1/write.nanopb.h index 3e22c2bc42a..f7dc73102dc 100644 --- a/Firestore/Protos/nanopb/google/firestore/v1/write.nanopb.h +++ b/Firestore/Protos/nanopb/google/firestore/v1/write.nanopb.h @@ -110,6 +110,7 @@ typedef struct _google_firestore_v1_DocumentTransform_FieldTransform { typedef struct _google_firestore_v1_ExistenceFilter { int32_t target_id; int32_t count; + bool has_unchanged_names; google_firestore_v1_BloomFilter unchanged_names; std::string ToString(int indent = 0) const; @@ -155,7 +156,7 @@ typedef struct _google_firestore_v1_WriteResult { #define google_firestore_v1_DocumentChange_init_default {google_firestore_v1_Document_init_default, 0, NULL, 0, NULL} #define google_firestore_v1_DocumentDelete_init_default {NULL, false, google_protobuf_Timestamp_init_default, 0, NULL} #define google_firestore_v1_DocumentRemove_init_default {NULL, 0, NULL, google_protobuf_Timestamp_init_default} -#define google_firestore_v1_ExistenceFilter_init_default {0, 0, google_firestore_v1_BloomFilter_init_default} +#define google_firestore_v1_ExistenceFilter_init_default {0, 0, false, google_firestore_v1_BloomFilter_init_default} #define google_firestore_v1_Write_init_zero {0, {google_firestore_v1_Document_init_zero}, false, google_firestore_v1_DocumentMask_init_zero, false, google_firestore_v1_Precondition_init_zero, 0, NULL} #define google_firestore_v1_DocumentTransform_init_zero {NULL, 0, NULL} #define google_firestore_v1_DocumentTransform_FieldTransform_init_zero {NULL, 0, {_google_firestore_v1_DocumentTransform_FieldTransform_ServerValue_MIN}} @@ -163,7 +164,7 @@ typedef struct _google_firestore_v1_WriteResult { #define google_firestore_v1_DocumentChange_init_zero {google_firestore_v1_Document_init_zero, 0, NULL, 0, NULL} #define google_firestore_v1_DocumentDelete_init_zero {NULL, false, google_protobuf_Timestamp_init_zero, 0, NULL} #define google_firestore_v1_DocumentRemove_init_zero {NULL, 0, NULL, google_protobuf_Timestamp_init_zero} -#define google_firestore_v1_ExistenceFilter_init_zero {0, 0, google_firestore_v1_BloomFilter_init_zero} +#define google_firestore_v1_ExistenceFilter_init_zero {0, 0, false, google_firestore_v1_BloomFilter_init_zero} /* Field tags (for use in manual encoding/decoding) */ #define google_firestore_v1_DocumentTransform_document_tag 1 diff --git a/Firestore/Protos/protos/google/firestore/v1/bloom_filter.options b/Firestore/Protos/protos/google/firestore/v1/bloom_filter.options new file mode 100644 index 00000000000..652655cd9d5 --- /dev/null +++ b/Firestore/Protos/protos/google/firestore/v1/bloom_filter.options @@ -0,0 +1,21 @@ +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# In proto3 mode, Nanopb doesn't allow distinguishing between unset fields and +# fields having default values, even for non-primitive types. Using the +# workaround suggested in +# https://github.com/nanopb/nanopb/issues/255#issuecomment-291842903 + +# `bits` might not be set if the BloomFilter is empty. +google.firestore.v1.BloomFilter.bits proto3:false \ No newline at end of file diff --git a/Firestore/Protos/protos/google/firestore/v1/write.options b/Firestore/Protos/protos/google/firestore/v1/write.options index 91805bd55a9..b7955f853c6 100644 --- a/Firestore/Protos/protos/google/firestore/v1/write.options +++ b/Firestore/Protos/protos/google/firestore/v1/write.options @@ -29,3 +29,7 @@ google.firestore.v1.Write.update_mask proto3:false # update_time should not be set for deletes. google.firestore.v1.WriteResult.update_time proto3:false + +# `unchanged_names` field might not be set if ListenRequest doesn't have +# `expected_count` field. +google.firestore.v1.ExistenceFilter.unchanged_names proto3:false \ No newline at end of file diff --git a/Firestore/core/src/remote/bloom_filter.cc b/Firestore/core/src/remote/bloom_filter.cc index 0740ef25860..7ca39e1b00d 100644 --- a/Firestore/core/src/remote/bloom_filter.cc +++ b/Firestore/core/src/remote/bloom_filter.cc @@ -29,6 +29,35 @@ namespace remote { using util::Status; using util::StatusOr; +namespace { +bool HasSameBits(const BloomFilter& lhs, const BloomFilter& rhs) { + if (lhs.bit_count() != rhs.bit_count()) { + return false; + } + if (lhs.bit_count() == 0) { + return true; + } + + const std::vector& bitmap1 = lhs.bitmap(); + const std::vector& bitmap2 = rhs.bitmap(); + const auto byte_count = static_cast(bitmap1.size()); + + // Compare all bytes from the bitmap, except for the last byte. + for (int32_t i = 0; i < byte_count - 1; ++i) { + if (bitmap1[i] != bitmap2[i]) { + return false; + } + } + + // Compare the last byte, ignoring the padding. + const int32_t padding = (byte_count * 8) - lhs.bit_count(); + const uint8_t last_byte1 = bitmap1[byte_count - 1] << padding; + const uint8_t last_byte2 = bitmap2[byte_count - 1] << padding; + + return (last_byte1 == last_byte2); +} +} // namespace + BloomFilter::Hash BloomFilter::Md5HashDigest(absl::string_view key) const { std::array md5_digest{util::CalculateMd5Digest(key)}; @@ -113,6 +142,10 @@ bool BloomFilter::MightContain(absl::string_view value) const { return true; } +bool operator==(const BloomFilter& lhs, const BloomFilter& rhs) { + return lhs.hash_count() == rhs.hash_count() && HasSameBits(lhs, rhs); +} + } // namespace remote } // namespace firestore } // namespace firebase diff --git a/Firestore/core/src/remote/bloom_filter.h b/Firestore/core/src/remote/bloom_filter.h index e406611b1cc..6c0facaad10 100644 --- a/Firestore/core/src/remote/bloom_filter.h +++ b/Firestore/core/src/remote/bloom_filter.h @@ -57,11 +57,28 @@ class BloomFilter final { */ bool MightContain(absl::string_view value) const; - /** Get the `bit_count_` field. Used for testing purpose. */ + /** + * The number of bits in the bloom filter. Guaranteed to be non-negative, and + * less than the max number of bits the bitmap can represent, i.e., + * bitmap().size() * 8. + */ int32_t bit_count() const { return bit_count_; } + /** + * The number of hash functions used to construct the filter. Guaranteed to + * be non-negative. + */ + int32_t hash_count() const { + return hash_count_; + } + + /** Bloom filter's bitmap. */ + const std::vector& bitmap() const { + return bitmap_; + } + private: /** * When checking membership of a key in bitmap, the first step is to generate @@ -87,23 +104,19 @@ class BloomFilter final { /** Return whether the bit at the given index in the bitmap is set to 1. */ bool IsBitSet(int32_t index) const; - /** - * The number of bits in the bloom filter. Guaranteed to be non-negative, and - * less than the max number of bits `bitmap_` can represent, i.e., - * bitmap_.size() * 8. - */ int32_t bit_count_ = 0; - /** - * The number of hash functions used to construct the filter. Guaranteed to - * be non-negative. - */ int32_t hash_count_ = 0; - /** Bloom filter's bitmap. */ std::vector bitmap_; }; +bool operator==(const BloomFilter& lhs, const BloomFilter& rhs); + +inline bool operator!=(const BloomFilter& lhs, const BloomFilter& rhs) { + return !(lhs == rhs); +} + } // namespace remote } // namespace firestore } // namespace firebase diff --git a/Firestore/core/src/remote/existence_filter.h b/Firestore/core/src/remote/existence_filter.h index 3f4019c8987..968341df588 100644 --- a/Firestore/core/src/remote/existence_filter.h +++ b/Firestore/core/src/remote/existence_filter.h @@ -17,6 +17,10 @@ #ifndef FIRESTORE_CORE_SRC_REMOTE_EXISTENCE_FILTER_H_ #define FIRESTORE_CORE_SRC_REMOTE_EXISTENCE_FILTER_H_ +#include + +#include "Firestore/core/src/remote/bloom_filter.h" + namespace firebase { namespace firestore { namespace remote { @@ -24,19 +28,26 @@ namespace remote { class ExistenceFilter { public: ExistenceFilter() = default; - explicit ExistenceFilter(int count) : count_{count} { + + ExistenceFilter(int count, absl::optional bloom_filter) + : count_{count}, bloom_filter_{std::move(bloom_filter)} { } int count() const { return count_; } + const absl::optional& bloom_filter() const { + return bloom_filter_; + } + private: int count_ = 0; + absl::optional bloom_filter_; }; inline bool operator==(const ExistenceFilter& lhs, const ExistenceFilter& rhs) { - return lhs.count() == rhs.count(); + return lhs.count() == rhs.count() && lhs.bloom_filter() == rhs.bloom_filter(); } } // namespace remote diff --git a/Firestore/core/src/remote/serializer.cc b/Firestore/core/src/remote/serializer.cc index ffce2da0087..2b9133bf422 100644 --- a/Firestore/core/src/remote/serializer.cc +++ b/Firestore/core/src/remote/serializer.cc @@ -26,6 +26,7 @@ #include #include #include +#include #include "Firestore/Protos/nanopb/google/firestore/v1/document.nanopb.h" #include "Firestore/Protos/nanopb/google/firestore/v1/firestore.nanopb.h" @@ -1429,9 +1430,29 @@ std::unique_ptr Serializer::DecodeDocumentRemove( std::unique_ptr Serializer::DecodeExistenceFilterWatchChange( ReadContext*, const google_firestore_v1_ExistenceFilter& filter) const { - ExistenceFilter existence_filter{filter.count}; - return absl::make_unique(existence_filter, - filter.target_id); + return absl::make_unique( + DecodeExistenceFilter(filter), filter.target_id); +} + +ExistenceFilter Serializer::DecodeExistenceFilter( + const google_firestore_v1_ExistenceFilter& filter) const { + // Create bloom filter if there is an unchanged_names present in the filter + // and inputs are valid, otherwise keep it null. + absl::optional bloom_filter; + if (filter.has_unchanged_names) { + // TODO(Mila): None of the ported spec tests here actually has the bloom + // filter json string, so hard code an empty bloom filter for now. The + // actual parsing code will be written in the next PR, where we can validate + // the parsing result. + // TODO(Mila): handle bloom filter creation failure. + StatusOr maybe_bloom_filter = + BloomFilter::Create(std::vector{}, 0, 0); + if (maybe_bloom_filter.ok()) { + bloom_filter = std::move(maybe_bloom_filter).ValueOrDie(); + } + } + + return {filter.count, std::move(bloom_filter)}; } bool Serializer::IsLocalResourceName(const ResourcePath& path) const { diff --git a/Firestore/core/src/remote/serializer.h b/Firestore/core/src/remote/serializer.h index ab9370b441a..c42c6c3ac1b 100644 --- a/Firestore/core/src/remote/serializer.h +++ b/Firestore/core/src/remote/serializer.h @@ -341,6 +341,9 @@ class Serializer { util::ReadContext* context, const google_firestore_v1_ExistenceFilter& filter) const; + ExistenceFilter DecodeExistenceFilter( + const google_firestore_v1_ExistenceFilter& filter) const; + model::DatabaseId database_id_; // TODO(varconst): Android caches the result of calling `EncodeDatabaseName` // as well, consider implementing that. diff --git a/Firestore/core/src/remote/watch_change.h b/Firestore/core/src/remote/watch_change.h index ba2951c66c2..20e6121a7b5 100644 --- a/Firestore/core/src/remote/watch_change.h +++ b/Firestore/core/src/remote/watch_change.h @@ -115,7 +115,7 @@ bool operator==(const DocumentWatchChange& lhs, const DocumentWatchChange& rhs); class ExistenceFilterWatchChange : public WatchChange { public: ExistenceFilterWatchChange(ExistenceFilter filter, model::TargetId target_id) - : filter_{filter}, target_id_{target_id} { + : filter_{std::move(filter)}, target_id_{target_id} { } Type type() const override { diff --git a/Firestore/core/test/unit/local/local_store_test.cc b/Firestore/core/test/unit/local/local_store_test.cc index 0c3e1a1e595..834a20a364c 100644 --- a/Firestore/core/test/unit/local/local_store_test.cc +++ b/Firestore/core/test/unit/local/local_store_test.cc @@ -146,7 +146,8 @@ RemoteEvent ExistenceFilterEvent(TargetId target_id, remote::FakeTargetMetadataProvider metadata_provider; metadata_provider.SetSyncedKeys(synced_keys, target_data); - ExistenceFilter existence_filter{remote_count}; + ExistenceFilter existence_filter{remote_count, + /*bloom_filter=*/absl::nullopt}; WatchChangeAggregator aggregator{&metadata_provider}; ExistenceFilterWatchChange existence_filter_watch_change{existence_filter, target_id}; diff --git a/Firestore/core/test/unit/remote/bloom_filter_test.cc b/Firestore/core/test/unit/remote/bloom_filter_test.cc index d5e8cf8ff37..43015e3981c 100644 --- a/Firestore/core/test/unit/remote/bloom_filter_test.cc +++ b/Firestore/core/test/unit/remote/bloom_filter_test.cc @@ -118,7 +118,65 @@ TEST(BloomFilterUnitTest, CreateShouldReturnNotOKStatusIfPaddingIsTooLarge) { EXPECT_EQ(maybe_bloom_filter.status().error_message(), "Invalid padding: 8"); } -TEST(BloomFilterUnitTest, MightContainCanProcessNonStandardCharacters) { +TEST(BloomFilterTest, CheckBloomFiltersEqualityWithSameInput) { + BloomFilter bloom_filter1(std::vector{1}, 1, 1); + BloomFilter bloom_filter2(std::vector{1}, 1, 1); + EXPECT_TRUE(bloom_filter1 == bloom_filter2); + EXPECT_FALSE(bloom_filter1 != bloom_filter2); +} + +TEST(BloomFilterTest, CheckBloomFiltersEqualityWithDifferentBitmap) { + { + BloomFilter bloom_filter1(std::vector{1}, 1, 1); + BloomFilter bloom_filter2(std::vector{2}, 1, 1); + EXPECT_FALSE(bloom_filter1 == bloom_filter2); + EXPECT_TRUE(bloom_filter1 != bloom_filter2); + } + { + BloomFilter bloom_filter1(std::vector{1}, 1, 1); + BloomFilter bloom_filter2(std::vector{1, 1}, 1, 1); + EXPECT_FALSE(bloom_filter1 == bloom_filter2); + EXPECT_TRUE(bloom_filter1 != bloom_filter2); + } +} + +TEST(BloomFilterTest, CheckBloomFiltersEqualityWithDifferentPadding) { + BloomFilter bloom_filter1(std::vector{1}, 1, 1); + BloomFilter bloom_filter2(std::vector{1}, 2, 1); + EXPECT_FALSE(bloom_filter1 == bloom_filter2); + EXPECT_TRUE(bloom_filter1 != bloom_filter2); +} + +TEST(BloomFilterTest, CheckBloomFiltersEqualityWithDifferentHashCount) { + BloomFilter bloom_filter1(std::vector{1}, 1, 1); + BloomFilter bloom_filter2(std::vector{1}, 1, 2); + EXPECT_FALSE(bloom_filter1 == bloom_filter2); + EXPECT_TRUE(bloom_filter1 != bloom_filter2); +} + +TEST(BloomFilterTest, + BloomFiltersEqualityCheckShouldIgnoreBitsInPaddingIndexes) { + // In BloomFilter bitmap, padding is guaranteed to be less than 8, and + // starts counting from the leftmost indexes of the last byte. + { + BloomFilter bloom_filter1(std::vector{255, 127}, 1, + 1); // bitmap -> 11111111 01111111 + BloomFilter bloom_filter2(std::vector{255, 255}, 1, + 1); // bitmap -> 11111111 11111111 + EXPECT_TRUE(bloom_filter1 == bloom_filter2); + EXPECT_FALSE(bloom_filter1 != bloom_filter2); + } + { + BloomFilter bloom_filter1(std::vector{255, 207}, 4, + 1); // bitmap -> 11111111 11001111 + BloomFilter bloom_filter2(std::vector{255, 255}, 4, + 1); // bitmap -> 11111111 11111111 + EXPECT_TRUE(bloom_filter1 == bloom_filter2); + EXPECT_FALSE(bloom_filter1 != bloom_filter2); + } +} + +TEST(BloomFilterTest, MightContainCanProcessNonStandardCharacters) { // A non-empty BloomFilter object with 1 insertion : "ÀÒ∑" BloomFilter bloom_filter(std::vector{237, 5}, 5, 8); EXPECT_TRUE(bloom_filter.MightContain("ÀÒ∑")); diff --git a/Firestore/core/test/unit/remote/remote_event_test.cc b/Firestore/core/test/unit/remote/remote_event_test.cc index 8764a3aae90..5e68c22ed66 100644 --- a/Firestore/core/test/unit/remote/remote_event_test.cc +++ b/Firestore/core/test/unit/remote/remote_event_test.cc @@ -512,6 +512,7 @@ TEST_F(RemoteEventTest, NoChangeWillStillMarkTheAffectedTargets) { ASSERT_TRUE(event.target_changes().at(1) == target_change); } +// TODO(Mila): Add test coverage for when the bloom filter is not null TEST_F(RemoteEventTest, ExistenceFilterMismatchClearsTarget) { std::unordered_map target_map = ActiveQueries({1, 2}); @@ -547,7 +548,8 @@ TEST_F(RemoteEventTest, ExistenceFilterMismatchClearsTarget) { // The existence filter mismatch will remove the document from target 1, // but not synthesize a document delete. - ExistenceFilterWatchChange change4{ExistenceFilter{1}, 1}; + ExistenceFilterWatchChange change4{ + ExistenceFilter{1, /*bloom_filter=*/absl::nullopt}, 1}; aggregator.HandleExistenceFilter(change4); event = aggregator.CreateRemoteEvent(testutil::Version(4)); @@ -578,7 +580,8 @@ TEST_F(RemoteEventTest, ExistenceFilterMismatchRemovesCurrentChanges) { // The existence filter mismatch will remove the document from target 1, but // not synthesize a document delete. - ExistenceFilterWatchChange existence_filter{ExistenceFilter{0}, 1}; + ExistenceFilterWatchChange existence_filter{ + ExistenceFilter{0, /*bloom_filter=*/absl::nullopt}, 1}; aggregator.HandleExistenceFilter(existence_filter); RemoteEvent event = aggregator.CreateRemoteEvent(testutil::Version(3)); diff --git a/Firestore/core/test/unit/remote/serializer_test.cc b/Firestore/core/test/unit/remote/serializer_test.cc index 0be416c9fe5..23e119f3922 100644 --- a/Firestore/core/test/unit/remote/serializer_test.cc +++ b/Firestore/core/test/unit/remote/serializer_test.cc @@ -1737,8 +1737,10 @@ TEST_F(SerializerTest, DecodesListenResponseWithDocumentRemove) { ExpectDeserializationRoundTrip(model, proto); } +// TODO(Mila): Add test coverage for when the bloom filter is not null TEST_F(SerializerTest, DecodesListenResponseWithExistenceFilter) { - ExistenceFilterWatchChange model(ExistenceFilter(2), 100); + ExistenceFilterWatchChange model( + ExistenceFilter(2, /*bloom_filter=*/absl::nullopt), 100); v1::ListenResponse proto; diff --git a/Firestore/core/test/unit/remote/watch_change_test.cc b/Firestore/core/test/unit/remote/watch_change_test.cc index 67a7e7d3a6f..c560d63a3f5 100644 --- a/Firestore/core/test/unit/remote/watch_change_test.cc +++ b/Firestore/core/test/unit/remote/watch_change_test.cc @@ -40,8 +40,9 @@ TEST(WatchChangeTest, CanCreateDocumentWatchChange) { EXPECT_EQ(change.new_document(), doc); } +// TODO(Mila): Add test coverage for when the bloom filter is not null TEST(WatchChangeTest, CanCreateExistenceFilterWatchChange) { - ExistenceFilter filter{7}; + ExistenceFilter filter{7, /*bloom_filter=*/absl::nullopt}; ExistenceFilterWatchChange change{filter, 5}; EXPECT_EQ(change.filter().count(), 7); EXPECT_EQ(change.target_id(), 5);