diff --git a/pkg/tasks/tasks_test.go b/pkg/tasks/tasks_test.go index b15b48ee4..4020fb09d 100644 --- a/pkg/tasks/tasks_test.go +++ b/pkg/tasks/tasks_test.go @@ -486,3 +486,45 @@ func TestNewCmdTaskRefresh(t *testing.T) { } } } + +// A read request is answered by the task that serves it calling the request's Then. +// This checks that requests still waiting when the task is stopped are answered too, +// which is what happens when a re-render replaces the task. +func TestQueuedReadRequestsAreAnsweredWhenTheTaskStops(t *testing.T) { + noop := func() {} + task := gocui.NewFakeTask() + + // A pipe the task blocks on, so that the requests are still waiting when it stops. + pipeReader, pipeWriter := io.Pipe() + defer pipeWriter.Close() + + manager := NewViewBufferManager( + utils.NewDummyLog(), bytes.NewBuffer(nil), noop, noop, noop, noop, noop, noop, + func() gocui.Task { return task }, + func(f func()) error { f(); return nil }, + ) + + stop := make(chan struct{}) + fn := manager.NewCmdTask( + func() (Cmd, io.Reader) { return ExecCmd{Cmd: exec.Command("true")}, pipeReader }, + "", LinesToRead{Total: 1, InitialRefreshAfter: -1}, noop) + go func() { _, _ = pipeWriter.Write([]byte("first line\n")) }() + go func() { _ = fn(TaskOpts{Stop: stop, InitialContentLoaded: noop}) }() + // Let the task start and read the line it was asked for, so that the requests + // below are handed to a task that is waiting for them. + time.Sleep(50 * time.Millisecond) + + answered := atomic.Int32{} + manager.ReadToEnd(func() { answered.Add(1) }) + manager.ReadToEnd(func() { answered.Add(1) }) + + // Let the first request be picked up and block on the pipe, then stop the task. + time.Sleep(50 * time.Millisecond) + close(stop) + time.Sleep(50 * time.Millisecond) + + /* EXPECTED: + assert.EqualValues(t, 2, answered.Load()) + ACTUAL: */ + assert.EqualValues(t, 1, answered.Load()) +}