@@ -14,7 +14,6 @@ import (
14
14
"os/exec"
15
15
"path/filepath"
16
16
"regexp"
17
- "runtime"
18
17
"strconv"
19
18
"strings"
20
19
"time"
@@ -29,7 +28,7 @@ func cmdtest() {
29
28
flag .StringVar (& t .runRxStr , "run" , os .Getenv ("GOTESTONLY" ),
30
29
"run only those tests matching the regular expression; empty means to run all. " +
31
30
"Special exception: if the string begins with '!', the match is inverted." )
32
- xflagparse (0 )
31
+ xflagparse (- 1 ) // any number of args
33
32
t .run ()
34
33
}
35
34
@@ -40,8 +39,9 @@ type tester struct {
40
39
keepGoing bool
41
40
runRxStr string
42
41
runRx * regexp.Regexp
43
- runRxWant bool
44
- banner string // prefix, or "" for none
42
+ runRxWant bool // want runRx to match (true) or not match (false)
43
+ runNames []string // tests to run, exclusive with runRx; empty means all
44
+ banner string // prefix, or "" for none
45
45
46
46
goroot string
47
47
goarch string
@@ -83,6 +83,10 @@ func (t *tester) run() {
83
83
log .Fatalf ("Error running go env CGO_ENABLED: %v" , err )
84
84
}
85
85
t .cgoEnabled , _ = strconv .ParseBool (strings .TrimSpace (string (slurp )))
86
+ if flag .NArg () > 0 && t .runRxStr != "" {
87
+ log .Fatalf ("the -run regular expression flag is mutually exclusive with test name arguments" )
88
+ }
89
+ t .runNames = flag .Args ()
86
90
87
91
if t .hasBash () {
88
92
if _ , err := exec .LookPath ("time" ); err == nil {
@@ -135,13 +139,6 @@ func (t *tester) run() {
135
139
}
136
140
137
141
if t .runRxStr != "" {
138
- // Temporary (2015-05-14) special case for "std",
139
- // which the plan9 builder was using for ages. Delete
140
- // this once we update dashboard/builders.go to use a
141
- // regexp instead.
142
- if runtime .GOOS == "plan9" && t .runRxStr == "std" {
143
- t .runRxStr = "^go_test:"
144
- }
145
142
if t .runRxStr [0 ] == '!' {
146
143
t .runRxWant = false
147
144
t .runRxStr = t .runRxStr [1 :]
@@ -164,10 +161,16 @@ func (t *tester) run() {
164
161
// at least runtime/debug test will fail.
165
162
os .Unsetenv ("GOROOT_FINAL" )
166
163
164
+ for _ , name := range t .runNames {
165
+ if ! t .isRegisteredTestName (name ) {
166
+ log .Fatalf ("unknown test %q" , name )
167
+ }
168
+ }
169
+
167
170
var lastHeading string
168
171
ok := true
169
172
for _ , dt := range t .tests {
170
- if t . runRx != nil && ( t . runRx . MatchString (dt .name ) != t . runRxWant ) {
173
+ if ! t . shouldRunTest (dt .name ) {
171
174
t .partial = true
172
175
continue
173
176
}
@@ -197,6 +200,21 @@ func (t *tester) run() {
197
200
}
198
201
}
199
202
203
+ func (t * tester ) shouldRunTest (name string ) bool {
204
+ if t .runRx != nil {
205
+ return t .runRx .MatchString (name ) == t .runRxWant
206
+ }
207
+ if len (t .runNames ) == 0 {
208
+ return true
209
+ }
210
+ for _ , runName := range t .runNames {
211
+ if runName == name {
212
+ return true
213
+ }
214
+ }
215
+ return false
216
+ }
217
+
200
218
func (t * tester ) timeout (sec int ) string {
201
219
return "-timeout=" + fmt .Sprint (time .Duration (sec )* time .Second * time .Duration (t .timeoutScale ))
202
220
}
@@ -235,36 +253,25 @@ func (t *tester) registerStdTest(pkg string) {
235
253
})
236
254
}
237
255
238
- // validStdPkg reports whether pkg looks like a standard library package name.
239
- // Notably, it's not blank and doesn't contain regexp characters.
240
- func validStdPkg (pkg string ) bool {
241
- if pkg == "" {
242
- return false
243
- }
244
- for _ , r := range pkg {
245
- switch {
246
- case 'a' <= r && r <= 'z' :
247
- case 'A' <= r && r <= 'Z' :
248
- case '0' <= r && r <= '9' :
249
- case r == '_' :
250
- case r == '/' :
251
- default :
252
- return false
253
- }
254
- }
255
- return true
256
- }
257
-
258
256
func (t * tester ) registerTests () {
259
257
// Fast path to avoid the ~1 second of `go list std cmd` when
260
- // the caller passed - run=^go_test:foo/bar$ (as the continuous
258
+ // the caller lists specific tests to run. (as the continuous
261
259
// build coordinator does).
262
- if strings .HasPrefix (t .runRxStr , "^go_test:" ) && strings .HasSuffix (t .runRxStr , "$" ) {
263
- pkg := strings .TrimPrefix (t .runRxStr , "^go_test:" )
264
- pkg = strings .TrimSuffix (pkg , "$" )
265
- if validStdPkg (pkg ) {
260
+ if len (t .runNames ) > 0 {
261
+ for _ , name := range t .runNames {
262
+ if strings .HasPrefix (name , "go_test:" ) {
263
+ t .registerStdTest (strings .TrimPrefix (name , "go_test:" ))
264
+ }
265
+ }
266
+ } else {
267
+ // Use a format string to only list packages and commands that have tests.
268
+ const format = "{{if (or .TestGoFiles .XTestGoFiles)}}{{.ImportPath}}{{end}}"
269
+ all , err := exec .Command ("go" , "list" , "-f" , format , "std" , "cmd" ).Output ()
270
+ if err != nil {
271
+ log .Fatalf ("Error running go list std cmd: %v" , err )
272
+ }
273
+ for _ , pkg := range strings .Fields (string (all )) {
266
274
t .registerStdTest (pkg )
267
- return
268
275
}
269
276
}
270
277
@@ -372,14 +379,15 @@ func (t *tester) registerTests() {
372
379
t .registerTest ("bench_go1" , "../test/bench/go1" , "go" , "test" )
373
380
}
374
381
if t .goos != "android" && ! t .iOS () {
375
- // TODO(bradfitz): shard down into these tests, as
376
- // this is one of the slowest (and most shardable)
377
- // tests.
378
- t .tests = append (t .tests , distTest {
379
- name : "test" ,
380
- heading : "../test" ,
381
- fn : t .testDirTest ,
382
- })
382
+ const nShards = 5
383
+ for shard := 0 ; shard < nShards ; shard ++ {
384
+ shard := shard
385
+ t .tests = append (t .tests , distTest {
386
+ name : fmt .Sprintf ("test:%d_%d" , shard , nShards ),
387
+ heading : "../test" ,
388
+ fn : func () error { return t .testDirTest (shard , nShards ) },
389
+ })
390
+ }
383
391
}
384
392
if t .goos != "nacl" && t .goos != "android" && ! t .iOS () {
385
393
t .tests = append (t .tests , distTest {
@@ -390,36 +398,6 @@ func (t *tester) registerTests() {
390
398
},
391
399
})
392
400
}
393
-
394
- // Register the standard library tests lasts, to avoid the ~1 second latency
395
- // of running `go list std cmd` if we're running a specific test.
396
- // Now we know the names of all the other tests registered so far.
397
- if ! t .wantSpecificRegisteredTest () {
398
- // Use a format string to only list packages and commands that have tests.
399
- const format = "{{if (or .TestGoFiles .XTestGoFiles)}}{{.ImportPath}}{{end}}"
400
- all , err := exec .Command ("go" , "list" , "-f" , format , "std" , "cmd" ).Output ()
401
- if err != nil {
402
- log .Fatalf ("Error running go list std cmd: %v" , err )
403
- }
404
- // Put the standard library tests first.
405
- orig := t .tests
406
- t .tests = nil
407
- for _ , pkg := range strings .Fields (string (all )) {
408
- t .registerStdTest (pkg )
409
- }
410
- t .tests = append (t .tests , orig ... )
411
- }
412
- }
413
-
414
- // wantSpecificRegisteredTest reports whether the caller is requesting a
415
- // run of a specific test via the flag -run=^TESTNAME$ (as is done by the
416
- // continuous build coordinator).
417
- func (t * tester ) wantSpecificRegisteredTest () bool {
418
- if ! strings .HasPrefix (t .runRxStr , "^" ) || ! strings .HasSuffix (t .runRxStr , "$" ) {
419
- return false
420
- }
421
- test := t .runRxStr [1 : len (t .runRxStr )- 1 ]
422
- return t .isRegisteredTestName (test )
423
401
}
424
402
425
403
// isRegisteredTestName reports whether a test named testName has already
@@ -437,6 +415,9 @@ func (t *tester) registerTest(name, dirBanner, bin string, args ...string) {
437
415
if bin == "time" && ! t .haveTime {
438
416
bin , args = args [0 ], args [1 :]
439
417
}
418
+ if t .isRegisteredTestName (name ) {
419
+ panic ("duplicate registered test name " + name )
420
+ }
440
421
t .tests = append (t .tests , distTest {
441
422
name : name ,
442
423
heading : dirBanner ,
@@ -716,7 +697,7 @@ func (t *tester) raceTest() error {
716
697
return nil
717
698
}
718
699
719
- func (t * tester ) testDirTest () error {
700
+ func (t * tester ) testDirTest (shard , shards int ) error {
720
701
const runExe = "runtest.exe" // named exe for Windows, but harmless elsewhere
721
702
cmd := t .dirCmd ("test" , "go" , "build" , "-o" , runExe , "run.go" )
722
703
cmd .Env = mergeEnvLists ([]string {"GOOS=" + t .gohostos , "GOARCH=" + t .gohostarch , "GOMAXPROCS=" }, os .Environ ())
@@ -725,10 +706,10 @@ func (t *tester) testDirTest() error {
725
706
}
726
707
absExe := filepath .Join (cmd .Dir , runExe )
727
708
defer os .Remove (absExe )
728
- if t .haveTime {
729
- return t . dirCmd ( "test " , "time" , absExe ). Run ()
730
- }
731
- return t . dirCmd ( "test" , absExe ).Run ()
709
+ return t .dirCmd ( "test" , absExe ,
710
+ fmt . Sprintf ( "--shard=%d " , shard ),
711
+ fmt . Sprintf ( "--shards=%d" , shards ),
712
+ ).Run ()
732
713
}
733
714
734
715
// mergeEnvLists merges the two environment lists such that
0 commit comments