Skip to content

Commit 804d032

Browse files
beneschrsc
authored andcommitted
cmd/go: rebuild as needed when vetting test packages
If A's external test package imports B, which imports A, and A's internal test code adds something to A that invalidates anything in A's export data, then we need to build B against the test-augmented version of A before using it to build A's external test package. https://golang.org/cl/92215 taught 'go test' to do this rebuilding properly, but 'go vet' was not taught the same trick when it learned to vet test packages in https://golang.org/cl/87636. This commit moves the necessary logic into the load.TestPackagesFor function so it can be shared by 'go test' and 'go vet'. Fixes #23701. Change-Id: I1086d447eca02933af53de693384eac99a08d9bd Reviewed-on: https://go-review.googlesource.com/104315 Run-TryBot: Russ Cox <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Russ Cox <[email protected]>
1 parent f2abca9 commit 804d032

File tree

3 files changed

+55
-51
lines changed

3 files changed

+55
-51
lines changed

src/cmd/go/go_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -5497,7 +5497,7 @@ func TestTestVet(t *testing.T) {
54975497
tg.grepStdout(`ok\s+vetfail/p2`, "did not run vetfail/p2")
54985498
}
54995499

5500-
func TestTestRebuild(t *testing.T) {
5500+
func TestTestVetRebuild(t *testing.T) {
55015501
tg := testgo(t)
55025502
defer tg.cleanup()
55035503
tg.parallel()
@@ -5533,6 +5533,7 @@ func TestTestRebuild(t *testing.T) {
55335533

55345534
tg.setenv("GOPATH", tg.path("."))
55355535
tg.run("test", "b")
5536+
tg.run("vet", "b")
55365537
}
55375538

55385539
func TestInstallDeps(t *testing.T) {

src/cmd/go/internal/load/pkg.go

+53
Original file line numberDiff line numberDiff line change
@@ -1712,6 +1712,18 @@ func TestPackagesFor(p *Package, forceTest bool) (ptest, pxtest *Package, err er
17121712
}
17131713
}
17141714

1715+
if p != ptest && pxtest != nil {
1716+
// We have made modifications to the package p being tested
1717+
// and are rebuilding p (as ptest).
1718+
// Arrange to rebuild all packages q such that
1719+
// pxtest depends on q and q depends on p.
1720+
// This makes sure that q sees the modifications to p.
1721+
// Strictly speaking, the rebuild is only necessary if the
1722+
// modifications to p change its export metadata, but
1723+
// determining that is a bit tricky, so we rebuild always.
1724+
recompileForTest(p, ptest, pxtest)
1725+
}
1726+
17151727
return ptest, pxtest, nil
17161728
}
17171729

@@ -1732,3 +1744,44 @@ Search:
17321744
}
17331745
return stk
17341746
}
1747+
1748+
func recompileForTest(preal, ptest, pxtest *Package) {
1749+
// The "test copy" of preal is ptest.
1750+
// For each package that depends on preal, make a "test copy"
1751+
// that depends on ptest. And so on, up the dependency tree.
1752+
testCopy := map[*Package]*Package{preal: ptest}
1753+
// Only pxtest and its dependencies can legally depend on p.
1754+
// If ptest or its dependencies depended on p, the dependency
1755+
// would be circular.
1756+
for _, p := range PackageList([]*Package{pxtest}) {
1757+
if p == preal {
1758+
continue
1759+
}
1760+
// Copy on write.
1761+
didSplit := p == pxtest
1762+
split := func() {
1763+
if didSplit {
1764+
return
1765+
}
1766+
didSplit = true
1767+
if testCopy[p] != nil {
1768+
panic("recompileForTest loop")
1769+
}
1770+
p1 := new(Package)
1771+
testCopy[p] = p1
1772+
*p1 = *p
1773+
p1.Internal.Imports = make([]*Package, len(p.Internal.Imports))
1774+
copy(p1.Internal.Imports, p.Internal.Imports)
1775+
p = p1
1776+
p.Target = ""
1777+
}
1778+
1779+
// Update p.Internal.Imports to use test copies.
1780+
for i, imp := range p.Internal.Imports {
1781+
if p1 := testCopy[imp]; p1 != nil && p1 != imp {
1782+
split()
1783+
p.Internal.Imports[i] = p1
1784+
}
1785+
}
1786+
}
1787+
}

src/cmd/go/internal/test/test.go

-50
Original file line numberDiff line numberDiff line change
@@ -911,18 +911,6 @@ func builderTest(b *work.Builder, p *load.Package) (buildAction, runAction, prin
911911
t.ImportXtest = true
912912
}
913913

914-
if ptest != p {
915-
// We have made modifications to the package p being tested
916-
// and are rebuilding p (as ptest).
917-
// Arrange to rebuild all packages q such that
918-
// the test depends on q and q depends on p.
919-
// This makes sure that q sees the modifications to p.
920-
// Strictly speaking, the rebuild is only necessary if the
921-
// modifications to p change its export metadata, but
922-
// determining that is a bit tricky, so we rebuild always.
923-
recompileForTest(pmain, p, ptest, pxtest)
924-
}
925-
926914
for _, cp := range pmain.Internal.Imports {
927915
if len(cp.Internal.CoverVars) > 0 {
928916
t.Cover = append(t.Cover, coverInfo{cp, cp.Internal.CoverVars})
@@ -1058,44 +1046,6 @@ func addTestVet(b *work.Builder, p *load.Package, runAction, installAction *work
10581046
}
10591047
}
10601048

1061-
func recompileForTest(pmain, preal, ptest, pxtest *load.Package) {
1062-
// The "test copy" of preal is ptest.
1063-
// For each package that depends on preal, make a "test copy"
1064-
// that depends on ptest. And so on, up the dependency tree.
1065-
testCopy := map[*load.Package]*load.Package{preal: ptest}
1066-
for _, p := range load.PackageList([]*load.Package{pmain}) {
1067-
if p == preal {
1068-
continue
1069-
}
1070-
// Copy on write.
1071-
didSplit := p == pmain || p == pxtest
1072-
split := func() {
1073-
if didSplit {
1074-
return
1075-
}
1076-
didSplit = true
1077-
if testCopy[p] != nil {
1078-
panic("recompileForTest loop")
1079-
}
1080-
p1 := new(load.Package)
1081-
testCopy[p] = p1
1082-
*p1 = *p
1083-
p1.Internal.Imports = make([]*load.Package, len(p.Internal.Imports))
1084-
copy(p1.Internal.Imports, p.Internal.Imports)
1085-
p = p1
1086-
p.Target = ""
1087-
}
1088-
1089-
// Update p.Internal.Imports to use test copies.
1090-
for i, imp := range p.Internal.Imports {
1091-
if p1 := testCopy[imp]; p1 != nil && p1 != imp {
1092-
split()
1093-
p.Internal.Imports[i] = p1
1094-
}
1095-
}
1096-
}
1097-
}
1098-
10991049
// isTestFile reports whether the source file is a set of tests and should therefore
11001050
// be excluded from coverage analysis.
11011051
func isTestFile(file string) bool {

0 commit comments

Comments
 (0)