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