Stop the terminal tests reading a pty nobody opened (closes #126)
check / check (push) Waiting to run

On Linux, pty.Open of github.com/creack/pty v1.1.24 passes the address
of a local variable to ioctl as a plain number, through a function
call. When Go moved the goroutine's stack in between, the kernel wrote
the pty's number to the old place, and pty.Open opened /dev/pts/0,
another terminal. secret rm wrote there, and the test read a terminal
no program had open, which never ends. Require the commit on the
library's main branch that passes a pointer; no release has it yet.

Both terminal tests now stop reading when their one-minute context
ends, and fail saying what was still waiting.

Model: opus-5-5
This commit is contained in:
2026-10-06 19:42:36 +00:00
parent 4ff0d20c10
commit 6f1b3f9fb7
4 changed files with 34 additions and 7 deletions
+19 -4
View File
@@ -2601,7 +2601,9 @@ func TestRemoveWithoutTerminalFailsAtOnce(t *testing.T) {
// and stderr, not both: whether it asks must depend on stdin alone, where
// the answer is read from. pty.Open returns the two ends of a new terminal:
// tty is the end a program uses as its terminal, and ptmx the end the test
// reads what the terminal shows from and types into.
// reads what the terminal shows from and types into. Reading ptmx stops at
// the context's deadline, when secret rm is killed too, so a terminal that
// stays open fails the test then instead of hanging it.
// TestRemoveIgnoresTerminalOnStdout runs `echo y | secret rm x` at a
// terminal. stdin is a pipe, so nobody can answer there, and the command
@@ -2619,6 +2621,9 @@ func TestRemoveIgnoresTerminalOnStdout(t *testing.T) {
defer func() { _ = ptmx.Close() }()
deadline, _ := ctx.Deadline()
require.NoError(t, ptmx.SetReadDeadline(deadline))
cmd.Stdin = strings.NewReader("y\n")
cmd.Stdout = tty
cmd.Stderr = tty
@@ -2628,9 +2633,15 @@ func TestRemoveIgnoresTerminalOnStdout(t *testing.T) {
_ = tty.Close()
// The read ends once secret rm has exited and so closed the terminal.
shown, _ := io.ReadAll(ptmx)
shown, err := io.ReadAll(ptmx)
require.NotErrorIs(t, err, os.ErrDeadlineExceeded,
"the terminal was still open a minute after secret rm started: %s",
shown)
require.Error(t, cmd.Wait())
err = cmd.Wait()
require.NoError(t, ctx.Err(), "secret rm did not exit within a minute")
require.Error(t, err)
assert.Contains(t, string(shown), "pass --force")
assert.DirExists(t, secretDir)
}
@@ -2650,6 +2661,9 @@ func TestRemoveAsksAtTerminalOnStdin(t *testing.T) {
defer func() { _ = ptmx.Close() }()
deadline, _ := ctx.Deadline()
require.NoError(t, ptmx.SetReadDeadline(deadline))
cmd.Stdin = tty
// Not a file, so exec.Cmd connects stdout through a pipe.
cmd.Stdout = io.Discard
@@ -2667,7 +2681,8 @@ func TestRemoveAsksAtTerminalOnStdin(t *testing.T) {
terminal := bufio.NewReader(ptmx)
for !bytes.HasSuffix(shown, []byte("[y/N] ")) {
char, err = terminal.ReadByte()
require.NoError(t, err, "secret rm ended without asking: %s", shown)
require.NoError(t, err, "secret rm did not ask on the terminal: %s",
shown)
shown = append(shown, char)
}