123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298 |
- ////////////////////////////////////////////////////////////////////////////
- //
- // 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"]);
- RLMAssertThrowsWithReason(([don.parents addNotificationBlock:^(RLMResults *, RLMCollectionChange *, NSError *) { }]),
- @"Linking objects notifications are only supported on managed objects.");
- }
- - (void)testLinkingObjectsOnFrozenObject {
- 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]]];
- [realm commitWriteTransaction];
- PersonObject *frozenHannah = hannah.freeze;
- PersonObject *frozenMark = mark.freeze;
- XCTAssertEqualObjects(asArray(frozenHannah.parents), (@[frozenMark]));
- [realm beginWriteTransaction];
- PersonObject *diane = [PersonObject createInRealm:realm withValue:@[@"Diane", @29, @[hannah]]];
- [realm commitWriteTransaction];
- PersonObject *frozenHannah2 = hannah.freeze;
- PersonObject *frozenMark2 = mark.freeze;
- PersonObject *frozenDiane = diane.freeze;
- XCTAssertEqualObjects(asArray(frozenHannah.parents), (@[frozenMark]));
- XCTAssertEqualObjects(asArray(frozenHannah2.parents), (@[frozenMark2, frozenDiane]));
- [realm beginWriteTransaction];
- [realm deleteObject:hannah];
- [realm commitWriteTransaction];
- XCTAssertEqualObjects(asArray(frozenHannah.parents), (@[frozenMark]));
- XCTAssertEqualObjects(asArray(frozenHannah2.parents), (@[frozenMark2, frozenDiane]));
- }
- - (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
|