|
|
@@ -112,4 +112,81 @@ class PendingOverlayCoreTest {
|
|
|
assertEquals(2, h.dsState.compatDsInt("b"))
|
|
|
assertEquals(h.dsState, h.core.preferencesFlow.value)
|
|
|
}
|
|
|
+
|
|
|
+ @Test
|
|
|
+ fun `写入异常时-overlay仍被清空且回退到磁盘旧值`() {
|
|
|
+ var throwError = false
|
|
|
+ var dsState: Preferences = mutablePreferencesOf(intPreferencesKey("k") to 1)
|
|
|
+ val writeQueue = ArrayDeque<suspend () -> Unit>()
|
|
|
+ val core = PendingOverlayCore(
|
|
|
+ initial = dsState,
|
|
|
+ launchWrite = { writeQueue += it },
|
|
|
+ persist = { key, value ->
|
|
|
+ if (throwError) {
|
|
|
+ throw java.io.IOException("Disk full")
|
|
|
+ }
|
|
|
+ val mutable = dsState.toMutablePreferences()
|
|
|
+ mutable.setPrefValue(key, value)
|
|
|
+ dsState = mutable.toPreferences()
|
|
|
+ dsState
|
|
|
+ },
|
|
|
+ )
|
|
|
+ core.put("k", 2)
|
|
|
+ assertEquals(2, core.preferencesFlow.value.compatDsInt("k"))
|
|
|
+
|
|
|
+ throwError = true
|
|
|
+ try {
|
|
|
+ runBlocking { writeQueue.removeFirst().invoke() }
|
|
|
+ } catch (e: Exception) {
|
|
|
+ // Expected
|
|
|
+ }
|
|
|
+
|
|
|
+ // 验证 overlay 已被清除,读取回退到快照中的旧值 1
|
|
|
+ assertEquals(1, core.preferencesFlow.value.compatDsInt("k"))
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ fun `同key连续写入-第一次失败第二次成功-最终正确应用第二次的值`() {
|
|
|
+ var firstWrite = true
|
|
|
+ var dsState: Preferences = mutablePreferencesOf(intPreferencesKey("k") to 1)
|
|
|
+ val writeQueue = ArrayDeque<suspend () -> Unit>()
|
|
|
+ val core = PendingOverlayCore(
|
|
|
+ initial = dsState,
|
|
|
+ launchWrite = { writeQueue += it },
|
|
|
+ persist = { key, value ->
|
|
|
+ if (firstWrite) {
|
|
|
+ firstWrite = false
|
|
|
+ throw java.io.IOException("Disk full on first write")
|
|
|
+ }
|
|
|
+ val mutable = dsState.toMutablePreferences()
|
|
|
+ mutable.setPrefValue(key, value)
|
|
|
+ dsState = mutable.toPreferences()
|
|
|
+ dsState
|
|
|
+ },
|
|
|
+ )
|
|
|
+ // 第一次写入 2 (失败)
|
|
|
+ core.put("k", 2)
|
|
|
+ // 第二次写入 3 (成功)
|
|
|
+ core.put("k", 3)
|
|
|
+
|
|
|
+ assertEquals(3, core.preferencesFlow.value.compatDsInt("k"))
|
|
|
+
|
|
|
+ // 执行第一个任务(失败)
|
|
|
+ try {
|
|
|
+ runBlocking { writeQueue.removeFirst().invoke() }
|
|
|
+ } catch (e: Exception) {
|
|
|
+ // Expected
|
|
|
+ }
|
|
|
+
|
|
|
+ // 此时因为 pending 中最新的 write 对象是 3 的写入,且 3 的任务还没跑完,
|
|
|
+ // 即使 2 的写入任务失败清理了,读取也应该保持最新值 3 (来自 overlay),而不是回退到 1
|
|
|
+ assertEquals(3, core.preferencesFlow.value.compatDsInt("k"))
|
|
|
+
|
|
|
+ // 执行第二个任务(成功)
|
|
|
+ runBlocking { writeQueue.removeFirst().invoke() }
|
|
|
+
|
|
|
+ // 第二个成功,快照更新为 3,并且 overlay 彻底清空,读取为 3
|
|
|
+ assertEquals(3, core.preferencesFlow.value.compatDsInt("k"))
|
|
|
+ assertEquals(3, dsState.compatDsInt("k"))
|
|
|
+ }
|
|
|
}
|