Skip to content

Commit f80d8fb

Browse files
tebekarobpike
authored andcommitted
testing: Add support for running tests in parallel (t.Parallel API).
See discussion at https://groups.google.com/d/topic/golang-dev/RAKiqi44GEU/discussion R=golang-dev, bradfitz, dvyukov, rogpeppe, r, r, borman CC=golang-dev https://golang.org/cl/5071044
1 parent cd80d04 commit f80d8fb

File tree

2 files changed

+72
-34
lines changed

2 files changed

+72
-34
lines changed

src/cmd/gotest/flag.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ var usageMessage = `Usage of %s:
2828
-cpuprofile="": passes -test.cpuprofile to test
2929
-memprofile="": passes -test.memprofile to test
3030
-memprofilerate=0: passes -test.memprofilerate to test
31+
-parallel=0: passes -test.parallel to test
3132
-run="": passes -test.run to test
3233
-short=false: passes -test.short to test
3334
-timeout=0: passes -test.timeout to test
@@ -63,6 +64,7 @@ var flagDefn = []*flagSpec{
6364
&flagSpec{name: "cpuprofile", passToTest: true},
6465
&flagSpec{name: "memprofile", passToTest: true},
6566
&flagSpec{name: "memprofilerate", passToTest: true},
67+
&flagSpec{name: "parallel", passToTest: true},
6668
&flagSpec{name: "run", passToTest: true},
6769
&flagSpec{name: "short", isBool: true, passToTest: true},
6870
&flagSpec{name: "timeout", passToTest: true},

src/pkg/testing/testing.go

Lines changed: 70 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ import (
4444
"os"
4545
"runtime"
4646
"runtime/pprof"
47-
"strings"
4847
"strconv"
48+
"strings"
4949
"time"
5050
)
5151

@@ -65,6 +65,7 @@ var (
6565
cpuProfile = flag.String("test.cpuprofile", "", "write a cpu profile to the named file during execution")
6666
timeout = flag.Int64("test.timeout", 0, "if > 0, sets time limit for tests in seconds")
6767
cpuListStr = flag.String("test.cpu", "", "comma-separated list of number of CPUs to use for each test")
68+
parallel = flag.Int("test.parallel", runtime.GOMAXPROCS(0), "maximum test parallelism")
6869

6970
cpuList []int
7071
)
@@ -92,9 +93,12 @@ func tabify(s string) string {
9293
// T is a type passed to Test functions to manage test state and support formatted test logs.
9394
// Logs are accumulated during execution and dumped to standard error when done.
9495
type T struct {
95-
errors string
96-
failed bool
97-
ch chan *T
96+
name string // Name of test.
97+
errors string // Error string from test.
98+
failed bool // Test has failed.
99+
ch chan *T // Output for serial tests.
100+
startParallel chan bool // Parallel tests will wait on this.
101+
ns int64 // Duration of test in nanoseconds.
98102
}
99103

100104
// Fail marks the Test function as having failed but continues execution.
@@ -145,6 +149,13 @@ func (t *T) Fatalf(format string, args ...interface{}) {
145149
t.FailNow()
146150
}
147151

152+
// Parallel signals that this test is to be run in parallel with (and only with)
153+
// other parallel tests in this CPU group.
154+
func (t *T) Parallel() {
155+
t.ch <- nil // Release main testing loop
156+
<-t.startParallel // Wait for serial tests to finish
157+
}
158+
148159
// An internal type but exported because it is cross-package; part of the implementation
149160
// of gotest.
150161
type InternalTest struct {
@@ -153,7 +164,9 @@ type InternalTest struct {
153164
}
154165

155166
func tRunner(t *T, test *InternalTest) {
167+
t.ns = time.Nanoseconds()
156168
test.F(t)
169+
t.ns = time.Nanoseconds() - t.ns
157170
t.ch <- t
158171
}
159172

@@ -171,50 +184,73 @@ func Main(matchString func(pat, str string) (bool, os.Error), tests []InternalTe
171184
after()
172185
}
173186

187+
func report(t *T) {
188+
tstr := fmt.Sprintf("(%.2f seconds)", float64(t.ns)/1e9)
189+
format := "--- %s: %s %s\n%s"
190+
if t.failed {
191+
fmt.Fprintf(os.Stderr, format, "FAIL", t.name, tstr, t.errors)
192+
} else if *chatty {
193+
fmt.Fprintf(os.Stderr, format, "PASS", t.name, tstr, t.errors)
194+
}
195+
}
196+
174197
func RunTests(matchString func(pat, str string) (bool, os.Error), tests []InternalTest) {
175-
ok := true
176198
if len(tests) == 0 {
177-
println("testing: warning: no tests to run")
199+
fmt.Fprintln(os.Stderr, "testing: warning: no tests to run")
200+
return
178201
}
179-
for i := 0; i < len(tests); i++ {
180-
matched, err := matchString(*match, tests[i].Name)
181-
if err != nil {
182-
println("invalid regexp for -test.run:", err.String())
183-
os.Exit(1)
184-
}
185-
if !matched {
186-
continue
187-
}
188-
for _, procs := range cpuList {
189-
runtime.GOMAXPROCS(procs)
202+
203+
ok := true
204+
ch := make(chan *T)
205+
206+
for _, procs := range cpuList {
207+
runtime.GOMAXPROCS(procs)
208+
209+
numParallel := 0
210+
startParallel := make(chan bool)
211+
212+
for i := 0; i < len(tests); i++ {
213+
matched, err := matchString(*match, tests[i].Name)
214+
if err != nil {
215+
println("invalid regexp for -test.run:", err.String())
216+
os.Exit(1)
217+
}
218+
if !matched {
219+
continue
220+
}
190221
testName := tests[i].Name
191222
if procs != 1 {
192223
testName = fmt.Sprintf("%s-%d", tests[i].Name, procs)
193224
}
225+
t := &T{ch: ch, name: testName, startParallel: startParallel}
194226
if *chatty {
195-
println("=== RUN ", testName)
227+
println("=== RUN", t.name)
196228
}
197-
ns := -time.Nanoseconds()
198-
t := new(T)
199-
t.ch = make(chan *T)
200229
go tRunner(t, &tests[i])
201-
<-t.ch
202-
ns += time.Nanoseconds()
203-
tstr := fmt.Sprintf("(%.2f seconds)", float64(ns)/1e9)
204-
if p := runtime.GOMAXPROCS(-1); t.failed == false && p != procs {
205-
t.failed = true
206-
t.errors = fmt.Sprintf("%s left GOMAXPROCS set to %d\n", testName, p)
230+
out := <-t.ch
231+
if out == nil { // Parallel run.
232+
numParallel++
233+
continue
207234
}
208-
if t.failed {
209-
println("--- FAIL:", testName, tstr)
210-
print(t.errors)
211-
ok = false
212-
} else if *chatty {
213-
println("--- PASS:", testName, tstr)
214-
print(t.errors)
235+
report(t)
236+
ok = ok && !out.failed
237+
}
238+
239+
running := 0
240+
for numParallel+running > 0 {
241+
if running < *parallel && numParallel > 0 {
242+
startParallel <- true
243+
running++
244+
numParallel--
245+
continue
215246
}
247+
t := <-ch
248+
report(t)
249+
ok = ok && !t.failed
250+
running--
216251
}
217252
}
253+
218254
if !ok {
219255
println("FAIL")
220256
os.Exit(1)

0 commit comments

Comments
 (0)