X-Git-Url: https://git.giacomolli.uk/onechat/blobdiff_plain/9f17bfb1173524553fff5301b0664267b430e6e3..9bd91a76e6277f7716e206ddb665dc5fe6f5b583:/view_test.go?ds=sidebyside
diff --git a/view_test.go b/view_test.go
index be38873..56abdd6 100644
--- a/view_test.go
+++ b/view_test.go
@@ -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,81 +56,113 @@ 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, "\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{
+ Lines: [][2]int{{1, 0}, {0, 2}, {0, 3}, {0, 4}, {0, 5}},
+ }, 100, 100, ViewBuiltTree{})
+ require.NoError(t, err)
+
+ other, err := restoreView(ViewState{
+ Lines: [][2]int{{0, 0}, {0, 1}, {1, 1}, {0, 4}, {0, 5}},
+ }, 100, 100, ViewBuiltTree{})
+ require.NoError(t, err)
+
+ 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, ViewBuiltTree{}))
+ require.Equal(t, []NodeKey{{2, 0}, {1, 2}}, view.lines)
+ require.False(t, view.merging)
+}
+
+func TestViewContextStopsAtTheFirstPendingLine(t *testing.T) {
+ tree := ViewPartTree{
+ 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, "\n0+2|aaa aaa\n2+1|"+pending+"\n", view.Render(tree))
- require.Equal(t, 7+len(pending), view.Size(tree))
+ require.Equal(t, "\n", view.Context(0))
+ require.Equal(t, "\n0+2|a\n", view.Context(2))
+ require.Equal(t, "\n0+2|a\n", 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)
@@ -139,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)
@@ -162,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),
+ }
}