@@ -126,8 +126,9 @@ elapsed time in the summary line.
126
126
127
127
The rule for a match in the cache is that the run involves the same
128
128
test binary and the flags on the command line come entirely from a
129
- restricted set of 'cacheable' test flags, defined as -benchtime, -cpu,
130
- -list, -parallel, -run, -short, -timeout, -failfast, -fullpath and -v.
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.
131
132
If a run of go test has any test or non-test flags outside this set,
132
133
the result is not cached. To disable test caching, use any test flag
133
134
or argument other than the cacheable flags. The idiomatic way to disable
@@ -1377,6 +1378,13 @@ type runCache struct {
1377
1378
id2 cache.ActionID
1378
1379
}
1379
1380
1381
+ func coverProfTempFile (a * work.Action ) string {
1382
+ if a .Objdir == "" {
1383
+ panic ("internal error: objdir not set in coverProfTempFile" )
1384
+ }
1385
+ return a .Objdir + "_cover_.out"
1386
+ }
1387
+
1380
1388
// stdoutMu and lockedStdout provide a locked standard output
1381
1389
// that guarantees never to interlace writes from multiple
1382
1390
// goroutines, so that we can have multiple JSON streams writing
@@ -1478,13 +1486,6 @@ func (r *runTestActor) Act(b *work.Builder, ctx context.Context, a *work.Action)
1478
1486
return nil
1479
1487
}
1480
1488
1481
- coverProfTempFile := func (a * work.Action ) string {
1482
- if a .Objdir == "" {
1483
- panic ("internal error: objdir not set in coverProfTempFile" )
1484
- }
1485
- return a .Objdir + "_cover_.out"
1486
- }
1487
-
1488
1489
if p := a .Package ; len (p .TestGoFiles )+ len (p .XTestGoFiles ) == 0 {
1489
1490
reportNoTestFiles := true
1490
1491
if cfg .BuildCover && cfg .Experiment .CoverageRedesign && p .Internal .Cover .GenMeta {
@@ -1508,7 +1509,7 @@ func (r *runTestActor) Act(b *work.Builder, ctx context.Context, a *work.Action)
1508
1509
if err := work .WriteCoverageProfile (b , a , mf , cp , stdout ); err != nil {
1509
1510
return err
1510
1511
}
1511
- mergeCoverProfile (stdout , cp )
1512
+ mergeCoverProfile (cp )
1512
1513
}
1513
1514
}
1514
1515
}
@@ -1671,7 +1672,7 @@ func (r *runTestActor) Act(b *work.Builder, ctx context.Context, a *work.Action)
1671
1672
a .TestOutput = & buf
1672
1673
t := fmt .Sprintf ("%.3fs" , time .Since (t0 ).Seconds ())
1673
1674
1674
- mergeCoverProfile (cmd . Stdout , a .Objdir + "_cover_.out" )
1675
+ mergeCoverProfile (a .Objdir + "_cover_.out" )
1675
1676
1676
1677
if err == nil {
1677
1678
norun := ""
@@ -1693,7 +1694,7 @@ func (r *runTestActor) Act(b *work.Builder, ctx context.Context, a *work.Action)
1693
1694
cmd .Stdout .Write ([]byte ("\n " ))
1694
1695
}
1695
1696
fmt .Fprintf (cmd .Stdout , "ok \t %s\t %s%s%s\n " , a .Package .ImportPath , t , coveragePercentage (out ), norun )
1696
- r .c .saveOutput (a )
1697
+ r .c .saveOutput (a , coverProfTempFile ( a ) )
1697
1698
} else {
1698
1699
if testFailFast {
1699
1700
testShouldFailFast .Store (true )
@@ -1791,7 +1792,18 @@ func (c *runCache) tryCacheWithID(b *work.Builder, a *work.Action, id string) bo
1791
1792
// Note that this list is documented above,
1792
1793
// so if you add to this list, update the docs too.
1793
1794
cacheArgs = append (cacheArgs , arg )
1794
-
1795
+ case "-test.coverprofile" ,
1796
+ "-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.
1805
+ // Note that this list is documented above,
1806
+ // so if you add to this list, update the docs too.
1795
1807
default :
1796
1808
// nothing else is cacheable
1797
1809
if cache .DebugTest {
@@ -1903,6 +1915,21 @@ func (c *runCache) tryCacheWithID(b *work.Builder, a *work.Action, id string) bo
1903
1915
j ++
1904
1916
}
1905
1917
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: test coverage profile not found: %v\n " , a .Package .ImportPath , err )
1928
+ }
1929
+ return false
1930
+ }
1931
+ mergeCoverProfile (f )
1932
+ }
1906
1933
return true
1907
1934
}
1908
1935
@@ -2051,7 +2078,12 @@ func testAndInputKey(testID, testInputsID cache.ActionID) cache.ActionID {
2051
2078
return cache .Subkey (testID , fmt .Sprintf ("inputs:%x" , testInputsID ))
2052
2079
}
2053
2080
2054
- func (c * runCache ) saveOutput (a * work.Action ) {
2081
+ // coverProfileAndInputKey returns the "coverprofile" cache key for the pair (testID, testInputsID).
2082
+ func coverProfileAndInputKey (testID , testInputsID cache.ActionID ) cache.ActionID {
2083
+ return cache .Subkey (testAndInputKey (testID , testInputsID ), "coverprofile" )
2084
+ }
2085
+
2086
+ func (c * runCache ) saveOutput (a * work.Action , coverProfileFile string ) {
2055
2087
if c .id1 == (cache.ActionID {}) && c .id2 == (cache.ActionID {}) {
2056
2088
return
2057
2089
}
@@ -2072,19 +2104,37 @@ func (c *runCache) saveOutput(a *work.Action) {
2072
2104
if err != nil {
2073
2105
return
2074
2106
}
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 )
2113
+ }
2114
+ defer func () {
2115
+ if err := coverProfile .Close (); err != nil && cache .DebugTest {
2116
+ fmt .Fprintf (os .Stderr , "testcache: %s: closing temporary coverprofile: %v" , a .Package .ImportPath , err )
2117
+ }
2118
+ }()
2119
+ } else if cache .DebugTest {
2120
+ fmt .Fprintf (os .Stderr , "testcache: %s: failed to open temporary coverprofile: %s" , a .Package .ImportPath , err )
2121
+ }
2122
+ }
2075
2123
if c .id1 != (cache.ActionID {}) {
2076
2124
if cache .DebugTest {
2077
2125
fmt .Fprintf (os .Stderr , "testcache: %s: save test ID %x => input ID %x => %x\n " , a .Package .ImportPath , c .id1 , testInputsID , testAndInputKey (c .id1 , testInputsID ))
2078
2126
}
2079
2127
cache .PutNoVerify (cache .Default (), c .id1 , bytes .NewReader (testlog ))
2080
2128
cache .PutNoVerify (cache .Default (), testAndInputKey (c .id1 , testInputsID ), bytes .NewReader (a .TestOutput .Bytes ()))
2129
+ saveCoverProfile (c .id1 )
2081
2130
}
2082
2131
if c .id2 != (cache.ActionID {}) {
2083
2132
if cache .DebugTest {
2084
2133
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 ))
2085
2134
}
2086
2135
cache .PutNoVerify (cache .Default (), c .id2 , bytes .NewReader (testlog ))
2087
2136
cache .PutNoVerify (cache .Default (), testAndInputKey (c .id2 , testInputsID ), bytes .NewReader (a .TestOutput .Bytes ()))
2137
+ saveCoverProfile (c .id2 )
2088
2138
}
2089
2139
}
2090
2140
0 commit comments