diff --git a/src/cmd/go/internal/test/cover.go b/src/cmd/go/internal/test/cover.go index 6be2e410077b26..ecae2a59b45147 100644 --- a/src/cmd/go/internal/test/cover.go +++ b/src/cmd/go/internal/test/cover.go @@ -16,7 +16,8 @@ import ( var coverMerge struct { f *os.File - sync.Mutex // for f.Write + fsize int64 // size of valid data written to f + sync.Mutex // for f.Write } // initCoverProfile initializes the test coverage profile. @@ -36,11 +37,12 @@ func initCoverProfile() { if err != nil { base.Fatalf("%v", err) } - _, err = fmt.Fprintf(f, "mode: %s\n", testCoverMode) + n, err := fmt.Fprintf(f, "mode: %s\n", testCoverMode) if err != nil { base.Fatalf("%v", err) } coverMerge.f = f + coverMerge.fsize += int64(n) } // mergeCoverProfile merges file into the profile stored in testCoverProfile. @@ -67,10 +69,15 @@ func mergeCoverProfile(file string) error { if err != nil || string(buf) != expect { return errMalformedCoverProfile } - _, err = io.Copy(coverMerge.f, r) + m, err := io.Copy(coverMerge.f, r) if err != nil { - return fmt.Errorf("saving coverage profile: %v\n", err) + if m > 0 { + // Attempt to rollback partial write. + coverMerge.f.Seek(coverMerge.fsize, 0) + } + return fmt.Errorf("saving coverage profile: %w", err) } + coverMerge.fsize += m return nil } @@ -78,6 +85,10 @@ func closeCoverProfile() { if coverMerge.f == nil { return } + // Discard any partially written data from a failed merge. + if err := coverMerge.f.Truncate(coverMerge.fsize); err != nil { + base.Errorf("closing coverage profile: %v", err) + } if err := coverMerge.f.Close(); err != nil { base.Errorf("closing coverage profile: %v", err) }