github.com/kekek/gb@v0.4.5-0.20170222120241-d4ba64b0b297/test/gotest.go (about)

     1  // Copyright 2011 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 test
     6  
     7  // imported from $GOROOT/src/cmd/go/test.go
     8  
     9  import (
    10  	"bytes"
    11  	"errors"
    12  	"go/ast"
    13  	"go/build"
    14  	"go/doc"
    15  	"go/parser"
    16  	"go/scanner"
    17  	"go/token"
    18  	"os"
    19  	"path/filepath"
    20  	"sort"
    21  	"strings"
    22  	"unicode"
    23  	"unicode/utf8"
    24  
    25  	"github.com/constabulary/gb"
    26  	"github.com/constabulary/gb/internal/debug"
    27  )
    28  
    29  type coverInfo struct {
    30  	Package *gb.Package
    31  	Vars    map[string]*CoverVar
    32  }
    33  
    34  // CoverVar holds the name of the generated coverage variables targeting the named file.
    35  type CoverVar struct {
    36  	File string // local file name
    37  	Var  string // name of count struct
    38  }
    39  
    40  var cwd, _ = os.Getwd()
    41  
    42  // shortPath returns an absolute or relative name for path, whatever is shorter.
    43  func shortPath(path string) string {
    44  	if rel, err := filepath.Rel(cwd, path); err == nil && len(rel) < len(path) {
    45  		return rel
    46  	}
    47  	return path
    48  }
    49  
    50  // isTestMain tells whether fn is a TestMain(m *testing.M) function.
    51  func isTestMain(fn *ast.FuncDecl) bool {
    52  	if fn.Name.String() != "TestMain" ||
    53  		fn.Type.Results != nil && len(fn.Type.Results.List) > 0 ||
    54  		fn.Type.Params == nil ||
    55  		len(fn.Type.Params.List) != 1 ||
    56  		len(fn.Type.Params.List[0].Names) > 1 {
    57  		return false
    58  	}
    59  	ptr, ok := fn.Type.Params.List[0].Type.(*ast.StarExpr)
    60  	if !ok {
    61  		return false
    62  	}
    63  	// We can't easily check that the type is *testing.M
    64  	// because we don't know how testing has been imported,
    65  	// but at least check that it's *M or *something.M.
    66  	if name, ok := ptr.X.(*ast.Ident); ok && name.Name == "M" {
    67  		return true
    68  	}
    69  	if sel, ok := ptr.X.(*ast.SelectorExpr); ok && sel.Sel.Name == "M" {
    70  		return true
    71  	}
    72  	return false
    73  }
    74  
    75  // isTest tells whether name looks like a test (or benchmark, according to prefix).
    76  // It is a Test (say) if there is a character after Test that is not a lower-case letter.
    77  // We don't want TesticularCancer.
    78  func isTest(name, prefix string) bool {
    79  	if !strings.HasPrefix(name, prefix) {
    80  		return false
    81  	}
    82  	if len(name) == len(prefix) { // "Test" is ok
    83  		return true
    84  	}
    85  	rune, _ := utf8.DecodeRuneInString(name[len(prefix):])
    86  	return !unicode.IsLower(rune)
    87  }
    88  
    89  // loadTestFuncs returns the testFuncs describing the tests that will be run.
    90  func loadTestFuncs(ptest *build.Package) (*testFuncs, error) {
    91  	t := &testFuncs{
    92  		Package: ptest,
    93  	}
    94  	debug.Debugf("loadTestFuncs: %v, %v", ptest.TestGoFiles, ptest.XTestGoFiles)
    95  	for _, file := range ptest.TestGoFiles {
    96  		if err := t.load(filepath.Join(ptest.Dir, file), "_test", &t.ImportTest, &t.NeedTest); err != nil {
    97  			return nil, err
    98  		}
    99  	}
   100  	for _, file := range ptest.XTestGoFiles {
   101  		if err := t.load(filepath.Join(ptest.Dir, file), "_xtest", &t.ImportXtest, &t.NeedXtest); err != nil {
   102  			return nil, err
   103  		}
   104  	}
   105  	return t, nil
   106  }
   107  
   108  // writeTestmain writes the _testmain.go file for t to the file named out.
   109  func writeTestmain(out string, t *testFuncs) error {
   110  	f, err := os.Create(out)
   111  	if err != nil {
   112  		return err
   113  	}
   114  	defer f.Close()
   115  
   116  	if err := testmainTmpl.Execute(f, t); err != nil {
   117  		return err
   118  	}
   119  
   120  	return nil
   121  }
   122  
   123  // expandScanner expands a scanner.List error into all the errors in the list.
   124  // The default Error method only shows the first error.
   125  func expandScanner(err error) error {
   126  	// Look for parser errors.
   127  	if err, ok := err.(scanner.ErrorList); ok {
   128  		// Prepare error with \n before each message.
   129  		// When printed in something like context: %v
   130  		// this will put the leading file positions each on
   131  		// its own line.  It will also show all the errors
   132  		// instead of just the first, as err.Error does.
   133  		var buf bytes.Buffer
   134  		for _, e := range err {
   135  			e.Pos.Filename = shortPath(e.Pos.Filename)
   136  			buf.WriteString("\n")
   137  			buf.WriteString(e.Error())
   138  		}
   139  		return errors.New(buf.String())
   140  	}
   141  	return err
   142  }
   143  
   144  type testFuncs struct {
   145  	Tests       []testFunc
   146  	Benchmarks  []testFunc
   147  	Examples    []testFunc
   148  	TestMain    *testFunc
   149  	Package     *build.Package
   150  	ImportTest  bool
   151  	NeedTest    bool
   152  	ImportXtest bool
   153  	NeedXtest   bool
   154  	NeedCgo     bool
   155  	Cover       []coverInfo
   156  }
   157  
   158  func (t *testFuncs) CoverMode() string {
   159  	return ""
   160  }
   161  
   162  func (t *testFuncs) CoverEnabled() bool {
   163  	return false
   164  }
   165  
   166  // Covered returns a string describing which packages are being tested for coverage.
   167  // If the covered package is the same as the tested package, it returns the empty string.
   168  // Otherwise it is a comma-separated human-readable list of packages beginning with
   169  // " in", ready for use in the coverage message.
   170  func (t *testFuncs) Covered() string {
   171  	return ""
   172  }
   173  
   174  // Tested returns the name of the package being tested.
   175  func (t *testFuncs) Tested() string {
   176  	return t.Package.Name
   177  }
   178  
   179  type testFunc struct {
   180  	Package   string // imported package name (_test or _xtest)
   181  	Name      string // function name
   182  	Output    string // output, for examples
   183  	Unordered bool   // TODO(dfc) used for tested output of examples.
   184  }
   185  
   186  var testFileSet = token.NewFileSet()
   187  
   188  func (t *testFuncs) load(filename, pkg string, doImport, seen *bool) error {
   189  	f, err := parser.ParseFile(testFileSet, filename, nil, parser.ParseComments)
   190  	if err != nil {
   191  		return expandScanner(err)
   192  	}
   193  	for _, d := range f.Decls {
   194  		n, ok := d.(*ast.FuncDecl)
   195  		if !ok {
   196  			continue
   197  		}
   198  		if n.Recv != nil {
   199  			continue
   200  		}
   201  		name := n.Name.String()
   202  		switch {
   203  		case isTestMain(n):
   204  			if t.TestMain != nil {
   205  				return errors.New("multiple definitions of TestMain")
   206  			}
   207  			t.TestMain = &testFunc{
   208  				Package: pkg,
   209  				Name:    name,
   210  				Output:  "",
   211  			}
   212  			*doImport, *seen = true, true
   213  		case isTest(name, "Test"):
   214  			t.Tests = append(t.Tests, testFunc{Package: pkg, Name: name})
   215  			*doImport, *seen = true, true
   216  		case isTest(name, "Benchmark"):
   217  			t.Benchmarks = append(t.Benchmarks, testFunc{Package: pkg, Name: name})
   218  			*doImport, *seen = true, true
   219  		}
   220  	}
   221  	ex := doc.Examples(f)
   222  	sort.Sort(byOrder(ex))
   223  	for _, e := range ex {
   224  		*doImport = true // import test file whether executed or not
   225  		if e.Output == "" && !e.EmptyOutput {
   226  			// Don't run examples with no output.
   227  			continue
   228  		}
   229  		t.Examples = append(t.Examples, testFunc{Package: pkg, Name: "Example" + e.Name, Output: e.Output})
   230  		*seen = true
   231  	}
   232  	return nil
   233  }
   234  
   235  type byOrder []*doc.Example
   236  
   237  func (x byOrder) Len() int           { return len(x) }
   238  func (x byOrder) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
   239  func (x byOrder) Less(i, j int) bool { return x[i].Order < x[j].Order }