2016-03-01 15:57:46 -07:00
// Copyright 2015 The Go Authors. All rights reserved.
2015-03-02 18:07:11 -07:00
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"bytes"
"errors"
"flag"
"fmt"
2016-09-14 10:34:27 -06:00
"io/ioutil"
2015-03-02 18:07:11 -07:00
"log"
"os"
"os/exec"
"path/filepath"
"regexp"
"strconv"
"strings"
2015-12-21 18:42:40 -07:00
"sync"
2015-03-02 18:07:11 -07:00
"time"
)
func cmdtest ( ) {
var t tester
2015-12-20 12:29:20 -07:00
var noRebuild bool
2015-03-02 18:07:11 -07:00
flag . BoolVar ( & t . listMode , "list" , false , "list available tests" )
2015-12-20 12:29:20 -07:00
flag . BoolVar ( & t . rebuild , "rebuild" , false , "rebuild everything first" )
flag . BoolVar ( & noRebuild , "no-rebuild" , false , "overrides -rebuild (historical dreg)" )
2015-05-01 20:23:04 -06:00
flag . BoolVar ( & t . keepGoing , "k" , false , "keep going even when error occurred" )
2015-06-08 18:56:27 -06:00
flag . BoolVar ( & t . race , "race" , false , "run in race builder mode (different set of tests)" )
2016-05-04 10:08:27 -06:00
flag . BoolVar ( & t . compileOnly , "compile-only" , false , "compile tests, but don't run them. This is for some builders. Not all dist tests respect this flag, but most do." )
2015-03-02 18:07:11 -07:00
flag . StringVar ( & t . banner , "banner" , "##### " , "banner prefix; blank means no section banners" )
2015-05-14 14:03:02 -06:00
flag . StringVar ( & t . runRxStr , "run" , os . Getenv ( "GOTESTONLY" ) ,
"run only those tests matching the regular expression; empty means to run all. " +
"Special exception: if the string begins with '!', the match is inverted." )
2015-06-04 00:21:30 -06:00
xflagparse ( - 1 ) // any number of args
2015-12-20 12:29:20 -07:00
if noRebuild {
t . rebuild = false
}
2015-03-02 18:07:11 -07:00
t . run ( )
}
// tester executes cmdtest.
type tester struct {
2015-12-21 14:08:57 -07:00
race bool
listMode bool
rebuild bool
failed bool
keepGoing bool
2016-05-04 10:08:27 -06:00
compileOnly bool // just try to compile all tests, but no need to run
2015-12-21 14:08:57 -07:00
runRxStr string
runRx * regexp . Regexp
runRxWant bool // want runRx to match (true) or not match (false)
runNames [ ] string // tests to run, exclusive with runRx; empty means all
banner string // prefix, or "" for none
lastHeading string // last dir heading printed
2015-03-02 18:07:11 -07:00
goroot string
goarch string
gohostarch string
goos string
gohostos string
cgoEnabled bool
partial bool
haveTime bool // the 'time' binary is available
tests [ ] distTest
timeoutScale int
2015-12-21 14:08:57 -07:00
worklist [ ] * work
}
type work struct {
dt * distTest
cmd * exec . Cmd
start chan bool
out [ ] byte
err error
end chan bool
2015-03-02 18:07:11 -07:00
}
// A distTest is a test run by dist test.
// Each test has a unique name and belongs to a group (heading)
type distTest struct {
name string // unique test name; may be filtered with -run flag
heading string // group section; this header is printed before the test is run.
2015-12-21 14:08:57 -07:00
fn func ( * distTest ) error
2015-03-02 18:07:11 -07:00
}
func mustEnv ( k string ) string {
v := os . Getenv ( k )
if v == "" {
log . Fatalf ( "Unset environment variable %v" , k )
}
return v
}
func ( t * tester ) run ( ) {
t . goroot = mustEnv ( "GOROOT" )
t . goos = mustEnv ( "GOOS" )
t . gohostos = mustEnv ( "GOHOSTOS" )
t . goarch = mustEnv ( "GOARCH" )
t . gohostarch = mustEnv ( "GOHOSTARCH" )
slurp , err := exec . Command ( "go" , "env" , "CGO_ENABLED" ) . Output ( )
if err != nil {
log . Fatalf ( "Error running go env CGO_ENABLED: %v" , err )
}
t . cgoEnabled , _ = strconv . ParseBool ( strings . TrimSpace ( string ( slurp ) ) )
2015-06-04 00:21:30 -06:00
if flag . NArg ( ) > 0 && t . runRxStr != "" {
log . Fatalf ( "the -run regular expression flag is mutually exclusive with test name arguments" )
}
t . runNames = flag . Args ( )
2015-03-02 18:07:11 -07:00
if t . hasBash ( ) {
if _ , err := exec . LookPath ( "time" ) ; err == nil {
t . haveTime = true
}
}
2015-12-20 12:29:20 -07:00
if t . rebuild {
2015-03-02 18:07:11 -07:00
t . out ( "Building packages and commands." )
cmd := exec . Command ( "go" , "install" , "-a" , "-v" , "std" , "cmd" )
cmd . Stdout = os . Stdout
cmd . Stderr = os . Stderr
if err := cmd . Run ( ) ; err != nil {
log . Fatalf ( "building packages and commands: %v" , err )
}
}
2015-04-30 14:38:10 -06:00
if t . iOS ( ) {
// Install the Mach exception handler used to intercept
// EXC_BAD_ACCESS and convert it into a Go panic. This is
// necessary for a Go program running under lldb (the way
// we run tests). It is disabled by default because iOS
// apps are not allowed to access the exc_server symbol.
cmd := exec . Command ( "go" , "install" , "-a" , "-tags" , "lldb" , "runtime/cgo" )
cmd . Stdout = os . Stdout
cmd . Stderr = os . Stderr
if err := cmd . Run ( ) ; err != nil {
log . Fatalf ( "building mach exception handler: %v" , err )
}
defer func ( ) {
cmd := exec . Command ( "go" , "install" , "-a" , "runtime/cgo" )
cmd . Stdout = os . Stdout
cmd . Stderr = os . Stderr
if err := cmd . Run ( ) ; err != nil {
log . Fatalf ( "reverting mach exception handler: %v" , err )
}
} ( )
}
2015-03-02 18:07:11 -07:00
t . timeoutScale = 1
if t . goarch == "arm" || t . goos == "windows" {
t . timeoutScale = 2
}
2015-04-23 00:16:31 -06:00
if s := os . Getenv ( "GO_TEST_TIMEOUT_SCALE" ) ; s != "" {
t . timeoutScale , err = strconv . Atoi ( s )
if err != nil {
log . Fatalf ( "failed to parse $GO_TEST_TIMEOUT_SCALE = %q as integer: %v" , s , err )
}
}
2015-03-02 18:07:11 -07:00
if t . runRxStr != "" {
2015-05-14 14:03:02 -06:00
if t . runRxStr [ 0 ] == '!' {
t . runRxWant = false
t . runRxStr = t . runRxStr [ 1 : ]
} else {
t . runRxWant = true
}
2015-03-02 18:07:11 -07:00
t . runRx = regexp . MustCompile ( t . runRxStr )
}
t . registerTests ( )
if t . listMode {
for _ , tt := range t . tests {
fmt . Println ( tt . name )
}
return
}
// we must unset GOROOT_FINAL before tests, because runtime/debug requires
// correct access to source code, so if we have GOROOT_FINAL in effect,
// at least runtime/debug test will fail.
os . Unsetenv ( "GOROOT_FINAL" )
2015-06-04 00:21:30 -06:00
for _ , name := range t . runNames {
if ! t . isRegisteredTestName ( name ) {
log . Fatalf ( "unknown test %q" , name )
}
}
2015-03-02 18:07:11 -07:00
for _ , dt := range t . tests {
2015-06-04 00:21:30 -06:00
if ! t . shouldRunTest ( dt . name ) {
2015-03-02 18:07:11 -07:00
t . partial = true
continue
}
2015-12-21 14:08:57 -07:00
dt := dt // dt used in background after this iteration
if err := dt . fn ( & dt ) ; err != nil {
2015-12-21 18:42:40 -07:00
t . runPending ( & dt ) // in case that hasn't been done yet
2015-12-21 14:08:57 -07:00
t . failed = true
2015-05-01 20:23:04 -06:00
if t . keepGoing {
log . Printf ( "Failed: %v" , err )
} else {
log . Fatalf ( "Failed: %v" , err )
}
2015-03-02 18:07:11 -07:00
}
}
2015-12-21 14:08:57 -07:00
t . runPending ( nil )
if t . failed {
2015-05-01 20:23:04 -06:00
fmt . Println ( "\nFAILED" )
os . Exit ( 1 )
} else if t . partial {
2015-03-02 18:07:11 -07:00
fmt . Println ( "\nALL TESTS PASSED (some were excluded)" )
} else {
fmt . Println ( "\nALL TESTS PASSED" )
}
}
2015-06-04 00:21:30 -06:00
func ( t * tester ) shouldRunTest ( name string ) bool {
if t . runRx != nil {
return t . runRx . MatchString ( name ) == t . runRxWant
}
if len ( t . runNames ) == 0 {
return true
}
for _ , runName := range t . runNames {
if runName == name {
return true
}
}
return false
}
2015-06-04 13:03:38 -06:00
func ( t * tester ) tags ( ) string {
if t . iOS ( ) {
return "-tags=lldb"
}
return "-tags="
}
2015-03-02 18:07:11 -07:00
func ( t * tester ) timeout ( sec int ) string {
return "-timeout=" + fmt . Sprint ( time . Duration ( sec ) * time . Second * time . Duration ( t . timeoutScale ) )
}
2015-05-27 17:33:03 -06:00
// ranGoTest and stdMatches are state closed over by the stdlib
// testing func in registerStdTest below. The tests are run
// sequentially, so there's no need for locks.
2015-06-08 18:56:27 -06:00
//
// ranGoBench and benchMatches are the same, but are only used
// in -race mode.
2015-05-27 17:33:03 -06:00
var (
ranGoTest bool
stdMatches [ ] string
2015-06-08 18:56:27 -06:00
ranGoBench bool
benchMatches [ ] string
2015-05-27 17:33:03 -06:00
)
func ( t * tester ) registerStdTest ( pkg string ) {
testName := "go_test:" + pkg
if t . runRx == nil || t . runRx . MatchString ( testName ) {
stdMatches = append ( stdMatches , pkg )
}
t . tests = append ( t . tests , distTest {
name : testName ,
heading : "Testing packages." ,
2015-12-21 14:08:57 -07:00
fn : func ( dt * distTest ) error {
2015-05-27 17:33:03 -06:00
if ranGoTest {
return nil
}
2015-12-21 14:08:57 -07:00
t . runPending ( dt )
2015-05-27 17:33:03 -06:00
ranGoTest = true
2015-06-08 18:56:27 -06:00
args := [ ] string {
2015-05-27 17:33:03 -06:00
"test" ,
"-short" ,
2015-06-04 13:03:38 -06:00
t . tags ( ) ,
2015-07-20 11:18:26 -06:00
t . timeout ( 180 ) ,
2015-05-27 17:33:03 -06:00
"-gcflags=" + os . Getenv ( "GO_GCFLAGS" ) ,
2015-06-08 18:56:27 -06:00
}
if t . race {
args = append ( args , "-race" )
}
2016-05-04 10:08:27 -06:00
if t . compileOnly {
args = append ( args , "-run=^$" )
}
2015-06-08 18:56:27 -06:00
args = append ( args , stdMatches ... )
cmd := exec . Command ( "go" , args ... )
cmd . Stdout = os . Stdout
cmd . Stderr = os . Stderr
return cmd . Run ( )
} ,
} )
}
func ( t * tester ) registerRaceBenchTest ( pkg string ) {
testName := "go_test_bench:" + pkg
if t . runRx == nil || t . runRx . MatchString ( testName ) {
benchMatches = append ( benchMatches , pkg )
}
t . tests = append ( t . tests , distTest {
name : testName ,
heading : "Running benchmarks briefly." ,
2015-12-21 14:08:57 -07:00
fn : func ( dt * distTest ) error {
2015-06-08 18:56:27 -06:00
if ranGoBench {
return nil
}
2015-12-21 14:08:57 -07:00
t . runPending ( dt )
2015-06-08 18:56:27 -06:00
ranGoBench = true
args := [ ] string {
"test" ,
"-short" ,
"-race" ,
"-run=^$" , // nothing. only benchmarks.
"-benchtime=.1s" ,
"-cpu=4" ,
}
2016-05-04 10:08:27 -06:00
if ! t . compileOnly {
args = append ( args , "-bench=.*" )
}
2015-06-08 18:56:27 -06:00
args = append ( args , benchMatches ... )
cmd := exec . Command ( "go" , args ... )
2015-05-27 17:33:03 -06:00
cmd . Stdout = os . Stdout
cmd . Stderr = os . Stderr
return cmd . Run ( )
} ,
} )
}
func ( t * tester ) registerTests ( ) {
2016-09-10 19:53:30 -06:00
if strings . HasSuffix ( os . Getenv ( "GO_BUILDER_NAME" ) , "-vetall" ) {
// Run vet over std and cmd and call it quits.
t . tests = append ( t . tests , distTest {
name : "vet/all" ,
heading : "go vet std cmd" ,
fn : func ( dt * distTest ) error {
// This runs vet/all for the current platform.
// TODO: on a fast builder or builders, run over all platforms.
t . addCmd ( dt , "src/cmd/vet/all" , "go" , "run" , "main.go" , "-all" )
return nil
} ,
} )
return
}
2015-05-27 17:33:03 -06:00
// Fast path to avoid the ~1 second of `go list std cmd` when
2015-06-04 00:21:30 -06:00
// the caller lists specific tests to run. (as the continuous
2015-05-27 17:33:03 -06:00
// build coordinator does).
2015-06-04 00:21:30 -06:00
if len ( t . runNames ) > 0 {
for _ , name := range t . runNames {
if strings . HasPrefix ( name , "go_test:" ) {
t . registerStdTest ( strings . TrimPrefix ( name , "go_test:" ) )
}
2015-06-08 18:56:27 -06:00
if strings . HasPrefix ( name , "go_test_bench:" ) {
t . registerRaceBenchTest ( strings . TrimPrefix ( name , "go_test_bench:" ) )
}
2015-06-04 00:21:30 -06:00
}
} else {
// Use a format string to only list packages and commands that have tests.
const format = "{{if (or .TestGoFiles .XTestGoFiles)}}{{.ImportPath}}{{end}}"
2016-01-18 20:42:40 -07:00
cmd := exec . Command ( "go" , "list" , "-f" , format )
if t . race {
cmd . Args = append ( cmd . Args , "-tags" , "race" )
}
cmd . Args = append ( cmd . Args , "std" )
2015-06-08 18:56:27 -06:00
if ! t . race {
cmd . Args = append ( cmd . Args , "cmd" )
}
2016-10-25 20:09:36 -06:00
all , err := cmd . Output ( )
2015-06-04 00:21:30 -06:00
if err != nil {
2015-06-05 09:11:34 -06:00
log . Fatalf ( "Error running go list std cmd: %v, %s" , err , all )
2015-06-04 00:21:30 -06:00
}
2015-06-08 18:56:27 -06:00
pkgs := strings . Fields ( string ( all ) )
for _ , pkg := range pkgs {
2015-05-27 17:33:03 -06:00
t . registerStdTest ( pkg )
2015-03-02 18:07:11 -07:00
}
2015-06-08 18:56:27 -06:00
if t . race {
for _ , pkg := range pkgs {
2016-09-14 10:34:27 -06:00
if t . packageHasBenchmarks ( pkg ) {
t . registerRaceBenchTest ( pkg )
}
2015-06-08 18:56:27 -06:00
}
}
}
if t . race {
return
2015-03-02 18:07:11 -07:00
}
// Runtime CPU tests.
2016-05-04 10:08:27 -06:00
if ! t . compileOnly {
testName := "runtime:cpu124"
t . tests = append ( t . tests , distTest {
name : testName ,
heading : "GOMAXPROCS=2 runtime -cpu=1,2,4" ,
fn : func ( dt * distTest ) error {
cmd := t . addCmd ( dt , "src" , "go" , "test" , "-short" , t . timeout ( 300 ) , t . tags ( ) , "runtime" , "-cpu=1,2,4" )
// We set GOMAXPROCS=2 in addition to -cpu=1,2,4 in order to test runtime bootstrap code,
// creation of first goroutines and first garbage collections in the parallel setting.
cmd . Env = mergeEnvLists ( [ ] string { "GOMAXPROCS=2" } , os . Environ ( ) )
return nil
} ,
} )
}
2015-03-02 18:07:11 -07:00
2015-11-16 19:11:35 -07:00
// Test that internal linking of standard packages does not
2016-03-01 16:21:55 -07:00
// require libgcc. This ensures that we can install a Go
2015-11-16 19:11:35 -07:00
// release on a system that does not have a C compiler
// installed and still build Go programs (that don't use cgo).
for _ , pkg := range cgoPackages {
2016-09-16 16:25:52 -06:00
if ! t . internalLink ( ) {
2015-11-16 19:11:35 -07:00
break
}
2015-11-18 10:28:24 -07:00
// ARM libgcc may be Thumb, which internal linking does not support.
if t . goarch == "arm" {
break
}
2015-11-16 19:11:35 -07:00
pkg := pkg
2015-12-21 11:49:12 -07:00
var run string
if pkg == "net" {
run = "TestTCPStress"
}
2015-11-16 19:11:35 -07:00
t . tests = append ( t . tests , distTest {
name : "nolibgcc:" + pkg ,
heading : "Testing without libgcc." ,
2015-12-21 14:08:57 -07:00
fn : func ( dt * distTest ) error {
2016-05-04 10:08:27 -06:00
t . addCmd ( dt , "src" , "go" , "test" , "-short" , "-ldflags=-linkmode=internal -libgcc=none" , t . tags ( ) , pkg , t . runFlag ( run ) )
2015-12-21 14:08:57 -07:00
return nil
2015-11-16 19:11:35 -07:00
} ,
} )
}
2016-09-06 07:16:48 -06:00
// Test internal linking of PIE binaries where it is supported.
2016-09-13 17:19:36 -06:00
if t . goos == "linux" && t . goarch == "amd64" {
2016-09-06 07:16:48 -06:00
t . tests = append ( t . tests , distTest {
name : "pie_internal" ,
heading : "internal linking of -buildmode=pie" ,
fn : func ( dt * distTest ) error {
t . addCmd ( dt , "src" , "go" , "test" , "reflect" , "-short" , "-buildmode=pie" , "-ldflags=-linkmode=internal" , t . timeout ( 60 ) , t . tags ( ) , t . runFlag ( "" ) )
return nil
} ,
} )
}
2015-03-02 18:07:11 -07:00
// sync tests
t . tests = append ( t . tests , distTest {
name : "sync_cpu" ,
heading : "sync -cpu=10" ,
2015-12-21 14:08:57 -07:00
fn : func ( dt * distTest ) error {
2016-05-04 10:08:27 -06:00
t . addCmd ( dt , "src" , "go" , "test" , "sync" , "-short" , t . timeout ( 120 ) , t . tags ( ) , "-cpu=10" , t . runFlag ( "" ) )
2015-12-21 14:08:57 -07:00
return nil
2015-03-02 18:07:11 -07:00
} ,
} )
2016-06-01 12:58:02 -06:00
if t . cgoEnabled && ! t . iOS ( ) {
// Disabled on iOS. golang.org/issue/15919
2015-03-02 18:07:11 -07:00
t . tests = append ( t . tests , distTest {
name : "cgo_stdio" ,
heading : "../misc/cgo/stdio" ,
2015-12-21 14:08:57 -07:00
fn : func ( dt * distTest ) error {
t . addCmd ( dt , "misc/cgo/stdio" , "go" , "run" , filepath . Join ( os . Getenv ( "GOROOT" ) , "test/run.go" ) , "-" , "." )
return nil
2015-03-02 18:07:11 -07:00
} ,
} )
t . tests = append ( t . tests , distTest {
name : "cgo_life" ,
heading : "../misc/cgo/life" ,
2015-12-21 14:08:57 -07:00
fn : func ( dt * distTest ) error {
t . addCmd ( dt , "misc/cgo/life" , "go" , "run" , filepath . Join ( os . Getenv ( "GOROOT" ) , "test/run.go" ) , "-" , "." )
return nil
2015-03-02 18:07:11 -07:00
} ,
} )
2016-02-18 03:49:03 -07:00
fortran := os . Getenv ( "FC" )
if fortran == "" {
fortran , _ = exec . LookPath ( "gfortran" )
}
2016-05-03 03:10:26 -06:00
if t . hasBash ( ) && fortran != "" {
2016-02-18 03:49:03 -07:00
t . tests = append ( t . tests , distTest {
name : "cgo_fortran" ,
heading : "../misc/cgo/fortran" ,
fn : func ( dt * distTest ) error {
2016-02-25 00:09:45 -07:00
t . addCmd ( dt , "misc/cgo/fortran" , "./test.bash" , fortran )
2016-02-18 03:49:03 -07:00
return nil
} ,
} )
}
2015-03-19 11:15:56 -06:00
}
2016-06-01 14:51:30 -06:00
if t . cgoEnabled {
2015-03-02 18:07:11 -07:00
t . tests = append ( t . tests , distTest {
name : "cgo_test" ,
heading : "../misc/cgo/test" ,
fn : t . cgoTest ,
} )
}
if t . raceDetectorSupported ( ) {
t . tests = append ( t . tests , distTest {
name : "race" ,
heading : "Testing race detector" ,
fn : t . raceTest ,
} )
}
2015-03-19 13:36:54 -06:00
if t . hasBash ( ) && t . cgoEnabled && t . goos != "android" && t . goos != "darwin" {
2015-03-02 18:07:11 -07:00
t . registerTest ( "testgodefs" , "../misc/cgo/testgodefs" , "./test.bash" )
}
2015-03-19 11:15:56 -06:00
if t . cgoEnabled {
2015-06-01 21:46:28 -06:00
if t . cgoTestSOSupported ( ) {
2015-03-02 18:07:11 -07:00
t . tests = append ( t . tests , distTest {
name : "testso" ,
heading : "../misc/cgo/testso" ,
2015-12-21 14:08:57 -07:00
fn : func ( dt * distTest ) error {
return t . cgoTestSO ( dt , "misc/cgo/testso" )
2015-06-15 23:36:06 -06:00
} ,
2015-03-02 18:07:11 -07:00
} )
2015-07-27 11:30:26 -06:00
t . tests = append ( t . tests , distTest {
name : "testsovar" ,
heading : "../misc/cgo/testsovar" ,
2015-12-21 14:08:57 -07:00
fn : func ( dt * distTest ) error {
return t . cgoTestSO ( dt , "misc/cgo/testsovar" )
2015-07-27 11:30:26 -06:00
} ,
} )
2015-03-02 18:07:11 -07:00
}
2016-03-24 14:47:02 -06:00
if t . supportedBuildmode ( "c-archive" ) {
2016-08-30 19:11:10 -06:00
t . registerHostTest ( "testcarchive" , "../misc/cgo/testcarchive" , "misc/cgo/testcarchive" , "carchive_test.go" )
2015-04-13 09:31:14 -06:00
}
2015-04-29 18:30:56 -06:00
if t . supportedBuildmode ( "c-shared" ) {
2015-04-17 12:50:07 -06:00
t . registerTest ( "testcshared" , "../misc/cgo/testcshared" , "./test.bash" )
}
2015-04-29 18:30:56 -06:00
if t . supportedBuildmode ( "shared" ) {
2015-05-07 03:29:47 -06:00
t . registerTest ( "testshared" , "../misc/cgo/testshared" , "go" , "test" )
2015-04-26 21:00:48 -06:00
}
2016-08-26 07:04:27 -06:00
if t . supportedBuildmode ( "plugin" ) {
t . registerTest ( "testplugin" , "../misc/cgo/testplugin" , "./test.bash" )
}
2015-03-03 16:50:32 -07:00
if t . gohostos == "linux" && t . goarch == "amd64" {
t . registerTest ( "testasan" , "../misc/cgo/testasan" , "go" , "run" , "main.go" )
}
2015-09-29 22:24:13 -06:00
if t . gohostos == "linux" && t . goarch == "amd64" {
t . registerTest ( "testsanitizers" , "../misc/cgo/testsanitizers" , "./test.bash" )
}
2015-05-08 13:33:30 -06:00
if t . hasBash ( ) && t . goos != "android" && ! t . iOS ( ) && t . gohostos != "windows" {
2015-03-03 16:50:32 -07:00
t . registerTest ( "cgo_errors" , "../misc/cgo/errors" , "./test.bash" )
}
runtime: signal forwarding
Forward signals to signal handlers installed before Go installs its own,
under certain circumstances. In particular, as iant@ suggests, signals are
forwarded iff:
(1) a non-SIG_DFL signal handler existed before Go, and
(2) signal is synchronous (i.e., one of SIGSEGV, SIGBUS, SIGFPE), and
(3a) signal occured on a non-Go thread, or
(3b) signal occurred on a Go thread but in CGo code.
Supported only on Linux, for now.
Change-Id: I403219ee47b26cf65da819fb86cf1ec04d3e25f5
Reviewed-on: https://go-review.googlesource.com/8712
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2015-04-09 12:12:12 -06:00
if t . gohostos == "linux" && t . extLink ( ) {
t . registerTest ( "testsigfwd" , "../misc/cgo/testsigfwd" , "go" , "run" , "main.go" )
}
2015-03-02 18:07:11 -07:00
}
2015-12-21 14:15:36 -07:00
2016-01-06 10:29:10 -07:00
// Doc tests only run on builders.
2015-12-21 14:15:36 -07:00
// They find problems approximately never.
if t . hasBash ( ) && t . goos != "nacl" && t . goos != "android" && ! t . iOS ( ) && os . Getenv ( "GO_BUILDER_NAME" ) != "" {
2015-04-02 09:53:27 -06:00
t . registerTest ( "doc_progs" , "../doc/progs" , "time" , "go" , "run" , "run.go" )
2015-03-02 18:07:11 -07:00
t . registerTest ( "wiki" , "../doc/articles/wiki" , "./test.bash" )
t . registerTest ( "codewalk" , "../doc/codewalk" , "time" , "./run" )
}
2015-12-21 14:15:36 -07:00
2015-05-08 13:33:30 -06:00
if t . goos != "android" && ! t . iOS ( ) {
2016-05-04 10:08:27 -06:00
t . registerTest ( "bench_go1" , "../test/bench/go1" , "go" , "test" , t . timeout ( 600 ) , t . runFlag ( "" ) )
2015-03-02 18:07:11 -07:00
}
2015-05-08 13:33:30 -06:00
if t . goos != "android" && ! t . iOS ( ) {
2015-06-04 00:21:30 -06:00
const nShards = 5
for shard := 0 ; shard < nShards ; shard ++ {
shard := shard
t . tests = append ( t . tests , distTest {
name : fmt . Sprintf ( "test:%d_%d" , shard , nShards ) ,
heading : "../test" ,
2015-12-21 14:08:57 -07:00
fn : func ( dt * distTest ) error { return t . testDirTest ( dt , shard , nShards ) } ,
2015-06-04 00:21:30 -06:00
} )
}
2015-03-02 18:07:11 -07:00
}
2015-05-08 13:33:30 -06:00
if t . goos != "nacl" && t . goos != "android" && ! t . iOS ( ) {
2015-03-02 18:07:11 -07:00
t . tests = append ( t . tests , distTest {
name : "api" ,
heading : "API check" ,
2015-12-21 14:08:57 -07:00
fn : func ( dt * distTest ) error {
2016-05-04 10:08:27 -06:00
if t . compileOnly {
t . addCmd ( dt , "src" , "go" , "build" , filepath . Join ( t . goroot , "src/cmd/api/run.go" ) )
return nil
}
2015-12-21 14:08:57 -07:00
t . addCmd ( dt , "src" , "go" , "run" , filepath . Join ( t . goroot , "src/cmd/api/run.go" ) )
return nil
2015-03-02 18:07:11 -07:00
} ,
} )
}
2015-05-27 17:33:03 -06:00
}
// isRegisteredTestName reports whether a test named testName has already
// been registered.
func ( t * tester ) isRegisteredTestName ( testName string ) bool {
for _ , tt := range t . tests {
if tt . name == testName {
return true
}
}
return false
2015-03-02 18:07:11 -07:00
}
2015-12-21 14:08:57 -07:00
func ( t * tester ) registerTest1 ( seq bool , name , dirBanner , bin string , args ... string ) {
2015-03-02 18:07:11 -07:00
if bin == "time" && ! t . haveTime {
bin , args = args [ 0 ] , args [ 1 : ]
}
2015-06-04 00:21:30 -06:00
if t . isRegisteredTestName ( name ) {
panic ( "duplicate registered test name " + name )
}
2015-03-02 18:07:11 -07:00
t . tests = append ( t . tests , distTest {
name : name ,
heading : dirBanner ,
2015-12-21 14:08:57 -07:00
fn : func ( dt * distTest ) error {
if seq {
t . runPending ( dt )
return t . dirCmd ( filepath . Join ( t . goroot , "src" , dirBanner ) , bin , args ... ) . Run ( )
}
t . addCmd ( dt , filepath . Join ( t . goroot , "src" , dirBanner ) , bin , args ... )
return nil
2015-03-02 18:07:11 -07:00
} ,
} )
}
2015-12-21 14:08:57 -07:00
func ( t * tester ) registerTest ( name , dirBanner , bin string , args ... string ) {
t . registerTest1 ( false , name , dirBanner , bin , args ... )
}
func ( t * tester ) registerSeqTest ( name , dirBanner , bin string , args ... string ) {
t . registerTest1 ( true , name , dirBanner , bin , args ... )
}
func ( t * tester ) bgDirCmd ( dir , bin string , args ... string ) * exec . Cmd {
2015-03-02 18:07:11 -07:00
cmd := exec . Command ( bin , args ... )
if filepath . IsAbs ( dir ) {
cmd . Dir = dir
} else {
cmd . Dir = filepath . Join ( t . goroot , dir )
}
2015-12-21 14:08:57 -07:00
return cmd
}
func ( t * tester ) dirCmd ( dir , bin string , args ... string ) * exec . Cmd {
cmd := t . bgDirCmd ( dir , bin , args ... )
2015-03-02 18:07:11 -07:00
cmd . Stdout = os . Stdout
cmd . Stderr = os . Stderr
2015-07-22 01:23:21 -06:00
if vflag > 1 {
errprintf ( "%s\n" , strings . Join ( cmd . Args , " " ) )
}
2015-03-02 18:07:11 -07:00
return cmd
}
2015-12-21 14:08:57 -07:00
func ( t * tester ) addCmd ( dt * distTest , dir , bin string , args ... string ) * exec . Cmd {
w := & work {
dt : dt ,
cmd : t . bgDirCmd ( dir , bin , args ... ) ,
}
t . worklist = append ( t . worklist , w )
return w . cmd
}
2015-04-30 14:38:10 -06:00
func ( t * tester ) iOS ( ) bool {
return t . goos == "darwin" && ( t . goarch == "arm" || t . goarch == "arm64" )
}
2015-03-02 18:07:11 -07:00
func ( t * tester ) out ( v string ) {
if t . banner == "" {
return
}
fmt . Println ( "\n" + t . banner + v )
}
func ( t * tester ) extLink ( ) bool {
pair := t . gohostos + "-" + t . goarch
switch pair {
case "android-arm" ,
2015-04-20 07:33:25 -06:00
"darwin-arm" , "darwin-arm64" ,
2015-03-02 18:07:11 -07:00
"dragonfly-386" , "dragonfly-amd64" ,
"freebsd-386" , "freebsd-amd64" , "freebsd-arm" ,
2016-04-27 20:18:44 -06:00
"linux-386" , "linux-amd64" , "linux-arm" , "linux-arm64" , "linux-ppc64le" , "linux-mips64" , "linux-mips64le" ,
2015-03-02 18:07:11 -07:00
"netbsd-386" , "netbsd-amd64" ,
2015-03-10 02:11:30 -06:00
"openbsd-386" , "openbsd-amd64" ,
2015-03-13 20:12:09 -06:00
"windows-386" , "windows-amd64" :
2015-03-02 18:07:11 -07:00
return true
case "darwin-386" , "darwin-amd64" :
// linkmode=external fails on OS X 10.6 and earlier == Darwin
// 10.8 and earlier.
unameR , err := exec . Command ( "uname" , "-r" ) . Output ( )
if err != nil {
log . Fatalf ( "uname -r: %v" , err )
}
major , _ := strconv . Atoi ( string ( unameR [ : bytes . IndexByte ( unameR , '.' ) ] ) )
return major > 10
}
return false
}
2016-09-16 16:25:52 -06:00
func ( t * tester ) internalLink ( ) bool {
if t . gohostos == "dragonfly" {
// linkmode=internal fails on dragonfly since errno is a TLS relocation.
return false
}
if t . gohostarch == "ppc64le" {
// linkmode=internal fails on ppc64le because cmd/link doesn't
// handle the TOC correctly (issue 15409).
return false
}
if t . goos == "android" {
return false
}
if t . goos == "darwin" && ( t . goarch == "arm" || t . goarch == "arm64" ) {
return false
}
// Internally linking cgo is incomplete on some architectures.
// https://golang.org/issue/10373
// https://golang.org/issue/14449
2016-09-18 13:28:41 -06:00
if t . goarch == "arm64" || t . goarch == "mips64" || t . goarch == "mips64le" {
2016-09-16 16:25:52 -06:00
return false
}
return true
}
2015-04-29 18:30:56 -06:00
func ( t * tester ) supportedBuildmode ( mode string ) bool {
2015-04-23 15:27:38 -06:00
pair := t . goos + "-" + t . goarch
2015-04-16 11:08:20 -06:00
switch mode {
case "c-archive" :
2015-04-23 15:27:38 -06:00
if ! t . extLink ( ) {
2015-04-16 11:08:20 -06:00
return false
2015-04-23 15:27:38 -06:00
}
switch pair {
2015-07-20 10:33:39 -06:00
case "darwin-386" , "darwin-amd64" , "darwin-arm" , "darwin-arm64" ,
2016-05-26 07:59:29 -06:00
"linux-amd64" , "linux-386" , "windows-amd64" , "windows-386" :
2015-04-16 11:08:20 -06:00
return true
}
2015-04-23 15:27:38 -06:00
return false
2015-04-17 12:50:07 -06:00
case "c-shared" :
2015-04-23 15:27:38 -06:00
switch pair {
2015-11-06 15:28:58 -07:00
case "linux-386" , "linux-amd64" , "linux-arm" , "linux-arm64" ,
2016-01-10 22:23:51 -07:00
"darwin-amd64" , "darwin-386" ,
2016-05-26 13:02:55 -06:00
"android-arm" , "android-arm64" , "android-386" :
2015-04-23 15:27:38 -06:00
return true
}
return false
2015-04-26 21:00:48 -06:00
case "shared" :
switch pair {
2016-03-20 21:00:40 -06:00
case "linux-386" , "linux-amd64" , "linux-arm" , "linux-arm64" , "linux-ppc64le" , "linux-s390x" :
2016-08-26 07:04:27 -06:00
return true
}
return false
case "plugin" :
2016-09-16 16:53:46 -06:00
// linux-arm64 is missing because it causes the external linker
// to crash, see https://golang.org/issue/17138
2016-08-26 07:04:27 -06:00
switch pair {
2016-09-19 12:14:58 -06:00
case "linux-386" , "linux-amd64" , "linux-arm" ,
"darwin-amd64" :
2015-04-26 21:00:48 -06:00
return true
}
return false
2015-04-16 11:08:20 -06:00
default :
2016-05-12 06:13:22 -06:00
log . Fatalf ( "internal error: unknown buildmode %s" , mode )
2015-04-16 11:08:20 -06:00
return false
}
}
2016-08-30 19:11:10 -06:00
func ( t * tester ) registerHostTest ( name , heading , dir , pkg string ) {
2016-03-24 14:47:02 -06:00
t . tests = append ( t . tests , distTest {
name : name ,
2016-08-30 19:11:10 -06:00
heading : heading ,
2016-03-24 14:47:02 -06:00
fn : func ( dt * distTest ) error {
2016-03-24 18:27:34 -06:00
t . runPending ( dt )
2016-08-30 19:11:10 -06:00
return t . runHostTest ( dir , pkg )
2016-03-24 14:47:02 -06:00
} ,
} )
}
2016-08-30 19:11:10 -06:00
func ( t * tester ) runHostTest ( dir , pkg string ) error {
2016-03-24 14:47:02 -06:00
env := mergeEnvLists ( [ ] string { "GOARCH=" + t . gohostarch , "GOOS=" + t . gohostos } , os . Environ ( ) )
2016-08-30 19:11:10 -06:00
defer os . Remove ( filepath . Join ( t . goroot , dir , "test.test" ) )
cmd := t . dirCmd ( dir , "go" , "test" , t . tags ( ) , "-c" , "-o" , "test.test" , pkg )
2016-03-24 14:47:02 -06:00
cmd . Env = env
if err := cmd . Run ( ) ; err != nil {
return err
}
2016-08-30 19:11:10 -06:00
return t . dirCmd ( dir , "./test.test" ) . Run ( )
2016-03-24 14:47:02 -06:00
}
2015-12-21 14:08:57 -07:00
func ( t * tester ) cgoTest ( dt * distTest ) error {
2015-03-02 18:07:11 -07:00
env := mergeEnvLists ( [ ] string { "GOTRACEBACK=2" } , os . Environ ( ) )
2016-05-04 10:08:27 -06:00
cmd := t . addCmd ( dt , "misc/cgo/test" , "go" , "test" , t . tags ( ) , "-ldflags" , "-linkmode=auto" , t . runFlag ( "" ) )
2015-04-07 00:43:11 -06:00
cmd . Env = env
2016-09-16 16:25:52 -06:00
if t . internalLink ( ) {
2016-05-04 10:08:27 -06:00
cmd := t . addCmd ( dt , "misc/cgo/test" , "go" , "test" , "-ldflags" , "-linkmode=internal" , t . runFlag ( "" ) )
2015-03-02 18:07:11 -07:00
cmd . Env = env
}
pair := t . gohostos + "-" + t . goarch
switch pair {
2015-06-29 11:12:10 -06:00
case "darwin-386" , "darwin-amd64" ,
"openbsd-386" , "openbsd-amd64" ,
"windows-386" , "windows-amd64" :
2015-03-02 18:07:11 -07:00
// test linkmode=external, but __thread not supported, so skip testtls.
2015-06-29 11:12:10 -06:00
if ! t . extLink ( ) {
break
}
2015-12-21 14:08:57 -07:00
cmd := t . addCmd ( dt , "misc/cgo/test" , "go" , "test" , "-ldflags" , "-linkmode=external" )
2015-03-02 18:07:11 -07:00
cmd . Env = env
2015-12-21 14:08:57 -07:00
cmd = t . addCmd ( dt , "misc/cgo/test" , "go" , "test" , "-ldflags" , "-linkmode=external -s" )
2015-06-29 11:12:10 -06:00
cmd . Env = env
2015-03-02 18:07:11 -07:00
case "android-arm" ,
"dragonfly-386" , "dragonfly-amd64" ,
"freebsd-386" , "freebsd-amd64" , "freebsd-arm" ,
2016-11-07 09:50:48 -07:00
"linux-386" , "linux-amd64" , "linux-arm" , "linux-ppc64le" , "linux-s390x" ,
2015-03-02 18:07:11 -07:00
"netbsd-386" , "netbsd-amd64" :
2015-12-21 14:08:57 -07:00
cmd := t . addCmd ( dt , "misc/cgo/test" , "go" , "test" , "-ldflags" , "-linkmode=external" )
2015-03-02 18:07:11 -07:00
cmd . Env = env
2015-12-21 14:08:57 -07:00
cmd = t . addCmd ( dt , "misc/cgo/testtls" , "go" , "test" , "-ldflags" , "-linkmode=auto" )
2015-03-02 18:07:11 -07:00
cmd . Env = env
2015-12-21 14:08:57 -07:00
cmd = t . addCmd ( dt , "misc/cgo/testtls" , "go" , "test" , "-ldflags" , "-linkmode=external" )
2015-03-02 18:07:11 -07:00
cmd . Env = env
switch pair {
case "netbsd-386" , "netbsd-amd64" :
// no static linking
case "freebsd-arm" :
// -fPIC compiled tls code will use __tls_get_addr instead
// of __aeabi_read_tp, however, on FreeBSD/ARM, __tls_get_addr
// is implemented in rtld-elf, so -fPIC isn't compatible with
// static linking on FreeBSD/ARM with clang. (cgo depends on
// -fPIC fundamentally.)
default :
cc := mustEnv ( "CC" )
cmd := t . dirCmd ( "misc/cgo/test" ,
cc , "-xc" , "-o" , "/dev/null" , "-static" , "-" )
cmd . Env = env
cmd . Stdin = strings . NewReader ( "int main() {}" )
if err := cmd . Run ( ) ; err != nil {
fmt . Println ( "No support for static linking found (lacks libc.a?), skip cgo static linking test." )
} else {
2016-06-01 12:58:02 -06:00
if t . goos != "android" {
cmd = t . addCmd ( dt , "misc/cgo/testtls" , "go" , "test" , "-ldflags" , ` -linkmode=external -extldflags "-static -pthread" ` )
cmd . Env = env
}
2015-03-02 18:07:11 -07:00
2015-12-21 14:08:57 -07:00
cmd = t . addCmd ( dt , "misc/cgo/nocgo" , "go" , "test" )
2015-03-02 18:07:11 -07:00
cmd . Env = env
2015-12-21 14:08:57 -07:00
cmd = t . addCmd ( dt , "misc/cgo/nocgo" , "go" , "test" , "-ldflags" , ` -linkmode=external ` )
2015-03-02 18:07:11 -07:00
cmd . Env = env
2016-06-01 12:58:02 -06:00
if t . goos != "android" {
cmd = t . addCmd ( dt , "misc/cgo/nocgo" , "go" , "test" , "-ldflags" , ` -linkmode=external -extldflags "-static -pthread" ` )
cmd . Env = env
}
2015-03-02 18:07:11 -07:00
}
if pair != "freebsd-amd64" { // clang -pie fails to link misc/cgo/test
cmd := t . dirCmd ( "misc/cgo/test" ,
cc , "-xc" , "-o" , "/dev/null" , "-pie" , "-" )
cmd . Env = env
cmd . Stdin = strings . NewReader ( "int main() {}" )
if err := cmd . Run ( ) ; err != nil {
fmt . Println ( "No support for -pie found, skip cgo PIE test." )
} else {
2015-12-21 14:08:57 -07:00
cmd = t . addCmd ( dt , "misc/cgo/test" , "go" , "test" , "-ldflags" , ` -linkmode=external -extldflags "-pie" ` )
2015-03-02 18:07:11 -07:00
cmd . Env = env
2015-12-21 14:08:57 -07:00
cmd = t . addCmd ( dt , "misc/cgo/testtls" , "go" , "test" , "-ldflags" , ` -linkmode=external -extldflags "-pie" ` )
2015-03-02 18:07:11 -07:00
cmd . Env = env
2015-12-21 14:08:57 -07:00
cmd = t . addCmd ( dt , "misc/cgo/nocgo" , "go" , "test" , "-ldflags" , ` -linkmode=external -extldflags "-pie" ` )
2015-03-02 18:07:11 -07:00
cmd . Env = env
2015-12-21 14:08:57 -07:00
2015-03-02 18:07:11 -07:00
}
}
}
}
return nil
}
2015-12-21 14:08:57 -07:00
// run pending test commands, in parallel, emitting headers as appropriate.
2015-12-21 18:42:40 -07:00
// When finished, emit header for nextTest, which is going to run after the
2015-12-21 14:08:57 -07:00
// pending commands are done (and runPending returns).
2015-12-21 18:42:40 -07:00
// A test should call runPending if it wants to make sure that it is not
// running in parallel with earlier tests, or if it has some other reason
// for needing the earlier tests to be done.
2015-12-21 14:08:57 -07:00
func ( t * tester ) runPending ( nextTest * distTest ) {
worklist := t . worklist
t . worklist = nil
for _ , w := range worklist {
w . start = make ( chan bool )
w . end = make ( chan bool )
go func ( w * work ) {
if ! <- w . start {
w . out = [ ] byte ( fmt . Sprintf ( "skipped due to earlier error\n" ) )
} else {
w . out , w . err = w . cmd . CombinedOutput ( )
}
w . end <- true
} ( w )
}
started := 0
ended := 0
var last * distTest
for ended < len ( worklist ) {
for started < len ( worklist ) && started - ended < maxbg {
//println("start", started)
w := worklist [ started ]
started ++
w . start <- ! t . failed || t . keepGoing
}
w := worklist [ ended ]
dt := w . dt
if dt . heading != "" && t . lastHeading != dt . heading {
t . lastHeading = dt . heading
t . out ( dt . heading )
}
if dt != last {
// Assumes all the entries for a single dt are in one worklist.
last = w . dt
if vflag > 0 {
fmt . Printf ( "# go tool dist test -run=^%s$\n" , dt . name )
}
}
if vflag > 1 {
errprintf ( "%s\n" , strings . Join ( w . cmd . Args , " " ) )
}
//println("wait", ended)
ended ++
<- w . end
os . Stdout . Write ( w . out )
if w . err != nil {
log . Printf ( "Failed: %v" , w . err )
t . failed = true
}
}
if t . failed && ! t . keepGoing {
log . Fatal ( "FAILED" )
}
if dt := nextTest ; dt != nil {
if dt . heading != "" && t . lastHeading != dt . heading {
t . lastHeading = dt . heading
t . out ( dt . heading )
}
if vflag > 0 {
fmt . Printf ( "# go tool dist test -run=^%s$\n" , dt . name )
}
}
}
2015-06-01 21:46:28 -06:00
func ( t * tester ) cgoTestSOSupported ( ) bool {
if t . goos == "android" || t . iOS ( ) {
// No exec facility on Android or iOS.
return false
}
2015-09-03 14:31:43 -06:00
if t . goarch == "ppc64" {
2015-06-01 21:46:28 -06:00
// External linking not implemented on ppc64 (issue #8912).
return false
}
2015-09-10 08:16:45 -06:00
if t . goarch == "mips64le" || t . goarch == "mips64" {
// External linking not implemented on mips64.
return false
}
2015-06-01 21:46:28 -06:00
return true
}
2015-12-21 14:08:57 -07:00
func ( t * tester ) cgoTestSO ( dt * distTest , testpath string ) error {
t . runPending ( dt )
2015-06-15 23:36:06 -06:00
dir := filepath . Join ( t . goroot , testpath )
2015-06-01 21:46:28 -06:00
// build shared object
output , err := exec . Command ( "go" , "env" , "CC" ) . Output ( )
if err != nil {
return fmt . Errorf ( "Error running go env CC: %v" , err )
}
cc := strings . TrimSuffix ( string ( output ) , "\n" )
if cc == "" {
return errors . New ( "CC environment variable (go env CC) cannot be empty" )
}
output , err = exec . Command ( "go" , "env" , "GOGCCFLAGS" ) . Output ( )
2015-03-03 22:16:26 -07:00
if err != nil {
2015-06-01 21:46:28 -06:00
return fmt . Errorf ( "Error running go env GOGCCFLAGS: %v" , err )
}
gogccflags := strings . Split ( strings . TrimSuffix ( string ( output ) , "\n" ) , " " )
ext := "so"
args := append ( gogccflags , "-shared" )
switch t . goos {
case "darwin" :
ext = "dylib"
args = append ( args , "-undefined" , "suppress" , "-flat_namespace" )
case "windows" :
ext = "dll"
2015-06-15 23:36:06 -06:00
args = append ( args , "-DEXPORT_DLL" )
2015-06-01 21:46:28 -06:00
}
sofname := "libcgosotest." + ext
args = append ( args , "-o" , sofname , "cgoso_c.c" )
if err := t . dirCmd ( dir , cc , args ... ) . Run ( ) ; err != nil {
2015-03-03 22:16:26 -07:00
return err
}
2015-06-01 21:46:28 -06:00
defer os . Remove ( filepath . Join ( dir , sofname ) )
if err := t . dirCmd ( dir , "go" , "build" , "-o" , "main.exe" , "main.go" ) . Run ( ) ; err != nil {
return err
2015-03-03 22:16:26 -07:00
}
2015-06-01 21:46:28 -06:00
defer os . Remove ( filepath . Join ( dir , "main.exe" ) )
cmd := t . dirCmd ( dir , "./main.exe" )
if t . goos != "windows" {
s := "LD_LIBRARY_PATH"
if t . goos == "darwin" {
s = "DYLD_LIBRARY_PATH"
}
cmd . Env = mergeEnvLists ( [ ] string { s + "=." } , os . Environ ( ) )
2016-01-08 05:22:12 -07:00
// On FreeBSD 64-bit architectures, the 32-bit linker looks for
// different environment variables.
if t . goos == "freebsd" && t . gohostarch == "386" {
cmd . Env = mergeEnvLists ( [ ] string { "LD_32_LIBRARY_PATH=." } , cmd . Env )
}
2015-06-01 21:46:28 -06:00
}
return cmd . Run ( )
2015-03-03 22:16:26 -07:00
}
2015-03-02 18:07:11 -07:00
func ( t * tester ) hasBash ( ) bool {
switch t . gohostos {
case "windows" , "plan9" :
return false
}
return true
}
func ( t * tester ) raceDetectorSupported ( ) bool {
switch t . gohostos {
case "linux" , "darwin" , "freebsd" , "windows" :
return t . cgoEnabled && t . goarch == "amd64" && t . gohostos == t . goos
}
return false
}
2016-05-04 10:08:27 -06:00
func ( t * tester ) runFlag ( rx string ) string {
if t . compileOnly {
return "-run=^$"
}
return "-run=" + rx
}
2015-12-21 14:08:57 -07:00
func ( t * tester ) raceTest ( dt * distTest ) error {
t . addCmd ( dt , "src" , "go" , "test" , "-race" , "-i" , "runtime/race" , "flag" , "os/exec" )
2016-05-04 10:08:27 -06:00
t . addCmd ( dt , "src" , "go" , "test" , "-race" , t . runFlag ( "Output" ) , "runtime/race" )
t . addCmd ( dt , "src" , "go" , "test" , "-race" , "-short" , t . runFlag ( "TestParse|TestEcho" ) , "flag" , "os/exec" )
2016-02-16 08:13:10 -07:00
// We don't want the following line, because it
// slows down all.bash (by 10 seconds on my laptop).
// The race builder should catch any error here, but doesn't.
// TODO(iant): Figure out how to catch this.
// t.addCmd(dt, "src", "go", "test", "-race", "-run=TestParallelTest", "cmd/go")
2016-10-28 09:12:39 -06:00
if t . cgoEnabled {
2015-07-22 13:31:54 -06:00
env := mergeEnvLists ( [ ] string { "GOTRACEBACK=2" } , os . Environ ( ) )
2016-05-04 10:08:27 -06:00
cmd := t . addCmd ( dt , "misc/cgo/test" , "go" , "test" , "-race" , "-short" , t . runFlag ( "" ) )
2015-07-22 13:31:54 -06:00
cmd . Env = env
}
2015-03-02 18:07:11 -07:00
if t . extLink ( ) {
// Test with external linking; see issue 9133.
2016-05-04 10:08:27 -06:00
t . addCmd ( dt , "src" , "go" , "test" , "-race" , "-short" , "-ldflags=-linkmode=external" , t . runFlag ( "TestParse|TestEcho" ) , "flag" , "os/exec" )
2015-03-02 18:07:11 -07:00
}
return nil
}
2015-12-21 18:42:40 -07:00
var runtest struct {
sync . Once
exe string
err error
}
2015-12-21 14:08:57 -07:00
func ( t * tester ) testDirTest ( dt * distTest , shard , shards int ) error {
2015-12-21 18:42:40 -07:00
runtest . Do ( func ( ) {
const exe = "runtest.exe" // named exe for Windows, but harmless elsewhere
cmd := t . dirCmd ( "test" , "go" , "build" , "-o" , exe , "run.go" )
cmd . Env = mergeEnvLists ( [ ] string { "GOOS=" + t . gohostos , "GOARCH=" + t . gohostarch , "GOMAXPROCS=" } , os . Environ ( ) )
runtest . exe = filepath . Join ( cmd . Dir , exe )
if err := cmd . Run ( ) ; err != nil {
runtest . err = err
return
}
xatexit ( func ( ) {
os . Remove ( runtest . exe )
} )
} )
if runtest . err != nil {
return runtest . err
2015-03-02 18:07:11 -07:00
}
2016-05-04 10:08:27 -06:00
if t . compileOnly {
return nil
}
2015-12-21 18:42:40 -07:00
t . addCmd ( dt , "test" , runtest . exe ,
2015-06-04 00:21:30 -06:00
fmt . Sprintf ( "--shard=%d" , shard ) ,
fmt . Sprintf ( "--shards=%d" , shards ) ,
2015-12-21 18:42:40 -07:00
)
return nil
2015-03-02 18:07:11 -07:00
}
// mergeEnvLists merges the two environment lists such that
// variables with the same name in "in" replace those in "out".
// out may be mutated.
func mergeEnvLists ( in , out [ ] string ) [ ] string {
NextVar :
for _ , inkv := range in {
k := strings . SplitAfterN ( inkv , "=" , 2 ) [ 0 ]
for i , outkv := range out {
if strings . HasPrefix ( outkv , k ) {
out [ i ] = inkv
continue NextVar
}
}
out = append ( out , inkv )
}
return out
}
2015-11-16 19:11:35 -07:00
// cgoPackages is the standard packages that use cgo.
var cgoPackages = [ ] string {
"crypto/x509" ,
"net" ,
"os/user" ,
}
2016-09-14 10:34:27 -06:00
var funcBenchmark = [ ] byte ( "\nfunc Benchmark" )
// packageHasBenchmarks reports whether pkg has benchmarks.
// On any error, it conservatively returns true.
//
// This exists just to eliminate work on the builders, since compiling
// a test in race mode just to discover it has no benchmarks costs a
// second or two per package, and this function returns false for
// about 100 packages.
func ( t * tester ) packageHasBenchmarks ( pkg string ) bool {
pkgDir := filepath . Join ( t . goroot , "src" , pkg )
d , err := os . Open ( pkgDir )
if err != nil {
return true // conservatively
}
defer d . Close ( )
names , err := d . Readdirnames ( - 1 )
if err != nil {
return true // conservatively
}
for _ , name := range names {
if ! strings . HasSuffix ( name , "_test.go" ) {
continue
}
slurp , err := ioutil . ReadFile ( filepath . Join ( pkgDir , name ) )
if err != nil {
return true // conservatively
}
if bytes . Contains ( slurp , funcBenchmark ) {
return true
}
}
return false
}