Skip to content

Commit b960982

Browse files
committed
ensure the coverprofile flag is not empty before retrieving the cover profile cache data
1 parent 948f23b commit b960982

File tree

3 files changed

+37
-51
lines changed

3 files changed

+37
-51
lines changed

src/cmd/go/alldocs.go

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

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

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,6 @@ import (
1414
"sync"
1515
)
1616

17-
// coverMerge manages the state for merging test coverage profiles.
18-
// It ensures thread-safe operations on a single coverage profile file
19-
// across multiple test runs and packages.
2017
var coverMerge struct {
2118
f *os.File
2219
sync.Mutex // for f.Write

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

Lines changed: 34 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,9 @@ elapsed time in the summary line.
126126
127127
The rule for a match in the cache is that the run involves the same
128128
test binary and the flags on the command line come entirely from a
129-
restricted set of 'cacheable' test flags, defined as -benchtime, -cover,
130-
-covermode, -coverprofile, -cpu, -list, -parallel, -run, -short, -timeout,
131-
-failfast, -fullpath and -v.
129+
restricted set of 'cacheable' test flags, defined as -benchtime,
130+
-coverprofile, -outputdir, -cpu, -list, -parallel, -run, -short,
131+
-timeout, -failfast, -fullpath and -v.
132132
If a run of go test has any test or non-test flags outside this set,
133133
the result is not cached. To disable test caching, use any test flag
134134
or argument other than the cacheable flags. The idiomatic way to disable
@@ -1672,7 +1672,7 @@ func (r *runTestActor) Act(b *work.Builder, ctx context.Context, a *work.Action)
16721672
a.TestOutput = &buf
16731673
t := fmt.Sprintf("%.3fs", time.Since(t0).Seconds())
16741674

1675-
mergeCoverProfile(a.Objdir + "_cover_.out")
1675+
mergeCoverProfile(coverProfTempFile(a))
16761676

16771677
if err == nil {
16781678
norun := ""
@@ -1694,7 +1694,7 @@ func (r *runTestActor) Act(b *work.Builder, ctx context.Context, a *work.Action)
16941694
cmd.Stdout.Write([]byte("\n"))
16951695
}
16961696
fmt.Fprintf(cmd.Stdout, "ok \t%s\t%s%s%s\n", a.Package.ImportPath, t, coveragePercentage(out), norun)
1697-
r.c.saveOutput(a, coverProfTempFile(a))
1697+
r.c.saveOutput(a)
16981698
} else {
16991699
if testFailFast {
17001700
testShouldFailFast.Store(true)
@@ -1794,14 +1794,7 @@ func (c *runCache) tryCacheWithID(b *work.Builder, a *work.Action, id string) bo
17941794
cacheArgs = append(cacheArgs, arg)
17951795
case "-test.coverprofile",
17961796
"-test.outputdir":
1797-
// The `-coverprofile` and `-outputdir` arguments, which
1798-
// only affect the location of profile output, are cacheable. This
1799-
// is based on the process where, upon a cache hit, stored profile
1800-
// data is copied to the specified output location. This mechanism
1801-
// ensures that output location preferences are honored without
1802-
// modifying the profile's content, thereby justifying their
1803-
// cacheability without impacting cache integrity. This allows
1804-
// cached coverage profiles to be written to different files.
1797+
// These are cacheable and do not invalidate the cache when they change.
18051798
// Note that this list is documented above,
18061799
// so if you add to this list, update the docs too.
18071800
default:
@@ -1875,6 +1868,20 @@ func (c *runCache) tryCacheWithID(b *work.Builder, a *work.Action, id string) bo
18751868
// Parse cached result in preparation for changing run time to "(cached)".
18761869
// If we can't parse the cached result, don't use it.
18771870
data, entry, err = cache.GetBytes(cache.Default(), testAndInputKey(testID, testInputsID))
1871+
1872+
// Merge cached cover profile data to cover profile.
1873+
if testCoverProfile != "" {
1874+
// Specifically ignore entry as it will be the same as above.
1875+
cpData, _, err := cache.GetFile(cache.Default(), coverProfileAndInputKey(testID, testInputsID))
1876+
if err != nil {
1877+
if cache.DebugTest {
1878+
fmt.Fprintf(os.Stderr, "testcache: %s: cached cover profile missing: %v\n", a.Package.ImportPath, err)
1879+
}
1880+
return false
1881+
}
1882+
mergeCoverProfile(cpData)
1883+
}
1884+
18781885
if len(data) == 0 || data[len(data)-1] != '\n' {
18791886
if cache.DebugTest {
18801887
if err != nil {
@@ -1915,21 +1922,6 @@ func (c *runCache) tryCacheWithID(b *work.Builder, a *work.Action, id string) bo
19151922
j++
19161923
}
19171924
c.buf.Write(data[j:])
1918-
1919-
// Write coverage data to profile.
1920-
if cfg.BuildCover {
1921-
// The cached coverprofile has the same expiration time as the
1922-
// test result it corresponds to. That time is already checked
1923-
// above, so we can ignore the entry returned by GetFile here.
1924-
f, _, err := cache.GetFile(cache.Default(), coverProfileAndInputKey(testID, testInputsID))
1925-
if err != nil {
1926-
if cache.DebugTest {
1927-
fmt.Fprintf(os.Stderr, "testcache: %s: cached test result valid but cached coverage profile missing: %v\n", a.Package.ImportPath, err)
1928-
}
1929-
return false
1930-
}
1931-
mergeCoverProfile(f)
1932-
}
19331925
return true
19341926
}
19351927

@@ -2083,7 +2075,7 @@ func coverProfileAndInputKey(testID, testInputsID cache.ActionID) cache.ActionID
20832075
return cache.Subkey(testAndInputKey(testID, testInputsID), "coverprofile")
20842076
}
20852077

2086-
func (c *runCache) saveOutput(a *work.Action, coverProfileFile string) {
2078+
func (c *runCache) saveOutput(a *work.Action) {
20872079
if c.id1 == (cache.ActionID{}) && c.id2 == (cache.ActionID{}) {
20882080
return
20892081
}
@@ -2104,21 +2096,14 @@ func (c *runCache) saveOutput(a *work.Action, coverProfileFile string) {
21042096
if err != nil {
21052097
return
21062098
}
2107-
var saveCoverProfile = func(testID cache.ActionID) {}
2108-
if coverProfileFile != "" {
2109-
coverProfile, err := os.Open(coverProfileFile)
2110-
if err == nil {
2111-
saveCoverProfile = func(testID cache.ActionID) {
2112-
cache.Default().Put(coverProfileAndInputKey(testID, testInputsID), coverProfile)
2099+
var coverProfile []byte
2100+
if testCoverProfile != "" {
2101+
coverProfile, err = os.ReadFile(coverProfTempFile(a))
2102+
if err != nil && !os.IsNotExist(err) {
2103+
if cache.DebugTest {
2104+
fmt.Fprintf(os.Stderr, "testcache: %s: reading cover profile: %v\n", a.Package.ImportPath, err)
21132105
}
2114-
defer func() {
2115-
if err := coverProfile.Close(); err != nil && cache.DebugTest {
2116-
base.Errorf("closing temporary coverprofile: %v", err)
2117-
}
2118-
}()
2119-
} else if cache.DebugTest {
2120-
// Not indicative of a problem, as the test may not have generated a cover profile yet.
2121-
fmt.Fprintf(os.Stderr, "testcache: %s: opening temporary coverprofile: %s\n", a.Package.ImportPath, err)
2106+
return
21222107
}
21232108
}
21242109
if c.id1 != (cache.ActionID{}) {
@@ -2127,15 +2112,19 @@ func (c *runCache) saveOutput(a *work.Action, coverProfileFile string) {
21272112
}
21282113
cache.PutNoVerify(cache.Default(), c.id1, bytes.NewReader(testlog))
21292114
cache.PutNoVerify(cache.Default(), testAndInputKey(c.id1, testInputsID), bytes.NewReader(a.TestOutput.Bytes()))
2130-
saveCoverProfile(c.id1)
2115+
if coverProfile != nil {
2116+
cache.PutNoVerify(cache.Default(), coverProfileAndInputKey(c.id1, testInputsID), bytes.NewReader(coverProfile))
2117+
}
21312118
}
21322119
if c.id2 != (cache.ActionID{}) {
21332120
if cache.DebugTest {
21342121
fmt.Fprintf(os.Stderr, "testcache: %s: save test ID %x => input ID %x => %x\n", a.Package.ImportPath, c.id2, testInputsID, testAndInputKey(c.id2, testInputsID))
21352122
}
21362123
cache.PutNoVerify(cache.Default(), c.id2, bytes.NewReader(testlog))
21372124
cache.PutNoVerify(cache.Default(), testAndInputKey(c.id2, testInputsID), bytes.NewReader(a.TestOutput.Bytes()))
2138-
saveCoverProfile(c.id2)
2125+
if coverProfile != nil {
2126+
cache.PutNoVerify(cache.Default(), coverProfileAndInputKey(c.id2, testInputsID), bytes.NewReader(coverProfile))
2127+
}
21392128
}
21402129
}
21412130

0 commit comments

Comments
 (0)