158
158
// if `kRunBenchmarkTests` is set to 'YES'.
159
159
static NSString *const kBenchmarkTag = @" benchmark" ;
160
160
161
+ // A tag for tests that should skip its pipeline run.
162
+ static NSString *const kNoPipelineConversion = @" no-pipeline-conversion" ;
163
+
161
164
NSString *const kEagerGC = @" eager-gc" ;
162
165
163
166
NSString *const kDurablePersistence = @" durable-persistence" ;
@@ -236,11 +239,14 @@ - (BOOL)shouldRunWithTags:(NSArray<NSString *> *)tags {
236
239
return NO ;
237
240
} else if (!kRunBenchmarkTests && [tags containsObject: kBenchmarkTag ]) {
238
241
return NO ;
242
+ } else if (self.usePipelineMode && [tags containsObject: kNoPipelineConversion ]) {
243
+ return NO ;
239
244
}
240
245
return YES ;
241
246
}
242
247
243
248
- (void )setUpForSpecWithConfig : (NSDictionary *)config {
249
+ _convertToPipeline = [self usePipelineMode ]; // Call new method
244
250
_reader = FSTTestUserDataReader ();
245
251
std::unique_ptr<Executor> user_executor = Executor::CreateSerial (" user executor" );
246
252
user_executor_ = absl::ShareUniquePtr (std::move (user_executor));
@@ -261,6 +267,7 @@ - (void)setUpForSpecWithConfig:(NSDictionary *)config {
261
267
self.driver =
262
268
[[FSTSyncEngineTestDriver alloc ] initWithPersistence: std: :move (persistence)
263
269
eagerGC: _useEagerGCForMemory
270
+ convertToPipeline: _convertToPipeline // Pass the flag
264
271
initialUser:User: :Unauthenticated ()
265
272
outstandingWrites: {}
266
273
maxConcurrentLimboResolutions: _maxConcurrentLimboResolutions];
@@ -282,6 +289,11 @@ - (BOOL)isTestBaseClass {
282
289
return [self class ] == [FSTSpecTests class ];
283
290
}
284
291
292
+ // Default implementation for pipeline mode. Subclasses can override.
293
+ - (BOOL )usePipelineMode {
294
+ return NO ;
295
+ }
296
+
285
297
#pragma mark - Methods for constructing objects from specs.
286
298
287
299
- (Query)parseQuery : (id )querySpec {
@@ -645,6 +657,7 @@ - (void)doRestart {
645
657
self.driver =
646
658
[[FSTSyncEngineTestDriver alloc ] initWithPersistence: std: :move (persistence)
647
659
eagerGC: _useEagerGCForMemory
660
+ convertToPipeline: _convertToPipeline // Pass the flag
648
661
initialUser: currentUser
649
662
outstandingWrites: outstandingWrites
650
663
maxConcurrentLimboResolutions: _maxConcurrentLimboResolutions];
@@ -721,8 +734,41 @@ - (void)doStep:(NSDictionary *)step {
721
734
}
722
735
723
736
- (void )validateEvent : (FSTQueryEvent *)actual matches : (NSDictionary *)expected {
724
- Query expectedQuery = [self parseQuery: expected[@" query" ]];
725
- XCTAssertEqual (actual.query , expectedQuery);
737
+ // The 'expected' query from JSON is always a standard Query.
738
+ Query expectedJSONQuery = [self parseQuery: expected[@" query" ]];
739
+ core::QueryOrPipeline actualQueryOrPipeline = actual.queryOrPipeline ;
740
+
741
+ if (_convertToPipeline) {
742
+ XCTAssertTrue (actualQueryOrPipeline.IsPipeline (),
743
+ @" In pipeline mode, actual event query should be a pipeline. Actual: %@ " ,
744
+ MakeNSString (actualQueryOrPipeline.ToString ()));
745
+
746
+ // Convert the expected JSON Query to a RealtimePipeline for comparison.
747
+ std::vector<std::shared_ptr<api::EvaluableStage>> expectedStages =
748
+ core::ToPipelineStages (expectedJSONQuery);
749
+ // TODO(specstest): Need access to the database_id for the serializer.
750
+ // Assuming self.driver.databaseInfo is accessible and provides it.
751
+ // This might require making databaseInfo public or providing a getter in
752
+ // FSTSyncEngineTestDriver. For now, proceeding with the assumption it's available.
753
+ auto serializer = absl::make_unique<remote::Serializer>(self.driver .databaseInfo .database_id ());
754
+ api::RealtimePipeline expectedPipeline (std::move (expectedStages), std::move (serializer));
755
+ core::QueryOrPipeline expectedQoPForComparison = expectedPipeline; // Wrap expected pipeline
756
+
757
+ XCTAssertEqual (actualQueryOrPipeline.CanonicalId (), expectedQoPForComparison.CanonicalId (),
758
+ @" Pipeline canonical IDs do not match. Actual: %@ , Expected: %@ " ,
759
+ MakeNSString (actualQueryOrPipeline.CanonicalId ()),
760
+ MakeNSString (expectedQoPForComparison.CanonicalId ()));
761
+
762
+ } else {
763
+ XCTAssertFalse (actualQueryOrPipeline.IsPipeline (),
764
+ @" In non-pipeline mode, actual event query should be a Query. Actual: %@ " ,
765
+ MakeNSString (actualQueryOrPipeline.ToString ()));
766
+ XCTAssertTrue (actualQueryOrPipeline.query () == expectedJSONQuery,
767
+ @" Queries do not match. Actual: %@ , Expected: %@ " ,
768
+ MakeNSString (actualQueryOrPipeline.query ().ToString ()),
769
+ MakeNSString (expectedJSONQuery.ToString ()));
770
+ }
771
+
726
772
if ([expected[@" errorCode" ] integerValue ] != 0 ) {
727
773
XCTAssertNotNil (actual.error );
728
774
XCTAssertEqual (actual.error .code , [expected[@" errorCode" ] integerValue ]);
@@ -787,13 +833,40 @@ - (void)validateExpectedSnapshotEvents:(NSArray *_Nullable)expectedEvents {
787
833
XCTAssertEqual (events.count , expectedEvents.count );
788
834
events =
789
835
[events sortedArrayUsingComparator: ^NSComparisonResult (FSTQueryEvent *q1, FSTQueryEvent *q2) {
790
- return WrapCompare (q1.query .CanonicalId (), q2.query .CanonicalId ());
836
+ // Use QueryOrPipeline's CanonicalId for sorting
837
+ return WrapCompare (q1.queryOrPipeline .CanonicalId (), q2.queryOrPipeline .CanonicalId ());
791
838
}];
792
839
expectedEvents = [expectedEvents
793
840
sortedArrayUsingComparator: ^NSComparisonResult (NSDictionary *left, NSDictionary *right) {
794
- Query leftQuery = [self parseQuery: left[@" query" ]];
795
- Query rightQuery = [self parseQuery: right[@" query" ]];
796
- return WrapCompare (leftQuery.CanonicalId (), rightQuery.CanonicalId ());
841
+ // Expected query from JSON is always a core::Query.
842
+ // For sorting consistency with actual events (which might be pipelines),
843
+ // we convert the expected query to QueryOrPipeline then get its CanonicalId.
844
+ // If _convertToPipeline is true, this will effectively sort expected items
845
+ // by their pipeline canonical ID.
846
+ Query leftJSONQuery = [self parseQuery: left[@" query" ]];
847
+ core::QueryOrPipeline leftQoP;
848
+ if (self->_convertToPipeline ) {
849
+ std::vector<std::shared_ptr<api::EvaluableStage>> stages =
850
+ core::ToPipelineStages (leftJSONQuery);
851
+ auto serializer =
852
+ absl::make_unique<remote::Serializer>(self.driver .databaseInfo .database_id ());
853
+ leftQoP = api::RealtimePipeline (std::move (stages), std::move (serializer));
854
+ } else {
855
+ leftQoP = leftJSONQuery;
856
+ }
857
+
858
+ Query rightJSONQuery = [self parseQuery: right[@" query" ]];
859
+ core::QueryOrPipeline rightQoP;
860
+ if (self->_convertToPipeline ) {
861
+ std::vector<std::shared_ptr<api::EvaluableStage>> stages =
862
+ core::ToPipelineStages (rightJSONQuery);
863
+ auto serializer =
864
+ absl::make_unique<remote::Serializer>(self.driver .databaseInfo .database_id ());
865
+ rightQoP = api::RealtimePipeline (std::move (stages), std::move (serializer));
866
+ } else {
867
+ rightQoP = rightJSONQuery;
868
+ }
869
+ return WrapCompare (leftQoP.CanonicalId (), rightQoP.CanonicalId ());
797
870
}];
798
871
799
872
NSUInteger i = 0 ;
@@ -849,14 +922,26 @@ - (void)validateExpectedState:(nullable NSDictionary *)expectedState {
849
922
NSArray *queriesJson = queryData[@" queries" ];
850
923
std::vector<TargetData> queries;
851
924
for (id queryJson in queriesJson) {
925
+ core::QueryOrPipeline qop;
852
926
Query query = [self parseQuery: queryJson];
853
927
854
928
QueryPurpose purpose = QueryPurpose::Listen;
855
929
if ([queryData objectForKey: @" targetPurpose" ] != nil ) {
856
930
purpose = [self parseQueryPurpose: queryData[@" targetPurpose" ]];
857
931
}
858
932
859
- TargetData target_data (query.ToTarget (), targetID, 0 , purpose);
933
+ if (self->_convertToPipeline &&
934
+ purpose != firebase::firestore::local::QueryPurpose::LimboResolution) {
935
+ std::vector<std::shared_ptr<api::EvaluableStage>> stages =
936
+ core::ToPipelineStages (query);
937
+ auto serializer =
938
+ absl::make_unique<remote::Serializer>(self.driver .databaseInfo .database_id ());
939
+ qop = api::RealtimePipeline (std::move (stages), std::move (serializer));
940
+ } else {
941
+ qop = query;
942
+ }
943
+
944
+ TargetData target_data (qop.ToTargetOrPipeline (), targetID, 0 , purpose);
860
945
if ([queryData objectForKey: @" resumeToken" ] != nil ) {
861
946
target_data = target_data.WithResumeToken (
862
947
MakeResumeToken (queryData[@" resumeToken" ]), SnapshotVersion::None ());
@@ -980,9 +1065,13 @@ - (void)validateActiveTargets {
980
1065
// is ever made to be consistent.
981
1066
// XCTAssertEqualObjects(actualTargets[targetID], TargetData);
982
1067
const TargetData &actual = found->second ;
983
-
1068
+ auto left = actual.target_or_pipeline ();
1069
+ auto left_p = left.IsPipeline ();
1070
+ auto right = targetData.target_or_pipeline ();
1071
+ auto right_p = right.IsPipeline ();
984
1072
XCTAssertEqual (actual.purpose (), targetData.purpose ());
985
- XCTAssertEqual (actual.target_or_pipeline (), targetData.target_or_pipeline ());
1073
+ XCTAssertEqual (left_p, right_p);
1074
+ XCTAssertEqual (left, right);
986
1075
XCTAssertEqual (actual.target_id (), targetData.target_id ());
987
1076
XCTAssertEqual (actual.snapshot_version (), targetData.snapshot_version ());
988
1077
XCTAssertEqual (actual.resume_token (), targetData.resume_token ());
@@ -1032,6 +1121,8 @@ - (void)runSpecTestSteps:(NSArray *)steps config:(NSDictionary *)config {
1032
1121
- (void )testSpecTests {
1033
1122
if ([self isTestBaseClass ]) return ;
1034
1123
1124
+ // LogSetLevel(firebase::firestore::util::kLogLevelDebug);
1125
+
1035
1126
// Enumerate the .json files containing the spec tests.
1036
1127
NSMutableArray <NSString *> *specFiles = [NSMutableArray array ];
1037
1128
NSMutableArray <NSDictionary *> *parsedSpecs = [NSMutableArray array ];
@@ -1121,10 +1212,10 @@ - (void)testSpecTests {
1121
1212
++testPassCount;
1122
1213
} else {
1123
1214
++testSkipCount;
1124
- NSLog (@" [SKIPPED] Spec test: %@ " , name);
1215
+ // NSLog(@" [SKIPPED] Spec test: %@", name);
1125
1216
NSString *comment = testDescription[@" comment" ];
1126
1217
if (comment) {
1127
- NSLog (@" %@ " , comment);
1218
+ // NSLog(@" %@", comment);
1128
1219
}
1129
1220
}
1130
1221
}];
0 commit comments