DynamicTests.m 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  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. [DynamicTestObject createInRealm:realm withValue:@[@"column1", @1]];
  34. [DynamicTestObject 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[@"DynamicTestObject"];
  41. XCTAssertNotNil(dynSchema, @"Should be able to get object schema dynamically");
  42. XCTAssertEqual(dynSchema.properties.count, (NSUInteger)2, @"DynamicTestObject 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:@"DynamicTestObject"];
  47. XCTAssertEqual(results.count, (NSUInteger)2, @"Array should have 2 elements");
  48. XCTAssertNotEqual(results.objectClassName, DynamicTestObject.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. @autoreleasepool {
  69. expectedSchema = self.realmWithTestPath.schema;
  70. }
  71. XCTAssertNotNil(expectedSchema);
  72. RLMRealmConfiguration *config = [RLMRealmConfiguration new];
  73. config.fileURL = RLMTestRealmURL();
  74. config.dynamic = YES;
  75. NSError *error = nil;
  76. RLMSchema *dynamicSchema = [[RLMRealm realmWithConfiguration:config error:&error] schema];
  77. XCTAssertNil(error);
  78. for (RLMObjectSchema *expectedObjectSchema in expectedSchema.objectSchema) {
  79. Class cls = expectedObjectSchema.objectClass;
  80. if ([cls _realmObjectName] || [cls _realmColumnNames]) {
  81. // Class overrides names, so the dynamic schema for it shoudn't match
  82. continue;
  83. }
  84. if (expectedObjectSchema.primaryKeyProperty.index != 0) {
  85. // The dynamic schema will always put the primary key first, so it
  86. // won't match if the static schema doesn't have it there
  87. continue;
  88. }
  89. RLMObjectSchema *dynamicObjectSchema = dynamicSchema[expectedObjectSchema.className];
  90. XCTAssertEqual(dynamicObjectSchema.properties.count, expectedObjectSchema.properties.count);
  91. for (NSUInteger propertyIndex = 0; propertyIndex < expectedObjectSchema.properties.count; propertyIndex++) {
  92. RLMProperty *dynamicProperty = dynamicObjectSchema.properties[propertyIndex];
  93. RLMProperty *expectedProperty = expectedObjectSchema.properties[propertyIndex];
  94. XCTAssertEqualObjects(dynamicProperty, expectedProperty);
  95. }
  96. }
  97. }
  98. - (void)testDynamicSchema {
  99. RLMSchema *schema = [[RLMSchema alloc] init];
  100. RLMProperty *prop = [[RLMProperty alloc] initWithName:@"a"
  101. type:RLMPropertyTypeInt
  102. objectClassName:nil
  103. linkOriginPropertyName:nil
  104. indexed:NO
  105. optional:NO];
  106. RLMObjectSchema *objectSchema = [[RLMObjectSchema alloc] initWithClassName:@"TrulyDynamicObject"
  107. objectClass:RLMObject.class
  108. properties:@[prop]];
  109. schema.objectSchema = @[objectSchema];
  110. RLMRealm *dyrealm = [self realmWithTestPathAndSchema:schema];
  111. XCTAssertNotNil(dyrealm, @"dynamic realm shouldn't be nil");
  112. }
  113. - (void)testDynamicProperties {
  114. @autoreleasepool {
  115. // open realm in autoreleasepool to create tables and then dispose
  116. RLMRealm *realm = [RLMRealm realmWithURL:RLMTestRealmURL()];
  117. [realm beginWriteTransaction];
  118. [DynamicTestObject createInRealm:realm withValue:@[@"column1", @1]];
  119. [DynamicTestObject createInRealm:realm withValue:@[@"column2", @2]];
  120. [realm commitWriteTransaction];
  121. }
  122. // verify properties
  123. RLMRealm *dyrealm = [self realmWithTestPathAndSchema:nil];
  124. RLMResults *results = [dyrealm allObjects:@"DynamicTestObject"];
  125. RLMObject *o1 = results[0], *o2 = results[1];
  126. XCTAssertEqualObjects(o1[@"intCol"], @1);
  127. XCTAssertEqualObjects(o2[@"stringCol"], @"column2");
  128. RLMAssertThrowsWithReason(o1[@"invalid"], @"Invalid property name");
  129. RLMAssertThrowsWithReason(o1[@"invalid"] = nil, @"Invalid property name");
  130. }
  131. - (void)testDynamicTypes {
  132. NSDate *now = [NSDate dateWithTimeIntervalSince1970:100000];
  133. id obj1 = @[@YES, @1, @1.1f, @1.11, @"string", [NSData dataWithBytes:"a" length:1],
  134. now, @YES, @11, NSNull.null];
  135. StringObject *obj = [[StringObject alloc] init];
  136. obj.stringCol = @"string";
  137. id obj2 = @[@NO, @2, @2.2f, @2.22, @"string2", [NSData dataWithBytes:"b" length:1],
  138. now, @NO, @22, obj];
  139. @autoreleasepool {
  140. // open realm in autoreleasepool to create tables and then dispose
  141. RLMRealm *realm = [RLMRealm realmWithURL:RLMTestRealmURL()];
  142. [realm beginWriteTransaction];
  143. [AllTypesObject createInRealm:realm withValue:obj1];
  144. [AllTypesObject createInRealm:realm withValue:obj2];
  145. [realm commitWriteTransaction];
  146. }
  147. // verify properties
  148. RLMRealm *dyrealm = [self realmWithTestPathAndSchema:nil];
  149. RLMResults<RLMObject *> *results = [dyrealm allObjects:AllTypesObject.className];
  150. XCTAssertEqual(results.count, (NSUInteger)2, @"Should have 2 objects");
  151. RLMObjectSchema *schema = dyrealm.schema[AllTypesObject.className];
  152. for (int i = 0; i < 9; i++) {
  153. NSString *propName = [schema.properties[i] name];
  154. XCTAssertEqualObjects(obj1[i], results[0][propName]);
  155. XCTAssertEqualObjects(obj2[i], results[1][propName]);
  156. }
  157. // check sub object type
  158. XCTAssertEqualObjects([schema.properties[9] objectClassName], @"StringObject",
  159. @"Sub-object type in schema should be 'StringObject'");
  160. // check object equality
  161. XCTAssertNil(results[0][@"objectCol"], @"object should be nil");
  162. XCTAssertEqualObjects(results[1][@"objectCol"][@"stringCol"], @"string",
  163. @"Child object should have string value 'string'");
  164. [dyrealm beginWriteTransaction];
  165. RLMObject *o = results[0];
  166. for (int i = 0; i < 9; i++) {
  167. RLMProperty *prop = schema.properties[i];
  168. id value = prop.type == RLMPropertyTypeString ? @1 : @"";
  169. RLMAssertThrowsWithReason(o[prop.name] = value,
  170. @"Invalid value '");
  171. RLMAssertThrowsWithReason(o[prop.name] = NSNull.null,
  172. @"Invalid value '<null>' of type 'NSNull' for");
  173. RLMAssertThrowsWithReason(o[prop.name] = nil,
  174. @"Invalid value '(null)' of type '(null)' for");
  175. }
  176. RLMProperty *prop = schema.properties[9];
  177. RLMAssertThrowsWithReason(o[prop.name] = @"str",
  178. @"Invalid value 'str' of type '__NSCFConstantString' for 'StringObject?' property 'AllTypesObject.objectCol'.");
  179. XCTAssertNoThrow(o[prop.name] = nil);
  180. XCTAssertNoThrow(o[prop.name] = NSNull.null);
  181. id otherObjectType = [dyrealm createObject:IntObject.className withValue:@[@1]];
  182. RLMAssertThrowsWithReason(o[prop.name] = otherObjectType,
  183. @"Invalid value 'IntObject");
  184. [dyrealm cancelWriteTransaction];
  185. }
  186. - (void)testDynamicAdd {
  187. @autoreleasepool {
  188. // open realm in autoreleasepool to create tables and then dispose
  189. [RLMRealm realmWithURL:RLMTestRealmURL()];
  190. }
  191. RLMRealm *dyrealm = [self realmWithTestPathAndSchema:nil];
  192. [dyrealm beginWriteTransaction];
  193. RLMObject *stringObject = [dyrealm createObject:StringObject.className withValue:@[@"string"]];
  194. [dyrealm createObject:AllTypesObject.className withValue:@[@NO, @2, @2.2f, @2.22, @"string2",
  195. [NSData dataWithBytes:"b" length:1], NSDate.date, @NO, @22, stringObject]];
  196. [dyrealm commitWriteTransaction];
  197. XCTAssertEqual(1U, [dyrealm allObjects:StringObject.className].count);
  198. XCTAssertEqual(1U, [dyrealm allObjects:AllTypesObject.className].count);
  199. }
  200. - (void)testDynamicArray {
  201. @autoreleasepool {
  202. // open realm in autoreleasepool to create tables and then dispose
  203. [RLMRealm realmWithURL:RLMTestRealmURL()];
  204. }
  205. RLMRealm *dyrealm = [self realmWithTestPathAndSchema:nil];
  206. [dyrealm beginWriteTransaction];
  207. RLMObject *stringObject = [dyrealm createObject:StringObject.className withValue:@[@"string"]];
  208. RLMObject *stringObject1 = [dyrealm createObject:StringObject.className withValue:@[@"string1"]];
  209. [dyrealm createObject:ArrayPropertyObject.className withValue:@[@"name", @[stringObject, stringObject1], @[]]];
  210. RLMResults<RLMObject *> *results = [dyrealm allObjects:ArrayPropertyObject.className];
  211. XCTAssertEqual(1U, results.count);
  212. RLMObject *arrayObj = results.firstObject;
  213. RLMArray<RLMObject *> *array = arrayObj[@"array"];
  214. XCTAssertEqual(2U, array.count);
  215. XCTAssertEqualObjects(array[0][@"stringCol"], stringObject[@"stringCol"]);
  216. [array removeObjectAtIndex:0];
  217. [array addObject:stringObject];
  218. XCTAssertEqual(2U, array.count);
  219. XCTAssertEqualObjects(array[0][@"stringCol"], stringObject1[@"stringCol"]);
  220. XCTAssertEqualObjects(array[1][@"stringCol"], stringObject[@"stringCol"]);
  221. arrayObj[@"array"] = NSNull.null;
  222. XCTAssertEqual(0U, array.count);
  223. [array addObject:stringObject];
  224. XCTAssertEqual(1U, array.count);
  225. arrayObj[@"array"] = nil;
  226. XCTAssertEqual(0U, array.count);
  227. arrayObj[@"array"] = @[stringObject, stringObject1];
  228. XCTAssertEqualObjects(array[0][@"stringCol"], stringObject[@"stringCol"]);
  229. XCTAssertEqualObjects(array[1][@"stringCol"], stringObject1[@"stringCol"]);
  230. [dyrealm commitWriteTransaction];
  231. }
  232. - (void)testOptionalProperties {
  233. @autoreleasepool {
  234. // open realm in autoreleasepool to create tables and then dispose
  235. [RLMRealm realmWithURL:RLMTestRealmURL()];
  236. }
  237. RLMRealm *dyrealm = [self realmWithTestPathAndSchema:nil];
  238. [dyrealm beginWriteTransaction];
  239. RLMObject *object = [dyrealm createObject:AllOptionalTypes.className withValue:@[]];
  240. XCTAssertNil(object[@"intObj"]);
  241. XCTAssertNil(object[@"floatObj"]);
  242. XCTAssertNil(object[@"doubleObj"]);
  243. XCTAssertNil(object[@"boolObj"]);
  244. XCTAssertNil(object[@"string"]);
  245. XCTAssertNil(object[@"data"]);
  246. XCTAssertNil(object[@"date"]);
  247. NSDate *date = [NSDate date];
  248. NSData *data = [NSData data];
  249. object[@"intObj"] = @1;
  250. object[@"floatObj"] = @2.2f;
  251. object[@"doubleObj"] = @3.3;
  252. object[@"boolObj"] = @YES;
  253. object[@"string"] = @"str";
  254. object[@"date"] = date;
  255. object[@"data"] = data;
  256. XCTAssertEqualObjects(object[@"intObj"], @1);
  257. XCTAssertEqualObjects(object[@"floatObj"], @2.2f);
  258. XCTAssertEqualObjects(object[@"doubleObj"], @3.3);
  259. XCTAssertEqualObjects(object[@"boolObj"], @YES);
  260. XCTAssertEqualObjects(object[@"string"], @"str");
  261. XCTAssertEqualObjects(object[@"date"], date);
  262. XCTAssertEqualObjects(object[@"data"], data);
  263. object[@"intObj"] = NSNull.null;
  264. object[@"floatObj"] = NSNull.null;
  265. object[@"doubleObj"] = NSNull.null;
  266. object[@"boolObj"] = NSNull.null;
  267. object[@"string"] = NSNull.null;
  268. object[@"date"] = NSNull.null;
  269. object[@"data"] = NSNull.null;
  270. XCTAssertNil(object[@"intObj"]);
  271. XCTAssertNil(object[@"floatObj"]);
  272. XCTAssertNil(object[@"doubleObj"]);
  273. XCTAssertNil(object[@"boolObj"]);
  274. XCTAssertNil(object[@"string"]);
  275. XCTAssertNil(object[@"data"]);
  276. XCTAssertNil(object[@"date"]);
  277. object[@"intObj"] = nil;
  278. object[@"floatObj"] = nil;
  279. object[@"doubleObj"] = nil;
  280. object[@"boolObj"] = nil;
  281. object[@"string"] = nil;
  282. object[@"date"] = nil;
  283. object[@"data"] = nil;
  284. XCTAssertNil(object[@"intObj"]);
  285. XCTAssertNil(object[@"floatObj"]);
  286. XCTAssertNil(object[@"doubleObj"]);
  287. XCTAssertNil(object[@"boolObj"]);
  288. XCTAssertNil(object[@"string"]);
  289. XCTAssertNil(object[@"data"]);
  290. XCTAssertNil(object[@"date"]);
  291. [dyrealm commitWriteTransaction];
  292. }
  293. - (void)testLinkingObjects {
  294. RLMRealm *realm = [RLMRealm defaultRealm];
  295. [realm beginWriteTransaction];
  296. RLMObject *person = [realm createObject:PersonObject.className
  297. withValue:@[@"parent", @10, @[@[@"child", @5, @[]]]]];
  298. XCTAssertEqual([person[@"parents"] count], 0U);
  299. RLMObject *child = [person[@"children"] firstObject];
  300. XCTAssertEqual([child[@"parents"] count], 1U);
  301. XCTAssertTrue([person isEqualToObject:[child[@"parents"] firstObject]]);
  302. RLMAssertThrowsWithReason(person[@"parents"] = @[],
  303. @"Cannot modify read-only property 'PersonObject.parents'");
  304. [realm commitWriteTransaction];
  305. }
  306. @end