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.
1123 lines
50 KiB
1123 lines
50 KiB
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright 2014 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"
|
|
|
|
#import <mach/mach.h>
|
|
#import <objc/runtime.h>
|
|
|
|
@interface ResultsTests : RLMTestCase
|
|
@end
|
|
|
|
@implementation ResultsTests
|
|
|
|
- (void)testFastEnumeration
|
|
{
|
|
RLMRealm *realm = self.realmWithTestPath;
|
|
|
|
// enumerate empty array
|
|
for (__unused id obj in [AggregateObject allObjectsInRealm:realm]) {
|
|
XCTFail(@"Should be empty");
|
|
}
|
|
|
|
[realm beginWriteTransaction];
|
|
for (int i = 0; i < 18; ++i) {
|
|
[AggregateObject createInRealm:realm withValue:@[@10, @1.2f, @0.0, @YES, NSDate.date]];
|
|
}
|
|
[realm commitWriteTransaction];
|
|
|
|
RLMResults *result = [AggregateObject objectsInRealm:realm where:@"intCol < %i", 100];
|
|
XCTAssertEqual(result.count, 18U);
|
|
|
|
__weak id objects[18];
|
|
NSInteger count = 0;
|
|
for (AggregateObject *ao in result) {
|
|
XCTAssertNotNil(ao, @"Object is not nil and accessible");
|
|
if (count > 16) {
|
|
// 16 is the size of blocks fast enumeration happens to ask for at
|
|
// the moment, but of course that's just an implementation detail
|
|
// that may change
|
|
XCTAssertNil(objects[count - 16]);
|
|
}
|
|
objects[count++] = ao;
|
|
}
|
|
|
|
XCTAssertEqual(count, 18, @"should have enumerated 18 objects");
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
XCTAssertNil(objects[i], @"Object should have been released");
|
|
}
|
|
|
|
@autoreleasepool {
|
|
for (AggregateObject *ao in result) {
|
|
objects[0] = ao;
|
|
break;
|
|
}
|
|
}
|
|
XCTAssertNil(objects[0], @"Object should have been released");
|
|
}
|
|
|
|
- (void)testFirst {
|
|
XCTAssertNil(IntObject.allObjects.firstObject);
|
|
XCTAssertNil([IntObject objectsWhere:@"intCol > 5"].firstObject);
|
|
|
|
RLMRealm *realm = [RLMRealm defaultRealm];
|
|
[realm beginWriteTransaction];
|
|
[IntObject createInDefaultRealmWithValue:@[@10]];
|
|
[IntObject createInDefaultRealmWithValue:@[@20]];
|
|
[realm commitWriteTransaction];
|
|
|
|
XCTAssertEqual(10, [IntObject.allObjects.firstObject intCol]);
|
|
XCTAssertEqual(10, [[IntObject objectsWhere:@"intCol > 5"].firstObject intCol]);
|
|
XCTAssertEqual(20, [[IntObject objectsWhere:@"intCol > 10"].firstObject intCol]);
|
|
}
|
|
|
|
- (void)testLast {
|
|
XCTAssertNil(IntObject.allObjects.lastObject);
|
|
XCTAssertNil([IntObject objectsWhere:@"intCol > 5"].lastObject);
|
|
|
|
RLMRealm *realm = [RLMRealm defaultRealm];
|
|
[realm beginWriteTransaction];
|
|
[IntObject createInDefaultRealmWithValue:@[@20]];
|
|
[IntObject createInDefaultRealmWithValue:@[@10]];
|
|
[realm commitWriteTransaction];
|
|
|
|
XCTAssertEqual(10, [IntObject.allObjects.lastObject intCol]);
|
|
XCTAssertEqual(10, [[IntObject objectsWhere:@"intCol > 5"].lastObject intCol]);
|
|
XCTAssertEqual(20, [[IntObject objectsWhere:@"intCol > 10"].lastObject intCol]);
|
|
}
|
|
|
|
- (void)testSubscript {
|
|
RLMAssertThrowsWithReasonMatching([IntObject allObjects][0], @"0.*less than 0");
|
|
RLMAssertThrowsWithReasonMatching([IntObject objectsWhere:@"intCol > 5"][0], @"0.*less than 0");
|
|
|
|
RLMRealm *realm = [RLMRealm defaultRealm];
|
|
[realm beginWriteTransaction];
|
|
[IntObject createInDefaultRealmWithValue:@[@10]];
|
|
[IntObject createInDefaultRealmWithValue:@[@20]];
|
|
[realm commitWriteTransaction];
|
|
|
|
XCTAssertEqual(10, [IntObject.allObjects[0] intCol]);
|
|
XCTAssertEqual(10, [[IntObject objectsWhere:@"intCol > 5"][0] intCol]);
|
|
XCTAssertEqual(20, [[IntObject objectsWhere:@"intCol > 10"][0] intCol]);
|
|
|
|
RLMAssertThrowsWithReasonMatching([IntObject allObjects][2], @"2.*less than 2");
|
|
RLMAssertThrowsWithReasonMatching([IntObject objectsWhere:@"intCol > 5"][2], @"2.*less than 2");
|
|
}
|
|
|
|
- (void)testValueForKey {
|
|
RLMRealm *realm = self.realmWithTestPath;
|
|
|
|
[realm beginWriteTransaction];
|
|
|
|
XCTAssertEqualObjects([[AggregateObject allObjectsInRealm:realm] valueForKey:@"intCol"], @[]);
|
|
|
|
NSDate *dateMinInput = [NSDate date];
|
|
NSDate *dateMaxInput = [dateMinInput dateByAddingTimeInterval:1000];
|
|
|
|
[AggregateObject createInRealm:realm withValue:@[@0, @1.2f, @0.0, @YES, dateMinInput]];
|
|
[AggregateObject createInRealm:realm withValue:@[@1, @0.0f, @2.5, @NO, dateMaxInput]];
|
|
[AggregateObject createInRealm:realm withValue:@[@0, @1.2f, @0.0, @YES, dateMinInput]];
|
|
[AggregateObject createInRealm:realm withValue:@[@1, @0.0f, @2.5, @NO, dateMaxInput]];
|
|
[AggregateObject createInRealm:realm withValue:@[@0, @1.2f, @0.0, @YES, dateMinInput]];
|
|
[AggregateObject createInRealm:realm withValue:@[@1, @0.0f, @2.5, @NO, dateMaxInput]];
|
|
[AggregateObject createInRealm:realm withValue:@[@0, @1.2f, @0.0, @YES, dateMinInput]];
|
|
[AggregateObject createInRealm:realm withValue:@[@1, @0.0f, @2.5, @NO, dateMaxInput]];
|
|
[AggregateObject createInRealm:realm withValue:@[@0, @1.2f, @0.0, @YES, dateMinInput]];
|
|
[AggregateObject createInRealm:realm withValue:@[@0, @1.2f, @0.0, @YES, dateMinInput]];
|
|
|
|
XCTAssertEqualObjects([[AggregateObject allObjectsInRealm:realm] valueForKey:@"intCol"],
|
|
(@[@0, @1, @0, @1, @0, @1, @0, @1, @0, @0]));
|
|
XCTAssertTrue([[[[AggregateObject allObjectsInRealm:realm] valueForKey:@"self"] firstObject]
|
|
isEqualToObject:[AggregateObject allObjectsInRealm:realm].firstObject]);
|
|
XCTAssertTrue([[[[AggregateObject allObjectsInRealm:realm] valueForKey:@"self"] lastObject]
|
|
isEqualToObject:[AggregateObject allObjectsInRealm:realm].lastObject]);
|
|
|
|
XCTAssertEqualObjects([[AggregateObject objectsInRealm:realm where:@"intCol != 1"] valueForKey:@"intCol"],
|
|
(@[@0, @0, @0, @0, @0, @0]));
|
|
XCTAssertTrue([[[[AggregateObject objectsInRealm:realm where:@"intCol != 1"] valueForKey:@"self"] firstObject]
|
|
isEqualToObject:[AggregateObject objectsInRealm:realm where:@"intCol != 1"].firstObject]);
|
|
XCTAssertTrue([[[[AggregateObject objectsInRealm:realm where:@"intCol != 1"] valueForKey:@"self"] lastObject]
|
|
isEqualToObject:[AggregateObject objectsInRealm:realm where:@"intCol != 1"].lastObject]);
|
|
|
|
[realm commitWriteTransaction];
|
|
|
|
XCTAssertEqualObjects([[AggregateObject allObjectsInRealm:realm] valueForKey:@"intCol"],
|
|
(@[@0, @1, @0, @1, @0, @1, @0, @1, @0, @0]));
|
|
XCTAssertEqualObjects([[AggregateObject objectsInRealm:realm where:@"intCol != 1"] valueForKey:@"intCol"],
|
|
(@[@0, @0, @0, @0, @0, @0]));
|
|
|
|
XCTAssertThrows([[AggregateObject allObjectsInRealm:realm] valueForKey:@"invalid"]);
|
|
}
|
|
|
|
- (void)testSetValueForKey {
|
|
RLMRealm *realm = self.realmWithTestPath;
|
|
|
|
[realm beginWriteTransaction];
|
|
|
|
NSDate *dateMinInput = [NSDate date];
|
|
NSDate *dateMaxInput = [dateMinInput dateByAddingTimeInterval:1000];
|
|
|
|
[AggregateObject createInRealm:realm withValue:@[@0, @1.2f, @0.0, @YES, dateMinInput]];
|
|
[AggregateObject createInRealm:realm withValue:@[@1, @0.0f, @2.5, @NO, dateMaxInput]];
|
|
[AggregateObject createInRealm:realm withValue:@[@0, @1.2f, @0.0, @YES, dateMinInput]];
|
|
[AggregateObject createInRealm:realm withValue:@[@1, @0.0f, @2.5, @NO, dateMaxInput]];
|
|
[AggregateObject createInRealm:realm withValue:@[@0, @1.2f, @0.0, @YES, dateMinInput]];
|
|
[AggregateObject createInRealm:realm withValue:@[@1, @0.0f, @2.5, @NO, dateMaxInput]];
|
|
[AggregateObject createInRealm:realm withValue:@[@0, @1.2f, @0.0, @YES, dateMinInput]];
|
|
[AggregateObject createInRealm:realm withValue:@[@1, @0.0f, @2.5, @NO, dateMaxInput]];
|
|
[AggregateObject createInRealm:realm withValue:@[@0, @1.2f, @0.0, @YES, dateMinInput]];
|
|
[AggregateObject createInRealm:realm withValue:@[@0, @1.2f, @0.0, @YES, dateMinInput]];
|
|
|
|
[[AggregateObject allObjectsInRealm:realm] setValue:@25 forKey:@"intCol"];
|
|
XCTAssertEqualObjects([[AggregateObject allObjectsInRealm:realm] valueForKey:@"intCol"],
|
|
(@[@25, @25, @25, @25, @25, @25, @25, @25, @25, @25]));
|
|
|
|
[[AggregateObject objectsInRealm:realm where:@"floatCol > 1"] setValue:@10 forKey:@"intCol"];
|
|
XCTAssertEqualObjects([[AggregateObject objectsInRealm:realm where:@"floatCol > 1"] valueForKey:@"intCol"],
|
|
(@[@10, @10, @10, @10, @10, @10]));
|
|
|
|
XCTAssertThrows([[AggregateObject allObjectsInRealm:realm] valueForKey:@"invalid"]);
|
|
|
|
[[AggregateObject objectsInRealm:realm where:@"intCol != 5"] setValue:@5 forKey:@"intCol"];
|
|
XCTAssertEqualObjects([[AggregateObject allObjectsInRealm:realm] valueForKey:@"intCol"],
|
|
(@[@5, @5, @5, @5, @5, @5, @5, @5, @5, @5]));
|
|
|
|
[realm commitWriteTransaction];
|
|
|
|
RLMAssertThrowsWithReasonMatching([[AggregateObject allObjectsInRealm:realm] setValue:@25 forKey:@"intCol"],
|
|
@"write transaction");
|
|
RLMAssertThrowsWithReasonMatching([[AggregateObject objectsInRealm:realm where:@"floatCol > 1"] setValue:@10 forKey:@"intCol"],
|
|
@"write transaction");
|
|
}
|
|
|
|
- (void)testObjectAggregate
|
|
{
|
|
RLMRealm *realm = [RLMRealm defaultRealm];
|
|
|
|
RLMResults *noArray = [AggregateObject objectsWhere:@"boolCol == NO"];
|
|
RLMResults *yesArray = [AggregateObject objectsWhere:@"boolCol == YES"];
|
|
RLMResults *allArray = [AggregateObject allObjects];
|
|
|
|
XCTAssertEqual(0, [noArray sumOfProperty:@"intCol"].intValue);
|
|
XCTAssertEqual(0, [allArray sumOfProperty:@"intCol"].intValue);
|
|
|
|
XCTAssertNil([noArray averageOfProperty:@"intCol"]);
|
|
XCTAssertNil([allArray averageOfProperty:@"intCol"]);
|
|
XCTAssertNil([noArray minOfProperty:@"intCol"]);
|
|
XCTAssertNil([allArray minOfProperty:@"intCol"]);
|
|
XCTAssertNil([noArray maxOfProperty:@"intCol"]);
|
|
XCTAssertNil([allArray maxOfProperty:@"intCol"]);
|
|
|
|
[realm beginWriteTransaction];
|
|
|
|
NSDate *dateMinInput = [NSDate date];
|
|
NSDate *dateMaxInput = [dateMinInput dateByAddingTimeInterval:1000];
|
|
|
|
[AggregateObject createInRealm:realm withValue:@[@0, @1.2f, @0.0, @YES, dateMinInput]];
|
|
[AggregateObject createInRealm:realm withValue:@[@1, @0.0f, @2.5, @NO, dateMaxInput]];
|
|
[AggregateObject createInRealm:realm withValue:@[@0, @1.2f, @0.0, @YES, dateMinInput]];
|
|
[AggregateObject createInRealm:realm withValue:@[@1, @0.0f, @2.5, @NO, dateMaxInput]];
|
|
[AggregateObject createInRealm:realm withValue:@[@0, @1.2f, @0.0, @YES, dateMinInput]];
|
|
[AggregateObject createInRealm:realm withValue:@[@1, @0.0f, @2.5, @NO, dateMaxInput]];
|
|
[AggregateObject createInRealm:realm withValue:@[@0, @1.2f, @0.0, @YES, dateMinInput]];
|
|
[AggregateObject createInRealm:realm withValue:@[@1, @0.0f, @2.5, @NO, dateMaxInput]];
|
|
[AggregateObject createInRealm:realm withValue:@[@0, @1.2f, @0.0, @YES, dateMinInput]];
|
|
[AggregateObject createInRealm:realm withValue:@[@0, @1.2f, @0.0, @YES, dateMinInput]];
|
|
|
|
[realm commitWriteTransaction];
|
|
|
|
// SUM ::::::::::::::::::::::::::::::::::::::::::::::
|
|
// Test int sum
|
|
XCTAssertEqual([noArray sumOfProperty:@"intCol"].integerValue, 4);
|
|
XCTAssertEqual([yesArray sumOfProperty:@"intCol"].integerValue, 0);
|
|
XCTAssertEqual([allArray sumOfProperty:@"intCol"].integerValue, 4);
|
|
|
|
// Test float sum
|
|
XCTAssertEqualWithAccuracy([noArray sumOfProperty:@"floatCol"].floatValue, 0.0f, 0.1f);
|
|
XCTAssertEqualWithAccuracy([yesArray sumOfProperty:@"floatCol"].floatValue, 7.2f, 0.1f);
|
|
XCTAssertEqualWithAccuracy([allArray sumOfProperty:@"floatCol"].floatValue, 7.2f, 0.1f);
|
|
|
|
// Test double sum
|
|
XCTAssertEqualWithAccuracy([noArray sumOfProperty:@"doubleCol"].doubleValue, 10.0, 0.1f);
|
|
XCTAssertEqualWithAccuracy([yesArray sumOfProperty:@"doubleCol"].doubleValue, 0.0, 0.1f);
|
|
XCTAssertEqualWithAccuracy([allArray sumOfProperty:@"doubleCol"].doubleValue, 10.0, 0.1f);
|
|
|
|
// Test invalid column name
|
|
RLMAssertThrowsWithReasonMatching([yesArray sumOfProperty:@"foo"], @"foo.*AggregateObject");
|
|
RLMAssertThrowsWithReasonMatching([allArray sumOfProperty:@"foo"], @"foo.*AggregateObject");
|
|
|
|
// Test operation not supported
|
|
RLMAssertThrowsWithReasonMatching([yesArray sumOfProperty:@"boolCol"], @"sum.*bool");
|
|
RLMAssertThrowsWithReasonMatching([allArray sumOfProperty:@"boolCol"], @"sum.*bool");
|
|
|
|
|
|
// Average ::::::::::::::::::::::::::::::::::::::::::::::
|
|
// Test int average
|
|
XCTAssertEqualWithAccuracy([noArray averageOfProperty:@"intCol"].doubleValue, 1.0, 0.1f);
|
|
XCTAssertEqualWithAccuracy([yesArray averageOfProperty:@"intCol"].doubleValue, 0.0, 0.1f);
|
|
XCTAssertEqualWithAccuracy([allArray averageOfProperty:@"intCol"].doubleValue, 0.4, 0.1f);
|
|
|
|
// Test float average
|
|
XCTAssertEqualWithAccuracy([noArray averageOfProperty:@"floatCol"].doubleValue, 0.0, 0.1f);
|
|
XCTAssertEqualWithAccuracy([yesArray averageOfProperty:@"floatCol"].doubleValue, 1.2, 0.1f);
|
|
XCTAssertEqualWithAccuracy([allArray averageOfProperty:@"floatCol"].doubleValue, 0.72, 0.1f);
|
|
|
|
// Test double average
|
|
XCTAssertEqualWithAccuracy([noArray averageOfProperty:@"doubleCol"].doubleValue, 2.5, 0.1f);
|
|
XCTAssertEqualWithAccuracy([yesArray averageOfProperty:@"doubleCol"].doubleValue, 0.0, 0.1f);
|
|
XCTAssertEqualWithAccuracy([allArray averageOfProperty:@"doubleCol"].doubleValue, 1.0, 0.1f);
|
|
|
|
// Test invalid column name
|
|
RLMAssertThrowsWithReasonMatching([yesArray averageOfProperty:@"foo"], @"foo.*AggregateObject");
|
|
RLMAssertThrowsWithReasonMatching([allArray averageOfProperty:@"foo"], @"foo.*AggregateObject");
|
|
|
|
// Test operation not supported
|
|
RLMAssertThrowsWithReasonMatching([yesArray averageOfProperty:@"boolCol"], @"average.*bool");
|
|
RLMAssertThrowsWithReasonMatching([allArray averageOfProperty:@"boolCol"], @"average.*bool");
|
|
|
|
// MIN ::::::::::::::::::::::::::::::::::::::::::::::
|
|
// Test int min
|
|
XCTAssertEqual(1, [[noArray minOfProperty:@"intCol"] intValue]);
|
|
XCTAssertEqual(0, [[yesArray minOfProperty:@"intCol"] intValue]);
|
|
XCTAssertEqual(0, [[allArray minOfProperty:@"intCol"] intValue]);
|
|
|
|
// Test float min
|
|
XCTAssertEqual(0.0f, [[noArray minOfProperty:@"floatCol"] floatValue]);
|
|
XCTAssertEqual(1.2f, [[yesArray minOfProperty:@"floatCol"] floatValue]);
|
|
XCTAssertEqual(0.0f, [[allArray minOfProperty:@"floatCol"] floatValue]);
|
|
|
|
// Test double min
|
|
XCTAssertEqual(2.5, [[noArray minOfProperty:@"doubleCol"] doubleValue]);
|
|
XCTAssertEqual(0.0, [[yesArray minOfProperty:@"doubleCol"] doubleValue]);
|
|
XCTAssertEqual(0.0, [[allArray minOfProperty:@"doubleCol"] doubleValue]);
|
|
|
|
// Test date min
|
|
XCTAssertEqualObjects(dateMaxInput, [noArray minOfProperty:@"dateCol"]);
|
|
XCTAssertEqualObjects(dateMinInput, [yesArray minOfProperty:@"dateCol"]);
|
|
XCTAssertEqualObjects(dateMinInput, [allArray minOfProperty:@"dateCol"]);
|
|
|
|
// Test invalid column name
|
|
RLMAssertThrowsWithReasonMatching([yesArray minOfProperty:@"foo"], @"foo.*AggregateObject");
|
|
RLMAssertThrowsWithReasonMatching([allArray minOfProperty:@"foo"], @"foo.*AggregateObject");
|
|
|
|
// Test operation not supported
|
|
RLMAssertThrowsWithReasonMatching([yesArray minOfProperty:@"boolCol"], @"min.*bool");
|
|
RLMAssertThrowsWithReasonMatching([allArray minOfProperty:@"boolCol"], @"min.*bool");
|
|
|
|
// MAX ::::::::::::::::::::::::::::::::::::::::::::::
|
|
// Test int max
|
|
XCTAssertEqual(1, [[noArray maxOfProperty:@"intCol"] intValue]);
|
|
XCTAssertEqual(0, [[yesArray maxOfProperty:@"intCol"] intValue]);
|
|
XCTAssertEqual(1, [[allArray maxOfProperty:@"intCol"] intValue]);
|
|
|
|
// Test float max
|
|
XCTAssertEqual(0.0f, [[noArray maxOfProperty:@"floatCol"] floatValue]);
|
|
XCTAssertEqual(1.2f, [[yesArray maxOfProperty:@"floatCol"] floatValue]);
|
|
XCTAssertEqual(1.2f, [[allArray maxOfProperty:@"floatCol"] floatValue]);
|
|
|
|
// Test double max
|
|
XCTAssertEqual(2.5, [[noArray maxOfProperty:@"doubleCol"] doubleValue]);
|
|
XCTAssertEqual(0.0, [[yesArray maxOfProperty:@"doubleCol"] doubleValue]);
|
|
XCTAssertEqual(2.5, [[allArray maxOfProperty:@"doubleCol"] doubleValue]);
|
|
|
|
// Test date max
|
|
XCTAssertEqualObjects(dateMaxInput, [noArray maxOfProperty:@"dateCol"]);
|
|
XCTAssertEqualObjects(dateMinInput, [yesArray maxOfProperty:@"dateCol"]);
|
|
XCTAssertEqualObjects(dateMaxInput, [allArray maxOfProperty:@"dateCol"]);
|
|
|
|
// Test invalid column name
|
|
RLMAssertThrowsWithReasonMatching([yesArray maxOfProperty:@"foo"], @"foo.*AggregateObject");
|
|
RLMAssertThrowsWithReasonMatching([allArray maxOfProperty:@"foo"], @"foo.*AggregateObject");
|
|
|
|
// Test operation not supported
|
|
RLMAssertThrowsWithReasonMatching([yesArray maxOfProperty:@"boolCol"], @"max.*bool");
|
|
RLMAssertThrowsWithReasonMatching([allArray maxOfProperty:@"boolCol"], @"max.*bool");
|
|
}
|
|
|
|
- (void)testRenamedPropertyAggregate {
|
|
RLMRealm *realm = [RLMRealm defaultRealm];
|
|
|
|
RLMResults *results = [RenamedProperties1 allObjectsInRealm:realm];
|
|
XCTAssertEqual(0, [results sumOfProperty:@"propA"].intValue);
|
|
XCTAssertNil([results averageOfProperty:@"propA"]);
|
|
XCTAssertNil([results minOfProperty:@"propA"]);
|
|
XCTAssertNil([results maxOfProperty:@"propA"]);
|
|
XCTAssertThrows([results sumOfProperty:@"prop 1"]);
|
|
|
|
[realm transactionWithBlock:^{
|
|
[RenamedProperties1 createInRealm:realm withValue:@[@1, @""]];
|
|
[RenamedProperties1 createInRealm:realm withValue:@[@2, @""]];
|
|
[RenamedProperties1 createInRealm:realm withValue:@[@3, @""]];
|
|
}];
|
|
|
|
XCTAssertEqual(6, [results sumOfProperty:@"propA"].intValue);
|
|
XCTAssertEqual(2.0, [results averageOfProperty:@"propA"].doubleValue);
|
|
XCTAssertEqual(1, [[results minOfProperty:@"propA"] intValue]);
|
|
XCTAssertEqual(3, [[results maxOfProperty:@"propA"] intValue]);
|
|
}
|
|
|
|
|
|
- (void)testValueForCollectionOperationKeyPath {
|
|
RLMRealm *realm = [RLMRealm defaultRealm];
|
|
|
|
[realm beginWriteTransaction];
|
|
EmployeeObject *c1e1 = [EmployeeObject createInRealm:realm withValue:@{@"name": @"Joe", @"age": @40, @"hired": @YES}];
|
|
EmployeeObject *c1e2 = [EmployeeObject createInRealm:realm withValue:@{@"name": @"John", @"age": @30, @"hired": @NO}];
|
|
EmployeeObject *c1e3 = [EmployeeObject createInRealm:realm withValue:@{@"name": @"Jill", @"age": @25, @"hired": @YES}];
|
|
[CompanyObject createInRealm:realm withValue:@{@"name": @"InspiringNames LLC", @"employees": @[c1e1, c1e2, c1e3]}];
|
|
|
|
EmployeeObject *c2e1 = [EmployeeObject createInRealm:realm withValue:@{@"name": @"A", @"age": @20, @"hired": @YES}];
|
|
EmployeeObject *c2e2 = [EmployeeObject createInRealm:realm withValue:@{@"name": @"B", @"age": @30, @"hired": @NO}];
|
|
EmployeeObject *c2e3 = [EmployeeObject createInRealm:realm withValue:@{@"name": @"C", @"age": @40, @"hired": @YES}];
|
|
[CompanyObject createInRealm:realm withValue:@{@"name": @"ABC AG", @"employees": @[c2e1, c2e2, c2e3]}];
|
|
|
|
EmployeeObject *c3e1 = [EmployeeObject createInRealm:realm withValue:@{@"name": @"A", @"age": @21, @"hired": @YES}];
|
|
[CompanyObject createInRealm:realm withValue:@{@"name": @"ABC AG", @"employees": @[c3e1]}];
|
|
[realm commitWriteTransaction];
|
|
|
|
RLMResults *allCompanies = [CompanyObject allObjects];
|
|
RLMResults *allEmployees = [EmployeeObject allObjects];
|
|
|
|
// count operator
|
|
XCTAssertEqual([[allCompanies valueForKeyPath:@"@count"] integerValue], 3);
|
|
|
|
// numeric operators
|
|
XCTAssertEqual([[allEmployees valueForKeyPath:@"@min.age"] intValue], 20);
|
|
XCTAssertEqual([[allEmployees valueForKeyPath:@"@max.age"] intValue], 40);
|
|
XCTAssertEqual([[allEmployees valueForKeyPath:@"@sum.age"] integerValue], 206);
|
|
XCTAssertEqualWithAccuracy([[allEmployees valueForKeyPath:@"@avg.age"] doubleValue], 29.43, 0.1f);
|
|
|
|
// collection
|
|
XCTAssertEqualObjects([allCompanies valueForKeyPath:@"@unionOfObjects.name"],
|
|
(@[@"InspiringNames LLC", @"ABC AG", @"ABC AG"]));
|
|
XCTAssertEqualObjects([allCompanies valueForKeyPath:@"@distinctUnionOfObjects.name"],
|
|
(@[@"ABC AG", @"InspiringNames LLC"]));
|
|
XCTAssertEqualObjects([allCompanies valueForKeyPath:@"employees.@unionOfArrays.name"],
|
|
(@[@"Joe", @"John", @"Jill", @"A", @"B", @"C", @"A"]));
|
|
XCTAssertEqualObjects([[allCompanies valueForKeyPath:@"employees.@distinctUnionOfArrays.name"]
|
|
sortedArrayUsingSelector:@selector(compare:)],
|
|
(@[@"A", @"B", @"C", @"Jill", @"Joe", @"John"]));
|
|
|
|
// invalid key paths
|
|
RLMAssertThrowsWithReasonMatching([allCompanies valueForKeyPath:@"@invalid.name"],
|
|
@"Unsupported KVC collection operator found in key path '@invalid.name'");
|
|
RLMAssertThrowsWithReasonMatching([allCompanies valueForKeyPath:@"@sum"],
|
|
@"Missing key path for KVC collection operator sum in key path '@sum'");
|
|
RLMAssertThrowsWithReasonMatching([allCompanies valueForKeyPath:@"@sum."],
|
|
@"Missing key path for KVC collection operator sum in key path '@sum.'");
|
|
RLMAssertThrowsWithReasonMatching([allCompanies valueForKeyPath:@"@sum.employees.@sum.age"],
|
|
@"Nested key paths.*not supported");
|
|
}
|
|
|
|
- (void)testArrayDescription
|
|
{
|
|
RLMRealm *realm = [RLMRealm defaultRealm];
|
|
|
|
[realm beginWriteTransaction];
|
|
for (NSInteger i = 0; i < 1012; ++i) {
|
|
EmployeeObject *person = [[EmployeeObject alloc] init];
|
|
person.name = @"Mary";
|
|
person.age = 24;
|
|
person.hired = YES;
|
|
[realm addObject:person];
|
|
}
|
|
[realm commitWriteTransaction];
|
|
|
|
NSString *description = [[EmployeeObject allObjects] description];
|
|
|
|
XCTAssertTrue([description rangeOfString:@"name"].location != NSNotFound);
|
|
XCTAssertTrue([description rangeOfString:@"Mary"].location != NSNotFound);
|
|
|
|
XCTAssertTrue([description rangeOfString:@"age"].location != NSNotFound);
|
|
XCTAssertTrue([description rangeOfString:@"24"].location != NSNotFound);
|
|
|
|
XCTAssertTrue([description rangeOfString:@"912 objects skipped"].location != NSNotFound);
|
|
}
|
|
|
|
- (void)testIndexOfObject
|
|
{
|
|
RLMRealm *realm = [RLMRealm defaultRealm];
|
|
|
|
[realm beginWriteTransaction];
|
|
EmployeeObject *po1 = [EmployeeObject createInRealm:realm withValue:@{@"name": @"Joe", @"age": @40, @"hired": @YES}];
|
|
EmployeeObject *po2 = [EmployeeObject createInRealm:realm withValue:@{@"name": @"John", @"age": @30, @"hired": @NO}];
|
|
EmployeeObject *po3 = [EmployeeObject createInRealm:realm withValue:@{@"name": @"Jill", @"age": @25, @"hired": @YES}];
|
|
StringObject *so = [StringObject createInRealm:realm withValue:@[@""]];
|
|
StringObject *deletedObject = [StringObject createInRealm:realm withValue:@[@""]];
|
|
[realm deleteObject:deletedObject];
|
|
[realm commitWriteTransaction];
|
|
|
|
EmployeeObject *unmanaged = [[EmployeeObject alloc] init];
|
|
|
|
RLMResults *results = [EmployeeObject objectsWhere:@"hired = YES"];
|
|
XCTAssertEqual(0U, [results indexOfObject:po1]);
|
|
XCTAssertEqual(1U, [results indexOfObject:po3]);
|
|
XCTAssertEqual((NSUInteger)NSNotFound, [results indexOfObject:po2]);
|
|
XCTAssertEqual((NSUInteger)NSNotFound, [results indexOfObject:unmanaged]);
|
|
RLMAssertThrowsWithReasonMatching([results indexOfObject:so], @"StringObject.*EmployeeObject");
|
|
RLMAssertThrowsWithReasonMatching([results indexOfObject:deletedObject], @"Object has been invalidated");
|
|
|
|
[results lastObject]; // Force to tableview mode
|
|
XCTAssertEqual(0U, [results indexOfObject:po1]);
|
|
XCTAssertEqual(1U, [results indexOfObject:po3]);
|
|
XCTAssertEqual((NSUInteger)NSNotFound, [results indexOfObject:po2]);
|
|
XCTAssertEqual((NSUInteger)NSNotFound, [results indexOfObject:unmanaged]);
|
|
RLMAssertThrowsWithReasonMatching([results indexOfObject:so], @"StringObject.*EmployeeObject");
|
|
RLMAssertThrowsWithReasonMatching([results indexOfObject:deletedObject], @"Object has been invalidated");
|
|
|
|
// reverse order from sort
|
|
results = [[EmployeeObject objectsWhere:@"hired = YES"] sortedResultsUsingKeyPath:@"age" ascending:YES];
|
|
XCTAssertEqual(1U, [results indexOfObject:po1]);
|
|
XCTAssertEqual(0U, [results indexOfObject:po3]);
|
|
XCTAssertEqual((NSUInteger)NSNotFound, [results indexOfObject:po2]);
|
|
XCTAssertEqual((NSUInteger)NSNotFound, [results indexOfObject:unmanaged]);
|
|
RLMAssertThrowsWithReasonMatching([results indexOfObject:so], @"StringObject.*EmployeeObject");
|
|
RLMAssertThrowsWithReasonMatching([results indexOfObject:deletedObject], @"Object has been invalidated");
|
|
|
|
results = [EmployeeObject allObjects];
|
|
XCTAssertEqual(0U, [results indexOfObject:po1]);
|
|
XCTAssertEqual(1U, [results indexOfObject:po2]);
|
|
XCTAssertEqual(2U, [results indexOfObject:po3]);
|
|
XCTAssertEqual((NSUInteger)NSNotFound, [results indexOfObject:unmanaged]);
|
|
RLMAssertThrowsWithReasonMatching([results indexOfObject:so], @"StringObject.*EmployeeObject");
|
|
RLMAssertThrowsWithReasonMatching([results indexOfObject:deletedObject], @"Object has been invalidated");
|
|
}
|
|
|
|
- (void)testIndexOfObjectWhere
|
|
{
|
|
RLMRealm *realm = [RLMRealm defaultRealm];
|
|
|
|
[realm beginWriteTransaction];
|
|
[EmployeeObject createInRealm:realm withValue:@{@"name": @"Joe", @"age": @40, @"hired": @YES}];
|
|
[EmployeeObject createInRealm:realm withValue:@{@"name": @"John", @"age": @30, @"hired": @NO}];
|
|
[EmployeeObject createInRealm:realm withValue:@{@"name": @"Jill", @"age": @25, @"hired": @YES}];
|
|
[EmployeeObject createInRealm:realm withValue:@{@"name": @"Phil", @"age": @38, @"hired": @NO}];
|
|
[realm commitWriteTransaction];
|
|
|
|
RLMResults *results = [EmployeeObject objectsWhere:@"hired = YES"];
|
|
XCTAssertEqual(0U, ([results indexOfObjectWhere:@"age = %d", 40]));
|
|
XCTAssertEqual(1U, ([results indexOfObjectWhere:@"age = %d", 25]));
|
|
XCTAssertEqual((NSUInteger)NSNotFound, ([results indexOfObjectWhere:@"age = %d", 30]));
|
|
|
|
results = [EmployeeObject allObjects];
|
|
XCTAssertEqual(0U, ([results indexOfObjectWhere:@"age = %d", 40]));
|
|
XCTAssertEqual(1U, ([results indexOfObjectWhere:@"age = %d", 30]));
|
|
XCTAssertEqual(2U, ([results indexOfObjectWhere:@"age = %d", 25]));
|
|
XCTAssertEqual((NSUInteger)NSNotFound, ([results indexOfObjectWhere:@"age = %d", 35]));
|
|
|
|
results = [[EmployeeObject allObjects] sortedResultsUsingKeyPath:@"age" ascending:YES];
|
|
NSUInteger youngestHired = [results indexOfObjectWhere:@"hired = YES"];
|
|
XCTAssertEqual(0U, youngestHired);
|
|
XCTAssertEqualObjects(@"Jill", [results[youngestHired] name]);
|
|
NSUInteger youngestNotHired = [results indexOfObjectWhere:@"hired = NO"];
|
|
XCTAssertEqual(1U, youngestNotHired);
|
|
XCTAssertEqualObjects(@"John", [results[youngestNotHired] name]);
|
|
}
|
|
|
|
- (void)testSubqueryLifetime
|
|
{
|
|
RLMRealm *realm = [RLMRealm defaultRealm];
|
|
|
|
[realm beginWriteTransaction];
|
|
[EmployeeObject createInRealm:realm withValue:@{@"name": @"John", @"age": @30, @"hired": @NO}];
|
|
[EmployeeObject createInRealm:realm withValue:@{@"name": @"Jill", @"age": @50, @"hired": @YES}];
|
|
[realm commitWriteTransaction];
|
|
|
|
RLMResults<EmployeeObject *> *subarray = nil;
|
|
{
|
|
__attribute((objc_precise_lifetime)) RLMResults *results = [EmployeeObject objectsWhere:@"hired = YES"];
|
|
subarray = [results objectsWhere:@"age = 40"];
|
|
}
|
|
|
|
[realm beginWriteTransaction];
|
|
[EmployeeObject createInRealm:realm withValue:@{@"name": @"Joe", @"age": @40, @"hired": @YES}];
|
|
[realm commitWriteTransaction];
|
|
|
|
XCTAssertEqualObjects(@"Joe", subarray[0][@"name"]);
|
|
}
|
|
|
|
- (void)testMultiSortLifetime
|
|
{
|
|
RLMRealm *realm = [RLMRealm defaultRealm];
|
|
|
|
[realm beginWriteTransaction];
|
|
[EmployeeObject createInRealm:realm withValue:@{@"name": @"John", @"age": @30, @"hired": @NO}];
|
|
[EmployeeObject createInRealm:realm withValue:@{@"name": @"Jill", @"age": @50, @"hired": @YES}];
|
|
[realm commitWriteTransaction];
|
|
|
|
RLMResults<EmployeeObject *> *subarray = nil;
|
|
{
|
|
__attribute((objc_precise_lifetime)) RLMResults *results = [[EmployeeObject allObjects] sortedResultsUsingKeyPath:@"age" ascending:YES];
|
|
subarray = [results sortedResultsUsingKeyPath:@"age" ascending:NO];
|
|
}
|
|
|
|
[realm beginWriteTransaction];
|
|
[EmployeeObject createInRealm:realm withValue:@{@"name": @"Joe", @"age": @40, @"hired": @YES}];
|
|
[realm commitWriteTransaction];
|
|
|
|
XCTAssertEqual(3U, subarray.count);
|
|
XCTAssertEqualObjects(@"Jill", subarray[0][@"name"]);
|
|
XCTAssertEqualObjects(@"Joe", subarray[1][@"name"]);
|
|
XCTAssertEqualObjects(@"John", subarray[2][@"name"]);
|
|
}
|
|
|
|
- (void)testSortingExistingQuery
|
|
{
|
|
RLMRealm *realm = [RLMRealm defaultRealm];
|
|
|
|
[realm beginWriteTransaction];
|
|
[EmployeeObject createInRealm:realm withValue:@{@"name": @"A", @"age": @20, @"hired": @YES}];
|
|
[EmployeeObject createInRealm:realm withValue:@{@"name": @"B", @"age": @30, @"hired": @NO}];
|
|
[EmployeeObject createInRealm:realm withValue:@{@"name": @"C", @"age": @40, @"hired": @YES}];
|
|
[realm commitWriteTransaction];
|
|
|
|
RLMResults *sortedAge = [[EmployeeObject allObjects] sortedResultsUsingKeyPath:@"age" ascending:YES];
|
|
RLMResults *sortedName = [sortedAge sortedResultsUsingKeyPath:@"name" ascending:NO];
|
|
|
|
XCTAssertEqual(20, [(EmployeeObject *)sortedAge[0] age]);
|
|
XCTAssertEqual(40, [(EmployeeObject *)sortedName[0] age]);
|
|
}
|
|
|
|
- (void)testRerunningSortedQuery {
|
|
RLMRealm *realm = [RLMRealm defaultRealm];
|
|
|
|
RLMResults *sortedAge = [[EmployeeObject allObjects] sortedResultsUsingKeyPath:@"age" ascending:YES];
|
|
[sortedAge lastObject]; // Force creation of the TableView
|
|
RLMResults *sortedName = [sortedAge sortedResultsUsingKeyPath:@"name" ascending:NO];
|
|
[sortedName lastObject]; // Force creation of the TableView
|
|
RLMResults *filtered = [sortedName objectsWhere:@"age > 20"];
|
|
[filtered lastObject]; // Force creation of the TableView
|
|
|
|
[realm beginWriteTransaction];
|
|
[EmployeeObject createInRealm:realm withValue:@{@"name": @"A", @"age": @20, @"hired": @YES}];
|
|
[EmployeeObject createInRealm:realm withValue:@{@"name": @"B", @"age": @30, @"hired": @NO}];
|
|
[EmployeeObject createInRealm:realm withValue:@{@"name": @"C", @"age": @40, @"hired": @YES}];
|
|
[realm commitWriteTransaction];
|
|
|
|
XCTAssertEqual(3U, sortedAge.count);
|
|
XCTAssertEqual(3U, sortedName.count);
|
|
XCTAssertEqual(2U, filtered.count);
|
|
|
|
XCTAssertEqual(20, [(EmployeeObject *)sortedAge[0] age]);
|
|
XCTAssertEqual(30, [(EmployeeObject *)sortedAge[1] age]);
|
|
XCTAssertEqual(40, [(EmployeeObject *)sortedAge[2] age]);
|
|
|
|
XCTAssertEqual(40, [(EmployeeObject *)sortedName[0] age]);
|
|
XCTAssertEqual(30, [(EmployeeObject *)sortedName[1] age]);
|
|
XCTAssertEqual(20, [(EmployeeObject *)sortedName[2] age]);
|
|
|
|
XCTAssertEqual(40, [(EmployeeObject *)filtered[0] age]);
|
|
XCTAssertEqual(30, [(EmployeeObject *)filtered[1] age]);
|
|
}
|
|
|
|
- (void)testLiveUpdateFirst {
|
|
RLMRealm *realm = self.realmWithTestPath;
|
|
[realm beginWriteTransaction];
|
|
[IntObject createInRealm:realm withValue:@[@0]];
|
|
|
|
RLMResults *objects = [IntObject objectsInRealm:realm where:@"intCol = 0"];
|
|
XCTAssertNotNil([objects firstObject]);
|
|
[objects.firstObject setIntCol:1];
|
|
XCTAssertNil([objects firstObject]);
|
|
|
|
[realm cancelWriteTransaction];
|
|
}
|
|
|
|
- (void)testLiveUpdateLast {
|
|
RLMRealm *realm = self.realmWithTestPath;
|
|
[realm beginWriteTransaction];
|
|
[IntObject createInRealm:realm withValue:@[@0]];
|
|
|
|
RLMResults *objects = [IntObject objectsInRealm:realm where:@"intCol = 0"];
|
|
XCTAssertNotNil([objects lastObject]);
|
|
[objects.lastObject setIntCol:1];
|
|
XCTAssertNil([objects lastObject]);
|
|
|
|
[realm cancelWriteTransaction];
|
|
}
|
|
|
|
static vm_size_t get_resident_size() {
|
|
struct task_basic_info info;
|
|
mach_msg_type_number_t size = sizeof(info);
|
|
task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &size);
|
|
return info.resident_size;
|
|
}
|
|
|
|
- (void)testQueryMemoryUsage {
|
|
RLMRealm *realm = [self realmWithTestPath];
|
|
|
|
[realm beginWriteTransaction];
|
|
StringObject *obj = [[StringObject alloc] init];
|
|
obj.stringCol = @"a";
|
|
[realm addObject:obj];
|
|
[realm commitWriteTransaction];
|
|
|
|
NSPredicate *pred = [NSPredicate predicateWithFormat:@"stringCol = 'a'"];
|
|
|
|
// Check for memory leaks when creating queries by comparing the memory usage
|
|
// before and after creating a very large number of queries. Anything less
|
|
// than doubling is allowed as there's going to be some natural fluctuation,
|
|
// and failing to clean up 10k queries resulted in far more than doubling.
|
|
vm_size_t size = get_resident_size();
|
|
for (int i = 0; i < 10000; ++i) {
|
|
@autoreleasepool {
|
|
RLMResults *matches = [StringObject objectsInRealm:realm withPredicate:pred];
|
|
XCTAssertEqualObjects([matches[0] stringCol], @"a");
|
|
}
|
|
}
|
|
XCTAssert(get_resident_size() < size * 2);
|
|
}
|
|
|
|
- (void)testDeleteAllObjects
|
|
{
|
|
RLMRealm *realm = self.realmWithTestPath;
|
|
|
|
[realm beginWriteTransaction];
|
|
[StringObject createInRealm:realm withValue:@[@"name1"]];
|
|
[StringObject createInRealm:realm withValue:@[@"name2"]];
|
|
[realm commitWriteTransaction];
|
|
|
|
RLMResults *results = [StringObject objectsInRealm:realm where:@"stringCol = 'name1'"];
|
|
RLMAssertThrowsWithReasonMatching([realm deleteObjects:results], @"write transaction");
|
|
[realm beginWriteTransaction];
|
|
[realm deleteObjects:results];
|
|
[realm commitWriteTransaction];
|
|
XCTAssertEqual(0U, results.count);
|
|
XCTAssertEqual(1U, [StringObject allObjectsInRealm:realm].count);
|
|
|
|
results = [StringObject allObjectsInRealm:realm];
|
|
RLMAssertThrowsWithReasonMatching([realm deleteObjects:results], @"write transaction");
|
|
[realm beginWriteTransaction];
|
|
[realm deleteObjects:results];
|
|
[realm commitWriteTransaction];
|
|
XCTAssertEqual(0U, results.count);
|
|
XCTAssertEqual(0U, [StringObject allObjectsInRealm:realm].count);
|
|
}
|
|
|
|
- (void)testEnumerateAndDeleteTableResults {
|
|
RLMRealm *realm = self.realmWithTestPath;
|
|
const int count = 40;
|
|
|
|
[realm beginWriteTransaction];
|
|
for (int i = 0; i < count; ++i) {
|
|
[IntObject createInRealm:realm withValue:@[@(i)]];
|
|
}
|
|
|
|
int enumeratedCount = 0;
|
|
for (IntObject *io in [IntObject allObjectsInRealm:realm]) {
|
|
++enumeratedCount;
|
|
[realm deleteObject:io];
|
|
}
|
|
|
|
XCTAssertEqual(0U, [IntObject allObjectsInRealm:realm].count);
|
|
XCTAssertEqual(count, enumeratedCount);
|
|
|
|
[realm cancelWriteTransaction];
|
|
}
|
|
|
|
- (void)testEnumerateAndMutateQueryCondition {
|
|
RLMRealm *realm = self.realmWithTestPath;
|
|
const int count = 40;
|
|
|
|
[realm beginWriteTransaction];
|
|
for (int i = 0; i < count; ++i) {
|
|
[IntObject createInRealm:realm withValue:@[@(0)]];
|
|
}
|
|
|
|
int enumeratedCount = 0;
|
|
for (IntObject *io in [IntObject objectsInRealm:realm where:@"intCol = 0"]) {
|
|
++enumeratedCount;
|
|
io.intCol = enumeratedCount;
|
|
}
|
|
|
|
XCTAssertEqual(0U, [IntObject objectsInRealm:realm where:@"intCol = 0"].count);
|
|
XCTAssertEqual(count, enumeratedCount);
|
|
|
|
[realm cancelWriteTransaction];
|
|
}
|
|
|
|
- (void)testManualRefreshDuringEnumeration {
|
|
RLMRealm *realm = self.realmWithTestPath;
|
|
const int count = 40;
|
|
|
|
[realm beginWriteTransaction];
|
|
for (int i = 0; i < count; ++i) {
|
|
[IntObject createInRealm:realm withValue:@[@(0)]];
|
|
}
|
|
[realm commitWriteTransaction];
|
|
|
|
realm.autorefresh = NO;
|
|
[self dispatchAsyncAndWait:^{
|
|
RLMRealm *realm = self.realmWithTestPath;
|
|
[realm beginWriteTransaction];
|
|
// FIXME: this is roundabout because `table.clear()` does not update
|
|
// table views correctly
|
|
[realm deleteObjects:[IntObject objectsInRealm:realm where:@"intCol = 0"]];
|
|
[realm commitWriteTransaction];
|
|
}];
|
|
|
|
int enumeratedCount = 0;
|
|
for (IntObject *io in [IntObject allObjectsInRealm:realm]) {
|
|
[realm refresh];
|
|
XCTAssertTrue(io.invalidated);
|
|
++enumeratedCount;
|
|
}
|
|
|
|
XCTAssertEqual(enumeratedCount, count);
|
|
}
|
|
|
|
- (void)testCallInvalidateDuringEnumeration {
|
|
RLMRealm *realm = self.realmWithTestPath;
|
|
const int count = 40;
|
|
|
|
[realm beginWriteTransaction];
|
|
for (int i = 0; i < count; ++i) {
|
|
[IntObject createInRealm:realm withValue:@[@(0)]];
|
|
}
|
|
[realm commitWriteTransaction];
|
|
|
|
int enumeratedCount = 0;
|
|
@try {
|
|
for (__unused IntObject *io in [IntObject objectsInRealm:realm where:@"intCol = 0"]) {
|
|
++enumeratedCount;
|
|
[realm invalidate];
|
|
}
|
|
XCTFail(@"Should have thrown an exception");
|
|
}
|
|
@catch (NSException *e) {
|
|
XCTAssertEqualObjects(e.reason, @"Collection is no longer valid");
|
|
}
|
|
XCTAssertEqual(16, enumeratedCount);
|
|
|
|
// Also test with the enumeration done within a write transaction
|
|
[realm beginWriteTransaction];
|
|
enumeratedCount = 0;
|
|
@try {
|
|
for (__unused IntObject *io in [IntObject objectsInRealm:realm where:@"intCol = 0"]) {
|
|
++enumeratedCount;
|
|
[realm invalidate];
|
|
}
|
|
XCTFail(@"Should have thrown an exception");
|
|
}
|
|
@catch (NSException *e) {
|
|
XCTAssertEqualObjects(e.reason, @"Collection is no longer valid");
|
|
}
|
|
XCTAssertEqual(16, enumeratedCount);
|
|
}
|
|
|
|
- (void)testBeginWriteTransactionDuringEnumeration {
|
|
RLMRealm *realm = self.realmWithTestPath;
|
|
const int count = 40;
|
|
|
|
[realm beginWriteTransaction];
|
|
for (int i = 0; i < count; ++i) {
|
|
[IntObject createInRealm:realm withValue:@[@(0)]];
|
|
}
|
|
[realm commitWriteTransaction];
|
|
|
|
for (IntObject *io in [IntObject objectsInRealm:realm where:@"intCol = 0"]) {
|
|
[realm beginWriteTransaction];
|
|
io.intCol = 1;
|
|
[realm commitWriteTransaction];
|
|
}
|
|
|
|
XCTAssertEqual(40U, [IntObject objectsInRealm:realm where:@"intCol = 1"].count);
|
|
}
|
|
|
|
- (void)testAllMethodsCheckThread {
|
|
RLMRealm *realm = [RLMRealm defaultRealm];
|
|
[realm transactionWithBlock:^{
|
|
[IntObject createInDefaultRealmWithValue:@[@0]];
|
|
}];
|
|
|
|
RLMResults *results = [IntObject allObjects];
|
|
XCTAssertNoThrow([results isInvalidated]);
|
|
XCTAssertNoThrow([results objectAtIndex:0]);
|
|
XCTAssertNoThrow([results firstObject]);
|
|
XCTAssertNoThrow([results lastObject]);
|
|
XCTAssertNoThrow([results indexOfObject:[IntObject allObjects].firstObject]);
|
|
XCTAssertNoThrow([results indexOfObjectWhere:@"intCol = 0"]);
|
|
XCTAssertNoThrow([results indexOfObjectWithPredicate:[NSPredicate predicateWithFormat:@"intCol = 0"]]);
|
|
XCTAssertNoThrow([results objectsWhere:@"intCol = 0"]);
|
|
XCTAssertNoThrow([results objectsWithPredicate:[NSPredicate predicateWithFormat:@"intCol = 0"]]);
|
|
XCTAssertNoThrow([results sortedResultsUsingKeyPath:@"intCol" ascending:YES]);
|
|
XCTAssertNoThrow([results sortedResultsUsingDescriptors:@[[RLMSortDescriptor sortDescriptorWithKeyPath:@"intCol" ascending:YES]]]);
|
|
XCTAssertNoThrow([results minOfProperty:@"intCol"]);
|
|
XCTAssertNoThrow([results maxOfProperty:@"intCol"]);
|
|
XCTAssertNoThrow([results sumOfProperty:@"intCol"]);
|
|
XCTAssertNoThrow([results averageOfProperty:@"intCol"]);
|
|
XCTAssertNoThrow(results[0]);
|
|
XCTAssertNoThrow([results valueForKey:@"intCol"]);
|
|
|
|
[self dispatchAsyncAndWait:^{
|
|
XCTAssertThrows([results isInvalidated]);
|
|
XCTAssertThrows([results objectAtIndex:0]);
|
|
XCTAssertThrows([results firstObject]);
|
|
XCTAssertThrows([results lastObject]);
|
|
XCTAssertThrows([results indexOfObject:[IntObject allObjects].firstObject]);
|
|
XCTAssertThrows([results indexOfObjectWhere:@"intCol = 0"]);
|
|
XCTAssertThrows([results indexOfObjectWithPredicate:[NSPredicate predicateWithFormat:@"intCol = 0"]]);
|
|
XCTAssertThrows([results objectsWhere:@"intCol = 0"]);
|
|
XCTAssertThrows([results objectsWithPredicate:[NSPredicate predicateWithFormat:@"intCol = 0"]]);
|
|
XCTAssertThrows([results sortedResultsUsingKeyPath:@"intCol" ascending:YES]);
|
|
XCTAssertThrows([results sortedResultsUsingDescriptors:@[[RLMSortDescriptor sortDescriptorWithKeyPath:@"intCol" ascending:YES]]]);
|
|
XCTAssertThrows([results minOfProperty:@"intCol"]);
|
|
XCTAssertThrows([results maxOfProperty:@"intCol"]);
|
|
XCTAssertThrows([results sumOfProperty:@"intCol"]);
|
|
XCTAssertThrows([results averageOfProperty:@"intCol"]);
|
|
XCTAssertThrows(results[0]);
|
|
XCTAssertThrows([results valueForKey:@"intCol"]);
|
|
}];
|
|
}
|
|
|
|
- (void)testAllMethodsCheckForInvalidation {
|
|
RLMRealm *realm = [RLMRealm defaultRealm];
|
|
[realm transactionWithBlock:^{
|
|
[IntObject createInDefaultRealmWithValue:@[@0]];
|
|
}];
|
|
|
|
RLMResults *results = [IntObject allObjects];
|
|
XCTAssertFalse(results.isInvalidated);
|
|
XCTAssertNoThrow([results objectAtIndex:0]);
|
|
XCTAssertNoThrow([results firstObject]);
|
|
XCTAssertNoThrow([results lastObject]);
|
|
XCTAssertNoThrow([results indexOfObject:[IntObject allObjects].firstObject]);
|
|
XCTAssertNoThrow([results indexOfObjectWhere:@"intCol = 0"]);
|
|
XCTAssertNoThrow([results indexOfObjectWithPredicate:[NSPredicate predicateWithFormat:@"intCol = 0"]]);
|
|
XCTAssertNoThrow([results objectsWhere:@"intCol = 0"]);
|
|
XCTAssertNoThrow([results objectsWithPredicate:[NSPredicate predicateWithFormat:@"intCol = 0"]]);
|
|
XCTAssertNoThrow([results sortedResultsUsingKeyPath:@"intCol" ascending:YES]);
|
|
XCTAssertNoThrow([results sortedResultsUsingDescriptors:@[[RLMSortDescriptor sortDescriptorWithKeyPath:@"intCol" ascending:YES]]]);
|
|
XCTAssertNoThrow([results minOfProperty:@"intCol"]);
|
|
XCTAssertNoThrow([results maxOfProperty:@"intCol"]);
|
|
XCTAssertNoThrow([results sumOfProperty:@"intCol"]);
|
|
XCTAssertNoThrow([results averageOfProperty:@"intCol"]);
|
|
XCTAssertNoThrow(results[0]);
|
|
XCTAssertNoThrow([results valueForKey:@"intCol"]);
|
|
XCTAssertNoThrow({for (__unused id obj in results);});
|
|
|
|
[realm invalidate];
|
|
|
|
XCTAssertTrue(results.isInvalidated);
|
|
XCTAssertThrows([results objectAtIndex:0]);
|
|
XCTAssertThrows([results firstObject]);
|
|
XCTAssertThrows([results lastObject]);
|
|
XCTAssertThrows([results indexOfObject:[IntObject allObjects].firstObject]);
|
|
XCTAssertThrows([results indexOfObjectWhere:@"intCol = 0"]);
|
|
XCTAssertThrows([results indexOfObjectWithPredicate:[NSPredicate predicateWithFormat:@"intCol = 0"]]);
|
|
XCTAssertThrows([results objectsWhere:@"intCol = 0"]);
|
|
XCTAssertThrows([results objectsWithPredicate:[NSPredicate predicateWithFormat:@"intCol = 0"]]);
|
|
XCTAssertThrows([results sortedResultsUsingKeyPath:@"intCol" ascending:YES]);
|
|
XCTAssertThrows([results sortedResultsUsingDescriptors:@[[RLMSortDescriptor sortDescriptorWithKeyPath:@"intCol" ascending:YES]]]);
|
|
XCTAssertThrows([results minOfProperty:@"intCol"]);
|
|
XCTAssertThrows([results maxOfProperty:@"intCol"]);
|
|
XCTAssertThrows([results sumOfProperty:@"intCol"]);
|
|
XCTAssertThrows([results averageOfProperty:@"intCol"]);
|
|
XCTAssertThrows(results[0]);
|
|
XCTAssertThrows([results valueForKey:@"intCol"]);
|
|
XCTAssertThrows({for (__unused id obj in results);});
|
|
}
|
|
|
|
- (void)testResultsDependingOnDeletedLinkView {
|
|
RLMRealm *realm = [RLMRealm defaultRealm];
|
|
__block IntegerArrayPropertyObject *object;
|
|
[realm transactionWithBlock:^{
|
|
IntObject* intObject = [IntObject createInDefaultRealmWithValue:@[@0]];
|
|
object = [IntegerArrayPropertyObject createInDefaultRealmWithValue:@[ @0, @[ intObject ] ]];
|
|
}];
|
|
|
|
RLMResults *results = [object.array sortedResultsUsingKeyPath:@"intCol" ascending:YES];
|
|
[results firstObject];
|
|
|
|
RLMResults *unevaluatedResults = [object.array sortedResultsUsingKeyPath:@"intCol" ascending:YES];
|
|
|
|
[realm transactionWithBlock:^{
|
|
[realm deleteObject:object];
|
|
}];
|
|
|
|
XCTAssertFalse(results.isInvalidated);
|
|
XCTAssertFalse(unevaluatedResults.isInvalidated);
|
|
|
|
XCTAssertEqual(0u, results.count);
|
|
XCTAssertEqual(0u, unevaluatedResults.count);
|
|
|
|
XCTAssertEqualObjects(nil, results.firstObject);
|
|
XCTAssertEqualObjects(nil, unevaluatedResults.firstObject);
|
|
}
|
|
|
|
- (void)testResultsDependingOnDeletedTableView {
|
|
RLMRealm *realm = [RLMRealm defaultRealm];
|
|
__block DogObject *dog;
|
|
[realm transactionWithBlock:^{
|
|
dog = [DogObject createInDefaultRealmWithValue:@[ @"Fido", @3 ]];
|
|
[OwnerObject createInDefaultRealmWithValue:@[ @"John", dog ]];
|
|
}];
|
|
|
|
RLMResults *results = [dog.owners objectsWhere:@"name != 'Not a real name'"];
|
|
[results firstObject];
|
|
|
|
RLMResults *unevaluatedResults = [dog.owners objectsWhere:@"name != 'Not a real name'"];
|
|
|
|
[realm transactionWithBlock:^{
|
|
[realm deleteObject:dog];
|
|
}];
|
|
|
|
XCTAssertFalse(results.isInvalidated);
|
|
XCTAssertFalse(unevaluatedResults.isInvalidated);
|
|
|
|
XCTAssertEqual(0u, results.count);
|
|
XCTAssertEqual(0u, unevaluatedResults.count);
|
|
|
|
XCTAssertEqualObjects(nil, results.firstObject);
|
|
XCTAssertEqualObjects(nil, unevaluatedResults.firstObject);
|
|
}
|
|
|
|
- (void)testResultsDependingOnLinkingObjects {
|
|
RLMRealm *realm = [RLMRealm defaultRealm];
|
|
__block DogObject *dog;
|
|
__block OwnerObject *owner;
|
|
[realm transactionWithBlock:^{
|
|
dog = [DogObject createInDefaultRealmWithValue:@[ @"Fido", @3 ]];
|
|
owner = [OwnerObject createInDefaultRealmWithValue:@[ @"John", dog ]];
|
|
}];
|
|
|
|
XCTestExpectation *expectation = [self expectationWithDescription:@""];
|
|
RLMResults *results = [DogObject objectsWhere:@"ANY owners.name == 'James'"];
|
|
id token = [results addNotificationBlock:^(__unused RLMResults *results, RLMCollectionChange *change, __unused NSError *error) {
|
|
if (change != nil) {
|
|
[expectation fulfill];
|
|
}
|
|
CFRunLoopStop(CFRunLoopGetCurrent());
|
|
}];
|
|
// Consume the initial notification.
|
|
CFRunLoopRun();
|
|
|
|
XCTAssertEqual(0u, results.count);
|
|
XCTAssertNil(results.firstObject);
|
|
|
|
[realm transactionWithBlock:^{
|
|
owner.name = @"James";
|
|
}];
|
|
|
|
XCTAssertEqual(1u, results.count);
|
|
XCTAssertEqualObjects(dog.dogName, [results.firstObject dogName]);
|
|
|
|
[self waitForExpectationsWithTimeout:1.0 handler:nil];
|
|
token = nil;
|
|
}
|
|
|
|
- (void)testDistinctQuery {
|
|
RLMRealm *realm = [RLMRealm defaultRealm];
|
|
__block DogObject *fido;
|
|
__block DogObject *cujo;
|
|
__block DogObject *buster;
|
|
[realm transactionWithBlock:^{
|
|
fido = [DogObject createInDefaultRealmWithValue:@[ @"Fido", @3 ]];
|
|
cujo = [DogObject createInDefaultRealmWithValue:@[ @"Cujo", @3 ]];
|
|
buster = [DogObject createInDefaultRealmWithValue:@[ @"Buster", @5 ]];
|
|
}];
|
|
|
|
RLMResults *results = [[DogObject allObjects] distinctResultsUsingKeyPaths:@[@"age"]];
|
|
NSMutableArray *ages = NSMutableArray.new;
|
|
for (id result in results) {
|
|
[ages addObject: result];
|
|
}
|
|
XCTAssertEqual(2u, results.count);
|
|
XCTAssertEqualObjects([ages valueForKey:@"age"], (@[@3, @5]));
|
|
}
|
|
- (void)testDistinctQueryWithMultipleKeyPaths {
|
|
RLMRealm *realm = [RLMRealm defaultRealm];
|
|
__block DogObject *fido;
|
|
__block DogObject *fido2;
|
|
__block DogObject *fido3;
|
|
__block DogObject *cujo;
|
|
__block DogObject *buster;
|
|
__block DogObject *buster2;
|
|
__block DogObject *rotunda;
|
|
|
|
[realm transactionWithBlock:^{
|
|
fido = [DogObject createInDefaultRealmWithValue:@[ @"Fido", @3 ]];
|
|
fido2 = [DogObject createInDefaultRealmWithValue:@[ @"Fido", @3 ]];
|
|
fido3 = [DogObject createInDefaultRealmWithValue:@[ @"Fido", @4 ]];
|
|
cujo = [DogObject createInDefaultRealmWithValue:@[ @"Cujo", @3 ]];
|
|
buster = [DogObject createInDefaultRealmWithValue:@[ @"Buster", @3 ]];
|
|
buster2 = [DogObject createInDefaultRealmWithValue:@[ @"Buster", @3 ]];
|
|
rotunda = [DogObject createInDefaultRealmWithValue:@[ @"Rotunda", @7 ]];
|
|
}];
|
|
|
|
RLMResults *results = [[DogObject allObjects] distinctResultsUsingKeyPaths:@[@"dogName", @"age"]];
|
|
NSMutableArray *resultsArr = NSMutableArray.new;
|
|
for (DogObject *result in results) {
|
|
[resultsArr addObject:[NSString stringWithFormat:@"%@/%@", result.dogName, @(result.age)]];
|
|
}
|
|
|
|
XCTAssertEqualObjects(resultsArr, (@[@"Fido/3", @"Fido/4", @"Cujo/3", @"Buster/3", @"Rotunda/7"]));
|
|
}
|
|
- (void)testDistinctQueryWithMultilevelKeyPath {
|
|
RLMRealm *realm = [RLMRealm defaultRealm];
|
|
__block OwnerObject *owner1;
|
|
__block OwnerObject *owner2;
|
|
__block OwnerObject *owner3;
|
|
__block DogObject *dog1;
|
|
__block DogObject *dog2;
|
|
__block DogObject *dog3;
|
|
|
|
[realm transactionWithBlock:^{
|
|
dog1 = [DogObject createInDefaultRealmWithValue:@[ @"Fido", @3 ]];
|
|
dog2 = [DogObject createInDefaultRealmWithValue:@[ @"Cujo", @3 ]];
|
|
dog3 = [DogObject createInDefaultRealmWithValue:@[ @"Rotunda", @7 ]];
|
|
|
|
owner1 = [OwnerObject createInDefaultRealmWithValue:@[ @"Joe", dog1 ]];
|
|
owner2 = [OwnerObject createInDefaultRealmWithValue:@[ @"Marie", dog2 ]];
|
|
owner3 = [OwnerObject createInDefaultRealmWithValue:@[ @"Marie", dog3 ]];
|
|
}];
|
|
|
|
RLMResults *results = [[OwnerObject allObjects] distinctResultsUsingKeyPaths:@[@"dog.age"]];
|
|
NSMutableArray *resultsArr = NSMutableArray.new;
|
|
for (OwnerObject *result in results) {
|
|
[resultsArr addObject:@(result.dog.age)];
|
|
}
|
|
XCTAssertEqualObjects(resultsArr, (@[@3, @7]));
|
|
}
|
|
|
|
- (void)testDistinctQueryThrowsInvalidKeyPathsSpecified {
|
|
RLMRealm *realm = [RLMRealm defaultRealm];
|
|
[realm transactionWithBlock:^{
|
|
[DogObject createInDefaultRealmWithValue:@[ @"Fido", @3 ]];
|
|
[DogObject createInDefaultRealmWithValue:@[ @"Fido", @3 ]];
|
|
[DogObject createInDefaultRealmWithValue:@[ @"Fido", @5 ]];
|
|
AggregateObject *ao1 = [AggregateObject createInDefaultRealmWithValue:@[ @0 ]];
|
|
AggregateObject *ao2 = [AggregateObject createInDefaultRealmWithValue:@[ @0 ]];
|
|
[AggregateArrayObject createInDefaultRealmWithValue:@[@[ao1, ao2]]];
|
|
[AllTypesObject createInDefaultRealmWithValue:@[]];
|
|
}];
|
|
|
|
XCTAssertThrows([[DogObject allObjects] distinctResultsUsingKeyPaths:@[@""]]);
|
|
XCTAssertThrows([[DogObject allObjects] distinctResultsUsingKeyPaths:@[@" "]]);
|
|
XCTAssertThrows([[DogObject allObjects] distinctResultsUsingKeyPaths:@[@"\n"]]);
|
|
XCTAssertThrows(([[DogObject allObjects] distinctResultsUsingKeyPaths:@[@"dogName", @""]]));
|
|
XCTAssertThrows(([[DogObject allObjects] distinctResultsUsingKeyPaths:@[@"dogName", @" "]]));
|
|
XCTAssertThrows(([[DogObject allObjects] distinctResultsUsingKeyPaths:@[@"dogName", @"\n"]]));
|
|
XCTAssertThrows(([[DogObject allObjects] distinctResultsUsingKeyPaths:@[@"@max.age"]]));
|
|
XCTAssertThrows([[AllTypesObject allObjects] distinctResultsUsingKeyPaths:@[@"linkingObjectsCol"]]);
|
|
XCTAssertThrows([[AllTypesObject allObjects] distinctResultsUsingKeyPaths:@[@"objectCol"]]);
|
|
XCTAssertThrows([[AggregateArrayObject allObjects] distinctResultsUsingKeyPaths:@[@"array"]]);
|
|
}
|
|
|
|
@end
|
|
|