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 t.registerTest("crypto with tag purego", &goTest{
696 variant: "purego",
697 tags: []string{"purego"},
698 pkg: "crypto/...",
699 runTests: "^$",
700 })
701
702
703 if goos == "darwin" && goarch == "amd64" && t.cgoEnabled {
704 t.registerTest("GOOS=ios on darwin/amd64",
705 &goTest{
706 variant: "amd64ios",
707 timeout: 300 * time.Second,
708 runTests: "SystemRoots",
709 env: []string{"GOOS=ios", "CGO_ENABLED=1"},
710 pkg: "crypto/x509",
711 })
712 }
713
714
715
716
717 if !t.compileOnly && !t.short {
718 t.registerTest("GODEBUG=gcstoptheworld=2 archive/zip",
719 &goTest{
720 variant: "runtime:gcstoptheworld2",
721 timeout: 300 * time.Second,
722 short: true,
723 env: []string{"GODEBUG=gcstoptheworld=2"},
724 pkg: "archive/zip",
725 })
726 }
727
728
729
730
731
732 if !t.compileOnly && !t.short {
733
734 hooks := []string{"mayMoreStackPreempt", "mayMoreStackMove"}
735
736
737 hookPkgs := []string{"runtime/...", "reflect", "sync"}
738
739
740 unhookPkgs := []string{"runtime/testdata/..."}
741 for _, hook := range hooks {
742
743
744
745
746
747
748 goFlagsList := []string{}
749 for _, flag := range []string{"-gcflags", "-asmflags"} {
750 for _, hookPkg := range hookPkgs {
751 goFlagsList = append(goFlagsList, flag+"="+hookPkg+"=-d=maymorestack=runtime."+hook)
752 }
753 for _, unhookPkg := range unhookPkgs {
754 goFlagsList = append(goFlagsList, flag+"="+unhookPkg+"=")
755 }
756 }
757 goFlags := strings.Join(goFlagsList, " ")
758
759 t.registerTest("maymorestack="+hook,
760 &goTest{
761 variant: hook,
762 timeout: 600 * time.Second,
763 short: true,
764 env: []string{"GOFLAGS=" + goFlags},
765 pkgs: []string{"runtime", "reflect", "sync"},
766 })
767 }
768 }
769
770
771
772
773
774 for _, pkg := range cgoPackages {
775 if !t.internalLink() {
776 break
777 }
778
779
780 if goarch == "arm" {
781 break
782 }
783
784
785
786 run := "^Test[^CS]"
787 if pkg == "net" {
788 run = "TestTCPStress"
789 }
790 t.registerTest("Testing without libgcc.",
791 &goTest{
792 variant: "nolibgcc",
793 ldflags: "-linkmode=internal -libgcc=none",
794 runTests: run,
795 pkg: pkg,
796 })
797 }
798
799
800 builderName := os.Getenv("GO_BUILDER_NAME")
801 disablePIE := strings.HasSuffix(builderName, "-alpine")
802
803
804 if t.internalLinkPIE() && !disablePIE {
805 t.registerTest("internal linking of -buildmode=pie",
806 &goTest{
807 variant: "pie_internal",
808 timeout: 60 * time.Second,
809 buildmode: "pie",
810 ldflags: "-linkmode=internal",
811 env: []string{"CGO_ENABLED=0"},
812 pkg: "reflect",
813 })
814
815 if t.cgoEnabled && t.internalLink() && !disablePIE {
816 t.registerTest("internal linking of -buildmode=pie",
817 &goTest{
818 variant: "pie_internal",
819 timeout: 60 * time.Second,
820 buildmode: "pie",
821 ldflags: "-linkmode=internal",
822 pkg: "os/user",
823 })
824 }
825 }
826
827
828 if t.hasParallelism() {
829 t.registerTest("sync -cpu=10",
830 &goTest{
831 variant: "cpu10",
832 timeout: 120 * time.Second,
833 cpu: "10",
834 pkg: "sync",
835 })
836 }
837
838 const cgoHeading = "Testing cgo"
839 if t.cgoEnabled {
840 t.registerCgoTests(cgoHeading)
841 }
842
843 if goos == "wasip1" {
844 t.registerTest("wasip1 host tests",
845 &goTest{
846 variant: "host",
847 pkg: "runtime/internal/wasitest",
848 timeout: 1 * time.Minute,
849 runOnHost: true,
850 })
851 }
852
853
854
855
856
857
858
859
860 if goos == "darwin" || ((goos == "linux" || goos == "windows") && goarch == "amd64") {
861 t.registerTest("API release note check", &goTest{variant: "check", pkg: "cmd/relnote", testFlags: []string{"-check"}})
862 t.registerTest("API check", &goTest{variant: "check", pkg: "cmd/api", timeout: 5 * time.Minute, testFlags: []string{"-check"}})
863 }
864
865
866 if !t.compileOnly && t.hasParallelism() {
867 for i := 1; i <= 4; i *= 2 {
868 t.registerTest(fmt.Sprintf("GOMAXPROCS=2 runtime -cpu=%d -quick", i),
869 &goTest{
870 variant: "cpu" + strconv.Itoa(i),
871 timeout: 300 * time.Second,
872 cpu: strconv.Itoa(i),
873 short: true,
874 testFlags: []string{"-quick"},
875
876
877 env: []string{"GOMAXPROCS=2"},
878 pkg: "runtime",
879 })
880 }
881 }
882
883 if t.raceDetectorSupported() {
884 t.registerRaceTests()
885 }
886
887 if goos != "android" && !t.iOS() {
888
889
890
891 nShards := 1
892 if os.Getenv("GO_BUILDER_NAME") != "" {
893 nShards = 10
894 }
895 if n, err := strconv.Atoi(os.Getenv("GO_TEST_SHARDS")); err == nil {
896 nShards = n
897 }
898 for shard := 0; shard < nShards; shard++ {
899 id := fmt.Sprintf("%d_%d", shard, nShards)
900 t.registerTest("../test",
901 &goTest{
902 variant: id,
903 omitVariant: true,
904 pkg: "cmd/internal/testdir",
905 testFlags: []string{fmt.Sprintf("-shard=%d", shard), fmt.Sprintf("-shards=%d", nShards)},
906 runOnHost: true,
907 },
908 )
909 }
910 }
911 }
912
913
914
915
916 func (t *tester) addTest(name, heading string, fn func(*distTest) error) {
917 if t.testNames[name] {
918 panic("duplicate registered test name " + name)
919 }
920 if heading == "" {
921 panic("empty heading")
922 }
923
924 if !strings.Contains(name, ":") && heading != "Testing packages." {
925 panic("empty variant is reserved exclusively for registerStdTest")
926 } else if strings.HasSuffix(name, ":racebench") && heading != "Running benchmarks briefly." {
927 panic("racebench variant is reserved exclusively for registerRaceBenchTest")
928 }
929 if t.testNames == nil {
930 t.testNames = make(map[string]bool)
931 }
932 t.testNames[name] = true
933 t.tests = append(t.tests, distTest{
934 name: name,
935 heading: heading,
936 fn: fn,
937 })
938 }
939
940 type registerTestOpt interface {
941 isRegisterTestOpt()
942 }
943
944
945
946 type rtSkipFunc struct {
947 skip func(*distTest) (string, bool)
948 }
949
950 func (rtSkipFunc) isRegisterTestOpt() {}
951
952
953
954
955
956
957
958 func (t *tester) registerTest(heading string, test *goTest, opts ...registerTestOpt) {
959 var skipFunc func(*distTest) (string, bool)
960 for _, opt := range opts {
961 switch opt := opt.(type) {
962 case rtSkipFunc:
963 skipFunc = opt.skip
964 }
965 }
966
967 register1 := func(test *goTest) {
968 if test.variant == "" {
969 panic("empty variant")
970 }
971 name := testName(test.pkg, test.variant)
972 t.addTest(name, heading, func(dt *distTest) error {
973 if skipFunc != nil {
974 msg, skip := skipFunc(dt)
975 if skip {
976 test.printSkip(t, msg)
977 return nil
978 }
979 }
980 w := &work{dt: dt}
981 w.cmd, w.flush = test.bgCommand(t, &w.out, &w.out)
982 t.worklist = append(t.worklist, w)
983 return nil
984 })
985 }
986 if test.pkg != "" && len(test.pkgs) == 0 {
987
988 register1(test)
989 return
990 }
991
992
993
994
995
996
997 for _, pkg := range test.packages() {
998 test1 := *test
999 test1.pkg, test1.pkgs = pkg, nil
1000 register1(&test1)
1001 }
1002 }
1003
1004
1005
1006
1007 func (t *tester) dirCmd(dir string, cmdline ...interface{}) *exec.Cmd {
1008 bin, args := flattenCmdline(cmdline)
1009 cmd := exec.Command(bin, args...)
1010 if filepath.IsAbs(dir) {
1011 setDir(cmd, dir)
1012 } else {
1013 setDir(cmd, filepath.Join(goroot, dir))
1014 }
1015 cmd.Stdout = os.Stdout
1016 cmd.Stderr = os.Stderr
1017 if vflag > 1 {
1018 errprintf("%s\n", strings.Join(cmd.Args, " "))
1019 }
1020 return cmd
1021 }
1022
1023
1024
1025 func flattenCmdline(cmdline []interface{}) (bin string, args []string) {
1026 var list []string
1027 for _, x := range cmdline {
1028 switch x := x.(type) {
1029 case string:
1030 list = append(list, x)
1031 case []string:
1032 list = append(list, x...)
1033 default:
1034 panic("invalid dirCmd argument type: " + reflect.TypeOf(x).String())
1035 }
1036 }
1037
1038 bin = list[0]
1039 if !filepath.IsAbs(bin) {
1040 panic("command is not absolute: " + bin)
1041 }
1042 return bin, list[1:]
1043 }
1044
1045 func (t *tester) iOS() bool {
1046 return goos == "ios"
1047 }
1048
1049 func (t *tester) out(v string) {
1050 if t.json {
1051 return
1052 }
1053 if t.banner == "" {
1054 return
1055 }
1056 fmt.Println("\n" + t.banner + v)
1057 }
1058
1059
1060
1061
1062 func (t *tester) extLink() bool {
1063 if goarch == "ppc64" && goos != "aix" {
1064 return false
1065 }
1066 return true
1067 }
1068
1069 func (t *tester) internalLink() bool {
1070 if gohostos == "dragonfly" {
1071
1072 return false
1073 }
1074 if goos == "android" {
1075 return false
1076 }
1077 if goos == "ios" {
1078 return false
1079 }
1080 if goos == "windows" && goarch == "arm64" {
1081 return false
1082 }
1083
1084
1085
1086 if goarch == "loong64" || goarch == "mips64" || goarch == "mips64le" || goarch == "mips" || goarch == "mipsle" || goarch == "riscv64" {
1087 return false
1088 }
1089 if goos == "aix" {
1090
1091 return false
1092 }
1093 return true
1094 }
1095
1096 func (t *tester) internalLinkPIE() bool {
1097 switch goos + "-" + goarch {
1098 case "darwin-amd64", "darwin-arm64",
1099 "linux-amd64", "linux-arm64", "linux-ppc64le",
1100 "android-arm64",
1101 "windows-amd64", "windows-386", "windows-arm":
1102 return true
1103 }
1104 return false
1105 }
1106
1107
1108 func (t *tester) supportedBuildmode(mode string) bool {
1109 switch mode {
1110 case "c-archive", "c-shared", "shared", "plugin", "pie":
1111 default:
1112 fatalf("internal error: unknown buildmode %s", mode)
1113 return false
1114 }
1115
1116 return buildModeSupported("gc", mode, goos, goarch)
1117 }
1118
1119 func (t *tester) registerCgoTests(heading string) {
1120 cgoTest := func(variant string, subdir, linkmode, buildmode string, opts ...registerTestOpt) *goTest {
1121 gt := &goTest{
1122 variant: variant,
1123 pkg: "cmd/cgo/internal/" + subdir,
1124 buildmode: buildmode,
1125 }
1126 var ldflags []string
1127 if linkmode != "auto" {
1128
1129 ldflags = append(ldflags, "-linkmode="+linkmode)
1130 }
1131
1132 if linkmode == "internal" {
1133 gt.tags = append(gt.tags, "internal")
1134 if buildmode == "pie" {
1135 gt.tags = append(gt.tags, "internal_pie")
1136 }
1137 }
1138 if buildmode == "static" {
1139
1140
1141 gt.buildmode = ""
1142 if linkmode == "external" {
1143 ldflags = append(ldflags, `-extldflags "-static -pthread"`)
1144 } else if linkmode == "auto" {
1145 gt.env = append(gt.env, "CGO_LDFLAGS=-static -pthread")
1146 } else {
1147 panic("unknown linkmode with static build: " + linkmode)
1148 }
1149 gt.tags = append(gt.tags, "static")
1150 }
1151 gt.ldflags = strings.Join(ldflags, " ")
1152
1153 t.registerTest(heading, gt, opts...)
1154 return gt
1155 }
1156
1157
1158
1159
1160
1161
1162 builderName := os.Getenv("GO_BUILDER_NAME")
1163 disablePIE := strings.HasSuffix(builderName, "-alpine")
1164
1165 if t.internalLink() {
1166 cgoTest("internal", "test", "internal", "")
1167 }
1168
1169 os := gohostos
1170 p := gohostos + "/" + goarch
1171 switch {
1172 case os == "darwin", os == "windows":
1173 if !t.extLink() {
1174 break
1175 }
1176
1177 cgoTest("external", "test", "external", "")
1178
1179 gt := cgoTest("external-s", "test", "external", "")
1180 gt.ldflags += " -s"
1181
1182 if t.supportedBuildmode("pie") && !disablePIE {
1183 cgoTest("auto-pie", "test", "auto", "pie")
1184 if t.internalLink() && t.internalLinkPIE() {
1185 cgoTest("internal-pie", "test", "internal", "pie")
1186 }
1187 }
1188
1189 case os == "aix", os == "android", os == "dragonfly", os == "freebsd", os == "linux", os == "netbsd", os == "openbsd":
1190 gt := cgoTest("external-g0", "test", "external", "")
1191 gt.env = append(gt.env, "CGO_CFLAGS=-g0 -fdiagnostics-color")
1192
1193 cgoTest("external", "testtls", "external", "")
1194 switch {
1195 case os == "aix":
1196
1197 case p == "freebsd/arm":
1198
1199
1200
1201
1202
1203 default:
1204
1205 var staticCheck rtSkipFunc
1206 ccName := compilerEnvLookup("CC", defaultcc, goos, goarch)
1207 cc, err := exec.LookPath(ccName)
1208 if err != nil {
1209 staticCheck.skip = func(*distTest) (string, bool) {
1210 return fmt.Sprintf("$CC (%q) not found, skip cgo static linking test.", ccName), true
1211 }
1212 } else {
1213 cmd := t.dirCmd("src/cmd/cgo/internal/test", cc, "-xc", "-o", "/dev/null", "-static", "-")
1214 cmd.Stdin = strings.NewReader("int main() {}")
1215 cmd.Stdout, cmd.Stderr = nil, nil
1216 if err := cmd.Run(); err != nil {
1217
1218 staticCheck.skip = func(*distTest) (string, bool) {
1219 return "No support for static linking found (lacks libc.a?), skip cgo static linking test.", true
1220 }
1221 }
1222 }
1223
1224
1225
1226
1227
1228 if staticCheck.skip == nil && goos == "linux" && strings.Contains(goexperiment, "boringcrypto") {
1229 staticCheck.skip = func(*distTest) (string, bool) {
1230 return "skipping static linking check on Linux when using boringcrypto to avoid C linker warning about getaddrinfo", true
1231 }
1232 }
1233
1234
1235 if goos != "android" && p != "netbsd/arm" {
1236
1237 cgoTest("static", "testtls", "external", "static", staticCheck)
1238 }
1239 cgoTest("external", "testnocgo", "external", "", staticCheck)
1240 if goos != "android" {
1241 cgoTest("static", "testnocgo", "external", "static", staticCheck)
1242 cgoTest("static", "test", "external", "static", staticCheck)
1243
1244
1245
1246 if goarch != "loong64" {
1247
1248 cgoTest("auto-static", "test", "auto", "static", staticCheck)
1249 }
1250 }
1251
1252
1253 if t.supportedBuildmode("pie") && !disablePIE {
1254 cgoTest("auto-pie", "test", "auto", "pie")
1255 if t.internalLink() && t.internalLinkPIE() {
1256 cgoTest("internal-pie", "test", "internal", "pie")
1257 }
1258 cgoTest("auto-pie", "testtls", "auto", "pie")
1259 cgoTest("auto-pie", "testnocgo", "auto", "pie")
1260 }
1261 }
1262 }
1263 }
1264
1265
1266
1267
1268
1269
1270
1271 func (t *tester) runPending(nextTest *distTest) {
1272 worklist := t.worklist
1273 t.worklist = nil
1274 for _, w := range worklist {
1275 w.start = make(chan bool)
1276 w.end = make(chan struct{})
1277
1278
1279 if w.cmd.Stdout == nil || w.cmd.Stdout == os.Stdout || w.cmd.Stderr == nil || w.cmd.Stderr == os.Stderr {
1280 panic("work.cmd.Stdout/Stderr must be redirected")
1281 }
1282 go func(w *work) {
1283 if !<-w.start {
1284 timelog("skip", w.dt.name)
1285 w.printSkip(t, "skipped due to earlier error")
1286 } else {
1287 timelog("start", w.dt.name)
1288 w.err = w.cmd.Run()
1289 if w.flush != nil {
1290 w.flush()
1291 }
1292 if w.err != nil {
1293 if isUnsupportedVMASize(w) {
1294 timelog("skip", w.dt.name)
1295 w.out.Reset()
1296 w.printSkip(t, "skipped due to unsupported VMA")
1297 w.err = nil
1298 }
1299 }
1300 }
1301 timelog("end", w.dt.name)
1302 w.end <- struct{}{}
1303 }(w)
1304 }
1305
1306 maxbg := maxbg
1307
1308
1309 if runtime.NumCPU() > 4 && runtime.GOMAXPROCS(0) != 1 {
1310 for _, w := range worklist {
1311
1312
1313
1314
1315
1316 if strings.Contains(w.dt.heading, "GOMAXPROCS=2 runtime") {
1317 maxbg = runtime.NumCPU()
1318 break
1319 }
1320 }
1321 }
1322
1323 started := 0
1324 ended := 0
1325 var last *distTest
1326 for ended < len(worklist) {
1327 for started < len(worklist) && started-ended < maxbg {
1328 w := worklist[started]
1329 started++
1330 w.start <- !t.failed || t.keepGoing
1331 }
1332 w := worklist[ended]
1333 dt := w.dt
1334 if t.lastHeading != dt.heading {
1335 t.lastHeading = dt.heading
1336 t.out(dt.heading)
1337 }
1338 if dt != last {
1339
1340 last = w.dt
1341 if vflag > 0 {
1342 fmt.Printf("# go tool dist test -run=^%s$\n", dt.name)
1343 }
1344 }
1345 if vflag > 1 {
1346 errprintf("%s\n", strings.Join(w.cmd.Args, " "))
1347 }
1348 ended++
1349 <-w.end
1350 os.Stdout.Write(w.out.Bytes())
1351
1352 w.out = bytes.Buffer{}
1353 if w.err != nil {
1354 log.Printf("Failed: %v", w.err)
1355 t.failed = true
1356 }
1357 }
1358 if t.failed && !t.keepGoing {
1359 fatalf("FAILED")
1360 }
1361
1362 if dt := nextTest; dt != nil {
1363 if t.lastHeading != dt.heading {
1364 t.lastHeading = dt.heading
1365 t.out(dt.heading)
1366 }
1367 if vflag > 0 {
1368 fmt.Printf("# go tool dist test -run=^%s$\n", dt.name)
1369 }
1370 }
1371 }
1372
1373 func (t *tester) hasBash() bool {
1374 switch gohostos {
1375 case "windows", "plan9":
1376 return false
1377 }
1378 return true
1379 }
1380
1381
1382
1383
1384 func (t *tester) hasParallelism() bool {
1385 switch goos {
1386 case "js", "wasip1":
1387 return false
1388 }
1389 return true
1390 }
1391
1392 func (t *tester) raceDetectorSupported() bool {
1393 if gohostos != goos {
1394 return false
1395 }
1396 if !t.cgoEnabled {
1397 return false
1398 }
1399 if !raceDetectorSupported(goos, goarch) {
1400 return false
1401 }
1402
1403
1404 if isAlpineLinux() {
1405 return false
1406 }
1407
1408
1409 if goos == "netbsd" {
1410 return false
1411 }
1412 return true
1413 }
1414
1415 func isAlpineLinux() bool {
1416 if runtime.GOOS != "linux" {
1417 return false
1418 }
1419 fi, err := os.Lstat("/etc/alpine-release")
1420 return err == nil && fi.Mode().IsRegular()
1421 }
1422
1423 func (t *tester) registerRaceTests() {
1424 hdr := "Testing race detector"
1425 t.registerTest(hdr,
1426 &goTest{
1427 variant: "race",
1428 race: true,
1429 runTests: "Output",
1430 pkg: "runtime/race",
1431 })
1432 t.registerTest(hdr,
1433 &goTest{
1434 variant: "race",
1435 race: true,
1436 runTests: "TestParse|TestEcho|TestStdinCloseRace|TestClosedPipeRace|TestTypeRace|TestFdRace|TestFdReadRace|TestFileCloseRace",
1437 pkgs: []string{"flag", "net", "os", "os/exec", "encoding/gob"},
1438 })
1439
1440
1441
1442
1443
1444 if t.cgoEnabled {
1445
1446
1447
1448
1449
1450 }
1451 if t.extLink() {
1452
1453 t.registerTest(hdr,
1454 &goTest{
1455 variant: "race-external",
1456 race: true,
1457 ldflags: "-linkmode=external",
1458 runTests: "TestParse|TestEcho|TestStdinCloseRace",
1459 pkgs: []string{"flag", "os/exec"},
1460 })
1461 }
1462 }
1463
1464
1465 var cgoPackages = []string{
1466 "net",
1467 "os/user",
1468 }
1469
1470 var funcBenchmark = []byte("\nfunc Benchmark")
1471
1472
1473
1474
1475
1476
1477
1478
1479 func (t *tester) packageHasBenchmarks(pkg string) bool {
1480 pkgDir := filepath.Join(goroot, "src", pkg)
1481 d, err := os.Open(pkgDir)
1482 if err != nil {
1483 return true
1484 }
1485 defer d.Close()
1486 names, err := d.Readdirnames(-1)
1487 if err != nil {
1488 return true
1489 }
1490 for _, name := range names {
1491 if !strings.HasSuffix(name, "_test.go") {
1492 continue
1493 }
1494 slurp, err := os.ReadFile(filepath.Join(pkgDir, name))
1495 if err != nil {
1496 return true
1497 }
1498 if bytes.Contains(slurp, funcBenchmark) {
1499 return true
1500 }
1501 }
1502 return false
1503 }
1504
1505
1506
1507 func (t *tester) makeGOROOTUnwritable() (undo func()) {
1508 dir := os.Getenv("GOROOT")
1509 if dir == "" {
1510 panic("GOROOT not set")
1511 }
1512
1513 type pathMode struct {
1514 path string
1515 mode os.FileMode
1516 }
1517 var dirs []pathMode
1518
1519 undo = func() {
1520 for i := range dirs {
1521 os.Chmod(dirs[i].path, dirs[i].mode)
1522 }
1523 }
1524
1525 filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
1526 if suffix := strings.TrimPrefix(path, dir+string(filepath.Separator)); suffix != "" {
1527 if suffix == ".git" {
1528
1529
1530
1531 return filepath.SkipDir
1532 }
1533 }
1534 if err != nil {
1535 return nil
1536 }
1537
1538 info, err := d.Info()
1539 if err != nil {
1540 return nil
1541 }
1542
1543 mode := info.Mode()
1544 if mode&0222 != 0 && (mode.IsDir() || mode.IsRegular()) {
1545 dirs = append(dirs, pathMode{path, mode})
1546 }
1547 return nil
1548 })
1549
1550
1551 for i := len(dirs) - 1; i >= 0; i-- {
1552 err := os.Chmod(dirs[i].path, dirs[i].mode&^0222)
1553 if err != nil {
1554 dirs = dirs[i:]
1555 undo()
1556 fatalf("failed to make GOROOT read-only: %v", err)
1557 }
1558 }
1559
1560 return undo
1561 }
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571 func raceDetectorSupported(goos, goarch string) bool {
1572 switch goos {
1573 case "linux":
1574 return goarch == "amd64" || goarch == "ppc64le" || goarch == "arm64" || goarch == "s390x"
1575 case "darwin":
1576 return goarch == "amd64" || goarch == "arm64"
1577 case "freebsd", "netbsd", "windows":
1578 return goarch == "amd64"
1579 default:
1580 return false
1581 }
1582 }
1583
1584
1585
1586
1587 func buildModeSupported(compiler, buildmode, goos, goarch string) bool {
1588 if compiler == "gccgo" {
1589 return true
1590 }
1591
1592 platform := goos + "/" + goarch
1593
1594 switch buildmode {
1595 case "archive":
1596 return true
1597
1598 case "c-archive":
1599 switch goos {
1600 case "aix", "darwin", "ios", "windows":
1601 return true
1602 case "linux":
1603 switch goarch {
1604 case "386", "amd64", "arm", "armbe", "arm64", "arm64be", "loong64", "ppc64le", "riscv64", "s390x":
1605
1606
1607 return true
1608 default:
1609
1610
1611
1612
1613
1614
1615 return false
1616 }
1617 case "freebsd":
1618 return goarch == "amd64"
1619 }
1620 return false
1621
1622 case "c-shared":
1623 switch platform {
1624 case "linux/amd64", "linux/arm", "linux/arm64", "linux/loong64", "linux/386", "linux/ppc64le", "linux/riscv64", "linux/s390x",
1625 "android/amd64", "android/arm", "android/arm64", "android/386",
1626 "freebsd/amd64",
1627 "darwin/amd64", "darwin/arm64",
1628 "windows/amd64", "windows/386", "windows/arm64":
1629 return true
1630 }
1631 return false
1632
1633 case "default":
1634 return true
1635
1636 case "exe":
1637 return true
1638
1639 case "pie":
1640 switch platform {
1641 case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/loong64", "linux/ppc64le", "linux/riscv64", "linux/s390x",
1642 "android/amd64", "android/arm", "android/arm64", "android/386",
1643 "freebsd/amd64",
1644 "darwin/amd64", "darwin/arm64",
1645 "ios/amd64", "ios/arm64",
1646 "aix/ppc64",
1647 "openbsd/arm64",
1648 "windows/386", "windows/amd64", "windows/arm", "windows/arm64":
1649 return true
1650 }
1651 return false
1652
1653 case "shared":
1654 switch platform {
1655 case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/s390x":
1656 return true
1657 }
1658 return false
1659
1660 case "plugin":
1661 switch platform {
1662 case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/loong64", "linux/s390x", "linux/ppc64le",
1663 "android/amd64", "android/386",
1664 "darwin/amd64", "darwin/arm64",
1665 "freebsd/amd64":
1666 return true
1667 }
1668 return false
1669
1670 default:
1671 return false
1672 }
1673 }
1674
1675
1676
1677
1678 func isUnsupportedVMASize(w *work) bool {
1679 unsupportedVMA := []byte("unsupported VMA range")
1680 return strings.Contains(w.dt.name, ":race") && bytes.Contains(w.out.Bytes(), unsupportedVMA)
1681 }
1682
1683
1684
1685 func isEnvSet(evar string) bool {
1686 evarEq := evar + "="
1687 for _, e := range os.Environ() {
1688 if strings.HasPrefix(e, evarEq) {
1689 return true
1690 }
1691 }
1692 return false
1693 }
1694
View as plain text