Skip to content

Commit 81a8f73

Browse files
committed
Refactor: WatchChangeAggregatorTestingHooks -> TestingHooks and WatchChangeAggregatorTestingHooksAccessor -> ExistenceFilterMismatchListener
1 parent 13aae05 commit 81a8f73

File tree

5 files changed

+201
-197
lines changed

5 files changed

+201
-197
lines changed

firebase-firestore/src/androidTest/java/com/google/firebase/firestore/QueryTest.java

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
import com.google.android.gms.tasks.Task;
3838
import com.google.common.collect.Lists;
3939
import com.google.firebase.firestore.Query.Direction;
40-
import com.google.firebase.firestore.remote.WatchChangeAggregatorTestingHooksAccessor;
40+
import com.google.firebase.firestore.remote.ExistenceFilterMismatchListener;
4141
import com.google.firebase.firestore.testutil.EventAccumulator;
4242
import com.google.firebase.firestore.testutil.IntegrationTestUtil;
4343
import java.util.ArrayList;
@@ -1079,26 +1079,23 @@ public void resumingAQueryShouldUseExistenceFilterToDetectDeletes() throws Excep
10791079

10801080
// Resume the query and save the resulting snapshot for verification. Use some internal testing
10811081
// hooks to "capture" the existence filter mismatches to verify them.
1082+
ExistenceFilterMismatchListener existenceFilterMismatchListener =
1083+
new ExistenceFilterMismatchListener();
10821084
QuerySnapshot snapshot2;
1083-
WatchChangeAggregatorTestingHooksAccessor.ExistenceFilterMismatchInfo
1084-
existenceFilterMismatchInfo;
1085-
WatchChangeAggregatorTestingHooksAccessor.ExistenceFilterMismatchAccumulator
1086-
existenceFilterMismatchAccumulator =
1087-
new WatchChangeAggregatorTestingHooksAccessor.ExistenceFilterMismatchAccumulator();
1088-
existenceFilterMismatchAccumulator.register();
1085+
ExistenceFilterMismatchListener.ExistenceFilterMismatchInfo existenceFilterMismatchInfo;
10891086
try {
1087+
existenceFilterMismatchListener.startListening();
10901088
snapshot2 = waitFor(collection.get());
10911089
// TODO(b/270731363): Remove the "if" condition below once the Firestore Emulator is fixed
10921090
// to send an existence filter.
10931091
if (isRunningAgainstEmulator()) {
10941092
existenceFilterMismatchInfo = null;
10951093
} else {
10961094
existenceFilterMismatchInfo =
1097-
existenceFilterMismatchAccumulator.waitForExistenceFilterMismatch(
1098-
/*timeoutMillis=*/ 5000);
1095+
existenceFilterMismatchListener.waitForExistenceFilterMismatch(/*timeoutMillis=*/ 5000);
10991096
}
11001097
} finally {
1101-
existenceFilterMismatchAccumulator.unregister();
1098+
existenceFilterMismatchListener.stopListening();
11021099
}
11031100

