Selaa lähdekoodia

Improve the integration test coverage for online vs offline comparisons. (#14707)

Ehsan 11 kuukautta sitten
vanhempi
sitoutus
08e129f179

+ 29 - 12
Firestore/Example/Tests/Integration/API/FIRCompositeIndexQueryTests.mm

@@ -122,9 +122,16 @@ static NSString *const COMPOSITE_INDEX_TEST_COLLECTION = @"composite-index-test-
 // Asserts that the result of running the query while online (against the backend/emulator) is
 // the same as running it while offline. The expected document Ids are hashed to match the
 // actual document IDs created by the test helper.
-- (void)assertOnlineAndOfflineResultsMatch:(FIRQuery *)query
+- (void)assertOnlineAndOfflineResultsMatch:(FIRCollectionReference *)collection
+                                 withQuery:(FIRQuery *)query
                               expectedDocs:(NSArray<NSString *> *)expectedDocs {
-  [self checkOnlineAndOfflineQuery:query matchesResult:[self toHashedIds:expectedDocs]];
+  // `checkOnlineAndOfflineCollection` first makes sure all documents needed for
+  // `query` are in the cache. It does so making a `get` on the first argument.
+  // Since *all* composite index tests use the same collection, this is very inefficient to do.
+  // Therefore, we should only do so for tests where `TEST_ID_FIELD` matches the current test.
+  [self checkOnlineAndOfflineCollection:[self compositeIndexQuery:collection]
+                                  query:query
+                          matchesResult:[self toHashedIds:expectedDocs]];
 }
 
 // Asserts that the IDs in the query snapshot matches the expected Ids. The expected document
@@ -219,7 +226,8 @@ static NSString *const COMPOSITE_INDEX_TEST_COLLECTION = @"composite-index-test-
         [FIRFilter filterWhereField:@"a" isGreaterThan:@2], [FIRFilter filterWhereField:@"b"
                                                                               isEqualTo:@1]
       ]]];
-  [self assertOnlineAndOfflineResultsMatch:[self compositeIndexQuery:query1]
+  [self assertOnlineAndOfflineResultsMatch:collRef
+                                 withQuery:[self compositeIndexQuery:query1]
                               expectedDocs:@[ @"doc5", @"doc2", @"doc3" ]];
 
   // Test with limits (implicit order by ASC): (a==1) || (b > 0) LIMIT 2
@@ -228,7 +236,8 @@ static NSString *const COMPOSITE_INDEX_TEST_COLLECTION = @"composite-index-test-
                  [FIRFilter filterWhereField:@"a" isEqualTo:@1], [FIRFilter filterWhereField:@"b"
                                                                                isGreaterThan:@0]
                ]]];
-  [self assertOnlineAndOfflineResultsMatch:[[self compositeIndexQuery:query2] queryLimitedTo:2]
+  [self assertOnlineAndOfflineResultsMatch:collRef
+                                 withQuery:[[self compositeIndexQuery:query2] queryLimitedTo:2]
                               expectedDocs:@[ @"doc1", @"doc2" ]];
 
   // Test with limits (explicit order by): (a==1) || (b > 0) LIMIT_TO_LAST 2
@@ -238,7 +247,8 @@ static NSString *const COMPOSITE_INDEX_TEST_COLLECTION = @"composite-index-test-
                  [FIRFilter filterWhereField:@"a" isEqualTo:@1], [FIRFilter filterWhereField:@"b"
                                                                                isGreaterThan:@0]
                ]]];
-  [self assertOnlineAndOfflineResultsMatch:[[[self compositeIndexQuery:query3] queryLimitedToLast:2]
+  [self assertOnlineAndOfflineResultsMatch:collRef
+                                 withQuery:[[[self compositeIndexQuery:query3] queryLimitedToLast:2]
                                                queryOrderedByField:@"b"]
                               expectedDocs:@[ @"doc3", @"doc4" ]];
 
@@ -248,7 +258,8 @@ static NSString *const COMPOSITE_INDEX_TEST_COLLECTION = @"composite-index-test-
                  [FIRFilter filterWhereField:@"a" isEqualTo:@2], [FIRFilter filterWhereField:@"b"
                                                                                    isEqualTo:@1]
                ]]];
-  [self assertOnlineAndOfflineResultsMatch:[[[self compositeIndexQuery:query4] queryLimitedTo:1]
+  [self assertOnlineAndOfflineResultsMatch:collRef
+                                 withQuery:[[[self compositeIndexQuery:query4] queryLimitedTo:1]
                                                queryOrderedByField:@"a"]
                               expectedDocs:@[ @"doc5" ]];
 
@@ -258,7 +269,8 @@ static NSString *const COMPOSITE_INDEX_TEST_COLLECTION = @"composite-index-test-
                  [FIRFilter filterWhereField:@"a" isEqualTo:@2], [FIRFilter filterWhereField:@"b"
                                                                                    isEqualTo:@1]
                ]]];
-  [self assertOnlineAndOfflineResultsMatch:[[[self compositeIndexQuery:query5] queryLimitedToLast:1]
+  [self assertOnlineAndOfflineResultsMatch:collRef
+                                 withQuery:[[[self compositeIndexQuery:query5] queryLimitedToLast:1]
                                                queryOrderedByField:@"a"]
                               expectedDocs:@[ @"doc2" ]];
 }
@@ -886,7 +898,8 @@ static NSString *const COMPOSITE_INDEX_TEST_COLLECTION = @"composite-index-test-
   // implicit AND: a != 1 && b < 2
   FIRQuery *query = [[collRef queryWhereField:@"a" isNotEqualTo:@1] queryWhereField:@"b"
                                                                          isLessThan:@2];
-  [self assertOnlineAndOfflineResultsMatch:[self compositeIndexQuery:query]
+  [self assertOnlineAndOfflineResultsMatch:collRef
+                                 withQuery:[self compositeIndexQuery:query]
                               expectedDocs:@[ @"doc2" ]];
 
   // explicit AND: a != 1 && b < 2
@@ -895,7 +908,8 @@ static NSString *const COMPOSITE_INDEX_TEST_COLLECTION = @"composite-index-test-
                  [FIRFilter filterWhereField:@"a" isNotEqualTo:@1], [FIRFilter filterWhereField:@"b"
                                                                                      isLessThan:@2]
                ]]];
