@@ -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
@@ -1353,6 +1354,13 @@ type runCache struct {
1353
1354
id2 cache.ActionID
1354
1355
}
1355
1356
1357
+ func coverProfTempFile (a * work.Action ) string {
1358
+ if a .Objdir == "" {
1359
+ panic ("internal error: objdir not set in coverProfTempFile" )
1360
+ }
1361
+ return a .Objdir + "_cover_.out"
1362
+ }
1363
+
1356
1364
// stdoutMu and lockedStdout provide a locked standard output
1357
1365
// that guarantees never to interlace writes from multiple
1358
1366
// goroutines, so that we can have multiple JSON streams writing
@@ -1450,13 +1458,6 @@ func (r *runTestActor) Act(b *work.Builder, ctx context.Context, a *work.Action)
1450
1458
return nil
1451
1459
}
1452
1460
1453
- coverProfTempFile := func (a * work.Action ) string {
1454
- if a .Objdir == "" {
1455
- panic ("internal error: objdir not set in coverProfTempFile" )
1456
- }
1457
- return a .Objdir + "_cover_.out"
1458
- }
1459
-
1460
1461
if p := a .Package ; len (p .TestGoFiles )+ len (p .XTestGoFiles ) == 0 {
1461
1462
reportNoTestFiles := true
1462
1463
if cfg .BuildCover && cfg .Experiment .CoverageRedesign && p .Internal .Cover .GenMeta {
@@ -1480,7 +1481,7 @@ func (r *runTestActor) Act(b *work.Builder, ctx context.Context, a *work.Action)
1480
1481
if err := work .WriteCoverageProfile (b , a , mf , cp , stdout ); err != nil {
1481
1482
return err
1482
1483
}
1483
- mergeCoverProfile (stdout , cp )
1484
+ mergeCoverProfile (cp )
1484
1485
}
1485
1486
}
1486
1487
}
@@ -1643,7 +1644,7 @@ func (r *runTestActor) Act(b *work.Builder, ctx context.Context, a *work.Action)
1643
1644
a .TestOutput = & buf
1644
1645
t := fmt .Sprintf ("%.3fs" , time .Since (t0 ).Seconds ())
1645
1646
1646
- mergeCoverProfile (cmd . Stdout , a .Objdir + "_cover_.out" )
1647
+ mergeCoverProfile (a .Objdir + "_cover_.out" )
1647
1648
1648
1649
if err == nil {
1649
1650
norun := ""
@@ -1665,7 +1666,7 @@ func (r *runTestActor) Act(b *work.Builder, ctx context.Context, a *work.Action)
1665
1666
cmd .Stdout .Write ([]byte ("\n " ))
1666
1667
}
1667
1668
fmt .Fprintf (cmd .Stdout , "ok \t %s\t %s%s%s\n " , a .Package .ImportPath , t , coveragePercentage (out ), norun )
1668
- r .c .saveOutput (a )
1669
+ r .c .saveOutput (a , coverProfTempFile ( a ) )
1669
1670
} else {
1670
1671
if testFailFast {
1671
1672
testShouldFailFast .Store (true )
@@ -1763,7 +1764,18 @@ func (c *runCache) tryCacheWithID(b *work.Builder, a *work.Action, id string) bo
1763
1764
// Note that this list is documented above,
1764
1765
// so if you add to this list, update the docs too.
1765
1766
cacheArgs = append (cacheArgs , arg )
1766
-
1767
+ case "-test.coverprofile" ,
1768
+ "-test.outputdir" :
1769
+ // The `-coverprofile` and `-outputdir` arguments, which
1770
+ // only affect the location of profile output, are cacheable. This
1771
+ // is based on the process where, upon a cache hit, stored profile
1772
+ // data is copied to the specified output location. This mechanism
1773
+ // ensures that output location preferences are honored without
1774
+ // modifying the profile's content, thereby justifying their
1775
+ // cacheability without impacting cache integrity. This allows
1776
+ // cached coverage profiles to be written to different files.
1777
+ // Note that this list is documented above,
1778
+ // so if you add to this list, update the docs too.
1767
1779
default :
1768
1780
// nothing else is cacheable
1769
1781
if cache .DebugTest {
@@ -1875,6 +1887,21 @@ func (c *runCache) tryCacheWithID(b *work.Builder, a *work.Action, id string) bo
1875
1887
j ++
1876
1888
}
1877
1889
c .buf .Write (data [j :])
1890
+
1891
+ // Write coverage data to profile.
1892
+ if cfg .BuildCover {
1893
+ // The cached coverprofile has the same expiration time as the
1894
+ // test result it corresponds to. That time is already checked
1895
+ // above, so we can ignore the entry returned by GetFile here.
1896
+ f , _ , err := cache .GetFile (cache .Default (), coverProfileAndInputKey (testID , testInputsID ))
1897
+ if err != nil {
1898
+ if cache .DebugTest {
1899
+ fmt .Fprintf (os .Stderr , "testcache: %s: test coverage profile not found: %v\n " , a .Package .ImportPath , err )
1900
+ }
1901
+ return false
1902
+ }
1903
+ mergeCoverProfile (f )
1904
+ }
1878
1905
return true
1879
1906
}
1880
1907
@@ -2023,7 +2050,12 @@ func testAndInputKey(testID, testInputsID cache.ActionID) cache.ActionID {
2023
2050
return cache .Subkey (testID , fmt .Sprintf ("inputs:%x" , testInputsID ))
2024
2051
}
2025
2052
2026
- func (c * runCache ) saveOutput (a * work.Action ) {
2053
+ // coverProfileAndInputKey returns the "coverprofile" cache key for the pair (testID, testInputsID).
2054
+ func coverProfileAndInputKey (testID , testInputsID cache.ActionID ) cache.ActionID {
2055
+ return cache .Subkey (testAndInputKey (testID , testInputsID ), "coverprofile" )
2056
+ }
2057
+
2058
+ func (c * runCache ) saveOutput (a * work.Action , coverProfileFile string ) {
2027
2059
if c .id1 == (cache.ActionID {}) && c .id2 == (cache.ActionID {}) {
2028
2060
return
2029
2061
}
@@ -2044,19 +2076,37 @@ func (c *runCache) saveOutput(a *work.Action) {
2044
2076
if err != nil {
2045
2077
return
2046
2078
}
2079
+ var saveCoverProfile = func (testID cache.ActionID ) {}
2080
+ if coverProfileFile != "" {
2081
+ coverProfile , err := os .Open (coverProfileFile )
2082
+ if err == nil {
2083
+ saveCoverProfile = func (testID cache.ActionID ) {
2084
+ cache .Default ().Put (coverProfileAndInputKey (testID , testInputsID ), coverProfile )
2085
+ }
2086
+ defer func () {
2087
+ if err := coverProfile .Close (); err != nil && cache .DebugTest {
2088
+ fmt .Fprintf (os .Stderr , "testcache: %s: closing temporary coverprofile: %v" , a .Package .ImportPath , err )
2089
+ }
2090
+ }()
2091
+ } else if cache .DebugTest {
2092
+ fmt .Fprintf (os .Stderr , "testcache: %s: failed to open temporary coverprofile: %s" , a .Package .ImportPath , err )
2093
+ }
2094
+ }
2047
2095
if c .id1 != (cache.ActionID {}) {
2048
2096
if cache .DebugTest {
2049
2097
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 ))
2050
2098
}
2051
2099
cache .PutNoVerify (cache .Default (), c .id1 , bytes .NewReader (testlog ))
2052
2100
cache .PutNoVerify (cache .Default (), testAndInputKey (c .id1 , testInputsID ), bytes .NewReader (a .TestOutput .Bytes ()))
2101
+ saveCoverProfile (c .id1 )
2053
2102
}
2054
2103
if c .id2 != (cache.ActionID {}) {
2055
2104
if cache .DebugTest {
2056
2105
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 ))
2057
2106
}
2058
2107
cache .PutNoVerify (cache .Default (), c .id2 , bytes .NewReader (testlog ))
2059
2108
cache .PutNoVerify (cache .Default (), testAndInputKey (c .id2 , testInputsID ), bytes .NewReader (a .TestOutput .Bytes ()))
2109
+ saveCoverProfile (c .id2 )
2060
2110
}
2061
2111
}
2062
2112
0 commit comments