RealmTests.mm 83 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209
  1. ////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2014 Realm Inc.
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License");
  6. // you may not use this file except in compliance with the License.
  7. // You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. //
  17. ////////////////////////////////////////////////////////////////////////////
  18. #import "RLMTestCase.h"
  19. #import "RLMObjectSchema_Private.hpp"
  20. #import "RLMRealmConfiguration_Private.hpp"
  21. #import "RLMRealmUtil.hpp"
  22. #import "RLMRealm_Dynamic.h"
  23. #import "RLMRealm_Private.hpp"
  24. #import "RLMSchema_Private.h"
  25. #import <mach/mach_init.h>
  26. #import <mach/vm_map.h>
  27. #import <sys/resource.h>
  28. #import <thread>
  29. #import <realm/util/file.hpp>
  30. #import <realm/db_options.hpp>
  31. @interface RLMRealm ()
  32. + (BOOL)isCoreDebug;
  33. @end
  34. @interface RLMObjectSchema (Private)
  35. + (instancetype)schemaForObjectClass:(Class)objectClass;
  36. @property (nonatomic, readwrite, assign) Class objectClass;
  37. @end
  38. @interface RLMSchema (Private)
  39. @property (nonatomic, readwrite, copy) NSArray *objectSchema;
  40. @end
  41. @interface RealmTests : RLMTestCase
  42. @end
  43. @implementation RealmTests
  44. - (void)deleteFiles {
  45. [super deleteFiles];
  46. for (NSString *realmPath in self.pathsFor100Realms) {
  47. [self deleteRealmFileAtURL:[NSURL fileURLWithPath:realmPath]];
  48. }
  49. }
  50. #pragma mark - Opening Realms
  51. - (void)testOpeningInvalidPathThrows {
  52. RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
  53. config.fileURL = [NSURL fileURLWithPath:@"/dev/null/foo"];
  54. RLMAssertThrowsWithCodeMatching([RLMRealm realmWithConfiguration:config error:nil], RLMErrorFileAccess);
  55. }
  56. - (void)testPathCannotBeBothInMemoryAndRegularDurability {
  57. RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
  58. config.inMemoryIdentifier = @"identifier";
  59. RLMRealm *inMemoryRealm = [RLMRealm realmWithConfiguration:config error:nil];
  60. // make sure we can't open disk-realm at same path
  61. config.fileURL = [NSURL fileURLWithPath:@(inMemoryRealm.configuration.config.path.c_str())];
  62. NSError *error; // passing in a reference to assert that this error can't be catched!
  63. RLMAssertThrowsWithReasonMatching([RLMRealm realmWithConfiguration:config error:&error], @"Realm at path '.*' already opened with different inMemory settings");
  64. }
  65. - (void)testRealmWithPathUsesDefaultConfiguration {
  66. RLMRealmConfiguration *originalDefaultConfiguration = [RLMRealmConfiguration defaultConfiguration];
  67. RLMRealmConfiguration *newDefaultConfiguration = [originalDefaultConfiguration copy];
  68. newDefaultConfiguration.objectClasses = @[];
  69. [RLMRealmConfiguration setDefaultConfiguration:newDefaultConfiguration];
  70. XCTAssertEqual([[[[RLMRealm realmWithURL:RLMTestRealmURL()] configuration] objectClasses] count], 0U);
  71. [RLMRealmConfiguration setDefaultConfiguration:originalDefaultConfiguration];
  72. }
  73. - (void)testReadOnlyFile {
  74. @autoreleasepool {
  75. RLMRealm *realm = self.realmWithTestPath;
  76. [realm beginWriteTransaction];
  77. [StringObject createInRealm:realm withValue:@[@"a"]];
  78. [realm commitWriteTransaction];
  79. }
  80. [NSFileManager.defaultManager setAttributes:@{NSFileImmutable: @YES} ofItemAtPath:RLMTestRealmURL().path error:nil];
  81. // Should not be able to open read-write
  82. RLMAssertThrowsWithCodeMatching([self realmWithTestPath], RLMErrorFileAccess);
  83. RLMRealm *realm;
  84. XCTAssertNoThrow(realm = [self readOnlyRealmWithURL:RLMTestRealmURL() error:nil]);
  85. XCTAssertEqual(1U, [StringObject allObjectsInRealm:realm].count);
  86. [NSFileManager.defaultManager setAttributes:@{NSFileImmutable: @NO} ofItemAtPath:RLMTestRealmURL().path error:nil];
  87. }
  88. - (void)testReadOnlyFileInImmutableDirectory {
  89. @autoreleasepool {
  90. RLMRealm *realm = self.realmWithTestPath;
  91. [realm beginWriteTransaction];
  92. [StringObject createInRealm:realm withValue:@[@"a"]];
  93. [realm commitWriteTransaction];
  94. }
  95. // Delete '*.lock' and '.note' files to simulate opening Realm in an app bundle
  96. [[NSFileManager defaultManager] removeItemAtURL:[RLMTestRealmURL() URLByAppendingPathExtension:@"lock"] error:nil];
  97. [[NSFileManager defaultManager] removeItemAtURL:[RLMTestRealmURL() URLByAppendingPathExtension:@"note"] error:nil];
  98. // Make parent directory immutable to simulate opening Realm in an app bundle
  99. NSURL *parentDirectoryOfTestRealmURL = [RLMTestRealmURL() URLByDeletingLastPathComponent];
  100. [NSFileManager.defaultManager setAttributes:@{NSFileImmutable: @YES} ofItemAtPath:parentDirectoryOfTestRealmURL.path error:nil];
  101. RLMRealm *realm;
  102. // Read-only Realm should be opened even in immutable directory
  103. XCTAssertNoThrow(realm = [self readOnlyRealmWithURL:RLMTestRealmURL() error:nil]);
  104. [self dispatchAsyncAndWait:^{ XCTAssertNoThrow([self readOnlyRealmWithURL:RLMTestRealmURL() error:nil]); }];
  105. [NSFileManager.defaultManager setAttributes:@{NSFileImmutable: @NO} ofItemAtPath:parentDirectoryOfTestRealmURL.path error:nil];
  106. }
  107. - (void)testReadOnlyRealmMustExist {
  108. RLMAssertThrowsWithCodeMatching([self readOnlyRealmWithURL:RLMTestRealmURL() error:nil], RLMErrorFileNotFound);
  109. }
  110. - (void)testCannotHaveReadOnlyAndReadWriteRealmsAtSamePathAtSameTime {
  111. NSString *exceptionReason = @"Realm at path '.*' already opened with different read permissions";
  112. @autoreleasepool {
  113. XCTAssertNoThrow([self realmWithTestPath]);
  114. RLMAssertThrowsWithReasonMatching([self readOnlyRealmWithURL:RLMTestRealmURL() error:nil], exceptionReason);
  115. }
  116. @autoreleasepool {
  117. XCTAssertNoThrow([self readOnlyRealmWithURL:RLMTestRealmURL() error:nil]);
  118. RLMAssertThrowsWithReasonMatching([self realmWithTestPath], exceptionReason);
  119. }
  120. [self dispatchAsyncAndWait:^{
  121. XCTAssertNoThrow([self readOnlyRealmWithURL:RLMTestRealmURL() error:nil]);
  122. RLMAssertThrowsWithReasonMatching([self realmWithTestPath], exceptionReason);
  123. }];
  124. }
  125. - (void)testCanOpenReadOnlyOnMulitpleThreadsAtOnce {
  126. @autoreleasepool {
  127. RLMRealm *realm = self.realmWithTestPath;
  128. [realm beginWriteTransaction];
  129. [StringObject createInRealm:realm withValue:@[@"a"]];
  130. [realm commitWriteTransaction];
  131. }
  132. RLMRealm *realm = [self readOnlyRealmWithURL:RLMTestRealmURL() error:nil];
  133. XCTAssertEqual(1U, [StringObject allObjectsInRealm:realm].count);
  134. [self dispatchAsyncAndWait:^{
  135. RLMRealm *realm = [self readOnlyRealmWithURL:RLMTestRealmURL() error:nil];
  136. XCTAssertEqual(1U, [StringObject allObjectsInRealm:realm].count);
  137. }];
  138. // Verify that closing the other RLMRealm didn't manage to break anything
  139. XCTAssertEqual(1U, [StringObject allObjectsInRealm:realm].count);
  140. }
  141. - (void)testFilePermissionDenied {
  142. @autoreleasepool {
  143. XCTAssertNoThrow([self realmWithTestPath]);
  144. }
  145. // Make Realm at test path temporarily unreadable
  146. NSError *error;
  147. NSNumber *permissions = [NSFileManager.defaultManager attributesOfItemAtPath:RLMTestRealmURL().path error:&error][NSFilePosixPermissions];
  148. assert(!error);
  149. [NSFileManager.defaultManager setAttributes:@{NSFilePosixPermissions: @(0000)} ofItemAtPath:RLMTestRealmURL().path error:&error];
  150. assert(!error);
  151. RLMAssertThrowsWithCodeMatching([self realmWithTestPath], RLMErrorFilePermissionDenied);
  152. [NSFileManager.defaultManager setAttributes:@{NSFilePosixPermissions: permissions} ofItemAtPath:RLMTestRealmURL().path error:&error];
  153. assert(!error);
  154. }
  155. // Check that the data for file was left unchanged when opened with upgrading
  156. // disabled, but allow expanding the file to the page size
  157. #define AssertFileUnmodified(oldURL, newURL) do { \
  158. NSData *oldData = [NSData dataWithContentsOfURL:oldURL]; \
  159. NSData *newData = [NSData dataWithContentsOfURL:newURL]; \
  160. if (oldData.length < realm::util::page_size()) { \
  161. XCTAssertEqual(newData.length, realm::util::page_size()); \
  162. XCTAssertNotEqual(([newData rangeOfData:oldData options:0 range:{0, oldData.length}]).location, NSNotFound); \
  163. } \
  164. else \
  165. XCTAssertEqualObjects(oldData, newData); \
  166. } while (0)
  167. #if 0 // FIXME: replace with migration from core 5 files
  168. - (void)testFileFormatUpgradeRequiredDeleteRealmIfNeeded {
  169. RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
  170. config.deleteRealmIfMigrationNeeded = YES;
  171. NSURL *bundledRealmURL = [[NSBundle bundleForClass:[RealmTests class]] URLForResource:@"fileformat-pre-null" withExtension:@"realm"];
  172. [NSFileManager.defaultManager copyItemAtURL:bundledRealmURL toURL:config.fileURL error:nil];
  173. @autoreleasepool {
  174. XCTAssertTrue([[RLMRealm realmWithConfiguration:config error:nil] isEmpty]);
  175. }
  176. bundledRealmURL = [[NSBundle bundleForClass:[RealmTests class]] URLForResource:@"fileformat-old-date" withExtension:@"realm"];
  177. [NSFileManager.defaultManager removeItemAtURL:config.fileURL error:nil];
  178. [NSFileManager.defaultManager copyItemAtURL:bundledRealmURL toURL:config.fileURL error:nil];
  179. @autoreleasepool {
  180. XCTAssertTrue([[RLMRealm realmWithConfiguration:config error:nil] isEmpty]);
  181. }
  182. }
  183. - (void)testFileFormatUpgradeRequiredButDisabled {
  184. RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
  185. config.disableFormatUpgrade = true;
  186. NSURL *bundledRealmURL = [[NSBundle bundleForClass:[RealmTests class]] URLForResource:@"fileformat-pre-null" withExtension:@"realm"];
  187. [NSFileManager.defaultManager copyItemAtURL:bundledRealmURL toURL:config.fileURL error:nil];
  188. RLMAssertThrowsWithCodeMatching([RLMRealm realmWithConfiguration:config error:nil],
  189. RLMErrorFileFormatUpgradeRequired);
  190. AssertFileUnmodified(bundledRealmURL, config.fileURL);
  191. bundledRealmURL = [[NSBundle bundleForClass:[RealmTests class]] URLForResource:@"fileformat-old-date" withExtension:@"realm"];
  192. [NSFileManager.defaultManager removeItemAtURL:config.fileURL error:nil];
  193. [NSFileManager.defaultManager copyItemAtURL:bundledRealmURL toURL:config.fileURL error:nil];
  194. RLMAssertThrowsWithCodeMatching([RLMRealm realmWithConfiguration:config error:nil],
  195. RLMErrorFileFormatUpgradeRequired);
  196. AssertFileUnmodified(bundledRealmURL, config.fileURL);
  197. }
  198. - (void)testFileFormatUpgradeRequiredButReadOnly {
  199. RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
  200. config.readOnly = true;
  201. NSURL *bundledRealmURL = [[NSBundle bundleForClass:[RealmTests class]] URLForResource:@"fileformat-pre-null" withExtension:@"realm"];
  202. [NSFileManager.defaultManager copyItemAtURL:bundledRealmURL toURL:config.fileURL error:nil];
  203. RLMAssertThrowsWithCodeMatching([RLMRealm realmWithConfiguration:config error:nil], RLMErrorFileAccess);
  204. XCTAssertEqualObjects([NSData dataWithContentsOfURL:bundledRealmURL],
  205. [NSData dataWithContentsOfURL:config.fileURL]);
  206. bundledRealmURL = [[NSBundle bundleForClass:[RealmTests class]] URLForResource:@"fileformat-old-date" withExtension:@"realm"];
  207. [NSFileManager.defaultManager removeItemAtURL:config.fileURL error:nil];
  208. [NSFileManager.defaultManager copyItemAtURL:bundledRealmURL toURL:config.fileURL error:nil];
  209. RLMAssertThrowsWithCodeMatching([RLMRealm realmWithConfiguration:config error:nil], RLMErrorFileAccess);
  210. XCTAssertEqualObjects([NSData dataWithContentsOfURL:bundledRealmURL],
  211. [NSData dataWithContentsOfURL:config.fileURL]);
  212. }
  213. #endif // REALM_SPM
  214. #if TARGET_OS_IPHONE && (!TARGET_IPHONE_SIMULATOR || !TARGET_RT_64_BIT)
  215. - (void)testExceedingVirtualAddressSpace {
  216. RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
  217. const NSUInteger stringLength = 1024 * 1024;
  218. void *mem = calloc(stringLength, '1');
  219. NSString *largeString = [[NSString alloc] initWithBytesNoCopy:mem
  220. length:stringLength
  221. encoding:NSUTF8StringEncoding
  222. freeWhenDone:YES];
  223. @autoreleasepool {
  224. RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:nil];
  225. [realm beginWriteTransaction];
  226. StringObject *stringObj = [StringObject new];
  227. stringObj.stringCol = largeString;
  228. [realm addObject:stringObj];
  229. [realm commitWriteTransaction];
  230. }
  231. struct VirtualMemoryChunk {
  232. vm_address_t address;
  233. vm_size_t size;
  234. };
  235. std::vector<VirtualMemoryChunk> allocatedChunks;
  236. NSUInteger size = 1024 * 1024 * 1024;
  237. while (size >= stringLength) {
  238. VirtualMemoryChunk chunk { .size = size };
  239. kern_return_t ret = vm_allocate(mach_task_self(), &chunk.address, chunk.size,
  240. VM_FLAGS_ANYWHERE);
  241. if (ret == KERN_NO_SPACE) {
  242. size /= 2;
  243. } else {
  244. allocatedChunks.push_back(chunk);
  245. }
  246. }
  247. @autoreleasepool {
  248. RLMAssertThrowsWithCodeMatching([RLMRealm realmWithConfiguration:config error:nil], RLMErrorAddressSpaceExhausted);
  249. }
  250. for (auto chunk : allocatedChunks) {
  251. kern_return_t ret = vm_deallocate(mach_task_self(), chunk.address, chunk.size);
  252. assert(ret == KERN_SUCCESS);
  253. }
  254. @autoreleasepool {
  255. XCTAssertNoThrow([RLMRealm realmWithConfiguration:config error:nil]);
  256. }
  257. }
  258. #endif
  259. - (void)testOpenAsync {
  260. // Locals
  261. RLMRealmConfiguration *c = [RLMRealmConfiguration defaultConfiguration];
  262. XCTestExpectation *ex = [self expectationWithDescription:@"open-async"];
  263. // Helpers
  264. auto assertNoCachedRealm = ^{ XCTAssertNil(RLMGetAnyCachedRealmForPath(c.pathOnDisk.UTF8String)); };
  265. auto fileExists = ^BOOL() {
  266. return [[NSFileManager defaultManager] fileExistsAtPath:c.pathOnDisk isDirectory:nil];
  267. };
  268. // Unsuccessful open
  269. c.readOnly = true;
  270. [RLMRealm asyncOpenWithConfiguration:c
  271. callbackQueue:dispatch_get_main_queue()
  272. callback:^(RLMRealm * _Nullable realm, NSError * _Nullable error) {
  273. XCTAssertEqual(error.code, RLMErrorFileNotFound);
  274. XCTAssertNil(realm);
  275. [ex fulfill];
  276. }];
  277. XCTAssertFalse(fileExists());
  278. assertNoCachedRealm();
  279. [self waitForExpectationsWithTimeout:1 handler:nil];
  280. XCTAssertFalse(fileExists());
  281. assertNoCachedRealm();
  282. // Successful open
  283. c.readOnly = false;
  284. ex = [self expectationWithDescription:@"open-async"];
  285. // Hold exclusive lock on lock file to prevent Realm from being created
  286. // if the dispatch_async happens too quickly
  287. NSString *lockFilePath = [c.pathOnDisk stringByAppendingString:@".lock"];
  288. [[NSFileManager defaultManager] createFileAtPath:lockFilePath contents:[NSData data] attributes:nil];
  289. int fd = open(lockFilePath.UTF8String, O_RDWR);
  290. XCTAssertNotEqual(-1, fd);
  291. int ret = flock(fd, LOCK_SH);
  292. XCTAssertEqual(0, ret);
  293. [RLMRealm asyncOpenWithConfiguration:c
  294. callbackQueue:dispatch_get_main_queue()
  295. callback:^(RLMRealm * _Nullable realm, NSError * _Nullable error) {
  296. XCTAssertNil(error);
  297. XCTAssertNotNil(realm);
  298. [ex fulfill];
  299. }];
  300. XCTAssertFalse(fileExists());
  301. flock(fd, LOCK_UN);
  302. close(fd);
  303. assertNoCachedRealm();
  304. [self waitForExpectationsWithTimeout:1 handler:nil];
  305. XCTAssertTrue(fileExists());
  306. assertNoCachedRealm();
  307. }
  308. #pragma mark - Adding and Removing Objects
  309. - (void)testRealmAddAndRemoveObjects {
  310. RLMRealm *realm = [self realmWithTestPath];
  311. [realm beginWriteTransaction];
  312. [StringObject createInRealm:realm withValue:@[@"a"]];
  313. [StringObject createInRealm:realm withValue:@[@"b"]];
  314. [StringObject createInRealm:realm withValue:@[@"c"]];
  315. XCTAssertEqual([StringObject objectsInRealm:realm withPredicate:nil].count, 3U, @"Expecting 3 objects");
  316. [realm commitWriteTransaction];
  317. // test again after write transaction
  318. RLMResults *objects = [StringObject allObjectsInRealm:realm];
  319. XCTAssertEqual(objects.count, 3U, @"Expecting 3 objects");
  320. XCTAssertEqualObjects([objects.firstObject stringCol], @"a", @"Expecting column to be 'a'");
  321. [realm beginWriteTransaction];
  322. [realm deleteObject:objects[2]];
  323. [realm deleteObject:objects[0]];
  324. XCTAssertEqual([StringObject objectsInRealm:realm withPredicate:nil].count, 1U, @"Expecting 1 object");
  325. [realm commitWriteTransaction];
  326. objects = [StringObject allObjectsInRealm:realm];
  327. XCTAssertEqual(objects.count, 1U, @"Expecting 1 object");
  328. XCTAssertEqualObjects([objects.firstObject stringCol], @"b", @"Expecting column to be 'b'");
  329. }
  330. - (void)testRemoveUnmanagedObject {
  331. RLMRealm *realm = [self realmWithTestPath];
  332. StringObject *obj = [[StringObject alloc] initWithValue:@[@"a"]];
  333. [realm beginWriteTransaction];
  334. XCTAssertThrows([realm deleteObject:obj]);
  335. obj = [StringObject createInRealm:realm withValue:@[@"b"]];
  336. [realm commitWriteTransaction];
  337. [self waitForNotification:RLMRealmDidChangeNotification realm:realm block:^{
  338. RLMRealm *realm = [self realmWithTestPath];
  339. RLMObject *obj = [[StringObject allObjectsInRealm:realm] firstObject];
  340. [realm beginWriteTransaction];
  341. [realm deleteObject:obj];
  342. XCTAssertThrows([realm deleteObject:obj]);
  343. [realm commitWriteTransaction];
  344. }];
  345. [realm beginWriteTransaction];
  346. [realm deleteObject:obj];
  347. [realm commitWriteTransaction];
  348. }
  349. - (void)testRealmBatchRemoveObjects {
  350. RLMRealm *realm = [self realmWithTestPath];
  351. [realm beginWriteTransaction];
  352. StringObject *strObj = [StringObject createInRealm:realm withValue:@[@"a"]];
  353. [StringObject createInRealm:realm withValue:@[@"b"]];
  354. [StringObject createInRealm:realm withValue:@[@"c"]];
  355. [realm commitWriteTransaction];
  356. // delete objects
  357. RLMResults *objects = [StringObject allObjectsInRealm:realm];
  358. XCTAssertEqual(objects.count, 3U, @"Expecting 3 objects");
  359. [realm beginWriteTransaction];
  360. [realm deleteObjects:[StringObject objectsInRealm:realm where:@"stringCol != 'a'"]];
  361. XCTAssertEqual([[StringObject allObjectsInRealm:realm] count], 1U, @"Expecting 0 objects");
  362. [realm deleteObjects:objects];
  363. XCTAssertEqual([[StringObject allObjectsInRealm:realm] count], 0U, @"Expecting 0 objects");
  364. [realm commitWriteTransaction];
  365. XCTAssertEqual([[StringObject allObjectsInRealm:realm] count], 0U, @"Expecting 0 objects");
  366. XCTAssertThrows(strObj.stringCol, @"Object should be invalidated");
  367. // add objects to linkView
  368. [realm beginWriteTransaction];
  369. ArrayPropertyObject *obj = [ArrayPropertyObject createInRealm:realm withValue:@[@"name", @[@[@"a"], @[@"b"], @[@"c"]], @[]]];
  370. [StringObject createInRealm:realm withValue:@[@"d"]];
  371. [realm commitWriteTransaction];
  372. XCTAssertEqual([[StringObject allObjectsInRealm:realm] count], 4U, @"Expecting 4 objects");
  373. // remove from linkView
  374. [realm beginWriteTransaction];
  375. [realm deleteObjects:obj.array];
  376. [realm commitWriteTransaction];
  377. XCTAssertEqual([[StringObject allObjectsInRealm:realm] count], 1U, @"Expecting 1 object");
  378. XCTAssertEqual(obj.array.count, 0U, @"Expecting 0 objects");
  379. // remove NSArray
  380. NSArray *arrayOfLastObject = @[[[StringObject allObjectsInRealm:realm] lastObject]];
  381. [realm beginWriteTransaction];
  382. [realm deleteObjects:arrayOfLastObject];
  383. [realm commitWriteTransaction];
  384. XCTAssertEqual(objects.count, 0U, @"Expecting 0 objects");
  385. // add objects to linkView
  386. [realm beginWriteTransaction];
  387. [obj.array addObject:[StringObject createInRealm:realm withValue:@[@"a"]]];
  388. [obj.array addObject:[[StringObject alloc] initWithValue:@[@"b"]]];
  389. [realm commitWriteTransaction];
  390. // remove objects from realm
  391. XCTAssertEqual(obj.array.count, 2U, @"Expecting 2 objects");
  392. [realm beginWriteTransaction];
  393. [realm deleteObjects:[StringObject allObjectsInRealm:realm]];
  394. [realm commitWriteTransaction];
  395. XCTAssertEqual(obj.array.count, 0U, @"Expecting 0 objects");
  396. }
  397. - (void)testAddManagedObjectToOtherRealm {
  398. RLMRealm *realm1 = [self realmWithTestPath];
  399. RLMRealm *realm2 = [RLMRealm defaultRealm];
  400. CircleObject *co1 = [[CircleObject alloc] init];
  401. co1.data = @"1";
  402. CircleObject *co2 = [[CircleObject alloc] init];
  403. co2.data = @"2";
  404. co2.next = co1;
  405. CircleArrayObject *cao = [[CircleArrayObject alloc] init];
  406. [cao.circles addObject:co1];
  407. [realm1 transactionWithBlock:^{ [realm1 addObject:co1]; }];
  408. [realm2 beginWriteTransaction];
  409. XCTAssertThrows([realm2 addObject:co1], @"should reject already-managed object");
  410. XCTAssertThrows([realm2 addObject:co2], @"should reject linked managed object");
  411. XCTAssertThrows([realm2 addObject:cao], @"should reject array containing managed object");
  412. [realm2 commitWriteTransaction];
  413. // The objects are left in an odd state if validation fails (since the
  414. // exception isn't supposed to be recoverable), so make new objects
  415. co2 = [[CircleObject alloc] init];
  416. co2.data = @"2";
  417. co2.next = co1;
  418. cao = [[CircleArrayObject alloc] init];
  419. [cao.circles addObject:co1];
  420. [realm1 beginWriteTransaction];
  421. XCTAssertNoThrow([realm1 addObject:co2],
  422. @"should be able to add object which links to object managed by target Realm");
  423. XCTAssertNoThrow([realm1 addObject:cao],
  424. @"should be able to add object with an array containing an object managed by target Realm");
  425. [realm1 commitWriteTransaction];
  426. }
  427. - (void)testCopyObjectsBetweenRealms {
  428. RLMRealm *realm1 = [self realmWithTestPath];
  429. RLMRealm *realm2 = [RLMRealm defaultRealm];
  430. StringObject *so = [[StringObject alloc] init];
  431. so.stringCol = @"value";
  432. [realm1 beginWriteTransaction];
  433. [realm1 addObject:so];
  434. [realm1 commitWriteTransaction];
  435. XCTAssertEqual(1U, [StringObject allObjectsInRealm:realm1].count);
  436. XCTAssertEqual(0U, [StringObject allObjectsInRealm:realm2].count);
  437. XCTAssertEqualObjects(so.stringCol, @"value");
  438. [realm2 beginWriteTransaction];
  439. StringObject *so2 = [StringObject createInRealm:realm2 withValue:so];
  440. [realm2 commitWriteTransaction];
  441. XCTAssertEqual(1U, [StringObject allObjectsInRealm:realm1].count);
  442. XCTAssertEqual(1U, [StringObject allObjectsInRealm:realm2].count);
  443. XCTAssertEqualObjects(so2.stringCol, @"value");
  444. }
  445. - (void)testCopyArrayPropertyBetweenRealms {
  446. RLMRealm *realm1 = [self realmWithTestPath];
  447. RLMRealm *realm2 = [RLMRealm defaultRealm];
  448. EmployeeObject *eo = [[EmployeeObject alloc] init];
  449. eo.name = @"name";
  450. eo.age = 50;
  451. eo.hired = YES;
  452. CompanyObject *co = [[CompanyObject alloc] init];
  453. co.name = @"company name";
  454. [co.employees addObject:eo];
  455. [realm1 beginWriteTransaction];
  456. [realm1 addObject:co];
  457. [realm1 commitWriteTransaction];
  458. XCTAssertEqual(1U, [EmployeeObject allObjectsInRealm:realm1].count);
  459. XCTAssertEqual(1U, [CompanyObject allObjectsInRealm:realm1].count);
  460. [realm2 beginWriteTransaction];
  461. CompanyObject *co2 = [CompanyObject createInRealm:realm2 withValue:co];
  462. [realm2 commitWriteTransaction];
  463. XCTAssertEqual(1U, [EmployeeObject allObjectsInRealm:realm1].count);
  464. XCTAssertEqual(1U, [CompanyObject allObjectsInRealm:realm1].count);
  465. XCTAssertEqual(1U, [EmployeeObject allObjectsInRealm:realm2].count);
  466. XCTAssertEqual(1U, [CompanyObject allObjectsInRealm:realm2].count);
  467. XCTAssertEqualObjects(@"name", [co2.employees.firstObject name]);
  468. }
  469. - (void)testCopyLinksBetweenRealms {
  470. RLMRealm *realm1 = [self realmWithTestPath];
  471. RLMRealm *realm2 = [RLMRealm defaultRealm];
  472. CircleObject *c = [[CircleObject alloc] init];
  473. c.data = @"1";
  474. c.next = [[CircleObject alloc] init];
  475. c.next.data = @"2";
  476. [realm1 beginWriteTransaction];
  477. [realm1 addObject:c];
  478. [realm1 commitWriteTransaction];
  479. XCTAssertEqual(realm1, c.realm);
  480. XCTAssertEqual(realm1, c.next.realm);
  481. XCTAssertEqual(2U, [CircleObject allObjectsInRealm:realm1].count);
  482. [realm2 beginWriteTransaction];
  483. CircleObject *c2 = [CircleObject createInRealm:realm2 withValue:c];
  484. [realm2 commitWriteTransaction];
  485. XCTAssertEqualObjects(c2.data, @"1");
  486. XCTAssertEqualObjects(c2.next.data, @"2");
  487. XCTAssertEqual(2U, [CircleObject allObjectsInRealm:realm1].count);
  488. XCTAssertEqual(2U, [CircleObject allObjectsInRealm:realm2].count);
  489. }
  490. - (void)testCopyObjectsInArrayLiteral {
  491. RLMRealm *realm1 = [self realmWithTestPath];
  492. RLMRealm *realm2 = [RLMRealm defaultRealm];
  493. CircleObject *c = [[CircleObject alloc] init];
  494. c.data = @"1";
  495. [realm1 beginWriteTransaction];
  496. [realm1 addObject:c];
  497. [realm1 commitWriteTransaction];
  498. [realm2 beginWriteTransaction];
  499. CircleObject *c2 = [CircleObject createInRealm:realm2 withValue:@[@"3", @[@"2", c]]];
  500. [realm2 commitWriteTransaction];
  501. XCTAssertEqual(1U, [CircleObject allObjectsInRealm:realm1].count);
  502. XCTAssertEqual(3U, [CircleObject allObjectsInRealm:realm2].count);
  503. XCTAssertEqual(realm1, c.realm);
  504. XCTAssertEqual(realm2, c2.realm);
  505. XCTAssertEqualObjects(@"1", c.data);
  506. XCTAssertEqualObjects(@"3", c2.data);
  507. XCTAssertEqualObjects(@"2", c2.next.data);
  508. XCTAssertEqualObjects(@"1", c2.next.next.data);
  509. }
  510. - (void)testAddOrUpdate {
  511. RLMRealm *realm = [RLMRealm defaultRealm];
  512. [realm beginWriteTransaction];
  513. PrimaryStringObject *obj = [[PrimaryStringObject alloc] initWithValue:@[@"string", @1]];
  514. [realm addOrUpdateObject:obj];
  515. RLMResults *objects = [PrimaryStringObject allObjects];
  516. XCTAssertEqual([objects count], 1U, @"Should have 1 object");
  517. XCTAssertEqual([(PrimaryStringObject *)objects[0] intCol], 1, @"Value should be 1");
  518. PrimaryStringObject *obj2 = [[PrimaryStringObject alloc] initWithValue:@[@"string2", @2]];
  519. [realm addOrUpdateObject:obj2];
  520. XCTAssertEqual([objects count], 2U, @"Should have 2 objects");
  521. // upsert with new secondary property
  522. PrimaryStringObject *obj3 = [[PrimaryStringObject alloc] initWithValue:@[@"string", @3]];
  523. [realm addOrUpdateObject:obj3];
  524. XCTAssertEqual([objects count], 2U, @"Should have 2 objects");
  525. XCTAssertEqual([(PrimaryStringObject *)objects[0] intCol], 3, @"Value should be 3");
  526. // upsert on non-primary key object should throw
  527. XCTAssertThrows([realm addOrUpdateObject:[[StringObject alloc] initWithValue:@[@"string"]]]);
  528. [realm commitWriteTransaction];
  529. }
  530. - (void)testAddOrUpdateObjectsFromArray {
  531. RLMRealm *realm = [RLMRealm defaultRealm];
  532. [realm beginWriteTransaction];
  533. PrimaryStringObject *obj = [[PrimaryStringObject alloc] initWithValue:@[@"string1", @1]];
  534. [realm addObject:obj];
  535. PrimaryStringObject *obj2 = [[PrimaryStringObject alloc] initWithValue:@[@"string2", @2]];
  536. [realm addObject:obj2];
  537. PrimaryStringObject *obj3 = [[PrimaryStringObject alloc] initWithValue:@[@"string3", @3]];
  538. [realm addObject:obj3];
  539. RLMResults *objects = [PrimaryStringObject allObjects];
  540. XCTAssertEqual([objects count], 3U);
  541. XCTAssertEqual(obj.intCol, 1);
  542. XCTAssertEqual(obj2.intCol, 2);
  543. XCTAssertEqual(obj3.intCol, 3);
  544. // upsert with array of 2 objects. One is to update the existing value, another is added
  545. NSArray *array = @[[[PrimaryStringObject alloc] initWithValue:@[@"string2", @4]],
  546. [[PrimaryStringObject alloc] initWithValue:@[@"string4", @5]]];
  547. [realm addOrUpdateObjects:array];
  548. XCTAssertEqual([objects count], 4U, @"Should have 4 objects");
  549. XCTAssertEqual(obj.intCol, 1);
  550. XCTAssertEqual(obj2.intCol, 4);
  551. XCTAssertEqual(obj3.intCol, 3);
  552. XCTAssertEqual([array[1] intCol], 5);
  553. [realm commitWriteTransaction];
  554. }
  555. - (void)testDelete {
  556. RLMRealm *realm = [RLMRealm defaultRealm];
  557. [realm beginWriteTransaction];
  558. OwnerObject *obj = [OwnerObject createInDefaultRealmWithValue:@[@"deeter", @[@"barney", @2]]];
  559. [realm commitWriteTransaction];
  560. XCTAssertEqual(1U, OwnerObject.allObjects.count);
  561. XCTAssertEqual(NO, obj.invalidated);
  562. XCTAssertThrows([realm deleteObject:obj]);
  563. RLMRealm *testRealm = [self realmWithTestPath];
  564. [testRealm transactionWithBlock:^{
  565. XCTAssertThrows([testRealm deleteObject:[[OwnerObject alloc] init]]);
  566. [realm transactionWithBlock:^{
  567. XCTAssertThrows([testRealm deleteObject:obj]);
  568. }];
  569. }];
  570. [realm transactionWithBlock:^{
  571. [realm deleteObject:obj];
  572. XCTAssertEqual(YES, obj.invalidated);
  573. }];
  574. XCTAssertEqual(0U, OwnerObject.allObjects.count);
  575. }
  576. - (void)testDeleteObjects {
  577. RLMRealm *realm = [RLMRealm defaultRealm];
  578. [realm beginWriteTransaction];
  579. CompanyObject *obj = [CompanyObject createInDefaultRealmWithValue:@[@"deeter", @[@[@"barney", @2, @YES]]]];
  580. NSArray *objects = @[obj];
  581. [realm commitWriteTransaction];
  582. XCTAssertEqual(1U, CompanyObject.allObjects.count);
  583. XCTAssertThrows([realm deleteObjects:objects]);
  584. XCTAssertThrows([realm deleteObjects:[CompanyObject allObjectsInRealm:realm]]);
  585. XCTAssertThrows([realm deleteObjects:obj.employees]);
  586. RLMRealm *testRealm = [self realmWithTestPath];
  587. [testRealm transactionWithBlock:^{
  588. [realm transactionWithBlock:^{
  589. XCTAssertThrows([testRealm deleteObjects:objects]);
  590. XCTAssertThrows([testRealm deleteObjects:[CompanyObject allObjectsInRealm:realm]]);
  591. XCTAssertThrows([testRealm deleteObjects:obj.employees]);
  592. }];
  593. }];
  594. XCTAssertEqual(1U, CompanyObject.allObjects.count);
  595. }
  596. - (void)testDeleteAllObjects {
  597. RLMRealm *realm = [RLMRealm defaultRealm];
  598. [realm beginWriteTransaction];
  599. OwnerObject *obj = [OwnerObject createInDefaultRealmWithValue:@[@"deeter", @[@"barney", @2]]];
  600. [realm commitWriteTransaction];
  601. XCTAssertEqual(1U, OwnerObject.allObjects.count);
  602. XCTAssertEqual(1U, DogObject.allObjects.count);
  603. XCTAssertEqual(NO, obj.invalidated);
  604. XCTAssertThrows([realm deleteAllObjects]);
  605. [realm transactionWithBlock:^{
  606. [realm deleteAllObjects];
  607. XCTAssertEqual(YES, obj.invalidated);
  608. }];
  609. XCTAssertEqual(0U, OwnerObject.allObjects.count);
  610. XCTAssertEqual(0U, DogObject.allObjects.count);
  611. }
  612. - (void)testAddObjectsFromArray
  613. {
  614. RLMRealm *realm = [self realmWithTestPath];
  615. [realm beginWriteTransaction];
  616. XCTAssertThrows(([realm addObjects:@[@[@"Rex", @10]]]),
  617. @"should reject non-RLMObject in array");
  618. DogObject *dog = [DogObject new];
  619. dog.dogName = @"Rex";
  620. dog.age = 10;
  621. XCTAssertNoThrow([realm addObjects:@[dog]], @"should allow RLMObject in array");
  622. XCTAssertEqual(1U, [[DogObject allObjectsInRealm:realm] count]);
  623. [realm cancelWriteTransaction];
  624. }
  625. #pragma mark - Transactions
  626. - (void)testRealmTransactionBlock {
  627. RLMRealm *realm = [self realmWithTestPath];
  628. [realm transactionWithBlock:^{
  629. [StringObject createInRealm:realm withValue:@[@"b"]];
  630. }];
  631. RLMResults *objects = [StringObject allObjectsInRealm:realm];
  632. XCTAssertEqual(objects.count, 1U, @"Expecting 1 object");
  633. XCTAssertEqualObjects([objects.firstObject stringCol], @"b", @"Expecting column to be 'b'");
  634. }
  635. - (void)testInWriteTransaction {
  636. RLMRealm *realm = [self realmWithTestPath];
  637. XCTAssertFalse(realm.inWriteTransaction);
  638. [realm beginWriteTransaction];
  639. XCTAssertTrue(realm.inWriteTransaction);
  640. [realm cancelWriteTransaction];
  641. [realm transactionWithBlock:^{
  642. XCTAssertTrue(realm.inWriteTransaction);
  643. [realm cancelWriteTransaction];
  644. XCTAssertFalse(realm.inWriteTransaction);
  645. }];
  646. [realm beginWriteTransaction];
  647. [realm invalidate];
  648. XCTAssertFalse(realm.inWriteTransaction);
  649. }
  650. - (void)testAutorefreshAfterBackgroundUpdate {
  651. RLMRealm *realm = [self realmWithTestPath];
  652. XCTAssertEqual(0U, [StringObject allObjectsInRealm:realm].count);
  653. [self waitForNotification:RLMRealmDidChangeNotification realm:realm block:^{
  654. RLMRealm *realm = [self realmWithTestPath];
  655. [realm beginWriteTransaction];
  656. [StringObject createInRealm:realm withValue:@[@"string"]];
  657. [realm commitWriteTransaction];
  658. }];
  659. XCTAssertEqual(1U, [StringObject allObjectsInRealm:realm].count);
  660. }
  661. - (void)testBackgroundUpdateWithoutAutorefresh {
  662. RLMRealm *realm = [self realmWithTestPath];
  663. realm.autorefresh = NO;
  664. XCTAssertEqual(0U, [StringObject allObjectsInRealm:realm].count);
  665. [self waitForNotification:RLMRealmRefreshRequiredNotification realm:realm block:^{
  666. RLMRealm *realm = [self realmWithTestPath];
  667. [realm beginWriteTransaction];
  668. [StringObject createInRealm:realm withValue:@[@"string"]];
  669. [realm commitWriteTransaction];
  670. XCTAssertEqual(1U, [StringObject allObjectsInRealm:realm].count);
  671. }];
  672. XCTAssertEqual(0U, [StringObject allObjectsInRealm:realm].count);
  673. [realm refresh];
  674. XCTAssertEqual(1U, [StringObject allObjectsInRealm:realm].count);
  675. }
  676. - (void)testBeginWriteTransactionsNotifiesWithUpdatedObjects {
  677. RLMRealm *realm = [self realmWithTestPath];
  678. realm.autorefresh = NO;
  679. XCTAssertEqual(0U, [StringObject allObjectsInRealm:realm].count);
  680. // Create an object in a background thread and wait for that to complete,
  681. // without refreshing the main thread realm
  682. [self waitForNotification:RLMRealmRefreshRequiredNotification realm:realm block:^{
  683. RLMRealm *realm = [self realmWithTestPath];
  684. [realm beginWriteTransaction];
  685. [StringObject createInRealm:realm withValue:@[@"string"]];
  686. [realm commitWriteTransaction];
  687. XCTAssertEqual(1U, [StringObject allObjectsInRealm:realm].count);
  688. }];
  689. // Verify that the main thread realm still doesn't have any objects
  690. XCTAssertEqual(0U, [StringObject allObjectsInRealm:realm].count);
  691. // Verify that the local notification sent by the beginWriteTransaction
  692. // below when it advances the realm to the latest version occurs *after*
  693. // the advance
  694. __block bool notificationFired = false;
  695. RLMNotificationToken *token = [realm addNotificationBlock:^(__unused NSString *note, RLMRealm *realm) {
  696. XCTAssertEqual(1U, [StringObject allObjectsInRealm:realm].count);
  697. notificationFired = true;
  698. }];
  699. [realm beginWriteTransaction];
  700. [realm commitWriteTransaction];
  701. [token invalidate];
  702. XCTAssertTrue(notificationFired);
  703. }
  704. - (void)testBeginWriteTransactionsRefreshesRealm {
  705. // auto refresh on by default
  706. RLMRealm *realm = [self realmWithTestPath];
  707. // Set up notification which will be triggered when calling beginWriteTransaction
  708. __block bool notificationFired = false;
  709. RLMNotificationToken *token = [realm addNotificationBlock:^(__unused NSString *note, RLMRealm *realm) {
  710. XCTAssertEqual(1U, [StringObject allObjectsInRealm:realm].count);
  711. XCTAssertThrows([realm beginWriteTransaction], @"We should already be in a write transaction");
  712. notificationFired = true;
  713. }];
  714. // dispatch to background syncronously
  715. [self dispatchAsyncAndWait:^{
  716. RLMRealm *realm = [self realmWithTestPath];
  717. [realm beginWriteTransaction];
  718. [StringObject createInRealm:realm withValue:@[@"string"]];
  719. [realm commitWriteTransaction];
  720. }];
  721. // notification shouldnt have fired
  722. XCTAssertFalse(notificationFired);
  723. [realm beginWriteTransaction];
  724. // notification should have fired
  725. XCTAssertTrue(notificationFired);
  726. [realm cancelWriteTransaction];
  727. [token invalidate];
  728. }
  729. - (void)testBeginWriteTransactionFromWithinRefreshRequiredNotification {
  730. RLMRealm *realm = [RLMRealm defaultRealm];
  731. realm.autorefresh = NO;
  732. auto expectation = [self expectationWithDescription:@""];
  733. RLMNotificationToken *token = [realm addNotificationBlock:^(NSString *note, RLMRealm *realm) {
  734. XCTAssertEqual(RLMRealmRefreshRequiredNotification, note);
  735. XCTAssertEqual(0U, [StringObject allObjectsInRealm:realm].count);
  736. [realm beginWriteTransaction];
  737. XCTAssertEqual(1U, [StringObject allObjectsInRealm:realm].count);
  738. [realm cancelWriteTransaction];
  739. [expectation fulfill]; // note that this will throw if the notification is incorrectly called twice
  740. }];
  741. [self dispatchAsyncAndWait:^{
  742. RLMRealm *realm = [RLMRealm defaultRealm];
  743. [realm beginWriteTransaction];
  744. [StringObject createInRealm:realm withValue:@[@"string"]];
  745. [realm commitWriteTransaction];
  746. }];
  747. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  748. [token invalidate];
  749. }
  750. - (void)testBeginWriteTransactionFromWithinRealmChangedNotification {
  751. RLMRealm *realm = [RLMRealm defaultRealm];
  752. auto createObject = ^{
  753. [self dispatchAsyncAndWait:^{
  754. RLMRealm *realm = [RLMRealm defaultRealm];
  755. [realm beginWriteTransaction];
  756. [StringObject createInRealm:realm withValue:@[@"string"]];
  757. [realm commitWriteTransaction];
  758. }];
  759. };
  760. // Test with the triggering transaction on a different thread
  761. auto expectation = [self expectationWithDescription:@""];
  762. RLMNotificationToken *token = [realm addNotificationBlock:^(NSString *note, RLMRealm *realm) {
  763. XCTAssertEqual(RLMRealmDidChangeNotification, note);
  764. // We're in DidChange, so the first object is already present
  765. XCTAssertEqual(1U, [StringObject allObjectsInRealm:realm].count);
  766. createObject();
  767. // Haven't refreshed yet, so still one
  768. XCTAssertEqual(1U, [StringObject allObjectsInRealm:realm].count);
  769. // Refreshes without sending notifications since we're within a notification
  770. [realm beginWriteTransaction];
  771. XCTAssertEqual(2U, [StringObject allObjectsInRealm:realm].count);
  772. [realm cancelWriteTransaction];
  773. [expectation fulfill]; // note that this will throw if the notification is incorrectly called twice
  774. }];
  775. createObject();
  776. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  777. [token invalidate];
  778. // Test with the triggering transaction on the same thread
  779. __block bool first = true;
  780. token = [realm addNotificationBlock:^(NSString *note, RLMRealm *realm) {
  781. XCTAssertTrue(first);
  782. XCTAssertEqual(RLMRealmDidChangeNotification, note);
  783. XCTAssertEqual(3U, [StringObject allObjectsInRealm:realm].count);
  784. first = false;
  785. [realm beginWriteTransaction]; // should not trigger a notification
  786. [StringObject createInRealm:realm withValue:@[@"string"]];
  787. [realm commitWriteTransaction]; // also should not trigger a notification
  788. }];
  789. [realm beginWriteTransaction];
  790. [StringObject createInRealm:realm withValue:@[@"string"]];
  791. [realm commitWriteTransaction];
  792. XCTAssertFalse(first);
  793. [token invalidate];
  794. }
  795. - (void)testBeginWriteTransactionFromWithinCollectionChangedNotification {
  796. RLMRealm *realm = [RLMRealm defaultRealm];
  797. auto createObject = ^{
  798. [self dispatchAsyncAndWait:^{
  799. RLMRealm *realm = [RLMRealm defaultRealm];
  800. [realm beginWriteTransaction];
  801. [StringObject createInRealm:realm withValue:@[@"string"]];
  802. [realm commitWriteTransaction];
  803. }];
  804. };
  805. __block auto expectation = [self expectationWithDescription:@""];
  806. __block RLMNotificationToken *token;
  807. auto block = ^(RLMResults *results, RLMCollectionChange *changes, NSError *) {
  808. if (!changes) {
  809. [expectation fulfill];
  810. return;
  811. }
  812. XCTAssertEqual(1U, results.count);
  813. createObject();
  814. XCTAssertEqual(1U, results.count);
  815. [realm beginWriteTransaction];
  816. XCTAssertEqual(2U, results.count);
  817. [realm cancelWriteTransaction];
  818. [expectation fulfill];
  819. [token invalidate];
  820. };
  821. token = [StringObject.allObjects addNotificationBlock:block];
  822. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  823. createObject();
  824. expectation = [self expectationWithDescription:@""];
  825. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  826. }
  827. - (void)testReadOnlyRealmIsImmutable
  828. {
  829. @autoreleasepool { [self realmWithTestPath]; }
  830. RLMRealm *realm = [self readOnlyRealmWithURL:RLMTestRealmURL() error:nil];
  831. XCTAssertThrows([realm beginWriteTransaction]);
  832. XCTAssertThrows([realm refresh]);
  833. }
  834. - (void)testRollbackInsert
  835. {
  836. RLMRealm *realm = [self realmWithTestPath];
  837. [realm beginWriteTransaction];
  838. IntObject *createdObject = [IntObject createInRealm:realm withValue:@[@0]];
  839. [realm cancelWriteTransaction];
  840. XCTAssertTrue(createdObject.isInvalidated);
  841. XCTAssertEqual(0U, [IntObject allObjectsInRealm:realm].count);
  842. }
  843. - (void)testRollbackDelete
  844. {
  845. RLMRealm *realm = [self realmWithTestPath];
  846. [realm beginWriteTransaction];
  847. IntObject *objectToDelete = [IntObject createInRealm:realm withValue:@[@5]];
  848. [realm commitWriteTransaction];
  849. [realm beginWriteTransaction];
  850. [realm deleteObject:objectToDelete];
  851. [realm cancelWriteTransaction];
  852. XCTAssertFalse(objectToDelete.isInvalidated);
  853. XCTAssertEqual(1U, [IntObject allObjectsInRealm:realm].count);
  854. XCTAssertEqual(5, objectToDelete.intCol);
  855. }
  856. - (void)testRollbackModify
  857. {
  858. RLMRealm *realm = [self realmWithTestPath];
  859. [realm beginWriteTransaction];
  860. IntObject *objectToModify = [IntObject createInRealm:realm withValue:@[@0]];
  861. [realm commitWriteTransaction];
  862. [realm beginWriteTransaction];
  863. objectToModify.intCol = 1;
  864. [realm cancelWriteTransaction];
  865. XCTAssertEqual(0, objectToModify.intCol);
  866. }
  867. - (void)testRollbackLink
  868. {
  869. RLMRealm *realm = [self realmWithTestPath];
  870. [realm beginWriteTransaction];
  871. CircleObject *obj1 = [CircleObject createInRealm:realm withValue:@[@"1", NSNull.null]];
  872. CircleObject *obj2 = [CircleObject createInRealm:realm withValue:@[@"2", NSNull.null]];
  873. [realm commitWriteTransaction];
  874. // Link to existing managed
  875. [realm beginWriteTransaction];
  876. obj1.next = obj2;
  877. [realm cancelWriteTransaction];
  878. XCTAssertNil(obj1.next);
  879. // Link to unmanaged
  880. [realm beginWriteTransaction];
  881. CircleObject *obj3 = [[CircleObject alloc] init];
  882. obj3.data = @"3";
  883. obj1.next = obj3;
  884. [realm cancelWriteTransaction];
  885. XCTAssertNil(obj1.next);
  886. XCTAssertEqual(2U, [CircleObject allObjectsInRealm:realm].count);
  887. // Remove link
  888. [realm beginWriteTransaction];
  889. obj1.next = obj2;
  890. [realm commitWriteTransaction];
  891. [realm beginWriteTransaction];
  892. obj1.next = nil;
  893. [realm cancelWriteTransaction];
  894. XCTAssertTrue([obj1.next isEqualToObject:obj2]);
  895. // Modify link
  896. [realm beginWriteTransaction];
  897. CircleObject *obj4 = [CircleObject createInRealm:realm withValue:@[@"4", NSNull.null]];
  898. [realm commitWriteTransaction];
  899. [realm beginWriteTransaction];
  900. obj1.next = obj4;
  901. [realm cancelWriteTransaction];
  902. XCTAssertTrue([obj1.next isEqualToObject:obj2]);
  903. }
  904. - (void)testRollbackLinkList
  905. {
  906. RLMRealm *realm = [self realmWithTestPath];
  907. [realm beginWriteTransaction];
  908. IntObject *obj1 = [IntObject createInRealm:realm withValue:@[@0]];
  909. IntObject *obj2 = [IntObject createInRealm:realm withValue:@[@1]];
  910. ArrayPropertyObject *array = [ArrayPropertyObject createInRealm:realm withValue:@[@"", @[], @[obj1]]];
  911. [realm commitWriteTransaction];
  912. // Add existing managed object
  913. [realm beginWriteTransaction];
  914. [array.intArray addObject:obj2];
  915. [realm cancelWriteTransaction];
  916. XCTAssertEqual(1U, array.intArray.count);
  917. // Add unmanaged object
  918. [realm beginWriteTransaction];
  919. [array.intArray addObject:[[IntObject alloc] init]];
  920. [realm cancelWriteTransaction];
  921. XCTAssertEqual(1U, array.intArray.count);
  922. XCTAssertEqual(2U, [IntObject allObjectsInRealm:realm].count);
  923. // Remove
  924. [realm beginWriteTransaction];
  925. [array.intArray removeObjectAtIndex:0];
  926. [realm cancelWriteTransaction];
  927. XCTAssertEqual(1U, array.intArray.count);
  928. // Modify
  929. [realm beginWriteTransaction];
  930. array.intArray[0] = obj2;
  931. [realm cancelWriteTransaction];
  932. XCTAssertEqual(1U, array.intArray.count);
  933. XCTAssertTrue([array.intArray[0] isEqualToObject:obj1]);
  934. }
  935. - (void)testRollbackTransactionWithBlock
  936. {
  937. RLMRealm *realm = [self realmWithTestPath];
  938. [realm transactionWithBlock:^{
  939. [IntObject createInRealm:realm withValue:@[@0]];
  940. [realm cancelWriteTransaction];
  941. }];
  942. XCTAssertEqual(0U, [IntObject allObjectsInRealm:realm].count);
  943. }
  944. - (void)testRollbackTransactionWithoutExplicitCommitOrCancel
  945. {
  946. @autoreleasepool {
  947. RLMRealm *realm = [self realmWithTestPath];
  948. [realm beginWriteTransaction];
  949. [IntObject createInRealm:realm withValue:@[@0]];
  950. }
  951. XCTAssertEqual(0U, [IntObject allObjectsInRealm:[self realmWithTestPath]].count);
  952. }
  953. - (void)testCanRestartReadTransactionAfterInvalidate
  954. {
  955. RLMRealm *realm = [RLMRealm defaultRealm];
  956. [realm transactionWithBlock:^{
  957. [IntObject createInRealm:realm withValue:@[@1]];
  958. }];
  959. [realm invalidate];
  960. IntObject *obj = [IntObject allObjectsInRealm:realm].firstObject;
  961. XCTAssertEqual(obj.intCol, 1);
  962. }
  963. - (void)testInvalidateDetachesAccessors
  964. {
  965. RLMRealm *realm = [RLMRealm defaultRealm];
  966. __block IntObject *obj;
  967. [realm transactionWithBlock:^{
  968. obj = [IntObject createInRealm:realm withValue:@[@0]];
  969. }];
  970. [realm invalidate];
  971. XCTAssertTrue(obj.isInvalidated);
  972. XCTAssertThrows([obj intCol]);
  973. }
  974. - (void)testInvalidateInvalidatesResults
  975. {
  976. RLMRealm *realm = [RLMRealm defaultRealm];
  977. [realm transactionWithBlock:^{
  978. [IntObject createInRealm:realm withValue:@[@1]];
  979. }];
  980. RLMResults *results = [IntObject objectsInRealm:realm where:@"intCol = 1"];
  981. XCTAssertEqual([results.firstObject intCol], 1);
  982. [realm invalidate];
  983. XCTAssertThrows([results count]);
  984. XCTAssertThrows([results firstObject]);
  985. }
  986. - (void)testInvalidateInvalidatesArrays
  987. {
  988. RLMRealm *realm = [RLMRealm defaultRealm];
  989. __block ArrayPropertyObject *arrayObject;
  990. [realm transactionWithBlock:^{
  991. arrayObject = [ArrayPropertyObject createInRealm:realm withValue:@[@"", @[], @[@[@1]]]];
  992. }];
  993. RLMArray *array = arrayObject.intArray;
  994. XCTAssertEqual(1U, array.count);
  995. [realm invalidate];
  996. XCTAssertThrows([array count]);
  997. }
  998. - (void)testInvalidateOnReadOnlyRealmIsError
  999. {
  1000. @autoreleasepool {
  1001. // Create the file
  1002. [self realmWithTestPath];
  1003. }
  1004. RLMRealm *realm = [self readOnlyRealmWithURL:RLMTestRealmURL() error:nil];
  1005. XCTAssertThrows([realm invalidate]);
  1006. }
  1007. - (void)testInvalidateBeforeReadDoesNotAssert
  1008. {
  1009. RLMRealm *realm = [RLMRealm defaultRealm];
  1010. [realm invalidate];
  1011. }
  1012. - (void)testInvalidateDuringWriteRollsBack
  1013. {
  1014. RLMRealm *realm = [RLMRealm defaultRealm];
  1015. [realm beginWriteTransaction];
  1016. @autoreleasepool {
  1017. [IntObject createInRealm:realm withValue:@[@1]];
  1018. }
  1019. [realm invalidate];
  1020. XCTAssertEqual(0U, [IntObject allObjectsInRealm:realm].count);
  1021. }
  1022. - (void)testRefreshCreatesAReadTransaction
  1023. {
  1024. RLMRealm *realm = [RLMRealm defaultRealm];
  1025. [self dispatchAsyncAndWait:^{
  1026. [RLMRealm.defaultRealm transactionWithBlock:^{
  1027. [IntObject createInDefaultRealmWithValue:@[@1]];
  1028. }];
  1029. }];
  1030. XCTAssertTrue([realm refresh]);
  1031. [self dispatchAsyncAndWait:^{
  1032. [RLMRealm.defaultRealm transactionWithBlock:^{
  1033. [IntObject createInDefaultRealmWithValue:@[@1]];
  1034. }];
  1035. }];
  1036. // refresh above should have created a read transaction, so realm should
  1037. // still only see one object
  1038. XCTAssertEqual(1U, [IntObject allObjects].count);
  1039. // Just a sanity check
  1040. XCTAssertTrue([realm refresh]);
  1041. XCTAssertEqual(2U, [IntObject allObjects].count);
  1042. }
  1043. - (void)testInWriteTransactionInNotificationFromBeginWrite {
  1044. RLMRealm *realm = RLMRealm.defaultRealm;
  1045. realm.autorefresh = NO;
  1046. __block bool called = false;
  1047. RLMNotificationToken *token = [realm addNotificationBlock:^(NSString *note, RLMRealm *realm) {
  1048. if (note == RLMRealmDidChangeNotification) {
  1049. called = true;
  1050. XCTAssertTrue(realm.inWriteTransaction);
  1051. }
  1052. }];
  1053. [self waitForNotification:RLMRealmRefreshRequiredNotification realm:realm block:^{
  1054. [RLMRealm.defaultRealm transactionWithBlock:^{ }];
  1055. }];
  1056. [realm beginWriteTransaction];
  1057. XCTAssertTrue(called);
  1058. [realm cancelWriteTransaction];
  1059. [token invalidate];
  1060. }
  1061. - (void)testThrowingFromDidChangeNotificationFromBeginWriteCancelsTransaction {
  1062. RLMRealm *realm = RLMRealm.defaultRealm;
  1063. realm.autorefresh = NO;
  1064. RLMNotificationToken *token = [realm addNotificationBlock:^(NSString *note, RLMRealm *) {
  1065. if (note == RLMRealmDidChangeNotification) {
  1066. throw 0;
  1067. }
  1068. }];
  1069. [self waitForNotification:RLMRealmRefreshRequiredNotification realm:realm block:^{
  1070. [RLMRealm.defaultRealm transactionWithBlock:^{ }];
  1071. }];
  1072. try {
  1073. [realm beginWriteTransaction];
  1074. XCTFail(@"should have thrown");
  1075. }
  1076. catch (int) { }
  1077. [token invalidate];
  1078. XCTAssertFalse(realm.inWriteTransaction);
  1079. XCTAssertNoThrow([realm beginWriteTransaction]);
  1080. [realm cancelWriteTransaction];
  1081. }
  1082. - (void)testThrowingFromDidChangeNotificationAfterLocalCommit {
  1083. RLMRealm *realm = RLMRealm.defaultRealm;
  1084. realm.autorefresh = NO;
  1085. RLMNotificationToken *token = [realm addNotificationBlock:^(NSString *note, RLMRealm *) {
  1086. if (note == RLMRealmDidChangeNotification) {
  1087. throw 0;
  1088. }
  1089. }];
  1090. [realm beginWriteTransaction];
  1091. try {
  1092. [realm commitWriteTransaction];
  1093. XCTFail(@"should have thrown");
  1094. }
  1095. catch (int) { }
  1096. [token invalidate];
  1097. XCTAssertFalse(realm.inWriteTransaction);
  1098. XCTAssertNoThrow([realm beginWriteTransaction]);
  1099. [realm cancelWriteTransaction];
  1100. }
  1101. - (void)testNotificationsFireEvenWithoutReadTransaction {
  1102. RLMRealm *realm = RLMRealm.defaultRealm;
  1103. XCTestExpectation *notificationFired = [self expectationWithDescription:@"notification fired"];
  1104. __block RLMNotificationToken *token = [realm addNotificationBlock:^(NSString *note, RLMRealm *) {
  1105. if (note == RLMRealmDidChangeNotification) {
  1106. [notificationFired fulfill];
  1107. [token invalidate];
  1108. }
  1109. }];
  1110. [realm invalidate];
  1111. [self dispatchAsync:^{
  1112. [RLMRealm.defaultRealm transactionWithBlock:^{ }];
  1113. }];
  1114. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  1115. }
  1116. - (void)testNotificationBlockMustNotBeNil {
  1117. RLMRealm *realm = RLMRealm.defaultRealm;
  1118. XCTAssertThrows([realm addNotificationBlock:self.nonLiteralNil]);
  1119. }
  1120. - (void)testRefreshInWriteTransactionReturnsFalse {
  1121. RLMRealm *realm = RLMRealm.defaultRealm;
  1122. [realm beginWriteTransaction];
  1123. [IntObject createInRealm:realm withValue:@[@0]];
  1124. XCTAssertFalse([realm refresh]);
  1125. [realm cancelWriteTransaction];
  1126. }
  1127. - (void)testCancelWriteWhenNotInWrite {
  1128. XCTAssertThrows([RLMRealm.defaultRealm cancelWriteTransaction]);
  1129. }
  1130. - (void)testActiveVersionLimit {
  1131. RLMRealmConfiguration *config = RLMRealmConfiguration.defaultConfiguration;
  1132. config.maximumNumberOfActiveVersions = 3;
  1133. RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:nil];
  1134. // Pin this version
  1135. __attribute((objc_precise_lifetime)) RLMRealm *frozen = [realm freeze];
  1136. // First 3 should work
  1137. [realm transactionWithBlock:^{ }];
  1138. [realm transactionWithBlock:^{ }];
  1139. [realm transactionWithBlock:^{ }];
  1140. XCTAssertThrows([realm beginWriteTransaction]);
  1141. XCTAssertThrows([realm transactionWithBlock:^{ }]);
  1142. NSError *error;
  1143. [realm transactionWithBlock:^{} error:&error];
  1144. XCTAssertNotNil(error);
  1145. }
  1146. #pragma mark - Threads
  1147. - (void)testCrossThreadAccess
  1148. {
  1149. RLMRealm *realm = RLMRealm.defaultRealm;
  1150. [self dispatchAsyncAndWait:^{
  1151. XCTAssertThrows([realm beginWriteTransaction]);
  1152. XCTAssertThrows([IntObject allObjectsInRealm:realm]);
  1153. XCTAssertThrows([IntObject objectsInRealm:realm where:@"intCol = 0"]);
  1154. }];
  1155. }
  1156. - (void)testHoldRealmAfterSourceThreadIsDestroyed {
  1157. RLMRealm *realm;
  1158. // Explicitly create a thread so that we can ensure the thread (and thus
  1159. // runloop) is actually destroyed
  1160. std::thread([&] { realm = [RLMRealm defaultRealm]; }).join();
  1161. [realm.configuration fileURL]; // ensure ARC releases the object after the thread has finished
  1162. }
  1163. - (void)testBackgroundRealmIsNotified {
  1164. RLMRealm *realm = [self realmWithTestPath];
  1165. XCTestExpectation *bgReady = [self expectationWithDescription:@"background queue waiting for commit"];
  1166. __block XCTestExpectation *bgDone = nil;
  1167. [self dispatchAsync:^{
  1168. RLMRealm *realm = [self realmWithTestPath];
  1169. __block bool fulfilled = false;
  1170. CFRunLoopPerformBlock(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, ^{
  1171. __block RLMNotificationToken *token = [realm addNotificationBlock:^(NSString *note, RLMRealm *realm) {
  1172. XCTAssertNotNil(realm, @"Realm should not be nil");
  1173. XCTAssertEqual(note, RLMRealmDidChangeNotification);
  1174. XCTAssertEqual(1U, [StringObject allObjectsInRealm:realm].count);
  1175. fulfilled = true;
  1176. [token invalidate];
  1177. }];
  1178. // notify main thread that we're ready for it to commit
  1179. [bgReady fulfill];
  1180. });
  1181. // run for two seconds or until we receive notification
  1182. NSDate *end = [NSDate dateWithTimeIntervalSinceNow:5.0];
  1183. while (!fulfilled) {
  1184. [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:end];
  1185. }
  1186. XCTAssertTrue(fulfilled, @"Notification should have been received");
  1187. [bgDone fulfill];
  1188. }];
  1189. // wait for background realm to be created
  1190. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  1191. bgDone = [self expectationWithDescription:@"background queue done"];;
  1192. [realm beginWriteTransaction];
  1193. [StringObject createInRealm:realm withValue:@[@"string"]];
  1194. [realm commitWriteTransaction];
  1195. [self waitForExpectationsWithTimeout:2.0 handler:nil];
  1196. }
  1197. - (void)testAddingNotificationOutsideOfRunLoopIsAnError {
  1198. [self dispatchAsyncAndWait:^{
  1199. RLMRealm *realm = RLMRealm.defaultRealm;
  1200. XCTAssertThrows([realm addNotificationBlock:^(NSString *, RLMRealm *) { }]);
  1201. CFRunLoopPerformBlock(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, ^{
  1202. RLMNotificationToken *token;
  1203. XCTAssertNoThrow(token = [realm addNotificationBlock:^(NSString *, RLMRealm *) { }]);
  1204. [token invalidate];
  1205. CFRunLoopStop(CFRunLoopGetCurrent());
  1206. });
  1207. CFRunLoopRun();
  1208. }];
  1209. }
  1210. - (void)testAddingNotificationToQueueBoundThreadOutsideOfRunLoop {
  1211. [self dispatchAsyncAndWait:^{
  1212. RLMRealm *realm = [RLMRealm defaultRealmForQueue:self.bgQueue];
  1213. XCTAssertNoThrow([realm addNotificationBlock:^(NSString *, RLMRealm *) { }]);
  1214. }];
  1215. }
  1216. - (void)testQueueBoundRealmCaching {
  1217. auto q1 = dispatch_queue_create("queue 1", DISPATCH_QUEUE_SERIAL);
  1218. auto q2 = dispatch_queue_create("queue 2", DISPATCH_QUEUE_SERIAL);
  1219. RLMRealm *mainThreadRealm1 = [RLMRealm defaultRealm];
  1220. RLMRealm *mainQueueRealm1 = [RLMRealm defaultRealmForQueue:dispatch_get_main_queue()];
  1221. __block RLMRealm *q1Realm1;
  1222. __block RLMRealm *q2Realm1;
  1223. dispatch_sync(q1, ^{ q1Realm1 = [RLMRealm defaultRealmForQueue:q1]; });
  1224. dispatch_sync(q2, ^{ q2Realm1 = [RLMRealm defaultRealmForQueue:q2]; });
  1225. XCTAssertEqual(mainQueueRealm1, mainThreadRealm1);
  1226. XCTAssertNotEqual(mainThreadRealm1, q1Realm1);
  1227. XCTAssertNotEqual(mainThreadRealm1, q2Realm1);
  1228. XCTAssertNotEqual(mainQueueRealm1, q1Realm1);
  1229. XCTAssertNotEqual(mainQueueRealm1, q2Realm1);
  1230. XCTAssertNotEqual(q1Realm1, q2Realm1);
  1231. RLMRealm *mainThreadRealm2 = [RLMRealm defaultRealm];
  1232. RLMRealm *mainQueueRealm2 = [RLMRealm defaultRealmForQueue:dispatch_get_main_queue()];
  1233. __block RLMRealm *q1Realm2;
  1234. __block RLMRealm *q2Realm2;
  1235. dispatch_sync(q1, ^{ q1Realm2 = [RLMRealm defaultRealmForQueue:q1]; });
  1236. dispatch_sync(q2, ^{ q2Realm2 = [RLMRealm defaultRealmForQueue:q2]; });
  1237. XCTAssertEqual(mainThreadRealm1, mainThreadRealm2);
  1238. XCTAssertEqual(mainQueueRealm1, mainQueueRealm2);
  1239. XCTAssertEqual(q1Realm1, q1Realm2);
  1240. XCTAssertEqual(q2Realm2, q2Realm2);
  1241. dispatch_async(q1, ^{
  1242. @autoreleasepool {
  1243. RLMRealm *backgroundThreadRealm = [RLMRealm defaultRealm];
  1244. RLMRealm *q1Realm3 = [RLMRealm defaultRealmForQueue:q1];
  1245. RLMRealm *q2Realm3 = [RLMRealm defaultRealmForQueue:q2];
  1246. XCTAssertNotEqual(backgroundThreadRealm, mainThreadRealm1);
  1247. XCTAssertNotEqual(backgroundThreadRealm, mainQueueRealm1);
  1248. XCTAssertNotEqual(backgroundThreadRealm, q1Realm1);
  1249. XCTAssertNotEqual(backgroundThreadRealm, q1Realm2);
  1250. XCTAssertEqual(q1Realm1, q1Realm3);
  1251. XCTAssertEqual(q2Realm2, q2Realm3);
  1252. }
  1253. });
  1254. dispatch_sync(q1, ^{});
  1255. }
  1256. - (void)testQueueValidation {
  1257. XCTAssertNoThrow([RLMRealm defaultRealmForQueue:dispatch_get_main_queue()]);
  1258. RLMAssertThrowsWithReason([RLMRealm defaultRealmForQueue:self.bgQueue],
  1259. @"Realm opened from incorrect dispatch queue.");
  1260. RLMAssertThrowsWithReasonMatching([RLMRealm defaultRealmForQueue:dispatch_get_global_queue(0, 0)],
  1261. @"Invalid queue '.*' \\(.*\\): Realms can only be confined to serial queues or the main queue.");
  1262. RLMAssertThrowsWithReason([RLMRealm defaultRealmForQueue:dispatch_queue_create("concurrent queue", DISPATCH_QUEUE_CONCURRENT)],
  1263. @"Invalid queue 'concurrent queue' (OS_dispatch_queue_concurrent): Realms can only be confined to serial queues or the main queue.");
  1264. dispatch_sync(self.bgQueue, ^{
  1265. XCTAssertNoThrow([RLMRealm defaultRealmForQueue:self.bgQueue]);
  1266. });
  1267. }
  1268. - (void)testQueueChecking {
  1269. auto q1 = dispatch_queue_create("queue 1", DISPATCH_QUEUE_SERIAL);
  1270. auto q2 = dispatch_queue_create("queue 2", DISPATCH_QUEUE_SERIAL);
  1271. RLMRealm *mainRealm = [RLMRealm defaultRealmForQueue:dispatch_get_main_queue()];
  1272. __block RLMRealm *q1Realm;
  1273. __block RLMRealm *q2Realm;
  1274. dispatch_sync(q1, ^{ q1Realm = [RLMRealm defaultRealmForQueue:q1]; });
  1275. dispatch_sync(q2, ^{ q2Realm = [RLMRealm defaultRealmForQueue:q2]; });
  1276. XCTAssertNoThrow([mainRealm refresh]);
  1277. RLMAssertThrowsWithReason([q1Realm refresh], @"thread");
  1278. RLMAssertThrowsWithReason([q2Realm refresh], @"thread");
  1279. dispatch_sync(q1, ^{
  1280. // dispatch_sync() doesn't change the thread and mainRealm is actually
  1281. // bound to the main thread and not the main queue
  1282. XCTAssertNoThrow([mainRealm refresh]);
  1283. XCTAssertNoThrow([q1Realm refresh]);
  1284. RLMAssertThrowsWithReason([q2Realm refresh], @"thread");
  1285. dispatch_sync(q2, ^{
  1286. XCTAssertNoThrow([mainRealm refresh]);
  1287. XCTAssertNoThrow([q2Realm refresh]);
  1288. RLMAssertThrowsWithReason([q1Realm refresh], @"thread");
  1289. });
  1290. [self dispatchAsyncAndWait:^{
  1291. RLMAssertThrowsWithReason([mainRealm refresh], @"thread");
  1292. RLMAssertThrowsWithReason([q1Realm refresh], @"thread");
  1293. RLMAssertThrowsWithReason([q2Realm refresh], @"thread");
  1294. }];
  1295. });
  1296. }
  1297. - (void)testReusingConfigOnMultipleQueues {
  1298. auto config = [RLMRealmConfiguration defaultConfiguration];
  1299. auto q1 = dispatch_queue_create("queue 1", DISPATCH_QUEUE_SERIAL);
  1300. auto q2 = dispatch_queue_create("queue 2", DISPATCH_QUEUE_SERIAL);
  1301. dispatch_sync(q1, ^{
  1302. XCTAssertNoThrow([RLMRealm realmWithConfiguration:config queue:q1 error:nil]);
  1303. });
  1304. dispatch_sync(q2, ^{
  1305. XCTAssertNoThrow([RLMRealm realmWithConfiguration:config queue:q2 error:nil]);
  1306. });
  1307. }
  1308. - (void)testConfigurationFromExistingRealmOnNewThread {
  1309. auto r1 = [RLMRealm defaultRealm];
  1310. [self dispatchAsyncAndWait:^{
  1311. auto r2 = [RLMRealm realmWithConfiguration:r1.configuration error:nil];
  1312. XCTAssertNoThrow([r2 refresh]);
  1313. }];
  1314. }
  1315. #pragma mark - In-memory Realms
  1316. - (void)testInMemoryRealm {
  1317. @autoreleasepool {
  1318. RLMRealm *inMemoryRealm = [self inMemoryRealmWithIdentifier:@"identifier"];
  1319. [self waitForNotification:RLMRealmDidChangeNotification realm:inMemoryRealm block:^{
  1320. RLMRealm *inMemoryRealm = [self inMemoryRealmWithIdentifier:@"identifier"];
  1321. [inMemoryRealm beginWriteTransaction];
  1322. [StringObject createInRealm:inMemoryRealm withValue:@[@"a"]];
  1323. [StringObject createInRealm:inMemoryRealm withValue:@[@"b"]];
  1324. [StringObject createInRealm:inMemoryRealm withValue:@[@"c"]];
  1325. XCTAssertEqual(3U, [StringObject allObjectsInRealm:inMemoryRealm].count);
  1326. [inMemoryRealm commitWriteTransaction];
  1327. }];
  1328. XCTAssertEqual(3U, [StringObject allObjectsInRealm:inMemoryRealm].count);
  1329. // make sure we can have another
  1330. RLMRealm *anotherInMemoryRealm = [self inMemoryRealmWithIdentifier:@"identifier2"];
  1331. XCTAssertEqual(0U, [StringObject allObjectsInRealm:anotherInMemoryRealm].count);
  1332. }
  1333. // Should now be empty
  1334. RLMRealm *inMemoryRealm = [self inMemoryRealmWithIdentifier:@"identifier"];
  1335. XCTAssertEqual(0U, [StringObject allObjectsInRealm:inMemoryRealm].count);
  1336. }
  1337. #pragma mark - Read-only Realms
  1338. - (void)testReadOnlyRealmWithMissingTables
  1339. {
  1340. // create a realm with only a StringObject table
  1341. @autoreleasepool {
  1342. RLMObjectSchema *objectSchema = [RLMObjectSchema schemaForObjectClass:StringObject.class];
  1343. objectSchema.objectClass = RLMObject.class;
  1344. RLMSchema *schema = [[RLMSchema alloc] init];
  1345. schema.objectSchema = @[objectSchema];
  1346. RLMRealm *realm = [self realmWithTestPathAndSchema:schema];
  1347. [realm beginWriteTransaction];
  1348. [realm createObject:StringObject.className withValue:@[@"a"]];
  1349. [realm commitWriteTransaction];
  1350. }
  1351. RLMRealm *realm = [self readOnlyRealmWithURL:RLMTestRealmURL() error:nil];
  1352. XCTAssertEqual(1U, [StringObject allObjectsInRealm:realm].count);
  1353. XCTAssertNil([PrimaryIntObject objectInRealm:realm forPrimaryKey:@0]);
  1354. // verify that reading a missing table gives an empty array rather than
  1355. // crashing
  1356. RLMResults *results = [IntObject allObjectsInRealm:realm];
  1357. XCTAssertEqual(0U, results.count);
  1358. XCTAssertEqual(results, [results objectsWhere:@"intCol = 5"]);
  1359. XCTAssertEqual(results, [results sortedResultsUsingKeyPath:@"intCol" ascending:YES]);
  1360. XCTAssertThrows([results objectAtIndex:0]);
  1361. XCTAssertEqual(NSNotFound, [results indexOfObject:self.nonLiteralNil]);
  1362. XCTAssertEqual(NSNotFound, [results indexOfObjectWhere:@"intCol = 5"]);
  1363. XCTAssertNoThrow([realm deleteObjects:results]);
  1364. XCTAssertNil([results maxOfProperty:@"intCol"]);
  1365. XCTAssertNil([results minOfProperty:@"intCol"]);
  1366. XCTAssertNil([results averageOfProperty:@"intCol"]);
  1367. XCTAssertEqualObjects(@0, [results sumOfProperty:@"intCol"]);
  1368. XCTAssertNil([results firstObject]);
  1369. XCTAssertNil([results lastObject]);
  1370. for (__unused id obj in results) {
  1371. XCTFail(@"Got an item in empty results");
  1372. }
  1373. }
  1374. - (void)testReadOnlyRealmWithMissingColumns
  1375. {
  1376. // create a realm with only a zero-column StringObject table
  1377. @autoreleasepool {
  1378. RLMObjectSchema *objectSchema = [RLMObjectSchema schemaForObjectClass:StringObject.class];
  1379. objectSchema.objectClass = RLMObject.class;
  1380. objectSchema.properties = @[];
  1381. RLMSchema *schema = [[RLMSchema alloc] init];
  1382. schema.objectSchema = @[objectSchema];
  1383. [self realmWithTestPathAndSchema:schema];
  1384. }
  1385. XCTAssertThrows([self readOnlyRealmWithURL:RLMTestRealmURL() error:nil],
  1386. @"should reject table missing column");
  1387. }
  1388. #pragma mark - Write Copy to Path
  1389. - (void)testWriteCopyOfRealm
  1390. {
  1391. RLMRealm *realm = [RLMRealm defaultRealm];
  1392. [realm transactionWithBlock:^{
  1393. [IntObject createInRealm:realm withValue:@[@0]];
  1394. }];
  1395. NSError *writeError;
  1396. XCTAssertTrue([realm writeCopyToURL:RLMTestRealmURL() encryptionKey:nil error:&writeError]);
  1397. XCTAssertNil(writeError);
  1398. RLMRealm *copy = [self realmWithTestPath];
  1399. XCTAssertEqual(1U, [IntObject allObjectsInRealm:copy].count);
  1400. }
  1401. - (void)testCannotOverwriteWithWriteCopy
  1402. {
  1403. RLMRealm *realm = [self realmWithTestPath];
  1404. [realm transactionWithBlock:^{
  1405. [IntObject createInRealm:realm withValue:@[@0]];
  1406. }];
  1407. NSError *writeError;
  1408. // Does not throw when given a nil error out param
  1409. XCTAssertFalse([realm writeCopyToURL:RLMTestRealmURL() encryptionKey:nil error:nil]);
  1410. NSString *expectedError = [NSString stringWithFormat:@"File at path '%@' already exists.", RLMTestRealmURL().path];
  1411. NSString *expectedUnderlying = [NSString stringWithFormat:@"open(\"%@\") failed: file exists", RLMTestRealmURL().path];
  1412. XCTAssertFalse([realm writeCopyToURL:RLMTestRealmURL() encryptionKey:nil error:&writeError]);
  1413. RLMValidateRealmError(writeError, RLMErrorFileExists, expectedError, expectedUnderlying);
  1414. }
  1415. - (void)testCannotWriteInNonExistentDirectory
  1416. {
  1417. RLMRealm *realm = [self realmWithTestPath];
  1418. [realm transactionWithBlock:^{
  1419. [IntObject createInRealm:realm withValue:@[@0]];
  1420. }];
  1421. NSString *badPath = @"/tmp/RLMTestDirMayNotExist/foo";
  1422. NSString *expectedError = [NSString stringWithFormat:@"Directory at path '%@' does not exist.", badPath];
  1423. NSString *expectedUnderlying = [NSString stringWithFormat:@"open(\"%@\") failed: no such file or directory", badPath];
  1424. NSError *writeError;
  1425. XCTAssertFalse([realm writeCopyToURL:[NSURL fileURLWithPath:badPath] encryptionKey:nil error:&writeError]);
  1426. RLMValidateRealmError(writeError, RLMErrorFileNotFound, expectedError, expectedUnderlying);
  1427. }
  1428. - (void)testWriteToReadOnlyDirectory
  1429. {
  1430. RLMRealm *realm = [RLMRealm defaultRealm];
  1431. // Make the parent directory temporarily read-only
  1432. NSString *directory = RLMTestRealmURL().URLByDeletingLastPathComponent.path;
  1433. NSFileManager *fm = NSFileManager.defaultManager;
  1434. NSNumber *oldPermissions = [fm attributesOfItemAtPath:directory error:nil][NSFilePosixPermissions];
  1435. [fm setAttributes:@{NSFilePosixPermissions: @(0100)} ofItemAtPath:directory error:nil];
  1436. NSString *expectedError = [NSString stringWithFormat:@"Unable to open a Realm at path '%@'. Please use a path where your app has read-write permissions.", RLMTestRealmURL().path];
  1437. NSString *expectedUnderlying = [NSString stringWithFormat:@"open(\"%@\") failed: permission denied", RLMTestRealmURL().path];
  1438. NSError *writeError;
  1439. XCTAssertFalse([realm writeCopyToURL:RLMTestRealmURL() encryptionKey:nil error:&writeError]);
  1440. RLMValidateRealmError(writeError, RLMErrorFilePermissionDenied, expectedError, expectedUnderlying);
  1441. // Restore old permissions
  1442. [fm setAttributes:@{NSFilePosixPermissions: oldPermissions} ofItemAtPath:directory error:nil];
  1443. }
  1444. - (void)testWriteWithNonSpecialCasedError
  1445. {
  1446. // Testing an open() error which doesn't have its own exception type and
  1447. // just uses the generic "something failed" error
  1448. RLMRealm *realm = [RLMRealm defaultRealm];
  1449. // Set the max open files to zero so that opening new files will fail
  1450. rlimit oldrl;
  1451. getrlimit(RLIMIT_NOFILE, &oldrl);
  1452. rlimit rl = oldrl;
  1453. rl.rlim_cur = 0;
  1454. setrlimit(RLIMIT_NOFILE, &rl);
  1455. NSString *expectedError = [NSString stringWithFormat:@"Unable to open a Realm at path '%@': open() failed: too many open files",
  1456. RLMTestRealmURL().path];
  1457. NSString *expectedUnderlying = [NSString stringWithFormat:@"open(\"%@\") failed: too many open files", RLMTestRealmURL().path];
  1458. NSError *writeError;
  1459. XCTAssertFalse([realm writeCopyToURL:RLMTestRealmURL() encryptionKey:nil error:&writeError]);
  1460. RLMValidateRealmError(writeError, RLMErrorFileAccess, expectedError, expectedUnderlying);
  1461. // Restore the old open file limit
  1462. setrlimit(RLIMIT_NOFILE, &oldrl);
  1463. }
  1464. - (void)testWritingCopyUsesWriteTransactionInProgress
  1465. {
  1466. RLMRealm *realm = [RLMRealm defaultRealm];
  1467. [realm transactionWithBlock:^{
  1468. [IntObject createInRealm:realm withValue:@[@0]];
  1469. NSError *writeError;
  1470. XCTAssertTrue([realm writeCopyToURL:RLMTestRealmURL() encryptionKey:nil error:&writeError]);
  1471. XCTAssertNil(writeError);
  1472. RLMRealm *copy = [self realmWithTestPath];
  1473. XCTAssertEqual(1U, [IntObject allObjectsInRealm:copy].count);
  1474. }];
  1475. }
  1476. #pragma mark - Frozen Realms
  1477. - (void)testIsFrozen {
  1478. RLMRealm *realm = [RLMRealm defaultRealm];
  1479. XCTAssertFalse(realm.frozen);
  1480. RLMRealm *frozenRealm = [realm freeze];
  1481. XCTAssertFalse(realm.frozen);
  1482. XCTAssertTrue(frozenRealm.frozen);
  1483. }
  1484. - (void)testRefreshFrozen {
  1485. RLMRealm *realm = [RLMRealm defaultRealm];
  1486. RLMRealm *frozenRealm = realm.freeze;
  1487. XCTAssertFalse([realm refresh]);
  1488. XCTAssertFalse([frozenRealm refresh]);
  1489. [realm transactionWithBlock:^{
  1490. [IntObject createInRealm:realm withValue:@[@0]];
  1491. }];
  1492. XCTAssertFalse([frozenRealm refresh]);
  1493. XCTAssertEqual(0U, [IntObject allObjectsInRealm:frozenRealm].count);
  1494. }
  1495. - (void)testForbiddenMethodsOnFrozenRealm {
  1496. RLMRealm *realm = [RLMRealm defaultRealm].freeze;
  1497. RLMAssertThrowsWithReason([realm setAutorefresh:YES],
  1498. @"Auto-refresh cannot be enabled for frozen Realms.");
  1499. RLMAssertThrowsWithReason([realm beginWriteTransaction],
  1500. @"Can't perform transactions on a frozen Realm");
  1501. RLMAssertThrowsWithReason([realm addNotificationBlock:^(RLMNotification, RLMRealm *) { }],
  1502. @"Frozen Realms do not change and do not have change notifications.");
  1503. RLMAssertThrowsWithReason(([[IntObject allObjectsInRealm:realm]
  1504. addNotificationBlock:^(RLMResults *, RLMCollectionChange *, NSError *) { }]),
  1505. @"Frozen Realms do not change and do not have change notifications.");
  1506. }
  1507. - (void)testFrozenRealmCaching {
  1508. RLMRealm *realm = [RLMRealm defaultRealm];
  1509. RLMRealm *fr1 = realm.freeze;
  1510. RLMRealm *fr2 = realm.freeze;
  1511. XCTAssertEqual(fr1, fr2); // note: pointer equality as it should return the same instance
  1512. [realm transactionWithBlock:^{ }];
  1513. RLMRealm *fr3 = realm.freeze;
  1514. RLMRealm *fr4 = realm.freeze;
  1515. XCTAssertEqual(fr3, fr4);
  1516. XCTAssertNotEqual(fr1, fr3);
  1517. }
  1518. - (void)testReadAfterInvalidateFrozen {
  1519. RLMRealm *realm = [RLMRealm defaultRealm].freeze;
  1520. [realm invalidate];
  1521. RLMAssertThrowsWithReason([IntObject allObjectsInRealm:realm],
  1522. @"Cannot read from a frozen Realm which has been invalidated.");
  1523. }
  1524. #pragma mark - Assorted tests
  1525. #ifndef REALM_SPM
  1526. - (void)testCoreDebug {
  1527. #if DEBUG
  1528. XCTAssertTrue([RLMRealm isCoreDebug], @"Debug version of Realm should use librealm{-ios}-dbg");
  1529. #else
  1530. XCTAssertFalse([RLMRealm isCoreDebug], @"Release version of Realm should use librealm{-ios}");
  1531. #endif
  1532. }
  1533. #endif
  1534. - (void)testIsEmpty {
  1535. RLMRealm *realm = [RLMRealm defaultRealm];
  1536. XCTAssertTrue(realm.isEmpty, @"Realm should be empty on creation.");
  1537. [realm beginWriteTransaction];
  1538. [StringObject createInRealm:realm withValue:@[@"a"]];
  1539. XCTAssertFalse(realm.isEmpty, @"Realm should not be empty within a write transaction after adding an object.");
  1540. [realm cancelWriteTransaction];
  1541. XCTAssertTrue(realm.isEmpty, @"Realm should be empty after canceling a write transaction that added an object.");
  1542. [realm beginWriteTransaction];
  1543. [StringObject createInRealm:realm withValue:@[@"a"]];
  1544. [realm commitWriteTransaction];
  1545. XCTAssertFalse(realm.isEmpty, @"Realm should not be empty after committing a write transaction that added an object.");
  1546. }
  1547. - (void)testRealmFileAccessNilPath {
  1548. RLMAssertThrowsWithReasonMatching([RLMRealm realmWithURL:self.nonLiteralNil],
  1549. @"Realm path must not be empty", @"nil path");
  1550. }
  1551. - (void)testRealmFileAccessNoExistingFile
  1552. {
  1553. NSURL *fileURL = [NSURL fileURLWithPath:RLMRealmPathForFile(@"filename.realm")];
  1554. [[NSFileManager defaultManager] removeItemAtPath:fileURL.path error:nil];
  1555. assert(![[NSFileManager defaultManager] fileExistsAtPath:fileURL.path]);
  1556. NSError *error;
  1557. RLMRealmConfiguration *configuration = [RLMRealmConfiguration defaultConfiguration];
  1558. configuration.fileURL = fileURL;
  1559. XCTAssertNotNil([RLMRealm realmWithConfiguration:configuration error:&error],
  1560. @"Database should have been created");
  1561. XCTAssertNil(error);
  1562. }
  1563. - (void)testRealmFileAccessInvalidFile
  1564. {
  1565. NSString *content = @"Some content";
  1566. NSData *fileContents = [content dataUsingEncoding:NSUTF8StringEncoding];
  1567. NSURL *fileURL = [NSURL fileURLWithPath:RLMRealmPathForFile(@"filename.realm")];
  1568. [[NSFileManager defaultManager] removeItemAtPath:fileURL.path error:nil];
  1569. assert(![[NSFileManager defaultManager] fileExistsAtPath:fileURL.path]);
  1570. [[NSFileManager defaultManager] createFileAtPath:fileURL.path contents:fileContents attributes:nil];
  1571. NSError *error;
  1572. RLMRealmConfiguration *configuration = [RLMRealmConfiguration defaultConfiguration];
  1573. configuration.fileURL = fileURL;
  1574. XCTAssertNil([RLMRealm realmWithConfiguration:configuration error:&error], @"Invalid database");
  1575. RLMValidateRealmError(error, RLMErrorFileAccess, @"Unable to open a realm at path", @"Realm file has bad size");
  1576. }
  1577. - (void)testRealmFileAccessFileIsDirectory
  1578. {
  1579. NSURL *testURL = RLMTestRealmURL();
  1580. [[NSFileManager defaultManager] createDirectoryAtPath:testURL.path
  1581. withIntermediateDirectories:NO
  1582. attributes:nil
  1583. error:nil];
  1584. NSError *error;
  1585. RLMRealmConfiguration *configuration = [RLMRealmConfiguration defaultConfiguration];
  1586. configuration.fileURL = testURL;
  1587. XCTAssertNil([RLMRealm realmWithConfiguration:configuration error:&error], @"Invalid database");
  1588. RLMValidateRealmError(error, RLMErrorFileAccess, @"Unable to open a realm at path", @"Is a directory");
  1589. }
  1590. #if TARGET_OS_TV
  1591. #else
  1592. - (void)testRealmFifoError
  1593. {
  1594. NSFileManager *manager = [NSFileManager defaultManager];
  1595. NSURL *testURL = RLMTestRealmURL();
  1596. RLMRealmConfiguration *configuration = [RLMRealmConfiguration defaultConfiguration];
  1597. configuration.fileURL = testURL;
  1598. // Create the expected fifo URL and create a directory.
  1599. // Note that creating a file when a directory with the same name exists produces a different errno, which is good.
  1600. NSURL *fifoURL = [[testURL URLByDeletingPathExtension] URLByAppendingPathExtension:@"realm.note"];
  1601. assert(![manager fileExistsAtPath:fifoURL.path]);
  1602. [manager createDirectoryAtPath:fifoURL.path withIntermediateDirectories:YES attributes:nil error:nil];
  1603. // Ensure that it doesn't try to fall back to putting it in the temp directory
  1604. auto oldTempDir = realm::DBOptions::get_sys_tmp_dir();
  1605. realm::DBOptions::set_sys_tmp_dir("");
  1606. NSError *error;
  1607. XCTAssertNil([RLMRealm realmWithConfiguration:configuration error:&error], @"Should not have been able to open FIFO");
  1608. XCTAssertNotNil(error);
  1609. RLMValidateRealmError(error, RLMErrorFileAccess, @"Is a directory", nil);
  1610. realm::DBOptions::set_sys_tmp_dir(std::move(oldTempDir));
  1611. }
  1612. #endif
  1613. - (void)testMultipleRealms
  1614. {
  1615. // Create one StringObject in two different realms
  1616. RLMRealm *defaultRealm = [RLMRealm defaultRealm];
  1617. RLMRealm *testRealm = self.realmWithTestPath;
  1618. [defaultRealm beginWriteTransaction];
  1619. [testRealm beginWriteTransaction];
  1620. [StringObject createInRealm:defaultRealm withValue:@[@"a"]];
  1621. [StringObject createInRealm:testRealm withValue:@[@"b"]];
  1622. [testRealm commitWriteTransaction];
  1623. [defaultRealm commitWriteTransaction];
  1624. // Confirm that objects were added to the correct realms
  1625. RLMResults *defaultObjects = [StringObject allObjectsInRealm:defaultRealm];
  1626. RLMResults *testObjects = [StringObject allObjectsInRealm:testRealm];
  1627. XCTAssertEqual(defaultObjects.count, 1U, @"Expecting 1 object");
  1628. XCTAssertEqual(testObjects.count, 1U, @"Expecting 1 object");
  1629. XCTAssertEqualObjects([defaultObjects.firstObject stringCol], @"a", @"Expecting column to be 'a'");
  1630. XCTAssertEqualObjects([testObjects.firstObject stringCol], @"b", @"Expecting column to be 'b'");
  1631. }
  1632. - (void)testInvalidLockFile
  1633. {
  1634. // Create the realm file and lock file
  1635. @autoreleasepool { [RLMRealm defaultRealm]; }
  1636. int fd = open([RLMRealmConfiguration.defaultConfiguration.fileURL.path stringByAppendingString:@".lock"].UTF8String, O_RDWR);
  1637. XCTAssertNotEqual(-1, fd);
  1638. // Change the value of the mutex size field in the shared info header
  1639. uint8_t value = 255;
  1640. pwrite(fd, &value, 1, 1);
  1641. // Ensure that SharedGroup can't get an exclusive lock on the lock file so
  1642. // that it can't just recreate it
  1643. int ret = flock(fd, LOCK_SH);
  1644. XCTAssertEqual(0, ret);
  1645. NSError *error;
  1646. RLMRealm *realm = [RLMRealm realmWithConfiguration:RLMRealmConfiguration.defaultConfiguration error:&error];
  1647. XCTAssertNil(realm);
  1648. RLMValidateRealmError(error, RLMErrorIncompatibleLockFile, @"Realm file is currently open in another process", nil);
  1649. flock(fd, LOCK_UN);
  1650. close(fd);
  1651. }
  1652. - (void)testCannotMigrateRealmWhenRealmIsOpen {
  1653. RLMRealm *realm = [self realmWithTestPath];
  1654. RLMRealmConfiguration *configuration = [RLMRealmConfiguration defaultConfiguration];
  1655. configuration.fileURL = realm.configuration.fileURL;
  1656. XCTAssertThrows([RLMRealm performMigrationForConfiguration:configuration error:nil]);
  1657. }
  1658. - (void)testNotificationPipeBufferOverfull {
  1659. RLMRealm *realm = [self inMemoryRealmWithIdentifier:@"test"];
  1660. // pipes have a 8 KB buffer on OS X, so verify we don't block after 8192 commits
  1661. for (int i = 0; i < 9000; ++i) {
  1662. [realm transactionWithBlock:^{}];
  1663. }
  1664. }
  1665. - (NSArray *)pathsFor100Realms
  1666. {
  1667. NSMutableArray *paths = [NSMutableArray array];
  1668. for (int i = 0; i < 100; ++i) {
  1669. NSString *realmFileName = [NSString stringWithFormat:@"test.%d.realm", i];
  1670. [paths addObject:RLMRealmPathForFile(realmFileName)];
  1671. }
  1672. return paths;
  1673. }
  1674. - (void)testCanCreate100RealmsWithoutBreakingGCD
  1675. {
  1676. NSMutableArray *realms = [NSMutableArray array];
  1677. for (NSString *realmPath in self.pathsFor100Realms) {
  1678. [realms addObject:[RLMRealm realmWithURL:[NSURL fileURLWithPath:realmPath]]];
  1679. }
  1680. XCTestExpectation *expectation = [self expectationWithDescription:@"Block dispatched to concurrent queue should be executed"];
  1681. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  1682. [expectation fulfill];
  1683. });
  1684. [self waitForExpectationsWithTimeout:1 handler:nil];
  1685. }
  1686. - (void)testAuxiliaryFilesAreExcludedFromBackup {
  1687. RLMSetSkipBackupAttribute(true);
  1688. @autoreleasepool { [RLMRealm defaultRealm]; }
  1689. #if TARGET_OS_TV
  1690. NSArray *auxiliaryFileExtensions = @[@"management", @"lock"]; // tvOS does not support named pipes
  1691. #else
  1692. NSArray *auxiliaryFileExtensions = @[@"management", @"lock", @"note"];
  1693. #endif
  1694. NSURL *fileURL = RLMRealmConfiguration.defaultConfiguration.fileURL;
  1695. for (NSString *pathExtension in auxiliaryFileExtensions) {
  1696. NSNumber *attribute = nil;
  1697. NSError *error = nil;
  1698. BOOL success = [[fileURL URLByAppendingPathExtension:pathExtension] getResourceValue:&attribute forKey:NSURLIsExcludedFromBackupKey error:&error];
  1699. XCTAssertTrue(success);
  1700. XCTAssertNil(error);
  1701. XCTAssertTrue(attribute.boolValue);
  1702. }
  1703. RLMSetSkipBackupAttribute(false);
  1704. }
  1705. - (void)testAuxiliaryFilesAreExcludedFromBackupPerformance {
  1706. RLMSetSkipBackupAttribute(true);
  1707. [self measureBlock:^{
  1708. @autoreleasepool {
  1709. RLMRealm *realm = [RLMRealm defaultRealm];
  1710. realm = [RLMRealm defaultRealm];
  1711. realm = [RLMRealm defaultRealm];
  1712. realm = [RLMRealm defaultRealm];
  1713. realm = [RLMRealm defaultRealm];
  1714. realm = [RLMRealm defaultRealm];
  1715. realm = [RLMRealm defaultRealm];
  1716. realm = [RLMRealm defaultRealm];
  1717. realm = [RLMRealm defaultRealm];
  1718. realm = [RLMRealm defaultRealm];
  1719. realm = [RLMRealm defaultRealm];
  1720. realm = [RLMRealm defaultRealm];
  1721. realm = [RLMRealm defaultRealm];
  1722. realm = [RLMRealm defaultRealm];
  1723. }
  1724. @autoreleasepool { [RLMRealm defaultRealm]; }
  1725. }];
  1726. NSURL *fileURL = RLMRealmConfiguration.defaultConfiguration.fileURL;
  1727. #if !TARGET_OS_TV
  1728. for (NSString *pathExtension in @[@"management", @"lock", @"note"]) {
  1729. #else
  1730. for (NSString *pathExtension in @[@"management", @"lock"]) {
  1731. #endif
  1732. NSNumber *attribute = nil;
  1733. NSError *error = nil;
  1734. BOOL success = [[fileURL URLByAppendingPathExtension:pathExtension] getResourceValue:&attribute forKey:NSURLIsExcludedFromBackupKey error:&error];
  1735. XCTAssertTrue(success);
  1736. XCTAssertNil(error);
  1737. XCTAssertTrue(attribute.boolValue);
  1738. }
  1739. }
  1740. - (void)testRealmExists {
  1741. RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
  1742. XCTAssertFalse([RLMRealm fileExistsForConfiguration:config]);
  1743. @autoreleasepool { [RLMRealm realmWithConfiguration:config error:nil]; }
  1744. XCTAssertTrue([RLMRealm fileExistsForConfiguration:config]);
  1745. [RLMRealm deleteFilesForConfiguration:config error:nil];
  1746. XCTAssertFalse([RLMRealm fileExistsForConfiguration:config]);
  1747. }
  1748. - (void)testDeleteNonexistentRealmFile {
  1749. NSError *error;
  1750. XCTAssertFalse([RLMRealm deleteFilesForConfiguration:RLMRealmConfiguration.defaultConfiguration error:&error]);
  1751. XCTAssertNil(error);
  1752. }
  1753. - (void)testDeleteClosedRealmFile {
  1754. RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
  1755. @autoreleasepool { [RLMRealm realmWithConfiguration:config error:nil]; }
  1756. NSError *error;
  1757. XCTAssertTrue([RLMRealm deleteFilesForConfiguration:config error:&error]);
  1758. XCTAssertNil(error);
  1759. NSFileManager *fm = NSFileManager.defaultManager;
  1760. XCTAssertTrue([fm fileExistsAtPath:[config.fileURL.path stringByAppendingPathExtension:@"lock"]]);
  1761. XCTAssertFalse([fm fileExistsAtPath:[config.fileURL.path stringByAppendingPathExtension:@"management"]]);
  1762. #if !TARGET_OS_TV
  1763. XCTAssertFalse([fm fileExistsAtPath:[config.fileURL.path stringByAppendingPathExtension:@"note"]]);
  1764. #endif
  1765. }
  1766. - (void)testDeleteRealmFileWithMissingManagementFiles {
  1767. RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
  1768. [NSFileManager.defaultManager createFileAtPath:config.fileURL.path contents:nil attributes:nil];
  1769. NSError *error;
  1770. XCTAssertTrue([RLMRealm deleteFilesForConfiguration:config error:&error]);
  1771. XCTAssertNil(error);
  1772. }
  1773. - (void)testDeleteRealmFileWithReadOnlyManagementFiles {
  1774. RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
  1775. NSFileManager *fm = NSFileManager.defaultManager;
  1776. [fm createFileAtPath:config.fileURL.path contents:nil attributes:nil];
  1777. NSString *notificationPipe = [config.fileURL.path stringByAppendingPathExtension:@"note"];
  1778. [fm createFileAtPath:notificationPipe contents:nil attributes:@{NSFileImmutable: @YES}];
  1779. NSError *error;
  1780. XCTAssertTrue([RLMRealm deleteFilesForConfiguration:config error:&error]);
  1781. XCTAssertEqual(error.code, NSFileWriteNoPermissionError);
  1782. [fm setAttributes:@{NSFileImmutable: @NO} ofItemAtPath:notificationPipe error:nil];
  1783. }
  1784. - (void)testDeleteOpenRealmFile {
  1785. RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
  1786. __attribute__((objc_precise_lifetime)) RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:nil];
  1787. NSError *error;
  1788. XCTAssertFalse([RLMRealm deleteFilesForConfiguration:config error:&error]);
  1789. XCTAssertEqual(error.code, RLMErrorAlreadyOpen);
  1790. XCTAssertTrue([NSFileManager.defaultManager fileExistsAtPath:config.fileURL.path]);
  1791. }
  1792. @end