import SwiftUI import Testing @testable import Defaults private final class MockStorage: DefaultsKeyValueStore { private var pairs: [String: Any] = [:] private let queue = DispatchQueue(label: "a") func data(forKey aKey: String) -> T? { queue.sync { guard let values = pairs[aKey] as? [Any], let data = values[safe: 1] as? T else { return nil } return data } } func object(forKey aKey: String) -> T? { queue.sync { pairs[aKey] as? T } } func object(forKey aKey: String) -> Any? { queue.sync { pairs[aKey] } } func set(_ anObject: Any?, forKey aKey: String) { queue.sync { pairs[aKey] = anObject } } func removeObject(forKey aKey: String) { _ = queue.sync { pairs.removeValue(forKey: aKey) } } func removeAll() { queue.sync { pairs.removeAll() } } @discardableResult func synchronize() -> Bool { let pairs = queue.sync { Array(self.pairs.keys) } NotificationCenter.default.post(Notification(name: NSUbiquitousKeyValueStore.didChangeExternallyNotification, userInfo: [NSUbiquitousKeyValueStoreChangedKeysKey: pairs])) return true } } private let mockStorage = MockStorage() @Suite(.serialized) final class DefaultsICloudTests { private let suite = createSuite() init() { Defaults.iCloud.isDebug = true Defaults.iCloud.syncOnChange = true Defaults.iCloud.synchronizer = iCloudSynchronizer(remoteStorage: mockStorage) } deinit { mockStorage.removeAll() Defaults.iCloud.removeAll() Defaults.removeAll(suite: suite) } private func updateMockStorage(key: String, value: some Any, _ date: Date? = nil) { mockStorage.set([date ?? Date(), value], forKey: key) } @Test func testICloudInitialize() async { let name = Defaults.Key("testICloudInitialize_name", default: "0", suite: suite, iCloud: true) let quality = Defaults.Key("testICloudInitialize_quality", default: 0.0, suite: suite, iCloud: true) await Defaults.iCloud.waitForSyncCompletion() #expect(mockStorage.data(forKey: name.name) == nil) #expect(mockStorage.data(forKey: quality.name) == nil) let name_expected = ["1", "2", "3", "4", "5", "6", "7"] let quality_expected = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0] for index in 0..("testDidChangeExternallyNotification_name", suite: suite, iCloud: true) let quality = Defaults.Key("testDidChangeExternallyNotification_quality", suite: suite, iCloud: true) await Defaults.iCloud.waitForSyncCompletion() #expect(Defaults[name] == "0") #expect(Defaults[quality] == 0.0) let name_expected = ["1", "2", "3", "4", "5", "6", "7"] let quality_expected = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0] for index in 0..("testICloudInitializeSyncLast_name", default: "0", suite: suite, iCloud: true) let quality = Defaults.Key("testICloudInitializeSyncLast_quality", default: 0.0, suite: suite, iCloud: true) let name_expected = ["1", "2", "3", "4", "5", "6", "7"] let quality_expected = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0] for index in 0..("testRemoveKey_name", default: "0", suite: suite, iCloud: true) let quality = Defaults.Key("testRemoveKey_quality", default: 0.0, suite: suite, iCloud: true) Defaults[name] = "1" Defaults[quality] = 1.0 await Defaults.iCloud.waitForSyncCompletion() #expect(mockStorage.data(forKey: name.name) == "1") #expect(mockStorage.data(forKey: quality.name) == 1.0) Defaults.iCloud.remove(quality) Defaults[name] = "2" Defaults[quality] = 1.0 await Defaults.iCloud.waitForSyncCompletion() #expect(mockStorage.data(forKey: name.name) == "2") #expect(mockStorage.data(forKey: quality.name) == 1.0) } @Test func testSyncKeysFromLocal() async { let name = Defaults.Key("testSyncKeysFromLocal_name", default: "0", suite: suite) let quality = Defaults.Key("testSyncKeysFromLocal_quality", default: 0.0, suite: suite) let name_expected = ["1", "2", "3", "4", "5", "6", "7"] let quality_expected = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0] for index in 0..("testSyncKeysFromRemote_name", suite: suite) let quality = Defaults.Key("testSyncKeysFromRemote_quality", suite: suite) let name_expected = ["1", "2", "3", "4", "5", "6", "7"] let quality_expected = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0] for index in 0..("testInitAddFromDetached_name", default: "0", suite: suite) let quantity = Defaults.Key("testInitAddFromDetached_quantity", default: false, suite: suite) await Task.detached { Defaults.iCloud.add(name, quantity) Defaults.iCloud.syncWithoutWaiting() await Defaults.iCloud.waitForSyncCompletion() }.value #expect(mockStorage.data(forKey: name.name) == "0") Defaults[name] = "1" await Defaults.iCloud.waitForSyncCompletion() #expect(mockStorage.data(forKey: name.name) == "1") } @Test func testICloudInitializeFromDetached() async { await Task.detached { let name = Defaults.Key("testICloudInitializeFromDetached_name", default: "0", suite: self.suite, iCloud: true) await Defaults.iCloud.waitForSyncCompletion() #expect(mockStorage.data(forKey: name.name) == nil) }.value } }