github.com/goplusjs/gopherjs@v1.2.6-0.20211206034512-f187917453b8/tests/run.go (about) 1 // +build ignore 2 3 // skip 4 5 // Copyright 2012 The Go Authors. All rights reserved. 6 // Use of this source code is governed by a BSD-style 7 // license that can be found in the LICENSE file. 8 9 // Run runs tests in the test directory. 10 // 11 // To run manually with summary, verbose output, and full stack traces of of known failures: 12 // 13 // go run run.go -summary -v -show_known_fails 14 // 15 // TODO(bradfitz): docs of some sort, once we figure out how we're changing 16 // headers of files 17 package main 18 19 import ( 20 "bytes" 21 "errors" 22 "flag" 23 "fmt" 24 "hash/fnv" 25 "io" 26 "io/ioutil" 27 "log" 28 "os" 29 "os/exec" 30 "path" 31 "path/filepath" 32 "regexp" 33 "runtime" 34 "sort" 35 "strconv" 36 "strings" 37 "time" 38 "unicode" 39 ) 40 41 // ----------------------------------------------------------------------------- 42 // GOPHERJS: Known test fails for GopherJS compiler. 43 // 44 // TODO: Reduce these to zero or as close as possible. 45 // 46 var knownFails = map[string]failReason{ 47 "fixedbugs/bug114.go": {desc: "fixedbugs/bug114.go:15:27: B32 (untyped int constant 4294967295) overflows int"}, 48 "fixedbugs/bug242.go": {desc: "bad map check 13 false false Error: fail"}, 49 "fixedbugs/bug260.go": {desc: "maybe unsupportedFeature, pointer arithm"}, 50 "fixedbugs/bug262.go": {desc: "Error: fail"}, 51 "fixedbugs/bug273.go": {desc: "BUG: didn't crash: badcap1"}, 52 "fixedbugs/bug328.go": {desc: "incorrect output"}, 53 "fixedbugs/bug347.go": {desc: "BUG: bug347: cannot find caller"}, 54 "fixedbugs/bug348.go": {desc: "BUG: bug348: cannot find caller"}, 55 "fixedbugs/bug352.go": {desc: "BUG: bug352 struct{}"}, 56 "fixedbugs/bug409.go": {desc: "1 2 3 4"}, 57 "fixedbugs/bug433.go": {desc: "Error: [object Object]"}, 58 "fixedbugs/issue10353.go": {desc: "incorrect output"}, 59 "fixedbugs/issue11656.go": {desc: "Error: Native function not implemented: runtime/debug.setPanicOnFault"}, 60 "fixedbugs/issue4085b.go": {desc: "Error: got panic JavaScript error: Invalid typed array length, want len out of range"}, 61 "fixedbugs/issue4316.go": {desc: "Error: runtime error: invalid memory address or nil pointer dereference"}, 62 "fixedbugs/issue4388.go": {desc: "Error: expected <autogenerated>:1 have anonymous function:0"}, 63 "fixedbugs/issue4562.go": {desc: "Error: cannot find issue4562.go on stack"}, 64 "fixedbugs/issue4620.go": {desc: "map[0:1 1:2], Error: m[i] != 2"}, 65 "fixedbugs/issue5856.go": {category: requiresSourceMapSupport}, 66 "fixedbugs/issue6899.go": {desc: "incorrect output -0"}, 67 "fixedbugs/issue7550.go": {desc: "FATAL ERROR: invalid table size Allocation failed - process out of memory"}, 68 "fixedbugs/issue7690.go": {desc: "Error: runtime error: slice bounds out of range"}, 69 "fixedbugs/issue8047.go": {desc: "null"}, 70 "fixedbugs/issue8047b.go": {desc: "Error: [object Object]"}, 71 72 // Failing due to use of os/exec.Command, which is unsupported. Now skipped via !nacl build tag. 73 /*"fixedbugs/bug248.go": {desc: "os/exec.Command unsupported"}, 74 "fixedbugs/bug302.go": {desc: "os/exec.Command unsupported"}, 75 "fixedbugs/bug345.go": {desc: "os/exec.Command unsupported"}, 76 "fixedbugs/bug369.go": {desc: "os/exec.Command unsupported"}, 77 "fixedbugs/bug429_run.go": {desc: "os/exec.Command unsupported"}, 78 "fixedbugs/issue9862_run.go": {desc: "os/exec.Command unsupported"},*/ 79 "fixedbugs/issue10607.go": {desc: "os/exec.Command unsupported"}, 80 "fixedbugs/issue13268.go": {desc: "os/exec.Command unsupported"}, 81 "fixedbugs/issue14636.go": {desc: "os/exec.Command unsupported"}, 82 83 // These are new tests in Go 1.7. 84 "fixedbugs/issue14646.go": {category: unsureIfGopherJSSupportsThisFeature, desc: "tests runtime.Caller behavior in a deferred func in SSA backend... does GopherJS even support runtime.Caller?"}, 85 "fixedbugs/issue15039.go": {desc: "valid bug but deal with after Go 1.7 support is out? it's likely not a regression"}, 86 "fixedbugs/issue15281.go": {desc: "also looks valid but deal with after Go 1.7 support is out? it's likely not a regression"}, 87 "fixedbugs/issue15975.go": {desc: "also looks valid but deal with after Go 1.7 support is out?"}, 88 89 // These are new tests in Go 1.8. 90 "fixedbugs/issue17381.go": {category: unsureIfGopherJSSupportsThisFeature, desc: "tests runtime.{Callers,FuncForPC} behavior in a deferred func with garbage on stack... does GopherJS even support runtime.{Callers,FuncForPC}?"}, 91 "fixedbugs/issue18149.go": {desc: "//line directives with filenames are not correctly parsed, see https://github.com/gopherjs/gopherjs/issues/553."}, 92 93 // These are new tests in Go 1.9. 94 "fixedbugs/issue19182.go": {category: neverTerminates, desc: "needs GOMAXPROCS=2"}, 95 "fixedbugs/issue19040.go": {desc: `panicwrap error text: 96 "runtime error: invalid memory address or nil pointer dereference" 97 want: 98 "value method main.T.F called using nil *T pointer"`}, 99 "fixedbugs/issue19246.go": {desc: "expected nil pointer dereference panic"}, // Issue https://golang.org/issues/19246: Failed to evaluate some zero-sized values when converting them to interfaces. 100 101 // These are new tests in Go 1.10. 102 "fixedbugs/issue21879.go": {desc: "incorrect output related to runtime.Callers, runtime.CallersFrames, etc."}, 103 "fixedbugs/issue21887.go": {desc: "incorrect output (although within spec, not worth fixing) for println(^uint64(0)). got: { '$high': 4294967295, '$low': 4294967295, '$val': [Circular] } want: 18446744073709551615"}, 104 "fixedbugs/issue22083.go": {category: requiresSourceMapSupport}, // Technically, added in Go 1.9.2. 105 "fixedbugs/issue22660.go": {category: notApplicable, desc: "test of gc compiler, uses os/exec.Command"}, 106 "fixedbugs/issue23305.go": {desc: "GopherJS fails to compile println(0xffffffff), maybe because 32-bit arch"}, 107 108 // These are new tests in Go 1.11. 109 "fixedbugs/issue21221.go": {category: usesUnsupportedPackage, desc: "uses unsafe package and compares nil pointers"}, 110 "fixedbugs/issue22662.go": {desc: "line directives not fully working. Error: got /private/var/folders/b8/66r1c5856mqds1mrf2tjtq8w0000gn/T:1; want ??:1"}, 111 "fixedbugs/issue22662b.go": {category: usesUnsupportedPackage, desc: "os/exec.Command unsupported"}, 112 "fixedbugs/issue23188.go": {desc: "incorrect order of evaluation of index operations"}, 113 "fixedbugs/issue24547.go": {desc: "incorrect computing method sets with shadowed methods"}, 114 115 // These are new tests in Go 1.11.5 116 "fixedbugs/issue28688.go": {category: notApplicable, desc: "testing runtime optimisations"}, 117 118 // These are new tests in Go 1.12. 119 "fixedbugs/issue23837.go": {desc: "missing panic on nil pointer-to-empty-struct dereference"}, 120 "fixedbugs/issue27201.go": {desc: "incorrect stack trace for nil dereference in inlined function"}, 121 "fixedbugs/issue27518b.go": {desc: "sigpanic can make dead pointer live again"}, 122 "fixedbugs/issue29190.go": {desc: "append does not fail when length overflows"}, 123 124 // These are new tests in Go 1.13. 125 "fixedbugs/issue15002.go": {desc: "native function not implemented: syscall.Getpagesize"}, 126 "fixedbugs/issue15992.go": {desc: "native function not implemented: syscall.Getpagesize"}, 127 "fixedbugs/issue19113.go": {desc: "did not panic"}, 128 "fixedbugs/issue22326.go": {desc: "incorrect output for print"}, 129 "fixedbugs/issue28748.go": {desc: "reflect error"}, 130 "fixedbugs/issue29504.go": {desc: "did not panic"}, 131 "fixedbugs/issue30116.go": {desc: "index out of range"}, 132 "fixedbugs/issue30116u.go": {desc: "index out of range"}, 133 "fixedbugs/issue30977.go": {category: neverTerminates, desc: "timeout"}, 134 "fixedbugs/issue31546.go": {desc: "throw error"}, 135 "fixedbugs/issue32477.go": {desc: "heap object finalized at the wrong time"}, 136 "fixedbugs/issue32680.go": {desc: "unknown shorthand flag"}, 137 138 // These are new test in Go 1.15 139 "fixedbugs/issue15277.go": {category: neverTerminates, desc: "skip amd64"}, 140 "fixedbugs/issue34395.go": {category: neverTerminates, desc: "FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory"}, 141 "fixedbugs/issue35027.go": {desc: "unknown shorthand flag: 'g' in -gcflags=-d=checkptr"}, 142 "fixedbugs/issue35073.go": {desc: "unknown shorthand flag: 'g' in -gcflags=-d=checkptr"}, 143 "fixedbugs/issue35576.go": {desc: "unknown shorthand flag: 'g' in -gcflags=-d=checkptr"}, 144 "fixedbugs/issue40917.go": {desc: "unknown shorthand flag: 'g' in -gcflags=-d=checkptr"}, 145 "fixedbugs/issue40629.go": {desc: "throw err"}, 146 147 // These are new test in Go 1.16 148 "fixedbugs/issue24491a.go": {desc: "throw err"}, 149 "fixedbugs/issue24491b.go": {desc: "throw err"}, 150 } 151 152 var knownFailsWindows = map[string]failReason{ 153 "fixedbugs/bug130.go": {desc: "fatal error: all goroutines are asleep - deadlock!"}, 154 "fixedbugs/issue18725.go": {desc: "fatal error: all goroutines are asleep - deadlock!"}, 155 "fixedbugs/issue29190.go": {category: neverTerminates, desc: " make([]T, maxInt) time out"}, 156 "fixedbugs/issue29215.go": {category: neverTerminates, desc: "time out"}, 157 "fixedbugs/issue7525.go": {category: neverTerminates, desc: "initialization cycle"}, 158 "fixedbugs/issue7525b.go": {category: neverTerminates, desc: "initialization cycle"}, 159 "fixedbugs/issue7525c.go": {category: neverTerminates, desc: "initialization cycle"}, 160 "fixedbugs/issue7525d.go": {category: neverTerminates, desc: "initialization cycle"}, 161 "fixedbugs/issue7525e.go": {category: neverTerminates, desc: "initialization cycle"}, 162 "fixedbugs/issue7538a.go": {category: neverTerminates, desc: "label _ not declared"}, 163 "fixedbugs/issue7538b.go": {category: neverTerminates, desc: "panic: interface conversion: types.Object is nil, not *types.Label"}, 164 "fixedbugs/issue7547.go": {category: neverTerminates, desc: "time out"}, 165 "fixedbugs/issue7550.go": {category: neverTerminates, desc: "time out"}, 166 } 167 168 type failCategory uint8 169 170 const ( 171 other failCategory = iota 172 neverTerminates // Test never terminates (so avoid starting it). 173 usesUnsupportedPackage // Test fails because it imports an unsupported package, e.g., "unsafe". 174 requiresSourceMapSupport // Test fails without source map support (as configured in CI), because it tries to check filename/line number via runtime.Caller. 175 compilerPanic 176 unsureIfGopherJSSupportsThisFeature 177 notApplicable // Test that doesn't need to run under GopherJS; it doesn't apply to the Go language in a general way. 178 ) 179 180 type failReason struct { 181 category failCategory 182 desc string 183 } 184 185 // ----------------------------------------------------------------------------- 186 187 var ( 188 verbose = flag.Bool("v", false, "verbose. if set, parallelism is set to 1.") 189 numParallel = flag.Int("n", runtime.NumCPU(), "number of parallel tests to run") 190 summary = flag.Bool("summary", false, "show summary of results") 191 showSkips = flag.Bool("show_skips", false, "show skipped tests") 192 showKnownFails = flag.Bool("show_known_fails", false, "show full error output of known fails") 193 updateErrors = flag.Bool("update_errors", false, "update error messages in test file based on compiler output") 194 runoutputLimit = flag.Int("l", defaultRunOutputLimit(), "number of parallel runoutput tests to run") 195 196 shard = flag.Int("shard", 0, "shard index to run. Only applicable if -shards is non-zero.") 197 shards = flag.Int("shards", 0, "number of shards. If 0, all tests are run. This is used by the continuous build.") 198 ) 199 200 var ( 201 goos, goarch string 202 203 // dirs are the directories to look for *.go files in. 204 // TODO(bradfitz): just use all directories? 205 dirs = []string{".", "ken", "chan", "interface", "syntax", "dwarf", "fixedbugs"} 206 207 // ratec controls the max number of tests running at a time. 208 ratec chan bool 209 210 // toRun is the channel of tests to run. 211 // It is nil until the first test is started. 212 toRun chan *test 213 214 // rungatec controls the max number of runoutput tests 215 // executed in parallel as they can each consume a lot of memory. 216 rungatec chan bool 217 ) 218 219 // maxTests is an upper bound on the total number of tests. 220 // It is used as a channel buffer size to make sure sends don't block. 221 const maxTests = 5000 222 223 func main() { 224 flag.Parse() 225 226 // GOPHERJS. 227 err := os.Chdir(filepath.Join(runtime.GOROOT(), "test")) 228 if err != nil { 229 log.Fatalln(err) 230 } 231 232 goos = getenv("GOOS", runtime.GOOS) 233 //goarch = getenv("GOARCH", runtime.GOARCH) 234 // GOPHERJS. 235 goarch = getenv("GOARCH", "js") // We're running this script natively, but the tests are executed with js architecture. 236 237 if *verbose { 238 fmt.Printf("goos: %q, goarch: %q\n", goos, goarch) 239 } 240 if goos == "windows" { 241 for k, v := range knownFailsWindows { 242 knownFails[k] = v 243 } 244 } 245 246 findExecCmd() 247 248 // Disable parallelism if printing or if using a simulator. 249 if *verbose || len(findExecCmd()) > 0 { 250 *numParallel = 1 251 } 252 253 ratec = make(chan bool, *numParallel) 254 rungatec = make(chan bool, *runoutputLimit) 255 256 var tests []*test 257 if flag.NArg() > 0 { 258 for _, arg := range flag.Args() { 259 if arg == "-" || arg == "--" { 260 // Permit running: 261 // $ go run run.go - env.go 262 // $ go run run.go -- env.go 263 // $ go run run.go - ./fixedbugs 264 // $ go run run.go -- ./fixedbugs 265 continue 266 } 267 if fi, err := os.Stat(arg); err == nil && fi.IsDir() { 268 for _, baseGoFile := range goFiles(arg) { 269 tests = append(tests, startTest(arg, baseGoFile)) 270 } 271 } else if strings.HasSuffix(arg, ".go") { 272 dir, file := filepath.Split(arg) 273 tests = append(tests, startTest(dir, file)) 274 } else { 275 log.Fatalf("can't yet deal with non-directory and non-go file %q", arg) 276 } 277 } 278 } else { 279 for _, dir := range dirs { 280 for _, baseGoFile := range goFiles(dir) { 281 tests = append(tests, startTest(dir, baseGoFile)) 282 } 283 } 284 } 285 286 failed := false 287 resCount := map[string]int{} 288 for _, test := range tests { 289 <-test.donec 290 // GOPHERJS. 291 if test.action == "skip" && !*showSkips { 292 continue 293 } 294 status := "ok " 295 errStr := "" 296 // GOPHERJS. 297 if _, ok := knownFails[filepath.ToSlash(test.goFileName())]; ok && test.err != nil { 298 errStr = test.err.Error() 299 test.err = nil 300 status = "knfl" // knfl means known failure. Expect test to fail. 301 } else if ok && test.err == nil { 302 // unok means unexpected okay. Test was expected to fail, but it unexpectedly succeeded. 303 // If this is not an accident, it should be removed from knownFails map. 304 status = "unok" 305 } 306 if _, isSkip := test.err.(skipError); isSkip { 307 test.err = nil 308 errStr = "unexpected skip for " + path.Join(test.dir, test.gofile) + ": " + errStr 309 status = "FAIL" 310 } 311 if test.err != nil { 312 status = "FAIL" 313 errStr = test.err.Error() 314 } 315 if status == "FAIL" { 316 failed = true 317 } 318 // GOPHERJS. 319 if status == "unok" { 320 failed = true 321 } 322 resCount[status]++ 323 if status == "skip" && !*verbose && !*showSkips { 324 continue 325 } 326 dt := fmt.Sprintf("%.3fs", test.dt.Seconds()) 327 if status == "FAIL" { 328 fmt.Printf("# go run run.go -- %s\n%s\nFAIL\t%s\t%s\n", 329 path.Join(test.dir, test.gofile), 330 errStr, test.goFileName(), dt) 331 continue 332 } 333 // GOPHERJS. 334 if status == "knfl" && *showKnownFails { 335 fmt.Printf("# go run run.go -show_known_fails -- %s\n%s\nknfl\t%s\t%s\n", 336 path.Join(test.dir, test.gofile), 337 errStr, test.goFileName(), dt) 338 continue 339 } 340 if !*verbose && status != "unok" { 341 continue 342 } 343 fmt.Printf("%s\t%s\t%s\n", status, test.goFileName(), dt) 344 } 345 346 if *summary { 347 for k, v := range resCount { 348 fmt.Printf("%5d %s\n", v, k) 349 } 350 } 351 352 if failed { 353 os.Exit(1) 354 } 355 } 356 357 func toolPath(name string) string { 358 p := filepath.Join(os.Getenv("GOROOT"), "bin", "tool", name) 359 if _, err := os.Stat(p); err != nil { 360 log.Fatalf("didn't find binary at %s", p) 361 } 362 return p 363 } 364 365 func shardMatch(name string) bool { 366 if *shards == 0 { 367 return true 368 } 369 h := fnv.New32() 370 io.WriteString(h, name) 371 return int(h.Sum32()%uint32(*shards)) == *shard 372 } 373 374 func goFiles(dir string) []string { 375 f, err := os.Open(dir) 376 check(err) 377 dirnames, err := f.Readdirnames(-1) 378 check(err) 379 names := []string{} 380 for _, name := range dirnames { 381 if !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") && shardMatch(name) { 382 names = append(names, name) 383 } 384 } 385 sort.Strings(names) 386 return names 387 } 388 389 type runCmd func(...string) ([]byte, error) 390 391 func compileFile(runcmd runCmd, longname string) (out []byte, err error) { 392 return runcmd("go", "tool", "compile", "-e", longname) 393 } 394 395 func compileInDir(runcmd runCmd, dir string, names ...string) (out []byte, err error) { 396 cmd := []string{"go", "tool", "compile", "-e", "-D", ".", "-I", "."} 397 for _, name := range names { 398 cmd = append(cmd, filepath.Join(dir, name)) 399 } 400 return runcmd(cmd...) 401 } 402 403 func linkFile(runcmd runCmd, goname string) (err error) { 404 pfile := strings.Replace(goname, ".go", ".o", -1) 405 _, err = runcmd("go", "tool", "link", "-w", "-o", "a.exe", "-L", ".", pfile) 406 return 407 } 408 409 // skipError describes why a test was skipped. 410 type skipError string 411 412 func (s skipError) Error() string { return string(s) } 413 414 func check(err error) { 415 if err != nil { 416 log.Fatal(err) 417 } 418 } 419 420 // test holds the state of a test. 421 type test struct { 422 dir, gofile string 423 donec chan bool // closed when done 424 dt time.Duration 425 426 src string 427 action string // "compile", "build", etc. 428 429 tempDir string 430 err error 431 } 432 433 // startTest 434 func startTest(dir, gofile string) *test { 435 t := &test{ 436 dir: dir, 437 gofile: gofile, 438 donec: make(chan bool, 1), 439 } 440 if toRun == nil { 441 toRun = make(chan *test, maxTests) 442 go runTests() 443 } 444 select { 445 case toRun <- t: 446 default: 447 panic("toRun buffer size (maxTests) is too small") 448 } 449 return t 450 } 451 452 // runTests runs tests in parallel, but respecting the order they 453 // were enqueued on the toRun channel. 454 func runTests() { 455 for { 456 ratec <- true 457 t := <-toRun 458 go func() { 459 t.run() 460 <-ratec 461 }() 462 } 463 } 464 465 var cwd, _ = os.Getwd() 466 467 func (t *test) goFileName() string { 468 return filepath.Join(t.dir, t.gofile) 469 } 470 471 func (t *test) goDirName() string { 472 return filepath.Join(t.dir, strings.Replace(t.gofile, ".go", ".dir", -1)) 473 } 474 475 func goDirFiles(longdir string) (filter []os.FileInfo, err error) { 476 files, dirErr := ioutil.ReadDir(longdir) 477 if dirErr != nil { 478 return nil, dirErr 479 } 480 for _, gofile := range files { 481 if filepath.Ext(gofile.Name()) == ".go" { 482 filter = append(filter, gofile) 483 } 484 } 485 return 486 } 487 488 var packageRE = regexp.MustCompile(`(?m)^package (\w+)`) 489 490 func goDirPackages(longdir string) ([][]string, error) { 491 files, err := goDirFiles(longdir) 492 if err != nil { 493 return nil, err 494 } 495 var pkgs [][]string 496 m := make(map[string]int) 497 for _, file := range files { 498 name := file.Name() 499 data, err := ioutil.ReadFile(filepath.Join(longdir, name)) 500 if err != nil { 501 return nil, err 502 } 503 pkgname := packageRE.FindStringSubmatch(string(data)) 504 if pkgname == nil { 505 return nil, fmt.Errorf("cannot find package name in %s", name) 506 } 507 i, ok := m[pkgname[1]] 508 if !ok { 509 i = len(pkgs) 510 pkgs = append(pkgs, nil) 511 m[pkgname[1]] = i 512 } 513 pkgs[i] = append(pkgs[i], name) 514 } 515 return pkgs, nil 516 } 517 518 type context struct { 519 GOOS string 520 GOARCH string 521 } 522 523 // shouldTest looks for build tags in a source file and returns 524 // whether the file should be used according to the tags. 525 func shouldTest(src string, goos, goarch string) (ok bool, whyNot string) { 526 // Custom rule, treat js as equivalent to nacl. 527 if goarch == "js" { 528 goarch = "nacl" 529 } 530 531 for _, line := range strings.Split(src, "\n") { 532 line = strings.TrimSpace(line) 533 if strings.HasPrefix(line, "//") { 534 line = line[2:] 535 } else { 536 continue 537 } 538 line = strings.TrimSpace(line) 539 if len(line) == 0 || line[0] != '+' { 540 continue 541 } 542 ctxt := &context{ 543 GOOS: goos, 544 GOARCH: goarch, 545 } 546 words := strings.Fields(line) 547 if words[0] == "+build" { 548 ok := false 549 for _, word := range words[1:] { 550 if ctxt.match(word) { 551 ok = true 552 break 553 } 554 } 555 if !ok { 556 // no matching tag found. 557 return false, line 558 } 559 } 560 } 561 // no build tags 562 return true, "" 563 } 564 565 func (ctxt *context) match(name string) bool { 566 if name == "" { 567 return false 568 } 569 if i := strings.Index(name, ","); i >= 0 { 570 // comma-separated list 571 return ctxt.match(name[:i]) && ctxt.match(name[i+1:]) 572 } 573 if strings.HasPrefix(name, "!!") { // bad syntax, reject always 574 return false 575 } 576 if strings.HasPrefix(name, "!") { // negation 577 if name[1:] == "js" { 578 return false 579 } 580 return len(name) > 1 && !ctxt.match(name[1:]) 581 } 582 583 // Tags must be letters, digits, underscores or dots. 584 // Unlike in Go identifiers, all digits are fine (e.g., "386"). 585 for _, c := range name { 586 if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' { 587 return false 588 } 589 } 590 591 if name == ctxt.GOOS || name == ctxt.GOARCH { 592 return true 593 } 594 595 return false 596 } 597 598 func init() { checkShouldTest() } 599 600 // run runs a test. 601 func (t *test) run() { 602 start := time.Now() 603 defer func() { 604 t.dt = time.Since(start) 605 close(t.donec) 606 }() 607 608 // GOPHERJS: Some tests may never terminate once started. Avoid starting them. 609 if kf, ok := knownFails[filepath.ToSlash(t.goFileName())]; ok && kf.category == neverTerminates { 610 t.err = skipError("skipping because it doesn't terminate") 611 return 612 } 613 614 srcBytes, err := ioutil.ReadFile(t.goFileName()) 615 if err != nil { 616 t.err = err 617 return 618 } 619 t.src = string(srcBytes) 620 if t.src[0] == '\n' { 621 t.err = skipError("starts with newline") 622 return 623 } 624 625 // Execution recipe stops at first blank line. 626 pos := strings.Index(t.src, "\n\n") 627 if pos == -1 { 628 t.err = errors.New("double newline not found") 629 return 630 } 631 action := t.src[:pos] 632 if nl := strings.Index(action, "\n"); nl >= 0 && strings.Contains(action[:nl], "+build") { 633 // skip first line 634 action = action[nl+1:] 635 } 636 if strings.HasPrefix(action, "//") { 637 action = action[2:] 638 } 639 640 // Check for build constraints only up to the actual code. 641 pkgPos := strings.Index(t.src, "\npackage") 642 if pkgPos == -1 { 643 pkgPos = pos // some files are intentionally malformed 644 } 645 if ok, why := shouldTest(t.src[:pkgPos], goos, goarch); !ok { 646 t.action = "skip" 647 if *showSkips { 648 fmt.Printf("%-20s %-20s: %s\n", t.action, t.goFileName(), why) 649 } 650 return 651 } 652 653 var args, flags []string 654 wantError := false 655 f := strings.Fields(action) 656 if len(f) > 0 { 657 action = f[0] 658 args = f[1:] 659 } 660 661 // GOPHERJS: For now, only run with "run", "cmpout" actions, in "fixedbugs" dir. Skip all others. 662 switch action { 663 case "run", "cmpout": 664 if filepath.Clean(t.dir) != "fixedbugs" { 665 action = "skip" 666 } 667 default: 668 action = "skip" 669 } 670 671 switch action { 672 case "rundircmpout": 673 action = "rundir" 674 t.action = "rundir" 675 case "cmpout": 676 action = "run" // the run case already looks for <dir>/<test>.out files 677 fallthrough 678 case "compile", "compiledir", "build", "run", "runoutput", "rundir": 679 t.action = action 680 case "errorcheck", "errorcheckdir", "errorcheckoutput": 681 t.action = action 682 wantError = true 683 for len(args) > 0 && strings.HasPrefix(args[0], "-") { 684 if args[0] == "-0" { 685 wantError = false 686 } else { 687 flags = append(flags, args[0]) 688 } 689 args = args[1:] 690 } 691 case "skip": 692 t.action = "skip" 693 return 694 default: 695 t.err = skipError("skipped; unknown pattern: " + action) 696 t.action = "??" 697 return 698 } 699 700 t.makeTempDir() 701 defer os.RemoveAll(t.tempDir) 702 703 err = ioutil.WriteFile(filepath.Join(t.tempDir, t.gofile), srcBytes, 0644) 704 check(err) 705 706 // A few tests (of things like the environment) require these to be set. 707 if os.Getenv("GOOS") == "" { 708 os.Setenv("GOOS", runtime.GOOS) 709 } 710 if os.Getenv("GOARCH") == "" { 711 os.Setenv("GOARCH", runtime.GOARCH) 712 } 713 714 useTmp := true 715 runcmd := func(args ...string) ([]byte, error) { 716 cmd := exec.Command(args[0], args[1:]...) 717 var buf bytes.Buffer 718 cmd.Stdout = &buf 719 cmd.Stderr = &buf 720 if useTmp { 721 cmd.Dir = t.tempDir 722 cmd.Env = envForDir(cmd.Dir) 723 } 724 err := cmd.Run() 725 if err != nil { 726 err = fmt.Errorf("%s\n%s", err, buf.Bytes()) 727 } 728 return buf.Bytes(), err 729 } 730 731 long := filepath.Join(cwd, t.goFileName()) 732 switch action { 733 default: 734 t.err = fmt.Errorf("unimplemented action %q", action) 735 736 case "errorcheck": 737 cmdline := []string{"go", "tool", "compile", "-e", "-o", "a.o"} 738 cmdline = append(cmdline, flags...) 739 cmdline = append(cmdline, long) 740 out, err := runcmd(cmdline...) 741 if wantError { 742 if err == nil { 743 t.err = fmt.Errorf("compilation succeeded unexpectedly\n%s", out) 744 return 745 } 746 } else { 747 if err != nil { 748 t.err = err 749 return 750 } 751 } 752 if *updateErrors { 753 t.updateErrors(string(out), long) 754 } 755 t.err = t.errorCheck(string(out), long, t.gofile) 756 return 757 758 case "compile": 759 _, t.err = compileFile(runcmd, long) 760 761 case "compiledir": 762 // Compile all files in the directory in lexicographic order. 763 longdir := filepath.Join(cwd, t.goDirName()) 764 pkgs, err := goDirPackages(longdir) 765 if err != nil { 766 t.err = err 767 return 768 } 769 for _, gofiles := range pkgs { 770 _, t.err = compileInDir(runcmd, longdir, gofiles...) 771 if t.err != nil { 772 return 773 } 774 } 775 776 case "errorcheckdir": 777 // errorcheck all files in lexicographic order 778 // useful for finding importing errors 779 longdir := filepath.Join(cwd, t.goDirName()) 780 pkgs, err := goDirPackages(longdir) 781 if err != nil { 782 t.err = err 783 return 784 } 785 for i, gofiles := range pkgs { 786 out, err := compileInDir(runcmd, longdir, gofiles...) 787 if i == len(pkgs)-1 { 788 if wantError && err == nil { 789 t.err = fmt.Errorf("compilation succeeded unexpectedly\n%s", out) 790 return 791 } else if !wantError && err != nil { 792 t.err = err 793 return 794 } 795 } else if err != nil { 796 t.err = err 797 return 798 } 799 var fullshort []string 800 for _, name := range gofiles { 801 fullshort = append(fullshort, filepath.Join(longdir, name), name) 802 } 803 t.err = t.errorCheck(string(out), fullshort...) 804 if t.err != nil { 805 break 806 } 807 } 808 809 case "rundir": 810 // Compile all files in the directory in lexicographic order. 811 // then link as if the last file is the main package and run it 812 longdir := filepath.Join(cwd, t.goDirName()) 813 pkgs, err := goDirPackages(longdir) 814 if err != nil { 815 t.err = err 816 return 817 } 818 for i, gofiles := range pkgs { 819 _, err := compileInDir(runcmd, longdir, gofiles...) 820 if err != nil { 821 t.err = err 822 return 823 } 824 if i == len(pkgs)-1 { 825 err = linkFile(runcmd, gofiles[0]) 826 if err != nil { 827 t.err = err 828 return 829 } 830 var cmd []string 831 cmd = append(cmd, findExecCmd()...) 832 cmd = append(cmd, filepath.Join(t.tempDir, "a.exe")) 833 cmd = append(cmd, args...) 834 out, err := runcmd(cmd...) 835 if err != nil { 836 t.err = err 837 return 838 } 839 if strings.Replace(string(out), "\r\n", "\n", -1) != t.expectedOutput() { 840 t.err = fmt.Errorf("incorrect output\n%s", out) 841 } 842 } 843 } 844 845 case "build": 846 _, err := runcmd("go", "build", "-o", "a.exe", long) 847 if err != nil { 848 t.err = err 849 } 850 851 case "run": 852 useTmp = false 853 // GOPHERJS. 854 out, err := runcmd(append([]string{"gopherjs", "run", "-q", t.goFileName()}, args...)...) 855 if err != nil { 856 t.err = err 857 return 858 } 859 if strings.Replace(string(out), "\r\n", "\n", -1) != t.expectedOutput() { 860 t.err = fmt.Errorf("incorrect output\n%s", out) 861 } 862 863 case "runoutput": 864 rungatec <- true 865 defer func() { 866 <-rungatec 867 }() 868 useTmp = false 869 out, err := runcmd(append([]string{"go", "run", t.goFileName()}, args...)...) 870 if err != nil { 871 t.err = err 872 return 873 } 874 tfile := filepath.Join(t.tempDir, "tmp__.go") 875 if err := ioutil.WriteFile(tfile, out, 0666); err != nil { 876 t.err = fmt.Errorf("write tempfile:%s", err) 877 return 878 } 879 out, err = runcmd("go", "run", tfile) 880 if err != nil { 881 t.err = err 882 return 883 } 884 if string(out) != t.expectedOutput() { 885 t.err = fmt.Errorf("incorrect output\n%s", out) 886 } 887 888 case "errorcheckoutput": 889 useTmp = false 890 out, err := runcmd(append([]string{"go", "run", t.goFileName()}, args...)...) 891 if err != nil { 892 t.err = err 893 return 894 } 895 tfile := filepath.Join(t.tempDir, "tmp__.go") 896 err = ioutil.WriteFile(tfile, out, 0666) 897 if err != nil { 898 t.err = fmt.Errorf("write tempfile:%s", err) 899 return 900 } 901 cmdline := []string{"go", "tool", "compile", "-e", "-o", "a.o"} 902 cmdline = append(cmdline, flags...) 903 cmdline = append(cmdline, tfile) 904 out, err = runcmd(cmdline...) 905 if wantError { 906 if err == nil { 907 t.err = fmt.Errorf("compilation succeeded unexpectedly\n%s", out) 908 return 909 } 910 } else { 911 if err != nil { 912 t.err = err 913 return 914 } 915 } 916 t.err = t.errorCheck(string(out), tfile, "tmp__.go") 917 return 918 } 919 } 920 921 var execCmd []string 922 923 func findExecCmd() []string { 924 if execCmd != nil { 925 return execCmd 926 } 927 execCmd = []string{} // avoid work the second time 928 if goos == runtime.GOOS && goarch == runtime.GOARCH { 929 return execCmd 930 } 931 path, err := exec.LookPath(fmt.Sprintf("go_%s_%s_exec", goos, goarch)) 932 if err == nil { 933 execCmd = []string{path} 934 } 935 return execCmd 936 } 937 938 func (t *test) String() string { 939 return filepath.Join(t.dir, t.gofile) 940 } 941 942 func (t *test) makeTempDir() { 943 var err error 944 t.tempDir, err = ioutil.TempDir("", "") 945 check(err) 946 } 947 948 func (t *test) expectedOutput() string { 949 filename := filepath.Join(t.dir, t.gofile) 950 filename = filename[:len(filename)-len(".go")] 951 filename += ".out" 952 b, _ := ioutil.ReadFile(filename) 953 return string(b) 954 } 955 956 func splitOutput(out string) []string { 957 // gc error messages continue onto additional lines with leading tabs. 958 // Split the output at the beginning of each line that doesn't begin with a tab. 959 // <autogenerated> lines are impossible to match so those are filtered out. 960 var res []string 961 for _, line := range strings.Split(out, "\n") { 962 if strings.HasSuffix(line, "\r") { // remove '\r', output by compiler on windows 963 line = line[:len(line)-1] 964 } 965 if strings.HasPrefix(line, "\t") { 966 res[len(res)-1] += "\n" + line 967 } else if strings.HasPrefix(line, "go tool") || strings.HasPrefix(line, "<autogenerated>") { 968 continue 969 } else if strings.TrimSpace(line) != "" { 970 res = append(res, line) 971 } 972 } 973 return res 974 } 975 976 func (t *test) errorCheck(outStr string, fullshort ...string) (err error) { 977 defer func() { 978 if *verbose && err != nil { 979 log.Printf("%s gc output:\n%s", t, outStr) 980 } 981 }() 982 var errs []error 983 out := splitOutput(outStr) 984 985 // Cut directory name. 986 for i := range out { 987 for j := 0; j < len(fullshort); j += 2 { 988 full, short := fullshort[j], fullshort[j+1] 989 out[i] = strings.Replace(out[i], full, short, -1) 990 } 991 } 992 993 var want []wantedError 994 for j := 0; j < len(fullshort); j += 2 { 995 full, short := fullshort[j], fullshort[j+1] 996 want = append(want, t.wantedErrors(full, short)...) 997 } 998 999 for _, we := range want { 1000 var errmsgs []string 1001 errmsgs, out = partitionStrings(we.prefix, out) 1002 if len(errmsgs) == 0 { 1003 errs = append(errs, fmt.Errorf("%s:%d: missing error %q", we.file, we.lineNum, we.reStr)) 1004 continue 1005 } 1006 matched := false 1007 n := len(out) 1008 for _, errmsg := range errmsgs { 1009 if we.re.MatchString(errmsg) { 1010 matched = true 1011 } else { 1012 out = append(out, errmsg) 1013 } 1014 } 1015 if !matched { 1016 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"))) 1017 continue 1018 } 1019 } 1020 1021 if len(out) > 0 { 1022 errs = append(errs, fmt.Errorf("Unmatched Errors:")) 1023 for _, errLine := range out { 1024 errs = append(errs, fmt.Errorf("%s", errLine)) 1025 } 1026 } 1027 1028 if len(errs) == 0 { 1029 return nil 1030 } 1031 if len(errs) == 1 { 1032 return errs[0] 1033 } 1034 var buf bytes.Buffer 1035 fmt.Fprintf(&buf, "\n") 1036 for _, err := range errs { 1037 fmt.Fprintf(&buf, "%s\n", err.Error()) 1038 } 1039 return errors.New(buf.String()) 1040 } 1041 1042 func (t *test) updateErrors(out string, file string) { 1043 // Read in source file. 1044 src, err := ioutil.ReadFile(file) 1045 if err != nil { 1046 fmt.Fprintln(os.Stderr, err) 1047 return 1048 } 1049 lines := strings.Split(string(src), "\n") 1050 // Remove old errors. 1051 for i, ln := range lines { 1052 pos := strings.Index(ln, " // ERROR ") 1053 if pos >= 0 { 1054 lines[i] = ln[:pos] 1055 } 1056 } 1057 // Parse new errors. 1058 errors := make(map[int]map[string]bool) 1059 tmpRe := regexp.MustCompile(`autotmp_[0-9]+`) 1060 for _, errStr := range splitOutput(out) { 1061 colon1 := strings.Index(errStr, ":") 1062 if colon1 < 0 || errStr[:colon1] != file { 1063 continue 1064 } 1065 colon2 := strings.Index(errStr[colon1+1:], ":") 1066 if colon2 < 0 { 1067 continue 1068 } 1069 colon2 += colon1 + 1 1070 line, err := strconv.Atoi(errStr[colon1+1 : colon2]) 1071 line-- 1072 if err != nil || line < 0 || line >= len(lines) { 1073 continue 1074 } 1075 msg := errStr[colon2+2:] 1076 for _, r := range []string{`\`, `*`, `+`, `[`, `]`, `(`, `)`} { 1077 msg = strings.Replace(msg, r, `\`+r, -1) 1078 } 1079 msg = strings.Replace(msg, `"`, `.`, -1) 1080 msg = tmpRe.ReplaceAllLiteralString(msg, `autotmp_[0-9]+`) 1081 if errors[line] == nil { 1082 errors[line] = make(map[string]bool) 1083 } 1084 errors[line][msg] = true 1085 } 1086 // Add new errors. 1087 for line, errs := range errors { 1088 var sorted []string 1089 for e := range errs { 1090 sorted = append(sorted, e) 1091 } 1092 sort.Strings(sorted) 1093 lines[line] += " // ERROR" 1094 for _, e := range sorted { 1095 lines[line] += fmt.Sprintf(` "%s$"`, e) 1096 } 1097 } 1098 // Write new file. 1099 err = ioutil.WriteFile(file, []byte(strings.Join(lines, "\n")), 0640) 1100 if err != nil { 1101 fmt.Fprintln(os.Stderr, err) 1102 return 1103 } 1104 // Polish. 1105 exec.Command("go", "fmt", file).CombinedOutput() 1106 } 1107 1108 // matchPrefix reports whether s is of the form ^(.*/)?prefix(:|[), 1109 // That is, it needs the file name prefix followed by a : or a [, 1110 // and possibly preceded by a directory name. 1111 func matchPrefix(s, prefix string) bool { 1112 i := strings.Index(s, ":") 1113 if i < 0 { 1114 return false 1115 } 1116 j := strings.LastIndex(s[:i], "/") 1117 s = s[j+1:] 1118 if len(s) <= len(prefix) || s[:len(prefix)] != prefix { 1119 return false 1120 } 1121 switch s[len(prefix)] { 1122 case '[', ':': 1123 return true 1124 } 1125 return false 1126 } 1127 1128 func partitionStrings(prefix string, strs []string) (matched, unmatched []string) { 1129 for _, s := range strs { 1130 if matchPrefix(s, prefix) { 1131 matched = append(matched, s) 1132 } else { 1133 unmatched = append(unmatched, s) 1134 } 1135 } 1136 return 1137 } 1138 1139 type wantedError struct { 1140 reStr string 1141 re *regexp.Regexp 1142 lineNum int 1143 file string 1144 prefix string 1145 } 1146 1147 var ( 1148 errRx = regexp.MustCompile(`// (?:GC_)?ERROR (.*)`) 1149 errQuotesRx = regexp.MustCompile(`"([^"]*)"`) 1150 lineRx = regexp.MustCompile(`LINE(([+-])([0-9]+))?`) 1151 ) 1152 1153 func (t *test) wantedErrors(file, short string) (errs []wantedError) { 1154 cache := make(map[string]*regexp.Regexp) 1155 1156 src, _ := ioutil.ReadFile(file) 1157 for i, line := range strings.Split(string(src), "\n") { 1158 lineNum := i + 1 1159 if strings.Contains(line, "////") { 1160 // double comment disables ERROR 1161 continue 1162 } 1163 m := errRx.FindStringSubmatch(line) 1164 if m == nil { 1165 continue 1166 } 1167 all := m[1] 1168 mm := errQuotesRx.FindAllStringSubmatch(all, -1) 1169 if mm == nil { 1170 log.Fatalf("%s:%d: invalid errchk line: %s", t.goFileName(), lineNum, line) 1171 } 1172 for _, m := range mm { 1173 rx := lineRx.ReplaceAllStringFunc(m[1], func(m string) string { 1174 n := lineNum 1175 if strings.HasPrefix(m, "LINE+") { 1176 delta, _ := strconv.Atoi(m[5:]) 1177 n += delta 1178 } else if strings.HasPrefix(m, "LINE-") { 1179 delta, _ := strconv.Atoi(m[5:]) 1180 n -= delta 1181 } 1182 return fmt.Sprintf("%s:%d", short, n) 1183 }) 1184 re := cache[rx] 1185 if re == nil { 1186 var err error 1187 re, err = regexp.Compile(rx) 1188 if err != nil { 1189 log.Fatalf("%s:%d: invalid regexp \"%s\" in ERROR line: %v", t.goFileName(), lineNum, rx, err) 1190 } 1191 cache[rx] = re 1192 } 1193 prefix := fmt.Sprintf("%s:%d", short, lineNum) 1194 errs = append(errs, wantedError{ 1195 reStr: rx, 1196 re: re, 1197 prefix: prefix, 1198 lineNum: lineNum, 1199 file: short, 1200 }) 1201 } 1202 } 1203 1204 return 1205 } 1206 1207 // defaultRunOutputLimit returns the number of runoutput tests that 1208 // can be executed in parallel. 1209 func defaultRunOutputLimit() int { 1210 const maxArmCPU = 2 1211 1212 cpu := runtime.NumCPU() 1213 if runtime.GOARCH == "arm" && cpu > maxArmCPU { 1214 cpu = maxArmCPU 1215 } 1216 return cpu 1217 } 1218 1219 // checkShouldTest runs sanity checks on the shouldTest function. 1220 func checkShouldTest() { 1221 assert := func(ok bool, _ string) { 1222 if !ok { 1223 panic("fail") 1224 } 1225 } 1226 assertNot := func(ok bool, _ string) { assert(!ok, "") } 1227 1228 // Simple tests. 1229 assert(shouldTest("// +build linux", "linux", "arm")) 1230 assert(shouldTest("// +build !windows", "linux", "arm")) 1231 assertNot(shouldTest("// +build !windows", "windows", "amd64")) 1232 1233 // A file with no build tags will always be tested. 1234 assert(shouldTest("// This is a test.", "os", "arch")) 1235 1236 // Build tags separated by a space are OR-ed together. 1237 assertNot(shouldTest("// +build arm 386", "linux", "amd64")) 1238 1239 // Build tags separated by a comma are AND-ed together. 1240 assertNot(shouldTest("// +build !windows,!plan9", "windows", "amd64")) 1241 assertNot(shouldTest("// +build !windows,!plan9", "plan9", "386")) 1242 1243 // Build tags on multiple lines are AND-ed together. 1244 assert(shouldTest("// +build !windows\n// +build amd64", "linux", "amd64")) 1245 assertNot(shouldTest("// +build !windows\n// +build amd64", "windows", "amd64")) 1246 1247 // Test that (!a OR !b) matches anything. 1248 assert(shouldTest("// +build !windows !plan9", "windows", "amd64")) 1249 1250 // GOPHERJS: Custom rule, test that don't run on nacl should also not run on js. 1251 assertNot(shouldTest("// +build !nacl,!plan9,!windows", "darwin", "js")) 1252 } 1253 1254 // envForDir returns a copy of the environment 1255 // suitable for running in the given directory. 1256 // The environment is the current process's environment 1257 // but with an updated $PWD, so that an os.Getwd in the 1258 // child will be faster. 1259 func envForDir(dir string) []string { 1260 env := os.Environ() 1261 for i, kv := range env { 1262 if strings.HasPrefix(kv, "PWD=") { 1263 env[i] = "PWD=" + dir 1264 return env 1265 } 1266 } 1267 env = append(env, "PWD="+dir) 1268 return env 1269 } 1270 1271 func getenv(key, def string) string { 1272 value := os.Getenv(key) 1273 if value != "" { 1274 return value 1275 } 1276 return def 1277 }