An amazing project that generates micro reports from tournament results
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.

414 lines
14 KiB

////////////////////////////////////////////////////////////////////////////
//
// Copyright 2015 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 "RLMMultiProcessTestCase.h"
#import "RLMConstants.h"
@interface InterprocessTest : RLMMultiProcessTestCase
@end
@implementation InterprocessTest
- (void)setUp {
[super setUp];
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
config.objectClasses = @[IntObject.class, DoubleObject.class];
[RLMRealmConfiguration setDefaultConfiguration:config];
}
- (void)testCreateInitialRealmInChild {
if (self.isParent) {
RLMRunChildAndWait();
RLMRealm *realm = [RLMRealm defaultRealm];
XCTAssertEqual(1U, [IntObject allObjectsInRealm:realm].count);
}
else {
RLMRealm *realm = [RLMRealm defaultRealm];
[realm beginWriteTransaction];
[IntObject createInRealm:realm withValue:@[@0]];
[realm commitWriteTransaction];
}
}
- (void)testCreateInitialRealmInParent {
RLMRealm *realm = [RLMRealm defaultRealm];
if (self.isParent) {
[realm beginWriteTransaction];
[IntObject createInRealm:realm withValue:@[@0]];
[realm commitWriteTransaction];
RLMRunChildAndWait();
}
else {
XCTAssertEqual(1U, [IntObject allObjectsInRealm:realm].count);
}
}
- (void)testCompactOnLaunchSuccessful {
if (self.isParent) {
@autoreleasepool {
RLMRealm *realm = RLMRealm.defaultRealm;
[realm transactionWithBlock:^{
for (int i = 0; i < 100; ++i) {
[IntObject createInRealm:realm withValue:@[@(i)]];
}
}];
[realm transactionWithBlock:^{
[realm deleteAllObjects];
}];
}
RLMRunChildAndWait(); // runs the event loop
} else {
unsigned long long (^fileSize)(NSString *) = ^unsigned long long(NSString *path) {
NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil];
return [(NSNumber *)attributes[NSFileSize] unsignedLongLongValue];
};
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
config.shouldCompactOnLaunch = ^BOOL(__unused NSUInteger totalBytes, __unused NSUInteger usedBytes){
return YES;
};
unsigned long long sizeBefore = fileSize(config.fileURL.path);
RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:nil];
unsigned long long sizeAfter = fileSize(config.fileURL.path);
XCTAssertGreaterThan(sizeBefore, sizeAfter);
XCTAssertTrue(realm.isEmpty);
}
}
- (void)testCompactOnLaunchBeginWriteFailed {
if (self.isParent) {
RLMRealm *realm = [RLMRealm defaultRealm];
[realm beginWriteTransaction];
RLMRunChildAndWait(); // runs the event loop
[realm cancelWriteTransaction];
} else {
unsigned long long (^fileSize)(NSString *) = ^unsigned long long(NSString *path) {
NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil];
return [(NSNumber *)attributes[NSFileSize] unsignedLongLongValue];
};
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
config.shouldCompactOnLaunch = ^BOOL(__unused NSUInteger totalBytes, __unused NSUInteger usedBytes){
return YES;
};
unsigned long long sizeBefore = fileSize(config.fileURL.path);
RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:nil];
unsigned long long sizeAfter = fileSize(config.fileURL.path);
XCTAssertEqual(sizeBefore, sizeAfter);
XCTAssertTrue(realm.isEmpty);
}
}
- (void)testCompactOnLaunchFailSilently {
if (self.isParent) {
RLMRealm *realm = [RLMRealm defaultRealm];
RLMRunChildAndWait(); // runs the event loop
(void)[realm configuration]; // ensure the Realm stays open while the child process runs
} else {
unsigned long long (^fileSize)(NSString *) = ^unsigned long long(NSString *path) {
NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil];
return [(NSNumber *)attributes[NSFileSize] unsignedLongLongValue];
};
__block BOOL blockCalled = NO;
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
config.shouldCompactOnLaunch = ^BOOL(__unused NSUInteger totalBytes, __unused NSUInteger usedBytes){
blockCalled = YES;
return YES;
};
unsigned long long sizeBefore = fileSize(config.fileURL.path);
RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:nil];
unsigned long long sizeAfter = fileSize(config.fileURL.path);
XCTAssertLessThanOrEqual(sizeBefore, sizeAfter);
XCTAssertTrue(realm.isEmpty);
XCTAssertTrue(blockCalled);
}
}
- (void)testOpenInParentThenAddObjectInChild {
RLMRealm *realm = [RLMRealm defaultRealm];
XCTAssertEqual(0U, [IntObject allObjectsInRealm:realm].count);
if (self.isParent) {
RLMRunChildAndWait(); // runs the event loop
XCTAssertEqual(1U, [IntObject allObjectsInRealm:realm].count);
}
else {
[realm beginWriteTransaction];
[IntObject createInRealm:realm withValue:@[@0]];
[realm commitWriteTransaction];
}
}
- (void)testOpenInParentThenAddObjectInChildWithoutAutorefresh {
RLMRealm *realm = [RLMRealm defaultRealm];
realm.autorefresh = NO;
XCTAssertEqual(0U, [IntObject allObjectsInRealm:realm].count);
if (self.isParent) {
RLMRunChildAndWait();
XCTAssertEqual(0U, [IntObject allObjectsInRealm:realm].count);
[realm refresh];
XCTAssertEqual(1U, [IntObject allObjectsInRealm:realm].count);
}
else {
[realm beginWriteTransaction];
[IntObject createInRealm:realm withValue:@[@0]];
[realm commitWriteTransaction];
}
}
- (void)testOpenInParentThenAddObjectInChildWithNoChanceToAutorefresh {
RLMRealm *realm = [RLMRealm defaultRealm];
XCTAssertEqual(0U, [IntObject allObjectsInRealm:realm].count);
if (self.isParent) {
// Wait on a different thread so that this thread doesn't get the chance
// to autorefresh
[self dispatchAsyncAndWait:^{
RLMRunChildAndWait();
}];
XCTAssertEqual(0U, [IntObject allObjectsInRealm:realm].count);
[realm refresh];
XCTAssertEqual(1U, [IntObject allObjectsInRealm:realm].count);
}
else {
[realm beginWriteTransaction];
[IntObject createInRealm:realm withValue:@[@0]];
[realm commitWriteTransaction];
}
}
- (void)testChangeInChildTriggersNotificationInParent {
RLMRealm *realm = [RLMRealm defaultRealm];
XCTAssertEqual(0U, [IntObject allObjectsInRealm:realm].count);
if (self.isParent) {
[self waitForNotification:RLMRealmDidChangeNotification realm:realm block:^{
RLMRunChildAndWait();
}];
XCTAssertEqual(1U, [IntObject allObjectsInRealm:realm].count);
}
else {
[realm beginWriteTransaction];
[IntObject createInRealm:realm withValue:@[@0]];
[realm commitWriteTransaction];
}
}
- (void)testBackgroundProcessDoesNotTriggerSpuriousNotifications {
RLMRealm *realm = [RLMRealm defaultRealm];
RLMNotificationToken *token = [realm addNotificationBlock:^(__unused RLMNotification notification, __unused RLMRealm *realm) {
XCTFail(@"Notification should not have been triggered");
}];
if (self.isParent) {
RLMRunChildAndWait();
}
else {
// Just a meaningless thing that reads from the realm
XCTAssertEqual(0U, [IntObject allObjectsInRealm:realm].count);
}
[token invalidate];
}
// FIXME: Re-enable this test when it can be made to pass reliably.
- (void)DISABLED_testShareInMemoryRealm {
RLMRealm *realm = [self inMemoryRealmWithIdentifier:@"test"];
XCTAssertEqual(0U, [IntObject allObjectsInRealm:realm].count);
if (self.isParent) {
[self waitForNotification:RLMRealmDidChangeNotification realm:realm block:^{
RLMRunChildAndWait();
}];
XCTAssertEqual(1U, [IntObject allObjectsInRealm:realm].count);
}
else {
[realm beginWriteTransaction];
[IntObject createInRealm:realm withValue:@[@0]];
[realm commitWriteTransaction];
}
}
- (void)testBidirectionalCommunication {
const int stopValue = 100;
RLMRealm *realm = [self inMemoryRealmWithIdentifier:@"test"];
[realm beginWriteTransaction];
IntObject *obj = [IntObject allObjectsInRealm:realm].firstObject;
if (!obj) {
obj = [IntObject createInRealm:realm withValue:@[@0]];
[realm commitWriteTransaction];
}
else {
[realm cancelWriteTransaction];
}
RLMNotificationToken *token = [realm addNotificationBlock:^(__unused NSString *note, __unused RLMRealm *realm) {
if (obj.intCol % 2 == self.isParent && obj.intCol < stopValue) {
[realm transactionWithBlock:^{
obj.intCol++;
}];
}
}];
if (self.isParent) {
dispatch_queue_t queue = dispatch_queue_create("background", 0);
dispatch_async(queue, ^{ RLMRunChildAndWait(); });
while (obj.intCol < stopValue) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
dispatch_sync(queue, ^{});
}
else {
[realm transactionWithBlock:^{
obj.intCol++;
}];
while (obj.intCol < stopValue) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
}
[token invalidate];
}
- (void)testManyWriters {
const int stopValue = 100;
const int workers = 10;
const RLMRealm *realm = RLMRealm.defaultRealm;
if (self.isParent) {
[realm beginWriteTransaction];
IntObject *obj = [IntObject createInDefaultRealmWithValue:@[@(-workers)]];
[realm commitWriteTransaction];
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < workers; ++i) {
dispatch_async(queue, ^{ RLMRunChildAndWait(); });
}
dispatch_barrier_sync(queue, ^{});
[realm refresh];
XCTAssertEqual(stopValue, obj.intCol);
XCTAssertEqual(stopValue, [DoubleObject allObjects].count);
XCTAssertEqual(stopValue / 2 + 1, [[DoubleObject.allObjects minOfProperty:@"doubleCol"] intValue]);
return;
}
// Run the run loop until someone else makes a commit
dispatch_block_t waitForExternalChange = ^{
RLMNotificationToken *token = [realm addNotificationBlock:^(__unused NSString *note, __unused RLMRealm *realm) {
CFRunLoopStop(CFRunLoopGetCurrent());
}];
CFRunLoopRun();
[token invalidate];
};
IntObject *obj = [IntObject allObjects].firstObject;
int nextRun = -1;
// Wait for all of the workers to start up
while (obj.intCol < 0) {
if (nextRun == -1) {
[realm beginWriteTransaction];
++obj.intCol;
[realm commitWriteTransaction];
nextRun = 0;
}
waitForExternalChange();
}
while (true) {
// Wait for someone else to run if it's not our turn yet
if (obj.intCol < nextRun && nextRun < 100) {
waitForExternalChange();
continue;
}
[realm beginWriteTransaction];
if (obj.intCol == stopValue) {
[realm commitWriteTransaction];
break;
}
++obj.intCol;
// Do some stuff
[DoubleObject createInDefaultRealmWithValue:@[@(obj.intCol)]];
[DoubleObject createInDefaultRealmWithValue:@[@(obj.intCol)]];
RLMResults *min = [DoubleObject objectsWhere:@"doubleCol = %@", [DoubleObject.allObjects minOfProperty:@"doubleCol"]];
[realm deleteObject:min.firstObject];
[realm commitWriteTransaction];
// Wait for a random number of other workers to do some work to avoid
// having a strict order that processes run in and to avoid having a
// single process do everything
nextRun = obj.intCol + arc4random() % 10;
}
}
- (void)testRecoverAfterCrash {
if (self.isParent) {
[self runChildAndWait];
RLMRealm *realm = RLMRealm.defaultRealm;
[realm beginWriteTransaction];
[IntObject createInRealm:realm withValue:@[@0]];
[realm commitWriteTransaction];
XCTAssertEqual(1U, [IntObject allObjects].count);
}
else {
RLMRealm *realm = RLMRealm.defaultRealm;
[realm beginWriteTransaction];
_Exit(1);
}
}
- (void)testRecoverAfterCrashWithFileAlreadyOpen {
if (self.isParent) {
RLMRealm *realm = RLMRealm.defaultRealm;
[self runChildAndWait];
[realm beginWriteTransaction];
[IntObject createInRealm:realm withValue:@[@0]];
[realm commitWriteTransaction];
XCTAssertEqual(1U, [IntObject allObjects].count);
}
else {
RLMRealm *realm = RLMRealm.defaultRealm;
[realm beginWriteTransaction];
_Exit(1);
}
}
- (void)testCanOpenAndReadWhileOtherProcessHoldsWriteLock {
RLMRealm *realm = RLMRealm.defaultRealm;
if (self.isParent) {
[realm beginWriteTransaction];
RLMRunChildAndWait();
[realm commitWriteTransaction];
}
else {
XCTAssertEqual(0U, [IntObject allObjectsInRealm:realm].count);
}
}
@end