From d513ee774cb3747eccc6b3483a67dbb9118dae8d Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Mon, 21 Dec 2015 20:42:40 -0500 Subject: [PATCH] cmd/dist: run shards of test dir in parallel Saves 15 seconds from all.bash on my laptop (3:20 -> 3:05). Change-Id: Ic5dc3c7804e78b584789dd856a3dada94000a8e2 Reviewed-on: https://go-review.googlesource.com/18199 Reviewed-by: Brad Fitzpatrick --- src/cmd/dist/test.go | 42 +++++++++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go index 77c736501a..cb99b0e358 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go @@ -17,6 +17,7 @@ import ( "regexp" "strconv" "strings" + "sync" "time" ) @@ -195,6 +196,7 @@ func (t *tester) run() { } dt := dt // dt used in background after this iteration if err := dt.fn(&dt); err != nil { + t.runPending(dt) // in case that hasn't been done yet t.failed = true if t.keepGoing { log.Printf("Failed: %v", err) @@ -788,8 +790,11 @@ func (t *tester) cgoTest(dt *distTest) error { } // run pending test commands, in parallel, emitting headers as appropriate. -// When finished, emit header for dt, which is going to run after the +// When finished, emit header for nextTest, which is going to run after the // pending commands are done (and runPending returns). +// A test should call runPending if it wants to make sure that it is not +// running in parallel with earlier tests, or if it has some other reason +// for needing the earlier tests to be done. func (t *tester) runPending(nextTest *distTest) { worklist := t.worklist t.worklist = nil @@ -961,20 +966,35 @@ func (t *tester) raceTest(dt *distTest) error { return nil } +var runtest struct { + sync.Once + exe string + err error +} + func (t *tester) testDirTest(dt *distTest, shard, shards int) error { - t.runPending(dt) - const runExe = "runtest.exe" // named exe for Windows, but harmless elsewhere - cmd := t.dirCmd("test", "go", "build", "-o", runExe, "run.go") - cmd.Env = mergeEnvLists([]string{"GOOS=" + t.gohostos, "GOARCH=" + t.gohostarch, "GOMAXPROCS="}, os.Environ()) - if err := cmd.Run(); err != nil { - return err + runtest.Do(func() { + const exe = "runtest.exe" // named exe for Windows, but harmless elsewhere + cmd := t.dirCmd("test", "go", "build", "-o", exe, "run.go") + cmd.Env = mergeEnvLists([]string{"GOOS=" + t.gohostos, "GOARCH=" + t.gohostarch, "GOMAXPROCS="}, os.Environ()) + runtest.exe = filepath.Join(cmd.Dir, exe) + if err := cmd.Run(); err != nil { + runtest.err = err + return + } + xatexit(func() { + os.Remove(runtest.exe) + }) + }) + if runtest.err != nil { + return runtest.err } - absExe := filepath.Join(cmd.Dir, runExe) - defer os.Remove(absExe) - return t.dirCmd("test", absExe, + + t.addCmd(dt, "test", runtest.exe, fmt.Sprintf("--shard=%d", shard), fmt.Sprintf("--shards=%d", shards), - ).Run() + ) + return nil } func (t *tester) shootoutTests() []string { -- 2.50.0