11041101
// Verify that the snapshot from the resumed query contains the expected documents; that is,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
// Copyright 2023 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.firebase.firestore.remote;
16+
17+
import android.os.SystemClock;
18+
import androidx.annotation.NonNull;
19+
import androidx.annotation.Nullable;
20+
import com.google.firebase.firestore.ListenerRegistration;
21+
import java.util.ArrayList;
22+
23+
/**
24+
* Provides a mechanism for tests to listen for existence filter mismatches in the Watch "listen"
25+
* stream.
26+
*/
27+
public final class ExistenceFilterMismatchListener {
28+
29+
private TestingHooksExistenceFilterMismatchListenerImpl listener;
30+
private ListenerRegistration listenerRegistration;
31+
32+
/**
33+
* Starts listening for existence filter mismatches.
34+
*
35+
* @throws IllegalStateException if this object is already started.
36+
* @see #stopListening
37+
*/
38+
public synchronized void startListening() {
39+
if (listener != null) {
40+
throw new IllegalStateException("already registered");
41+
}
42+
listener = new TestingHooksExistenceFilterMismatchListenerImpl();
43+
listenerRegistration = TestingHooks.getInstance().addExistenceFilterMismatchListener(listener);
44+
}
45+
46+
/**
47+
* Stops listening for existence filter mismatches.
48+
*
49+
* <p>If listening has not been started then this method does nothing.
50+
*
51+
* @see #startListening
52+
*/
53+
public synchronized void stopListening() {
54+
if (listenerRegistration != null) {
55+
listenerRegistration.remove();
56+
}
57+
listenerRegistration = null;
58+
listener = null;
59+
}
60+
61+
/**
62+
* Waits for an existence filter mismatch.
63+
*
64+
* @param timeoutMillis the amount of time, in milliseconds, to wait for an existence filter
65+
* mismatch.
66+
* @return information about the existence filter mismatch that occurred.
67+
* @throws InterruptedException if waiting is interrupted.
68+
* @throws IllegalStateException if this object has not been started by {@link #startListening}.
69+
* @throws IllegalArgumentException if the given timeout is less than or equal to zero.
70+
*/
71+
@Nullable
72+
public ExistenceFilterMismatchInfo waitForExistenceFilterMismatch(long timeoutMillis)
73+
throws InterruptedException {
74+
if (timeoutMillis <= 0) {
75+
throw new IllegalArgumentException("invalid timeout: " + timeoutMillis);
76+
}
77+
78+
TestingHooksExistenceFilterMismatchListenerImpl registeredListener;
79+
synchronized (this) {
80+
registeredListener = listener;
81+
}
82+
83+
if (registeredListener == null) {
84+
throw new IllegalStateException(
85+
"must be registered before waiting for an existence filter mismatch");
86+
}
87+
88+
return registeredListener.waitForExistenceFilterMismatch(timeoutMillis);
89+
}
90+
91+
private static final class TestingHooksExistenceFilterMismatchListenerImpl
92+
implements TestingHooks.ExistenceFilterMismatchListener {
93+
94+
private final ArrayList<ExistenceFilterMismatchInfo> existenceFilterMismatches =
95+
new ArrayList<>();
96+
97+
@Override
98+
public synchronized void onExistenceFilterMismatch(
99+
@NonNull TestingHooks.ExistenceFilterMismatchInfo info) {
100+
existenceFilterMismatches.add(new ExistenceFilterMismatchInfo(info));
101+
notifyAll();
102+
}
103+
104+
@Nullable
105+
synchronized ExistenceFilterMismatchInfo waitForExistenceFilterMismatch(long timeoutMillis)
106+
throws InterruptedException {
107+
if (timeoutMillis <= 0) {
108+
throw new IllegalArgumentException("invalid timeout: " + timeoutMillis);
109+
}
110+
111+
long endTimeMillis = SystemClock.uptimeMillis() + timeoutMillis;
112+
while (true) {
113+
if (existenceFilterMismatches.size() > 0) {
114+
return existenceFilterMismatches.remove(0);
115+
}
116+
long currentWaitMillis = endTimeMillis - SystemClock.uptimeMillis();
117+
if (currentWaitMillis <= 0) {
118+
return null;
119+
}
120+
121+
wait(currentWaitMillis);
122+
}
123+
}
124+
}
125+
126+
/** @see TestingHooks.ExistenceFilterMismatchInfo */
127+
public static final class ExistenceFilterMismatchInfo {
128+
129+
private final TestingHooks.ExistenceFilterMismatchInfo info;
130+
131+
ExistenceFilterMismatchInfo(@NonNull TestingHooks.ExistenceFilterMismatchInfo info) {
132+
this.info = info;
133+
}
134+
135+
public int localCacheCount() {
136+
return info.localCacheCount();
137+
}
138+
139+
public int existenceFilterCount() {
140+
return info.existenceFilterCount();
141+
}
142+
}
143+
}

firebase-firestore/src/androidTest/java/com/google/firebase/firestore/remote/WatchChangeAggregatorTestingHooksAccessor.java

Lines changed: 0 additions & 173 deletions
This file was deleted.

0 commit comments

Comments
 (0)