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