Bläddra i källkod

[realppl 8] realppl spec tests

wu-hui 10 månader sedan
förälder
incheckning
042515a2c6

+ 31 - 69
Firestore/Example/Firestore.xcodeproj/project.pbxproj

@@ -139,6 +139,7 @@
 		1145D70555D8CDC75183A88C /* leveldb_mutation_queue_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 5C7942B6244F4C416B11B86C /* leveldb_mutation_queue_test.cc */; };
 		11627F3A48F710D654829807 /* comparison_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 87DD1A65EBA9FFC1FFAAE657 /* comparison_test.cc */; };
 		117AFA7934A52466633E12C1 /* FSTTestingHooks.mm in Sources */ = {isa = PBXBuildFile; fileRef = D85AC18C55650ED230A71B82 /* FSTTestingHooks.mm */; };
+		11A5189E73D954824F015424 /* pipeline_util_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 0401C6FDE59C493BFBD5DFED /* pipeline_util_test.cc */; };
 		11BC867491A6631D37DE56A8 /* async_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = 872C92ABD71B12784A1C5520 /* async_testing.cc */; };
 		11EBD28DBD24063332433947 /* value_util_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 40F9D09063A07F710811A84F /* value_util_test.cc */; };
 		11F8EE69182C9699E90A9E3D /* database_info_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB38D92E20235D22000A432D /* database_info_test.cc */; };
@@ -334,7 +335,6 @@
 		2AAEABFD550255271E3BAC91 /* to_string_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = B68B1E002213A764008977EF /* to_string_apple_test.mm */; };
 		2ABA80088D70E7A58F95F7D8 /* delayed_constructor_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D0A6E9136804A41CEC9D55D4 /* delayed_constructor_test.cc */; };
 		2AC442FEC73D872B5751523D /* error_handling_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B37729DE4DE097CBBCB9B0DD /* error_handling_test.cc */; };
-		2AD2CB51469AE35331C39258 /* pipeline.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7142B5EC46E88349FAB3384F /* pipeline.pb.cc */; };
 		2AD8EE91928AE68DF268BEDA /* limbo_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA129E1F315EE100DD57A1 /* limbo_spec_test.json */; };
 		2AD98CD29CC6F820A74CDD5E /* Validation_BloomFilterTest_MD5_1_0001_bloom_filter_proto.json in Resources */ = {isa = PBXBuildFile; fileRef = 4B59C0A7B2A4548496ED4E7D /* Validation_BloomFilterTest_MD5_1_0001_bloom_filter_proto.json */; };
 		2AE3914BBC4EDF91BD852939 /* memory_query_engine_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8EF6A33BC2D84233C355F1D0 /* memory_query_engine_test.cc */; };
@@ -366,8 +366,8 @@
 		2F8FDF35BBB549A6F4D2118E /* FSTMemorySpecTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E02F20213FFC00B64F25 /* FSTMemorySpecTests.mm */; };
 		2FA0BAE32D587DF2EA5EEB97 /* async_queue_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB467B208E9A8200554BA2 /* async_queue_test.cc */; };
 		2FAE0BCBE559ED7214AEFEB7 /* Validation_BloomFilterTest_MD5_1_01_bloom_filter_proto.json in Resources */ = {isa = PBXBuildFile; fileRef = 0D964D4936953635AC7E0834 /* Validation_BloomFilterTest_MD5_1_01_bloom_filter_proto.json */; };
-		2FDBDA7CB161F4F26CD7E0DE /* utils.cc in Sources */ = {isa = PBXBuildFile; fileRef = 1924149B429A2020C3CD94D6 /* utils.cc */; };
 		2FC2B732841BF2C425EB35DF /* field_behavior.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 1F78CD3208A1D5885B4C134E /* field_behavior.pb.cc */; };
+		2FDBDA7CB161F4F26CD7E0DE /* utils.cc in Sources */ = {isa = PBXBuildFile; fileRef = 1924149B429A2020C3CD94D6 /* utils.cc */; };
 		3040FD156E1B7C92B0F2A70C /* ordered_code_benchmark.cc in Sources */ = {isa = PBXBuildFile; fileRef = 0473AFFF5567E667A125347B /* ordered_code_benchmark.cc */; };
 		3056418E81BC7584FBE8AD6C /* user_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = CCC9BD953F121B9E29F9AA42 /* user_test.cc */; };
 		306E762DC6B829CED4FD995D /* target_id_generator_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB380CF82019382300D97691 /* target_id_generator_test.cc */; };
@@ -441,7 +441,6 @@
 		3B37BD3C13A66625EC82CF77 /* hard_assert_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 444B7AB3F5A2929070CB1363 /* hard_assert_test.cc */; };
 		3B47CC43DBA24434E215B8ED /* memory_index_manager_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = DB5A1E760451189DA36028B3 /* memory_index_manager_test.cc */; };
 		3B496F47CE9E663B8A22FB43 /* nested_properties_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8AC88AA2B929CFEC2656E37D /* nested_properties_test.cc */; };
-		3B4CFB45208A7EEF1EA58ADC /* pipeline.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7142B5EC46E88349FAB3384F /* pipeline.pb.cc */; };
 		3B5CEA04AC1627256A1AE8BA /* bloom_filter_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = A2E6F09AD1EE0A6A452E9A08 /* bloom_filter_test.cc */; };
 		3B843E4C1F3A182900548890 /* remote_store_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 3B843E4A1F3930A400548890 /* remote_store_spec_test.json */; };
 		3BA4EEA6153B3833F86B8104 /* writer_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = BC3C788D290A935C353CEAA1 /* writer_test.cc */; };
@@ -520,6 +519,7 @@
 		48720B5768AFA2B2F3E14C04 /* Validation_BloomFilterTest_MD5_500_1_bloom_filter_proto.json in Resources */ = {isa = PBXBuildFile; fileRef = D8E530B27D5641B9C26A452C /* Validation_BloomFilterTest_MD5_500_1_bloom_filter_proto.json */; };
 		48926FF55484E996B474D32F /* Validation_BloomFilterTest_MD5_500_01_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = DD990FD89C165F4064B4F608 /* Validation_BloomFilterTest_MD5_500_01_membership_test_result.json */; };
 		489D672CAA09B9BC66798E9F /* status.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE9920B89AAC00B5BCE7 /* status.pb.cc */; };
+		48A9AD22B0601C52B0522CF7 /* pipeline_util_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 0401C6FDE59C493BFBD5DFED /* pipeline_util_test.cc */; };
 		48BC5801432127A90CFF55E3 /* index.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 395E8B07639E69290A929695 /* index.pb.cc */; };
 		48D1B38B93D34F1B82320577 /* view_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = A5466E7809AD2871FFDE6C76 /* view_testing.cc */; };
 		48F44AA226FAD5DE4EAC3798 /* leveldb_query_engine_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = DB1F1E1B1ED15E8D042144B1 /* leveldb_query_engine_test.cc */; };
@@ -702,7 +702,6 @@
 		54C2294F1FECABAE007D065B /* log_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54C2294E1FECABAE007D065B /* log_test.cc */; };
 		54C3242322D3B627000FE6DD /* CodableIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 124C932B22C1642C00CA8C2D /* CodableIntegrationTests.swift */; };
 		54D400D42148BACE001D2BCC /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 54D400D32148BACE001D2BCC /* GoogleService-Info.plist */; };
-		54D54C9289C8AD6254887E56 /* Pods_Firestore_FuzzTests_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7B037EE2F287E5D070C81D0F /* Pods_Firestore_FuzzTests_iOS.framework */; };
 		54DA12A61F315EE100DD57A1 /* collection_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA129C1F315EE100DD57A1 /* collection_spec_test.json */; };
 		54DA12A71F315EE100DD57A1 /* existence_filter_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA129D1F315EE100DD57A1 /* existence_filter_spec_test.json */; };
 		54DA12A81F315EE100DD57A1 /* limbo_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA129E1F315EE100DD57A1 /* limbo_spec_test.json */; };
@@ -719,6 +718,7 @@
 		5556B648B9B1C2F79A706B4F /* common.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D221C2DDC800EFB9CC /* common.pb.cc */; };
 		55B9A6ACDF95D356EA501D92 /* Pods_Firestore_Example_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BB5A5E6DD07DA3EB7AD46CA7 /* Pods_Firestore_Example_iOS.framework */; };
 		55E84644D385A70E607A0F91 /* leveldb_local_store_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 5FF903AEFA7A3284660FA4C5 /* leveldb_local_store_test.cc */; };
+		563FE05627C7E66469E99292 /* pipeline_util_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 0401C6FDE59C493BFBD5DFED /* pipeline_util_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 */; };
 		57171BD004A1691B19A76453 /* Validation_BloomFilterTest_MD5_1_0001_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = C939D1789E38C09F9A0C1157 /* Validation_BloomFilterTest_MD5_1_0001_membership_test_result.json */; };
@@ -956,6 +956,7 @@
 		75C6CECF607CA94F56260BAB /* memory_document_overlay_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 29D9C76922DAC6F710BC1EF4 /* memory_document_overlay_cache_test.cc */; };
 		75CC1D1F7F1093C2E09D9998 /* inequality_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = A410E38FA5C3EB5AECDB6F1C /* inequality_test.cc */; };
 		75D124966E727829A5F99249 /* FIRTypeTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E071202154D600B64F25 /* FIRTypeTests.mm */; };
+		7676C06AF7FF67806747E4F0 /* pipeline_util_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 0401C6FDE59C493BFBD5DFED /* pipeline_util_test.cc */; };
 		76A5447D76F060E996555109 /* task_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 899FC22684B0F7BEEAE13527 /* task_test.cc */; };
 		76AD5862714F170251BDEACB /* Validation_BloomFilterTest_MD5_50000_0001_bloom_filter_proto.json in Resources */ = {isa = PBXBuildFile; fileRef = A5D9044B72061CAF284BC9E4 /* Validation_BloomFilterTest_MD5_50000_0001_bloom_filter_proto.json */; };
 		76C18D1BA96E4F5DF1BF7F4B /* Validation_BloomFilterTest_MD5_500_1_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = 8AB49283E544497A9C5A0E59 /* Validation_BloomFilterTest_MD5_500_1_membership_test_result.json */; };
@@ -1213,8 +1214,8 @@
 		A25FF76DEF542E01A2DF3B0E /* time_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = 5497CB76229DECDE000FB92F /* time_testing.cc */; };
 		A27096F764227BC73526FED3 /* leveldb_remote_document_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 0840319686A223CC4AD3FAB1 /* leveldb_remote_document_cache_test.cc */; };
 		A27908A198E1D2230C1801AC /* bundle_serializer_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B5C2A94EE24E60543F62CC35 /* bundle_serializer_test.cc */; };
-		A29D82322423DA4EE09C81BE /* null_semantics_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = DD520991DBDF5C11BBFAFE6D /* null_semantics_test.cc */; };
 		A296B0110550890E1D8D59A3 /* explain_stats.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 428662F00938E9E21F7080D7 /* explain_stats.pb.cc */; };
