fix(coverage): compare overall on shared files and cover compact edges
Deleting 100%-covered packages such as recovery falsely regressed overall percent against merge-base. Baseline overall now uses only files still present in the candidate profile, with a 0.1pp CI tolerance. Also exercise stripSchemaValueCompact nested map/slice branches. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -132,7 +132,10 @@ func run(
|
||||
fmt.Fprintln(stderr, baselineErr)
|
||||
return 2
|
||||
}
|
||||
baselineOverall = coveragePercent(baseline)
|
||||
// Compare overall non-regression on the shared file set only.
|
||||
// Deleted packages (for example a retired 100%-covered helper tree)
|
||||
// must not create a false overall regression against merge-base.
|
||||
baselineOverall = coveragePercent(filterBlocksByFiles(baseline, profileFiles(overall)))
|
||||
}
|
||||
}
|
||||
diff, err := readProfiles(diffPaths, modulePath)
|
||||
@@ -483,6 +486,29 @@ func coveragePercent(blocks []coverageBlock) float64 {
|
||||
return float64(covered) * 100 / float64(total)
|
||||
}
|
||||
|
||||
func profileFiles(blocks []coverageBlock) map[string]bool {
|
||||
files := map[string]bool{}
|
||||
for _, block := range blocks {
|
||||
if block.File != "" {
|
||||
files[block.File] = true
|
||||
}
|
||||
}
|
||||
return files
|
||||
}
|
||||
|
||||
func filterBlocksByFiles(blocks []coverageBlock, allowed map[string]bool) []coverageBlock {
|
||||
if len(allowed) == 0 {
|
||||
return nil
|
||||
}
|
||||
filtered := make([]coverageBlock, 0, len(blocks))
|
||||
for _, block := range blocks {
|
||||
if allowed[block.File] {
|
||||
filtered = append(filtered, block)
|
||||
}
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
// mergeCoverageBlocks unions duplicate blocks emitted by cross-package
|
||||
// coverage. A block is covered when any instrumented test binary executed it;
|
||||
// counting the same source block once per binary would both dilute the overall
|
||||
|
||||
@@ -92,7 +92,7 @@ func TestRunDefaultsToFullChangedCodeCoverage(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunEvaluatesBaselineProfileWithCandidateCoverageModel(t *testing.T) {
|
||||
func TestCrossPlatformCoverageRunEvaluatesBaselineProfileWithCandidateCoverageModel(t *testing.T) {
|
||||
profile := filepath.Join(t.TempDir(), "coverage.out")
|
||||
body := "mode: atomic\n" +
|
||||
"example.com/project/internal/a.go:10.1,12.2 5 0\n" +
|
||||
@@ -101,9 +101,14 @@ func TestRunEvaluatesBaselineProfileWithCandidateCoverageModel(t *testing.T) {
|
||||
if err := os.WriteFile(profile, []byte(body), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
baseline := filepath.Join(t.TempDir(), "baseline.out")
|
||||
baselineBody := body + "example.com/project/internal/deleted.go:1.1,2.2 100 1\n"
|
||||
if err := os.WriteFile(baseline, []byte(baselineBody), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
args := []string{
|
||||
"--overall-profile", profile,
|
||||
"--baseline-profile", profile,
|
||||
"--baseline-profile", baseline,
|
||||
"--diff-profile", profile,
|
||||
"--base-ref", "base",
|
||||
"--module", "example.com/project",
|
||||
@@ -115,8 +120,10 @@ func TestRunEvaluatesBaselineProfileWithCandidateCoverageModel(t *testing.T) {
|
||||
if code := run(args, &stdout, &stderr, loader, nil); code != 0 {
|
||||
t.Fatalf("run code=%d stderr=%s", code, stderr.String())
|
||||
}
|
||||
// Candidate overall stays 50%; merge-base ignores the deleted 100%-covered
|
||||
// package so non-regression compares the shared file set only.
|
||||
if !strings.Contains(stdout.String(), "overall coverage: 50.0000% (merge-base 50.0000%") {
|
||||
t.Fatalf("baseline and candidate did not share one coverage model: %q", stdout.String())
|
||||
t.Fatalf("shared-file baseline comparison failed: %q", stdout.String())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -436,6 +443,25 @@ func TestEvaluateAllowsMeasurementTolerance(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCrossPlatformCoverageFilterBlocksByFilesDropsDeletedPackages(t *testing.T) {
|
||||
baseline := []coverageBlock{
|
||||
{File: "internal/keep.go", StartLine: 1, EndLine: 10, Statements: 50, Count: 1},
|
||||
{File: "internal/keep.go", StartLine: 11, EndLine: 20, Statements: 50, Count: 0},
|
||||
{File: "internal/deleted.go", StartLine: 1, EndLine: 40, Statements: 100, Count: 1},
|
||||
}
|
||||
currentFiles := profileFiles([]coverageBlock{
|
||||
{File: "internal/keep.go", StartLine: 1, EndLine: 8, Statements: 40, Count: 1},
|
||||
})
|
||||
filtered := filterBlocksByFiles(baseline, currentFiles)
|
||||
got := coveragePercent(filtered)
|
||||
if got != 50 {
|
||||
t.Fatalf("shared-file baseline = %.4f%%, want 50%% (deleted 100%%-covered package excluded)", got)
|
||||
}
|
||||
if len(profileFiles(filtered)) != 1 || !profileFiles(filtered)["internal/keep.go"] {
|
||||
t.Fatalf("filtered files = %#v, want only keep.go", profileFiles(filtered))
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvaluateDefaultsToZeroOverallTolerance(t *testing.T) {
|
||||
result := evaluate(gateInput{
|
||||
Overall: []coverageBlock{{Statements: 403, Count: 1}, {Statements: 597, Count: 0}},
|
||||
|
||||
Reference in New Issue
Block a user