-  [self assertOnlineAndOfflineResultsMatch:[self compositeIndexQuery:query]
+  [self assertOnlineAndOfflineResultsMatch:collRef
+                                 withQuery:[self compositeIndexQuery:query]
                               expectedDocs:@[ @"doc2" ]];
 
   // explicit AND: a < 3 && b not-in [2, 3]
@@ -905,14 +919,16 @@ static NSString *const COMPOSITE_INDEX_TEST_COLLECTION = @"composite-index-test-
         [FIRFilter filterWhereField:@"a" isLessThan:@3], [FIRFilter filterWhereField:@"b"
                                                                                notIn:@[ @2, @3 ]]
       ]]];
-  [self assertOnlineAndOfflineResultsMatch:[self compositeIndexQuery:query]
+  [self assertOnlineAndOfflineResultsMatch:collRef
+                                 withQuery:[self compositeIndexQuery:query]
                               expectedDocs:@[ @"doc1", @"doc5", @"doc2" ]];
 
   // a <3 && b != 0, ordered by: b desc, a desc, __name__ desc
   query = [[[[collRef queryWhereField:@"a" isLessThan:@3] queryWhereField:@"b" isNotEqualTo:@0]
       queryOrderedByField:@"b"
                descending:YES] queryLimitedTo:2];
-  [self assertOnlineAndOfflineResultsMatch:[self compositeIndexQuery:query]
+  [self assertOnlineAndOfflineResultsMatch:collRef
+                                 withQuery:[self compositeIndexQuery:query]
                               expectedDocs:@[ @"doc4", @"doc2" ]];
 
   // explicit OR: a>2 || b<1.
@@ -921,7 +937,8 @@ static NSString *const COMPOSITE_INDEX_TEST_COLLECTION = @"composite-index-test-
         [FIRFilter filterWhereField:@"a" isGreaterThan:@2], [FIRFilter filterWhereField:@"b"
                                                                              isLessThan:@1]
       ]]];
-  [self assertOnlineAndOfflineResultsMatch:[self compositeIndexQuery:query]
+  [self assertOnlineAndOfflineResultsMatch:collRef
+                                 withQuery:[self compositeIndexQuery:query]
                               expectedDocs:@[ @"doc1", @"doc3" ]];
 }
 

+ 94 - 65
Firestore/Example/Tests/Integration/API/FIRQueryTests.mm

@@ -555,15 +555,18 @@
   // populate cache with all documents first to ensure getDocsFromCache() scans all docs
   [self readDocumentSetForRef:collection];
 
-  [self checkOnlineAndOfflineQuery:[collection queryWhereField:@"zip" isNotEqualTo:@98101]
-                     matchesResult:@[ @"a", @"b", @"d", @"e", @"f", @"g", @"h" ]];
-
-  [self checkOnlineAndOfflineQuery:[collection queryWhereField:@"zip" isNotEqualTo:@(NAN)]
-                     matchesResult:@[ @"b", @"c", @"d", @"e", @"f", @"g", @"h" ]];
-
-  [self checkOnlineAndOfflineQuery:[collection queryWhereField:@"zip"
-                                                  isNotEqualTo:@[ [NSNull null] ]]
-                     matchesResult:@[ @"a", @"b", @"c", @"d", @"e", @"f", @"g", @"h" ]];
+  [self checkOnlineAndOfflineCollection:collection
+                                  query:[collection queryWhereField:@"zip" isNotEqualTo:@98101]
+                          matchesResult:@[ @"a", @"b", @"d", @"e", @"f", @"g", @"h" ]];
+
+  [self checkOnlineAndOfflineCollection:collection
+                                  query:[collection queryWhereField:@"zip" isNotEqualTo:@(NAN)]
+                          matchesResult:@[ @"b", @"c", @"d", @"e", @"f", @"g", @"h" ]];
+
+  [self checkOnlineAndOfflineCollection:collection
+                                  query:[collection queryWhereField:@"zip"
+                                                       isNotEqualTo:@[ [NSNull null] ]]
+                          matchesResult:@[ @"a", @"b", @"c", @"d", @"e", @"f", @"g", @"h" ]];
 }
 
 - (void)testQueriesCanUseArrayContainsFilters {
@@ -741,13 +744,16 @@
   // populate cache with all documents first to ensure getDocsFromCache() scans all docs
   [self readDocumentSetForRef:collection];
 
-  [self checkOnlineAndOfflineQuery:[collection
-                                       queryWhereField:@"zip"
-                                                 notIn:@[ @98101, @98103, @[ @98101, @98102 ] ]]
-                     matchesResult:@[ @"a", @"b", @"d", @"e", @"g", @"h" ]];
+  [self
+      checkOnlineAndOfflineCollection:collection
+                                query:[collection
+                                          queryWhereField:@"zip"
+                                                    notIn:@[ @98101, @98103, @[ @98101, @98102 ] ]]
+                        matchesResult:@[ @"a", @"b", @"d", @"e", @"g", @"h" ]];
 
-  [self checkOnlineAndOfflineQuery:[collection queryWhereField:@"zip" notIn:@[ [NSNull null] ]]
-                     matchesResult:@[]];
+  [self checkOnlineAndOfflineCollection:collection
+                                  query:[collection queryWhereField:@"zip" notIn:@[ [NSNull null] ]]
+                          matchesResult:@[]];
 }
 
 - (void)testQueriesCanUseArrayContainsAnyFilters {
@@ -944,12 +950,13 @@
     @"__id-9223372036854775808__" : @{@"a" : @1},
   }];
 
