diff options
author | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2023-08-23 18:23:52 +0200 |
---|---|---|
committer | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2023-08-23 21:49:27 +0200 |
commit | 24b1be45c17d68c67bab61b2fbb568f53a3d8202 (patch) | |
tree | 4202ce89de8f523e051912e365bdf93b1a4422fd /tpl/internal/go_templates/testenv/testenv.go | |
parent | 111f02db2a2cb139a19e4643dd73fa637e40ad6e (diff) |
Go 1.21 Upgrade
Fixes #11351
Diffstat (limited to 'tpl/internal/go_templates/testenv/testenv.go')
-rw-r--r-- | tpl/internal/go_templates/testenv/testenv.go | 195 |
1 files changed, 146 insertions, 49 deletions
diff --git a/tpl/internal/go_templates/testenv/testenv.go b/tpl/internal/go_templates/testenv/testenv.go index 91de6e76c..2430ae6cf 100644 --- a/tpl/internal/go_templates/testenv/testenv.go +++ b/tpl/internal/go_templates/testenv/testenv.go @@ -11,9 +11,14 @@ package testenv import ( + "bytes" "errors" "flag" "fmt" + + "github.com/gohugoio/hugo/tpl/internal/go_templates/cfg" + + //"internal/platform" "os" "os/exec" "path/filepath" @@ -22,10 +27,14 @@ import ( "strings" "sync" "testing" - - "github.com/gohugoio/hugo/tpl/internal/go_templates/cfg" ) +// Save the original environment during init for use in checks. A test +// binary may modify its environment before calling HasExec to change its +// behavior (such as mimicking a command-line tool), and that modified +// environment might cause environment checks to behave erratically. +var origEnv = os.Environ() + // Builder reports the name of the builder running this test // (for example, "linux-amd64" or "windows-386-gce"). // If the test is not running on the build infrastructure, @@ -37,29 +46,26 @@ func Builder() string { // HasGoBuild reports whether the current system can build programs with “go build” // and then run them with os.StartProcess or exec.Command. func HasGoBuild() bool { - if os.Getenv("GO_GCFLAGS") != "" { - // It's too much work to require every caller of the go command - // to pass along "-gcflags="+os.Getenv("GO_GCFLAGS"). - // For now, if $GO_GCFLAGS is set, report that we simply can't - // run go build. - return false - } - switch runtime.GOOS { - case "android", "js", "ios": - return false - } - return true + // Modified by Hugo (not needed) + return false } +var ( + goBuildOnce sync.Once + goBuildErr error +) + // MustHaveGoBuild checks that the current system can build programs with “go build” // and then run them with os.StartProcess or exec.Command. // If not, MustHaveGoBuild calls t.Skip with an explanation. func MustHaveGoBuild(t testing.TB) { if os.Getenv("GO_GCFLAGS") != "" { + t.Helper() t.Skipf("skipping test: 'go build' not compatible with setting $GO_GCFLAGS") } if !HasGoBuild() { - t.Skipf("skipping test: 'go build' not available on %s/%s", runtime.GOOS, runtime.GOARCH) + t.Helper() + t.Skipf("skipping test: 'go build' unavailable: %v", goBuildErr) } } @@ -77,6 +83,25 @@ func MustHaveGoRun(t testing.TB) { } } +// HasParallelism reports whether the current system can execute multiple +// threads in parallel. +// There is a copy of this function in cmd/dist/test.go. +func HasParallelism() bool { + switch runtime.GOOS { + case "js", "wasip1": + return false + } + return true +} + +// MustHaveParallelism checks that the current system can execute multiple +// threads in parallel. If not, MustHaveParallelism calls t.Skip with an explanation. +func MustHaveParallelism(t testing.TB) { + if !HasParallelism() { + t.Skipf("skipping test: no parallelism available on %s/%s", runtime.GOOS, runtime.GOARCH) + } +} + // GoToolPath reports the path to the Go tool. // It is a convenience wrapper around GoTool. // If the tool is unavailable GoToolPath calls t.Skip. @@ -124,6 +149,9 @@ func findGOROOT() (string, error) { // runs the test in the directory containing the packaged under test.) That // means that if we start walking up the tree, we should eventually find // GOROOT/src/go.mod, and we can report the parent directory of that. + // + // Notably, this works even if we can't run 'go env GOROOT' as a + // subprocess. cwd, err := os.Getwd() if err != nil { @@ -174,7 +202,8 @@ func findGOROOT() (string, error) { // GOROOT reports the path to the directory containing the root of the Go // project source tree. This is normally equivalent to runtime.GOROOT, but -// works even if the test binary was built with -trimpath. +// works even if the test binary was built with -trimpath and cannot exec +// 'go env GOROOT'. // // If GOROOT cannot be found, GOROOT skips t if t is non-nil, // or panics otherwise. @@ -195,25 +224,18 @@ func GoTool() (string, error) { if !HasGoBuild() { return "", errors.New("platform cannot run go tool") } - var exeSuffix string - if runtime.GOOS == "windows" { - exeSuffix = ".exe" - } - goroot, err := findGOROOT() - if err != nil { - return "", fmt.Errorf("cannot find go tool: %w", err) - } - path := filepath.Join(goroot, "bin", "go"+exeSuffix) - if _, err := os.Stat(path); err == nil { - return path, nil - } - goBin, err := exec.LookPath("go" + exeSuffix) - if err != nil { - return "", errors.New("cannot find go tool: " + err.Error()) - } - return goBin, nil + goToolOnce.Do(func() { + goToolPath, goToolErr = exec.LookPath("go") + }) + return goToolPath, goToolErr } +var ( + goToolOnce sync.Once + goToolPath string + goToolErr error +) + // HasSrc reports whether the entire source tree is available under GOROOT. func HasSrc() bool { switch runtime.GOOS { @@ -226,50 +248,82 @@ func HasSrc() bool { // HasExternalNetwork reports whether the current system can use // external (non-localhost) networks. func HasExternalNetwork() bool { - return !testing.Short() && runtime.GOOS != "js" + return !testing.Short() && runtime.GOOS != "js" && runtime.GOOS != "wasip1" } // MustHaveExternalNetwork checks that the current system can use // external (non-localhost) networks. // If not, MustHaveExternalNetwork calls t.Skip with an explanation. func MustHaveExternalNetwork(t testing.TB) { - if runtime.GOOS == "js" { + if runtime.GOOS == "js" || runtime.GOOS == "wasip1" { + t.Helper() t.Skipf("skipping test: no external network on %s", runtime.GOOS) } if testing.Short() { + t.Helper() t.Skipf("skipping test: no external network in -short mode") } } -var haveCGO bool - // HasCGO reports whether the current system can use cgo. func HasCGO() bool { - return haveCGO + hasCgoOnce.Do(func() { + goTool, err := GoTool() + if err != nil { + return + } + cmd := exec.Command(goTool, "env", "CGO_ENABLED") + cmd.Env = origEnv + out, err := cmd.Output() + if err != nil { + panic(fmt.Sprintf("%v: %v", cmd, out)) + } + hasCgo, err = strconv.ParseBool(string(bytes.TrimSpace(out))) + if err != nil { + panic(fmt.Sprintf("%v: non-boolean output %q", cmd, out)) + } + }) + return hasCgo } +var ( + hasCgoOnce sync.Once + hasCgo bool +) + // MustHaveCGO calls t.Skip if cgo is not available. func MustHaveCGO(t testing.TB) { - if !haveCGO { + if !HasCGO() { t.Skipf("skipping test: no cgo") } } // CanInternalLink reports whether the current system can link programs with // internal linking. -func CanInternalLink() bool { - panic("not implemented, not needed by Hugo") +func CanInternalLink(withCgo bool) bool { + // Modified by Hugo (not needed) + return false } // MustInternalLink checks that the current system can link programs with internal // linking. // If not, MustInternalLink calls t.Skip with an explanation. -func MustInternalLink(t testing.TB) { - if !CanInternalLink() { +func MustInternalLink(t testing.TB, withCgo bool) { + if !CanInternalLink(withCgo) { + if withCgo && CanInternalLink(false) { + t.Skipf("skipping test: internal linking on %s/%s is not supported with cgo", runtime.GOOS, runtime.GOARCH) + } t.Skipf("skipping test: internal linking on %s/%s is not supported", runtime.GOOS, runtime.GOARCH) } } +// MustHaveBuildMode reports whether the current system can build programs in +// the given build mode. +// If not, MustHaveBuildMode calls t.Skip with an explanation. +func MustHaveBuildMode(t testing.TB, buildmode string) { + // Modified by Hugo (not needed) +} + // HasSymlink reports whether the current system can use os.Symlink. func HasSymlink() bool { ok, _ := hasSymlink() @@ -281,7 +335,7 @@ func HasSymlink() bool { func MustHaveSymlink(t testing.TB) { ok, reason := hasSymlink() if !ok { - t.Skipf("skipping test: cannot make symlinks on %s/%s%s", runtime.GOOS, runtime.GOARCH, reason) + t.Skipf("skipping test: cannot make symlinks on %s/%s: %s", runtime.GOOS, runtime.GOARCH, reason) } } @@ -320,7 +374,7 @@ func SkipFlakyNet(t testing.TB) { // CPUIsSlow reports whether the CPU running the test is suspected to be slow. func CPUIsSlow() bool { switch runtime.GOARCH { - case "arm", "mips", "mipsle", "mips64", "mips64le": + case "arm", "mips", "mipsle", "mips64", "mips64le", "wasm": return true } return false @@ -346,8 +400,51 @@ func SkipIfOptimizationOff(t testing.TB) { } // WriteImportcfg writes an importcfg file used by the compiler or linker to -// dstPath containing entries for the packages in std and cmd in addition -// to the package to package file mappings in additionalPackageFiles. -func WriteImportcfg(t testing.TB, dstPath string, additionalPackageFiles map[string]string) { - panic("not implemented, not needed by Hugo") +// dstPath containing entries for the file mappings in packageFiles, as well +// as for the packages transitively imported by the package(s) in pkgs. +// +// pkgs may include any package pattern that is valid to pass to 'go list', +// so it may also be a list of Go source files all in the same directory. +func WriteImportcfg(t testing.TB, dstPath string, packageFiles map[string]string, pkgs ...string) { + t.Helper() + + icfg := new(bytes.Buffer) + icfg.WriteString("# import config\n") + for k, v := range packageFiles { + fmt.Fprintf(icfg, "packagefile %s=%s\n", k, v) + } + + if len(pkgs) > 0 { + // Use 'go list' to resolve any missing packages and rewrite the import map. + cmd := Command(t, GoToolPath(t), "list", "-export", "-deps", "-f", `{{if ne .ImportPath "command-line-arguments"}}{{if .Export}}{{.ImportPath}}={{.Export}}{{end}}{{end}}`) + cmd.Args = append(cmd.Args, pkgs...) + cmd.Stderr = new(strings.Builder) + out, err := cmd.Output() + if err != nil { + t.Fatalf("%v: %v\n%s", cmd, err, cmd.Stderr) + } + + for _, line := range strings.Split(string(out), "\n") { + if line == "" { + continue + } + importPath, export, ok := strings.Cut(line, "=") + if !ok { + t.Fatalf("invalid line in output from %v:\n%s", cmd, line) + } + if packageFiles[importPath] == "" { + fmt.Fprintf(icfg, "packagefile %s=%s\n", importPath, export) + } + } + } + + if err := os.WriteFile(dstPath, icfg.Bytes(), 0666); err != nil { + t.Fatal(err) + } +} + +// SyscallIsNotSupported reports whether err may indicate that a system call is +// not supported by the current platform or execution environment. +func SyscallIsNotSupported(err error) bool { + return syscallIsNotSupported(err) } |