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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8144aeeac145c88e63c5f57ea0d31c17648b8ea428d11755b15b4bb706fa2982
4
- data.tar.gz: 769fbd667e3bd48572829c4214f17afcd23cafaf1e7795452a2832b8105b15e0
3
+ metadata.gz: 8d93f862aad4a70d2370f9da2021a4b32c284084f4a56bc2a04da6f409d2b777
4
+ data.tar.gz: 1cc3a1f5e26aa234019c3ae76df456534a7047dfbb1421175db56ab869d4150f
5
5
  SHA512:
6
- metadata.gz: af0adac825397c7c7be37a1e4d1d0a1cc039c4b0ab3a774460d4352fbf8157f4b485b3a1f4b11621d3e2ea0368a251e43ae345ca3082f3baebd5f0d5dfe36fa5
7
- data.tar.gz: a3ba29ca707eeac980a4d1eab2f17ec17bc206f33ef57e599bd5cb0cffa76d7aaae7934d29b2a6453942abae90f8210bc5d83e23c604349c70bec8dc45fe212a
6
+ metadata.gz: ae42ba519cd6bd3435fee10c67c20907690036a4bccdd425c2cde2b4276a2bbd33ffd9d8d091b73c6d760fdf7e4bca52e400d71c85f21440f2d2c4ad2fb53d86
7
+ data.tar.gz: 28c65742716800a1a042b513ebe4bf14f254d46d22ef8959406d49f682132525fff4e19a21a7e7418562485cda79f9cb167bfb11877763b0b17e9645a09e116b
@@ -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: ['3.4', '3.0']
8
+ ruby: ['4.0', '3.4']
9
9
  steps:
10
- - uses: actions/checkout@v5
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@v5
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@v6
34
+ - uses: actions/download-artifact@v8
35
35
  with:
36
- name: undercover-3.4-coverage
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 3.4.2
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.3...HEAD
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
@@ -9,7 +9,7 @@ gem 'bundler'
9
9
  gem 'pry'
10
10
  gem 'rake', '~> 13.0'
11
11
  gem 'rspec', '~> 3.0'
12
- gem 'rubocop'
12
+ gem 'rubocop', '~> 1.84.0'
13
13
  gem 'ruby-lsp-rspec', require: false
14
14
  gem 'simplecov'
15
15
  gem 'simplecov-html'
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
@@ -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
- error = report.validate(coverage_path)
53
- if error
54
- puts(WARNINGS_TO_S[error])
55
- return 0 if error == :no_changes
56
- end
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
- flagged = report.flagged_results
59
- puts Undercover::Formatter.new(flagged)
60
- flagged.any? ? 1 : 0
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)
@@ -2,16 +2,33 @@
2
2
 
3
3
  module Undercover
4
4
  class Formatter
5
- def initialize(results)
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 = Class.new(StandardError)
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
  []
@@ -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
@@ -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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Undercover
4
- VERSION = '0.8.3'
4
+ VERSION = '0.8.5'
5
5
  end
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.3
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: 2025-11-20 00:00:00.000000000 Z
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: 3.6.2
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: []