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