返回 DeepSeek-Reasonix
runtime_state_incremental_test.go
根目录 / desktop / runtime_state_incremental_test.go
1 package main
2
3 import (
4 "sync/atomic"
5 "testing"
6
7 "reasonix/internal/event"
8 )
9
10 type countedRuntimeReader struct {
11 bindingRuntimeReader
12 reads atomic.Int32
13 }
14
15 func (r *countedRuntimeReader) RuntimeStateSnapshot() event.RuntimeStateSnapshot {
16 r.reads.Add(1)
17 return r.bindingRuntimeReader.RuntimeStateSnapshot()
18 }
19
20 func TestRuntimeStateUpdateDoesNotResampleUnchangedBindings(t *testing.T) {
21 isolateDesktopUserDirs(t)
22 first, second := &countedRuntimeReader{}, &countedRuntimeReader{}
23 first.state = event.RuntimeStateSnapshot{SchemaVersion: 1, RuntimeEpoch: "first", Revision: 1}
24 second.state = event.RuntimeStateSnapshot{SchemaVersion: 1, RuntimeEpoch: "second", Revision: 1}
25 a := &WorkspaceTab{ID: "a", Ctrl: first}
26 b := &WorkspaceTab{ID: "b", Ctrl: second}
27 app := &App{tabs: map[string]*WorkspaceTab{"a": a, "b": b}}
28 app.GetRuntimeStateSnapshot()
29 for revision := uint64(2); revision <= 20; revision++ {
30 state := first.state
31 state.Revision = revision
32 app.runtimeStateSnapshotWithUpdate(&localRuntimeUpdate{tab: a, ctrl: first, state: state})
33 }
34 if first.reads.Load() != 1 || second.reads.Load() != 1 {
35 t.Fatal("incremental update sampled unrelated controllers")
36 }
37 app.GetRuntimeStateSnapshot()
38 if second.reads.Load() != 2 {
39 t.Fatal("explicit reconciliation did not sample the owner")
40 }
41 // An older callback must not regress an already-published producer revision.
42 result := app.runtimeStateSnapshotWithUpdate(&localRuntimeUpdate{tab: a, ctrl: first, state: first.state})
43 if result.Sessions[0].State.Revision != 20 {
44 t.Fatal("late callback regressed the projection")
45 }
46 replacement := &countedRuntimeReader{}
47 replacement.state = event.RuntimeStateSnapshot{RuntimeEpoch: "replacement", Revision: 1}
48 app.mu.Lock()
49 b.Ctrl = replacement
50 app.mu.Unlock()
51 app.runtimeStateSnapshotWithUpdate(&localRuntimeUpdate{tab: a, ctrl: first, state: first.state})
52 if replacement.reads.Load() != 1 {
53 t.Fatal("replacement inherited the old binding cache")
54 }
55 }
56
57 func TestRuntimeStateQueuedUpdatesKeepNewestAndFenceRebinding(t *testing.T) {
58 isolateDesktopUserDirs(t)
59 reader := &countedRuntimeReader{}
60 reader.state = event.RuntimeStateSnapshot{RuntimeEpoch: "owner", Revision: 1}
61 tab := &WorkspaceTab{ID: "a", Ctrl: reader}
62 app := &App{tabs: map[string]*WorkspaceTab{"a": tab}}
63 app.GetRuntimeStateSnapshot()
64 // Prevent the worker from sampling while all producers enqueue their cut.
65 app.mu.Lock()
66 for revision := uint64(100); revision > 1; revision-- {
67 state := reader.state
68 state.Revision = revision
69 app.queueRuntimeProjection(localRuntimeUpdate{tab: tab, ctrl: reader, state: state})
70 }
71 app.mu.Unlock()
72 app.flushRuntimeProjections()
73 projection := app.runtimeStateSnapshotWithUpdate(&localRuntimeUpdate{tab: tab, ctrl: reader, state: reader.state})
74 if projection.Sessions[0].State.Revision != 100 || reader.reads.Load() != 1 {
75 t.Fatal("batched callbacks lost the newest revision or resampled the owner")
76 }
77 // A queued callback may outlive a same-controller session rebind.
78 app.mu.Lock()
79 tab.SessionGeneration = 2
80 tab.SessionPath, tab.SessionID = "session:new", "new"
81 app.mu.Unlock()
82 projection = app.runtimeStateSnapshotWithUpdate(&localRuntimeUpdate{tab: tab, ctrl: reader,
83 state: event.RuntimeStateSnapshot{RuntimeEpoch: "obsolete", Revision: 999}})
84 if projection.Sessions[0].State.RuntimeEpoch == "obsolete" || reader.reads.Load() != 2 {
85 t.Fatal("obsolete callback crossed the binding generation fence")
86 }
87 app.shuttingDown.Store(true)
88 app.queueRuntimeProjection(localRuntimeUpdate{tab: tab, ctrl: reader})
89 if app.runtimeStateProjection.events.done != nil {
90 t.Fatal("shutdown admitted another projection worker")
91 }
92 }
93
93 lines GO