-  [self checkOnlineAndOfflineQuery:[collRef queryOrderedByFieldPath:[FIRFieldPath documentID]]
-                     matchesResult:@[
-                       @"__id-9223372036854775808__", @"__id-2__", @"__id7__", @"__id12__",
-                       @"__id9223372036854775807__", @"12", @"7", @"A", @"Aa", @"__id", @"__id1_",
-                       @"_id1__", @"a"
-                     ]];
+  [self checkOnlineAndOfflineCollection:collRef
+                                  query:[collRef queryOrderedByFieldPath:[FIRFieldPath documentID]]
+                          matchesResult:@[
+                            @"__id-9223372036854775808__", @"__id-2__", @"__id7__", @"__id12__",
+                            @"__id9223372036854775807__", @"12", @"7", @"A", @"Aa", @"__id",
+                            @"__id1_", @"_id1__", @"a"
+                          ]];
 }
 
 - (void)testSnapshotListenerSortsUnicodeStringsInTheSameOrderAsServer {
@@ -976,7 +983,7 @@
 
   [registration remove];
 
-  [self checkOnlineAndOfflineQuery:query matchesResult:expectedDocs];
+  [self checkOnlineAndOfflineCollection:collRef query:query matchesResult:expectedDocs];
 }
 
 - (void)testSnapshotListenerSortsUnicodeStringsInArrayInTheSameOrderAsServer {
@@ -1003,7 +1010,7 @@
 
   [registration remove];
 
-  [self checkOnlineAndOfflineQuery:query matchesResult:expectedDocs];
+  [self checkOnlineAndOfflineCollection:collRef query:query matchesResult:expectedDocs];
 }
 
 - (void)testSnapshotListenerSortsUnicodeStringsInMapInTheSameOrderAsServer {
@@ -1030,7 +1037,7 @@
 
   [registration remove];
 
-  [self checkOnlineAndOfflineQuery:query matchesResult:expectedDocs];
+  [self checkOnlineAndOfflineCollection:collRef query:query matchesResult:expectedDocs];
 }
 
 - (void)testSnapshotListenerSortsUnicodeStringsInMapKeyInTheSameOrderAsServer {
@@ -1057,7 +1064,7 @@
 
   [registration remove];
 
-  [self checkOnlineAndOfflineQuery:query matchesResult:expectedDocs];
+  [self checkOnlineAndOfflineCollection:collRef query:query matchesResult:expectedDocs];
 }
 
 - (void)testSnapshotListenerSortsUnicodeStringsInDocumentKeyInTheSameOrderAsServer {
@@ -1085,7 +1092,7 @@
 
   [registration remove];
 
-  [self checkOnlineAndOfflineQuery:query matchesResult:expectedDocs];
+  [self checkOnlineAndOfflineCollection:collRef query:query matchesResult:expectedDocs];
 }
 
 - (void)testCollectionGroupQueriesWithWhereFiltersOnArbitraryDocumentIDs {
@@ -1133,8 +1140,9 @@
   FIRFilter *filter1 = [FIRFilter orFilterWithFilters:@[
     [FIRFilter filterWhereField:@"a" isEqualTo:@1], [FIRFilter filterWhereField:@"b" isEqualTo:@1]
   ]];
-  [self checkOnlineAndOfflineQuery:[collRef queryWhereFilter:filter1]
-                     matchesResult:@[ @"doc1", @"doc2", @"doc4", @"doc5" ]];
+  [self checkOnlineAndOfflineCollection:collRef
+                                  query:[collRef queryWhereFilter:filter1]
+                          matchesResult:@[ @"doc1", @"doc2", @"doc4", @"doc5" ]];
 
   // (a==1 && b==0) || (a==3 && b==2)
   FIRFilter *filter2 = [FIRFilter orFilterWithFilters:@[
@@ -1145,8 +1153,9 @@
       [FIRFilter filterWhereField:@"a" isEqualTo:@3], [FIRFilter filterWhereField:@"b" isEqualTo:@2]
     ]]
   ]];
-  [self checkOnlineAndOfflineQuery:[collRef queryWhereFilter:filter2]
-                     matchesResult:@[ @"doc1", @"doc3" ]];
+  [self checkOnlineAndOfflineCollection:collRef
+                                  query:[collRef queryWhereFilter:filter2]
+                          matchesResult:@[ @"doc1", @"doc3" ]];
 
   // a==1 && (b==0 || b==3).
   FIRFilter *filter3 = [FIRFilter andFilterWithFilters:@[
@@ -1154,8 +1163,9 @@
       [FIRFilter filterWhereField:@"b" isEqualTo:@0], [FIRFilter filterWhereField:@"b" isEqualTo:@3]
     ]]
   ]];
-  [self checkOnlineAndOfflineQuery:[collRef queryWhereFilter:filter3]
-                     matchesResult:@[ @"doc1", @"doc4" ]];
+  [self checkOnlineAndOfflineCollection:collRef
+                                  query:[collRef queryWhereFilter:filter3]
+                          matchesResult:@[ @"doc1", @"doc4" ]];
 
   // (a==2 || b==2) && (a==3 || b==3)
   FIRFilter *filter4 = [FIRFilter andFilterWithFilters:@[
@@ -1166,14 +1176,17 @@
       [FIRFilter filterWhereField:@"a" isEqualTo:@3], [FIRFilter filterWhereField:@"b" isEqualTo:@3]
     ]]
   ]];
