Files
barnard/uiterm/tree_regression_test.go

87 lines
2.1 KiB
Go

package uiterm
import (
"reflect"
"testing"
"time"
)
// cyclicItem reports itself as its own child, standing in for a channel graph
// in which a channel is its own ancestor.
type cyclicItem struct{ name string }
func (i *cyclicItem) String() string { return i.name }
func (i *cyclicItem) TreeItemStyle(fg, bg Attribute, active bool) (Attribute, Attribute) {
return fg, bg
}
func TestTreePassesLeftRightAndBackspaceToFocusedItem(t *testing.T) {
item := &cyclicItem{name: "user"}
var received []Key
tree := Tree{
KeyListener: func(_ *Ui, _ *Tree, _ TreeItem, key Key) {
received = append(received, key)
},
lines: []renderedTreeItem{{Item: item}},
}
tree.uiInitialize(New(nil))
for _, key := range []Key{KeyArrowLeft, KeyArrowRight, KeyBackspace, KeyBackspace2} {
tree.uiKeyEvent(key)
}
want := []Key{KeyArrowLeft, KeyArrowRight, KeyBackspace, KeyBackspace2}
if !reflect.DeepEqual(received, want) {
t.Fatalf("tree keys = %v, want %v", received, want)
}
}
// Regression: rebuild_rec followed parent/child links with no depth limit, so
// a cyclic channel graph recursed until the process ran out of memory. A
// rebuild must now terminate and stay bounded.
func TestTreeRebuildTerminatesOnCyclicGraph(t *testing.T) {
t.Parallel()
self := &cyclicItem{name: "loop"}
tree := Tree{
Generator: func(item TreeItem) []TreeItem {
return []TreeItem{self}
},
}
done := make(chan struct{})
go func() {
tree.rebuild(false, nil)
close(done)
}()
select {
case <-done:
case <-timeoutAfterSeconds(10):
t.Fatal("rebuild did not terminate on a cyclic tree")
}
if len(tree.lines) == 0 {
t.Fatal("expected the bounded rebuild to still produce lines")
}
if len(tree.lines) > maxTreeLines {
t.Fatalf("rebuild produced %d lines, above the %d cap",
len(tree.lines), maxTreeLines)
}
for _, line := range tree.lines {
if line.Level >= maxTreeDepth {
t.Fatalf("rebuild recursed to level %d, at or past the %d cap",
line.Level, maxTreeDepth)
}
}
}
func timeoutAfterSeconds(n int) <-chan struct{} {
ch := make(chan struct{})
go func() {
time.Sleep(time.Duration(n) * time.Second)
close(ch)
}()
return ch
}