Skip to content

Commit

Permalink
feat: coverage diff (#127)
Browse files Browse the repository at this point in the history
  • Loading branch information
vladopajic authored Nov 21, 2024
1 parent 21fedf0 commit 099e1ff
Show file tree
Hide file tree
Showing 18 changed files with 481 additions and 42 deletions.
11 changes: 10 additions & 1 deletion .testcoverage.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,13 @@ exclude:
# Exclude files or packages matching their paths
paths:
- \.pb\.go$ # excludes all protobuf generated files
- ^pkg/bar # exclude package `pkg/bar`
- ^pkg/bar # exclude package `pkg/bar`

# File name of go-test-coverage breakdown file, which can be used to
# analyze coverage difference.
breakdown-file-name: ''

diff:
# File name of go-test-coverage breakdown file which will be used to
# report coverage difference.
base-breakdown-file-name: ''
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ lint: get-golangcilint
.PHONY: test
test:
go test -timeout=3s -race -count=10 -failfast -shuffle=on -short ./...
go test -timeout=10s -race -count=1 -failfast -shuffle=on ./... -coverprofile=./cover.profile -covermode=atomic -coverpkg=./...
go test -timeout=20s -race -count=1 -failfast -shuffle=on ./... -coverprofile=./cover.profile -covermode=atomic -coverpkg=./...

# Runs test coverage check
.PHONY: check-coverage
Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,15 @@ exclude:
paths:
- \.pb\.go$ # excludes all protobuf generated files
- ^pkg/bar # exclude package `pkg/bar`

# File name of go-test-coverage breakdown file, which can be used to
# analyze coverage difference.
breakdown-file-name: ''

diff:
# File name of go-test-coverage breakdown file which will be used to
# report coverage difference.
base-breakdown-file-name: ''
```

### Exclude Code from Coverage
Expand Down
14 changes: 14 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,18 @@ inputs:
default: -1
type: number

breakdown-file-name:
description: File name of go-test-coverage breakdown file, which can be used to analyze coverage difference. Overrides value from configuration.
required: false
default: ""
type: string

diff-base-breakdown-file-name:
description: File name of go-test-coverage breakdown file used to calculate coverage difference from current (head).
required: false
default: ""
type: string

# Badge (as file)
badge-file-name:
description: If specified, a coverage badge will be generated and saved to the given file path.
Expand Down Expand Up @@ -123,6 +135,8 @@ runs:
- --threshold-file=${{ inputs.threshold-file }}
- --threshold-package=${{ inputs.threshold-package }}
- --threshold-total=${{ inputs.threshold-total }}
- --breakdown-file-name=${{ inputs.breakdown-file-name || '''''' }}
- --diff-base-breakdown-file-name=${{ inputs.diff-base-breakdown-file-name || '''''' }}
- --badge-file-name=${{ inputs.badge-file-name || '''''' }}
- --cdn-key=${{ inputs.cdn-key || '''''' }}
- --cdn-secret=${{ inputs.cdn-secret || '''''' }}
Expand Down
19 changes: 17 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ type args struct {
ThresholdFile int `arg:"-f,--threshold-file"`
ThresholdPackage int `arg:"-k,--threshold-package"`
ThresholdTotal int `arg:"-t,--threshold-total"`
BadgeFileName string `arg:"-b,--badge-file-name"`

BreakdownFileName string `arg:"--breakdown-file-name"`
DiffBaseBreakdownFileName string `arg:"--diff-base-breakdown-file-name"`

BadgeFileName string `arg:"-b,--badge-file-name"`

CDNKey string `arg:"--cdn-key"`
CDNSecret string `arg:"--cdn-secret"`
Expand All @@ -57,6 +61,9 @@ func newArgs() args {
ThresholdPackage: ciDefaultInt,
ThresholdTotal: ciDefaultInt,

BreakdownFileName: ciDefaultString,
DiffBaseBreakdownFileName: ciDefaultString,

// Badge
BadgeFileName: ciDefaultString,

Expand All @@ -81,7 +88,7 @@ func (args) Version() string {
return Name + " " + Version
}

//nolint:cyclop,maintidx,mnd // relax
//nolint:cyclop,maintidx,mnd,funlen // relax
func (a *args) overrideConfig(cfg testcoverage.Config) (testcoverage.Config, error) {
if !isCIDefaultString(a.Profile) {
cfg.Profile = a.Profile
Expand All @@ -107,6 +114,14 @@ func (a *args) overrideConfig(cfg testcoverage.Config) (testcoverage.Config, err
cfg.Threshold.Total = a.ThresholdTotal
}

if !isCIDefaultString(a.BreakdownFileName) {
cfg.BreakdownFileName = a.BreakdownFileName
}

if !isCIDefaultString(a.DiffBaseBreakdownFileName) {
cfg.Diff.BaseBreakdownFileName = a.DiffBaseBreakdownFileName
}

if !isCIDefaultString(a.BadgeFileName) {
cfg.Badge.FileName = a.BadgeFileName
}
Expand Down
66 changes: 56 additions & 10 deletions pkg/testcoverage/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,32 @@ import (
"bytes"
"fmt"
"io"
"os"
"strings"

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

func Check(w io.Writer, cfg Config) bool {
stats, err := coverage.GenerateCoverageStats(coverage.Config{
Profiles: strings.Split(cfg.Profile, ","),
LocalPrefix: cfg.LocalPrefix,
ExcludePaths: cfg.Exclude.Paths,
})
currentStats, err := GenerateCoverageStats(cfg)
if err != nil {
fmt.Fprintf(w, "failed to generate coverage statistics: %v\n", err)
return false
}

result := Analyze(cfg, stats)
err = saveCoverageBreakdown(cfg, currentStats)
if err != nil {
fmt.Fprintf(w, "failed to save coverage breakdown: %v\n", err)
return false
}

baseStats, err := loadBaseCoverageBreakdown(cfg)
if err != nil {
fmt.Fprintf(w, "failed to load base coverage breakdown: %v\n", err)
return false
}

result := Analyze(cfg, currentStats, baseStats)

report := reportForHuman(w, result)

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

func Analyze(cfg Config, coverageStats []coverage.Stats) AnalyzeResult {
func GenerateCoverageStats(cfg Config) ([]coverage.Stats, error) {
return coverage.GenerateCoverageStats(coverage.Config{ //nolint:wrapcheck // err wrapped above
Profiles: strings.Split(cfg.Profile, ","),
LocalPrefix: cfg.LocalPrefix,
ExcludePaths: cfg.Exclude.Paths,
})
}

func Analyze(cfg Config, current, base []coverage.Stats) AnalyzeResult {
thr := cfg.Threshold
overrideRules := compileOverridePathRules(cfg)

return AnalyzeResult{
Threshold: thr,
FilesBelowThreshold: checkCoverageStatsBelowThreshold(coverageStats, thr.File, overrideRules),
FilesBelowThreshold: checkCoverageStatsBelowThreshold(current, thr.File, overrideRules),
PackagesBelowThreshold: checkCoverageStatsBelowThreshold(
makePackageStats(coverageStats), thr.Package, overrideRules,
makePackageStats(current), thr.Package, overrideRules,
),
TotalStats: coverage.CalcTotalStats(coverageStats),
TotalStats: coverage.CalcTotalStats(current),
HasBaseBreakdown: len(base) > 0,
Diff: calculateStatsDiff(current, base),
}
}

func saveCoverageBreakdown(cfg Config, stats []coverage.Stats) error {
if cfg.BreakdownFileName == "" {
return nil
}

//nolint:mnd,wrapcheck,gosec // relax
return os.WriteFile(cfg.BreakdownFileName, coverage.SerializeStats(stats), 0o644)
}

func loadBaseCoverageBreakdown(cfg Config) ([]coverage.Stats, error) {
if cfg.Diff.BaseBreakdownFileName == "" {
return nil, nil
}

data, err := os.ReadFile(cfg.Diff.BaseBreakdownFileName)
if err != nil {
return nil, fmt.Errorf("reading file content failed: %w", err)
}

stats, err := coverage.DeserializeStats(data)
if err != nil {
return nil, fmt.Errorf("parsing file failed: %w", err)
}

return stats, nil
}
Loading

0 comments on commit 099e1ff

Please sign in to comment.