-  [self checkOnlineAndOfflineQuery:[collRef queryWhereFilter:filter4] matchesResult:@[ @"doc3" ]];
+  [self checkOnlineAndOfflineCollection:collRef
+                                  query:[collRef queryWhereFilter:filter4]
+                          matchesResult:@[ @"doc3" ]];
 
   // Test with limits without orderBy (the __name__ ordering is the tie breaker).
   FIRFilter *filter5 = [FIRFilter orFilterWithFilters:@[
     [FIRFilter filterWhereField:@"a" isEqualTo:@2], [FIRFilter filterWhereField:@"b" isEqualTo:@1]
   ]];
-  [self checkOnlineAndOfflineQuery:[[collRef queryWhereFilter:filter5] queryLimitedTo:1]
-                     matchesResult:@[ @"doc2" ]];
+  [self checkOnlineAndOfflineCollection:collRef
+                                  query:[[collRef queryWhereFilter:filter5] queryLimitedTo:1]
+                          matchesResult:@[ @"doc2" ]];
 }
 
 - (void)testOrQueriesWithIn {
@@ -1190,8 +1203,9 @@
   FIRFilter *filter = [FIRFilter orFilterWithFilters:@[
     [FIRFilter filterWhereField:@"a" isEqualTo:@2], [FIRFilter filterWhereField:@"b" in:@[ @2, @3 ]]
   ]];
-  [self checkOnlineAndOfflineQuery:[collRef queryWhereFilter:filter]
-                     matchesResult:@[ @"doc3", @"doc4", @"doc6" ]];
+  [self checkOnlineAndOfflineCollection:collRef
+                                  query:[collRef queryWhereFilter:filter]
+                          matchesResult:@[ @"doc3", @"doc4", @"doc6" ]];
 }
 
 - (void)testOrQueriesWithArrayMembership {
@@ -1209,16 +1223,18 @@
     [FIRFilter filterWhereField:@"a" isEqualTo:@2], [FIRFilter filterWhereField:@"b"
                                                                   arrayContains:@7]
   ]];
-  [self checkOnlineAndOfflineQuery:[collRef queryWhereFilter:filter1]
-                     matchesResult:@[ @"doc3", @"doc4", @"doc6" ]];
+  [self checkOnlineAndOfflineCollection:collRef
+                                  query:[collRef queryWhereFilter:filter1]
+                          matchesResult:@[ @"doc3", @"doc4", @"doc6" ]];
 
   // a==2 || b array-contains-any [0, 3]
   FIRFilter *filter2 = [FIRFilter orFilterWithFilters:@[
     [FIRFilter filterWhereField:@"a" isEqualTo:@2], [FIRFilter filterWhereField:@"b"
                                                                arrayContainsAny:@[ @0, @3 ]]
   ]];
-  [self checkOnlineAndOfflineQuery:[collRef queryWhereFilter:filter2]
-                     matchesResult:@[ @"doc1", @"doc4", @"doc6" ]];
+  [self checkOnlineAndOfflineCollection:collRef
+                                  query:[collRef queryWhereFilter:filter2]
+                          matchesResult:@[ @"doc1", @"doc4", @"doc6" ]];
 }
 
 - (void)testMultipleInOps {
@@ -1236,8 +1252,9 @@
     [FIRFilter filterWhereField:@"a" in:@[ @2, @3 ]], [FIRFilter filterWhereField:@"b"
                                                                                in:@[ @0, @2 ]]
   ]];
-  [self checkOnlineAndOfflineQuery:[collRef queryWhereFilter:filter1]
-                     matchesResult:@[ @"doc1", @"doc3", @"doc6" ]];
+  [self checkOnlineAndOfflineCollection:collRef
+                                  query:[collRef queryWhereFilter:filter1]
+                          matchesResult:@[ @"doc1", @"doc3", @"doc6" ]];
 
   // Two IN operations on the same field with disjunction.
   // a IN [0,3] || a IN [0,2] should union them (similar to: a IN [0,2,3]).
@@ -1245,8 +1262,9 @@
     [FIRFilter filterWhereField:@"a" in:@[ @0, @3 ]], [FIRFilter filterWhereField:@"a"
                                                                                in:@[ @0, @2 ]]
   ]];
-  [self checkOnlineAndOfflineQuery:[collRef queryWhereFilter:filter2]
-                     matchesResult:@[ @"doc3", @"doc6" ]];
+  [self checkOnlineAndOfflineCollection:collRef
+                                  query:[collRef queryWhereFilter:filter2]
+                          matchesResult:@[ @"doc3", @"doc6" ]];
 }
 
 - (void)testUsingInWithArrayContainsAny {
@@ -1263,8 +1281,9 @@
     [FIRFilter filterWhereField:@"a" in:@[ @2, @3 ]], [FIRFilter filterWhereField:@"b"
                                                                  arrayContainsAny:@[ @0, @7 ]]
   ]];
-  [self checkOnlineAndOfflineQuery:[collRef queryWhereFilter:filter1]
-                     matchesResult:@[ @"doc1", @"doc3", @"doc4", @"doc6" ]];
+  [self checkOnlineAndOfflineCollection:collRef
+                                  query:[collRef queryWhereFilter:filter1]
+                          matchesResult:@[ @"doc1", @"doc3", @"doc4", @"doc6" ]];
 
   FIRFilter *filter2 = [FIRFilter orFilterWithFilters:@[
     [FIRFilter andFilterWithFilters:@[
@@ -1273,8 +1292,9 @@
     ]],
     [FIRFilter filterWhereField:@"b" arrayContainsAny:@[ @0, @7 ]]
   ]];
