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  }