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  }