-  [self checkOnlineAndOfflineQuery:[collRef queryWhereFilter:filter2]
-                     matchesResult:@[ @"doc1", @"doc3", @"doc4" ]];
+  [self checkOnlineAndOfflineCollection:collRef
+                                  query:[collRef queryWhereFilter:filter2]
+                          matchesResult:@[ @"doc1", @"doc3", @"doc4" ]];
 }
 
 - (void)testUseInWithArrayContains {
@@ -1291,14 +1311,17 @@
     [FIRFilter filterWhereField:@"a" in:@[ @2, @3 ]], [FIRFilter filterWhereField:@"b"
                                                                  arrayContainsAny:@[ @3 ]]
   ]];
-  [self checkOnlineAndOfflineQuery:[collRef queryWhereFilter:filter1]
-                     matchesResult:@[ @"doc3", @"doc4", @"doc6" ]];
+  [self checkOnlineAndOfflineCollection:collRef
+                                  query:[collRef queryWhereFilter:filter1]
+                          matchesResult:@[ @"doc3", @"doc4", @"doc6" ]];
 
   FIRFilter *filter2 = [FIRFilter andFilterWithFilters:@[
     [FIRFilter filterWhereField:@"a" in:@[ @2, @3 ]], [FIRFilter filterWhereField:@"b"
                                                                     arrayContains:@7]
   ]];
-  [self checkOnlineAndOfflineQuery:[collRef queryWhereFilter:filter2] matchesResult:@[ @"doc3" ]];
+  [self checkOnlineAndOfflineCollection:collRef
+                                  query:[collRef queryWhereFilter:filter2]
+                          matchesResult:@[ @"doc3" ]];
 
   FIRFilter *filter3 = [FIRFilter orFilterWithFilters:@[
     [FIRFilter filterWhereField:@"a" in:@[ @2, @3 ]], [FIRFilter andFilterWithFilters:@[
@@ -1306,8 +1329,9 @@
                                                                             isEqualTo:@1]
     ]]
   ]];
-  [self checkOnlineAndOfflineQuery:[collRef queryWhereFilter:filter3]
-                     matchesResult:@[ @"doc3", @"doc4", @"doc6" ]];
+  [self checkOnlineAndOfflineCollection:collRef
+                                  query:[collRef queryWhereFilter:filter3]
+                          matchesResult:@[ @"doc3", @"doc4", @"doc6" ]];
 
   FIRFilter *filter4 = [FIRFilter andFilterWithFilters:@[
     [FIRFilter filterWhereField:@"a" in:@[ @2, @3 ]], [FIRFilter orFilterWithFilters:@[
@@ -1315,7 +1339,9 @@
                                                                             isEqualTo:@1]
     ]]
   ]];
-  [self checkOnlineAndOfflineQuery:[collRef queryWhereFilter:filter4] matchesResult:@[ @"doc3" ]];
+  [self checkOnlineAndOfflineCollection:collRef
+                                  query:[collRef queryWhereFilter:filter4]
+                          matchesResult:@[ @"doc3" ]];
 }
 
 - (void)testOrderByEquality {
@@ -1333,16 +1359,19 @@
     @"doc6" : @{@"a" : @2, @"c" : @20}
   }];
 
