diff --git a/.github/workflows/ubuntu-ci.yml b/.github/workflows/ubuntu-ci.yml new file mode 100644 index 0000000000..a961c47bd6 --- /dev/null +++ b/.github/workflows/ubuntu-ci.yml @@ -0,0 +1,97 @@ +# https://github.com/actions/runner-images/blob/main/images/macos/macos-15-Readme.md + +name: "Linux CI" + +on: + push: + branches: + - master + - development + - grdb_linux_changes + paths: + - 'GRDB/**' + - 'Tests/**' + - '.github/workflows/**' + - 'Makefile' + - 'Package.swift' + - 'SQLiteCustom/src' + pull_request: + paths: + - 'GRDB/**' + - 'Tests/**' + - '.github/workflows/**' + - 'Makefile' + - 'Package.swift' + - 'SQLiteCustom/src' + +concurrency: + group: ${{ github.ref_name }} + cancel-in-progress: false +permissions: + contents: read + +jobs: + build-and-test-on-linux: + runs-on: ${{ matrix.os }} + timeout-minutes: 60 + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + swift: ["6.2", "6.1"] + steps: + - name: Checkout Repository + uses: actions/checkout@v5 + + - name: Install Swift Dependencies + run: | + sudo apt-get update + sudo apt-get install -y \ + binutils \ + git \ + gnupg2 \ + libc6-dev \ + libcurl4-openssl-dev \ + libedit2 \ + libgcc-13-dev \ + libpython3-dev \ + libsqlite3-0 \ + libstdc++-13-dev \ + libxml2-dev \ + libz3-dev \ + pkg-config \ + python3-lldb \ + tzdata \ + unzip \ + zip \ + zlib1g-dev \ + wget \ + curl \ + ca-certificates \ + util-linux + + - name: Cache Swiftly toolchains + id: cache-swiftly + uses: actions/cache@v4 + with: + path: ${SWIFTLY_HOME_DIR:-$HOME/.local/share/swiftly} + key: swiftly-${{ matrix.os }}-${{ matrix.swift }} + + - name: Install Swift Using Swiftly + if: steps.cache-swiftly.outputs.cache-hit != 'true' + run: | + curl -O https://download.swift.org/swiftly/linux/swiftly-$(uname -m).tar.gz + tar zxf swiftly-$(uname -m).tar.gz + ./swiftly init --quiet-shell-followup -y --skip-install + . "${SWIFTLY_HOME_DIR:-$HOME/.local/share/swiftly}/env.sh" + hash -r + swiftly install ${{ matrix.swift }} --assume-yes --use + . "${SWIFTLY_HOME_DIR:-$HOME/.local/share/swiftly}/env.sh" + + - name: Build SPM Package + run: | + swift build -c release + + - name: Run tests + run: | + swift test -c release diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000..35164a22f8 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "editor.formatOnSave": false, + "editor.insertSpaces": true, + "editor.tabSize": 4, + "editor.detectIndentation": false, + "editor.trimAutoWhitespace": false +} \ No newline at end of file diff --git a/GRDB/Core/DispatchQueueActor.swift b/GRDB/Core/DispatchQueueActor.swift index 3a51d8897f..93f054a30e 100644 --- a/GRDB/Core/DispatchQueueActor.swift +++ b/GRDB/Core/DispatchQueueActor.swift @@ -1,4 +1,8 @@ +#if os(Linux) +@preconcurrency import Dispatch +#else import Dispatch +#endif /// An actor that runs in a DispatchQueue. /// diff --git a/GRDB/Core/Support/CoreGraphics/CGFloat.swift b/GRDB/Core/Support/CoreGraphics/CGFloat.swift index 6b0fb6053b..99d6153506 100644 --- a/GRDB/Core/Support/CoreGraphics/CGFloat.swift +++ b/GRDB/Core/Support/CoreGraphics/CGFloat.swift @@ -1,5 +1,8 @@ #if canImport(CoreGraphics) import CoreGraphics +#elseif !canImport(Darwin) +import Foundation +#endif /// CGFloat adopts DatabaseValueConvertible extension CGFloat: DatabaseValueConvertible { @@ -15,4 +18,3 @@ extension CGFloat: DatabaseValueConvertible { return CGFloat(double) } } -#endif diff --git a/GRDB/Core/Support/Foundation/Date.swift b/GRDB/Core/Support/Foundation/Date.swift index dadc00fd65..379e3ff5b0 100644 --- a/GRDB/Core/Support/Foundation/Date.swift +++ b/GRDB/Core/Support/Foundation/Date.swift @@ -12,7 +12,6 @@ import GRDBSQLite import Foundation -#if !os(Linux) /// NSDate is stored in the database using the format /// "yyyy-MM-dd HH:mm:ss.SSS", in the UTC time zone. extension NSDate: DatabaseValueConvertible { @@ -41,7 +40,6 @@ extension NSDate: DatabaseValueConvertible { return cast(date) } } -#endif /// Date is stored in the database using the format /// "yyyy-MM-dd HH:mm:ss.SSS", in the UTC time zone. diff --git a/GRDB/Core/Support/Foundation/Decimal.swift b/GRDB/Core/Support/Foundation/Decimal.swift index 70d84f3ab7..d315fb6d45 100644 --- a/GRDB/Core/Support/Foundation/Decimal.swift +++ b/GRDB/Core/Support/Foundation/Decimal.swift @@ -1,4 +1,3 @@ -#if !os(Linux) // Import C SQLite functions #if GRDBCIPHER // CocoaPods (SQLCipher subspec) import SQLCipher @@ -68,4 +67,3 @@ extension Decimal: StatementColumnConvertible { @usableFromInline let _posixLocale = Locale(identifier: "en_US_POSIX") -#endif diff --git a/GRDB/Core/Support/Foundation/NSData.swift b/GRDB/Core/Support/Foundation/NSData.swift index 72861ac02e..12cba1a3ce 100644 --- a/GRDB/Core/Support/Foundation/NSData.swift +++ b/GRDB/Core/Support/Foundation/NSData.swift @@ -1,4 +1,3 @@ -#if !os(Linux) import Foundation /// NSData is convertible to and from DatabaseValue. @@ -24,4 +23,3 @@ extension NSData: DatabaseValueConvertible { return cast(data) } } -#endif diff --git a/GRDB/Core/Support/Foundation/NSNumber.swift b/GRDB/Core/Support/Foundation/NSNumber.swift index 1c5778c19e..3b5076fd34 100644 --- a/GRDB/Core/Support/Foundation/NSNumber.swift +++ b/GRDB/Core/Support/Foundation/NSNumber.swift @@ -1,4 +1,4 @@ -#if !os(Linux) && !os(Windows) +#if !os(Windows) import Foundation private let integerRoundingBehavior = NSDecimalNumberHandler( @@ -81,10 +81,18 @@ extension NSNumber: DatabaseValueConvertible { /// Otherwise, returns nil. public static func fromDatabaseValue(_ dbValue: DatabaseValue) -> Self? { switch dbValue.storage { + case .int64(let int64) where self is NSDecimalNumber.Type: + let number = NSDecimalNumber(value: int64) + return number as? Self case .int64(let int64): - return self.init(value: int64) + let number = NSNumber(value: int64) + return number as? Self + case .double(let double) where self is NSDecimalNumber.Type: + let number = NSDecimalNumber(value: double) + return number as? Self case .double(let double): - return self.init(value: double) + let number = NSNumber(value: double) + return number as? Self case let .string(string): // Must match Decimal.fromDatabaseValue(_:) guard let decimal = Decimal(string: string, locale: posixLocale) else { return nil } diff --git a/GRDB/Core/Support/Foundation/NSString.swift b/GRDB/Core/Support/Foundation/NSString.swift index acddddb35d..3f019ad403 100644 --- a/GRDB/Core/Support/Foundation/NSString.swift +++ b/GRDB/Core/Support/Foundation/NSString.swift @@ -1,4 +1,3 @@ -#if !os(Linux) import Foundation /// NSString adopts DatabaseValueConvertible @@ -24,4 +23,3 @@ extension NSString: DatabaseValueConvertible { return self.init(string: string) } } -#endif diff --git a/GRDB/Core/Support/Foundation/URL.swift b/GRDB/Core/Support/Foundation/URL.swift index 6db66ec3b8..b3630b14a4 100644 --- a/GRDB/Core/Support/Foundation/URL.swift +++ b/GRDB/Core/Support/Foundation/URL.swift @@ -1,12 +1,16 @@ import Foundation -#if !os(Linux) && !os(Windows) +#if !os(Windows) /// NSURL stores its absoluteString in the database. extension NSURL: DatabaseValueConvertible { /// Returns a TEXT database value containing the absolute URL. public var databaseValue: DatabaseValue { - absoluteString?.databaseValue ?? .null + #if !canImport(Darwin) + absoluteString.databaseValue + #else + absoluteString?.databaseValue ?? .null + #endif } public static func fromDatabaseValue(_ dbValue: DatabaseValue) -> Self? { diff --git a/GRDB/Core/Support/Foundation/UUID.swift b/GRDB/Core/Support/Foundation/UUID.swift index 54b18b2c80..92d4e1fcba 100644 --- a/GRDB/Core/Support/Foundation/UUID.swift +++ b/GRDB/Core/Support/Foundation/UUID.swift @@ -12,7 +12,7 @@ import GRDBSQLite import Foundation -#if !os(Linux) && !os(Windows) +#if !os(Windows) /// NSUUID adopts DatabaseValueConvertible extension NSUUID: DatabaseValueConvertible { /// Returns a BLOB database value containing the uuid bytes. @@ -36,10 +36,17 @@ extension NSUUID: DatabaseValueConvertible { switch dbValue.storage { case .blob(let data) where data.count == 16: return data.withUnsafeBytes { - self.init(uuidBytes: $0.bindMemory(to: UInt8.self).baseAddress) + #if canImport(Darwin) + self.init(uuidBytes: $0.bindMemory(to: UInt8.self).baseAddress) + #else + guard let uuidBytes = $0.bindMemory(to: UInt8.self).baseAddress else { + return nil as Self? + } + return NSUUID(uuidBytes: uuidBytes) as? Self + #endif } case .string(let string): - return self.init(uuidString: string) + return NSUUID(uuidString: string) as? Self default: return nil } @@ -91,8 +98,8 @@ extension UUID: StatementColumnConvertible { self.init(uuid: uuid.uuid) case SQLITE_BLOB: guard sqlite3_column_bytes(sqliteStatement, index) == 16, - let blob = sqlite3_column_blob(sqliteStatement, index) else - { + let blob = sqlite3_column_blob(sqliteStatement, index) + else { return nil } self.init(uuid: blob.assumingMemoryBound(to: uuid_t.self).pointee) diff --git a/Package.swift b/Package.swift index 26c61734a5..9a677d82c2 100644 --- a/Package.swift +++ b/Package.swift @@ -15,6 +15,8 @@ let darwinPlatforms: [Platform] = [ var swiftSettings: [SwiftSetting] = [ .define("SQLITE_ENABLE_FTS5"), .define("SQLITE_ENABLE_SNAPSHOT"), + // Not all Linux distributions have support for WAL snapshots. + .define("SQLITE_DISABLE_SNAPSHOT", .when(platforms: [.linux])), ] var cSettings: [CSetting] = [] var dependencies: [PackageDescription.Package.Dependency] = [] diff --git a/README.md b/README.md index 4261c65e8f..1682f59ce8 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ Swift 6.1 License CI Status + Linux Status

