github.com/megatontech/mynoteforgo@v0.0.0-20200507084910-5d0c6ea6e890/源码/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 allTestImports := make([]*Package, 0, len(pmain.Internal.Imports)+len(imports)+len(ximports)) 231 allTestImports = append(allTestImports, pmain.Internal.Imports...) 232 allTestImports = append(allTestImports, imports...) 233 allTestImports = append(allTestImports, ximports...) 234 setToolFlags(allTestImports...) 235 236 // Do initial scan for metadata needed for writing _testmain.go 237 // Use that metadata to update the list of imports for package main. 238 // The list of imports is used by recompileForTest and by the loop 239 // afterward that gathers t.Cover information. 240 t, err := loadTestFuncs(ptest) 241 if err != nil { 242 return nil, nil, nil, err 243 } 244 t.Cover = cover 245 if len(ptest.GoFiles)+len(ptest.CgoFiles) > 0 { 246 pmain.Internal.Imports = append(pmain.Internal.Imports, ptest) 247 pmain.Imports = append(pmain.Imports, ptest.ImportPath) 248 t.ImportTest = true 249 } 250 if pxtest != nil { 251 pmain.Internal.Imports = append(pmain.Internal.Imports, pxtest) 252 pmain.Imports = append(pmain.Imports, pxtest.ImportPath) 253 t.ImportXtest = true 254 } 255 256 // Sort and dedup pmain.Imports. 257 // Only matters for go list -test output. 258 sort.Strings(pmain.Imports) 259 w := 0 260 for _, path := range pmain.Imports { 261 if w == 0 || path != pmain.Imports[w-1] { 262 pmain.Imports[w] = path 263 w++ 264 } 265 } 266 pmain.Imports = pmain.Imports[:w] 267 pmain.Internal.RawImports = str.StringList(pmain.Imports) 268 269 if ptest != p { 270 // We have made modifications to the package p being tested 271 // and are rebuilding p (as ptest). 272 // Arrange to rebuild all packages q such that 273 // the test depends on q and q depends on p. 274 // This makes sure that q sees the modifications to p. 275 // Strictly speaking, the rebuild is only necessary if the 276 // modifications to p change its export metadata, but 277 // determining that is a bit tricky, so we rebuild always. 278 recompileForTest(pmain, p, ptest, pxtest) 279 } 280 281 // Should we apply coverage analysis locally, 282 // only for this package and only for this test? 283 // Yes, if -cover is on but -coverpkg has not specified 284 // a list of packages for global coverage. 285 if cover != nil && cover.Local { 286 ptest.Internal.CoverMode = cover.Mode 287 var coverFiles []string 288 coverFiles = append(coverFiles, ptest.GoFiles...) 289 coverFiles = append(coverFiles, ptest.CgoFiles...) 290 ptest.Internal.CoverVars = cover.DeclVars(ptest, coverFiles...) 291 } 292 293 for _, cp := range pmain.Internal.Imports { 294 if len(cp.Internal.CoverVars) > 0 { 295 t.Cover.Vars = append(t.Cover.Vars, coverInfo{cp, cp.Internal.CoverVars}) 296 } 297 } 298 299 data, err := formatTestmain(t) 300 if err != nil { 301 return nil, nil, nil, err 302 } 303 pmain.Internal.TestmainGo = &data 304 305 return pmain, ptest, pxtest, nil 306 } 307 308 func testImportStack(top string, p *Package, target string) []string { 309 stk := []string{top, p.ImportPath} 310 Search: 311 for p.ImportPath != target { 312 for _, p1 := range p.Internal.Imports { 313 if p1.ImportPath == target || str.Contains(p1.Deps, target) { 314 stk = append(stk, p1.ImportPath) 315 p = p1 316 continue Search 317 } 318 } 319 // Can't happen, but in case it does... 320 stk = append(stk, "<lost path to cycle>") 321 break 322 } 323 return stk 324 } 325 326 func recompileForTest(pmain, preal, ptest, pxtest *Package) { 327 // The "test copy" of preal is ptest. 328 // For each package that depends on preal, make a "test copy" 329 // that depends on ptest. And so on, up the dependency tree. 330 testCopy := map[*Package]*Package{preal: ptest} 331 for _, p := range PackageList([]*Package{pmain}) { 332 if p == preal { 333 continue 334 } 335 // Copy on write. 336 didSplit := p == pmain || p == pxtest 337 split := func() { 338 if didSplit { 339 return 340 } 341 didSplit = true 342 if testCopy[p] != nil { 343 panic("recompileForTest loop") 344 } 345 p1 := new(Package) 346 testCopy[p] = p1 347 *p1 = *p 348 p1.ForTest = preal.ImportPath 349 p1.Internal.Imports = make([]*Package, len(p.Internal.Imports)) 350 copy(p1.Internal.Imports, p.Internal.Imports) 351 p1.Imports = make([]string, len(p.Imports)) 352 copy(p1.Imports, p.Imports) 353 p = p1 354 p.Target = "" 355 } 356 357 // Update p.Internal.Imports to use test copies. 358 for i, imp := range p.Internal.Imports { 359 if p1 := testCopy[imp]; p1 != nil && p1 != imp { 360 split() 361 p.Internal.Imports[i] = p1 362 } 363 } 364 } 365 } 366 367 // isTestFunc tells whether fn has the type of a testing function. arg 368 // specifies the parameter type we look for: B, M or T. 369 func isTestFunc(fn *ast.FuncDecl, arg string) bool { 370 if fn.Type.Results != nil && len(fn.Type.Results.List) > 0 || 371 fn.Type.Params.List == nil || 372 len(fn.Type.Params.List) != 1 || 373 len(fn.Type.Params.List[0].Names) > 1 { 374 return false 375 } 376 ptr, ok := fn.Type.Params.List[0].Type.(*ast.StarExpr) 377 if !ok { 378 return false 379 } 380 // We can't easily check that the type is *testing.M 381 // because we don't know how testing has been imported, 382 // but at least check that it's *M or *something.M. 383 // Same applies for B and T. 384 if name, ok := ptr.X.(*ast.Ident); ok && name.Name == arg { 385 return true 386 } 387 if sel, ok := ptr.X.(*ast.SelectorExpr); ok && sel.Sel.Name == arg { 388 return true 389 } 390 return false 391 } 392 393 // isTest tells whether name looks like a test (or benchmark, according to prefix). 394 // It is a Test (say) if there is a character after Test that is not a lower-case letter. 395 // We don't want TesticularCancer. 396 func isTest(name, prefix string) bool { 397 if !strings.HasPrefix(name, prefix) { 398 return false 399 } 400 if len(name) == len(prefix) { // "Test" is ok 401 return true 402 } 403 rune, _ := utf8.DecodeRuneInString(name[len(prefix):]) 404 return !unicode.IsLower(rune) 405 } 406 407 type coverInfo struct { 408 Package *Package 409 Vars map[string]*CoverVar 410 } 411 412 // loadTestFuncs returns the testFuncs describing the tests that will be run. 413 func loadTestFuncs(ptest *Package) (*testFuncs, error) { 414 t := &testFuncs{ 415 Package: ptest, 416 } 417 for _, file := range ptest.TestGoFiles { 418 if err := t.load(filepath.Join(ptest.Dir, file), "_test", &t.ImportTest, &t.NeedTest); err != nil { 419 return nil, err 420 } 421 } 422 for _, file := range ptest.XTestGoFiles { 423 if err := t.load(filepath.Join(ptest.Dir, file), "_xtest", &t.ImportXtest, &t.NeedXtest); err != nil { 424 return nil, err 425 } 426 } 427 return t, nil 428 } 429 430 // formatTestmain returns the content of the _testmain.go file for t. 431 func formatTestmain(t *testFuncs) ([]byte, error) { 432 var buf bytes.Buffer 433 if err := testmainTmpl.Execute(&buf, t); err != nil { 434 return nil, err 435 } 436 return buf.Bytes(), nil 437 } 438 439 type testFuncs struct { 440 Tests []testFunc 441 Benchmarks []testFunc 442 Examples []testFunc 443 TestMain *testFunc 444 Package *Package 445 ImportTest bool 446 NeedTest bool 447 ImportXtest bool 448 NeedXtest bool 449 Cover *TestCover 450 } 451 452 // ImportPath returns the import path of the package being tested, if it is within GOPATH. 453 // This is printed by the testing package when running benchmarks. 454 func (t *testFuncs) ImportPath() string { 455 pkg := t.Package.ImportPath 456 if strings.HasPrefix(pkg, "_/") { 457 return "" 458 } 459 if pkg == "command-line-arguments" { 460 return "" 461 } 462 return pkg 463 } 464 465 // Covered returns a string describing which packages are being tested for coverage. 466 // If the covered package is the same as the tested package, it returns the empty string. 467 // Otherwise it is a comma-separated human-readable list of packages beginning with 468 // " in", ready for use in the coverage message. 469 func (t *testFuncs) Covered() string { 470 if t.Cover == nil || t.Cover.Paths == nil { 471 return "" 472 } 473 return " in " + strings.Join(t.Cover.Paths, ", ") 474 } 475 476 // Tested returns the name of the package being tested. 477 func (t *testFuncs) Tested() string { 478 return t.Package.Name 479 } 480 481 type testFunc struct { 482 Package string // imported package name (_test or _xtest) 483 Name string // function name 484 Output string // output, for examples 485 Unordered bool // output is allowed to be unordered. 486 } 487 488 var testFileSet = token.NewFileSet() 489 490 func (t *testFuncs) load(filename, pkg string, doImport, seen *bool) error { 491 f, err := parser.ParseFile(testFileSet, filename, nil, parser.ParseComments) 492 if err != nil { 493 return base.ExpandScanner(err) 494 } 495 for _, d := range f.Decls { 496 n, ok := d.(*ast.FuncDecl) 497 if !ok { 498 continue 499 } 500 if n.Recv != nil { 501 continue 502 } 503 name := n.Name.String() 504 switch { 505 case name == "TestMain": 506 if isTestFunc(n, "T") { 507 t.Tests = append(t.Tests, testFunc{pkg, name, "", false}) 508 *doImport, *seen = true, true 509 continue 510 } 511 err := checkTestFunc(n, "M") 512 if err != nil { 513 return err 514 } 515 if t.TestMain != nil { 516 return errors.New("multiple definitions of TestMain") 517 } 518 t.TestMain = &testFunc{pkg, name, "", false} 519 *doImport, *seen = true, true 520 case isTest(name, "Test"): 521 err := checkTestFunc(n, "T") 522 if err != nil { 523 return err 524 } 525 t.Tests = append(t.Tests, testFunc{pkg, name, "", false}) 526 *doImport, *seen = true, true 527 case isTest(name, "Benchmark"): 528 err := checkTestFunc(n, "B") 529 if err != nil { 530 return err 531 } 532 t.Benchmarks = append(t.Benchmarks, testFunc{pkg, name, "", false}) 533 *doImport, *seen = true, true 534 } 535 } 536 ex := doc.Examples(f) 537 sort.Slice(ex, func(i, j int) bool { return ex[i].Order < ex[j].Order }) 538 for _, e := range ex { 539 *doImport = true // import test file whether executed or not 540 if e.Output == "" && !e.EmptyOutput { 541 // Don't run examples with no output. 542 continue 543 } 544 t.Examples = append(t.Examples, testFunc{pkg, "Example" + e.Name, e.Output, e.Unordered}) 545 *seen = true 546 } 547 return nil 548 } 549 550 func checkTestFunc(fn *ast.FuncDecl, arg string) error { 551 if !isTestFunc(fn, arg) { 552 name := fn.Name.String() 553 pos := testFileSet.Position(fn.Pos()) 554 return fmt.Errorf("%s: wrong signature for %s, must be: func %s(%s *testing.%s)", pos, name, name, strings.ToLower(arg), arg) 555 } 556 return nil 557 } 558 559 var testmainTmpl = template.Must(template.New("main").Parse(` 560 package main 561 562 import ( 563 {{if not .TestMain}} 564 "os" 565 {{end}} 566 "testing" 567 "testing/internal/testdeps" 568 569 {{if .ImportTest}} 570 {{if .NeedTest}}_test{{else}}_{{end}} {{.Package.ImportPath | printf "%q"}} 571 {{end}} 572 {{if .ImportXtest}} 573 {{if .NeedXtest}}_xtest{{else}}_{{end}} {{.Package.ImportPath | printf "%s_test" | printf "%q"}} 574 {{end}} 575 {{if .Cover}} 576 {{range $i, $p := .Cover.Vars}} 577 _cover{{$i}} {{$p.Package.ImportPath | printf "%q"}} 578 {{end}} 579 {{end}} 580 ) 581 582 var tests = []testing.InternalTest{ 583 {{range .Tests}} 584 {"{{.Name}}", {{.Package}}.{{.Name}}}, 585 {{end}} 586 } 587 588 var benchmarks = []testing.InternalBenchmark{ 589 {{range .Benchmarks}} 590 {"{{.Name}}", {{.Package}}.{{.Name}}}, 591 {{end}} 592 } 593 594 var examples = []testing.InternalExample{ 595 {{range .Examples}} 596 {"{{.Name}}", {{.Package}}.{{.Name}}, {{.Output | printf "%q"}}, {{.Unordered}}}, 597 {{end}} 598 } 599 600 func init() { 601 testdeps.ImportPath = {{.ImportPath | printf "%q"}} 602 } 603 604 {{if .Cover}} 605 606 // Only updated by init functions, so no need for atomicity. 607 var ( 608 coverCounters = make(map[string][]uint32) 609 coverBlocks = make(map[string][]testing.CoverBlock) 610 ) 611 612 func init() { 613 {{range $i, $p := .Cover.Vars}} 614 {{range $file, $cover := $p.Vars}} 615 coverRegisterFile({{printf "%q" $cover.File}}, _cover{{$i}}.{{$cover.Var}}.Count[:], _cover{{$i}}.{{$cover.Var}}.Pos[:], _cover{{$i}}.{{$cover.Var}}.NumStmt[:]) 616 {{end}} 617 {{end}} 618 } 619 620 func coverRegisterFile(fileName string, counter []uint32, pos []uint32, numStmts []uint16) { 621 if 3*len(counter) != len(pos) || len(counter) != len(numStmts) { 622 panic("coverage: mismatched sizes") 623 } 624 if coverCounters[fileName] != nil { 625 // Already registered. 626 return 627 } 628 coverCounters[fileName] = counter 629 block := make([]testing.CoverBlock, len(counter)) 630 for i := range counter { 631 block[i] = testing.CoverBlock{ 632 Line0: pos[3*i+0], 633 Col0: uint16(pos[3*i+2]), 634 Line1: pos[3*i+1], 635 Col1: uint16(pos[3*i+2]>>16), 636 Stmts: numStmts[i], 637 } 638 } 639 coverBlocks[fileName] = block 640 } 641 {{end}} 642 643 func main() { 644 {{if .Cover}} 645 testing.RegisterCover(testing.Cover{ 646 Mode: {{printf "%q" .Cover.Mode}}, 647 Counters: coverCounters, 648 Blocks: coverBlocks, 649 CoveredPackages: {{printf "%q" .Covered}}, 650 }) 651 {{end}} 652 m := testing.MainStart(testdeps.TestDeps{}, tests, benchmarks, examples) 653 {{with .TestMain}} 654 {{.Package}}.{{.Name}}(m) 655 {{else}} 656 os.Exit(m.Run()) 657 {{end}} 658 } 659 660 `))