@@ -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
@@ -1338,6 +1339,13 @@ type runCache struct {
1338
1339
id2 cache.ActionID
1339
1340
}
1340
1341
1342
+ func coverProfTempFile (a * work.Action ) string {
1343
+ if a .Objdir == "" {
1344
+ panic ("internal error: objdir not set in coverProfTempFile" )
1345
+ }
1346
+ return a .Objdir + "_cover_.out"
1347
+ }
1348
+
1341
1349
// stdoutMu and lockedStdout provide a locked standard output
1342
1350
// that guarantees never to interlace writes from multiple
1343
1351
// goroutines, so that we can have multiple JSON streams writing
@@ -1396,13 +1404,6 @@ func (r *runTestActor) Act(b *work.Builder, ctx context.Context, a *work.Action)
1396
1404
return nil
1397
1405
}
1398
1406
1399
- coverProfTempFile := func (a * work.Action ) string {
1400
- if a .Objdir == "" {
1401
- panic ("internal error: objdir not set in coverProfTempFile" )
1402
- }
1403
- return a .Objdir + "_cover_.out"
1404
- }
1405
-
1406
1407
if p := a .Package ; len (p .TestGoFiles )+ len (p .XTestGoFiles ) == 0 {
1407
1408
reportNoTestFiles := true
1408
1409
if cfg .BuildCover && cfg .Experiment .CoverageRedesign && p .Internal .Cover .GenMeta {
@@ -1426,7 +1427,7 @@ func (r *runTestActor) Act(b *work.Builder, ctx context.Context, a *work.Action)
1426
1427
if err := work .WriteCoverageProfile (b , a , mf , cp , stdout ); err != nil {
1427
1428
return err
1428
1429
}
1429
- mergeCoverProfile (stdout , cp )
1430
+ mergeCoverProfile (cp )
1430
1431
}
1431
1432
}
1432
1433
}
@@ -1616,7 +1617,7 @@ func (r *runTestActor) Act(b *work.Builder, ctx context.Context, a *work.Action)
1616
1617
a .TestOutput = & buf
1617
1618
t := fmt .Sprintf ("%.3fs" , time .Since (t0 ).Seconds ())
1618
1619
1619
- mergeCoverProfile (cmd . Stdout , a .Objdir + "_cover_.out" )
1620
+ mergeCoverProfile (a .Objdir + "_cover_.out" )
1620
1621
1621
1622
if err == nil {
1622
1623
norun := ""
@@ -1638,10 +1639,10 @@ func (r *runTestActor) Act(b *work.Builder, ctx context.Context, a *work.Action)
1638
1639
cmd .Stdout .Write ([]byte ("\n " ))
1639
1640
}
1640
1641
fmt .Fprintf (cmd .Stdout , "ok \t %s\t %s%s%s\n " , a .Package .ImportPath , t , coveragePercentage (out ), norun )
1641
- r .c .saveOutput (a )
1642
+ r .c .saveOutput (a , coverProfTempFile ( a ) )
1642
1643
} else {
1643
1644
if testFailFast {
1644
- testShouldFailFast . Store ( true )
1645
+ r . c . saveOutput ( a , coverProfTempFile ( a ) )
1645
1646
}
1646
1647
1647
1648
base .SetExitStatus (1 )
@@ -1736,7 +1737,18 @@ func (c *runCache) tryCacheWithID(b *work.Builder, a *work.Action, id string) bo
1736
1737
// Note that this list is documented above,
1737
1738
// so if you add to this list, update the docs too.
1738
1739
cacheArgs = append (cacheArgs , arg )
1739
-
1740
+ case "-test.coverprofile" ,
1741
+ "-test.outputdir" :
1742
+ // The `-coverprofile` and `-outputdir` arguments, which
1743
+ // only affect the location of profile output, are cacheable. This
1744
+ // is based on the process where, upon a cache hit, stored profile
1745
+ // data is copied to the specified output location. This mechanism
1746
+ // ensures that output location preferences are honored without
1747
+ // modifying the profile's content, thereby justifying their
1748
+ // cacheability without impacting cache integrity. This allows
1749
+ // cached coverage profiles to be written to different files.
1750
+ // Note that this list is documented above,
1751
+ // so if you add to this list, update the docs too.
1740
1752
default :
1741
1753
// nothing else is cacheable
1742
1754
if cache .DebugTest {
@@ -1848,6 +1860,21 @@ func (c *runCache) tryCacheWithID(b *work.Builder, a *work.Action, id string) bo
1848
1860
j ++
1849
1861
}
1850
1862
c .buf .Write (data [j :])
1863
+
1864
+ // Write coverage data to profile.
1865
+ if cfg .BuildCover {
1866
+ // The cached coverprofile has the same expiration time as the
1867
+ // test result it corresponds to. That time is already checked
1868
+ // above, so we can ignore the entry returned by GetFile here.
1869
+ f , _ , err := cache .GetFile (cache .Default (), coverProfileAndInputKey (testID , testInputsID ))
1870
+ if err != nil {
1871
+ if cache .DebugTest {
1872
+ fmt .Fprintf (os .Stderr , "testcache: %s: test coverage profile not found: %v\n " , a .Package .ImportPath , err )
1873
+ }
1874
+ return false
1875
+ }
1876
+ mergeCoverProfile (f )
1877
+ }
1851
1878
return true
1852
1879
}
1853
1880
@@ -1996,7 +2023,12 @@ func testAndInputKey(testID, testInputsID cache.ActionID) cache.ActionID {
1996
2023
return cache .Subkey (testID , fmt .Sprintf ("inputs:%x" , testInputsID ))
1997
2024
}
1998
2025
1999
- func (c * runCache ) saveOutput (a * work.Action ) {
2026
+ // coverProfileAndInputKey returns the "coverprofile" cache key for the pair (testID, testInputsID).
2027
+ func coverProfileAndInputKey (testID , testInputsID cache.ActionID ) cache.ActionID {
2028
+ return cache .Subkey (testAndInputKey (testID , testInputsID ), "coverprofile" )
2029
+ }
2030
+
2031
+ func (c * runCache ) saveOutput (a * work.Action , coverProfileFile string ) {
2000
2032
if c .id1 == (cache.ActionID {}) && c .id2 == (cache.ActionID {}) {
2001
2033
return
2002
2034
}
@@ -2017,19 +2049,37 @@ func (c *runCache) saveOutput(a *work.Action) {
2017
2049
if err != nil {
2018
2050
return
2019
2051
}
2052
+ var saveCoverProfile = func (testID cache.ActionID ) {}
2053
+ if coverProfileFile != "" {
2054
+ coverProfile , err := os .Open (coverProfileFile )
2055
+ if err == nil {
2056
+ saveCoverProfile = func (testID cache.ActionID ) {
2057
+ cache .Default ().Put (coverProfileAndInputKey (testID , testInputsID ), coverProfile )
2058
+ }
2059
+ defer func () {
2060
+ if err := coverProfile .Close (); err != nil && cache .DebugTest {
2061
+ fmt .Fprintf (os .Stderr , "testcache: %s: closing temporary coverprofile: %v" , a .Package .ImportPath , err )
2062
+ }
2063
+ }()
2064
+ } else if cache .DebugTest {
2065
+ fmt .Fprintf (os .Stderr , "testcache: %s: failed to open temporary coverprofile: %s" , a .Package .ImportPath , err )
2066
+ }
2067
+ }
2020
2068
if c .id1 != (cache.ActionID {}) {
2021
2069
if cache .DebugTest {
2022
2070
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 ))
2023
2071
}
2024
2072
cache .PutNoVerify (cache .Default (), c .id1 , bytes .NewReader (testlog ))
2025
2073
cache .PutNoVerify (cache .Default (), testAndInputKey (c .id1 , testInputsID ), bytes .NewReader (a .TestOutput .Bytes ()))
2074
+ saveCoverProfile (c .id1 )
2026
2075
}
2027
2076
if c .id2 != (cache.ActionID {}) {
2028
2077
if cache .DebugTest {
2029
2078
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 ))
2030
2079
}
2031
2080
cache .PutNoVerify (cache .Default (), c .id2 , bytes .NewReader (testlog ))
2032
2081
cache .PutNoVerify (cache .Default (), testAndInputKey (c .id2 , testInputsID ), bytes .NewReader (a .TestOutput .Bytes ()))
2082
+ saveCoverProfile (c .id2 )
2033
2083
}
2034
2084
}
2035
2085
0 commit comments