-  [self checkOnlineAndOfflineQuery:[[collRef queryWhereFilter:[FIRFilter filterWhereField:@"a"
-                                                                                isEqualTo:@1]]
-                                       queryOrderedByField:@"a"]
-                     matchesResult:@[ @"doc1", @"doc4", @"doc5" ]];
-
-  [self checkOnlineAndOfflineQuery:[[collRef
-                                       queryWhereFilter:[FIRFilter filterWhereField:@"a"
-                                                                                 in:@[ @2, @3 ]]]
-                                       queryOrderedByField:@"a"]
-                     matchesResult:@[ @"doc6", @"doc3" ]];
+  [self checkOnlineAndOfflineCollection:collRef
+                                  query:[[collRef queryWhereFilter:[FIRFilter filterWhereField:@"a"
+                                                                                     isEqualTo:@1]]
+                                            queryOrderedByField:@"a"]
+                          matchesResult:@[ @"doc1", @"doc4", @"doc5" ]];
+
+  [self
+      checkOnlineAndOfflineCollection:collRef
+                                query:[[collRef
+                                          queryWhereFilter:[FIRFilter filterWhereField:@"a"
+                                                                                    in:@[ @2, @3 ]]]
+                                          queryOrderedByField:@"a"]
+                        matchesResult:@[ @"doc6", @"doc3" ]];
 }
 
 - (void)testResumingAQueryShouldUseBloomFilterToAvoidFullRequery {

+ 3 - 1
Firestore/Example/Tests/Util/FSTIntegrationTestCase.h

@@ -130,7 +130,9 @@ extern "C" {
 
 - (void)enableNetwork;
 
-- (void)checkOnlineAndOfflineQuery:(FIRQuery *)query matchesResult:(NSArray *)expectedDocs;
+- (void)checkOnlineAndOfflineCollection:(FIRQuery *)collection
+                                  query:(FIRQuery *)query
+                          matchesResult:(NSArray *)expectedDocs;
 
 /**
  * "Blocks" the current thread/run loop until the block returns YES.

+ 32 - 9
Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm

@@ -623,21 +623,44 @@ class FakeAuthCredentialsProvider : public EmptyAuthCredentialsProvider {
 
 /**
  * Checks that running the query while online (against the backend/emulator) results in the same
- * documents as running the query while offline. It also checks that both online and offline
- * query result is equal to the expected documents.
+ * documents as running the query while offline. If `expectedDocs` is provided, it also checks
+ * that both online and offline query result is equal to the expected documents.
  *
- * @param query The query to check.
- * @param expectedDocs Array of document keys that are expected to match the query.
+ * This function first performs a "get" for the entire COLLECTION from the server.
+ * It then performs the QUERY from CACHE which, results in `executeFullCollectionScan()`
+ * It then performs the QUERY from SERVER.
+ * It then performs the QUERY from CACHE again, which results in `performQueryUsingRemoteKeys()`.
+ * It then ensure that all the above QUERY results are the same.
+ *
+ * @param collection The collection on which the query is performed.
+ * @param query The query to check
+ * @param expectedDocs Ordered list of document keys that are expected to match the query
  */
-- (void)checkOnlineAndOfflineQuery:(FIRQuery *)query matchesResult:(NSArray *)expectedDocs {
+- (void)checkOnlineAndOfflineCollection:(FIRQuery *)collection
+                                  query:(FIRQuery *)query
+                          matchesResult:(NSArray *)expectedDocs {
+  // Note: Order matters. The following has to be done in the specific order:
+
+  // 1- Pre-populate the cache with the entire collection.
+  [self readDocumentSetForRef:collection source:FIRFirestoreSourceServer];
+
+  // 2- This performs the query against the cache using full collection scan.
+  FIRQuerySnapshot *docsFromCacheFullCollectionScan =
+      [self readDocumentSetForRef:query source:FIRFirestoreSourceCache];
+
+  // 3- This goes to the server (backend/emulator).
   FIRQuerySnapshot *docsFromServer = [self readDocumentSetForRef:query
                                                           source:FIRFirestoreSourceServer];
-  FIRQuerySnapshot *docsFromCache = [self readDocumentSetForRef:query
-                                                         source:FIRFirestoreSourceCache];
 
+  // 4- This performs the query against the cache using remote keys.
+  FIRQuerySnapshot *docsFromCacheUsingRemoteKeys =
+      [self readDocumentSetForRef:query source:FIRFirestoreSourceCache];
+
+  XCTAssertEqualObjects(FIRQuerySnapshotGetIDs(docsFromServer),
+                        FIRQuerySnapshotGetIDs(docsFromCacheFullCollectionScan));
   XCTAssertEqualObjects(FIRQuerySnapshotGetIDs(docsFromServer),
-                        FIRQuerySnapshotGetIDs(docsFromCache));
-  XCTAssertEqualObjects(FIRQuerySnapshotGetIDs(docsFromCache), expectedDocs);
+                        FIRQuerySnapshotGetIDs(docsFromCacheUsingRemoteKeys));
+  XCTAssertEqualObjects(FIRQuerySnapshotGetIDs(docsFromServer), expectedDocs);
 }
 
 - (const std::shared_ptr<AsyncQueue> &)queueForFirestore:(FIRFirestore *)firestore {

+ 47 - 65
Firestore/Swift/Tests/Integration/QueryIntegrationTests.swift

@@ -18,28 +18,6 @@ import FirebaseFirestore
 import Foundation
 
 class QueryIntegrationTests: FSTIntegrationTestCase {
-  /**
-   * Checks that running the query while online (against the backend/emulator) results in the same
-   * documents as running the query while offline. If expectedDocs is provided, it also checks
-   * that both online and offline query result is equal to the expected documents.
-   *
-   * @param query The query to check.
-   * @param expectedDocs Ordered list of document keys that are expected to match the query.
-   */
-  private func checkOnlineAndOfflineQuery(_ query: Query, matchesResult expectedDocs: [String]?) {
-    let docsFromServer = readDocumentSet(forRef: query,
-                                         source: FirestoreSource.server)
-
-    let docsFromCache = readDocumentSet(forRef: query,
-                                        source: FirestoreSource.cache)
-
-    XCTAssertEqual(FIRQuerySnapshotGetIDs(docsFromServer),
-                   FIRQuerySnapshotGetIDs(docsFromCache))
-    if expectedDocs != nil {
-      XCTAssertEqual(FIRQuerySnapshotGetIDs(docsFromCache), expectedDocs)
-    }
-  }
-
   func testOrQueries() throws {
     let collRef = collectionRef(
       withDocuments: ["doc1": ["a": 1, "b": 0],
@@ -54,8 +32,8 @@ class QueryIntegrationTests: FSTIntegrationTestCase {
       [Filter.whereField("a", isEqualTo: 1),
        Filter.whereField("b", isEqualTo: 1)]
     )
-    checkOnlineAndOfflineQuery(collRef.whereFilter(filter1),
-                               matchesResult: ["doc1", "doc2", "doc4", "doc5"])
+    checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter1),
+                                    matchesResult: ["doc1", "doc2", "doc4", "doc5"])
 
     // (a==1 && b==0) || (a==3 && b==2)
     let filter2 = Filter.orFilter(
@@ -68,8 +46,8 @@ class QueryIntegrationTests: FSTIntegrationTestCase {
          Filter.whereField("b", isEqualTo: 2)]
       )]
     )
-    checkOnlineAndOfflineQuery(collRef.whereFilter(filter2),
-                               matchesResult: ["doc1", "doc3"])
+    checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter2),
+                                    matchesResult: ["doc1", "doc3"])
 
     // a==1 && (b==0 || b==3).
     let filter3 = Filter.andFilter(
@@ -79,8 +57,8 @@ class QueryIntegrationTests: FSTIntegrationTestCase {
           Filter.whereField("b", isEqualTo: 3)]
        )]
     )
-    checkOnlineAndOfflineQuery(collRef.whereFilter(filter3),
-                               matchesResult: ["doc1", "doc4"])
+    checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter3),
+                                    matchesResult: ["doc1", "doc4"])
 
     // (a==2 || b==2) && (a==3 || b==3)
     let filter4 = Filter.andFilter(
@@ -93,16 +71,16 @@ class QueryIntegrationTests: FSTIntegrationTestCase {
          Filter.whereField("b", isEqualTo: 3)]
       )]
     )
