add swiftUI code
This commit is contained in:
+225
@@ -0,0 +1,225 @@
|
||||
import XCTest
|
||||
@testable import GRDB
|
||||
|
||||
class DatabasePoolSchemaCacheTests : GRDBTestCase {
|
||||
|
||||
func testCache() throws {
|
||||
let dbPool = try makeDatabasePool()
|
||||
|
||||
try dbPool.write { db in
|
||||
try db.execute(sql: "CREATE TABLE items (id INTEGER PRIMARY KEY, email TEXT UNIQUE, foo INT, bar DOUBLE)")
|
||||
try db.execute(sql: "CREATE INDEX foobar ON items(foo, bar)")
|
||||
}
|
||||
|
||||
try dbPool.write { db in
|
||||
// Assert that the writer cache is empty
|
||||
XCTAssertTrue(db.schemaCache[.main].primaryKey("items") == nil)
|
||||
XCTAssertTrue(db.schemaCache[.main].columns(in: "items") == nil)
|
||||
XCTAssertTrue(db.schemaCache[.main].indexes(on: "items") == nil)
|
||||
}
|
||||
|
||||
try dbPool.read { db in
|
||||
// Assert that a reader cache is empty
|
||||
XCTAssertTrue(db.schemaCache[.main].primaryKey("items") == nil)
|
||||
XCTAssertTrue(db.schemaCache[.main].columns(in: "items") == nil)
|
||||
XCTAssertTrue(db.schemaCache[.main].indexes(on: "items") == nil)
|
||||
}
|
||||
|
||||
try dbPool.read { db in
|
||||
// Warm cache in a reader
|
||||
_ = try db.primaryKey("items")
|
||||
_ = try db.columns(in: "items")
|
||||
_ = try db.indexes(on: "items")
|
||||
|
||||
// Assert that reader cache is warm
|
||||
XCTAssertTrue(db.schemaCache[.main].primaryKey("items") != nil)
|
||||
XCTAssertTrue(db.schemaCache[.main].columns(in: "items") != nil)
|
||||
XCTAssertTrue(db.schemaCache[.main].indexes(on: "items") != nil)
|
||||
}
|
||||
|
||||
try dbPool.read { db in
|
||||
// Assert that reader cache is still warm
|
||||
XCTAssertTrue(db.schemaCache[.main].primaryKey("items") != nil)
|
||||
XCTAssertTrue(db.schemaCache[.main].columns(in: "items") != nil)
|
||||
XCTAssertTrue(db.schemaCache[.main].indexes(on: "items") != nil)
|
||||
}
|
||||
|
||||
try dbPool.write { db in
|
||||
// Warm cache in the writer
|
||||
_ = try db.primaryKey("items")
|
||||
_ = try db.columns(in: "items")
|
||||
_ = try db.indexes(on: "items")
|
||||
|
||||
// Assert that writer cache is warm
|
||||
XCTAssertTrue(db.schemaCache[.main].primaryKey("items") != nil)
|
||||
XCTAssertTrue(db.schemaCache[.main].columns(in: "items") != nil)
|
||||
XCTAssertTrue(db.schemaCache[.main].indexes(on: "items") != nil)
|
||||
}
|
||||
|
||||
try dbPool.write { db in
|
||||
// Assert that writer cache is still warm
|
||||
XCTAssertTrue(db.schemaCache[.main].primaryKey("items") != nil)
|
||||
XCTAssertTrue(db.schemaCache[.main].columns(in: "items") != nil)
|
||||
XCTAssertTrue(db.schemaCache[.main].indexes(on: "items") != nil)
|
||||
}
|
||||
|
||||
try dbPool.write { db in
|
||||
// Change schema
|
||||
try db.execute(sql: "DROP TABLE items")
|
||||
|
||||
// Assert that the writer cache is cleared
|
||||
XCTAssertTrue(db.schemaCache[.main].primaryKey("items") == nil)
|
||||
XCTAssertTrue(db.schemaCache[.main].columns(in: "items") == nil)
|
||||
XCTAssertTrue(db.schemaCache[.main].indexes(on: "items") == nil)
|
||||
}
|
||||
|
||||
try dbPool.read { db in
|
||||
// Assert that reader cache is cleared
|
||||
XCTAssertTrue(db.schemaCache[.main].primaryKey("items") == nil)
|
||||
XCTAssertTrue(db.schemaCache[.main].columns(in: "items") == nil)
|
||||
XCTAssertTrue(db.schemaCache[.main].indexes(on: "items") == nil)
|
||||
}
|
||||
|
||||
try dbPool.read { db in
|
||||
do {
|
||||
// Assert that cache is used: we expect an error now that
|
||||
// the cache is empty.
|
||||
_ = try db.primaryKey("items")
|
||||
XCTFail()
|
||||
} catch let error as DatabaseError {
|
||||
XCTAssertEqual(error.resultCode, .SQLITE_ERROR)
|
||||
XCTAssertEqual(error.message!, "no such table: items")
|
||||
XCTAssertEqual(error.description, "SQLite error 1: no such table: items")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: fix flaky test
|
||||
// func testCachedStatementsAreNotShared() throws {
|
||||
// // This is a regression test.
|
||||
// //
|
||||
// // If cached statements were shared between reader connections, this
|
||||
// // test would crash with fatal error: Database was not used on the
|
||||
// // correct thread.
|
||||
// let dbPool = try makeDatabasePool()
|
||||
// try dbPool.write { db in
|
||||
// try db.execute(sql: "CREATE TABLE items (id INTEGER PRIMARY KEY)")
|
||||
// try db.execute(sql: "INSERT INTO items (id) VALUES (1)")
|
||||
// }
|
||||
//
|
||||
// // Block 1 Block 2
|
||||
// // SELECT 1 FROM items WHERE id = 1
|
||||
// // >
|
||||
// let s1 = DispatchSemaphore(value: 0)
|
||||
// // SELECT 1 FROM items WHERE id = 1
|
||||
//
|
||||
// let block1 = { () in
|
||||
// try! dbPool.read { db in
|
||||
// let stmt = try! db.cachedStatement(sql: "SELECT * FROM items")
|
||||
// XCTAssertEqual(try Int.fetchOne(stmt)!, 1)
|
||||
// s1.signal()
|
||||
// }
|
||||
// }
|
||||
// let block2 = { () in
|
||||
// try! dbPool.read { db in
|
||||
// _ = s1.wait(timeout: .distantFuture)
|
||||
// let stmt = try! db.cachedStatement(sql: "SELECT * FROM items")
|
||||
// XCTAssertEqual(try Int.fetchOne(stmt)!, 1)
|
||||
// }
|
||||
// }
|
||||
// let blocks = [block1, block2]
|
||||
// DispatchQueue.concurrentPerform(iterations: blocks.count) { index in
|
||||
// blocks[index]()
|
||||
// }
|
||||
// }
|
||||
|
||||
func testCacheSnapshotIsolation() throws {
|
||||
// This test checks that the schema cache follows snapshot isolation.
|
||||
// and that writer and readers do not naively share the same cache.
|
||||
let dbPool = try makeDatabasePool()
|
||||
|
||||
// writer reader
|
||||
// CREATE TABLE foo
|
||||
// table exists: true
|
||||
// >
|
||||
let s1 = DispatchSemaphore(value: 0)
|
||||
// table exists: true
|
||||
// <
|
||||
let s2 = DispatchSemaphore(value: 0)
|
||||
// DROP TABLE foo
|
||||
// table exists: false
|
||||
// >
|
||||
let s3 = DispatchSemaphore(value: 0)
|
||||
// table exists: true
|
||||
// <
|
||||
let s4 = DispatchSemaphore(value: 0)
|
||||
// table exists: false
|
||||
|
||||
let block1 = { () in
|
||||
try! dbPool.writeWithoutTransaction { db in
|
||||
try db.execute(sql: "CREATE TABLE foo(id INTEGER PRIMARY KEY)")
|
||||
// warm cache
|
||||
_ = try db.primaryKey("foo")
|
||||
// cache contains the primary key
|
||||
XCTAssertNotNil(db.schemaCache[.main].primaryKey("foo"))
|
||||
s1.signal()
|
||||
_ = s2.wait(timeout: .distantFuture)
|
||||
try db.execute(sql: "DROP TABLE foo")
|
||||
// cache does not contain the primary key
|
||||
XCTAssertNil(db.schemaCache[.main].primaryKey("foo"))
|
||||
s3.signal()
|
||||
_ = s4.wait(timeout: .distantFuture)
|
||||
// cache does not contain the primary key
|
||||
XCTAssertNil(db.schemaCache[.main].primaryKey("foo"))
|
||||
}
|
||||
}
|
||||
let block2 = { () in
|
||||
_ = s1.wait(timeout: .distantFuture)
|
||||
try! dbPool.read { db in
|
||||
// activate snapshot isolation so that foo table is visible during the whole read. Any read is enough.
|
||||
try db.makeStatement(sql: "SELECT * FROM sqlite_master").makeCursor().next()
|
||||
// warm cache
|
||||
_ = try db.primaryKey("foo")
|
||||
// cache contains the primary key
|
||||
XCTAssertNotNil(db.schemaCache[.main].primaryKey("foo"))
|
||||
s2.signal()
|
||||
_ = s3.wait(timeout: .distantFuture)
|
||||
// cache contains the primary key
|
||||
XCTAssertNotNil(db.schemaCache[.main].primaryKey("foo"))
|
||||
// warm cache if needed
|
||||
_ = try db.primaryKey("foo")
|
||||
s4.signal()
|
||||
}
|
||||
}
|
||||
let blocks = [block1, block2]
|
||||
DispatchQueue.concurrentPerform(iterations: blocks.count) { index in
|
||||
blocks[index]()
|
||||
}
|
||||
}
|
||||
|
||||
func testUnsafeReaderHasDatabaseCache() throws {
|
||||
let dbPool = try makeDatabasePool()
|
||||
|
||||
try dbPool.write { db in
|
||||
try db.execute(sql: "CREATE TABLE items (id INTEGER PRIMARY KEY, email TEXT UNIQUE, foo INT, bar DOUBLE)")
|
||||
try db.execute(sql: "CREATE INDEX foobar ON items(foo, bar)")
|
||||
}
|
||||
|
||||
try dbPool.unsafeRead { db in
|
||||
// Assert that a cache is empty
|
||||
XCTAssertTrue(db.schemaCache[.main].primaryKey("items") == nil)
|
||||
XCTAssertTrue(db.schemaCache[.main].columns(in: "items") == nil)
|
||||
XCTAssertTrue(db.schemaCache[.main].indexes(on: "items") == nil)
|
||||
|
||||
// Warm cache in a reader
|
||||
_ = try db.primaryKey("items")
|
||||
_ = try db.columns(in: "items")
|
||||
_ = try db.indexes(on: "items")
|
||||
|
||||
// Assert that a reader cache is warmed
|
||||
XCTAssertFalse(db.schemaCache[.main].primaryKey("items") == nil)
|
||||
XCTAssertFalse(db.schemaCache[.main].columns(in: "items") == nil)
|
||||
XCTAssertFalse(db.schemaCache[.main].indexes(on: "items") == nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user