1
2
3
4
5
6
7
8
9
10
11 package testenv
12
13 import (
14 "bytes"
15 "errors"
16 "flag"
17 "fmt"
18 "internal/cfg"
19 "internal/goarch"
20 "internal/platform"
21 "os"
22 "os/exec"
23 "path/filepath"
24 "runtime"
25 "strconv"
26 "strings"
27 "sync"
28 "testing"
29 )
30
31
32
33
34
35 var origEnv = os.Environ()
36
37
38
39
40
41 func Builder() string {
42 return os.Getenv("GO_BUILDER_NAME")
43 }
44
45
46
47 func HasGoBuild() bool {
48 if os.Getenv("GO_GCFLAGS") != "" {
49
50
51
52
53 return false
54 }
55
56 goBuildOnce.Do(func() {
57
58
59
60
61
62 cmd := exec.Command("go", "tool", "-n", "compile")
63 cmd.Env = origEnv
64 out, err := cmd.Output()
65 if err != nil {
66 goBuildErr = fmt.Errorf("%v: %w", cmd, err)
67 return
68 }
69 out = bytes.TrimSpace(out)
70 if len(out) == 0 {
71 goBuildErr = fmt.Errorf("%v: no tool reported", cmd)
72 return
73 }
74 if _, err := exec.LookPath(string(out)); err != nil {
75 goBuildErr = err
76 return
77 }
78
79 if platform.MustLinkExternal(runtime.GOOS, runtime.GOARCH, false) {
80
81
82
83
84
85
86
87
88 if os.Getenv("CC") == "" {
89 cmd := exec.Command("go", "env", "CC")
90 cmd.Env = origEnv
91 out, err := cmd.Output()
92 if err != nil {
93 goBuildErr = fmt.Errorf("%v: %w", cmd, err)
94 return
95 }
96 out = bytes.TrimSpace(out)
97 if len(out) == 0 {
98 goBuildErr = fmt.Errorf("%v: no CC reported", cmd)
99 return
100 }
101 _, goBuildErr = exec.LookPath(string(out))
102 }
103 }
104 })
105
106 return goBuildErr == nil
107 }
108
109 var (
110 goBuildOnce sync.Once
111 goBuildErr error
112 )
113
114
115
116
117 func MustHaveGoBuild(t testing.TB) {
118 if os.Getenv("GO_GCFLAGS") != "" {
119 t.Helper()
120 t.Skipf("skipping test: 'go build' not compatible with setting $GO_GCFLAGS")
121 }
122 if !HasGoBuild() {
123 t.Helper()
124 t.Skipf("skipping test: 'go build' unavailable: %v", goBuildErr)
125 }
126 }
127
128
129 func HasGoRun() bool {
130
131 return HasGoBuild()
132 }
133
134
135
136 func MustHaveGoRun(t testing.TB) {
137 if !HasGoRun() {
138 t.Skipf("skipping test: 'go run' not available on %s/%s", runtime.GOOS, runtime.GOARCH)
139 }
140 }
141
142
143
144
145 func HasParallelism() bool {
146 switch runtime.GOOS {
147 case "js", "wasip1":
148 return false
149 }
150 return true
151 }
152
153
154
155 func MustHaveParallelism(t testing.TB) {
156 if !HasParallelism() {
157 t.Skipf("skipping test: no parallelism available on %s/%s", runtime.GOOS, runtime.GOARCH)
158 }
159 }
160
161
162
163
164
165 func GoToolPath(t testing.TB) string {
166 MustHaveGoBuild(t)
167 path, err := GoTool()
168 if err != nil {
169 t.Fatal(err)
170 }
171
172
173
174 for _, envVar := range strings.Fields(cfg.KnownEnv) {
175 os.Getenv(envVar)
176 }
177 return path
178 }
179
180 var (
181 gorootOnce sync.Once
182 gorootPath string
183 gorootErr error
184 )
185
186 func findGOROOT() (string, error) {
187 gorootOnce.Do(func() {
188 gorootPath = runtime.GOROOT()
189 if gorootPath != "" {
190
191
192
193
194
195 return
196 }
197
198
199
200
201
202
203
204
205
206
207
208
209
210 cwd, err := os.Getwd()
211 if err != nil {
212 gorootErr = fmt.Errorf("finding GOROOT: %w", err)
213 return
214 }
215
216 dir := cwd
217 for {
218 parent := filepath.Dir(dir)
219 if parent == dir {
220
221 gorootErr = fmt.Errorf("failed to locate GOROOT/src in any parent directory")
222 return
223 }
224
225 if base := filepath.Base(dir); base != "src" {
226 dir = parent
227 continue
228 }
229
230 b, err := os.ReadFile(filepath.Join(dir, "go.mod"))
231 if err != nil {
232 if os.IsNotExist(err) {
233 dir = parent
234 continue
235 }
236 gorootErr = fmt.Errorf("finding GOROOT: %w", err)
237 return
238 }
239 goMod := string(b)
240
241 for goMod != "" {
242 var line string
243 line, goMod, _ = strings.Cut(goMod, "\n")
244 fields := strings.Fields(line)
245 if len(fields) >= 2 && fields[0] == "module" && fields[1] == "std" {
246
247 gorootPath = parent
248 return
249 }
250 }
251 }
252 })
253
254 return gorootPath, gorootErr
255 }
256
257
258
259
260
261
262
263
264 func GOROOT(t testing.TB) string {
265 path, err := findGOROOT()
266 if err != nil {
267 if t == nil {
268 panic(err)
269 }
270 t.Helper()
271 t.Skip(err)
272 }
273 return path
274 }
275
276
277 func GoTool() (string, error) {
278 if !HasGoBuild() {
279 return "", errors.New("platform cannot run go tool")
280 }
281 goToolOnce.Do(func() {
282 goToolPath, goToolErr = exec.LookPath("go")
283 })
284 return goToolPath, goToolErr
285 }
286
287 var (
288 goToolOnce sync.Once
289 goToolPath string
290 goToolErr error
291 )
292
293
294 func HasSrc() bool {
295 switch runtime.GOOS {
296 case "ios":
297 return false
298 }
299 return true
300 }
301
302
303
304 func HasExternalNetwork() bool {
305 return !testing.Short() && runtime.GOOS != "js" && runtime.GOOS != "wasip1"
306 }
307
308
309
310
311 func MustHaveExternalNetwork(t testing.TB) {
312 if runtime.GOOS == "js" || runtime.GOOS == "wasip1" {
313 t.Helper()
314 t.Skipf("skipping test: no external network on %s", runtime.GOOS)
315 }
316 if testing.Short() {
317 t.Helper()
318 t.Skipf("skipping test: no external network in -short mode")
319 }
320 }
321
322
323 func HasCGO() bool {
324 hasCgoOnce.Do(func() {
325 goTool, err := GoTool()
326 if err != nil {
327 return
328 }
329 cmd := exec.Command(goTool, "env", "CGO_ENABLED")
330 cmd.Env = origEnv
331 out, err := cmd.Output()
332 if err != nil {
333 panic(fmt.Sprintf("%v: %v", cmd, out))
334 }
335 hasCgo, err = strconv.ParseBool(string(bytes.TrimSpace(out)))
336 if err != nil {
337 panic(fmt.Sprintf("%v: non-boolean output %q", cmd, out))
338 }
339 })
340 return hasCgo
341 }
342
343 var (
344 hasCgoOnce sync.Once
345 hasCgo bool
346 )
347
348
349 func MustHaveCGO(t testing.TB) {
350 if !HasCGO() {
351 t.Skipf("skipping test: no cgo")
352 }
353 }
354
355
356
357 func CanInternalLink(withCgo bool) bool {
358 return !platform.MustLinkExternal(runtime.GOOS, runtime.GOARCH, withCgo)
359 }
360
361
362
363
364 func MustInternalLink(t testing.TB, withCgo bool) {
365 if !CanInternalLink(withCgo) {
366 if withCgo && CanInternalLink(false) {
367 t.Skipf("skipping test: internal linking on %s/%s is not supported with cgo", runtime.GOOS, runtime.GOARCH)
368 }
369 t.Skipf("skipping test: internal linking on %s/%s is not supported", runtime.GOOS, runtime.GOARCH)
370 }
371 }
372
373
374
375
376 func MustInternalLinkPIE(t testing.TB) {
377 if !platform.InternalLinkPIESupported(runtime.GOOS, runtime.GOARCH) {
378 t.Skipf("skipping test: internal linking for buildmode=pie on %s/%s is not supported", runtime.GOOS, runtime.GOARCH)
379 }
380 }
381
382
383
384
385 func MustHaveBuildMode(t testing.TB, buildmode string) {
386 if !platform.BuildModeSupported(runtime.Compiler, buildmode, runtime.GOOS, runtime.GOARCH) {
387 t.Skipf("skipping test: build mode %s on %s/%s is not supported by the %s compiler", buildmode, runtime.GOOS, runtime.GOARCH, runtime.Compiler)
388 }
389 }
390
391
392 func HasSymlink() bool {
393 ok, _ := hasSymlink()
394 return ok
395 }
396
397
398
399 func MustHaveSymlink(t testing.TB) {
400 ok, reason := hasSymlink()
401 if !ok {
402 t.Skipf("skipping test: cannot make symlinks on %s/%s: %s", runtime.GOOS, runtime.GOARCH, reason)
403 }
404 }
405
406
407 func HasLink() bool {
408
409
410
411 return runtime.GOOS != "plan9" && runtime.GOOS != "android"
412 }
413
414
415
416 func MustHaveLink(t testing.TB) {
417 if !HasLink() {
418 t.Skipf("skipping test: hardlinks are not supported on %s/%s", runtime.GOOS, runtime.GOARCH)
419 }
420 }
421
422 var flaky = flag.Bool("flaky", false, "run known-flaky tests too")
423
424 func SkipFlaky(t testing.TB, issue int) {
425 t.Helper()
426 if !*flaky {
427 t.Skipf("skipping known flaky test without the -flaky flag; see golang.org/issue/%d", issue)
428 }
429 }
430
431 func SkipFlakyNet(t testing.TB) {
432 t.Helper()
433 if v, _ := strconv.ParseBool(os.Getenv("GO_BUILDER_FLAKY_NET")); v {
434 t.Skip("skipping test on builder known to have frequent network failures")
435 }
436 }
437
438
439 func CPUIsSlow() bool {
440 switch runtime.GOARCH {
441 case "arm", "mips", "mipsle", "mips64", "mips64le", "wasm":
442 return true
443 }
444 return false
445 }
446
447
448
449
450
451 func SkipIfShortAndSlow(t testing.TB) {
452 if testing.Short() && CPUIsSlow() {
453 t.Helper()
454 t.Skipf("skipping test in -short mode on %s", runtime.GOARCH)
455 }
456 }
457
458
459 func SkipIfOptimizationOff(t testing.TB) {
460 if OptimizationOff() {
461 t.Helper()
462 t.Skip("skipping test with optimization disabled")
463 }
464 }
465
466
467
468
469
470
471
472 func WriteImportcfg(t testing.TB, dstPath string, packageFiles map[string]string, pkgs ...string) {
473 t.Helper()
474
475 icfg := new(bytes.Buffer)
476 icfg.WriteString("# import config\n")
477 for k, v := range packageFiles {
478 fmt.Fprintf(icfg, "packagefile %s=%s\n", k, v)
479 }
480
481 if len(pkgs) > 0 {
482
483 cmd := Command(t, GoToolPath(t), "list", "-export", "-deps", "-f", `{{if ne .ImportPath "command-line-arguments"}}{{if .Export}}{{.ImportPath}}={{.Export}}{{end}}{{end}}`)
484 cmd.Args = append(cmd.Args, pkgs...)
485 cmd.Stderr = new(strings.Builder)
486 out, err := cmd.Output()
487 if err != nil {
488 t.Fatalf("%v: %v\n%s", cmd, err, cmd.Stderr)
489 }
490
491 for _, line := range strings.Split(string(out), "\n") {
492 if line == "" {
493 continue
494 }
495 importPath, export, ok := strings.Cut(line, "=")
496 if !ok {
497 t.Fatalf("invalid line in output from %v:\n%s", cmd, line)
498 }
499 if packageFiles[importPath] == "" {
500 fmt.Fprintf(icfg, "packagefile %s=%s\n", importPath, export)
501 }
502 }
503 }
504
505 if err := os.WriteFile(dstPath, icfg.Bytes(), 0666); err != nil {
506 t.Fatal(err)
507 }
508 }
509
510
511
512 func SyscallIsNotSupported(err error) bool {
513 return syscallIsNotSupported(err)
514 }
515
516
517
518
519 func ParallelOn64Bit(t *testing.T) {
520 if goarch.PtrSize == 4 {
521 return
522 }
523 t.Parallel()
524 }
525
View as plain text