github.com/JimmyHuang454/JLS-go@v0.0.0-20230831150107-90d536585ba0/internal/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  	"fmt"
    17  	"github.com/JimmyHuang454/JLS-go/internal/cfg"
    18  	"github.com/JimmyHuang454/JLS-go/internal/goroot"
    19  	"github.com/JimmyHuang454/JLS-go/internal/platform"
    20  	"os"
    21  	"os/exec"
    22  	"path/filepath"
    23  	"runtime"
    24  	"strconv"
    25  	"strings"
    26  	"sync"
    27  	"testing"
    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  var (
   101  	gorootOnce sync.Once
   102  	gorootPath string
   103  	gorootErr  error
   104  )
   105  
   106  func findGOROOT() (string, error) {
   107  	gorootOnce.Do(func() {
   108  		gorootPath = runtime.GOROOT()
   109  		if gorootPath != "" {
   110  			// If runtime.GOROOT() is non-empty, assume that it is valid.
   111  			//
   112  			// (It might not be: for example, the user may have explicitly set GOROOT
   113  			// to the wrong directory, or explicitly set GOROOT_FINAL but not GOROOT
   114  			// and hasn't moved the tree to GOROOT_FINAL yet. But those cases are
   115  			// rare, and if that happens the user can fix what they broke.)
   116  			return
   117  		}
   118  
   119  		// runtime.GOROOT doesn't know where GOROOT is (perhaps because the test
   120  		// binary was built with -trimpath, or perhaps because GOROOT_FINAL was set
   121  		// without GOROOT and the tree hasn't been moved there yet).
   122  		//
   123  		// Since this is internal/testenv, we can cheat and assume that the caller
   124  		// is a test of some package in a subdirectory of GOROOT/src. ('go test'
   125  		// runs the test in the directory containing the packaged under test.) That
   126  		// means that if we start walking up the tree, we should eventually find
   127  		// GOROOT/src/go.mod, and we can report the parent directory of that.
   128  
   129  		cwd, err := os.Getwd()
   130  		if err != nil {
   131  			gorootErr = fmt.Errorf("finding GOROOT: %w", err)
   132  			return
   133  		}
   134  
   135  		dir := cwd
   136  		for {
   137  			parent := filepath.Dir(dir)
   138  			if parent == dir {
   139  				// dir is either "." or only a volume name.
   140  				gorootErr = fmt.Errorf("failed to locate GOROOT/src in any parent directory")
   141  				return
   142  			}
   143  
   144  			if base := filepath.Base(dir); base != "src" {
   145  				dir = parent
   146  				continue // dir cannot be GOROOT/src if it doesn't end in "src".
   147  			}
   148  
   149  			b, err := os.ReadFile(filepath.Join(dir, "go.mod"))
   150  			if err != nil {
   151  				if os.IsNotExist(err) {
   152  					dir = parent
   153  					continue
   154  				}
   155  				gorootErr = fmt.Errorf("finding GOROOT: %w", err)
   156  				return
   157  			}
   158  			goMod := string(b)
   159  
   160  			for goMod != "" {
   161  				var line string
   162  				line, goMod, _ = strings.Cut(goMod, "\n")
   163  				fields := strings.Fields(line)
   164  				if len(fields) >= 2 && fields[0] == "module" && fields[1] == "std" {
   165  					// Found "module std", which is the module declaration in GOROOT/src!
   166  					gorootPath = parent
   167  					return
   168  				}
   169  			}
   170  		}
   171  	})
   172  
   173  	return gorootPath, gorootErr
   174  }
   175  
   176  // GOROOT reports the path to the directory containing the root of the Go
   177  // project source tree. This is normally equivalent to runtime.GOROOT, but
   178  // works even if the test binary was built with -trimpath.
   179  //
   180  // If GOROOT cannot be found, GOROOT skips t if t is non-nil,
   181  // or panics otherwise.
   182  func GOROOT(t testing.TB) string {
   183  	path, err := findGOROOT()
   184  	if err != nil {
   185  		if t == nil {
   186  			panic(err)
   187  		}
   188  		t.Helper()
   189  		t.Skip(err)
   190  	}
   191  	return path
   192  }
   193  
   194  // GoTool reports the path to the Go tool.
   195  func GoTool() (string, error) {
   196  	if !HasGoBuild() {
   197  		return "", errors.New("platform cannot run go tool")
   198  	}
   199  	var exeSuffix string
   200  	if runtime.GOOS == "windows" {
   201  		exeSuffix = ".exe"
   202  	}
   203  	goroot, err := findGOROOT()
   204  	if err != nil {
   205  		return "", fmt.Errorf("cannot find go tool: %w", err)
   206  	}
   207  	path := filepath.Join(goroot, "bin", "go"+exeSuffix)
   208  	if _, err := os.Stat(path); err == nil {
   209  		return path, nil
   210  	}
   211  	goBin, err := exec.LookPath("go" + exeSuffix)
   212  	if err != nil {
   213  		return "", errors.New("cannot find go tool: " + err.Error())
   214  	}
   215  	return goBin, nil
   216  }
   217  
   218  // HasSrc reports whether the entire source tree is available under GOROOT.
   219  func HasSrc() bool {
   220  	switch runtime.GOOS {
   221  	case "ios":
   222  		return false
   223  	}
   224  	return true
   225  }
   226  
   227  // HasExternalNetwork reports whether the current system can use
   228  // external (non-localhost) networks.
   229  func HasExternalNetwork() bool {
   230  	return !testing.Short() && runtime.GOOS != "js"
   231  }
   232  
   233  // MustHaveExternalNetwork checks that the current system can use
   234  // external (non-localhost) networks.
   235  // If not, MustHaveExternalNetwork calls t.Skip with an explanation.
   236  func MustHaveExternalNetwork(t testing.TB) {
   237  	if runtime.GOOS == "js" {
   238  		t.Skipf("skipping test: no external network on %s", runtime.GOOS)
   239  	}
   240  	if testing.Short() {
   241  		t.Skipf("skipping test: no external network in -short mode")
   242  	}
   243  }
   244  
   245  var haveCGO bool
   246  
   247  // HasCGO reports whether the current system can use cgo.
   248  func HasCGO() bool {
   249  	return haveCGO
   250  }
   251  
   252  // MustHaveCGO calls t.Skip if cgo is not available.
   253  func MustHaveCGO(t testing.TB) {
   254  	if !haveCGO {
   255  		t.Skipf("skipping test: no cgo")
   256  	}
   257  }
   258  
   259  // CanInternalLink reports whether the current system can link programs with
   260  // internal linking.
   261  func CanInternalLink() bool {
   262  	return !platform.MustLinkExternal(runtime.GOOS, runtime.GOARCH)
   263  }
   264  
   265  // MustInternalLink checks that the current system can link programs with internal
   266  // linking.
   267  // If not, MustInternalLink calls t.Skip with an explanation.
   268  func MustInternalLink(t testing.TB) {
   269  	if !CanInternalLink() {
   270  		t.Skipf("skipping test: internal linking on %s/%s is not supported", runtime.GOOS, runtime.GOARCH)
   271  	}
   272  }
   273  
   274  // HasSymlink reports whether the current system can use os.Symlink.
   275  func HasSymlink() bool {
   276  	ok, _ := hasSymlink()
   277  	return ok
   278  }
   279  
   280  // MustHaveSymlink reports whether the current system can use os.Symlink.
   281  // If not, MustHaveSymlink calls t.Skip with an explanation.
   282  func MustHaveSymlink(t testing.TB) {
   283  	ok, reason := hasSymlink()
   284  	if !ok {
   285  		t.Skipf("skipping test: cannot make symlinks on %s/%s%s", runtime.GOOS, runtime.GOARCH, reason)
   286  	}
   287  }
   288  
   289  // HasLink reports whether the current system can use os.Link.
   290  func HasLink() bool {
   291  	// From Android release M (Marshmallow), hard linking files is blocked
   292  	// and an attempt to call link() on a file will return EACCES.
   293  	// - https://code.google.com/p/android-developer-preview/issues/detail?id=3150
   294  	return runtime.GOOS != "plan9" && runtime.GOOS != "android"
   295  }
   296  
   297  // MustHaveLink reports whether the current system can use os.Link.
   298  // If not, MustHaveLink calls t.Skip with an explanation.
   299  func MustHaveLink(t testing.TB) {
   300  	if !HasLink() {
   301  		t.Skipf("skipping test: hardlinks are not supported on %s/%s", runtime.GOOS, runtime.GOARCH)
   302  	}
   303  }
   304  
   305  var flaky = flag.Bool("flaky", false, "run known-flaky tests too")
   306  
   307  func SkipFlaky(t testing.TB, issue int) {
   308  	t.Helper()
   309  	if !*flaky {
   310  		t.Skipf("skipping known flaky test without the -flaky flag; see golang.org/issue/%d", issue)
   311  	}
   312  }
   313  
   314  func SkipFlakyNet(t testing.TB) {
   315  	t.Helper()
   316  	if v, _ := strconv.ParseBool(os.Getenv("GO_BUILDER_FLAKY_NET")); v {
   317  		t.Skip("skipping test on builder known to have frequent network failures")
   318  	}
   319  }
   320  
   321  // CPUIsSlow reports whether the CPU running the test is suspected to be slow.
   322  func CPUIsSlow() bool {
   323  	switch runtime.GOARCH {
   324  	case "arm", "mips", "mipsle", "mips64", "mips64le":
   325  		return true
   326  	}
   327  	return false
   328  }
   329  
   330  // SkipIfShortAndSlow skips t if -short is set and the CPU running the test is
   331  // suspected to be slow.
   332  //
   333  // (This is useful for CPU-intensive tests that otherwise complete quickly.)
   334  func SkipIfShortAndSlow(t testing.TB) {
   335  	if testing.Short() && CPUIsSlow() {
   336  		t.Helper()
   337  		t.Skipf("skipping test in -short mode on %s", runtime.GOARCH)
   338  	}
   339  }
   340  
   341  // SkipIfOptimizationOff skips t if optimization is disabled.
   342  func SkipIfOptimizationOff(t testing.TB) {
   343  	if OptimizationOff() {
   344  		t.Helper()
   345  		t.Skip("skipping test with optimization disabled")
   346  	}
   347  }
   348  
   349  // WriteImportcfg writes an importcfg file used by the compiler or linker to
   350  // dstPath containing entries for the packages in std and cmd in addition
   351  // to the package to package file mappings in additionalPackageFiles.
   352  func WriteImportcfg(t testing.TB, dstPath string, additionalPackageFiles map[string]string) {
   353  	importcfg, err := goroot.Importcfg()
   354  	for k, v := range additionalPackageFiles {
   355  		importcfg += fmt.Sprintf("\npackagefile %s=%s", k, v)
   356  	}
   357  	if err != nil {
   358  		t.Fatalf("preparing the importcfg failed: %s", err)
   359  	}
   360  	err = os.WriteFile(dstPath, []byte(importcfg), 0655)
   361  	if err != nil {
   362  		t.Fatalf("writing the importcfg failed: %s", err)
   363  	}
   364  }