github.com/rsc/go@v0.0.0-20150416155037-e040fd465409/src/cmd/dist/test.go (about) 1 // Copyright 2015 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package main 6 7 import ( 8 "bytes" 9 "errors" 10 "flag" 11 "fmt" 12 "log" 13 "os" 14 "os/exec" 15 "path/filepath" 16 "regexp" 17 "strconv" 18 "strings" 19 "time" 20 ) 21 22 func cmdtest() { 23 var t tester 24 flag.BoolVar(&t.listMode, "list", false, "list available tests") 25 flag.BoolVar(&t.noRebuild, "no-rebuild", false, "don't rebuild std and cmd packages") 26 flag.StringVar(&t.banner, "banner", "##### ", "banner prefix; blank means no section banners") 27 flag.StringVar(&t.runRxStr, "run", "", "run only those tests matching the regular expression; empty means to run all") 28 xflagparse(0) 29 t.run() 30 } 31 32 // tester executes cmdtest. 33 type tester struct { 34 listMode bool 35 noRebuild bool 36 runRxStr string 37 runRx *regexp.Regexp 38 banner string // prefix, or "" for none 39 40 goroot string 41 goarch string 42 gohostarch string 43 goos string 44 gohostos string 45 cgoEnabled bool 46 partial bool 47 haveTime bool // the 'time' binary is available 48 49 tests []distTest 50 timeoutScale int 51 } 52 53 // A distTest is a test run by dist test. 54 // Each test has a unique name and belongs to a group (heading) 55 type distTest struct { 56 name string // unique test name; may be filtered with -run flag 57 heading string // group section; this header is printed before the test is run. 58 fn func() error 59 } 60 61 func mustEnv(k string) string { 62 v := os.Getenv(k) 63 if v == "" { 64 log.Fatalf("Unset environment variable %v", k) 65 } 66 return v 67 } 68 69 func (t *tester) run() { 70 t.goroot = mustEnv("GOROOT") 71 t.goos = mustEnv("GOOS") 72 t.gohostos = mustEnv("GOHOSTOS") 73 t.goarch = mustEnv("GOARCH") 74 t.gohostarch = mustEnv("GOHOSTARCH") 75 slurp, err := exec.Command("go", "env", "CGO_ENABLED").Output() 76 if err != nil { 77 log.Fatalf("Error running go env CGO_ENABLED: %v", err) 78 } 79 t.cgoEnabled, _ = strconv.ParseBool(strings.TrimSpace(string(slurp))) 80 81 if t.hasBash() { 82 if _, err := exec.LookPath("time"); err == nil { 83 t.haveTime = true 84 } 85 } 86 87 if !t.noRebuild { 88 t.out("Building packages and commands.") 89 cmd := exec.Command("go", "install", "-a", "-v", "std", "cmd") 90 cmd.Stdout = os.Stdout 91 cmd.Stderr = os.Stderr 92 if err := cmd.Run(); err != nil { 93 log.Fatalf("building packages and commands: %v", err) 94 } 95 } 96 97 t.timeoutScale = 1 98 if t.goarch == "arm" || t.goos == "windows" { 99 t.timeoutScale = 2 100 } 101 102 if t.runRxStr != "" { 103 t.runRx = regexp.MustCompile(t.runRxStr) 104 } 105 106 t.registerTests() 107 if t.listMode { 108 for _, tt := range t.tests { 109 fmt.Println(tt.name) 110 } 111 return 112 } 113 114 // we must unset GOROOT_FINAL before tests, because runtime/debug requires 115 // correct access to source code, so if we have GOROOT_FINAL in effect, 116 // at least runtime/debug test will fail. 117 os.Unsetenv("GOROOT_FINAL") 118 119 var lastHeading string 120 for _, dt := range t.tests { 121 if t.runRx != nil && !t.runRx.MatchString(dt.name) { 122 t.partial = true 123 continue 124 } 125 if dt.heading != "" && lastHeading != dt.heading { 126 lastHeading = dt.heading 127 t.out(dt.heading) 128 } 129 if vflag > 0 { 130 fmt.Printf("# go tool dist test -run=^%s$\n", dt.name) 131 } 132 if err := dt.fn(); err != nil { 133 log.Fatalf("Failed: %v", err) 134 } 135 } 136 if t.partial { 137 fmt.Println("\nALL TESTS PASSED (some were excluded)") 138 } else { 139 fmt.Println("\nALL TESTS PASSED") 140 } 141 } 142 143 func (t *tester) timeout(sec int) string { 144 return "-timeout=" + fmt.Sprint(time.Duration(sec)*time.Second*time.Duration(t.timeoutScale)) 145 } 146 147 func (t *tester) registerTests() { 148 // Register a separate logical test for each package in the standard library 149 // but actually group them together at execution time to share the cost of 150 // building packages shared between them. 151 all, err := exec.Command("go", "list", "std", "cmd").Output() 152 if err != nil { 153 log.Fatalf("Error running go list std cmd: %v", err) 154 } 155 // ranGoTest and stdMatches are state closed over by the 156 // stdlib testing func below. The tests are run sequentially, 157 // so there's no need for locks. 158 var ( 159 ranGoTest bool 160 stdMatches []string 161 ) 162 for _, pkg := range strings.Fields(string(all)) { 163 testName := "go_test:" + pkg 164 if t.runRx == nil || t.runRx.MatchString(testName) { 165 stdMatches = append(stdMatches, pkg) 166 } 167 t.tests = append(t.tests, distTest{ 168 name: testName, 169 heading: "Testing packages.", 170 fn: func() error { 171 if ranGoTest { 172 return nil 173 } 174 ranGoTest = true 175 cmd := exec.Command("go", append([]string{ 176 "test", 177 "-short", 178 t.timeout(120), 179 "-gcflags=" + os.Getenv("GO_GCFLAGS"), 180 }, stdMatches...)...) 181 cmd.Stdout = os.Stdout 182 cmd.Stderr = os.Stderr 183 return cmd.Run() 184 }, 185 }) 186 } 187 188 // Old hack for when Plan 9 on GCE was too slow. 189 // We're keeping this until test sharding (Issue 10029) is finished, though. 190 if os.Getenv("GOTESTONLY") == "std" { 191 t.partial = true 192 return 193 } 194 195 // Runtime CPU tests. 196 for _, cpu := range []string{"1", "2", "4"} { 197 cpu := cpu 198 testName := "runtime:cpu" + cpu 199 t.tests = append(t.tests, distTest{ 200 name: testName, 201 heading: "GOMAXPROCS=2 runtime -cpu=1,2,4", 202 fn: func() error { 203 cmd := t.dirCmd(".", "go", "test", "-short", t.timeout(300), "runtime", "-cpu="+cpu) 204 // We set GOMAXPROCS=2 in addition to -cpu=1,2,4 in order to test runtime bootstrap code, 205 // creation of first goroutines and first garbage collections in the parallel setting. 206 cmd.Env = mergeEnvLists([]string{"GOMAXPROCS=2"}, os.Environ()) 207 return cmd.Run() 208 }, 209 }) 210 } 211 212 // sync tests 213 t.tests = append(t.tests, distTest{ 214 name: "sync_cpu", 215 heading: "sync -cpu=10", 216 fn: func() error { 217 return t.dirCmd(".", "go", "test", "sync", "-short", t.timeout(120), "-cpu=10").Run() 218 }, 219 }) 220 221 iOS := t.goos == "darwin" && (t.goarch == "arm" || t.goarch == "arm64") 222 223 if t.cgoEnabled && t.goos != "android" && !iOS { 224 // Disabled on android and iOS. golang.org/issue/8345 225 t.tests = append(t.tests, distTest{ 226 name: "cgo_stdio", 227 heading: "../misc/cgo/stdio", 228 fn: func() error { 229 return t.dirCmd("misc/cgo/stdio", 230 "go", "run", filepath.Join(os.Getenv("GOROOT"), "test/run.go"), "-", ".").Run() 231 }, 232 }) 233 t.tests = append(t.tests, distTest{ 234 name: "cgo_life", 235 heading: "../misc/cgo/life", 236 fn: func() error { 237 return t.dirCmd("misc/cgo/life", 238 "go", "run", filepath.Join(os.Getenv("GOROOT"), "test/run.go"), "-", ".").Run() 239 }, 240 }) 241 } 242 if t.cgoEnabled && t.goos != "android" && !iOS { 243 // TODO(crawshaw): reenable on android and iOS 244 // golang.org/issue/8345 245 // 246 // These tests are not designed to run off the host. 247 t.tests = append(t.tests, distTest{ 248 name: "cgo_test", 249 heading: "../misc/cgo/test", 250 fn: t.cgoTest, 251 }) 252 } 253 254 if t.raceDetectorSupported() { 255 t.tests = append(t.tests, distTest{ 256 name: "race", 257 heading: "Testing race detector", 258 fn: t.raceTest, 259 }) 260 } 261 262 if t.hasBash() && t.cgoEnabled && t.goos != "android" && t.goos != "darwin" { 263 t.registerTest("testgodefs", "../misc/cgo/testgodefs", "./test.bash") 264 } 265 if t.cgoEnabled { 266 if t.gohostos == "windows" { 267 t.tests = append(t.tests, distTest{ 268 name: "testso", 269 heading: "../misc/cgo/testso", 270 fn: t.cgoTestSOWindows, 271 }) 272 } else if t.hasBash() && t.goos != "android" && !iOS { 273 t.registerTest("testso", "../misc/cgo/testso", "./test.bash") 274 } 275 if t.extLink() && t.goos == "darwin" && t.goarch == "amd64" { 276 // TODO(crawshaw): add darwin/arm{,64} 277 t.registerTest("testcarchive", "../misc/cgo/testcarchive", "./test.bash") 278 } 279 if t.gohostos == "linux" && t.goarch == "amd64" { 280 t.registerTest("testasan", "../misc/cgo/testasan", "go", "run", "main.go") 281 } 282 if t.hasBash() && t.goos != "android" && !iOS && t.gohostos != "windows" { 283 t.registerTest("cgo_errors", "../misc/cgo/errors", "./test.bash") 284 } 285 } 286 if t.hasBash() && t.goos != "nacl" && t.goos != "android" && !iOS { 287 t.registerTest("doc_progs", "../doc/progs", "time", "go", "run", "run.go") 288 t.registerTest("wiki", "../doc/articles/wiki", "./test.bash") 289 t.registerTest("codewalk", "../doc/codewalk", "time", "./run") 290 t.registerTest("shootout", "../test/bench/shootout", "time", "./timing.sh", "-test") 291 } 292 if t.goos != "android" && !iOS { 293 t.registerTest("bench_go1", "../test/bench/go1", "go", "test") 294 } 295 if t.goos != "android" && !iOS { 296 // TODO(bradfitz): shard down into these tests, as 297 // this is one of the slowest (and most shardable) 298 // tests. 299 t.tests = append(t.tests, distTest{ 300 name: "test", 301 heading: "../test", 302 fn: t.testDirTest, 303 }) 304 } 305 if t.goos != "nacl" && t.goos != "android" && !iOS { 306 t.tests = append(t.tests, distTest{ 307 name: "api", 308 heading: "API check", 309 fn: func() error { 310 return t.dirCmd(".", "go", "run", filepath.Join(t.goroot, "src/cmd/api/run.go")).Run() 311 }, 312 }) 313 } 314 315 } 316 317 func (t *tester) registerTest(name, dirBanner, bin string, args ...string) { 318 if bin == "time" && !t.haveTime { 319 bin, args = args[0], args[1:] 320 } 321 t.tests = append(t.tests, distTest{ 322 name: name, 323 heading: dirBanner, 324 fn: func() error { 325 return t.dirCmd(filepath.Join(t.goroot, "src", dirBanner), bin, args...).Run() 326 }, 327 }) 328 } 329 330 func (t *tester) dirCmd(dir string, bin string, args ...string) *exec.Cmd { 331 cmd := exec.Command(bin, args...) 332 if filepath.IsAbs(dir) { 333 cmd.Dir = dir 334 } else { 335 cmd.Dir = filepath.Join(t.goroot, dir) 336 } 337 cmd.Stdout = os.Stdout 338 cmd.Stderr = os.Stderr 339 return cmd 340 } 341 342 func (t *tester) out(v string) { 343 if t.banner == "" { 344 return 345 } 346 fmt.Println("\n" + t.banner + v) 347 } 348 349 func (t *tester) extLink() bool { 350 pair := t.gohostos + "-" + t.goarch 351 switch pair { 352 case "android-arm", 353 "dragonfly-386", "dragonfly-amd64", 354 "freebsd-386", "freebsd-amd64", "freebsd-arm", 355 "linux-386", "linux-amd64", "linux-arm", 356 "netbsd-386", "netbsd-amd64", 357 "openbsd-386", "openbsd-amd64", 358 "windows-386", "windows-amd64": 359 return true 360 case "darwin-386", "darwin-amd64": 361 // linkmode=external fails on OS X 10.6 and earlier == Darwin 362 // 10.8 and earlier. 363 unameR, err := exec.Command("uname", "-r").Output() 364 if err != nil { 365 log.Fatalf("uname -r: %v", err) 366 } 367 major, _ := strconv.Atoi(string(unameR[:bytes.IndexByte(unameR, '.')])) 368 return major > 10 369 } 370 return false 371 } 372 373 func (t *tester) cgoTest() error { 374 env := mergeEnvLists([]string{"GOTRACEBACK=2"}, os.Environ()) 375 376 iOS := t.goos == "darwin" && (t.goarch == "arm" || t.goarch == "arm64") 377 if t.goos == "android" || iOS { 378 cmd := t.dirCmd("misc/cgo/test", "go", "test") 379 cmd.Env = env 380 return cmd.Run() 381 } 382 383 cmd := t.dirCmd("misc/cgo/test", "go", "test", "-ldflags", "-linkmode=auto") 384 cmd.Env = env 385 if err := cmd.Run(); err != nil { 386 return err 387 } 388 389 if t.gohostos != "dragonfly" { 390 // linkmode=internal fails on dragonfly since errno is a TLS relocation. 391 cmd := t.dirCmd("misc/cgo/test", "go", "test", "-ldflags", "-linkmode=internal") 392 cmd.Env = env 393 if err := cmd.Run(); err != nil { 394 return err 395 } 396 } 397 398 pair := t.gohostos + "-" + t.goarch 399 switch pair { 400 case "openbsd-386", "openbsd-amd64": 401 // test linkmode=external, but __thread not supported, so skip testtls. 402 cmd := t.dirCmd("misc/cgo/test", "go", "test", "-ldflags", "-linkmode=external") 403 cmd.Env = env 404 if err := cmd.Run(); err != nil { 405 return err 406 } 407 case "darwin-386", "darwin-amd64", 408 "windows-386", "windows-amd64": 409 if t.extLink() { 410 cmd := t.dirCmd("misc/cgo/test", "go", "test", "-ldflags", "-linkmode=external") 411 cmd.Env = env 412 if err := cmd.Run(); err != nil { 413 return err 414 } 415 } 416 case "android-arm", 417 "dragonfly-386", "dragonfly-amd64", 418 "freebsd-386", "freebsd-amd64", "freebsd-arm", 419 "linux-386", "linux-amd64", "linux-arm", 420 "netbsd-386", "netbsd-amd64": 421 422 cmd := t.dirCmd("misc/cgo/test", "go", "test", "-ldflags", "-linkmode=external") 423 cmd.Env = env 424 if err := cmd.Run(); err != nil { 425 return err 426 } 427 cmd = t.dirCmd("misc/cgo/testtls", "go", "test", "-ldflags", "-linkmode=auto") 428 cmd.Env = env 429 if err := cmd.Run(); err != nil { 430 return err 431 } 432 cmd = t.dirCmd("misc/cgo/testtls", "go", "test", "-ldflags", "-linkmode=external") 433 cmd.Env = env 434 if err := cmd.Run(); err != nil { 435 return err 436 } 437 438 switch pair { 439 case "netbsd-386", "netbsd-amd64": 440 // no static linking 441 case "freebsd-arm": 442 // -fPIC compiled tls code will use __tls_get_addr instead 443 // of __aeabi_read_tp, however, on FreeBSD/ARM, __tls_get_addr 444 // is implemented in rtld-elf, so -fPIC isn't compatible with 445 // static linking on FreeBSD/ARM with clang. (cgo depends on 446 // -fPIC fundamentally.) 447 default: 448 cc := mustEnv("CC") 449 cmd := t.dirCmd("misc/cgo/test", 450 cc, "-xc", "-o", "/dev/null", "-static", "-") 451 cmd.Env = env 452 cmd.Stdin = strings.NewReader("int main() {}") 453 if err := cmd.Run(); err != nil { 454 fmt.Println("No support for static linking found (lacks libc.a?), skip cgo static linking test.") 455 } else { 456 cmd = t.dirCmd("misc/cgo/testtls", "go", "test", "-ldflags", `-linkmode=external -extldflags "-static -pthread"`) 457 cmd.Env = env 458 if err := cmd.Run(); err != nil { 459 return err 460 } 461 462 cmd = t.dirCmd("misc/cgo/nocgo", "go", "test") 463 cmd.Env = env 464 if err := cmd.Run(); err != nil { 465 return err 466 } 467 468 cmd = t.dirCmd("misc/cgo/nocgo", "go", "test", "-ldflags", `-linkmode=external`) 469 cmd.Env = env 470 if err := cmd.Run(); err != nil { 471 return err 472 } 473 474 cmd = t.dirCmd("misc/cgo/nocgo", "go", "test", "-ldflags", `-linkmode=external -extldflags "-static -pthread"`) 475 cmd.Env = env 476 if err := cmd.Run(); err != nil { 477 return err 478 } 479 } 480 481 if pair != "freebsd-amd64" { // clang -pie fails to link misc/cgo/test 482 cmd := t.dirCmd("misc/cgo/test", 483 cc, "-xc", "-o", "/dev/null", "-pie", "-") 484 cmd.Env = env 485 cmd.Stdin = strings.NewReader("int main() {}") 486 if err := cmd.Run(); err != nil { 487 fmt.Println("No support for -pie found, skip cgo PIE test.") 488 } else { 489 cmd = t.dirCmd("misc/cgo/test", "go", "test", "-ldflags", `-linkmode=external -extldflags "-pie"`) 490 cmd.Env = env 491 if err := cmd.Run(); err != nil { 492 return fmt.Errorf("pie cgo/test: %v", err) 493 } 494 cmd = t.dirCmd("misc/cgo/testtls", "go", "test", "-ldflags", `-linkmode=external -extldflags "-pie"`) 495 cmd.Env = env 496 if err := cmd.Run(); err != nil { 497 return fmt.Errorf("pie cgo/testtls: %v", err) 498 } 499 cmd = t.dirCmd("misc/cgo/nocgo", "go", "test", "-ldflags", `-linkmode=external -extldflags "-pie"`) 500 cmd.Env = env 501 if err := cmd.Run(); err != nil { 502 return fmt.Errorf("pie cgo/nocgo: %v", err) 503 } 504 } 505 } 506 } 507 } 508 509 return nil 510 } 511 512 func (t *tester) cgoTestSOWindows() error { 513 cmd := t.dirCmd("misc/cgo/testso", `.\test`) 514 var buf bytes.Buffer 515 cmd.Stdout = &buf 516 cmd.Stderr = &buf 517 err := cmd.Run() 518 s := buf.String() 519 fmt.Println(s) 520 if err != nil { 521 return err 522 } 523 if strings.Contains(s, "FAIL") { 524 return errors.New("test failed") 525 } 526 return nil 527 } 528 529 func (t *tester) hasBash() bool { 530 switch t.gohostos { 531 case "windows", "plan9": 532 return false 533 } 534 return true 535 } 536 537 func (t *tester) raceDetectorSupported() bool { 538 switch t.gohostos { 539 case "linux", "darwin", "freebsd", "windows": 540 return t.cgoEnabled && t.goarch == "amd64" && t.gohostos == t.goos 541 } 542 return false 543 } 544 545 func (t *tester) raceTest() error { 546 if err := t.dirCmd(".", "go", "test", "-race", "-i", "runtime/race", "flag", "os/exec").Run(); err != nil { 547 return err 548 } 549 if err := t.dirCmd(".", "go", "test", "-race", "-run=Output", "runtime/race").Run(); err != nil { 550 return err 551 } 552 if err := t.dirCmd(".", "go", "test", "-race", "-short", "flag", "os/exec").Run(); err != nil { 553 return err 554 } 555 if t.extLink() { 556 // Test with external linking; see issue 9133. 557 if err := t.dirCmd(".", "go", "test", "-race", "-short", "-ldflags=-linkmode=external", "flag", "os/exec").Run(); err != nil { 558 return err 559 } 560 } 561 return nil 562 } 563 564 func (t *tester) testDirTest() error { 565 const runExe = "runtest.exe" // named exe for Windows, but harmless elsewhere 566 cmd := t.dirCmd("test", "go", "build", "-o", runExe, "run.go") 567 cmd.Env = mergeEnvLists([]string{"GOOS=" + t.gohostos, "GOARCH=" + t.gohostarch, "GOMAXPROCS="}, os.Environ()) 568 if err := cmd.Run(); err != nil { 569 return err 570 } 571 absExe := filepath.Join(cmd.Dir, runExe) 572 defer os.Remove(absExe) 573 if t.haveTime { 574 return t.dirCmd("test", "time", absExe).Run() 575 } 576 return t.dirCmd("test", absExe).Run() 577 } 578 579 // mergeEnvLists merges the two environment lists such that 580 // variables with the same name in "in" replace those in "out". 581 // out may be mutated. 582 func mergeEnvLists(in, out []string) []string { 583 NextVar: 584 for _, inkv := range in { 585 k := strings.SplitAfterN(inkv, "=", 2)[0] 586 for i, outkv := range out { 587 if strings.HasPrefix(outkv, k) { 588 out[i] = inkv 589 continue NextVar 590 } 591 } 592 out = append(out, inkv) 593 } 594 return out 595 }