github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/tools/go/ssa/testmain.go (about) 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 5 // +build go1.5 6 7 package ssa 8 9 // CreateTestMainPackage synthesizes a main package that runs all the 10 // tests of the supplied packages. 11 // It is closely coupled to $GOROOT/src/cmd/go/test.go and $GOROOT/src/testing. 12 13 import ( 14 "go/ast" 15 exact "go/constant" 16 "go/token" 17 "go/types" 18 "os" 19 "sort" 20 "strings" 21 ) 22 23 // FindTests returns the list of packages that define at least one Test, 24 // Example or Benchmark function (as defined by "go test"), and the 25 // lists of all such functions. 26 // 27 func FindTests(pkgs []*Package) (testpkgs []*Package, tests, benchmarks, examples []*Function) { 28 if len(pkgs) == 0 { 29 return 30 } 31 prog := pkgs[0].Prog 32 33 // The first two of these may be nil: if the program doesn't import "testing", 34 // it can't contain any tests, but it may yet contain Examples. 35 var testSig *types.Signature // func(*testing.T) 36 var benchmarkSig *types.Signature // func(*testing.B) 37 var exampleSig = types.NewSignature(nil, nil, nil, false) // func() 38 39 // Obtain the types from the parameters of testing.Main(). 40 if testingPkg := prog.ImportedPackage("testing"); testingPkg != nil { 41 params := testingPkg.Func("Main").Signature.Params() 42 testSig = funcField(params.At(1).Type()) 43 benchmarkSig = funcField(params.At(2).Type()) 44 } 45 46 seen := make(map[*Package]bool) 47 for _, pkg := range pkgs { 48 if pkg.Prog != prog { 49 panic("wrong Program") 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 58 switch { 59 case testSig != nil && isTestSig(f, "Test", testSig): 60 tests = append(tests, f) 61 case benchmarkSig != nil && isTestSig(f, "Benchmark", benchmarkSig): 62 benchmarks = append(benchmarks, f) 63 case isTestSig(f, "Example", exampleSig): 64 examples = append(examples, f) 65 default: 66 continue 67 } 68 69 if !seen[pkg] { 70 seen[pkg] = true 71 testpkgs = append(testpkgs, pkg) 72 } 73 } 74 } 75 } 76 return 77 } 78 79 // Like isTest, but checks the signature too. 80 func isTestSig(f *Function, prefix string, sig *types.Signature) bool { 81 return isTest(f.Name(), prefix) && types.Identical(f.Signature, sig) 82 } 83 84 // If non-nil, testMainStartBodyHook is called immediately after 85 // startBody for main.init and main.main, making it easy for users to 86 // add custom imports and initialization steps for proprietary build 87 // systems that don't exactly follow 'go test' conventions. 88 var testMainStartBodyHook func(*Function) 89 90 // CreateTestMainPackage creates and returns a synthetic "main" 91 // package that runs all the tests of the supplied packages, similar 92 // to the one that would be created by the 'go test' tool. 93 // 94 // It returns nil if the program contains no tests. 95 // 96 func (prog *Program) CreateTestMainPackage(pkgs ...*Package) *Package { 97 pkgs, tests, benchmarks, examples := FindTests(pkgs) 98 if len(pkgs) == 0 { 99 return nil 100 } 101 102 testmain := &Package{ 103 Prog: prog, 104 Members: make(map[string]Member), 105 values: make(map[types.Object]Value), 106 Pkg: types.NewPackage("test$main", "main"), 107 } 108 109 // Build package's init function. 110 init := &Function{ 111 name: "init", 112 Signature: new(types.Signature), 113 Synthetic: "package initializer", 114 Pkg: testmain, 115 Prog: prog, 116 } 117 init.startBody() 118 119 if testMainStartBodyHook != nil { 120 testMainStartBodyHook(init) 121 } 122 123 // Initialize packages to test. 124 var pkgpaths []string 125 for _, pkg := range pkgs { 126 var v Call 127 v.Call.Value = pkg.init 128 v.setType(types.NewTuple()) 129 init.emit(&v) 130 131 pkgpaths = append(pkgpaths, pkg.Pkg.Path()) 132 } 133 sort.Strings(pkgpaths) 134 init.emit(new(Return)) 135 init.finishBody() 136 testmain.init = init 137 testmain.Pkg.MarkComplete() 138 testmain.Members[init.name] = init 139 140 // For debugging convenience, define an unexported const 141 // that enumerates the packages. 142 packagesConst := types.NewConst(token.NoPos, testmain.Pkg, "packages", tString, 143 exact.MakeString(strings.Join(pkgpaths, " "))) 144 memberFromObject(testmain, packagesConst, nil) 145 146 // Create main *types.Func and *ssa.Function 147 mainFunc := types.NewFunc(token.NoPos, testmain.Pkg, "main", new(types.Signature)) 148 memberFromObject(testmain, mainFunc, nil) 149 main := testmain.Func("main") 150 main.Synthetic = "test main function" 151 152 main.startBody() 153 154 if testMainStartBodyHook != nil { 155 testMainStartBodyHook(main) 156 } 157 158 if testingPkg := prog.ImportedPackage("testing"); testingPkg != nil { 159 testingMain := testingPkg.Func("Main") 160 testingMainParams := testingMain.Signature.Params() 161 162 // The generated code is as if compiled from this: 163 // 164 // func main() { 165 // match := func(_, _ string) (bool, error) { return true, nil } 166 // tests := []testing.InternalTest{{"TestFoo", TestFoo}, ...} 167 // benchmarks := []testing.InternalBenchmark{...} 168 // examples := []testing.InternalExample{...} 169 // testing.Main(match, tests, benchmarks, examples) 170 // } 171 172 matcher := &Function{ 173 name: "matcher", 174 Signature: testingMainParams.At(0).Type().(*types.Signature), 175 Synthetic: "test matcher predicate", 176 parent: main, 177 Pkg: testmain, 178 Prog: prog, 179 } 180 main.AnonFuncs = append(main.AnonFuncs, matcher) 181 matcher.startBody() 182 matcher.emit(&Return{Results: []Value{vTrue, nilConst(types.Universe.Lookup("error").Type())}}) 183 matcher.finishBody() 184 185 // Emit call: testing.Main(matcher, tests, benchmarks, examples). 186 var c Call 187 c.Call.Value = testingMain 188 c.Call.Args = []Value{ 189 matcher, 190 testMainSlice(main, tests, testingMainParams.At(1).Type()), 191 testMainSlice(main, benchmarks, testingMainParams.At(2).Type()), 192 testMainSlice(main, examples, testingMainParams.At(3).Type()), 193 } 194 emitTailCall(main, &c) 195 } else { 196 // The program does not import "testing", but FindTests 197 // returned non-nil, which must mean there were Examples 198 // but no Tests or Benchmarks. 199 // We'll simply call them from testmain.main; this will 200 // ensure they don't panic, but will not check any 201 // "Output:" comments. 202 for _, eg := range examples { 203 var c Call 204 c.Call.Value = eg 205 c.setType(types.NewTuple()) 206 main.emit(&c) 207 } 208 main.emit(&Return{}) 209 main.currentBlock = nil 210 } 211 212 main.finishBody() 213 214 testmain.Members["main"] = main 215 216 if prog.mode&PrintPackages != 0 { 217 printMu.Lock() 218 testmain.WriteTo(os.Stdout) 219 printMu.Unlock() 220 } 221 222 if prog.mode&SanityCheckFunctions != 0 { 223 sanityCheckPackage(testmain) 224 } 225 226 prog.packages[testmain.Pkg] = testmain 227 228 return testmain 229 } 230 231 // testMainSlice emits to fn code to construct a slice of type slice 232 // (one of []testing.Internal{Test,Benchmark,Example}) for all 233 // functions in testfuncs. It returns the slice value. 234 // 235 func testMainSlice(fn *Function, testfuncs []*Function, slice types.Type) Value { 236 if testfuncs == nil { 237 return nilConst(slice) 238 } 239 240 tElem := slice.(*types.Slice).Elem() 241 tPtrString := types.NewPointer(tString) 242 tPtrElem := types.NewPointer(tElem) 243 tPtrFunc := types.NewPointer(funcField(slice)) 244 245 // TODO(adonovan): fix: populate the 246 // testing.InternalExample.Output field correctly so that tests 247 // work correctly under the interpreter. This requires that we 248 // do this step using ASTs, not *ssa.Functions---quite a 249 // redesign. See also the fake runExample in go/ssa/interp. 250 251 // Emit: array = new [n]testing.InternalTest 252 tArray := types.NewArray(tElem, int64(len(testfuncs))) 253 array := emitNew(fn, tArray, token.NoPos) 254 array.Comment = "test main" 255 for i, testfunc := range testfuncs { 256 // Emit: pitem = &array[i] 257 ia := &IndexAddr{X: array, Index: intConst(int64(i))} 258 ia.setType(tPtrElem) 259 pitem := fn.emit(ia) 260 261 // Emit: pname = &pitem.Name 262 fa := &FieldAddr{X: pitem, Field: 0} // .Name 263 fa.setType(tPtrString) 264 pname := fn.emit(fa) 265 266 // Emit: *pname = "testfunc" 267 emitStore(fn, pname, stringConst(testfunc.Name()), token.NoPos) 268 269 // Emit: pfunc = &pitem.F 270 fa = &FieldAddr{X: pitem, Field: 1} // .F 271 fa.setType(tPtrFunc) 272 pfunc := fn.emit(fa) 273 274 // Emit: *pfunc = testfunc 275 emitStore(fn, pfunc, testfunc, token.NoPos) 276 } 277 278 // Emit: slice array[:] 279 sl := &Slice{X: array} 280 sl.setType(slice) 281 return fn.emit(sl) 282 } 283 284 // Given the type of one of the three slice parameters of testing.Main, 285 // returns the function type. 286 func funcField(slice types.Type) *types.Signature { 287 return slice.(*types.Slice).Elem().Underlying().(*types.Struct).Field(1).Type().(*types.Signature) 288 } 289 290 // Plundered from $GOROOT/src/cmd/go/test.go 291 292 // isTest tells whether name looks like a test (or benchmark, according to prefix). 293 // It is a Test (say) if there is a character after Test that is not a lower-case letter. 294 // We don't want TesticularCancer. 295 func isTest(name, prefix string) bool { 296 if !strings.HasPrefix(name, prefix) { 297 return false 298 } 299 if len(name) == len(prefix) { // "Test" is ok 300 return true 301 } 302 return ast.IsExported(name[len(prefix):]) 303 }