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