Skip to content

Commit c48bc1a

Browse files
pdostalwilliammartin
authored andcommitted
Poll TTY echo mode instead of sleeping in password tests
Replace the fixed-duration sleep with a polling loop that checks the actual TTY echo flag before sending password input. This eliminates the race condition where huh has not yet disabled echo mode, which caused flaky test failures in slow environments. Follow-up to #13304.
1 parent 9b457e8 commit c48bc1a

3 files changed

Lines changed: 35 additions & 8 deletions

File tree

‎internal/prompter/accessible_prompter_test.go‎

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package prompter_test
55
import (
66
"fmt"
77
"io"
8+
"os"
89
"slices"
910
"strings"
1011
"testing"
@@ -17,6 +18,7 @@ import (
1718
"github.com/hinshun/vt10x"
1819
"github.com/stretchr/testify/assert"
1920
"github.com/stretchr/testify/require"
21+
"golang.org/x/sys/unix"
2022
)
2123

2224
// The following tests are broadly testing the accessible prompter, and NOT asserting
@@ -34,8 +36,6 @@ import (
3436
// but doesn't mandate that prompts always look exactly the same.
3537
func TestAccessiblePrompter(t *testing.T) {
3638

37-
beforePasswordSendTimeout := 100 * time.Millisecond
38-
3939
t.Run("Select", func(t *testing.T) {
4040
console := newTestVirtualTerminal(t)
4141
p := newTestAccessiblePrompter(t, console)
@@ -505,8 +505,8 @@ func TestAccessiblePrompter(t *testing.T) {
505505
_, err := console.ExpectString("Enter password")
506506
require.NoError(t, err)
507507

508-
// Wait to ensure huh has time to set the echo mode
509-
time.Sleep(beforePasswordSendTimeout)
508+
// Wait until huh has disabled echo mode on the TTY
509+
waitForEchoDisabled(t, console.Tty(), 5*time.Second)
510510

511511
// Enter a number
512512
_, err = console.SendLine(dummyPassword)
@@ -596,8 +596,8 @@ func TestAccessiblePrompter(t *testing.T) {
596596
_, err := console.ExpectString("Paste your authentication token:")
597597
require.NoError(t, err)
598598

599-
// Wait to ensure huh has time to set the echo mode
600-
time.Sleep(beforePasswordSendTimeout)
599+
// Wait until huh has disabled echo mode on the TTY
600+
waitForEchoDisabled(t, console.Tty(), 5*time.Second)
601601

602602
// Enter some dummy auth token
603603
_, err = console.SendLine(dummyAuthToken)
@@ -641,8 +641,8 @@ func TestAccessiblePrompter(t *testing.T) {
641641
_, err = console.ExpectString("Paste your authentication token:")
642642
require.NoError(t, err)
643643

644-
// Wait to ensure huh has time to set the echo mode
645-
time.Sleep(beforePasswordSendTimeout)
644+
// Wait until huh has disabled echo mode on the TTY
645+
waitForEchoDisabled(t, console.Tty(), 5*time.Second)
646646

647647
// Now enter some dummy auth token to return control back to the test
648648
_, err = console.SendLine(dummyAuthTokenForAfterFailure)
@@ -956,3 +956,20 @@ func testCloser(t *testing.T, closer io.Closer) {
956956
t.Errorf("Close failed: %s", err)
957957
}
958958
}
959+
960+
// waitForEchoDisabled polls the TTY until echo mode is disabled or the
961+
// timeout is reached. This is used in password and auth token tests to
962+
// ensure that huh has configured the terminal before we send input.
963+
func waitForEchoDisabled(t *testing.T, tty *os.File, timeout time.Duration) {
964+
t.Helper()
965+
deadline := time.Now().Add(timeout)
966+
for time.Now().Before(deadline) {
967+
termios, err := unix.IoctlGetTermios(int(tty.Fd()), ioctlGetTermios)
968+
require.NoError(t, err)
969+
if termios.Lflag&unix.ECHO == 0 {
970+
return
971+
}
972+
time.Sleep(time.Millisecond)
973+
}
974+
t.Fatal("timed out waiting for echo mode to be disabled")
975+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package prompter_test
2+
3+
import "golang.org/x/sys/unix"
4+
5+
const ioctlGetTermios = unix.TIOCGETA
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package prompter_test
2+
3+
import "golang.org/x/sys/unix"
4+
5+
const ioctlGetTermios = unix.TCGETS

0 commit comments

Comments
 (0)