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

View as plain text