ResultsTests.m 52 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188
  1. ////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2014 Realm Inc.
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License");
  6. // you may not use this file except in compliance with the License.
  7. // You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. //
  17. ////////////////////////////////////////////////////////////////////////////
  18. #import "RLMTestCase.h"
  19. #import <mach/mach.h>
  20. #import <objc/runtime.h>
  21. @interface ResultsTests : RLMTestCase
  22. @end
  23. @implementation ResultsTests
  24. - (void)testFastEnumeration
  25. {
  26. RLMRealm *realm = self.realmWithTestPath;
  27. // enumerate empty array
  28. for (__unused id obj in [AggregateObject allObjectsInRealm:realm]) {
  29. XCTFail(@"Should be empty");
  30. }
  31. [realm beginWriteTransaction];
  32. for (int i = 0; i < 18; ++i) {
  33. [AggregateObject createInRealm:realm withValue:@[@10, @1.2f, @0.0, @YES, NSDate.date]];
  34. }
  35. [realm commitWriteTransaction];
  36. RLMResults *result = [AggregateObject objectsInRealm:realm where:@"intCol < %i", 100];
  37. XCTAssertEqual(result.count, 18U);
  38. __weak id objects[18];
  39. NSInteger count = 0;
  40. for (AggregateObject *ao in result) {
  41. XCTAssertNotNil(ao, @"Object is not nil and accessible");
  42. if (count > 16) {
  43. // 16 is the size of blocks fast enumeration happens to ask for at
  44. // the moment, but of course that's just an implementation detail
  45. // that may change
  46. XCTAssertNil(objects[count - 16]);
  47. }
  48. objects[count++] = ao;
  49. }
  50. XCTAssertEqual(count, 18, @"should have enumerated 18 objects");
  51. for (int i = 0; i < count; i++) {
  52. XCTAssertNil(objects[i], @"Object should have been released");
  53. }
  54. @autoreleasepool {
  55. for (AggregateObject *ao in result) {
  56. objects[0] = ao;
  57. break;
  58. }
  59. }
  60. XCTAssertNil(objects[0], @"Object should have been released");
  61. }
  62. - (void)testFirst {
  63. XCTAssertNil(IntObject.allObjects.firstObject);
  64. XCTAssertNil([IntObject objectsWhere:@"intCol > 5"].firstObject);
  65. RLMRealm *realm = [RLMRealm defaultRealm];
  66. [realm beginWriteTransaction];
  67. [IntObject createInDefaultRealmWithValue:@[@10]];
  68. [IntObject createInDefaultRealmWithValue:@[@20]];
  69. [realm commitWriteTransaction];
  70. XCTAssertEqual(10, [IntObject.allObjects.firstObject intCol]);
  71. XCTAssertEqual(10, [[IntObject objectsWhere:@"intCol > 5"].firstObject intCol]);
  72. XCTAssertEqual(20, [[IntObject objectsWhere:@"intCol > 10"].firstObject intCol]);
  73. }
  74. - (void)testLast {
  75. XCTAssertNil(IntObject.allObjects.lastObject);
  76. XCTAssertNil([IntObject objectsWhere:@"intCol > 5"].lastObject);
  77. RLMRealm *realm = [RLMRealm defaultRealm];
  78. [realm beginWriteTransaction];
  79. [IntObject createInDefaultRealmWithValue:@[@20]];
  80. [IntObject createInDefaultRealmWithValue:@[@10]];
  81. [realm commitWriteTransaction];
  82. XCTAssertEqual(10, [IntObject.allObjects.lastObject intCol]);
  83. XCTAssertEqual(10, [[IntObject objectsWhere:@"intCol > 5"].lastObject intCol]);
  84. XCTAssertEqual(20, [[IntObject objectsWhere:@"intCol > 10"].lastObject intCol]);
  85. }
  86. - (void)testSubscript {
  87. RLMAssertThrowsWithReasonMatching([IntObject allObjects][0], @"0.*less than 0");
  88. RLMAssertThrowsWithReasonMatching([IntObject objectsWhere:@"intCol > 5"][0], @"0.*less than 0");
  89. RLMRealm *realm = [RLMRealm defaultRealm];
  90. [realm beginWriteTransaction];
  91. [IntObject createInDefaultRealmWithValue:@[@10]];
  92. [IntObject createInDefaultRealmWithValue:@[@20]];
  93. [realm commitWriteTransaction];
  94. XCTAssertEqual(10, [IntObject.allObjects[0] intCol]);
  95. XCTAssertEqual(10, [[IntObject objectsWhere:@"intCol > 5"][0] intCol]);
  96. XCTAssertEqual(20, [[IntObject objectsWhere:@"intCol > 10"][0] intCol]);
  97. RLMAssertThrowsWithReasonMatching([IntObject allObjects][2], @"2.*less than 2");
  98. RLMAssertThrowsWithReasonMatching([IntObject objectsWhere:@"intCol > 5"][2], @"2.*less than 2");
  99. }
  100. - (void)testValueForKey {
  101. RLMRealm *realm = self.realmWithTestPath;
  102. [realm beginWriteTransaction];
  103. XCTAssertEqualObjects([[AggregateObject allObjectsInRealm:realm] valueForKey:@"intCol"], @[]);
  104. NSDate *dateMinInput = [NSDate date];
  105. NSDate *dateMaxInput = [dateMinInput dateByAddingTimeInterval:1000];
  106. [AggregateObject createInRealm:realm withValue:@[@0, @1.2f, @0.0, @YES, dateMinInput]];
  107. [AggregateObject createInRealm:realm withValue:@[@1, @0.0f, @2.5, @NO, dateMaxInput]];
  108. [AggregateObject createInRealm:realm withValue:@[@0, @1.2f, @0.0, @YES, dateMinInput]];
  109. [AggregateObject createInRealm:realm withValue:@[@1, @0.0f, @2.5, @NO, dateMaxInput]];
  110. [AggregateObject createInRealm:realm withValue:@[@0, @1.2f, @0.0, @YES, dateMinInput]];
  111. [AggregateObject createInRealm:realm withValue:@[@1, @0.0f, @2.5, @NO, dateMaxInput]];
  112. [AggregateObject createInRealm:realm withValue:@[@0, @1.2f, @0.0, @YES, dateMinInput]];
  113. [AggregateObject createInRealm:realm withValue:@[@1, @0.0f, @2.5, @NO, dateMaxInput]];
  114. [AggregateObject createInRealm:realm withValue:@[@0, @1.2f, @0.0, @YES, dateMinInput]];
  115. [AggregateObject createInRealm:realm withValue:@[@0, @1.2f, @0.0, @YES, dateMinInput]];
  116. XCTAssertEqualObjects([[AggregateObject allObjectsInRealm:realm] valueForKey:@"intCol"],
  117. (@[@0, @1, @0, @1, @0, @1, @0, @1, @0, @0]));
  118. XCTAssertTrue([[[[AggregateObject allObjectsInRealm:realm] valueForKey:@"self"] firstObject]
  119. isEqualToObject:[AggregateObject allObjectsInRealm:realm].firstObject]);
  120. XCTAssertTrue([[[[AggregateObject allObjectsInRealm:realm] valueForKey:@"self"] lastObject]
  121. isEqualToObject:[AggregateObject allObjectsInRealm:realm].lastObject]);
  122. XCTAssertEqualObjects([[AggregateObject objectsInRealm:realm where:@"intCol != 1"] valueForKey:@"intCol"],
  123. (@[@0, @0, @0, @0, @0, @0]));
  124. XCTAssertTrue([[[[AggregateObject objectsInRealm:realm where:@"intCol != 1"] valueForKey:@"self"] firstObject]
  125. isEqualToObject:[AggregateObject objectsInRealm:realm where:@"intCol != 1"].firstObject]);
  126. XCTAssertTrue([[[[AggregateObject objectsInRealm:realm where:@"intCol != 1"] valueForKey:@"self"] lastObject]
  127. isEqualToObject:[AggregateObject objectsInRealm:realm where:@"intCol != 1"].lastObject]);
  128. [realm commitWriteTransaction];
  129. XCTAssertEqualObjects([[AggregateObject allObjectsInRealm:realm] valueForKey:@"intCol"],
  130. (@[@0, @1, @0, @1, @0, @1, @0, @1, @0, @0]));
  131. XCTAssertEqualObjects([[AggregateObject objectsInRealm:realm where:@"intCol != 1"] valueForKey:@"intCol"],
  132. (@[@0, @0, @0, @0, @0, @0]));
  133. XCTAssertThrows([[AggregateObject allObjectsInRealm:realm] valueForKey:@"invalid"]);
  134. }
  135. - (void)testSetValueForKey {
  136. RLMRealm *realm = self.realmWithTestPath;
  137. [realm beginWriteTransaction];
  138. NSDate *dateMinInput = [NSDate date];
  139. NSDate *dateMaxInput = [dateMinInput dateByAddingTimeInterval:1000];
  140. [AggregateObject createInRealm:realm withValue:@[@0, @1.2f, @0.0, @YES, dateMinInput]];
  141. [AggregateObject createInRealm:realm withValue:@[@1, @0.0f, @2.5, @NO, dateMaxInput]];
  142. [AggregateObject createInRealm:realm withValue:@[@0, @1.2f, @0.0, @YES, dateMinInput]];
  143. [AggregateObject createInRealm:realm withValue:@[@1, @0.0f, @2.5, @NO, dateMaxInput]];
  144. [AggregateObject createInRealm:realm withValue:@[@0, @1.2f, @0.0, @YES, dateMinInput]];
  145. [AggregateObject createInRealm:realm withValue:@[@1, @0.0f, @2.5, @NO, dateMaxInput]];
  146. [AggregateObject createInRealm:realm withValue:@[@0, @1.2f, @0.0, @YES, dateMinInput]];
  147. [AggregateObject createInRealm:realm withValue:@[@1, @0.0f, @2.5, @NO, dateMaxInput]];
  148. [AggregateObject createInRealm:realm withValue:@[@0, @1.2f, @0.0, @YES, dateMinInput]];
  149. [AggregateObject createInRealm:realm withValue:@[@0, @1.2f, @0.0, @YES, dateMinInput]];
  150. [[AggregateObject allObjectsInRealm:realm] setValue:@25 forKey:@"intCol"];
  151. XCTAssertEqualObjects([[AggregateObject allObjectsInRealm:realm] valueForKey:@"intCol"],
  152. (@[@25, @25, @25, @25, @25, @25, @25, @25, @25, @25]));
  153. [[AggregateObject objectsInRealm:realm where:@"floatCol > 1"] setValue:@10 forKey:@"intCol"];
  154. XCTAssertEqualObjects([[AggregateObject objectsInRealm:realm where:@"floatCol > 1"] valueForKey:@"intCol"],
  155. (@[@10, @10, @10, @10, @10, @10]));
  156. XCTAssertThrows([[AggregateObject allObjectsInRealm:realm] valueForKey:@"invalid"]);
  157. [[AggregateObject objectsInRealm:realm where:@"intCol != 5"] setValue:@5 forKey:@"intCol"];
  158. XCTAssertEqualObjects([[AggregateObject allObjectsInRealm:realm] valueForKey:@"intCol"],
  159. (@[@5, @5, @5, @5, @5, @5, @5, @5, @5, @5]));
  160. [realm commitWriteTransaction];
  161. RLMAssertThrowsWithReasonMatching([[AggregateObject allObjectsInRealm:realm] setValue:@25 forKey:@"intCol"],
  162. @"write transaction");
  163. RLMAssertThrowsWithReasonMatching([[AggregateObject objectsInRealm:realm where:@"floatCol > 1"] setValue:@10 forKey:@"intCol"],
  164. @"write transaction");
  165. }
  166. - (void)testObjectAggregate
  167. {
  168. RLMRealm *realm = [RLMRealm defaultRealm];
  169. RLMResults *noArray = [AggregateObject objectsWhere:@"boolCol == NO"];
  170. RLMResults *yesArray = [AggregateObject objectsWhere:@"boolCol == YES"];
  171. RLMResults *allArray = [AggregateObject allObjects];
  172. XCTAssertEqual(0, [noArray sumOfProperty:@"intCol"].intValue);
  173. XCTAssertEqual(0, [allArray sumOfProperty:@"intCol"].intValue);
  174. XCTAssertNil([noArray averageOfProperty:@"intCol"]);
  175. XCTAssertNil([allArray averageOfProperty:@"intCol"]);
  176. XCTAssertNil([noArray minOfProperty:@"intCol"]);
  177. XCTAssertNil([allArray minOfProperty:@"intCol"]);
  178. XCTAssertNil([noArray maxOfProperty:@"intCol"]);
  179. XCTAssertNil([allArray maxOfProperty:@"intCol"]);
  180. [realm beginWriteTransaction];
  181. NSDate *dateMinInput = [NSDate date];
  182. NSDate *dateMaxInput = [dateMinInput dateByAddingTimeInterval:1000];
  183. [AggregateObject createInRealm:realm withValue:@[@0, @1.2f, @0.0, @YES, dateMinInput]];
  184. [AggregateObject createInRealm:realm withValue:@[@1, @0.0f, @2.5, @NO, dateMaxInput]];
  185. [AggregateObject createInRealm:realm withValue:@[@0, @1.2f, @0.0, @YES, dateMinInput]];
  186. [AggregateObject createInRealm:realm withValue:@[@1, @0.0f, @2.5, @NO, dateMaxInput]];
  187. [AggregateObject createInRealm:realm withValue:@[@0, @1.2f, @0.0, @YES, dateMinInput]];
  188. [AggregateObject createInRealm:realm withValue:@[@1, @0.0f, @2.5, @NO, dateMaxInput]];
  189. [AggregateObject createInRealm:realm withValue:@[@0, @1.2f, @0.0, @YES, dateMinInput]];
  190. [AggregateObject createInRealm:realm withValue:@[@1, @0.0f, @2.5, @NO, dateMaxInput]];
  191. [AggregateObject createInRealm:realm withValue:@[@0, @1.2f, @0.0, @YES, dateMinInput]];
  192. [AggregateObject createInRealm:realm withValue:@[@0, @1.2f, @0.0, @YES, dateMinInput]];
  193. [realm commitWriteTransaction];
  194. // SUM ::::::::::::::::::::::::::::::::::::::::::::::
  195. // Test int sum
  196. XCTAssertEqual([noArray sumOfProperty:@"intCol"].integerValue, 4);
  197. XCTAssertEqual([yesArray sumOfProperty:@"intCol"].integerValue, 0);
  198. XCTAssertEqual([allArray sumOfProperty:@"intCol"].integerValue, 4);
  199. // Test float sum
  200. XCTAssertEqualWithAccuracy([noArray sumOfProperty:@"floatCol"].floatValue, 0.0f, 0.1f);
  201. XCTAssertEqualWithAccuracy([yesArray sumOfProperty:@"floatCol"].floatValue, 7.2f, 0.1f);
  202. XCTAssertEqualWithAccuracy([allArray sumOfProperty:@"floatCol"].floatValue, 7.2f, 0.1f);
  203. // Test double sum
  204. XCTAssertEqualWithAccuracy([noArray sumOfProperty:@"doubleCol"].doubleValue, 10.0, 0.1f);
  205. XCTAssertEqualWithAccuracy([yesArray sumOfProperty:@"doubleCol"].doubleValue, 0.0, 0.1f);
  206. XCTAssertEqualWithAccuracy([allArray sumOfProperty:@"doubleCol"].doubleValue, 10.0, 0.1f);
  207. // Test invalid column name
  208. RLMAssertThrowsWithReasonMatching([yesArray sumOfProperty:@"foo"], @"foo.*AggregateObject");
  209. RLMAssertThrowsWithReasonMatching([allArray sumOfProperty:@"foo"], @"foo.*AggregateObject");
  210. // Test operation not supported
  211. RLMAssertThrowsWithReasonMatching([yesArray sumOfProperty:@"boolCol"], @"sum.*bool");
  212. RLMAssertThrowsWithReasonMatching([allArray sumOfProperty:@"boolCol"], @"sum.*bool");
  213. // Average ::::::::::::::::::::::::::::::::::::::::::::::
  214. // Test int average
  215. XCTAssertEqualWithAccuracy([noArray averageOfProperty:@"intCol"].doubleValue, 1.0, 0.1f);
  216. XCTAssertEqualWithAccuracy([yesArray averageOfProperty:@"intCol"].doubleValue, 0.0, 0.1f);
  217. XCTAssertEqualWithAccuracy([allArray averageOfProperty:@"intCol"].doubleValue, 0.4, 0.1f);
  218. // Test float average
  219. XCTAssertEqualWithAccuracy([noArray averageOfProperty:@"floatCol"].doubleValue, 0.0, 0.1f);
  220. XCTAssertEqualWithAccuracy([yesArray averageOfProperty:@"floatCol"].doubleValue, 1.2, 0.1f);
  221. XCTAssertEqualWithAccuracy([allArray averageOfProperty:@"floatCol"].doubleValue, 0.72, 0.1f);
  222. // Test double average
  223. XCTAssertEqualWithAccuracy([noArray averageOfProperty:@"doubleCol"].doubleValue, 2.5, 0.1f);
  224. XCTAssertEqualWithAccuracy([yesArray averageOfProperty:@"doubleCol"].doubleValue, 0.0, 0.1f);
  225. XCTAssertEqualWithAccuracy([allArray averageOfProperty:@"doubleCol"].doubleValue, 1.0, 0.1f);
  226. // Test invalid column name
  227. RLMAssertThrowsWithReasonMatching([yesArray averageOfProperty:@"foo"], @"foo.*AggregateObject");
  228. RLMAssertThrowsWithReasonMatching([allArray averageOfProperty:@"foo"], @"foo.*AggregateObject");
  229. // Test operation not supported
  230. RLMAssertThrowsWithReasonMatching([yesArray averageOfProperty:@"boolCol"], @"average.*bool");
  231. RLMAssertThrowsWithReasonMatching([allArray averageOfProperty:@"boolCol"], @"average.*bool");
  232. // MIN ::::::::::::::::::::::::::::::::::::::::::::::
  233. // Test int min
  234. XCTAssertEqual(1, [[noArray minOfProperty:@"intCol"] intValue]);
  235. XCTAssertEqual(0, [[yesArray minOfProperty:@"intCol"] intValue]);
  236. XCTAssertEqual(0, [[allArray minOfProperty:@"intCol"] intValue]);
  237. // Test float min
  238. XCTAssertEqual(0.0f, [[noArray minOfProperty:@"floatCol"] floatValue]);
  239. XCTAssertEqual(1.2f, [[yesArray minOfProperty:@"floatCol"] floatValue]);
  240. XCTAssertEqual(0.0f, [[allArray minOfProperty:@"floatCol"] floatValue]);
  241. // Test double min
  242. XCTAssertEqual(2.5, [[noArray minOfProperty:@"doubleCol"] doubleValue]);
  243. XCTAssertEqual(0.0, [[yesArray minOfProperty:@"doubleCol"] doubleValue]);
  244. XCTAssertEqual(0.0, [[allArray minOfProperty:@"doubleCol"] doubleValue]);
  245. // Test date min
  246. XCTAssertEqualObjects(dateMaxInput, [noArray minOfProperty:@"dateCol"]);
  247. XCTAssertEqualObjects(dateMinInput, [yesArray minOfProperty:@"dateCol"]);
  248. XCTAssertEqualObjects(dateMinInput, [allArray minOfProperty:@"dateCol"]);
  249. // Test invalid column name
  250. RLMAssertThrowsWithReasonMatching([yesArray minOfProperty:@"foo"], @"foo.*AggregateObject");
  251. RLMAssertThrowsWithReasonMatching([allArray minOfProperty:@"foo"], @"foo.*AggregateObject");
  252. // Test operation not supported
  253. RLMAssertThrowsWithReasonMatching([yesArray minOfProperty:@"boolCol"], @"min.*bool");
  254. RLMAssertThrowsWithReasonMatching([allArray minOfProperty:@"boolCol"], @"min.*bool");
  255. // MAX ::::::::::::::::::::::::::::::::::::::::::::::
  256. // Test int max
  257. XCTAssertEqual(1, [[noArray maxOfProperty:@"intCol"] intValue]);
  258. XCTAssertEqual(0, [[yesArray maxOfProperty:@"intCol"] intValue]);
  259. XCTAssertEqual(1, [[allArray maxOfProperty:@"intCol"] intValue]);
  260. // Test float max
  261. XCTAssertEqual(0.0f, [[noArray maxOfProperty:@"floatCol"] floatValue]);
  262. XCTAssertEqual(1.2f, [[yesArray maxOfProperty:@"floatCol"] floatValue]);
  263. XCTAssertEqual(1.2f, [[allArray maxOfProperty:@"floatCol"] floatValue]);
  264. // Test double max
  265. XCTAssertEqual(2.5, [[noArray maxOfProperty:@"doubleCol"] doubleValue]);
  266. XCTAssertEqual(0.0, [[yesArray maxOfProperty:@"doubleCol"] doubleValue]);
  267. XCTAssertEqual(2.5, [[allArray maxOfProperty:@"doubleCol"] doubleValue]);
  268. // Test date max
  269. XCTAssertEqualObjects(dateMaxInput, [noArray maxOfProperty:@"dateCol"]);
  270. XCTAssertEqualObjects(dateMinInput, [yesArray maxOfProperty:@"dateCol"]);
  271. XCTAssertEqualObjects(dateMaxInput, [allArray maxOfProperty:@"dateCol"]);
  272. // Test invalid column name
  273. RLMAssertThrowsWithReasonMatching([yesArray maxOfProperty:@"foo"], @"foo.*AggregateObject");
  274. RLMAssertThrowsWithReasonMatching([allArray maxOfProperty:@"foo"], @"foo.*AggregateObject");
  275. // Test operation not supported
  276. RLMAssertThrowsWithReasonMatching([yesArray maxOfProperty:@"boolCol"], @"max.*bool");
  277. RLMAssertThrowsWithReasonMatching([allArray maxOfProperty:@"boolCol"], @"max.*bool");
  278. }
  279. - (void)testRenamedPropertyAggregate {
  280. RLMRealm *realm = [RLMRealm defaultRealm];
  281. RLMResults *results = [RenamedProperties1 allObjectsInRealm:realm];
  282. XCTAssertEqual(0, [results sumOfProperty:@"propA"].intValue);
  283. XCTAssertNil([results averageOfProperty:@"propA"]);
  284. XCTAssertNil([results minOfProperty:@"propA"]);
  285. XCTAssertNil([results maxOfProperty:@"propA"]);
  286. XCTAssertThrows([results sumOfProperty:@"prop 1"]);
  287. [realm transactionWithBlock:^{
  288. [RenamedProperties1 createInRealm:realm withValue:@[@1, @""]];
  289. [RenamedProperties1 createInRealm:realm withValue:@[@2, @""]];
  290. [RenamedProperties1 createInRealm:realm withValue:@[@3, @""]];
  291. }];
  292. XCTAssertEqual(6, [results sumOfProperty:@"propA"].intValue);
  293. XCTAssertEqual(2.0, [results averageOfProperty:@"propA"].doubleValue);
  294. XCTAssertEqual(1, [[results minOfProperty:@"propA"] intValue]);
  295. XCTAssertEqual(3, [[results maxOfProperty:@"propA"] intValue]);
  296. }
  297. - (void)testValueForCollectionOperationKeyPath {
  298. RLMRealm *realm = [RLMRealm defaultRealm];
  299. [realm beginWriteTransaction];
  300. EmployeeObject *c1e1 = [EmployeeObject createInRealm:realm withValue:@{@"name": @"Joe", @"age": @40, @"hired": @YES}];
  301. EmployeeObject *c1e2 = [EmployeeObject createInRealm:realm withValue:@{@"name": @"John", @"age": @30, @"hired": @NO}];
  302. EmployeeObject *c1e3 = [EmployeeObject createInRealm:realm withValue:@{@"name": @"Jill", @"age": @25, @"hired": @YES}];
  303. [CompanyObject createInRealm:realm withValue:@{@"name": @"InspiringNames LLC", @"employees": @[c1e1, c1e2, c1e3]}];
  304. EmployeeObject *c2e1 = [EmployeeObject createInRealm:realm withValue:@{@"name": @"A", @"age": @20, @"hired": @YES}];
  305. EmployeeObject *c2e2 = [EmployeeObject createInRealm:realm withValue:@{@"name": @"B", @"age": @30, @"hired": @NO}];
  306. EmployeeObject *c2e3 = [EmployeeObject createInRealm:realm withValue:@{@"name": @"C", @"age": @40, @"hired": @YES}];
  307. [CompanyObject createInRealm:realm withValue:@{@"name": @"ABC AG", @"employees": @[c2e1, c2e2, c2e3]}];
  308. EmployeeObject *c3e1 = [EmployeeObject createInRealm:realm withValue:@{@"name": @"A", @"age": @21, @"hired": @YES}];
  309. [CompanyObject createInRealm:realm withValue:@{@"name": @"ABC AG", @"employees": @[c3e1]}];
  310. [realm commitWriteTransaction];
  311. RLMResults *allCompanies = [CompanyObject allObjects];
  312. RLMResults *allEmployees = [EmployeeObject allObjects];
  313. // count operator
  314. XCTAssertEqual([[allCompanies valueForKeyPath:@"@count"] integerValue], 3);
  315. // numeric operators
  316. XCTAssertEqual([[allEmployees valueForKeyPath:@"@min.age"] intValue], 20);
  317. XCTAssertEqual([[allEmployees valueForKeyPath:@"@max.age"] intValue], 40);
  318. XCTAssertEqual([[allEmployees valueForKeyPath:@"@sum.age"] integerValue], 206);
  319. XCTAssertEqualWithAccuracy([[allEmployees valueForKeyPath:@"@avg.age"] doubleValue], 29.43, 0.1f);
  320. // collection
  321. XCTAssertEqualObjects([allCompanies valueForKeyPath:@"@unionOfObjects.name"],
  322. (@[@"InspiringNames LLC", @"ABC AG", @"ABC AG"]));
  323. XCTAssertEqualObjects([allCompanies valueForKeyPath:@"@distinctUnionOfObjects.name"],
  324. (@[@"ABC AG", @"InspiringNames LLC"]));
  325. XCTAssertEqualObjects([allCompanies valueForKeyPath:@"employees.@unionOfArrays.name"],
  326. (@[@"Joe", @"John", @"Jill", @"A", @"B", @"C", @"A"]));
  327. XCTAssertEqualObjects([[allCompanies valueForKeyPath:@"employees.@distinctUnionOfArrays.name"]
  328. sortedArrayUsingSelector:@selector(compare:)],
  329. (@[@"A", @"B", @"C", @"Jill", @"Joe", @"John"]));
  330. // invalid key paths
  331. RLMAssertThrowsWithReasonMatching([allCompanies valueForKeyPath:@"@invalid.name"],
  332. @"Unsupported KVC collection operator found in key path '@invalid.name'");
  333. RLMAssertThrowsWithReasonMatching([allCompanies valueForKeyPath:@"@sum"],
  334. @"Missing key path for KVC collection operator sum in key path '@sum'");
  335. RLMAssertThrowsWithReasonMatching([allCompanies valueForKeyPath:@"@sum."],
  336. @"Missing key path for KVC collection operator sum in key path '@sum.'");
  337. RLMAssertThrowsWithReasonMatching([allCompanies valueForKeyPath:@"@sum.employees.@sum.age"],
  338. @"Nested key paths.*not supported");
  339. }
  340. - (void)testArrayDescription
  341. {
  342. RLMRealm *realm = [RLMRealm defaultRealm];
  343. [realm beginWriteTransaction];
  344. for (NSInteger i = 0; i < 1012; ++i) {
  345. EmployeeObject *person = [[EmployeeObject alloc] init];
  346. person.name = @"Mary";
  347. person.age = 24;
  348. person.hired = YES;
  349. [realm addObject:person];
  350. }
  351. [realm commitWriteTransaction];
  352. NSString *description = [[EmployeeObject allObjects] description];
  353. XCTAssertTrue([description rangeOfString:@"name"].location != NSNotFound);
  354. XCTAssertTrue([description rangeOfString:@"Mary"].location != NSNotFound);
  355. XCTAssertTrue([description rangeOfString:@"age"].location != NSNotFound);
  356. XCTAssertTrue([description rangeOfString:@"24"].location != NSNotFound);
  357. XCTAssertTrue([description rangeOfString:@"912 objects skipped"].location != NSNotFound);
  358. }
  359. - (void)testIndexOfObject
  360. {
  361. RLMRealm *realm = [RLMRealm defaultRealm];
  362. [realm beginWriteTransaction];
  363. EmployeeObject *po1 = [EmployeeObject createInRealm:realm withValue:@{@"name": @"Joe", @"age": @40, @"hired": @YES}];
  364. EmployeeObject *po2 = [EmployeeObject createInRealm:realm withValue:@{@"name": @"John", @"age": @30, @"hired": @NO}];
  365. EmployeeObject *po3 = [EmployeeObject createInRealm:realm withValue:@{@"name": @"Jill", @"age": @25, @"hired": @YES}];
  366. StringObject *so = [StringObject createInRealm:realm withValue:@[@""]];
  367. StringObject *deletedObject = [StringObject createInRealm:realm withValue:@[@""]];
  368. [realm deleteObject:deletedObject];
  369. [realm commitWriteTransaction];
  370. EmployeeObject *unmanaged = [[EmployeeObject alloc] init];
  371. RLMResults *results = [EmployeeObject objectsWhere:@"hired = YES"];
  372. XCTAssertEqual(0U, [results indexOfObject:po1]);
  373. XCTAssertEqual(1U, [results indexOfObject:po3]);
  374. XCTAssertEqual((NSUInteger)NSNotFound, [results indexOfObject:po2]);
  375. XCTAssertEqual((NSUInteger)NSNotFound, [results indexOfObject:unmanaged]);
  376. RLMAssertThrowsWithReasonMatching([results indexOfObject:so], @"StringObject.*EmployeeObject");
  377. RLMAssertThrowsWithReasonMatching([results indexOfObject:deletedObject], @"Object has been invalidated");
  378. [results lastObject]; // Force to tableview mode
  379. XCTAssertEqual(0U, [results indexOfObject:po1]);
  380. XCTAssertEqual(1U, [results indexOfObject:po3]);
  381. XCTAssertEqual((NSUInteger)NSNotFound, [results indexOfObject:po2]);
  382. XCTAssertEqual((NSUInteger)NSNotFound, [results indexOfObject:unmanaged]);
  383. RLMAssertThrowsWithReasonMatching([results indexOfObject:so], @"StringObject.*EmployeeObject");
  384. RLMAssertThrowsWithReasonMatching([results indexOfObject:deletedObject], @"Object has been invalidated");
  385. // reverse order from sort
  386. results = [[EmployeeObject objectsWhere:@"hired = YES"] sortedResultsUsingKeyPath:@"age" ascending:YES];
  387. XCTAssertEqual(1U, [results indexOfObject:po1]);
  388. XCTAssertEqual(0U, [results indexOfObject:po3]);
  389. XCTAssertEqual((NSUInteger)NSNotFound, [results indexOfObject:po2]);
  390. XCTAssertEqual((NSUInteger)NSNotFound, [results indexOfObject:unmanaged]);
  391. RLMAssertThrowsWithReasonMatching([results indexOfObject:so], @"StringObject.*EmployeeObject");
  392. RLMAssertThrowsWithReasonMatching([results indexOfObject:deletedObject], @"Object has been invalidated");
  393. results = [EmployeeObject allObjects];
  394. XCTAssertEqual(0U, [results indexOfObject:po1]);
  395. XCTAssertEqual(1U, [results indexOfObject:po2]);
  396. XCTAssertEqual(2U, [results indexOfObject:po3]);
  397. XCTAssertEqual((NSUInteger)NSNotFound, [results indexOfObject:unmanaged]);
  398. RLMAssertThrowsWithReasonMatching([results indexOfObject:so], @"StringObject.*EmployeeObject");
  399. RLMAssertThrowsWithReasonMatching([results indexOfObject:deletedObject], @"Object has been invalidated");
  400. }
  401. - (void)testIndexOfObjectWhere
  402. {
  403. RLMRealm *realm = [RLMRealm defaultRealm];
  404. [realm beginWriteTransaction];
  405. [EmployeeObject createInRealm:realm withValue:@{@"name": @"Joe", @"age": @40, @"hired": @YES}];
  406. [EmployeeObject createInRealm:realm withValue:@{@"name": @"John", @"age": @30, @"hired": @NO}];
  407. [EmployeeObject createInRealm:realm withValue:@{@"name": @"Jill", @"age": @25, @"hired": @YES}];
  408. [EmployeeObject createInRealm:realm withValue:@{@"name": @"Phil", @"age": @38, @"hired": @NO}];
  409. [realm commitWriteTransaction];
  410. RLMResults *results = [EmployeeObject objectsWhere:@"hired = YES"];
  411. XCTAssertEqual(0U, ([results indexOfObjectWhere:@"age = %d", 40]));
  412. XCTAssertEqual(1U, ([results indexOfObjectWhere:@"age = %d", 25]));
  413. XCTAssertEqual((NSUInteger)NSNotFound, ([results indexOfObjectWhere:@"age = %d", 30]));
  414. results = [EmployeeObject allObjects];
  415. XCTAssertEqual(0U, ([results indexOfObjectWhere:@"age = %d", 40]));
  416. XCTAssertEqual(1U, ([results indexOfObjectWhere:@"age = %d", 30]));
  417. XCTAssertEqual(2U, ([results indexOfObjectWhere:@"age = %d", 25]));
  418. XCTAssertEqual((NSUInteger)NSNotFound, ([results indexOfObjectWhere:@"age = %d", 35]));
  419. results = [[EmployeeObject allObjects] sortedResultsUsingKeyPath:@"age" ascending:YES];
  420. NSUInteger youngestHired = [results indexOfObjectWhere:@"hired = YES"];
  421. XCTAssertEqual(0U, youngestHired);
  422. XCTAssertEqualObjects(@"Jill", [results[youngestHired] name]);
  423. NSUInteger youngestNotHired = [results indexOfObjectWhere:@"hired = NO"];
  424. XCTAssertEqual(1U, youngestNotHired);
  425. XCTAssertEqualObjects(@"John", [results[youngestNotHired] name]);
  426. }
  427. - (void)testSubqueryLifetime
  428. {
  429. RLMRealm *realm = [RLMRealm defaultRealm];
  430. [realm beginWriteTransaction];
  431. [EmployeeObject createInRealm:realm withValue:@{@"name": @"John", @"age": @30, @"hired": @NO}];
  432. [EmployeeObject createInRealm:realm withValue:@{@"name": @"Jill", @"age": @50, @"hired": @YES}];
  433. [realm commitWriteTransaction];
  434. RLMResults<EmployeeObject *> *subarray = nil;
  435. {
  436. __attribute((objc_precise_lifetime)) RLMResults *results = [EmployeeObject objectsWhere:@"hired = YES"];
  437. subarray = [results objectsWhere:@"age = 40"];
  438. }
  439. [realm beginWriteTransaction];
  440. [EmployeeObject createInRealm:realm withValue:@{@"name": @"Joe", @"age": @40, @"hired": @YES}];
  441. [realm commitWriteTransaction];
  442. XCTAssertEqualObjects(@"Joe", subarray[0][@"name"]);
  443. }
  444. - (void)testMultiSortLifetime
  445. {
  446. RLMRealm *realm = [RLMRealm defaultRealm];
  447. [realm beginWriteTransaction];
  448. [EmployeeObject createInRealm:realm withValue:@{@"name": @"John", @"age": @30, @"hired": @NO}];
  449. [EmployeeObject createInRealm:realm withValue:@{@"name": @"Jill", @"age": @50, @"hired": @YES}];
  450. [realm commitWriteTransaction];
  451. RLMResults<EmployeeObject *> *subarray = nil;
  452. {
  453. __attribute((objc_precise_lifetime)) RLMResults *results = [[EmployeeObject allObjects] sortedResultsUsingKeyPath:@"age" ascending:YES];
  454. subarray = [results sortedResultsUsingKeyPath:@"age" ascending:NO];
  455. }
  456. [realm beginWriteTransaction];
  457. [EmployeeObject createInRealm:realm withValue:@{@"name": @"Joe", @"age": @40, @"hired": @YES}];
  458. [realm commitWriteTransaction];
  459. XCTAssertEqual(3U, subarray.count);
  460. XCTAssertEqualObjects(@"Jill", subarray[0][@"name"]);
  461. XCTAssertEqualObjects(@"Joe", subarray[1][@"name"]);
  462. XCTAssertEqualObjects(@"John", subarray[2][@"name"]);
  463. }
  464. - (void)testSortingExistingQuery
  465. {
  466. RLMRealm *realm = [RLMRealm defaultRealm];
  467. [realm beginWriteTransaction];
  468. [EmployeeObject createInRealm:realm withValue:@{@"name": @"A", @"age": @20, @"hired": @YES}];
  469. [EmployeeObject createInRealm:realm withValue:@{@"name": @"B", @"age": @30, @"hired": @NO}];
  470. [EmployeeObject createInRealm:realm withValue:@{@"name": @"C", @"age": @40, @"hired": @YES}];
  471. [realm commitWriteTransaction];
  472. RLMResults *sortedAge = [[EmployeeObject allObjects] sortedResultsUsingKeyPath:@"age" ascending:YES];
  473. RLMResults *sortedName = [sortedAge sortedResultsUsingKeyPath:@"name" ascending:NO];
  474. XCTAssertEqual(20, [(EmployeeObject *)sortedAge[0] age]);
  475. XCTAssertEqual(40, [(EmployeeObject *)sortedName[0] age]);
  476. }
  477. - (void)testRerunningSortedQuery {
  478. RLMRealm *realm = [RLMRealm defaultRealm];
  479. RLMResults *sortedAge = [[EmployeeObject allObjects] sortedResultsUsingKeyPath:@"age" ascending:YES];
  480. [sortedAge lastObject]; // Force creation of the TableView
  481. RLMResults *sortedName = [sortedAge sortedResultsUsingKeyPath:@"name" ascending:NO];
  482. [sortedName lastObject]; // Force creation of the TableView
  483. RLMResults *filtered = [sortedName objectsWhere:@"age > 20"];
  484. [filtered lastObject]; // Force creation of the TableView
  485. [realm beginWriteTransaction];
  486. [EmployeeObject createInRealm:realm withValue:@{@"name": @"A", @"age": @20, @"hired": @YES}];
  487. [EmployeeObject createInRealm:realm withValue:@{@"name": @"B", @"age": @30, @"hired": @NO}];
  488. [EmployeeObject createInRealm:realm withValue:@{@"name": @"C", @"age": @40, @"hired": @YES}];
  489. [realm commitWriteTransaction];
  490. XCTAssertEqual(3U, sortedAge.count);
  491. XCTAssertEqual(3U, sortedName.count);
  492. XCTAssertEqual(2U, filtered.count);
  493. XCTAssertEqual(20, [(EmployeeObject *)sortedAge[0] age]);
  494. XCTAssertEqual(30, [(EmployeeObject *)sortedAge[1] age]);
  495. XCTAssertEqual(40, [(EmployeeObject *)sortedAge[2] age]);
  496. XCTAssertEqual(40, [(EmployeeObject *)sortedName[0] age]);
  497. XCTAssertEqual(30, [(EmployeeObject *)sortedName[1] age]);
  498. XCTAssertEqual(20, [(EmployeeObject *)sortedName[2] age]);
  499. XCTAssertEqual(40, [(EmployeeObject *)filtered[0] age]);
  500. XCTAssertEqual(30, [(EmployeeObject *)filtered[1] age]);
  501. }
  502. - (void)testLiveUpdateFirst {
  503. RLMRealm *realm = self.realmWithTestPath;
  504. [realm beginWriteTransaction];
  505. [IntObject createInRealm:realm withValue:@[@0]];
  506. RLMResults *objects = [IntObject objectsInRealm:realm where:@"intCol = 0"];
  507. XCTAssertNotNil([objects firstObject]);
  508. [objects.firstObject setIntCol:1];
  509. XCTAssertNil([objects firstObject]);
  510. [realm cancelWriteTransaction];
  511. }
  512. - (void)testLiveUpdateLast {
  513. RLMRealm *realm = self.realmWithTestPath;
  514. [realm beginWriteTransaction];
  515. [IntObject createInRealm:realm withValue:@[@0]];
  516. RLMResults *objects = [IntObject objectsInRealm:realm where:@"intCol = 0"];
  517. XCTAssertNotNil([objects lastObject]);
  518. [objects.lastObject setIntCol:1];
  519. XCTAssertNil([objects lastObject]);
  520. [realm cancelWriteTransaction];
  521. }
  522. static vm_size_t get_resident_size() {
  523. struct task_basic_info info;
  524. mach_msg_type_number_t size = sizeof(info);
  525. task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &size);
  526. return info.resident_size;
  527. }
  528. - (void)testQueryMemoryUsage {
  529. RLMRealm *realm = [self realmWithTestPath];
  530. [realm beginWriteTransaction];
  531. StringObject *obj = [[StringObject alloc] init];
  532. obj.stringCol = @"a";
  533. [realm addObject:obj];
  534. [realm commitWriteTransaction];
  535. NSPredicate *pred = [NSPredicate predicateWithFormat:@"stringCol = 'a'"];
  536. // Check for memory leaks when creating queries by comparing the memory usage
  537. // before and after creating a very large number of queries. Anything less
  538. // than doubling is allowed as there's going to be some natural fluctuation,
  539. // and failing to clean up 10k queries resulted in far more than doubling.
  540. vm_size_t size = get_resident_size();
  541. for (int i = 0; i < 10000; ++i) {
  542. @autoreleasepool {
  543. RLMResults *matches = [StringObject objectsInRealm:realm withPredicate:pred];
  544. XCTAssertEqualObjects([matches[0] stringCol], @"a");
  545. }
  546. }
  547. XCTAssert(get_resident_size() < size * 2);
  548. }
  549. - (void)testDeleteAllObjects
  550. {
  551. RLMRealm *realm = self.realmWithTestPath;
  552. [realm beginWriteTransaction];
  553. [StringObject createInRealm:realm withValue:@[@"name1"]];
  554. [StringObject createInRealm:realm withValue:@[@"name2"]];
  555. [realm commitWriteTransaction];
  556. RLMResults *results = [StringObject objectsInRealm:realm where:@"stringCol = 'name1'"];
  557. RLMAssertThrowsWithReasonMatching([realm deleteObjects:results], @"write transaction");
  558. [realm beginWriteTransaction];
  559. [realm deleteObjects:results];
  560. [realm commitWriteTransaction];
  561. XCTAssertEqual(0U, results.count);
  562. XCTAssertEqual(1U, [StringObject allObjectsInRealm:realm].count);
  563. results = [StringObject allObjectsInRealm:realm];
  564. RLMAssertThrowsWithReasonMatching([realm deleteObjects:results], @"write transaction");
  565. [realm beginWriteTransaction];
  566. [realm deleteObjects:results];
  567. [realm commitWriteTransaction];
  568. XCTAssertEqual(0U, results.count);
  569. XCTAssertEqual(0U, [StringObject allObjectsInRealm:realm].count);
  570. }
  571. - (void)testEnumerateAndDeleteTableResults {
  572. RLMRealm *realm = self.realmWithTestPath;
  573. const int count = 40;
  574. [realm beginWriteTransaction];
  575. for (int i = 0; i < count; ++i) {
  576. [IntObject createInRealm:realm withValue:@[@(i)]];
  577. }
  578. int enumeratedCount = 0;
  579. for (IntObject *io in [IntObject allObjectsInRealm:realm]) {
  580. ++enumeratedCount;
  581. [realm deleteObject:io];
  582. }
  583. XCTAssertEqual(0U, [IntObject allObjectsInRealm:realm].count);
  584. XCTAssertEqual(count, enumeratedCount);
  585. [realm cancelWriteTransaction];
  586. }
  587. - (void)testEnumerateAndMutateQueryCondition {
  588. RLMRealm *realm = self.realmWithTestPath;
  589. const int count = 40;
  590. [realm beginWriteTransaction];
  591. for (int i = 0; i < count; ++i) {
  592. [IntObject createInRealm:realm withValue:@[@(0)]];
  593. }
  594. int enumeratedCount = 0;
  595. for (IntObject *io in [IntObject objectsInRealm:realm where:@"intCol = 0"]) {
  596. ++enumeratedCount;
  597. io.intCol = enumeratedCount;
  598. }
  599. XCTAssertEqual(0U, [IntObject objectsInRealm:realm where:@"intCol = 0"].count);
  600. XCTAssertEqual(count, enumeratedCount);
  601. [realm cancelWriteTransaction];
  602. }
  603. - (void)testManualRefreshDuringEnumeration {
  604. RLMRealm *realm = self.realmWithTestPath;
  605. const int count = 40;
  606. [realm beginWriteTransaction];
  607. for (int i = 0; i < count; ++i) {
  608. [IntObject createInRealm:realm withValue:@[@(0)]];
  609. }
  610. [realm commitWriteTransaction];
  611. realm.autorefresh = NO;
  612. [self dispatchAsyncAndWait:^{
  613. RLMRealm *realm = self.realmWithTestPath;
  614. [realm beginWriteTransaction];
  615. // FIXME: this is roundabout because `table.clear()` does not update
  616. // table views correctly
  617. [realm deleteObjects:[IntObject objectsInRealm:realm where:@"intCol = 0"]];
  618. [realm commitWriteTransaction];
  619. }];
  620. int enumeratedCount = 0;
  621. for (IntObject *io in [IntObject allObjectsInRealm:realm]) {
  622. [realm refresh];
  623. XCTAssertTrue(io.invalidated);
  624. ++enumeratedCount;
  625. }
  626. XCTAssertEqual(enumeratedCount, count);
  627. }
  628. - (void)testCallInvalidateDuringEnumeration {
  629. RLMRealm *realm = self.realmWithTestPath;
  630. const int count = 40;
  631. [realm beginWriteTransaction];
  632. for (int i = 0; i < count; ++i) {
  633. [IntObject createInRealm:realm withValue:@[@(0)]];
  634. }
  635. [realm commitWriteTransaction];
  636. int enumeratedCount = 0;
  637. @try {
  638. for (__unused IntObject *io in [IntObject objectsInRealm:realm where:@"intCol = 0"]) {
  639. ++enumeratedCount;
  640. [realm invalidate];
  641. }
  642. XCTFail(@"Should have thrown an exception");
  643. }
  644. @catch (NSException *e) {
  645. XCTAssertEqualObjects(e.reason, @"Collection is no longer valid");
  646. }
  647. XCTAssertEqual(16, enumeratedCount);
  648. // Also test with the enumeration done within a write transaction
  649. [realm beginWriteTransaction];
  650. enumeratedCount = 0;
  651. @try {
  652. for (__unused IntObject *io in [IntObject objectsInRealm:realm where:@"intCol = 0"]) {
  653. ++enumeratedCount;
  654. [realm invalidate];
  655. }
  656. XCTFail(@"Should have thrown an exception");
  657. }
  658. @catch (NSException *e) {
  659. XCTAssertEqualObjects(e.reason, @"Collection is no longer valid");
  660. }
  661. XCTAssertEqual(16, enumeratedCount);
  662. }
  663. - (void)testBeginWriteTransactionDuringEnumeration {
  664. RLMRealm *realm = self.realmWithTestPath;
  665. const int count = 40;
  666. [realm beginWriteTransaction];
  667. for (int i = 0; i < count; ++i) {
  668. [IntObject createInRealm:realm withValue:@[@(0)]];
  669. }
  670. [realm commitWriteTransaction];
  671. for (IntObject *io in [IntObject objectsInRealm:realm where:@"intCol = 0"]) {
  672. [realm beginWriteTransaction];
  673. io.intCol = 1;
  674. [realm commitWriteTransaction];
  675. }
  676. XCTAssertEqual(40U, [IntObject objectsInRealm:realm where:@"intCol = 1"].count);
  677. }
  678. - (void)testAllMethodsCheckThread {
  679. RLMRealm *realm = [RLMRealm defaultRealm];
  680. [realm transactionWithBlock:^{
  681. [IntObject createInDefaultRealmWithValue:@[@0]];
  682. }];
  683. RLMResults *results = [IntObject allObjects];
  684. XCTAssertNoThrow([results isInvalidated]);
  685. XCTAssertNoThrow([results objectAtIndex:0]);
  686. XCTAssertNoThrow([results firstObject]);
  687. XCTAssertNoThrow([results lastObject]);
  688. XCTAssertNoThrow([results indexOfObject:[IntObject allObjects].firstObject]);
  689. XCTAssertNoThrow([results indexOfObjectWhere:@"intCol = 0"]);
  690. XCTAssertNoThrow([results indexOfObjectWithPredicate:[NSPredicate predicateWithFormat:@"intCol = 0"]]);
  691. XCTAssertNoThrow([results objectsWhere:@"intCol = 0"]);
  692. XCTAssertNoThrow([results objectsWithPredicate:[NSPredicate predicateWithFormat:@"intCol = 0"]]);
  693. XCTAssertNoThrow([results sortedResultsUsingKeyPath:@"intCol" ascending:YES]);
  694. XCTAssertNoThrow([results sortedResultsUsingDescriptors:@[[RLMSortDescriptor sortDescriptorWithKeyPath:@"intCol" ascending:YES]]]);
  695. XCTAssertNoThrow([results minOfProperty:@"intCol"]);
  696. XCTAssertNoThrow([results maxOfProperty:@"intCol"]);
  697. XCTAssertNoThrow([results sumOfProperty:@"intCol"]);
  698. XCTAssertNoThrow([results averageOfProperty:@"intCol"]);
  699. XCTAssertNoThrow(results[0]);
  700. XCTAssertNoThrow([results valueForKey:@"intCol"]);
  701. [self dispatchAsyncAndWait:^{
  702. XCTAssertThrows([results isInvalidated]);
  703. XCTAssertThrows([results objectAtIndex:0]);
  704. XCTAssertThrows([results firstObject]);
  705. XCTAssertThrows([results lastObject]);
  706. XCTAssertThrows([results indexOfObject:[IntObject allObjects].firstObject]);
  707. XCTAssertThrows([results indexOfObjectWhere:@"intCol = 0"]);
  708. XCTAssertThrows([results indexOfObjectWithPredicate:[NSPredicate predicateWithFormat:@"intCol = 0"]]);
  709. XCTAssertThrows([results objectsWhere:@"intCol = 0"]);
  710. XCTAssertThrows([results objectsWithPredicate:[NSPredicate predicateWithFormat:@"intCol = 0"]]);
  711. XCTAssertThrows([results sortedResultsUsingKeyPath:@"intCol" ascending:YES]);
  712. XCTAssertThrows([results sortedResultsUsingDescriptors:@[[RLMSortDescriptor sortDescriptorWithKeyPath:@"intCol" ascending:YES]]]);
  713. XCTAssertThrows([results minOfProperty:@"intCol"]);
  714. XCTAssertThrows([results maxOfProperty:@"intCol"]);
  715. XCTAssertThrows([results sumOfProperty:@"intCol"]);
  716. XCTAssertThrows([results averageOfProperty:@"intCol"]);
  717. XCTAssertThrows(results[0]);
  718. XCTAssertThrows([results valueForKey:@"intCol"]);
  719. }];
  720. }
  721. - (void)testAllMethodsCheckForInvalidation {
  722. RLMRealm *realm = [RLMRealm defaultRealm];
  723. [realm transactionWithBlock:^{
  724. [IntObject createInDefaultRealmWithValue:@[@0]];
  725. }];
  726. RLMResults *results = [IntObject allObjects];
  727. XCTAssertFalse(results.isInvalidated);
  728. XCTAssertNoThrow([results objectAtIndex:0]);
  729. XCTAssertNoThrow([results firstObject]);
  730. XCTAssertNoThrow([results lastObject]);
  731. XCTAssertNoThrow([results indexOfObject:[IntObject allObjects].firstObject]);
  732. XCTAssertNoThrow([results indexOfObjectWhere:@"intCol = 0"]);
  733. XCTAssertNoThrow([results indexOfObjectWithPredicate:[NSPredicate predicateWithFormat:@"intCol = 0"]]);
  734. XCTAssertNoThrow([results objectsWhere:@"intCol = 0"]);
  735. XCTAssertNoThrow([results objectsWithPredicate:[NSPredicate predicateWithFormat:@"intCol = 0"]]);
  736. XCTAssertNoThrow([results sortedResultsUsingKeyPath:@"intCol" ascending:YES]);
  737. XCTAssertNoThrow([results sortedResultsUsingDescriptors:@[[RLMSortDescriptor sortDescriptorWithKeyPath:@"intCol" ascending:YES]]]);
  738. XCTAssertNoThrow([results minOfProperty:@"intCol"]);
  739. XCTAssertNoThrow([results maxOfProperty:@"intCol"]);
  740. XCTAssertNoThrow([results sumOfProperty:@"intCol"]);
  741. XCTAssertNoThrow([results averageOfProperty:@"intCol"]);
  742. XCTAssertNoThrow(results[0]);
  743. XCTAssertNoThrow([results valueForKey:@"intCol"]);
  744. XCTAssertNoThrow({for (__unused id obj in results);});
  745. [realm invalidate];
  746. XCTAssertTrue(results.isInvalidated);
  747. XCTAssertThrows([results objectAtIndex:0]);
  748. XCTAssertThrows([results firstObject]);
  749. XCTAssertThrows([results lastObject]);
  750. XCTAssertThrows([results indexOfObject:[IntObject allObjects].firstObject]);
  751. XCTAssertThrows([results indexOfObjectWhere:@"intCol = 0"]);
  752. XCTAssertThrows([results indexOfObjectWithPredicate:[NSPredicate predicateWithFormat:@"intCol = 0"]]);
  753. XCTAssertThrows([results objectsWhere:@"intCol = 0"]);
  754. XCTAssertThrows([results objectsWithPredicate:[NSPredicate predicateWithFormat:@"intCol = 0"]]);
  755. XCTAssertThrows([results sortedResultsUsingKeyPath:@"intCol" ascending:YES]);
  756. XCTAssertThrows([results sortedResultsUsingDescriptors:@[[RLMSortDescriptor sortDescriptorWithKeyPath:@"intCol" ascending:YES]]]);
  757. XCTAssertThrows([results minOfProperty:@"intCol"]);
  758. XCTAssertThrows([results maxOfProperty:@"intCol"]);
  759. XCTAssertThrows([results sumOfProperty:@"intCol"]);
  760. XCTAssertThrows([results averageOfProperty:@"intCol"]);
  761. XCTAssertThrows(results[0]);
  762. XCTAssertThrows([results valueForKey:@"intCol"]);
  763. XCTAssertThrows({for (__unused id obj in results);});
  764. }
  765. - (void)testResultsDependingOnDeletedLinkView {
  766. RLMRealm *realm = [RLMRealm defaultRealm];
  767. __block IntegerArrayPropertyObject *object;
  768. [realm transactionWithBlock:^{
  769. IntObject* intObject = [IntObject createInDefaultRealmWithValue:@[@0]];
  770. object = [IntegerArrayPropertyObject createInDefaultRealmWithValue:@[ @0, @[ intObject ] ]];
  771. }];
  772. RLMResults *results = [object.array sortedResultsUsingKeyPath:@"intCol" ascending:YES];
  773. [results firstObject];
  774. RLMResults *unevaluatedResults = [object.array sortedResultsUsingKeyPath:@"intCol" ascending:YES];
  775. [realm transactionWithBlock:^{
  776. [realm deleteObject:object];
  777. }];
  778. XCTAssertFalse(results.isInvalidated);
  779. XCTAssertFalse(unevaluatedResults.isInvalidated);
  780. XCTAssertEqual(0u, results.count);
  781. XCTAssertEqual(0u, unevaluatedResults.count);
  782. XCTAssertEqualObjects(nil, results.firstObject);
  783. XCTAssertEqualObjects(nil, unevaluatedResults.firstObject);
  784. }
  785. - (void)testResultsDependingOnDeletedTableView {
  786. RLMRealm *realm = [RLMRealm defaultRealm];
  787. __block DogObject *dog;
  788. [realm transactionWithBlock:^{
  789. dog = [DogObject createInDefaultRealmWithValue:@[ @"Fido", @3 ]];
  790. [OwnerObject createInDefaultRealmWithValue:@[ @"John", dog ]];
  791. }];
  792. RLMResults *results = [dog.owners objectsWhere:@"name != 'Not a real name'"];
  793. [results firstObject];
  794. RLMResults *unevaluatedResults = [dog.owners objectsWhere:@"name != 'Not a real name'"];
  795. [realm transactionWithBlock:^{
  796. [realm deleteObject:dog];
  797. }];
  798. XCTAssertFalse(results.isInvalidated);
  799. XCTAssertFalse(unevaluatedResults.isInvalidated);
  800. XCTAssertEqual(0u, results.count);
  801. XCTAssertEqual(0u, unevaluatedResults.count);
  802. XCTAssertEqualObjects(nil, results.firstObject);
  803. XCTAssertEqualObjects(nil, unevaluatedResults.firstObject);
  804. }
  805. - (void)testResultsDependingOnLinkingObjects {
  806. RLMRealm *realm = [RLMRealm defaultRealm];
  807. __block DogObject *dog;
  808. __block OwnerObject *owner;
  809. [realm transactionWithBlock:^{
  810. dog = [DogObject createInDefaultRealmWithValue:@[ @"Fido", @3 ]];
  811. owner = [OwnerObject createInDefaultRealmWithValue:@[ @"John", dog ]];
  812. }];
  813. XCTestExpectation *expectation = [self expectationWithDescription:@""];
  814. RLMResults *results = [DogObject objectsWhere:@"ANY owners.name == 'James'"];
  815. id token = [results addNotificationBlock:^(__unused RLMResults *results, RLMCollectionChange *change, __unused NSError *error) {
  816. if (change != nil) {
  817. [expectation fulfill];
  818. }
  819. CFRunLoopStop(CFRunLoopGetCurrent());
  820. }];
  821. // Consume the initial notification.
  822. CFRunLoopRun();
  823. XCTAssertEqual(0u, results.count);
  824. XCTAssertNil(results.firstObject);
  825. [realm transactionWithBlock:^{
  826. owner.name = @"James";
  827. }];
  828. XCTAssertEqual(1u, results.count);
  829. XCTAssertEqualObjects(dog.dogName, [results.firstObject dogName]);
  830. [self waitForExpectationsWithTimeout:1.0 handler:nil];
  831. token = nil;
  832. }
  833. - (void)testDistinctQuery {
  834. RLMRealm *realm = [RLMRealm defaultRealm];
  835. __block DogObject *fido;
  836. __block DogObject *cujo;
  837. __block DogObject *buster;
  838. [realm transactionWithBlock:^{
  839. fido = [DogObject createInDefaultRealmWithValue:@[ @"Fido", @3 ]];
  840. cujo = [DogObject createInDefaultRealmWithValue:@[ @"Cujo", @3 ]];
  841. buster = [DogObject createInDefaultRealmWithValue:@[ @"Buster", @5 ]];
  842. }];
  843. RLMResults *results = [[DogObject allObjects] distinctResultsUsingKeyPaths:@[@"age"]];
  844. NSMutableArray *ages = NSMutableArray.new;
  845. for (id result in results) {
  846. [ages addObject: result];
  847. }
  848. XCTAssertEqual(2u, results.count);
  849. XCTAssertEqualObjects([ages valueForKey:@"age"], (@[@3, @5]));
  850. }
  851. - (void)testDistinctQueryWithMultipleKeyPaths {
  852. RLMRealm *realm = [RLMRealm defaultRealm];
  853. __block DogObject *fido;
  854. __block DogObject *fido2;
  855. __block DogObject *fido3;
  856. __block DogObject *cujo;
  857. __block DogObject *buster;
  858. __block DogObject *buster2;
  859. __block DogObject *rotunda;
  860. [realm transactionWithBlock:^{
  861. fido = [DogObject createInDefaultRealmWithValue:@[ @"Fido", @3 ]];
  862. fido2 = [DogObject createInDefaultRealmWithValue:@[ @"Fido", @3 ]];
  863. fido3 = [DogObject createInDefaultRealmWithValue:@[ @"Fido", @4 ]];
  864. cujo = [DogObject createInDefaultRealmWithValue:@[ @"Cujo", @3 ]];
  865. buster = [DogObject createInDefaultRealmWithValue:@[ @"Buster", @3 ]];
  866. buster2 = [DogObject createInDefaultRealmWithValue:@[ @"Buster", @3 ]];
  867. rotunda = [DogObject createInDefaultRealmWithValue:@[ @"Rotunda", @7 ]];
  868. }];
  869. RLMResults *results = [[DogObject allObjects] distinctResultsUsingKeyPaths:@[@"dogName", @"age"]];
  870. NSMutableArray *resultsArr = NSMutableArray.new;
  871. for (DogObject *result in results) {
  872. [resultsArr addObject:[NSString stringWithFormat:@"%@/%@", result.dogName, @(result.age)]];
  873. }
  874. XCTAssertEqualObjects(resultsArr, (@[@"Fido/3", @"Fido/4", @"Cujo/3", @"Buster/3", @"Rotunda/7"]));
  875. }
  876. - (void)testDistinctQueryWithMultilevelKeyPath {
  877. RLMRealm *realm = [RLMRealm defaultRealm];
  878. __block OwnerObject *owner1;
  879. __block OwnerObject *owner2;
  880. __block OwnerObject *owner3;
  881. __block DogObject *dog1;
  882. __block DogObject *dog2;
  883. __block DogObject *dog3;
  884. [realm transactionWithBlock:^{
  885. dog1 = [DogObject createInDefaultRealmWithValue:@[ @"Fido", @3 ]];
  886. dog2 = [DogObject createInDefaultRealmWithValue:@[ @"Cujo", @3 ]];
  887. dog3 = [DogObject createInDefaultRealmWithValue:@[ @"Rotunda", @7 ]];
  888. owner1 = [OwnerObject createInDefaultRealmWithValue:@[ @"Joe", dog1 ]];
  889. owner2 = [OwnerObject createInDefaultRealmWithValue:@[ @"Marie", dog2 ]];
  890. owner3 = [OwnerObject createInDefaultRealmWithValue:@[ @"Marie", dog3 ]];
  891. }];
  892. RLMResults *results = [[OwnerObject allObjects] distinctResultsUsingKeyPaths:@[@"dog.age"]];
  893. NSMutableArray *resultsArr = NSMutableArray.new;
  894. for (OwnerObject *result in results) {
  895. [resultsArr addObject:@(result.dog.age)];
  896. }
  897. XCTAssertEqualObjects(resultsArr, (@[@3, @7]));
  898. }
  899. - (void)testDistinctQueryThrowsInvalidKeyPathsSpecified {
  900. RLMRealm *realm = [RLMRealm defaultRealm];
  901. [realm transactionWithBlock:^{
  902. [DogObject createInDefaultRealmWithValue:@[ @"Fido", @3 ]];
  903. [DogObject createInDefaultRealmWithValue:@[ @"Fido", @3 ]];
  904. [DogObject createInDefaultRealmWithValue:@[ @"Fido", @5 ]];
  905. AggregateObject *ao1 = [AggregateObject createInDefaultRealmWithValue:@[ @0 ]];
  906. AggregateObject *ao2 = [AggregateObject createInDefaultRealmWithValue:@[ @0 ]];
  907. [AggregateArrayObject createInDefaultRealmWithValue:@[@[ao1, ao2]]];
  908. [AllTypesObject createInDefaultRealmWithValue:@[]];
  909. }];
  910. XCTAssertThrows([[DogObject allObjects] distinctResultsUsingKeyPaths:@[@""]]);
  911. XCTAssertThrows([[DogObject allObjects] distinctResultsUsingKeyPaths:@[@" "]]);
  912. XCTAssertThrows([[DogObject allObjects] distinctResultsUsingKeyPaths:@[@"\n"]]);
  913. XCTAssertThrows(([[DogObject allObjects] distinctResultsUsingKeyPaths:@[@"dogName", @""]]));
  914. XCTAssertThrows(([[DogObject allObjects] distinctResultsUsingKeyPaths:@[@"dogName", @" "]]));
  915. XCTAssertThrows(([[DogObject allObjects] distinctResultsUsingKeyPaths:@[@"dogName", @"\n"]]));
  916. XCTAssertThrows(([[DogObject allObjects] distinctResultsUsingKeyPaths:@[@"@max.age"]]));
  917. XCTAssertThrows([[AllTypesObject allObjects] distinctResultsUsingKeyPaths:@[@"linkingObjectsCol"]]);
  918. XCTAssertThrows([[AllTypesObject allObjects] distinctResultsUsingKeyPaths:@[@"objectCol"]]);
  919. XCTAssertThrows([[AggregateArrayObject allObjects] distinctResultsUsingKeyPaths:@[@"array"]]);
  920. }
  921. #pragma mark - Frozen Results
  922. static RLMResults<IntObject *> *testResults() {
  923. RLMRealm *realm = [RLMRealm defaultRealm];
  924. [realm transactionWithBlock:^{
  925. [IntObject createInDefaultRealmWithValue:@[@0]];
  926. [IntObject createInDefaultRealmWithValue:@[@1]];
  927. [IntObject createInDefaultRealmWithValue:@[@2]];
  928. }];
  929. return [IntObject allObjects];
  930. }
  931. - (void)testIsFrozen {
  932. RLMResults *unfrozen = testResults();
  933. RLMResults *frozen = [unfrozen freeze];
  934. XCTAssertFalse(unfrozen.isFrozen);
  935. XCTAssertTrue(frozen.isFrozen);
  936. }
  937. - (void)testFreezingFrozenObjectReturnsSelf {
  938. RLMResults *results = testResults();
  939. RLMResults *frozen = [results freeze];
  940. XCTAssertNotEqual(results, frozen);
  941. XCTAssertNotEqual(results.freeze, frozen);
  942. XCTAssertEqual(frozen, frozen.freeze);
  943. }
  944. - (void)testFreezeFromWrongThread {
  945. RLMResults *results = testResults();
  946. [self dispatchAsyncAndWait:^{
  947. RLMAssertThrowsWithReason([results freeze],
  948. @"Realm accessed from incorrect thread");
  949. }];
  950. }
  951. - (void)testAccessFrozenResultsFromDifferentThread {
  952. RLMResults *frozen = [testResults() freeze];
  953. [self dispatchAsyncAndWait:^{
  954. XCTAssertEqualObjects([frozen valueForKey:@"intCol"], (@[@0, @1, @2]));
  955. }];
  956. }
  957. - (void)testObserveFrozenResults {
  958. RLMResults *frozen = [testResults() freeze];
  959. id block = ^(__unused BOOL deleted, __unused NSArray *changes, __unused NSError *error) {};
  960. RLMAssertThrowsWithReason([frozen addNotificationBlock:block],
  961. @"Frozen Realms do not change and do not have change notifications.");
  962. }
  963. - (void)testQueryFrozenResults {
  964. RLMResults *frozen = [testResults() freeze];
  965. XCTAssertEqualObjects([[frozen objectsWhere:@"intCol > 0"] valueForKey:@"intCol"], (@[@1, @2]));
  966. }
  967. - (void)testFrozenResultsDoNotUpdate {
  968. RLMResults *frozen = [testResults() freeze];
  969. RLMRealm *realm = [RLMRealm defaultRealm];
  970. [realm transactionWithBlock:^{
  971. [IntObject createInDefaultRealmWithValue:@[@3]];
  972. }];
  973. XCTAssertEqual(frozen.count, 3);
  974. XCTAssertEqual([frozen objectsWhere:@"intCol = 3"].count, 0);
  975. }
  976. @end