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,42 @@ - (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
+ auto expectedQoPForComparison =
756
+ core::QueryOrPipeline (expectedPipeline); // Wrap expected pipeline
757
+
758
+ XCTAssertEqual (actualQueryOrPipeline.CanonicalId (), expectedQoPForComparison.CanonicalId (),
759
+ @" Pipeline canonical IDs do not match. Actual: %@ , Expected: %@ " ,
760
+ MakeNSString (actualQueryOrPipeline.CanonicalId ()),
761
+ MakeNSString (expectedQoPForComparison.CanonicalId ()));
762
+
763
+ } else {
764
+ XCTAssertFalse (actualQueryOrPipeline.IsPipeline (),
765
+ @" In non-pipeline mode, actual event query should be a Query. Actual: %@ " ,
766
+ MakeNSString (actualQueryOrPipeline.ToString ()));
767
+ XCTAssertTrue (actualQueryOrPipeline.query () == expectedJSONQuery,
768
+ @" Queries do not match. Actual: %@ , Expected: %@ " ,
769
+ MakeNSString (actualQueryOrPipeline.query ().ToString ()),
770
+ MakeNSString (expectedJSONQuery.ToString ()));
771
+ }
772
+
726
773
if ([expected[@" errorCode" ] integerValue ] != 0 ) {
727
774
XCTAssertNotNil (actual.error );
728
775
XCTAssertEqual (actual.error .code , [expected[@" errorCode" ] integerValue ]);
@@ -787,14 +834,43 @@ - (void)validateExpectedSnapshotEvents:(NSArray *_Nullable)expectedEvents {
787
834
XCTAssertEqual (events.count , expectedEvents.count );
788
835
events =
789
836
[events sortedArrayUsingComparator: ^NSComparisonResult (FSTQueryEvent *q1, FSTQueryEvent *q2) {
790
- return WrapCompare (q1.query .CanonicalId (), q2.query .CanonicalId ());
791
- }];
792
- expectedEvents = [expectedEvents
793
- 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 ());
837
+ // Use QueryOrPipeline's CanonicalId for sorting
838
+ return WrapCompare (q1.queryOrPipeline .CanonicalId (), q2.queryOrPipeline .CanonicalId ());
797
839
}];
840
+ expectedEvents = [expectedEvents sortedArrayUsingComparator: ^NSComparisonResult (
841
+ NSDictionary *left, NSDictionary *right) {
842
+ // Expected query from JSON is always a core::Query.
843
+ // For sorting consistency with actual events (which might be pipelines),
844
+ // we convert the expected query to QueryOrPipeline then get its CanonicalId.
845
+ // If _convertToPipeline is true, this will effectively sort expected items
846
+ // by their pipeline canonical ID.
847
+ Query leftJSONQuery = [self parseQuery: left[@" query" ]];
848
+ core::QueryOrPipeline leftQoP;
849
+ if (self->_convertToPipeline ) {
850
+ std::vector<std::shared_ptr<api::EvaluableStage>> stages =
851
+ core::ToPipelineStages (leftJSONQuery);
852
+ auto serializer =
853
+ absl::make_unique<remote::Serializer>(self.driver .databaseInfo .database_id ());
854
+ leftQoP =
855
+ core::QueryOrPipeline (api::RealtimePipeline (std::move (stages), std::move (serializer)));
856
+ } else {
857
+ leftQoP = core::QueryOrPipeline (leftJSONQuery);
858
+ }
859
+
860
+ Query rightJSONQuery = [self parseQuery: right[@" query" ]];
861
+ core::QueryOrPipeline rightQoP;
862
+ if (self->_convertToPipeline ) {
863
+ std::vector<std::shared_ptr<api::EvaluableStage>> stages =
864
+ core::ToPipelineStages (rightJSONQuery);
865
+ auto serializer =
866
+ absl::make_unique<remote::Serializer>(self.driver .databaseInfo .database_id ());
867
+ rightQoP =
868
+ core::QueryOrPipeline (api::RealtimePipeline (std::move (stages), std::move (serializer)));
869
+ } else {
870
+ rightQoP = core::QueryOrPipeline (rightJSONQuery);
871
+ }
872
+ return WrapCompare (leftQoP.CanonicalId (), rightQoP.CanonicalId ());
873
+ }];
798
874
799
875
NSUInteger i = 0 ;
800
876
for (; i < expectedEvents.count && i < events.count ; ++i) {
@@ -849,6 +925,7 @@ - (void)validateExpectedState:(nullable NSDictionary *)expectedState {
849
925
NSArray *queriesJson = queryData[@" queries" ];
850
926
std::vector<TargetData> queries;
851
927
for (id queryJson in queriesJson) {
928
+ core::QueryOrPipeline qop;
852
929
Query query = [self parseQuery: queryJson];
853
930
854
931
QueryPurpose purpose = QueryPurpose::Listen;
@@ -980,9 +1057,13 @@ - (void)validateActiveTargets {
980
1057
// is ever made to be consistent.
981
1058
// XCTAssertEqualObjects(actualTargets[targetID], TargetData);
982
1059
const TargetData &actual = found->second ;
983
-
1060
+ auto left = actual.target_or_pipeline ();
1061
+ auto left_p = left.IsPipeline ();
1062
+ auto right = targetData.target_or_pipeline ();
1063
+ auto right_p = right.IsPipeline ();
984
1064
XCTAssertEqual (actual.purpose (), targetData.purpose ());
985
- XCTAssertEqual (actual.target_or_pipeline (), targetData.target_or_pipeline ());
1065
+ XCTAssertEqual (left_p, right_p);
1066
+ XCTAssertEqual (left, right);
986
1067
XCTAssertEqual (actual.target_id (), targetData.target_id ());
987
1068
XCTAssertEqual (actual.snapshot_version (), targetData.snapshot_version ());
988
1069
XCTAssertEqual (actual.resume_token (), targetData.resume_token ());
@@ -1032,6 +1113,8 @@ - (void)runSpecTestSteps:(NSArray *)steps config:(NSDictionary *)config {
1032
1113
- (void )testSpecTests {
1033
1114
if ([self isTestBaseClass ]) return ;
1034
1115
1116
+ // LogSetLevel(firebase::firestore::util::kLogLevelDebug);
1117
+
1035
1118
// Enumerate the .json files containing the spec tests.
1036
1119
NSMutableArray <NSString *> *specFiles = [NSMutableArray array ];
1037
1120
NSMutableArray <NSDictionary *> *parsedSpecs = [NSMutableArray array ];
@@ -1121,10 +1204,10 @@ - (void)testSpecTests {
1121
1204
++testPassCount;
1122
1205
} else {
1123
1206
++testSkipCount;
1124
- NSLog (@" [SKIPPED] Spec test: %@ " , name);
1207
+ // NSLog(@" [SKIPPED] Spec test: %@", name);
1125
1208
NSString *comment = testDescription[@" comment" ];
1126
1209
if (comment) {
1127
- NSLog (@" %@ " , comment);
1210
+ // NSLog(@" %@", comment);
1128
1211
}
1129
1212
}
1130
1213
}];
0 commit comments