diff options
author | Russ Cox <rsc@golang.org> | 2014-09-19 13:51:06 -0400 |
---|---|---|
committer | Russ Cox <rsc@golang.org> | 2014-09-19 13:51:06 -0400 |
commit | 3dbb764a310e1243a23c56c19aba8bde32a6c7c0 (patch) | |
tree | 262335d25978b6009192f8e7c42b6766e721d788 /src/cmd/go/test.go | |
parent | 1a6be6763cdd63016a6342235bdc2f6658205293 (diff) | |
download | go-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.go | 46 |
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}} } `)) |