mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-08-21 11:53:07 +09:00
220 lines
8.8 KiB
Swift
220 lines
8.8 KiB
Swift
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
import Foundation
|
|
import Shared
|
|
@testable import Storage
|
|
|
|
import XCTest
|
|
|
|
// TODO: rewrite this test to not use BrowserSchema. It used to use HistoryTable…
|
|
class TestSwiftData: XCTestCase {
|
|
var swiftData: SwiftData?
|
|
var urlCounter = 1
|
|
var testDB: String!
|
|
|
|
override func setUp() {
|
|
let files = MockFiles()
|
|
do {
|
|
try files.remove("testSwiftData.db")
|
|
} catch _ {
|
|
}
|
|
testDB = (try! (files.getAndEnsureDirectory() as NSString)).appendingPathComponent("testSwiftData.db")
|
|
swiftData = SwiftData(filename: testDB, schema: BrowserSchema(), files: files)
|
|
let table = BrowserSchema()
|
|
|
|
// Ensure static flags match expected values.
|
|
XCTAssert(SwiftData.ReuseConnections, "Reusing database connections")
|
|
XCTAssert(SwiftData.EnableWAL, "WAL enabled")
|
|
|
|
XCTAssertNil(addSite(table, url: "http://url0", title: "title0"), "Added url0.")
|
|
}
|
|
|
|
override func tearDown() {
|
|
// Restore static flags to their default values.
|
|
SwiftData.ReuseConnections = true
|
|
SwiftData.EnableWAL = true
|
|
}
|
|
|
|
/*
|
|
// These two tests broke after pull #427.
|
|
func testNoWALOrConnectionReuse() {
|
|
SwiftData.EnableWAL = false
|
|
SwiftData.ReuseConnections = false
|
|
var error = writeDuringRead()
|
|
XCTAssertEqual(error!.code, 5, "Got 'database is locked' error")
|
|
}
|
|
|
|
func testNoConnectionReuse() {
|
|
SwiftData.EnableWAL = true
|
|
SwiftData.ReuseConnections = false
|
|
var error = writeDuringRead()
|
|
XCTAssertNotNil(error, "Expected error during write.")
|
|
XCTAssertEqual(error?.code ?? 0, 8, "Got 'attempt to write to read-only database' error")
|
|
}
|
|
*/
|
|
|
|
func testNoWAL() {
|
|
SwiftData.EnableWAL = false
|
|
SwiftData.ReuseConnections = true
|
|
let error = writeDuringRead()
|
|
XCTAssertNil(error, "Insertion succeeded")
|
|
}
|
|
|
|
func testDefaultSettings() {
|
|
SwiftData.EnableWAL = true
|
|
SwiftData.ReuseConnections = true
|
|
let error = writeDuringRead()
|
|
XCTAssertNil(error, "Insertion succeeded")
|
|
}
|
|
|
|
func testBusyTimeout() {
|
|
SwiftData.EnableWAL = false
|
|
SwiftData.ReuseConnections = false
|
|
let error = writeDuringRead(closeTimeout: 1)
|
|
XCTAssertNil(error, "Insertion succeeded")
|
|
}
|
|
|
|
func testFilledCursor() {
|
|
SwiftData.ReuseConnections = false
|
|
SwiftData.EnableWAL = false
|
|
XCTAssertNil(writeDuringRead(true), "Insertion succeeded")
|
|
}
|
|
|
|
fileprivate func writeDuringRead(_ safeQuery: Bool = false, closeTimeout: UInt64? = nil) -> MaybeErrorType? {
|
|
|
|
// Query the database and hold the cursor.
|
|
var c: Cursor<SDRow>!
|
|
let result = swiftData!.withConnection(SwiftData.Flags.readOnly) { db -> Void in
|
|
if safeQuery {
|
|
c = db.executeQuery("SELECT * FROM history", factory: { $0 })
|
|
} else {
|
|
c = db.executeQueryUnsafe("SELECT * FROM history", factory: { $0 }, withArgs: nil)
|
|
}
|
|
return ()
|
|
}
|
|
|
|
XCTAssertNil(result.value.failureValue, "Queried database")
|
|
|
|
// If we have a live cursor, this will step to the first result.
|
|
// Stepping through a prepared statement without resetting it will lock the connection.
|
|
let _ = c[0]
|
|
|
|
// Close the cursor after a delay if there's a close timeout set.
|
|
if let closeTimeout = closeTimeout {
|
|
let queue = DispatchQueue(label: "cursor timeout queue", attributes: [])
|
|
queue.asyncAfter(deadline: DispatchTime.now() + Double(Int64(closeTimeout * NSEC_PER_SEC)) / Double(NSEC_PER_SEC)) {
|
|
c.close()
|
|
}
|
|
}
|
|
|
|
defer { urlCounter += 1 }
|
|
return addSite(BrowserSchema(), url: "http://url/\(urlCounter)", title: "title\(urlCounter)")
|
|
}
|
|
|
|
fileprivate func addSite(_ table: BrowserSchema, url: String, title: String) -> MaybeErrorType? {
|
|
let result = swiftData!.withConnection(SwiftData.Flags.readWrite) { connection -> Void in
|
|
let args: Args = [Bytes.generateGUID(), url, title]
|
|
try connection.executeChange("INSERT INTO history (guid, url, title, is_deleted, should_upload) VALUES (?, ?, ?, 0, 0)", withArgs: args)
|
|
}
|
|
|
|
return result.value.failureValue
|
|
}
|
|
|
|
func testEncrypt() {
|
|
// XXX: Something is holding an open connection to the normal database, making it impossible
|
|
// to change its encryption. This kills it so that we can move on.
|
|
let files = MockFiles()
|
|
do {
|
|
try files.remove("testSwiftData.db")
|
|
} catch _ {
|
|
}
|
|
let path = testDB
|
|
func verifyData(_ swiftData: SwiftData) -> MaybeErrorType? {
|
|
let resultDeferred = swiftData.withConnection(SwiftData.Flags.readOnly) { db -> Void in
|
|
return ()
|
|
}
|
|
return resultDeferred.value.failureValue
|
|
}
|
|
|
|
XCTAssertNotNil(SwiftData(filename: path!, schema: BrowserSchema(), files: files), "Connected to unencrypted database")
|
|
|
|
// Encrypt the database.
|
|
XCTAssertNil(verifyData(SwiftData(filename: path!, key: "Secret", schema: BrowserSchema(), files: files)), "Encrypted database")
|
|
|
|
// Now change the encryption key.
|
|
XCTAssertNil(verifyData(SwiftData(filename: path!, key: "Secret2", prevKey: "Secret", schema: BrowserSchema(), files: files)), "Re-encrypted database")
|
|
|
|
// Changing the encryption without the prevKey should fail.
|
|
XCTAssertNotNil(verifyData(SwiftData(filename: path!, schema: BrowserSchema(), files: files)), "Failed decrypting database")
|
|
|
|
// Now remove the encryption key.
|
|
XCTAssertNil(verifyData(SwiftData(filename: path!, prevKey: "Secret2", schema: BrowserSchema(), files: files)), "Decrypted database")
|
|
}
|
|
|
|
func testNulls() {
|
|
guard let db = swiftData else {
|
|
XCTFail("DB not open")
|
|
return
|
|
}
|
|
db.withConnection(SwiftData.Flags.readWriteCreate) { db in
|
|
try! db.executeChange("CREATE TABLE foo ( bar TEXT, baz INTEGER )")
|
|
try! db.executeChange("INSERT INTO foo VALUES (NULL, 1), ('here', 2)")
|
|
let shouldBeString = db.executeQuery("SELECT bar FROM foo WHERE baz = 2", factory: { (row) in row["bar"] }).asArray()[0]
|
|
guard let s = shouldBeString as? String else {
|
|
XCTFail("Couldn't cast.")
|
|
return
|
|
}
|
|
XCTAssertEqual(s, "here")
|
|
|
|
let shouldBeNull = db.executeQuery("SELECT bar FROM foo WHERE baz = 1", factory: { (row) in row["bar"] }).asArray()[0]
|
|
XCTAssertNil(shouldBeNull as? String)
|
|
XCTAssertNil(shouldBeNull)
|
|
}.succeeded()
|
|
}
|
|
|
|
func testArrayCursor() {
|
|
let data = ["One", "Two", "Three"]
|
|
let t = ArrayCursor<String>(data: data)
|
|
|
|
// Test subscript access
|
|
XCTAssertNil(t[-1], "Subscript -1 returns nil")
|
|
XCTAssertEqual(t[0]!, "One", "Subscript zero returns the correct data")
|
|
XCTAssertEqual(t[1]!, "Two", "Subscript one returns the correct data")
|
|
XCTAssertEqual(t[2]!, "Three", "Subscript two returns the correct data")
|
|
XCTAssertNil(t[3], "Subscript three returns nil")
|
|
|
|
// Test status data with default initializer
|
|
XCTAssertEqual(t.status, CursorStatus.success, "Cursor as correct status")
|
|
XCTAssertEqual(t.statusMessage, "Success", "Cursor as correct status message")
|
|
XCTAssertEqual(t.count, 3, "Cursor as correct size")
|
|
|
|
// Test generator access
|
|
var i = 0
|
|
for s in t {
|
|
XCTAssertEqual(s!, data[i], "Subscript zero returns the correct data")
|
|
i += 1
|
|
}
|
|
|
|
// Test creating a failed cursor
|
|
let t2 = ArrayCursor<String>(data: data, status: CursorStatus.failure, statusMessage: "Custom status message")
|
|
XCTAssertEqual(t2.status, CursorStatus.failure, "Cursor as correct status")
|
|
XCTAssertEqual(t2.statusMessage, "Custom status message", "Cursor as correct status message")
|
|
XCTAssertEqual(t2.count, 0, "Cursor as correct size")
|
|
|
|
// Test subscript access return nil for a failed cursor
|
|
XCTAssertNil(t2[0], "Subscript zero returns nil if failure")
|
|
XCTAssertNil(t2[1], "Subscript one returns nil if failure")
|
|
XCTAssertNil(t2[2], "Subscript two returns nil if failure")
|
|
XCTAssertNil(t2[3], "Subscript three returns nil if failure")
|
|
|
|
// Test that generator doesn't work with failed cursors
|
|
var ran = false
|
|
for s in t2 {
|
|
print("Got \(s ?? "nil")", terminator: "\n")
|
|
ran = true
|
|
}
|
|
XCTAssertFalse(ran, "for...in didn't run for failed cursor")
|
|
}
|
|
}
|