Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 1d6283b

Browse files
authored
fix: Improve debuggability of ptytest failures (#3367)
Since we were not failing tests with `require` the error output was somewhat hidden in the stream of log messages. This change standardizes `ptytest` logging and failing to improve visibility.
1 parent 8f33878 commit 1d6283b

File tree

1 file changed

+49
-19
lines changed

1 file changed

+49
-19
lines changed

pty/ptytest/ptytest.go

Lines changed: 49 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bufio"
55
"bytes"
66
"context"
7+
"fmt"
78
"io"
89
"os/exec"
910
"runtime"
@@ -21,13 +22,17 @@ import (
2122
)
2223

2324
func New(t *testing.T) *PTY {
25+
t.Helper()
26+
2427
ptty, err := pty.New()
2528
require.NoError(t, err)
2629

2730
return create(t, ptty, "cmd")
2831
}
2932

3033
func Start(t *testing.T, cmd *exec.Cmd) (*PTY, pty.Process) {
34+
t.Helper()
35+
3136
ptty, ps, err := pty.Start(cmd)
3237
require.NoError(t, err)
3338
return create(t, ptty, cmd.Args[0]), ps
@@ -42,14 +47,6 @@ func create(t *testing.T, ptty pty.PTY, name string) *PTY {
4247
_ = logr.Close()
4348
<-logDone // Guard against logging after test.
4449
})
45-
go func() {
46-
defer close(logDone)
47-
s := bufio.NewScanner(logr)
48-
for s.Scan() {
49-
// Quote output to avoid terminal escape codes, e.g. bell.
50-
t.Logf("%s: stdout: %q", name, s.Text())
51-
}
52-
}()
5350

5451
// Write to log and output buffer.
5552
copyDone := make(chan struct{})
@@ -66,19 +63,32 @@ func create(t *testing.T, ptty pty.PTY, name string) *PTY {
6663
<-copyDone
6764
})
6865

69-
return &PTY{
70-
t: t,
71-
PTY: ptty,
72-
out: out,
66+
tpty := &PTY{
67+
t: t,
68+
PTY: ptty,
69+
out: out,
70+
name: name,
7371

7472
runeReader: bufio.NewReaderSize(out, utf8.UTFMax),
7573
}
74+
75+
go func() {
76+
defer close(logDone)
77+
s := bufio.NewScanner(logr)
78+
for s.Scan() {
79+
// Quote output to avoid terminal escape codes, e.g. bell.
80+
tpty.logf("stdout: %q", s.Text())
81+
}
82+
}()
83+
84+
return tpty
7685
}
7786

7887
type PTY struct {
79-
t *testing.T
8088
pty.PTY
81-
out *stdbuf
89+
t *testing.T
90+
out *stdbuf
91+
name string
8292

8393
runeReader *bufio.Reader
8494
}
@@ -113,26 +123,27 @@ func (p *PTY) ExpectMatch(str string) string {
113123
select {
114124
case err := <-match:
115125
if err != nil {
116-
p.t.Fatalf("%s: read error: %v (wanted %q; got %q)", time.Now(), err, str, buffer.String())
126+
p.fatalf("read error", "%v (wanted %q; got %q)", err, str, buffer.String())
117127
return ""
118128
}
119-
p.t.Logf("%s: matched %q = %q", time.Now(), str, buffer.String())
129+
p.logf("matched %q = %q", str, buffer.String())
120130
return buffer.String()
121131
case <-timeout.Done():
122132
// Ensure goroutine is cleaned up before test exit.
123133
_ = p.out.closeErr(p.Close())
124134
<-match
125135

126-
p.t.Fatalf("%s: match exceeded deadline: wanted %q; got %q", time.Now(), str, buffer.String())
136+
p.fatalf("match exceeded deadline", "wanted %q; got %q", str, buffer.String())
127137
return ""
128138
}
129139
}
130140

131141
func (p *PTY) Write(r rune) {
132142
p.t.Helper()
133143

144+
p.logf("stdin: %q", r)
134145
_, err := p.Input().Write([]byte{byte(r)})
135-
require.NoError(p.t, err)
146+
require.NoError(p.t, err, "write failed")
136147
}
137148

138149
func (p *PTY) WriteLine(str string) {
@@ -142,8 +153,27 @@ func (p *PTY) WriteLine(str string) {
142153
if runtime.GOOS == "windows" {
143154
newline = append(newline, '\n')
144155
}
156+
p.logf("stdin: %q", str+string(newline))
145157
_, err := p.Input().Write(append([]byte(str), newline...))
146-
require.NoError(p.t, err)
158+
require.NoError(p.t, err, "write line failed")
159+
}
160+
161+
func (p *PTY) logf(format string, args ...interface{}) {
162+
p.t.Helper()
163+
164+
// Match regular logger timestamp format, we seem to be logging in
165+
// UTC in other places as well, so match here.
166+
p.t.Logf("%s: %s: %s", time.Now().UTC().Format("2006-01-02 15:04:05.000"), p.name, fmt.Sprintf(format, args...))
167+
}
168+
169+
func (p *PTY) fatalf(reason string, format string, args ...interface{}) {
170+
p.t.Helper()
171+
172+
// Ensure the message is part of the normal log stream before
173+
// failing the test.
174+
p.logf("%s: %s", reason, fmt.Sprintf(format, args...))
175+
176+
require.FailNowf(p.t, reason, format, args...)
147177
}
148178

149179
// stdbuf is like a buffered stdout, it buffers writes until read.

0 commit comments

Comments
 (0)