github.com/bir3/gocompiler@v0.9.2202/src/cmd/gocmd/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 "context" 10 "errors" 11 "fmt" 12 "github.com/bir3/gocompiler/src/go/ast" 13 "github.com/bir3/gocompiler/src/go/build" 14 "github.com/bir3/gocompiler/src/go/doc" 15 "github.com/bir3/gocompiler/src/go/parser" 16 "github.com/bir3/gocompiler/src/go/token" 17 "github.com/bir3/gocompiler/src/internal/lazytemplate" 18 "path/filepath" 19 "slices" 20 "sort" 21 "strings" 22 "unicode" 23 "unicode/utf8" 24 25 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/cfg" 26 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/fsys" 27 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/str" 28 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/trace" 29 ) 30 31 var TestMainDeps = []string{ 32 // Dependencies for testmain. 33 "os", 34 "reflect", 35 "testing", 36 "testing/internal/testdeps", 37 } 38 39 type TestCover struct { 40 Mode string 41 Local bool 42 Pkgs []*Package 43 Paths []string 44 Vars []coverInfo 45 } 46 47 // TestPackagesFor is like TestPackagesAndErrors but it returns 48 // an error if the test packages or their dependencies have errors. 49 // Only test packages without errors are returned. 50 func TestPackagesFor(ctx context.Context, opts PackageOpts, p *Package, cover *TestCover) (pmain, ptest, pxtest *Package, err error) { 51 pmain, ptest, pxtest = TestPackagesAndErrors(ctx, nil, opts, p, cover) 52 for _, p1 := range []*Package{ptest, pxtest, pmain} { 53 if p1 == nil { 54 // pxtest may be nil 55 continue 56 } 57 if p1.Error != nil { 58 err = p1.Error 59 break 60 } 61 if p1.Incomplete { 62 ps := PackageList([]*Package{p1}) 63 for _, p := range ps { 64 if p.Error != nil { 65 err = p.Error 66 break 67 } 68 } 69 break 70 } 71 } 72 if pmain.Error != nil || pmain.Incomplete { 73 pmain = nil 74 } 75 if ptest.Error != nil || ptest.Incomplete { 76 ptest = nil 77 } 78 if pxtest != nil && (pxtest.Error != nil || pxtest.Incomplete) { 79 pxtest = nil 80 } 81 return pmain, ptest, pxtest, err 82 } 83 84 // TestPackagesAndErrors returns three packages: 85 // - pmain, the package main corresponding to the test binary (running tests in ptest and pxtest). 86 // - ptest, the package p compiled with added "package p" test files. 87 // - pxtest, the result of compiling any "package p_test" (external) test files. 88 // 89 // If the package has no "package p_test" test files, pxtest will be nil. 90 // If the non-test compilation of package p can be reused 91 // (for example, if there are no "package p" test files and 92 // package p need not be instrumented for coverage or any other reason), 93 // then the returned ptest == p. 94 // 95 // If done is non-nil, TestPackagesAndErrors will finish filling out the returned 96 // package structs in a goroutine and call done once finished. The members of the 97 // returned packages should not be accessed until done is called. 98 // 99 // The caller is expected to have checked that len(p.TestGoFiles)+len(p.XTestGoFiles) > 0, 100 // or else there's no point in any of this. 101 func TestPackagesAndErrors(ctx context.Context, done func(), opts PackageOpts, p *Package, cover *TestCover) (pmain, ptest, pxtest *Package) { 102 ctx, span := trace.StartSpan(ctx, "load.TestPackagesAndErrors") 103 defer span.Done() 104 105 pre := newPreload() 106 defer pre.flush() 107 allImports := append([]string{}, p.TestImports...) 108 allImports = append(allImports, p.XTestImports...) 109 pre.preloadImports(ctx, opts, allImports, p.Internal.Build) 110 111 var ptestErr, pxtestErr *PackageError 112 var imports, ximports []*Package 113 var stk ImportStack 114 var testEmbed, xtestEmbed map[string][]string 115 var incomplete bool 116 stk.Push(p.ImportPath + " (test)") 117 rawTestImports := str.StringList(p.TestImports) 118 for i, path := range p.TestImports { 119 p1, err := loadImport(ctx, opts, pre, path, p.Dir, p, &stk, p.Internal.Build.TestImportPos[path], ResolveImport) 120 if err != nil && ptestErr == nil { 121 ptestErr = err 122 incomplete = true 123 } 124 if p1.Incomplete { 125 incomplete = true 126 } 127 p.TestImports[i] = p1.ImportPath 128 imports = append(imports, p1) 129 } 130 var err error 131 p.TestEmbedFiles, testEmbed, err = resolveEmbed(p.Dir, p.TestEmbedPatterns) 132 if err != nil { 133 ptestErr = &PackageError{ 134 ImportStack: stk.Copy(), 135 Err: err, 136 } 137 incomplete = true 138 embedErr := err.(*EmbedError) 139 ptestErr.setPos(p.Internal.Build.TestEmbedPatternPos[embedErr.Pattern]) 140 } 141 stk.Pop() 142 143 stk.Push(p.ImportPath + "_test") 144 pxtestNeedsPtest := false 145 var pxtestIncomplete bool 146 rawXTestImports := str.StringList(p.XTestImports) 147 for i, path := range p.XTestImports { 148 p1, err := loadImport(ctx, opts, pre, path, p.Dir, p, &stk, p.Internal.Build.XTestImportPos[path], ResolveImport) 149 if err != nil && pxtestErr == nil { 150 pxtestErr = err 151 } 152 if p1.Incomplete { 153 pxtestIncomplete = true 154 } 155 if p1.ImportPath == p.ImportPath { 156 pxtestNeedsPtest = true 157 } else { 158 ximports = append(ximports, p1) 159 } 160 p.XTestImports[i] = p1.ImportPath 161 } 162 p.XTestEmbedFiles, xtestEmbed, err = resolveEmbed(p.Dir, p.XTestEmbedPatterns) 163 if err != nil && pxtestErr == nil { 164 pxtestErr = &PackageError{ 165 ImportStack: stk.Copy(), 166 Err: err, 167 } 168 embedErr := err.(*EmbedError) 169 pxtestErr.setPos(p.Internal.Build.XTestEmbedPatternPos[embedErr.Pattern]) 170 } 171 pxtestIncomplete = pxtestIncomplete || pxtestErr != nil 172 stk.Pop() 173 174 // Test package. 175 if len(p.TestGoFiles) > 0 || p.Name == "main" || cover != nil && cover.Local { 176 ptest = new(Package) 177 *ptest = *p 178 ptest.Error = ptestErr 179 ptest.Incomplete = incomplete 180 ptest.ForTest = p.ImportPath 181 ptest.GoFiles = nil 182 ptest.GoFiles = append(ptest.GoFiles, p.GoFiles...) 183 ptest.GoFiles = append(ptest.GoFiles, p.TestGoFiles...) 184 ptest.Target = "" 185 // Note: The preparation of the vet config requires that common 186 // indexes in ptest.Imports and ptest.Internal.RawImports 187 // all line up (but RawImports can be shorter than the others). 188 // That is, for 0 ≤ i < len(RawImports), 189 // RawImports[i] is the import string in the program text, and 190 // Imports[i] is the expanded import string (vendoring applied or relative path expanded away). 191 // Any implicitly added imports appear in Imports and Internal.Imports 192 // but not RawImports (because they were not in the source code). 193 // We insert TestImports, imports, and rawTestImports at the start of 194 // these lists to preserve the alignment. 195 // Note that p.Internal.Imports may not be aligned with p.Imports/p.Internal.RawImports, 196 // but we insert at the beginning there too just for consistency. 197 ptest.Imports = str.StringList(p.TestImports, p.Imports) 198 ptest.Internal.Imports = append(imports, p.Internal.Imports...) 199 ptest.Internal.RawImports = str.StringList(rawTestImports, p.Internal.RawImports) 200 ptest.Internal.ForceLibrary = true 201 ptest.Internal.BuildInfo = nil 202 ptest.Internal.Build = new(build.Package) 203 *ptest.Internal.Build = *p.Internal.Build 204 m := map[string][]token.Position{} 205 for k, v := range p.Internal.Build.ImportPos { 206 m[k] = append(m[k], v...) 207 } 208 for k, v := range p.Internal.Build.TestImportPos { 209 m[k] = append(m[k], v...) 210 } 211 ptest.Internal.Build.ImportPos = m 212 if testEmbed == nil && len(p.Internal.Embed) > 0 { 213 testEmbed = map[string][]string{} 214 } 215 for k, v := range p.Internal.Embed { 216 testEmbed[k] = v 217 } 218 ptest.Internal.Embed = testEmbed 219 ptest.EmbedFiles = str.StringList(p.EmbedFiles, p.TestEmbedFiles) 220 ptest.Internal.OrigImportPath = p.Internal.OrigImportPath 221 ptest.Internal.PGOProfile = p.Internal.PGOProfile 222 ptest.Internal.Build.Directives = append(slices.Clip(p.Internal.Build.Directives), p.Internal.Build.TestDirectives...) 223 } else { 224 ptest = p 225 } 226 227 // External test package. 228 if len(p.XTestGoFiles) > 0 { 229 pxtest = &Package{ 230 PackagePublic: PackagePublic{ 231 Name: p.Name + "_test", 232 ImportPath: p.ImportPath + "_test", 233 Root: p.Root, 234 Dir: p.Dir, 235 Goroot: p.Goroot, 236 GoFiles: p.XTestGoFiles, 237 Imports: p.XTestImports, 238 ForTest: p.ImportPath, 239 Module: p.Module, 240 Error: pxtestErr, 241 Incomplete: pxtestIncomplete, 242 EmbedFiles: p.XTestEmbedFiles, 243 }, 244 Internal: PackageInternal{ 245 LocalPrefix: p.Internal.LocalPrefix, 246 Build: &build.Package{ 247 ImportPos: p.Internal.Build.XTestImportPos, 248 Directives: p.Internal.Build.XTestDirectives, 249 }, 250 Imports: ximports, 251 RawImports: rawXTestImports, 252 253 Asmflags: p.Internal.Asmflags, 254 Gcflags: p.Internal.Gcflags, 255 Ldflags: p.Internal.Ldflags, 256 Gccgoflags: p.Internal.Gccgoflags, 257 Embed: xtestEmbed, 258 OrigImportPath: p.Internal.OrigImportPath, 259 PGOProfile: p.Internal.PGOProfile, 260 }, 261 } 262 if pxtestNeedsPtest { 263 pxtest.Internal.Imports = append(pxtest.Internal.Imports, ptest) 264 } 265 } 266 267 // Arrange for testing.Testing to report true. 268 ldflags := append(p.Internal.Ldflags, "-X", "testing.testBinary=1") 269 gccgoflags := append(p.Internal.Gccgoflags, "-Wl,--defsym,testing.gccgoTestBinary=1") 270 271 // Build main package. 272 pmain = &Package{ 273 PackagePublic: PackagePublic{ 274 Name: "main", 275 Dir: p.Dir, 276 GoFiles: []string{"_testmain.go"}, 277 ImportPath: p.ImportPath + ".test", 278 Root: p.Root, 279 Imports: str.StringList(TestMainDeps), 280 Module: p.Module, 281 }, 282 Internal: PackageInternal{ 283 Build: &build.Package{Name: "main"}, 284 BuildInfo: p.Internal.BuildInfo, 285 Asmflags: p.Internal.Asmflags, 286 Gcflags: p.Internal.Gcflags, 287 Ldflags: ldflags, 288 Gccgoflags: gccgoflags, 289 OrigImportPath: p.Internal.OrigImportPath, 290 PGOProfile: p.Internal.PGOProfile, 291 }, 292 } 293 294 pb := p.Internal.Build 295 pmain.DefaultGODEBUG = defaultGODEBUG(pmain, pb.Directives, pb.TestDirectives, pb.XTestDirectives) 296 297 // The generated main also imports testing, regexp, and os. 298 // Also the linker introduces implicit dependencies reported by LinkerDeps. 299 stk.Push("testmain") 300 deps := TestMainDeps // cap==len, so safe for append 301 ldDeps, err := LinkerDeps(p) 302 if err != nil && pmain.Error == nil { 303 pmain.Error = &PackageError{Err: err} 304 } 305 for _, d := range ldDeps { 306 deps = append(deps, d) 307 } 308 for _, dep := range deps { 309 if dep == ptest.ImportPath { 310 pmain.Internal.Imports = append(pmain.Internal.Imports, ptest) 311 } else { 312 p1, err := loadImport(ctx, opts, pre, dep, "", nil, &stk, nil, 0) 313 if err != nil && pmain.Error == nil { 314 pmain.Error = err 315 pmain.Incomplete = true 316 } 317 pmain.Internal.Imports = append(pmain.Internal.Imports, p1) 318 } 319 } 320 stk.Pop() 321 322 parallelizablePart := func() { 323 if cover != nil && cover.Pkgs != nil && !cfg.Experiment.CoverageRedesign { 324 // Add imports, but avoid duplicates. 325 seen := map[*Package]bool{p: true, ptest: true} 326 for _, p1 := range pmain.Internal.Imports { 327 seen[p1] = true 328 } 329 for _, p1 := range cover.Pkgs { 330 if seen[p1] { 331 // Don't add duplicate imports. 332 continue 333 } 334 seen[p1] = true 335 pmain.Internal.Imports = append(pmain.Internal.Imports, p1) 336 } 337 } 338 339 allTestImports := make([]*Package, 0, len(pmain.Internal.Imports)+len(imports)+len(ximports)) 340 allTestImports = append(allTestImports, pmain.Internal.Imports...) 341 allTestImports = append(allTestImports, imports...) 342 allTestImports = append(allTestImports, ximports...) 343 setToolFlags(allTestImports...) 344 345 // Do initial scan for metadata needed for writing _testmain.go 346 // Use that metadata to update the list of imports for package main. 347 // The list of imports is used by recompileForTest and by the loop 348 // afterward that gathers t.Cover information. 349 t, err := loadTestFuncs(p) 350 if err != nil && pmain.Error == nil { 351 pmain.setLoadPackageDataError(err, p.ImportPath, &stk, nil) 352 } 353 t.Cover = cover 354 if len(ptest.GoFiles)+len(ptest.CgoFiles) > 0 { 355 pmain.Internal.Imports = append(pmain.Internal.Imports, ptest) 356 pmain.Imports = append(pmain.Imports, ptest.ImportPath) 357 t.ImportTest = true 358 } 359 if pxtest != nil { 360 pmain.Internal.Imports = append(pmain.Internal.Imports, pxtest) 361 pmain.Imports = append(pmain.Imports, pxtest.ImportPath) 362 t.ImportXtest = true 363 } 364 365 // Sort and dedup pmain.Imports. 366 // Only matters for go list -test output. 367 sort.Strings(pmain.Imports) 368 w := 0 369 for _, path := range pmain.Imports { 370 if w == 0 || path != pmain.Imports[w-1] { 371 pmain.Imports[w] = path 372 w++ 373 } 374 } 375 pmain.Imports = pmain.Imports[:w] 376 pmain.Internal.RawImports = str.StringList(pmain.Imports) 377 378 // Replace pmain's transitive dependencies with test copies, as necessary. 379 cycleErr := recompileForTest(pmain, p, ptest, pxtest) 380 if cycleErr != nil { 381 ptest.Error = cycleErr 382 ptest.Incomplete = true 383 } 384 385 if cover != nil { 386 if cfg.Experiment.CoverageRedesign { 387 // Here ptest needs to inherit the proper coverage mode (since 388 // it contains p's Go files), whereas pmain contains only 389 // test harness code (don't want to instrument it, and 390 // we don't want coverage hooks in the pkg init). 391 ptest.Internal.Cover.Mode = p.Internal.Cover.Mode 392 pmain.Internal.Cover.Mode = "testmain" 393 } 394 // Should we apply coverage analysis locally, only for this 395 // package and only for this test? Yes, if -cover is on but 396 // -coverpkg has not specified a list of packages for global 397 // coverage. 398 if cover.Local { 399 ptest.Internal.Cover.Mode = cover.Mode 400 401 if !cfg.Experiment.CoverageRedesign { 402 var coverFiles []string 403 coverFiles = append(coverFiles, ptest.GoFiles...) 404 coverFiles = append(coverFiles, ptest.CgoFiles...) 405 ptest.Internal.CoverVars = DeclareCoverVars(ptest, coverFiles...) 406 } 407 } 408 409 if !cfg.Experiment.CoverageRedesign { 410 for _, cp := range pmain.Internal.Imports { 411 if len(cp.Internal.CoverVars) > 0 { 412 t.Cover.Vars = append(t.Cover.Vars, coverInfo{cp, cp.Internal.CoverVars}) 413 } 414 } 415 } 416 } 417 418 data, err := formatTestmain(t) 419 if err != nil && pmain.Error == nil { 420 pmain.Error = &PackageError{Err: err} 421 pmain.Incomplete = true 422 } 423 // Set TestmainGo even if it is empty: the presence of a TestmainGo 424 // indicates that this package is, in fact, a test main. 425 pmain.Internal.TestmainGo = &data 426 } 427 428 if done != nil { 429 go func() { 430 parallelizablePart() 431 done() 432 }() 433 } else { 434 parallelizablePart() 435 } 436 437 return pmain, ptest, pxtest 438 } 439 440 // recompileForTest copies and replaces certain packages in pmain's dependency 441 // graph. This is necessary for two reasons. First, if ptest is different than 442 // preal, packages that import the package under test should get ptest instead 443 // of preal. This is particularly important if pxtest depends on functionality 444 // exposed in test sources in ptest. Second, if there is a main package 445 // (other than pmain) anywhere, we need to set p.Internal.ForceLibrary and 446 // clear p.Internal.BuildInfo in the test copy to prevent link conflicts. 447 // This may happen if both -coverpkg and the command line patterns include 448 // multiple main packages. 449 func recompileForTest(pmain, preal, ptest, pxtest *Package) *PackageError { 450 // The "test copy" of preal is ptest. 451 // For each package that depends on preal, make a "test copy" 452 // that depends on ptest. And so on, up the dependency tree. 453 testCopy := map[*Package]*Package{preal: ptest} 454 for _, p := range PackageList([]*Package{pmain}) { 455 if p == preal { 456 continue 457 } 458 // Copy on write. 459 didSplit := p == pmain || p == pxtest || p == ptest 460 split := func() { 461 if didSplit { 462 return 463 } 464 didSplit = true 465 if testCopy[p] != nil { 466 panic("recompileForTest loop") 467 } 468 p1 := new(Package) 469 testCopy[p] = p1 470 *p1 = *p 471 p1.ForTest = preal.ImportPath 472 p1.Internal.Imports = make([]*Package, len(p.Internal.Imports)) 473 copy(p1.Internal.Imports, p.Internal.Imports) 474 p1.Imports = make([]string, len(p.Imports)) 475 copy(p1.Imports, p.Imports) 476 p = p1 477 p.Target = "" 478 p.Internal.BuildInfo = nil 479 p.Internal.ForceLibrary = true 480 p.Internal.PGOProfile = preal.Internal.PGOProfile 481 } 482 483 // Update p.Internal.Imports to use test copies. 484 for i, imp := range p.Internal.Imports { 485 if p1 := testCopy[imp]; p1 != nil && p1 != imp { 486 split() 487 488 // If the test dependencies cause a cycle with pmain, this is 489 // where it is introduced. 490 // (There are no cycles in the graph until this assignment occurs.) 491 p.Internal.Imports[i] = p1 492 } 493 } 494 495 // Force main packages the test imports to be built as libraries. 496 // Normal imports of main packages are forbidden by the package loader, 497 // but this can still happen if -coverpkg patterns include main packages: 498 // covered packages are imported by pmain. Linking multiple packages 499 // compiled with '-p main' causes duplicate symbol errors. 500 // See golang.org/issue/30907, golang.org/issue/34114. 501 if p.Name == "main" && p != pmain && p != ptest { 502 split() 503 } 504 // Split and attach PGO information to test dependencies if preal 505 // is built with PGO. 506 if preal.Internal.PGOProfile != "" && p.Internal.PGOProfile == "" { 507 split() 508 } 509 } 510 511 // Do search to find cycle. 512 // importerOf maps each import path to its importer nearest to p. 513 importerOf := map[*Package]*Package{} 514 for _, p := range ptest.Internal.Imports { 515 importerOf[p] = nil 516 } 517 518 // q is a breadth-first queue of packages to search for target. 519 // Every package added to q has a corresponding entry in pathTo. 520 // 521 // We search breadth-first for two reasons: 522 // 523 // 1. We want to report the shortest cycle. 524 // 525 // 2. If p contains multiple cycles, the first cycle we encounter might not 526 // contain target. To ensure termination, we have to break all cycles 527 // other than the first. 528 q := slices.Clip(ptest.Internal.Imports) 529 for len(q) > 0 { 530 p := q[0] 531 q = q[1:] 532 if p == ptest { 533 // The stack is supposed to be in the order x imports y imports z. 534 // We collect in the reverse order: z is imported by y is imported 535 // by x, and then we reverse it. 536 var stk []string 537 for p != nil { 538 stk = append(stk, p.ImportPath) 539 p = importerOf[p] 540 } 541 // complete the cycle: we set importer[p] = nil to break the cycle 542 // in importerOf, it's an implicit importerOf[p] == pTest. Add it 543 // back here since we reached nil in the loop above to demonstrate 544 // the cycle as (for example) package p imports package q imports package r 545 // imports package p. 546 stk = append(stk, ptest.ImportPath) 547 slices.Reverse(stk) 548 549 return &PackageError{ 550 ImportStack: stk, 551 Err: errors.New("import cycle not allowed in test"), 552 IsImportCycle: true, 553 } 554 } 555 for _, dep := range p.Internal.Imports { 556 if _, ok := importerOf[dep]; !ok { 557 importerOf[dep] = p 558 q = append(q, dep) 559 } 560 } 561 } 562 563 return nil 564 } 565 566 // isTestFunc tells whether fn has the type of a testing function. arg 567 // specifies the parameter type we look for: B, M or T. 568 func isTestFunc(fn *ast.FuncDecl, arg string) bool { 569 if fn.Type.Results != nil && len(fn.Type.Results.List) > 0 || 570 fn.Type.Params.List == nil || 571 len(fn.Type.Params.List) != 1 || 572 len(fn.Type.Params.List[0].Names) > 1 { 573 return false 574 } 575 ptr, ok := fn.Type.Params.List[0].Type.(*ast.StarExpr) 576 if !ok { 577 return false 578 } 579 // We can't easily check that the type is *testing.M 580 // because we don't know how testing has been imported, 581 // but at least check that it's *M or *something.M. 582 // Same applies for B and T. 583 if name, ok := ptr.X.(*ast.Ident); ok && name.Name == arg { 584 return true 585 } 586 if sel, ok := ptr.X.(*ast.SelectorExpr); ok && sel.Sel.Name == arg { 587 return true 588 } 589 return false 590 } 591 592 // isTest tells whether name looks like a test (or benchmark, according to prefix). 593 // It is a Test (say) if there is a character after Test that is not a lower-case letter. 594 // We don't want TesticularCancer. 595 func isTest(name, prefix string) bool { 596 if !strings.HasPrefix(name, prefix) { 597 return false 598 } 599 if len(name) == len(prefix) { // "Test" is ok 600 return true 601 } 602 rune, _ := utf8.DecodeRuneInString(name[len(prefix):]) 603 return !unicode.IsLower(rune) 604 } 605 606 type coverInfo struct { 607 Package *Package 608 Vars map[string]*CoverVar 609 } 610 611 // loadTestFuncs returns the testFuncs describing the tests that will be run. 612 // The returned testFuncs is always non-nil, even if an error occurred while 613 // processing test files. 614 func loadTestFuncs(ptest *Package) (*testFuncs, error) { 615 t := &testFuncs{ 616 Package: ptest, 617 } 618 var err error 619 for _, file := range ptest.TestGoFiles { 620 if lerr := t.load(filepath.Join(ptest.Dir, file), "_test", &t.ImportTest, &t.NeedTest); lerr != nil && err == nil { 621 err = lerr 622 } 623 } 624 for _, file := range ptest.XTestGoFiles { 625 if lerr := t.load(filepath.Join(ptest.Dir, file), "_xtest", &t.ImportXtest, &t.NeedXtest); lerr != nil && err == nil { 626 err = lerr 627 } 628 } 629 return t, err 630 } 631 632 // formatTestmain returns the content of the _testmain.go file for t. 633 func formatTestmain(t *testFuncs) ([]byte, error) { 634 var buf bytes.Buffer 635 tmpl := testmainTmpl 636 if cfg.Experiment.CoverageRedesign { 637 tmpl = testmainTmplNewCoverage 638 } 639 if err := tmpl.Execute(&buf, t); err != nil { 640 return nil, err 641 } 642 return buf.Bytes(), nil 643 } 644 645 type testFuncs struct { 646 Tests []testFunc 647 Benchmarks []testFunc 648 FuzzTargets []testFunc 649 Examples []testFunc 650 TestMain *testFunc 651 Package *Package 652 ImportTest bool 653 NeedTest bool 654 ImportXtest bool 655 NeedXtest bool 656 Cover *TestCover 657 } 658 659 // ImportPath returns the import path of the package being tested, if it is within GOPATH. 660 // This is printed by the testing package when running benchmarks. 661 func (t *testFuncs) ImportPath() string { 662 pkg := t.Package.ImportPath 663 if strings.HasPrefix(pkg, "_/") { 664 return "" 665 } 666 if pkg == "command-line-arguments" { 667 return "" 668 } 669 return pkg 670 } 671 672 // Covered returns a string describing which packages are being tested for coverage. 673 // If the covered package is the same as the tested package, it returns the empty string. 674 // Otherwise it is a comma-separated human-readable list of packages beginning with 675 // " in", ready for use in the coverage message. 676 func (t *testFuncs) Covered() string { 677 if t.Cover == nil || t.Cover.Paths == nil { 678 return "" 679 } 680 return " in " + strings.Join(t.Cover.Paths, ", ") 681 } 682 683 // Tested returns the name of the package being tested. 684 func (t *testFuncs) Tested() string { 685 return t.Package.Name 686 } 687 688 type testFunc struct { 689 Package string // imported package name (_test or _xtest) 690 Name string // function name 691 Output string // output, for examples 692 Unordered bool // output is allowed to be unordered. 693 } 694 695 var testFileSet = token.NewFileSet() 696 697 func (t *testFuncs) load(filename, pkg string, doImport, seen *bool) error { 698 // Pass in the overlaid source if we have an overlay for this file. 699 src, err := fsys.Open(filename) 700 if err != nil { 701 return err 702 } 703 defer src.Close() 704 f, err := parser.ParseFile(testFileSet, filename, src, parser.ParseComments|parser.SkipObjectResolution) 705 if err != nil { 706 return err 707 } 708 for _, d := range f.Decls { 709 n, ok := d.(*ast.FuncDecl) 710 if !ok { 711 continue 712 } 713 if n.Recv != nil { 714 continue 715 } 716 name := n.Name.String() 717 switch { 718 case name == "TestMain": 719 if isTestFunc(n, "T") { 720 t.Tests = append(t.Tests, testFunc{pkg, name, "", false}) 721 *doImport, *seen = true, true 722 continue 723 } 724 err := checkTestFunc(n, "M") 725 if err != nil { 726 return err 727 } 728 if t.TestMain != nil { 729 return errors.New("multiple definitions of TestMain") 730 } 731 t.TestMain = &testFunc{pkg, name, "", false} 732 *doImport, *seen = true, true 733 case isTest(name, "Test"): 734 err := checkTestFunc(n, "T") 735 if err != nil { 736 return err 737 } 738 t.Tests = append(t.Tests, testFunc{pkg, name, "", false}) 739 *doImport, *seen = true, true 740 case isTest(name, "Benchmark"): 741 err := checkTestFunc(n, "B") 742 if err != nil { 743 return err 744 } 745 t.Benchmarks = append(t.Benchmarks, testFunc{pkg, name, "", false}) 746 *doImport, *seen = true, true 747 case isTest(name, "Fuzz"): 748 err := checkTestFunc(n, "F") 749 if err != nil { 750 return err 751 } 752 t.FuzzTargets = append(t.FuzzTargets, testFunc{pkg, name, "", false}) 753 *doImport, *seen = true, true 754 } 755 } 756 ex := doc.Examples(f) 757 sort.Slice(ex, func(i, j int) bool { return ex[i].Order < ex[j].Order }) 758 for _, e := range ex { 759 *doImport = true // import test file whether executed or not 760 if e.Output == "" && !e.EmptyOutput { 761 // Don't run examples with no output. 762 continue 763 } 764 t.Examples = append(t.Examples, testFunc{pkg, "Example" + e.Name, e.Output, e.Unordered}) 765 *seen = true 766 } 767 return nil 768 } 769 770 func checkTestFunc(fn *ast.FuncDecl, arg string) error { 771 var why string 772 if !isTestFunc(fn, arg) { 773 why = fmt.Sprintf("must be: func %s(%s *testing.%s)", fn.Name.String(), strings.ToLower(arg), arg) 774 } 775 if fn.Type.TypeParams.NumFields() > 0 { 776 why = "test functions cannot have type parameters" 777 } 778 if why != "" { 779 pos := testFileSet.Position(fn.Pos()) 780 return fmt.Errorf("%s: wrong signature for %s, %s", pos, fn.Name.String(), why) 781 } 782 return nil 783 } 784 785 var testmainTmpl = lazytemplate.New("main", ` 786 // Code generated by 'go test'. DO NOT EDIT. 787 788 package main 789 790 import ( 791 "os" 792 {{if .TestMain}} 793 "reflect" 794 {{end}} 795 "testing" 796 "testing/internal/testdeps" 797 798 {{if .ImportTest}} 799 {{if .NeedTest}}_test{{else}}_{{end}} {{.Package.ImportPath | printf "%q"}} 800 {{end}} 801 {{if .ImportXtest}} 802 {{if .NeedXtest}}_xtest{{else}}_{{end}} {{.Package.ImportPath | printf "%s_test" | printf "%q"}} 803 {{end}} 804 {{if .Cover}} 805 {{range $i, $p := .Cover.Vars}} 806 _cover{{$i}} {{$p.Package.ImportPath | printf "%q"}} 807 {{end}} 808 {{end}} 809 ) 810 811 var tests = []testing.InternalTest{ 812 {{range .Tests}} 813 {"{{.Name}}", {{.Package}}.{{.Name}}}, 814 {{end}} 815 } 816 817 var benchmarks = []testing.InternalBenchmark{ 818 {{range .Benchmarks}} 819 {"{{.Name}}", {{.Package}}.{{.Name}}}, 820 {{end}} 821 } 822 823 var fuzzTargets = []testing.InternalFuzzTarget{ 824 {{range .FuzzTargets}} 825 {"{{.Name}}", {{.Package}}.{{.Name}}}, 826 {{end}} 827 } 828 829 var examples = []testing.InternalExample{ 830 {{range .Examples}} 831 {"{{.Name}}", {{.Package}}.{{.Name}}, {{.Output | printf "%q"}}, {{.Unordered}}}, 832 {{end}} 833 } 834 835 func init() { 836 testdeps.ImportPath = {{.ImportPath | printf "%q"}} 837 } 838 839 {{if .Cover}} 840 841 // Only updated by init functions, so no need for atomicity. 842 var ( 843 coverCounters = make(map[string][]uint32) 844 coverBlocks = make(map[string][]testing.CoverBlock) 845 ) 846 847 func init() { 848 {{range $i, $p := .Cover.Vars}} 849 {{range $file, $cover := $p.Vars}} 850 coverRegisterFile({{printf "%q" $cover.File}}, _cover{{$i}}.{{$cover.Var}}.Count[:], _cover{{$i}}.{{$cover.Var}}.Pos[:], _cover{{$i}}.{{$cover.Var}}.NumStmt[:]) 851 {{end}} 852 {{end}} 853 } 854 855 func coverRegisterFile(fileName string, counter []uint32, pos []uint32, numStmts []uint16) { 856 if 3*len(counter) != len(pos) || len(counter) != len(numStmts) { 857 panic("coverage: mismatched sizes") 858 } 859 if coverCounters[fileName] != nil { 860 // Already registered. 861 return 862 } 863 coverCounters[fileName] = counter 864 block := make([]testing.CoverBlock, len(counter)) 865 for i := range counter { 866 block[i] = testing.CoverBlock{ 867 Line0: pos[3*i+0], 868 Col0: uint16(pos[3*i+2]), 869 Line1: pos[3*i+1], 870 Col1: uint16(pos[3*i+2]>>16), 871 Stmts: numStmts[i], 872 } 873 } 874 coverBlocks[fileName] = block 875 } 876 {{end}} 877 878 func main() { 879 {{if .Cover}} 880 testing.RegisterCover(testing.Cover{ 881 Mode: {{printf "%q" .Cover.Mode}}, 882 Counters: coverCounters, 883 Blocks: coverBlocks, 884 CoveredPackages: {{printf "%q" .Covered}}, 885 }) 886 {{end}} 887 m := testing.MainStart(testdeps.TestDeps{}, tests, benchmarks, fuzzTargets, examples) 888 {{with .TestMain}} 889 {{.Package}}.{{.Name}}(m) 890 os.Exit(int(reflect.ValueOf(m).Elem().FieldByName("exitCode").Int())) 891 {{else}} 892 os.Exit(m.Run()) 893 {{end}} 894 } 895 896 `) 897 898 var testmainTmplNewCoverage = lazytemplate.New("main", ` 899 // Code generated by 'go test'. DO NOT EDIT. 900 901 package main 902 903 import ( 904 "os" 905 {{if .Cover}} 906 _ "unsafe" 907 {{end}} 908 {{if .TestMain}} 909 "reflect" 910 {{end}} 911 "testing" 912 "testing/internal/testdeps" 913 914 {{if .ImportTest}} 915 {{if .NeedTest}}_test{{else}}_{{end}} {{.Package.ImportPath | printf "%q"}} 916 {{end}} 917 {{if .ImportXtest}} 918 {{if .NeedXtest}}_xtest{{else}}_{{end}} {{.Package.ImportPath | printf "%s_test" | printf "%q"}} 919 {{end}} 920 ) 921 922 var tests = []testing.InternalTest{ 923 {{range .Tests}} 924 {"{{.Name}}", {{.Package}}.{{.Name}}}, 925 {{end}} 926 } 927 928 var benchmarks = []testing.InternalBenchmark{ 929 {{range .Benchmarks}} 930 {"{{.Name}}", {{.Package}}.{{.Name}}}, 931 {{end}} 932 } 933 934 var fuzzTargets = []testing.InternalFuzzTarget{ 935 {{range .FuzzTargets}} 936 {"{{.Name}}", {{.Package}}.{{.Name}}}, 937 {{end}} 938 } 939 940 var examples = []testing.InternalExample{ 941 {{range .Examples}} 942 {"{{.Name}}", {{.Package}}.{{.Name}}, {{.Output | printf "%q"}}, {{.Unordered}}}, 943 {{end}} 944 } 945 946 func init() { 947 testdeps.ImportPath = {{.ImportPath | printf "%q"}} 948 } 949 950 {{if .Cover}} 951 952 //go:linkname runtime_coverage_processCoverTestDir runtime/coverage.processCoverTestDir 953 func runtime_coverage_processCoverTestDir(dir string, cfile string, cmode string, cpkgs string) error 954 955 //go:linkname testing_registerCover2 testing.registerCover2 956 func testing_registerCover2(mode string, tearDown func(coverprofile string, gocoverdir string) (string, error), snapcov func() float64) 957 958 //go:linkname runtime_coverage_markProfileEmitted runtime/coverage.markProfileEmitted 959 func runtime_coverage_markProfileEmitted(val bool) 960 961 //go:linkname runtime_coverage_snapshot runtime/coverage.snapshot 962 func runtime_coverage_snapshot() float64 963 964 func coverTearDown(coverprofile string, gocoverdir string) (string, error) { 965 var err error 966 if gocoverdir == "" { 967 gocoverdir, err = os.MkdirTemp("", "gocoverdir") 968 if err != nil { 969 return "error setting GOCOVERDIR: bad os.MkdirTemp return", err 970 } 971 defer os.RemoveAll(gocoverdir) 972 } 973 runtime_coverage_markProfileEmitted(true) 974 cmode := {{printf "%q" .Cover.Mode}} 975 if err := runtime_coverage_processCoverTestDir(gocoverdir, coverprofile, cmode, {{printf "%q" .Covered}}); err != nil { 976 return "error generating coverage report", err 977 } 978 return "", nil 979 } 980 {{end}} 981 982 func main() { 983 {{if .Cover}} 984 testing_registerCover2({{printf "%q" .Cover.Mode}}, coverTearDown, runtime_coverage_snapshot) 985 {{end}} 986 m := testing.MainStart(testdeps.TestDeps{}, tests, benchmarks, fuzzTargets, examples) 987 {{with .TestMain}} 988 {{.Package}}.{{.Name}}(m) 989 os.Exit(int(reflect.ValueOf(m).Elem().FieldByName("exitCode").Int())) 990 {{else}} 991 os.Exit(m.Run()) 992 {{end}} 993 } 994 995 `)