+		A29D82322423DA4EE09C81BE /* null_semantics_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = DD520991DBDF5C11BBFAFE6D /* null_semantics_test.cc */; };
 		A2E9978E02F7BCB016555F09 /* Validation_BloomFilterTest_MD5_1_1_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = 3369AC938F82A70685C5ED58 /* Validation_BloomFilterTest_MD5_1_1_membership_test_result.json */; };
 		A3262936317851958C8EABAF /* byte_stream_cpp_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 01D10113ECC5B446DB35E96D /* byte_stream_cpp_test.cc */; };
 		A405A976DB6444D3ED3FCAB2 /* timestamp_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 82DF854A7238D538FA53C908 /* timestamp_test.cc */; };
@@ -1224,7 +1225,6 @@
 		A4ECA8335000CBDF94586C94 /* FSTDatastoreTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E07E202154EC00B64F25 /* FSTDatastoreTests.mm */; };
 		A4F2B68E7EFADB0EB443CFF8 /* Pods_Firestore_Tests_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8294C2063C0096AE5E43F6DF /* Pods_Firestore_Tests_iOS.framework */; };
 		A5175CA2E677E13CC5F23D72 /* document_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB6B908320322E4D00CC290A /* document_test.cc */; };
-		A5301AA55748A11801E3EE47 /* field_behavior.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = FAAF1A69F4A315C38357BDC4 /* field_behavior.pb.cc */; };
 		A53C9BA3D0E366DCCDD640BF /* canonify_eq_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 51004EAF5EE01ADCE8FE3788 /* canonify_eq_test.cc */; };
 		A55266E6C986251D283CE948 /* FIRCursorTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E070202154D600B64F25 /* FIRCursorTests.mm */; };
 		A5583822218F9D5B1E86FCAC /* overlay_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = E1459FA70B8FC18DE4B80D0D /* overlay_test.cc */; };
@@ -1243,8 +1243,8 @@
 		A7309DAD4A3B5334536ECA46 /* remote_event_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 584AE2C37A55B408541A6FF3 /* remote_event_test.cc */; };
 		A7399FB3BEC50BBFF08EC9BA /* mutation_queue_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 3068AA9DFBBA86C1FE2A946E /* mutation_queue_test.cc */; };
 		A7669E72BCED7FBADA4B1314 /* thread_safe_memoizer_testing_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = EA10515F99A42D71DA2D2841 /* thread_safe_memoizer_testing_test.cc */; };
-		A78366DBE0BFDE42474A728A /* TestHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E73D03B9C02CAC7BEBAFA86 /* TestHelper.swift */; };
 		A76A3879A497533584C91D97 /* sort_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 15EAAEEE767299A3CDA96132 /* sort_test.cc */; };
+		A78366DBE0BFDE42474A728A /* TestHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E73D03B9C02CAC7BEBAFA86 /* TestHelper.swift */; };
 		A80D38096052F928B17E1504 /* user_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = CCC9BD953F121B9E29F9AA42 /* user_test.cc */; };
 		A833A216988ADFD4876763CD /* Validation_BloomFilterTest_MD5_50000_01_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = C8FB22BCB9F454DA44BA80C8 /* Validation_BloomFilterTest_MD5_50000_01_membership_test_result.json */; };
 		A841EEB5A94A271523EAE459 /* Validation_BloomFilterTest_MD5_50000_0001_bloom_filter_proto.json in Resources */ = {isa = PBXBuildFile; fileRef = A5D9044B72061CAF284BC9E4 /* Validation_BloomFilterTest_MD5_50000_0001_bloom_filter_proto.json */; };
@@ -1642,6 +1642,7 @@
 		E1016ECF143B732E7821358E /* byte_stream_apple_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7628664347B9C96462D4BF17 /* byte_stream_apple_test.mm */; };
 		E11DDA3DD75705F26245E295 /* FIRCollectionReferenceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E045202154AA00B64F25 /* FIRCollectionReferenceTests.mm */; };
 		E1264B172412967A09993EC6 /* byte_string_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 5342CDDB137B4E93E2E85CCA /* byte_string_test.cc */; };
+		E14DBE1D9FC94B5E7E391BEE /* pipeline_util_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 0401C6FDE59C493BFBD5DFED /* pipeline_util_test.cc */; };
 		E15A05789FF01F44BCAE75EF /* fields_array_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA4CBA48204C9E25B56993BC /* fields_array_test.cc */; };
 		E186D002520881AD2906ADDB /* status.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE9920B89AAC00B5BCE7 /* status.pb.cc */; };
 		E1DB8E1A4CF3DCE2AE8454D8 /* string_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = EEF23C7104A4D040C3A8CF9B /* string_test.cc */; };
@@ -1667,6 +1668,7 @@
 		E54AC3EA240C05B3720A2FE9 /* Validation_BloomFilterTest_MD5_5000_0001_bloom_filter_proto.json in Resources */ = {isa = PBXBuildFile; fileRef = 728F617782600536F2561463 /* Validation_BloomFilterTest_MD5_5000_0001_bloom_filter_proto.json */; };
 		E56EEC9DAC455E2BE77D110A /* memory_document_overlay_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 29D9C76922DAC6F710BC1EF4 /* memory_document_overlay_cache_test.cc */; };
 		E59F597947D3E130A57E1B5E /* Validation_BloomFilterTest_MD5_1_1_membership_test_result.json in Resources */ = {isa = PBXBuildFile; fileRef = 3369AC938F82A70685C5ED58 /* Validation_BloomFilterTest_MD5_1_1_membership_test_result.json */; };
+		E5FE2BEECD70D59361B51540 /* pipeline_util_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 0401C6FDE59C493BFBD5DFED /* pipeline_util_test.cc */; };
 		E63342115B1DA65DB6F2C59A /* leveldb_local_store_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 5FF903AEFA7A3284660FA4C5 /* leveldb_local_store_test.cc */; };
 		E6357221227031DD77EE5265 /* index_manager_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AE4A9E38D65688EE000EE2A1 /* index_manager_test.cc */; };
 		E6603BA4B16C9E1422DD3A4B /* FSTTestingHooks.mm in Sources */ = {isa = PBXBuildFile; fileRef = D85AC18C55650ED230A71B82 /* FSTTestingHooks.mm */; };
@@ -1691,8 +1693,8 @@
 		E8AB8024B70F6C960D8C7530 /* document_overlay_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = FFCA39825D9678A03D1845D0 /* document_overlay_cache_test.cc */; };
 		E8BA7055EDB8B03CC99A528F /* recovery_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 9C1AFCC9E616EC33D6E169CF /* recovery_spec_test.json */; };
 		E8BB7CCF3928A5866B1C9B86 /* arithmetic_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 76EED4ED84056B623D92FE20 /* arithmetic_test.cc */; };
-		E92D194F027C325631036B75 /* unicode_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 09C56D14F17CA02A07C60847 /* unicode_test.cc */; };
 		E9071BE412DC42300B936BAF /* explain_stats.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 428662F00938E9E21F7080D7 /* explain_stats.pb.cc */; };
+		E92D194F027C325631036B75 /* unicode_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 09C56D14F17CA02A07C60847 /* unicode_test.cc */; };
 		E962CA641FB1312638593131 /* leveldb_document_overlay_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AE89CFF09C6804573841397F /* leveldb_document_overlay_cache_test.cc */; };
 		E99D5467483B746D4AA44F74 /* fields_array_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA4CBA48204C9E25B56993BC /* fields_array_test.cc */; };
 		E9BC6A5BC2B209B1BA2F8BD6 /* field_behavior.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 1F78CD3208A1D5885B4C134E /* field_behavior.pb.cc */; };
@@ -1897,6 +1899,7 @@
 		014C60628830D95031574D15 /* random_access_queue_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = random_access_queue_test.cc; sourceTree = "<group>"; };
 		01D10113ECC5B446DB35E96D /* byte_stream_cpp_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = byte_stream_cpp_test.cc; sourceTree = "<group>"; };
 		03BD47161789F26754D3B958 /* Pods-Firestore_Benchmarks_iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Benchmarks_iOS.release.xcconfig"; path = "Target Support Files/Pods-Firestore_Benchmarks_iOS/Pods-Firestore_Benchmarks_iOS.release.xcconfig"; sourceTree = "<group>"; };
+		0401C6FDE59C493BFBD5DFED /* pipeline_util_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; path = pipeline_util_test.cc; sourceTree = "<group>"; };
 		0458BABD8F8738AD16F4A2FE /* array_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; name = array_test.cc; path = expressions/array_test.cc; sourceTree = "<group>"; };
 		045D39C4A7D52AF58264240F /* remote_document_cache_test.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = remote_document_cache_test.h; sourceTree = "<group>"; };
 		0473AFFF5567E667A125347B /* ordered_code_benchmark.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = ordered_code_benchmark.cc; sourceTree = "<group>"; };
@@ -1914,8 +1917,8 @@
 		129A369928CA555B005AE7E2 /* FIRCountTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRCountTests.mm; sourceTree = "<group>"; };
 		12F4357299652983A615F886 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = "<group>"; };
 		132E32997D781B896672D30A /* reference_set_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = reference_set_test.cc; sourceTree = "<group>"; };
-		15EAAEEE767299A3CDA96132 /* sort_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; name = sort_test.cc; path = pipeline/sort_test.cc; sourceTree = "<group>"; };
 		15249D092D85B40EFC8A1459 /* pipeline.pb.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = pipeline.pb.h; sourceTree = "<group>"; };
+		15EAAEEE767299A3CDA96132 /* sort_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; name = sort_test.cc; path = pipeline/sort_test.cc; sourceTree = "<group>"; };
 		166CE73C03AB4366AAC5201C /* leveldb_index_manager_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = leveldb_index_manager_test.cc; sourceTree = "<group>"; };
 		1924149B429A2020C3CD94D6 /* utils.cc */ = {isa = PBXFileReference; includeInIndex = 1; name = utils.cc; path = pipeline/utils.cc; sourceTree = "<group>"; };
 		1A7D48A017ECB54FD381D126 /* Validation_BloomFilterTest_MD5_5000_1_membership_test_result.json */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.json; name = Validation_BloomFilterTest_MD5_5000_1_membership_test_result.json; path = bloom_filter_golden_test_data/Validation_BloomFilterTest_MD5_5000_1_membership_test_result.json; sourceTree = "<group>"; };
@@ -2167,7 +2170,6 @@
 		73F1F73A2210F3D800E1F692 /* index_manager_test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = index_manager_test.h; sourceTree = "<group>"; };
 		74FBEFA4FE4B12C435011763 /* memory_mutation_queue_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = memory_mutation_queue_test.cc; sourceTree = "<group>"; };
 		7515B47C92ABEEC66864B55C /* field_transform_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = field_transform_test.cc; sourceTree = "<group>"; };
-		756DC5F038E54F8B82B64780 /* Pods-Firestore_FuzzTests_iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_FuzzTests_iOS.debug.xcconfig"; path = "Target Support Files/Pods-Firestore_FuzzTests_iOS/Pods-Firestore_FuzzTests_iOS.debug.xcconfig"; sourceTree = "<group>"; };
 		75860CD13AF47EB1EA39EC2F /* leveldb_opener_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = leveldb_opener_test.cc; sourceTree = "<group>"; };
 		75E24C5CD7BC423D48713100 /* counting_query_engine.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = counting_query_engine.h; sourceTree = "<group>"; };
 		7628664347B9C96462D4BF17 /* byte_stream_apple_test.mm */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.objcpp; path = byte_stream_apple_test.mm; sourceTree = "<group>"; };
