Visitar URL original
Adding Watch conformane tests (#3171) · googleapis/google-cloud-java@d94eb19 · GitHub
Skip to content

Commit d94eb19

Browse files
schmidt-sebastianpongad
authored andcommitted
Adding Watch conformane tests (#3171)
1 parent b7ae636 commit d94eb19

5 files changed

Lines changed: 4064 additions & 186 deletions

File tree

‎google-cloud-firestore/src/main/java/com/google/cloud/firestore/Watch.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ class Watch implements ApiStreamObserver<ListenResponse> {
6565
* Target ID used by watch. Watch uses a fixed target id since we only support one target per
6666
* stream. The actual target ID we use is arbitrary.
6767
*/
68-
private static final int WATCH_TARGET_ID = 0xD0;
68+
private static final int WATCH_TARGET_ID = 0x1;
6969

7070
private static RetrySettings RETRY_SETTINGS =
7171
RetrySettings.newBuilder()

‎google-cloud-firestore/src/test/java/com/google/cloud/firestore/ConformanceTest.java‎

Lines changed: 164 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,12 @@
2222
import static com.google.cloud.firestore.UserDataConverter.NO_DELETES;
2323
import static org.mockito.Mockito.doAnswer;
2424
import static org.mockito.Mockito.doReturn;
25+
import static org.mockito.Mockito.when;
2526

2627
import com.google.api.core.ApiFuture;
28+
import com.google.api.core.SettableApiFuture;
2729
import com.google.api.gax.rpc.ApiStreamObserver;
30+
import com.google.api.gax.rpc.BidiStreamingCallable;
2831
import com.google.api.gax.rpc.ServerStreamingCallable;
2932
import com.google.api.gax.rpc.UnaryCallable;
3033
import com.google.cloud.firestore.Query.Direction;
@@ -33,33 +36,44 @@
3336
import com.google.cloud.firestore.conformance.TestDefinition.CreateTest;
3437
import com.google.cloud.firestore.conformance.TestDefinition.Cursor;
3538
import com.google.cloud.firestore.conformance.TestDefinition.DeleteTest;
39+
import com.google.cloud.firestore.conformance.TestDefinition.DocChange;
40+
import com.google.cloud.firestore.conformance.TestDefinition.DocChange.Kind;
3641
import com.google.cloud.firestore.conformance.TestDefinition.DocSnapshot;
3742
import com.google.cloud.firestore.conformance.TestDefinition.GetTest;
3843
import com.google.cloud.firestore.conformance.TestDefinition.OrderBy;
3944
import com.google.cloud.firestore.conformance.TestDefinition.SetTest;
45+
import com.google.cloud.firestore.conformance.TestDefinition.Snapshot;
4046
import com.google.cloud.firestore.conformance.TestDefinition.UpdatePathsTest;
4147
import com.google.cloud.firestore.conformance.TestDefinition.UpdateTest;
4248
import com.google.cloud.firestore.conformance.TestDefinition.Where;
4349
import com.google.cloud.firestore.spi.v1beta1.FirestoreRpc;
4450
import com.google.common.base.Preconditions;
4551
import com.google.firestore.v1beta1.BatchGetDocumentsRequest;
4652
import com.google.firestore.v1beta1.CommitRequest;
53+
import com.google.firestore.v1beta1.Document;
54+
import com.google.firestore.v1beta1.ListenRequest;
55+
import com.google.firestore.v1beta1.ListenResponse;
4756
import com.google.firestore.v1beta1.Precondition;
4857
import com.google.firestore.v1beta1.RunQueryRequest;
4958
import com.google.firestore.v1beta1.Value;
5059
import com.google.gson.Gson;
5160
import com.google.gson.reflect.TypeToken;
61+
import com.google.protobuf.GeneratedMessageV3;
5262
import com.google.protobuf.Message;
5363
import java.io.FileInputStream;
5464
import java.io.IOException;
5565
import java.lang.reflect.Type;
5666
import java.util.ArrayList;
5767
import java.util.Collections;
68+
import java.util.Comparator;
5869
import java.util.List;
5970
import java.util.Map;
6071
import java.util.Map.Entry;
6172
import java.util.Set;
6273
import java.util.concurrent.ExecutionException;
74+
import java.util.concurrent.Executors;
75+
import java.util.concurrent.ScheduledExecutorService;
76+
import javax.annotation.Nullable;
6377
import junit.framework.Protectable;
6478
import junit.framework.Test;
6579
import junit.framework.TestResult;
@@ -72,9 +86,11 @@
7286
import org.mockito.ArgumentCaptor;
7387
import org.mockito.Captor;
7488
import org.mockito.Matchers;
89+
import org.mockito.Mock;
7590
import org.mockito.Mockito;
7691
import org.mockito.MockitoAnnotations;
77-
import org.mockito.Spy;
92+
import org.mockito.invocation.InvocationOnMock;
93+
import org.mockito.stubbing.Answer;
7894
import org.threeten.bp.Instant;
7995

8096
@RunWith(AllTests.class)
@@ -90,22 +106,34 @@ private interface ConformanceTestCase extends Test, Describable {}
90106
/** If non-empty, only runs tests included in this set. */
91107
private final Set<String> includedTests = Collections.emptySet();
92108

93-
@Spy
94-
private FirestoreImpl firestoreMock =
95-
new FirestoreImpl(
96-
FirestoreOptions.newBuilder().setProjectId("projectID").build(),
97-
Mockito.mock(FirestoreRpc.class));
98-
99109
@Captor private ArgumentCaptor<CommitRequest> commitCapture;
100110

101111
@Captor private ArgumentCaptor<BatchGetDocumentsRequest> getAllCapture;
102112

103-
@Captor private ArgumentCaptor<ApiStreamObserver> streamObserverCapture;
113+
@Captor private ArgumentCaptor<ApiStreamObserver<GeneratedMessageV3>> streamObserverCapture;
104114

105115
@Captor private ArgumentCaptor<RunQueryRequest> runQueryCapture;
106116

107117
private Gson gson = new Gson();
108118

119+
@Mock private ApiStreamObserver<ListenRequest> noOpRequestObserver;
120+
121+
private FirestoreImpl firestoreMock;
122+
123+
private Query watchQuery;
124+
125+
public ConformanceTest() {
126+
ScheduledExecutorService firestoreExecutor = Executors.newSingleThreadScheduledExecutor();
127+
FirestoreRpc firestoreRpc = Mockito.mock(FirestoreRpc.class);
128+
when(firestoreRpc.getExecutor()).thenReturn(firestoreExecutor);
129+
130+
firestoreMock =
131+
Mockito.spy(
132+
new FirestoreImpl(
133+
FirestoreOptions.newBuilder().setProjectId("projectID").build(), firestoreRpc));
134+
watchQuery = collection("projects/projectID/databases/(default)/documents/C").orderBy("a");
135+
}
136+
109137
/** Generate the test suite based on the tests defined in test_data.binprotos. */
110138
public static TestSuite suite() throws IOException {
111139
TestSuite suite = new TestSuite();
@@ -221,6 +249,84 @@ private Object convertValue(Object data) {
221249
}
222250
}
223251

252+
/** Converts a Kind into a DocumentChange.Type */
253+
private DocumentChange.Type convertKind(Kind kind) {
254+
DocumentChange.Type changeType = null;
255+
256+
switch (kind) {
257+
case ADDED:
258+
changeType = DocumentChange.Type.ADDED;
259+
break;
260+
case REMOVED:
261+
changeType = DocumentChange.Type.REMOVED;
262+
break;
263+
case MODIFIED:
264+
changeType = DocumentChange.Type.MODIFIED;
265+
break;
266+
default:
267+
Assert.fail("Unable to convert document change for kind: " + kind);
268+
break;
269+
}
270+
271+
return changeType;
272+
}
273+
274+
/** Converts a test snapshot to its API representation. */
275+
private QuerySnapshot convertQuerySnapshot(Snapshot testSnapshot) {
276+
Instant readTime =
277+
Instant.ofEpochSecond(
278+
testSnapshot.getReadTime().getSeconds(), testSnapshot.getReadTime().getNanos());
279+
280+
final List<QueryDocumentSnapshot> documentSnapshots = new ArrayList<>();
281+
282+
for (Document document : testSnapshot.getDocsList()) {
283+
documentSnapshots.add(
284+
QueryDocumentSnapshot.fromDocument(firestoreMock, testSnapshot.getReadTime(), document));
285+
}
286+
287+
DocumentSet documentSet =
288+
DocumentSet.emptySet(
289+
new Comparator<QueryDocumentSnapshot>() {
290+
@Override
291+
public int compare(QueryDocumentSnapshot o1, QueryDocumentSnapshot o2) {
292+
// Comparator that preserves the insertion order as defined in the test case.
293+
for (QueryDocumentSnapshot snapshot : documentSnapshots) {
294+
if (o1.equals(o2)) {
295+
return 0;
296+
} else if (o1.equals(snapshot)) {
297+
return -1;
298+
} else if (o2.equals(snapshot)) {
299+
return 1;
300+
}
301+
}
302+
303+
Assert.fail("Unable to find position for document snapshot");
304+
return 0;
305+
}
306+
});
307+
308+
for (QueryDocumentSnapshot snapshot : documentSnapshots) {
309+
documentSet = documentSet.add(snapshot);
310+
}
311+
312+
List<DocumentChange> documentChanges = new ArrayList<>();
313+
314+
for (DocChange documentChange : testSnapshot.getChangesList()) {
315+
QueryDocumentSnapshot documentSnapshot =
316+
QueryDocumentSnapshot.fromDocument(
317+
firestoreMock, testSnapshot.getReadTime(), documentChange.getDoc());
318+
DocumentChange.Type changeType = convertKind(documentChange.getKind());
319+
documentChanges.add(
320+
new DocumentChange(
321+
documentSnapshot,
322+
changeType,
323+
documentChange.getOldIndex(),
324+
documentChange.getNewIndex()));
325+
}
326+
327+
return QuerySnapshot.withChanges(watchQuery, readTime, documentSet, documentChanges);
328+
}
329+
224330
/** Helper function to convert test values in a list to Firestore API types. */
225331
private List<Object> convertArray(List<Object> list) {
226332
for (int i = 0; i < list.size(); ++i) {
@@ -294,6 +400,9 @@ public void protect() throws Throwable {
294400
case QUERY:
295401
runQueryTest(testDefinition.getQuery());
296402
break;
403+
case LISTEN:
404+
runWatchTest(testDefinition.getListen());
405+
break;
297406
default:
298407
throw new UnsupportedOperationException();
299408
}
@@ -330,6 +439,53 @@ private void runQueryTest(TestDefinition.QueryTest testCase) {
330439
}
331440
}
332441

442+
private void runWatchTest(TestDefinition.ListenTest testCase)
443+
throws ExecutionException, InterruptedException {
444+
final SettableApiFuture<Void> testCaseStarted = SettableApiFuture.create();
445+
final SettableApiFuture<Void> testCaseFinished = SettableApiFuture.create();
446+
447+
doAnswer(
448+
new Answer<ApiStreamObserver<ListenRequest>>() {
449+
@Override
450+
public ApiStreamObserver<ListenRequest> answer(InvocationOnMock invocationOnMock) {
451+
testCaseStarted.set(null);
452+
return noOpRequestObserver;
453+
}
454+
})
455+
.when(firestoreMock)
456+
.streamRequest(streamObserverCapture.capture(), Matchers.any(BidiStreamingCallable.class));
457+
458+
final List<Snapshot> expectedSnapshots = new ArrayList<>(testCase.getSnapshotsList());
459+
460+
final ListenerRegistration registration =
461+
watchQuery.addSnapshotListener(
462+
new EventListener<QuerySnapshot>() {
463+
@Override
464+
public void onEvent(
465+
@Nullable QuerySnapshot actualSnapshot, @Nullable FirestoreException error) {
466+
try {
467+
Assert.assertFalse(expectedSnapshots.isEmpty());
468+
Snapshot expectedSnapshot = expectedSnapshots.remove(0);
469+
Assert.assertEquals(convertQuerySnapshot(expectedSnapshot), actualSnapshot);
470+
if (expectedSnapshots.isEmpty()) {
471+
testCaseFinished.set(null);
472+
}
473+
} catch (AssertionError e) {
474+
testCaseFinished.setException(e);
475+
}
476+
}
477+
});
478+
479+
testCaseStarted.get();
480+
481+
for (ListenResponse response : testCase.getResponsesList()) {
482+
streamObserverCapture.getValue().onNext(response);
483+
}
484+
485+
testCaseFinished.get();
486+
registration.remove();
487+
}
488+
333489
/** Returns a new query with 'clause' applied. */
334490
private Query applyClause(Query query, Clause clause) {
335491
FieldPath fieldPath;

‎google-cloud-firestore/src/test/java/com/google/cloud/firestore/WatchTest.java‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@
8484
@RunWith(MockitoJUnitRunner.class)
8585
public class WatchTest {
8686
/** The Target ID used by the Java Firestore SDK. */
87-
private static final int TARGET_ID = 0xD0;
87+
private static final int TARGET_ID = 0x1;
8888

8989
/** The resume token used by this test harness. */
9090
private static final ByteString RESUME_TOKEN = ByteString.copyFromUtf8("token");
@@ -654,7 +654,7 @@ public void queryWatchHandlesIgnoresDifferentTarget() throws InterruptedExceptio
654654

655655
ListenResponse.Builder request = doc("coll/doc1", SINGLE_FIELD_PROTO).toBuilder();
656656
request.getDocumentChangeBuilder().clearTargetIds();
657-
request.getDocumentChangeBuilder().addTargetIds(0x1);
657+
request.getDocumentChangeBuilder().addTargetIds(0x2);
658658

659659
send(request.build());
660660
send(snapshot());

0 commit comments

Comments
 (0)