blob: 991eca04e18c9ea6aa1b0c3cc17a03901e675514 [file] [log] [blame]
Colin Crossdde49cb2019-06-08 21:59:42 -07001// Copyright 2018 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package terminal
16
17import (
18 "bytes"
19 "fmt"
Colin Cross3dac80e2019-06-11 11:19:06 -070020 "os"
Colin Cross4355ee62019-06-11 23:01:36 -070021 "syscall"
Colin Crossdde49cb2019-06-08 21:59:42 -070022 "testing"
23
24 "android/soong/ui/status"
25)
26
27func TestStatusOutput(t *testing.T) {
28 tests := []struct {
Patrice Arrudaf445ba12020-07-28 17:49:01 +000029 name string
30 calls func(stat status.StatusOutput)
31 smart string
32 simple string
Colin Crossdde49cb2019-06-08 21:59:42 -070033 }{
34 {
Patrice Arrudaf445ba12020-07-28 17:49:01 +000035 name: "two actions",
36 calls: twoActions,
37 smart: "\r\x1b[1m[ 0% 0/2] action1\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action1\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action2\x1b[0m\x1b[K\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\n",
38 simple: "[ 50% 1/2] action1\n[100% 2/2] action2\n",
Colin Crossdde49cb2019-06-08 21:59:42 -070039 },
40 {
Patrice Arrudaf445ba12020-07-28 17:49:01 +000041 name: "two parallel actions",
42 calls: twoParallelActions,
43 smart: "\r\x1b[1m[ 0% 0/2] action1\x1b[0m\x1b[K\r\x1b[1m[ 0% 0/2] action2\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action1\x1b[0m\x1b[K\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\n",
44 simple: "[ 50% 1/2] action1\n[100% 2/2] action2\n",
Colin Crossdde49cb2019-06-08 21:59:42 -070045 },
46 {
Patrice Arrudaf445ba12020-07-28 17:49:01 +000047 name: "action with output",
48 calls: actionsWithOutput,
49 smart: "\r\x1b[1m[ 0% 0/3] action1\x1b[0m\x1b[K\r\x1b[1m[ 33% 1/3] action1\x1b[0m\x1b[K\r\x1b[1m[ 33% 1/3] action2\x1b[0m\x1b[K\r\x1b[1m[ 66% 2/3] action2\x1b[0m\x1b[K\noutput1\noutput2\n\r\x1b[1m[ 66% 2/3] action3\x1b[0m\x1b[K\r\x1b[1m[100% 3/3] action3\x1b[0m\x1b[K\n",
50 simple: "[ 33% 1/3] action1\n[ 66% 2/3] action2\noutput1\noutput2\n[100% 3/3] action3\n",
Colin Crossdde49cb2019-06-08 21:59:42 -070051 },
52 {
Patrice Arrudaf445ba12020-07-28 17:49:01 +000053 name: "action with output without newline",
54 calls: actionsWithOutputWithoutNewline,
55 smart: "\r\x1b[1m[ 0% 0/3] action1\x1b[0m\x1b[K\r\x1b[1m[ 33% 1/3] action1\x1b[0m\x1b[K\r\x1b[1m[ 33% 1/3] action2\x1b[0m\x1b[K\r\x1b[1m[ 66% 2/3] action2\x1b[0m\x1b[K\noutput1\noutput2\n\r\x1b[1m[ 66% 2/3] action3\x1b[0m\x1b[K\r\x1b[1m[100% 3/3] action3\x1b[0m\x1b[K\n",
56 simple: "[ 33% 1/3] action1\n[ 66% 2/3] action2\noutput1\noutput2\n[100% 3/3] action3\n",
Colin Crossdde49cb2019-06-08 21:59:42 -070057 },
58 {
Patrice Arrudaf445ba12020-07-28 17:49:01 +000059 name: "action with error",
60 calls: actionsWithError,
cherokeeMetaf7119b52024-07-11 16:52:18 -070061 smart: "\r\x1b[1m[ 0% 0/3] action1\x1b[0m\x1b[K\r\x1b[1m[ 33% 1/3] action1\x1b[0m\x1b[K\r\x1b[1m[ 33% 1/3] action2\x1b[0m\x1b[K\r\x1b[1m[ 66% 2/3] action2\x1b[0m\x1b[K\n\x1b[31m\x1b[1mFAILED:\x1b[0m f1 f2\ntouch f1 f2\nerror1\nerror2\n\r\x1b[1m[ 66% 2/3] action3\x1b[0m\x1b[K\r\x1b[1m[100% 3/3] action3\x1b[0m\x1b[K\n",
Patrice Arrudaf445ba12020-07-28 17:49:01 +000062 simple: "[ 33% 1/3] action1\n[ 66% 2/3] action2\nFAILED: f1 f2\ntouch f1 f2\nerror1\nerror2\n[100% 3/3] action3\n",
Colin Crossdde49cb2019-06-08 21:59:42 -070063 },
64 {
Patrice Arrudaf445ba12020-07-28 17:49:01 +000065 name: "action with empty description",
66 calls: actionWithEmptyDescription,
67 smart: "\r\x1b[1m[ 0% 0/1] command1\x1b[0m\x1b[K\r\x1b[1m[100% 1/1] command1\x1b[0m\x1b[K\n",
68 simple: "[100% 1/1] command1\n",
Colin Crossdde49cb2019-06-08 21:59:42 -070069 },
70 {
Patrice Arrudaf445ba12020-07-28 17:49:01 +000071 name: "messages",
72 calls: actionsWithMessages,
cherokeeMetaf7119b52024-07-11 16:52:18 -070073 smart: "\r\x1b[1m[ 0% 0/2] action1\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action1\x1b[0m\x1b[K\r\x1b[1mstatus\x1b[0m\x1b[K\r\x1b[Kprint\n\x1b[31m\x1b[1mFAILED:\x1b[0m error\n\r\x1b[1m[ 50% 1/2] action2\x1b[0m\x1b[K\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\n",
Patrice Arrudaf445ba12020-07-28 17:49:01 +000074 simple: "[ 50% 1/2] action1\nstatus\nprint\nFAILED: error\n[100% 2/2] action2\n",
Colin Crossdde49cb2019-06-08 21:59:42 -070075 },
76 {
Patrice Arrudaf445ba12020-07-28 17:49:01 +000077 name: "action with long description",
78 calls: actionWithLongDescription,
79 smart: "\r\x1b[1m[ 0% 0/2] action with very long descrip\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action with very long descrip\x1b[0m\x1b[K\n",
80 simple: "[ 50% 1/2] action with very long description to test eliding\n",
Colin Crossdde49cb2019-06-08 21:59:42 -070081 },
82 {
Patrice Arrudaf445ba12020-07-28 17:49:01 +000083 name: "action with output with ansi codes",
Dan Willemsene2cdecf2022-06-08 20:32:07 -070084 calls: actionWithOutputWithAnsiCodes,
85 smart: "\r\x1b[1m[ 0% 0/1] action1\x1b[0m\x1b[K\r\x1b[1m[100% 1/1] action1\x1b[0m\x1b[K\n\x1b[31mcolor\x1b[0m\n\x1b[31mcolor message\x1b[0m\n",
86 simple: "[100% 1/1] action1\ncolor\ncolor message\n",
Colin Crossdde49cb2019-06-08 21:59:42 -070087 },
88 }
89
Colin Cross3dac80e2019-06-11 11:19:06 -070090 os.Setenv(tableHeightEnVar, "")
91
Colin Crossdde49cb2019-06-08 21:59:42 -070092 for _, tt := range tests {
93 t.Run(tt.name, func(t *testing.T) {
Colin Cross3dac80e2019-06-11 11:19:06 -070094
Colin Crossdde49cb2019-06-08 21:59:42 -070095 t.Run("smart", func(t *testing.T) {
96 smart := &fakeSmartTerminal{termWidth: 40}
Colin Cross3c0fe0e2021-02-10 13:11:18 -080097 stat := NewStatusOutput(smart, "", false, false, false)
Colin Crossdde49cb2019-06-08 21:59:42 -070098 tt.calls(stat)
99 stat.Flush()
Colin Crossdde49cb2019-06-08 21:59:42 -0700100
101 if g, w := smart.String(), tt.smart; g != w {
102 t.Errorf("want:\n%q\ngot:\n%q", w, g)
103 }
104 })
105
Patrice Arrudaf445ba12020-07-28 17:49:01 +0000106 t.Run("simple", func(t *testing.T) {
107 simple := &bytes.Buffer{}
Colin Cross3c0fe0e2021-02-10 13:11:18 -0800108 stat := NewStatusOutput(simple, "", false, false, false)
Colin Crossdde49cb2019-06-08 21:59:42 -0700109 tt.calls(stat)
110 stat.Flush()
Colin Crossdde49cb2019-06-08 21:59:42 -0700111
Patrice Arrudaf445ba12020-07-28 17:49:01 +0000112 if g, w := simple.String(), tt.simple; g != w {
Colin Crossdde49cb2019-06-08 21:59:42 -0700113 t.Errorf("want:\n%q\ngot:\n%q", w, g)
114 }
115 })
Colin Crossc0b9f6b2019-09-23 12:44:54 -0700116
Patrice Arrudaf445ba12020-07-28 17:49:01 +0000117 t.Run("force simple", func(t *testing.T) {
Colin Crossc0b9f6b2019-09-23 12:44:54 -0700118 smart := &fakeSmartTerminal{termWidth: 40}
Colin Cross3c0fe0e2021-02-10 13:11:18 -0800119 stat := NewStatusOutput(smart, "", true, false, false)
Colin Crossc0b9f6b2019-09-23 12:44:54 -0700120 tt.calls(stat)
121 stat.Flush()
122
Patrice Arrudaf445ba12020-07-28 17:49:01 +0000123 if g, w := smart.String(), tt.simple; g != w {
Colin Crossc0b9f6b2019-09-23 12:44:54 -0700124 t.Errorf("want:\n%q\ngot:\n%q", w, g)
125 }
126 })
Colin Crossdde49cb2019-06-08 21:59:42 -0700127 })
128 }
129}
130
131type runner struct {
132 counts status.Counts
133 stat status.StatusOutput
134}
135
136func newRunner(stat status.StatusOutput, totalActions int) *runner {
137 return &runner{
138 counts: status.Counts{TotalActions: totalActions},
139 stat: stat,
140 }
141}
142
143func (r *runner) startAction(action *status.Action) {
144 r.counts.StartedActions++
145 r.counts.RunningActions++
146 r.stat.StartAction(action, r.counts)
147}
148
149func (r *runner) finishAction(result status.ActionResult) {
150 r.counts.FinishedActions++
151 r.counts.RunningActions--
152 r.stat.FinishAction(result, r.counts)
153}
154
155func (r *runner) finishAndStartAction(result status.ActionResult, action *status.Action) {
156 r.counts.FinishedActions++
157 r.stat.FinishAction(result, r.counts)
158
159 r.counts.StartedActions++
160 r.stat.StartAction(action, r.counts)
161}
162
163var (
164 action1 = &status.Action{Description: "action1"}
165 result1 = status.ActionResult{Action: action1}
166 action2 = &status.Action{Description: "action2"}
167 result2 = status.ActionResult{Action: action2}
168 action3 = &status.Action{Description: "action3"}
169 result3 = status.ActionResult{Action: action3}
170)
171
172func twoActions(stat status.StatusOutput) {
173 runner := newRunner(stat, 2)
174 runner.startAction(action1)
175 runner.finishAction(result1)
176 runner.startAction(action2)
177 runner.finishAction(result2)
178}
179
180func twoParallelActions(stat status.StatusOutput) {
181 runner := newRunner(stat, 2)
182 runner.startAction(action1)
183 runner.startAction(action2)
184 runner.finishAction(result1)
185 runner.finishAction(result2)
186}
187
188func actionsWithOutput(stat status.StatusOutput) {
189 result2WithOutput := status.ActionResult{Action: action2, Output: "output1\noutput2\n"}
190
191 runner := newRunner(stat, 3)
192 runner.startAction(action1)
193 runner.finishAction(result1)
194 runner.startAction(action2)
195 runner.finishAction(result2WithOutput)
196 runner.startAction(action3)
197 runner.finishAction(result3)
198}
199
200func actionsWithOutputWithoutNewline(stat status.StatusOutput) {
201 result2WithOutputWithoutNewline := status.ActionResult{Action: action2, Output: "output1\noutput2"}
202
203 runner := newRunner(stat, 3)
204 runner.startAction(action1)
205 runner.finishAction(result1)
206 runner.startAction(action2)
207 runner.finishAction(result2WithOutputWithoutNewline)
208 runner.startAction(action3)
209 runner.finishAction(result3)
210}
211
212func actionsWithError(stat status.StatusOutput) {
213 action2WithError := &status.Action{Description: "action2", Outputs: []string{"f1", "f2"}, Command: "touch f1 f2"}
214 result2WithError := status.ActionResult{Action: action2WithError, Output: "error1\nerror2\n", Error: fmt.Errorf("error1")}
215
216 runner := newRunner(stat, 3)
217 runner.startAction(action1)
218 runner.finishAction(result1)
219 runner.startAction(action2WithError)
220 runner.finishAction(result2WithError)
221 runner.startAction(action3)
222 runner.finishAction(result3)
223}
224
225func actionWithEmptyDescription(stat status.StatusOutput) {
226 action1 := &status.Action{Command: "command1"}
227 result1 := status.ActionResult{Action: action1}
228
229 runner := newRunner(stat, 1)
230 runner.startAction(action1)
231 runner.finishAction(result1)
232}
233
234func actionsWithMessages(stat status.StatusOutput) {
235 runner := newRunner(stat, 2)
236
237 runner.startAction(action1)
238 runner.finishAction(result1)
239
240 stat.Message(status.VerboseLvl, "verbose")
241 stat.Message(status.StatusLvl, "status")
242 stat.Message(status.PrintLvl, "print")
243 stat.Message(status.ErrorLvl, "error")
244
245 runner.startAction(action2)
246 runner.finishAction(result2)
247}
248
249func actionWithLongDescription(stat status.StatusOutput) {
250 action1 := &status.Action{Description: "action with very long description to test eliding"}
251 result1 := status.ActionResult{Action: action1}
252
253 runner := newRunner(stat, 2)
254
255 runner.startAction(action1)
256
257 runner.finishAction(result1)
258}
259
Dan Willemsene2cdecf2022-06-08 20:32:07 -0700260func actionWithOutputWithAnsiCodes(stat status.StatusOutput) {
Colin Crossdde49cb2019-06-08 21:59:42 -0700261 result1WithOutputWithAnsiCodes := status.ActionResult{Action: action1, Output: "\x1b[31mcolor\x1b[0m"}
262
263 runner := newRunner(stat, 1)
264 runner.startAction(action1)
265 runner.finishAction(result1WithOutputWithAnsiCodes)
Dan Willemsene2cdecf2022-06-08 20:32:07 -0700266
267 stat.Message(status.PrintLvl, "\x1b[31mcolor message\x1b[0m")
Colin Crossdde49cb2019-06-08 21:59:42 -0700268}
269
270func TestSmartStatusOutputWidthChange(t *testing.T) {
Colin Cross3dac80e2019-06-11 11:19:06 -0700271 os.Setenv(tableHeightEnVar, "")
272
Colin Crossdde49cb2019-06-08 21:59:42 -0700273 smart := &fakeSmartTerminal{termWidth: 40}
Colin Cross3c0fe0e2021-02-10 13:11:18 -0800274 stat := NewStatusOutput(smart, "", false, false, false)
Colin Cross4355ee62019-06-11 23:01:36 -0700275 smartStat := stat.(*smartStatusOutput)
276 smartStat.sigwinchHandled = make(chan bool)
Colin Crossdde49cb2019-06-08 21:59:42 -0700277
278 runner := newRunner(stat, 2)
279
280 action := &status.Action{Description: "action with very long description to test eliding"}
281 result := status.ActionResult{Action: action}
282
283 runner.startAction(action)
284 smart.termWidth = 30
Colin Cross4355ee62019-06-11 23:01:36 -0700285 // Fake a SIGWINCH
286 smartStat.sigwinch <- syscall.SIGWINCH
287 <-smartStat.sigwinchHandled
Colin Crossdde49cb2019-06-08 21:59:42 -0700288 runner.finishAction(result)
289
290 stat.Flush()
Colin Crossdde49cb2019-06-08 21:59:42 -0700291
Colin Cross00bdfd82019-06-11 11:16:23 -0700292 w := "\r\x1b[1m[ 0% 0/2] action with very long descrip\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action with very lo\x1b[0m\x1b[K\n"
Colin Crossdde49cb2019-06-08 21:59:42 -0700293
294 if g := smart.String(); g != w {
295 t.Errorf("want:\n%q\ngot:\n%q", w, g)
296 }
297}
Joe Onoratoeadb0fb2023-06-24 15:03:28 -0700298
299func TestSmartStatusDoesntHideAfterSucecss(t *testing.T) {
300 os.Setenv(tableHeightEnVar, "")
301
302 smart := &fakeSmartTerminal{termWidth: 40}
303 stat := NewStatusOutput(smart, "", false, false, false)
304 smartStat := stat.(*smartStatusOutput)
305 smartStat.sigwinchHandled = make(chan bool)
306
307 runner := newRunner(stat, 2)
308
309 action1 := &status.Action{Description: "action1"}
310 result1 := status.ActionResult{
311 Action: action1,
312 Output: "Output1",
313 }
314
315 action2 := &status.Action{Description: "action2"}
316 result2 := status.ActionResult{
317 Action: action2,
318 Output: "Output2",
319 }
320
321 runner.startAction(action1)
322 runner.startAction(action2)
323 runner.finishAction(result1)
324 runner.finishAction(result2)
325
326 stat.Flush()
327
328 w := "\r\x1b[1m[ 0% 0/2] action1\x1b[0m\x1b[K\r\x1b[1m[ 0% 0/2] action2\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action1\x1b[0m\x1b[K\nOutput1\n\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\nOutput2\n"
329
330 if g := smart.String(); g != w {
331 t.Errorf("want:\n%q\ngot:\n%q", w, g)
332 }
333}
334
335func TestSmartStatusHideAfterFailure(t *testing.T) {
336 os.Setenv(tableHeightEnVar, "")
337
338 smart := &fakeSmartTerminal{termWidth: 40}
339 stat := NewStatusOutput(smart, "", false, false, false)
340 smartStat := stat.(*smartStatusOutput)
341 smartStat.sigwinchHandled = make(chan bool)
342
343 runner := newRunner(stat, 2)
344
345 action1 := &status.Action{Description: "action1"}
346 result1 := status.ActionResult{
347 Action: action1,
348 Output: "Output1",
349 Error: fmt.Errorf("Error1"),
350 }
351
352 action2 := &status.Action{Description: "action2"}
353 result2 := status.ActionResult{
354 Action: action2,
355 Output: "Output2",
356 }
357
358 runner.startAction(action1)
359 runner.startAction(action2)
360 runner.finishAction(result1)
361 runner.finishAction(result2)
362
363 stat.Flush()
364
cherokeeMetaf7119b52024-07-11 16:52:18 -0700365 w := "\r\x1b[1m[ 0% 0/2] action1\x1b[0m\x1b[K\r\x1b[1m[ 0% 0/2] action2\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action1\x1b[0m\x1b[K\n\x1b[31m\x1b[1mFAILED:\x1b[0m \nOutput1\n\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\nThere was 1 action that completed after the action that failed. See verbose.log.gz for its output.\n"
Joe Onoratoeadb0fb2023-06-24 15:03:28 -0700366
367 if g := smart.String(); g != w {
368 t.Errorf("want:\n%q\ngot:\n%q", w, g)
369 }
370}
371
372func TestSmartStatusHideAfterFailurePlural(t *testing.T) {
373 os.Setenv(tableHeightEnVar, "")
374
375 smart := &fakeSmartTerminal{termWidth: 40}
376 stat := NewStatusOutput(smart, "", false, false, false)
377 smartStat := stat.(*smartStatusOutput)
378 smartStat.sigwinchHandled = make(chan bool)
379
380 runner := newRunner(stat, 2)
381
382 action1 := &status.Action{Description: "action1"}
383 result1 := status.ActionResult{
384 Action: action1,
385 Output: "Output1",
386 Error: fmt.Errorf("Error1"),
387 }
388
389 action2 := &status.Action{Description: "action2"}
390 result2 := status.ActionResult{
391 Action: action2,
392 Output: "Output2",
393 }
394
395 action3 := &status.Action{Description: "action3"}
396 result3 := status.ActionResult{
397 Action: action3,
398 Output: "Output3",
399 }
400
401 runner.startAction(action1)
402 runner.startAction(action2)
403 runner.startAction(action3)
404 runner.finishAction(result1)
405 runner.finishAction(result2)
406 runner.finishAction(result3)
407
408 stat.Flush()
409
cherokeeMetaf7119b52024-07-11 16:52:18 -0700410 w := "\r\x1b[1m[ 0% 0/2] action1\x1b[0m\x1b[K\r\x1b[1m[ 0% 0/2] action2\x1b[0m\x1b[K\r\x1b[1m[ 0% 0/2] action3\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action1\x1b[0m\x1b[K\n\x1b[31m\x1b[1mFAILED:\x1b[0m \nOutput1\n\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\r\x1b[1m[150% 3/2] action3\x1b[0m\x1b[K\nThere were 2 actions that completed after the action that failed. See verbose.log.gz for their output.\n"
Joe Onoratoeadb0fb2023-06-24 15:03:28 -0700411
412 if g := smart.String(); g != w {
413 t.Errorf("want:\n%q\ngot:\n%q", w, g)
414 }
415}
416
417func TestSmartStatusDontHideErrorAfterFailure(t *testing.T) {
418 os.Setenv(tableHeightEnVar, "")
419
420 smart := &fakeSmartTerminal{termWidth: 40}
421 stat := NewStatusOutput(smart, "", false, false, false)
422 smartStat := stat.(*smartStatusOutput)
423 smartStat.sigwinchHandled = make(chan bool)
424
425 runner := newRunner(stat, 2)
426
427 action1 := &status.Action{Description: "action1"}
428 result1 := status.ActionResult{
429 Action: action1,
430 Output: "Output1",
431 Error: fmt.Errorf("Error1"),
432 }
433
434 action2 := &status.Action{Description: "action2"}
435 result2 := status.ActionResult{
436 Action: action2,
437 Output: "Output2",
438 Error: fmt.Errorf("Error1"),
439 }
440
441 runner.startAction(action1)
442 runner.startAction(action2)
443 runner.finishAction(result1)
444 runner.finishAction(result2)
445
446 stat.Flush()
447
cherokeeMetaf7119b52024-07-11 16:52:18 -0700448 w := "\r\x1b[1m[ 0% 0/2] action1\x1b[0m\x1b[K\r\x1b[1m[ 0% 0/2] action2\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action1\x1b[0m\x1b[K\n\x1b[31m\x1b[1mFAILED:\x1b[0m \nOutput1\n\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\n\x1b[31m\x1b[1mFAILED:\x1b[0m \nOutput2\n"
Joe Onoratoeadb0fb2023-06-24 15:03:28 -0700449
450 if g := smart.String(); g != w {
451 t.Errorf("want:\n%q\ngot:\n%q", w, g)
452 }
453}