summaryrefslogtreecommitdiffstats
path: root/src/cmd/go/scriptconds_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/go/scriptconds_test.go')
-rw-r--r--src/cmd/go/scriptconds_test.go235
1 files changed, 235 insertions, 0 deletions
diff --git a/src/cmd/go/scriptconds_test.go b/src/cmd/go/scriptconds_test.go
new file mode 100644
index 0000000..8dd9b0d
--- /dev/null
+++ b/src/cmd/go/scriptconds_test.go
@@ -0,0 +1,235 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main_test
+
+import (
+ "cmd/go/internal/cfg"
+ "cmd/go/internal/script"
+ "cmd/go/internal/script/scripttest"
+ "errors"
+ "fmt"
+ "internal/buildcfg"
+ "internal/platform"
+ "internal/testenv"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "runtime/debug"
+ "strings"
+ "sync"
+)
+
+func scriptConditions() map[string]script.Cond {
+ conds := scripttest.DefaultConds()
+
+ add := func(name string, cond script.Cond) {
+ if _, ok := conds[name]; ok {
+ panic(fmt.Sprintf("condition %q is already registered", name))
+ }
+ conds[name] = cond
+ }
+
+ lazyBool := func(summary string, f func() bool) script.Cond {
+ return script.OnceCondition(summary, func() (bool, error) { return f(), nil })
+ }
+
+ add("abscc", script.Condition("default $CC path is absolute and exists", defaultCCIsAbsolute))
+ add("asan", sysCondition("-asan", platform.ASanSupported, true))
+ add("buildmode", script.PrefixCondition("go supports -buildmode=<suffix>", hasBuildmode))
+ add("case-sensitive", script.OnceCondition("$WORK filesystem is case-sensitive", isCaseSensitive))
+ add("cc", script.PrefixCondition("go env CC = <suffix> (ignoring the go/env file)", ccIs))
+ add("cgo", script.BoolCondition("host CGO_ENABLED", testenv.HasCGO()))
+ add("cgolinkext", script.Condition("platform requires external linking for cgo", cgoLinkExt))
+ add("cross", script.BoolCondition("cmd/go GOOS/GOARCH != GOHOSTOS/GOHOSTARCH", goHostOS != runtime.GOOS || goHostArch != runtime.GOARCH))
+ add("fuzz", sysCondition("-fuzz", platform.FuzzSupported, false))
+ add("fuzz-instrumented", sysCondition("-fuzz with instrumentation", platform.FuzzInstrumented, false))
+ add("git", lazyBool("the 'git' executable exists and provides the standard CLI", hasWorkingGit))
+ add("GODEBUG", script.PrefixCondition("GODEBUG contains <suffix>", hasGodebug))
+ add("GOEXPERIMENT", script.PrefixCondition("GOEXPERIMENT <suffix> is enabled", hasGoexperiment))
+ add("go-builder", script.BoolCondition("GO_BUILDER_NAME is non-empty", testenv.Builder() != ""))
+ add("link", lazyBool("testenv.HasLink()", testenv.HasLink))
+ add("mismatched-goroot", script.Condition("test's GOROOT_FINAL does not match the real GOROOT", isMismatchedGoroot))
+ add("msan", sysCondition("-msan", platform.MSanSupported, true))
+ add("mustlinkext", script.Condition("platform always requires external linking", mustLinkExt))
+ add("net", script.PrefixCondition("can connect to external network host <suffix>", hasNet))
+ add("race", sysCondition("-race", platform.RaceDetectorSupported, true))
+ add("symlink", lazyBool("testenv.HasSymlink()", testenv.HasSymlink))
+ add("trimpath", script.OnceCondition("test binary was built with -trimpath", isTrimpath))
+
+ return conds
+}
+
+func defaultCCIsAbsolute(s *script.State) (bool, error) {
+ GOOS, _ := s.LookupEnv("GOOS")
+ GOARCH, _ := s.LookupEnv("GOARCH")
+ defaultCC := cfg.DefaultCC(GOOS, GOARCH)
+ if filepath.IsAbs(defaultCC) {
+ if _, err := exec.LookPath(defaultCC); err == nil {
+ return true, nil
+ }
+ }
+ return false, nil
+}
+
+func ccIs(s *script.State, want string) (bool, error) {
+ CC, _ := s.LookupEnv("CC")
+ if CC != "" {
+ return CC == want, nil
+ }
+ GOOS, _ := s.LookupEnv("GOOS")
+ GOARCH, _ := s.LookupEnv("GOARCH")
+ return cfg.DefaultCC(GOOS, GOARCH) == want, nil
+}
+
+func isMismatchedGoroot(s *script.State) (bool, error) {
+ gorootFinal, _ := s.LookupEnv("GOROOT_FINAL")
+ if gorootFinal == "" {
+ gorootFinal, _ = s.LookupEnv("GOROOT")
+ }
+ return gorootFinal != testGOROOT, nil
+}
+
+func sysCondition(flag string, f func(goos, goarch string) bool, needsCgo bool) script.Cond {
+ return script.Condition(
+ "GOOS/GOARCH supports "+flag,
+ func(s *script.State) (bool, error) {
+ GOOS, _ := s.LookupEnv("GOOS")
+ GOARCH, _ := s.LookupEnv("GOARCH")
+ cross := goHostOS != GOOS || goHostArch != GOARCH
+ return (!needsCgo || (testenv.HasCGO() && !cross)) && f(GOOS, GOARCH), nil
+ })
+}
+
+func hasBuildmode(s *script.State, mode string) (bool, error) {
+ GOOS, _ := s.LookupEnv("GOOS")
+ GOARCH, _ := s.LookupEnv("GOARCH")
+ return platform.BuildModeSupported(runtime.Compiler, mode, GOOS, GOARCH), nil
+}
+
+var scriptNetEnabled sync.Map // testing.TB → already enabled
+
+func hasNet(s *script.State, host string) (bool, error) {
+ if !testenv.HasExternalNetwork() {
+ return false, nil
+ }
+
+ // TODO(bcmills): Add a flag or environment variable to allow skipping tests
+ // for specific hosts and/or skipping all net tests except for specific hosts.
+
+ t, ok := tbFromContext(s.Context())
+ if !ok {
+ return false, errors.New("script Context unexpectedly missing testing.TB key")
+ }
+
+ if netTestSem != nil {
+ // When the number of external network connections is limited, we limit the
+ // number of net tests that can run concurrently so that the overall number
+ // of network connections won't exceed the limit.
+ _, dup := scriptNetEnabled.LoadOrStore(t, true)
+ if !dup {
+ // Acquire a net token for this test until the test completes.
+ netTestSem <- struct{}{}
+ t.Cleanup(func() {
+ <-netTestSem
+ scriptNetEnabled.Delete(t)
+ })
+ }
+ }
+
+ // Since we have confirmed that the network is available,
+ // allow cmd/go to use it.
+ s.Setenv("TESTGONETWORK", "")
+ return true, nil
+}
+
+func hasGodebug(s *script.State, value string) (bool, error) {
+ godebug, _ := s.LookupEnv("GODEBUG")
+ for _, p := range strings.Split(godebug, ",") {
+ if strings.TrimSpace(p) == value {
+ return true, nil
+ }
+ }
+ return false, nil
+}
+
+func hasGoexperiment(s *script.State, value string) (bool, error) {
+ GOOS, _ := s.LookupEnv("GOOS")
+ GOARCH, _ := s.LookupEnv("GOARCH")
+ goexp, _ := s.LookupEnv("GOEXPERIMENT")
+ flags, err := buildcfg.ParseGOEXPERIMENT(GOOS, GOARCH, goexp)
+ if err != nil {
+ return false, err
+ }
+ for _, exp := range flags.All() {
+ if value == exp {
+ return true, nil
+ }
+ if strings.TrimPrefix(value, "no") == strings.TrimPrefix(exp, "no") {
+ return false, nil
+ }
+ }
+ return false, fmt.Errorf("unrecognized GOEXPERIMENT %q", value)
+}
+
+func isCaseSensitive() (bool, error) {
+ tmpdir, err := os.MkdirTemp(testTmpDir, "case-sensitive")
+ if err != nil {
+ return false, fmt.Errorf("failed to create directory to determine case-sensitivity: %w", err)
+ }
+ defer os.RemoveAll(tmpdir)
+
+ fcap := filepath.Join(tmpdir, "FILE")
+ if err := os.WriteFile(fcap, []byte{}, 0644); err != nil {
+ return false, fmt.Errorf("error writing file to determine case-sensitivity: %w", err)
+ }
+
+ flow := filepath.Join(tmpdir, "file")
+ _, err = os.ReadFile(flow)
+ switch {
+ case err == nil:
+ return false, nil
+ case os.IsNotExist(err):
+ return true, nil
+ default:
+ return false, fmt.Errorf("unexpected error reading file when determining case-sensitivity: %w", err)
+ }
+}
+
+func isTrimpath() (bool, error) {
+ info, _ := debug.ReadBuildInfo()
+ if info == nil {
+ return false, errors.New("missing build info")
+ }
+
+ for _, s := range info.Settings {
+ if s.Key == "-trimpath" && s.Value == "true" {
+ return true, nil
+ }
+ }
+ return false, nil
+}
+
+func hasWorkingGit() bool {
+ if runtime.GOOS == "plan9" {
+ // The Git command is usually not the real Git on Plan 9.
+ // See https://golang.org/issues/29640.
+ return false
+ }
+ _, err := exec.LookPath("git")
+ return err == nil
+}
+
+func cgoLinkExt(s *script.State) (bool, error) {
+ GOOS, _ := s.LookupEnv("GOOS")
+ GOARCH, _ := s.LookupEnv("GOARCH")
+ return platform.MustLinkExternal(GOOS, GOARCH, true), nil
+}
+
+func mustLinkExt(s *script.State) (bool, error) {
+ GOOS, _ := s.LookupEnv("GOOS")
+ GOARCH, _ := s.LookupEnv("GOARCH")
+ return platform.MustLinkExternal(GOOS, GOARCH, false), nil
+}