blob: 483775d841f42808670c3ec096f62f8b6b2ee566 [file] [log] [blame]
Shenghou Mae2662832012-03-22 02:14:44 +08001// skip
Brad Fitzpatrickce837b32012-02-21 14:28:49 +11002
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.
Rémy Oudompheng9aef20e2012-12-22 19:16:31 +01008//
Brad Fitzpatrickce837b32012-02-21 14:28:49 +11009// 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"
Russ Coxcd22afa2012-09-23 13:16:14 -040023 "path"
Brad Fitzpatrickce837b32012-02-21 14:28:49 +110024 "path/filepath"
25 "regexp"
26 "runtime"
27 "sort"
28 "strconv"
29 "strings"
Russ Coxde8549d2013-12-10 14:02:42 -050030 "time"
Anthony Martina5385582013-08-13 12:25:41 -040031 "unicode"
Brad Fitzpatrickce837b32012-02-21 14:28:49 +110032)
33
34var (
Dave Cheneydc756702013-01-12 17:52:52 +110035 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")
Brad Fitzpatrickce837b32012-02-21 14:28:49 +110040)
41
42var (
43 // gc and ld are [568][gl].
44 gc, ld string
45
46 // letter is the build.ArchChar
47 letter string
David du Colombier6d20e722014-08-01 22:34:36 +020048
Dave Cheney7c8280c2014-02-25 09:47:42 -050049 goos, goarch string
Brad Fitzpatrickce837b32012-02-21 14:28:49 +110050
51 // dirs are the directories to look for *.go files in.
52 // TODO(bradfitz): just use all directories?
53 dirs = []string{".", "ken", "chan", "interface", "syntax", "dwarf", "fixedbugs", "bugs"}
54
55 // ratec controls the max number of tests running at a time.
56 ratec chan bool
57
58 // toRun is the channel of tests to run.
59 // It is nil until the first test is started.
60 toRun chan *test
Dave Cheneydc756702013-01-12 17:52:52 +110061
62 // rungatec controls the max number of runoutput tests
63 // executed in parallel as they can each consume a lot of memory.
64 rungatec chan bool
Brad Fitzpatrickce837b32012-02-21 14:28:49 +110065)
66
67// maxTests is an upper bound on the total number of tests.
68// It is used as a channel buffer size to make sure sends don't block.
69const maxTests = 5000
70
71func main() {
72 flag.Parse()
Shenghou Ma47ee9822012-03-07 12:43:25 +080073
David du Colombier748e5db2014-07-24 23:18:54 +020074 goos = getenv("GOOS", runtime.GOOS)
75 goarch = getenv("GOARCH", runtime.GOARCH)
76
Dave Cheney7c8280c2014-02-25 09:47:42 -050077 findExecCmd()
78
Russ Cox4895f0d2014-05-28 01:01:08 -040079 // Disable parallelism if printing or if using a simulator.
80 if *verbose || len(findExecCmd()) > 0 {
81 *numParallel = 1
82 }
83
Brad Fitzpatrickce837b32012-02-21 14:28:49 +110084 ratec = make(chan bool, *numParallel)
Dave Cheneydc756702013-01-12 17:52:52 +110085 rungatec = make(chan bool, *runoutputLimit)
Brad Fitzpatrickce837b32012-02-21 14:28:49 +110086 var err error
Shenghou Maeb5af842012-03-06 03:34:53 +080087 letter, err = build.ArchChar(build.Default.GOARCH)
Brad Fitzpatrickce837b32012-02-21 14:28:49 +110088 check(err)
89 gc = letter + "g"
90 ld = letter + "l"
91
92 var tests []*test
93 if flag.NArg() > 0 {
94 for _, arg := range flag.Args() {
95 if arg == "-" || arg == "--" {
Shenghou Ma1e954292012-08-07 09:38:35 +080096 // Permit running:
Brad Fitzpatrickce837b32012-02-21 14:28:49 +110097 // $ go run run.go - env.go
98 // $ go run run.go -- env.go
Shenghou Ma1e954292012-08-07 09:38:35 +080099 // $ go run run.go - ./fixedbugs
100 // $ go run run.go -- ./fixedbugs
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100101 continue
102 }
Shenghou Ma1e954292012-08-07 09:38:35 +0800103 if fi, err := os.Stat(arg); err == nil && fi.IsDir() {
104 for _, baseGoFile := range goFiles(arg) {
105 tests = append(tests, startTest(arg, baseGoFile))
106 }
107 } else if strings.HasSuffix(arg, ".go") {
108 dir, file := filepath.Split(arg)
109 tests = append(tests, startTest(dir, file))
110 } else {
111 log.Fatalf("can't yet deal with non-directory and non-go file %q", arg)
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100112 }
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100113 }
114 } else {
115 for _, dir := range dirs {
116 for _, baseGoFile := range goFiles(dir) {
117 tests = append(tests, startTest(dir, baseGoFile))
118 }
119 }
120 }
121
122 failed := false
123 resCount := map[string]int{}
124 for _, test := range tests {
David du Colombier6d20e722014-08-01 22:34:36 +0200125 <-test.donec
Russ Coxde8549d2013-12-10 14:02:42 -0500126 status := "ok "
127 errStr := ""
128 if _, isSkip := test.err.(skipError); isSkip {
129 status = "skip"
130 test.err = nil
131 if !skipOkay[path.Join(test.dir, test.gofile)] {
132 errStr = "unexpected skip for " + path.Join(test.dir, test.gofile) + ": " + errStr
133 status = "FAIL"
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100134 }
135 }
Russ Coxde8549d2013-12-10 14:02:42 -0500136 if test.err != nil {
137 status = "FAIL"
138 errStr = test.err.Error()
139 }
140 if status == "FAIL" {
Russ Coxcd22afa2012-09-23 13:16:14 -0400141 failed = true
142 }
Russ Coxde8549d2013-12-10 14:02:42 -0500143 resCount[status]++
144 if status == "skip" && !*verbose && !*showSkips {
Brad Fitzpatricka55a5c82012-02-24 12:52:15 +1100145 continue
146 }
Russ Coxde8549d2013-12-10 14:02:42 -0500147 dt := fmt.Sprintf("%.3fs", test.dt.Seconds())
148 if status == "FAIL" {
149 fmt.Printf("# go run run.go -- %s\n%s\nFAIL\t%s\t%s\n",
150 path.Join(test.dir, test.gofile),
151 errStr, test.goFileName(), dt)
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100152 continue
Brad Fitzpatricka55a5c82012-02-24 12:52:15 +1100153 }
Russ Coxde8549d2013-12-10 14:02:42 -0500154 if !*verbose {
155 continue
156 }
157 fmt.Printf("%s\t%s\t%s\n", status, test.goFileName(), dt)
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100158 }
159
160 if *summary {
161 for k, v := range resCount {
162 fmt.Printf("%5d %s\n", v, k)
163 }
164 }
165
166 if failed {
167 os.Exit(1)
168 }
169}
170
171func toolPath(name string) string {
172 p := filepath.Join(os.Getenv("GOROOT"), "bin", "tool", name)
173 if _, err := os.Stat(p); err != nil {
174 log.Fatalf("didn't find binary at %s", p)
175 }
176 return p
177}
178
179func goFiles(dir string) []string {
180 f, err := os.Open(dir)
181 check(err)
182 dirnames, err := f.Readdirnames(-1)
183 check(err)
184 names := []string{}
185 for _, name := range dirnames {
Russ Coxc978a5a2012-03-08 14:03:40 -0500186 if !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") {
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100187 names = append(names, name)
188 }
189 }
190 sort.Strings(names)
191 return names
192}
193
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200194type runCmd func(...string) ([]byte, error)
195
196func compileFile(runcmd runCmd, longname string) (out []byte, err error) {
197 return runcmd("go", "tool", gc, "-e", longname)
198}
199
Russ Cox8850d142013-01-02 15:31:49 -0500200func compileInDir(runcmd runCmd, dir string, names ...string) (out []byte, err error) {
201 cmd := []string{"go", "tool", gc, "-e", "-D", ".", "-I", "."}
202 for _, name := range names {
203 cmd = append(cmd, filepath.Join(dir, name))
204 }
205 return runcmd(cmd...)
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200206}
207
208func linkFile(runcmd runCmd, goname string) (err error) {
209 pfile := strings.Replace(goname, ".go", "."+letter, -1)
Russ Coxae38b032014-02-19 10:01:15 -0500210 _, err = runcmd("go", "tool", ld, "-w", "-o", "a.exe", "-L", ".", pfile)
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200211 return
212}
213
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100214// skipError describes why a test was skipped.
215type skipError string
216
217func (s skipError) Error() string { return string(s) }
218
219func check(err error) {
220 if err != nil {
221 log.Fatal(err)
222 }
223}
224
225// test holds the state of a test.
226type test struct {
227 dir, gofile string
228 donec chan bool // closed when done
David du Colombier6d20e722014-08-01 22:34:36 +0200229 dt time.Duration
230
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100231 src string
Ian Lance Taylore08008e2012-11-07 12:33:54 -0800232 action string // "compile", "build", etc.
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100233
234 tempDir string
235 err error
236}
237
Rémy Oudompheng9aef20e2012-12-22 19:16:31 +0100238// startTest
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100239func startTest(dir, gofile string) *test {
240 t := &test{
241 dir: dir,
242 gofile: gofile,
243 donec: make(chan bool, 1),
244 }
245 if toRun == nil {
246 toRun = make(chan *test, maxTests)
247 go runTests()
248 }
249 select {
250 case toRun <- t:
251 default:
252 panic("toRun buffer size (maxTests) is too small")
253 }
254 return t
255}
256
257// runTests runs tests in parallel, but respecting the order they
258// were enqueued on the toRun channel.
259func runTests() {
260 for {
261 ratec <- true
262 t := <-toRun
263 go func() {
264 t.run()
265 <-ratec
266 }()
267 }
268}
269
Russ Cox105c5fa2012-03-07 01:54:39 -0500270var cwd, _ = os.Getwd()
271
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100272func (t *test) goFileName() string {
273 return filepath.Join(t.dir, t.gofile)
274}
275
Rémy Oudomphengadc93372012-07-30 21:12:05 +0200276func (t *test) goDirName() string {
277 return filepath.Join(t.dir, strings.Replace(t.gofile, ".go", ".dir", -1))
278}
279
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200280func goDirFiles(longdir string) (filter []os.FileInfo, err error) {
281 files, dirErr := ioutil.ReadDir(longdir)
282 if dirErr != nil {
283 return nil, dirErr
284 }
285 for _, gofile := range files {
286 if filepath.Ext(gofile.Name()) == ".go" {
287 filter = append(filter, gofile)
288 }
289 }
290 return
291}
292
Russ Cox8850d142013-01-02 15:31:49 -0500293var packageRE = regexp.MustCompile(`(?m)^package (\w+)`)
294
295func goDirPackages(longdir string) ([][]string, error) {
296 files, err := goDirFiles(longdir)
297 if err != nil {
298 return nil, err
299 }
300 var pkgs [][]string
301 m := make(map[string]int)
302 for _, file := range files {
303 name := file.Name()
304 data, err := ioutil.ReadFile(filepath.Join(longdir, name))
305 if err != nil {
306 return nil, err
307 }
308 pkgname := packageRE.FindStringSubmatch(string(data))
309 if pkgname == nil {
310 return nil, fmt.Errorf("cannot find package name in %s", name)
311 }
312 i, ok := m[pkgname[1]]
313 if !ok {
314 i = len(pkgs)
315 pkgs = append(pkgs, nil)
316 m[pkgname[1]] = i
317 }
318 pkgs[i] = append(pkgs[i], name)
319 }
320 return pkgs, nil
321}
Rémy Oudomphengc3836ed2013-01-11 22:00:48 +0100322
Anthony Martina5385582013-08-13 12:25:41 -0400323type context struct {
324 GOOS string
325 GOARCH string
326}
327
Rémy Oudompheng4f6a2b92013-01-28 21:29:45 +0100328// shouldTest looks for build tags in a source file and returns
329// whether the file should be used according to the tags.
330func shouldTest(src string, goos, goarch string) (ok bool, whyNot string) {
331 if idx := strings.Index(src, "\npackage"); idx >= 0 {
332 src = src[:idx]
333 }
Rémy Oudompheng4f6a2b92013-01-28 21:29:45 +0100334 for _, line := range strings.Split(src, "\n") {
335 line = strings.TrimSpace(line)
336 if strings.HasPrefix(line, "//") {
337 line = line[2:]
338 } else {
339 continue
340 }
341 line = strings.TrimSpace(line)
342 if len(line) == 0 || line[0] != '+' {
343 continue
344 }
Anthony Martina5385582013-08-13 12:25:41 -0400345 ctxt := &context{
346 GOOS: goos,
347 GOARCH: goarch,
348 }
Rémy Oudompheng4f6a2b92013-01-28 21:29:45 +0100349 words := strings.Fields(line)
350 if words[0] == "+build" {
Anthony Martina5385582013-08-13 12:25:41 -0400351 ok := false
352 for _, word := range words[1:] {
353 if ctxt.match(word) {
354 ok = true
355 break
Rémy Oudompheng4f6a2b92013-01-28 21:29:45 +0100356 }
357 }
Anthony Martina5385582013-08-13 12:25:41 -0400358 if !ok {
359 // no matching tag found.
360 return false, line
361 }
Rémy Oudompheng4f6a2b92013-01-28 21:29:45 +0100362 }
363 }
Anthony Martina5385582013-08-13 12:25:41 -0400364 // no build tags
Rémy Oudompheng4f6a2b92013-01-28 21:29:45 +0100365 return true, ""
366}
367
Anthony Martina5385582013-08-13 12:25:41 -0400368func (ctxt *context) match(name string) bool {
369 if name == "" {
370 return false
371 }
372 if i := strings.Index(name, ","); i >= 0 {
373 // comma-separated list
374 return ctxt.match(name[:i]) && ctxt.match(name[i+1:])
375 }
376 if strings.HasPrefix(name, "!!") { // bad syntax, reject always
377 return false
378 }
379 if strings.HasPrefix(name, "!") { // negation
380 return len(name) > 1 && !ctxt.match(name[1:])
381 }
382
383 // Tags must be letters, digits, underscores or dots.
384 // Unlike in Go identifiers, all digits are fine (e.g., "386").
385 for _, c := range name {
386 if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' {
387 return false
388 }
389 }
390
391 if name == ctxt.GOOS || name == ctxt.GOARCH {
392 return true
393 }
394
395 return false
396}
397
Rémy Oudompheng4f6a2b92013-01-28 21:29:45 +0100398func init() { checkShouldTest() }
399
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100400// run runs a test.
401func (t *test) run() {
Russ Coxde8549d2013-12-10 14:02:42 -0500402 start := time.Now()
403 defer func() {
404 t.dt = time.Since(start)
405 close(t.donec)
406 }()
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100407
408 srcBytes, err := ioutil.ReadFile(t.goFileName())
409 if err != nil {
410 t.err = err
411 return
412 }
413 t.src = string(srcBytes)
414 if t.src[0] == '\n' {
415 t.err = skipError("starts with newline")
416 return
417 }
418 pos := strings.Index(t.src, "\n\n")
419 if pos == -1 {
420 t.err = errors.New("double newline not found")
421 return
422 }
Dave Cheney7c8280c2014-02-25 09:47:42 -0500423 if ok, why := shouldTest(t.src, goos, goarch); !ok {
Rémy Oudompheng4f6a2b92013-01-28 21:29:45 +0100424 t.action = "skip"
425 if *showSkips {
426 fmt.Printf("%-20s %-20s: %s\n", t.action, t.goFileName(), why)
427 }
428 return
429 }
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100430 action := t.src[:pos]
Rémy Oudompheng4f6a2b92013-01-28 21:29:45 +0100431 if nl := strings.Index(action, "\n"); nl >= 0 && strings.Contains(action[:nl], "+build") {
432 // skip first line
433 action = action[nl+1:]
434 }
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100435 if strings.HasPrefix(action, "//") {
436 action = action[2:]
437 }
Russ Coxc978a5a2012-03-08 14:03:40 -0500438
Russ Coxcd22afa2012-09-23 13:16:14 -0400439 var args, flags []string
440 wantError := false
Russ Cox105c5fa2012-03-07 01:54:39 -0500441 f := strings.Fields(action)
442 if len(f) > 0 {
443 action = f[0]
444 args = f[1:]
445 }
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100446
447 switch action {
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200448 case "rundircmpout":
449 action = "rundir"
450 t.action = "rundir"
Brad Fitzpatricke014cf02012-02-24 13:17:26 +1100451 case "cmpout":
452 action = "run" // the run case already looks for <dir>/<test>.out files
453 fallthrough
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200454 case "compile", "compiledir", "build", "run", "runoutput", "rundir":
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100455 t.action = action
Ian Lance Taylore08008e2012-11-07 12:33:54 -0800456 case "errorcheck", "errorcheckdir", "errorcheckoutput":
Russ Coxcd22afa2012-09-23 13:16:14 -0400457 t.action = action
458 wantError = true
459 for len(args) > 0 && strings.HasPrefix(args[0], "-") {
460 if args[0] == "-0" {
461 wantError = false
462 } else {
463 flags = append(flags, args[0])
464 }
465 args = args[1:]
466 }
Shenghou Mae2662832012-03-22 02:14:44 +0800467 case "skip":
468 t.action = "skip"
469 return
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100470 default:
471 t.err = skipError("skipped; unknown pattern: " + action)
472 t.action = "??"
473 return
474 }
475
476 t.makeTempDir()
477 defer os.RemoveAll(t.tempDir)
478
479 err = ioutil.WriteFile(filepath.Join(t.tempDir, t.gofile), srcBytes, 0644)
480 check(err)
Russ Coxc978a5a2012-03-08 14:03:40 -0500481
Russ Cox71247362012-03-07 02:22:08 -0500482 // A few tests (of things like the environment) require these to be set.
Dave Cheney7c8280c2014-02-25 09:47:42 -0500483 if os.Getenv("GOOS") == "" {
484 os.Setenv("GOOS", runtime.GOOS)
485 }
486 if os.Getenv("GOARCH") == "" {
487 os.Setenv("GOARCH", runtime.GOARCH)
488 }
Russ Cox71247362012-03-07 02:22:08 -0500489
Russ Cox105c5fa2012-03-07 01:54:39 -0500490 useTmp := true
491 runcmd := func(args ...string) ([]byte, error) {
492 cmd := exec.Command(args[0], args[1:]...)
493 var buf bytes.Buffer
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100494 cmd.Stdout = &buf
495 cmd.Stderr = &buf
Russ Cox105c5fa2012-03-07 01:54:39 -0500496 if useTmp {
497 cmd.Dir = t.tempDir
Russ Cox551f3f22013-02-14 14:21:26 -0500498 cmd.Env = envForDir(cmd.Dir)
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100499 }
Russ Cox105c5fa2012-03-07 01:54:39 -0500500 err := cmd.Run()
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200501 if err != nil {
502 err = fmt.Errorf("%s\n%s", err, buf.Bytes())
503 }
Russ Cox105c5fa2012-03-07 01:54:39 -0500504 return buf.Bytes(), err
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100505 }
506
Russ Cox105c5fa2012-03-07 01:54:39 -0500507 long := filepath.Join(cwd, t.goFileName())
Russ Coxc978a5a2012-03-08 14:03:40 -0500508 switch action {
Russ Cox105c5fa2012-03-07 01:54:39 -0500509 default:
510 t.err = fmt.Errorf("unimplemented action %q", action)
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100511
Russ Cox105c5fa2012-03-07 01:54:39 -0500512 case "errorcheck":
Russ Coxcd22afa2012-09-23 13:16:14 -0400513 cmdline := []string{"go", "tool", gc, "-e", "-o", "a." + letter}
514 cmdline = append(cmdline, flags...)
515 cmdline = append(cmdline, long)
516 out, err := runcmd(cmdline...)
517 if wantError {
518 if err == nil {
519 t.err = fmt.Errorf("compilation succeeded unexpectedly\n%s", out)
520 return
521 }
522 } else {
523 if err != nil {
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200524 t.err = err
Russ Coxcd22afa2012-09-23 13:16:14 -0400525 return
526 }
527 }
Russ Cox105c5fa2012-03-07 01:54:39 -0500528 t.err = t.errorCheck(string(out), long, t.gofile)
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100529 return
Russ Coxc978a5a2012-03-08 14:03:40 -0500530
Russ Cox105c5fa2012-03-07 01:54:39 -0500531 case "compile":
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200532 _, t.err = compileFile(runcmd, long)
Russ Coxc978a5a2012-03-08 14:03:40 -0500533
Rémy Oudomphengadc93372012-07-30 21:12:05 +0200534 case "compiledir":
535 // Compile all files in the directory in lexicographic order.
536 longdir := filepath.Join(cwd, t.goDirName())
Russ Cox8850d142013-01-02 15:31:49 -0500537 pkgs, err := goDirPackages(longdir)
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200538 if err != nil {
539 t.err = err
Rémy Oudomphengadc93372012-07-30 21:12:05 +0200540 return
541 }
Russ Cox8850d142013-01-02 15:31:49 -0500542 for _, gofiles := range pkgs {
543 _, t.err = compileInDir(runcmd, longdir, gofiles...)
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200544 if t.err != nil {
545 return
Alex Brainman1df9ee02012-08-06 14:56:39 +1000546 }
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200547 }
548
549 case "errorcheckdir":
550 // errorcheck all files in lexicographic order
551 // useful for finding importing errors
552 longdir := filepath.Join(cwd, t.goDirName())
Russ Cox8850d142013-01-02 15:31:49 -0500553 pkgs, err := goDirPackages(longdir)
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200554 if err != nil {
555 t.err = err
556 return
557 }
Russ Cox8850d142013-01-02 15:31:49 -0500558 for i, gofiles := range pkgs {
559 out, err := compileInDir(runcmd, longdir, gofiles...)
560 if i == len(pkgs)-1 {
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200561 if wantError && err == nil {
562 t.err = fmt.Errorf("compilation succeeded unexpectedly\n%s", out)
563 return
564 } else if !wantError && err != nil {
565 t.err = err
566 return
567 }
568 } else if err != nil {
569 t.err = err
570 return
571 }
Russ Cox8850d142013-01-02 15:31:49 -0500572 var fullshort []string
573 for _, name := range gofiles {
574 fullshort = append(fullshort, filepath.Join(longdir, name), name)
575 }
576 t.err = t.errorCheck(string(out), fullshort...)
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200577 if t.err != nil {
Rémy Oudomphengadc93372012-07-30 21:12:05 +0200578 break
579 }
580 }
581
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200582 case "rundir":
583 // Compile all files in the directory in lexicographic order.
584 // then link as if the last file is the main package and run it
585 longdir := filepath.Join(cwd, t.goDirName())
Rémy Oudomphengc3836ed2013-01-11 22:00:48 +0100586 pkgs, err := goDirPackages(longdir)
Russ Cox105c5fa2012-03-07 01:54:39 -0500587 if err != nil {
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200588 t.err = err
589 return
590 }
Rémy Oudomphengc3836ed2013-01-11 22:00:48 +0100591 for i, gofiles := range pkgs {
592 _, err := compileInDir(runcmd, longdir, gofiles...)
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200593 if err != nil {
594 t.err = err
595 return
596 }
Rémy Oudomphengc3836ed2013-01-11 22:00:48 +0100597 if i == len(pkgs)-1 {
598 err = linkFile(runcmd, gofiles[0])
599 if err != nil {
600 t.err = err
601 return
602 }
Dave Cheney7c8280c2014-02-25 09:47:42 -0500603 var cmd []string
604 cmd = append(cmd, findExecCmd()...)
605 cmd = append(cmd, filepath.Join(t.tempDir, "a.exe"))
606 cmd = append(cmd, args...)
607 out, err := runcmd(cmd...)
Rémy Oudomphengc3836ed2013-01-11 22:00:48 +0100608 if err != nil {
609 t.err = err
610 return
611 }
612 if strings.Replace(string(out), "\r\n", "\n", -1) != t.expectedOutput() {
613 t.err = fmt.Errorf("incorrect output\n%s", out)
614 }
615 }
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200616 }
617
618 case "build":
619 _, err := runcmd("go", "build", "-o", "a.exe", long)
620 if err != nil {
621 t.err = err
Russ Cox105c5fa2012-03-07 01:54:39 -0500622 }
Russ Coxc978a5a2012-03-08 14:03:40 -0500623
Russ Cox105c5fa2012-03-07 01:54:39 -0500624 case "run":
625 useTmp = false
626 out, err := runcmd(append([]string{"go", "run", t.goFileName()}, args...)...)
627 if err != nil {
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200628 t.err = err
Russ Cox105c5fa2012-03-07 01:54:39 -0500629 }
Shenghou Ma674bbaf2012-09-20 00:27:23 +0800630 if strings.Replace(string(out), "\r\n", "\n", -1) != t.expectedOutput() {
Russ Cox105c5fa2012-03-07 01:54:39 -0500631 t.err = fmt.Errorf("incorrect output\n%s", out)
632 }
Shenghou Madda6d6a2012-04-20 23:45:43 +0800633
634 case "runoutput":
Dave Cheneydc756702013-01-12 17:52:52 +1100635 rungatec <- true
636 defer func() {
637 <-rungatec
638 }()
Shenghou Madda6d6a2012-04-20 23:45:43 +0800639 useTmp = false
Ian Lance Taylore08008e2012-11-07 12:33:54 -0800640 out, err := runcmd(append([]string{"go", "run", t.goFileName()}, args...)...)
Shenghou Madda6d6a2012-04-20 23:45:43 +0800641 if err != nil {
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200642 t.err = err
Shenghou Madda6d6a2012-04-20 23:45:43 +0800643 }
644 tfile := filepath.Join(t.tempDir, "tmp__.go")
Dave Cheneydc756702013-01-12 17:52:52 +1100645 if err := ioutil.WriteFile(tfile, out, 0666); err != nil {
Shenghou Madda6d6a2012-04-20 23:45:43 +0800646 t.err = fmt.Errorf("write tempfile:%s", err)
647 return
648 }
649 out, err = runcmd("go", "run", tfile)
650 if err != nil {
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200651 t.err = err
Shenghou Madda6d6a2012-04-20 23:45:43 +0800652 }
653 if string(out) != t.expectedOutput() {
654 t.err = fmt.Errorf("incorrect output\n%s", out)
655 }
Ian Lance Taylore08008e2012-11-07 12:33:54 -0800656
657 case "errorcheckoutput":
658 useTmp = false
659 out, err := runcmd(append([]string{"go", "run", t.goFileName()}, args...)...)
660 if err != nil {
661 t.err = err
662 }
663 tfile := filepath.Join(t.tempDir, "tmp__.go")
664 err = ioutil.WriteFile(tfile, out, 0666)
665 if err != nil {
666 t.err = fmt.Errorf("write tempfile:%s", err)
667 return
668 }
669 cmdline := []string{"go", "tool", gc, "-e", "-o", "a." + letter}
670 cmdline = append(cmdline, flags...)
671 cmdline = append(cmdline, tfile)
672 out, err = runcmd(cmdline...)
673 if wantError {
674 if err == nil {
675 t.err = fmt.Errorf("compilation succeeded unexpectedly\n%s", out)
676 return
677 }
678 } else {
679 if err != nil {
680 t.err = err
681 return
682 }
683 }
684 t.err = t.errorCheck(string(out), tfile, "tmp__.go")
685 return
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100686 }
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100687}
688
Dave Cheney7c8280c2014-02-25 09:47:42 -0500689var execCmd []string
690
691func findExecCmd() []string {
692 if execCmd != nil {
693 return execCmd
694 }
695 execCmd = []string{} // avoid work the second time
696 if goos == runtime.GOOS && goarch == runtime.GOARCH {
697 return execCmd
698 }
699 path, err := exec.LookPath(fmt.Sprintf("go_%s_%s_exec", goos, goarch))
700 if err == nil {
701 execCmd = []string{path}
702 }
703 return execCmd
David du Colombier6d20e722014-08-01 22:34:36 +0200704}
Dave Cheney7c8280c2014-02-25 09:47:42 -0500705
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100706func (t *test) String() string {
707 return filepath.Join(t.dir, t.gofile)
708}
709
710func (t *test) makeTempDir() {
711 var err error
712 t.tempDir, err = ioutil.TempDir("", "")
713 check(err)
714}
715
716func (t *test) expectedOutput() string {
717 filename := filepath.Join(t.dir, t.gofile)
718 filename = filename[:len(filename)-len(".go")]
719 filename += ".out"
720 b, _ := ioutil.ReadFile(filename)
721 return string(b)
722}
723
Russ Cox8850d142013-01-02 15:31:49 -0500724func (t *test) errorCheck(outStr string, fullshort ...string) (err error) {
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100725 defer func() {
726 if *verbose && err != nil {
727 log.Printf("%s gc output:\n%s", t, outStr)
728 }
729 }()
730 var errs []error
731
732 var out []string
733 // 6g error messages continue onto additional lines with leading tabs.
734 // Split the output at the beginning of each line that doesn't begin with a tab.
735 for _, line := range strings.Split(outStr, "\n") {
Russ Coxcd22afa2012-09-23 13:16:14 -0400736 if strings.HasSuffix(line, "\r") { // remove '\r', output by compiler on windows
Alex Brainman42534cb2012-08-16 16:46:59 +1000737 line = line[:len(line)-1]
738 }
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100739 if strings.HasPrefix(line, "\t") {
740 out[len(out)-1] += "\n" + line
Daniel Morsingc81293a2012-10-08 16:36:45 +0200741 } else if strings.HasPrefix(line, "go tool") {
742 continue
743 } else if strings.TrimSpace(line) != "" {
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100744 out = append(out, line)
745 }
746 }
747
Russ Cox105c5fa2012-03-07 01:54:39 -0500748 // Cut directory name.
749 for i := range out {
Russ Cox8850d142013-01-02 15:31:49 -0500750 for j := 0; j < len(fullshort); j += 2 {
751 full, short := fullshort[j], fullshort[j+1]
752 out[i] = strings.Replace(out[i], full, short, -1)
753 }
754 }
Rémy Oudomphengc3836ed2013-01-11 22:00:48 +0100755
Russ Cox8850d142013-01-02 15:31:49 -0500756 var want []wantedError
757 for j := 0; j < len(fullshort); j += 2 {
758 full, short := fullshort[j], fullshort[j+1]
759 want = append(want, t.wantedErrors(full, short)...)
Russ Cox105c5fa2012-03-07 01:54:39 -0500760 }
761
Russ Cox8850d142013-01-02 15:31:49 -0500762 for _, we := range want {
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100763 var errmsgs []string
Russ Coxd5887c52014-03-11 23:58:24 -0400764 errmsgs, out = partitionStrings(we.prefix, out)
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100765 if len(errmsgs) == 0 {
Russ Cox105c5fa2012-03-07 01:54:39 -0500766 errs = append(errs, fmt.Errorf("%s:%d: missing error %q", we.file, we.lineNum, we.reStr))
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100767 continue
768 }
769 matched := false
Russ Cox89313062013-02-01 23:10:02 -0500770 n := len(out)
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100771 for _, errmsg := range errmsgs {
772 if we.re.MatchString(errmsg) {
773 matched = true
774 } else {
775 out = append(out, errmsg)
776 }
777 }
778 if !matched {
Russ Cox89313062013-02-01 23:10:02 -0500779 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")))
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100780 continue
781 }
782 }
783
Daniel Morsingc81293a2012-10-08 16:36:45 +0200784 if len(out) > 0 {
785 errs = append(errs, fmt.Errorf("Unmatched Errors:"))
786 for _, errLine := range out {
787 errs = append(errs, fmt.Errorf("%s", errLine))
788 }
789 }
790
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100791 if len(errs) == 0 {
792 return nil
793 }
794 if len(errs) == 1 {
795 return errs[0]
796 }
797 var buf bytes.Buffer
Russ Cox105c5fa2012-03-07 01:54:39 -0500798 fmt.Fprintf(&buf, "\n")
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100799 for _, err := range errs {
800 fmt.Fprintf(&buf, "%s\n", err.Error())
801 }
802 return errors.New(buf.String())
803
804}
805
Russ Coxd5887c52014-03-11 23:58:24 -0400806// matchPrefix reports whether s is of the form ^(.*/)?prefix(:|[),
807// That is, it needs the file name prefix followed by a : or a [,
808// and possibly preceded by a directory name.
809func matchPrefix(s, prefix string) bool {
810 i := strings.Index(s, ":")
811 if i < 0 {
812 return false
813 }
814 j := strings.LastIndex(s[:i], "/")
815 s = s[j+1:]
816 if len(s) <= len(prefix) || s[:len(prefix)] != prefix {
817 return false
818 }
819 switch s[len(prefix)] {
820 case '[', ':':
821 return true
822 }
823 return false
824}
825
826func partitionStrings(prefix string, strs []string) (matched, unmatched []string) {
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100827 for _, s := range strs {
Russ Coxd5887c52014-03-11 23:58:24 -0400828 if matchPrefix(s, prefix) {
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100829 matched = append(matched, s)
830 } else {
831 unmatched = append(unmatched, s)
832 }
833 }
834 return
835}
836
837type wantedError struct {
David du Colombier6d20e722014-08-01 22:34:36 +0200838 reStr string
839 re *regexp.Regexp
840 lineNum int
841 file string
842 prefix string
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100843}
844
845var (
846 errRx = regexp.MustCompile(`// (?:GC_)?ERROR (.*)`)
847 errQuotesRx = regexp.MustCompile(`"([^"]*)"`)
848 lineRx = regexp.MustCompile(`LINE(([+-])([0-9]+))?`)
849)
850
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200851func (t *test) wantedErrors(file, short string) (errs []wantedError) {
Russ Coxd5887c52014-03-11 23:58:24 -0400852 cache := make(map[string]*regexp.Regexp)
853
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200854 src, _ := ioutil.ReadFile(file)
855 for i, line := range strings.Split(string(src), "\n") {
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100856 lineNum := i + 1
857 if strings.Contains(line, "////") {
858 // double comment disables ERROR
859 continue
860 }
861 m := errRx.FindStringSubmatch(line)
862 if m == nil {
863 continue
864 }
865 all := m[1]
866 mm := errQuotesRx.FindAllStringSubmatch(all, -1)
867 if mm == nil {
Russ Cox89313062013-02-01 23:10:02 -0500868 log.Fatalf("%s:%d: invalid errchk line: %s", t.goFileName(), lineNum, line)
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100869 }
870 for _, m := range mm {
871 rx := lineRx.ReplaceAllStringFunc(m[1], func(m string) string {
872 n := lineNum
873 if strings.HasPrefix(m, "LINE+") {
874 delta, _ := strconv.Atoi(m[5:])
875 n += delta
876 } else if strings.HasPrefix(m, "LINE-") {
877 delta, _ := strconv.Atoi(m[5:])
878 n -= delta
879 }
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200880 return fmt.Sprintf("%s:%d", short, n)
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100881 })
Russ Coxd5887c52014-03-11 23:58:24 -0400882 re := cache[rx]
883 if re == nil {
884 var err error
885 re, err = regexp.Compile(rx)
886 if err != nil {
887 log.Fatalf("%s:%d: invalid regexp in ERROR line: %v", t.goFileName(), lineNum, err)
888 }
889 cache[rx] = re
Russ Cox89313062013-02-01 23:10:02 -0500890 }
Russ Coxd5887c52014-03-11 23:58:24 -0400891 prefix := fmt.Sprintf("%s:%d", short, lineNum)
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100892 errs = append(errs, wantedError{
David du Colombier6d20e722014-08-01 22:34:36 +0200893 reStr: rx,
894 re: re,
895 prefix: prefix,
896 lineNum: lineNum,
897 file: short,
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100898 })
899 }
900 }
901
902 return
903}
Russ Coxcd22afa2012-09-23 13:16:14 -0400904
905var skipOkay = map[string]bool{
Rémy Oudompheng4f6a2b92013-01-28 21:29:45 +0100906 "linkx.go": true, // like "run" but wants linker flags
907 "sinit.go": true,
908 "fixedbugs/bug248.go": true, // combines errorcheckdir and rundir in the same dir.
909 "fixedbugs/bug302.go": true, // tests both .$O and .a imports.
910 "fixedbugs/bug345.go": true, // needs the appropriate flags in gc invocation.
911 "fixedbugs/bug369.go": true, // needs compiler flags.
912 "fixedbugs/bug429.go": true, // like "run" but program should fail
913 "bugs/bug395.go": true,
Russ Coxcd22afa2012-09-23 13:16:14 -0400914}
Dave Cheneydc756702013-01-12 17:52:52 +1100915
916// defaultRunOutputLimit returns the number of runoutput tests that
917// can be executed in parallel.
918func defaultRunOutputLimit() int {
919 const maxArmCPU = 2
920
921 cpu := runtime.NumCPU()
922 if runtime.GOARCH == "arm" && cpu > maxArmCPU {
923 cpu = maxArmCPU
924 }
925 return cpu
926}
Rémy Oudompheng4f6a2b92013-01-28 21:29:45 +0100927
Anthony Martina5385582013-08-13 12:25:41 -0400928// checkShouldTest runs sanity checks on the shouldTest function.
Rémy Oudompheng4f6a2b92013-01-28 21:29:45 +0100929func checkShouldTest() {
930 assert := func(ok bool, _ string) {
931 if !ok {
932 panic("fail")
933 }
934 }
935 assertNot := func(ok bool, _ string) { assert(!ok, "") }
Anthony Martina5385582013-08-13 12:25:41 -0400936
937 // Simple tests.
Rémy Oudompheng4f6a2b92013-01-28 21:29:45 +0100938 assert(shouldTest("// +build linux", "linux", "arm"))
939 assert(shouldTest("// +build !windows", "linux", "arm"))
940 assertNot(shouldTest("// +build !windows", "windows", "amd64"))
Anthony Martina5385582013-08-13 12:25:41 -0400941
942 // A file with no build tags will always be tested.
Rémy Oudompheng4f6a2b92013-01-28 21:29:45 +0100943 assert(shouldTest("// This is a test.", "os", "arch"))
Anthony Martina5385582013-08-13 12:25:41 -0400944
945 // Build tags separated by a space are OR-ed together.
946 assertNot(shouldTest("// +build arm 386", "linux", "amd64"))
947
Martin Olsson54990342013-12-27 08:59:02 -0800948 // Build tags separated by a comma are AND-ed together.
Anthony Martina5385582013-08-13 12:25:41 -0400949 assertNot(shouldTest("// +build !windows,!plan9", "windows", "amd64"))
950 assertNot(shouldTest("// +build !windows,!plan9", "plan9", "386"))
951
952 // Build tags on multiple lines are AND-ed together.
953 assert(shouldTest("// +build !windows\n// +build amd64", "linux", "amd64"))
954 assertNot(shouldTest("// +build !windows\n// +build amd64", "windows", "amd64"))
955
956 // Test that (!a OR !b) matches anything.
957 assert(shouldTest("// +build !windows !plan9", "windows", "amd64"))
Rémy Oudompheng4f6a2b92013-01-28 21:29:45 +0100958}
Russ Cox551f3f22013-02-14 14:21:26 -0500959
960// envForDir returns a copy of the environment
961// suitable for running in the given directory.
962// The environment is the current process's environment
963// but with an updated $PWD, so that an os.Getwd in the
964// child will be faster.
965func envForDir(dir string) []string {
966 env := os.Environ()
967 for i, kv := range env {
968 if strings.HasPrefix(kv, "PWD=") {
969 env[i] = "PWD=" + dir
970 return env
971 }
972 }
973 env = append(env, "PWD="+dir)
974 return env
975}
David du Colombier748e5db2014-07-24 23:18:54 +0200976
977func getenv(key, def string) string {
978 value := os.Getenv(key)
979 if value != "" {
980 return value
981 }
982 return def
983}