RealmTests.mm 84 KB

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