]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/dist/test.go
[dev.ssa] Merge remote-tracking branch 'origin/master' into mergebranch
[gostls13.git] / src / cmd / dist / test.go
1 // Copyright 2015 The Go Authors.  All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package main
6
7 import (
8         "bytes"
9         "errors"
10         "flag"
11         "fmt"
12         "io/ioutil"
13         "log"
14         "os"
15         "os/exec"
16         "path"
17         "path/filepath"
18         "regexp"
19         "strconv"
20         "strings"
21         "time"
22 )
23
24 func cmdtest() {
25         var t tester
26         flag.BoolVar(&t.listMode, "list", false, "list available tests")
27         flag.BoolVar(&t.noRebuild, "no-rebuild", false, "don't rebuild std and cmd packages")
28         flag.BoolVar(&t.keepGoing, "k", false, "keep going even when error occurred")
29         flag.BoolVar(&t.race, "race", false, "run in race builder mode (different set of tests)")
30         flag.StringVar(&t.banner, "banner", "##### ", "banner prefix; blank means no section banners")
31         flag.StringVar(&t.runRxStr, "run", os.Getenv("GOTESTONLY"),
32                 "run only those tests matching the regular expression; empty means to run all. "+
33                         "Special exception: if the string begins with '!', the match is inverted.")
34         xflagparse(-1) // any number of args
35         t.run()
36 }
37
38 // tester executes cmdtest.
39 type tester struct {
40         race      bool
41         listMode  bool
42         noRebuild bool
43         keepGoing bool
44         runRxStr  string
45         runRx     *regexp.Regexp
46         runRxWant bool     // want runRx to match (true) or not match (false)
47         runNames  []string // tests to run, exclusive with runRx; empty means all
48         banner    string   // prefix, or "" for none
49
50         goroot     string
51         goarch     string
52         gohostarch string
53         goos       string
54         gohostos   string
55         cgoEnabled bool
56         partial    bool
57         haveTime   bool // the 'time' binary is available
58
59         tests        []distTest
60         timeoutScale int
61 }
62
63 // A distTest is a test run by dist test.
64 // Each test has a unique name and belongs to a group (heading)
65 type distTest struct {
66         name    string // unique test name; may be filtered with -run flag
67         heading string // group section; this header is printed before the test is run.
68         fn      func() error
69 }
70
71 func mustEnv(k string) string {
72         v := os.Getenv(k)
73         if v == "" {
74                 log.Fatalf("Unset environment variable %v", k)
75         }
76         return v
77 }
78
79 func (t *tester) run() {
80         t.goroot = mustEnv("GOROOT")
81         t.goos = mustEnv("GOOS")
82         t.gohostos = mustEnv("GOHOSTOS")
83         t.goarch = mustEnv("GOARCH")
84         t.gohostarch = mustEnv("GOHOSTARCH")
85         slurp, err := exec.Command("go", "env", "CGO_ENABLED").Output()
86         if err != nil {
87                 log.Fatalf("Error running go env CGO_ENABLED: %v", err)
88         }
89         t.cgoEnabled, _ = strconv.ParseBool(strings.TrimSpace(string(slurp)))
90         if flag.NArg() > 0 && t.runRxStr != "" {
91                 log.Fatalf("the -run regular expression flag is mutually exclusive with test name arguments")
92         }
93         t.runNames = flag.Args()
94
95         if t.hasBash() {
96                 if _, err := exec.LookPath("time"); err == nil {
97                         t.haveTime = true
98                 }
99         }
100
101         if !t.noRebuild {
102                 t.out("Building packages and commands.")
103                 cmd := exec.Command("go", "install", "-a", "-v", "std", "cmd")
104                 cmd.Stdout = os.Stdout
105                 cmd.Stderr = os.Stderr
106                 if err := cmd.Run(); err != nil {
107                         log.Fatalf("building packages and commands: %v", err)
108                 }
109         }
110
111         if t.iOS() {
112                 // Install the Mach exception handler used to intercept
113                 // EXC_BAD_ACCESS and convert it into a Go panic. This is
114                 // necessary for a Go program running under lldb (the way
115                 // we run tests). It is disabled by default because iOS
116                 // apps are not allowed to access the exc_server symbol.
117                 cmd := exec.Command("go", "install", "-a", "-tags", "lldb", "runtime/cgo")
118                 cmd.Stdout = os.Stdout
119                 cmd.Stderr = os.Stderr
120                 if err := cmd.Run(); err != nil {
121                         log.Fatalf("building mach exception handler: %v", err)
122                 }
123
124                 defer func() {
125                         cmd := exec.Command("go", "install", "-a", "runtime/cgo")
126                         cmd.Stdout = os.Stdout
127                         cmd.Stderr = os.Stderr
128                         if err := cmd.Run(); err != nil {
129                                 log.Fatalf("reverting mach exception handler: %v", err)
130                         }
131                 }()
132         }
133
134         t.timeoutScale = 1
135         if t.goarch == "arm" || t.goos == "windows" {
136                 t.timeoutScale = 2
137         }
138         if s := os.Getenv("GO_TEST_TIMEOUT_SCALE"); s != "" {
139                 t.timeoutScale, err = strconv.Atoi(s)
140                 if err != nil {
141                         log.Fatalf("failed to parse $GO_TEST_TIMEOUT_SCALE = %q as integer: %v", s, err)
142                 }
143         }
144
145         if t.runRxStr != "" {
146                 if t.runRxStr[0] == '!' {
147                         t.runRxWant = false
148                         t.runRxStr = t.runRxStr[1:]
149                 } else {
150                         t.runRxWant = true
151                 }
152                 t.runRx = regexp.MustCompile(t.runRxStr)
153         }
154
155         t.registerTests()
156         if t.listMode {
157                 for _, tt := range t.tests {
158                         fmt.Println(tt.name)
159                 }
160                 return
161         }
162
163         // we must unset GOROOT_FINAL before tests, because runtime/debug requires
164         // correct access to source code, so if we have GOROOT_FINAL in effect,
165         // at least runtime/debug test will fail.
166         os.Unsetenv("GOROOT_FINAL")
167
168         for _, name := range t.runNames {
169                 if !t.isRegisteredTestName(name) {
170                         log.Fatalf("unknown test %q", name)
171                 }
172         }
173
174         var lastHeading string
175         ok := true
176         for _, dt := range t.tests {
177                 if !t.shouldRunTest(dt.name) {
178                         t.partial = true
179                         continue
180                 }
181                 if dt.heading != "" && lastHeading != dt.heading {
182                         lastHeading = dt.heading
183                         t.out(dt.heading)
184                 }
185                 if vflag > 0 {
186                         fmt.Printf("# go tool dist test -run=^%s$\n", dt.name)
187                 }
188                 if err := dt.fn(); err != nil {
189                         ok = false
190                         if t.keepGoing {
191                                 log.Printf("Failed: %v", err)
192                         } else {
193                                 log.Fatalf("Failed: %v", err)
194                         }
195                 }
196         }
197         if !ok {
198                 fmt.Println("\nFAILED")
199                 os.Exit(1)
200         } else if t.partial {
201                 fmt.Println("\nALL TESTS PASSED (some were excluded)")
202         } else {
203                 fmt.Println("\nALL TESTS PASSED")
204         }
205 }
206
207 func (t *tester) shouldRunTest(name string) bool {
208         if t.runRx != nil {
209                 return t.runRx.MatchString(name) == t.runRxWant
210         }
211         if len(t.runNames) == 0 {
212                 return true
213         }
214         for _, runName := range t.runNames {
215                 if runName == name {
216                         return true
217                 }
218         }
219         return false
220 }
221
222 func (t *tester) tags() string {
223         if t.iOS() {
224                 return "-tags=lldb"
225         }
226         return "-tags="
227 }
228
229 func (t *tester) timeout(sec int) string {
230         return "-timeout=" + fmt.Sprint(time.Duration(sec)*time.Second*time.Duration(t.timeoutScale))
231 }
232
233 // ranGoTest and stdMatches are state closed over by the stdlib
234 // testing func in registerStdTest below. The tests are run
235 // sequentially, so there's no need for locks.
236 //
237 // ranGoBench and benchMatches are the same, but are only used
238 // in -race mode.
239 var (
240         ranGoTest  bool
241         stdMatches []string
242
243         ranGoBench   bool
244         benchMatches []string
245 )
246
247 func (t *tester) registerStdTest(pkg string) {
248         testName := "go_test:" + pkg
249         if t.runRx == nil || t.runRx.MatchString(testName) {
250                 stdMatches = append(stdMatches, pkg)
251         }
252         t.tests = append(t.tests, distTest{
253                 name:    testName,
254                 heading: "Testing packages.",
255                 fn: func() error {
256                         if ranGoTest {
257                                 return nil
258                         }
259                         ranGoTest = true
260                         args := []string{
261                                 "test",
262                                 "-short",
263                                 t.tags(),
264                                 t.timeout(180),
265                                 "-gcflags=" + os.Getenv("GO_GCFLAGS"),
266                         }
267                         if t.race {
268                                 args = append(args, "-race")
269                         }
270                         args = append(args, stdMatches...)
271                         cmd := exec.Command("go", args...)
272                         cmd.Stdout = os.Stdout
273                         cmd.Stderr = os.Stderr
274                         return cmd.Run()
275                 },
276         })
277 }
278
279 // TODO: Remove when SSA codegen is used by default.
280 func (t *tester) registerSSATest(pkg string) {
281         switch pkg {
282         // known failures
283         case "runtime":
284                 return
285         }
286         t.tests = append(t.tests, distTest{
287                 name:    "go_test_ssa:" + pkg,
288                 heading: "Testing packages with SSA codegen.",
289                 fn: func() error {
290                         args := []string{
291                                 "test",
292                                 "-short",
293                                 t.timeout(180 * 3), // SSA generates slower code right now
294                                 "-gcflags=" + os.Getenv("GO_GCFLAGS"),
295                         }
296                         if t.race {
297                                 args = append(args, "-race")
298                         }
299                         args = append(args, pkg)
300                         cmd := exec.Command("go", args...)
301                         cmd.Env = mergeEnvLists([]string{"GOSSAPKG=" + path.Base(pkg)}, os.Environ())
302                         cmd.Stdout = os.Stdout
303                         cmd.Stderr = os.Stderr
304                         return cmd.Run()
305                 },
306         })
307 }
308
309 func (t *tester) registerRaceBenchTest(pkg string) {
310         testName := "go_test_bench:" + pkg
311         if t.runRx == nil || t.runRx.MatchString(testName) {
312                 benchMatches = append(benchMatches, pkg)
313         }
314         t.tests = append(t.tests, distTest{
315                 name:    testName,
316                 heading: "Running benchmarks briefly.",
317                 fn: func() error {
318                         if ranGoBench {
319                                 return nil
320                         }
321                         ranGoBench = true
322                         args := []string{
323                                 "test",
324                                 "-short",
325                                 "-race",
326                                 "-run=^$", // nothing. only benchmarks.
327                                 "-bench=.*",
328                                 "-benchtime=.1s",
329                                 "-cpu=4",
330                         }
331                         args = append(args, benchMatches...)
332                         cmd := exec.Command("go", args...)
333                         cmd.Stdout = os.Stdout
334                         cmd.Stderr = os.Stderr
335                         return cmd.Run()
336                 },
337         })
338 }
339
340 func (t *tester) registerTests() {
341         // Fast path to avoid the ~1 second of `go list std cmd` when
342         // the caller lists specific tests to run. (as the continuous
343         // build coordinator does).
344         if len(t.runNames) > 0 {
345                 for _, name := range t.runNames {
346                         if strings.HasPrefix(name, "go_test:") {
347                                 t.registerStdTest(strings.TrimPrefix(name, "go_test:"))
348                         }
349                         if strings.HasPrefix(name, "go_test_bench:") {
350                                 t.registerRaceBenchTest(strings.TrimPrefix(name, "go_test_bench:"))
351                         }
352                         if t.goarch == "amd64" && strings.HasPrefix(name, "go_test_ssa:") {
353                                 t.registerSSATest(strings.TrimPrefix(name, "go_test_ssa:"))
354                         }
355                 }
356         } else {
357                 // Use a format string to only list packages and commands that have tests.
358                 const format = "{{if (or .TestGoFiles .XTestGoFiles)}}{{.ImportPath}}{{end}}"
359                 cmd := exec.Command("go", "list", "-f", format, "std")
360                 if !t.race {
361                         cmd.Args = append(cmd.Args, "cmd")
362                 }
363                 all, err := cmd.CombinedOutput()
364                 if err != nil {
365                         log.Fatalf("Error running go list std cmd: %v, %s", err, all)
366                 }
367                 pkgs := strings.Fields(string(all))
368                 for _, pkg := range pkgs {
369                         t.registerStdTest(pkg)
370                 }
371                 if t.goarch == "amd64" {
372                         for _, pkg := range pkgs {
373                                 t.registerSSATest(pkg)
374                         }
375                 }
376                 if t.race {
377                         for _, pkg := range pkgs {
378                                 t.registerRaceBenchTest(pkg)
379                         }
380                 }
381         }
382
383         if t.race {
384                 return
385         }
386
387         // Runtime CPU tests.
388         testName := "runtime:cpu124"
389         t.tests = append(t.tests, distTest{
390                 name:    testName,
391                 heading: "GOMAXPROCS=2 runtime -cpu=1,2,4",
392                 fn: func() error {
393                         cmd := t.dirCmd("src", "go", "test", "-short", t.timeout(300), t.tags(), "-cpu=1,2,4", "runtime")
394                         // We set GOMAXPROCS=2 in addition to -cpu=1,2,4 in order to test runtime bootstrap code,
395                         // creation of first goroutines and first garbage collections in the parallel setting.
396                         cmd.Env = mergeEnvLists([]string{"GOMAXPROCS=2"}, os.Environ())
397                         return cmd.Run()
398                 },
399         })
400
401         // sync tests
402         t.tests = append(t.tests, distTest{
403                 name:    "sync_cpu",
404                 heading: "sync -cpu=10",
405                 fn: func() error {
406                         return t.dirCmd("src", "go", "test", "-short", t.timeout(120), t.tags(), "-cpu=10", "sync").Run()
407                 },
408         })
409
410         if t.cgoEnabled && t.goos != "android" && !t.iOS() {
411                 // Disabled on android and iOS. golang.org/issue/8345
412                 t.tests = append(t.tests, distTest{
413                         name:    "cgo_stdio",
414                         heading: "../misc/cgo/stdio",
415                         fn: func() error {
416                                 return t.dirCmd("misc/cgo/stdio",
417                                         "go", "run", filepath.Join(os.Getenv("GOROOT"), "test/run.go"), "-", ".").Run()
418                         },
419                 })
420                 t.tests = append(t.tests, distTest{
421                         name:    "cgo_life",
422                         heading: "../misc/cgo/life",
423                         fn: func() error {
424                                 return t.dirCmd("misc/cgo/life",
425                                         "go", "run", filepath.Join(os.Getenv("GOROOT"), "test/run.go"), "-", ".").Run()
426                         },
427                 })
428         }
429         if t.cgoEnabled && t.goos != "android" && !t.iOS() {
430                 // TODO(crawshaw): reenable on android and iOS
431                 // golang.org/issue/8345
432                 //
433                 // These tests are not designed to run off the host.
434                 t.tests = append(t.tests, distTest{
435                         name:    "cgo_test",
436                         heading: "../misc/cgo/test",
437                         fn:      t.cgoTest,
438                 })
439         }
440
441         if t.raceDetectorSupported() {
442                 t.tests = append(t.tests, distTest{
443                         name:    "race",
444                         heading: "Testing race detector",
445                         fn:      t.raceTest,
446                 })
447         }
448
449         if t.hasBash() && t.cgoEnabled && t.goos != "android" && t.goos != "darwin" {
450                 t.registerTest("testgodefs", "../misc/cgo/testgodefs", "./test.bash")
451         }
452         if t.cgoEnabled {
453                 if t.cgoTestSOSupported() {
454                         t.tests = append(t.tests, distTest{
455                                 name:    "testso",
456                                 heading: "../misc/cgo/testso",
457                                 fn: func() error {
458                                         return t.cgoTestSO("misc/cgo/testso")
459                                 },
460                         })
461                         t.tests = append(t.tests, distTest{
462                                 name:    "testsovar",
463                                 heading: "../misc/cgo/testsovar",
464                                 fn: func() error {
465                                         return t.cgoTestSO("misc/cgo/testsovar")
466                                 },
467                         })
468                 }
469                 if t.supportedBuildmode("c-archive") {
470                         t.registerTest("testcarchive", "../misc/cgo/testcarchive", "./test.bash")
471                 }
472                 if t.supportedBuildmode("c-shared") {
473                         t.registerTest("testcshared", "../misc/cgo/testcshared", "./test.bash")
474                 }
475                 if t.supportedBuildmode("shared") {
476                         t.registerTest("testshared", "../misc/cgo/testshared", "go", "test")
477                 }
478                 if t.gohostos == "linux" && t.goarch == "amd64" {
479                         t.registerTest("testasan", "../misc/cgo/testasan", "go", "run", "main.go")
480                 }
481                 if t.gohostos == "linux" && t.goarch == "amd64" {
482                         t.registerTest("testsanitizers", "../misc/cgo/testsanitizers", "./test.bash")
483                 }
484                 if t.hasBash() && t.goos != "android" && !t.iOS() && t.gohostos != "windows" {
485                         t.registerTest("cgo_errors", "../misc/cgo/errors", "./test.bash")
486                 }
487                 if t.gohostos == "linux" && t.extLink() {
488                         t.registerTest("testsigfwd", "../misc/cgo/testsigfwd", "go", "run", "main.go")
489                 }
490         }
491         if t.hasBash() && t.goos != "nacl" && t.goos != "android" && !t.iOS() {
492                 t.registerTest("doc_progs", "../doc/progs", "time", "go", "run", "run.go")
493                 t.registerTest("wiki", "../doc/articles/wiki", "./test.bash")
494                 t.registerTest("codewalk", "../doc/codewalk", "time", "./run")
495                 for _, name := range t.shootoutTests() {
496                         if name == "spectralnorm" && os.Getenv("GO_BUILDER_NAME") == "linux-arm-arm5" {
497                                 // Heavy on floating point and takes over 20 minutes with softfloat.
498                                 // Disabled per Issue 12688.
499                                 continue
500                         }
501                         t.registerTest("shootout:"+name, "../test/bench/shootout", "time", "./timing.sh", "-test", name)
502                 }
503         }
504         if t.goos != "android" && !t.iOS() {
505                 t.registerTest("bench_go1", "../test/bench/go1", "go", "test")
506         }
507         if t.goos != "android" && !t.iOS() {
508                 const nShards = 5
509                 for shard := 0; shard < nShards; shard++ {
510                         shard := shard
511                         t.tests = append(t.tests, distTest{
512                                 name:    fmt.Sprintf("test:%d_%d", shard, nShards),
513                                 heading: "../test",
514                                 fn:      func() error { return t.testDirTest(shard, nShards) },
515                         })
516                 }
517         }
518         if t.goos != "nacl" && t.goos != "android" && !t.iOS() {
519                 t.tests = append(t.tests, distTest{
520                         name:    "api",
521                         heading: "API check",
522                         fn: func() error {
523                                 return t.dirCmd("src", "go", "run", filepath.Join(t.goroot, "src/cmd/api/run.go")).Run()
524                         },
525                 })
526         }
527 }
528
529 // isRegisteredTestName reports whether a test named testName has already
530 // been registered.
531 func (t *tester) isRegisteredTestName(testName string) bool {
532         for _, tt := range t.tests {
533                 if tt.name == testName {
534                         return true
535                 }
536         }
537         return false
538 }
539
540 func (t *tester) registerTest(name, dirBanner, bin string, args ...string) {
541         if bin == "time" && !t.haveTime {
542                 bin, args = args[0], args[1:]
543         }
544         if t.isRegisteredTestName(name) {
545                 panic("duplicate registered test name " + name)
546         }
547         t.tests = append(t.tests, distTest{
548                 name:    name,
549                 heading: dirBanner,
550                 fn: func() error {
551                         return t.dirCmd(filepath.Join(t.goroot, "src", dirBanner), bin, args...).Run()
552                 },
553         })
554 }
555
556 func (t *tester) dirCmd(dir string, bin string, args ...string) *exec.Cmd {
557         cmd := exec.Command(bin, args...)
558         if filepath.IsAbs(dir) {
559                 cmd.Dir = dir
560         } else {
561                 cmd.Dir = filepath.Join(t.goroot, dir)
562         }
563         cmd.Stdout = os.Stdout
564         cmd.Stderr = os.Stderr
565         if vflag > 1 {
566                 errprintf("%s\n", strings.Join(cmd.Args, " "))
567         }
568         return cmd
569 }
570
571 func (t *tester) iOS() bool {
572         return t.goos == "darwin" && (t.goarch == "arm" || t.goarch == "arm64")
573 }
574
575 func (t *tester) out(v string) {
576         if t.banner == "" {
577                 return
578         }
579         fmt.Println("\n" + t.banner + v)
580 }
581
582 func (t *tester) extLink() bool {
583         pair := t.gohostos + "-" + t.goarch
584         switch pair {
585         case "android-arm",
586                 "darwin-arm", "darwin-arm64",
587                 "dragonfly-386", "dragonfly-amd64",
588                 "freebsd-386", "freebsd-amd64", "freebsd-arm",
589                 "linux-386", "linux-amd64", "linux-arm", "linux-arm64",
590                 "netbsd-386", "netbsd-amd64",
591                 "openbsd-386", "openbsd-amd64",
592                 "windows-386", "windows-amd64":
593                 return true
594         case "darwin-386", "darwin-amd64":
595                 // linkmode=external fails on OS X 10.6 and earlier == Darwin
596                 // 10.8 and earlier.
597                 unameR, err := exec.Command("uname", "-r").Output()
598                 if err != nil {
599                         log.Fatalf("uname -r: %v", err)
600                 }
601                 major, _ := strconv.Atoi(string(unameR[:bytes.IndexByte(unameR, '.')]))
602                 return major > 10
603         }
604         return false
605 }
606
607 func (t *tester) supportedBuildmode(mode string) bool {
608         pair := t.goos + "-" + t.goarch
609         switch mode {
610         case "c-archive":
611                 if !t.extLink() {
612                         return false
613                 }
614                 switch pair {
615                 case "darwin-386", "darwin-amd64", "darwin-arm", "darwin-arm64",
616                         "linux-amd64", "linux-386":
617                         return true
618                 }
619                 return false
620         case "c-shared":
621                 // TODO(hyangah): add linux-386.
622                 switch pair {
623                 case "linux-amd64", "darwin-amd64", "android-arm", "linux-arm":
624                         return true
625                 }
626                 return false
627         case "shared":
628                 switch pair {
629                 case "linux-amd64":
630                         return true
631                 }
632                 return false
633         default:
634                 log.Fatal("internal error: unknown buildmode %s", mode)
635                 return false
636         }
637 }
638
639 func (t *tester) cgoTest() error {
640         env := mergeEnvLists([]string{"GOTRACEBACK=2"}, os.Environ())
641
642         if t.goos == "android" || t.iOS() {
643                 cmd := t.dirCmd("misc/cgo/test", "go", "test", t.tags())
644                 cmd.Env = env
645                 return cmd.Run()
646         }
647
648         cmd := t.dirCmd("misc/cgo/test", "go", "test", t.tags(), "-ldflags", "-linkmode=auto")
649         cmd.Env = env
650         if err := cmd.Run(); err != nil {
651                 return err
652         }
653
654         if t.gohostos != "dragonfly" {
655                 // linkmode=internal fails on dragonfly since errno is a TLS relocation.
656                 cmd := t.dirCmd("misc/cgo/test", "go", "test", "-ldflags", "-linkmode=internal")
657                 cmd.Env = env
658                 if err := cmd.Run(); err != nil {
659                         return err
660                 }
661         }
662
663         pair := t.gohostos + "-" + t.goarch
664         switch pair {
665         case "darwin-386", "darwin-amd64",
666                 "openbsd-386", "openbsd-amd64",
667                 "windows-386", "windows-amd64":
668                 // test linkmode=external, but __thread not supported, so skip testtls.
669                 if !t.extLink() {
670                         break
671                 }
672                 cmd := t.dirCmd("misc/cgo/test", "go", "test", "-ldflags", "-linkmode=external")
673                 cmd.Env = env
674                 if err := cmd.Run(); err != nil {
675                         return err
676                 }
677                 cmd = t.dirCmd("misc/cgo/test", "go", "test", "-ldflags", "-linkmode=external -s")
678                 cmd.Env = env
679                 if err := cmd.Run(); err != nil {
680                         return err
681                 }
682         case "android-arm",
683                 "dragonfly-386", "dragonfly-amd64",
684                 "freebsd-386", "freebsd-amd64", "freebsd-arm",
685                 "linux-386", "linux-amd64", "linux-arm",
686                 "netbsd-386", "netbsd-amd64":
687
688                 cmd := t.dirCmd("misc/cgo/test", "go", "test", "-ldflags", "-linkmode=external")
689                 cmd.Env = env
690                 if err := cmd.Run(); err != nil {
691                         return err
692                 }
693                 cmd = t.dirCmd("misc/cgo/testtls", "go", "test", "-ldflags", "-linkmode=auto")
694                 cmd.Env = env
695                 if err := cmd.Run(); err != nil {
696                         return err
697                 }
698                 cmd = t.dirCmd("misc/cgo/testtls", "go", "test", "-ldflags", "-linkmode=external")
699                 cmd.Env = env
700                 if err := cmd.Run(); err != nil {
701                         return err
702                 }
703
704                 switch pair {
705                 case "netbsd-386", "netbsd-amd64":
706                         // no static linking
707                 case "freebsd-arm":
708                         // -fPIC compiled tls code will use __tls_get_addr instead
709                         // of __aeabi_read_tp, however, on FreeBSD/ARM, __tls_get_addr
710                         // is implemented in rtld-elf, so -fPIC isn't compatible with
711                         // static linking on FreeBSD/ARM with clang. (cgo depends on
712                         // -fPIC fundamentally.)
713                 default:
714                         cc := mustEnv("CC")
715                         cmd := t.dirCmd("misc/cgo/test",
716                                 cc, "-xc", "-o", "/dev/null", "-static", "-")
717                         cmd.Env = env
718                         cmd.Stdin = strings.NewReader("int main() {}")
719                         if err := cmd.Run(); err != nil {
720                                 fmt.Println("No support for static linking found (lacks libc.a?), skip cgo static linking test.")
721                         } else {
722                                 cmd = t.dirCmd("misc/cgo/testtls", "go", "test", "-ldflags", `-linkmode=external -extldflags "-static -pthread"`)
723                                 cmd.Env = env
724                                 if err := cmd.Run(); err != nil {
725                                         return err
726                                 }
727
728                                 cmd = t.dirCmd("misc/cgo/nocgo", "go", "test")
729                                 cmd.Env = env
730                                 if err := cmd.Run(); err != nil {
731                                         return err
732                                 }
733
734                                 cmd = t.dirCmd("misc/cgo/nocgo", "go", "test", "-ldflags", `-linkmode=external`)
735                                 cmd.Env = env
736                                 if err := cmd.Run(); err != nil {
737                                         return err
738                                 }
739
740                                 cmd = t.dirCmd("misc/cgo/nocgo", "go", "test", "-ldflags", `-linkmode=external -extldflags "-static -pthread"`)
741                                 cmd.Env = env
742                                 if err := cmd.Run(); err != nil {
743                                         return err
744                                 }
745                         }
746
747                         if pair != "freebsd-amd64" { // clang -pie fails to link misc/cgo/test
748                                 cmd := t.dirCmd("misc/cgo/test",
749                                         cc, "-xc", "-o", "/dev/null", "-pie", "-")
750                                 cmd.Env = env
751                                 cmd.Stdin = strings.NewReader("int main() {}")
752                                 if err := cmd.Run(); err != nil {
753                                         fmt.Println("No support for -pie found, skip cgo PIE test.")
754                                 } else {
755                                         cmd = t.dirCmd("misc/cgo/test", "go", "test", "-ldflags", `-linkmode=external -extldflags "-pie"`)
756                                         cmd.Env = env
757                                         if err := cmd.Run(); err != nil {
758                                                 return fmt.Errorf("pie cgo/test: %v", err)
759                                         }
760                                         cmd = t.dirCmd("misc/cgo/testtls", "go", "test", "-ldflags", `-linkmode=external -extldflags "-pie"`)
761                                         cmd.Env = env
762                                         if err := cmd.Run(); err != nil {
763                                                 return fmt.Errorf("pie cgo/testtls: %v", err)
764                                         }
765                                         cmd = t.dirCmd("misc/cgo/nocgo", "go", "test", "-ldflags", `-linkmode=external -extldflags "-pie"`)
766                                         cmd.Env = env
767                                         if err := cmd.Run(); err != nil {
768                                                 return fmt.Errorf("pie cgo/nocgo: %v", err)
769                                         }
770                                 }
771                         }
772                 }
773         }
774
775         return nil
776 }
777
778 func (t *tester) cgoTestSOSupported() bool {
779         if t.goos == "android" || t.iOS() {
780                 // No exec facility on Android or iOS.
781                 return false
782         }
783         if t.goarch == "ppc64le" || t.goarch == "ppc64" {
784                 // External linking not implemented on ppc64 (issue #8912).
785                 return false
786         }
787         return true
788 }
789
790 func (t *tester) cgoTestSO(testpath string) error {
791         dir := filepath.Join(t.goroot, testpath)
792
793         // build shared object
794         output, err := exec.Command("go", "env", "CC").Output()
795         if err != nil {
796                 return fmt.Errorf("Error running go env CC: %v", err)
797         }
798         cc := strings.TrimSuffix(string(output), "\n")
799         if cc == "" {
800                 return errors.New("CC environment variable (go env CC) cannot be empty")
801         }
802         output, err = exec.Command("go", "env", "GOGCCFLAGS").Output()
803         if err != nil {
804                 return fmt.Errorf("Error running go env GOGCCFLAGS: %v", err)
805         }
806         gogccflags := strings.Split(strings.TrimSuffix(string(output), "\n"), " ")
807
808         ext := "so"
809         args := append(gogccflags, "-shared")
810         switch t.goos {
811         case "darwin":
812                 ext = "dylib"
813                 args = append(args, "-undefined", "suppress", "-flat_namespace")
814         case "windows":
815                 ext = "dll"
816                 args = append(args, "-DEXPORT_DLL")
817         }
818         sofname := "libcgosotest." + ext
819         args = append(args, "-o", sofname, "cgoso_c.c")
820
821         if err := t.dirCmd(dir, cc, args...).Run(); err != nil {
822                 return err
823         }
824         defer os.Remove(filepath.Join(dir, sofname))
825
826         if err := t.dirCmd(dir, "go", "build", "-o", "main.exe", "main.go").Run(); err != nil {
827                 return err
828         }
829         defer os.Remove(filepath.Join(dir, "main.exe"))
830
831         cmd := t.dirCmd(dir, "./main.exe")
832         if t.goos != "windows" {
833                 s := "LD_LIBRARY_PATH"
834                 if t.goos == "darwin" {
835                         s = "DYLD_LIBRARY_PATH"
836                 }
837                 cmd.Env = mergeEnvLists([]string{s + "=."}, os.Environ())
838         }
839         return cmd.Run()
840 }
841
842 func (t *tester) hasBash() bool {
843         switch t.gohostos {
844         case "windows", "plan9":
845                 return false
846         }
847         return true
848 }
849
850 func (t *tester) raceDetectorSupported() bool {
851         switch t.gohostos {
852         case "linux", "darwin", "freebsd", "windows":
853                 return t.cgoEnabled && t.goarch == "amd64" && t.gohostos == t.goos
854         }
855         return false
856 }
857
858 func (t *tester) raceTest() error {
859         if err := t.dirCmd("src", "go", "test", "-race", "-i", "runtime/race", "flag", "os/exec").Run(); err != nil {
860                 return err
861         }
862         if err := t.dirCmd("src", "go", "test", "-race", "-run=Output", "runtime/race").Run(); err != nil {
863                 return err
864         }
865         if err := t.dirCmd("src", "go", "test", "-race", "-short", "flag", "os/exec").Run(); err != nil {
866                 return err
867         }
868         if t.cgoEnabled {
869                 env := mergeEnvLists([]string{"GOTRACEBACK=2"}, os.Environ())
870                 cmd := t.dirCmd("misc/cgo/test", "go", "test", "-race", "-short")
871                 cmd.Env = env
872                 if err := cmd.Run(); err != nil {
873                         return err
874                 }
875         }
876         if t.extLink() {
877                 // Test with external linking; see issue 9133.
878                 if err := t.dirCmd("src", "go", "test", "-race", "-short", "-ldflags=-linkmode=external", "flag", "os/exec").Run(); err != nil {
879                         return err
880                 }
881         }
882         return nil
883 }
884
885 func (t *tester) testDirTest(shard, shards int) error {
886         const runExe = "runtest.exe" // named exe for Windows, but harmless elsewhere
887         cmd := t.dirCmd("test", "go", "build", "-o", runExe, "run.go")
888         cmd.Env = mergeEnvLists([]string{"GOOS=" + t.gohostos, "GOARCH=" + t.gohostarch, "GOMAXPROCS="}, os.Environ())
889         if err := cmd.Run(); err != nil {
890                 return err
891         }
892         absExe := filepath.Join(cmd.Dir, runExe)
893         defer os.Remove(absExe)
894         return t.dirCmd("test", absExe,
895                 fmt.Sprintf("--shard=%d", shard),
896                 fmt.Sprintf("--shards=%d", shards),
897         ).Run()
898 }
899
900 func (t *tester) shootoutTests() []string {
901         sh, err := ioutil.ReadFile(filepath.Join(t.goroot, "test", "bench", "shootout", "timing.sh"))
902         if err != nil {
903                 log.Fatal(err)
904         }
905         m := regexp.MustCompile(`(?m)^\s+run="([\w+ ]+)"\s*$`).FindSubmatch(sh)
906         if m == nil {
907                 log.Fatal("failed to find run=\"...\" line in test/bench/shootout/timing.sh")
908         }
909         return strings.Fields(string(m[1]))
910 }
911
912 // mergeEnvLists merges the two environment lists such that
913 // variables with the same name in "in" replace those in "out".
914 // out may be mutated.
915 func mergeEnvLists(in, out []string) []string {
916 NextVar:
917         for _, inkv := range in {
918                 k := strings.SplitAfterN(inkv, "=", 2)[0]
919                 for i, outkv := range out {
920                         if strings.HasPrefix(outkv, k) {
921                                 out[i] = inkv
922                                 continue NextVar
923                         }
924                 }
925                 out = append(out, inkv)
926         }
927         return out
928 }