RealmTests.mm 84 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244
  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. - (void)testInvalidLockFile
  1646. {
  1647. // Create the realm file and lock file
  1648. @autoreleasepool { [RLMRealm defaultRealm]; }
  1649. int fd = open([RLMRealmConfiguration.defaultConfiguration.fileURL.path stringByAppendingString:@".lock"].UTF8String, O_RDWR);
  1650. XCTAssertNotEqual(-1, fd);
  1651. // Change the value of the mutex size field in the shared info header
  1652. uint8_t value = 255;
  1653. pwrite(fd, &value, 1, 1);
  1654. // Ensure that SharedGroup can't get an exclusive lock on the lock file so
  1655. // that it can't just recreate it
  1656. int ret = flock(fd, LOCK_SH);
  1657. XCTAssertEqual(0, ret);
  1658. NSError *error;
  1659. RLMRealm *realm = [RLMRealm realmWithConfiguration:RLMRealmConfiguration.defaultConfiguration error:&error];
  1660. XCTAssertNil(realm);
  1661. RLMValidateRealmError(error, RLMErrorIncompatibleLockFile, @"Realm file is currently open in another process", nil);
  1662. flock(fd, LOCK_UN);
  1663. close(fd);
  1664. }
  1665. - (void)testCannotMigrateRealmWhenRealmIsOpen {
  1666. RLMRealm *realm = [self realmWithTestPath];
  1667. RLMRealmConfiguration *configuration = [RLMRealmConfiguration defaultConfiguration];
  1668. configuration.fileURL = realm.configuration.fileURL;
  1669. XCTAssertThrows([RLMRealm performMigrationForConfiguration:configuration error:nil]);
  1670. }
  1671. - (void)testNotificationPipeBufferOverfull {
  1672. RLMRealm *realm = [self inMemoryRealmWithIdentifier:@"test"];
  1673. // pipes have a 8 KB buffer on OS X, so verify we don't block after 8192 commits
  1674. for (int i = 0; i < 9000; ++i) {
  1675. [realm transactionWithBlock:^{}];
  1676. }
  1677. }
  1678. - (NSArray *)pathsFor100Realms
  1679. {
  1680. NSMutableArray *paths = [NSMutableArray array];
  1681. for (int i = 0; i < 100; ++i) {
  1682. NSString *realmFileName = [NSString stringWithFormat:@"test.%d.realm", i];
  1683. [paths addObject:RLMRealmPathForFile(realmFileName)];
  1684. }
  1685. return paths;
  1686. }
  1687. - (void)testCanCreate100RealmsWithoutBreakingGCD
  1688. {
  1689. NSMutableArray *realms = [NSMutableArray array];
  1690. for (NSString *realmPath in self.pathsFor100Realms) {
  1691. [realms addObject:[RLMRealm realmWithURL:[NSURL fileURLWithPath:realmPath]]];
  1692. }
  1693. XCTestExpectation *expectation = [self expectationWithDescription:@"Block dispatched to concurrent queue should be executed"];
  1694. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  1695. [expectation fulfill];
  1696. });
  1697. [self waitForExpectationsWithTimeout:1 handler:nil];
  1698. }
  1699. - (void)testThreadIDReuse {
  1700. // Open Realms on new threads until we get repeated thread IDs, while
  1701. // retaining each Realm opened. This verifies that we don't get a Realm from
  1702. // an old thread that no longer exists from the cache.
  1703. NSMutableArray *realms = [NSMutableArray array];
  1704. std::unordered_set<pthread_t> threadIds;
  1705. bool done = false;
  1706. while (!done) {
  1707. std::thread([&] {
  1708. RLMRealm *realm = [RLMRealm defaultRealm];
  1709. [realms addObject:realm];
  1710. (void)[IntObject allObjectsInRealm:realm].count;
  1711. [realm refresh];
  1712. if (!threadIds.insert(pthread_self()).second) {
  1713. done = true;
  1714. }
  1715. }).join();
  1716. }
  1717. }
  1718. - (void)testAuxiliaryFilesAreExcludedFromBackup {
  1719. RLMSetSkipBackupAttribute(true);
  1720. @autoreleasepool { [RLMRealm defaultRealm]; }
  1721. #if TARGET_OS_TV
  1722. NSArray *auxiliaryFileExtensions = @[@"management", @"lock"]; // tvOS does not support named pipes
  1723. #else
  1724. NSArray *auxiliaryFileExtensions = @[@"management", @"lock", @"note"];
  1725. #endif
  1726. NSURL *fileURL = RLMRealmConfiguration.defaultConfiguration.fileURL;
  1727. for (NSString *pathExtension in auxiliaryFileExtensions) {
  1728. NSNumber *attribute = nil;
  1729. NSError *error = nil;
  1730. BOOL success = [[fileURL URLByAppendingPathExtension:pathExtension] getResourceValue:&attribute forKey:NSURLIsExcludedFromBackupKey error:&error];
  1731. XCTAssertTrue(success);
  1732. XCTAssertNil(error);
  1733. XCTAssertTrue(attribute.boolValue);
  1734. }
  1735. RLMSetSkipBackupAttribute(false);
  1736. }
  1737. - (void)testAuxiliaryFilesAreExcludedFromBackupPerformance {
  1738. RLMSetSkipBackupAttribute(true);
  1739. [self measureBlock:^{
  1740. @autoreleasepool {
  1741. RLMRealm *realm = [RLMRealm defaultRealm];
  1742. realm = [RLMRealm defaultRealm];
  1743. 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. }
  1756. @autoreleasepool { [RLMRealm defaultRealm]; }
  1757. }];
  1758. NSURL *fileURL = RLMRealmConfiguration.defaultConfiguration.fileURL;
  1759. #if !TARGET_OS_TV
  1760. for (NSString *pathExtension in @[@"management", @"lock", @"note"]) {
  1761. #else
  1762. for (NSString *pathExtension in @[@"management", @"lock"]) {
  1763. #endif
  1764. NSNumber *attribute = nil;
  1765. NSError *error = nil;
  1766. BOOL success = [[fileURL URLByAppendingPathExtension:pathExtension] getResourceValue:&attribute forKey:NSURLIsExcludedFromBackupKey error:&error];
  1767. XCTAssertTrue(success);
  1768. XCTAssertNil(error);
  1769. XCTAssertTrue(attribute.boolValue);
  1770. }
  1771. }
  1772. - (void)testRealmExists {
  1773. RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
  1774. XCTAssertFalse([RLMRealm fileExistsForConfiguration:config]);
  1775. @autoreleasepool { [RLMRealm realmWithConfiguration:config error:nil]; }
  1776. XCTAssertTrue([RLMRealm fileExistsForConfiguration:config]);
  1777. [RLMRealm deleteFilesForConfiguration:config error:nil];
  1778. XCTAssertFalse([RLMRealm fileExistsForConfiguration:config]);
  1779. }
  1780. - (void)testDeleteNonexistentRealmFile {
  1781. NSError *error;
  1782. XCTAssertFalse([RLMRealm deleteFilesForConfiguration:RLMRealmConfiguration.defaultConfiguration error:&error]);
  1783. XCTAssertNil(error);
  1784. }
  1785. - (void)testDeleteClosedRealmFile {
  1786. RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
  1787. @autoreleasepool { [RLMRealm realmWithConfiguration:config error:nil]; }
  1788. NSError *error;
  1789. XCTAssertTrue([RLMRealm deleteFilesForConfiguration:config error:&error]);
  1790. XCTAssertNil(error);
  1791. NSFileManager *fm = NSFileManager.defaultManager;
  1792. XCTAssertTrue([fm fileExistsAtPath:[config.fileURL.path stringByAppendingPathExtension:@"lock"]]);
  1793. XCTAssertFalse([fm fileExistsAtPath:[config.fileURL.path stringByAppendingPathExtension:@"management"]]);
  1794. #if !TARGET_OS_TV
  1795. XCTAssertFalse([fm fileExistsAtPath:[config.fileURL.path stringByAppendingPathExtension:@"note"]]);
  1796. #endif
  1797. }
  1798. - (void)testDeleteRealmFileWithMissingManagementFiles {
  1799. RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
  1800. [NSFileManager.defaultManager createFileAtPath:config.fileURL.path contents:nil attributes:nil];
  1801. NSError *error;
  1802. XCTAssertTrue([RLMRealm deleteFilesForConfiguration:config error:&error]);
  1803. XCTAssertNil(error);
  1804. }
  1805. - (void)testDeleteRealmFileWithReadOnlyManagementFiles {
  1806. RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
  1807. NSFileManager *fm = NSFileManager.defaultManager;
  1808. [fm createFileAtPath:config.fileURL.path contents:nil attributes:nil];
  1809. NSString *notificationPipe = [config.fileURL.path stringByAppendingPathExtension:@"note"];
  1810. [fm createFileAtPath:notificationPipe contents:nil attributes:@{NSFileImmutable: @YES}];
  1811. NSError *error;
  1812. XCTAssertTrue([RLMRealm deleteFilesForConfiguration:config error:&error]);
  1813. XCTAssertEqual(error.code, NSFileWriteNoPermissionError);
  1814. [fm setAttributes:@{NSFileImmutable: @NO} ofItemAtPath:notificationPipe error:nil];
  1815. }
  1816. - (void)testDeleteOpenRealmFile {
  1817. RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
  1818. __attribute__((objc_precise_lifetime)) RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:nil];
  1819. NSError *error;
  1820. XCTAssertFalse([RLMRealm deleteFilesForConfiguration:config error:&error]);
  1821. XCTAssertEqual(error.code, RLMErrorAlreadyOpen);
  1822. XCTAssertTrue([NSFileManager.defaultManager fileExistsAtPath:config.fileURL.path]);
  1823. }
  1824. @end