tg.grepStderrNot("compile.* -N .*-p fmt", "incorrectly built fmt with -N flag")
}
-// Issue 22644
func TestGoTestMinusN(t *testing.T) {
// Intent here is to verify that 'go test -n' works without crashing.
// This reuses flag_test.go, but really any test would do.
defer tg.cleanup()
tg.run("test", "testdata/flag_test.go", "-n", "-args", "-v=7")
}
+
+func TestGoTestJSON(t *testing.T) {
+ tg := testgo(t)
+ defer tg.cleanup()
+ tg.parallel()
+ tg.makeTempdir()
+ tg.setenv("GOCACHE", tg.tempdir)
+ tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
+
+ // Test that math and fmt output is interlaced.
+ if runtime.GOMAXPROCS(-1) < 2 {
+ tg.setenv("GOMAXPROCS", "2")
+ }
+ // This has the potential to be a flaky test.
+ // Probably the first try will work, but the second try should have
+ // both tests equally cached and should definitely work.
+ for try := 0; ; try++ {
+ tg.run("test", "-json", "-short", "-v", "sleepy1", "sleepy2")
+ state := 0
+ for _, line := range strings.Split(tg.getStdout(), "\n") {
+ if state == 0 && strings.Contains(line, `"Package":"sleepy1"`) {
+ state = 1
+ }
+ if state == 1 && strings.Contains(line, `"Package":"sleepy2"`) {
+ state = 2
+ }
+ if state == 2 && strings.Contains(line, `"Package":"sleepy1"`) {
+ state = 3
+ break
+ }
+ }
+ if state != 3 {
+ if try < 1 {
+ continue
+ }
+ t.Fatalf("did not find fmt interlaced with math")
+ }
+ break
+ }
+}
"regexp"
"sort"
"strings"
+ "sync"
"text/template"
"time"
"unicode"
"cmd/go/internal/load"
"cmd/go/internal/str"
"cmd/go/internal/work"
+ "cmd/internal/test2json"
)
// Break init loop.
testO string // -o flag
testProfile bool // some profiling flag
testNeedBinary bool // profile needs to keep binary around
+ testJSON bool // -json flag
testV bool // -v flag
testTimeout string // -timeout flag
testArgs []string
id2 cache.ActionID
}
+// stdoutMu and lockedStdout provide a locked standard output
+// that guarantees never to interlace writes from multiple
+// goroutines, so that we can have multiple JSON streams writing
+// to a lockedStdout simultaneously and know that events will
+// still be intelligible.
+var stdoutMu sync.Mutex
+
+type lockedStdout struct{}
+
+func (lockedStdout) Write(b []byte) (int, error) {
+ stdoutMu.Lock()
+ defer stdoutMu.Unlock()
+ return os.Stdout.Write(b)
+}
+
// builderRunTest is the action for running a test binary.
func (c *runCache) builderRunTest(b *work.Builder, a *work.Action) error {
if c.buf == nil {
cmd.Dir = a.Package.Dir
cmd.Env = base.EnvForDir(cmd.Dir, cfg.OrigEnv)
var buf bytes.Buffer
+ var stdout io.Writer = os.Stdout
+ if testJSON {
+ json := test2json.NewConverter(lockedStdout{}, a.Package.ImportPath, test2json.Timestamp)
+ defer json.Close()
+ stdout = json
+ }
if len(pkgArgs) == 0 || testBench {
// Stream test output (no buffering) when no package has
// been given on the command line (implicit current directory)
// subject to change. It would be nice to remove this special case
// entirely, but it is surely very helpful to see progress being made
// when tests are run on slow single-CPU ARM systems.
- if testShowPass && (len(pkgs) == 1 || cfg.BuildP == 1) {
+ //
+ // If we're showing JSON output, then display output as soon as
+ // possible even when multiple tests are being run: the JSON output
+ // events are attributed to specific package tests, so interlacing them
+ // is OK.
+ if testShowPass && (len(pkgs) == 1 || cfg.BuildP == 1) || testJSON {
// Write both to stdout and buf, for possible saving
// to cache, and for looking for the "no tests to run" message.
- cmd.Stdout = io.MultiWriter(os.Stdout, &buf)
+ cmd.Stdout = io.MultiWriter(stdout, &buf)
} else {
cmd.Stdout = &buf
}
{Name: "covermode"},
{Name: "coverpkg"},
{Name: "exec"},
+ {Name: "json", BoolVar: &testJSON},
{Name: "vet"},
// Passed to 6.out, adding a "test." prefix to the name if necessary: -v becomes -test.v.
// Arguably should be handled by f.Value, but aren't.
switch f.Name {
// bool flags.
- case "c", "i", "v", "cover":
+ case "c", "i", "v", "cover", "json":
cmdflag.SetBool(cmd, f.BoolVar, value)
+ if f.Name == "json" && testJSON {
+ passToTest = append(passToTest, "-test.v")
+ }
case "o":
testO = value
testNeedBinary = true
--- /dev/null
+package p
+
+import (
+ "testing"
+ "time"
+)
+
+func Test1(t *testing.T) {
+ time.Sleep(200 * time.Millisecond)
+}
--- /dev/null
+package p
+
+import (
+ "testing"
+ "time"
+)
+
+func Test1(t *testing.T) {
+ time.Sleep(200 * time.Millisecond)
+}