Source file src/cmd/go/internal/load/test.go

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

View as plain text