@@ -2178,13 +2180,11 @@
 		795AA8FC31D2AF6864B07D39 /* FIRIndexingTests.mm */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRIndexingTests.mm; sourceTree = "<group>"; };
 		79D4CD6A707ED3F7A6D2ECF5 /* view_testing.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = view_testing.h; sourceTree = "<group>"; };
 		79EAA9F7B1B9592B5F053923 /* bundle_spec_test.json */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.json; path = bundle_spec_test.json; sourceTree = "<group>"; };
-		7B037EE2F287E5D070C81D0F /* Pods_Firestore_FuzzTests_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_FuzzTests_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		7B44DD11682C4803B73DCC34 /* Validation_BloomFilterTest_MD5_50000_01_bloom_filter_proto.json */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.json; name = Validation_BloomFilterTest_MD5_50000_01_bloom_filter_proto.json; path = bloom_filter_golden_test_data/Validation_BloomFilterTest_MD5_50000_01_bloom_filter_proto.json; sourceTree = "<group>"; };
 		7B65C996438B84DBC7616640 /* CodableTimestampTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CodableTimestampTests.swift; sourceTree = "<group>"; };
 		7C3F995E040E9E9C5E8514BB /* query_listener_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = query_listener_test.cc; sourceTree = "<group>"; };
 		7C5C40C7BFBB86032F1DC632 /* FSTExceptionCatcher.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = FSTExceptionCatcher.h; sourceTree = "<group>"; };
 		7EB299CF85034F09CFD6F3FD /* remote_document_cache_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = remote_document_cache_test.cc; sourceTree = "<group>"; };
-		80B9DCD61D9C9A3793248509 /* Pods-Firestore_FuzzTests_iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_FuzzTests_iOS.release.xcconfig"; path = "Target Support Files/Pods-Firestore_FuzzTests_iOS/Pods-Firestore_FuzzTests_iOS.release.xcconfig"; sourceTree = "<group>"; };
 		81DFB7DE556603F7FDEDCA84 /* Pods-Firestore_Example_iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example_iOS.debug.xcconfig"; path = "Target Support Files/Pods-Firestore_Example_iOS/Pods-Firestore_Example_iOS.debug.xcconfig"; sourceTree = "<group>"; };
 		8294C2063C0096AE5E43F6DF /* Pods_Firestore_Tests_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_Tests_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		82DF854A7238D538FA53C908 /* timestamp_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; name = timestamp_test.cc; path = expressions/timestamp_test.cc; sourceTree = "<group>"; };
@@ -2227,8 +2227,8 @@
 		A2E6F09AD1EE0A6A452E9A08 /* bloom_filter_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = bloom_filter_test.cc; sourceTree = "<group>"; };
 		A366F6AE1A5A77548485C091 /* bundle.pb.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = bundle.pb.cc; sourceTree = "<group>"; };
 		A410E38FA5C3EB5AECDB6F1C /* inequality_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; name = inequality_test.cc; path = pipeline/inequality_test.cc; sourceTree = "<group>"; };
-		A47DF1B9E7CDA6F76A0BFF57 /* Pods-Firestore_Example_tvOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example_tvOS.debug.xcconfig"; path = "Target Support Files/Pods-Firestore_Example_tvOS/Pods-Firestore_Example_tvOS.debug.xcconfig"; sourceTree = "<group>"; };
 		A4192EB032E23129EF23605A /* field_behavior.pb.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = field_behavior.pb.h; sourceTree = "<group>"; };
+		A47DF1B9E7CDA6F76A0BFF57 /* Pods-Firestore_Example_tvOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example_tvOS.debug.xcconfig"; path = "Target Support Files/Pods-Firestore_Example_tvOS/Pods-Firestore_Example_tvOS.debug.xcconfig"; sourceTree = "<group>"; };
 		A5466E7809AD2871FFDE6C76 /* view_testing.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = view_testing.cc; sourceTree = "<group>"; };
 		A5D9044B72061CAF284BC9E4 /* Validation_BloomFilterTest_MD5_50000_0001_bloom_filter_proto.json */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.json; name = Validation_BloomFilterTest_MD5_50000_0001_bloom_filter_proto.json; path = bloom_filter_golden_test_data/Validation_BloomFilterTest_MD5_50000_0001_bloom_filter_proto.json; sourceTree = "<group>"; };
 		A668C02CBF00BC56AEC81C2A /* Pods-Firestore_IntegrationTests_tvOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_IntegrationTests_tvOS.debug.xcconfig"; path = "Target Support Files/Pods-Firestore_IntegrationTests_tvOS/Pods-Firestore_IntegrationTests_tvOS.debug.xcconfig"; sourceTree = "<group>"; };
@@ -2443,7 +2443,6 @@
 			buildActionMask = 2147483647;
 			files = (
 				6EDD3B4620BF247500C33877 /* Foundation.framework in Frameworks */,
-				54D54C9289C8AD6254887E56 /* Pods_Firestore_FuzzTests_iOS.framework in Frameworks */,
 				6EDD3B4820BF247500C33877 /* UIKit.framework in Frameworks */,
 				6EDD3B4920BF247500C33877 /* XCTest.framework in Frameworks */,
 			);
@@ -2877,7 +2876,6 @@
 				BB5A5E6DD07DA3EB7AD46CA7 /* Pods_Firestore_Example_iOS.framework */,
 				A17F8CBAFA07CAE9FFBC8BC5 /* Pods_Firestore_Example_macOS.framework */,
 				9A7EE8E1466BA54F199B0991 /* Pods_Firestore_Example_tvOS.framework */,
-				7B037EE2F287E5D070C81D0F /* Pods_Firestore_FuzzTests_iOS.framework */,
 				D9C9F60851E52197B30E0AF9 /* Pods_Firestore_IntegrationTests_iOS.framework */,
 				253A7A96FFAA2C8A8754D3CF /* Pods_Firestore_IntegrationTests_macOS.framework */,
 				453332546740E27077C65FDC /* Pods_Firestore_IntegrationTests_tvOS.framework */,
@@ -3036,8 +3034,6 @@
 				88B7F25F26338EB9C03AE440 /* Pods-Firestore_Example_macOS.release.xcconfig */,
 				A47DF1B9E7CDA6F76A0BFF57 /* Pods-Firestore_Example_tvOS.debug.xcconfig */,
 				F339B5B848F79BBDB2133210 /* Pods-Firestore_Example_tvOS.release.xcconfig */,
-				756DC5F038E54F8B82B64780 /* Pods-Firestore_FuzzTests_iOS.debug.xcconfig */,
-				80B9DCD61D9C9A3793248509 /* Pods-Firestore_FuzzTests_iOS.release.xcconfig */,
 				708BC2920AEF83DC6630887E /* Pods-Firestore_IntegrationTests_iOS.debug.xcconfig */,
 				62CF8E2E7611B285B46228FE /* Pods-Firestore_IntegrationTests_iOS.release.xcconfig */,
 				5C767F7D43A603B557327513 /* Pods-Firestore_IntegrationTests_macOS.debug.xcconfig */,
@@ -3182,6 +3178,7 @@
 				AB38D92E20235D22000A432D /* database_info_test.cc */,
 				6F57521E161450FAF89075ED /* event_manager_test.cc */,
 				F02F734F272C3C70D1307076 /* filter_test.cc */,
+				0401C6FDE59C493BFBD5DFED /* pipeline_util_test.cc */,
 				7C3F995E040E9E9C5E8514BB /* query_listener_test.cc */,
 				B9C261C26C5D311E1E3C0CB9 /* query_test.cc */,
 				AB380CF82019382300D97691 /* target_id_generator_test.cc */,
@@ -3192,14 +3189,6 @@
 			path = core;
 			sourceTree = "<group>";
 		};
