From f710b4209e47591f83b4efc934ca695b933722f6 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Wed, 27 May 2026 09:12:49 -0500 Subject: [PATCH 1/2] Use new task local trait from ConcurrencyExtras. --- Package.resolved | 6 +- Package.swift | 3 +- .../CloudKitTests/AccountLifecycleTests.swift | 11 +- .../AttachedMetadatabaseTests.swift | 3 +- .../CloudKitTests/MergeConflictTests.swift | 5 +- .../CloudKitTests/MetadataTests.swift | 5 +- .../CloudKitTests/NewTableSyncTests.swift | 3 +- .../NextRecordZoneChangeBatchTests.swift | 6 +- .../SyncEngineDelegateTests.swift | 5 +- .../SyncEngineLifecycleTests.swift | 6 +- .../Internal/BaseCloudKitTests.swift | 17 +- .../Internal/CloudKit+CustomDump.swift | 10 +- .../SQLiteDataTests/Internal/TestScopes.swift | 180 ++---------------- 13 files changed, 66 insertions(+), 194 deletions(-) diff --git a/Package.resolved b/Package.resolved index a192e0c6..5dc88bf9 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "2b95f254bb904411c5a0aba0e0402c157014542e9bd40eb5848c42c8598b80a9", + "originHash" : "9bbe54e061cabd9b24fe67ff19058dfd72c866f0097ef4c5d9c8111c7a7aa422", "pins" : [ { "identity" : "combine-schedulers", @@ -42,8 +42,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-concurrency-extras", "state" : { - "revision" : "5a3825302b1a0d744183200915a47b508c828e6f", - "version" : "1.3.2" + "branch" : "task-local-test-trait", + "revision" : "99ff72ffe57cc9afcbb4c0c39c29c0d41fdb1f82" } }, { diff --git a/Package.swift b/Package.swift index e1f278e8..208c4a50 100644 --- a/Package.swift +++ b/Package.swift @@ -29,7 +29,7 @@ let package = Package( dependencies: [ .package(url: "https://github.com/apple/swift-collections", from: "1.0.0"), .package(url: "https://github.com/groue/GRDB.swift", from: "7.6.0"), - .package(url: "https://github.com/pointfreeco/swift-concurrency-extras", from: "1.0.0"), + .package(url: "https://github.com/pointfreeco/swift-concurrency-extras", branch: "task-local-test-trait"), .package(url: "https://github.com/pointfreeco/swift-custom-dump", from: "1.3.3"), .package(url: "https://github.com/pointfreeco/swift-dependencies", from: "1.9.0"), .package(url: "https://github.com/pointfreeco/swift-perception", from: "2.0.0"), @@ -69,6 +69,7 @@ let package = Package( dependencies: [ "SQLiteData", .product(name: "ConcurrencyExtras", package: "swift-concurrency-extras"), + .product(name: "ConcurrencyExtrasTestSupport", package: "swift-concurrency-extras"), .product(name: "CustomDump", package: "swift-custom-dump"), .product(name: "Dependencies", package: "swift-dependencies"), .product(name: "InlineSnapshotTesting", package: "swift-snapshot-testing"), diff --git a/Tests/SQLiteDataTests/CloudKitTests/AccountLifecycleTests.swift b/Tests/SQLiteDataTests/CloudKitTests/AccountLifecycleTests.swift index c90130db..279f444c 100644 --- a/Tests/SQLiteDataTests/CloudKitTests/AccountLifecycleTests.swift +++ b/Tests/SQLiteDataTests/CloudKitTests/AccountLifecycleTests.swift @@ -1,4 +1,5 @@ #if canImport(CloudKit) + import ConcurrencyExtrasTestSupport import CloudKit import CustomDump import Foundation @@ -38,7 +39,8 @@ } @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) - @Test(.accountStatus(.noAccount)) func signInUploadsLocalRecordsToCloudKit() async throws { + @Test(.taskLocal(_$accountStatus, .noAccount)) + func signInUploadsLocalRecordsToCloudKit() async throws { try await userDatabase.userWrite { db in try db.seed { RemindersList(id: 1, title: "Personal") @@ -367,7 +369,8 @@ _ = try syncEngine.modifyRecords(scope: .shared, saving: [share, remindersListRecord]) let freshShare = try syncEngine.shared.database.record(for: share.recordID) as! CKShare let freshRemindersListRecord = try syncEngine.shared.database.record( - for: remindersListRecord.recordID) + for: remindersListRecord.recordID + ) try await syncEngine .acceptShare( @@ -604,8 +607,8 @@ @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test( - .accountStatus(.noAccount), - .prepareDatabase { userDatabase in + .taskLocal(_$accountStatus, .noAccount), + .taskLocal(_$prepareDatabase) { userDatabase in try await userDatabase.write { db in try db.seed { RemindersList(id: 1, title: "Personal") diff --git a/Tests/SQLiteDataTests/CloudKitTests/AttachedMetadatabaseTests.swift b/Tests/SQLiteDataTests/CloudKitTests/AttachedMetadatabaseTests.swift index 1d13ead2..880cde49 100644 --- a/Tests/SQLiteDataTests/CloudKitTests/AttachedMetadatabaseTests.swift +++ b/Tests/SQLiteDataTests/CloudKitTests/AttachedMetadatabaseTests.swift @@ -1,6 +1,7 @@ #if canImport(CloudKit) import CloudKit import ConcurrencyExtras + import ConcurrencyExtrasTestSupport import CustomDump import InlineSnapshotTesting import OrderedCollections @@ -11,7 +12,7 @@ extension BaseCloudKitTests { @MainActor - @Suite(.attachMetadatabase(true)) + @Suite(.taskLocal(_$attachMetadatabase, false)) final class AttachedMetadatabaseTests: BaseCloudKitTests, @unchecked Sendable { @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func basics() async throws { diff --git a/Tests/SQLiteDataTests/CloudKitTests/MergeConflictTests.swift b/Tests/SQLiteDataTests/CloudKitTests/MergeConflictTests.swift index 34530951..b9840148 100644 --- a/Tests/SQLiteDataTests/CloudKitTests/MergeConflictTests.swift +++ b/Tests/SQLiteDataTests/CloudKitTests/MergeConflictTests.swift @@ -1,5 +1,6 @@ #if canImport(CloudKit) import CloudKit + import ConcurrencyExtrasTestSupport import CustomDump import Foundation import InlineSnapshotTesting @@ -11,8 +12,8 @@ extension BaseCloudKitTests { @MainActor - @Suite(.printTimestamps) final class MergeConflictTests: BaseCloudKitTests, @unchecked Sendable - { + @Suite(.taskLocal(CKRecord._$printTimestamps, true)) + final class MergeConflictTests: BaseCloudKitTests, @unchecked Sendable { @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func merge_clientRecordUpdatedBeforeServerRecord() async throws { try await userDatabase.userWrite { db in diff --git a/Tests/SQLiteDataTests/CloudKitTests/MetadataTests.swift b/Tests/SQLiteDataTests/CloudKitTests/MetadataTests.swift index 0f58f0d6..3a3c5307 100644 --- a/Tests/SQLiteDataTests/CloudKitTests/MetadataTests.swift +++ b/Tests/SQLiteDataTests/CloudKitTests/MetadataTests.swift @@ -1,5 +1,6 @@ #if canImport(CloudKit) import CloudKit + import ConcurrencyExtrasTestSupport import CustomDump import SQLiteDataTestSupport import Foundation @@ -623,7 +624,7 @@ } @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) - @Test(.attachMetadatabase(true)) + @Test(.taskLocal(_$attachMetadatabase, true)) func observation() async throws { let remindersList = RemindersList(id: 1, title: "Personal") try await userDatabase.userWrite { db in @@ -652,7 +653,7 @@ } @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) - @Test(.attachMetadatabase(true)) + @Test(.taskLocal(_$attachMetadatabase, true)) func observation_GeneratedColumn() async throws { let remindersList = RemindersList(id: 1, title: "Personal") try await userDatabase.userWrite { db in diff --git a/Tests/SQLiteDataTests/CloudKitTests/NewTableSyncTests.swift b/Tests/SQLiteDataTests/CloudKitTests/NewTableSyncTests.swift index 6c0868fb..bf471eec 100644 --- a/Tests/SQLiteDataTests/CloudKitTests/NewTableSyncTests.swift +++ b/Tests/SQLiteDataTests/CloudKitTests/NewTableSyncTests.swift @@ -1,5 +1,6 @@ #if canImport(CloudKit) import CloudKit +import ConcurrencyExtrasTestSupport import CustomDump import SQLiteDataTestSupport import Foundation @@ -11,7 +12,7 @@ extension BaseCloudKitTests { @MainActor @Suite( - .prepareDatabase { userDatabase in + .taskLocal(_$prepareDatabase) { userDatabase in try await userDatabase.userWrite { db in try db.seed { RemindersList(id: 1, title: "Personal") diff --git a/Tests/SQLiteDataTests/CloudKitTests/NextRecordZoneChangeBatchTests.swift b/Tests/SQLiteDataTests/CloudKitTests/NextRecordZoneChangeBatchTests.swift index 098eafb1..8a3c2066 100644 --- a/Tests/SQLiteDataTests/CloudKitTests/NextRecordZoneChangeBatchTests.swift +++ b/Tests/SQLiteDataTests/CloudKitTests/NextRecordZoneChangeBatchTests.swift @@ -1,5 +1,6 @@ #if canImport(CloudKit) import CloudKit + import ConcurrencyExtrasTestSupport import CustomDump import Foundation import InlineSnapshotTesting @@ -264,7 +265,10 @@ } @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) - @Test(.printTimestamps(true), .printRecordChangeTag) + @Test( + .taskLocal(CKRecord._$printRecordChangeTag, true), + .taskLocal(CKRecord._$printTimestamps, true) + ) func editBetweenBatchAndSentRecordZoneChanges() async throws { try await userDatabase.userWrite { db in try db.seed { diff --git a/Tests/SQLiteDataTests/CloudKitTests/SyncEngineDelegateTests.swift b/Tests/SQLiteDataTests/CloudKitTests/SyncEngineDelegateTests.swift index 40c55eaf..12819ec0 100644 --- a/Tests/SQLiteDataTests/CloudKitTests/SyncEngineDelegateTests.swift +++ b/Tests/SQLiteDataTests/CloudKitTests/SyncEngineDelegateTests.swift @@ -1,5 +1,6 @@ #if canImport(CloudKit) import CloudKit + import ConcurrencyExtrasTestSupport import CustomDump import DependenciesTestSupport import Foundation @@ -14,7 +15,7 @@ final class SyncEngineDelegateTests: BaseCloudKitTests, @unchecked Sendable { @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) - @Test(.syncEngineDelegate(MyDelegate())) + @Test(.taskLocal(_$syncEngineDelegate, MyDelegate())) func accountChanged() async throws { try await userDatabase.userWrite { db in try db.seed { @@ -173,7 +174,7 @@ try await syncEngine.processPendingDatabaseChanges(scope: .private) } - @Test(.syncEngineDelegate(DefaultImplementationDelegate())) + @Test(.taskLocal(_$syncEngineDelegate, DefaultImplementationDelegate())) func accountChanged_DefaultImplementation() async throws { try await userDatabase.userWrite { db in try db.seed { diff --git a/Tests/SQLiteDataTests/CloudKitTests/SyncEngineLifecycleTests.swift b/Tests/SQLiteDataTests/CloudKitTests/SyncEngineLifecycleTests.swift index 7b720c2e..cc4ab5df 100644 --- a/Tests/SQLiteDataTests/CloudKitTests/SyncEngineLifecycleTests.swift +++ b/Tests/SQLiteDataTests/CloudKitTests/SyncEngineLifecycleTests.swift @@ -1,5 +1,6 @@ #if canImport(CloudKit) import CloudKit + import ConcurrencyExtrasTestSupport import DependenciesTestSupport import InlineSnapshotTesting import SQLiteDataTestSupport @@ -16,8 +17,7 @@ @MainActor @Suite final class SyncEngineLifecycleTests_ImmediatelyStarted: BaseCloudKitTests, - @unchecked - Sendable + @unchecked Sendable { @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func stopAndReStart() async throws { @@ -581,7 +581,7 @@ // * Start sync engine // * Verify that data is sent to CloudKit database and cached locally. @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) - @Test(.startImmediately(false)) func writeAndThenStart() async throws { + @Test(.taskLocal(_$startImmediately, false)) func writeAndThenStart() async throws { try await userDatabase.userWrite { db in try db.seed { RemindersList(id: 1, title: "Personal") diff --git a/Tests/SQLiteDataTests/Internal/BaseCloudKitTests.swift b/Tests/SQLiteDataTests/Internal/BaseCloudKitTests.swift index 8dd00241..b28963f6 100644 --- a/Tests/SQLiteDataTests/Internal/BaseCloudKitTests.swift +++ b/Tests/SQLiteDataTests/Internal/BaseCloudKitTests.swift @@ -1,6 +1,7 @@ #if canImport(CloudKit) import Clocks import CloudKit + import ConcurrencyExtrasTestSupport import DependenciesTestSupport import OrderedCollections import SQLiteData @@ -15,7 +16,7 @@ $0.continuousClock = TestClock() $0.dataManager = InMemoryDataManager() }, - .attachMetadatabase(false) + .taskLocal(_$attachMetadatabase, false) ) class BaseCloudKitTests: @unchecked Sendable { let userDatabase: UserDatabase @@ -49,14 +50,14 @@ self.userDatabase = UserDatabase( database: try SQLiteDataTests.database( containerIdentifier: testContainerIdentifier, - attachMetadatabase: _AttachMetadatabaseTrait.attachMetadatabase + attachMetadatabase: attachMetadatabase ) ) - try await _PrepareDatabaseTrait.prepareDatabase(userDatabase) + try await prepareDatabase(userDatabase) let privateDatabase = MockCloudDatabase(databaseScope: .private) let sharedDatabase = MockCloudDatabase(databaseScope: .shared) let container = MockCloudContainer( - accountStatus: _AccountStatusScope.accountStatus, + accountStatus: accountStatus, containerIdentifier: testContainerIdentifier, privateCloudDatabase: privateDatabase, sharedCloudDatabase: sharedDatabase @@ -67,7 +68,7 @@ _syncEngine = try await SyncEngine( container: container, userDatabase: self.userDatabase, - delegate: _SyncEngineDelegateTrait.syncEngineDelegate, + delegate: syncEngineDelegate, tables: Reminder.self, RemindersList.self, RemindersListAsset.self, @@ -81,11 +82,9 @@ ModelC.self, ScopedModel.self, privateTables: RemindersListPrivate.self, - startImmediately: _StartImmediatelyTrait.startImmediately + startImmediately: startImmediately ) - if _StartImmediatelyTrait.startImmediately, - _AccountStatusScope.accountStatus == .available - { + if startImmediately, accountStatus == .available { await syncEngine.handleEvent( .accountChange(changeType: .signIn(currentUser: currentUserRecordID)), syncEngine: syncEngine.private diff --git a/Tests/SQLiteDataTests/Internal/CloudKit+CustomDump.swift b/Tests/SQLiteDataTests/Internal/CloudKit+CustomDump.swift index 338db9df..16d1903b 100644 --- a/Tests/SQLiteDataTests/Internal/CloudKit+CustomDump.swift +++ b/Tests/SQLiteDataTests/Internal/CloudKit+CustomDump.swift @@ -19,8 +19,14 @@ } extension CKRecord { - @TaskLocal static var printTimestamps = false - @TaskLocal static var printRecordChangeTag = false + static let _$printTimestamps = TaskLocal(wrappedValue: false) + static let _$printRecordChangeTag = TaskLocal(wrappedValue: false) + static var printTimestamps: Bool { + _$printTimestamps.get() + } + static var printRecordChangeTag: Bool { + _$printRecordChangeTag.get() + } } @available(macOS 14, iOS 17, tvOS 17, watchOS 10, *) diff --git a/Tests/SQLiteDataTests/Internal/TestScopes.swift b/Tests/SQLiteDataTests/Internal/TestScopes.swift index 64b1f92f..5b0e699c 100644 --- a/Tests/SQLiteDataTests/Internal/TestScopes.swift +++ b/Tests/SQLiteDataTests/Internal/TestScopes.swift @@ -3,176 +3,30 @@ import Testing import SQLiteData - struct _PrepareDatabaseTrait: SuiteTrait, TestScoping, TestTrait { - @TaskLocal static var prepareDatabase: @Sendable (UserDatabase) async throws -> Void = - { _ in } - let prepareDatabase: @Sendable (UserDatabase) async throws -> Void - init(prepareDatabase: @escaping @Sendable (UserDatabase) async throws -> Void = { _ in }) { - self.prepareDatabase = prepareDatabase - } - func provideScope( - for test: Test, - testCase: Test.Case?, - performing function: () async throws -> Void - ) async throws { - try await Self.$prepareDatabase.withValue(prepareDatabase) { - try await function() - } - } + var prepareDatabase: @Sendable (UserDatabase) async throws -> Void { + _$prepareDatabase.get() } + let _$prepareDatabase = TaskLocal<@Sendable (UserDatabase) async throws -> Void>( + wrappedValue: { _ in } + ) - extension Trait where Self == _PrepareDatabaseTrait { - static func prepareDatabase( - _ prepareDatabase: @escaping @Sendable (UserDatabase) async throws -> Void - ) -> Self { - Self(prepareDatabase: prepareDatabase) - } + var startImmediately: Bool { + _$startImmediately.get() } + let _$startImmediately = TaskLocal(wrappedValue: true) - struct _StartImmediatelyTrait: SuiteTrait, TestScoping, TestTrait { - @TaskLocal static var startImmediately = true - let startImmediately: Bool - init(startImmediately: Bool = true) { - self.startImmediately = startImmediately - } - func provideScope( - for test: Test, - testCase: Test.Case?, - performing function: () async throws -> Void - ) async throws { - try await Self.$startImmediately.withValue(startImmediately) { - try await function() - } - } + var attachMetadatabase: Bool { + _$attachMetadatabase.get() } + let _$attachMetadatabase = TaskLocal(wrappedValue: false) - extension Trait where Self == _StartImmediatelyTrait { - static func startImmediately(_ startImmediately: Bool) -> Self { - Self(startImmediately: startImmediately) - } + var accountStatus: CKAccountStatus { + _$accountStatus.get() } + let _$accountStatus = TaskLocal(wrappedValue: CKAccountStatus.available) - struct _AttachMetadatabaseTrait: SuiteTrait, TestScoping, TestTrait { - @TaskLocal static var attachMetadatabase = false - let attachMetadatabase: Bool - init(attachMetadatabase: Bool = false) { - self.attachMetadatabase = attachMetadatabase - } - func provideScope( - for test: Test, - testCase: Test.Case?, - performing function: () async throws -> Void - ) async throws { - try await Self.$attachMetadatabase.withValue(attachMetadatabase) { - try await function() - } - } + var syncEngineDelegate: (any SyncEngineDelegate)? { + _$syncEngineDelegate.get() } - - extension Trait where Self == _AttachMetadatabaseTrait { - static var attachMetadatabase: Self { Self(attachMetadatabase: true) } - static func attachMetadatabase(_ attachMetadatabase: Bool) -> Self { - Self(attachMetadatabase: attachMetadatabase) - } - } - - struct _AccountStatusScope: SuiteTrait, TestScoping, TestTrait { - @TaskLocal static var accountStatus = CKAccountStatus.available - - let accountStatus: CKAccountStatus - init(_ accountStatus: CKAccountStatus = .available) { - self.accountStatus = accountStatus - } - - func provideScope( - for test: Test, - testCase: Test.Case?, - performing function: @Sendable () async throws -> Void - ) async throws { - try await Self.$accountStatus.withValue(accountStatus) { - try await function() - } - } - } - - extension Trait where Self == _AccountStatusScope { - static var accountStatus: Self { Self() } - static func accountStatus(_ accountStatus: CKAccountStatus) -> Self { - Self(accountStatus) - } - } - - struct _SyncEngineDelegateTrait: SuiteTrait, TestScoping, TestTrait { - @TaskLocal static var syncEngineDelegate: (any SyncEngineDelegate)? - let syncEngineDelegate: (any SyncEngineDelegate)? - init(syncEngineDelegate: (any SyncEngineDelegate)?) { - self.syncEngineDelegate = syncEngineDelegate - } - func provideScope( - for test: Test, - testCase: Test.Case?, - performing function: () async throws -> Void - ) async throws { - try await Self.$syncEngineDelegate.withValue(syncEngineDelegate) { - try await function() - } - } - } - - extension Trait where Self == _SyncEngineDelegateTrait { - static func syncEngineDelegate( - _ syncEngineDelegate: (any SyncEngineDelegate)? - ) -> Self { - Self(syncEngineDelegate: syncEngineDelegate) - } - } - - struct _PrintRecordChangeTag: SuiteTrait, TestScoping, TestTrait { - let printRecordChangeTag: Bool - init(_ printRecordChangeTag: Bool = true) { - self.printRecordChangeTag = printRecordChangeTag - } - - func provideScope( - for test: Test, - testCase: Test.Case?, - performing function: @Sendable () async throws -> Void - ) async throws { - try await CKRecord.$printRecordChangeTag.withValue(true) { - try await function() - } - } - } - - extension Trait where Self == _PrintRecordChangeTag { - static var printRecordChangeTag: Self { Self() } - static func printRecordChangeTag(_ printRecordChangeTag: Bool) -> Self { - Self(printRecordChangeTag) - } - } - - struct _PrintTimestampsScope: SuiteTrait, TestScoping, TestTrait { - let printTimestamps: Bool - init(_ printTimestamps: Bool = true) { - self.printTimestamps = printTimestamps - } - - func provideScope( - for test: Test, - testCase: Test.Case?, - performing function: @Sendable () async throws -> Void - ) async throws { - try await CKRecord.$printTimestamps.withValue(true) { - try await function() - } - } - } - - extension Trait where Self == _PrintTimestampsScope { - static var printTimestamps: Self { Self() } - static func printTimestamps(_ printTimestamps: Bool) -> Self { - Self(printTimestamps) - } - } - + let _$syncEngineDelegate = TaskLocal<(any SyncEngineDelegate)?>(wrappedValue: nil) #endif From cd513a0be5186a1ed3bfc0437f74c292e33b01dc Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Wed, 27 May 2026 12:12:57 -0500 Subject: [PATCH 2/2] wip --- Package.resolved | 6 ++-- Package.swift | 7 +++- Sources/TestLocals/TestLocals.swift | 12 +++++++ .../CloudKitTests/AccountLifecycleTests.swift | 7 ++-- .../AttachedMetadatabaseTests.swift | 3 +- .../CloudKitTests/MetadataTests.swift | 5 +-- .../CloudKitTests/NewTableSyncTests.swift | 5 +-- .../SyncEngineDelegateTests.swift | 5 +-- .../SyncEngineLifecycleTests.swift | 4 ++- .../Internal/BaseCloudKitTests.swift | 3 +- .../SQLiteDataTests/Internal/TestScopes.swift | 32 ------------------- 11 files changed, 41 insertions(+), 48 deletions(-) create mode 100644 Sources/TestLocals/TestLocals.swift delete mode 100644 Tests/SQLiteDataTests/Internal/TestScopes.swift diff --git a/Package.resolved b/Package.resolved index 5dc88bf9..05ebe497 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "9bbe54e061cabd9b24fe67ff19058dfd72c866f0097ef4c5d9c8111c7a7aa422", + "originHash" : "331e95a9e67db363c63a9a11d77e2c329584c69879e043f099ad4b54aa1ff405", "pins" : [ { "identity" : "combine-schedulers", @@ -42,8 +42,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-concurrency-extras", "state" : { - "branch" : "task-local-test-trait", - "revision" : "99ff72ffe57cc9afcbb4c0c39c29c0d41fdb1f82" + "revision" : "a90e2e40a7a840a853dd29e57cbef5dbb72c9d5b", + "version" : "1.4.0" } }, { diff --git a/Package.swift b/Package.swift index 208c4a50..4ab214a5 100644 --- a/Package.swift +++ b/Package.swift @@ -29,7 +29,7 @@ let package = Package( dependencies: [ .package(url: "https://github.com/apple/swift-collections", from: "1.0.0"), .package(url: "https://github.com/groue/GRDB.swift", from: "7.6.0"), - .package(url: "https://github.com/pointfreeco/swift-concurrency-extras", branch: "task-local-test-trait"), + .package(url: "https://github.com/pointfreeco/swift-concurrency-extras", from: "1.4.0"), .package(url: "https://github.com/pointfreeco/swift-custom-dump", from: "1.3.3"), .package(url: "https://github.com/pointfreeco/swift-dependencies", from: "1.9.0"), .package(url: "https://github.com/pointfreeco/swift-perception", from: "2.0.0"), @@ -81,12 +81,17 @@ let package = Package( dependencies: [ "SQLiteData", "SQLiteDataTestSupport", + "TestLocals", .product(name: "DependenciesTestSupport", package: "swift-dependencies"), .product(name: "InlineSnapshotTesting", package: "swift-snapshot-testing"), .product(name: "SnapshotTestingCustomDump", package: "swift-snapshot-testing"), .product(name: "StructuredQueries", package: "swift-structured-queries"), ] ), + .target( + name: "TestLocals", + dependencies: ["SQLiteData"] + ) ], swiftLanguageModes: [.v6] ) diff --git a/Sources/TestLocals/TestLocals.swift b/Sources/TestLocals/TestLocals.swift new file mode 100644 index 00000000..88c39bd0 --- /dev/null +++ b/Sources/TestLocals/TestLocals.swift @@ -0,0 +1,12 @@ +#if canImport(CloudKit) + import CloudKit + import Testing + import SQLiteData + + @TaskLocal package var prepareDatabase: @Sendable (UserDatabase) async throws -> Void = { _ in } + @TaskLocal package var startImmediately = true + @TaskLocal package var attachMetadatabase = false + @TaskLocal package var accountStatus = CKAccountStatus.available + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) + @TaskLocal package var syncEngineDelegate: (any SyncEngineDelegate)? = nil +#endif diff --git a/Tests/SQLiteDataTests/CloudKitTests/AccountLifecycleTests.swift b/Tests/SQLiteDataTests/CloudKitTests/AccountLifecycleTests.swift index 279f444c..dd8f0ef9 100644 --- a/Tests/SQLiteDataTests/CloudKitTests/AccountLifecycleTests.swift +++ b/Tests/SQLiteDataTests/CloudKitTests/AccountLifecycleTests.swift @@ -7,6 +7,7 @@ import SQLiteData import SnapshotTestingCustomDump import Testing + import TestLocals import SQLiteDataTestSupport extension BaseCloudKitTests { @@ -39,7 +40,7 @@ } @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) - @Test(.taskLocal(_$accountStatus, .noAccount)) + @Test($accountStatus.set(.noAccount)) func signInUploadsLocalRecordsToCloudKit() async throws { try await userDatabase.userWrite { db in try db.seed { @@ -607,8 +608,8 @@ @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test( - .taskLocal(_$accountStatus, .noAccount), - .taskLocal(_$prepareDatabase) { userDatabase in + $accountStatus.set(.noAccount), + $prepareDatabase.set { userDatabase in try await userDatabase.write { db in try db.seed { RemindersList(id: 1, title: "Personal") diff --git a/Tests/SQLiteDataTests/CloudKitTests/AttachedMetadatabaseTests.swift b/Tests/SQLiteDataTests/CloudKitTests/AttachedMetadatabaseTests.swift index 880cde49..dbec8bb2 100644 --- a/Tests/SQLiteDataTests/CloudKitTests/AttachedMetadatabaseTests.swift +++ b/Tests/SQLiteDataTests/CloudKitTests/AttachedMetadatabaseTests.swift @@ -9,10 +9,11 @@ import SQLiteDataTestSupport import SnapshotTestingCustomDump import Testing + import TestLocals extension BaseCloudKitTests { @MainActor - @Suite(.taskLocal(_$attachMetadatabase, false)) + @Suite($attachMetadatabase.set(false)) final class AttachedMetadatabaseTests: BaseCloudKitTests, @unchecked Sendable { @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) @Test func basics() async throws { diff --git a/Tests/SQLiteDataTests/CloudKitTests/MetadataTests.swift b/Tests/SQLiteDataTests/CloudKitTests/MetadataTests.swift index 3a3c5307..bd81326d 100644 --- a/Tests/SQLiteDataTests/CloudKitTests/MetadataTests.swift +++ b/Tests/SQLiteDataTests/CloudKitTests/MetadataTests.swift @@ -9,6 +9,7 @@ import SQLiteData import SnapshotTestingCustomDump import Testing + import TestLocals extension BaseCloudKitTests { @MainActor @@ -624,7 +625,7 @@ } @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) - @Test(.taskLocal(_$attachMetadatabase, true)) + @Test($attachMetadatabase.set(true)) func observation() async throws { let remindersList = RemindersList(id: 1, title: "Personal") try await userDatabase.userWrite { db in @@ -653,7 +654,7 @@ } @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) - @Test(.taskLocal(_$attachMetadatabase, true)) + @Test($attachMetadatabase.set(true)) func observation_GeneratedColumn() async throws { let remindersList = RemindersList(id: 1, title: "Personal") try await userDatabase.userWrite { db in diff --git a/Tests/SQLiteDataTests/CloudKitTests/NewTableSyncTests.swift b/Tests/SQLiteDataTests/CloudKitTests/NewTableSyncTests.swift index bf471eec..568184a5 100644 --- a/Tests/SQLiteDataTests/CloudKitTests/NewTableSyncTests.swift +++ b/Tests/SQLiteDataTests/CloudKitTests/NewTableSyncTests.swift @@ -1,6 +1,6 @@ #if canImport(CloudKit) import CloudKit -import ConcurrencyExtrasTestSupport + import ConcurrencyExtrasTestSupport import CustomDump import SQLiteDataTestSupport import Foundation @@ -8,11 +8,12 @@ import ConcurrencyExtrasTestSupport import SQLiteData import SnapshotTestingCustomDump import Testing + import TestLocals extension BaseCloudKitTests { @MainActor @Suite( - .taskLocal(_$prepareDatabase) { userDatabase in + $prepareDatabase.set { userDatabase in try await userDatabase.userWrite { db in try db.seed { RemindersList(id: 1, title: "Personal") diff --git a/Tests/SQLiteDataTests/CloudKitTests/SyncEngineDelegateTests.swift b/Tests/SQLiteDataTests/CloudKitTests/SyncEngineDelegateTests.swift index 12819ec0..395be01e 100644 --- a/Tests/SQLiteDataTests/CloudKitTests/SyncEngineDelegateTests.swift +++ b/Tests/SQLiteDataTests/CloudKitTests/SyncEngineDelegateTests.swift @@ -9,13 +9,14 @@ import SQLiteDataTestSupport import SnapshotTestingCustomDump import Testing + import TestLocals extension BaseCloudKitTests { @MainActor final class SyncEngineDelegateTests: BaseCloudKitTests, @unchecked Sendable { @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) - @Test(.taskLocal(_$syncEngineDelegate, MyDelegate())) + @Test($syncEngineDelegate.set(MyDelegate())) func accountChanged() async throws { try await userDatabase.userWrite { db in try db.seed { @@ -174,7 +175,7 @@ try await syncEngine.processPendingDatabaseChanges(scope: .private) } - @Test(.taskLocal(_$syncEngineDelegate, DefaultImplementationDelegate())) + @Test($syncEngineDelegate.set(DefaultImplementationDelegate())) func accountChanged_DefaultImplementation() async throws { try await userDatabase.userWrite { db in try db.seed { diff --git a/Tests/SQLiteDataTests/CloudKitTests/SyncEngineLifecycleTests.swift b/Tests/SQLiteDataTests/CloudKitTests/SyncEngineLifecycleTests.swift index cc4ab5df..229d6b02 100644 --- a/Tests/SQLiteDataTests/CloudKitTests/SyncEngineLifecycleTests.swift +++ b/Tests/SQLiteDataTests/CloudKitTests/SyncEngineLifecycleTests.swift @@ -9,6 +9,7 @@ import SnapshotTesting import SnapshotTestingCustomDump import Testing + import TestLocals import os extension BaseCloudKitTests { @@ -581,7 +582,8 @@ // * Start sync engine // * Verify that data is sent to CloudKit database and cached locally. @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) - @Test(.taskLocal(_$startImmediately, false)) func writeAndThenStart() async throws { + @Test($startImmediately.set(false)) + func writeAndThenStart() async throws { try await userDatabase.userWrite { db in try db.seed { RemindersList(id: 1, title: "Personal") diff --git a/Tests/SQLiteDataTests/Internal/BaseCloudKitTests.swift b/Tests/SQLiteDataTests/Internal/BaseCloudKitTests.swift index b28963f6..f55e5a64 100644 --- a/Tests/SQLiteDataTests/Internal/BaseCloudKitTests.swift +++ b/Tests/SQLiteDataTests/Internal/BaseCloudKitTests.swift @@ -7,6 +7,7 @@ import SQLiteData import SnapshotTesting import Testing +import TestLocals import os @Suite( @@ -16,7 +17,7 @@ $0.continuousClock = TestClock() $0.dataManager = InMemoryDataManager() }, - .taskLocal(_$attachMetadatabase, false) + $attachMetadatabase.set(false) ) class BaseCloudKitTests: @unchecked Sendable { let userDatabase: UserDatabase diff --git a/Tests/SQLiteDataTests/Internal/TestScopes.swift b/Tests/SQLiteDataTests/Internal/TestScopes.swift deleted file mode 100644 index 5b0e699c..00000000 --- a/Tests/SQLiteDataTests/Internal/TestScopes.swift +++ /dev/null @@ -1,32 +0,0 @@ -#if canImport(CloudKit) - import CloudKit - import Testing - import SQLiteData - - var prepareDatabase: @Sendable (UserDatabase) async throws -> Void { - _$prepareDatabase.get() - } - let _$prepareDatabase = TaskLocal<@Sendable (UserDatabase) async throws -> Void>( - wrappedValue: { _ in } - ) - - var startImmediately: Bool { - _$startImmediately.get() - } - let _$startImmediately = TaskLocal(wrappedValue: true) - - var attachMetadatabase: Bool { - _$attachMetadatabase.get() - } - let _$attachMetadatabase = TaskLocal(wrappedValue: false) - - var accountStatus: CKAccountStatus { - _$accountStatus.get() - } - let _$accountStatus = TaskLocal(wrappedValue: CKAccountStatus.available) - - var syncEngineDelegate: (any SyncEngineDelegate)? { - _$syncEngineDelegate.get() - } - let _$syncEngineDelegate = TaskLocal<(any SyncEngineDelegate)?>(wrappedValue: nil) -#endif