@@ -46,62 +46,69 @@ func create(t *testing.T, ptty pty.PTY, name string) *PTY {
46
46
// Use pipe for logging.
47
47
logDone := make (chan struct {})
48
48
logr , logw := io .Pipe ()
49
- t .Cleanup (func () {
50
- ctx , cancel := context .WithTimeout (context .Background (), testutil .WaitShort )
51
- defer cancel ()
52
-
53
- logf (t , name , "close logw on cleanup" )
54
- _ = logw .Close ()
55
-
56
- logf (t , name , "close logr on cleanup" )
57
- _ = logr .Close ()
58
-
59
- logf (t , name , "logr and logw closed" )
60
-
61
- select {
62
- case <- ctx .Done ():
63
- fatalf (t , name , "cleanup" , "log pipe did not close in time" )
64
- case <- logDone : // Guard against logging after test.
65
- }
66
- })
67
49
68
50
// Write to log and output buffer.
69
51
copyDone := make (chan struct {})
70
52
out := newStdbuf ()
71
53
w := io .MultiWriter (logw , out )
72
- go func () {
73
- defer close (copyDone )
74
- _ , err := io .Copy (w , ptty .Output ())
75
- _ = out .closeErr (err )
76
- }()
54
+
55
+ tpty := & PTY {
56
+ t : t ,
57
+ PTY : ptty ,
58
+ out : out ,
59
+ name : name ,
60
+
61
+ runeReader : bufio .NewReaderSize (out , utf8 .UTFMax ),
62
+ }
63
+ // Ensure pty is cleaned up at the end of test.
77
64
t .Cleanup (func () {
65
+ _ = tpty .Close ()
66
+ })
67
+
68
+ logClose := func (name string , c io.Closer ) {
69
+ tpty .logf ("closing %s" , name )
70
+ err := c .Close ()
71
+ tpty .logf ("closed %s: %v" , name , err )
72
+ }
73
+ // Set the actual close function for the tpty.
74
+ tpty .close = func (reason string ) error {
78
75
ctx , cancel := context .WithTimeout (context .Background (), testutil .WaitShort )
79
76
defer cancel ()
80
77
78
+ tpty .logf ("closing tpty: %s" , reason )
79
+
81
80
// Close pty only so that the copy goroutine can consume the
82
81
// remainder of it's buffer and then exit.
83
- logf (t , name , "close pty on cleanup" )
84
- err := ptty .Close ()
85
- // Pty may already be closed, so don't fail the test, but log
86
- // the error in case it's significant.
87
- logf (t , name , "closed pty: %v" , err )
88
-
82
+ logClose ("pty" , ptty )
89
83
select {
90
84
case <- ctx .Done ():
91
- fatalf (t , name , "cleanup " , "copy did not close in time" )
85
+ tpty . fatalf ("close " , "copy did not close in time" )
92
86
case <- copyDone :
93
87
}
94
- })
95
88
96
- tpty := & PTY {
97
- t : t ,
98
- PTY : ptty ,
99
- out : out ,
100
- name : name ,
89
+ logClose ("logw" , logw )
90
+ logClose ("logr" , logr )
91
+ select {
92
+ case <- ctx .Done ():
93
+ tpty .fatalf ("close" , "log pipe did not close in time" )
94
+ case <- logDone :
95
+ }
101
96
102
- runeReader : bufio .NewReaderSize (out , utf8 .UTFMax ),
97
+ tpty .logf ("closed tpty" )
98
+
99
+ return nil
103
100
}
104
101
102
+ go func () {
103
+ defer close (copyDone )
104
+ _ , err := io .Copy (w , ptty .Output ())
105
+ tpty .logf ("copy done: %v" , err )
106
+ tpty .logf ("closing out" )
107
+ err = out .closeErr (err )
108
+ tpty .logf ("closed out: %v" , err )
109
+ }()
110
+
111
+ // Log all output as part of test for easier debugging on errors.
105
112
go func () {
106
113
defer close (logDone )
107
114
s := bufio .NewScanner (logr )
@@ -116,13 +123,20 @@ func create(t *testing.T, ptty pty.PTY, name string) *PTY {
116
123
117
124
type PTY struct {
118
125
pty.PTY
119
- t * testing.T
120
- out * stdbuf
121
- name string
126
+ t * testing.T
127
+ close func (reason string ) error
128
+ out * stdbuf
129
+ name string
122
130
123
131
runeReader * bufio.Reader
124
132
}
125
133
134
+ func (p * PTY ) Close () error {
135
+ p .t .Helper ()
136
+
137
+ return p .close ("close" )
138
+ }
139
+
126
140
func (p * PTY ) ExpectMatch (str string ) string {
127
141
p .t .Helper ()
128
142
@@ -160,7 +174,7 @@ func (p *PTY) ExpectMatch(str string) string {
160
174
return buffer .String ()
161
175
case <- timeout .Done ():
162
176
// Ensure goroutine is cleaned up before test exit.
163
- _ = p .out . closeErr ( p . Close () )
177
+ _ = p .close ( "expect match timeout" )
164
178
<- match
165
179
166
180
p .fatalf ("match exceeded deadline" , "wanted %q; got %q" , str , buffer .String ())
@@ -190,30 +204,20 @@ func (p *PTY) WriteLine(str string) {
190
204
191
205
func (p * PTY ) logf (format string , args ... interface {}) {
192
206
p .t .Helper ()
193
- logf (p .t , p .name , format , args ... )
194
- }
195
-
196
- func (p * PTY ) fatalf (reason string , format string , args ... interface {}) {
197
- p .t .Helper ()
198
- fatalf (p .t , p .name , reason , format , args ... )
199
- }
200
-
201
- func logf (t * testing.T , name , format string , args ... interface {}) {
202
- t .Helper ()
203
207
204
208
// Match regular logger timestamp format, we seem to be logging in
205
209
// UTC in other places as well, so match here.
206
- t .Logf ("%s: %s: %s" , time .Now ().UTC ().Format ("2006-01-02 15:04:05.000" ), name , fmt .Sprintf (format , args ... ))
210
+ p . t .Logf ("%s: %s: %s" , time .Now ().UTC ().Format ("2006-01-02 15:04:05.000" ), p . name , fmt .Sprintf (format , args ... ))
207
211
}
208
212
209
- func fatalf ( t * testing. T , name , reason , format string , args ... interface {}) {
210
- t .Helper ()
213
+ func ( p * PTY ) fatalf ( reason string , format string , args ... interface {}) {
214
+ p . t .Helper ()
211
215
212
216
// Ensure the message is part of the normal log stream before
213
217
// failing the test.
214
- logf (t , name , "%s: %s" , reason , fmt .Sprintf (format , args ... ))
218
+ p . logf ("%s: %s" , reason , fmt .Sprintf (format , args ... ))
215
219
216
- require .FailNowf (t , reason , format , args ... )
220
+ require .FailNowf (p . t , reason , format , args ... )
217
221
}
218
222
219
223
// stdbuf is like a buffered stdout, it buffers writes until read.
0 commit comments