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