From 60f186d23765e017b739c217260f67afebb5518a Mon Sep 17 00:00:00 2001 From: Sergey Malenkov Date: Fri, 25 Oct 2024 14:38:10 +0300 Subject: [PATCH] Add sequential versions of snapshots Signed-off-by: Sergey Malenkov --- arkoala/framework/src/Application.ts | 1 + incremental/runtime/src/memo/testing.ts | 8 +- .../runtime/src/states/GlobalStateManager.ts | 1 + incremental/runtime/src/states/State.ts | 40 +++++-- .../runtime/test-arkts/states/State.test.ts | 77 ++++++++++++++ incremental/runtime/test/states/State.test.ts | 100 ++++++++++++++++-- 6 files changed, 205 insertions(+), 22 deletions(-) diff --git a/arkoala/framework/src/Application.ts b/arkoala/framework/src/Application.ts index d9885568e..5207914a2 100644 --- a/arkoala/framework/src/Application.ts +++ b/arkoala/framework/src/Application.ts @@ -113,6 +113,7 @@ function uiRoot( function updateStates(manager: StateManager, root: State): void { // Ensure all current state updates took effect. + manager.prepareSnapshot() manager.updateSnapshot() root.value if (partialUpdates.length > 0) { diff --git a/incremental/runtime/src/memo/testing.ts b/incremental/runtime/src/memo/testing.ts index a97a3379e..281623a25 100644 --- a/incremental/runtime/src/memo/testing.ts +++ b/incremental/runtime/src/memo/testing.ts @@ -14,7 +14,7 @@ */ import { uint32 } from "@koalaui/common" -import { callScheduledCallbacks, updateStateManager } from "../states/GlobalStateManager" +import { callScheduledCallbacks, GlobalStateManager, updateStateManager } from "../states/GlobalStateManager" import { ComputableState, State } from "../states/State" import { IncrementalNode } from "../tree/IncrementalNode" import { memoRoot } from "./entry" @@ -60,7 +60,9 @@ export function testRoot( /** @internal */ export function testTick(root: State, withCallbacks: boolean = true) { - updateStateManager() + const manager = GlobalStateManager.instance + manager.prepareSnapshot() + manager.updateSnapshot() root.value - if (withCallbacks) callScheduledCallbacks() + if (withCallbacks) manager.callCallbacks() } diff --git a/incremental/runtime/src/states/GlobalStateManager.ts b/incremental/runtime/src/states/GlobalStateManager.ts index 1cf1da8d4..5203fb69a 100644 --- a/incremental/runtime/src/states/GlobalStateManager.ts +++ b/incremental/runtime/src/states/GlobalStateManager.ts @@ -62,6 +62,7 @@ export function updateStateManager(manager: StateManager = GlobalStateManager.in */ export function callScheduledCallbacks(manager: StateManager = GlobalStateManager.instance): void { manager.callCallbacks() + manager.prepareSnapshot() } /** diff --git a/incremental/runtime/src/states/State.ts b/incremental/runtime/src/states/State.ts index bf6b8c1c9..0d90579cd 100644 --- a/incremental/runtime/src/states/State.ts +++ b/incremental/runtime/src/states/State.ts @@ -13,7 +13,7 @@ * limitations under the License. */ -import { Array_from_set, className, int32, KoalaCallsiteKey, KoalaCallsiteKeys, KoalaProfiler, refEqual, uint32 } from "@koalaui/common" +import { Array_from_set, className, int32, KoalaCallsiteKey, KoalaCallsiteKeys, KoalaProfiler, refEqual, uint32, uint64 } from "@koalaui/common" import { Dependencies, Dependency } from "./Dependency" import { Disposable, disposeContent, disposeContentBackward } from "./Disposable" import { Observable, ObservableHandler } from "./Observable" @@ -45,6 +45,7 @@ export function createStateManager(): StateManager { */ export interface StateManager extends StateContext { isUpdateNeeded(): boolean + prepareSnapshot(): void updateSnapshot(): uint32 updatableNode(node: Node, update: (context: StateContext) => void, cleanup?: () => void): ComputableState scheduleCallback(callback: () => void): void @@ -163,7 +164,7 @@ interface ManagedState extends Disposable { */ readonly global: boolean readonly modified: boolean - updateStateSnapshot(): void + updateStateSnapshot(version: uint64): void } interface ManagedScope extends Disposable, Dependency, ReadonlyTreeNode { @@ -186,7 +187,9 @@ interface ManagedScope extends Disposable, Dependency, ReadonlyTreeNode { class StateImpl implements Observable, ManagedState, MutableState { private manager: StateManagerImpl | undefined = undefined private dependencies: Dependencies | undefined = undefined + private version: uint64 = 0 private current: Value + private previous: Value private snapshot: Value private myModified = false private myUpdated = true @@ -211,6 +214,7 @@ class StateImpl implements Observable, ManagedState, MutableState this.manager = manager this.dependencies = new Dependencies() this.current = initial + this.previous = initial this.snapshot = initial ObservableHandler.attach(initial, this) manager.addCreatedState(this) @@ -232,6 +236,14 @@ class StateImpl implements Observable, ManagedState, MutableState set value(value: Value) { if (this.setProhibited) throw new Error("prohibited to modify a state when updating a call tree") + const manager = this.manager + if (manager) { + const version = manager.version + 1 + if (this.version < version) { + this.version = version + this.previous = this.current + } + } const tracker = this.tracker this.current = tracker ? tracker.onUpdate(value) : value this.onModify() @@ -247,7 +259,7 @@ class StateImpl implements Observable, ManagedState, MutableState if (manager) { manager.updateNeeded = true } else { - this.updateStateSnapshot() + this.updateStateSnapshot(this.version) } } @@ -259,18 +271,20 @@ class StateImpl implements Observable, ManagedState, MutableState return true } - updateStateSnapshot(): void { - const isModified = ObservableHandler.dropModified(this.snapshot) + updateStateSnapshot(version: uint64): void { + const oldValue = this.snapshot + const isModified = ObservableHandler.dropModified(oldValue) // optimization: ignore comparison if the state is already updated if (this.myUpdated) { this.myModified = false } else { this.myUpdated = true - if (isDifferent(this.current, this.snapshot, this.equivalent)) { - ObservableHandler.detach(this.snapshot, this) - ObservableHandler.attach(this.current, this) - this.snapshot = this.current + const newValue = this.version <= version ? this.current : this.previous + if (isDifferent(newValue, oldValue, this.equivalent)) { + ObservableHandler.detach(oldValue, this) + ObservableHandler.attach(newValue, this) + this.snapshot = newValue this.myModified = true } else { this.myModified = isModified @@ -382,6 +396,7 @@ class StateManagerImpl implements StateManager { external: Dependency | undefined = undefined updateNeeded = false frozen = false + version: uint64 = 0 private readonly callbacks = new Array<() => void>() constructor() { @@ -411,6 +426,10 @@ class StateManagerImpl implements StateManager { return this.updateNeeded } + prepareSnapshot() { + this.version++ + } + updateSnapshot(): uint32 { KoalaProfiler.counters?.updateSnapshotEnter() this.checkForStateComputing() @@ -420,11 +439,12 @@ class StateManagerImpl implements StateManager { // try to update snapshot for every state, except for parameter states const created = this.statesCreated.size as int32 // amount of created states to update if (created > 0) { + const version = this.version const it = this.statesCreated.keys() while (true) { const result = it.next() if (result.done) break - result.value?.updateStateSnapshot() + result.value?.updateStateSnapshot(version) if (result.value?.modified == true) modified++ } } diff --git a/incremental/runtime/test-arkts/states/State.test.ts b/incremental/runtime/test-arkts/states/State.test.ts index 66f46df49..913037235 100644 --- a/incremental/runtime/test-arkts/states/State.test.ts +++ b/incremental/runtime/test-arkts/states/State.test.ts @@ -77,6 +77,7 @@ suite("State", () => { let manager = createStateManager() let state = manager.mutableState(200) state.value = 404 + manager.prepareSnapshot() Assert.equal(1, manager.updateSnapshot()) assertModifiedState(state, 404) }) @@ -84,7 +85,9 @@ suite("State", () => { let manager = createStateManager() let state = manager.mutableState(200) state.value = 404 + manager.prepareSnapshot() Assert.equal(1, manager.updateSnapshot()) + manager.prepareSnapshot() Assert.equal(0, manager.updateSnapshot()) assertState(state, 404) }) @@ -94,6 +97,7 @@ suite("State", () => { for (let index = 0; index <= 200; index++) { state.value = index } + manager.prepareSnapshot() Assert.equal(0, manager.updateSnapshot()) assertState(state, 200) }) @@ -119,6 +123,7 @@ suite("State", () => { let manager = createStateManager() manager.namedState("named", (): float64 => 200) manager.stateBy("named")!.value = 404 + manager.prepareSnapshot() Assert.equal(1, manager.updateSnapshot()) Assert.equal(manager.valueBy("named"), 404) Assert.isTrue(manager.stateBy("named")?.modified) @@ -127,7 +132,9 @@ suite("State", () => { let manager = createStateManager() manager.namedState("named", (): float64 => 200) manager.stateBy("named")!.value = 404 + manager.prepareSnapshot() Assert.equal(1, manager.updateSnapshot()) + manager.prepareSnapshot() Assert.equal(0, manager.updateSnapshot()) Assert.equal(manager.valueBy("named"), 404) Assert.isFalse(manager.stateBy("named")?.modified) @@ -138,6 +145,7 @@ suite("State", () => { for (let index = 0; index <= 200; index++) { manager.stateBy("named")!.value = index } + manager.prepareSnapshot() Assert.equal(0, manager.updateSnapshot()) Assert.equal(manager.valueBy("named"), 200) Assert.isFalse(manager.stateBy("named")?.modified) @@ -187,12 +195,14 @@ suite("State", () => { updatableNode.value // updatable node is already computed after accessing Assert.isFalse(updatableNode.recomputeNeeded) + manager.prepareSnapshot() manager.updateSnapshot() // updatable node is already computed because nothing is changed Assert.isFalse(updatableNode.recomputeNeeded) mutableState.value = !mutableState.value // updatable node does not know that the mutable state is changed Assert.isFalse(updatableNode.recomputeNeeded) + manager.prepareSnapshot() manager.updateSnapshot() // updatable node needs to be recomputed because the mutable state is changed Assert.isTrue(updatableNode.recomputeNeeded) @@ -225,12 +235,14 @@ suite("State", () => { computableState.value // computable state is already computed after accessing Assert.isFalse(computableState.recomputeNeeded) + manager.prepareSnapshot() manager.updateSnapshot() // computable state is already computed because nothing is changed Assert.isFalse(computableState.recomputeNeeded) mutableState.value = !mutableState.value // computable state does not know that the mutable state is changed Assert.isFalse(computableState.recomputeNeeded) + manager.prepareSnapshot() manager.updateSnapshot() // computable state needs to be recomputed because the mutable state is changed Assert.isTrue(computableState.recomputeNeeded) @@ -270,11 +282,13 @@ suite("State", () => { assertState(result, "<= NAME =>") assertStringsAndCleanup(computing, "main ; left ; name ; right") // computable state is not modified + manager.prepareSnapshot() Assert.equal(0, manager.updateSnapshot()) assertState(result, "<= NAME =>") Assert.isEmpty(computing) // compute state only when snapshot updated name.value = "Sergey Malenkov" + manager.prepareSnapshot() Assert.equal(1, manager.updateSnapshot()) assertModifiedState(result, "<= Sergey Malenkov =>") assertStringsAndCleanup(computing, "main ; name") @@ -302,11 +316,13 @@ suite("State", () => { assertState(result, "<= NAME =>") assertStringsAndCleanup(computing, "main ; left ; name ; right") // computable state is not modified + manager.prepareSnapshot() Assert.equal(0, manager.updateSnapshot()) assertState(result, "<= NAME =>") Assert.isEmpty(computing) // compute state only when snapshot updated manager.stateBy("global")!.value = "Sergey Malenkov" + manager.prepareSnapshot() Assert.equal(1, manager.updateSnapshot()) assertModifiedState(result, "<= Sergey Malenkov =>") assertStringsAndCleanup(computing, "main ; name") @@ -338,11 +354,13 @@ suite("State", () => { Assert.isEmpty(computing) // compute state only when snapshot updated manager.callCallbacks() + manager.prepareSnapshot() Assert.equal(1, manager.updateSnapshot()) assertModifiedState(result, "<= 2 =>") assertStringsAndCleanup(computing, "main ; name") // compute state on next snapshot update manager.callCallbacks() + manager.prepareSnapshot() Assert.equal(1, manager.updateSnapshot()) assertModifiedState(result, "<= 3 =>") assertStringsAndCleanup(computing, "main ; name") @@ -374,11 +392,13 @@ suite("State", () => { Assert.isEmpty(computing) // compute state only when snapshot updated manager.callCallbacks() + manager.prepareSnapshot() Assert.equal(1, manager.updateSnapshot()) assertModifiedState(result, "<= 2 =>") assertStringsAndCleanup(computing, "main ; name") // compute state on next snapshot update manager.callCallbacks() + manager.prepareSnapshot() Assert.equal(1, manager.updateSnapshot()) assertModifiedState(result, "<= 3 =>") assertStringsAndCleanup(computing, "main ; name") @@ -432,11 +452,13 @@ suite("State", () => { assertStringsAndCleanup(computing, "compute:value ; compute:condition ; compute:true ; compute:inner:true") // condition changed from true to false condition.value = false + manager.prepareSnapshot() Assert.equal(1, manager.updateSnapshot()) Assert.equal(result.value, "value is false") assertStringsAndCleanup(computing, "compute:condition ; compute:false ; compute:inner:false ; cleanup:true ; cleanup:inner:true") // condition changed from false to true condition.value = true + manager.prepareSnapshot() Assert.equal(1, manager.updateSnapshot()) Assert.equal(result.value, "value is true") assertStringsAndCleanup(computing, "compute:condition ; compute:true ; compute:inner:true ; cleanup:false ; cleanup:inner:false") @@ -601,6 +623,7 @@ suite("State", () => { assertState(computable, "NAME") // do not update result when snapshot updated mutable.value = "Sergey Malenkov" + manager.prepareSnapshot() Assert.equal(1, manager.updateSnapshot()) assertState(computable, "NAME") }) @@ -617,17 +640,20 @@ suite("State", () => { // ensure that initial state is managed state.value = -10 assertState(initial, -1) + manager.prepareSnapshot() Assert.equal(1, manager.updateSnapshot()) assertModifiedState(state, -10) assertModifiedState(initial, -10) // dispose first inner scope Assert.strictEqual(computable.value, initial) selector.value = false + manager.prepareSnapshot() Assert.equal(1, manager.updateSnapshot()) Assert.notStrictEqual(computable.value, initial) // ensure that initial state is not managed assertState(initial, -10) state.value = -100 + manager.prepareSnapshot() Assert.equal(1, manager.updateSnapshot()) assertState(initial, -10) }) @@ -644,11 +670,13 @@ suite("State", () => { // ensure that initial state is managed initial.value = -10 assertState(initial, -1) + manager.prepareSnapshot() Assert.equal(1, manager.updateSnapshot()) assertModifiedState(initial, -10) // dispose first inner scope Assert.strictEqual(computable.value, initial) selector.value = false + manager.prepareSnapshot() Assert.equal(1, manager.updateSnapshot()) Assert.notStrictEqual(computable.value, initial) // ensure that initial state is not managed @@ -669,11 +697,13 @@ suite("State", () => { // ensure that initial state is managed initial.value = -10 assertState(initial, -1) + manager.prepareSnapshot() Assert.equal(1, manager.updateSnapshot()) assertModifiedState(initial, -10) // dispose first inner scope Assert.strictEqual(computable.value, initial) selector.value = false + manager.prepareSnapshot() Assert.equal(1, manager.updateSnapshot()) Assert.notStrictEqual(computable.value, initial) // ensure that initial state is not managed @@ -693,9 +723,11 @@ suite("State", () => { context.compute(key("b"), increment) + context.compute(key("c"), increment)) Assert.equal(computable.value, 0) + manager.prepareSnapshot() Assert.equal(0, manager.updateSnapshot()) Assert.equal(computable.value, 0) state.value++ + manager.prepareSnapshot() Assert.equal(1, manager.updateSnapshot()) Assert.equal(computable.value, 3) }) @@ -712,8 +744,10 @@ suite("State", () => { const globalState = computable.value Assert.equal("GlobalState=0", globalState.toString()) globalState.value = 1 + manager.prepareSnapshot() manager.updateSnapshot() Assert.equal("GlobalState,modified=1", globalState.toString()) + manager.prepareSnapshot() manager.updateSnapshot() computable.dispose() Assert.equal("GlobalState=1", globalState.toString()) @@ -726,8 +760,10 @@ suite("State", () => { const localState = computable.value Assert.equal("LocalState=0", localState.toString()) localState.value = 1 + manager.prepareSnapshot() manager.updateSnapshot() Assert.equal("LocalState,modified=1", localState.toString()) + manager.prepareSnapshot() manager.updateSnapshot() computable.dispose() Assert.equal("LocalState,disposed=1", localState.toString()) @@ -747,8 +783,10 @@ suite("State", () => { Assert.equal(globalState, manager.stateBy("global")!) Assert.equal("GlobalState(global)=0", globalState.toString()) globalState.value = 1 + manager.prepareSnapshot() manager.updateSnapshot() Assert.equal("GlobalState(global),modified=1", globalState.toString()) + manager.prepareSnapshot() manager.updateSnapshot() computable.dispose() Assert.equal("GlobalState(global)=1", globalState.toString()) @@ -772,8 +810,10 @@ suite("State", () => { Assert.isUndefined>(manager.stateBy("local")) Assert.equal("LocalState(local)=0", localState.toString()) localState.value = 1 + manager.prepareSnapshot() manager.updateSnapshot() Assert.equal("LocalState(local),modified=1", localState.toString()) + manager.prepareSnapshot() manager.updateSnapshot() computable.dispose() Assert.equal("LocalState(local),disposed=1", localState.toString()) @@ -829,6 +869,7 @@ suite("State", () => { assertStringsAndCleanup(computing, "computable ; name") // compute state only when snapshot updated state.value = -1 + manager.prepareSnapshot() Assert.equal(1, manager.updateSnapshot()) assertState(computable, 0) Assert.isEmpty(computing) @@ -846,6 +887,7 @@ suite("State", () => { stateCounter++ return computable.value }) + manager.prepareSnapshot() Assert.equal(manager.updateSnapshot(), 0) Assert.equal(computableCounter, 0) // computable is not recomputed Assert.equal(stateCounter, 0) // state is not recomputed @@ -853,6 +895,7 @@ suite("State", () => { Assert.equal(computableCounter, 1) // computable is recomputed Assert.equal(stateCounter, 1) // state is recomputed mutable.value = true + manager.prepareSnapshot() Assert.equal(manager.updateSnapshot(), 2) Assert.equal(computableCounter, 2) // computable is recomputed automatically Assert.equal(stateCounter, 1) // state is not recomputed @@ -873,6 +916,7 @@ suite("State", () => { stateCounter++ return computable.value }) + manager.prepareSnapshot() Assert.equal(manager.updateSnapshot(), 0) Assert.equal(computableCounter, 0) // computable is not recomputed Assert.equal(stateCounter, 0) // state is not recomputed @@ -880,6 +924,7 @@ suite("State", () => { Assert.equal(computableCounter, 1) // computable is recomputed Assert.equal(stateCounter, 1) // state is recomputed mutable.value = true + manager.prepareSnapshot() Assert.equal(manager.updateSnapshot(), 1) Assert.equal(computableCounter, 2) // computable is recomputed automatically Assert.equal(stateCounter, 1) // state is not recomputed @@ -900,6 +945,7 @@ suite("State", () => { stateCounter++ return computable.value && false }) + manager.prepareSnapshot() Assert.equal(manager.updateSnapshot(), 0) Assert.equal(computableCounter, 0) // computable is not recomputed Assert.equal(stateCounter, 0) // state is not recomputed @@ -907,6 +953,7 @@ suite("State", () => { Assert.equal(computableCounter, 1) // computable is recomputed Assert.equal(stateCounter, 1) // state is recomputed mutable.value = true + manager.prepareSnapshot() Assert.equal(manager.updateSnapshot(), 2) Assert.equal(computableCounter, 2) // computable is recomputed automatically Assert.equal(stateCounter, 1) // state is not recomputed @@ -978,6 +1025,7 @@ suite("State", () => { assertStringsAndCleanup(computing, "update root ; init root ; create first ; update first ; init first ; create second ; update second ; init second") + manager.prepareSnapshot() Assert.equal(0, manager.updateSnapshot()) assertNode(root, "root\n" + @@ -987,6 +1035,7 @@ suite("State", () => { Assert.isEmpty(computing) count.value = 20 + manager.prepareSnapshot() Assert.equal(1, manager.updateSnapshot()) assertNode(root, "root\n" + @@ -995,6 +1044,7 @@ suite("State", () => { assertStringsAndCleanup(computing, "update root ; detach&dispose second") second.value = "last node" + manager.prepareSnapshot() Assert.equal(1, manager.updateSnapshot()) assertNode(root, "root\n" + @@ -1003,6 +1053,7 @@ suite("State", () => { Assert.isEmpty(computing) count.value = 40 + manager.prepareSnapshot() Assert.equal(1, manager.updateSnapshot()) assertNode(root, "root\n" + @@ -1011,6 +1062,7 @@ suite("State", () => { assertStringsAndCleanup(computing, "update root ; create second ; update second ; init second ; detach&dispose first") count.value = 30 + manager.prepareSnapshot() Assert.equal(1, manager.updateSnapshot()) assertNode(root, "root\n" + @@ -1020,6 +1072,7 @@ suite("State", () => { assertStringsAndCleanup(computing, "update root ; create first ; update first ; init first") count.value = 40 + manager.prepareSnapshot() Assert.equal(1, manager.updateSnapshot()) assertNode(root, "root\n" + @@ -1028,6 +1081,7 @@ suite("State", () => { assertStringsAndCleanup(computing, "update root ; detach&dispose first") second.value = "second node" + manager.prepareSnapshot() Assert.equal(1, manager.updateSnapshot()) assertNode(root, "root\n" + @@ -1120,6 +1174,7 @@ suite("State", () => { assertStringsAndCleanup(computing, "update root ; init root ; create parent ; update parent ; init parent ; create child ; update child ; init child ; create leaf ; update leaf ; init leaf") + manager.prepareSnapshot() Assert.equal(0, manager.updateSnapshot()) assertNode(root, "root\n" + @@ -1130,6 +1185,7 @@ suite("State", () => { Assert.isEmpty(computing) manager.stateBy("leaf")!.value = "leaf node" + manager.prepareSnapshot() Assert.equal(1, manager.updateSnapshot()) assertNode(root, "root\n" + @@ -1140,6 +1196,7 @@ suite("State", () => { assertStringsAndCleanup(computing, "update root ; update parent ; update child ; update leaf ; init leaf") manager.stateBy("parent")!.value = "" + manager.prepareSnapshot() Assert.equal(1, manager.updateSnapshot()) assertNode(root, "root") @@ -1147,6 +1204,7 @@ suite("State", () => { assertStringsAndCleanup(computing, "update root ; detach&dispose parent ; detach&dispose child ; detach&dispose leaf") manager.stateBy("child")!.value = "child node" + manager.prepareSnapshot() Assert.equal(1, manager.updateSnapshot()) assertNode(root, "root") @@ -1154,6 +1212,7 @@ suite("State", () => { Assert.isEmpty(computing) manager.stateBy("parent")!.value = "parent node" + manager.prepareSnapshot() Assert.equal(1, manager.updateSnapshot()) assertNode(root, "root\n" + @@ -1164,6 +1223,7 @@ suite("State", () => { assertStringsAndCleanup(computing, "update root ; create parent ; update parent ; init parent ; create child ; update child ; init child ; create leaf ; update leaf ; init leaf") manager.stateBy("leaf")!.value = "" + manager.prepareSnapshot() Assert.equal(1, manager.updateSnapshot()) assertNode(root, "root\n" + @@ -1179,6 +1239,7 @@ suite("State", () => { assertState(state, 0) // first frame manager.callCallbacks() + manager.prepareSnapshot() Assert.equal(1, manager.updateSnapshot()) assertModifiedState(state, 10) }) @@ -1189,10 +1250,12 @@ suite("State", () => { assertState(state, 0) // first frame manager.callCallbacks() + manager.prepareSnapshot() Assert.equal(0, manager.updateSnapshot()) assertState(state, 0) // second frame manager.callCallbacks() + manager.prepareSnapshot() Assert.equal(1, manager.updateSnapshot()) assertModifiedState(state, 10) }) @@ -1203,14 +1266,17 @@ suite("State", () => { assertState(state, 0) // first frame manager.callCallbacks() + manager.prepareSnapshot() Assert.equal(0, manager.updateSnapshot()) assertState(state, 0) // second frame manager.callCallbacks() + manager.prepareSnapshot() Assert.equal(0, manager.updateSnapshot()) assertState(state, 0) // third frame manager.callCallbacks() + manager.prepareSnapshot() Assert.equal(1, manager.updateSnapshot()) assertModifiedState(state, 10) }) @@ -1237,6 +1303,7 @@ suite("Equivalent", () => { const manager = createStateManager() const state = manager.mutableState(age16, undefined, Data.equivalent) assertState(state, age16) + manager.prepareSnapshot() Assert.equal(0, manager.updateSnapshot()) assertState(state, age16) }) @@ -1247,6 +1314,7 @@ suite("Equivalent", () => { const age64 = new Data("age", 64) Assert.notEqual(age16, age64) state.value = age64 + manager.prepareSnapshot() Assert.equal(1, manager.updateSnapshot()) assertModifiedState(state, age64) Assert.notEqual(state.value, age16) @@ -1257,9 +1325,11 @@ suite("Equivalent", () => { assertState(state, age16) const age64 = new Data("age", 64) state.value = age64 + manager.prepareSnapshot() Assert.equal(1, manager.updateSnapshot()) assertModifiedState(state, age64) Assert.notEqual(state.value, age16) + manager.prepareSnapshot() Assert.equal(0, manager.updateSnapshot()) assertState(state, age64) }) @@ -1271,6 +1341,7 @@ suite("Equivalent", () => { Assert.notEqual(equal, age16) Assert.isTrue(Data.equivalent(equal, age16)) state.value = equal + manager.prepareSnapshot() Assert.equal(0, manager.updateSnapshot()) assertState(state, equal) Assert.notEqual(state.value, age16) @@ -1284,6 +1355,7 @@ suite("Equivalent", () => { Assert.notEqual(equal, age16) Assert.isTrue(Data.equivalent(equal, age16)) state.value = equal + manager.prepareSnapshot() Assert.equal(0, manager.updateSnapshot()) assertState(state, age16) Assert.notEqual(state.value, equal) @@ -1296,6 +1368,7 @@ suite("Equivalent", () => { for (let age = 0; age <= 16; age++) { state.value = value = new Data("age", age) } + manager.prepareSnapshot() Assert.equal(0, manager.updateSnapshot()) assertState(state, value) }) @@ -1307,6 +1380,7 @@ suite("Equivalent", () => { for (let age = 0; age <= 16; age++) { state.value = new Data("age", age) } + manager.prepareSnapshot() Assert.equal(0, manager.updateSnapshot()) assertState(state, age16) }) @@ -1348,6 +1422,7 @@ suite("ValueTracker", () => { })) assertState(state, 5) Assert.throws(() => { state.value = 404 }) + manager.prepareSnapshot() Assert.equal(0, manager.updateSnapshot()) assertState(state, 5) }) @@ -1356,6 +1431,7 @@ suite("ValueTracker", () => { const state = manager.mutableState(5, undefined, undefined, onUpdate((_: int32) => 5)) assertState(state, 5) state.value = 404 + manager.prepareSnapshot() Assert.equal(0, manager.updateSnapshot()) assertState(state, 5) }) @@ -1364,6 +1440,7 @@ suite("ValueTracker", () => { const state = manager.mutableState(5, undefined, undefined, onUpdate((_: int32) => 5)) assertState(state, 5) state.value = 404 + manager.prepareSnapshot() Assert.equal(0, manager.updateSnapshot()) assertState(state, 5) state.dispose() diff --git a/incremental/runtime/test/states/State.test.ts b/incremental/runtime/test/states/State.test.ts index 36f2beedb..0485309d9 100644 --- a/incremental/runtime/test/states/State.test.ts +++ b/incremental/runtime/test/states/State.test.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023 Huawei Device Co., Ltd. + * Copyright (c) 2022-2024 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -73,6 +73,7 @@ suite("State", () => { let manager = createStateManager() let state = manager.mutableState(200) state.value = 404 + manager.prepareSnapshot() assert.equal(1, manager.updateSnapshot()) assertModifiedState(state, 404) }) @@ -80,7 +81,9 @@ suite("State", () => { let manager = createStateManager() let state = manager.mutableState(200) state.value = 404 + manager.prepareSnapshot() assert.equal(1, manager.updateSnapshot()) + manager.prepareSnapshot() assert.equal(0, manager.updateSnapshot()) assertState(state, 404) }) @@ -90,6 +93,7 @@ suite("State", () => { for (let index = 0; index <= 200; index++) { state.value = index } + manager.prepareSnapshot() assert.equal(0, manager.updateSnapshot()) assertState(state, 200) }) @@ -99,9 +103,9 @@ suite("State", () => { assert.equal(state, manager.stateBy("named")!) assert.isDefined>(manager.stateBy("named")!) state.dispose() - assert.isUndefined>(manager.stateBy("named")!) + assert.isUndefined(manager.stateBy("named")!) state.dispose() - assert.isUndefined>(manager.stateBy("named")!) + assert.isUndefined(manager.stateBy("named")!) }) test("managed named state is not modified immediately", () => { let manager = createStateManager() @@ -115,6 +119,7 @@ suite("State", () => { let manager = createStateManager() manager.namedState("named", () => 200) manager.stateBy("named")!.value = 404 + manager.prepareSnapshot() assert.equal(1, manager.updateSnapshot()) assert.equal(manager.valueBy("named"), 404) assert.isTrue(manager.stateBy("named")?.modified) @@ -123,7 +128,9 @@ suite("State", () => { let manager = createStateManager() manager.namedState("named", () => 200) manager.stateBy("named")!.value = 404 + manager.prepareSnapshot() assert.equal(1, manager.updateSnapshot()) + manager.prepareSnapshot() assert.equal(0, manager.updateSnapshot()) assert.equal(manager.valueBy("named"), 404) assert.isFalse(manager.stateBy("named")?.modified) @@ -134,6 +141,7 @@ suite("State", () => { for (let index = 0; index <= 200; index++) { manager.stateBy("named")!.value = index } + manager.prepareSnapshot() assert.equal(0, manager.updateSnapshot()) assert.equal(manager.valueBy("named"), 200) assert.isFalse(manager.stateBy("named")?.modified) @@ -179,12 +187,14 @@ suite("State", () => { updatableNode.value // updatable node is already computed after accessing assert.isFalse(updatableNode.recomputeNeeded) + manager.prepareSnapshot() manager.updateSnapshot() // updatable node is already computed because nothing is changed assert.isFalse(updatableNode.recomputeNeeded) mutableState.value = !mutableState.value // updatable node does not know that the mutable state is changed assert.isFalse(updatableNode.recomputeNeeded) + manager.prepareSnapshot() manager.updateSnapshot() // updatable node needs to be recomputed because the mutable state is changed assert.isTrue(updatableNode.recomputeNeeded) @@ -213,12 +223,14 @@ suite("State", () => { computableState.value // computable state is already computed after accessing assert.isFalse(computableState.recomputeNeeded) + manager.prepareSnapshot() manager.updateSnapshot() // computable state is already computed because nothing is changed assert.isFalse(computableState.recomputeNeeded) mutableState.value = !mutableState.value // computable state does not know that the mutable state is changed assert.isFalse(computableState.recomputeNeeded) + manager.prepareSnapshot() manager.updateSnapshot() // computable state needs to be recomputed because the mutable state is changed assert.isTrue(computableState.recomputeNeeded) @@ -255,11 +267,13 @@ suite("State", () => { assertState(result, "<= NAME =>") assertStringsAndCleanup(computing, "main ; left ; name ; right") // computable state is not modified + manager.prepareSnapshot() assert.equal(0, manager.updateSnapshot()) assertState(result, "<= NAME =>") assert.isEmpty(computing) // compute state only when snapshot updated name.value = "Sergey Malenkov" + manager.prepareSnapshot() assert.equal(1, manager.updateSnapshot()) assertModifiedState(result, "<= Sergey Malenkov =>") assertStringsAndCleanup(computing, "main ; name") @@ -287,11 +301,13 @@ suite("State", () => { assertState(result, "<= NAME =>") assertStringsAndCleanup(computing, "main ; left ; name ; right") // computable state is not modified + manager.prepareSnapshot() assert.equal(0, manager.updateSnapshot()) assertState(result, "<= NAME =>") assert.isEmpty(computing) // compute state only when snapshot updated manager.stateBy("global")!.value = "Sergey Malenkov" + manager.prepareSnapshot() assert.equal(1, manager.updateSnapshot()) assertModifiedState(result, "<= Sergey Malenkov =>") assertStringsAndCleanup(computing, "main ; name") @@ -323,11 +339,13 @@ suite("State", () => { assert.isEmpty(computing) // compute state only when snapshot updated manager.callCallbacks() + manager.prepareSnapshot() assert.equal(1, manager.updateSnapshot()) assertModifiedState(result, "<= 2 =>") assertStringsAndCleanup(computing, "main ; name") // compute state on next snapshot update manager.callCallbacks() + manager.prepareSnapshot() assert.equal(1, manager.updateSnapshot()) assertModifiedState(result, "<= 3 =>") assertStringsAndCleanup(computing, "main ; name") @@ -359,11 +377,13 @@ suite("State", () => { assert.isEmpty(computing) // compute state only when snapshot updated manager.callCallbacks() + manager.prepareSnapshot() assert.equal(1, manager.updateSnapshot()) assertModifiedState(result, "<= 2 =>") assertStringsAndCleanup(computing, "main ; name") // compute state on next snapshot update manager.callCallbacks() + manager.prepareSnapshot() assert.equal(1, manager.updateSnapshot()) assertModifiedState(result, "<= 3 =>") assertStringsAndCleanup(computing, "main ; name") @@ -388,7 +408,7 @@ suite("State", () => { computing.push("compute:inner:true") return "true" }, old => { - assert.isUndefined>(context.stateBy("false")!) + assert.isUndefined(context.stateBy("false")!) assert.isDefined>(context.stateBy("true")!) assert.isTrue(context.valueBy("true")) assert.equal(old, "true") @@ -402,7 +422,7 @@ suite("State", () => { computing.push("compute:inner:false") return "false" }, old => { - assert.isUndefined>(context.stateBy("true")!) + assert.isUndefined(context.stateBy("true")!) assert.isDefined>(context.stateBy("false")!) assert.isFalse(context.valueBy("false")) assert.equal(old, "false") @@ -417,11 +437,13 @@ suite("State", () => { assertStringsAndCleanup(computing, "compute:value ; compute:condition ; compute:true ; compute:inner:true") // condition changed from true to false condition.value = false + manager.prepareSnapshot() assert.equal(1, manager.updateSnapshot()) assert.equal(result.value, "value is false") assertStringsAndCleanup(computing, "compute:condition ; compute:false ; compute:inner:false ; cleanup:true ; cleanup:inner:true") // condition changed from false to true condition.value = true + manager.prepareSnapshot() assert.equal(1, manager.updateSnapshot()) assert.equal(result.value, "value is true") assertStringsAndCleanup(computing, "compute:condition ; compute:true ; compute:inner:true ; cleanup:false ; cleanup:inner:false") @@ -450,6 +472,7 @@ suite("State", () => { assertStringsAndCleanup(computing, "compute:condition ; compute:second") // condition changed from true to false condition.value = false + manager.prepareSnapshot() assert.equal(1, manager.updateSnapshot()) assert.equal(result.value, "first & second") assertStringsAndCleanup(computing, "compute:condition ; compute:first") @@ -478,6 +501,7 @@ suite("State", () => { assertStringsAndCleanup(computing, "compute:condition ; compute:first ; compute:second") // condition changed from false to true condition.value = true + manager.prepareSnapshot() assert.equal(1, manager.updateSnapshot()) assert.equal(result.value, "only second") assertStringsAndCleanup(computing, "compute:condition") @@ -633,6 +657,7 @@ suite("State", () => { assertState(computable, "NAME") // do not update result when snapshot updated mutable.value = "Sergey Malenkov" + manager.prepareSnapshot() assert.equal(1, manager.updateSnapshot()) assertState(computable, "NAME") }) @@ -649,17 +674,20 @@ suite("State", () => { // ensure that initial state is managed state.value = -10 assertState(initial, -1) + manager.prepareSnapshot() assert.equal(1, manager.updateSnapshot()) assertModifiedState(state, -10) assertModifiedState(initial, -10) // dispose first inner scope assert.strictEqual(computable.value, initial) selector.value = false + manager.prepareSnapshot() assert.equal(1, manager.updateSnapshot()) assert.notStrictEqual(computable.value, initial) // ensure that initial state is not managed assertState(initial, -10) state.value = -100 + manager.prepareSnapshot() assert.equal(1, manager.updateSnapshot()) assertState(initial, -10) }) @@ -676,11 +704,13 @@ suite("State", () => { // ensure that initial state is managed initial.value = -10 assertState(initial, -1) + manager.prepareSnapshot() assert.equal(1, manager.updateSnapshot()) assertModifiedState(initial, -10) // dispose first inner scope assert.strictEqual(computable.value, initial) selector.value = false + manager.prepareSnapshot() assert.equal(1, manager.updateSnapshot()) assert.notStrictEqual(computable.value, initial) // ensure that initial state is not managed @@ -701,11 +731,13 @@ suite("State", () => { // ensure that initial state is managed initial.value = -10 assertState(initial, -1) + manager.prepareSnapshot() assert.equal(1, manager.updateSnapshot()) assertModifiedState(initial, -10) // dispose first inner scope assert.strictEqual(computable.value, initial) selector.value = false + manager.prepareSnapshot() assert.equal(1, manager.updateSnapshot()) assert.notStrictEqual(computable.value, initial) // ensure that initial state is not managed @@ -725,9 +757,11 @@ suite("State", () => { context.compute(key("b"), increment) + context.compute(key("c"), increment)) assert.equal(computable.value, 0) + manager.prepareSnapshot() assert.equal(0, manager.updateSnapshot()) assert.equal(computable.value, 0) state.value++ + manager.prepareSnapshot() assert.equal(1, manager.updateSnapshot()) assert.equal(computable.value, 3) }) @@ -744,8 +778,10 @@ suite("State", () => { const globalState = computable.value assert.equal("GlobalState=0", globalState.toString()) globalState.value = 1 + manager.prepareSnapshot() manager.updateSnapshot() assert.equal("GlobalState,modified=1", globalState.toString()) + manager.prepareSnapshot() manager.updateSnapshot() computable.dispose() assert.equal("GlobalState=1", globalState.toString()) @@ -758,8 +794,10 @@ suite("State", () => { const localState = computable.value assert.equal("LocalState=0", localState.toString()) localState.value = 1 + manager.prepareSnapshot() manager.updateSnapshot() assert.equal("LocalState,modified=1", localState.toString()) + manager.prepareSnapshot() manager.updateSnapshot() computable.dispose() assert.equal("LocalState,disposed=1", localState.toString()) @@ -772,15 +810,17 @@ suite("State", () => { const state = context.namedState("global", () => 0, true) assert.equal(state, manager.stateBy("global")!) assert.isDefined>(manager.stateBy("global", true)!) - assert.isUndefined>(manager.stateBy("global", false)!) + assert.isUndefined(manager.stateBy("global", false)!) return state }) const globalState = computable.value assert.equal(globalState, manager.stateBy("global")) assert.equal("GlobalState(global)=0", globalState.toString()) globalState.value = 1 + manager.prepareSnapshot() manager.updateSnapshot() assert.equal("GlobalState(global),modified=1", globalState.toString()) + manager.prepareSnapshot() manager.updateSnapshot() computable.dispose() assert.equal("GlobalState(global)=1", globalState.toString()) @@ -793,19 +833,21 @@ suite("State", () => { const computable = manager.computableState>(context => { assert.equal(globalState, manager.stateBy("global")!) assert.isDefined>(manager.stateBy("global", true)!) - assert.isUndefined>(manager.stateBy("global", false)!) + assert.isUndefined(manager.stateBy("global", false)!) const state = context.namedState("local", () => 0, false) assert.equal(state, manager.stateBy("local")!) - assert.isUndefined>(manager.stateBy("local", true)!) + assert.isUndefined(manager.stateBy("local", true)!) assert.isDefined>(manager.stateBy("local", false)!) return state }) const localState = computable.value - assert.isUndefined>(manager.stateBy("local")!) + assert.isUndefined(manager.stateBy("local")!) assert.equal("LocalState(local)=0", localState.toString()) localState.value = 1 + manager.prepareSnapshot() manager.updateSnapshot() assert.equal("LocalState(local),modified=1", localState.toString()) + manager.prepareSnapshot() manager.updateSnapshot() computable.dispose() assert.equal("LocalState(local),disposed=1", localState.toString()) @@ -857,6 +899,7 @@ suite("State", () => { assertStringsAndCleanup(computing, "computable ; name") // compute state only when snapshot updated state.value = -1 + manager.prepareSnapshot() assert.equal(1, manager.updateSnapshot()) assertState(computable, 0) assert.isEmpty(computing) @@ -874,6 +917,7 @@ suite("State", () => { stateCounter++ return computable.value }) + manager.prepareSnapshot() assert.equal(manager.updateSnapshot(), 0) assert.equal(computableCounter, 0) // computable is not recomputed assert.equal(stateCounter, 0) // state is not recomputed @@ -881,6 +925,7 @@ suite("State", () => { assert.equal(computableCounter, 1) // computable is recomputed assert.equal(stateCounter, 1) // state is recomputed mutable.value = true + manager.prepareSnapshot() assert.equal(manager.updateSnapshot(), 2) assert.equal(computableCounter, 2) // computable is recomputed automatically assert.equal(stateCounter, 1) // state is not recomputed @@ -901,6 +946,7 @@ suite("State", () => { stateCounter++ return computable.value }) + manager.prepareSnapshot() assert.equal(manager.updateSnapshot(), 0) assert.equal(computableCounter, 0) // computable is not recomputed assert.equal(stateCounter, 0) // state is not recomputed @@ -908,6 +954,7 @@ suite("State", () => { assert.equal(computableCounter, 1) // computable is recomputed assert.equal(stateCounter, 1) // state is recomputed mutable.value = true + manager.prepareSnapshot() assert.equal(manager.updateSnapshot(), 1) assert.equal(computableCounter, 2) // computable is recomputed automatically assert.equal(stateCounter, 1) // state is not recomputed @@ -928,6 +975,7 @@ suite("State", () => { stateCounter++ return computable.value && false }) + manager.prepareSnapshot() assert.equal(manager.updateSnapshot(), 0) assert.equal(computableCounter, 0) // computable is not recomputed assert.equal(stateCounter, 0) // state is not recomputed @@ -935,6 +983,7 @@ suite("State", () => { assert.equal(computableCounter, 1) // computable is recomputed assert.equal(stateCounter, 1) // state is recomputed mutable.value = true + manager.prepareSnapshot() assert.equal(manager.updateSnapshot(), 2) assert.equal(computableCounter, 2) // computable is recomputed automatically assert.equal(stateCounter, 1) // state is not recomputed @@ -1003,6 +1052,7 @@ suite("State", () => { assertStringsAndCleanup(computing, "update root ; init root ; create first ; update first ; init first ; create second ; update second ; init second") + manager.prepareSnapshot() assert.equal(0, manager.updateSnapshot()) assertNode(root, "root\n" + @@ -1012,6 +1062,7 @@ suite("State", () => { assert.isEmpty(computing) count.value = 20 + manager.prepareSnapshot() assert.equal(1, manager.updateSnapshot()) assertNode(root, "root\n" + @@ -1020,6 +1071,7 @@ suite("State", () => { assertStringsAndCleanup(computing, "update root ; detach&dispose second") second.value = "last node" + manager.prepareSnapshot() assert.equal(1, manager.updateSnapshot()) assertNode(root, "root\n" + @@ -1028,6 +1080,7 @@ suite("State", () => { assert.isEmpty(computing) count.value = 40 + manager.prepareSnapshot() assert.equal(1, manager.updateSnapshot()) assertNode(root, "root\n" + @@ -1036,6 +1089,7 @@ suite("State", () => { assertStringsAndCleanup(computing, "update root ; create second ; update second ; init second ; detach&dispose first") count.value = 30 + manager.prepareSnapshot() assert.equal(1, manager.updateSnapshot()) assertNode(root, "root\n" + @@ -1045,6 +1099,7 @@ suite("State", () => { assertStringsAndCleanup(computing, "update root ; create first ; update first ; init first") count.value = 40 + manager.prepareSnapshot() assert.equal(1, manager.updateSnapshot()) assertNode(root, "root\n" + @@ -1053,6 +1108,7 @@ suite("State", () => { assertStringsAndCleanup(computing, "update root ; detach&dispose first") second.value = "second node" + manager.prepareSnapshot() assert.equal(1, manager.updateSnapshot()) assertNode(root, "root\n" + @@ -1141,6 +1197,7 @@ suite("State", () => { assertStringsAndCleanup(computing, "update root ; init root ; create parent ; update parent ; init parent ; create child ; update child ; init child ; create leaf ; update leaf ; init leaf") + manager.prepareSnapshot() assert.equal(0, manager.updateSnapshot()) assertNode(root, "root\n" + @@ -1151,6 +1208,7 @@ suite("State", () => { assert.isEmpty(computing) manager.stateBy("leaf")!.value = "leaf node" + manager.prepareSnapshot() assert.equal(1, manager.updateSnapshot()) assertNode(root, "root\n" + @@ -1161,6 +1219,7 @@ suite("State", () => { assertStringsAndCleanup(computing, "update root ; update parent ; update child ; update leaf ; init leaf") manager.stateBy("parent")!.value = "" + manager.prepareSnapshot() assert.equal(1, manager.updateSnapshot()) assertNode(root, "root") @@ -1168,6 +1227,7 @@ suite("State", () => { assertStringsAndCleanup(computing, "update root ; detach&dispose parent ; detach&dispose child ; detach&dispose leaf") manager.stateBy("child")!.value = "child node" + manager.prepareSnapshot() assert.equal(1, manager.updateSnapshot()) assertNode(root, "root") @@ -1175,6 +1235,7 @@ suite("State", () => { assert.isEmpty(computing) manager.stateBy("parent")!.value = "parent node" + manager.prepareSnapshot() assert.equal(1, manager.updateSnapshot()) assertNode(root, "root\n" + @@ -1185,6 +1246,7 @@ suite("State", () => { assertStringsAndCleanup(computing, "update root ; create parent ; update parent ; init parent ; create child ; update child ; init child ; create leaf ; update leaf ; init leaf") manager.stateBy("leaf")!.value = "" + manager.prepareSnapshot() assert.equal(1, manager.updateSnapshot()) assertNode(root, "root\n" + @@ -1200,6 +1262,7 @@ suite("State", () => { assertState(state, 0) // first frame manager.callCallbacks() + manager.prepareSnapshot() assert.equal(1, manager.updateSnapshot()) assertModifiedState(state, 10) }) @@ -1210,10 +1273,12 @@ suite("State", () => { assertState(state, 0) // first frame manager.callCallbacks() + manager.prepareSnapshot() assert.equal(0, manager.updateSnapshot()) assertState(state, 0) // second frame manager.callCallbacks() + manager.prepareSnapshot() assert.equal(1, manager.updateSnapshot()) assertModifiedState(state, 10) }) @@ -1224,14 +1289,17 @@ suite("State", () => { assertState(state, 0) // first frame manager.callCallbacks() + manager.prepareSnapshot() assert.equal(0, manager.updateSnapshot()) assertState(state, 0) // second frame manager.callCallbacks() + manager.prepareSnapshot() assert.equal(0, manager.updateSnapshot()) assertState(state, 0) // third frame manager.callCallbacks() + manager.prepareSnapshot() assert.equal(1, manager.updateSnapshot()) assertModifiedState(state, 10) }) @@ -1258,6 +1326,7 @@ suite("Equivalent", () => { const manager = createStateManager() const state = manager.mutableState(age16, undefined, Data.equivalent) assertState(state, age16) + manager.prepareSnapshot() assert.equal(0, manager.updateSnapshot()) assertState(state, age16) }) @@ -1268,6 +1337,7 @@ suite("Equivalent", () => { const age64 = new Data("age", 64) assert.notEqual(age16, age64) state.value = age64 + manager.prepareSnapshot() assert.equal(1, manager.updateSnapshot()) assertModifiedState(state, age64) assert.notEqual(state.value, age16) @@ -1278,9 +1348,11 @@ suite("Equivalent", () => { assertState(state, age16) const age64 = new Data("age", 64) state.value = age64 + manager.prepareSnapshot() assert.equal(1, manager.updateSnapshot()) assertModifiedState(state, age64) assert.notEqual(state.value, age16) + manager.prepareSnapshot() assert.equal(0, manager.updateSnapshot()) assertState(state, age64) }) @@ -1292,6 +1364,7 @@ suite("Equivalent", () => { assert.notEqual(equal, age16) assert.isTrue(Data.equivalent(equal, age16)) state.value = equal + manager.prepareSnapshot() assert.equal(0, manager.updateSnapshot()) assertState(state, equal) assert.notEqual(state.value, age16) @@ -1305,6 +1378,7 @@ suite("Equivalent", () => { assert.notEqual(equal, age16) assert.isTrue(Data.equivalent(equal, age16)) state.value = equal + manager.prepareSnapshot() assert.equal(0, manager.updateSnapshot()) assertState(state, age16) assert.notEqual(state.value, equal) @@ -1317,6 +1391,7 @@ suite("Equivalent", () => { for (let age = 0; age <= 16; age++) { state.value = value = new Data("age", age) } + manager.prepareSnapshot() assert.equal(0, manager.updateSnapshot()) assertState(state, value) }) @@ -1328,6 +1403,7 @@ suite("Equivalent", () => { for (let age = 0; age <= 16; age++) { state.value = new Data("age", age) } + manager.prepareSnapshot() assert.equal(0, manager.updateSnapshot()) assertState(state, age16) }) @@ -1359,6 +1435,7 @@ suite("ValueTracker", () => { })) assertState(state, 5) assert.throws(() => state.value = 404) + manager.prepareSnapshot() assert.equal(0, manager.updateSnapshot()) assertState(state, 5) }) @@ -1367,6 +1444,7 @@ suite("ValueTracker", () => { const state = manager.mutableState(5, undefined, undefined, onUpdate(() => 5)) assertState(state, 5) state.value = 404 + manager.prepareSnapshot() assert.equal(0, manager.updateSnapshot()) assertState(state, 5) }) @@ -1375,10 +1453,14 @@ suite("ValueTracker", () => { const state = manager.mutableState(5, undefined, undefined, onUpdate(() => 5)) assertState(state, 5) state.value = 404 + manager.prepareSnapshot() assert.equal(0, manager.updateSnapshot()) assertState(state, 5) + console.log("before:" + state.toString()) state.dispose() + console.log(" after:" + state.toString()) state.value = 999 + console.log(" set:" + state.toString()) assertModifiedState(state, 999) }) }) -- Gitee