Skip to content

[realppl 8] realppl spec tests #14861

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: wuandy/RealPpl_7
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 8 additions & 60 deletions Firestore/Example/Firestore.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

7 changes: 0 additions & 7 deletions Firestore/Example/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,6 @@ if is_platform(:ios)

pod 'leveldb-library'
end

target 'Firestore_FuzzTests_iOS' do
inherit! :search_paths
platform :ios, '13.0'

pod 'LibFuzzer', :podspec => 'LibFuzzer.podspec', :inhibit_warnings => true
end
end
end

Expand Down
14 changes: 14 additions & 0 deletions Firestore/Example/Tests/SpecTests/FSTLevelDBSpecTests.mm
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,18 @@ - (BOOL)shouldRunWithTags:(NSArray<NSString *> *)tags {

@end

/**
* An implementation of FSTLevelDBSpecTests that runs tests in pipeline mode.
*/
@interface FSTLevelDBPipelineSpecTests : FSTLevelDBSpecTests
@end

@implementation FSTLevelDBPipelineSpecTests

- (BOOL)usePipelineMode {
return YES;
}

@end

NS_ASSUME_NONNULL_END
14 changes: 14 additions & 0 deletions Firestore/Example/Tests/SpecTests/FSTMemorySpecTests.mm
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,18 @@ - (BOOL)shouldRunWithTags:(NSArray<NSString *> *)tags {

@end

/**
* An implementation of FSTMemorySpecTests that runs tests in pipeline mode.
*/
@interface FSTMemoryPipelineSpecTests : FSTMemorySpecTests
@end

@implementation FSTMemoryPipelineSpecTests

- (BOOL)usePipelineMode {
return YES;
}

@end

NS_ASSUME_NONNULL_END
8 changes: 7 additions & 1 deletion Firestore/Example/Tests/SpecTests/FSTSpecTests.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,20 @@ extern NSString *const kDurablePersistence;
* + Subclass FSTSpecTests
* + override -persistence to create and return an appropriate Persistence implementation.
*/
@interface FSTSpecTests : XCTestCase
@interface FSTSpecTests : XCTestCase {
@protected
BOOL _convertToPipeline;
}

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

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

/** Determines if tests should run in pipeline mode. Subclasses can override. */
- (BOOL)usePipelineMode;

@end

NS_ASSUME_NONNULL_END
113 changes: 102 additions & 11 deletions Firestore/Example/Tests/SpecTests/FSTSpecTests.mm
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@
// if `kRunBenchmarkTests` is set to 'YES'.
static NSString *const kBenchmarkTag = @"benchmark";

// A tag for tests that should skip its pipeline run.
static NSString *const kNoPipelineConversion = @"no-pipeline-conversion";

NSString *const kEagerGC = @"eager-gc";

NSString *const kDurablePersistence = @"durable-persistence";
Expand Down Expand Up @@ -236,11 +239,14 @@ - (BOOL)shouldRunWithTags:(NSArray<NSString *> *)tags {
return NO;
} else if (!kRunBenchmarkTests && [tags containsObject:kBenchmarkTag]) {
return NO;
} else if (self.usePipelineMode && [tags containsObject:kNoPipelineConversion]) {
return NO;
}
return YES;
}

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

// Default implementation for pipeline mode. Subclasses can override.
- (BOOL)usePipelineMode {
return NO;
}

#pragma mark - Methods for constructing objects from specs.

- (Query)parseQuery:(id)querySpec {
Expand Down Expand Up @@ -645,6 +657,7 @@ - (void)doRestart {
self.driver =
[[FSTSyncEngineTestDriver alloc] initWithPersistence:std::move(persistence)
eagerGC:_useEagerGCForMemory
convertToPipeline:_convertToPipeline // Pass the flag
initialUser:currentUser
outstandingWrites:outstandingWrites
maxConcurrentLimboResolutions:_maxConcurrentLimboResolutions];
Expand Down Expand Up @@ -721,8 +734,41 @@ - (void)doStep:(NSDictionary *)step {
}

- (void)validateEvent:(FSTQueryEvent *)actual matches:(NSDictionary *)expected {
Query expectedQuery = [self parseQuery:expected[@"query"]];
XCTAssertEqual(actual.query, expectedQuery);
// The 'expected' query from JSON is always a standard Query.
Query expectedJSONQuery = [self parseQuery:expected[@"query"]];
core::QueryOrPipeline actualQueryOrPipeline = actual.queryOrPipeline;

if (_convertToPipeline) {
XCTAssertTrue(actualQueryOrPipeline.IsPipeline(),
@"In pipeline mode, actual event query should be a pipeline. Actual: %@",
MakeNSString(actualQueryOrPipeline.ToString()));

// Convert the expected JSON Query to a RealtimePipeline for comparison.
std::vector<std::shared_ptr<api::EvaluableStage>> expectedStages =
core::ToPipelineStages(expectedJSONQuery);
// TODO(specstest): Need access to the database_id for the serializer.
// Assuming self.driver.databaseInfo is accessible and provides it.
// This might require making databaseInfo public or providing a getter in
// FSTSyncEngineTestDriver. For now, proceeding with the assumption it's available.
auto serializer = absl::make_unique<remote::Serializer>(self.driver.databaseInfo.database_id());
api::RealtimePipeline expectedPipeline(std::move(expectedStages), std::move(serializer));
core::QueryOrPipeline expectedQoPForComparison = expectedPipeline; // Wrap expected pipeline

XCTAssertEqual(actualQueryOrPipeline.CanonicalId(), expectedQoPForComparison.CanonicalId(),
@"Pipeline canonical IDs do not match. Actual: %@, Expected: %@",
MakeNSString(actualQueryOrPipeline.CanonicalId()),
MakeNSString(expectedQoPForComparison.CanonicalId()));

} else {
XCTAssertFalse(actualQueryOrPipeline.IsPipeline(),
@"In non-pipeline mode, actual event query should be a Query. Actual: %@",
MakeNSString(actualQueryOrPipeline.ToString()));
XCTAssertTrue(actualQueryOrPipeline.query() == expectedJSONQuery,
@"Queries do not match. Actual: %@, Expected: %@",
MakeNSString(actualQueryOrPipeline.query().ToString()),
MakeNSString(expectedJSONQuery.ToString()));
}

if ([expected[@"errorCode"] integerValue] != 0) {
XCTAssertNotNil(actual.error);
XCTAssertEqual(actual.error.code, [expected[@"errorCode"] integerValue]);
Expand Down Expand Up @@ -787,13 +833,40 @@ - (void)validateExpectedSnapshotEvents:(NSArray *_Nullable)expectedEvents {
XCTAssertEqual(events.count, expectedEvents.count);
events =
[events sortedArrayUsingComparator:^NSComparisonResult(FSTQueryEvent *q1, FSTQueryEvent *q2) {
return WrapCompare(q1.query.CanonicalId(), q2.query.CanonicalId());
// Use QueryOrPipeline's CanonicalId for sorting
return WrapCompare(q1.queryOrPipeline.CanonicalId(), q2.queryOrPipeline.CanonicalId());
}];
expectedEvents = [expectedEvents
sortedArrayUsingComparator:^NSComparisonResult(NSDictionary *left, NSDictionary *right) {
Query leftQuery = [self parseQuery:left[@"query"]];
Query rightQuery = [self parseQuery:right[@"query"]];
return WrapCompare(leftQuery.CanonicalId(), rightQuery.CanonicalId());
// Expected query from JSON is always a core::Query.
// For sorting consistency with actual events (which might be pipelines),
// we convert the expected query to QueryOrPipeline then get its CanonicalId.
// If _convertToPipeline is true, this will effectively sort expected items
// by their pipeline canonical ID.
Query leftJSONQuery = [self parseQuery:left[@"query"]];
core::QueryOrPipeline leftQoP;
if (self->_convertToPipeline) {
std::vector<std::shared_ptr<api::EvaluableStage>> stages =
core::ToPipelineStages(leftJSONQuery);
auto serializer =
absl::make_unique<remote::Serializer>(self.driver.databaseInfo.database_id());
leftQoP = api::RealtimePipeline(std::move(stages), std::move(serializer));
} else {
leftQoP = leftJSONQuery;
}

Query rightJSONQuery = [self parseQuery:right[@"query"]];
core::QueryOrPipeline rightQoP;
if (self->_convertToPipeline) {
std::vector<std::shared_ptr<api::EvaluableStage>> stages =
core::ToPipelineStages(rightJSONQuery);
auto serializer =
absl::make_unique<remote::Serializer>(self.driver.databaseInfo.database_id());
rightQoP = api::RealtimePipeline(std::move(stages), std::move(serializer));
} else {
rightQoP = rightJSONQuery;
}
return WrapCompare(leftQoP.CanonicalId(), rightQoP.CanonicalId());
}];

NSUInteger i = 0;
Expand Down Expand Up @@ -849,14 +922,26 @@ - (void)validateExpectedState:(nullable NSDictionary *)expectedState {
NSArray *queriesJson = queryData[@"queries"];
std::vector<TargetData> queries;
for (id queryJson in queriesJson) {
core::QueryOrPipeline qop;
Query query = [self parseQuery:queryJson];

QueryPurpose purpose = QueryPurpose::Listen;
if ([queryData objectForKey:@"targetPurpose"] != nil) {
purpose = [self parseQueryPurpose:queryData[@"targetPurpose"]];
}

TargetData target_data(query.ToTarget(), targetID, 0, purpose);
if (self->_convertToPipeline &&
purpose != firebase::firestore::local::QueryPurpose::LimboResolution) {
std::vector<std::shared_ptr<api::EvaluableStage>> stages =
core::ToPipelineStages(query);
auto serializer =
absl::make_unique<remote::Serializer>(self.driver.databaseInfo.database_id());
qop = api::RealtimePipeline(std::move(stages), std::move(serializer));
} else {
qop = query;
}

TargetData target_data(qop.ToTargetOrPipeline(), targetID, 0, purpose);
if ([queryData objectForKey:@"resumeToken"] != nil) {
target_data = target_data.WithResumeToken(
MakeResumeToken(queryData[@"resumeToken"]), SnapshotVersion::None());
Expand Down Expand Up @@ -980,9 +1065,13 @@ - (void)validateActiveTargets {
// is ever made to be consistent.
// XCTAssertEqualObjects(actualTargets[targetID], TargetData);
const TargetData &actual = found->second;

auto left = actual.target_or_pipeline();
auto left_p = left.IsPipeline();
auto right = targetData.target_or_pipeline();
auto right_p = right.IsPipeline();
XCTAssertEqual(actual.purpose(), targetData.purpose());
XCTAssertEqual(actual.target_or_pipeline(), targetData.target_or_pipeline());
XCTAssertEqual(left_p, right_p);
XCTAssertEqual(left, right);
XCTAssertEqual(actual.target_id(), targetData.target_id());
XCTAssertEqual(actual.snapshot_version(), targetData.snapshot_version());
XCTAssertEqual(actual.resume_token(), targetData.resume_token());
Expand Down Expand Up @@ -1032,6 +1121,8 @@ - (void)runSpecTestSteps:(NSArray *)steps config:(NSDictionary *)config {
- (void)testSpecTests {
if ([self isTestBaseClass]) return;

// LogSetLevel(firebase::firestore::util::kLogLevelDebug);

// Enumerate the .json files containing the spec tests.
NSMutableArray<NSString *> *specFiles = [NSMutableArray array];
NSMutableArray<NSDictionary *> *parsedSpecs = [NSMutableArray array];
Expand Down Expand Up @@ -1121,10 +1212,10 @@ - (void)testSpecTests {
++testPassCount;
} else {
++testSkipCount;
NSLog(@" [SKIPPED] Spec test: %@", name);
// NSLog(@" [SKIPPED] Spec test: %@", name);
NSString *comment = testDescription[@"comment"];
if (comment) {
NSLog(@" %@", comment);
// NSLog(@" %@", comment);
}
}
}];
Expand Down
9 changes: 7 additions & 2 deletions Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "Firestore/core/src/api/load_bundle_task.h"
#include "Firestore/core/src/bundle/bundle_reader.h"
#include "Firestore/core/src/core/database_info.h"
#include "Firestore/core/src/core/pipeline_util.h" // For QueryOrPipeline
#include "Firestore/core/src/core/query.h"
#include "Firestore/core/src/core/view_snapshot.h"
#include "Firestore/core/src/credentials/user.h"
Expand Down Expand Up @@ -66,7 +67,7 @@ NS_ASSUME_NONNULL_BEGIN
* given query.
*/
@interface FSTQueryEvent : NSObject
@property(nonatomic, assign) core::Query query;
@property(nonatomic, assign) core::QueryOrPipeline queryOrPipeline;
@property(nonatomic, strong, nullable) NSError *error;

- (const absl::optional<core::ViewSnapshot> &)viewSnapshot;
Expand Down Expand Up @@ -115,7 +116,10 @@ typedef std::
*
* Each method on the driver injects a different event into the system.
*/
@interface FSTSyncEngineTestDriver : NSObject
@interface FSTSyncEngineTestDriver : NSObject {
@protected
BOOL _convertToPipeline;
}

/**
* Initializes the underlying FSTSyncEngine with the given local persistence implementation and
Expand All @@ -124,6 +128,7 @@ typedef std::
*/
- (instancetype)initWithPersistence:(std::unique_ptr<local::Persistence>)persistence
eagerGC:(BOOL)eagerGC
convertToPipeline:(BOOL)convertToPipeline
initialUser:(const credentials::User &)initialUser
outstandingWrites:(const FSTOutstandingWriteQueues &)outstandingWrites
maxConcurrentLimboResolutions:(size_t)maxConcurrentLimboResolutions NS_DESIGNATED_INITIALIZER;
Expand Down
Loading
Loading