@testable import Defaults import SwiftUI import XCTest 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() @available(iOS 15, tvOS 15, watchOS 8, visionOS 1.0, *) final class DefaultsICloudTests: XCTestCase { override final class func setUp() { Defaults.iCloud.isDebug = true Defaults.iCloud.syncOnChange = true Defaults.iCloud.synchronizer = iCloudSynchronizer(remoteStorage: mockStorage) } override func setUp() { super.setUp() mockStorage.removeAll() Defaults.iCloud.removeAll() Defaults.removeAll() } override func tearDown() { super.tearDown() mockStorage.removeAll() Defaults.iCloud.removeAll() Defaults.removeAll() } private func updateMockStorage(key: String, value: some Any, _ date: Date? = nil) { mockStorage.set([date ?? Date(), value], forKey: key) } func testICloudInitialize() async { print(Defaults.iCloud.keys) let name = Defaults.Key("testICloudInitialize_name", default: "0", iCloud: true) let quality = Defaults.Key("testICloudInitialize_quality", default: 0.0, iCloud: true) print(Defaults.iCloud.keys) await Defaults.iCloud.waitForSyncCompletion() XCTAssertEqual(mockStorage.data(forKey: name.name), "0") XCTAssertEqual(mockStorage.data(forKey: quality.name), 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..("testDidChangeExternallyNotification_name", iCloud: true) let quality = Defaults.Key("testDidChangeExternallyNotification_quality", iCloud: true) await Defaults.iCloud.waitForSyncCompletion() XCTAssertEqual(Defaults[name], "0") XCTAssertEqual(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", iCloud: true) let quality = Defaults.Key("testICloudInitializeSyncLast_quality", default: 0.0, 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", iCloud: true) let quality = Defaults.Key("testRemoveKey_quality", default: 0.0, iCloud: true) Defaults[name] = "1" Defaults[quality] = 1.0 await Defaults.iCloud.waitForSyncCompletion() XCTAssertEqual(mockStorage.data(forKey: name.name), "1") XCTAssertEqual(mockStorage.data(forKey: quality.name), 1.0) Defaults.iCloud.remove(quality) Defaults[name] = "2" Defaults[quality] = 1.0 await Defaults.iCloud.waitForSyncCompletion() XCTAssertEqual(mockStorage.data(forKey: name.name), "2") XCTAssertEqual(mockStorage.data(forKey: quality.name), 1.0) } func testSyncKeysFromLocal() async { let name = Defaults.Key("testSyncKeysFromLocal_name", default: "0") let quality = Defaults.Key("testSyncKeysFromLocal_quality", default: 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..("testSyncKeysFromRemote_name") let quality = Defaults.Key("testSyncKeysFromRemote_quality") 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") let task = Task.detached { Defaults.iCloud.add(name) Defaults.iCloud.syncWithoutWaiting() await Defaults.iCloud.waitForSyncCompletion() } await task.value XCTAssertEqual(mockStorage.data(forKey: name.name), "0") Defaults[name] = "1" await Defaults.iCloud.waitForSyncCompletion() XCTAssertEqual(mockStorage.data(forKey: name.name), "1") } func testICloudInitializeFromDetached() async { let task = Task.detached { let name = Defaults.Key("testICloudInitializeFromDetached_name", default: "0", iCloud: true) await Defaults.iCloud.waitForSyncCompletion() XCTAssertEqual(mockStorage.data(forKey: name.name), "0") } await task.value } }