106 lines
4.0 KiB
Swift
106 lines
4.0 KiB
Swift
import XCTest
|
|
import GRDB
|
|
|
|
class DatabaseQueueCrashTests: GRDBCrashTestCase {
|
|
|
|
|
|
// =========================================================================
|
|
// MARK: - Reentrancy
|
|
|
|
func testInDatabaseIsNotReentrant() {
|
|
assertCrash("Database methods are not reentrant.") {
|
|
dbQueue.inDatabase { db in
|
|
dbQueue.inDatabase { db in
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func testInTransactionInsideInDatabaseIsNotReentrant() {
|
|
assertCrash("Database methods are not reentrant.") {
|
|
try dbQueue.inDatabase { db in
|
|
try dbQueue.inTransaction { db in
|
|
return .commit
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func testInTransactionIsNotReentrant() {
|
|
assertCrash("Database methods are not reentrant.") {
|
|
try dbQueue.inTransaction { db in
|
|
try dbQueue.inTransaction { db in
|
|
return .commit
|
|
}
|
|
return .commit
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// =========================================================================
|
|
// MARK: - Sequence iteration in wrong queue
|
|
|
|
func testRowSequenceCanNotBeGeneratedOutsideOfDatabaseQueue() {
|
|
assertCrash("Database was not used on the correct thread: execute your statements inside DatabaseQueue.inDatabase() or DatabaseQueue.inTransaction(). If you get this error while iterating the result of a fetch() method, consider using the array returned by fetchAll() instead.") {
|
|
var rows: DatabaseSequence<Row>?
|
|
try dbQueue.inDatabase { db in
|
|
try db.execute(sql: "CREATE TABLE persons (name TEXT)")
|
|
rows = try Row.fetch(db, "SELECT * FROM persons")
|
|
}
|
|
_ = rows!.makeIterator()
|
|
}
|
|
}
|
|
|
|
func testRowSequenceCanNotBeIteratedOutsideOfDatabaseQueue() {
|
|
assertCrash("Database was not used on the correct thread: execute your statements inside DatabaseQueue.inDatabase() or DatabaseQueue.inTransaction(). If you get this error while iterating the result of a fetch() method, consider using the array returned by fetchAll() instead.") {
|
|
var iterator: DatabaseIterator<Row>?
|
|
try dbQueue.inDatabase { db in
|
|
try db.execute(sql: "CREATE TABLE persons (name TEXT)")
|
|
iterator = try Row.fetch(db, "SELECT * FROM persons").makeIterator()
|
|
}
|
|
_ = iterator!.next()
|
|
}
|
|
}
|
|
|
|
|
|
// =========================================================================
|
|
// MARK: - Concurrency
|
|
|
|
func testReaderCrashDuringExclusiveTransaction() {
|
|
assertCrash("SQLite error 5 with statement `SELECT * FROM stuffs`: database is locked") {
|
|
let dbQueue1 = try! makeDatabaseQueue(path: dbQueuepath, configuration: dbConfiguration)
|
|
let dbQueue2 = try! makeDatabaseQueue(path: dbQueuepath, configuration: dbConfiguration)
|
|
|
|
try! dbQueue1.inDatabase { db in
|
|
try db.execute(sql: "CREATE TABLE stuffs (id INTEGER PRIMARY KEY)")
|
|
}
|
|
|
|
let queue = NSOperationQueue()
|
|
queue.maxConcurrentOperationCount = 2
|
|
queue.addOperation(NSBlockOperation {
|
|
do {
|
|
try dbQueue1.inTransaction(.exclusive) { db in
|
|
sleep(2) // let other queue try to read.
|
|
return .commit
|
|
}
|
|
}
|
|
catch is DatabaseError {
|
|
}
|
|
catch {
|
|
XCTFail("\(error)")
|
|
}
|
|
})
|
|
|
|
queue.addOperation(NSBlockOperation {
|
|
dbQueue2.inDatabase { db in
|
|
sleep(1) // let other queue open transaction
|
|
_ = try Row.fetch(db, "SELECT * FROM stuffs") // Crash expected
|
|
}
|
|
})
|
|
|
|
queue.waitUntilAllOperationsAreFinished()
|
|
}
|
|
}
|
|
}
|