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