KVOTests.mm 67 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077
  1. ////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2015 Realm Inc.
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License");
  6. // you may not use this file except in compliance with the License.
  7. // You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. //
  17. ////////////////////////////////////////////////////////////////////////////
  18. #import "RLMTestCase.h"
  19. #import "RLMObjectSchema_Private.hpp"
  20. #import "RLMObjectStore.h"
  21. #import "RLMObject_Private.hpp"
  22. #import "RLMRealmConfiguration_Private.hpp"
  23. #import "RLMRealm_Private.hpp"
  24. #import "RLMSchema_Private.h"
  25. #import "shared_realm.hpp"
  26. #import <realm/descriptor.hpp>
  27. #import <realm/group.hpp>
  28. #import <atomic>
  29. #import <memory>
  30. #import <objc/runtime.h>
  31. #import <vector>
  32. RLM_ARRAY_TYPE(KVOObject)
  33. RLM_ARRAY_TYPE(KVOLinkObject1)
  34. @interface KVOObject : RLMObject
  35. @property int pk; // Primary key for isEqual:
  36. @property int ignored;
  37. @property BOOL boolCol;
  38. @property int16_t int16Col;
  39. @property int32_t int32Col;
  40. @property int64_t int64Col;
  41. @property float floatCol;
  42. @property double doubleCol;
  43. @property bool cBoolCol;
  44. @property NSString *stringCol;
  45. @property NSData *binaryCol;
  46. @property NSDate *dateCol;
  47. @property KVOObject *objectCol;
  48. @property RLMArray<RLMBool> *boolArray;
  49. @property RLMArray<RLMInt> *intArray;
  50. @property RLMArray<RLMFloat> *floatArray;
  51. @property RLMArray<RLMDouble> *doubleArray;
  52. @property RLMArray<RLMString> *stringArray;
  53. @property RLMArray<RLMData> *dataArray;
  54. @property RLMArray<RLMDate> *dateArray;
  55. @property RLMArray<KVOObject> *objectArray;
  56. @property NSNumber<RLMInt> *optIntCol;
  57. @property NSNumber<RLMFloat> *optFloatCol;
  58. @property NSNumber<RLMDouble> *optDoubleCol;
  59. @property NSNumber<RLMBool> *optBoolCol;
  60. @end
  61. @implementation KVOObject
  62. + (NSString *)primaryKey {
  63. return @"pk";
  64. }
  65. + (NSArray *)ignoredProperties {
  66. return @[@"ignored"];
  67. }
  68. @end
  69. @interface KVOLinkObject1 : RLMObject
  70. @property int pk; // Primary key for isEqual:
  71. @property KVOObject *obj;
  72. @property RLMArray<KVOObject> *array;
  73. @end
  74. @implementation KVOLinkObject1
  75. + (NSString *)primaryKey {
  76. return @"pk";
  77. }
  78. @end
  79. @interface KVOLinkObject2 : RLMObject
  80. @property int pk; // Primary key for isEqual:
  81. @property KVOLinkObject1 *obj;
  82. @property RLMArray<KVOLinkObject1> *array;
  83. @end
  84. @implementation KVOLinkObject2
  85. + (NSString *)primaryKey {
  86. return @"pk";
  87. }
  88. @end
  89. @interface PlainKVOObject : NSObject
  90. @property int ignored;
  91. @property BOOL boolCol;
  92. @property int16_t int16Col;
  93. @property int32_t int32Col;
  94. @property int64_t int64Col;
  95. @property float floatCol;
  96. @property double doubleCol;
  97. @property bool cBoolCol;
  98. @property NSString *stringCol;
  99. @property NSData *binaryCol;
  100. @property NSDate *dateCol;
  101. @property PlainKVOObject *objectCol;
  102. @property NSMutableArray *boolArray;
  103. @property NSMutableArray *intArray;
  104. @property NSMutableArray *floatArray;
  105. @property NSMutableArray *doubleArray;
  106. @property NSMutableArray *stringArray;
  107. @property NSMutableArray *dataArray;
  108. @property NSMutableArray *dateArray;
  109. @property NSMutableArray *objectArray;
  110. @property NSNumber<RLMInt> *optIntCol;
  111. @property NSNumber<RLMFloat> *optFloatCol;
  112. @property NSNumber<RLMDouble> *optDoubleCol;
  113. @property NSNumber<RLMBool> *optBoolCol;
  114. @end
  115. @implementation PlainKVOObject
  116. @end
  117. @interface PlainLinkObject1 : NSObject
  118. @property PlainKVOObject *obj;
  119. @property NSMutableArray *array;
  120. @end
  121. @implementation PlainLinkObject1
  122. @end
  123. @interface PlainLinkObject2 : NSObject
  124. @property PlainLinkObject1 *obj;
  125. @property NSMutableArray *array;
  126. @end
  127. @implementation PlainLinkObject2
  128. @end
  129. // Tables with no links (or backlinks) preserve the order of rows on
  130. // insertion/deletion, while tables with links do not, so we need an object
  131. // class known to have no links to test the ordered case
  132. @interface ObjectWithNoLinksToOrFrom : RLMObject
  133. @property int value;
  134. @end
  135. @implementation ObjectWithNoLinksToOrFrom
  136. @end
  137. // An object which removes a KVO registration when it's deallocated, for use
  138. // as an associated object
  139. @interface KVOUnregisterHelper : NSObject
  140. @end
  141. @implementation KVOUnregisterHelper {
  142. __unsafe_unretained id _obj;
  143. __unsafe_unretained id _observer;
  144. NSString *_keyPath;
  145. }
  146. + (void)automaticallyUnregister:(id)observer object:(id)obj keyPath:(NSString *)keyPath {
  147. KVOUnregisterHelper *helper = [self new];
  148. helper->_observer = observer;
  149. helper->_obj = obj;
  150. helper->_keyPath = keyPath;
  151. objc_setAssociatedObject(obj, (__bridge void *)helper, helper, OBJC_ASSOCIATION_RETAIN);
  152. }
  153. - (void)dealloc {
  154. [_obj removeObserver:_observer forKeyPath:_keyPath];
  155. }
  156. @end
  157. // A KVO observer which retains the given object until it observes a change
  158. @interface ReleaseOnObservation : NSObject
  159. @property (strong) id object;
  160. @end
  161. @implementation ReleaseOnObservation
  162. - (void)observeValueForKeyPath:(NSString *)keyPath
  163. ofObject:(id)object
  164. change:(__unused NSDictionary *)change
  165. context:(void *)context
  166. {
  167. [object removeObserver:self forKeyPath:keyPath context:context];
  168. _object = nil;
  169. }
  170. @end
  171. @interface KVOTests : RLMTestCase
  172. // get an object that should be observed for the given object being mutated
  173. // used by some of the subclasses to observe a different accessor for the same row
  174. - (id)observableForObject:(id)obj;
  175. @end
  176. // subscribes to kvo notifications on the passed object on creation, records
  177. // all change notifications sent and makes them available in `notifications`,
  178. // and automatically unsubscribes on destruction
  179. class KVORecorder {
  180. id _observer;
  181. id _obj;
  182. NSString *_keyPath;
  183. RLMRealm *_mutationRealm;
  184. RLMRealm *_observationRealm;
  185. NSMutableArray *_notifications;
  186. public:
  187. // construct a new recorder for the given `keyPath` on `obj`, using `observer`
  188. // as the NSObject helper to actually add as an observer
  189. KVORecorder(id observer, id obj, NSString *keyPath,
  190. int options = NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew)
  191. : _observer(observer)
  192. , _obj([observer observableForObject:obj])
  193. , _keyPath(keyPath)
  194. , _mutationRealm([obj respondsToSelector:@selector(realm)] ? (RLMRealm *)[obj realm] : nil)
  195. , _observationRealm([_obj respondsToSelector:@selector(realm)] ? (RLMRealm *)[_obj realm] : nil)
  196. , _notifications([NSMutableArray new])
  197. {
  198. [_obj addObserver:observer forKeyPath:keyPath options:options context:this];
  199. }
  200. ~KVORecorder() {
  201. id self = _observer;
  202. @try {
  203. [_obj removeObserver:_observer forKeyPath:_keyPath context:this];
  204. }
  205. @catch (NSException *e) {
  206. XCTFail(@"%@", e.description);
  207. }
  208. XCTAssertEqual(0U, _notifications.count);
  209. }
  210. // record a single notification
  211. void operator()(NSString *key, id obj, NSDictionary *changeDictionary) {
  212. id self = _observer;
  213. XCTAssertEqual(obj, _obj);
  214. XCTAssertEqualObjects(key, _keyPath);
  215. [_notifications addObject:changeDictionary.copy];
  216. }
  217. // ensure that the observed object is updated for any changes made to the
  218. // object being mutated if they are different
  219. void refresh() {
  220. if (_mutationRealm != _observationRealm) {
  221. [_mutationRealm commitWriteTransaction];
  222. [_observationRealm refresh];
  223. [_mutationRealm beginWriteTransaction];
  224. }
  225. }
  226. NSDictionary *pop_front() {
  227. NSDictionary *value = [_notifications firstObject];
  228. if (value) {
  229. [_notifications removeObjectAtIndex:0U];
  230. }
  231. return value;
  232. }
  233. NSUInteger size() const {
  234. return _notifications.count;
  235. }
  236. bool empty() const {
  237. return _notifications.count == 0;
  238. }
  239. };
  240. // Assert that `recorder` has a notification at `index` and return it if so
  241. #define AssertNotification(recorder) ([&]{ \
  242. (recorder).refresh(); \
  243. NSDictionary *value = recorder.pop_front(); \
  244. XCTAssertNotNil(value, @"Did not get a notification when expected"); \
  245. return value; \
  246. })()
  247. // Validate that `recorder` has at least one notification, and that the first
  248. // notification is the expected one
  249. #define AssertChanged(recorder, from, to) do { \
  250. if (NSDictionary *note = AssertNotification((recorder))) { \
  251. XCTAssertEqualObjects(@(NSKeyValueChangeSetting), note[NSKeyValueChangeKindKey]); \
  252. XCTAssertEqualObjects((from), note[NSKeyValueChangeOldKey]); \
  253. XCTAssertEqualObjects((to), note[NSKeyValueChangeNewKey]); \
  254. } \
  255. else { \
  256. return; \
  257. } \
  258. } while (false)
  259. // Validate that `r` has a notification with the given kind and changed indexes,
  260. // remove it, and verify that there are no more notifications
  261. #define AssertIndexChange(kind, indexes) do { \
  262. if (NSDictionary *note = AssertNotification(r)) { \
  263. XCTAssertEqual([note[NSKeyValueChangeKindKey] intValue], static_cast<int>(kind)); \
  264. XCTAssertEqualObjects(note[NSKeyValueChangeIndexesKey], indexes); \
  265. } \
  266. XCTAssertTrue(r.empty()); \
  267. } while (0)
  268. // Tests for plain Foundation key-value observing to verify that we correctly
  269. // match the standard semantics. Each of the subclasses of KVOTests runs the
  270. // same set of tests on RLMObjects in difference scenarios
  271. @implementation KVOTests
  272. // forward a KVO notification to the KVORecorder stored in the context
  273. - (void)observeValueForKeyPath:(NSString *)keyPath
  274. ofObject:(id)object
  275. change:(NSDictionary *)change
  276. context:(void *)context
  277. {
  278. (*static_cast<KVORecorder *>(context))(keyPath, object, change);
  279. }
  280. // overridden in the multiple accessors, one realm and multiple realms cases
  281. - (id)observableForObject:(id)obj {
  282. return obj;
  283. }
  284. // overridden in the multiple realms case because `-refresh` does not send
  285. // notifications for intermediate states
  286. - (bool)collapsesNotifications {
  287. return false;
  288. }
  289. // overridden in all subclases to return the appropriate object
  290. // base class runs the tests on a plain NSObject using stock KVO to ensure that
  291. // the tests are actually covering the correct behavior, since there's a great
  292. // deal that the documentation doesn't specify
  293. - (id)createObject {
  294. PlainKVOObject *obj = [PlainKVOObject new];
  295. obj.int16Col = 1;
  296. obj.int32Col = 2;
  297. obj.int64Col = 3;
  298. obj.binaryCol = NSData.data;
  299. obj.stringCol = @"";
  300. obj.dateCol = [NSDate dateWithTimeIntervalSinceReferenceDate:0];
  301. obj.boolArray = [NSMutableArray array];
  302. obj.intArray = [NSMutableArray array];
  303. obj.floatArray = [NSMutableArray array];
  304. obj.doubleArray = [NSMutableArray array];
  305. obj.stringArray = [NSMutableArray array];
  306. obj.dataArray = [NSMutableArray array];
  307. obj.dateArray = [NSMutableArray array];
  308. obj.objectArray = [NSMutableArray array];
  309. return obj;
  310. }
  311. - (id)createLinkObject {
  312. PlainLinkObject1 *obj1 = [PlainLinkObject1 new];
  313. obj1.obj = [self createObject];
  314. obj1.array = [NSMutableArray new];
  315. PlainLinkObject2 *obj2 = [PlainLinkObject2 new];
  316. obj2.obj = obj1;
  317. obj2.array = [NSMutableArray new];
  318. return obj2;
  319. }
  320. // actual tests follow
  321. - (void)testRegisterForUnknownProperty {
  322. KVOObject *obj = [self createObject];
  323. XCTAssertNoThrow([obj addObserver:self forKeyPath:@"non-existent" options:0 context:nullptr]);
  324. XCTAssertNoThrow([obj removeObserver:self forKeyPath:@"non-existent"]);
  325. XCTAssertNoThrow([obj addObserver:self forKeyPath:@"non-existent" options:NSKeyValueObservingOptionOld context:nullptr]);
  326. XCTAssertNoThrow([obj removeObserver:self forKeyPath:@"non-existent"]);
  327. XCTAssertNoThrow([obj addObserver:self forKeyPath:@"non-existent" options:NSKeyValueObservingOptionPrior context:nullptr]);
  328. XCTAssertNoThrow([obj removeObserver:self forKeyPath:@"non-existent"]);
  329. }
  330. - (void)testRemoveObserver {
  331. KVOObject *obj = [self createObject];
  332. XCTAssertThrowsSpecificNamed([obj removeObserver:self forKeyPath:@"int32Col"], NSException, NSRangeException);
  333. XCTAssertThrowsSpecificNamed([obj removeObserver:self forKeyPath:@"int32Col" context:nullptr], NSException, NSRangeException);
  334. XCTAssertNoThrow([obj addObserver:self forKeyPath:@"int32Col" options:0 context:nullptr]);
  335. XCTAssertNoThrow([obj removeObserver:self forKeyPath:@"int32Col"]);
  336. XCTAssertThrowsSpecificNamed([obj removeObserver:self forKeyPath:@"int32Col"], NSException, NSRangeException);
  337. // `context` parameter must match if it's passed, but the overload that doesn't
  338. // take one will unregister any context
  339. void *context1 = (void *)1;
  340. void *context2 = (void *)2;
  341. XCTAssertNoThrow([obj addObserver:self forKeyPath:@"int32Col" options:0 context:context1]);
  342. XCTAssertThrows([obj removeObserver:self forKeyPath:@"int32Col" context:context2]);
  343. XCTAssertNoThrow([obj removeObserver:self forKeyPath:@"int32Col" context:context1]);
  344. XCTAssertNoThrow([obj addObserver:self forKeyPath:@"int32Col" options:0 context:context2]);
  345. XCTAssertNoThrow([obj removeObserver:self forKeyPath:@"int32Col" context:context2]);
  346. XCTAssertNoThrow([obj addObserver:self forKeyPath:@"int32Col" options:0 context:context2]);
  347. XCTAssertNoThrow([obj removeObserver:self forKeyPath:@"int32Col"]);
  348. XCTAssertThrows([obj removeObserver:self forKeyPath:@"int32Col"]);
  349. XCTAssertNoThrow([obj addObserver:self forKeyPath:@"int32Col" options:0 context:context1]);
  350. XCTAssertNoThrow([obj addObserver:self forKeyPath:@"int32Col" options:0 context:context2]);
  351. XCTAssertNoThrow([obj removeObserver:self forKeyPath:@"int32Col" context:context1]);
  352. XCTAssertThrows([obj removeObserver:self forKeyPath:@"int32Col" context:context1]);
  353. XCTAssertNoThrow([obj removeObserver:self forKeyPath:@"int32Col" context:context2]);
  354. XCTAssertThrows([obj removeObserver:self forKeyPath:@"int32Col" context:context2]);
  355. XCTAssertNoThrow([obj addObserver:self forKeyPath:@"int32Col" options:0 context:context1]);
  356. XCTAssertNoThrow([obj addObserver:self forKeyPath:@"int32Col" options:0 context:context2]);
  357. XCTAssertNoThrow([obj removeObserver:self forKeyPath:@"int32Col" context:context2]);
  358. XCTAssertThrows([obj removeObserver:self forKeyPath:@"int32Col" context:context2]);
  359. XCTAssertNoThrow([obj removeObserver:self forKeyPath:@"int32Col" context:context1]);
  360. XCTAssertThrows([obj removeObserver:self forKeyPath:@"int32Col" context:context1]);
  361. // no context version should only unregister one (unspecified) observer
  362. XCTAssertNoThrow([obj addObserver:self forKeyPath:@"int32Col" options:0 context:context1]);
  363. XCTAssertNoThrow([obj addObserver:self forKeyPath:@"int32Col" options:0 context:context2]);
  364. XCTAssertNoThrow([obj removeObserver:self forKeyPath:@"int32Col"]);
  365. XCTAssertNoThrow([obj removeObserver:self forKeyPath:@"int32Col"]);
  366. XCTAssertThrows([obj removeObserver:self forKeyPath:@"int32Col"]);
  367. }
  368. - (void)testRemoveObserverInObservation {
  369. auto helper = [ReleaseOnObservation new];
  370. __unsafe_unretained id obj;
  371. __weak id weakObj;
  372. @autoreleasepool {
  373. obj = weakObj = helper.object = [self createObject];
  374. [obj addObserver:helper forKeyPath:@"int32Col" options:NSKeyValueObservingOptionOld context:nullptr];
  375. }
  376. [obj setInt32Col:0];
  377. XCTAssertNil(helper.object);
  378. XCTAssertNil(weakObj);
  379. }
  380. - (void)testSimple {
  381. KVOObject *obj = [self createObject];
  382. {
  383. KVORecorder r(self, obj, @"int32Col");
  384. obj.int32Col = 10;
  385. AssertChanged(r, @2, @10);
  386. }
  387. {
  388. KVORecorder r(self, obj, @"int32Col");
  389. obj.int32Col = 1;
  390. AssertChanged(r, @10, @1);
  391. }
  392. }
  393. - (void)testSelfAssignmentNotifies {
  394. KVOObject *obj = [self createObject];
  395. {
  396. KVORecorder r(self, obj, @"int32Col");
  397. obj.int32Col = obj.int32Col;
  398. AssertChanged(r, @2, @2);
  399. }
  400. }
  401. - (void)testMultipleObserversAreNotified {
  402. KVOObject *obj = [self createObject];
  403. {
  404. KVORecorder r1(self, obj, @"int32Col");
  405. KVORecorder r2(self, obj, @"int32Col");
  406. KVORecorder r3(self, obj, @"int32Col");
  407. obj.int32Col = 10;
  408. AssertChanged(r1, @2, @10);
  409. AssertChanged(r2, @2, @10);
  410. AssertChanged(r3, @2, @10);
  411. }
  412. }
  413. - (void)testOnlyObserversForTheCorrectPropertyAreNotified {
  414. KVOObject *obj = [self createObject];
  415. {
  416. KVORecorder r16(self, obj, @"int16Col");
  417. KVORecorder r32(self, obj, @"int32Col");
  418. KVORecorder r64(self, obj, @"int64Col");
  419. obj.int16Col = 2;
  420. AssertChanged(r16, @1, @2);
  421. XCTAssertTrue(r16.empty());
  422. XCTAssertTrue(r32.empty());
  423. XCTAssertTrue(r64.empty());
  424. obj.int32Col = 2;
  425. AssertChanged(r32, @2, @2);
  426. XCTAssertTrue(r16.empty());
  427. XCTAssertTrue(r32.empty());
  428. XCTAssertTrue(r64.empty());
  429. obj.int64Col = 2;
  430. AssertChanged(r64, @3, @2);
  431. XCTAssertTrue(r16.empty());
  432. XCTAssertTrue(r32.empty());
  433. XCTAssertTrue(r64.empty());
  434. }
  435. }
  436. - (void)testMultipleChangesWithSingleObserver {
  437. KVOObject *obj = [self createObject];
  438. KVORecorder r(self, obj, @"int32Col");
  439. obj.int32Col = 1;
  440. obj.int32Col = 2;
  441. obj.int32Col = 3;
  442. obj.int32Col = 3;
  443. if (self.collapsesNotifications) {
  444. AssertChanged(r, @2, @3);
  445. }
  446. else {
  447. AssertChanged(r, @2, @1);
  448. AssertChanged(r, @1, @2);
  449. AssertChanged(r, @2, @3);
  450. AssertChanged(r, @3, @3);
  451. }
  452. }
  453. - (void)testOnlyObserversForTheCorrectObjectAreNotified {
  454. KVOObject *obj1 = [self createObject];
  455. KVOObject *obj2 = [self createObject];
  456. KVORecorder r1(self, obj1, @"int32Col");
  457. KVORecorder r2(self, obj2, @"int32Col");
  458. obj1.int32Col = 10;
  459. AssertChanged(r1, @2, @10);
  460. XCTAssertEqual(0U, r2.size());
  461. obj2.int32Col = 5;
  462. AssertChanged(r2, @2, @5);
  463. }
  464. - (void)testOptionsInitial {
  465. KVOObject *obj = [self createObject];
  466. {
  467. KVORecorder r(self, obj, @"int32Col", 0);
  468. XCTAssertEqual(0U, r.size());
  469. }
  470. {
  471. KVORecorder r(self, obj, @"int32Col", NSKeyValueObservingOptionInitial);
  472. r.pop_front();
  473. }
  474. }
  475. - (void)testOptionsOld {
  476. KVOObject *obj = [self createObject];
  477. {
  478. KVORecorder r(self, obj, @"int32Col", 0);
  479. obj.int32Col = 0;
  480. if (NSDictionary *note = AssertNotification(r)) {
  481. XCTAssertNil(note[NSKeyValueChangeOldKey]);
  482. }
  483. }
  484. {
  485. KVORecorder r(self, obj, @"int32Col", NSKeyValueObservingOptionOld);
  486. obj.int32Col = 0;
  487. if (NSDictionary *note = AssertNotification(r)) {
  488. XCTAssertNotNil(note[NSKeyValueChangeOldKey]);
  489. }
  490. }
  491. }
  492. - (void)testOptionsNew {
  493. KVOObject *obj = [self createObject];
  494. {
  495. KVORecorder r(self, obj, @"int32Col", 0);
  496. obj.int32Col = 0;
  497. if (NSDictionary *note = AssertNotification(r)) {
  498. XCTAssertNil(note[NSKeyValueChangeNewKey]);
  499. }
  500. }
  501. {
  502. KVORecorder r(self, obj, @"int32Col", NSKeyValueObservingOptionNew);
  503. obj.int32Col = 0;
  504. if (NSDictionary *note = AssertNotification(r)) {
  505. XCTAssertNotNil(note[NSKeyValueChangeNewKey]);
  506. }
  507. }
  508. }
  509. - (void)testOptionsPrior {
  510. KVOObject *obj = [self createObject];
  511. KVORecorder r(self, obj, @"int32Col", NSKeyValueObservingOptionNew|NSKeyValueObservingOptionPrior);
  512. obj.int32Col = 0;
  513. r.refresh();
  514. XCTAssertEqual(2U, r.size());
  515. if (NSDictionary *note = AssertNotification(r)) {
  516. XCTAssertNil(note[NSKeyValueChangeNewKey]);
  517. XCTAssertEqualObjects(@YES, note[NSKeyValueChangeNotificationIsPriorKey]);
  518. }
  519. if (NSDictionary *note = AssertNotification(r)) {
  520. XCTAssertNotNil(note[NSKeyValueChangeNewKey]);
  521. XCTAssertNil(note[NSKeyValueChangeNotificationIsPriorKey]);
  522. }
  523. }
  524. - (void)testAllPropertyTypes {
  525. KVOObject *obj = [self createObject];
  526. {
  527. KVORecorder r(self, obj, @"boolCol");
  528. obj.boolCol = YES;
  529. AssertChanged(r, @NO, @YES);
  530. }
  531. {
  532. KVORecorder r(self, obj, @"int16Col");
  533. obj.int16Col = 0;
  534. AssertChanged(r, @1, @0);
  535. }
  536. {
  537. KVORecorder r(self, obj, @"int32Col");
  538. obj.int32Col = 0;
  539. AssertChanged(r, @2, @0);
  540. }
  541. {
  542. KVORecorder r(self, obj, @"int64Col");
  543. obj.int64Col = 0;
  544. AssertChanged(r, @3, @0);
  545. }
  546. {
  547. KVORecorder r(self, obj, @"floatCol");
  548. obj.floatCol = 1.0f;
  549. AssertChanged(r, @0, @1);
  550. }
  551. {
  552. KVORecorder r(self, obj, @"doubleCol");
  553. obj.doubleCol = 1.0;
  554. AssertChanged(r, @0, @1);
  555. }
  556. {
  557. KVORecorder r(self, obj, @"cBoolCol");
  558. obj.cBoolCol = YES;
  559. AssertChanged(r, @NO, @YES);
  560. }
  561. {
  562. KVORecorder r(self, obj, @"stringCol");
  563. obj.stringCol = @"abc";
  564. AssertChanged(r, @"", @"abc");
  565. obj.stringCol = nil;
  566. AssertChanged(r, @"abc", NSNull.null);
  567. }
  568. {
  569. KVORecorder r(self, obj, @"binaryCol");
  570. NSData *data = [@"abc" dataUsingEncoding:NSUTF8StringEncoding];
  571. obj.binaryCol = data;
  572. AssertChanged(r, NSData.data, data);
  573. obj.binaryCol = nil;
  574. AssertChanged(r, data, NSNull.null);
  575. }
  576. {
  577. KVORecorder r(self, obj, @"dateCol");
  578. NSDate *date = [NSDate dateWithTimeIntervalSinceReferenceDate:1];
  579. obj.dateCol = date;
  580. AssertChanged(r, [NSDate dateWithTimeIntervalSinceReferenceDate:0], date);
  581. obj.dateCol = nil;
  582. AssertChanged(r, date, NSNull.null);
  583. }
  584. {
  585. KVORecorder r(self, obj, @"objectCol");
  586. obj.objectCol = obj;
  587. AssertChanged(r, NSNull.null, [self observableForObject:obj]);
  588. obj.objectCol = nil;
  589. AssertChanged(r, [self observableForObject:obj], NSNull.null);
  590. }
  591. {
  592. KVORecorder r(self, obj, @"intArray");
  593. obj.intArray = obj.intArray;
  594. r.refresh();
  595. r.pop_front(); // asserts that there's something to pop
  596. }
  597. {
  598. KVORecorder r(self, obj, @"boolArray");
  599. obj.boolArray = obj.boolArray;
  600. r.refresh();
  601. r.pop_front(); // asserts that there's something to pop
  602. }
  603. {
  604. KVORecorder r(self, obj, @"floatArray");
  605. obj.floatArray = obj.floatArray;
  606. r.refresh();
  607. r.pop_front(); // asserts that there's something to pop
  608. }
  609. {
  610. KVORecorder r(self, obj, @"doubleArray");
  611. obj.doubleArray = obj.doubleArray;
  612. r.refresh();
  613. r.pop_front(); // asserts that there's something to pop
  614. }
  615. {
  616. KVORecorder r(self, obj, @"stringArray");
  617. obj.stringArray = obj.stringArray;
  618. r.refresh();
  619. r.pop_front(); // asserts that there's something to pop
  620. }
  621. {
  622. KVORecorder r(self, obj, @"dataArray");
  623. obj.dataArray = obj.dataArray;
  624. r.refresh();
  625. r.pop_front(); // asserts that there's something to pop
  626. }
  627. {
  628. KVORecorder r(self, obj, @"dateArray");
  629. obj.dateArray = obj.dateArray;
  630. r.refresh();
  631. r.pop_front(); // asserts that there's something to pop
  632. }
  633. {
  634. KVORecorder r(self, obj, @"objectArray");
  635. obj.objectArray = obj.objectArray;
  636. r.refresh();
  637. r.pop_front(); // asserts that there's something to pop
  638. }
  639. {
  640. KVORecorder r(self, obj, @"optIntCol");
  641. obj.optIntCol = @1;
  642. AssertChanged(r, NSNull.null, @1);
  643. obj.optIntCol = nil;
  644. AssertChanged(r, @1, NSNull.null);
  645. }
  646. {
  647. KVORecorder r(self, obj, @"optFloatCol");
  648. obj.optFloatCol = @1.1f;
  649. AssertChanged(r, NSNull.null, @1.1f);
  650. obj.optFloatCol = nil;
  651. AssertChanged(r, @1.1f, NSNull.null);
  652. }
  653. {
  654. KVORecorder r(self, obj, @"optDoubleCol");
  655. obj.optDoubleCol = @1.1;
  656. AssertChanged(r, NSNull.null, @1.1);
  657. obj.optDoubleCol = nil;
  658. AssertChanged(r, @1.1, NSNull.null);
  659. }
  660. {
  661. KVORecorder r(self, obj, @"optBoolCol");
  662. obj.optBoolCol = @YES;
  663. AssertChanged(r, NSNull.null, @YES);
  664. obj.optBoolCol = nil;
  665. AssertChanged(r, @YES, NSNull.null);
  666. }
  667. }
  668. - (void)testAllPropertyTypesKVC {
  669. KVOObject *obj = [self createObject];
  670. {
  671. KVORecorder r(self, obj, @"boolCol");
  672. [obj setValue:@YES forKey:@"boolCol"];
  673. AssertChanged(r, @NO, @YES);
  674. }
  675. {
  676. KVORecorder r(self, obj, @"int16Col");
  677. [obj setValue:@0 forKey:@"int16Col"];
  678. AssertChanged(r, @1, @0);
  679. }
  680. {
  681. KVORecorder r(self, obj, @"int32Col");
  682. [obj setValue:@0 forKey:@"int32Col"];
  683. AssertChanged(r, @2, @0);
  684. }
  685. {
  686. KVORecorder r(self, obj, @"int64Col");
  687. [obj setValue:@0 forKey:@"int64Col"];
  688. AssertChanged(r, @3, @0);
  689. }
  690. {
  691. KVORecorder r(self, obj, @"floatCol");
  692. [obj setValue:@1.0f forKey:@"floatCol"];
  693. AssertChanged(r, @0, @1);
  694. }
  695. {
  696. KVORecorder r(self, obj, @"doubleCol");
  697. [obj setValue:@1.0 forKey:@"doubleCol"];
  698. AssertChanged(r, @0, @1);
  699. }
  700. {
  701. KVORecorder r(self, obj, @"cBoolCol");
  702. [obj setValue:@YES forKey:@"cBoolCol"];
  703. AssertChanged(r, @NO, @YES);
  704. }
  705. {
  706. KVORecorder r(self, obj, @"stringCol");
  707. [obj setValue:@"abc" forKey:@"stringCol"];
  708. AssertChanged(r, @"", @"abc");
  709. [obj setValue:nil forKey:@"stringCol"];
  710. AssertChanged(r, @"abc", NSNull.null);
  711. }
  712. {
  713. KVORecorder r(self, obj, @"binaryCol");
  714. NSData *data = [@"abc" dataUsingEncoding:NSUTF8StringEncoding];
  715. [obj setValue:data forKey:@"binaryCol"];
  716. AssertChanged(r, NSData.data, data);
  717. [obj setValue:nil forKey:@"binaryCol"];
  718. AssertChanged(r, data, NSNull.null);
  719. }
  720. {
  721. KVORecorder r(self, obj, @"dateCol");
  722. NSDate *date = [NSDate dateWithTimeIntervalSinceReferenceDate:1];
  723. [obj setValue:date forKey:@"dateCol"];
  724. AssertChanged(r, [NSDate dateWithTimeIntervalSinceReferenceDate:0], date);
  725. [obj setValue:nil forKey:@"dateCol"];
  726. AssertChanged(r, date, NSNull.null);
  727. }
  728. {
  729. KVORecorder r(self, obj, @"objectCol");
  730. [obj setValue:obj forKey:@"objectCol"];
  731. AssertChanged(r, NSNull.null, [self observableForObject:obj]);
  732. [obj setValue:nil forKey:@"objectCol"];
  733. AssertChanged(r, [self observableForObject:obj], NSNull.null);
  734. }
  735. {
  736. KVORecorder r(self, obj, @"objectArray");
  737. [obj setValue:obj.objectArray forKey:@"objectArray"];
  738. r.refresh();
  739. r.pop_front(); // asserts that there's something to pop
  740. }
  741. {
  742. KVORecorder r(self, obj, @"optIntCol");
  743. [obj setValue:@1 forKey:@"optIntCol"];
  744. AssertChanged(r, NSNull.null, @1);
  745. [obj setValue:nil forKey:@"optIntCol"];
  746. AssertChanged(r, @1, NSNull.null);
  747. }
  748. {
  749. KVORecorder r(self, obj, @"optFloatCol");
  750. [obj setValue:@1.1f forKey:@"optFloatCol"];
  751. AssertChanged(r, NSNull.null, @1.1f);
  752. [obj setValue:nil forKey:@"optFloatCol"];
  753. AssertChanged(r, @1.1f, NSNull.null);
  754. }
  755. {
  756. KVORecorder r(self, obj, @"optDoubleCol");
  757. [obj setValue:@1.1 forKey:@"optDoubleCol"];
  758. AssertChanged(r, NSNull.null, @1.1);
  759. [obj setValue:nil forKey:@"optDoubleCol"];
  760. AssertChanged(r, @1.1, NSNull.null);
  761. }
  762. {
  763. KVORecorder r(self, obj, @"optBoolCol");
  764. [obj setValue:@YES forKey:@"optBoolCol"];
  765. AssertChanged(r, NSNull.null, @YES);
  766. [obj setValue:nil forKey:@"optBoolCol"];
  767. AssertChanged(r, @YES, NSNull.null);
  768. }
  769. }
  770. - (void)testAllPropertyTypesDynamic {
  771. KVOObject *obj = [self createObject];
  772. if (![obj respondsToSelector:@selector(setObject:forKeyedSubscript:)]) {
  773. return;
  774. }
  775. {
  776. KVORecorder r(self, obj, @"boolCol");
  777. obj[@"boolCol"] = @YES;
  778. AssertChanged(r, @NO, @YES);
  779. }
  780. {
  781. KVORecorder r(self, obj, @"int16Col");
  782. obj[@"int16Col"] = @0;
  783. AssertChanged(r, @1, @0);
  784. }
  785. {
  786. KVORecorder r(self, obj, @"int32Col");
  787. obj[@"int32Col"] = @0;
  788. AssertChanged(r, @2, @0);
  789. }
  790. {
  791. KVORecorder r(self, obj, @"int64Col");
  792. obj[@"int64Col"] = @0;
  793. AssertChanged(r, @3, @0);
  794. }
  795. {
  796. KVORecorder r(self, obj, @"floatCol");
  797. obj[@"floatCol"] = @1.0f;
  798. AssertChanged(r, @0, @1);
  799. }
  800. {
  801. KVORecorder r(self, obj, @"doubleCol");
  802. obj[@"doubleCol"] = @1.0;
  803. AssertChanged(r, @0, @1);
  804. }
  805. {
  806. KVORecorder r(self, obj, @"cBoolCol");
  807. obj[@"cBoolCol"] = @YES;
  808. AssertChanged(r, @NO, @YES);
  809. }
  810. {
  811. KVORecorder r(self, obj, @"stringCol");
  812. obj[@"stringCol"] = @"abc";
  813. AssertChanged(r, @"", @"abc");
  814. obj[@"stringCol"] = nil;
  815. AssertChanged(r, @"abc", NSNull.null);
  816. }
  817. {
  818. KVORecorder r(self, obj, @"binaryCol");
  819. NSData *data = [@"abc" dataUsingEncoding:NSUTF8StringEncoding];
  820. obj[@"binaryCol"] = data;
  821. AssertChanged(r, NSData.data, data);
  822. obj[@"binaryCol"] = nil;
  823. AssertChanged(r, data, NSNull.null);
  824. }
  825. {
  826. KVORecorder r(self, obj, @"dateCol");
  827. NSDate *date = [NSDate dateWithTimeIntervalSinceReferenceDate:1];
  828. obj[@"dateCol"] = date;
  829. AssertChanged(r, [NSDate dateWithTimeIntervalSinceReferenceDate:0], date);
  830. obj[@"dateCol"] = nil;
  831. AssertChanged(r, date, NSNull.null);
  832. }
  833. {
  834. KVORecorder r(self, obj, @"objectCol");
  835. obj[@"objectCol"] = obj;
  836. AssertChanged(r, NSNull.null, [self observableForObject:obj]);
  837. obj[@"objectCol"] = nil;
  838. AssertChanged(r, [self observableForObject:obj], NSNull.null);
  839. }
  840. {
  841. KVORecorder r(self, obj, @"objectArray");
  842. obj[@"objectArray"] = obj.objectArray;
  843. r.refresh();
  844. r.pop_front(); // asserts that there's something to pop
  845. }
  846. {
  847. KVORecorder r(self, obj, @"optIntCol");
  848. obj[@"optIntCol"] = @1;
  849. AssertChanged(r, NSNull.null, @1);
  850. obj[@"optIntCol"] = nil;
  851. AssertChanged(r, @1, NSNull.null);
  852. }
  853. {
  854. KVORecorder r(self, obj, @"optFloatCol");
  855. obj[@"optFloatCol"] = @1.1f;
  856. AssertChanged(r, NSNull.null, @1.1f);
  857. obj[@"optFloatCol"] = nil;
  858. AssertChanged(r, @1.1f, NSNull.null);
  859. }
  860. {
  861. KVORecorder r(self, obj, @"optDoubleCol");
  862. obj[@"optDoubleCol"] = @1.1;
  863. AssertChanged(r, NSNull.null, @1.1);
  864. obj[@"optDoubleCol"] = nil;
  865. AssertChanged(r, @1.1, NSNull.null);
  866. }
  867. {
  868. KVORecorder r(self, obj, @"optBoolCol");
  869. obj[@"optBoolCol"] = @YES;
  870. AssertChanged(r, NSNull.null, @YES);
  871. obj[@"optBoolCol"] = nil;
  872. AssertChanged(r, @YES, NSNull.null);
  873. }
  874. }
  875. - (void)testArrayDiffs {
  876. KVOLinkObject2 *obj = [self createLinkObject];
  877. KVORecorder r(self, obj, @"array");
  878. id mutator = [obj mutableArrayValueForKey:@"array"];
  879. [mutator addObject:obj.obj];
  880. AssertIndexChange(NSKeyValueChangeInsertion, [NSIndexSet indexSetWithIndex:0]);
  881. [mutator addObject:obj.obj];
  882. AssertIndexChange(NSKeyValueChangeInsertion, [NSIndexSet indexSetWithIndex:1]);
  883. [mutator removeObjectAtIndex:0];
  884. AssertIndexChange(NSKeyValueChangeRemoval, [NSIndexSet indexSetWithIndex:0]);
  885. [mutator replaceObjectAtIndex:0 withObject:obj.obj];
  886. AssertIndexChange(NSKeyValueChangeReplacement, [NSIndexSet indexSetWithIndex:0]);
  887. NSMutableIndexSet *indexes = [NSMutableIndexSet new];
  888. [indexes addIndex:0];
  889. [indexes addIndex:2];
  890. [mutator insertObjects:@[obj.obj, obj.obj] atIndexes:indexes];
  891. AssertIndexChange(NSKeyValueChangeInsertion, indexes);
  892. [mutator removeObjectsAtIndexes:indexes];
  893. AssertIndexChange(NSKeyValueChangeRemoval, indexes);
  894. if (![obj.array isKindOfClass:[NSArray class]]) {
  895. // We deliberately diverge from NSMutableArray for `removeAllObjects` and
  896. // `addObjectsFromArray:`, because generating a separate notification for
  897. // each object added or removed is needlessly pessimal.
  898. [mutator addObjectsFromArray:@[obj.obj, obj.obj]];
  899. AssertIndexChange(NSKeyValueChangeInsertion, [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(1, 2)]);
  900. // NSArray sends multiple notifications for exchange, which we can't do
  901. // on refresh
  902. [mutator exchangeObjectAtIndex:0 withObjectAtIndex:1];
  903. AssertIndexChange(NSKeyValueChangeReplacement, [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 2)]);
  904. // NSArray doesn't have move
  905. [mutator moveObjectAtIndex:1 toIndex:0];
  906. AssertIndexChange(NSKeyValueChangeReplacement, [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 2)]);
  907. [mutator removeLastObject];
  908. AssertIndexChange(NSKeyValueChangeRemoval, [NSIndexSet indexSetWithIndex:2]);
  909. [mutator removeAllObjects];
  910. AssertIndexChange(NSKeyValueChangeRemoval, [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 2)]);
  911. }
  912. }
  913. - (void)testPrimitiveArrayDiffs {
  914. KVOObject *obj = [self createObject];
  915. KVORecorder r(self, obj, @"intArray");
  916. id mutator = [obj mutableArrayValueForKey:@"intArray"];
  917. [mutator addObject:@1];
  918. AssertIndexChange(NSKeyValueChangeInsertion, [NSIndexSet indexSetWithIndex:0]);
  919. [mutator addObject:@2];
  920. AssertIndexChange(NSKeyValueChangeInsertion, [NSIndexSet indexSetWithIndex:1]);
  921. [mutator removeObjectAtIndex:0];
  922. AssertIndexChange(NSKeyValueChangeRemoval, [NSIndexSet indexSetWithIndex:0]);
  923. [mutator replaceObjectAtIndex:0 withObject:@3];
  924. AssertIndexChange(NSKeyValueChangeReplacement, [NSIndexSet indexSetWithIndex:0]);
  925. NSMutableIndexSet *indexes = [NSMutableIndexSet new];
  926. [indexes addIndex:0];
  927. [indexes addIndex:2];
  928. [mutator insertObjects:@[@4, @5] atIndexes:indexes];
  929. AssertIndexChange(NSKeyValueChangeInsertion, indexes);
  930. [mutator removeObjectsAtIndexes:indexes];
  931. AssertIndexChange(NSKeyValueChangeRemoval, indexes);
  932. if (![obj.intArray isKindOfClass:[NSArray class]]) {
  933. // We deliberately diverge from NSMutableArray for `removeAllObjects` and
  934. // `addObjectsFromArray:`, because generating a separate notification for
  935. // each object added or removed is needlessly pessimal.
  936. [mutator addObjectsFromArray:@[@6, @7]];
  937. AssertIndexChange(NSKeyValueChangeInsertion, [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(1, 2)]);
  938. // NSArray sends multiple notifications for exchange, which we can't do
  939. // on refresh
  940. [mutator exchangeObjectAtIndex:0 withObjectAtIndex:1];
  941. AssertIndexChange(NSKeyValueChangeReplacement, [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 2)]);
  942. // NSArray doesn't have move
  943. [mutator moveObjectAtIndex:1 toIndex:0];
  944. AssertIndexChange(NSKeyValueChangeReplacement, [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 2)]);
  945. [mutator removeLastObject];
  946. AssertIndexChange(NSKeyValueChangeRemoval, [NSIndexSet indexSetWithIndex:2]);
  947. [mutator removeAllObjects];
  948. AssertIndexChange(NSKeyValueChangeRemoval, [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 2)]);
  949. }
  950. }
  951. - (void)testIgnoredProperty {
  952. KVOObject *obj = [self createObject];
  953. KVORecorder r(self, obj, @"ignored");
  954. obj.ignored = 10;
  955. AssertChanged(r, @0, @10);
  956. }
  957. - (void)testChangeEndOfKeyPath {
  958. KVOLinkObject2 *obj = [self createLinkObject];
  959. std::unique_ptr<KVORecorder> r;
  960. @autoreleasepool {
  961. r = std::make_unique<KVORecorder>(self, obj, @"obj.obj.boolCol");
  962. }
  963. obj.obj.obj.boolCol = YES;
  964. AssertChanged(*r, @NO, @YES);
  965. }
  966. - (void)testChangeMiddleOfKeyPath {
  967. KVOLinkObject2 *obj = [self createLinkObject];
  968. KVOObject *oldObj = obj.obj.obj;
  969. KVOObject *newObj = [self createObject];
  970. newObj.boolCol = YES;
  971. KVORecorder r(self, obj, @"obj.obj.boolCol");
  972. obj.obj.obj = newObj;
  973. AssertChanged(r, @NO, @YES);
  974. newObj.boolCol = NO;
  975. AssertChanged(r, @YES, @NO);
  976. oldObj.boolCol = YES;
  977. }
  978. - (void)testNullifyMiddleOfKeyPath {
  979. KVOLinkObject2 *obj = [self createLinkObject];
  980. KVORecorder r(self, obj, @"obj.obj.boolCol");
  981. obj.obj = nil;
  982. AssertChanged(r, @NO, NSNull.null);
  983. }
  984. - (void)testChangeMiddleOfKeyPathToNonNil {
  985. KVOLinkObject2 *obj = [self createLinkObject];
  986. KVOLinkObject1 *obj2 = obj.obj;
  987. obj.obj = nil;
  988. obj2.obj.boolCol = YES;
  989. KVORecorder r(self, obj, @"obj.obj.boolCol");
  990. obj.obj = obj2;
  991. AssertChanged(r, NSNull.null, @YES);
  992. }
  993. - (void)testArrayKVC {
  994. KVOObject *obj = [self createObject];
  995. [obj.objectArray addObject:obj];
  996. KVORecorder r(self, obj, @"boolCol");
  997. [obj.objectArray setValue:@YES forKey:@"boolCol"];
  998. AssertChanged(r, @NO, @YES);
  999. }
  1000. // RLMArray doesn't support @count at all
  1001. //- (void)testObserveArrayCount {
  1002. // KVOObject *obj = [self createObject];
  1003. // KVORecorder r(self, obj, @"objectArray.@count");
  1004. // id mutator = [obj mutableArrayValueForKey:@"objectArray"];
  1005. // [mutator addObject:obj];
  1006. // AssertChanged(r, @0, @1);
  1007. //}
  1008. @end
  1009. // Run tests on an unmanaged RLMObject instance
  1010. @interface KVOUnmanagedObjectTests : KVOTests
  1011. @end
  1012. @implementation KVOUnmanagedObjectTests
  1013. - (id)createObject {
  1014. static int pk = 0;
  1015. KVOObject *obj = [KVOObject new];
  1016. obj.pk = pk++;
  1017. obj.int16Col = 1;
  1018. obj.int32Col = 2;
  1019. obj.int64Col = 3;
  1020. obj.binaryCol = NSData.data;
  1021. obj.stringCol = @"";
  1022. obj.dateCol = [NSDate dateWithTimeIntervalSinceReferenceDate:0];
  1023. return obj;
  1024. }
  1025. - (id)createLinkObject {
  1026. static int pk = 0;
  1027. KVOLinkObject1 *obj1 = [KVOLinkObject1 new];
  1028. obj1.pk = pk++;
  1029. obj1.obj = [self createObject];
  1030. KVOLinkObject2 *obj2 = [KVOLinkObject2 new];
  1031. obj2.pk = pk++;
  1032. obj2.obj = obj1;
  1033. return obj2;
  1034. }
  1035. - (void)testAddToRealmAfterAddingObservers {
  1036. RLMRealm *realm = RLMRealm.defaultRealm;
  1037. [realm beginWriteTransaction];
  1038. KVOObject *obj = [self createObject];
  1039. {
  1040. KVORecorder r(self, obj, @"int32Col");
  1041. XCTAssertThrows([realm addObject:obj]);
  1042. }
  1043. XCTAssertNoThrow([realm addObject:obj]);
  1044. [realm cancelWriteTransaction];
  1045. }
  1046. - (void)testObserveInvalidArrayProperty {
  1047. KVOObject *obj = [self createObject];
  1048. XCTAssertThrows([obj.objectArray addObserver:self forKeyPath:@"self" options:0 context:0]);
  1049. XCTAssertNoThrow([obj.objectArray addObserver:self forKeyPath:RLMInvalidatedKey options:0 context:0]);
  1050. XCTAssertNoThrow([obj.objectArray removeObserver:self forKeyPath:RLMInvalidatedKey context:0]);
  1051. }
  1052. - (void)testUnregisteringViaAnAssociatedObject {
  1053. @autoreleasepool {
  1054. __attribute__((objc_precise_lifetime)) KVOObject *obj = [self createObject];
  1055. [obj addObserver:self forKeyPath:@"boolCol" options:0 context:0];
  1056. [KVOUnregisterHelper automaticallyUnregister:self object:obj keyPath:@"boolCol"];
  1057. }
  1058. // Throws if the unregistration doesn't succeed
  1059. }
  1060. @end
  1061. // Run tests on a managed object, modifying the actual object instance being
  1062. // observed
  1063. @interface KVOManagedObjectTests : KVOTests
  1064. @property (nonatomic, strong) RLMRealm *realm;
  1065. @end
  1066. @implementation KVOManagedObjectTests
  1067. - (void)setUp {
  1068. [super setUp];
  1069. _realm = [self getRealm];
  1070. [_realm beginWriteTransaction];
  1071. }
  1072. - (void)tearDown {
  1073. [self.realm cancelWriteTransaction];
  1074. self.realm = nil;
  1075. [super tearDown];
  1076. }
  1077. - (RLMRealm *)getRealm {
  1078. RLMRealmConfiguration *configuration = [[RLMRealmConfiguration alloc] init];
  1079. configuration.inMemoryIdentifier = @"test";
  1080. configuration.schemaMode = realm::SchemaMode::Additive;
  1081. return [RLMRealm realmWithConfiguration:configuration error:nil];
  1082. }
  1083. - (id)createObject {
  1084. static std::atomic<int> pk{0};
  1085. return [KVOObject createInRealm:_realm withValue:@[@(++pk),
  1086. @NO, @1, @2, @3, @0, @0, @NO, @"",
  1087. NSData.data, [NSDate dateWithTimeIntervalSinceReferenceDate:0]]];
  1088. }
  1089. - (id)createLinkObject {
  1090. static std::atomic<int> pk{0};
  1091. return [KVOLinkObject2 createInRealm:_realm withValue:@[@(++pk), @[@(++pk), [self createObject], @[]], @[]]];
  1092. }
  1093. - (void)testDeleteObservedObject {
  1094. KVOObject *obj = [self createObject];
  1095. KVORecorder r1(self, obj, @"boolCol");
  1096. KVORecorder r2(self, obj, RLMInvalidatedKey);
  1097. [self.realm deleteObject:obj];
  1098. AssertChanged(r2, @NO, @YES);
  1099. // should not crash
  1100. }
  1101. - (void)testDeleteMiddleOfKeyPath {
  1102. KVOLinkObject2 *obj = [self createLinkObject];
  1103. KVORecorder r(self, obj, @"obj.obj.boolCol");
  1104. [self.realm deleteObject:obj.obj];
  1105. AssertChanged(r, @NO, NSNull.null);
  1106. }
  1107. - (void)testDeleteParentOfObservedRLMArray {
  1108. KVOObject *obj = [self createObject];
  1109. KVORecorder r1(self, obj, @"objectArray");
  1110. KVORecorder r2(self, obj, @"objectArray.invalidated");
  1111. KVORecorder r3(self, obj.objectArray, RLMInvalidatedKey);
  1112. [self.realm deleteObject:obj];
  1113. AssertChanged(r2, @NO, @YES);
  1114. AssertChanged(r3, @NO, @YES);
  1115. }
  1116. - (void)testDeleteAllObjects {
  1117. KVOObject *obj = [self createObject];
  1118. KVORecorder r1(self, obj, @"boolCol");
  1119. KVORecorder r2(self, obj, RLMInvalidatedKey);
  1120. [self.realm deleteAllObjects];
  1121. AssertChanged(r2, @NO, @YES);
  1122. // should not crash
  1123. }
  1124. - (void)testClearTable {
  1125. KVOObject *obj = [self createObject];
  1126. KVORecorder r1(self, obj, @"boolCol");
  1127. KVORecorder r2(self, obj, RLMInvalidatedKey);
  1128. [self.realm deleteObjects:[KVOObject allObjectsInRealm:self.realm]];
  1129. AssertChanged(r2, @NO, @YES);
  1130. // should not crash
  1131. }
  1132. - (void)testClearQuery {
  1133. KVOObject *obj = [self createObject];
  1134. KVORecorder r1(self, obj, @"boolCol");
  1135. KVORecorder r2(self, obj, RLMInvalidatedKey);
  1136. [self.realm deleteObjects:[KVOObject objectsInRealm:self.realm where:@"TRUEPREDICATE"]];
  1137. AssertChanged(r2, @NO, @YES);
  1138. // should not crash
  1139. }
  1140. - (void)testClearLinkView {
  1141. KVOObject *obj = [self createObject];
  1142. KVOObject *obj2 = [self createObject];
  1143. [obj2.objectArray addObject:obj];
  1144. KVORecorder r1(self, obj, @"boolCol");
  1145. KVORecorder r2(self, obj, RLMInvalidatedKey);
  1146. [self.realm deleteObjects:obj2.objectArray];
  1147. AssertChanged(r2, @NO, @YES);
  1148. // should not crash
  1149. }
  1150. - (void)testCreateObserverAfterDealloc {
  1151. @autoreleasepool {
  1152. KVOObject *obj = [self createObject];
  1153. KVORecorder r(self, obj, @"boolCol");
  1154. obj.boolCol = YES;
  1155. AssertChanged(r, @NO, @YES);
  1156. }
  1157. @autoreleasepool {
  1158. KVOObject *obj = [self createObject];
  1159. KVORecorder r(self, obj, @"boolCol");
  1160. obj.boolCol = YES;
  1161. AssertChanged(r, @NO, @YES);
  1162. }
  1163. }
  1164. - (void)testDirectlyDeleteLinkedToObject {
  1165. KVOLinkObject2 *obj = [self createLinkObject];
  1166. KVOLinkObject1 *linked = obj.obj;
  1167. KVORecorder r(self, obj, @"obj");
  1168. KVORecorder r2(self, obj, @"obj.invalidated");
  1169. [self.realm deleteObject:linked];
  1170. if (NSDictionary *note = AssertNotification(r)) {
  1171. XCTAssertTrue([note[NSKeyValueChangeOldKey] isKindOfClass:[RLMObjectBase class]]);
  1172. XCTAssertEqualObjects(note[NSKeyValueChangeNewKey], NSNull.null);
  1173. }
  1174. AssertChanged(r2, @NO, NSNull.null);
  1175. }
  1176. - (void)testDeleteLinkedToObjectViaTableClear {
  1177. KVOLinkObject2 *obj = [self createLinkObject];
  1178. KVORecorder r(self, obj, @"obj");
  1179. KVORecorder r2(self, obj, @"obj.invalidated");
  1180. [self.realm deleteObjects:[KVOLinkObject1 allObjectsInRealm:self.realm]];
  1181. if (NSDictionary *note = AssertNotification(r)) {
  1182. XCTAssertTrue([note[NSKeyValueChangeOldKey] isKindOfClass:[RLMObjectBase class]]);
  1183. XCTAssertEqualObjects(note[NSKeyValueChangeNewKey], NSNull.null);
  1184. }
  1185. AssertChanged(r2, @NO, NSNull.null);
  1186. }
  1187. - (void)testDeleteLinkedToObjectViaQueryClear {
  1188. KVOLinkObject2 *obj = [self createLinkObject];
  1189. KVORecorder r(self, obj, @"obj");
  1190. KVORecorder r2(self, obj, @"obj.invalidated");
  1191. [self.realm deleteObjects:[KVOLinkObject1 objectsInRealm:self.realm where:@"TRUEPREDICATE"]];
  1192. if (NSDictionary *note = AssertNotification(r)) {
  1193. XCTAssertTrue([note[NSKeyValueChangeOldKey] isKindOfClass:[RLMObjectBase class]]);
  1194. XCTAssertEqualObjects(note[NSKeyValueChangeNewKey], NSNull.null);
  1195. }
  1196. AssertChanged(r2, @NO, NSNull.null);
  1197. }
  1198. - (void)testDeleteObjectInArray {
  1199. KVOLinkObject2 *obj = [self createLinkObject];
  1200. KVOLinkObject1 *linked = obj.obj;
  1201. [obj.array addObject:linked];
  1202. KVORecorder r(self, obj, @"array");
  1203. [self.realm deleteObject:linked];
  1204. AssertIndexChange(NSKeyValueChangeRemoval, [NSIndexSet indexSetWithIndex:0]);
  1205. }
  1206. - (void)testDeleteObjectsInArrayViaTableClear {
  1207. KVOLinkObject2 *obj = [self createLinkObject];
  1208. KVOLinkObject2 *obj2 = [self createLinkObject];
  1209. [obj.array addObject:obj.obj];
  1210. [obj.array addObject:obj.obj];
  1211. [obj.array addObject:obj2.obj];
  1212. KVORecorder r(self, obj, @"array");
  1213. [self.realm deleteObjects:[KVOLinkObject1 allObjectsInRealm:self.realm]];
  1214. AssertIndexChange(NSKeyValueChangeRemoval, ([NSIndexSet indexSetWithIndexesInRange:{0, 3}]));
  1215. }
  1216. - (void)testDeleteObjectsInArrayViaTableViewClear {
  1217. KVOLinkObject2 *obj = [self createLinkObject];
  1218. KVOLinkObject2 *obj2 = [self createLinkObject];
  1219. [obj.array addObject:obj.obj];
  1220. [obj.array addObject:obj.obj];
  1221. [obj.array addObject:obj2.obj];
  1222. KVORecorder r(self, obj, @"array");
  1223. RLMResults *results = [KVOLinkObject1 objectsInRealm:self.realm where:@"TRUEPREDICATE"];
  1224. [results lastObject];
  1225. [self.realm deleteObjects:results];
  1226. AssertIndexChange(NSKeyValueChangeRemoval, ([NSIndexSet indexSetWithIndexesInRange:{0, 3}]));
  1227. }
  1228. - (void)testDeleteObjectsInArrayViaQueryClear {
  1229. KVOLinkObject2 *obj = [self createLinkObject];
  1230. KVOLinkObject2 *obj2 = [self createLinkObject];
  1231. [obj.array addObject:obj.obj];
  1232. [obj.array addObject:obj.obj];
  1233. [obj.array addObject:obj2.obj];
  1234. KVORecorder r(self, obj, @"array");
  1235. [self.realm deleteObjects:[KVOLinkObject1 objectsInRealm:self.realm where:@"TRUEPREDICATE"]];
  1236. AssertIndexChange(NSKeyValueChangeRemoval, ([NSIndexSet indexSetWithIndexesInRange:{0, 3}]));
  1237. }
  1238. - (void)testObserveInvalidArrayProperty {
  1239. KVOObject *obj = [self createObject];
  1240. RLMArray *array = obj.objectArray;
  1241. XCTAssertThrows([array addObserver:self forKeyPath:@"self" options:0 context:0]);
  1242. XCTAssertNoThrow([array addObserver:self forKeyPath:RLMInvalidatedKey options:0 context:0]);
  1243. XCTAssertNoThrow([array removeObserver:self forKeyPath:RLMInvalidatedKey context:0]);
  1244. }
  1245. - (void)testInvalidOperationOnObservedArray {
  1246. KVOLinkObject2 *obj = [self createLinkObject];
  1247. KVOLinkObject1 *linked = obj.obj;
  1248. [obj.array addObject:linked];
  1249. KVORecorder r(self, obj, @"array");
  1250. XCTAssertThrows([obj.array exchangeObjectAtIndex:2 withObjectAtIndex:3]);
  1251. // A KVO notification is still sent to observers on the same thread since we
  1252. // can't cancel willChange, but the data is not very meaningful so don't check it
  1253. if (!self.collapsesNotifications) {
  1254. AssertNotification(r);
  1255. }
  1256. }
  1257. @end
  1258. // Mutate a different accessor backed by the same row as the accessor being observed
  1259. @interface KVOMultipleAccessorsTests : KVOManagedObjectTests
  1260. @end
  1261. @implementation KVOMultipleAccessorsTests
  1262. - (id)observableForObject:(id)value {
  1263. if (RLMObject *obj = RLMDynamicCast<RLMObject>(value)) {
  1264. RLMObject *copy = RLMCreateManagedAccessor(obj.objectSchema.accessorClass, obj.realm, obj->_info);
  1265. copy->_row = obj->_row;
  1266. return copy;
  1267. }
  1268. else if (RLMArray *array = RLMDynamicCast<RLMArray>(value)) {
  1269. return array;
  1270. }
  1271. else {
  1272. XCTFail(@"unsupported type");
  1273. return nil;
  1274. }
  1275. }
  1276. - (void)testIgnoredProperty {
  1277. // ignored properties do not notify other accessors for the same row
  1278. }
  1279. - (void)testAddOrUpdate {
  1280. KVOObject *obj = [self createObject];
  1281. KVOObject *obj2 = [[KVOObject alloc] initWithValue:obj];
  1282. KVORecorder r(self, obj, @"boolCol");
  1283. obj2.boolCol = true;
  1284. XCTAssertTrue(r.empty());
  1285. [self.realm addOrUpdateObject:obj2];
  1286. AssertChanged(r, @NO, @YES);
  1287. }
  1288. - (void)testCreateOrUpdate {
  1289. KVOObject *obj = [self createObject];
  1290. KVOObject *obj2 = [[KVOObject alloc] initWithValue:obj];
  1291. KVORecorder r(self, obj, @"boolCol");
  1292. obj2.boolCol = true;
  1293. XCTAssertTrue(r.empty());
  1294. [KVOObject createOrUpdateInRealm:self.realm withValue:obj2];
  1295. AssertChanged(r, @NO, @YES);
  1296. }
  1297. // The following tests aren't really multiple-accessor-specific, but they're
  1298. // conceptually similar and don't make sense in the multiple realm instances case
  1299. - (void)testCancelWriteTransactionWhileObservingNewObject {
  1300. KVOObject *obj = [self createObject];
  1301. KVORecorder r(self, obj, RLMInvalidatedKey);
  1302. KVORecorder r2(self, obj, @"boolCol");
  1303. [self.realm cancelWriteTransaction];
  1304. AssertChanged(r, @NO, @YES);
  1305. r2.pop_front();
  1306. [self.realm beginWriteTransaction];
  1307. }
  1308. - (void)testCancelWriteTransactionWhileObservingChangedProperty {
  1309. KVOObject *obj = [self createObject];
  1310. [self.realm commitWriteTransaction];
  1311. [self.realm beginWriteTransaction];
  1312. obj.boolCol = YES;
  1313. KVORecorder r(self, obj, @"boolCol");
  1314. [self.realm cancelWriteTransaction];
  1315. AssertChanged(r, @YES, @NO);
  1316. [self.realm beginWriteTransaction];
  1317. }
  1318. - (void)testCancelWriteTransactionWhileObservingLinkToExistingObject {
  1319. KVOObject *obj = [self createObject];
  1320. KVOObject *obj2 = [self createObject];
  1321. [self.realm commitWriteTransaction];
  1322. [self.realm beginWriteTransaction];
  1323. obj.objectCol = obj2;
  1324. KVORecorder r(self, obj, @"objectCol");
  1325. [self.realm cancelWriteTransaction];
  1326. AssertChanged(r, obj2, NSNull.null);
  1327. [self.realm beginWriteTransaction];
  1328. }
  1329. - (void)testCancelWriteTransactionWhileObservingLinkToNewObject {
  1330. KVOObject *obj = [self createObject];
  1331. [self.realm commitWriteTransaction];
  1332. [self.realm beginWriteTransaction];
  1333. obj.objectCol = [self createObject];
  1334. KVORecorder r(self, obj, @"objectCol");
  1335. [self.realm cancelWriteTransaction];
  1336. if (NSDictionary *note = AssertNotification(r)) {
  1337. XCTAssertTrue([note[NSKeyValueChangeOldKey] isKindOfClass:[RLMObjectBase class]]);
  1338. XCTAssertEqualObjects(note[NSKeyValueChangeNewKey], NSNull.null);
  1339. }
  1340. [self.realm beginWriteTransaction];
  1341. }
  1342. - (void)testCancelWriteTransactionWhileObservingNewObjectLinkingToNewObject {
  1343. KVOObject *obj = [self createObject];
  1344. obj.objectCol = [self createObject];
  1345. KVORecorder r(self, obj, RLMInvalidatedKey);
  1346. KVORecorder r2(self, obj, @"objectCol");
  1347. KVORecorder r3(self, obj, @"objectCol.boolCol");
  1348. [self.realm cancelWriteTransaction];
  1349. AssertChanged(r, @NO, @YES);
  1350. [self.realm beginWriteTransaction];
  1351. }
  1352. - (void)testCancelWriteWithArrayChanges {
  1353. KVOObject *obj = [self createObject];
  1354. [obj.objectArray addObject:obj];
  1355. [self.realm commitWriteTransaction];
  1356. [self.realm beginWriteTransaction];
  1357. {
  1358. [obj.objectArray addObject:obj];
  1359. KVORecorder r(self, obj, @"objectArray");
  1360. [self.realm cancelWriteTransaction];
  1361. [self.realm beginWriteTransaction];
  1362. AssertIndexChange(NSKeyValueChangeRemoval, [NSIndexSet indexSetWithIndex:1]);
  1363. }
  1364. {
  1365. [obj.objectArray removeLastObject];
  1366. KVORecorder r(self, obj, @"objectArray");
  1367. [self.realm cancelWriteTransaction];
  1368. [self.realm beginWriteTransaction];
  1369. AssertIndexChange(NSKeyValueChangeInsertion, [NSIndexSet indexSetWithIndex:0]);
  1370. }
  1371. {
  1372. obj.objectArray[0] = obj;
  1373. KVORecorder r(self, obj, @"objectArray");
  1374. [self.realm cancelWriteTransaction];
  1375. [self.realm beginWriteTransaction];
  1376. AssertIndexChange(NSKeyValueChangeReplacement, [NSIndexSet indexSetWithIndex:0]);
  1377. }
  1378. // test batching with multiple items changed
  1379. [obj.objectArray addObject:obj];
  1380. [self.realm commitWriteTransaction];
  1381. [self.realm beginWriteTransaction];
  1382. {
  1383. [obj.objectArray removeAllObjects];
  1384. KVORecorder r(self, obj, @"objectArray");
  1385. [self.realm cancelWriteTransaction];
  1386. [self.realm beginWriteTransaction];
  1387. AssertIndexChange(NSKeyValueChangeInsertion, ([NSIndexSet indexSetWithIndexesInRange:{0, 2}]));
  1388. }
  1389. {
  1390. [obj.objectArray removeLastObject];
  1391. [obj.objectArray removeLastObject];
  1392. KVORecorder r(self, obj, @"objectArray");
  1393. [self.realm cancelWriteTransaction];
  1394. [self.realm beginWriteTransaction];
  1395. AssertIndexChange(NSKeyValueChangeInsertion, ([NSIndexSet indexSetWithIndexesInRange:{0, 2}]));
  1396. }
  1397. {
  1398. [obj.objectArray insertObject:obj atIndex:1];
  1399. [obj.objectArray insertObject:obj atIndex:0];
  1400. KVORecorder r(self, obj, @"objectArray");
  1401. [self.realm cancelWriteTransaction];
  1402. [self.realm beginWriteTransaction];
  1403. NSMutableIndexSet *expected = [NSMutableIndexSet new];
  1404. [expected addIndex:0];
  1405. [expected addIndex:2]; // shifted due to inserting at 0 after 1
  1406. AssertIndexChange(NSKeyValueChangeRemoval, expected);
  1407. }
  1408. {
  1409. [obj.objectArray insertObject:obj atIndex:0];
  1410. [obj.objectArray removeLastObject];
  1411. KVORecorder r(self, obj, @"objectArray");
  1412. [self.realm cancelWriteTransaction];
  1413. [self.realm beginWriteTransaction];
  1414. AssertChanged(r, obj.objectArray, obj.objectArray);
  1415. }
  1416. }
  1417. - (void)testCancelWriteWithPrimitiveArrayChanges {
  1418. KVOObject *obj = [self createObject];
  1419. [obj.intArray addObject:@1];
  1420. [self.realm commitWriteTransaction];
  1421. [self.realm beginWriteTransaction];
  1422. {
  1423. [obj.intArray addObject:@2];
  1424. KVORecorder r(self, obj, @"intArray");
  1425. [self.realm cancelWriteTransaction];
  1426. [self.realm beginWriteTransaction];
  1427. AssertIndexChange(NSKeyValueChangeRemoval, [NSIndexSet indexSetWithIndex:1]);
  1428. }
  1429. {
  1430. [obj.intArray removeLastObject];
  1431. KVORecorder r(self, obj, @"intArray");
  1432. [self.realm cancelWriteTransaction];
  1433. [self.realm beginWriteTransaction];
  1434. AssertIndexChange(NSKeyValueChangeInsertion, [NSIndexSet indexSetWithIndex:0]);
  1435. }
  1436. {
  1437. obj.intArray[0] = @3;
  1438. KVORecorder r(self, obj, @"intArray");
  1439. [self.realm cancelWriteTransaction];
  1440. [self.realm beginWriteTransaction];
  1441. AssertIndexChange(NSKeyValueChangeReplacement, [NSIndexSet indexSetWithIndex:0]);
  1442. }
  1443. // test batching with multiple items changed
  1444. [obj.intArray addObject:@4];
  1445. [self.realm commitWriteTransaction];
  1446. [self.realm beginWriteTransaction];
  1447. {
  1448. [obj.intArray removeAllObjects];
  1449. KVORecorder r(self, obj, @"intArray");
  1450. [self.realm cancelWriteTransaction];
  1451. [self.realm beginWriteTransaction];
  1452. AssertIndexChange(NSKeyValueChangeInsertion, ([NSIndexSet indexSetWithIndexesInRange:{0, 2}]));
  1453. }
  1454. {
  1455. [obj.intArray removeLastObject];
  1456. [obj.intArray removeLastObject];
  1457. KVORecorder r(self, obj, @"intArray");
  1458. [self.realm cancelWriteTransaction];
  1459. [self.realm beginWriteTransaction];
  1460. AssertIndexChange(NSKeyValueChangeInsertion, ([NSIndexSet indexSetWithIndexesInRange:{0, 2}]));
  1461. }
  1462. {
  1463. [obj.intArray insertObject:@5 atIndex:1];
  1464. [obj.intArray insertObject:@6 atIndex:0];
  1465. KVORecorder r(self, obj, @"intArray");
  1466. [self.realm cancelWriteTransaction];
  1467. [self.realm beginWriteTransaction];
  1468. NSMutableIndexSet *expected = [NSMutableIndexSet new];
  1469. [expected addIndex:0];
  1470. [expected addIndex:2]; // shifted due to inserting at 0 after 1
  1471. AssertIndexChange(NSKeyValueChangeRemoval, expected);
  1472. }
  1473. {
  1474. [obj.intArray insertObject:@7 atIndex:0];
  1475. [obj.intArray removeLastObject];
  1476. KVORecorder r(self, obj, @"intArray");
  1477. [self.realm cancelWriteTransaction];
  1478. [self.realm beginWriteTransaction];
  1479. AssertChanged(r, obj.intArray, obj.intArray);
  1480. }
  1481. }
  1482. - (void)testCancelWriteWithLinkedObjectedRemoved {
  1483. KVOLinkObject2 *obj = [self createLinkObject];
  1484. [obj.array addObject:obj.obj];
  1485. [self.realm commitWriteTransaction];
  1486. [self.realm beginWriteTransaction];
  1487. {
  1488. [self.realm deleteObject:obj.obj];
  1489. KVORecorder r(self, obj, @"array");
  1490. KVORecorder r2(self, obj, @"obj");
  1491. [self.realm cancelWriteTransaction];
  1492. [self.realm beginWriteTransaction];
  1493. AssertIndexChange(NSKeyValueChangeInsertion, [NSIndexSet indexSetWithIndex:0]);
  1494. AssertChanged(r2, NSNull.null, [KVOLinkObject1 allObjectsInRealm:self.realm].firstObject);
  1495. }
  1496. {
  1497. [self.realm deleteObjects:[KVOLinkObject1 allObjectsInRealm:self.realm]];
  1498. KVORecorder r(self, obj, @"array");
  1499. KVORecorder r2(self, obj, @"obj");
  1500. [self.realm cancelWriteTransaction];
  1501. [self.realm beginWriteTransaction];
  1502. AssertIndexChange(NSKeyValueChangeInsertion, [NSIndexSet indexSetWithIndex:0]);
  1503. AssertChanged(r2, NSNull.null, [KVOLinkObject1 allObjectsInRealm:self.realm].firstObject);
  1504. }
  1505. {
  1506. [self.realm deleteObjects:obj.array];
  1507. KVORecorder r(self, obj, @"array");
  1508. KVORecorder r2(self, obj, @"obj");
  1509. [self.realm cancelWriteTransaction];
  1510. [self.realm beginWriteTransaction];
  1511. AssertIndexChange(NSKeyValueChangeInsertion, [NSIndexSet indexSetWithIndex:0]);
  1512. AssertChanged(r2, NSNull.null, [KVOLinkObject1 allObjectsInRealm:self.realm].firstObject);
  1513. }
  1514. }
  1515. - (void)testInvalidateRealm {
  1516. KVOObject *obj = [self createObject];
  1517. [self.realm commitWriteTransaction];
  1518. KVORecorder r1(self, obj, RLMInvalidatedKey);
  1519. KVORecorder r2(self, obj, @"objectArray.invalidated");
  1520. [self.realm invalidate];
  1521. [self.realm beginWriteTransaction];
  1522. AssertChanged(r1, @NO, @YES);
  1523. AssertChanged(r2, @NO, @YES);
  1524. }
  1525. - (void)testRenamedProperties {
  1526. auto obj = [RenamedProperties1 createInRealm:self.realm withValue:@[@1, @"a"]];
  1527. [self.realm commitWriteTransaction];
  1528. [self.realm beginWriteTransaction];
  1529. KVORecorder r(self, obj, @"propA");
  1530. obj.propA = 2;
  1531. AssertChanged(r, @1, @2);
  1532. obj[@"propA"] = @3;
  1533. AssertChanged(r, @2, @3);
  1534. [obj setValue:@4 forKey:@"propA"];
  1535. AssertChanged(r, @3, @4);
  1536. // Only rollback will notify objects of different types with the same table,
  1537. // not direct modification. Probably not worth fixing this.
  1538. RenamedProperties2 *obj2 = [RenamedProperties2 allObjectsInRealm:self.realm].firstObject;
  1539. KVORecorder r2(self, obj2, @"propC");
  1540. [self.realm cancelWriteTransaction];
  1541. [self.realm beginWriteTransaction];
  1542. AssertChanged(r, @4, @1);
  1543. AssertChanged(r2, @4, @1);
  1544. }
  1545. @end
  1546. // Observing an object from a different RLMRealm instance backed by the same
  1547. // row as the managed object being mutated
  1548. @interface KVOMultipleRealmsTests : KVOManagedObjectTests
  1549. @property RLMRealm *secondaryRealm;
  1550. @end
  1551. @implementation KVOMultipleRealmsTests
  1552. - (void)setUp {
  1553. [super setUp];
  1554. RLMRealmConfiguration *config = self.realm.configuration;
  1555. config.cache = false;
  1556. self.secondaryRealm = [RLMRealm realmWithConfiguration:config error:nil];
  1557. }
  1558. - (void)tearDown {
  1559. self.secondaryRealm = nil;
  1560. [super tearDown];
  1561. }
  1562. - (id)observableForObject:(id)value {
  1563. [self.realm commitWriteTransaction];
  1564. [self.realm beginWriteTransaction];
  1565. [self.secondaryRealm refresh];
  1566. if (RLMObject *obj = RLMDynamicCast<RLMObject>(value)) {
  1567. RLMObject *copy = RLMCreateManagedAccessor(obj.objectSchema.accessorClass, self.secondaryRealm,
  1568. &self.secondaryRealm->_info[obj.objectSchema.className]);
  1569. copy->_row = (*copy->_info->table())[obj->_row.get_index()];
  1570. return copy;
  1571. }
  1572. else if (RLMArray *array = RLMDynamicCast<RLMArray>(value)) {
  1573. return array;
  1574. }
  1575. else {
  1576. XCTFail(@"unsupported type");
  1577. return nil;
  1578. }
  1579. }
  1580. - (bool)collapsesNotifications {
  1581. return true;
  1582. }
  1583. - (void)testIgnoredProperty {
  1584. // ignored properties do not notify other accessors for the same row
  1585. }
  1586. - (void)testBatchArrayChanges {
  1587. KVOObject *obj = [self createObject];
  1588. [obj.objectArray addObject:obj];
  1589. [obj.objectArray addObject:obj];
  1590. [obj.objectArray addObject:obj];
  1591. {
  1592. KVORecorder r(self, obj, @"objectArray");
  1593. [obj.objectArray insertObject:obj atIndex:1];
  1594. [obj.objectArray insertObject:obj atIndex:0];
  1595. NSMutableIndexSet *expected = [NSMutableIndexSet new];
  1596. [expected addIndex:0];
  1597. [expected addIndex:2]; // shifted due to inserting at 0 after 1
  1598. AssertIndexChange(NSKeyValueChangeInsertion, expected);
  1599. }
  1600. {
  1601. KVORecorder r(self, obj, @"objectArray");
  1602. [obj.objectArray removeObjectAtIndex:3];
  1603. [obj.objectArray removeObjectAtIndex:3];
  1604. AssertIndexChange(NSKeyValueChangeRemoval, ([NSIndexSet indexSetWithIndexesInRange:{3, 2}]));
  1605. }
  1606. {
  1607. KVORecorder r(self, obj, @"objectArray");
  1608. [obj.objectArray removeObjectAtIndex:0];
  1609. [obj.objectArray removeAllObjects];
  1610. AssertIndexChange(NSKeyValueChangeRemoval, ([NSIndexSet indexSetWithIndexesInRange:{0, 3}]));
  1611. }
  1612. [obj.objectArray addObject:obj];
  1613. {
  1614. KVORecorder r(self, obj, @"objectArray");
  1615. [obj.objectArray addObject:obj];
  1616. [obj.objectArray removeAllObjects];
  1617. AssertIndexChange(NSKeyValueChangeRemoval, [NSIndexSet indexSetWithIndex:0]);
  1618. }
  1619. [obj.objectArray addObject:obj];
  1620. {
  1621. KVORecorder r(self, obj, @"objectArray");
  1622. obj.objectArray[0] = obj;
  1623. [obj.objectArray removeAllObjects];
  1624. AssertIndexChange(NSKeyValueChangeRemoval, [NSIndexSet indexSetWithIndex:0]);
  1625. }
  1626. }
  1627. - (void)testOrderedErase {
  1628. NSMutableArray *objects = [NSMutableArray arrayWithCapacity:10];
  1629. for (int i = 0; i < 10; ++i) @autoreleasepool {
  1630. [objects addObject:[ObjectWithNoLinksToOrFrom createInRealm:self.realm withValue:@[@(i)]]];
  1631. }
  1632. // deleteObject: always uses move_last_over(), but TableView::clear() uses
  1633. // erase() if there's no links
  1634. auto deleteObject = ^(int value) {
  1635. [self.realm deleteObjects:[ObjectWithNoLinksToOrFrom objectsInRealm:self.realm where:@"value = %d", value]];
  1636. };
  1637. { // delete object before observed, then observed
  1638. KVORecorder r(self, objects[2], @"invalidated");
  1639. deleteObject(1);
  1640. deleteObject(2);
  1641. AssertChanged(r, @NO, @YES);
  1642. }
  1643. { // delete object after observed, then observed
  1644. KVORecorder r(self, objects[3], @"invalidated");
  1645. deleteObject(4);
  1646. deleteObject(3);
  1647. AssertChanged(r, @NO, @YES);
  1648. }
  1649. { // delete observed, then object before observed
  1650. KVORecorder r(self, objects[6], @"invalidated");
  1651. deleteObject(6);
  1652. deleteObject(5);
  1653. AssertChanged(r, @NO, @YES);
  1654. }
  1655. { // delete observed, then object after observed
  1656. KVORecorder r(self, objects[7], @"invalidated");
  1657. deleteObject(7);
  1658. deleteObject(8);
  1659. AssertChanged(r, @NO, @YES);
  1660. }
  1661. }
  1662. - (void)testInsertNewTables {
  1663. KVOObject *obj = [self createObject];
  1664. KVORecorder r1(self, obj, @"boolCol");
  1665. KVORecorder r2(self, obj, @"int32Col");
  1666. obj.boolCol = YES;
  1667. // Add tables before the observed one so that the observed one's index changes
  1668. realm::Group &group = self.realm->_realm->read_group();
  1669. realm::TableRef table1 = group.insert_table(5, "new table");
  1670. realm::TableRef table2 = group.insert_table(0, "new table 2");
  1671. table1->add_column(realm::type_Int, "col");
  1672. table2->add_column(realm::type_Int, "col");
  1673. obj.int32Col = 3;
  1674. AssertChanged(r1, @NO, @YES);
  1675. AssertChanged(r2, @2, @3);
  1676. }
  1677. - (void)testInsertNewColumns {
  1678. KVOObject *obj = [self createObject];
  1679. KVORecorder r1(self, obj, @"boolCol");
  1680. KVORecorder r2(self, obj, @"int32Col");
  1681. auto ndx = obj->_info->tableColumn(@"int32Col");
  1682. // Add a column before the observed one so that the observed one's index changes
  1683. obj.boolCol = YES;
  1684. auto& table = *obj->_info->table();
  1685. table.insert_column(0, realm::type_Binary, "new col");
  1686. table.insert_column(ndx, realm::type_Binary, "new col 2");
  1687. obj->_row.set_int(ndx + 2, 3); // can't use the accessor after a local schema change
  1688. AssertChanged(r1, @NO, @YES);
  1689. AssertChanged(r2, @2, @3);
  1690. }
  1691. - (void)testShiftObservedColumnBeforeChange {
  1692. KVOObject *obj = [self createObject];
  1693. auto ndx = obj->_info->tableColumn(@"boolCol");
  1694. KVORecorder r(self, obj, @"boolCol");
  1695. obj->_info->table()->insert_column(0, realm::type_Binary, "new col");
  1696. obj->_row.set_bool(ndx + 1, true); // can't use the accessor after a local schema change
  1697. AssertChanged(r, @NO, @YES);
  1698. }
  1699. - (void)testShiftObservedColumnAfterChange {
  1700. KVOObject *obj = [self createObject];
  1701. KVORecorder r(self, obj, @"boolCol");
  1702. obj.boolCol = YES;
  1703. obj->_info->table()->insert_column(0, realm::type_Binary, "new col");
  1704. AssertChanged(r, @NO, @YES);
  1705. }
  1706. - (void)testSwapRowsIsNotAChange {
  1707. KVOObject *obj = [self createObject];
  1708. [self createObject];
  1709. KVORecorder r(self, obj, @"boolCol");
  1710. obj->_info->table()->swap_rows(0, 1);
  1711. r.refresh();
  1712. XCTAssertTrue(r.empty());
  1713. }
  1714. - (void)testSwapRowsBeforeChange {
  1715. KVOObject *obj = [self createObject];
  1716. [self createObject];
  1717. KVORecorder r(self, obj, @"boolCol");
  1718. obj->_info->table()->swap_rows(0, 1);
  1719. obj.boolCol = YES;
  1720. AssertChanged(r, @NO, @YES);
  1721. }
  1722. - (void)testSwapRowsAfterChange {
  1723. KVOObject *obj = [self createObject];
  1724. [self createObject];
  1725. KVORecorder r(self, obj, @"boolCol");
  1726. obj.boolCol = YES;
  1727. obj->_info->table()->swap_rows(0, 1);
  1728. AssertChanged(r, @NO, @YES);
  1729. }
  1730. - (void)testSwapRowsBeforeArrayChange {
  1731. KVOObject *obj = [self createObject];
  1732. [self createObject];
  1733. KVORecorder r(self, obj, @"objectArray");
  1734. obj->_info->table()->swap_rows(0, 1);
  1735. [obj.objectArray addObject:obj];
  1736. AssertIndexChange(NSKeyValueChangeInsertion, [NSIndexSet indexSetWithIndex:0]);
  1737. }
  1738. - (void)testSwapRowsAfterArrayChange {
  1739. KVOObject *obj = [self createObject];
  1740. [self createObject];
  1741. KVORecorder r(self, obj, @"objectArray");
  1742. [obj.objectArray addObject:obj];
  1743. obj->_info->table()->swap_rows(0, 1);
  1744. AssertIndexChange(NSKeyValueChangeInsertion, [NSIndexSet indexSetWithIndex:0]);
  1745. }
  1746. @end
  1747. // Test with the table column order not matching the order of the properties
  1748. @interface KVOManagedObjectWithReorderedPropertiesTests : KVOManagedObjectTests
  1749. @end
  1750. @implementation KVOManagedObjectWithReorderedPropertiesTests
  1751. - (RLMRealm *)getRealm {
  1752. // Initialize the file with the properties in reverse order, then re-open
  1753. // with it in the normal order while the reversed one is still open (as
  1754. // otherwise it'll recreate the file due to being in-memory)
  1755. RLMSchema *schema = [RLMSchema new];
  1756. schema.objectSchema = @[[self reverseProperties:KVOObject.sharedSchema],
  1757. [self reverseProperties:KVOLinkObject1.sharedSchema],
  1758. [self reverseProperties:KVOLinkObject2.sharedSchema]];
  1759. RLMRealmConfiguration *configuration = [[RLMRealmConfiguration alloc] init];
  1760. configuration.cache = false;
  1761. configuration.inMemoryIdentifier = @"test";
  1762. configuration.customSchema = schema;
  1763. RLMRealm *reversedRealm = [RLMRealm realmWithConfiguration:configuration error:nil];
  1764. configuration.customSchema = nil;
  1765. RLMRealm *realm = [RLMRealm realmWithConfiguration:configuration error:nil];
  1766. XCTAssertNotEqualObjects(realm.schema, reversedRealm.schema);
  1767. return realm;
  1768. }
  1769. - (RLMObjectSchema *)reverseProperties:(RLMObjectSchema *)source {
  1770. RLMObjectSchema *objectSchema = [source copy];
  1771. objectSchema.properties = objectSchema.properties.reverseObjectEnumerator.allObjects;
  1772. return objectSchema;
  1773. }
  1774. @end