package main import ( "slices" "strings" "testing" "github.com/stretchr/testify/require" ) func TestViewMergesInTheOrderOfTaelinsPush(t *testing.T) { tree := builtTree{} view := newView(0, 0) var states *pushEntry for tick := range 20001 { states = push(tick, states) starts := states.starts() view.high = len(starts) view.low = len(starts) view.Append(tree) lines := make([]int, len(view.lines)) for n, line := range view.lines { lines[n] = line.index << line.level } require.Equal(t, starts, lines, "t=%d", tick) } } func TestViewMergesInOneBatchFromHighToLow(t *testing.T) { tree := builtTree{} view := newView(10, 5) sizes := make([]int, 0, 20) for range 20 { view.Append(tree) sizes = append(sizes, view.Size(tree)) } require.Equal(t, []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 5, 6, 7, 8, 9, 10, 5, 6, 7, 8}, sizes) } func TestViewMergesOnlyBuiltParents(t *testing.T) { tree := partTree{ sizes: map[nodeKey]int{ {0, 0}: 1, {0, 1}: 1, {0, 2}: 1, {0, 3}: 1, {0, 4}: 1, {0, 5}: 1, }, } view := newView(3, 2) for range 4 { view.Append(tree) } 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 view.Append(tree) 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 view.Append(tree) 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{ {0, 0}: 3, {0, 1}: 3, {1, 0}: 7, }, } view, err := restoreView(viewState{ Lines: [][2]int{{1, 0}, {0, 2}}, }, chatHigh, chatLow, tree) require.NoError(t, err) require.Equal(t, "\n0+2|aaa aaa\n2+1|"+pending+"\n", view.Render(tree)) require.Equal(t, 7+len(pending), view.Size(tree)) } func TestViewCoarsenTakesTheCoarserLines(t *testing.T) { view, err := restoreView(viewState{ Lines: [][2]int{{1, 0}, {0, 2}, {0, 3}, {0, 4}, {0, 5}}, }, 100, 100, builtTree{}) require.NoError(t, err) other, err := restoreView(viewState{ Lines: [][2]int{{0, 0}, {0, 1}, {1, 1}, {0, 4}, {0, 5}}, }, 100, 100, builtTree{}) require.NoError(t, err) view.Coarsen(other, builtTree{}) require.Equal(t, []nodeKey{{1, 0}, {1, 1}, {0, 4}, {0, 5}}, view.lines) view.low = 2 view.Coarsen(other, builtTree{}) 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{ {0, 0}: 1, {0, 1}: 1, {1, 0}: 1, {0, 3}: 1, }, } view, err := restoreView(viewState{ Lines: [][2]int{{1, 0}, {0, 2}, {0, 3}}, }, 100, 100, tree) require.NoError(t, err) require.Equal(t, "\n", view.Context(tree, 0)) require.Equal(t, "\n0+2|a\n", view.Context(tree, 2)) require.Equal(t, "\n0+2|a\n", view.Context(tree, 3)) } func TestRestoreViewRejectsAGap(t *testing.T) { _, err := restoreView(viewState{ Lines: [][2]int{{0, 0}, {0, 2}}, }, chatHigh, chatLow, builtTree{}) 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 } // push is the push of rollback_state_list.js. func push(state int, states *pushEntry) *pushEntry { if states == nil { return &pushEntry{ state: state, } } if !states.keep { return &pushEntry{ keep: true, state: states.state, older: states.older, } } return &pushEntry{ state: state, older: push(states.state, states.older), } } // starts returns the states of the list, oldest first. func (e *pushEntry) starts() []int { var starts []int for entry := e; entry != nil; entry = entry.older { starts = append(starts, entry.state) } slices.Reverse(starts) return starts } // builtTree is a tree with each node built, with 1 byte of text. type builtTree struct{} func (builtTree) Node(level, index int) (Node, bool) { return testNode(level, index, "a"), true } // partTree is a tree with the built nodes of its sizes. type partTree struct { sizes map[nodeKey]int } func (p partTree) Node(level, index int) (Node, bool) { size, built := p.sizes[nodeKey{level, index}] if !built { return Node{}, false } text := strings.Repeat("a", size) if size == 7 { text = "aaa\naaa" } return testNode(level, index, text), true }