mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
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
This commit is contained in:
parent
cd80d04c5c
commit
f80d8fbcf0
2 changed files with 74 additions and 36 deletions
|
|
@ -28,6 +28,7 @@ var usageMessage = `Usage of %s:
|
||||||
-cpuprofile="": passes -test.cpuprofile to test
|
-cpuprofile="": passes -test.cpuprofile to test
|
||||||
-memprofile="": passes -test.memprofile to test
|
-memprofile="": passes -test.memprofile to test
|
||||||
-memprofilerate=0: passes -test.memprofilerate to test
|
-memprofilerate=0: passes -test.memprofilerate to test
|
||||||
|
-parallel=0: passes -test.parallel to test
|
||||||
-run="": passes -test.run to test
|
-run="": passes -test.run to test
|
||||||
-short=false: passes -test.short to test
|
-short=false: passes -test.short to test
|
||||||
-timeout=0: passes -test.timeout to test
|
-timeout=0: passes -test.timeout to test
|
||||||
|
|
@ -63,6 +64,7 @@ var flagDefn = []*flagSpec{
|
||||||
&flagSpec{name: "cpuprofile", passToTest: true},
|
&flagSpec{name: "cpuprofile", passToTest: true},
|
||||||
&flagSpec{name: "memprofile", passToTest: true},
|
&flagSpec{name: "memprofile", passToTest: true},
|
||||||
&flagSpec{name: "memprofilerate", passToTest: true},
|
&flagSpec{name: "memprofilerate", passToTest: true},
|
||||||
|
&flagSpec{name: "parallel", passToTest: true},
|
||||||
&flagSpec{name: "run", passToTest: true},
|
&flagSpec{name: "run", passToTest: true},
|
||||||
&flagSpec{name: "short", isBool: true, passToTest: true},
|
&flagSpec{name: "short", isBool: true, passToTest: true},
|
||||||
&flagSpec{name: "timeout", passToTest: true},
|
&flagSpec{name: "timeout", passToTest: true},
|
||||||
|
|
|
||||||
|
|
@ -44,8 +44,8 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"runtime/pprof"
|
"runtime/pprof"
|
||||||
"strings"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -65,6 +65,7 @@ var (
|
||||||
cpuProfile = flag.String("test.cpuprofile", "", "write a cpu profile to the named file during execution")
|
cpuProfile = flag.String("test.cpuprofile", "", "write a cpu profile to the named file during execution")
|
||||||
timeout = flag.Int64("test.timeout", 0, "if > 0, sets time limit for tests in seconds")
|
timeout = flag.Int64("test.timeout", 0, "if > 0, sets time limit for tests in seconds")
|
||||||
cpuListStr = flag.String("test.cpu", "", "comma-separated list of number of CPUs to use for each test")
|
cpuListStr = flag.String("test.cpu", "", "comma-separated list of number of CPUs to use for each test")
|
||||||
|
parallel = flag.Int("test.parallel", runtime.GOMAXPROCS(0), "maximum test parallelism")
|
||||||
|
|
||||||
cpuList []int
|
cpuList []int
|
||||||
)
|
)
|
||||||
|
|
@ -92,9 +93,12 @@ func tabify(s string) string {
|
||||||
// T is a type passed to Test functions to manage test state and support formatted test logs.
|
// T is a type passed to Test functions to manage test state and support formatted test logs.
|
||||||
// Logs are accumulated during execution and dumped to standard error when done.
|
// Logs are accumulated during execution and dumped to standard error when done.
|
||||||
type T struct {
|
type T struct {
|
||||||
errors string
|
name string // Name of test.
|
||||||
failed bool
|
errors string // Error string from test.
|
||||||
ch chan *T
|
failed bool // Test has failed.
|
||||||
|
ch chan *T // Output for serial tests.
|
||||||
|
startParallel chan bool // Parallel tests will wait on this.
|
||||||
|
ns int64 // Duration of test in nanoseconds.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fail marks the Test function as having failed but continues execution.
|
// Fail marks the Test function as having failed but continues execution.
|
||||||
|
|
@ -145,6 +149,13 @@ func (t *T) Fatalf(format string, args ...interface{}) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parallel signals that this test is to be run in parallel with (and only with)
|
||||||
|
// other parallel tests in this CPU group.
|
||||||
|
func (t *T) Parallel() {
|
||||||
|
t.ch <- nil // Release main testing loop
|
||||||
|
<-t.startParallel // Wait for serial tests to finish
|
||||||
|
}
|
||||||
|
|
||||||
// An internal type but exported because it is cross-package; part of the implementation
|
// An internal type but exported because it is cross-package; part of the implementation
|
||||||
// of gotest.
|
// of gotest.
|
||||||
type InternalTest struct {
|
type InternalTest struct {
|
||||||
|
|
@ -153,7 +164,9 @@ type InternalTest struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func tRunner(t *T, test *InternalTest) {
|
func tRunner(t *T, test *InternalTest) {
|
||||||
|
t.ns = time.Nanoseconds()
|
||||||
test.F(t)
|
test.F(t)
|
||||||
|
t.ns = time.Nanoseconds() - t.ns
|
||||||
t.ch <- t
|
t.ch <- t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -171,50 +184,73 @@ func Main(matchString func(pat, str string) (bool, os.Error), tests []InternalTe
|
||||||
after()
|
after()
|
||||||
}
|
}
|
||||||
|
|
||||||
func RunTests(matchString func(pat, str string) (bool, os.Error), tests []InternalTest) {
|
func report(t *T) {
|
||||||
ok := true
|
tstr := fmt.Sprintf("(%.2f seconds)", float64(t.ns)/1e9)
|
||||||
if len(tests) == 0 {
|
format := "--- %s: %s %s\n%s"
|
||||||
println("testing: warning: no tests to run")
|
if t.failed {
|
||||||
|
fmt.Fprintf(os.Stderr, format, "FAIL", t.name, tstr, t.errors)
|
||||||
|
} else if *chatty {
|
||||||
|
fmt.Fprintf(os.Stderr, format, "PASS", t.name, tstr, t.errors)
|
||||||
}
|
}
|
||||||
for i := 0; i < len(tests); i++ {
|
}
|
||||||
matched, err := matchString(*match, tests[i].Name)
|
|
||||||
if err != nil {
|
func RunTests(matchString func(pat, str string) (bool, os.Error), tests []InternalTest) {
|
||||||
println("invalid regexp for -test.run:", err.String())
|
if len(tests) == 0 {
|
||||||
os.Exit(1)
|
fmt.Fprintln(os.Stderr, "testing: warning: no tests to run")
|
||||||
}
|
return
|
||||||
if !matched {
|
}
|
||||||
continue
|
|
||||||
}
|
ok := true
|
||||||
for _, procs := range cpuList {
|
ch := make(chan *T)
|
||||||
runtime.GOMAXPROCS(procs)
|
|
||||||
|
for _, procs := range cpuList {
|
||||||
|
runtime.GOMAXPROCS(procs)
|
||||||
|
|
||||||
|
numParallel := 0
|
||||||
|
startParallel := make(chan bool)
|
||||||
|
|
||||||
|
for i := 0; i < len(tests); i++ {
|
||||||
|
matched, err := matchString(*match, tests[i].Name)
|
||||||
|
if err != nil {
|
||||||
|
println("invalid regexp for -test.run:", err.String())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
if !matched {
|
||||||
|
continue
|
||||||
|
}
|
||||||
testName := tests[i].Name
|
testName := tests[i].Name
|
||||||
if procs != 1 {
|
if procs != 1 {
|
||||||
testName = fmt.Sprintf("%s-%d", tests[i].Name, procs)
|
testName = fmt.Sprintf("%s-%d", tests[i].Name, procs)
|
||||||
}
|
}
|
||||||
|
t := &T{ch: ch, name: testName, startParallel: startParallel}
|
||||||
if *chatty {
|
if *chatty {
|
||||||
println("=== RUN ", testName)
|
println("=== RUN", t.name)
|
||||||
}
|
}
|
||||||
ns := -time.Nanoseconds()
|
|
||||||
t := new(T)
|
|
||||||
t.ch = make(chan *T)
|
|
||||||
go tRunner(t, &tests[i])
|
go tRunner(t, &tests[i])
|
||||||
<-t.ch
|
out := <-t.ch
|
||||||
ns += time.Nanoseconds()
|
if out == nil { // Parallel run.
|
||||||
tstr := fmt.Sprintf("(%.2f seconds)", float64(ns)/1e9)
|
numParallel++
|
||||||
if p := runtime.GOMAXPROCS(-1); t.failed == false && p != procs {
|
continue
|
||||||
t.failed = true
|
|
||||||
t.errors = fmt.Sprintf("%s left GOMAXPROCS set to %d\n", testName, p)
|
|
||||||
}
|
}
|
||||||
if t.failed {
|
report(t)
|
||||||
println("--- FAIL:", testName, tstr)
|
ok = ok && !out.failed
|
||||||
print(t.errors)
|
}
|
||||||
ok = false
|
|
||||||
} else if *chatty {
|
running := 0
|
||||||
println("--- PASS:", testName, tstr)
|
for numParallel+running > 0 {
|
||||||
print(t.errors)
|
if running < *parallel && numParallel > 0 {
|
||||||
|
startParallel <- true
|
||||||
|
running++
|
||||||
|
numParallel--
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
t := <-ch
|
||||||
|
report(t)
|
||||||
|
ok = ok && !t.failed
|
||||||
|
running--
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
println("FAIL")
|
println("FAIL")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue