github.com/hikaru7719/go@v0.0.0-20181025140707-c8b2ac68906a/src/cmd/go/internal/load/test.go (about) 1 // Copyright 2018 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 load 6 7 import ( 8 "bytes" 9 "cmd/go/internal/base" 10 "cmd/go/internal/str" 11 "errors" 12 "fmt" 13 "go/ast" 14 "go/build" 15 "go/doc" 16 "go/parser" 17 "go/token" 18 "path/filepath" 19 "sort" 20 "strings" 21 "text/template" 22 "unicode" 23 "unicode/utf8" 24 ) 25 26 var TestMainDeps = []string{ 27 // Dependencies for testmain. 28 "os", 29 "testing", 30 "testing/internal/testdeps", 31 } 32 33 type TestCover struct { 34 Mode string 35 Local bool 36 Pkgs []*Package 37 Paths []string 38 Vars []coverInfo 39 DeclVars func(*Package, ...string) map[string]*CoverVar 40 } 41 42 // TestPackagesFor returns three packages: 43 // - ptest, the package p compiled with added "package p" test files. 44 // - pxtest, the result of compiling any "package p_test" (external) test files. 45 // - pmain, the package main corresponding to the test binary (running tests in ptest and pxtest). 46 // 47 // If the package has no "package p_test" test files, pxtest will be nil. 48 // If the non-test compilation of package p can be reused 49 // (for example, if there are no "package p" test files and 50 // package p need not be instrumented for coverage or any other reason), 51 // then the returned ptest == p. 52 // 53 // The caller is expected to have checked that len(p.TestGoFiles)+len(p.XTestGoFiles) > 0, 54 // or else there's no point in any of this. 55 func TestPackagesFor(p *Package, cover *TestCover) (pmain, ptest, pxtest *Package, err error) { 56 var imports, ximports []*Package 57 var stk ImportStack 58 stk.Push(p.ImportPath + " (test)") 59 rawTestImports := str.StringList(p.TestImports) 60 for i, path := range p.TestImports { 61 p1 := LoadImport(path, p.Dir, p, &stk, p.Internal.Build.TestImportPos[path], ResolveImport) 62 if p1.Error != nil { 63 return nil, nil, nil, p1.Error 64 } 65 if len(p1.DepsErrors) > 0 { 66 err := p1.DepsErrors[0] 67 err.Pos = "" // show full import stack 68 return nil, nil, nil, err 69 } 70 if str.Contains(p1.Deps, p.ImportPath) || p1.ImportPath == p.ImportPath { 71 // Same error that loadPackage returns (via reusePackage) in pkg.go. 72 // Can't change that code, because that code is only for loading the 73 // non-test copy of a package. 74 err := &PackageError{ 75 ImportStack: testImportStack(stk[0], p1, p.ImportPath), 76 Err: "import cycle not allowed in test", 77 IsImportCycle: true, 78 } 79 return nil, nil, nil, err 80 } 81 p.TestImports[i] = p1.ImportPath 82 imports = append(imports, p1) 83 } 84 stk.Pop() 85 stk.Push(p.ImportPath + "_test") 86 pxtestNeedsPtest := false 87 rawXTestImports := str.StringList(p.XTestImports) 88 for i, path := range p.XTestImports { 89 p1 := LoadImport(path, p.Dir, p, &stk, p.Internal.Build.XTestImportPos[path], ResolveImport) 90 if p1.Error != nil { 91 return nil, nil, nil, p1.Error 92 } 93 if len(p1.DepsErrors) > 0 { 94 err := p1.DepsErrors[0] 95 err.Pos = "" // show full import stack 96 return nil, nil, nil, err 97 } 98 if p1.ImportPath == p.ImportPath { 99 pxtestNeedsPtest = true 100 } else { 101 ximports = append(ximports, p1) 102 } 103 p.XTestImports[i] = p1.ImportPath 104 } 105 stk.Pop() 106 107 // Test package. 108 if len(p.TestGoFiles) > 0 || p.Name == "main" || cover != nil && cover.Local { 109 ptest = new(Package) 110 *ptest = *p 111 ptest.ForTest = p.ImportPath 112 ptest.GoFiles = nil 113 ptest.GoFiles = append(ptest.GoFiles, p.GoFiles...) 114 ptest.GoFiles = append(ptest.GoFiles, p.TestGoFiles...) 115 ptest.Target = "" 116 // Note: The preparation of the vet config requires that common 117 // indexes in ptest.Imports and ptest.Internal.RawImports 118 // all line up (but RawImports can be shorter than the others). 119 // That is, for 0 ≤ i < len(RawImports), 120 // RawImports[i] is the import string in the program text, and 121 // Imports[i] is the expanded import string (vendoring applied or relative path expanded away). 122 // Any implicitly added imports appear in Imports and Internal.Imports 123 // but not RawImports (because they were not in the source code). 124 // We insert TestImports, imports, and rawTestImports at the start of 125 // these lists to preserve the alignment. 126 // Note that p.Internal.Imports may not be aligned with p.Imports/p.Internal.RawImports, 127 // but we insert at the beginning there too just for consistency. 128 ptest.Imports = str.StringList(p.TestImports, p.Imports) 129 ptest.Internal.Imports = append(imports, p.Internal.Imports...) 130 ptest.Internal.RawImports = str.StringList(rawTestImports, p.Internal.RawImports) 131 ptest.Internal.ForceLibrary = true 132 ptest.Internal.Build = new(build.Package) 133 *ptest.Internal.Build = *p.Internal.Build 134 m := map[string][]token.Position{} 135 for k, v := range p.Internal.Build.ImportPos { 136 m[k] = append(m[k], v...) 137 } 138 for k, v := range p.Internal.Build.TestImportPos { 139 m[k] = append(m[k], v...) 140 } 141 ptest.Internal.Build.ImportPos = m 142 } else { 143 ptest = p 144 } 145 146 // External test package. 147 if len(p.XTestGoFiles) > 0 { 148 pxtest = &Package{ 149 PackagePublic: PackagePublic{ 150 Name: p.Name + "_test", 151 ImportPath: p.ImportPath + "_test", 152 Root: p.Root, 153 Dir: p.Dir, 154 GoFiles: p.XTestGoFiles, 155 Imports: p.XTestImports, 156 ForTest: p.ImportPath, 157 }, 158 Internal: PackageInternal{ 159 LocalPrefix: p.Internal.LocalPrefix, 160 Build: &build.Package{ 161 ImportPos: p.Internal.Build.XTestImportPos, 162 }, 163 Imports: ximports, 164 RawImports: rawXTestImports, 165 166 Asmflags: p.Internal.Asmflags, 167 Gcflags: p.Internal.Gcflags, 168 Ldflags: p.Internal.Ldflags, 169 Gccgoflags: p.Internal.Gccgoflags, 170 }, 171 } 172 if pxtestNeedsPtest { 173 pxtest.Internal.Imports = append(pxtest.Internal.Imports, ptest) 174 } 175 } 176 177 // Build main package. 178 pmain = &Package{ 179 PackagePublic: PackagePublic{ 180 Name: "main", 181 Dir: p.Dir, 182 GoFiles: []string{"_testmain.go"}, 183 ImportPath: p.ImportPath + ".test", 184 Root: p.Root, 185 Imports: str.StringList(TestMainDeps), 186 }, 187 Internal: PackageInternal{ 188 Build: &build.Package{Name: "main"}, 189 Asmflags: p.Internal.Asmflags, 190 Gcflags: p.Internal.Gcflags, 191 Ldflags: p.Internal.Ldflags, 192 Gccgoflags: p.Internal.Gccgoflags, 193 }, 194 } 195 196 // The generated main also imports testing, regexp, and os. 197 // Also the linker introduces implicit dependencies reported by LinkerDeps. 198 stk.Push("testmain") 199 deps := TestMainDeps // cap==len, so safe for append 200 for _, d := range LinkerDeps(p) { 201 deps = append(deps, d) 202 } 203 for _, dep := range deps { 204 if dep == ptest.ImportPath { 205 pmain.Internal.Imports = append(pmain.Internal.Imports, ptest) 206 } else { 207 p1 := LoadImport(dep, "", nil, &stk, nil, 0) 208 if p1.Error != nil { 209 return nil, nil, nil, p1.Error 210 } 211 pmain.Internal.Imports = append(pmain.Internal.Imports, p1) 212 } 213 } 214 stk.Pop() 215 216 if cover != nil && cover.Pkgs != nil { 217 // Add imports, but avoid duplicates. 218 seen := map[*Package]bool{p: true, ptest: true} 219 for _, p1 := range pmain.Internal.Imports { 220 seen[p1] = true 221 } 222 for _, p1 := range cover.Pkgs { 223 if !seen[p1] { 224 seen[p1] = true 225 pmain.Internal.Imports = append(pmain.Internal.Imports, p1) 226 } 227 } 228 } 229 230 // Do initial scan for metadata needed for writing _testmain.go 231 // Use that metadata to update the list of imports for package main. 232 // The list of imports is used by recompileForTest and by the loop 233 // afterward that gathers t.Cover information. 234 t, err := loadTestFuncs(ptest) 235 if err != nil { 236 return nil, nil, nil, err 237 } 238 t.Cover = cover 239 if len(ptest.GoFiles)+len(ptest.CgoFiles) > 0 { 240 pmain.Internal.Imports = append(pmain.Internal.Imports, ptest) 241 pmain.Imports = append(pmain.Imports, ptest.ImportPath) 242 t.ImportTest = true 243 } 244 if pxtest != nil { 245 pmain.Internal.Imports = append(pmain.Internal.Imports, pxtest) 246 pmain.Imports = append(pmain.Imports, pxtest.ImportPath) 247 t.ImportXtest = true 248 } 249 250 // Sort and dedup pmain.Imports. 251 // Only matters for go list -test output. 252 sort.Strings(pmain.Imports) 253 w := 0 254 for _, path := range pmain.Imports { 255 if w == 0 || path != pmain.Imports[w-1] { 256 pmain.Imports[w] = path 257 w++ 258 } 259 } 260 pmain.Imports = pmain.Imports[:w] 261 pmain.Internal.RawImports = str.StringList(pmain.Imports) 262 263 if ptest != p { 264 // We have made modifications to the package p being tested 265 // and are rebuilding p (as ptest). 266 // Arrange to rebuild all packages q such that 267 // the test depends on q and q depends on p. 268 // This makes sure that q sees the modifications to p. 269 // Strictly speaking, the rebuild is only necessary if the 270 // modifications to p change its export metadata, but 271 // determining that is a bit tricky, so we rebuild always. 272 recompileForTest(pmain, p, ptest, pxtest) 273 } 274 275 // Should we apply coverage analysis locally, 276 // only for this package and only for this test? 277 // Yes, if -cover is on but -coverpkg has not specified 278 // a list of packages for global coverage. 279 if cover != nil && cover.Local { 280 ptest.Internal.CoverMode = cover.Mode 281 var coverFiles []string 282 coverFiles = append(coverFiles, ptest.GoFiles...) 283 coverFiles = append(coverFiles, ptest.CgoFiles...) 284 ptest.Internal.CoverVars = cover.DeclVars(ptest, coverFiles...) 285 } 286 287 for _, cp := range pmain.Internal.Imports { 288 if len(cp.Internal.CoverVars) > 0 { 289 t.Cover.Vars = append(t.Cover.Vars, coverInfo{cp, cp.Internal.CoverVars}) 290 } 291 } 292 293 data, err := formatTestmain(t) 294 if err != nil { 295 return nil, nil, nil, err 296 } 297 pmain.Internal.TestmainGo = &data 298 299 return pmain, ptest, pxtest, nil 300 } 301 302 func testImportStack(top string, p *Package, target string) []string { 303 stk := []string{top, p.ImportPath} 304 Search: 305 for p.ImportPath != target { 306 for _, p1 := range p.Internal.Imports { 307 if p1.ImportPath == target || str.Contains(p1.Deps, target) { 308 stk = append(stk, p1.ImportPath) 309 p = p1 310 continue Search 311 } 312 } 313 // Can't happen, but in case it does... 314 stk = append(stk, "<lost path to cycle>") 315 break 316 } 317 return stk 318 } 319 320 func recompileForTest(pmain, preal, ptest, pxtest *Package) { 321 // The "test copy" of preal is ptest. 322 // For each package that depends on preal, make a "test copy" 323 // that depends on ptest. And so on, up the dependency tree. 324 testCopy := map[*Package]*Package{preal: ptest} 325 for _, p := range PackageList([]*Package{pmain}) { 326 if p == preal { 327 continue 328 } 329 // Copy on write. 330 didSplit := p == pmain || p == pxtest 331 split := func() { 332 if didSplit { 333 return 334 } 335 didSplit = true 336 if testCopy[p] != nil { 337 panic("recompileForTest loop") 338 } 339 p1 := new(Package) 340 testCopy[p] = p1 341 *p1 = *p 342 p1.ForTest = preal.ImportPath 343 p1.Internal.Imports = make([]*Package, len(p.Internal.Imports)) 344 copy(p1.Internal.Imports, p.Internal.Imports) 345 p1.Imports = make([]string, len(p.Imports)) 346 copy(p1.Imports, p.Imports) 347 p = p1 348 p.Target = "" 349 } 350 351 // Update p.Internal.Imports to use test copies. 352 for i, imp := range p.Internal.Imports { 353 if p1 := testCopy[imp]; p1 != nil && p1 != imp { 354 split() 355 p.Internal.Imports[i] = p1 356 } 357 } 358 } 359 } 360 361 // isTestFunc tells whether fn has the type of a testing function. arg 362 // specifies the parameter type we look for: B, M or T. 363 func isTestFunc(fn *ast.FuncDecl, arg string) bool { 364 if fn.Type.Results != nil && len(fn.Type.Results.List) > 0 || 365 fn.Type.Params.List == nil || 366 len(fn.Type.Params.List) != 1 || 367 len(fn.Type.Params.List[0].Names) > 1 { 368 return false 369 } 370 ptr, ok := fn.Type.Params.List[0].Type.(*ast.StarExpr) 371 if !ok { 372 return false 373 } 374 // We can't easily check that the type is *testing.M 375 // because we don't know how testing has been imported, 376 // but at least check that it's *M or *something.M. 377 // Same applies for B and T. 378 if name, ok := ptr.X.(*ast.Ident); ok && name.Name == arg { 379 return true 380 } 381 if sel, ok := ptr.X.(*ast.SelectorExpr); ok && sel.Sel.Name == arg { 382 return true 383 } 384 return false 385 } 386 387 // isTest tells whether name looks like a test (or benchmark, according to prefix). 388 // It is a Test (say) if there is a character after Test that is not a lower-case letter. 389 // We don't want TesticularCancer. 390 func isTest(name, prefix string) bool { 391 if !strings.HasPrefix(name, prefix) { 392 return false 393 } 394 if len(name) == len(prefix) { // "Test" is ok 395 return true 396 } 397 rune, _ := utf8.DecodeRuneInString(name[len(prefix):]) 398 return !unicode.IsLower(rune) 399 } 400 401 type coverInfo struct { 402 Package *Package 403 Vars map[string]*CoverVar 404 } 405 406 // loadTestFuncs returns the testFuncs describing the tests that will be run. 407 func loadTestFuncs(ptest *Package) (*testFuncs, error) { 408 t := &testFuncs{ 409 Package: ptest, 410 } 411 for _, file := range ptest.TestGoFiles { 412 if err := t.load(filepath.Join(ptest.Dir, file), "_test", &t.ImportTest, &t.NeedTest); err != nil { 413 return nil, err 414 } 415 } 416 for _, file := range ptest.XTestGoFiles { 417 if err := t.load(filepath.Join(ptest.Dir, file), "_xtest", &t.ImportXtest, &t.NeedXtest); err != nil { 418 return nil, err 419 } 420 } 421 return t, nil 422 } 423 424 // formatTestmain returns the content of the _testmain.go file for t. 425 func formatTestmain(t *testFuncs) ([]byte, error) { 426 var buf bytes.Buffer 427 if err := testmainTmpl.Execute(&buf, t); err != nil { 428 return nil, err 429 } 430 return buf.Bytes(), nil 431 } 432 433 type testFuncs struct { 434 Tests []testFunc 435 Benchmarks []testFunc 436 Examples []testFunc 437 TestMain *testFunc 438 Package *Package 439 ImportTest bool 440 NeedTest bool 441 ImportXtest bool 442 NeedXtest bool 443 Cover *TestCover 444 } 445 446 // ImportPath returns the import path of the package being tested, if it is within GOPATH. 447 // This is printed by the testing package when running benchmarks. 448 func (t *testFuncs) ImportPath() string { 449 pkg := t.Package.ImportPath 450 if strings.HasPrefix(pkg, "_/") { 451 return "" 452 } 453 if pkg == "command-line-arguments" { 454 return "" 455 } 456 return pkg 457 } 458 459 // Covered returns a string describing which packages are being tested for coverage. 460 // If the covered package is the same as the tested package, it returns the empty string. 461 // Otherwise it is a comma-separated human-readable list of packages beginning with 462 // " in", ready for use in the coverage message. 463 func (t *testFuncs) Covered() string { 464 if t.Cover == nil || t.Cover.Paths == nil { 465 return "" 466 } 467 return " in " + strings.Join(t.Cover.Paths, ", ") 468 } 469 470 // Tested returns the name of the package being tested. 471 func (t *testFuncs) Tested() string { 472 return t.Package.Name 473 } 474 475 type testFunc struct { 476 Package string // imported package name (_test or _xtest) 477 Name string // function name 478 Output string // output, for examples 479 Unordered bool // output is allowed to be unordered. 480 } 481 482 var testFileSet = token.NewFileSet() 483 484 func (t *testFuncs) load(filename, pkg string, doImport, seen *bool) error { 485 f, err := parser.ParseFile(testFileSet, filename, nil, parser.ParseComments) 486 if err != nil { 487 return base.ExpandScanner(err) 488 } 489 for _, d := range f.Decls { 490 n, ok := d.(*ast.FuncDecl) 491 if !ok { 492 continue 493 } 494 if n.Recv != nil { 495 continue 496 } 497 name := n.Name.String() 498 switch { 499 case name == "TestMain": 500 if isTestFunc(n, "T") { 501 t.Tests = append(t.Tests, testFunc{pkg, name, "", false}) 502 *doImport, *seen = true, true 503 continue 504 } 505 err := checkTestFunc(n, "M") 506 if err != nil { 507 return err 508 } 509 if t.TestMain != nil { 510 return errors.New("multiple definitions of TestMain") 511 } 512 t.TestMain = &testFunc{pkg, name, "", false} 513 *doImport, *seen = true, true 514 case isTest(name, "Test"): 515 err := checkTestFunc(n, "T") 516 if err != nil { 517 return err 518 } 519 t.Tests = append(t.Tests, testFunc{pkg, name, "", false}) 520 *doImport, *seen = true, true 521 case isTest(name, "Benchmark"): 522 err := checkTestFunc(n, "B") 523 if err != nil { 524 return err 525 } 526 t.Benchmarks = append(t.Benchmarks, testFunc{pkg, name, "", false}) 527 *doImport, *seen = true, true 528 } 529 } 530 ex := doc.Examples(f) 531 sort.Slice(ex, func(i, j int) bool { return ex[i].Order < ex[j].Order }) 532 for _, e := range ex { 533 *doImport = true // import test file whether executed or not 534 if e.Output == "" && !e.EmptyOutput { 535 // Don't run examples with no output. 536 continue 537 } 538 t.Examples = append(t.Examples, testFunc{pkg, "Example" + e.Name, e.Output, e.Unordered}) 539 *seen = true 540 } 541 return nil 542 } 543 544 func checkTestFunc(fn *ast.FuncDecl, arg string) error { 545 if !isTestFunc(fn, arg) { 546 name := fn.Name.String() 547 pos := testFileSet.Position(fn.Pos()) 548 return fmt.Errorf("%s: wrong signature for %s, must be: func %s(%s *testing.%s)", pos, name, name, strings.ToLower(arg), arg) 549 } 550 return nil 551 } 552 553 var testmainTmpl = template.Must(template.New("main").Parse(` 554 package main 555 556 import ( 557 {{if not .TestMain}} 558 "os" 559 {{end}} 560 "testing" 561 "testing/internal/testdeps" 562 563 {{if .ImportTest}} 564 {{if .NeedTest}}_test{{else}}_{{end}} {{.Package.ImportPath | printf "%q"}} 565 {{end}} 566 {{if .ImportXtest}} 567 {{if .NeedXtest}}_xtest{{else}}_{{end}} {{.Package.ImportPath | printf "%s_test" | printf "%q"}} 568 {{end}} 569 {{if .Cover}} 570 {{range $i, $p := .Cover.Vars}} 571 _cover{{$i}} {{$p.Package.ImportPath | printf "%q"}} 572 {{end}} 573 {{end}} 574 ) 575 576 var tests = []testing.InternalTest{ 577 {{range .Tests}} 578 {"{{.Name}}", {{.Package}}.{{.Name}}}, 579 {{end}} 580 } 581 582 var benchmarks = []testing.InternalBenchmark{ 583 {{range .Benchmarks}} 584 {"{{.Name}}", {{.Package}}.{{.Name}}}, 585 {{end}} 586 } 587 588 var examples = []testing.InternalExample{ 589 {{range .Examples}} 590 {"{{.Name}}", {{.Package}}.{{.Name}}, {{.Output | printf "%q"}}, {{.Unordered}}}, 591 {{end}} 592 } 593 594 func init() { 595 testdeps.ImportPath = {{.ImportPath | printf "%q"}} 596 } 597 598 {{if .Cover}} 599 600 // Only updated by init functions, so no need for atomicity. 601 var ( 602 coverCounters = make(map[string][]uint32) 603 coverBlocks = make(map[string][]testing.CoverBlock) 604 ) 605 606 func init() { 607 {{range $i, $p := .Cover.Vars}} 608 {{range $file, $cover := $p.Vars}} 609 coverRegisterFile({{printf "%q" $cover.File}}, _cover{{$i}}.{{$cover.Var}}.Count[:], _cover{{$i}}.{{$cover.Var}}.Pos[:], _cover{{$i}}.{{$cover.Var}}.NumStmt[:]) 610 {{end}} 611 {{end}} 612 } 613 614 func coverRegisterFile(fileName string, counter []uint32, pos []uint32, numStmts []uint16) { 615 if 3*len(counter) != len(pos) || len(counter) != len(numStmts) { 616 panic("coverage: mismatched sizes") 617 } 618 if coverCounters[fileName] != nil { 619 // Already registered. 620 return 621 } 622 coverCounters[fileName] = counter 623 block := make([]testing.CoverBlock, len(counter)) 624 for i := range counter { 625 block[i] = testing.CoverBlock{ 626 Line0: pos[3*i+0], 627 Col0: uint16(pos[3*i+2]), 628 Line1: pos[3*i+1], 629 Col1: uint16(pos[3*i+2]>>16), 630 Stmts: numStmts[i], 631 } 632 } 633 coverBlocks[fileName] = block 634 } 635 {{end}} 636 637 func main() { 638 {{if .Cover}} 639 testing.RegisterCover(testing.Cover{ 640 Mode: {{printf "%q" .Cover.Mode}}, 641 Counters: coverCounters, 642 Blocks: coverBlocks, 643 CoveredPackages: {{printf "%q" .Covered}}, 644 }) 645 {{end}} 646 m := testing.MainStart(testdeps.TestDeps{}, tests, benchmarks, examples) 647 {{with .TestMain}} 648 {{.Package}}.{{.Name}}(m) 649 {{else}} 650 os.Exit(m.Run()) 651 {{end}} 652 } 653 654 `))