41
41
import com .google .firebase .firestore .EventListener ;
42
42
import com .google .firebase .firestore .FirebaseFirestore ;
43
43
import com .google .firebase .firestore .FirebaseFirestoreException ;
44
+ import com .google .firebase .firestore .FirebaseFirestoreIntegrationTestFactory ;
44
45
import com .google .firebase .firestore .ListenSource ;
45
46
import com .google .firebase .firestore .LoadBundleTask ;
47
+ import com .google .firebase .firestore .UserDataReader ;
46
48
import com .google .firebase .firestore .auth .User ;
47
49
import com .google .firebase .firestore .bundle .BundleReader ;
48
50
import com .google .firebase .firestore .bundle .BundleSerializer ;
57
59
import com .google .firebase .firestore .core .QueryListener ;
58
60
import com .google .firebase .firestore .core .QueryOrPipeline ;
59
61
import com .google .firebase .firestore .core .SyncEngine ;
62
+ import com .google .firebase .firestore .core .Target ;
60
63
import com .google .firebase .firestore .core .TargetOrPipeline ;
61
64
import com .google .firebase .firestore .local .LocalStore ;
62
65
import com .google .firebase .firestore .local .LruDelegate ;
104
107
import java .util .ArrayList ;
105
108
import java .util .Arrays ;
106
109
import java .util .Collections ;
110
+ import java .util .Comparator ;
107
111
import java .util .HashMap ;
108
112
import java .util .HashSet ;
109
113
import java .util .Iterator ;
@@ -163,6 +167,8 @@ public abstract class SpecTestCase implements RemoteStoreCallback {
163
167
// separated by a space character.
164
168
private static final String TEST_FILTER_PROPERTY = "specTestFilter" ;
165
169
170
+ private static final String NO_PIPELINE_CONVERSION_TAG = "no-pipeline-conversion" ;
171
+
166
172
// Tags on tests that should be excluded from execution, useful to allow the platforms to
167
173
// temporarily diverge or for features that are designed to be platform specific (such as
168
174
// 'multi-client').
@@ -174,6 +180,9 @@ public abstract class SpecTestCase implements RemoteStoreCallback {
174
180
private boolean useEagerGcForMemory ;
175
181
private int maxConcurrentLimboResolutions ;
176
182
private boolean networkEnabled = true ;
183
+ protected boolean usePipelineMode = false ;
184
+
185
+ private FirebaseFirestore db ;
177
186
178
187
//
179
188
// Parts of the Firestore system that the spec tests need to control.
@@ -196,7 +205,7 @@ public abstract class SpecTestCase implements RemoteStoreCallback {
196
205
* A dictionary for tracking the listens on queries. Note that the identity of the listeners is
197
206
* used to remove them.
198
207
*/
199
- private Map <Query , QueryListener > queryListeners ;
208
+ private Map <QueryOrPipeline , QueryListener > queryListeners ;
200
209
201
210
/**
202
211
* Set of documents that are expected to be in limbo with an active target. Verified at every
@@ -291,6 +300,7 @@ protected void specSetUp(JSONObject config) {
291
300
292
301
currentUser = User .UNAUTHENTICATED ;
293
302
databaseInfo = PersistenceTestHelpers .nextDatabaseInfo ();
303
+ db = new FirebaseFirestoreIntegrationTestFactory (databaseInfo .getDatabaseId ()).firestore ;
294
304
295
305
if (config .optInt ("numClients" , 1 ) != 1 ) {
296
306
throw Assert .fail ("The Android client does not support multi-client tests" );
@@ -577,21 +587,30 @@ private void doListen(JSONObject listenSpec) throws Exception {
577
587
Query query = parseQuery (listenSpec .getJSONObject ("query" ));
578
588
ListenOptions options = parseListenOptions (listenSpec );
579
589
590
+ QueryOrPipeline queryOrPipeline ;
591
+ if (usePipelineMode ) {
592
+ queryOrPipeline =
593
+ new QueryOrPipeline .PipelineWrapper (
594
+ query .toRealtimePipeline (db , new UserDataReader (databaseInfo .getDatabaseId ())));
595
+ } else {
596
+ queryOrPipeline = new QueryOrPipeline .QueryWrapper (query );
597
+ }
598
+
580
599
QueryListener listener =
581
600
new QueryListener (
582
- new QueryOrPipeline . QueryWrapper ( query ) ,
601
+ queryOrPipeline ,
583
602
options ,
584
603
(value , error ) -> {
585
604
QueryEvent event = new QueryEvent ();
586
- event .query = query ;
605
+ event .queryOrPipeline = queryOrPipeline ;
587
606
if (value != null ) {
588
607
event .view = value ;
589
608
} else {
590
609
event .error = error ;
591
610
}
592
611
events .add (event );
593
612
});
594
- queryListeners .put (query , listener );
613
+ queryListeners .put (queryOrPipeline , listener );
595
614
queue .runSync (
596
615
() -> {
597
616
int actualId = eventManager .addQueryListener (listener );
@@ -601,7 +620,15 @@ private void doListen(JSONObject listenSpec) throws Exception {
601
620
602
621
private void doUnlisten (JSONArray unlistenSpec ) throws Exception {
603
622
Query query = parseQuery (unlistenSpec .get (1 ));
604
- QueryListener listener = queryListeners .remove (query );
623
+ QueryOrPipeline queryOrPipeline ;
624
+ if (usePipelineMode ) {
625
+ queryOrPipeline =
626
+ new QueryOrPipeline .PipelineWrapper (
627
+ query .toRealtimePipeline (db , new UserDataReader (databaseInfo .getDatabaseId ())));
628
+ } else {
629
+ queryOrPipeline = new QueryOrPipeline .QueryWrapper (query );
630
+ }
631
+ QueryListener listener = queryListeners .remove (queryOrPipeline );
605
632
queue .runSync (() -> eventManager .removeQueryListener (listener ));
606
633
}
607
634
@@ -990,7 +1017,14 @@ private void doStep(JSONObject step) throws Exception {
990
1017
991
1018
private void assertEventMatches (JSONObject expected , QueryEvent actual ) throws JSONException {
992
1019
Query expectedQuery = parseQuery (expected .get ("query" ));
993
- assertEquals (expectedQuery , actual .query );
1020
+ if (usePipelineMode ) {
1021
+ assertEquals (
1022
+ expectedQuery .toRealtimePipeline (db , new UserDataReader (databaseInfo .getDatabaseId ())),
1023
+ actual .queryOrPipeline .pipeline ());
1024
+ } else {
1025
+ assertEquals (expectedQuery , actual .queryOrPipeline .query ());
1026
+ }
1027
+
994
1028
if (expected .has ("errorCode" ) && !Status .fromCodeValue (expected .getInt ("errorCode" )).isOk ()) {
995
1029
assertNotNull (actual .error );
996
1030
assertEquals (expected .getInt ("errorCode" ), actual .error .getCode ().value ());
@@ -1041,7 +1075,7 @@ private void validateExpectedSnapshotEvents(@Nullable JSONArray expectedEventsJs
1041
1075
}
1042
1076
1043
1077
// Sort both the expected and actual events by the query's canonical ID.
1044
- events .sort (( q1 , q2 ) -> q1 . query . getCanonicalId (). compareTo ( q2 . query . getCanonicalId ()));
1078
+ events .sort (Comparator . comparing ( q -> q . queryOrPipeline . canonicalId ()));
1045
1079
1046
1080
List <JSONObject > expectedEvents = new ArrayList <>();
1047
1081
for (int i = 0 ; i < expectedEventsJson .length (); ++i ) {
@@ -1052,6 +1086,16 @@ private void validateExpectedSnapshotEvents(@Nullable JSONArray expectedEventsJs
1052
1086
try {
1053
1087
Query leftQuery = parseQuery (left .get ("query" ));
1054
1088
Query rightQuery = parseQuery (right .get ("query" ));
1089
+ if (usePipelineMode ) {
1090
+ return leftQuery
1091
+ .toRealtimePipeline (db , new UserDataReader (databaseInfo .getDatabaseId ()))
1092
+ .canonicalId ()
1093
+ .compareTo (
1094
+ rightQuery
1095
+ .toRealtimePipeline (db , new UserDataReader (databaseInfo .getDatabaseId ()))
1096
+ .canonicalId ());
1097
+ }
1098
+
1055
1099
return leftQuery .getCanonicalId ().compareTo (rightQuery .getCanonicalId ());
1056
1100
} catch (JSONException e ) {
1057
1101
throw new RuntimeException ("Failed to parse JSON during event sorting" , e );
@@ -1270,9 +1314,25 @@ private void validateActiveTargets() {
1270
1314
// with the single assertEquals on the TargetData objects themselves if the sequenceNumber is
1271
1315
// ever made to be consistent.
1272
1316
// assertEquals(expectedTarget, actualTarget);
1273
-
1274
1317
assertEquals (expectedTarget .getPurpose (), actualTarget .getPurpose ());
1275
- assertEquals (expectedTarget .getTarget (), actualTarget .getTarget ());
1318
+ if (usePipelineMode && !expectedTarget .getPurpose ().equals (QueryPurpose .LIMBO_RESOLUTION )) {
1319
+ Target target = expectedTarget .getTarget ().target ();
1320
+ assertEquals (
1321
+ new TargetOrPipeline .PipelineWrapper (
1322
+ new Query (
1323
+ target .getPath (),
1324
+ target .getCollectionGroup (),
1325
+ target .getFilters (),
1326
+ target .getOrderBy (),
1327
+ target .getLimit (),
1328
+ Query .LimitType .LIMIT_TO_FIRST ,
1329
+ target .getStartAt (),
1330
+ target .getEndAt ())
1331
+ .toRealtimePipeline (db , new UserDataReader (databaseInfo .getDatabaseId ()))),
1332
+ actualTarget .getTarget ());
1333
+ } else {
1334
+ assertEquals (expectedTarget .getTarget (), actualTarget .getTarget ());
1335
+ }
1276
1336
assertEquals (expectedTarget .getTargetId (), actualTarget .getTargetId ());
1277
1337
assertEquals (expectedTarget .getSnapshotVersion (), actualTarget .getSnapshotVersion ());
1278
1338
assertEquals (
@@ -1392,6 +1452,10 @@ public void testSpecTests() throws Exception {
1392
1452
JSONArray steps = testJSON .getJSONArray ("steps" );
1393
1453
Set <String > tags = getTestTags (testJSON );
1394
1454
1455
+ if (name .contains ("Newer " )) {
1456
+ info ("Skipping test: " + name );
1457
+ }
1458
+
1395
1459
boolean runTest ;
1396
1460
if (!shouldRunTest (tags )) {
1397
1461
runTest = false ;
@@ -1443,6 +1507,9 @@ private static boolean anyTestsAreMarkedExclusive(JSONObject fileJSON) throws JS
1443
1507
1444
1508
/** Called before executing each test to see if it should be run. */
1445
1509
private boolean shouldRunTest (Set <String > tags ) {
1510
+ if (usePipelineMode && tags .contains (NO_PIPELINE_CONVERSION_TAG )) {
1511
+ return false ;
1512
+ }
1446
1513
return shouldRun (tags );
1447
1514
}
1448
1515
0 commit comments