elvish/pkg/cli/loop_test.go
Qi Xiao fd932969d3 pkg/cli: Change ExampleLoop to a test.
This prevents it from appearing in the godoc.
2021-02-11 19:31:53 +00:00

178 lines
4.0 KiB
Go

package cli
import (
"fmt"
"io"
"reflect"
"testing"
)
func TestRead_PassesInputEventsToHandler(t *testing.T) {
var handlerGotEvents []event
lp := newLoop()
lp.HandleCb(func(e event) {
handlerGotEvents = append(handlerGotEvents, e)
if e == "^D" {
lp.Return("", nil)
}
})
inputPassedEvents := []event{"foo", "bar", "lorem", "ipsum", "^D"}
supplyInputs(lp, inputPassedEvents...)
_, _ = lp.Run()
if !reflect.DeepEqual(handlerGotEvents, inputPassedEvents) {
t.Errorf("Handler got events %v, expect same as events passed to input (%v)",
handlerGotEvents, inputPassedEvents)
}
}
func TestLoop_RunReturnsAfterReturnCalled(t *testing.T) {
lp := newLoop()
lp.HandleCb(func(event) { lp.Return("buffer", io.EOF) })
supplyInputs(lp, "x")
buf, err := lp.Run()
if buf != "buffer" || err != io.EOF {
fmt.Printf("Run -> (%v, %v), want (%v, %v)", buf, err, "buffer", io.EOF)
}
}
func TestRead_CallsDrawWhenRedrawRequestedBeforeRead(t *testing.T) {
testReadCallsDrawWhenRedrawRequestedBeforeRead(t, true, fullRedraw)
testReadCallsDrawWhenRedrawRequestedBeforeRead(t, false, 0)
}
func testReadCallsDrawWhenRedrawRequestedBeforeRead(t *testing.T, full bool, wantRedrawFlag redrawFlag) {
t.Helper()
var gotRedrawFlag redrawFlag
drawSeq := 0
doneCh := make(chan struct{})
drawer := func(full redrawFlag) {
if drawSeq == 0 {
gotRedrawFlag = full
close(doneCh)
}
drawSeq++
}
lp := newLoop()
lp.HandleCb(quitOn(lp, "^D", "", nil))
go func() {
<-doneCh
lp.Input("^D")
}()
lp.RedrawCb(drawer)
lp.Redraw(full)
_, _ = lp.Run()
if gotRedrawFlag != wantRedrawFlag {
t.Errorf("Drawer got flag %v, want %v", gotRedrawFlag, wantRedrawFlag)
}
}
func TestRead_callsDrawWhenRedrawRequestedAfterFirstDraw(t *testing.T) {
testReadCallsDrawWhenRedrawRequestedAfterFirstDraw(t, true, fullRedraw)
testReadCallsDrawWhenRedrawRequestedAfterFirstDraw(t, false, 0)
}
func testReadCallsDrawWhenRedrawRequestedAfterFirstDraw(t *testing.T, full bool, wantRedrawFlag redrawFlag) {
t.Helper()
var gotRedrawFlag redrawFlag
drawSeq := 0
firstDrawCalledCh := make(chan struct{})
doneCh := make(chan struct{})
drawer := func(flag redrawFlag) {
if drawSeq == 0 {
close(firstDrawCalledCh)
} else if drawSeq == 1 {
gotRedrawFlag = flag
close(doneCh)
}
drawSeq++
}
lp := newLoop()
lp.HandleCb(quitOn(lp, "^D", "", nil))
go func() {
<-doneCh
lp.Input("^D")
}()
lp.RedrawCb(drawer)
go func() {
<-firstDrawCalledCh
lp.Redraw(full)
}()
_, _ = lp.Run()
if gotRedrawFlag != wantRedrawFlag {
t.Errorf("Drawer got flag %v, want %v", gotRedrawFlag, wantRedrawFlag)
}
}
// Helpers.
func supplyInputs(lp *loop, events ...event) {
for _, event := range events {
lp.Input(event)
}
}
// Returns a HandleCb that quits on a trigger event.
func quitOn(lp *loop, retTrigger event, ret string, err error) handleCb {
return func(e event) {
if e == retTrigger {
lp.Return(ret, err)
}
}
}
func TestLoop_FullLifecycle(t *testing.T) {
// A test for the entire lifecycle of a loop.
var initialBuffer, finalBuffer string
buffer := ""
firstDrawerCall := true
drawer := func(flag redrawFlag) {
// Because the consumption of events is batched, calls to the drawer is
// nondeterministic except for the first and final calls.
switch {
case firstDrawerCall:
initialBuffer = buffer
firstDrawerCall = false
case flag&finalRedraw != 0:
finalBuffer = buffer
}
}
lp := newLoop()
lp.HandleCb(func(e event) {
if e == '\n' {
lp.Return(buffer, nil)
return
}
buffer += string(e.(rune))
})
go func() {
for _, event := range "echo\n" {
lp.Input(event)
}
}()
lp.RedrawCb(drawer)
returnedBuffer, err := lp.Run()
if initialBuffer != "" {
t.Errorf("got initial buffer %q, want %q", initialBuffer, "")
}
if finalBuffer != "echo" {
t.Errorf("got final buffer %q, want %q", finalBuffer, "echo")
}
if returnedBuffer != "echo" {
t.Errorf("got returned buffer %q, want %q", returnedBuffer, "echo")
}
if err != nil {
t.Errorf("got error %v, want nil", err)
}
}