QueryTests.m 146 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858
  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 "RLMObjectSchema_Private.h"
  20. #import "RLMRealmConfiguration_Private.h"
  21. #import "RLMRealm_Dynamic.h"
  22. #import "RLMSchema_Private.h"
  23. #pragma mark - Test Objects
  24. @class LinkChain2, LinkChain3;
  25. @interface LinkChain1 : RLMObject
  26. @property int value;
  27. @property LinkChain2 *next;
  28. @end
  29. @interface LinkChain2 : RLMObject
  30. @property LinkChain3 *next;
  31. @property (readonly) RLMLinkingObjects *prev;
  32. @end
  33. @interface LinkChain3 : RLMObject
  34. @property (readonly) RLMLinkingObjects *prev;
  35. @end
  36. @implementation LinkChain1
  37. @end
  38. @implementation LinkChain2
  39. + (NSDictionary *)linkingObjectsProperties {
  40. return @{@"prev": [RLMPropertyDescriptor descriptorWithClass:LinkChain1.class propertyName:@"next"]};
  41. }
  42. @end
  43. @implementation LinkChain3
  44. + (NSDictionary *)linkingObjectsProperties {
  45. return @{@"prev": [RLMPropertyDescriptor descriptorWithClass:LinkChain2.class propertyName:@"next"]};
  46. }
  47. @end
  48. #pragma mark NonRealmEmployeeObject
  49. @interface NonRealmEmployeeObject : NSObject
  50. @property NSString *name;
  51. @property NSInteger age;
  52. @end
  53. @implementation NonRealmEmployeeObject
  54. @end
  55. @interface PersonLinkObject : RLMObject
  56. @property PersonObject *person;
  57. @end
  58. @implementation PersonLinkObject
  59. @end
  60. #pragma mark QueryObject
  61. @interface QueryObject : RLMObject
  62. @property (nonatomic, assign) BOOL bool1;
  63. @property (nonatomic, assign) BOOL bool2;
  64. @property (nonatomic, assign) NSInteger int1;
  65. @property (nonatomic, assign) NSInteger int2;
  66. @property (nonatomic, assign) float float1;
  67. @property (nonatomic, assign) float float2;
  68. @property (nonatomic, assign) double double1;
  69. @property (nonatomic, assign) double double2;
  70. @property (nonatomic, copy) NSString *string1;
  71. @property (nonatomic, copy) NSString *string2;
  72. @end
  73. @implementation QueryObject
  74. @end
  75. @interface NullQueryObject : RLMObject
  76. @property (nonatomic, copy) NSNumber<RLMBool> *bool1;
  77. @property (nonatomic, copy) NSNumber<RLMBool> *bool2;
  78. @property (nonatomic, copy) NSNumber<RLMInt> *int1;
  79. @property (nonatomic, copy) NSNumber<RLMInt> *int2;
  80. @property (nonatomic, copy) NSNumber<RLMFloat> *float1;
  81. @property (nonatomic, copy) NSNumber<RLMFloat> *float2;
  82. @property (nonatomic, copy) NSNumber<RLMDouble> *double1;
  83. @property (nonatomic, copy) NSNumber<RLMDouble> *double2;
  84. @property (nonatomic, copy) NSString *string1;
  85. @property (nonatomic, copy) NSString *string2;
  86. @end
  87. @implementation NullQueryObject
  88. @end
  89. #pragma mark - Tests
  90. #define RLMAssertCount(cls, expectedCount, ...) \
  91. XCTAssertEqual(expectedCount, ([self evaluate:[cls objectsWhere:__VA_ARGS__]].count))
  92. @interface QueryConstructionTests : RLMTestCase
  93. @end
  94. @implementation QueryConstructionTests
  95. - (RLMResults *)evaluate:(RLMResults *)results {
  96. return results;
  97. }
  98. - (void)testQueryingNilRealmThrows {
  99. XCTAssertThrows([PersonObject allObjectsInRealm:self.nonLiteralNil]);
  100. }
  101. - (void)testDynamicQueryInvalidClass
  102. {
  103. RLMRealm *realm = [RLMRealm defaultRealm];
  104. // class not derived from RLMObject
  105. XCTAssertThrows([realm objects:@"NonRealmPersonObject" where:@"age > 25"], @"invalid object type");
  106. XCTAssertThrows([[realm objects:@"NonRealmPersonObject" where:@"age > 25"] sortedResultsUsingKeyPath:@"age" ascending:YES], @"invalid object type");
  107. // empty string for class name
  108. XCTAssertThrows([realm objects:@"" where:@"age > 25"], @"missing class name");
  109. XCTAssertThrows([[realm objects:@"" where:@"age > 25"] sortedResultsUsingKeyPath:@"age" ascending:YES], @"missing class name");
  110. // nil class name
  111. #pragma clang diagnostic push
  112. #pragma clang diagnostic ignored "-Wnonnull"
  113. XCTAssertThrows([realm objects:nil where:@"age > 25"], @"nil class name");
  114. XCTAssertThrows([[realm objects:nil where:@"age > 25"] sortedResultsUsingKeyPath:@"age" ascending:YES], @"nil class name");
  115. #pragma clang diagnostic pop
  116. }
  117. - (void)testPredicateValidUse
  118. {
  119. RLMRealm *realm = [RLMRealm defaultRealm];
  120. // boolean false
  121. XCTAssertNoThrow([AllTypesObject objectsInRealm:realm where:@"boolCol == no"], @"== no");
  122. XCTAssertNoThrow([AllTypesObject objectsInRealm:realm where:@"boolCol == No"], @"== No");
  123. XCTAssertNoThrow([AllTypesObject objectsInRealm:realm where:@"boolCol == NO"], @"== NO");
  124. XCTAssertNoThrow([AllTypesObject objectsInRealm:realm where:@"boolCol == false"], @"== false");
  125. XCTAssertNoThrow([AllTypesObject objectsInRealm:realm where:@"boolCol == False"], @"== False");
  126. XCTAssertNoThrow([AllTypesObject objectsInRealm:realm where:@"boolCol == FALSE"], @"== FALSE");
  127. XCTAssertNoThrow([AllTypesObject objectsInRealm:realm where:@"boolCol == 0"], @"== 0");
  128. // boolean true
  129. XCTAssertNoThrow([AllTypesObject objectsInRealm:realm where:@"boolCol == yes"], @"== yes");
  130. XCTAssertNoThrow([AllTypesObject objectsInRealm:realm where:@"boolCol == Yes"], @"== Yes");
  131. XCTAssertNoThrow([AllTypesObject objectsInRealm:realm where:@"boolCol == YES"], @"== YES");
  132. XCTAssertNoThrow([AllTypesObject objectsInRealm:realm where:@"boolCol == true"], @"== true");
  133. XCTAssertNoThrow([AllTypesObject objectsInRealm:realm where:@"boolCol == True"], @"== True");
  134. XCTAssertNoThrow([AllTypesObject objectsInRealm:realm where:@"boolCol == TRUE"], @"== TRUE");
  135. XCTAssertNoThrow([AllTypesObject objectsInRealm:realm where:@"boolCol == 1"], @"== 1");
  136. // inequality
  137. XCTAssertNoThrow([AllTypesObject objectsInRealm:realm where:@"boolCol != YES"], @"!= YES");
  138. XCTAssertNoThrow([AllTypesObject objectsInRealm:realm where:@"boolCol <> YES"], @"<> YES");
  139. }
  140. - (void)testPredicateNotSupported
  141. {
  142. // These are things which are valid predicates, but which we do not support
  143. // Aggregate operators on non-arrays
  144. RLMAssertThrowsWithReasonMatching([PersonObject objectsWhere:@"ANY age > 5"], @"Aggregate operations can only.*array property");
  145. RLMAssertThrowsWithReasonMatching([PersonObject objectsWhere:@"ALL age > 5"], @"ALL modifier not supported");
  146. RLMAssertThrowsWithReasonMatching([PersonObject objectsWhere:@"SOME age > 5"], @"Aggregate operations can only.*array property");
  147. RLMAssertThrowsWithReasonMatching([PersonObject objectsWhere:@"NONE age > 5"], @"Aggregate operations can only.*array property");
  148. RLMAssertThrowsWithReasonMatching([PersonLinkObject objectsWhere:@"ANY person.age > 5"], @"Aggregate operations can only.*array property");
  149. RLMAssertThrowsWithReasonMatching([PersonLinkObject objectsWhere:@"ALL person.age > 5"], @"ALL modifier not supported");
  150. RLMAssertThrowsWithReasonMatching([PersonLinkObject objectsWhere:@"SOME person.age > 5"], @"Aggregate operations can only.*array property");
  151. RLMAssertThrowsWithReasonMatching([PersonLinkObject objectsWhere:@"NONE person.age > 5"], @"Aggregate operations can only.*array property");
  152. // nil on LHS of comparison with nullable property
  153. XCTAssertThrows([AllOptionalTypes objectsWhere:@"nil = boolObj"]);
  154. XCTAssertThrows([AllOptionalTypes objectsWhere:@"nil = intObj"]);
  155. XCTAssertThrows([AllOptionalTypes objectsWhere:@"nil = floatObj"]);
  156. XCTAssertThrows([AllOptionalTypes objectsWhere:@"nil = doubleObj"]);
  157. XCTAssertThrows([AllOptionalTypes objectsWhere:@"nil = string"]);
  158. XCTAssertThrows([AllOptionalTypes objectsWhere:@"nil = data"]);
  159. XCTAssertThrows([AllOptionalTypes objectsWhere:@"nil = date"]);
  160. // comparing two constants
  161. XCTAssertThrows([PersonObject objectsWhere:@"5 = 5"]);
  162. XCTAssertThrows([PersonObject objectsWhere:@"nil = nil"]);
  163. // substring operations with constant on LHS
  164. XCTAssertThrows([AllOptionalTypes objectsWhere:@"'' CONTAINS string"]);
  165. XCTAssertThrows([AllOptionalTypes objectsWhere:@"'' BEGINSWITH string"]);
  166. XCTAssertThrows([AllOptionalTypes objectsWhere:@"'' ENDSWITH string"]);
  167. XCTAssertThrows([AllOptionalTypes objectsWhere:@"'' LIKE string"]);
  168. XCTAssertThrows(([AllOptionalTypes objectsWhere:@"%@ CONTAINS data", [NSData data]]));
  169. // data is missing stuff
  170. XCTAssertThrows([AllOptionalTypes objectsWhere:@"data = data"]);
  171. XCTAssertThrows(([LinkToAllTypesObject objectsWhere:@"%@ = allTypesCol.binaryCol", [NSData data]]));
  172. XCTAssertThrows(([LinkToAllTypesObject objectsWhere:@"allTypesCol.binaryCol CONTAINS %@", [NSData data]]));
  173. // LinkList equality is unsupport since the semantics are unclear
  174. XCTAssertThrows(([ArrayOfAllTypesObject objectsWhere:@"ANY array = array"]));
  175. // Unsupported variants of subqueries.
  176. RLMAssertThrowsWithReasonMatching(([ArrayOfAllTypesObject objectsWhere:@"SUBQUERY(array, $obj, $obj.intCol = 5).@count == array.@count"]), @"SUBQUERY.*compared with a constant number");
  177. RLMAssertThrowsWithReasonMatching(([ArrayOfAllTypesObject objectsWhere:@"SUBQUERY(array, $obj, $obj.intCol = 5) == 0"]), @"SUBQUERY.*immediately followed by .@count");
  178. RLMAssertThrowsWithReasonMatching(([ArrayOfAllTypesObject objectsWhere:@"SELF IN SUBQUERY(array, $obj, $obj.intCol = 5)"]), @"Predicate with IN operator must compare.*aggregate$");
  179. // block-based predicate
  180. NSPredicate *pred = [NSPredicate predicateWithBlock:^BOOL (__unused id obj, __unused NSDictionary *bindings) {
  181. return true;
  182. }];
  183. XCTAssertThrows([IntObject objectsWithPredicate:pred]);
  184. }
  185. - (void)testPredicateMisuse
  186. {
  187. RLMRealm *realm = [RLMRealm defaultRealm];
  188. NSString *className = PersonObject.className;
  189. // invalid column/property name
  190. RLMAssertThrowsWithReasonMatching([realm objects:className where:@"height > 72"], @"'height' not found in .* 'PersonObject'");
  191. // wrong/invalid data types
  192. RLMAssertThrowsWithReasonMatching([realm objects:className where:@"age != xyz"], @"'xyz' not found in .* 'PersonObject'");
  193. RLMAssertThrowsWithReasonMatching([realm objects:className where:@"name == 3"], @"type string .* property 'name' .* 'PersonObject'.*: 3");
  194. RLMAssertThrowsWithReasonMatching([realm objects:className where:@"age IN {'xyz'}"], @"type int .* property 'age' .* 'PersonObject'.*: xyz");
  195. XCTAssertThrows([realm objects:className where:@"name IN {3}"], @"invalid type");
  196. className = AllTypesObject.className;
  197. XCTAssertThrows([realm objects:className where:@"boolCol == Foo"], @"invalid type");
  198. XCTAssertThrows([realm objects:className where:@"boolCol == 2"], @"invalid type");
  199. XCTAssertThrows([realm objects:className where:@"dateCol == 7"], @"invalid type");
  200. XCTAssertThrows([realm objects:className where:@"doubleCol == The"], @"invalid type");
  201. XCTAssertThrows([realm objects:className where:@"floatCol == Bar"], @"invalid type");
  202. XCTAssertThrows([realm objects:className where:@"intCol == Baz"], @"invalid type");
  203. className = PersonObject.className;
  204. // compare two constants
  205. XCTAssertThrows([realm objects:className where:@"3 == 3"], @"comparing 2 constants");
  206. // invalid strings
  207. RLMAssertThrowsWithReasonMatching([realm objects:className where:@""], @"Unable to parse");
  208. XCTAssertThrows([realm objects:className where:@"age"], @"column name only");
  209. XCTAssertThrows([realm objects:className where:@"sdlfjasdflj"], @"gibberish");
  210. XCTAssertThrows([realm objects:className where:@"age * 25"], @"invalid operator");
  211. XCTAssertThrows([realm objects:className where:@"age === 25"], @"invalid operator");
  212. XCTAssertThrows([realm objects:className where:@","], @"comma");
  213. XCTAssertThrows([realm objects:className where:@"()"], @"parens");
  214. // Misspelled keypath (should be %K)
  215. RLMAssertThrowsWithReasonMatching([realm objects:className where:@"@K == YES"], @"'@K' is not a valid key path'");
  216. // not a link column
  217. RLMAssertThrowsWithReasonMatching([realm objects:className where:@"age.age == 25"], @"'age' is not a link .* 'PersonObject'");
  218. XCTAssertThrows([realm objects:className where:@"age.age.age == 25"]);
  219. // abuse of BETWEEN
  220. RLMAssertThrowsWithReasonMatching([realm objects:className where:@"age BETWEEN 25"], @"type NSArray for BETWEEN");
  221. RLMAssertThrowsWithReasonMatching([realm objects:className where:@"age BETWEEN Foo"], @"BETWEEN operator must compare a KeyPath with an aggregate");
  222. RLMAssertThrowsWithReasonMatching([realm objects:className where:@"age BETWEEN {age, age}"], @"must be constant values");
  223. RLMAssertThrowsWithReasonMatching([realm objects:className where:@"age BETWEEN {age, 0}"], @"must be constant values");
  224. RLMAssertThrowsWithReasonMatching([realm objects:className where:@"age BETWEEN {0, age}"], @"must be constant values");
  225. RLMAssertThrowsWithReasonMatching([realm objects:className where:@"age BETWEEN {0, {1, 10}}"], @"must be constant values");
  226. NSPredicate *pred = [NSPredicate predicateWithFormat:@"age BETWEEN %@", @[@1]];
  227. RLMAssertThrowsWithReasonMatching([realm objects:className withPredicate:pred], @"exactly two objects");
  228. pred = [NSPredicate predicateWithFormat:@"age BETWEEN %@", @[@1, @2, @3]];
  229. RLMAssertThrowsWithReasonMatching([realm objects:className withPredicate:pred], @"exactly two objects");
  230. pred = [NSPredicate predicateWithFormat:@"age BETWEEN %@", @[@"Foo", @"Bar"]];
  231. RLMAssertThrowsWithReasonMatching([realm objects:className withPredicate:pred], @"type int for BETWEEN");
  232. pred = [NSPredicate predicateWithFormat:@"age BETWEEN %@", @[@1.5, @2.5]];
  233. RLMAssertThrowsWithReasonMatching([realm objects:className withPredicate:pred], @"type int for BETWEEN");
  234. pred = [NSPredicate predicateWithFormat:@"age BETWEEN %@", @[@1, @[@2, @3]]];
  235. RLMAssertThrowsWithReasonMatching([realm objects:className withPredicate:pred], @"type int for BETWEEN");
  236. pred = [NSPredicate predicateWithFormat:@"age BETWEEN %@", @{@25 : @35}];
  237. RLMAssertThrowsWithReasonMatching([realm objects:className withPredicate:pred], @"type NSArray for BETWEEN");
  238. pred = [NSPredicate predicateWithFormat:@"height BETWEEN %@", @[@25, @35]];
  239. RLMAssertThrowsWithReasonMatching([realm objects:className withPredicate:pred], @"'height' not found .* 'PersonObject'");
  240. // bad type in link IN
  241. XCTAssertThrows([PersonLinkObject objectsInRealm:realm where:@"person.age IN {'Tim'}"]);
  242. }
  243. - (void)testStringUnsupportedOperations
  244. {
  245. XCTAssertThrows([StringObject objectsWhere:@"stringCol MATCHES 'abc'"]);
  246. XCTAssertThrows([StringObject objectsWhere:@"stringCol BETWEEN {'a', 'b'}"]);
  247. XCTAssertThrows([StringObject objectsWhere:@"stringCol < 'abc'"]);
  248. XCTAssertThrows([AllTypesObject objectsWhere:@"objectCol.stringCol MATCHES 'abc'"]);
  249. XCTAssertThrows([AllTypesObject objectsWhere:@"objectCol.stringCol BETWEEN {'a', 'b'}"]);
  250. XCTAssertThrows([AllTypesObject objectsWhere:@"objectCol.stringCol < 'abc'"]);
  251. }
  252. - (void)testBinaryComparisonInPredicate {
  253. NSData *data = [NSData data];
  254. RLMAssertCount(BinaryObject, 0U, @"binaryCol BEGINSWITH %@", data);
  255. RLMAssertCount(BinaryObject, 0U, @"binaryCol ENDSWITH %@", data);
  256. RLMAssertCount(BinaryObject, 0U, @"binaryCol CONTAINS %@", data);
  257. RLMAssertCount(BinaryObject, 0U, @"binaryCol BEGINSWITH NULL");
  258. RLMAssertCount(BinaryObject, 0U, @"binaryCol ENDSWITH NULL");
  259. RLMAssertCount(BinaryObject, 0U, @"binaryCol CONTAINS NULL");
  260. RLMAssertCount(BinaryObject, 0U, @"binaryCol = %@", data);
  261. RLMAssertCount(BinaryObject, 0U, @"binaryCol != %@", data);
  262. XCTAssertThrows(([BinaryObject objectsWhere:@"binaryCol < %@", data]));
  263. XCTAssertThrows(([BinaryObject objectsWhere:@"binaryCol <= %@", data]));
  264. XCTAssertThrows(([BinaryObject objectsWhere:@"binaryCol > %@", data]));
  265. XCTAssertThrows(([BinaryObject objectsWhere:@"binaryCol >= %@", data]));
  266. XCTAssertThrows(([BinaryObject objectsWhere:@"binaryCol LIKE %@", data]));
  267. XCTAssertThrows(([BinaryObject objectsWhere:@"binaryCol MATCHES %@", data]));
  268. }
  269. - (void)testLinkQueryInvalid {
  270. XCTAssertThrows([LinkToAllTypesObject objectsWhere:@"allTypesCol.binaryCol = 'a'"], @"Binary data not supported");
  271. XCTAssertThrows([LinkToAllTypesObject objectsWhere:@"allTypesCol.invalidCol = 'a'"], @"Invalid column name should throw");
  272. XCTAssertThrows([LinkToAllTypesObject objectsWhere:@"allTypesCol.longCol = 'a'"], @"Wrong data type should throw");
  273. RLMAssertThrowsWithReasonMatching([ArrayPropertyObject objectsWhere:@"intArray.intCol > 5"], @"Key paths.*array property.*aggregate operations");
  274. RLMAssertThrowsWithReasonMatching([LinkToCompanyObject objectsWhere:@"company.employees.age > 5"], @"Key paths.*array property.*aggregate operations");
  275. RLMAssertThrowsWithReasonMatching([LinkToAllTypesObject objectsWhere:@"allTypesCol.intCol = allTypesCol.doubleCol"], @"Property type mismatch");
  276. }
  277. - (void)testNumericOperatorsOnClass:(Class)class property:(NSString *)property value:(id)value {
  278. NSArray *operators = @[@"<", @"<=", @">", @">=", @"==", @"!="];
  279. for (NSString *operator in operators) {
  280. NSString *fmt = [@[property, operator, @"%@"] componentsJoinedByString:@" "];
  281. RLMAssertCount(class, 0U, fmt, value);
  282. }
  283. }
  284. - (void)testValidOperatorsInNumericComparison {
  285. [self testNumericOperatorsOnClass:[IntObject class] property:@"intCol" value:@0];
  286. [self testNumericOperatorsOnClass:[FloatObject class] property:@"floatCol" value:@0];
  287. [self testNumericOperatorsOnClass:[DoubleObject class] property:@"doubleCol" value:@0];
  288. [self testNumericOperatorsOnClass:[DateObject class] property:@"dateCol" value:NSDate.date];
  289. }
  290. - (void)testStringOperatorsOnClass:(Class)class property:(NSString *)property value:(id)value {
  291. NSArray *operators = @[@"BEGINSWITH", @"ENDSWITH", @"CONTAINS", @"LIKE", @"MATCHES"];
  292. for (NSString *operator in operators) {
  293. NSString *fmt = [@[property, operator, @"%@"] componentsJoinedByString:@" "];
  294. RLMAssertThrowsWithReasonMatching(([class objectsWhere:fmt, value]),
  295. @"not supported for type");
  296. }
  297. }
  298. - (void)testInvalidOperatorsInNumericComparison {
  299. [self testStringOperatorsOnClass:[IntObject class] property:@"intCol" value:@0];
  300. [self testStringOperatorsOnClass:[FloatObject class] property:@"floatCol" value:@0];
  301. [self testStringOperatorsOnClass:[DoubleObject class] property:@"doubleCol" value:@0];
  302. [self testStringOperatorsOnClass:[DateObject class] property:@"dateCol" value:NSDate.date];
  303. }
  304. @end
  305. @interface QueryTests : RLMTestCase
  306. - (Class)queryObjectClass;
  307. @end
  308. @implementation QueryTests
  309. - (Class)queryObjectClass {
  310. return [QueryObject class];
  311. }
  312. - (RLMResults *)evaluate:(RLMResults *)results {
  313. return results;
  314. }
  315. - (RLMRealm *)realm {
  316. return [RLMRealm defaultRealm];
  317. }
  318. - (void)testBasicQuery
  319. {
  320. RLMRealm *realm = [self realm];
  321. [realm beginWriteTransaction];
  322. [PersonObject createInRealm:realm withValue:@[@"Fiel", @27]];
  323. [PersonObject createInRealm:realm withValue:@[@"Ari", @33]];
  324. [PersonObject createInRealm:realm withValue:@[@"Tim", @29]];
  325. [realm commitWriteTransaction];
  326. // query on realm
  327. RLMAssertCount(PersonObject, 2U, @"age > 28");
  328. // query on realm with order
  329. RLMResults *results = [[PersonObject objectsInRealm:realm where:@"age > 28"] sortedResultsUsingKeyPath:@"age" ascending:YES];
  330. XCTAssertEqualObjects([results[0] name], @"Tim", @"Tim should be first results");
  331. // query on sorted results
  332. results = [[[PersonObject allObjectsInRealm:realm] sortedResultsUsingKeyPath:@"age" ascending:YES] objectsWhere:@"age > 28"];
  333. XCTAssertEqualObjects([results[0] name], @"Tim", @"Tim should be first results");
  334. }
  335. -(void)testQueryBetween
  336. {
  337. RLMRealm *realm = [self realm];
  338. NSDate *date1 = [NSDate date];
  339. NSDate *date2 = [date1 dateByAddingTimeInterval:1];
  340. NSDate *date3 = [date2 dateByAddingTimeInterval:1];
  341. NSDate *date33 = [date3 dateByAddingTimeInterval:1];
  342. StringObject *stringObj = [StringObject new];
  343. stringObj.stringCol = @"string";
  344. [realm beginWriteTransaction];
  345. id a = [AllTypesObject createInRealm:realm withValue:@[@YES, @1, @1.0f, @1.0, @"a", [@"a" dataUsingEncoding:NSUTF8StringEncoding], date1, @YES, @((long)1), stringObj]];
  346. id b = [AllTypesObject createInRealm:realm withValue:@[@YES, @2, @2.0f, @2.0, @"b", [@"b" dataUsingEncoding:NSUTF8StringEncoding], date2, @YES, @((long)2), stringObj]];
  347. id c = [AllTypesObject createInRealm:realm withValue:@[@NO, @3, @3.0f, @3.0, @"c", [@"c" dataUsingEncoding:NSUTF8StringEncoding], date3, @YES, @((long)3), stringObj]];
  348. id d = [AllTypesObject createInRealm:realm withValue:@[@NO, @33, @3.3f, @3.3, @"cc", [@"cc" dataUsingEncoding:NSUTF8StringEncoding], date33, @NO, @((long)3.3), stringObj]];
  349. [ArrayOfAllTypesObject createInRealm:realm withValue:@[ @[ a, c] ]];
  350. [ArrayOfAllTypesObject createInRealm:realm withValue:@[ @[ b, d] ]];
  351. [realm commitWriteTransaction];
  352. RLMAssertCount(AllTypesObject, 2U, @"intCol BETWEEN %@", @[@2, @3]);
  353. RLMAssertCount(AllTypesObject, 4U, @"floatCol BETWEEN %@", @[@1.0f, @4.0f]);
  354. RLMAssertCount(AllTypesObject, 2U, @"doubleCol BETWEEN %@", @[@3.0, @7.0]);
  355. RLMAssertCount(AllTypesObject, 2U, @"dateCol BETWEEN %@", @[date2, date3]);
  356. RLMAssertCount(AllTypesObject, 2U, @"intCol BETWEEN {2, 3}");
  357. RLMAssertCount(AllTypesObject, 2U, @"doubleCol BETWEEN {3.0, 7.0}");
  358. RLMAssertCount(AllTypesObject.allObjects, 2U, @"intCol BETWEEN {2, 3}");
  359. RLMAssertCount(AllTypesObject.allObjects, 2U, @"doubleCol BETWEEN {3.0, 7.0}");
  360. RLMAssertCount(ArrayOfAllTypesObject, 1U, @"ANY array.intCol BETWEEN %@", @[@3, @5]);
  361. RLMAssertCount(ArrayOfAllTypesObject, 0U, @"ANY array.floatCol BETWEEN %@", @[@3.1, @3.2]);
  362. RLMAssertCount(ArrayOfAllTypesObject, 0U, @"ANY array.doubleCol BETWEEN %@", @[@3.1, @3.2]);
  363. }
  364. - (void)testQueryWithDates
  365. {
  366. RLMRealm *realm = [self realm];
  367. NSDate *date1 = [NSDate date];
  368. NSDate *date2 = [date1 dateByAddingTimeInterval:1];
  369. NSDate *date3 = [date2 dateByAddingTimeInterval:1];
  370. StringObject *stringObj = [StringObject new];
  371. stringObj.stringCol = @"string";
  372. [realm beginWriteTransaction];
  373. [AllTypesObject createInRealm:realm withValue:@[@YES, @1, @1.0f, @1.0, @"a", [@"a" dataUsingEncoding:NSUTF8StringEncoding], date1, @YES, @((long)1), stringObj]];
  374. [AllTypesObject createInRealm:realm withValue:@[@YES, @2, @2.0f, @2.0, @"b", [@"b" dataUsingEncoding:NSUTF8StringEncoding], date2, @YES, @((long)2), stringObj]];
  375. [AllTypesObject createInRealm:realm withValue:@[@NO, @3, @3.0f, @3.0, @"c", [@"c" dataUsingEncoding:NSUTF8StringEncoding], date3, @YES, @((long)3), stringObj]];
  376. [realm commitWriteTransaction];
  377. RLMAssertCount(AllTypesObject, 2U, @"dateCol < %@", date3);
  378. RLMAssertCount(AllTypesObject, 3U, @"dateCol <= %@", date3);
  379. RLMAssertCount(AllTypesObject, 2U, @"dateCol > %@", date1);
  380. RLMAssertCount(AllTypesObject, 3U, @"dateCol >= %@", date1);
  381. RLMAssertCount(AllTypesObject, 1U, @"dateCol == %@", date1);
  382. RLMAssertCount(AllTypesObject, 2U, @"dateCol != %@", date1);
  383. }
  384. - (void)testDefaultRealmQuery
  385. {
  386. RLMRealm *realm = [self realm];
  387. [realm beginWriteTransaction];
  388. [PersonObject createInRealm:realm withValue:@[@"Fiel", @27]];
  389. [PersonObject createInRealm:realm withValue:@[@"Tim", @29]];
  390. [PersonObject createInRealm:realm withValue:@[@"Ari", @33]];
  391. [realm commitWriteTransaction];
  392. // query on class
  393. XCTAssertEqual([PersonObject allObjects].count, 3U);
  394. RLMAssertCount(PersonObject, 1U, @"age == 27");
  395. // with order
  396. RLMResults *results = [[PersonObject objectsWhere:@"age > 28"] sortedResultsUsingKeyPath:@"age" ascending:YES];
  397. PersonObject *tim = results[0];
  398. XCTAssertEqualObjects(tim.name, @"Tim", @"Tim should be first results");
  399. }
  400. - (void)testArrayQuery
  401. {
  402. RLMRealm *realm = [self realm];
  403. [realm beginWriteTransaction];
  404. [PersonObject createInRealm:realm withValue:@[@"Fiel", @27]];
  405. [PersonObject createInRealm:realm withValue:@[@"Tim", @29]];
  406. [PersonObject createInRealm:realm withValue:@[@"Ari", @33]];
  407. [realm commitWriteTransaction];
  408. // query on class
  409. RLMResults *all = [PersonObject allObjects];
  410. XCTAssertEqual(all.count, 3U, @"Expecting 3 results");
  411. RLMResults *some = [[PersonObject objectsWhere:@"age > 28"] sortedResultsUsingKeyPath:@"age" ascending:YES];
  412. // query/order on array
  413. RLMAssertCount(all, 1U, @"age == 27");
  414. RLMAssertCount(all, 0U, @"age == 28");
  415. some = [some sortedResultsUsingKeyPath:@"age" ascending:NO];
  416. XCTAssertEqualObjects([some[0] name], @"Ari", @"Ari should be first results");
  417. }
  418. - (void)verifySort:(RLMRealm *)realm column:(NSString *)column ascending:(BOOL)ascending expected:(id)val {
  419. RLMResults *results = [[AllTypesObject allObjectsInRealm:realm] sortedResultsUsingKeyPath:column ascending:ascending];
  420. AllTypesObject *obj = results[0];
  421. XCTAssertEqualObjects(obj[column], val);
  422. RLMArray *ar = [(ArrayPropertyObject *)[[ArrayOfAllTypesObject allObjectsInRealm:realm] firstObject] array];
  423. results = [ar sortedResultsUsingKeyPath:column ascending:ascending];
  424. obj = results[0];
  425. XCTAssertEqualObjects(obj[column], val);
  426. }
  427. - (void)verifySortWithAccuracy:(RLMRealm *)realm column:(NSString *)column ascending:(BOOL)ascending getter:(double(^)(id))getter expected:(double)val accuracy:(double)accuracy {
  428. // test TableView query
  429. RLMResults<AllTypesObject *> *results = [[AllTypesObject allObjectsInRealm:realm]
  430. sortedResultsUsingKeyPath:column ascending:ascending];
  431. XCTAssertEqualWithAccuracy(getter(results[0][column]), val, accuracy, @"Array not sorted as expected");
  432. // test LinkView query
  433. RLMArray *ar = [(ArrayPropertyObject *)[[ArrayOfAllTypesObject allObjectsInRealm:realm] firstObject] array];
  434. results = [ar sortedResultsUsingKeyPath:column ascending:ascending];
  435. XCTAssertEqualWithAccuracy(getter(results[0][column]), val, accuracy, @"Array not sorted as expected");
  436. }
  437. - (void)testQuerySorting
  438. {
  439. RLMRealm *realm = [self realm];
  440. NSDate *date1 = [NSDate date];
  441. NSDate *date2 = [date1 dateByAddingTimeInterval:1];
  442. NSDate *date3 = [date2 dateByAddingTimeInterval:1];
  443. NSDate *date33 = [date3 dateByAddingTimeInterval:1];
  444. [realm beginWriteTransaction];
  445. ArrayOfAllTypesObject *arrayOfAll = [ArrayOfAllTypesObject createInRealm:realm withValue:@{}];
  446. StringObject *stringObj = [StringObject new];
  447. stringObj.stringCol = @"string";
  448. [arrayOfAll.array addObject:[AllTypesObject createInRealm:realm withValue:@[@YES, @1, @1.0f, @1.0, @"a", [@"a" dataUsingEncoding:NSUTF8StringEncoding], date1, @YES, @1, stringObj]]];
  449. [arrayOfAll.array addObject:[AllTypesObject createInRealm:realm withValue:@[@YES, @2, @2.0f, @2.0, @"b", [@"b" dataUsingEncoding:NSUTF8StringEncoding], date2, @YES, @2, stringObj]]];
  450. [arrayOfAll.array addObject:[AllTypesObject createInRealm:realm withValue:@[@NO, @3, @3.0f, @3.0, @"c", [@"c" dataUsingEncoding:NSUTF8StringEncoding], date3, @YES, @3, stringObj]]];
  451. [arrayOfAll.array addObject:[AllTypesObject createInRealm:realm withValue:@[@NO, @33, @3.3f, @3.3, @"cc", [@"cc" dataUsingEncoding:NSUTF8StringEncoding], date33, @NO, @3, stringObj]]];
  452. [realm commitWriteTransaction];
  453. //////////// sort by boolCol
  454. [self verifySort:realm column:@"boolCol" ascending:YES expected:@NO];
  455. [self verifySort:realm column:@"boolCol" ascending:NO expected:@YES];
  456. //////////// sort by intCol
  457. [self verifySort:realm column:@"intCol" ascending:YES expected:@1];
  458. [self verifySort:realm column:@"intCol" ascending:NO expected:@33];
  459. //////////// sort by dateCol
  460. double (^dateGetter)(id) = ^(NSDate *d) { return d.timeIntervalSince1970; };
  461. [self verifySortWithAccuracy:realm column:@"dateCol" ascending:YES getter:dateGetter expected:date1.timeIntervalSince1970 accuracy:1];
  462. [self verifySortWithAccuracy:realm column:@"dateCol" ascending:NO getter:dateGetter expected:date33.timeIntervalSince1970 accuracy:1];
  463. //////////// sort by doubleCol
  464. double (^doubleGetter)(id) = ^(NSNumber *n) { return n.doubleValue; };
  465. [self verifySortWithAccuracy:realm column:@"doubleCol" ascending:YES getter:doubleGetter expected:1.0 accuracy:0.0000001];
  466. [self verifySortWithAccuracy:realm column:@"doubleCol" ascending:NO getter:doubleGetter expected:3.3 accuracy:0.0000001];
  467. //////////// sort by floatCol
  468. [self verifySortWithAccuracy:realm column:@"floatCol" ascending:YES getter:doubleGetter expected:1.0 accuracy:0.0000001];
  469. [self verifySortWithAccuracy:realm column:@"floatCol" ascending:NO getter:doubleGetter expected:3.3 accuracy:0.0000001];
  470. //////////// sort by stringCol
  471. [self verifySort:realm column:@"stringCol" ascending:YES expected:@"a"];
  472. [self verifySort:realm column:@"stringCol" ascending:NO expected:@"cc"];
  473. // sort invalid name
  474. RLMAssertThrowsWithReason([[AllTypesObject allObjects] sortedResultsUsingKeyPath:@"invalidCol" ascending:YES],
  475. @"Cannot sort on key path 'invalidCol': property 'AllTypesObject.invalidCol' does not exist.");
  476. RLMAssertThrowsWithReason([arrayOfAll.array sortedResultsUsingKeyPath:@"invalidCol" ascending:NO],
  477. @"Cannot sort on key path 'invalidCol': property 'AllTypesObject.invalidCol' does not exist.");
  478. }
  479. - (void)testSortByNoColumns {
  480. RLMRealm *realm = [RLMRealm defaultRealm];
  481. [realm beginWriteTransaction];
  482. DogObject *a2 = [DogObject createInDefaultRealmWithValue:@[@"a", @2]];
  483. DogObject *b1 = [DogObject createInDefaultRealmWithValue:@[@"b", @1]];
  484. DogObject *a1 = [DogObject createInDefaultRealmWithValue:@[@"a", @1]];
  485. DogObject *b2 = [DogObject createInDefaultRealmWithValue:@[@"b", @2]];
  486. [realm commitWriteTransaction];
  487. RLMResults *notActuallySorted = [DogObject.allObjects sortedResultsUsingDescriptors:@[]];
  488. XCTAssertTrue([a2 isEqualToObject:notActuallySorted[0]]);
  489. XCTAssertTrue([b1 isEqualToObject:notActuallySorted[1]]);
  490. XCTAssertTrue([a1 isEqualToObject:notActuallySorted[2]]);
  491. XCTAssertTrue([b2 isEqualToObject:notActuallySorted[3]]);
  492. }
  493. - (void)testSortByMultipleColumns {
  494. RLMRealm *realm = [self realm];
  495. [realm beginWriteTransaction];
  496. DogObject *a1 = [DogObject createInDefaultRealmWithValue:@[@"a", @1]];
  497. DogObject *a2 = [DogObject createInDefaultRealmWithValue:@[@"a", @2]];
  498. DogObject *b1 = [DogObject createInDefaultRealmWithValue:@[@"b", @1]];
  499. DogObject *b2 = [DogObject createInDefaultRealmWithValue:@[@"b", @2]];
  500. [realm commitWriteTransaction];
  501. bool (^checkOrder)(NSArray *, NSArray *, NSArray *) = ^bool(NSArray *properties, NSArray *ascending, NSArray *dogs) {
  502. NSArray *sort = @[[RLMSortDescriptor sortDescriptorWithKeyPath:properties[0] ascending:[ascending[0] boolValue]],
  503. [RLMSortDescriptor sortDescriptorWithKeyPath:properties[1] ascending:[ascending[1] boolValue]]];
  504. RLMResults *actual = [DogObject.allObjects sortedResultsUsingDescriptors:sort];
  505. return [actual[0] isEqualToObject:dogs[0]]
  506. && [actual[1] isEqualToObject:dogs[1]]
  507. && [actual[2] isEqualToObject:dogs[2]]
  508. && [actual[3] isEqualToObject:dogs[3]];
  509. };
  510. // Check each valid sort
  511. XCTAssertTrue(checkOrder(@[@"dogName", @"age"], @[@YES, @YES], @[a1, a2, b1, b2]));
  512. XCTAssertTrue(checkOrder(@[@"dogName", @"age"], @[@YES, @NO], @[a2, a1, b2, b1]));
  513. XCTAssertTrue(checkOrder(@[@"dogName", @"age"], @[@NO, @YES], @[b1, b2, a1, a2]));
  514. XCTAssertTrue(checkOrder(@[@"dogName", @"age"], @[@NO, @NO], @[b2, b1, a2, a1]));
  515. XCTAssertTrue(checkOrder(@[@"age", @"dogName"], @[@YES, @YES], @[a1, b1, a2, b2]));
  516. XCTAssertTrue(checkOrder(@[@"age", @"dogName"], @[@YES, @NO], @[b1, a1, b2, a2]));
  517. XCTAssertTrue(checkOrder(@[@"age", @"dogName"], @[@NO, @YES], @[a2, b2, a1, b1]));
  518. XCTAssertTrue(checkOrder(@[@"age", @"dogName"], @[@NO, @NO], @[b2, a2, b1, a1]));
  519. }
  520. - (void)testSortByKeyPath {
  521. RLMRealm *realm = [self realm];
  522. [realm beginWriteTransaction];
  523. DogObject *lucy = [DogObject createInDefaultRealmWithValue:@[@"Lucy", @7]];
  524. DogObject *freyja = [DogObject createInDefaultRealmWithValue:@[@"Freyja", @6]];
  525. DogObject *ziggy = [DogObject createInDefaultRealmWithValue:@[@"Ziggy", @9]];
  526. OwnerObject *mark = [OwnerObject createInDefaultRealmWithValue:@[@"Mark", freyja]];
  527. OwnerObject *diane = [OwnerObject createInDefaultRealmWithValue:@[@"Diane", lucy]];
  528. OwnerObject *hannah = [OwnerObject createInDefaultRealmWithValue:@[@"Hannah"]];
  529. OwnerObject *don = [OwnerObject createInDefaultRealmWithValue:@[@"Don", ziggy]];
  530. OwnerObject *diane_sr = [OwnerObject createInDefaultRealmWithValue:@[@"Diane Sr", ziggy]];
  531. [realm commitWriteTransaction];
  532. NSArray *(^asArray)(RLMResults *) = ^(RLMResults *results) {
  533. return [[self evaluate:results] valueForKeyPath:@"self"];
  534. };
  535. RLMResults *r1 = [OwnerObject.allObjects sortedResultsUsingKeyPath:@"dog.age" ascending:YES];
  536. XCTAssertEqualObjects(asArray(r1), (@[ mark, diane, don, diane_sr, hannah ]));
  537. RLMResults *r2 = [OwnerObject.allObjects sortedResultsUsingKeyPath:@"dog.age" ascending:NO];
  538. XCTAssertEqualObjects(asArray(r2), (@[ hannah, don, diane_sr, diane, mark ]));
  539. RLMResults *r3 = [OwnerObject.allObjects sortedResultsUsingDescriptors:@[
  540. [RLMSortDescriptor sortDescriptorWithKeyPath:@"dog.age" ascending:YES],
  541. [RLMSortDescriptor sortDescriptorWithKeyPath:@"name" ascending:YES]
  542. ]];
  543. XCTAssertEqualObjects(asArray(r3), (@[ mark, diane, diane_sr, don, hannah ]));
  544. RLMResults *r4 = [OwnerObject.allObjects sortedResultsUsingDescriptors:@[
  545. [RLMSortDescriptor sortDescriptorWithKeyPath:@"dog.age" ascending:NO],
  546. [RLMSortDescriptor sortDescriptorWithKeyPath:@"name" ascending:YES]
  547. ]];
  548. XCTAssertEqualObjects(asArray(r4), (@[ hannah, diane_sr, don, diane, mark ]));
  549. }
  550. - (void)testSortByUnspportedKeyPath {
  551. // Array property
  552. RLMAssertThrowsWithReason([DogArrayObject.allObjects sortedResultsUsingKeyPath:@"dogs.age" ascending:YES],
  553. @"Cannot sort on key path 'dogs.age': property 'DogArrayObject.dogs' is of unsupported type 'array'.");
  554. // Backlinks property
  555. RLMAssertThrowsWithReason([DogObject.allObjects sortedResultsUsingKeyPath:@"owners.name" ascending:YES],
  556. @"Cannot sort on key path 'owners.name': property 'DogObject.owners' is of unsupported type 'linking objects'.");
  557. // Collection operator
  558. RLMAssertThrowsWithReason([DogArrayObject.allObjects sortedResultsUsingKeyPath:@"dogs.@count" ascending:YES],
  559. @"Cannot sort on key path 'dogs.@count': KVC collection operators are not supported.");
  560. }
  561. - (void)testSortedLinkViewWithDeletion {
  562. RLMRealm *realm = [self realm];
  563. NSDate *date1 = [NSDate date];
  564. NSDate *date2 = [date1 dateByAddingTimeInterval:1];
  565. NSDate *date3 = [date2 dateByAddingTimeInterval:1];
  566. NSDate *date33 = [date3 dateByAddingTimeInterval:1];
  567. [realm beginWriteTransaction];
  568. ArrayOfAllTypesObject *arrayOfAll = [ArrayOfAllTypesObject createInRealm:realm withValue:@{}];
  569. StringObject *stringObj = [StringObject new];
  570. stringObj.stringCol = @"string";
  571. [arrayOfAll.array addObject:[AllTypesObject createInRealm:realm withValue:@[@YES, @1, @1.0f, @1.0, @"a", [@"a" dataUsingEncoding:NSUTF8StringEncoding], date1, @YES, @1, stringObj]]];
  572. [arrayOfAll.array addObject:[AllTypesObject createInRealm:realm withValue:@[@YES, @2, @2.0f, @2.0, @"b", [@"b" dataUsingEncoding:NSUTF8StringEncoding], date2, @YES, @2, stringObj]]];
  573. [arrayOfAll.array addObject:[AllTypesObject createInRealm:realm withValue:@[@NO, @3, @3.0f, @3.0, @"c", [@"c" dataUsingEncoding:NSUTF8StringEncoding], date3, @YES, @3, stringObj]]];
  574. [arrayOfAll.array addObject:[AllTypesObject createInRealm:realm withValue:@[@NO, @33, @3.3f, @3.3, @"cc", [@"cc" dataUsingEncoding:NSUTF8StringEncoding], date33, @NO, @3, stringObj]]];
  575. [realm commitWriteTransaction];
  576. RLMResults *results = [arrayOfAll.array sortedResultsUsingKeyPath:@"stringCol" ascending:NO];
  577. XCTAssertEqualObjects([results[0] stringCol], @"cc");
  578. // delete cc, add d results should update
  579. [realm transactionWithBlock:^{
  580. [arrayOfAll.array removeObjectAtIndex:3];
  581. // create extra alltypesobject
  582. [arrayOfAll.array addObject:[AllTypesObject createInRealm:realm withValue:@[@YES, @1, @1.0f, @1.0, @"d", [@"d" dataUsingEncoding:NSUTF8StringEncoding], date1, @YES, @((long)1), stringObj]]];
  583. }];
  584. XCTAssertEqualObjects([results[0] stringCol], @"d");
  585. XCTAssertEqualObjects([results[1] stringCol], @"c");
  586. // delete from realm should be removed from results
  587. [realm transactionWithBlock:^{
  588. [realm deleteObject:arrayOfAll.array.lastObject];
  589. }];
  590. XCTAssertEqualObjects([results[0] stringCol], @"c");
  591. }
  592. - (void)testQueryingSortedQueryPreservesOrder {
  593. RLMRealm *realm = [self realm];
  594. [realm beginWriteTransaction];
  595. for (int i = 0; i < 5; ++i) {
  596. [IntObject createInRealm:realm withValue:@[@(i)]];
  597. }
  598. ArrayPropertyObject *array = [ArrayPropertyObject createInRealm:realm withValue:@[@"name", @[], [IntObject allObjects]]];
  599. [realm commitWriteTransaction];
  600. RLMResults *asc = [IntObject.allObjects sortedResultsUsingKeyPath:@"intCol" ascending:YES];
  601. RLMResults *desc = [IntObject.allObjects sortedResultsUsingKeyPath:@"intCol" ascending:NO];
  602. // sanity check; would work even without sort order being preserved
  603. XCTAssertEqual(2, [[[asc objectsWhere:@"intCol >= 2"] firstObject] intCol]);
  604. // check query on allObjects and query on query
  605. XCTAssertEqual(4, [[[desc objectsWhere:@"intCol >= 2"] firstObject] intCol]);
  606. XCTAssertEqual(3, [[[[desc objectsWhere:@"intCol >= 2"] objectsWhere:@"intCol < 4"] firstObject] intCol]);
  607. // same thing but on an linkview
  608. asc = [array.intArray sortedResultsUsingKeyPath:@"intCol" ascending:YES];
  609. desc = [array.intArray sortedResultsUsingKeyPath:@"intCol" ascending:NO];
  610. XCTAssertEqual(2, [[[asc objectsWhere:@"intCol >= 2"] firstObject] intCol]);
  611. XCTAssertEqual(4, [[[desc objectsWhere:@"intCol >= 2"] firstObject] intCol]);
  612. XCTAssertEqual(3, [[[[desc objectsWhere:@"intCol >= 2"] objectsWhere:@"intCol < 4"] firstObject] intCol]);
  613. }
  614. - (void)testTwoColumnComparison
  615. {
  616. RLMRealm *realm = [self realm];
  617. [realm beginWriteTransaction];
  618. [self.queryObjectClass createInRealm:realm withValue:@[@YES, @YES, @1, @2, @23.0f, @1.7f, @0.0, @5.55, @"a", @"a"]];
  619. [self.queryObjectClass createInRealm:realm withValue:@[@YES, @NO, @1, @3, @-5.3f, @4.21f, @1.0, @4.44, @"a", @"A"]];
  620. [self.queryObjectClass createInRealm:realm withValue:@[@NO, @NO, @2, @2, @1.0f, @3.55f, @99.9, @6.66, @"a", @"ab"]];
  621. [self.queryObjectClass createInRealm:realm withValue:@[@NO, @YES, @3, @6, @4.21f, @1.0f, @1.0, @7.77, @"a", @"AB"]];
  622. [self.queryObjectClass createInRealm:realm withValue:@[@YES, @YES, @4, @5, @23.0f, @23.0f, @7.4, @8.88, @"a", @"b"]];
  623. [self.queryObjectClass createInRealm:realm withValue:@[@YES, @NO, @15, @8, @1.0f, @66.0f, @1.01, @9.99, @"a", @"ba"]];
  624. [self.queryObjectClass createInRealm:realm withValue:@[@NO, @YES, @15, @15, @1.0f, @66.0f, @1.01, @9.99, @"a", @"BA"]];
  625. [realm commitWriteTransaction];
  626. RLMAssertCount(self.queryObjectClass, 7U, @"bool1 == bool1");
  627. RLMAssertCount(self.queryObjectClass, 3U, @"bool1 == bool2");
  628. RLMAssertCount(self.queryObjectClass, 4U, @"bool1 != bool2");
  629. RLMAssertCount(self.queryObjectClass, 7U, @"int1 == int1");
  630. RLMAssertCount(self.queryObjectClass, 2U, @"int1 == int2");
  631. RLMAssertCount(self.queryObjectClass, 5U, @"int1 != int2");
  632. RLMAssertCount(self.queryObjectClass, 1U, @"int1 > int2");
  633. RLMAssertCount(self.queryObjectClass, 4U, @"int1 < int2");
  634. RLMAssertCount(self.queryObjectClass, 3U, @"int1 >= int2");
  635. RLMAssertCount(self.queryObjectClass, 6U, @"int1 <= int2");
  636. RLMAssertCount(self.queryObjectClass, 7U, @"float1 == float1");
  637. RLMAssertCount(self.queryObjectClass, 1U, @"float1 == float2");
  638. RLMAssertCount(self.queryObjectClass, 6U, @"float1 != float2");
  639. RLMAssertCount(self.queryObjectClass, 2U, @"float1 > float2");
  640. RLMAssertCount(self.queryObjectClass, 4U, @"float1 < float2");
  641. RLMAssertCount(self.queryObjectClass, 3U, @"float1 >= float2");
  642. RLMAssertCount(self.queryObjectClass, 5U, @"float1 <= float2");
  643. RLMAssertCount(self.queryObjectClass, 7U, @"double1 == double1");
  644. RLMAssertCount(self.queryObjectClass, 0U, @"double1 == double2");
  645. RLMAssertCount(self.queryObjectClass, 7U, @"double1 != double2");
  646. RLMAssertCount(self.queryObjectClass, 1U, @"double1 > double2");
  647. RLMAssertCount(self.queryObjectClass, 6U, @"double1 < double2");
  648. RLMAssertCount(self.queryObjectClass, 1U, @"double1 >= double2");
  649. RLMAssertCount(self.queryObjectClass, 6U, @"double1 <= double2");
  650. RLMAssertCount(self.queryObjectClass, 7U, @"string1 == string1");
  651. RLMAssertCount(self.queryObjectClass, 1U, @"string1 == string2");
  652. RLMAssertCount(self.queryObjectClass, 6U, @"string1 != string2");
  653. RLMAssertCount(self.queryObjectClass, 7U, @"string1 CONTAINS string1");
  654. RLMAssertCount(self.queryObjectClass, 1U, @"string1 CONTAINS string2");
  655. RLMAssertCount(self.queryObjectClass, 3U, @"string2 CONTAINS string1");
  656. RLMAssertCount(self.queryObjectClass, 7U, @"string1 BEGINSWITH string1");
  657. RLMAssertCount(self.queryObjectClass, 1U, @"string1 BEGINSWITH string2");
  658. RLMAssertCount(self.queryObjectClass, 2U, @"string2 BEGINSWITH string1");
  659. RLMAssertCount(self.queryObjectClass, 7U, @"string1 ENDSWITH string1");
  660. RLMAssertCount(self.queryObjectClass, 1U, @"string1 ENDSWITH string2");
  661. RLMAssertCount(self.queryObjectClass, 2U, @"string2 ENDSWITH string1");
  662. RLMAssertCount(self.queryObjectClass, 7U, @"string1 LIKE string1");
  663. RLMAssertCount(self.queryObjectClass, 1U, @"string1 LIKE string2");
  664. RLMAssertCount(self.queryObjectClass, 1U, @"string2 LIKE string1");
  665. RLMAssertCount(self.queryObjectClass, 7U, @"string1 ==[c] string1");
  666. RLMAssertCount(self.queryObjectClass, 2U, @"string1 ==[c] string2");
  667. RLMAssertCount(self.queryObjectClass, 5U, @"string1 !=[c] string2");
  668. RLMAssertCount(self.queryObjectClass, 7U, @"string1 CONTAINS[c] string1");
  669. RLMAssertCount(self.queryObjectClass, 2U, @"string1 CONTAINS[c] string2");
  670. RLMAssertCount(self.queryObjectClass, 6U, @"string2 CONTAINS[c] string1");
  671. RLMAssertCount(self.queryObjectClass, 7U, @"string1 BEGINSWITH[c] string1");
  672. RLMAssertCount(self.queryObjectClass, 2U, @"string1 BEGINSWITH[c] string2");
  673. RLMAssertCount(self.queryObjectClass, 4U, @"string2 BEGINSWITH[c] string1");
  674. RLMAssertCount(self.queryObjectClass, 7U, @"string1 ENDSWITH[c] string1");
  675. RLMAssertCount(self.queryObjectClass, 2U, @"string1 ENDSWITH[c] string2");
  676. RLMAssertCount(self.queryObjectClass, 4U, @"string2 ENDSWITH[c] string1");
  677. RLMAssertCount(self.queryObjectClass, 7U, @"string1 LIKE[c] string1");
  678. RLMAssertCount(self.queryObjectClass, 2U, @"string1 LIKE[c] string2");
  679. RLMAssertCount(self.queryObjectClass, 2U, @"string2 LIKE[c] string1");
  680. RLMAssertThrowsWithReasonMatching([self.queryObjectClass objectsWhere:@"int1 == float1"],
  681. @"Property type mismatch between int and float");
  682. RLMAssertThrowsWithReasonMatching([self.queryObjectClass objectsWhere:@"float2 >= double1"],
  683. @"Property type mismatch between float and double");
  684. RLMAssertThrowsWithReasonMatching([self.queryObjectClass objectsWhere:@"double2 <= int2"],
  685. @"Property type mismatch between double and int");
  686. RLMAssertThrowsWithReasonMatching([self.queryObjectClass objectsWhere:@"int2 != string1"],
  687. @"Property type mismatch between int and string");
  688. RLMAssertThrowsWithReasonMatching([self.queryObjectClass objectsWhere:@"float1 > string1"],
  689. @"Property type mismatch between float and string");
  690. RLMAssertThrowsWithReasonMatching([self.queryObjectClass objectsWhere:@"double1 < string1"],
  691. @"Property type mismatch between double and string");
  692. RLMAssertThrowsWithReasonMatching([self.queryObjectClass objectsWhere:@"double1 LIKE string1"],
  693. @"Property type mismatch between double and string");
  694. RLMAssertThrowsWithReasonMatching([self.queryObjectClass objectsWhere:@"string1 LIKE double1"],
  695. @"Property type mismatch between string and double");
  696. }
  697. - (void)testBooleanPredicate
  698. {
  699. RLMAssertCount(BoolObject, 0U, @"boolCol == TRUE");
  700. RLMAssertCount(BoolObject, 0U, @"boolCol != TRUE");
  701. XCTAssertThrows([BoolObject objectsWhere:@"boolCol == NULL"]);
  702. XCTAssertThrows([BoolObject objectsWhere:@"boolCol != NULL"]);
  703. XCTAssertThrowsSpecificNamed([BoolObject objectsWhere:@"boolCol >= TRUE"],
  704. NSException,
  705. @"Invalid operator type",
  706. @"Invalid operator in bool predicate.");
  707. }
  708. - (void)testStringBeginsWith
  709. {
  710. RLMRealm *realm = [self realm];
  711. [realm beginWriteTransaction];
  712. StringObject *so = [StringObject createInRealm:realm withValue:@[@"abc"]];
  713. [StringObject createInRealm:realm withValue:@[@"üvw"]];
  714. [StringObject createInRealm:realm withValue:@[@"ûvw"]];
  715. [StringObject createInRealm:realm withValue:@[@"uvw"]];
  716. [StringObject createInRealm:realm withValue:@[@"stü"]];
  717. [AllTypesObject createInRealm:realm withValue:@[@YES, @1, @1.0f, @1.0, @"a", [@"a" dataUsingEncoding:NSUTF8StringEncoding], NSDate.date, @YES, @1LL, so]];
  718. [realm commitWriteTransaction];
  719. RLMAssertCount(StringObject, 1U, @"stringCol BEGINSWITH 'a'");
  720. RLMAssertCount(StringObject, 1U, @"stringCol BEGINSWITH 'ab'");
  721. RLMAssertCount(StringObject, 1U, @"stringCol BEGINSWITH 'abc'");
  722. RLMAssertCount(StringObject, 0U, @"stringCol BEGINSWITH 'abcd'");
  723. RLMAssertCount(StringObject, 0U, @"stringCol BEGINSWITH 'abd'");
  724. RLMAssertCount(StringObject, 0U, @"stringCol BEGINSWITH 'c'");
  725. RLMAssertCount(StringObject, 0U, @"stringCol BEGINSWITH 'A'");
  726. RLMAssertCount(StringObject, 0U, @"stringCol BEGINSWITH ''");
  727. RLMAssertCount(StringObject, 1U, @"stringCol BEGINSWITH[c] 'a'");
  728. RLMAssertCount(StringObject, 1U, @"stringCol BEGINSWITH[c] 'A'");
  729. RLMAssertCount(StringObject, 0U, @"stringCol BEGINSWITH[c] ''");
  730. RLMAssertCount(StringObject, 0U, @"stringCol BEGINSWITH[d] ''");
  731. RLMAssertCount(StringObject, 0U, @"stringCol BEGINSWITH[cd] ''");
  732. RLMAssertCount(StringObject, 1U, @"stringCol BEGINSWITH 'u'");
  733. RLMAssertCount(StringObject, 1U, @"stringCol BEGINSWITH[c] 'U'");
  734. RLMAssertCount(StringObject, 3U, @"stringCol BEGINSWITH[d] 'u'");
  735. RLMAssertCount(StringObject, 3U, @"stringCol BEGINSWITH[cd] 'U'");
  736. RLMAssertCount(StringObject, 1U, @"stringCol BEGINSWITH 'ü'");
  737. RLMAssertCount(StringObject, 0U, @"stringCol BEGINSWITH[c] 'Ü'");
  738. RLMAssertCount(StringObject, 3U, @"stringCol BEGINSWITH[d] 'ü'");
  739. RLMAssertCount(StringObject, 3U, @"stringCol BEGINSWITH[cd] 'Ü'");
  740. RLMAssertCount(StringObject, 0U, @"stringCol BEGINSWITH NULL");
  741. RLMAssertCount(StringObject, 0U, @"stringCol BEGINSWITH[c] NULL");
  742. RLMAssertCount(StringObject, 0U, @"stringCol BEGINSWITH[d] NULL");
  743. RLMAssertCount(StringObject, 0U, @"stringCol BEGINSWITH[cd] NULL");
  744. RLMAssertCount(AllTypesObject, 1U, @"objectCol.stringCol BEGINSWITH 'a'");
  745. RLMAssertCount(AllTypesObject, 0U, @"objectCol.stringCol BEGINSWITH 'c'");
  746. RLMAssertCount(AllTypesObject, 0U, @"objectCol.stringCol BEGINSWITH 'A'");
  747. RLMAssertCount(AllTypesObject, 0U, @"objectCol.stringCol BEGINSWITH ''");
  748. RLMAssertCount(AllTypesObject, 1U, @"objectCol.stringCol BEGINSWITH[c] 'a'");
  749. RLMAssertCount(AllTypesObject, 1U, @"objectCol.stringCol BEGINSWITH[c] 'A'");
  750. RLMAssertCount(AllTypesObject, 0U, @"objectCol.stringCol BEGINSWITH[c] ''");
  751. RLMAssertCount(AllTypesObject, 0U, @"objectCol.stringCol BEGINSWITH[d] ''");
  752. RLMAssertCount(AllTypesObject, 0U, @"objectCol.stringCol BEGINSWITH[cd] ''");
  753. RLMAssertCount(AllTypesObject, 0U, @"objectCol.stringCol BEGINSWITH NULL");
  754. RLMAssertCount(AllTypesObject, 0U, @"objectCol.stringCol BEGINSWITH[c] NULL");
  755. RLMAssertCount(AllTypesObject, 0U, @"objectCol.stringCol BEGINSWITH[d] NULL");
  756. RLMAssertCount(AllTypesObject, 0U, @"objectCol.stringCol BEGINSWITH[cd] NULL");
  757. }
  758. - (void)testStringEndsWith
  759. {
  760. RLMRealm *realm = [self realm];
  761. [realm beginWriteTransaction];
  762. StringObject *so = [StringObject createInRealm:realm withValue:@[@"abc"]];
  763. [StringObject createInRealm:realm withValue:@[@"üvw"]];
  764. [StringObject createInRealm:realm withValue:@[@"stü"]];
  765. [StringObject createInRealm:realm withValue:@[@"stú"]];
  766. [StringObject createInRealm:realm withValue:@[@"stu"]];
  767. [AllTypesObject createInRealm:realm withValue:@[@YES, @1, @1.0f, @1.0, @"a", [@"a" dataUsingEncoding:NSUTF8StringEncoding], NSDate.date, @YES, @1LL, so]];
  768. [realm commitWriteTransaction];
  769. RLMAssertCount(StringObject, 1U, @"stringCol ENDSWITH 'c'");
  770. RLMAssertCount(StringObject, 1U, @"stringCol ENDSWITH 'bc'");
  771. RLMAssertCount(StringObject, 1U, @"stringCol ENDSWITH 'abc'");
  772. RLMAssertCount(StringObject, 0U, @"stringCol ENDSWITH 'aabc'");
  773. RLMAssertCount(StringObject, 0U, @"stringCol ENDSWITH 'bbc'");
  774. RLMAssertCount(StringObject, 0U, @"stringCol ENDSWITH 'a'");
  775. RLMAssertCount(StringObject, 0U, @"stringCol ENDSWITH 'C'");
  776. RLMAssertCount(StringObject, 0U, @"stringCol ENDSWITH ''");
  777. RLMAssertCount(StringObject, 1U, @"stringCol ENDSWITH[c] 'c'");
  778. RLMAssertCount(StringObject, 1U, @"stringCol ENDSWITH[c] 'C'");
  779. RLMAssertCount(StringObject, 0U, @"stringCol ENDSWITH[c] ''");
  780. RLMAssertCount(StringObject, 0U, @"stringCol ENDSWITH[d] ''");
  781. RLMAssertCount(StringObject, 0U, @"stringCol ENDSWITH[cd] ''");
  782. RLMAssertCount(StringObject, 1U, @"stringCol ENDSWITH 'u'");
  783. RLMAssertCount(StringObject, 1U, @"stringCol ENDSWITH[c] 'U'");
  784. RLMAssertCount(StringObject, 3U, @"stringCol ENDSWITH[d] 'u'");
  785. RLMAssertCount(StringObject, 3U, @"stringCol ENDSWITH[cd] 'U'");
  786. RLMAssertCount(StringObject, 1U, @"stringCol ENDSWITH 'ü'");
  787. RLMAssertCount(StringObject, 0U, @"stringCol ENDSWITH[c] 'Ü'");
  788. RLMAssertCount(StringObject, 3U, @"stringCol ENDSWITH[d] 'ü'");
  789. RLMAssertCount(StringObject, 3U, @"stringCol ENDSWITH[cd] 'Ü'");
  790. RLMAssertCount(StringObject, 0U, @"stringCol ENDSWITH NULL");
  791. RLMAssertCount(StringObject, 0U, @"stringCol ENDSWITH[c] NULL");
  792. RLMAssertCount(StringObject, 0U, @"stringCol ENDSWITH[d] NULL");
  793. RLMAssertCount(StringObject, 0U, @"stringCol ENDSWITH[cd] NULL");
  794. RLMAssertCount(AllTypesObject, 1U, @"objectCol.stringCol ENDSWITH 'c'");
  795. RLMAssertCount(AllTypesObject, 0U, @"objectCol.stringCol ENDSWITH 'a'");
  796. RLMAssertCount(AllTypesObject, 0U, @"objectCol.stringCol ENDSWITH 'C'");
  797. RLMAssertCount(AllTypesObject, 0U, @"objectCol.stringCol ENDSWITH ''");
  798. RLMAssertCount(AllTypesObject, 1U, @"objectCol.stringCol ENDSWITH[c] 'c'");
  799. RLMAssertCount(AllTypesObject, 1U, @"objectCol.stringCol ENDSWITH[c] 'C'");
  800. RLMAssertCount(AllTypesObject, 0U, @"objectCol.stringCol ENDSWITH[c] ''");
  801. RLMAssertCount(AllTypesObject, 0U, @"objectCol.stringCol ENDSWITH[d] ''");
  802. RLMAssertCount(AllTypesObject, 0U, @"objectCol.stringCol ENDSWITH[cd] ''");
  803. RLMAssertCount(AllTypesObject, 0U, @"objectCol.stringCol ENDSWITH NULL");
  804. RLMAssertCount(AllTypesObject, 0U, @"objectCol.stringCol ENDSWITH[c] NULL");
  805. RLMAssertCount(AllTypesObject, 0U, @"objectCol.stringCol ENDSWITH[d] NULL");
  806. RLMAssertCount(AllTypesObject, 0U, @"objectCol.stringCol ENDSWITH[cd] NULL");
  807. }
  808. - (void)testStringContains
  809. {
  810. RLMRealm *realm = [self realm];
  811. [realm beginWriteTransaction];
  812. StringObject *so = [StringObject createInRealm:realm withValue:@[@"abc"]];
  813. [StringObject createInRealm:realm withValue:@[@"tüv"]];
  814. [StringObject createInRealm:realm withValue:@[@"tûv"]];
  815. [StringObject createInRealm:realm withValue:@[@"tuv"]];
  816. [AllTypesObject createInRealm:realm withValue:@[@YES, @1, @1.0f, @1.0, @"a", [@"a" dataUsingEncoding:NSUTF8StringEncoding], NSDate.date, @YES, @1LL, so]];
  817. [realm commitWriteTransaction];
  818. RLMAssertCount(StringObject, 1U, @"stringCol CONTAINS 'a'");
  819. RLMAssertCount(StringObject, 1U, @"stringCol CONTAINS 'b'");
  820. RLMAssertCount(StringObject, 1U, @"stringCol CONTAINS 'c'");
  821. RLMAssertCount(StringObject, 1U, @"stringCol CONTAINS 'ab'");
  822. RLMAssertCount(StringObject, 1U, @"stringCol CONTAINS 'bc'");
  823. RLMAssertCount(StringObject, 1U, @"stringCol CONTAINS 'abc'");
  824. RLMAssertCount(StringObject, 0U, @"stringCol CONTAINS 'd'");
  825. RLMAssertCount(StringObject, 0U, @"stringCol CONTAINS 'aabc'");
  826. RLMAssertCount(StringObject, 0U, @"stringCol CONTAINS 'bbc'");
  827. RLMAssertCount(StringObject, 0U, @"stringCol CONTAINS ''");
  828. RLMAssertCount(StringObject, 0U, @"stringCol CONTAINS 'C'");
  829. RLMAssertCount(StringObject, 1U, @"stringCol CONTAINS[c] 'c'");
  830. RLMAssertCount(StringObject, 1U, @"stringCol CONTAINS[c] 'C'");
  831. RLMAssertCount(StringObject, 0U, @"stringCol CONTAINS[c] ''");
  832. RLMAssertCount(StringObject, 1U, @"stringCol CONTAINS 'u'");
  833. RLMAssertCount(StringObject, 1U, @"stringCol CONTAINS[c] 'U'");
  834. RLMAssertCount(StringObject, 3U, @"stringCol CONTAINS[d] 'u'");
  835. RLMAssertCount(StringObject, 3U, @"stringCol CONTAINS[cd] 'U'");
  836. RLMAssertCount(StringObject, 0U, @"stringCol CONTAINS[d] ''");
  837. RLMAssertCount(StringObject, 0U, @"stringCol CONTAINS[cd] ''");
  838. RLMAssertCount(StringObject, 1U, @"stringCol CONTAINS 'ü'");
  839. RLMAssertCount(StringObject, 0U, @"stringCol CONTAINS[c] 'Ü'");
  840. RLMAssertCount(StringObject, 3U, @"stringCol CONTAINS[d] 'ü'");
  841. RLMAssertCount(StringObject, 3U, @"stringCol CONTAINS[cd] 'Ü'");
  842. RLMAssertCount(StringObject, 0U, @"stringCol CONTAINS NULL");
  843. RLMAssertCount(StringObject, 0U, @"stringCol CONTAINS[c] NULL");
  844. RLMAssertCount(StringObject, 0U, @"stringCol CONTAINS[d] NULL");
  845. RLMAssertCount(StringObject, 0U, @"stringCol CONTAINS[cd] NULL");
  846. RLMAssertCount(AllTypesObject, 0U, @"objectCol.stringCol CONTAINS 'd'");
  847. RLMAssertCount(AllTypesObject, 1U, @"objectCol.stringCol CONTAINS 'c'");
  848. RLMAssertCount(AllTypesObject, 0U, @"objectCol.stringCol CONTAINS 'C'");
  849. RLMAssertCount(AllTypesObject, 0U, @"objectCol.stringCol CONTAINS ''");
  850. RLMAssertCount(AllTypesObject, 1U, @"objectCol.stringCol CONTAINS[c] 'c'");
  851. RLMAssertCount(AllTypesObject, 1U, @"objectCol.stringCol CONTAINS[c] 'C'");
  852. RLMAssertCount(AllTypesObject, 0U, @"objectCol.stringCol CONTAINS[c] ''");
  853. RLMAssertCount(AllTypesObject, 0U, @"objectCol.stringCol CONTAINS[d] ''");
  854. RLMAssertCount(AllTypesObject, 0U, @"objectCol.stringCol CONTAINS[cd] ''");
  855. RLMAssertCount(AllTypesObject, 0U, @"objectCol.stringCol CONTAINS NULL");
  856. RLMAssertCount(AllTypesObject, 0U, @"objectCol.stringCol CONTAINS[c] NULL");
  857. RLMAssertCount(AllTypesObject, 0U, @"objectCol.stringCol CONTAINS[d] NULL");
  858. RLMAssertCount(AllTypesObject, 0U, @"objectCol.stringCol CONTAINS[cd] NULL");
  859. }
  860. - (void)testStringLike
  861. {
  862. RLMRealm *realm = [self realm];
  863. [realm beginWriteTransaction];
  864. StringObject *so = [StringObject createInRealm:realm withValue:(@[@"abc"])];
  865. [AllTypesObject createInRealm:realm withValue:@[@YES, @1, @1.0f, @1.0, @"a", [@"a" dataUsingEncoding:NSUTF8StringEncoding], NSDate.date, @YES, @1LL, so]];
  866. [realm commitWriteTransaction];
  867. RLMAssertCount(StringObject, 1U, @"stringCol LIKE '*a*'");
  868. RLMAssertCount(StringObject, 1U, @"stringCol LIKE '*b*'");
  869. RLMAssertCount(StringObject, 1U, @"stringCol LIKE '*c'");
  870. RLMAssertCount(StringObject, 1U, @"stringCol LIKE 'ab*'");
  871. RLMAssertCount(StringObject, 1U, @"stringCol LIKE '*bc'");
  872. RLMAssertCount(StringObject, 1U, @"stringCol LIKE 'a*bc'");
  873. RLMAssertCount(StringObject, 1U, @"stringCol LIKE '*abc*'");
  874. RLMAssertCount(StringObject, 0U, @"stringCol LIKE '*d*'");
  875. RLMAssertCount(StringObject, 0U, @"stringCol LIKE 'aabc'");
  876. RLMAssertCount(StringObject, 0U, @"stringCol LIKE 'b*bc'");
  877. RLMAssertCount(StringObject, 1U, @"stringCol LIKE 'a?" "?'");
  878. RLMAssertCount(StringObject, 1U, @"stringCol LIKE '?b?'");
  879. RLMAssertCount(StringObject, 1U, @"stringCol LIKE '*?c'");
  880. RLMAssertCount(StringObject, 1U, @"stringCol LIKE 'ab?'");
  881. RLMAssertCount(StringObject, 1U, @"stringCol LIKE '?bc'");
  882. RLMAssertCount(StringObject, 0U, @"stringCol LIKE '?d?'");
  883. RLMAssertCount(StringObject, 0U, @"stringCol LIKE '?abc'");
  884. RLMAssertCount(StringObject, 0U, @"stringCol LIKE 'b?bc'");
  885. RLMAssertCount(StringObject, 0U, @"stringCol LIKE '*C*'");
  886. RLMAssertCount(StringObject, 1U, @"stringCol LIKE[c] '*c*'");
  887. RLMAssertCount(StringObject, 1U, @"stringCol LIKE[c] '*C*'");
  888. RLMAssertCount(AllTypesObject, 0U, @"objectCol.stringCol LIKE '*d*'");
  889. RLMAssertCount(AllTypesObject, 1U, @"objectCol.stringCol LIKE '*c*'");
  890. RLMAssertCount(AllTypesObject, 0U, @"objectCol.stringCol LIKE '*C*'");
  891. RLMAssertCount(AllTypesObject, 1U, @"objectCol.stringCol LIKE[c] '*c*'");
  892. RLMAssertCount(AllTypesObject, 1U, @"objectCol.stringCol LIKE[c] '*C*'");
  893. RLMAssertThrowsWithReasonMatching([StringObject objectsWhere:@"stringCol LIKE[d] '*'"], @"'LIKE' not supported .* diacritic-insensitive");
  894. RLMAssertThrowsWithReasonMatching([StringObject objectsWhere:@"stringCol LIKE[cd] '*'"], @"'LIKE' not supported .* diacritic-insensitive");
  895. }
  896. - (void)testStringEquality
  897. {
  898. RLMRealm *realm = [self realm];
  899. [realm beginWriteTransaction];
  900. StringObject *so = [StringObject createInRealm:realm withValue:(@[@"abc"])];
  901. [StringObject createInRealm:realm withValue:@[@"tüv"]];
  902. [StringObject createInRealm:realm withValue:@[@"tûv"]];
  903. [StringObject createInRealm:realm withValue:@[@"tuv"]];
  904. [AllTypesObject createInRealm:realm withValue:@[@YES, @1, @1.0f, @1.0, @"a", [@"a" dataUsingEncoding:NSUTF8StringEncoding], NSDate.date, @YES, @1LL, so]];
  905. [realm commitWriteTransaction];
  906. RLMAssertCount(StringObject, 1U, @"stringCol == 'abc'");
  907. RLMAssertCount(StringObject, 4U, @"stringCol != 'def'");
  908. RLMAssertCount(StringObject, 1U, @"stringCol ==[c] 'abc'");
  909. RLMAssertCount(StringObject, 1U, @"stringCol ==[c] 'ABC'");
  910. RLMAssertCount(StringObject, 3U, @"stringCol != 'abc'");
  911. RLMAssertCount(StringObject, 0U, @"stringCol == 'def'");
  912. RLMAssertCount(StringObject, 0U, @"stringCol == 'ABC'");
  913. RLMAssertCount(StringObject, 1U, @"stringCol == 'tuv'");
  914. RLMAssertCount(StringObject, 1U, @"stringCol ==[c] 'TUV'");
  915. RLMAssertCount(StringObject, 3U, @"stringCol ==[d] 'tuv'");
  916. RLMAssertCount(StringObject, 3U, @"stringCol ==[cd] 'TUV'");
  917. RLMAssertCount(StringObject, 3U, @"stringCol != 'tuv'");
  918. RLMAssertCount(StringObject, 3U, @"stringCol !=[c] 'TUV'");
  919. RLMAssertCount(StringObject, 1U, @"stringCol !=[d] 'tuv'");
  920. RLMAssertCount(StringObject, 1U, @"stringCol !=[cd] 'TUV'");
  921. RLMAssertCount(AllTypesObject, 1U, @"objectCol.stringCol == 'abc'");
  922. RLMAssertCount(AllTypesObject, 1U, @"objectCol.stringCol != 'def'");
  923. RLMAssertCount(AllTypesObject, 1U, @"objectCol.stringCol ==[c] 'abc'");
  924. RLMAssertCount(AllTypesObject, 1U, @"objectCol.stringCol ==[c] 'ABC'");
  925. RLMAssertCount(AllTypesObject, 0U, @"objectCol.stringCol != 'abc'");
  926. RLMAssertCount(AllTypesObject, 0U, @"objectCol.stringCol == 'def'");
  927. RLMAssertCount(AllTypesObject, 0U, @"objectCol.stringCol == 'ABC'");
  928. }
  929. - (void)testFloatQuery
  930. {
  931. RLMRealm *realm = [self realm];
  932. [realm beginWriteTransaction];
  933. [FloatObject createInRealm:realm withValue:@[@1.7f]];
  934. [realm commitWriteTransaction];
  935. RLMAssertCount(FloatObject, 1U, @"floatCol > 1");
  936. RLMAssertCount(FloatObject, 1U, @"floatCol > %d", 1);
  937. RLMAssertCount(FloatObject, 1U, @"floatCol = 1.7");
  938. RLMAssertCount(FloatObject, 1U, @"floatCol = %f", 1.7f);
  939. RLMAssertCount(FloatObject, 1U, @"floatCol > 1.0");
  940. RLMAssertCount(FloatObject, 1U, @"floatCol >= 1.0");
  941. RLMAssertCount(FloatObject, 0U, @"floatCol < 1.0");
  942. RLMAssertCount(FloatObject, 0U, @"floatCol <= 1.0");
  943. RLMAssertCount(FloatObject, 1U, @"floatCol BETWEEN %@", @[@1.0, @2.0]);
  944. RLMAssertCount(FloatObject, 1U, @"floatCol = %e", 1.7);
  945. RLMAssertCount(FloatObject, 0U, @"floatCol == %f", FLT_MAX);
  946. XCTAssertThrows([FloatObject objectsInRealm:realm where:@"floatCol = 3.5e+38"], @"Too large to be a float");
  947. XCTAssertThrows([FloatObject objectsInRealm:realm where:@"floatCol = -3.5e+38"], @"Too small to be a float");
  948. }
  949. - (void)testLiveQueriesInsideTransaction
  950. {
  951. RLMRealm *realm = [self realm];
  952. [realm beginWriteTransaction];
  953. {
  954. [self.queryObjectClass createInRealm:realm withValue:@[@YES, @YES, @1, @2, @23.0f, @1.7f, @0.0, @5.55, @"", @""]];
  955. RLMResults *resultsQuery = [self.queryObjectClass objectsWhere:@"bool1 = YES"];
  956. RLMResults *resultsTableView = [self.queryObjectClass objectsWhere:@"bool1 = YES"];
  957. // Force resultsTableView to form the TableView to verify that it syncs
  958. // correctly, and don't call anything but count on resultsQuery so that
  959. // it always reruns the query count method
  960. (void)[resultsTableView firstObject];
  961. XCTAssertEqual(resultsQuery.count, 1U);
  962. XCTAssertEqual(resultsTableView.count, 1U);
  963. // Delete the (only) object in result set
  964. [realm deleteObject:[resultsTableView lastObject]];
  965. XCTAssertEqual(resultsQuery.count, 0U);
  966. XCTAssertEqual(resultsTableView.count, 0U);
  967. // Add an object that does not match query
  968. QueryObject *q1 = [self.queryObjectClass createInRealm:realm withValue:@[@NO, @YES, @1, @2, @23.0f, @1.7f, @0.0, @5.55, @"", @""]];
  969. XCTAssertEqual(resultsQuery.count, 0U);
  970. XCTAssertEqual(resultsTableView.count, 0U);
  971. // Change object to match query
  972. q1[@"bool1"] = @YES;
  973. XCTAssertEqual(resultsQuery.count, 1U);
  974. XCTAssertEqual(resultsTableView.count, 1U);
  975. // Add another object that matches
  976. [self.queryObjectClass createInRealm:realm withValue:@[@YES, @NO, @1, @3, @-5.3f, @4.21f, @1.0, @4.44, @"", @""]];
  977. XCTAssertEqual(resultsQuery.count, 2U);
  978. XCTAssertEqual(resultsTableView.count, 2U);
  979. }
  980. [realm commitWriteTransaction];
  981. }
  982. - (void)testLiveQueriesBetweenTransactions
  983. {
  984. RLMRealm *realm = [self realm];
  985. [realm beginWriteTransaction];
  986. [self.queryObjectClass createInRealm:realm withValue:@[@YES, @YES, @1, @2, @23.0f, @1.7f, @0.0, @5.55, @"", @""]];
  987. [realm commitWriteTransaction];
  988. RLMResults *resultsQuery = [self.queryObjectClass objectsWhere:@"bool1 = YES"];
  989. RLMResults *resultsTableView = [self.queryObjectClass objectsWhere:@"bool1 = YES"];
  990. // Force resultsTableView to form the TableView to verify that it syncs
  991. // correctly, and don't call anything but count on resultsQuery so that
  992. // it always reruns the query count method
  993. (void)[resultsTableView firstObject];
  994. XCTAssertEqual(resultsQuery.count, 1U);
  995. XCTAssertEqual(resultsTableView.count, 1U);
  996. // Delete the (only) object in result set
  997. [realm beginWriteTransaction];
  998. [realm deleteObject:[resultsTableView lastObject]];
  999. [realm commitWriteTransaction];
  1000. XCTAssertEqual(resultsQuery.count, 0U);
  1001. XCTAssertEqual(resultsTableView.count, 0U);
  1002. // Add an object that does not match query
  1003. [realm beginWriteTransaction];
  1004. QueryObject *q1 = [self.queryObjectClass createInRealm:realm withValue:@[@NO, @YES, @1, @2, @23.0f, @1.7f, @0.0, @5.55, @"", @""]];
  1005. [realm commitWriteTransaction];
  1006. XCTAssertEqual(resultsQuery.count, 0U);
  1007. XCTAssertEqual(resultsTableView.count, 0U);
  1008. // Change object to match query
  1009. [realm beginWriteTransaction];
  1010. q1[@"bool1"] = @YES;
  1011. [realm commitWriteTransaction];
  1012. XCTAssertEqual(resultsQuery.count, 1U);
  1013. XCTAssertEqual(resultsTableView.count, 1U);
  1014. // Add another object that matches
  1015. [realm beginWriteTransaction];
  1016. [self.queryObjectClass createInRealm:realm withValue:@[@YES, @NO, @1, @3, @-5.3f, @4.21f, @1.0, @4.44, @"", @""]];
  1017. [realm commitWriteTransaction];
  1018. XCTAssertEqual(resultsQuery.count, 2U);
  1019. XCTAssertEqual(resultsTableView.count, 2U);
  1020. }
  1021. - (void)makeDogWithName:(NSString *)name owner:(NSString *)ownerName {
  1022. RLMRealm *realm = [self realm];
  1023. OwnerObject *owner = [[OwnerObject alloc] init];
  1024. owner.name = ownerName;
  1025. owner.dog = [[DogObject alloc] init];
  1026. owner.dog.dogName = name;
  1027. [realm beginWriteTransaction];
  1028. [realm addObject:owner];
  1029. [realm commitWriteTransaction];
  1030. }
  1031. - (void)makeDogWithAge:(int)age owner:(NSString *)ownerName {
  1032. RLMRealm *realm = [self realm];
  1033. OwnerObject *owner = [[OwnerObject alloc] init];
  1034. owner.name = ownerName;
  1035. owner.dog = [[DogObject alloc] init];
  1036. owner.dog.dogName = @"";
  1037. owner.dog.age = age;
  1038. [realm beginWriteTransaction];
  1039. [realm addObject:owner];
  1040. [realm commitWriteTransaction];
  1041. }
  1042. - (void)testLinkQueryNewObjectCausesEmptyResults
  1043. {
  1044. [self makeDogWithName:@"Harvie" owner:@"Tim"];
  1045. DogObject *newDogObject = [[DogObject alloc] init];
  1046. RLMAssertCount(OwnerObject, 0U, @"dog = %@", newDogObject);
  1047. }
  1048. - (void)testLinkQueryDifferentRealmsThrows
  1049. {
  1050. RLMRealm *testRealm = [self realmWithTestPath];
  1051. [self makeDogWithName:@"Harvie" owner:@"Tim"];
  1052. RLMRealm *defaultRealm = [self realm];
  1053. DogObject *dog = [[DogObject alloc] init];
  1054. dog.dogName = @"Fido";
  1055. [defaultRealm beginWriteTransaction];
  1056. [defaultRealm addObject:dog];
  1057. [defaultRealm commitWriteTransaction];
  1058. XCTAssertThrows(([OwnerObject objectsInRealm:testRealm where:@"dog = %@", dog]));
  1059. }
  1060. - (void)testLinkQueryString
  1061. {
  1062. RLMRealm *realm = [self realm];
  1063. [self makeDogWithName:@"Harvie" owner:@"Tim"];
  1064. RLMAssertCount(OwnerObject, 1U, @"dog.dogName = 'Harvie'");
  1065. RLMAssertCount(OwnerObject, 0U, @"dog.dogName != 'Harvie'");
  1066. RLMAssertCount(OwnerObject, 0U, @"dog.dogName = 'eivraH'");
  1067. RLMAssertCount(OwnerObject, 0U, @"dog.dogName = 'Fido'");
  1068. RLMAssertCount(OwnerObject, 1U, @"dog.dogName IN {'Fido', 'Harvie'}");
  1069. RLMAssertCount(OwnerObject, 0U, @"dog.dogName IN {'Fido', 'eivraH'}");
  1070. [self makeDogWithName:@"Harvie" owner:@"Joe"];
  1071. RLMAssertCount(OwnerObject, 2U, @"dog.dogName = 'Harvie'");
  1072. RLMAssertCount(OwnerObject, 0U, @"dog.dogName != 'Harvie'");
  1073. RLMAssertCount(OwnerObject, 0U, @"dog.dogName = 'eivraH'");
  1074. RLMAssertCount(OwnerObject, 0U, @"dog.dogName = 'Fido'");
  1075. RLMAssertCount(OwnerObject, 2U, @"dog.dogName IN {'Fido', 'Harvie'}");
  1076. RLMAssertCount(OwnerObject, 0U, @"dog.dogName IN {'Fido', 'eivraH'}");
  1077. [self makeDogWithName:@"Fido" owner:@"Jim"];
  1078. RLMAssertCount(OwnerObject, 2U, @"dog.dogName = 'Harvie'");
  1079. RLMAssertCount(OwnerObject, 1U, @"dog.dogName != 'Harvie'");
  1080. RLMAssertCount(OwnerObject, 0U, @"dog.dogName = 'eivraH'");
  1081. RLMAssertCount(OwnerObject, 1U, @"dog.dogName = 'Fido'");
  1082. RLMAssertCount(OwnerObject, 3U, @"dog.dogName IN {'Fido', 'Harvie'}");
  1083. RLMAssertCount(OwnerObject, 1U, @"dog.dogName IN {'Fido', 'eivraH'}");
  1084. RLMAssertCount(OwnerObject, 1U, @"dog.dogName = 'Harvie' and name = 'Tim'");
  1085. RLMAssertCount(OwnerObject, 0U, @"dog.dogName = 'Harvie' and name = 'Jim'");
  1086. [self makeDogWithName:@"Rex" owner:@"Rex"];
  1087. RLMAssertCount(OwnerObject, 1U, @"dog.dogName = name");
  1088. RLMAssertCount(OwnerObject, 1U, @"name = dog.dogName");
  1089. RLMAssertCount(OwnerObject, 3U, @"dog.dogName != name");
  1090. RLMAssertCount(OwnerObject, 3U, @"name != dog.dogName");
  1091. RLMAssertCount(OwnerObject, 4U, @"dog.dogName == dog.dogName");
  1092. RLMAssertCount(OwnerObject, 0U, @"dog.dogName != dog.dogName");
  1093. // test invalid operators
  1094. XCTAssertThrows([OwnerObject objectsInRealm:realm where:@"dog.dogName > 'Harvie'"], @"Invalid operator should throw");
  1095. }
  1096. - (void)testLinkQueryInt
  1097. {
  1098. [self makeDogWithAge:5 owner:@"Tim"];
  1099. RLMAssertCount(OwnerObject, 1U, @"dog.age = 5");
  1100. RLMAssertCount(OwnerObject, 0U, @"dog.age != 5");
  1101. RLMAssertCount(OwnerObject, 0U, @"dog.age = 10");
  1102. RLMAssertCount(OwnerObject, 0U, @"dog.age = 8");
  1103. RLMAssertCount(OwnerObject, 1U, @"dog.age IN {5, 8}");
  1104. RLMAssertCount(OwnerObject, 0U, @"dog.age IN {8, 10}");
  1105. RLMAssertCount(OwnerObject, 1U, @"dog.age BETWEEN {0, 10}");
  1106. RLMAssertCount(OwnerObject, 1U, @"dog.age BETWEEN {0, 7}");
  1107. [self makeDogWithAge:5 owner:@"Joe"];
  1108. RLMAssertCount(OwnerObject, 2U, @"dog.age = 5");
  1109. RLMAssertCount(OwnerObject, 0U, @"dog.age != 5");
  1110. RLMAssertCount(OwnerObject, 0U, @"dog.age = 10");
  1111. RLMAssertCount(OwnerObject, 0U, @"dog.age = 8");
  1112. RLMAssertCount(OwnerObject, 2U, @"dog.age IN {5, 8}");
  1113. RLMAssertCount(OwnerObject, 0U, @"dog.age IN {8, 10}");
  1114. RLMAssertCount(OwnerObject, 2U, @"dog.age BETWEEN {0, 10}");
  1115. RLMAssertCount(OwnerObject, 2U, @"dog.age BETWEEN {0, 7}");
  1116. [self makeDogWithAge:8 owner:@"Jim"];
  1117. RLMAssertCount(OwnerObject, 2U, @"dog.age = 5");
  1118. RLMAssertCount(OwnerObject, 1U, @"dog.age != 5");
  1119. RLMAssertCount(OwnerObject, 0U, @"dog.age = 10");
  1120. RLMAssertCount(OwnerObject, 1U, @"dog.age = 8");
  1121. RLMAssertCount(OwnerObject, 3U, @"dog.age IN {5, 8}");
  1122. RLMAssertCount(OwnerObject, 1U, @"dog.age IN {8, 10}");
  1123. RLMAssertCount(OwnerObject, 3U, @"dog.age BETWEEN {0, 10}");
  1124. RLMAssertCount(OwnerObject, 2U, @"dog.age BETWEEN {0, 7}");
  1125. }
  1126. - (void)testLinkQueryAllTypes
  1127. {
  1128. RLMRealm *realm = [self realm];
  1129. NSDate *now = [NSDate dateWithTimeIntervalSince1970:100000];
  1130. LinkToAllTypesObject *linkToAllTypes = [[LinkToAllTypesObject alloc] init];
  1131. linkToAllTypes.allTypesCol = [[AllTypesObject alloc] init];
  1132. linkToAllTypes.allTypesCol.boolCol = YES;
  1133. linkToAllTypes.allTypesCol.intCol = 1;
  1134. linkToAllTypes.allTypesCol.floatCol = 1.1f;
  1135. linkToAllTypes.allTypesCol.doubleCol = 1.11;
  1136. linkToAllTypes.allTypesCol.stringCol = @"string";
  1137. linkToAllTypes.allTypesCol.binaryCol = [NSData dataWithBytes:"a" length:1];
  1138. linkToAllTypes.allTypesCol.dateCol = now;
  1139. linkToAllTypes.allTypesCol.cBoolCol = YES;
  1140. linkToAllTypes.allTypesCol.longCol = 11;
  1141. StringObject *obj = [[StringObject alloc] initWithValue:@[@"string"]];
  1142. linkToAllTypes.allTypesCol.objectCol = obj;
  1143. [realm beginWriteTransaction];
  1144. [realm addObject:linkToAllTypes];
  1145. [realm commitWriteTransaction];
  1146. RLMAssertCount(LinkToAllTypesObject, 1U, @"allTypesCol.boolCol = YES");
  1147. RLMAssertCount(LinkToAllTypesObject, 0U, @"allTypesCol.boolCol = NO");
  1148. RLMAssertCount(LinkToAllTypesObject, 1U, @"allTypesCol.intCol = 1");
  1149. RLMAssertCount(LinkToAllTypesObject, 0U, @"allTypesCol.intCol != 1");
  1150. RLMAssertCount(LinkToAllTypesObject, 1U, @"allTypesCol.intCol > 0");
  1151. RLMAssertCount(LinkToAllTypesObject, 0U, @"allTypesCol.intCol > 1");
  1152. RLMAssertCount(LinkToAllTypesObject, 1U, @"allTypesCol.floatCol = %f", 1.1);
  1153. RLMAssertCount(LinkToAllTypesObject, 1U, @"allTypesCol.floatCol <= %f", 1.1);
  1154. RLMAssertCount(LinkToAllTypesObject, 0U, @"allTypesCol.floatCol < %f", 1.1);
  1155. RLMAssertCount(LinkToAllTypesObject, 1U, @"allTypesCol.doubleCol = 1.11");
  1156. RLMAssertCount(LinkToAllTypesObject, 1U, @"allTypesCol.doubleCol >= 1.11");
  1157. RLMAssertCount(LinkToAllTypesObject, 0U, @"allTypesCol.doubleCol > 1.11");
  1158. RLMAssertCount(LinkToAllTypesObject, 1U, @"allTypesCol.longCol = 11");
  1159. RLMAssertCount(LinkToAllTypesObject, 0U, @"allTypesCol.longCol != 11");
  1160. RLMAssertCount(LinkToAllTypesObject, 1U, @"allTypesCol.dateCol = %@", now);
  1161. RLMAssertCount(LinkToAllTypesObject, 0U, @"allTypesCol.dateCol != %@", now);
  1162. }
  1163. - (void)testLinkQueryMany
  1164. {
  1165. RLMRealm *realm = [self realm];
  1166. ArrayPropertyObject *arrPropObj1 = [[ArrayPropertyObject alloc] init];
  1167. arrPropObj1.name = @"Test";
  1168. for(NSUInteger i=0; i<10; i++) {
  1169. StringObject *sobj = [[StringObject alloc] init];
  1170. sobj.stringCol = [NSString stringWithFormat:@"%lu", (unsigned long)i];
  1171. [arrPropObj1.array addObject:sobj];
  1172. IntObject *iobj = [[IntObject alloc] init];
  1173. iobj.intCol = (int)i;
  1174. [arrPropObj1.intArray addObject:iobj];
  1175. }
  1176. [realm beginWriteTransaction];
  1177. [realm addObject:arrPropObj1];
  1178. [realm commitWriteTransaction];
  1179. RLMAssertCount(ArrayPropertyObject, 0U, @"ANY intArray.intCol > 10");
  1180. RLMAssertCount(ArrayPropertyObject, 0U, @"ANY intArray.intCol > 10");
  1181. RLMAssertCount(ArrayPropertyObject, 1U, @"ANY intArray.intCol > 5");
  1182. RLMAssertCount(ArrayPropertyObject, 1U, @"ANY array.stringCol = '1'");
  1183. RLMAssertCount(ArrayPropertyObject, 0U, @"NONE intArray.intCol == 5");
  1184. RLMAssertCount(ArrayPropertyObject, 1U, @"NONE intArray.intCol > 10");
  1185. ArrayPropertyObject *arrPropObj2 = [[ArrayPropertyObject alloc] init];
  1186. arrPropObj2.name = @"Test";
  1187. for(NSUInteger i=0; i<4; i++) {
  1188. StringObject *sobj = [[StringObject alloc] init];
  1189. sobj.stringCol = [NSString stringWithFormat:@"%lu", (unsigned long)i];
  1190. [arrPropObj2.array addObject:sobj];
  1191. IntObject *iobj = [[IntObject alloc] init];
  1192. iobj.intCol = (int)i;
  1193. [arrPropObj2.intArray addObject:iobj];
  1194. }
  1195. [realm beginWriteTransaction];
  1196. [realm addObject:arrPropObj2];
  1197. [realm commitWriteTransaction];
  1198. RLMAssertCount(ArrayPropertyObject, 0U, @"ANY intArray.intCol > 10");
  1199. RLMAssertCount(ArrayPropertyObject, 1U, @"ANY intArray.intCol > 5");
  1200. RLMAssertCount(ArrayPropertyObject, 2U, @"ANY intArray.intCol > 2");
  1201. RLMAssertCount(ArrayPropertyObject, 1U, @"NONE intArray.intCol == 5");
  1202. RLMAssertCount(ArrayPropertyObject, 2U, @"NONE intArray.intCol > 10");
  1203. }
  1204. - (void)testMultiLevelLinkQuery
  1205. {
  1206. RLMRealm *realm = [self realm];
  1207. [realm beginWriteTransaction];
  1208. CircleObject *circle = nil;
  1209. for (int i = 0; i < 5; ++i) {
  1210. circle = [CircleObject createInRealm:realm withValue:@{@"data": [NSString stringWithFormat:@"%d", i],
  1211. @"next": circle ?: NSNull.null}];
  1212. }
  1213. [realm commitWriteTransaction];
  1214. XCTAssertTrue([circle isEqualToObject:[CircleObject objectsInRealm:realm where:@"data = '4'"].firstObject]);
  1215. XCTAssertTrue([circle isEqualToObject:[CircleObject objectsInRealm:realm where:@"next.data = '3'"].firstObject]);
  1216. XCTAssertTrue([circle isEqualToObject:[CircleObject objectsInRealm:realm where:@"next.next.data = '2'"].firstObject]);
  1217. XCTAssertTrue([circle isEqualToObject:[CircleObject objectsInRealm:realm where:@"next.next.next.data = '1'"].firstObject]);
  1218. XCTAssertTrue([circle isEqualToObject:[CircleObject objectsInRealm:realm where:@"next.next.next.next.data = '0'"].firstObject]);
  1219. XCTAssertTrue([circle.next isEqualToObject:[CircleObject objectsInRealm:realm where:@"next.next.next.data = '0'"].firstObject]);
  1220. XCTAssertTrue([circle.next.next isEqualToObject:[CircleObject objectsInRealm:realm where:@"next.next.data = '0'"].firstObject]);
  1221. XCTAssertNoThrow(([CircleObject objectsInRealm:realm where:@"next = %@", circle]));
  1222. XCTAssertNoThrow(([CircleObject objectsInRealm:realm where:@"next.next = %@", circle]));
  1223. XCTAssertTrue([circle.next.next.next.next isEqualToObject:[CircleObject objectsInRealm:realm where:@"next = nil"].firstObject]);
  1224. XCTAssertTrue([circle.next.next.next isEqualToObject:[CircleObject objectsInRealm:realm where:@"next != nil AND next.next = nil"].firstObject]);
  1225. XCTAssertTrue([circle.next.next isEqualToObject:[CircleObject objectsInRealm:realm where:@"next.next != nil AND next.next.next = nil"].firstObject]);
  1226. }
  1227. - (void)testArrayMultiLevelLinkQuery
  1228. {
  1229. RLMRealm *realm = [self realm];
  1230. [realm beginWriteTransaction];
  1231. CircleObject *circle = nil;
  1232. for (int i = 0; i < 5; ++i) {
  1233. circle = [CircleObject createInRealm:realm withValue:@{@"data": [NSString stringWithFormat:@"%d", i],
  1234. @"next": circle ?: NSNull.null}];
  1235. }
  1236. [CircleArrayObject createInRealm:realm withValue:@[[CircleObject allObjectsInRealm:realm]]];
  1237. [realm commitWriteTransaction];
  1238. RLMAssertCount(CircleArrayObject, 1U, @"ANY circles.data = '4'");
  1239. RLMAssertCount(CircleArrayObject, 0U, @"ANY circles.next.data = '4'");
  1240. RLMAssertCount(CircleArrayObject, 1U, @"ANY circles.next.data = '3'");
  1241. RLMAssertCount(CircleArrayObject, 1U, @"ANY circles.data = '3'");
  1242. RLMAssertCount(CircleArrayObject, 1U, @"NONE circles.next.data = '4'");
  1243. RLMAssertCount(CircleArrayObject, 0U, @"ANY circles.next.next.data = '3'");
  1244. RLMAssertCount(CircleArrayObject, 1U, @"ANY circles.next.next.data = '2'");
  1245. RLMAssertCount(CircleArrayObject, 1U, @"ANY circles.next.data = '2'");
  1246. RLMAssertCount(CircleArrayObject, 1U, @"ANY circles.data = '2'");
  1247. RLMAssertCount(CircleArrayObject, 1U, @"NONE circles.next.next.data = '3'");
  1248. XCTAssertThrows([CircleArrayObject objectsInRealm:realm where:@"ANY data = '2'"]);
  1249. XCTAssertThrows([CircleArrayObject objectsInRealm:realm where:@"ANY circles.next = '2'"]);
  1250. XCTAssertThrows([CircleArrayObject objectsInRealm:realm where:@"ANY data.circles = '2'"]);
  1251. XCTAssertThrows([CircleArrayObject objectsInRealm:realm where:@"circles.data = '2'"]);
  1252. XCTAssertThrows([CircleArrayObject objectsInRealm:realm where:@"NONE data.circles = '2'"]);
  1253. }
  1254. - (void)testMultiLevelBackLinkQuery
  1255. {
  1256. RLMRealm *realm = [self realm];
  1257. [realm beginWriteTransaction];
  1258. LinkChain1 *root1 = [LinkChain1 createInRealm:realm withValue:@{@"value": @1, @"next": @[@[]]}];
  1259. LinkChain1 *root2 = [LinkChain1 createInRealm:realm withValue:@{@"value": @2, @"next": @[@[]]}];
  1260. [realm commitWriteTransaction];
  1261. RLMResults *results = [LinkChain3 objectsInRealm:realm where:@"ANY prev.prev.value = 1"];
  1262. XCTAssertEqual(1U, results.count);
  1263. XCTAssertTrue([root1.next.next isEqualToObject:results.firstObject]);
  1264. results = [LinkChain3 objectsInRealm:realm where:@"ANY prev.prev.value = 2"];
  1265. XCTAssertEqual(1U, results.count);
  1266. XCTAssertTrue([root2.next.next isEqualToObject:results.firstObject]);
  1267. results = [LinkChain3 objectsInRealm:realm where:@"ANY prev.prev.value = 3"];
  1268. XCTAssertEqual(0U, results.count);
  1269. }
  1270. - (void)testQueryWithObjects
  1271. {
  1272. RLMRealm *realm = [self realm];
  1273. NSDate *date1 = [NSDate date];
  1274. NSDate *date2 = [date1 dateByAddingTimeInterval:1];
  1275. NSDate *date3 = [date2 dateByAddingTimeInterval:1];
  1276. [realm beginWriteTransaction];
  1277. StringObject *stringObj0 = [StringObject createInRealm:realm withValue:@[@"string0"]];
  1278. StringObject *stringObj1 = [StringObject createInRealm:realm withValue:@[@"string1"]];
  1279. StringObject *stringObj2 = [StringObject createInRealm:realm withValue:@[@"string2"]];
  1280. AllTypesObject *obj0 = [AllTypesObject createInRealm:realm withValue:@[@YES, @1, @1.0f, @1.0, @"a", [@"a" dataUsingEncoding:NSUTF8StringEncoding], date1, @YES, @1LL, stringObj0]];
  1281. AllTypesObject *obj1 = [AllTypesObject createInRealm:realm withValue:@[@YES, @2, @2.0f, @2.0, @"b", [@"b" dataUsingEncoding:NSUTF8StringEncoding], date2, @YES, @2LL, stringObj1]];
  1282. AllTypesObject *obj2 = [AllTypesObject createInRealm:realm withValue:@[@NO, @3, @3.0f, @3.0, @"c", [@"c" dataUsingEncoding:NSUTF8StringEncoding], date3, @YES, @3LL, stringObj0]];
  1283. AllTypesObject *obj3 = [AllTypesObject createInRealm:realm withValue:@[@NO, @3, @3.0f, @3.0, @"c", [@"c" dataUsingEncoding:NSUTF8StringEncoding], date3, @YES, @3LL, stringObj2]];
  1284. AllTypesObject *obj4 = [AllTypesObject createInRealm:realm withValue:@[@NO, @3, @3.0f, @3.0, @"c", [@"c" dataUsingEncoding:NSUTF8StringEncoding], date3, @YES, @34359738368LL, NSNull.null]];
  1285. [ArrayOfAllTypesObject createInDefaultRealmWithValue:@[@[obj0, obj1]]];
  1286. [ArrayOfAllTypesObject createInDefaultRealmWithValue:@[@[obj1]]];
  1287. [ArrayOfAllTypesObject createInDefaultRealmWithValue:@[@[obj0, obj2, obj3]]];
  1288. [ArrayOfAllTypesObject createInDefaultRealmWithValue:@[@[obj4]]];
  1289. [realm commitWriteTransaction];
  1290. // simple queries
  1291. RLMAssertCount(AllTypesObject, 2U, @"objectCol = %@", stringObj0);
  1292. RLMAssertCount(AllTypesObject, 1U, @"objectCol = %@", stringObj1);
  1293. RLMAssertCount(AllTypesObject, 1U, @"objectCol = nil");
  1294. RLMAssertCount(AllTypesObject, 4U, @"objectCol != nil");
  1295. RLMAssertCount(AllTypesObject, 3U, @"objectCol != %@", stringObj0);
  1296. RLMAssertCount(AllTypesObject, 1U, @"longCol = %lli", 34359738368);
  1297. RLMAssertCount(AllTypesObject, 1U, @"longCol BETWEEN %@", @[@34359738367LL, @34359738369LL]);
  1298. // check for ANY object in array
  1299. RLMAssertCount(ArrayOfAllTypesObject, 2U, @"ANY array = %@", obj0);
  1300. RLMAssertCount(ArrayOfAllTypesObject, 3U, @"ANY array != %@", obj1);
  1301. RLMAssertCount(ArrayOfAllTypesObject, 2U, @"NONE array = %@", obj0);
  1302. RLMAssertCount(ArrayOfAllTypesObject, 1U, @"NONE array != %@", obj1);
  1303. XCTAssertThrows(([ArrayOfAllTypesObject objectsWhere:@"array = %@", obj0].count));
  1304. XCTAssertThrows(([ArrayOfAllTypesObject objectsWhere:@"array != %@", obj0].count));
  1305. }
  1306. - (void)testCompoundOrQuery {
  1307. RLMRealm *realm = [self realm];
  1308. [realm beginWriteTransaction];
  1309. [PersonObject createInRealm:realm withValue:@[@"Tim", @29]];
  1310. [PersonObject createInRealm:realm withValue:@[@"Ari", @33]];
  1311. [realm commitWriteTransaction];
  1312. RLMAssertCount(PersonObject, 2U, @"name == 'Ari' or age < 30");
  1313. RLMAssertCount(PersonObject, 1U, @"name == 'Ari' or age > 40");
  1314. }
  1315. - (void)testCompoundAndQuery {
  1316. RLMRealm *realm = [self realm];
  1317. [realm beginWriteTransaction];
  1318. [PersonObject createInRealm:realm withValue:@[@"Tim", @29]];
  1319. [PersonObject createInRealm:realm withValue:@[@"Ari", @33]];
  1320. [realm commitWriteTransaction];
  1321. RLMAssertCount(PersonObject, 1U, @"name == 'Ari' and age > 30");
  1322. RLMAssertCount(PersonObject, 0U, @"name == 'Ari' and age > 40");
  1323. }
  1324. - (void)testClass:(Class)class
  1325. withNormalCount:(NSUInteger)normalCount
  1326. notCount:(NSUInteger)notCount
  1327. where:(NSString *)predicateFormat, ...
  1328. {
  1329. va_list args;
  1330. va_start(args, predicateFormat);
  1331. NSPredicate *predicate = [NSPredicate predicateWithFormat:predicateFormat arguments:args];
  1332. va_end(args);
  1333. XCTAssertEqual(normalCount, [[self evaluate:[class objectsWithPredicate:predicate]] count]);
  1334. predicate = [NSCompoundPredicate notPredicateWithSubpredicate:predicate];
  1335. XCTAssertEqual(notCount, [[self evaluate:[class objectsWithPredicate:predicate]] count]);
  1336. }
  1337. - (void)testINPredicate
  1338. {
  1339. RLMRealm *realm = [self realm];
  1340. [realm beginWriteTransaction];
  1341. StringObject *so = [StringObject createInRealm:realm withValue:(@[@"abc"])];
  1342. [AllTypesObject createInRealm:realm withValue:@[@YES, @1, @1.0f, @1.0, @"abc", [@"a" dataUsingEncoding:NSUTF8StringEncoding], [NSDate dateWithTimeIntervalSince1970:1], @YES, @1LL, so]];
  1343. [realm commitWriteTransaction];
  1344. // Tests for each type always follow: none, some, more
  1345. ////////////////////////
  1346. // Literal Predicates
  1347. ////////////////////////
  1348. // BOOL
  1349. [self testClass:[AllTypesObject class] withNormalCount:0 notCount:1 where:@"boolCol IN {NO}"];
  1350. [self testClass:[AllTypesObject class] withNormalCount:1 notCount:0 where:@"boolCol IN {YES}"];
  1351. [self testClass:[AllTypesObject class] withNormalCount:1 notCount:0 where:@"boolCol IN {NO, YES}"];
  1352. // int
  1353. [self testClass:[AllTypesObject class] withNormalCount:0 notCount:1 where:@"intCol IN {0, 2, 3}"];
  1354. [self testClass:[AllTypesObject class] withNormalCount:1 notCount:0 where:@"intCol IN {1}"];
  1355. [self testClass:[AllTypesObject class] withNormalCount:1 notCount:0 where:@"intCol IN {1, 2}"];
  1356. // float
  1357. [self testClass:[AllTypesObject class] withNormalCount:0 notCount:1 where:@"floatCol IN {0, 2, 3}"];
  1358. [self testClass:[AllTypesObject class] withNormalCount:1 notCount:0 where:@"floatCol IN {1}"];
  1359. [self testClass:[AllTypesObject class] withNormalCount:1 notCount:0 where:@"floatCol IN {1, 2}"];
  1360. // double
  1361. [self testClass:[AllTypesObject class] withNormalCount:0 notCount:1 where:@"doubleCol IN {0, 2, 3}"];
  1362. [self testClass:[AllTypesObject class] withNormalCount:1 notCount:0 where:@"doubleCol IN {1}"];
  1363. [self testClass:[AllTypesObject class] withNormalCount:1 notCount:0 where:@"doubleCol IN {1, 2}"];
  1364. // NSString
  1365. [self testClass:[AllTypesObject class] withNormalCount:1 notCount:0 where:@"stringCol IN {'abc'}"];
  1366. [self testClass:[AllTypesObject class] withNormalCount:0 notCount:1 where:@"stringCol IN {'def'}"];
  1367. [self testClass:[AllTypesObject class] withNormalCount:0 notCount:1 where:@"stringCol IN {'ABC'}"];
  1368. [self testClass:[AllTypesObject class] withNormalCount:1 notCount:0 where:@"stringCol IN[c] {'abc'}"];
  1369. [self testClass:[AllTypesObject class] withNormalCount:1 notCount:0 where:@"stringCol IN[c] {'ABC'}"];
  1370. // NSData
  1371. // Can't represent NSData with NSPredicate literal. See format predicates below
  1372. // NSDate
  1373. // Can't represent NSDate with NSPredicate literal. See format predicates below
  1374. // bool
  1375. [self testClass:[AllTypesObject class] withNormalCount:0 notCount:1 where:@"cBoolCol IN {NO}"];
  1376. [self testClass:[AllTypesObject class] withNormalCount:1 notCount:0 where:@"cBoolCol IN {YES}"];
  1377. [self testClass:[AllTypesObject class] withNormalCount:1 notCount:0 where:@"cBoolCol IN {NO, YES}"];
  1378. // int64_t
  1379. [self testClass:[AllTypesObject class] withNormalCount:0 notCount:1 where:@"longCol IN {0, 2, 3}"];
  1380. [self testClass:[AllTypesObject class] withNormalCount:1 notCount:0 where:@"longCol IN {1}"];
  1381. [self testClass:[AllTypesObject class] withNormalCount:1 notCount:0 where:@"longCol IN {1, 2}"];
  1382. // string subobject
  1383. [self testClass:[AllTypesObject class] withNormalCount:1 notCount:0 where:@"objectCol.stringCol IN {'abc'}"];
  1384. [self testClass:[AllTypesObject class] withNormalCount:0 notCount:1 where:@"objectCol.stringCol IN {'def'}"];
  1385. [self testClass:[AllTypesObject class] withNormalCount:0 notCount:1 where:@"objectCol.stringCol IN {'ABC'}"];
  1386. [self testClass:[AllTypesObject class] withNormalCount:1 notCount:0 where:@"objectCol.stringCol IN[c] {'abc'}"];
  1387. [self testClass:[AllTypesObject class] withNormalCount:1 notCount:0 where:@"objectCol.stringCol IN[c] {'ABC'}"];
  1388. ////////////////////////
  1389. // Format Predicates
  1390. ////////////////////////
  1391. // BOOL
  1392. [self testClass:[AllTypesObject class] withNormalCount:0U notCount:1U where:@"boolCol IN %@", @[@NO]];
  1393. [self testClass:[AllTypesObject class] withNormalCount:1U notCount:0U where:@"boolCol IN %@", @[@YES]];
  1394. [self testClass:[AllTypesObject class] withNormalCount:1U notCount:0U where:@"boolCol IN %@", @[@NO, @YES]];
  1395. // int
  1396. [self testClass:[AllTypesObject class] withNormalCount:0U notCount:1U where:@"intCol IN %@", @[@0, @2, @3]];
  1397. [self testClass:[AllTypesObject class] withNormalCount:1U notCount:0U where:@"intCol IN %@", @[@1]];
  1398. [self testClass:[AllTypesObject class] withNormalCount:1U notCount:0U where:@"intCol IN %@", @[@1, @2]];
  1399. // float
  1400. [self testClass:[AllTypesObject class] withNormalCount:0U notCount:1U where:@"floatCol IN %@", @[@0, @2, @3]];
  1401. [self testClass:[AllTypesObject class] withNormalCount:1U notCount:0U where:@"floatCol IN %@", @[@1]];
  1402. [self testClass:[AllTypesObject class] withNormalCount:1U notCount:0U where:@"floatCol IN %@", @[@1, @2]];
  1403. // double
  1404. [self testClass:[AllTypesObject class] withNormalCount:0U notCount:1U where:@"doubleCol IN %@", @[@0, @2, @3]];
  1405. [self testClass:[AllTypesObject class] withNormalCount:1U notCount:0U where:@"doubleCol IN %@", @[@1]];
  1406. [self testClass:[AllTypesObject class] withNormalCount:1U notCount:0U where:@"doubleCol IN %@", @[@1, @2]];
  1407. // NSString
  1408. [self testClass:[AllTypesObject class] withNormalCount:1U notCount:0U where:@"stringCol IN %@", @[@"abc"]];
  1409. [self testClass:[AllTypesObject class] withNormalCount:0U notCount:1U where:@"stringCol IN %@", @[@"def"]];
  1410. [self testClass:[AllTypesObject class] withNormalCount:0U notCount:1U where:@"stringCol IN %@", @[@"ABC"]];
  1411. [self testClass:[AllTypesObject class] withNormalCount:1U notCount:0U where:@"stringCol IN[c] %@", @[@"abc"]];
  1412. [self testClass:[AllTypesObject class] withNormalCount:1U notCount:0U where:@"stringCol IN[c] %@", @[@"ABC"]];
  1413. // NSData
  1414. [self testClass:[AllTypesObject class] withNormalCount:0U notCount:1U where:@"binaryCol IN %@", @[[@"" dataUsingEncoding:NSUTF8StringEncoding]]];
  1415. [self testClass:[AllTypesObject class] withNormalCount:1U notCount:0U where:@"binaryCol IN %@", @[[@"a" dataUsingEncoding:NSUTF8StringEncoding]]];
  1416. [self testClass:[AllTypesObject class] withNormalCount:1U notCount:0U where:@"binaryCol IN %@", @[[@"a" dataUsingEncoding:NSUTF8StringEncoding], [@"b" dataUsingEncoding:NSUTF8StringEncoding]]];
  1417. // NSDate
  1418. [self testClass:[AllTypesObject class] withNormalCount:0U notCount:1U where:@"dateCol IN %@", @[[NSDate dateWithTimeIntervalSince1970:0]]];
  1419. [self testClass:[AllTypesObject class] withNormalCount:1U notCount:0U where:@"dateCol IN %@", @[[NSDate dateWithTimeIntervalSince1970:1]]];
  1420. [self testClass:[AllTypesObject class] withNormalCount:1U notCount:0U where:@"dateCol IN %@", @[[NSDate dateWithTimeIntervalSince1970:0], [NSDate dateWithTimeIntervalSince1970:1]]];
  1421. // bool
  1422. [self testClass:[AllTypesObject class] withNormalCount:0U notCount:1U where:@"cBoolCol IN %@", @[@NO]];
  1423. [self testClass:[AllTypesObject class] withNormalCount:1U notCount:0U where:@"cBoolCol IN %@", @[@YES]];
  1424. [self testClass:[AllTypesObject class] withNormalCount:1U notCount:0U where:@"cBoolCol IN %@", @[@NO, @YES]];
  1425. // int64_t
  1426. [self testClass:[AllTypesObject class] withNormalCount:0U notCount:1U where:@"longCol IN %@", @[@0, @2, @3]];
  1427. [self testClass:[AllTypesObject class] withNormalCount:1U notCount:0U where:@"longCol IN %@", @[@1]];
  1428. [self testClass:[AllTypesObject class] withNormalCount:1U notCount:0U where:@"longCol IN %@", @[@1, @2]];
  1429. // string subobject
  1430. [self testClass:[AllTypesObject class] withNormalCount:1U notCount:0U where:@"objectCol.stringCol IN %@", @[@"abc"]];
  1431. [self testClass:[AllTypesObject class] withNormalCount:0U notCount:1U where:@"objectCol.stringCol IN %@", @[@"def"]];
  1432. [self testClass:[AllTypesObject class] withNormalCount:0U notCount:1U where:@"objectCol.stringCol IN %@", @[@"ABC"]];
  1433. [self testClass:[AllTypesObject class] withNormalCount:1U notCount:0U where:@"objectCol.stringCol IN[c] %@", @[@"abc"]];
  1434. [self testClass:[AllTypesObject class] withNormalCount:1U notCount:0U where:@"objectCol.stringCol IN[c] %@", @[@"ABC"]];
  1435. }
  1436. - (void)testArrayIn {
  1437. RLMRealm *realm = [self realm];
  1438. [realm beginWriteTransaction];
  1439. ArrayPropertyObject *arr = [ArrayPropertyObject createInRealm:realm withValue:@[@"name", @[], @[]]];
  1440. [arr.array addObject:[StringObject createInRealm:realm withValue:@[@"value"]]];
  1441. StringObject *otherStringObject = [StringObject createInRealm:realm withValue:@[@"some other value"]];
  1442. [realm commitWriteTransaction];
  1443. RLMAssertCount(ArrayPropertyObject, 0U, @"ANY array.stringCol IN %@", @[@"missing"]);
  1444. RLMAssertCount(ArrayPropertyObject, 1U, @"ANY array.stringCol IN %@", @[@"value"]);
  1445. RLMAssertCount(ArrayPropertyObject, 1U, @"NONE array.stringCol IN %@", @[@"missing"]);
  1446. RLMAssertCount(ArrayPropertyObject, 0U, @"NONE array.stringCol IN %@", @[@"value"]);
  1447. RLMAssertCount(ArrayPropertyObject, 0U, @"ANY array IN %@", [StringObject objectsWhere:@"stringCol = 'missing'"]);
  1448. RLMAssertCount(ArrayPropertyObject, 1U, @"ANY array IN %@", [StringObject objectsWhere:@"stringCol = 'value'"]);
  1449. RLMAssertCount(ArrayPropertyObject, 1U, @"NONE array IN %@", [StringObject objectsWhere:@"stringCol = 'missing'"]);
  1450. RLMAssertCount(ArrayPropertyObject, 0U, @"NONE array IN %@", [StringObject objectsWhere:@"stringCol = 'value'"]);
  1451. StringObject *stringObject = [[StringObject allObjectsInRealm:realm] firstObject];
  1452. RLMAssertCount(ArrayPropertyObject, 1U, @"%@ IN array", stringObject);
  1453. RLMAssertCount(ArrayPropertyObject, 0U, @"%@ IN array", otherStringObject);
  1454. }
  1455. - (void)testQueryChaining {
  1456. RLMRealm *realm = [self realm];
  1457. [realm beginWriteTransaction];
  1458. [PersonObject createInRealm:realm withValue:@[@"Tim", @29]];
  1459. [PersonObject createInRealm:realm withValue:@[@"Ari", @33]];
  1460. [realm commitWriteTransaction];
  1461. RLMAssertCount(PersonObject, 1U, @"name == 'Ari'");
  1462. RLMAssertCount(PersonObject, 0U, @"name == 'Ari' and age == 29");
  1463. XCTAssertEqual(0U, [[[PersonObject objectsWhere:@"name == 'Ari'"] objectsWhere:@"age == 29"] count]);
  1464. }
  1465. - (void)testLinkViewQuery {
  1466. RLMRealm *realm = [self realm];
  1467. [realm beginWriteTransaction];
  1468. [CompanyObject createInRealm:realm
  1469. withValue:@[@"company name", @[@{@"name": @"John", @"age": @30, @"hired": @NO},
  1470. @{@"name": @"Joe", @"age": @40, @"hired": @YES},
  1471. @{@"name": @"Jill", @"age": @50, @"hired": @YES}]]];
  1472. [realm commitWriteTransaction];
  1473. CompanyObject *co = [CompanyObject allObjects][0];
  1474. RLMAssertCount(co.employees, 1U, @"hired = NO");
  1475. RLMAssertCount(co.employees, 2U, @"hired = YES");
  1476. RLMAssertCount(co.employees, 1U, @"hired = YES AND age = 40");
  1477. RLMAssertCount(co.employees, 0U, @"hired = YES AND age = 30");
  1478. RLMAssertCount(co.employees, 3U, @"hired = YES OR age = 30");
  1479. RLMAssertCount([co.employees, 1U, @"hired = YES"] objectsWhere:@"name = 'Joe'");
  1480. }
  1481. - (void)testLinkViewQueryLifetime {
  1482. RLMRealm *realm = [self realm];
  1483. [realm beginWriteTransaction];
  1484. [CompanyObject createInRealm:realm
  1485. withValue:@[@"company name", @[@{@"name": @"John", @"age": @30, @"hired": @NO},
  1486. @{@"name": @"Jill", @"age": @50, @"hired": @YES}]]];
  1487. [EmployeeObject createInRealm:realm withValue:@{@"name": @"Joe", @"age": @40, @"hired": @YES}];
  1488. [realm commitWriteTransaction];
  1489. RLMResults<EmployeeObject *> *subarray = nil;
  1490. @autoreleasepool {
  1491. __attribute((objc_precise_lifetime)) CompanyObject *co = [CompanyObject allObjects][0];
  1492. subarray = [co.employees objectsWhere:@"age = 40"];
  1493. XCTAssertEqual(0U, subarray.count);
  1494. }
  1495. [realm beginWriteTransaction];
  1496. @autoreleasepool {
  1497. __attribute((objc_precise_lifetime)) CompanyObject *co = [CompanyObject allObjects][0];
  1498. [co.employees addObject:[EmployeeObject createInRealm:realm withValue:@{@"name": @"Joe", @"age": @40, @"hired": @YES}]];
  1499. }
  1500. [realm commitWriteTransaction];
  1501. XCTAssertEqual(1U, subarray.count);
  1502. XCTAssertEqualObjects(@"Joe", subarray[0][@"name"]);
  1503. }
  1504. - (void)testLinkViewQueryLiveUpdate {
  1505. RLMRealm *realm = [self realm];
  1506. [realm beginWriteTransaction];
  1507. [CompanyObject createInRealm:realm
  1508. withValue:@[@"company name", @[@{@"name": @"John", @"age": @30, @"hired": @NO},
  1509. @{@"name": @"Jill", @"age": @40, @"hired": @YES}]]];
  1510. EmployeeObject *eo = [EmployeeObject createInRealm:realm withValue:@{@"name": @"Joe", @"age": @40, @"hired": @YES}];
  1511. [realm commitWriteTransaction];
  1512. CompanyObject *co = CompanyObject.allObjects.firstObject;
  1513. RLMResults *basic = [co.employees objectsWhere:@"age = 40"];
  1514. RLMResults *sort = [co.employees sortedResultsUsingKeyPath:@"name" ascending:YES];
  1515. RLMResults *sortQuery = [[co.employees sortedResultsUsingKeyPath:@"name" ascending:YES] objectsWhere:@"age = 40"];
  1516. RLMResults *querySort = [[co.employees objectsWhere:@"age = 40"] sortedResultsUsingKeyPath:@"name" ascending:YES];
  1517. XCTAssertEqual(1U, basic.count);
  1518. XCTAssertEqual(2U, sort.count);
  1519. XCTAssertEqual(1U, sortQuery.count);
  1520. XCTAssertEqual(1U, querySort.count);
  1521. XCTAssertEqualObjects(@"Jill", [[basic lastObject] name]);
  1522. XCTAssertEqualObjects(@"Jill", [[sortQuery lastObject] name]);
  1523. XCTAssertEqualObjects(@"Jill", [[querySort lastObject] name]);
  1524. [realm beginWriteTransaction];
  1525. [co.employees addObject:eo];
  1526. [realm commitWriteTransaction];
  1527. XCTAssertEqual(2U, basic.count);
  1528. XCTAssertEqual(3U, sort.count);
  1529. XCTAssertEqual(2U, sortQuery.count);
  1530. XCTAssertEqual(2U, querySort.count);
  1531. XCTAssertEqualObjects(@"Joe", [[basic lastObject] name]);
  1532. XCTAssertEqualObjects(@"Joe", [[sortQuery lastObject] name]);
  1533. XCTAssertEqualObjects(@"Joe", [[querySort lastObject] name]);
  1534. }
  1535. - (void)testConstantPredicates
  1536. {
  1537. RLMRealm *realm = [self realm];
  1538. [realm beginWriteTransaction];
  1539. [PersonObject createInRealm:realm withValue:@[@"Fiel", @27]];
  1540. [PersonObject createInRealm:realm withValue:@[@"Tim", @29]];
  1541. [PersonObject createInRealm:realm withValue:@[@"Ari", @33]];
  1542. [realm commitWriteTransaction];
  1543. RLMResults *all = [PersonObject objectsWithPredicate:[NSPredicate predicateWithValue:YES]];
  1544. XCTAssertEqual(all.count, 3U, @"Expecting 3 results");
  1545. RLMResults *none = [PersonObject objectsWithPredicate:[NSPredicate predicateWithValue:NO]];
  1546. XCTAssertEqual(none.count, 0U, @"Expecting 0 results");
  1547. }
  1548. - (void)testEmptyCompoundPredicates
  1549. {
  1550. RLMRealm *realm = [self realm];
  1551. [realm beginWriteTransaction];
  1552. [PersonObject createInRealm:realm withValue:@[@"Fiel", @27]];
  1553. [PersonObject createInRealm:realm withValue:@[@"Tim", @29]];
  1554. [PersonObject createInRealm:realm withValue:@[@"Ari", @33]];
  1555. [realm commitWriteTransaction];
  1556. RLMResults *all = [PersonObject objectsWithPredicate:[NSCompoundPredicate andPredicateWithSubpredicates:@[]]];
  1557. XCTAssertEqual(all.count, 3U, @"Expecting 3 results");
  1558. RLMResults *none = [PersonObject objectsWithPredicate:[NSCompoundPredicate orPredicateWithSubpredicates:@[]]];
  1559. XCTAssertEqual(none.count, 0U, @"Expecting 0 results");
  1560. }
  1561. - (void)testComparisonsWithKeyPathOnRHS
  1562. {
  1563. RLMRealm *realm = [self realm];
  1564. [realm beginWriteTransaction];
  1565. [self.queryObjectClass createInRealm:realm withValue:@[@YES, @YES, @1, @2, @23.0f, @1.7f, @0.0, @5.55, @"a", @"a"]];
  1566. [self.queryObjectClass createInRealm:realm withValue:@[@YES, @NO, @1, @3, @-5.3f, @4.21f, @1.0, @4.44, @"a", @"A"]];
  1567. [self.queryObjectClass createInRealm:realm withValue:@[@NO, @NO, @2, @2, @1.0f, @3.55f, @99.9, @6.66, @"a", @"ab"]];
  1568. [self.queryObjectClass createInRealm:realm withValue:@[@NO, @YES, @3, @6, @4.21f, @1.0f, @1.0, @7.77, @"a", @"AB"]];
  1569. [self.queryObjectClass createInRealm:realm withValue:@[@YES, @YES, @4, @5, @23.0f, @23.0f, @7.4, @8.88, @"a", @"b"]];
  1570. [self.queryObjectClass createInRealm:realm withValue:@[@YES, @NO, @15, @8, @1.0f, @66.0f, @1.01, @9.99, @"a", @"ba"]];
  1571. [self.queryObjectClass createInRealm:realm withValue:@[@NO, @YES, @15, @15, @1.0f, @66.0f, @1.01, @9.99, @"a", @"BA"]];
  1572. [realm commitWriteTransaction];
  1573. RLMAssertCount(self.queryObjectClass, 4U, @"TRUE == bool1");
  1574. RLMAssertCount(self.queryObjectClass, 3U, @"TRUE != bool2");
  1575. RLMAssertCount(self.queryObjectClass, 2U, @"1 == int1");
  1576. RLMAssertCount(self.queryObjectClass, 5U, @"2 != int2");
  1577. RLMAssertCount(self.queryObjectClass, 2U, @"2 > int1");
  1578. RLMAssertCount(self.queryObjectClass, 4U, @"2 < int1");
  1579. RLMAssertCount(self.queryObjectClass, 3U, @"2 >= int1");
  1580. RLMAssertCount(self.queryObjectClass, 5U, @"2 <= int1");
  1581. RLMAssertCount(self.queryObjectClass, 3U, @"1.0 == float1");
  1582. RLMAssertCount(self.queryObjectClass, 6U, @"1.0 != float2");
  1583. RLMAssertCount(self.queryObjectClass, 1U, @"1.0 > float1");
  1584. RLMAssertCount(self.queryObjectClass, 6U, @"1.0 < float2");
  1585. RLMAssertCount(self.queryObjectClass, 4U, @"1.0 >= float1");
  1586. RLMAssertCount(self.queryObjectClass, 7U, @"1.0 <= float2");
  1587. RLMAssertCount(self.queryObjectClass, 2U, @"1.0 == double1");
  1588. RLMAssertCount(self.queryObjectClass, 5U, @"1.0 != double1");
  1589. RLMAssertCount(self.queryObjectClass, 1U, @"5.0 > double2");
  1590. RLMAssertCount(self.queryObjectClass, 6U, @"5.0 < double2");
  1591. RLMAssertCount(self.queryObjectClass, 2U, @"5.55 >= double2");
  1592. RLMAssertCount(self.queryObjectClass, 6U, @"5.55 <= double2");
  1593. RLMAssertCount(self.queryObjectClass, 1U, @"'a' == string2");
  1594. RLMAssertCount(self.queryObjectClass, 6U, @"'a' != string2");
  1595. RLMAssertThrowsWithReasonMatching([self.queryObjectClass objectsWhere:@"'Realm' CONTAINS string1"].count,
  1596. @"Operator 'CONTAINS' is not supported .* right side");
  1597. RLMAssertThrowsWithReasonMatching([self.queryObjectClass objectsWhere:@"'Amazon' BEGINSWITH string2"].count,
  1598. @"Operator 'BEGINSWITH' is not supported .* right side");
  1599. RLMAssertThrowsWithReasonMatching([self.queryObjectClass objectsWhere:@"'Tuba' ENDSWITH string1"].count,
  1600. @"Operator 'ENDSWITH' is not supported .* right side");
  1601. }
  1602. - (void)testLinksToDeletedOrMovedObject
  1603. {
  1604. RLMRealm *realm = [self realm];
  1605. [realm beginWriteTransaction];
  1606. DogObject *fido = [DogObject createInRealm:realm withValue:@[ @"Fido", @3 ]];
  1607. [OwnerObject createInRealm:realm withValue:@[ @"Fido's owner", fido ]];
  1608. DogObject *rex = [DogObject createInRealm:realm withValue:@[ @"Rex", @2 ]];
  1609. [OwnerObject createInRealm:realm withValue:@[ @"Rex's owner", rex ]];
  1610. DogObject *spot = [DogObject createInRealm:realm withValue:@[ @"Spot", @2 ]];
  1611. [OwnerObject createInRealm:realm withValue:@[ @"Spot's owner", spot ]];
  1612. [realm commitWriteTransaction];
  1613. RLMResults *fidoQuery = [OwnerObject objectsInRealm:realm where:@"dog == %@", fido];
  1614. RLMResults *rexQuery = [OwnerObject objectsInRealm:realm where:@"dog == %@", rex];
  1615. RLMResults *spotQuery = [OwnerObject objectsInRealm:realm where:@"dog == %@", spot];
  1616. [realm beginWriteTransaction];
  1617. [realm deleteObject:fido];
  1618. [realm commitWriteTransaction];
  1619. // Fido was removed, so we should not find his owner.
  1620. XCTAssertEqual(0u, fidoQuery.count);
  1621. // Rex's owner should be found as the row was not touched.
  1622. XCTAssertEqual(1u, rexQuery.count);
  1623. XCTAssertEqualObjects(@"Rex's owner", [rexQuery.firstObject name]);
  1624. // Spot's owner should be found, despite Spot's row having moved.
  1625. XCTAssertEqual(1u, spotQuery.count);
  1626. XCTAssertEqualObjects(@"Spot's owner", [spotQuery.firstObject name]);
  1627. }
  1628. - (void)testQueryOnDeletedArrayProperty
  1629. {
  1630. RLMRealm *realm = [self realm];
  1631. [realm beginWriteTransaction];
  1632. IntObject *io = [IntObject createInRealm:realm withValue:@[@0]];
  1633. ArrayPropertyObject *array = [ArrayPropertyObject createInRealm:realm withValue:@[@"", @[], @[io]]];
  1634. [realm commitWriteTransaction];
  1635. RLMResults *results = [array.intArray objectsWhere:@"TRUEPREDICATE"];
  1636. XCTAssertEqual(1U, results.count);
  1637. [realm beginWriteTransaction];
  1638. [realm deleteObject:array];
  1639. [realm commitWriteTransaction];
  1640. XCTAssertEqual(0U, results.count);
  1641. XCTAssertNil(results.firstObject);
  1642. }
  1643. - (void)testSubqueries
  1644. {
  1645. RLMRealm *realm = [self realm];
  1646. [realm beginWriteTransaction];
  1647. CompanyObject *first = [CompanyObject createInRealm:realm
  1648. withValue:@[@"first company", @[@{@"name": @"John", @"age": @30, @"hired": @NO},
  1649. @{@"name": @"Jill", @"age": @40, @"hired": @YES},
  1650. @{@"name": @"Joe", @"age": @40, @"hired": @YES}]]];
  1651. CompanyObject *second = [CompanyObject createInRealm:realm
  1652. withValue:@[@"second company", @[@{@"name": @"Bill", @"age": @35, @"hired": @YES},
  1653. @{@"name": @"Don", @"age": @45, @"hired": @NO},
  1654. @{@"name": @"Tim", @"age": @60, @"hired": @NO}]]];
  1655. [LinkToCompanyObject createInRealm:realm withValue:@[ first ]];
  1656. [LinkToCompanyObject createInRealm:realm withValue:@[ second ]];
  1657. [realm commitWriteTransaction];
  1658. RLMAssertCount(CompanyObject, 1U, @"SUBQUERY(employees, $employee, $employee.age > 30 AND $employee.hired = FALSE).@count > 0");
  1659. RLMAssertCount(CompanyObject, 2U, @"SUBQUERY(employees, $employee, $employee.age < 30 AND $employee.hired = TRUE).@count == 0");
  1660. RLMAssertCount(LinkToCompanyObject, 1U, @"SUBQUERY(company.employees, $employee, $employee.age > 30 AND $employee.hired = FALSE).@count > 0");
  1661. RLMAssertCount(LinkToCompanyObject, 2U, @"SUBQUERY(company.employees, $employee, $employee.age < 30 AND $employee.hired = TRUE).@count == 0");
  1662. }
  1663. - (void)testLinkingObjects {
  1664. RLMRealm *realm = [self realm];
  1665. [realm beginWriteTransaction];
  1666. PersonObject *hannah = [PersonObject createInRealm:realm withValue:@[ @"Hannah", @0 ]];
  1667. PersonObject *elijah = [PersonObject createInRealm:realm withValue:@[ @"Elijah", @3 ]];
  1668. PersonObject *mark = [PersonObject createInRealm:realm withValue:@[ @"Mark", @30, @[ hannah ]]];
  1669. PersonObject *jason = [PersonObject createInRealm:realm withValue:@[ @"Jason", @31, @[ elijah ]]];
  1670. PersonObject *diane = [PersonObject createInRealm:realm withValue:@[ @"Diane", @29, @[ hannah ]]];
  1671. PersonObject *carol = [PersonObject createInRealm:realm withValue:@[ @"Carol", @31 ]];
  1672. PersonObject *michael = [PersonObject createInRealm:realm withValue:@[ @"Michael", @57, @[ jason, mark ]]];
  1673. PersonObject *raewynne = [PersonObject createInRealm:realm withValue:@[ @"Raewynne", @57, @[ jason, mark ]]];
  1674. PersonObject *don = [PersonObject createInRealm:realm withValue:@[ @"Don", @64, @[ carol, diane ]]];
  1675. PersonObject *diane_sr = [PersonObject createInRealm:realm withValue:@[ @"Diane", @60, @[ carol, diane ]]];
  1676. [realm commitWriteTransaction];
  1677. NSArray *(^asArray)(RLMResults *) = ^(RLMResults *results) {
  1678. return [[self evaluate:results] valueForKeyPath:@"self"];
  1679. };
  1680. // People that have a parent with a name that starts with 'M'.
  1681. RLMResults *r1 = [PersonObject objectsWhere:@"ANY parents.name BEGINSWITH 'M'"];
  1682. XCTAssertEqualObjects(asArray(r1), (@[ hannah, mark, jason ]));
  1683. // People that have a grandparent with a name that starts with 'M'.
  1684. RLMResults *r2 = [PersonObject objectsWhere:@"ANY parents.parents.name BEGINSWITH 'M'"];
  1685. XCTAssertEqualObjects(asArray(r2), (@[ hannah, elijah ]));
  1686. // People that have children that have a parent named Diane.
  1687. RLMResults *r3 = [PersonObject objectsWhere:@"ANY children.parents.name == 'Diane'"];
  1688. XCTAssertEqualObjects(asArray(r3), (@[ mark, diane, don, diane_sr ]));
  1689. // People that have children that have a grandparent named Don.
  1690. RLMResults *r4 = [PersonObject objectsWhere:@"ANY children.parents.parents.name == 'Don'"];
  1691. XCTAssertEqualObjects(asArray(r4), (@[ mark, diane ]));
  1692. // People whose parents have an average age of < 60.
  1693. RLMResults *r5 = [PersonObject objectsWhere:@"parents.@avg.age < 60"];
  1694. XCTAssertEqualObjects(asArray(r5), (@[ hannah, elijah, mark, jason ]));
  1695. // People that have at least one sibling.
  1696. RLMResults *r6 = [PersonObject objectsWhere:@"SUBQUERY(parents, $parent, $parent.children.@count > 1).@count > 0"];
  1697. XCTAssertEqualObjects(asArray(r6), (@[ mark, jason, diane, carol ]));
  1698. // People that have Raewynne as a parent.
  1699. RLMResults *r7 = [PersonObject objectsWhere:@"ANY parents == %@", raewynne];
  1700. XCTAssertEqualObjects(asArray(r7), (@[ mark, jason ]));
  1701. // People that have Mark as a child.
  1702. RLMResults *r8 = [PersonObject objectsWhere:@"ANY children == %@", mark];
  1703. XCTAssertEqualObjects(asArray(r8), (@[ michael, raewynne ]));
  1704. // People that have Michael as a grandparent.
  1705. RLMResults *r9 = [PersonObject objectsWhere:@"ANY parents.parents == %@", michael];
  1706. XCTAssertEqualObjects(asArray(r9), (@[ hannah, elijah ]));
  1707. // People that have Hannah as a grandchild.
  1708. RLMResults *r10 = [PersonObject objectsWhere:@"ANY children.children == %@", hannah];
  1709. XCTAssertEqualObjects(asArray(r10), (@[ michael, raewynne, don, diane_sr ]));
  1710. // People that have no listed parents.
  1711. RLMResults *r11 = [PersonObject objectsWhere:@"parents.@count == 0"];
  1712. XCTAssertEqualObjects(asArray(r11), (@[ michael, raewynne, don, diane_sr ]));
  1713. // No links are equal to a detached row accessor.
  1714. RLMResults *r12 = [PersonObject objectsWhere:@"ANY parents == %@", [PersonObject new]];
  1715. XCTAssertEqualObjects(asArray(r12), (@[ ]));
  1716. // All links are not equal to a detached row accessor so this will match all rows that are linked to.
  1717. RLMResults *r13 = [PersonObject objectsWhere:@"ANY parents != %@", [PersonObject new]];
  1718. XCTAssertEqualObjects(asArray(r13), (@[ hannah, elijah, mark, jason, diane, carol ]));
  1719. // Linking objects cannot contain null so their members cannot be compared with null.
  1720. XCTAssertThrows([PersonObject objectsWhere:@"ANY parents == NULL"]);
  1721. // People that have a parent under the age of 31 where that parent has a parent over the age of 35 whose name is Michael.
  1722. RLMResults *r14 = [PersonObject objectsWhere:@"SUBQUERY(parents, $p1, $p1.age < 31 AND SUBQUERY($p1.parents, $p2, $p2.age > 35 AND $p2.name == 'Michael').@count > 0).@count > 0"];
  1723. XCTAssertEqualObjects(asArray(r14), (@[ hannah ]));
  1724. // Add a new link and verify that the existing results update as expected.
  1725. __block PersonObject *mackenzie;
  1726. [realm transactionWithBlock:^{
  1727. mackenzie = [PersonObject createInRealm:realm withValue:@[ @"Mackenzie", @0 ]];
  1728. [jason.children addObject:mackenzie];
  1729. }];
  1730. // People that have a parent with a name that starts with 'M'.
  1731. XCTAssertEqualObjects(asArray(r1), (@[ hannah, mark, jason ]));
  1732. // People that have a grandparent with a name that starts with 'M'.
  1733. XCTAssertEqualObjects(asArray(r2), (@[ hannah, elijah, mackenzie ]));
  1734. // People that have children that have a parent named Diane.
  1735. XCTAssertEqualObjects(asArray(r3), (@[ mark, diane, don, diane_sr ]));
  1736. // People that have children that have a grandparent named Don.
  1737. XCTAssertEqualObjects(asArray(r4), (@[ mark, diane ]));
  1738. // People whose parents have an average age of < 60.
  1739. XCTAssertEqualObjects(asArray(r5), (@[ hannah, elijah, mark, jason, mackenzie ]));
  1740. // People that have at least one sibling.
  1741. XCTAssertEqualObjects(asArray(r6), (@[ elijah, mark, jason, diane, carol, mackenzie ]));
  1742. // People that have Raewynne as a parent.
  1743. XCTAssertEqualObjects(asArray(r7), (@[ mark, jason ]));
  1744. // People that have Mark as a child.
  1745. XCTAssertEqualObjects(asArray(r8), (@[ michael, raewynne ]));
  1746. // People that have Michael as a grandparent.
  1747. XCTAssertEqualObjects(asArray(r9), (@[ hannah, elijah, mackenzie ]));
  1748. // People that have Hannah as a grandchild.
  1749. XCTAssertEqualObjects(asArray(r10), (@[ michael, raewynne, don, diane_sr ]));
  1750. // People that have no listed parents.
  1751. XCTAssertEqualObjects(asArray(r11), (@[ michael, raewynne, don, diane_sr ]));
  1752. // No links are equal to a detached row accessor.
  1753. XCTAssertEqualObjects(asArray(r12), (@[ ]));
  1754. // All links are not equal to a detached row accessor so this will match all rows that are linked to.
  1755. XCTAssertEqualObjects(asArray(r13), (@[ hannah, elijah, mark, jason, diane, carol, mackenzie ]));
  1756. // People that have a parent under the age of 31 where that parent has a parent over the age of 35 whose name is Michael.
  1757. XCTAssertEqualObjects(asArray(r14), (@[ hannah ]));
  1758. }
  1759. - (void)testCountOnCollection {
  1760. RLMRealm *realm = [self realm];
  1761. [realm beginWriteTransaction];
  1762. IntegerArrayPropertyObject *arr = [IntegerArrayPropertyObject createInRealm:realm withValue:@[ @1, @[]]];
  1763. [arr.array addObject:[IntObject createInRealm:realm withValue:@[ @456 ]]];
  1764. arr = [IntegerArrayPropertyObject createInRealm:realm withValue:@[ @2, @[]]];
  1765. [arr.array addObject:[IntObject createInRealm:realm withValue:@[ @1 ]]];
  1766. [arr.array addObject:[IntObject createInRealm:realm withValue:@[ @2 ]]];
  1767. [arr.array addObject:[IntObject createInRealm:realm withValue:@[ @3 ]]];
  1768. arr = [IntegerArrayPropertyObject createInRealm:realm withValue:@[ @0, @[]]];
  1769. [realm commitWriteTransaction];
  1770. RLMAssertCount(IntegerArrayPropertyObject, 2U, @"array.@count > 0");
  1771. RLMAssertCount(IntegerArrayPropertyObject, 1U, @"array.@count == 3");
  1772. RLMAssertCount(IntegerArrayPropertyObject, 1U, @"array.@count < 1");
  1773. RLMAssertCount(IntegerArrayPropertyObject, 2U, @"0 < array.@count");
  1774. RLMAssertCount(IntegerArrayPropertyObject, 1U, @"3 == array.@count");
  1775. RLMAssertCount(IntegerArrayPropertyObject, 1U, @"1 > array.@count");
  1776. RLMAssertCount(IntegerArrayPropertyObject, 2U, @"array.@count == number");
  1777. RLMAssertCount(IntegerArrayPropertyObject, 1U, @"array.@count > number");
  1778. RLMAssertCount(IntegerArrayPropertyObject, 1U, @"number < array.@count");
  1779. // We do not yet handle collection operations on both sides of the comparison.
  1780. RLMAssertThrowsWithReasonMatching(([IntegerArrayPropertyObject objectsWhere:@"array.@count == array.@count"]),
  1781. @"aggregate operations cannot be compared with other aggregate operations");
  1782. RLMAssertThrowsWithReasonMatching(([IntegerArrayPropertyObject objectsWhere:@"array.@count.foo.bar != 0"]),
  1783. @"single level key");
  1784. RLMAssertThrowsWithReasonMatching(([IntegerArrayPropertyObject objectsWhere:@"array.@count.intCol > 0"]),
  1785. @"@count does not have any properties");
  1786. RLMAssertThrowsWithReasonMatching(([IntegerArrayPropertyObject objectsWhere:@"array.@count != 'Hello'"]),
  1787. @"@count can only be compared with a numeric value");
  1788. }
  1789. - (void)testAggregateCollectionOperators {
  1790. RLMRealm *realm = [self realm];
  1791. [realm beginWriteTransaction];
  1792. IntegerArrayPropertyObject *arr = [IntegerArrayPropertyObject createInRealm:realm withValue:@[ @1111, @[] ]];
  1793. [arr.array addObject:[IntObject createInRealm:realm withValue:@[ @1234 ]]];
  1794. [arr.array addObject:[IntObject createInRealm:realm withValue:@[ @2 ]]];
  1795. [arr.array addObject:[IntObject createInRealm:realm withValue:@[ @-12345 ]]];
  1796. arr = [IntegerArrayPropertyObject createInRealm:realm withValue:@[ @2222, @[] ]];
  1797. [arr.array addObject:[IntObject createInRealm:realm withValue:@[ @100 ]]];
  1798. arr = [IntegerArrayPropertyObject createInRealm:realm withValue:@[ @3333, @[] ]];
  1799. [realm commitWriteTransaction];
  1800. RLMAssertCount(IntegerArrayPropertyObject, 1U, @"array.@min.intCol == -12345");
  1801. RLMAssertCount(IntegerArrayPropertyObject, 1U, @"array.@min.intCol == 100");
  1802. RLMAssertCount(IntegerArrayPropertyObject, 2U, @"array.@min.intCol < 1000");
  1803. RLMAssertCount(IntegerArrayPropertyObject, 1U, @"array.@min.intCol > -1000");
  1804. RLMAssertCount(IntegerArrayPropertyObject, 1U, @"array.@max.intCol == 1234");
  1805. RLMAssertCount(IntegerArrayPropertyObject, 1U, @"array.@max.intCol == 100");
  1806. RLMAssertCount(IntegerArrayPropertyObject, 2U, @"array.@max.intCol > -1000");
  1807. RLMAssertCount(IntegerArrayPropertyObject, 1U, @"array.@max.intCol > 1000");
  1808. RLMAssertCount(IntegerArrayPropertyObject, 1U, @"array.@sum.intCol == 100");
  1809. RLMAssertCount(IntegerArrayPropertyObject, 1U, @"array.@sum.intCol == -11109");
  1810. RLMAssertCount(IntegerArrayPropertyObject, 1U, @"array.@sum.intCol == 0");
  1811. RLMAssertCount(IntegerArrayPropertyObject, 2U, @"array.@sum.intCol > -50");
  1812. RLMAssertCount(IntegerArrayPropertyObject, 2U, @"array.@sum.intCol < 50");
  1813. RLMAssertCount(IntegerArrayPropertyObject, 1U, @"array.@avg.intCol == 100");
  1814. RLMAssertCount(IntegerArrayPropertyObject, 1U, @"array.@avg.intCol == -3703.0");
  1815. RLMAssertCount(IntegerArrayPropertyObject, 0U, @"array.@avg.intCol == 0");
  1816. RLMAssertCount(IntegerArrayPropertyObject, 1U, @"array.@avg.intCol < -50");
  1817. RLMAssertCount(IntegerArrayPropertyObject, 1U, @"array.@avg.intCol > 50");
  1818. RLMAssertCount(IntegerArrayPropertyObject, 2U, @"array.@min.intCol < number");
  1819. RLMAssertCount(IntegerArrayPropertyObject, 2U, @"number > array.@min.intCol");
  1820. RLMAssertCount(IntegerArrayPropertyObject, 1U, @"array.@max.intCol < number");
  1821. RLMAssertCount(IntegerArrayPropertyObject, 1U, @"number > array.@max.intCol");
  1822. RLMAssertCount(IntegerArrayPropertyObject, 2U, @"array.@avg.intCol < number");
  1823. RLMAssertCount(IntegerArrayPropertyObject, 2U, @"number > array.@avg.intCol");
  1824. // We do not yet handle collection operations on both sides of the comparison.
  1825. RLMAssertThrowsWithReasonMatching(([IntegerArrayPropertyObject objectsWhere:@"array.@min.intCol == array.@min.intCol"]), @"aggregate operations cannot be compared with other aggregate operations");
  1826. RLMAssertThrowsWithReasonMatching(([IntegerArrayPropertyObject objectsWhere:@"array.@min.intCol.foo.bar == 1.23"]), @"single level key");
  1827. RLMAssertThrowsWithReasonMatching(([IntegerArrayPropertyObject objectsWhere:@"array.@max.intCol.foo.bar == 1.23"]), @"single level key");
  1828. RLMAssertThrowsWithReasonMatching(([IntegerArrayPropertyObject objectsWhere:@"array.@sum.intCol.foo.bar == 1.23"]), @"single level key");
  1829. RLMAssertThrowsWithReasonMatching(([IntegerArrayPropertyObject objectsWhere:@"array.@avg.intCol.foo.bar == 1.23"]), @"single level key");
  1830. // Average is omitted from this test as its result is always a double.
  1831. RLMAssertThrowsWithReasonMatching(([IntegerArrayPropertyObject objectsWhere:@"array.@min.intCol == 1.23"]), @"@min.*type int cannot be compared");
  1832. RLMAssertThrowsWithReasonMatching(([IntegerArrayPropertyObject objectsWhere:@"array.@max.intCol == 1.23"]), @"@max.*type int cannot be compared");
  1833. RLMAssertThrowsWithReasonMatching(([IntegerArrayPropertyObject objectsWhere:@"array.@sum.intCol == 1.23"]), @"@sum.*type int cannot be compared");
  1834. }
  1835. @end
  1836. @interface NullQueryTests : QueryTests
  1837. @end
  1838. @implementation NullQueryTests
  1839. - (Class)queryObjectClass {
  1840. return [NullQueryObject class];
  1841. }
  1842. - (void)testQueryOnNullableStringColumn {
  1843. void (^testWithStringClass)(Class) = ^(Class stringObjectClass) {
  1844. RLMRealm *realm = [self realm];
  1845. [realm transactionWithBlock:^{
  1846. [stringObjectClass createInRealm:realm withValue:@[@"a"]];
  1847. [stringObjectClass createInRealm:realm withValue:@[NSNull.null]];
  1848. [stringObjectClass createInRealm:realm withValue:@[@"b"]];
  1849. [stringObjectClass createInRealm:realm withValue:@[NSNull.null]];
  1850. [stringObjectClass createInRealm:realm withValue:@[@""]];
  1851. }];
  1852. RLMResults *allObjects = [stringObjectClass allObjectsInRealm:realm];
  1853. XCTAssertEqual(5U, allObjects.count);
  1854. RLMResults *nilStrings = [stringObjectClass objectsInRealm:realm where:@"stringCol = NULL"];
  1855. XCTAssertEqual(2U, nilStrings.count);
  1856. XCTAssertEqualObjects((@[NSNull.null, NSNull.null]), [nilStrings valueForKey:@"stringCol"]);
  1857. RLMResults *nilLikeStrings = [stringObjectClass objectsInRealm:realm where:@"stringCol LIKE NULL"];
  1858. XCTAssertEqual(2U, nilLikeStrings.count);
  1859. XCTAssertEqualObjects((@[NSNull.null, NSNull.null]), [nilLikeStrings valueForKey:@"stringCol"]);
  1860. RLMResults *nonNilStrings = [stringObjectClass objectsInRealm:realm where:@"stringCol != NULL"];
  1861. XCTAssertEqual(3U, nonNilStrings.count);
  1862. XCTAssertEqualObjects((@[@"a", @"b", @""]), [nonNilStrings valueForKey:@"stringCol"]);
  1863. RLMResults *nonNilLikeStrings = [stringObjectClass objectsInRealm:realm where:@"NOT stringCol LIKE NULL"];
  1864. XCTAssertEqual(3U, nonNilLikeStrings.count);
  1865. XCTAssertEqualObjects((@[@"a", @"b", @""]), [nonNilLikeStrings valueForKey:@"stringCol"]);
  1866. RLMAssertCount(stringObjectClass, 3U, @"stringCol IN {NULL, 'a'}");
  1867. RLMAssertCount(stringObjectClass, 1U, @"stringCol CONTAINS 'a'");
  1868. RLMAssertCount(stringObjectClass, 1U, @"stringCol BEGINSWITH 'a'");
  1869. RLMAssertCount(stringObjectClass, 1U, @"stringCol ENDSWITH 'a'");
  1870. RLMAssertCount(stringObjectClass, 1U, @"stringCol LIKE 'a'");
  1871. RLMAssertCount(stringObjectClass, 0U, @"stringCol CONTAINS 'z'");
  1872. RLMAssertCount(stringObjectClass, 0U, @"stringCol LIKE 'z'");
  1873. RLMAssertCount(stringObjectClass, 1U, @"stringCol = ''");
  1874. RLMResults *sorted = [[stringObjectClass allObjectsInRealm:realm] sortedResultsUsingKeyPath:@"stringCol" ascending:YES];
  1875. XCTAssertEqualObjects((@[NSNull.null, NSNull.null, @"", @"a", @"b"]), [sorted valueForKey:@"stringCol"]);
  1876. XCTAssertEqualObjects((@[@"b", @"a", @"", NSNull.null, NSNull.null]), [[sorted sortedResultsUsingKeyPath:@"stringCol" ascending:NO] valueForKey:@"stringCol"]);
  1877. [realm transactionWithBlock:^{
  1878. [realm deleteObject:[stringObjectClass allObjectsInRealm:realm].firstObject];
  1879. }];
  1880. XCTAssertEqual(2U, nilStrings.count);
  1881. XCTAssertEqual(2U, nonNilStrings.count);
  1882. XCTAssertEqualObjects(@[], [[stringObjectClass objectsInRealm:realm where:@"stringCol CONTAINS ''"] valueForKey:@"stringCol"]);
  1883. XCTAssertEqualObjects(@[], [[stringObjectClass objectsInRealm:realm where:@"stringCol BEGINSWITH ''"] valueForKey:@"stringCol"]);
  1884. XCTAssertEqualObjects(@[], [[stringObjectClass objectsInRealm:realm where:@"stringCol ENDSWITH ''"] valueForKey:@"stringCol"]);
  1885. XCTAssertEqualObjects([nonNilStrings valueForKey:@"stringCol"],
  1886. [[stringObjectClass objectsInRealm:realm where:@"stringCol LIKE '*'"] valueForKey:@"stringCol"]);
  1887. XCTAssertEqualObjects(@[], [[stringObjectClass objectsInRealm:realm where:@"stringCol CONTAINS[c] ''"] valueForKey:@"stringCol"]);
  1888. XCTAssertEqualObjects(@[], [[stringObjectClass objectsInRealm:realm where:@"stringCol BEGINSWITH[c] ''"] valueForKey:@"stringCol"]);
  1889. XCTAssertEqualObjects(@[], [[stringObjectClass objectsInRealm:realm where:@"stringCol ENDSWITH[c] ''"] valueForKey:@"stringCol"]);
  1890. XCTAssertEqualObjects([nonNilStrings valueForKey:@"stringCol"],
  1891. [[stringObjectClass objectsInRealm:realm where:@"stringCol LIKE[c] '*'"] valueForKey:@"stringCol"]);
  1892. XCTAssertEqualObjects(@[], [[stringObjectClass objectsInRealm:realm where:@"stringCol CONTAINS[d] ''"] valueForKey:@"stringCol"]);
  1893. XCTAssertEqualObjects(@[], [[stringObjectClass objectsInRealm:realm where:@"stringCol BEGINSWITH[d] ''"] valueForKey:@"stringCol"]);
  1894. XCTAssertEqualObjects(@[], [[stringObjectClass objectsInRealm:realm where:@"stringCol ENDSWITH[d] ''"] valueForKey:@"stringCol"]);
  1895. XCTAssertEqualObjects(@[], [[stringObjectClass objectsInRealm:realm where:@"stringCol CONTAINS[cd] ''"] valueForKey:@"stringCol"]);
  1896. XCTAssertEqualObjects(@[], [[stringObjectClass objectsInRealm:realm where:@"stringCol BEGINSWITH[cd] ''"] valueForKey:@"stringCol"]);
  1897. XCTAssertEqualObjects(@[], [[stringObjectClass objectsInRealm:realm where:@"stringCol ENDSWITH[cd] ''"] valueForKey:@"stringCol"]);
  1898. XCTAssertEqualObjects(@[], ([[stringObjectClass objectsInRealm:realm where:@"stringCol CONTAINS %@", @"\0"] valueForKey:@"self"]));
  1899. XCTAssertEqualObjects(@[], ([[stringObjectClass objectsInRealm:realm where:@"stringCol CONTAINS NULL"] valueForKey:@"stringCol"]));
  1900. XCTAssertEqualObjects(@[], ([[stringObjectClass objectsInRealm:realm where:@"stringCol CONTAINS[c] NULL"] valueForKey:@"stringCol"]));
  1901. XCTAssertEqualObjects(@[], ([[stringObjectClass objectsInRealm:realm where:@"stringCol CONTAINS[d] NULL"] valueForKey:@"stringCol"]));
  1902. XCTAssertEqualObjects(@[], ([[stringObjectClass objectsInRealm:realm where:@"stringCol CONTAINS[cd] NULL"] valueForKey:@"stringCol"]));
  1903. };
  1904. testWithStringClass([StringObject class]);
  1905. testWithStringClass([IndexedStringObject class]);
  1906. }
  1907. - (void)testQueryingOnLinkToNullableStringColumn {
  1908. void (^testWithStringClass)(Class, Class) = ^(Class stringLinkClass, Class stringObjectClass) {
  1909. RLMRealm *realm = [self realm];
  1910. [realm transactionWithBlock:^{
  1911. [stringLinkClass createInRealm:realm withValue:@[[stringObjectClass createInRealm:realm withValue:@[@"a"]]]];
  1912. [stringLinkClass createInRealm:realm withValue:@[[stringObjectClass createInRealm:realm withValue:@[NSNull.null]]]];
  1913. [stringLinkClass createInRealm:realm withValue:@[[stringObjectClass createInRealm:realm withValue:@[@"b"]]]];
  1914. [stringLinkClass createInRealm:realm withValue:@[[stringObjectClass createInRealm:realm withValue:@[NSNull.null]]]];
  1915. [stringLinkClass createInRealm:realm withValue:@[[stringObjectClass createInRealm:realm withValue:@[@""]]]];
  1916. }];
  1917. RLMResults *nilStrings = [stringLinkClass objectsInRealm:realm where:@"objectCol.stringCol = NULL"];
  1918. XCTAssertEqual(2U, nilStrings.count);
  1919. XCTAssertEqualObjects((@[NSNull.null, NSNull.null]), [nilStrings valueForKeyPath:@"objectCol.stringCol"]);
  1920. RLMResults *nilLikeStrings = [stringLinkClass objectsInRealm:realm where:@"objectCol.stringCol LIKE NULL"];
  1921. XCTAssertEqual(2U, nilLikeStrings.count);
  1922. XCTAssertEqualObjects((@[NSNull.null, NSNull.null]), [nilLikeStrings valueForKeyPath:@"objectCol.stringCol"]);
  1923. RLMResults *nonNilStrings = [stringLinkClass objectsInRealm:realm where:@"objectCol.stringCol != NULL"];
  1924. XCTAssertEqual(3U, nonNilStrings.count);
  1925. XCTAssertEqualObjects((@[@"a", @"b", @""]), [nonNilStrings valueForKeyPath:@"objectCol.stringCol"]);
  1926. RLMResults *nonNilLikeStrings = [stringLinkClass objectsInRealm:realm where:@"NOT objectCol.stringCol LIKE NULL"];
  1927. XCTAssertEqual(3U, nonNilLikeStrings.count);
  1928. XCTAssertEqualObjects((@[@"a", @"b", @""]), [nonNilLikeStrings valueForKeyPath:@"objectCol.stringCol"]);
  1929. RLMAssertCount(stringLinkClass, 3U, @"objectCol.stringCol IN {NULL, 'a'}");
  1930. RLMAssertCount(stringLinkClass, 1U, @"objectCol.stringCol CONTAINS 'a'");
  1931. RLMAssertCount(stringLinkClass, 1U, @"objectCol.stringCol BEGINSWITH 'a'");
  1932. RLMAssertCount(stringLinkClass, 1U, @"objectCol.stringCol ENDSWITH 'a'");
  1933. RLMAssertCount(stringLinkClass, 1U, @"objectCol.stringCol LIKE 'a'");
  1934. RLMAssertCount(stringLinkClass, 0U, @"objectCol.stringCol LIKE 'c'");
  1935. RLMAssertCount(stringLinkClass, 0U, @"objectCol.stringCol CONTAINS 'z'");
  1936. RLMAssertCount(stringLinkClass, 1U, @"objectCol.stringCol = ''");
  1937. };
  1938. testWithStringClass([LinkStringObject class], [StringObject class]);
  1939. testWithStringClass([LinkIndexedStringObject class], [IndexedStringObject class]);
  1940. }
  1941. - (void)testSortingColumnsWithNull {
  1942. RLMRealm *realm = [self realm];
  1943. [realm beginWriteTransaction];
  1944. {
  1945. NumberObject *no1 = [NumberObject createInRealm:realm withValue:@[@1, @1.1f, @1.1, @YES]];
  1946. NumberObject *noNull = [NumberObject createInRealm:realm withValue:@[NSNull.null, NSNull.null, NSNull.null, NSNull.null]];
  1947. NumberObject *no0 = [NumberObject createInRealm:realm withValue:@[@0, @0.0f, @0.0, @NO]];
  1948. for (RLMProperty *property in [[NumberObject alloc] init].objectSchema.properties) {
  1949. NSString *name = property.name;
  1950. RLMResults *ascending = [[NumberObject allObjectsInRealm:realm] sortedResultsUsingKeyPath:name ascending:YES];
  1951. XCTAssertEqualObjects([ascending valueForKey:name], ([@[noNull, no0, no1] valueForKey:name]));
  1952. RLMResults *descending = [[NumberObject allObjectsInRealm:realm] sortedResultsUsingKeyPath:name ascending:NO];
  1953. XCTAssertEqualObjects([descending valueForKey:name], ([@[no1, no0, noNull] valueForKey:name]));
  1954. }
  1955. }
  1956. {
  1957. DateObject *doPositive = [DateObject createInRealm:realm withValue:@[[NSDate dateWithTimeIntervalSince1970:100]]];
  1958. DateObject *doNegative = [DateObject createInRealm:realm withValue:@[[NSDate dateWithTimeIntervalSince1970:-100]]];
  1959. DateObject *doZero = [DateObject createInRealm:realm withValue:@[[NSDate dateWithTimeIntervalSince1970:0]]];
  1960. DateObject *doNull = [DateObject createInRealm:realm withValue:@[NSNull.null]];
  1961. RLMResults *ascending = [[DateObject allObjectsInRealm:realm] sortedResultsUsingKeyPath:@"dateCol" ascending:YES];
  1962. XCTAssertEqualObjects([ascending valueForKey:@"dateCol"], ([@[doNull, doNegative, doZero, doPositive] valueForKey:@"dateCol"]));
  1963. RLMResults *descending = [[DateObject allObjectsInRealm:realm] sortedResultsUsingKeyPath:@"dateCol" ascending:NO];
  1964. XCTAssertEqualObjects([descending valueForKey:@"dateCol"], ([@[doPositive, doZero, doNegative, doNull] valueForKey:@"dateCol"]));
  1965. }
  1966. {
  1967. StringObject *soA = [StringObject createInRealm:realm withValue:@[@"A"]];
  1968. StringObject *soEmpty = [StringObject createInRealm:realm withValue:@[@""]];
  1969. StringObject *soB = [StringObject createInRealm:realm withValue:@[@"B"]];
  1970. StringObject *soNull = [StringObject createInRealm:realm withValue:@[NSNull.null]];
  1971. StringObject *soAB = [StringObject createInRealm:realm withValue:@[@"AB"]];
  1972. RLMResults *ascending = [[StringObject allObjectsInRealm:realm] sortedResultsUsingKeyPath:@"stringCol" ascending:YES];
  1973. XCTAssertEqualObjects([ascending valueForKey:@"stringCol"], ([@[soNull, soEmpty, soA, soAB, soB] valueForKey:@"stringCol"]));
  1974. RLMResults *descending = [[StringObject allObjectsInRealm:realm] sortedResultsUsingKeyPath:@"stringCol" ascending:NO];
  1975. XCTAssertEqualObjects([descending valueForKey:@"stringCol"], ([@[soB, soAB, soA, soEmpty, soNull] valueForKey:@"stringCol"]));
  1976. }
  1977. [realm cancelWriteTransaction];
  1978. }
  1979. struct NullTestData {
  1980. __unsafe_unretained NSString *propertyName;
  1981. __unsafe_unretained NSString *nonMatchingStr;
  1982. __unsafe_unretained NSString *matchingStr;
  1983. __unsafe_unretained id nonMatchingValue;
  1984. __unsafe_unretained id matchingValue;
  1985. bool orderable;
  1986. bool substringOperations;
  1987. };
  1988. - (void)testPrimitiveOperatorsOnAllNullablePropertyTypes {
  1989. RLMRealm *realm = [self realm];
  1990. // nil on LHS is currently not supported by core
  1991. XCTAssertThrows([AllOptionalTypes objectsWhere:@"nil = boolObj"]);
  1992. // These need to be stored in variables because the struct does not retain them
  1993. NSData *matchingData = [@"" dataUsingEncoding:NSUTF8StringEncoding];
  1994. NSData *notMatchingData = [@"a" dataUsingEncoding:NSUTF8StringEncoding];
  1995. NSDate *matchingDate = [NSDate dateWithTimeIntervalSince1970:1];
  1996. NSDate *notMatchingDate = [NSDate dateWithTimeIntervalSince1970:2];
  1997. struct NullTestData data[] = {
  1998. {@"boolObj", @"YES", @"NO", @YES, @NO},
  1999. {@"intObj", @"1", @"0", @1, @0, true},
  2000. {@"floatObj", @"1", @"0", @1, @0, true},
  2001. {@"doubleObj", @"1", @"0", @1, @0, true},
  2002. {@"string", @"'a'", @"''", @"a", @"", false, true},
  2003. {@"data", nil, nil, notMatchingData, matchingData, false, true},
  2004. {@"date", nil, nil, notMatchingDate, matchingDate, true},
  2005. };
  2006. // Assert that the query "prop op value" gives expectedCount results when
  2007. // assembled via string formatting
  2008. #define RLMAssertCountWithString(expectedCount, op, prop, value) \
  2009. do { \
  2010. NSString *queryStr = [NSString stringWithFormat:@"%@ " #op " %@", prop, value]; \
  2011. NSUInteger actual = [AllOptionalTypes objectsWhere:queryStr].count; \
  2012. XCTAssertEqual(expectedCount, actual, @"%@: expected %@, got %@", queryStr, @(expectedCount), @(actual)); \
  2013. } while (0)
  2014. // Assert that the query "prop op value" gives expectedCount results when
  2015. // assembled via predicateWithFormat
  2016. #define RLMAssertCountWithPredicate(expectedCount, op, prop, value) \
  2017. do { \
  2018. NSPredicate *query = [NSPredicate predicateWithFormat:@"%K " #op " %@", prop, value]; \
  2019. NSUInteger actual = [AllOptionalTypes objectsWithPredicate:query].count; \
  2020. XCTAssertEqual(expectedCount, actual, @"%@ " #op " %@: expected %@, got %@", prop, value, @(expectedCount), @(actual)); \
  2021. } while (0)
  2022. // Assert that the given operator gives the expected count for each of the
  2023. // stored value, a different value, and nil
  2024. #define RLMAssertOperator(op, matchingCount, notMatchingCount, nilCount) \
  2025. do { \
  2026. if (d.matchingStr) { \
  2027. RLMAssertCountWithString(matchingCount, op, d.propertyName, d.matchingStr); \
  2028. RLMAssertCountWithString(notMatchingCount, op, d.propertyName, d.nonMatchingStr); \
  2029. } \
  2030. RLMAssertCountWithString(nilCount, op, d.propertyName, nil); \
  2031. \
  2032. RLMAssertCountWithPredicate(matchingCount, op, d.propertyName, d.matchingValue); \
  2033. RLMAssertCountWithPredicate(notMatchingCount, op, d.propertyName, d.nonMatchingValue); \
  2034. RLMAssertCountWithPredicate(nilCount, op, d.propertyName, nil); \
  2035. } while (0)
  2036. // First test with the `matchingValue` stored in each property
  2037. [realm beginWriteTransaction];
  2038. [AllOptionalTypes createInRealm:realm withValue:@[@NO, @0, @0, @0, @"", matchingData, matchingDate]];
  2039. [realm commitWriteTransaction];
  2040. for (size_t i = 0; i < sizeof(data) / sizeof(data[0]); ++i) {
  2041. struct NullTestData d = data[i];
  2042. RLMAssertOperator(=, 1U, 0U, 0U);
  2043. RLMAssertOperator(!=, 0U, 1U, 1U);
  2044. if (d.orderable) {
  2045. RLMAssertOperator(<, 0U, 1U, 0U);
  2046. RLMAssertOperator(<=, 1U, 1U, 0U);
  2047. RLMAssertOperator(>, 0U, 0U, 0U);
  2048. RLMAssertOperator(>=, 1U, 0U, 0U);
  2049. }
  2050. if (d.substringOperations) {
  2051. RLMAssertOperator(BEGINSWITH, 0U, 0U, 0U);
  2052. RLMAssertOperator(ENDSWITH, 0U, 0U, 0U);
  2053. RLMAssertOperator(CONTAINS, 0U, 0U, 0U);
  2054. }
  2055. }
  2056. // Retest with all properties nil
  2057. [realm beginWriteTransaction];
  2058. [realm deleteAllObjects];
  2059. [AllOptionalTypes createInRealm:realm withValue:@[NSNull.null, NSNull.null,
  2060. NSNull.null, NSNull.null,
  2061. NSNull.null, NSNull.null,
  2062. NSNull.null]];
  2063. [realm commitWriteTransaction];
  2064. for (size_t i = 0; i < sizeof(data) / sizeof(data[0]); ++i) {
  2065. struct NullTestData d = data[i];
  2066. RLMAssertOperator(=, 0U, 0U, 1U);
  2067. RLMAssertOperator(!=, 1U, 1U, 0U);
  2068. if (d.orderable) {
  2069. RLMAssertOperator(<, 0U, 0U, 0U);
  2070. RLMAssertOperator(<=, 0U, 0U, 1U);
  2071. RLMAssertOperator(>, 0U, 0U, 0U);
  2072. RLMAssertOperator(>=, 0U, 0U, 1U);
  2073. }
  2074. if (d.substringOperations) {
  2075. RLMAssertOperator(BEGINSWITH, 0U, 0U, 0U);
  2076. RLMAssertOperator(ENDSWITH, 0U, 0U, 0U);
  2077. RLMAssertOperator(CONTAINS, 0U, 0U, 0U);
  2078. }
  2079. }
  2080. }
  2081. - (void)testINPredicateOnNullWithNonNullValues
  2082. {
  2083. RLMRealm *realm = [self realm];
  2084. [realm beginWriteTransaction];
  2085. [AllOptionalTypes createInRealm:realm withValue:@[@YES, @1, @1, @1, @"abc",
  2086. [@"a" dataUsingEncoding:NSUTF8StringEncoding],
  2087. [NSDate dateWithTimeIntervalSince1970:1]]];
  2088. [realm commitWriteTransaction];
  2089. ////////////////////////
  2090. // Literal Predicates
  2091. ////////////////////////
  2092. // BOOL
  2093. [self testClass:[AllOptionalTypes class] withNormalCount:0 notCount:1 where:@"boolObj IN {NULL}"];
  2094. [self testClass:[AllOptionalTypes class] withNormalCount:1 notCount:0 where:@"boolObj IN {YES}"];
  2095. // int
  2096. [self testClass:[AllOptionalTypes class] withNormalCount:0 notCount:1 where:@"intObj IN {NULL}"];
  2097. [self testClass:[AllOptionalTypes class] withNormalCount:1 notCount:0 where:@"intObj IN {1}"];
  2098. // float
  2099. [self testClass:[AllOptionalTypes class] withNormalCount:0 notCount:1 where:@"floatObj IN {NULL}"];
  2100. [self testClass:[AllOptionalTypes class] withNormalCount:1 notCount:0 where:@"floatObj IN {1}"];
  2101. // double
  2102. [self testClass:[AllOptionalTypes class] withNormalCount:0 notCount:1 where:@"doubleObj IN {NULL}"];
  2103. [self testClass:[AllOptionalTypes class] withNormalCount:1 notCount:0 where:@"doubleObj IN {1}"];
  2104. // NSString
  2105. [self testClass:[AllOptionalTypes class] withNormalCount:0 notCount:1 where:@"string IN {NULL}"];
  2106. [self testClass:[AllOptionalTypes class] withNormalCount:1 notCount:0 where:@"string IN {'abc'}"];
  2107. // NSData
  2108. // Can't represent NSData with NSPredicate literal. See format predicates below
  2109. // NSDate
  2110. // Can't represent NSDate with NSPredicate literal. See format predicates below
  2111. ////////////////////////
  2112. // Format Predicates
  2113. ////////////////////////
  2114. // BOOL
  2115. [self testClass:[AllOptionalTypes class] withNormalCount:0U notCount:1U where:@"boolObj IN %@", @[NSNull.null]];
  2116. [self testClass:[AllOptionalTypes class] withNormalCount:1U notCount:0U where:@"boolObj IN %@", @[@YES]];
  2117. // int
  2118. [self testClass:[AllOptionalTypes class] withNormalCount:0U notCount:1U where:@"intObj IN %@", @[NSNull.null]];
  2119. [self testClass:[AllOptionalTypes class] withNormalCount:1U notCount:0U where:@"intObj IN %@", @[@1]];
  2120. // float
  2121. [self testClass:[AllOptionalTypes class] withNormalCount:0U notCount:1U where:@"floatObj IN %@", @[NSNull.null]];
  2122. [self testClass:[AllOptionalTypes class] withNormalCount:1U notCount:0U where:@"floatObj IN %@", @[@1]];
  2123. // double
  2124. [self testClass:[AllOptionalTypes class] withNormalCount:0U notCount:1U where:@"doubleObj IN %@", @[NSNull.null]];
  2125. [self testClass:[AllOptionalTypes class] withNormalCount:1U notCount:0U where:@"doubleObj IN %@", @[@1]];
  2126. // NSString
  2127. [self testClass:[AllOptionalTypes class] withNormalCount:1U notCount:0U where:@"string IN %@", @[@"abc"]];
  2128. [self testClass:[AllOptionalTypes class] withNormalCount:0U notCount:1U where:@"string IN %@", @[NSNull.null]];
  2129. // NSData
  2130. [self testClass:[AllOptionalTypes class] withNormalCount:0U notCount:1U where:@"data IN %@", @[NSNull.null]];
  2131. [self testClass:[AllOptionalTypes class] withNormalCount:1U notCount:0U where:@"data IN %@", @[[@"a" dataUsingEncoding:NSUTF8StringEncoding]]];
  2132. // NSDate
  2133. [self testClass:[AllOptionalTypes class] withNormalCount:0U notCount:1U where:@"date IN %@", @[NSNull.null]];
  2134. [self testClass:[AllOptionalTypes class] withNormalCount:1U notCount:0U where:@"date IN %@", @[[NSDate dateWithTimeIntervalSince1970:1]]];
  2135. }
  2136. - (void)testINPredicateOnNullWithNullValues
  2137. {
  2138. RLMRealm *realm = [self realm];
  2139. [realm beginWriteTransaction];
  2140. [AllOptionalTypes createInRealm:realm withValue:@[NSNull.null, NSNull.null,
  2141. NSNull.null, NSNull.null,
  2142. NSNull.null, NSNull.null,
  2143. NSNull.null]];
  2144. [realm commitWriteTransaction];
  2145. ////////////////////////
  2146. // Literal Predicates
  2147. ////////////////////////
  2148. // BOOL
  2149. [self testClass:[AllOptionalTypes class] withNormalCount:1 notCount:0 where:@"boolObj IN {NULL}"];
  2150. [self testClass:[AllOptionalTypes class] withNormalCount:0 notCount:1 where:@"boolObj IN {YES}"];
  2151. // int
  2152. [self testClass:[AllOptionalTypes class] withNormalCount:1 notCount:0 where:@"intObj IN {NULL}"];
  2153. [self testClass:[AllOptionalTypes class] withNormalCount:0 notCount:1 where:@"intObj IN {1}"];
  2154. // float
  2155. [self testClass:[AllOptionalTypes class] withNormalCount:1 notCount:0 where:@"floatObj IN {NULL}"];
  2156. [self testClass:[AllOptionalTypes class] withNormalCount:0 notCount:1 where:@"floatObj IN {1}"];
  2157. // double
  2158. [self testClass:[AllOptionalTypes class] withNormalCount:1 notCount:0 where:@"doubleObj IN {NULL}"];
  2159. [self testClass:[AllOptionalTypes class] withNormalCount:0 notCount:1 where:@"doubleObj IN {1}"];
  2160. // NSString
  2161. [self testClass:[AllOptionalTypes class] withNormalCount:1 notCount:0 where:@"string IN {NULL}"];
  2162. [self testClass:[AllOptionalTypes class] withNormalCount:0 notCount:1 where:@"string IN {'abc'}"];
  2163. // NSData
  2164. // Can't represent NSData with NSPredicate literal. See format predicates below
  2165. // NSDate
  2166. // Can't represent NSDate with NSPredicate literal. See format predicates below
  2167. ////////////////////////
  2168. // Format Predicates
  2169. ////////////////////////
  2170. // BOOL
  2171. [self testClass:[AllOptionalTypes class] withNormalCount:1U notCount:0U where:@"boolObj IN %@", @[NSNull.null]];
  2172. [self testClass:[AllOptionalTypes class] withNormalCount:0U notCount:1U where:@"boolObj IN %@", @[@YES]];
  2173. // int
  2174. [self testClass:[AllOptionalTypes class] withNormalCount:1U notCount:0U where:@"intObj IN %@", @[NSNull.null]];
  2175. [self testClass:[AllOptionalTypes class] withNormalCount:0U notCount:1U where:@"intObj IN %@", @[@1]];
  2176. // float
  2177. [self testClass:[AllOptionalTypes class] withNormalCount:1U notCount:0U where:@"floatObj IN %@", @[NSNull.null]];
  2178. [self testClass:[AllOptionalTypes class] withNormalCount:0U notCount:1U where:@"floatObj IN %@", @[@1]];
  2179. // double
  2180. [self testClass:[AllOptionalTypes class] withNormalCount:1U notCount:0U where:@"doubleObj IN %@", @[NSNull.null]];
  2181. [self testClass:[AllOptionalTypes class] withNormalCount:0U notCount:1U where:@"doubleObj IN %@", @[@1]];
  2182. // NSString
  2183. [self testClass:[AllOptionalTypes class] withNormalCount:0U notCount:1U where:@"string IN %@", @[@"abc"]];
  2184. [self testClass:[AllOptionalTypes class] withNormalCount:1U notCount:0U where:@"string IN %@", @[NSNull.null]];
  2185. // NSData
  2186. [self testClass:[AllOptionalTypes class] withNormalCount:1U notCount:0U where:@"data IN %@", @[NSNull.null]];
  2187. [self testClass:[AllOptionalTypes class] withNormalCount:0U notCount:1U where:@"data IN %@", @[[@"a" dataUsingEncoding:NSUTF8StringEncoding]]];
  2188. // NSDate
  2189. [self testClass:[AllOptionalTypes class] withNormalCount:1U notCount:0U where:@"date IN %@", @[NSNull.null]];
  2190. [self testClass:[AllOptionalTypes class] withNormalCount:0U notCount:1U where:@"date IN %@", @[[NSDate dateWithTimeIntervalSince1970:1]]];
  2191. }
  2192. - (void)testQueryOnRenamedProperties {
  2193. RLMRealm *realm = [self realm];
  2194. [realm beginWriteTransaction];
  2195. [RenamedProperties1 createInRealm:realm withValue:@[@1, @"a"]];
  2196. [RenamedProperties2 createInRealm:realm withValue:@[@2, @"b"]];
  2197. [realm commitWriteTransaction];
  2198. [self testClass:[RenamedProperties1 class] withNormalCount:2 notCount:0 where:@"propA != 0"];
  2199. [self testClass:[RenamedProperties1 class] withNormalCount:1 notCount:1 where:@"propA = 1"];
  2200. [self testClass:[RenamedProperties1 class] withNormalCount:1 notCount:1 where:@"propA = 2"];
  2201. [self testClass:[RenamedProperties1 class] withNormalCount:0 notCount:2 where:@"propA = 3"];
  2202. [self testClass:[RenamedProperties2 class] withNormalCount:2 notCount:0 where:@"propC != 0"];
  2203. [self testClass:[RenamedProperties2 class] withNormalCount:1 notCount:1 where:@"propC = 1"];
  2204. [self testClass:[RenamedProperties2 class] withNormalCount:1 notCount:1 where:@"propC = 2"];
  2205. [self testClass:[RenamedProperties2 class] withNormalCount:0 notCount:2 where:@"propC = 3"];
  2206. }
  2207. - (void)testQueryOverRenamedLinks {
  2208. RLMRealm *realm = [self realm];
  2209. [realm beginWriteTransaction];
  2210. id obj1 = [RenamedProperties1 createInRealm:realm withValue:@[@1, @"a"]];
  2211. id obj2 = [RenamedProperties2 createInRealm:realm withValue:@[@2, @"b"]];
  2212. [LinkToRenamedProperties1 createInRealm:realm withValue:@[obj1, NSNull.null, @[obj1]]];
  2213. [LinkToRenamedProperties2 createInRealm:realm withValue:@[obj2, NSNull.null, @[obj2]]];
  2214. [realm commitWriteTransaction];
  2215. [self testClass:[LinkToRenamedProperties1 class] withNormalCount:2 notCount:0 where:@"linkA.propA != 0"];
  2216. [self testClass:[LinkToRenamedProperties1 class] withNormalCount:1 notCount:1 where:@"linkA.propA = 1"];
  2217. [self testClass:[LinkToRenamedProperties1 class] withNormalCount:1 notCount:1 where:@"linkA.propA = 2"];
  2218. [self testClass:[LinkToRenamedProperties1 class] withNormalCount:0 notCount:2 where:@"linkA.propA = 3"];
  2219. [self testClass:[LinkToRenamedProperties2 class] withNormalCount:2 notCount:0 where:@"linkC.propC != 0"];
  2220. [self testClass:[LinkToRenamedProperties2 class] withNormalCount:1 notCount:1 where:@"linkC.propC = 1"];
  2221. [self testClass:[LinkToRenamedProperties2 class] withNormalCount:1 notCount:1 where:@"linkC.propC = 2"];
  2222. [self testClass:[LinkToRenamedProperties2 class] withNormalCount:0 notCount:2 where:@"linkC.propC = 3"];
  2223. [self testClass:[LinkToRenamedProperties1 class] withNormalCount:2 notCount:0 where:@"ANY array.propA != 0"];
  2224. [self testClass:[LinkToRenamedProperties1 class] withNormalCount:1 notCount:1 where:@"ANY array.propA = 1"];
  2225. [self testClass:[LinkToRenamedProperties1 class] withNormalCount:1 notCount:1 where:@"ANY array.propA = 2"];
  2226. [self testClass:[LinkToRenamedProperties1 class] withNormalCount:0 notCount:2 where:@"ANY array.propA = 3"];
  2227. [self testClass:[LinkToRenamedProperties2 class] withNormalCount:2 notCount:0 where:@"ANY array.propC != 0"];
  2228. [self testClass:[LinkToRenamedProperties2 class] withNormalCount:1 notCount:1 where:@"ANY array.propC = 1"];
  2229. [self testClass:[LinkToRenamedProperties2 class] withNormalCount:1 notCount:1 where:@"ANY array.propC = 2"];
  2230. [self testClass:[LinkToRenamedProperties2 class] withNormalCount:0 notCount:2 where:@"ANY array.propC = 3"];
  2231. }
  2232. - (void)testQueryOverRenamedBacklinks {
  2233. RLMRealm *realm = [self realm];
  2234. [realm beginWriteTransaction];
  2235. id obj1 = [RenamedProperties1 createInRealm:realm withValue:@[@1, @"a"]];
  2236. id obj2 = [RenamedProperties2 createInRealm:realm withValue:@[@2, @"b"]];
  2237. [LinkToRenamedProperties1 createInRealm:realm withValue:@[obj1, NSNull.null, @[obj1]]];
  2238. [LinkToRenamedProperties2 createInRealm:realm withValue:@[obj2, NSNull.null, @[obj2]]];
  2239. [realm commitWriteTransaction];
  2240. [self testClass:[RenamedProperties1 class] withNormalCount:2 notCount:0 where:@"ANY linking1.linkA.propA != 0"];
  2241. [self testClass:[RenamedProperties1 class] withNormalCount:1 notCount:1 where:@"ANY linking1.linkA.propA = 1"];
  2242. [self testClass:[RenamedProperties1 class] withNormalCount:1 notCount:1 where:@"ANY linking1.linkA.propA = 2"];
  2243. [self testClass:[RenamedProperties1 class] withNormalCount:0 notCount:2 where:@"ANY linking1.linkA.propA = 3"];
  2244. }
  2245. @end
  2246. @interface AsyncQueryTests : QueryTests
  2247. @end
  2248. @implementation AsyncQueryTests
  2249. - (RLMResults *)evaluate:(RLMResults *)results {
  2250. id token = [results addNotificationBlock:^(RLMResults *r, __unused RLMCollectionChange *changed, NSError *e) {
  2251. XCTAssertNil(e);
  2252. XCTAssertNotNil(r);
  2253. CFRunLoopStop(CFRunLoopGetCurrent());
  2254. }];
  2255. CFRunLoopRun();
  2256. [(RLMNotificationToken *)token invalidate];
  2257. return results;
  2258. }
  2259. @end
  2260. @interface QueryWithReversedColumnOrderTests : QueryTests
  2261. @end
  2262. @implementation QueryWithReversedColumnOrderTests
  2263. - (RLMRealm *)realm {
  2264. @autoreleasepool {
  2265. NSSet *classNames = [NSSet setWithArray:@[@"AllTypesObject", @"QueryObject", @"PersonObject", @"DogObject",
  2266. @"EmployeeObject", @"CompanyObject", @"OwnerObject"]];
  2267. RLMSchema *schema = [RLMSchema.sharedSchema copy];
  2268. NSMutableArray *objectSchemas = [schema.objectSchema mutableCopy];
  2269. for (NSUInteger i = 0; i < objectSchemas.count; i++) {
  2270. RLMObjectSchema *objectSchema = objectSchemas[i];
  2271. if ([classNames member:objectSchema.className]) {
  2272. objectSchemas[i] = [self reverseProperties:objectSchema];
  2273. }
  2274. }
  2275. schema.objectSchema = objectSchemas;
  2276. RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
  2277. config.customSchema = schema;
  2278. [RLMRealm realmWithConfiguration:config error:nil];
  2279. }
  2280. return RLMRealm.defaultRealm;
  2281. }
  2282. - (RLMObjectSchema *)reverseProperties:(RLMObjectSchema *)source {
  2283. RLMObjectSchema *objectSchema = [source copy];
  2284. objectSchema.properties = objectSchema.properties.reverseObjectEnumerator.allObjects;
  2285. return objectSchema;
  2286. }
  2287. @end