Skip to content

Commit 8c8ae8c

Browse files
committed
Add tests
1 parent 32cfc69 commit 8c8ae8c

File tree

4 files changed

+167
-1
lines changed

4 files changed

+167
-1
lines changed

firebase-firestore/src/main/java/com/google/firebase/firestore/local/QueryEngine.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ public ImmutableSortedMap<DocumentKey, Document> getDocumentsMatchingQuery(
108108
// TODO(csi): Auto experiment data.
109109
private void CreateCacheIndexes(Query query, QueryContext context, int resultSize) {
110110
String decisionStr = "";
111+
// If evaluation is updated, please update tests in SQLiteLocalStoreTest.java
111112
if (context.getDocumentReadCount() > 2 * resultSize) {
112113
indexManager.createTargetIndices(query.toTarget());
113114
} else {

firebase-firestore/src/test/java/com/google/firebase/firestore/local/CountingQueryEngine.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,16 @@ public ImmutableSortedMap<DocumentKey, Document> getDocumentsMatchingQuery(
8787
return queryEngine.getDocumentsMatchingQuery(query, lastLimboFreeSnapshotVersion, remoteKeys);
8888
}
8989

90+
@Override
91+
public void enableIndexAutoCreation() {
92+
queryEngine.enableIndexAutoCreation();
93+
}
94+
95+
@Override
96+
public void disableIndexAutoCreation() {
97+
queryEngine.disableIndexAutoCreation();
98+
}
99+
90100
/**
91101
* Returns the number of documents returned by the RemoteDocumentCache's `getAll()` API (since the
92102
* last call to `resetCounts()`)
@@ -181,7 +191,7 @@ public Map<DocumentKey, MutableDocument> getDocumentsMatchingQuery(
181191
@NonNull Set<DocumentKey> mutatedKeys,
182192
@Nullable QueryContext context) {
183193
Map<DocumentKey, MutableDocument> result =
184-
subject.getDocumentsMatchingQuery(query, offset, mutatedKeys);
194+
subject.getDocumentsMatchingQuery(query, offset, mutatedKeys, context);
185195
documentsReadByCollection[0] += result.size();
186196
return result;
187197
}

firebase-firestore/src/test/java/com/google/firebase/firestore/local/LocalStoreTestCase.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,10 @@ protected void backfillIndexes() {
161161
indexBackfiller.backfill();
162162
}
163163

164+
protected void setBackfillerMaxDocumentsToProcess(int newMax) {
165+
indexBackfiller.setMaxDocumentsToProcess(newMax);
166+
}
167+
164168
private void updateViews(int targetId, boolean fromCache) {
165169
notifyLocalViewChanges(viewChanges(targetId, fromCache, asList(), asList()));
166170
}
@@ -213,6 +217,21 @@ protected void executeQuery(Query query) {
213217
lastQueryResult = localStore.executeQuery(query, /* usePreviousResults= */ true);
214218
}
215219

220+
protected void enableIndexAutoCreation() {
221+
// Noted: there are two queryEngines here, the first one is extended by CountingQueryEngine,
222+
// which is set by localStore function; The second one a pointer inside CountingQueryEngine,
223+
// which is set by queryEngine function.
224+
// Only the second function takes effect in the tests. Adding first one here for compatibility.
225+
localStore.enableIndexAutoCreation();
226+
queryEngine.enableIndexAutoCreation();
227+
}
228+
229+
protected void disableIndexAutoCreation() {
230+
// Please refer to the notes in `enableIndexAutoCreation()`
231+
localStore.disableIndexAutoCreation();
232+
queryEngine.disableIndexAutoCreation();
233+
}
234+
216235
private void releaseTarget(int targetId) {
217236
localStore.releaseTarget(targetId);
218237
}

firebase-firestore/src/test/java/com/google/firebase/firestore/local/SQLiteLocalStoreTest.java

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,4 +366,140 @@ public void testDeeplyNestedServerTimestamps() {
366366
}
367367
assertThat(error.get()).isNull();
368368
}
369+
370+
@Test
371+
public void testIndexAutoCreationWorks() {
372+
Query query = query("coll").filter(filter("matches", "==", true));
373+
int targetId = allocateQuery(query);
374+
375+
enableIndexAutoCreation();
376+
377+
applyRemoteEvent(addedRemoteEvent(doc("coll/a", 10, map("matches", true)), targetId));
378+
applyRemoteEvent(addedRemoteEvent(doc("coll/b", 10, map("matches", false)), targetId));
379+
applyRemoteEvent(addedRemoteEvent(doc("coll/c", 10, map("matches", false)), targetId));
380+
applyRemoteEvent(addedRemoteEvent(doc("coll/d", 10, map("matches", false)), targetId));
381+
applyRemoteEvent(addedRemoteEvent(doc("coll/e", 10, map("matches", true)), targetId));
382+
383+
// First time query runs without indexes.
384+
// Based on current heuristic, collection document counts (5) > 2 * resultSize (2).
385+
// Full matched index should be created.
386+
executeQuery(query);
387+
assertRemoteDocumentsRead(/* byKey= */ 0, /* byCollection= */ 2);
388+
assertQueryReturned("coll/a", "coll/e");
389+
390+
backfillIndexes();
391+
392+
applyRemoteEvent(addedRemoteEvent(doc("coll/f", 20, map("matches", true)), targetId));
393+
394+
executeQuery(query);
395+
assertRemoteDocumentsRead(/* byKey= */ 2, /* byCollection= */ 1);
396+
assertQueryReturned("coll/a", "coll/e", "coll/f");
397+
}
398+
399+
@Test
400+
public void testIndexAutoCreationWorksWhenBackfillerRunsHalfway() {
401+
Query query = query("coll").filter(filter("matches", "==", true));
402+
int targetId = allocateQuery(query);
403+
404+
enableIndexAutoCreation();
405+
406+
applyRemoteEvent(addedRemoteEvent(doc("coll/a", 10, map("matches", true)), targetId));
407+
applyRemoteEvent(addedRemoteEvent(doc("coll/b", 10, map("matches", false)), targetId));
408+
applyRemoteEvent(addedRemoteEvent(doc("coll/c", 10, map("matches", false)), targetId));
409+
applyRemoteEvent(addedRemoteEvent(doc("coll/d", 10, map("matches", false)), targetId));
410+
applyRemoteEvent(addedRemoteEvent(doc("coll/e", 10, map("matches", true)), targetId));
411+
412+
// First time query is running without indexes.
413+
// Based on current heuristic, collection document counts (5) > 2 * resultSize (2).
414+
// Full matched index should be created.
415+
executeQuery(query);
416+
// Only document a matches the result
417+
assertRemoteDocumentsRead(/* byKey= */ 0, /* byCollection= */ 2);
418+
assertQueryReturned("coll/a", "coll/e");
419+
420+
setBackfillerMaxDocumentsToProcess(2);
421+
backfillIndexes();
422+
423+
applyRemoteEvent(addedRemoteEvent(doc("coll/f", 20, map("matches", true)), targetId));
424+
425+
executeQuery(query);
426+
assertRemoteDocumentsRead(/* byKey= */ 1, /* byCollection= */ 2);
427+
assertQueryReturned("coll/a", "coll/e", "coll/f");
428+
}
429+
430+
@Test
431+
public void testIndexCreatedByIndexAutoCreationExistsAfterTurnOffAutoCreation() {
432+
Query query = query("coll").filter(filter("matches", "==", true));
433+
int targetId = allocateQuery(query);
434+
435+
enableIndexAutoCreation();
436+
437+
applyRemoteEvent(addedRemoteEvent(doc("coll/a", 10, map("matches", true)), targetId));
438+
applyRemoteEvent(addedRemoteEvent(doc("coll/b", 10, map("matches", false)), targetId));
439+
applyRemoteEvent(addedRemoteEvent(doc("coll/c", 10, map("matches", false)), targetId));
440+
applyRemoteEvent(addedRemoteEvent(doc("coll/d", 10, map("matches", false)), targetId));
441+
applyRemoteEvent(addedRemoteEvent(doc("coll/e", 10, map("matches", true)), targetId));
442+
443+
// First time query runs without indexes.
444+
// Based on current heuristic, collection document counts (5) > 2 * resultSize (2).
445+
// Full matched index should be created.
446+
executeQuery(query);
447+
assertRemoteDocumentsRead(/* byKey= */ 0, /* byCollection= */ 2);
448+
assertQueryReturned("coll/a", "coll/e");
449+
450+
disableIndexAutoCreation();
451+
452+
backfillIndexes();
453+
454+
applyRemoteEvent(addedRemoteEvent(doc("coll/f", 20, map("matches", true)), targetId));
455+
456+
executeQuery(query);
457+
assertRemoteDocumentsRead(/* byKey= */ 2, /* byCollection= */ 1);
458+
assertQueryReturned("coll/a", "coll/e", "coll/f");
459+
}
460+
461+
@Test
462+
public void testDisableIndexAutoCreationWorks() {
463+
Query query1 = query("coll").filter(filter("matches", "==", true));
464+
int targetId1 = allocateQuery(query1);
465+
466+
enableIndexAutoCreation();
467+
468+
applyRemoteEvent(addedRemoteEvent(doc("coll/a", 10, map("matches", true)), targetId1));
469+
applyRemoteEvent(addedRemoteEvent(doc("coll/b", 10, map("matches", false)), targetId1));
470+
applyRemoteEvent(addedRemoteEvent(doc("coll/c", 10, map("matches", false)), targetId1));
471+
applyRemoteEvent(addedRemoteEvent(doc("coll/d", 10, map("matches", false)), targetId1));
472+
applyRemoteEvent(addedRemoteEvent(doc("coll/e", 10, map("matches", true)), targetId1));
473+
474+
// First time query is running without indexes.
475+
// Based on current heuristic, collection document counts (5) > 2 * resultSize (2).
476+
// Full matched index should be created.
477+
executeQuery(query1);
478+
assertRemoteDocumentsRead(/* byKey= */ 0, /* byCollection= */ 2);
479+
assertQueryReturned("coll/a", "coll/e");
480+
481+
disableIndexAutoCreation();
482+
483+
backfillIndexes();
484+
485+
executeQuery(query1);
486+
assertRemoteDocumentsRead(/* byKey= */ 2, /* byCollection= */ 0);
487+
assertQueryReturned("coll/a", "coll/e");
488+
489+
Query query2 = query("foo").filter(filter("matches", "==", true));
490+
int targetId2 = allocateQuery(query2);
491+
492+
applyRemoteEvent(addedRemoteEvent(doc("foo/a", 10, map("matches", true)), targetId2));
493+
applyRemoteEvent(addedRemoteEvent(doc("foo/b", 10, map("matches", false)), targetId2));
494+
applyRemoteEvent(addedRemoteEvent(doc("foo/c", 10, map("matches", false)), targetId2));
495+
applyRemoteEvent(addedRemoteEvent(doc("foo/d", 10, map("matches", false)), targetId2));
496+
applyRemoteEvent(addedRemoteEvent(doc("foo/e", 10, map("matches", true)), targetId2));
497+
498+
executeQuery(query2);
499+
assertRemoteDocumentsRead(/* byKey= */ 0, /* byCollection= */ 2);
500+
501+
// Run the query in second time, test index won't be created
502+
executeQuery(query2);
503+
assertRemoteDocumentsRead(/* byKey= */ 0, /* byCollection= */ 2);
504+
}
369505
}

0 commit comments

Comments
 (0)