Black Lives Matter. Support the Equal Justice Initiative.

Source file src/internal/testenv/testenv.go

Documentation: internal/testenv

     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 testenv provides information about what functionality
     6  // is available in different testing environments run by the Go team.
     7  //
     8  // It is an internal package because these details are specific
     9  // to the Go team's test setup (on build.golang.org) and not
    10  // fundamental to tests in general.
    11  package testenv
    12  
    13  import (
    14  	"errors"
    15  	"flag"
    16  	"internal/cfg"
    17  	"os"
    18  	"os/exec"
    19  	"path/filepath"
    20  	"runtime"
    21  	"strconv"
    22  	"strings"
    23  	"sync"
    24  	"testing"
    25  )
    26  
    27  // Builder reports the name of the builder running this test
    28  // (for example, "linux-amd64" or "windows-386-gce").
    29  // If the test is not running on the build infrastructure,
    30  // Builder returns the empty string.
    31  func Builder() string {
    32  	return os.Getenv("GO_BUILDER_NAME")
    33  }
    34  
    35  // HasGoBuild reports whether the current system can build programs with ``go build''
    36  // and then run them with os.StartProcess or exec.Command.
    37  func HasGoBuild() bool {
    38  	if os.Getenv("GO_GCFLAGS") != "" {
    39  		// It's too much work to require every caller of the go command
    40  		// to pass along "-gcflags="+os.Getenv("GO_GCFLAGS").
    41  		// For now, if $GO_GCFLAGS is set, report that we simply can't
    42  		// run go build.
    43  		return false
    44  	}
    45  	switch runtime.GOOS {
    46  	case "android", "js", "ios":
    47  		return false
    48  	}
    49  	return true
    50  }
    51  
    52  // MustHaveGoBuild checks that the current system can build programs with ``go build''
    53  // and then run them with os.StartProcess or exec.Command.
    54  // If not, MustHaveGoBuild calls t.Skip with an explanation.
    55  func MustHaveGoBuild(t testing.TB) {
    56  	if os.Getenv("GO_GCFLAGS") != "" {
    57  		t.Skipf("skipping test: 'go build' not compatible with setting $GO_GCFLAGS")
    58  	}
    59  	if !HasGoBuild() {
    60  		t.Skipf("skipping test: 'go build' not available on %s/%s", runtime.GOOS, runtime.GOARCH)
    61  	}
    62  }
    63  
    64  // HasGoRun reports whether the current system can run programs with ``go run.''
    65  func HasGoRun() bool {
    66  	// For now, having go run and having go build are the same.
    67  	return HasGoBuild()
    68  }
    69  
    70  // MustHaveGoRun checks that the current system can run programs with ``go run.''
    71  // If not, MustHaveGoRun calls t.Skip with an explanation.
    72  func MustHaveGoRun(t testing.TB) {
    73  	if !HasGoRun() {
    74  		t.Skipf("skipping test: 'go run' not available on %s/%s", runtime.GOOS, runtime.GOARCH)
    75  	}
    76  }
    77  
    78  // GoToolPath reports the path to the Go tool.
    79  // It is a convenience wrapper around GoTool.
    80  // If the tool is unavailable GoToolPath calls t.Skip.
    81  // If the tool should be available and isn't, GoToolPath calls t.Fatal.
    82  func GoToolPath(t testing.TB) string {
    83  	MustHaveGoBuild(t)
    84  	path, err := GoTool()
    85  	if err != nil {
    86  		t.Fatal(err)
    87  	}
    88  	// Add all environment variables that affect the Go command to test metadata.
    89  	// Cached test results will be invalidate when these variables change.
    90  	// See golang.org/issue/32285.
    91  	for _, envVar := range strings.Fields(cfg.KnownEnv) {
    92  		os.Getenv(envVar)
    93  	}
    94  	return path
    95  }
    96  
    97  // GoTool reports the path to the Go tool.
    98  func GoTool() (string, error) {
    99  	if !HasGoBuild() {
   100  		return "", errors.New("platform cannot run go tool")
   101  	}
   102  	var exeSuffix string
   103  	if runtime.GOOS == "windows" {
   104  		exeSuffix = ".exe"
   105  	}
   106  	path := filepath.Join(runtime.GOROOT(), "bin", "go"+exeSuffix)
   107  	if _, err := os.Stat(path); err == nil {
   108  		return path, nil
   109  	}
   110  	goBin, err := exec.LookPath("go" + exeSuffix)
   111  	if err != nil {
   112  		return "", errors.New("cannot find go tool: " + err.Error())
   113  	}
   114  	return goBin, nil
   115  }
   116  
   117  // HasExec reports whether the current system can start new processes
   118  // using os.StartProcess or (more commonly) exec.Command.
   119  func HasExec() bool {
   120  	switch runtime.GOOS {
   121  	case "js", "ios":
   122  		return false
   123  	}
   124  	return true
   125  }
   126  
   127  // HasSrc reports whether the entire source tree is available under GOROOT.
   128  func HasSrc() bool {
   129  	switch runtime.GOOS {
   130  	case "ios":
   131  		return false
   132  	}
   133  	return true
   134  }
   135  
   136  // MustHaveExec checks that the current system can start new processes
   137  // using os.StartProcess or (more commonly) exec.Command.
   138  // If not, MustHaveExec calls t.Skip with an explanation.
   139  func MustHaveExec(t testing.TB) {
   140  	if !HasExec() {
   141  		t.Skipf("skipping test: cannot exec subprocess on %s/%s", runtime.GOOS, runtime.GOARCH)
   142  	}
   143  }
   144  
   145  var execPaths sync.Map // path -> error
   146  
   147  // MustHaveExecPath checks that the current system can start the named executable
   148  // using os.StartProcess or (more commonly) exec.Command.
   149  // If not, MustHaveExecPath calls t.Skip with an explanation.
   150  func MustHaveExecPath(t testing.TB, path string) {
   151  	MustHaveExec(t)
   152  
   153  	err, found := execPaths.Load(path)
   154  	if !found {
   155  		_, err = exec.LookPath(path)
   156  		err, _ = execPaths.LoadOrStore(path, err)
   157  	}
   158  	if err != nil {
   159  		t.Skipf("skipping test: %s: %s", path, err)
   160  	}
   161  }
   162  
   163  // HasExternalNetwork reports whether the current system can use
   164  // external (non-localhost) networks.
   165  func HasExternalNetwork() bool {
   166  	return !testing.Short() && runtime.GOOS != "js"
   167  }
   168  
   169  // MustHaveExternalNetwork checks that the current system can use
   170  // external (non-localhost) networks.
   171  // If not, MustHaveExternalNetwork calls t.Skip with an explanation.
   172  func MustHaveExternalNetwork(t testing.TB) {
   173  	if runtime.GOOS == "js" {
   174  		t.Skipf("skipping test: no external network on %s", runtime.GOOS)
   175  	}
   176  	if testing.Short() {
   177  		t.Skipf("skipping test: no external network in -short mode")
   178  	}
   179  }
   180  
   181  var haveCGO bool
   182  
   183  // HasCGO reports whether the current system can use cgo.
   184  func HasCGO() bool {
   185  	return haveCGO
   186  }
   187  
   188  // MustHaveCGO calls t.Skip if cgo is not available.
   189  func MustHaveCGO(t testing.TB) {
   190  	if !haveCGO {
   191  		t.Skipf("skipping test: no cgo")
   192  	}
   193  }
   194  
   195  // CanInternalLink reports whether the current system can link programs with
   196  // internal linking.
   197  // (This is the opposite of cmd/internal/sys.MustLinkExternal. Keep them in sync.)
   198  func CanInternalLink() bool {
   199  	switch runtime.GOOS {
   200  	case "android":
   201  		if runtime.GOARCH != "arm64" {
   202  			return false
   203  		}
   204  	case "ios":
   205  		if runtime.GOARCH == "arm64" {
   206  			return false
   207  		}
   208  	}
   209  	return true
   210  }
   211  
   212  // MustInternalLink checks that the current system can link programs with internal
   213  // linking.
   214  // If not, MustInternalLink calls t.Skip with an explanation.
   215  func MustInternalLink(t testing.TB) {
   216  	if !CanInternalLink() {
   217  		t.Skipf("skipping test: internal linking on %s/%s is not supported", runtime.GOOS, runtime.GOARCH)
   218  	}
   219  }
   220  
   221  // HasSymlink reports whether the current system can use os.Symlink.
   222  func HasSymlink() bool {
   223  	ok, _ := hasSymlink()
   224  	return ok
   225  }
   226  
   227  // MustHaveSymlink reports whether the current system can use os.Symlink.
   228  // If not, MustHaveSymlink calls t.Skip with an explanation.
   229  func MustHaveSymlink(t testing.TB) {
   230  	ok, reason := hasSymlink()
   231  	if !ok {
   232  		t.Skipf("skipping test: cannot make symlinks on %s/%s%s", runtime.GOOS, runtime.GOARCH, reason)
   233  	}
   234  }
   235  
   236  // HasLink reports whether the current system can use os.Link.
   237  func HasLink() bool {
   238  	// From Android release M (Marshmallow), hard linking files is blocked
   239  	// and an attempt to call link() on a file will return EACCES.
   240  	// - https://code.google.com/p/android-developer-preview/issues/detail?id=3150
   241  	return runtime.GOOS != "plan9" && runtime.GOOS != "android"
   242  }
   243  
   244  // MustHaveLink reports whether the current system can use os.Link.
   245  // If not, MustHaveLink calls t.Skip with an explanation.
   246  func MustHaveLink(t testing.TB) {
   247  	if !HasLink() {
   248  		t.Skipf("skipping test: hardlinks are not supported on %s/%s", runtime.GOOS, runtime.GOARCH)
   249  	}
   250  }
   251  
   252  var flaky = flag.Bool("flaky", false, "run known-flaky tests too")
   253  
   254  func SkipFlaky(t testing.TB, issue int) {
   255  	t.Helper()
   256  	if !*flaky {
   257  		t.Skipf("skipping known flaky test without the -flaky flag; see golang.org/issue/%d", issue)
   258  	}
   259  }
   260  
   261  func SkipFlakyNet(t testing.TB) {
   262  	t.Helper()
   263  	if v, _ := strconv.ParseBool(os.Getenv("GO_BUILDER_FLAKY_NET")); v {
   264  		t.Skip("skipping test on builder known to have frequent network failures")
   265  	}
   266  }
   267  
   268  // CleanCmdEnv will fill cmd.Env with the environment, excluding certain
   269  // variables that could modify the behavior of the Go tools such as
   270  // GODEBUG and GOTRACEBACK.
   271  func CleanCmdEnv(cmd *exec.Cmd) *exec.Cmd {
   272  	if cmd.Env != nil {
   273  		panic("environment already set")
   274  	}
   275  	for _, env := range os.Environ() {
   276  		// Exclude GODEBUG from the environment to prevent its output
   277  		// from breaking tests that are trying to parse other command output.
   278  		if strings.HasPrefix(env, "GODEBUG=") {
   279  			continue
   280  		}
   281  		// Exclude GOTRACEBACK for the same reason.
   282  		if strings.HasPrefix(env, "GOTRACEBACK=") {
   283  			continue
   284  		}
   285  		cmd.Env = append(cmd.Env, env)
   286  	}
   287  	return cmd
   288  }
   289  
   290  // CPUIsSlow reports whether the CPU running the test is suspected to be slow.
   291  func CPUIsSlow() bool {
   292  	switch runtime.GOARCH {
   293  	case "arm", "mips", "mipsle", "mips64", "mips64le":
   294  		return true
   295  	}
   296  	return false
   297  }
   298  
   299  // SkipIfShortAndSlow skips t if -short is set and the CPU running the test is
   300  // suspected to be slow.
   301  //
   302  // (This is useful for CPU-intensive tests that otherwise complete quickly.)
   303  func SkipIfShortAndSlow(t testing.TB) {
   304  	if testing.Short() && CPUIsSlow() {
   305  		t.Helper()
   306  		t.Skipf("skipping test in -short mode on %s", runtime.GOARCH)
   307  	}
   308  }
   309  

View as plain text