Go version
go1.26-devel_bb124921e9 Sun Jul 27 12:36:07 2025 -0400 darwin/amd64
Output of go env
in your module/workspace:
╰─ go env
AR='ar'
CC='clang'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='clang++'
GCCGO='gccgo'
GO111MODULE=''
GOAMD64='v1'
GOARCH='amd64'
GOAUTH='netrc'
GOBIN=''
GOCACHE='/Users/ryancurrah/Library/Caches/go-build'
GOCACHEPROG=''
GODEBUG=''
GOENV='/Users/ryancurrah/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -arch x86_64 -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/fk/8lgm5_252mb0ln68l_z9wlz80000gn/T/go-build3317734112=/tmp/go-build -gno-record-gcc-switches -fno-common'
GOHOSTARCH='amd64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMOD='/dev/null'
GOMODCACHE='/Users/ryancurrah/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='darwin'
GOPATH='/Users/ryancurrah/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/Users/ryancurrah/git/github.com/ryancurrah/go'
GOSUMDB='sum.golang.org'
GOTELEMETRY='local'
GOTELEMETRYDIR='/Users/ryancurrah/Library/Application Support/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/Users/ryancurrah/git/github.com/ryancurrah/go/pkg/tool/darwin_amd64'
GOVCS=''
GOVERSION='go1.26-devel_bb124921e9 Sun Jul 27 12:36:07 2025 -0400'
GOWORK=''
PKG_CONFIG='pkg-config'
What did you do?
Hello guys,
Thanks a lot for resolving https://github.com/golang/go/issues/23565.
I started testing this feature and encountered a problem that occurs when I use the go test command with the -coverpkg flag.
When coverage is collected with the -coverpkg flag and test result caching is enabled, the generated report may include lines that no longer exist. This happens because:
Each test package attempts to collect coverage for all packages matching the -coverpkg pattern.
If the test result is loaded from the cache, the coverage data may be outdated—especially if the test package does not directly or indirectly depend on the modified code, leaving the cache uninvalidated.
for example we have project with layout
Project layout
proj/
some_func.go
some_func_test.go
sub/
sub.go
sub_test.go
sum/
sum.go
Files Content
some_func.go
package proj
import "proj/sum"
func SomeFunc(a, b int) int {
if a == 0 && b == 0 {
return 0
}
return sum.Sum(a, b)
}
sub.go
package sub
func Sub(a, b int) int {
if a == 0 && b == 0 {
return 0
}
return a - b
}
sum.go
package sum
func Sum(a, b int) int {
if a == 0 {
return b
}
return a + b
}
some_func_test.go
package proj
import (
"github.com/stretchr/testify/require"
"testing"
)
func Test_SomeFunc(t *testing.T) {
t.Run("test1", func(t *testing.T) {
require.Equal(t, 2, SomeFunc(1, 1))
})
}
sub_test.go
package sub
import (
"github.com/stretchr/testify/require"
"testing"
)
func Test_Sub(t *testing.T) {
t.Run("test_sub1", func(t *testing.T) {
require.Equal(t, 0, Sub(1, 1))
})
}
Coverage result of this tests
mode: set
proj/some_func.go:5.29,6.22 1 1
proj/some_func.go:6.22,8.3 1 0
proj/some_func.go:9.2,9.22 1 1
proj/sub/sub.go:3.24,4.22 1 0
proj/sub/sub.go:4.22,6.3 1 0
proj/sub/sub.go:7.2,7.14 1 0
proj/sum/sum.go:3.24,4.12 1 1
proj/sum/sum.go:4.12,6.3 1 0
proj/sum/sum.go:7.2,7.14 1 1
proj/some_func.go:5.29,6.22 1 0
proj/some_func.go:6.22,8.3 1 0
proj/some_func.go:9.2,9.22 1 0
proj/sub/sub.go:3.24,4.22 1 1
proj/sub/sub.go:4.22,6.3 1 0
proj/sub/sub.go:7.2,7.14 1 1
proj/sum/sum.go:3.24,4.12 1 0
proj/sum/sum.go:4.12,6.3 1 0
proj/sum/sum.go:7.2,7.14 1 0
proj/sum/sum.go:3.24,4.12 1 0
proj/sum/sum.go:4.12,6.3 1 0
proj/sum/sum.go:7.2,7.14 1 0
Change a bit sub.go
sub.go
package sub
func Sub(a, b int) int {
if a == 0 && b == 0 || a == -100 {
return 0
}
return a - b
}
Coverage result after change
mode: set
proj/some_func.go:5.29,6.22 1 1
proj/some_func.go:6.22,8.3 1 0
proj/some_func.go:9.2,9.22 1 1
proj/sub/sub.go:3.24,4.22 1 0 <- Old (Should have been invalidated)
proj/sub/sub.go:4.22,6.3 1 0
proj/sub/sub.go:7.2,7.14 1 0
proj/sum/sum.go:3.24,4.12 1 1
proj/sum/sum.go:4.12,6.3 1 0
proj/sum/sum.go:7.2,7.14 1 1
proj/some_func.go:5.29,6.22 1 0
proj/some_func.go:6.22,8.3 1 0
proj/some_func.go:9.2,9.22 1 0
proj/sub/sub.go:3.24,4.35 1 1 <- New
proj/sub/sub.go:4.35,6.3 1 0
proj/sub/sub.go:7.2,7.14 1 1
proj/sum/sum.go:3.24,4.12 1 0
proj/sum/sum.go:4.12,6.3 1 0
proj/sum/sum.go:7.2,7.14 1 0
proj/sum/sum.go:3.24,4.12 1 0
proj/sum/sum.go:4.12,6.3 1 0
proj/sum/sum.go:7.2,7.14 1 0
Commands
First run
go test -coverpkg=proj/... -coverprofile=cover.out ./proj/...
ok proj (cached) coverage: 44.4% of statements in proj/...
ok proj/sub (cached) coverage: 22.2% of statements in proj/...
proj/sum coverage: 0.0% of statements
Second run
go test -coverpkg=proj/... -coverprofile=cover.out ./proj/...
ok proj (cached) coverage: 44.4% of statements in proj/...
ok proj/sub 0.005s coverage: 22.2% of statements in proj/...
proj/sum coverage: 0.0% of statements
Conclusion
So there are now 2 versions of sub.go:3.24
line in one report and proj/sub/sub.go:3.24,4.22 1 0
doesn't exist, as a result it can break coverage tools which generate cobertura coverage.
What did you see happen?
Duplicate coverage results. The cached coverage and the new coverage.
What did you expect to see?
Only the new coverage results and the cached results to be invalidated.