fix: wait on bash prompt ready marker before sending in keystrokes

This commit is contained in:
2026-03-21 20:54:24 +00:00
parent cc696bb4be
commit fe42c40d1e
4 changed files with 159 additions and 2 deletions
+14 -1
View File
@@ -32,6 +32,7 @@ type RecordRequest struct {
type ReplayRequest struct {
Cmd *exec.Cmd
Input []byte
InputReady <-chan struct{}
OutputLog io.Writer
}
@@ -90,7 +91,7 @@ func Replay(req ReplayRequest) error {
defer ptmx.Close()
outputDone := copyAsync(combineWriters(req.OutputLog), ptmx)
inputDone := copyAsync(ptmx, bytes.NewReader(replayInput(req.Input)))
inputDone := copyAsyncWhenReady(ptmx, bytes.NewReader(replayInput(req.Input)), req.InputReady)
waitErr := req.Cmd.Wait()
ptmx.Close()
@@ -128,6 +129,18 @@ func copyAsync(dst io.Writer, src io.Reader) <-chan error {
return done
}
func copyAsyncWhenReady(dst io.Writer, src io.Reader, ready <-chan struct{}) <-chan error {
done := make(chan error, 1)
go func() {
if ready != nil {
<-ready
}
_, err := io.Copy(dst, src)
done <- normalizeCopyError(err)
}()
return done
}
func replayInput(input []byte) []byte {
if len(input) > 0 && input[len(input)-1] == terminalEOF {
return input
+29
View File
@@ -5,6 +5,7 @@ import (
"os"
"os/exec"
"testing"
"time"
)
func TestRecordCopiesInputAndOutput(t *testing.T) {
@@ -64,3 +65,31 @@ func TestReplayCapturesOutput(t *testing.T) {
t.Fatalf("output log = %q, want marker + line:hello", got)
}
}
func TestReplayWaitsForInputReadySignal(t *testing.T) {
cmd := exec.Command("sh", "-c", `IFS= read -r line; printf 'line:%s\n' "$line"`)
ready := make(chan struct{})
var outputLog bytes.Buffer
start := time.Now()
go func() {
time.Sleep(100 * time.Millisecond)
close(ready)
}()
if err := Replay(ReplayRequest{
Cmd: cmd,
Input: []byte("hello\n"),
InputReady: ready,
OutputLog: &outputLog,
}); err != nil {
t.Fatalf("Replay() error = %v", err)
}
if elapsed := time.Since(start); elapsed < 100*time.Millisecond {
t.Fatalf("Replay() elapsed = %v, want >= %v", elapsed, 100*time.Millisecond)
}
if !bytes.Contains(outputLog.Bytes(), []byte("line:hello")) {
t.Fatalf("output log = %q, want line:hello", outputLog.String())
}
}