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