From 9182664845affa6715c39d38c14c396ab8f907c7 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Thu, 30 Mar 2023 21:35:18 -0700 Subject: [PATCH] testing: make test output unbuffered when verbose Fixes #3579 --- main.go | 18 ++++++++++------- src/testing/testing.go | 44 ++++++++++++++++++++++++++++++------------ 2 files changed, 43 insertions(+), 19 deletions(-) diff --git a/main.go b/main.go index 74aa0f1f..435708bb 100644 --- a/main.go +++ b/main.go @@ -249,10 +249,16 @@ func Test(pkgName string, stdout, stderr io.Writer, options *compileopts.Options flags = append(flags, "-test.count="+strconv.Itoa(testConfig.Count)) } - buf := bytes.Buffer{} + var buf bytes.Buffer + var output io.Writer = &buf + // Send the test output to stdout if -v or -bench + if testConfig.Verbose || testConfig.BenchRegexp != "" { + output = os.Stdout + } + passed := false var duration time.Duration - result, err := buildAndRun(pkgName, config, &buf, flags, nil, 0, func(cmd *exec.Cmd, result builder.BuildResult) error { + result, err := buildAndRun(pkgName, config, output, flags, nil, 0, func(cmd *exec.Cmd, result builder.BuildResult) error { if testConfig.CompileOnly || outpath != "" { // Write test binary to the specified file name. if outpath == "" { @@ -310,11 +316,9 @@ func Test(pkgName string, stdout, stderr io.Writer, options *compileopts.Options duration = time.Since(start) passed = err == nil - // print the test output if - // 1) the tests passed and in verbose mode - // 2) the tests failed - // 3) running benchmarks - if (passed && testConfig.Verbose) || (!passed) || (testConfig.BenchRegexp != "") { + // if verbose or benchmarks, then output is already going to stdout + // However, if we failed and weren't printing to stdout, print the output we accumulated. + if !passed && output != os.Stdout { buf.WriteTo(stdout) } diff --git a/src/testing/testing.go b/src/testing/testing.go index 76ff7e69..26cd53eb 100644 --- a/src/testing/testing.go +++ b/src/testing/testing.go @@ -13,6 +13,7 @@ import ( "errors" "flag" "fmt" + "io" "io/fs" "os" "strings" @@ -49,7 +50,7 @@ func Init() { // common holds the elements common between T and B and // captures common methods such as Errorf. type common struct { - output bytes.Buffer + output *logger indent string ran bool // Test or benchmark (or one of its subtests) was executed. failed bool // Test or benchmark has failed. @@ -70,6 +71,27 @@ type common struct { tempDirSeq int32 } +type logger struct { + logToStdout bool + b bytes.Buffer +} + +func (l *logger) Write(p []byte) (int, error) { + if l.logToStdout { + return os.Stdout.Write(p) + } + return l.b.Write(p) +} + +func (l *logger) WriteTo(w io.Writer) (int64, error) { + if l.logToStdout { + // We've already been logging to stdout; nothing to do. + return 0, nil + } + return l.b.WriteTo(w) + +} + // Short reports whether the -test.short flag is set. func Short() bool { return flagShort @@ -95,8 +117,8 @@ func (c *common) flushToParent(testName, format string, args ...interface{}) { // Not quite sure how this works upstream. c.output.WriteTo(os.Stdout) } else { - fmt.Fprintf(&c.parent.output, format, args...) - c.output.WriteTo(&c.parent.output) + fmt.Fprintf(c.parent.output, format, args...) + c.output.WriteTo(c.parent.output) } } @@ -178,16 +200,10 @@ func (c *common) log(s string) { } lines := strings.Split(s, "\n") // First line. - c.output.WriteString(c.indent) - c.output.WriteString(" ") // 4 spaces - c.output.WriteString(lines[0]) - c.output.WriteByte('\n') + fmt.Fprintf(c.output, "%s %s\n", c.indent, lines[0]) // More lines. for _, line := range lines[1:] { - c.output.WriteString(c.indent) - c.output.WriteString(" ") // 8 spaces - c.output.WriteString(line) - c.output.WriteByte('\n') + fmt.Fprintf(c.output, "%s %s\n", c.indent, line) } } @@ -409,6 +425,7 @@ func (t *T) Run(name string, f func(t *T)) bool { // Create a subtest. sub := T{ common: common{ + output: &logger{logToStdout: flagVerbose}, name: testName, parent: &t.common, level: t.level + 1, @@ -419,7 +436,7 @@ func (t *T) Run(name string, f func(t *T)) bool { sub.indent = sub.indent + " " } if flagVerbose { - fmt.Fprintf(&t.output, "=== RUN %s\n", sub.name) + fmt.Fprintf(t.output, "=== RUN %s\n", sub.name) } tRunner(&sub, f) @@ -484,6 +501,9 @@ func runTests(matchString func(pat, str string) (bool, error), tests []InternalT ctx := newTestContext(newMatcher(matchString, flagRunRegexp, "-test.run")) t := &T{ + common: common{ + output: &logger{logToStdout: flagVerbose}, + }, context: ctx, }