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  }