From 75ffe51cd25ea1217562ed7874f0a6266d5e6ee6 Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Tue, 17 Jan 2023 12:10:49 +0000 Subject: [PATCH 1/3] test: Improve TestSSH/ForwardGPG stability on macOS via pty.ReadRune Writing to stdin for `coder ssh` too early could result in the input being discarded. To work around this we add a new `ptytest` method called `ReadRune` that lets us read one character of output. This will indicate the command is ready to accept input. It could be one character of the prompt, or of the loading message waiting for connection to be established. --- cli/ssh_test.go | 4 ++++ pty/ptytest/ptytest.go | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/cli/ssh_test.go b/cli/ssh_test.go index ceb34cc7f6b80..b5e3f725f67e4 100644 --- a/cli/ssh_test.go +++ b/cli/ssh_test.go @@ -475,6 +475,10 @@ Expire-Date: 0 // real error from being printed. t.Cleanup(cancel) + // Wait for the prompt or any output really to indicate the command has + // started and accepting input on stdin. + _ = pty.ReadRune(ctx) + pty.WriteLine("echo hello 'world'") pty.ExpectMatch("hello world") diff --git a/pty/ptytest/ptytest.go b/pty/ptytest/ptytest.go index 40d7018118b79..12fbf2fdf51d2 100644 --- a/pty/ptytest/ptytest.go +++ b/pty/ptytest/ptytest.go @@ -182,6 +182,46 @@ func (p *PTY) ExpectMatch(str string) string { } } +func (p *PTY) ReadRune(ctx context.Context) rune { + p.t.Helper() + + // A timeout is mandatory, caller can decide by passing a context + // that times out. + if _, ok := ctx.Deadline(); !ok { + timeout := testutil.WaitMedium + p.logf("ReadRune ctx has no deadline, using %s", timeout) + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, timeout) + defer cancel() + } + + var r rune + match := make(chan error, 1) + go func() { + defer close(match) + var err error + r, _, err = p.runeReader.ReadRune() + match <- err + }() + + select { + case err := <-match: + if err != nil { + p.fatalf("read error", "%v (wanted newline; got %q)", err, r) + return 0 + } + p.logf("matched rune = %q", r) + return r + case <-ctx.Done(): + // Ensure goroutine is cleaned up before test exit. + _ = p.close("read rune context done: " + ctx.Err().Error()) + <-match + + p.fatalf("read rune context done", "wanted rune; got nothing") + return 0 + } +} + func (p *PTY) ReadLine() string { p.t.Helper() From 0b48b8da275457380a31394afba7a22292099f60 Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Tue, 17 Jan 2023 12:17:03 +0000 Subject: [PATCH 2/3] Disable ruleguard --- pty/ptytest/ptytest.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pty/ptytest/ptytest.go b/pty/ptytest/ptytest.go index 12fbf2fdf51d2..2f97fb1eecbfb 100644 --- a/pty/ptytest/ptytest.go +++ b/pty/ptytest/ptytest.go @@ -191,6 +191,7 @@ func (p *PTY) ReadRune(ctx context.Context) rune { timeout := testutil.WaitMedium p.logf("ReadRune ctx has no deadline, using %s", timeout) var cancel context.CancelFunc + //nolint:ruleguard // Rule guard doesn't detect that we're using testutil.Wait*. ctx, cancel = context.WithTimeout(ctx, timeout) defer cancel() } From 4c135d922f12a3cf1fbb655ef0d183bb462ea7b0 Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Tue, 17 Jan 2023 12:18:24 +0000 Subject: [PATCH 3/3] Disable gocritic --- pty/ptytest/ptytest.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pty/ptytest/ptytest.go b/pty/ptytest/ptytest.go index 2f97fb1eecbfb..6f0bae64fce42 100644 --- a/pty/ptytest/ptytest.go +++ b/pty/ptytest/ptytest.go @@ -191,7 +191,7 @@ func (p *PTY) ReadRune(ctx context.Context) rune { timeout := testutil.WaitMedium p.logf("ReadRune ctx has no deadline, using %s", timeout) var cancel context.CancelFunc - //nolint:ruleguard // Rule guard doesn't detect that we're using testutil.Wait*. + //nolint:gocritic // Rule guard doesn't detect that we're using testutil.Wait*. ctx, cancel = context.WithTimeout(ctx, timeout) defer cancel() }