diff --git a/CHANGELOG.md b/CHANGELOG.md index be1de46401..d11be2fdde 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,8 @@ x.y.z Release notes (yyyy-MM-dd) ============================================================= ### Enhancements -* None. +* Greatly improve the performance of obtaining cached Realm instances in Swift + when using a sync configuration. ### Fixed * Add missing `initialSubscription` and `rerunOnOpen` to copyWithZone method on `RLMRealmConfiguration`. This resulted in incorrect values when using `RLMRealmConfiguration.defaultConfiguration`. diff --git a/Realm/ObjectServerTests/RLMFlexibleSyncServerTests.mm b/Realm/ObjectServerTests/RLMFlexibleSyncServerTests.mm index 7a9e70d61e..afda74b854 100644 --- a/Realm/ObjectServerTests/RLMFlexibleSyncServerTests.mm +++ b/Realm/ObjectServerTests/RLMFlexibleSyncServerTests.mm @@ -953,7 +953,7 @@ - (void)testFlexibleSyncInitialSubscriptionAwait { CHECK_COUNT(11, Person, realm); [ex fulfill]; }]; - [self waitForExpectationsWithTimeout:10.0 handler:nil]; + [self waitForExpectationsWithTimeout:30.0 handler:nil]; } - (void)testFlexibleSyncInitialSubscriptionDoNotRerunOnOpen { @@ -1007,7 +1007,7 @@ - (void)testFlexibleSyncInitialSubscriptionRerunOnOpen { CHECK_COUNT(11, Person, realm); [ex fulfill]; }]; - [self waitForExpectationsWithTimeout:10.0 handler:nil]; + [self waitForExpectationsWithTimeout:30.0 handler:nil]; XCTAssertEqual(openCount, 1); __block RLMRealm *realm; @@ -1021,7 +1021,7 @@ - (void)testFlexibleSyncInitialSubscriptionRerunOnOpen { CHECK_COUNT(16, Person, realm); [ex2 fulfill]; }]; - [self waitForExpectationsWithTimeout:10.0 handler:nil]; + [self waitForExpectationsWithTimeout:30.0 handler:nil]; XCTAssertEqual(openCount, 2); [self dispatchAsyncAndWait:^{ @@ -1072,7 +1072,7 @@ - (void)testFlexibleSyncInitialOnConnectionTimeout { XCTAssertNil(realm); [ex fulfill]; }]; - [self waitForExpectationsWithTimeout:10.0 handler:nil]; + [self waitForExpectationsWithTimeout:30.0 handler:nil]; [proxy stop]; } @@ -1095,6 +1095,6 @@ - (void)testFlexibleSyncInitialSubscriptionThrowsError { [ex fulfill]; }]; - [self waitForExpectationsWithTimeout:10.0 handler:nil]; + [self waitForExpectationsWithTimeout:30.0 handler:nil]; } @end diff --git a/Realm/ObjectServerTests/RLMObjectServerTests.mm b/Realm/ObjectServerTests/RLMObjectServerTests.mm index b3c522797c..6001bfc03a 100644 --- a/Realm/ObjectServerTests/RLMObjectServerTests.mm +++ b/Realm/ObjectServerTests/RLMObjectServerTests.mm @@ -142,7 +142,7 @@ - (void)testLogoutSpecificUser { [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:10.0 handler:nil]; + [self waitForExpectationsWithTimeout:30.0 handler:nil]; } - (void)testSwitchUser { @@ -205,14 +205,14 @@ - (void)testDeviceRegistration { XCTAssertNil(error); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:10.0 handler:nil]; + [self waitForExpectationsWithTimeout:30.0 handler:nil]; expectation = [self expectationWithDescription:@"should deregister device"]; [client deregisterDeviceForUser:self.app.currentUser completion:^(NSError *error) { XCTAssertNil(error); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:10.0 handler:nil]; + [self waitForExpectationsWithTimeout:30.0 handler:nil]; } // FIXME: Reenable once possible underlying race condition is understood @@ -576,7 +576,7 @@ - (void)testSyncErrorHandlerErrorDomain { encryptionKey:nil stopPolicy:RLMSyncStopPolicyAfterChangesUploaded]; - [self waitForExpectationsWithTimeout:10.0 handler:nil]; + [self waitForExpectationsWithTimeout:30.0 handler:nil]; } #pragma mark - User Profile @@ -1432,7 +1432,7 @@ - (void)testClientReset { [ex fulfill]; }; [user simulateClientResetErrorForSession:@"realm_id"]; - [self waitForExpectationsWithTimeout:10 handler:nil]; + [self waitForExpectationsWithTimeout:30 handler:nil]; XCTAssertNotNil(theError); XCTAssertTrue(theError.code == RLMSyncErrorClientResetError); NSString *pathValue = [theError rlmSync_clientResetBackedUpRealmPath]; @@ -1457,7 +1457,7 @@ - (void)testClientResetManualInitiation { [ex fulfill]; }; [user simulateClientResetErrorForSession:partitionValue]; - [self waitForExpectationsWithTimeout:10 handler:nil]; + [self waitForExpectationsWithTimeout:30 handler:nil]; XCTAssertNotNil(theError); } // At this point the Realm should be invalidated and client reset should be possible. @@ -1550,7 +1550,7 @@ - (void)testStreamingDownloadNotifier { }]; // Wait for the child process to upload everything. RLMRunChildAndWait(); - [self waitForExpectationsWithTimeout:10.0 handler:nil]; + [self waitForExpectationsWithTimeout:30.0 handler:nil]; [token invalidate]; // The notifier should have been called at least twice: once at the beginning and at least once // to report progress. @@ -1592,7 +1592,7 @@ - (void)testStreamingUploadNotifier { } [realm commitWriteTransaction]; // Wait for upload to begin and finish - [self waitForExpectationsWithTimeout:10.0 handler:nil]; + [self waitForExpectationsWithTimeout:30.0 handler:nil]; [token invalidate]; // The notifier should have been called at least twice: once at the beginning and at least once // to report progress. @@ -1633,7 +1633,7 @@ - (void)testDownloadRealm { return 0; }; XCTAssertNil(RLMGetAnyCachedRealmForPath(c.pathOnDisk.UTF8String)); - [self waitForExpectationsWithTimeout:10.0 handler:nil]; + [self waitForExpectationsWithTimeout:30.0 handler:nil]; XCTAssertGreaterThan(fileSize(c.pathOnDisk), 0U); XCTAssertNil(RLMGetAnyCachedRealmForPath(c.pathOnDisk.UTF8String)); } @@ -1799,7 +1799,7 @@ - (void)testAsyncOpenConnectionTimeout { XCTAssertNil(realm); [ex fulfill]; }]; - [self waitForExpectationsWithTimeout:10.0 handler:nil]; + [self waitForExpectationsWithTimeout:30.0 handler:nil]; // Delay below the timeout should work proxy.delay = 0.5; @@ -1812,7 +1812,7 @@ - (void)testAsyncOpenConnectionTimeout { XCTAssertNil(error); [ex fulfill]; }]; - [self waitForExpectationsWithTimeout:10.0 handler:nil]; + [self waitForExpectationsWithTimeout:30.0 handler:nil]; [proxy stop]; } @@ -2501,7 +2501,7 @@ - (void)testMongoDelete { RLMMongoDatabase *database = [client databaseWithName:@"test_data"]; RLMMongoCollection *collection = [database collectionWithName:@"Dog"]; - NSArray *objectIds = [self insertDogDocuments:collection]; + NSArray *objectIds = [self prepareDogDocumentsIn:collection]; RLMObjectId *rexObjectId = objectIds[1]; XCTestExpectation *deleteOneExpectation1 = [self expectationWithDescription:@"should delete first document in collection"]; @@ -2622,8 +2622,15 @@ - (void)testWatchWithMatchFilterAsync { [self performWatchWithMatchFilterTest:asyncQueue]; } -- (NSArray *)insertDogDocuments:(RLMMongoCollection *)collection { +- (NSArray *)prepareDogDocumentsIn:(RLMMongoCollection *)collection { __block NSArray *objectIds; + XCTestExpectation *ex = [self expectationWithDescription:@"delete existing documents"]; + [collection deleteManyDocumentsWhere:@{} completion:^(NSInteger, NSError *error) { + XCTAssertNil(error); + [ex fulfill]; + }]; + [self waitForExpectations:@[ex] timeout:60.0]; + XCTestExpectation *insertManyExpectation = [self expectationWithDescription:@"should insert documents"]; [collection insertManyDocuments:@[ @{@"name": @"fido", @"breed": @"cane corso"}, @@ -2646,7 +2653,7 @@ - (void)performWatchWithMatchFilterTest:(nullable dispatch_queue_t)delegateQueue RLMMongoClient *client = [self.anonymousUser mongoClientWithServiceName:@"mongodb1"]; RLMMongoDatabase *database = [client databaseWithName:@"test_data"]; __block RLMMongoCollection *collection = [database collectionWithName:@"Dog"]; - NSArray *objectIds = [self insertDogDocuments:collection]; + NSArray *objectIds = [self prepareDogDocumentsIn:collection]; XCTestExpectation *expectation = [self expectationWithDescription:@"watch collection and receive change event 3 times"]; @@ -2693,7 +2700,7 @@ - (void)performWatchWithFilterIdsTest:(nullable dispatch_queue_t)delegateQueue { RLMMongoClient *client = [self.anonymousUser mongoClientWithServiceName:@"mongodb1"]; RLMMongoDatabase *database = [client databaseWithName:@"test_data"]; __block RLMMongoCollection *collection = [database collectionWithName:@"Dog"]; - NSArray *objectIds = [self insertDogDocuments:collection]; + NSArray *objectIds = [self prepareDogDocumentsIn:collection]; XCTestExpectation *expectation = [self expectationWithDescription:@"watch collection and receive change event 3 times"]; @@ -2741,7 +2748,7 @@ - (void)performMultipleWatchStreamsTest:(nullable dispatch_queue_t)delegateQueue RLMMongoClient *client = [self.anonymousUser mongoClientWithServiceName:@"mongodb1"]; RLMMongoDatabase *database = [client databaseWithName:@"test_data"]; __block RLMMongoCollection *collection = [database collectionWithName:@"Dog"]; - NSArray *objectIds = [self insertDogDocuments:collection]; + NSArray *objectIds = [self prepareDogDocumentsIn:collection]; XCTestExpectation *expectation = [self expectationWithDescription:@"watch collection and receive change event 3 times"]; expectation.expectedFulfillmentCount = 2; diff --git a/Realm/ObjectServerTests/RLMSyncTestCase.mm b/Realm/ObjectServerTests/RLMSyncTestCase.mm index e3ebfa6036..ae6355ab2c 100644 --- a/Realm/ObjectServerTests/RLMSyncTestCase.mm +++ b/Realm/ObjectServerTests/RLMSyncTestCase.mm @@ -132,7 +132,7 @@ - (RLMCredentials *)basicCredentialsWithName:(NSString *)name register:(BOOL)sho XCTAssertNil(error); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:4.0 handler:nil]; + [self waitForExpectationsWithTimeout:20.0 handler:nil]; } return [RLMCredentials credentialsWithEmail:name password:@"password"]; @@ -209,7 +209,7 @@ - (RLMRealm *)asyncOpenRealmWithConfiguration:(RLMRealmConfiguration *)config { r = realm; [ex fulfill]; }]; - [self waitForExpectationsWithTimeout:10.0 handler:nil]; + [self waitForExpectationsWithTimeout:30.0 handler:nil]; // Ensure that the block does not retain the Realm, as it may not be dealloced // immediately and so would extend the lifetime of the Realm an inconsistent amount auto realm = r; @@ -228,7 +228,7 @@ - (NSError *)asyncOpenErrorWithConfiguration:(RLMRealmConfiguration *)config { error = err; [ex fulfill]; }]; - [self waitForExpectationsWithTimeout:10.0 handler:nil]; + [self waitForExpectationsWithTimeout:30.0 handler:nil]; return error; } @@ -267,7 +267,7 @@ - (RLMUser *)logInUserForCredentials:(RLMCredentials *)credentials app:(RLMApp * user = u; [expectation fulfill]; }]; - [self waitForExpectations:@[expectation] timeout:4.0]; + [self waitForExpectations:@[expectation] timeout:20.0]; XCTAssertTrue(user.state == RLMUserStateLoggedIn, @"User should have been valid, but wasn't"); return user; } @@ -278,7 +278,7 @@ - (void)logOutUser:(RLMUser *)user { XCTAssertNil(error); [expectation fulfill]; }]; - [self waitForExpectations:@[expectation] timeout:4.0]; + [self waitForExpectations:@[expectation] timeout:20.0]; XCTAssertTrue(user.state == RLMUserStateLoggedOut, @"User should have been logged out, but wasn't"); } @@ -359,7 +359,7 @@ - (void)waitForDownloadsForUser:(RLMUser *)user XCTFail(@"Download waiter did not queue; session was invalid or errored out."); return; } - [self waitForExpectations:@[ex] timeout:20.0]; + [self waitForExpectations:@[ex] timeout:60.0]; if (error) { *error = theError; } @@ -378,7 +378,7 @@ - (void)waitForUploadsForRealm:(RLMRealm *)realm error:(NSError **)error { XCTFail(@"Upload waiter did not queue; session was invalid or errored out."); return; } - [self waitForExpectations:@[ex] timeout:20.0]; + [self waitForExpectations:@[ex] timeout:60.0]; if (error) *error = completionError; } @@ -396,7 +396,7 @@ - (void)waitForDownloadsForRealm:(RLMRealm *)realm error:(NSError **)error { XCTFail(@"Download waiter did not queue; session was invalid or errored out."); return; } - [self waitForExpectations:@[ex] timeout:20.0]; + [self waitForExpectations:@[ex] timeout:60.0]; if (error) { *error = completionError; } diff --git a/Realm/ObjectServerTests/RealmServer.swift b/Realm/ObjectServerTests/RealmServer.swift index 7cb60f3d3a..02590fd90a 100644 --- a/Realm/ObjectServerTests/RealmServer.swift +++ b/Realm/ObjectServerTests/RealmServer.swift @@ -293,7 +293,7 @@ class Admin { result = $0 group.leave() } - guard case .success = group.wait(timeout: .now() + 5) else { + guard case .success = group.wait(timeout: .now() + 30) else { return .failure(URLError(.badServerResponse)) } return result diff --git a/Realm/ObjectServerTests/SwiftMongoClientTests.swift b/Realm/ObjectServerTests/SwiftMongoClientTests.swift index 683c63ca09..ec8298e339 100644 --- a/Realm/ObjectServerTests/SwiftMongoClientTests.swift +++ b/Realm/ObjectServerTests/SwiftMongoClientTests.swift @@ -51,7 +51,7 @@ class SwiftMongoClientTests: SwiftSyncTestCase { } deleteEx.fulfill() } - wait(for: [deleteEx], timeout: 4.0) + wait(for: [deleteEx], timeout: 20.0) } func setupMongoCollection() -> MongoCollection { @@ -96,7 +96,7 @@ class SwiftMongoClientTests: SwiftSyncTestCase { } insertOneEx1.fulfill() } - wait(for: [insertOneEx1], timeout: 4.0) + wait(for: [insertOneEx1], timeout: 20.0) let insertManyEx1 = expectation(description: "Insert many documents") collection.insertMany([document, document2]) { result in @@ -108,7 +108,7 @@ class SwiftMongoClientTests: SwiftSyncTestCase { } insertManyEx1.fulfill() } - wait(for: [insertManyEx1], timeout: 4.0) + wait(for: [insertManyEx1], timeout: 20.0) let findEx1 = expectation(description: "Find documents") collection.find(filter: [:]) { result in @@ -123,7 +123,7 @@ class SwiftMongoClientTests: SwiftSyncTestCase { } findEx1.fulfill() } - wait(for: [findEx1], timeout: 4.0) + wait(for: [findEx1], timeout: 20.0) } func testMongoFindResultCompletion() { @@ -144,7 +144,7 @@ class SwiftMongoClientTests: SwiftSyncTestCase { } insertManyEx1.fulfill() } - wait(for: [insertManyEx1], timeout: 4.0) + wait(for: [insertManyEx1], timeout: 20.0) let findEx1 = expectation(description: "Find documents") collection.find(filter: [:]) { result in @@ -159,7 +159,7 @@ class SwiftMongoClientTests: SwiftSyncTestCase { } findEx1.fulfill() } - wait(for: [findEx1], timeout: 4.0) + wait(for: [findEx1], timeout: 20.0) let findEx2 = expectation(description: "Find documents") collection.find(filter: [:], options: findOptions) { result in @@ -172,7 +172,7 @@ class SwiftMongoClientTests: SwiftSyncTestCase { } findEx2.fulfill() } - wait(for: [findEx2], timeout: 4.0) + wait(for: [findEx2], timeout: 20.0) let findEx3 = expectation(description: "Find documents") collection.find(filter: document3, options: findOptions) { result in @@ -184,7 +184,7 @@ class SwiftMongoClientTests: SwiftSyncTestCase { } findEx3.fulfill() } - wait(for: [findEx3], timeout: 4.0) + wait(for: [findEx3], timeout: 20.0) let findOneEx1 = expectation(description: "Find one document") collection.findOneDocument(filter: document) { result in @@ -196,7 +196,7 @@ class SwiftMongoClientTests: SwiftSyncTestCase { } findOneEx1.fulfill() } - wait(for: [findOneEx1], timeout: 4.0) + wait(for: [findOneEx1], timeout: 20.0) let findOneEx2 = expectation(description: "Find one document") collection.findOneDocument(filter: document, options: findOptions) { result in @@ -208,7 +208,7 @@ class SwiftMongoClientTests: SwiftSyncTestCase { } findOneEx2.fulfill() } - wait(for: [findOneEx2], timeout: 4.0) + wait(for: [findOneEx2], timeout: 20.0) } func testMongoFindAndReplaceResultCompletion() { @@ -228,7 +228,7 @@ class SwiftMongoClientTests: SwiftSyncTestCase { } findOneReplaceEx1.fulfill() } - wait(for: [findOneReplaceEx1], timeout: 4.0) + wait(for: [findOneReplaceEx1], timeout: 20.0) let options1 = FindOneAndModifyOptions(["name": 1], ["_id": 1], true, true) let findOneReplaceEx2 = expectation(description: "Find one document and replace") @@ -241,7 +241,7 @@ class SwiftMongoClientTests: SwiftSyncTestCase { } findOneReplaceEx2.fulfill() } - wait(for: [findOneReplaceEx2], timeout: 4.0) + wait(for: [findOneReplaceEx2], timeout: 20.0) let options2 = FindOneAndModifyOptions(["name": 1], ["_id": 1], true, false) let findOneReplaceEx3 = expectation(description: "Find one document and replace") @@ -255,7 +255,7 @@ class SwiftMongoClientTests: SwiftSyncTestCase { } findOneReplaceEx3.fulfill() } - wait(for: [findOneReplaceEx3], timeout: 4.0) + wait(for: [findOneReplaceEx3], timeout: 20.0) } func testMongoFindAndUpdateResultCompletion() { @@ -275,7 +275,7 @@ class SwiftMongoClientTests: SwiftSyncTestCase { } findOneUpdateEx1.fulfill() } - wait(for: [findOneUpdateEx1], timeout: 4.0) + wait(for: [findOneUpdateEx1], timeout: 20.0) let options1 = FindOneAndModifyOptions(["name": 1], ["_id": 1], true, true) let findOneUpdateEx2 = expectation(description: "Find one document and update") @@ -289,7 +289,7 @@ class SwiftMongoClientTests: SwiftSyncTestCase { } findOneUpdateEx2.fulfill() } - wait(for: [findOneUpdateEx2], timeout: 4.0) + wait(for: [findOneUpdateEx2], timeout: 20.0) let options2 = FindOneAndModifyOptions(["name": 1], ["_id": 1], true, true) let findOneUpdateEx3 = expectation(description: "Find one document and update") @@ -303,7 +303,7 @@ class SwiftMongoClientTests: SwiftSyncTestCase { } findOneUpdateEx3.fulfill() } - wait(for: [findOneUpdateEx3], timeout: 4.0) + wait(for: [findOneUpdateEx3], timeout: 20.0) } func testMongoFindAndDeleteResultCompletion() { @@ -320,7 +320,7 @@ class SwiftMongoClientTests: SwiftSyncTestCase { } insertManyEx.fulfill() } - wait(for: [insertManyEx], timeout: 4.0) + wait(for: [insertManyEx], timeout: 20.0) let findOneDeleteEx1 = expectation(description: "Find one document and delete") collection.findOneAndDelete(filter: document) { result in @@ -333,7 +333,7 @@ class SwiftMongoClientTests: SwiftSyncTestCase { } findOneDeleteEx1.fulfill() } - wait(for: [findOneDeleteEx1], timeout: 4.0) + wait(for: [findOneDeleteEx1], timeout: 20.0) let options1 = FindOneAndModifyOptions(["name": 1], ["_id": 1], false, false) let findOneDeleteEx2 = expectation(description: "Find one document and delete") @@ -347,7 +347,7 @@ class SwiftMongoClientTests: SwiftSyncTestCase { XCTFail("Should find") } } - wait(for: [findOneDeleteEx2], timeout: 4.0) + wait(for: [findOneDeleteEx2], timeout: 20.0) let options2 = FindOneAndModifyOptions(["name": 1], ["_id": 1]) let findOneDeleteEx3 = expectation(description: "Find one document and delete") @@ -361,7 +361,7 @@ class SwiftMongoClientTests: SwiftSyncTestCase { XCTFail("Should find") } } - wait(for: [findOneDeleteEx3], timeout: 4.0) + wait(for: [findOneDeleteEx3], timeout: 20.0) let findEx = expectation(description: "Find documents") collection.find(filter: [:]) { result in @@ -373,7 +373,7 @@ class SwiftMongoClientTests: SwiftSyncTestCase { } findEx.fulfill() } - wait(for: [findEx], timeout: 4.0) + wait(for: [findEx], timeout: 20.0) } func testMongoUpdateOneResultCompletion() { @@ -394,7 +394,7 @@ class SwiftMongoClientTests: SwiftSyncTestCase { } insertManyEx.fulfill() } - wait(for: [insertManyEx], timeout: 4.0) + wait(for: [insertManyEx], timeout: 20.0) let updateEx1 = expectation(description: "Update one document") collection.updateOneDocument(filter: document, update: document2) { result in @@ -408,7 +408,7 @@ class SwiftMongoClientTests: SwiftSyncTestCase { } updateEx1.fulfill() } - wait(for: [updateEx1], timeout: 4.0) + wait(for: [updateEx1], timeout: 20.0) let updateEx2 = expectation(description: "Update one document") collection.updateOneDocument(filter: document5, update: document2, upsert: true) { result in @@ -422,7 +422,7 @@ class SwiftMongoClientTests: SwiftSyncTestCase { } updateEx2.fulfill() } - wait(for: [updateEx2], timeout: 4.0) + wait(for: [updateEx2], timeout: 20.0) } func testMongoUpdateManyResultCompletion() { @@ -443,7 +443,7 @@ class SwiftMongoClientTests: SwiftSyncTestCase { } insertManyEx.fulfill() } - wait(for: [insertManyEx], timeout: 4.0) + wait(for: [insertManyEx], timeout: 20.0) let updateEx1 = expectation(description: "Update one document") collection.updateManyDocuments(filter: document, update: document2) { result in @@ -457,7 +457,7 @@ class SwiftMongoClientTests: SwiftSyncTestCase { } updateEx1.fulfill() } - wait(for: [updateEx1], timeout: 4.0) + wait(for: [updateEx1], timeout: 20.0) let updateEx2 = expectation(description: "Update one document") collection.updateManyDocuments(filter: document5, update: document2, upsert: true) { result in @@ -471,7 +471,7 @@ class SwiftMongoClientTests: SwiftSyncTestCase { } updateEx2.fulfill() } - wait(for: [updateEx2], timeout: 4.0) + wait(for: [updateEx2], timeout: 20.0) } func testMongoDeleteOneResultCompletion() { @@ -489,7 +489,7 @@ class SwiftMongoClientTests: SwiftSyncTestCase { } deleteEx1.fulfill() } - wait(for: [deleteEx1], timeout: 4.0) + wait(for: [deleteEx1], timeout: 20.0) let insertManyEx = expectation(description: "Insert many documents") collection.insertMany([document, document2]) { result in @@ -501,7 +501,7 @@ class SwiftMongoClientTests: SwiftSyncTestCase { } insertManyEx.fulfill() } - wait(for: [insertManyEx], timeout: 4.0) + wait(for: [insertManyEx], timeout: 20.0) let deleteEx2 = expectation(description: "Delete one document") collection.deleteOneDocument(filter: document) { result in @@ -513,7 +513,7 @@ class SwiftMongoClientTests: SwiftSyncTestCase { } deleteEx2.fulfill() } - wait(for: [deleteEx2], timeout: 4.0) + wait(for: [deleteEx2], timeout: 20.0) } func testMongoDeleteManyResultCompletion() { @@ -531,7 +531,7 @@ class SwiftMongoClientTests: SwiftSyncTestCase { } deleteEx1.fulfill() } - wait(for: [deleteEx1], timeout: 4.0) + wait(for: [deleteEx1], timeout: 20.0) let insertManyEx = expectation(description: "Insert many documents") collection.insertMany([document, document2]) { result in @@ -543,7 +543,7 @@ class SwiftMongoClientTests: SwiftSyncTestCase { } insertManyEx.fulfill() } - wait(for: [insertManyEx], timeout: 4.0) + wait(for: [insertManyEx], timeout: 20.0) let deleteEx2 = expectation(description: "Delete one document") collection.deleteManyDocuments(filter: ["breed": "cane corso"]) { result in @@ -555,7 +555,7 @@ class SwiftMongoClientTests: SwiftSyncTestCase { } deleteEx2.fulfill() } - wait(for: [deleteEx2], timeout: 4.0) + wait(for: [deleteEx2], timeout: 20.0) } func testMongoCountAndAggregateResultCompletion() { @@ -572,7 +572,7 @@ class SwiftMongoClientTests: SwiftSyncTestCase { } insertManyEx1.fulfill() } - wait(for: [insertManyEx1], timeout: 4.0) + wait(for: [insertManyEx1], timeout: 20.0) collection.aggregate(pipeline: [["$match": ["name": "fido"]], ["$group": ["_id": "$name"]]]) { result in switch result { @@ -593,7 +593,7 @@ class SwiftMongoClientTests: SwiftSyncTestCase { } countEx1.fulfill() } - wait(for: [countEx1], timeout: 4.0) + wait(for: [countEx1], timeout: 20.0) let countEx2 = expectation(description: "Count documents") collection.count(filter: document, limit: 1) { result in @@ -605,7 +605,7 @@ class SwiftMongoClientTests: SwiftSyncTestCase { } countEx2.fulfill() } - wait(for: [countEx2], timeout: 4.0) + wait(for: [countEx2], timeout: 20.0) } func testWatch() { @@ -673,7 +673,7 @@ class SwiftMongoClientTests: SwiftSyncTestCase { } insertManyEx.fulfill() } - wait(for: [insertManyEx], timeout: 4.0) + wait(for: [insertManyEx], timeout: 20.0) var watchEx = expectation(description: "Watch 3 document events") let watchTestUtility = WatchTestUtility(targetEventCount: 3, matchingObjectId: objectIds.first!, expectation: &watchEx) @@ -739,7 +739,7 @@ class SwiftMongoClientTests: SwiftSyncTestCase { } insertManyEx.fulfill() } - wait(for: [insertManyEx], timeout: 4.0) + wait(for: [insertManyEx], timeout: 20.0) var watchEx = expectation(description: "Watch 3 document events") let watchTestUtility = WatchTestUtility(targetEventCount: 3, @@ -803,7 +803,7 @@ class SwiftMongoClientTests: SwiftSyncTestCase { } insertManyEx.fulfill() } - wait(for: [insertManyEx], timeout: 4.0) + wait(for: [insertManyEx], timeout: 20.0) var watchEx = expectation(description: "Watch 5 document events") watchEx.expectedFulfillmentCount = 2 diff --git a/Realm/ObjectServerTests/SwiftObjectServerTests.swift b/Realm/ObjectServerTests/SwiftObjectServerTests.swift index c52c1c5dec..f8878e5c84 100644 --- a/Realm/ObjectServerTests/SwiftObjectServerTests.swift +++ b/Realm/ObjectServerTests/SwiftObjectServerTests.swift @@ -354,7 +354,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase { waitForSyncDisabled(appServerId: appServerId, syncServiceId: syncServiceId) - try block() + try autoreleasepool(invoking: block) waitForSyncEnabled(appServerId: appServerId, syncServiceId: syncServiceId, syncServiceConfig: syncServiceConfig) try waitForDevModeEnabled(appServerId: appServerId, syncServiceId: syncServiceId, syncServiceConfig: syncServiceConfig) @@ -436,16 +436,15 @@ class SwiftObjectServerTests: SwiftSyncTestCase { // Sync is disabled, block executed, sync re-enabled try executeBlockOffline { - try autoreleasepool { - var configuration = user.configuration(partitionValue: partition) - configuration.objectTypes = [SwiftPerson.self] - let realm = try Realm(configuration: configuration) - // Add an object to the local realm that will not be in the server realm (because sync is disabled). - try realm.write { - realm.add(SwiftPerson(firstName: "John", lastName: "L")) - } - XCTAssertEqual(realm.objects(SwiftPerson.self).count, 1) + var configuration = user.configuration(partitionValue: partition) + configuration.objectTypes = [SwiftPerson.self] + let realm = try Realm(configuration: configuration) + realm.syncSession!.suspend() + // Add an object to the local realm that will not be in the server realm (because sync is disabled). + try realm.write { + realm.add(SwiftPerson(firstName: "John", lastName: "L")) } + XCTAssertEqual(realm.objects(SwiftPerson.self).count, 1) } // After restarting sync, the sync history translator service needs time @@ -498,7 +497,8 @@ class SwiftObjectServerTests: SwiftSyncTestCase { afterCallbackEx.fulfill() } - var configuration = user.configuration(partitionValue: #function, clientResetMode: .discardLocal(beforeClientResetBlock, afterClientResetBlock)) + var configuration = user.configuration(partitionValue: #function, + clientResetMode: .discardLocal(beforeClientResetBlock, afterClientResetBlock)) configuration.objectTypes = [SwiftPerson.self] guard let syncConfig = configuration.syncConfiguration else { fatalError("Test condition failure. SyncConfiguration not set.") } @@ -805,7 +805,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase { } ex.fulfill() } - waitForExpectations(timeout: 4.0, handler: nil) + waitForExpectations(timeout: 20.0, handler: nil) } proxy.stop() @@ -928,7 +928,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase { XCTAssertNil(error) registerUserEx.fulfill() } - wait(for: [registerUserEx], timeout: 4.0) + wait(for: [registerUserEx], timeout: 20.0) let loginEx = expectation(description: "Login user") var syncUser: User? @@ -943,7 +943,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase { loginEx.fulfill() } - wait(for: [loginEx], timeout: 4.0) + wait(for: [loginEx], timeout: 20.0) XCTAssertEqual(syncUser?.id, app.currentUser?.id) XCTAssertEqual(app.allUsers.count, 1) @@ -968,7 +968,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase { registerUser2Ex.fulfill() } - wait(for: [registerUser1Ex, registerUser2Ex], timeout: 4.0) + wait(for: [registerUser1Ex, registerUser2Ex], timeout: 20.0) let login1Ex = expectation(description: "Login user 1") let login2Ex = expectation(description: "Login user 2") @@ -985,7 +985,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase { login1Ex.fulfill() } - wait(for: [login1Ex], timeout: 4.0) + wait(for: [login1Ex], timeout: 20.0) app.login(credentials: Credentials.emailPassword(email: email2, password: password2)) { result in if case .success(let user) = result { @@ -996,7 +996,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase { login2Ex.fulfill() } - wait(for: [login2Ex], timeout: 4.0) + wait(for: [login2Ex], timeout: 20.0) XCTAssertEqual(app.allUsers.count, 2) @@ -1012,7 +1012,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase { removeEx.fulfill() } - wait(for: [removeEx], timeout: 4.0) + wait(for: [removeEx], timeout: 20.0) XCTAssertEqual(syncUser2!.id, app.currentUser!.id) XCTAssertEqual(app.allUsers.count, 1) @@ -1031,7 +1031,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase { XCTFail("Should login user") } } - wait(for: [loginEx], timeout: 4.0) + wait(for: [loginEx], timeout: 20.0) let user = app.currentUser! @@ -1054,7 +1054,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase { } deleteUserEx.fulfill() } - wait(for: [deleteUserEx], timeout: 4.0) + wait(for: [deleteUserEx], timeout: 20.0) // Try to open a Realm with the user; this will cause our errorHandler block defined above to be fired. XCTAssertFalse(blockCalled) @@ -1078,7 +1078,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase { } serverEx.fulfill() } - wait(for: [serverEx], timeout: 4.0) + wait(for: [serverEx], timeout: 20.0) return userExists } @@ -1091,7 +1091,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase { XCTAssertNil(error) registerUserEx.fulfill() } - wait(for: [registerUserEx], timeout: 4.0) + wait(for: [registerUserEx], timeout: 20.0) let loginEx = expectation(description: "Login user") var syncUser: User? @@ -1106,7 +1106,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase { loginEx.fulfill() } - wait(for: [loginEx], timeout: 4.0) + wait(for: [loginEx], timeout: 20.0) XCTAssertTrue(userExistsOnServer(syncUser!)) XCTAssertEqual(syncUser?.id, app.currentUser?.id) @@ -1121,7 +1121,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase { deleteEx.fulfill() } - wait(for: [deleteEx], timeout: 4.0) + wait(for: [deleteEx], timeout: 20.0) XCTAssertFalse(userExistsOnServer(syncUser!)) XCTAssertNil(app.currentUser) @@ -1138,7 +1138,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase { XCTAssertNil(error) registerUserEx.fulfill() } - wait(for: [registerUserEx], timeout: 4.0) + wait(for: [registerUserEx], timeout: 20.0) let loginEx = expectation(description: "Login user") var syncUser: User! @@ -1153,7 +1153,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase { } loginEx.fulfill() } - wait(for: [loginEx], timeout: 4.0) + wait(for: [loginEx], timeout: 20.0) let linkEx = expectation(description: "Link user") syncUser.linkUser(credentials: credentials) { result in @@ -1166,7 +1166,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase { linkEx.fulfill() } - wait(for: [linkEx], timeout: 4.0) + wait(for: [linkEx], timeout: 20.0) XCTAssertEqual(syncUser?.id, app.currentUser?.id) XCTAssertEqual(syncUser?.identities.count, 2) @@ -1184,7 +1184,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase { XCTAssertNil(error) registerUserEx.fulfill() } - wait(for: [registerUserEx], timeout: 4.0) + wait(for: [registerUserEx], timeout: 20.0) let confirmUserEx = expectation(description: "Confirm user") @@ -1192,7 +1192,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase { XCTAssertNotNil(error) confirmUserEx.fulfill() } - wait(for: [confirmUserEx], timeout: 4.0) + wait(for: [confirmUserEx], timeout: 20.0) let resendEmailEx = expectation(description: "Resend email confirmation") @@ -1200,7 +1200,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase { XCTAssertNotNil(error) resendEmailEx.fulfill() } - wait(for: [resendEmailEx], timeout: 4.0) + wait(for: [resendEmailEx], timeout: 20.0) let retryCustomEx = expectation(description: "Retry custom confirmation") @@ -1208,7 +1208,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase { XCTAssertNotNil(error) retryCustomEx.fulfill() } - wait(for: [retryCustomEx], timeout: 4.0) + wait(for: [retryCustomEx], timeout: 20.0) let resendResetPasswordEx = expectation(description: "Resend reset password email") @@ -1216,7 +1216,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase { XCTAssertNotNil(error) resendResetPasswordEx.fulfill() } - wait(for: [resendResetPasswordEx], timeout: 4.0) + wait(for: [resendResetPasswordEx], timeout: 20.0) let resetPasswordEx = expectation(description: "Reset password email") @@ -1224,7 +1224,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase { XCTAssertNotNil(error) resetPasswordEx.fulfill() } - wait(for: [resetPasswordEx], timeout: 4.0) + wait(for: [resetPasswordEx], timeout: 20.0) let callResetFunctionEx = expectation(description: "Reset password function") app.emailPasswordAuth.callResetPasswordFunction(email: email, @@ -1233,7 +1233,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase { XCTAssertNotNil(error) callResetFunctionEx.fulfill() } - wait(for: [callResetFunctionEx], timeout: 4.0) + wait(for: [callResetFunctionEx], timeout: 20.0) } func testUserAPIKeyProviderClient() { @@ -1246,7 +1246,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase { XCTAssertNil(error) registerUserEx.fulfill() } - wait(for: [registerUserEx], timeout: 4.0) + wait(for: [registerUserEx], timeout: 20.0) let loginEx = expectation(description: "Login user") let credentials = Credentials.emailPassword(email: email, password: password) @@ -1262,7 +1262,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase { loginEx.fulfill() } - wait(for: [loginEx], timeout: 4.0) + wait(for: [loginEx], timeout: 20.0) let createAPIKeyEx = expectation(description: "Create user api key") @@ -1273,7 +1273,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase { apiKey = key createAPIKeyEx.fulfill() } - wait(for: [createAPIKeyEx], timeout: 4.0) + wait(for: [createAPIKeyEx], timeout: 20.0) let fetchAPIKeyEx = expectation(description: "Fetch user api key") syncUser?.apiKeysAuth.fetchAPIKey(apiKey!.objectId) { (key, error) in @@ -1281,7 +1281,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase { XCTAssertNil(error) fetchAPIKeyEx.fulfill() } - wait(for: [fetchAPIKeyEx], timeout: 4.0) + wait(for: [fetchAPIKeyEx], timeout: 20.0) let fetchAPIKeysEx = expectation(description: "Fetch user api keys") syncUser?.apiKeysAuth.fetchAPIKeys(completion: { (keys, error) in @@ -1290,28 +1290,28 @@ class SwiftObjectServerTests: SwiftSyncTestCase { XCTAssertNil(error) fetchAPIKeysEx.fulfill() }) - wait(for: [fetchAPIKeysEx], timeout: 4.0) + wait(for: [fetchAPIKeysEx], timeout: 20.0) let disableKeyEx = expectation(description: "Disable API key") syncUser?.apiKeysAuth.disableAPIKey(apiKey!.objectId) { (error) in XCTAssertNil(error) disableKeyEx.fulfill() } - wait(for: [disableKeyEx], timeout: 4.0) + wait(for: [disableKeyEx], timeout: 20.0) let enableKeyEx = expectation(description: "Enable API key") syncUser?.apiKeysAuth.enableAPIKey(apiKey!.objectId) { (error) in XCTAssertNil(error) enableKeyEx.fulfill() } - wait(for: [enableKeyEx], timeout: 4.0) + wait(for: [enableKeyEx], timeout: 20.0) let deleteKeyEx = expectation(description: "Delete API key") syncUser?.apiKeysAuth.deleteAPIKey(apiKey!.objectId) { (error) in XCTAssertNil(error) deleteKeyEx.fulfill() } - wait(for: [deleteKeyEx], timeout: 4.0) + wait(for: [deleteKeyEx], timeout: 20.0) } func testApiKeyAuthResultCompletion() { @@ -1323,7 +1323,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase { XCTAssertNil(error) registerUserEx.fulfill() } - wait(for: [registerUserEx], timeout: 4.0) + wait(for: [registerUserEx], timeout: 20.0) let loginEx = expectation(description: "Login user") let credentials = Credentials.emailPassword(email: email, password: password) @@ -1337,7 +1337,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase { } loginEx.fulfill() } - wait(for: [loginEx], timeout: 4.0) + wait(for: [loginEx], timeout: 20.0) let createAPIKeyEx = expectation(description: "Create user api key") var apiKey: UserAPIKey? @@ -1350,7 +1350,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase { } createAPIKeyEx.fulfill() } - wait(for: [createAPIKeyEx], timeout: 4.0) + wait(for: [createAPIKeyEx], timeout: 20.0) let fetchAPIKeyEx = expectation(description: "Fetch user api key") syncUser?.apiKeysAuth.fetchAPIKey(apiKey!.objectId as! ObjectId, { result in @@ -1359,7 +1359,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase { } fetchAPIKeyEx.fulfill() }) - wait(for: [fetchAPIKeyEx], timeout: 4.0) + wait(for: [fetchAPIKeyEx], timeout: 20.0) let fetchAPIKeysEx = expectation(description: "Fetch user api keys") syncUser?.apiKeysAuth.fetchAPIKeys { result in @@ -1371,7 +1371,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase { } fetchAPIKeysEx.fulfill() } - wait(for: [fetchAPIKeysEx], timeout: 4.0) + wait(for: [fetchAPIKeysEx], timeout: 20.0) } func testCallFunction() { @@ -1384,7 +1384,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase { XCTAssertNil(error) registerUserEx.fulfill() } - wait(for: [registerUserEx], timeout: 4.0) + wait(for: [registerUserEx], timeout: 20.0) let loginEx = expectation(description: "Login user") @@ -1398,7 +1398,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase { } loginEx.fulfill() } - wait(for: [loginEx], timeout: 4.0) + wait(for: [loginEx], timeout: 20.0) let callFunctionEx = expectation(description: "Call function") app.currentUser?.functions.sum([1, 2, 3, 4, 5]) { bson, error in @@ -1416,7 +1416,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase { XCTAssertEqual(sum, 15) callFunctionEx.fulfill() } - wait(for: [callFunctionEx], timeout: 4.0) + wait(for: [callFunctionEx], timeout: 20.0) // Test function with completion handler (Result) -> Void let callFunctionResultEx = expectation(description: "Call function") @@ -1433,7 +1433,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase { } callFunctionResultEx.fulfill() } - wait(for: [callFunctionResultEx], timeout: 4.0) + wait(for: [callFunctionResultEx], timeout: 20.0) } func testPushRegistration() { @@ -1446,7 +1446,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase { XCTAssertNil(error) registerUserEx.fulfill() } - wait(for: [registerUserEx], timeout: 4.0) + wait(for: [registerUserEx], timeout: 20.0) let loginExpectation = expectation(description: "Login user") @@ -1457,7 +1457,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase { } loginExpectation.fulfill() } - wait(for: [loginExpectation], timeout: 4.0) + wait(for: [loginExpectation], timeout: 20.0) let registerDeviceExpectation = expectation(description: "Register Device") let client = app.pushClient(serviceName: "gcm") @@ -1465,14 +1465,14 @@ class SwiftObjectServerTests: SwiftSyncTestCase { XCTAssertNil(error) registerDeviceExpectation.fulfill() } - wait(for: [registerDeviceExpectation], timeout: 4.0) + wait(for: [registerDeviceExpectation], timeout: 20.0) let dergisterDeviceExpectation = expectation(description: "Deregister Device") client.deregisterDevice(user: app.currentUser!, completion: { error in XCTAssertNil(error) dergisterDeviceExpectation.fulfill() }) - wait(for: [dergisterDeviceExpectation], timeout: 4.0) + wait(for: [dergisterDeviceExpectation], timeout: 20.0) } func testCustomUserData() { @@ -1485,7 +1485,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase { XCTAssertNil(error) registerUserEx.fulfill() } - wait(for: [registerUserEx], timeout: 4.0) + wait(for: [registerUserEx], timeout: 20.0) let loginEx = expectation(description: "Login user") let credentials = Credentials.emailPassword(email: email, password: password) @@ -1498,14 +1498,14 @@ class SwiftObjectServerTests: SwiftSyncTestCase { } loginEx.fulfill() } - wait(for: [loginEx], timeout: 4.0) + wait(for: [loginEx], timeout: 20.0) let userDataEx = expectation(description: "Update user data") app.currentUser?.functions.updateUserData([["favourite_colour": "green", "apples": 10]]) { _, error in XCTAssertNil(error) userDataEx.fulfill() } - wait(for: [userDataEx], timeout: 4.0) + wait(for: [userDataEx], timeout: 20.0) let refreshDataEx = expectation(description: "Refresh user data") app.currentUser?.refreshCustomData { customData, error in @@ -1515,7 +1515,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase { XCTAssertEqual(customData?["favourite_colour"] as! String, "green") refreshDataEx.fulfill() } - wait(for: [refreshDataEx], timeout: 4.0) + wait(for: [refreshDataEx], timeout: 20.0) XCTAssertEqual(app.currentUser?.customData["favourite_colour"], .string("green")) XCTAssertEqual(app.currentUser?.customData["apples"], .int64(10)) @@ -1933,7 +1933,7 @@ extension Publisher { }) } - func await(_ testCase: XCTestCase, timeout: TimeInterval = 4.0, receiveValue: ((Self.Output) -> Void)? = nil) { + func await(_ testCase: XCTestCase, timeout: TimeInterval = 20.0, receiveValue: ((Self.Output) -> Void)? = nil) { let expectation = testCase.expectation(description: "Async combine pipeline") let cancellable = self.expectValue(testCase, expectation, receiveValue: receiveValue) testCase.wait(for: [expectation], timeout: timeout) @@ -1941,7 +1941,7 @@ extension Publisher { } @discardableResult - func await(_ testCase: XCTestCase, timeout: TimeInterval = 4.0) -> Self.Output { + func await(_ testCase: XCTestCase, timeout: TimeInterval = 20.0) -> Self.Output { let expectation = testCase.expectation(description: "Async combine pipeline") var value: Self.Output? let cancellable = self.expectValue(testCase, expectation, receiveValue: { value = $0 }) @@ -1950,7 +1950,7 @@ extension Publisher { return value! } - func awaitFailure(_ testCase: XCTestCase, timeout: TimeInterval = 4.0, + func awaitFailure(_ testCase: XCTestCase, timeout: TimeInterval = 20.0, _ errorHandler: ((Self.Failure) -> Void)? = nil) { let expectation = testCase.expectation(description: "Async combine pipeline should fail") let cancellable = self.sink(receiveCompletion: { result in @@ -2077,7 +2077,7 @@ class CombineObjectServerTests: SwiftSyncTestCase { } insertManyEx.fulfill() } - wait(for: [insertManyEx], timeout: 4.0) + wait(for: [insertManyEx], timeout: 20.0) let watchEx1 = expectation(description: "Watch 3 document events") watchEx1.expectedFulfillmentCount = 3 @@ -2172,7 +2172,7 @@ class CombineObjectServerTests: SwiftSyncTestCase { } insertManyEx.fulfill() } - wait(for: [insertManyEx], timeout: 4.0) + wait(for: [insertManyEx], timeout: 20.0) let watchEx1 = expectation(description: "Watch 3 document events") watchEx1.expectedFulfillmentCount = 3 @@ -2365,7 +2365,7 @@ class CombineObjectServerTests: SwiftSyncTestCase { XCTAssertNil(error) userDataEx.fulfill() } - wait(for: [userDataEx], timeout: 4.0) + wait(for: [userDataEx], timeout: 20.0) app.currentUser?.refreshCustomData() .await(self) { customData in diff --git a/Realm/ObjectServerTests/config_overrides.json b/Realm/ObjectServerTests/config_overrides.json index 3ed284c93b..1aa586547a 100644 --- a/Realm/ObjectServerTests/config_overrides.json +++ b/Realm/ObjectServerTests/config_overrides.json @@ -1,13 +1,28 @@ { "events": { - "streams": { - "eventSubscriptionPollingPeriodSec": 2, - "eventSubscriptionHeartbeatPeriodSec": 2 + "database": { + "maxCoordinatorChangeStreams": 50000 + }, + "log_forwarder": { + "enabled": false }, "mediator": { - "eventSubscriptionPollingPeriodSeconds": 4, - "unownedEventSubscriptionPollingPeriodSeconds": 2, - "staleOwnedJobPollingPeriodSec": 2 + "eventSubscriptionPollingPeriodSeconds": 2, + "unownedEventSubscriptionPollingPeriodSeconds": 1, + "staleOwnedJobPollingPeriodSec": 1 + }, + "streams": { + "eventSubscriptionPollingPeriodSec": 2, + "eventSubscriptionHeartbeatPeriodSec": 1 } + }, + "graphql": { + "enabled": false + }, + "metrics": { + "enabled": false + }, + "secretsManager": { + "enabled": false } } diff --git a/Realm/ObjectServerTests/setup_baas.rb b/Realm/ObjectServerTests/setup_baas.rb index a4aedc8b3c..4b873bb4a2 100644 --- a/Realm/ObjectServerTests/setup_baas.rb +++ b/Realm/ObjectServerTests/setup_baas.rb @@ -19,11 +19,11 @@ MONGODB_VERSION='5.0.6' GO_VERSION='1.17.8' -NODE_VERSION='13.14.0' +NODE_VERSION='16.13.1' STITCH_VERSION=DEPENDENCIES["STITCH_VERSION"] MONGODB_URL="https://fastdl.mongodb.org/osx/mongodb-macos-x86_64-#{MONGODB_VERSION}.tgz" -TRANSPILER_TARGET='node13-macos' +TRANSPILER_TARGET='node16-macos' SERVER_STITCH_LIB_URL="https://s3.amazonaws.com/stitch-artifacts/stitch-support/stitch-support-macos-debug-4.3.2-721-ge791a2e-patch-5e2a6ad2a4cf473ae2e67b09.tgz" MONGO_DIR="#{BUILD_DIR}/mongodb-macos-x86_64-#{MONGODB_VERSION}" @@ -141,6 +141,9 @@ def setup_stitch end puts 'building transpiler' + # Install a newer version of pkg that actually supports recent versions of node + puts `#{exports.length() == 0 ? "" : exports.join(' && ') + ' &&'} \ + cd '#{stitch_dir}/etc/transpiler' && yarn add pkg` puts `#{exports.length() == 0 ? "" : exports.join(' && ') + ' &&'} \ cd '#{stitch_dir}/etc/transpiler' && yarn install && yarn run build -t "#{TRANSPILER_TARGET}" && cp -c bin/transpiler #{BUILD_DIR}/bin` diff --git a/Realm/RLMApp.h b/Realm/RLMApp.h index 825cac68eb..34a3d57156 100644 --- a/Realm/RLMApp.h +++ b/Realm/RLMApp.h @@ -76,7 +76,7 @@ Create a new Realm App configuration. */ - (instancetype)initWithBaseURL:(nullable NSString *) baseURL transport:(nullable id)transport - localAppName:(nullable NSString *) localAppName + localAppName:(nullable NSString *)localAppName localAppVersion:(nullable NSString *)localAppVersion defaultRequestTimeoutMS:(NSUInteger)defaultRequestTimeoutMS; diff --git a/Realm/RLMApp.mm b/Realm/RLMApp.mm index 30a3d65be1..ee32444a2f 100644 --- a/Realm/RLMApp.mm +++ b/Realm/RLMApp.mm @@ -98,6 +98,13 @@ - (instancetype)initWithConfig:(const realm::app::App::Config &)config { return nil; } +- (instancetype)init { + return [self initWithBaseURL:nil + transport:nil + localAppName:nil + localAppVersion:nil]; +} + - (instancetype)initWithBaseURL:(nullable NSString *)baseURL transport:(nullable id)transport localAppName:(nullable NSString *)localAppName @@ -111,7 +118,7 @@ - (instancetype)initWithBaseURL:(nullable NSString *)baseURL - (instancetype)initWithBaseURL:(nullable NSString *)baseURL transport:(nullable id)transport - localAppName:(NSString *)localAppName + localAppName:(nullable NSString *)localAppName localAppVersion:(nullable NSString *)localAppVersion defaultRequestTimeoutMS:(NSUInteger)defaultRequestTimeoutMS { if (self = [super init]) { diff --git a/Realm/RLMRealmConfiguration.mm b/Realm/RLMRealmConfiguration.mm index a7d5bd23cf..1111511159 100644 --- a/Realm/RLMRealmConfiguration.mm +++ b/Realm/RLMRealmConfiguration.mm @@ -179,7 +179,9 @@ - (void)setInMemoryIdentifier:(NSString *)inMemoryIdentifier { - (void)setSeedFilePath:(NSURL *)seedFilePath { _seedFilePath = seedFilePath; - _config.in_memory = false; + if (_seedFilePath) { + _config.in_memory = false; + } } - (NSData *)encryptionKey { @@ -356,15 +358,8 @@ - (void)setSyncConfiguration:(RLMSyncConfiguration *)syncConfiguration { NSAssert(user.identifier, @"Cannot call this method on a user that doesn't have an identifier."); _config.in_memory = false; - _config.sync_config = std::make_shared([syncConfiguration rawConfiguration]); - - if (syncConfiguration.customFileURL) { - _config.path = syncConfiguration.customFileURL.path.UTF8String; - } else if (_config.sync_config->flx_sync_requested) { - _config.path = [user pathForFlexibleSync]; - } else { - _config.path = [user pathForPartitionValue:_config.sync_config->partition_value]; - } + _config.sync_config = std::make_shared(syncConfiguration.rawConfiguration); + _config.path = syncConfiguration.path; [self updateSchemaMode]; } @@ -373,7 +368,7 @@ - (RLMSyncConfiguration *)syncConfiguration { if (!_config.sync_config) { return nil; } - return [[RLMSyncConfiguration alloc] initWithRawConfig:*_config.sync_config]; + return [[RLMSyncConfiguration alloc] initWithRawConfig:*_config.sync_config path:_config.path]; } #else // REALM_ENABLE_SYNC diff --git a/Realm/RLMSyncConfiguration.h b/Realm/RLMSyncConfiguration.h index a31330d5db..86d34e3c5e 100644 --- a/Realm/RLMSyncConfiguration.h +++ b/Realm/RLMSyncConfiguration.h @@ -112,16 +112,6 @@ typedef void(^RLMClientResetAfterBlock)(RLMRealm * _Nonnull beforeFrozen, RLMRea */ @property (nonatomic) bool cancelAsyncOpenOnNonFatalErrors; -/// :nodoc: -- (instancetype)initWithUser:(RLMUser *)user - partitionValue:(nullable id)partitionValue __attribute__((unavailable("Use [RLMUser configurationWithPartitionValue:] instead"))); - -/// :nodoc: -+ (RLMRealmConfiguration *)automaticConfiguration __attribute__((unavailable("Use [RLMUser configuration] instead"))); - -/// :nodoc: -+ (RLMRealmConfiguration *)automaticConfigurationForUser:(RLMUser *)user __attribute__((unavailable("Use [RLMUser configuration] instead"))); - /// :nodoc: - (instancetype)init __attribute__((unavailable("This type cannot be created directly"))); diff --git a/Realm/RLMSyncConfiguration.mm b/Realm/RLMSyncConfiguration.mm index 28f67e92ce..b864e6ec0f 100644 --- a/Realm/RLMSyncConfiguration.mm +++ b/Realm/RLMSyncConfiguration.mm @@ -113,9 +113,10 @@ @implementation RLMSyncConfiguration @dynamic stopPolicy; -- (instancetype)initWithRawConfig:(realm::SyncConfig)config { +- (instancetype)initWithRawConfig:(realm::SyncConfig)config path:(std::string const&)path { if (self = [super init]) { _config = std::make_unique(std::move(config)); + _path = path; } return self; } @@ -126,8 +127,8 @@ - (BOOL)isEqual:(id)object { } RLMSyncConfiguration *that = (RLMSyncConfiguration *)object; return [self.partitionValue isEqual:that.partitionValue] - && [self.user isEqual:that.user] - && self.stopPolicy == that.stopPolicy; + && [self.user isEqual:that.user] + && self.stopPolicy == that.stopPolicy; } - (realm::SyncConfig&)rawConfiguration { @@ -151,6 +152,10 @@ - (RLMClientResetMode)clientResetMode { return RLMClientResetMode(_config->client_resync_mode); } +- (void)setClientResetMode:(RLMClientResetMode)clientResetMode { + _config->client_resync_mode = realm::ClientResyncMode(clientResetMode); +} + - (RLMClientResetBeforeBlock)beforeClientReset { if (_config->notify_before_client_reset) { auto wrapper = _config->notify_before_client_reset.target(); @@ -223,79 +228,6 @@ - (BOOL)enableFlexibleSync { return _config->flx_sync_requested; } -- (instancetype)initWithUser:(RLMUser *)user - partitionValue:(nullable id)partitionValue { - return [self initWithUser:user - partitionValue:partitionValue - customFileURL:nil - stopPolicy:RLMSyncStopPolicyAfterChangesUploaded - enableFlexibleSync:false - clientResetMode:RLMClientResetModeManual - notifyBeforeReset:nil - notifyAfterReset:nil]; -} - -- (instancetype)initWithUser:(RLMUser *)user - partitionValue:(nullable id)partitionValue - stopPolicy:(RLMSyncStopPolicy)stopPolicy - clientResetMode:(RLMClientResetMode)clientResetMode - notifyBeforeReset:(nullable RLMClientResetBeforeBlock)beforeResetBlock - notifyAfterReset:(nullable RLMClientResetAfterBlock)afterResetBlock { - auto config = [self initWithUser:user - partitionValue:partitionValue - customFileURL:nil - stopPolicy:stopPolicy - enableFlexibleSync:false - clientResetMode:clientResetMode - notifyBeforeReset:beforeResetBlock - notifyAfterReset:afterResetBlock]; - return config; -} - -- (instancetype)initWithUser:(RLMUser *)user - stopPolicy:(RLMSyncStopPolicy)stopPolicy - enableFlexibleSync:(BOOL)enableFlexibleSync { - auto config = [self initWithUser:user - partitionValue:nil - customFileURL:nil - stopPolicy:stopPolicy - enableFlexibleSync:enableFlexibleSync - clientResetMode:RLMClientResetModeManual - notifyBeforeReset:nil - notifyAfterReset:nil]; - return config; -} - - - -- (instancetype)initWithUser:(RLMUser *)user - partitionValue:(nullable id)partitionValue - clientResetMode:(RLMClientResetMode)clientResetMode { - auto config = [self initWithUser:user - partitionValue:partitionValue - customFileURL:nil - stopPolicy:RLMSyncStopPolicyAfterChangesUploaded - enableFlexibleSync:false - clientResetMode:clientResetMode - notifyBeforeReset:nil - notifyAfterReset:nil]; - return config; -} - -- (instancetype)initWithUser:(RLMUser *)user - enableFlexibleSync:(BOOL)enableFlexibleSync - clientResetMode:(RLMClientResetMode)clientResetMode { - auto config = [self initWithUser:user - partitionValue:nil - customFileURL:nil - stopPolicy:RLMSyncStopPolicyAfterChangesUploaded - enableFlexibleSync:enableFlexibleSync - clientResetMode:clientResetMode - notifyBeforeReset:nil - notifyAfterReset:nil]; - return config; -} - NSError *RLMTranslateSyncError(SyncError error) { NSString *recoveryPath; RLMSyncErrorActionToken *token; @@ -338,61 +270,59 @@ - (instancetype)initWithUser:(RLMUser *)user return make_sync_error(errorClass, @(error.message.c_str()), error.error_code.value(), custom); } -- (instancetype)initWithUser:(RLMUser *)user - partitionValue:(nullable id)partitionValue - customFileURL:(nullable NSURL *)customFileURL - stopPolicy:(RLMSyncStopPolicy)stopPolicy - enableFlexibleSync:(BOOL)enableFlexibleSync - clientResetMode:(RLMClientResetMode)clientResetMode - notifyBeforeReset:(RLMClientResetBeforeBlock)beforeResetBlock - notifyAfterReset:(RLMClientResetAfterBlock)afterResetBlock { - if (self = [super init]) { - if (enableFlexibleSync) { - _config = std::make_unique([user _syncUser], SyncConfig::FLXSyncEnabled{}); - } else { - std::stringstream s; - s << RLMConvertRLMBSONToBson(partitionValue); - _config = std::make_unique([user _syncUser], - s.str()); +static void setDefaults(SyncConfig& config, RLMUser *user) { + config.client_resync_mode = ClientResyncMode::Manual; + config.stop_policy = SyncSessionStopPolicy::AfterChangesUploaded; + + RLMSyncManager *manager = [user.app syncManager]; + __weak RLMSyncManager *weakManager = manager; + config.error_handler = [weakManager](std::shared_ptr errored_session, SyncError error) { + RLMSyncErrorReportingBlock errorHandler; + @autoreleasepool { + errorHandler = weakManager.errorHandler; } - _config->stop_policy = translateStopPolicy(stopPolicy); - RLMSyncManager *manager = [user.app syncManager]; - __weak RLMSyncManager *weakManager = manager; - _config->error_handler = [weakManager](std::shared_ptr errored_session, SyncError error) { - RLMSyncErrorReportingBlock errorHandler; - @autoreleasepool { - errorHandler = weakManager.errorHandler; - } - if (!errorHandler) { - return; - } - NSError *nsError = RLMTranslateSyncError(std::move(error)); - if (!nsError) { - return; - } - RLMSyncSession *session = [[RLMSyncSession alloc] initWithSyncSession:errored_session]; - dispatch_async(dispatch_get_main_queue(), ^{ - errorHandler(nsError, session); - }); - }; - // Default to manual mode - _config->client_resync_mode = realm::ClientResyncMode(clientResetMode); - self.beforeClientReset = beforeResetBlock; - self.afterClientReset = afterResetBlock; - - if (NSString *authorizationHeaderName = manager.authorizationHeaderName) { - _config->authorization_header_name.emplace(authorizationHeaderName.UTF8String); + if (!errorHandler) { + return; } - if (NSDictionary *customRequestHeaders = manager.customRequestHeaders) { - for (NSString *key in customRequestHeaders) { - _config->custom_http_headers.emplace(key.UTF8String, customRequestHeaders[key].UTF8String); - } + NSError *nsError = RLMTranslateSyncError(std::move(error)); + if (!nsError) { + return; + } + RLMSyncSession *session = [[RLMSyncSession alloc] initWithSyncSession:errored_session]; + dispatch_async(dispatch_get_main_queue(), ^{ + errorHandler(nsError, session); + }); + }; + + if (NSString *authorizationHeaderName = manager.authorizationHeaderName) { + config.authorization_header_name.emplace(authorizationHeaderName.UTF8String); + } + if (NSDictionary *customRequestHeaders = manager.customRequestHeaders) { + for (NSString *key in customRequestHeaders) { + config.custom_http_headers.emplace(key.UTF8String, customRequestHeaders[key].UTF8String); } + } +} - self.customFileURL = customFileURL; - return self; +- (instancetype)initWithUser:(RLMUser *)user + partitionValue:(nullable id)partitionValue { + if (self = [super init]) { + std::stringstream s; + s << RLMConvertRLMBSONToBson(partitionValue); + _config = std::make_unique([user _syncUser], s.str()); + _path = [user pathForPartitionValue:_config->partition_value]; + setDefaults(*_config, user); } - return nil; + return self; +} + +- (instancetype)initWithUser:(RLMUser *)user { + if (self = [super init]) { + _config = std::make_unique([user _syncUser], SyncConfig::FLXSyncEnabled{}); + _path = [user pathForFlexibleSync]; + setDefaults(*_config, user); + } + return self; } @end diff --git a/Realm/RLMSyncConfiguration_Private.h b/Realm/RLMSyncConfiguration_Private.h index 66cc4a5203..b951952fc4 100644 --- a/Realm/RLMSyncConfiguration_Private.h +++ b/Realm/RLMSyncConfiguration_Private.h @@ -33,22 +33,14 @@ typedef RLM_CLOSED_ENUM(NSUInteger, RLMSyncStopPolicy) { @interface RLMSyncConfiguration () +// Flexible sync +- (instancetype)initWithUser:(RLMUser *)user; +// Partition-based sync - (instancetype)initWithUser:(RLMUser *)user - partitionValue:(nullable id)partitionValue - stopPolicy:(RLMSyncStopPolicy)stopPolicy - clientResetMode:(RLMClientResetMode)clientResetMode - notifyBeforeReset:(nullable RLMClientResetBeforeBlock)beforeResetBlock - notifyAfterReset:(nullable RLMClientResetAfterBlock)afterResetBlock; - -- (instancetype)initWithUser:(RLMUser *)user - stopPolicy:(RLMSyncStopPolicy)stopPolicy - enableFlexibleSync:(BOOL)enableFlexibleSync; - -@property (nonatomic, readwrite) RLMSyncStopPolicy stopPolicy; + partitionValue:(nullable id)partitionValue; // Internal-only APIs -@property (nullable, nonatomic) NSURL *customFileURL; - +@property (nonatomic, readwrite) RLMSyncStopPolicy stopPolicy; @property (nonatomic, readonly) BOOL enableFlexibleSync; @end diff --git a/Realm/RLMSyncConfiguration_Private.hpp b/Realm/RLMSyncConfiguration_Private.hpp index 28cedd64b6..ef9dbdb482 100644 --- a/Realm/RLMSyncConfiguration_Private.hpp +++ b/Realm/RLMSyncConfiguration_Private.hpp @@ -32,14 +32,15 @@ NS_ASSUME_NONNULL_BEGIN @interface RLMSyncConfiguration () -- (instancetype)initWithRawConfig:(realm::SyncConfig)config; - +- (instancetype)initWithRawConfig:(realm::SyncConfig)config path:(std::string const&)path; - (realm::SyncConfig&)rawConfiguration; // Pass the RLMRealmConfiguration to it's sync configuration so client reset callbacks // can access schema, dynamic, and path properties. void RLMSetConfigInfoForClientResetCallbacks(realm::SyncConfig& syncConfig, RLMRealmConfiguration *config); +@property (nonatomic) std::string path; + @end NSError *_Nullable RLMTranslateSyncError(realm::SyncError); diff --git a/Realm/RLMSyncManager.h b/Realm/RLMSyncManager.h index 7fab3ee2cd..b87adf816c 100644 --- a/Realm/RLMSyncManager.h +++ b/Realm/RLMSyncManager.h @@ -79,7 +79,7 @@ typedef void(^RLMSyncErrorReportingBlock)(NSError *, RLMSyncSession * _Nullable) @see `RLMSyncError` */ -@property (nullable, nonatomic, copy) RLMSyncErrorReportingBlock errorHandler; +@property (nullable, atomic, copy) RLMSyncErrorReportingBlock errorHandler; /** A reverse-DNS string uniquely identifying this application. In most cases this diff --git a/Realm/RLMSyncSession.mm b/Realm/RLMSyncSession.mm index fe44ff2ab5..9447861f06 100644 --- a/Realm/RLMSyncSession.mm +++ b/Realm/RLMSyncSession.mm @@ -125,7 +125,7 @@ - (instancetype)initWithSyncSession:(std::shared_ptr const&)session - (RLMSyncConfiguration *)configuration { if (auto session = _session.lock()) { - return [[RLMSyncConfiguration alloc] initWithRawConfig:session->config()]; + return [[RLMSyncConfiguration alloc] initWithRawConfig:session->config() path:session->path()]; } return nil; } diff --git a/Realm/RLMUser.mm b/Realm/RLMUser.mm index 763e414ed4..878bdf9cf7 100644 --- a/Realm/RLMUser.mm +++ b/Realm/RLMUser.mm @@ -87,10 +87,12 @@ - (RLMRealmConfiguration *)configurationWithPartitionValue:(nullable id - (RLMRealmConfiguration *)configurationWithPartitionValue:(nullable id)partitionValue clientResetMode:(RLMClientResetMode)clientResetMode { - return [self configurationWithPartitionValue:partitionValue - clientResetMode:clientResetMode - notifyBeforeReset:nil - notifyAfterReset:nil]; + auto syncConfig = [[RLMSyncConfiguration alloc] initWithUser:self + partitionValue:partitionValue]; + syncConfig.clientResetMode = clientResetMode; + RLMRealmConfiguration *config = [[RLMRealmConfiguration alloc] init]; + config.syncConfiguration = syncConfig; + return config; } - (RLMRealmConfiguration *)configurationWithPartitionValue:(nullable id)partitionValue @@ -98,30 +100,24 @@ - (RLMRealmConfiguration *)configurationWithPartitionValue:(nullable id notifyBeforeReset:(nullable RLMClientResetBeforeBlock)beforeResetBlock notifyAfterReset:(nullable RLMClientResetAfterBlock)afterResetBlock { auto syncConfig = [[RLMSyncConfiguration alloc] initWithUser:self - partitionValue:partitionValue - stopPolicy:RLMSyncStopPolicyImmediately - clientResetMode:clientResetMode - notifyBeforeReset:beforeResetBlock - notifyAfterReset:afterResetBlock]; + partitionValue:partitionValue]; + syncConfig.clientResetMode = clientResetMode; + syncConfig.beforeClientReset = beforeResetBlock; + syncConfig.afterClientReset = afterResetBlock; RLMRealmConfiguration *config = [[RLMRealmConfiguration alloc] init]; config.syncConfiguration = syncConfig; return config; } - (RLMRealmConfiguration *)flexibleSyncConfiguration { - auto syncConfig = [[RLMSyncConfiguration alloc] initWithUser:self - stopPolicy:RLMSyncStopPolicyAfterChangesUploaded - enableFlexibleSync:true]; RLMRealmConfiguration *config = [[RLMRealmConfiguration alloc] init]; - config.syncConfiguration = syncConfig; + config.syncConfiguration = [[RLMSyncConfiguration alloc] initWithUser:self]; return config; } - (RLMRealmConfiguration *)flexibleSyncConfigurationWithInitialSubscriptions:(RLMFlexibleSyncInitialSubscriptionsBlock)initialSubscriptions rerunOnOpen:(BOOL)rerunOnOpen { - auto syncConfig = [[RLMSyncConfiguration alloc] initWithUser:self - stopPolicy:RLMSyncStopPolicyAfterChangesUploaded - enableFlexibleSync:true]; + auto syncConfig = [[RLMSyncConfiguration alloc] initWithUser:self]; RLMRealmConfiguration *config = [[RLMRealmConfiguration alloc] init]; config.initialSubscriptions = initialSubscriptions; config.rerunOnOpen = rerunOnOpen; diff --git a/Realm/TestUtils/RLMTestCase.m b/Realm/TestUtils/RLMTestCase.m index 481a800a22..1095748de9 100644 --- a/Realm/TestUtils/RLMTestCase.m +++ b/Realm/TestUtils/RLMTestCase.m @@ -201,7 +201,7 @@ - (void)waitForNotification:(NSString *)expectedNote realm:(RLMRealm *)realm blo } }); - [self waitForExpectationsWithTimeout:10.0 handler:nil]; + [self waitForExpectationsWithTimeout:30.0 handler:nil]; // wait for queue to finish dispatch_sync(queue, ^{}); diff --git a/Realm/TestUtils/TestUtils.mm b/Realm/TestUtils/TestUtils.mm index 496392947a..153ef1138f 100644 --- a/Realm/TestUtils/TestUtils.mm +++ b/Realm/TestUtils/TestUtils.mm @@ -194,6 +194,19 @@ bool RLMHasCachedRealmForPath(NSString *path) { return encoded_prefix + "." + encoded_body + "." + suffix; } +// A network transport which doesn't actually do anything +@interface NoOpTransport : NSObject +@end +@implementation NoOpTransport +- (void)sendRequestToServer:(RLMRequest *)request + completion:(RLMNetworkTransportCompletionBlock)completionBlock { +} +- (NSURLSession *)doStreamRequest:(RLMRequest *)request + eventSubscriber:(id)subscriber { + return nil; +} +@end + RLMUser *RLMDummyUser() { // Add a fake user to the metadata Realm @autoreleasepool { @@ -213,7 +226,9 @@ bool RLMHasCachedRealmForPath(NSString *path) { } // Creating an app reads the fake cached user - RLMApp *app = [RLMApp appWithId:@"dummy"]; + RLMAppConfiguration *config = [RLMAppConfiguration new]; + config.transport = [NoOpTransport new]; + RLMApp *app = [RLMApp appWithId:@"dummy" configuration:config]; return app.allUsers.allValues.firstObject; } diff --git a/Realm/Tests/RealmTests.mm b/Realm/Tests/RealmTests.mm index ab6429e8f4..b48d3492a0 100644 --- a/Realm/Tests/RealmTests.mm +++ b/Realm/Tests/RealmTests.mm @@ -1579,7 +1579,7 @@ - (void)testAsyncTransactionShouldAutorefresh { [StringObject createInRealm:realm withValue:@[@"string"]]; }]; - [self waitForExpectationsWithTimeout:10.0 handler:nil]; + [self waitForExpectationsWithTimeout:30.0 handler:nil]; XCTAssertEqual(1U, [StringObject allObjectsInRealm:realm].count); } diff --git a/Realm/Tests/SchemaTests.mm b/Realm/Tests/SchemaTests.mm index 190617d23a..bde91d55b8 100644 --- a/Realm/Tests/SchemaTests.mm +++ b/Realm/Tests/SchemaTests.mm @@ -1006,7 +1006,7 @@ - (void)testMultipleProcessesTryingToInitializeSchema { [notificationFired fulfill]; } }]; - [self waitForExpectationsWithTimeout:10.0 handler:nil]; + [self waitForExpectationsWithTimeout:30.0 handler:nil]; [token invalidate]; // Release the write transaction and let them run diff --git a/Realm/Tests/SwiftUISyncTestHost/ContentView.swift b/Realm/Tests/SwiftUISyncTestHost/ContentView.swift index e5f3a88e91..06e2bd47e7 100644 --- a/Realm/Tests/SwiftUISyncTestHost/ContentView.swift +++ b/Realm/Tests/SwiftUISyncTestHost/ContentView.swift @@ -388,7 +388,7 @@ struct AsyncOpenFlexibleSyncView: View { Task { do { let subs = realm.subscriptions - try await subs.write { + try await subs.update { subs.append(QuerySubscription(name: "person_age") { $0.age > 5 && $0.firstName == ProcessInfo.processInfo.environment["firstName"]! }) @@ -444,7 +444,7 @@ struct AutoOpenFlexibleSyncView: View { Task { do { let subs = realm.subscriptions - try await subs.write { + try await subs.update { subs.append(QuerySubscription(name: "person_age") { $0.age > 2 && $0.firstName == ProcessInfo.processInfo.environment["firstName"]! }) diff --git a/Realm/Tests/SwiftUISyncTestHostUITests/SwiftUISyncTestHostUITests.swift b/Realm/Tests/SwiftUISyncTestHostUITests/SwiftUISyncTestHostUITests.swift index 462074f065..2a4bea148e 100644 --- a/Realm/Tests/SwiftUISyncTestHostUITests/SwiftUISyncTestHostUITests.swift +++ b/Realm/Tests/SwiftUISyncTestHostUITests/SwiftUISyncTestHostUITests.swift @@ -391,7 +391,7 @@ extension SwiftUISyncTestHostUITests { let realm = try Realm(configuration: config) let subs = realm.subscriptions let ex = expectation(description: "state change complete") - subs.write({ + subs.update({ subs.append(QuerySubscription(name: "person_age", where: "TRUEPREDICATE")) }, onComplete: { error in if error == nil { diff --git a/RealmSwift/ObjectiveCSupport+Sync.swift b/RealmSwift/ObjectiveCSupport+Sync.swift index b06ce69049..558294e04f 100644 --- a/RealmSwift/ObjectiveCSupport+Sync.swift +++ b/RealmSwift/ObjectiveCSupport+Sync.swift @@ -24,7 +24,7 @@ import Realm public extension ObjectiveCSupport { /// Convert a `SyncConfiguration` to a `RLMSyncConfiguration`. static func convert(object: SyncConfiguration) -> RLMSyncConfiguration { - return object.asConfig() + return object.config } /// Convert a `RLMSyncConfiguration` to a `SyncConfiguration`. diff --git a/RealmSwift/RealmConfiguration.swift b/RealmSwift/RealmConfiguration.swift index b21a6170f0..34a183724f 100644 --- a/RealmSwift/RealmConfiguration.swift +++ b/RealmSwift/RealmConfiguration.swift @@ -289,7 +289,7 @@ extension Realm { internal var rlmConfiguration: RLMRealmConfiguration { let configuration = RLMRealmConfiguration() if let syncConfiguration = syncConfiguration { - configuration.syncConfiguration = syncConfiguration.asConfig() + configuration.syncConfiguration = syncConfiguration.config } if let fileURL = fileURL { configuration.fileURL = fileURL @@ -298,21 +298,13 @@ extension Realm { } else if syncConfiguration == nil { fatalError("A Realm Configuration must specify a path or an in-memory identifier.") } - if let seedFilePath = seedFilePath { - configuration.seedFilePath = seedFilePath - } else if let inMemoryIdentifier = inMemoryIdentifier { - configuration.inMemoryIdentifier = inMemoryIdentifier - } + configuration.seedFilePath = self.seedFilePath configuration.encryptionKey = self.encryptionKey configuration.readOnly = self.readOnly configuration.schemaVersion = self.schemaVersion configuration.migrationBlock = self.migrationBlock.map { accessorMigrationBlock($0) } configuration.deleteRealmIfMigrationNeeded = self.deleteRealmIfMigrationNeeded - if let shouldCompactOnLaunch = self.shouldCompactOnLaunch { - configuration.shouldCompactOnLaunch = ObjectiveCSupport.convert(object: shouldCompactOnLaunch) - } else { - configuration.shouldCompactOnLaunch = nil - } + configuration.shouldCompactOnLaunch = self.shouldCompactOnLaunch.map(ObjectiveCSupport.convert(object:)) configuration.setCustomSchemaWithoutCopying(self.customSchema) configuration.disableFormatUpgrade = self.disableFormatUpgrade configuration.maximumNumberOfActiveVersions = self.maximumNumberOfActiveVersions ?? 0 @@ -337,11 +329,7 @@ extension Realm { var configuration = Configuration() configuration._path = rlmConfiguration.fileURL?.path configuration._inMemoryIdentifier = rlmConfiguration.inMemoryIdentifier - if let objcSyncConfig = rlmConfiguration.syncConfiguration { - configuration._syncConfiguration = SyncConfiguration(config: objcSyncConfig) - } else { - configuration._syncConfiguration = nil - } + configuration._syncConfiguration = rlmConfiguration.syncConfiguration.map(SyncConfiguration.init(config:)) configuration.encryptionKey = rlmConfiguration.encryptionKey configuration.readOnly = rlmConfiguration.readOnly configuration.schemaVersion = rlmConfiguration.schemaVersion diff --git a/RealmSwift/Sync.swift b/RealmSwift/Sync.swift index ef3ff30c9b..b5725ba038 100644 --- a/RealmSwift/Sync.swift +++ b/RealmSwift/Sync.swift @@ -262,20 +262,18 @@ public enum ClientResetMode { */ @frozen public struct SyncConfiguration { /// The `SyncUser` who owns the Realm that this configuration should open. - public let user: User + public var user: User { + config.user + } /** The value this Realm is partitioned on. The partition key is a property defined in Atlas App Services. All classes with a property with this value will be synchronized to the Realm. */ - public let partitionValue: AnyBSON? - - /** - A policy that determines what should happen when all references to Realms opened by this - configuration go out of scope. - */ - internal let stopPolicy: RLMSyncStopPolicy + public var partitionValue: AnyBSON? { + ObjectiveCSupport.convert(object: config.partitionValue) + } /** An enum which determines file recovery behvaior in the event of a client reset. @@ -284,12 +282,16 @@ public enum ClientResetMode { - see: `ClientResetMode` and `RLMClientResetMode` - see: https://docs.mongodb.com/realm/sync/error-handling/client-resets/ */ - public let clientResetMode: ClientResetMode - - /** - Determines if the sync configuration is flexible sync or not - */ - internal let isFlexibleSync: Bool + public var clientResetMode: ClientResetMode { + switch config.clientResetMode { + case .manual: + return .manual + case .discardLocal: + return .discardLocal(ObjectiveCSupport.convert(object: config.beforeClientReset), ObjectiveCSupport.convert(object: config.afterClientReset)) + @unknown default: + fatalError() + } + } /** By default, Realm.asyncOpen() swallows non-fatal connection errors such as @@ -297,49 +299,13 @@ public enum ClientResetMode { this is set to `true`, instead the error will be reported to the callback and the async open will be cancelled. */ - public let cancelAsyncOpenOnNonFatalErrors: Bool - - internal init(config: RLMSyncConfiguration) { - self.user = config.user - self.stopPolicy = config.stopPolicy - self.partitionValue = ObjectiveCSupport.convert(object: config.partitionValue) - self.cancelAsyncOpenOnNonFatalErrors = config.cancelAsyncOpenOnNonFatalErrors - self.isFlexibleSync = config.enableFlexibleSync - switch config.clientResetMode { - case .manual: - self.clientResetMode = .manual - case .discardLocal: - self.clientResetMode = .discardLocal(ObjectiveCSupport.convert(object: config.beforeClientReset), ObjectiveCSupport.convert(object: config.afterClientReset)) - @unknown default: - fatalError("what's best in this case?") - } + public var cancelAsyncOpenOnNonFatalErrors: Bool { + config.cancelAsyncOpenOnNonFatalErrors } - func asConfig() -> RLMSyncConfiguration { - let syncConfiguration: RLMSyncConfiguration - if isFlexibleSync { - syncConfiguration = RLMSyncConfiguration(user: user, stopPolicy: stopPolicy, enableFlexibleSync: isFlexibleSync) - } else { - switch clientResetMode { - case .manual: - syncConfiguration = RLMSyncConfiguration(user: user, - partitionValue: partitionValue.map(ObjectiveCSupport.convertBson), - stopPolicy: stopPolicy, - clientResetMode: .manual, - notifyBeforeReset: nil, - notifyAfterReset: nil) - case .discardLocal(let before, let after): - syncConfiguration = RLMSyncConfiguration(user: user, - partitionValue: partitionValue.map(ObjectiveCSupport.convertBson), - stopPolicy: stopPolicy, - clientResetMode: .discardLocal, - notifyBeforeReset: ObjectiveCSupport.convert(object: before), - notifyAfterReset: ObjectiveCSupport.convert(object: after)) - } - - } - syncConfiguration.cancelAsyncOpenOnNonFatalErrors = cancelAsyncOpenOnNonFatalErrors - return syncConfiguration + internal let config: RLMSyncConfiguration + internal init(config: RLMSyncConfiguration) { + self.config = config } } diff --git a/RealmSwift/Tests/CombineTests.swift b/RealmSwift/Tests/CombineTests.swift index 77c02f28ad..fe7225c732 100644 --- a/RealmSwift/Tests/CombineTests.swift +++ b/RealmSwift/Tests/CombineTests.swift @@ -111,6 +111,7 @@ class CombinePublisherTestCase: TestCase { override func setUp() { super.setUp() realm = try! Realm(configuration: Realm.Configuration(inMemoryIdentifier: "test")) + XCTAssertTrue(realm.isEmpty) } override func tearDown() { @@ -2775,9 +2776,7 @@ class CombineProjectionPublisherTests: CombinePublisherTestCase { class CombineAsyncRealmTests: CombinePublisherTestCase { func testWillChangeLocalWrite() { let asyncWriteExpectation = expectation(description: "Should complete async write") - cancellable = realm - .objectWillChange - .sink { + cancellable = realm.objectWillChange.sink { asyncWriteExpectation.fulfill() } @@ -2792,8 +2791,8 @@ class CombineAsyncRealmTests: CombinePublisherTestCase { cancellable = realm.objectWillChange.sink { exp.fulfill() } - DispatchQueue.main.async { - let realm = try! Realm(configuration: self.realm.configuration) + queue.async { + let realm = try! Realm(configuration: self.realm.configuration, queue: self.queue) realm.writeAsync { realm.create(SwiftIntObject.self, value: []) } diff --git a/RealmSwift/Tests/PerformanceTests.swift b/RealmSwift/Tests/PerformanceTests.swift index 3324d3d31f..5a222d8f12 100644 --- a/RealmSwift/Tests/PerformanceTests.swift +++ b/RealmSwift/Tests/PerformanceTests.swift @@ -19,6 +19,10 @@ import XCTest import RealmSwift +#if canImport(RealmTestSupport) +import RealmTestSupport +#endif + private func createStringObjects(_ factor: Int) -> Realm { let realm = inMemoryRealm(factor.description) try! realm.write { @@ -435,6 +439,92 @@ class SwiftPerformanceTests: TestCase { } } + func deleteServerFiles() { + try! FileManager.default.removeItem(at: URL(fileURLWithPath: testDir, isDirectory: true).deletingLastPathComponent().appendingPathComponent("mongodb-realm")) + App.resetAppCache() + } + + func testSyncRealmCacheLookup() { + var config = RLMDummyUser().configuration(partitionValue: "") + config.objectTypes = [] + let realm = try! Realm(configuration: config) + + measure { + for _ in 0..<1250 { + autoreleasepool { + _ = try! Realm(configuration: config) + } + } + } + realm.invalidate() + deleteServerFiles() + } + + func testSyncRealmCreationCached() { + var config = RLMDummyUser().configuration(partitionValue: "") + config.objectTypes = [] + var realm: Realm! + dispatchSyncNewThread { + realm = try! Realm(configuration: config) + } + + measure { + for _ in 0..<1250 { + autoreleasepool { + _ = try! Realm(configuration: config) + } + } + } + _ = realm.configuration + deleteServerFiles() + } + + func testSyncRealmMultithreadedCacheLookup() { + var config = RLMDummyUser().configuration(partitionValue: "") + config.objectTypes = [] + var realm: Realm! + dispatchSyncNewThread { + realm = try! Realm(configuration: config) + } + + measure { + DispatchQueue.concurrentPerform(iterations: 50) { _ in + autoreleasepool { + let realm = try! Realm(configuration: config) + for _ in 0..<25 { + autoreleasepool { + _ = try! Realm(configuration: config) + } + } + realm.invalidate() + } + } + } + _ = realm.configuration + deleteServerFiles() + } + + func testSyncRealmMultithreadedCreationCached() { + var config = RLMDummyUser().configuration(partitionValue: "") + config.objectTypes = [] + var realm: Realm! + dispatchSyncNewThread { + realm = try! Realm(configuration: config) + } + + measure { + DispatchQueue.concurrentPerform(iterations: 50) { _ in + for _ in 0..<25 { + autoreleasepool { + _ = try! Realm(configuration: config) + } + } + } + } + _ = realm.configuration + deleteServerFiles() + } + func testCommitWriteTransaction() { inMeasureBlock { let realm = inMemoryRealm("test") @@ -855,3 +945,117 @@ class SwiftPerformanceTests: TestCase { } } } + +class SwiftSyncRealmPerformanceTests: TestCase { + override class var defaultTestSuite: XCTestSuite { + #if !DEBUG && os(iOS) && !targetEnvironment(macCatalyst) + if isRunningOnDevice { + return super.defaultTestSuite + } + #endif + return XCTestSuite(name: "SwiftSyncRealmPerformanceTests") + } + + override func measure(_ block: (() -> Void)) { + super.measure { + autoreleasepool { + block() + } + } + } + + func deleteServerFiles() { + try! FileManager.default.removeItem(at: URL(fileURLWithPath: testDir, isDirectory: true).deletingLastPathComponent().appendingPathComponent("mongodb-realm")) + App.resetAppCache() + } + + var config: Realm.Configuration { + var config = RLMDummyUser().configuration(partitionValue: "") + config.objectTypes = [] + return config + } + + func testSyncRealmCacheLookup() { + let config = self.config + let realm = try! Realm(configuration: config) + + measure { + for _ in 0..<1250 { + autoreleasepool { + _ = try! Realm(configuration: config) + } + } + } + realm.invalidate() + deleteServerFiles() + } + + func testSyncRealmCreationCached() { + let config = self.config + var realm: Realm! + dispatchSyncNewThread { + // Open on a different thread so that the test hits the path where + // the cache lookup is a miss but there's a cached Realm on a + // different thread + realm = try! Realm(configuration: config) + } + + measure { + for _ in 0..<1250 { + autoreleasepool { + _ = try! Realm(configuration: config) + } + } + } + _ = realm.configuration // ensure realm is still alive until this point + deleteServerFiles() + } + + func testSyncRealmMultithreadedCacheLookup() { + let config = self.config + let realm = try! Realm(configuration: config) + + measure { + DispatchQueue.concurrentPerform(iterations: 50) { _ in + autoreleasepool { + // Ideally we wouldn't measure this and would only measure + // the cache lookups but that'd be much more difficult to set up + let realm = try! Realm(configuration: config) + for _ in 0..<25 { + autoreleasepool { + _ = try! Realm(configuration: config) + } + } + realm.invalidate() + } + } + } + realm.invalidate() // ensure realm is still alive until this point + deleteServerFiles() + } + + func testSyncRealmMultithreadedCreationCached() { + let config = self.config + let realm = try! Realm(configuration: config) + + measure { + DispatchQueue.concurrentPerform(iterations: 50) { _ in + for _ in 0..<25 { + autoreleasepool { + _ = try! Realm(configuration: config) + } + } + } + } + realm.invalidate() // ensure realm is still alive until this point + deleteServerFiles() + } +} + +class SwiftFlexibleSyncRealmPerformanceTests: SwiftSyncRealmPerformanceTests { + override var config: Realm.Configuration { + var config = RLMDummyUser().flexibleSyncConfiguration() + config.objectTypes = [] + return config + } +} diff --git a/build.sh b/build.sh index ff33b7fa8a..e9bf1bce34 100755 --- a/build.sh +++ b/build.sh @@ -1043,7 +1043,7 @@ case "$COMMAND" in mkdir .baas mv build/stitch .baas source "$(brew --prefix nvm)/nvm.sh" --no-use - nvm install 13.14.0 + nvm install 16.5.0 sh build.sh setup-baas fi