Source file
test/run.go
Documentation: test
1
2
3
4
5
6
7
8 package main
9
10 import (
11 "bytes"
12 "errors"
13 "flag"
14 "fmt"
15 "go/build"
16 "hash/fnv"
17 "io"
18 "io/fs"
19 "io/ioutil"
20 "log"
21 "os"
22 "os/exec"
23 "path"
24 "path/filepath"
25 "regexp"
26 "runtime"
27 "sort"
28 "strconv"
29 "strings"
30 "time"
31 "unicode"
32 )
33
34 var (
35 verbose = flag.Bool("v", false, "verbose. if set, parallelism is set to 1.")
36 keep = flag.Bool("k", false, "keep. keep temporary directory.")
37 numParallel = flag.Int("n", runtime.NumCPU(), "number of parallel tests to run")
38 summary = flag.Bool("summary", false, "show summary of results")
39 allCodegen = flag.Bool("all_codegen", defaultAllCodeGen(), "run all goos/goarch for codegen")
40 showSkips = flag.Bool("show_skips", false, "show skipped tests")
41 runSkips = flag.Bool("run_skips", false, "run skipped tests (ignore skip and build tags)")
42 linkshared = flag.Bool("linkshared", false, "")
43 updateErrors = flag.Bool("update_errors", false, "update error messages in test file based on compiler output")
44 runoutputLimit = flag.Int("l", defaultRunOutputLimit(), "number of parallel runoutput tests to run")
45
46 shard = flag.Int("shard", 0, "shard index to run. Only applicable if -shards is non-zero.")
47 shards = flag.Int("shards", 0, "number of shards. If 0, all tests are run. This is used by the continuous build.")
48 )
49
50
51
52
53
54 func defaultAllCodeGen() bool {
55 return os.Getenv("GO_BUILDER_NAME") == "linux-amd64"
56 }
57
58 var (
59 goos, goarch string
60 cgoEnabled bool
61
62
63
64 dirs = []string{".", "ken", "chan", "interface", "syntax", "dwarf", "fixedbugs", "codegen", "runtime", "abi", "typeparam"}
65
66
67 ratec chan bool
68
69
70
71 toRun chan *test
72
73
74
75 rungatec chan bool
76 )
77
78
79
80 const maxTests = 5000
81
82 func main() {
83 flag.Parse()
84
85 goos = getenv("GOOS", runtime.GOOS)
86 goarch = getenv("GOARCH", runtime.GOARCH)
87 cgoEnv, err := exec.Command(goTool(), "env", "CGO_ENABLED").Output()
88 if err == nil {
89 cgoEnabled, _ = strconv.ParseBool(strings.TrimSpace(string(cgoEnv)))
90 }
91
92 findExecCmd()
93
94
95 if *verbose || len(findExecCmd()) > 0 {
96 *numParallel = 1
97 *runoutputLimit = 1
98 }
99
100 ratec = make(chan bool, *numParallel)
101 rungatec = make(chan bool, *runoutputLimit)
102
103 var tests []*test
104 if flag.NArg() > 0 {
105 for _, arg := range flag.Args() {
106 if arg == "-" || arg == "--" {
107
108
109
110
111
112 continue
113 }
114 if fi, err := os.Stat(arg); err == nil && fi.IsDir() {
115 for _, baseGoFile := range goFiles(arg) {
116 tests = append(tests, startTest(arg, baseGoFile))
117 }
118 } else if strings.HasSuffix(arg, ".go") {
119 dir, file := filepath.Split(arg)
120 tests = append(tests, startTest(dir, file))
121 } else {
122 log.Fatalf("can't yet deal with non-directory and non-go file %q", arg)
123 }
124 }
125 } else {
126 for _, dir := range dirs {
127 for _, baseGoFile := range goFiles(dir) {
128 tests = append(tests, startTest(dir, baseGoFile))
129 }
130 }
131 }
132
133 failed := false
134 resCount := map[string]int{}
135 for _, test := range tests {
136 <-test.donec
137 status := "ok "
138 errStr := ""
139 if e, isSkip := test.err.(skipError); isSkip {
140 test.err = nil
141 errStr = "unexpected skip for " + path.Join(test.dir, test.gofile) + ": " + string(e)
142 status = "FAIL"
143 }
144 if test.err != nil {
145 status = "FAIL"
146 errStr = test.err.Error()
147 }
148 if status == "FAIL" {
149 failed = true
150 }
151 resCount[status]++
152 dt := fmt.Sprintf("%.3fs", test.dt.Seconds())
153 if status == "FAIL" {
154 fmt.Printf("# go run run.go -- %s\n%s\nFAIL\t%s\t%s\n",
155 path.Join(test.dir, test.gofile),
156 errStr, test.goFileName(), dt)
157 continue
158 }
159 if !*verbose {
160 continue
161 }
162 fmt.Printf("%s\t%s\t%s\n", status, test.goFileName(), dt)
163 }
164
165 if *summary {
166 for k, v := range resCount {
167 fmt.Printf("%5d %s\n", v, k)
168 }
169 }
170
171 if failed {
172 os.Exit(1)
173 }
174 }
175
176
177
178
179 func goTool() string {
180 var exeSuffix string
181 if runtime.GOOS == "windows" {
182 exeSuffix = ".exe"
183 }
184 path := filepath.Join(runtime.GOROOT(), "bin", "go"+exeSuffix)
185 if _, err := os.Stat(path); err == nil {
186 return path
187 }
188
189 return "go"
190 }
191
192 func shardMatch(name string) bool {
193 if *shards == 0 {
194 return true
195 }
196 h := fnv.New32()
197 io.WriteString(h, name)
198 return int(h.Sum32()%uint32(*shards)) == *shard
199 }
200
201 func goFiles(dir string) []string {
202 f, err := os.Open(dir)
203 if err != nil {
204 log.Fatal(err)
205 }
206 dirnames, err := f.Readdirnames(-1)
207 f.Close()
208 if err != nil {
209 log.Fatal(err)
210 }
211 names := []string{}
212 for _, name := range dirnames {
213 if !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") && shardMatch(name) {
214 names = append(names, name)
215 }
216 }
217 sort.Strings(names)
218 return names
219 }
220
221 type runCmd func(...string) ([]byte, error)
222
223 func compileFile(runcmd runCmd, longname string, flags []string) (out []byte, err error) {
224 cmd := []string{goTool(), "tool", "compile", "-e"}
225 cmd = append(cmd, flags...)
226 if *linkshared {
227 cmd = append(cmd, "-dynlink", "-installsuffix=dynlink")
228 }
229 cmd = append(cmd, longname)
230 return runcmd(cmd...)
231 }
232
233 func compileInDir(runcmd runCmd, dir string, flags []string, localImports bool, names ...string) (out []byte, err error) {
234 cmd := []string{goTool(), "tool", "compile", "-e"}
235 if localImports {
236
237 cmd = append(cmd, "-D", ".", "-I", ".")
238 }
239 cmd = append(cmd, flags...)
240 if *linkshared {
241 cmd = append(cmd, "-dynlink", "-installsuffix=dynlink")
242 }
243 for _, name := range names {
244 cmd = append(cmd, filepath.Join(dir, name))
245 }
246 return runcmd(cmd...)
247 }
248
249 func linkFile(runcmd runCmd, goname string, ldflags []string) (err error) {
250 pfile := strings.Replace(goname, ".go", ".o", -1)
251 cmd := []string{goTool(), "tool", "link", "-w", "-o", "a.exe", "-L", "."}
252 if *linkshared {
253 cmd = append(cmd, "-linkshared", "-installsuffix=dynlink")
254 }
255 if ldflags != nil {
256 cmd = append(cmd, ldflags...)
257 }
258 cmd = append(cmd, pfile)
259 _, err = runcmd(cmd...)
260 return
261 }
262
263
264 type skipError string
265
266 func (s skipError) Error() string { return string(s) }
267
268
269 type test struct {
270 dir, gofile string
271 donec chan bool
272 dt time.Duration
273
274 src string
275
276 tempDir string
277 err error
278 }
279
280
281 func startTest(dir, gofile string) *test {
282 t := &test{
283 dir: dir,
284 gofile: gofile,
285 donec: make(chan bool, 1),
286 }
287 if toRun == nil {
288 toRun = make(chan *test, maxTests)
289 go runTests()
290 }
291 select {
292 case toRun <- t:
293 default:
294 panic("toRun buffer size (maxTests) is too small")
295 }
296 return t
297 }
298
299
300
301 func runTests() {
302 for {
303 ratec <- true
304 t := <-toRun
305 go func() {
306 t.run()
307 <-ratec
308 }()
309 }
310 }
311
312 var cwd, _ = os.Getwd()
313
314 func (t *test) goFileName() string {
315 return filepath.Join(t.dir, t.gofile)
316 }
317
318 func (t *test) goDirName() string {
319 return filepath.Join(t.dir, strings.Replace(t.gofile, ".go", ".dir", -1))
320 }
321
322 func goDirFiles(longdir string) (filter []os.FileInfo, err error) {
323 files, dirErr := ioutil.ReadDir(longdir)
324 if dirErr != nil {
325 return nil, dirErr
326 }
327 for _, gofile := range files {
328 if filepath.Ext(gofile.Name()) == ".go" {
329 filter = append(filter, gofile)
330 }
331 }
332 return
333 }
334
335 var packageRE = regexp.MustCompile(`(?m)^package ([\p{Lu}\p{Ll}\w]+)`)
336
337 func getPackageNameFromSource(fn string) (string, error) {
338 data, err := ioutil.ReadFile(fn)
339 if err != nil {
340 return "", err
341 }
342 pkgname := packageRE.FindStringSubmatch(string(data))
343 if pkgname == nil {
344 return "", fmt.Errorf("cannot find package name in %s", fn)
345 }
346 return pkgname[1], nil
347 }
348
349
350
351 func goDirPackages(longdir string, singlefilepkgs bool) ([][]string, error) {
352 files, err := goDirFiles(longdir)
353 if err != nil {
354 return nil, err
355 }
356 var pkgs [][]string
357 m := make(map[string]int)
358 for _, file := range files {
359 name := file.Name()
360 pkgname, err := getPackageNameFromSource(filepath.Join(longdir, name))
361 if err != nil {
362 log.Fatal(err)
363 }
364 i, ok := m[pkgname]
365 if singlefilepkgs || !ok {
366 i = len(pkgs)
367 pkgs = append(pkgs, nil)
368 m[pkgname] = i
369 }
370 pkgs[i] = append(pkgs[i], name)
371 }
372 return pkgs, nil
373 }
374
375 type context struct {
376 GOOS string
377 GOARCH string
378 cgoEnabled bool
379 noOptEnv bool
380 }
381
382
383
384 func shouldTest(src string, goos, goarch string) (ok bool, whyNot string) {
385 if *runSkips {
386 return true, ""
387 }
388 for _, line := range strings.Split(src, "\n") {
389 line = strings.TrimSpace(line)
390 if strings.HasPrefix(line, "//") {
391 line = line[2:]
392 } else {
393 continue
394 }
395 line = strings.TrimSpace(line)
396 if len(line) == 0 || line[0] != '+' {
397 continue
398 }
399 gcFlags := os.Getenv("GO_GCFLAGS")
400 ctxt := &context{
401 GOOS: goos,
402 GOARCH: goarch,
403 cgoEnabled: cgoEnabled,
404 noOptEnv: strings.Contains(gcFlags, "-N") || strings.Contains(gcFlags, "-l"),
405 }
406
407 words := strings.Fields(line)
408 if words[0] == "+build" {
409 ok := false
410 for _, word := range words[1:] {
411 if ctxt.match(word) {
412 ok = true
413 break
414 }
415 }
416 if !ok {
417
418 return false, line
419 }
420 }
421 }
422
423 return true, ""
424 }
425
426 func (ctxt *context) match(name string) bool {
427 if name == "" {
428 return false
429 }
430 if i := strings.Index(name, ","); i >= 0 {
431
432 return ctxt.match(name[:i]) && ctxt.match(name[i+1:])
433 }
434 if strings.HasPrefix(name, "!!") {
435 return false
436 }
437 if strings.HasPrefix(name, "!") {
438 return len(name) > 1 && !ctxt.match(name[1:])
439 }
440
441
442
443 for _, c := range name {
444 if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' {
445 return false
446 }
447 }
448
449 if strings.HasPrefix(name, "goexperiment.") {
450 for _, tag := range build.Default.ToolTags {
451 if tag == name {
452 return true
453 }
454 }
455 return false
456 }
457
458 if name == "cgo" && ctxt.cgoEnabled {
459 return true
460 }
461
462 if name == ctxt.GOOS || name == ctxt.GOARCH || name == "gc" {
463 return true
464 }
465
466 if ctxt.noOptEnv && name == "gcflags_noopt" {
467 return true
468 }
469
470 if name == "test_run" {
471 return true
472 }
473
474 return false
475 }
476
477 func init() { checkShouldTest() }
478
479
480
481
482
483 func goGcflags() string {
484 return "-gcflags=all=" + os.Getenv("GO_GCFLAGS")
485 }
486
487 func goGcflagsIsEmpty() bool {
488 return "" == os.Getenv("GO_GCFLAGS")
489 }
490
491 var errTimeout = errors.New("command exceeded time limit")
492
493
494 func (t *test) run() {
495 start := time.Now()
496 defer func() {
497 t.dt = time.Since(start)
498 close(t.donec)
499 }()
500
501 srcBytes, err := ioutil.ReadFile(t.goFileName())
502 if err != nil {
503 t.err = err
504 return
505 }
506 t.src = string(srcBytes)
507 if t.src[0] == '\n' {
508 t.err = skipError("starts with newline")
509 return
510 }
511
512
513 pos := strings.Index(t.src, "\n\n")
514 if pos == -1 {
515 t.err = fmt.Errorf("double newline ending execution recipe not found in %s", t.goFileName())
516 return
517 }
518 action := t.src[:pos]
519 if nl := strings.Index(action, "\n"); nl >= 0 && strings.Contains(action[:nl], "+build") {
520
521 action = action[nl+1:]
522 }
523 action = strings.TrimPrefix(action, "//")
524
525
526 pkgPos := strings.Index(t.src, "\npackage")
527 if pkgPos == -1 {
528 pkgPos = pos
529 }
530 if ok, why := shouldTest(t.src[:pkgPos], goos, goarch); !ok {
531 if *showSkips {
532 fmt.Printf("%-20s %-20s: %s\n", "skip", t.goFileName(), why)
533 }
534 return
535 }
536
537 var args, flags, runenv []string
538 var tim int
539 wantError := false
540 wantAuto := false
541 singlefilepkgs := false
542 setpkgpaths := false
543 localImports := true
544 f := strings.Fields(action)
545 if len(f) > 0 {
546 action = f[0]
547 args = f[1:]
548 }
549
550
551 switch action {
552 case "compile", "compiledir", "build", "builddir", "buildrundir", "run", "buildrun", "runoutput", "rundir", "runindir", "asmcheck":
553
554 case "errorcheckandrundir":
555 wantError = false
556 case "errorcheckwithauto":
557 action = "errorcheck"
558 wantAuto = true
559 wantError = true
560 case "errorcheck", "errorcheckdir", "errorcheckoutput":
561 wantError = true
562 case "skip":
563 if *runSkips {
564 break
565 }
566 return
567 default:
568 t.err = skipError("skipped; unknown pattern: " + action)
569 return
570 }
571
572
573 for len(args) > 0 && strings.HasPrefix(args[0], "-") {
574 switch args[0] {
575 case "-1":
576 wantError = true
577 case "-0":
578 wantError = false
579 case "-s":
580 singlefilepkgs = true
581 case "-P":
582 setpkgpaths = true
583 case "-n":
584
585
586
587
588 localImports = false
589 case "-t":
590 args = args[1:]
591 var err error
592 tim, err = strconv.Atoi(args[0])
593 if err != nil {
594 t.err = fmt.Errorf("need number of seconds for -t timeout, got %s instead", args[0])
595 }
596 case "-goexperiment":
597 args = args[1:]
598 runenv = append(runenv, "GOEXPERIMENT="+args[0])
599
600 default:
601 flags = append(flags, args[0])
602 }
603 args = args[1:]
604 }
605 if action == "errorcheck" {
606 found := false
607 for i, f := range flags {
608 if strings.HasPrefix(f, "-d=") {
609 flags[i] = f + ",ssa/check/on"
610 found = true
611 break
612 }
613 }
614 if !found {
615 flags = append(flags, "-d=ssa/check/on")
616 }
617 }
618
619 t.makeTempDir()
620 if !*keep {
621 defer os.RemoveAll(t.tempDir)
622 }
623
624 err = ioutil.WriteFile(filepath.Join(t.tempDir, t.gofile), srcBytes, 0644)
625 if err != nil {
626 log.Fatal(err)
627 }
628
629
630 if os.Getenv("GOOS") == "" {
631 os.Setenv("GOOS", runtime.GOOS)
632 }
633 if os.Getenv("GOARCH") == "" {
634 os.Setenv("GOARCH", runtime.GOARCH)
635 }
636
637 var (
638 runInDir = t.tempDir
639 tempDirIsGOPATH = false
640 )
641 runcmd := func(args ...string) ([]byte, error) {
642 cmd := exec.Command(args[0], args[1:]...)
643 var buf bytes.Buffer
644 cmd.Stdout = &buf
645 cmd.Stderr = &buf
646 cmd.Env = append(os.Environ(), "GOENV=off", "GOFLAGS=")
647 if runInDir != "" {
648 cmd.Dir = runInDir
649
650 cmd.Env = append(cmd.Env, "PWD="+cmd.Dir)
651 }
652 if tempDirIsGOPATH {
653 cmd.Env = append(cmd.Env, "GOPATH="+t.tempDir)
654 }
655 cmd.Env = append(cmd.Env, runenv...)
656
657 var err error
658
659 if tim != 0 {
660 err = cmd.Start()
661
662 if err == nil {
663 tick := time.NewTimer(time.Duration(tim) * time.Second)
664 done := make(chan error)
665 go func() {
666 done <- cmd.Wait()
667 }()
668 select {
669 case err = <-done:
670
671 case <-tick.C:
672 cmd.Process.Signal(os.Interrupt)
673 time.Sleep(1 * time.Second)
674 cmd.Process.Kill()
675 <-done
676 err = errTimeout
677 }
678 tick.Stop()
679 }
680 } else {
681 err = cmd.Run()
682 }
683 if err != nil && err != errTimeout {
684 err = fmt.Errorf("%s\n%s", err, buf.Bytes())
685 }
686 return buf.Bytes(), err
687 }
688
689 long := filepath.Join(cwd, t.goFileName())
690 switch action {
691 default:
692 t.err = fmt.Errorf("unimplemented action %q", action)
693
694 case "asmcheck":
695
696
697 ops := t.wantedAsmOpcodes(long)
698 self := runtime.GOOS + "/" + runtime.GOARCH
699 for _, env := range ops.Envs() {
700
701
702 if string(env) != self && !strings.HasPrefix(string(env), self+"/") && !*allCodegen {
703 continue
704 }
705
706 cmdline := []string{"build", "-gcflags", "-S=2"}
707
708
709 for i := 0; i < len(flags); i++ {
710 flag := flags[i]
711 switch {
712 case strings.HasPrefix(flag, "-gcflags="):
713 cmdline[2] += " " + strings.TrimPrefix(flag, "-gcflags=")
714 case strings.HasPrefix(flag, "--gcflags="):
715 cmdline[2] += " " + strings.TrimPrefix(flag, "--gcflags=")
716 case flag == "-gcflags", flag == "--gcflags":
717 i++
718 if i < len(flags) {
719 cmdline[2] += " " + flags[i]
720 }
721 default:
722 cmdline = append(cmdline, flag)
723 }
724 }
725
726 cmdline = append(cmdline, long)
727 cmd := exec.Command(goTool(), cmdline...)
728 cmd.Env = append(os.Environ(), env.Environ()...)
729 if len(flags) > 0 && flags[0] == "-race" {
730 cmd.Env = append(cmd.Env, "CGO_ENABLED=1")
731 }
732
733 var buf bytes.Buffer
734 cmd.Stdout, cmd.Stderr = &buf, &buf
735 if err := cmd.Run(); err != nil {
736 fmt.Println(env, "\n", cmd.Stderr)
737 t.err = err
738 return
739 }
740
741 t.err = t.asmCheck(buf.String(), long, env, ops[env])
742 if t.err != nil {
743 return
744 }
745 }
746 return
747
748 case "errorcheck":
749
750
751
752
753 cmdline := []string{goTool(), "tool", "compile", "-d=panic", "-C", "-e", "-o", "a.o"}
754
755 cmdline = append(cmdline, flags...)
756 cmdline = append(cmdline, long)
757 out, err := runcmd(cmdline...)
758 if wantError {
759 if err == nil {
760 t.err = fmt.Errorf("compilation succeeded unexpectedly\n%s", out)
761 return
762 }
763 if err == errTimeout {
764 t.err = fmt.Errorf("compilation timed out")
765 return
766 }
767 } else {
768 if err != nil {
769 t.err = err
770 return
771 }
772 }
773 if *updateErrors {
774 t.updateErrors(string(out), long)
775 }
776 t.err = t.errorCheck(string(out), wantAuto, long, t.gofile)
777 if t.err != nil {
778 return
779 }
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794 filename := strings.Replace(t.goFileName(), "\\", "/", -1)
795 if excluded[filename] {
796 if *verbose {
797 fmt.Printf("excl\t%s\n", filename)
798 }
799 return
800 }
801
802
803 for _, flag := range flags {
804 for _, pattern := range []string{
805 "-m",
806 } {
807 if strings.Contains(flag, pattern) {
808 if *verbose {
809 fmt.Printf("excl\t%s\t%s\n", filename, flags)
810 }
811 return
812 }
813 }
814 }
815
816
817 cmdline = []string{goTool(), "tool", "compile", "-G=3", "-C", "-e", "-o", "a.o"}
818
819 cmdline = append(cmdline, flags...)
820 cmdline = append(cmdline, long)
821 out, err = runcmd(cmdline...)
822 if wantError {
823 if err == nil {
824 t.err = fmt.Errorf("compilation succeeded unexpectedly\n%s", out)
825 return
826 }
827 } else {
828 if err != nil {
829 t.err = err
830 return
831 }
832 }
833 if *updateErrors {
834 t.updateErrors(string(out), long)
835 }
836 t.err = t.errorCheck(string(out), wantAuto, long, t.gofile)
837
838 case "compile":
839
840 _, t.err = compileFile(runcmd, long, flags)
841
842 case "compiledir":
843
844 longdir := filepath.Join(cwd, t.goDirName())
845 pkgs, err := goDirPackages(longdir, singlefilepkgs)
846 if err != nil {
847 t.err = err
848 return
849 }
850 for _, gofiles := range pkgs {
851 _, t.err = compileInDir(runcmd, longdir, flags, localImports, gofiles...)
852 if t.err != nil {
853 return
854 }
855 }
856
857 case "errorcheckdir", "errorcheckandrundir":
858 flags = append(flags, "-d=panic")
859
860
861
862 longdir := filepath.Join(cwd, t.goDirName())
863 pkgs, err := goDirPackages(longdir, singlefilepkgs)
864 if err != nil {
865 t.err = err
866 return
867 }
868 errPkg := len(pkgs) - 1
869 if wantError && action == "errorcheckandrundir" {
870
871
872 errPkg--
873 }
874 for i, gofiles := range pkgs {
875 out, err := compileInDir(runcmd, longdir, flags, localImports, gofiles...)
876 if i == errPkg {
877 if wantError && err == nil {
878 t.err = fmt.Errorf("compilation succeeded unexpectedly\n%s", out)
879 return
880 } else if !wantError && err != nil {
881 t.err = err
882 return
883 }
884 } else if err != nil {
885 t.err = err
886 return
887 }
888 var fullshort []string
889 for _, name := range gofiles {
890 fullshort = append(fullshort, filepath.Join(longdir, name), name)
891 }
892 t.err = t.errorCheck(string(out), wantAuto, fullshort...)
893 if t.err != nil {
894 break
895 }
896 }
897 if action == "errorcheckdir" {
898 return
899 }
900 fallthrough
901
902 case "rundir":
903
904
905
906
907 longdir := filepath.Join(cwd, t.goDirName())
908 pkgs, err := goDirPackages(longdir, singlefilepkgs)
909 if err != nil {
910 t.err = err
911 return
912 }
913
914 ldflags := []string{}
915 for i, fl := range flags {
916 if fl == "-ldflags" {
917 ldflags = flags[i+1:]
918 flags = flags[0:i]
919 break
920 }
921 }
922
923 for i, gofiles := range pkgs {
924 pflags := []string{}
925 pflags = append(pflags, flags...)
926 if setpkgpaths {
927 fp := filepath.Join(longdir, gofiles[0])
928 pkgname, err := getPackageNameFromSource(fp)
929 if err != nil {
930 log.Fatal(err)
931 }
932 pflags = append(pflags, "-p", pkgname)
933 }
934 _, err := compileInDir(runcmd, longdir, pflags, localImports, gofiles...)
935
936
937 if err != nil && !(wantError && action == "errorcheckandrundir" && i == len(pkgs)-2) {
938 t.err = err
939 return
940 }
941 if i == len(pkgs)-1 {
942 err = linkFile(runcmd, gofiles[0], ldflags)
943 if err != nil {
944 t.err = err
945 return
946 }
947 var cmd []string
948 cmd = append(cmd, findExecCmd()...)
949 cmd = append(cmd, filepath.Join(t.tempDir, "a.exe"))
950 cmd = append(cmd, args...)
951 out, err := runcmd(cmd...)
952 if err != nil {
953 t.err = err
954 return
955 }
956 t.checkExpectedOutput(out)
957 }
958 }
959
960 case "runindir":
961
962
963
964
965
966
967
968
969 tempDirIsGOPATH = true
970 srcDir := t.goDirName()
971 modName := filepath.Base(srcDir)
972 gopathSrcDir := filepath.Join(t.tempDir, "src", modName)
973 runInDir = gopathSrcDir
974
975 if err := overlayDir(gopathSrcDir, srcDir); err != nil {
976 t.err = err
977 return
978 }
979
980 modFile := fmt.Sprintf("module %s\ngo 1.14\n", modName)
981 if err := ioutil.WriteFile(filepath.Join(gopathSrcDir, "go.mod"), []byte(modFile), 0666); err != nil {
982 t.err = err
983 return
984 }
985
986 cmd := []string{goTool(), "run", goGcflags()}
987 if *linkshared {
988 cmd = append(cmd, "-linkshared")
989 }
990 cmd = append(cmd, flags...)
991 cmd = append(cmd, ".")
992 out, err := runcmd(cmd...)
993 if err != nil {
994 t.err = err
995 return
996 }
997 t.checkExpectedOutput(out)
998
999 case "build":
1000
1001 _, err := runcmd(goTool(), "build", goGcflags(), "-o", "a.exe", long)
1002 if err != nil {
1003 t.err = err
1004 }
1005
1006 case "builddir", "buildrundir":
1007
1008
1009 longdir := filepath.Join(cwd, t.goDirName())
1010 files, dirErr := ioutil.ReadDir(longdir)
1011 if dirErr != nil {
1012 t.err = dirErr
1013 break
1014 }
1015 var gos []string
1016 var asms []string
1017 for _, file := range files {
1018 switch filepath.Ext(file.Name()) {
1019 case ".go":
1020 gos = append(gos, filepath.Join(longdir, file.Name()))
1021 case ".s":
1022 asms = append(asms, filepath.Join(longdir, file.Name()))
1023 }
1024
1025 }
1026 if len(asms) > 0 {
1027 emptyHdrFile := filepath.Join(t.tempDir, "go_asm.h")
1028 if err := ioutil.WriteFile(emptyHdrFile, nil, 0666); err != nil {
1029 t.err = fmt.Errorf("write empty go_asm.h: %s", err)
1030 return
1031 }
1032 cmd := []string{goTool(), "tool", "asm", "-gensymabis", "-o", "symabis"}
1033 cmd = append(cmd, asms...)
1034 _, err = runcmd(cmd...)
1035 if err != nil {
1036 t.err = err
1037 break
1038 }
1039 }
1040 var objs []string
1041 cmd := []string{goTool(), "tool", "compile", "-e", "-D", ".", "-I", ".", "-o", "go.o"}
1042 if len(asms) > 0 {
1043 cmd = append(cmd, "-asmhdr", "go_asm.h", "-symabis", "symabis")
1044 }
1045 cmd = append(cmd, gos...)
1046 _, err := runcmd(cmd...)
1047 if err != nil {
1048 t.err = err
1049 break
1050 }
1051 objs = append(objs, "go.o")
1052 if len(asms) > 0 {
1053 cmd = []string{goTool(), "tool", "asm", "-e", "-I", ".", "-o", "asm.o"}
1054 cmd = append(cmd, asms...)
1055 _, err = runcmd(cmd...)
1056 if err != nil {
1057 t.err = err
1058 break
1059 }
1060 objs = append(objs, "asm.o")
1061 }
1062 cmd = []string{goTool(), "tool", "pack", "c", "all.a"}
1063 cmd = append(cmd, objs...)
1064 _, err = runcmd(cmd...)
1065 if err != nil {
1066 t.err = err
1067 break
1068 }
1069 cmd = []string{goTool(), "tool", "link", "-o", "a.exe", "all.a"}
1070 _, err = runcmd(cmd...)
1071 if err != nil {
1072 t.err = err
1073 break
1074 }
1075 if action == "buildrundir" {
1076 cmd = append(findExecCmd(), filepath.Join(t.tempDir, "a.exe"))
1077 out, err := runcmd(cmd...)
1078 if err != nil {
1079 t.err = err
1080 break
1081 }
1082 t.checkExpectedOutput(out)
1083 }
1084
1085 case "buildrun":
1086
1087
1088
1089 cmd := []string{goTool(), "build", goGcflags(), "-o", "a.exe"}
1090 if *linkshared {
1091 cmd = append(cmd, "-linkshared")
1092 }
1093 longdirgofile := filepath.Join(filepath.Join(cwd, t.dir), t.gofile)
1094 cmd = append(cmd, flags...)
1095 cmd = append(cmd, longdirgofile)
1096 _, err := runcmd(cmd...)
1097 if err != nil {
1098 t.err = err
1099 return
1100 }
1101 cmd = []string{"./a.exe"}
1102 out, err := runcmd(append(cmd, args...)...)
1103 if err != nil {
1104 t.err = err
1105 return
1106 }
1107
1108 t.checkExpectedOutput(out)
1109
1110 case "run":
1111
1112
1113
1114 runInDir = ""
1115 var out []byte
1116 var err error
1117 if len(flags)+len(args) == 0 && goGcflagsIsEmpty() && !*linkshared && goarch == runtime.GOARCH && goos == runtime.GOOS {
1118
1119
1120
1121
1122
1123
1124
1125 pkg := filepath.Join(t.tempDir, "pkg.a")
1126 if _, err := runcmd(goTool(), "tool", "compile", "-o", pkg, t.goFileName()); err != nil {
1127 t.err = err
1128 return
1129 }
1130 exe := filepath.Join(t.tempDir, "test.exe")
1131 cmd := []string{goTool(), "tool", "link", "-s", "-w"}
1132 cmd = append(cmd, "-o", exe, pkg)
1133 if _, err := runcmd(cmd...); err != nil {
1134 t.err = err
1135 return
1136 }
1137 out, err = runcmd(append([]string{exe}, args...)...)
1138 } else {
1139 cmd := []string{goTool(), "run", goGcflags()}
1140 if *linkshared {
1141 cmd = append(cmd, "-linkshared")
1142 }
1143 cmd = append(cmd, flags...)
1144 cmd = append(cmd, t.goFileName())
1145 out, err = runcmd(append(cmd, args...)...)
1146 }
1147 if err != nil {
1148 t.err = err
1149 return
1150 }
1151 t.checkExpectedOutput(out)
1152
1153 case "runoutput":
1154
1155
1156 rungatec <- true
1157 defer func() {
1158 <-rungatec
1159 }()
1160 runInDir = ""
1161 cmd := []string{goTool(), "run", goGcflags()}
1162 if *linkshared {
1163 cmd = append(cmd, "-linkshared")
1164 }
1165 cmd = append(cmd, t.goFileName())
1166 out, err := runcmd(append(cmd, args...)...)
1167 if err != nil {
1168 t.err = err
1169 return
1170 }
1171 tfile := filepath.Join(t.tempDir, "tmp__.go")
1172 if err := ioutil.WriteFile(tfile, out, 0666); err != nil {
1173 t.err = fmt.Errorf("write tempfile:%s", err)
1174 return
1175 }
1176 cmd = []string{goTool(), "run", goGcflags()}
1177 if *linkshared {
1178 cmd = append(cmd, "-linkshared")
1179 }
1180 cmd = append(cmd, tfile)
1181 out, err = runcmd(cmd...)
1182 if err != nil {
1183 t.err = err
1184 return
1185 }
1186 t.checkExpectedOutput(out)
1187
1188 case "errorcheckoutput":
1189
1190
1191 runInDir = ""
1192 cmd := []string{goTool(), "run", goGcflags()}
1193 if *linkshared {
1194 cmd = append(cmd, "-linkshared")
1195 }
1196 cmd = append(cmd, t.goFileName())
1197 out, err := runcmd(append(cmd, args...)...)
1198 if err != nil {
1199 t.err = err
1200 return
1201 }
1202 tfile := filepath.Join(t.tempDir, "tmp__.go")
1203 err = ioutil.WriteFile(tfile, out, 0666)
1204 if err != nil {
1205 t.err = fmt.Errorf("write tempfile:%s", err)
1206 return
1207 }
1208 cmdline := []string{goTool(), "tool", "compile", "-d=panic", "-e", "-o", "a.o"}
1209 cmdline = append(cmdline, flags...)
1210 cmdline = append(cmdline, tfile)
1211 out, err = runcmd(cmdline...)
1212 if wantError {
1213 if err == nil {
1214 t.err = fmt.Errorf("compilation succeeded unexpectedly\n%s", out)
1215 return
1216 }
1217 } else {
1218 if err != nil {
1219 t.err = err
1220 return
1221 }
1222 }
1223 t.err = t.errorCheck(string(out), false, tfile, "tmp__.go")
1224 return
1225 }
1226 }
1227
1228 var execCmd []string
1229
1230 func findExecCmd() []string {
1231 if execCmd != nil {
1232 return execCmd
1233 }
1234 execCmd = []string{}
1235 if goos == runtime.GOOS && goarch == runtime.GOARCH {
1236 return execCmd
1237 }
1238 path, err := exec.LookPath(fmt.Sprintf("go_%s_%s_exec", goos, goarch))
1239 if err == nil {
1240 execCmd = []string{path}
1241 }
1242 return execCmd
1243 }
1244
1245 func (t *test) String() string {
1246 return filepath.Join(t.dir, t.gofile)
1247 }
1248
1249 func (t *test) makeTempDir() {
1250 var err error
1251 t.tempDir, err = ioutil.TempDir("", "")
1252 if err != nil {
1253 log.Fatal(err)
1254 }
1255 if *keep {
1256 log.Printf("Temporary directory is %s", t.tempDir)
1257 }
1258 }
1259
1260
1261
1262
1263 func (t *test) checkExpectedOutput(gotBytes []byte) {
1264 got := string(gotBytes)
1265 filename := filepath.Join(t.dir, t.gofile)
1266 filename = filename[:len(filename)-len(".go")]
1267 filename += ".out"
1268 b, err := ioutil.ReadFile(filename)
1269
1270 got = strings.Replace(got, "\r\n", "\n", -1)
1271 if got != string(b) {
1272 if err == nil {
1273 t.err = fmt.Errorf("output does not match expected in %s. Instead saw\n%s", filename, got)
1274 } else {
1275 t.err = fmt.Errorf("output should be empty when (optional) expected-output file %s is not present. Instead saw\n%s", filename, got)
1276 }
1277 }
1278 }
1279
1280 func splitOutput(out string, wantAuto bool) []string {
1281
1282
1283
1284 var res []string
1285 for _, line := range strings.Split(out, "\n") {
1286 if strings.HasSuffix(line, "\r") {
1287 line = line[:len(line)-1]
1288 }
1289 if strings.HasPrefix(line, "\t") {
1290 res[len(res)-1] += "\n" + line
1291 } else if strings.HasPrefix(line, "go tool") || strings.HasPrefix(line, "#") || !wantAuto && strings.HasPrefix(line, "<autogenerated>") {
1292 continue
1293 } else if strings.TrimSpace(line) != "" {
1294 res = append(res, line)
1295 }
1296 }
1297 return res
1298 }
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311 func (t *test) errorCheck(outStr string, wantAuto bool, fullshort ...string) (err error) {
1312 defer func() {
1313 if *verbose && err != nil {
1314 log.Printf("%s gc output:\n%s", t, outStr)
1315 }
1316 }()
1317 var errs []error
1318 out := splitOutput(outStr, wantAuto)
1319
1320
1321 for i := range out {
1322 for j := 0; j < len(fullshort); j += 2 {
1323 full, short := fullshort[j], fullshort[j+1]
1324 out[i] = strings.Replace(out[i], full, short, -1)
1325 }
1326 }
1327
1328 var want []wantedError
1329 for j := 0; j < len(fullshort); j += 2 {
1330 full, short := fullshort[j], fullshort[j+1]
1331 want = append(want, t.wantedErrors(full, short)...)
1332 }
1333
1334 for _, we := range want {
1335 var errmsgs []string
1336 if we.auto {
1337 errmsgs, out = partitionStrings("<autogenerated>", out)
1338 } else {
1339 errmsgs, out = partitionStrings(we.prefix, out)
1340 }
1341 if len(errmsgs) == 0 {
1342 errs = append(errs, fmt.Errorf("%s:%d: missing error %q", we.file, we.lineNum, we.reStr))
1343 continue
1344 }
1345 matched := false
1346 n := len(out)
1347 for _, errmsg := range errmsgs {
1348
1349
1350 text := errmsg
1351 if i := strings.Index(text, " "); i >= 0 {
1352 text = text[i+1:]
1353 }
1354 if we.re.MatchString(text) {
1355 matched = true
1356 } else {
1357 out = append(out, errmsg)
1358 }
1359 }
1360 if !matched {
1361 errs = append(errs, fmt.Errorf("%s:%d: no match for %#q in:\n\t%s", we.file, we.lineNum, we.reStr, strings.Join(out[n:], "\n\t")))
1362 continue
1363 }
1364 }
1365
1366 if len(out) > 0 {
1367 errs = append(errs, fmt.Errorf("Unmatched Errors:"))
1368 for _, errLine := range out {
1369 errs = append(errs, fmt.Errorf("%s", errLine))
1370 }
1371 }
1372
1373 if len(errs) == 0 {
1374 return nil
1375 }
1376 if len(errs) == 1 {
1377 return errs[0]
1378 }
1379 var buf bytes.Buffer
1380 fmt.Fprintf(&buf, "\n")
1381 for _, err := range errs {
1382 fmt.Fprintf(&buf, "%s\n", err.Error())
1383 }
1384 return errors.New(buf.String())
1385 }
1386
1387 func (t *test) updateErrors(out, file string) {
1388 base := path.Base(file)
1389
1390 src, err := ioutil.ReadFile(file)
1391 if err != nil {
1392 fmt.Fprintln(os.Stderr, err)
1393 return
1394 }
1395 lines := strings.Split(string(src), "\n")
1396
1397 for i, ln := range lines {
1398 pos := strings.Index(ln, " // ERROR ")
1399 if pos >= 0 {
1400 lines[i] = ln[:pos]
1401 }
1402 }
1403
1404 errors := make(map[int]map[string]bool)
1405 tmpRe := regexp.MustCompile(`autotmp_[0-9]+`)
1406 for _, errStr := range splitOutput(out, false) {
1407 colon1 := strings.Index(errStr, ":")
1408 if colon1 < 0 || errStr[:colon1] != file {
1409 continue
1410 }
1411 colon2 := strings.Index(errStr[colon1+1:], ":")
1412 if colon2 < 0 {
1413 continue
1414 }
1415 colon2 += colon1 + 1
1416 line, err := strconv.Atoi(errStr[colon1+1 : colon2])
1417 line--
1418 if err != nil || line < 0 || line >= len(lines) {
1419 continue
1420 }
1421 msg := errStr[colon2+2:]
1422 msg = strings.Replace(msg, file, base, -1)
1423 msg = strings.TrimLeft(msg, " \t")
1424 for _, r := range []string{`\`, `*`, `+`, `?`, `[`, `]`, `(`, `)`} {
1425 msg = strings.Replace(msg, r, `\`+r, -1)
1426 }
1427 msg = strings.Replace(msg, `"`, `.`, -1)
1428 msg = tmpRe.ReplaceAllLiteralString(msg, `autotmp_[0-9]+`)
1429 if errors[line] == nil {
1430 errors[line] = make(map[string]bool)
1431 }
1432 errors[line][msg] = true
1433 }
1434
1435 for line, errs := range errors {
1436 var sorted []string
1437 for e := range errs {
1438 sorted = append(sorted, e)
1439 }
1440 sort.Strings(sorted)
1441 lines[line] += " // ERROR"
1442 for _, e := range sorted {
1443 lines[line] += fmt.Sprintf(` "%s$"`, e)
1444 }
1445 }
1446
1447 err = ioutil.WriteFile(file, []byte(strings.Join(lines, "\n")), 0640)
1448 if err != nil {
1449 fmt.Fprintln(os.Stderr, err)
1450 return
1451 }
1452
1453 exec.Command(goTool(), "fmt", file).CombinedOutput()
1454 }
1455
1456
1457
1458
1459 func matchPrefix(s, prefix string) bool {
1460 i := strings.Index(s, ":")
1461 if i < 0 {
1462 return false
1463 }
1464 j := strings.LastIndex(s[:i], "/")
1465 s = s[j+1:]
1466 if len(s) <= len(prefix) || s[:len(prefix)] != prefix {
1467 return false
1468 }
1469 switch s[len(prefix)] {
1470 case '[', ':':
1471 return true
1472 }
1473 return false
1474 }
1475
1476 func partitionStrings(prefix string, strs []string) (matched, unmatched []string) {
1477 for _, s := range strs {
1478 if matchPrefix(s, prefix) {
1479 matched = append(matched, s)
1480 } else {
1481 unmatched = append(unmatched, s)
1482 }
1483 }
1484 return
1485 }
1486
1487 type wantedError struct {
1488 reStr string
1489 re *regexp.Regexp
1490 lineNum int
1491 auto bool
1492 file string
1493 prefix string
1494 }
1495
1496 var (
1497 errRx = regexp.MustCompile(`// (?:GC_)?ERROR (.*)`)
1498 errAutoRx = regexp.MustCompile(`// (?:GC_)?ERRORAUTO (.*)`)
1499 errQuotesRx = regexp.MustCompile(`"([^"]*)"`)
1500 lineRx = regexp.MustCompile(`LINE(([+-])([0-9]+))?`)
1501 )
1502
1503 func (t *test) wantedErrors(file, short string) (errs []wantedError) {
1504 cache := make(map[string]*regexp.Regexp)
1505
1506 src, _ := ioutil.ReadFile(file)
1507 for i, line := range strings.Split(string(src), "\n") {
1508 lineNum := i + 1
1509 if strings.Contains(line, "////") {
1510
1511 continue
1512 }
1513 var auto bool
1514 m := errAutoRx.FindStringSubmatch(line)
1515 if m != nil {
1516 auto = true
1517 } else {
1518 m = errRx.FindStringSubmatch(line)
1519 }
1520 if m == nil {
1521 continue
1522 }
1523 all := m[1]
1524 mm := errQuotesRx.FindAllStringSubmatch(all, -1)
1525 if mm == nil {
1526 log.Fatalf("%s:%d: invalid errchk line: %s", t.goFileName(), lineNum, line)
1527 }
1528 for _, m := range mm {
1529 rx := lineRx.ReplaceAllStringFunc(m[1], func(m string) string {
1530 n := lineNum
1531 if strings.HasPrefix(m, "LINE+") {
1532 delta, _ := strconv.Atoi(m[5:])
1533 n += delta
1534 } else if strings.HasPrefix(m, "LINE-") {
1535 delta, _ := strconv.Atoi(m[5:])
1536 n -= delta
1537 }
1538 return fmt.Sprintf("%s:%d", short, n)
1539 })
1540 re := cache[rx]
1541 if re == nil {
1542 var err error
1543 re, err = regexp.Compile(rx)
1544 if err != nil {
1545 log.Fatalf("%s:%d: invalid regexp \"%s\" in ERROR line: %v", t.goFileName(), lineNum, rx, err)
1546 }
1547 cache[rx] = re
1548 }
1549 prefix := fmt.Sprintf("%s:%d", short, lineNum)
1550 errs = append(errs, wantedError{
1551 reStr: rx,
1552 re: re,
1553 prefix: prefix,
1554 auto: auto,
1555 lineNum: lineNum,
1556 file: short,
1557 })
1558 }
1559 }
1560
1561 return
1562 }
1563
1564 const (
1565
1566
1567
1568 reMatchCheck = `-?(?:\x60[^\x60]*\x60|"(?:[^"\\]|\\.)*")`
1569 )
1570
1571 var (
1572
1573 rxAsmComment = regexp.MustCompile(`^\s*(.*?)\s*(?://\s*(.+)\s*)?$`)
1574
1575
1576
1577
1578 rxAsmPlatform = regexp.MustCompile(`(\w+)(/\w+)?(/\w*)?\s*:\s*(` + reMatchCheck + `(?:\s*,\s*` + reMatchCheck + `)*)`)
1579
1580
1581 rxAsmCheck = regexp.MustCompile(reMatchCheck)
1582
1583
1584
1585
1586 archVariants = map[string][]string{
1587 "386": {"GO386", "sse2", "softfloat"},
1588 "amd64": {},
1589 "arm": {"GOARM", "5", "6", "7"},
1590 "arm64": {},
1591 "mips": {"GOMIPS", "hardfloat", "softfloat"},
1592 "mips64": {"GOMIPS64", "hardfloat", "softfloat"},
1593 "ppc64": {"GOPPC64", "power8", "power9"},
1594 "ppc64le": {"GOPPC64", "power8", "power9"},
1595 "s390x": {},
1596 "wasm": {},
1597 }
1598 )
1599
1600
1601 type wantedAsmOpcode struct {
1602 fileline string
1603 line int
1604 opcode *regexp.Regexp
1605 negative bool
1606 found bool
1607 }
1608
1609
1610
1611 type buildEnv string
1612
1613
1614
1615 func (b buildEnv) Environ() []string {
1616 fields := strings.Split(string(b), "/")
1617 if len(fields) != 3 {
1618 panic("invalid buildEnv string: " + string(b))
1619 }
1620 env := []string{"GOOS=" + fields[0], "GOARCH=" + fields[1]}
1621 if fields[2] != "" {
1622 env = append(env, archVariants[fields[1]][0]+"="+fields[2])
1623 }
1624 return env
1625 }
1626
1627
1628
1629
1630
1631 type asmChecks map[buildEnv]map[string][]wantedAsmOpcode
1632
1633
1634 func (a asmChecks) Envs() []buildEnv {
1635 var envs []buildEnv
1636 for e := range a {
1637 envs = append(envs, e)
1638 }
1639 sort.Slice(envs, func(i, j int) bool {
1640 return string(envs[i]) < string(envs[j])
1641 })
1642 return envs
1643 }
1644
1645 func (t *test) wantedAsmOpcodes(fn string) asmChecks {
1646 ops := make(asmChecks)
1647
1648 comment := ""
1649 src, _ := ioutil.ReadFile(fn)
1650 for i, line := range strings.Split(string(src), "\n") {
1651 matches := rxAsmComment.FindStringSubmatch(line)
1652 code, cmt := matches[1], matches[2]
1653
1654
1655
1656 comment += " " + cmt
1657 if code == "" {
1658 continue
1659 }
1660
1661
1662
1663 lnum := fn + ":" + strconv.Itoa(i+1)
1664 for _, ac := range rxAsmPlatform.FindAllStringSubmatch(comment, -1) {
1665 archspec, allchecks := ac[1:4], ac[4]
1666
1667 var arch, subarch, os string
1668 switch {
1669 case archspec[2] != "":
1670 os, arch, subarch = archspec[0], archspec[1][1:], archspec[2][1:]
1671 case archspec[1] != "":
1672 os, arch, subarch = "linux", archspec[0], archspec[1][1:]
1673 default:
1674 os, arch, subarch = "linux", archspec[0], ""
1675 if arch == "wasm" {
1676 os = "js"
1677 }
1678 }
1679
1680 if _, ok := archVariants[arch]; !ok {
1681 log.Fatalf("%s:%d: unsupported architecture: %v", t.goFileName(), i+1, arch)
1682 }
1683
1684
1685 envs := make([]buildEnv, 0, 4)
1686 if subarch != "" {
1687 envs = append(envs, buildEnv(os+"/"+arch+"/"+subarch))
1688 } else {
1689 subarchs := archVariants[arch]
1690 if len(subarchs) == 0 {
1691 envs = append(envs, buildEnv(os+"/"+arch+"/"))
1692 } else {
1693 for _, sa := range archVariants[arch][1:] {
1694 envs = append(envs, buildEnv(os+"/"+arch+"/"+sa))
1695 }
1696 }
1697 }
1698
1699 for _, m := range rxAsmCheck.FindAllString(allchecks, -1) {
1700 negative := false
1701 if m[0] == '-' {
1702 negative = true
1703 m = m[1:]
1704 }
1705
1706 rxsrc, err := strconv.Unquote(m)
1707 if err != nil {
1708 log.Fatalf("%s:%d: error unquoting string: %v", t.goFileName(), i+1, err)
1709 }
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719 oprx, err := regexp.Compile("^" + rxsrc)
1720 if err != nil {
1721 log.Fatalf("%s:%d: %v", t.goFileName(), i+1, err)
1722 }
1723
1724 for _, env := range envs {
1725 if ops[env] == nil {
1726 ops[env] = make(map[string][]wantedAsmOpcode)
1727 }
1728 ops[env][lnum] = append(ops[env][lnum], wantedAsmOpcode{
1729 negative: negative,
1730 fileline: lnum,
1731 line: i + 1,
1732 opcode: oprx,
1733 })
1734 }
1735 }
1736 }
1737 comment = ""
1738 }
1739
1740 return ops
1741 }
1742
1743 func (t *test) asmCheck(outStr string, fn string, env buildEnv, fullops map[string][]wantedAsmOpcode) (err error) {
1744
1745
1746
1747
1748 functionMarkers := make([]int, 1)
1749 lineFuncMap := make(map[string]int)
1750
1751 lines := strings.Split(outStr, "\n")
1752 rxLine := regexp.MustCompile(fmt.Sprintf(`\((%s:\d+)\)\s+(.*)`, regexp.QuoteMeta(fn)))
1753
1754 for nl, line := range lines {
1755
1756 if len(line) > 0 && line[0] != '\t' {
1757 functionMarkers = append(functionMarkers, nl)
1758 }
1759
1760
1761
1762 matches := rxLine.FindStringSubmatch(line)
1763 if len(matches) == 0 {
1764 continue
1765 }
1766 srcFileLine, asm := matches[1], matches[2]
1767
1768
1769
1770
1771 lineFuncMap[srcFileLine] = len(functionMarkers) - 1
1772
1773
1774
1775 if ops, found := fullops[srcFileLine]; found {
1776 for i := range ops {
1777 if !ops[i].found && ops[i].opcode.FindString(asm) != "" {
1778 ops[i].found = true
1779 }
1780 }
1781 }
1782 }
1783 functionMarkers = append(functionMarkers, len(lines))
1784
1785 var failed []wantedAsmOpcode
1786 for _, ops := range fullops {
1787 for _, o := range ops {
1788
1789
1790 if o.negative == o.found {
1791 failed = append(failed, o)
1792 }
1793 }
1794 }
1795 if len(failed) == 0 {
1796 return
1797 }
1798
1799
1800 sort.Slice(failed, func(i, j int) bool {
1801 return failed[i].line < failed[j].line
1802 })
1803
1804 lastFunction := -1
1805 var errbuf bytes.Buffer
1806 fmt.Fprintln(&errbuf)
1807 for _, o := range failed {
1808
1809
1810 funcIdx := lineFuncMap[o.fileline]
1811 if funcIdx != 0 && funcIdx != lastFunction {
1812 funcLines := lines[functionMarkers[funcIdx]:functionMarkers[funcIdx+1]]
1813 log.Println(strings.Join(funcLines, "\n"))
1814 lastFunction = funcIdx
1815 }
1816
1817 if o.negative {
1818 fmt.Fprintf(&errbuf, "%s:%d: %s: wrong opcode found: %q\n", t.goFileName(), o.line, env, o.opcode.String())
1819 } else {
1820 fmt.Fprintf(&errbuf, "%s:%d: %s: opcode not found: %q\n", t.goFileName(), o.line, env, o.opcode.String())
1821 }
1822 }
1823 err = errors.New(errbuf.String())
1824 return
1825 }
1826
1827
1828
1829 func defaultRunOutputLimit() int {
1830 const maxArmCPU = 2
1831
1832 cpu := runtime.NumCPU()
1833 if runtime.GOARCH == "arm" && cpu > maxArmCPU {
1834 cpu = maxArmCPU
1835 }
1836 return cpu
1837 }
1838
1839
1840 func checkShouldTest() {
1841 assert := func(ok bool, _ string) {
1842 if !ok {
1843 panic("fail")
1844 }
1845 }
1846 assertNot := func(ok bool, _ string) { assert(!ok, "") }
1847
1848
1849 assert(shouldTest("// +build linux", "linux", "arm"))
1850 assert(shouldTest("// +build !windows", "linux", "arm"))
1851 assertNot(shouldTest("// +build !windows", "windows", "amd64"))
1852
1853
1854 assert(shouldTest("// This is a test.", "os", "arch"))
1855
1856
1857 assertNot(shouldTest("// +build arm 386", "linux", "amd64"))
1858
1859
1860 assertNot(shouldTest("// +build !windows,!plan9", "windows", "amd64"))
1861 assertNot(shouldTest("// +build !windows,!plan9", "plan9", "386"))
1862
1863
1864 assert(shouldTest("// +build !windows\n// +build amd64", "linux", "amd64"))
1865 assertNot(shouldTest("// +build !windows\n// +build amd64", "windows", "amd64"))
1866
1867
1868 assert(shouldTest("// +build !windows !plan9", "windows", "amd64"))
1869 }
1870
1871 func getenv(key, def string) string {
1872 value := os.Getenv(key)
1873 if value != "" {
1874 return value
1875 }
1876 return def
1877 }
1878
1879
1880 func overlayDir(dstRoot, srcRoot string) error {
1881 dstRoot = filepath.Clean(dstRoot)
1882 if err := os.MkdirAll(dstRoot, 0777); err != nil {
1883 return err
1884 }
1885
1886 srcRoot, err := filepath.Abs(srcRoot)
1887 if err != nil {
1888 return err
1889 }
1890
1891 return filepath.WalkDir(srcRoot, func(srcPath string, d fs.DirEntry, err error) error {
1892 if err != nil || srcPath == srcRoot {
1893 return err
1894 }
1895
1896 suffix := strings.TrimPrefix(srcPath, srcRoot)
1897 for len(suffix) > 0 && suffix[0] == filepath.Separator {
1898 suffix = suffix[1:]
1899 }
1900 dstPath := filepath.Join(dstRoot, suffix)
1901
1902 var info fs.FileInfo
1903 if d.Type()&os.ModeSymlink != 0 {
1904 info, err = os.Stat(srcPath)
1905 } else {
1906 info, err = d.Info()
1907 }
1908 if err != nil {
1909 return err
1910 }
1911 perm := info.Mode() & os.ModePerm
1912
1913
1914
1915 if info.IsDir() {
1916 return os.MkdirAll(dstPath, perm|0200)
1917 }
1918
1919
1920 if err := os.Symlink(srcPath, dstPath); err == nil {
1921 return nil
1922 }
1923
1924
1925 src, err := os.Open(srcPath)
1926 if err != nil {
1927 return err
1928 }
1929 defer src.Close()
1930
1931 dst, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, perm)
1932 if err != nil {
1933 return err
1934 }
1935
1936 _, err = io.Copy(dst, src)
1937 if closeErr := dst.Close(); err == nil {
1938 err = closeErr
1939 }
1940 return err
1941 })
1942 }
1943
1944
1945
1946 var excluded = map[string]bool{
1947 "complit1.go": true,
1948 "const2.go": true,
1949 "ddd1.go": true,
1950 "directive.go": true,
1951 "float_lit3.go": true,
1952 "import1.go": true,
1953 "import5.go": true,
1954 "import6.go": true,
1955 "initializerr.go": true,
1956 "linkname2.go": true,
1957 "notinheap.go": true,
1958 "shift1.go": true,
1959 "typecheck.go": true,
1960 "writebarrier.go": true,
1961
1962 "fixedbugs/bug176.go": true,
1963 "fixedbugs/bug195.go": true,
1964 "fixedbugs/bug228.go": true,
1965 "fixedbugs/bug231.go": true,
1966 "fixedbugs/bug255.go": true,
1967 "fixedbugs/bug351.go": true,
1968 "fixedbugs/bug374.go": true,
1969 "fixedbugs/bug385_32.go": true,
1970 "fixedbugs/bug388.go": true,
1971 "fixedbugs/bug412.go": true,
1972
1973 "fixedbugs/issue11590.go": true,
1974 "fixedbugs/issue11610.go": true,
1975 "fixedbugs/issue11614.go": true,
1976 "fixedbugs/issue13415.go": true,
1977 "fixedbugs/issue14520.go": true,
1978 "fixedbugs/issue16428.go": true,
1979 "fixedbugs/issue17038.go": true,
1980 "fixedbugs/issue17645.go": true,
1981 "fixedbugs/issue18331.go": true,
1982 "fixedbugs/issue18393.go": true,
1983 "fixedbugs/issue19012.go": true,
1984 "fixedbugs/issue20233.go": true,
1985 "fixedbugs/issue20245.go": true,
1986 "fixedbugs/issue20250.go": true,
1987 "fixedbugs/issue21979.go": true,
1988 "fixedbugs/issue23732.go": true,
1989 "fixedbugs/issue25958.go": true,
1990 "fixedbugs/issue28079b.go": true,
1991 "fixedbugs/issue28268.go": true,
1992 "fixedbugs/issue33460.go": true,
1993 "fixedbugs/issue41575.go": true,
1994 "fixedbugs/issue42058a.go": true,
1995 "fixedbugs/issue42058b.go": true,
1996 "fixedbugs/issue4232.go": true,
1997 "fixedbugs/issue4452.go": true,
1998 "fixedbugs/issue5609.go": true,
1999 "fixedbugs/issue6889.go": true,
2000 "fixedbugs/issue7525.go": true,
2001 "fixedbugs/issue7525b.go": true,
2002 "fixedbugs/issue7525c.go": true,
2003 "fixedbugs/issue7525d.go": true,
2004 "fixedbugs/issue7525e.go": true,
2005 "fixedbugs/issue46749.go": true,
2006 }
2007
View as plain text