--- diff --git a/Tests/GRDBTests/CGFloatTests.swift b/Tests/GRDBTests/CGFloatTests.swift index 1bcfaaa582..4c9ebd93b4 100644 --- a/Tests/GRDBTests/CGFloatTests.swift +++ b/Tests/GRDBTests/CGFloatTests.swift @@ -1,7 +1,12 @@ import XCTest -import CoreGraphics import GRDB +#if canImport(CoreGraphics) +import CoreGraphics +#elseif !canImport(Darwin) +import Foundation +#endif + class CGFloatTests: GRDBTestCase { func testCGFLoat() throws { diff --git a/Tests/GRDBTests/DatabaseErrorTests.swift b/Tests/GRDBTests/DatabaseErrorTests.swift index e968b44074..d0106ad64b 100644 --- a/Tests/GRDBTests/DatabaseErrorTests.swift +++ b/Tests/GRDBTests/DatabaseErrorTests.swift @@ -216,6 +216,9 @@ class DatabaseErrorTests: GRDBTestCase { } func testNSErrorBridging() throws { +#if !canImport(Darwin) + throw XCTSkip("NSError bridging not available on non-Darwin platforms") +#else let dbQueue = try makeDatabaseQueue() try dbQueue.inDatabase { db in try db.create(table: "parents") { $0.column("id", .integer).primaryKey() } @@ -229,5 +232,6 @@ class DatabaseErrorTests: GRDBTestCase { XCTAssertNotNil(error.localizedFailureReason) } } +#endif } } diff --git a/Tests/GRDBTests/DatabaseMigratorTests.swift b/Tests/GRDBTests/DatabaseMigratorTests.swift index da09225083..1b053e2583 100644 --- a/Tests/GRDBTests/DatabaseMigratorTests.swift +++ b/Tests/GRDBTests/DatabaseMigratorTests.swift @@ -10,6 +10,9 @@ class DatabaseMigratorTests : GRDBTestCase { } func testEmptyMigratorSync() throws { +#if !canImport(Combine) + throw XCTSkip("Combine not supported on this platform") +#else func test(writer: some DatabaseWriter) throws { let migrator = DatabaseMigrator() try migrator.migrate(writer) @@ -18,9 +21,13 @@ class DatabaseMigratorTests : GRDBTestCase { try Test(test).run { try DatabaseQueue() } try Test(test).runAtTemporaryDatabasePath { try DatabaseQueue(path: $0) } try Test(test).runAtTemporaryDatabasePath { try DatabasePool(path: $0) } +#endif } func testEmptyMigratorAsync() throws { +#if !canImport(Combine) + throw XCTSkip("Combine not supported on this platform") +#else func test(writer: some DatabaseWriter) throws { let expectation = self.expectation(description: "") let migrator = DatabaseMigrator() @@ -38,9 +45,13 @@ class DatabaseMigratorTests : GRDBTestCase { try Test(test).run { try DatabaseQueue() } try Test(test).runAtTemporaryDatabasePath { try DatabaseQueue(path: $0) } try Test(test).runAtTemporaryDatabasePath { try DatabasePool(path: $0) } +#endif } func testEmptyMigratorPublisher() throws { +#if !canImport(Combine) + throw XCTSkip("Combine not supported on this platform") +#else func test(writer: some DatabaseWriter) throws { let migrator = DatabaseMigrator() let publisher = migrator.migratePublisher(writer) @@ -51,9 +62,13 @@ class DatabaseMigratorTests : GRDBTestCase { try Test(test).run { try DatabaseQueue() } try Test(test).runAtTemporaryDatabasePath { try DatabaseQueue(path: $0) } try Test(test).runAtTemporaryDatabasePath { try DatabasePool(path: $0) } +#endif } func testNonEmptyMigratorSync() throws { +#if !canImport(Combine) + throw XCTSkip("Combine not supported on this platform") +#else func test(writer: some DatabaseWriter) throws { var migrator = DatabaseMigrator() migrator.registerMigration("createPersons") { db in @@ -95,9 +110,13 @@ class DatabaseMigratorTests : GRDBTestCase { try Test(test).run { try DatabaseQueue() } try Test(test).runAtTemporaryDatabasePath { try DatabaseQueue(path: $0) } try Test(test).runAtTemporaryDatabasePath { try DatabasePool(path: $0) } +#endif } func testNonEmptyMigratorAsync() throws { +#if !canImport(Combine) + throw XCTSkip("Combine not supported on this platform") +#else func test(writer: some DatabaseWriter) throws { var migrator = DatabaseMigrator() migrator.registerMigration("createPersons") { db in @@ -146,9 +165,13 @@ class DatabaseMigratorTests : GRDBTestCase { try Test(test).run { try DatabaseQueue() } try Test(test).runAtTemporaryDatabasePath { try DatabaseQueue(path: $0) } try Test(test).runAtTemporaryDatabasePath { try DatabasePool(path: $0) } +#endif } func testNonEmptyMigratorPublisher() throws { +#if !canImport(Combine) + throw XCTSkip("Combine not supported on this platform") +#else func test(writer: some DatabaseWriter) throws { var migrator = DatabaseMigrator() migrator.registerMigration("createPersons") { db in @@ -198,9 +221,13 @@ class DatabaseMigratorTests : GRDBTestCase { try Test(test).run { try DatabaseQueue() } try Test(test).runAtTemporaryDatabasePath { try DatabaseQueue(path: $0) } try Test(test).runAtTemporaryDatabasePath { try DatabasePool(path: $0) } +#endif } func testEmptyMigratorPublisherIsAsynchronous() throws { +#if !canImport(Combine) + throw XCTSkip("Combine not supported on this platform") +#else func test(writer: some DatabaseWriter) throws { let migrator = DatabaseMigrator() let expectation = self.expectation(description: "") @@ -220,9 +247,13 @@ class DatabaseMigratorTests : GRDBTestCase { try Test(test).run { try DatabaseQueue() } try Test(test).runAtTemporaryDatabasePath { try DatabaseQueue(path: $0) } try Test(test).runAtTemporaryDatabasePath { try DatabasePool(path: $0) } +#endif } func testNonEmptyMigratorPublisherIsAsynchronous() throws { +#if !canImport(Combine) + throw XCTSkip("Combine not supported on this platform") +#else func test(writer: some DatabaseWriter) throws { var migrator = DatabaseMigrator() migrator.registerMigration("first", migrate: { _ in }) @@ -243,9 +274,13 @@ class DatabaseMigratorTests : GRDBTestCase { try Test(test).run { try DatabaseQueue() } try Test(test).runAtTemporaryDatabasePath { try DatabaseQueue(path: $0) } try Test(test).runAtTemporaryDatabasePath { try DatabasePool(path: $0) } +#endif } func testMigratorPublisherDefaultScheduler() throws { +#if !canImport(Combine) + throw XCTSkip("Combine not supported on this platform") +#else func test(writer: Writer) { var migrator = DatabaseMigrator() migrator.registerMigration("first", migrate: { _ in }) @@ -268,9 +303,13 @@ class DatabaseMigratorTests : GRDBTestCase { try Test(test).run { try DatabaseQueue() } try Test(test).runAtTemporaryDatabasePath { try DatabaseQueue(path: $0) } try Test(test).runAtTemporaryDatabasePath { try DatabasePool(path: $0) } +#endif } func testMigratorPublisherCustomScheduler() throws { +#if !canImport(Combine) + throw XCTSkip("Combine not supported on this platform") +#else func test(writer: Writer) { var migrator = DatabaseMigrator() migrator.registerMigration("first", migrate: { _ in }) @@ -294,9 +333,13 @@ class DatabaseMigratorTests : GRDBTestCase { try Test(test).run { try DatabaseQueue() } try Test(test).runAtTemporaryDatabasePath { try DatabaseQueue(path: $0) } try Test(test).runAtTemporaryDatabasePath { try DatabasePool(path: $0) } +#endif } func testMigrateUpTo() throws { +#if !canImport(Combine) + throw XCTSkip("Combine not supported on this platform") +#else func test(writer: some DatabaseWriter) throws { var migrator = DatabaseMigrator() migrator.registerMigration("a") { db in @@ -345,6 +388,7 @@ class DatabaseMigratorTests : GRDBTestCase { try Test(test).run { try DatabaseQueue() } try Test(test).runAtTemporaryDatabasePath { try DatabaseQueue(path: $0) } try Test(test).runAtTemporaryDatabasePath { try DatabasePool(path: $0) } +#endif } func testMigrationFailureTriggersRollback() throws { diff --git a/Tests/GRDBTests/DatabasePoolConcurrencyTests.swift b/Tests/GRDBTests/DatabasePoolConcurrencyTests.swift index 7d2478545b..bbdf523d44 100644 --- a/Tests/GRDBTests/DatabasePoolConcurrencyTests.swift +++ b/Tests/GRDBTests/DatabasePoolConcurrencyTests.swift @@ -877,10 +877,12 @@ class DatabasePoolConcurrencyTests: GRDBTestCase { XCTAssertEqual(db.configuration.label, nil) XCTAssertEqual(db.description, "GRDB.DatabasePool.writer") +#if canImport(Darwin) // This test CAN break in future releases: the dispatch queue labels // are documented to be a debug-only tool. let label = String(utf8String: __dispatch_queue_get_label(nil)) XCTAssertEqual(label, "GRDB.DatabasePool.writer") +#endif } let s1 = DispatchSemaphore(value: 0) @@ -890,10 +892,12 @@ class DatabasePoolConcurrencyTests: GRDBTestCase { XCTAssertEqual(db.configuration.label, nil) XCTAssertEqual(db.description, "GRDB.DatabasePool.reader.1") +#if canImport(Darwin) // This test CAN break in future releases: the dispatch queue labels // are documented to be a debug-only tool. let label = String(utf8String: __dispatch_queue_get_label(nil)) XCTAssertEqual(label, "GRDB.DatabasePool.reader.1") +#endif _ = s1.signal() _ = s2.wait(timeout: .distantFuture) @@ -906,10 +910,12 @@ class DatabasePoolConcurrencyTests: GRDBTestCase { XCTAssertEqual(db.configuration.label, nil) XCTAssertEqual(db.description, "GRDB.DatabasePool.reader.2") +#if canImport(Darwin) // This test CAN break in future releases: the dispatch queue labels // are documented to be a debug-only tool. let label = String(utf8String: __dispatch_queue_get_label(nil)) XCTAssertEqual(label, "GRDB.DatabasePool.reader.2") +#endif } } let blocks = [block1, block2] @@ -925,10 +931,12 @@ class DatabasePoolConcurrencyTests: GRDBTestCase { XCTAssertEqual(db.configuration.label, "Toreador") XCTAssertEqual(db.description, "Toreador.writer") +#if canImport(Darwin) // This test CAN break in future releases: the dispatch queue labels // are documented to be a debug-only tool. let label = String(utf8String: __dispatch_queue_get_label(nil)) XCTAssertEqual(label, "Toreador.writer") +#endif } let s1 = DispatchSemaphore(value: 0) @@ -938,10 +946,12 @@ class DatabasePoolConcurrencyTests: GRDBTestCase { XCTAssertEqual(db.configuration.label, "Toreador") XCTAssertEqual(db.description, "Toreador.reader.1") +#if canImport(Darwin) // This test CAN break in future releases: the dispatch queue labels // are documented to be a debug-only tool. let label = String(utf8String: __dispatch_queue_get_label(nil)) XCTAssertEqual(label, "Toreador.reader.1") +#endif _ = s1.signal() _ = s2.wait(timeout: .distantFuture) @@ -954,10 +964,12 @@ class DatabasePoolConcurrencyTests: GRDBTestCase { XCTAssertEqual(db.configuration.label, "Toreador") XCTAssertEqual(db.description, "Toreador.reader.2") +#if canImport(Darwin) // This test CAN break in future releases: the dispatch queue labels // are documented to be a debug-only tool. let label = String(utf8String: __dispatch_queue_get_label(nil)) XCTAssertEqual(label, "Toreador.reader.2") +#endif } } let blocks = [block1, block2] @@ -1037,6 +1049,9 @@ class DatabasePoolConcurrencyTests: GRDBTestCase { } func testQoS() throws { +#if !canImport(Darwin) + throw XCTSkip("__dispatch_get_global_queue unavailable") +#else func test(qos: DispatchQoS) throws { // https://forums.swift.org/t/what-is-the-default-target-queue-for-a-serial-queue/18094/5 // @@ -1072,6 +1087,7 @@ class DatabasePoolConcurrencyTests: GRDBTestCase { try test(qos: .background) try test(qos: .userInitiated) +#endif } // MARK: - AsyncConcurrentRead @@ -1254,6 +1270,9 @@ class DatabasePoolConcurrencyTests: GRDBTestCase { // MARK: - Concurrent opening func testConcurrentOpening() throws { +#if !canImport(Darwin) + throw XCTSkip("NSFileCoordinator unavailable") +#else for _ in 0..<50 { let dbDirectoryName = "DatabasePoolConcurrencyTests-\(ProcessInfo.processInfo.globallyUniqueString)" let directoryURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true) @@ -1277,6 +1296,7 @@ class DatabasePoolConcurrencyTests: GRDBTestCase { XCTAssert(poolError ?? coordinatorError == nil) } } +#endif } // MARK: - NSFileCoordinator sample code tests @@ -1284,6 +1304,9 @@ class DatabasePoolConcurrencyTests: GRDBTestCase { // Test for sample code in Documentation.docc/DatabaseSharing.md. // This test passes if this method compiles private func openSharedDatabase(at databaseURL: URL) throws -> DatabasePool { +#if !canImport(Darwin) + throw XCTSkip("NSFileCoordinator unavailable") +#else let coordinator = NSFileCoordinator(filePresenter: nil) var coordinatorError: NSError? var dbPool: DatabasePool? @@ -1299,6 +1322,7 @@ class DatabasePoolConcurrencyTests: GRDBTestCase { throw error } return dbPool! +#endif } // Test for sample code in Documentation.docc/DatabaseSharing.md. @@ -1313,6 +1337,9 @@ class DatabasePoolConcurrencyTests: GRDBTestCase { // Test for sample code in Documentation.docc/DatabaseSharing.md. // This test passes if this method compiles private func openSharedReadOnlyDatabase(at databaseURL: URL) throws -> DatabasePool? { +#if !canImport(Darwin) + throw XCTSkip("NSFileCoordinator unavailable") +#else let coordinator = NSFileCoordinator(filePresenter: nil) var coordinatorError: NSError? var dbPool: DatabasePool? @@ -1328,6 +1355,7 @@ class DatabasePoolConcurrencyTests: GRDBTestCase { throw error } return dbPool +#endif } // Test for sample code in Documentation.docc/DatabaseSharing.md. diff --git a/Tests/GRDBTests/DatabaseQueueTests.swift b/Tests/GRDBTests/DatabaseQueueTests.swift index c22be83261..0372d97776 100644 --- a/Tests/GRDBTests/DatabaseQueueTests.swift +++ b/Tests/GRDBTests/DatabaseQueueTests.swift @@ -129,10 +129,12 @@ class DatabaseQueueTests: GRDBTestCase { XCTAssertEqual(db.configuration.label, nil) XCTAssertEqual(db.description, "GRDB.DatabaseQueue") +#if canImport(Darwin) // This test CAN break in future releases: the dispatch queue labels // are documented to be a debug-only tool. let label = String(utf8String: __dispatch_queue_get_label(nil)) XCTAssertEqual(label, "GRDB.DatabaseQueue") +#endif } } @@ -144,10 +146,12 @@ class DatabaseQueueTests: GRDBTestCase { XCTAssertEqual(db.configuration.label, "Toreador") XCTAssertEqual(db.description, "Toreador") +#if canImport(Darwin) // This test CAN break in future releases: the dispatch queue labels // are documented to be a debug-only tool. let label = String(utf8String: __dispatch_queue_get_label(nil)) XCTAssertEqual(label, "Toreador") +#endif } } @@ -221,6 +225,9 @@ class DatabaseQueueTests: GRDBTestCase { } func testQoS() throws { +#if !canImport(Darwin) + throw XCTSkip("__dispatch_get_global_queue not available on non-Darwin platforms") +#else func test(qos: DispatchQoS) throws { // https://forums.swift.org/t/what-is-the-default-target-queue-for-a-serial-queue/18094/5 // @@ -256,6 +263,7 @@ class DatabaseQueueTests: GRDBTestCase { try test(qos: .background) try test(qos: .userInitiated) +#endif } // MARK: - SQLITE_BUSY prevention diff --git a/Tests/GRDBTests/DatabaseRegionObservationTests.swift b/Tests/GRDBTests/DatabaseRegionObservationTests.swift index 3ffdfc39eb..6338a76057 100644 --- a/Tests/GRDBTests/DatabaseRegionObservationTests.swift +++ b/Tests/GRDBTests/DatabaseRegionObservationTests.swift @@ -4,12 +4,14 @@ import GRDB class DatabaseRegionObservationTests: GRDBTestCase { // Test passes if it compiles. // See + #if canImport(Combine) func testAnyDatabaseWriter(writer: any DatabaseWriter) throws { let observation = DatabaseRegionObservation(tracking: .fullDatabase) _ = observation.start(in: writer, onError: { _ in }, onChange: { _ in }) _ = observation.publisher(in: writer) } + #endif func testDatabaseRegionObservation_FullDatabase() throws { let dbQueue = try makeDatabaseQueue() diff --git a/Tests/GRDBTests/DatabaseSnapshotTests.swift b/Tests/GRDBTests/DatabaseSnapshotTests.swift index 1cb422ecdd..b31ccea69d 100644 --- a/Tests/GRDBTests/DatabaseSnapshotTests.swift +++ b/Tests/GRDBTests/DatabaseSnapshotTests.swift @@ -230,10 +230,12 @@ class DatabaseSnapshotTests: GRDBTestCase { XCTAssertEqual(db.configuration.label, nil) XCTAssertEqual(db.description, "GRDB.DatabasePool.snapshot.1") +#if canImport(Darwin) // This test CAN break in future releases: the dispatch queue labels // are documented to be a debug-only tool. let label = String(utf8String: __dispatch_queue_get_label(nil)) XCTAssertEqual(label, "GRDB.DatabasePool.snapshot.1") +#endif } let snapshot2 = try dbPool.makeSnapshot() @@ -241,10 +243,12 @@ class DatabaseSnapshotTests: GRDBTestCase { XCTAssertEqual(db.configuration.label, nil) XCTAssertEqual(db.description, "GRDB.DatabasePool.snapshot.2") +#if canImport(Darwin) // This test CAN break in future releases: the dispatch queue labels // are documented to be a debug-only tool. let label = String(utf8String: __dispatch_queue_get_label(nil)) XCTAssertEqual(label, "GRDB.DatabasePool.snapshot.2") +#endif } } @@ -257,10 +261,12 @@ class DatabaseSnapshotTests: GRDBTestCase { XCTAssertEqual(db.configuration.label, "Toreador") XCTAssertEqual(db.description, "Toreador.snapshot.1") +#if canImport(Darwin) // This test CAN break in future releases: the dispatch queue labels // are documented to be a debug-only tool. let label = String(utf8String: __dispatch_queue_get_label(nil)) XCTAssertEqual(label, "Toreador.snapshot.1") +#endif } let snapshot2 = try dbPool.makeSnapshot() @@ -268,10 +274,12 @@ class DatabaseSnapshotTests: GRDBTestCase { XCTAssertEqual(db.configuration.label, "Toreador") XCTAssertEqual(db.description, "Toreador.snapshot.2") +#if canImport(Darwin) // This test CAN break in future releases: the dispatch queue labels // are documented to be a debug-only tool. let label = String(utf8String: __dispatch_queue_get_label(nil)) XCTAssertEqual(label, "Toreador.snapshot.2") +#endif } } diff --git a/Tests/GRDBTests/FailureTestCase.swift b/Tests/GRDBTests/FailureTestCase.swift index 9dfb9770ba..7b552aaa04 100644 --- a/Tests/GRDBTests/FailureTestCase.swift +++ b/Tests/GRDBTests/FailureTestCase.swift @@ -1,3 +1,4 @@ +#if canImport(Darwin) // Inspired by https://github.com/groue/CombineExpectations import XCTest @@ -207,3 +208,4 @@ class FailureTestCaseTests: FailureTestCase { } } } +#endif diff --git a/Tests/GRDBTests/ValueObservationRecorderTests.swift b/Tests/GRDBTests/ValueObservationRecorderTests.swift index 4b8e8a2ae0..134543db4f 100644 --- a/Tests/GRDBTests/ValueObservationRecorderTests.swift +++ b/Tests/GRDBTests/ValueObservationRecorderTests.swift @@ -1,3 +1,4 @@ +#if canImport(Darwin) import Dispatch import XCTest @@ -781,3 +782,4 @@ class ValueObservationRecorderTests: FailureTestCase { } } } +#endif diff --git a/Tests/GRDBTests/ValueObservationTests.swift b/Tests/GRDBTests/ValueObservationTests.swift index 6134d8453b..e884fc2a0e 100644 --- a/Tests/GRDBTests/ValueObservationTests.swift +++ b/Tests/GRDBTests/ValueObservationTests.swift @@ -926,6 +926,7 @@ class ValueObservationTests: GRDBTestCase { } // MARK: - Async Await +#if canImport(Combine) func testAsyncAwait_values_prefix() async throws { func test(_ writer: some DatabaseWriter) async throws { @@ -1238,6 +1239,7 @@ class ValueObservationTests: GRDBTestCase { try Test(test).runAtTemporaryDatabasePath { try DatabasePool(path: $0) } } +#endif // Regression test for func testIssue1383() throws {