Skip to content

Commit 099e1ff

Browse files
authored
feat: coverage diff (#127)
1 parent 21fedf0 commit 099e1ff

18 files changed

+481
-42
lines changed

.testcoverage.example.yml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,13 @@ exclude:
4343
# Exclude files or packages matching their paths
4444
paths:
4545
- \.pb\.go$ # excludes all protobuf generated files
46-
- ^pkg/bar # exclude package `pkg/bar`
46+
- ^pkg/bar # exclude package `pkg/bar`
47+
48+
# File name of go-test-coverage breakdown file, which can be used to
49+
# analyze coverage difference.
50+
breakdown-file-name: ''
51+
52+
diff:
53+
# File name of go-test-coverage breakdown file which will be used to
54+
# report coverage difference.
55+
base-breakdown-file-name: ''

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ lint: get-golangcilint
2222
.PHONY: test
2323
test:
2424
go test -timeout=3s -race -count=10 -failfast -shuffle=on -short ./...
25-
go test -timeout=10s -race -count=1 -failfast -shuffle=on ./... -coverprofile=./cover.profile -covermode=atomic -coverpkg=./...
25+
go test -timeout=20s -race -count=1 -failfast -shuffle=on ./... -coverprofile=./cover.profile -covermode=atomic -coverpkg=./...
2626

2727
# Runs test coverage check
2828
.PHONY: check-coverage

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,15 @@ exclude:
126126
paths:
127127
- \.pb\.go$ # excludes all protobuf generated files
128128
- ^pkg/bar # exclude package `pkg/bar`
129+
130+
# File name of go-test-coverage breakdown file, which can be used to
131+
# analyze coverage difference.
132+
breakdown-file-name: ''
133+
134+
diff:
135+
# File name of go-test-coverage breakdown file which will be used to
136+
# report coverage difference.
137+
base-breakdown-file-name: ''
129138
```
130139

131140
### Exclude Code from Coverage

action.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,18 @@ inputs:
3636
default: -1
3737
type: number
3838

39+
breakdown-file-name:
40+
description: File name of go-test-coverage breakdown file, which can be used to analyze coverage difference. Overrides value from configuration.
41+
required: false
42+
default: ""
43+
type: string
44+
45+
diff-base-breakdown-file-name:
46+
description: File name of go-test-coverage breakdown file used to calculate coverage difference from current (head).
47+
required: false
48+
default: ""
49+
type: string
50+
3951
# Badge (as file)
4052
badge-file-name:
4153
description: If specified, a coverage badge will be generated and saved to the given file path.
@@ -123,6 +135,8 @@ runs:
123135
- --threshold-file=${{ inputs.threshold-file }}
124136
- --threshold-package=${{ inputs.threshold-package }}
125137
- --threshold-total=${{ inputs.threshold-total }}
138+
- --breakdown-file-name=${{ inputs.breakdown-file-name || '''''' }}
139+
- --diff-base-breakdown-file-name=${{ inputs.diff-base-breakdown-file-name || '''''' }}
126140
- --badge-file-name=${{ inputs.badge-file-name || '''''' }}
127141
- --cdn-key=${{ inputs.cdn-key || '''''' }}
128142
- --cdn-secret=${{ inputs.cdn-secret || '''''' }}

main.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,11 @@ type args struct {
3131
ThresholdFile int `arg:"-f,--threshold-file"`
3232
ThresholdPackage int `arg:"-k,--threshold-package"`
3333
ThresholdTotal int `arg:"-t,--threshold-total"`
34-
BadgeFileName string `arg:"-b,--badge-file-name"`
34+
35+
BreakdownFileName string `arg:"--breakdown-file-name"`
36+
DiffBaseBreakdownFileName string `arg:"--diff-base-breakdown-file-name"`
37+
38+
BadgeFileName string `arg:"-b,--badge-file-name"`
3539

3640
CDNKey string `arg:"--cdn-key"`
3741
CDNSecret string `arg:"--cdn-secret"`
@@ -57,6 +61,9 @@ func newArgs() args {
5761
ThresholdPackage: ciDefaultInt,
5862
ThresholdTotal: ciDefaultInt,
5963

64+
BreakdownFileName: ciDefaultString,
65+
DiffBaseBreakdownFileName: ciDefaultString,
66+
6067
// Badge
6168
BadgeFileName: ciDefaultString,
6269

@@ -81,7 +88,7 @@ func (args) Version() string {
8188
return Name + " " + Version
8289
}
8390

84-
//nolint:cyclop,maintidx,mnd // relax
91+
//nolint:cyclop,maintidx,mnd,funlen // relax
8592
func (a *args) overrideConfig(cfg testcoverage.Config) (testcoverage.Config, error) {
8693
if !isCIDefaultString(a.Profile) {
8794
cfg.Profile = a.Profile
@@ -107,6 +114,14 @@ func (a *args) overrideConfig(cfg testcoverage.Config) (testcoverage.Config, err
107114
cfg.Threshold.Total = a.ThresholdTotal
108115
}
109116

117+
if !isCIDefaultString(a.BreakdownFileName) {
118+
cfg.BreakdownFileName = a.BreakdownFileName
119+
}
120+
121+
if !isCIDefaultString(a.DiffBaseBreakdownFileName) {
122+
cfg.Diff.BaseBreakdownFileName = a.DiffBaseBreakdownFileName
123+
}
124+
110125
if !isCIDefaultString(a.BadgeFileName) {
111126
cfg.Badge.FileName = a.BadgeFileName
112127
}

pkg/testcoverage/check.go

Lines changed: 56 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,32 @@ import (
55
"bytes"
66
"fmt"
77
"io"
8+
"os"
89
"strings"
910

1011
"github.com/vladopajic/go-test-coverage/v2/pkg/testcoverage/coverage"
1112
)
1213

1314
func Check(w io.Writer, cfg Config) bool {
14-
stats, err := coverage.GenerateCoverageStats(coverage.Config{
15-
Profiles: strings.Split(cfg.Profile, ","),
16-
LocalPrefix: cfg.LocalPrefix,
17-
ExcludePaths: cfg.Exclude.Paths,
18-
})
15+
currentStats, err := GenerateCoverageStats(cfg)
1916
if err != nil {
2017
fmt.Fprintf(w, "failed to generate coverage statistics: %v\n", err)
2118
return false
2219
}
2320

24-
result := Analyze(cfg, stats)
21+
err = saveCoverageBreakdown(cfg, currentStats)
22+
if err != nil {
23+
fmt.Fprintf(w, "failed to save coverage breakdown: %v\n", err)
24+
return false
25+
}
26+
27+
baseStats, err := loadBaseCoverageBreakdown(cfg)
28+
if err != nil {
29+
fmt.Fprintf(w, "failed to load base coverage breakdown: %v\n", err)
30+
return false
31+
}
32+
33+
result := Analyze(cfg, currentStats, baseStats)
2534

2635
report := reportForHuman(w, result)
2736

@@ -56,16 +65,53 @@ func reportForHuman(w io.Writer, result AnalyzeResult) string {
5665
return buffer.String()
5766
}
5867

59-
func Analyze(cfg Config, coverageStats []coverage.Stats) AnalyzeResult {
68+
func GenerateCoverageStats(cfg Config) ([]coverage.Stats, error) {
69+
return coverage.GenerateCoverageStats(coverage.Config{ //nolint:wrapcheck // err wrapped above
70+
Profiles: strings.Split(cfg.Profile, ","),
71+
LocalPrefix: cfg.LocalPrefix,
72+
ExcludePaths: cfg.Exclude.Paths,
73+
})
74+
}
75+
76+
func Analyze(cfg Config, current, base []coverage.Stats) AnalyzeResult {
6077
thr := cfg.Threshold
6178
overrideRules := compileOverridePathRules(cfg)
6279

6380
return AnalyzeResult{
6481
Threshold: thr,
65-
FilesBelowThreshold: checkCoverageStatsBelowThreshold(coverageStats, thr.File, overrideRules),
82+
FilesBelowThreshold: checkCoverageStatsBelowThreshold(current, thr.File, overrideRules),
6683
PackagesBelowThreshold: checkCoverageStatsBelowThreshold(
67-
makePackageStats(coverageStats), thr.Package, overrideRules,
84+
makePackageStats(current), thr.Package, overrideRules,
6885
),
69-
TotalStats: coverage.CalcTotalStats(coverageStats),
86+
TotalStats: coverage.CalcTotalStats(current),
87+
HasBaseBreakdown: len(base) > 0,
88+
Diff: calculateStatsDiff(current, base),
89+
}
90+
}
91+
92+
func saveCoverageBreakdown(cfg Config, stats []coverage.Stats) error {
93+
if cfg.BreakdownFileName == "" {
94+
return nil
95+
}
96+
97+
//nolint:mnd,wrapcheck,gosec // relax
98+
return os.WriteFile(cfg.BreakdownFileName, coverage.SerializeStats(stats), 0o644)
99+
}
100+
101+
func loadBaseCoverageBreakdown(cfg Config) ([]coverage.Stats, error) {
102+
if cfg.Diff.BaseBreakdownFileName == "" {
103+
return nil, nil
104+
}
105+
106+
data, err := os.ReadFile(cfg.Diff.BaseBreakdownFileName)
107+
if err != nil {
108+
return nil, fmt.Errorf("reading file content failed: %w", err)
70109
}
110+
111+
stats, err := coverage.DeserializeStats(data)
112+
if err != nil {
113+
return nil, fmt.Errorf("parsing file failed: %w", err)
114+
}
115+
116+
return stats, nil
71117
}

0 commit comments

Comments
 (0)