summaryrefslogtreecommitdiff
path: root/src/cmd/go/test.go
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2014-09-19 13:51:06 -0400
committerRuss Cox <rsc@golang.org>2014-09-19 13:51:06 -0400
commit3dbb764a310e1243a23c56c19aba8bde32a6c7c0 (patch)
tree262335d25978b6009192f8e7c42b6766e721d788 /src/cmd/go/test.go
parent1a6be6763cdd63016a6342235bdc2f6658205293 (diff)
downloadgo-3dbb764a310e1243a23c56c19aba8bde32a6c7c0.tar.gz
cmd/go, testing: add TestMain support
Fixes issue 8202. LGTM=r, bradfitz R=r, josharian, bradfitz CC=golang-codereviews https://codereview.appspot.com/148770043
Diffstat (limited to 'src/cmd/go/test.go')
-rw-r--r--src/cmd/go/test.go46
1 files changed, 44 insertions, 2 deletions
diff --git a/src/cmd/go/test.go b/src/cmd/go/test.go
index a602469e4..e990b17bf 100644
--- a/src/cmd/go/test.go
+++ b/src/cmd/go/test.go
@@ -6,6 +6,7 @@ package main
import (
"bytes"
+ "errors"
"fmt"
"go/ast"
"go/build"
@@ -291,6 +292,7 @@ var testMainDeps = map[string]bool{
// Dependencies for testmain.
"testing": true,
"regexp": true,
+ "os": true,
}
func runTest(cmd *Command, args []string) {
@@ -687,7 +689,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,
omitDWARF: !testC && !testNeedBinary,
}
- // The generated main also imports testing and regexp.
+ // The generated main also imports testing, regexp, and os.
stk.push("testmain")
for dep := range testMainDeps {
if dep == ptest.ImportPath {
@@ -1057,6 +1059,31 @@ func (b *builder) notest(a *action) error {
return nil
}
+// isTestMain tells whether fn is a TestMain(m *testing.Main) function.
+func isTestMain(fn *ast.FuncDecl) bool {
+ if fn.Name.String() != "TestMain" ||
+ fn.Type.Results != nil && len(fn.Type.Results.List) > 0 ||
+ fn.Type.Params == nil ||
+ len(fn.Type.Params.List) != 1 ||
+ len(fn.Type.Params.List[0].Names) > 1 {
+ return false
+ }
+ ptr, ok := fn.Type.Params.List[0].Type.(*ast.StarExpr)
+ if !ok {
+ return false
+ }
+ // We can't easily check that the type is *testing.M
+ // because we don't know how testing has been imported,
+ // but at least check that it's *M or *something.M.
+ if name, ok := ptr.X.(*ast.Ident); ok && name.Name == "M" {
+ return true
+ }
+ if sel, ok := ptr.X.(*ast.SelectorExpr); ok && sel.Sel.Name == "M" {
+ return true
+ }
+ return false
+}
+
// isTest tells whether name looks like a test (or benchmark, according to prefix).
// It is a Test (say) if there is a character after Test that is not a lower-case letter.
// We don't want TesticularCancer.
@@ -1113,6 +1140,7 @@ type testFuncs struct {
Tests []testFunc
Benchmarks []testFunc
Examples []testFunc
+ TestMain *testFunc
Package *Package
ImportTest bool
NeedTest bool
@@ -1168,6 +1196,12 @@ func (t *testFuncs) load(filename, pkg string, doImport, seen *bool) error {
}
name := n.Name.String()
switch {
+ case isTestMain(n):
+ if t.TestMain != nil {
+ return errors.New("multiple definitions of TestMain")
+ }
+ t.TestMain = &testFunc{pkg, name, ""}
+ *doImport, *seen = true, true
case isTest(name, "Test"):
t.Tests = append(t.Tests, testFunc{pkg, name, ""})
*doImport, *seen = true, true
@@ -1200,6 +1234,9 @@ var testmainTmpl = template.Must(template.New("main").Parse(`
package main
import (
+{{if not .TestMain}}
+ "os"
+{{end}}
"regexp"
"testing"
@@ -1294,7 +1331,12 @@ func main() {
CoveredPackages: {{printf "%q" .Covered}},
})
{{end}}
- testing.Main(matchString, tests, benchmarks, examples)
+ m := testing.MainStart(matchString, tests, benchmarks, examples)
+{{with .TestMain}}
+ {{.Package}}.{{.Name}}(m)
+{{else}}
+ os.Exit(m.Run())
+{{end}}
}
`))