blob: 6e1cde9390000aadc408ee58b52c5c7c139ad698 [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"
Brad Fitzpatrickec745fc2015-06-03 23:21:30 -070018 "hash/fnv"
19 "io"
Brad Fitzpatrickce837b32012-02-21 14:28:49 +110020 "io/ioutil"
21 "log"
22 "os"
23 "os/exec"
Russ Coxcd22afa2012-09-23 13:16:14 -040024 "path"
Brad Fitzpatrickce837b32012-02-21 14:28:49 +110025 "path/filepath"
26 "regexp"
27 "runtime"
28 "sort"
29 "strconv"
30 "strings"
Russ Coxde8549d2013-12-10 14:02:42 -050031 "time"
Anthony Martina5385582013-08-13 12:25:41 -040032 "unicode"
Brad Fitzpatrickce837b32012-02-21 14:28:49 +110033)
34
35var (
Dave Cheneydc756702013-01-12 17:52:52 +110036 verbose = flag.Bool("v", false, "verbose. if set, parallelism is set to 1.")
37 numParallel = flag.Int("n", runtime.NumCPU(), "number of parallel tests to run")
38 summary = flag.Bool("summary", false, "show summary of results")
39 showSkips = flag.Bool("show_skips", false, "show skipped tests")
Dmitry Vyukov76477412015-02-19 22:00:11 +030040 updateErrors = flag.Bool("update_errors", false, "update error messages in test file based on compiler output")
Dave Cheneydc756702013-01-12 17:52:52 +110041 runoutputLimit = flag.Int("l", defaultRunOutputLimit(), "number of parallel runoutput tests to run")
Brad Fitzpatrickec745fc2015-06-03 23:21:30 -070042
43 shard = flag.Int("shard", 0, "shard index to run. Only applicable if -shards is non-zero.")
44 shards = flag.Int("shards", 0, "number of shards. If 0, all tests are run. This is used by the continuous build.")
Brad Fitzpatrickce837b32012-02-21 14:28:49 +110045)
46
47var (
Dave Cheney7c8280c2014-02-25 09:47:42 -050048 goos, goarch string
Brad Fitzpatrickce837b32012-02-21 14:28:49 +110049
50 // dirs are the directories to look for *.go files in.
51 // TODO(bradfitz): just use all directories?
Matthew Dempsky8a413752015-04-27 16:06:22 -070052 dirs = []string{".", "ken", "chan", "interface", "syntax", "dwarf", "fixedbugs", "bugs"}
Brad Fitzpatrickce837b32012-02-21 14:28:49 +110053
54 // ratec controls the max number of tests running at a time.
55 ratec chan bool
56
57 // toRun is the channel of tests to run.
58 // It is nil until the first test is started.
59 toRun chan *test
Dave Cheneydc756702013-01-12 17:52:52 +110060
61 // rungatec controls the max number of runoutput tests
62 // executed in parallel as they can each consume a lot of memory.
63 rungatec chan bool
Brad Fitzpatrickce837b32012-02-21 14:28:49 +110064)
65
66// maxTests is an upper bound on the total number of tests.
67// It is used as a channel buffer size to make sure sends don't block.
68const maxTests = 5000
69
70func main() {
71 flag.Parse()
Shenghou Ma47ee9822012-03-07 12:43:25 +080072
David du Colombier748e5db2014-07-24 23:18:54 +020073 goos = getenv("GOOS", runtime.GOOS)
74 goarch = getenv("GOARCH", runtime.GOARCH)
75
Dave Cheney7c8280c2014-02-25 09:47:42 -050076 findExecCmd()
77
Russ Cox4895f0d2014-05-28 01:01:08 -040078 // Disable parallelism if printing or if using a simulator.
79 if *verbose || len(findExecCmd()) > 0 {
80 *numParallel = 1
81 }
82
Brad Fitzpatrickce837b32012-02-21 14:28:49 +110083 ratec = make(chan bool, *numParallel)
Dave Cheneydc756702013-01-12 17:52:52 +110084 rungatec = make(chan bool, *runoutputLimit)
Brad Fitzpatrickce837b32012-02-21 14:28:49 +110085
86 var tests []*test
87 if flag.NArg() > 0 {
88 for _, arg := range flag.Args() {
89 if arg == "-" || arg == "--" {
Shenghou Ma1e954292012-08-07 09:38:35 +080090 // Permit running:
Brad Fitzpatrickce837b32012-02-21 14:28:49 +110091 // $ go run run.go - env.go
92 // $ go run run.go -- env.go
Shenghou Ma1e954292012-08-07 09:38:35 +080093 // $ go run run.go - ./fixedbugs
94 // $ go run run.go -- ./fixedbugs
Brad Fitzpatrickce837b32012-02-21 14:28:49 +110095 continue
96 }
Shenghou Ma1e954292012-08-07 09:38:35 +080097 if fi, err := os.Stat(arg); err == nil && fi.IsDir() {
98 for _, baseGoFile := range goFiles(arg) {
99 tests = append(tests, startTest(arg, baseGoFile))
100 }
101 } else if strings.HasSuffix(arg, ".go") {
102 dir, file := filepath.Split(arg)
103 tests = append(tests, startTest(dir, file))
104 } else {
105 log.Fatalf("can't yet deal with non-directory and non-go file %q", arg)
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100106 }
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100107 }
108 } else {
109 for _, dir := range dirs {
110 for _, baseGoFile := range goFiles(dir) {
111 tests = append(tests, startTest(dir, baseGoFile))
112 }
113 }
114 }
115
116 failed := false
117 resCount := map[string]int{}
118 for _, test := range tests {
David du Colombier6d20e722014-08-01 22:34:36 +0200119 <-test.donec
Russ Coxde8549d2013-12-10 14:02:42 -0500120 status := "ok "
121 errStr := ""
122 if _, isSkip := test.err.(skipError); isSkip {
Russ Coxde8549d2013-12-10 14:02:42 -0500123 test.err = nil
Josh Bleecher Snydere7173df2014-12-18 10:34:12 -0800124 errStr = "unexpected skip for " + path.Join(test.dir, test.gofile) + ": " + errStr
125 status = "FAIL"
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100126 }
Russ Coxde8549d2013-12-10 14:02:42 -0500127 if test.err != nil {
128 status = "FAIL"
129 errStr = test.err.Error()
130 }
131 if status == "FAIL" {
Russ Coxcd22afa2012-09-23 13:16:14 -0400132 failed = true
133 }
Russ Coxde8549d2013-12-10 14:02:42 -0500134 resCount[status]++
135 if status == "skip" && !*verbose && !*showSkips {
Brad Fitzpatricka55a5c82012-02-24 12:52:15 +1100136 continue
137 }
Russ Coxde8549d2013-12-10 14:02:42 -0500138 dt := fmt.Sprintf("%.3fs", test.dt.Seconds())
139 if status == "FAIL" {
140 fmt.Printf("# go run run.go -- %s\n%s\nFAIL\t%s\t%s\n",
141 path.Join(test.dir, test.gofile),
142 errStr, test.goFileName(), dt)
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100143 continue
Brad Fitzpatricka55a5c82012-02-24 12:52:15 +1100144 }
Russ Coxde8549d2013-12-10 14:02:42 -0500145 if !*verbose {
146 continue
147 }
148 fmt.Printf("%s\t%s\t%s\n", status, test.goFileName(), dt)
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100149 }
150
151 if *summary {
152 for k, v := range resCount {
153 fmt.Printf("%5d %s\n", v, k)
154 }
155 }
156
157 if failed {
158 os.Exit(1)
159 }
160}
161
162func toolPath(name string) string {
163 p := filepath.Join(os.Getenv("GOROOT"), "bin", "tool", name)
164 if _, err := os.Stat(p); err != nil {
165 log.Fatalf("didn't find binary at %s", p)
166 }
167 return p
168}
169
Brad Fitzpatrickec745fc2015-06-03 23:21:30 -0700170func shardMatch(name string) bool {
171 if *shards == 0 {
172 return true
173 }
174 h := fnv.New32()
175 io.WriteString(h, name)
176 return int(h.Sum32()%uint32(*shards)) == *shard
177}
178
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100179func 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 {
Brad Fitzpatrickec745fc2015-06-03 23:21:30 -0700186 if !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") && shardMatch(name) {
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) {
Russ Cox0f4132c2015-05-21 13:28:13 -0400197 return runcmd("go", "tool", "compile", "-e", longname)
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200198}
199
Russ Cox8850d142013-01-02 15:31:49 -0500200func compileInDir(runcmd runCmd, dir string, names ...string) (out []byte, err error) {
Russ Cox0f4132c2015-05-21 13:28:13 -0400201 cmd := []string{"go", "tool", "compile", "-e", "-D", ".", "-I", "."}
Russ Cox8850d142013-01-02 15:31:49 -0500202 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) {
Russ Coxcf932cd2015-05-21 13:28:17 -0400209 pfile := strings.Replace(goname, ".go", ".o", -1)
Russ Cox0f4132c2015-05-21 13:28:13 -0400210 _, err = runcmd("go", "tool", "link", "-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) {
Rémy Oudompheng4f6a2b92013-01-28 21:29:45 +0100331 for _, line := range strings.Split(src, "\n") {
332 line = strings.TrimSpace(line)
333 if strings.HasPrefix(line, "//") {
334 line = line[2:]
335 } else {
336 continue
337 }
338 line = strings.TrimSpace(line)
339 if len(line) == 0 || line[0] != '+' {
340 continue
341 }
Anthony Martina5385582013-08-13 12:25:41 -0400342 ctxt := &context{
343 GOOS: goos,
344 GOARCH: goarch,
345 }
Rémy Oudompheng4f6a2b92013-01-28 21:29:45 +0100346 words := strings.Fields(line)
347 if words[0] == "+build" {
Anthony Martina5385582013-08-13 12:25:41 -0400348 ok := false
349 for _, word := range words[1:] {
350 if ctxt.match(word) {
351 ok = true
352 break
Rémy Oudompheng4f6a2b92013-01-28 21:29:45 +0100353 }
354 }
Anthony Martina5385582013-08-13 12:25:41 -0400355 if !ok {
356 // no matching tag found.
357 return false, line
358 }
Rémy Oudompheng4f6a2b92013-01-28 21:29:45 +0100359 }
360 }
Anthony Martina5385582013-08-13 12:25:41 -0400361 // no build tags
Rémy Oudompheng4f6a2b92013-01-28 21:29:45 +0100362 return true, ""
363}
364
Anthony Martina5385582013-08-13 12:25:41 -0400365func (ctxt *context) match(name string) bool {
366 if name == "" {
367 return false
368 }
369 if i := strings.Index(name, ","); i >= 0 {
370 // comma-separated list
371 return ctxt.match(name[:i]) && ctxt.match(name[i+1:])
372 }
373 if strings.HasPrefix(name, "!!") { // bad syntax, reject always
374 return false
375 }
376 if strings.HasPrefix(name, "!") { // negation
377 return len(name) > 1 && !ctxt.match(name[1:])
378 }
379
380 // Tags must be letters, digits, underscores or dots.
381 // Unlike in Go identifiers, all digits are fine (e.g., "386").
382 for _, c := range name {
383 if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' {
384 return false
385 }
386 }
387
388 if name == ctxt.GOOS || name == ctxt.GOARCH {
389 return true
390 }
391
392 return false
393}
394
Rémy Oudompheng4f6a2b92013-01-28 21:29:45 +0100395func init() { checkShouldTest() }
396
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100397// run runs a test.
398func (t *test) run() {
Russ Coxde8549d2013-12-10 14:02:42 -0500399 start := time.Now()
400 defer func() {
401 t.dt = time.Since(start)
402 close(t.donec)
403 }()
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100404
405 srcBytes, err := ioutil.ReadFile(t.goFileName())
406 if err != nil {
407 t.err = err
408 return
409 }
410 t.src = string(srcBytes)
411 if t.src[0] == '\n' {
412 t.err = skipError("starts with newline")
413 return
414 }
Russ Cox0bcdffe2015-07-10 12:32:03 -0400415
416 // Execution recipe stops at first blank line.
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100417 pos := strings.Index(t.src, "\n\n")
418 if pos == -1 {
419 t.err = errors.New("double newline not found")
420 return
421 }
422 action := t.src[:pos]
Rémy Oudompheng4f6a2b92013-01-28 21:29:45 +0100423 if nl := strings.Index(action, "\n"); nl >= 0 && strings.Contains(action[:nl], "+build") {
424 // skip first line
425 action = action[nl+1:]
426 }
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100427 if strings.HasPrefix(action, "//") {
428 action = action[2:]
429 }
Russ Coxc978a5a2012-03-08 14:03:40 -0500430
Russ Cox0bcdffe2015-07-10 12:32:03 -0400431 // Check for build constraints only up to the actual code.
432 pkgPos := strings.Index(t.src, "\npackage")
433 if pkgPos == -1 {
434 pkgPos = pos // some files are intentionally malformed
435 }
436 if ok, why := shouldTest(t.src[:pkgPos], goos, goarch); !ok {
437 t.action = "skip"
438 if *showSkips {
439 fmt.Printf("%-20s %-20s: %s\n", t.action, t.goFileName(), why)
440 }
441 return
442 }
443
Russ Coxcd22afa2012-09-23 13:16:14 -0400444 var args, flags []string
445 wantError := false
Russ Cox105c5fa2012-03-07 01:54:39 -0500446 f := strings.Fields(action)
447 if len(f) > 0 {
448 action = f[0]
449 args = f[1:]
450 }
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100451
452 switch action {
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200453 case "rundircmpout":
454 action = "rundir"
455 t.action = "rundir"
Brad Fitzpatricke014cf02012-02-24 13:17:26 +1100456 case "cmpout":
457 action = "run" // the run case already looks for <dir>/<test>.out files
458 fallthrough
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200459 case "compile", "compiledir", "build", "run", "runoutput", "rundir":
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100460 t.action = action
Ian Lance Taylore08008e2012-11-07 12:33:54 -0800461 case "errorcheck", "errorcheckdir", "errorcheckoutput":
Russ Coxcd22afa2012-09-23 13:16:14 -0400462 t.action = action
463 wantError = true
464 for len(args) > 0 && strings.HasPrefix(args[0], "-") {
465 if args[0] == "-0" {
466 wantError = false
467 } else {
468 flags = append(flags, args[0])
469 }
470 args = args[1:]
471 }
Shenghou Mae2662832012-03-22 02:14:44 +0800472 case "skip":
473 t.action = "skip"
474 return
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100475 default:
476 t.err = skipError("skipped; unknown pattern: " + action)
477 t.action = "??"
478 return
479 }
480
481 t.makeTempDir()
482 defer os.RemoveAll(t.tempDir)
483
484 err = ioutil.WriteFile(filepath.Join(t.tempDir, t.gofile), srcBytes, 0644)
485 check(err)
Russ Coxc978a5a2012-03-08 14:03:40 -0500486
Russ Cox71247362012-03-07 02:22:08 -0500487 // A few tests (of things like the environment) require these to be set.
Dave Cheney7c8280c2014-02-25 09:47:42 -0500488 if os.Getenv("GOOS") == "" {
489 os.Setenv("GOOS", runtime.GOOS)
490 }
491 if os.Getenv("GOARCH") == "" {
492 os.Setenv("GOARCH", runtime.GOARCH)
493 }
Russ Cox71247362012-03-07 02:22:08 -0500494
Russ Cox105c5fa2012-03-07 01:54:39 -0500495 useTmp := true
496 runcmd := func(args ...string) ([]byte, error) {
497 cmd := exec.Command(args[0], args[1:]...)
498 var buf bytes.Buffer
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100499 cmd.Stdout = &buf
500 cmd.Stderr = &buf
Russ Cox105c5fa2012-03-07 01:54:39 -0500501 if useTmp {
502 cmd.Dir = t.tempDir
Russ Cox551f3f22013-02-14 14:21:26 -0500503 cmd.Env = envForDir(cmd.Dir)
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100504 }
Russ Cox105c5fa2012-03-07 01:54:39 -0500505 err := cmd.Run()
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200506 if err != nil {
507 err = fmt.Errorf("%s\n%s", err, buf.Bytes())
508 }
Russ Cox105c5fa2012-03-07 01:54:39 -0500509 return buf.Bytes(), err
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100510 }
511
Russ Cox105c5fa2012-03-07 01:54:39 -0500512 long := filepath.Join(cwd, t.goFileName())
Russ Coxc978a5a2012-03-08 14:03:40 -0500513 switch action {
Russ Cox105c5fa2012-03-07 01:54:39 -0500514 default:
515 t.err = fmt.Errorf("unimplemented action %q", action)
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100516
Russ Cox105c5fa2012-03-07 01:54:39 -0500517 case "errorcheck":
Russ Coxcf932cd2015-05-21 13:28:17 -0400518 cmdline := []string{"go", "tool", "compile", "-e", "-o", "a.o"}
Russ Coxcd22afa2012-09-23 13:16:14 -0400519 cmdline = append(cmdline, flags...)
520 cmdline = append(cmdline, long)
521 out, err := runcmd(cmdline...)
522 if wantError {
523 if err == nil {
524 t.err = fmt.Errorf("compilation succeeded unexpectedly\n%s", out)
525 return
526 }
527 } else {
528 if err != nil {
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200529 t.err = err
Russ Coxcd22afa2012-09-23 13:16:14 -0400530 return
531 }
532 }
Dmitry Vyukov76477412015-02-19 22:00:11 +0300533 if *updateErrors {
534 t.updateErrors(string(out), long)
535 }
Russ Cox105c5fa2012-03-07 01:54:39 -0500536 t.err = t.errorCheck(string(out), long, t.gofile)
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100537 return
Russ Coxc978a5a2012-03-08 14:03:40 -0500538
Russ Cox105c5fa2012-03-07 01:54:39 -0500539 case "compile":
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200540 _, t.err = compileFile(runcmd, long)
Russ Coxc978a5a2012-03-08 14:03:40 -0500541
Rémy Oudomphengadc93372012-07-30 21:12:05 +0200542 case "compiledir":
543 // Compile all files in the directory in lexicographic order.
544 longdir := filepath.Join(cwd, t.goDirName())
Russ Cox8850d142013-01-02 15:31:49 -0500545 pkgs, err := goDirPackages(longdir)
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200546 if err != nil {
547 t.err = err
Rémy Oudomphengadc93372012-07-30 21:12:05 +0200548 return
549 }
Russ Cox8850d142013-01-02 15:31:49 -0500550 for _, gofiles := range pkgs {
551 _, t.err = compileInDir(runcmd, longdir, gofiles...)
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200552 if t.err != nil {
553 return
Alex Brainman1df9ee02012-08-06 14:56:39 +1000554 }
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200555 }
556
557 case "errorcheckdir":
558 // errorcheck all files in lexicographic order
559 // useful for finding importing errors
560 longdir := filepath.Join(cwd, t.goDirName())
Russ Cox8850d142013-01-02 15:31:49 -0500561 pkgs, err := goDirPackages(longdir)
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200562 if err != nil {
563 t.err = err
564 return
565 }
Russ Cox8850d142013-01-02 15:31:49 -0500566 for i, gofiles := range pkgs {
567 out, err := compileInDir(runcmd, longdir, gofiles...)
568 if i == len(pkgs)-1 {
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200569 if wantError && err == nil {
570 t.err = fmt.Errorf("compilation succeeded unexpectedly\n%s", out)
571 return
572 } else if !wantError && err != nil {
573 t.err = err
574 return
575 }
576 } else if err != nil {
577 t.err = err
578 return
579 }
Russ Cox8850d142013-01-02 15:31:49 -0500580 var fullshort []string
581 for _, name := range gofiles {
582 fullshort = append(fullshort, filepath.Join(longdir, name), name)
583 }
584 t.err = t.errorCheck(string(out), fullshort...)
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200585 if t.err != nil {
Rémy Oudomphengadc93372012-07-30 21:12:05 +0200586 break
587 }
588 }
589
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200590 case "rundir":
591 // Compile all files in the directory in lexicographic order.
592 // then link as if the last file is the main package and run it
593 longdir := filepath.Join(cwd, t.goDirName())
Rémy Oudomphengc3836ed2013-01-11 22:00:48 +0100594 pkgs, err := goDirPackages(longdir)
Russ Cox105c5fa2012-03-07 01:54:39 -0500595 if err != nil {
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200596 t.err = err
597 return
598 }
Rémy Oudomphengc3836ed2013-01-11 22:00:48 +0100599 for i, gofiles := range pkgs {
600 _, err := compileInDir(runcmd, longdir, gofiles...)
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200601 if err != nil {
602 t.err = err
603 return
604 }
Rémy Oudomphengc3836ed2013-01-11 22:00:48 +0100605 if i == len(pkgs)-1 {
606 err = linkFile(runcmd, gofiles[0])
607 if err != nil {
608 t.err = err
609 return
610 }
Dave Cheney7c8280c2014-02-25 09:47:42 -0500611 var cmd []string
612 cmd = append(cmd, findExecCmd()...)
613 cmd = append(cmd, filepath.Join(t.tempDir, "a.exe"))
614 cmd = append(cmd, args...)
615 out, err := runcmd(cmd...)
Rémy Oudomphengc3836ed2013-01-11 22:00:48 +0100616 if err != nil {
617 t.err = err
618 return
619 }
620 if strings.Replace(string(out), "\r\n", "\n", -1) != t.expectedOutput() {
621 t.err = fmt.Errorf("incorrect output\n%s", out)
622 }
623 }
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200624 }
625
626 case "build":
627 _, err := runcmd("go", "build", "-o", "a.exe", long)
628 if err != nil {
629 t.err = err
Russ Cox105c5fa2012-03-07 01:54:39 -0500630 }
Russ Coxc978a5a2012-03-08 14:03:40 -0500631
Russ Cox105c5fa2012-03-07 01:54:39 -0500632 case "run":
633 useTmp = false
634 out, err := runcmd(append([]string{"go", "run", t.goFileName()}, args...)...)
635 if err != nil {
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200636 t.err = err
Josh Bleecher Snyder8cc6cb22014-09-11 12:47:17 -0700637 return
Russ Cox105c5fa2012-03-07 01:54:39 -0500638 }
Shenghou Ma674bbaf2012-09-20 00:27:23 +0800639 if strings.Replace(string(out), "\r\n", "\n", -1) != t.expectedOutput() {
Russ Cox105c5fa2012-03-07 01:54:39 -0500640 t.err = fmt.Errorf("incorrect output\n%s", out)
641 }
Shenghou Madda6d6a2012-04-20 23:45:43 +0800642
643 case "runoutput":
Dave Cheneydc756702013-01-12 17:52:52 +1100644 rungatec <- true
645 defer func() {
646 <-rungatec
647 }()
Shenghou Madda6d6a2012-04-20 23:45:43 +0800648 useTmp = false
Ian Lance Taylore08008e2012-11-07 12:33:54 -0800649 out, err := runcmd(append([]string{"go", "run", t.goFileName()}, args...)...)
Shenghou Madda6d6a2012-04-20 23:45:43 +0800650 if err != nil {
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200651 t.err = err
Josh Bleecher Snyder8cc6cb22014-09-11 12:47:17 -0700652 return
Shenghou Madda6d6a2012-04-20 23:45:43 +0800653 }
654 tfile := filepath.Join(t.tempDir, "tmp__.go")
Dave Cheneydc756702013-01-12 17:52:52 +1100655 if err := ioutil.WriteFile(tfile, out, 0666); err != nil {
Shenghou Madda6d6a2012-04-20 23:45:43 +0800656 t.err = fmt.Errorf("write tempfile:%s", err)
657 return
658 }
659 out, err = runcmd("go", "run", tfile)
660 if err != nil {
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200661 t.err = err
Josh Bleecher Snyder8cc6cb22014-09-11 12:47:17 -0700662 return
Shenghou Madda6d6a2012-04-20 23:45:43 +0800663 }
664 if string(out) != t.expectedOutput() {
665 t.err = fmt.Errorf("incorrect output\n%s", out)
666 }
Ian Lance Taylore08008e2012-11-07 12:33:54 -0800667
668 case "errorcheckoutput":
669 useTmp = false
670 out, err := runcmd(append([]string{"go", "run", t.goFileName()}, args...)...)
671 if err != nil {
672 t.err = err
Josh Bleecher Snyder8cc6cb22014-09-11 12:47:17 -0700673 return
Ian Lance Taylore08008e2012-11-07 12:33:54 -0800674 }
675 tfile := filepath.Join(t.tempDir, "tmp__.go")
676 err = ioutil.WriteFile(tfile, out, 0666)
677 if err != nil {
678 t.err = fmt.Errorf("write tempfile:%s", err)
679 return
680 }
Russ Coxcf932cd2015-05-21 13:28:17 -0400681 cmdline := []string{"go", "tool", "compile", "-e", "-o", "a.o"}
Ian Lance Taylore08008e2012-11-07 12:33:54 -0800682 cmdline = append(cmdline, flags...)
683 cmdline = append(cmdline, tfile)
684 out, err = runcmd(cmdline...)
685 if wantError {
686 if err == nil {
687 t.err = fmt.Errorf("compilation succeeded unexpectedly\n%s", out)
688 return
689 }
690 } else {
691 if err != nil {
692 t.err = err
693 return
694 }
695 }
696 t.err = t.errorCheck(string(out), tfile, "tmp__.go")
697 return
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100698 }
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100699}
700
Dave Cheney7c8280c2014-02-25 09:47:42 -0500701var execCmd []string
702
703func findExecCmd() []string {
704 if execCmd != nil {
705 return execCmd
706 }
707 execCmd = []string{} // avoid work the second time
708 if goos == runtime.GOOS && goarch == runtime.GOARCH {
709 return execCmd
710 }
711 path, err := exec.LookPath(fmt.Sprintf("go_%s_%s_exec", goos, goarch))
712 if err == nil {
713 execCmd = []string{path}
714 }
715 return execCmd
David du Colombier6d20e722014-08-01 22:34:36 +0200716}
Dave Cheney7c8280c2014-02-25 09:47:42 -0500717
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100718func (t *test) String() string {
719 return filepath.Join(t.dir, t.gofile)
720}
721
722func (t *test) makeTempDir() {
723 var err error
724 t.tempDir, err = ioutil.TempDir("", "")
725 check(err)
726}
727
728func (t *test) expectedOutput() string {
729 filename := filepath.Join(t.dir, t.gofile)
730 filename = filename[:len(filename)-len(".go")]
731 filename += ".out"
732 b, _ := ioutil.ReadFile(filename)
733 return string(b)
734}
735
Dmitry Vyukov76477412015-02-19 22:00:11 +0300736func splitOutput(out string) []string {
Aaron Jacobs86286882015-06-24 09:50:12 +1000737 // gc error messages continue onto additional lines with leading tabs.
Dmitry Vyukov76477412015-02-19 22:00:11 +0300738 // Split the output at the beginning of each line that doesn't begin with a tab.
David Chase5be61b12015-06-16 18:28:01 -0400739 // <autogenerated> lines are impossible to match so those are filtered out.
Dmitry Vyukov76477412015-02-19 22:00:11 +0300740 var res []string
741 for _, line := range strings.Split(out, "\n") {
742 if strings.HasSuffix(line, "\r") { // remove '\r', output by compiler on windows
743 line = line[:len(line)-1]
744 }
745 if strings.HasPrefix(line, "\t") {
746 res[len(res)-1] += "\n" + line
David Chase5be61b12015-06-16 18:28:01 -0400747 } else if strings.HasPrefix(line, "go tool") || strings.HasPrefix(line, "<autogenerated>") {
Dmitry Vyukov76477412015-02-19 22:00:11 +0300748 continue
749 } else if strings.TrimSpace(line) != "" {
750 res = append(res, line)
751 }
752 }
753 return res
754}
755
Russ Cox8850d142013-01-02 15:31:49 -0500756func (t *test) errorCheck(outStr string, fullshort ...string) (err error) {
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100757 defer func() {
758 if *verbose && err != nil {
759 log.Printf("%s gc output:\n%s", t, outStr)
760 }
761 }()
762 var errs []error
Dmitry Vyukov76477412015-02-19 22:00:11 +0300763 out := splitOutput(outStr)
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100764
Russ Cox105c5fa2012-03-07 01:54:39 -0500765 // Cut directory name.
766 for i := range out {
Russ Cox8850d142013-01-02 15:31:49 -0500767 for j := 0; j < len(fullshort); j += 2 {
768 full, short := fullshort[j], fullshort[j+1]
769 out[i] = strings.Replace(out[i], full, short, -1)
770 }
771 }
Rémy Oudomphengc3836ed2013-01-11 22:00:48 +0100772
Russ Cox8850d142013-01-02 15:31:49 -0500773 var want []wantedError
774 for j := 0; j < len(fullshort); j += 2 {
775 full, short := fullshort[j], fullshort[j+1]
776 want = append(want, t.wantedErrors(full, short)...)
Russ Cox105c5fa2012-03-07 01:54:39 -0500777 }
778
Russ Cox8850d142013-01-02 15:31:49 -0500779 for _, we := range want {
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100780 var errmsgs []string
Russ Coxd5887c52014-03-11 23:58:24 -0400781 errmsgs, out = partitionStrings(we.prefix, out)
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100782 if len(errmsgs) == 0 {
Russ Cox105c5fa2012-03-07 01:54:39 -0500783 errs = append(errs, fmt.Errorf("%s:%d: missing error %q", we.file, we.lineNum, we.reStr))
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100784 continue
785 }
786 matched := false
Russ Cox89313062013-02-01 23:10:02 -0500787 n := len(out)
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100788 for _, errmsg := range errmsgs {
789 if we.re.MatchString(errmsg) {
790 matched = true
791 } else {
792 out = append(out, errmsg)
793 }
794 }
795 if !matched {
Russ Cox89313062013-02-01 23:10:02 -0500796 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 +1100797 continue
798 }
799 }
800
Daniel Morsingc81293a2012-10-08 16:36:45 +0200801 if len(out) > 0 {
802 errs = append(errs, fmt.Errorf("Unmatched Errors:"))
803 for _, errLine := range out {
804 errs = append(errs, fmt.Errorf("%s", errLine))
805 }
806 }
807
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100808 if len(errs) == 0 {
809 return nil
810 }
811 if len(errs) == 1 {
812 return errs[0]
813 }
814 var buf bytes.Buffer
Russ Cox105c5fa2012-03-07 01:54:39 -0500815 fmt.Fprintf(&buf, "\n")
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100816 for _, err := range errs {
817 fmt.Fprintf(&buf, "%s\n", err.Error())
818 }
819 return errors.New(buf.String())
Dmitry Vyukov76477412015-02-19 22:00:11 +0300820}
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100821
Dmitry Vyukov76477412015-02-19 22:00:11 +0300822func (t *test) updateErrors(out string, file string) {
823 // Read in source file.
824 src, err := ioutil.ReadFile(file)
825 if err != nil {
826 fmt.Fprintln(os.Stderr, err)
827 return
828 }
829 lines := strings.Split(string(src), "\n")
830 // Remove old errors.
831 for i, ln := range lines {
832 pos := strings.Index(ln, " // ERROR ")
833 if pos >= 0 {
834 lines[i] = ln[:pos]
835 }
836 }
837 // Parse new errors.
838 errors := make(map[int]map[string]bool)
839 tmpRe := regexp.MustCompile(`autotmp_[0-9]+`)
840 for _, errStr := range splitOutput(out) {
841 colon1 := strings.Index(errStr, ":")
Josh Bleecher Snyder673bd182015-04-27 16:50:05 -0700842 if colon1 < 0 || errStr[:colon1] != file {
Dmitry Vyukov76477412015-02-19 22:00:11 +0300843 continue
844 }
845 colon2 := strings.Index(errStr[colon1+1:], ":")
846 if colon2 < 0 {
847 continue
848 }
Josh Bleecher Snyder673bd182015-04-27 16:50:05 -0700849 colon2 += colon1 + 1
850 line, err := strconv.Atoi(errStr[colon1+1 : colon2])
Dmitry Vyukov76477412015-02-19 22:00:11 +0300851 line--
852 if err != nil || line < 0 || line >= len(lines) {
853 continue
854 }
855 msg := errStr[colon2+2:]
856 for _, r := range []string{`\`, `*`, `+`, `[`, `]`, `(`, `)`} {
Josh Bleecher Snyder673bd182015-04-27 16:50:05 -0700857 msg = strings.Replace(msg, r, `\`+r, -1)
Dmitry Vyukov76477412015-02-19 22:00:11 +0300858 }
859 msg = strings.Replace(msg, `"`, `.`, -1)
860 msg = tmpRe.ReplaceAllLiteralString(msg, `autotmp_[0-9]+`)
861 if errors[line] == nil {
862 errors[line] = make(map[string]bool)
863 }
864 errors[line][msg] = true
865 }
866 // Add new errors.
867 for line, errs := range errors {
868 var sorted []string
869 for e := range errs {
870 sorted = append(sorted, e)
871 }
872 sort.Strings(sorted)
873 lines[line] += " // ERROR"
874 for _, e := range sorted {
875 lines[line] += fmt.Sprintf(` "%s$"`, e)
876 }
877 }
878 // Write new file.
879 err = ioutil.WriteFile(file, []byte(strings.Join(lines, "\n")), 0640)
880 if err != nil {
881 fmt.Fprintln(os.Stderr, err)
882 return
883 }
884 // Polish.
885 exec.Command("go", "fmt", file).CombinedOutput()
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100886}
887
Russ Coxd5887c52014-03-11 23:58:24 -0400888// matchPrefix reports whether s is of the form ^(.*/)?prefix(:|[),
889// That is, it needs the file name prefix followed by a : or a [,
890// and possibly preceded by a directory name.
891func matchPrefix(s, prefix string) bool {
892 i := strings.Index(s, ":")
893 if i < 0 {
894 return false
895 }
896 j := strings.LastIndex(s[:i], "/")
897 s = s[j+1:]
898 if len(s) <= len(prefix) || s[:len(prefix)] != prefix {
899 return false
900 }
901 switch s[len(prefix)] {
902 case '[', ':':
903 return true
904 }
905 return false
906}
907
908func partitionStrings(prefix string, strs []string) (matched, unmatched []string) {
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100909 for _, s := range strs {
Russ Coxd5887c52014-03-11 23:58:24 -0400910 if matchPrefix(s, prefix) {
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100911 matched = append(matched, s)
912 } else {
913 unmatched = append(unmatched, s)
914 }
915 }
916 return
917}
918
919type wantedError struct {
David du Colombier6d20e722014-08-01 22:34:36 +0200920 reStr string
921 re *regexp.Regexp
922 lineNum int
923 file string
924 prefix string
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100925}
926
927var (
928 errRx = regexp.MustCompile(`// (?:GC_)?ERROR (.*)`)
929 errQuotesRx = regexp.MustCompile(`"([^"]*)"`)
930 lineRx = regexp.MustCompile(`LINE(([+-])([0-9]+))?`)
931)
932
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200933func (t *test) wantedErrors(file, short string) (errs []wantedError) {
Russ Coxd5887c52014-03-11 23:58:24 -0400934 cache := make(map[string]*regexp.Regexp)
935
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200936 src, _ := ioutil.ReadFile(file)
937 for i, line := range strings.Split(string(src), "\n") {
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100938 lineNum := i + 1
939 if strings.Contains(line, "////") {
940 // double comment disables ERROR
941 continue
942 }
943 m := errRx.FindStringSubmatch(line)
944 if m == nil {
945 continue
946 }
947 all := m[1]
948 mm := errQuotesRx.FindAllStringSubmatch(all, -1)
949 if mm == nil {
Russ Cox89313062013-02-01 23:10:02 -0500950 log.Fatalf("%s:%d: invalid errchk line: %s", t.goFileName(), lineNum, line)
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100951 }
952 for _, m := range mm {
953 rx := lineRx.ReplaceAllStringFunc(m[1], func(m string) string {
954 n := lineNum
955 if strings.HasPrefix(m, "LINE+") {
956 delta, _ := strconv.Atoi(m[5:])
957 n += delta
958 } else if strings.HasPrefix(m, "LINE-") {
959 delta, _ := strconv.Atoi(m[5:])
960 n -= delta
961 }
Daniel Morsingebb0e5d2012-10-06 09:23:31 +0200962 return fmt.Sprintf("%s:%d", short, n)
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100963 })
Russ Coxd5887c52014-03-11 23:58:24 -0400964 re := cache[rx]
965 if re == nil {
966 var err error
967 re, err = regexp.Compile(rx)
968 if err != nil {
Dmitry Vyukov76477412015-02-19 22:00:11 +0300969 log.Fatalf("%s:%d: invalid regexp \"%s\" in ERROR line: %v", t.goFileName(), lineNum, rx, err)
Russ Coxd5887c52014-03-11 23:58:24 -0400970 }
971 cache[rx] = re
Russ Cox89313062013-02-01 23:10:02 -0500972 }
Russ Coxd5887c52014-03-11 23:58:24 -0400973 prefix := fmt.Sprintf("%s:%d", short, lineNum)
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100974 errs = append(errs, wantedError{
David du Colombier6d20e722014-08-01 22:34:36 +0200975 reStr: rx,
976 re: re,
977 prefix: prefix,
978 lineNum: lineNum,
979 file: short,
Brad Fitzpatrickce837b32012-02-21 14:28:49 +1100980 })
981 }
982 }
983
984 return
985}
Russ Coxcd22afa2012-09-23 13:16:14 -0400986
Dave Cheneydc756702013-01-12 17:52:52 +1100987// defaultRunOutputLimit returns the number of runoutput tests that
988// can be executed in parallel.
989func defaultRunOutputLimit() int {
990 const maxArmCPU = 2
991
992 cpu := runtime.NumCPU()
993 if runtime.GOARCH == "arm" && cpu > maxArmCPU {
994 cpu = maxArmCPU
995 }
996 return cpu
997}
Rémy Oudompheng4f6a2b92013-01-28 21:29:45 +0100998
Anthony Martina5385582013-08-13 12:25:41 -0400999// checkShouldTest runs sanity checks on the shouldTest function.
Rémy Oudompheng4f6a2b92013-01-28 21:29:45 +01001000func checkShouldTest() {
1001 assert := func(ok bool, _ string) {
1002 if !ok {
1003 panic("fail")
1004 }
1005 }
1006 assertNot := func(ok bool, _ string) { assert(!ok, "") }
Anthony Martina5385582013-08-13 12:25:41 -04001007
1008 // Simple tests.
Rémy Oudompheng4f6a2b92013-01-28 21:29:45 +01001009 assert(shouldTest("// +build linux", "linux", "arm"))
1010 assert(shouldTest("// +build !windows", "linux", "arm"))
1011 assertNot(shouldTest("// +build !windows", "windows", "amd64"))
Anthony Martina5385582013-08-13 12:25:41 -04001012
1013 // A file with no build tags will always be tested.
Rémy Oudompheng4f6a2b92013-01-28 21:29:45 +01001014 assert(shouldTest("// This is a test.", "os", "arch"))
Anthony Martina5385582013-08-13 12:25:41 -04001015
1016 // Build tags separated by a space are OR-ed together.
1017 assertNot(shouldTest("// +build arm 386", "linux", "amd64"))
1018
Martin Olsson54990342013-12-27 08:59:02 -08001019 // Build tags separated by a comma are AND-ed together.
Anthony Martina5385582013-08-13 12:25:41 -04001020 assertNot(shouldTest("// +build !windows,!plan9", "windows", "amd64"))
1021 assertNot(shouldTest("// +build !windows,!plan9", "plan9", "386"))
1022
1023 // Build tags on multiple lines are AND-ed together.
1024 assert(shouldTest("// +build !windows\n// +build amd64", "linux", "amd64"))
1025 assertNot(shouldTest("// +build !windows\n// +build amd64", "windows", "amd64"))
1026
1027 // Test that (!a OR !b) matches anything.
1028 assert(shouldTest("// +build !windows !plan9", "windows", "amd64"))
Rémy Oudompheng4f6a2b92013-01-28 21:29:45 +01001029}
Russ Cox551f3f22013-02-14 14:21:26 -05001030
1031// envForDir returns a copy of the environment
1032// suitable for running in the given directory.
1033// The environment is the current process's environment
1034// but with an updated $PWD, so that an os.Getwd in the
1035// child will be faster.
1036func envForDir(dir string) []string {
1037 env := os.Environ()
1038 for i, kv := range env {
1039 if strings.HasPrefix(kv, "PWD=") {
1040 env[i] = "PWD=" + dir
1041 return env
1042 }
1043 }
1044 env = append(env, "PWD="+dir)
1045 return env
1046}
David du Colombier748e5db2014-07-24 23:18:54 +02001047
1048func getenv(key, def string) string {
1049 value := os.Getenv(key)
1050 if value != "" {
1051 return value
1052 }
1053 return def
1054}