2016-02-18 19:59:34 +00:00
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
2015-11-16 23:05:50 +00:00
'use strict' ;
var Realm = require ( 'realm' ) ;
var BaseTest = require ( './base-test' ) ;
var TestCase = require ( './asserts' ) ;
var schemas = require ( './schemas' ) ;
2015-12-09 23:49:43 +00:00
var testCases = require ( './query-tests.json' ) ;
2015-11-16 23:05:50 +00:00
2015-11-18 00:13:52 +00:00
var typeConverters = { } ;
2015-11-28 02:26:58 +00:00
function convertValue ( value , schema , type ) {
var objSchema = schema . find ( function ( el ) { return el . name == type } ) ;
if ( ! objSchema ) {
throw "Object schema '" + type + "' not found in test suite." ;
}
return value . map ( function ( propValue , index ) {
if ( propValue == null ) {
return null ;
}
var property = objSchema . properties [ index ] ;
var converter = typeConverters [ property . type ] ;
var propType = property . objectType ? property . objectType : property . type ;
return converter ? converter ( propValue , schema , propType ) : propValue ;
} ) ;
}
2015-11-18 00:13:52 +00:00
typeConverters [ Realm . Types . DATE ] = function ( value ) { return new Date ( value ) ; } ;
2015-11-19 23:17:57 +00:00
typeConverters [ Realm . Types . DATA ] = function ( value ) { return new Uint8Array ( value ) ; } ;
2015-11-28 02:26:58 +00:00
typeConverters [ Realm . Types . OBJECT ] = convertValue ;
2015-11-18 00:13:52 +00:00
2015-11-18 01:17:54 +00:00
function runQuerySuite ( suite ) {
2015-11-18 00:13:52 +00:00
var realm = new Realm ( { schema : suite . schema } ) ;
2015-11-18 01:17:54 +00:00
var objects = suite . objects . map ( function ( obj ) {
2015-11-28 02:26:58 +00:00
return { type : obj . type , value : convertValue ( obj . value , suite . schema , obj . type ) } ;
2015-11-20 22:14:35 +00:00
} ) ;
2015-11-18 01:17:54 +00:00
realm . write ( function ( ) {
2015-11-20 22:14:35 +00:00
for ( var i = 0 ; i < objects . length ; i ++ ) {
objects [ i ] = realm . create ( objects [ i ] . type , objects [ i ] . value ) ;
2015-11-18 00:13:52 +00:00
}
} ) ;
2015-11-24 18:39:42 +00:00
function getArgs ( startArg ) {
2016-02-18 04:06:42 +00:00
var args = [ test [ startArg ] ] ;
for ( var i = startArg + 1 ; i < test . length ; i ++ ) {
2015-11-24 18:39:42 +00:00
var arg = test [ i ] ;
if ( Array . isArray ( arg ) ) {
// aray arguments correspond to [objectAtIndex, propertyName]
args . push ( objects [ arg [ 0 ] ] [ arg [ 1 ] ] ) ;
}
else {
args . push ( arg ) ;
}
}
return args ;
}
2016-01-12 03:32:46 +00:00
for ( var index in suite . tests ) {
var test = suite . tests [ index ] ;
2015-11-18 01:17:54 +00:00
if ( test [ 0 ] == "QueryCount" ) {
2016-02-18 04:06:42 +00:00
var type = test [ 2 ] ;
var args = getArgs ( 3 ) ;
var objects = realm . objects ( type ) ;
var length = objects . filtered . apply ( objects , args ) . length ;
TestCase . assertEqual ( test [ 1 ] , length , "Query '" + args [ 0 ] + "' on type '" + type + "' expected " + test [ 1 ] + " results, got " + length ) ;
2015-11-24 18:39:42 +00:00
}
else if ( test [ 0 ] == "ObjectSet" ) {
2016-02-18 04:06:42 +00:00
var type = test [ 2 ] ;
var args = getArgs ( 3 ) ;
var objects = realm . objects ( type ) ;
var results = objects . filtered . apply ( objects , args ) ;
TestCase . assertEqual ( test [ 1 ] . length , results . length , "Query '" + args [ 0 ] + "' on type '" + type + "' expected " + test [ 1 ] . length + " results, got " + results . length ) ;
2015-11-24 18:39:42 +00:00
2016-02-18 04:06:42 +00:00
var objSchema = suite . schema . find ( function ( el ) { return el . name == type } ) ;
2015-11-24 18:39:42 +00:00
var primary = objSchema . primaryKey ;
if ( ! primary ) {
throw "Primary key required for object comparison" ;
2015-11-18 00:13:52 +00:00
}
2015-11-18 01:17:54 +00:00
2016-02-29 05:40:27 +00:00
TestCase . assertArraysEqual ( test [ 1 ] , results . map ( function ( el ) {
return el [ primary ] ;
2015-11-24 18:39:42 +00:00
} ) ) ;
2015-11-18 01:17:54 +00:00
}
else if ( test [ 0 ] == "QueryThrows" ) {
2016-03-21 20:41:00 +00:00
var type = test [ 1 ] ;
var args = getArgs ( 2 ) ;
var objects = realm . objects ( type ) ;
2015-11-18 01:17:54 +00:00
TestCase . assertThrows ( function ( ) {
2016-03-21 20:41:00 +00:00
objects . filtered . apply ( objects , args ) ;
2015-11-18 20:40:25 +00:00
} , "Expected exception not thrown for query: " + JSON . stringify ( args ) ) ;
2015-11-18 01:17:54 +00:00
}
2015-11-19 22:36:42 +00:00
else if ( test [ 0 ] != "Disabled" ) {
2015-11-18 01:17:54 +00:00
throw "Invalid query test '" + test [ 0 ] + "'" ;
2015-11-18 00:13:52 +00:00
}
}
}
2015-11-18 19:32:52 +00:00
2015-11-18 01:17:54 +00:00
module . exports = BaseTest . extend ( {
2015-11-18 01:50:17 +00:00
testDateQueries : function ( ) {
2015-11-18 19:54:45 +00:00
runQuerySuite ( testCases . dateTests ) ;
2015-11-18 01:50:17 +00:00
} ,
2015-11-18 01:17:54 +00:00
testBoolQueries : function ( ) {
2015-11-18 19:54:45 +00:00
runQuerySuite ( testCases . boolTests ) ;
2015-11-16 23:05:50 +00:00
} ,
2015-11-18 01:50:17 +00:00
testIntQueries : function ( ) {
2015-11-18 19:54:45 +00:00
runQuerySuite ( testCases . intTests ) ;
2015-11-18 01:50:17 +00:00
} ,
2015-11-18 19:32:52 +00:00
testFloatQueries : function ( ) {
2015-11-18 20:17:39 +00:00
runQuerySuite ( testCases . floatTests ) ;
2015-11-18 19:32:52 +00:00
} ,
testDoubleQueries : function ( ) {
2015-11-18 20:17:39 +00:00
runQuerySuite ( testCases . doubleTests ) ;
} ,
testStringQueries : function ( ) {
runQuerySuite ( testCases . stringTests ) ;
2015-11-18 19:32:52 +00:00
} ,
2015-11-19 23:17:57 +00:00
testBinaryQueries : function ( ) {
runQuerySuite ( testCases . binaryTests ) ;
} ,
2015-11-20 22:14:35 +00:00
testObjectQueries : function ( ) {
runQuerySuite ( testCases . objectTests ) ;
2015-11-24 18:39:42 +00:00
} ,
testCompoundQueries : function ( ) {
runQuerySuite ( testCases . compoundTests ) ;
2015-11-28 02:26:58 +00:00
} ,
testKeyPathQueries : function ( ) {
runQuerySuite ( testCases . keyPathTests ) ;
2016-04-11 23:01:51 +00:00
} ,
testOptionalQueries : function ( ) {
runQuerySuite ( testCases . optionalTests ) ;
2015-11-20 22:14:35 +00:00
}
2015-11-16 23:05:50 +00:00
} ) ;
2015-11-18 01:17:54 +00:00
2015-11-16 23:05:50 +00:00
/ *
- ( void ) testQueryBetween
{
RLMRealm * realm = [ RLMRealm defaultRealm ] ;
NSDate * date1 = [ NSDate date ] ;
NSDate * date2 = [ date1 dateByAddingTimeInterval : 1 ] ;
NSDate * date3 = [ date2 dateByAddingTimeInterval : 1 ] ;
NSDate * date33 = [ date3 dateByAddingTimeInterval : 1 ] ;
StringObject * stringObj = [ StringObject new ] ;
stringObj . stringCol = @ "string" ;
[ realm beginWriteTransaction ] ;
[ AllTypesObject createInRealm : realm withValue : @ [ @ YES , @ 1 , @ 1.0 f , @ 1.0 , @ "a" , [ @ "a" dataUsingEncoding : NSUTF8StringEncoding ] , date1 , @ YES , @ ( ( long ) 1 ) , @ 1 , stringObj ] ] ;
[ AllTypesObject createInRealm : realm withValue : @ [ @ YES , @ 2 , @ 2.0 f , @ 2.0 , @ "b" , [ @ "b" dataUsingEncoding : NSUTF8StringEncoding ] , date2 , @ YES , @ ( ( long ) 2 ) , @ "mixed" , stringObj ] ] ;
[ AllTypesObject createInRealm : realm withValue : @ [ @ NO , @ 3 , @ 3.0 f , @ 3.0 , @ "c" , [ @ "c" dataUsingEncoding : NSUTF8StringEncoding ] , date3 , @ YES , @ ( ( long ) 3 ) , @ "mixed" , stringObj ] ] ;
[ AllTypesObject createInRealm : realm withValue : @ [ @ NO , @ 33 , @ 3.3 f , @ 3.3 , @ "cc" , [ @ "cc" dataUsingEncoding : NSUTF8StringEncoding ] , date33 , @ NO , @ ( ( long ) 3.3 ) , @ "mixed" , stringObj ] ] ;
[ realm commitWriteTransaction ] ;
RLMResults * betweenResults = [ AllTypesObject objectsWithPredicate : [ NSPredicate predicateWithFormat : @ "intCol BETWEEN %@" , @ [ @ 2 , @ 3 ] ] ] ;
XCTAssertEqual ( betweenResults . count , 2 U , @ "Should equal 2" ) ;
betweenResults = [ AllTypesObject objectsWithPredicate : [ NSPredicate predicateWithFormat : @ "floatCol BETWEEN %@" , @ [ @ 1.0 f , @ 4.0 f ] ] ] ;
XCTAssertEqual ( betweenResults . count , 4 U , @ "Should equal 4" ) ;
betweenResults = [ AllTypesObject objectsWithPredicate : [ NSPredicate predicateWithFormat : @ "doubleCol BETWEEN %@" , @ [ @ 3.0 , @ 7.0 f ] ] ] ;
XCTAssertEqual ( betweenResults . count , 2 U , @ "Should equal 2" ) ;
betweenResults = [ AllTypesObject objectsWithPredicate : [ NSPredicate predicateWithFormat : @ "dateCol BETWEEN %@" , @ [ date2 , date3 ] ] ] ;
XCTAssertEqual ( betweenResults . count , 2 U , @ "Should equal 2" ) ;
betweenResults = [ AllTypesObject objectsWhere : @ "intCol BETWEEN {2, 3}" ] ;
XCTAssertEqual ( betweenResults . count , 2 U , @ "Should equal 2" ) ;
betweenResults = [ AllTypesObject objectsWhere : @ "doubleCol BETWEEN {3.0, 7.0}" ] ;
XCTAssertEqual ( betweenResults . count , 2 U , @ "Should equal 2" ) ;
betweenResults = [ AllTypesObject . allObjects objectsWhere : @ "intCol BETWEEN {2, 3}" ] ;
XCTAssertEqual ( betweenResults . count , 2 U , @ "Should equal 2" ) ;
betweenResults = [ AllTypesObject . allObjects objectsWhere : @ "doubleCol BETWEEN {3.0, 7.0}" ] ;
XCTAssertEqual ( betweenResults . count , 2 U , @ "Should equal 2" ) ;
}
- ( void ) testArrayQuery
{
RLMRealm * realm = [ RLMRealm defaultRealm ] ;
[ realm beginWriteTransaction ] ;
[ PersonObject createInRealm : realm withValue : @ [ @ "Fiel" , @ 27 ] ] ;
[ PersonObject createInRealm : realm withValue : @ [ @ "Tim" , @ 29 ] ] ;
[ PersonObject createInRealm : realm withValue : @ [ @ "Ari" , @ 33 ] ] ;
[ realm commitWriteTransaction ] ;
// query on class
RLMResults * all = [ PersonObject allObjects ] ;
XCTAssertEqual ( all . count , 3 U , @ "Expecting 3 results" ) ;
RLMResults * some = [ [ PersonObject objectsWhere : @ "age > 28" ] sortedResultsUsingProperty : @ "age" ascending : YES ] ;
// query/order on array
XCTAssertEqual ( [ all objectsWhere : @ "age == 27" ] . count , 1 U , @ "Expecting 1 result" ) ;
XCTAssertEqual ( [ all objectsWhere : @ "age == 28" ] . count , 0 U , @ "Expecting 0 results" ) ;
some = [ some sortedResultsUsingProperty : @ "age" ascending : NO ] ;
XCTAssertEqualObjects ( [ some [ 0 ] name ] , @ "Ari" , @ "Ari should be first results" ) ;
}
- ( void ) verifySort : ( RLMRealm * ) realm column : ( NSString * ) column ascending : ( BOOL ) ascending expected : ( id ) val {
RLMResults * results = [ [ AllTypesObject allObjectsInRealm : realm ] sortedResultsUsingProperty : column ascending : ascending ] ;
AllTypesObject * obj = results [ 0 ] ;
XCTAssertEqualObjects ( obj [ column ] , val , @ "Array not sorted as expected - %@ != %@" , obj [ column ] , val ) ;
RLMArray * ar = ( RLMArray * ) [ [ [ ArrayOfAllTypesObject allObjectsInRealm : realm ] firstObject ] array ] ;
results = [ ar sortedResultsUsingProperty : column ascending : ascending ] ;
obj = results [ 0 ] ;
XCTAssertEqualObjects ( obj [ column ] , val , @ "Array not sorted as expected - %@ != %@" , obj [ column ] , val ) ;
}
- ( void ) verifySortWithAccuracy : ( RLMRealm * ) realm column : ( NSString * ) column ascending : ( BOOL ) ascending getter : ( double ( ^ ) ( id ) ) getter expected : ( double ) val accuracy : ( double ) accuracy {
// test TableView query
RLMResults * results = [ [ AllTypesObject allObjectsInRealm : realm ] sortedResultsUsingProperty : column ascending : ascending ] ;
XCTAssertEqualWithAccuracy ( getter ( results [ 0 ] [ column ] ) , val , accuracy , @ "Array not sorted as expected" ) ;
// test LinkView query
RLMArray * ar = ( RLMArray * ) [ [ [ ArrayOfAllTypesObject allObjectsInRealm : realm ] firstObject ] array ] ;
results = [ ar sortedResultsUsingProperty : column ascending : ascending ] ;
XCTAssertEqualWithAccuracy ( getter ( results [ 0 ] [ column ] ) , val , accuracy , @ "Array not sorted as expected" ) ;
}
- ( void ) testQuerySorting
{
RLMRealm * realm = [ RLMRealm defaultRealm ] ;
NSDate * date1 = [ NSDate date ] ;
NSDate * date2 = [ date1 dateByAddingTimeInterval : 1 ] ;
NSDate * date3 = [ date2 dateByAddingTimeInterval : 1 ] ;
NSDate * date33 = [ date3 dateByAddingTimeInterval : 1 ] ;
[ realm beginWriteTransaction ] ;
ArrayOfAllTypesObject * arrayOfAll = [ ArrayOfAllTypesObject createInRealm : realm withValue : @ { } ] ;
StringObject * stringObj = [ StringObject new ] ;
stringObj . stringCol = @ "string" ;
[ arrayOfAll . array addObject : [ AllTypesObject createInRealm : realm withValue : @ [ @ YES , @ 1 , @ 1.0 f , @ 1.0 , @ "a" , [ @ "a" dataUsingEncoding : NSUTF8StringEncoding ] , date1 , @ YES , @ 1 , @ 1 , stringObj ] ] ] ;
[ arrayOfAll . array addObject : [ AllTypesObject createInRealm : realm withValue : @ [ @ YES , @ 2 , @ 2.0 f , @ 2.0 , @ "b" , [ @ "b" dataUsingEncoding : NSUTF8StringEncoding ] , date2 , @ YES , @ 2 , @ "mixed" , stringObj ] ] ] ;
[ arrayOfAll . array addObject : [ AllTypesObject createInRealm : realm withValue : @ [ @ NO , @ 3 , @ 3.0 f , @ 3.0 , @ "c" , [ @ "c" dataUsingEncoding : NSUTF8StringEncoding ] , date3 , @ YES , @ 3 , @ "mixed" , stringObj ] ] ] ;
[ arrayOfAll . array addObject : [ AllTypesObject createInRealm : realm withValue : @ [ @ NO , @ 33 , @ 3.3 f , @ 3.3 , @ "cc" , [ @ "cc" dataUsingEncoding : NSUTF8StringEncoding ] , date33 , @ NO , @ 3 , @ "mixed" , stringObj ] ] ] ;
[ realm commitWriteTransaction ] ;
//////////// sort by boolCol
[ self verifySort : realm column : @ "boolCol" ascending : YES expected : @ NO ] ;
[ self verifySort : realm column : @ "boolCol" ascending : NO expected : @ YES ] ;
//////////// sort by intCol
[ self verifySort : realm column : @ "intCol" ascending : YES expected : @ 1 ] ;
[ self verifySort : realm column : @ "intCol" ascending : NO expected : @ 33 ] ;
//////////// sort by dateCol
double ( ^ dateGetter ) ( id ) = ^ ( NSDate * d ) { return d . timeIntervalSince1970 ; } ;
[ self verifySortWithAccuracy : realm column : @ "dateCol" ascending : YES getter : dateGetter expected : date1 . timeIntervalSince1970 accuracy : 1 ] ;
[ self verifySortWithAccuracy : realm column : @ "dateCol" ascending : NO getter : dateGetter expected : date33 . timeIntervalSince1970 accuracy : 1 ] ;
//////////// sort by doubleCol
double ( ^ doubleGetter ) ( id ) = ^ ( NSNumber * n ) { return n . doubleValue ; } ;
[ self verifySortWithAccuracy : realm column : @ "doubleCol" ascending : YES getter : doubleGetter expected : 1.0 accuracy : 0.0000001 ] ;
[ self verifySortWithAccuracy : realm column : @ "doubleCol" ascending : NO getter : doubleGetter expected : 3.3 accuracy : 0.0000001 ] ;
//////////// sort by floatCol
[ self verifySortWithAccuracy : realm column : @ "floatCol" ascending : YES getter : doubleGetter expected : 1.0 accuracy : 0.0000001 ] ;
[ self verifySortWithAccuracy : realm column : @ "floatCol" ascending : NO getter : doubleGetter expected : 3.3 accuracy : 0.0000001 ] ;
//////////// sort by stringCol
[ self verifySort : realm column : @ "stringCol" ascending : YES expected : @ "a" ] ;
[ self verifySort : realm column : @ "stringCol" ascending : NO expected : @ "cc" ] ;
// sort by mixed column
RLMAssertThrowsWithReasonMatching ( [ [ AllTypesObject allObjects ] sortedResultsUsingProperty : @ "mixedCol" ascending : YES ] , @ "'mixedCol' .* 'AllTypesObject': sorting is only supported .* type any" ) ;
XCTAssertThrows ( [ arrayOfAll . array sortedResultsUsingProperty : @ "mixedCol" ascending : NO ] ) ;
// sort invalid name
RLMAssertThrowsWithReasonMatching ( [ [ AllTypesObject allObjects ] sortedResultsUsingProperty : @ "invalidCol" ascending : YES ] , @ "'invalidCol'.* 'AllTypesObject'.* not found" ) ;
XCTAssertThrows ( [ arrayOfAll . array sortedResultsUsingProperty : @ "invalidCol" ascending : NO ] ) ;
// sort on key path
RLMAssertThrowsWithReasonMatching ( [ [ AllTypesObject allObjects ] sortedResultsUsingProperty : @ "key.path" ascending : YES ] , @ "key paths is not supported" ) ;
XCTAssertThrows ( [ arrayOfAll . array sortedResultsUsingProperty : @ "key.path" ascending : NO ] ) ;
}
- ( void ) testSortByMultipleColumns {
RLMRealm * realm = [ RLMRealm defaultRealm ] ;
[ realm beginWriteTransaction ] ;
DogObject * a1 = [ DogObject createInDefaultRealmWithValue : @ [ @ "a" , @ 1 ] ] ;
DogObject * a2 = [ DogObject createInDefaultRealmWithValue : @ [ @ "a" , @ 2 ] ] ;
DogObject * b1 = [ DogObject createInDefaultRealmWithValue : @ [ @ "b" , @ 1 ] ] ;
DogObject * b2 = [ DogObject createInDefaultRealmWithValue : @ [ @ "b" , @ 2 ] ] ;
[ realm commitWriteTransaction ] ;
bool ( ^ checkOrder ) ( NSArray * , NSArray * , NSArray * ) = ^ bool ( NSArray * properties , NSArray * ascending , NSArray * dogs ) {
NSArray * sort = @ [ [ RLMSortDescriptor sortDescriptorWithProperty : properties [ 0 ] ascending : [ ascending [ 0 ] boolValue ] ] ,
[ RLMSortDescriptor sortDescriptorWithProperty : properties [ 1 ] ascending : [ ascending [ 1 ] boolValue ] ] ] ;
RLMResults * actual = [ DogObject . allObjects sortedResultsUsingDescriptors : sort ] ;
return [ actual [ 0 ] isEqualToObject : dogs [ 0 ] ]
&& [ actual [ 1 ] isEqualToObject : dogs [ 1 ] ]
&& [ actual [ 2 ] isEqualToObject : dogs [ 2 ] ]
&& [ actual [ 3 ] isEqualToObject : dogs [ 3 ] ] ;
} ;
// Check each valid sort
XCTAssertTrue ( checkOrder ( @ [ @ "dogName" , @ "age" ] , @ [ @ YES , @ YES ] , @ [ a1 , a2 , b1 , b2 ] ) ) ;
XCTAssertTrue ( checkOrder ( @ [ @ "dogName" , @ "age" ] , @ [ @ YES , @ NO ] , @ [ a2 , a1 , b2 , b1 ] ) ) ;
XCTAssertTrue ( checkOrder ( @ [ @ "dogName" , @ "age" ] , @ [ @ NO , @ YES ] , @ [ b1 , b2 , a1 , a2 ] ) ) ;
XCTAssertTrue ( checkOrder ( @ [ @ "dogName" , @ "age" ] , @ [ @ NO , @ NO ] , @ [ b2 , b1 , a2 , a1 ] ) ) ;
XCTAssertTrue ( checkOrder ( @ [ @ "age" , @ "dogName" ] , @ [ @ YES , @ YES ] , @ [ a1 , b1 , a2 , b2 ] ) ) ;
XCTAssertTrue ( checkOrder ( @ [ @ "age" , @ "dogName" ] , @ [ @ YES , @ NO ] , @ [ b1 , a1 , b2 , a2 ] ) ) ;
XCTAssertTrue ( checkOrder ( @ [ @ "age" , @ "dogName" ] , @ [ @ NO , @ YES ] , @ [ a2 , b2 , a1 , b1 ] ) ) ;
XCTAssertTrue ( checkOrder ( @ [ @ "age" , @ "dogName" ] , @ [ @ NO , @ NO ] , @ [ b2 , a2 , b1 , a1 ] ) ) ;
}
- ( void ) testSortedLinkViewWithDeletion {
RLMRealm * realm = [ RLMRealm defaultRealm ] ;
NSDate * date1 = [ NSDate date ] ;
NSDate * date2 = [ date1 dateByAddingTimeInterval : 1 ] ;
NSDate * date3 = [ date2 dateByAddingTimeInterval : 1 ] ;
NSDate * date33 = [ date3 dateByAddingTimeInterval : 1 ] ;
[ realm beginWriteTransaction ] ;
ArrayOfAllTypesObject * arrayOfAll = [ ArrayOfAllTypesObject createInRealm : realm withValue : @ { } ] ;
StringObject * stringObj = [ StringObject new ] ;
stringObj . stringCol = @ "string" ;
[ arrayOfAll . array addObject : [ AllTypesObject createInRealm : realm withValue : @ [ @ YES , @ 1 , @ 1.0 f , @ 1.0 , @ "a" , [ @ "a" dataUsingEncoding : NSUTF8StringEncoding ] , date1 , @ YES , @ 1 , @ 1 , stringObj ] ] ] ;
[ arrayOfAll . array addObject : [ AllTypesObject createInRealm : realm withValue : @ [ @ YES , @ 2 , @ 2.0 f , @ 2.0 , @ "b" , [ @ "b" dataUsingEncoding : NSUTF8StringEncoding ] , date2 , @ YES , @ 2 , @ "mixed" , stringObj ] ] ] ;
[ arrayOfAll . array addObject : [ AllTypesObject createInRealm : realm withValue : @ [ @ NO , @ 3 , @ 3.0 f , @ 3.0 , @ "c" , [ @ "c" dataUsingEncoding : NSUTF8StringEncoding ] , date3 , @ YES , @ 3 , @ "mixed" , stringObj ] ] ] ;
[ arrayOfAll . array addObject : [ AllTypesObject createInRealm : realm withValue : @ [ @ NO , @ 33 , @ 3.3 f , @ 3.3 , @ "cc" , [ @ "cc" dataUsingEncoding : NSUTF8StringEncoding ] , date33 , @ NO , @ 3 , @ "mixed" , stringObj ] ] ] ;
[ realm commitWriteTransaction ] ;
RLMResults * results = [ arrayOfAll . array sortedResultsUsingProperty : @ "stringCol" ascending : NO ] ;
XCTAssertEqualObjects ( [ results [ 0 ] stringCol ] , @ "cc" ) ;
// delete cc, add d results should update
[ realm transactionWithBlock : ^ {
[ arrayOfAll . array removeObjectAtIndex : 3 ] ;
// create extra alltypesobject
[ arrayOfAll . array addObject : [ AllTypesObject createInRealm : realm withValue : @ [ @ YES , @ 1 , @ 1.0 f , @ 1.0 , @ "d" , [ @ "d" dataUsingEncoding : NSUTF8StringEncoding ] , date1 , @ YES , @ ( ( long ) 1 ) , @ 1 , stringObj ] ] ] ;
} ] ;
XCTAssertEqualObjects ( [ results [ 0 ] stringCol ] , @ "d" ) ;
XCTAssertEqualObjects ( [ results [ 1 ] stringCol ] , @ "c" ) ;
// delete from realm should be removed from results
[ realm transactionWithBlock : ^ {
[ realm deleteObject : arrayOfAll . array . lastObject ] ;
} ] ;
XCTAssertEqualObjects ( [ results [ 0 ] stringCol ] , @ "c" ) ;
}
- ( void ) testQueryingSortedQueryPreservesOrder {
RLMRealm * realm = [ RLMRealm defaultRealm ] ;
[ realm beginWriteTransaction ] ;
for ( int i = 0 ; i < 5 ; ++ i ) {
[ IntObject createInRealm : realm withValue : @ [ @ ( i ) ] ] ;
}
ArrayPropertyObject * array = [ ArrayPropertyObject createInRealm : realm withValue : @ [ @ "name" , @ [ ] , [ IntObject allObjects ] ] ] ;
[ realm commitWriteTransaction ] ;
RLMResults * asc = [ IntObject . allObjects sortedResultsUsingProperty : @ "intCol" ascending : YES ] ;
RLMResults * desc = [ IntObject . allObjects sortedResultsUsingProperty : @ "intCol" ascending : NO ] ;
// sanity check; would work even without sort order being preserved
XCTAssertEqual ( 2 , [ [ [ asc objectsWhere : @ "intCol >= 2" ] firstObject ] intCol ] ) ;
// check query on allObjects and query on query
XCTAssertEqual ( 4 , [ [ [ desc objectsWhere : @ "intCol >= 2" ] firstObject ] intCol ] ) ;
XCTAssertEqual ( 3 , [ [ [ [ desc objectsWhere : @ "intCol >= 2" ] objectsWhere : @ "intCol < 4" ] firstObject ] intCol ] ) ;
// same thing but on an linkview
asc = [ array . intArray sortedResultsUsingProperty : @ "intCol" ascending : YES ] ;
desc = [ array . intArray sortedResultsUsingProperty : @ "intCol" ascending : NO ] ;
XCTAssertEqual ( 2 , [ [ [ asc objectsWhere : @ "intCol >= 2" ] firstObject ] intCol ] ) ;
XCTAssertEqual ( 4 , [ [ [ desc objectsWhere : @ "intCol >= 2" ] firstObject ] intCol ] ) ;
XCTAssertEqual ( 3 , [ [ [ [ desc objectsWhere : @ "intCol >= 2" ] objectsWhere : @ "intCol < 4" ] firstObject ] intCol ] ) ;
}
- ( void ) testPredicateNotSupported
{
// These are things which are valid predicates, but which we do not support
// Aggregate operators on non-arrays
XCTAssertThrows ( [ PersonObject objectsWhere : @ "ANY age > 5" ] ) ;
XCTAssertThrows ( [ PersonObject objectsWhere : @ "ALL age > 5" ] ) ;
XCTAssertThrows ( [ PersonObject objectsWhere : @ "SOME age > 5" ] ) ;
XCTAssertThrows ( [ PersonObject objectsWhere : @ "NONE age > 5" ] ) ;
// nil on LHS of comparison with nullable property
XCTAssertThrows ( [ AllOptionalTypes objectsWhere : @ "nil = boolObj" ] ) ;
XCTAssertThrows ( [ AllOptionalTypes objectsWhere : @ "nil = intObj" ] ) ;
XCTAssertThrows ( [ AllOptionalTypes objectsWhere : @ "nil = floatObj" ] ) ;
XCTAssertThrows ( [ AllOptionalTypes objectsWhere : @ "nil = doubleObj" ] ) ;
XCTAssertThrows ( [ AllOptionalTypes objectsWhere : @ "nil = string" ] ) ;
XCTAssertThrows ( [ AllOptionalTypes objectsWhere : @ "nil = data" ] ) ;
XCTAssertThrows ( [ AllOptionalTypes objectsWhere : @ "nil = date" ] ) ;
// comparing two constants
XCTAssertThrows ( [ PersonObject objectsWhere : @ "5 = 5" ] ) ;
XCTAssertThrows ( [ PersonObject objectsWhere : @ "nil = nil" ] ) ;
// substring operations with constant on LHS
XCTAssertThrows ( [ AllOptionalTypes objectsWhere : @ "'' CONTAINS string" ] ) ;
XCTAssertThrows ( [ AllOptionalTypes objectsWhere : @ "'' BEGINSWITH string" ] ) ;
XCTAssertThrows ( [ AllOptionalTypes objectsWhere : @ "'' ENDSWITH string" ] ) ;
XCTAssertThrows ( ( [ AllOptionalTypes objectsWhere : @ "%@ CONTAINS data" , [ NSData data ] ] ) ) ;
// data is missing stuff
XCTAssertThrows ( [ AllOptionalTypes objectsWhere : @ "data = data" ] ) ;
XCTAssertThrows ( ( [ LinkToAllTypesObject objectsWhere : @ "%@ = allTypesCol.binaryCol" , [ NSData data ] ] ) ) ;
XCTAssertThrows ( ( [ LinkToAllTypesObject objectsWhere : @ "allTypesCol.binaryCol CONTAINS %@" , [ NSData data ] ] ) ) ;
// LinkList equality is unsupport since the semantics are unclear
XCTAssertThrows ( ( [ ArrayOfAllTypesObject objectsWhere : @ "ANY array = array" ] ) ) ;
// subquery
XCTAssertThrows ( ( [ ArrayOfAllTypesObject objectsWhere : @ "SUBQUERY(array, $obj, $obj.intCol = 5).@count > 1" ] ) ) ;
}
- ( void ) testPredicateMisuse
{
RLMRealm * realm = [ RLMRealm defaultRealm ] ;
NSString * className = PersonObject . className ;
// invalid column/property name
RLMAssertThrowsWithReasonMatching ( [ realm objects : className where : @ "height > 72" ] , @ "'height' not found in .* 'PersonObject'" ) ;
// wrong/invalid data types
RLMAssertThrowsWithReasonMatching ( [ realm objects : className where : @ "age != xyz" ] , @ "'xyz' not found in .* 'PersonObject'" ) ;
RLMAssertThrowsWithReasonMatching ( [ realm objects : className where : @ "name == 3" ] , @ "type string .* property 'name' .* 'PersonObject'.*: 3" ) ;
RLMAssertThrowsWithReasonMatching ( [ realm objects : className where : @ "age IN {'xyz'}" ] , @ "type int .* property 'age' .* 'PersonObject'.*: xyz" ) ;
XCTAssertThrows ( [ realm objects : className where : @ "name IN {3}" ] , @ "invalid type" ) ;
className = AllTypesObject . className ;
XCTAssertThrows ( [ realm objects : className where : @ "boolCol == Foo" ] , @ "invalid type" ) ;
XCTAssertThrows ( [ realm objects : className where : @ "boolCol == 2" ] , @ "invalid type" ) ;
XCTAssertThrows ( [ realm objects : className where : @ "dateCol == 7" ] , @ "invalid type" ) ;
XCTAssertThrows ( [ realm objects : className where : @ "doubleCol == The" ] , @ "invalid type" ) ;
XCTAssertThrows ( [ realm objects : className where : @ "floatCol == Bar" ] , @ "invalid type" ) ;
XCTAssertThrows ( [ realm objects : className where : @ "intCol == Baz" ] , @ "invalid type" ) ;
className = PersonObject . className ;
// compare two constants
XCTAssertThrows ( [ realm objects : className where : @ "3 == 3" ] , @ "comparing 2 constants" ) ;
// invalid strings
RLMAssertThrowsWithReasonMatching ( [ realm objects : className where : @ "" ] , @ "Unable to parse" ) ;
XCTAssertThrows ( [ realm objects : className where : @ "age" ] , @ "column name only" ) ;
XCTAssertThrows ( [ realm objects : className where : @ "sdlfjasdflj" ] , @ "gibberish" ) ;
XCTAssertThrows ( [ realm objects : className where : @ "age * 25" ] , @ "invalid operator" ) ;
XCTAssertThrows ( [ realm objects : className where : @ "age === 25" ] , @ "invalid operator" ) ;
XCTAssertThrows ( [ realm objects : className where : @ "," ] , @ "comma" ) ;
XCTAssertThrows ( [ realm objects : className where : @ "()" ] , @ "parens" ) ;
// not a link column
RLMAssertThrowsWithReasonMatching ( [ realm objects : className where : @ "age.age == 25" ] , @ "'age' is not a link .* 'PersonObject'" ) ;
XCTAssertThrows ( [ realm objects : className where : @ "age.age.age == 25" ] ) ;
// abuse of BETWEEN
RLMAssertThrowsWithReasonMatching ( [ realm objects : className where : @ "age BETWEEN 25" ] , @ "type NSArray for BETWEEN" ) ;
RLMAssertThrowsWithReasonMatching ( [ realm objects : className where : @ "age BETWEEN Foo" ] , @ "BETWEEN operator must compare a KeyPath with an aggregate" ) ;
RLMAssertThrowsWithReasonMatching ( [ realm objects : className where : @ "age BETWEEN {age, age}" ] , @ "must be constant values" ) ;
RLMAssertThrowsWithReasonMatching ( [ realm objects : className where : @ "age BETWEEN {age, 0}" ] , @ "must be constant values" ) ;
RLMAssertThrowsWithReasonMatching ( [ realm objects : className where : @ "age BETWEEN {0, age}" ] , @ "must be constant values" ) ;
RLMAssertThrowsWithReasonMatching ( [ realm objects : className where : @ "age BETWEEN {0, {1, 10}}" ] , @ "must be constant values" ) ;
NSPredicate * pred = [ NSPredicate predicateWithFormat : @ "age BETWEEN %@" , @ [ @ 1 ] ] ;
RLMAssertThrowsWithReasonMatching ( [ realm objects : className withPredicate : pred ] , @ "exactly two objects" ) ;
pred = [ NSPredicate predicateWithFormat : @ "age BETWEEN %@" , @ [ @ 1 , @ 2 , @ 3 ] ] ;
RLMAssertThrowsWithReasonMatching ( [ realm objects : className withPredicate : pred ] , @ "exactly two objects" ) ;
pred = [ NSPredicate predicateWithFormat : @ "age BETWEEN %@" , @ [ @ "Foo" , @ "Bar" ] ] ;
RLMAssertThrowsWithReasonMatching ( [ realm objects : className withPredicate : pred ] , @ "type int for BETWEEN" ) ;
pred = [ NSPredicate predicateWithFormat : @ "age BETWEEN %@" , @ [ @ 1.5 , @ 2.5 ] ] ;
RLMAssertThrowsWithReasonMatching ( [ realm objects : className withPredicate : pred ] , @ "type int for BETWEEN" ) ;
pred = [ NSPredicate predicateWithFormat : @ "age BETWEEN %@" , @ [ @ 1 , @ [ @ 2 , @ 3 ] ] ] ;
RLMAssertThrowsWithReasonMatching ( [ realm objects : className withPredicate : pred ] , @ "type int for BETWEEN" ) ;
pred = [ NSPredicate predicateWithFormat : @ "age BETWEEN %@" , @ { @ 25 : @ 35 } ] ;
RLMAssertThrowsWithReasonMatching ( [ realm objects : className withPredicate : pred ] , @ "type NSArray for BETWEEN" ) ;
pred = [ NSPredicate predicateWithFormat : @ "height BETWEEN %@" , @ [ @ 25 , @ 35 ] ] ;
RLMAssertThrowsWithReasonMatching ( [ realm objects : className withPredicate : pred ] , @ "'height' not found .* 'PersonObject'" ) ;
// bad type in link IN
XCTAssertThrows ( [ PersonLinkObject objectsInRealm : realm where : @ "person.age IN {'Tim'}" ] ) ;
}
- ( void ) testTwoColumnComparison
{
RLMRealm * realm = [ RLMRealm defaultRealm ] ;
[ realm beginWriteTransaction ] ;
[ self . queryObjectClass createInRealm : realm withValue : @ [ @ YES , @ YES , @ 1 , @ 2 , @ 23.0 f , @ 1.7 f , @ 0.0 , @ 5.55 , @ "a" , @ "a" ] ] ;
[ self . queryObjectClass createInRealm : realm withValue : @ [ @ YES , @ NO , @ 1 , @ 3 , @ - 5.3 f , @ 4.21 f , @ 1.0 , @ 4.44 , @ "a" , @ "A" ] ] ;
[ self . queryObjectClass createInRealm : realm withValue : @ [ @ NO , @ NO , @ 2 , @ 2 , @ 1.0 f , @ 3.55 f , @ 99.9 , @ 6.66 , @ "a" , @ "ab" ] ] ;
[ self . queryObjectClass createInRealm : realm withValue : @ [ @ NO , @ YES , @ 3 , @ 6 , @ 4.21 f , @ 1.0 f , @ 1.0 , @ 7.77 , @ "a" , @ "AB" ] ] ;
[ self . queryObjectClass createInRealm : realm withValue : @ [ @ YES , @ YES , @ 4 , @ 5 , @ 23.0 f , @ 23.0 f , @ 7.4 , @ 8.88 , @ "a" , @ "b" ] ] ;
[ self . queryObjectClass createInRealm : realm withValue : @ [ @ YES , @ NO , @ 15 , @ 8 , @ 1.0 f , @ 66.0 f , @ 1.01 , @ 9.99 , @ "a" , @ "ba" ] ] ;
[ self . queryObjectClass createInRealm : realm withValue : @ [ @ NO , @ YES , @ 15 , @ 15 , @ 1.0 f , @ 66.0 f , @ 1.01 , @ 9.99 , @ "a" , @ "BA" ] ] ;
[ realm commitWriteTransaction ] ;
XCTAssertEqual ( 7 U , [ self . queryObjectClass objectsWhere : @ "bool1 == bool1" ] . count ) ;
XCTAssertEqual ( 3 U , [ self . queryObjectClass objectsWhere : @ "bool1 == bool2" ] . count ) ;
XCTAssertEqual ( 4 U , [ self . queryObjectClass objectsWhere : @ "bool1 != bool2" ] . count ) ;
XCTAssertEqual ( 7 U , [ self . queryObjectClass objectsWhere : @ "int1 == int1" ] . count ) ;
XCTAssertEqual ( 2 U , [ self . queryObjectClass objectsWhere : @ "int1 == int2" ] . count ) ;
XCTAssertEqual ( 5 U , [ self . queryObjectClass objectsWhere : @ "int1 != int2" ] . count ) ;
XCTAssertEqual ( 1 U , [ self . queryObjectClass objectsWhere : @ "int1 > int2" ] . count ) ;
XCTAssertEqual ( 4 U , [ self . queryObjectClass objectsWhere : @ "int1 < int2" ] . count ) ;
XCTAssertEqual ( 3 U , [ self . queryObjectClass objectsWhere : @ "int1 >= int2" ] . count ) ;
XCTAssertEqual ( 6 U , [ self . queryObjectClass objectsWhere : @ "int1 <= int2" ] . count ) ;
XCTAssertEqual ( 7 U , [ self . queryObjectClass objectsWhere : @ "float1 == float1" ] . count ) ;
XCTAssertEqual ( 1 U , [ self . queryObjectClass objectsWhere : @ "float1 == float2" ] . count ) ;
XCTAssertEqual ( 6 U , [ self . queryObjectClass objectsWhere : @ "float1 != float2" ] . count ) ;
XCTAssertEqual ( 2 U , [ self . queryObjectClass objectsWhere : @ "float1 > float2" ] . count ) ;
XCTAssertEqual ( 4 U , [ self . queryObjectClass objectsWhere : @ "float1 < float2" ] . count ) ;
XCTAssertEqual ( 3 U , [ self . queryObjectClass objectsWhere : @ "float1 >= float2" ] . count ) ;
XCTAssertEqual ( 5 U , [ self . queryObjectClass objectsWhere : @ "float1 <= float2" ] . count ) ;
XCTAssertEqual ( 7 U , [ self . queryObjectClass objectsWhere : @ "double1 == double1" ] . count ) ;
XCTAssertEqual ( 0 U , [ self . queryObjectClass objectsWhere : @ "double1 == double2" ] . count ) ;
XCTAssertEqual ( 7 U , [ self . queryObjectClass objectsWhere : @ "double1 != double2" ] . count ) ;
XCTAssertEqual ( 1 U , [ self . queryObjectClass objectsWhere : @ "double1 > double2" ] . count ) ;
XCTAssertEqual ( 6 U , [ self . queryObjectClass objectsWhere : @ "double1 < double2" ] . count ) ;
XCTAssertEqual ( 1 U , [ self . queryObjectClass objectsWhere : @ "double1 >= double2" ] . count ) ;
XCTAssertEqual ( 6 U , [ self . queryObjectClass objectsWhere : @ "double1 <= double2" ] . count ) ;
XCTAssertEqual ( 7 U , [ self . queryObjectClass objectsWhere : @ "string1 == string1" ] . count ) ;
XCTAssertEqual ( 1 U , [ self . queryObjectClass objectsWhere : @ "string1 == string2" ] . count ) ;
XCTAssertEqual ( 6 U , [ self . queryObjectClass objectsWhere : @ "string1 != string2" ] . count ) ;
XCTAssertEqual ( 7 U , [ self . queryObjectClass objectsWhere : @ "string1 CONTAINS string1" ] . count ) ;
XCTAssertEqual ( 1 U , [ self . queryObjectClass objectsWhere : @ "string1 CONTAINS string2" ] . count ) ;
XCTAssertEqual ( 3 U , [ self . queryObjectClass objectsWhere : @ "string2 CONTAINS string1" ] . count ) ;
XCTAssertEqual ( 7 U , [ self . queryObjectClass objectsWhere : @ "string1 BEGINSWITH string1" ] . count ) ;
XCTAssertEqual ( 1 U , [ self . queryObjectClass objectsWhere : @ "string1 BEGINSWITH string2" ] . count ) ;
XCTAssertEqual ( 2 U , [ self . queryObjectClass objectsWhere : @ "string2 BEGINSWITH string1" ] . count ) ;
XCTAssertEqual ( 7 U , [ self . queryObjectClass objectsWhere : @ "string1 ENDSWITH string1" ] . count ) ;
XCTAssertEqual ( 1 U , [ self . queryObjectClass objectsWhere : @ "string1 ENDSWITH string2" ] . count ) ;
XCTAssertEqual ( 2 U , [ self . queryObjectClass objectsWhere : @ "string2 ENDSWITH string1" ] . count ) ;
XCTAssertEqual ( 7 U , [ self . queryObjectClass objectsWhere : @ "string1 ==[c] string1" ] . count ) ;
XCTAssertEqual ( 2 U , [ self . queryObjectClass objectsWhere : @ "string1 ==[c] string2" ] . count ) ;
XCTAssertEqual ( 5 U , [ self . queryObjectClass objectsWhere : @ "string1 !=[c] string2" ] . count ) ;
XCTAssertEqual ( 7 U , [ self . queryObjectClass objectsWhere : @ "string1 CONTAINS[c] string1" ] . count ) ;
XCTAssertEqual ( 2 U , [ self . queryObjectClass objectsWhere : @ "string1 CONTAINS[c] string2" ] . count ) ;
XCTAssertEqual ( 6 U , [ self . queryObjectClass objectsWhere : @ "string2 CONTAINS[c] string1" ] . count ) ;
XCTAssertEqual ( 7 U , [ self . queryObjectClass objectsWhere : @ "string1 BEGINSWITH[c] string1" ] . count ) ;
XCTAssertEqual ( 2 U , [ self . queryObjectClass objectsWhere : @ "string1 BEGINSWITH[c] string2" ] . count ) ;
XCTAssertEqual ( 4 U , [ self . queryObjectClass objectsWhere : @ "string2 BEGINSWITH[c] string1" ] . count ) ;
XCTAssertEqual ( 7 U , [ self . queryObjectClass objectsWhere : @ "string1 ENDSWITH[c] string1" ] . count ) ;
XCTAssertEqual ( 2 U , [ self . queryObjectClass objectsWhere : @ "string1 ENDSWITH[c] string2" ] . count ) ;
XCTAssertEqual ( 4 U , [ self . queryObjectClass objectsWhere : @ "string2 ENDSWITH[c] string1" ] . count ) ;
RLMAssertThrowsWithReasonMatching ( [ self . queryObjectClass objectsWhere : @ "int1 == float1" ] ,
@ "Property type mismatch between int and float" ) ;
RLMAssertThrowsWithReasonMatching ( [ self . queryObjectClass objectsWhere : @ "float2 >= double1" ] ,
@ "Property type mismatch between float and double" ) ;
RLMAssertThrowsWithReasonMatching ( [ self . queryObjectClass objectsWhere : @ "double2 <= int2" ] ,
@ "Property type mismatch between double and int" ) ;
RLMAssertThrowsWithReasonMatching ( [ self . queryObjectClass objectsWhere : @ "int2 != string1" ] ,
@ "Property type mismatch between int and string" ) ;
RLMAssertThrowsWithReasonMatching ( [ self . queryObjectClass objectsWhere : @ "float1 > string1" ] ,
@ "Property type mismatch between float and string" ) ;
RLMAssertThrowsWithReasonMatching ( [ self . queryObjectClass objectsWhere : @ "double1 < string1" ] ,
@ "Property type mismatch between double and string" ) ;
}
- ( void ) testKeyPathLocationInComparison
{
NSExpression * keyPath = [ NSExpression expressionForKeyPath : @ "intCol" ] ;
NSExpression * expr = [ NSExpression expressionForConstantValue : @ 0 ] ;
NSPredicate * predicate ;
predicate = [ RLMPredicateUtil defaultPredicateGenerator ] ( keyPath , expr ) ;
XCTAssert ( [ RLMPredicateUtil isEmptyIntColWithPredicate : predicate ] ,
@ "Key path to the left in an integer comparison." ) ;
predicate = [ RLMPredicateUtil defaultPredicateGenerator ] ( expr , keyPath ) ;
XCTAssert ( [ RLMPredicateUtil isEmptyIntColWithPredicate : predicate ] ,
@ "Key path to the right in an integer comparison." ) ;
predicate = [ RLMPredicateUtil defaultPredicateGenerator ] ( keyPath , keyPath ) ;
XCTAssert ( [ RLMPredicateUtil isEmptyIntColWithPredicate : predicate ] ,
@ "Key path in both locations in an integer comparison." ) ;
predicate = [ RLMPredicateUtil defaultPredicateGenerator ] ( expr , expr ) ;
XCTAssertThrowsSpecificNamed ( [ RLMPredicateUtil isEmptyIntColWithPredicate : predicate ] ,
NSException , @ "Invalid predicate expressions" ,
@ "Key path in absent in an integer comparison." ) ;
}
- ( void ) testLiveQueriesInsideTransaction
{
RLMRealm * realm = [ RLMRealm defaultRealm ] ;
[ realm beginWriteTransaction ] ;
{
[ self . queryObjectClass createInRealm : realm withValue : @ [ @ YES , @ YES , @ 1 , @ 2 , @ 23.0 f , @ 1.7 f , @ 0.0 , @ 5.55 , @ "" , @ "" ] ] ;
RLMResults * resultsQuery = [ self . queryObjectClass objectsWhere : @ "bool1 = YES" ] ;
RLMResults * resultsTableView = [ self . queryObjectClass objectsWhere : @ "bool1 = YES" ] ;
// Force resultsTableView to form the TableView to verify that it syncs
// correctly, and don't call anything but count on resultsQuery so that
// it always reruns the query count method
( void ) [ resultsTableView firstObject ] ;
XCTAssertEqual ( resultsQuery . count , 1 U ) ;
XCTAssertEqual ( resultsTableView . count , 1 U ) ;
// Delete the (only) object in result set
[ realm deleteObject : [ resultsTableView lastObject ] ] ;
XCTAssertEqual ( resultsQuery . count , 0 U ) ;
XCTAssertEqual ( resultsTableView . count , 0 U ) ;
// Add an object that does not match query
QueryObject * q1 = [ self . queryObjectClass createInRealm : realm withValue : @ [ @ NO , @ YES , @ 1 , @ 2 , @ 23.0 f , @ 1.7 f , @ 0.0 , @ 5.55 , @ "" , @ "" ] ] ;
XCTAssertEqual ( resultsQuery . count , 0 U ) ;
XCTAssertEqual ( resultsTableView . count , 0 U ) ;
// Change object to match query
q1 [ @ "bool1" ] = @ YES ;
XCTAssertEqual ( resultsQuery . count , 1 U ) ;
XCTAssertEqual ( resultsTableView . count , 1 U ) ;
// Add another object that matches
[ self . queryObjectClass createInRealm : realm withValue : @ [ @ YES , @ NO , @ 1 , @ 3 , @ - 5.3 f , @ 4.21 f , @ 1.0 , @ 4.44 , @ "" , @ "" ] ] ;
XCTAssertEqual ( resultsQuery . count , 2 U ) ;
XCTAssertEqual ( resultsTableView . count , 2 U ) ;
}
[ realm commitWriteTransaction ] ;
}
- ( void ) testLiveQueriesBetweenTransactions
{
RLMRealm * realm = [ RLMRealm defaultRealm ] ;
[ realm beginWriteTransaction ] ;
[ self . queryObjectClass createInRealm : realm withValue : @ [ @ YES , @ YES , @ 1 , @ 2 , @ 23.0 f , @ 1.7 f , @ 0.0 , @ 5.55 , @ "" , @ "" ] ] ;
[ realm commitWriteTransaction ] ;
RLMResults * resultsQuery = [ self . queryObjectClass objectsWhere : @ "bool1 = YES" ] ;
RLMResults * resultsTableView = [ self . queryObjectClass objectsWhere : @ "bool1 = YES" ] ;
// Force resultsTableView to form the TableView to verify that it syncs
// correctly, and don't call anything but count on resultsQuery so that
// it always reruns the query count method
( void ) [ resultsTableView firstObject ] ;
XCTAssertEqual ( resultsQuery . count , 1 U ) ;
XCTAssertEqual ( resultsTableView . count , 1 U ) ;
// Delete the (only) object in result set
[ realm beginWriteTransaction ] ;
[ realm deleteObject : [ resultsTableView lastObject ] ] ;
[ realm commitWriteTransaction ] ;
XCTAssertEqual ( resultsQuery . count , 0 U ) ;
XCTAssertEqual ( resultsTableView . count , 0 U ) ;
// Add an object that does not match query
[ realm beginWriteTransaction ] ;
QueryObject * q1 = [ self . queryObjectClass createInRealm : realm withValue : @ [ @ NO , @ YES , @ 1 , @ 2 , @ 23.0 f , @ 1.7 f , @ 0.0 , @ 5.55 , @ "" , @ "" ] ] ;
[ realm commitWriteTransaction ] ;
XCTAssertEqual ( resultsQuery . count , 0 U ) ;
XCTAssertEqual ( resultsTableView . count , 0 U ) ;
// Change object to match query
[ realm beginWriteTransaction ] ;
q1 [ @ "bool1" ] = @ YES ;
[ realm commitWriteTransaction ] ;
XCTAssertEqual ( resultsQuery . count , 1 U ) ;
XCTAssertEqual ( resultsTableView . count , 1 U ) ;
// Add another object that matches
[ realm beginWriteTransaction ] ;
[ self . queryObjectClass createInRealm : realm withValue : @ [ @ YES , @ NO , @ 1 , @ 3 , @ - 5.3 f , @ 4.21 f , @ 1.0 , @ 4.44 , @ "" , @ "" ] ] ;
[ realm commitWriteTransaction ] ;
XCTAssertEqual ( resultsQuery . count , 2 U ) ;
XCTAssertEqual ( resultsTableView . count , 2 U ) ;
}
- ( void ) makeDogWithName : ( NSString * ) name owner : ( NSString * ) ownerName {
RLMRealm * realm = [ self realmWithTestPath ] ;
OwnerObject * owner = [ [ OwnerObject alloc ] init ] ;
owner . name = ownerName ;
owner . dog = [ [ DogObject alloc ] init ] ;
owner . dog . dogName = name ;
[ realm beginWriteTransaction ] ;
[ realm addObject : owner ] ;
[ realm commitWriteTransaction ] ;
}
- ( void ) makeDogWithAge : ( int ) age owner : ( NSString * ) ownerName {
RLMRealm * realm = [ self realmWithTestPath ] ;
OwnerObject * owner = [ [ OwnerObject alloc ] init ] ;
owner . name = ownerName ;
owner . dog = [ [ DogObject alloc ] init ] ;
owner . dog . dogName = @ "" ;
owner . dog . age = age ;
[ realm beginWriteTransaction ] ;
[ realm addObject : owner ] ;
[ realm commitWriteTransaction ] ;
}
- ( void ) testLinkQueryString
{
RLMRealm * realm = [ self realmWithTestPath ] ;
[ self makeDogWithName : @ "Harvie" owner : @ "Tim" ] ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.dogName = 'Harvie'" ] . count ) , 1 U ) ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.dogName != 'Harvie'" ] . count ) , 0 U ) ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.dogName = 'eivraH'" ] . count ) , 0 U ) ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.dogName = 'Fido'" ] . count ) , 0 U ) ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.dogName IN {'Fido', 'Harvie'}" ] . count ) , 1 U ) ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.dogName IN {'Fido', 'eivraH'}" ] . count ) , 0 U ) ;
[ self makeDogWithName : @ "Harvie" owner : @ "Joe" ] ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.dogName = 'Harvie'" ] . count ) , 2 U ) ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.dogName != 'Harvie'" ] . count ) , 0 U ) ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.dogName = 'eivraH'" ] . count ) , 0 U ) ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.dogName = 'Fido'" ] . count ) , 0 U ) ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.dogName IN {'Fido', 'Harvie'}" ] . count ) , 2 U ) ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.dogName IN {'Fido', 'eivraH'}" ] . count ) , 0 U ) ;
[ self makeDogWithName : @ "Fido" owner : @ "Jim" ] ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.dogName = 'Harvie'" ] . count ) , 2 U ) ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.dogName != 'Harvie'" ] . count ) , 1 U ) ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.dogName = 'eivraH'" ] . count ) , 0 U ) ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.dogName = 'Fido'" ] . count ) , 1 U ) ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.dogName IN {'Fido', 'Harvie'}" ] . count ) , 3 U ) ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.dogName IN {'Fido', 'eivraH'}" ] . count ) , 1 U ) ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.dogName = 'Harvie' and name = 'Tim'" ] . count ) , 1 U ) ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.dogName = 'Harvie' and name = 'Jim'" ] . count ) , 0 U ) ;
// test invalid operators
XCTAssertThrows ( [ realm objects : [ OwnerObject className ] where : @ "dog.dogName > 'Harvie'" ] , @ "Invalid operator should throw" ) ;
}
- ( void ) testLinkQueryInt
{
RLMRealm * realm = [ self realmWithTestPath ] ;
[ self makeDogWithAge : 5 owner : @ "Tim" ] ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.age = 5" ] . count ) , 1 U ) ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.age != 5" ] . count ) , 0 U ) ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.age = 10" ] . count ) , 0 U ) ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.age = 8" ] . count ) , 0 U ) ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.age IN {5, 8}" ] . count ) , 1 U ) ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.age IN {8, 10}" ] . count ) , 0 U ) ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.age BETWEEN {0, 10}" ] . count ) , 1 U ) ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.age BETWEEN {0, 7}" ] . count ) , 1 U ) ;
[ self makeDogWithAge : 5 owner : @ "Joe" ] ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.age = 5" ] . count ) , 2 U ) ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.age != 5" ] . count ) , 0 U ) ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.age = 10" ] . count ) , 0 U ) ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.age = 8" ] . count ) , 0 U ) ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.age IN {5, 8}" ] . count ) , 2 U ) ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.age IN {8, 10}" ] . count ) , 0 U ) ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.age BETWEEN {0, 10}" ] . count ) , 2 U ) ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.age BETWEEN {0, 7}" ] . count ) , 2 U ) ;
[ self makeDogWithAge : 8 owner : @ "Jim" ] ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.age = 5" ] . count ) , 2 U ) ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.age != 5" ] . count ) , 1 U ) ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.age = 10" ] . count ) , 0 U ) ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.age = 8" ] . count ) , 1 U ) ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.age IN {5, 8}" ] . count ) , 3 U ) ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.age IN {8, 10}" ] . count ) , 1 U ) ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.age BETWEEN {0, 10}" ] . count ) , 3 U ) ;
XCTAssertEqual ( ( [ OwnerObject objectsInRealm : realm where : @ "dog.age BETWEEN {0, 7}" ] . count ) , 2 U ) ;
}
- ( void ) testLinkQueryAllTypes
{
RLMRealm * realm = [ self realmWithTestPath ] ;
NSDate * now = [ NSDate dateWithTimeIntervalSince1970 : 100000 ] ;
LinkToAllTypesObject * linkToAllTypes = [ [ LinkToAllTypesObject alloc ] init ] ;
linkToAllTypes . allTypesCol = [ [ AllTypesObject alloc ] init ] ;
linkToAllTypes . allTypesCol . boolCol = YES ;
linkToAllTypes . allTypesCol . intCol = 1 ;
linkToAllTypes . allTypesCol . floatCol = 1.1 f ;
linkToAllTypes . allTypesCol . doubleCol = 1.11 ;
linkToAllTypes . allTypesCol . stringCol = @ "string" ;
linkToAllTypes . allTypesCol . binaryCol = [ NSData dataWithBytes : "a" length : 1 ] ;
linkToAllTypes . allTypesCol . dateCol = now ;
linkToAllTypes . allTypesCol . cBoolCol = YES ;
linkToAllTypes . allTypesCol . longCol = 11 ;
linkToAllTypes . allTypesCol . mixedCol = @ 0 ;
StringObject * obj = [ [ StringObject alloc ] initWithValue : @ [ @ "string" ] ] ;
linkToAllTypes . allTypesCol . objectCol = obj ;
[ realm beginWriteTransaction ] ;
[ realm addObject : linkToAllTypes ] ;
[ realm commitWriteTransaction ] ;
XCTAssertEqual ( [ [ realm objects : [ LinkToAllTypesObject className ] where : @ "allTypesCol.boolCol = YES" ] count ] , 1 U ) ;
XCTAssertEqual ( [ [ realm objects : [ LinkToAllTypesObject className ] where : @ "allTypesCol.boolCol = NO" ] count ] , 0 U ) ;
XCTAssertEqual ( [ [ realm objects : [ LinkToAllTypesObject className ] where : @ "allTypesCol.intCol = 1" ] count ] , 1 U ) ;
XCTAssertEqual ( [ [ realm objects : [ LinkToAllTypesObject className ] where : @ "allTypesCol.intCol != 1" ] count ] , 0 U ) ;
XCTAssertEqual ( [ [ realm objects : [ LinkToAllTypesObject className ] where : @ "allTypesCol.intCol > 0" ] count ] , 1 U ) ;
XCTAssertEqual ( [ [ realm objects : [ LinkToAllTypesObject className ] where : @ "allTypesCol.intCol > 1" ] count ] , 0 U ) ;
NSPredicate * predEq = [ NSPredicate predicateWithFormat : @ "allTypesCol.floatCol = %f" , 1.1 ] ;
XCTAssertEqual ( [ LinkToAllTypesObject objectsInRealm : realm withPredicate : predEq ] . count , 1 U ) ;
NSPredicate * predLessEq = [ NSPredicate predicateWithFormat : @ "allTypesCol.floatCol <= %f" , 1.1 ] ;
XCTAssertEqual ( [ LinkToAllTypesObject objectsInRealm : realm withPredicate : predLessEq ] . count , 1 U ) ;
NSPredicate * predLess = [ NSPredicate predicateWithFormat : @ "allTypesCol.floatCol < %f" , 1.1 ] ;
XCTAssertEqual ( [ LinkToAllTypesObject objectsInRealm : realm withPredicate : predLess ] . count , 0 U ) ;
XCTAssertEqual ( [ [ realm objects : [ LinkToAllTypesObject className ] where : @ "allTypesCol.doubleCol = 1.11" ] count ] , 1 U ) ;
XCTAssertEqual ( [ [ realm objects : [ LinkToAllTypesObject className ] where : @ "allTypesCol.doubleCol >= 1.11" ] count ] , 1 U ) ;
XCTAssertEqual ( [ [ realm objects : [ LinkToAllTypesObject className ] where : @ "allTypesCol.doubleCol > 1.11" ] count ] , 0 U ) ;
XCTAssertEqual ( [ [ realm objects : [ LinkToAllTypesObject className ] where : @ "allTypesCol.longCol = 11" ] count ] , 1 U ) ;
XCTAssertEqual ( [ [ realm objects : [ LinkToAllTypesObject className ] where : @ "allTypesCol.longCol != 11" ] count ] , 0 U ) ;
XCTAssertEqual ( ( [ [ realm objects : [ LinkToAllTypesObject className ] where : @ "allTypesCol.dateCol = %@" , now ] count ] ) , 1 U ) ;
XCTAssertEqual ( ( [ [ realm objects : [ LinkToAllTypesObject className ] where : @ "allTypesCol.dateCol != %@" , now ] count ] ) , 0 U ) ;
}
- ( void ) testLinkQueryInvalid {
XCTAssertThrows ( [ LinkToAllTypesObject objectsWhere : @ "allTypesCol.binaryCol = 'a'" ] , @ "Binary data not supported" ) ;
XCTAssertThrows ( [ LinkToAllTypesObject objectsWhere : @ "allTypesCol.mixedCol = 'a'" ] , @ "Mixed data not supported" ) ;
XCTAssertThrows ( [ LinkToAllTypesObject objectsWhere : @ "allTypesCol.invalidCol = 'a'" ] , @ "Invalid column name should throw" ) ;
XCTAssertThrows ( [ LinkToAllTypesObject objectsWhere : @ "allTypesCol.longCol = 'a'" ] , @ "Wrong data type should throw" ) ;
XCTAssertThrows ( [ LinkToAllTypesObject objectsWhere : @ "intArray.intCol > 5" ] , @ "RLMArray query without ANY modifier should throw" ) ;
}
- ( void ) testLinkQueryMany
{
RLMRealm * realm = [ self realmWithTestPath ] ;
ArrayPropertyObject * arrPropObj1 = [ [ ArrayPropertyObject alloc ] init ] ;
arrPropObj1 . name = @ "Test" ;
for ( NSUInteger i = 0 ; i < 10 ; i ++ ) {
StringObject * sobj = [ [ StringObject alloc ] init ] ;
sobj . stringCol = [ NSString stringWithFormat : @ "%lu" , ( unsigned long ) i ] ;
[ arrPropObj1 . array addObject : sobj ] ;
IntObject * iobj = [ [ IntObject alloc ] init ] ;
iobj . intCol = ( int ) i ;
[ arrPropObj1 . intArray addObject : iobj ] ;
}
[ realm beginWriteTransaction ] ;
[ realm addObject : arrPropObj1 ] ;
[ realm commitWriteTransaction ] ;
XCTAssertEqual ( [ [ realm objects : [ ArrayPropertyObject className ] where : @ "ANY intArray.intCol > 10" ] count ] , 0 U ) ;
XCTAssertEqual ( [ [ realm objects : [ ArrayPropertyObject className ] where : @ "ANY intArray.intCol > 5" ] count ] , 1 U ) ;
XCTAssertEqual ( [ [ realm objects : [ ArrayPropertyObject className ] where : @ "ANY array.stringCol = '1'" ] count ] , 1 U ) ;
XCTAssertEqual ( [ realm objects : [ ArrayPropertyObject className ] where : @ "NONE intArray.intCol == 5" ] . count , 0 U ) ;
XCTAssertEqual ( [ realm objects : [ ArrayPropertyObject className ] where : @ "NONE intArray.intCol > 10" ] . count , 1 U ) ;
ArrayPropertyObject * arrPropObj2 = [ [ ArrayPropertyObject alloc ] init ] ;
arrPropObj2 . name = @ "Test" ;
for ( NSUInteger i = 0 ; i < 4 ; i ++ ) {
StringObject * sobj = [ [ StringObject alloc ] init ] ;
sobj . stringCol = [ NSString stringWithFormat : @ "%lu" , ( unsigned long ) i ] ;
[ arrPropObj2 . array addObject : sobj ] ;
IntObject * iobj = [ [ IntObject alloc ] init ] ;
iobj . intCol = ( int ) i ;
[ arrPropObj2 . intArray addObject : iobj ] ;
}
[ realm beginWriteTransaction ] ;
[ realm addObject : arrPropObj2 ] ;
[ realm commitWriteTransaction ] ;
XCTAssertEqual ( [ [ realm objects : [ ArrayPropertyObject className ] where : @ "ANY intArray.intCol > 10" ] count ] , 0 U ) ;
XCTAssertEqual ( [ [ realm objects : [ ArrayPropertyObject className ] where : @ "ANY intArray.intCol > 5" ] count ] , 1 U ) ;
XCTAssertEqual ( [ [ realm objects : [ ArrayPropertyObject className ] where : @ "ANY intArray.intCol > 2" ] count ] , 2 U ) ;
XCTAssertEqual ( [ realm objects : [ ArrayPropertyObject className ] where : @ "NONE intArray.intCol == 5" ] . count , 1 U ) ;
XCTAssertEqual ( [ realm objects : [ ArrayPropertyObject className ] where : @ "NONE intArray.intCol > 10" ] . count , 2 U ) ;
}
- ( void ) testMultiLevelLinkQuery
{
RLMRealm * realm = [ self realmWithTestPath ] ;
[ realm beginWriteTransaction ] ;
CircleObject * circle = nil ;
for ( int i = 0 ; i < 5 ; ++ i ) {
circle = [ CircleObject createInRealm : realm withValue : @ { @ "data" : [ NSString stringWithFormat : @ "%d" , i ] ,
@ "next" : circle ? : NSNull . null } ] ;
}
[ realm commitWriteTransaction ] ;
XCTAssertTrue ( [ circle isEqualToObject : [ CircleObject objectsInRealm : realm where : @ "data = '4'" ] . firstObject ] ) ;
XCTAssertTrue ( [ circle isEqualToObject : [ CircleObject objectsInRealm : realm where : @ "next.data = '3'" ] . firstObject ] ) ;
XCTAssertTrue ( [ circle isEqualToObject : [ CircleObject objectsInRealm : realm where : @ "next.next.data = '2'" ] . firstObject ] ) ;
XCTAssertTrue ( [ circle isEqualToObject : [ CircleObject objectsInRealm : realm where : @ "next.next.next.data = '1'" ] . firstObject ] ) ;
XCTAssertTrue ( [ circle isEqualToObject : [ CircleObject objectsInRealm : realm where : @ "next.next.next.next.data = '0'" ] . firstObject ] ) ;
XCTAssertTrue ( [ circle . next isEqualToObject : [ CircleObject objectsInRealm : realm where : @ "next.next.next.data = '0'" ] . firstObject ] ) ;
XCTAssertTrue ( [ circle . next . next isEqualToObject : [ CircleObject objectsInRealm : realm where : @ "next.next.data = '0'" ] . firstObject ] ) ;
XCTAssertNoThrow ( ( [ CircleObject objectsInRealm : realm where : @ "next = %@" , circle ] ) ) ;
XCTAssertThrows ( ( [ CircleObject objectsInRealm : realm where : @ "next.next = %@" , circle ] ) ) ;
XCTAssertTrue ( [ circle . next . next . next . next isEqualToObject : [ CircleObject objectsInRealm : realm where : @ "next = nil" ] . firstObject ] ) ;
}
- ( void ) testArrayMultiLevelLinkQuery
{
RLMRealm * realm = [ self realmWithTestPath ] ;
[ realm beginWriteTransaction ] ;
CircleObject * circle = nil ;
for ( int i = 0 ; i < 5 ; ++ i ) {
circle = [ CircleObject createInRealm : realm withValue : @ { @ "data" : [ NSString stringWithFormat : @ "%d" , i ] ,
@ "next" : circle ? : NSNull . null } ] ;
}
[ CircleArrayObject createInRealm : realm withValue : @ [ [ CircleObject allObjectsInRealm : realm ] ] ] ;
[ realm commitWriteTransaction ] ;
XCTAssertEqual ( 1 U , [ CircleArrayObject objectsInRealm : realm where : @ "ANY circles.data = '4'" ] . count ) ;
XCTAssertEqual ( 0 U , [ CircleArrayObject objectsInRealm : realm where : @ "ANY circles.next.data = '4'" ] . count ) ;
XCTAssertEqual ( 1 U , [ CircleArrayObject objectsInRealm : realm where : @ "ANY circles.next.data = '3'" ] . count ) ;
XCTAssertEqual ( 1 U , [ CircleArrayObject objectsInRealm : realm where : @ "ANY circles.data = '3'" ] . count ) ;
XCTAssertEqual ( 1 U , [ CircleArrayObject objectsInRealm : realm where : @ "NONE circles.next.data = '4'" ] . count ) ;
XCTAssertEqual ( 0 U , [ CircleArrayObject objectsInRealm : realm where : @ "ANY circles.next.next.data = '3'" ] . count ) ;
XCTAssertEqual ( 1 U , [ CircleArrayObject objectsInRealm : realm where : @ "ANY circles.next.next.data = '2'" ] . count ) ;
XCTAssertEqual ( 1 U , [ CircleArrayObject objectsInRealm : realm where : @ "ANY circles.next.data = '2'" ] . count ) ;
XCTAssertEqual ( 1 U , [ CircleArrayObject objectsInRealm : realm where : @ "ANY circles.data = '2'" ] . count ) ;
XCTAssertEqual ( 1 U , [ CircleArrayObject objectsInRealm : realm where : @ "NONE circles.next.next.data = '3'" ] . count ) ;
XCTAssertThrows ( [ CircleArrayObject objectsInRealm : realm where : @ "ANY data = '2'" ] ) ;
XCTAssertThrows ( [ CircleArrayObject objectsInRealm : realm where : @ "ANY circles.next = '2'" ] ) ;
XCTAssertThrows ( [ CircleArrayObject objectsInRealm : realm where : @ "ANY data.circles = '2'" ] ) ;
XCTAssertThrows ( [ CircleArrayObject objectsInRealm : realm where : @ "circles.data = '2'" ] ) ;
XCTAssertThrows ( [ CircleArrayObject objectsInRealm : realm where : @ "NONE data.circles = '2'" ] ) ;
}
- ( void ) testClass : ( Class ) class
withNormalCount : ( NSUInteger ) normalCount
notCount : ( NSUInteger ) notCount
where : ( NSString * ) predicateFormat , ...
{
va _list args ;
va _start ( args , predicateFormat ) ;
va _end ( args ) ;
XCTAssertEqual ( normalCount , [ [ class objectsWithPredicate : [ NSPredicate predicateWithFormat : predicateFormat arguments : args ] ] count ] ) ;
predicateFormat = [ NSString stringWithFormat : @ "NOT(%@)" , predicateFormat ] ;
va _start ( args , predicateFormat ) ;
va _end ( args ) ;
XCTAssertEqual ( notCount , [ [ class objectsWithPredicate : [ NSPredicate predicateWithFormat : predicateFormat arguments : args ] ] count ] ) ;
}
- ( void ) testINPredicate
{
RLMRealm * realm = [ RLMRealm defaultRealm ] ;
[ realm beginWriteTransaction ] ;
StringObject * so = [ StringObject createInRealm : realm withValue : ( @ [ @ "abc" ] ) ] ;
[ AllTypesObject createInRealm : realm withValue : @ [ @ YES , @ 1 , @ 1.0 f , @ 1.0 , @ "abc" , [ @ "a" dataUsingEncoding : NSUTF8StringEncoding ] , [ NSDate dateWithTimeIntervalSince1970 : 1 ] , @ YES , @ 1 LL , @ 1 , so ] ] ;
[ realm commitWriteTransaction ] ;
// Tests for each type always follow: none, some, more
////////////////////////
// Literal Predicates
////////////////////////
// BOOL
[ self testClass : [ AllTypesObject class ] withNormalCount : 0 notCount : 1 where : @ "boolCol IN {NO}" ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 1 notCount : 0 where : @ "boolCol IN {YES}" ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 1 notCount : 0 where : @ "boolCol IN {NO, YES}" ] ;
// int
[ self testClass : [ AllTypesObject class ] withNormalCount : 0 notCount : 1 where : @ "intCol IN {0, 2, 3}" ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 1 notCount : 0 where : @ "intCol IN {1}" ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 1 notCount : 0 where : @ "intCol IN {1, 2}" ] ;
// float
[ self testClass : [ AllTypesObject class ] withNormalCount : 0 notCount : 1 where : @ "floatCol IN {0, 2, 3}" ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 1 notCount : 0 where : @ "floatCol IN {1}" ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 1 notCount : 0 where : @ "floatCol IN {1, 2}" ] ;
// double
[ self testClass : [ AllTypesObject class ] withNormalCount : 0 notCount : 1 where : @ "doubleCol IN {0, 2, 3}" ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 1 notCount : 0 where : @ "doubleCol IN {1}" ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 1 notCount : 0 where : @ "doubleCol IN {1, 2}" ] ;
// NSString
[ self testClass : [ AllTypesObject class ] withNormalCount : 1 notCount : 0 where : @ "stringCol IN {'abc'}" ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 0 notCount : 1 where : @ "stringCol IN {'def'}" ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 0 notCount : 1 where : @ "stringCol IN {'ABC'}" ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 1 notCount : 0 where : @ "stringCol IN[c] {'abc'}" ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 1 notCount : 0 where : @ "stringCol IN[c] {'ABC'}" ] ;
// NSData
// Can't represent NSData with NSPredicate literal. See format predicates below
// NSDate
// Can't represent NSDate with NSPredicate literal. See format predicates below
// bool
[ self testClass : [ AllTypesObject class ] withNormalCount : 0 notCount : 1 where : @ "cBoolCol IN {NO}" ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 1 notCount : 0 where : @ "cBoolCol IN {YES}" ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 1 notCount : 0 where : @ "cBoolCol IN {NO, YES}" ] ;
// int64_t
[ self testClass : [ AllTypesObject class ] withNormalCount : 0 notCount : 1 where : @ "longCol IN {0, 2, 3}" ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 1 notCount : 0 where : @ "longCol IN {1}" ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 1 notCount : 0 where : @ "longCol IN {1, 2}" ] ;
// mixed
// FIXME: Support IN predicates with mixed properties
XCTAssertThrows ( [ AllTypesObject objectsWhere : @ "mixedCol IN {0, 2, 3}" ] ) ;
XCTAssertThrows ( [ AllTypesObject objectsWhere : @ "NOT(mixedCol IN {0, 2, 3})" ] ) ;
// string subobject
[ self testClass : [ AllTypesObject class ] withNormalCount : 1 notCount : 0 where : @ "objectCol.stringCol IN {'abc'}" ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 0 notCount : 1 where : @ "objectCol.stringCol IN {'def'}" ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 0 notCount : 1 where : @ "objectCol.stringCol IN {'ABC'}" ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 1 notCount : 0 where : @ "objectCol.stringCol IN[c] {'abc'}" ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 1 notCount : 0 where : @ "objectCol.stringCol IN[c] {'ABC'}" ] ;
////////////////////////
// Format Predicates
////////////////////////
// BOOL
[ self testClass : [ AllTypesObject class ] withNormalCount : 0 U notCount : 1 U where : @ "boolCol IN %@" , @ [ @ NO ] ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 1 U notCount : 0 U where : @ "boolCol IN %@" , @ [ @ YES ] ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 1 U notCount : 0 U where : @ "boolCol IN %@" , @ [ @ NO , @ YES ] ] ;
// int
[ self testClass : [ AllTypesObject class ] withNormalCount : 0 U notCount : 1 U where : @ "intCol IN %@" , @ [ @ 0 , @ 2 , @ 3 ] ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 1 U notCount : 0 U where : @ "intCol IN %@" , @ [ @ 1 ] ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 1 U notCount : 0 U where : @ "intCol IN %@" , @ [ @ 1 , @ 2 ] ] ;
// float
[ self testClass : [ AllTypesObject class ] withNormalCount : 0 U notCount : 1 U where : @ "floatCol IN %@" , @ [ @ 0 , @ 2 , @ 3 ] ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 1 U notCount : 0 U where : @ "floatCol IN %@" , @ [ @ 1 ] ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 1 U notCount : 0 U where : @ "floatCol IN %@" , @ [ @ 1 , @ 2 ] ] ;
// double
[ self testClass : [ AllTypesObject class ] withNormalCount : 0 U notCount : 1 U where : @ "doubleCol IN %@" , @ [ @ 0 , @ 2 , @ 3 ] ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 1 U notCount : 0 U where : @ "doubleCol IN %@" , @ [ @ 1 ] ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 1 U notCount : 0 U where : @ "doubleCol IN %@" , @ [ @ 1 , @ 2 ] ] ;
// NSString
[ self testClass : [ AllTypesObject class ] withNormalCount : 1 U notCount : 0 U where : @ "stringCol IN %@" , @ [ @ "abc" ] ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 0 U notCount : 1 U where : @ "stringCol IN %@" , @ [ @ "def" ] ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 0 U notCount : 1 U where : @ "stringCol IN %@" , @ [ @ "ABC" ] ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 1 U notCount : 0 U where : @ "stringCol IN[c] %@" , @ [ @ "abc" ] ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 1 U notCount : 0 U where : @ "stringCol IN[c] %@" , @ [ @ "ABC" ] ] ;
// NSData
[ self testClass : [ AllTypesObject class ] withNormalCount : 0 U notCount : 1 U where : @ "binaryCol IN %@" , @ [ [ @ "" dataUsingEncoding : NSUTF8StringEncoding ] ] ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 1 U notCount : 0 U where : @ "binaryCol IN %@" , @ [ [ @ "a" dataUsingEncoding : NSUTF8StringEncoding ] ] ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 1 U notCount : 0 U where : @ "binaryCol IN %@" , @ [ [ @ "a" dataUsingEncoding : NSUTF8StringEncoding ] , [ @ "b" dataUsingEncoding : NSUTF8StringEncoding ] ] ] ;
// NSDate
[ self testClass : [ AllTypesObject class ] withNormalCount : 0 U notCount : 1 U where : @ "dateCol IN %@" , @ [ [ NSDate dateWithTimeIntervalSince1970 : 0 ] ] ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 1 U notCount : 0 U where : @ "dateCol IN %@" , @ [ [ NSDate dateWithTimeIntervalSince1970 : 1 ] ] ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 1 U notCount : 0 U where : @ "dateCol IN %@" , @ [ [ NSDate dateWithTimeIntervalSince1970 : 0 ] , [ NSDate dateWithTimeIntervalSince1970 : 1 ] ] ] ;
// bool
[ self testClass : [ AllTypesObject class ] withNormalCount : 0 U notCount : 1 U where : @ "cBoolCol IN %@" , @ [ @ NO ] ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 1 U notCount : 0 U where : @ "cBoolCol IN %@" , @ [ @ YES ] ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 1 U notCount : 0 U where : @ "cBoolCol IN %@" , @ [ @ NO , @ YES ] ] ;
// int64_t
[ self testClass : [ AllTypesObject class ] withNormalCount : 0 U notCount : 1 U where : @ "longCol IN %@" , @ [ @ 0 , @ 2 , @ 3 ] ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 1 U notCount : 0 U where : @ "longCol IN %@" , @ [ @ 1 ] ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 1 U notCount : 0 U where : @ "longCol IN %@" , @ [ @ 1 , @ 2 ] ] ;
// mixed
// FIXME: Support IN predicates with mixed properties
XCTAssertThrows ( ( [ [ AllTypesObject objectsWhere : @ "mixedCol IN %@" , @ [ @ 0 , @ 2 , @ 3 ] ] count ] ) ) ;
XCTAssertThrows ( ( [ [ AllTypesObject objectsWhere : @ "NOT(mixedCol IN %@)" , @ [ @ 0 , @ 2 , @ 3 ] ] count ] ) ) ;
// string subobject
[ self testClass : [ AllTypesObject class ] withNormalCount : 1 U notCount : 0 U where : @ "objectCol.stringCol IN %@" , @ [ @ "abc" ] ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 0 U notCount : 1 U where : @ "objectCol.stringCol IN %@" , @ [ @ "def" ] ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 0 U notCount : 1 U where : @ "objectCol.stringCol IN %@" , @ [ @ "ABC" ] ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 1 U notCount : 0 U where : @ "objectCol.stringCol IN[c] %@" , @ [ @ "abc" ] ] ;
[ self testClass : [ AllTypesObject class ] withNormalCount : 1 U notCount : 0 U where : @ "objectCol.stringCol IN[c] %@" , @ [ @ "ABC" ] ] ;
}
- ( void ) testArrayIn
{
RLMRealm * realm = [ RLMRealm defaultRealm ] ;
[ realm beginWriteTransaction ] ;
ArrayPropertyObject * arr = [ ArrayPropertyObject createInRealm : realm withValue : @ [ @ "name" , @ [ ] , @ [ ] ] ] ;
[ arr . array addObject : [ StringObject createInRealm : realm withValue : @ [ @ "value" ] ] ] ;
[ realm commitWriteTransaction ] ;
XCTAssertEqual ( 0 U , ( [ [ ArrayPropertyObject objectsWhere : @ "ANY array.stringCol IN %@" , @ [ @ "missing" ] ] count ] ) ) ;
XCTAssertEqual ( 1 U , ( [ [ ArrayPropertyObject objectsWhere : @ "ANY array.stringCol IN %@" , @ [ @ "value" ] ] count ] ) ) ;
XCTAssertEqual ( 1 U , ( [ [ ArrayPropertyObject objectsWhere : @ "NONE array.stringCol IN %@" , @ [ @ "missing" ] ] count ] ) ) ;
XCTAssertEqual ( 0 U , ( [ [ ArrayPropertyObject objectsWhere : @ "NONE array.stringCol IN %@" , @ [ @ "value" ] ] count ] ) ) ;
XCTAssertEqual ( 0 U , ( [ [ ArrayPropertyObject objectsWhere : @ "ANY array IN %@" , [ StringObject objectsWhere : @ "stringCol = 'missing'" ] ] count ] ) ) ;
XCTAssertEqual ( 1 U , ( [ [ ArrayPropertyObject objectsWhere : @ "ANY array IN %@" , [ StringObject objectsWhere : @ "stringCol = 'value'" ] ] count ] ) ) ;
XCTAssertEqual ( 1 U , ( [ [ ArrayPropertyObject objectsWhere : @ "NONE array IN %@" , [ StringObject objectsWhere : @ "stringCol = 'missing'" ] ] count ] ) ) ;
XCTAssertEqual ( 0 U , ( [ [ ArrayPropertyObject objectsWhere : @ "NONE array IN %@" , [ StringObject objectsWhere : @ "stringCol = 'value'" ] ] count ] ) ) ;
}
- ( void ) testQueryChaining {
RLMRealm * realm = [ RLMRealm defaultRealm ] ;
[ realm beginWriteTransaction ] ;
[ PersonObject createInRealm : realm withValue : @ [ @ "Tim" , @ 29 ] ] ;
[ PersonObject createInRealm : realm withValue : @ [ @ "Ari" , @ 33 ] ] ;
[ realm commitWriteTransaction ] ;
XCTAssertEqual ( 1 U , [ [ PersonObject objectsWhere : @ "name == 'Ari'" ] count ] ) ;
XCTAssertEqual ( 0 U , [ [ PersonObject objectsWhere : @ "name == 'Ari' and age == 29" ] count ] ) ;
XCTAssertEqual ( 0 U , [ [ [ PersonObject objectsWhere : @ "name == 'Ari'" ] objectsWhere : @ "age == 29" ] count ] ) ;
}
- ( void ) testLinkViewQuery {
RLMRealm * realm = [ RLMRealm defaultRealm ] ;
[ realm beginWriteTransaction ] ;
[ CompanyObject createInRealm : realm
withValue : @ [ @ "company name" , @ [ @ { @ "name" : @ "John" , @ "age" : @ 30 , @ "hired" : @ NO } ,
@ { @ "name" : @ "Joe" , @ "age" : @ 40 , @ "hired" : @ YES } ,
@ { @ "name" : @ "Jill" , @ "age" : @ 50 , @ "hired" : @ YES } ] ] ] ;
[ realm commitWriteTransaction ] ;
CompanyObject * co = [ CompanyObject allObjects ] [ 0 ] ;
XCTAssertEqual ( 1 U , [ co . employees objectsWhere : @ "hired = NO" ] . count ) ;
XCTAssertEqual ( 2 U , [ co . employees objectsWhere : @ "hired = YES" ] . count ) ;
XCTAssertEqual ( 1 U , [ co . employees objectsWhere : @ "hired = YES AND age = 40" ] . count ) ;
XCTAssertEqual ( 0 U , [ co . employees objectsWhere : @ "hired = YES AND age = 30" ] . count ) ;
XCTAssertEqual ( 3 U , [ co . employees objectsWhere : @ "hired = YES OR age = 30" ] . count ) ;
XCTAssertEqual ( 1 U , [ [ co . employees objectsWhere : @ "hired = YES" ] objectsWhere : @ "name = 'Joe'" ] . count ) ;
}
- ( void ) testLinkViewQueryLifetime {
RLMRealm * realm = [ RLMRealm defaultRealm ] ;
[ realm beginWriteTransaction ] ;
[ CompanyObject createInRealm : realm
withValue : @ [ @ "company name" , @ [ @ { @ "name" : @ "John" , @ "age" : @ 30 , @ "hired" : @ NO } ,
@ { @ "name" : @ "Jill" , @ "age" : @ 50 , @ "hired" : @ YES } ] ] ] ;
[ EmployeeObject createInRealm : realm withValue : @ { @ "name" : @ "Joe" , @ "age" : @ 40 , @ "hired" : @ YES } ] ;
[ realm commitWriteTransaction ] ;
RLMResults * subarray = nil ;
@ autoreleasepool {
_ _attribute ( ( objc _precise _lifetime ) ) CompanyObject * co = [ CompanyObject allObjects ] [ 0 ] ;
subarray = [ co . employees objectsWhere : @ "age = 40" ] ;
XCTAssertEqual ( 0 U , subarray . count ) ;
}
[ realm beginWriteTransaction ] ;
@ autoreleasepool {
_ _attribute ( ( objc _precise _lifetime ) ) CompanyObject * co = [ CompanyObject allObjects ] [ 0 ] ;
[ co . employees addObject : [ EmployeeObject createInRealm : realm withValue : @ { @ "name" : @ "Joe" , @ "age" : @ 40 , @ "hired" : @ YES } ] ] ;
}
[ realm commitWriteTransaction ] ;
XCTAssertEqual ( 1 U , subarray . count ) ;
XCTAssertEqualObjects ( @ "Joe" , subarray [ 0 ] [ @ "name" ] ) ;
}
- ( void ) testLinkViewQueryLiveUpdate {
RLMRealm * realm = [ RLMRealm defaultRealm ] ;
[ realm beginWriteTransaction ] ;
[ CompanyObject createInRealm : realm
withValue : @ [ @ "company name" , @ [ @ { @ "name" : @ "John" , @ "age" : @ 30 , @ "hired" : @ NO } ,
@ { @ "name" : @ "Jill" , @ "age" : @ 40 , @ "hired" : @ YES } ] ] ] ;
EmployeeObject * eo = [ EmployeeObject createInRealm : realm withValue : @ { @ "name" : @ "Joe" , @ "age" : @ 40 , @ "hired" : @ YES } ] ;
[ realm commitWriteTransaction ] ;
CompanyObject * co = CompanyObject . allObjects . firstObject ;
RLMResults * basic = [ co . employees objectsWhere : @ "age = 40" ] ;
RLMResults * sort = [ co . employees sortedResultsUsingProperty : @ "name" ascending : YES ] ;
RLMResults * sortQuery = [ [ co . employees sortedResultsUsingProperty : @ "name" ascending : YES ] objectsWhere : @ "age = 40" ] ;
RLMResults * querySort = [ [ co . employees objectsWhere : @ "age = 40" ] sortedResultsUsingProperty : @ "name" ascending : YES ] ;
XCTAssertEqual ( 1 U , basic . count ) ;
XCTAssertEqual ( 2 U , sort . count ) ;
XCTAssertEqual ( 1 U , sortQuery . count ) ;
XCTAssertEqual ( 1 U , querySort . count ) ;
XCTAssertEqualObjects ( @ "Jill" , [ [ basic lastObject ] name ] ) ;
XCTAssertEqualObjects ( @ "Jill" , [ [ sortQuery lastObject ] name ] ) ;
XCTAssertEqualObjects ( @ "Jill" , [ [ querySort lastObject ] name ] ) ;
[ realm beginWriteTransaction ] ;
[ co . employees addObject : eo ] ;
[ realm commitWriteTransaction ] ;
XCTAssertEqual ( 2 U , basic . count ) ;
XCTAssertEqual ( 3 U , sort . count ) ;
XCTAssertEqual ( 2 U , sortQuery . count ) ;
XCTAssertEqual ( 2 U , querySort . count ) ;
XCTAssertEqualObjects ( @ "Joe" , [ [ basic lastObject ] name ] ) ;
XCTAssertEqualObjects ( @ "Joe" , [ [ sortQuery lastObject ] name ] ) ;
XCTAssertEqualObjects ( @ "Joe" , [ [ querySort lastObject ] name ] ) ;
}
- ( void ) testConstantPredicates
{
RLMRealm * realm = [ RLMRealm defaultRealm ] ;
[ realm beginWriteTransaction ] ;
[ PersonObject createInRealm : realm withValue : @ [ @ "Fiel" , @ 27 ] ] ;
[ PersonObject createInRealm : realm withValue : @ [ @ "Tim" , @ 29 ] ] ;
[ PersonObject createInRealm : realm withValue : @ [ @ "Ari" , @ 33 ] ] ;
[ realm commitWriteTransaction ] ;
RLMResults * all = [ PersonObject objectsWithPredicate : [ NSPredicate predicateWithValue : YES ] ] ;
XCTAssertEqual ( all . count , 3 U , @ "Expecting 3 results" ) ;
RLMResults * none = [ PersonObject objectsWithPredicate : [ NSPredicate predicateWithValue : NO ] ] ;
XCTAssertEqual ( none . count , 0 U , @ "Expecting 0 results" ) ;
}
- ( void ) testEmptyCompoundPredicates
{
RLMRealm * realm = [ RLMRealm defaultRealm ] ;
[ realm beginWriteTransaction ] ;
[ PersonObject createInRealm : realm withValue : @ [ @ "Fiel" , @ 27 ] ] ;
[ PersonObject createInRealm : realm withValue : @ [ @ "Tim" , @ 29 ] ] ;
[ PersonObject createInRealm : realm withValue : @ [ @ "Ari" , @ 33 ] ] ;
[ realm commitWriteTransaction ] ;
RLMResults * all = [ PersonObject objectsWithPredicate : [ NSCompoundPredicate andPredicateWithSubpredicates : @ [ ] ] ] ;
XCTAssertEqual ( all . count , 3 U , @ "Expecting 3 results" ) ;
RLMResults * none = [ PersonObject objectsWithPredicate : [ NSCompoundPredicate orPredicateWithSubpredicates : @ [ ] ] ] ;
XCTAssertEqual ( none . count , 0 U , @ "Expecting 0 results" ) ;
}
- ( void ) testComparisonsWithKeyPathOnRHS
{
RLMRealm * realm = [ RLMRealm defaultRealm ] ;
[ realm beginWriteTransaction ] ;
[ self . queryObjectClass createInRealm : realm withValue : @ [ @ YES , @ YES , @ 1 , @ 2 , @ 23.0 f , @ 1.7 f , @ 0.0 , @ 5.55 , @ "a" , @ "a" ] ] ;
[ self . queryObjectClass createInRealm : realm withValue : @ [ @ YES , @ NO , @ 1 , @ 3 , @ - 5.3 f , @ 4.21 f , @ 1.0 , @ 4.44 , @ "a" , @ "A" ] ] ;
[ self . queryObjectClass createInRealm : realm withValue : @ [ @ NO , @ NO , @ 2 , @ 2 , @ 1.0 f , @ 3.55 f , @ 99.9 , @ 6.66 , @ "a" , @ "ab" ] ] ;
[ self . queryObjectClass createInRealm : realm withValue : @ [ @ NO , @ YES , @ 3 , @ 6 , @ 4.21 f , @ 1.0 f , @ 1.0 , @ 7.77 , @ "a" , @ "AB" ] ] ;
[ self . queryObjectClass createInRealm : realm withValue : @ [ @ YES , @ YES , @ 4 , @ 5 , @ 23.0 f , @ 23.0 f , @ 7.4 , @ 8.88 , @ "a" , @ "b" ] ] ;
[ self . queryObjectClass createInRealm : realm withValue : @ [ @ YES , @ NO , @ 15 , @ 8 , @ 1.0 f , @ 66.0 f , @ 1.01 , @ 9.99 , @ "a" , @ "ba" ] ] ;
[ self . queryObjectClass createInRealm : realm withValue : @ [ @ NO , @ YES , @ 15 , @ 15 , @ 1.0 f , @ 66.0 f , @ 1.01 , @ 9.99 , @ "a" , @ "BA" ] ] ;
[ realm commitWriteTransaction ] ;
XCTAssertEqual ( 4 U , [ self . queryObjectClass objectsWhere : @ "TRUE == bool1" ] . count ) ;
XCTAssertEqual ( 3 U , [ self . queryObjectClass objectsWhere : @ "TRUE != bool2" ] . count ) ;
XCTAssertEqual ( 2 U , [ self . queryObjectClass objectsWhere : @ "1 == int1" ] . count ) ;
XCTAssertEqual ( 5 U , [ self . queryObjectClass objectsWhere : @ "2 != int2" ] . count ) ;
XCTAssertEqual ( 2 U , [ self . queryObjectClass objectsWhere : @ "2 > int1" ] . count ) ;
XCTAssertEqual ( 4 U , [ self . queryObjectClass objectsWhere : @ "2 < int1" ] . count ) ;
XCTAssertEqual ( 3 U , [ self . queryObjectClass objectsWhere : @ "2 >= int1" ] . count ) ;
XCTAssertEqual ( 5 U , [ self . queryObjectClass objectsWhere : @ "2 <= int1" ] . count ) ;
XCTAssertEqual ( 3 U , [ self . queryObjectClass objectsWhere : @ "1.0 == float1" ] . count ) ;
XCTAssertEqual ( 6 U , [ self . queryObjectClass objectsWhere : @ "1.0 != float2" ] . count ) ;
XCTAssertEqual ( 1 U , [ self . queryObjectClass objectsWhere : @ "1.0 > float1" ] . count ) ;
XCTAssertEqual ( 6 U , [ self . queryObjectClass objectsWhere : @ "1.0 < float2" ] . count ) ;
XCTAssertEqual ( 4 U , [ self . queryObjectClass objectsWhere : @ "1.0 >= float1" ] . count ) ;
XCTAssertEqual ( 7 U , [ self . queryObjectClass objectsWhere : @ "1.0 <= float2" ] . count ) ;
XCTAssertEqual ( 2 U , [ self . queryObjectClass objectsWhere : @ "1.0 == double1" ] . count ) ;
XCTAssertEqual ( 5 U , [ self . queryObjectClass objectsWhere : @ "1.0 != double1" ] . count ) ;
XCTAssertEqual ( 1 U , [ self . queryObjectClass objectsWhere : @ "5.0 > double2" ] . count ) ;
XCTAssertEqual ( 6 U , [ self . queryObjectClass objectsWhere : @ "5.0 < double2" ] . count ) ;
XCTAssertEqual ( 2 U , [ self . queryObjectClass objectsWhere : @ "5.55 >= double2" ] . count ) ;
XCTAssertEqual ( 6 U , [ self . queryObjectClass objectsWhere : @ "5.55 <= double2" ] . count ) ;
XCTAssertEqual ( 1 U , [ self . queryObjectClass objectsWhere : @ "'a' == string2" ] . count ) ;
XCTAssertEqual ( 6 U , [ self . queryObjectClass objectsWhere : @ "'a' != string2" ] . count ) ;
RLMAssertThrowsWithReasonMatching ( [ self . queryObjectClass objectsWhere : @ "'Realm' CONTAINS string1" ] . count ,
@ "Operator 'CONTAINS' is not supported .* right side" ) ;
RLMAssertThrowsWithReasonMatching ( [ self . queryObjectClass objectsWhere : @ "'Amazon' BEGINSWITH string2" ] . count ,
@ "Operator 'BEGINSWITH' is not supported .* right side" ) ;
RLMAssertThrowsWithReasonMatching ( [ self . queryObjectClass objectsWhere : @ "'Tuba' ENDSWITH string1" ] . count ,
@ "Operator 'ENDSWITH' is not supported .* right side" ) ;
}
@ end
@ interface NullQueryTests : QueryTests
@ end
@ implementation NullQueryTests
- ( Class ) queryObjectClass {
return [ NullQueryObject class ] ;
}
- ( void ) testQueryOnNullableStringColumn {
void ( ^ testWithStringClass ) ( Class ) = ^ ( Class stringObjectClass ) {
RLMRealm * realm = [ RLMRealm defaultRealm ] ;
[ realm transactionWithBlock : ^ {
[ stringObjectClass createInRealm : realm withValue : @ [ @ "a" ] ] ;
[ stringObjectClass createInRealm : realm withValue : @ [ NSNull . null ] ] ;
[ stringObjectClass createInRealm : realm withValue : @ [ @ "b" ] ] ;
[ stringObjectClass createInRealm : realm withValue : @ [ NSNull . null ] ] ;
[ stringObjectClass createInRealm : realm withValue : @ [ @ "" ] ] ;
} ] ;
RLMResults * allObjects = [ stringObjectClass allObjectsInRealm : realm ] ;
XCTAssertEqual ( 5 U , allObjects . count ) ;
RLMResults * nilStrings = [ stringObjectClass objectsInRealm : realm where : @ "stringCol = NULL" ] ;
XCTAssertEqual ( 2 U , nilStrings . count ) ;
XCTAssertEqualObjects ( ( @ [ NSNull . null , NSNull . null ] ) , [ nilStrings valueForKey : @ "stringCol" ] ) ;
RLMResults * nonNilStrings = [ stringObjectClass objectsInRealm : realm where : @ "stringCol != NULL" ] ;
XCTAssertEqual ( 3 U , nonNilStrings . count ) ;
XCTAssertEqualObjects ( ( @ [ @ "a" , @ "b" , @ "" ] ) , [ nonNilStrings valueForKey : @ "stringCol" ] ) ;
XCTAssertEqual ( 3 U , [ stringObjectClass objectsInRealm : realm where : @ "stringCol IN {NULL, 'a'}" ] . count ) ;
XCTAssertEqual ( 1 U , [ stringObjectClass objectsInRealm : realm where : @ "stringCol CONTAINS 'a'" ] . count ) ;
XCTAssertEqual ( 1 U , [ stringObjectClass objectsInRealm : realm where : @ "stringCol BEGINSWITH 'a'" ] . count ) ;
XCTAssertEqual ( 1 U , [ stringObjectClass objectsInRealm : realm where : @ "stringCol ENDSWITH 'a'" ] . count ) ;
XCTAssertEqual ( 0 U , [ stringObjectClass objectsInRealm : realm where : @ "stringCol CONTAINS 'z'" ] . count ) ;
XCTAssertEqual ( 1 U , [ stringObjectClass objectsInRealm : realm where : @ "stringCol = ''" ] . count ) ;
RLMResults * sorted = [ [ stringObjectClass allObjectsInRealm : realm ] sortedResultsUsingProperty : @ "stringCol" ascending : YES ] ;
XCTAssertEqualObjects ( ( @ [ NSNull . null , NSNull . null , @ "" , @ "a" , @ "b" ] ) , [ sorted valueForKey : @ "stringCol" ] ) ;
XCTAssertEqualObjects ( ( @ [ @ "b" , @ "a" , @ "" , NSNull . null , NSNull . null ] ) , [ [ sorted sortedResultsUsingProperty : @ "stringCol" ascending : NO ] valueForKey : @ "stringCol" ] ) ;
[ realm transactionWithBlock : ^ {
[ realm deleteObject : [ stringObjectClass allObjectsInRealm : realm ] . firstObject ] ;
} ] ;
XCTAssertEqual ( 2 U , nilStrings . count ) ;
XCTAssertEqual ( 2 U , nonNilStrings . count ) ;
XCTAssertEqualObjects ( [ nonNilStrings valueForKey : @ "stringCol" ] , [ [ stringObjectClass objectsInRealm : realm where : @ "stringCol CONTAINS ''" ] valueForKey : @ "stringCol" ] ) ;
XCTAssertEqualObjects ( [ nonNilStrings valueForKey : @ "stringCol" ] , [ [ stringObjectClass objectsInRealm : realm where : @ "stringCol BEGINSWITH ''" ] valueForKey : @ "stringCol" ] ) ;
XCTAssertEqualObjects ( [ nonNilStrings valueForKey : @ "stringCol" ] , [ [ stringObjectClass objectsInRealm : realm where : @ "stringCol ENDSWITH ''" ] valueForKey : @ "stringCol" ] ) ;
XCTAssertEqualObjects ( [ nonNilStrings valueForKey : @ "stringCol" ] , [ [ stringObjectClass objectsInRealm : realm where : @ "stringCol CONTAINS[c] ''" ] valueForKey : @ "stringCol" ] ) ;
XCTAssertEqualObjects ( [ nonNilStrings valueForKey : @ "stringCol" ] , [ [ stringObjectClass objectsInRealm : realm where : @ "stringCol BEGINSWITH[c] ''" ] valueForKey : @ "stringCol" ] ) ;
XCTAssertEqualObjects ( [ nonNilStrings valueForKey : @ "stringCol" ] , [ [ stringObjectClass objectsInRealm : realm where : @ "stringCol ENDSWITH[c] ''" ] valueForKey : @ "stringCol" ] ) ;
XCTAssertEqualObjects ( @ [ ] , ( [ [ stringObjectClass objectsInRealm : realm where : @ "stringCol CONTAINS %@" , @ "\0" ] valueForKey : @ "self" ] ) ) ;
XCTAssertEqualObjects ( [ [ stringObjectClass allObjectsInRealm : realm ] valueForKey : @ "stringCol" ] , ( [ [ StringObject objectsInRealm : realm where : @ "stringCol CONTAINS NULL" ] valueForKey : @ "stringCol" ] ) ) ;
} ;
testWithStringClass ( [ StringObject class ] ) ;
testWithStringClass ( [ IndexedStringObject class ] ) ;
}
- ( void ) testQueryingOnLinkToNullableStringColumn {
void ( ^ testWithStringClass ) ( Class , Class ) = ^ ( Class stringLinkClass , Class stringObjectClass ) {
RLMRealm * realm = [ RLMRealm defaultRealm ] ;
[ realm transactionWithBlock : ^ {
[ stringLinkClass createInRealm : realm withValue : @ [ [ stringObjectClass createInRealm : realm withValue : @ [ @ "a" ] ] ] ] ;
[ stringLinkClass createInRealm : realm withValue : @ [ [ stringObjectClass createInRealm : realm withValue : @ [ NSNull . null ] ] ] ] ;
[ stringLinkClass createInRealm : realm withValue : @ [ [ stringObjectClass createInRealm : realm withValue : @ [ @ "b" ] ] ] ] ;
[ stringLinkClass createInRealm : realm withValue : @ [ [ stringObjectClass createInRealm : realm withValue : @ [ NSNull . null ] ] ] ] ;
[ stringLinkClass createInRealm : realm withValue : @ [ [ stringObjectClass createInRealm : realm withValue : @ [ @ "" ] ] ] ] ;
} ] ;
RLMResults * nilStrings = [ stringLinkClass objectsInRealm : realm where : @ "objectCol.stringCol = NULL" ] ;
XCTAssertEqual ( 2 U , nilStrings . count ) ;
XCTAssertEqualObjects ( ( @ [ NSNull . null , NSNull . null ] ) , [ nilStrings valueForKeyPath : @ "objectCol.stringCol" ] ) ;
RLMResults * nonNilStrings = [ stringLinkClass objectsInRealm : realm where : @ "objectCol.stringCol != NULL" ] ;
XCTAssertEqual ( 3 U , nonNilStrings . count ) ;
XCTAssertEqualObjects ( ( @ [ @ "a" , @ "b" , @ "" ] ) , [ nonNilStrings valueForKeyPath : @ "objectCol.stringCol" ] ) ;
XCTAssertEqual ( 3 U , [ stringLinkClass objectsInRealm : realm where : @ "objectCol.stringCol IN {NULL, 'a'}" ] . count ) ;
XCTAssertEqual ( 1 U , [ stringLinkClass objectsInRealm : realm where : @ "objectCol.stringCol CONTAINS 'a'" ] . count ) ;
XCTAssertEqual ( 1 U , [ stringLinkClass objectsInRealm : realm where : @ "objectCol.stringCol BEGINSWITH 'a'" ] . count ) ;
XCTAssertEqual ( 1 U , [ stringLinkClass objectsInRealm : realm where : @ "objectCol.stringCol ENDSWITH 'a'" ] . count ) ;
XCTAssertEqual ( 0 U , [ stringLinkClass objectsInRealm : realm where : @ "objectCol.stringCol CONTAINS 'z'" ] . count ) ;
XCTAssertEqual ( 1 U , [ stringLinkClass objectsInRealm : realm where : @ "objectCol.stringCol = ''" ] . count ) ;
} ;
testWithStringClass ( [ LinkStringObject class ] , [ StringObject class ] ) ;
testWithStringClass ( [ LinkIndexedStringObject class ] , [ IndexedStringObject class ] ) ;
}
- ( void ) testSortingColumnsWithNull {
RLMRealm * realm = [ RLMRealm defaultRealm ] ;
[ realm beginWriteTransaction ] ;
{
NumberObject * no1 = [ NumberObject createInRealm : realm withValue : @ [ @ 1 , @ 1.1 f , @ 1.1 , @ YES ] ] ;
NumberObject * noNull = [ NumberObject createInRealm : realm withValue : @ [ NSNull . null , NSNull . null , NSNull . null , NSNull . null ] ] ;
NumberObject * no0 = [ NumberObject createInRealm : realm withValue : @ [ @ 0 , @ 0.0 f , @ 0.0 , @ NO ] ] ;
for ( RLMProperty * property in [ [ NumberObject alloc ] init ] . objectSchema . properties ) {
NSString * name = property . name ;
RLMResults * ascending = [ [ NumberObject allObjectsInRealm : realm ] sortedResultsUsingProperty : name ascending : YES ] ;
XCTAssertEqualObjects ( [ ascending valueForKey : name ] , ( [ @ [ noNull , no0 , no1 ] valueForKey : name ] ) ) ;
RLMResults * descending = [ [ NumberObject allObjectsInRealm : realm ] sortedResultsUsingProperty : name ascending : NO ] ;
XCTAssertEqualObjects ( [ descending valueForKey : name ] , ( [ @ [ no1 , no0 , noNull ] valueForKey : name ] ) ) ;
}
}
{
DateObject * doPositive = [ DateObject createInRealm : realm withValue : @ [ [ NSDate dateWithTimeIntervalSince1970 : 100 ] ] ] ;
DateObject * doNegative = [ DateObject createInRealm : realm withValue : @ [ [ NSDate dateWithTimeIntervalSince1970 : - 100 ] ] ] ;
DateObject * doZero = [ DateObject createInRealm : realm withValue : @ [ [ NSDate dateWithTimeIntervalSince1970 : 0 ] ] ] ;
DateObject * doNull = [ DateObject createInRealm : realm withValue : @ [ NSNull . null ] ] ;
RLMResults * ascending = [ [ DateObject allObjectsInRealm : realm ] sortedResultsUsingProperty : @ "dateCol" ascending : YES ] ;
XCTAssertEqualObjects ( [ ascending valueForKey : @ "dateCol" ] , ( [ @ [ doNull , doNegative , doZero , doPositive ] valueForKey : @ "dateCol" ] ) ) ;
RLMResults * descending = [ [ DateObject allObjectsInRealm : realm ] sortedResultsUsingProperty : @ "dateCol" ascending : NO ] ;
XCTAssertEqualObjects ( [ descending valueForKey : @ "dateCol" ] , ( [ @ [ doPositive , doZero , doNegative , doNull ] valueForKey : @ "dateCol" ] ) ) ;
}
{
StringObject * soA = [ StringObject createInRealm : realm withValue : @ [ @ "A" ] ] ;
StringObject * soEmpty = [ StringObject createInRealm : realm withValue : @ [ @ "" ] ] ;
StringObject * soB = [ StringObject createInRealm : realm withValue : @ [ @ "B" ] ] ;
StringObject * soNull = [ StringObject createInRealm : realm withValue : @ [ NSNull . null ] ] ;
StringObject * soAB = [ StringObject createInRealm : realm withValue : @ [ @ "AB" ] ] ;
RLMResults * ascending = [ [ StringObject allObjectsInRealm : realm ] sortedResultsUsingProperty : @ "stringCol" ascending : YES ] ;
XCTAssertEqualObjects ( [ ascending valueForKey : @ "stringCol" ] , ( [ @ [ soNull , soEmpty , soA , soAB , soB ] valueForKey : @ "stringCol" ] ) ) ;
RLMResults * descending = [ [ StringObject allObjectsInRealm : realm ] sortedResultsUsingProperty : @ "stringCol" ascending : NO ] ;
XCTAssertEqualObjects ( [ descending valueForKey : @ "stringCol" ] , ( [ @ [ soB , soAB , soA , soEmpty , soNull ] valueForKey : @ "stringCol" ] ) ) ;
}
[ realm cancelWriteTransaction ] ;
}
- ( void ) testCountOnCollection {
RLMRealm * realm = [ RLMRealm defaultRealm ] ;
[ realm beginWriteTransaction ] ;
IntegerArrayPropertyObject * arr = [ IntegerArrayPropertyObject createInRealm : realm withValue : @ [ @ 1234 , @ [ ] ] ] ;
[ arr . array addObject : [ IntObject createInRealm : realm withValue : @ [ @ 456 ] ] ] ;
arr = [ IntegerArrayPropertyObject createInRealm : realm withValue : @ [ @ 4567 , @ [ ] ] ] ;
[ arr . array addObject : [ IntObject createInRealm : realm withValue : @ [ @ 1 ] ] ] ;
[ arr . array addObject : [ IntObject createInRealm : realm withValue : @ [ @ 2 ] ] ] ;
[ arr . array addObject : [ IntObject createInRealm : realm withValue : @ [ @ 3 ] ] ] ;
arr = [ IntegerArrayPropertyObject createInRealm : realm withValue : @ [ @ 4567 , @ [ ] ] ] ;
[ realm commitWriteTransaction ] ;
XCTAssertEqual ( 2 U , ( [ IntegerArrayPropertyObject objectsWhere : @ "array.@count > 0" ] . count ) ) ;
XCTAssertEqual ( 1 U , ( [ IntegerArrayPropertyObject objectsWhere : @ "array.@count == 3" ] . count ) ) ;
XCTAssertEqual ( 1 U , ( [ IntegerArrayPropertyObject objectsWhere : @ "array.@count < 1" ] . count ) ) ;
XCTAssertEqual ( 2 U , ( [ IntegerArrayPropertyObject objectsWhere : @ "0 < array.@count" ] . count ) ) ;
XCTAssertEqual ( 1 U , ( [ IntegerArrayPropertyObject objectsWhere : @ "3 == array.@count" ] . count ) ) ;
XCTAssertEqual ( 1 U , ( [ IntegerArrayPropertyObject objectsWhere : @ "1 > array.@count" ] . count ) ) ;
// We do not yet handle collection operations with a keypath on the other side of the comparison.
RLMAssertThrowsWithReasonMatching ( ( [ IntegerArrayPropertyObject objectsWhere : @ "array.@count != number" ] ) , @ "'array.@count' not found in object" ) ;
RLMAssertThrowsWithReasonMatching ( ( [ IntegerArrayPropertyObject objectsWhere : @ "array.@count.foo.bar != 0" ] ) , @ "single level key" ) ;
RLMAssertThrowsWithReasonMatching ( ( [ IntegerArrayPropertyObject objectsWhere : @ "array.@count.intCol > 0" ] ) , @ "@count does not have any properties" ) ;
RLMAssertThrowsWithReasonMatching ( ( [ IntegerArrayPropertyObject objectsWhere : @ "array.@count != 'Hello'" ] ) , @ "@count can only be compared with a numeric value" ) ;
}
- ( void ) testAggregateCollectionOperators {
RLMRealm * realm = [ RLMRealm defaultRealm ] ;
[ realm beginWriteTransaction ] ;
IntegerArrayPropertyObject * arr = [ IntegerArrayPropertyObject createInRealm : realm withValue : @ [ @ 1111 , @ [ ] ] ] ;
[ arr . array addObject : [ IntObject createInRealm : realm withValue : @ [ @ 1234 ] ] ] ;
[ arr . array addObject : [ IntObject createInRealm : realm withValue : @ [ @ 2 ] ] ] ;
[ arr . array addObject : [ IntObject createInRealm : realm withValue : @ [ @ - 12345 ] ] ] ;
arr = [ IntegerArrayPropertyObject createInRealm : realm withValue : @ [ @ 2222 , @ [ ] ] ] ;
[ arr . array addObject : [ IntObject createInRealm : realm withValue : @ [ @ 100 ] ] ] ;
arr = [ IntegerArrayPropertyObject createInRealm : realm withValue : @ [ @ 3333 , @ [ ] ] ] ;
[ realm commitWriteTransaction ] ;
XCTAssertEqual ( 1 U , ( [ IntegerArrayPropertyObject objectsWhere : @ "array.@min.intCol == -12345" ] . count ) ) ;
XCTAssertEqual ( 1 U , ( [ IntegerArrayPropertyObject objectsWhere : @ "array.@min.intCol == 100" ] . count ) ) ;
XCTAssertEqual ( 2 U , ( [ IntegerArrayPropertyObject objectsWhere : @ "array.@min.intCol < 1000" ] . count ) ) ;
XCTAssertEqual ( 1 U , ( [ IntegerArrayPropertyObject objectsWhere : @ "array.@min.intCol > -1000" ] . count ) ) ;
XCTAssertEqual ( 1 U , ( [ IntegerArrayPropertyObject objectsWhere : @ "array.@max.intCol == 1234" ] . count ) ) ;
XCTAssertEqual ( 1 U , ( [ IntegerArrayPropertyObject objectsWhere : @ "array.@max.intCol == 100" ] . count ) ) ;
XCTAssertEqual ( 2 U , ( [ IntegerArrayPropertyObject objectsWhere : @ "array.@max.intCol > -1000" ] . count ) ) ;
XCTAssertEqual ( 1 U , ( [ IntegerArrayPropertyObject objectsWhere : @ "array.@max.intCol > 1000" ] . count ) ) ;
XCTAssertEqual ( 1 U , ( [ IntegerArrayPropertyObject objectsWhere : @ "array.@sum.intCol == 100" ] . count ) ) ;
XCTAssertEqual ( 1 U , ( [ IntegerArrayPropertyObject objectsWhere : @ "array.@sum.intCol == -11109" ] . count ) ) ;
XCTAssertEqual ( 1 U , ( [ IntegerArrayPropertyObject objectsWhere : @ "array.@sum.intCol == 0" ] . count ) ) ;
XCTAssertEqual ( 2 U , ( [ IntegerArrayPropertyObject objectsWhere : @ "array.@sum.intCol > -50" ] . count ) ) ;
XCTAssertEqual ( 2 U , ( [ IntegerArrayPropertyObject objectsWhere : @ "array.@sum.intCol < 50" ] . count ) ) ;
XCTAssertEqual ( 1 U , ( [ IntegerArrayPropertyObject objectsWhere : @ "array.@avg.intCol == 100" ] . count ) ) ;
XCTAssertEqual ( 1 U , ( [ IntegerArrayPropertyObject objectsWhere : @ "array.@avg.intCol == -3703.0" ] . count ) ) ;
XCTAssertEqual ( 0 U , ( [ IntegerArrayPropertyObject objectsWhere : @ "array.@avg.intCol == 0" ] . count ) ) ;
XCTAssertEqual ( 1 U , ( [ IntegerArrayPropertyObject objectsWhere : @ "array.@avg.intCol < -50" ] . count ) ) ;
XCTAssertEqual ( 1 U , ( [ IntegerArrayPropertyObject objectsWhere : @ "array.@avg.intCol > 50" ] . count ) ) ;
// We do not yet handle collection operations with a keypath on the other side of the comparison.
RLMAssertThrowsWithReasonMatching ( ( [ IntegerArrayPropertyObject objectsWhere : @ "array.@min.intCol == number" ] ) , @ "'array.@min.intCol' not found in object" ) ;
RLMAssertThrowsWithReasonMatching ( ( [ IntegerArrayPropertyObject objectsWhere : @ "array.@min.intCol.foo.bar == 1.23" ] ) , @ "single level key" ) ;
RLMAssertThrowsWithReasonMatching ( ( [ IntegerArrayPropertyObject objectsWhere : @ "array.@max.intCol.foo.bar == 1.23" ] ) , @ "single level key" ) ;
RLMAssertThrowsWithReasonMatching ( ( [ IntegerArrayPropertyObject objectsWhere : @ "array.@sum.intCol.foo.bar == 1.23" ] ) , @ "single level key" ) ;
RLMAssertThrowsWithReasonMatching ( ( [ IntegerArrayPropertyObject objectsWhere : @ "array.@avg.intCol.foo.bar == 1.23" ] ) , @ "single level key" ) ;
// Average is omitted from this test as its result is always a double.
RLMAssertThrowsWithReasonMatching ( ( [ IntegerArrayPropertyObject objectsWhere : @ "array.@min.intCol == 1.23" ] ) , @ "@min.*type int cannot be compared" ) ;
RLMAssertThrowsWithReasonMatching ( ( [ IntegerArrayPropertyObject objectsWhere : @ "array.@max.intCol == 1.23" ] ) , @ "@max.*type int cannot be compared" ) ;
RLMAssertThrowsWithReasonMatching ( ( [ IntegerArrayPropertyObject objectsWhere : @ "array.@sum.intCol == 1.23" ] ) , @ "@sum.*type int cannot be compared" ) ;
}
struct NullTestData {
_ _unsafe _unretained NSString * propertyName ;
_ _unsafe _unretained NSString * nonMatchingStr ;
_ _unsafe _unretained NSString * matchingStr ;
_ _unsafe _unretained id nonMatchingValue ;
_ _unsafe _unretained id matchingValue ;
bool orderable ;
bool substringOperations ;
} ;
- ( void ) testPrimitiveOperatorsOnAllNullablePropertyTypes {
RLMRealm * realm = [ RLMRealm defaultRealm ] ;
// nil on LHS is currently not supported by core
XCTAssertThrows ( [ AllOptionalTypes objectsWhere : @ "nil = boolObj" ] ) ;
// These need to be stored in variables because the struct does not retain them
NSData * matchingData = [ @ "" dataUsingEncoding : NSUTF8StringEncoding ] ;
NSData * notMatchingData = [ @ "a" dataUsingEncoding : NSUTF8StringEncoding ] ;
NSDate * matchingDate = [ NSDate dateWithTimeIntervalSince1970 : 1 ] ;
NSDate * notMatchingDate = [ NSDate dateWithTimeIntervalSince1970 : 2 ] ;
struct NullTestData data [ ] = {
{ @ "boolObj" , @ "YES" , @ "NO" , @ YES , @ NO } ,
{ @ "intObj" , @ "1" , @ "0" , @ 1 , @ 0 , true } ,
{ @ "floatObj" , @ "1" , @ "0" , @ 1 , @ 0 , true } ,
{ @ "doubleObj" , @ "1" , @ "0" , @ 1 , @ 0 , true } ,
{ @ "string" , @ "'a'" , @ "''" , @ "a" , @ "" , false , true } ,
{ @ "data" , nil , nil , notMatchingData , matchingData } ,
{ @ "date" , nil , nil , notMatchingDate , matchingDate , true } ,
} ;
// Assert that the query "prop op value" gives expectedCount results when
// assembled via string formatting
# define RLMAssertCountWithString ( expectedCount , op , prop , value ) \
do { \
NSString * queryStr = [ NSString stringWithFormat : @ "%@ " # op " %@" , prop , value ] ; \
NSUInteger actual = [ AllOptionalTypes objectsWhere : queryStr ] . count ; \
XCTAssertEqual ( expectedCount , actual , @ "%@: expected %@, got %@" , queryStr , @ ( expectedCount ) , @ ( actual ) ) ; \
} while ( 0 )
// Assert that the query "prop op value" gives expectedCount results when
// assembled via predicateWithFormat
# define RLMAssertCountWithPredicate ( expectedCount , op , prop , value ) \
do { \
NSPredicate * query = [ NSPredicate predicateWithFormat : @ "%K " # op " %@" , prop , value ] ; \
NSUInteger actual = [ AllOptionalTypes objectsWithPredicate : query ] . count ; \
XCTAssertEqual ( expectedCount , actual , @ "%@ " # op " %@: expected %@, got %@" , prop , value , @ ( expectedCount ) , @ ( actual ) ) ; \
} while ( 0 )
// Assert that the given operator gives the expected count for each of the
// stored value, a different value, and nil
# define RLMAssertOperator ( op , matchingCount , notMatchingCount , nilCount ) \
do { \
if ( d . matchingStr ) { \
RLMAssertCountWithString ( matchingCount , op , d . propertyName , d . matchingStr ) ; \
RLMAssertCountWithString ( notMatchingCount , op , d . propertyName , d . nonMatchingStr ) ; \
} \
RLMAssertCountWithString ( nilCount , op , d . propertyName , nil ) ; \
\
RLMAssertCountWithPredicate ( matchingCount , op , d . propertyName , d . matchingValue ) ; \
RLMAssertCountWithPredicate ( notMatchingCount , op , d . propertyName , d . nonMatchingValue ) ; \
RLMAssertCountWithPredicate ( nilCount , op , d . propertyName , nil ) ; \
} while ( 0 )
// First test with the `matchingValue` stored in each property
[ realm beginWriteTransaction ] ;
[ AllOptionalTypes createInRealm : realm withValue : @ [ @ NO , @ 0 , @ 0 , @ 0 , @ "" , matchingData , matchingDate ] ] ;
[ realm commitWriteTransaction ] ;
for ( size _t i = 0 ; i < sizeof ( data ) / sizeof ( data [ 0 ] ) ; ++ i ) {
struct NullTestData d = data [ i ] ;
RLMAssertOperator ( = , 1 U , 0 U , 0 U ) ;
RLMAssertOperator ( != , 0 U , 1 U , 1 U ) ;
if ( d . orderable ) {
RLMAssertOperator ( < , 0 U , 1 U , 0 U ) ;
RLMAssertOperator ( <= , 1 U , 1 U , 0 U ) ;
RLMAssertOperator ( > , 0 U , 0 U , 0 U ) ;
RLMAssertOperator ( >= , 1 U , 0 U , 0 U ) ;
}
if ( d . substringOperations ) {
RLMAssertOperator ( BEGINSWITH , 1 U , 0 U , 1 U ) ;
RLMAssertOperator ( ENDSWITH , 1 U , 0 U , 1 U ) ;
RLMAssertOperator ( CONTAINS , 1 U , 0 U , 1 U ) ;
}
}
// Retest with all properties nil
[ realm beginWriteTransaction ] ;
[ realm deleteAllObjects ] ;
[ AllOptionalTypes createInRealm : realm withValue : @ [ NSNull . null , NSNull . null ,
NSNull . null , NSNull . null ,
NSNull . null , NSNull . null ,
NSNull . null ] ] ;
[ realm commitWriteTransaction ] ;
for ( size _t i = 0 ; i < sizeof ( data ) / sizeof ( data [ 0 ] ) ; ++ i ) {
struct NullTestData d = data [ i ] ;
RLMAssertOperator ( = , 0 U , 0 U , 1 U ) ;
RLMAssertOperator ( != , 1 U , 1 U , 0 U ) ;
if ( d . orderable ) {
RLMAssertOperator ( < , 0 U , 0 U , 0 U ) ;
RLMAssertOperator ( <= , 0 U , 0 U , 1 U ) ;
RLMAssertOperator ( > , 0 U , 0 U , 0 U ) ;
RLMAssertOperator ( >= , 0 U , 0 U , 1 U ) ;
}
if ( d . substringOperations ) {
RLMAssertOperator ( BEGINSWITH , 0 U , 0 U , 1 U ) ;
RLMAssertOperator ( ENDSWITH , 0 U , 0 U , 1 U ) ;
RLMAssertOperator ( CONTAINS , 0 U , 0 U , 1 U ) ;
}
}
}
- ( void ) testINPredicateOnNullWithNonNullValues
{
RLMRealm * realm = [ RLMRealm defaultRealm ] ;
[ realm beginWriteTransaction ] ;
[ AllOptionalTypes createInRealm : realm withValue : @ [ @ YES , @ 1 , @ 1 , @ 1 , @ "abc" ,
[ @ "a" dataUsingEncoding : NSUTF8StringEncoding ] ,
[ NSDate dateWithTimeIntervalSince1970 : 1 ] ] ] ;
[ realm commitWriteTransaction ] ;
////////////////////////
// Literal Predicates
////////////////////////
// BOOL
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 0 notCount : 1 where : @ "boolObj IN {NULL}" ] ;
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 1 notCount : 0 where : @ "boolObj IN {YES}" ] ;
// int
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 0 notCount : 1 where : @ "intObj IN {NULL}" ] ;
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 1 notCount : 0 where : @ "intObj IN {1}" ] ;
// float
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 0 notCount : 1 where : @ "floatObj IN {NULL}" ] ;
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 1 notCount : 0 where : @ "floatObj IN {1}" ] ;
// double
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 0 notCount : 1 where : @ "doubleObj IN {NULL}" ] ;
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 1 notCount : 0 where : @ "doubleObj IN {1}" ] ;
// NSString
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 0 notCount : 1 where : @ "string IN {NULL}" ] ;
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 1 notCount : 0 where : @ "string IN {'abc'}" ] ;
// NSData
// Can't represent NSData with NSPredicate literal. See format predicates below
// NSDate
// Can't represent NSDate with NSPredicate literal. See format predicates below
////////////////////////
// Format Predicates
////////////////////////
// BOOL
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 0 U notCount : 1 U where : @ "boolObj IN %@" , @ [ NSNull . null ] ] ;
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 1 U notCount : 0 U where : @ "boolObj IN %@" , @ [ @ YES ] ] ;
// int
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 0 U notCount : 1 U where : @ "intObj IN %@" , @ [ NSNull . null ] ] ;
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 1 U notCount : 0 U where : @ "intObj IN %@" , @ [ @ 1 ] ] ;
// float
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 0 U notCount : 1 U where : @ "floatObj IN %@" , @ [ NSNull . null ] ] ;
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 1 U notCount : 0 U where : @ "floatObj IN %@" , @ [ @ 1 ] ] ;
// double
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 0 U notCount : 1 U where : @ "doubleObj IN %@" , @ [ NSNull . null ] ] ;
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 1 U notCount : 0 U where : @ "doubleObj IN %@" , @ [ @ 1 ] ] ;
// NSString
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 1 U notCount : 0 U where : @ "string IN %@" , @ [ @ "abc" ] ] ;
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 0 U notCount : 1 U where : @ "string IN %@" , @ [ NSNull . null ] ] ;
// NSData
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 0 U notCount : 1 U where : @ "data IN %@" , @ [ NSNull . null ] ] ;
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 1 U notCount : 0 U where : @ "data IN %@" , @ [ [ @ "a" dataUsingEncoding : NSUTF8StringEncoding ] ] ] ;
// NSDate
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 0 U notCount : 1 U where : @ "date IN %@" , @ [ NSNull . null ] ] ;
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 1 U notCount : 0 U where : @ "date IN %@" , @ [ [ NSDate dateWithTimeIntervalSince1970 : 1 ] ] ] ;
}
- ( void ) testINPredicateOnNullWithNullValues
{
RLMRealm * realm = [ RLMRealm defaultRealm ] ;
[ realm beginWriteTransaction ] ;
[ AllOptionalTypes createInRealm : realm withValue : @ [ NSNull . null , NSNull . null ,
NSNull . null , NSNull . null ,
NSNull . null , NSNull . null ,
NSNull . null ] ] ;
[ realm commitWriteTransaction ] ;
////////////////////////
// Literal Predicates
////////////////////////
// BOOL
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 1 notCount : 0 where : @ "boolObj IN {NULL}" ] ;
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 0 notCount : 1 where : @ "boolObj IN {YES}" ] ;
// int
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 1 notCount : 0 where : @ "intObj IN {NULL}" ] ;
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 0 notCount : 1 where : @ "intObj IN {1}" ] ;
// float
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 1 notCount : 0 where : @ "floatObj IN {NULL}" ] ;
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 0 notCount : 1 where : @ "floatObj IN {1}" ] ;
// double
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 1 notCount : 0 where : @ "doubleObj IN {NULL}" ] ;
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 0 notCount : 1 where : @ "doubleObj IN {1}" ] ;
// NSString
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 1 notCount : 0 where : @ "string IN {NULL}" ] ;
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 0 notCount : 1 where : @ "string IN {'abc'}" ] ;
// NSData
// Can't represent NSData with NSPredicate literal. See format predicates below
// NSDate
// Can't represent NSDate with NSPredicate literal. See format predicates below
////////////////////////
// Format Predicates
////////////////////////
// BOOL
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 1 U notCount : 0 U where : @ "boolObj IN %@" , @ [ NSNull . null ] ] ;
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 0 U notCount : 1 U where : @ "boolObj IN %@" , @ [ @ YES ] ] ;
// int
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 1 U notCount : 0 U where : @ "intObj IN %@" , @ [ NSNull . null ] ] ;
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 0 U notCount : 1 U where : @ "intObj IN %@" , @ [ @ 1 ] ] ;
// float
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 1 U notCount : 0 U where : @ "floatObj IN %@" , @ [ NSNull . null ] ] ;
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 0 U notCount : 1 U where : @ "floatObj IN %@" , @ [ @ 1 ] ] ;
// double
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 1 U notCount : 0 U where : @ "doubleObj IN %@" , @ [ NSNull . null ] ] ;
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 0 U notCount : 1 U where : @ "doubleObj IN %@" , @ [ @ 1 ] ] ;
// NSString
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 0 U notCount : 1 U where : @ "string IN %@" , @ [ @ "abc" ] ] ;
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 1 U notCount : 0 U where : @ "string IN %@" , @ [ NSNull . null ] ] ;
// NSData
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 1 U notCount : 0 U where : @ "data IN %@" , @ [ NSNull . null ] ] ;
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 0 U notCount : 1 U where : @ "data IN %@" , @ [ [ @ "a" dataUsingEncoding : NSUTF8StringEncoding ] ] ] ;
// NSDate
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 1 U notCount : 0 U where : @ "date IN %@" , @ [ NSNull . null ] ] ;
[ self testClass : [ AllOptionalTypes class ] withNormalCount : 0 U notCount : 1 U where : @ "date IN %@" , @ [ [ NSDate dateWithTimeIntervalSince1970 : 1 ] ] ] ;
}
* /