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 }