QueryTests.m 146 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859
  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. {
  1438. RLMRealm *realm = [self realm];
  1439. [realm beginWriteTransaction];
  1440. ArrayPropertyObject *arr = [ArrayPropertyObject createInRealm:realm withValue:@[@"name", @[], @[]]];
  1441. [arr.array addObject:[StringObject createInRealm:realm withValue:@[@"value"]]];
  1442. StringObject *otherStringObject = [StringObject createInRealm:realm withValue:@[@"some other value"]];
  1443. [realm commitWriteTransaction];
  1444. RLMAssertCount(ArrayPropertyObject, 0U, @"ANY array.stringCol IN %@", @[@"missing"]);
  1445. RLMAssertCount(ArrayPropertyObject, 1U, @"ANY array.stringCol IN %@", @[@"value"]);
  1446. RLMAssertCount(ArrayPropertyObject, 1U, @"NONE array.stringCol IN %@", @[@"missing"]);
  1447. RLMAssertCount(ArrayPropertyObject, 0U, @"NONE array.stringCol IN %@", @[@"value"]);
  1448. RLMAssertCount(ArrayPropertyObject, 0U, @"ANY array IN %@", [StringObject objectsWhere:@"stringCol = 'missing'"]);
  1449. RLMAssertCount(ArrayPropertyObject, 1U, @"ANY array IN %@", [StringObject objectsWhere:@"stringCol = 'value'"]);
  1450. RLMAssertCount(ArrayPropertyObject, 1U, @"NONE array IN %@", [StringObject objectsWhere:@"stringCol = 'missing'"]);
  1451. RLMAssertCount(ArrayPropertyObject, 0U, @"NONE array IN %@", [StringObject objectsWhere:@"stringCol = 'value'"]);
  1452. StringObject *stringObject = [[StringObject allObjectsInRealm:realm] firstObject];
  1453. RLMAssertCount(ArrayPropertyObject, 1U, @"%@ IN array", stringObject);
  1454. RLMAssertCount(ArrayPropertyObject, 0U, @"%@ IN array", otherStringObject);
  1455. }
  1456. - (void)testQueryChaining {
  1457. RLMRealm *realm = [self realm];
  1458. [realm beginWriteTransaction];
  1459. [PersonObject createInRealm:realm withValue:@[@"Tim", @29]];
  1460. [PersonObject createInRealm:realm withValue:@[@"Ari", @33]];
  1461. [realm commitWriteTransaction];
  1462. RLMAssertCount(PersonObject, 1U, @"name == 'Ari'");
  1463. RLMAssertCount(PersonObject, 0U, @"name == 'Ari' and age == 29");
  1464. XCTAssertEqual(0U, [[[PersonObject objectsWhere:@"name == 'Ari'"] objectsWhere:@"age == 29"] count]);
  1465. }
  1466. - (void)testLinkViewQuery {
  1467. RLMRealm *realm = [self realm];
  1468. [realm beginWriteTransaction];
  1469. [CompanyObject createInRealm:realm
  1470. withValue:@[@"company name", @[@{@"name": @"John", @"age": @30, @"hired": @NO},
  1471. @{@"name": @"Joe", @"age": @40, @"hired": @YES},
  1472. @{@"name": @"Jill", @"age": @50, @"hired": @YES}]]];
  1473. [realm commitWriteTransaction];
  1474. CompanyObject *co = [CompanyObject allObjects][0];
  1475. RLMAssertCount(co.employees, 1U, @"hired = NO");
  1476. RLMAssertCount(co.employees, 2U, @"hired = YES");
  1477. RLMAssertCount(co.employees, 1U, @"hired = YES AND age = 40");
  1478. RLMAssertCount(co.employees, 0U, @"hired = YES AND age = 30");
  1479. RLMAssertCount(co.employees, 3U, @"hired = YES OR age = 30");
  1480. RLMAssertCount([co.employees, 1U, @"hired = YES"] objectsWhere:@"name = 'Joe'");
  1481. }
  1482. - (void)testLinkViewQueryLifetime {
  1483. RLMRealm *realm = [self realm];
  1484. [realm beginWriteTransaction];
  1485. [CompanyObject createInRealm:realm
  1486. withValue:@[@"company name", @[@{@"name": @"John", @"age": @30, @"hired": @NO},
  1487. @{@"name": @"Jill", @"age": @50, @"hired": @YES}]]];
  1488. [EmployeeObject createInRealm:realm withValue:@{@"name": @"Joe", @"age": @40, @"hired": @YES}];
  1489. [realm commitWriteTransaction];
  1490. RLMResults<EmployeeObject *> *subarray = nil;
  1491. @autoreleasepool {
  1492. __attribute((objc_precise_lifetime)) CompanyObject *co = [CompanyObject allObjects][0];
  1493. subarray = [co.employees objectsWhere:@"age = 40"];
  1494. XCTAssertEqual(0U, subarray.count);
  1495. }
  1496. [realm beginWriteTransaction];
  1497. @autoreleasepool {
  1498. __attribute((objc_precise_lifetime)) CompanyObject *co = [CompanyObject allObjects][0];
  1499. [co.employees addObject:[EmployeeObject createInRealm:realm withValue:@{@"name": @"Joe", @"age": @40, @"hired": @YES}]];
  1500. }
  1501. [realm commitWriteTransaction];
  1502. XCTAssertEqual(1U, subarray.count);
  1503. XCTAssertEqualObjects(@"Joe", subarray[0][@"name"]);
  1504. }
  1505. - (void)testLinkViewQueryLiveUpdate {
  1506. RLMRealm *realm = [self realm];
  1507. [realm beginWriteTransaction];
  1508. [CompanyObject createInRealm:realm
  1509. withValue:@[@"company name", @[@{@"name": @"John", @"age": @30, @"hired": @NO},
  1510. @{@"name": @"Jill", @"age": @40, @"hired": @YES}]]];
  1511. EmployeeObject *eo = [EmployeeObject createInRealm:realm withValue:@{@"name": @"Joe", @"age": @40, @"hired": @YES}];
  1512. [realm commitWriteTransaction];
  1513. CompanyObject *co = CompanyObject.allObjects.firstObject;
  1514. RLMResults *basic = [co.employees objectsWhere:@"age = 40"];
  1515. RLMResults *sort = [co.employees sortedResultsUsingKeyPath:@"name" ascending:YES];
  1516. RLMResults *sortQuery = [[co.employees sortedResultsUsingKeyPath:@"name" ascending:YES] objectsWhere:@"age = 40"];
  1517. RLMResults *querySort = [[co.employees objectsWhere:@"age = 40"] sortedResultsUsingKeyPath:@"name" ascending:YES];
  1518. XCTAssertEqual(1U, basic.count);
  1519. XCTAssertEqual(2U, sort.count);
  1520. XCTAssertEqual(1U, sortQuery.count);
  1521. XCTAssertEqual(1U, querySort.count);
  1522. XCTAssertEqualObjects(@"Jill", [[basic lastObject] name]);
  1523. XCTAssertEqualObjects(@"Jill", [[sortQuery lastObject] name]);
  1524. XCTAssertEqualObjects(@"Jill", [[querySort lastObject] name]);
  1525. [realm beginWriteTransaction];
  1526. [co.employees addObject:eo];
  1527. [realm commitWriteTransaction];
  1528. XCTAssertEqual(2U, basic.count);
  1529. XCTAssertEqual(3U, sort.count);
  1530. XCTAssertEqual(2U, sortQuery.count);
  1531. XCTAssertEqual(2U, querySort.count);
  1532. XCTAssertEqualObjects(@"Joe", [[basic lastObject] name]);
  1533. XCTAssertEqualObjects(@"Joe", [[sortQuery lastObject] name]);
  1534. XCTAssertEqualObjects(@"Joe", [[querySort lastObject] name]);
  1535. }
  1536. - (void)testConstantPredicates
  1537. {
  1538. RLMRealm *realm = [self realm];
  1539. [realm beginWriteTransaction];
  1540. [PersonObject createInRealm:realm withValue:@[@"Fiel", @27]];
  1541. [PersonObject createInRealm:realm withValue:@[@"Tim", @29]];
  1542. [PersonObject createInRealm:realm withValue:@[@"Ari", @33]];
  1543. [realm commitWriteTransaction];
  1544. RLMResults *all = [PersonObject objectsWithPredicate:[NSPredicate predicateWithValue:YES]];
  1545. XCTAssertEqual(all.count, 3U, @"Expecting 3 results");
  1546. RLMResults *none = [PersonObject objectsWithPredicate:[NSPredicate predicateWithValue:NO]];
  1547. XCTAssertEqual(none.count, 0U, @"Expecting 0 results");
  1548. }
  1549. - (void)testEmptyCompoundPredicates
  1550. {
  1551. RLMRealm *realm = [self realm];
  1552. [realm beginWriteTransaction];
  1553. [PersonObject createInRealm:realm withValue:@[@"Fiel", @27]];
  1554. [PersonObject createInRealm:realm withValue:@[@"Tim", @29]];
  1555. [PersonObject createInRealm:realm withValue:@[@"Ari", @33]];
  1556. [realm commitWriteTransaction];
  1557. RLMResults *all = [PersonObject objectsWithPredicate:[NSCompoundPredicate andPredicateWithSubpredicates:@[]]];
  1558. XCTAssertEqual(all.count, 3U, @"Expecting 3 results");
  1559. RLMResults *none = [PersonObject objectsWithPredicate:[NSCompoundPredicate orPredicateWithSubpredicates:@[]]];
  1560. XCTAssertEqual(none.count, 0U, @"Expecting 0 results");
  1561. }
  1562. - (void)testComparisonsWithKeyPathOnRHS
  1563. {
  1564. RLMRealm *realm = [self realm];
  1565. [realm beginWriteTransaction];
  1566. [self.queryObjectClass createInRealm:realm withValue:@[@YES, @YES, @1, @2, @23.0f, @1.7f, @0.0, @5.55, @"a", @"a"]];
  1567. [self.queryObjectClass createInRealm:realm withValue:@[@YES, @NO, @1, @3, @-5.3f, @4.21f, @1.0, @4.44, @"a", @"A"]];
  1568. [self.queryObjectClass createInRealm:realm withValue:@[@NO, @NO, @2, @2, @1.0f, @3.55f, @99.9, @6.66, @"a", @"ab"]];
  1569. [self.queryObjectClass createInRealm:realm withValue:@[@NO, @YES, @3, @6, @4.21f, @1.0f, @1.0, @7.77, @"a", @"AB"]];
  1570. [self.queryObjectClass createInRealm:realm withValue:@[@YES, @YES, @4, @5, @23.0f, @23.0f, @7.4, @8.88, @"a", @"b"]];
  1571. [self.queryObjectClass createInRealm:realm withValue:@[@YES, @NO, @15, @8, @1.0f, @66.0f, @1.01, @9.99, @"a", @"ba"]];
  1572. [self.queryObjectClass createInRealm:realm withValue:@[@NO, @YES, @15, @15, @1.0f, @66.0f, @1.01, @9.99, @"a", @"BA"]];
  1573. [realm commitWriteTransaction];
  1574. RLMAssertCount(self.queryObjectClass, 4U, @"TRUE == bool1");
  1575. RLMAssertCount(self.queryObjectClass, 3U, @"TRUE != bool2");
  1576. RLMAssertCount(self.queryObjectClass, 2U, @"1 == int1");
  1577. RLMAssertCount(self.queryObjectClass, 5U, @"2 != int2");
  1578. RLMAssertCount(self.queryObjectClass, 2U, @"2 > int1");
  1579. RLMAssertCount(self.queryObjectClass, 4U, @"2 < int1");
  1580. RLMAssertCount(self.queryObjectClass, 3U, @"2 >= int1");
  1581. RLMAssertCount(self.queryObjectClass, 5U, @"2 <= int1");
  1582. RLMAssertCount(self.queryObjectClass, 3U, @"1.0 == float1");
  1583. RLMAssertCount(self.queryObjectClass, 6U, @"1.0 != float2");
  1584. RLMAssertCount(self.queryObjectClass, 1U, @"1.0 > float1");
  1585. RLMAssertCount(self.queryObjectClass, 6U, @"1.0 < float2");
  1586. RLMAssertCount(self.queryObjectClass, 4U, @"1.0 >= float1");
  1587. RLMAssertCount(self.queryObjectClass, 7U, @"1.0 <= float2");
  1588. RLMAssertCount(self.queryObjectClass, 2U, @"1.0 == double1");
  1589. RLMAssertCount(self.queryObjectClass, 5U, @"1.0 != double1");
  1590. RLMAssertCount(self.queryObjectClass, 1U, @"5.0 > double2");
  1591. RLMAssertCount(self.queryObjectClass, 6U, @"5.0 < double2");
  1592. RLMAssertCount(self.queryObjectClass, 2U, @"5.55 >= double2");
  1593. RLMAssertCount(self.queryObjectClass, 6U, @"5.55 <= double2");
  1594. RLMAssertCount(self.queryObjectClass, 1U, @"'a' == string2");
  1595. RLMAssertCount(self.queryObjectClass, 6U, @"'a' != string2");
  1596. RLMAssertThrowsWithReasonMatching([self.queryObjectClass objectsWhere:@"'Realm' CONTAINS string1"].count,
  1597. @"Operator 'CONTAINS' is not supported .* right side");
  1598. RLMAssertThrowsWithReasonMatching([self.queryObjectClass objectsWhere:@"'Amazon' BEGINSWITH string2"].count,
  1599. @"Operator 'BEGINSWITH' is not supported .* right side");
  1600. RLMAssertThrowsWithReasonMatching([self.queryObjectClass objectsWhere:@"'Tuba' ENDSWITH string1"].count,
  1601. @"Operator 'ENDSWITH' is not supported .* right side");
  1602. }
  1603. - (void)testLinksToDeletedOrMovedObject
  1604. {
  1605. RLMRealm *realm = [self realm];
  1606. [realm beginWriteTransaction];
  1607. DogObject *fido = [DogObject createInRealm:realm withValue:@[ @"Fido", @3 ]];
  1608. [OwnerObject createInRealm:realm withValue:@[ @"Fido's owner", fido ]];
  1609. DogObject *rex = [DogObject createInRealm:realm withValue:@[ @"Rex", @2 ]];
  1610. [OwnerObject createInRealm:realm withValue:@[ @"Rex's owner", rex ]];
  1611. DogObject *spot = [DogObject createInRealm:realm withValue:@[ @"Spot", @2 ]];
  1612. [OwnerObject createInRealm:realm withValue:@[ @"Spot's owner", spot ]];
  1613. [realm commitWriteTransaction];
  1614. RLMResults *fidoQuery = [OwnerObject objectsInRealm:realm where:@"dog == %@", fido];
  1615. RLMResults *rexQuery = [OwnerObject objectsInRealm:realm where:@"dog == %@", rex];
  1616. RLMResults *spotQuery = [OwnerObject objectsInRealm:realm where:@"dog == %@", spot];
  1617. [realm beginWriteTransaction];
  1618. [realm deleteObject:fido];
  1619. [realm commitWriteTransaction];
  1620. // Fido was removed, so we should not find his owner.
  1621. XCTAssertEqual(0u, fidoQuery.count);
  1622. // Rex's owner should be found as the row was not touched.
  1623. XCTAssertEqual(1u, rexQuery.count);
  1624. XCTAssertEqualObjects(@"Rex's owner", [rexQuery.firstObject name]);
  1625. // Spot's owner should be found, despite Spot's row having moved.
  1626. XCTAssertEqual(1u, spotQuery.count);
  1627. XCTAssertEqualObjects(@"Spot's owner", [spotQuery.firstObject name]);
  1628. }
  1629. - (void)testQueryOnDeletedArrayProperty
  1630. {
  1631. RLMRealm *realm = [self realm];
  1632. [realm beginWriteTransaction];
  1633. IntObject *io = [IntObject createInRealm:realm withValue:@[@0]];
  1634. ArrayPropertyObject *array = [ArrayPropertyObject createInRealm:realm withValue:@[@"", @[], @[io]]];
  1635. [realm commitWriteTransaction];
  1636. RLMResults *results = [array.intArray objectsWhere:@"TRUEPREDICATE"];
  1637. XCTAssertEqual(1U, results.count);
  1638. [realm beginWriteTransaction];
  1639. [realm deleteObject:array];
  1640. [realm commitWriteTransaction];
  1641. XCTAssertEqual(0U, results.count);
  1642. XCTAssertNil(results.firstObject);
  1643. }
  1644. - (void)testSubqueries
  1645. {
  1646. RLMRealm *realm = [self realm];
  1647. [realm beginWriteTransaction];
  1648. CompanyObject *first = [CompanyObject createInRealm:realm
  1649. withValue:@[@"first company", @[@{@"name": @"John", @"age": @30, @"hired": @NO},
  1650. @{@"name": @"Jill", @"age": @40, @"hired": @YES},
  1651. @{@"name": @"Joe", @"age": @40, @"hired": @YES}]]];
  1652. CompanyObject *second = [CompanyObject createInRealm:realm
  1653. withValue:@[@"second company", @[@{@"name": @"Bill", @"age": @35, @"hired": @YES},
  1654. @{@"name": @"Don", @"age": @45, @"hired": @NO},
  1655. @{@"name": @"Tim", @"age": @60, @"hired": @NO}]]];
  1656. [LinkToCompanyObject createInRealm:realm withValue:@[ first ]];
  1657. [LinkToCompanyObject createInRealm:realm withValue:@[ second ]];
  1658. [realm commitWriteTransaction];
  1659. RLMAssertCount(CompanyObject, 1U, @"SUBQUERY(employees, $employee, $employee.age > 30 AND $employee.hired = FALSE).@count > 0");
  1660. RLMAssertCount(CompanyObject, 2U, @"SUBQUERY(employees, $employee, $employee.age < 30 AND $employee.hired = TRUE).@count == 0");
  1661. RLMAssertCount(LinkToCompanyObject, 1U, @"SUBQUERY(company.employees, $employee, $employee.age > 30 AND $employee.hired = FALSE).@count > 0");
  1662. RLMAssertCount(LinkToCompanyObject, 2U, @"SUBQUERY(company.employees, $employee, $employee.age < 30 AND $employee.hired = TRUE).@count == 0");
  1663. }
  1664. - (void)testLinkingObjects {
  1665. RLMRealm *realm = [self realm];
  1666. [realm beginWriteTransaction];
  1667. PersonObject *hannah = [PersonObject createInRealm:realm withValue:@[ @"Hannah", @0 ]];
  1668. PersonObject *elijah = [PersonObject createInRealm:realm withValue:@[ @"Elijah", @3 ]];
  1669. PersonObject *mark = [PersonObject createInRealm:realm withValue:@[ @"Mark", @30, @[ hannah ]]];
  1670. PersonObject *jason = [PersonObject createInRealm:realm withValue:@[ @"Jason", @31, @[ elijah ]]];
  1671. PersonObject *diane = [PersonObject createInRealm:realm withValue:@[ @"Diane", @29, @[ hannah ]]];
  1672. PersonObject *carol = [PersonObject createInRealm:realm withValue:@[ @"Carol", @31 ]];
  1673. PersonObject *michael = [PersonObject createInRealm:realm withValue:@[ @"Michael", @57, @[ jason, mark ]]];
  1674. PersonObject *raewynne = [PersonObject createInRealm:realm withValue:@[ @"Raewynne", @57, @[ jason, mark ]]];
  1675. PersonObject *don = [PersonObject createInRealm:realm withValue:@[ @"Don", @64, @[ carol, diane ]]];
  1676. PersonObject *diane_sr = [PersonObject createInRealm:realm withValue:@[ @"Diane", @60, @[ carol, diane ]]];
  1677. [realm commitWriteTransaction];
  1678. NSArray *(^asArray)(RLMResults *) = ^(RLMResults *results) {
  1679. return [[self evaluate:results] valueForKeyPath:@"self"];
  1680. };
  1681. // People that have a parent with a name that starts with 'M'.
  1682. RLMResults *r1 = [PersonObject objectsWhere:@"ANY parents.name BEGINSWITH 'M'"];
  1683. XCTAssertEqualObjects(asArray(r1), (@[ hannah, mark, jason ]));
  1684. // People that have a grandparent with a name that starts with 'M'.
  1685. RLMResults *r2 = [PersonObject objectsWhere:@"ANY parents.parents.name BEGINSWITH 'M'"];
  1686. XCTAssertEqualObjects(asArray(r2), (@[ hannah, elijah ]));
  1687. // People that have children that have a parent named Diane.
  1688. RLMResults *r3 = [PersonObject objectsWhere:@"ANY children.parents.name == 'Diane'"];
  1689. XCTAssertEqualObjects(asArray(r3), (@[ mark, diane, don, diane_sr ]));
  1690. // People that have children that have a grandparent named Don.
  1691. RLMResults *r4 = [PersonObject objectsWhere:@"ANY children.parents.parents.name == 'Don'"];
  1692. XCTAssertEqualObjects(asArray(r4), (@[ mark, diane ]));
  1693. // People whose parents have an average age of < 60.
  1694. RLMResults *r5 = [PersonObject objectsWhere:@"parents.@avg.age < 60"];
  1695. XCTAssertEqualObjects(asArray(r5), (@[ hannah, elijah, mark, jason ]));
  1696. // People that have at least one sibling.
  1697. RLMResults *r6 = [PersonObject objectsWhere:@"SUBQUERY(parents, $parent, $parent.children.@count > 1).@count > 0"];
  1698. XCTAssertEqualObjects(asArray(r6), (@[ mark, jason, diane, carol ]));
  1699. // People that have Raewynne as a parent.
  1700. RLMResults *r7 = [PersonObject objectsWhere:@"ANY parents == %@", raewynne];
  1701. XCTAssertEqualObjects(asArray(r7), (@[ mark, jason ]));
  1702. // People that have Mark as a child.
  1703. RLMResults *r8 = [PersonObject objectsWhere:@"ANY children == %@", mark];
  1704. XCTAssertEqualObjects(asArray(r8), (@[ michael, raewynne ]));
  1705. // People that have Michael as a grandparent.
  1706. RLMResults *r9 = [PersonObject objectsWhere:@"ANY parents.parents == %@", michael];
  1707. XCTAssertEqualObjects(asArray(r9), (@[ hannah, elijah ]));
  1708. // People that have Hannah as a grandchild.
  1709. RLMResults *r10 = [PersonObject objectsWhere:@"ANY children.children == %@", hannah];
  1710. XCTAssertEqualObjects(asArray(r10), (@[ michael, raewynne, don, diane_sr ]));
  1711. // People that have no listed parents.
  1712. RLMResults *r11 = [PersonObject objectsWhere:@"parents.@count == 0"];
  1713. XCTAssertEqualObjects(asArray(r11), (@[ michael, raewynne, don, diane_sr ]));
  1714. // No links are equal to a detached row accessor.
  1715. RLMResults *r12 = [PersonObject objectsWhere:@"ANY parents == %@", [PersonObject new]];
  1716. XCTAssertEqualObjects(asArray(r12), (@[ ]));
  1717. // All links are not equal to a detached row accessor so this will match all rows that are linked to.
  1718. RLMResults *r13 = [PersonObject objectsWhere:@"ANY parents != %@", [PersonObject new]];
  1719. XCTAssertEqualObjects(asArray(r13), (@[ hannah, elijah, mark, jason, diane, carol ]));
  1720. // Linking objects cannot contain null so their members cannot be compared with null.
  1721. XCTAssertThrows([PersonObject objectsWhere:@"ANY parents == NULL"]);
  1722. // 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.
  1723. 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"];
  1724. XCTAssertEqualObjects(asArray(r14), (@[ hannah ]));
  1725. // Add a new link and verify that the existing results update as expected.
  1726. __block PersonObject *mackenzie;
  1727. [realm transactionWithBlock:^{
  1728. mackenzie = [PersonObject createInRealm:realm withValue:@[ @"Mackenzie", @0 ]];
  1729. [jason.children addObject:mackenzie];
  1730. }];
  1731. // People that have a parent with a name that starts with 'M'.
  1732. XCTAssertEqualObjects(asArray(r1), (@[ hannah, mark, jason ]));
  1733. // People that have a grandparent with a name that starts with 'M'.
  1734. XCTAssertEqualObjects(asArray(r2), (@[ hannah, elijah, mackenzie ]));
  1735. // People that have children that have a parent named Diane.
  1736. XCTAssertEqualObjects(asArray(r3), (@[ mark, diane, don, diane_sr ]));
  1737. // People that have children that have a grandparent named Don.
  1738. XCTAssertEqualObjects(asArray(r4), (@[ mark, diane ]));
  1739. // People whose parents have an average age of < 60.
  1740. XCTAssertEqualObjects(asArray(r5), (@[ hannah, elijah, mark, jason, mackenzie ]));
  1741. // People that have at least one sibling.
  1742. XCTAssertEqualObjects(asArray(r6), (@[ elijah, mark, jason, diane, carol, mackenzie ]));
  1743. // People that have Raewynne as a parent.
  1744. XCTAssertEqualObjects(asArray(r7), (@[ mark, jason ]));
  1745. // People that have Mark as a child.
  1746. XCTAssertEqualObjects(asArray(r8), (@[ michael, raewynne ]));
  1747. // People that have Michael as a grandparent.
  1748. XCTAssertEqualObjects(asArray(r9), (@[ hannah, elijah, mackenzie ]));
  1749. // People that have Hannah as a grandchild.
  1750. XCTAssertEqualObjects(asArray(r10), (@[ michael, raewynne, don, diane_sr ]));
  1751. // People that have no listed parents.
  1752. XCTAssertEqualObjects(asArray(r11), (@[ michael, raewynne, don, diane_sr ]));
  1753. // No links are equal to a detached row accessor.
  1754. XCTAssertEqualObjects(asArray(r12), (@[ ]));
  1755. // All links are not equal to a detached row accessor so this will match all rows that are linked to.
  1756. XCTAssertEqualObjects(asArray(r13), (@[ hannah, elijah, mark, jason, diane, carol, mackenzie ]));
  1757. // 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.
  1758. XCTAssertEqualObjects(asArray(r14), (@[ hannah ]));
  1759. }
  1760. - (void)testCountOnCollection {
  1761. RLMRealm *realm = [self realm];
  1762. [realm beginWriteTransaction];
  1763. IntegerArrayPropertyObject *arr = [IntegerArrayPropertyObject createInRealm:realm withValue:@[ @1, @[]]];
  1764. [arr.array addObject:[IntObject createInRealm:realm withValue:@[ @456 ]]];
  1765. arr = [IntegerArrayPropertyObject createInRealm:realm withValue:@[ @2, @[]]];
  1766. [arr.array addObject:[IntObject createInRealm:realm withValue:@[ @1 ]]];
  1767. [arr.array addObject:[IntObject createInRealm:realm withValue:@[ @2 ]]];
  1768. [arr.array addObject:[IntObject createInRealm:realm withValue:@[ @3 ]]];
  1769. arr = [IntegerArrayPropertyObject createInRealm:realm withValue:@[ @0, @[]]];
  1770. [realm commitWriteTransaction];
  1771. RLMAssertCount(IntegerArrayPropertyObject, 2U, @"array.@count > 0");
  1772. RLMAssertCount(IntegerArrayPropertyObject, 1U, @"array.@count == 3");
  1773. RLMAssertCount(IntegerArrayPropertyObject, 1U, @"array.@count < 1");
  1774. RLMAssertCount(IntegerArrayPropertyObject, 2U, @"0 < array.@count");
  1775. RLMAssertCount(IntegerArrayPropertyObject, 1U, @"3 == array.@count");
  1776. RLMAssertCount(IntegerArrayPropertyObject, 1U, @"1 > array.@count");
  1777. RLMAssertCount(IntegerArrayPropertyObject, 2U, @"array.@count == number");
  1778. RLMAssertCount(IntegerArrayPropertyObject, 1U, @"array.@count > number");
  1779. RLMAssertCount(IntegerArrayPropertyObject, 1U, @"number < array.@count");
  1780. // We do not yet handle collection operations on both sides of the comparison.
  1781. RLMAssertThrowsWithReasonMatching(([IntegerArrayPropertyObject objectsWhere:@"array.@count == array.@count"]),
  1782. @"aggregate operations cannot be compared with other aggregate operations");
  1783. RLMAssertThrowsWithReasonMatching(([IntegerArrayPropertyObject objectsWhere:@"array.@count.foo.bar != 0"]),
  1784. @"single level key");
  1785. RLMAssertThrowsWithReasonMatching(([IntegerArrayPropertyObject objectsWhere:@"array.@count.intCol > 0"]),
  1786. @"@count does not have any properties");
  1787. RLMAssertThrowsWithReasonMatching(([IntegerArrayPropertyObject objectsWhere:@"array.@count != 'Hello'"]),
  1788. @"@count can only be compared with a numeric value");
  1789. }
  1790. - (void)testAggregateCollectionOperators {
  1791. RLMRealm *realm = [self realm];
  1792. [realm beginWriteTransaction];
  1793. IntegerArrayPropertyObject *arr = [IntegerArrayPropertyObject createInRealm:realm withValue:@[ @1111, @[] ]];
  1794. [arr.array addObject:[IntObject createInRealm:realm withValue:@[ @1234 ]]];
  1795. [arr.array addObject:[IntObject createInRealm:realm withValue:@[ @2 ]]];
  1796. [arr.array addObject:[IntObject createInRealm:realm withValue:@[ @-12345 ]]];
  1797. arr = [IntegerArrayPropertyObject createInRealm:realm withValue:@[ @2222, @[] ]];
  1798. [arr.array addObject:[IntObject createInRealm:realm withValue:@[ @100 ]]];
  1799. arr = [IntegerArrayPropertyObject createInRealm:realm withValue:@[ @3333, @[] ]];
  1800. [realm commitWriteTransaction];
  1801. RLMAssertCount(IntegerArrayPropertyObject, 1U, @"array.@min.intCol == -12345");
  1802. RLMAssertCount(IntegerArrayPropertyObject, 1U, @"array.@min.intCol == 100");
  1803. RLMAssertCount(IntegerArrayPropertyObject, 2U, @"array.@min.intCol < 1000");
  1804. RLMAssertCount(IntegerArrayPropertyObject, 1U, @"array.@min.intCol > -1000");
  1805. RLMAssertCount(IntegerArrayPropertyObject, 1U, @"array.@max.intCol == 1234");
  1806. RLMAssertCount(IntegerArrayPropertyObject, 1U, @"array.@max.intCol == 100");
  1807. RLMAssertCount(IntegerArrayPropertyObject, 2U, @"array.@max.intCol > -1000");
  1808. RLMAssertCount(IntegerArrayPropertyObject, 1U, @"array.@max.intCol > 1000");
  1809. RLMAssertCount(IntegerArrayPropertyObject, 1U, @"array.@sum.intCol == 100");
  1810. RLMAssertCount(IntegerArrayPropertyObject, 1U, @"array.@sum.intCol == -11109");
  1811. RLMAssertCount(IntegerArrayPropertyObject, 1U, @"array.@sum.intCol == 0");
  1812. RLMAssertCount(IntegerArrayPropertyObject, 2U, @"array.@sum.intCol > -50");
  1813. RLMAssertCount(IntegerArrayPropertyObject, 2U, @"array.@sum.intCol < 50");
  1814. RLMAssertCount(IntegerArrayPropertyObject, 1U, @"array.@avg.intCol == 100");
  1815. RLMAssertCount(IntegerArrayPropertyObject, 1U, @"array.@avg.intCol == -3703.0");
  1816. RLMAssertCount(IntegerArrayPropertyObject, 0U, @"array.@avg.intCol == 0");
  1817. RLMAssertCount(IntegerArrayPropertyObject, 1U, @"array.@avg.intCol < -50");
  1818. RLMAssertCount(IntegerArrayPropertyObject, 1U, @"array.@avg.intCol > 50");
  1819. RLMAssertCount(IntegerArrayPropertyObject, 2U, @"array.@min.intCol < number");
  1820. RLMAssertCount(IntegerArrayPropertyObject, 2U, @"number > array.@min.intCol");
  1821. RLMAssertCount(IntegerArrayPropertyObject, 1U, @"array.@max.intCol < number");
  1822. RLMAssertCount(IntegerArrayPropertyObject, 1U, @"number > array.@max.intCol");
  1823. RLMAssertCount(IntegerArrayPropertyObject, 2U, @"array.@avg.intCol < number");
  1824. RLMAssertCount(IntegerArrayPropertyObject, 2U, @"number > array.@avg.intCol");
  1825. // We do not yet handle collection operations on both sides of the comparison.
  1826. RLMAssertThrowsWithReasonMatching(([IntegerArrayPropertyObject objectsWhere:@"array.@min.intCol == array.@min.intCol"]), @"aggregate operations cannot be compared with other aggregate operations");
  1827. RLMAssertThrowsWithReasonMatching(([IntegerArrayPropertyObject objectsWhere:@"array.@min.intCol.foo.bar == 1.23"]), @"single level key");
  1828. RLMAssertThrowsWithReasonMatching(([IntegerArrayPropertyObject objectsWhere:@"array.@max.intCol.foo.bar == 1.23"]), @"single level key");
  1829. RLMAssertThrowsWithReasonMatching(([IntegerArrayPropertyObject objectsWhere:@"array.@sum.intCol.foo.bar == 1.23"]), @"single level key");
  1830. RLMAssertThrowsWithReasonMatching(([IntegerArrayPropertyObject objectsWhere:@"array.@avg.intCol.foo.bar == 1.23"]), @"single level key");
  1831. // Average is omitted from this test as its result is always a double.
  1832. RLMAssertThrowsWithReasonMatching(([IntegerArrayPropertyObject objectsWhere:@"array.@min.intCol == 1.23"]), @"@min.*type int cannot be compared");
  1833. RLMAssertThrowsWithReasonMatching(([IntegerArrayPropertyObject objectsWhere:@"array.@max.intCol == 1.23"]), @"@max.*type int cannot be compared");
  1834. RLMAssertThrowsWithReasonMatching(([IntegerArrayPropertyObject objectsWhere:@"array.@sum.intCol == 1.23"]), @"@sum.*type int cannot be compared");
  1835. }
  1836. @end
  1837. @interface NullQueryTests : QueryTests
  1838. @end
  1839. @implementation NullQueryTests
  1840. - (Class)queryObjectClass {
  1841. return [NullQueryObject class];
  1842. }
  1843. - (void)testQueryOnNullableStringColumn {
  1844. void (^testWithStringClass)(Class) = ^(Class stringObjectClass) {
  1845. RLMRealm *realm = [self realm];
  1846. [realm transactionWithBlock:^{
  1847. [stringObjectClass createInRealm:realm withValue:@[@"a"]];
  1848. [stringObjectClass createInRealm:realm withValue:@[NSNull.null]];
  1849. [stringObjectClass createInRealm:realm withValue:@[@"b"]];
  1850. [stringObjectClass createInRealm:realm withValue:@[NSNull.null]];
  1851. [stringObjectClass createInRealm:realm withValue:@[@""]];
  1852. }];
  1853. RLMResults *allObjects = [stringObjectClass allObjectsInRealm:realm];
  1854. XCTAssertEqual(5U, allObjects.count);
  1855. RLMResults *nilStrings = [stringObjectClass objectsInRealm:realm where:@"stringCol = NULL"];
  1856. XCTAssertEqual(2U, nilStrings.count);
  1857. XCTAssertEqualObjects((@[NSNull.null, NSNull.null]), [nilStrings valueForKey:@"stringCol"]);
  1858. RLMResults *nilLikeStrings = [stringObjectClass objectsInRealm:realm where:@"stringCol LIKE NULL"];
  1859. XCTAssertEqual(2U, nilLikeStrings.count);
  1860. XCTAssertEqualObjects((@[NSNull.null, NSNull.null]), [nilLikeStrings valueForKey:@"stringCol"]);
  1861. RLMResults *nonNilStrings = [stringObjectClass objectsInRealm:realm where:@"stringCol != NULL"];
  1862. XCTAssertEqual(3U, nonNilStrings.count);
  1863. XCTAssertEqualObjects((@[@"a", @"b", @""]), [nonNilStrings valueForKey:@"stringCol"]);
  1864. RLMResults *nonNilLikeStrings = [stringObjectClass objectsInRealm:realm where:@"NOT stringCol LIKE NULL"];
  1865. XCTAssertEqual(3U, nonNilLikeStrings.count);
  1866. XCTAssertEqualObjects((@[@"a", @"b", @""]), [nonNilLikeStrings valueForKey:@"stringCol"]);
  1867. RLMAssertCount(stringObjectClass, 3U, @"stringCol IN {NULL, 'a'}");
  1868. RLMAssertCount(stringObjectClass, 1U, @"stringCol CONTAINS 'a'");
  1869. RLMAssertCount(stringObjectClass, 1U, @"stringCol BEGINSWITH 'a'");
  1870. RLMAssertCount(stringObjectClass, 1U, @"stringCol ENDSWITH 'a'");
  1871. RLMAssertCount(stringObjectClass, 1U, @"stringCol LIKE 'a'");
  1872. RLMAssertCount(stringObjectClass, 0U, @"stringCol CONTAINS 'z'");
  1873. RLMAssertCount(stringObjectClass, 0U, @"stringCol LIKE 'z'");
  1874. RLMAssertCount(stringObjectClass, 1U, @"stringCol = ''");
  1875. RLMResults *sorted = [[stringObjectClass allObjectsInRealm:realm] sortedResultsUsingKeyPath:@"stringCol" ascending:YES];
  1876. XCTAssertEqualObjects((@[NSNull.null, NSNull.null, @"", @"a", @"b"]), [sorted valueForKey:@"stringCol"]);
  1877. XCTAssertEqualObjects((@[@"b", @"a", @"", NSNull.null, NSNull.null]), [[sorted sortedResultsUsingKeyPath:@"stringCol" ascending:NO] valueForKey:@"stringCol"]);
  1878. [realm transactionWithBlock:^{
  1879. [realm deleteObject:[stringObjectClass allObjectsInRealm:realm].firstObject];
  1880. }];
  1881. XCTAssertEqual(2U, nilStrings.count);
  1882. XCTAssertEqual(2U, nonNilStrings.count);
  1883. XCTAssertEqualObjects(@[], [[stringObjectClass objectsInRealm:realm where:@"stringCol CONTAINS ''"] valueForKey:@"stringCol"]);
  1884. XCTAssertEqualObjects(@[], [[stringObjectClass objectsInRealm:realm where:@"stringCol BEGINSWITH ''"] valueForKey:@"stringCol"]);
  1885. XCTAssertEqualObjects(@[], [[stringObjectClass objectsInRealm:realm where:@"stringCol ENDSWITH ''"] valueForKey:@"stringCol"]);
  1886. XCTAssertEqualObjects([nonNilStrings valueForKey:@"stringCol"],
  1887. [[stringObjectClass objectsInRealm:realm where:@"stringCol LIKE '*'"] valueForKey:@"stringCol"]);
  1888. XCTAssertEqualObjects(@[], [[stringObjectClass objectsInRealm:realm where:@"stringCol CONTAINS[c] ''"] valueForKey:@"stringCol"]);
  1889. XCTAssertEqualObjects(@[], [[stringObjectClass objectsInRealm:realm where:@"stringCol BEGINSWITH[c] ''"] valueForKey:@"stringCol"]);
  1890. XCTAssertEqualObjects(@[], [[stringObjectClass objectsInRealm:realm where:@"stringCol ENDSWITH[c] ''"] valueForKey:@"stringCol"]);
  1891. XCTAssertEqualObjects([nonNilStrings valueForKey:@"stringCol"],
  1892. [[stringObjectClass objectsInRealm:realm where:@"stringCol LIKE[c] '*'"] valueForKey:@"stringCol"]);
  1893. XCTAssertEqualObjects(@[], [[stringObjectClass objectsInRealm:realm where:@"stringCol CONTAINS[d] ''"] valueForKey:@"stringCol"]);
  1894. XCTAssertEqualObjects(@[], [[stringObjectClass objectsInRealm:realm where:@"stringCol BEGINSWITH[d] ''"] valueForKey:@"stringCol"]);
  1895. XCTAssertEqualObjects(@[], [[stringObjectClass objectsInRealm:realm where:@"stringCol ENDSWITH[d] ''"] valueForKey:@"stringCol"]);
  1896. XCTAssertEqualObjects(@[], [[stringObjectClass objectsInRealm:realm where:@"stringCol CONTAINS[cd] ''"] valueForKey:@"stringCol"]);
  1897. XCTAssertEqualObjects(@[], [[stringObjectClass objectsInRealm:realm where:@"stringCol BEGINSWITH[cd] ''"] valueForKey:@"stringCol"]);
  1898. XCTAssertEqualObjects(@[], [[stringObjectClass objectsInRealm:realm where:@"stringCol ENDSWITH[cd] ''"] valueForKey:@"stringCol"]);
  1899. XCTAssertEqualObjects(@[], ([[stringObjectClass objectsInRealm:realm where:@"stringCol CONTAINS %@", @"\0"] valueForKey:@"self"]));
  1900. XCTAssertEqualObjects(@[], ([[stringObjectClass objectsInRealm:realm where:@"stringCol CONTAINS NULL"] valueForKey:@"stringCol"]));
  1901. XCTAssertEqualObjects(@[], ([[stringObjectClass objectsInRealm:realm where:@"stringCol CONTAINS[c] NULL"] valueForKey:@"stringCol"]));
  1902. XCTAssertEqualObjects(@[], ([[stringObjectClass objectsInRealm:realm where:@"stringCol CONTAINS[d] NULL"] valueForKey:@"stringCol"]));
  1903. XCTAssertEqualObjects(@[], ([[stringObjectClass objectsInRealm:realm where:@"stringCol CONTAINS[cd] NULL"] valueForKey:@"stringCol"]));
  1904. };
  1905. testWithStringClass([StringObject class]);
  1906. testWithStringClass([IndexedStringObject class]);
  1907. }
  1908. - (void)testQueryingOnLinkToNullableStringColumn {
  1909. void (^testWithStringClass)(Class, Class) = ^(Class stringLinkClass, Class stringObjectClass) {
  1910. RLMRealm *realm = [self realm];
  1911. [realm transactionWithBlock:^{
  1912. [stringLinkClass createInRealm:realm withValue:@[[stringObjectClass createInRealm:realm withValue:@[@"a"]]]];
  1913. [stringLinkClass createInRealm:realm withValue:@[[stringObjectClass createInRealm:realm withValue:@[NSNull.null]]]];
  1914. [stringLinkClass createInRealm:realm withValue:@[[stringObjectClass createInRealm:realm withValue:@[@"b"]]]];
  1915. [stringLinkClass createInRealm:realm withValue:@[[stringObjectClass createInRealm:realm withValue:@[NSNull.null]]]];
  1916. [stringLinkClass createInRealm:realm withValue:@[[stringObjectClass createInRealm:realm withValue:@[@""]]]];
  1917. }];
  1918. RLMResults *nilStrings = [stringLinkClass objectsInRealm:realm where:@"objectCol.stringCol = NULL"];
  1919. XCTAssertEqual(2U, nilStrings.count);
  1920. XCTAssertEqualObjects((@[NSNull.null, NSNull.null]), [nilStrings valueForKeyPath:@"objectCol.stringCol"]);
  1921. RLMResults *nilLikeStrings = [stringLinkClass objectsInRealm:realm where:@"objectCol.stringCol LIKE NULL"];
  1922. XCTAssertEqual(2U, nilLikeStrings.count);
  1923. XCTAssertEqualObjects((@[NSNull.null, NSNull.null]), [nilLikeStrings valueForKeyPath:@"objectCol.stringCol"]);
  1924. RLMResults *nonNilStrings = [stringLinkClass objectsInRealm:realm where:@"objectCol.stringCol != NULL"];
  1925. XCTAssertEqual(3U, nonNilStrings.count);
  1926. XCTAssertEqualObjects((@[@"a", @"b", @""]), [nonNilStrings valueForKeyPath:@"objectCol.stringCol"]);
  1927. RLMResults *nonNilLikeStrings = [stringLinkClass objectsInRealm:realm where:@"NOT objectCol.stringCol LIKE NULL"];
  1928. XCTAssertEqual(3U, nonNilLikeStrings.count);
  1929. XCTAssertEqualObjects((@[@"a", @"b", @""]), [nonNilLikeStrings valueForKeyPath:@"objectCol.stringCol"]);
  1930. RLMAssertCount(stringLinkClass, 3U, @"objectCol.stringCol IN {NULL, 'a'}");
  1931. RLMAssertCount(stringLinkClass, 1U, @"objectCol.stringCol CONTAINS 'a'");
  1932. RLMAssertCount(stringLinkClass, 1U, @"objectCol.stringCol BEGINSWITH 'a'");
  1933. RLMAssertCount(stringLinkClass, 1U, @"objectCol.stringCol ENDSWITH 'a'");
  1934. RLMAssertCount(stringLinkClass, 1U, @"objectCol.stringCol LIKE 'a'");
  1935. RLMAssertCount(stringLinkClass, 0U, @"objectCol.stringCol LIKE 'c'");
  1936. RLMAssertCount(stringLinkClass, 0U, @"objectCol.stringCol CONTAINS 'z'");
  1937. RLMAssertCount(stringLinkClass, 1U, @"objectCol.stringCol = ''");
  1938. };
  1939. testWithStringClass([LinkStringObject class], [StringObject class]);
  1940. testWithStringClass([LinkIndexedStringObject class], [IndexedStringObject class]);
  1941. }
  1942. - (void)testSortingColumnsWithNull {
  1943. RLMRealm *realm = [self realm];
  1944. [realm beginWriteTransaction];
  1945. {
  1946. NumberObject *no1 = [NumberObject createInRealm:realm withValue:@[@1, @1.1f, @1.1, @YES]];
  1947. NumberObject *noNull = [NumberObject createInRealm:realm withValue:@[NSNull.null, NSNull.null, NSNull.null, NSNull.null]];
  1948. NumberObject *no0 = [NumberObject createInRealm:realm withValue:@[@0, @0.0f, @0.0, @NO]];
  1949. for (RLMProperty *property in [[NumberObject alloc] init].objectSchema.properties) {
  1950. NSString *name = property.name;
  1951. RLMResults *ascending = [[NumberObject allObjectsInRealm:realm] sortedResultsUsingKeyPath:name ascending:YES];
  1952. XCTAssertEqualObjects([ascending valueForKey:name], ([@[noNull, no0, no1] valueForKey:name]));
  1953. RLMResults *descending = [[NumberObject allObjectsInRealm:realm] sortedResultsUsingKeyPath:name ascending:NO];
  1954. XCTAssertEqualObjects([descending valueForKey:name], ([@[no1, no0, noNull] valueForKey:name]));
  1955. }
  1956. }
  1957. {
  1958. DateObject *doPositive = [DateObject createInRealm:realm withValue:@[[NSDate dateWithTimeIntervalSince1970:100]]];
  1959. DateObject *doNegative = [DateObject createInRealm:realm withValue:@[[NSDate dateWithTimeIntervalSince1970:-100]]];
  1960. DateObject *doZero = [DateObject createInRealm:realm withValue:@[[NSDate dateWithTimeIntervalSince1970:0]]];
  1961. DateObject *doNull = [DateObject createInRealm:realm withValue:@[NSNull.null]];
  1962. RLMResults *ascending = [[DateObject allObjectsInRealm:realm] sortedResultsUsingKeyPath:@"dateCol" ascending:YES];
  1963. XCTAssertEqualObjects([ascending valueForKey:@"dateCol"], ([@[doNull, doNegative, doZero, doPositive] valueForKey:@"dateCol"]));
  1964. RLMResults *descending = [[DateObject allObjectsInRealm:realm] sortedResultsUsingKeyPath:@"dateCol" ascending:NO];
  1965. XCTAssertEqualObjects([descending valueForKey:@"dateCol"], ([@[doPositive, doZero, doNegative, doNull] valueForKey:@"dateCol"]));
  1966. }
  1967. {
  1968. StringObject *soA = [StringObject createInRealm:realm withValue:@[@"A"]];
  1969. StringObject *soEmpty = [StringObject createInRealm:realm withValue:@[@""]];
  1970. StringObject *soB = [StringObject createInRealm:realm withValue:@[@"B"]];
  1971. StringObject *soNull = [StringObject createInRealm:realm withValue:@[NSNull.null]];
  1972. StringObject *soAB = [StringObject createInRealm:realm withValue:@[@"AB"]];
  1973. RLMResults *ascending = [[StringObject allObjectsInRealm:realm] sortedResultsUsingKeyPath:@"stringCol" ascending:YES];
  1974. XCTAssertEqualObjects([ascending valueForKey:@"stringCol"], ([@[soNull, soEmpty, soA, soAB, soB] valueForKey:@"stringCol"]));
  1975. RLMResults *descending = [[StringObject allObjectsInRealm:realm] sortedResultsUsingKeyPath:@"stringCol" ascending:NO];
  1976. XCTAssertEqualObjects([descending valueForKey:@"stringCol"], ([@[soB, soAB, soA, soEmpty, soNull] valueForKey:@"stringCol"]));
  1977. }
  1978. [realm cancelWriteTransaction];
  1979. }
  1980. struct NullTestData {
  1981. __unsafe_unretained NSString *propertyName;
  1982. __unsafe_unretained NSString *nonMatchingStr;
  1983. __unsafe_unretained NSString *matchingStr;
  1984. __unsafe_unretained id nonMatchingValue;
  1985. __unsafe_unretained id matchingValue;
  1986. bool orderable;
  1987. bool substringOperations;
  1988. };
  1989. - (void)testPrimitiveOperatorsOnAllNullablePropertyTypes {
  1990. RLMRealm *realm = [self realm];
  1991. // nil on LHS is currently not supported by core
  1992. XCTAssertThrows([AllOptionalTypes objectsWhere:@"nil = boolObj"]);
  1993. // These need to be stored in variables because the struct does not retain them
  1994. NSData *matchingData = [@"" dataUsingEncoding:NSUTF8StringEncoding];
  1995. NSData *notMatchingData = [@"a" dataUsingEncoding:NSUTF8StringEncoding];
  1996. NSDate *matchingDate = [NSDate dateWithTimeIntervalSince1970:1];
  1997. NSDate *notMatchingDate = [NSDate dateWithTimeIntervalSince1970:2];
  1998. struct NullTestData data[] = {
  1999. {@"boolObj", @"YES", @"NO", @YES, @NO},
  2000. {@"intObj", @"1", @"0", @1, @0, true},
  2001. {@"floatObj", @"1", @"0", @1, @0, true},
  2002. {@"doubleObj", @"1", @"0", @1, @0, true},
  2003. {@"string", @"'a'", @"''", @"a", @"", false, true},
  2004. {@"data", nil, nil, notMatchingData, matchingData, false, true},
  2005. {@"date", nil, nil, notMatchingDate, matchingDate, true},
  2006. };
  2007. // Assert that the query "prop op value" gives expectedCount results when
  2008. // assembled via string formatting
  2009. #define RLMAssertCountWithString(expectedCount, op, prop, value) \
  2010. do { \
  2011. NSString *queryStr = [NSString stringWithFormat:@"%@ " #op " %@", prop, value]; \
  2012. NSUInteger actual = [AllOptionalTypes objectsWhere:queryStr].count; \
  2013. XCTAssertEqual(expectedCount, actual, @"%@: expected %@, got %@", queryStr, @(expectedCount), @(actual)); \
  2014. } while (0)
  2015. // Assert that the query "prop op value" gives expectedCount results when
  2016. // assembled via predicateWithFormat
  2017. #define RLMAssertCountWithPredicate(expectedCount, op, prop, value) \
  2018. do { \
  2019. NSPredicate *query = [NSPredicate predicateWithFormat:@"%K " #op " %@", prop, value]; \
  2020. NSUInteger actual = [AllOptionalTypes objectsWithPredicate:query].count; \
  2021. XCTAssertEqual(expectedCount, actual, @"%@ " #op " %@: expected %@, got %@", prop, value, @(expectedCount), @(actual)); \
  2022. } while (0)
  2023. // Assert that the given operator gives the expected count for each of the
  2024. // stored value, a different value, and nil
  2025. #define RLMAssertOperator(op, matchingCount, notMatchingCount, nilCount) \
  2026. do { \
  2027. if (d.matchingStr) { \
  2028. RLMAssertCountWithString(matchingCount, op, d.propertyName, d.matchingStr); \
  2029. RLMAssertCountWithString(notMatchingCount, op, d.propertyName, d.nonMatchingStr); \
  2030. } \
  2031. RLMAssertCountWithString(nilCount, op, d.propertyName, nil); \
  2032. \
  2033. RLMAssertCountWithPredicate(matchingCount, op, d.propertyName, d.matchingValue); \
  2034. RLMAssertCountWithPredicate(notMatchingCount, op, d.propertyName, d.nonMatchingValue); \
  2035. RLMAssertCountWithPredicate(nilCount, op, d.propertyName, nil); \
  2036. } while (0)
  2037. // First test with the `matchingValue` stored in each property
  2038. [realm beginWriteTransaction];
  2039. [AllOptionalTypes createInRealm:realm withValue:@[@NO, @0, @0, @0, @"", matchingData, matchingDate]];
  2040. [realm commitWriteTransaction];
  2041. for (size_t i = 0; i < sizeof(data) / sizeof(data[0]); ++i) {
  2042. struct NullTestData d = data[i];
  2043. RLMAssertOperator(=, 1U, 0U, 0U);
  2044. RLMAssertOperator(!=, 0U, 1U, 1U);
  2045. if (d.orderable) {
  2046. RLMAssertOperator(<, 0U, 1U, 0U);
  2047. RLMAssertOperator(<=, 1U, 1U, 0U);
  2048. RLMAssertOperator(>, 0U, 0U, 0U);
  2049. RLMAssertOperator(>=, 1U, 0U, 0U);
  2050. }
  2051. if (d.substringOperations) {
  2052. RLMAssertOperator(BEGINSWITH, 0U, 0U, 0U);
  2053. RLMAssertOperator(ENDSWITH, 0U, 0U, 0U);
  2054. RLMAssertOperator(CONTAINS, 0U, 0U, 0U);
  2055. }
  2056. }
  2057. // Retest with all properties nil
  2058. [realm beginWriteTransaction];
  2059. [realm deleteAllObjects];
  2060. [AllOptionalTypes createInRealm:realm withValue:@[NSNull.null, NSNull.null,
  2061. NSNull.null, NSNull.null,
  2062. NSNull.null, NSNull.null,
  2063. NSNull.null]];
  2064. [realm commitWriteTransaction];
  2065. for (size_t i = 0; i < sizeof(data) / sizeof(data[0]); ++i) {
  2066. struct NullTestData d = data[i];
  2067. RLMAssertOperator(=, 0U, 0U, 1U);
  2068. RLMAssertOperator(!=, 1U, 1U, 0U);
  2069. if (d.orderable) {
  2070. RLMAssertOperator(<, 0U, 0U, 0U);
  2071. RLMAssertOperator(<=, 0U, 0U, 1U);
  2072. RLMAssertOperator(>, 0U, 0U, 0U);
  2073. RLMAssertOperator(>=, 0U, 0U, 1U);
  2074. }
  2075. if (d.substringOperations) {
  2076. RLMAssertOperator(BEGINSWITH, 0U, 0U, 0U);
  2077. RLMAssertOperator(ENDSWITH, 0U, 0U, 0U);
  2078. RLMAssertOperator(CONTAINS, 0U, 0U, 0U);
  2079. }
  2080. }
  2081. }
  2082. - (void)testINPredicateOnNullWithNonNullValues
  2083. {
  2084. RLMRealm *realm = [self realm];
  2085. [realm beginWriteTransaction];
  2086. [AllOptionalTypes createInRealm:realm withValue:@[@YES, @1, @1, @1, @"abc",
  2087. [@"a" dataUsingEncoding:NSUTF8StringEncoding],
  2088. [NSDate dateWithTimeIntervalSince1970:1]]];
  2089. [realm commitWriteTransaction];
  2090. ////////////////////////
  2091. // Literal Predicates
  2092. ////////////////////////
  2093. // BOOL
  2094. [self testClass:[AllOptionalTypes class] withNormalCount:0 notCount:1 where:@"boolObj IN {NULL}"];
  2095. [self testClass:[AllOptionalTypes class] withNormalCount:1 notCount:0 where:@"boolObj IN {YES}"];
  2096. // int
  2097. [self testClass:[AllOptionalTypes class] withNormalCount:0 notCount:1 where:@"intObj IN {NULL}"];
  2098. [self testClass:[AllOptionalTypes class] withNormalCount:1 notCount:0 where:@"intObj IN {1}"];
  2099. // float
  2100. [self testClass:[AllOptionalTypes class] withNormalCount:0 notCount:1 where:@"floatObj IN {NULL}"];
  2101. [self testClass:[AllOptionalTypes class] withNormalCount:1 notCount:0 where:@"floatObj IN {1}"];
  2102. // double
  2103. [self testClass:[AllOptionalTypes class] withNormalCount:0 notCount:1 where:@"doubleObj IN {NULL}"];
  2104. [self testClass:[AllOptionalTypes class] withNormalCount:1 notCount:0 where:@"doubleObj IN {1}"];
  2105. // NSString
  2106. [self testClass:[AllOptionalTypes class] withNormalCount:0 notCount:1 where:@"string IN {NULL}"];
  2107. [self testClass:[AllOptionalTypes class] withNormalCount:1 notCount:0 where:@"string IN {'abc'}"];
  2108. // NSData
  2109. // Can't represent NSData with NSPredicate literal. See format predicates below
  2110. // NSDate
  2111. // Can't represent NSDate with NSPredicate literal. See format predicates below
  2112. ////////////////////////
  2113. // Format Predicates
  2114. ////////////////////////
  2115. // BOOL
  2116. [self testClass:[AllOptionalTypes class] withNormalCount:0U notCount:1U where:@"boolObj IN %@", @[NSNull.null]];
  2117. [self testClass:[AllOptionalTypes class] withNormalCount:1U notCount:0U where:@"boolObj IN %@", @[@YES]];
  2118. // int
  2119. [self testClass:[AllOptionalTypes class] withNormalCount:0U notCount:1U where:@"intObj IN %@", @[NSNull.null]];
  2120. [self testClass:[AllOptionalTypes class] withNormalCount:1U notCount:0U where:@"intObj IN %@", @[@1]];
  2121. // float
  2122. [self testClass:[AllOptionalTypes class] withNormalCount:0U notCount:1U where:@"floatObj IN %@", @[NSNull.null]];
  2123. [self testClass:[AllOptionalTypes class] withNormalCount:1U notCount:0U where:@"floatObj IN %@", @[@1]];
  2124. // double
  2125. [self testClass:[AllOptionalTypes class] withNormalCount:0U notCount:1U where:@"doubleObj IN %@", @[NSNull.null]];
  2126. [self testClass:[AllOptionalTypes class] withNormalCount:1U notCount:0U where:@"doubleObj IN %@", @[@1]];
  2127. // NSString
  2128. [self testClass:[AllOptionalTypes class] withNormalCount:1U notCount:0U where:@"string IN %@", @[@"abc"]];
  2129. [self testClass:[AllOptionalTypes class] withNormalCount:0U notCount:1U where:@"string IN %@", @[NSNull.null]];
  2130. // NSData
  2131. [self testClass:[AllOptionalTypes class] withNormalCount:0U notCount:1U where:@"data IN %@", @[NSNull.null]];
  2132. [self testClass:[AllOptionalTypes class] withNormalCount:1U notCount:0U where:@"data IN %@", @[[@"a" dataUsingEncoding:NSUTF8StringEncoding]]];
  2133. // NSDate
  2134. [self testClass:[AllOptionalTypes class] withNormalCount:0U notCount:1U where:@"date IN %@", @[NSNull.null]];
  2135. [self testClass:[AllOptionalTypes class] withNormalCount:1U notCount:0U where:@"date IN %@", @[[NSDate dateWithTimeIntervalSince1970:1]]];
  2136. }
  2137. - (void)testINPredicateOnNullWithNullValues
  2138. {
  2139. RLMRealm *realm = [self realm];
  2140. [realm beginWriteTransaction];
  2141. [AllOptionalTypes createInRealm:realm withValue:@[NSNull.null, NSNull.null,
  2142. NSNull.null, NSNull.null,
  2143. NSNull.null, NSNull.null,
  2144. NSNull.null]];
  2145. [realm commitWriteTransaction];
  2146. ////////////////////////
  2147. // Literal Predicates
  2148. ////////////////////////
  2149. // BOOL
  2150. [self testClass:[AllOptionalTypes class] withNormalCount:1 notCount:0 where:@"boolObj IN {NULL}"];
  2151. [self testClass:[AllOptionalTypes class] withNormalCount:0 notCount:1 where:@"boolObj IN {YES}"];
  2152. // int
  2153. [self testClass:[AllOptionalTypes class] withNormalCount:1 notCount:0 where:@"intObj IN {NULL}"];
  2154. [self testClass:[AllOptionalTypes class] withNormalCount:0 notCount:1 where:@"intObj IN {1}"];
  2155. // float
  2156. [self testClass:[AllOptionalTypes class] withNormalCount:1 notCount:0 where:@"floatObj IN {NULL}"];
  2157. [self testClass:[AllOptionalTypes class] withNormalCount:0 notCount:1 where:@"floatObj IN {1}"];
  2158. // double
  2159. [self testClass:[AllOptionalTypes class] withNormalCount:1 notCount:0 where:@"doubleObj IN {NULL}"];
  2160. [self testClass:[AllOptionalTypes class] withNormalCount:0 notCount:1 where:@"doubleObj IN {1}"];
  2161. // NSString
  2162. [self testClass:[AllOptionalTypes class] withNormalCount:1 notCount:0 where:@"string IN {NULL}"];
  2163. [self testClass:[AllOptionalTypes class] withNormalCount:0 notCount:1 where:@"string IN {'abc'}"];
  2164. // NSData
  2165. // Can't represent NSData with NSPredicate literal. See format predicates below
  2166. // NSDate
  2167. // Can't represent NSDate with NSPredicate literal. See format predicates below
  2168. ////////////////////////
  2169. // Format Predicates
  2170. ////////////////////////
  2171. // BOOL
  2172. [self testClass:[AllOptionalTypes class] withNormalCount:1U notCount:0U where:@"boolObj IN %@", @[NSNull.null]];
  2173. [self testClass:[AllOptionalTypes class] withNormalCount:0U notCount:1U where:@"boolObj IN %@", @[@YES]];
  2174. // int
  2175. [self testClass:[AllOptionalTypes class] withNormalCount:1U notCount:0U where:@"intObj IN %@", @[NSNull.null]];
  2176. [self testClass:[AllOptionalTypes class] withNormalCount:0U notCount:1U where:@"intObj IN %@", @[@1]];
  2177. // float
  2178. [self testClass:[AllOptionalTypes class] withNormalCount:1U notCount:0U where:@"floatObj IN %@", @[NSNull.null]];
  2179. [self testClass:[AllOptionalTypes class] withNormalCount:0U notCount:1U where:@"floatObj IN %@", @[@1]];
  2180. // double
  2181. [self testClass:[AllOptionalTypes class] withNormalCount:1U notCount:0U where:@"doubleObj IN %@", @[NSNull.null]];
  2182. [self testClass:[AllOptionalTypes class] withNormalCount:0U notCount:1U where:@"doubleObj IN %@", @[@1]];
  2183. // NSString
  2184. [self testClass:[AllOptionalTypes class] withNormalCount:0U notCount:1U where:@"string IN %@", @[@"abc"]];
  2185. [self testClass:[AllOptionalTypes class] withNormalCount:1U notCount:0U where:@"string IN %@", @[NSNull.null]];
  2186. // NSData
  2187. [self testClass:[AllOptionalTypes class] withNormalCount:1U notCount:0U where:@"data IN %@", @[NSNull.null]];
  2188. [self testClass:[AllOptionalTypes class] withNormalCount:0U notCount:1U where:@"data IN %@", @[[@"a" dataUsingEncoding:NSUTF8StringEncoding]]];
  2189. // NSDate
  2190. [self testClass:[AllOptionalTypes class] withNormalCount:1U notCount:0U where:@"date IN %@", @[NSNull.null]];
  2191. [self testClass:[AllOptionalTypes class] withNormalCount:0U notCount:1U where:@"date IN %@", @[[NSDate dateWithTimeIntervalSince1970:1]]];
  2192. }
  2193. - (void)testQueryOnRenamedProperties {
  2194. RLMRealm *realm = [self realm];
  2195. [realm beginWriteTransaction];
  2196. [RenamedProperties1 createInRealm:realm withValue:@[@1, @"a"]];
  2197. [RenamedProperties2 createInRealm:realm withValue:@[@2, @"b"]];
  2198. [realm commitWriteTransaction];
  2199. [self testClass:[RenamedProperties1 class] withNormalCount:2 notCount:0 where:@"propA != 0"];
  2200. [self testClass:[RenamedProperties1 class] withNormalCount:1 notCount:1 where:@"propA = 1"];
  2201. [self testClass:[RenamedProperties1 class] withNormalCount:1 notCount:1 where:@"propA = 2"];
  2202. [self testClass:[RenamedProperties1 class] withNormalCount:0 notCount:2 where:@"propA = 3"];
  2203. [self testClass:[RenamedProperties2 class] withNormalCount:2 notCount:0 where:@"propC != 0"];
  2204. [self testClass:[RenamedProperties2 class] withNormalCount:1 notCount:1 where:@"propC = 1"];
  2205. [self testClass:[RenamedProperties2 class] withNormalCount:1 notCount:1 where:@"propC = 2"];
  2206. [self testClass:[RenamedProperties2 class] withNormalCount:0 notCount:2 where:@"propC = 3"];
  2207. }
  2208. - (void)testQueryOverRenamedLinks {
  2209. RLMRealm *realm = [self realm];
  2210. [realm beginWriteTransaction];
  2211. id obj1 = [RenamedProperties1 createInRealm:realm withValue:@[@1, @"a"]];
  2212. id obj2 = [RenamedProperties2 createInRealm:realm withValue:@[@2, @"b"]];
  2213. [LinkToRenamedProperties1 createInRealm:realm withValue:@[obj1, NSNull.null, @[obj1]]];
  2214. [LinkToRenamedProperties2 createInRealm:realm withValue:@[obj2, NSNull.null, @[obj2]]];
  2215. [realm commitWriteTransaction];
  2216. [self testClass:[LinkToRenamedProperties1 class] withNormalCount:2 notCount:0 where:@"linkA.propA != 0"];
  2217. [self testClass:[LinkToRenamedProperties1 class] withNormalCount:1 notCount:1 where:@"linkA.propA = 1"];
  2218. [self testClass:[LinkToRenamedProperties1 class] withNormalCount:1 notCount:1 where:@"linkA.propA = 2"];
  2219. [self testClass:[LinkToRenamedProperties1 class] withNormalCount:0 notCount:2 where:@"linkA.propA = 3"];
  2220. [self testClass:[LinkToRenamedProperties2 class] withNormalCount:2 notCount:0 where:@"linkC.propC != 0"];
  2221. [self testClass:[LinkToRenamedProperties2 class] withNormalCount:1 notCount:1 where:@"linkC.propC = 1"];
  2222. [self testClass:[LinkToRenamedProperties2 class] withNormalCount:1 notCount:1 where:@"linkC.propC = 2"];
  2223. [self testClass:[LinkToRenamedProperties2 class] withNormalCount:0 notCount:2 where:@"linkC.propC = 3"];
  2224. [self testClass:[LinkToRenamedProperties1 class] withNormalCount:2 notCount:0 where:@"ANY array.propA != 0"];
  2225. [self testClass:[LinkToRenamedProperties1 class] withNormalCount:1 notCount:1 where:@"ANY array.propA = 1"];
  2226. [self testClass:[LinkToRenamedProperties1 class] withNormalCount:1 notCount:1 where:@"ANY array.propA = 2"];
  2227. [self testClass:[LinkToRenamedProperties1 class] withNormalCount:0 notCount:2 where:@"ANY array.propA = 3"];
  2228. [self testClass:[LinkToRenamedProperties2 class] withNormalCount:2 notCount:0 where:@"ANY array.propC != 0"];
  2229. [self testClass:[LinkToRenamedProperties2 class] withNormalCount:1 notCount:1 where:@"ANY array.propC = 1"];
  2230. [self testClass:[LinkToRenamedProperties2 class] withNormalCount:1 notCount:1 where:@"ANY array.propC = 2"];
  2231. [self testClass:[LinkToRenamedProperties2 class] withNormalCount:0 notCount:2 where:@"ANY array.propC = 3"];
  2232. }
  2233. - (void)testQueryOverRenamedBacklinks {
  2234. RLMRealm *realm = [self realm];
  2235. [realm beginWriteTransaction];
  2236. id obj1 = [RenamedProperties1 createInRealm:realm withValue:@[@1, @"a"]];
  2237. id obj2 = [RenamedProperties2 createInRealm:realm withValue:@[@2, @"b"]];
  2238. [LinkToRenamedProperties1 createInRealm:realm withValue:@[obj1, NSNull.null, @[obj1]]];
  2239. [LinkToRenamedProperties2 createInRealm:realm withValue:@[obj2, NSNull.null, @[obj2]]];
  2240. [realm commitWriteTransaction];
  2241. [self testClass:[RenamedProperties1 class] withNormalCount:2 notCount:0 where:@"ANY linking1.linkA.propA != 0"];
  2242. [self testClass:[RenamedProperties1 class] withNormalCount:1 notCount:1 where:@"ANY linking1.linkA.propA = 1"];
  2243. [self testClass:[RenamedProperties1 class] withNormalCount:1 notCount:1 where:@"ANY linking1.linkA.propA = 2"];
  2244. [self testClass:[RenamedProperties1 class] withNormalCount:0 notCount:2 where:@"ANY linking1.linkA.propA = 3"];
  2245. }
  2246. @end
  2247. @interface AsyncQueryTests : QueryTests
  2248. @end
  2249. @implementation AsyncQueryTests
  2250. - (RLMResults *)evaluate:(RLMResults *)results {
  2251. id token = [results addNotificationBlock:^(RLMResults *r, __unused RLMCollectionChange *changed, NSError *e) {
  2252. XCTAssertNil(e);
  2253. XCTAssertNotNil(r);
  2254. CFRunLoopStop(CFRunLoopGetCurrent());
  2255. }];
  2256. CFRunLoopRun();
  2257. [(RLMNotificationToken *)token invalidate];
  2258. return results;
  2259. }
  2260. @end
  2261. @interface QueryWithReversedColumnOrderTests : QueryTests
  2262. @end
  2263. @implementation QueryWithReversedColumnOrderTests
  2264. - (RLMRealm *)realm {
  2265. @autoreleasepool {
  2266. NSSet *classNames = [NSSet setWithArray:@[@"AllTypesObject", @"QueryObject", @"PersonObject", @"DogObject",
  2267. @"EmployeeObject", @"CompanyObject", @"OwnerObject"]];
  2268. RLMSchema *schema = [RLMSchema.sharedSchema copy];
  2269. NSMutableArray *objectSchemas = [schema.objectSchema mutableCopy];
  2270. for (NSUInteger i = 0; i < objectSchemas.count; i++) {
  2271. RLMObjectSchema *objectSchema = objectSchemas[i];
  2272. if ([classNames member:objectSchema.className]) {
  2273. objectSchemas[i] = [self reverseProperties:objectSchema];
  2274. }
  2275. }
  2276. schema.objectSchema = objectSchemas;
  2277. RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
  2278. config.customSchema = schema;
  2279. [RLMRealm realmWithConfiguration:config error:nil];
  2280. }
  2281. return RLMRealm.defaultRealm;
  2282. }
  2283. - (RLMObjectSchema *)reverseProperties:(RLMObjectSchema *)source {
  2284. RLMObjectSchema *objectSchema = [source copy];
  2285. objectSchema.properties = objectSchema.properties.reverseObjectEnumerator.allObjects;
  2286. return objectSchema;
  2287. }
  2288. @end