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