]> git.giacomolli.uk - onechat/blobdiff - view_test.go
Give the WhatsApp batches to the agent
[onechat] / view_test.go
index 0162f28d43617a85da1e7a40767ebe52163ac408..56abdd6b81e12e72d0db7ad5a5e72e4b6d5f9537 100644 (file)
@@ -8,16 +8,17 @@ 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)
-               view.Append(tree)
+               _, err := view.Append(tree)
+               require.NoError(t, err)
 
                lines := make([]int, len(view.lines))
                for n, line := range view.lines {
@@ -29,20 +30,21 @@ 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 {
-               view.Append(tree)
-               sizes = append(sizes, view.Size(tree))
+               _, err := view.Append(tree)
+               require.NoError(t, err)
+               sizes = append(sizes, view.Size())
        }
 
        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{
+       tree := ViewPartTree{
+               sizes: map[NodeKey]int{
                        {0, 0}: 1,
                        {0, 1}: 1,
                        {0, 2}: 1,
@@ -54,65 +56,68 @@ func TestViewMergesOnlyBuiltParents(t *testing.T) {
 
        view := newView(3, 2)
        for range 4 {
-               view.Append(tree)
+               _, err := view.Append(tree)
+               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
-       view.Append(tree)
-       require.Equal(t, []nodeKey{{1, 0}, {0, 2}, {0, 3}, {0, 4}}, view.lines)
+       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.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)
+       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.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, "<chat>\n0+2|aaa aaa\n2+1|"+pending+"\n</chat>", view.Render(tree))
-       require.Equal(t, 7+len(pending), view.Size(tree))
+       require.Equal(t, "<chat>\n0+2|aaa aaa\n2+1|"+viewPending+"\n</chat>", 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)
 
-       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
-       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,
@@ -120,54 +125,44 @@ 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)
-       require.Equal(t, "<chat>\n</chat>", view.Context(tree, 0))
-       require.Equal(t, "<chat>\n0+2|a\n</chat>", view.Context(tree, 2))
-       require.Equal(t, "<chat>\n0+2|a\n</chat>", view.Context(tree, 3))
+       require.Equal(t, "<chat>\n</chat>", view.Context(0))
+       require.Equal(t, "<chat>\n0+2|a\n</chat>", view.Context(2))
+       require.Equal(t, "<chat>\n0+2|a\n</chat>", view.Context(3))
 }
 
 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 (ViewBuiltTree) Built(level, index 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)
@@ -178,22 +173,20 @@ 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) Node(level, index int) (Node, bool) {
-       return testNode(level, index, "a"), true
+// 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 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) {
-       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
+               return Node{}, false, nil
        }
 
        text := strings.Repeat("a", size)
@@ -201,5 +194,31 @@ func (p partTree) Node(level, index int) (Node, bool) {
                text = "aaa\naaa"
        }
 
-       return testNode(level, index, text), true
+       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),
+       }
 }