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.
262 lines
10 KiB
262 lines
10 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 "RLMTestCase.h"
|
|
|
|
@interface LinkingObjectsTests : RLMTestCase
|
|
@end
|
|
|
|
@implementation LinkingObjectsTests
|
|
|
|
- (void)testBasics {
|
|
NSArray *(^asArray)(id) = ^(id arrayLike) {
|
|
return [arrayLike valueForKeyPath:@"self"];
|
|
};
|
|
|
|
RLMRealm *realm = [self realmWithTestPath];
|
|
[realm beginWriteTransaction];
|
|
|
|
PersonObject *hannah = [PersonObject createInRealm:realm withValue:@[ @"Hannah", @0 ]];
|
|
PersonObject *mark = [PersonObject createInRealm:realm withValue:@[ @"Mark", @30, @[ hannah ]]];
|
|
|
|
RLMLinkingObjects *hannahsParents = hannah.parents;
|
|
XCTAssertEqualObjects(asArray(hannahsParents), (@[ mark ]));
|
|
|
|
[realm commitWriteTransaction];
|
|
|
|
XCTAssertEqualObjects(asArray(hannahsParents), (@[ mark ]));
|
|
|
|
[realm beginWriteTransaction];
|
|
PersonObject *diane = [PersonObject createInRealm:realm withValue:@[ @"Diane", @29, @[ hannah ]]];
|
|
[realm commitWriteTransaction];
|
|
|
|
XCTAssertEqualObjects(asArray(hannahsParents), (@[ mark, diane ]));
|
|
|
|
[realm beginWriteTransaction];
|
|
[realm deleteObject:hannah];
|
|
[realm commitWriteTransaction];
|
|
XCTAssertEqualObjects(asArray(hannahsParents), (@[ ]));
|
|
}
|
|
|
|
- (void)testLinkingObjectsOnUnmanagedObject {
|
|
PersonObject *don = [[PersonObject alloc] initWithValue:@[ @"Don", @60, @[] ]];
|
|
|
|
XCTAssertEqual(0u, don.parents.count);
|
|
XCTAssertNil(don.parents.firstObject);
|
|
XCTAssertNil(don.parents.lastObject);
|
|
|
|
for (__unused id parent in don.parents) {
|
|
XCTFail(@"Got an item in empty linking objects");
|
|
}
|
|
|
|
XCTAssertEqual(0u, [don.parents sortedResultsUsingKeyPath:@"age" ascending:YES].count);
|
|
XCTAssertEqual(0u, [don.parents objectsWhere:@"TRUEPREDICATE"].count);
|
|
|
|
XCTAssertNil([don.parents minOfProperty:@"age"]);
|
|
XCTAssertNil([don.parents maxOfProperty:@"age"]);
|
|
XCTAssertEqualObjects(@0, [don.parents sumOfProperty:@"age"]);
|
|
XCTAssertNil([don.parents averageOfProperty:@"age"]);
|
|
|
|
XCTAssertEqualObjects(@[], [don.parents valueForKey:@"age"]);
|
|
XCTAssertEqualObjects(@0, [don.parents valueForKeyPath:@"@count"]);
|
|
XCTAssertNil([don.parents valueForKeyPath:@"@min.age"]);
|
|
XCTAssertNil([don.parents valueForKeyPath:@"@max.age"]);
|
|
XCTAssertEqualObjects(@0, [don.parents valueForKeyPath:@"@sum.age"]);
|
|
XCTAssertNil([don.parents valueForKeyPath:@"@avg.age"]);
|
|
|
|
PersonObject *mark = [[PersonObject alloc] initWithValue:@[ @"Mark", @30, @[] ]];
|
|
XCTAssertEqual(NSNotFound, [don.parents indexOfObject:mark]);
|
|
XCTAssertEqual(NSNotFound, [don.parents indexOfObjectWhere:@"TRUEPREDICATE"]);
|
|
}
|
|
|
|
- (void)testFilteredLinkingObjects {
|
|
NSArray *(^asArray)(id) = ^(id arrayLike) {
|
|
return [arrayLike valueForKeyPath:@"self"];
|
|
};
|
|
|
|
RLMRealm *realm = [self realmWithTestPath];
|
|
[realm beginWriteTransaction];
|
|
|
|
PersonObject *hannah = [PersonObject createInRealm:realm withValue:@[ @"Hannah", @0 ]];
|
|
PersonObject *mark = [PersonObject createInRealm:realm withValue:@[ @"Mark", @30, @[ hannah ]]];
|
|
PersonObject *diane = [PersonObject createInRealm:realm withValue:@[ @"Diane", @29, @[ hannah ]]];
|
|
|
|
RLMLinkingObjects *hannahsParents = hannah.parents;
|
|
|
|
// Three separate queries so that accessing a property on one doesn't invalidate testing of other properties.
|
|
RLMResults *resultsA = [hannahsParents objectsWhere:@"age > 25"];
|
|
RLMResults *resultsB = [hannahsParents objectsWhere:@"age > 25"];
|
|
RLMResults *resultsC = [hannahsParents objectsWhere:@"age > 25"];
|
|
|
|
[mark.children removeAllObjects];
|
|
[realm commitWriteTransaction];
|
|
|
|
XCTAssertEqual(resultsA.count, 1u);
|
|
XCTAssertEqual(NSNotFound, [resultsB indexOfObjectWhere:@"name = 'Mark'"]);
|
|
XCTAssertEqualObjects(asArray(resultsC), (@[ diane ]));
|
|
}
|
|
|
|
- (void)testNotificationSentInitially {
|
|
RLMRealm *realm = [self realmWithTestPath];
|
|
[realm beginWriteTransaction];
|
|
|
|
PersonObject *hannah = [PersonObject createInRealm:realm withValue:@[ @"Hannah", @0 ]];
|
|
PersonObject *mark = [PersonObject createInRealm:realm withValue:@[ @"Mark", @30, @[ hannah ]]];
|
|
|
|
[realm commitWriteTransaction];
|
|
|
|
id expectation = [self expectationWithDescription:@""];
|
|
RLMNotificationToken *token = [hannah.parents addNotificationBlock:^(RLMResults *linkingObjects, RLMCollectionChange *change, NSError *error) {
|
|
XCTAssertEqualObjects([linkingObjects valueForKeyPath:@"self"], (@[ mark ]));
|
|
XCTAssertNil(change);
|
|
XCTAssertNil(error);
|
|
[expectation fulfill];
|
|
}];
|
|
|
|
[self waitForExpectationsWithTimeout:2.0 handler:nil];
|
|
[token invalidate];
|
|
}
|
|
|
|
- (void)testNotificationSentAfterCommit {
|
|
RLMRealm *realm = self.realmWithTestPath;
|
|
[realm beginWriteTransaction];
|
|
PersonObject *hannah = [PersonObject createInRealm:realm withValue:@[ @"Hannah", @0 ]];
|
|
[realm commitWriteTransaction];
|
|
|
|
__block bool first = true;
|
|
__block id expectation = [self expectationWithDescription:@""];
|
|
RLMNotificationToken *token = [hannah.parents addNotificationBlock:^(RLMResults *linkingObjects, RLMCollectionChange *change, NSError *error) {
|
|
XCTAssertNotNil(linkingObjects);
|
|
XCTAssert(first ? !change : !!change);
|
|
XCTAssertNil(error);
|
|
first = false;
|
|
[expectation fulfill];
|
|
}];
|
|
[self waitForExpectationsWithTimeout:2.0 handler:nil];
|
|
|
|
expectation = [self expectationWithDescription:@""];
|
|
[self dispatchAsyncAndWait:^{
|
|
RLMRealm *realm = self.realmWithTestPath;
|
|
[realm transactionWithBlock:^{
|
|
[PersonObject createInRealm:realm withValue:@[ @"Mark", @30, [PersonObject objectsInRealm:realm where:@"name == 'Hannah'"] ]];
|
|
}];
|
|
}];
|
|
[self waitForExpectationsWithTimeout:2.0 handler:nil];
|
|
|
|
[token invalidate];
|
|
}
|
|
|
|
- (void)testNotificationNotSentForUnrelatedChange {
|
|
RLMRealm *realm = self.realmWithTestPath;
|
|
[realm beginWriteTransaction];
|
|
PersonObject *hannah = [PersonObject createInRealm:realm withValue:@[ @"Hannah", @0 ]];
|
|
[realm commitWriteTransaction];
|
|
|
|
id expectation = [self expectationWithDescription:@""];
|
|
RLMNotificationToken *token = [hannah.parents addNotificationBlock:^(RLMResults *, RLMCollectionChange *, NSError *) {
|
|
// will throw if it's incorrectly called a second time due to the
|
|
// unrelated write transaction
|
|
[expectation fulfill];
|
|
}];
|
|
[self waitForExpectationsWithTimeout:2.0 handler:nil];
|
|
|
|
// All notification blocks are called as part of a single runloop event, so
|
|
// waiting for this one also waits for the above one to get a chance to run
|
|
[self waitForNotification:RLMRealmDidChangeNotification realm:realm block:^{
|
|
[self dispatchAsyncAndWait:^{
|
|
[self.realmWithTestPath transactionWithBlock:^{ }];
|
|
}];
|
|
}];
|
|
[token invalidate];
|
|
}
|
|
|
|
- (void)testNotificationSentOnlyForActualRefresh {
|
|
RLMRealm *realm = self.realmWithTestPath;
|
|
[realm beginWriteTransaction];
|
|
PersonObject *hannah = [PersonObject createInRealm:realm withValue:@[ @"Hannah", @0 ]];
|
|
[realm commitWriteTransaction];
|
|
|
|
__block id expectation = [self expectationWithDescription:@""];
|
|
RLMNotificationToken *token = [hannah.parents addNotificationBlock:^(RLMResults *linkingObjects, RLMCollectionChange *, NSError *error) {
|
|
XCTAssertNotNil(linkingObjects);
|
|
XCTAssertNil(error);
|
|
// will throw if it's called a second time before we create the new
|
|
// expectation object immediately before manually refreshing
|
|
[expectation fulfill];
|
|
}];
|
|
[self waitForExpectationsWithTimeout:2.0 handler:nil];
|
|
|
|
// Turn off autorefresh, so the background commit should not result in a notification
|
|
realm.autorefresh = NO;
|
|
|
|
// All notification blocks are called as part of a single runloop event, so
|
|
// waiting for this one also waits for the above one to get a chance to run
|
|
[self waitForNotification:RLMRealmRefreshRequiredNotification realm:realm block:^{
|
|
[self dispatchAsyncAndWait:^{
|
|
RLMRealm *realm = self.realmWithTestPath;
|
|
[realm transactionWithBlock:^{
|
|
[PersonObject createInRealm:realm withValue:@[ @"Mark", @30, [PersonObject objectsInRealm:realm where:@"name == 'Hannah'"] ]];
|
|
}];
|
|
}];
|
|
}];
|
|
|
|
expectation = [self expectationWithDescription:@""];
|
|
[realm refresh];
|
|
[self waitForExpectationsWithTimeout:2.0 handler:nil];
|
|
|
|
[token invalidate];
|
|
}
|
|
|
|
- (void)testDeletingObjectWithNotificationsRegistered {
|
|
RLMRealm *realm = self.realmWithTestPath;
|
|
[realm beginWriteTransaction];
|
|
PersonObject *hannah = [PersonObject createInRealm:realm withValue:@[ @"Hannah", @0 ]];
|
|
PersonObject *mark = [PersonObject createInRealm:realm withValue:@[ @"Mark", @30, @[ hannah ]]];
|
|
[realm commitWriteTransaction];
|
|
|
|
__block id expectation = [self expectationWithDescription:@""];
|
|
RLMNotificationToken *token = [hannah.parents addNotificationBlock:^(RLMResults *linkingObjects, RLMCollectionChange *, NSError *error) {
|
|
XCTAssertNotNil(linkingObjects);
|
|
XCTAssertNil(error);
|
|
[expectation fulfill];
|
|
}];
|
|
[self waitForExpectationsWithTimeout:2.0 handler:nil];
|
|
|
|
[realm beginWriteTransaction];
|
|
[realm deleteObject:mark];
|
|
[realm commitWriteTransaction];
|
|
|
|
[token invalidate];
|
|
}
|
|
|
|
- (void)testRenamedProperties {
|
|
RLMRealm *realm = self.realmWithTestPath;
|
|
[realm beginWriteTransaction];
|
|
auto obj1 = [RenamedProperties1 createInRealm:realm withValue:@[@1, @"a"]];
|
|
auto obj2 = [RenamedProperties2 createInRealm:realm withValue:@[@2, @"b"]];
|
|
auto link = [LinkToRenamedProperties1 createInRealm:realm withValue:@[obj1, obj2, @[obj1, obj1]]];
|
|
[realm commitWriteTransaction];
|
|
|
|
XCTAssertEqualObjects(obj1.linking1.objectClassName, @"LinkToRenamedProperties1");
|
|
XCTAssertEqualObjects(obj1.linking2.objectClassName, @"LinkToRenamedProperties2");
|
|
|
|
XCTAssertTrue([obj1.linking1[0] isEqualToObject:link]);
|
|
XCTAssertTrue([obj2.linking2[0] isEqualToObject:link]);
|
|
}
|
|
|
|
@end
|
|
|