87 lines
2.1 KiB
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
|
|
}
|