add swiftUI code
This commit is contained in:
@@ -0,0 +1,360 @@
|
||||
import XCTest
|
||||
import GRDB
|
||||
|
||||
private struct CustomValueType : DatabaseValueConvertible {
|
||||
var databaseValue: DatabaseValue { "CustomValueType".databaseValue }
|
||||
static func fromDatabaseValue(_ dbValue: DatabaseValue) -> CustomValueType? {
|
||||
guard let string = String.fromDatabaseValue(dbValue), string == "CustomValueType" else {
|
||||
return nil
|
||||
}
|
||||
return CustomValueType()
|
||||
}
|
||||
}
|
||||
|
||||
class DatabaseFunctionTests: GRDBTestCase {
|
||||
|
||||
// MARK: - Default functions
|
||||
|
||||
func testDefaultFunctions() throws {
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
try dbQueue.inDatabase { db in
|
||||
|
||||
// Those functions are automatically added to all connections.
|
||||
// See Database.setupDefaultFunctions()
|
||||
|
||||
let capitalize = DatabaseFunction.capitalize
|
||||
XCTAssertEqual(try String.fetchOne(db, sql: "SELECT \(capitalize.name)('jérÔME')"), "Jérôme")
|
||||
|
||||
let lowercase = DatabaseFunction.lowercase
|
||||
XCTAssertEqual(try String.fetchOne(db, sql: "SELECT \(lowercase.name)('jérÔME')"), "jérôme")
|
||||
|
||||
let uppercase = DatabaseFunction.uppercase
|
||||
XCTAssertEqual(try String.fetchOne(db, sql: "SELECT \(uppercase.name)('jérÔME')"), "JÉRÔME")
|
||||
|
||||
// Locale-dependent tests. Are they fragile?
|
||||
|
||||
let localizedCapitalize = DatabaseFunction.localizedCapitalize
|
||||
XCTAssertEqual(try String.fetchOne(db, sql: "SELECT \(localizedCapitalize.name)('jérÔME')"), "Jérôme")
|
||||
|
||||
let localizedLowercase = DatabaseFunction.localizedLowercase
|
||||
XCTAssertEqual(try String.fetchOne(db, sql: "SELECT \(localizedLowercase.name)('jérÔME')"), "jérôme")
|
||||
|
||||
let localizedUppercase = DatabaseFunction.localizedUppercase
|
||||
XCTAssertEqual(try String.fetchOne(db, sql: "SELECT \(localizedUppercase.name)('jérÔME')"), "JÉRÔME")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Return values
|
||||
|
||||
func testFunctionReturningNull() throws {
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
let fn = DatabaseFunction("f", argumentCount: 0) { dbValues in
|
||||
return nil
|
||||
}
|
||||
try dbQueue.inDatabase { db in
|
||||
db.add(function: fn)
|
||||
XCTAssertTrue(try DatabaseValue.fetchOne(db, sql: "SELECT f()")!.isNull)
|
||||
}
|
||||
}
|
||||
|
||||
func testFunctionReturningInt64() throws {
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
let fn = DatabaseFunction("f", argumentCount: 0) { dbValues in
|
||||
return Int64(1)
|
||||
}
|
||||
try dbQueue.inDatabase { db in
|
||||
db.add(function: fn)
|
||||
XCTAssertEqual(try Int64.fetchOne(db, sql: "SELECT f()")!, Int64(1))
|
||||
}
|
||||
}
|
||||
|
||||
func testFunctionReturningDouble() throws {
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
let fn = DatabaseFunction("f", argumentCount: 0) { dbValues in
|
||||
return 1e100
|
||||
}
|
||||
try dbQueue.inDatabase { db in
|
||||
db.add(function: fn)
|
||||
XCTAssertEqual(try Double.fetchOne(db, sql: "SELECT f()")!, 1e100)
|
||||
}
|
||||
}
|
||||
|
||||
func testFunctionReturningString() throws {
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
let fn = DatabaseFunction("f", argumentCount: 0) { dbValues in
|
||||
return "foo"
|
||||
}
|
||||
try dbQueue.inDatabase { db in
|
||||
db.add(function: fn)
|
||||
XCTAssertEqual(try String.fetchOne(db, sql: "SELECT f()")!, "foo")
|
||||
}
|
||||
}
|
||||
|
||||
func testFunctionReturningData() throws {
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
let fn = DatabaseFunction("f", argumentCount: 0) { dbValues in
|
||||
return "foo".data(using: .utf8)
|
||||
}
|
||||
try dbQueue.inDatabase { db in
|
||||
db.add(function: fn)
|
||||
XCTAssertEqual(try Data.fetchOne(db, sql: "SELECT f()")!, "foo".data(using: .utf8))
|
||||
}
|
||||
}
|
||||
|
||||
func testFunctionReturningCustomValueType() throws {
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
let fn = DatabaseFunction("f", argumentCount: 0) { dbValues in
|
||||
return CustomValueType()
|
||||
}
|
||||
try dbQueue.inDatabase { db in
|
||||
db.add(function: fn)
|
||||
XCTAssertTrue(try CustomValueType.fetchOne(db, sql: "SELECT f()") != nil)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Argument values
|
||||
|
||||
func testFunctionArgumentNil() throws {
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
let fn = DatabaseFunction("f", argumentCount: 1) { (dbValues: [DatabaseValue]) in
|
||||
return dbValues[0].isNull
|
||||
}
|
||||
try dbQueue.inDatabase { db in
|
||||
db.add(function: fn)
|
||||
XCTAssertTrue(try Bool.fetchOne(db, sql: "SELECT f(NULL)")!)
|
||||
XCTAssertFalse(try Bool.fetchOne(db, sql: "SELECT f(1)")!)
|
||||
XCTAssertFalse(try Bool.fetchOne(db, sql: "SELECT f(1.1)")!)
|
||||
XCTAssertFalse(try Bool.fetchOne(db, sql: "SELECT f('foo')")!)
|
||||
XCTAssertFalse(try Bool.fetchOne(db, sql: "SELECT f(?)", arguments: ["foo".data(using: .utf8)])!)
|
||||
XCTAssertFalse(try Bool.fetchOne(db, sql: "SELECT f(?)", arguments: [Data()])!)
|
||||
}
|
||||
}
|
||||
|
||||
func testFunctionArgumentInt64() throws {
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
let fn = DatabaseFunction("f", argumentCount: 1) { (dbValues: [DatabaseValue]) in
|
||||
return Int64.fromDatabaseValue(dbValues[0])
|
||||
}
|
||||
try dbQueue.inDatabase { db in
|
||||
db.add(function: fn)
|
||||
XCTAssertTrue(try Int64.fetchOne(db, sql: "SELECT f(NULL)") == nil)
|
||||
XCTAssertEqual(try Int64.fetchOne(db, sql: "SELECT f(1)")!, 1)
|
||||
XCTAssertEqual(try Int64.fetchOne(db, sql: "SELECT f(1.1)")!, 1)
|
||||
}
|
||||
}
|
||||
|
||||
func testFunctionArgumentDouble() throws {
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
let fn = DatabaseFunction("f", argumentCount: 1) { (dbValues: [DatabaseValue]) in
|
||||
return Double.fromDatabaseValue(dbValues[0])
|
||||
}
|
||||
try dbQueue.inDatabase { db in
|
||||
db.add(function: fn)
|
||||
XCTAssertTrue(try Double.fetchOne(db, sql: "SELECT f(NULL)") == nil)
|
||||
XCTAssertEqual(try Double.fetchOne(db, sql: "SELECT f(1)")!, 1.0)
|
||||
XCTAssertEqual(try Double.fetchOne(db, sql: "SELECT f(1.1)")!, 1.1)
|
||||
}
|
||||
}
|
||||
|
||||
func testFunctionArgumentString() throws {
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
let fn = DatabaseFunction("f", argumentCount: 1) { (dbValues: [DatabaseValue]) in
|
||||
return String.fromDatabaseValue(dbValues[0])
|
||||
}
|
||||
try dbQueue.inDatabase { db in
|
||||
db.add(function: fn)
|
||||
XCTAssertTrue(try String.fetchOne(db, sql: "SELECT f(NULL)") == nil)
|
||||
XCTAssertEqual(try String.fetchOne(db, sql: "SELECT f('foo')")!, "foo")
|
||||
}
|
||||
}
|
||||
|
||||
func testFunctionArgumentBlob() throws {
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
let fn = DatabaseFunction("f", argumentCount: 1) { (dbValues: [DatabaseValue]) in
|
||||
return Data.fromDatabaseValue(dbValues[0])
|
||||
}
|
||||
try dbQueue.inDatabase { db in
|
||||
db.add(function: fn)
|
||||
XCTAssertTrue(try Data.fetchOne(db, sql: "SELECT f(NULL)") == nil)
|
||||
XCTAssertEqual(try Data.fetchOne(db, sql: "SELECT f(?)", arguments: ["foo".data(using: .utf8)])!, "foo".data(using: .utf8))
|
||||
XCTAssertEqual(try Data.fetchOne(db, sql: "SELECT f(?)", arguments: [Data()])!, Data())
|
||||
}
|
||||
}
|
||||
|
||||
func testFunctionArgumentCustomValueType() throws {
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
let fn = DatabaseFunction("f", argumentCount: 1) { (dbValues: [DatabaseValue]) in
|
||||
return CustomValueType.fromDatabaseValue(dbValues[0])
|
||||
}
|
||||
try dbQueue.inDatabase { db in
|
||||
db.add(function: fn)
|
||||
XCTAssertTrue(try CustomValueType.fetchOne(db, sql: "SELECT f(NULL)") == nil)
|
||||
XCTAssertTrue(try CustomValueType.fetchOne(db, sql: "SELECT f('CustomValueType')") != nil)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Argument count
|
||||
|
||||
func testFunctionWithoutArgument() throws {
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
let fn = DatabaseFunction("f", argumentCount: 0) { dbValues in
|
||||
return "foo"
|
||||
}
|
||||
try dbQueue.inDatabase { db in
|
||||
db.add(function: fn)
|
||||
XCTAssertEqual(try String.fetchOne(db, sql: "SELECT f()")!, "foo")
|
||||
do {
|
||||
try db.execute(sql: "SELECT f(1)")
|
||||
XCTFail("Expected error")
|
||||
} catch let error as DatabaseError {
|
||||
XCTAssertEqual(error.resultCode, .SQLITE_ERROR)
|
||||
XCTAssertEqual(error.message!, "wrong number of arguments to function f()")
|
||||
XCTAssertEqual(error.sql!, "SELECT f(1)")
|
||||
XCTAssertEqual(error.description, "SQLite error 1: wrong number of arguments to function f() - while executing `SELECT f(1)`")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testFunctionOfOneArgument() throws {
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
let fn = DatabaseFunction("f", argumentCount: 1) { (dbValues: [DatabaseValue]) in
|
||||
String.fromDatabaseValue(dbValues[0]).map { $0.uppercased() }
|
||||
}
|
||||
try dbQueue.inDatabase { db in
|
||||
db.add(function: fn)
|
||||
XCTAssertEqual(try String.fetchOne(db, sql: "SELECT upper(?)", arguments: ["Roué"])!, "ROUé")
|
||||
XCTAssertEqual(try String.fetchOne(db, sql: "SELECT f(?)", arguments: ["Roué"])!, "ROUÉ")
|
||||
XCTAssertTrue(try String.fetchOne(db, sql: "SELECT f(NULL)") == nil)
|
||||
do {
|
||||
try db.execute(sql: "SELECT f()")
|
||||
XCTFail("Expected error")
|
||||
} catch let error as DatabaseError {
|
||||
XCTAssertEqual(error.resultCode, .SQLITE_ERROR)
|
||||
XCTAssertEqual(error.message!, "wrong number of arguments to function f()")
|
||||
XCTAssertEqual(error.sql!, "SELECT f()")
|
||||
XCTAssertEqual(error.description, "SQLite error 1: wrong number of arguments to function f() - while executing `SELECT f()`")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testFunctionOfTwoArguments() throws {
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
let fn = DatabaseFunction("f", argumentCount: 2) { dbValues in
|
||||
let ints = dbValues.compactMap { Int.fromDatabaseValue($0) }
|
||||
return ints.reduce(0, +)
|
||||
}
|
||||
try dbQueue.inDatabase { db in
|
||||
db.add(function: fn)
|
||||
XCTAssertEqual(try Int.fetchOne(db, sql: "SELECT f(1, 2)")!, 3)
|
||||
do {
|
||||
try db.execute(sql: "SELECT f()")
|
||||
XCTFail("Expected error")
|
||||
} catch let error as DatabaseError {
|
||||
XCTAssertEqual(error.resultCode, .SQLITE_ERROR)
|
||||
XCTAssertEqual(error.message!, "wrong number of arguments to function f()")
|
||||
XCTAssertEqual(error.sql!, "SELECT f()")
|
||||
XCTAssertEqual(error.description, "SQLite error 1: wrong number of arguments to function f() - while executing `SELECT f()`")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testVariadicFunction() throws {
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
let fn = DatabaseFunction("f") { dbValues in
|
||||
return dbValues.count
|
||||
}
|
||||
try dbQueue.inDatabase { db in
|
||||
db.add(function: fn)
|
||||
XCTAssertEqual(try Int.fetchOne(db, sql: "SELECT f()")!, 0)
|
||||
XCTAssertEqual(try Int.fetchOne(db, sql: "SELECT f(1)")!, 1)
|
||||
XCTAssertEqual(try Int.fetchOne(db, sql: "SELECT f(1, 1)")!, 2)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Errors
|
||||
|
||||
func testFunctionThrowingDatabaseErrorWithMessage() throws {
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
let fn = DatabaseFunction("f") { dbValues in
|
||||
throw DatabaseError(message: "custom error message")
|
||||
}
|
||||
try dbQueue.inDatabase { db in
|
||||
db.add(function: fn)
|
||||
do {
|
||||
try db.execute(sql: "SELECT f()")
|
||||
XCTFail("Expected DatabaseError")
|
||||
} catch let error as DatabaseError {
|
||||
XCTAssertEqual(error.resultCode, .SQLITE_ERROR)
|
||||
XCTAssertEqual(error.message, "custom error message")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testFunctionThrowingDatabaseErrorWithCode() throws {
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
let fn = DatabaseFunction("f") { dbValues in
|
||||
throw DatabaseError(resultCode: ResultCode(rawValue: 123))
|
||||
}
|
||||
try dbQueue.inDatabase { db in
|
||||
db.add(function: fn)
|
||||
do {
|
||||
try db.execute(sql: "SELECT f()")
|
||||
XCTFail("Expected DatabaseError")
|
||||
} catch let error as DatabaseError {
|
||||
XCTAssertEqual(error.resultCode.rawValue, 123)
|
||||
XCTAssertEqual(error.message, "unknown error")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testFunctionThrowingDatabaseErrorWithMessageAndCode() throws {
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
let fn = DatabaseFunction("f") { dbValues in
|
||||
throw DatabaseError(resultCode: ResultCode(rawValue: 123), message: "custom error message")
|
||||
}
|
||||
try dbQueue.inDatabase { db in
|
||||
db.add(function: fn)
|
||||
do {
|
||||
try db.execute(sql: "SELECT f()")
|
||||
XCTFail("Expected DatabaseError")
|
||||
} catch let error as DatabaseError {
|
||||
XCTAssertEqual(error.resultCode.rawValue, 123)
|
||||
XCTAssertEqual(error.message, "custom error message")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testFunctionThrowingCustomError() throws {
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
let fn = DatabaseFunction("f") { dbValues in
|
||||
throw NSError(domain: "CustomErrorDomain", code: 123, userInfo: [NSLocalizedDescriptionKey: "custom error message"])
|
||||
}
|
||||
try dbQueue.inDatabase { db in
|
||||
db.add(function: fn)
|
||||
do {
|
||||
try db.execute(sql: "SELECT f()")
|
||||
XCTFail("Expected DatabaseError")
|
||||
} catch let error as DatabaseError {
|
||||
XCTAssertEqual(error.resultCode, .SQLITE_ERROR)
|
||||
XCTAssertTrue(error.message!.contains("CustomErrorDomain"))
|
||||
XCTAssertTrue(error.message!.contains("123"))
|
||||
XCTAssertTrue(error.message!.contains("custom error message"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Misc
|
||||
|
||||
func testFunctionsAreClosures() throws {
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
var x = 123
|
||||
let fn = DatabaseFunction("f", argumentCount: 0) { dbValues in
|
||||
return x
|
||||
}
|
||||
try dbQueue.inDatabase { db in
|
||||
db.add(function: fn)
|
||||
x = 321
|
||||
XCTAssertEqual(try Int.fetchOne(db, sql: "SELECT f()")!, 321)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user