Source file
src/cmd/dist/test.go
1
2
3
4
5 package main
6
7 import (
8 "bytes"
9 "encoding/json"
10 "flag"
11 "fmt"
12 "io"
13 "io/fs"
14 "log"
15 "os"
16 "os/exec"
17 "path/filepath"
18 "reflect"
19 "regexp"
20 "runtime"
21 "strconv"
22 "strings"
23 "time"
24 )
25
26 func cmdtest() {
27 gogcflags = os.Getenv("GO_GCFLAGS")
28 setNoOpt()
29
30 var t tester
31
32 var noRebuild bool
33 flag.BoolVar(&t.listMode, "list", false, "list available tests")
34 flag.BoolVar(&t.rebuild, "rebuild", false, "rebuild everything first")
35 flag.BoolVar(&noRebuild, "no-rebuild", false, "overrides -rebuild (historical dreg)")
36 flag.BoolVar(&t.keepGoing, "k", false, "keep going even when error occurred")
37 flag.BoolVar(&t.race, "race", false, "run in race builder mode (different set of tests)")
38 flag.BoolVar(&t.compileOnly, "compile-only", false, "compile tests, but don't run them")
39 flag.StringVar(&t.banner, "banner", "##### ", "banner prefix; blank means no section banners")
40 flag.StringVar(&t.runRxStr, "run", "",
41 "run only those tests matching the regular expression; empty means to run all. "+
42 "Special exception: if the string begins with '!', the match is inverted.")
43 flag.BoolVar(&t.msan, "msan", false, "run in memory sanitizer builder mode")
44 flag.BoolVar(&t.asan, "asan", false, "run in address sanitizer builder mode")
45 flag.BoolVar(&t.json, "json", false, "report test results in JSON")
46
47 xflagparse(-1)
48 if noRebuild {
49 t.rebuild = false
50 }
51
52 t.run()
53 }
54
55
56 type tester struct {
57 race bool
58 msan bool
59 asan bool
60 listMode bool
61 rebuild bool
62 failed bool
63 keepGoing bool
64 compileOnly bool
65 runRxStr string
66 runRx *regexp.Regexp
67 runRxWant bool
68 runNames []string
69 banner string
70 lastHeading string
71
72 short bool
73 cgoEnabled bool
74 json bool
75
76 tests []distTest
77 testNames map[string]bool
78 timeoutScale int
79
80 worklist []*work
81 }
82
83
84 type work struct {
85 dt *distTest
86 cmd *exec.Cmd
87 flush func()
88 start chan bool
89 out bytes.Buffer
90 err error
91 end chan struct{}
92 }
93
94
95 func (w *work) printSkip(t *tester, msg string) {
96 if t.json {
97 synthesizeSkipEvent(json.NewEncoder(&w.out), w.dt.name, msg)
98 return
99 }
100 fmt.Fprintln(&w.out, msg)
101 }
102
103
104
105 type distTest struct {
106 name string
107 heading string
108 fn func(*distTest) error
109 }
110
111 func (t *tester) run() {
112 timelog("start", "dist test")
113
114 os.Setenv("PATH", fmt.Sprintf("%s%c%s", gorootBin, os.PathListSeparator, os.Getenv("PATH")))
115
116 t.short = true
117 if v := os.Getenv("GO_TEST_SHORT"); v != "" {
118 short, err := strconv.ParseBool(v)
119 if err != nil {
120 fatalf("invalid GO_TEST_SHORT %q: %v", v, err)
121 }
122 t.short = short
123 }
124
125 cmd := exec.Command(gorootBinGo, "env", "CGO_ENABLED")
126 cmd.Stderr = new(bytes.Buffer)
127 slurp, err := cmd.Output()
128 if err != nil {
129 fatalf("Error running %s: %v\n%s", cmd, err, cmd.Stderr)
130 }
131 parts := strings.Split(string(slurp), "\n")
132 if nlines := len(parts) - 1; nlines < 1 {
133 fatalf("Error running %s: output contains <1 lines\n%s", cmd, cmd.Stderr)
134 }
135 t.cgoEnabled, _ = strconv.ParseBool(parts[0])
136
137 if flag.NArg() > 0 && t.runRxStr != "" {
138 fatalf("the -run regular expression flag is mutually exclusive with test name arguments")
139 }
140
141 t.runNames = flag.Args()
142
143
144
145
146
147
148 if ok := isEnvSet("GOTRACEBACK"); !ok {
149 if err := os.Setenv("GOTRACEBACK", "system"); err != nil {
150 if t.keepGoing {
151 log.Printf("Failed to set GOTRACEBACK: %v", err)
152 } else {
153 fatalf("Failed to set GOTRACEBACK: %v", err)
154 }
155 }
156 }
157
158 if t.rebuild {
159 t.out("Building packages and commands.")
160
161 goInstall(toolenv(), gorootBinGo, append([]string{"-a"}, toolchain...)...)
162 }
163
164 if !t.listMode {
165 if builder := os.Getenv("GO_BUILDER_NAME"); builder == "" {
166
167
168
169
170
171
172
173
174
175
176
177
178 goInstall(toolenv(), gorootBinGo, toolchain...)
179 goInstall(toolenv(), gorootBinGo, toolchain...)
180 goInstall(toolenv(), gorootBinGo, "cmd")
181 }
182 }
183
184 t.timeoutScale = 1
185 if s := os.Getenv("GO_TEST_TIMEOUT_SCALE"); s != "" {
186 t.timeoutScale, err = strconv.Atoi(s)
187 if err != nil {
188 fatalf("failed to parse $GO_TEST_TIMEOUT_SCALE = %q as integer: %v", s, err)
189 }
190 }
191
192 if t.runRxStr != "" {
193 if t.runRxStr[0] == '!' {
194 t.runRxWant = false
195 t.runRxStr = t.runRxStr[1:]
196 } else {
197 t.runRxWant = true
198 }
199 t.runRx = regexp.MustCompile(t.runRxStr)
200 }
201
202 t.registerTests()
203 if t.listMode {
204 for _, tt := range t.tests {
205 fmt.Println(tt.name)
206 }
207 return
208 }
209
210 for _, name := range t.runNames {
211 if !t.testNames[name] {
212 fatalf("unknown test %q", name)
213 }
214 }
215
216
217 if strings.HasPrefix(os.Getenv("GO_BUILDER_NAME"), "linux-") {
218 if os.Getuid() == 0 {
219
220
221 } else {
222 xatexit(t.makeGOROOTUnwritable())
223 }
224 }
225
226 if !t.json {
227 if err := t.maybeLogMetadata(); err != nil {
228 t.failed = true
229 if t.keepGoing {
230 log.Printf("Failed logging metadata: %v", err)
231 } else {
232 fatalf("Failed logging metadata: %v", err)
233 }
234 }
235 }
236
237 var anyIncluded, someExcluded bool
238 for _, dt := range t.tests {
239 if !t.shouldRunTest(dt.name) {
240 someExcluded = true
241 continue
242 }
243 anyIncluded = true
244 dt := dt
245 if err := dt.fn(&dt); err != nil {
246 t.runPending(&dt)
247 t.failed = true
248 if t.keepGoing {
249 log.Printf("Failed: %v", err)
250 } else {
251 fatalf("Failed: %v", err)
252 }
253 }
254 }
255 t.runPending(nil)
256 timelog("end", "dist test")
257
258 if !t.json {
259 if t.failed {
260 fmt.Println("\nFAILED")
261 } else if !anyIncluded {
262 fmt.Println()
263 errprintf("go tool dist: warning: %q matched no tests; use the -list flag to list available tests\n", t.runRxStr)
264 fmt.Println("NO TESTS TO RUN")
265 } else if someExcluded {
266 fmt.Println("\nALL TESTS PASSED (some were excluded)")
267 } else {
268 fmt.Println("\nALL TESTS PASSED")
269 }
270 }
271 if t.failed {
272 xexit(1)
273 }
274 }
275
276 func (t *tester) shouldRunTest(name string) bool {
277 if t.runRx != nil {
278 return t.runRx.MatchString(name) == t.runRxWant
279 }
280 if len(t.runNames) == 0 {
281 return true
282 }
283 for _, runName := range t.runNames {
284 if runName == name {
285 return true
286 }
287 }
288 return false
289 }
290
291 func (t *tester) maybeLogMetadata() error {
292 if t.compileOnly {
293
294
295 return nil
296 }
297 t.out("Test execution environment.")
298
299
300
301
302
303
304 return t.dirCmd(filepath.Join(goroot, "src/cmd/internal/metadata"), gorootBinGo, []string{"run", "main.go"}).Run()
305 }
306
307
308 func testName(pkg, variant string) string {
309 name := pkg
310 if variant != "" {
311 name += ":" + variant
312 }
313 return name
314 }
315
316
317
318 type goTest struct {
319 timeout time.Duration
320 short bool
321 tags []string
322 race bool
323 bench bool
324 runTests string
325 cpu string
326
327 gcflags string
328 ldflags string
329 buildmode string
330
331 env []string
332
333 runOnHost bool
334
335
336
337
338 variant string
339
340
341
342 omitVariant bool
343
344
345
346 pkgs []string
347 pkg string
348
349 testFlags []string
350 }
351
352
353
354
355 func (opts *goTest) bgCommand(t *tester, stdout, stderr io.Writer) (cmd *exec.Cmd, flush func()) {
356 build, run, pkgs, testFlags, setupCmd := opts.buildArgs(t)
357
358
359 args := append([]string{"test"}, build...)
360 if t.compileOnly {
361 args = append(args, "-c", "-o", os.DevNull)
362 } else {
363 args = append(args, run...)
364 }
365 args = append(args, pkgs...)
366 if !t.compileOnly {
367 args = append(args, testFlags...)
368 }
369
370 cmd = exec.Command(gorootBinGo, args...)
371 setupCmd(cmd)
372 if t.json && opts.variant != "" && !opts.omitVariant {
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387 if stdout == stderr {
388 stdout = &lockedWriter{w: stdout}
389 stderr = stdout
390 }
391 f := &testJSONFilter{w: stdout, variant: opts.variant}
392 cmd.Stdout = f
393 flush = f.Flush
394 } else {
395 cmd.Stdout = stdout
396 flush = func() {}
397 }
398 cmd.Stderr = stderr
399
400 return cmd, flush
401 }
402
403
404 func (opts *goTest) run(t *tester) error {
405 cmd, flush := opts.bgCommand(t, os.Stdout, os.Stderr)
406 err := cmd.Run()
407 flush()
408 return err
409 }
410
411
412
413
414
415
416
417
418 func (opts *goTest) buildArgs(t *tester) (build, run, pkgs, testFlags []string, setupCmd func(*exec.Cmd)) {
419 run = append(run, "-count=1")
420 if opts.timeout != 0 {
421 d := opts.timeout * time.Duration(t.timeoutScale)
422 run = append(run, "-timeout="+d.String())
423 } else if t.timeoutScale != 1 {
424 const goTestDefaultTimeout = 10 * time.Minute
425 run = append(run, "-timeout="+(goTestDefaultTimeout*time.Duration(t.timeoutScale)).String())
426 }
427 if opts.short || t.short {
428 run = append(run, "-short")
429 }
430 var tags []string
431 if t.iOS() {
432 tags = append(tags, "lldb")
433 }
434 if noOpt {
435 tags = append(tags, "noopt")
436 }
437 tags = append(tags, opts.tags...)
438 if len(tags) > 0 {
439 build = append(build, "-tags="+strings.Join(tags, ","))
440 }
441 if t.race || opts.race {
442 build = append(build, "-race")
443 }
444 if t.msan {
445 build = append(build, "-msan")
446 }
447 if t.asan {
448 build = append(build, "-asan")
449 }
450 if opts.bench {
451
452 run = append(run, "-run=^$")
453
454 run = append(run, "-bench=.*", "-benchtime=.1s")
455 } else if opts.runTests != "" {
456 run = append(run, "-run="+opts.runTests)
457 }
458 if opts.cpu != "" {
459 run = append(run, "-cpu="+opts.cpu)
460 }
461 if t.json {
462 run = append(run, "-json")
463 }
464
465 if opts.gcflags != "" {
466 build = append(build, "-gcflags=all="+opts.gcflags)
467 }
468 if opts.ldflags != "" {
469 build = append(build, "-ldflags="+opts.ldflags)
470 }
471 if opts.buildmode != "" {
472 build = append(build, "-buildmode="+opts.buildmode)
473 }
474
475 pkgs = opts.packages()
476
477 runOnHost := opts.runOnHost && (goarch != gohostarch || goos != gohostos)
478 needTestFlags := len(opts.testFlags) > 0 || runOnHost
479 if needTestFlags {
480 testFlags = append([]string{"-args"}, opts.testFlags...)
481 }
482 if runOnHost {
483
484 testFlags = append(testFlags, "-target="+goos+"/"+goarch)
485 }
486
487 setupCmd = func(cmd *exec.Cmd) {
488 setDir(cmd, filepath.Join(goroot, "src"))
489 if len(opts.env) != 0 {
490 for _, kv := range opts.env {
491 if i := strings.Index(kv, "="); i < 0 {
492 unsetEnv(cmd, kv[:len(kv)-1])
493 } else {
494 setEnv(cmd, kv[:i], kv[i+1:])
495 }
496 }
497 }
498 if runOnHost {
499 setEnv(cmd, "GOARCH", gohostarch)
500 setEnv(cmd, "GOOS", gohostos)
501 }
502 }
503
504 return
505 }
506
507
508
509 func (opts *goTest) packages() []string {
510 pkgs := opts.pkgs
511 if opts.pkg != "" {
512 pkgs = append(pkgs[:len(pkgs):len(pkgs)], opts.pkg)
513 }
514 if len(pkgs) == 0 {
515 panic("no packages")
516 }
517 return pkgs
518 }
519
520
521 func (opts *goTest) printSkip(t *tester, msg string) {
522 if t.json {
523 enc := json.NewEncoder(os.Stdout)
524 for _, pkg := range opts.packages() {
525 synthesizeSkipEvent(enc, pkg, msg)
526 }
527 return
528 }
529 fmt.Println(msg)
530 }
531
532
533
534
535
536
537
538 var (
539 ranGoTest bool
540 stdMatches []string
541
542 ranGoBench bool
543 benchMatches []string
544 )
545
546 func (t *tester) registerStdTest(pkg string) {
547 const stdTestHeading = "Testing packages."
548 gcflags := gogcflags
549 name := testName(pkg, "")
550 if t.runRx == nil || t.runRx.MatchString(name) == t.runRxWant {
551 stdMatches = append(stdMatches, pkg)
552 }
553 t.addTest(name, stdTestHeading, func(dt *distTest) error {
554 if ranGoTest {
555 return nil
556 }
557 t.runPending(dt)
558 timelog("start", dt.name)
559 defer timelog("end", dt.name)
560 ranGoTest = true
561
562 timeoutSec := 180 * time.Second
563 for _, pkg := range stdMatches {
564 if pkg == "cmd/go" {
565 timeoutSec *= 3
566 break
567 }
568 }
569 return (&goTest{
570 timeout: timeoutSec,
571 gcflags: gcflags,
572 pkgs: stdMatches,
573 }).run(t)
574 })
575 }
576
577 func (t *tester) registerRaceBenchTest(pkg string) {
578 const raceBenchHeading = "Running benchmarks briefly."
579 name := testName(pkg, "racebench")
580 if t.runRx == nil || t.runRx.MatchString(name) == t.runRxWant {
581 benchMatches = append(benchMatches, pkg)
582 }
583 t.addTest(name, raceBenchHeading, func(dt *distTest) error {
584 if ranGoBench {
585 return nil
586 }
587 t.runPending(dt)
588 timelog("start", dt.name)
589 defer timelog("end", dt.name)
590 ranGoBench = true
591 return (&goTest{
592 variant: "racebench",
593 omitVariant: true,
594 timeout: 1200 * time.Second,
595 race: true,
596 bench: true,
597 cpu: "4",
598 pkgs: benchMatches,
599 }).run(t)
600 })
601 }
602
603 func (t *tester) registerTests() {
604
605
606
607
608
609
610
611 registerStdTestSpecially := map[string]bool{
612
613
614
615
616 "cmd/internal/testdir": true,
617 }
618
619
620
621
622 if len(t.runNames) > 0 {
623 for _, name := range t.runNames {
624 if !strings.Contains(name, ":") {
625 t.registerStdTest(name)
626 } else if strings.HasSuffix(name, ":racebench") {
627 t.registerRaceBenchTest(strings.TrimSuffix(name, ":racebench"))
628 }
629 }
630 } else {
631
632
633
634
635
636
637
638
639
640 cmd := exec.Command(gorootBinGo, "list")
641 if t.short {
642
643
644 const format = "{{if (or .TestGoFiles .XTestGoFiles)}}{{.ImportPath}}{{end}}"
645 cmd.Args = append(cmd.Args, "-f", format)
646 }
647 if t.race {
648 cmd.Args = append(cmd.Args, "-tags=race")
649 }
650 cmd.Args = append(cmd.Args, "std", "cmd")
651 cmd.Stderr = new(bytes.Buffer)
652 all, err := cmd.Output()
653 if err != nil {
654 fatalf("Error running go list std cmd: %v:\n%s", err, cmd.Stderr)
655 }
656 pkgs := strings.Fields(string(all))
657 for _, pkg := range pkgs {
658 if registerStdTestSpecially[pkg] {
659 continue
660 }
661 t.registerStdTest(pkg)
662 }
663 if t.race {
664 for _, pkg := range pkgs {
665 if t.packageHasBenchmarks(pkg) {
666 t.registerRaceBenchTest(pkg)
667 }
668 }
669 }
670 }
671
672 if t.race {
673 return
674 }
675
676
677 if !t.compileOnly {
678 t.registerTest("os/user with tag osusergo",
679 &goTest{
680 variant: "osusergo",
681 timeout: 300 * time.Second,
682 tags: []string{"osusergo"},
683 pkg: "os/user",
684 })
685 t.registerTest("hash/maphash purego implementation",
686 &goTest{
687 variant: "purego",
688 timeout: 300 * time.Second,
689 tags: []string{"purego"},
690 pkg: "hash/maphash",
691 })
692 }
693
694
695 if goos == "darwin" && goarch == "amd64" && t.cgoEnabled {
696 t.registerTest("GOOS=ios on darwin/amd64",
697 &goTest{
698 variant: "amd64ios",
699 timeout: 300 * time.Second,
700 runTests: "SystemRoots",
701 env: []string{"GOOS=ios", "CGO_ENABLED=1"},
702 pkg: "crypto/x509",
703 })
704 }
705
706
707 if !t.compileOnly && t.hasParallelism() {
708 t.registerTest("GOMAXPROCS=2 runtime -cpu=1,2,4 -quick",
709 &goTest{
710 variant: "cpu124",
711 timeout: 300 * time.Second,
712 cpu: "1,2,4",
713 short: true,
714 testFlags: []string{"-quick"},
715
716
717 env: []string{"GOMAXPROCS=2"},
718 pkg: "runtime",
719 })
720 }
721
722
723 if !t.compileOnly {
724 t.registerTest("GOEXPERIMENT=rangefunc go test iter",
725 &goTest{
726 variant: "iter",
727 short: t.short,
728 env: []string{"GOEXPERIMENT=rangefunc"},
729 pkg: "iter",
730 })
731 }
732
733
734
735
736 if !t.compileOnly && !t.short {
737 t.registerTest("GODEBUG=gcstoptheworld=2 archive/zip",
738 &goTest{
739 variant: "runtime:gcstoptheworld2",
740 timeout: 300 * time.Second,
741 short: true,
742 env: []string{"GODEBUG=gcstoptheworld=2"},
743 pkg: "archive/zip",
744 })
745 }
746
747
748
749
750
751 if !t.compileOnly && !t.short {
752
753 hooks := []string{"mayMoreStackPreempt", "mayMoreStackMove"}
754
755
756 hookPkgs := []string{"runtime/...", "reflect", "sync"}
757
758
759 unhookPkgs := []string{"runtime/testdata/..."}
760 for _, hook := range hooks {
761
762
763
764
765
766
767 goFlagsList := []string{}
768 for _, flag := range []string{"-gcflags", "-asmflags"} {
769 for _, hookPkg := range hookPkgs {
770 goFlagsList = append(goFlagsList, flag+"="+hookPkg+"=-d=maymorestack=runtime."+hook)
771 }
772 for _, unhookPkg := range unhookPkgs {
773 goFlagsList = append(goFlagsList, flag+"="+unhookPkg+"=")
774 }
775 }
776 goFlags := strings.Join(goFlagsList, " ")
777
778 t.registerTest("maymorestack="+hook,
779 &goTest{
780 variant: hook,
781 timeout: 600 * time.Second,
782 short: true,
783 env: []string{"GOFLAGS=" + goFlags},
784 pkgs: []string{"runtime", "reflect", "sync"},
785 })
786 }
787 }
788
789
790
791
792
793 for _, pkg := range cgoPackages {
794 if !t.internalLink() {
795 break
796 }
797
798
799 if goarch == "arm" {
800 break
801 }
802
803
804
805 run := "^Test[^CS]"
806 if pkg == "net" {
807 run = "TestTCPStress"
808 }
809 t.registerTest("Testing without libgcc.",
810 &goTest{
811 variant: "nolibgcc",
812 ldflags: "-linkmode=internal -libgcc=none",
813 runTests: run,
814 pkg: pkg,
815 })
816 }
817
818
819 builderName := os.Getenv("GO_BUILDER_NAME")
820 disablePIE := strings.HasSuffix(builderName, "-alpine")
821
822
823 if t.internalLinkPIE() && !disablePIE {
824 t.registerTest("internal linking of -buildmode=pie",
825 &goTest{
826 variant: "pie_internal",
827 timeout: 60 * time.Second,
828 buildmode: "pie",
829 ldflags: "-linkmode=internal",
830 env: []string{"CGO_ENABLED=0"},
831 pkg: "reflect",
832 })
833
834 if t.cgoEnabled && t.internalLink() && !disablePIE {
835 t.registerTest("internal linking of -buildmode=pie",
836 &goTest{
837 variant: "pie_internal",
838 timeout: 60 * time.Second,
839 buildmode: "pie",
840 ldflags: "-linkmode=internal",
841 pkg: "os/user",
842 })
843 }
844 }
845
846
847 if t.hasParallelism() {
848 t.registerTest("sync -cpu=10",
849 &goTest{
850 variant: "cpu10",
851 timeout: 120 * time.Second,
852 cpu: "10",
853 pkg: "sync",
854 })
855 }
856
857 if t.raceDetectorSupported() {
858 t.registerRaceTests()
859 }
860
861 const cgoHeading = "Testing cgo"
862 if t.cgoEnabled {
863 t.registerCgoTests(cgoHeading)
864 }
865
866 if goos == "wasip1" {
867 t.registerTest("wasip1 host tests",
868 &goTest{
869 variant: "host",
870 pkg: "runtime/internal/wasitest",
871 timeout: 1 * time.Minute,
872 runOnHost: true,
873 })
874 }
875
876 if goos != "android" && !t.iOS() {
877
878
879
880 nShards := 1
881 if os.Getenv("GO_BUILDER_NAME") != "" {
882 nShards = 10
883 }
884 if n, err := strconv.Atoi(os.Getenv("GO_TEST_SHARDS")); err == nil {
885 nShards = n
886 }
887 for shard := 0; shard < nShards; shard++ {
888 id := fmt.Sprintf("%d_%d", shard, nShards)
889 t.registerTest("../test",
890 &goTest{
891 variant: id,
892 omitVariant: true,
893 pkg: "cmd/internal/testdir",
894 testFlags: []string{fmt.Sprintf("-shard=%d", shard), fmt.Sprintf("-shards=%d", nShards)},
895 runOnHost: true,
896 },
897 )
898 }
899 }
900
901
902
903
904
905 if goos == "darwin" || ((goos == "linux" || goos == "windows") && goarch == "amd64") {
906 t.registerTest("API check", &goTest{variant: "check", pkg: "cmd/api", timeout: 5 * time.Minute, testFlags: []string{"-check"}})
907 }
908 }
909
910
911
912
913 func (t *tester) addTest(name, heading string, fn func(*distTest) error) {
914 if t.testNames[name] {
915 panic("duplicate registered test name " + name)
916 }
917 if heading == "" {
918 panic("empty heading")
919 }
920
921 if !strings.Contains(name, ":") && heading != "Testing packages." {
922 panic("empty variant is reserved exclusively for registerStdTest")
923 } else if strings.HasSuffix(name, ":racebench") && heading != "Running benchmarks briefly." {
924 panic("racebench variant is reserved exclusively for registerRaceBenchTest")
925 }
926 if t.testNames == nil {
927 t.testNames = make(map[string]bool)
928 }
929 t.testNames[name] = true
930 t.tests = append(t.tests, distTest{
931 name: name,
932 heading: heading,
933 fn: fn,
934 })
935 }
936
937 type registerTestOpt interface {
938 isRegisterTestOpt()
939 }
940
941
942
943 type rtSkipFunc struct {
944 skip func(*distTest) (string, bool)
945 }
946
947 func (rtSkipFunc) isRegisterTestOpt() {}
948
949
950
951
952
953
954
955 func (t *tester) registerTest(heading string, test *goTest, opts ...registerTestOpt) {
956 var skipFunc func(*distTest) (string, bool)
957 for _, opt := range opts {
958 switch opt := opt.(type) {
959 case rtSkipFunc:
960 skipFunc = opt.skip
961 }
962 }
963
964 register1 := func(test *goTest) {
965 if test.variant == "" {
966 panic("empty variant")
967 }
968 name := testName(test.pkg, test.variant)
969 t.addTest(name, heading, func(dt *distTest) error {
970 if skipFunc != nil {
971 msg, skip := skipFunc(dt)
972 if skip {
973 test.printSkip(t, msg)
974 return nil
975 }
976 }
977 w := &work{dt: dt}
978 w.cmd, w.flush = test.bgCommand(t, &w.out, &w.out)
979 t.worklist = append(t.worklist, w)
980 return nil
981 })
982 }
983 if test.pkg != "" && len(test.pkgs) == 0 {
984
985 register1(test)
986 return
987 }
988
989
990
991
992
993
994 for _, pkg := range test.packages() {
995 test1 := *test
996 test1.pkg, test1.pkgs = pkg, nil
997 register1(&test1)
998 }
999 }
1000
1001
1002
1003
1004 func (t *tester) dirCmd(dir string, cmdline ...interface{}) *exec.Cmd {
1005 bin, args := flattenCmdline(cmdline)
1006 cmd := exec.Command(bin, args...)
1007 if filepath.IsAbs(dir) {
1008 setDir(cmd, dir)
1009 } else {
1010 setDir(cmd, filepath.Join(goroot, dir))
1011 }
1012 cmd.Stdout = os.Stdout
1013 cmd.Stderr = os.Stderr
1014 if vflag > 1 {
1015 errprintf("%s\n", strings.Join(cmd.Args, " "))
1016 }
1017 return cmd
1018 }
1019
1020
1021
1022 func flattenCmdline(cmdline []interface{}) (bin string, args []string) {
1023 var list []string
1024 for _, x := range cmdline {
1025 switch x := x.(type) {
1026 case string:
1027 list = append(list, x)
1028 case []string:
1029 list = append(list, x...)
1030 default:
1031 panic("invalid dirCmd argument type: " + reflect.TypeOf(x).String())
1032 }
1033 }
1034
1035 bin = list[0]
1036 if !filepath.IsAbs(bin) {
1037 panic("command is not absolute: " + bin)
1038 }
1039 return bin, list[1:]
1040 }
1041
1042 func (t *tester) iOS() bool {
1043 return goos == "ios"
1044 }
1045
1046 func (t *tester) out(v string) {
1047 if t.json {
1048 return
1049 }
1050 if t.banner == "" {
1051 return
1052 }
1053 fmt.Println("\n" + t.banner + v)
1054 }
1055
1056
1057
1058
1059 func (t *tester) extLink() bool {
1060 if goarch == "ppc64" && goos != "aix" {
1061 return false
1062 }
1063 return true
1064 }
1065
1066 func (t *tester) internalLink() bool {
1067 if gohostos == "dragonfly" {
1068
1069 return false
1070 }
1071 if goos == "android" {
1072 return false
1073 }
1074 if goos == "ios" {
1075 return false
1076 }
1077 if goos == "windows" && goarch == "arm64" {
1078 return false
1079 }
1080
1081
1082
1083 if goarch == "loong64" || goarch == "mips64" || goarch == "mips64le" || goarch == "mips" || goarch == "mipsle" || goarch == "riscv64" {
1084 return false
1085 }
1086 if goos == "aix" {
1087
1088 return false
1089 }
1090 return true
1091 }
1092
1093 func (t *tester) internalLinkPIE() bool {
1094 switch goos + "-" + goarch {
1095 case "darwin-amd64", "darwin-arm64",
1096 "linux-amd64", "linux-arm64", "linux-ppc64le",
1097 "android-arm64",
1098 "windows-amd64", "windows-386", "windows-arm":
1099 return true
1100 }
1101 return false
1102 }
1103
1104
1105 func (t *tester) supportedBuildmode(mode string) bool {
1106 switch mode {
1107 case "c-archive", "c-shared", "shared", "plugin", "pie":
1108 default:
1109 fatalf("internal error: unknown buildmode %s", mode)
1110 return false
1111 }
1112
1113 return buildModeSupported("gc", mode, goos, goarch)
1114 }
1115
1116 func (t *tester) registerCgoTests(heading string) {
1117 cgoTest := func(variant string, subdir, linkmode, buildmode string, opts ...registerTestOpt) *goTest {
1118 gt := &goTest{
1119 variant: variant,
1120 pkg: "cmd/cgo/internal/" + subdir,
1121 buildmode: buildmode,
1122 }
1123 var ldflags []string
1124 if linkmode != "auto" {
1125
1126 ldflags = append(ldflags, "-linkmode="+linkmode)
1127 }
1128
1129 if linkmode == "internal" {
1130 gt.tags = append(gt.tags, "internal")
1131 if buildmode == "pie" {
1132 gt.tags = append(gt.tags, "internal_pie")
1133 }
1134 }
1135 if buildmode == "static" {
1136
1137
1138 gt.buildmode = ""
1139 if linkmode == "external" {
1140 ldflags = append(ldflags, `-extldflags "-static -pthread"`)
1141 } else if linkmode == "auto" {
1142 gt.env = append(gt.env, "CGO_LDFLAGS=-static -pthread")
1143 } else {
1144 panic("unknown linkmode with static build: " + linkmode)
1145 }
1146 gt.tags = append(gt.tags, "static")
1147 }
1148 gt.ldflags = strings.Join(ldflags, " ")
1149
1150 t.registerTest(heading, gt, opts...)
1151 return gt
1152 }
1153
1154
1155
1156
1157
1158
1159 builderName := os.Getenv("GO_BUILDER_NAME")
1160 disablePIE := strings.HasSuffix(builderName, "-alpine")
1161
1162 if t.internalLink() {
1163 cgoTest("internal", "test", "internal", "")
1164 }
1165
1166 os := gohostos
1167 p := gohostos + "/" + goarch
1168 switch {
1169 case os == "darwin", os == "windows":
1170 if !t.extLink() {
1171 break
1172 }
1173
1174 cgoTest("external", "test", "external", "")
1175
1176 gt := cgoTest("external-s", "test", "external", "")
1177 gt.ldflags += " -s"
1178
1179 if t.supportedBuildmode("pie") && !disablePIE {
1180 cgoTest("auto-pie", "test", "auto", "pie")
1181 if t.internalLink() && t.internalLinkPIE() {
1182 cgoTest("internal-pie", "test", "internal", "pie")
1183 }
1184 }
1185
1186 case os == "aix", os == "android", os == "dragonfly", os == "freebsd", os == "linux", os == "netbsd", os == "openbsd":
1187 gt := cgoTest("external-g0", "test", "external", "")
1188 gt.env = append(gt.env, "CGO_CFLAGS=-g0 -fdiagnostics-color")
1189
1190 cgoTest("external", "testtls", "external", "")
1191 switch {
1192 case os == "aix":
1193
1194 case p == "freebsd/arm":
1195
1196
1197
1198
1199
1200 default:
1201
1202 var staticCheck rtSkipFunc
1203 ccName := compilerEnvLookup("CC", defaultcc, goos, goarch)
1204 cc, err := exec.LookPath(ccName)
1205 if err != nil {
1206 staticCheck.skip = func(*distTest) (string, bool) {
1207 return fmt.Sprintf("$CC (%q) not found, skip cgo static linking test.", ccName), true
1208 }
1209 } else {
1210 cmd := t.dirCmd("src/cmd/cgo/internal/test", cc, "-xc", "-o", "/dev/null", "-static", "-")
1211 cmd.Stdin = strings.NewReader("int main() {}")
1212 cmd.Stdout, cmd.Stderr = nil, nil
1213 if err := cmd.Run(); err != nil {
1214
1215 staticCheck.skip = func(*distTest) (string, bool) {
1216 return "No support for static linking found (lacks libc.a?), skip cgo static linking test.", true
1217 }
1218 }
1219 }
1220
1221
1222
1223
1224
1225 if staticCheck.skip == nil && goos == "linux" && strings.Contains(goexperiment, "boringcrypto") {
1226 staticCheck.skip = func(*distTest) (string, bool) {
1227 return "skipping static linking check on Linux when using boringcrypto to avoid C linker warning about getaddrinfo", true
1228 }
1229 }
1230
1231
1232 if goos != "android" && p != "netbsd/arm" {
1233
1234 cgoTest("static", "testtls", "external", "static", staticCheck)
1235 }
1236 cgoTest("external", "testnocgo", "external", "", staticCheck)
1237 if goos != "android" {
1238 cgoTest("static", "testnocgo", "external", "static", staticCheck)
1239 cgoTest("static", "test", "external", "static", staticCheck)
1240
1241
1242
1243 if goarch != "loong64" {
1244
1245 cgoTest("auto-static", "test", "auto", "static", staticCheck)
1246 }
1247 }
1248
1249
1250 if t.supportedBuildmode("pie") && !disablePIE {
1251 cgoTest("auto-pie", "test", "auto", "pie")
1252 if t.internalLink() && t.internalLinkPIE() {
1253 cgoTest("internal-pie", "test", "internal", "pie")
1254 }
1255 cgoTest("auto-pie", "testtls", "auto", "pie")
1256 cgoTest("auto-pie", "testnocgo", "auto", "pie")
1257 }
1258 }
1259 }
1260 }
1261
1262
1263
1264
1265
1266
1267
1268 func (t *tester) runPending(nextTest *distTest) {
1269 worklist := t.worklist
1270 t.worklist = nil
1271 for _, w := range worklist {
1272 w.start = make(chan bool)
1273 w.end = make(chan struct{})
1274
1275
1276 if w.cmd.Stdout == nil || w.cmd.Stdout == os.Stdout || w.cmd.Stderr == nil || w.cmd.Stderr == os.Stderr {
1277 panic("work.cmd.Stdout/Stderr must be redirected")
1278 }
1279 go func(w *work) {
1280 if !<-w.start {
1281 timelog("skip", w.dt.name)
1282 w.printSkip(t, "skipped due to earlier error")
1283 } else {
1284 timelog("start", w.dt.name)
1285 w.err = w.cmd.Run()
1286 if w.flush != nil {
1287 w.flush()
1288 }
1289 if w.err != nil {
1290 if isUnsupportedVMASize(w) {
1291 timelog("skip", w.dt.name)
1292 w.out.Reset()
1293 w.printSkip(t, "skipped due to unsupported VMA")
1294 w.err = nil
1295 }
1296 }
1297 }
1298 timelog("end", w.dt.name)
1299 w.end <- struct{}{}
1300 }(w)
1301 }
1302
1303 started := 0
1304 ended := 0
1305 var last *distTest
1306 for ended < len(worklist) {
1307 for started < len(worklist) && started-ended < maxbg {
1308 w := worklist[started]
1309 started++
1310 w.start <- !t.failed || t.keepGoing
1311 }
1312 w := worklist[ended]
1313 dt := w.dt
1314 if t.lastHeading != dt.heading {
1315 t.lastHeading = dt.heading
1316 t.out(dt.heading)
1317 }
1318 if dt != last {
1319
1320 last = w.dt
1321 if vflag > 0 {
1322 fmt.Printf("# go tool dist test -run=^%s$\n", dt.name)
1323 }
1324 }
1325 if vflag > 1 {
1326 errprintf("%s\n", strings.Join(w.cmd.Args, " "))
1327 }
1328 ended++
1329 <-w.end
1330 os.Stdout.Write(w.out.Bytes())
1331
1332 w.out = bytes.Buffer{}
1333 if w.err != nil {
1334 log.Printf("Failed: %v", w.err)
1335 t.failed = true
1336 }
1337 }
1338 if t.failed && !t.keepGoing {
1339 fatalf("FAILED")
1340 }
1341
1342 if dt := nextTest; dt != nil {
1343 if t.lastHeading != dt.heading {
1344 t.lastHeading = dt.heading
1345 t.out(dt.heading)
1346 }
1347 if vflag > 0 {
1348 fmt.Printf("# go tool dist test -run=^%s$\n", dt.name)
1349 }
1350 }
1351 }
1352
1353 func (t *tester) hasBash() bool {
1354 switch gohostos {
1355 case "windows", "plan9":
1356 return false
1357 }
1358 return true
1359 }
1360
1361
1362
1363
1364 func (t *tester) hasParallelism() bool {
1365 switch goos {
1366 case "js", "wasip1":
1367 return false
1368 }
1369 return true
1370 }
1371
1372 func (t *tester) raceDetectorSupported() bool {
1373 if gohostos != goos {
1374 return false
1375 }
1376 if !t.cgoEnabled {
1377 return false
1378 }
1379 if !raceDetectorSupported(goos, goarch) {
1380 return false
1381 }
1382
1383
1384 if isAlpineLinux() {
1385 return false
1386 }
1387
1388
1389 if goos == "netbsd" {
1390 return false
1391 }
1392 return true
1393 }
1394
1395 func isAlpineLinux() bool {
1396 if runtime.GOOS != "linux" {
1397 return false
1398 }
1399 fi, err := os.Lstat("/etc/alpine-release")
1400 return err == nil && fi.Mode().IsRegular()
1401 }
1402
1403 func (t *tester) registerRaceTests() {
1404 hdr := "Testing race detector"
1405 t.registerTest(hdr,
1406 &goTest{
1407 variant: "race",
1408 race: true,
1409 runTests: "Output",
1410 pkg: "runtime/race",
1411 })
1412 t.registerTest(hdr,
1413 &goTest{
1414 variant: "race",
1415 race: true,
1416 runTests: "TestParse|TestEcho|TestStdinCloseRace|TestClosedPipeRace|TestTypeRace|TestFdRace|TestFdReadRace|TestFileCloseRace",
1417 pkgs: []string{"flag", "net", "os", "os/exec", "encoding/gob"},
1418 })
1419
1420
1421
1422
1423
1424 if t.cgoEnabled {
1425
1426
1427
1428
1429
1430 }
1431 if t.extLink() {
1432
1433 t.registerTest(hdr,
1434 &goTest{
1435 variant: "race-external",
1436 race: true,
1437 ldflags: "-linkmode=external",
1438 runTests: "TestParse|TestEcho|TestStdinCloseRace",
1439 pkgs: []string{"flag", "os/exec"},
1440 })
1441 }
1442 }
1443
1444
1445 var cgoPackages = []string{
1446 "net",
1447 "os/user",
1448 }
1449
1450 var funcBenchmark = []byte("\nfunc Benchmark")
1451
1452
1453
1454
1455
1456
1457
1458
1459 func (t *tester) packageHasBenchmarks(pkg string) bool {
1460 pkgDir := filepath.Join(goroot, "src", pkg)
1461 d, err := os.Open(pkgDir)
1462 if err != nil {
1463 return true
1464 }
1465 defer d.Close()
1466 names, err := d.Readdirnames(-1)
1467 if err != nil {
1468 return true
1469 }
1470 for _, name := range names {
1471 if !strings.HasSuffix(name, "_test.go") {
1472 continue
1473 }
1474 slurp, err := os.ReadFile(filepath.Join(pkgDir, name))
1475 if err != nil {
1476 return true
1477 }
1478 if bytes.Contains(slurp, funcBenchmark) {
1479 return true
1480 }
1481 }
1482 return false
1483 }
1484
1485
1486
1487 func (t *tester) makeGOROOTUnwritable() (undo func()) {
1488 dir := os.Getenv("GOROOT")
1489 if dir == "" {
1490 panic("GOROOT not set")
1491 }
1492
1493 type pathMode struct {
1494 path string
1495 mode os.FileMode
1496 }
1497 var dirs []pathMode
1498
1499 undo = func() {
1500 for i := range dirs {
1501 os.Chmod(dirs[i].path, dirs[i].mode)
1502 }
1503 }
1504
1505 filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
1506 if suffix := strings.TrimPrefix(path, dir+string(filepath.Separator)); suffix != "" {
1507 if suffix == ".git" {
1508
1509
1510
1511 return filepath.SkipDir
1512 }
1513 }
1514 if err != nil {
1515 return nil
1516 }
1517
1518 info, err := d.Info()
1519 if err != nil {
1520 return nil
1521 }
1522
1523 mode := info.Mode()
1524 if mode&0222 != 0 && (mode.IsDir() || mode.IsRegular()) {
1525 dirs = append(dirs, pathMode{path, mode})
1526 }
1527 return nil
1528 })
1529
1530
1531 for i := len(dirs) - 1; i >= 0; i-- {
1532 err := os.Chmod(dirs[i].path, dirs[i].mode&^0222)
1533 if err != nil {
1534 dirs = dirs[i:]
1535 undo()
1536 fatalf("failed to make GOROOT read-only: %v", err)
1537 }
1538 }
1539
1540 return undo
1541 }
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551 func raceDetectorSupported(goos, goarch string) bool {
1552 switch goos {
1553 case "linux":
1554 return goarch == "amd64" || goarch == "ppc64le" || goarch == "arm64" || goarch == "s390x"
1555 case "darwin":
1556 return goarch == "amd64" || goarch == "arm64"
1557 case "freebsd", "netbsd", "openbsd", "windows":
1558 return goarch == "amd64"
1559 default:
1560 return false
1561 }
1562 }
1563
1564
1565
1566
1567 func buildModeSupported(compiler, buildmode, goos, goarch string) bool {
1568 if compiler == "gccgo" {
1569 return true
1570 }
1571
1572 platform := goos + "/" + goarch
1573
1574 switch buildmode {
1575 case "archive":
1576 return true
1577
1578 case "c-archive":
1579 switch goos {
1580 case "aix", "darwin", "ios", "windows":
1581 return true
1582 case "linux":
1583 switch goarch {
1584 case "386", "amd64", "arm", "armbe", "arm64", "arm64be", "loong64", "ppc64le", "riscv64", "s390x":
1585
1586
1587 return true
1588 default:
1589
1590
1591
1592
1593
1594
1595 return false
1596 }
1597 case "freebsd":
1598 return goarch == "amd64"
1599 }
1600 return false
1601
1602 case "c-shared":
1603 switch platform {
1604 case "linux/amd64", "linux/arm", "linux/arm64", "linux/loong64", "linux/386", "linux/ppc64le", "linux/riscv64", "linux/s390x",
1605 "android/amd64", "android/arm", "android/arm64", "android/386",
1606 "freebsd/amd64",
1607 "darwin/amd64", "darwin/arm64",
1608 "windows/amd64", "windows/386", "windows/arm64":
1609 return true
1610 }
1611 return false
1612
1613 case "default":
1614 return true
1615
1616 case "exe":
1617 return true
1618
1619 case "pie":
1620 switch platform {
1621 case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/loong64", "linux/ppc64le", "linux/riscv64", "linux/s390x",
1622 "android/amd64", "android/arm", "android/arm64", "android/386",
1623 "freebsd/amd64",
1624 "darwin/amd64", "darwin/arm64",
1625 "ios/amd64", "ios/arm64",
1626 "aix/ppc64",
1627 "windows/386", "windows/amd64", "windows/arm", "windows/arm64":
1628 return true
1629 }
1630 return false
1631
1632 case "shared":
1633 switch platform {
1634 case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/s390x":
1635 return true
1636 }
1637 return false
1638
1639 case "plugin":
1640 switch platform {
1641 case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/loong64", "linux/s390x", "linux/ppc64le",
1642 "android/amd64", "android/386",
1643 "darwin/amd64", "darwin/arm64",
1644 "freebsd/amd64":
1645 return true
1646 }
1647 return false
1648
1649 default:
1650 return false
1651 }
1652 }
1653
1654
1655
1656
1657 func isUnsupportedVMASize(w *work) bool {
1658 unsupportedVMA := []byte("unsupported VMA range")
1659 return strings.Contains(w.dt.name, ":race") && bytes.Contains(w.out.Bytes(), unsupportedVMA)
1660 }
1661
1662
1663
1664 func isEnvSet(evar string) bool {
1665 evarEq := evar + "="
1666 for _, e := range os.Environ() {
1667 if strings.HasPrefix(e, evarEq) {
1668 return true
1669 }
1670 }
1671 return false
1672 }
1673
View as plain text