PrimitiveArrayPropertyTests.tpl.m 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830
  1. ////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2017 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. @interface PrimitiveArrayPropertyTests : RLMTestCase
  20. @end
  21. @implementation PrimitiveArrayPropertyTests {
  22. AllPrimitiveArrays *unmanaged;
  23. AllPrimitiveArrays *managed;
  24. AllOptionalPrimitiveArrays *optUnmanaged;
  25. AllOptionalPrimitiveArrays *optManaged;
  26. RLMRealm *realm;
  27. }
  28. - (void)setUp {
  29. unmanaged = [[AllPrimitiveArrays alloc] init];
  30. optUnmanaged = [[AllOptionalPrimitiveArrays alloc] init];
  31. realm = [RLMRealm defaultRealm];
  32. [realm beginWriteTransaction];
  33. managed = [AllPrimitiveArrays createInRealm:realm withValue:@[]];
  34. optManaged = [AllOptionalPrimitiveArrays createInRealm:realm withValue:@[]];
  35. }
  36. - (void)tearDown {
  37. if (realm.inWriteTransaction) {
  38. [realm cancelWriteTransaction];
  39. }
  40. }
  41. - (void)testCount {
  42. XCTAssertEqual(unmanaged.intObj.count, 0U);
  43. [unmanaged.intObj addObject:@1];
  44. XCTAssertEqual(unmanaged.intObj.count, 1U);
  45. }
  46. - (void)testType {
  47. XCTAssertEqual(unmanaged.boolObj.type, RLMPropertyTypeBool);
  48. XCTAssertEqual(unmanaged.intObj.type, RLMPropertyTypeInt);
  49. XCTAssertEqual(unmanaged.floatObj.type, RLMPropertyTypeFloat);
  50. XCTAssertEqual(unmanaged.doubleObj.type, RLMPropertyTypeDouble);
  51. XCTAssertEqual(unmanaged.stringObj.type, RLMPropertyTypeString);
  52. XCTAssertEqual(unmanaged.dataObj.type, RLMPropertyTypeData);
  53. XCTAssertEqual(unmanaged.dateObj.type, RLMPropertyTypeDate);
  54. XCTAssertEqual(optUnmanaged.boolObj.type, RLMPropertyTypeBool);
  55. XCTAssertEqual(optUnmanaged.intObj.type, RLMPropertyTypeInt);
  56. XCTAssertEqual(optUnmanaged.floatObj.type, RLMPropertyTypeFloat);
  57. XCTAssertEqual(optUnmanaged.doubleObj.type, RLMPropertyTypeDouble);
  58. XCTAssertEqual(optUnmanaged.stringObj.type, RLMPropertyTypeString);
  59. XCTAssertEqual(optUnmanaged.dataObj.type, RLMPropertyTypeData);
  60. XCTAssertEqual(optUnmanaged.dateObj.type, RLMPropertyTypeDate);
  61. }
  62. - (void)testOptional {
  63. XCTAssertFalse(unmanaged.boolObj.optional);
  64. XCTAssertFalse(unmanaged.intObj.optional);
  65. XCTAssertFalse(unmanaged.floatObj.optional);
  66. XCTAssertFalse(unmanaged.doubleObj.optional);
  67. XCTAssertFalse(unmanaged.stringObj.optional);
  68. XCTAssertFalse(unmanaged.dataObj.optional);
  69. XCTAssertFalse(unmanaged.dateObj.optional);
  70. XCTAssertTrue(optUnmanaged.boolObj.optional);
  71. XCTAssertTrue(optUnmanaged.intObj.optional);
  72. XCTAssertTrue(optUnmanaged.floatObj.optional);
  73. XCTAssertTrue(optUnmanaged.doubleObj.optional);
  74. XCTAssertTrue(optUnmanaged.stringObj.optional);
  75. XCTAssertTrue(optUnmanaged.dataObj.optional);
  76. XCTAssertTrue(optUnmanaged.dateObj.optional);
  77. }
  78. - (void)testObjectClassName {
  79. XCTAssertNil(unmanaged.boolObj.objectClassName);
  80. XCTAssertNil(unmanaged.intObj.objectClassName);
  81. XCTAssertNil(unmanaged.floatObj.objectClassName);
  82. XCTAssertNil(unmanaged.doubleObj.objectClassName);
  83. XCTAssertNil(unmanaged.stringObj.objectClassName);
  84. XCTAssertNil(unmanaged.dataObj.objectClassName);
  85. XCTAssertNil(unmanaged.dateObj.objectClassName);
  86. XCTAssertNil(optUnmanaged.boolObj.objectClassName);
  87. XCTAssertNil(optUnmanaged.intObj.objectClassName);
  88. XCTAssertNil(optUnmanaged.floatObj.objectClassName);
  89. XCTAssertNil(optUnmanaged.doubleObj.objectClassName);
  90. XCTAssertNil(optUnmanaged.stringObj.objectClassName);
  91. XCTAssertNil(optUnmanaged.dataObj.objectClassName);
  92. XCTAssertNil(optUnmanaged.dateObj.objectClassName);
  93. }
  94. - (void)testRealm {
  95. XCTAssertNil(unmanaged.boolObj.realm);
  96. XCTAssertNil(unmanaged.intObj.realm);
  97. XCTAssertNil(unmanaged.floatObj.realm);
  98. XCTAssertNil(unmanaged.doubleObj.realm);
  99. XCTAssertNil(unmanaged.stringObj.realm);
  100. XCTAssertNil(unmanaged.dataObj.realm);
  101. XCTAssertNil(unmanaged.dateObj.realm);
  102. XCTAssertNil(optUnmanaged.boolObj.realm);
  103. XCTAssertNil(optUnmanaged.intObj.realm);
  104. XCTAssertNil(optUnmanaged.floatObj.realm);
  105. XCTAssertNil(optUnmanaged.doubleObj.realm);
  106. XCTAssertNil(optUnmanaged.stringObj.realm);
  107. XCTAssertNil(optUnmanaged.dataObj.realm);
  108. XCTAssertNil(optUnmanaged.dateObj.realm);
  109. }
  110. - (void)testInvalidated {
  111. RLMArray *array;
  112. @autoreleasepool {
  113. AllPrimitiveArrays *obj = [[AllPrimitiveArrays alloc] init];
  114. array = obj.intObj;
  115. XCTAssertFalse(array.invalidated);
  116. }
  117. XCTAssertFalse(array.invalidated);
  118. }
  119. - (void)testDeleteObjectsInRealm {
  120. RLMAssertThrowsWithReason([realm deleteObjects:$array], @"Cannot delete objects from RLMArray");
  121. }
  122. static NSDate *date(int i) {
  123. return [NSDate dateWithTimeIntervalSince1970:i];
  124. }
  125. static NSData *data(int i) {
  126. return [NSData dataWithBytesNoCopy:calloc(i, 1) length:i freeWhenDone:YES];
  127. }
  128. - (void)testObjectAtIndex {
  129. RLMAssertThrowsWithReason([unmanaged.intObj objectAtIndex:0],
  130. @"Index 0 is out of bounds (must be less than 0).");
  131. [unmanaged.intObj addObject:@1];
  132. XCTAssertEqualObjects([unmanaged.intObj objectAtIndex:0], @1);
  133. }
  134. - (void)testFirstObject {
  135. XCTAssertNil($array.firstObject);
  136. %r [$array addObjects:$values];
  137. %r XCTAssertEqualObjects($array.firstObject, $first);
  138. %o [$array addObject:NSNull.null];
  139. %o XCTAssertEqualObjects($array.firstObject, NSNull.null);
  140. %o [$array removeAllObjects];
  141. %o [$array addObjects:$values];
  142. %o XCTAssertEqualObjects($array.firstObject, $first);
  143. }
  144. - (void)testLastObject {
  145. XCTAssertNil($array.lastObject);
  146. [$array addObjects:$values];
  147. XCTAssertEqualObjects($array.lastObject, $last);
  148. %o [$array removeLastObject];
  149. %o XCTAssertEqualObjects($array.lastObject, $v1);
  150. }
  151. - (void)testAddObject {
  152. RLMAssertThrowsWithReason([$array addObject:$wrong], ^n @"Invalid value '$wdesc' of type '$wtype' for expected type '$type'");
  153. %r RLMAssertThrowsWithReason([$array addObject:NSNull.null], ^n @"Invalid value '<null>' of type 'NSNull' for expected type '$type'");
  154. [$array addObject:$v0];
  155. XCTAssertEqualObjects($array[0], $v0);
  156. %o [$array addObject:NSNull.null];
  157. %o XCTAssertEqualObjects($array[1], NSNull.null);
  158. }
  159. - (void)testAddObjects {
  160. RLMAssertThrowsWithReason([$array addObjects:@[$wrong]], ^n @"Invalid value '$wdesc' of type '$wtype' for expected type '$type'");
  161. %r RLMAssertThrowsWithReason([$array addObjects:@[NSNull.null]], ^n @"Invalid value '<null>' of type 'NSNull' for expected type '$type'");
  162. [$array addObjects:$values];
  163. XCTAssertEqualObjects($array[0], $v0);
  164. XCTAssertEqualObjects($array[1], $v1);
  165. %o XCTAssertEqualObjects($array[2], NSNull.null);
  166. }
  167. - (void)testInsertObject {
  168. RLMAssertThrowsWithReason([$array insertObject:$wrong atIndex:0], ^n @"Invalid value '$wdesc' of type '$wtype' for expected type '$type'");
  169. %r RLMAssertThrowsWithReason([$array insertObject:NSNull.null atIndex:0], ^n @"Invalid value '<null>' of type 'NSNull' for expected type '$type'");
  170. RLMAssertThrowsWithReason([$array insertObject:$v0 atIndex:1], ^n @"Index 1 is out of bounds (must be less than 1).");
  171. [$array insertObject:$v0 atIndex:0];
  172. XCTAssertEqualObjects($array[0], $v0);
  173. [$array insertObject:$v1 atIndex:0];
  174. XCTAssertEqualObjects($array[0], $v1);
  175. XCTAssertEqualObjects($array[1], $v0);
  176. %o [$array insertObject:NSNull.null atIndex:1];
  177. %o XCTAssertEqualObjects($array[0], $v1);
  178. %o XCTAssertEqualObjects($array[1], NSNull.null);
  179. %o XCTAssertEqualObjects($array[2], $v0);
  180. }
  181. - (void)testRemoveObject {
  182. RLMAssertThrowsWithReason([$array removeObjectAtIndex:0], ^n @"Index 0 is out of bounds (must be less than 0).");
  183. [$array addObjects:$values];
  184. %r XCTAssertEqual($array.count, 2U);
  185. %o XCTAssertEqual($array.count, 3U);
  186. %r RLMAssertThrowsWithReason([$array removeObjectAtIndex:2], ^n @"Index 2 is out of bounds (must be less than 2).");
  187. %o RLMAssertThrowsWithReason([$array removeObjectAtIndex:3], ^n @"Index 3 is out of bounds (must be less than 3).");
  188. [$array removeObjectAtIndex:0];
  189. %r XCTAssertEqual($array.count, 1U);
  190. %o XCTAssertEqual($array.count, 2U);
  191. XCTAssertEqualObjects($array[0], $v1);
  192. %o XCTAssertEqualObjects($array[1], NSNull.null);
  193. }
  194. - (void)testRemoveLastObject {
  195. XCTAssertNoThrow([$array removeLastObject]);
  196. [$array addObjects:$values];
  197. %r XCTAssertEqual($array.count, 2U);
  198. %o XCTAssertEqual($array.count, 3U);
  199. [$array removeLastObject];
  200. %r XCTAssertEqual($array.count, 1U);
  201. %o XCTAssertEqual($array.count, 2U);
  202. XCTAssertEqualObjects($array[0], $v0);
  203. %o XCTAssertEqualObjects($array[1], $v1);
  204. }
  205. - (void)testReplace {
  206. RLMAssertThrowsWithReason([$array replaceObjectAtIndex:0 withObject:$v0], ^n @"Index 0 is out of bounds (must be less than 0).");
  207. [$array addObject:$v0]; ^nl [$array replaceObjectAtIndex:0 withObject:$v1]; ^nl XCTAssertEqualObjects($array[0], $v1); ^nl
  208. %o [$array replaceObjectAtIndex:0 withObject:NSNull.null]; ^nl XCTAssertEqualObjects($array[0], NSNull.null);
  209. RLMAssertThrowsWithReason([$array replaceObjectAtIndex:0 withObject:$wrong], ^n @"Invalid value '$wdesc' of type '$wtype' for expected type '$type'");
  210. %r RLMAssertThrowsWithReason([$array replaceObjectAtIndex:0 withObject:NSNull.null], ^n @"Invalid value '<null>' of type 'NSNull' for expected type '$type'");
  211. }
  212. - (void)testMove {
  213. RLMAssertThrowsWithReason([$array moveObjectAtIndex:0 toIndex:1], ^n @"Index 0 is out of bounds (must be less than 0).");
  214. RLMAssertThrowsWithReason([$array moveObjectAtIndex:1 toIndex:0], ^n @"Index 1 is out of bounds (must be less than 0).");
  215. [$array addObjects:@[$v0, $v1, $v0, $v1]];
  216. [$array moveObjectAtIndex:2 toIndex:0];
  217. XCTAssertEqualObjects([$array valueForKey:@"self"], ^n (@[$v0, $v0, $v1, $v1]));
  218. }
  219. - (void)testExchange {
  220. RLMAssertThrowsWithReason([$array exchangeObjectAtIndex:0 withObjectAtIndex:1], ^n @"Index 0 is out of bounds (must be less than 0).");
  221. RLMAssertThrowsWithReason([$array exchangeObjectAtIndex:1 withObjectAtIndex:0], ^n @"Index 1 is out of bounds (must be less than 0).");
  222. [$array addObjects:@[$v0, $v1, $v0, $v1]];
  223. [$array exchangeObjectAtIndex:2 withObjectAtIndex:1];
  224. XCTAssertEqualObjects([$array valueForKey:@"self"], ^n (@[$v0, $v0, $v1, $v1]));
  225. }
  226. - (void)testIndexOfObject {
  227. XCTAssertEqual(NSNotFound, [$array indexOfObject:$v0]);
  228. RLMAssertThrowsWithReason([$array indexOfObject:$wrong], ^n @"Invalid value '$wdesc' of type '$wtype' for expected type '$type'");
  229. %r RLMAssertThrowsWithReason([$array indexOfObject:NSNull.null], ^n @"Invalid value '<null>' of type 'NSNull' for expected type '$type'");
  230. %o XCTAssertEqual(NSNotFound, [$array indexOfObject:NSNull.null]);
  231. [$array addObjects:$values];
  232. XCTAssertEqual(1U, [$array indexOfObject:$v1]);
  233. }
  234. - (void)testIndexOfObjectWhere {
  235. %man RLMAssertThrowsWithReason([$array indexOfObjectWhere:@"TRUEPREDICATE"], @"implemented");
  236. %man RLMAssertThrowsWithReason([[$array sortedResultsUsingKeyPath:@"self" ascending:NO] ^n indexOfObjectWhere:@"TRUEPREDICATE"], @"implemented");
  237. %unman XCTAssertEqual(NSNotFound, [$array indexOfObjectWhere:@"TRUEPREDICATE"]);
  238. %unman [$array addObjects:$values];
  239. %unman XCTAssertEqual(0U, [$array indexOfObjectWhere:@"TRUEPREDICATE"]);
  240. %unman XCTAssertEqual(NSNotFound, [$array indexOfObjectWhere:@"FALSEPREDICATE"]);
  241. }
  242. - (void)testIndexOfObjectWithPredicate {
  243. %man RLMAssertThrowsWithReason([$array indexOfObjectWithPredicate:[NSPredicate predicateWithValue:YES]], @"implemented");
  244. %man RLMAssertThrowsWithReason([[$array sortedResultsUsingKeyPath:@"self" ascending:NO] ^n indexOfObjectWithPredicate:[NSPredicate predicateWithValue:YES]], @"implemented");
  245. %unman XCTAssertEqual(NSNotFound, [$array indexOfObjectWithPredicate:[NSPredicate predicateWithValue:YES]]);
  246. %unman [$array addObjects:$values];
  247. %unman XCTAssertEqual(0U, [$array indexOfObjectWithPredicate:[NSPredicate predicateWithValue:YES]]);
  248. %unman XCTAssertEqual(NSNotFound, [$array indexOfObjectWithPredicate:[NSPredicate predicateWithValue:NO]]);
  249. }
  250. - (void)testSort {
  251. %unman RLMAssertThrowsWithReason([$array sortedResultsUsingKeyPath:@"self" ascending:NO], ^n @"This method may only be called on RLMArray instances retrieved from an RLMRealm");
  252. %unman RLMAssertThrowsWithReason([$array sortedResultsUsingDescriptors:@[]], ^n @"This method may only be called on RLMArray instances retrieved from an RLMRealm");
  253. %man RLMAssertThrowsWithReason([$array sortedResultsUsingKeyPath:@"not self" ascending:NO], ^n @"can only be sorted on 'self'");
  254. %man %r [$array addObjects:@[$v0, $v1, $v0]];
  255. %man %o [$array addObjects:@[$v0, $v1, NSNull.null, $v1, $v0]];
  256. %man %r XCTAssertEqualObjects([[$array sortedResultsUsingDescriptors:@[]] valueForKey:@"self"], ^n (@[$v0, $v1, $v0]));
  257. %man %o XCTAssertEqualObjects([[$array sortedResultsUsingDescriptors:@[]] valueForKey:@"self"], ^n (@[$v0, $v1, NSNull.null, $v1, $v0]));
  258. %man %r XCTAssertEqualObjects([[$array sortedResultsUsingKeyPath:@"self" ascending:NO] valueForKey:@"self"], ^n (@[$v1, $v0, $v0]));
  259. %man %o XCTAssertEqualObjects([[$array sortedResultsUsingKeyPath:@"self" ascending:NO] valueForKey:@"self"], ^n (@[$v1, $v1, $v0, $v0, NSNull.null]));
  260. %man %r XCTAssertEqualObjects([[$array sortedResultsUsingKeyPath:@"self" ascending:YES] valueForKey:@"self"], ^n (@[$v0, $v0, $v1]));
  261. %man %o XCTAssertEqualObjects([[$array sortedResultsUsingKeyPath:@"self" ascending:YES] valueForKey:@"self"], ^n (@[NSNull.null, $v0, $v0, $v1, $v1]));
  262. }
  263. - (void)testFilter {
  264. %unman RLMAssertThrowsWithReason([$array objectsWhere:@"TRUEPREDICATE"], ^n @"This method may only be called on RLMArray instances retrieved from an RLMRealm");
  265. %unman RLMAssertThrowsWithReason([$array objectsWithPredicate:[NSPredicate predicateWithValue:YES]], ^n @"This method may only be called on RLMArray instances retrieved from an RLMRealm");
  266. %man RLMAssertThrowsWithReason([$array objectsWhere:@"TRUEPREDICATE"], ^n @"implemented");
  267. %man RLMAssertThrowsWithReason([$array objectsWithPredicate:[NSPredicate predicateWithValue:YES]], ^n @"implemented");
  268. %man RLMAssertThrowsWithReason([[$array sortedResultsUsingKeyPath:@"self" ascending:NO] ^n objectsWhere:@"TRUEPREDICATE"], @"implemented");
  269. %man RLMAssertThrowsWithReason([[$array sortedResultsUsingKeyPath:@"self" ascending:NO] ^n objectsWithPredicate:[NSPredicate predicateWithValue:YES]], @"implemented");
  270. }
  271. - (void)testNotifications {
  272. %unman RLMAssertThrowsWithReason([$array addNotificationBlock:^(__unused id a, __unused id c, __unused id e) { }], ^n @"This method may only be called on RLMArray instances retrieved from an RLMRealm");
  273. }
  274. - (void)testMin {
  275. %nominmax %unman RLMAssertThrowsWithReason([$array minOfProperty:@"self"], ^n @"minOfProperty: is not supported for $type array");
  276. %nominmax %man RLMAssertThrowsWithReason([$array minOfProperty:@"self"], ^n @"minOfProperty: is not supported for $type array '$class.$prop'");
  277. %minmax XCTAssertNil([$array minOfProperty:@"self"]);
  278. %minmax [$array addObjects:$values];
  279. %minmax XCTAssertEqualObjects([$array minOfProperty:@"self"], $v0);
  280. }
  281. - (void)testMax {
  282. %nominmax %unman RLMAssertThrowsWithReason([$array maxOfProperty:@"self"], ^n @"maxOfProperty: is not supported for $type array");
  283. %nominmax %man RLMAssertThrowsWithReason([$array maxOfProperty:@"self"], ^n @"maxOfProperty: is not supported for $type array '$class.$prop'");
  284. %minmax XCTAssertNil([$array maxOfProperty:@"self"]);
  285. %minmax [$array addObjects:$values];
  286. %minmax XCTAssertEqualObjects([$array maxOfProperty:@"self"], $v1);
  287. }
  288. - (void)testSum {
  289. %nosum %unman RLMAssertThrowsWithReason([$array sumOfProperty:@"self"], ^n @"sumOfProperty: is not supported for $type array");
  290. %nosum %man RLMAssertThrowsWithReason([$array sumOfProperty:@"self"], ^n @"sumOfProperty: is not supported for $type array '$class.$prop'");
  291. %sum XCTAssertEqualObjects([$array sumOfProperty:@"self"], @0);
  292. %sum [$array addObjects:$values];
  293. %sum XCTAssertEqualObjects([$array sumOfProperty:@"self"], @($s0 + $s1));
  294. }
  295. - (void)testAverage {
  296. %noavg %unman RLMAssertThrowsWithReason([$array averageOfProperty:@"self"], ^n @"averageOfProperty: is not supported for $type array");
  297. %noavg %man RLMAssertThrowsWithReason([$array averageOfProperty:@"self"], ^n @"averageOfProperty: is not supported for $type array '$class.$prop'");
  298. %avg XCTAssertNil([$array averageOfProperty:@"self"]);
  299. %avg [$array addObjects:$values];
  300. %avg XCTAssertEqualWithAccuracy([$array averageOfProperty:@"self"].doubleValue, ($s0 + $s1) / 2.0, .001);
  301. }
  302. - (void)testFastEnumeration {
  303. for (int i = 0; i < 10; ++i) {
  304. [$array addObjects:$values];
  305. }
  306. { ^nl NSUInteger i = 0; ^nl NSArray *values = $values; ^nl for (id value in $array) { ^nl XCTAssertEqualObjects(values[i++ % values.count], value); ^nl } ^nl XCTAssertEqual(i, $array.count); ^nl } ^nl
  307. }
  308. - (void)testValueForKeySelf {
  309. XCTAssertEqualObjects([$array valueForKey:@"self"], @[]);
  310. [$array addObjects:$values];
  311. XCTAssertEqualObjects([$array valueForKey:@"self"], ($values));
  312. }
  313. - (void)testValueForKeyNumericAggregates {
  314. %minmax XCTAssertNil([$array valueForKeyPath:@"@min.self"]);
  315. %minmax XCTAssertNil([$array valueForKeyPath:@"@max.self"]);
  316. %sum XCTAssertEqualObjects([$array valueForKeyPath:@"@sum.self"], @0);
  317. %avg XCTAssertNil([$array valueForKeyPath:@"@avg.self"]);
  318. [$array addObjects:$values];
  319. %minmax XCTAssertEqualObjects([$array valueForKeyPath:@"@min.self"], $v0);
  320. %minmax XCTAssertEqualObjects([$array valueForKeyPath:@"@max.self"], $v1);
  321. %sum XCTAssertEqualObjects([$array valueForKeyPath:@"@sum.self"], @($s0 + $s1));
  322. %avg XCTAssertEqualWithAccuracy([[$array valueForKeyPath:@"@avg.self"] doubleValue], ($s0 + $s1) / 2.0, .001);
  323. }
  324. // Sort the distinct results to match the order used in values, as it
  325. // doesn't preserve the order naturally
  326. static NSArray *sortedDistinctUnion(id array, NSString *type, NSString *prop) {
  327. return [[array valueForKeyPath:[NSString stringWithFormat:@"@distinctUnionOf%@.%@", type, prop]]
  328. sortedArrayUsingComparator:^NSComparisonResult(id a, id b) {
  329. bool aIsNull = a == NSNull.null;
  330. bool bIsNull = b == NSNull.null;
  331. if (aIsNull && bIsNull) {
  332. return 0;
  333. }
  334. if (aIsNull) {
  335. return 1;
  336. }
  337. if (bIsNull) {
  338. return -1;
  339. }
  340. if ([a isKindOfClass:[NSData class]]) {
  341. if ([a length] != [b length]) {
  342. return [a length] < [b length] ? -1 : 1;
  343. }
  344. int result = memcmp([a bytes], [b bytes], [a length]);
  345. if (!result) {
  346. return 0;
  347. }
  348. return result < 0 ? -1 : 1;
  349. }
  350. return [a compare:b];
  351. }];
  352. }
  353. - (void)testUnionOfObjects {
  354. XCTAssertEqualObjects([$array valueForKeyPath:@"@unionOfObjects.self"], @[]);
  355. XCTAssertEqualObjects([$array valueForKeyPath:@"@distinctUnionOfObjects.self"], @[]);
  356. [$array addObjects:$values2];
  357. XCTAssertEqualObjects([$array valueForKeyPath:@"@unionOfObjects.self"], ^n ($values2));
  358. XCTAssertEqualObjects(sortedDistinctUnion($array, @"Objects", @"self"), ^n ($values));
  359. }
  360. - (void)testUnionOfArrays {
  361. RLMResults *allRequired = [AllPrimitiveArrays allObjectsInRealm:realm];
  362. RLMResults *allOptional = [AllOptionalPrimitiveArrays allObjectsInRealm:realm];
  363. %man %r XCTAssertEqualObjects([allRequired valueForKeyPath:@"@unionOfArrays.$prop"], @[]);
  364. %man %o XCTAssertEqualObjects([allOptional valueForKeyPath:@"@unionOfArrays.$prop"], @[]);
  365. %man %r XCTAssertEqualObjects([allRequired valueForKeyPath:@"@distinctUnionOfArrays.$prop"], @[]);
  366. %man %o XCTAssertEqualObjects([allOptional valueForKeyPath:@"@distinctUnionOfArrays.$prop"], @[]);
  367. [$array addObjects:$values];
  368. [AllPrimitiveArrays createInRealm:realm withValue:managed];
  369. [AllOptionalPrimitiveArrays createInRealm:realm withValue:optManaged];
  370. %man %r XCTAssertEqualObjects([allRequired valueForKeyPath:@"@unionOfArrays.$prop"], ^n ($values2));
  371. %man %o XCTAssertEqualObjects([allOptional valueForKeyPath:@"@unionOfArrays.$prop"], ^n ($values2));
  372. %man %r XCTAssertEqualObjects(sortedDistinctUnion(allRequired, @"Arrays", @"$prop"), ^n ($values));
  373. %man %o XCTAssertEqualObjects(sortedDistinctUnion(allOptional, @"Arrays", @"$prop"), ^n ($values));
  374. }
  375. - (void)testSetValueForKey {
  376. RLMAssertThrowsWithReason([$array setValue:@0 forKey:@"not self"], ^n @"this class is not key value coding-compliant for the key not self.");
  377. RLMAssertThrowsWithReason([$array setValue:$wrong forKey:@"self"], ^n @"Invalid value '$wdesc' of type '$wtype' for expected type '$type'");
  378. %r RLMAssertThrowsWithReason([$array setValue:NSNull.null forKey:@"self"], ^n @"Invalid value '<null>' of type 'NSNull' for expected type '$type'");
  379. [$array addObjects:$values];
  380. [$array setValue:$v0 forKey:@"self"];
  381. XCTAssertEqualObjects($array[0], $v0);
  382. XCTAssertEqualObjects($array[1], $v0);
  383. %o XCTAssertEqualObjects($array[2], $v0);
  384. %o [$array setValue:NSNull.null forKey:@"self"];
  385. %o XCTAssertEqualObjects($array[0], NSNull.null);
  386. }
  387. - (void)testAssignment {
  388. $array = (id)@[$v1]; ^nl XCTAssertEqualObjects($array[0], $v1);
  389. // Should replace and not append
  390. $array = (id)$values; ^nl XCTAssertEqualObjects([$array valueForKey:@"self"], ($values)); ^nl
  391. // Should not clear the array
  392. $array = $array; ^nl XCTAssertEqualObjects([$array valueForKey:@"self"], ($values)); ^nl
  393. [unmanaged.intObj removeAllObjects];
  394. unmanaged.intObj = managed.intObj;
  395. XCTAssertEqualObjects([unmanaged.intObj valueForKey:@"self"], (@[@2, @3]));
  396. [managed.intObj removeAllObjects];
  397. managed.intObj = unmanaged.intObj;
  398. XCTAssertEqualObjects([managed.intObj valueForKey:@"self"], (@[@2, @3]));
  399. }
  400. - (void)testDynamicAssignment {
  401. $obj[@"$prop"] = (id)@[$v1]; ^nl XCTAssertEqualObjects($obj[@"$prop"][0], $v1);
  402. // Should replace and not append
  403. $obj[@"$prop"] = (id)$values; ^nl XCTAssertEqualObjects([$obj[@"$prop"] valueForKey:@"self"], ($values)); ^nl
  404. // Should not clear the array
  405. $obj[@"$prop"] = $obj[@"$prop"]; ^nl XCTAssertEqualObjects([$obj[@"$prop"] valueForKey:@"self"], ($values)); ^nl
  406. [unmanaged[@"intObj"] removeAllObjects];
  407. unmanaged[@"intObj"] = managed.intObj;
  408. XCTAssertEqualObjects([unmanaged[@"intObj"] valueForKey:@"self"], (@[@2, @3]));
  409. [managed[@"intObj"] removeAllObjects];
  410. managed[@"intObj"] = unmanaged.intObj;
  411. XCTAssertEqualObjects([managed[@"intObj"] valueForKey:@"self"], (@[@2, @3]));
  412. }
  413. - (void)testInvalidAssignment {
  414. RLMAssertThrowsWithReason(unmanaged.intObj = (id)@[NSNull.null],
  415. @"Invalid value '<null>' of type 'NSNull' for 'int' array property 'AllPrimitiveArrays.intObj'.");
  416. RLMAssertThrowsWithReason(unmanaged.intObj = (id)@[@"a"],
  417. @"Invalid value 'a' of type '__NSCFConstantString' for 'int' array property 'AllPrimitiveArrays.intObj'.");
  418. RLMAssertThrowsWithReason(unmanaged.intObj = (id)(@[@1, @"a"]),
  419. @"Invalid value 'a' of type '__NSCFConstantString' for 'int' array property 'AllPrimitiveArrays.intObj'.");
  420. RLMAssertThrowsWithReason(unmanaged.intObj = (id)unmanaged.floatObj,
  421. @"RLMArray<float> does not match expected type 'int' for property 'AllPrimitiveArrays.intObj'.");
  422. RLMAssertThrowsWithReason(unmanaged.intObj = (id)optUnmanaged.intObj,
  423. @"RLMArray<int?> does not match expected type 'int' for property 'AllPrimitiveArrays.intObj'.");
  424. RLMAssertThrowsWithReason(unmanaged[@"intObj"] = unmanaged[@"floatObj"],
  425. @"RLMArray<float> does not match expected type 'int' for property 'AllPrimitiveArrays.intObj'.");
  426. RLMAssertThrowsWithReason(unmanaged[@"intObj"] = optUnmanaged[@"intObj"],
  427. @"RLMArray<int?> does not match expected type 'int' for property 'AllPrimitiveArrays.intObj'.");
  428. RLMAssertThrowsWithReason(managed.intObj = (id)@[NSNull.null],
  429. @"Invalid value '<null>' of type 'NSNull' for 'int' array property 'AllPrimitiveArrays.intObj'.");
  430. RLMAssertThrowsWithReason(managed.intObj = (id)@[@"a"],
  431. @"Invalid value 'a' of type '__NSCFConstantString' for 'int' array property 'AllPrimitiveArrays.intObj'.");
  432. RLMAssertThrowsWithReason(managed.intObj = (id)(@[@1, @"a"]),
  433. @"Invalid value 'a' of type '__NSCFConstantString' for 'int' array property 'AllPrimitiveArrays.intObj'.");
  434. RLMAssertThrowsWithReason(managed.intObj = (id)managed.floatObj,
  435. @"RLMArray<float> does not match expected type 'int' for property 'AllPrimitiveArrays.intObj'.");
  436. RLMAssertThrowsWithReason(managed.intObj = (id)optManaged.intObj,
  437. @"RLMArray<int?> does not match expected type 'int' for property 'AllPrimitiveArrays.intObj'.");
  438. RLMAssertThrowsWithReason(managed[@"intObj"] = (id)managed[@"floatObj"],
  439. @"RLMArray<float> does not match expected type 'int' for property 'AllPrimitiveArrays.intObj'.");
  440. RLMAssertThrowsWithReason(managed[@"intObj"] = (id)optManaged[@"intObj"],
  441. @"RLMArray<int?> does not match expected type 'int' for property 'AllPrimitiveArrays.intObj'.");
  442. }
  443. - (void)testAllMethodsCheckThread {
  444. RLMArray *array = managed.intObj;
  445. [self dispatchAsyncAndWait:^{
  446. RLMAssertThrowsWithReason([array count], @"thread");
  447. RLMAssertThrowsWithReason([array objectAtIndex:0], @"thread");
  448. RLMAssertThrowsWithReason([array firstObject], @"thread");
  449. RLMAssertThrowsWithReason([array lastObject], @"thread");
  450. RLMAssertThrowsWithReason([array addObject:@0], @"thread");
  451. RLMAssertThrowsWithReason([array addObjects:@[@0]], @"thread");
  452. RLMAssertThrowsWithReason([array insertObject:@0 atIndex:0], @"thread");
  453. RLMAssertThrowsWithReason([array removeObjectAtIndex:0], @"thread");
  454. RLMAssertThrowsWithReason([array removeLastObject], @"thread");
  455. RLMAssertThrowsWithReason([array removeAllObjects], @"thread");
  456. RLMAssertThrowsWithReason([array replaceObjectAtIndex:0 withObject:@0], @"thread");
  457. RLMAssertThrowsWithReason([array moveObjectAtIndex:0 toIndex:1], @"thread");
  458. RLMAssertThrowsWithReason([array exchangeObjectAtIndex:0 withObjectAtIndex:1], @"thread");
  459. RLMAssertThrowsWithReason([array indexOfObject:@1], @"thread");
  460. /* RLMAssertThrowsWithReason([array indexOfObjectWhere:@"TRUEPREDICATE"], @"thread"); */
  461. /* RLMAssertThrowsWithReason([array indexOfObjectWithPredicate:[NSPredicate predicateWithValue:NO]], @"thread"); */
  462. /* RLMAssertThrowsWithReason([array objectsWhere:@"TRUEPREDICATE"], @"thread"); */
  463. /* RLMAssertThrowsWithReason([array objectsWithPredicate:[NSPredicate predicateWithValue:NO]], @"thread"); */
  464. RLMAssertThrowsWithReason([array sortedResultsUsingKeyPath:@"self" ascending:YES], @"thread");
  465. RLMAssertThrowsWithReason([array sortedResultsUsingDescriptors:@[[RLMSortDescriptor sortDescriptorWithKeyPath:@"self" ascending:YES]]], @"thread");
  466. RLMAssertThrowsWithReason(array[0], @"thread");
  467. RLMAssertThrowsWithReason(array[0] = @0, @"thread");
  468. RLMAssertThrowsWithReason([array valueForKey:@"self"], @"thread");
  469. RLMAssertThrowsWithReason([array setValue:@1 forKey:@"self"], @"thread");
  470. RLMAssertThrowsWithReason({for (__unused id obj in array);}, @"thread");
  471. }];
  472. }
  473. - (void)testAllMethodsCheckForInvalidation {
  474. RLMArray *array = managed.intObj;
  475. [realm cancelWriteTransaction];
  476. [realm invalidate];
  477. XCTAssertNoThrow([array objectClassName]);
  478. XCTAssertNoThrow([array realm]);
  479. XCTAssertNoThrow([array isInvalidated]);
  480. RLMAssertThrowsWithReason([array count], @"invalidated");
  481. RLMAssertThrowsWithReason([array objectAtIndex:0], @"invalidated");
  482. RLMAssertThrowsWithReason([array firstObject], @"invalidated");
  483. RLMAssertThrowsWithReason([array lastObject], @"invalidated");
  484. RLMAssertThrowsWithReason([array addObject:@0], @"invalidated");
  485. RLMAssertThrowsWithReason([array addObjects:@[@0]], @"invalidated");
  486. RLMAssertThrowsWithReason([array insertObject:@0 atIndex:0], @"invalidated");
  487. RLMAssertThrowsWithReason([array removeObjectAtIndex:0], @"invalidated");
  488. RLMAssertThrowsWithReason([array removeLastObject], @"invalidated");
  489. RLMAssertThrowsWithReason([array removeAllObjects], @"invalidated");
  490. RLMAssertThrowsWithReason([array replaceObjectAtIndex:0 withObject:@0], @"invalidated");
  491. RLMAssertThrowsWithReason([array moveObjectAtIndex:0 toIndex:1], @"invalidated");
  492. RLMAssertThrowsWithReason([array exchangeObjectAtIndex:0 withObjectAtIndex:1], @"invalidated");
  493. RLMAssertThrowsWithReason([array indexOfObject:@1], @"invalidated");
  494. /* RLMAssertThrowsWithReason([array indexOfObjectWhere:@"TRUEPREDICATE"], @"invalidated"); */
  495. /* RLMAssertThrowsWithReason([array indexOfObjectWithPredicate:[NSPredicate predicateWithValue:YES]], @"invalidated"); */
  496. /* RLMAssertThrowsWithReason([array objectsWhere:@"TRUEPREDICATE"], @"invalidated"); */
  497. /* RLMAssertThrowsWithReason([array objectsWithPredicate:[NSPredicate predicateWithValue:YES]], @"invalidated"); */
  498. RLMAssertThrowsWithReason([array sortedResultsUsingKeyPath:@"self" ascending:YES], @"invalidated");
  499. RLMAssertThrowsWithReason([array sortedResultsUsingDescriptors:@[[RLMSortDescriptor sortDescriptorWithKeyPath:@"self" ascending:YES]]], @"invalidated");
  500. RLMAssertThrowsWithReason(array[0], @"invalidated");
  501. RLMAssertThrowsWithReason(array[0] = @0, @"invalidated");
  502. RLMAssertThrowsWithReason([array valueForKey:@"self"], @"invalidated");
  503. RLMAssertThrowsWithReason([array setValue:@1 forKey:@"self"], @"invalidated");
  504. RLMAssertThrowsWithReason({for (__unused id obj in array);}, @"invalidated");
  505. [realm beginWriteTransaction];
  506. }
  507. - (void)testMutatingMethodsCheckForWriteTransaction {
  508. RLMArray *array = managed.intObj;
  509. [array addObject:@0];
  510. [realm commitWriteTransaction];
  511. XCTAssertNoThrow([array objectClassName]);
  512. XCTAssertNoThrow([array realm]);
  513. XCTAssertNoThrow([array isInvalidated]);
  514. XCTAssertNoThrow([array count]);
  515. XCTAssertNoThrow([array objectAtIndex:0]);
  516. XCTAssertNoThrow([array firstObject]);
  517. XCTAssertNoThrow([array lastObject]);
  518. XCTAssertNoThrow([array indexOfObject:@1]);
  519. /* XCTAssertNoThrow([array indexOfObjectWhere:@"TRUEPREDICATE"]); */
  520. /* XCTAssertNoThrow([array indexOfObjectWithPredicate:[NSPredicate predicateWithValue:YES]]); */
  521. /* XCTAssertNoThrow([array objectsWhere:@"TRUEPREDICATE"]); */
  522. /* XCTAssertNoThrow([array objectsWithPredicate:[NSPredicate predicateWithValue:YES]]); */
  523. XCTAssertNoThrow([array sortedResultsUsingKeyPath:@"self" ascending:YES]);
  524. XCTAssertNoThrow([array sortedResultsUsingDescriptors:@[[RLMSortDescriptor sortDescriptorWithKeyPath:@"self" ascending:YES]]]);
  525. XCTAssertNoThrow(array[0]);
  526. XCTAssertNoThrow([array valueForKey:@"self"]);
  527. XCTAssertNoThrow({for (__unused id obj in array);});
  528. RLMAssertThrowsWithReason([array addObject:@0], @"write transaction");
  529. RLMAssertThrowsWithReason([array addObjects:@[@0]], @"write transaction");
  530. RLMAssertThrowsWithReason([array insertObject:@0 atIndex:0], @"write transaction");
  531. RLMAssertThrowsWithReason([array removeObjectAtIndex:0], @"write transaction");
  532. RLMAssertThrowsWithReason([array removeLastObject], @"write transaction");
  533. RLMAssertThrowsWithReason([array removeAllObjects], @"write transaction");
  534. RLMAssertThrowsWithReason([array replaceObjectAtIndex:0 withObject:@0], @"write transaction");
  535. RLMAssertThrowsWithReason([array moveObjectAtIndex:0 toIndex:1], @"write transaction");
  536. RLMAssertThrowsWithReason([array exchangeObjectAtIndex:0 withObjectAtIndex:1], @"write transaction");
  537. RLMAssertThrowsWithReason(array[0] = @0, @"write transaction");
  538. RLMAssertThrowsWithReason([array setValue:@1 forKey:@"self"], @"write transaction");
  539. }
  540. - (void)testDeleteOwningObject {
  541. RLMArray *array = managed.intObj;
  542. XCTAssertFalse(array.isInvalidated);
  543. [realm deleteObject:managed];
  544. XCTAssertTrue(array.isInvalidated);
  545. }
  546. #pragma clang diagnostic ignored "-Warc-retain-cycles"
  547. - (void)testNotificationSentInitially {
  548. [realm commitWriteTransaction];
  549. id expectation = [self expectationWithDescription:@""];
  550. id token = [managed.intObj addNotificationBlock:^(RLMArray *array, RLMCollectionChange *change, NSError *error) {
  551. XCTAssertNotNil(array);
  552. XCTAssertNil(change);
  553. XCTAssertNil(error);
  554. [expectation fulfill];
  555. }];
  556. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  557. [(RLMNotificationToken *)token invalidate];
  558. }
  559. - (void)testNotificationSentAfterCommit {
  560. [realm commitWriteTransaction];
  561. __block bool first = true;
  562. __block id expectation = [self expectationWithDescription:@""];
  563. id token = [managed.intObj addNotificationBlock:^(RLMArray *array, RLMCollectionChange *change, NSError *error) {
  564. XCTAssertNotNil(array);
  565. XCTAssertNil(error);
  566. if (first) {
  567. XCTAssertNil(change);
  568. }
  569. else {
  570. XCTAssertEqualObjects(change.insertions, @[@0]);
  571. }
  572. first = false;
  573. [expectation fulfill];
  574. }];
  575. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  576. expectation = [self expectationWithDescription:@""];
  577. [self dispatchAsyncAndWait:^{
  578. RLMRealm *r = [RLMRealm defaultRealm];
  579. [r transactionWithBlock:^{
  580. RLMArray *array = [(AllPrimitiveArrays *)[AllPrimitiveArrays allObjectsInRealm:r].firstObject intObj];
  581. [array addObject:@0];
  582. }];
  583. }];
  584. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  585. [(RLMNotificationToken *)token invalidate];
  586. }
  587. - (void)testNotificationNotSentForUnrelatedChange {
  588. [realm commitWriteTransaction];
  589. id expectation = [self expectationWithDescription:@""];
  590. id token = [managed.intObj addNotificationBlock:^(__unused RLMArray *array, __unused RLMCollectionChange *change, __unused NSError *error) {
  591. // will throw if it's incorrectly called a second time due to the
  592. // unrelated write transaction
  593. [expectation fulfill];
  594. }];
  595. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  596. // All notification blocks are called as part of a single runloop event, so
  597. // waiting for this one also waits for the above one to get a chance to run
  598. [self waitForNotification:RLMRealmDidChangeNotification realm:realm block:^{
  599. [self dispatchAsyncAndWait:^{
  600. RLMRealm *r = [RLMRealm defaultRealm];
  601. [r transactionWithBlock:^{
  602. [AllPrimitiveArrays createInRealm:r withValue:@[]];
  603. }];
  604. }];
  605. }];
  606. [(RLMNotificationToken *)token invalidate];
  607. }
  608. - (void)testNotificationSentOnlyForActualRefresh {
  609. [realm commitWriteTransaction];
  610. __block id expectation = [self expectationWithDescription:@""];
  611. id token = [managed.intObj addNotificationBlock:^(RLMArray *array, __unused RLMCollectionChange *change, NSError *error) {
  612. XCTAssertNotNil(array);
  613. XCTAssertNil(error);
  614. // will throw if it's called a second time before we create the new
  615. // expectation object immediately before manually refreshing
  616. [expectation fulfill];
  617. }];
  618. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  619. // Turn off autorefresh, so the background commit should not result in a notification
  620. realm.autorefresh = NO;
  621. // All notification blocks are called as part of a single runloop event, so
  622. // waiting for this one also waits for the above one to get a chance to run
  623. [self waitForNotification:RLMRealmRefreshRequiredNotification realm:realm block:^{
  624. [self dispatchAsyncAndWait:^{
  625. RLMRealm *r = [RLMRealm defaultRealm];
  626. [r transactionWithBlock:^{
  627. RLMArray *array = [(AllPrimitiveArrays *)[AllPrimitiveArrays allObjectsInRealm:r].firstObject intObj];
  628. [array addObject:@0];
  629. }];
  630. }];
  631. }];
  632. expectation = [self expectationWithDescription:@""];
  633. [realm refresh];
  634. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  635. [(RLMNotificationToken *)token invalidate];
  636. }
  637. - (void)testDeletingObjectWithNotificationsRegistered {
  638. [managed.intObj addObjects:@[@10, @20]];
  639. [realm commitWriteTransaction];
  640. __block bool first = true;
  641. __block id expectation = [self expectationWithDescription:@""];
  642. id token = [managed.intObj addNotificationBlock:^(RLMArray *array, RLMCollectionChange *change, NSError *error) {
  643. XCTAssertNotNil(array);
  644. XCTAssertNil(error);
  645. if (first) {
  646. XCTAssertNil(change);
  647. first = false;
  648. }
  649. else {
  650. XCTAssertEqualObjects(change.deletions, (@[@0, @1]));
  651. }
  652. [expectation fulfill];
  653. }];
  654. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  655. [realm beginWriteTransaction];
  656. [realm deleteObject:managed];
  657. [realm commitWriteTransaction];
  658. expectation = [self expectationWithDescription:@""];
  659. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  660. [(RLMNotificationToken *)token invalidate];
  661. }
  662. @end