Files
UUVPN/iOS-SwiftUI-Code/GRDB.swift-6.29.3/Tests/GRDBTests/DatabaseReaderTests.swift
T
2025-01-28 12:28:03 +08:00

348 lines
14 KiB
Swift

import XCTest
import GRDB
class DatabaseReaderTests : GRDBTestCase {
func testAnyDatabaseReader() throws {
// This test passes if this code compiles.
let dbQueue = try DatabaseQueue()
let _: any DatabaseReader = AnyDatabaseReader(dbQueue)
}
// Test passes if it compiles.
func testInitFromGeneric(_ reader: some DatabaseReader) {
_ = AnyDatabaseReader(reader)
}
// Test passes if it compiles.
// See <https://github.com/groue/GRDB.swift/issues/1541>
func testInitFromExistentialReader(_ reader: any DatabaseReader) {
_ = AnyDatabaseReader(reader)
}
// Test passes if it compiles.
// See <https://github.com/groue/GRDB.swift/issues/1541>
func testInitFromExistentialWriter(_ writer: any DatabaseWriter) {
_ = AnyDatabaseReader(writer)
}
// MARK: - Read
func testReadCanRead() throws {
func setup<T: DatabaseWriter>(_ dbWriter: T) throws -> T {
try dbWriter.write { db in
try db.execute(sql: "CREATE TABLE t (id INTEGER PRIMARY KEY)")
}
return dbWriter
}
func test(_ dbReader: some DatabaseReader) throws {
let count = try dbReader.read { db in
try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM t")
}
XCTAssertEqual(count, 0)
}
try test(setup(makeDatabaseQueue()))
try test(setup(makeDatabasePool()))
try test(setup(makeDatabasePool()).makeSnapshot())
#if SQLITE_ENABLE_SNAPSHOT || (!GRDBCUSTOMSQLITE && !GRDBCIPHER && (compiler(>=5.7.1) || !(os(macOS) || targetEnvironment(macCatalyst))))
try test(setup(makeDatabasePool()).makeSnapshotPool())
#endif
}
@available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *)
func testAsyncAwait_ReadCanRead() async throws {
func setup<T: DatabaseWriter>(_ dbWriter: T) throws -> T {
try dbWriter.write { db in
try db.execute(sql: "CREATE TABLE t (id INTEGER PRIMARY KEY)")
}
return dbWriter
}
func test(_ dbReader: some DatabaseReader) async throws {
let count = try await dbReader.read { db in
try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM t")
}
XCTAssertEqual(count, 0)
}
try await test(setup(makeDatabaseQueue()))
try await test(setup(makeDatabasePool()))
try await test(setup(makeDatabasePool()).makeSnapshot())
#if SQLITE_ENABLE_SNAPSHOT || (!GRDBCUSTOMSQLITE && !GRDBCIPHER && (compiler(>=5.7.1) || !(os(macOS) || targetEnvironment(macCatalyst))))
try await test(setup(makeDatabasePool()).makeSnapshotPool())
#endif
}
func testReadPreventsDatabaseModification() throws {
func test(_ dbReader: some DatabaseReader) throws {
do {
try dbReader.read { db in
try db.execute(sql: "CREATE TABLE t (id INTEGER PRIMARY KEY)")
}
XCTFail("Expected error")
} catch DatabaseError.SQLITE_READONLY {
}
}
try test(makeDatabaseQueue())
try test(makeDatabasePool())
try test(makeDatabasePool().makeSnapshot())
#if SQLITE_ENABLE_SNAPSHOT || (!GRDBCUSTOMSQLITE && !GRDBCIPHER && (compiler(>=5.7.1) || !(os(macOS) || targetEnvironment(macCatalyst))))
try test(makeDatabasePool().makeSnapshotPool())
#endif
}
@available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *)
func testAsyncAwait_ReadPreventsDatabaseModification() async throws {
func test(_ dbReader: some DatabaseReader) async throws {
do {
try await dbReader.read { db in
try db.execute(sql: "CREATE TABLE t (id INTEGER PRIMARY KEY)")
}
XCTFail("Expected error")
} catch DatabaseError.SQLITE_READONLY {
}
}
try await test(makeDatabaseQueue())
try await test(makeDatabasePool())
try await test(makeDatabasePool().makeSnapshot())
#if SQLITE_ENABLE_SNAPSHOT || (!GRDBCUSTOMSQLITE && !GRDBCIPHER && (compiler(>=5.7.1) || !(os(macOS) || targetEnvironment(macCatalyst))))
try await test(makeDatabasePool().makeSnapshotPool())
#endif
}
// MARK: - UnsafeRead
func testUnsafeReadCanRead() throws {
func setup<T: DatabaseWriter>(_ dbWriter: T) throws -> T {
try dbWriter.write { db in
try db.execute(sql: "CREATE TABLE t (id INTEGER PRIMARY KEY)")
}
return dbWriter
}
func test(_ dbReader: some DatabaseReader) throws {
let count = try dbReader.unsafeRead { db in
try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM t")
}
XCTAssertEqual(count, 0)
}
try test(setup(makeDatabaseQueue()))
try test(setup(makeDatabasePool()))
try test(setup(makeDatabasePool()).makeSnapshot())
#if SQLITE_ENABLE_SNAPSHOT || (!GRDBCUSTOMSQLITE && !GRDBCIPHER && (compiler(>=5.7.1) || !(os(macOS) || targetEnvironment(macCatalyst))))
try test(setup(makeDatabasePool()).makeSnapshotPool())
#endif
}
@available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *)
func testAsyncAwait_UnsafeReadCanRead() async throws {
func setup<T: DatabaseWriter>(_ dbWriter: T) throws -> T {
try dbWriter.write { db in
try db.execute(sql: "CREATE TABLE t (id INTEGER PRIMARY KEY)")
}
return dbWriter
}
func test(_ dbReader: some DatabaseReader) async throws {
let count = try await dbReader.unsafeRead { db in
try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM t")
}
XCTAssertEqual(count, 0)
}
try await test(setup(makeDatabaseQueue()))
try await test(setup(makeDatabasePool()))
try await test(setup(makeDatabasePool()).makeSnapshot())
#if SQLITE_ENABLE_SNAPSHOT || (!GRDBCUSTOMSQLITE && !GRDBCIPHER && (compiler(>=5.7.1) || !(os(macOS) || targetEnvironment(macCatalyst))))
try await test(setup(makeDatabasePool()).makeSnapshotPool())
#endif
}
// MARK: - UnsafeReentrantRead
func testUnsafeReentrantReadCanRead() throws {
func setup<T: DatabaseWriter>(_ dbWriter: T) throws -> T {
try dbWriter.write { db in
try db.execute(sql: "CREATE TABLE t (id INTEGER PRIMARY KEY)")
}
return dbWriter
}
func test(_ dbReader: some DatabaseReader) throws {
let count = try dbReader.unsafeReentrantRead { db in
try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM t")
}
XCTAssertEqual(count, 0)
}
try test(setup(makeDatabaseQueue()))
try test(setup(makeDatabasePool()))
try test(setup(makeDatabasePool()).makeSnapshot())
#if SQLITE_ENABLE_SNAPSHOT || (!GRDBCUSTOMSQLITE && !GRDBCIPHER && (compiler(>=5.7.1) || !(os(macOS) || targetEnvironment(macCatalyst))))
try test(setup(makeDatabasePool()).makeSnapshotPool())
#endif
}
func testUnsafeReentrantReadIsReentrant() throws {
func test(_ dbReader: some DatabaseReader) throws {
try dbReader.unsafeReentrantRead { db1 in
try dbReader.unsafeReentrantRead { db2 in
try dbReader.unsafeReentrantRead { db3 in
XCTAssertTrue(db1 === db2)
XCTAssertTrue(db2 === db3)
}
}
}
}
try test(makeDatabaseQueue())
try test(makeDatabasePool())
try test(makeDatabasePool().makeSnapshot())
#if SQLITE_ENABLE_SNAPSHOT || (!GRDBCUSTOMSQLITE && !GRDBCIPHER && (compiler(>=5.7.1) || !(os(macOS) || targetEnvironment(macCatalyst))))
try test(makeDatabasePool().makeSnapshotPool())
#endif
}
func testUnsafeReentrantReadIsReentrantFromWrite() throws {
func test(_ dbWriter: some DatabaseWriter) throws {
try dbWriter.write { db1 in
try dbWriter.unsafeReentrantRead { db2 in
try dbWriter.unsafeReentrantRead { db3 in
XCTAssertTrue(db1 === db2)
XCTAssertTrue(db2 === db3)
}
}
}
}
try test(makeDatabaseQueue())
try test(makeDatabasePool())
}
// MARK: - AsyncRead
func testAsyncRead() throws {
func test(_ dbReader: some DatabaseReader) throws {
let expectation = self.expectation(description: "updates")
let semaphore = DispatchSemaphore(value: 0)
var count: Int?
dbReader.asyncRead { dbResult in
// Make sure this block executes asynchronously
semaphore.wait()
do {
count = try Int.fetchOne(dbResult.get(), sql: "SELECT COUNT(*) FROM sqlite_master")
} catch {
XCTFail("Unexpected error: \(error)")
}
expectation.fulfill()
}
semaphore.signal()
waitForExpectations(timeout: 1, handler: nil)
XCTAssertNotNil(count)
}
try test(makeDatabaseQueue())
try test(makeDatabasePool())
try test(makeDatabasePool().makeSnapshot())
#if SQLITE_ENABLE_SNAPSHOT || (!GRDBCUSTOMSQLITE && !GRDBCIPHER && (compiler(>=5.7.1) || !(os(macOS) || targetEnvironment(macCatalyst))))
try test(makeDatabasePool().makeSnapshotPool())
#endif
}
func testAsyncReadPreventsDatabaseModification() throws {
func test(_ dbReader: some DatabaseReader) throws {
let expectation = self.expectation(description: "updates")
let semaphore = DispatchSemaphore(value: 0)
dbReader.asyncRead { dbResult in
// Make sure this block executes asynchronously
semaphore.wait()
do {
try dbResult.get().execute(sql: "CREATE TABLE testAsyncReadPreventsDatabaseModification (a)")
XCTFail("Expected error")
} catch let error as DatabaseError {
XCTAssertEqual(error.resultCode, .SQLITE_READONLY)
} catch {
XCTFail("Unexpected error: \(error)")
}
expectation.fulfill()
}
semaphore.signal()
waitForExpectations(timeout: 1, handler: nil)
}
try test(makeDatabaseQueue())
try test(makeDatabasePool())
try test(makeDatabasePool().makeSnapshot())
#if SQLITE_ENABLE_SNAPSHOT || (!GRDBCUSTOMSQLITE && !GRDBCIPHER && (compiler(>=5.7.1) || !(os(macOS) || targetEnvironment(macCatalyst))))
try test(makeDatabasePool().makeSnapshotPool())
#endif
}
// MARK: - Function
func testAddFunction() throws {
func test(_ dbReader: some DatabaseReader) throws {
let value = try dbReader.read { db -> Int? in
let f = DatabaseFunction("f", argumentCount: 0, pure: true) { _ in 0 }
db.add(function: f)
return try Int.fetchOne(db, sql: "SELECT f()")
}
XCTAssertEqual(value, 0)
}
try test(makeDatabaseQueue())
try test(makeDatabasePool())
try test(makeDatabasePool().makeSnapshot())
#if SQLITE_ENABLE_SNAPSHOT || (!GRDBCUSTOMSQLITE && !GRDBCIPHER && (compiler(>=5.7.1) || !(os(macOS) || targetEnvironment(macCatalyst))))
try test(makeDatabasePool().makeSnapshotPool())
#endif
}
// MARK: - Collation
func testAddCollation() throws {
func test(_ dbReader: some DatabaseReader) throws {
let value = try dbReader.read { db -> Int? in
let collation = DatabaseCollation("c") { _, _ in .orderedSame }
db.add(collation: collation)
return try Int.fetchOne(db, sql: "SELECT 'foo' AS str ORDER BY str COLLATE c")
}
XCTAssertEqual(value, 0)
}
try test(makeDatabaseQueue())
try test(makeDatabasePool())
try test(makeDatabasePool().makeSnapshot())
#if SQLITE_ENABLE_SNAPSHOT || (!GRDBCUSTOMSQLITE && !GRDBCIPHER && (compiler(>=5.7.1) || !(os(macOS) || targetEnvironment(macCatalyst))))
try test(makeDatabasePool().makeSnapshotPool())
#endif
}
// MARK: - Backup
func testBackup() throws {
func setup<T: DatabaseWriter>(_ dbWriter: T) throws -> T {
try dbWriter.write { db in
try db.execute(sql: "CREATE TABLE t (id INTEGER PRIMARY KEY)")
}
return dbWriter
}
func test(_ source: some DatabaseReader) throws {
let dest = try makeDatabaseQueue(configuration: Configuration())
try source.backup(to: dest)
let count = try dest.read { db in
try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM t")
}
XCTAssertEqual(count, 0)
}
// SQLCipher can't backup encrypted databases: use a pristine Configuration
try test(setup(makeDatabaseQueue(configuration: Configuration())))
try test(setup(makeDatabasePool(configuration: Configuration())))
try test(setup(makeDatabasePool(configuration: Configuration())).makeSnapshot())
#if SQLITE_ENABLE_SNAPSHOT || (!GRDBCUSTOMSQLITE && !GRDBCIPHER && (compiler(>=5.7.1) || !(os(macOS) || targetEnvironment(macCatalyst))))
try test(setup(makeDatabasePool(configuration: Configuration())).makeSnapshotPool())
#endif
}
}