X-Git-Url: https://git.giacomolli.uk/onechat/blobdiff_plain/feda522eba3e2a27c91741c77d0d01f1840c0004..871743f6ab20d68dcede1673a9764730fda38308:/view_test.go diff --git a/view_test.go b/view_test.go index 2900860..46f76da 100644 --- a/view_test.go +++ b/view_test.go @@ -8,12 +8,15 @@ import ( "github.com/stretchr/testify/require" ) -func TestViewMergesInTheOrderOfTaelinsPush(t *testing.T) { - tree := builtTree{} +func TestViewMergesInTheOrderOfTheRollbackPush(t *testing.T) { + tree := ViewBuiltTree{} + view := newView(0, 0) - var states *pushEntry + + var states *ViewPushEntry + for tick := range 20001 { - states = push(tick, states) + states = viewPush(tick, states) starts := states.starts() view.high = len(starts) view.low = len(starts) @@ -30,12 +33,14 @@ func TestViewMergesInTheOrderOfTaelinsPush(t *testing.T) { } func TestViewMergesInOneBatchFromHighToLow(t *testing.T) { - tree := builtTree{} + tree := ViewBuiltTree{} + view := newView(10, 5) sizes := make([]int, 0, 20) for range 20 { _, err := view.Append(tree) require.NoError(t, err) + sizes = append(sizes, view.Size()) } @@ -43,8 +48,8 @@ func TestViewMergesInOneBatchFromHighToLow(t *testing.T) { } func TestViewMergesOnlyBuiltParents(t *testing.T) { - tree := partTree{ - sizes: map[nodeKey]int{ + tree := ViewPartTree{ + sizes: map[NodeKey]int{ {0, 0}: 1, {0, 1}: 1, {0, 2}: 1, @@ -60,64 +65,64 @@ func TestViewMergesOnlyBuiltParents(t *testing.T) { require.NoError(t, err) } - require.Equal(t, []nodeKey{{0, 0}, {0, 1}, {0, 2}, {0, 3}}, view.lines) + require.Equal(t, []NodeKey{{0, 0}, {0, 1}, {0, 2}, {0, 3}}, view.lines) require.True(t, view.merging) - tree.sizes[nodeKey{1, 0}] = 1 + tree.sizes[NodeKey{1, 0}] = 1 _, err := view.Append(tree) require.NoError(t, err) - require.Equal(t, []nodeKey{{1, 0}, {0, 2}, {0, 3}, {0, 4}}, view.lines) + require.Equal(t, []NodeKey{{1, 0}, {0, 2}, {0, 3}, {0, 4}}, view.lines) require.True(t, view.merging) - tree.sizes[nodeKey{1, 1}] = 1 - tree.sizes[nodeKey{1, 2}] = 1 - tree.sizes[nodeKey{2, 0}] = 1 + tree.sizes[NodeKey{1, 1}] = 1 + tree.sizes[NodeKey{1, 2}] = 1 + tree.sizes[NodeKey{2, 0}] = 1 _, err = view.Append(tree) require.NoError(t, err) - require.Equal(t, []nodeKey{{2, 0}, {1, 2}}, view.lines) + require.Equal(t, []NodeKey{{2, 0}, {1, 2}}, view.lines) require.False(t, view.merging) } func TestViewRendersPendingLinesWithoutTheirMessage(t *testing.T) { - tree := partTree{ - sizes: map[nodeKey]int{ + tree := ViewPartTree{ + sizes: map[NodeKey]int{ {0, 0}: 3, {0, 1}: 3, {1, 0}: 7, }, } - view, err := restoreView(viewState{ + view, err := restoreView(ViewState{ Lines: [][2]int{{1, 0}, {0, 2}}, - }, chatHigh, chatLow, tree) + }, viewChatHigh, viewChatLow, tree) require.NoError(t, err) - require.Equal(t, "\n0+2|aaa aaa\n2+1|"+pending+"\n", view.Render()) - require.Equal(t, 7+len(pending), view.Size()) + require.Equal(t, "\n0+2|aaa aaa\n2+1|"+viewPending+"\n", view.Render()) + require.Equal(t, 7+len(viewPending), view.Size()) } func TestViewCoarsenTakesTheCoarserLines(t *testing.T) { - view, err := restoreView(viewState{ + view, err := restoreView(ViewState{ Lines: [][2]int{{1, 0}, {0, 2}, {0, 3}, {0, 4}, {0, 5}}, - }, 100, 100, builtTree{}) + }, 100, 100, ViewBuiltTree{}) require.NoError(t, err) - other, err := restoreView(viewState{ + other, err := restoreView(ViewState{ Lines: [][2]int{{0, 0}, {0, 1}, {1, 1}, {0, 4}, {0, 5}}, - }, 100, 100, builtTree{}) + }, 100, 100, ViewBuiltTree{}) require.NoError(t, err) - require.NoError(t, view.Coarsen(other, builtTree{})) - require.Equal(t, []nodeKey{{1, 0}, {1, 1}, {0, 4}, {0, 5}}, view.lines) + require.NoError(t, view.Coarsen(other, ViewBuiltTree{})) + require.Equal(t, []NodeKey{{1, 0}, {1, 1}, {0, 4}, {0, 5}}, view.lines) view.low = 2 - require.NoError(t, view.Coarsen(other, builtTree{})) - require.Equal(t, []nodeKey{{2, 0}, {1, 2}}, view.lines) + require.NoError(t, view.Coarsen(other, ViewBuiltTree{})) + require.Equal(t, []NodeKey{{2, 0}, {1, 2}}, view.lines) require.False(t, view.merging) } func TestViewContextStopsAtTheFirstPendingLine(t *testing.T) { - tree := partTree{ - sizes: map[nodeKey]int{ + tree := ViewPartTree{ + sizes: map[NodeKey]int{ {0, 0}: 1, {0, 1}: 1, {1, 0}: 1, @@ -125,7 +130,7 @@ func TestViewContextStopsAtTheFirstPendingLine(t *testing.T) { }, } - view, err := restoreView(viewState{ + view, err := restoreView(ViewState{ Lines: [][2]int{{1, 0}, {0, 2}, {0, 3}}, }, 100, 100, tree) require.NoError(t, err) @@ -135,45 +140,36 @@ func TestViewContextStopsAtTheFirstPendingLine(t *testing.T) { } func TestRestoreViewRejectsAGap(t *testing.T) { - _, err := restoreView(viewState{ + _, err := restoreView(ViewState{ Lines: [][2]int{{0, 0}, {0, 2}}, - }, chatHigh, chatLow, builtTree{}) + }, viewChatHigh, viewChatLow, ViewBuiltTree{}) require.ErrorContains(t, err, "does not start at message 1") } -// pushEntry is one entry of Taelin's rollback state list, newest first. Under -// push alone, life stays 0, so the entry leaves it out. -type pushEntry struct { - keep bool - state int - older *pushEntry -} +// ViewBuiltTree is a tree with each node built, with 1 byte of text. +type ViewBuiltTree struct{} -// push is the push of rollback_state_list.js. -func push(state int, states *pushEntry) *pushEntry { - if states == nil { - return &pushEntry{ - state: state, - } - } +func (v ViewBuiltTree) Built(_, _ int) (bool, error) { + return true, nil +} - if !states.keep { - return &pushEntry{ - keep: true, - state: states.state, - older: states.older, - } - } +func (ViewBuiltTree) Node(level, index int) (Node, bool, error) { + return treeTestNode(level, index, "a"), true, nil +} - return &pushEntry{ - state: state, - older: push(states.state, states.older), - } +// ViewPushEntry is one entry of the rollback state list of the reference +// design, newest first. Under push alone, life stays 0, so the entry leaves it +// out. +type ViewPushEntry struct { + keep bool + state int + older *ViewPushEntry } // starts returns the states of the list, oldest first. -func (e *pushEntry) starts() []int { +func (e *ViewPushEntry) starts() []int { var starts []int + for entry := e; entry != nil; entry = entry.older { starts = append(starts, entry.state) } @@ -183,29 +179,18 @@ func (e *pushEntry) starts() []int { return starts } -// builtTree is a tree with each node built, with 1 byte of text. -type builtTree struct{} - -func (builtTree) Built(level, index int) (bool, error) { - return true, nil -} - -func (builtTree) Node(level, index int) (Node, bool, error) { - return testNode(level, index, "a"), true, nil +// ViewPartTree is a tree with the built nodes of its sizes. +type ViewPartTree struct { + sizes map[NodeKey]int } -// partTree is a tree with the built nodes of its sizes. -type partTree struct { - sizes map[nodeKey]int -} - -func (p partTree) Built(level, index int) (bool, error) { - _, built := p.sizes[nodeKey{level, index}] +func (p ViewPartTree) Built(level, index int) (bool, error) { + _, built := p.sizes[NodeKey{level, index}] return built, nil } -func (p partTree) Node(level, index int) (Node, bool, error) { - size, built := p.sizes[nodeKey{level, index}] +func (p ViewPartTree) Node(level, index int) (Node, bool, error) { + size, built := p.sizes[NodeKey{level, index}] if !built { return Node{}, false, nil } @@ -215,5 +200,31 @@ func (p partTree) Node(level, index int) (Node, bool, error) { text = "aaa\naaa" } - return testNode(level, index, text), true, nil + return treeTestNode(level, index, text), true, nil +} + +// viewPush adds state to the newest end of states and returns the new list. An +// empty list gets one entry. When the newest entry is not kept, it becomes kept +// with its own state, and state is dropped. When it is kept, state becomes a +// new newest entry, and the state of the old newest entry goes to the older +// entries by the same push. +func viewPush(state int, states *ViewPushEntry) *ViewPushEntry { + if states == nil { + return &ViewPushEntry{ + state: state, + } + } + + if !states.keep { + return &ViewPushEntry{ + keep: true, + state: states.state, + older: states.older, + } + } + + return &ViewPushEntry{ + state: state, + older: viewPush(states.state, states.older), + } }