-		C7D3D622BB13EB3C3301DA4F /* TestHelper */ = {
-			isa = PBXGroup;
-			children = (
-				0E73D03B9C02CAC7BEBAFA86 /* TestHelper.swift */,
-			);
-			name = TestHelper;
-			sourceTree = "<group>";
-		};
 		AD2E6E1CDE874DD15298E8F5 /* expressions */ = {
 			isa = PBXGroup;
 			children = (
@@ -3217,6 +3206,14 @@
 			name = expressions;
 			sourceTree = "<group>";
 		};
+		C7D3D622BB13EB3C3301DA4F /* TestHelper */ = {
+			isa = PBXGroup;
+			children = (
+				0E73D03B9C02CAC7BEBAFA86 /* TestHelper.swift */,
+			);
+			name = TestHelper;
+			sourceTree = "<group>";
+		};
 		DAFF0CF621E64AC30062958F /* macOS */ = {
 			isa = PBXGroup;
 			children = (
@@ -3533,12 +3530,10 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = 6EDD3B5820BF247500C33877 /* Build configuration list for PBXNativeTarget "Firestore_FuzzTests_iOS" */;
 			buildPhases = (
-				A0E5B5F1FF12D2093E1A06D4 /* [CP] Check Pods Manifest.lock */,
 				6EDD3AD520BF247500C33877 /* Sources */,
 				6EDD3B4520BF247500C33877 /* Frameworks */,
 				6EDD3B4A20BF247500C33877 /* Resources */,
 				6E622C7A20F52C8300B7E93A /* Run Script */,
-				39AA18B34547A803396E030C /* [CP] Embed Pods Frameworks */,
 			);
 			buildRules = (
 			);
@@ -4110,21 +4105,6 @@
 			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Firestore_IntegrationTests_iOS/Pods-Firestore_IntegrationTests_iOS-frameworks.sh\"\n";
 			showEnvVarsInLog = 0;
 		};
-		39AA18B34547A803396E030C /* [CP] Embed Pods Frameworks */ = {
-			isa = PBXShellScriptBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			inputPaths = (
-			);
-			name = "[CP] Embed Pods Frameworks";
-			outputPaths = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-			shellPath = /bin/sh;
-			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Firestore_FuzzTests_iOS/Pods-Firestore_FuzzTests_iOS-frameworks.sh\"\n";
-			showEnvVarsInLog = 0;
-		};
 		42C55F231E24330A93F24CD3 /* [CP] Check Pods Manifest.lock */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
@@ -4230,28 +4210,6 @@
 			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;
 		};
-		A0E5B5F1FF12D2093E1A06D4 /* [CP] Check Pods Manifest.lock */ = {
-			isa = PBXShellScriptBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			inputFileListPaths = (
-			);
-			inputPaths = (
-				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
-				"${PODS_ROOT}/Manifest.lock",
-			);
-			name = "[CP] Check Pods Manifest.lock";
-			outputFileListPaths = (
-			);
-			outputPaths = (
-				"$(DERIVED_FILE_DIR)/Pods-Firestore_FuzzTests_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;
-		};
 		AC3A1FAA5AB14C1518AB82C3 /* [CP] Check Pods Manifest.lock */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
@@ -4630,6 +4588,7 @@
 				DB7E9C5A59CCCDDB7F0C238A /* path_test.cc in Sources */,
 				E30BF9E316316446371C956C /* persistence_testing.cc in Sources */,
 				60DA778E447F9ACD402FDA2F /* pipeline.pb.cc in Sources */,
+				E5FE2BEECD70D59361B51540 /* pipeline_util_test.cc in Sources */,
 				0455FC6E2A281BD755FD933A /* precondition_test.cc in Sources */,
 				5ECE040F87E9FCD0A5D215DB /* pretty_printing_test.cc in Sources */,
 				938F2AF6EC5CD0B839300DB0 /* query.pb.cc in Sources */,
@@ -4676,8 +4635,8 @@
 				482D503CC826265FCEAB53DE /* thread_safe_memoizer_testing.cc in Sources */,
 				451EFFB413364E5A420F8B2D /* thread_safe_memoizer_testing_test.cc in Sources */,
 				5497CB78229DECDE000FB92F /* time_testing.cc in Sources */,
-				B7EFE1206B6A5A1712BD6745 /* timestamp_test.cc in Sources */,
 				ACC9369843F5ED3BD2284078 /* timestamp_test.cc in Sources */,
+				B7EFE1206B6A5A1712BD6745 /* timestamp_test.cc in Sources */,
 				2AAEABFD550255271E3BAC91 /* to_string_apple_test.mm in Sources */,
 				1E2AE064CF32A604DC7BFD4D /* to_string_test.cc in Sources */,
 				AAFA9D7A0A067F2D3D8D5487 /* token_test.cc in Sources */,
@@ -4882,6 +4841,7 @@
 				0963F6D7B0F9AE1E24B82866 /* path_test.cc in Sources */,
 				92D7081085679497DC112EDB /* persistence_testing.cc in Sources */,
 				8429E18EFBAF473209731E01 /* pipeline.pb.cc in Sources */,
+				48A9AD22B0601C52B0522CF7 /* pipeline_util_test.cc in Sources */,
 				152543FD706D5E8851C8DA92 /* precondition_test.cc in Sources */,
 				2639ABDA17EECEB7F62D1D83 /* pretty_printing_test.cc in Sources */,
 				5FA3DB52A478B01384D3A2ED /* query.pb.cc in Sources */,
@@ -4928,8 +4888,8 @@
 				3D6AC48D6197E6539BBBD28F /* thread_safe_memoizer_testing.cc in Sources */,
 				7801E06BFFB08FCE7AB54AD6 /* thread_safe_memoizer_testing_test.cc in Sources */,
 				5497CB79229DECDE000FB92F /* time_testing.cc in Sources */,
-				02E1EA3818F4BEEA9CE40DAE /* timestamp_test.cc in Sources */,
 				26CB3D7C871BC56456C6021E /* timestamp_test.cc in Sources */,
+				02E1EA3818F4BEEA9CE40DAE /* timestamp_test.cc in Sources */,
 				5BE49546D57C43DDFCDB6FBD /* to_string_apple_test.mm in Sources */,
 				E500AB82DF2E7F3AFDB1AB3F /* to_string_test.cc in Sources */,
 				5C9B5696644675636A052018 /* token_test.cc in Sources */,
@@ -5161,6 +5121,7 @@
 				70A171FC43BE328767D1B243 /* path_test.cc in Sources */,
 				EECC1EC64CA963A8376FA55C /* persistence_testing.cc in Sources */,
 				5CDD24225992674A4D3E3D4E /* pipeline.pb.cc in Sources */,
+				E14DBE1D9FC94B5E7E391BEE /* pipeline_util_test.cc in Sources */,
 				34D69886DAD4A2029BFC5C63 /* precondition_test.cc in Sources */,
 				F56E9334642C207D7D85D428 /* pretty_printing_test.cc in Sources */,
 				22A00AC39CAB3426A943E037 /* query.pb.cc in Sources */,
@@ -5440,6 +5401,7 @@
 				B3A309CCF5D75A555C7196E1 /* path_test.cc in Sources */,
 				46EAC2828CD942F27834F497 /* persistence_testing.cc in Sources */,
 				D64792BBFA130E26CB3D1028 /* pipeline.pb.cc in Sources */,
+				563FE05627C7E66469E99292 /* pipeline_util_test.cc in Sources */,
 				9EE1447AA8E68DF98D0590FF /* precondition_test.cc in Sources */,
 				F6079BFC9460B190DA85C2E6 /* pretty_printing_test.cc in Sources */,
 				7B0F073BDB6D0D6E542E23D4 /* query.pb.cc in Sources */,
@@ -5702,6 +5664,7 @@
 				5A080105CCBFDB6BF3F3772D /* path_test.cc in Sources */,
 				21C17F15579341289AD01051 /* persistence_testing.cc in Sources */,
 				C8889F3C37F1CC3E64558287 /* pipeline.pb.cc in Sources */,
+				11A5189E73D954824F015424 /* pipeline_util_test.cc in Sources */,
 				549CCA5920A36E1F00BCEB75 /* precondition_test.cc in Sources */,
 				6A94393D83EB338DFAF6A0D2 /* pretty_printing_test.cc in Sources */,
 				544129DC21C2DDC800EFB9CC /* query.pb.cc in Sources */,
@@ -5748,8 +5711,8 @@
 				8D67BAAD6D2F1913BACA6AC1 /* thread_safe_memoizer_testing.cc in Sources */,
 				BD0882A40BD8AE042629C179 /* thread_safe_memoizer_testing_test.cc in Sources */,
 				5497CB77229DECDE000FB92F /* time_testing.cc in Sources */,
-				3D1365A99984C2F86C2B8A82 /* timestamp_test.cc in Sources */,
 				ABF6506C201131F8005F2C74 /* timestamp_test.cc in Sources */,
+				3D1365A99984C2F86C2B8A82 /* timestamp_test.cc in Sources */,
 				B68B1E012213A765008977EF /* to_string_apple_test.mm in Sources */,
 				B696858E2214B53900271095 /* to_string_test.cc in Sources */,
 				D50232D696F19C2881AC01CE /* token_test.cc in Sources */,
@@ -6000,6 +5963,7 @@
 				6105A1365831B79A7DEEA4F3 /* path_test.cc in Sources */,
 				CB8BEF34CC4A996C7BE85119 /* persistence_testing.cc in Sources */,
 				BC9966788F245D79A63C2E47 /* pipeline.pb.cc in Sources */,
+				7676C06AF7FF67806747E4F0 /* pipeline_util_test.cc in Sources */,
 				4194B7BB8B0352E1AC5D69B9 /* precondition_test.cc in Sources */,
 				0EA40EDACC28F445F9A3F32F /* pretty_printing_test.cc in Sources */,
 				63B91FC476F3915A44F00796 /* query.pb.cc in Sources */,
@@ -6697,7 +6661,6 @@
 		};
 		6EDD3B5920BF247500C33877 /* Debug */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = 756DC5F038E54F8B82B64780 /* Pods-Firestore_FuzzTests_iOS.debug.xcconfig */;
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
 				DEVELOPMENT_TEAM = EQHXZ8M8AV;
@@ -6712,7 +6675,6 @@
 		};
 		6EDD3B5A20BF247500C33877 /* Release */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = 80B9DCD61D9C9A3793248509 /* Pods-Firestore_FuzzTests_iOS.release.xcconfig */;
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
 				DEVELOPMENT_TEAM = EQHXZ8M8AV;

+ 14 - 0
Firestore/Example/Tests/SpecTests/FSTLevelDBSpecTests.mm

@@ -62,4 +62,18 @@ NS_ASSUME_NONNULL_BEGIN
 
 @end
 
+/**
+ * An implementation of FSTLevelDBSpecTests that runs tests in pipeline mode.
+ */
+@interface FSTLevelDBPipelineSpecTests : FSTLevelDBSpecTests
+@end
+
+@implementation FSTLevelDBPipelineSpecTests
+
+- (BOOL)usePipelineMode {
+  return YES;
+}
+
+@end
+
 NS_ASSUME_NONNULL_END

+ 14 - 0
Firestore/Example/Tests/SpecTests/FSTMemorySpecTests.mm

@@ -57,4 +57,18 @@ using firebase::firestore::local::Persistence;
 
 @end
 
+/**
+ * An implementation of FSTMemorySpecTests that runs tests in pipeline mode.
+ */
+@interface FSTMemoryPipelineSpecTests : FSTMemorySpecTests
+@end
+
+@implementation FSTMemoryPipelineSpecTests
+
+- (BOOL)usePipelineMode {
+  return YES;
+}
+
+@end
+
 NS_ASSUME_NONNULL_END

+ 7 - 1
Firestore/Example/Tests/SpecTests/FSTSpecTests.h

@@ -37,7 +37,10 @@ extern NSString *const kDurablePersistence;
  * + Subclass FSTSpecTests
  * + override -persistence to create and return an appropriate Persistence implementation.
  */
-@interface FSTSpecTests : XCTestCase
+@interface FSTSpecTests : XCTestCase {
+ @protected
+  BOOL _convertToPipeline;
+}
 
 /** Based on its tags, determine whether the test case should run. */
 - (BOOL)shouldRunWithTags:(NSArray<NSString *> *)tags;
@@ -45,6 +48,9 @@ extern NSString *const kDurablePersistence;
 /** Do any necessary setup for a single spec test */
 - (void)setUpForSpecWithConfig:(NSDictionary *)config;
 
+/** Determines if tests should run in pipeline mode. Subclasses can override. */
+- (BOOL)usePipelineMode;
+
 @end
 
 NS_ASSUME_NONNULL_END

+ 96 - 13
Firestore/Example/Tests/SpecTests/FSTSpecTests.mm

@@ -158,6 +158,9 @@ static NSString *const kMultiClientTag = @"multi-client";
 // if `kRunBenchmarkTests` is set to 'YES'.
 static NSString *const kBenchmarkTag = @"benchmark";
 
+// A tag for tests that should skip its pipeline run.
+static NSString *const kNoPipelineConversion = @"no-pipeline-conversion";
+
 NSString *const kEagerGC = @"eager-gc";
 
 NSString *const kDurablePersistence = @"durable-persistence";
