From 0b308891fb0dda4f6fe5b6b5f8ede68423e524fd Mon Sep 17 00:00:00 2001 From: Jaanus Kase Date: Wed, 22 Oct 2025 16:28:07 +0300 Subject: [PATCH 01/20] Starting modernization: bumped Swift tools, fixes --- Package.swift | 2 +- .../CKDatabaseAPI+SimulatedFail.swift | 8 +++--- .../Sources/CKDatabaseAPI/CKDatabaseAPI.swift | 2 +- .../ReplayingMockCKContainer.swift | 27 ++++++++++++------- 4 files changed, 23 insertions(+), 16 deletions(-) diff --git a/Package.swift b/Package.swift index 4d361a5..df4ea35 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version: 5.10 +// swift-tools-version: 6.2 // The swift-tools-version declares the minimum version of Swift required to build this package. import Foundation diff --git a/Targets/Canopy/Sources/CKDatabaseAPI/CKDatabaseAPI+SimulatedFail.swift b/Targets/Canopy/Sources/CKDatabaseAPI/CKDatabaseAPI+SimulatedFail.swift index 7c56c18..98a8fd7 100644 --- a/Targets/Canopy/Sources/CKDatabaseAPI/CKDatabaseAPI+SimulatedFail.swift +++ b/Targets/Canopy/Sources/CKDatabaseAPI/CKDatabaseAPI+SimulatedFail.swift @@ -22,14 +22,14 @@ extension CKDatabaseAPI { var partialErrors: [AnyHashable: Error] = [:] if let saved = recordsToSave { - saved.forEach { - partialErrors[$0.recordID] = randomCKRecordError(codes: otherCodes).ckError + for oneSaved in saved { + partialErrors[oneSaved.recordID] = randomCKRecordError(codes: otherCodes).ckError } } if let deleted = recordIDsToDelete { - deleted.forEach { - partialErrors[$0] = randomCKRecordError(codes: otherCodes).ckError + for oneDeleted in deleted { + partialErrors[oneDeleted] = randomCKRecordError(codes: otherCodes).ckError } } diff --git a/Targets/Canopy/Sources/CKDatabaseAPI/CKDatabaseAPI.swift b/Targets/Canopy/Sources/CKDatabaseAPI/CKDatabaseAPI.swift index addd489..aa98910 100644 --- a/Targets/Canopy/Sources/CKDatabaseAPI/CKDatabaseAPI.swift +++ b/Targets/Canopy/Sources/CKDatabaseAPI/CKDatabaseAPI.swift @@ -7,7 +7,7 @@ import Semaphore actor CKDatabaseAPI: CKDatabaseAPIType { private let database: CKDatabaseType private let databaseScope: CKDatabase.Scope - internal let settingsProvider: () async -> CanopySettingsType + internal let settingsProvider: @Sendable () async -> CanopySettingsType private let tokenStore: TokenStoreType private let fetchDatabaseChangesSemaphore = AsyncSemaphore(value: 1) private let fetchZoneChangesSemaphore = AsyncSemaphore(value: 1) diff --git a/Targets/CanopyTestTools/Sources/ReplayingMockCKContainer/ReplayingMockCKContainer.swift b/Targets/CanopyTestTools/Sources/ReplayingMockCKContainer/ReplayingMockCKContainer.swift index 8533c2a..2a27d02 100644 --- a/Targets/CanopyTestTools/Sources/ReplayingMockCKContainer/ReplayingMockCKContainer.swift +++ b/Targets/CanopyTestTools/Sources/ReplayingMockCKContainer/ReplayingMockCKContainer.swift @@ -34,7 +34,8 @@ public actor ReplayingMockCKContainer { } } - func privateFetchUserRecordID(completionHandler: @escaping (CKRecord.ID?, Error?) -> Void) { + // Private helpers now return Sendable results rather than invoking closures. + private func privateFetchUserRecordID() -> (CKRecord.ID?, Error?) { guard let operationResult = userRecordIDResults.first, case let .userRecordID(result) = operationResult else { fatalError("Asked to fetch user record ID without an available result. Likely a logic error on caller side") } @@ -42,13 +43,13 @@ public actor ReplayingMockCKContainer { operationsRun += 1 if let error = result.recordError { - completionHandler(nil, error.ckError) + return (nil, error.ckError) } else { - completionHandler(result.userRecordIDArchive!.recordIDs.first!, nil) + return (result.userRecordIDArchive!.recordIDs.first!, nil) } } - func privateAccountStatus(completionHandler: @escaping (CKAccountStatus, Error?) -> Void) { + private func privateAccountStatus() -> (CKAccountStatus, Error?) { guard let operationResult = accountStatusResults.first, case let .accountStatus(result) = operationResult else { fatalError("Asked for account status without an available result. Likely a logic error on caller side") } @@ -62,10 +63,10 @@ public actor ReplayingMockCKContainer { operationsRun += 1 if let error = result.canopyError { - completionHandler(.couldNotDetermine, error.ckError) + return (.couldNotDetermine, error.ckError) } else { if let accountStatus = CKAccountStatus(rawValue: result.statusValue) { - completionHandler(accountStatus, nil) + return (accountStatus, nil) } else { fatalError("Could not recreate CKAccountStatus from value \(result.statusValue)") } @@ -94,15 +95,21 @@ public actor ReplayingMockCKContainer { } extension ReplayingMockCKContainer: CKContainerType { - public nonisolated func accountStatus(completionHandler: @escaping (CKAccountStatus, Error?) -> Void) { + public nonisolated func accountStatus(completionHandler: @escaping @Sendable (CKAccountStatus, Error?) -> Void) { Task { - await privateAccountStatus(completionHandler: completionHandler) + // Enter the actor to compute the result without sending the closure across domains. + let (status, error) = await self.privateAccountStatus() + // Call the completion handler from the current task context (non-actor). + completionHandler(status, error) } } - public nonisolated func fetchUserRecordID(completionHandler: @escaping (CKRecord.ID?, Error?) -> Void) { + public nonisolated func fetchUserRecordID(completionHandler: @escaping @Sendable (CKRecord.ID?, Error?) -> Void) { Task { - await privateFetchUserRecordID(completionHandler: completionHandler) + // Enter the actor to compute the result without sending the closure across domains. + let (recordID, error) = await self.privateFetchUserRecordID() + // Call the completion handler from the current task context (non-actor). + completionHandler(recordID, error) } } From 60912a637c6671bf839977e8a7af2612c9f185fd Mon Sep 17 00:00:00 2001 From: Jaanus Kase Date: Wed, 22 Oct 2025 16:31:53 +0300 Subject: [PATCH 02/20] Updated dependencies --- Package.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Package.swift b/Package.swift index df4ea35..2b99a33 100644 --- a/Package.swift +++ b/Package.swift @@ -7,11 +7,11 @@ import PackageDescription var dependencies: [PackageDescription.Package.Dependency] = [ .package( url: "https://github.com/groue/Semaphore", - from: "0.0.8" + from: "0.1.0" ), .package( url: "https://github.com/pointfreeco/swift-dependencies", - from: "1.0.0" + from: "1.10.0" ) ] From 785cd8a9f0d77b023a43860b45c49cd15f7eb630 Mon Sep 17 00:00:00 2001 From: Jaanus Kase Date: Wed, 22 Oct 2025 16:33:55 +0300 Subject: [PATCH 03/20] Bumped OS requirement, removed availability gates --- Package.swift | 2 +- .../Sources/CKDatabaseAPI/CKDatabaseAPI+SimulatedFail.swift | 1 - Targets/Canopy/Sources/CKDatabaseAPI/CKDatabaseAPI.swift | 1 - Targets/Canopy/Sources/Canopy/Canopy.swift | 1 - Targets/Canopy/Sources/Canopy/MockCanopyWithCKMocks.swift | 1 - Targets/Canopy/Sources/Dependency/Canopy+Dependency.swift | 2 -- Targets/Canopy/Tests/CanopyTests.swift | 1 - Targets/Canopy/Tests/DatabaseAPITests.swift | 1 - Targets/Canopy/Tests/DependencyTests.swift | 1 - Targets/Canopy/Tests/FetchDatabaseChangesTests.swift | 1 - Targets/Canopy/Tests/FetchZoneChangesTests.swift | 1 - Targets/Canopy/Tests/ModifyRecordsTests.swift | 1 - Targets/Canopy/Tests/QueryRecordsFeatureTests.swift | 1 - Targets/Canopy/Tests/SerialFetchChangesTests.swift | 1 - 14 files changed, 1 insertion(+), 15 deletions(-) diff --git a/Package.swift b/Package.swift index 2b99a33..8369f00 100644 --- a/Package.swift +++ b/Package.swift @@ -30,7 +30,7 @@ if ProcessInfo.processInfo.environment["SPI_BUILDER"] == "1" { let package = Package( name: "Canopy", defaultLocalization: "en", - platforms: [.iOS(.v15), .macOS(.v12)], + platforms: [.iOS(.v18), .macOS(.v15)], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. .library(name: "Canopy", targets: ["Canopy"]), diff --git a/Targets/Canopy/Sources/CKDatabaseAPI/CKDatabaseAPI+SimulatedFail.swift b/Targets/Canopy/Sources/CKDatabaseAPI/CKDatabaseAPI+SimulatedFail.swift index 98a8fd7..4b23f70 100644 --- a/Targets/Canopy/Sources/CKDatabaseAPI/CKDatabaseAPI+SimulatedFail.swift +++ b/Targets/Canopy/Sources/CKDatabaseAPI/CKDatabaseAPI+SimulatedFail.swift @@ -1,6 +1,5 @@ import CloudKit -@available(iOS 16.4, macOS 13.3, *) extension CKDatabaseAPI { func randomCKRecordError( codes: Set, diff --git a/Targets/Canopy/Sources/CKDatabaseAPI/CKDatabaseAPI.swift b/Targets/Canopy/Sources/CKDatabaseAPI/CKDatabaseAPI.swift index aa98910..ec67107 100644 --- a/Targets/Canopy/Sources/CKDatabaseAPI/CKDatabaseAPI.swift +++ b/Targets/Canopy/Sources/CKDatabaseAPI/CKDatabaseAPI.swift @@ -3,7 +3,6 @@ import Foundation import os.log import Semaphore -@available(iOS 16.4, macOS 13.3, *) actor CKDatabaseAPI: CKDatabaseAPIType { private let database: CKDatabaseType private let databaseScope: CKDatabase.Scope diff --git a/Targets/Canopy/Sources/Canopy/Canopy.swift b/Targets/Canopy/Sources/Canopy/Canopy.swift index e576471..37fc15c 100644 --- a/Targets/Canopy/Sources/Canopy/Canopy.swift +++ b/Targets/Canopy/Sources/Canopy/Canopy.swift @@ -6,7 +6,6 @@ import Foundation /// You construct Canopy with injected CloudKit container and databases, token store, and settings provider. /// Canopy has reasonable defaults for all of these, and you need to only override the ones that need to use /// a different value from the default. -@available(iOS 16.4, macOS 13.3, *) public actor Canopy: CanopyType { private let containerProvider: @Sendable () -> CKContainerType private let publicCloudDatabaseProvider: @Sendable () -> CKDatabaseType diff --git a/Targets/Canopy/Sources/Canopy/MockCanopyWithCKMocks.swift b/Targets/Canopy/Sources/Canopy/MockCanopyWithCKMocks.swift index 1ca5fb9..f462543 100644 --- a/Targets/Canopy/Sources/Canopy/MockCanopyWithCKMocks.swift +++ b/Targets/Canopy/Sources/Canopy/MockCanopyWithCKMocks.swift @@ -15,7 +15,6 @@ import CloudKit /// MockCanopyWithCKMocks is mostly appropriate to use as a testing tool for Canopy’s /// own logic, or when you need to inject your own Canopy settings for various behaviors. /// For using in your own tests, `MockCanopy` is more appropriate and simpler to use. -@available(iOS 16.4, macOS 13.3, *) public struct MockCanopyWithCKMocks: CanopyType { private let mockPrivateCKDatabase: CKDatabaseType? private let mockSharedCKDatabase: CKDatabaseType? diff --git a/Targets/Canopy/Sources/Dependency/Canopy+Dependency.swift b/Targets/Canopy/Sources/Dependency/Canopy+Dependency.swift index 2feab68..1d3f5a1 100644 --- a/Targets/Canopy/Sources/Dependency/Canopy+Dependency.swift +++ b/Targets/Canopy/Sources/Dependency/Canopy+Dependency.swift @@ -1,11 +1,9 @@ import Dependencies -@available(iOS 16.4, macOS 13.3, *) private enum CanopyKey: DependencyKey, Sendable { static let liveValue: CanopyType = Canopy() } -@available(iOS 16.4, macOS 13.3, *) public extension DependencyValues { /// Canopy packaged as CloudKit dependency via swift-dependencies. var cloudKit: CanopyType { diff --git a/Targets/Canopy/Tests/CanopyTests.swift b/Targets/Canopy/Tests/CanopyTests.swift index 856559a..2784070 100644 --- a/Targets/Canopy/Tests/CanopyTests.swift +++ b/Targets/Canopy/Tests/CanopyTests.swift @@ -3,7 +3,6 @@ import CanopyTestTools import CloudKit import XCTest -@available(iOS 16.4, macOS 13.3, *) final class CanopyTests: XCTestCase { func test_init_with_default_settings() async { let _ = Canopy( diff --git a/Targets/Canopy/Tests/DatabaseAPITests.swift b/Targets/Canopy/Tests/DatabaseAPITests.swift index 03ccac2..3f13d65 100644 --- a/Targets/Canopy/Tests/DatabaseAPITests.swift +++ b/Targets/Canopy/Tests/DatabaseAPITests.swift @@ -7,7 +7,6 @@ import XCTest /// Contains most Canopy database API tests. /// /// Some tests are in individual test classes (fetch changes). -@available(iOS 16.4, macOS 13.3, *) final class DatabaseAPITests: XCTestCase { private func databaseAPI(_ db: CKDatabaseType, settings: CanopySettingsType = CanopySettings()) -> CKDatabaseAPIType { CKDatabaseAPI(database: db, databaseScope: .private, settingsProvider: { settings }, tokenStore: TestTokenStore()) diff --git a/Targets/Canopy/Tests/DependencyTests.swift b/Targets/Canopy/Tests/DependencyTests.swift index 50c8059..3d2a723 100644 --- a/Targets/Canopy/Tests/DependencyTests.swift +++ b/Targets/Canopy/Tests/DependencyTests.swift @@ -4,7 +4,6 @@ import CloudKit import Dependencies import XCTest -@available(iOS 16.4, macOS 13.3, *) final class DependencyTests: XCTestCase { struct Fetcher { @Dependency(\.cloudKit) private var canopy diff --git a/Targets/Canopy/Tests/FetchDatabaseChangesTests.swift b/Targets/Canopy/Tests/FetchDatabaseChangesTests.swift index 7d682d3..090838b 100644 --- a/Targets/Canopy/Tests/FetchDatabaseChangesTests.swift +++ b/Targets/Canopy/Tests/FetchDatabaseChangesTests.swift @@ -4,7 +4,6 @@ import CloudKit import Foundation import XCTest -@available(iOS 16.4, macOS 13.3, *) final class FetchDatabaseChangesTests: XCTestCase { func test_success() async { let changedRecordZoneID1 = CKRecordZone.ID(zoneName: "changedZone1", ownerName: CKCurrentUserDefaultName) diff --git a/Targets/Canopy/Tests/FetchZoneChangesTests.swift b/Targets/Canopy/Tests/FetchZoneChangesTests.swift index bf32cdd..596819d 100644 --- a/Targets/Canopy/Tests/FetchZoneChangesTests.swift +++ b/Targets/Canopy/Tests/FetchZoneChangesTests.swift @@ -4,7 +4,6 @@ import CloudKit import Foundation import XCTest -@available(iOS 16.4, macOS 13.3, *) final class FetchZoneChangesTests: XCTestCase { func test_success() async { let changedRecordID = CKRecord.ID(recordName: "SomeRecordName") diff --git a/Targets/Canopy/Tests/ModifyRecordsTests.swift b/Targets/Canopy/Tests/ModifyRecordsTests.swift index 100c7b3..9d4dea5 100644 --- a/Targets/Canopy/Tests/ModifyRecordsTests.swift +++ b/Targets/Canopy/Tests/ModifyRecordsTests.swift @@ -3,7 +3,6 @@ import CanopyTestTools import CloudKit import XCTest -@available(iOS 16.4, macOS 13.3, *) final class ModifyRecordsTests: XCTestCase { private func databaseAPI(_ db: CKDatabaseType, settings: CanopySettingsType = CanopySettings()) -> CKDatabaseAPIType { CKDatabaseAPI(database: db, databaseScope: .private, settingsProvider: { settings }, tokenStore: TestTokenStore()) diff --git a/Targets/Canopy/Tests/QueryRecordsFeatureTests.swift b/Targets/Canopy/Tests/QueryRecordsFeatureTests.swift index eab52bd..5ad9d0f 100644 --- a/Targets/Canopy/Tests/QueryRecordsFeatureTests.swift +++ b/Targets/Canopy/Tests/QueryRecordsFeatureTests.swift @@ -4,7 +4,6 @@ import CloudKit import Foundation import XCTest -@available(iOS 17, macOS 14, *) final class QueryRecordsFeatureTests: XCTestCase { func records(startIndex: Int, endIndex: Int) -> [CKRecord] { stride(from: startIndex, to: endIndex + 1, by: 1).map { i in diff --git a/Targets/Canopy/Tests/SerialFetchChangesTests.swift b/Targets/Canopy/Tests/SerialFetchChangesTests.swift index ef6d43c..95513c6 100644 --- a/Targets/Canopy/Tests/SerialFetchChangesTests.swift +++ b/Targets/Canopy/Tests/SerialFetchChangesTests.swift @@ -18,7 +18,6 @@ import XCTest /// fetches. Next fetches wait for previous ones to finish. /// /// These tests make sure that this behavior is correct. -@available(iOS 16.4, macOS 13.3, *) final class SerialFetchChangesTests: XCTestCase { /// A token store that balances calls to getting and storing tokens. /// From 66cd71934c89fc5f519f3223b1206a0b90c62014 Mon Sep 17 00:00:00 2001 From: Jaanus Kase Date: Wed, 22 Oct 2025 16:35:29 +0300 Subject: [PATCH 04/20] Bumped CircleCI Xcode version --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 016bff9..c3b5137 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -4,14 +4,14 @@ version: 2.1 jobs: build: macos: - xcode: "16.0.0" + xcode: "26.0.0" steps: - checkout - run: swift build -v test: macos: - xcode: "16.0.0" + xcode: "26.0.0" steps: - checkout - run: swift test -v From cf00b0279e3a8ff07af7ff2cc771ce5685f0b35c Mon Sep 17 00:00:00 2001 From: Jaanus Kase Date: Wed, 22 Oct 2025 16:41:49 +0300 Subject: [PATCH 05/20] CircleCi xcode bump --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c3b5137..80261ba 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -4,14 +4,14 @@ version: 2.1 jobs: build: macos: - xcode: "26.0.0" + xcode: "26.0.1" steps: - checkout - run: swift build -v test: macos: - xcode: "26.0.0" + xcode: "26.0.1" steps: - checkout - run: swift test -v From 688979440d0b974bbcd9155430448ab64ae2c020 Mon Sep 17 00:00:00 2001 From: Jaanus Kase Date: Wed, 22 Oct 2025 16:44:08 +0300 Subject: [PATCH 06/20] Use another CircleCI resource class --- .circleci/config.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 80261ba..4661cdb 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -5,6 +5,7 @@ jobs: build: macos: xcode: "26.0.1" + resource_class: m4pro.medium steps: - checkout - run: swift build -v @@ -12,6 +13,7 @@ jobs: test: macos: xcode: "26.0.1" + resource_class: m4pro.medium steps: - checkout - run: swift test -v From 8a3d814fd3535942f80ab388f72dd4f8e310b547 Mon Sep 17 00:00:00 2001 From: Jaanus Kase Date: Wed, 22 Oct 2025 16:52:31 +0300 Subject: [PATCH 07/20] Started migrating to Swift Testing --- .../Tests/CanopyResultRecordTests.swift | 126 +++++++++--------- 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/Targets/Canopy/Tests/CanopyResultRecordTests.swift b/Targets/Canopy/Tests/CanopyResultRecordTests.swift index ebfb43c..0d24358 100644 --- a/Targets/Canopy/Tests/CanopyResultRecordTests.swift +++ b/Targets/Canopy/Tests/CanopyResultRecordTests.swift @@ -1,29 +1,29 @@ @testable import Canopy import CanopyTestTools import CloudKit -import XCTest +import Testing -final class CanopyResultRecordTests: XCTestCase { - func test_init_with_ckrecord() { +@Suite final class CanopyResultRecordTests { + @Test func test_init_with_ckrecord() { let ckRecord = CKRecord(recordType: "SomeRecordType", recordID: .init(recordName: "someRecordName")) ckRecord["textValue"] = "someTextValue" ckRecord.encryptedValues["encryptedTextValue"] = "someEncryptedTextValue" let canopyResultRecord = CanopyResultRecord(ckRecord: ckRecord) - XCTAssertEqual(canopyResultRecord.recordType, "SomeRecordType") - XCTAssertEqual(canopyResultRecord.recordID.recordName, "someRecordName") - XCTAssertEqual(canopyResultRecord["textValue"] as? String, "someTextValue") - XCTAssertEqual(canopyResultRecord.encryptedValues["encryptedTextValue"] as? String, "someEncryptedTextValue") - XCTAssertNil(canopyResultRecord.recordChangeTag) - XCTAssertNil(canopyResultRecord.creationDate) - XCTAssertNil(canopyResultRecord.modificationDate) - XCTAssertNil(canopyResultRecord.creatorUserRecordID) - XCTAssertNil(canopyResultRecord.lastModifiedUserRecordID) - XCTAssertNil(canopyResultRecord.parent) - XCTAssertNil(canopyResultRecord.share) + #expect(canopyResultRecord.recordType == "SomeRecordType") + #expect(canopyResultRecord.recordID.recordName == "someRecordName") + #expect(canopyResultRecord["textValue"] as? String == "someTextValue") + #expect(canopyResultRecord.encryptedValues["encryptedTextValue"] as? String == "someEncryptedTextValue") + #expect(canopyResultRecord.recordChangeTag == nil) + #expect(canopyResultRecord.creationDate == nil) + #expect(canopyResultRecord.modificationDate == nil) + #expect(canopyResultRecord.creatorUserRecordID == nil) + #expect(canopyResultRecord.lastModifiedUserRecordID == nil) + #expect(canopyResultRecord.parent == nil) + #expect(canopyResultRecord.share == nil) } - func test_init_with_mock() { + @Test func test_init_with_mock() { let creationDate = Date() let modificationDate = Date() let parent = CKRecord.Reference(recordID: .init(recordName: "parentRecordID"), action: .none) @@ -42,39 +42,39 @@ final class CanopyResultRecordTests: XCTestCase { encryptedValues: ["encryptedTextValue": "someEncryptedTextValue"] ) let canopyResultRecord = CanopyResultRecord(mock: mockRecord) - XCTAssertEqual(canopyResultRecord.recordID.recordName, "mockRecordName") - XCTAssertEqual(canopyResultRecord.recordType, "MockRecord") - XCTAssertEqual(canopyResultRecord.creatorUserRecordID!.recordName, "creatorRecordID") - XCTAssertEqual(canopyResultRecord.lastModifiedUserRecordID!.recordName, "modifierRecordID") - XCTAssertEqual(canopyResultRecord.creationDate, creationDate) - XCTAssertEqual(canopyResultRecord.modificationDate, modificationDate) - XCTAssertEqual(canopyResultRecord["textValue"] as? String, "someTextValue") - XCTAssertEqual(canopyResultRecord.encryptedValues["encryptedTextValue"] as? String, "someEncryptedTextValue") - XCTAssertEqual(canopyResultRecord.recordChangeTag, "changeTag") - XCTAssertEqual(canopyResultRecord.parent!.recordID.recordName, "parentRecordID") - XCTAssertEqual(canopyResultRecord.share!.recordID.recordName, "shareRecordID") + #expect(canopyResultRecord.recordID.recordName == "mockRecordName") + #expect(canopyResultRecord.recordType == "MockRecord") + #expect(canopyResultRecord.creatorUserRecordID!.recordName == "creatorRecordID") + #expect(canopyResultRecord.lastModifiedUserRecordID!.recordName == "modifierRecordID") + #expect(canopyResultRecord.creationDate == creationDate) + #expect(canopyResultRecord.modificationDate == modificationDate) + #expect(canopyResultRecord["textValue"] as? String == "someTextValue") + #expect(canopyResultRecord.encryptedValues["encryptedTextValue"] as? String == "someEncryptedTextValue") + #expect(canopyResultRecord.recordChangeTag == "changeTag") + #expect(canopyResultRecord.parent!.recordID.recordName == "parentRecordID") + #expect(canopyResultRecord.share!.recordID.recordName == "shareRecordID") } - func test_codes_ckrecord() throws { + @Test func test_codes_ckrecord() throws { let ckRecord = CKRecord(recordType: "SomeRecordType", recordID: .init(recordName: "someRecordName")) ckRecord["textValue"] = "someTextValue" ckRecord.encryptedValues["encryptedTextValue"] = "someEncryptedTextValue" let canopyResultRecord = CanopyResultRecord(ckRecord: ckRecord) let coded = try JSONEncoder().encode(canopyResultRecord) let decodedRecord = try JSONDecoder().decode(CanopyResultRecord.self, from: coded) - XCTAssertEqual(decodedRecord.recordType, "SomeRecordType") - XCTAssertEqual(decodedRecord.recordID.recordName, "someRecordName") + #expect(decodedRecord.recordType == "SomeRecordType") + #expect(decodedRecord.recordID.recordName == "someRecordName") } - func test_codes_mock() throws { + @Test func test_codes_mock() throws { let mock = MockCanopyResultRecord(recordType: "MockRecordType") let canopyResultRecord = CanopyResultRecord(mock: mock) let coded = try JSONEncoder().encode(canopyResultRecord) let decodedRecord = try JSONDecoder().decode(CanopyResultRecord.self, from: coded) - XCTAssertEqual(decodedRecord.recordType, "MockRecordType") + #expect(decodedRecord.recordType == "MockRecordType") } - func test_throws_on_invalid_type() { + @Test func test_throws_on_invalid_type() { let badJson = "{\"backingValue\":{\"recordType\":\"MockRecordType\",\"encryptedValuesStore\":[],\"valuesStore\":[],\"recordID\":\"deadbeef\"},\"type\":\"badType\"}" let data = badJson.data(using: .utf8)! do { @@ -83,14 +83,14 @@ final class CanopyResultRecordTests: XCTestCase { let dataCorruptedError = error as! DecodingError switch dataCorruptedError { case .dataCorrupted(let context): - XCTAssertEqual(context.debugDescription, "Invalid backing value type: badType") + #expect(context.debugDescription == "Invalid backing value type: badType") default: - XCTFail("Unexpected error: \(dataCorruptedError)") + Issue.record("Unexpected error: \(dataCorruptedError)") } } } - func test_throws_on_bad_ckrecord_data() { + @Test func test_throws_on_bad_ckrecord_data() { let badJson = "{\"type\":\"ckRecord\",\"backingValue\":\"deadbeef\"}" let data = badJson.data(using: .utf8)! do { @@ -99,31 +99,31 @@ final class CanopyResultRecordTests: XCTestCase { let dataCorruptedError = error as! DecodingError switch dataCorruptedError { case .dataCorrupted(let context): - XCTAssertEqual(context.debugDescription, "Invalid data for CKRecord") + #expect(context.debugDescription == "Invalid data for CKRecord") default: - XCTFail("Unexpected error: \(dataCorruptedError)") + Issue.record("Unexpected error: \(dataCorruptedError)") } } } - func test_returns_real_ckshare() { + @Test func test_returns_real_ckshare() { let record = CanopyResultRecord(ckRecord: CKShare.mock_owned_by_current_user) let share = record.asCKShare! - XCTAssertEqual(share.participants.count, 3) + #expect(share.participants.count == 3) } - func test_does_not_return_ckshare_for_mock() { + @Test func test_does_not_return_ckshare_for_mock() { let record = CanopyResultRecord(mock: .init(recordType: "SomeType")) - XCTAssertNil(record.asCKShare) + #expect(record.asCKShare == nil) } - func test_does_not_return_ckshare_for_ckrecord() { + @Test func test_does_not_return_ckshare_for_ckrecord() { let ckRecord = CKRecord(recordType: "SomeType", recordID: .init(recordName: "recordName")) let record = CanopyResultRecord(ckRecord: ckRecord) - XCTAssertNil(record.asCKShare) + #expect(record.asCKShare == nil) } - func test_equatable() { + @Test func test_equatable() { let ckRecord1 = CKRecord(recordType: "SomeType", recordID: .init(recordName: "recordName")) let record1 = CanopyResultRecord(ckRecord: ckRecord1) let record2 = CanopyResultRecord(ckRecord: ckRecord1) @@ -139,54 +139,54 @@ final class CanopyResultRecordTests: XCTestCase { recordType: "Type1" ) ) - XCTAssertEqual(record1, record2) - XCTAssertEqual(mockRecord1, mockRecord2) - XCTAssertNotEqual(record1, mockRecord1) + #expect(record1 == record2) + #expect(mockRecord1 == mockRecord2) + #expect(record1 != mockRecord1) } - func test_boolforkey_bool_ckrecord() throws { + @Test func test_boolforkey_bool_ckrecord() throws { let ckRecord = CKRecord(recordType: "SomeType", recordID: .init(recordName: "recordName")) ckRecord["myBool"] = true let record = CanopyResultRecord(ckRecord: ckRecord) - let boolValue = try XCTUnwrap(record.boolForKey("myBool")) - XCTAssertTrue(boolValue) + let boolValue = record.boolForKey("myBool") + #expect(boolValue == true) } - func test_boolforkey_bool_mock() throws { + @Test func test_boolforkey_bool_mock() throws { let mock = MockCanopyResultRecord(recordType: "SomeType", values: ["myBool": true]) let record = CanopyResultRecord(mock: mock) - let boolValue = try XCTUnwrap(record.boolForKey("myBool")) - XCTAssertTrue(boolValue) + let boolValue = record.boolForKey("myBool") + #expect(boolValue == true) } - func test_boolforkey_int() throws { + @Test func test_boolforkey_int() throws { let ckRecord = CKRecord(recordType: "SomeType", recordID: .init(recordName: "recordName")) ckRecord["myInt64"] = Int64(1) let record = CanopyResultRecord(ckRecord: ckRecord) - let boolValue = try XCTUnwrap(record.boolForKey("myInt64")) - XCTAssertTrue(boolValue) + let boolValue = record.boolForKey("myInt64") + #expect(boolValue == true) } - func test_boolforkey_int_false() throws { + @Test func test_boolforkey_int_false() throws { let ckRecord = CKRecord(recordType: "SomeType", recordID: .init(recordName: "recordName")) ckRecord["myInt"] = Int(0) let record = CanopyResultRecord(ckRecord: ckRecord) - let boolValue = try XCTUnwrap(record.boolForKey("myInt")) - XCTAssertFalse(boolValue) + let boolValue = record.boolForKey("myInt") + #expect(boolValue == false) } - func test_boolforkey_missing() throws { + @Test func test_boolforkey_missing() throws { let ckRecord = CKRecord(recordType: "SomeType", recordID: .init(recordName: "recordName")) ckRecord["myInt"] = Int(0) let record = CanopyResultRecord(ckRecord: ckRecord) - XCTAssertNil(record.boolForKey("nonexistentValue")) + #expect(record.boolForKey("nonexistentValue") == nil) } - func test_boolforkey_string() { + @Test func test_boolforkey_string() { let ckRecord = CKRecord(recordType: "SomeType", recordID: .init(recordName: "recordName")) ckRecord["stringValue"] = "Hello" let record = CanopyResultRecord(ckRecord: ckRecord) - XCTAssertNil(record.boolForKey("stringValue")) + #expect(record.boolForKey("stringValue") == nil) } } From d67d53b7d79dd1a8a9dc13e336f5508cd63ef0c8 Mon Sep 17 00:00:00 2001 From: Jaanus Kase Date: Thu, 23 Oct 2025 11:42:22 +0300 Subject: [PATCH 08/20] Migrate to Swift Testing --- .../Tests/CKDatabaseScopeExtensionTests.swift | 16 ++++++------ .../Tests/CKRecordZoneIDExtensionTests.swift | 12 ++++----- .../Tests/CanopyResultRecordTests.swift | 2 +- Targets/Canopy/Tests/CanopyTests.swift | 25 +++++++++---------- 4 files changed, 27 insertions(+), 28 deletions(-) diff --git a/Targets/Canopy/Tests/CKDatabaseScopeExtensionTests.swift b/Targets/Canopy/Tests/CKDatabaseScopeExtensionTests.swift index 605d176..d8d6e18 100644 --- a/Targets/Canopy/Tests/CKDatabaseScopeExtensionTests.swift +++ b/Targets/Canopy/Tests/CKDatabaseScopeExtensionTests.swift @@ -1,17 +1,17 @@ @testable import Canopy import CloudKit -import XCTest +import Testing -final class CKDatabaseScopeExtensionTests: XCTestCase { - func test_private_scope() { - XCTAssertEqual(CKDatabase.Scope.private.asString, "private") +@Suite struct CKDatabaseScopeExtensionTests { + @Test func test_private_scope() { + #expect(CKDatabase.Scope.private.asString == "private") } - func test_shared_scope() { - XCTAssertEqual(CKDatabase.Scope.shared.asString, "shared") + @Test func test_shared_scope() { + #expect(CKDatabase.Scope.shared.asString == "shared") } - func test_public_scope() { - XCTAssertEqual(CKDatabase.Scope.public.asString, "public") + @Test func test_public_scope() { + #expect(CKDatabase.Scope.public.asString == "public") } } diff --git a/Targets/Canopy/Tests/CKRecordZoneIDExtensionTests.swift b/Targets/Canopy/Tests/CKRecordZoneIDExtensionTests.swift index ff5d339..dccd729 100644 --- a/Targets/Canopy/Tests/CKRecordZoneIDExtensionTests.swift +++ b/Targets/Canopy/Tests/CKRecordZoneIDExtensionTests.swift @@ -1,15 +1,15 @@ @testable import Canopy import CloudKit -import XCTest +import Testing -final class CKRecordZoneIDExtensionTests: XCTestCase { - func test_private_zone() { +@Suite struct CKRecordZoneIDExtensionTests { + @Test func test_private_zone() { let privateZoneID = CKRecordZone.ID(zoneName: "someZone", ownerName: CKCurrentUserDefaultName) - XCTAssertEqual(privateZoneID.ckDatabaseScope, .private) + #expect(privateZoneID.ckDatabaseScope == .private) } - func test_shared_zone() { + @Test func test_shared_zone() { let sharedZoneID = CKRecordZone.ID(zoneName: "someSharedZone", ownerName: "someOtherPerson") - XCTAssertEqual(sharedZoneID.ckDatabaseScope, .shared) + #expect(sharedZoneID.ckDatabaseScope == .shared) } } diff --git a/Targets/Canopy/Tests/CanopyResultRecordTests.swift b/Targets/Canopy/Tests/CanopyResultRecordTests.swift index 0d24358..385f3ae 100644 --- a/Targets/Canopy/Tests/CanopyResultRecordTests.swift +++ b/Targets/Canopy/Tests/CanopyResultRecordTests.swift @@ -3,7 +3,7 @@ import CanopyTestTools import CloudKit import Testing -@Suite final class CanopyResultRecordTests { +@Suite struct CanopyResultRecordTests { @Test func test_init_with_ckrecord() { let ckRecord = CKRecord(recordType: "SomeRecordType", recordID: .init(recordName: "someRecordName")) ckRecord["textValue"] = "someTextValue" diff --git a/Targets/Canopy/Tests/CanopyTests.swift b/Targets/Canopy/Tests/CanopyTests.swift index 2784070..3124a6f 100644 --- a/Targets/Canopy/Tests/CanopyTests.swift +++ b/Targets/Canopy/Tests/CanopyTests.swift @@ -1,10 +1,10 @@ @testable import Canopy import CanopyTestTools import CloudKit -import XCTest +import Testing -final class CanopyTests: XCTestCase { - func test_init_with_default_settings() async { +@Suite struct CanopyTests { + @Test func test_init_with_default_settings() async { let _ = Canopy( container: ReplayingMockCKContainer(), publicCloudDatabase: ReplayingMockCKDatabase(), @@ -14,7 +14,7 @@ final class CanopyTests: XCTestCase { ) } - func test_settings_provider_uses_modified_value() async { + @Test func test_settings_provider_uses_modified_value() async { let changedRecordID = CKRecord.ID(recordName: "SomeRecordName") let changedRecord = CKRecord(recordType: "TestRecord", recordID: changedRecordID) @@ -64,8 +64,8 @@ final class CanopyTests: XCTestCase { // First request will succeed. let result1 = try! await api.modifyRecords(saving: [changedRecord]).get() - XCTAssertTrue(result1.savedRecords.count == 1) - XCTAssertTrue(result1.savedRecords[0].isEqualToRecord(changedRecord.canopyResultRecord)) + #expect(result1.savedRecords.count == 1) + #expect(result1.savedRecords[0].isEqualToRecord(changedRecord.canopyResultRecord) == true) // Second request will fail after modifying the settings. await modifiableSettings.setModifyRecordsBehavior(behavior: .simulatedFail(nil)) @@ -73,13 +73,12 @@ final class CanopyTests: XCTestCase { do { let _ = try await api.modifyRecords(saving: [changedRecord]).get() } catch { - XCTAssertNotNil(error) // Error is CKRecordError, so we use a CKRecordError API to test it - XCTAssertEqual(error.batchErrors, [:]) + #expect(error.batchErrors == [:]) } } - func test_returns_same_api_instances() async { + @Test func test_returns_same_api_instances() async { let canopy = Canopy( container: ReplayingMockCKContainer(), publicCloudDatabase: ReplayingMockCKDatabase(), @@ -89,19 +88,19 @@ final class CanopyTests: XCTestCase { let privateApi1 = await canopy.databaseAPI(usingDatabaseScope: .private) as! CKDatabaseAPI let privateApi2 = await canopy.databaseAPI(usingDatabaseScope: .private) as! CKDatabaseAPI - XCTAssertTrue(privateApi1 === privateApi2) + #expect(privateApi1 === privateApi2) let publicApi1 = await canopy.databaseAPI(usingDatabaseScope: .public) as! CKDatabaseAPI let publicApi2 = await canopy.databaseAPI(usingDatabaseScope: .public) as! CKDatabaseAPI - XCTAssertTrue(publicApi1 === publicApi2) + #expect(publicApi1 === publicApi2) let sharedApi1 = await canopy.databaseAPI(usingDatabaseScope: .shared) as! CKDatabaseAPI let sharedApi2 = await canopy.databaseAPI(usingDatabaseScope: .shared) as! CKDatabaseAPI - XCTAssertTrue(sharedApi1 === sharedApi2) + #expect(sharedApi1 === sharedApi2) let containerApi1 = await canopy.containerAPI() as! CKContainerAPI let containerApi2 = await canopy.containerAPI() as! CKContainerAPI - XCTAssertTrue(containerApi1 === containerApi2) + #expect(containerApi1 === containerApi2) } } From fc33bc20f8ab30f12ba75a6baf76b608645b1206 Mon Sep 17 00:00:00 2001 From: Jaanus Kase Date: Thu, 23 Oct 2025 11:51:11 +0300 Subject: [PATCH 09/20] Swift Testing --- Targets/Canopy/Tests/ContainerAPITests.swift | 62 ++++++------- Targets/Canopy/Tests/DatabaseAPITests.swift | 92 ++++++++++---------- 2 files changed, 77 insertions(+), 77 deletions(-) diff --git a/Targets/Canopy/Tests/ContainerAPITests.swift b/Targets/Canopy/Tests/ContainerAPITests.swift index 37b0ff5..602ef82 100644 --- a/Targets/Canopy/Tests/ContainerAPITests.swift +++ b/Targets/Canopy/Tests/ContainerAPITests.swift @@ -2,10 +2,10 @@ import CanopyTestTools import CloudKit import Foundation -import XCTest +import Testing -final class ContainerAPITests: XCTestCase { - func test_userRecordID_success() async { +@Suite struct ContainerAPITests { + @Test func test_userRecordID_success() async { let recordID = CKRecord.ID(recordName: "SomeUserID") let container = ReplayingMockCKContainer( operationResults: [ @@ -19,10 +19,10 @@ final class ContainerAPITests: XCTestCase { ) let containerAPI = CKContainerAPI(container: container, accountChangedSequence: .mock(elementsToProduce: 0)) let result = try? await containerAPI.userRecordID.get() - XCTAssertEqual(result, recordID) + #expect(result == recordID) } - func test_userRecordID_failure() async { + @Test func test_userRecordID_failure() async { let ckError = CKError(CKError.Code.networkUnavailable) let container = ReplayingMockCKContainer( operationResults: [ @@ -33,11 +33,11 @@ final class ContainerAPITests: XCTestCase { do { let _ = try await containerAPI.userRecordID.get() } catch { - XCTAssertEqual(error, CKRecordError(from: ckError)) + #expect(error == CKRecordError(from: ckError)) } } - func test_accountStatus_success() async { + @Test func test_accountStatus_success() async { let container = ReplayingMockCKContainer( operationResults: [ .accountStatus(.init(status: .available, error: nil)) @@ -45,10 +45,10 @@ final class ContainerAPITests: XCTestCase { ) let containerAPI = CKContainerAPI(container: container, accountChangedSequence: .mock(elementsToProduce: 0)) let result = try? await containerAPI.accountStatus.get() - XCTAssertEqual(result, .available) + #expect(result == .available) } - func test_accountStatus_success_multiple_requests() async { + @Test func test_accountStatus_success_multiple_requests() async { let container = ReplayingMockCKContainer( operationResults: [ .accountStatus(.init(status: .available, error: nil)) @@ -56,17 +56,17 @@ final class ContainerAPITests: XCTestCase { ) let containerAPI = CKContainerAPI(container: container, accountChangedSequence: .mock(elementsToProduce: 0)) let result = try! await containerAPI.accountStatus.get() - XCTAssertEqual(result, .available) + #expect(result == .available) // Try more requests, so there are more requests than inputs. // mock accountStatus API returns the last status without dequeueing it. let result2 = try! await containerAPI.accountStatus.get() let result3 = try! await containerAPI.accountStatus.get() - XCTAssertEqual(result2, .available) - XCTAssertEqual(result3, .available) + #expect(result2 == .available) + #expect(result3 == .available) } - func test_accountStatus_failure() async { + @Test func test_accountStatus_failure() async { let container = ReplayingMockCKContainer( operationResults: [ .accountStatus( @@ -81,11 +81,11 @@ final class ContainerAPITests: XCTestCase { do { let _ = try await containerAPI.accountStatus.get() } catch { - XCTAssertEqual(error, .ckAccountError("The operation couldn’t be completed. (CKErrorDomain error 36.)", CKError.Code.accountTemporarilyUnavailable.rawValue)) + #expect(error == .ckAccountError("The operation couldn’t be completed. (CKErrorDomain error 36.)", CKError.Code.accountTemporarilyUnavailable.rawValue)) } } - func test_accountStatus_stream() async { + @Test func test_accountStatus_stream() async { // This test was sometimes failing. The cause was that the account statuses were sometimes delivered out of order. // https://github.com/tact/Canopy/issues/6 // Got a repeatable scenario by running this as a stress test with many iterations. @@ -109,10 +109,10 @@ final class ContainerAPITests: XCTestCase { if statuses.count == 3 { break } } let expectedStatuses: [CKAccountStatus] = [.available, .noAccount, .restricted] - XCTAssertEqual(statuses, expectedStatuses) + #expect(statuses == expectedStatuses) } - func test_accountStatus_twoStreams() async { + @Test func test_accountStatus_twoStreams() async { let container = ReplayingMockCKContainer( operationResults: [ .accountStatus(.init(status: .available, error: nil)), @@ -129,7 +129,7 @@ final class ContainerAPITests: XCTestCase { do { let _ = try await containerAPI.accountStatusStream.get() } catch { - XCTAssertEqual(error, .onlyOneAccountStatusStreamSupported) + #expect(error == .onlyOneAccountStatusStreamSupported) } for await status in stream1 { @@ -138,10 +138,10 @@ final class ContainerAPITests: XCTestCase { } let expected: [CKAccountStatus] = [.available, .noAccount, .restricted] - XCTAssertEqual(statuses1, expected) + #expect(statuses1 == expected) } - func test_fetch_share_participants_success() async { + @Test func test_fetch_share_participants_success() async { let lookupInfo1 = CKUserIdentity.LookupInfo(emailAddress: "email@example.com") let lookupInfo2 = CKUserIdentity.LookupInfo(emailAddress: "email2@example.com") @@ -167,10 +167,10 @@ final class ContainerAPITests: XCTestCase { let containerAPI = CKContainerAPI(container: mockContainer, accountChangedSequence: .mock(elementsToProduce: 0)) let participants = try? await containerAPI.fetchShareParticipants(with: [lookupInfo1, lookupInfo2]).get() - XCTAssertEqual(participants, [CKShare.Participant.mock, CKShare.Participant.mock]) + #expect(participants == [CKShare.Participant.mock, CKShare.Participant.mock]) } - func test_fetch_share_participants_record_error() async { + @Test func test_fetch_share_participants_record_error() async { let lookupInfo1 = CKUserIdentity.LookupInfo(emailAddress: "email@example.com") let lookupInfo2 = CKUserIdentity.LookupInfo(emailAddress: "email2@example.com") @@ -198,11 +198,11 @@ final class ContainerAPITests: XCTestCase { do { let _ = try await containerAPI.fetchShareParticipants(with: [lookupInfo1, lookupInfo2]).get() } catch { - XCTAssertEqual(error, CKRecordError(from: CKError(CKError.Code.badContainer))) + #expect(error == CKRecordError(from: CKError(CKError.Code.badContainer))) } } - func test_fetch_share_participants_result_error() async { + @Test func test_fetch_share_participants_result_error() async { let lookupInfo1 = CKUserIdentity.LookupInfo(emailAddress: "email@example.com") let lookupInfo2 = CKUserIdentity.LookupInfo(emailAddress: "email2@example.com") @@ -230,11 +230,11 @@ final class ContainerAPITests: XCTestCase { do { let _ = try await containerAPI.fetchShareParticipants(with: [lookupInfo1, lookupInfo2]).get() } catch { - XCTAssertEqual(error, CKRecordError(from: CKError(CKError.Code.networkFailure))) + #expect(error == CKRecordError(from: CKError(CKError.Code.networkFailure))) } } - func test_accept_shares_success() async { + @Test func test_accept_shares_success() async { let mockContainer = ReplayingMockCKContainer( operationResults: [ .acceptShares( @@ -253,10 +253,10 @@ final class ContainerAPITests: XCTestCase { let containerAPI = CKContainerAPI(container: mockContainer, accountChangedSequence: .mock(elementsToProduce: 0)) let shares = try! await containerAPI.acceptShares(with: [CKShare.Metadata.mock, CKShare.Metadata.mock]).get() - XCTAssertEqual(shares.count, 2) + #expect(shares.count == 2) } - func test_accept_shares_record_failure() async { + @Test func test_accept_shares_record_failure() async { let mockContainer = ReplayingMockCKContainer( operationResults: [ .acceptShares( @@ -277,11 +277,11 @@ final class ContainerAPITests: XCTestCase { do { let _ = try await containerAPI.acceptShares(with: [CKShare.Metadata.mock, CKShare.Metadata.mock]).get() } catch { - XCTAssertEqual(error, CKRecordError(from: CKError(CKError.Code.networkUnavailable))) + #expect(error == CKRecordError(from: CKError(CKError.Code.networkUnavailable))) } } - func test_accept_shares_result_failure() async { + @Test func test_accept_shares_result_failure() async { let mockContainer = ReplayingMockCKContainer( operationResults: [ .acceptShares( @@ -302,7 +302,7 @@ final class ContainerAPITests: XCTestCase { do { let _ = try await containerAPI.acceptShares(with: [CKShare.Metadata.mock, CKShare.Metadata.mock]).get() } catch { - XCTAssertEqual(error, CKRecordError(from: CKError(CKError.Code.badContainer))) + #expect(error == CKRecordError(from: CKError(CKError.Code.badContainer))) } } } diff --git a/Targets/Canopy/Tests/DatabaseAPITests.swift b/Targets/Canopy/Tests/DatabaseAPITests.swift index 3f13d65..ef9475f 100644 --- a/Targets/Canopy/Tests/DatabaseAPITests.swift +++ b/Targets/Canopy/Tests/DatabaseAPITests.swift @@ -2,23 +2,23 @@ import CanopyTestTools import CloudKit import Foundation -import XCTest +import Testing /// Contains most Canopy database API tests. /// /// Some tests are in individual test classes (fetch changes). -final class DatabaseAPITests: XCTestCase { +@Suite struct DatabaseAPITests { private func databaseAPI(_ db: CKDatabaseType, settings: CanopySettingsType = CanopySettings()) -> CKDatabaseAPIType { CKDatabaseAPI(database: db, databaseScope: .private, settingsProvider: { settings }, tokenStore: TestTokenStore()) } - func test_init_with_default_settings() async { + @Test func test_init_with_default_settings() async { let databaseAPI = CKDatabaseAPI(database: ReplayingMockCKDatabase(), databaseScope: .private, tokenStore: TestTokenStore()) let fetchDatabaseChangesBehavior = await databaseAPI.settingsProvider().fetchDatabaseChangesBehavior - XCTAssertEqual(fetchDatabaseChangesBehavior, .regular(nil)) + #expect(fetchDatabaseChangesBehavior == .regular(nil)) } - func test_query_records() async { + @Test func test_query_records() async { let recordID = CKRecord.ID(recordName: "TestRecordName") let record = CKRecord(recordType: "TestRecord", recordID: recordID) let db = ReplayingMockCKDatabase( @@ -41,10 +41,10 @@ final class DatabaseAPITests: XCTestCase { let query = CKQuery(recordType: "TestRecord", predicate: NSPredicate(value: true)) let result = try! await api.queryRecords(with: query, in: nil).get() - XCTAssertTrue(result.first!.isEqualToRecord(record.canopyResultRecord)) + #expect(result.first!.isEqualToRecord(record.canopyResultRecord) == true) } - func test_delete_records_success() async { + @Test func test_delete_records_success() async { let recordID = CKRecord.ID(recordName: "TestRecordName") let record = CKRecord(recordType: "TestRecord", recordID: recordID) let db = ReplayingMockCKDatabase( @@ -75,10 +75,10 @@ final class DatabaseAPITests: XCTestCase { let api = databaseAPI(db) let query = CKQuery(recordType: "TestRecord", predicate: NSPredicate(value: true)) let result = try! await api.deleteRecords(with: query, in: nil).get() - XCTAssertEqual(result.deletedRecordIDs, [recordID]) + #expect(result.deletedRecordIDs == [recordID]) } - func test_delete_records_query_failure() async { + @Test func test_delete_records_query_failure() async { let recordID = CKRecord.ID(recordName: "TestRecordName") let record = CKRecord(recordType: "TestRecord", recordID: recordID) let db = ReplayingMockCKDatabase( @@ -102,11 +102,11 @@ final class DatabaseAPITests: XCTestCase { do { let _ = try await api.deleteRecords(with: query, in: nil).get() } catch { - XCTAssertEqual(error, CKRecordError(from: CKError(CKError.Code.notAuthenticated))) + #expect(error == CKRecordError(from: CKError(CKError.Code.notAuthenticated))) } } - func test_delete_records_empty_success() async { + @Test func test_delete_records_empty_success() async { // When there are no records returned by query, // the deletion should still report a success, since there is no work to be done. let db = ReplayingMockCKDatabase( @@ -123,10 +123,10 @@ final class DatabaseAPITests: XCTestCase { let api = databaseAPI(db) let query = CKQuery(recordType: "TestRecord", predicate: NSPredicate(value: true)) let result = try! await api.deleteRecords(with: query, in: nil).get() - XCTAssertEqual(result.deletedRecordIDs, []) + #expect(result.deletedRecordIDs == []) } - func test_fetch_records_success() async { + @Test func test_fetch_records_success() async { let recordID = CKRecord.ID(recordName: "testRecord") let record = CKRecord(recordType: "TestRecord", recordID: recordID) let db = ReplayingMockCKDatabase(operationResults: [ @@ -141,10 +141,10 @@ final class DatabaseAPITests: XCTestCase { ]) let api = databaseAPI(db) let result = try! await api.fetchRecords(with: [recordID]).get() - XCTAssertTrue(result.foundRecords.first!.isEqualToRecord(record.canopyResultRecord)) + #expect(result.foundRecords.first!.isEqualToRecord(record.canopyResultRecord) == true) } - func test_fetch_records_record_failure() async { + @Test func test_fetch_records_record_failure() async { let recordID = CKRecord.ID(recordName: "testRecord") let db = ReplayingMockCKDatabase(operationResults: [ .fetch( @@ -160,11 +160,11 @@ final class DatabaseAPITests: XCTestCase { do { let _ = try await api.fetchRecords(with: [recordID]).get() } catch { - XCTAssertEqual(error , CKRecordError(from: CKError(CKError.Code.notAuthenticated))) + #expect(error == CKRecordError(from: CKError(CKError.Code.notAuthenticated))) } } - func test_fetch_records_result_failure() async { + @Test func test_fetch_records_result_failure() async { let recordID = CKRecord.ID(recordName: "testRecord") let record = CKRecord(recordType: "TestRecord", recordID: recordID) let db = ReplayingMockCKDatabase(operationResults: [ @@ -181,11 +181,11 @@ final class DatabaseAPITests: XCTestCase { do { let _ = try await api.fetchRecords(with: [recordID]).get() } catch { - XCTAssertEqual(error , CKRecordError(from: CKError(CKError.Code.managedAccountRestricted))) + #expect(error == CKRecordError(from: CKError(CKError.Code.managedAccountRestricted))) } } - func test_fetch_records_not_found() async { + @Test func test_fetch_records_not_found() async { let recordID = CKRecord.ID(recordName: "testRecord") let recordID2 = CKRecord.ID(recordName: "testRecord2") let record = CKRecord(recordType: "TestRecord", recordID: recordID) @@ -202,11 +202,11 @@ final class DatabaseAPITests: XCTestCase { ]) let api = databaseAPI(db) let fetchResult = try! await api.fetchRecords(with: [recordID]).get() - XCTAssertTrue(fetchResult.foundRecords.first!.isEqualToRecord(record.canopyResultRecord)) - XCTAssertEqual(fetchResult.notFoundRecordIDs, [recordID2]) + #expect(fetchResult.foundRecords.first!.isEqualToRecord(record.canopyResultRecord) == true) + #expect(fetchResult.notFoundRecordIDs == [recordID2]) } - func test_modify_zones_success() async { + @Test func test_modify_zones_success() async { let zoneToSave = CKRecordZone(zoneID: .init(zoneName: "SomeZone")) let zoneIDToDelete = CKRecordZone.ID(zoneName: "ZoneToDelete") let db = ReplayingMockCKDatabase(operationResults: [ @@ -224,11 +224,11 @@ final class DatabaseAPITests: XCTestCase { ]) let api = databaseAPI(db) let result = try! await api.modifyZones(saving: [zoneToSave], deleting: [zoneIDToDelete]).get() - XCTAssertEqual(result.deletedZoneIDs.first!, zoneIDToDelete) - XCTAssertTrue(result.savedZones.first!.isEqualToZone(zoneToSave)) + #expect(result.deletedZoneIDs.first == zoneIDToDelete) + #expect(result.savedZones.first!.isEqualToZone(zoneToSave) == true) } - func test_modify_zones_save_failure() async { + @Test func test_modify_zones_save_failure() async { let zoneToSave = CKRecordZone(zoneID: .init(zoneName: "SomeZone")) let zoneIDToDelete = CKRecordZone.ID(zoneName: "ZoneToDelete") let db = ReplayingMockCKDatabase(operationResults: [ @@ -248,11 +248,11 @@ final class DatabaseAPITests: XCTestCase { do { let _ = try await api.modifyZones(saving: [zoneToSave], deleting: [zoneIDToDelete]).get() } catch { - XCTAssertEqual(error , CKRecordZoneError(from: CKError(CKError.Code.networkUnavailable))) + #expect(error == CKRecordZoneError(from: CKError(CKError.Code.networkUnavailable))) } } - func test_modify_zones_delete_failure() async { + @Test func test_modify_zones_delete_failure() async { let zoneToSave = CKRecordZone(zoneID: .init(zoneName: "SomeZone")) let zoneIDToDelete = CKRecordZone.ID(zoneName: "ZoneToDelete") let db = ReplayingMockCKDatabase(operationResults: [ @@ -272,11 +272,11 @@ final class DatabaseAPITests: XCTestCase { do { let _ = try await api.modifyZones(saving: [zoneToSave], deleting: [zoneIDToDelete]).get() } catch { - XCTAssertEqual(error , CKRecordZoneError(from: CKError(CKError.Code.accountTemporarilyUnavailable))) + #expect(error == CKRecordZoneError(from: CKError(CKError.Code.accountTemporarilyUnavailable))) } } - func test_modify_zones_operation_failure() async { + @Test func test_modify_zones_operation_failure() async { let zoneToSave = CKRecordZone(zoneID: .init(zoneName: "SomeZone")) let zoneIDToDelete = CKRecordZone.ID(zoneName: "ZoneToDelete") let db = ReplayingMockCKDatabase(operationResults: [ @@ -296,11 +296,11 @@ final class DatabaseAPITests: XCTestCase { do { let _ = try await api.modifyZones(saving: [zoneToSave], deleting: [zoneIDToDelete]).get() } catch { - XCTAssertEqual(error , CKRecordZoneError(from: CKError(CKError.Code.invalidArguments))) + #expect(error == CKRecordZoneError(from: CKError(CKError.Code.invalidArguments))) } } - func test_fetch_all_zones_success() async { + @Test func test_fetch_all_zones_success() async { let mockZone = CKRecordZone(zoneID: .init(zoneName: "MockZone", ownerName: CKCurrentUserDefaultName)) let db = ReplayingMockCKDatabase(operationResults: [ .fetchZones( @@ -314,10 +314,10 @@ final class DatabaseAPITests: XCTestCase { ]) let api = databaseAPI(db) let result = try! await api.fetchAllZones(qualityOfService: .default).get() - XCTAssertTrue(result.first!.isEqualToZone(mockZone)) + #expect(result.first!.isEqualToZone(mockZone) == true) } - func test_fetch_zones_success() async { + @Test func test_fetch_zones_success() async { let mockZone = CKRecordZone(zoneID: .init(zoneName: "MockZone", ownerName: CKCurrentUserDefaultName)) let db = ReplayingMockCKDatabase(operationResults: [ .fetchZones( @@ -331,10 +331,10 @@ final class DatabaseAPITests: XCTestCase { ]) let api = databaseAPI(db) let result = try! await api.fetchAllZones(qualityOfService: .default).get() - XCTAssertTrue(result.first!.isEqualToZone(mockZone)) + #expect(result.first!.isEqualToZone(mockZone) == true) } - func test_fetch_zones_one_failure() async { + @Test func test_fetch_zones_one_failure() async { let mockZone = CKRecordZone(zoneID: .init(zoneName: "MockZone", ownerName: CKCurrentUserDefaultName)) let db = ReplayingMockCKDatabase(operationResults: [ .fetchZones( @@ -350,11 +350,11 @@ final class DatabaseAPITests: XCTestCase { do { let _ = try await api.fetchZones(with: [mockZone.zoneID]).get() } catch { - XCTAssertEqual(error , CKRecordZoneError(from: CKError(CKError.Code.badDatabase))) + #expect(error == CKRecordZoneError(from: CKError(CKError.Code.badDatabase))) } } - func test_fetch_zones_result_failure() async { + @Test func test_fetch_zones_result_failure() async { let mockZone = CKRecordZone(zoneID: .init(zoneName: "MockZone", ownerName: CKCurrentUserDefaultName)) let db = ReplayingMockCKDatabase(operationResults: [ .fetchZones( @@ -370,11 +370,11 @@ final class DatabaseAPITests: XCTestCase { do { let _ = try await api.fetchZones(with: [mockZone.zoneID]).get() } catch { - XCTAssertEqual(error , CKRecordZoneError(from: CKError(CKError.Code.zoneNotFound))) + #expect(error == CKRecordZoneError(from: CKError(CKError.Code.zoneNotFound))) } } - func test_modify_subscriptions_success() async { + @Test func test_modify_subscriptions_success() async { let subscriptionID = CKSubscription.ID("DBSubscription") let subscriptionIDToDelete = CKSubscription.ID("DBSubscriptionToDelete") let subscription = CKDatabaseSubscription(subscriptionID: subscriptionID) @@ -395,10 +395,10 @@ final class DatabaseAPITests: XCTestCase { ) let api = databaseAPI(db) let result = try! await api.modifySubscriptions(saving: [subscription]).get() - XCTAssertEqual(result, .init(savedSubscriptions: [subscription], deletedSubscriptionIDs: [subscriptionIDToDelete])) + #expect(result == .init(savedSubscriptions: [subscription], deletedSubscriptionIDs: [subscriptionIDToDelete])) } - func test_modify_subscriptions_save_failure() async { + @Test func test_modify_subscriptions_save_failure() async { let subscriptionID = CKSubscription.ID("DBSubscription") let subscriptionIDToDelete = CKSubscription.ID("DBSubscriptionToDelete") let subscription = CKDatabaseSubscription(subscriptionID: subscriptionID) @@ -421,11 +421,11 @@ final class DatabaseAPITests: XCTestCase { do { let _ = try await api.modifySubscriptions(saving: [subscription]).get() } catch { - XCTAssertEqual(error , CKSubscriptionError(from: CKError(CKError.Code.badDatabase))) + #expect(error == CKSubscriptionError(from: CKError(CKError.Code.badDatabase))) } } - func test_modify_subscriptions_delete_failure() async { + @Test func test_modify_subscriptions_delete_failure() async { let subscriptionID = CKSubscription.ID("DBSubscription") let subscriptionIDToDelete = CKSubscription.ID("DBSubscriptionToDelete") let subscription = CKDatabaseSubscription(subscriptionID: subscriptionID) @@ -448,11 +448,11 @@ final class DatabaseAPITests: XCTestCase { do { let _ = try await api.modifySubscriptions(saving: [subscription]).get() } catch { - XCTAssertEqual(error , CKSubscriptionError(from: CKError(CKError.Code.badDatabase))) + #expect(error == CKSubscriptionError(from: CKError(CKError.Code.badDatabase))) } } - func test_modify_subscriptions_operation_failure() async { + @Test func test_modify_subscriptions_operation_failure() async { let subscriptionID = CKSubscription.ID("DBSubscription") let subscriptionIDToDelete = CKSubscription.ID("DBSubscriptionToDelete") let subscription = CKDatabaseSubscription(subscriptionID: subscriptionID) @@ -475,7 +475,7 @@ final class DatabaseAPITests: XCTestCase { do { let _ = try await api.modifySubscriptions(saving: [subscription]).get() } catch { - XCTAssertEqual(error , CKSubscriptionError(from: CKError(CKError.Code.badDatabase))) + #expect(error == CKSubscriptionError(from: CKError(CKError.Code.badDatabase))) } } } From 58b1b6369899670854abb19808089f75ed8f11dc Mon Sep 17 00:00:00 2001 From: Jaanus Kase Date: Thu, 23 Oct 2025 12:04:43 +0300 Subject: [PATCH 10/20] Migrate to Swift Testing --- Targets/Canopy/Tests/DependencyTests.swift | 8 ++-- .../FetchDatabaseChangesResultTests.swift | 38 +++++++-------- .../Tests/FetchDatabaseChangesTests.swift | 48 +++++++++---------- .../Tests/FetchRecordsResultTests.swift | 14 +++--- .../Tests/FetchZoneChangesResultTests.swift | 16 +++---- .../Tests/SerialFetchChangesTests.swift | 12 ++--- 6 files changed, 68 insertions(+), 68 deletions(-) diff --git a/Targets/Canopy/Tests/DependencyTests.swift b/Targets/Canopy/Tests/DependencyTests.swift index 3d2a723..bc0f18d 100644 --- a/Targets/Canopy/Tests/DependencyTests.swift +++ b/Targets/Canopy/Tests/DependencyTests.swift @@ -2,9 +2,9 @@ import Canopy import CanopyTestTools import CloudKit import Dependencies -import XCTest +import Testing -final class DependencyTests: XCTestCase { +@Suite struct DependencyTests { struct Fetcher { @Dependency(\.cloudKit) private var canopy func fetchRecord(recordID: CKRecord.ID) async -> CanopyResultRecord? { @@ -14,7 +14,7 @@ final class DependencyTests: XCTestCase { } } - func test_dependency() async { + @Test func test_dependency() async { let fetcher = withDependencies { let testRecordID = CKRecord.ID(recordName: "testRecordID") let testRecord = CKRecord(recordType: "TestRecord", recordID: testRecordID) @@ -42,6 +42,6 @@ final class DependencyTests: XCTestCase { Fetcher() } let record = await fetcher.fetchRecord(recordID: .init(recordName: "testRecordID"))! - XCTAssertEqual(record["testKey"] as! String, "testValue") + #expect(record["testKey"] as! String == "testValue") } } diff --git a/Targets/Canopy/Tests/FetchDatabaseChangesResultTests.swift b/Targets/Canopy/Tests/FetchDatabaseChangesResultTests.swift index b793909..6020da9 100644 --- a/Targets/Canopy/Tests/FetchDatabaseChangesResultTests.swift +++ b/Targets/Canopy/Tests/FetchDatabaseChangesResultTests.swift @@ -1,9 +1,9 @@ import Canopy import CloudKit -import XCTest +import Testing -final class FetchDatabaseChangesResultTests: XCTestCase { - func test_codes() throws { +@Suite struct FetchDatabaseChangesResultTests { + @Test func test_codes() throws { let result = FetchDatabaseChangesResult( changedRecordZoneIDs: [.init(zoneName: "changedZone", ownerName: "owner1")], deletedRecordZoneIDs: [.init(zoneName: "deletedZone", ownerName: "owner2")], @@ -11,51 +11,51 @@ final class FetchDatabaseChangesResultTests: XCTestCase { ) let coded = try JSONEncoder().encode(result) let decoded = try JSONDecoder().decode(FetchDatabaseChangesResult.self, from: coded) - XCTAssertEqual(decoded.changedRecordZoneIDs[0].zoneName, "changedZone") - XCTAssertEqual(decoded.deletedRecordZoneIDs[0].zoneName, "deletedZone") - XCTAssertEqual(decoded.purgedRecordZoneIDs[0].zoneName, "purgedZone") + #expect(decoded.changedRecordZoneIDs[0].zoneName == "changedZone") + #expect(decoded.deletedRecordZoneIDs[0].zoneName == "deletedZone") + #expect(decoded.purgedRecordZoneIDs[0].zoneName == "purgedZone") } - func test_empty() { + @Test func test_empty() { let result = FetchDatabaseChangesResult.empty - XCTAssertTrue(result.changedRecordZoneIDs.isEmpty) - XCTAssertTrue(result.deletedRecordZoneIDs.isEmpty) - XCTAssertTrue(result.purgedRecordZoneIDs.isEmpty) + #expect(result.changedRecordZoneIDs.isEmpty == true) + #expect(result.deletedRecordZoneIDs.isEmpty == true) + #expect(result.purgedRecordZoneIDs.isEmpty == true) } - func test_throws_on_changed_data_error() throws { + @Test func test_throws_on_changed_data_error() throws { let badJson = "{\"deletedRecordZoneIDs\":\"YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGnCwwSHB0eJFUkbnVsbNINDg8RWk5TLm9iamVjdHNWJGNsYXNzoRCAAoAG1RMUFRYOFxgZGhtfEBBkYXRhYmFzZVNjb3BlS2V5XxARYW5vbnltb3VzQ0tVc2VySURZb3duZXJOYW1lWFpvbmVOYW1lEACAAIAEgAOABVtkZWxldGVkWm9uZVZvd25lcjLSHyAhIlokY2xhc3NuYW1lWCRjbGFzc2VzXkNLUmVjb3JkWm9uZUlEoiEjWE5TT2JqZWN00h8gJSZXTlNBcnJheaIlIwAIABEAGgAkACkAMgA3AEkATABRAFMAWwBhAGYAcQB4AHoAfAB+AIkAnACwALoAwwDFAMcAyQDLAM0A2QDgAOUA8AD5AQgBCwEUARkBIQAAAAAAAAIBAAAAAAAAACcAAAAAAAAAAAAAAAAAAAEk\",\"changedRecordZoneIDs\":\"deadbeef\",\"purgedRecordZoneIDs\":\"YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGnCwwSHB0eJFUkbnVsbNINDg8RWk5TLm9iamVjdHNWJGNsYXNzoRCAAoAG1RMUFRYOFxgZGhtfEBBkYXRhYmFzZVNjb3BlS2V5XxARYW5vbnltb3VzQ0tVc2VySURZb3duZXJOYW1lWFpvbmVOYW1lEACAAIAEgAOABVpwdXJnZWRab25lVm93bmVyM9IfICEiWiRjbGFzc25hbWVYJGNsYXNzZXNeQ0tSZWNvcmRab25lSUSiISNYTlNPYmplY3TSHyAlJldOU0FycmF5oiUjAAgAEQAaACQAKQAyADcASQBMAFEAUwBbAGEAZgBxAHgAegB8AH4AiQCcALAAugDDAMUAxwDJAMsAzQDYAN8A5ADvAPgBBwEKARMBGAEgAAAAAAAAAgEAAAAAAAAAJwAAAAAAAAAAAAAAAAAAASM=\"}" let data = badJson.data(using: .utf8)! do { let _ = try JSONDecoder().decode(FetchDatabaseChangesResult.self, from: data) } catch DecodingError.dataCorrupted(let context) { - XCTAssertEqual(context.debugDescription, "Invalid changed record zone IDs value in source data") + #expect(context.debugDescription == "Invalid changed record zone IDs value in source data") } catch { - XCTFail("Unexpected error: \(error)") + Issue.record("Unexpected error: \(error)") } } - func test_throws_on_deleted_data_error() throws { + @Test func test_throws_on_deleted_data_error() throws { let badJson = "{\"deletedRecordZoneIDs\":\"deadbeef\",\"changedRecordZoneIDs\":\"YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGnCwwSHB0eJFUkbnVsbNINDg8RWk5TLm9iamVjdHNWJGNsYXNzoRCAAoAG1RMUFRYOFxgZGhtfEBBkYXRhYmFzZVNjb3BlS2V5XxARYW5vbnltb3VzQ0tVc2VySURZb3duZXJOYW1lWFpvbmVOYW1lEACAAIAEgAOABVtjaGFuZ2VkWm9uZVZvd25lcjHSHyAhIlokY2xhc3NuYW1lWCRjbGFzc2VzXkNLUmVjb3JkWm9uZUlEoiEjWE5TT2JqZWN00h8gJSZXTlNBcnJheaIlIwAIABEAGgAkACkAMgA3AEkATABRAFMAWwBhAGYAcQB4AHoAfAB+AIkAnACwALoAwwDFAMcAyQDLAM0A2QDgAOUA8AD5AQgBCwEUARkBIQAAAAAAAAIBAAAAAAAAACcAAAAAAAAAAAAAAAAAAAEk\",\"purgedRecordZoneIDs\":\"YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGnCwwSHB0eJFUkbnVsbNINDg8RWk5TLm9iamVjdHNWJGNsYXNzoRCAAoAG1RMUFRYOFxgZGhtfEBBkYXRhYmFzZVNjb3BlS2V5XxARYW5vbnltb3VzQ0tVc2VySURZb3duZXJOYW1lWFpvbmVOYW1lEACAAIAEgAOABVpwdXJnZWRab25lVm93bmVyM9IfICEiWiRjbGFzc25hbWVYJGNsYXNzZXNeQ0tSZWNvcmRab25lSUSiISNYTlNPYmplY3TSHyAlJldOU0FycmF5oiUjAAgAEQAaACQAKQAyADcASQBMAFEAUwBbAGEAZgBxAHgAegB8AH4AiQCcALAAugDDAMUAxwDJAMsAzQDYAN8A5ADvAPgBBwEKARMBGAEgAAAAAAAAAgEAAAAAAAAAJwAAAAAAAAAAAAAAAAAAASM=\"}" let data = badJson.data(using: .utf8)! do { let _ = try JSONDecoder().decode(FetchDatabaseChangesResult.self, from: data) } catch DecodingError.dataCorrupted(let context) { - XCTAssertEqual(context.debugDescription, "Invalid deleted record zone IDs value in source data") + #expect(context.debugDescription == "Invalid deleted record zone IDs value in source data") } catch { - XCTFail("Unexpected error: \(error)") + Issue.record("Unexpected error: \(error)") } } - func test_throws_on_purged_data_error() throws { + @Test func test_throws_on_purged_data_error() throws { let badJson = "{\"deletedRecordZoneIDs\":\"YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGnCwwSHB0eJFUkbnVsbNINDg8RWk5TLm9iamVjdHNWJGNsYXNzoRCAAoAG1RMUFRYOFxgZGhtfEBBkYXRhYmFzZVNjb3BlS2V5XxARYW5vbnltb3VzQ0tVc2VySURZb3duZXJOYW1lWFpvbmVOYW1lEACAAIAEgAOABVtkZWxldGVkWm9uZVZvd25lcjLSHyAhIlokY2xhc3NuYW1lWCRjbGFzc2VzXkNLUmVjb3JkWm9uZUlEoiEjWE5TT2JqZWN00h8gJSZXTlNBcnJheaIlIwAIABEAGgAkACkAMgA3AEkATABRAFMAWwBhAGYAcQB4AHoAfAB+AIkAnACwALoAwwDFAMcAyQDLAM0A2QDgAOUA8AD5AQgBCwEUARkBIQAAAAAAAAIBAAAAAAAAACcAAAAAAAAAAAAAAAAAAAEk\",\"changedRecordZoneIDs\":\"YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGnCwwSHB0eJFUkbnVsbNINDg8RWk5TLm9iamVjdHNWJGNsYXNzoRCAAoAG1RMUFRYOFxgZGhtfEBBkYXRhYmFzZVNjb3BlS2V5XxARYW5vbnltb3VzQ0tVc2VySURZb3duZXJOYW1lWFpvbmVOYW1lEACAAIAEgAOABVtjaGFuZ2VkWm9uZVZvd25lcjHSHyAhIlokY2xhc3NuYW1lWCRjbGFzc2VzXkNLUmVjb3JkWm9uZUlEoiEjWE5TT2JqZWN00h8gJSZXTlNBcnJheaIlIwAIABEAGgAkACkAMgA3AEkATABRAFMAWwBhAGYAcQB4AHoAfAB+AIkAnACwALoAwwDFAMcAyQDLAM0A2QDgAOUA8AD5AQgBCwEUARkBIQAAAAAAAAIBAAAAAAAAACcAAAAAAAAAAAAAAAAAAAEk\",\"purgedRecordZoneIDs\":\"deadbeef\"}" let data = badJson.data(using: .utf8)! do { let _ = try JSONDecoder().decode(FetchDatabaseChangesResult.self, from: data) } catch DecodingError.dataCorrupted(let context) { - XCTAssertEqual(context.debugDescription, "Invalid purged record zone IDs value in source data") + #expect(context.debugDescription == "Invalid purged record zone IDs value in source data") } catch { - XCTFail("Unexpected error: \(error)") + Issue.record("Unexpected error: \(error)") } } } diff --git a/Targets/Canopy/Tests/FetchDatabaseChangesTests.swift b/Targets/Canopy/Tests/FetchDatabaseChangesTests.swift index 090838b..52a00e2 100644 --- a/Targets/Canopy/Tests/FetchDatabaseChangesTests.swift +++ b/Targets/Canopy/Tests/FetchDatabaseChangesTests.swift @@ -2,10 +2,10 @@ import CanopyTestTools import CloudKit import Foundation -import XCTest +import Testing -final class FetchDatabaseChangesTests: XCTestCase { - func test_success() async { +@Suite struct FetchDatabaseChangesTests { + @Test func test_success() async { let changedRecordZoneID1 = CKRecordZone.ID(zoneName: "changedZone1", ownerName: CKCurrentUserDefaultName) let changedRecordZoneID2 = CKRecordZone.ID(zoneName: "changedZone2", ownerName: CKCurrentUserDefaultName) let deletedRecordZoneID = CKRecordZone.ID(zoneName: "deletedZone", ownerName: CKCurrentUserDefaultName) @@ -23,7 +23,7 @@ final class FetchDatabaseChangesTests: XCTestCase { let testTokenStore = TestTokenStore() let api = CKDatabaseAPI(database: db, databaseScope: .private, tokenStore: testTokenStore) let result = try? await api.fetchDatabaseChanges().get() - XCTAssertEqual(result, FetchDatabaseChangesResult( + #expect(result == FetchDatabaseChangesResult( changedRecordZoneIDs: [changedRecordZoneID1, changedRecordZoneID2], deletedRecordZoneIDs: [deletedRecordZoneID], purgedRecordZoneIDs: [purgedRecordZoneID] @@ -32,11 +32,11 @@ final class FetchDatabaseChangesTests: XCTestCase { let getTokenForDatabaseScopeCalls = await testTokenStore.getTokenForDatabaseScopeCalls let storeTokenForDatabaseScopeCalls = await testTokenStore.storeTokenForDatabaseScopeCalls - XCTAssertEqual(getTokenForDatabaseScopeCalls, 1) - XCTAssertEqual(storeTokenForDatabaseScopeCalls, 1) + #expect(getTokenForDatabaseScopeCalls == 1) + #expect(storeTokenForDatabaseScopeCalls == 1) } - func test_token_expired_error() async { + @Test func test_token_expired_error() async { let db = ReplayingMockCKDatabase(operationResults: [ .fetchDatabaseChanges( .init( @@ -52,13 +52,13 @@ final class FetchDatabaseChangesTests: XCTestCase { do { let _ = try await api.fetchDatabaseChanges().get() } catch { - XCTAssertEqual(error, CanopyError.ckChangeTokenExpired) + #expect(error == CanopyError.ckChangeTokenExpired) let storeTokenForDatabaseScopeCalls = await testTokenStore.storeTokenForDatabaseScopeCalls - XCTAssertEqual(storeTokenForDatabaseScopeCalls, 1) // nil token was stored + #expect(storeTokenForDatabaseScopeCalls == 1) // nil token was stored } } - func test_other_error() async { + @Test func test_other_error() async { let db = ReplayingMockCKDatabase(operationResults: [ .fetchDatabaseChanges( .init( @@ -74,13 +74,13 @@ final class FetchDatabaseChangesTests: XCTestCase { do { let _ = try await api.fetchDatabaseChanges().get() } catch { - XCTAssertEqual(error, CanopyError.ckRequestError(CKRequestError(from: CKError(CKError.Code.networkFailure)))) + #expect(error == CanopyError.ckRequestError(CKRequestError(from: CKError(CKError.Code.networkFailure)))) let storeTokenForDatabaseScopeCalls = await testTokenStore.storeTokenForDatabaseScopeCalls - XCTAssertEqual(storeTokenForDatabaseScopeCalls, 0) // nothing should have been stored + #expect(storeTokenForDatabaseScopeCalls == 0) // nothing should have been stored } } - func test_success_with_delay() async { + @Test func test_success_with_delay() async { let changedRecordZoneID1 = CKRecordZone.ID(zoneName: "changedZone1", ownerName: CKCurrentUserDefaultName) let db = ReplayingMockCKDatabase(operationResults: [ .fetchDatabaseChanges( @@ -102,18 +102,18 @@ final class FetchDatabaseChangesTests: XCTestCase { tokenStore: testTokenStore ) let result = try? await api.fetchDatabaseChanges().get() - XCTAssertEqual(result, FetchDatabaseChangesResult( + #expect(result == FetchDatabaseChangesResult( changedRecordZoneIDs: [changedRecordZoneID1], deletedRecordZoneIDs: [], purgedRecordZoneIDs: [] )) let getTokenForDatabaseScopeCalls = await testTokenStore.getTokenForDatabaseScopeCalls let storeTokenForDatabaseScopeCalls = await testTokenStore.storeTokenForDatabaseScopeCalls - XCTAssertEqual(getTokenForDatabaseScopeCalls, 1) - XCTAssertEqual(storeTokenForDatabaseScopeCalls, 1) + #expect(getTokenForDatabaseScopeCalls == 1) + #expect(storeTokenForDatabaseScopeCalls == 1) } - func test_simulated_fail() async { + @Test func test_simulated_fail() async { let changedRecordZoneID1 = CKRecordZone.ID(zoneName: "changedZone1", ownerName: CKCurrentUserDefaultName) let db = ReplayingMockCKDatabase(operationResults: [ .fetchDatabaseChanges( @@ -141,17 +141,17 @@ final class FetchDatabaseChangesTests: XCTestCase { case .ckRequestError: break default: - XCTFail("Unexpected error type: \(error)") + Issue.record("Unexpected error type: \(error)") } let getTokenForDatabaseScopeCalls = await testTokenStore.getTokenForDatabaseScopeCalls let storeTokenForDatabaseScopeCalls = await testTokenStore.storeTokenForDatabaseScopeCalls - XCTAssertEqual(getTokenForDatabaseScopeCalls, 0) - XCTAssertEqual(storeTokenForDatabaseScopeCalls, 0) + #expect(getTokenForDatabaseScopeCalls == 0) + #expect(storeTokenForDatabaseScopeCalls == 0) } } - func test_simulated_fail_with_delay() async { + @Test func test_simulated_fail_with_delay() async { let changedRecordZoneID1 = CKRecordZone.ID(zoneName: "changedZone1", ownerName: CKCurrentUserDefaultName) let db = ReplayingMockCKDatabase(operationResults: [ .fetchDatabaseChanges( @@ -179,14 +179,14 @@ final class FetchDatabaseChangesTests: XCTestCase { case .ckRequestError: break default: - XCTFail("Unexpected error type: \(error)") + Issue.record("Unexpected error type: \(error)") } let getTokenForDatabaseScopeCalls = await testTokenStore.getTokenForDatabaseScopeCalls let storeTokenForDatabaseScopeCalls = await testTokenStore.storeTokenForDatabaseScopeCalls - XCTAssertEqual(getTokenForDatabaseScopeCalls, 0) - XCTAssertEqual(storeTokenForDatabaseScopeCalls, 0) + #expect(getTokenForDatabaseScopeCalls == 0) + #expect(storeTokenForDatabaseScopeCalls == 0) } } } diff --git a/Targets/Canopy/Tests/FetchRecordsResultTests.swift b/Targets/Canopy/Tests/FetchRecordsResultTests.swift index 060d6e8..1bcc909 100644 --- a/Targets/Canopy/Tests/FetchRecordsResultTests.swift +++ b/Targets/Canopy/Tests/FetchRecordsResultTests.swift @@ -1,10 +1,10 @@ import Canopy import CanopyTestTools import CloudKit -import XCTest +import Testing -final class FetchRecordsResultTests: XCTestCase { - func test_codes() throws { +@Suite struct FetchRecordsResultTests { + @Test func test_codes() throws { let foundRecords = [CanopyResultRecord.mock(.init(recordType: "MockType"))] let notFoundRecordIDs = [CKRecord.ID(recordName: "notFoundId")] let fetchRecordsResult = FetchRecordsResult( @@ -13,18 +13,18 @@ final class FetchRecordsResultTests: XCTestCase { ) let coded = try JSONEncoder().encode(fetchRecordsResult) let decoded = try JSONDecoder().decode(FetchRecordsResult.self, from: coded) - XCTAssertEqual(fetchRecordsResult, decoded) + #expect(fetchRecordsResult == decoded) } - func test_throws_on_bad_deleted_ids_data() { + @Test func test_throws_on_bad_deleted_ids_data() { let badJson = "{\"foundRecords\":[],\"notFoundRecordIDs\":\"deadbeef\"}" let data = badJson.data(using: .utf8)! do { let _ = try JSONDecoder().decode(FetchRecordsResult.self, from: data) } catch DecodingError.dataCorrupted(let context) { - XCTAssertEqual(context.debugDescription, "Invalid not found record IDs value in source data") + #expect(context.debugDescription == "Invalid not found record IDs value in source data") } catch { - XCTFail("Unexpected error: \(error)") + Issue.record("Unexpected error: \(error)") } } } diff --git a/Targets/Canopy/Tests/FetchZoneChangesResultTests.swift b/Targets/Canopy/Tests/FetchZoneChangesResultTests.swift index 64bc5ee..4e007b1 100644 --- a/Targets/Canopy/Tests/FetchZoneChangesResultTests.swift +++ b/Targets/Canopy/Tests/FetchZoneChangesResultTests.swift @@ -1,22 +1,22 @@ import Canopy import CloudKit -import XCTest +import Testing -final class FetchZoneChangesResultTests: XCTestCase { - func test_codes() throws { +@Suite struct FetchZoneChangesResultTests { + @Test func test_codes() throws { let result = FetchZoneChangesResult( records: [.mock(.init(recordID: .init(recordName: "recordName1"), recordType: "SomeType"))], deletedRecords: [.init(recordID: .init(recordName: "deletedId1"), recordType: "SomeDeletedType")] ) let coded = try JSONEncoder().encode(result) let decoded = try JSONDecoder().decode(FetchZoneChangesResult.self, from: coded) - XCTAssertEqual(decoded.changedRecords[0].recordID.recordName, "recordName1") - XCTAssertEqual(decoded.deletedRecords[0].recordID.recordName, "deletedId1") + #expect(decoded.changedRecords[0].recordID.recordName == "recordName1") + #expect(decoded.deletedRecords[0].recordID.recordName == "deletedId1") } - func test_empty() { + @Test func test_empty() { let result = FetchZoneChangesResult.empty - XCTAssertTrue(result.changedRecords.isEmpty) - XCTAssertTrue(result.deletedRecords.isEmpty) + #expect(result.changedRecords.isEmpty == true) + #expect(result.deletedRecords.isEmpty == true) } } diff --git a/Targets/Canopy/Tests/SerialFetchChangesTests.swift b/Targets/Canopy/Tests/SerialFetchChangesTests.swift index 95513c6..7ca4095 100644 --- a/Targets/Canopy/Tests/SerialFetchChangesTests.swift +++ b/Targets/Canopy/Tests/SerialFetchChangesTests.swift @@ -2,7 +2,7 @@ import CanopyTestTools import CloudKit import Foundation -import XCTest +import Testing /// Tests to make sure that fetching database and zone changes operates in a serial fashion. /// @@ -18,7 +18,7 @@ import XCTest /// fetches. Next fetches wait for previous ones to finish. /// /// These tests make sure that this behavior is correct. -final class SerialFetchChangesTests: XCTestCase { +@Suite struct SerialFetchChangesTests { /// A token store that balances calls to getting and storing tokens. /// /// The store makes sure that only one token of a given type is ever in use, @@ -67,7 +67,7 @@ final class SerialFetchChangesTests: XCTestCase { func clear() {} } - func test_two_database_fetches_work() async { + @Test func test_two_database_fetches_work() async { // Two simultaneous change fetch requests for the same database should get queued up. let privateZoneID1 = CKRecordZone.ID(zoneName: "SomePrivateZone1", ownerName: CKCurrentUserDefaultName) @@ -99,7 +99,7 @@ final class SerialFetchChangesTests: XCTestCase { databaseScope: .private, tokenStore: BalancingTokenStore( databaseViolationReporter: { scope in - XCTFail("Token balance error for database scope: \(scope)") + Issue.record("Token balance error for database scope: \(scope)") }, zoneViolationReporter: { _ in } ) @@ -111,7 +111,7 @@ final class SerialFetchChangesTests: XCTestCase { let _ = await [results1, results2] } - func test_two_zone_fetches_work() async { + @Test func test_two_zone_fetches_work() async { // Two simultaneous change fetch requests for the same record zone should get queued up. let privateZoneID1 = CKRecordZone.ID(zoneName: "SomePrivateZone1", ownerName: CKCurrentUserDefaultName) @@ -166,7 +166,7 @@ final class SerialFetchChangesTests: XCTestCase { tokenStore: BalancingTokenStore( databaseViolationReporter: { _ in }, zoneViolationReporter: { zoneID in - XCTFail("Token balance error for zone: \(zoneID)") + Issue.record("Token balance error for zone: \(zoneID)") } ) ) From 56f760a0a4a548f668e01aaa273f188409465717 Mon Sep 17 00:00:00 2001 From: Jaanus Kase Date: Thu, 23 Oct 2025 12:47:30 +0300 Subject: [PATCH 11/20] Move to Swift Testing --- .../Canopy/Tests/FetchZoneChangesTests.swift | 74 ++++---- .../Tests/MockCanopyResultRecordTests.swift | 86 +++++----- Targets/Canopy/Tests/MockObjectTests.swift | 12 +- .../Canopy/Tests/MockValueStoreTests.swift | 158 +++++++++--------- .../Tests/ModifyRecordsFeatureTests.swift | 102 +++++------ .../Tests/ModifyRecordsResultTests.swift | 14 +- .../Tests/ModifySubscriptionResultTests.swift | 18 +- .../Canopy/Tests/ModifyZonesResultTests.swift | 22 +-- Targets/Canopy/Tests/ResultsTypesTests.swift | 16 +- 9 files changed, 251 insertions(+), 251 deletions(-) diff --git a/Targets/Canopy/Tests/FetchZoneChangesTests.swift b/Targets/Canopy/Tests/FetchZoneChangesTests.swift index 596819d..99af1d7 100644 --- a/Targets/Canopy/Tests/FetchZoneChangesTests.swift +++ b/Targets/Canopy/Tests/FetchZoneChangesTests.swift @@ -2,10 +2,10 @@ import CanopyTestTools import CloudKit import Foundation -import XCTest +import Testing -final class FetchZoneChangesTests: XCTestCase { - func test_success() async { +@Suite struct FetchZoneChangesTests { + @Test func test_success() async { let changedRecordID = CKRecord.ID(recordName: "SomeRecordName") let changedRecord = CKRecord(recordType: "TestRecord", recordID: changedRecordID) let zoneID = CKRecordZone.ID(zoneName: "testZone", ownerName: CKCurrentUserDefaultName) @@ -37,15 +37,15 @@ final class FetchZoneChangesTests: XCTestCase { recordZoneIDs: [zoneID], fetchMethod: .changeTokenAndAllData ).get() - XCTAssertTrue(result.changedRecords.first!.isEqualToRecord(changedRecord.canopyResultRecord)) - XCTAssertEqual(result.deletedRecords, []) + #expect(result.changedRecords.first!.isEqualToRecord(changedRecord.canopyResultRecord) == true) + #expect(result.deletedRecords == []) let getTokenForRecordZoneCalls = await tokenStore.getTokenForRecordZoneCalls let storeTokenForRecordZoneCalls = await tokenStore.storeTokenForRecordZoneCalls - XCTAssertEqual(getTokenForRecordZoneCalls, 1) - XCTAssertEqual(storeTokenForRecordZoneCalls, 1) + #expect(getTokenForRecordZoneCalls == 1) + #expect(storeTokenForRecordZoneCalls == 1) } - func test_fetch_tokens_only() async { + @Test func test_fetch_tokens_only() async { let changedRecordID = CKRecord.ID(recordName: "SomeRecordName") let changedRecord = CKRecord(recordType: "TestRecord", recordID: changedRecordID) let zoneID = CKRecordZone.ID(zoneName: "testZone", ownerName: CKCurrentUserDefaultName) @@ -77,15 +77,15 @@ final class FetchZoneChangesTests: XCTestCase { recordZoneIDs: [zoneID], fetchMethod: .changeTokenOnly ).get() - XCTAssertEqual(result.changedRecords, []) - XCTAssertEqual(result.deletedRecords, []) + #expect(result.changedRecords == []) + #expect(result.deletedRecords == []) let getTokenForRecordZoneCalls = await tokenStore.getTokenForRecordZoneCalls let storeTokenForRecordZoneCalls = await tokenStore.storeTokenForRecordZoneCalls - XCTAssertEqual(getTokenForRecordZoneCalls, 1) - XCTAssertEqual(storeTokenForRecordZoneCalls, 1) + #expect(getTokenForRecordZoneCalls == 1) + #expect(storeTokenForRecordZoneCalls == 1) } - func test_record_error() async { + @Test func test_record_error() async { let changedRecordID = CKRecord.ID(recordName: "SomeRecordName") let deletedRecordID = CKRecord.ID(recordName: "DeletedRecordID") let zoneID = CKRecordZone.ID(zoneName: "testZone", ownerName: CKCurrentUserDefaultName) @@ -122,15 +122,15 @@ final class FetchZoneChangesTests: XCTestCase { fetchMethod: .changeTokenAndSpecificKeys(["key1", "key2"]) ).get() } catch CanopyError.ckRecordError(let recordError) { - XCTAssertEqual(recordError, .init(from: CKError(CKError.Code.networkUnavailable))) + #expect(recordError == .init(from: CKError(CKError.Code.networkUnavailable))) let storeTokenForRecordZoneCalls = await tokenStore.storeTokenForRecordZoneCalls - XCTAssertEqual(storeTokenForRecordZoneCalls, 0) + #expect(storeTokenForRecordZoneCalls == 0) } catch { - XCTFail("Unexpected error: \(error)") + Issue.record("Unexpected error: \(error)") } } - func test_token_expired() async { + @Test func test_token_expired() async { let zoneID1 = CKRecordZone.ID(zoneName: "testZone1", ownerName: CKCurrentUserDefaultName) let zoneID2 = CKRecordZone.ID(zoneName: "testZone2", ownerName: CKCurrentUserDefaultName) @@ -165,14 +165,14 @@ final class FetchZoneChangesTests: XCTestCase { let getTokenForRecordZoneCalls = await tokenStore.getTokenForRecordZoneCalls let storeTokenForRecordZoneCalls = await tokenStore.storeTokenForRecordZoneCalls - XCTAssertEqual(getTokenForRecordZoneCalls, 2) + #expect(getTokenForRecordZoneCalls == 2) // Stored only one nil token - XCTAssertEqual(storeTokenForRecordZoneCalls, 1) - XCTAssertEqual(error, .ckRecordZoneError(.init(from: CKError(CKError.Code.changeTokenExpired)))) + #expect(storeTokenForRecordZoneCalls == 1) + #expect(error == .ckRecordZoneError(.init(from: CKError(CKError.Code.changeTokenExpired)))) } } - func test_result_error() async { + @Test func test_result_error() async { let zoneID = CKRecordZone.ID(zoneName: "testZone", ownerName: CKCurrentUserDefaultName) let db = ReplayingMockCKDatabase(operationResults: [ @@ -206,14 +206,14 @@ final class FetchZoneChangesTests: XCTestCase { let getTokenForRecordZoneCalls = await tokenStore.getTokenForRecordZoneCalls let storeTokenForRecordZoneCalls = await tokenStore.storeTokenForRecordZoneCalls - XCTAssertEqual(getTokenForRecordZoneCalls, 1) - XCTAssertEqual(storeTokenForRecordZoneCalls, 0) + #expect(getTokenForRecordZoneCalls == 1) + #expect(storeTokenForRecordZoneCalls == 0) - XCTAssertEqual(error, .ckRequestError(.init(from: CKError(CKError.Code.accountTemporarilyUnavailable)))) + #expect(error == .ckRequestError(.init(from: CKError(CKError.Code.accountTemporarilyUnavailable)))) } } - func test_success_with_delay() async { + @Test func test_success_with_delay() async { let changedRecordID = CKRecord.ID(recordName: "SomeRecordName") let changedRecord = CKRecord(recordType: "TestRecord", recordID: changedRecordID) let zoneID = CKRecordZone.ID(zoneName: "testZone", ownerName: CKCurrentUserDefaultName) @@ -245,16 +245,16 @@ final class FetchZoneChangesTests: XCTestCase { recordZoneIDs: [zoneID], fetchMethod: .changeTokenAndAllData ).get() - XCTAssertTrue(result.changedRecords.first!.isEqualToRecord(changedRecord.canopyResultRecord)) - XCTAssertEqual(result.deletedRecords, []) + #expect(result.changedRecords.first!.isEqualToRecord(changedRecord.canopyResultRecord) == true) + #expect(result.deletedRecords == []) let getTokenForRecordZoneCalls = await tokenStore.getTokenForRecordZoneCalls let storeTokenForRecordZoneCalls = await tokenStore.storeTokenForRecordZoneCalls - XCTAssertEqual(getTokenForRecordZoneCalls, 1) - XCTAssertEqual(storeTokenForRecordZoneCalls, 1) + #expect(getTokenForRecordZoneCalls == 1) + #expect(storeTokenForRecordZoneCalls == 1) } - func test_simulated_fail() async { + @Test func test_simulated_fail() async { let changedRecordID = CKRecord.ID(recordName: "SomeRecordName") let changedRecord = CKRecord(recordType: "TestRecord", recordID: changedRecordID) let zoneID = CKRecordZone.ID(zoneName: "testZone", ownerName: CKCurrentUserDefaultName) @@ -292,17 +292,17 @@ final class FetchZoneChangesTests: XCTestCase { case .ckRequestError: break default: - XCTFail("Unexpected error type: \(error)") + Issue.record("Unexpected error type: \(error)") } let getTokenForRecordZoneCalls = await tokenStore.getTokenForRecordZoneCalls let storeTokenForRecordZoneCalls = await tokenStore.storeTokenForRecordZoneCalls - XCTAssertEqual(getTokenForRecordZoneCalls, 0) - XCTAssertEqual(storeTokenForRecordZoneCalls, 0) + #expect(getTokenForRecordZoneCalls == 0) + #expect(storeTokenForRecordZoneCalls == 0) } } - func test_simulated_fail_with_delay() async { + @Test func test_simulated_fail_with_delay() async { let changedRecordID = CKRecord.ID(recordName: "SomeRecordName") let changedRecord = CKRecord(recordType: "TestRecord", recordID: changedRecordID) let zoneID = CKRecordZone.ID(zoneName: "testZone", ownerName: CKCurrentUserDefaultName) @@ -340,13 +340,13 @@ final class FetchZoneChangesTests: XCTestCase { case .ckRequestError: break default: - XCTFail("Unexpected error type: \(error)") + Issue.record("Unexpected error type: \(error)") } let getTokenForRecordZoneCalls = await tokenStore.getTokenForRecordZoneCalls let storeTokenForRecordZoneCalls = await tokenStore.storeTokenForRecordZoneCalls - XCTAssertEqual(getTokenForRecordZoneCalls, 0) - XCTAssertEqual(storeTokenForRecordZoneCalls, 0) + #expect(getTokenForRecordZoneCalls == 0) + #expect(storeTokenForRecordZoneCalls == 0) } } } diff --git a/Targets/Canopy/Tests/MockCanopyResultRecordTests.swift b/Targets/Canopy/Tests/MockCanopyResultRecordTests.swift index a727394..ab8c64e 100644 --- a/Targets/Canopy/Tests/MockCanopyResultRecordTests.swift +++ b/Targets/Canopy/Tests/MockCanopyResultRecordTests.swift @@ -1,11 +1,11 @@ @testable import Canopy import CloudKit import Foundation -import XCTest +import Testing -final class MockCanopyResultRecordTests: XCTestCase { +@Suite struct MockCanopyResultRecordTests { let date = Date() - func test_codes_complete_record() throws { + @Test func test_codes_complete_record() throws { let record = MockCanopyResultRecord( recordID: CKRecord.ID(recordName: "someName"), recordType: "SomeRecordType", @@ -25,27 +25,27 @@ final class MockCanopyResultRecordTests: XCTestCase { ) let data = try JSONEncoder().encode(record) let decodedRecord = try JSONDecoder().decode(MockCanopyResultRecord.self, from: data) - XCTAssertEqual(decodedRecord.recordID.recordName, "someName") - XCTAssertEqual(decodedRecord.recordType, "SomeRecordType") - XCTAssertEqual(decodedRecord["key1"] as! String, "String 1") - XCTAssertEqual(decodedRecord.encryptedValues["encrypted1"] as! String, "Encrypted String 1") - XCTAssertEqual(decodedRecord.creationDate, date) - XCTAssertEqual(decodedRecord.modificationDate, date) - XCTAssertEqual(decodedRecord.recordChangeTag, "changeTag") - XCTAssertEqual(decodedRecord.creatorUserRecordID?.recordName, "creator") - XCTAssertEqual(decodedRecord.lastModifiedUserRecordID!.recordName, "last modifier") - XCTAssertEqual(decodedRecord.parent?.recordID.recordName, "parentRecordName") - XCTAssertEqual(decodedRecord.share?.recordID.recordName, "shareRecordName") + #expect(decodedRecord.recordID.recordName == "someName") + #expect(decodedRecord.recordType == "SomeRecordType") + #expect(decodedRecord["key1"] as! String == "String 1") + #expect(decodedRecord.encryptedValues["encrypted1"] as! String == "Encrypted String 1") + #expect(decodedRecord.creationDate == date) + #expect(decodedRecord.modificationDate == date) + #expect(decodedRecord.recordChangeTag == "changeTag") + #expect(decodedRecord.creatorUserRecordID?.recordName == "creator") + #expect(decodedRecord.lastModifiedUserRecordID!.recordName == "last modifier") + #expect(decodedRecord.parent?.recordID.recordName == "parentRecordName") + #expect(decodedRecord.share?.recordID.recordName == "shareRecordName") } - func test_codes_minimal_record() throws { + @Test func test_codes_minimal_record() throws { let record = MockCanopyResultRecord(recordType: "MinimalType") let data = try JSONEncoder().encode(record) let decodedRecord = try JSONDecoder().decode(MockCanopyResultRecord.self, from: data) - XCTAssertEqual(decodedRecord.recordType, "MinimalType") + #expect(decodedRecord.recordType == "MinimalType") } - func test_invalid_record_id_throws() { + @Test func test_invalid_record_id_throws() { let brokenJson = "{\"recordType\":\"MinimalType\",\"valuesStore\":[],\"recordID\":\"deadbeef\",\"encryptedValuesStore\":[]}" do { @@ -55,14 +55,14 @@ final class MockCanopyResultRecordTests: XCTestCase { let decodingError = error as! DecodingError switch (decodingError) { case .dataCorrupted(let context): - XCTAssertEqual(context.debugDescription, "Invalid record ID") + #expect(context.debugDescription == "Invalid record ID") default: - XCTFail("Unexpected error: \(error)") + Issue.record("Unexpected error: \(error)") } } } - func test_invalid_creator_user_record_id_throws() { + @Test func test_invalid_creator_user_record_id_throws() { let brokenJson = "{\"recordType\":\"MinimalType\",\"valuesStore\":[],\"recordID\":\"YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGoCwwTFB4fICZVJG51bGzTDQ4PEBESViRjbGFzc1pSZWNvcmROYW1lVlpvbmVJRIAHgAKAA15tb2NrUmVjb3JkTmFtZdUVFhcYDRkaGxwdXxAQZGF0YWJhc2VTY29wZUtleV8QEWFub255bW91c0NLVXNlcklEWW93bmVyTmFtZVhab25lTmFtZRAAgACABYAEgAZcX2RlZmF1bHRab25lXxAQX19kZWZhdWx0T3duZXJfX9IhIiMkWiRjbGFzc25hbWVYJGNsYXNzZXNeQ0tSZWNvcmRab25lSUSiIyVYTlNPYmplY3TSISInKFpDS1JlY29yZElEoiclAAgAEQAaACQAKQAyADcASQBMAFEAUwBcAGIAaQBwAHsAggCEAIYAiACXAKIAtQDJANMA3ADeAOAA4gDkAOYA8wEGAQsBFgEfAS4BMQE6AT8BSgAAAAAAAAIBAAAAAAAAACkAAAAAAAAAAAAAAAAAAAFN\",\"encryptedValuesStore\":[],\"creatorUserRecordID\":\"deadbeef\"}" do { @@ -72,14 +72,14 @@ final class MockCanopyResultRecordTests: XCTestCase { let decodingError = error as! DecodingError switch (decodingError) { case .dataCorrupted(let context): - XCTAssertEqual(context.debugDescription, "Invalid creator user record ID") + #expect(context.debugDescription == "Invalid creator user record ID") default: - XCTFail("Unexpected error: \(error)") + Issue.record("Unexpected error: \(error)") } } } - func test_invalid_last_modified_user_record_id_throws() { + @Test func test_invalid_last_modified_user_record_id_throws() { let brokenJson = "{\"recordType\":\"MinimalType\",\"valuesStore\":[],\"recordID\":\"YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGoCwwTFB4fICZVJG51bGzTDQ4PEBESViRjbGFzc1pSZWNvcmROYW1lVlpvbmVJRIAHgAKAA15tb2NrUmVjb3JkTmFtZdUVFhcYDRkaGxwdXxAQZGF0YWJhc2VTY29wZUtleV8QEWFub255bW91c0NLVXNlcklEWW93bmVyTmFtZVhab25lTmFtZRAAgACABYAEgAZcX2RlZmF1bHRab25lXxAQX19kZWZhdWx0T3duZXJfX9IhIiMkWiRjbGFzc25hbWVYJGNsYXNzZXNeQ0tSZWNvcmRab25lSUSiIyVYTlNPYmplY3TSISInKFpDS1JlY29yZElEoiclAAgAEQAaACQAKQAyADcASQBMAFEAUwBcAGIAaQBwAHsAggCEAIYAiACXAKIAtQDJANMA3ADeAOAA4gDkAOYA8wEGAQsBFgEfAS4BMQE6AT8BSgAAAAAAAAIBAAAAAAAAACkAAAAAAAAAAAAAAAAAAAFN\",\"encryptedValuesStore\":[],\"lastModifiedUserRecordID\":\"deadbeef\"}" do { @@ -89,14 +89,14 @@ final class MockCanopyResultRecordTests: XCTestCase { let decodingError = error as! DecodingError switch (decodingError) { case .dataCorrupted(let context): - XCTAssertEqual(context.debugDescription, "Invalid last modified user record ID") + #expect(context.debugDescription == "Invalid last modified user record ID") default: - XCTFail("Unexpected error: \(error)") + Issue.record("Unexpected error: \(error)") } } } - func test_invalid_parent_throws() { + @Test func test_invalid_parent_throws() { let brokenJson = "{\"recordType\":\"MinimalType\",\"valuesStore\":[],\"recordID\":\"YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGoCwwTFB4fICZVJG51bGzTDQ4PEBESViRjbGFzc1pSZWNvcmROYW1lVlpvbmVJRIAHgAKAA15tb2NrUmVjb3JkTmFtZdUVFhcYDRkaGxwdXxAQZGF0YWJhc2VTY29wZUtleV8QEWFub255bW91c0NLVXNlcklEWW93bmVyTmFtZVhab25lTmFtZRAAgACABYAEgAZcX2RlZmF1bHRab25lXxAQX19kZWZhdWx0T3duZXJfX9IhIiMkWiRjbGFzc25hbWVYJGNsYXNzZXNeQ0tSZWNvcmRab25lSUSiIyVYTlNPYmplY3TSISInKFpDS1JlY29yZElEoiclAAgAEQAaACQAKQAyADcASQBMAFEAUwBcAGIAaQBwAHsAggCEAIYAiACXAKIAtQDJANMA3ADeAOAA4gDkAOYA8wEGAQsBFgEfAS4BMQE6AT8BSgAAAAAAAAIBAAAAAAAAACkAAAAAAAAAAAAAAAAAAAFN\",\"encryptedValuesStore\":[],\"parent\":\"deadbeef\"}" do { @@ -106,14 +106,14 @@ final class MockCanopyResultRecordTests: XCTestCase { let decodingError = error as! DecodingError switch (decodingError) { case .dataCorrupted(let context): - XCTAssertEqual(context.debugDescription, "Invalid parent") + #expect(context.debugDescription == "Invalid parent") default: - XCTFail("Unexpected error: \(error)") + Issue.record("Unexpected error: \(error)") } } } - func test_invalid_share_throws() { + @Test func test_invalid_share_throws() { let brokenJson = "{\"recordType\":\"MinimalType\",\"valuesStore\":[],\"recordID\":\"YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGoCwwTFB4fICZVJG51bGzTDQ4PEBESViRjbGFzc1pSZWNvcmROYW1lVlpvbmVJRIAHgAKAA15tb2NrUmVjb3JkTmFtZdUVFhcYDRkaGxwdXxAQZGF0YWJhc2VTY29wZUtleV8QEWFub255bW91c0NLVXNlcklEWW93bmVyTmFtZVhab25lTmFtZRAAgACABYAEgAZcX2RlZmF1bHRab25lXxAQX19kZWZhdWx0T3duZXJfX9IhIiMkWiRjbGFzc25hbWVYJGNsYXNzZXNeQ0tSZWNvcmRab25lSUSiIyVYTlNPYmplY3TSISInKFpDS1JlY29yZElEoiclAAgAEQAaACQAKQAyADcASQBMAFEAUwBcAGIAaQBwAHsAggCEAIYAiACXAKIAtQDJANMA3ADeAOAA4gDkAOYA8wEGAQsBFgEfAS4BMQE6AT8BSgAAAAAAAAIBAAAAAAAAACkAAAAAAAAAAAAAAAAAAAFN\",\"encryptedValuesStore\":[],\"share\":\"deadbeef\"}" do { @@ -123,14 +123,14 @@ final class MockCanopyResultRecordTests: XCTestCase { let decodingError = error as! DecodingError switch (decodingError) { case .dataCorrupted(let context): - XCTAssertEqual(context.debugDescription, "Invalid share") + #expect(context.debugDescription == "Invalid share") default: - XCTFail("Unexpected error: \(error)") + Issue.record("Unexpected error: \(error)") } } } - func test_compact_map_values() throws { + @Test func test_compact_map_values() throws { let mock = MockCanopyResultRecord( recordType: "MockRecord", values: [ @@ -143,14 +143,14 @@ final class MockCanopyResultRecordTests: XCTestCase { ] ) - XCTAssertEqual(mock["key1"] as! String, "value1") - XCTAssertNil(mock["nullKey"]) + #expect(mock["key1"] as! String == "value1") + #expect(mock["nullKey"] == nil) - XCTAssertEqual(mock.encryptedValues["encryptedKey1"] as! String, "encryptedValue1") - XCTAssertNil(mock["nullEncryptedKey"]) + #expect(mock.encryptedValues["encryptedKey1"] as! String == "encryptedValue1") + #expect(mock["nullEncryptedKey"] == nil) } - func test_equatable() { + @Test func test_equatable() { let creationDate = Date().advanced(by: -10) let modificationDate = Date() @@ -190,17 +190,17 @@ final class MockCanopyResultRecordTests: XCTestCase { share: .init(recordID: .init(recordName: "shareId"), action: .none) ) - XCTAssertEqual(record1, record2) - XCTAssertNotEqual(record1, record3) + #expect(record1 == record2) + #expect(record1 != record3) } - func test_from_ckrecord() { + @Test func test_from_ckrecord() { let ckRecord = CKRecord(recordType: "MyType", recordID: .init(recordName: "myRecordName")) ckRecord["key1"] = "value1" ckRecord.encryptedValues["encryptedKey1"] = "encryptedValue1" let mock = MockCanopyResultRecord.from(ckRecord: ckRecord) - XCTAssertEqual(mock.recordID.recordName, "myRecordName") - XCTAssertEqual(mock["key1"] as! String, "value1") - XCTAssertEqual(mock.encryptedValues["encryptedKey1"] as! String, "encryptedValue1") + #expect(mock.recordID.recordName == "myRecordName") + #expect(mock["key1"] as! String == "value1") + #expect(mock.encryptedValues["encryptedKey1"] as! String == "encryptedValue1") } } diff --git a/Targets/Canopy/Tests/MockObjectTests.swift b/Targets/Canopy/Tests/MockObjectTests.swift index 28949f7..100ea36 100644 --- a/Targets/Canopy/Tests/MockObjectTests.swift +++ b/Targets/Canopy/Tests/MockObjectTests.swift @@ -2,17 +2,17 @@ import CanopyTestTools import CloudKit import Foundation -import XCTest +import Testing /// Test the validity mock objects provided as part of the test tools. -final class MockObjectTests: XCTestCase { - func test_mock_share_owned_by_another_user() { +@Suite struct MockObjectTests { + @Test func test_mock_share_owned_by_another_user() { let share = CKShare.mock - XCTAssertEqual(share.participants.count, 2) + #expect(share.participants.count == 2) } - func test_mock_share_owned_by_current_user() { + @Test func test_mock_share_owned_by_current_user() { let share = CKShare.mock_owned_by_current_user - XCTAssertEqual(share.participants.count, 3) + #expect(share.participants.count == 3) } } diff --git a/Targets/Canopy/Tests/MockValueStoreTests.swift b/Targets/Canopy/Tests/MockValueStoreTests.swift index a1b67eb..54abc57 100644 --- a/Targets/Canopy/Tests/MockValueStoreTests.swift +++ b/Targets/Canopy/Tests/MockValueStoreTests.swift @@ -1,15 +1,15 @@ @testable import Canopy import CloudKit import Foundation -import XCTest +import Testing typealias ValueStore = MockCanopyResultRecord.MockValueStore -final class MockValueStoreTests: XCTestCase { +@Suite struct MockValueStoreTests { // MARK: - Invididual data types - func test_codes_string() throws { + @Test func test_codes_string() throws { let sut = ValueStore(values: [ "key1": "Hello world", "key2": "Another value", @@ -19,11 +19,11 @@ final class MockValueStoreTests: XCTestCase { let key1Value = outcome["key1"] as? String let key2Value = outcome["key2"] as? String - XCTAssertEqual(key1Value, "Hello world") - XCTAssertEqual(key2Value, "Another value") + #expect(key1Value == "Hello world") + #expect(key2Value == "Another value") } - func test_codes_nsstring() throws { + @Test func test_codes_nsstring() throws { let sut = ValueStore(values: [ "_force_nstype_key1": NSString(string: "Hello world"), "_force_nstype_key2": NSString(string: "Another value"), @@ -33,11 +33,11 @@ final class MockValueStoreTests: XCTestCase { let key1Value = outcome["_force_nstype_key1"] as? NSString let key2Value = outcome["_force_nstype_key2"] as? NSString - XCTAssertEqual(key1Value, "Hello world") - XCTAssertEqual(key2Value, "Another value") + #expect(key1Value == "Hello world") + #expect(key2Value == "Another value") } - func test_codes_ints() throws { + @Test func test_codes_ints() throws { let sut = ValueStore(values: [ "intKey": Int(42), "zeroKey": Int(0), @@ -51,17 +51,17 @@ final class MockValueStoreTests: XCTestCase { let data = try JSONEncoder().encode(sut) let outcome = try JSONDecoder().decode(ValueStore.self, from: data) - XCTAssertEqual(outcome["intKey"] as! Int, 42) - XCTAssertEqual(outcome["zeroKey"] as! Int, 0) - XCTAssertEqual(outcome["oneKey"] as! Int, 1) - XCTAssertEqual(outcome["twoKey"] as! Int, 2) - XCTAssertEqual(outcome["int8Key"] as! Int8, 9) - XCTAssertEqual(outcome["int16Key"] as! Int16, 17) - XCTAssertEqual(outcome["int32Key"] as! Int32, 33) - XCTAssertEqual(outcome["int64Key"] as! Int64, 65) + #expect(outcome["intKey"] as! Int == 42) + #expect(outcome["zeroKey"] as! Int == 0) + #expect(outcome["oneKey"] as! Int == 1) + #expect(outcome["twoKey"] as! Int == 2) + #expect(outcome["int8Key"] as! Int8 == 9) + #expect(outcome["int16Key"] as! Int16 == 17) + #expect(outcome["int32Key"] as! Int32 == 33) + #expect(outcome["int64Key"] as! Int64 == 65) } - func test_codes_uints() throws { + @Test func test_codes_uints() throws { let sut = ValueStore(values: [ "zeroKey": UInt(0), "oneKey": UInt(1), @@ -74,16 +74,16 @@ final class MockValueStoreTests: XCTestCase { let data = try JSONEncoder().encode(sut) let outcome = try JSONDecoder().decode(ValueStore.self, from: data) - XCTAssertEqual(outcome["zeroKey"] as! UInt, 0) - XCTAssertEqual(outcome["oneKey"] as! UInt, 1) - XCTAssertEqual(outcome["twoKey"] as! UInt, 2) - XCTAssertEqual(outcome["uint8Key"] as! UInt8, 9) - XCTAssertEqual(outcome["uint16Key"] as! UInt16, 17) - XCTAssertEqual(outcome["uint32Key"] as! UInt32, 33) - XCTAssertEqual(outcome["uint64Key"] as! UInt64, 65) + #expect(outcome["zeroKey"] as! UInt == 0) + #expect(outcome["oneKey"] as! UInt == 1) + #expect(outcome["twoKey"] as! UInt == 2) + #expect(outcome["uint8Key"] as! UInt8 == 9) + #expect(outcome["uint16Key"] as! UInt16 == 17) + #expect(outcome["uint32Key"] as! UInt32 == 33) + #expect(outcome["uint64Key"] as! UInt64 == 65) } - func test_codes_double() throws { + @Test func test_codes_double() throws { let sut = ValueStore(values: [ "doubleKey": Double(3.14) ]) @@ -91,10 +91,10 @@ final class MockValueStoreTests: XCTestCase { let outcome = try JSONDecoder().decode(ValueStore.self, from: data) let doubleValue = outcome["doubleKey"] as? Double - XCTAssertEqual(doubleValue, 3.14) + #expect(doubleValue == 3.14) } - func test_codes_float() throws { + @Test func test_codes_float() throws { let sut = ValueStore(values: [ "floatKey": Float(3.14) ]) @@ -102,10 +102,10 @@ final class MockValueStoreTests: XCTestCase { let outcome = try JSONDecoder().decode(ValueStore.self, from: data) let floatValue = outcome["floatKey"] as? Float - XCTAssertEqual(floatValue, 3.14) + #expect(floatValue == 3.14) } - func test_codes_bool() throws { + @Test func test_codes_bool() throws { let sut = ValueStore(values: [ "trueValue": true, "falseValue": false @@ -115,11 +115,11 @@ final class MockValueStoreTests: XCTestCase { let trueValue = outcome["trueValue"] as! Bool let falseValue = outcome["falseValue"] as! Bool - XCTAssertTrue(trueValue) - XCTAssertFalse(falseValue) + #expect(trueValue == true) + #expect(falseValue == false) } - func test_codes_nsnumber() throws { + @Test func test_codes_nsnumber() throws { let sut = ValueStore(values: [ "_force_nstype_numberKey": NSNumber(floatLiteral: 2.5) ]) @@ -128,10 +128,10 @@ final class MockValueStoreTests: XCTestCase { let numberValue = outcome["_force_nstype_numberKey"] as? NSNumber let expected = NSNumber(floatLiteral: 2.5) - XCTAssertEqual(numberValue, expected) + #expect(numberValue == expected) } - func test_codes_array() throws { + @Test func test_codes_array() throws { let sut = ValueStore(values: [ "texts": ["one", "two", "three"] ]) @@ -140,10 +140,10 @@ final class MockValueStoreTests: XCTestCase { let outcome = try JSONDecoder().decode(ValueStore.self, from: data) let arrayValue = outcome["texts"] as? Array - XCTAssertEqual(arrayValue, ["one", "two", "three"]) + #expect(arrayValue == ["one", "two", "three"]) } - func test_codes_nsArray() throws { + @Test func test_codes_nsArray() throws { let sut = ValueStore(values: [ "texts": NSArray(array: ["one", "two", "three"]) ]) @@ -152,10 +152,10 @@ final class MockValueStoreTests: XCTestCase { let outcome = try JSONDecoder().decode(ValueStore.self, from: data) let arrayValue = outcome["texts"] as? Array - XCTAssertEqual(arrayValue, ["one", "two", "three"]) + #expect(arrayValue == ["one", "two", "three"]) } - func test_codes_date() throws { + @Test func test_codes_date() throws { let date = Date() let sut = ValueStore(values: [ "dateKey": date @@ -164,10 +164,10 @@ final class MockValueStoreTests: XCTestCase { let outcome = try JSONDecoder().decode(ValueStore.self, from: data) let dateValue = outcome["dateKey"] as? Date - XCTAssertEqual(dateValue, date) + #expect(dateValue == date) } - func test_codes_nsDate() throws { + @Test func test_codes_nsDate() throws { let nsDate = NSDate() let sut = ValueStore(values: [ "_force_nstype_dateKey": nsDate @@ -176,10 +176,10 @@ final class MockValueStoreTests: XCTestCase { let outcome = try JSONDecoder().decode(ValueStore.self, from: data) let nsDateValue = outcome["_force_nstype_dateKey"] as? NSDate - XCTAssertEqual(nsDateValue, nsDate) + #expect(nsDateValue == nsDate) } - func test_codes_data() throws { + @Test func test_codes_data() throws { let sut = ValueStore(values: [ "dataKey": Data([2, 4, 7]) ]) @@ -187,10 +187,10 @@ final class MockValueStoreTests: XCTestCase { let outcome = try JSONDecoder().decode(ValueStore.self, from: data) let dataValue = outcome["dataKey"] as? Data - XCTAssertEqual(dataValue, Data([2, 4, 7])) + #expect(dataValue == Data([2, 4, 7])) } - func test_codes_nsData() throws { + @Test func test_codes_nsData() throws { let nsData = NSData(bytes: [0x01, 0x02, 0x04] as [UInt8], length: 3) let sut = ValueStore(values: [ "_force_nstype_dataKey": nsData @@ -199,10 +199,10 @@ final class MockValueStoreTests: XCTestCase { let outcome = try JSONDecoder().decode(ValueStore.self, from: data) let nsDataValue = outcome["_force_nstype_dataKey"] as? NSData - XCTAssertEqual(nsDataValue, NSData(bytes: [0x01, 0x02, 0x04] as [UInt8], length: 3)) + #expect(nsDataValue == NSData(bytes: [0x01, 0x02, 0x04] as [UInt8], length: 3)) } - func test_codes_ckAsset() throws { + @Test func test_codes_ckAsset() throws { let url = Bundle.module.url(forResource: "textFile", withExtension: "txt")! let ckAsset = CKAsset(fileURL: url) let sut = ValueStore(values: [ @@ -211,10 +211,10 @@ final class MockValueStoreTests: XCTestCase { let data = try JSONEncoder().encode(sut) let outcome = try JSONDecoder().decode(ValueStore.self, from: data) let asset = outcome["assetKey"] as? CKAsset - XCTAssertEqual(asset!.fileURL!.lastPathComponent, "textFile.txt") + #expect(asset!.fileURL!.lastPathComponent == "textFile.txt") } - func test_codes_clLocation() throws { + @Test func test_codes_clLocation() throws { let location = CLLocation(latitude: 37.332939350106514, longitude: -122.00488014474543) let sut = ValueStore(values: [ "locationKey": location @@ -223,10 +223,10 @@ final class MockValueStoreTests: XCTestCase { let data = try JSONEncoder().encode(sut) let outcome = try JSONDecoder().decode(ValueStore.self, from: data) let locationValue = outcome["locationKey"] as? CLLocation - XCTAssertEqual(locationValue!.distance(from: location), 0) + #expect(locationValue!.distance(from: location) == 0) } - func test_codes_ckRecordReference() throws { + @Test func test_codes_ckRecordReference() throws { let reference = CKRecord.Reference(recordID: .init(recordName: "demoRecord"), action: .none) let sut = ValueStore(values: [ "recordReferenceKey": reference @@ -234,10 +234,10 @@ final class MockValueStoreTests: XCTestCase { let data = try JSONEncoder().encode(sut) let outcome = try JSONDecoder().decode(ValueStore.self, from: data) let referenceValue = outcome["recordReferenceKey"] as? CKRecord.Reference - XCTAssertEqual(referenceValue?.recordID.recordName, "demoRecord") + #expect(referenceValue?.recordID.recordName == "demoRecord") } - func test_codes_ckRecordReference_array() throws { + @Test func test_codes_ckRecordReference_array() throws { let reference1 = CKRecord.Reference(recordID: .init(recordName: "demoRecord1"), action: .none) let reference2 = CKRecord.Reference(recordID: .init(recordName: "demoRecord2"), action: .none) let sut = ValueStore(values: [ @@ -246,13 +246,13 @@ final class MockValueStoreTests: XCTestCase { let data = try JSONEncoder().encode(sut) let outcome = try JSONDecoder().decode(ValueStore.self, from: data) let references = outcome["recordReferenceArrayKey"] as? [CKRecord.Reference] - XCTAssertEqual(references![0].recordID.recordName, "demoRecord1") - XCTAssertEqual(references![1].recordID.recordName, "demoRecord2") + #expect(references![0].recordID.recordName == "demoRecord1") + #expect(references![1].recordID.recordName == "demoRecord2") } // MARK: - Error and invalid data handling - func test_throws_on_invalid_data_type() { + @Test func test_throws_on_invalid_data_type() { let brokenTypeJson = "[{\"value\":42,\"key\":\"intKey\",\"type\":\"BrokenType\"}]" let data = brokenTypeJson.data(using: .utf8)! do { @@ -261,14 +261,14 @@ final class MockValueStoreTests: XCTestCase { let dataCorruptedError = error as! DecodingError switch dataCorruptedError { case .dataCorrupted(let context): - XCTAssertEqual(context.debugDescription, "Invalid data type: BrokenType") + #expect(context.debugDescription == "Invalid data type: BrokenType") default: - XCTFail("Unexpected error: \(dataCorruptedError)") + Issue.record("Unexpected error: \(dataCorruptedError)") } } } - func test_throws_on_invalid_nsnumber_data() { + @Test func test_throws_on_invalid_nsnumber_data() { let brokenTypeJson = "[{\"value\":\"deadbeef\",\"key\":\"intKey\",\"type\":\"nsNumber\"}]" let data = brokenTypeJson.data(using: .utf8)! do { @@ -277,14 +277,14 @@ final class MockValueStoreTests: XCTestCase { let dataCorruptedError = error as! DecodingError switch dataCorruptedError { case .dataCorrupted(let context): - XCTAssertEqual(context.debugDescription, "Invalid NSNumber value in source data") + #expect(context.debugDescription == "Invalid NSNumber value in source data") default: - XCTFail("Unexpected error: \(dataCorruptedError)") + Issue.record("Unexpected error: \(dataCorruptedError)") } } } - func test_throws_on_invalid_nsstring_data() { + @Test func test_throws_on_invalid_nsstring_data() { let brokenTypeJson = "[{\"value\":\"deadbeef\",\"key\":\"intKey\",\"type\":\"nsString\"}]" let data = brokenTypeJson.data(using: .utf8)! do { @@ -293,14 +293,14 @@ final class MockValueStoreTests: XCTestCase { let dataCorruptedError = error as! DecodingError switch dataCorruptedError { case .dataCorrupted(let context): - XCTAssertEqual(context.debugDescription, "Invalid NSString value in source data") + #expect(context.debugDescription == "Invalid NSString value in source data") default: - XCTFail("Unexpected error: \(dataCorruptedError)") + Issue.record("Unexpected error: \(dataCorruptedError)") } } } - func test_throws_on_invalid_nsdate_data() { + @Test func test_throws_on_invalid_nsdate_data() { let brokenTypeJson = "[{\"value\":\"deadbeef\",\"key\":\"dateKey\",\"type\":\"nsDate\"}]" let data = brokenTypeJson.data(using: .utf8)! do { @@ -309,14 +309,14 @@ final class MockValueStoreTests: XCTestCase { let dataCorruptedError = error as! DecodingError switch dataCorruptedError { case .dataCorrupted(let context): - XCTAssertEqual(context.debugDescription, "Invalid NSDate value in source data") + #expect(context.debugDescription == "Invalid NSDate value in source data") default: - XCTFail("Unexpected error: \(dataCorruptedError)") + Issue.record("Unexpected error: \(dataCorruptedError)") } } } - func test_throws_on_invalid_nsdata_data() { + @Test func test_throws_on_invalid_nsdata_data() { let brokenTypeJson = "[{\"value\":\"deadbeef\",\"key\":\"dataKey\",\"type\":\"nsData\"}]" let data = brokenTypeJson.data(using: .utf8)! do { @@ -325,14 +325,14 @@ final class MockValueStoreTests: XCTestCase { let dataCorruptedError = error as! DecodingError switch dataCorruptedError { case .dataCorrupted(let context): - XCTAssertEqual(context.debugDescription, "Invalid NSData value in source data") + #expect(context.debugDescription == "Invalid NSData value in source data") default: - XCTFail("Unexpected error: \(dataCorruptedError)") + Issue.record("Unexpected error: \(dataCorruptedError)") } } } - func test_throws_on_invalid_location_data() { + @Test func test_throws_on_invalid_location_data() { let brokenTypeJson = "[{\"value\":\"deadbeef\",\"key\":\"dataKey\",\"type\":\"clLocation\"}]" let data = brokenTypeJson.data(using: .utf8)! do { @@ -341,14 +341,14 @@ final class MockValueStoreTests: XCTestCase { let dataCorruptedError = error as! DecodingError switch dataCorruptedError { case .dataCorrupted(let context): - XCTAssertEqual(context.debugDescription, "Invalid CLLocation value in source data") + #expect(context.debugDescription == "Invalid CLLocation value in source data") default: - XCTFail("Unexpected error: \(dataCorruptedError)") + Issue.record("Unexpected error: \(dataCorruptedError)") } } } - func test_throws_on_invalid_ckRecordReference_data() { + @Test func test_throws_on_invalid_ckRecordReference_data() { let brokenTypeJson = "[{\"value\":\"deadbeef\",\"key\":\"dataKey\",\"type\":\"ckRecordReference\"}]" let data = brokenTypeJson.data(using: .utf8)! do { @@ -357,16 +357,16 @@ final class MockValueStoreTests: XCTestCase { let dataCorruptedError = error as! DecodingError switch dataCorruptedError { case .dataCorrupted(let context): - XCTAssertEqual(context.debugDescription, "Invalid CKRecord.Reference value in source data") + #expect(context.debugDescription == "Invalid CKRecord.Reference value in source data") default: - XCTFail("Unexpected error: \(dataCorruptedError)") + Issue.record("Unexpected error: \(dataCorruptedError)") } } } - func test_invalid_ckAsset_url() { + @Test func test_invalid_ckAsset_url() { // CKAsset API says that “if the system can’t create the asset” (and I’d expect - // the URL pointing to nonexistent file to tigger this), the return value + // the URL pointing to nonexistent file to trigger this), the return value // will be nil. // // In reality, there seems to be no file validation at asset creation @@ -377,6 +377,6 @@ final class MockValueStoreTests: XCTestCase { let data = brokenAssetJson.data(using: .utf8)! let store = try? JSONDecoder().decode(ValueStore.self, from: data) let asset = store!["assetKey"] as? CKAsset - XCTAssertEqual(asset!.fileURL!.lastPathComponent, "textFile.txt") + #expect(asset!.fileURL!.lastPathComponent == "textFile.txt") } } diff --git a/Targets/Canopy/Tests/ModifyRecordsFeatureTests.swift b/Targets/Canopy/Tests/ModifyRecordsFeatureTests.swift index d46b1af..38ce85b 100644 --- a/Targets/Canopy/Tests/ModifyRecordsFeatureTests.swift +++ b/Targets/Canopy/Tests/ModifyRecordsFeatureTests.swift @@ -2,9 +2,9 @@ import CanopyTestTools import CloudKit import Foundation -import XCTest +import Testing -final class ModifyRecordsFeatureTests: XCTestCase { +@Suite struct ModifyRecordsFeatureTests { private func records(startIndex: Int, endIndex: Int) -> [CKRecord] { stride(from: startIndex, to: endIndex + 1, by: 1).map { i in CKRecord(recordType: "TestRecord", recordID: .init(recordName: "id\(i)")) @@ -28,7 +28,7 @@ final class ModifyRecordsFeatureTests: XCTestCase { ) } - func test_fails_correctly_on_empty_input() async { + @Test func test_fails_correctly_on_empty_input() async { let db = ReplayingMockCKDatabase(operationResults: []) do { let _ = try await ModifyRecords.with( @@ -39,11 +39,11 @@ final class ModifyRecordsFeatureTests: XCTestCase { qualityOfService: .default ).get() } catch let recordError { - XCTAssertEqual(recordError, CKRecordError(from: CKError(CKError.Code.internalError))) + #expect(recordError == CKRecordError(from: CKError(CKError.Code.internalError))) } } - func test_simple_modify() async { + @Test func test_simple_modify() async { let recordsToSave = records(startIndex: 1, endIndex: 10) let db = ReplayingMockCKDatabase( operationResults: [ @@ -70,14 +70,14 @@ final class ModifyRecordsFeatureTests: XCTestCase { qualityOfService: .default ).get() - XCTAssertEqual(result.savedRecords.count, 10) - XCTAssertEqual(result.deletedRecordIDs.count, 0) + #expect(result.savedRecords.count == 10) + #expect(result.deletedRecordIDs.count == 0) let operationsRun = await db.operationsRun - XCTAssertEqual(operationsRun, 1) + #expect(operationsRun == 1) } - func test_simple_delete() async { + @Test func test_simple_delete() async { let recordIDsToDelete = records(startIndex: 1, endIndex: 10).map(\.recordID) let db = ReplayingMockCKDatabase( operationResults: [ @@ -103,14 +103,14 @@ final class ModifyRecordsFeatureTests: XCTestCase { qualityOfService: .default ).get() - XCTAssertEqual(result.deletedRecordIDs.count, 10) - XCTAssertEqual(result.savedRecords.count, 0) + #expect(result.deletedRecordIDs.count == 10) + #expect(result.savedRecords.count == 0) let operationsRun = await db.operationsRun - XCTAssertEqual(operationsRun, 1) + #expect(operationsRun == 1) } - func test_chunked_modify() async { + @Test func test_chunked_modify() async { let recordsToSave1 = records(startIndex: 1, endIndex: 3) let recordsToSave2 = records(startIndex: 4, endIndex: 6) let recordsToSave3 = records(startIndex: 7, endIndex: 9) @@ -180,14 +180,14 @@ final class ModifyRecordsFeatureTests: XCTestCase { customBatchSize: 3 ).get() - XCTAssertEqual(result.savedRecords.count, 11) - XCTAssertEqual(result.deletedRecordIDs.count, 0) + #expect(result.savedRecords.count == 11) + #expect(result.deletedRecordIDs.count == 0) let operationsRun = await db.operationsRun - XCTAssertEqual(operationsRun, 4) + #expect(operationsRun == 4) } - func test_cancellation() async { + @Test func test_cancellation() async { let recordsToSave1 = records(startIndex: 1, endIndex: 3) let recordsToSave2 = records(startIndex: 4, endIndex: 6) let recordsToSave3 = records(startIndex: 7, endIndex: 9) @@ -267,14 +267,14 @@ final class ModifyRecordsFeatureTests: XCTestCase { // Second get gets the operation result, and this will throw because the operation resulted with error. let _ = try await task.result.get().get() } catch { - XCTAssertEqual(error, CKRecordError(from: CKError(CKError.Code.operationCancelled))) + #expect(error == CKRecordError(from: CKError(CKError.Code.operationCancelled))) } let operationsRun = await db.operationsRun - XCTAssertEqual(operationsRun, 1) + #expect(operationsRun == 1) } - func test_modify_recorderror() async { + @Test func test_modify_recorderror() async { let recordsToSave = records(startIndex: 1, endIndex: 10) let db = ReplayingMockCKDatabase( operationResults: [ @@ -302,14 +302,14 @@ final class ModifyRecordsFeatureTests: XCTestCase { qualityOfService: .default ).get() } catch { - XCTAssertEqual(error, CKRecordError(from: CKError(CKError.Code.internalError))) + #expect(error == CKRecordError(from: CKError(CKError.Code.internalError))) } let operationsRun = await db.operationsRun - XCTAssertEqual(operationsRun, 1) + #expect(operationsRun == 1) } - func test_delete_recorderror() async { + @Test func test_delete_recorderror() async { let recordIDsToDelete = records(startIndex: 1, endIndex: 10).map(\.recordID) let db = ReplayingMockCKDatabase( operationResults: [ @@ -336,14 +336,14 @@ final class ModifyRecordsFeatureTests: XCTestCase { qualityOfService: .default ).get() } catch { - XCTAssertEqual(error, CKRecordError(from: CKError(CKError.Code.internalError))) + #expect(error == CKRecordError(from: CKError(CKError.Code.internalError))) } let operationsRun = await db.operationsRun - XCTAssertEqual(operationsRun, 1) + #expect(operationsRun == 1) } - func test_modify_resulterror() async { + @Test func test_modify_resulterror() async { let recordsToSave = records(startIndex: 1, endIndex: 10) let db = ReplayingMockCKDatabase( operationResults: [ @@ -371,14 +371,14 @@ final class ModifyRecordsFeatureTests: XCTestCase { qualityOfService: .default ).get() } catch { - XCTAssertEqual(error, CKRecordError(from: CKError(CKError.Code.networkFailure))) + #expect(error == CKRecordError(from: CKError(CKError.Code.networkFailure))) } let operationsRun = await db.operationsRun - XCTAssertEqual(operationsRun, 1) + #expect(operationsRun == 1) } - func test_limit_exceeded() async { + @Test func test_limit_exceeded() async { let recordsToSave = records(startIndex: 1, endIndex: 9) let recordIDToDelete = CKRecord.ID(recordName: "idToDelete") let db = ReplayingMockCKDatabase( @@ -434,17 +434,17 @@ final class ModifyRecordsFeatureTests: XCTestCase { customBatchSize: 9 ).get() - XCTAssertEqual(result.savedRecords.count, 9) + #expect(result.savedRecords.count == 9) for index in 0 ..< result.savedRecords.count { - XCTAssertTrue(result.savedRecords[index].isEqualToRecord(recordsToSave[index].canopyResultRecord)) + #expect(result.savedRecords[index].isEqualToRecord(recordsToSave[index].canopyResultRecord) == true) } - XCTAssertEqual(result.deletedRecordIDs.count, 1) + #expect(result.deletedRecordIDs.count == 1) let operationsRun = await db.operationsRun - XCTAssertEqual(operationsRun, 4) + #expect(operationsRun == 4) } - func test_limit_exceeded_without_autobatch() async { + @Test func test_limit_exceeded_without_autobatch() async { let recordsToSave = records(startIndex: 1, endIndex: 9) let recordIDToDelete = CKRecord.ID(recordName: "idToDelete") let db = ReplayingMockCKDatabase( @@ -470,13 +470,13 @@ final class ModifyRecordsFeatureTests: XCTestCase { autoBatchToSmallerWhenLimitExceeded: false ).get() } catch { - XCTAssertEqual(error, CKRecordError(from: CKError(CKError.Code.limitExceeded))) + #expect(error == CKRecordError(from: CKError(CKError.Code.limitExceeded))) let operationsRun = await db.operationsRun - XCTAssertEqual(operationsRun, 1) + #expect(operationsRun == 1) } } - func test_autoretry_one_pass() async { + @Test func test_autoretry_one_pass() async { let recordsToSave = records(startIndex: 0, endIndex: 0) let db = ReplayingMockCKDatabase( operationResults: [ @@ -502,13 +502,13 @@ final class ModifyRecordsFeatureTests: XCTestCase { qualityOfService: .default, autoRetryForRetriableErrors: true ).get() - XCTAssertTrue(result.savedRecords[0].isEqualToRecord(recordsToSave[0].canopyResultRecord)) - XCTAssertEqual(result.savedRecords.count, 1) + #expect(result.savedRecords[0].isEqualToRecord(recordsToSave[0].canopyResultRecord) == true) + #expect(result.savedRecords.count == 1) let operationsRun = await db.operationsRun - XCTAssertEqual(operationsRun, 1) + #expect(operationsRun == 1) } - func test_autoretry_one_retry() async { + @Test func test_autoretry_one_retry() async { let recordsToSave = records(startIndex: 0, endIndex: 0) let db = ReplayingMockCKDatabase( operationResults: [ @@ -527,13 +527,13 @@ final class ModifyRecordsFeatureTests: XCTestCase { qualityOfService: .default, autoRetryForRetriableErrors: true ).get() - XCTAssertTrue(result.savedRecords[0].isEqualToRecord(recordsToSave[0].canopyResultRecord)) - XCTAssertEqual(result.savedRecords.count, 1) + #expect(result.savedRecords[0].isEqualToRecord(recordsToSave[0].canopyResultRecord) == true) + #expect(result.savedRecords.count == 1) let operationsRun = await db.operationsRun - XCTAssertEqual(operationsRun, 2) + #expect(operationsRun == 2) } - func test_autoretry_two_retries() async { + @Test func test_autoretry_two_retries() async { let recordsToSave = records(startIndex: 0, endIndex: 0) let db = ReplayingMockCKDatabase( operationResults: [ @@ -553,13 +553,13 @@ final class ModifyRecordsFeatureTests: XCTestCase { qualityOfService: .default, autoRetryForRetriableErrors: true ).get() - XCTAssertTrue(result.savedRecords[0].isEqualToRecord(recordsToSave[0].canopyResultRecord)) - XCTAssertEqual(result.savedRecords.count, 1) + #expect(result.savedRecords[0].isEqualToRecord(recordsToSave[0].canopyResultRecord) == true) + #expect(result.savedRecords.count == 1) let operationsRun = await db.operationsRun - XCTAssertEqual(operationsRun, 3) + #expect(operationsRun == 3) } - func test_autoretry_three_retries_failure() async { + @Test func test_autoretry_three_retries_failure() async { let recordsToSave = records(startIndex: 0, endIndex: 0) let db = ReplayingMockCKDatabase( operationResults: [ @@ -578,9 +578,9 @@ final class ModifyRecordsFeatureTests: XCTestCase { autoRetryForRetriableErrors: true ).get() } catch { - XCTAssertTrue(error.code == CKError.Code.zoneBusy.rawValue) + #expect(error.code == CKError.Code.zoneBusy.rawValue) let operationsRun = await db.operationsRun - XCTAssertEqual(operationsRun, 3) + #expect(operationsRun == 3) } } } diff --git a/Targets/Canopy/Tests/ModifyRecordsResultTests.swift b/Targets/Canopy/Tests/ModifyRecordsResultTests.swift index 77cea2c..20572d7 100644 --- a/Targets/Canopy/Tests/ModifyRecordsResultTests.swift +++ b/Targets/Canopy/Tests/ModifyRecordsResultTests.swift @@ -1,10 +1,10 @@ import Canopy import CanopyTestTools import CloudKit -import XCTest +import Testing -final class ModifyRecordsResultTests: XCTestCase { - func test_codes() throws { +@Suite struct ModifyRecordsResultTests { + @Test func test_codes() throws { let savedRecords = [CanopyResultRecord.mock(.init(recordType: "MockType"))] let deletedRecordIDs = [CKRecord.ID(recordName: "deletedId")] let modifyRecordsResult = ModifyRecordsResult( @@ -13,18 +13,18 @@ final class ModifyRecordsResultTests: XCTestCase { ) let coded = try JSONEncoder().encode(modifyRecordsResult) let decoded = try JSONDecoder().decode(ModifyRecordsResult.self, from: coded) - XCTAssertEqual(modifyRecordsResult, decoded) + #expect(modifyRecordsResult == decoded) } - func test_throws_on_bad_deleted_ids_data() { + @Test func test_throws_on_bad_deleted_ids_data() { let badJson = "{\"savedRecords\":[],\"deletedRecordIDs\":\"deadbeef\"}" let data = badJson.data(using: .utf8)! do { let _ = try JSONDecoder().decode(ModifyRecordsResult.self, from: data) } catch DecodingError.dataCorrupted(let context) { - XCTAssertEqual(context.debugDescription, "Invalid deleted record IDs value in source data") + #expect(context.debugDescription == "Invalid deleted record IDs value in source data") } catch { - XCTFail("Unexpected error: \(error)") + Issue.record("Unexpected error: \(error)") } } } diff --git a/Targets/Canopy/Tests/ModifySubscriptionResultTests.swift b/Targets/Canopy/Tests/ModifySubscriptionResultTests.swift index dc6c59e..9cd0e3e 100644 --- a/Targets/Canopy/Tests/ModifySubscriptionResultTests.swift +++ b/Targets/Canopy/Tests/ModifySubscriptionResultTests.swift @@ -1,9 +1,9 @@ import Canopy import CloudKit -import XCTest +import Testing -final class ModifySubscriptionResultTests: XCTestCase { - func test_codes() throws { +@Suite struct ModifySubscriptionResultTests { + @Test func test_codes() throws { let result = ModifySubscriptionsResult( savedSubscriptions: [ CKDatabaseSubscription(subscriptionID: "db1"), @@ -18,20 +18,20 @@ final class ModifySubscriptionResultTests: XCTestCase { let encoded = try JSONEncoder().encode(result) let decoded = try JSONDecoder().decode(ModifySubscriptionsResult.self, from: encoded) - XCTAssertEqual(decoded.deletedSubscriptionIDs, ["deletedID1", "deletedID2"]) - XCTAssertEqual(decoded.savedSubscriptions[0].subscriptionID, "db1") - XCTAssertEqual(decoded.savedSubscriptions[1].subscriptionID, "query1") + #expect(decoded.deletedSubscriptionIDs == ["deletedID1", "deletedID2"]) + #expect(decoded.savedSubscriptions[0].subscriptionID == "db1") + #expect(decoded.savedSubscriptions[1].subscriptionID == "query1") } - func test_throws_on_invalid_saved_subscriptions_data() { + @Test func test_throws_on_invalid_saved_subscriptions_data() { let badJson = "{\"savedSubscriptions\":\"deadBeef\",\"deletedSubscriptionIDs\":[\"sub1\",\"sub2\"]}" let data = badJson.data(using: .utf8)! do { let _ = try JSONDecoder().decode(ModifySubscriptionsResult.self, from: data) } catch DecodingError.dataCorrupted(let context) { - XCTAssertEqual(context.debugDescription, "Invalid saved subscriptions value in source data") + #expect(context.debugDescription == "Invalid saved subscriptions value in source data") } catch { - XCTFail("Unexpected error: \(error)") + Issue.record("Unexpected error: \(error)") } } } diff --git a/Targets/Canopy/Tests/ModifyZonesResultTests.swift b/Targets/Canopy/Tests/ModifyZonesResultTests.swift index 510751d..b0dafd5 100644 --- a/Targets/Canopy/Tests/ModifyZonesResultTests.swift +++ b/Targets/Canopy/Tests/ModifyZonesResultTests.swift @@ -1,10 +1,10 @@ import Canopy import CanopyTestTools import CloudKit -import XCTest +import Testing -final class ModifyZonesResultTests: XCTestCase { - func test_codes() throws { +@Suite struct ModifyZonesResultTests { + @Test func test_codes() throws { let savedZones = [ CKRecordZone(zoneID: .init(zoneName: "someZone1", ownerName: "someOwner")), CKRecordZone(zoneID: .init(zoneName: "someZone2", ownerName: "someOwner")) @@ -19,31 +19,31 @@ final class ModifyZonesResultTests: XCTestCase { ) let coded = try JSONEncoder().encode(modifyZonesResult) let decoded = try JSONDecoder().decode(ModifyZonesResult.self, from: coded) - XCTAssertEqual(decoded.savedZones[1].zoneID.zoneName, "someZone2") - XCTAssertEqual(decoded.deletedZoneIDs[1].zoneName, "deletedId2") + #expect(decoded.savedZones[1].zoneID.zoneName == "someZone2") + #expect(decoded.deletedZoneIDs[1].zoneName == "deletedId2") } - func test_throws_on_bad_saved_zones_data() { + @Test func test_throws_on_bad_saved_zones_data() { let badJson = "{\"deletedZoneIDs\":\"\",\"savedZones\":\"deadbeef\"}" let data = badJson.data(using: .utf8)! do { let _ = try JSONDecoder().decode(ModifyZonesResult.self, from: data) } catch DecodingError.dataCorrupted(let context) { - XCTAssertEqual(context.debugDescription, "Invalid saved zones value in source data") + #expect(context.debugDescription == "Invalid saved zones value in source data") } catch { - XCTFail("Unexpected error: \(error)") + Issue.record("Unexpected error: \(error)") } } - func test_throws_on_bad_deleted_zoneIDs_data() { + @Test func test_throws_on_bad_deleted_zoneIDs_data() { let badJson = "{\"savedZones\":\"YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGtCwwTNj9AQSpHSlpfYFUkbnVsbNINDg8SWk5TLm9iamVjdHNWJGNsYXNzohARgAKACYAM3xASFBUWFxgZGhscHR4fICEiDiMkJSUlJSkqJSUtKiUvLSUyMyUlWFBDU0tleUlEXFpvbmVpc2hLZXlJRF8QEUNsaWVudENoYW5nZVRva2VuXlNoYXJlUmVmZXJlbmNlW0RldmljZUNvdW50XxAPQXNzZXRRdW90YVVzYWdlXkV4cGlyYXRpb25EYXRlXxATUENTTW9kaWZpY2F0aW9uRGF0ZVdFeHBpcmVkXxASTWV0YWRhdGFRdW90YVVzYWdlXxAWUHJldmlvdXNQcm90ZWN0aW9uRXRhZ1Zab25lSURfEBRIYXNVcGRhdGVkRXhwaXJhdGlvbl8QEVVwZGF0ZWRFeHBpcmF0aW9uXENhcGFiaWxpdGllc18QE0ludml0ZWRLZXlzVG9SZW1vdmVfEBhDdXJyZW50U2VydmVyQ2hhbmdlVG9rZW6AAIAAgACAABAAEACAAIAACIAAgAMIgACAB4AIgACAANU3ODk6DiolPD0+XxAQZGF0YWJhc2VTY29wZUtleV8QEWFub255bW91c0NLVXNlcklEWW93bmVyTmFtZVhab25lTmFtZYAAgAWABIAGWXNvbWVab25lMVlzb21lT3duZXLSQkNERVokY2xhc3NuYW1lWCRjbGFzc2VzXkNLUmVjb3JkWm9uZUlEokRGWE5TT2JqZWN00kJDSElcQ0tSZWNvcmRab25lokhG3xASFBUWFxgZGhscHR4fICEiDiMkJSUlJSkqJSUtKiVTLSUyMyUlgACAAIAAgACAAIAACIAAgAoIgACAB4AIgACAANU3ODk6DiolPF0+gACABYALgAZZc29tZVpvbmUy0kJDYWJXTlNBcnJheaJhRgAIABEAGgAkACkAMgA3AEkATABRAFMAYQBnAGwAdwB+AIEAgwCFAIcArgC3AMQA2ADnAPMBBQEUASoBMgFHAWABZwF+AZIBnwG1AdAB0gHUAdYB2AHaAdwB3gHgAeEB4wHlAeYB6AHqAewB7gHwAfsCDgIiAiwCNQI3AjkCOwI9AkcCUQJWAmECagJ5AnwChQKKApcCmgLBAsMCxQLHAskCywLNAs4C0ALSAtMC1QLXAtkC2wLdAugC6gLsAu4C8AL6Av8DBwAAAAAAAAIBAAAAAAAAAGMAAAAAAAAAAAAAAAAAAAMK\",\"deletedZoneIDs\":\"deadbeef\"}" let data = badJson.data(using: .utf8)! do { let _ = try JSONDecoder().decode(ModifyZonesResult.self, from: data) } catch DecodingError.dataCorrupted(let context) { - XCTAssertEqual(context.debugDescription, "Invalid deleted record zone IDs value in source data") + #expect(context.debugDescription == "Invalid deleted record zone IDs value in source data") } catch { - XCTFail("Unexpected error: \(error)") + Issue.record("Unexpected error: \(error)") } } } diff --git a/Targets/Canopy/Tests/ResultsTypesTests.swift b/Targets/Canopy/Tests/ResultsTypesTests.swift index 9c485cb..848b991 100644 --- a/Targets/Canopy/Tests/ResultsTypesTests.swift +++ b/Targets/Canopy/Tests/ResultsTypesTests.swift @@ -1,12 +1,12 @@ @testable import Canopy import CloudKit -import XCTest +import Testing -final class ResultsTypesTests: XCTestCase { - func test_empty_database_changes_result() { +@Suite struct ResultsTypesTests { + @Test func test_empty_database_changes_result() { let empty = FetchDatabaseChangesResult.empty - XCTAssertEqual( - empty, + #expect( + empty == .init( changedRecordZoneIDs: [], deletedRecordZoneIDs: [], @@ -15,11 +15,11 @@ final class ResultsTypesTests: XCTestCase { ) } - func test_deleted_ckrecord() { + @Test func test_deleted_ckrecord() { let zoneID = CKRecordZone.ID(zoneName: "someZone", ownerName: "someOtherPerson") let recordID = CKRecord.ID(recordName: "deletedRecordID", zoneID: zoneID) let deletedCKRecord = DeletedCKRecord(recordID: recordID, recordType: "DeletedRecordType") - XCTAssertEqual(deletedCKRecord.recordType, "DeletedRecordType") - XCTAssertEqual(deletedCKRecord.recordID, recordID) + #expect(deletedCKRecord.recordType == "DeletedRecordType") + #expect(deletedCKRecord.recordID == recordID) } } From 59964a6db6f56b06ee9c464050205dd33802005f Mon Sep 17 00:00:00 2001 From: Jaanus Kase Date: Thu, 23 Oct 2025 12:58:58 +0300 Subject: [PATCH 12/20] Migrated to Swift Testing --- Targets/Canopy/Tests/ModifyRecordsTests.swift | 60 ++++----- .../Tests/QueryRecordsFeatureTests.swift | 56 ++++---- .../Tests/ReplayingMockContainerTests.swift | 44 +++--- .../Tests/ReplayingMockDatabaseTests.swift | 126 +++++++++--------- 4 files changed, 142 insertions(+), 144 deletions(-) diff --git a/Targets/Canopy/Tests/ModifyRecordsTests.swift b/Targets/Canopy/Tests/ModifyRecordsTests.swift index 9d4dea5..a91e0b2 100644 --- a/Targets/Canopy/Tests/ModifyRecordsTests.swift +++ b/Targets/Canopy/Tests/ModifyRecordsTests.swift @@ -1,9 +1,9 @@ @testable import Canopy import CanopyTestTools import CloudKit -import XCTest +import Testing -final class ModifyRecordsTests: XCTestCase { +@Suite struct ModifyRecordsTests { private func databaseAPI(_ db: CKDatabaseType, settings: CanopySettingsType = CanopySettings()) -> CKDatabaseAPIType { CKDatabaseAPI(database: db, databaseScope: .private, settingsProvider: { settings }, tokenStore: TestTokenStore()) } @@ -31,7 +31,7 @@ final class ModifyRecordsTests: XCTestCase { ) } - func test_success() async { + @Test func test_success() async { let recordID = CKRecord.ID(recordName: "TestRecordName") let record = CKRecord(recordType: "TestRecord", recordID: recordID) let db = ReplayingMockCKDatabase( @@ -54,11 +54,11 @@ final class ModifyRecordsTests: XCTestCase { let api = databaseAPI(db) let result = try! await api.modifyRecords(saving: [record]).get() - XCTAssertTrue(result.savedRecords.first!.isEqualToRecord(record.canopyResultRecord)) - XCTAssertEqual(result.deletedRecordIDs, []) + #expect(result.savedRecords.first!.isEqualToRecord(record.canopyResultRecord) == true) + #expect(result.deletedRecordIDs == []) } - func test_success_with_delay() async { + @Test func test_success_with_delay() async { let recordID = CKRecord.ID(recordName: "TestRecordName") let record = CKRecord(recordType: "TestRecord", recordID: recordID) let db = ReplayingMockCKDatabase( @@ -81,11 +81,11 @@ final class ModifyRecordsTests: XCTestCase { let api = databaseAPI(db, settings: CanopySettings(modifyRecordsBehavior: .regular(0.01))) let result = try! await api.modifyRecords(saving: [record]).get() - XCTAssertTrue(result.savedRecords.first!.isEqualToRecord(record.canopyResultRecord)) - XCTAssertEqual(result.deletedRecordIDs, []) + #expect(result.savedRecords.first!.isEqualToRecord(record.canopyResultRecord) == true) + #expect(result.deletedRecordIDs == []) } - func test_simulated_fail() async { + @Test func test_simulated_fail() async { let recordID = CKRecord.ID(recordName: "TestRecordName") let record = CKRecord(recordType: "TestRecord", recordID: recordID) let db = ReplayingMockCKDatabase( @@ -109,12 +109,11 @@ final class ModifyRecordsTests: XCTestCase { do { let _ = try await api.modifyRecords(saving: [record]).get() } catch { - XCTAssertNotNil(error) - XCTAssertEqual(error.batchErrors, [:]) + #expect(error.batchErrors == [:]) } } - func test_simulated_fail_with_delay() async { + @Test func test_simulated_fail_with_delay() async { let recordID = CKRecord.ID(recordName: "TestRecordName") let record = CKRecord(recordType: "TestRecord", recordID: recordID) let db = ReplayingMockCKDatabase( @@ -138,12 +137,11 @@ final class ModifyRecordsTests: XCTestCase { do { let _ = try await api.modifyRecords(saving: [record]).get() } catch { - XCTAssertNotNil(error) - XCTAssertEqual(error.batchErrors, [:]) + #expect(error.batchErrors == [:]) } } - func test_simulated_fail_with_partial_errors() async { + @Test func test_simulated_fail_with_partial_errors() async { let recordID = CKRecord.ID(recordName: "TestRecordName") let recordIDToDelete = CKRecord.ID(recordName: "TestRecordNameToDelete") let record = CKRecord(recordType: "TestRecord", recordID: recordID) @@ -176,11 +174,11 @@ final class ModifyRecordsTests: XCTestCase { deleting: [recordIDToDelete] ).get() } catch { - XCTAssertEqual(error.batchErrors.count, 2) + #expect(error.batchErrors.count == 2) } } - func test_explicit_autobatch_true() async { + @Test func test_explicit_autobatch_true() async { let recordsToSave = records(startIndex: 1, endIndex: 10) let recordIDToDelete = CKRecord.ID(recordName: "idToDelete") let db = ReplayingMockCKDatabase( @@ -221,17 +219,17 @@ final class ModifyRecordsTests: XCTestCase { deleting: [recordIDToDelete] ).get() - XCTAssertEqual(result.savedRecords.count, 10) + #expect(result.savedRecords.count == 10) for index in 0 ..< result.savedRecords.count { - XCTAssertTrue(result.savedRecords[index].isEqualToRecord(recordsToSave[index].canopyResultRecord)) + #expect(result.savedRecords[index].isEqualToRecord(recordsToSave[index].canopyResultRecord) == true) } - XCTAssertEqual(result.deletedRecordIDs.count, 1) + #expect(result.deletedRecordIDs.count == 1) let operationsRun = await db.operationsRun - XCTAssertEqual(operationsRun, 2) + #expect(operationsRun == 2) } - func test_explicit_autobatch_false() async { + @Test func test_explicit_autobatch_false() async { let recordsToSave = records(startIndex: 1, endIndex: 10) let recordIDToDelete = CKRecord.ID(recordName: "idToDelete") let db = ReplayingMockCKDatabase( @@ -259,13 +257,13 @@ final class ModifyRecordsTests: XCTestCase { deleting: [recordIDToDelete] ).get() } catch { - XCTAssertEqual(error, CKRecordError(from: CKError(CKError.Code.limitExceeded))) + #expect(error == CKRecordError(from: CKError(CKError.Code.limitExceeded))) let operationsRun = await db.operationsRun - XCTAssertEqual(operationsRun, 1) + #expect(operationsRun == 1) } } - func test_explicit_autoretry_true() async { + @Test func test_explicit_autoretry_true() async { let recordsToSave = records(startIndex: 0, endIndex: 0) let db = ReplayingMockCKDatabase( operationResults: [ @@ -289,13 +287,13 @@ final class ModifyRecordsTests: XCTestCase { deleting: [] ).get() - XCTAssertTrue(result.savedRecords[0].isEqualToRecord(recordsToSave[0].canopyResultRecord)) - XCTAssertEqual(result.savedRecords.count, 1) + #expect(result.savedRecords[0].isEqualToRecord(recordsToSave[0].canopyResultRecord)) + #expect(result.savedRecords.count == 1) let operationsRun = await db.operationsRun - XCTAssertEqual(operationsRun, 2) + #expect(operationsRun == 2) } - func test_explicit_autoretry_false() async { + @Test func test_explicit_autoretry_false() async { let recordsToSave = records(startIndex: 0, endIndex: 0) let db = ReplayingMockCKDatabase( operationResults: [ @@ -320,9 +318,9 @@ final class ModifyRecordsTests: XCTestCase { deleting: [] ).get() } catch { - XCTAssertEqual(error, CKRecordError(from: CKError(CKError.Code.zoneBusy, userInfo: [CKErrorRetryAfterKey: 0.2]))) + #expect(error == CKRecordError(from: CKError(CKError.Code.zoneBusy, userInfo: [CKErrorRetryAfterKey: 0.2]))) let operationsRun = await db.operationsRun - XCTAssertEqual(operationsRun, 1) + #expect(operationsRun == 1) } } } diff --git a/Targets/Canopy/Tests/QueryRecordsFeatureTests.swift b/Targets/Canopy/Tests/QueryRecordsFeatureTests.swift index 5ad9d0f..b52229c 100644 --- a/Targets/Canopy/Tests/QueryRecordsFeatureTests.swift +++ b/Targets/Canopy/Tests/QueryRecordsFeatureTests.swift @@ -2,16 +2,16 @@ import CanopyTestTools import CloudKit import Foundation -import XCTest +import Testing -final class QueryRecordsFeatureTests: XCTestCase { - func records(startIndex: Int, endIndex: Int) -> [CKRecord] { +@Suite struct QueryRecordsFeatureTests { + private func records(startIndex: Int, endIndex: Int) -> [CKRecord] { stride(from: startIndex, to: endIndex + 1, by: 1).map { i in CKRecord(recordType: "TestRecord", recordID: .init(recordName: "id\(i)")) } } - func test_simple_query() async { + @Test func test_simple_query() async { let query = CKQuery(recordType: "TestRecord", predicate: NSPredicate(value: true)) let db = ReplayingMockCKDatabase( operationResults: [ @@ -32,12 +32,12 @@ final class QueryRecordsFeatureTests: XCTestCase { database: db ).get() - XCTAssertEqual(results.count, 10) + #expect(results.count == 10) let operationsRun = await db.operationsRun - XCTAssertEqual(operationsRun, 1) + #expect(operationsRun == 1) } - func test_simple_nested_query() async { + @Test func test_simple_nested_query() async { let query = CKQuery(recordType: "TestRecord", predicate: NSPredicate(value: true)) let db = ReplayingMockCKDatabase( operationResults: [ @@ -64,14 +64,14 @@ final class QueryRecordsFeatureTests: XCTestCase { recordZoneID: nil, database: db ).get() - XCTAssertEqual(records.count, 20) + #expect(records.count == 20) let operationsRun = await db.operationsRun - XCTAssertEqual(operationsRun, 2) - XCTAssertEqual(records[0].recordID.recordName, "id1") - XCTAssertEqual(records[19].recordID.recordName, "id20") + #expect(operationsRun == 2) + #expect(records[0].recordID.recordName == "id1") + #expect(records[19].recordID.recordName == "id20") } - func test_results_limit_query() async { + @Test func test_results_limit_query() async { let query = CKQuery(recordType: "TestRecord", predicate: NSPredicate(value: true)) let db = ReplayingMockCKDatabase( operationResults: [ @@ -92,14 +92,14 @@ final class QueryRecordsFeatureTests: XCTestCase { database: db, resultsLimit: 10 ).get() - XCTAssertEqual(records.count, 10) + #expect(records.count == 10) let operationsRun = await db.operationsRun - XCTAssertEqual(operationsRun, 1) - XCTAssertEqual(records[0].recordID.recordName, "id1") - XCTAssertEqual(records[9].recordID.recordName, "id10") + #expect(operationsRun == 1) + #expect(records[0].recordID.recordName == "id1") + #expect(records[9].recordID.recordName == "id10") } - func test_depth3_query() async { + @Test func test_depth3_query() async { let query = CKQuery(recordType: "TestRecord", predicate: NSPredicate(value: true)) let db = ReplayingMockCKDatabase( operationResults: [ @@ -136,14 +136,14 @@ final class QueryRecordsFeatureTests: XCTestCase { database: db ).get() - XCTAssertEqual(records.count, 9) + #expect(records.count == 9) let operationsRun = await db.operationsRun - XCTAssertEqual(operationsRun, 3) - XCTAssertEqual(records[0].recordID.recordName, "id1") - XCTAssertEqual(records[8].recordID.recordName, "id9") + #expect(operationsRun == 3) + #expect(records[0].recordID.recordName == "id1") + #expect(records[8].recordID.recordName == "id9") } - func test_task_cancellation_query() async { + @Test func test_task_cancellation_query() async { let query = CKQuery(recordType: "TestRecord", predicate: NSPredicate(value: true)) let db = ReplayingMockCKDatabase( operationResults: [ @@ -179,14 +179,14 @@ final class QueryRecordsFeatureTests: XCTestCase { do { let _ = try await task.result.get().get() } catch let recordError { - XCTAssertEqual(recordError, .init(from: CKError(CKError.Code.operationCancelled))) + #expect(recordError == .init(from: CKError(CKError.Code.operationCancelled))) } let operationsRun = await db.operationsRun - XCTAssertEqual(operationsRun, 1) + #expect(operationsRun == 1) } - func test_record_error() async { + @Test func test_record_error() async { let query = CKQuery(recordType: "TestRecord", predicate: NSPredicate(value: true)) let db = ReplayingMockCKDatabase( operationResults: [ @@ -208,11 +208,11 @@ final class QueryRecordsFeatureTests: XCTestCase { database: db ).get() } catch { - XCTAssertEqual(error, CKRecordError(from: CKError(CKError.Code.requestRateLimited))) + #expect(error == CKRecordError(from: CKError(CKError.Code.requestRateLimited))) } } - func test_nested_request_error() async { + @Test func test_nested_request_error() async { let query = CKQuery(recordType: "TestRecord", predicate: NSPredicate(value: true)) let db = ReplayingMockCKDatabase( operationResults: [ @@ -242,7 +242,7 @@ final class QueryRecordsFeatureTests: XCTestCase { database: db ).get() } catch { - XCTAssertEqual(error, CKRecordError(from: CKError(CKError.Code.networkFailure))) + #expect(error == CKRecordError(from: CKError(CKError.Code.networkFailure))) } } } diff --git a/Targets/Canopy/Tests/ReplayingMockContainerTests.swift b/Targets/Canopy/Tests/ReplayingMockContainerTests.swift index e61117a..24a053b 100644 --- a/Targets/Canopy/Tests/ReplayingMockContainerTests.swift +++ b/Targets/Canopy/Tests/ReplayingMockContainerTests.swift @@ -1,10 +1,10 @@ import Canopy import CanopyTestTools import CloudKit -import XCTest +import Testing -final class ReplayingMockContainerTests: XCTestCase { - func test_userRecordID_success() async { +@Suite struct ReplayingMockContainerTests { + @Test func test_userRecordID_success() async { let mockContainer = ReplayingMockContainer( operationResults: [ .userRecordID( @@ -16,10 +16,10 @@ final class ReplayingMockContainerTests: XCTestCase { ) let result = try! await mockContainer.userRecordID.get()! - XCTAssertEqual(result.recordName, "myRecordId") + #expect(result.recordName == "myRecordId") } - func test_userRecordID_error() async { + @Test func test_userRecordID_error() async { let mockContainer = ReplayingMockContainer( operationResults: [ .userRecordID( @@ -33,11 +33,11 @@ final class ReplayingMockContainerTests: XCTestCase { do { let _ = try await mockContainer.userRecordID.get() } catch { - XCTAssertEqual(error, CKRecordError(from: CKError(CKError.Code.networkFailure))) + #expect(error == CKRecordError(from: CKError(CKError.Code.networkFailure))) } } - func test_accountStatus_success() async { + @Test func test_accountStatus_success() async { let mockContainer = ReplayingMockContainer( operationResults: [ .accountStatus(.init(status: .couldNotDetermine, error: nil)) @@ -45,10 +45,10 @@ final class ReplayingMockContainerTests: XCTestCase { ) let result = try! await mockContainer.accountStatus.get() - XCTAssertEqual(result, .couldNotDetermine) + #expect(result == .couldNotDetermine) } - func test_accountStatus_error() async { + @Test func test_accountStatus_error() async { let mockContainer = ReplayingMockContainer( operationResults: [ .accountStatus( @@ -63,11 +63,11 @@ final class ReplayingMockContainerTests: XCTestCase { do { let _ = try await mockContainer.accountStatus.get() } catch { - XCTAssertEqual(error.code, CKError.Code.badContainer.rawValue) + #expect(error.code == CKError.Code.badContainer.rawValue) } } - func test_accountStatusStream_success() async { + @Test func test_accountStatusStream_success() async { let mockContainer = ReplayingMockContainer( operationResults: [ .accountStatusStream(.init(statuses: [.available, .noAccount, .couldNotDetermine], error: nil)) @@ -79,10 +79,10 @@ final class ReplayingMockContainerTests: XCTestCase { for await status in accountStatusStream.prefix(2) { statuses.append(status) } - XCTAssertEqual(statuses, [.available, .noAccount]) + #expect(statuses == [.available, .noAccount]) } - func test_accountStatusStream_error() async { + @Test func test_accountStatusStream_error() async { let mockContainer = ReplayingMockContainer( operationResults: [ .accountStatusStream(.init(statuses: [], error: .onlyOneAccountStatusStreamSupported)) @@ -91,21 +91,21 @@ final class ReplayingMockContainerTests: XCTestCase { do { let _ = try await mockContainer.accountStatusStream.get() } catch { - XCTAssertEqual(error, .onlyOneAccountStatusStreamSupported) + #expect(error == .onlyOneAccountStatusStreamSupported) } } - func test_acceptShares_success() async { + @Test func test_acceptShares_success() async { let mockContainer = ReplayingMockContainer( operationResults: [ .acceptShares(.init(result: .success([CKShare.mock, CKShare.mock_owned_by_current_user]))) ] ) let result = try! await mockContainer.acceptShares(with: []).get() - XCTAssertEqual(result.count, 2) + #expect(result.count == 2) } - func test_acceptShares_error() async { + @Test func test_acceptShares_error() async { let mockContainer = ReplayingMockContainer( operationResults: [ .acceptShares(.init(result: .failure(.init(from: CKError(CKError.Code.networkFailure))))) @@ -114,21 +114,21 @@ final class ReplayingMockContainerTests: XCTestCase { do { let _ = try await mockContainer.acceptShares(with: []).get() } catch { - XCTAssertEqual(error.code, CKError.Code.networkFailure.rawValue) + #expect(error.code == CKError.Code.networkFailure.rawValue) } } - func test_fetchShareParticipants_success() async { + @Test func test_fetchShareParticipants_success() async { let mockContainer = ReplayingMockContainer( operationResults: [ .fetchShareParticipants(.init(result: .success([CKShare.Participant.mock, CKShare.Participant.mock]))) ] ) let result = try! await mockContainer.fetchShareParticipants(with: []).get() - XCTAssertEqual(result.count, 2) + #expect(result.count == 2) } - func test_fetchShareParticipants_error() async { + @Test func test_fetchShareParticipants_error() async { let mockContainer = ReplayingMockContainer( operationResults: [ .fetchShareParticipants(.init(result: .failure(.init(from: CKError(CKError.Code.networkFailure))))) @@ -137,7 +137,7 @@ final class ReplayingMockContainerTests: XCTestCase { do { let _ = try await mockContainer.fetchShareParticipants(with: []).get() } catch { - XCTAssertEqual(error.code, CKError.Code.networkFailure.rawValue) + #expect(error.code == CKError.Code.networkFailure.rawValue) } } } diff --git a/Targets/Canopy/Tests/ReplayingMockDatabaseTests.swift b/Targets/Canopy/Tests/ReplayingMockDatabaseTests.swift index 0223b92..f650961 100644 --- a/Targets/Canopy/Tests/ReplayingMockDatabaseTests.swift +++ b/Targets/Canopy/Tests/ReplayingMockDatabaseTests.swift @@ -1,10 +1,10 @@ import Canopy import CanopyTestTools import CloudKit -import XCTest +import Testing -final class ReplayingMockDatabaseTests: XCTestCase { - func test_query_records_success() async { +@Suite struct ReplayingMockDatabaseTests { + @Test func test_query_records_success() async { let db = ReplayingMockDatabase(operationResults: [ .queryRecords( .init( @@ -15,21 +15,21 @@ final class ReplayingMockDatabaseTests: XCTestCase { ) ]) let result = try! await db.queryRecords(with: .init(recordType: "MockType", predicate: NSPredicate(value: true)), in: nil).get() - XCTAssertEqual(result.first!.recordID.recordName, "mockName") + #expect(result.first!.recordID.recordName == "mockName") } - func test_query_records_failure() async { + @Test func test_query_records_failure() async { let db = ReplayingMockDatabase(operationResults: [ .queryRecords(.init(result: .failure(.init(from: CKError(CKError.Code.badDatabase))))) ]) do { let _ = try await db.queryRecords(with: .init(recordType: "MockType", predicate: NSPredicate(value: true)), in: nil).get() } catch let recordError { - XCTAssertEqual(recordError, CKRecordError(from: CKError(CKError.Code.badDatabase))) + #expect(recordError == CKRecordError(from: CKError(CKError.Code.badDatabase))) } } - func test_modify_records_success() async { + @Test func test_modify_records_success() async { let db = ReplayingMockDatabase(operationResults: [ .modifyRecords( .init( @@ -51,23 +51,23 @@ final class ReplayingMockDatabaseTests: XCTestCase { saving: [], deleting: [] ).get() - XCTAssertEqual(result.savedRecords.first!.recordType, "MockType") - XCTAssertEqual(result.deletedRecordIDs[0].recordName, "deleted0") - XCTAssertEqual(result.deletedRecordIDs[1].recordName, "deleted1") + #expect(result.savedRecords.first!.recordType == "MockType") + #expect(result.deletedRecordIDs[0].recordName == "deleted0") + #expect(result.deletedRecordIDs[1].recordName == "deleted1") } - func test_modify_records_error() async { + @Test func test_modify_records_error() async { let db = ReplayingMockDatabase(operationResults: [ .modifyRecords(.init(result: .failure(.init(from: CKError(CKError.Code.networkFailure))))) ]) do { let _ = try await db.modifyRecords(saving: [], deleting: []).get() } catch let recordError { - XCTAssertEqual(recordError, CKRecordError(from: CKError(CKError.Code.networkFailure))) + #expect(recordError == CKRecordError(from: CKError(CKError.Code.networkFailure))) } } - func test_delete_records_success() async { + @Test func test_delete_records_success() async { let db = ReplayingMockDatabase(operationResults: [ .deleteRecords( .init( @@ -84,22 +84,22 @@ final class ReplayingMockDatabaseTests: XCTestCase { ) ]) let result = try! await db.deleteRecords(with: CKQuery(recordType: "SomeType", predicate: NSPredicate(value: true)), in: nil).get() - XCTAssertEqual(result.deletedRecordIDs[0].recordName, "deleted0") - XCTAssertEqual(result.deletedRecordIDs[1].recordName, "deleted1") + #expect(result.deletedRecordIDs[0].recordName == "deleted0") + #expect(result.deletedRecordIDs[1].recordName == "deleted1") } - func test_delete_records_error() async { + @Test func test_delete_records_error() async { let db = ReplayingMockDatabase(operationResults: [ .deleteRecords(.init(result: .failure(.init(from: CKError(CKError.Code.networkFailure))))) ]) do { let _ = try await db.deleteRecords(with: CKQuery(recordType: "SomeType", predicate: NSPredicate(value: true)), in: nil).get() } catch let recordError { - XCTAssertEqual(recordError, CKRecordError(from: CKError(CKError.Code.networkFailure))) + #expect(recordError == CKRecordError(from: CKError(CKError.Code.networkFailure))) } } - func test_fetch_records_success() async { + @Test func test_fetch_records_success() async { let db = ReplayingMockDatabase(operationResults: [ .fetchRecords( .init( @@ -120,25 +120,25 @@ final class ReplayingMockDatabaseTests: XCTestCase { ) ]) let result = try! await db.fetchRecords(with: []).get() - XCTAssertEqual(result.foundRecords[0].recordID.recordName, "record1") - XCTAssertEqual(result.foundRecords[1].recordID.recordName, "record2") - XCTAssertEqual(result.notFoundRecordIDs[0].recordName, "notFound1") - XCTAssertEqual(result.notFoundRecordIDs[1].recordName, "notFound2") - XCTAssertEqual(result.notFoundRecordIDs[2].recordName, "notFound3") + #expect(result.foundRecords[0].recordID.recordName == "record1") + #expect(result.foundRecords[1].recordID.recordName == "record2") + #expect(result.notFoundRecordIDs[0].recordName == "notFound1") + #expect(result.notFoundRecordIDs[1].recordName == "notFound2") + #expect(result.notFoundRecordIDs[2].recordName == "notFound3") } - func test_fetch_records_error() async { + @Test func test_fetch_records_error() async { let db = ReplayingMockDatabase(operationResults: [ .fetchRecords(.init(result: .failure(.init(from: CKError(CKError.Code.networkFailure))))) ]) do { let _ = try await db.fetchRecords(with: []).get() } catch let recordError { - XCTAssertEqual(recordError, CKRecordError(from: CKError(CKError.Code.networkFailure))) + #expect(recordError == CKRecordError(from: CKError(CKError.Code.networkFailure))) } } - func test_modify_zones_success() async { + @Test func test_modify_zones_success() async { let db = ReplayingMockDatabase(operationResults: [ .modifyZones( .init( @@ -156,22 +156,22 @@ final class ReplayingMockDatabaseTests: XCTestCase { ) ]) let result = try! await db.modifyZones(saving: [], deleting: []).get() - XCTAssertEqual(result.savedZones[0].zoneID.zoneName, "myZone") - XCTAssertEqual(result.deletedZoneIDs[0].zoneName, "myDeletedZone") + #expect(result.savedZones[0].zoneID.zoneName == "myZone") + #expect(result.deletedZoneIDs[0].zoneName == "myDeletedZone") } - func test_modify_zones_error() async { + @Test func test_modify_zones_error() async { let db = ReplayingMockDatabase(operationResults: [ .modifyZones(.init(result: .failure(.init(from: CKError(CKError.Code.badDatabase))))) ]) do { let _ = try await db.modifyZones(saving: [], deleting: []).get() } catch let recordError { - XCTAssertEqual(recordError, CKRecordZoneError(from: CKError(CKError.Code.badDatabase))) + #expect(recordError == CKRecordZoneError(from: CKError(CKError.Code.badDatabase))) } } - func test_fetch_zones_success() async { + @Test func test_fetch_zones_success() async { let db = ReplayingMockDatabase(operationResults: [ .fetchZones( .init( @@ -183,22 +183,22 @@ final class ReplayingMockDatabaseTests: XCTestCase { ) ]) let result = try! await db.fetchZones(with: []).get() - XCTAssertEqual(result[0].zoneID.zoneName, "zone1") - XCTAssertEqual(result[1].zoneID.zoneName, "zone2") + #expect(result[0].zoneID.zoneName == "zone1") + #expect(result[1].zoneID.zoneName == "zone2") } - func test_fetch_zones_error() async { + @Test func test_fetch_zones_error() async { let db = ReplayingMockDatabase(operationResults: [ .fetchZones(.init(result: .failure(.init(from: CKError(CKError.Code.badDatabase))))) ]) do { let _ = try await db.fetchZones(with: []).get() } catch let recordError { - XCTAssertEqual(recordError, CKRecordZoneError(from: CKError(CKError.Code.badDatabase))) + #expect(recordError == CKRecordZoneError(from: CKError(CKError.Code.badDatabase))) } } - func test_fetch_all_zones_success() async { + @Test func test_fetch_all_zones_success() async { let db = ReplayingMockDatabase(operationResults: [ .fetchAllZones( .init( @@ -210,22 +210,22 @@ final class ReplayingMockDatabaseTests: XCTestCase { ) ]) let result = try! await db.fetchAllZones().get() - XCTAssertEqual(result[0].zoneID.zoneName, "zone1") - XCTAssertEqual(result[1].zoneID.zoneName, "zone2") + #expect(result[0].zoneID.zoneName == "zone1") + #expect(result[1].zoneID.zoneName == "zone2") } - func test_fetch_all_zones_error() async { + @Test func test_fetch_all_zones_error() async { let db = ReplayingMockDatabase(operationResults: [ .fetchAllZones(.init(result: .failure(.init(from: CKError(CKError.Code.badDatabase))))) ]) do { let _ = try await db.fetchAllZones().get() } catch let recordError { - XCTAssertEqual(recordError, CKRecordZoneError(from: CKError(CKError.Code.badDatabase))) + #expect(recordError == CKRecordZoneError(from: CKError(CKError.Code.badDatabase))) } } - func test_modify_subscriptions_success() async { + @Test func test_modify_subscriptions_success() async { let db = ReplayingMockDatabase(operationResults: [ .modifySubscriptions( .init( @@ -246,22 +246,22 @@ final class ReplayingMockDatabaseTests: XCTestCase { ) ]) let result = try! await db.modifySubscriptions(saving: [], deleting: []).get() - XCTAssertEqual(result.savedSubscriptions[1].subscriptionID, "query") - XCTAssertEqual(result.deletedSubscriptionIDs[1], "deleted2") + #expect(result.savedSubscriptions[1].subscriptionID == "query") + #expect(result.deletedSubscriptionIDs[1] == "deleted2") } - func test_modify_subscriptions_error() async { + @Test func test_modify_subscriptions_error() async { let db = ReplayingMockDatabase(operationResults: [ .modifySubscriptions(.init(result: .failure(.init(from: CKError(CKError.Code.badDatabase))))) ]) do { let _ = try await db.modifySubscriptions(saving: [], deleting: []).get() } catch let recordError { - XCTAssertEqual(recordError, CKSubscriptionError(from: CKError(CKError.Code.badDatabase))) + #expect(recordError == CKSubscriptionError(from: CKError(CKError.Code.badDatabase))) } } - func test_fetch_database_changes_success() async { + @Test func test_fetch_database_changes_success() async { let db = ReplayingMockDatabase(operationResults: [ .fetchDatabaseChanges( .init( @@ -276,12 +276,12 @@ final class ReplayingMockDatabaseTests: XCTestCase { ) ]) let result = try! await db.fetchDatabaseChanges().get() - XCTAssertEqual(result.changedRecordZoneIDs[0].zoneName, "changedZone") - XCTAssertEqual(result.deletedRecordZoneIDs[0].zoneName, "deletedZone") - XCTAssertEqual(result.purgedRecordZoneIDs[0].zoneName, "purgedZone") + #expect(result.changedRecordZoneIDs[0].zoneName == "changedZone") + #expect(result.deletedRecordZoneIDs[0].zoneName == "deletedZone") + #expect(result.purgedRecordZoneIDs[0].zoneName == "purgedZone") } - func test_fetch_database_changes_error() async { + @Test func test_fetch_database_changes_error() async { let db = ReplayingMockDatabase(operationResults: [ .fetchDatabaseChanges( .init( @@ -292,13 +292,13 @@ final class ReplayingMockDatabaseTests: XCTestCase { do { let _ = try await db.fetchDatabaseChanges().get() } catch CanopyError.ckRequestError(let requestError) { - XCTAssertEqual(requestError, CKRequestError(from: CKError(CKError.Code.notAuthenticated))) + #expect(requestError == CKRequestError(from: CKError(CKError.Code.notAuthenticated))) } catch { - XCTFail("Unexpected error: \(error)") + Issue.record("Unexpected error: \(error)") } } - func test_fetch_zone_changes_success() async { + @Test func test_fetch_zone_changes_success() async { let db = ReplayingMockDatabase(operationResults: [ .fetchZoneChanges( .init( @@ -325,11 +325,11 @@ final class ReplayingMockDatabaseTests: XCTestCase { ) ]) let zoneChanges = try! await db.fetchZoneChanges(recordZoneIDs: []).get() - XCTAssertEqual(zoneChanges.changedRecords[0].recordID.recordName, "someRecordName") - XCTAssertEqual(zoneChanges.deletedRecords[0].recordID.recordName, "deletedRecordName") + #expect(zoneChanges.changedRecords[0].recordID.recordName == "someRecordName") + #expect(zoneChanges.deletedRecords[0].recordID.recordName == "deletedRecordName") } - func test_fetch_zone_changes_error() async { + @Test func test_fetch_zone_changes_error() async { let db = ReplayingMockDatabase(operationResults: [ .fetchZoneChanges( .init( @@ -340,13 +340,13 @@ final class ReplayingMockDatabaseTests: XCTestCase { do { let _ = try await db.fetchZoneChanges(recordZoneIDs: []).get() } catch CanopyError.ckRequestError(let requestError) { - XCTAssertEqual(requestError, CKRequestError(from: CKError(CKError.Code.notAuthenticated))) + #expect(requestError == CKRequestError(from: CKError(CKError.Code.notAuthenticated))) } catch { - XCTFail("Unexpected error: \(error)") + Issue.record("Unexpected error: \(error)") } } - func test_two_operations() async { + @Test func test_two_operations() async { let db = ReplayingMockDatabase(operationResults: [ .queryRecords( .init( @@ -374,13 +374,13 @@ final class ReplayingMockDatabaseTests: XCTestCase { ) ]) let result1 = try! await db.queryRecords(with: .init(recordType: "MockType", predicate: NSPredicate(value: true)), in: nil).get() - XCTAssertEqual(result1.first!.recordID.recordName, "mockName") + #expect(result1.first!.recordID.recordName == "mockName") let result2 = try! await db.fetchRecords(with: []).get() - XCTAssertEqual(result2.notFoundRecordIDs[0].recordName, "notFound1") + #expect(result2.notFoundRecordIDs[0].recordName == "notFound1") } - func test_sleep_if_needed() async { + @Test func test_sleep_if_needed() async { let db = ReplayingMockDatabase( operationResults: [ .queryRecords( @@ -394,6 +394,6 @@ final class ReplayingMockDatabaseTests: XCTestCase { sleepBeforeEachOperation: 0.01 ) let result = try! await db.queryRecords(with: .init(recordType: "MockType", predicate: NSPredicate(value: true)), in: nil).get() - XCTAssertEqual(result.first!.recordID.recordName, "mockName") + #expect(result.first!.recordID.recordName == "mockName") } } From 17cdab889f9cf0072d4da3bd39e1b3967c422d17 Mon Sep 17 00:00:00 2001 From: Jaanus Kase Date: Thu, 23 Oct 2025 13:07:33 +0300 Subject: [PATCH 13/20] Try github action again --- .github/workflows/swift.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .github/workflows/swift.yml diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml new file mode 100644 index 0000000..3b4f2e2 --- /dev/null +++ b/.github/workflows/swift.yml @@ -0,0 +1,23 @@ +# This workflow will build a Swift project +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-swift + +name: Swift + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + + runs-on: macos-latest + + steps: + - uses: actions/checkout@v4 + - uses: swift-actions/setup-swift@v2 + - name: Build + run: swift build -v + - name: Run tests + run: swift test -v From 011ee759159beeb072fd53085dbf115a21638369 Mon Sep 17 00:00:00 2001 From: Jaanus Kase Date: Thu, 23 Oct 2025 13:11:28 +0300 Subject: [PATCH 14/20] Request Swift 6.2 from GH Swift action --- .github/workflows/swift.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index 3b4f2e2..2076527 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -17,6 +17,8 @@ jobs: steps: - uses: actions/checkout@v4 - uses: swift-actions/setup-swift@v2 + with: + swift-version: "6.2" - name: Build run: swift build -v - name: Run tests From 4511e13cbb4122b6b517780189e8905198b7fd46 Mon Sep 17 00:00:00 2001 From: Jaanus Kase Date: Thu, 23 Oct 2025 13:13:50 +0300 Subject: [PATCH 15/20] Try CircleCI without specifying resource class --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 4661cdb..63783d5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -5,7 +5,7 @@ jobs: build: macos: xcode: "26.0.1" - resource_class: m4pro.medium + # resource_class: m4pro.medium steps: - checkout - run: swift build -v @@ -13,7 +13,7 @@ jobs: test: macos: xcode: "26.0.1" - resource_class: m4pro.medium + #resource_class: m4pro.medium steps: - checkout - run: swift test -v From af7ad1840bda3f65a91fd3800a31a72f65878c2c Mon Sep 17 00:00:00 2001 From: Jaanus Kase Date: Thu, 23 Oct 2025 13:15:39 +0300 Subject: [PATCH 16/20] Try m2pro CircleCI resource --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 63783d5..41b1ae6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -5,7 +5,7 @@ jobs: build: macos: xcode: "26.0.1" - # resource_class: m4pro.medium + resource_class: m2pro.medium steps: - checkout - run: swift build -v @@ -13,7 +13,7 @@ jobs: test: macos: xcode: "26.0.1" - #resource_class: m4pro.medium + resource_class: m2pro.medium steps: - checkout - run: swift test -v From eccee9c66951f8cc9b19f4c861a8f47979d04efa Mon Sep 17 00:00:00 2001 From: Jaanus Kase Date: Thu, 23 Oct 2025 13:19:20 +0300 Subject: [PATCH 17/20] CircleCI hacking --- .circleci/config.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 41b1ae6..25fa248 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -5,17 +5,19 @@ jobs: build: macos: xcode: "26.0.1" - resource_class: m2pro.medium + resource_class: m4pro.medium steps: - - checkout + - checkout: + method: blobless - run: swift build -v test: macos: xcode: "26.0.1" - resource_class: m2pro.medium + resource_class: m4pro.medium steps: - - checkout + - checkout: + method: blobless - run: swift test -v workflows: From fd0191dd0bf092e5d99e59bff670562b5f9967b6 Mon Sep 17 00:00:00 2001 From: Jaanus Kase Date: Thu, 23 Oct 2025 13:22:45 +0300 Subject: [PATCH 18/20] More CircleCI --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 25fa248..b2980de 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -5,7 +5,7 @@ jobs: build: macos: xcode: "26.0.1" - resource_class: m4pro.medium + resource_class: macos.m1.medium.gen1 steps: - checkout: method: blobless @@ -14,7 +14,7 @@ jobs: test: macos: xcode: "26.0.1" - resource_class: m4pro.medium + resource_class: macos.m1.medium.gen1 steps: - checkout: method: blobless From f1514283cebd5a12eef11a9fd2b07a955b0ae6eb Mon Sep 17 00:00:00 2001 From: Jaanus Kase Date: Thu, 23 Oct 2025 13:25:58 +0300 Subject: [PATCH 19/20] Commented out CI - Swift 6.2 not available yet --- .circleci/config.yml | 48 +++++++++++++++++++------------------ .github/workflows/swift.yml | 38 +++++++++++++++-------------- 2 files changed, 45 insertions(+), 41 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b2980de..f0d1469 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,27 +1,29 @@ # CircleCI configuration file +# Commented out, because currently Swift 6.2 isn’t available +# on CircleCI free plan resources -version: 2.1 -jobs: - build: - macos: - xcode: "26.0.1" - resource_class: macos.m1.medium.gen1 - steps: - - checkout: - method: blobless - - run: swift build -v +# version: 2.1 +# jobs: +# build: +# macos: +# xcode: "26.0.1" +# resource_class: macos.m1.medium.gen1 +# steps: +# - checkout: +# method: blobless +# - run: swift build -v - test: - macos: - xcode: "26.0.1" - resource_class: macos.m1.medium.gen1 - steps: - - checkout: - method: blobless - - run: swift test -v +# test: +# macos: +# xcode: "26.0.1" +# resource_class: macos.m1.medium.gen1 +# steps: +# - checkout: +# method: blobless +# - run: swift test -v -workflows: - build-and-test: - jobs: - - build - - test \ No newline at end of file +# workflows: +# build-and-test: +# jobs: +# - build +# - test \ No newline at end of file diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index 2076527..3921521 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -1,25 +1,27 @@ # This workflow will build a Swift project # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-swift +# Currently commented out, as Swift 6.2 isn’t available +# on GitHub Actions. -name: Swift +# name: Swift -on: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] +# on: +# push: +# branches: [ "main" ] +# pull_request: +# branches: [ "main" ] -jobs: - build: +# jobs: +# build: - runs-on: macos-latest +# runs-on: macos-latest - steps: - - uses: actions/checkout@v4 - - uses: swift-actions/setup-swift@v2 - with: - swift-version: "6.2" - - name: Build - run: swift build -v - - name: Run tests - run: swift test -v +# steps: +# - uses: actions/checkout@v4 +# - uses: swift-actions/setup-swift@v2 +# with: +# swift-version: "6.2" +# - name: Build +# run: swift build -v +# - name: Run tests +# run: swift test -v From 0c65202c089406cc5d2b6b0cb408f491495cff96 Mon Sep 17 00:00:00 2001 From: Jaanus Kase Date: Thu, 23 Oct 2025 13:29:32 +0300 Subject: [PATCH 20/20] Cleaned up Package.swift --- Package.swift | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/Package.swift b/Package.swift index 8369f00..affaff9 100644 --- a/Package.swift +++ b/Package.swift @@ -32,14 +32,11 @@ let package = Package( defaultLocalization: "en", platforms: [.iOS(.v18), .macOS(.v15)], products: [ - // Products define the executables and libraries a package produces, and make them visible to other packages. .library(name: "Canopy", targets: ["Canopy"]), .library(name: "CanopyTestTools", targets: ["CanopyTestTools"]) ], dependencies: dependencies, targets: [ - // Targets are the basic building blocks of a package. A target can define a module or a test suite. - // Targets can depend on other targets in this package, and on products in packages this package depends on. .target( name: "Canopy", dependencies: [ @@ -47,13 +44,6 @@ let package = Package( .product(name: "Dependencies", package: "swift-dependencies") ], path: "Targets/Canopy/Sources" - // https://danielsaidi.com/blog/2022/05/18/how-to-suppress-linking-warning - // Canopy by default gives a warning about unsafe code for application extensions. Not sure why it says that. - // See the above blog post for more info. - // The following line is OK to have in local development, but in live setting, cannot be used. - // This could also be obsolete, latest Canopy does not give warnings with extensions any more. - // Keeping this info here just for a while longer. - // linkerSettings: [.unsafeFlags(["-Xlinker", "-no_application_extension"])] ), .target( name: "CanopyTestTools",