FMDatabasePoolTests.m 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. //
  2. // FMDatabasePoolTests.m
  3. // fmdb
  4. //
  5. // Created by Graham Dennis on 24/11/2013.
  6. //
  7. //
  8. #import <XCTest/XCTest.h>
  9. @interface FMDatabasePoolTests : FMDBTempDBTests
  10. @property FMDatabasePool *pool;
  11. @end
  12. @implementation FMDatabasePoolTests
  13. + (void)populateDatabase:(FMDatabase *)db
  14. {
  15. [db executeUpdate:@"create table easy (a text)"];
  16. [db executeUpdate:@"create table easy2 (a text)"];
  17. [db executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:1001]];
  18. [db executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:1002]];
  19. [db executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:1003]];
  20. [db executeUpdate:@"create table likefoo (foo text)"];
  21. [db executeUpdate:@"insert into likefoo values ('hi')"];
  22. [db executeUpdate:@"insert into likefoo values ('hello')"];
  23. [db executeUpdate:@"insert into likefoo values ('not')"];
  24. }
  25. - (void)setUp
  26. {
  27. [super setUp];
  28. // Put setup code here. This method is called before the invocation of each test method in the class.
  29. [self setPool:[FMDatabasePool databasePoolWithPath:self.databasePath]];
  30. [[self pool] setDelegate:self];
  31. }
  32. - (void)tearDown
  33. {
  34. // Put teardown code here. This method is called after the invocation of each test method in the class.
  35. [super tearDown];
  36. }
  37. - (void)testPoolIsInitiallyEmpty
  38. {
  39. XCTAssertEqual([self.pool countOfOpenDatabases], (NSUInteger)0, @"Pool should be empty on creation");
  40. }
  41. - (void)testDatabaseCreation
  42. {
  43. __block FMDatabase *db1;
  44. [self.pool inDatabase:^(FMDatabase *db) {
  45. XCTAssertEqual([self.pool countOfOpenDatabases], (NSUInteger)1, @"Should only have one database at this point");
  46. db1 = db;
  47. }];
  48. [self.pool inDatabase:^(FMDatabase *db) {
  49. XCTAssertEqualObjects(db, db1, @"We should get the same database back because there was no need to create a new one");
  50. [self.pool inDatabase:^(FMDatabase *db2) {
  51. XCTAssertNotEqualObjects(db2, db, @"We should get a different database because the first was in use.");
  52. }];
  53. }];
  54. XCTAssertEqual([self.pool countOfOpenDatabases], (NSUInteger)2);
  55. [self.pool releaseAllDatabases];
  56. XCTAssertEqual([self.pool countOfOpenDatabases], (NSUInteger)0, @"We should be back to zero databases again");
  57. }
  58. - (void)testCheckedInCheckoutOutCount
  59. {
  60. [self.pool inDatabase:^(FMDatabase *aDb) {
  61. XCTAssertEqual([self.pool countOfCheckedInDatabases], (NSUInteger)0);
  62. XCTAssertEqual([self.pool countOfCheckedOutDatabases], (NSUInteger)1);
  63. XCTAssertTrue(([aDb executeUpdate:@"insert into easy (a) values (?)", @"hi"]));
  64. // just for fun.
  65. FMResultSet *rs = [aDb executeQuery:@"select * from easy"];
  66. XCTAssertNotNil(rs);
  67. XCTAssertTrue([rs next]);
  68. while ([rs next]) { ; } // whatevers.
  69. XCTAssertEqual([self.pool countOfOpenDatabases], (NSUInteger)1);
  70. XCTAssertEqual([self.pool countOfCheckedInDatabases], (NSUInteger)0);
  71. XCTAssertEqual([self.pool countOfCheckedOutDatabases], (NSUInteger)1);
  72. }];
  73. XCTAssertEqual([self.pool countOfOpenDatabases], (NSUInteger)1);
  74. }
  75. - (void)testMaximumDatabaseLimit
  76. {
  77. [self.pool setMaximumNumberOfDatabasesToCreate:2];
  78. [self.pool inDatabase:^(FMDatabase *db) {
  79. [self.pool inDatabase:^(FMDatabase *db2) {
  80. [self.pool inDatabase:^(FMDatabase *db3) {
  81. XCTAssertEqual([self.pool countOfOpenDatabases], (NSUInteger)2);
  82. XCTAssertNil(db3, @"The third database must be nil because we have a maximum of 2 databases in the pool");
  83. }];
  84. }];
  85. }];
  86. }
  87. - (void)testTransaction
  88. {
  89. [self.pool inTransaction:^(FMDatabase *adb, BOOL *rollback) {
  90. [adb executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:1001]];
  91. [adb executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:1002]];
  92. [adb executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:1003]];
  93. XCTAssertEqual([self.pool countOfOpenDatabases], (NSUInteger)1);
  94. XCTAssertEqual([self.pool countOfCheckedInDatabases], (NSUInteger)0);
  95. XCTAssertEqual([self.pool countOfCheckedOutDatabases], (NSUInteger)1);
  96. }];
  97. XCTAssertEqual([self.pool countOfOpenDatabases], (NSUInteger)1);
  98. XCTAssertEqual([self.pool countOfCheckedInDatabases], (NSUInteger)1);
  99. XCTAssertEqual([self.pool countOfCheckedOutDatabases], (NSUInteger)0);
  100. }
  101. - (void)testSelect
  102. {
  103. [self.pool inDatabase:^(FMDatabase *db) {
  104. FMResultSet *rs = [db executeQuery:@"select * from easy where a = ?", [NSNumber numberWithInt:1001]];
  105. XCTAssertNotNil(rs);
  106. XCTAssertTrue ([rs next]);
  107. XCTAssertFalse([rs next]);
  108. }];
  109. }
  110. - (void)testTransactionRollback
  111. {
  112. [self.pool inDeferredTransaction:^(FMDatabase *adb, BOOL *rollback) {
  113. XCTAssertTrue(([adb executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:1004]]));
  114. XCTAssertTrue(([adb executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:1005]]));
  115. XCTAssertTrue([[adb executeQuery:@"select * from easy where a == '1004'"] next], @"1004 should be in database");
  116. *rollback = YES;
  117. }];
  118. [self.pool inDatabase:^(FMDatabase *db) {
  119. XCTAssertFalse([[db executeQuery:@"select * from easy where a == '1004'"] next], @"1004 should not be in database");
  120. }];
  121. XCTAssertEqual([self.pool countOfOpenDatabases], (NSUInteger)1);
  122. XCTAssertEqual([self.pool countOfCheckedInDatabases], (NSUInteger)1);
  123. XCTAssertEqual([self.pool countOfCheckedOutDatabases], (NSUInteger)0);
  124. }
  125. - (void)testSavepoint
  126. {
  127. NSError *err = [self.pool inSavePoint:^(FMDatabase *db, BOOL *rollback) {
  128. [db executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:1006]];
  129. }];
  130. XCTAssertNil(err);
  131. }
  132. - (void)testNestedSavepointRollback
  133. {
  134. NSError *err = [self.pool inSavePoint:^(FMDatabase *adb, BOOL *rollback) {
  135. XCTAssertFalse([adb hadError]);
  136. XCTAssertTrue(([adb executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:1009]]));
  137. [adb inSavePoint:^(BOOL *arollback) {
  138. XCTAssertTrue(([adb executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:1010]]));
  139. *arollback = YES;
  140. }];
  141. }];
  142. XCTAssertNil(err);
  143. [self.pool inDatabase:^(FMDatabase *db) {
  144. FMResultSet *rs = [db executeQuery:@"select * from easy where a = ?", [NSNumber numberWithInt:1009]];
  145. XCTAssertTrue ([rs next]);
  146. XCTAssertFalse([rs next]); // close it out.
  147. rs = [db executeQuery:@"select * from easy where a = ?", [NSNumber numberWithInt:1010]];
  148. XCTAssertFalse([rs next]);
  149. }];
  150. }
  151. - (void)testLikeStringQuery
  152. {
  153. [self.pool inDatabase:^(FMDatabase *db) {
  154. int count = 0;
  155. FMResultSet *rsl = [db executeQuery:@"select * from likefoo where foo like 'h%'"];
  156. while ([rsl next]) {
  157. count++;
  158. }
  159. XCTAssertEqual(count, 2);
  160. count = 0;
  161. rsl = [db executeQuery:@"select * from likefoo where foo like ?", @"h%"];
  162. while ([rsl next]) {
  163. count++;
  164. }
  165. XCTAssertEqual(count, 2);
  166. }];
  167. }
  168. - (void)testStressTest
  169. {
  170. size_t ops = 128;
  171. dispatch_queue_t dqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  172. dispatch_apply(ops, dqueue, ^(size_t nby) {
  173. // just mix things up a bit for demonstration purposes.
  174. if (nby % 2 == 1) {
  175. [NSThread sleepForTimeInterval:.001];
  176. }
  177. [self.pool inDatabase:^(FMDatabase *db) {
  178. FMResultSet *rsl = [db executeQuery:@"select * from likefoo where foo like 'h%'"];
  179. XCTAssertNotNil(rsl);
  180. int i = 0;
  181. while ([rsl next]) {
  182. i++;
  183. if (nby % 3 == 1) {
  184. [NSThread sleepForTimeInterval:.0005];
  185. }
  186. }
  187. XCTAssertEqual(i, 2);
  188. }];
  189. });
  190. XCTAssert([self.pool countOfOpenDatabases] < 64, @"There should be significantly less than 64 databases after that stress test");
  191. }
  192. - (BOOL)databasePool:(FMDatabasePool*)pool shouldAddDatabaseToPool:(FMDatabase*)database {
  193. [database setMaxBusyRetryTimeInterval:10];
  194. // [database setCrashOnErrors:YES];
  195. return YES;
  196. }
  197. - (void)testReadWriteStressTest
  198. {
  199. int ops = 16;
  200. dispatch_queue_t dqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  201. dispatch_apply(ops, dqueue, ^(size_t nby) {
  202. // just mix things up a bit for demonstration purposes.
  203. if (nby % 2 == 1) {
  204. [NSThread sleepForTimeInterval:.01];
  205. [self.pool inTransaction:^(FMDatabase *db, BOOL *rollback) {
  206. FMResultSet *rsl = [db executeQuery:@"select * from likefoo where foo like 'h%'"];
  207. XCTAssertNotNil(rsl);
  208. while ([rsl next]) {
  209. ;// whatever.
  210. }
  211. }];
  212. }
  213. if (nby % 3 == 1) {
  214. [NSThread sleepForTimeInterval:.01];
  215. }
  216. [self.pool inTransaction:^(FMDatabase *db, BOOL *rollback) {
  217. XCTAssertTrue([db executeUpdate:@"insert into likefoo values ('1')"]);
  218. XCTAssertTrue([db executeUpdate:@"insert into likefoo values ('2')"]);
  219. XCTAssertTrue([db executeUpdate:@"insert into likefoo values ('3')"]);
  220. }];
  221. });
  222. [self.pool releaseAllDatabases];
  223. [self.pool inDatabase:^(FMDatabase *db) {
  224. XCTAssertTrue([db executeUpdate:@"insert into likefoo values ('1')"]);
  225. }];
  226. }
  227. @end