1 // Copyright 2019 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.
5 // Package testenv contains helper functions for skipping tests
6 // based on which tools are present in the environment.
19 // Testing is an abstraction of a *testing.T.
20 type Testing interface {
21 Skipf(format string, args ...interface{})
22 Fatalf(format string, args ...interface{})
25 type helperer interface {
29 // packageMainIsDevel reports whether the module containing package main
30 // is a development version (if module information is available).
32 // Builds in GOPATH mode and builds that lack module information are assumed to
33 // be development versions.
34 var packageMainIsDevel = func() bool { return true }
36 var checkGoGoroot struct {
41 func hasTool(tool string) error {
42 _, err := exec.LookPath(tool)
49 // check that the patch tools supports the -o argument
50 temp, err := ioutil.TempFile("", "patch-test")
55 defer os.Remove(temp.Name())
56 cmd := exec.Command(tool, "-o", temp.Name())
57 if err := cmd.Run(); err != nil {
62 checkGoGoroot.once.Do(func() {
63 // Ensure that the 'go' command found by exec.LookPath is from the correct
64 // GOROOT. Otherwise, 'some/path/go test ./...' will test against some
65 // version of the 'go' binary other than 'some/path/go', which is almost
66 // certainly not what the user intended.
67 out, err := exec.Command(tool, "env", "GOROOT").CombinedOutput()
69 checkGoGoroot.err = err
72 GOROOT := strings.TrimSpace(string(out))
73 if GOROOT != runtime.GOROOT() {
74 checkGoGoroot.err = fmt.Errorf("'go env GOROOT' does not match runtime.GOROOT:\n\tgo env: %s\n\tGOROOT: %s", GOROOT, runtime.GOROOT())
77 if checkGoGoroot.err != nil {
78 return checkGoGoroot.err
85 func allowMissingTool(tool string) bool {
86 if runtime.GOOS == "android" {
87 // Android builds generally run tests on a separate machine from the build,
88 // so don't expect any external tools to be available.
94 if os.Getenv("GO_BUILDER_NAME") == "illumos-amd64-joyent" {
95 // Work around a misconfigured builder (see https://golang.org/issue/33950).
99 if os.Getenv("GO_BUILDER_NAME") != "" {
103 if os.Getenv("GO_BUILDER_NAME") != "" {
108 // If a developer is actively working on this test, we expect them to have all
109 // of its dependencies installed. However, if it's just a dependency of some
110 // other module (for example, being run via 'go test all'), we should be more
111 // tolerant of unusual environments.
112 return !packageMainIsDevel()
115 // NeedsTool skips t if the named tool is not present in the path.
116 func NeedsTool(t Testing, tool string) {
117 if t, ok := t.(helperer); ok {
124 if allowMissingTool(tool) {
125 t.Skipf("skipping because %s tool not available: %v", tool, err)
127 t.Fatalf("%s tool not available: %v", tool, err)
131 // NeedsGoPackages skips t if the go/packages driver (or 'go' tool) implied by
132 // the current process environment is not present in the path.
133 func NeedsGoPackages(t Testing) {
134 if t, ok := t.(helperer); ok {
138 tool := os.Getenv("GOPACKAGESDRIVER")
141 // "off" forces go/packages to use the go command.
144 if _, err := exec.LookPath("gopackagesdriver"); err == nil {
145 tool = "gopackagesdriver"
154 // NeedsGoPackagesEnv skips t if the go/packages driver (or 'go' tool) implied
155 // by env is not present in the path.
156 func NeedsGoPackagesEnv(t Testing, env []string) {
157 if t, ok := t.(helperer); ok {
161 for _, v := range env {
162 if strings.HasPrefix(v, "GOPACKAGESDRIVER=") {
163 tool := strings.TrimPrefix(v, "GOPACKAGESDRIVER=")
176 // ExitIfSmallMachine emits a helpful diagnostic and calls os.Exit(0) if the
177 // current machine is a builder known to have scarce resources.
179 // It should be called from within a TestMain function.
180 func ExitIfSmallMachine() {
181 if os.Getenv("GO_BUILDER_NAME") == "linux-arm" {
182 fmt.Fprintln(os.Stderr, "skipping test: linux-arm builder lacks sufficient memory (https://golang.org/issue/32834)")