testmain.go 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. // Copyright 2013 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. package ssa
  5. // CreateTestMainPackage synthesizes a main package that runs all the
  6. // tests of the supplied packages.
  7. // It is closely coupled to $GOROOT/src/cmd/go/test.go and $GOROOT/src/testing.
  8. //
  9. // TODO(adonovan): throws this all away now that x/tools/go/packages
  10. // provides access to the actual synthetic test main files.
  11. import (
  12. "bytes"
  13. "fmt"
  14. "go/ast"
  15. "go/parser"
  16. "go/types"
  17. "log"
  18. "os"
  19. "strings"
  20. "text/template"
  21. )
  22. // FindTests returns the Test, Benchmark, and Example functions
  23. // (as defined by "go test") defined in the specified package,
  24. // and its TestMain function, if any.
  25. //
  26. // Deprecated: use x/tools/go/packages to access synthetic testmain packages.
  27. func FindTests(pkg *Package) (tests, benchmarks, examples []*Function, main *Function) {
  28. prog := pkg.Prog
  29. // The first two of these may be nil: if the program doesn't import "testing",
  30. // it can't contain any tests, but it may yet contain Examples.
  31. var testSig *types.Signature // func(*testing.T)
  32. var benchmarkSig *types.Signature // func(*testing.B)
  33. var exampleSig = types.NewSignature(nil, nil, nil, false) // func()
  34. // Obtain the types from the parameters of testing.MainStart.
  35. if testingPkg := prog.ImportedPackage("testing"); testingPkg != nil {
  36. mainStart := testingPkg.Func("MainStart")
  37. params := mainStart.Signature.Params()
  38. testSig = funcField(params.At(1).Type())
  39. benchmarkSig = funcField(params.At(2).Type())
  40. // Does the package define this function?
  41. // func TestMain(*testing.M)
  42. if f := pkg.Func("TestMain"); f != nil {
  43. sig := f.Type().(*types.Signature)
  44. starM := mainStart.Signature.Results().At(0).Type() // *testing.M
  45. if sig.Results().Len() == 0 &&
  46. sig.Params().Len() == 1 &&
  47. types.Identical(sig.Params().At(0).Type(), starM) {
  48. main = f
  49. }
  50. }
  51. }
  52. // TODO(adonovan): use a stable order, e.g. lexical.
  53. for _, mem := range pkg.Members {
  54. if f, ok := mem.(*Function); ok &&
  55. ast.IsExported(f.Name()) &&
  56. strings.HasSuffix(prog.Fset.Position(f.Pos()).Filename, "_test.go") {
  57. switch {
  58. case testSig != nil && isTestSig(f, "Test", testSig):
  59. tests = append(tests, f)
  60. case benchmarkSig != nil && isTestSig(f, "Benchmark", benchmarkSig):
  61. benchmarks = append(benchmarks, f)
  62. case isTestSig(f, "Example", exampleSig):
  63. examples = append(examples, f)
  64. default:
  65. continue
  66. }
  67. }
  68. }
  69. return
  70. }
  71. // Like isTest, but checks the signature too.
  72. func isTestSig(f *Function, prefix string, sig *types.Signature) bool {
  73. return isTest(f.Name(), prefix) && types.Identical(f.Signature, sig)
  74. }
  75. // Given the type of one of the three slice parameters of testing.Main,
  76. // returns the function type.
  77. func funcField(slice types.Type) *types.Signature {
  78. return slice.(*types.Slice).Elem().Underlying().(*types.Struct).Field(1).Type().(*types.Signature)
  79. }
  80. // isTest tells whether name looks like a test (or benchmark, according to prefix).
  81. // It is a Test (say) if there is a character after Test that is not a lower-case letter.
  82. // We don't want TesticularCancer.
  83. // Plundered from $GOROOT/src/cmd/go/test.go
  84. func isTest(name, prefix string) bool {
  85. if !strings.HasPrefix(name, prefix) {
  86. return false
  87. }
  88. if len(name) == len(prefix) { // "Test" is ok
  89. return true
  90. }
  91. return ast.IsExported(name[len(prefix):])
  92. }
  93. // CreateTestMainPackage creates and returns a synthetic "testmain"
  94. // package for the specified package if it defines tests, benchmarks or
  95. // executable examples, or nil otherwise. The new package is named
  96. // "main" and provides a function named "main" that runs the tests,
  97. // similar to the one that would be created by the 'go test' tool.
  98. //
  99. // Subsequent calls to prog.AllPackages include the new package.
  100. // The package pkg must belong to the program prog.
  101. //
  102. // Deprecated: use x/tools/go/packages to access synthetic testmain packages.
  103. func (prog *Program) CreateTestMainPackage(pkg *Package) *Package {
  104. if pkg.Prog != prog {
  105. log.Fatal("Package does not belong to Program")
  106. }
  107. // Template data
  108. var data struct {
  109. Pkg *Package
  110. Tests, Benchmarks, Examples []*Function
  111. Main *Function
  112. Go18 bool
  113. }
  114. data.Pkg = pkg
  115. // Enumerate tests.
  116. data.Tests, data.Benchmarks, data.Examples, data.Main = FindTests(pkg)
  117. if data.Main == nil &&
  118. data.Tests == nil && data.Benchmarks == nil && data.Examples == nil {
  119. return nil
  120. }
  121. // Synthesize source for testmain package.
  122. path := pkg.Pkg.Path() + "$testmain"
  123. tmpl := testmainTmpl
  124. if testingPkg := prog.ImportedPackage("testing"); testingPkg != nil {
  125. // In Go 1.8, testing.MainStart's first argument is an interface, not a func.
  126. data.Go18 = types.IsInterface(testingPkg.Func("MainStart").Signature.Params().At(0).Type())
  127. } else {
  128. // The program does not import "testing", but FindTests
  129. // returned non-nil, which must mean there were Examples
  130. // but no Test, Benchmark, or TestMain functions.
  131. // We'll simply call them from testmain.main; this will
  132. // ensure they don't panic, but will not check any
  133. // "Output:" comments.
  134. // (We should not execute an Example that has no
  135. // "Output:" comment, but it's impossible to tell here.)
  136. tmpl = examplesOnlyTmpl
  137. }
  138. var buf bytes.Buffer
  139. if err := tmpl.Execute(&buf, data); err != nil {
  140. log.Fatalf("internal error expanding template for %s: %v", path, err)
  141. }
  142. if false { // debugging
  143. fmt.Fprintln(os.Stderr, buf.String())
  144. }
  145. // Parse and type-check the testmain package.
  146. f, err := parser.ParseFile(prog.Fset, path+".go", &buf, parser.Mode(0))
  147. if err != nil {
  148. log.Fatalf("internal error parsing %s: %v", path, err)
  149. }
  150. conf := types.Config{
  151. DisableUnusedImportCheck: true,
  152. Importer: importer{pkg},
  153. }
  154. files := []*ast.File{f}
  155. info := &types.Info{
  156. Types: make(map[ast.Expr]types.TypeAndValue),
  157. Defs: make(map[*ast.Ident]types.Object),
  158. Uses: make(map[*ast.Ident]types.Object),
  159. Implicits: make(map[ast.Node]types.Object),
  160. Scopes: make(map[ast.Node]*types.Scope),
  161. Selections: make(map[*ast.SelectorExpr]*types.Selection),
  162. }
  163. testmainPkg, err := conf.Check(path, prog.Fset, files, info)
  164. if err != nil {
  165. log.Fatalf("internal error type-checking %s: %v", path, err)
  166. }
  167. // Create and build SSA code.
  168. testmain := prog.CreatePackage(testmainPkg, files, info, false)
  169. testmain.SetDebugMode(false)
  170. testmain.Build()
  171. testmain.Func("main").Synthetic = "test main function"
  172. testmain.Func("init").Synthetic = "package initializer"
  173. return testmain
  174. }
  175. // An implementation of types.Importer for an already loaded SSA program.
  176. type importer struct {
  177. pkg *Package // package under test; may be non-importable
  178. }
  179. func (imp importer) Import(path string) (*types.Package, error) {
  180. if p := imp.pkg.Prog.ImportedPackage(path); p != nil {
  181. return p.Pkg, nil
  182. }
  183. if path == imp.pkg.Pkg.Path() {
  184. return imp.pkg.Pkg, nil
  185. }
  186. return nil, fmt.Errorf("not found") // can't happen
  187. }
  188. var testmainTmpl = template.Must(template.New("testmain").Parse(`
  189. package main
  190. import "io"
  191. import "os"
  192. import "testing"
  193. import p {{printf "%q" .Pkg.Pkg.Path}}
  194. {{if .Go18}}
  195. type deps struct{}
  196. func (deps) ImportPath() string { return "" }
  197. func (deps) MatchString(pat, str string) (bool, error) { return true, nil }
  198. func (deps) StartCPUProfile(io.Writer) error { return nil }
  199. func (deps) StartTestLog(io.Writer) {}
  200. func (deps) StopCPUProfile() {}
  201. func (deps) StopTestLog() error { return nil }
  202. func (deps) WriteHeapProfile(io.Writer) error { return nil }
  203. func (deps) WriteProfileTo(string, io.Writer, int) error { return nil }
  204. var match deps
  205. {{else}}
  206. func match(_, _ string) (bool, error) { return true, nil }
  207. {{end}}
  208. func main() {
  209. tests := []testing.InternalTest{
  210. {{range .Tests}}
  211. { {{printf "%q" .Name}}, p.{{.Name}} },
  212. {{end}}
  213. }
  214. benchmarks := []testing.InternalBenchmark{
  215. {{range .Benchmarks}}
  216. { {{printf "%q" .Name}}, p.{{.Name}} },
  217. {{end}}
  218. }
  219. examples := []testing.InternalExample{
  220. {{range .Examples}}
  221. {Name: {{printf "%q" .Name}}, F: p.{{.Name}}},
  222. {{end}}
  223. }
  224. m := testing.MainStart(match, tests, benchmarks, examples)
  225. {{with .Main}}
  226. p.{{.Name}}(m)
  227. {{else}}
  228. os.Exit(m.Run())
  229. {{end}}
  230. }
  231. `))
  232. var examplesOnlyTmpl = template.Must(template.New("examples").Parse(`
  233. package main
  234. import p {{printf "%q" .Pkg.Pkg.Path}}
  235. func main() {
  236. {{range .Examples}}
  237. p.{{.Name}}()
  238. {{end}}
  239. }
  240. `))