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