DynamicTests.m 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. ////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2014 Realm Inc.
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License");
  6. // you may not use this file except in compliance with the License.
  7. // You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. //
  17. ////////////////////////////////////////////////////////////////////////////
  18. #import "RLMTestCase.h"
  19. #import "RLMRealm_Dynamic.h"
  20. #import "RLMRealm_Private.h"
  21. #import "RLMProperty_Private.h"
  22. #import "RLMObjectSchema_Private.h"
  23. #import "RLMSchema_Private.h"
  24. @interface DynamicTests : RLMTestCase
  25. @end
  26. @implementation DynamicTests
  27. #pragma mark - Tests
  28. - (void)testDynamicRealmExists {
  29. @autoreleasepool {
  30. // open realm in autoreleasepool to create tables and then dispose
  31. RLMRealm *realm = [RLMRealm realmWithURL:RLMTestRealmURL()];
  32. [realm beginWriteTransaction];
  33. [DynamicObject createInRealm:realm withValue:@[@"column1", @1]];
  34. [DynamicObject createInRealm:realm withValue:@[@"column2", @2]];
  35. [realm commitWriteTransaction];
  36. }
  37. RLMRealm *dyrealm = [self realmWithTestPathAndSchema:nil];
  38. XCTAssertNotNil(dyrealm, @"realm should not be nil");
  39. // verify schema
  40. RLMObjectSchema *dynSchema = dyrealm.schema[@"DynamicObject"];
  41. XCTAssertNotNil(dynSchema, @"Should be able to get object schema dynamically");
  42. XCTAssertEqual(dynSchema.properties.count, (NSUInteger)2, @"DynamicObject should have 2 properties");
  43. XCTAssertEqualObjects([dynSchema.properties[0] name], @"stringCol", @"Invalid property name");
  44. XCTAssertEqual([(RLMProperty *)dynSchema.properties[1] type], RLMPropertyTypeInt, @"Invalid type");
  45. // verify object type
  46. RLMResults *results = [dyrealm allObjects:@"DynamicObject"];
  47. XCTAssertEqual(results.count, (NSUInteger)2, @"Array should have 2 elements");
  48. XCTAssertNotEqual(results.objectClassName, DynamicObject.className,
  49. @"Array class should by a dynamic object class");
  50. }
  51. - (void)testDynamicObjectRetrieval {
  52. @autoreleasepool {
  53. // open realm in autoreleasepool to create tables and then dispose
  54. RLMRealm *realm = [self realmWithTestPath];
  55. [realm beginWriteTransaction];
  56. [PrimaryStringObject createInRealm:realm withValue:@[@"key", @1]];
  57. [realm commitWriteTransaction];
  58. }
  59. RLMRealm *testRealm = [self realmWithTestPath];
  60. RLMObject *object = [testRealm objectWithClassName:@"PrimaryStringObject" forPrimaryKey:@"key"];
  61. XCTAssertNotNil(object, @"Should be able to retrieve object by primary key dynamically");
  62. XCTAssert([[object valueForKey:@"stringCol"] isEqualToString:@"key"],@"stringCol should equal 'key'");
  63. XCTAssert([[[object class] className] isEqualToString:@"PrimaryStringObject"],@"Object class name should equal 'PrimaryStringObject'");
  64. XCTAssert([object isKindOfClass:[PrimaryStringObject class]], @"Object should be of class 'PrimaryStringObject'");
  65. }
  66. - (void)testDynamicSchemaMatchesRegularSchema {
  67. RLMSchema *expectedSchema = nil;
  68. // Force create and close realm
  69. @autoreleasepool {
  70. RLMRealm *realm = self.realmWithTestPath;
  71. expectedSchema = realm.schema;
  72. }
  73. XCTAssertNotNil(expectedSchema);
  74. RLMRealmConfiguration *config = [RLMRealmConfiguration new];
  75. config.fileURL = RLMTestRealmURL();
  76. config.dynamic = YES;
  77. NSError *error = nil;
  78. RLMSchema *dynamicSchema = [[RLMRealm realmWithConfiguration:config error:&error] schema];
  79. XCTAssertNil(error);
  80. for (RLMObjectSchema *expectedObjectSchema in expectedSchema.objectSchema) {
  81. Class cls = expectedObjectSchema.objectClass;
  82. if ([cls _realmObjectName] || [cls _realmColumnNames]) {
  83. // Class overrides names, so the dynamic schema for it shoudn't match
  84. continue;
  85. }
  86. RLMObjectSchema *dynamicObjectSchema = dynamicSchema[expectedObjectSchema.className];
  87. XCTAssertEqual(dynamicObjectSchema.properties.count, expectedObjectSchema.properties.count);
  88. for (NSUInteger propertyIndex = 0; propertyIndex < expectedObjectSchema.properties.count; propertyIndex++) {
  89. RLMProperty *dynamicProperty = dynamicObjectSchema.properties[propertyIndex];
  90. RLMProperty *expectedProperty = expectedObjectSchema.properties[propertyIndex];
  91. XCTAssertEqualObjects(dynamicProperty, expectedProperty);
  92. }
  93. }
  94. }
  95. - (void)testDynamicSchema {
  96. RLMSchema *schema = [[RLMSchema alloc] init];
  97. RLMProperty *prop = [[RLMProperty alloc] initWithName:@"a"
  98. type:RLMPropertyTypeInt
  99. objectClassName:nil
  100. linkOriginPropertyName:nil
  101. indexed:NO
  102. optional:NO];
  103. RLMObjectSchema *objectSchema = [[RLMObjectSchema alloc] initWithClassName:@"TrulyDynamicObject"
  104. objectClass:RLMObject.class
  105. properties:@[prop]];
  106. schema.objectSchema = @[objectSchema];
  107. RLMRealm *dyrealm = [self realmWithTestPathAndSchema:schema];
  108. XCTAssertNotNil(dyrealm, @"dynamic realm shouldn't be nil");
  109. }
  110. - (void)testDynamicProperties {
  111. @autoreleasepool {
  112. // open realm in autoreleasepool to create tables and then dispose
  113. RLMRealm *realm = [RLMRealm realmWithURL:RLMTestRealmURL()];
  114. [realm beginWriteTransaction];
  115. [DynamicObject createInRealm:realm withValue:@[@"column1", @1]];
  116. [DynamicObject createInRealm:realm withValue:@[@"column2", @2]];
  117. [realm commitWriteTransaction];
  118. }
  119. // verify properties
  120. RLMRealm *dyrealm = [self realmWithTestPathAndSchema:nil];
  121. RLMResults *results = [dyrealm allObjects:@"DynamicObject"];
  122. RLMObject *o1 = results[0], *o2 = results[1];
  123. XCTAssertEqualObjects(o1[@"intCol"], @1);
  124. XCTAssertEqualObjects(o2[@"stringCol"], @"column2");
  125. RLMAssertThrowsWithReason(o1[@"invalid"], @"Invalid property name");
  126. RLMAssertThrowsWithReason(o1[@"invalid"] = nil, @"Invalid property name");
  127. }
  128. - (void)testDynamicTypes {
  129. NSDate *now = [NSDate dateWithTimeIntervalSince1970:100000];
  130. id obj1 = @[@YES, @1, @1.1f, @1.11, @"string", [NSData dataWithBytes:"a" length:1],
  131. now, @YES, @11, NSNull.null];
  132. StringObject *obj = [[StringObject alloc] init];
  133. obj.stringCol = @"string";
  134. id obj2 = @[@NO, @2, @2.2f, @2.22, @"string2", [NSData dataWithBytes:"b" length:1],
  135. now, @NO, @22, obj];
  136. @autoreleasepool {
  137. // open realm in autoreleasepool to create tables and then dispose
  138. RLMRealm *realm = [RLMRealm realmWithURL:RLMTestRealmURL()];
  139. [realm beginWriteTransaction];
  140. [AllTypesObject createInRealm:realm withValue:obj1];
  141. [AllTypesObject createInRealm:realm withValue:obj2];
  142. [realm commitWriteTransaction];
  143. }
  144. // verify properties
  145. RLMRealm *dyrealm = [self realmWithTestPathAndSchema:nil];
  146. RLMResults<RLMObject *> *results = [dyrealm allObjects:AllTypesObject.className];
  147. XCTAssertEqual(results.count, (NSUInteger)2, @"Should have 2 objects");
  148. RLMObjectSchema *schema = dyrealm.schema[AllTypesObject.className];
  149. for (int i = 0; i < 9; i++) {
  150. NSString *propName = [schema.properties[i] name];
  151. XCTAssertEqualObjects(obj1[i], results[0][propName]);
  152. XCTAssertEqualObjects(obj2[i], results[1][propName]);
  153. }
  154. // check sub object type
  155. XCTAssertEqualObjects([schema.properties[9] objectClassName], @"StringObject",
  156. @"Sub-object type in schema should be 'StringObject'");
  157. // check object equality
  158. XCTAssertNil(results[0][@"objectCol"], @"object should be nil");
  159. XCTAssertEqualObjects(results[1][@"objectCol"][@"stringCol"], @"string",
  160. @"Child object should have string value 'string'");
  161. [dyrealm beginWriteTransaction];
  162. RLMObject *o = results[0];
  163. for (int i = 0; i < 9; i++) {
  164. RLMProperty *prop = schema.properties[i];
  165. id value = prop.type == RLMPropertyTypeString ? @1 : @"";
  166. RLMAssertThrowsWithReason(o[prop.name] = value,
  167. @"Invalid value '");
  168. RLMAssertThrowsWithReason(o[prop.name] = NSNull.null,
  169. @"Invalid value '<null>' of type 'NSNull' for");
  170. RLMAssertThrowsWithReason(o[prop.name] = nil,
  171. @"Invalid value '(null)' of type '(null)' for");
  172. }
  173. RLMProperty *prop = schema.properties[9];
  174. RLMAssertThrowsWithReason(o[prop.name] = @"str",
  175. @"Invalid value 'str' of type '__NSCFConstantString' for 'StringObject?' property 'AllTypesObject.objectCol'.");
  176. XCTAssertNoThrow(o[prop.name] = nil);
  177. XCTAssertNoThrow(o[prop.name] = NSNull.null);
  178. id otherObjectType = [dyrealm createObject:IntObject.className withValue:@[@1]];
  179. RLMAssertThrowsWithReason(o[prop.name] = otherObjectType,
  180. @"Invalid value 'IntObject");
  181. [dyrealm cancelWriteTransaction];
  182. }
  183. - (void)testDynamicAdd {
  184. @autoreleasepool {
  185. // open realm in autoreleasepool to create tables and then dispose
  186. [RLMRealm realmWithURL:RLMTestRealmURL()];
  187. }
  188. RLMRealm *dyrealm = [self realmWithTestPathAndSchema:nil];
  189. [dyrealm beginWriteTransaction];
  190. RLMObject *stringObject = [dyrealm createObject:StringObject.className withValue:@[@"string"]];
  191. [dyrealm createObject:AllTypesObject.className withValue:@[@NO, @2, @2.2f, @2.22, @"string2",
  192. [NSData dataWithBytes:"b" length:1], NSDate.date, @NO, @22, stringObject]];
  193. [dyrealm commitWriteTransaction];
  194. XCTAssertEqual(1U, [dyrealm allObjects:StringObject.className].count);
  195. XCTAssertEqual(1U, [dyrealm allObjects:AllTypesObject.className].count);
  196. }
  197. - (void)testDynamicArray {
  198. @autoreleasepool {
  199. // open realm in autoreleasepool to create tables and then dispose
  200. [RLMRealm realmWithURL:RLMTestRealmURL()];
  201. }
  202. RLMRealm *dyrealm = [self realmWithTestPathAndSchema:nil];
  203. [dyrealm beginWriteTransaction];
  204. RLMObject *stringObject = [dyrealm createObject:StringObject.className withValue:@[@"string"]];
  205. RLMObject *stringObject1 = [dyrealm createObject:StringObject.className withValue:@[@"string1"]];
  206. [dyrealm createObject:ArrayPropertyObject.className withValue:@[@"name", @[stringObject, stringObject1], @[]]];
  207. RLMResults<RLMObject *> *results = [dyrealm allObjects:ArrayPropertyObject.className];
  208. XCTAssertEqual(1U, results.count);
  209. RLMObject *arrayObj = results.firstObject;
  210. RLMArray<RLMObject *> *array = arrayObj[@"array"];
  211. XCTAssertEqual(2U, array.count);
  212. XCTAssertEqualObjects(array[0][@"stringCol"], stringObject[@"stringCol"]);
  213. [array removeObjectAtIndex:0];
  214. [array addObject:stringObject];
  215. XCTAssertEqual(2U, array.count);
  216. XCTAssertEqualObjects(array[0][@"stringCol"], stringObject1[@"stringCol"]);
  217. XCTAssertEqualObjects(array[1][@"stringCol"], stringObject[@"stringCol"]);
  218. arrayObj[@"array"] = NSNull.null;
  219. XCTAssertEqual(0U, array.count);
  220. [array addObject:stringObject];
  221. XCTAssertEqual(1U, array.count);
  222. arrayObj[@"array"] = nil;
  223. XCTAssertEqual(0U, array.count);
  224. arrayObj[@"array"] = @[stringObject, stringObject1];
  225. XCTAssertEqualObjects(array[0][@"stringCol"], stringObject[@"stringCol"]);
  226. XCTAssertEqualObjects(array[1][@"stringCol"], stringObject1[@"stringCol"]);
  227. [dyrealm commitWriteTransaction];
  228. }
  229. - (void)testOptionalProperties {
  230. @autoreleasepool {
  231. // open realm in autoreleasepool to create tables and then dispose
  232. [RLMRealm realmWithURL:RLMTestRealmURL()];
  233. }
  234. RLMRealm *dyrealm = [self realmWithTestPathAndSchema:nil];
  235. [dyrealm beginWriteTransaction];
  236. RLMObject *object = [dyrealm createObject:AllOptionalTypes.className withValue:@[]];
  237. XCTAssertNil(object[@"intObj"]);
  238. XCTAssertNil(object[@"floatObj"]);
  239. XCTAssertNil(object[@"doubleObj"]);
  240. XCTAssertNil(object[@"boolObj"]);
  241. XCTAssertNil(object[@"string"]);
  242. XCTAssertNil(object[@"data"]);
  243. XCTAssertNil(object[@"date"]);
  244. NSDate *date = [NSDate date];
  245. NSData *data = [NSData data];
  246. object[@"intObj"] = @1;
  247. object[@"floatObj"] = @2.2f;
  248. object[@"doubleObj"] = @3.3;
  249. object[@"boolObj"] = @YES;
  250. object[@"string"] = @"str";
  251. object[@"date"] = date;
  252. object[@"data"] = data;
  253. XCTAssertEqualObjects(object[@"intObj"], @1);
  254. XCTAssertEqualObjects(object[@"floatObj"], @2.2f);
  255. XCTAssertEqualObjects(object[@"doubleObj"], @3.3);
  256. XCTAssertEqualObjects(object[@"boolObj"], @YES);
  257. XCTAssertEqualObjects(object[@"string"], @"str");
  258. XCTAssertEqualObjects(object[@"date"], date);
  259. XCTAssertEqualObjects(object[@"data"], data);
  260. object[@"intObj"] = NSNull.null;
  261. object[@"floatObj"] = NSNull.null;
  262. object[@"doubleObj"] = NSNull.null;
  263. object[@"boolObj"] = NSNull.null;
  264. object[@"string"] = NSNull.null;
  265. object[@"date"] = NSNull.null;
  266. object[@"data"] = NSNull.null;
  267. XCTAssertNil(object[@"intObj"]);
  268. XCTAssertNil(object[@"floatObj"]);
  269. XCTAssertNil(object[@"doubleObj"]);
  270. XCTAssertNil(object[@"boolObj"]);
  271. XCTAssertNil(object[@"string"]);
  272. XCTAssertNil(object[@"data"]);
  273. XCTAssertNil(object[@"date"]);
  274. object[@"intObj"] = nil;
  275. object[@"floatObj"] = nil;
  276. object[@"doubleObj"] = nil;
  277. object[@"boolObj"] = nil;
  278. object[@"string"] = nil;
  279. object[@"date"] = nil;
  280. object[@"data"] = nil;
  281. XCTAssertNil(object[@"intObj"]);
  282. XCTAssertNil(object[@"floatObj"]);
  283. XCTAssertNil(object[@"doubleObj"]);
  284. XCTAssertNil(object[@"boolObj"]);
  285. XCTAssertNil(object[@"string"]);
  286. XCTAssertNil(object[@"data"]);
  287. XCTAssertNil(object[@"date"]);
  288. [dyrealm commitWriteTransaction];
  289. }
  290. - (void)testLinkingObjects {
  291. RLMRealm *realm = [RLMRealm defaultRealm];
  292. [realm beginWriteTransaction];
  293. RLMObject *person = [realm createObject:PersonObject.className
  294. withValue:@[@"parent", @10, @[@[@"child", @5, @[]]]]];
  295. XCTAssertEqual([person[@"parents"] count], 0U);
  296. RLMObject *child = [person[@"children"] firstObject];
  297. XCTAssertEqual([child[@"parents"] count], 1U);
  298. XCTAssertTrue([person isEqualToObject:[child[@"parents"] firstObject]]);
  299. RLMAssertThrowsWithReason(person[@"parents"] = @[],
  300. @"Cannot modify read-only property 'PersonObject.parents'");
  301. [realm commitWriteTransaction];
  302. }
  303. @end