-    checkOnlineAndOfflineQuery(collRef.whereFilter(filter4),
-                               matchesResult: ["doc3"])
+    checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter4),
+                                    matchesResult: ["doc3"])
 
     // Test with limits without orderBy (the __name__ ordering is the tie breaker).
     let filter5 = Filter.orFilter(
       [Filter.whereField("a", isEqualTo: 2),
        Filter.whereField("b", isEqualTo: 1)]
     )
-    checkOnlineAndOfflineQuery(collRef.whereFilter(filter5).limit(to: 1),
-                               matchesResult: ["doc2"])
+    checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter5).limit(to: 1),
+                                    matchesResult: ["doc2"])
   }
 
   func testOrQueriesWithCompositeIndexes() throws {
@@ -124,16 +102,16 @@ class QueryIntegrationTests: FSTIntegrationTestCase {
       [Filter.whereField("a", isGreaterThan: 2),
        Filter.whereField("b", isEqualTo: 1)]
     )
-    checkOnlineAndOfflineQuery(collRef.whereFilter(filter1),
-                               matchesResult: ["doc5", "doc2", "doc3"])
+    checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter1),
+                                    matchesResult: ["doc5", "doc2", "doc3"])
 
     // Test with limits (implicit order by ASC): (a==1) || (b > 0) LIMIT 2
     let filter2 = Filter.orFilter(
       [Filter.whereField("a", isEqualTo: 1),
        Filter.whereField("b", isGreaterThan: 0)]
     )
-    checkOnlineAndOfflineQuery(collRef.whereFilter(filter2).limit(to: 2),
-                               matchesResult: ["doc1", "doc2"])
+    checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter2).limit(to: 2),
+                                    matchesResult: ["doc1", "doc2"])
 
     // Test with limits (explicit order by): (a==1) || (b > 0) LIMIT_TO_LAST 2
     // Note: The public query API does not allow implicit ordering when limitToLast is used.
@@ -141,7 +119,7 @@ class QueryIntegrationTests: FSTIntegrationTestCase {
       [Filter.whereField("a", isEqualTo: 1),
        Filter.whereField("b", isGreaterThan: 0)]
     )
-    checkOnlineAndOfflineQuery(collRef.whereFilter(filter3)
+    checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter3)
       .limit(toLast: 2)
       .order(by: "b"),
       matchesResult: ["doc3", "doc4"])
@@ -151,7 +129,7 @@ class QueryIntegrationTests: FSTIntegrationTestCase {
       [Filter.whereField("a", isEqualTo: 2),
        Filter.whereField("b", isEqualTo: 1)]
     )
-    checkOnlineAndOfflineQuery(collRef.whereFilter(filter4).limit(to: 1)
+    checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter4).limit(to: 1)
       .order(by: "a"),
       matchesResult: ["doc5"])
 
@@ -160,7 +138,7 @@ class QueryIntegrationTests: FSTIntegrationTestCase {
       [Filter.whereField("a", isEqualTo: 2),
        Filter.whereField("b", isEqualTo: 1)]
     )
-    checkOnlineAndOfflineQuery(collRef.whereFilter(filter5).limit(toLast: 1)
+    checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter5).limit(toLast: 1)
       .order(by: "a"),
       matchesResult: ["doc2"])
   }
@@ -180,8 +158,8 @@ class QueryIntegrationTests: FSTIntegrationTestCase {
       [Filter.whereField("a", isEqualTo: 2),
        Filter.whereField("b", in: [2, 3])]
     )
-    checkOnlineAndOfflineQuery(collRef.whereFilter(filter),
-                               matchesResult: ["doc3", "doc4", "doc6"])
+    checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter),
+                                    matchesResult: ["doc3", "doc4", "doc6"])
   }
 
   func testOrQueriesWithArrayMembership() throws {
@@ -199,16 +177,16 @@ class QueryIntegrationTests: FSTIntegrationTestCase {
       [Filter.whereField("a", isEqualTo: 2),
        Filter.whereField("b", arrayContains: 7)]
     )
-    checkOnlineAndOfflineQuery(collRef.whereFilter(filter1),
-                               matchesResult: ["doc3", "doc4", "doc6"])
+    checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter1),
+                                    matchesResult: ["doc3", "doc4", "doc6"])
 
     // a==2 || b array-contains-any [0, 3]
     let filter2 = Filter.orFilter(
       [Filter.whereField("a", isEqualTo: 2),
        Filter.whereField("b", arrayContainsAny: [0, 3])]
     )
-    checkOnlineAndOfflineQuery(collRef.whereFilter(filter2),
-                               matchesResult: ["doc1", "doc4", "doc6"])
+    checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter2),
+                                    matchesResult: ["doc1", "doc4", "doc6"])
   }
 
   func testMultipleInOps() throws {
@@ -226,8 +204,8 @@ class QueryIntegrationTests: FSTIntegrationTestCase {
       [Filter.whereField("a", in: [2, 3]),
        Filter.whereField("b", in: [0, 2])]
     )
-    checkOnlineAndOfflineQuery(collRef.whereFilter(filter1).order(by: "a"),
-                               matchesResult: ["doc1", "doc6", "doc3"])
+    checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter1).order(by: "a"),
+                                    matchesResult: ["doc1", "doc6", "doc3"])
 
     // Two IN operations on same fields with disjunction.
     // a IN [0,3] || a IN [0,2] should union them (similar to: a IN [0,2,3]).
@@ -235,8 +213,8 @@ class QueryIntegrationTests: FSTIntegrationTestCase {
       [Filter.whereField("a", in: [0, 3]),
        Filter.whereField("a", in: [0, 2])]
     )
