undercover 0.8.3 → 0.8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +15 -8
- data/.tool-versions +1 -1
- data/CHANGELOG.md +17 -1
- data/Gemfile +1 -1
- data/README.md +57 -0
- data/lib/undercover/ast_branch_annotator.rb +50 -0
- data/lib/undercover/cli.rb +15 -16
- data/lib/undercover/formatter.rb +18 -1
- data/lib/undercover/json_formatter.rb +84 -0
- data/lib/undercover/lcov_parser.rb +6 -1
- data/lib/undercover/options.rb +13 -1
- data/lib/undercover/result.rb +12 -2
- data/lib/undercover/simplecov_result_adapter.rb +7 -0
- data/lib/undercover/version.rb +1 -1
- data/lib/undercover.rb +1 -0
- data/undercover.gemspec +2 -1
- metadata +19 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8d93f862aad4a70d2370f9da2021a4b32c284084f4a56bc2a04da6f409d2b777
|
|
4
|
+
data.tar.gz: 1cc3a1f5e26aa234019c3ae76df456534a7047dfbb1421175db56ab869d4150f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ae42ba519cd6bd3435fee10c67c20907690036a4bccdd425c2cde2b4276a2bbd33ffd9d8d091b73c6d760fdf7e4bca52e400d71c85f21440f2d2c4ad2fb53d86
|
|
7
|
+
data.tar.gz: 28c65742716800a1a042b513ebe4bf14f254d46d22ef8959406d49f682132525fff4e19a21a7e7418562485cda79f9cb167bfb11877763b0b17e9645a09e116b
|
data/.github/workflows/ruby.yml
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
name: Ruby
|
|
2
|
-
on: [push]
|
|
2
|
+
on: [push, pull_request]
|
|
3
3
|
jobs:
|
|
4
4
|
build:
|
|
5
5
|
runs-on: ubuntu-latest
|
|
6
6
|
strategy:
|
|
7
7
|
matrix:
|
|
8
|
-
ruby: ['
|
|
8
|
+
ruby: ['4.0', '3.4']
|
|
9
9
|
steps:
|
|
10
|
-
- uses: actions/checkout@
|
|
10
|
+
- uses: actions/checkout@v6
|
|
11
11
|
with:
|
|
12
12
|
fetch-depth: 0 # fetch all since test fixtures depend on history
|
|
13
13
|
- name: Set up Ruby ${{ matrix.ruby }}
|
|
@@ -23,7 +23,7 @@ jobs:
|
|
|
23
23
|
run: |
|
|
24
24
|
git fetch --update-head-ok origin master:master
|
|
25
25
|
undercover --simplecov coverage/undercover_coverage.json --compare master
|
|
26
|
-
- uses: actions/upload-artifact@
|
|
26
|
+
- uses: actions/upload-artifact@v7
|
|
27
27
|
with:
|
|
28
28
|
name: undercover-${{ matrix.ruby }}-coverage
|
|
29
29
|
path: coverage/undercover_coverage.json
|
|
@@ -31,12 +31,19 @@ jobs:
|
|
|
31
31
|
runs-on: ubuntu-latest
|
|
32
32
|
needs: build
|
|
33
33
|
steps:
|
|
34
|
-
- uses: actions/download-artifact@
|
|
34
|
+
- uses: actions/download-artifact@v8
|
|
35
35
|
with:
|
|
36
|
-
name: undercover-
|
|
36
|
+
name: undercover-4.0-coverage
|
|
37
37
|
- name: Upload coverage
|
|
38
38
|
run: |
|
|
39
|
-
ruby -e "$(curl -s https://undercover-ci.com/uploader.rb)" -- \
|
|
39
|
+
output=$(ruby -e "$(curl -s https://undercover-ci.com/uploader.rb)" -- \
|
|
40
40
|
--repo grodowski/undercover \
|
|
41
41
|
--commit ${{ github.event.pull_request.head.sha || github.sha }} \
|
|
42
|
-
--simplecov /home/runner/work/undercover/undercover/undercover_coverage.json
|
|
42
|
+
--simplecov /home/runner/work/undercover/undercover/undercover_coverage.json 2>&1) || {
|
|
43
|
+
if echo "$output" | grep -q "has already completed"; then
|
|
44
|
+
echo "Coverage already processed for this commit ✅"
|
|
45
|
+
else
|
|
46
|
+
echo "Coverage upload failed: $output"
|
|
47
|
+
exit 1
|
|
48
|
+
fi
|
|
49
|
+
}
|
data/.tool-versions
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
ruby
|
|
1
|
+
ruby 4.0.1
|
|
2
2
|
nodejs v24.4.0
|
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,20 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
# [0.8.5] - 2026-04-21
|
|
10
|
+
### Added
|
|
11
|
+
- `--format json` CLI option for machine-readable output ([#251](https://github.com/grodowski/undercover/pull/251))
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
- Add `benchmark` as a dependency, required for Ruby 4
|
|
15
|
+
|
|
16
|
+
# [0.8.4] - 2026-02-06
|
|
17
|
+
### Fixed
|
|
18
|
+
- Print branch coverage even on n/a lines (https://github.com/grodowski/undercover/pull/249) by [@kuahyeow](https://github.com/kuahyeow)
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
- Ruby 4 support in CI
|
|
22
|
+
|
|
9
23
|
# [0.8.3] - 2025-11-20
|
|
10
24
|
### Fixed
|
|
11
25
|
- Normalize slash prefix in FilterSet support to work with the 'rails' profile (https://github.com/grodowski/undercover/pull/244)
|
|
@@ -209,7 +223,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|
|
209
223
|
### Added
|
|
210
224
|
- First release of `undercover` 🎉
|
|
211
225
|
|
|
212
|
-
[Unreleased]: https://github.com/grodowski/undercover/compare/v0.8.
|
|
226
|
+
[Unreleased]: https://github.com/grodowski/undercover/compare/v0.8.5...HEAD
|
|
227
|
+
[0.8.5]: https://github.com/grodowski/undercover/compare/v0.8.4...v0.8.5
|
|
228
|
+
[0.8.4]: https://github.com/grodowski/undercover/compare/v0.8.3...v0.8.4
|
|
213
229
|
[0.8.3]: https://github.com/grodowski/undercover/compare/v0.8.2...v0.8.3
|
|
214
230
|
[0.8.2]: https://github.com/grodowski/undercover/compare/v0.8.1...v0.8.2
|
|
215
231
|
[0.8.1]: https://github.com/grodowski/undercover/compare/v0.8.0...v0.8.1
|
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -134,10 +134,67 @@ Usage: undercover [options]
|
|
|
134
134
|
-w, --max-warnings limit Maximum number of warnings to generate before stopping analysis. Useful as a performance improvement for large diffs.
|
|
135
135
|
-f, --include-files globs Include files matching specified glob patterns (comma separated). Defaults to '*.rb,*.rake,*.ru,Rakefile'
|
|
136
136
|
-x, --exclude-files globs Skip files matching specified glob patterns (comma separated). Empty by default.
|
|
137
|
+
--format FORMAT Output format: text, json (default: text)
|
|
137
138
|
-h, --help Prints this help
|
|
138
139
|
--version Show version
|
|
139
140
|
```
|
|
140
141
|
|
|
142
|
+
### JSON Output
|
|
143
|
+
|
|
144
|
+
Use `--format json` to get machine-readable output, useful for CI integrations and tooling:
|
|
145
|
+
|
|
146
|
+
```sh
|
|
147
|
+
undercover --compare origin/main --format json
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Output structure:
|
|
151
|
+
|
|
152
|
+
```json
|
|
153
|
+
{
|
|
154
|
+
"warnings": [
|
|
155
|
+
{
|
|
156
|
+
"node": "Foo#bar",
|
|
157
|
+
"type": "instance method",
|
|
158
|
+
"file": "lib/foo.rb",
|
|
159
|
+
"first_line": 10,
|
|
160
|
+
"last_line": 20,
|
|
161
|
+
"coverage": 0.5,
|
|
162
|
+
"uncovered_lines": [11],
|
|
163
|
+
"uncovered_branches": [
|
|
164
|
+
{
|
|
165
|
+
"line": 13,
|
|
166
|
+
"block": 0,
|
|
167
|
+
"branch": 0,
|
|
168
|
+
"description": "if user.admin?"
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"line": 16,
|
|
172
|
+
"block": 0,
|
|
173
|
+
"branch": 1,
|
|
174
|
+
"description": "else"
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
"line": 19,
|
|
178
|
+
"block": 0,
|
|
179
|
+
"branch": 2,
|
|
180
|
+
"description": "? role == :admin → else"
|
|
181
|
+
}
|
|
182
|
+
]
|
|
183
|
+
}
|
|
184
|
+
],
|
|
185
|
+
"summary": {
|
|
186
|
+
"total_warnings": 1,
|
|
187
|
+
"files_affected": 1
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Each warning includes:
|
|
193
|
+
- `uncovered_lines` — line numbers with zero execution count
|
|
194
|
+
- `uncovered_branches` — branch entries that were never taken, each with `line`, `block`, and `branch` identifiers. The optional `description` field names the branch from the source (e.g. `"if user.admin?"`, `"else"`, `"when :active"`). For same-line branches such as ternaries, the arm type is appended after ` → ` (e.g. `"? role == :admin → else"`).
|
|
195
|
+
|
|
196
|
+
Branch-level entries require `enable_coverage(:branch)` in your SimpleCov configuration (see [Setting up coverage reporting](#setting-up-coverage-reporting)).
|
|
197
|
+
|
|
141
198
|
### Configuration File
|
|
142
199
|
|
|
143
200
|
A configuration file named `.undercover` can be created at the top level of a project's directory containing the same set of options for the CLI.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Undercover
|
|
4
|
+
class AstBranchAnnotator
|
|
5
|
+
def self.call(ast_node)
|
|
6
|
+
new.annotate(ast_node)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# rubocop:disable Metrics/MethodLength,Metrics/AbcSize
|
|
10
|
+
def annotate(ast_node, info = {})
|
|
11
|
+
return info unless ast_node.is_a?(::Parser::AST::Node)
|
|
12
|
+
|
|
13
|
+
case ast_node.type
|
|
14
|
+
when :if
|
|
15
|
+
annotate_if_node(ast_node, info)
|
|
16
|
+
when :case
|
|
17
|
+
ast_node.children.drop(1).each do |child|
|
|
18
|
+
next unless child.is_a?(::Parser::AST::Node) && child.type == :when
|
|
19
|
+
|
|
20
|
+
cond_src = expression_source(child.children.first)
|
|
21
|
+
info[child.location.keyword.line] ||= "when #{cond_src}"
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
ast_node.children.each { |c| annotate(c, info) }
|
|
26
|
+
info
|
|
27
|
+
end
|
|
28
|
+
# rubocop:enable Metrics/MethodLength,Metrics/AbcSize
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def annotate_if_node(ast_node, info) # rubocop:disable Metrics/MethodLength,Metrics/AbcSize
|
|
33
|
+
loc = ast_node.location
|
|
34
|
+
cond_src = expression_source(ast_node.children.first)
|
|
35
|
+
case loc
|
|
36
|
+
when Parser::Source::Map::Condition
|
|
37
|
+
kw = loc.keyword
|
|
38
|
+
info[kw.line] ||= "#{kw.source} #{cond_src}"
|
|
39
|
+
info[loc.else.line] ||= 'else' if loc.else&.source == 'else'
|
|
40
|
+
when Parser::Source::Map::Ternary
|
|
41
|
+
info[loc.question.line] ||= "? #{cond_src}"
|
|
42
|
+
info[loc.colon.line] ||= ':'
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def expression_source(node)
|
|
47
|
+
node.location.expression.source
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
data/lib/undercover/cli.rb
CHANGED
|
@@ -5,12 +5,6 @@ require 'rainbow'
|
|
|
5
5
|
|
|
6
6
|
module Undercover
|
|
7
7
|
module CLI
|
|
8
|
-
WARNINGS_TO_S = {
|
|
9
|
-
stale_coverage: Rainbow('🚨 WARNING: Coverage data is older than your ' \
|
|
10
|
-
'latest changes and results might be incomplete. ' \
|
|
11
|
-
'Re-run tests to update').yellow,
|
|
12
|
-
no_changes: Rainbow('✅ No reportable changes').green
|
|
13
|
-
}.freeze
|
|
14
8
|
def self.run(args)
|
|
15
9
|
opts = Undercover::Options.new.parse(args)
|
|
16
10
|
syntax_version(opts.syntax_version)
|
|
@@ -32,7 +26,7 @@ module Undercover
|
|
|
32
26
|
|
|
33
27
|
changeset_obj = changeset(opts)
|
|
34
28
|
report = Undercover::Report.new(changeset_obj, opts, simplecov_adapter).build
|
|
35
|
-
handle_report_validation(report, coverage_path)
|
|
29
|
+
handle_report_validation(report, coverage_path, opts)
|
|
36
30
|
end
|
|
37
31
|
|
|
38
32
|
def self.handle_missing_coverage_path(opts)
|
|
@@ -48,16 +42,21 @@ module Undercover
|
|
|
48
42
|
1
|
|
49
43
|
end
|
|
50
44
|
|
|
51
|
-
def self.handle_report_validation(report, coverage_path)
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
45
|
+
def self.handle_report_validation(report, coverage_path, opts)
|
|
46
|
+
validation_error = report.validate(coverage_path)
|
|
47
|
+
flagged = validation_error ? [] : report.flagged_results
|
|
48
|
+
formatter = build_formatter(flagged, validation_error, opts)
|
|
49
|
+
|
|
50
|
+
puts formatter
|
|
51
|
+
formatter.exit_code
|
|
52
|
+
end
|
|
57
53
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
54
|
+
def self.build_formatter(flagged, validation_error, opts)
|
|
55
|
+
if opts.formatter == 'json'
|
|
56
|
+
Undercover::JsonFormatter.new(flagged, validation_error)
|
|
57
|
+
else
|
|
58
|
+
Undercover::Formatter.new(flagged, validation_error)
|
|
59
|
+
end
|
|
61
60
|
end
|
|
62
61
|
|
|
63
62
|
def self.syntax_version(version)
|
data/lib/undercover/formatter.rb
CHANGED
|
@@ -2,16 +2,33 @@
|
|
|
2
2
|
|
|
3
3
|
module Undercover
|
|
4
4
|
class Formatter
|
|
5
|
-
|
|
5
|
+
WARNINGS_TO_S = {
|
|
6
|
+
stale_coverage: Rainbow('🚨 WARNING: Coverage data is older than your ' \
|
|
7
|
+
'latest changes and results might be incomplete. ' \
|
|
8
|
+
'Re-run tests to update').yellow,
|
|
9
|
+
no_changes: Rainbow('✅ No reportable changes').green
|
|
10
|
+
}.freeze
|
|
11
|
+
|
|
12
|
+
def initialize(results, validation_error = nil)
|
|
6
13
|
@results = results
|
|
14
|
+
@validation_error = validation_error
|
|
7
15
|
end
|
|
8
16
|
|
|
9
17
|
def to_s
|
|
18
|
+
return WARNINGS_TO_S[@validation_error] if @validation_error
|
|
19
|
+
|
|
10
20
|
return success unless @results.any?
|
|
11
21
|
|
|
12
22
|
([warnings_header] + formatted_warnings).join("\n")
|
|
13
23
|
end
|
|
14
24
|
|
|
25
|
+
def exit_code
|
|
26
|
+
return 0 if @validation_error
|
|
27
|
+
return 0 unless @results.any?
|
|
28
|
+
|
|
29
|
+
1
|
|
30
|
+
end
|
|
31
|
+
|
|
15
32
|
private
|
|
16
33
|
|
|
17
34
|
def formatted_warnings
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'undercover/ast_branch_annotator'
|
|
5
|
+
|
|
6
|
+
module Undercover
|
|
7
|
+
class JsonFormatter
|
|
8
|
+
def initialize(results, validation_error = nil)
|
|
9
|
+
@results = results
|
|
10
|
+
@validation_error = validation_error
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def to_s
|
|
14
|
+
JSON.pretty_generate(to_h)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def to_h
|
|
18
|
+
output = {
|
|
19
|
+
warnings: warnings,
|
|
20
|
+
summary: summary
|
|
21
|
+
}
|
|
22
|
+
output[:validation] = @validation_error.to_s if @validation_error && @validation_error != :no_changes
|
|
23
|
+
output
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def exit_code
|
|
27
|
+
return 0 if @validation_error
|
|
28
|
+
return 0 unless @results.any?
|
|
29
|
+
|
|
30
|
+
1
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def warnings # rubocop:disable Metrics/MethodLength
|
|
36
|
+
@results.map do |result|
|
|
37
|
+
{
|
|
38
|
+
node: result.node.name,
|
|
39
|
+
type: result.node.human_name,
|
|
40
|
+
file: result.file_path,
|
|
41
|
+
first_line: result.first_line,
|
|
42
|
+
last_line: result.last_line,
|
|
43
|
+
coverage: result.coverage_f,
|
|
44
|
+
uncovered_lines: uncovered_lines(result),
|
|
45
|
+
uncovered_branches: uncovered_branches(result)
|
|
46
|
+
}
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def uncovered_lines(result)
|
|
51
|
+
result.coverage
|
|
52
|
+
.select { |cov| cov.size == 2 }
|
|
53
|
+
.reject { |ln, _| result.skipped?(result.file_path, ln) }
|
|
54
|
+
.select { |_, count| count.zero? }
|
|
55
|
+
.map { |ln, _| ln }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def uncovered_branches(result)
|
|
59
|
+
annotated_branches(result)
|
|
60
|
+
.reject { |b| result.skipped?(result.file_path, b[:line]) || b[:count] == 'ignored' }
|
|
61
|
+
.select { |b| b[:count].zero? }
|
|
62
|
+
.map { |b| b.except(:count) }
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def annotated_branches(result) # rubocop:disable Metrics/AbcSize
|
|
66
|
+
ast_info = AstBranchAnnotator.call(result.node.ast_node)
|
|
67
|
+
counts_per_line = result.branches.each_with_object(Hash.new(0)) { |b, h| h[b[:line]] += 1 }
|
|
68
|
+
result.branches.map do |entry|
|
|
69
|
+
arm = result.branch_label(result.file_path, entry[:branch]) if counts_per_line[entry[:line]] > 1
|
|
70
|
+
label = [ast_info[entry[:line]], arm].compact.join(' → ')
|
|
71
|
+
next entry if label.empty?
|
|
72
|
+
|
|
73
|
+
entry.merge(description: label)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def summary
|
|
78
|
+
{
|
|
79
|
+
total_warnings: @results.size,
|
|
80
|
+
files_affected: @results.map(&:file_path).uniq.size
|
|
81
|
+
}
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
require 'undercover/root_to_relative_paths'
|
|
4
4
|
|
|
5
5
|
module Undercover
|
|
6
|
-
LcovParseError
|
|
6
|
+
class LcovParseError < StandardError
|
|
7
|
+
end
|
|
7
8
|
|
|
8
9
|
class LcovParser
|
|
9
10
|
include RootToRelativePaths
|
|
@@ -57,6 +58,10 @@ module Undercover
|
|
|
57
58
|
false
|
|
58
59
|
end
|
|
59
60
|
|
|
61
|
+
def branch_label(_filepath, _branch_no)
|
|
62
|
+
nil
|
|
63
|
+
end
|
|
64
|
+
|
|
60
65
|
def ignored_files
|
|
61
66
|
# supported by SimplecovResultAdapter only
|
|
62
67
|
[]
|
data/lib/undercover/options.rb
CHANGED
|
@@ -34,6 +34,8 @@ module Undercover
|
|
|
34
34
|
DEFAULT_FILE_INCLUDE_GLOBS = %w[*.rb *.rake *.ru Rakefile].freeze
|
|
35
35
|
DEFAULT_FILE_EXCLUDE_GLOBS = %w[test/* spec/* db/* config/* *_test.rb *_spec.rb].freeze
|
|
36
36
|
|
|
37
|
+
FORMATS = %w[text json].freeze
|
|
38
|
+
|
|
37
39
|
attr_accessor :lcov,
|
|
38
40
|
:simplecov_resultset,
|
|
39
41
|
:path,
|
|
@@ -44,7 +46,8 @@ module Undercover
|
|
|
44
46
|
:file_scope,
|
|
45
47
|
:glob_allow_filters,
|
|
46
48
|
:glob_reject_filters,
|
|
47
|
-
:max_warnings_limit
|
|
49
|
+
:max_warnings_limit,
|
|
50
|
+
:formatter
|
|
48
51
|
|
|
49
52
|
def initialize
|
|
50
53
|
@run_mode = DIFF_TRIGGER_LINE
|
|
@@ -55,6 +58,7 @@ module Undercover
|
|
|
55
58
|
self.glob_allow_filters = DEFAULT_FILE_INCLUDE_GLOBS
|
|
56
59
|
self.glob_reject_filters = DEFAULT_FILE_EXCLUDE_GLOBS
|
|
57
60
|
self.max_warnings_limit = nil
|
|
61
|
+
self.formatter = 'text'
|
|
58
62
|
end
|
|
59
63
|
|
|
60
64
|
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
|
@@ -84,6 +88,7 @@ module Undercover
|
|
|
84
88
|
ruby_syntax_option(opts)
|
|
85
89
|
max_warnings_limit_option(opts)
|
|
86
90
|
file_filters(opts)
|
|
91
|
+
format_option(opts)
|
|
87
92
|
end.parse(args)
|
|
88
93
|
|
|
89
94
|
guess_resultset_path unless simplecov_resultset
|
|
@@ -197,5 +202,12 @@ module Undercover
|
|
|
197
202
|
self.glob_reject_filters = split_comma_separated_with_braces(comma_separated_globs)
|
|
198
203
|
end
|
|
199
204
|
end
|
|
205
|
+
|
|
206
|
+
def format_option(parser)
|
|
207
|
+
desc = "Output format: #{FORMATS.join(', ')} (default: text)"
|
|
208
|
+
parser.on('--format FORMAT', FORMATS, desc) do |format|
|
|
209
|
+
self.formatter = format
|
|
210
|
+
end
|
|
211
|
+
end
|
|
200
212
|
end
|
|
201
213
|
end
|
data/lib/undercover/result.rb
CHANGED
|
@@ -9,7 +9,7 @@ module Undercover
|
|
|
9
9
|
attr_reader :node, :coverage, :coverage_adapter, :file_path
|
|
10
10
|
|
|
11
11
|
def_delegators :node, :first_line, :last_line, :name
|
|
12
|
-
def_delegators :coverage_adapter, :skipped
|
|
12
|
+
def_delegators :coverage_adapter, :skipped?, :branch_label
|
|
13
13
|
|
|
14
14
|
def initialize(node, coverage_adapter, file_path) # rubocop:disable Metrics/MethodLength,Metrics/AbcSize
|
|
15
15
|
@node = node
|
|
@@ -108,7 +108,8 @@ module Undercover
|
|
|
108
108
|
Rainbow(' skipped with :nocov:').italic.darkgray.dark
|
|
109
109
|
elsif covered.nil?
|
|
110
110
|
Rainbow(formatted_line).darkgray.dark +
|
|
111
|
-
Rainbow(' hits: n/a').italic.darkgray.dark
|
|
111
|
+
Rainbow(' hits: n/a').italic.darkgray.dark +
|
|
112
|
+
count_covered_branches(num)
|
|
112
113
|
elsif covered.positive?
|
|
113
114
|
Rainbow(formatted_line).green +
|
|
114
115
|
Rainbow(" hits: #{covered}").italic.darkgray.dark +
|
|
@@ -122,6 +123,15 @@ module Undercover
|
|
|
122
123
|
end
|
|
123
124
|
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
|
|
124
125
|
|
|
126
|
+
# Returns raw branch coverage entries for this result as structured hashes.
|
|
127
|
+
# Callers that need human-readable descriptions should use AstBranchAnnotator
|
|
128
|
+
# separately (e.g. JsonFormatter).
|
|
129
|
+
def branches
|
|
130
|
+
coverage.select { |cov| cov.size == 4 }.map do |ln, block_no, branch_no, count|
|
|
131
|
+
{line: ln, block: block_no, branch: branch_no, count: count}
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
125
135
|
def file_path_with_lines
|
|
126
136
|
"#{file_path}:#{first_line}:#{last_line}"
|
|
127
137
|
end
|
|
@@ -52,6 +52,13 @@ module Undercover
|
|
|
52
52
|
source_file['lines'][line_no - 1] == 'ignored'
|
|
53
53
|
end
|
|
54
54
|
|
|
55
|
+
def branch_label(filepath, branch_no)
|
|
56
|
+
source_file = find_file(filepath)
|
|
57
|
+
return nil unless source_file
|
|
58
|
+
|
|
59
|
+
source_file['branches']&.dig(branch_no - 1, 'type')
|
|
60
|
+
end
|
|
61
|
+
|
|
55
62
|
# unused for now
|
|
56
63
|
def total_coverage; end
|
|
57
64
|
def total_branch_coverage; end
|
data/lib/undercover/version.rb
CHANGED
data/lib/undercover.rb
CHANGED
|
@@ -13,6 +13,7 @@ require 'undercover/result'
|
|
|
13
13
|
require 'undercover/cli'
|
|
14
14
|
require 'undercover/changeset'
|
|
15
15
|
require 'undercover/formatter'
|
|
16
|
+
require 'undercover/json_formatter'
|
|
16
17
|
require 'undercover/options'
|
|
17
18
|
require 'undercover/filter_set'
|
|
18
19
|
require 'undercover/simplecov_result_adapter'
|
data/undercover.gemspec
CHANGED
|
@@ -4,7 +4,7 @@ lib = File.expand_path('lib', __dir__)
|
|
|
4
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
5
5
|
require 'undercover/version'
|
|
6
6
|
|
|
7
|
-
Gem::Specification.new do |spec|
|
|
7
|
+
Gem::Specification.new do |spec| # rubocop:disable Metrics/BlockLength
|
|
8
8
|
spec.name = 'undercover'
|
|
9
9
|
spec.version = Undercover::VERSION
|
|
10
10
|
spec.authors = ['Jan Grodowski']
|
|
@@ -28,6 +28,7 @@ Gem::Specification.new do |spec|
|
|
|
28
28
|
spec.required_ruby_version = '>= 3.0.0'
|
|
29
29
|
|
|
30
30
|
spec.add_dependency 'base64'
|
|
31
|
+
spec.add_dependency 'benchmark'
|
|
31
32
|
spec.add_dependency 'bigdecimal'
|
|
32
33
|
spec.add_dependency 'imagen', '>= 0.2.0'
|
|
33
34
|
spec.add_dependency 'rainbow', '>= 2.1', '< 4.0'
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: undercover
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.8.
|
|
4
|
+
version: 0.8.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jan Grodowski
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: base64
|
|
@@ -23,6 +23,20 @@ dependencies:
|
|
|
23
23
|
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: '0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: benchmark
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0'
|
|
26
40
|
- !ruby/object:Gem::Dependency
|
|
27
41
|
name: bigdecimal
|
|
28
42
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -147,10 +161,12 @@ files:
|
|
|
147
161
|
- docs/screenshot_warnings.png
|
|
148
162
|
- docs/semaphore.yml
|
|
149
163
|
- lib/undercover.rb
|
|
164
|
+
- lib/undercover/ast_branch_annotator.rb
|
|
150
165
|
- lib/undercover/changeset.rb
|
|
151
166
|
- lib/undercover/cli.rb
|
|
152
167
|
- lib/undercover/filter_set.rb
|
|
153
168
|
- lib/undercover/formatter.rb
|
|
169
|
+
- lib/undercover/json_formatter.rb
|
|
154
170
|
- lib/undercover/lcov_parser.rb
|
|
155
171
|
- lib/undercover/options.rb
|
|
156
172
|
- lib/undercover/result.rb
|
|
@@ -178,7 +194,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
178
194
|
- !ruby/object:Gem::Version
|
|
179
195
|
version: '0'
|
|
180
196
|
requirements: []
|
|
181
|
-
rubygems_version:
|
|
197
|
+
rubygems_version: 4.0.3
|
|
182
198
|
specification_version: 4
|
|
183
199
|
summary: Actionable code coverage - detects untested code blocks in recent changes
|
|
184
200
|
test_files: []
|