1// skip 2 3// Copyright 2012 The Go Authors. All rights reserved. 4// Use of this source code is governed by a BSD-style 5// license that can be found in the LICENSE file. 6 7// Run runs tests in the test directory. 8// 9// TODO(bradfitz): docs of some sort, once we figure out how we're changing 10// headers of files 11package main 12 13import ( 14 "bytes" 15 "errors" 16 "flag" 17 "fmt" 18 "go/build" 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 34var ( 35 verbose = flag.Bool("v", false, "verbose. if set, parallelism is set to 1.") 36 numParallel = flag.Int("n", runtime.NumCPU(), "number of parallel tests to run") 37 summary = flag.Bool("summary", false, "show summary of results") 38 showSkips = flag.Bool("show_skips", false, "show skipped tests") 39 runoutputLimit = flag.Int("l", defaultRunOutputLimit(), "number of parallel runoutput tests to run") 40) 41 42var ( 43 // gc and ld are [568][gl]. 44 gc, ld string 45 46 // letter is the build.ArchChar 47 letter string 48 49 // dirs are the directories to look for *.go files in. 50 // TODO(bradfitz): just use all directories? 51 dirs = []string{".", "ken", "chan", "interface", "syntax", "dwarf", "fixedbugs", "bugs"} 52 53 // ratec controls the max number of tests running at a time. 54 ratec chan bool 55 56 // toRun is the channel of tests to run. 57 // It is nil until the first test is started. 58 toRun chan *test 59 60 // rungatec controls the max number of runoutput tests 61 // executed in parallel as they can each consume a lot of memory. 62 rungatec chan bool 63) 64 65// maxTests is an upper bound on the total number of tests. 66// It is used as a channel buffer size to make sure sends don't block. 67const maxTests = 5000 68 69func main() { 70 flag.Parse() 71 72 // Disable parallelism if printing 73 if *verbose { 74 *numParallel = 1 75 } 76 77 ratec = make(chan bool, *numParallel) 78 rungatec = make(chan bool, *runoutputLimit) 79 var err error 80 letter, err = build.ArchChar(build.Default.GOARCH) 81 check(err) 82 gc = letter + "g" 83 ld = letter + "l" 84 85 var tests []*test 86 if flag.NArg() > 0 { 87 for _, arg := range flag.Args() { 88 if arg == "-" || arg == "--" { 89 // Permit running: 90 // $ go run run.go - env.go 91 // $ go run run.go -- env.go 92 // $ go run run.go - ./fixedbugs 93 // $ go run run.go -- ./fixedbugs 94 continue 95 } 96 if fi, err := os.Stat(arg); err == nil && fi.IsDir() { 97 for _, baseGoFile := range goFiles(arg) { 98 tests = append(tests, startTest(arg, baseGoFile)) 99 } 100 } else if strings.HasSuffix(arg, ".go") { 101 dir, file := filepath.Split(arg) 102 tests = append(tests, startTest(dir, file)) 103 } else { 104 log.Fatalf("can't yet deal with non-directory and non-go file %q", arg) 105 } 106 } 107 } else { 108 for _, dir := range dirs { 109 for _, baseGoFile := range goFiles(dir) { 110 tests = append(tests, startTest(dir, baseGoFile)) 111 } 112 } 113 } 114 115 failed := false 116 resCount := map[string]int{} 117 for _, test := range tests { 118 <-test.donec 119 status := "ok " 120 errStr := "" 121 if _, isSkip := test.err.(skipError); isSkip { 122 status = "skip" 123 test.err = nil 124 if !skipOkay[path.Join(test.dir, test.gofile)] { 125 errStr = "unexpected skip for " + path.Join(test.dir, test.gofile) + ": " + errStr 126 status = "FAIL" 127 } 128 } 129 if test.err != nil { 130 status = "FAIL" 131 errStr = test.err.Error() 132 } 133 if status == "FAIL" { 134 failed = true 135 } 136 resCount[status]++ 137 if status == "skip" && !*verbose && !*showSkips { 138 continue 139 } 140 dt := fmt.Sprintf("%.3fs", test.dt.Seconds()) 141 if status == "FAIL" { 142 fmt.Printf("# go run run.go -- %s\n%s\nFAIL\t%s\t%s\n", 143 path.Join(test.dir, test.gofile), 144 errStr, test.goFileName(), dt) 145 continue 146 } 147 if !*verbose { 148 continue 149 } 150 fmt.Printf("%s\t%s\t%s\n", status, test.goFileName(), dt) 151 } 152 153 if *summary { 154 for k, v := range resCount { 155 fmt.Printf("%5d %s\n", v, k) 156 } 157 } 158 159 if failed { 160 os.Exit(1) 161 } 162} 163 164func toolPath(name string) string { 165 p := filepath.Join(os.Getenv("GOROOT"), "bin", "tool", name) 166 if _, err := os.Stat(p); err != nil { 167 log.Fatalf("didn't find binary at %s", p) 168 } 169 return p 170} 171 172func goFiles(dir string) []string { 173 f, err := os.Open(dir) 174 check(err) 175 dirnames, err := f.Readdirnames(-1) 176 check(err) 177 names := []string{} 178 for _, name := range dirnames { 179 if !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") { 180 names = append(names, name) 181 } 182 } 183 sort.Strings(names) 184 return names 185} 186 187type runCmd func(...string) ([]byte, error) 188 189func compileFile(runcmd runCmd, longname string) (out []byte, err error) { 190 return runcmd("go", "tool", gc, "-e", longname) 191} 192 193func compileInDir(runcmd runCmd, dir string, names ...string) (out []byte, err error) { 194 cmd := []string{"go", "tool", gc, "-e", "-D", ".", "-I", "."} 195 for _, name := range names { 196 cmd = append(cmd, filepath.Join(dir, name)) 197 } 198 return runcmd(cmd...) 199} 200 201func linkFile(runcmd runCmd, goname string) (err error) { 202 pfile := strings.Replace(goname, ".go", "."+letter, -1) 203 _, err = runcmd("go", "tool", ld, "-o", "a.exe", "-L", ".", pfile) 204 return 205} 206 207// skipError describes why a test was skipped. 208type skipError string 209 210func (s skipError) Error() string { return string(s) } 211 212func check(err error) { 213 if err != nil { 214 log.Fatal(err) 215 } 216} 217 218// test holds the state of a test. 219type test struct { 220 dir, gofile string 221 donec chan bool // closed when done 222 dt time.Duration 223 224 src string 225 action string // "compile", "build", etc. 226 227 tempDir string 228 err error 229} 230 231// startTest 232func startTest(dir, gofile string) *test { 233 t := &test{ 234 dir: dir, 235 gofile: gofile, 236 donec: make(chan bool, 1), 237 } 238 if toRun == nil { 239 toRun = make(chan *test, maxTests) 240 go runTests() 241 } 242 select { 243 case toRun <- t: 244 default: 245 panic("toRun buffer size (maxTests) is too small") 246 } 247 return t 248} 249 250// runTests runs tests in parallel, but respecting the order they 251// were enqueued on the toRun channel. 252func runTests() { 253 for { 254 ratec <- true 255 t := <-toRun 256 go func() { 257 t.run() 258 <-ratec 259 }() 260 } 261} 262 263var cwd, _ = os.Getwd() 264 265func (t *test) goFileName() string { 266 return filepath.Join(t.dir, t.gofile) 267} 268 269func (t *test) goDirName() string { 270 return filepath.Join(t.dir, strings.Replace(t.gofile, ".go", ".dir", -1)) 271} 272 273func goDirFiles(longdir string) (filter []os.FileInfo, err error) { 274 files, dirErr := ioutil.ReadDir(longdir) 275 if dirErr != nil { 276 return nil, dirErr 277 } 278 for _, gofile := range files { 279 if filepath.Ext(gofile.Name()) == ".go" { 280 filter = append(filter, gofile) 281 } 282 } 283 return 284} 285 286var packageRE = regexp.MustCompile(`(?m)^package (\w+)`) 287 288func goDirPackages(longdir string) ([][]string, error) { 289 files, err := goDirFiles(longdir) 290 if err != nil { 291 return nil, err 292 } 293 var pkgs [][]string 294 m := make(map[string]int) 295 for _, file := range files { 296 name := file.Name() 297 data, err := ioutil.ReadFile(filepath.Join(longdir, name)) 298 if err != nil { 299 return nil, err 300 } 301 pkgname := packageRE.FindStringSubmatch(string(data)) 302 if pkgname == nil { 303 return nil, fmt.Errorf("cannot find package name in %s", name) 304 } 305 i, ok := m[pkgname[1]] 306 if !ok { 307 i = len(pkgs) 308 pkgs = append(pkgs, nil) 309 m[pkgname[1]] = i 310 } 311 pkgs[i] = append(pkgs[i], name) 312 } 313 return pkgs, nil 314} 315 316type context struct { 317 GOOS string 318 GOARCH string 319} 320 321// shouldTest looks for build tags in a source file and returns 322// whether the file should be used according to the tags. 323func shouldTest(src string, goos, goarch string) (ok bool, whyNot string) { 324 if idx := strings.Index(src, "\npackage"); idx >= 0 { 325 src = src[:idx] 326 } 327 for _, line := range strings.Split(src, "\n") { 328 line = strings.TrimSpace(line) 329 if strings.HasPrefix(line, "//") { 330 line = line[2:] 331 } else { 332 continue 333 } 334 line = strings.TrimSpace(line) 335 if len(line) == 0 || line[0] != '+' { 336 continue 337 } 338 ctxt := &context{ 339 GOOS: goos, 340 GOARCH: goarch, 341 } 342 words := strings.Fields(line) 343 if words[0] == "+build" { 344 ok := false 345 for _, word := range words[1:] { 346 if ctxt.match(word) { 347 ok = true 348 break 349 } 350 } 351 if !ok { 352 // no matching tag found. 353 return false, line 354 } 355 } 356 } 357 // no build tags 358 return true, "" 359} 360 361func (ctxt *context) match(name string) bool { 362 if name == "" { 363 return false 364 } 365 if i := strings.Index(name, ","); i >= 0 { 366 // comma-separated list 367 return ctxt.match(name[:i]) && ctxt.match(name[i+1:]) 368 } 369 if strings.HasPrefix(name, "!!") { // bad syntax, reject always 370 return false 371 } 372 if strings.HasPrefix(name, "!") { // negation 373 return len(name) > 1 && !ctxt.match(name[1:]) 374 } 375 376 // Tags must be letters, digits, underscores or dots. 377 // Unlike in Go identifiers, all digits are fine (e.g., "386"). 378 for _, c := range name { 379 if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' { 380 return false 381 } 382 } 383 384 if name == ctxt.GOOS || name == ctxt.GOARCH { 385 return true 386 } 387 388 return false 389} 390 391func init() { checkShouldTest() } 392 393// run runs a test. 394func (t *test) run() { 395 start := time.Now() 396 defer func() { 397 t.dt = time.Since(start) 398 close(t.donec) 399 }() 400 401 srcBytes, err := ioutil.ReadFile(t.goFileName()) 402 if err != nil { 403 t.err = err 404 return 405 } 406 t.src = string(srcBytes) 407 if t.src[0] == '\n' { 408 t.err = skipError("starts with newline") 409 return 410 } 411 pos := strings.Index(t.src, "\n\n") 412 if pos == -1 { 413 t.err = errors.New("double newline not found") 414 return 415 } 416 if ok, why := shouldTest(t.src, runtime.GOOS, runtime.GOARCH); !ok { 417 t.action = "skip" 418 if *showSkips { 419 fmt.Printf("%-20s %-20s: %s\n", t.action, t.goFileName(), why) 420 } 421 return 422 } 423 action := t.src[:pos] 424 if nl := strings.Index(action, "\n"); nl >= 0 && strings.Contains(action[:nl], "+build") { 425 // skip first line 426 action = action[nl+1:] 427 } 428 if strings.HasPrefix(action, "//") { 429 action = action[2:] 430 } 431 432 var args, flags []string 433 wantError := false 434 f := strings.Fields(action) 435 if len(f) > 0 { 436 action = f[0] 437 args = f[1:] 438 } 439 440 switch action { 441 case "rundircmpout": 442 action = "rundir" 443 t.action = "rundir" 444 case "cmpout": 445 action = "run" // the run case already looks for <dir>/<test>.out files 446 fallthrough 447 case "compile", "compiledir", "build", "run", "runoutput", "rundir": 448 t.action = action 449 case "errorcheck", "errorcheckdir", "errorcheckoutput": 450 t.action = action 451 wantError = true 452 for len(args) > 0 && strings.HasPrefix(args[0], "-") { 453 if args[0] == "-0" { 454 wantError = false 455 } else { 456 flags = append(flags, args[0]) 457 } 458 args = args[1:] 459 } 460 case "skip": 461 t.action = "skip" 462 return 463 default: 464 t.err = skipError("skipped; unknown pattern: " + action) 465 t.action = "??" 466 return 467 } 468 469 t.makeTempDir() 470 defer os.RemoveAll(t.tempDir) 471 472 err = ioutil.WriteFile(filepath.Join(t.tempDir, t.gofile), srcBytes, 0644) 473 check(err) 474 475 // A few tests (of things like the environment) require these to be set. 476 os.Setenv("GOOS", runtime.GOOS) 477 os.Setenv("GOARCH", runtime.GOARCH) 478 479 useTmp := true 480 runcmd := func(args ...string) ([]byte, error) { 481 cmd := exec.Command(args[0], args[1:]...) 482 var buf bytes.Buffer 483 cmd.Stdout = &buf 484 cmd.Stderr = &buf 485 if useTmp { 486 cmd.Dir = t.tempDir 487 cmd.Env = envForDir(cmd.Dir) 488 } 489 err := cmd.Run() 490 if err != nil { 491 err = fmt.Errorf("%s\n%s", err, buf.Bytes()) 492 } 493 return buf.Bytes(), err 494 } 495 496 long := filepath.Join(cwd, t.goFileName()) 497 switch action { 498 default: 499 t.err = fmt.Errorf("unimplemented action %q", action) 500 501 case "errorcheck": 502 cmdline := []string{"go", "tool", gc, "-e", "-o", "a." + letter} 503 cmdline = append(cmdline, flags...) 504 cmdline = append(cmdline, long) 505 out, err := runcmd(cmdline...) 506 if wantError { 507 if err == nil { 508 t.err = fmt.Errorf("compilation succeeded unexpectedly\n%s", out) 509 return 510 } 511 } else { 512 if err != nil { 513 t.err = err 514 return 515 } 516 } 517 t.err = t.errorCheck(string(out), long, t.gofile) 518 return 519 520 case "compile": 521 _, t.err = compileFile(runcmd, long) 522 523 case "compiledir": 524 // Compile all files in the directory in lexicographic order. 525 longdir := filepath.Join(cwd, t.goDirName()) 526 pkgs, err := goDirPackages(longdir) 527 if err != nil { 528 t.err = err 529 return 530 } 531 for _, gofiles := range pkgs { 532 _, t.err = compileInDir(runcmd, longdir, gofiles...) 533 if t.err != nil { 534 return 535 } 536 } 537 538 case "errorcheckdir": 539 // errorcheck all files in lexicographic order 540 // useful for finding importing errors 541 longdir := filepath.Join(cwd, t.goDirName()) 542 pkgs, err := goDirPackages(longdir) 543 if err != nil { 544 t.err = err 545 return 546 } 547 for i, gofiles := range pkgs { 548 out, err := compileInDir(runcmd, longdir, gofiles...) 549 if i == len(pkgs)-1 { 550 if wantError && err == nil { 551 t.err = fmt.Errorf("compilation succeeded unexpectedly\n%s", out) 552 return 553 } else if !wantError && err != nil { 554 t.err = err 555 return 556 } 557 } else if err != nil { 558 t.err = err 559 return 560 } 561 var fullshort []string 562 for _, name := range gofiles { 563 fullshort = append(fullshort, filepath.Join(longdir, name), name) 564 } 565 t.err = t.errorCheck(string(out), fullshort...) 566 if t.err != nil { 567 break 568 } 569 } 570 571 case "rundir": 572 // Compile all files in the directory in lexicographic order. 573 // then link as if the last file is the main package and run it 574 longdir := filepath.Join(cwd, t.goDirName()) 575 pkgs, err := goDirPackages(longdir) 576 if err != nil { 577 t.err = err 578 return 579 } 580 for i, gofiles := range pkgs { 581 _, err := compileInDir(runcmd, longdir, gofiles...) 582 if err != nil { 583 t.err = err 584 return 585 } 586 if i == len(pkgs)-1 { 587 err = linkFile(runcmd, gofiles[0]) 588 if err != nil { 589 t.err = err 590 return 591 } 592 out, err := runcmd(append([]string{filepath.Join(t.tempDir, "a.exe")}, args...)...) 593 if err != nil { 594 t.err = err 595 return 596 } 597 if strings.Replace(string(out), "\r\n", "\n", -1) != t.expectedOutput() { 598 t.err = fmt.Errorf("incorrect output\n%s", out) 599 } 600 } 601 } 602 603 case "build": 604 _, err := runcmd("go", "build", "-o", "a.exe", long) 605 if err != nil { 606 t.err = err 607 } 608 609 case "run": 610 useTmp = false 611 out, err := runcmd(append([]string{"go", "run", t.goFileName()}, args...)...) 612 if err != nil { 613 t.err = err 614 } 615 if strings.Replace(string(out), "\r\n", "\n", -1) != t.expectedOutput() { 616 t.err = fmt.Errorf("incorrect output\n%s", out) 617 } 618 619 case "runoutput": 620 rungatec <- true 621 defer func() { 622 <-rungatec 623 }() 624 useTmp = false 625 out, err := runcmd(append([]string{"go", "run", t.goFileName()}, args...)...) 626 if err != nil { 627 t.err = err 628 } 629 tfile := filepath.Join(t.tempDir, "tmp__.go") 630 if err := ioutil.WriteFile(tfile, out, 0666); err != nil { 631 t.err = fmt.Errorf("write tempfile:%s", err) 632 return 633 } 634 out, err = runcmd("go", "run", tfile) 635 if err != nil { 636 t.err = err 637 } 638 if string(out) != t.expectedOutput() { 639 t.err = fmt.Errorf("incorrect output\n%s", out) 640 } 641 642 case "errorcheckoutput": 643 useTmp = false 644 out, err := runcmd(append([]string{"go", "run", t.goFileName()}, args...)...) 645 if err != nil { 646 t.err = err 647 } 648 tfile := filepath.Join(t.tempDir, "tmp__.go") 649 err = ioutil.WriteFile(tfile, out, 0666) 650 if err != nil { 651 t.err = fmt.Errorf("write tempfile:%s", err) 652 return 653 } 654 cmdline := []string{"go", "tool", gc, "-e", "-o", "a." + letter} 655 cmdline = append(cmdline, flags...) 656 cmdline = append(cmdline, tfile) 657 out, err = runcmd(cmdline...) 658 if wantError { 659 if err == nil { 660 t.err = fmt.Errorf("compilation succeeded unexpectedly\n%s", out) 661 return 662 } 663 } else { 664 if err != nil { 665 t.err = err 666 return 667 } 668 } 669 t.err = t.errorCheck(string(out), tfile, "tmp__.go") 670 return 671 } 672} 673 674func (t *test) String() string { 675 return filepath.Join(t.dir, t.gofile) 676} 677 678func (t *test) makeTempDir() { 679 var err error 680 t.tempDir, err = ioutil.TempDir("", "") 681 check(err) 682} 683 684func (t *test) expectedOutput() string { 685 filename := filepath.Join(t.dir, t.gofile) 686 filename = filename[:len(filename)-len(".go")] 687 filename += ".out" 688 b, _ := ioutil.ReadFile(filename) 689 return string(b) 690} 691 692func (t *test) errorCheck(outStr string, fullshort ...string) (err error) { 693 defer func() { 694 if *verbose && err != nil { 695 log.Printf("%s gc output:\n%s", t, outStr) 696 } 697 }() 698 var errs []error 699 700 var out []string 701 // 6g error messages continue onto additional lines with leading tabs. 702 // Split the output at the beginning of each line that doesn't begin with a tab. 703 for _, line := range strings.Split(outStr, "\n") { 704 if strings.HasSuffix(line, "\r") { // remove '\r', output by compiler on windows 705 line = line[:len(line)-1] 706 } 707 if strings.HasPrefix(line, "\t") { 708 out[len(out)-1] += "\n" + line 709 } else if strings.HasPrefix(line, "go tool") { 710 continue 711 } else if strings.TrimSpace(line) != "" { 712 out = append(out, line) 713 } 714 } 715 716 // Cut directory name. 717 for i := range out { 718 for j := 0; j < len(fullshort); j += 2 { 719 full, short := fullshort[j], fullshort[j+1] 720 out[i] = strings.Replace(out[i], full, short, -1) 721 } 722 } 723 724 var want []wantedError 725 for j := 0; j < len(fullshort); j += 2 { 726 full, short := fullshort[j], fullshort[j+1] 727 want = append(want, t.wantedErrors(full, short)...) 728 } 729 730 for _, we := range want { 731 var errmsgs []string 732 errmsgs, out = partitionStrings(we.filterRe, out) 733 if len(errmsgs) == 0 { 734 errs = append(errs, fmt.Errorf("%s:%d: missing error %q", we.file, we.lineNum, we.reStr)) 735 continue 736 } 737 matched := false 738 n := len(out) 739 for _, errmsg := range errmsgs { 740 if we.re.MatchString(errmsg) { 741 matched = true 742 } else { 743 out = append(out, errmsg) 744 } 745 } 746 if !matched { 747 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"))) 748 continue 749 } 750 } 751 752 if len(out) > 0 { 753 errs = append(errs, fmt.Errorf("Unmatched Errors:")) 754 for _, errLine := range out { 755 errs = append(errs, fmt.Errorf("%s", errLine)) 756 } 757 } 758 759 if len(errs) == 0 { 760 return nil 761 } 762 if len(errs) == 1 { 763 return errs[0] 764 } 765 var buf bytes.Buffer 766 fmt.Fprintf(&buf, "\n") 767 for _, err := range errs { 768 fmt.Fprintf(&buf, "%s\n", err.Error()) 769 } 770 return errors.New(buf.String()) 771 772} 773 774func partitionStrings(rx *regexp.Regexp, strs []string) (matched, unmatched []string) { 775 for _, s := range strs { 776 if rx.MatchString(s) { 777 matched = append(matched, s) 778 } else { 779 unmatched = append(unmatched, s) 780 } 781 } 782 return 783} 784 785type wantedError struct { 786 reStr string 787 re *regexp.Regexp 788 lineNum int 789 file string 790 filterRe *regexp.Regexp // /^file:linenum\b/m 791} 792 793var ( 794 errRx = regexp.MustCompile(`// (?:GC_)?ERROR (.*)`) 795 errQuotesRx = regexp.MustCompile(`"([^"]*)"`) 796 lineRx = regexp.MustCompile(`LINE(([+-])([0-9]+))?`) 797) 798 799func (t *test) wantedErrors(file, short string) (errs []wantedError) { 800 src, _ := ioutil.ReadFile(file) 801 for i, line := range strings.Split(string(src), "\n") { 802 lineNum := i + 1 803 if strings.Contains(line, "////") { 804 // double comment disables ERROR 805 continue 806 } 807 m := errRx.FindStringSubmatch(line) 808 if m == nil { 809 continue 810 } 811 all := m[1] 812 mm := errQuotesRx.FindAllStringSubmatch(all, -1) 813 if mm == nil { 814 log.Fatalf("%s:%d: invalid errchk line: %s", t.goFileName(), lineNum, line) 815 } 816 for _, m := range mm { 817 rx := lineRx.ReplaceAllStringFunc(m[1], func(m string) string { 818 n := lineNum 819 if strings.HasPrefix(m, "LINE+") { 820 delta, _ := strconv.Atoi(m[5:]) 821 n += delta 822 } else if strings.HasPrefix(m, "LINE-") { 823 delta, _ := strconv.Atoi(m[5:]) 824 n -= delta 825 } 826 return fmt.Sprintf("%s:%d", short, n) 827 }) 828 re, err := regexp.Compile(rx) 829 if err != nil { 830 log.Fatalf("%s:%d: invalid regexp in ERROR line: %v", t.goFileName(), lineNum, err) 831 } 832 filterPattern := fmt.Sprintf(`^(\w+/)?%s:%d[:[]`, regexp.QuoteMeta(short), lineNum) 833 errs = append(errs, wantedError{ 834 reStr: rx, 835 re: re, 836 filterRe: regexp.MustCompile(filterPattern), 837 lineNum: lineNum, 838 file: short, 839 }) 840 } 841 } 842 843 return 844} 845 846var skipOkay = map[string]bool{ 847 "linkx.go": true, // like "run" but wants linker flags 848 "sinit.go": true, 849 "fixedbugs/bug248.go": true, // combines errorcheckdir and rundir in the same dir. 850 "fixedbugs/bug302.go": true, // tests both .$O and .a imports. 851 "fixedbugs/bug345.go": true, // needs the appropriate flags in gc invocation. 852 "fixedbugs/bug369.go": true, // needs compiler flags. 853 "fixedbugs/bug429.go": true, // like "run" but program should fail 854 "bugs/bug395.go": true, 855} 856 857// defaultRunOutputLimit returns the number of runoutput tests that 858// can be executed in parallel. 859func defaultRunOutputLimit() int { 860 const maxArmCPU = 2 861 862 cpu := runtime.NumCPU() 863 if runtime.GOARCH == "arm" && cpu > maxArmCPU { 864 cpu = maxArmCPU 865 } 866 return cpu 867} 868 869// checkShouldTest runs sanity checks on the shouldTest function. 870func checkShouldTest() { 871 assert := func(ok bool, _ string) { 872 if !ok { 873 panic("fail") 874 } 875 } 876 assertNot := func(ok bool, _ string) { assert(!ok, "") } 877 878 // Simple tests. 879 assert(shouldTest("// +build linux", "linux", "arm")) 880 assert(shouldTest("// +build !windows", "linux", "arm")) 881 assertNot(shouldTest("// +build !windows", "windows", "amd64")) 882 883 // A file with no build tags will always be tested. 884 assert(shouldTest("// This is a test.", "os", "arch")) 885 886 // Build tags separated by a space are OR-ed together. 887 assertNot(shouldTest("// +build arm 386", "linux", "amd64")) 888 889 // Build tags seperated by a comma are AND-ed together. 890 assertNot(shouldTest("// +build !windows,!plan9", "windows", "amd64")) 891 assertNot(shouldTest("// +build !windows,!plan9", "plan9", "386")) 892 893 // Build tags on multiple lines are AND-ed together. 894 assert(shouldTest("// +build !windows\n// +build amd64", "linux", "amd64")) 895 assertNot(shouldTest("// +build !windows\n// +build amd64", "windows", "amd64")) 896 897 // Test that (!a OR !b) matches anything. 898 assert(shouldTest("// +build !windows !plan9", "windows", "amd64")) 899} 900 901// envForDir returns a copy of the environment 902// suitable for running in the given directory. 903// The environment is the current process's environment 904// but with an updated $PWD, so that an os.Getwd in the 905// child will be faster. 906func envForDir(dir string) []string { 907 env := os.Environ() 908 for i, kv := range env { 909 if strings.HasPrefix(kv, "PWD=") { 910 env[i] = "PWD=" + dir 911 return env 912 } 913 } 914 env = append(env, "PWD="+dir) 915 return env 916} 917