Skip to content

Commit bb12492

Browse files
committed
fix(cmd/go): prevent stale coverage data when using -coverpkg with test caching
- Add coverage package source files to cache invalidation calculation - Include GoFiles and CgoFiles from testCoverPkgs in computeTestInputsID - Ensure cache is invalidated when any covered source file changes - Prevent mixing old and new coverage data in profiles - Add test case to reproduce and verify the fix Fixes #23565 where cached coverage profiles with -coverpkg contained outdated line references when source files were modified but tests didn't directly depend on the changed packages.
1 parent 6fbad4b commit bb12492

File tree

2 files changed

+160
-0
lines changed

2 files changed

+160
-0
lines changed

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1986,6 +1986,29 @@ func computeTestInputsID(a *work.Action, testlog []byte) (cache.ActionID, error)
19861986
fmt.Fprintf(h, "open %s %x\n", name, fh)
19871987
}
19881988
}
1989+
1990+
// When using -coverpkg, include source files from coverage packages in cache calculation.
1991+
// This ensures cache is invalidated when any covered source file changes.
1992+
if len(testCoverPkgs) > 0 {
1993+
for _, pkg := range testCoverPkgs {
1994+
// Include all Go source files from coverage packages
1995+
for _, file := range pkg.GoFiles {
1996+
name := filepath.Join(pkg.Dir, file)
1997+
if a.Package.Root == "" || search.InDir(name, a.Package.Root) == "" {
1998+
continue
1999+
}
2000+
fmt.Fprintf(h, "coverstat %s %x\n", name, hashStat(name))
2001+
}
2002+
for _, file := range pkg.CgoFiles {
2003+
name := filepath.Join(pkg.Dir, file)
2004+
if a.Package.Root == "" || search.InDir(name, a.Package.Root) == "" {
2005+
continue
2006+
}
2007+
fmt.Fprintf(h, "coverstat %s %x\n", name, hashStat(name))
2008+
}
2009+
}
2010+
}
2011+
19892012
sum := h.Sum()
19902013
return sum, nil
19912014
}
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
env GO111MODULE=on
2+
3+
# Test for bug where cached coverage profiles with -coverpkg can contain
4+
# outdated line references when source files are modified.
5+
# This reproduces the issue where coverage data from cache may reference
6+
# lines that no longer exist in the updated source files.
7+
8+
[short] skip
9+
[GODEBUG:gocacheverify=1] skip
10+
11+
# We're testing cache behavior, so start with a clean GOCACHE.
12+
env GOCACHE=$WORK/cache
13+
14+
# Create a project structure with multiple packages
15+
# proj/
16+
# some_func.go
17+
# some_func_test.go
18+
# sub/
19+
# sub.go
20+
# sub_test.go
21+
# sum/
22+
# sum.go
23+
24+
# Switch to the proj directory
25+
cd proj
26+
27+
# Run tests with -coverpkg to collect coverage for all packages
28+
go test -coverpkg=proj/... -coverprofile=cover1.out ./...
29+
stdout 'coverage:'
30+
31+
# Verify the first coverage profile exists and has expected content
32+
exists cover1.out
33+
grep -q 'proj/sub/sub.go:' cover1.out
34+
35+
# Run again to ensure caching works
36+
go test -coverpkg=proj/... -coverprofile=cover1_cached.out ./...
37+
stdout '\(cached\)'
38+
stdout 'coverage:'
39+
40+
# Note: Due to the bug, cached coverage profiles may have duplicate entries
41+
# This is the issue we're trying to fix
42+
43+
# Now modify sub.go to change line structure - this will invalidate
44+
# the cache for sub package but not for proj package
45+
cp ../sub_modified.go sub/sub.go
46+
47+
# The bug manifests as duplicate or invalid line references in the coverage profile
48+
# After modifying sub.go, we should not have both old and new line references
49+
go test -coverpkg=proj/... -coverprofile=cover2.out ./...
50+
stdout 'coverage:'
51+
52+
# With the bug present, we would see duplicate entries for the same lines
53+
# This demonstrates the bug - there should be duplicates in the coverage profile
54+
grep 'proj/sub/sub.go:' cover2.out
55+
# The fix should ensure that only the new line format exists, not the old one
56+
grep 'proj/sub/sub.go:3.24,4.35' cover2.out
57+
# This should fail if the stale coverage line exists (the bug is present)
58+
! grep 'proj/sub/sub.go:3.24,4.22' cover2.out
59+
60+
-- proj/go.mod --
61+
module proj
62+
63+
go 1.21
64+
65+
-- proj/some_func.go --
66+
package proj
67+
68+
import "proj/sum"
69+
70+
func SomeFunc(a, b int) int {
71+
if a == 0 && b == 0 {
72+
return 0
73+
}
74+
return sum.Sum(a, b)
75+
}
76+
77+
-- proj/some_func_test.go --
78+
package proj
79+
80+
import (
81+
"testing"
82+
)
83+
84+
func Test_SomeFunc(t *testing.T) {
85+
t.Run("test1", func(t *testing.T) {
86+
result := SomeFunc(1, 1)
87+
if result != 2 {
88+
t.Errorf("Expected 2, got %d", result)
89+
}
90+
})
91+
}
92+
93+
-- proj/sub/sub.go --
94+
package sub
95+
96+
func Sub(a, b int) int {
97+
if a == 0 && b == 0 {
98+
return 0
99+
}
100+
return a - b
101+
}
102+
103+
-- proj/sub/sub_test.go --
104+
package sub
105+
106+
import (
107+
"testing"
108+
)
109+
110+
func Test_Sub(t *testing.T) {
111+
t.Run("test_sub1", func(t *testing.T) {
112+
result := Sub(1, 1)
113+
if result != 0 {
114+
t.Errorf("Expected 0, got %d", result)
115+
}
116+
})
117+
}
118+
119+
-- proj/sum/sum.go --
120+
package sum
121+
122+
func Sum(a, b int) int {
123+
if a == 0 {
124+
return b
125+
}
126+
return a + b
127+
}
128+
129+
-- sub_modified.go --
130+
package sub
131+
132+
func Sub(a, b int) int {
133+
if a == 0 && b == 0 || a == -100 {
134+
return 0
135+
}
136+
return a - b
137+
}

0 commit comments

Comments
 (0)