You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
540 lines
20 KiB
540 lines
20 KiB
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright 2016 Realm Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
#import "RLMSyncTestCase.h"
|
|
|
|
#import <XCTest/XCTest.h>
|
|
#import <Realm/Realm.h>
|
|
|
|
#import "RLMRealm_Dynamic.h"
|
|
#import "RLMRealm_Private.hpp"
|
|
#import "RLMRealmConfiguration_Private.h"
|
|
#import "RLMSyncManager+ObjectServerTests.h"
|
|
#import "RLMSyncSessionRefreshHandle+ObjectServerTests.h"
|
|
#import "RLMSyncConfiguration_Private.h"
|
|
#import "RLMUtil.hpp"
|
|
|
|
#import "sync/sync_manager.hpp"
|
|
#import "sync/sync_session.hpp"
|
|
#import "sync/sync_user.hpp"
|
|
|
|
// Set this to 1 if you want the test ROS instance to log its debug messages to console.
|
|
#define LOG_ROS_OUTPUT 0
|
|
|
|
#if !TARGET_OS_MAC
|
|
#error These tests can only be run on a macOS host.
|
|
#endif
|
|
|
|
static NSString *nodePath() {
|
|
static NSString *path = [] {
|
|
NSDictionary *environment = NSProcessInfo.processInfo.environment;
|
|
if (NSString *path = environment[@"REALM_NODE_PATH"]) {
|
|
return path;
|
|
}
|
|
return @"/usr/local/bin/node";
|
|
}();
|
|
return path;
|
|
}
|
|
|
|
@interface RLMSyncManager ()
|
|
+ (void)_setCustomBundleID:(NSString *)customBundleID;
|
|
- (instancetype)initWithCustomRootDirectory:(NSURL *)rootDirectory;
|
|
@end
|
|
|
|
@interface RLMSyncTestCase ()
|
|
@property (nonatomic) NSTask *task;
|
|
@end
|
|
|
|
@interface RLMSyncCredentials ()
|
|
+ (instancetype)credentialsWithDebugUserID:(NSString *)userID isAdmin:(BOOL)isAdmin;
|
|
@end
|
|
|
|
@interface RLMSyncSession ()
|
|
- (BOOL)waitForUploadCompletionOnQueue:(dispatch_queue_t)queue callback:(void(^)(NSError *))callback;
|
|
- (BOOL)waitForDownloadCompletionOnQueue:(dispatch_queue_t)queue callback:(void(^)(NSError *))callback;
|
|
@end
|
|
|
|
@interface RLMSyncUser()
|
|
- (std::shared_ptr<realm::SyncUser>)_syncUser;
|
|
@end
|
|
|
|
@implementation SyncObject
|
|
@end
|
|
|
|
@implementation HugeSyncObject
|
|
|
|
+ (instancetype)object {
|
|
const NSInteger fakeDataSize = 1000000;
|
|
HugeSyncObject *object = [[self alloc] init];
|
|
char fakeData[fakeDataSize];
|
|
memset(fakeData, 16, sizeof(fakeData));
|
|
object.dataProp = [NSData dataWithBytes:fakeData length:sizeof(fakeData)];
|
|
return object;
|
|
}
|
|
|
|
@end
|
|
|
|
static NSTask *s_task;
|
|
static RLMSyncManager *s_managerForTest;
|
|
|
|
static NSURL *syncDirectoryForChildProcess() {
|
|
NSString *path = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES)[0];
|
|
NSBundle *bundle = [NSBundle mainBundle];
|
|
NSString *bundleIdentifier = bundle.bundleIdentifier ?: bundle.executablePath.lastPathComponent;
|
|
path = [path stringByAppendingPathComponent:[NSString stringWithFormat:@"%@-child", bundleIdentifier]];
|
|
return [NSURL fileURLWithPath:path isDirectory:YES];
|
|
}
|
|
|
|
@interface RealmObjectServer : NSObject
|
|
@property (nonatomic, readonly) NSURL *serverDataRoot;
|
|
|
|
+ (instancetype)sharedServer;
|
|
|
|
- (void)launch;
|
|
@end
|
|
|
|
@implementation RealmObjectServer {
|
|
NSTask *_task;
|
|
NSURL *_serverDataRoot;
|
|
}
|
|
+ (instancetype)sharedServer {
|
|
static RealmObjectServer *instance = [RealmObjectServer new];
|
|
return instance;
|
|
}
|
|
|
|
- (instancetype)init {
|
|
if (self = [super init]) {
|
|
_serverDataRoot = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"test-ros-data"]];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)launch {
|
|
if (_task) {
|
|
return;
|
|
}
|
|
// Clean up any old state from the server
|
|
[[NSTask launchedTaskWithLaunchPath:@"/usr/bin/pkill"
|
|
arguments:@[@"-f", @"node.*test-ros-server.js"]] waitUntilExit];
|
|
NSError *error;
|
|
[NSFileManager.defaultManager removeItemAtURL:self.serverDataRoot error:&error];
|
|
if (error && error.code != NSFileNoSuchFileError) {
|
|
NSLog(@"Failed to delete old test state: %@", error);
|
|
abort();
|
|
}
|
|
error = nil;
|
|
[NSFileManager.defaultManager createDirectoryAtURL:self.serverDataRoot
|
|
withIntermediateDirectories:YES attributes:nil error:&error];
|
|
if (error) {
|
|
NSLog(@"Failed to create scratch directory: %@", error);
|
|
abort();
|
|
}
|
|
|
|
// Install ROS if it isn't already present
|
|
[self downloadObjectServer];
|
|
|
|
// Set up the actual ROS task
|
|
NSPipe *pipe = [NSPipe pipe];
|
|
_task = [[NSTask alloc] init];
|
|
_task.currentDirectoryPath = self.serverDataRoot.path;
|
|
_task.launchPath = nodePath();
|
|
NSString *directory = [@(__FILE__) stringByDeletingLastPathComponent];
|
|
_task.arguments = @[[directory stringByAppendingPathComponent:@"test-ros-server.js"],
|
|
self.serverDataRoot.path];
|
|
_task.standardOutput = pipe;
|
|
[_task launch];
|
|
|
|
NSData *childStdout = pipe.fileHandleForReading.readDataToEndOfFile;
|
|
if (![childStdout isEqual:[@"started\n" dataUsingEncoding:NSUTF8StringEncoding]]) {
|
|
abort();
|
|
}
|
|
|
|
atexit([] {
|
|
auto self = RealmObjectServer.sharedServer;
|
|
[self->_task terminate];
|
|
[self->_task waitUntilExit];
|
|
[NSFileManager.defaultManager removeItemAtURL:self->_serverDataRoot error:nil];
|
|
});
|
|
}
|
|
|
|
- (NSString *)desiredObjectServerVersion {
|
|
auto path = [[[[@(__FILE__) stringByDeletingLastPathComponent] // RLMSyncTestCase.mm
|
|
stringByDeletingLastPathComponent] // ObjectServerTests
|
|
stringByDeletingLastPathComponent] // Realm
|
|
stringByAppendingPathComponent:@"dependencies.list"];
|
|
auto file = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
|
|
if (!file) {
|
|
NSLog(@"Failed to read dependencies.list");
|
|
abort();
|
|
}
|
|
|
|
auto regex = [NSRegularExpression regularExpressionWithPattern:@"^REALM_OBJECT_SERVER_VERSION=(.*)$"
|
|
options:NSRegularExpressionAnchorsMatchLines error:nil];
|
|
auto match = [regex firstMatchInString:file options:0 range:{0, file.length}];
|
|
if (!match) {
|
|
NSLog(@"Failed to read REALM_OBJECT_SERVER_VERSION from dependencies.list");
|
|
abort();
|
|
}
|
|
return [file substringWithRange:[match rangeAtIndex:1]];
|
|
}
|
|
|
|
- (NSString *)currentObjectServerVersion {
|
|
auto path = [[[[@(__FILE__) stringByDeletingLastPathComponent] // RLMSyncTestCase.mm
|
|
stringByAppendingPathComponent:@"node_modules"]
|
|
stringByAppendingPathComponent:@"realm-object-server"]
|
|
stringByAppendingPathComponent:@"package.json"];
|
|
auto file = [NSData dataWithContentsOfFile:path];
|
|
if (!file) {
|
|
return nil;
|
|
}
|
|
|
|
NSError *error;
|
|
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:file options:0 error:&error];
|
|
if (!json) {
|
|
NSLog(@"Error reading version from installed ROS: %@", error);
|
|
abort();
|
|
}
|
|
|
|
return json[@"version"];
|
|
}
|
|
|
|
- (void)downloadObjectServer {
|
|
NSString *desiredVersion = [self desiredObjectServerVersion];
|
|
NSString *currentVersion = [self currentObjectServerVersion];
|
|
if ([currentVersion isEqualToString:desiredVersion]) {
|
|
return;
|
|
}
|
|
|
|
NSLog(@"Installing Realm Object Server %@", desiredVersion);
|
|
NSTask *task = [[NSTask alloc] init];
|
|
task.currentDirectoryPath = [@(__FILE__) stringByDeletingLastPathComponent];
|
|
task.launchPath = nodePath();
|
|
task.arguments = @[[[nodePath() stringByDeletingLastPathComponent] stringByAppendingPathComponent:@"npm"],
|
|
@"--scripts-prepend-node-path=auto",
|
|
@"--no-color",
|
|
@"--no-progress",
|
|
@"--no-save",
|
|
@"--no-package-lock",
|
|
@"install",
|
|
[@"realm-object-server@" stringByAppendingString:desiredVersion]
|
|
];
|
|
[task launch];
|
|
[task waitUntilExit];
|
|
}
|
|
@end
|
|
|
|
@implementation RLMSyncTestCase
|
|
|
|
+ (RLMSyncManager *)managerForCurrentTest {
|
|
return s_managerForTest;
|
|
}
|
|
|
|
#pragma mark - Helper methods
|
|
|
|
- (BOOL)isPartial {
|
|
return NO;
|
|
}
|
|
|
|
+ (NSURL *)authServerURL {
|
|
return [NSURL URLWithString:@"http://127.0.0.1:9080"];
|
|
}
|
|
|
|
+ (NSURL *)secureAuthServerURL {
|
|
return [NSURL URLWithString:@"https://localhost:9443"];
|
|
}
|
|
|
|
+ (RLMSyncCredentials *)basicCredentialsWithName:(NSString *)name register:(BOOL)shouldRegister {
|
|
return [RLMSyncCredentials credentialsWithUsername:name
|
|
password:@"a"
|
|
register:shouldRegister];
|
|
}
|
|
|
|
+ (NSURL *)onDiskPathForSyncedRealm:(RLMRealm *)realm {
|
|
return [NSURL fileURLWithPath:@(realm->_realm->config().path.data())];
|
|
}
|
|
|
|
- (void)addSyncObjectsToRealm:(RLMRealm *)realm descriptions:(NSArray<NSString *> *)descriptions {
|
|
[realm beginWriteTransaction];
|
|
for (NSString *desc in descriptions) {
|
|
[SyncObject createInRealm:realm withValue:@[desc]];
|
|
}
|
|
[realm commitWriteTransaction];
|
|
}
|
|
|
|
- (void)waitForDownloadsForUser:(RLMSyncUser *)user
|
|
realms:(NSArray<RLMRealm *> *)realms
|
|
realmURLs:(NSArray<NSURL *> *)realmURLs
|
|
expectedCounts:(NSArray<NSNumber *> *)counts {
|
|
NSAssert(realms.count == counts.count && realms.count == realmURLs.count,
|
|
@"Test logic error: all array arguments must be the same size.");
|
|
for (NSUInteger i = 0; i < realms.count; i++) {
|
|
[self waitForDownloadsForUser:user url:realmURLs[i] expectation:nil error:nil];
|
|
[realms[i] refresh];
|
|
CHECK_COUNT([counts[i] integerValue], SyncObject, realms[i]);
|
|
}
|
|
}
|
|
|
|
- (RLMRealm *)openRealmForURL:(NSURL *)url user:(RLMSyncUser *)user {
|
|
return [self openRealmForURL:url user:user immediatelyBlock:nil];
|
|
}
|
|
|
|
- (RLMRealm *)openRealmForURL:(NSURL *)url user:(RLMSyncUser *)user immediatelyBlock:(void(^)(void))block {
|
|
return [self openRealmForURL:url
|
|
user:user
|
|
encryptionKey:nil
|
|
stopPolicy:RLMSyncStopPolicyAfterChangesUploaded
|
|
immediatelyBlock:block];
|
|
}
|
|
|
|
- (RLMRealm *)openRealmForURL:(NSURL *)url
|
|
user:(RLMSyncUser *)user
|
|
encryptionKey:(nullable NSData *)encryptionKey
|
|
stopPolicy:(RLMSyncStopPolicy)stopPolicy
|
|
immediatelyBlock:(nullable void(^)(void))block {
|
|
const NSTimeInterval timeout = 4;
|
|
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
|
|
RLMSyncManager.sharedManager.sessionCompletionNotifier = ^(NSError *error) {
|
|
if (error) {
|
|
XCTFail(@"Received an asynchronous error when trying to open Realm at '%@' for user '%@': %@ (process: %@)",
|
|
url, user.identity, error, self.isParent ? @"parent" : @"child");
|
|
}
|
|
dispatch_semaphore_signal(sema);
|
|
};
|
|
|
|
RLMRealm *realm = [self immediatelyOpenRealmForURL:url user:user encryptionKey:encryptionKey stopPolicy:stopPolicy];
|
|
if (block) {
|
|
block();
|
|
}
|
|
// Wait for login to succeed or fail.
|
|
XCTAssert(dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeout * NSEC_PER_SEC))) == 0,
|
|
@"Timed out while trying to asynchronously open Realm for URL: %@", url);
|
|
return realm;
|
|
}
|
|
|
|
- (RLMRealm *)openRealmWithConfiguration:(RLMRealmConfiguration *)configuration {
|
|
return [self openRealmWithConfiguration:configuration immediatelyBlock:nullptr];
|
|
}
|
|
|
|
- (RLMRealm *)openRealmWithConfiguration:(RLMRealmConfiguration *)configuration
|
|
immediatelyBlock:(nullable void(^)(void))block {
|
|
const NSTimeInterval timeout = 4;
|
|
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
|
|
RLMSyncConfiguration *syncConfig = configuration.syncConfiguration;
|
|
RLMSyncManager.sharedManager.sessionCompletionNotifier = ^(NSError *error) {
|
|
if (error) {
|
|
XCTFail(@"Received an asynchronous error when trying to open Realm at '%@' for user '%@': %@ (process: %@)",
|
|
syncConfig.realmURL, syncConfig.user.identity, error, self.isParent ? @"parent" : @"child");
|
|
}
|
|
dispatch_semaphore_signal(sema);
|
|
};
|
|
|
|
RLMRealm *realm = [RLMRealm realmWithConfiguration:configuration error:nullptr];
|
|
if (block) {
|
|
block();
|
|
}
|
|
// Wait for login to succeed or fail.
|
|
XCTAssert(dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeout * NSEC_PER_SEC))) == 0,
|
|
@"Timed out while trying to asynchronously open Realm for URL: %@", syncConfig.realmURL);
|
|
return realm;
|
|
}
|
|
- (RLMRealm *)immediatelyOpenRealmForURL:(NSURL *)url user:(RLMSyncUser *)user {
|
|
return [self immediatelyOpenRealmForURL:url
|
|
user:user
|
|
encryptionKey:nil
|
|
stopPolicy:RLMSyncStopPolicyAfterChangesUploaded];
|
|
}
|
|
|
|
- (RLMRealm *)immediatelyOpenRealmForURL:(NSURL *)url
|
|
user:(RLMSyncUser *)user
|
|
encryptionKey:(NSData *)encryptionKey
|
|
stopPolicy:(RLMSyncStopPolicy)stopPolicy {
|
|
auto c = [user configurationWithURL:url fullSynchronization:!self.isPartial];
|
|
c.encryptionKey = encryptionKey;
|
|
RLMSyncConfiguration *syncConfig = c.syncConfiguration;
|
|
syncConfig.stopPolicy = stopPolicy;
|
|
c.syncConfiguration = syncConfig;
|
|
return [RLMRealm realmWithConfiguration:c error:nil];
|
|
}
|
|
|
|
- (RLMSyncUser *)logInUserForCredentials:(RLMSyncCredentials *)credentials
|
|
server:(NSURL *)url {
|
|
NSString *process = self.isParent ? @"parent" : @"child";
|
|
__block RLMSyncUser *theUser = nil;
|
|
XCTestExpectation *expectation = [self expectationWithDescription:@"Should log in the user properly"];
|
|
[RLMSyncUser logInWithCredentials:credentials
|
|
authServerURL:url
|
|
onCompletion:^(RLMSyncUser *user, NSError *error) {
|
|
XCTAssertTrue(NSThread.isMainThread);
|
|
XCTAssertNil(error,
|
|
@"Error when trying to log in a user: %@ (process: %@)",
|
|
error, process);
|
|
XCTAssertNotNil(user);
|
|
theUser = user;
|
|
[expectation fulfill];
|
|
}];
|
|
[self waitForExpectationsWithTimeout:4.0 handler:nil];
|
|
XCTAssertTrue(theUser.state == RLMSyncUserStateActive,
|
|
@"User should have been valid, but wasn't. (process: %@)", process);
|
|
return theUser;
|
|
}
|
|
|
|
- (RLMSyncUser *)createAdminUserForURL:(NSURL *)url username:(NSString *)username {
|
|
return [self logInUserForCredentials:[RLMSyncCredentials credentialsWithDebugUserID:username isAdmin:YES]
|
|
server:url];
|
|
}
|
|
|
|
- (NSString *)adminToken {
|
|
NSURL *target = [RealmObjectServer.sharedServer.serverDataRoot
|
|
URLByAppendingPathComponent:@"/keys/admin.json"];
|
|
if (![[NSFileManager defaultManager] fileExistsAtPath:[target path]]) {
|
|
XCTFail(@"Could not find the JSON file containing the admin token.");
|
|
return nil;
|
|
}
|
|
NSData *raw = [NSData dataWithContentsOfURL:target];
|
|
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:raw options:0 error:nil];
|
|
NSString *token = json[@"ADMIN_TOKEN"];
|
|
if ([token length] == 0) {
|
|
XCTFail(@"Could not successfully extract the token.");
|
|
}
|
|
return token;
|
|
}
|
|
|
|
- (NSString *)emailForAddress:(NSString *)email {
|
|
NSURL *target = [[RealmObjectServer.sharedServer.serverDataRoot
|
|
URLByAppendingPathComponent:@"/email"]
|
|
URLByAppendingPathComponent:email];
|
|
NSString *body = [NSString stringWithContentsOfURL:target encoding:NSUTF8StringEncoding error:nil];
|
|
if (body) {
|
|
[NSFileManager.defaultManager removeItemAtURL:target error:nil];
|
|
}
|
|
return body;
|
|
}
|
|
|
|
- (void)waitForDownloadsForRealm:(RLMRealm *)realm {
|
|
[self waitForDownloadsForRealm:realm error:nil];
|
|
}
|
|
|
|
- (void)waitForUploadsForRealm:(RLMRealm *)realm {
|
|
[self waitForUploadsForRealm:realm error:nil];
|
|
}
|
|
|
|
- (void)waitForDownloadsForUser:(RLMSyncUser *)user
|
|
url:(NSURL *)url
|
|
expectation:(XCTestExpectation *)expectation
|
|
error:(NSError **)error {
|
|
RLMSyncSession *session = [user sessionForURL:url];
|
|
NSAssert(session, @"Cannot call with invalid URL");
|
|
XCTestExpectation *ex = expectation ?: [self expectationWithDescription:@"Wait for download completion"];
|
|
__block NSError *theError = nil;
|
|
BOOL queued = [session waitForDownloadCompletionOnQueue:nil callback:^(NSError *err) {
|
|
theError = err;
|
|
[ex fulfill];
|
|
}];
|
|
if (!queued) {
|
|
XCTFail(@"Download waiter did not queue; session was invalid or errored out.");
|
|
return;
|
|
}
|
|
[self waitForExpectations:@[ex] timeout:20.0];
|
|
if (error) {
|
|
*error = theError;
|
|
}
|
|
}
|
|
|
|
- (void)waitForUploadsForRealm:(RLMRealm *)realm error:(NSError **)error {
|
|
RLMSyncSession *session = realm.syncSession;
|
|
NSAssert(session, @"Cannot call with invalid Realm");
|
|
XCTestExpectation *ex = [self expectationWithDescription:@"Wait for upload completion"];
|
|
__block NSError *completionError;
|
|
BOOL queued = [session waitForUploadCompletionOnQueue:nil callback:^(NSError *error) {
|
|
completionError = error;
|
|
[ex fulfill];
|
|
}];
|
|
if (!queued) {
|
|
XCTFail(@"Upload waiter did not queue; session was invalid or errored out.");
|
|
return;
|
|
}
|
|
[self waitForExpectations:@[ex] timeout:20.0];
|
|
if (error)
|
|
*error = completionError;
|
|
}
|
|
|
|
- (void)waitForDownloadsForRealm:(RLMRealm *)realm error:(NSError **)error {
|
|
RLMSyncSession *session = realm.syncSession;
|
|
NSAssert(session, @"Cannot call with invalid Realm");
|
|
XCTestExpectation *ex = [self expectationWithDescription:@"Wait for download completion"];
|
|
__block NSError *completionError;
|
|
BOOL queued = [session waitForDownloadCompletionOnQueue:nil callback:^(NSError *error) {
|
|
completionError = error;
|
|
[ex fulfill];
|
|
}];
|
|
if (!queued) {
|
|
XCTFail(@"Download waiter did not queue; session was invalid or errored out.");
|
|
return;
|
|
}
|
|
[self waitForExpectations:@[ex] timeout:20.0];
|
|
if (error)
|
|
*error = completionError;
|
|
}
|
|
|
|
- (void)manuallySetRefreshTokenForUser:(RLMSyncUser *)user value:(NSString *)tokenValue {
|
|
[user _syncUser]->update_refresh_token(tokenValue.UTF8String);
|
|
}
|
|
|
|
// FIXME: remove this API once the new token system is implemented.
|
|
- (void)primeSyncManagerWithSemaphore:(dispatch_semaphore_t)semaphore {
|
|
if (semaphore == nil) {
|
|
[[RLMSyncManager sharedManager] setSessionCompletionNotifier:^(__unused NSError *error){ }];
|
|
return;
|
|
}
|
|
[[RLMSyncManager sharedManager] setSessionCompletionNotifier:^(NSError *error) {
|
|
XCTAssertNil(error, @"Session completion block returned with an error: %@", error);
|
|
dispatch_semaphore_signal(semaphore);
|
|
}];
|
|
}
|
|
|
|
#pragma mark - XCUnitTest Lifecycle
|
|
|
|
- (void)setUp {
|
|
[super setUp];
|
|
self.continueAfterFailure = NO;
|
|
NSURL *clientDataRoot;
|
|
if (self.isParent) {
|
|
[RealmObjectServer.sharedServer launch];
|
|
clientDataRoot = [NSURL fileURLWithPath:RLMDefaultDirectoryForBundleIdentifier(nil)];
|
|
}
|
|
else {
|
|
clientDataRoot = syncDirectoryForChildProcess();
|
|
}
|
|
NSError *error;
|
|
[NSFileManager.defaultManager removeItemAtURL:clientDataRoot error:&error];
|
|
[NSFileManager.defaultManager createDirectoryAtURL:clientDataRoot
|
|
withIntermediateDirectories:YES attributes:nil error:&error];
|
|
s_managerForTest = [[RLMSyncManager alloc] initWithCustomRootDirectory:clientDataRoot];
|
|
[RLMSyncManager sharedManager].logLevel = RLMSyncLogLevelOff;
|
|
[RLMSyncManager sharedManager].userAgent = self.name;
|
|
}
|
|
|
|
- (void)tearDown {
|
|
[s_managerForTest prepareForDestruction];
|
|
s_managerForTest = nil;
|
|
[RLMSyncSessionRefreshHandle calculateFireDateUsingTestLogic:NO blockOnRefreshCompletion:nil];
|
|
|
|
[super tearDown];
|
|
}
|
|
|
|
@end
|
|
|