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