add iOS
This commit is contained in:
@@ -0,0 +1,347 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user