Skip to content

Commit 4b0625f

Browse files
author
Mathias M
authored
Add CLI support for coverage thresholds (#54)
Fixes #43
1 parent 9ce370e commit 4b0625f

File tree

5 files changed

+91
-47
lines changed

5 files changed

+91
-47
lines changed

README.md

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -28,29 +28,37 @@ List available themes|`-lt`|`1.2.0`
2828
Render with a specific theme|`-t <theme>`|`1.2.0`
2929
New `kit` theme |`-t kit`|`1.3.0`
3030
Put lower coverage functions on top|`-r`|`1.3.1`
31+
Only show functions whose coverage is smaller than a max threshold|`-cmax`|`1.4.0`
32+
Only show functions whose coverage is greater than a min threshold|`-cmin`|`1.4.0`
3133

3234
## Usage
3335

3436
```
35-
$ gocov-html -h
36-
Usage of ./gocov-html:
37+
Usage of gocov-html:
38+
-cmax uint
39+
only show functions whose coverage is less than cmax (default 100)
40+
-cmin uint
41+
only show functions whose coverage is more than cmin
3742
-d output CSS of default theme
3843
-lt
3944
list available themes
45+
-r put lower coverage functions on top
4046
-s string
4147
path to custom CSS file
4248
-t string
4349
theme to use for rendering (default "golang")
4450
-v show program version
4551
```
4652

47-
`gocov-html` can read a JSON file or read from standard input:
53+
## Examples
54+
55+
Generate code coverage for the `strings` package then generate an HTML report:
4856
```
4957
$ gocov test strings | gocov-html > strings.html
5058
ok strings 0.700s coverage: 98.1% of statements
5159
```
5260

53-
Several packages can be tested at once and added to a single report. Let's test the `fmt`, `math` and `io` packages:
61+
Merge several coverage stats for different packages into a single report:
5462
```
5563
$ gocov test fmt math io | gocov-html > report.html
5664
ok fmt 0.045s coverage: 95.2% of statements
@@ -60,29 +68,23 @@ ok io 0.024s coverage: 88.2% of statements
6068

6169
In this case, the generated report will have an *overview* section with stats per package along with the global coverage percentage. This section may be rendered depending on the theme used. The `golang` (default) theme displays it.
6270

63-
The generated HTML content comes along with a default embedded CSS. However a custom stylesheet can be used with the `-s` flag:
71+
List all available themes:
72+
```
73+
$ gocov-html -lt
74+
golang -- original golang theme (default)
75+
kit -- AdminKit theme
76+
```
77+
78+
Generate a report using a specific theme with `-t`:
6479
```
65-
$ gocov test net/http | gocov-html -s mystyle.css > http.html
80+
$ gocov test io | gocov-html -t kit > io.html
6681
```
6782

68-
As of version 1.2,
69-
- A `-d` flag is available to write the defaut stylesheet to the standard output. This is provided for convenience and easy editing:
70-
```
71-
$ gocov-html -d > newstyle.css
72-
... edit newstyle.css ...
73-
$ gocov test strings | gocov-html -s newstyle.css > http.html
74-
```
75-
- The content of the stylesheet given to `-s` is embedded into the final HTML document
76-
- Theming capabilities are available (to go further than just using a CSS file) through the use of Go templates.
77-
- Use the `-lt` flag to list available themes:
78-
```
79-
$ gocov-html -lt
80-
golang -- original golang theme (default)
81-
```
82-
- Generate a report using a specific theme with `-t`:
83-
```
84-
$ gocov test io | gocov-html -t golang > io.html
85-
```
83+
Only show functions whose code coverage is lower than 90% for the `strings` package:
84+
```
85+
$ gocov test strings|./gocov-html -cmax 90 > strings.html
86+
```
87+
In this example, only 5 matches are added to the report.
8688

8789
## Donate
8890

cmd/gocov-html/main.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ func main() {
4343
listThemes := flag.Bool("lt", false, "list available themes")
4444
theme := flag.String("t", "golang", "theme to use for rendering")
4545
reverseOrder := flag.Bool("r", false, "put lower coverage functions on top")
46+
maxCoverage := flag.Uint64("cmax", 100, "only show functions whose coverage is greater than cmax")
47+
minCoverage := flag.Uint64("cmin", 0, "only show functions whose coverage is smaller than cmin")
4648

4749
flag.Parse()
4850

@@ -63,6 +65,13 @@ func main() {
6365
return
6466
}
6567

68+
if *minCoverage > *maxCoverage {
69+
log.Fatal("error: empty report if cmin > cmax, please use a smaller cmin value.")
70+
}
71+
if *maxCoverage > 100 {
72+
*maxCoverage = 100
73+
}
74+
6675
err := themes.Use(*theme)
6776
if err != nil {
6877
log.Fatalf("theme selection: %v", err)
@@ -85,7 +94,13 @@ func main() {
8594
log.Fatalf("Usage: %s data.json\n", os.Args[0])
8695
}
8796

88-
if err := cov.HTMLReportCoverage(r, *reverseOrder, *css); err != nil {
97+
opts := cov.ReportOptions{
98+
LowCoverageOnTop: *reverseOrder,
99+
Stylesheet: *css,
100+
CoverageMin: uint8(*minCoverage),
101+
CoverageMax: uint8(*maxCoverage),
102+
}
103+
if err := cov.HTMLReportCoverage(r, opts); err != nil {
89104
log.Fatal(err)
90105
}
91106
}

pkg/cov/report.go

Lines changed: 43 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,24 @@ import (
3737
"github.com/rotisserie/eris"
3838
)
3939

40+
// ReportOptions holds various options used when generating the final
41+
// HTML report.
42+
type ReportOptions struct {
43+
// LowCoverageOnTop puts low coverage functions first.
44+
LowCoverageOnTop bool
45+
// Stylesheet is the path to a custom CSS file.
46+
Stylesheet string
47+
// CoverageMin filters out all functions whose code coverage is smaller than it is.
48+
CoverageMin uint8
49+
// CoverageMax filters out all functions whose code coverage is greater than it is.
50+
CoverageMax uint8
51+
}
52+
53+
type report struct {
54+
ReportOptions
55+
packages []*gocov.Package
56+
}
57+
4058
func unmarshalJSON(data []byte) (packages []*gocov.Package, err error) {
4159
result := &struct{ Packages []*gocov.Package }{}
4260
err = json.Unmarshal(data, result)
@@ -46,11 +64,6 @@ func unmarshalJSON(data []byte) (packages []*gocov.Package, err error) {
4664
return
4765
}
4866

49-
type report struct {
50-
packages []*gocov.Package
51-
stylesheet string // absolute path to CSS
52-
}
53-
5467
type reverse struct {
5568
sort.Interface
5669
}
@@ -84,32 +97,36 @@ func (r *report) clear() {
8497
r.packages = nil
8598
}
8699

87-
func buildReportPackage(pkg *gocov.Package, lowCoverageOnTop bool) types.ReportPackage {
100+
func buildReportPackage(pkg *gocov.Package, r *report) types.ReportPackage {
88101
rv := types.ReportPackage{
89102
Pkg: pkg,
90-
Functions: make(types.ReportFunctionList, len(pkg.Functions)),
103+
Functions: make(types.ReportFunctionList, 0),
91104
}
92-
for i, fn := range pkg.Functions {
105+
for _, fn := range pkg.Functions {
93106
reached := 0
94107
for _, stmt := range fn.Statements {
95108
if stmt.Reached > 0 {
96109
reached++
97110
}
98111
}
99-
rv.Functions[i] = types.ReportFunction{Function: fn, StatementsReached: reached}
112+
rf := types.ReportFunction{Function: fn, StatementsReached: reached}
113+
covp := rf.CoveragePercent()
114+
if covp >= float64(r.CoverageMin) && covp <= float64(r.CoverageMax) {
115+
rv.Functions = append(rv.Functions, rf)
116+
}
100117
rv.TotalStatements += len(fn.Statements)
101118
rv.ReachedStatements += reached
102119
}
103-
if lowCoverageOnTop {
120+
if r.LowCoverageOnTop {
104121
sort.Sort(rv.Functions)
105122
} else {
106123
sort.Sort(reverse{rv.Functions})
107124
}
108125
return rv
109126
}
110127

111-
// PrintReport prints a coverage report to the given writer.
112-
func printReport(w io.Writer, lowCoverageOnTop bool, r *report) error {
128+
// printReport prints a coverage report to the given writer.
129+
func printReport(w io.Writer, r *report) error {
113130
theme := themes.Current()
114131
data := theme.Data()
115132

@@ -125,9 +142,9 @@ func printReport(w io.Writer, lowCoverageOnTop bool, r *report) error {
125142
return eris.Wrap(err, "decode script")
126143
}
127144

128-
if len(r.stylesheet) > 0 {
145+
if len(r.Stylesheet) > 0 {
129146
// Inline CSS.
130-
f, err := os.Open(r.stylesheet)
147+
f, err := os.Open(r.Stylesheet)
131148
if err != nil {
132149
return eris.Wrap(err, "print report")
133150
}
@@ -140,14 +157,17 @@ func printReport(w io.Writer, lowCoverageOnTop bool, r *report) error {
140157
reportPackages := make(types.ReportPackageList, len(r.packages))
141158
pkgNames := make([]string, len(r.packages))
142159
for i, pkg := range r.packages {
143-
reportPackages[i] = buildReportPackage(pkg, lowCoverageOnTop)
160+
reportPackages[i] = buildReportPackage(pkg, r)
144161
pkgNames[i] = pkg.Name
145162
}
146163

147164
data.Script = string(sc)
148165
data.Style = css
149166
data.Packages = reportPackages
150-
data.Command = fmt.Sprintf("gocov test %s | gocov-html -t %s", strings.Join(pkgNames, " "), theme.Name())
167+
data.Command = fmt.Sprintf("gocov test %s | gocov-html %s",
168+
strings.Join(pkgNames, " "),
169+
strings.Join(os.Args[1:], " "),
170+
)
151171

152172
if len(reportPackages) > 1 {
153173
rv := types.ReportPackage{
@@ -174,19 +194,20 @@ func exists(path string) (bool, error) {
174194
// parsing JSON data generated by axw/gocov. The css parameter
175195
// is an absolute path to a custom stylesheet. Use an empty
176196
// string to use the default stylesheet available.
177-
func HTMLReportCoverage(r io.Reader, lowCoverageOnTop bool, css string) error {
197+
func HTMLReportCoverage(r io.Reader, opts ReportOptions) error {
178198
t0 := time.Now()
179199
report := newReport()
200+
report.ReportOptions = opts
180201

181202
// Custom stylesheet?
182203
stylesheet := ""
183-
if css != "" {
184-
if _, err := exists(css); err != nil {
204+
if opts.Stylesheet != "" {
205+
if _, err := exists(opts.Stylesheet); err != nil {
185206
return eris.Wrap(err, "stylesheet")
186207
}
187-
stylesheet = css
208+
stylesheet = opts.Stylesheet
188209
}
189-
report.stylesheet = stylesheet
210+
report.Stylesheet = stylesheet
190211

191212
data, err := ioutil.ReadAll(r)
192213
if err != nil {
@@ -202,7 +223,7 @@ func HTMLReportCoverage(r io.Reader, lowCoverageOnTop bool, css string) error {
202223
report.addPackage(pkg)
203224
}
204225
fmt.Println()
205-
err = printReport(os.Stdout, lowCoverageOnTop, report)
226+
err = printReport(os.Stdout, report)
206227
fmt.Fprintf(os.Stderr, "Took %v\n", time.Since(t0))
207228
return eris.Wrap(err, "HTML report")
208229
}

pkg/themes/kit_gen.go

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

themes/kit/index.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,9 @@ <h5 class="card-title mb-0" id="fn_{{$f.Name}}"><code class="codex">func {{$f.Na
215215
</div>
216216
<div class="col-6 text-end">
217217
<ul class="list-inline">
218+
<li class="list-inline-item">
219+
<a class="text-muted" href="{{.ProjectURL}}#donate" target="_blank">Donate!</a>
220+
</li>
218221
<li class="list-inline-item">
219222
<a class="text-muted" href="{{.ProjectURL}}" target="_blank">GitHub</a>
220223
</li>

0 commit comments

Comments
 (0)