@@ -44,8 +44,8 @@ import (
4444 "os"
4545 "runtime"
4646 "runtime/pprof"
47- "strings"
4847 "strconv"
48+ "strings"
4949 "time"
5050)
5151
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.
9495type 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.
150161type InternalTest struct {
@@ -153,7 +164,9 @@ type InternalTest struct {
153164}
154165
155166func 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+
174197func 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