// a non-nil Error field; other information may or may not be missing
// (zeroed).
//
+// The -test flag causes list to add to its output test binaries for the
+// named packages that have tests, to make information about test
+// binary construction available to source code analysis tools.
+// The reported import path for a test binary is the import path of
+// the package followed by a ".test" suffix, as in "math/rand.test".
+// When building a test, it is sometimes necessary to rebuild certain
+// dependencies specially for that test (most commonly the tested
+// package itself). The reported import path of a package recompiled
+// for a particular test binary is followed by a space and the name of
+// the test binary in brackets, as in "math/rand [math/rand.test]"
+// or "regexp [sort.test]".
+//
// For more about build flags, see 'go help build'.
//
// For more about specifying packages, see 'go help packages'.
}
}
+func TestGoListTest(t *testing.T) {
+ tg := testgo(t)
+ defer tg.cleanup()
+ tg.parallel()
+ tg.makeTempdir()
+ tg.setenv("GOCACHE", tg.tempdir)
+
+ tg.run("list", "-test", "-deps", "sort")
+ tg.grepStdout(`^sort.test$`, "missing test main")
+ tg.grepStdout(`^sort$`, "missing real sort")
+ tg.grepStdout(`^sort \[sort.test\]$`, "missing test copy of sort")
+ tg.grepStdout(`^testing \[sort.test\]$`, "missing test copy of testing")
+ tg.grepStdoutNot(`^testing$`, "unexpected real copy of testing")
+
+ tg.run("list", "-test", "sort")
+ tg.grepStdout(`^sort.test$`, "missing test main")
+ tg.grepStdout(`^sort$`, "missing real sort")
+ tg.grepStdoutNot(`^sort \[sort.test\]$`, "unexpected test copy of sort")
+ tg.grepStdoutNot(`^testing \[sort.test\]$`, "unexpected test copy of testing")
+ tg.grepStdoutNot(`^testing$`, "unexpected real copy of testing")
+
+ tg.run("list", "-test", "cmd/dist", "cmd/doc")
+ tg.grepStdout(`^cmd/dist$`, "missing cmd/dist")
+ tg.grepStdout(`^cmd/doc$`, "missing cmd/doc")
+ tg.grepStdout(`^cmd/doc\.test$`, "missing cmd/doc test")
+ tg.grepStdoutNot(`^cmd/dist\.test$`, "unexpected cmd/dist test")
+ tg.grepStdoutNot(`^testing`, "unexpected testing")
+}
+
// Issue 4096. Validate the output of unsuccessful go install foo/quxx.
func TestUnsuccessfulGoInstallShouldMentionMissingPackage(t *testing.T) {
tg := testgo(t)
import (
"bufio"
+ "bytes"
"encoding/json"
"io"
"os"
+ "sort"
"strings"
"text/template"
"cmd/go/internal/base"
+ "cmd/go/internal/cache"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"cmd/go/internal/work"
a non-nil Error field; other information may or may not be missing
(zeroed).
+The -test flag causes list to report not only the named packages
+but also their test binaries (for packages with tests), to convey to
+source code analysis tools exactly how test binaries are constructed.
+The reported import path for a test binary is the import path of
+the package followed by a ".test" suffix, as in "math/rand.test".
+When building a test, it is sometimes necessary to rebuild certain
+dependencies specially for that test (most commonly the tested
+package itself). The reported import path of a package recompiled
+for a particular test binary is followed by a space and the name of
+the test binary in brackets, as in "math/rand [math/rand.test]"
+or "regexp [sort.test]".
+
For more about build flags, see 'go help build'.
For more about specifying packages, see 'go help packages'.
var listE = CmdList.Flag.Bool("e", false, "")
var listFmt = CmdList.Flag.String("f", "{{.ImportPath}}", "")
var listJson = CmdList.Flag.Bool("json", false, "")
+var listTest = CmdList.Flag.Bool("test", false, "")
var nl = []byte{'\n'}
func runList(cmd *base.Command, args []string) {
pkgs = load.Packages(args)
}
+ if *listTest {
+ c := cache.Default()
+ if c == nil {
+ base.Fatalf("go list -test requires build cache")
+ }
+ // Add test binaries to packages to be listed.
+ for _, p := range pkgs {
+ if p.Error != nil {
+ continue
+ }
+ if len(p.TestGoFiles)+len(p.XTestGoFiles) > 0 {
+ pmain, _, _, err := load.TestPackagesFor(p, nil)
+ if err != nil {
+ if !*listE {
+ base.Errorf("can't load test package: %s", err)
+ continue
+ }
+ pmain = &load.Package{
+ PackagePublic: load.PackagePublic{
+ ImportPath: p.ImportPath + ".test",
+ Error: &load.PackageError{Err: err.Error()},
+ },
+ }
+ }
+ pkgs = append(pkgs, pmain)
+
+ data := *pmain.Internal.TestmainGo
+ h := cache.NewHash("testmain")
+ h.Write([]byte("testmain\n"))
+ h.Write(data)
+ out, _, err := c.Put(h.Sum(), bytes.NewReader(data))
+ if err != nil {
+ base.Fatalf("%s", err)
+ }
+ pmain.GoFiles[0] = c.OutputFile(out)
+ }
+ }
+ }
+
+ // Remember which packages are named on the command line.
+ cmdline := make(map[*load.Package]bool)
+ for _, p := range pkgs {
+ cmdline[p] = true
+ }
+
if *listDeps {
// Note: This changes the order of the listed packages
// from "as written on the command line" to
// "a depth-first post-order traversal".
// (The dependency exploration order for a given node
// is alphabetical, same as listed in .Deps.)
+ // Note that -deps is applied after -test,
+ // so that you only get descriptions of tests for the things named
+ // explicitly on the command line, not for all dependencies.
pkgs = load.PackageList(pkgs)
}
b.Do(a)
}
- for _, pkg := range pkgs {
+ for _, p := range pkgs {
// Show vendor-expanded paths in listing
- pkg.TestImports = pkg.Vendored(pkg.TestImports)
- pkg.XTestImports = pkg.Vendored(pkg.XTestImports)
+ p.TestImports = p.Vendored(p.TestImports)
+ p.XTestImports = p.Vendored(p.XTestImports)
+ }
+
+ if *listTest {
+ all := pkgs
+ if !*listDeps {
+ all = load.PackageList(pkgs)
+ }
+ // Update import paths to distinguish the real package p
+ // from p recompiled for q.test.
+ // This must happen only once the build code is done
+ // looking at import paths, because it will get very confused
+ // if it sees these.
+ for _, p := range all {
+ if p.ForTest != "" {
+ p.ImportPath += " [" + p.ForTest + ".test]"
+ }
+ p.DepOnly = !cmdline[p]
+ }
+ // Update import path lists to use new strings.
+ for _, p := range all {
+ for i := range p.Imports {
+ p.Imports[i] = p.Internal.Imports[i].ImportPath
+ }
+ }
+ // Recompute deps lists using new strings, from the leaves up.
+ for _, p := range all {
+ deps := make(map[string]bool)
+ for _, p1 := range p.Internal.Imports {
+ deps[p1.ImportPath] = true
+ for _, d := range p1.Deps {
+ deps[d] = true
+ }
+ }
+ p.Deps = make([]string, 0, len(deps))
+ for d := range deps {
+ p.Deps = append(p.Deps, d)
+ }
+ sort.Strings(p.Deps)
+ }
+ }
- do(&pkg.PackagePublic)
+ for _, p := range pkgs {
+ do(&p.PackagePublic)
}
}
Root string `json:",omitempty"` // Go root or Go path dir containing this package
ConflictDir string `json:",omitempty"` // Dir is hidden by this other directory
BinaryOnly bool `json:",omitempty"` // package cannot be recompiled
+ ForTest string `json:",omitempty"` // package is only for use in named test
+ DepOnly bool `json:",omitempty"` // package is only as a dependency, not explicitly listed
// Stale and StaleReason remain here *only* for the list command.
// They are only initialized in preparation for list execution.
CoverVars map[string]*CoverVar // variables created by coverage analysis
OmitDebug bool // tell linker not to write debug information
GobinSubdir bool // install target would be subdir of GOBIN
+ TestmainGo *[]byte // content for _testmain.go
Asmflags []string // -asmflags for this package
Gcflags []string // -gcflags for this package
package load
import (
+ "bytes"
+ "cmd/go/internal/base"
"cmd/go/internal/str"
+ "errors"
+ "fmt"
+ "go/ast"
"go/build"
+ "go/doc"
+ "go/parser"
"go/token"
+ "path/filepath"
+ "sort"
+ "strings"
+ "text/template"
+ "unicode"
+ "unicode/utf8"
)
-// TestPackagesFor returns package structs ptest, the package p plus
-// its test files, and pxtest, the external tests of package p.
-// pxtest may be nil. If there are no test files, forceTest decides
-// whether this returns a new package struct or just returns p.
-func TestPackagesFor(p *Package, forceTest bool) (ptest, pxtest *Package, err error) {
+var TestMainDeps = []string{
+ // Dependencies for testmain.
+ "os",
+ "testing",
+ "testing/internal/testdeps",
+}
+
+type TestCover struct {
+ Mode string
+ Local bool
+ Pkgs []*Package
+ Paths []string
+ Vars []coverInfo
+ DeclVars func(string, ...string) map[string]*CoverVar
+}
+
+// TestPackagesFor returns three packages:
+// - ptest, the package p compiled with added "package p" test files.
+// - pxtest, the result of compiling any "package p_test" (external) test files.
+// - pmain, the package main corresponding to the test binary (running tests in ptest and pxtest).
+//
+// If the package has no "package p_test" test files, pxtest will be nil.
+// If the non-test compilation of package p can be reused
+// (for example, if there are no "package p" test files and
+// package p need not be instrumented for coverage or any other reason),
+// then the returned ptest == p.
+//
+// The caller is expected to have checked that len(p.TestGoFiles)+len(p.XTestGoFiles) > 0,
+// or else there's no point in any of this.
+func TestPackagesFor(p *Package, cover *TestCover) (pmain, ptest, pxtest *Package, err error) {
var imports, ximports []*Package
var stk ImportStack
stk.Push(p.ImportPath + " (test)")
for i, path := range p.TestImports {
p1 := LoadImport(path, p.Dir, p, &stk, p.Internal.Build.TestImportPos[path], UseVendor)
if p1.Error != nil {
- return nil, nil, p1.Error
+ return nil, nil, nil, p1.Error
}
if len(p1.DepsErrors) > 0 {
err := p1.DepsErrors[0]
err.Pos = "" // show full import stack
- return nil, nil, err
+ return nil, nil, nil, err
}
if str.Contains(p1.Deps, p.ImportPath) || p1.ImportPath == p.ImportPath {
// Same error that loadPackage returns (via reusePackage) in pkg.go.
Err: "import cycle not allowed in test",
IsImportCycle: true,
}
- return nil, nil, err
+ return nil, nil, nil, err
}
p.TestImports[i] = p1.ImportPath
imports = append(imports, p1)
for i, path := range p.XTestImports {
p1 := LoadImport(path, p.Dir, p, &stk, p.Internal.Build.XTestImportPos[path], UseVendor)
if p1.Error != nil {
- return nil, nil, p1.Error
+ return nil, nil, nil, p1.Error
}
if len(p1.DepsErrors) > 0 {
err := p1.DepsErrors[0]
err.Pos = "" // show full import stack
- return nil, nil, err
+ return nil, nil, nil, err
}
if p1.ImportPath == p.ImportPath {
pxtestNeedsPtest = true
stk.Pop()
// Test package.
- if len(p.TestGoFiles) > 0 || forceTest {
+ if len(p.TestGoFiles) > 0 || p.Name == "main" || cover != nil && cover.Local {
ptest = new(Package)
*ptest = *p
+ ptest.ForTest = p.ImportPath
ptest.GoFiles = nil
ptest.GoFiles = append(ptest.GoFiles, p.GoFiles...)
ptest.GoFiles = append(ptest.GoFiles, p.TestGoFiles...)
Dir: p.Dir,
GoFiles: p.XTestGoFiles,
Imports: p.XTestImports,
+ ForTest: p.ImportPath,
},
Internal: PackageInternal{
LocalPrefix: p.Internal.LocalPrefix,
}
}
- if p != ptest && pxtest != nil {
+ // Build main package.
+ pmain = &Package{
+ PackagePublic: PackagePublic{
+ Name: "main",
+ Dir: p.Dir,
+ GoFiles: []string{"_testmain.go"},
+ ImportPath: p.ImportPath + ".test",
+ Root: p.Root,
+ },
+ Internal: PackageInternal{
+ Build: &build.Package{Name: "main"},
+ Asmflags: p.Internal.Asmflags,
+ Gcflags: p.Internal.Gcflags,
+ Ldflags: p.Internal.Ldflags,
+ Gccgoflags: p.Internal.Gccgoflags,
+ },
+ }
+
+ // The generated main also imports testing, regexp, and os.
+ // Also the linker introduces implicit dependencies reported by LinkerDeps.
+ stk.Push("testmain")
+ deps := TestMainDeps // cap==len, so safe for append
+ for _, d := range LinkerDeps(p) {
+ deps = append(deps, d)
+ }
+ for _, dep := range deps {
+ if dep == ptest.ImportPath {
+ pmain.Internal.Imports = append(pmain.Internal.Imports, ptest)
+ } else {
+ p1 := LoadImport(dep, "", nil, &stk, nil, 0)
+ if p1.Error != nil {
+ return nil, nil, nil, p1.Error
+ }
+ pmain.Internal.Imports = append(pmain.Internal.Imports, p1)
+ }
+ }
+ stk.Pop()
+
+ if cover != nil && cover.Pkgs != nil {
+ // Add imports, but avoid duplicates.
+ seen := map[*Package]bool{p: true, ptest: true}
+ for _, p1 := range pmain.Internal.Imports {
+ seen[p1] = true
+ }
+ for _, p1 := range cover.Pkgs {
+ if !seen[p1] {
+ seen[p1] = true
+ pmain.Internal.Imports = append(pmain.Internal.Imports, p1)
+ }
+ }
+ }
+
+ // Do initial scan for metadata needed for writing _testmain.go
+ // Use that metadata to update the list of imports for package main.
+ // The list of imports is used by recompileForTest and by the loop
+ // afterward that gathers t.Cover information.
+ t, err := loadTestFuncs(ptest)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+ t.Cover = cover
+ if len(ptest.GoFiles)+len(ptest.CgoFiles) > 0 {
+ pmain.Internal.Imports = append(pmain.Internal.Imports, ptest)
+ t.ImportTest = true
+ }
+ if pxtest != nil {
+ pmain.Internal.Imports = append(pmain.Internal.Imports, pxtest)
+ t.ImportXtest = true
+ }
+
+ if ptest != p {
// We have made modifications to the package p being tested
// and are rebuilding p (as ptest).
// Arrange to rebuild all packages q such that
- // pxtest depends on q and q depends on p.
+ // the test depends on q and q depends on p.
// This makes sure that q sees the modifications to p.
// Strictly speaking, the rebuild is only necessary if the
// modifications to p change its export metadata, but
// determining that is a bit tricky, so we rebuild always.
- recompileForTest(p, ptest, pxtest)
+ recompileForTest(pmain, p, ptest, pxtest)
+ }
+
+ // Should we apply coverage analysis locally,
+ // only for this package and only for this test?
+ // Yes, if -cover is on but -coverpkg has not specified
+ // a list of packages for global coverage.
+ if cover != nil && cover.Local {
+ ptest.Internal.CoverMode = cover.Mode
+ var coverFiles []string
+ coverFiles = append(coverFiles, ptest.GoFiles...)
+ coverFiles = append(coverFiles, ptest.CgoFiles...)
+ ptest.Internal.CoverVars = cover.DeclVars(ptest.ImportPath, coverFiles...)
+ }
+
+ for _, cp := range pmain.Internal.Imports {
+ if len(cp.Internal.CoverVars) > 0 {
+ t.Cover.Vars = append(t.Cover.Vars, coverInfo{cp, cp.Internal.CoverVars})
+ }
+ }
+
+ data, err := formatTestmain(t)
+ if err != nil {
+ return nil, nil, nil, err
}
+ pmain.Internal.TestmainGo = &data
- return ptest, pxtest, nil
+ return pmain, ptest, pxtest, nil
}
func testImportStack(top string, p *Package, target string) []string {
return stk
}
-func recompileForTest(preal, ptest, pxtest *Package) {
+func recompileForTest(pmain, preal, ptest, pxtest *Package) {
// The "test copy" of preal is ptest.
// For each package that depends on preal, make a "test copy"
// that depends on ptest. And so on, up the dependency tree.
testCopy := map[*Package]*Package{preal: ptest}
- // Only pxtest and its dependencies can legally depend on p.
- // If ptest or its dependencies depended on p, the dependency
- // would be circular.
- for _, p := range PackageList([]*Package{pxtest}) {
+ for _, p := range PackageList([]*Package{pmain}) {
if p == preal {
continue
}
// Copy on write.
- didSplit := p == pxtest
+ didSplit := p == pmain || p == pxtest
split := func() {
if didSplit {
return
p1 := new(Package)
testCopy[p] = p1
*p1 = *p
+ p1.ForTest = preal.ImportPath
p1.Internal.Imports = make([]*Package, len(p.Internal.Imports))
copy(p1.Internal.Imports, p.Internal.Imports)
p = p1
}
}
}
+
+// isTestFunc tells whether fn has the type of a testing function. arg
+// specifies the parameter type we look for: B, M or T.
+func isTestFunc(fn *ast.FuncDecl, arg string) bool {
+ if fn.Type.Results != nil && len(fn.Type.Results.List) > 0 ||
+ fn.Type.Params.List == nil ||
+ len(fn.Type.Params.List) != 1 ||
+ len(fn.Type.Params.List[0].Names) > 1 {
+ return false
+ }
+ ptr, ok := fn.Type.Params.List[0].Type.(*ast.StarExpr)
+ if !ok {
+ return false
+ }
+ // We can't easily check that the type is *testing.M
+ // because we don't know how testing has been imported,
+ // but at least check that it's *M or *something.M.
+ // Same applies for B and T.
+ if name, ok := ptr.X.(*ast.Ident); ok && name.Name == arg {
+ return true
+ }
+ if sel, ok := ptr.X.(*ast.SelectorExpr); ok && sel.Sel.Name == arg {
+ return true
+ }
+ return false
+}
+
+// isTest tells whether name looks like a test (or benchmark, according to prefix).
+// It is a Test (say) if there is a character after Test that is not a lower-case letter.
+// We don't want TesticularCancer.
+func isTest(name, prefix string) bool {
+ if !strings.HasPrefix(name, prefix) {
+ return false
+ }
+ if len(name) == len(prefix) { // "Test" is ok
+ return true
+ }
+ rune, _ := utf8.DecodeRuneInString(name[len(prefix):])
+ return !unicode.IsLower(rune)
+}
+
+type coverInfo struct {
+ Package *Package
+ Vars map[string]*CoverVar
+}
+
+// loadTestFuncs returns the testFuncs describing the tests that will be run.
+func loadTestFuncs(ptest *Package) (*testFuncs, error) {
+ t := &testFuncs{
+ Package: ptest,
+ }
+ for _, file := range ptest.TestGoFiles {
+ if err := t.load(filepath.Join(ptest.Dir, file), "_test", &t.ImportTest, &t.NeedTest); err != nil {
+ return nil, err
+ }
+ }
+ for _, file := range ptest.XTestGoFiles {
+ if err := t.load(filepath.Join(ptest.Dir, file), "_xtest", &t.ImportXtest, &t.NeedXtest); err != nil {
+ return nil, err
+ }
+ }
+ return t, nil
+}
+
+// formatTestmain returns the content of the _testmain.go file for t.
+func formatTestmain(t *testFuncs) ([]byte, error) {
+ var buf bytes.Buffer
+ if err := testmainTmpl.Execute(&buf, t); err != nil {
+ return nil, err
+ }
+ return buf.Bytes(), nil
+}
+
+type testFuncs struct {
+ Tests []testFunc
+ Benchmarks []testFunc
+ Examples []testFunc
+ TestMain *testFunc
+ Package *Package
+ ImportTest bool
+ NeedTest bool
+ ImportXtest bool
+ NeedXtest bool
+ Cover *TestCover
+}
+
+// ImportPath returns the import path of the package being tested, if it is within GOPATH.
+// This is printed by the testing package when running benchmarks.
+func (t *testFuncs) ImportPath() string {
+ pkg := t.Package.ImportPath
+ if strings.HasPrefix(pkg, "_/") {
+ return ""
+ }
+ if pkg == "command-line-arguments" {
+ return ""
+ }
+ return pkg
+}
+
+// Covered returns a string describing which packages are being tested for coverage.
+// If the covered package is the same as the tested package, it returns the empty string.
+// Otherwise it is a comma-separated human-readable list of packages beginning with
+// " in", ready for use in the coverage message.
+func (t *testFuncs) Covered() string {
+ if t.Cover == nil || t.Cover.Paths == nil {
+ return ""
+ }
+ return " in " + strings.Join(t.Cover.Paths, ", ")
+}
+
+// Tested returns the name of the package being tested.
+func (t *testFuncs) Tested() string {
+ return t.Package.Name
+}
+
+type testFunc struct {
+ Package string // imported package name (_test or _xtest)
+ Name string // function name
+ Output string // output, for examples
+ Unordered bool // output is allowed to be unordered.
+}
+
+var testFileSet = token.NewFileSet()
+
+func (t *testFuncs) load(filename, pkg string, doImport, seen *bool) error {
+ f, err := parser.ParseFile(testFileSet, filename, nil, parser.ParseComments)
+ if err != nil {
+ return base.ExpandScanner(err)
+ }
+ for _, d := range f.Decls {
+ n, ok := d.(*ast.FuncDecl)
+ if !ok {
+ continue
+ }
+ if n.Recv != nil {
+ continue
+ }
+ name := n.Name.String()
+ switch {
+ case name == "TestMain":
+ if isTestFunc(n, "T") {
+ t.Tests = append(t.Tests, testFunc{pkg, name, "", false})
+ *doImport, *seen = true, true
+ continue
+ }
+ err := checkTestFunc(n, "M")
+ if err != nil {
+ return err
+ }
+ if t.TestMain != nil {
+ return errors.New("multiple definitions of TestMain")
+ }
+ t.TestMain = &testFunc{pkg, name, "", false}
+ *doImport, *seen = true, true
+ case isTest(name, "Test"):
+ err := checkTestFunc(n, "T")
+ if err != nil {
+ return err
+ }
+ t.Tests = append(t.Tests, testFunc{pkg, name, "", false})
+ *doImport, *seen = true, true
+ case isTest(name, "Benchmark"):
+ err := checkTestFunc(n, "B")
+ if err != nil {
+ return err
+ }
+ t.Benchmarks = append(t.Benchmarks, testFunc{pkg, name, "", false})
+ *doImport, *seen = true, true
+ }
+ }
+ ex := doc.Examples(f)
+ sort.Slice(ex, func(i, j int) bool { return ex[i].Order < ex[j].Order })
+ for _, e := range ex {
+ *doImport = true // import test file whether executed or not
+ if e.Output == "" && !e.EmptyOutput {
+ // Don't run examples with no output.
+ continue
+ }
+ t.Examples = append(t.Examples, testFunc{pkg, "Example" + e.Name, e.Output, e.Unordered})
+ *seen = true
+ }
+ return nil
+}
+
+func checkTestFunc(fn *ast.FuncDecl, arg string) error {
+ if !isTestFunc(fn, arg) {
+ name := fn.Name.String()
+ pos := testFileSet.Position(fn.Pos())
+ return fmt.Errorf("%s: wrong signature for %s, must be: func %s(%s *testing.%s)", pos, name, name, strings.ToLower(arg), arg)
+ }
+ return nil
+}
+
+var testmainTmpl = template.Must(template.New("main").Parse(`
+package main
+
+import (
+{{if not .TestMain}}
+ "os"
+{{end}}
+ "testing"
+ "testing/internal/testdeps"
+
+{{if .ImportTest}}
+ {{if .NeedTest}}_test{{else}}_{{end}} {{.Package.ImportPath | printf "%q"}}
+{{end}}
+{{if .ImportXtest}}
+ {{if .NeedXtest}}_xtest{{else}}_{{end}} {{.Package.ImportPath | printf "%s_test" | printf "%q"}}
+{{end}}
+{{if .Cover}}
+{{range $i, $p := .Cover.Vars}}
+ _cover{{$i}} {{$p.Package.ImportPath | printf "%q"}}
+{{end}}
+{{end}}
+)
+
+var tests = []testing.InternalTest{
+{{range .Tests}}
+ {"{{.Name}}", {{.Package}}.{{.Name}}},
+{{end}}
+}
+
+var benchmarks = []testing.InternalBenchmark{
+{{range .Benchmarks}}
+ {"{{.Name}}", {{.Package}}.{{.Name}}},
+{{end}}
+}
+
+var examples = []testing.InternalExample{
+{{range .Examples}}
+ {"{{.Name}}", {{.Package}}.{{.Name}}, {{.Output | printf "%q"}}, {{.Unordered}}},
+{{end}}
+}
+
+func init() {
+ testdeps.ImportPath = {{.ImportPath | printf "%q"}}
+}
+
+{{if .Cover}}
+
+// Only updated by init functions, so no need for atomicity.
+var (
+ coverCounters = make(map[string][]uint32)
+ coverBlocks = make(map[string][]testing.CoverBlock)
+)
+
+func init() {
+ {{range $i, $p := .Cover.Vars}}
+ {{range $file, $cover := $p.Vars}}
+ coverRegisterFile({{printf "%q" $cover.File}}, _cover{{$i}}.{{$cover.Var}}.Count[:], _cover{{$i}}.{{$cover.Var}}.Pos[:], _cover{{$i}}.{{$cover.Var}}.NumStmt[:])
+ {{end}}
+ {{end}}
+}
+
+func coverRegisterFile(fileName string, counter []uint32, pos []uint32, numStmts []uint16) {
+ if 3*len(counter) != len(pos) || len(counter) != len(numStmts) {
+ panic("coverage: mismatched sizes")
+ }
+ if coverCounters[fileName] != nil {
+ // Already registered.
+ return
+ }
+ coverCounters[fileName] = counter
+ block := make([]testing.CoverBlock, len(counter))
+ for i := range counter {
+ block[i] = testing.CoverBlock{
+ Line0: pos[3*i+0],
+ Col0: uint16(pos[3*i+2]),
+ Line1: pos[3*i+1],
+ Col1: uint16(pos[3*i+2]>>16),
+ Stmts: numStmts[i],
+ }
+ }
+ coverBlocks[fileName] = block
+}
+{{end}}
+
+func main() {
+{{if .Cover}}
+ testing.RegisterCover(testing.Cover{
+ Mode: {{printf "%q" .Cover.Mode}},
+ Counters: coverCounters,
+ Blocks: coverBlocks,
+ CoveredPackages: {{printf "%q" .Covered}},
+ })
+{{end}}
+ m := testing.MainStart(testdeps.TestDeps{}, tests, benchmarks, examples)
+{{with .TestMain}}
+ {{.Package}}.{{.Name}}(m)
+{{else}}
+ os.Exit(m.Run())
+{{end}}
+}
+
+`))
"crypto/sha256"
"errors"
"fmt"
- "go/ast"
"go/build"
- "go/doc"
- "go/parser"
- "go/token"
"io"
"io/ioutil"
"os"
"strconv"
"strings"
"sync"
- "text/template"
"time"
- "unicode"
- "unicode/utf8"
"cmd/go/internal/base"
"cmd/go/internal/cache"
testCacheExpire time.Time // ignore cached test results before this time
)
-var testMainDeps = []string{
- // Dependencies for testmain.
- "os",
- "testing",
- "testing/internal/testdeps",
-}
-
// testVetFlags is the list of flags to pass to vet when invoked automatically during go test.
var testVetFlags = []string{
// TODO(rsc): Decide which tests are enabled by default.
cfg.BuildV = testV
deps := make(map[string]bool)
- for _, dep := range testMainDeps {
+ for _, dep := range load.TestMainDeps {
deps[dep] = true
}
}
// Build Package structs describing:
+ // pmain - pkg.test binary
// ptest - package + test files
// pxtest - package of external test files
- // pmain - pkg.test binary
- var ptest, pxtest, pmain *load.Package
-
- localCover := testCover && testCoverPaths == nil
-
- ptest, pxtest, err = load.TestPackagesFor(p, localCover || p.Name == "main")
+ var cover *load.TestCover
+ if testCover {
+ cover = &load.TestCover{
+ Mode: testCoverMode,
+ Local: testCover && testCoverPaths == nil,
+ Pkgs: testCoverPkgs,
+ Paths: testCoverPaths,
+ DeclVars: declareCoverVars,
+ }
+ }
+ pmain, ptest, pxtest, err := load.TestPackagesFor(p, cover)
if err != nil {
return nil, nil, nil, err
}
}
testBinary := elem + ".test"
- // Should we apply coverage analysis locally,
- // only for this package and only for this test?
- // Yes, if -cover is on but -coverpkg has not specified
- // a list of packages for global coverage.
- if localCover {
- ptest.Internal.CoverMode = testCoverMode
- var coverFiles []string
- coverFiles = append(coverFiles, ptest.GoFiles...)
- coverFiles = append(coverFiles, ptest.CgoFiles...)
- ptest.Internal.CoverVars = declareCoverVars(ptest.ImportPath, coverFiles...)
- }
-
testDir := b.NewObjdir()
if err := b.Mkdir(testDir); err != nil {
return nil, nil, nil, err
}
- // Action for building pkg.test.
- pmain = &load.Package{
- PackagePublic: load.PackagePublic{
- Name: "main",
- Dir: testDir,
- GoFiles: []string{"_testmain.go"},
- ImportPath: p.ImportPath + " (testmain)",
- Root: p.Root,
- },
- Internal: load.PackageInternal{
- Build: &build.Package{Name: "main"},
- OmitDebug: !testC && !testNeedBinary,
-
- Asmflags: p.Internal.Asmflags,
- Gcflags: p.Internal.Gcflags,
- Ldflags: p.Internal.Ldflags,
- Gccgoflags: p.Internal.Gccgoflags,
- },
- }
-
- // The generated main also imports testing, regexp, and os.
- // Also the linker introduces implicit dependencies reported by LinkerDeps.
- var stk load.ImportStack
- stk.Push("testmain")
- deps := testMainDeps // cap==len, so safe for append
- for _, d := range load.LinkerDeps(p) {
- deps = append(deps, d)
- }
- for _, dep := range deps {
- if dep == ptest.ImportPath {
- pmain.Internal.Imports = append(pmain.Internal.Imports, ptest)
- } else {
- p1 := load.LoadImport(dep, "", nil, &stk, nil, 0)
- if p1.Error != nil {
- return nil, nil, nil, p1.Error
- }
- pmain.Internal.Imports = append(pmain.Internal.Imports, p1)
- }
- }
-
- if testCoverPkgs != nil {
- // Add imports, but avoid duplicates.
- seen := map[*load.Package]bool{p: true, ptest: true}
- for _, p1 := range pmain.Internal.Imports {
- seen[p1] = true
- }
- for _, p1 := range testCoverPkgs {
- if !seen[p1] {
- seen[p1] = true
- pmain.Internal.Imports = append(pmain.Internal.Imports, p1)
- }
- }
- }
-
- // Do initial scan for metadata needed for writing _testmain.go
- // Use that metadata to update the list of imports for package main.
- // The list of imports is used by recompileForTest and by the loop
- // afterward that gathers t.Cover information.
- t, err := loadTestFuncs(ptest)
- if err != nil {
- return nil, nil, nil, err
- }
- if len(ptest.GoFiles)+len(ptest.CgoFiles) > 0 {
- pmain.Internal.Imports = append(pmain.Internal.Imports, ptest)
- t.ImportTest = true
- }
- if pxtest != nil {
- pmain.Internal.Imports = append(pmain.Internal.Imports, pxtest)
- t.ImportXtest = true
- }
-
- for _, cp := range pmain.Internal.Imports {
- if len(cp.Internal.CoverVars) > 0 {
- t.Cover = append(t.Cover, coverInfo{cp, cp.Internal.CoverVars})
- }
- }
+ pmain.Dir = testDir
+ pmain.Internal.OmitDebug = !testC && !testNeedBinary
if !cfg.BuildN {
// writeTestmain writes _testmain.go,
// using the test description gathered in t.
- if err := writeTestmain(testDir+"_testmain.go", t); err != nil {
+ if err := ioutil.WriteFile(testDir+"_testmain.go", *pmain.Internal.TestmainGo, 0666); err != nil {
return nil, nil, nil, err
}
}
fmt.Fprintf(stdout, "? \t%s\t[no test files]\n", a.Package.ImportPath)
return nil
}
-
-// isTestFunc tells whether fn has the type of a testing function. arg
-// specifies the parameter type we look for: B, M or T.
-func isTestFunc(fn *ast.FuncDecl, arg string) bool {
- if fn.Type.Results != nil && len(fn.Type.Results.List) > 0 ||
- fn.Type.Params.List == nil ||
- len(fn.Type.Params.List) != 1 ||
- len(fn.Type.Params.List[0].Names) > 1 {
- return false
- }
- ptr, ok := fn.Type.Params.List[0].Type.(*ast.StarExpr)
- if !ok {
- return false
- }
- // We can't easily check that the type is *testing.M
- // because we don't know how testing has been imported,
- // but at least check that it's *M or *something.M.
- // Same applies for B and T.
- if name, ok := ptr.X.(*ast.Ident); ok && name.Name == arg {
- return true
- }
- if sel, ok := ptr.X.(*ast.SelectorExpr); ok && sel.Sel.Name == arg {
- return true
- }
- return false
-}
-
-// isTest tells whether name looks like a test (or benchmark, according to prefix).
-// It is a Test (say) if there is a character after Test that is not a lower-case letter.
-// We don't want TesticularCancer.
-func isTest(name, prefix string) bool {
- if !strings.HasPrefix(name, prefix) {
- return false
- }
- if len(name) == len(prefix) { // "Test" is ok
- return true
- }
- rune, _ := utf8.DecodeRuneInString(name[len(prefix):])
- return !unicode.IsLower(rune)
-}
-
-type coverInfo struct {
- Package *load.Package
- Vars map[string]*load.CoverVar
-}
-
-// loadTestFuncs returns the testFuncs describing the tests that will be run.
-func loadTestFuncs(ptest *load.Package) (*testFuncs, error) {
- t := &testFuncs{
- Package: ptest,
- }
- for _, file := range ptest.TestGoFiles {
- if err := t.load(filepath.Join(ptest.Dir, file), "_test", &t.ImportTest, &t.NeedTest); err != nil {
- return nil, err
- }
- }
- for _, file := range ptest.XTestGoFiles {
- if err := t.load(filepath.Join(ptest.Dir, file), "_xtest", &t.ImportXtest, &t.NeedXtest); err != nil {
- return nil, err
- }
- }
- return t, nil
-}
-
-// writeTestmain writes the _testmain.go file for t to the file named out.
-func writeTestmain(out string, t *testFuncs) error {
- f, err := os.Create(out)
- if err != nil {
- return err
- }
- defer f.Close()
-
- return testmainTmpl.Execute(f, t)
-}
-
-type testFuncs struct {
- Tests []testFunc
- Benchmarks []testFunc
- Examples []testFunc
- TestMain *testFunc
- Package *load.Package
- ImportTest bool
- NeedTest bool
- ImportXtest bool
- NeedXtest bool
- Cover []coverInfo
-}
-
-func (t *testFuncs) CoverMode() string {
- return testCoverMode
-}
-
-func (t *testFuncs) CoverEnabled() bool {
- return testCover
-}
-
-// ImportPath returns the import path of the package being tested, if it is within GOPATH.
-// This is printed by the testing package when running benchmarks.
-func (t *testFuncs) ImportPath() string {
- pkg := t.Package.ImportPath
- if strings.HasPrefix(pkg, "_/") {
- return ""
- }
- if pkg == "command-line-arguments" {
- return ""
- }
- return pkg
-}
-
-// Covered returns a string describing which packages are being tested for coverage.
-// If the covered package is the same as the tested package, it returns the empty string.
-// Otherwise it is a comma-separated human-readable list of packages beginning with
-// " in", ready for use in the coverage message.
-func (t *testFuncs) Covered() string {
- if testCoverPaths == nil {
- return ""
- }
- return " in " + strings.Join(testCoverPaths, ", ")
-}
-
-// Tested returns the name of the package being tested.
-func (t *testFuncs) Tested() string {
- return t.Package.Name
-}
-
-type testFunc struct {
- Package string // imported package name (_test or _xtest)
- Name string // function name
- Output string // output, for examples
- Unordered bool // output is allowed to be unordered.
-}
-
-var testFileSet = token.NewFileSet()
-
-func (t *testFuncs) load(filename, pkg string, doImport, seen *bool) error {
- f, err := parser.ParseFile(testFileSet, filename, nil, parser.ParseComments)
- if err != nil {
- return base.ExpandScanner(err)
- }
- for _, d := range f.Decls {
- n, ok := d.(*ast.FuncDecl)
- if !ok {
- continue
- }
- if n.Recv != nil {
- continue
- }
- name := n.Name.String()
- switch {
- case name == "TestMain":
- if isTestFunc(n, "T") {
- t.Tests = append(t.Tests, testFunc{pkg, name, "", false})
- *doImport, *seen = true, true
- continue
- }
- err := checkTestFunc(n, "M")
- if err != nil {
- return err
- }
- if t.TestMain != nil {
- return errors.New("multiple definitions of TestMain")
- }
- t.TestMain = &testFunc{pkg, name, "", false}
- *doImport, *seen = true, true
- case isTest(name, "Test"):
- err := checkTestFunc(n, "T")
- if err != nil {
- return err
- }
- t.Tests = append(t.Tests, testFunc{pkg, name, "", false})
- *doImport, *seen = true, true
- case isTest(name, "Benchmark"):
- err := checkTestFunc(n, "B")
- if err != nil {
- return err
- }
- t.Benchmarks = append(t.Benchmarks, testFunc{pkg, name, "", false})
- *doImport, *seen = true, true
- }
- }
- ex := doc.Examples(f)
- sort.Slice(ex, func(i, j int) bool { return ex[i].Order < ex[j].Order })
- for _, e := range ex {
- *doImport = true // import test file whether executed or not
- if e.Output == "" && !e.EmptyOutput {
- // Don't run examples with no output.
- continue
- }
- t.Examples = append(t.Examples, testFunc{pkg, "Example" + e.Name, e.Output, e.Unordered})
- *seen = true
- }
- return nil
-}
-
-func checkTestFunc(fn *ast.FuncDecl, arg string) error {
- if !isTestFunc(fn, arg) {
- name := fn.Name.String()
- pos := testFileSet.Position(fn.Pos())
- return fmt.Errorf("%s: wrong signature for %s, must be: func %s(%s *testing.%s)", pos, name, name, strings.ToLower(arg), arg)
- }
- return nil
-}
-
-var testmainTmpl = template.Must(template.New("main").Parse(`
-package main
-
-import (
-{{if not .TestMain}}
- "os"
-{{end}}
- "testing"
- "testing/internal/testdeps"
-
-{{if .ImportTest}}
- {{if .NeedTest}}_test{{else}}_{{end}} {{.Package.ImportPath | printf "%q"}}
-{{end}}
-{{if .ImportXtest}}
- {{if .NeedXtest}}_xtest{{else}}_{{end}} {{.Package.ImportPath | printf "%s_test" | printf "%q"}}
-{{end}}
-{{range $i, $p := .Cover}}
- _cover{{$i}} {{$p.Package.ImportPath | printf "%q"}}
-{{end}}
-)
-
-var tests = []testing.InternalTest{
-{{range .Tests}}
- {"{{.Name}}", {{.Package}}.{{.Name}}},
-{{end}}
-}
-
-var benchmarks = []testing.InternalBenchmark{
-{{range .Benchmarks}}
- {"{{.Name}}", {{.Package}}.{{.Name}}},
-{{end}}
-}
-
-var examples = []testing.InternalExample{
-{{range .Examples}}
- {"{{.Name}}", {{.Package}}.{{.Name}}, {{.Output | printf "%q"}}, {{.Unordered}}},
-{{end}}
-}
-
-func init() {
- testdeps.ImportPath = {{.ImportPath | printf "%q"}}
-}
-
-{{if .CoverEnabled}}
-
-// Only updated by init functions, so no need for atomicity.
-var (
- coverCounters = make(map[string][]uint32)
- coverBlocks = make(map[string][]testing.CoverBlock)
-)
-
-func init() {
- {{range $i, $p := .Cover}}
- {{range $file, $cover := $p.Vars}}
- coverRegisterFile({{printf "%q" $cover.File}}, _cover{{$i}}.{{$cover.Var}}.Count[:], _cover{{$i}}.{{$cover.Var}}.Pos[:], _cover{{$i}}.{{$cover.Var}}.NumStmt[:])
- {{end}}
- {{end}}
-}
-
-func coverRegisterFile(fileName string, counter []uint32, pos []uint32, numStmts []uint16) {
- if 3*len(counter) != len(pos) || len(counter) != len(numStmts) {
- panic("coverage: mismatched sizes")
- }
- if coverCounters[fileName] != nil {
- // Already registered.
- return
- }
- coverCounters[fileName] = counter
- block := make([]testing.CoverBlock, len(counter))
- for i := range counter {
- block[i] = testing.CoverBlock{
- Line0: pos[3*i+0],
- Col0: uint16(pos[3*i+2]),
- Line1: pos[3*i+1],
- Col1: uint16(pos[3*i+2]>>16),
- Stmts: numStmts[i],
- }
- }
- coverBlocks[fileName] = block
-}
-{{end}}
-
-func main() {
-{{if .CoverEnabled}}
- testing.RegisterCover(testing.Cover{
- Mode: {{printf "%q" .CoverMode}},
- Counters: coverCounters,
- Blocks: coverBlocks,
- CoveredPackages: {{printf "%q" .Covered}},
- })
-{{end}}
- m := testing.MainStart(testdeps.TestDeps{}, tests, benchmarks, examples)
-{{with .TestMain}}
- {{.Package}}.{{.Name}}(m)
-{{else}}
- os.Exit(m.Run())
-{{end}}
-}
-
-`))
root := &work.Action{Mode: "go vet"}
for _, p := range pkgs {
- ptest, pxtest, err := load.TestPackagesFor(p, false)
+ _, ptest, pxtest, err := load.TestPackagesFor(p, nil)
if err != nil {
base.Errorf("%v", err)
continue