ArrayPropertyTests.m 69 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496
  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. @implementation DogArrayObject
  20. @end
  21. @interface ArrayPropertyTests : RLMTestCase
  22. @end
  23. @implementation ArrayPropertyTests
  24. -(void)testPopulateEmptyArray {
  25. RLMRealm *realm = [self realmWithTestPath];
  26. [realm beginWriteTransaction];
  27. ArrayPropertyObject *array = [ArrayPropertyObject createInRealm:realm withValue:@[@"arrayObject", @[], @[]]];
  28. XCTAssertNotNil(array.array, @"Should be able to get an empty array");
  29. XCTAssertEqual(array.array.count, 0U, @"Should start with no array elements");
  30. StringObject *obj = [[StringObject alloc] init];
  31. obj.stringCol = @"a";
  32. [array.array addObject:obj];
  33. [array.array addObject:[StringObject createInRealm:realm withValue:@[@"b"]]];
  34. [array.array addObject:obj];
  35. [realm commitWriteTransaction];
  36. XCTAssertEqual(array.array.count, 3U, @"Should have three elements in array");
  37. XCTAssertEqualObjects([array.array[0] stringCol], @"a", @"First element should have property value 'a'");
  38. XCTAssertEqualObjects([array.array[1] stringCol], @"b", @"Second element should have property value 'b'");
  39. XCTAssertEqualObjects([array.array[2] stringCol], @"a", @"Third element should have property value 'a'");
  40. RLMArray *arrayProp = array.array;
  41. RLMAssertThrowsWithReasonMatching([arrayProp addObject:obj], @"write transaction");
  42. // make sure we can fast enumerate
  43. for (RLMObject *obj in array.array) {
  44. XCTAssertTrue(obj.description.length, @"Object should have description");
  45. }
  46. }
  47. -(void)testModifyDetatchedArray {
  48. RLMRealm *realm = [self realmWithTestPath];
  49. [realm beginWriteTransaction];
  50. ArrayPropertyObject *arObj = [ArrayPropertyObject createInRealm:realm withValue:@[@"arrayObject", @[], @[]]];
  51. XCTAssertNotNil(arObj.array, @"Should be able to get an empty array");
  52. XCTAssertEqual(arObj.array.count, 0U, @"Should start with no array elements");
  53. StringObject *obj = [[StringObject alloc] init];
  54. obj.stringCol = @"a";
  55. RLMArray *array = arObj.array;
  56. [array addObject:obj];
  57. [array addObject:[StringObject createInRealm:realm withValue:@[@"b"]]];
  58. [realm commitWriteTransaction];
  59. XCTAssertEqual(array.count, 2U, @"Should have two elements in array");
  60. XCTAssertEqualObjects([array[0] stringCol], @"a", @"First element should have property value 'a'");
  61. XCTAssertEqualObjects([arObj.array[1] stringCol], @"b", @"Second element should have property value 'b'");
  62. RLMAssertThrowsWithReasonMatching([array addObject:obj], @"write transaction");
  63. }
  64. - (void)testDeleteUnmanagedObjectWithArrayProperty {
  65. ArrayPropertyObject *arObj = [[ArrayPropertyObject alloc] initWithValue:@[@"arrayObject", @[@[@"a"]], @[]]];
  66. RLMArray *stringArray = arObj.array;
  67. XCTAssertFalse(stringArray.isInvalidated, @"stringArray should be valid after creation.");
  68. arObj = nil;
  69. XCTAssertFalse(stringArray.isInvalidated, @"stringArray should still be valid after parent deletion.");
  70. }
  71. - (void)testDeleteObjectWithArrayProperty {
  72. RLMRealm *realm = [self realmWithTestPath];
  73. [realm beginWriteTransaction];
  74. ArrayPropertyObject *arObj = [ArrayPropertyObject createInRealm:realm withValue:@[@"arrayObject", @[@[@"a"]], @[]]];
  75. RLMArray *stringArray = arObj.array;
  76. XCTAssertFalse(stringArray.isInvalidated, @"stringArray should be valid after creation.");
  77. [realm deleteObject:arObj];
  78. XCTAssertTrue(stringArray.isInvalidated, @"stringArray should be invalid after parent deletion.");
  79. [realm commitWriteTransaction];
  80. }
  81. - (void)testDeleteObjectInArrayProperty {
  82. RLMRealm *realm = [self realmWithTestPath];
  83. [realm beginWriteTransaction];
  84. ArrayPropertyObject *arObj = [ArrayPropertyObject createInRealm:realm withValue:@[@"arrayObject", @[@[@"a"]], @[]]];
  85. RLMArray *stringArray = arObj.array;
  86. StringObject *firstObject = stringArray.firstObject;
  87. [realm deleteObjects:[StringObject allObjectsInRealm:realm]];
  88. XCTAssertFalse(stringArray.isInvalidated, @"stringArray should be valid after member object deletion.");
  89. XCTAssertTrue(firstObject.isInvalidated, @"firstObject should be invalid after deletion.");
  90. XCTAssertEqual(stringArray.count, 0U, @"stringArray.count should be zero after deleting its only member.");
  91. [realm commitWriteTransaction];
  92. }
  93. -(void)testInsertMultiple {
  94. RLMRealm *realm = [self realmWithTestPath];
  95. [realm beginWriteTransaction];
  96. ArrayPropertyObject *obj = [ArrayPropertyObject createInRealm:realm withValue:@[@"arrayObject", @[], @[]]];
  97. StringObject *child1 = [StringObject createInRealm:realm withValue:@[@"a"]];
  98. StringObject *child2 = [[StringObject alloc] init];
  99. child2.stringCol = @"b";
  100. [obj.array addObjects:@[child2, child1]];
  101. [realm commitWriteTransaction];
  102. RLMResults *children = [StringObject allObjectsInRealm:realm];
  103. XCTAssertEqualObjects([children[0] stringCol], @"a", @"First child should be 'a'");
  104. XCTAssertEqualObjects([children[1] stringCol], @"b", @"Second child should be 'b'");
  105. }
  106. -(void)testInsertAtIndex {
  107. RLMRealm *realm = [self realmWithTestPath];
  108. [realm beginWriteTransaction];
  109. ArrayPropertyObject *obj = [ArrayPropertyObject createInRealm:realm withValue:@[@"arrayObject", @[], @[]]];
  110. StringObject *child1 = [StringObject createInRealm:realm withValue:@[@"a"]];
  111. StringObject *child2 = [[StringObject alloc] init];
  112. child2.stringCol = @"b";
  113. [obj.array addObject:child2];
  114. RLMAssertThrowsWithReasonMatching([obj.array insertObject:child1 atIndex:2], @"must be less than 2");
  115. [realm commitWriteTransaction];
  116. RLMArray *children = obj.array;
  117. XCTAssertEqual(children.count, 1U);
  118. XCTAssertEqualObjects([children[0] stringCol], @"b", @"Only child should be 'b'");
  119. }
  120. - (void)testMove {
  121. RLMRealm *realm = [self realmWithTestPath];
  122. ArrayPropertyObject *obj = [[ArrayPropertyObject alloc] initWithValue:@[@"arrayObject", @[@[@"a"], @[@"b"]], @[]]];
  123. RLM_GENERIC_ARRAY(StringObject) *children = obj.array;
  124. [children moveObjectAtIndex:1 toIndex:0];
  125. XCTAssertEqualObjects([children[0] stringCol], @"b");
  126. XCTAssertEqualObjects([children[1] stringCol], @"a");
  127. [children moveObjectAtIndex:0 toIndex:1];
  128. XCTAssertEqualObjects([children[0] stringCol], @"a");
  129. XCTAssertEqualObjects([children[1] stringCol], @"b");
  130. [children moveObjectAtIndex:0 toIndex:0];
  131. XCTAssertEqualObjects([children[0] stringCol], @"a");
  132. XCTAssertEqualObjects([children[1] stringCol], @"b");
  133. RLMAssertThrowsWithReasonMatching([children moveObjectAtIndex:0 toIndex:2], @"must be less than 2");
  134. RLMAssertThrowsWithReasonMatching([children moveObjectAtIndex:2 toIndex:0], @"must be less than 2");
  135. [realm beginWriteTransaction];
  136. [realm addObject:obj];
  137. children = obj.array;
  138. [children moveObjectAtIndex:1 toIndex:0];
  139. XCTAssertEqualObjects([children[0] stringCol], @"b");
  140. XCTAssertEqualObjects([children[1] stringCol], @"a");
  141. [children moveObjectAtIndex:0 toIndex:1];
  142. XCTAssertEqualObjects([children[0] stringCol], @"a");
  143. XCTAssertEqualObjects([children[1] stringCol], @"b");
  144. [children moveObjectAtIndex:0 toIndex:0];
  145. XCTAssertEqualObjects([children[0] stringCol], @"a");
  146. XCTAssertEqualObjects([children[1] stringCol], @"b");
  147. RLMAssertThrowsWithReasonMatching([children moveObjectAtIndex:0 toIndex:2], @"must be less than 2");
  148. RLMAssertThrowsWithReasonMatching([children moveObjectAtIndex:2 toIndex:0], @"must be less than 2");
  149. [realm commitWriteTransaction];
  150. RLMAssertThrowsWithReasonMatching([children moveObjectAtIndex:1 toIndex:0], @"write transaction");
  151. }
  152. - (void)testAddInvalidated {
  153. RLMRealm *realm = [RLMRealm defaultRealm];
  154. [realm beginWriteTransaction];
  155. CompanyObject *company = [CompanyObject createInDefaultRealmWithValue:@[@"company", @[]]];
  156. EmployeeObject *person = [[EmployeeObject alloc] init];
  157. person.name = @"Mary";
  158. [realm addObject:person];
  159. [realm deleteObjects:[EmployeeObject allObjects]];
  160. RLMAssertThrowsWithReasonMatching([company.employees addObject:person], @"invalidated");
  161. RLMAssertThrowsWithReasonMatching([company.employees insertObject:person atIndex:0], @"invalidated");
  162. [realm cancelWriteTransaction];
  163. }
  164. - (void)testAddNil {
  165. RLMRealm *realm = [RLMRealm defaultRealm];
  166. [realm beginWriteTransaction];
  167. CompanyObject *company = [CompanyObject createInDefaultRealmWithValue:@[@"company", @[]]];
  168. RLMAssertThrowsWithReason([company.employees addObject:self.nonLiteralNil],
  169. @"Invalid nil value for array of 'EmployeeObject'.");
  170. [realm cancelWriteTransaction];
  171. }
  172. - (void)testUnmanaged {
  173. RLMRealm *realm = [self realmWithTestPath];
  174. ArrayPropertyObject *array = [[ArrayPropertyObject alloc] init];
  175. array.name = @"name";
  176. XCTAssertNotNil(array.array, @"RLMArray property should get created on access");
  177. XCTAssertNil(array.array.firstObject, @"No objects added yet");
  178. XCTAssertNil(array.array.lastObject, @"No objects added yet");
  179. StringObject *obj1 = [[StringObject alloc] init];
  180. obj1.stringCol = @"a";
  181. StringObject *obj2 = [[StringObject alloc] init];
  182. obj2.stringCol = @"b";
  183. StringObject *obj3 = [[StringObject alloc] init];
  184. obj3.stringCol = @"c";
  185. [array.array addObject:obj1];
  186. [array.array addObject:obj2];
  187. [array.array addObject:obj3];
  188. XCTAssertEqualObjects(array.array.firstObject, obj1, @"Objects should be equal");
  189. XCTAssertEqualObjects(array.array.lastObject, obj3, @"Objects should be equal");
  190. XCTAssertEqualObjects([array.array objectAtIndex:1], obj2, @"Objects should be equal");
  191. [realm beginWriteTransaction];
  192. [realm addObject:array];
  193. [realm commitWriteTransaction];
  194. XCTAssertEqual(array.array.count, 3U, @"Should have two elements in array");
  195. XCTAssertEqualObjects([array.array[0] stringCol], @"a", @"First element should have property value 'a'");
  196. XCTAssertEqualObjects([array.array[1] stringCol], @"b", @"Second element should have property value 'b'");
  197. [realm beginWriteTransaction];
  198. [array.array replaceObjectAtIndex:0 withObject:obj3];
  199. XCTAssertTrue([[array.array objectAtIndex:0] isEqualToObject:obj3], @"Objects should be replaced");
  200. array.array[0] = obj1;
  201. XCTAssertTrue([obj1 isEqualToObject:[array.array objectAtIndex:0]], @"Objects should be replaced");
  202. [array.array removeLastObject];
  203. XCTAssertEqual(array.array.count, 2U, @"2 objects left");
  204. [array.array addObject:obj1];
  205. [array.array removeAllObjects];
  206. XCTAssertEqual(array.array.count, 0U, @"All objects removed");
  207. [realm commitWriteTransaction];
  208. ArrayPropertyObject *intArray = [[ArrayPropertyObject alloc] init];
  209. IntObject *intObj = [[IntObject alloc] init];
  210. intObj.intCol = 1;
  211. RLMAssertThrowsWithReasonMatching([intArray.array addObject:(id)intObj], @"IntObject.*StringObject");
  212. [intArray.intArray addObject:intObj];
  213. XCTAssertThrows([intArray.intArray objectsWhere:@"intCol == 1"], @"Should throw on unmanaged RLMArray");
  214. XCTAssertThrows(([intArray.intArray objectsWithPredicate:[NSPredicate predicateWithFormat:@"intCol == %i", 1]]), @"Should throw on unmanaged RLMArray");
  215. XCTAssertThrows([intArray.intArray sortedResultsUsingKeyPath:@"intCol" ascending:YES], @"Should throw on unmanaged RLMArray");
  216. XCTAssertEqual(0U, [intArray.intArray indexOfObjectWhere:@"intCol == 1"]);
  217. XCTAssertEqual(0U, ([intArray.intArray indexOfObjectWithPredicate:[NSPredicate predicateWithFormat:@"intCol == %i", 1]]));
  218. XCTAssertEqual([intArray.intArray indexOfObject:intObj], 0U, @"Should be first element");
  219. XCTAssertEqual([intArray.intArray indexOfObject:intObj], 0U, @"Should be first element");
  220. // test unmanaged with literals
  221. __unused ArrayPropertyObject *obj = [[ArrayPropertyObject alloc] initWithValue:@[@"n", @[], @[[[IntObject alloc] initWithValue:@[@1]]]]];
  222. }
  223. - (void)testUnmanagedPrimitive {
  224. AllPrimitiveArrays *obj = [[AllPrimitiveArrays alloc] init];
  225. XCTAssertTrue([obj.intObj isKindOfClass:[RLMArray class]]);
  226. XCTAssertTrue([obj.floatObj isKindOfClass:[RLMArray class]]);
  227. XCTAssertTrue([obj.doubleObj isKindOfClass:[RLMArray class]]);
  228. XCTAssertTrue([obj.boolObj isKindOfClass:[RLMArray class]]);
  229. XCTAssertTrue([obj.stringObj isKindOfClass:[RLMArray class]]);
  230. XCTAssertTrue([obj.dataObj isKindOfClass:[RLMArray class]]);
  231. XCTAssertTrue([obj.dateObj isKindOfClass:[RLMArray class]]);
  232. [obj.intObj addObject:@1];
  233. XCTAssertEqualObjects(obj.intObj[0], @1);
  234. XCTAssertThrows([obj.intObj addObject:@""]);
  235. RLMRealm *realm = [RLMRealm defaultRealm];
  236. [realm beginWriteTransaction];
  237. obj = [AllPrimitiveArrays createInRealm:realm withValue:@[@[],@[],@[],@[],@[],@[],@[]]];
  238. XCTAssertTrue([obj.intObj isKindOfClass:[RLMArray class]]);
  239. XCTAssertTrue([obj.floatObj isKindOfClass:[RLMArray class]]);
  240. XCTAssertTrue([obj.doubleObj isKindOfClass:[RLMArray class]]);
  241. XCTAssertTrue([obj.boolObj isKindOfClass:[RLMArray class]]);
  242. XCTAssertTrue([obj.stringObj isKindOfClass:[RLMArray class]]);
  243. XCTAssertTrue([obj.dataObj isKindOfClass:[RLMArray class]]);
  244. XCTAssertTrue([obj.dateObj isKindOfClass:[RLMArray class]]);
  245. [obj.intObj addObject:@5];
  246. XCTAssertEqualObjects(obj.intObj.firstObject, @5);
  247. }
  248. - (void)testReplaceObjectAtIndexInUnmanagedArray {
  249. ArrayPropertyObject *array = [[ArrayPropertyObject alloc] init];
  250. array.name = @"name";
  251. StringObject *stringObj1 = [[StringObject alloc] init];
  252. stringObj1.stringCol = @"a";
  253. StringObject *stringObj2 = [[StringObject alloc] init];
  254. stringObj2.stringCol = @"b";
  255. StringObject *stringObj3 = [[StringObject alloc] init];
  256. stringObj3.stringCol = @"c";
  257. [array.array addObject:stringObj1];
  258. [array.array addObject:stringObj2];
  259. [array.array addObject:stringObj3];
  260. IntObject *intObj1 = [[IntObject alloc] init];
  261. intObj1.intCol = 0;
  262. IntObject *intObj2 = [[IntObject alloc] init];
  263. intObj2.intCol = 1;
  264. IntObject *intObj3 = [[IntObject alloc] init];
  265. intObj3.intCol = 2;
  266. [array.intArray addObject:intObj1];
  267. [array.intArray addObject:intObj2];
  268. [array.intArray addObject:intObj3];
  269. XCTAssertEqualObjects(array.array[0], stringObj1, @"Objects should be equal");
  270. XCTAssertEqualObjects(array.array[1], stringObj2, @"Objects should be equal");
  271. XCTAssertEqualObjects(array.array[2], stringObj3, @"Objects should be equal");
  272. XCTAssertEqual(array.array.count, 3U, @"Should have 3 elements in string array");
  273. XCTAssertEqualObjects(array.intArray[0], intObj1, @"Objects should be equal");
  274. XCTAssertEqualObjects(array.intArray[1], intObj2, @"Objects should be equal");
  275. XCTAssertEqualObjects(array.intArray[2], intObj3, @"Objects should be equal");
  276. XCTAssertEqual(array.intArray.count, 3U, @"Should have 3 elements in int array");
  277. StringObject *stringObj4 = [[StringObject alloc] init];
  278. stringObj4.stringCol = @"d";
  279. [array.array replaceObjectAtIndex:0 withObject:stringObj4];
  280. XCTAssertTrue([[array.array objectAtIndex:0] isEqualToObject:stringObj4], @"Objects should be replaced");
  281. XCTAssertEqual(array.array.count, 3U, @"Should have 3 elements in int array");
  282. IntObject *intObj4 = [[IntObject alloc] init];
  283. intObj4.intCol = 3;
  284. [array.intArray replaceObjectAtIndex:1 withObject:intObj4];
  285. XCTAssertTrue([[array.intArray objectAtIndex:1] isEqualToObject:intObj4], @"Objects should be replaced");
  286. XCTAssertEqual(array.intArray.count, 3U, @"Should have 3 elements in int array");
  287. RLMAssertThrowsWithReasonMatching([array.array replaceObjectAtIndex:0 withObject:(id)intObj4],
  288. @"IntObject.*StringObject");
  289. RLMAssertThrowsWithReasonMatching([array.intArray replaceObjectAtIndex:1 withObject:(id)stringObj4],
  290. @"StringObject.*IntObject");
  291. }
  292. - (void)testDeleteObjectInUnmanagedArray {
  293. ArrayPropertyObject *array = [[ArrayPropertyObject alloc] init];
  294. array.name = @"name";
  295. StringObject *stringObj1 = [[StringObject alloc] init];
  296. stringObj1.stringCol = @"a";
  297. StringObject *stringObj2 = [[StringObject alloc] init];
  298. stringObj2.stringCol = @"b";
  299. StringObject *stringObj3 = [[StringObject alloc] init];
  300. stringObj3.stringCol = @"c";
  301. [array.array addObject:stringObj1];
  302. [array.array addObject:stringObj2];
  303. [array.array addObject:stringObj3];
  304. IntObject *intObj1 = [[IntObject alloc] init];
  305. intObj1.intCol = 0;
  306. IntObject *intObj2 = [[IntObject alloc] init];
  307. intObj2.intCol = 1;
  308. IntObject *intObj3 = [[IntObject alloc] init];
  309. intObj3.intCol = 2;
  310. [array.intArray addObject:intObj1];
  311. [array.intArray addObject:intObj2];
  312. [array.intArray addObject:intObj3];
  313. XCTAssertEqualObjects(array.array[0], stringObj1, @"Objects should be equal");
  314. XCTAssertEqualObjects(array.array[1], stringObj2, @"Objects should be equal");
  315. XCTAssertEqualObjects(array.array[2], stringObj3, @"Objects should be equal");
  316. XCTAssertEqual(array.array.count, 3U, @"Should have 3 elements in string array");
  317. XCTAssertEqualObjects(array.intArray[0], intObj1, @"Objects should be equal");
  318. XCTAssertEqualObjects(array.intArray[1], intObj2, @"Objects should be equal");
  319. XCTAssertEqualObjects(array.intArray[2], intObj3, @"Objects should be equal");
  320. XCTAssertEqual(array.intArray.count, 3U, @"Should have 3 elements in int array");
  321. [array.array removeLastObject];
  322. XCTAssertEqualObjects(array.array[0], stringObj1, @"Objects should be equal");
  323. XCTAssertEqualObjects(array.array[1], stringObj2, @"Objects should be equal");
  324. XCTAssertEqual(array.array.count, 2U, @"Should have 2 elements in string array");
  325. [array.array removeLastObject];
  326. XCTAssertEqualObjects(array.array[0], stringObj1, @"Objects should be equal");
  327. XCTAssertEqual(array.array.count, 1U, @"Should have 1 elements in string array");
  328. [array.array removeLastObject];
  329. XCTAssertEqual(array.array.count, 0U, @"Should have 0 elements in string array");
  330. [array.intArray removeAllObjects];
  331. XCTAssertEqual(array.intArray.count, 0U, @"Should have 0 elements in int array");
  332. }
  333. - (void)testExchangeObjectAtIndexWithObjectAtIndex {
  334. void (^test)(RLMArray *) = ^(RLMArray *array) {
  335. [array exchangeObjectAtIndex:0 withObjectAtIndex:1];
  336. XCTAssertEqual(2U, array.count);
  337. XCTAssertEqualObjects(@"b", [array[0] stringCol]);
  338. XCTAssertEqualObjects(@"a", [array[1] stringCol]);
  339. [array exchangeObjectAtIndex:1 withObjectAtIndex:1];
  340. XCTAssertEqual(2U, array.count);
  341. XCTAssertEqualObjects(@"b", [array[0] stringCol]);
  342. XCTAssertEqualObjects(@"a", [array[1] stringCol]);
  343. [array exchangeObjectAtIndex:1 withObjectAtIndex:0];
  344. XCTAssertEqual(2U, array.count);
  345. XCTAssertEqualObjects(@"a", [array[0] stringCol]);
  346. XCTAssertEqualObjects(@"b", [array[1] stringCol]);
  347. RLMAssertThrowsWithReasonMatching([array exchangeObjectAtIndex:1 withObjectAtIndex:20], @"less than 2");
  348. };
  349. ArrayPropertyObject *array = [[ArrayPropertyObject alloc] initWithValue:@[@"foo", @[@[@"a"], @[@"b"]], @[]]];
  350. test(array.array);
  351. RLMRealm *realm = [RLMRealm defaultRealm];
  352. [realm beginWriteTransaction];
  353. [realm addObject:array];
  354. test(array.array);
  355. [realm commitWriteTransaction];
  356. }
  357. - (void)testIndexOfObject
  358. {
  359. RLMRealm *realm = [RLMRealm defaultRealm];
  360. [realm beginWriteTransaction];
  361. EmployeeObject *po1 = [EmployeeObject createInRealm:realm withValue:@{@"name": @"Joe", @"age": @40, @"hired": @YES}];
  362. EmployeeObject *po2 = [EmployeeObject createInRealm:realm withValue:@{@"name": @"John", @"age": @30, @"hired": @NO}];
  363. EmployeeObject *po3 = [EmployeeObject createInRealm:realm withValue:@{@"name": @"Jill", @"age": @25, @"hired": @YES}];
  364. EmployeeObject *deleted = [EmployeeObject createInRealm:realm withValue:@{@"name": @"Jill", @"age": @25, @"hired": @YES}];
  365. EmployeeObject *indirectlyDeleted = [EmployeeObject allObjectsInRealm:realm].lastObject;
  366. [realm deleteObject:deleted];
  367. // create company
  368. CompanyObject *company = [[CompanyObject alloc] init];
  369. company.name = @"name";
  370. [company.employees addObjects:[EmployeeObject allObjects]];
  371. [company.employees removeObjectAtIndex:1];
  372. // test unmanaged
  373. XCTAssertEqual(0U, [company.employees indexOfObject:po1]);
  374. XCTAssertEqual(1U, [company.employees indexOfObject:po3]);
  375. XCTAssertEqual((NSUInteger)NSNotFound, [company.employees indexOfObject:po2]);
  376. // add to realm
  377. [realm addObject:company];
  378. [realm commitWriteTransaction];
  379. // test LinkView RLMArray
  380. XCTAssertEqual(0U, [company.employees indexOfObject:po1]);
  381. XCTAssertEqual(1U, [company.employees indexOfObject:po3]);
  382. XCTAssertEqual((NSUInteger)NSNotFound, [company.employees indexOfObject:po2]);
  383. // non realm employee
  384. EmployeeObject *notInRealm = [[EmployeeObject alloc] initWithValue:@[@"NoName", @1, @NO]];
  385. XCTAssertEqual((NSUInteger)NSNotFound, [company.employees indexOfObject:notInRealm]);
  386. // invalid object
  387. XCTAssertThrows([company.employees indexOfObject:(EmployeeObject *)company]);
  388. RLMAssertThrowsWithReasonMatching([company.employees indexOfObject:deleted], @"invalidated");
  389. RLMAssertThrowsWithReasonMatching([company.employees indexOfObject:indirectlyDeleted], @"invalidated");
  390. RLMResults *employees = [company.employees objectsWhere:@"age = %@", @40];
  391. XCTAssertEqual(0U, [employees indexOfObject:po1]);
  392. XCTAssertEqual((NSUInteger)NSNotFound, [employees indexOfObject:po3]);
  393. }
  394. - (void)testIndexOfObjectWhere
  395. {
  396. RLMRealm *realm = [RLMRealm defaultRealm];
  397. [realm beginWriteTransaction];
  398. EmployeeObject *po1 = [EmployeeObject createInRealm:realm withValue:@{@"name": @"Joe", @"age": @40, @"hired": @YES}];
  399. [EmployeeObject createInRealm:realm withValue:@{@"name": @"John", @"age": @30, @"hired": @NO}];
  400. EmployeeObject *po3 = [EmployeeObject createInRealm:realm withValue:@{@"name": @"Jill", @"age": @25, @"hired": @YES}];
  401. EmployeeObject *po4 = [EmployeeObject createInRealm:realm withValue:@{@"name": @"Bill", @"age": @55, @"hired": @YES}];
  402. // create company
  403. CompanyObject *company = [[CompanyObject alloc] init];
  404. company.name = @"name";
  405. [company.employees addObjects:@[po3, po1, po4]];
  406. // test unmanaged
  407. XCTAssertEqual(0U, [company.employees indexOfObjectWhere:@"name = 'Jill'"]);
  408. XCTAssertEqual(1U, [company.employees indexOfObjectWhere:@"name = 'Joe'"]);
  409. XCTAssertEqual((NSUInteger)NSNotFound, [company.employees indexOfObjectWhere:@"name = 'John'"]);
  410. // add to realm
  411. [realm addObject:company];
  412. [realm commitWriteTransaction];
  413. // test LinkView RLMArray
  414. XCTAssertEqual(0U, [company.employees indexOfObjectWhere:@"name = 'Jill'"]);
  415. XCTAssertEqual(1U, [company.employees indexOfObjectWhere:@"name = 'Joe'"]);
  416. XCTAssertEqual((NSUInteger)NSNotFound, [company.employees indexOfObjectWhere:@"name = 'John'"]);
  417. RLMResults *results = [company.employees objectsWhere:@"age > 30"];
  418. XCTAssertEqual(0U, [results indexOfObjectWhere:@"name = 'Joe'"]);
  419. XCTAssertEqual(1U, [results indexOfObjectWhere:@"name = 'Bill'"]);
  420. XCTAssertEqual((NSUInteger)NSNotFound, [results indexOfObjectWhere:@"name = 'John'"]);
  421. XCTAssertEqual((NSUInteger)NSNotFound, [results indexOfObjectWhere:@"name = 'Jill'"]);
  422. }
  423. - (void)testFastEnumeration
  424. {
  425. RLMRealm *realm = self.realmWithTestPath;
  426. [realm beginWriteTransaction];
  427. CompanyObject *company = [[CompanyObject alloc] init];
  428. company.name = @"name";
  429. [realm addObject:company];
  430. [realm commitWriteTransaction];
  431. // enumerate empty array
  432. for (__unused id obj in company.employees) {
  433. XCTFail(@"Should be empty");
  434. }
  435. [realm beginWriteTransaction];
  436. for (int i = 0; i < 30; ++i) {
  437. EmployeeObject *eo = [EmployeeObject createInRealm:realm withValue:@{@"name": @"Joe", @"age": @40, @"hired": @YES}];
  438. [company.employees addObject:eo];
  439. }
  440. [realm commitWriteTransaction];
  441. XCTAssertEqual(company.employees.count, 30U);
  442. __weak id objects[30];
  443. NSInteger count = 0;
  444. for (EmployeeObject *e in company.employees) {
  445. XCTAssertNotNil(e, @"Object is not nil and accessible");
  446. if (count > 16) {
  447. // 16 is the size of blocks fast enumeration happens to ask for at
  448. // the moment, but of course that's just an implementation detail
  449. // that may change
  450. XCTAssertNil(objects[count - 16]);
  451. }
  452. objects[count++] = e;
  453. }
  454. XCTAssertEqual(count, 30, @"should have enumerated 30 objects");
  455. for (int i = 0; i < count; i++) {
  456. XCTAssertNil(objects[i], @"Object should have been released");
  457. }
  458. @autoreleasepool {
  459. for (EmployeeObject *e in company.employees) {
  460. objects[0] = e;
  461. break;
  462. }
  463. }
  464. XCTAssertNil(objects[0], @"Object should have been released");
  465. }
  466. - (void)testModifyDuringEnumeration {
  467. RLMRealm *realm = self.realmWithTestPath;
  468. [realm beginWriteTransaction];
  469. CompanyObject *company = [[CompanyObject alloc] init];
  470. company.name = @"name";
  471. [realm addObject:company];
  472. const size_t totalCount = 40;
  473. for (size_t i = 0; i < totalCount; ++i) {
  474. [company.employees addObject:[EmployeeObject createInRealm:realm withValue:@[@"name", @(i), @NO]]];
  475. }
  476. size_t count = 0;
  477. for (EmployeeObject *eo in company.employees) {
  478. ++count;
  479. [company.employees addObject:eo];
  480. }
  481. XCTAssertEqual(totalCount, count);
  482. XCTAssertEqual(totalCount * 2, company.employees.count);
  483. [realm cancelWriteTransaction];
  484. // Unmanaged array
  485. company = [[CompanyObject alloc] init];
  486. for (size_t i = 0; i < totalCount; ++i) {
  487. [company.employees addObject:[[EmployeeObject alloc] initWithValue:@[@"name", @(i), @NO]]];
  488. }
  489. count = 0;
  490. for (EmployeeObject *eo in company.employees) {
  491. ++count;
  492. [company.employees addObject:eo];
  493. }
  494. XCTAssertEqual(totalCount, count);
  495. XCTAssertEqual(totalCount * 2, company.employees.count);
  496. }
  497. - (void)testDeleteDuringEnumeration {
  498. RLMRealm *realm = self.realmWithTestPath;
  499. [realm beginWriteTransaction];
  500. CompanyObject *company = [[CompanyObject alloc] init];
  501. company.name = @"name";
  502. [realm addObject:company];
  503. const size_t totalCount = 40;
  504. for (size_t i = 0; i < totalCount; ++i) {
  505. [company.employees addObject:[EmployeeObject createInRealm:realm withValue:@[@"name", @(i), @NO]]];
  506. }
  507. [realm commitWriteTransaction];
  508. [realm beginWriteTransaction];
  509. for (__unused EmployeeObject *eo in company.employees) {
  510. [realm deleteObjects:company.employees];
  511. }
  512. [realm commitWriteTransaction];
  513. }
  514. - (void)testValueForKey {
  515. RLMRealm *realm = self.realmWithTestPath;
  516. [realm beginWriteTransaction];
  517. CompanyObject *company = [[CompanyObject alloc] init];
  518. company.name = @"name";
  519. XCTAssertEqualObjects([company.employees valueForKey:@"name"], @[]);
  520. [realm addObject:company];
  521. [realm commitWriteTransaction];
  522. XCTAssertEqualObjects([company.employees valueForKey:@"age"], @[]);
  523. // managed
  524. NSMutableArray *ages = [NSMutableArray array];
  525. [realm beginWriteTransaction];
  526. for (int i = 0; i < 30; ++i) {
  527. [ages addObject:@(i)];
  528. EmployeeObject *eo = [EmployeeObject createInRealm:realm withValue:@{@"name": @"Joe", @"age": @(i), @"hired": @YES}];
  529. [company.employees addObject:eo];
  530. }
  531. [realm commitWriteTransaction];
  532. RLM_GENERIC_ARRAY(EmployeeObject) *employeeObjects = [company valueForKey:@"employees"];
  533. NSMutableArray *kvcAgeProperties = [NSMutableArray array];
  534. for (EmployeeObject *employee in employeeObjects) {
  535. [kvcAgeProperties addObject:@(employee.age)];
  536. }
  537. XCTAssertEqualObjects(kvcAgeProperties, ages);
  538. XCTAssertEqualObjects([company.employees valueForKey:@"age"], ages);
  539. XCTAssertTrue([[[company.employees valueForKey:@"self"] firstObject] isEqualToObject:company.employees.firstObject]);
  540. XCTAssertTrue([[[company.employees valueForKey:@"self"] lastObject] isEqualToObject:company.employees.lastObject]);
  541. XCTAssertEqual([[company.employees valueForKeyPath:@"@count"] integerValue], 30);
  542. XCTAssertEqual([[company.employees valueForKeyPath:@"@min.age"] integerValue], 0);
  543. XCTAssertEqual([[company.employees valueForKeyPath:@"@max.age"] integerValue], 29);
  544. XCTAssertEqualWithAccuracy([[company.employees valueForKeyPath:@"@avg.age"] doubleValue], 14.5, 0.1f);
  545. XCTAssertEqualObjects([company.employees valueForKeyPath:@"@unionOfObjects.age"],
  546. (@[@0, @1, @2, @3, @4, @5, @6, @7, @8, @9, @10, @11, @12, @13, @14, @15, @16, @17, @18, @19, @20, @21, @22, @23, @24, @25, @26, @27, @28, @29]));
  547. XCTAssertEqualObjects([company.employees valueForKeyPath:@"@distinctUnionOfObjects.name"], (@[@"Joe"]));
  548. RLMAssertThrowsWithReasonMatching([company.employees valueForKeyPath:@"@sum.dogs.@sum.age"], @"Nested key paths.*not supported");
  549. // unmanaged object
  550. company = [[CompanyObject alloc] init];
  551. ages = [NSMutableArray array];
  552. for (int i = 0; i < 30; ++i) {
  553. [ages addObject:@(i)];
  554. EmployeeObject *eo = [[EmployeeObject alloc] initWithValue:@{@"name": @"Joe", @"age": @(i), @"hired": @YES}];
  555. [company.employees addObject:eo];
  556. }
  557. XCTAssertEqualObjects([company.employees valueForKey:@"age"], ages);
  558. XCTAssertTrue([[[company.employees valueForKey:@"self"] firstObject] isEqualToObject:company.employees.firstObject]);
  559. XCTAssertTrue([[[company.employees valueForKey:@"self"] lastObject] isEqualToObject:company.employees.lastObject]);
  560. XCTAssertEqual([[company.employees valueForKeyPath:@"@count"] integerValue], 30);
  561. XCTAssertEqual([[company.employees valueForKeyPath:@"@min.age"] integerValue], 0);
  562. XCTAssertEqual([[company.employees valueForKeyPath:@"@max.age"] integerValue], 29);
  563. XCTAssertEqualWithAccuracy([[company.employees valueForKeyPath:@"@avg.age"] doubleValue], 14.5, 0.1f);
  564. XCTAssertEqualObjects([company.employees valueForKeyPath:@"@unionOfObjects.age"],
  565. (@[@0, @1, @2, @3, @4, @5, @6, @7, @8, @9, @10, @11, @12, @13, @14, @15, @16, @17, @18, @19, @20, @21, @22, @23, @24, @25, @26, @27, @28, @29]));
  566. XCTAssertEqualObjects([company.employees valueForKeyPath:@"@distinctUnionOfObjects.name"], (@[@"Joe"]));
  567. RLMAssertThrowsWithReasonMatching([company.employees valueForKeyPath:@"@sum.dogs.@sum.age"], @"Nested key paths.*not supported");
  568. }
  569. - (void)testSetValueForKey {
  570. RLMRealm *realm = self.realmWithTestPath;
  571. [realm beginWriteTransaction];
  572. CompanyObject *company = [[CompanyObject alloc] init];
  573. company.name = @"name";
  574. [company.employees setValue:@"name" forKey:@"name"];
  575. XCTAssertEqualObjects([company.employees valueForKey:@"name"], @[]);
  576. [realm addObject:company];
  577. [realm commitWriteTransaction];
  578. XCTAssertThrows([company.employees setValue:@10 forKey:@"age"]);
  579. XCTAssertEqualObjects([company.employees valueForKey:@"age"], @[]);
  580. // managed
  581. NSMutableArray *ages = [NSMutableArray array];
  582. [realm beginWriteTransaction];
  583. for (int i = 0; i < 30; ++i) {
  584. [ages addObject:@(20)];
  585. EmployeeObject *eo = [EmployeeObject createInRealm:realm withValue:@{@"name": @"Joe", @"age": @(i), @"hired": @YES}];
  586. [company.employees addObject:eo];
  587. }
  588. [company.employees setValue:@20 forKey:@"age"];
  589. [realm commitWriteTransaction];
  590. XCTAssertEqualObjects([company.employees valueForKey:@"age"], ages);
  591. // unmanaged object
  592. company = [[CompanyObject alloc] init];
  593. ages = [NSMutableArray array];
  594. for (int i = 0; i < 30; ++i) {
  595. [ages addObject:@(20)];
  596. EmployeeObject *eo = [[EmployeeObject alloc] initWithValue:@{@"name": @"Joe", @"age": @(i), @"hired": @YES}];
  597. [company.employees addObject:eo];
  598. }
  599. [company.employees setValue:@20 forKey:@"age"];
  600. XCTAssertEqualObjects([company.employees valueForKey:@"age"], ages);
  601. }
  602. - (void)testObjectAggregate {
  603. RLMRealm *realm = [RLMRealm defaultRealm];
  604. AggregateArrayObject *obj = [AggregateArrayObject new];
  605. XCTAssertEqual(0, [obj.array sumOfProperty:@"intCol"].intValue);
  606. XCTAssertNil([obj.array averageOfProperty:@"intCol"]);
  607. XCTAssertNil([obj.array minOfProperty:@"intCol"]);
  608. XCTAssertNil([obj.array maxOfProperty:@"intCol"]);
  609. NSDate *dateMinInput = [NSDate date];
  610. NSDate *dateMaxInput = [dateMinInput dateByAddingTimeInterval:1000];
  611. [realm transactionWithBlock:^{
  612. [AggregateObject createInRealm:realm withValue:@[@0, @1.2f, @0.0, @YES, dateMinInput]];
  613. [AggregateObject createInRealm:realm withValue:@[@1, @0.0f, @2.5, @NO, dateMaxInput]];
  614. [AggregateObject createInRealm:realm withValue:@[@0, @1.2f, @0.0, @YES, dateMinInput]];
  615. [AggregateObject createInRealm:realm withValue:@[@1, @0.0f, @2.5, @NO, dateMaxInput]];
  616. [AggregateObject createInRealm:realm withValue:@[@0, @1.2f, @0.0, @YES, dateMinInput]];
  617. [AggregateObject createInRealm:realm withValue:@[@1, @0.0f, @2.5, @NO, dateMaxInput]];
  618. [AggregateObject createInRealm:realm withValue:@[@0, @1.2f, @0.0, @YES, dateMinInput]];
  619. [AggregateObject createInRealm:realm withValue:@[@1, @0.0f, @2.5, @NO, dateMaxInput]];
  620. [AggregateObject createInRealm:realm withValue:@[@0, @1.2f, @0.0, @YES, dateMinInput]];
  621. [AggregateObject createInRealm:realm withValue:@[@0, @1.2f, @0.0, @YES, dateMinInput]];
  622. [obj.array addObjects:[AggregateObject allObjectsInRealm:realm]];
  623. }];
  624. void (^test)(void) = ^{
  625. RLMArray *array = obj.array;
  626. // SUM
  627. XCTAssertEqual([array sumOfProperty:@"intCol"].integerValue, 4);
  628. XCTAssertEqualWithAccuracy([array sumOfProperty:@"floatCol"].floatValue, 7.2f, 0.1f);
  629. XCTAssertEqualWithAccuracy([array sumOfProperty:@"doubleCol"].doubleValue, 10.0, 0.1f);
  630. RLMAssertThrowsWithReasonMatching([array sumOfProperty:@"foo"], @"foo.*AggregateObject");
  631. RLMAssertThrowsWithReasonMatching([array sumOfProperty:@"boolCol"], @"sum.*bool");
  632. RLMAssertThrowsWithReasonMatching([array sumOfProperty:@"dateCol"], @"sum.*date");
  633. // Average
  634. XCTAssertEqualWithAccuracy([array averageOfProperty:@"intCol"].doubleValue, 0.4, 0.1f);
  635. XCTAssertEqualWithAccuracy([array averageOfProperty:@"floatCol"].doubleValue, 0.72, 0.1f);
  636. XCTAssertEqualWithAccuracy([array averageOfProperty:@"doubleCol"].doubleValue, 1.0, 0.1f);
  637. RLMAssertThrowsWithReasonMatching([array averageOfProperty:@"foo"], @"foo.*AggregateObject");
  638. RLMAssertThrowsWithReasonMatching([array averageOfProperty:@"boolCol"], @"average.*bool");
  639. RLMAssertThrowsWithReasonMatching([array averageOfProperty:@"dateCol"], @"average.*date");
  640. // MIN
  641. XCTAssertEqual(0, [[array minOfProperty:@"intCol"] intValue]);
  642. XCTAssertEqual(0.0f, [[array minOfProperty:@"floatCol"] floatValue]);
  643. XCTAssertEqual(0.0, [[array minOfProperty:@"doubleCol"] doubleValue]);
  644. XCTAssertEqualObjects(dateMinInput, [array minOfProperty:@"dateCol"]);
  645. RLMAssertThrowsWithReasonMatching([array minOfProperty:@"foo"], @"foo.*AggregateObject");
  646. RLMAssertThrowsWithReasonMatching([array minOfProperty:@"boolCol"], @"min.*bool");
  647. // MAX
  648. XCTAssertEqual(1, [[array maxOfProperty:@"intCol"] intValue]);
  649. XCTAssertEqual(1.2f, [[array maxOfProperty:@"floatCol"] floatValue]);
  650. XCTAssertEqual(2.5, [[array maxOfProperty:@"doubleCol"] doubleValue]);
  651. XCTAssertEqualObjects(dateMaxInput, [array maxOfProperty:@"dateCol"]);
  652. RLMAssertThrowsWithReasonMatching([array maxOfProperty:@"foo"], @"foo.*AggregateObject");
  653. RLMAssertThrowsWithReasonMatching([array maxOfProperty:@"boolCol"], @"max.*bool");
  654. };
  655. test();
  656. [realm transactionWithBlock:^{ [realm addObject:obj]; }];
  657. test();
  658. }
  659. - (void)testRenamedPropertyAggregate {
  660. RLMRealm *realm = [RLMRealm defaultRealm];
  661. LinkToRenamedProperties1 *obj = [LinkToRenamedProperties1 new];
  662. XCTAssertEqual(0, [obj.array sumOfProperty:@"propA"].intValue);
  663. XCTAssertNil([obj.array averageOfProperty:@"propA"]);
  664. XCTAssertNil([obj.array minOfProperty:@"propA"]);
  665. XCTAssertNil([obj.array maxOfProperty:@"propA"]);
  666. XCTAssertThrows([obj.array sumOfProperty:@"prop 1"]);
  667. [realm transactionWithBlock:^{
  668. [RenamedProperties1 createInRealm:realm withValue:@[@1, @""]];
  669. [RenamedProperties1 createInRealm:realm withValue:@[@2, @""]];
  670. [RenamedProperties1 createInRealm:realm withValue:@[@3, @""]];
  671. [obj.array addObjects:[RenamedProperties1 allObjectsInRealm:realm]];
  672. }];
  673. XCTAssertEqual(6, [obj.array sumOfProperty:@"propA"].intValue);
  674. XCTAssertEqual(2.0, [obj.array averageOfProperty:@"propA"].doubleValue);
  675. XCTAssertEqual(1, [[obj.array minOfProperty:@"propA"] intValue]);
  676. XCTAssertEqual(3, [[obj.array maxOfProperty:@"propA"] intValue]);
  677. [realm transactionWithBlock:^{ [realm addObject:obj]; }];
  678. XCTAssertEqual(6, [obj.array sumOfProperty:@"propA"].intValue);
  679. XCTAssertEqual(2.0, [obj.array averageOfProperty:@"propA"].doubleValue);
  680. XCTAssertEqual(1, [[obj.array minOfProperty:@"propA"] intValue]);
  681. XCTAssertEqual(3, [[obj.array maxOfProperty:@"propA"] intValue]);
  682. }
  683. - (void)testValueForCollectionOperationKeyPath
  684. {
  685. RLMRealm *realm = [RLMRealm defaultRealm];
  686. [realm beginWriteTransaction];
  687. EmployeeObject *e1 = [PrimaryEmployeeObject createInRealm:realm withValue:@{@"name": @"A", @"age": @20, @"hired": @YES}];
  688. EmployeeObject *e2 = [PrimaryEmployeeObject createInRealm:realm withValue:@{@"name": @"B", @"age": @30, @"hired": @NO}];
  689. EmployeeObject *e3 = [PrimaryEmployeeObject createInRealm:realm withValue:@{@"name": @"C", @"age": @40, @"hired": @YES}];
  690. EmployeeObject *e4 = [PrimaryEmployeeObject createInRealm:realm withValue:@{@"name": @"D", @"age": @50, @"hired": @YES}];
  691. PrimaryCompanyObject *c1 = [PrimaryCompanyObject createInRealm:realm withValue:@{@"name": @"ABC AG", @"employees": @[e1, e2, e3, e2]}];
  692. PrimaryCompanyObject *c2 = [PrimaryCompanyObject createInRealm:realm withValue:@{@"name": @"ABC AG 2", @"employees": @[e1, e4]}];
  693. ArrayOfPrimaryCompanies *companies = [ArrayOfPrimaryCompanies createInRealm:realm withValue:@[@[c1, c2]]];
  694. [realm commitWriteTransaction];
  695. // count operator
  696. XCTAssertEqual([[c1.employees valueForKeyPath:@"@count"] integerValue], 4);
  697. // numeric operators
  698. XCTAssertEqual([[c1.employees valueForKeyPath:@"@min.age"] intValue], 20);
  699. XCTAssertEqual([[c1.employees valueForKeyPath:@"@max.age"] intValue], 40);
  700. XCTAssertEqual([[c1.employees valueForKeyPath:@"@sum.age"] integerValue], 120);
  701. XCTAssertEqualWithAccuracy([[c1.employees valueForKeyPath:@"@avg.age"] doubleValue], 30, 0.1f);
  702. // collection
  703. XCTAssertEqualObjects([c1.employees valueForKeyPath:@"@unionOfObjects.name"],
  704. (@[@"A", @"B", @"C", @"B"]));
  705. XCTAssertEqualObjects([[c1.employees valueForKeyPath:@"@distinctUnionOfObjects.name"] sortedArrayUsingSelector:@selector(compare:)],
  706. (@[@"A", @"B", @"C"]));
  707. XCTAssertEqualObjects([companies.companies valueForKeyPath:@"@unionOfArrays.employees"],
  708. (@[e1, e2, e3, e2, e1, e4]));
  709. NSComparator cmp = ^NSComparisonResult(id obj1, id obj2) { return [[obj1 name] compare:[obj2 name]]; };
  710. XCTAssertEqualObjects([[companies.companies valueForKeyPath:@"@distinctUnionOfArrays.employees"] sortedArrayUsingComparator:cmp],
  711. (@[e1, e2, e3, e4]));
  712. // invalid key paths
  713. RLMAssertThrowsWithReasonMatching([c1.employees valueForKeyPath:@"@invalid.name"],
  714. @"Unsupported KVC collection operator found in key path '@invalid.name'");
  715. RLMAssertThrowsWithReasonMatching([c1.employees valueForKeyPath:@"@sum"],
  716. @"Missing key path for KVC collection operator sum in key path '@sum'");
  717. RLMAssertThrowsWithReasonMatching([c1.employees valueForKeyPath:@"@sum."],
  718. @"Missing key path for KVC collection operator sum in key path '@sum.'");
  719. RLMAssertThrowsWithReasonMatching([c1.employees valueForKeyPath:@"@sum.employees.@sum.age"],
  720. @"Nested key paths.*not supported");
  721. }
  722. - (void)testCrossThreadAccess
  723. {
  724. CompanyObject *company = [[CompanyObject alloc] init];
  725. company.name = @"name";
  726. EmployeeObject *eo = [[EmployeeObject alloc] init];
  727. eo.name = @"Joe";
  728. eo.age = 40;
  729. eo.hired = YES;
  730. [company.employees addObject:eo];
  731. RLMArray *employees = company.employees;
  732. // Unmanaged object can be accessed from other threads
  733. [self dispatchAsyncAndWait:^{
  734. XCTAssertNoThrow(company.employees);
  735. XCTAssertNoThrow([employees lastObject]);
  736. }];
  737. [RLMRealm.defaultRealm beginWriteTransaction];
  738. [RLMRealm.defaultRealm addObject:company];
  739. [RLMRealm.defaultRealm commitWriteTransaction];
  740. employees = company.employees;
  741. XCTAssertNoThrow(company.employees);
  742. XCTAssertNoThrow([employees lastObject]);
  743. [self dispatchAsyncAndWait:^{
  744. XCTAssertThrows(company.employees);
  745. XCTAssertThrows([employees lastObject]);
  746. }];
  747. }
  748. - (void)testSortByNoColumns {
  749. RLMRealm *realm = [RLMRealm defaultRealm];
  750. [realm beginWriteTransaction];
  751. DogObject *a2 = [DogObject createInDefaultRealmWithValue:@[@"a", @2]];
  752. DogObject *b1 = [DogObject createInDefaultRealmWithValue:@[@"b", @1]];
  753. DogObject *a1 = [DogObject createInDefaultRealmWithValue:@[@"a", @1]];
  754. DogObject *b2 = [DogObject createInDefaultRealmWithValue:@[@"b", @2]];
  755. RLMArray<DogObject *> *array = [DogArrayObject createInDefaultRealmWithValue:@[@[a2, b1, a1, b2]]].dogs;
  756. [realm commitWriteTransaction];
  757. RLMResults *notActuallySorted = [array sortedResultsUsingDescriptors:@[]];
  758. XCTAssertTrue([array[0] isEqualToObject:notActuallySorted[0]]);
  759. XCTAssertTrue([array[1] isEqualToObject:notActuallySorted[1]]);
  760. XCTAssertTrue([array[2] isEqualToObject:notActuallySorted[2]]);
  761. XCTAssertTrue([array[3] isEqualToObject:notActuallySorted[3]]);
  762. }
  763. - (void)testSortByMultipleColumns {
  764. RLMRealm *realm = [RLMRealm defaultRealm];
  765. [realm beginWriteTransaction];
  766. DogObject *a1 = [DogObject createInDefaultRealmWithValue:@[@"a", @1]];
  767. DogObject *a2 = [DogObject createInDefaultRealmWithValue:@[@"a", @2]];
  768. DogObject *b1 = [DogObject createInDefaultRealmWithValue:@[@"b", @1]];
  769. DogObject *b2 = [DogObject createInDefaultRealmWithValue:@[@"b", @2]];
  770. DogArrayObject *array = [DogArrayObject createInDefaultRealmWithValue:@[@[a1, a2, b1, b2]]];
  771. [realm commitWriteTransaction];
  772. bool (^checkOrder)(NSArray *, NSArray *, NSArray *) = ^bool(NSArray *properties, NSArray *ascending, NSArray *dogs) {
  773. NSArray *sort = @[[RLMSortDescriptor sortDescriptorWithKeyPath:properties[0] ascending:[ascending[0] boolValue]],
  774. [RLMSortDescriptor sortDescriptorWithKeyPath:properties[1] ascending:[ascending[1] boolValue]]];
  775. RLMResults *actual = [array.dogs sortedResultsUsingDescriptors:sort];
  776. return [actual[0] isEqualToObject:dogs[0]]
  777. && [actual[1] isEqualToObject:dogs[1]]
  778. && [actual[2] isEqualToObject:dogs[2]]
  779. && [actual[3] isEqualToObject:dogs[3]];
  780. };
  781. // Check each valid sort
  782. XCTAssertTrue(checkOrder(@[@"dogName", @"age"], @[@YES, @YES], @[a1, a2, b1, b2]));
  783. XCTAssertTrue(checkOrder(@[@"dogName", @"age"], @[@YES, @NO], @[a2, a1, b2, b1]));
  784. XCTAssertTrue(checkOrder(@[@"dogName", @"age"], @[@NO, @YES], @[b1, b2, a1, a2]));
  785. XCTAssertTrue(checkOrder(@[@"dogName", @"age"], @[@NO, @NO], @[b2, b1, a2, a1]));
  786. XCTAssertTrue(checkOrder(@[@"age", @"dogName"], @[@YES, @YES], @[a1, b1, a2, b2]));
  787. XCTAssertTrue(checkOrder(@[@"age", @"dogName"], @[@YES, @NO], @[b1, a1, b2, a2]));
  788. XCTAssertTrue(checkOrder(@[@"age", @"dogName"], @[@NO, @YES], @[a2, b2, a1, b1]));
  789. XCTAssertTrue(checkOrder(@[@"age", @"dogName"], @[@NO, @NO], @[b2, a2, b1, a1]));
  790. }
  791. - (void)testSortByRenamedColumns {
  792. RLMRealm *realm = [RLMRealm defaultRealm];
  793. [realm beginWriteTransaction];
  794. id value = @{@"array": @[@[@1, @"c"], @[@2, @"b"], @[@3, @"a"]]};
  795. LinkToRenamedProperties1 *obj = [LinkToRenamedProperties1 createInRealm:realm withValue:value];
  796. // FIXME: sorting has to use the column names because the parsing is done by
  797. // the object store. This is not ideal.
  798. XCTAssertEqualObjects([[obj.array sortedResultsUsingKeyPath:@"prop 1" ascending:YES] valueForKeyPath:@"propA"],
  799. (@[@1, @2, @3]));
  800. XCTAssertEqualObjects([[obj.array sortedResultsUsingKeyPath:@"prop 2" ascending:YES] valueForKeyPath:@"propA"],
  801. (@[@3, @2, @1]));
  802. LinkToRenamedProperties2 *obj2 = [LinkToRenamedProperties2 allObjectsInRealm:realm].firstObject;
  803. XCTAssertEqualObjects([[obj2.array sortedResultsUsingKeyPath:@"prop 1" ascending:YES] valueForKeyPath:@"propC"],
  804. (@[@1, @2, @3]));
  805. XCTAssertEqualObjects([[obj2.array sortedResultsUsingKeyPath:@"prop 2" ascending:YES] valueForKeyPath:@"propC"],
  806. (@[@3, @2, @1]));
  807. [realm cancelWriteTransaction];
  808. }
  809. - (void)testDeleteLinksAndObjectsInArray
  810. {
  811. RLMRealm *realm = [RLMRealm defaultRealm];
  812. [realm beginWriteTransaction];
  813. EmployeeObject *po1 = [EmployeeObject createInRealm:realm withValue:@[@"Joe", @40, @YES]];
  814. EmployeeObject *po2 = [EmployeeObject createInRealm:realm withValue:@[@"John", @30, @NO]];
  815. EmployeeObject *po3 = [EmployeeObject createInRealm:realm withValue:@[@"Jill", @25, @YES]];
  816. CompanyObject *company = [[CompanyObject alloc] init];
  817. company.name = @"name";
  818. [company.employees addObjects:[EmployeeObject allObjects]];
  819. [realm addObject:company];
  820. [realm commitWriteTransaction];
  821. RLMArray *peopleInCompany = company.employees;
  822. // Delete link to employee
  823. XCTAssertThrowsSpecificNamed([peopleInCompany removeObjectAtIndex:1], NSException, @"RLMException", @"Not allowed in read transaction");
  824. XCTAssertEqual(peopleInCompany.count, 3U, @"No links should have been deleted");
  825. [realm beginWriteTransaction];
  826. XCTAssertThrowsSpecificNamed([peopleInCompany removeObjectAtIndex:3], NSException, @"RLMException", @"Out of bounds");
  827. XCTAssertNoThrow([peopleInCompany removeObjectAtIndex:1], @"Should delete link to employee");
  828. [realm commitWriteTransaction];
  829. XCTAssertEqual(peopleInCompany.count, 2U, @"link deleted when accessing via links");
  830. EmployeeObject *test = peopleInCompany[0];
  831. XCTAssertEqual(test.age, po1.age, @"Should be equal");
  832. XCTAssertEqualObjects(test.name, po1.name, @"Should be equal");
  833. XCTAssertEqual(test.hired, po1.hired, @"Should be equal");
  834. XCTAssertTrue([test isEqualToObject:po1], @"Should be equal");
  835. test = peopleInCompany[1];
  836. XCTAssertEqual(test.age, po3.age, @"Should be equal");
  837. XCTAssertEqualObjects(test.name, po3.name, @"Should be equal");
  838. XCTAssertEqual(test.hired, po3.hired, @"Should be equal");
  839. XCTAssertTrue([test isEqualToObject:po3], @"Should be equal");
  840. XCTAssertThrowsSpecificNamed([peopleInCompany removeLastObject], NSException, @"RLMException", @"Not allowed in read transaction");
  841. XCTAssertThrowsSpecificNamed([peopleInCompany removeAllObjects], NSException, @"RLMException", @"Not allowed in read transaction");
  842. XCTAssertThrowsSpecificNamed([peopleInCompany replaceObjectAtIndex:0 withObject:po2], NSException, @"RLMException", @"Not allowed in read transaction");
  843. XCTAssertThrowsSpecificNamed([peopleInCompany insertObject:po2 atIndex:0], NSException, @"RLMException", @"Not allowed in read transaction");
  844. [realm beginWriteTransaction];
  845. XCTAssertNoThrow([peopleInCompany removeLastObject], @"Should delete last link");
  846. XCTAssertEqual(peopleInCompany.count, 1U, @"1 remaining link");
  847. [peopleInCompany replaceObjectAtIndex:0 withObject:po2];
  848. XCTAssertEqual(peopleInCompany.count, 1U, @"1 link replaced");
  849. [peopleInCompany insertObject:po1 atIndex:0];
  850. XCTAssertEqual(peopleInCompany.count, 2U, @"2 links");
  851. XCTAssertNoThrow([peopleInCompany removeAllObjects], @"Should delete all links");
  852. XCTAssertEqual(peopleInCompany.count, 0U, @"0 remaining links");
  853. [realm commitWriteTransaction];
  854. RLMResults *allPeople = [EmployeeObject allObjects];
  855. XCTAssertEqual(allPeople.count, 3U, @"Only links should have been deleted, not the employees");
  856. }
  857. - (void)testArrayDescription {
  858. RLMRealm *realm = [RLMRealm defaultRealm];
  859. [realm beginWriteTransaction];
  860. RLMArray<EmployeeObject *> *employees = [CompanyObject createInDefaultRealmWithValue:@[@"company"]].employees;
  861. RLMArray<NSNumber *> *ints = [AllPrimitiveArrays createInDefaultRealmWithValue:@[]].intObj;
  862. for (NSInteger i = 0; i < 1012; ++i) {
  863. EmployeeObject *person = [[EmployeeObject alloc] init];
  864. person.name = @"Mary";
  865. person.age = 24;
  866. person.hired = YES;
  867. [employees addObject:person];
  868. [ints addObject:@(i + 100)];
  869. }
  870. [realm commitWriteTransaction];
  871. RLMAssertMatches(employees.description,
  872. @"(?s)RLMArray\\<EmployeeObject\\> \\<0x[a-z0-9]+\\> \\(\n"
  873. @"\t\\[0\\] EmployeeObject \\{\n"
  874. @"\t\tname = Mary;\n"
  875. @"\t\tage = 24;\n"
  876. @"\t\thired = 1;\n"
  877. @"\t\\},\n"
  878. @".*\n"
  879. @"\t... 912 objects skipped.\n"
  880. @"\\)");
  881. RLMAssertMatches(ints.description,
  882. @"(?s)RLMArray\\<int\\> \\<0x[a-z0-9]+\\> \\(\n"
  883. @"\t\\[0\\] 100,\n"
  884. @"\t\\[1\\] 101,\n"
  885. @"\t\\[2\\] 102,\n"
  886. @".*\n"
  887. @"\t... 912 objects skipped.\n"
  888. @"\\)");
  889. }
  890. - (void)testUnmanagedAssignment {
  891. IntObject *io1 = [[IntObject alloc] init];
  892. IntObject *io2 = [[IntObject alloc] init];
  893. IntObject *io3 = [[IntObject alloc] init];
  894. ArrayPropertyObject *array1 = [[ArrayPropertyObject alloc] init];
  895. ArrayPropertyObject *array2 = [[ArrayPropertyObject alloc] init];
  896. // Assigning NSArray shallow copies
  897. array1.intArray = (id)@[io1, io2];
  898. XCTAssertEqualObjects([array1.intArray valueForKey:@"self"], (@[io1, io2]));
  899. [array1 setValue:@[io3, io1] forKey:@"intArray"];
  900. XCTAssertEqualObjects([array1.intArray valueForKey:@"self"], (@[io3, io1]));
  901. array1[@"intArray"] = @[io2, io3];
  902. XCTAssertEqualObjects([array1.intArray valueForKey:@"self"], (@[io2, io3]));
  903. // Assigning RLMArray shallow copies
  904. array2.intArray = array1.intArray;
  905. XCTAssertEqualObjects([array2.intArray valueForKey:@"self"], (@[io2, io3]));
  906. [array1.intArray removeAllObjects];
  907. XCTAssertEqualObjects([array2.intArray valueForKey:@"self"], (@[io2, io3]));
  908. // Self-assignment is a no-op
  909. array2.intArray = array2.intArray;
  910. XCTAssertEqualObjects([array2.intArray valueForKey:@"self"], (@[io2, io3]));
  911. array2[@"intArray"] = array2[@"intArray"];
  912. XCTAssertEqualObjects([array2[@"intArray"] valueForKey:@"self"], (@[io2, io3]));
  913. }
  914. - (void)testManagedAssignment {
  915. RLMRealm *realm = self.realmWithTestPath;
  916. [realm beginWriteTransaction];
  917. IntObject *io1 = [IntObject createInRealm:realm withValue:@[@1]];
  918. IntObject *io2 = [IntObject createInRealm:realm withValue:@[@2]];
  919. IntObject *io3 = [IntObject createInRealm:realm withValue:@[@3]];
  920. ArrayPropertyObject *array1 = [ArrayPropertyObject createInRealm:realm withValue:@[@""]];
  921. ArrayPropertyObject *array2 = [ArrayPropertyObject createInRealm:realm withValue:@[@""]];
  922. // Assigning NSArray shallow copies
  923. array1.intArray = (id)@[io1, io2];
  924. XCTAssertEqualObjects([array1.intArray valueForKey:@"intCol"], (@[@1, @2]));
  925. [array1 setValue:@[io3, io1] forKey:@"intArray"];
  926. XCTAssertEqualObjects([array1.intArray valueForKey:@"intCol"], (@[@3, @1]));
  927. array1[@"intArray"] = @[io2, io3];
  928. XCTAssertEqualObjects([array1.intArray valueForKey:@"intCol"], (@[@2, @3]));
  929. // Assigning RLMArray shallow copies
  930. array2.intArray = array1.intArray;
  931. XCTAssertEqualObjects([array2.intArray valueForKey:@"intCol"], (@[@2, @3]));
  932. [array1.intArray removeAllObjects];
  933. XCTAssertEqualObjects([array2.intArray valueForKey:@"intCol"], (@[@2, @3]));
  934. // Self-assignment is a no-op
  935. array2.intArray = array2.intArray;
  936. XCTAssertEqualObjects([array2.intArray valueForKey:@"intCol"], (@[@2, @3]));
  937. array2[@"intArray"] = array2[@"intArray"];
  938. XCTAssertEqualObjects([array2[@"intArray"] valueForKey:@"intCol"], (@[@2, @3]));
  939. [realm cancelWriteTransaction];
  940. }
  941. - (void)testAssignIncorrectType {
  942. RLMRealm *realm = self.realmWithTestPath;
  943. [realm beginWriteTransaction];
  944. ArrayPropertyObject *array = [ArrayPropertyObject createInRealm:realm
  945. withValue:@[@"", @[@[@"a"]], @[@[@0]]]];
  946. RLMAssertThrowsWithReason(array.intArray = (id)array.array,
  947. @"RLMArray<StringObject> does not match expected type 'IntObject' for property 'ArrayPropertyObject.intArray'.");
  948. RLMAssertThrowsWithReason(array[@"intArray"] = array[@"array"],
  949. @"RLMArray<StringObject> does not match expected type 'IntObject' for property 'ArrayPropertyObject.intArray'.");
  950. [realm cancelWriteTransaction];
  951. }
  952. - (void)testNotificationSentInitially {
  953. RLMRealm *realm = self.realmWithTestPath;
  954. [realm beginWriteTransaction];
  955. ArrayPropertyObject *array = [ArrayPropertyObject createInRealm:realm withValue:@[@"", @[], @[]]];
  956. [realm commitWriteTransaction];
  957. id expectation = [self expectationWithDescription:@""];
  958. id token = [array.array addNotificationBlock:^(RLMArray *array, RLMCollectionChange *change, NSError *error) {
  959. XCTAssertNotNil(array);
  960. XCTAssertNil(change);
  961. XCTAssertNil(error);
  962. [expectation fulfill];
  963. }];
  964. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  965. [(RLMNotificationToken *)token invalidate];
  966. }
  967. - (void)testNotificationSentAfterCommit {
  968. RLMRealm *realm = self.realmWithTestPath;
  969. [realm beginWriteTransaction];
  970. ArrayPropertyObject *array = [ArrayPropertyObject createInRealm:realm withValue:@[@"", @[], @[]]];
  971. [realm commitWriteTransaction];
  972. __block bool first = true;
  973. __block id expectation = [self expectationWithDescription:@""];
  974. id token = [array.array addNotificationBlock:^(RLMArray *array, RLMCollectionChange *change, NSError *error) {
  975. XCTAssertNotNil(array);
  976. XCTAssert(first ? !change : !!change);
  977. XCTAssertNil(error);
  978. first = false;
  979. [expectation fulfill];
  980. }];
  981. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  982. expectation = [self expectationWithDescription:@""];
  983. [self dispatchAsyncAndWait:^{
  984. RLMRealm *realm = self.realmWithTestPath;
  985. [realm transactionWithBlock:^{
  986. RLMArray *array = [(ArrayPropertyObject *)[ArrayPropertyObject allObjectsInRealm:realm].firstObject array];
  987. [array addObject:[[StringObject alloc] init]];
  988. }];
  989. }];
  990. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  991. [(RLMNotificationToken *)token invalidate];
  992. }
  993. - (void)testNotificationNotSentForUnrelatedChange {
  994. RLMRealm *realm = self.realmWithTestPath;
  995. [realm beginWriteTransaction];
  996. ArrayPropertyObject *array = [ArrayPropertyObject createInRealm:realm withValue:@[@"", @[], @[]]];
  997. [realm commitWriteTransaction];
  998. id expectation = [self expectationWithDescription:@""];
  999. id token = [array.array addNotificationBlock:^(__unused RLMArray *array, __unused RLMCollectionChange *change, __unused NSError *error) {
  1000. // will throw if it's incorrectly called a second time due to the
  1001. // unrelated write transaction
  1002. [expectation fulfill];
  1003. }];
  1004. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  1005. // All notification blocks are called as part of a single runloop event, so
  1006. // waiting for this one also waits for the above one to get a chance to run
  1007. [self waitForNotification:RLMRealmDidChangeNotification realm:realm block:^{
  1008. [self dispatchAsyncAndWait:^{
  1009. RLMRealm *realm = self.realmWithTestPath;
  1010. [realm transactionWithBlock:^{
  1011. [ArrayPropertyObject createInRealm:realm withValue:@[@"", @[], @[]]];
  1012. }];
  1013. }];
  1014. }];
  1015. [(RLMNotificationToken *)token invalidate];
  1016. }
  1017. - (void)testNotificationSentOnlyForActualRefresh {
  1018. RLMRealm *realm = self.realmWithTestPath;
  1019. [realm beginWriteTransaction];
  1020. ArrayPropertyObject *array = [ArrayPropertyObject createInRealm:realm withValue:@[@"", @[], @[]]];
  1021. [realm commitWriteTransaction];
  1022. __block id expectation = [self expectationWithDescription:@""];
  1023. id token = [array.array addNotificationBlock:^(RLMArray *array, __unused RLMCollectionChange *change, NSError *error) {
  1024. XCTAssertNotNil(array);
  1025. XCTAssertNil(error);
  1026. // will throw if it's called a second time before we create the new
  1027. // expectation object immediately before manually refreshing
  1028. [expectation fulfill];
  1029. }];
  1030. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  1031. // Turn off autorefresh, so the background commit should not result in a notification
  1032. realm.autorefresh = NO;
  1033. // All notification blocks are called as part of a single runloop event, so
  1034. // waiting for this one also waits for the above one to get a chance to run
  1035. [self waitForNotification:RLMRealmRefreshRequiredNotification realm:realm block:^{
  1036. [self dispatchAsyncAndWait:^{
  1037. RLMRealm *realm = self.realmWithTestPath;
  1038. [realm transactionWithBlock:^{
  1039. RLMArray *array = [(ArrayPropertyObject *)[ArrayPropertyObject allObjectsInRealm:realm].firstObject array];
  1040. [array addObject:[[StringObject alloc] init]];
  1041. }];
  1042. }];
  1043. }];
  1044. expectation = [self expectationWithDescription:@""];
  1045. [realm refresh];
  1046. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  1047. [(RLMNotificationToken *)token invalidate];
  1048. }
  1049. - (void)testDeletingObjectWithNotificationsRegistered {
  1050. RLMRealm *realm = self.realmWithTestPath;
  1051. [realm beginWriteTransaction];
  1052. ArrayPropertyObject *array = [ArrayPropertyObject createInRealm:realm withValue:@[@"", @[], @[]]];
  1053. [realm commitWriteTransaction];
  1054. __block id expectation = [self expectationWithDescription:@""];
  1055. id token = [array.array addNotificationBlock:^(RLMArray *array, __unused RLMCollectionChange *change, NSError *error) {
  1056. XCTAssertNotNil(array);
  1057. XCTAssertNil(error);
  1058. [expectation fulfill];
  1059. }];
  1060. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  1061. [realm beginWriteTransaction];
  1062. [realm deleteObject:array];
  1063. [realm commitWriteTransaction];
  1064. [(RLMNotificationToken *)token invalidate];
  1065. }
  1066. - (void)testAllMethodsCheckThread {
  1067. RLMRealm *realm = [RLMRealm defaultRealm];
  1068. __block IntObject *io;
  1069. __block RLMArray *array;
  1070. [realm transactionWithBlock:^{
  1071. io = [IntObject createInDefaultRealmWithValue:@[@0]];
  1072. ArrayPropertyObject *obj = [ArrayPropertyObject createInDefaultRealmWithValue:@[@"", @[], @[io]]];
  1073. array = obj.intArray;
  1074. }];
  1075. [realm beginWriteTransaction];
  1076. [self dispatchAsyncAndWait:^{
  1077. RLMAssertThrowsWithReasonMatching([array count], @"thread");
  1078. RLMAssertThrowsWithReasonMatching([array objectAtIndex:0], @"thread");
  1079. RLMAssertThrowsWithReasonMatching([array firstObject], @"thread");
  1080. RLMAssertThrowsWithReasonMatching([array lastObject], @"thread");
  1081. RLMAssertThrowsWithReasonMatching([array addObject:io], @"thread");
  1082. RLMAssertThrowsWithReasonMatching([array addObjects:@[io]], @"thread");
  1083. RLMAssertThrowsWithReasonMatching([array insertObject:io atIndex:0], @"thread");
  1084. RLMAssertThrowsWithReasonMatching([array removeObjectAtIndex:0], @"thread");
  1085. RLMAssertThrowsWithReasonMatching([array removeLastObject], @"thread");
  1086. RLMAssertThrowsWithReasonMatching([array removeAllObjects], @"thread");
  1087. RLMAssertThrowsWithReasonMatching([array replaceObjectAtIndex:0 withObject:io], @"thread");
  1088. RLMAssertThrowsWithReasonMatching([array moveObjectAtIndex:0 toIndex:1], @"thread");
  1089. RLMAssertThrowsWithReasonMatching([array exchangeObjectAtIndex:0 withObjectAtIndex:1], @"thread");
  1090. RLMAssertThrowsWithReasonMatching([array indexOfObject:[IntObject allObjects].firstObject], @"thread");
  1091. RLMAssertThrowsWithReasonMatching([array indexOfObjectWhere:@"intCol = 0"], @"thread");
  1092. RLMAssertThrowsWithReasonMatching([array indexOfObjectWithPredicate:[NSPredicate predicateWithFormat:@"intCol = 0"]], @"thread");
  1093. RLMAssertThrowsWithReasonMatching([array objectsWhere:@"intCol = 0"], @"thread");
  1094. RLMAssertThrowsWithReasonMatching([array objectsWithPredicate:[NSPredicate predicateWithFormat:@"intCol = 0"]], @"thread");
  1095. RLMAssertThrowsWithReasonMatching([array sortedResultsUsingKeyPath:@"intCol" ascending:YES], @"thread");
  1096. RLMAssertThrowsWithReasonMatching([array sortedResultsUsingDescriptors:@[[RLMSortDescriptor sortDescriptorWithKeyPath:@"intCol" ascending:YES]]], @"thread");
  1097. RLMAssertThrowsWithReasonMatching(array[0], @"thread");
  1098. RLMAssertThrowsWithReasonMatching(array[0] = io, @"thread");
  1099. RLMAssertThrowsWithReasonMatching([array valueForKey:@"intCol"], @"thread");
  1100. RLMAssertThrowsWithReasonMatching([array setValue:@1 forKey:@"intCol"], @"thread");
  1101. RLMAssertThrowsWithReasonMatching({for (__unused id obj in array);}, @"thread");
  1102. }];
  1103. [realm cancelWriteTransaction];
  1104. }
  1105. - (void)testAllMethodsCheckForInvalidation {
  1106. RLMRealm *realm = [RLMRealm defaultRealm];
  1107. __block IntObject *io;
  1108. __block RLMArray *array;
  1109. [realm transactionWithBlock:^{
  1110. io = [IntObject createInDefaultRealmWithValue:@[@0]];
  1111. ArrayPropertyObject *obj = [ArrayPropertyObject createInDefaultRealmWithValue:@[@"", @[], @[io]]];
  1112. array = obj.intArray;
  1113. }];
  1114. [realm beginWriteTransaction];
  1115. XCTAssertNoThrow([array objectClassName]);
  1116. XCTAssertNoThrow([array realm]);
  1117. XCTAssertNoThrow([array isInvalidated]);
  1118. XCTAssertNoThrow([array count]);
  1119. XCTAssertNoThrow([array objectAtIndex:0]);
  1120. XCTAssertNoThrow([array firstObject]);
  1121. XCTAssertNoThrow([array lastObject]);
  1122. XCTAssertNoThrow([array addObject:io]);
  1123. XCTAssertNoThrow([array addObjects:@[io]]);
  1124. XCTAssertNoThrow([array insertObject:io atIndex:0]);
  1125. XCTAssertNoThrow([array removeObjectAtIndex:0]);
  1126. XCTAssertNoThrow([array removeLastObject]);
  1127. XCTAssertNoThrow([array removeAllObjects]);
  1128. [array addObjects:@[io, io, io]];
  1129. XCTAssertNoThrow([array replaceObjectAtIndex:0 withObject:io]);
  1130. XCTAssertNoThrow([array moveObjectAtIndex:0 toIndex:1]);
  1131. XCTAssertNoThrow([array exchangeObjectAtIndex:0 withObjectAtIndex:1]);
  1132. XCTAssertNoThrow([array indexOfObject:[IntObject allObjects].firstObject]);
  1133. XCTAssertNoThrow([array indexOfObjectWhere:@"intCol = 0"]);
  1134. XCTAssertNoThrow([array indexOfObjectWithPredicate:[NSPredicate predicateWithFormat:@"intCol = 0"]]);
  1135. XCTAssertNoThrow([array objectsWhere:@"intCol = 0"]);
  1136. XCTAssertNoThrow([array objectsWithPredicate:[NSPredicate predicateWithFormat:@"intCol = 0"]]);
  1137. XCTAssertNoThrow([array sortedResultsUsingKeyPath:@"intCol" ascending:YES]);
  1138. XCTAssertNoThrow([array sortedResultsUsingDescriptors:@[[RLMSortDescriptor sortDescriptorWithKeyPath:@"intCol" ascending:YES]]]);
  1139. XCTAssertNoThrow(array[0]);
  1140. XCTAssertNoThrow(array[0] = io);
  1141. XCTAssertNoThrow([array valueForKey:@"intCol"]);
  1142. XCTAssertNoThrow([array setValue:@1 forKey:@"intCol"]);
  1143. XCTAssertNoThrow({for (__unused id obj in array);});
  1144. [realm cancelWriteTransaction];
  1145. [realm invalidate];
  1146. [realm beginWriteTransaction];
  1147. io = [IntObject createInDefaultRealmWithValue:@[@0]];
  1148. XCTAssertNoThrow([array objectClassName]);
  1149. XCTAssertNoThrow([array realm]);
  1150. XCTAssertNoThrow([array isInvalidated]);
  1151. RLMAssertThrowsWithReasonMatching([array count], @"invalidated");
  1152. RLMAssertThrowsWithReasonMatching([array objectAtIndex:0], @"invalidated");
  1153. RLMAssertThrowsWithReasonMatching([array firstObject], @"invalidated");
  1154. RLMAssertThrowsWithReasonMatching([array lastObject], @"invalidated");
  1155. RLMAssertThrowsWithReasonMatching([array addObject:io], @"invalidated");
  1156. RLMAssertThrowsWithReasonMatching([array addObjects:@[io]], @"invalidated");
  1157. RLMAssertThrowsWithReasonMatching([array insertObject:io atIndex:0], @"invalidated");
  1158. RLMAssertThrowsWithReasonMatching([array removeObjectAtIndex:0], @"invalidated");
  1159. RLMAssertThrowsWithReasonMatching([array removeLastObject], @"invalidated");
  1160. RLMAssertThrowsWithReasonMatching([array removeAllObjects], @"invalidated");
  1161. RLMAssertThrowsWithReasonMatching([array replaceObjectAtIndex:0 withObject:io], @"invalidated");
  1162. RLMAssertThrowsWithReasonMatching([array moveObjectAtIndex:0 toIndex:1], @"invalidated");
  1163. RLMAssertThrowsWithReasonMatching([array exchangeObjectAtIndex:0 withObjectAtIndex:1], @"invalidated");
  1164. RLMAssertThrowsWithReasonMatching([array indexOfObject:[IntObject allObjects].firstObject], @"invalidated");
  1165. RLMAssertThrowsWithReasonMatching([array indexOfObjectWhere:@"intCol = 0"], @"invalidated");
  1166. RLMAssertThrowsWithReasonMatching([array indexOfObjectWithPredicate:[NSPredicate predicateWithFormat:@"intCol = 0"]], @"invalidated");
  1167. RLMAssertThrowsWithReasonMatching([array objectsWhere:@"intCol = 0"], @"invalidated");
  1168. RLMAssertThrowsWithReasonMatching([array objectsWithPredicate:[NSPredicate predicateWithFormat:@"intCol = 0"]], @"invalidated");
  1169. RLMAssertThrowsWithReasonMatching([array sortedResultsUsingKeyPath:@"intCol" ascending:YES], @"invalidated");
  1170. RLMAssertThrowsWithReasonMatching([array sortedResultsUsingDescriptors:@[[RLMSortDescriptor sortDescriptorWithKeyPath:@"intCol" ascending:YES]]], @"invalidated");
  1171. RLMAssertThrowsWithReasonMatching(array[0], @"invalidated");
  1172. RLMAssertThrowsWithReasonMatching(array[0] = io, @"invalidated");
  1173. RLMAssertThrowsWithReasonMatching([array valueForKey:@"intCol"], @"invalidated");
  1174. RLMAssertThrowsWithReasonMatching([array setValue:@1 forKey:@"intCol"], @"invalidated");
  1175. RLMAssertThrowsWithReasonMatching({for (__unused id obj in array);}, @"invalidated");
  1176. [realm cancelWriteTransaction];
  1177. }
  1178. - (void)testMutatingMethodsCheckForWriteTransaction {
  1179. RLMRealm *realm = [RLMRealm defaultRealm];
  1180. __block IntObject *io;
  1181. __block RLMArray *array;
  1182. [realm transactionWithBlock:^{
  1183. io = [IntObject createInDefaultRealmWithValue:@[@0]];
  1184. ArrayPropertyObject *obj = [ArrayPropertyObject createInDefaultRealmWithValue:@[@"", @[], @[io]]];
  1185. array = obj.intArray;
  1186. }];
  1187. XCTAssertNoThrow([array objectClassName]);
  1188. XCTAssertNoThrow([array realm]);
  1189. XCTAssertNoThrow([array isInvalidated]);
  1190. XCTAssertNoThrow([array count]);
  1191. XCTAssertNoThrow([array objectAtIndex:0]);
  1192. XCTAssertNoThrow([array firstObject]);
  1193. XCTAssertNoThrow([array lastObject]);
  1194. XCTAssertNoThrow([array indexOfObject:[IntObject allObjects].firstObject]);
  1195. XCTAssertNoThrow([array indexOfObjectWhere:@"intCol = 0"]);
  1196. XCTAssertNoThrow([array indexOfObjectWithPredicate:[NSPredicate predicateWithFormat:@"intCol = 0"]]);
  1197. XCTAssertNoThrow([array objectsWhere:@"intCol = 0"]);
  1198. XCTAssertNoThrow([array objectsWithPredicate:[NSPredicate predicateWithFormat:@"intCol = 0"]]);
  1199. XCTAssertNoThrow([array sortedResultsUsingKeyPath:@"intCol" ascending:YES]);
  1200. XCTAssertNoThrow([array sortedResultsUsingDescriptors:@[[RLMSortDescriptor sortDescriptorWithKeyPath:@"intCol" ascending:YES]]]);
  1201. XCTAssertNoThrow(array[0]);
  1202. XCTAssertNoThrow([array valueForKey:@"intCol"]);
  1203. XCTAssertNoThrow({for (__unused id obj in array);});
  1204. RLMAssertThrowsWithReasonMatching([array addObject:io], @"write transaction");
  1205. RLMAssertThrowsWithReasonMatching([array addObjects:@[io]], @"write transaction");
  1206. RLMAssertThrowsWithReasonMatching([array insertObject:io atIndex:0], @"write transaction");
  1207. RLMAssertThrowsWithReasonMatching([array removeObjectAtIndex:0], @"write transaction");
  1208. RLMAssertThrowsWithReasonMatching([array removeLastObject], @"write transaction");
  1209. RLMAssertThrowsWithReasonMatching([array removeAllObjects], @"write transaction");
  1210. RLMAssertThrowsWithReasonMatching([array replaceObjectAtIndex:0 withObject:io], @"write transaction");
  1211. RLMAssertThrowsWithReasonMatching([array moveObjectAtIndex:0 toIndex:1], @"write transaction");
  1212. RLMAssertThrowsWithReasonMatching([array exchangeObjectAtIndex:0 withObjectAtIndex:1], @"write transaction");
  1213. RLMAssertThrowsWithReasonMatching(array[0] = io, @"write transaction");
  1214. RLMAssertThrowsWithReasonMatching([array setValue:@1 forKey:@"intCol"], @"write transaction");
  1215. }
  1216. @end