Skip to content

Commit 38b72bd

Browse files
committed
[realppl 8] realppl spec tests
1 parent 4c03e49 commit 38b72bd

29 files changed

+1429758
-341
lines changed

Firestore/Example/Firestore.xcodeproj/project.pbxproj

Lines changed: 31 additions & 69 deletions
Large diffs are not rendered by default.

Firestore/Example/Tests/SpecTests/FSTLevelDBSpecTests.mm

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,18 @@ - (BOOL)shouldRunWithTags:(NSArray<NSString *> *)tags {
6262

6363
@end
6464

65+
/**
66+
* An implementation of FSTLevelDBSpecTests that runs tests in pipeline mode.
67+
*/
68+
@interface FSTLevelDBPipelineSpecTests : FSTLevelDBSpecTests
69+
@end
70+
71+
@implementation FSTLevelDBPipelineSpecTests
72+
73+
- (BOOL)usePipelineMode {
74+
return YES;
75+
}
76+
77+
@end
78+
6579
NS_ASSUME_NONNULL_END

Firestore/Example/Tests/SpecTests/FSTMemorySpecTests.mm

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,18 @@ - (BOOL)shouldRunWithTags:(NSArray<NSString *> *)tags {
5757

5858
@end
5959

60+
/**
61+
* An implementation of FSTMemorySpecTests that runs tests in pipeline mode.
62+
*/
63+
@interface FSTMemoryPipelineSpecTests : FSTMemorySpecTests
64+
@end
65+
66+
@implementation FSTMemoryPipelineSpecTests
67+
68+
- (BOOL)usePipelineMode {
69+
return YES;
70+
}
71+
72+
@end
73+
6074
NS_ASSUME_NONNULL_END

Firestore/Example/Tests/SpecTests/FSTSpecTests.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,20 @@ extern NSString *const kDurablePersistence;
3737
* + Subclass FSTSpecTests
3838
* + override -persistence to create and return an appropriate Persistence implementation.
3939
*/
40-
@interface FSTSpecTests : XCTestCase
40+
@interface FSTSpecTests : XCTestCase {
41+
@protected
42+
BOOL _convertToPipeline;
43+
}
4144

4245
/** Based on its tags, determine whether the test case should run. */
4346
- (BOOL)shouldRunWithTags:(NSArray<NSString *> *)tags;
4447

4548
/** Do any necessary setup for a single spec test */
4649
- (void)setUpForSpecWithConfig:(NSDictionary *)config;
4750

51+
/** Determines if tests should run in pipeline mode. Subclasses can override. */
52+
- (BOOL)usePipelineMode;
53+
4854
@end
4955

5056
NS_ASSUME_NONNULL_END

Firestore/Example/Tests/SpecTests/FSTSpecTests.mm

Lines changed: 96 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,9 @@
158158
// if `kRunBenchmarkTests` is set to 'YES'.
159159
static NSString *const kBenchmarkTag = @"benchmark";
160160

161+
// A tag for tests that should skip its pipeline run.
162+
static NSString *const kNoPipelineConversion = @"no-pipeline-conversion";
163+
161164
NSString *const kEagerGC = @"eager-gc";
162165

163166
NSString *const kDurablePersistence = @"durable-persistence";
@@ -236,11 +239,14 @@ - (BOOL)shouldRunWithTags:(NSArray<NSString *> *)tags {
236239
return NO;
237240
} else if (!kRunBenchmarkTests && [tags containsObject:kBenchmarkTag]) {
238241
return NO;
242+
} else if (self.usePipelineMode && [tags containsObject:kNoPipelineConversion]) {
243+
return NO;
239244
}
240245
return YES;
241246
}
242247

243248
- (void)setUpForSpecWithConfig:(NSDictionary *)config {
249+
_convertToPipeline = [self usePipelineMode]; // Call new method
244250
_reader = FSTTestUserDataReader();
245251
std::unique_ptr<Executor> user_executor = Executor::CreateSerial("user executor");
246252
user_executor_ = absl::ShareUniquePtr(std::move(user_executor));
@@ -261,6 +267,7 @@ - (void)setUpForSpecWithConfig:(NSDictionary *)config {
261267
self.driver =
262268
[[FSTSyncEngineTestDriver alloc] initWithPersistence:std::move(persistence)
263269
eagerGC:_useEagerGCForMemory
270+
convertToPipeline:_convertToPipeline // Pass the flag
264271
initialUser:User::Unauthenticated()
265272
outstandingWrites:{}
266273
maxConcurrentLimboResolutions:_maxConcurrentLimboResolutions];
@@ -282,6 +289,11 @@ - (BOOL)isTestBaseClass {
282289
return [self class] == [FSTSpecTests class];
283290
}
284291

292+
// Default implementation for pipeline mode. Subclasses can override.
293+
- (BOOL)usePipelineMode {
294+
return NO;
295+
}
296+
285297
#pragma mark - Methods for constructing objects from specs.
286298

287299
- (Query)parseQuery:(id)querySpec {
@@ -645,6 +657,7 @@ - (void)doRestart {
645657
self.driver =
646658
[[FSTSyncEngineTestDriver alloc] initWithPersistence:std::move(persistence)
647659
eagerGC:_useEagerGCForMemory
660+
convertToPipeline:_convertToPipeline // Pass the flag
648661
initialUser:currentUser
649662
outstandingWrites:outstandingWrites
650663
maxConcurrentLimboResolutions:_maxConcurrentLimboResolutions];
@@ -721,8 +734,42 @@ - (void)doStep:(NSDictionary *)step {
721734
}
722735

723736
- (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+
726773
if ([expected[@"errorCode"] integerValue] != 0) {
727774
XCTAssertNotNil(actual.error);
728775
XCTAssertEqual(actual.error.code, [expected[@"errorCode"] integerValue]);
@@ -787,14 +834,43 @@ - (void)validateExpectedSnapshotEvents:(NSArray *_Nullable)expectedEvents {
787834
XCTAssertEqual(events.count, expectedEvents.count);
788835
events =
789836
[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());
797839
}];
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+
}];
798874

799875
NSUInteger i = 0;
800876
for (; i < expectedEvents.count && i < events.count; ++i) {
@@ -849,6 +925,7 @@ - (void)validateExpectedState:(nullable NSDictionary *)expectedState {
849925
NSArray *queriesJson = queryData[@"queries"];
850926
std::vector<TargetData> queries;
851927
for (id queryJson in queriesJson) {
928+
core::QueryOrPipeline qop;
852929
Query query = [self parseQuery:queryJson];
853930

854931
QueryPurpose purpose = QueryPurpose::Listen;
@@ -980,9 +1057,13 @@ - (void)validateActiveTargets {
9801057
// is ever made to be consistent.
9811058
// XCTAssertEqualObjects(actualTargets[targetID], TargetData);
9821059
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();
9841064
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);
9861067
XCTAssertEqual(actual.target_id(), targetData.target_id());
9871068
XCTAssertEqual(actual.snapshot_version(), targetData.snapshot_version());
9881069
XCTAssertEqual(actual.resume_token(), targetData.resume_token());
@@ -1032,6 +1113,8 @@ - (void)runSpecTestSteps:(NSArray *)steps config:(NSDictionary *)config {
10321113
- (void)testSpecTests {
10331114
if ([self isTestBaseClass]) return;
10341115

1116+
// LogSetLevel(firebase::firestore::util::kLogLevelDebug);
1117+
10351118
// Enumerate the .json files containing the spec tests.
10361119
NSMutableArray<NSString *> *specFiles = [NSMutableArray array];
10371120
NSMutableArray<NSDictionary *> *parsedSpecs = [NSMutableArray array];
@@ -1121,10 +1204,10 @@ - (void)testSpecTests {
11211204
++testPassCount;
11221205
} else {
11231206
++testSkipCount;
1124-
NSLog(@" [SKIPPED] Spec test: %@", name);
1207+
// NSLog(@" [SKIPPED] Spec test: %@", name);
11251208
NSString *comment = testDescription[@"comment"];
11261209
if (comment) {
1127-
NSLog(@" %@", comment);
1210+
// NSLog(@" %@", comment);
11281211
}
11291212
}
11301213
}];

Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "Firestore/core/src/api/load_bundle_task.h"
2727
#include "Firestore/core/src/bundle/bundle_reader.h"
2828
#include "Firestore/core/src/core/database_info.h"
29+
#include "Firestore/core/src/core/pipeline_util.h" // For QueryOrPipeline
2930
#include "Firestore/core/src/core/query.h"
3031
#include "Firestore/core/src/core/view_snapshot.h"
3132
#include "Firestore/core/src/credentials/user.h"
@@ -66,7 +67,7 @@ NS_ASSUME_NONNULL_BEGIN
6667
* given query.
6768
*/
6869
@interface FSTQueryEvent : NSObject
69-
@property(nonatomic, assign) core::Query query;
70+
@property(nonatomic, assign) core::QueryOrPipeline queryOrPipeline;
7071
@property(nonatomic, strong, nullable) NSError *error;
7172

7273
- (const absl::optional<core::ViewSnapshot> &)viewSnapshot;
@@ -115,7 +116,10 @@ typedef std::
115116
*
116117
* Each method on the driver injects a different event into the system.
117118
*/
118-
@interface FSTSyncEngineTestDriver : NSObject
119+
@interface FSTSyncEngineTestDriver : NSObject {
120+
@protected
121+
BOOL _convertToPipeline;
122+
}
119123

120124
/**
121125
* Initializes the underlying FSTSyncEngine with the given local persistence implementation and
@@ -124,6 +128,7 @@ typedef std::
124128
*/
125129
- (instancetype)initWithPersistence:(std::unique_ptr<local::Persistence>)persistence
126130
eagerGC:(BOOL)eagerGC
131+
convertToPipeline:(BOOL)convertToPipeline
127132
initialUser:(const credentials::User &)initialUser
128133
outstandingWrites:(const FSTOutstandingWriteQueues &)outstandingWrites
129134
maxConcurrentLimboResolutions:(size_t)maxConcurrentLimboResolutions NS_DESIGNATED_INITIALIZER;

0 commit comments

Comments
 (0)