Skip to content

Commit 6333725

Browse files
aclementsgopherbot
authored andcommitted
cmd/dist: flush incomplete lines in -json mode
Currently, if a test prints an incomplete line and then exits, in JSON mode, the filter we use to rewrite Package lines will keep the last incomplete line in an internal buffer and never print it. In theory this should never happen anyway because the test should only write JSON to stdout, but we try pretty hard to pass through any non-JSON, so it seems inconsistent to swallow incomplete lines. Fix this by adding a testJSONFilter.Flush method and calling it in the right places. Unfortunately this is a bit tricky because the filter is constructed pretty far from where we run the exec.Cmd, so we return the flush function through the various layers in order to route it to the place where we call Cmd.Run. Updates #37486. Change-Id: I38af67e8ad23458598a32fd428779bb0ec21ac3c Reviewed-on: https://go-review.googlesource.com/c/go/+/496516 Run-TryBot: Austin Clements <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Auto-Submit: Austin Clements <[email protected]> Reviewed-by: Bryan Mills <[email protected]>
1 parent e6fb190 commit 6333725

File tree

3 files changed

+35
-13
lines changed

3 files changed

+35
-13
lines changed

src/cmd/dist/test.go

+24-11
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ type tester struct {
8686
type work struct {
8787
dt *distTest
8888
cmd *exec.Cmd // Must write stdout/stderr to work.out
89+
flush func() // If non-nil, called after cmd.Run
8990
start chan bool
9091
out bytes.Buffer
9192
err error
@@ -326,9 +327,10 @@ type goTest struct {
326327
testFlags []string // Additional flags accepted by this test
327328
}
328329

329-
// bgCommand returns a go test Cmd. The result will write its output to stdout
330-
// and stderr. If stdout==stderr, bgCommand ensures Writes are serialized.
331-
func (opts *goTest) bgCommand(t *tester, stdout, stderr io.Writer) *exec.Cmd {
330+
// bgCommand returns a go test Cmd and a post-Run flush function. The result
331+
// will write its output to stdout and stderr. If stdout==stderr, bgCommand
332+
// ensures Writes are serialized. The caller should call flush() after Cmd exits.
333+
func (opts *goTest) bgCommand(t *tester, stdout, stderr io.Writer) (cmd *exec.Cmd, flush func()) {
332334
goCmd, build, run, pkgs, testFlags, setupCmd := opts.buildArgs(t)
333335

334336
// Combine the flags.
@@ -343,7 +345,7 @@ func (opts *goTest) bgCommand(t *tester, stdout, stderr io.Writer) *exec.Cmd {
343345
args = append(args, testFlags...)
344346
}
345347

346-
cmd := exec.Command(goCmd, args...)
348+
cmd = exec.Command(goCmd, args...)
347349
setupCmd(cmd)
348350
if t.json && opts.variant != "" && !opts.sharded {
349351
// Rewrite Package in the JSON output to be pkg:variant. For sharded
@@ -364,22 +366,29 @@ func (opts *goTest) bgCommand(t *tester, stdout, stderr io.Writer) *exec.Cmd {
364366
stdout = &lockedWriter{w: stdout}
365367
stderr = stdout
366368
}
367-
cmd.Stdout = &testJSONFilter{w: stdout, variant: opts.variant}
369+
f := &testJSONFilter{w: stdout, variant: opts.variant}
370+
cmd.Stdout = f
371+
flush = f.Flush
368372
} else {
369373
cmd.Stdout = stdout
374+
flush = func() {}
370375
}
371376
cmd.Stderr = stderr
372377

373-
return cmd
378+
return cmd, flush
374379
}
375380

376-
// command returns a go test Cmd intended to be run immediately.
377-
func (opts *goTest) command(t *tester) *exec.Cmd {
381+
// command returns a go test Cmd intended to be run immediately and a flush
382+
// function to call after it has run.
383+
func (opts *goTest) command(t *tester) (*exec.Cmd, func()) {
378384
return opts.bgCommand(t, os.Stdout, os.Stderr)
379385
}
380386

381387
func (opts *goTest) run(t *tester) error {
382-
return opts.command(t).Run()
388+
cmd, flush := opts.command(t)
389+
err := cmd.Run()
390+
flush()
391+
return err
383392
}
384393

385394
// buildArgs is in internal helper for goTest that constructs the elements of
@@ -742,13 +751,14 @@ func (t *tester) registerTests() {
742751

743752
// Run `go test fmt` in the moved GOROOT, without explicitly setting
744753
// GOROOT in the environment. The 'go' command should find itself.
745-
cmd := (&goTest{
754+
cmd, flush := (&goTest{
746755
variant: "moved_goroot",
747756
goroot: moved,
748757
pkg: "fmt",
749758
}).command(t)
750759
unsetEnv(cmd, "GOROOT")
751760
err := cmd.Run()
761+
flush()
752762

753763
if rerr := os.Rename(moved, goroot); rerr != nil {
754764
fatalf("failed to restore GOROOT: %v", rerr)
@@ -936,7 +946,7 @@ func (t *tester) registerTest(name, heading string, test *goTest, opts ...regist
936946
}
937947
}
938948
w := &work{dt: dt}
939-
w.cmd = test.bgCommand(t, &w.out, &w.out)
949+
w.cmd, w.flush = test.bgCommand(t, &w.out, &w.out)
940950
t.worklist = append(t.worklist, w)
941951
return nil
942952
})
@@ -1255,6 +1265,9 @@ func (t *tester) runPending(nextTest *distTest) {
12551265
} else {
12561266
timelog("start", w.dt.name)
12571267
w.err = w.cmd.Run()
1268+
if w.flush != nil {
1269+
w.flush()
1270+
}
12581271
if w.err != nil {
12591272
if isUnsupportedVMASize(w) {
12601273
timelog("skip", w.dt.name)

src/cmd/dist/testjson.go

+8
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,14 @@ func (f *testJSONFilter) Write(b []byte) (int, error) {
6262
return bn, nil
6363
}
6464

65+
func (f *testJSONFilter) Flush() {
66+
// Write any remaining partial line to the underlying writer.
67+
if f.lineBuf.Len() > 0 {
68+
f.w.Write(f.lineBuf.Bytes())
69+
f.lineBuf.Reset()
70+
}
71+
}
72+
6573
func (f *testJSONFilter) process(line []byte) {
6674
if len(line) > 0 && line[0] == '{' {
6775
// Plausible test2json output. Parse it generically.

src/cmd/dist/testjson_test.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,13 @@ func TestJSONFilterMalformed(t *testing.T) {
2727
more text
2828
{"Package":"abc"}trailing text
2929
{not json}
30-
`
30+
no newline`
3131
const want = `unexpected text
3232
{"Package":"abc:variant"}
3333
more text
3434
{"Package":"abc:variant"}trailing text
3535
{not json}
36-
`
36+
no newline`
3737
checkJSONFilter(t, in, want)
3838
}
3939

@@ -77,6 +77,7 @@ func checkJSONFilterWith(t *testing.T, want string, write func(*testJSONFilter))
7777
out := new(strings.Builder)
7878
f := &testJSONFilter{w: out, variant: "variant"}
7979
write(f)
80+
f.Flush()
8081
got := out.String()
8182
if want != got {
8283
t.Errorf("want:\n%s\ngot:\n%s", want, got)

0 commit comments

Comments
 (0)