-    checkOnlineAndOfflineQuery(collRef.whereFilter(filter2),
-                               matchesResult: ["doc3", "doc6"])
+    checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter2),
+                                    matchesResult: ["doc3", "doc6"])
   }
 
   func testUsingInWithArrayContainsAny() throws {
@@ -253,8 +231,8 @@ class QueryIntegrationTests: FSTIntegrationTestCase {
       [Filter.whereField("a", in: [2, 3]),
        Filter.whereField("b", arrayContainsAny: [0, 7])]
     )
-    checkOnlineAndOfflineQuery(collRef.whereFilter(filter1),
-                               matchesResult: ["doc1", "doc3", "doc4", "doc6"])
+    checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter1),
+                                    matchesResult: ["doc1", "doc3", "doc4", "doc6"])
 
     let filter2 = Filter.orFilter(
       [Filter.andFilter(
@@ -263,8 +241,8 @@ class QueryIntegrationTests: FSTIntegrationTestCase {
       ),
       Filter.whereField("b", arrayContainsAny: [0, 7])]
     )
-    checkOnlineAndOfflineQuery(collRef.whereFilter(filter2),
-                               matchesResult: ["doc1", "doc3", "doc4"])
+    checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter2),
+                                    matchesResult: ["doc1", "doc3", "doc4"])
   }
 
   func testUseInWithArrayContains() throws {
@@ -281,15 +259,15 @@ class QueryIntegrationTests: FSTIntegrationTestCase {
       [Filter.whereField("a", in: [2, 3]),
        Filter.whereField("b", arrayContainsAny: [3])]
     )
-    checkOnlineAndOfflineQuery(collRef.whereFilter(filter1),
-                               matchesResult: ["doc3", "doc4", "doc6"])
+    checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter1),
+                                    matchesResult: ["doc3", "doc4", "doc6"])
 
     let filter2 = Filter.andFilter(
       [Filter.whereField("a", in: [2, 3]),
        Filter.whereField("b", arrayContains: 7)]
     )
-    checkOnlineAndOfflineQuery(collRef.whereFilter(filter2),
-                               matchesResult: ["doc3"])
+    checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter2),
+                                    matchesResult: ["doc3"])
 
     let filter3 = Filter.orFilter(
       [Filter.whereField("a", in: [2, 3]),
@@ -298,8 +276,8 @@ class QueryIntegrationTests: FSTIntegrationTestCase {
           Filter.whereField("a", isEqualTo: 1)]
        )]
     )
-    checkOnlineAndOfflineQuery(collRef.whereFilter(filter3),
-                               matchesResult: ["doc3", "doc4", "doc6"])
+    checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter3),
+                                    matchesResult: ["doc3", "doc4", "doc6"])
 
     let filter4 = Filter.andFilter(
       [Filter.whereField("a", in: [2, 3]),
@@ -308,8 +286,8 @@ class QueryIntegrationTests: FSTIntegrationTestCase {
           Filter.whereField("a", isEqualTo: 1)]
        )]
     )
-    checkOnlineAndOfflineQuery(collRef.whereFilter(filter4),
-                               matchesResult: ["doc3"])
+    checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter4),
+                                    matchesResult: ["doc3"])
   }
 
   func testOrderByEquality() throws {
@@ -326,11 +304,15 @@ class QueryIntegrationTests: FSTIntegrationTestCase {
                       "doc6": ["a": 2, "c": 20]]
     )
 
-    checkOnlineAndOfflineQuery(collRef.whereFilter(Filter.whereField("a", isEqualTo: 1)),
-                               matchesResult: ["doc1", "doc4", "doc5"])
+    checkOnlineAndOfflineCollection(
+      collRef,
+      query: collRef.whereFilter(Filter.whereField("a", isEqualTo: 1)),
+      matchesResult: ["doc1", "doc4", "doc5"]
+    )
 
-    checkOnlineAndOfflineQuery(
-      collRef.whereFilter(Filter.whereField("a", in: [2, 3])).order(by: "a"),
+    checkOnlineAndOfflineCollection(
+      collRef,
+      query: collRef.whereFilter(Filter.whereField("a", in: [2, 3])).order(by: "a"),
       matchesResult: ["doc6", "doc3"]
     )
   }

+ 9 - 7
Firestore/Swift/Tests/Integration/VectorIntegrationTests.swift

@@ -146,7 +146,11 @@ class VectorIntegrationTests: FSTIntegrationTestCase {
       docIds.append(docRef.documentID)
     }
 
-    checkOnlineAndOfflineQuery(collection.order(by: "embedding"), matchesResult: docIds)
+    checkOnlineAndOfflineCollection(
+      collection,
+      query: collection.order(by: "embedding"),
+      matchesResult: docIds
+    )
   }
 
   func testSdkFiltersVectorFieldSameWayOnlineAndOffline() async throws {
@@ -176,16 +180,14 @@ class VectorIntegrationTests: FSTIntegrationTestCase {
       docIds.append(docRef.documentID)
     }
 
-    checkOnlineAndOfflineQuery(
+    checkOnlineAndOfflineCollection(collection, query:
       collection.order(by: "embedding")
         .whereField("embedding", isLessThan: FieldValue.vector([1, 2, 100, 4, 4.0])),
-      matchesResult: Array(docIds[2 ... 10])
-    )
-    checkOnlineAndOfflineQuery(
+      matchesResult: Array(docIds[2 ... 10]))
+    checkOnlineAndOfflineCollection(collection, query:
       collection.order(by: "embedding")
         .whereField("embedding", isGreaterThanOrEqualTo: FieldValue.vector([1, 2, 100, 4, 4.0])),
-      matchesResult: Array(docIds[11 ... 12])
-    )
+      matchesResult: Array(docIds[11 ... 12]))
   }
 
   func testQueryVectorValueWrittenByCodable() async throws {