Home | History | Annotate | Download | only in dist
      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 	"io/ioutil"
     13 	"log"
     14 	"os"
     15 	"os/exec"
     16 	"path/filepath"
     17 	"regexp"
     18 	"strconv"
     19 	"strings"
     20 	"sync"
     21 	"time"
     22 )
     23 
     24 func cmdtest() {
     25 	var t tester
     26 	var noRebuild bool
     27 	flag.BoolVar(&t.listMode, "list", false, "list available tests")
     28 	flag.BoolVar(&t.rebuild, "rebuild", false, "rebuild everything first")
     29 	flag.BoolVar(&noRebuild, "no-rebuild", false, "overrides -rebuild (historical dreg)")
     30 	flag.BoolVar(&t.keepGoing, "k", false, "keep going even when error occurred")
     31 	flag.BoolVar(&t.race, "race", false, "run in race builder mode (different set of tests)")
     32 	flag.BoolVar(&t.compileOnly, "compile-only", false, "compile tests, but don't run them. This is for some builders. Not all dist tests respect this flag, but most do.")
     33 	flag.StringVar(&t.banner, "banner", "##### ", "banner prefix; blank means no section banners")
     34 	flag.StringVar(&t.runRxStr, "run", os.Getenv("GOTESTONLY"),
     35 		"run only those tests matching the regular expression; empty means to run all. "+
     36 			"Special exception: if the string begins with '!', the match is inverted.")
     37 	xflagparse(-1) // any number of args
     38 	if noRebuild {
     39 		t.rebuild = false
     40 	}
     41 	t.run()
     42 }
     43 
     44 // tester executes cmdtest.
     45 type tester struct {
     46 	race        bool
     47 	listMode    bool
     48 	rebuild     bool
     49 	failed      bool
     50 	keepGoing   bool
     51 	compileOnly bool // just try to compile all tests, but no need to run
     52 	runRxStr    string
     53 	runRx       *regexp.Regexp
     54 	runRxWant   bool     // want runRx to match (true) or not match (false)
     55 	runNames    []string // tests to run, exclusive with runRx; empty means all
     56 	banner      string   // prefix, or "" for none
     57 	lastHeading string   // last dir heading printed
     58 
     59 	goroot     string
     60 	goarch     string
     61 	gohostarch string
     62 	goos       string
     63 	gohostos   string
     64 	cgoEnabled bool
     65 	partial    bool
     66 	haveTime   bool // the 'time' binary is available
     67 
     68 	tests        []distTest
     69 	timeoutScale int
     70 
     71 	worklist []*work
     72 }
     73 
     74 type work struct {
     75 	dt    *distTest
     76 	cmd   *exec.Cmd
     77 	start chan bool
     78 	out   []byte
     79 	err   error
     80 	end   chan bool
     81 }
     82 
     83 // A distTest is a test run by dist test.
     84 // Each test has a unique name and belongs to a group (heading)
     85 type distTest struct {
     86 	name    string // unique test name; may be filtered with -run flag
     87 	heading string // group section; this header is printed before the test is run.
     88 	fn      func(*distTest) error
     89 }
     90 
     91 func mustEnv(k string) string {
     92 	v := os.Getenv(k)
     93 	if v == "" {
     94 		log.Fatalf("Unset environment variable %v", k)
     95 	}
     96 	return v
     97 }
     98 
     99 func (t *tester) run() {
    100 	t.goroot = mustEnv("GOROOT")
    101 	t.goos = mustEnv("GOOS")
    102 	t.gohostos = mustEnv("GOHOSTOS")
    103 	t.goarch = mustEnv("GOARCH")
    104 	t.gohostarch = mustEnv("GOHOSTARCH")
    105 	slurp, err := exec.Command("go", "env", "CGO_ENABLED").Output()
    106 	if err != nil {
    107 		log.Fatalf("Error running go env CGO_ENABLED: %v", err)
    108 	}
    109 	t.cgoEnabled, _ = strconv.ParseBool(strings.TrimSpace(string(slurp)))
    110 	if flag.NArg() > 0 && t.runRxStr != "" {
    111 		log.Fatalf("the -run regular expression flag is mutually exclusive with test name arguments")
    112 	}
    113 	t.runNames = flag.Args()
    114 
    115 	if t.hasBash() {
    116 		if _, err := exec.LookPath("time"); err == nil {
    117 			t.haveTime = true
    118 		}
    119 	}
    120 
    121 	if t.rebuild {
    122 		t.out("Building packages and commands.")
    123 		cmd := exec.Command("go", "install", "-a", "-v", "std", "cmd")
    124 		cmd.Stdout = os.Stdout
    125 		cmd.Stderr = os.Stderr
    126 		if err := cmd.Run(); err != nil {
    127 			log.Fatalf("building packages and commands: %v", err)
    128 		}
    129 	}
    130 
    131 	if t.iOS() {
    132 		// Install the Mach exception handler used to intercept
    133 		// EXC_BAD_ACCESS and convert it into a Go panic. This is
    134 		// necessary for a Go program running under lldb (the way
    135 		// we run tests). It is disabled by default because iOS
    136 		// apps are not allowed to access the exc_server symbol.
    137 		cmd := exec.Command("go", "install", "-a", "-tags", "lldb", "runtime/cgo")
    138 		cmd.Stdout = os.Stdout
    139 		cmd.Stderr = os.Stderr
    140 		if err := cmd.Run(); err != nil {
    141 			log.Fatalf("building mach exception handler: %v", err)
    142 		}
    143 
    144 		defer func() {
    145 			cmd := exec.Command("go", "install", "-a", "runtime/cgo")
    146 			cmd.Stdout = os.Stdout
    147 			cmd.Stderr = os.Stderr
    148 			if err := cmd.Run(); err != nil {
    149 				log.Fatalf("reverting mach exception handler: %v", err)
    150 			}
    151 		}()
    152 	}
    153 
    154 	t.timeoutScale = 1
    155 	switch t.goarch {
    156 	case "arm":
    157 		t.timeoutScale = 2
    158 	case "mips", "mipsle", "mips64", "mips64le":
    159 		t.timeoutScale = 4
    160 	}
    161 	if s := os.Getenv("GO_TEST_TIMEOUT_SCALE"); s != "" {
    162 		t.timeoutScale, err = strconv.Atoi(s)
    163 		if err != nil {
    164 			log.Fatalf("failed to parse $GO_TEST_TIMEOUT_SCALE = %q as integer: %v", s, err)
    165 		}
    166 	}
    167 
    168 	if t.runRxStr != "" {
    169 		if t.runRxStr[0] == '!' {
    170 			t.runRxWant = false
    171 			t.runRxStr = t.runRxStr[1:]
    172 		} else {
    173 			t.runRxWant = true
    174 		}
    175 		t.runRx = regexp.MustCompile(t.runRxStr)
    176 	}
    177 
    178 	t.registerTests()
    179 	if t.listMode {
    180 		for _, tt := range t.tests {
    181 			fmt.Println(tt.name)
    182 		}
    183 		return
    184 	}
    185 
    186 	// we must unset GOROOT_FINAL before tests, because runtime/debug requires
    187 	// correct access to source code, so if we have GOROOT_FINAL in effect,
    188 	// at least runtime/debug test will fail.
    189 	os.Unsetenv("GOROOT_FINAL")
    190 
    191 	for _, name := range t.runNames {
    192 		if !t.isRegisteredTestName(name) {
    193 			log.Fatalf("unknown test %q", name)
    194 		}
    195 	}
    196 
    197 	for _, dt := range t.tests {
    198 		if !t.shouldRunTest(dt.name) {
    199 			t.partial = true
    200 			continue
    201 		}
    202 		dt := dt // dt used in background after this iteration
    203 		if err := dt.fn(&dt); err != nil {
    204 			t.runPending(&dt) // in case that hasn't been done yet
    205 			t.failed = true
    206 			if t.keepGoing {
    207 				log.Printf("Failed: %v", err)
    208 			} else {
    209 				log.Fatalf("Failed: %v", err)
    210 			}
    211 		}
    212 	}
    213 	t.runPending(nil)
    214 	if t.failed {
    215 		fmt.Println("\nFAILED")
    216 		os.Exit(1)
    217 	} else if t.partial {
    218 		fmt.Println("\nALL TESTS PASSED (some were excluded)")
    219 	} else {
    220 		fmt.Println("\nALL TESTS PASSED")
    221 	}
    222 }
    223 
    224 func (t *tester) shouldRunTest(name string) bool {
    225 	if t.runRx != nil {
    226 		return t.runRx.MatchString(name) == t.runRxWant
    227 	}
    228 	if len(t.runNames) == 0 {
    229 		return true
    230 	}
    231 	for _, runName := range t.runNames {
    232 		if runName == name {
    233 			return true
    234 		}
    235 	}
    236 	return false
    237 }
    238 
    239 func (t *tester) tags() string {
    240 	if t.iOS() {
    241 		return "-tags=lldb"
    242 	}
    243 	return "-tags="
    244 }
    245 
    246 func (t *tester) timeout(sec int) string {
    247 	return "-timeout=" + fmt.Sprint(time.Duration(sec)*time.Second*time.Duration(t.timeoutScale))
    248 }
    249 
    250 // ranGoTest and stdMatches are state closed over by the stdlib
    251 // testing func in registerStdTest below. The tests are run
    252 // sequentially, so there's no need for locks.
    253 //
    254 // ranGoBench and benchMatches are the same, but are only used
    255 // in -race mode.
    256 var (
    257 	ranGoTest  bool
    258 	stdMatches []string
    259 
    260 	ranGoBench   bool
    261 	benchMatches []string
    262 )
    263 
    264 func (t *tester) registerStdTest(pkg string) {
    265 	testName := "go_test:" + pkg
    266 	if t.runRx == nil || t.runRx.MatchString(testName) {
    267 		stdMatches = append(stdMatches, pkg)
    268 	}
    269 	t.tests = append(t.tests, distTest{
    270 		name:    testName,
    271 		heading: "Testing packages.",
    272 		fn: func(dt *distTest) error {
    273 			if ranGoTest {
    274 				return nil
    275 			}
    276 			t.runPending(dt)
    277 			ranGoTest = true
    278 			args := []string{
    279 				"test",
    280 				"-short",
    281 				t.tags(),
    282 				t.timeout(180),
    283 				"-gcflags=" + os.Getenv("GO_GCFLAGS"),
    284 			}
    285 			if t.race {
    286 				args = append(args, "-race")
    287 			}
    288 			if t.compileOnly {
    289 				args = append(args, "-run=^$")
    290 			}
    291 			args = append(args, stdMatches...)
    292 			cmd := exec.Command("go", args...)
    293 			cmd.Stdout = os.Stdout
    294 			cmd.Stderr = os.Stderr
    295 			return cmd.Run()
    296 		},
    297 	})
    298 }
    299 
    300 func (t *tester) registerRaceBenchTest(pkg string) {
    301 	testName := "go_test_bench:" + pkg
    302 	if t.runRx == nil || t.runRx.MatchString(testName) {
    303 		benchMatches = append(benchMatches, pkg)
    304 	}
    305 	t.tests = append(t.tests, distTest{
    306 		name:    testName,
    307 		heading: "Running benchmarks briefly.",
    308 		fn: func(dt *distTest) error {
    309 			if ranGoBench {
    310 				return nil
    311 			}
    312 			t.runPending(dt)
    313 			ranGoBench = true
    314 			args := []string{
    315 				"test",
    316 				"-short",
    317 				"-race",
    318 				"-run=^$", // nothing. only benchmarks.
    319 				"-benchtime=.1s",
    320 				"-cpu=4",
    321 			}
    322 			if !t.compileOnly {
    323 				args = append(args, "-bench=.*")
    324 			}
    325 			args = append(args, benchMatches...)
    326 			cmd := exec.Command("go", args...)
    327 			cmd.Stdout = os.Stdout
    328 			cmd.Stderr = os.Stderr
    329 			return cmd.Run()
    330 		},
    331 	})
    332 }
    333 
    334 // stdOutErrAreTerminals is defined in test_linux.go, to report
    335 // whether stdout & stderr are terminals.
    336 var stdOutErrAreTerminals func() bool
    337 
    338 func (t *tester) registerTests() {
    339 	if strings.HasSuffix(os.Getenv("GO_BUILDER_NAME"), "-vetall") {
    340 		// Run vet over std and cmd and call it quits.
    341 		t.tests = append(t.tests, distTest{
    342 			name:    "vet/all",
    343 			heading: "go vet std cmd",
    344 			fn: func(dt *distTest) error {
    345 				// This runs vet/all for the current platform.
    346 				// TODO: on a fast builder or builders, run over all platforms.
    347 				t.addCmd(dt, "src/cmd/vet/all", "go", "run", "main.go", "-all")
    348 				return nil
    349 			},
    350 		})
    351 		return
    352 	}
    353 
    354 	// This test needs its stdout/stderr to be terminals, so we don't run it from cmd/go's tests.
    355 	// See issue 18153.
    356 	if t.goos == "linux" {
    357 		t.tests = append(t.tests, distTest{
    358 			name:    "cmd_go_test_terminal",
    359 			heading: "cmd/go terminal test",
    360 			fn: func(dt *distTest) error {
    361 				t.runPending(dt)
    362 				if !stdOutErrAreTerminals() {
    363 					fmt.Println("skipping terminal test; stdout/stderr not terminals")
    364 					return nil
    365 				}
    366 				cmd := exec.Command("go", "test")
    367 				cmd.Dir = filepath.Join(os.Getenv("GOROOT"), "src/cmd/go/testdata/testterminal18153")
    368 				cmd.Stdout = os.Stdout
    369 				cmd.Stderr = os.Stderr
    370 				return cmd.Run()
    371 			},
    372 		})
    373 	}
    374 
    375 	// Fast path to avoid the ~1 second of `go list std cmd` when
    376 	// the caller lists specific tests to run. (as the continuous
    377 	// build coordinator does).
    378 	if len(t.runNames) > 0 {
    379 		for _, name := range t.runNames {
    380 			if strings.HasPrefix(name, "go_test:") {
    381 				t.registerStdTest(strings.TrimPrefix(name, "go_test:"))
    382 			}
    383 			if strings.HasPrefix(name, "go_test_bench:") {
    384 				t.registerRaceBenchTest(strings.TrimPrefix(name, "go_test_bench:"))
    385 			}
    386 		}
    387 	} else {
    388 		// Use a format string to only list packages and commands that have tests.
    389 		const format = "{{if (or .TestGoFiles .XTestGoFiles)}}{{.ImportPath}}{{end}}"
    390 		cmd := exec.Command("go", "list", "-f", format)
    391 		if t.race {
    392 			cmd.Args = append(cmd.Args, "-tags", "race")
    393 		}
    394 		cmd.Args = append(cmd.Args, "std")
    395 		if !t.race {
    396 			cmd.Args = append(cmd.Args, "cmd")
    397 		}
    398 		all, err := cmd.Output()
    399 		if err != nil {
    400 			log.Fatalf("Error running go list std cmd: %v, %s", err, all)
    401 		}
    402 		pkgs := strings.Fields(string(all))
    403 		for _, pkg := range pkgs {
    404 			t.registerStdTest(pkg)
    405 		}
    406 		if t.race {
    407 			for _, pkg := range pkgs {
    408 				if t.packageHasBenchmarks(pkg) {
    409 					t.registerRaceBenchTest(pkg)
    410 				}
    411 			}
    412 		}
    413 	}
    414 
    415 	if t.race {
    416 		return
    417 	}
    418 
    419 	// Runtime CPU tests.
    420 	if !t.compileOnly {
    421 		testName := "runtime:cpu124"
    422 		t.tests = append(t.tests, distTest{
    423 			name:    testName,
    424 			heading: "GOMAXPROCS=2 runtime -cpu=1,2,4",
    425 			fn: func(dt *distTest) error {
    426 				cmd := t.addCmd(dt, "src", "go", "test", "-short", t.timeout(300), t.tags(), "runtime", "-cpu=1,2,4")
    427 				// We set GOMAXPROCS=2 in addition to -cpu=1,2,4 in order to test runtime bootstrap code,
    428 				// creation of first goroutines and first garbage collections in the parallel setting.
    429 				cmd.Env = mergeEnvLists([]string{"GOMAXPROCS=2"}, os.Environ())
    430 				return nil
    431 			},
    432 		})
    433 	}
    434 
    435 	// Test that internal linking of standard packages does not
    436 	// require libgcc. This ensures that we can install a Go
    437 	// release on a system that does not have a C compiler
    438 	// installed and still build Go programs (that don't use cgo).
    439 	for _, pkg := range cgoPackages {
    440 		if !t.internalLink() {
    441 			break
    442 		}
    443 
    444 		// ARM libgcc may be Thumb, which internal linking does not support.
    445 		if t.goarch == "arm" {
    446 			break
    447 		}
    448 
    449 		pkg := pkg
    450 		var run string
    451 		if pkg == "net" {
    452 			run = "TestTCPStress"
    453 		}
    454 		t.tests = append(t.tests, distTest{
    455 			name:    "nolibgcc:" + pkg,
    456 			heading: "Testing without libgcc.",
    457 			fn: func(dt *distTest) error {
    458 				t.addCmd(dt, "src", "go", "test", "-short", "-ldflags=-linkmode=internal -libgcc=none", t.tags(), pkg, t.runFlag(run))
    459 				return nil
    460 			},
    461 		})
    462 	}
    463 
    464 	// Test internal linking of PIE binaries where it is supported.
    465 	if t.goos == "linux" && t.goarch == "amd64" {
    466 		t.tests = append(t.tests, distTest{
    467 			name:    "pie_internal",
    468 			heading: "internal linking of -buildmode=pie",
    469 			fn: func(dt *distTest) error {
    470 				t.addCmd(dt, "src", "go", "test", "reflect", "-short", "-buildmode=pie", "-ldflags=-linkmode=internal", t.timeout(60), t.tags(), t.runFlag(""))
    471 				return nil
    472 			},
    473 		})
    474 	}
    475 
    476 	// sync tests
    477 	t.tests = append(t.tests, distTest{
    478 		name:    "sync_cpu",
    479 		heading: "sync -cpu=10",
    480 		fn: func(dt *distTest) error {
    481 			t.addCmd(dt, "src", "go", "test", "sync", "-short", t.timeout(120), t.tags(), "-cpu=10", t.runFlag(""))
    482 			return nil
    483 		},
    484 	})
    485 
    486 	if t.cgoEnabled && !t.iOS() {
    487 		// Disabled on iOS. golang.org/issue/15919
    488 		t.tests = append(t.tests, distTest{
    489 			name:    "cgo_stdio",
    490 			heading: "../misc/cgo/stdio",
    491 			fn: func(dt *distTest) error {
    492 				t.addCmd(dt, "misc/cgo/stdio", "go", "run", filepath.Join(os.Getenv("GOROOT"), "test/run.go"), "-", ".")
    493 				return nil
    494 			},
    495 		})
    496 		t.tests = append(t.tests, distTest{
    497 			name:    "cgo_life",
    498 			heading: "../misc/cgo/life",
    499 			fn: func(dt *distTest) error {
    500 				t.addCmd(dt, "misc/cgo/life", "go", "run", filepath.Join(os.Getenv("GOROOT"), "test/run.go"), "-", ".")
    501 				return nil
    502 			},
    503 		})
    504 		fortran := os.Getenv("FC")
    505 		if fortran == "" {
    506 			fortran, _ = exec.LookPath("gfortran")
    507 		}
    508 		if t.hasBash() && fortran != "" {
    509 			t.tests = append(t.tests, distTest{
    510 				name:    "cgo_fortran",
    511 				heading: "../misc/cgo/fortran",
    512 				fn: func(dt *distTest) error {
    513 					t.addCmd(dt, "misc/cgo/fortran", "./test.bash", fortran)
    514 					return nil
    515 				},
    516 			})
    517 		}
    518 	}
    519 	if t.cgoEnabled {
    520 		t.tests = append(t.tests, distTest{
    521 			name:    "cgo_test",
    522 			heading: "../misc/cgo/test",
    523 			fn:      t.cgoTest,
    524 		})
    525 	}
    526 
    527 	if t.raceDetectorSupported() {
    528 		t.tests = append(t.tests, distTest{
    529 			name:    "race",
    530 			heading: "Testing race detector",
    531 			fn:      t.raceTest,
    532 		})
    533 	}
    534 
    535 	if t.hasBash() && t.cgoEnabled && t.goos != "android" && t.goos != "darwin" {
    536 		t.registerTest("testgodefs", "../misc/cgo/testgodefs", "./test.bash")
    537 	}
    538 	if t.cgoEnabled {
    539 		if t.cgoTestSOSupported() {
    540 			t.tests = append(t.tests, distTest{
    541 				name:    "testso",
    542 				heading: "../misc/cgo/testso",
    543 				fn: func(dt *distTest) error {
    544 					return t.cgoTestSO(dt, "misc/cgo/testso")
    545 				},
    546 			})
    547 			t.tests = append(t.tests, distTest{
    548 				name:    "testsovar",
    549 				heading: "../misc/cgo/testsovar",
    550 				fn: func(dt *distTest) error {
    551 					return t.cgoTestSO(dt, "misc/cgo/testsovar")
    552 				},
    553 			})
    554 		}
    555 		if t.supportedBuildmode("c-archive") {
    556 			t.registerHostTest("testcarchive", "../misc/cgo/testcarchive", "misc/cgo/testcarchive", "carchive_test.go")
    557 		}
    558 		if t.supportedBuildmode("c-shared") {
    559 			t.registerTest("testcshared", "../misc/cgo/testcshared", "./test.bash")
    560 		}
    561 		if t.supportedBuildmode("shared") {
    562 			t.registerTest("testshared", "../misc/cgo/testshared", "go", "test")
    563 		}
    564 		if t.supportedBuildmode("plugin") {
    565 			t.registerTest("testplugin", "../misc/cgo/testplugin", "./test.bash")
    566 		}
    567 		if t.gohostos == "linux" && t.goarch == "amd64" {
    568 			t.registerTest("testasan", "../misc/cgo/testasan", "go", "run", "main.go")
    569 		}
    570 		if t.goos == "linux" && t.goarch == "amd64" {
    571 			t.registerTest("testsanitizers", "../misc/cgo/testsanitizers", "./test.bash")
    572 		}
    573 		if t.hasBash() && t.goos != "android" && !t.iOS() && t.gohostos != "windows" {
    574 			t.registerTest("cgo_errors", "../misc/cgo/errors", "./test.bash")
    575 		}
    576 		if t.gohostos == "linux" && t.extLink() {
    577 			t.registerTest("testsigfwd", "../misc/cgo/testsigfwd", "go", "run", "main.go")
    578 		}
    579 	}
    580 
    581 	// Doc tests only run on builders.
    582 	// They find problems approximately never.
    583 	if t.hasBash() && t.goos != "nacl" && t.goos != "android" && !t.iOS() && os.Getenv("GO_BUILDER_NAME") != "" {
    584 		t.registerTest("doc_progs", "../doc/progs", "time", "go", "run", "run.go")
    585 		t.registerTest("wiki", "../doc/articles/wiki", "./test.bash")
    586 		t.registerTest("codewalk", "../doc/codewalk", "time", "./run")
    587 	}
    588 
    589 	if t.goos != "android" && !t.iOS() {
    590 		t.registerTest("bench_go1", "../test/bench/go1", "go", "test", t.timeout(600), t.runFlag(""))
    591 	}
    592 	if t.goos != "android" && !t.iOS() {
    593 		const nShards = 5
    594 		for shard := 0; shard < nShards; shard++ {
    595 			shard := shard
    596 			t.tests = append(t.tests, distTest{
    597 				name:    fmt.Sprintf("test:%d_%d", shard, nShards),
    598 				heading: "../test",
    599 				fn:      func(dt *distTest) error { return t.testDirTest(dt, shard, nShards) },
    600 			})
    601 		}
    602 	}
    603 	if t.goos != "nacl" && t.goos != "android" && !t.iOS() {
    604 		t.tests = append(t.tests, distTest{
    605 			name:    "api",
    606 			heading: "API check",
    607 			fn: func(dt *distTest) error {
    608 				if t.compileOnly {
    609 					t.addCmd(dt, "src", "go", "build", filepath.Join(t.goroot, "src/cmd/api/run.go"))
    610 					return nil
    611 				}
    612 				t.addCmd(dt, "src", "go", "run", filepath.Join(t.goroot, "src/cmd/api/run.go"))
    613 				return nil
    614 			},
    615 		})
    616 	}
    617 }
    618 
    619 // isRegisteredTestName reports whether a test named testName has already
    620 // been registered.
    621 func (t *tester) isRegisteredTestName(testName string) bool {
    622 	for _, tt := range t.tests {
    623 		if tt.name == testName {
    624 			return true
    625 		}
    626 	}
    627 	return false
    628 }
    629 
    630 func (t *tester) registerTest1(seq bool, name, dirBanner, bin string, args ...string) {
    631 	if bin == "time" && !t.haveTime {
    632 		bin, args = args[0], args[1:]
    633 	}
    634 	if t.isRegisteredTestName(name) {
    635 		panic("duplicate registered test name " + name)
    636 	}
    637 	t.tests = append(t.tests, distTest{
    638 		name:    name,
    639 		heading: dirBanner,
    640 		fn: func(dt *distTest) error {
    641 			if seq {
    642 				t.runPending(dt)
    643 				return t.dirCmd(filepath.Join(t.goroot, "src", dirBanner), bin, args...).Run()
    644 			}
    645 			t.addCmd(dt, filepath.Join(t.goroot, "src", dirBanner), bin, args...)
    646 			return nil
    647 		},
    648 	})
    649 }
    650 
    651 func (t *tester) registerTest(name, dirBanner, bin string, args ...string) {
    652 	t.registerTest1(false, name, dirBanner, bin, args...)
    653 }
    654 
    655 func (t *tester) registerSeqTest(name, dirBanner, bin string, args ...string) {
    656 	t.registerTest1(true, name, dirBanner, bin, args...)
    657 }
    658 
    659 func (t *tester) bgDirCmd(dir, bin string, args ...string) *exec.Cmd {
    660 	cmd := exec.Command(bin, args...)
    661 	if filepath.IsAbs(dir) {
    662 		cmd.Dir = dir
    663 	} else {
    664 		cmd.Dir = filepath.Join(t.goroot, dir)
    665 	}
    666 	return cmd
    667 }
    668 
    669 func (t *tester) dirCmd(dir, bin string, args ...string) *exec.Cmd {
    670 	cmd := t.bgDirCmd(dir, bin, args...)
    671 	cmd.Stdout = os.Stdout
    672 	cmd.Stderr = os.Stderr
    673 	if vflag > 1 {
    674 		errprintf("%s\n", strings.Join(cmd.Args, " "))
    675 	}
    676 	return cmd
    677 }
    678 
    679 func (t *tester) addCmd(dt *distTest, dir, bin string, args ...string) *exec.Cmd {
    680 	w := &work{
    681 		dt:  dt,
    682 		cmd: t.bgDirCmd(dir, bin, args...),
    683 	}
    684 	t.worklist = append(t.worklist, w)
    685 	return w.cmd
    686 }
    687 
    688 func (t *tester) iOS() bool {
    689 	return t.goos == "darwin" && (t.goarch == "arm" || t.goarch == "arm64")
    690 }
    691 
    692 func (t *tester) out(v string) {
    693 	if t.banner == "" {
    694 		return
    695 	}
    696 	fmt.Println("\n" + t.banner + v)
    697 }
    698 
    699 func (t *tester) extLink() bool {
    700 	pair := t.gohostos + "-" + t.goarch
    701 	switch pair {
    702 	case "android-arm",
    703 		"darwin-arm", "darwin-arm64",
    704 		"dragonfly-386", "dragonfly-amd64",
    705 		"freebsd-386", "freebsd-amd64", "freebsd-arm",
    706 		"linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-mips64", "linux-mips64le", "linux-mips", "linux-mipsle", "linux-s390x",
    707 		"netbsd-386", "netbsd-amd64",
    708 		"openbsd-386", "openbsd-amd64",
    709 		"windows-386", "windows-amd64":
    710 		return true
    711 	case "darwin-386", "darwin-amd64":
    712 		// linkmode=external fails on OS X 10.6 and earlier == Darwin
    713 		// 10.8 and earlier.
    714 		unameR, err := exec.Command("uname", "-r").Output()
    715 		if err != nil {
    716 			log.Fatalf("uname -r: %v", err)
    717 		}
    718 		major, _ := strconv.Atoi(string(unameR[:bytes.IndexByte(unameR, '.')]))
    719 		return major > 10
    720 	}
    721 	return false
    722 }
    723 
    724 func (t *tester) internalLink() bool {
    725 	if t.gohostos == "dragonfly" {
    726 		// linkmode=internal fails on dragonfly since errno is a TLS relocation.
    727 		return false
    728 	}
    729 	if t.gohostarch == "ppc64le" {
    730 		// linkmode=internal fails on ppc64le because cmd/link doesn't
    731 		// handle the TOC correctly (issue 15409).
    732 		return false
    733 	}
    734 	if t.goos == "android" {
    735 		return false
    736 	}
    737 	if t.goos == "darwin" && (t.goarch == "arm" || t.goarch == "arm64") {
    738 		return false
    739 	}
    740 	// Internally linking cgo is incomplete on some architectures.
    741 	// https://golang.org/issue/10373
    742 	// https://golang.org/issue/14449
    743 	if t.goarch == "arm64" || t.goarch == "mips64" || t.goarch == "mips64le" || t.goarch == "mips" || t.goarch == "mipsle" {
    744 		return false
    745 	}
    746 	return true
    747 }
    748 
    749 func (t *tester) supportedBuildmode(mode string) bool {
    750 	pair := t.goos + "-" + t.goarch
    751 	switch mode {
    752 	case "c-archive":
    753 		if !t.extLink() {
    754 			return false
    755 		}
    756 		switch pair {
    757 		case "darwin-386", "darwin-amd64", "darwin-arm", "darwin-arm64",
    758 			"linux-amd64", "linux-386", "windows-amd64", "windows-386":
    759 			return true
    760 		}
    761 		return false
    762 	case "c-shared":
    763 		switch pair {
    764 		case "linux-386", "linux-amd64", "linux-arm", "linux-arm64",
    765 			"darwin-amd64", "darwin-386",
    766 			"android-arm", "android-arm64", "android-386":
    767 			return true
    768 		}
    769 		return false
    770 	case "shared":
    771 		switch pair {
    772 		case "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-s390x":
    773 			return true
    774 		}
    775 		return false
    776 	case "plugin":
    777 		if os.Getenv("GO_BUILDER_NAME") == "linux-amd64-noopt" {
    778 			// Skip the plugin tests on noopt. They're
    779 			// causing build failures potentially
    780 			// obscuring other issues. This is hopefully a
    781 			// temporary workaround. See golang.org/issue/17937.
    782 			return false
    783 		}
    784 
    785 		// linux-arm64 is missing because it causes the external linker
    786 		// to crash, see https://golang.org/issue/17138
    787 		switch pair {
    788 		case "linux-386", "linux-amd64", "linux-arm":
    789 			return true
    790 		}
    791 		return false
    792 	default:
    793 		log.Fatalf("internal error: unknown buildmode %s", mode)
    794 		return false
    795 	}
    796 }
    797 
    798 func (t *tester) registerHostTest(name, heading, dir, pkg string) {
    799 	t.tests = append(t.tests, distTest{
    800 		name:    name,
    801 		heading: heading,
    802 		fn: func(dt *distTest) error {
    803 			t.runPending(dt)
    804 			return t.runHostTest(dir, pkg)
    805 		},
    806 	})
    807 }
    808 
    809 func (t *tester) runHostTest(dir, pkg string) error {
    810 	env := mergeEnvLists([]string{"GOARCH=" + t.gohostarch, "GOOS=" + t.gohostos}, os.Environ())
    811 	defer os.Remove(filepath.Join(t.goroot, dir, "test.test"))
    812 	cmd := t.dirCmd(dir, "go", "test", t.tags(), "-c", "-o", "test.test", pkg)
    813 	cmd.Env = env
    814 	if err := cmd.Run(); err != nil {
    815 		return err
    816 	}
    817 	return t.dirCmd(dir, "./test.test").Run()
    818 }
    819 
    820 func (t *tester) cgoTest(dt *distTest) error {
    821 	env := mergeEnvLists([]string{"GOTRACEBACK=2"}, os.Environ())
    822 
    823 	cmd := t.addCmd(dt, "misc/cgo/test", "go", "test", t.tags(), "-ldflags", "-linkmode=auto", t.runFlag(""))
    824 	cmd.Env = env
    825 
    826 	if t.internalLink() {
    827 		cmd := t.addCmd(dt, "misc/cgo/test", "go", "test", "-ldflags", "-linkmode=internal", t.runFlag(""))
    828 		cmd.Env = env
    829 	}
    830 
    831 	pair := t.gohostos + "-" + t.goarch
    832 	switch pair {
    833 	case "darwin-386", "darwin-amd64",
    834 		"openbsd-386", "openbsd-amd64",
    835 		"windows-386", "windows-amd64":
    836 		// test linkmode=external, but __thread not supported, so skip testtls.
    837 		if !t.extLink() {
    838 			break
    839 		}
    840 		cmd := t.addCmd(dt, "misc/cgo/test", "go", "test", "-ldflags", "-linkmode=external")
    841 		cmd.Env = env
    842 		cmd = t.addCmd(dt, "misc/cgo/test", "go", "test", "-ldflags", "-linkmode=external -s")
    843 		cmd.Env = env
    844 	case "android-arm",
    845 		"dragonfly-386", "dragonfly-amd64",
    846 		"freebsd-386", "freebsd-amd64", "freebsd-arm",
    847 		"linux-386", "linux-amd64", "linux-arm", "linux-ppc64le", "linux-s390x",
    848 		"netbsd-386", "netbsd-amd64":
    849 
    850 		cmd := t.addCmd(dt, "misc/cgo/test", "go", "test", "-ldflags", "-linkmode=external")
    851 		cmd.Env = env
    852 
    853 		cmd = t.addCmd(dt, "misc/cgo/testtls", "go", "test", "-ldflags", "-linkmode=auto")
    854 		cmd.Env = env
    855 
    856 		cmd = t.addCmd(dt, "misc/cgo/testtls", "go", "test", "-ldflags", "-linkmode=external")
    857 		cmd.Env = env
    858 
    859 		switch pair {
    860 		case "netbsd-386", "netbsd-amd64":
    861 			// no static linking
    862 		case "freebsd-arm":
    863 			// -fPIC compiled tls code will use __tls_get_addr instead
    864 			// of __aeabi_read_tp, however, on FreeBSD/ARM, __tls_get_addr
    865 			// is implemented in rtld-elf, so -fPIC isn't compatible with
    866 			// static linking on FreeBSD/ARM with clang. (cgo depends on
    867 			// -fPIC fundamentally.)
    868 		default:
    869 			cc := mustEnv("CC")
    870 			cmd := t.dirCmd("misc/cgo/test",
    871 				cc, "-xc", "-o", "/dev/null", "-static", "-")
    872 			cmd.Env = env
    873 			cmd.Stdin = strings.NewReader("int main() {}")
    874 			if err := cmd.Run(); err != nil {
    875 				fmt.Println("No support for static linking found (lacks libc.a?), skip cgo static linking test.")
    876 			} else {
    877 				if t.goos != "android" {
    878 					cmd = t.addCmd(dt, "misc/cgo/testtls", "go", "test", "-ldflags", `-linkmode=external -extldflags "-static -pthread"`)
    879 					cmd.Env = env
    880 				}
    881 
    882 				cmd = t.addCmd(dt, "misc/cgo/nocgo", "go", "test")
    883 				cmd.Env = env
    884 
    885 				cmd = t.addCmd(dt, "misc/cgo/nocgo", "go", "test", "-ldflags", `-linkmode=external`)
    886 				cmd.Env = env
    887 
    888 				if t.goos != "android" {
    889 					cmd = t.addCmd(dt, "misc/cgo/nocgo", "go", "test", "-ldflags", `-linkmode=external -extldflags "-static -pthread"`)
    890 					cmd.Env = env
    891 				}
    892 			}
    893 
    894 			if pair != "freebsd-amd64" { // clang -pie fails to link misc/cgo/test
    895 				cmd := t.dirCmd("misc/cgo/test",
    896 					cc, "-xc", "-o", "/dev/null", "-pie", "-")
    897 				cmd.Env = env
    898 				cmd.Stdin = strings.NewReader("int main() {}")
    899 				if err := cmd.Run(); err != nil {
    900 					fmt.Println("No support for -pie found, skip cgo PIE test.")
    901 				} else {
    902 					cmd = t.addCmd(dt, "misc/cgo/test", "go", "test", "-ldflags", `-linkmode=external -extldflags "-pie"`)
    903 					cmd.Env = env
    904 
    905 					cmd = t.addCmd(dt, "misc/cgo/testtls", "go", "test", "-ldflags", `-linkmode=external -extldflags "-pie"`)
    906 					cmd.Env = env
    907 
    908 					cmd = t.addCmd(dt, "misc/cgo/nocgo", "go", "test", "-ldflags", `-linkmode=external -extldflags "-pie"`)
    909 					cmd.Env = env
    910 
    911 				}
    912 			}
    913 		}
    914 	}
    915 
    916 	return nil
    917 }
    918 
    919 // run pending test commands, in parallel, emitting headers as appropriate.
    920 // When finished, emit header for nextTest, which is going to run after the
    921 // pending commands are done (and runPending returns).
    922 // A test should call runPending if it wants to make sure that it is not
    923 // running in parallel with earlier tests, or if it has some other reason
    924 // for needing the earlier tests to be done.
    925 func (t *tester) runPending(nextTest *distTest) {
    926 	worklist := t.worklist
    927 	t.worklist = nil
    928 	for _, w := range worklist {
    929 		w.start = make(chan bool)
    930 		w.end = make(chan bool)
    931 		go func(w *work) {
    932 			if !<-w.start {
    933 				w.out = []byte(fmt.Sprintf("skipped due to earlier error\n"))
    934 			} else {
    935 				w.out, w.err = w.cmd.CombinedOutput()
    936 			}
    937 			w.end <- true
    938 		}(w)
    939 	}
    940 
    941 	started := 0
    942 	ended := 0
    943 	var last *distTest
    944 	for ended < len(worklist) {
    945 		for started < len(worklist) && started-ended < maxbg {
    946 			//println("start", started)
    947 			w := worklist[started]
    948 			started++
    949 			w.start <- !t.failed || t.keepGoing
    950 		}
    951 		w := worklist[ended]
    952 		dt := w.dt
    953 		if dt.heading != "" && t.lastHeading != dt.heading {
    954 			t.lastHeading = dt.heading
    955 			t.out(dt.heading)
    956 		}
    957 		if dt != last {
    958 			// Assumes all the entries for a single dt are in one worklist.
    959 			last = w.dt
    960 			if vflag > 0 {
    961 				fmt.Printf("# go tool dist test -run=^%s$\n", dt.name)
    962 			}
    963 		}
    964 		if vflag > 1 {
    965 			errprintf("%s\n", strings.Join(w.cmd.Args, " "))
    966 		}
    967 		//println("wait", ended)
    968 		ended++
    969 		<-w.end
    970 		os.Stdout.Write(w.out)
    971 		if w.err != nil {
    972 			log.Printf("Failed: %v", w.err)
    973 			t.failed = true
    974 		}
    975 	}
    976 	if t.failed && !t.keepGoing {
    977 		log.Fatal("FAILED")
    978 	}
    979 
    980 	if dt := nextTest; dt != nil {
    981 		if dt.heading != "" && t.lastHeading != dt.heading {
    982 			t.lastHeading = dt.heading
    983 			t.out(dt.heading)
    984 		}
    985 		if vflag > 0 {
    986 			fmt.Printf("# go tool dist test -run=^%s$\n", dt.name)
    987 		}
    988 	}
    989 }
    990 
    991 func (t *tester) cgoTestSOSupported() bool {
    992 	if t.goos == "android" || t.iOS() {
    993 		// No exec facility on Android or iOS.
    994 		return false
    995 	}
    996 	if t.goarch == "ppc64" {
    997 		// External linking not implemented on ppc64 (issue #8912).
    998 		return false
    999 	}
   1000 	if t.goarch == "mips64le" || t.goarch == "mips64" {
   1001 		// External linking not implemented on mips64.
   1002 		return false
   1003 	}
   1004 	return true
   1005 }
   1006 
   1007 func (t *tester) cgoTestSO(dt *distTest, testpath string) error {
   1008 	t.runPending(dt)
   1009 
   1010 	dir := filepath.Join(t.goroot, testpath)
   1011 
   1012 	// build shared object
   1013 	output, err := exec.Command("go", "env", "CC").Output()
   1014 	if err != nil {
   1015 		return fmt.Errorf("Error running go env CC: %v", err)
   1016 	}
   1017 	cc := strings.TrimSuffix(string(output), "\n")
   1018 	if cc == "" {
   1019 		return errors.New("CC environment variable (go env CC) cannot be empty")
   1020 	}
   1021 	output, err = exec.Command("go", "env", "GOGCCFLAGS").Output()
   1022 	if err != nil {
   1023 		return fmt.Errorf("Error running go env GOGCCFLAGS: %v", err)
   1024 	}
   1025 	gogccflags := strings.Split(strings.TrimSuffix(string(output), "\n"), " ")
   1026 
   1027 	ext := "so"
   1028 	args := append(gogccflags, "-shared")
   1029 	switch t.goos {
   1030 	case "darwin":
   1031 		ext = "dylib"
   1032 		args = append(args, "-undefined", "suppress", "-flat_namespace")
   1033 	case "windows":
   1034 		ext = "dll"
   1035 		args = append(args, "-DEXPORT_DLL")
   1036 	}
   1037 	sofname := "libcgosotest." + ext
   1038 	args = append(args, "-o", sofname, "cgoso_c.c")
   1039 
   1040 	if err := t.dirCmd(dir, cc, args...).Run(); err != nil {
   1041 		return err
   1042 	}
   1043 	defer os.Remove(filepath.Join(dir, sofname))
   1044 
   1045 	if err := t.dirCmd(dir, "go", "build", "-o", "main.exe", "main.go").Run(); err != nil {
   1046 		return err
   1047 	}
   1048 	defer os.Remove(filepath.Join(dir, "main.exe"))
   1049 
   1050 	cmd := t.dirCmd(dir, "./main.exe")
   1051 	if t.goos != "windows" {
   1052 		s := "LD_LIBRARY_PATH"
   1053 		if t.goos == "darwin" {
   1054 			s = "DYLD_LIBRARY_PATH"
   1055 		}
   1056 		cmd.Env = mergeEnvLists([]string{s + "=."}, os.Environ())
   1057 
   1058 		// On FreeBSD 64-bit architectures, the 32-bit linker looks for
   1059 		// different environment variables.
   1060 		if t.goos == "freebsd" && t.gohostarch == "386" {
   1061 			cmd.Env = mergeEnvLists([]string{"LD_32_LIBRARY_PATH=."}, cmd.Env)
   1062 		}
   1063 	}
   1064 	return cmd.Run()
   1065 }
   1066 
   1067 func (t *tester) hasBash() bool {
   1068 	switch t.gohostos {
   1069 	case "windows", "plan9":
   1070 		return false
   1071 	}
   1072 	return true
   1073 }
   1074 
   1075 func (t *tester) raceDetectorSupported() bool {
   1076 	switch t.gohostos {
   1077 	case "linux", "darwin", "freebsd", "windows":
   1078 		return t.cgoEnabled && t.goarch == "amd64" && t.gohostos == t.goos
   1079 	}
   1080 	return false
   1081 }
   1082 
   1083 func (t *tester) runFlag(rx string) string {
   1084 	if t.compileOnly {
   1085 		return "-run=^$"
   1086 	}
   1087 	return "-run=" + rx
   1088 }
   1089 
   1090 func (t *tester) raceTest(dt *distTest) error {
   1091 	t.addCmd(dt, "src", "go", "test", "-race", "-i", "runtime/race", "flag", "os/exec")
   1092 	t.addCmd(dt, "src", "go", "test", "-race", t.runFlag("Output"), "runtime/race")
   1093 	t.addCmd(dt, "src", "go", "test", "-race", "-short", t.runFlag("TestParse|TestEcho|TestStdinCloseRace"), "flag", "os/exec")
   1094 	// We don't want the following line, because it
   1095 	// slows down all.bash (by 10 seconds on my laptop).
   1096 	// The race builder should catch any error here, but doesn't.
   1097 	// TODO(iant): Figure out how to catch this.
   1098 	// t.addCmd(dt, "src", "go", "test", "-race", "-run=TestParallelTest", "cmd/go")
   1099 	if t.cgoEnabled {
   1100 		env := mergeEnvLists([]string{"GOTRACEBACK=2"}, os.Environ())
   1101 		cmd := t.addCmd(dt, "misc/cgo/test", "go", "test", "-race", "-short", t.runFlag(""))
   1102 		cmd.Env = env
   1103 	}
   1104 	if t.extLink() {
   1105 		// Test with external linking; see issue 9133.
   1106 		t.addCmd(dt, "src", "go", "test", "-race", "-short", "-ldflags=-linkmode=external", t.runFlag("TestParse|TestEcho|TestStdinCloseRace"), "flag", "os/exec")
   1107 	}
   1108 	return nil
   1109 }
   1110 
   1111 var runtest struct {
   1112 	sync.Once
   1113 	exe string
   1114 	err error
   1115 }
   1116 
   1117 func (t *tester) testDirTest(dt *distTest, shard, shards int) error {
   1118 	runtest.Do(func() {
   1119 		const exe = "runtest.exe" // named exe for Windows, but harmless elsewhere
   1120 		cmd := t.dirCmd("test", "go", "build", "-o", exe, "run.go")
   1121 		cmd.Env = mergeEnvLists([]string{"GOOS=" + t.gohostos, "GOARCH=" + t.gohostarch, "GOMAXPROCS="}, os.Environ())
   1122 		runtest.exe = filepath.Join(cmd.Dir, exe)
   1123 		if err := cmd.Run(); err != nil {
   1124 			runtest.err = err
   1125 			return
   1126 		}
   1127 		xatexit(func() {
   1128 			os.Remove(runtest.exe)
   1129 		})
   1130 	})
   1131 	if runtest.err != nil {
   1132 		return runtest.err
   1133 	}
   1134 	if t.compileOnly {
   1135 		return nil
   1136 	}
   1137 	t.addCmd(dt, "test", runtest.exe,
   1138 		fmt.Sprintf("--shard=%d", shard),
   1139 		fmt.Sprintf("--shards=%d", shards),
   1140 	)
   1141 	return nil
   1142 }
   1143 
   1144 // mergeEnvLists merges the two environment lists such that
   1145 // variables with the same name in "in" replace those in "out".
   1146 // out may be mutated.
   1147 func mergeEnvLists(in, out []string) []string {
   1148 NextVar:
   1149 	for _, inkv := range in {
   1150 		k := strings.SplitAfterN(inkv, "=", 2)[0]
   1151 		for i, outkv := range out {
   1152 			if strings.HasPrefix(outkv, k) {
   1153 				out[i] = inkv
   1154 				continue NextVar
   1155 			}
   1156 		}
   1157 		out = append(out, inkv)
   1158 	}
   1159 	return out
   1160 }
   1161 
   1162 // cgoPackages is the standard packages that use cgo.
   1163 var cgoPackages = []string{
   1164 	"crypto/x509",
   1165 	"net",
   1166 	"os/user",
   1167 }
   1168 
   1169 var funcBenchmark = []byte("\nfunc Benchmark")
   1170 
   1171 // packageHasBenchmarks reports whether pkg has benchmarks.
   1172 // On any error, it conservatively returns true.
   1173 //
   1174 // This exists just to eliminate work on the builders, since compiling
   1175 // a test in race mode just to discover it has no benchmarks costs a
   1176 // second or two per package, and this function returns false for
   1177 // about 100 packages.
   1178 func (t *tester) packageHasBenchmarks(pkg string) bool {
   1179 	pkgDir := filepath.Join(t.goroot, "src", pkg)
   1180 	d, err := os.Open(pkgDir)
   1181 	if err != nil {
   1182 		return true // conservatively
   1183 	}
   1184 	defer d.Close()
   1185 	names, err := d.Readdirnames(-1)
   1186 	if err != nil {
   1187 		return true // conservatively
   1188 	}
   1189 	for _, name := range names {
   1190 		if !strings.HasSuffix(name, "_test.go") {
   1191 			continue
   1192 		}
   1193 		slurp, err := ioutil.ReadFile(filepath.Join(pkgDir, name))
   1194 		if err != nil {
   1195 			return true // conservatively
   1196 		}
   1197 		if bytes.Contains(slurp, funcBenchmark) {
   1198 			return true
   1199 		}
   1200 	}
   1201 	return false
   1202 }
   1203