12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472 |
- #import "RLMTestCase.h"
- #import "RLMObjectSchema_Private.h"
- #import "RLMSchema_Private.h"
- #import <libkern/OSAtomic.h>
- #import <math.h>
- #import <objc/runtime.h>
- #import <stdalign.h>
- #pragma mark - Test Objects
- @interface DefaultObject : RLMObject
- @property int intCol;
- @property float floatCol;
- @property double doubleCol;
- @property BOOL boolCol;
- @property NSDate *dateCol;
- @property NSString *stringCol;
- @property NSData *binaryCol;
- @end
- @implementation DefaultObject
- + (NSDictionary *)defaultPropertyValues {
- NSString *binaryString = @"binary";
- NSData *binaryData = [binaryString dataUsingEncoding:NSUTF8StringEncoding];
- return @{@"intCol": @12,
- @"floatCol": @88.9f,
- @"doubleCol": @1002.892,
- @"boolCol": @YES,
- @"dateCol": [NSDate dateWithTimeIntervalSince1970:999999],
- @"stringCol": @"potato",
- @"binaryCol": binaryData};
- }
- @end
- @interface DynamicDefaultObject : RLMObject
- @property int intCol;
- @property float floatCol;
- @property double doubleCol;
- @property NSDate *dateCol;
- @property NSString *stringCol;
- @property NSData *binaryCol;
- @end
- @implementation DynamicDefaultObject
- + (BOOL)shouldIncludeInDefaultSchema {
- return NO;
- }
- + (NSDictionary *)defaultPropertyValues {
- static NSInteger dynamicDefaultSeed = 0;
- dynamicDefaultSeed++;
- return @{@"intCol": @(dynamicDefaultSeed),
- @"floatCol": @((float)dynamicDefaultSeed),
- @"doubleCol": @((double)dynamicDefaultSeed),
- @"dateCol": [NSDate dateWithTimeIntervalSince1970:dynamicDefaultSeed],
- @"stringCol": [[NSUUID UUID] UUIDString],
- @"binaryCol": [[[NSUUID UUID] UUIDString] dataUsingEncoding:NSUTF8StringEncoding]};
- }
- + (NSString *)primaryKey {
- return @"intCol";
- }
- @end
- @class CycleObject;
- RLM_ARRAY_TYPE(CycleObject)
- @interface CycleObject : RLMObject
- @property RLM_GENERIC_ARRAY(CycleObject) *objects;
- @end
- @implementation CycleObject
- @end
- @interface PrimaryStringObjectWrapper : RLMObject
- @property PrimaryStringObject *primaryStringObject;
- @end
- @implementation PrimaryStringObjectWrapper
- @end
- @interface PrimaryNestedObject : RLMObject
- @property int primaryCol;
- @property PrimaryStringObject *primaryStringObject;
- @property PrimaryStringObjectWrapper *primaryStringObjectWrapper;
- @property StringObject *stringObject;
- @property RLM_GENERIC_ARRAY(PrimaryIntObject) *primaryIntArray;
- @property NSString *stringCol;
- @end
- @implementation PrimaryNestedObject
- + (NSString *)primaryKey {
- return @"primaryCol";
- }
- + (NSDictionary *)defaultPropertyValues {
- return @{@"stringCol": @"default"};
- }
- @end
- @interface StringSubclassObject : StringObject
- @property NSString *stringCol2;
- @end
- @implementation StringSubclassObject
- @end
- @interface StringObjectNoThrow : StringObject
- @end
- @implementation StringObjectNoThrow
- - (id)valueForUndefinedKey:(__unused NSString *)key {
- return nil;
- }
- @end
- @interface StringSubclassObjectWithDefaults : StringObjectNoThrow
- @property NSString *stringCol2;
- @end
- @implementation StringSubclassObjectWithDefaults
- +(NSDictionary *)defaultPropertyValues {
- return @{@"stringCol2": @"default"};
- }
- @end
- @interface StringLinkObject : RLMObject
- @property StringObject *stringObjectCol;
- @property RLM_GENERIC_ARRAY(StringObject) *stringObjectArrayCol;
- @end
- @implementation StringLinkObject
- @end
- @interface ReadOnlyPropertyObject ()
- @property (readwrite) int readOnlyPropertyMadeReadWriteInClassExtension;
- @end
- @interface DataObject : RLMObject
- @property NSData *data1;
- @property NSData *data2;
- @end
- @implementation DataObject
- @end
- @interface DateObjectNoThrow : DateObject
- @property NSDate *date2;
- @end
- @implementation DateObjectNoThrow
- - (id)valueForUndefinedKey:(__unused NSString *)key {
- return nil;
- }
- @end
- @interface DateSubclassObject : DateObjectNoThrow
- @property NSDate *date3;
- @end
- @implementation DateSubclassObject
- @end
- @interface DateDefaultsObject : DateObjectNoThrow
- @property NSDate *date3;
- @end
- @implementation DateDefaultsObject
- + (NSDictionary *)defaultPropertyValues {
- return @{@"date3": [NSDate date]};
- }
- @end
- @interface SubclassDateObject : NSObject
- @property NSDate *dateCol;
- @property (getter=customGetter) NSDate *date2;
- @property (setter=customSetter:) NSDate *date3;
- @end
- @implementation SubclassDateObject
- @end
- #pragma mark - Tests
- @interface ObjectTests : RLMTestCase
- @end
- @implementation ObjectTests
- - (void)testKeyedSubscripting {
- EmployeeObject *objs = [[EmployeeObject alloc] initWithValue:@{@"name": @"Test0", @"age": @23, @"hired": @NO}];
- XCTAssertEqualObjects(objs[@"name"], @"Test0", @"Name should be Test0");
- XCTAssertEqualObjects(objs[@"age"], @23, @"age should be 23");
- XCTAssertEqualObjects(objs[@"hired"], @NO, @"hired should be NO");
- objs[@"name"] = @"Test1";
- XCTAssertEqualObjects(objs.name, @"Test1", @"Name should be Test1");
- RLMRealm *realm = [RLMRealm defaultRealm];
- [realm beginWriteTransaction];
- EmployeeObject *obj0 = [EmployeeObject createInRealm:realm withValue:@{@"name": @"Test1", @"age": @24, @"hired": @NO}];
- EmployeeObject *obj1 = [EmployeeObject createInRealm:realm withValue:@{@"name": @"Test2", @"age": @25, @"hired": @YES}];
- [realm commitWriteTransaction];
- XCTAssertEqualObjects(obj0[@"name"], @"Test1", @"Name should be Test1");
- XCTAssertEqualObjects(obj1[@"name"], @"Test2", @"Name should be Test1");
- [realm beginWriteTransaction];
- obj0[@"name"] = @"newName";
- [realm commitWriteTransaction];
- XCTAssertEqualObjects(obj0[@"name"], @"newName", @"Name should be newName");
- [realm beginWriteTransaction];
- obj0[@"name"] = nil;
- [realm commitWriteTransaction];
- XCTAssertNil(obj0[@"name"]);
- }
- - (void)testCannotUpdatePrimaryKey {
- PrimaryIntObject *intObj = [[PrimaryIntObject alloc] init];
- intObj.intCol = 1;
- XCTAssertNoThrow(intObj.intCol = 0);
- PrimaryStringObject *stringObj = [[PrimaryStringObject alloc] init];
- stringObj.stringCol = @"a";
- XCTAssertNoThrow(stringObj.stringCol = @"b");
- RLMRealm *realm = [RLMRealm defaultRealm];
- [realm beginWriteTransaction];
- [realm addObject:intObj];
- RLMAssertThrowsWithReason(intObj.intCol = 1, @"Primary key can't be changed");
- RLMAssertThrowsWithReason(intObj[@"intCol"] = @1, @"Primary key can't be changed");
- RLMAssertThrowsWithReason([intObj setValue:@1 forKey:@"intCol"], @"Primary key can't be changed");
- [realm addObject:stringObj];
- RLMAssertThrowsWithReason(stringObj.stringCol = @"a", @"Primary key can't be changed");
- RLMAssertThrowsWithReason(stringObj[@"stringCol"] = @"a", @"Primary key can't be changed");
- RLMAssertThrowsWithReason([stringObj setValue:@"a" forKey:@"stringCol"], @"Primary key can't be changed");
- [realm cancelWriteTransaction];
- }
- - (void)testDataTypes {
- RLMRealm *realm = [RLMRealm defaultRealm];
- [realm beginWriteTransaction];
- const char bin[4] = { 0, 1, 2, 3 };
- NSData *bin1 = [[NSData alloc] initWithBytes:bin length:sizeof bin / 2];
- NSData *bin2 = [[NSData alloc] initWithBytes:bin length:sizeof bin];
- NSDate *timeNow = [NSDate dateWithTimeIntervalSince1970:1000000];
- NSDate *timeZero = [NSDate dateWithTimeIntervalSince1970:0];
- AllTypesObject *c = [[AllTypesObject alloc] init];
- c.boolCol = NO;
- c.intCol = 54;
- c.floatCol = 0.7f;
- c.doubleCol = 0.8;
- c.stringCol = @"foo";
- c.binaryCol = bin1;
- c.dateCol = timeZero;
- c.cBoolCol = false;
- c.longCol = 99;
- c.objectCol = [[StringObject alloc] init];
- c.objectCol.stringCol = @"c";
- [realm addObject:c];
- [AllTypesObject createInRealm:realm withValue:@[@YES, @506, @7.7f, @8.8, @"banach", bin2,
- timeNow, @YES, @(-20), NSNull.null]];
- [realm commitWriteTransaction];
- AllTypesObject *row1 = [AllTypesObject allObjects][0];
- AllTypesObject *row2 = [AllTypesObject allObjects][1];
- XCTAssertEqual(row1.boolCol, NO, @"row1.BoolCol");
- XCTAssertEqual(row2.boolCol, YES, @"row2.BoolCol");
- XCTAssertEqual(row1.intCol, 54, @"row1.IntCol");
- XCTAssertEqual(row2.intCol, 506, @"row2.IntCol");
- XCTAssertEqual(row1.floatCol, 0.7f, @"row1.FloatCol");
- XCTAssertEqual(row2.floatCol, 7.7f, @"row2.FloatCol");
- XCTAssertEqual(row1.doubleCol, 0.8, @"row1.DoubleCol");
- XCTAssertEqual(row2.doubleCol, 8.8, @"row2.DoubleCol");
- XCTAssertTrue([row1.stringCol isEqual:@"foo"], @"row1.StringCol");
- XCTAssertTrue([row2.stringCol isEqual:@"banach"], @"row2.StringCol");
- XCTAssertTrue([row1.binaryCol isEqual:bin1], @"row1.BinaryCol");
- XCTAssertTrue([row2.binaryCol isEqual:bin2], @"row2.BinaryCol");
- XCTAssertTrue(([row1.dateCol isEqual:timeZero]), @"row1.DateCol");
- XCTAssertTrue(([row2.dateCol isEqual:timeNow]), @"row2.DateCol");
- XCTAssertEqual(row1.cBoolCol, false, @"row1.cBoolCol");
- XCTAssertEqual(row2.cBoolCol, true, @"row2.cBoolCol");
- XCTAssertEqual(row1.longCol, 99L, @"row1.IntCol");
- XCTAssertEqual(row2.longCol, -20L, @"row2.IntCol");
- XCTAssertTrue([row1.objectCol.stringCol isEqual:@"c"], @"row1.objectCol");
- XCTAssertNil(row2.objectCol, @"row2.objectCol");
- [realm transactionWithBlock:^{
- row1.boolCol = NO;
- row1.cBoolCol = false;
- row1.boolCol = (BOOL)6;
- row1.cBoolCol = (BOOL)6;
- }];
- XCTAssertEqual(row1.boolCol, true);
- XCTAssertEqual(row1.cBoolCol, true);
- AllTypesObject *o = [[AllTypesObject alloc] initWithValue:row1];
- o.floatCol = NAN;
- o.doubleCol = NAN;
- [realm transactionWithBlock:^{
- [realm addObject:o];
- }];
- XCTAssertTrue(isnan(o.floatCol));
- XCTAssertTrue(isnan(o.doubleCol));
- }
- - (void)testObjectSubclass {
-
- XCTAssertEqualObjects(@"StringObject", [StringObject className]);
- XCTAssertEqualObjects(@"StringSubclassObject", [StringSubclassObject className]);
- RLMRealm *realm = [RLMRealm defaultRealm];
- [realm beginWriteTransaction];
- [StringObject createInDefaultRealmWithValue:@[@"string"]];
- StringSubclassObject *obj = [StringSubclassObject createInDefaultRealmWithValue:@[@"string", @"string2"]];
-
- XCTAssertEqualObjects([obj.objectSchema.properties[0] name], @"stringCol");
- XCTAssertEqualObjects([obj.objectSchema.properties[1] name], @"stringCol2");
- [realm commitWriteTransaction];
-
- RLMResults *results = StringSubclassObject.allObjects;
- XCTAssertEqual(1U, results.count);
- XCTAssertEqual(1U, StringObject.allObjects.count);
-
- [realm beginWriteTransaction];
- StringLinkObject *linkObject = [StringLinkObject createInDefaultRealmWithValue:@[NSNull.null, @[]]];
- RLMAssertThrowsWithReasonMatching(linkObject.stringObjectCol = obj,
- @"Can't .*StringSubclassObject.*StringObject");
- RLMAssertThrowsWithReasonMatching([linkObject.stringObjectArrayCol addObject:obj],
- @"Object of type .*StringSubclassObject.*does not match.*StringObject.*");
- [realm commitWriteTransaction];
- }
- - (void)testDateDistantFuture {
- RLMRealm *realm = [RLMRealm defaultRealm];
- [realm beginWriteTransaction];
- DateObject *dateObject = [DateObject createInRealm:realm withValue:@[NSDate.distantFuture]];
- [realm commitWriteTransaction];
- XCTAssertEqualObjects(NSDate.distantFuture, dateObject.dateCol);
- }
- - (void)testDateDistantPast {
- RLMRealm *realm = [RLMRealm defaultRealm];
- [realm beginWriteTransaction];
- DateObject *dateObject = [DateObject createInRealm:realm withValue:@[NSDate.distantPast]];
- [realm commitWriteTransaction];
- XCTAssertEqualObjects(NSDate.distantPast, dateObject.dateCol);
- }
- - (void)testDate50kYears {
- NSCalendarUnit units = (NSCalendarUnit)(NSCalendarUnitMonth | NSCalendarUnitYear | NSCalendarUnitDay);
- NSDateComponents *components = [[NSCalendar currentCalendar] components:units fromDate:NSDate.date];
- components.calendar = [NSCalendar currentCalendar];
- components.year += 50000;
- RLMRealm *realm = [RLMRealm defaultRealm];
- [realm beginWriteTransaction];
- DateObject *dateObject = [DateObject createInRealm:realm withValue:@[components.date]];
- [realm commitWriteTransaction];
- XCTAssertEqualObjects(components.date, dateObject.dateCol);
- }
- static void testDatesInRange(NSTimeInterval from, NSTimeInterval to, void (^check)(NSDate *, NSDate *)) {
- NSDate *date = [NSDate dateWithTimeIntervalSinceReferenceDate:from];
- RLMRealm *realm = [RLMRealm defaultRealm];
- [realm beginWriteTransaction];
- DateObject *dateObject = [DateObject createInRealm:realm withValue:@[date]];
- while (from < to) @autoreleasepool {
- check(dateObject.dateCol, date);
- from = nextafter(from, DBL_MAX);
- date = [NSDate dateWithTimeIntervalSinceReferenceDate:from];
- dateObject.dateCol = date;
- }
- [realm commitWriteTransaction];
- }
- - (void)testExactRepresentationOfDatesAroundNow {
- NSDate *date = [NSDate date];
- NSTimeInterval time = date.timeIntervalSinceReferenceDate;
- testDatesInRange(time - .001, time + .001, ^(NSDate *d1, NSDate *d2) {
- XCTAssertEqualObjects(d1, d2);
- });
- }
- - (void)testExactRepresentationOfDatesAroundDistantFuture {
- NSDate *date = [NSDate distantFuture];
- NSTimeInterval time = date.timeIntervalSinceReferenceDate;
- testDatesInRange(time - .001, time + .001, ^(NSDate *d1, NSDate *d2) {
- XCTAssertEqualObjects(d1, d2);
- });
- }
- - (void)testExactRepresentationOfDatesAroundEpoch {
- NSDate *date = [NSDate dateWithTimeIntervalSince1970:0];
- NSTimeInterval time = date.timeIntervalSinceReferenceDate;
- testDatesInRange(time - .001, time + .001, ^(NSDate *d1, NSDate *d2) {
- XCTAssertEqualObjects(d1, d2);
- });
- }
- - (void)testExactRepresentationOfDatesAroundReferenceDate {
- RLMRealm *realm = [RLMRealm defaultRealm];
- [realm beginWriteTransaction];
- NSDate *zero = [NSDate dateWithTimeIntervalSinceReferenceDate:0];
- DateObject *dateObject = [DateObject createInRealm:realm withValue:@[zero]];
- XCTAssertEqualObjects(dateObject.dateCol, zero);
-
- dateObject.dateCol = [NSDate dateWithTimeIntervalSinceReferenceDate:nextafter(1e-9, -DBL_MAX)];
- XCTAssertEqualObjects(dateObject.dateCol, zero);
-
- dateObject.dateCol = [NSDate dateWithTimeIntervalSinceReferenceDate:1e-9];
- XCTAssertNotEqualObjects(dateObject.dateCol, zero);
-
- dateObject.dateCol = [NSDate dateWithTimeIntervalSinceReferenceDate:nextafter(0, -DBL_MAX)];
- XCTAssertEqualObjects(dateObject.dateCol, zero);
- dateObject.dateCol = [NSDate dateWithTimeIntervalSinceReferenceDate:nextafter(-1e-9, DBL_MAX)];
- XCTAssertEqualObjects(dateObject.dateCol, zero);
- dateObject.dateCol = [NSDate dateWithTimeIntervalSinceReferenceDate:-1e-9];
- XCTAssertNotEqualObjects(dateObject.dateCol, zero);
- [realm commitWriteTransaction];
- }
- - (void)testDatesOutsideOfTimestampRange {
- NSDate *date = [NSDate date];
- NSDate *maxDate = [NSDate dateWithTimeIntervalSince1970:(double)(1ULL << 63) + .999999999];
- NSDate *minDate = [NSDate dateWithTimeIntervalSince1970:-(double)(1ULL << 63) - .999999999];
- NSDate *justOverMaxDate = [NSDate dateWithTimeIntervalSince1970:nextafter(maxDate.timeIntervalSince1970, DBL_MAX)];
- NSDate *justUnderMaxDate = [NSDate dateWithTimeIntervalSince1970:nextafter(maxDate.timeIntervalSince1970, -DBL_MAX)];
- NSDate *justOverMinDate = [NSDate dateWithTimeIntervalSince1970:nextafter(minDate.timeIntervalSince1970, DBL_MAX)];
- NSDate *justUnderMinDate = [NSDate dateWithTimeIntervalSince1970:nextafter(minDate.timeIntervalSince1970, -DBL_MAX)];
- RLMRealm *realm = [RLMRealm defaultRealm];
- [realm beginWriteTransaction];
- DateObject *dateObject = [DateObject createInRealm:realm withValue:@[date]];
- dateObject.dateCol = [NSDate dateWithTimeIntervalSinceReferenceDate:0.0/0.0];
- XCTAssertEqualObjects(dateObject.dateCol, [NSDate dateWithTimeIntervalSince1970:0]);
- dateObject.dateCol = maxDate;
- XCTAssertEqualObjects(dateObject.dateCol, maxDate);
- dateObject.dateCol = justOverMaxDate;
- XCTAssertEqualObjects(dateObject.dateCol, maxDate);
- dateObject.dateCol = [NSDate dateWithTimeIntervalSinceReferenceDate:DBL_MAX];
- XCTAssertEqualObjects(dateObject.dateCol, maxDate);
- dateObject.dateCol = [NSDate dateWithTimeIntervalSinceReferenceDate:1.0/0.0];
- XCTAssertEqualObjects(dateObject.dateCol, maxDate);
- dateObject.dateCol = minDate;
- XCTAssertEqualObjects(dateObject.dateCol, minDate);
- dateObject.dateCol = justUnderMinDate;
- XCTAssertEqualObjects(dateObject.dateCol, minDate);
- dateObject.dateCol = [NSDate dateWithTimeIntervalSinceReferenceDate:-DBL_MAX];
- XCTAssertEqualObjects(dateObject.dateCol, minDate);
- dateObject.dateCol = [NSDate dateWithTimeIntervalSinceReferenceDate:-1.0/0.0];
- XCTAssertEqualObjects(dateObject.dateCol, minDate);
- dateObject.dateCol = justUnderMaxDate;
- XCTAssertEqualObjects(dateObject.dateCol, justUnderMaxDate);
- dateObject.dateCol = justOverMinDate;
- XCTAssertEqualObjects(dateObject.dateCol, justOverMinDate);
- [realm commitWriteTransaction];
- }
- - (void)testDataSizeLimits {
- RLMRealm *realm = [RLMRealm defaultRealm];
-
-
- static const int maxSize = 0xFFFFFF - 15;
-
- void *buffer = malloc(maxSize);
- strcpy((char *)buffer + maxSize - sizeof("hello") - 1, "hello");
- DataObject *obj = [[DataObject alloc] init];
- obj.data1 = obj.data2 = [NSData dataWithBytesNoCopy:buffer length:maxSize freeWhenDone:YES];
- [realm beginWriteTransaction];
- [realm addObject:obj];
- [realm commitWriteTransaction];
- XCTAssertEqual(maxSize, obj.data1.length);
- XCTAssertEqual(maxSize, obj.data2.length);
- XCTAssertTrue(strcmp((const char *)obj.data1.bytes + obj.data1.length - sizeof("hello") - 1, "hello") == 0);
- XCTAssertTrue(strcmp((const char *)obj.data2.bytes + obj.data2.length - sizeof("hello") - 1, "hello") == 0);
-
- [realm beginWriteTransaction];
- RLMAssertThrowsWithReason(obj.data1 = [NSData dataWithBytesNoCopy:malloc(maxSize + 1)
- length:maxSize + 1 freeWhenDone:YES],
- @"Binary too big");
- [realm commitWriteTransaction];
- }
- - (void)testStringSizeLimits {
- RLMRealm *realm = [RLMRealm defaultRealm];
-
-
- static const int maxSize = 0xFFFFFF - 16;
- void *buffer = calloc(maxSize, 1);
- strcpy((char *)buffer + maxSize - sizeof("hello") - 1, "hello");
- NSString *str = [[NSString alloc] initWithBytesNoCopy:buffer length:maxSize
- encoding:NSUTF8StringEncoding freeWhenDone:YES];
- StringObject *obj = [[StringObject alloc] init];
- obj.stringCol = str;
- [realm beginWriteTransaction];
- [realm addObject:obj];
- [realm commitWriteTransaction];
- XCTAssertEqualObjects(str, obj.stringCol);
-
- [realm beginWriteTransaction];
- XCTAssertThrows(obj.stringCol = [[NSString alloc] initWithBytesNoCopy:calloc(maxSize + 1, 1)
- length:maxSize + 1
- encoding:NSUTF8StringEncoding
- freeWhenDone:YES]);
- [realm commitWriteTransaction];
- }
- - (void)testAddingObjectNotInSchemaThrows {
- RLMRealmConfiguration *configuration = [RLMRealmConfiguration defaultConfiguration];
- configuration.objectClasses = @[StringObject.class];
- RLMRealm *realm = [RLMRealm realmWithConfiguration:configuration error:nil];
- [realm beginWriteTransaction];
- RLMAssertThrowsWithReasonMatching([realm addObject:[[IntObject alloc] initWithValue:@[@1]]],
- @"Object type 'IntObject' is not managed by the Realm.*custom `objectClasses`");
- RLMAssertThrowsWithReasonMatching([IntObject createInRealm:realm withValue:@[@1]],
- @"Object type 'IntObject' is not managed by the Realm.*custom `objectClasses`");
- XCTAssertNoThrow([realm addObject:[[StringObject alloc] initWithValue:@[@"A"]]]);
- XCTAssertNoThrow([StringObject createInRealm:realm withValue:@[@"A"]]);
- [realm cancelWriteTransaction];
- }
- static void addProperty(Class cls, const char *name, const char *type, size_t size, size_t align, id getter) {
- objc_property_attribute_t objectColAttrs[] = {
- {"T", type},
- {"V", name},
- };
- class_addIvar(cls, name, size, align, type);
- class_addProperty(cls, name, objectColAttrs, sizeof(objectColAttrs) / sizeof(objc_property_attribute_t));
- char encoding[4] = " @:";
- encoding[0] = *type;
- class_addMethod(cls, sel_registerName(name), imp_implementationWithBlock(getter), encoding);
- }
- - (void)testObjectSubclassAddedAtRuntime {
- Class objectClass = objc_allocateClassPair(RLMObject.class, "RuntimeGeneratedObject", 0);
- addProperty(objectClass, "objectCol", "@\"RuntimeGeneratedObject\"", sizeof(id), alignof(id), ^(__unused id obj) { return nil; });
- addProperty(objectClass, "intCol", "i", sizeof(int), alignof(int), ^int(__unused id obj) { return 0; });
- objc_registerClassPair(objectClass);
- XCTAssertEqualObjects([objectClass className], @"RuntimeGeneratedObject");
- RLMRealmConfiguration *configuration = [RLMRealmConfiguration defaultConfiguration];
- configuration.objectClasses = @[objectClass];
- XCTAssertEqualObjects([objectClass className], @"RuntimeGeneratedObject");
- RLMRealm *realm = [RLMRealm realmWithConfiguration:configuration error:nil];
- [realm beginWriteTransaction];
- id object = [objectClass createInRealm:realm withValue:@{@"objectCol": [[objectClass alloc] init], @"intCol": @17}];
- RLMObjectSchema *schema = [object objectSchema];
- XCTAssertNotNil(schema[@"objectCol"]);
- XCTAssertNotNil(schema[@"intCol"]);
- XCTAssert([[object objectCol] isKindOfClass:objectClass]);
- XCTAssertEqual([object intCol], 17);
- [realm commitWriteTransaction];
- }
- #pragma mark - Default Property Values
- - (NSDictionary *)defaultValuesDictionary {
- return @{@"intCol" : @98,
- @"floatCol" : @231.0f,
- @"doubleCol": @123732.9231,
- @"boolCol" : @NO,
- @"dateCol" : [NSDate dateWithTimeIntervalSince1970:454321],
- @"stringCol": @"Westeros",
- @"binaryCol": [@"inputData" dataUsingEncoding:NSUTF8StringEncoding]};
- }
- - (void)testDefaultValuesFromNoValuePresent {
- RLMRealm *realm = [RLMRealm defaultRealm];
- [realm beginWriteTransaction];
- NSDictionary *inputValues = [self defaultValuesDictionary];
- NSArray *keys = [inputValues allKeys];
- for (NSString *key in keys) {
- NSMutableDictionary *dict = [inputValues mutableCopy];
- [dict removeObjectForKey:key];
- [DefaultObject createInRealm:realm withValue:dict];
- }
- [realm commitWriteTransaction];
-
- NSDictionary *defaultValues = [DefaultObject defaultPropertyValues];
- RLMResults *allObjects = [DefaultObject allObjectsInRealm:realm];
- for (NSUInteger i = 0; i < keys.count; ++i) {
- DefaultObject *object = allObjects[i];
- for (NSUInteger j = 0; j < keys.count; ++j) {
- NSString *key = keys[j];
- if (i == j) {
- XCTAssertEqualObjects(object[key], defaultValues[key]);
- }
- else {
- XCTAssertEqualObjects(object[key], inputValues[key]);
- }
- }
- }
- }
- - (void)testDefaultValuesFromNSNull {
- RLMRealm *realm = [RLMRealm defaultRealm];
- NSDictionary *defaultValues = [DefaultObject defaultPropertyValues];
- NSDictionary *inputValues = [self defaultValuesDictionary];
- NSArray *keys = [inputValues allKeys];
- for (NSString *key in keys) {
- NSMutableDictionary *dict = [inputValues mutableCopy];
- dict[key] = NSNull.null;
- RLMProperty *prop = realm.schema[@"DefaultObject"][key];
- if (prop.optional) {
- [realm beginWriteTransaction];
- [DefaultObject createInRealm:realm withValue:dict];
- [realm commitWriteTransaction];
- DefaultObject *object = DefaultObject.allObjects.lastObject;
- for (NSUInteger j = 0; j < keys.count; ++j) {
- NSString *key2 = keys[j];
- if ([key isEqualToString:key2]) {
- XCTAssertEqualObjects(object[key2], prop.optional ? nil : defaultValues[key2]);
- }
- else {
- XCTAssertEqualObjects(object[key2], inputValues[key2]);
- }
- }
- }
- else {
- [realm beginWriteTransaction];
- RLMAssertThrowsWithReason([DefaultObject createInRealm:realm withValue:dict],
- @"Invalid value '<null>' of type 'NSNull' for ");
- [realm commitWriteTransaction];
- }
- }
- }
- - (void)testDefaultNSNumberPropertyValues {
- void (^assertDefaults)(NumberObject *) = ^(NumberObject *no) {
- XCTAssertEqualObjects(no.intObj, @1);
- XCTAssertEqualObjects(no.floatObj, @2.2f);
- XCTAssertEqualObjects(no.doubleObj, @3.3);
- XCTAssertEqualObjects(no.boolObj, @NO);
- };
- assertDefaults([[NumberDefaultsObject alloc] init]);
- RLMRealm *realm = [RLMRealm defaultRealm];
- [realm beginWriteTransaction];
- assertDefaults([NumberDefaultsObject createInRealm:realm withValue:@{}]);
- [realm cancelWriteTransaction];
- }
- - (void)testDynamicDefaultPropertyValues {
- void (^assertDifferentPropertyValues)(DynamicDefaultObject *, DynamicDefaultObject *) = ^(DynamicDefaultObject *obj1, DynamicDefaultObject *obj2) {
- XCTAssertNotEqual(obj1.intCol, obj2.intCol);
- XCTAssertNotEqual(obj1.floatCol, obj2.floatCol);
- XCTAssertNotEqual(obj1.doubleCol, obj2.doubleCol);
- XCTAssertNotEqualWithAccuracy(obj1.dateCol.timeIntervalSinceReferenceDate, obj2.dateCol.timeIntervalSinceReferenceDate, 0.01f);
- XCTAssertNotEqualObjects(obj1.stringCol, obj2.stringCol);
- XCTAssertNotEqualObjects(obj1.binaryCol, obj2.binaryCol);
- };
- assertDifferentPropertyValues([[DynamicDefaultObject alloc] init], [[DynamicDefaultObject alloc] init]);
- RLMRealmConfiguration *configuration = [RLMRealmConfiguration defaultConfiguration];
- configuration.objectClasses = @[[DynamicDefaultObject class]];
- RLMRealm *realm = [RLMRealm realmWithConfiguration:configuration error:nil];
- [realm beginWriteTransaction];
- assertDifferentPropertyValues([DynamicDefaultObject createInRealm:realm withValue:@{}], [DynamicDefaultObject createInRealm:realm withValue:@{}]);
- [realm cancelWriteTransaction];
- }
- #pragma mark - Ignored Properties
- - (void)testCanUseIgnoredProperty {
- NSURL *url = [NSURL URLWithString:@"http://realm.io"];
- RLMRealm *realm = [RLMRealm defaultRealm];
- [realm beginWriteTransaction];
- IgnoredURLObject *obj = [IgnoredURLObject new];
- obj.name = @"Realm";
- obj.url = url;
- [realm addObject:obj];
- XCTAssertEqual(obj.url, url, @"ignored properties should still be assignable and gettable inside a write block");
- [realm commitWriteTransaction];
- XCTAssertEqual(obj.url, url, @"ignored properties should still be assignable and gettable outside a write block");
- IgnoredURLObject *obj2 = [[IgnoredURLObject objectsWithPredicate:nil] firstObject];
- XCTAssertNotNil(obj2, @"object with ignored property should still be stored and accessible through the realm");
- XCTAssertEqualObjects(obj2.name, obj.name, @"managed property should be the same");
- XCTAssertNil(obj2.url, @"ignored property should be nil when getting from realm");
- }
- #pragma mark - Create
- - (void)testCreateInRealmValidationForDictionary {
- RLMRealm *realm = [RLMRealm defaultRealm];
- const char bin[4] = { 0, 1, 2, 3 };
- NSData *bin1 = [[NSData alloc] initWithBytes:bin length:sizeof bin / 2];
- NSDate *timeNow = [NSDate dateWithTimeIntervalSince1970:1000000];
- NSDictionary * const dictValidAllTypes = @{@"boolCol" : @NO,
- @"intCol" : @54,
- @"floatCol" : @0.7f,
- @"doubleCol": @0.8,
- @"stringCol": @"foo",
- @"binaryCol": bin1,
- @"dateCol" : timeNow,
- @"cBoolCol" : @NO,
- @"longCol" : @(99),
- @"objectCol": NSNull.null};
- [realm beginWriteTransaction];
-
- XCTAssertNoThrow(([AllTypesObject createInRealm:realm withValue:dictValidAllTypes]),
- @"Creating object with valid value types should not throw exception");
- for (NSString *keyToInvalidate in dictValidAllTypes.allKeys) {
- NSMutableDictionary *invalidInput = [dictValidAllTypes mutableCopy];
- id obj = @"invalid";
- if ([keyToInvalidate isEqualToString:@"stringCol"]) {
- obj = @1;
- }
- invalidInput[keyToInvalidate] = obj;
- RLMAssertThrowsWithReasonMatching([AllTypesObject createInRealm:realm withValue:invalidInput],
- @"Invalid value '.*'");
- }
- [realm commitWriteTransaction];
- }
- - (void)testCreateInRealmValidationForArray {
- RLMRealm *realm = [RLMRealm defaultRealm];
-
- [realm beginWriteTransaction];
- StringObject *to = [StringObject createInRealm:realm withValue:@[@"c"]];
- [realm commitWriteTransaction];
- const char bin[4] = { 0, 1, 2, 3 };
- NSData *bin1 = [[NSData alloc] initWithBytes:bin length:sizeof bin / 2];
- NSDate *timeNow = [NSDate dateWithTimeIntervalSince1970:1000000];
- NSArray *const arrayValidAllTypes = @[@NO, @54, @0.7f, @0.8, @"foo", bin1, timeNow, @NO, @(99), to];
- [realm beginWriteTransaction];
-
- XCTAssertNoThrow(([AllTypesObject createInRealm:realm withValue:arrayValidAllTypes]),
- @"Creating object with valid value types should not throw exception");
- const NSInteger stringColIndex = 4;
- for (NSUInteger i = 0; i < arrayValidAllTypes.count; i++) {
- NSMutableArray *invalidInput = [arrayValidAllTypes mutableCopy];
- id obj = @"invalid";
- if (i == stringColIndex) {
- obj = @1;
- }
- invalidInput[i] = obj;
- RLMAssertThrowsWithReasonMatching([AllTypesObject createInRealm:realm withValue:invalidInput],
- @"Invalid value '.*'");
- }
- [realm commitWriteTransaction];
- }
- - (void)testCreateInRealmReusesExistingObjects {
- RLMRealm *realm = [RLMRealm defaultRealm];
- [realm beginWriteTransaction];
- DogObject *dog = [DogObject createInDefaultRealmWithValue:@[@"Fido", @5]];
- OwnerObject *owner = [OwnerObject createInDefaultRealmWithValue:@[@"name", dog]];
- XCTAssertTrue([owner.dog isEqualToObject:dog]);
- XCTAssertEqual(1U, DogObject.allObjects.count);
- DogArrayObject *dogArray = [DogArrayObject createInDefaultRealmWithValue:@[@[dog]]];
- XCTAssertTrue([dogArray.dogs[0] isEqualToObject:dog]);
- XCTAssertEqual(1U, DogObject.allObjects.count);
- [realm commitWriteTransaction];
- }
- - (void)testCreateInRealmReusesExistingNestedObjectsByPrimaryKey {
- RLMRealm *realm = [RLMRealm defaultRealm];
- [realm beginWriteTransaction];
- PrimaryEmployeeObject *eo = [PrimaryEmployeeObject createInRealm:realm withValue:@[@"Samuel", @19, @NO]];
- PrimaryCompanyObject *co = [PrimaryCompanyObject createInRealm:realm withValue:@[@"Realm", @[eo], eo, @[eo]]];
- [realm commitWriteTransaction];
- [realm beginWriteTransaction];
- [PrimaryCompanyObject createOrUpdateInRealm:realm withValue:@{
- @"name": @"Realm",
- @"intern": @{@"name":@"Samuel", @"hired":@YES},
- }];
- [realm commitWriteTransaction];
- XCTAssertEqual(1U, co.employees.count);
- XCTAssertEqual(1U, [PrimaryEmployeeObject allObjectsInRealm:realm].count);
- XCTAssertEqualObjects(@"Samuel", eo.name);
- XCTAssertEqual(YES, eo.hired);
- XCTAssertEqual(19, eo.age);
- [realm beginWriteTransaction];
- [PrimaryCompanyObject createOrUpdateInRealm:realm withValue:@{
- @"name": @"Realm",
- @"employees": @[@{@"name":@"Samuel", @"hired":@NO}],
- @"intern": @{@"name":@"Samuel", @"age":@20},
- }];
- [realm commitWriteTransaction];
- XCTAssertEqual(1U, co.employees.count);
- XCTAssertEqual(1U, [PrimaryEmployeeObject allObjectsInRealm:realm].count);
- XCTAssertEqualObjects(@"Samuel", eo.name);
- XCTAssertEqual(NO, eo.hired);
- XCTAssertEqual(20, eo.age);
- [realm beginWriteTransaction];
- [PrimaryCompanyObject createOrUpdateInRealm:realm withValue:@{@"name": @"Realm",
- @"wrappedIntern": @[eo]}];
- [realm commitWriteTransaction];
- XCTAssertEqual(1U, [[PrimaryEmployeeObject allObjectsInRealm:realm] count]);
- }
- - (void)testCreateInRealmCopiesFromOtherRealm {
- RLMRealm *realm1 = [RLMRealm defaultRealm];
- RLMRealm *realm2 = [self realmWithTestPath];
- [realm1 beginWriteTransaction];
- [realm2 beginWriteTransaction];
- DogObject *dog = [DogObject createInDefaultRealmWithValue:@[@"Fido", @5]];
- OwnerObject *owner = [OwnerObject createInRealm:realm2 withValue:@[@"name", dog]];
- XCTAssertFalse([owner.dog isEqualToObject:dog]);
- XCTAssertEqual(1U, DogObject.allObjects.count);
- XCTAssertEqual(1U, [DogObject allObjectsInRealm:realm2].count);
- DogArrayObject *dogArray = [DogArrayObject createInRealm:realm2 withValue:@[@[dog]]];
- XCTAssertFalse([dogArray.dogs[0] isEqualToObject:dog]);
- XCTAssertEqual(1U, DogObject.allObjects.count);
- XCTAssertEqual(2U, [DogObject allObjectsInRealm:realm2].count);
- [realm1 commitWriteTransaction];
- [realm2 commitWriteTransaction];
- }
- - (void)testCreateInRealmWithOtherObjects {
- RLMRealm *realm = [RLMRealm defaultRealm];
- [realm beginWriteTransaction];
- DateObjectNoThrow *object = [DateObjectNoThrow createInDefaultRealmWithValue:@[NSDate.date, NSDate.date]];
-
- XCTAssertNoThrow([DateSubclassObject createInDefaultRealmWithValue:object]);
- XCTAssertNoThrow([DateObjectNoThrow createInDefaultRealmWithValue:object]);
-
- SubclassDateObject *obj = [SubclassDateObject new];
- obj.dateCol = [NSDate dateWithTimeIntervalSinceReferenceDate:1000];
- obj.date2 = [NSDate dateWithTimeIntervalSinceReferenceDate:2000];
- obj.date3 = [NSDate dateWithTimeIntervalSinceReferenceDate:3000];
- [DateDefaultsObject createInDefaultRealmWithValue:obj];
- XCTAssertEqual(2U, DateObjectNoThrow.allObjects.count);
- [realm commitWriteTransaction];
- }
- #pragma mark - Description
- - (void)testObjectDescription {
- RLMRealm *realm = [RLMRealm defaultRealm];
- [realm beginWriteTransaction];
-
- EmployeeObject *soInit = [[EmployeeObject alloc] init];
- soInit.name = @"Peter";
- soInit.age = 30;
- soInit.hired = YES;
- [realm addObject:soInit];
-
- void (^descriptionAsserts)(NSString *) = ^(NSString *description) {
- XCTAssertTrue([description rangeOfString:@"name"].location != NSNotFound,
- @"column names should be displayed when calling \"description\" on RLMObject subclasses");
- XCTAssertTrue([description rangeOfString:@"Peter"].location != NSNotFound,
- @"column values should be displayed when calling \"description\" on RLMObject subclasses");
- XCTAssertTrue([description rangeOfString:@"age"].location != NSNotFound,
- @"column names should be displayed when calling \"description\" on RLMObject subclasses");
- XCTAssertTrue([description rangeOfString:[@30 description]].location != NSNotFound,
- @"column values should be displayed when calling \"description\" on RLMObject subclasses");
- XCTAssertTrue([description rangeOfString:@"hired"].location != NSNotFound,
- @"column names should be displayed when calling \"description\" on RLMObject subclasses");
- XCTAssertTrue([description rangeOfString:[@YES description]].location != NSNotFound,
- @"column values should be displayed when calling \"description\" on RLMObject subclasses");
- };
-
- descriptionAsserts(soInit.description);
- [realm commitWriteTransaction];
-
- NSString *objDescription = [[[EmployeeObject objectsWithPredicate:nil] firstObject] description];
- descriptionAsserts(objDescription);
- soInit = [[EmployeeObject alloc] init];
- soInit.age = 20;
- XCTAssert([soInit.description rangeOfString:@"(null)"].location != NSNotFound);
- }
- - (void)testObjectCycleDescription {
- CycleObject *obj = [[CycleObject alloc] init];
- [RLMRealm.defaultRealm transactionWithBlock:^{
- [RLMRealm.defaultRealm addObject:obj];
- [obj.objects addObject:obj];
- }];
- XCTAssertNoThrow(obj.description);
- }
- - (void)testDataObjectDescription {
- RLMRealm *realm = [RLMRealm defaultRealm];
- [realm beginWriteTransaction];
- char longData[200];
- [DataObject createInRealm:realm withValue:@[[NSData dataWithBytes:&longData length:200], [NSData dataWithBytes:&longData length:2]]];
- [realm commitWriteTransaction];
- DataObject *obj = [DataObject allObjectsInRealm:realm].firstObject;
- XCTAssertTrue([obj.description rangeOfString:@"200 total bytes"].location != NSNotFound);
- XCTAssertTrue([obj.description rangeOfString:@"2 total bytes"].location != NSNotFound);
- }
- - (void)testDeletedObjectDescription {
- RLMRealm *realm = [RLMRealm defaultRealm];
- [realm beginWriteTransaction];
- EmployeeObject *obj = [EmployeeObject createInRealm:realm withValue:@[@"Peter", @30, @YES]];
- [realm deleteObject:obj];
- [realm commitWriteTransaction];
- XCTAssertNoThrow(obj.description);
- }
- - (void)testManagedObjectUnknownKey {
- IntObject *obj = [[IntObject alloc] init];
- RLMRealm *realm = [RLMRealm defaultRealm];
- [realm beginWriteTransaction];
- [realm addObject:obj];
- [realm commitWriteTransaction];
- RLMAssertThrowsWithReason([obj objectForKeyedSubscript:@""],
- @"Invalid property name '' for class 'IntObject'");
- RLMAssertThrowsWithReason([obj setObject:@0 forKeyedSubscript:@""],
- @"Invalid property name '' for class 'IntObject'");
- }
- - (void)testUnmanagedRealmObjectUnknownKey {
- IntObject *obj = [[IntObject alloc] init];
- XCTAssertThrows([obj objectForKeyedSubscript:@""]);
- XCTAssertThrows([obj setObject:@0 forKeyedSubscript:@""]);
- }
- - (void)testEquality {
- IntObject *obj = [[IntObject alloc] init];
- IntObject *otherObj = [[IntObject alloc] init];
- RLMRealm *realm = [RLMRealm defaultRealm];
- RLMRealm *otherRealm = [self realmWithTestPath];
- XCTAssertFalse([obj isEqual:[NSObject new]], @"Comparing an RLMObject to a non-RLMObject should be false.");
- XCTAssertFalse([obj isEqualToObject:(RLMObject *)[NSObject new]], @"Comparing an RLMObject to a non-RLMObject should be false.");
- XCTAssertTrue([obj isEqual:obj], @"Same instance.");
- XCTAssertTrue([obj isEqualToObject:obj], @"Same instance.");
- XCTAssertFalse([obj isEqualToObject:otherObj], @"Comparison outside of realm.");
- [realm beginWriteTransaction];
- [realm addObject:obj];
- [realm commitWriteTransaction];
- XCTAssertFalse([obj isEqualToObject:otherObj], @"One in realm, the other is not.");
- XCTAssertTrue([obj isEqualToObject:[IntObject allObjects][0]], @"Same table and index.");
- [otherRealm beginWriteTransaction];
- [otherRealm addObject:otherObj];
- [otherRealm commitWriteTransaction];
- XCTAssertFalse([obj isEqualToObject:otherObj], @"Different realms.");
- [realm beginWriteTransaction];
- [realm addObject:[[IntObject alloc] init]];
- [realm addObject:[[BoolObject alloc] init]];
- [realm commitWriteTransaction];
- XCTAssertFalse([obj isEqualToObject:[IntObject allObjects][1]], @"Same table, different index.");
- XCTAssertFalse([obj isEqualToObject:[BoolObject allObjects][0]], @"Different tables.");
- }
- - (void)testCrossThreadAccess {
- IntObject *obj = [[IntObject alloc] init];
-
- [self dispatchAsyncAndWait:^{ XCTAssertNoThrow(obj.intCol = 5); }];
- [RLMRealm.defaultRealm beginWriteTransaction];
- [RLMRealm.defaultRealm addObject:obj];
- [RLMRealm.defaultRealm commitWriteTransaction];
- [self dispatchAsyncAndWait:^{ RLMAssertThrowsWithReason(obj.intCol, @"incorrect thread"); }];
- }
- - (void)testIsDeleted {
- StringObject *obj1 = [[StringObject alloc] initWithValue:@[@"a"]];
- XCTAssertEqual(obj1.invalidated, NO);
- RLMRealm *realm = [self realmWithTestPath];
- [realm beginWriteTransaction];
- [realm addObject:obj1];
- StringObject *obj2 = [StringObject createInRealm:realm withValue:@[@"b"]];
- XCTAssertEqual([obj1 isInvalidated], NO);
- XCTAssertEqual(obj2.invalidated, NO);
- [realm commitWriteTransaction];
-
- [realm beginWriteTransaction];
-
- [realm deleteObject:obj1];
-
- [realm deleteObject:[[StringObject allObjectsInRealm:realm] firstObject]];
- XCTAssertEqual(obj1.invalidated, YES);
- XCTAssertEqual(obj2.invalidated, YES);
- RLMAssertThrowsWithReason([realm addObject:obj1], @"deleted or invalidated");
- NSArray *propObject = @[@"", @[obj2], @[]];
- RLMAssertThrowsWithReason([ArrayPropertyObject createInRealm:realm withValue:propObject],
- @"deleted or invalidated");
- [realm commitWriteTransaction];
- XCTAssertEqual(obj1.invalidated, YES);
- XCTAssertNil(obj1.realm, @"Realm should be nil after deletion");
- }
- #pragma mark - Primary Keys
- - (void)testPrimaryKey {
- [[RLMRealm defaultRealm] beginWriteTransaction];
- [PrimaryStringObject createInDefaultRealmWithValue:(@[@"string", @1])];
- [PrimaryStringObject createInDefaultRealmWithValue:(@[@"string2", @1])];
- RLMAssertThrowsWithReason([PrimaryStringObject createInDefaultRealmWithValue:(@[@"string", @1])],
- @"existing primary key value");
- [PrimaryIntObject createInDefaultRealmWithValue:(@[@1])];
- [PrimaryIntObject createInDefaultRealmWithValue:(@{@"intCol": @2})];
- RLMAssertThrowsWithReason([PrimaryIntObject createInDefaultRealmWithValue:(@[@1])],
- @"existing primary key value");
- [PrimaryInt64Object createInDefaultRealmWithValue:(@[@(1LL << 40)])];
- [PrimaryInt64Object createInDefaultRealmWithValue:(@[@(1LL << 41)])];
- RLMAssertThrowsWithReason([PrimaryInt64Object createInDefaultRealmWithValue:(@[@(1LL << 40)])],
- @"existing primary key value");
- [PrimaryNullableIntObject createInDefaultRealmWithValue:@[@1]];
- [PrimaryNullableIntObject createInDefaultRealmWithValue:(@{@"optIntCol": @2, @"value": @0})];
- [PrimaryNullableIntObject createInDefaultRealmWithValue:@[NSNull.null]];
- RLMAssertThrowsWithReason([PrimaryNullableIntObject createInDefaultRealmWithValue:(@[@1, @0])],
- @"existing primary key value");
- RLMAssertThrowsWithReason([PrimaryNullableIntObject createInDefaultRealmWithValue:(@[NSNull.null, @0])],
- @"existing primary key value");
- [[RLMRealm defaultRealm] commitWriteTransaction];
- }
- - (void)testCreateOrUpdate {
- RLMRealm *realm = [RLMRealm defaultRealm];
- [realm beginWriteTransaction];
- PrimaryNullableStringObject *obj1 = [PrimaryNullableStringObject
- createOrUpdateInDefaultRealmWithValue:@[@"string", @1]];
- RLMResults *objects = [PrimaryNullableStringObject allObjects];
- XCTAssertEqual([objects count], 1U, @"Should have 1 object");
- XCTAssertEqual(obj1.intCol, 1, @"Value should be 1");
- [PrimaryNullableStringObject createOrUpdateInRealm:realm withValue:@{@"stringCol": @"string2", @"intCol": @2}];
- XCTAssertEqual([objects count], 2U, @"Should have 2 objects");
- [PrimaryNullableStringObject createOrUpdateInRealm:realm withValue:@{@"intCol": @5}];
- [PrimaryNullableStringObject createOrUpdateInRealm:realm withValue:@{@"intCol": @7}];
- XCTAssertEqual([PrimaryNullableStringObject objectInRealm:realm forPrimaryKey:NSNull.null].intCol, 7);
- [PrimaryNullableStringObject createOrUpdateInRealm:realm withValue:@{@"stringCol": NSNull.null, @"intCol": @11}];
- XCTAssertEqual([PrimaryNullableStringObject objectInRealm:realm forPrimaryKey:nil].intCol, 11);
-
- [PrimaryNullableStringObject createOrUpdateInDefaultRealmWithValue:@[@"string", @3]];
- XCTAssertEqual([objects count], 3U, @"Should have 3 objects");
- XCTAssertEqual(obj1.intCol, 3, @"Value should be 3");
- [realm commitWriteTransaction];
- }
- - (void)testCreateOrUpdateNestedObjects {
- RLMRealm *realm = [RLMRealm defaultRealm];
- [realm beginWriteTransaction];
- [PrimaryNestedObject createOrUpdateInDefaultRealmWithValue:@[@0, @[@"string", @1], @[@[@"string", @1]], @[@"string"], @[@[@1]], @""]];
- XCTAssertEqual([[PrimaryNestedObject allObjects] count], 1U, @"Should have 1 object");
- XCTAssertEqual([[PrimaryStringObject allObjects] count], 1U, @"Should have 1 object");
- XCTAssertEqual([[PrimaryIntObject allObjects] count], 1U, @"Should have 1 object");
-
- [PrimaryNestedObject createOrUpdateInDefaultRealmWithValue:@{@"primaryCol": @0,
- @"primaryStringObject": @[@"string", @2],
- @"primaryStringObjectWrapper": @[@[@"string", @2]],
- @"stringObject": @[@"string2"]}];
- XCTAssertEqual([[PrimaryNestedObject allObjects] count], 1U, @"Should have 1 object");
- XCTAssertEqual([[PrimaryStringObject allObjects] count], 1U, @"Should have 1 object");
- XCTAssertEqual([PrimaryStringObject.allObjects.lastObject intCol], 2, @"intCol should be 2");
- XCTAssertEqualObjects([PrimaryNestedObject.allObjects.lastObject stringCol], @"", @"stringCol should not have been updated");
- XCTAssertEqual(1U, [PrimaryNestedObject.allObjects.lastObject primaryIntArray].count, @"intArray should not have been overwritten");
- XCTAssertEqual([[StringObject allObjects] count], 2U, @"Should have 2 objects");
-
- [PrimaryNestedObject createOrUpdateInDefaultRealmWithValue:@{@"primaryCol": @0,
- @"stringCol": @"updated",
- @"stringObject": NSNull.null,
- @"primaryIntArray": NSNull.null}];
- PrimaryNestedObject *obj = PrimaryNestedObject.allObjects.lastObject;
- XCTAssertEqual(2, obj.primaryStringObject.intCol, @"primaryStringObject should not have changed");
- XCTAssertEqualObjects(obj.stringCol, @"updated", @"stringCol should have been updated");
- XCTAssertEqual(0U, obj.primaryIntArray.count, @"intArray should not have been emptied");
- XCTAssertNil(obj.stringObject, @"stringObject should be nil");
-
- obj = [PrimaryNestedObject createOrUpdateInDefaultRealmWithValue:@[@1, @[@"string", @3], @[@[@"string", @3]], @[@"string"], @[], @""]];
- XCTAssertEqual([[PrimaryNestedObject allObjects] count], 2U, @"Should have 2 objects");
- XCTAssertEqual([[PrimaryStringObject allObjects] count], 1U, @"Should have 1 object");
- XCTAssertEqual([(PrimaryStringObject *)[[PrimaryStringObject allObjects] lastObject] intCol], 3, @"intCol should be 3");
-
- obj.primaryStringObject = [PrimaryStringObject createInDefaultRealmWithValue:@[@"string2", @1]];
- PrimaryNestedObject *obj1 = [[PrimaryNestedObject alloc] initWithValue:@[@1, @[@"string2", @4], @[@[@"string2", @4]], @[@"string"], @[@[@1], @[@2]], @""]];
- [realm addOrUpdateObject:obj1];
- XCTAssertEqual([[PrimaryNestedObject allObjects] count], 2U, @"Should have 2 objects");
- XCTAssertEqual([[PrimaryStringObject allObjects] count], 2U, @"Should have 2 objects");
- XCTAssertEqual([[PrimaryIntObject allObjects] count], 2U, @"Should have 2 objects");
- XCTAssertEqual([(PrimaryStringObject *)[[PrimaryStringObject allObjects] lastObject] intCol], 4, @"intCol should be 4");
- [realm commitWriteTransaction];
- }
- - (void)testCreateOrUpdateWithReorderedColumns {
- @autoreleasepool {
-
- RLMObjectSchema *objectSchema = [RLMObjectSchema schemaForObjectClass:PrimaryStringObject.class];
- objectSchema.properties = @[objectSchema.properties[1], objectSchema.properties[0]];
- RLMSchema *schema = [RLMSchema new];
- schema.objectSchema = @[objectSchema];
- RLMRealm *realm = [self realmWithTestPathAndSchema:schema];
- [realm beginWriteTransaction];
- [PrimaryStringObject createOrUpdateInRealm:realm withValue:@[@5, @"a"]];
- [realm commitWriteTransaction];
- }
- RLMRealm *realm = [self realmWithTestPath];
- [realm beginWriteTransaction];
- XCTAssertEqual([PrimaryStringObject objectInRealm:realm forPrimaryKey:@"a"].intCol, 5);
-
- [PrimaryStringObject createOrUpdateInRealm:realm withValue:@[@"a", @6]];
- XCTAssertEqual([PrimaryStringObject objectInRealm:realm forPrimaryKey:@"a"].intCol, 6);
- [PrimaryStringObject createOrUpdateInRealm:realm withValue:@{@"stringCol": @"a", @"intCol": @7}];
- XCTAssertEqual([PrimaryStringObject objectInRealm:realm forPrimaryKey:@"a"].intCol, 7);
- [realm commitWriteTransaction];
- }
- - (void)testObjectInSet {
- [[RLMRealm defaultRealm] beginWriteTransaction];
-
- PrimaryStringObject *obj = [PrimaryStringObject createInDefaultRealmWithValue:(@[@"string2", @1])];
- StringObject *strObj = [StringObject createInDefaultRealmWithValue:@[@"string"]];
- NSMutableSet *dict = [NSMutableSet set];
- [dict addObject:obj];
- [dict addObject:strObj];
-
- XCTAssertTrue([dict containsObject:obj]);
- XCTAssertTrue([dict containsObject:[[PrimaryStringObject allObjects] firstObject]]);
-
- XCTAssertTrue([dict containsObject:strObj]);
- XCTAssertFalse([dict containsObject:[[StringObject allObjects] firstObject]]);
- [[RLMRealm defaultRealm] commitWriteTransaction];
- }
- - (void)testObjectForKey {
- [RLMRealm.defaultRealm beginWriteTransaction];
- PrimaryStringObject *strObj = [PrimaryStringObject createInDefaultRealmWithValue:@[@"key", @0]];
- PrimaryNullableStringObject *nullStrObj = [PrimaryNullableStringObject createInDefaultRealmWithValue:@[NSNull.null, @0]];
- PrimaryIntObject *intObj = [PrimaryIntObject createInDefaultRealmWithValue:@[@0]];
- PrimaryNullableIntObject *nonNullIntObj = [PrimaryNullableIntObject createInDefaultRealmWithValue:@[@0]];
- PrimaryNullableIntObject *nullIntObj = [PrimaryNullableIntObject createInDefaultRealmWithValue:@[NSNull.null]];
- [RLMRealm.defaultRealm commitWriteTransaction];
-
- RLMAssertThrowsWithReason([StringObject objectForPrimaryKey:@""],
- @"does not have a primary key");
- RLMAssertThrowsWithReason([IntObject objectForPrimaryKey:@0],
- @"does not have a primary key");
- RLMAssertThrowsWithReason([StringObject objectForPrimaryKey:NSNull.null],
- @"does not have a primary key");
- RLMAssertThrowsWithReason([StringObject objectForPrimaryKey:nil],
- @"does not have a primary key");
- RLMAssertThrowsWithReason([IntObject objectForPrimaryKey:nil],
- @"does not have a primary key");
-
- RLMAssertThrowsWithReasonMatching([PrimaryStringObject objectForPrimaryKey:@0],
- @"Invalid value '0' of type '.*Number.*' for 'string' property 'PrimaryStringObject.stringCol'.");
- RLMAssertThrowsWithReasonMatching([PrimaryStringObject objectForPrimaryKey:@[]],
- @"of type '.*Array.*' for 'string' property 'PrimaryStringObject.stringCol'.");
- RLMAssertThrowsWithReasonMatching([PrimaryIntObject objectForPrimaryKey:@""],
- @"Invalid value '' of type '.*String.*' for 'int' property 'PrimaryIntObject.intCol'.");
- RLMAssertThrowsWithReason([PrimaryIntObject objectForPrimaryKey:NSNull.null],
- @"Invalid value '<null>' of type 'NSNull' for 'int' property 'PrimaryIntObject.intCol'.");
- RLMAssertThrowsWithReason([PrimaryIntObject objectForPrimaryKey:nil],
- @"Invalid value '(null)' of type '(null)' for 'int' property 'PrimaryIntObject.intCol'.");
- RLMAssertThrowsWithReason([PrimaryStringObject objectForPrimaryKey:NSNull.null],
- @"Invalid value '<null>' of type 'NSNull' for 'string' property 'PrimaryStringObject.stringCol'.");
- RLMAssertThrowsWithReason([PrimaryStringObject objectForPrimaryKey:nil],
- @"Invalid value '(null)' of type '(null)' for 'string' property 'PrimaryStringObject.stringCol'.");
-
- XCTAssertNil([PrimaryStringObject objectForPrimaryKey:@"bad key"]);
- XCTAssertNil([PrimaryIntObject objectForPrimaryKey:@1]);
-
- XCTAssertEqualObjects(strObj, [PrimaryStringObject objectForPrimaryKey:@"key"]);
- XCTAssertEqualObjects(nullStrObj, [PrimaryNullableStringObject objectForPrimaryKey:NSNull.null]);
- XCTAssertEqualObjects(nullStrObj, [PrimaryNullableStringObject objectForPrimaryKey:nil]);
- XCTAssertEqualObjects(intObj, [PrimaryIntObject objectForPrimaryKey:@0]);
- XCTAssertEqualObjects(nonNullIntObj, [PrimaryNullableIntObject objectForPrimaryKey:@0]);
- XCTAssertEqualObjects(nullIntObj, [PrimaryNullableIntObject objectForPrimaryKey:NSNull.null]);
- XCTAssertEqualObjects(nullIntObj, [PrimaryNullableIntObject objectForPrimaryKey:nil]);
-
- RLMAssertThrowsWithReason([PrimaryIntObject objectInRealm:self.nonLiteralNil forPrimaryKey:@0],
- @"Realm must not be nil");
- }
- - (void)testClassExtension {
- RLMRealm *realm = [RLMRealm defaultRealm];
- [realm beginWriteTransaction];
- BaseClassStringObject *bObject = [[BaseClassStringObject alloc ] init];
- bObject.intCol = 1;
- bObject.stringCol = @"stringVal";
- [realm addObject:bObject];
- [realm commitWriteTransaction];
- BaseClassStringObject *objectFromRealm = [BaseClassStringObject allObjects][0];
- XCTAssertEqual(1, objectFromRealm.intCol);
- XCTAssertEqualObjects(@"stringVal", objectFromRealm.stringCol);
- }
- #pragma mark - Frozen Objects
- static IntObject *managedObject() {
- IntObject *obj = [[IntObject alloc] init];
- RLMRealm *realm = RLMRealm.defaultRealm;
- [realm transactionWithBlock:^{
- [realm addObject:obj];
- }];
- return obj;
- }
- - (void)testIsFrozen {
- IntObject *standalone = [[IntObject alloc] init];
- IntObject *managed = managedObject();
- IntObject *frozen = [managed freeze];
- XCTAssertFalse(standalone.isFrozen);
- XCTAssertFalse(managed.isFrozen);
- XCTAssertTrue(frozen.isFrozen);
- }
- - (void)testFreezeUnmanagedObject {
- RLMAssertThrowsWithReason([[[IntObject alloc] init] freeze],
- @"Unmanaged objects cannot be frozen.");
- }
- - (void)testFreezingFrozenObjectReturnsSelf {
- IntObject *obj = managedObject();
- IntObject *frozen = obj.freeze;
- XCTAssertNotEqual(obj, frozen);
- XCTAssertNotEqual(obj.freeze, frozen);
- XCTAssertEqual(frozen, frozen.freeze);
- }
- - (void)testFreezingDeletedObject {
- IntObject *obj = managedObject();
- [obj.realm transactionWithBlock:^{
- [obj.realm deleteObject:obj];
- }];
- RLMAssertThrowsWithReason([obj freeze],
- @"Object has been deleted or invalidated.");
- }
- - (void)testFreezeFromWrongThread {
- IntObject *obj = managedObject();
- [self dispatchAsyncAndWait:^{
- RLMAssertThrowsWithReason([obj freeze],
- @"Realm accessed from incorrect thread");
- }];
- }
- - (void)testAccessFrozenObjectFromDifferentThread {
- IntObject *obj = managedObject();
- IntObject *frozen = [obj freeze];
- [self dispatchAsyncAndWait:^{
- XCTAssertEqual(frozen.intCol, 0);
- }];
- }
- - (void)testMutateFrozenObject {
- IntObject *obj = managedObject();
- IntObject *frozen = obj.freeze;
- XCTAssertThrows(frozen.intCol = 1);
- }
- - (void)testObserveFrozenObject {
- IntObject *frozen = [managedObject() freeze];
- id block = ^(__unused BOOL deleted, __unused NSArray *changes, __unused NSError *error) {};
- RLMAssertThrowsWithReason([frozen addNotificationBlock:block],
- @"Frozen Realms do not change and do not have change notifications.");
- }
- - (void)testFrozenObjectEquality {
- IntObject *liveObj = [[IntObject alloc] init];
- RLMRealm *realm = RLMRealm.defaultRealm;
- [realm transactionWithBlock:^{
- [realm addObject:liveObj];
- }];
- IntObject *frozen1 = [liveObj freeze];
- IntObject *frozen2 = [liveObj freeze];
- XCTAssertNotEqual(frozen1, frozen2);
- XCTAssertEqualObjects(frozen1, frozen2);
- [realm transactionWithBlock:^{
- [StringObject createInRealm:realm withValue:@[@"a"]];
- }];
- IntObject *frozen3 = [liveObj freeze];
- XCTAssertEqualObjects(frozen1, frozen2);
- XCTAssertNotEqualObjects(frozen1, frozen3);
- XCTAssertNotEqualObjects(frozen2, frozen3);
- }
- - (void)testFrozenObjectHashing {
- RLMRealm *realm = RLMRealm.defaultRealm;
- [realm transactionWithBlock:^{
-
-
- for (int i = 0; i < 200; ++i) {
- [IntObject createInRealm:realm withValue:@[@(i)]];
- }
- }];
- NSMutableSet *frozenSet = [NSMutableSet new];
- NSMutableSet *thawedSet = [NSMutableSet new];
- RLMResults<IntObject *> *allObjects = [IntObject allObjectsInRealm:realm];
- for (int i = 0; i < 100; ++i) {
- [thawedSet addObject:allObjects[i]];
- [frozenSet addObject:allObjects[i].freeze];
- }
- for (IntObject *obj in allObjects) {
- XCTAssertFalse([thawedSet containsObject:obj]);
- XCTAssertFalse([frozenSet containsObject:obj]);
- XCTAssertEqual([frozenSet containsObject:obj.freeze], obj.intCol < 100);
- }
- }
- - (void)testFreezeInsideWriteTransaction {
- RLMRealm *realm = RLMRealm.defaultRealm;
- [realm beginWriteTransaction];
- IntObject *obj = [IntObject createInRealm:realm withValue:@[@1]];
- RLMAssertThrowsWithReason([obj freeze], @"Cannot freeze an object in the same write transaction as it was created in.");
- [realm commitWriteTransaction];
- [realm beginWriteTransaction];
- obj.intCol = 2;
-
- XCTAssertEqual(obj.freeze.intCol, 1);
- [realm cancelWriteTransaction];
- }
- @end
|