diff --git a/src/cmd/go/internal/modload/search.go b/src/cmd/go/internal/modload/search.go index a08c01479f6d83..536f44037b7533 100644 --- a/src/cmd/go/internal/modload/search.go +++ b/src/cmd/go/internal/modload/search.go @@ -102,7 +102,7 @@ func matchPackages(ld *Loader, ctx context.Context, m *search.Match, tags map[st } else { // Avoid .foo, _foo, and testdata subdirectory trees. _, elem = filepath.Split(pkgDir) - if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" { + if IsIgnoredDirectory(elem) { want = false } else if ignorePatternsMap[cleanRoot] != nil && ignorePatternsMap[cleanRoot].ShouldIgnore(relPkgDir) { if cfg.BuildX { @@ -231,7 +231,7 @@ func walkFromIndex(index *modindex.Module, importPathRoot string, isMatch, treeC p := reldir for { elem, rest, found := strings.Cut(p, string(filepath.Separator)) - if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" { + if IsIgnoredDirectory(elem) { return } if found && elem == "vendor" { @@ -358,3 +358,9 @@ func parseIgnorePatterns(ld *Loader, ctx context.Context, treeCanMatch func(stri } return ignorePatternsMap } + +// IsIgnoredDirectory checks whether the path element should be ignored. +// That is, if it starts with '.' or '_' or is called 'testdata'. +func IsIgnoredDirectory(pathElem string) bool { + return strings.HasPrefix(pathElem, ".") || strings.HasPrefix(pathElem, "_") || pathElem == "testdata" +} diff --git a/src/cmd/go/internal/test/test.go b/src/cmd/go/internal/test/test.go index 2084bc68a91217..53cdbc26bdfaab 100644 --- a/src/cmd/go/internal/test/test.go +++ b/src/cmd/go/internal/test/test.go @@ -878,7 +878,10 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) { if cfg.BuildCoverPkg != nil { match := make([]func(*modload.Loader, *load.Package) bool, len(cfg.BuildCoverPkg)) for i := range cfg.BuildCoverPkg { - match[i] = load.MatchPackage(cfg.BuildCoverPkg[i], base.Cwd()) + match[i] = func(l *modload.Loader, p *load.Package) bool { + cwd := base.Cwd() + return load.MatchPackage(cfg.BuildCoverPkg[i], cwd)(l, p) && !isUnderSpecial(cwd, p.Dir) + } } // Select for coverage all dependencies matching the -coverpkg @@ -2311,3 +2314,19 @@ func testBinaryName(p *load.Package) string { return elem + ".test" } + +// isUnderSpecial checks whether dir is contained within a 'special' directory under 'cwd'. +// A directory is special if it beings with "." or "_" , or is called "testdata" +func isUnderSpecial(cwd string, dir string) bool { + rel, err := filepath.Rel(cwd, dir) + if err != nil { + return false + } + + sep := string(filepath.Separator) + pathComponents := strings.Split(rel, sep) + shouldIgnoreElemt := func(elem string) bool { + return modload.IsIgnoredDirectory(elem) && elem != "." && elem != ".." + } + return slices.IndexFunc(pathComponents, shouldIgnoreElemt) != -1 +} diff --git a/src/cmd/go/testdata/script/coverpkg_ignores_special_dirs.txt b/src/cmd/go/testdata/script/coverpkg_ignores_special_dirs.txt new file mode 100644 index 00000000000000..664cfbdfb4ca4a --- /dev/null +++ b/src/cmd/go/testdata/script/coverpkg_ignores_special_dirs.txt @@ -0,0 +1,59 @@ +# test for https://github.com/golang/go/issues/66038 +[short] skip + +env GO111MODULE=off +# files +env GOPATH=$WORK/.dir${:}$WORK/_dir${:}$WORK/testdata + +cd $WORK +go test -coverpkg=./... ./... +stdout 'coverage: 100.0%' +go test -coverpkg=. ./... +stdout 'coverage: 100.0%' + +-- $WORK/a.go -- +package a + +// trivial function with 100% test coverage + +import ( + _ "dot_dir" + _ "under_dir" + _ "testdata_dir" +) + +func F(i int) int { + return i*i +} + +-- $WORK/a_test.go -- +package a + +import ( + "testing" +) + +func TestF(t *testing.T) { + F(2) +} + +-- $WORK/.dir/src/dot_dir/b.go -- +package dot_dir + +func G(i int) int { + return i*i +} + +-- $WORK/_dir/src/under_dir/b.go -- +package dot_dir + +func G(i int) int { + return i*i +} + +-- $WORK/testdata/src/testdata_dir/b.go -- +package testdata_dir + +func G(i int) int { + return i*i +}