2024-04-20 12:04:24 +08:00
|
|
|
import SwiftUI
|
2024-09-26 05:17:26 +08:00
|
|
|
import Testing
|
|
|
|
@testable import Defaults
|
2024-04-20 12:04:24 +08:00
|
|
|
|
2024-09-26 05:17:26 +08:00
|
|
|
private final class MockStorage: DefaultsKeyValueStore {
|
2024-04-20 12:04:24 +08:00
|
|
|
private var pairs: [String: Any] = [:]
|
|
|
|
private let queue = DispatchQueue(label: "a")
|
|
|
|
|
|
|
|
func data<T>(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<T>(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 {
|
2024-09-26 05:17:26 +08:00
|
|
|
let pairs = queue.sync { Array(self.pairs.keys) }
|
2024-04-20 12:04:24 +08:00
|
|
|
NotificationCenter.default.post(Notification(name: NSUbiquitousKeyValueStore.didChangeExternallyNotification, userInfo: [NSUbiquitousKeyValueStoreChangedKeysKey: pairs]))
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private let mockStorage = MockStorage()
|
|
|
|
|
2024-09-26 05:17:26 +08:00
|
|
|
@Suite(.serialized)
|
|
|
|
final class DefaultsICloudTests {
|
|
|
|
private let suite = createSuite()
|
|
|
|
|
|
|
|
init() {
|
2024-04-20 12:04:24 +08:00
|
|
|
Defaults.iCloud.isDebug = true
|
|
|
|
Defaults.iCloud.syncOnChange = true
|
|
|
|
Defaults.iCloud.synchronizer = iCloudSynchronizer(remoteStorage: mockStorage)
|
|
|
|
}
|
|
|
|
|
2024-09-26 05:17:26 +08:00
|
|
|
deinit {
|
2024-04-20 12:04:24 +08:00
|
|
|
mockStorage.removeAll()
|
|
|
|
Defaults.iCloud.removeAll()
|
2024-09-26 05:17:26 +08:00
|
|
|
Defaults.removeAll(suite: suite)
|
2024-04-20 12:04:24 +08:00
|
|
|
}
|
|
|
|
|
2024-03-29 00:45:39 +08:00
|
|
|
private func updateMockStorage(key: String, value: some Any, _ date: Date? = nil) {
|
2024-04-20 12:04:24 +08:00
|
|
|
mockStorage.set([date ?? Date(), value], forKey: key)
|
|
|
|
}
|
|
|
|
|
2024-09-26 05:17:26 +08:00
|
|
|
@Test
|
2024-04-20 12:04:24 +08:00
|
|
|
func testICloudInitialize() async {
|
2024-09-26 05:17:26 +08:00
|
|
|
let name = Defaults.Key<String>("testICloudInitialize_name", default: "0", suite: suite, iCloud: true)
|
|
|
|
let quality = Defaults.Key<Double>("testICloudInitialize_quality", default: 0.0, suite: suite, iCloud: true)
|
2024-04-20 12:04:24 +08:00
|
|
|
|
2024-03-29 00:45:39 +08:00
|
|
|
await Defaults.iCloud.waitForSyncCompletion()
|
2024-09-26 05:17:26 +08:00
|
|
|
#expect(mockStorage.data(forKey: name.name) == nil)
|
|
|
|
#expect(mockStorage.data(forKey: quality.name) == nil)
|
2024-04-20 12:04:24 +08:00
|
|
|
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..<name_expected.count {
|
|
|
|
Defaults[name] = name_expected[index]
|
|
|
|
Defaults[quality] = quality_expected[index]
|
2024-03-29 00:45:39 +08:00
|
|
|
await Defaults.iCloud.waitForSyncCompletion()
|
2024-09-26 05:17:26 +08:00
|
|
|
#expect(mockStorage.data(forKey: name.name) == name_expected[index])
|
|
|
|
#expect(mockStorage.data(forKey: quality.name) == quality_expected[index])
|
2024-04-20 12:04:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
updateMockStorage(key: quality.name, value: 8.0)
|
|
|
|
updateMockStorage(key: name.name, value: "8")
|
|
|
|
mockStorage.synchronize()
|
2024-03-29 00:45:39 +08:00
|
|
|
await Defaults.iCloud.waitForSyncCompletion()
|
2024-09-26 05:17:26 +08:00
|
|
|
#expect(Defaults[quality] == 8.0)
|
|
|
|
#expect(Defaults[name] == "8")
|
2024-04-20 12:04:24 +08:00
|
|
|
|
|
|
|
Defaults[name] = "9"
|
|
|
|
Defaults[quality] = 9.0
|
2024-03-29 00:45:39 +08:00
|
|
|
await Defaults.iCloud.waitForSyncCompletion()
|
2024-09-26 05:17:26 +08:00
|
|
|
#expect(mockStorage.data(forKey: name.name) == "9")
|
|
|
|
#expect(mockStorage.data(forKey: quality.name) == 9.0)
|
2024-04-20 12:04:24 +08:00
|
|
|
|
|
|
|
updateMockStorage(key: quality.name, value: 10)
|
|
|
|
updateMockStorage(key: name.name, value: "10")
|
|
|
|
mockStorage.synchronize()
|
2024-03-29 00:45:39 +08:00
|
|
|
await Defaults.iCloud.waitForSyncCompletion()
|
2024-09-26 05:17:26 +08:00
|
|
|
#expect(Defaults[quality] == 10.0)
|
|
|
|
#expect(Defaults[name] == "10")
|
2024-04-20 12:04:24 +08:00
|
|
|
}
|
|
|
|
|
2024-09-26 05:17:26 +08:00
|
|
|
@Test
|
2024-04-20 12:04:24 +08:00
|
|
|
func testDidChangeExternallyNotification() async {
|
|
|
|
updateMockStorage(key: "testDidChangeExternallyNotification_name", value: "0")
|
|
|
|
updateMockStorage(key: "testDidChangeExternallyNotification_quality", value: 0.0)
|
2024-09-26 05:17:26 +08:00
|
|
|
let name = Defaults.Key<String?>("testDidChangeExternallyNotification_name", suite: suite, iCloud: true)
|
|
|
|
let quality = Defaults.Key<Double?>("testDidChangeExternallyNotification_quality", suite: suite, iCloud: true)
|
2024-03-29 00:45:39 +08:00
|
|
|
await Defaults.iCloud.waitForSyncCompletion()
|
2024-09-26 05:17:26 +08:00
|
|
|
#expect(Defaults[name] == "0")
|
|
|
|
#expect(Defaults[quality] == 0.0)
|
2024-04-20 12:04:24 +08:00
|
|
|
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..<name_expected.count {
|
|
|
|
updateMockStorage(key: name.name, value: name_expected[index])
|
|
|
|
updateMockStorage(key: quality.name, value: quality_expected[index])
|
|
|
|
mockStorage.synchronize()
|
|
|
|
}
|
2024-03-29 00:45:39 +08:00
|
|
|
await Defaults.iCloud.waitForSyncCompletion()
|
2024-09-26 05:17:26 +08:00
|
|
|
#expect(Defaults[name] == "7")
|
|
|
|
#expect(Defaults[quality] == 7.0)
|
2024-04-20 12:04:24 +08:00
|
|
|
|
|
|
|
Defaults[name] = "8"
|
|
|
|
Defaults[quality] = 8.0
|
2024-03-29 00:45:39 +08:00
|
|
|
await Defaults.iCloud.waitForSyncCompletion()
|
2024-09-26 05:17:26 +08:00
|
|
|
#expect(mockStorage.data(forKey: name.name) == "8")
|
|
|
|
#expect(mockStorage.data(forKey: quality.name) == 8.0)
|
2024-04-20 12:04:24 +08:00
|
|
|
|
|
|
|
Defaults[name] = nil
|
|
|
|
Defaults[quality] = nil
|
2024-03-29 00:45:39 +08:00
|
|
|
await Defaults.iCloud.waitForSyncCompletion()
|
2024-09-26 05:17:26 +08:00
|
|
|
#expect(mockStorage.data(forKey: name.name) == nil)
|
|
|
|
#expect(mockStorage.data(forKey: quality.name) == nil)
|
2024-04-20 12:04:24 +08:00
|
|
|
}
|
|
|
|
|
2024-09-26 05:17:26 +08:00
|
|
|
@Test
|
2024-04-20 12:04:24 +08:00
|
|
|
func testICloudInitializeSyncLast() async {
|
2024-09-26 05:17:26 +08:00
|
|
|
let name = Defaults.Key<String>("testICloudInitializeSyncLast_name", default: "0", suite: suite, iCloud: true)
|
|
|
|
let quality = Defaults.Key<Double>("testICloudInitializeSyncLast_quality", default: 0.0, suite: suite, iCloud: true)
|
2024-04-20 12:04:24 +08:00
|
|
|
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..<name_expected.count {
|
|
|
|
Defaults[name] = name_expected[index]
|
|
|
|
Defaults[quality] = quality_expected[index]
|
2024-09-26 05:17:26 +08:00
|
|
|
#expect(Defaults[name] == name_expected[index])
|
|
|
|
#expect(Defaults[quality] == quality_expected[index])
|
2024-04-20 12:04:24 +08:00
|
|
|
}
|
|
|
|
|
2024-03-29 00:45:39 +08:00
|
|
|
await Defaults.iCloud.waitForSyncCompletion()
|
2024-09-26 05:17:26 +08:00
|
|
|
#expect(mockStorage.data(forKey: name.name) == "7")
|
|
|
|
#expect(mockStorage.data(forKey: quality.name) == 7.0)
|
2024-04-20 12:04:24 +08:00
|
|
|
}
|
|
|
|
|
2024-09-26 05:17:26 +08:00
|
|
|
@Test
|
2024-04-20 12:04:24 +08:00
|
|
|
func testRemoveKey() async {
|
2024-09-26 05:17:26 +08:00
|
|
|
let name = Defaults.Key<String>("testRemoveKey_name", default: "0", suite: suite, iCloud: true)
|
|
|
|
let quality = Defaults.Key<Double>("testRemoveKey_quality", default: 0.0, suite: suite, iCloud: true)
|
2024-04-20 12:04:24 +08:00
|
|
|
Defaults[name] = "1"
|
|
|
|
Defaults[quality] = 1.0
|
2024-03-29 00:45:39 +08:00
|
|
|
await Defaults.iCloud.waitForSyncCompletion()
|
2024-09-26 05:17:26 +08:00
|
|
|
#expect(mockStorage.data(forKey: name.name) == "1")
|
|
|
|
#expect(mockStorage.data(forKey: quality.name) == 1.0)
|
2024-04-20 12:04:24 +08:00
|
|
|
|
|
|
|
Defaults.iCloud.remove(quality)
|
|
|
|
Defaults[name] = "2"
|
|
|
|
Defaults[quality] = 1.0
|
2024-03-29 00:45:39 +08:00
|
|
|
await Defaults.iCloud.waitForSyncCompletion()
|
2024-09-26 05:17:26 +08:00
|
|
|
#expect(mockStorage.data(forKey: name.name) == "2")
|
|
|
|
#expect(mockStorage.data(forKey: quality.name) == 1.0)
|
2024-04-20 12:04:24 +08:00
|
|
|
}
|
|
|
|
|
2024-09-26 05:17:26 +08:00
|
|
|
@Test
|
2024-04-20 12:04:24 +08:00
|
|
|
func testSyncKeysFromLocal() async {
|
2024-09-26 05:17:26 +08:00
|
|
|
let name = Defaults.Key<String>("testSyncKeysFromLocal_name", default: "0", suite: suite)
|
|
|
|
let quality = Defaults.Key<Double>("testSyncKeysFromLocal_quality", default: 0.0, suite: suite)
|
2024-04-20 12:04:24 +08:00
|
|
|
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..<name_expected.count {
|
|
|
|
Defaults[name] = name_expected[index]
|
|
|
|
Defaults[quality] = quality_expected[index]
|
|
|
|
Defaults.iCloud.syncWithoutWaiting(name, quality, source: .local)
|
2024-03-29 00:45:39 +08:00
|
|
|
await Defaults.iCloud.waitForSyncCompletion()
|
2024-09-26 05:17:26 +08:00
|
|
|
#expect(mockStorage.data(forKey: name.name) == name_expected[index])
|
|
|
|
#expect(mockStorage.data(forKey: quality.name) == quality_expected[index])
|
2024-04-20 12:04:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
updateMockStorage(key: name.name, value: "8")
|
|
|
|
updateMockStorage(key: quality.name, value: 8)
|
|
|
|
Defaults.iCloud.syncWithoutWaiting(name, quality, source: .remote)
|
2024-03-29 00:45:39 +08:00
|
|
|
await Defaults.iCloud.waitForSyncCompletion()
|
2024-09-26 05:17:26 +08:00
|
|
|
#expect(Defaults[quality] == 8.0)
|
|
|
|
#expect(Defaults[name] == "8")
|
2024-04-20 12:04:24 +08:00
|
|
|
}
|
|
|
|
|
2024-09-26 05:17:26 +08:00
|
|
|
@Test
|
2024-04-20 12:04:24 +08:00
|
|
|
func testSyncKeysFromRemote() async {
|
2024-09-26 05:17:26 +08:00
|
|
|
let name = Defaults.Key<String?>("testSyncKeysFromRemote_name", suite: suite)
|
|
|
|
let quality = Defaults.Key<Double?>("testSyncKeysFromRemote_quality", suite: suite)
|
2024-04-20 12:04:24 +08:00
|
|
|
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..<name_expected.count {
|
|
|
|
updateMockStorage(key: name.name, value: name_expected[index])
|
|
|
|
updateMockStorage(key: quality.name, value: quality_expected[index])
|
|
|
|
Defaults.iCloud.syncWithoutWaiting(name, quality, source: .remote)
|
2024-03-29 00:45:39 +08:00
|
|
|
await Defaults.iCloud.waitForSyncCompletion()
|
2024-09-26 05:17:26 +08:00
|
|
|
#expect(Defaults[name] == name_expected[index])
|
|
|
|
#expect(Defaults[quality] == quality_expected[index])
|
2024-04-20 12:04:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
Defaults[name] = "8"
|
|
|
|
Defaults[quality] = 8.0
|
|
|
|
Defaults.iCloud.syncWithoutWaiting(name, quality, source: .local)
|
2024-03-29 00:45:39 +08:00
|
|
|
await Defaults.iCloud.waitForSyncCompletion()
|
2024-09-26 05:17:26 +08:00
|
|
|
#expect(mockStorage.data(forKey: name.name) == "8")
|
|
|
|
#expect(mockStorage.data(forKey: quality.name) == 8.0)
|
2024-04-20 12:04:24 +08:00
|
|
|
|
|
|
|
Defaults[name] = nil
|
|
|
|
Defaults[quality] = nil
|
|
|
|
Defaults.iCloud.syncWithoutWaiting(name, quality, source: .local)
|
2024-03-29 00:45:39 +08:00
|
|
|
await Defaults.iCloud.waitForSyncCompletion()
|
2024-09-26 05:17:26 +08:00
|
|
|
#expect(mockStorage.object(forKey: name.name) == nil)
|
|
|
|
#expect(mockStorage.object(forKey: quality.name) == nil)
|
2024-04-20 12:04:24 +08:00
|
|
|
}
|
|
|
|
|
2024-09-26 05:17:26 +08:00
|
|
|
@Test
|
2024-04-20 12:04:24 +08:00
|
|
|
func testAddFromDetached() async {
|
2024-09-26 05:17:26 +08:00
|
|
|
let name = Defaults.Key<String>("testInitAddFromDetached_name", default: "0", suite: suite)
|
|
|
|
let quantity = Defaults.Key<Bool>("testInitAddFromDetached_quantity", default: false, suite: suite)
|
|
|
|
await Task.detached {
|
2024-08-13 04:50:47 +08:00
|
|
|
Defaults.iCloud.add(name, quantity)
|
2024-04-20 12:04:24 +08:00
|
|
|
Defaults.iCloud.syncWithoutWaiting()
|
2024-03-29 00:45:39 +08:00
|
|
|
await Defaults.iCloud.waitForSyncCompletion()
|
2024-09-26 05:17:26 +08:00
|
|
|
}.value
|
|
|
|
#expect(mockStorage.data(forKey: name.name) == "0")
|
2024-04-20 12:04:24 +08:00
|
|
|
Defaults[name] = "1"
|
2024-03-29 00:45:39 +08:00
|
|
|
await Defaults.iCloud.waitForSyncCompletion()
|
2024-09-26 05:17:26 +08:00
|
|
|
#expect(mockStorage.data(forKey: name.name) == "1")
|
2024-04-20 12:04:24 +08:00
|
|
|
}
|
|
|
|
|
2024-09-26 05:17:26 +08:00
|
|
|
@Test
|
2024-04-20 12:04:24 +08:00
|
|
|
func testICloudInitializeFromDetached() async {
|
2024-09-26 05:17:26 +08:00
|
|
|
await Task.detached {
|
|
|
|
let name = Defaults.Key<String>("testICloudInitializeFromDetached_name", default: "0", suite: self.suite, iCloud: true)
|
2024-04-20 12:04:24 +08:00
|
|
|
|
2024-03-29 00:45:39 +08:00
|
|
|
await Defaults.iCloud.waitForSyncCompletion()
|
2024-09-26 05:17:26 +08:00
|
|
|
#expect(mockStorage.data(forKey: name.name) == nil)
|
|
|
|
}.value
|
2024-04-20 12:04:24 +08:00
|
|
|
}
|
|
|
|
}
|