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.

1496 lines
69 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"
@implementation DogArrayObject
@end
@interface ArrayPropertyTests : RLMTestCase
@end
@implementation ArrayPropertyTests
-(void)testPopulateEmptyArray {
RLMRealm *realm = [self realmWithTestPath];
[realm beginWriteTransaction];
ArrayPropertyObject *array = [ArrayPropertyObject createInRealm:realm withValue:@[@"arrayObject", @[], @[]]];
XCTAssertNotNil(array.array, @"Should be able to get an empty array");
XCTAssertEqual(array.array.count, 0U, @"Should start with no array elements");
StringObject *obj = [[StringObject alloc] init];
obj.stringCol = @"a";
[array.array addObject:obj];
[array.array addObject:[StringObject createInRealm:realm withValue:@[@"b"]]];
[array.array addObject:obj];
[realm commitWriteTransaction];
XCTAssertEqual(array.array.count, 3U, @"Should have three elements in array");
XCTAssertEqualObjects([array.array[0] stringCol], @"a", @"First element should have property value 'a'");
XCTAssertEqualObjects([array.array[1] stringCol], @"b", @"Second element should have property value 'b'");
XCTAssertEqualObjects([array.array[2] stringCol], @"a", @"Third element should have property value 'a'");
RLMArray *arrayProp = array.array;
RLMAssertThrowsWithReasonMatching([arrayProp addObject:obj], @"write transaction");
// make sure we can fast enumerate
for (RLMObject *obj in array.array) {
XCTAssertTrue(obj.description.length, @"Object should have description");
}
}
-(void)testModifyDetatchedArray {
RLMRealm *realm = [self realmWithTestPath];
[realm beginWriteTransaction];
ArrayPropertyObject *arObj = [ArrayPropertyObject createInRealm:realm withValue:@[@"arrayObject", @[], @[]]];
XCTAssertNotNil(arObj.array, @"Should be able to get an empty array");
XCTAssertEqual(arObj.array.count, 0U, @"Should start with no array elements");
StringObject *obj = [[StringObject alloc] init];
obj.stringCol = @"a";
RLMArray *array = arObj.array;
[array addObject:obj];
[array addObject:[StringObject createInRealm:realm withValue:@[@"b"]]];
[realm commitWriteTransaction];
XCTAssertEqual(array.count, 2U, @"Should have two elements in array");
XCTAssertEqualObjects([array[0] stringCol], @"a", @"First element should have property value 'a'");
XCTAssertEqualObjects([arObj.array[1] stringCol], @"b", @"Second element should have property value 'b'");
RLMAssertThrowsWithReasonMatching([array addObject:obj], @"write transaction");
}
- (void)testDeleteUnmanagedObjectWithArrayProperty {
ArrayPropertyObject *arObj = [[ArrayPropertyObject alloc] initWithValue:@[@"arrayObject", @[@[@"a"]], @[]]];
RLMArray *stringArray = arObj.array;
XCTAssertFalse(stringArray.isInvalidated, @"stringArray should be valid after creation.");
arObj = nil;
XCTAssertFalse(stringArray.isInvalidated, @"stringArray should still be valid after parent deletion.");
}
- (void)testDeleteObjectWithArrayProperty {
RLMRealm *realm = [self realmWithTestPath];
[realm beginWriteTransaction];
ArrayPropertyObject *arObj = [ArrayPropertyObject createInRealm:realm withValue:@[@"arrayObject", @[@[@"a"]], @[]]];
RLMArray *stringArray = arObj.array;
XCTAssertFalse(stringArray.isInvalidated, @"stringArray should be valid after creation.");
[realm deleteObject:arObj];
XCTAssertTrue(stringArray.isInvalidated, @"stringArray should be invalid after parent deletion.");
[realm commitWriteTransaction];
}
- (void)testDeleteObjectInArrayProperty {
RLMRealm *realm = [self realmWithTestPath];
[realm beginWriteTransaction];
ArrayPropertyObject *arObj = [ArrayPropertyObject createInRealm:realm withValue:@[@"arrayObject", @[@[@"a"]], @[]]];
RLMArray *stringArray = arObj.array;
StringObject *firstObject = stringArray.firstObject;
[realm deleteObjects:[StringObject allObjectsInRealm:realm]];
XCTAssertFalse(stringArray.isInvalidated, @"stringArray should be valid after member object deletion.");
XCTAssertTrue(firstObject.isInvalidated, @"firstObject should be invalid after deletion.");
XCTAssertEqual(stringArray.count, 0U, @"stringArray.count should be zero after deleting its only member.");
[realm commitWriteTransaction];
}
-(void)testInsertMultiple {
RLMRealm *realm = [self realmWithTestPath];
[realm beginWriteTransaction];
ArrayPropertyObject *obj = [ArrayPropertyObject createInRealm:realm withValue:@[@"arrayObject", @[], @[]]];
StringObject *child1 = [StringObject createInRealm:realm withValue:@[@"a"]];
StringObject *child2 = [[StringObject alloc] init];
child2.stringCol = @"b";
[obj.array addObjects:@[child2, child1]];
[realm commitWriteTransaction];
RLMResults *children = [StringObject allObjectsInRealm:realm];
XCTAssertEqualObjects([children[0] stringCol], @"a", @"First child should be 'a'");
XCTAssertEqualObjects([children[1] stringCol], @"b", @"Second child should be 'b'");
}
-(void)testInsertAtIndex {
RLMRealm *realm = [self realmWithTestPath];
[realm beginWriteTransaction];
ArrayPropertyObject *obj = [ArrayPropertyObject createInRealm:realm withValue:@[@"arrayObject", @[], @[]]];
StringObject *child1 = [StringObject createInRealm:realm withValue:@[@"a"]];
StringObject *child2 = [[StringObject alloc] init];
child2.stringCol = @"b";
[obj.array addObject:child2];
RLMAssertThrowsWithReasonMatching([obj.array insertObject:child1 atIndex:2], @"must be less than 2");
[realm commitWriteTransaction];
RLMArray *children = obj.array;
XCTAssertEqual(children.count, 1U);
XCTAssertEqualObjects([children[0] stringCol], @"b", @"Only child should be 'b'");
}
- (void)testMove {
RLMRealm *realm = [self realmWithTestPath];
ArrayPropertyObject *obj = [[ArrayPropertyObject alloc] initWithValue:@[@"arrayObject", @[@[@"a"], @[@"b"]], @[]]];
RLM_GENERIC_ARRAY(StringObject) *children = obj.array;
[children moveObjectAtIndex:1 toIndex:0];
XCTAssertEqualObjects([children[0] stringCol], @"b");
XCTAssertEqualObjects([children[1] stringCol], @"a");
[children moveObjectAtIndex:0 toIndex:1];
XCTAssertEqualObjects([children[0] stringCol], @"a");
XCTAssertEqualObjects([children[1] stringCol], @"b");
[children moveObjectAtIndex:0 toIndex:0];
XCTAssertEqualObjects([children[0] stringCol], @"a");
XCTAssertEqualObjects([children[1] stringCol], @"b");
RLMAssertThrowsWithReasonMatching([children moveObjectAtIndex:0 toIndex:2], @"must be less than 2");
RLMAssertThrowsWithReasonMatching([children moveObjectAtIndex:2 toIndex:0], @"must be less than 2");
[realm beginWriteTransaction];
[realm addObject:obj];
children = obj.array;
[children moveObjectAtIndex:1 toIndex:0];
XCTAssertEqualObjects([children[0] stringCol], @"b");
XCTAssertEqualObjects([children[1] stringCol], @"a");
[children moveObjectAtIndex:0 toIndex:1];
XCTAssertEqualObjects([children[0] stringCol], @"a");
XCTAssertEqualObjects([children[1] stringCol], @"b");
[children moveObjectAtIndex:0 toIndex:0];
XCTAssertEqualObjects([children[0] stringCol], @"a");
XCTAssertEqualObjects([children[1] stringCol], @"b");
RLMAssertThrowsWithReasonMatching([children moveObjectAtIndex:0 toIndex:2], @"must be less than 2");
RLMAssertThrowsWithReasonMatching([children moveObjectAtIndex:2 toIndex:0], @"must be less than 2");
[realm commitWriteTransaction];
RLMAssertThrowsWithReasonMatching([children moveObjectAtIndex:1 toIndex:0], @"write transaction");
}
- (void)testAddInvalidated {
RLMRealm *realm = [RLMRealm defaultRealm];
[realm beginWriteTransaction];
CompanyObject *company = [CompanyObject createInDefaultRealmWithValue:@[@"company", @[]]];
EmployeeObject *person = [[EmployeeObject alloc] init];
person.name = @"Mary";
[realm addObject:person];
[realm deleteObjects:[EmployeeObject allObjects]];
RLMAssertThrowsWithReasonMatching([company.employees addObject:person], @"invalidated");
RLMAssertThrowsWithReasonMatching([company.employees insertObject:person atIndex:0], @"invalidated");
[realm cancelWriteTransaction];
}
- (void)testAddNil {
RLMRealm *realm = [RLMRealm defaultRealm];
[realm beginWriteTransaction];
CompanyObject *company = [CompanyObject createInDefaultRealmWithValue:@[@"company", @[]]];
RLMAssertThrowsWithReason([company.employees addObject:self.nonLiteralNil],
@"Invalid nil value for array of 'EmployeeObject'.");
[realm cancelWriteTransaction];
}
- (void)testUnmanaged {
RLMRealm *realm = [self realmWithTestPath];
ArrayPropertyObject *array = [[ArrayPropertyObject alloc] init];
array.name = @"name";
XCTAssertNotNil(array.array, @"RLMArray property should get created on access");
XCTAssertNil(array.array.firstObject, @"No objects added yet");
XCTAssertNil(array.array.lastObject, @"No objects added yet");
StringObject *obj1 = [[StringObject alloc] init];
obj1.stringCol = @"a";
StringObject *obj2 = [[StringObject alloc] init];
obj2.stringCol = @"b";
StringObject *obj3 = [[StringObject alloc] init];
obj3.stringCol = @"c";
[array.array addObject:obj1];
[array.array addObject:obj2];
[array.array addObject:obj3];
XCTAssertEqualObjects(array.array.firstObject, obj1, @"Objects should be equal");
XCTAssertEqualObjects(array.array.lastObject, obj3, @"Objects should be equal");
XCTAssertEqualObjects([array.array objectAtIndex:1], obj2, @"Objects should be equal");
[realm beginWriteTransaction];
[realm addObject:array];
[realm commitWriteTransaction];
XCTAssertEqual(array.array.count, 3U, @"Should have two elements in array");
XCTAssertEqualObjects([array.array[0] stringCol], @"a", @"First element should have property value 'a'");
XCTAssertEqualObjects([array.array[1] stringCol], @"b", @"Second element should have property value 'b'");
[realm beginWriteTransaction];
[array.array replaceObjectAtIndex:0 withObject:obj3];
XCTAssertTrue([[array.array objectAtIndex:0] isEqualToObject:obj3], @"Objects should be replaced");
array.array[0] = obj1;
XCTAssertTrue([obj1 isEqualToObject:[array.array objectAtIndex:0]], @"Objects should be replaced");
[array.array removeLastObject];
XCTAssertEqual(array.array.count, 2U, @"2 objects left");
[array.array addObject:obj1];
[array.array removeAllObjects];
XCTAssertEqual(array.array.count, 0U, @"All objects removed");
[realm commitWriteTransaction];
ArrayPropertyObject *intArray = [[ArrayPropertyObject alloc] init];
IntObject *intObj = [[IntObject alloc] init];
intObj.intCol = 1;
RLMAssertThrowsWithReasonMatching([intArray.array addObject:(id)intObj], @"IntObject.*StringObject");
[intArray.intArray addObject:intObj];
XCTAssertThrows([intArray.intArray objectsWhere:@"intCol == 1"], @"Should throw on unmanaged RLMArray");
XCTAssertThrows(([intArray.intArray objectsWithPredicate:[NSPredicate predicateWithFormat:@"intCol == %i", 1]]), @"Should throw on unmanaged RLMArray");
XCTAssertThrows([intArray.intArray sortedResultsUsingKeyPath:@"intCol" ascending:YES], @"Should throw on unmanaged RLMArray");
XCTAssertEqual(0U, [intArray.intArray indexOfObjectWhere:@"intCol == 1"]);
XCTAssertEqual(0U, ([intArray.intArray indexOfObjectWithPredicate:[NSPredicate predicateWithFormat:@"intCol == %i", 1]]));
XCTAssertEqual([intArray.intArray indexOfObject:intObj], 0U, @"Should be first element");
XCTAssertEqual([intArray.intArray indexOfObject:intObj], 0U, @"Should be first element");
// test unmanaged with literals
__unused ArrayPropertyObject *obj = [[ArrayPropertyObject alloc] initWithValue:@[@"n", @[], @[[[IntObject alloc] initWithValue:@[@1]]]]];
}
- (void)testUnmanagedPrimitive {
AllPrimitiveArrays *obj = [[AllPrimitiveArrays alloc] init];
XCTAssertTrue([obj.intObj isKindOfClass:[RLMArray class]]);
XCTAssertTrue([obj.floatObj isKindOfClass:[RLMArray class]]);
XCTAssertTrue([obj.doubleObj isKindOfClass:[RLMArray class]]);
XCTAssertTrue([obj.boolObj isKindOfClass:[RLMArray class]]);
XCTAssertTrue([obj.stringObj isKindOfClass:[RLMArray class]]);
XCTAssertTrue([obj.dataObj isKindOfClass:[RLMArray class]]);
XCTAssertTrue([obj.dateObj isKindOfClass:[RLMArray class]]);
[obj.intObj addObject:@1];
XCTAssertEqualObjects(obj.intObj[0], @1);
XCTAssertThrows([obj.intObj addObject:@""]);
RLMRealm *realm = [RLMRealm defaultRealm];
[realm beginWriteTransaction];
obj = [AllPrimitiveArrays createInRealm:realm withValue:@[@[],@[],@[],@[],@[],@[],@[]]];
XCTAssertTrue([obj.intObj isKindOfClass:[RLMArray class]]);
XCTAssertTrue([obj.floatObj isKindOfClass:[RLMArray class]]);
XCTAssertTrue([obj.doubleObj isKindOfClass:[RLMArray class]]);
XCTAssertTrue([obj.boolObj isKindOfClass:[RLMArray class]]);
XCTAssertTrue([obj.stringObj isKindOfClass:[RLMArray class]]);
XCTAssertTrue([obj.dataObj isKindOfClass:[RLMArray class]]);
XCTAssertTrue([obj.dateObj isKindOfClass:[RLMArray class]]);
[obj.intObj addObject:@5];
XCTAssertEqualObjects(obj.intObj.firstObject, @5);
}
- (void)testReplaceObjectAtIndexInUnmanagedArray {
ArrayPropertyObject *array = [[ArrayPropertyObject alloc] init];
array.name = @"name";
StringObject *stringObj1 = [[StringObject alloc] init];
stringObj1.stringCol = @"a";
StringObject *stringObj2 = [[StringObject alloc] init];
stringObj2.stringCol = @"b";
StringObject *stringObj3 = [[StringObject alloc] init];
stringObj3.stringCol = @"c";
[array.array addObject:stringObj1];
[array.array addObject:stringObj2];
[array.array addObject:stringObj3];
IntObject *intObj1 = [[IntObject alloc] init];
intObj1.intCol = 0;
IntObject *intObj2 = [[IntObject alloc] init];
intObj2.intCol = 1;
IntObject *intObj3 = [[IntObject alloc] init];
intObj3.intCol = 2;
[array.intArray addObject:intObj1];
[array.intArray addObject:intObj2];
[array.intArray addObject:intObj3];
XCTAssertEqualObjects(array.array[0], stringObj1, @"Objects should be equal");
XCTAssertEqualObjects(array.array[1], stringObj2, @"Objects should be equal");
XCTAssertEqualObjects(array.array[2], stringObj3, @"Objects should be equal");
XCTAssertEqual(array.array.count, 3U, @"Should have 3 elements in string array");
XCTAssertEqualObjects(array.intArray[0], intObj1, @"Objects should be equal");
XCTAssertEqualObjects(array.intArray[1], intObj2, @"Objects should be equal");
XCTAssertEqualObjects(array.intArray[2], intObj3, @"Objects should be equal");
XCTAssertEqual(array.intArray.count, 3U, @"Should have 3 elements in int array");
StringObject *stringObj4 = [[StringObject alloc] init];
stringObj4.stringCol = @"d";
[array.array replaceObjectAtIndex:0 withObject:stringObj4];
XCTAssertTrue([[array.array objectAtIndex:0] isEqualToObject:stringObj4], @"Objects should be replaced");
XCTAssertEqual(array.array.count, 3U, @"Should have 3 elements in int array");
IntObject *intObj4 = [[IntObject alloc] init];
intObj4.intCol = 3;
[array.intArray replaceObjectAtIndex:1 withObject:intObj4];
XCTAssertTrue([[array.intArray objectAtIndex:1] isEqualToObject:intObj4], @"Objects should be replaced");
XCTAssertEqual(array.intArray.count, 3U, @"Should have 3 elements in int array");
RLMAssertThrowsWithReasonMatching([array.array replaceObjectAtIndex:0 withObject:(id)intObj4],
@"IntObject.*StringObject");
RLMAssertThrowsWithReasonMatching([array.intArray replaceObjectAtIndex:1 withObject:(id)stringObj4],
@"StringObject.*IntObject");
}
- (void)testDeleteObjectInUnmanagedArray {
ArrayPropertyObject *array = [[ArrayPropertyObject alloc] init];
array.name = @"name";
StringObject *stringObj1 = [[StringObject alloc] init];
stringObj1.stringCol = @"a";
StringObject *stringObj2 = [[StringObject alloc] init];
stringObj2.stringCol = @"b";
StringObject *stringObj3 = [[StringObject alloc] init];
stringObj3.stringCol = @"c";
[array.array addObject:stringObj1];
[array.array addObject:stringObj2];
[array.array addObject:stringObj3];
IntObject *intObj1 = [[IntObject alloc] init];
intObj1.intCol = 0;
IntObject *intObj2 = [[IntObject alloc] init];
intObj2.intCol = 1;
IntObject *intObj3 = [[IntObject alloc] init];
intObj3.intCol = 2;
[array.intArray addObject:intObj1];
[array.intArray addObject:intObj2];
[array.intArray addObject:intObj3];
XCTAssertEqualObjects(array.array[0], stringObj1, @"Objects should be equal");
XCTAssertEqualObjects(array.array[1], stringObj2, @"Objects should be equal");
XCTAssertEqualObjects(array.array[2], stringObj3, @"Objects should be equal");
XCTAssertEqual(array.array.count, 3U, @"Should have 3 elements in string array");
XCTAssertEqualObjects(array.intArray[0], intObj1, @"Objects should be equal");
XCTAssertEqualObjects(array.intArray[1], intObj2, @"Objects should be equal");
XCTAssertEqualObjects(array.intArray[2], intObj3, @"Objects should be equal");
XCTAssertEqual(array.intArray.count, 3U, @"Should have 3 elements in int array");
[array.array removeLastObject];
XCTAssertEqualObjects(array.array[0], stringObj1, @"Objects should be equal");
XCTAssertEqualObjects(array.array[1], stringObj2, @"Objects should be equal");
XCTAssertEqual(array.array.count, 2U, @"Should have 2 elements in string array");
[array.array removeLastObject];
XCTAssertEqualObjects(array.array[0], stringObj1, @"Objects should be equal");
XCTAssertEqual(array.array.count, 1U, @"Should have 1 elements in string array");
[array.array removeLastObject];
XCTAssertEqual(array.array.count, 0U, @"Should have 0 elements in string array");
[array.intArray removeAllObjects];
XCTAssertEqual(array.intArray.count, 0U, @"Should have 0 elements in int array");
}
- (void)testExchangeObjectAtIndexWithObjectAtIndex {
void (^test)(RLMArray *) = ^(RLMArray *array) {
[array exchangeObjectAtIndex:0 withObjectAtIndex:1];
XCTAssertEqual(2U, array.count);
XCTAssertEqualObjects(@"b", [array[0] stringCol]);
XCTAssertEqualObjects(@"a", [array[1] stringCol]);
[array exchangeObjectAtIndex:1 withObjectAtIndex:1];
XCTAssertEqual(2U, array.count);
XCTAssertEqualObjects(@"b", [array[0] stringCol]);
XCTAssertEqualObjects(@"a", [array[1] stringCol]);
[array exchangeObjectAtIndex:1 withObjectAtIndex:0];
XCTAssertEqual(2U, array.count);
XCTAssertEqualObjects(@"a", [array[0] stringCol]);
XCTAssertEqualObjects(@"b", [array[1] stringCol]);
RLMAssertThrowsWithReasonMatching([array exchangeObjectAtIndex:1 withObjectAtIndex:20], @"less than 2");
};
ArrayPropertyObject *array = [[ArrayPropertyObject alloc] initWithValue:@[@"foo", @[@[@"a"], @[@"b"]], @[]]];
test(array.array);
RLMRealm *realm = [RLMRealm defaultRealm];
[realm beginWriteTransaction];
[realm addObject:array];
test(array.array);
[realm commitWriteTransaction];
}
- (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}];
EmployeeObject *deleted = [EmployeeObject createInRealm:realm withValue:@{@"name": @"Jill", @"age": @25, @"hired": @YES}];
EmployeeObject *indirectlyDeleted = [EmployeeObject allObjectsInRealm:realm].lastObject;
[realm deleteObject:deleted];
// create company
CompanyObject *company = [[CompanyObject alloc] init];
company.name = @"name";
[company.employees addObjects:[EmployeeObject allObjects]];
[company.employees removeObjectAtIndex:1];
// test unmanaged
XCTAssertEqual(0U, [company.employees indexOfObject:po1]);
XCTAssertEqual(1U, [company.employees indexOfObject:po3]);
XCTAssertEqual((NSUInteger)NSNotFound, [company.employees indexOfObject:po2]);
// add to realm
[realm addObject:company];
[realm commitWriteTransaction];
// test LinkView RLMArray
XCTAssertEqual(0U, [company.employees indexOfObject:po1]);
XCTAssertEqual(1U, [company.employees indexOfObject:po3]);
XCTAssertEqual((NSUInteger)NSNotFound, [company.employees indexOfObject:po2]);
// non realm employee
EmployeeObject *notInRealm = [[EmployeeObject alloc] initWithValue:@[@"NoName", @1, @NO]];
XCTAssertEqual((NSUInteger)NSNotFound, [company.employees indexOfObject:notInRealm]);
// invalid object
XCTAssertThrows([company.employees indexOfObject:(EmployeeObject *)company]);
RLMAssertThrowsWithReasonMatching([company.employees indexOfObject:deleted], @"invalidated");
RLMAssertThrowsWithReasonMatching([company.employees indexOfObject:indirectlyDeleted], @"invalidated");
RLMResults *employees = [company.employees objectsWhere:@"age = %@", @40];
XCTAssertEqual(0U, [employees indexOfObject:po1]);
XCTAssertEqual((NSUInteger)NSNotFound, [employees indexOfObject:po3]);
}
- (void)testIndexOfObjectWhere
{
RLMRealm *realm = [RLMRealm defaultRealm];
[realm beginWriteTransaction];
EmployeeObject *po1 = [EmployeeObject createInRealm:realm withValue:@{@"name": @"Joe", @"age": @40, @"hired": @YES}];
[EmployeeObject createInRealm:realm withValue:@{@"name": @"John", @"age": @30, @"hired": @NO}];
EmployeeObject *po3 = [EmployeeObject createInRealm:realm withValue:@{@"name": @"Jill", @"age": @25, @"hired": @YES}];
EmployeeObject *po4 = [EmployeeObject createInRealm:realm withValue:@{@"name": @"Bill", @"age": @55, @"hired": @YES}];
// create company
CompanyObject *company = [[CompanyObject alloc] init];
company.name = @"name";
[company.employees addObjects:@[po3, po1, po4]];
// test unmanaged
XCTAssertEqual(0U, [company.employees indexOfObjectWhere:@"name = 'Jill'"]);
XCTAssertEqual(1U, [company.employees indexOfObjectWhere:@"name = 'Joe'"]);
XCTAssertEqual((NSUInteger)NSNotFound, [company.employees indexOfObjectWhere:@"name = 'John'"]);
// add to realm
[realm addObject:company];
[realm commitWriteTransaction];
// test LinkView RLMArray
XCTAssertEqual(0U, [company.employees indexOfObjectWhere:@"name = 'Jill'"]);
XCTAssertEqual(1U, [company.employees indexOfObjectWhere:@"name = 'Joe'"]);
XCTAssertEqual((NSUInteger)NSNotFound, [company.employees indexOfObjectWhere:@"name = 'John'"]);
RLMResults *results = [company.employees objectsWhere:@"age > 30"];
XCTAssertEqual(0U, [results indexOfObjectWhere:@"name = 'Joe'"]);
XCTAssertEqual(1U, [results indexOfObjectWhere:@"name = 'Bill'"]);
XCTAssertEqual((NSUInteger)NSNotFound, [results indexOfObjectWhere:@"name = 'John'"]);
XCTAssertEqual((NSUInteger)NSNotFound, [results indexOfObjectWhere:@"name = 'Jill'"]);
}
- (void)testFastEnumeration
{
RLMRealm *realm = self.realmWithTestPath;
[realm beginWriteTransaction];
CompanyObject *company = [[CompanyObject alloc] init];
company.name = @"name";
[realm addObject:company];
[realm commitWriteTransaction];
// enumerate empty array
for (__unused id obj in company.employees) {
XCTFail(@"Should be empty");
}
[realm beginWriteTransaction];
for (int i = 0; i < 30; ++i) {
EmployeeObject *eo = [EmployeeObject createInRealm:realm withValue:@{@"name": @"Joe", @"age": @40, @"hired": @YES}];
[company.employees addObject:eo];
}
[realm commitWriteTransaction];
XCTAssertEqual(company.employees.count, 30U);
__weak id objects[30];
NSInteger count = 0;
for (EmployeeObject *e in company.employees) {
XCTAssertNotNil(e, @"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++] = e;
}
XCTAssertEqual(count, 30, @"should have enumerated 30 objects");
for (int i = 0; i < count; i++) {
XCTAssertNil(objects[i], @"Object should have been released");
}
@autoreleasepool {
for (EmployeeObject *e in company.employees) {
objects[0] = e;
break;
}
}
XCTAssertNil(objects[0], @"Object should have been released");
}
- (void)testModifyDuringEnumeration {
RLMRealm *realm = self.realmWithTestPath;
[realm beginWriteTransaction];
CompanyObject *company = [[CompanyObject alloc] init];
company.name = @"name";
[realm addObject:company];
const size_t totalCount = 40;
for (size_t i = 0; i < totalCount; ++i) {
[company.employees addObject:[EmployeeObject createInRealm:realm withValue:@[@"name", @(i), @NO]]];
}
size_t count = 0;
for (EmployeeObject *eo in company.employees) {
++count;
[company.employees addObject:eo];
}
XCTAssertEqual(totalCount, count);
XCTAssertEqual(totalCount * 2, company.employees.count);
[realm cancelWriteTransaction];
// Unmanaged array
company = [[CompanyObject alloc] init];
for (size_t i = 0; i < totalCount; ++i) {
[company.employees addObject:[[EmployeeObject alloc] initWithValue:@[@"name", @(i), @NO]]];
}
count = 0;
for (EmployeeObject *eo in company.employees) {
++count;
[company.employees addObject:eo];
}
XCTAssertEqual(totalCount, count);
XCTAssertEqual(totalCount * 2, company.employees.count);
}
- (void)testDeleteDuringEnumeration {
RLMRealm *realm = self.realmWithTestPath;
[realm beginWriteTransaction];
CompanyObject *company = [[CompanyObject alloc] init];
company.name = @"name";
[realm addObject:company];
const size_t totalCount = 40;
for (size_t i = 0; i < totalCount; ++i) {
[company.employees addObject:[EmployeeObject createInRealm:realm withValue:@[@"name", @(i), @NO]]];
}
[realm commitWriteTransaction];
[realm beginWriteTransaction];
for (__unused EmployeeObject *eo in company.employees) {
[realm deleteObjects:company.employees];
}
[realm commitWriteTransaction];
}
- (void)testValueForKey {
RLMRealm *realm = self.realmWithTestPath;
[realm beginWriteTransaction];
CompanyObject *company = [[CompanyObject alloc] init];
company.name = @"name";
XCTAssertEqualObjects([company.employees valueForKey:@"name"], @[]);
[realm addObject:company];
[realm commitWriteTransaction];
XCTAssertEqualObjects([company.employees valueForKey:@"age"], @[]);
// managed
NSMutableArray *ages = [NSMutableArray array];
[realm beginWriteTransaction];
for (int i = 0; i < 30; ++i) {
[ages addObject:@(i)];
EmployeeObject *eo = [EmployeeObject createInRealm:realm withValue:@{@"name": @"Joe", @"age": @(i), @"hired": @YES}];
[company.employees addObject:eo];
}
[realm commitWriteTransaction];
RLM_GENERIC_ARRAY(EmployeeObject) *employeeObjects = [company valueForKey:@"employees"];
NSMutableArray *kvcAgeProperties = [NSMutableArray array];
for (EmployeeObject *employee in employeeObjects) {
[kvcAgeProperties addObject:@(employee.age)];
}
XCTAssertEqualObjects(kvcAgeProperties, ages);
XCTAssertEqualObjects([company.employees valueForKey:@"age"], ages);
XCTAssertTrue([[[company.employees valueForKey:@"self"] firstObject] isEqualToObject:company.employees.firstObject]);
XCTAssertTrue([[[company.employees valueForKey:@"self"] lastObject] isEqualToObject:company.employees.lastObject]);
XCTAssertEqual([[company.employees valueForKeyPath:@"@count"] integerValue], 30);
XCTAssertEqual([[company.employees valueForKeyPath:@"@min.age"] integerValue], 0);
XCTAssertEqual([[company.employees valueForKeyPath:@"@max.age"] integerValue], 29);
XCTAssertEqualWithAccuracy([[company.employees valueForKeyPath:@"@avg.age"] doubleValue], 14.5, 0.1f);
XCTAssertEqualObjects([company.employees valueForKeyPath:@"@unionOfObjects.age"],
(@[@0, @1, @2, @3, @4, @5, @6, @7, @8, @9, @10, @11, @12, @13, @14, @15, @16, @17, @18, @19, @20, @21, @22, @23, @24, @25, @26, @27, @28, @29]));
XCTAssertEqualObjects([company.employees valueForKeyPath:@"@distinctUnionOfObjects.name"], (@[@"Joe"]));
RLMAssertThrowsWithReasonMatching([company.employees valueForKeyPath:@"@sum.dogs.@sum.age"], @"Nested key paths.*not supported");
// unmanaged object
company = [[CompanyObject alloc] init];
ages = [NSMutableArray array];
for (int i = 0; i < 30; ++i) {
[ages addObject:@(i)];
EmployeeObject *eo = [[EmployeeObject alloc] initWithValue:@{@"name": @"Joe", @"age": @(i), @"hired": @YES}];
[company.employees addObject:eo];
}
XCTAssertEqualObjects([company.employees valueForKey:@"age"], ages);
XCTAssertTrue([[[company.employees valueForKey:@"self"] firstObject] isEqualToObject:company.employees.firstObject]);
XCTAssertTrue([[[company.employees valueForKey:@"self"] lastObject] isEqualToObject:company.employees.lastObject]);
XCTAssertEqual([[company.employees valueForKeyPath:@"@count"] integerValue], 30);
XCTAssertEqual([[company.employees valueForKeyPath:@"@min.age"] integerValue], 0);
XCTAssertEqual([[company.employees valueForKeyPath:@"@max.age"] integerValue], 29);
XCTAssertEqualWithAccuracy([[company.employees valueForKeyPath:@"@avg.age"] doubleValue], 14.5, 0.1f);
XCTAssertEqualObjects([company.employees valueForKeyPath:@"@unionOfObjects.age"],
(@[@0, @1, @2, @3, @4, @5, @6, @7, @8, @9, @10, @11, @12, @13, @14, @15, @16, @17, @18, @19, @20, @21, @22, @23, @24, @25, @26, @27, @28, @29]));
XCTAssertEqualObjects([company.employees valueForKeyPath:@"@distinctUnionOfObjects.name"], (@[@"Joe"]));
RLMAssertThrowsWithReasonMatching([company.employees valueForKeyPath:@"@sum.dogs.@sum.age"], @"Nested key paths.*not supported");
}
- (void)testSetValueForKey {
RLMRealm *realm = self.realmWithTestPath;
[realm beginWriteTransaction];
CompanyObject *company = [[CompanyObject alloc] init];
company.name = @"name";
[company.employees setValue:@"name" forKey:@"name"];
XCTAssertEqualObjects([company.employees valueForKey:@"name"], @[]);
[realm addObject:company];
[realm commitWriteTransaction];
XCTAssertThrows([company.employees setValue:@10 forKey:@"age"]);
XCTAssertEqualObjects([company.employees valueForKey:@"age"], @[]);
// managed
NSMutableArray *ages = [NSMutableArray array];
[realm beginWriteTransaction];
for (int i = 0; i < 30; ++i) {
[ages addObject:@(20)];
EmployeeObject *eo = [EmployeeObject createInRealm:realm withValue:@{@"name": @"Joe", @"age": @(i), @"hired": @YES}];
[company.employees addObject:eo];
}
[company.employees setValue:@20 forKey:@"age"];
[realm commitWriteTransaction];
XCTAssertEqualObjects([company.employees valueForKey:@"age"], ages);
// unmanaged object
company = [[CompanyObject alloc] init];
ages = [NSMutableArray array];
for (int i = 0; i < 30; ++i) {
[ages addObject:@(20)];
EmployeeObject *eo = [[EmployeeObject alloc] initWithValue:@{@"name": @"Joe", @"age": @(i), @"hired": @YES}];
[company.employees addObject:eo];
}
[company.employees setValue:@20 forKey:@"age"];
XCTAssertEqualObjects([company.employees valueForKey:@"age"], ages);
}
- (void)testObjectAggregate {
RLMRealm *realm = [RLMRealm defaultRealm];
AggregateArrayObject *obj = [AggregateArrayObject new];
XCTAssertEqual(0, [obj.array sumOfProperty:@"intCol"].intValue);
XCTAssertNil([obj.array averageOfProperty:@"intCol"]);
XCTAssertNil([obj.array minOfProperty:@"intCol"]);
XCTAssertNil([obj.array maxOfProperty:@"intCol"]);
NSDate *dateMinInput = [NSDate date];
NSDate *dateMaxInput = [dateMinInput dateByAddingTimeInterval:1000];
[realm transactionWithBlock:^{
[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]];
[obj.array addObjects:[AggregateObject allObjectsInRealm:realm]];
}];
void (^test)(void) = ^{
RLMArray *array = obj.array;
// SUM
XCTAssertEqual([array sumOfProperty:@"intCol"].integerValue, 4);
XCTAssertEqualWithAccuracy([array sumOfProperty:@"floatCol"].floatValue, 7.2f, 0.1f);
XCTAssertEqualWithAccuracy([array sumOfProperty:@"doubleCol"].doubleValue, 10.0, 0.1f);
RLMAssertThrowsWithReasonMatching([array sumOfProperty:@"foo"], @"foo.*AggregateObject");
RLMAssertThrowsWithReasonMatching([array sumOfProperty:@"boolCol"], @"sum.*bool");
RLMAssertThrowsWithReasonMatching([array sumOfProperty:@"dateCol"], @"sum.*date");
// Average
XCTAssertEqualWithAccuracy([array averageOfProperty:@"intCol"].doubleValue, 0.4, 0.1f);
XCTAssertEqualWithAccuracy([array averageOfProperty:@"floatCol"].doubleValue, 0.72, 0.1f);
XCTAssertEqualWithAccuracy([array averageOfProperty:@"doubleCol"].doubleValue, 1.0, 0.1f);
RLMAssertThrowsWithReasonMatching([array averageOfProperty:@"foo"], @"foo.*AggregateObject");
RLMAssertThrowsWithReasonMatching([array averageOfProperty:@"boolCol"], @"average.*bool");
RLMAssertThrowsWithReasonMatching([array averageOfProperty:@"dateCol"], @"average.*date");
// MIN
XCTAssertEqual(0, [[array minOfProperty:@"intCol"] intValue]);
XCTAssertEqual(0.0f, [[array minOfProperty:@"floatCol"] floatValue]);
XCTAssertEqual(0.0, [[array minOfProperty:@"doubleCol"] doubleValue]);
XCTAssertEqualObjects(dateMinInput, [array minOfProperty:@"dateCol"]);
RLMAssertThrowsWithReasonMatching([array minOfProperty:@"foo"], @"foo.*AggregateObject");
RLMAssertThrowsWithReasonMatching([array minOfProperty:@"boolCol"], @"min.*bool");
// MAX
XCTAssertEqual(1, [[array maxOfProperty:@"intCol"] intValue]);
XCTAssertEqual(1.2f, [[array maxOfProperty:@"floatCol"] floatValue]);
XCTAssertEqual(2.5, [[array maxOfProperty:@"doubleCol"] doubleValue]);
XCTAssertEqualObjects(dateMaxInput, [array maxOfProperty:@"dateCol"]);
RLMAssertThrowsWithReasonMatching([array maxOfProperty:@"foo"], @"foo.*AggregateObject");
RLMAssertThrowsWithReasonMatching([array maxOfProperty:@"boolCol"], @"max.*bool");
};
test();
[realm transactionWithBlock:^{ [realm addObject:obj]; }];
test();
}
- (void)testRenamedPropertyAggregate {
RLMRealm *realm = [RLMRealm defaultRealm];
LinkToRenamedProperties1 *obj = [LinkToRenamedProperties1 new];
XCTAssertEqual(0, [obj.array sumOfProperty:@"propA"].intValue);
XCTAssertNil([obj.array averageOfProperty:@"propA"]);
XCTAssertNil([obj.array minOfProperty:@"propA"]);
XCTAssertNil([obj.array maxOfProperty:@"propA"]);
XCTAssertThrows([obj.array sumOfProperty:@"prop 1"]);
[realm transactionWithBlock:^{
[RenamedProperties1 createInRealm:realm withValue:@[@1, @""]];
[RenamedProperties1 createInRealm:realm withValue:@[@2, @""]];
[RenamedProperties1 createInRealm:realm withValue:@[@3, @""]];
[obj.array addObjects:[RenamedProperties1 allObjectsInRealm:realm]];
}];
XCTAssertEqual(6, [obj.array sumOfProperty:@"propA"].intValue);
XCTAssertEqual(2.0, [obj.array averageOfProperty:@"propA"].doubleValue);
XCTAssertEqual(1, [[obj.array minOfProperty:@"propA"] intValue]);
XCTAssertEqual(3, [[obj.array maxOfProperty:@"propA"] intValue]);
[realm transactionWithBlock:^{ [realm addObject:obj]; }];
XCTAssertEqual(6, [obj.array sumOfProperty:@"propA"].intValue);
XCTAssertEqual(2.0, [obj.array averageOfProperty:@"propA"].doubleValue);
XCTAssertEqual(1, [[obj.array minOfProperty:@"propA"] intValue]);
XCTAssertEqual(3, [[obj.array maxOfProperty:@"propA"] intValue]);
}
- (void)testValueForCollectionOperationKeyPath
{
RLMRealm *realm = [RLMRealm defaultRealm];
[realm beginWriteTransaction];
EmployeeObject *e1 = [PrimaryEmployeeObject createInRealm:realm withValue:@{@"name": @"A", @"age": @20, @"hired": @YES}];
EmployeeObject *e2 = [PrimaryEmployeeObject createInRealm:realm withValue:@{@"name": @"B", @"age": @30, @"hired": @NO}];
EmployeeObject *e3 = [PrimaryEmployeeObject createInRealm:realm withValue:@{@"name": @"C", @"age": @40, @"hired": @YES}];
EmployeeObject *e4 = [PrimaryEmployeeObject createInRealm:realm withValue:@{@"name": @"D", @"age": @50, @"hired": @YES}];
PrimaryCompanyObject *c1 = [PrimaryCompanyObject createInRealm:realm withValue:@{@"name": @"ABC AG", @"employees": @[e1, e2, e3, e2]}];
PrimaryCompanyObject *c2 = [PrimaryCompanyObject createInRealm:realm withValue:@{@"name": @"ABC AG 2", @"employees": @[e1, e4]}];
ArrayOfPrimaryCompanies *companies = [ArrayOfPrimaryCompanies createInRealm:realm withValue:@[@[c1, c2]]];
[realm commitWriteTransaction];
// count operator
XCTAssertEqual([[c1.employees valueForKeyPath:@"@count"] integerValue], 4);
// numeric operators
XCTAssertEqual([[c1.employees valueForKeyPath:@"@min.age"] intValue], 20);
XCTAssertEqual([[c1.employees valueForKeyPath:@"@max.age"] intValue], 40);
XCTAssertEqual([[c1.employees valueForKeyPath:@"@sum.age"] integerValue], 120);
XCTAssertEqualWithAccuracy([[c1.employees valueForKeyPath:@"@avg.age"] doubleValue], 30, 0.1f);
// collection
XCTAssertEqualObjects([c1.employees valueForKeyPath:@"@unionOfObjects.name"],
(@[@"A", @"B", @"C", @"B"]));
XCTAssertEqualObjects([[c1.employees valueForKeyPath:@"@distinctUnionOfObjects.name"] sortedArrayUsingSelector:@selector(compare:)],
(@[@"A", @"B", @"C"]));
XCTAssertEqualObjects([companies.companies valueForKeyPath:@"@unionOfArrays.employees"],
(@[e1, e2, e3, e2, e1, e4]));
NSComparator cmp = ^NSComparisonResult(id obj1, id obj2) { return [[obj1 name] compare:[obj2 name]]; };
XCTAssertEqualObjects([[companies.companies valueForKeyPath:@"@distinctUnionOfArrays.employees"] sortedArrayUsingComparator:cmp],
(@[e1, e2, e3, e4]));
// invalid key paths
RLMAssertThrowsWithReasonMatching([c1.employees valueForKeyPath:@"@invalid.name"],
@"Unsupported KVC collection operator found in key path '@invalid.name'");
RLMAssertThrowsWithReasonMatching([c1.employees valueForKeyPath:@"@sum"],
@"Missing key path for KVC collection operator sum in key path '@sum'");
RLMAssertThrowsWithReasonMatching([c1.employees valueForKeyPath:@"@sum."],
@"Missing key path for KVC collection operator sum in key path '@sum.'");
RLMAssertThrowsWithReasonMatching([c1.employees valueForKeyPath:@"@sum.employees.@sum.age"],
@"Nested key paths.*not supported");
}
- (void)testCrossThreadAccess
{
CompanyObject *company = [[CompanyObject alloc] init];
company.name = @"name";
EmployeeObject *eo = [[EmployeeObject alloc] init];
eo.name = @"Joe";
eo.age = 40;
eo.hired = YES;
[company.employees addObject:eo];
RLMArray *employees = company.employees;
// Unmanaged object can be accessed from other threads
[self dispatchAsyncAndWait:^{
XCTAssertNoThrow(company.employees);
XCTAssertNoThrow([employees lastObject]);
}];
[RLMRealm.defaultRealm beginWriteTransaction];
[RLMRealm.defaultRealm addObject:company];
[RLMRealm.defaultRealm commitWriteTransaction];
employees = company.employees;
XCTAssertNoThrow(company.employees);
XCTAssertNoThrow([employees lastObject]);
[self dispatchAsyncAndWait:^{
XCTAssertThrows(company.employees);
XCTAssertThrows([employees lastObject]);
}];
}
- (void)testSortByNoColumns {
RLMRealm *realm = [RLMRealm defaultRealm];
[realm beginWriteTransaction];
DogObject *a2 = [DogObject createInDefaultRealmWithValue:@[@"a", @2]];
DogObject *b1 = [DogObject createInDefaultRealmWithValue:@[@"b", @1]];
DogObject *a1 = [DogObject createInDefaultRealmWithValue:@[@"a", @1]];
DogObject *b2 = [DogObject createInDefaultRealmWithValue:@[@"b", @2]];
RLMArray<DogObject *> *array = [DogArrayObject createInDefaultRealmWithValue:@[@[a2, b1, a1, b2]]].dogs;
[realm commitWriteTransaction];
RLMResults *notActuallySorted = [array sortedResultsUsingDescriptors:@[]];
XCTAssertTrue([array[0] isEqualToObject:notActuallySorted[0]]);
XCTAssertTrue([array[1] isEqualToObject:notActuallySorted[1]]);
XCTAssertTrue([array[2] isEqualToObject:notActuallySorted[2]]);
XCTAssertTrue([array[3] isEqualToObject:notActuallySorted[3]]);
}
- (void)testSortByMultipleColumns {
RLMRealm *realm = [RLMRealm defaultRealm];
[realm beginWriteTransaction];
DogObject *a1 = [DogObject createInDefaultRealmWithValue:@[@"a", @1]];
DogObject *a2 = [DogObject createInDefaultRealmWithValue:@[@"a", @2]];
DogObject *b1 = [DogObject createInDefaultRealmWithValue:@[@"b", @1]];
DogObject *b2 = [DogObject createInDefaultRealmWithValue:@[@"b", @2]];
DogArrayObject *array = [DogArrayObject createInDefaultRealmWithValue:@[@[a1, a2, b1, b2]]];
[realm commitWriteTransaction];
bool (^checkOrder)(NSArray *, NSArray *, NSArray *) = ^bool(NSArray *properties, NSArray *ascending, NSArray *dogs) {
NSArray *sort = @[[RLMSortDescriptor sortDescriptorWithKeyPath:properties[0] ascending:[ascending[0] boolValue]],
[RLMSortDescriptor sortDescriptorWithKeyPath:properties[1] ascending:[ascending[1] boolValue]]];
RLMResults *actual = [array.dogs sortedResultsUsingDescriptors:sort];
return [actual[0] isEqualToObject:dogs[0]]
&& [actual[1] isEqualToObject:dogs[1]]
&& [actual[2] isEqualToObject:dogs[2]]
&& [actual[3] isEqualToObject:dogs[3]];
};
// Check each valid sort
XCTAssertTrue(checkOrder(@[@"dogName", @"age"], @[@YES, @YES], @[a1, a2, b1, b2]));
XCTAssertTrue(checkOrder(@[@"dogName", @"age"], @[@YES, @NO], @[a2, a1, b2, b1]));
XCTAssertTrue(checkOrder(@[@"dogName", @"age"], @[@NO, @YES], @[b1, b2, a1, a2]));
XCTAssertTrue(checkOrder(@[@"dogName", @"age"], @[@NO, @NO], @[b2, b1, a2, a1]));
XCTAssertTrue(checkOrder(@[@"age", @"dogName"], @[@YES, @YES], @[a1, b1, a2, b2]));
XCTAssertTrue(checkOrder(@[@"age", @"dogName"], @[@YES, @NO], @[b1, a1, b2, a2]));
XCTAssertTrue(checkOrder(@[@"age", @"dogName"], @[@NO, @YES], @[a2, b2, a1, b1]));
XCTAssertTrue(checkOrder(@[@"age", @"dogName"], @[@NO, @NO], @[b2, a2, b1, a1]));
}
- (void)testSortByRenamedColumns {
RLMRealm *realm = [RLMRealm defaultRealm];
[realm beginWriteTransaction];
id value = @{@"array": @[@[@1, @"c"], @[@2, @"b"], @[@3, @"a"]]};
LinkToRenamedProperties1 *obj = [LinkToRenamedProperties1 createInRealm:realm withValue:value];
// FIXME: sorting has to use the column names because the parsing is done by
// the object store. This is not ideal.
XCTAssertEqualObjects([[obj.array sortedResultsUsingKeyPath:@"prop 1" ascending:YES] valueForKeyPath:@"propA"],
(@[@1, @2, @3]));
XCTAssertEqualObjects([[obj.array sortedResultsUsingKeyPath:@"prop 2" ascending:YES] valueForKeyPath:@"propA"],
(@[@3, @2, @1]));
LinkToRenamedProperties2 *obj2 = [LinkToRenamedProperties2 allObjectsInRealm:realm].firstObject;
XCTAssertEqualObjects([[obj2.array sortedResultsUsingKeyPath:@"prop 1" ascending:YES] valueForKeyPath:@"propC"],
(@[@1, @2, @3]));
XCTAssertEqualObjects([[obj2.array sortedResultsUsingKeyPath:@"prop 2" ascending:YES] valueForKeyPath:@"propC"],
(@[@3, @2, @1]));
[realm cancelWriteTransaction];
}
- (void)testDeleteLinksAndObjectsInArray
{
RLMRealm *realm = [RLMRealm defaultRealm];
[realm beginWriteTransaction];
EmployeeObject *po1 = [EmployeeObject createInRealm:realm withValue:@[@"Joe", @40, @YES]];
EmployeeObject *po2 = [EmployeeObject createInRealm:realm withValue:@[@"John", @30, @NO]];
EmployeeObject *po3 = [EmployeeObject createInRealm:realm withValue:@[@"Jill", @25, @YES]];
CompanyObject *company = [[CompanyObject alloc] init];
company.name = @"name";
[company.employees addObjects:[EmployeeObject allObjects]];
[realm addObject:company];
[realm commitWriteTransaction];
RLMArray *peopleInCompany = company.employees;
// Delete link to employee
XCTAssertThrowsSpecificNamed([peopleInCompany removeObjectAtIndex:1], NSException, @"RLMException", @"Not allowed in read transaction");
XCTAssertEqual(peopleInCompany.count, 3U, @"No links should have been deleted");
[realm beginWriteTransaction];
XCTAssertThrowsSpecificNamed([peopleInCompany removeObjectAtIndex:3], NSException, @"RLMException", @"Out of bounds");
XCTAssertNoThrow([peopleInCompany removeObjectAtIndex:1], @"Should delete link to employee");
[realm commitWriteTransaction];
XCTAssertEqual(peopleInCompany.count, 2U, @"link deleted when accessing via links");
EmployeeObject *test = peopleInCompany[0];
XCTAssertEqual(test.age, po1.age, @"Should be equal");
XCTAssertEqualObjects(test.name, po1.name, @"Should be equal");
XCTAssertEqual(test.hired, po1.hired, @"Should be equal");
XCTAssertTrue([test isEqualToObject:po1], @"Should be equal");
test = peopleInCompany[1];
XCTAssertEqual(test.age, po3.age, @"Should be equal");
XCTAssertEqualObjects(test.name, po3.name, @"Should be equal");
XCTAssertEqual(test.hired, po3.hired, @"Should be equal");
XCTAssertTrue([test isEqualToObject:po3], @"Should be equal");
XCTAssertThrowsSpecificNamed([peopleInCompany removeLastObject], NSException, @"RLMException", @"Not allowed in read transaction");
XCTAssertThrowsSpecificNamed([peopleInCompany removeAllObjects], NSException, @"RLMException", @"Not allowed in read transaction");
XCTAssertThrowsSpecificNamed([peopleInCompany replaceObjectAtIndex:0 withObject:po2], NSException, @"RLMException", @"Not allowed in read transaction");
XCTAssertThrowsSpecificNamed([peopleInCompany insertObject:po2 atIndex:0], NSException, @"RLMException", @"Not allowed in read transaction");
[realm beginWriteTransaction];
XCTAssertNoThrow([peopleInCompany removeLastObject], @"Should delete last link");
XCTAssertEqual(peopleInCompany.count, 1U, @"1 remaining link");
[peopleInCompany replaceObjectAtIndex:0 withObject:po2];
XCTAssertEqual(peopleInCompany.count, 1U, @"1 link replaced");
[peopleInCompany insertObject:po1 atIndex:0];
XCTAssertEqual(peopleInCompany.count, 2U, @"2 links");
XCTAssertNoThrow([peopleInCompany removeAllObjects], @"Should delete all links");
XCTAssertEqual(peopleInCompany.count, 0U, @"0 remaining links");
[realm commitWriteTransaction];
RLMResults *allPeople = [EmployeeObject allObjects];
XCTAssertEqual(allPeople.count, 3U, @"Only links should have been deleted, not the employees");
}
- (void)testArrayDescription {
RLMRealm *realm = [RLMRealm defaultRealm];
[realm beginWriteTransaction];
RLMArray<EmployeeObject *> *employees = [CompanyObject createInDefaultRealmWithValue:@[@"company"]].employees;
RLMArray<NSNumber *> *ints = [AllPrimitiveArrays createInDefaultRealmWithValue:@[]].intObj;
for (NSInteger i = 0; i < 1012; ++i) {
EmployeeObject *person = [[EmployeeObject alloc] init];
person.name = @"Mary";
person.age = 24;
person.hired = YES;
[employees addObject:person];
[ints addObject:@(i + 100)];
}
[realm commitWriteTransaction];
RLMAssertMatches(employees.description,
@"(?s)RLMArray\\<EmployeeObject\\> \\<0x[a-z0-9]+\\> \\(\n"
@"\t\\[0\\] EmployeeObject \\{\n"
@"\t\tname = Mary;\n"
@"\t\tage = 24;\n"
@"\t\thired = 1;\n"
@"\t\\},\n"
@".*\n"
@"\t... 912 objects skipped.\n"
@"\\)");
RLMAssertMatches(ints.description,
@"(?s)RLMArray\\<int\\> \\<0x[a-z0-9]+\\> \\(\n"
@"\t\\[0\\] 100,\n"
@"\t\\[1\\] 101,\n"
@"\t\\[2\\] 102,\n"
@".*\n"
@"\t... 912 objects skipped.\n"
@"\\)");
}
- (void)testUnmanagedAssignment {
IntObject *io1 = [[IntObject alloc] init];
IntObject *io2 = [[IntObject alloc] init];
IntObject *io3 = [[IntObject alloc] init];
ArrayPropertyObject *array1 = [[ArrayPropertyObject alloc] init];
ArrayPropertyObject *array2 = [[ArrayPropertyObject alloc] init];
// Assigning NSArray shallow copies
array1.intArray = (id)@[io1, io2];
XCTAssertEqualObjects([array1.intArray valueForKey:@"self"], (@[io1, io2]));
[array1 setValue:@[io3, io1] forKey:@"intArray"];
XCTAssertEqualObjects([array1.intArray valueForKey:@"self"], (@[io3, io1]));
array1[@"intArray"] = @[io2, io3];
XCTAssertEqualObjects([array1.intArray valueForKey:@"self"], (@[io2, io3]));
// Assigning RLMArray shallow copies
array2.intArray = array1.intArray;
XCTAssertEqualObjects([array2.intArray valueForKey:@"self"], (@[io2, io3]));
[array1.intArray removeAllObjects];
XCTAssertEqualObjects([array2.intArray valueForKey:@"self"], (@[io2, io3]));
// Self-assignment is a no-op
array2.intArray = array2.intArray;
XCTAssertEqualObjects([array2.intArray valueForKey:@"self"], (@[io2, io3]));
array2[@"intArray"] = array2[@"intArray"];
XCTAssertEqualObjects([array2[@"intArray"] valueForKey:@"self"], (@[io2, io3]));
}
- (void)testManagedAssignment {
RLMRealm *realm = self.realmWithTestPath;
[realm beginWriteTransaction];
IntObject *io1 = [IntObject createInRealm:realm withValue:@[@1]];
IntObject *io2 = [IntObject createInRealm:realm withValue:@[@2]];
IntObject *io3 = [IntObject createInRealm:realm withValue:@[@3]];
ArrayPropertyObject *array1 = [ArrayPropertyObject createInRealm:realm withValue:@[@""]];
ArrayPropertyObject *array2 = [ArrayPropertyObject createInRealm:realm withValue:@[@""]];
// Assigning NSArray shallow copies
array1.intArray = (id)@[io1, io2];
XCTAssertEqualObjects([array1.intArray valueForKey:@"intCol"], (@[@1, @2]));
[array1 setValue:@[io3, io1] forKey:@"intArray"];
XCTAssertEqualObjects([array1.intArray valueForKey:@"intCol"], (@[@3, @1]));
array1[@"intArray"] = @[io2, io3];
XCTAssertEqualObjects([array1.intArray valueForKey:@"intCol"], (@[@2, @3]));
// Assigning RLMArray shallow copies
array2.intArray = array1.intArray;
XCTAssertEqualObjects([array2.intArray valueForKey:@"intCol"], (@[@2, @3]));
[array1.intArray removeAllObjects];
XCTAssertEqualObjects([array2.intArray valueForKey:@"intCol"], (@[@2, @3]));
// Self-assignment is a no-op
array2.intArray = array2.intArray;
XCTAssertEqualObjects([array2.intArray valueForKey:@"intCol"], (@[@2, @3]));
array2[@"intArray"] = array2[@"intArray"];
XCTAssertEqualObjects([array2[@"intArray"] valueForKey:@"intCol"], (@[@2, @3]));
[realm cancelWriteTransaction];
}
- (void)testAssignIncorrectType {
RLMRealm *realm = self.realmWithTestPath;
[realm beginWriteTransaction];
ArrayPropertyObject *array = [ArrayPropertyObject createInRealm:realm
withValue:@[@"", @[@[@"a"]], @[@[@0]]]];
RLMAssertThrowsWithReason(array.intArray = (id)array.array,
@"RLMArray<StringObject> does not match expected type 'IntObject' for property 'ArrayPropertyObject.intArray'.");
RLMAssertThrowsWithReason(array[@"intArray"] = array[@"array"],
@"RLMArray<StringObject> does not match expected type 'IntObject' for property 'ArrayPropertyObject.intArray'.");
[realm cancelWriteTransaction];
}
- (void)testNotificationSentInitially {
RLMRealm *realm = self.realmWithTestPath;
[realm beginWriteTransaction];
ArrayPropertyObject *array = [ArrayPropertyObject createInRealm:realm withValue:@[@"", @[], @[]]];
[realm commitWriteTransaction];
id expectation = [self expectationWithDescription:@""];
id token = [array.array addNotificationBlock:^(RLMArray *array, RLMCollectionChange *change, NSError *error) {
XCTAssertNotNil(array);
XCTAssertNil(change);
XCTAssertNil(error);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:2.0 handler:nil];
[(RLMNotificationToken *)token invalidate];
}
- (void)testNotificationSentAfterCommit {
RLMRealm *realm = self.realmWithTestPath;
[realm beginWriteTransaction];
ArrayPropertyObject *array = [ArrayPropertyObject createInRealm:realm withValue:@[@"", @[], @[]]];
[realm commitWriteTransaction];
__block bool first = true;
__block id expectation = [self expectationWithDescription:@""];
id token = [array.array addNotificationBlock:^(RLMArray *array, RLMCollectionChange *change, NSError *error) {
XCTAssertNotNil(array);
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:^{
RLMArray *array = [(ArrayPropertyObject *)[ArrayPropertyObject allObjectsInRealm:realm].firstObject array];
[array addObject:[[StringObject alloc] init]];
}];
}];
[self waitForExpectationsWithTimeout:2.0 handler:nil];
[(RLMNotificationToken *)token invalidate];
}
- (void)testNotificationNotSentForUnrelatedChange {
RLMRealm *realm = self.realmWithTestPath;
[realm beginWriteTransaction];
ArrayPropertyObject *array = [ArrayPropertyObject createInRealm:realm withValue:@[@"", @[], @[]]];
[realm commitWriteTransaction];
id expectation = [self expectationWithDescription:@""];
id token = [array.array addNotificationBlock:^(__unused RLMArray *array, __unused RLMCollectionChange *change, __unused NSError *error) {
// 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:^{
RLMRealm *realm = self.realmWithTestPath;
[realm transactionWithBlock:^{
[ArrayPropertyObject createInRealm:realm withValue:@[@"", @[], @[]]];
}];
}];
}];
[(RLMNotificationToken *)token invalidate];
}
- (void)testNotificationSentOnlyForActualRefresh {
RLMRealm *realm = self.realmWithTestPath;
[realm beginWriteTransaction];
ArrayPropertyObject *array = [ArrayPropertyObject createInRealm:realm withValue:@[@"", @[], @[]]];
[realm commitWriteTransaction];
__block id expectation = [self expectationWithDescription:@""];
id token = [array.array addNotificationBlock:^(RLMArray *array, __unused RLMCollectionChange *change, NSError *error) {
XCTAssertNotNil(array);
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:^{
RLMArray *array = [(ArrayPropertyObject *)[ArrayPropertyObject allObjectsInRealm:realm].firstObject array];
[array addObject:[[StringObject alloc] init]];
}];
}];
}];
expectation = [self expectationWithDescription:@""];
[realm refresh];
[self waitForExpectationsWithTimeout:2.0 handler:nil];
[(RLMNotificationToken *)token invalidate];
}
- (void)testDeletingObjectWithNotificationsRegistered {
RLMRealm *realm = self.realmWithTestPath;
[realm beginWriteTransaction];
ArrayPropertyObject *array = [ArrayPropertyObject createInRealm:realm withValue:@[@"", @[], @[]]];
[realm commitWriteTransaction];
__block id expectation = [self expectationWithDescription:@""];
id token = [array.array addNotificationBlock:^(RLMArray *array, __unused RLMCollectionChange *change, NSError *error) {
XCTAssertNotNil(array);
XCTAssertNil(error);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:2.0 handler:nil];
[realm beginWriteTransaction];
[realm deleteObject:array];
[realm commitWriteTransaction];
[(RLMNotificationToken *)token invalidate];
}
- (void)testAllMethodsCheckThread {
RLMRealm *realm = [RLMRealm defaultRealm];
__block IntObject *io;
__block RLMArray *array;
[realm transactionWithBlock:^{
io = [IntObject createInDefaultRealmWithValue:@[@0]];
ArrayPropertyObject *obj = [ArrayPropertyObject createInDefaultRealmWithValue:@[@"", @[], @[io]]];
array = obj.intArray;
}];
[realm beginWriteTransaction];
[self dispatchAsyncAndWait:^{
RLMAssertThrowsWithReasonMatching([array count], @"thread");
RLMAssertThrowsWithReasonMatching([array objectAtIndex:0], @"thread");
RLMAssertThrowsWithReasonMatching([array firstObject], @"thread");
RLMAssertThrowsWithReasonMatching([array lastObject], @"thread");
RLMAssertThrowsWithReasonMatching([array addObject:io], @"thread");
RLMAssertThrowsWithReasonMatching([array addObjects:@[io]], @"thread");
RLMAssertThrowsWithReasonMatching([array insertObject:io atIndex:0], @"thread");
RLMAssertThrowsWithReasonMatching([array removeObjectAtIndex:0], @"thread");
RLMAssertThrowsWithReasonMatching([array removeLastObject], @"thread");
RLMAssertThrowsWithReasonMatching([array removeAllObjects], @"thread");
RLMAssertThrowsWithReasonMatching([array replaceObjectAtIndex:0 withObject:io], @"thread");
RLMAssertThrowsWithReasonMatching([array moveObjectAtIndex:0 toIndex:1], @"thread");
RLMAssertThrowsWithReasonMatching([array exchangeObjectAtIndex:0 withObjectAtIndex:1], @"thread");
RLMAssertThrowsWithReasonMatching([array indexOfObject:[IntObject allObjects].firstObject], @"thread");
RLMAssertThrowsWithReasonMatching([array indexOfObjectWhere:@"intCol = 0"], @"thread");
RLMAssertThrowsWithReasonMatching([array indexOfObjectWithPredicate:[NSPredicate predicateWithFormat:@"intCol = 0"]], @"thread");
RLMAssertThrowsWithReasonMatching([array objectsWhere:@"intCol = 0"], @"thread");
RLMAssertThrowsWithReasonMatching([array objectsWithPredicate:[NSPredicate predicateWithFormat:@"intCol = 0"]], @"thread");
RLMAssertThrowsWithReasonMatching([array sortedResultsUsingKeyPath:@"intCol" ascending:YES], @"thread");
RLMAssertThrowsWithReasonMatching([array sortedResultsUsingDescriptors:@[[RLMSortDescriptor sortDescriptorWithKeyPath:@"intCol" ascending:YES]]], @"thread");
RLMAssertThrowsWithReasonMatching(array[0], @"thread");
RLMAssertThrowsWithReasonMatching(array[0] = io, @"thread");
RLMAssertThrowsWithReasonMatching([array valueForKey:@"intCol"], @"thread");
RLMAssertThrowsWithReasonMatching([array setValue:@1 forKey:@"intCol"], @"thread");
RLMAssertThrowsWithReasonMatching({for (__unused id obj in array);}, @"thread");
}];
[realm cancelWriteTransaction];
}
- (void)testAllMethodsCheckForInvalidation {
RLMRealm *realm = [RLMRealm defaultRealm];
__block IntObject *io;
__block RLMArray *array;
[realm transactionWithBlock:^{
io = [IntObject createInDefaultRealmWithValue:@[@0]];
ArrayPropertyObject *obj = [ArrayPropertyObject createInDefaultRealmWithValue:@[@"", @[], @[io]]];
array = obj.intArray;
}];
[realm beginWriteTransaction];
XCTAssertNoThrow([array objectClassName]);
XCTAssertNoThrow([array realm]);
XCTAssertNoThrow([array isInvalidated]);
XCTAssertNoThrow([array count]);
XCTAssertNoThrow([array objectAtIndex:0]);
XCTAssertNoThrow([array firstObject]);
XCTAssertNoThrow([array lastObject]);
XCTAssertNoThrow([array addObject:io]);
XCTAssertNoThrow([array addObjects:@[io]]);
XCTAssertNoThrow([array insertObject:io atIndex:0]);
XCTAssertNoThrow([array removeObjectAtIndex:0]);
XCTAssertNoThrow([array removeLastObject]);
XCTAssertNoThrow([array removeAllObjects]);
[array addObjects:@[io, io, io]];
XCTAssertNoThrow([array replaceObjectAtIndex:0 withObject:io]);
XCTAssertNoThrow([array moveObjectAtIndex:0 toIndex:1]);
XCTAssertNoThrow([array exchangeObjectAtIndex:0 withObjectAtIndex:1]);
XCTAssertNoThrow([array indexOfObject:[IntObject allObjects].firstObject]);
XCTAssertNoThrow([array indexOfObjectWhere:@"intCol = 0"]);
XCTAssertNoThrow([array indexOfObjectWithPredicate:[NSPredicate predicateWithFormat:@"intCol = 0"]]);
XCTAssertNoThrow([array objectsWhere:@"intCol = 0"]);
XCTAssertNoThrow([array objectsWithPredicate:[NSPredicate predicateWithFormat:@"intCol = 0"]]);
XCTAssertNoThrow([array sortedResultsUsingKeyPath:@"intCol" ascending:YES]);
XCTAssertNoThrow([array sortedResultsUsingDescriptors:@[[RLMSortDescriptor sortDescriptorWithKeyPath:@"intCol" ascending:YES]]]);
XCTAssertNoThrow(array[0]);
XCTAssertNoThrow(array[0] = io);
XCTAssertNoThrow([array valueForKey:@"intCol"]);
XCTAssertNoThrow([array setValue:@1 forKey:@"intCol"]);
XCTAssertNoThrow({for (__unused id obj in array);});
[realm cancelWriteTransaction];
[realm invalidate];
[realm beginWriteTransaction];
io = [IntObject createInDefaultRealmWithValue:@[@0]];
XCTAssertNoThrow([array objectClassName]);
XCTAssertNoThrow([array realm]);
XCTAssertNoThrow([array isInvalidated]);
RLMAssertThrowsWithReasonMatching([array count], @"invalidated");
RLMAssertThrowsWithReasonMatching([array objectAtIndex:0], @"invalidated");
RLMAssertThrowsWithReasonMatching([array firstObject], @"invalidated");
RLMAssertThrowsWithReasonMatching([array lastObject], @"invalidated");
RLMAssertThrowsWithReasonMatching([array addObject:io], @"invalidated");
RLMAssertThrowsWithReasonMatching([array addObjects:@[io]], @"invalidated");
RLMAssertThrowsWithReasonMatching([array insertObject:io atIndex:0], @"invalidated");
RLMAssertThrowsWithReasonMatching([array removeObjectAtIndex:0], @"invalidated");
RLMAssertThrowsWithReasonMatching([array removeLastObject], @"invalidated");
RLMAssertThrowsWithReasonMatching([array removeAllObjects], @"invalidated");
RLMAssertThrowsWithReasonMatching([array replaceObjectAtIndex:0 withObject:io], @"invalidated");
RLMAssertThrowsWithReasonMatching([array moveObjectAtIndex:0 toIndex:1], @"invalidated");
RLMAssertThrowsWithReasonMatching([array exchangeObjectAtIndex:0 withObjectAtIndex:1], @"invalidated");
RLMAssertThrowsWithReasonMatching([array indexOfObject:[IntObject allObjects].firstObject], @"invalidated");
RLMAssertThrowsWithReasonMatching([array indexOfObjectWhere:@"intCol = 0"], @"invalidated");
RLMAssertThrowsWithReasonMatching([array indexOfObjectWithPredicate:[NSPredicate predicateWithFormat:@"intCol = 0"]], @"invalidated");
RLMAssertThrowsWithReasonMatching([array objectsWhere:@"intCol = 0"], @"invalidated");
RLMAssertThrowsWithReasonMatching([array objectsWithPredicate:[NSPredicate predicateWithFormat:@"intCol = 0"]], @"invalidated");
RLMAssertThrowsWithReasonMatching([array sortedResultsUsingKeyPath:@"intCol" ascending:YES], @"invalidated");
RLMAssertThrowsWithReasonMatching([array sortedResultsUsingDescriptors:@[[RLMSortDescriptor sortDescriptorWithKeyPath:@"intCol" ascending:YES]]], @"invalidated");
RLMAssertThrowsWithReasonMatching(array[0], @"invalidated");
RLMAssertThrowsWithReasonMatching(array[0] = io, @"invalidated");
RLMAssertThrowsWithReasonMatching([array valueForKey:@"intCol"], @"invalidated");
RLMAssertThrowsWithReasonMatching([array setValue:@1 forKey:@"intCol"], @"invalidated");
RLMAssertThrowsWithReasonMatching({for (__unused id obj in array);}, @"invalidated");
[realm cancelWriteTransaction];
}
- (void)testMutatingMethodsCheckForWriteTransaction {
RLMRealm *realm = [RLMRealm defaultRealm];
__block IntObject *io;
__block RLMArray *array;
[realm transactionWithBlock:^{
io = [IntObject createInDefaultRealmWithValue:@[@0]];
ArrayPropertyObject *obj = [ArrayPropertyObject createInDefaultRealmWithValue:@[@"", @[], @[io]]];
array = obj.intArray;
}];
XCTAssertNoThrow([array objectClassName]);
XCTAssertNoThrow([array realm]);
XCTAssertNoThrow([array isInvalidated]);
XCTAssertNoThrow([array count]);
XCTAssertNoThrow([array objectAtIndex:0]);
XCTAssertNoThrow([array firstObject]);
XCTAssertNoThrow([array lastObject]);
XCTAssertNoThrow([array indexOfObject:[IntObject allObjects].firstObject]);
XCTAssertNoThrow([array indexOfObjectWhere:@"intCol = 0"]);
XCTAssertNoThrow([array indexOfObjectWithPredicate:[NSPredicate predicateWithFormat:@"intCol = 0"]]);
XCTAssertNoThrow([array objectsWhere:@"intCol = 0"]);
XCTAssertNoThrow([array objectsWithPredicate:[NSPredicate predicateWithFormat:@"intCol = 0"]]);
XCTAssertNoThrow([array sortedResultsUsingKeyPath:@"intCol" ascending:YES]);
XCTAssertNoThrow([array sortedResultsUsingDescriptors:@[[RLMSortDescriptor sortDescriptorWithKeyPath:@"intCol" ascending:YES]]]);
XCTAssertNoThrow(array[0]);
XCTAssertNoThrow([array valueForKey:@"intCol"]);
XCTAssertNoThrow({for (__unused id obj in array);});
RLMAssertThrowsWithReasonMatching([array addObject:io], @"write transaction");
RLMAssertThrowsWithReasonMatching([array addObjects:@[io]], @"write transaction");
RLMAssertThrowsWithReasonMatching([array insertObject:io atIndex:0], @"write transaction");
RLMAssertThrowsWithReasonMatching([array removeObjectAtIndex:0], @"write transaction");
RLMAssertThrowsWithReasonMatching([array removeLastObject], @"write transaction");
RLMAssertThrowsWithReasonMatching([array removeAllObjects], @"write transaction");
RLMAssertThrowsWithReasonMatching([array replaceObjectAtIndex:0 withObject:io], @"write transaction");
RLMAssertThrowsWithReasonMatching([array moveObjectAtIndex:0 toIndex:1], @"write transaction");
RLMAssertThrowsWithReasonMatching([array exchangeObjectAtIndex:0 withObjectAtIndex:1], @"write transaction");
RLMAssertThrowsWithReasonMatching(array[0] = io, @"write transaction");
RLMAssertThrowsWithReasonMatching([array setValue:@1 forKey:@"intCol"], @"write transaction");
}
@end