FMDatabaseQueueTests.m 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. //
  2. // FMDatabaseQueueTests.m
  3. // fmdb
  4. //
  5. // Created by Graham Dennis on 24/11/2013.
  6. //
  7. //
  8. #import <XCTest/XCTest.h>
  9. #import "FMDatabaseQueue.h"
  10. #if FMDB_SQLITE_STANDALONE
  11. #import <sqlite3/sqlite3.h>
  12. #else
  13. #import <sqlite3.h>
  14. #endif
  15. @interface FMDatabaseQueueTests : FMDBTempDBTests
  16. @property FMDatabaseQueue *queue;
  17. @end
  18. @implementation FMDatabaseQueueTests
  19. + (void)populateDatabase:(FMDatabase *)db
  20. {
  21. [db executeUpdate:@"create table easy (a text)"];
  22. [db executeUpdate:@"create table qfoo (foo text)"];
  23. [db executeUpdate:@"insert into qfoo values ('hi')"];
  24. [db executeUpdate:@"insert into qfoo values ('hello')"];
  25. [db executeUpdate:@"insert into qfoo values ('not')"];
  26. }
  27. - (void)setUp
  28. {
  29. [super setUp];
  30. // Put setup code here. This method is called before the invocation of each test method in the class.
  31. self.queue = [FMDatabaseQueue databaseQueueWithPath:self.databasePath];
  32. }
  33. - (void)tearDown
  34. {
  35. // Put teardown code here. This method is called after the invocation of each test method in the class.
  36. [super tearDown];
  37. }
  38. - (void)testURLOpenNoPath {
  39. FMDatabaseQueue *queue = [[FMDatabaseQueue alloc] init];
  40. XCTAssert(queue, @"Database queue should be returned");
  41. queue = nil;
  42. }
  43. - (void)testURLOpenNoURL {
  44. FMDatabaseQueue *queue = [[FMDatabaseQueue alloc] initWithURL:nil];
  45. XCTAssert(queue, @"Database queue should be returned");
  46. queue = nil;
  47. }
  48. - (void)testURLOpen {
  49. NSURL *tempFolder = [NSURL fileURLWithPath:NSTemporaryDirectory()];
  50. NSURL *fileURL = [tempFolder URLByAppendingPathComponent:[[NSUUID UUID] UUIDString]];
  51. FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithURL:fileURL];
  52. XCTAssert(queue, @"Database queue should be returned");
  53. queue = nil;
  54. [[NSFileManager defaultManager] removeItemAtURL:fileURL error:nil];
  55. }
  56. - (void)testURLOpenInit {
  57. NSURL *tempFolder = [NSURL fileURLWithPath:NSTemporaryDirectory()];
  58. NSURL *fileURL = [tempFolder URLByAppendingPathComponent:[[NSUUID UUID] UUIDString]];
  59. FMDatabaseQueue *queue = [[FMDatabaseQueue alloc] initWithURL:fileURL];
  60. XCTAssert(queue, @"Database queue should be returned");
  61. queue = nil;
  62. [[NSFileManager defaultManager] removeItemAtURL:fileURL error:nil];
  63. }
  64. - (void)testURLOpenWithOptions {
  65. NSURL *tempFolder = [NSURL fileURLWithPath:NSTemporaryDirectory()];
  66. NSURL *fileURL = [tempFolder URLByAppendingPathComponent:[[NSUUID UUID] UUIDString]];
  67. FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithURL:fileURL flags:SQLITE_OPEN_READWRITE];
  68. XCTAssertNil(queue, @"Database queue should not have been created");
  69. }
  70. - (void)testURLOpenInitWithOptions {
  71. NSURL *tempFolder = [NSURL fileURLWithPath:NSTemporaryDirectory()];
  72. NSURL *fileURL = [tempFolder URLByAppendingPathComponent:[[NSUUID UUID] UUIDString]];
  73. FMDatabaseQueue *queue = [[FMDatabaseQueue alloc] initWithURL:fileURL flags:SQLITE_OPEN_READWRITE];
  74. XCTAssertNil(queue, @"Database queue should not have been created");
  75. queue = [[FMDatabaseQueue alloc] initWithURL:fileURL flags:SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE];
  76. XCTAssert(queue, @"Database queue should have been created");
  77. [queue inDatabase:^(FMDatabase * _Nonnull db) {
  78. BOOL success = [db executeUpdate:@"CREATE TABLE foo (bar INT)"];
  79. XCTAssert(success, @"Create failed");
  80. success = [db executeUpdate:@"INSERT INTO foo (bar) VALUES (?)", @42];
  81. XCTAssert(success, @"Insert failed");
  82. }];
  83. queue = nil;
  84. queue = [[FMDatabaseQueue alloc] initWithURL:fileURL flags:SQLITE_OPEN_READONLY];
  85. XCTAssert(queue, @"Now database queue should open have been created");
  86. [queue inDatabase:^(FMDatabase * _Nonnull db) {
  87. BOOL success = [db executeUpdate:@"CREATE TABLE baz (qux INT)"];
  88. XCTAssertFalse(success, @"But updates should fail on read only database");
  89. }];
  90. queue = nil;
  91. [[NSFileManager defaultManager] removeItemAtURL:fileURL error:nil];
  92. }
  93. - (void)testURLOpenWithOptionsVfs {
  94. sqlite3_vfs vfs = *sqlite3_vfs_find(NULL);
  95. vfs.zName = "MyCustomVFS";
  96. XCTAssertEqual(SQLITE_OK, sqlite3_vfs_register(&vfs, 0));
  97. NSURL *tempFolder = [NSURL fileURLWithPath:NSTemporaryDirectory()];
  98. NSURL *fileURL = [tempFolder URLByAppendingPathComponent:[[NSUUID UUID] UUIDString]];
  99. FMDatabaseQueue *queue = [[FMDatabaseQueue alloc] initWithURL:fileURL flags:SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE vfs:@"MyCustomVFS"];
  100. XCTAssert(queue, @"Database queue should not have been created");
  101. queue = nil;
  102. XCTAssertEqual(SQLITE_OK, sqlite3_vfs_unregister(&vfs));
  103. }
  104. - (void)testQueueSelect
  105. {
  106. [self.queue inDatabase:^(FMDatabase *adb) {
  107. int count = 0;
  108. FMResultSet *rsl = [adb executeQuery:@"select * from qfoo where foo like 'h%'"];
  109. while ([rsl next]) {
  110. count++;
  111. }
  112. XCTAssertEqual(count, 2);
  113. count = 0;
  114. rsl = [adb executeQuery:@"select * from qfoo where foo like ?", @"h%"];
  115. while ([rsl next]) {
  116. count++;
  117. }
  118. XCTAssertEqual(count, 2);
  119. }];
  120. }
  121. - (void)testReadOnlyQueue
  122. {
  123. FMDatabaseQueue *queue2 = [FMDatabaseQueue databaseQueueWithPath:self.databasePath flags:SQLITE_OPEN_READONLY];
  124. XCTAssertNotNil(queue2);
  125. {
  126. [queue2 inDatabase:^(FMDatabase *db2) {
  127. FMResultSet *rs1 = [db2 executeQuery:@"SELECT * FROM qfoo"];
  128. XCTAssertNotNil(rs1);
  129. [rs1 close];
  130. XCTAssertFalse(([db2 executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:3]]), @"Insert should fail because this is a read-only database");
  131. }];
  132. [queue2 close];
  133. // Check that when we re-open the database, it's still read-only
  134. [queue2 inDatabase:^(FMDatabase *db2) {
  135. FMResultSet *rs1 = [db2 executeQuery:@"SELECT * FROM qfoo"];
  136. XCTAssertNotNil(rs1);
  137. [rs1 close];
  138. XCTAssertFalse(([db2 executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:3]]), @"Insert should fail because this is a read-only database");
  139. }];
  140. }
  141. }
  142. - (void)testStressTest
  143. {
  144. size_t ops = 16;
  145. dispatch_queue_t dqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  146. dispatch_apply(ops, dqueue, ^(size_t nby) {
  147. // just mix things up a bit for demonstration purposes.
  148. if (nby % 2 == 1) {
  149. [NSThread sleepForTimeInterval:.01];
  150. [self.queue inTransaction:^(FMDatabase *adb, BOOL *rollback) {
  151. FMResultSet *rsl = [adb executeQuery:@"select * from qfoo where foo like 'h%'"];
  152. while ([rsl next]) {
  153. ;// whatever.
  154. }
  155. }];
  156. }
  157. if (nby % 3 == 1) {
  158. [NSThread sleepForTimeInterval:.01];
  159. }
  160. [self.queue inTransaction:^(FMDatabase *adb, BOOL *rollback) {
  161. XCTAssertTrue([adb executeUpdate:@"insert into qfoo values ('1')"]);
  162. XCTAssertTrue([adb executeUpdate:@"insert into qfoo values ('2')"]);
  163. XCTAssertTrue([adb executeUpdate:@"insert into qfoo values ('3')"]);
  164. }];
  165. });
  166. [self.queue close];
  167. [self.queue inDatabase:^(FMDatabase *adb) {
  168. XCTAssertTrue([adb executeUpdate:@"insert into qfoo values ('1')"]);
  169. }];
  170. }
  171. - (void)testTransaction
  172. {
  173. [self.queue inDatabase:^(FMDatabase *adb) {
  174. [adb executeUpdate:@"create table transtest (a integer)"];
  175. XCTAssertTrue([adb executeUpdate:@"insert into transtest values (1)"]);
  176. XCTAssertTrue([adb executeUpdate:@"insert into transtest values (2)"]);
  177. int rowCount = 0;
  178. FMResultSet *ars = [adb executeQuery:@"select * from transtest"];
  179. while ([ars next]) {
  180. rowCount++;
  181. }
  182. XCTAssertEqual(rowCount, 2);
  183. }];
  184. [self.queue inTransaction:^(FMDatabase *adb, BOOL *rollback) {
  185. XCTAssertTrue([adb executeUpdate:@"insert into transtest values (3)"]);
  186. if (YES) {
  187. // uh oh!, something went wrong (not really, this is just a test
  188. *rollback = YES;
  189. return;
  190. }
  191. XCTFail(@"This shouldn't be reached");
  192. }];
  193. [self.queue inDatabase:^(FMDatabase *adb) {
  194. int rowCount = 0;
  195. FMResultSet *ars = [adb executeQuery:@"select * from transtest"];
  196. while ([ars next]) {
  197. rowCount++;
  198. }
  199. XCTAssertFalse([adb hasOpenResultSets]);
  200. XCTAssertEqual(rowCount, 2);
  201. }];
  202. }
  203. @end