@@ -236,11 +239,14 @@ NSString *ToTargetIdListString(const ActiveTargetMap &map) {
     return NO;
   } else if (!kRunBenchmarkTests && [tags containsObject:kBenchmarkTag]) {
     return NO;
+  } else if (self.usePipelineMode && [tags containsObject:kNoPipelineConversion]) {
+    return NO;
   }
   return YES;
 }
 
 - (void)setUpForSpecWithConfig:(NSDictionary *)config {
+  _convertToPipeline = [self usePipelineMode];  // Call new method
   _reader = FSTTestUserDataReader();
   std::unique_ptr<Executor> user_executor = Executor::CreateSerial("user executor");
   user_executor_ = absl::ShareUniquePtr(std::move(user_executor));
@@ -261,6 +267,7 @@ NSString *ToTargetIdListString(const ActiveTargetMap &map) {
   self.driver =
       [[FSTSyncEngineTestDriver alloc] initWithPersistence:std::move(persistence)
                                                    eagerGC:_useEagerGCForMemory
+                                         convertToPipeline:_convertToPipeline  // Pass the flag
                                                initialUser:User::Unauthenticated()
                                          outstandingWrites:{}
                              maxConcurrentLimboResolutions:_maxConcurrentLimboResolutions];
@@ -282,6 +289,11 @@ NSString *ToTargetIdListString(const ActiveTargetMap &map) {
   return [self class] == [FSTSpecTests class];
 }
 
+// Default implementation for pipeline mode. Subclasses can override.
+- (BOOL)usePipelineMode {
+  return NO;
+}
+
 #pragma mark - Methods for constructing objects from specs.
 
 - (Query)parseQuery:(id)querySpec {
@@ -645,6 +657,7 @@ NSString *ToTargetIdListString(const ActiveTargetMap &map) {
   self.driver =
       [[FSTSyncEngineTestDriver alloc] initWithPersistence:std::move(persistence)
                                                    eagerGC:_useEagerGCForMemory
+                                         convertToPipeline:_convertToPipeline  // Pass the flag
                                                initialUser:currentUser
                                          outstandingWrites:outstandingWrites
                              maxConcurrentLimboResolutions:_maxConcurrentLimboResolutions];
@@ -721,8 +734,42 @@ NSString *ToTargetIdListString(const ActiveTargetMap &map) {
 }
 
 - (void)validateEvent:(FSTQueryEvent *)actual matches:(NSDictionary *)expected {
-  Query expectedQuery = [self parseQuery:expected[@"query"]];
-  XCTAssertEqual(actual.query, expectedQuery);
+  // The 'expected' query from JSON is always a standard Query.
+  Query expectedJSONQuery = [self parseQuery:expected[@"query"]];
+  core::QueryOrPipeline actualQueryOrPipeline = actual.queryOrPipeline;
+
+  if (_convertToPipeline) {
+    XCTAssertTrue(actualQueryOrPipeline.IsPipeline(),
+                  @"In pipeline mode, actual event query should be a pipeline. Actual: %@",
+                  MakeNSString(actualQueryOrPipeline.ToString()));
+
+    // Convert the expected JSON Query to a RealtimePipeline for comparison.
+    std::vector<std::shared_ptr<api::EvaluableStage>> expectedStages =
+        core::ToPipelineStages(expectedJSONQuery);
+    // TODO(specstest): Need access to the database_id for the serializer.
+    // Assuming self.driver.databaseInfo is accessible and provides it.
+    // This might require making databaseInfo public or providing a getter in
+    // FSTSyncEngineTestDriver. For now, proceeding with the assumption it's available.
+    auto serializer = absl::make_unique<remote::Serializer>(self.driver.databaseInfo.database_id());
+    api::RealtimePipeline expectedPipeline(std::move(expectedStages), std::move(serializer));
+    auto expectedQoPForComparison =
+        core::QueryOrPipeline(expectedPipeline);  // Wrap expected pipeline
+
+    XCTAssertEqual(actualQueryOrPipeline.CanonicalId(), expectedQoPForComparison.CanonicalId(),
+                   @"Pipeline canonical IDs do not match. Actual: %@, Expected: %@",
+                   MakeNSString(actualQueryOrPipeline.CanonicalId()),
+                   MakeNSString(expectedQoPForComparison.CanonicalId()));
+
+  } else {
+    XCTAssertFalse(actualQueryOrPipeline.IsPipeline(),
+                   @"In non-pipeline mode, actual event query should be a Query. Actual: %@",
+                   MakeNSString(actualQueryOrPipeline.ToString()));
+    XCTAssertTrue(actualQueryOrPipeline.query() == expectedJSONQuery,
+                  @"Queries do not match. Actual: %@, Expected: %@",
+                  MakeNSString(actualQueryOrPipeline.query().ToString()),
+                  MakeNSString(expectedJSONQuery.ToString()));
+  }
+
   if ([expected[@"errorCode"] integerValue] != 0) {
     XCTAssertNotNil(actual.error);
     XCTAssertEqual(actual.error.code, [expected[@"errorCode"] integerValue]);
@@ -787,14 +834,43 @@ NSString *ToTargetIdListString(const ActiveTargetMap &map) {
   XCTAssertEqual(events.count, expectedEvents.count);
   events =
       [events sortedArrayUsingComparator:^NSComparisonResult(FSTQueryEvent *q1, FSTQueryEvent *q2) {
-        return WrapCompare(q1.query.CanonicalId(), q2.query.CanonicalId());
-      }];
-  expectedEvents = [expectedEvents
-      sortedArrayUsingComparator:^NSComparisonResult(NSDictionary *left, NSDictionary *right) {
-        Query leftQuery = [self parseQuery:left[@"query"]];
-        Query rightQuery = [self parseQuery:right[@"query"]];
-        return WrapCompare(leftQuery.CanonicalId(), rightQuery.CanonicalId());
+        // Use QueryOrPipeline's CanonicalId for sorting
+        return WrapCompare(q1.queryOrPipeline.CanonicalId(), q2.queryOrPipeline.CanonicalId());
       }];
+  expectedEvents = [expectedEvents sortedArrayUsingComparator:^NSComparisonResult(
+                                       NSDictionary *left, NSDictionary *right) {
+    // Expected query from JSON is always a core::Query.
+    // For sorting consistency with actual events (which might be pipelines),
+    // we convert the expected query to QueryOrPipeline then get its CanonicalId.
+    // If _convertToPipeline is true, this will effectively sort expected items
+    // by their pipeline canonical ID.
+    Query leftJSONQuery = [self parseQuery:left[@"query"]];
+    core::QueryOrPipeline leftQoP;
+    if (self->_convertToPipeline) {
+      std::vector<std::shared_ptr<api::EvaluableStage>> stages =
+          core::ToPipelineStages(leftJSONQuery);
+      auto serializer =
+          absl::make_unique<remote::Serializer>(self.driver.databaseInfo.database_id());
+      leftQoP =
+          core::QueryOrPipeline(api::RealtimePipeline(std::move(stages), std::move(serializer)));
+    } else {
+      leftQoP = core::QueryOrPipeline(leftJSONQuery);
+    }
+
+    Query rightJSONQuery = [self parseQuery:right[@"query"]];
+    core::QueryOrPipeline rightQoP;
+    if (self->_convertToPipeline) {
+      std::vector<std::shared_ptr<api::EvaluableStage>> stages =
+          core::ToPipelineStages(rightJSONQuery);
+      auto serializer =
+          absl::make_unique<remote::Serializer>(self.driver.databaseInfo.database_id());
+      rightQoP =
+          core::QueryOrPipeline(api::RealtimePipeline(std::move(stages), std::move(serializer)));
+    } else {
+      rightQoP = core::QueryOrPipeline(rightJSONQuery);
+    }
+    return WrapCompare(leftQoP.CanonicalId(), rightQoP.CanonicalId());
+  }];
 
   NSUInteger i = 0;
   for (; i < expectedEvents.count && i < events.count; ++i) {
@@ -849,6 +925,7 @@ NSString *ToTargetIdListString(const ActiveTargetMap &map) {
         NSArray *queriesJson = queryData[@"queries"];
         std::vector<TargetData> queries;
         for (id queryJson in queriesJson) {
+          core::QueryOrPipeline qop;
           Query query = [self parseQuery:queryJson];
 
           QueryPurpose purpose = QueryPurpose::Listen;
@@ -980,9 +1057,13 @@ NSString *ToTargetIdListString(const ActiveTargetMap &map) {
     // is ever made to be consistent.
     // XCTAssertEqualObjects(actualTargets[targetID], TargetData);
     const TargetData &actual = found->second;
-
+    auto left = actual.target_or_pipeline();
+    auto left_p = left.IsPipeline();
+    auto right = targetData.target_or_pipeline();
+    auto right_p = right.IsPipeline();
     XCTAssertEqual(actual.purpose(), targetData.purpose());
-    XCTAssertEqual(actual.target_or_pipeline(), targetData.target_or_pipeline());
+    XCTAssertEqual(left_p, right_p);
+    XCTAssertEqual(left, right);
     XCTAssertEqual(actual.target_id(), targetData.target_id());
     XCTAssertEqual(actual.snapshot_version(), targetData.snapshot_version());
     XCTAssertEqual(actual.resume_token(), targetData.resume_token());
@@ -1032,6 +1113,8 @@ NSString *ToTargetIdListString(const ActiveTargetMap &map) {
 - (void)testSpecTests {
   if ([self isTestBaseClass]) return;
 
+  // LogSetLevel(firebase::firestore::util::kLogLevelDebug);
+
   // Enumerate the .json files containing the spec tests.
   NSMutableArray<NSString *> *specFiles = [NSMutableArray array];
   NSMutableArray<NSDictionary *> *parsedSpecs = [NSMutableArray array];
@@ -1121,10 +1204,10 @@ NSString *ToTargetIdListString(const ActiveTargetMap &map) {
         ++testPassCount;
       } else {
         ++testSkipCount;
-        NSLog(@"  [SKIPPED] Spec test: %@", name);
+        // NSLog(@"  [SKIPPED] Spec test: %@", name);
         NSString *comment = testDescription[@"comment"];
         if (comment) {
-          NSLog(@"    %@", comment);
+          // NSLog(@"    %@", comment);
         }
       }
     }];

+ 7 - 2
Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.h

@@ -26,6 +26,7 @@
 #include "Firestore/core/src/api/load_bundle_task.h"
 #include "Firestore/core/src/bundle/bundle_reader.h"
 #include "Firestore/core/src/core/database_info.h"
+#include "Firestore/core/src/core/pipeline_util.h"  // For QueryOrPipeline
 #include "Firestore/core/src/core/query.h"
 #include "Firestore/core/src/core/view_snapshot.h"
 #include "Firestore/core/src/credentials/user.h"
@@ -66,7 +67,7 @@ NS_ASSUME_NONNULL_BEGIN
  * given query.
  */
 @interface FSTQueryEvent : NSObject
-@property(nonatomic, assign) core::Query query;
+@property(nonatomic, assign) core::QueryOrPipeline queryOrPipeline;
 @property(nonatomic, strong, nullable) NSError *error;
 
 - (const absl::optional<core::ViewSnapshot> &)viewSnapshot;
@@ -115,7 +116,10 @@ typedef std::
  *
  * Each method on the driver injects a different event into the system.
  */
-@interface FSTSyncEngineTestDriver : NSObject
+@interface FSTSyncEngineTestDriver : NSObject {
+ @protected
+  BOOL _convertToPipeline;
+}
 
 /**
  * Initializes the underlying FSTSyncEngine with the given local persistence implementation and
@@ -124,6 +128,7 @@ typedef std::
  */
 - (instancetype)initWithPersistence:(std::unique_ptr<local::Persistence>)persistence
                             eagerGC:(BOOL)eagerGC
+                  convertToPipeline:(BOOL)convertToPipeline
                         initialUser:(const credentials::User &)initialUser
                   outstandingWrites:(const FSTOutstandingWriteQueues &)outstandingWrites
       maxConcurrentLimboResolutions:(size_t)maxConcurrentLimboResolutions NS_DESIGNATED_INITIALIZER;

+ 49 - 18
Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.mm

@@ -34,6 +34,7 @@
 #include "Firestore/core/src/core/database_info.h"
 #include "Firestore/core/src/core/event_manager.h"
 #include "Firestore/core/src/core/listen_options.h"
+#include "Firestore/core/src/core/pipeline_util.h"  // Added for ToRealtimePipeline
 #include "Firestore/core/src/core/query_listener.h"
 #include "Firestore/core/src/core/sync_engine.h"
 #include "Firestore/core/src/credentials/empty_credentials_provider.h"
@@ -48,6 +49,7 @@
 #include "Firestore/core/src/remote/firebase_metadata_provider.h"
 #include "Firestore/core/src/remote/firebase_metadata_provider_noop.h"
 #include "Firestore/core/src/remote/remote_store.h"
+#include "Firestore/core/src/remote/serializer.h"  // Added for RealtimePipeline constructor
 #include "Firestore/core/src/util/async_queue.h"
 #include "Firestore/core/src/util/delayed_constructor.h"
 #include "Firestore/core/src/util/error_apple.h"
@@ -200,7 +202,7 @@ NS_ASSUME_NONNULL_BEGIN
   DocumentKeySet _expectedEnqueuedLimboDocuments;
 
   /** A dictionary for tracking the listens on queries. */
-  std::unordered_map<Query, std::shared_ptr<QueryListener>> _queryListeners;
+  std::unordered_map<core::QueryOrPipeline, std::shared_ptr<QueryListener>> _queryListeners;
 
   DatabaseInfo _databaseInfo;
   User _currentUser;
@@ -216,10 +218,12 @@ NS_ASSUME_NONNULL_BEGIN
 
 - (instancetype)initWithPersistence:(std::unique_ptr<Persistence>)persistence
                             eagerGC:(BOOL)eagerGC
+                  convertToPipeline:(BOOL)convertToPipeline
                         initialUser:(const User &)initialUser
                   outstandingWrites:(const FSTOutstandingWriteQueues &)outstandingWrites
       maxConcurrentLimboResolutions:(size_t)maxConcurrentLimboResolutions {
   if (self = [super init]) {
+    _convertToPipeline = convertToPipeline;  // Store the flag
     _maxConcurrentLimboResolutions = maxConcurrentLimboResolutions;
 
     // Do a deep copy.
@@ -477,28 +481,55 @@ NS_ASSUME_NONNULL_BEGIN
 }
 
 - (TargetId)addUserListenerWithQuery:(Query)query options:(ListenOptions)options {
-  // TODO(dimond): Change spec tests to verify isFromCache on snapshots
-  auto listener =
-      QueryListener::Create(core::QueryOrPipeline(query), options,
-                            [self, query](const StatusOr<ViewSnapshot> &maybe_snapshot) {
-                              FSTQueryEvent *event = [[FSTQueryEvent alloc] init];
-                              event.query = query;
-                              if (maybe_snapshot.ok()) {
-                                [event setViewSnapshot:maybe_snapshot.ValueOrDie()];
-                              } else {
-                                event.error = MakeNSError(maybe_snapshot.status());
-                              }
-
-                              [self.events addObject:event];
-                            });
-  _queryListeners[query] = listener;
+  core::QueryOrPipeline qop_for_listen;
+  if (_convertToPipeline) {
+    std::vector<std::shared_ptr<firebase::firestore::api::EvaluableStage>> stages =
+        firebase::firestore::core::ToPipelineStages(query);
+    auto serializer =
+        absl::make_unique<firebase::firestore::remote::Serializer>(_databaseInfo.database_id());
+    firebase::firestore::api::RealtimePipeline pipeline(std::move(stages), std::move(serializer));
+    qop_for_listen = core::QueryOrPipeline(pipeline);
+  } else {
+    qop_for_listen = core::QueryOrPipeline(query);
+  }
+
+  auto listener = QueryListener::Create(
+      qop_for_listen, options,
+      [self, qop_for_listen](const StatusOr<ViewSnapshot> &maybe_snapshot) {
+        FSTQueryEvent *event = [[FSTQueryEvent alloc] init];
+        event.queryOrPipeline = qop_for_listen;  // Event now holds QueryOrPipeline
+        if (maybe_snapshot.ok()) {
+          [event setViewSnapshot:maybe_snapshot.ValueOrDie()];
+        } else {
+          event.error = MakeNSError(maybe_snapshot.status());
+        }
+        [self.events addObject:event];
+      });
+
+  _queryListeners[qop_for_listen] = listener;  // Use QueryOrPipeline as key
   TargetId targetID;
+
+  // The actual call to EventManager still uses the listener based on the original Query.
+  // The expectation is that SyncEngine will be made mode-aware if _convertToPipeline is true,
+  // or that EventManager/QueryListener will be updated to handle QueryOrPipeline directly.
   _workerQueue->EnqueueBlocking([&] { targetID = _eventManager->AddQueryListener(listener); });
   return targetID;
 }
 
-- (void)removeUserListenerWithQuery:(const Query &)query {
-  auto found_iter = _queryListeners.find(query);
+- (void)removeUserListenerWithQuery:(const core::Query &)query {
+  core::QueryOrPipeline qop;
+  if (_convertToPipeline) {
+    std::vector<std::shared_ptr<firebase::firestore::api::EvaluableStage>> stages =
+        firebase::firestore::core::ToPipelineStages(query);
+    auto serializer =
+        absl::make_unique<firebase::firestore::remote::Serializer>(_databaseInfo.database_id());
+    firebase::firestore::api::RealtimePipeline pipeline(std::move(stages), std::move(serializer));
+    qop = core::QueryOrPipeline(pipeline);
+  } else {
+    qop = core::QueryOrPipeline(query);
+  }
+
+  auto found_iter = _queryListeners.find(qop);
   if (found_iter != _queryListeners.end()) {
     std::shared_ptr<QueryListener> listener = found_iter->second;
     _queryListeners.erase(found_iter);

+ 3 - 1
Firestore/Example/Tests/SpecTests/json/bundle_spec_test.json

@@ -3,7 +3,8 @@
     "describeName": "Bundles:",
     "itName": "Bundles query can be loaded and resumed from different tabs",
     "tags": [
-      "multi-client"
+      "multi-client",
+      "no-pipeline-conversion"
     ],
     "config": {
       "numClients": 2,
@@ -225,6 +226,7 @@
     "describeName": "Bundles:",
     "itName": "Bundles query can be resumed from same query.",
     "tags": [
+      "no-pipeline-conversion"
     ],
     "config": {
       "numClients": 1,

+ 2 - 2
Firestore/Example/Tests/SpecTests/json/existence_filter_spec_test.json

@@ -6967,9 +6967,9 @@
       }
     ]
   },
-  "Full re-query is triggered when bloom filter can not identify documents deleted": {
+  "Full re-query is triggered when bloom filter cannot identify documents deleted": {
     "describeName": "Existence Filters:",
-    "itName": "Full re-query is triggered when bloom filter can not identify documents deleted",
+    "itName": "Full re-query is triggered when bloom filter cannot identify documents deleted",
     "tags": [
     ],
     "config": {

+ 8 - 4
Firestore/Example/Tests/SpecTests/json/index_spec_test.json

@@ -71,7 +71,8 @@
                   "readTime": {
                     "timestamp": {
                       "nanoseconds": 0,
-                      "seconds": 0
+                      "seconds": 0,
+                      "type": "firestore/timestamp/1.0"
                     }
                   }
                 },
@@ -115,7 +116,8 @@
                   "readTime": {
                     "timestamp": {
                       "nanoseconds": 0,
-                      "seconds": 0
+                      "seconds": 0,
+                      "type": "firestore/timestamp/1.0"
                     }
                   }
                 },
@@ -192,7 +194,8 @@
                   "readTime": {
                     "timestamp": {
                       "nanoseconds": 0,
-                      "seconds": 0
+                      "seconds": 0,
+                      "type": "firestore/timestamp/1.0"
                     }
                   }
                 },
@@ -236,7 +239,8 @@
                   "readTime": {
                     "timestamp": {
                       "nanoseconds": 0,
-                      "seconds": 0
+                      "seconds": 0,
+                      "type": "firestore/timestamp/1.0"
                     }
                   }
                 },

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1910 - 0
Firestore/Example/Tests/SpecTests/json/limbo_spec_test.json


+ 10 - 8
Firestore/Example/Tests/SpecTests/json/listen_source_spec_test.json

@@ -1603,7 +1603,7 @@
               }
             ],
             "errorCode": 0,
-            "fromCache": true,
+            "fromCache": false,
             "hasPendingWrites": false,
             "query": {
               "filters": [
@@ -1655,7 +1655,7 @@
               }
             ],
             "errorCode": 0,
-            "fromCache": true,
+            "fromCache": false,
             "hasPendingWrites": false,
             "query": {
               "filters": [
@@ -1996,7 +1996,8 @@
     "describeName": "Listens source options:",
     "itName": "Mirror queries being listened from different sources while listening to server in primary tab",
     "tags": [
-      "multi-client"
+      "multi-client",
+      "no-pipeline-conversion"
     ],
     "config": {
       "numClients": 2,
@@ -2211,7 +2212,7 @@
               }
             ],
             "errorCode": 0,
-            "fromCache": true,
+            "fromCache": false,
             "hasPendingWrites": false,
             "query": {
               "filters": [
@@ -3233,7 +3234,8 @@
     "describeName": "Listens source options:",
     "itName": "Mirror queries from different sources while listening to server in secondary tab",
     "tags": [
-      "multi-client"
+      "multi-client",
+      "no-pipeline-conversion"
     ],
     "config": {
       "numClients": 2,
@@ -3482,7 +3484,7 @@
               }
             ],
             "errorCode": 0,
-            "fromCache": true,
+            "fromCache": false,
             "hasPendingWrites": false,
             "query": {
               "filters": [
@@ -5490,7 +5492,7 @@
               }
             ],
             "errorCode": 0,
-            "fromCache": true,
+            "fromCache": false,
             "hasPendingWrites": false,
             "query": {
               "filters": [
@@ -5556,7 +5558,7 @@
               }
             ],
             "errorCode": 0,
-            "fromCache": true,
+            "fromCache": false,
             "hasPendingWrites": true,
             "query": {
               "filters": [

+ 350 - 4
Firestore/Example/Tests/SpecTests/json/listen_spec_test.json

@@ -333,6 +333,7 @@
     "describeName": "Listens:",
     "itName": "Can listen/unlisten to mirror queries.",
     "tags": [
+      "no-pipeline-conversion"
     ],
     "config": {
       "numClients": 1,
@@ -3534,6 +3535,345 @@
       }
     ]
   },
+  "Global snapshots would not alter query state if there is no changes": {
+    "describeName": "Listens:",
+    "itName": "Global snapshots would not alter query state if there is no changes",
+    "tags": [
+      "multi-client"
+    ],
+    "config": {
+      "numClients": 2,
+      "useEagerGCForMemory": false
+    },
+    "steps": [
+      {
+        "clientIndex": 0,
+        "drainQueue": true
+      },
+      {
+        "applyClientState": {
+          "visibility": "visible"
+        },
+        "clientIndex": 0,
+        "expectedState": {
+          "isPrimary": true
+        }
+      },
+      {
+        "clientIndex": 0,
+        "userListen": {
+          "query": {
+            "filters": [
+            ],
+            "orderBys": [
+            ],
+            "path": "collection"
+          },
+          "targetId": 2
+        },
+        "expectedState": {
+          "activeTargets": {
+            "2": {
+              "queries": [
+                {
+                  "filters": [
+                  ],
+                  "orderBys": [
+                  ],
+                  "path": "collection"
+                }
+              ],
+              "resumeToken": ""
+            }
+          }
+        }
+      },
+      {
+        "clientIndex": 0,
+        "watchAck": [
+          2
+        ]
+      },
+      {
+        "clientIndex": 0,
+        "watchEntity": {
+          "docs": [
+            {
+              "createTime": 0,
+              "key": "collection/a",
+              "options": {
+                "hasCommittedMutations": false,
+                "hasLocalMutations": false
+              },
+              "value": {
+                "key": "a"
+              },
+              "version": 1000
+            }
+          ],
+          "targets": [
+            2
+          ]
+        }
+      },
+      {
+        "clientIndex": 0,
+        "watchCurrent": [
+          [
+            2
+          ],
+          "resume-token-1000"
+        ]
+      },
+      {
+        "clientIndex": 0,
+        "watchSnapshot": {
+          "targetIds": [
+          ],
+          "version": 1000
+        },
+        "expectedSnapshotEvents": [
+          {
+            "added": [
+              {
+                "createTime": 0,
+                "key": "collection/a",
+                "options": {
+                  "hasCommittedMutations": false,
+                  "hasLocalMutations": false
+                },
+                "value": {
+                  "key": "a"
+                },
+                "version": 1000
+              }
+            ],
+            "errorCode": 0,
+            "fromCache": false,
+            "hasPendingWrites": false,
+            "query": {
+              "filters": [
+              ],
+              "orderBys": [
+              ],
+              "path": "collection"
+            }
+          }
+        ]
+      },
+      {
+        "clientIndex": 0,
+        "userUnlisten": [
+          2,
+          {
+            "filters": [
+            ],
+            "orderBys": [
+            ],
+            "path": "collection"
+          }
+        ],
+        "expectedState": {
+          "activeTargets": {
+          }
+        }
+      },
+      {
+        "clientIndex": 0,
+        "watchRemove": {
+          "targetIds": [
+            2
+          ]
+        }
+      },
+      {
+        "clientIndex": 0,
+        "userListen": {
+          "query": {
+            "filters": [
+            ],
+            "orderBys": [
+            ],
+            "path": "collection"
+          },
+          "targetId": 2
+        },
+        "expectedSnapshotEvents": [
+          {
+            "added": [
+              {
+                "createTime": 0,
+                "key": "collection/a",
+                "options": {
+                  "hasCommittedMutations": false,
+                  "hasLocalMutations": false
+                },
+                "value": {
+                  "key": "a"
+                },
+                "version": 1000
+              }
+            ],
+            "errorCode": 0,
+            "fromCache": true,
+            "hasPendingWrites": false,
+            "query": {
+              "filters": [
+              ],
+              "orderBys": [
+              ],
+              "path": "collection"
+            }
+          }
+        ],
+        "expectedState": {
+          "activeTargets": {
+            "2": {
+              "queries": [
+                {
+                  "filters": [
+                  ],
+                  "orderBys": [
+                  ],
+                  "path": "collection"
+                }
+              ],
+              "resumeToken": "resume-token-1000"
+            }
+          }
+        }
+      },
+      {
+        "clientIndex": 0,
+        "watchAck": [
+          2
+        ]
+      },
+      {
+        "clientIndex": 0,
+        "watchEntity": {
+          "docs": [
+            {
+              "createTime": 0,
+              "key": "collection/a",
+              "options": {
+                "hasCommittedMutations": false,
+                "hasLocalMutations": false
+              },
+              "value": {
+                "key": "a"
+              },
+              "version": 1000
+            }
+          ],
+          "targets": [
+            2
+          ]
+        }
+      },
+      {
+        "clientIndex": 0,
+        "watchCurrent": [
+          [
+            2
+          ],
+          "resume-token-2000"
+        ]
+      },
+      {
+        "clientIndex": 0,
+        "watchSnapshot": {
+          "targetIds": [
+          ],
+          "version": 2000
+        },
+        "expectedSnapshotEvents": [
+          {
+            "errorCode": 0,
+            "fromCache": false,
+            "hasPendingWrites": false,
+            "query": {
+              "filters": [
+              ],
+              "orderBys": [
+              ],
+              "path": "collection"
+            }
+          }
+        ]
+      },
+      {
+        "clientIndex": 0,
+        "watchSnapshot": {
+          "resumeToken": "resume-token-3000",
+          "targetIds": [
+          ],
+          "version": 3000
+        }
+      },
+      {
+        "clientIndex": 1,
+        "drainQueue": true
+      },
+      {
+        "clientIndex": 1,
+        "userListen": {
+          "query": {
+            "filters": [
+            ],
+            "orderBys": [
+            ],
+            "path": "collection"
+          },
+          "targetId": 2
+        },
+        "expectedSnapshotEvents": [
+          {
+            "added": [
+              {
+                "createTime": 0,
+                "key": "collection/a",
+                "options": {
+                  "hasCommittedMutations": false,
+                  "hasLocalMutations": false
+                },
+                "value": {
+                  "key": "a"
+                },
+                "version": 1000
+              }
+            ],
+            "errorCode": 0,
+            "fromCache": false,
+            "hasPendingWrites": false,
+            "query": {
+              "filters": [
+              ],
+              "orderBys": [
+              ],
+              "path": "collection"
+            }
+          }
+        ],
+        "expectedState": {
+          "activeTargets": {
+            "2": {
+              "queries": [
+                {
+                  "filters": [
+                  ],
+                  "orderBys": [
+                  ],
+                  "path": "collection"
+                }
+              ],
+              "resumeToken": ""
+            }
+          }
+        }
+      }
+    ]
+  },
   "Ignores update from inactive target": {
     "describeName": "Listens:",
     "itName": "Ignores update from inactive target",
@@ -5984,7 +6324,8 @@
     "describeName": "Listens:",
     "itName": "Mirror queries from different secondary client",
     "tags": [
-      "multi-client"
+      "multi-client",
+      "no-pipeline-conversion"
     ],
     "config": {
       "numClients": 3,
@@ -6424,7 +6765,8 @@
     "describeName": "Listens:",
     "itName": "Mirror queries from primary and secondary client",
     "tags": [
-      "multi-client"
+      "multi-client",
+      "no-pipeline-conversion"
     ],
     "config": {
       "numClients": 2,
@@ -7136,7 +7478,8 @@
     "describeName": "Listens:",
     "itName": "Mirror queries from same secondary client",
     "tags": [
-      "multi-client"
+      "multi-client",
+      "no-pipeline-conversion"
     ],
     "config": {
       "numClients": 2,
@@ -13270,7 +13613,10 @@
     "describeName": "Listens:",
     "itName": "Secondary client advances query state with global snapshot from primary",
     "tags": [
-      "multi-client"
+      "multi-client",
+      "no-web",
+      "no-ios",
+      "no-android"
     ],
     "config": {
       "numClients": 2,

+ 318 - 0
Firestore/Example/Tests/SpecTests/json/query_spec_test.json

@@ -1617,5 +1617,323 @@
         }
       }
     ]
+  },
+  "Queries in different tabs will not interfere": {
+    "describeName": "Queries:",
+    "itName": "Queries in different tabs will not interfere",
+    "tags": [
+      "multi-client"
+    ],
+    "config": {
+      "numClients": 2,
+      "useEagerGCForMemory": false
+    },
+    "steps": [
+      {
+        "clientIndex": 0,
+        "drainQueue": true
+      },
+      {
+        "applyClientState": {
+          "visibility": "visible"
+        },
+        "clientIndex": 0,
+        "expectedState": {
+          "isPrimary": true
+        }
+      },
+      {
+        "clientIndex": 0,
+        "userListen": {
+          "query": {
+            "filters": [
+              [
+                "key",
+                "==",
+                "a"
+              ]
+            ],
+            "orderBys": [
+            ],
+            "path": "collection"
+          },
+          "targetId": 2
+        },
+        "expectedState": {
+          "activeTargets": {
+            "2": {
+              "queries": [
+                {
+                  "filters": [
+                    [
+                      "key",
+                      "==",
+                      "a"
+                    ]
+                  ],
+                  "orderBys": [
+                  ],
+                  "path": "collection"
+                }
+              ],
+              "resumeToken": ""
+            }
+          }
+        }
+      },
+      {
+        "clientIndex": 0,
+        "watchAck": [
+          2
+        ]
+      },
+      {
+        "clientIndex": 0,
+        "watchEntity": {
+          "docs": [
+            {
+              "createTime": 0,
+              "key": "collection/a",
+              "options": {
+                "hasCommittedMutations": false,
+                "hasLocalMutations": false
+              },
+              "value": {
+                "key": "a"
+              },
+              "version": 1000
+            }
+          ],
+          "targets": [
+            2
+          ]
+        }
+      },
+      {
+        "clientIndex": 1,
+        "drainQueue": true
+      },
+      {
+        "clientIndex": 1,
+        "userListen": {
+          "query": {
+            "filters": [
+              [
+                "key",
+                "==",
+                "b"
+              ]
+            ],
+            "orderBys": [
+            ],
+            "path": "collection"
+          },
+          "targetId": 4
+        },
+        "expectedState": {
+          "activeTargets": {
+            "4": {
+              "queries": [
+                {
+                  "filters": [
+                    [
+                      "key",
+                      "==",
+                      "b"
+                    ]
+                  ],
+                  "orderBys": [
+                  ],
+                  "path": "collection"
+                }
+              ],
+              "resumeToken": ""
+            }
+          }
+        }
+      },
+      {
+        "clientIndex": 0,
+        "drainQueue": true,
+        "expectedState": {
+          "activeTargets": {
+            "2": {
+              "queries": [
+                {
+                  "filters": [
+                    [
+                      "key",
+                      "==",
+                      "a"
+                    ]
+                  ],
+                  "orderBys": [
+                  ],
+                  "path": "collection"
+                }
+              ],
+              "resumeToken": ""
+            },
+            "4": {
+              "queries": [
+                {
+                  "filters": [
+                    [
+                      "key",
+                      "==",
+                      "b"
+                    ]
+                  ],
+                  "orderBys": [
+                  ],
+                  "path": "collection"
+                }
+              ],
+              "resumeToken": ""
+            }
+          }
+        }
+      },
+      {
+        "clientIndex": 0,
+        "watchCurrent": [
+          [
+            2
+          ],
+          "resume-token-1000"
+        ]
+      },
+      {
+        "clientIndex": 0,
+        "watchSnapshot": {
+          "targetIds": [
+          ],
+          "version": 1000
+        },
+        "expectedSnapshotEvents": [
+          {
+            "added": [
+              {
+                "createTime": 0,
+                "key": "collection/a",
+                "options": {
+                  "hasCommittedMutations": false,
+                  "hasLocalMutations": false
+                },
+                "value": {
+                  "key": "a"
+                },
+                "version": 1000
+              }
+            ],
+            "errorCode": 0,
+            "fromCache": false,
+            "hasPendingWrites": false,
+            "query": {
+              "filters": [
+                [
+                  "key",
+                  "==",
+                  "a"
+                ]
+              ],
+              "orderBys": [
+              ],
+              "path": "collection"
+            }
+          }
+        ]
+      },
+      {
+        "clientIndex": 1,
+        "drainQueue": true
+      },
+      {
+        "clientIndex": 0,
+        "drainQueue": true
+      },
+      {
+        "clientIndex": 0,
+        "watchAck": [
+          4
+        ]
+      },
+      {
+        "clientIndex": 0,
+        "watchEntity": {
+          "docs": [
+            {
+              "createTime": 0,
+              "key": "collection/b",
+              "options": {
+                "hasCommittedMutations": false,
+                "hasLocalMutations": false
+              },
+              "value": {
+                "key": "b"
+              },
+              "version": 1000
+            }
+          ],
+          "targets": [
+            4
+          ]
+        }
+      },
+      {
+        "clientIndex": 0,
+        "watchCurrent": [
+          [
+            4
+          ],
+          "resume-token-2000"
+        ]
+      },
+      {
+        "clientIndex": 0,
+        "watchSnapshot": {
+          "targetIds": [
+          ],
+          "version": 2000
+        }
+      },
+      {
+        "clientIndex": 1,
+        "drainQueue": true,
+        "expectedSnapshotEvents": [
+          {
+            "added": [
+              {
+                "createTime": 0,
+                "key": "collection/b",
+                "options": {
+                  "hasCommittedMutations": false,
+                  "hasLocalMutations": false
+                },
+                "value": {
+                  "key": "b"
+                },
+                "version": 1000
+              }
+            ],
+            "errorCode": 0,
+            "fromCache": false,
+            "hasPendingWrites": false,
+            "query": {
+              "filters": [
+                [
+                  "key",
+                  "==",
+                  "b"
+                ]
+              ],
+              "orderBys": [
+              ],
+              "path": "collection"
+            }
+          }
+        ]
+      }
+    ]
   }
 }

+ 1 - 9
Firestore/core/src/core/event_manager.cc

@@ -37,15 +37,11 @@ EventManager::EventManager(QueryEventSource* query_event_source)
 model::TargetId EventManager::AddQueryListener(
     std::shared_ptr<core::QueryListener> listener) {
   const QueryOrPipeline& query_or_pipeline = listener->query();
-  if (query_or_pipeline.IsPipeline()) {
-    HARD_FAIL("Unimplemented");
-  }
 
-  const auto& query = query_or_pipeline.query();
   ListenerSetupAction listener_action =
       ListenerSetupAction::NoSetupActionRequired;
 
-  auto inserted = queries_.emplace(query, QueryListenersInfo{});
+  auto inserted = queries_.emplace(query_or_pipeline, QueryListenersInfo{});
   // If successfully inserted, it means we haven't listened to this query
   // before.
   bool first_listen = inserted.second;
@@ -98,10 +94,6 @@ model::TargetId EventManager::AddQueryListener(
 void EventManager::RemoveQueryListener(
     std::shared_ptr<core::QueryListener> listener) {
   const auto& query_or_pipeline = listener->query();
-  if (query_or_pipeline.IsPipeline()) {
-    HARD_FAIL("Unimplemented");
-  }
-
   ListenerRemovalAction listener_action =
       ListenerRemovalAction::NoRemovalActionRequired;
 

+ 3 - 3
Firestore/core/src/core/pipeline_util.cc

@@ -335,11 +335,11 @@ bool QueryOrPipeline::Matches(const model::Document& doc) const {
 model::DocumentComparator QueryOrPipeline::Comparator() const {
   if (IsPipeline()) {
     // Capture pipeline by reference. Orderings captured by value inside lambda.
-    const auto& p = pipeline();
+    const api::RealtimePipeline& p = pipeline();
     const auto& orderings = GetLastEffectiveSortOrderings(p);
     return model::DocumentComparator(
-        [&p, &orderings](const model::Document& d1,
-                         const model::Document& d2) -> util::ComparisonResult {
+        [p, orderings](const model::Document& d1,
+                       const model::Document& d2) -> util::ComparisonResult {
           auto context =
               const_cast<api::RealtimePipeline&>(p).evaluate_context();
 

+ 52 - 16
Firestore/core/src/remote/remote_event.cc

@@ -237,11 +237,44 @@ create_existence_filter_mismatch_info_for_testing_hooks(
           std::move(bloom_filter_info)};
 }
 
-bool IsSingleDocumentTarget(const core::TargetOrPipeline target_or_pipeline) {
-  // TODO(pipeline): We only handle the non-pipeline case because realtime
-  // pipeline does not support single document lookup yet.
-  return !target_or_pipeline.IsPipeline() &&
-         target_or_pipeline.target().IsDocumentQuery();
+absl::optional<model::ResourcePath> GetSingleDocumentPath(
+    const core::TargetOrPipeline target_or_pipeline) {
+  if (target_or_pipeline.IsPipeline()) {
+    if (core::GetPipelineSourceType(target_or_pipeline.pipeline()) ==
+        core::PipelineSourceType::kDocuments) {
+      const auto& documents =
+          core::GetPipelineDocuments(target_or_pipeline.pipeline());
+      if (documents.has_value() && documents.value().size() == 1) {
+        return model::ResourcePath::FromString(documents.value()[0]);
+      }
+    }
+  } else if (target_or_pipeline.target().IsDocumentQuery()) {
+    return target_or_pipeline.target().path();
+  }
+
+  return absl::nullopt;
+}
+
+absl::optional<std::vector<model::ResourcePath>> GetDocumentPaths(
+    const core::TargetOrPipeline target_or_pipeline) {
+  if (target_or_pipeline.IsPipeline()) {
+    if (core::GetPipelineSourceType(target_or_pipeline.pipeline()) ==
+        core::PipelineSourceType::kDocuments) {
+      const auto& documents =
+          core::GetPipelineDocuments(target_or_pipeline.pipeline());
+      if (documents.has_value()) {
+        std::vector<model::ResourcePath> results;
+        for (const std::string& document : documents.value()) {
+          results.push_back(model::ResourcePath::FromString(document));
+        }
+        return results;
+      }
+    }
+  } else if (target_or_pipeline.target().IsDocumentQuery()) {
+    return std::vector<model::ResourcePath>{target_or_pipeline.target().path()};
+  }
+
+  return absl::nullopt;
 }
 
 }  // namespace
@@ -256,7 +289,8 @@ void WatchChangeAggregator::HandleExistenceFilter(
     const core::TargetOrPipeline& target_or_pipeline =
         target_data->target_or_pipeline();
 
-    if (!IsSingleDocumentTarget(target_or_pipeline)) {
+    auto single_doc_path = GetSingleDocumentPath(target_or_pipeline);
+    if (!single_doc_path.has_value()) {
       int current_size = GetCurrentDocumentCountForTarget(target_id);
       if (current_size != expected_count) {
         // Apply bloom filter to identify and mark removed documents.
@@ -292,7 +326,7 @@ void WatchChangeAggregator::HandleExistenceFilter(
         // another query that will raise this document as part of a snapshot
         // until it is resolved, essentially exposing inconsistency between
         // queries.
-        DocumentKey key{target_or_pipeline.target().path()};
+        DocumentKey key{std::move(single_doc_path.value())};
         RemoveDocumentFromTarget(
             target_id, key,
             MutableDocument::NoDocument(key, SnapshotVersion::None()));
@@ -377,20 +411,22 @@ RemoteEvent WatchChangeAggregator::CreateRemoteEvent(
     absl::optional<TargetData> target_data =
         TargetDataForActiveTarget(target_id);
     if (target_data) {
-      if (target_state.current() &&
-          IsSingleDocumentTarget(target_data->target_or_pipeline())) {
+      auto doc_paths = GetDocumentPaths(target_data->target_or_pipeline());
+      if (target_state.current() && doc_paths.has_value()) {
         // Document queries for document that don't exist can produce an empty
         // result set. To update our local cache, we synthesize a document
         // delete if we have not previously received the document. This resolves
         // the limbo state of the document, removing it from
         // SyncEngine::limbo_document_refs_.
-        DocumentKey key{target_data->target_or_pipeline().target().path()};
-        if (pending_document_updates_.find(key) ==
-                pending_document_updates_.end() &&
-            !TargetContainsDocument(target_id, key)) {
-          RemoveDocumentFromTarget(
-              target_id, key,
-              MutableDocument::NoDocument(key, snapshot_version));
+        for (const model::ResourcePath& single_doc_path : doc_paths.value()) {
+          DocumentKey key{std::move(single_doc_path)};
+          if (pending_document_updates_.find(key) ==
+                  pending_document_updates_.end() &&
+              !TargetContainsDocument(target_id, key)) {
+            RemoveDocumentFromTarget(
+                target_id, key,
+                MutableDocument::NoDocument(key, snapshot_version));
+          }
         }
       }
 

+ 2 - 2
Firestore/core/src/remote/serializer.cc

@@ -1622,8 +1622,8 @@ std::unique_ptr<api::EvaluableStage> Serializer::DecodeStage(
     document_paths.reserve(args_count);
     for (pb_size_t i = 0; i < args_count; ++i) {
       if (current_args[i].which_value_type ==
-          google_firestore_v1_Value_string_value_tag) {
-        document_paths.push_back(DecodeString(current_args[i].string_value));
+          google_firestore_v1_Value_reference_value_tag) {
+        document_paths.push_back(DecodeString(current_args[i].reference_value));
       } else {
         context->Fail(StringFormat(
             "Invalid argument type for 'documents' stage at index %zu: "

+ 9 - 9
Firestore/core/test/unit/core/pipeline/canonify_eq_test.cc

@@ -75,7 +75,7 @@ using testutil::EqExpr;
 
 // Helper to get canonical ID directly for RealtimePipeline
 std::string GetPipelineCanonicalId(const RealtimePipeline& pipeline) {
-  QueryOrPipeline variant = pipeline;
+  QueryOrPipeline variant = QueryOrPipeline(pipeline);
   // Use the specific helper for QueryOrPipeline canonicalization
   return variant.CanonicalId();
 }
@@ -246,8 +246,8 @@ TEST_F(CanonifyEqPipelineTest, EqReturnsTrueForIdenticalPipelines) {
   p2 = p2.AddingStage(std::make_shared<Where>(EqExpr(
       {std::make_shared<api::Field>("foo"), SharedConstant(Value(42LL))})));
 
-  QueryOrPipeline v1 = p1;
-  QueryOrPipeline v2 = p2;
+  QueryOrPipeline v1 = QueryOrPipeline(p1);
+  QueryOrPipeline v2 = QueryOrPipeline(p2);
   EXPECT_TRUE(v1 == v2);  // Expect TRUE based on TS
 }
 
@@ -259,8 +259,8 @@ TEST_F(CanonifyEqPipelineTest, EqReturnsFalseForDifferentStages) {
   RealtimePipeline p2 = StartPipeline("test");
   p2 = p2.AddingStage(std::make_shared<LimitStage>(10));
 
-  QueryOrPipeline v1 = p1;
-  QueryOrPipeline v2 = p2;
+  QueryOrPipeline v1 = QueryOrPipeline(p1);
+  QueryOrPipeline v2 = QueryOrPipeline(p2);
   EXPECT_FALSE(v1 == v2);  // Expect FALSE based on TS
 }
 
@@ -274,8 +274,8 @@ TEST_F(CanonifyEqPipelineTest, EqReturnsFalseForDifferentParamsInStage) {
       EqExpr({std::make_shared<api::Field>("bar"),
               SharedConstant(Value(42LL))})));  // Different field
 
-  QueryOrPipeline v1 = p1;
-  QueryOrPipeline v2 = p2;
+  QueryOrPipeline v1 = QueryOrPipeline(p1);
+  QueryOrPipeline v2 = QueryOrPipeline(p2);
   EXPECT_FALSE(v1 == v2);  // Expect FALSE based on TS
 }
 
@@ -290,8 +290,8 @@ TEST_F(CanonifyEqPipelineTest, EqReturnsFalseForDifferentStageOrder) {
   p2 = p2.AddingStage(std::make_shared<Where>(EqExpr(
       {std::make_shared<api::Field>("foo"), SharedConstant(Value(42LL))})));
 
-  QueryOrPipeline v1 = p1;
-  QueryOrPipeline v2 = p2;
+  QueryOrPipeline v1 = QueryOrPipeline(p1);
+  QueryOrPipeline v2 = QueryOrPipeline(p2);
   EXPECT_FALSE(v1 == v2);  // Expect FALSE based on TS
 }
 

Vissa filer visades inte eftersom för många filer har ändrats