undercover 0.7.4 → 0.8.0

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: fb439eb426932d432434081cf9e17cce9b19b90a85c71b6a4c173d20cceb9392
4
- data.tar.gz: a2703dd7d19a18f943dd3fdaebc34faf23cc995b1cc94b673756f1b5142cf046
3
+ metadata.gz: 8c8f43820b6be7cbae834f152ed56778f8fb3540e878a012ce0bb5274be7b0e3
4
+ data.tar.gz: '0798f165b6deef05078311ff04822bc4c1dc708e3830b569b5575be2d1f8a3fe'
5
5
  SHA512:
6
- metadata.gz: 034ff6bd2826c4826b06780bd874e28c04c8fbcb0fcde85a5c0a1dd8901e4f5d2ebfe6c149b828c0fcf28fd4064f14ef122f69c763f59170b62d99941281d44b
7
- data.tar.gz: 114c9a9873fb75c17a3bfe79612bfe8d2891cd2d2b2dd68f8253d49f3529def86bca607d4774d1f113ade2fd8eb2d6d5d55e53d4b47b4a5464c3a6075eb2da3b
6
+ metadata.gz: 922c43acac57c8e57df9c902b9739c4f084a090e6afc9b838868e80ce8c39ea5d5e087952c40e7446fceaa97313ee686aacfef1e8a62af8dd01879d665e5abda
7
+ data.tar.gz: 3d538e5526d124c9ffba53c53de75dd2e6009b0abf1de4a333ccd3e013da195b15811217d0e1a8b1b12bd7c34d47cac47c42dd6e4838cd82e3a571602c0e5966
data/.gitignore CHANGED
@@ -10,3 +10,6 @@ Gemfile.lock
10
10
 
11
11
  # rspec failure tracking
12
12
  .rspec_status
13
+
14
+ # branch ignore fixture coverage metadata
15
+ spec/fixtures/branch_ignored_coverage/.*
data/.rubocop.yml CHANGED
@@ -25,10 +25,10 @@ Metrics/BlockLength:
25
25
  - spec/**/*
26
26
 
27
27
  Metrics/CyclomaticComplexity:
28
- Max: 10
28
+ Max: 12
29
29
 
30
30
  Metrics/PerceivedComplexity:
31
- Max: 10
31
+ Max: 12
32
32
 
33
33
  Style/HashEachMethods:
34
34
  Enabled: true
data/CHANGELOG.md CHANGED
@@ -6,6 +6,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ # [0.8.0] - 2025-08-28
10
+
11
+ ### Added
12
+ - Filter out files ignored by simplecov using `add_filter` ([#234](https://github.com/grodowski/undercover/pull/234))
13
+ - Add LF and LH parsing to LcovParser for simplecov-lcov 0.9 compatibility
14
+
15
+ ### Fixed
16
+ - Fix an `Undercover::Result` edge case causing errors with ignored branches on uningnored lines
17
+
9
18
  # [0.7.4] - 2025-07-13
10
19
 
11
20
  ### Fixed
@@ -185,7 +194,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
185
194
  ### Added
186
195
  - First release of `undercover` 🎉
187
196
 
188
- [Unreleased]: https://github.com/grodowski/undercover/compare/v0.7.4...HEAD
197
+ [Unreleased]: https://github.com/grodowski/undercover/compare/v0.8.0...HEAD
198
+ [0.8.0]: https://github.com/grodowski/undercover/compare/v0.7.4...v0.8.0
189
199
  [0.7.4]: https://github.com/grodowski/undercover/compare/v0.7.3...v0.7.4
190
200
  [0.7.3]: https://github.com/grodowski/undercover/compare/v0.7.2...v0.7.3
191
201
  [0.7.2]: https://github.com/grodowski/undercover/compare/v0.7.1...v0.7.2
@@ -49,6 +49,10 @@ module Undercover
49
49
  :stale_coverage if last_modified > File.mtime(lcov_report_path)
50
50
  end
51
51
 
52
+ def filter_with(filter_set)
53
+ @filter_set = filter_set
54
+ end
55
+
52
56
  private
53
57
 
54
58
  # Diffs `head` or `head` + `compare_base` (if exists),
@@ -5,9 +5,6 @@ require 'rainbow'
5
5
 
6
6
  module Undercover
7
7
  module CLI
8
- # TODO: Report calls >parser< for each file instead of
9
- # traversing the whole project at first!
10
-
11
8
  WARNINGS_TO_S = {
12
9
  stale_coverage: Rainbow('🚨 WARNING: Coverage data is older than your ' \
13
10
  'latest changes and results might be incomplete. ' \
@@ -21,12 +18,20 @@ module Undercover
21
18
  run_report(opts)
22
19
  end
23
20
 
24
- def self.run_report(opts)
21
+ def self.run_report(opts) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
25
22
  coverage_path = opts.simplecov_resultset || opts.lcov
26
23
  return handle_missing_coverage_path(opts) if coverage_path.nil?
27
24
  return handle_missing_file(coverage_path) unless File.exist?(coverage_path)
28
25
 
29
- report = Undercover::Report.new(changeset(opts), opts).build
26
+ simplecov_adapter = if opts.simplecov_resultset
27
+ SimplecovResultAdapter.parse(File.open(opts.simplecov_resultset), opts)
28
+ else
29
+ # TODO: lcov will be deprecated end of 2025 and we'll be able to refactor harder
30
+ LcovParser.parse(File.open(opts.lcov), opts)
31
+ end
32
+
33
+ changeset_obj = changeset(opts)
34
+ report = Undercover::Report.new(changeset_obj, opts, simplecov_adapter).build
30
35
  handle_report_validation(report, coverage_path)
31
36
  end
32
37
 
@@ -63,8 +68,7 @@ module Undercover
63
68
 
64
69
  def self.changeset(opts)
65
70
  git_dir = File.join(opts.path, opts.git_dir)
66
- filter_set = Undercover::FilterSet.new(opts.glob_allow_filters, opts.glob_reject_filters)
67
- Undercover::Changeset.new(git_dir, opts.compare, filter_set)
71
+ Undercover::Changeset.new(git_dir, opts.compare)
68
72
  end
69
73
  end
70
74
  end
@@ -2,16 +2,36 @@
2
2
 
3
3
  module Undercover
4
4
  class FilterSet
5
- attr_reader :allow_filters, :reject_filters
5
+ attr_reader :allow_filters, :reject_filters, :simplecov_filters
6
6
 
7
- def initialize(allow_filters, reject_filters)
7
+ def initialize(allow_filters, reject_filters, simplecov_filters)
8
8
  @allow_filters = allow_filters || []
9
9
  @reject_filters = reject_filters || []
10
+ @simplecov_filters = simplecov_filters || []
10
11
  end
11
12
 
12
13
  def include?(filepath)
13
14
  fnmatch = proc { |glob| File.fnmatch(glob, filepath, File::FNM_EXTGLOB) }
15
+
16
+ # Check if file was ignored by SimpleCov filters
17
+ return false if ignored_by_simplecov?(filepath)
18
+
19
+ # Apply Undercover's own filters
14
20
  allow_filters.any?(fnmatch) && reject_filters.none?(fnmatch)
15
21
  end
22
+
23
+ private
24
+
25
+ def ignored_by_simplecov?(filepath)
26
+ simplecov_filters.any? do |filter|
27
+ if filter[:string]
28
+ filepath.include?(filter[:string])
29
+ elsif filter[:regex]
30
+ filepath.match?(Regexp.new(filter[:regex]))
31
+ elsif filter[:file]
32
+ filepath == filter[:file]
33
+ end
34
+ end
35
+ end
16
36
  end
17
37
  end
@@ -57,6 +57,11 @@ module Undercover
57
57
  false
58
58
  end
59
59
 
60
+ def ignored_files
61
+ # supported by SimplecovResultAdapter only
62
+ []
63
+ end
64
+
60
65
  private
61
66
 
62
67
  # rubocop:disable Metrics/MethodLength, Style/SpecialGlobalVars, Metrics/AbcSize
@@ -79,6 +84,7 @@ module Undercover
79
84
  source_files[@current_filename] << [line_no.to_i, block_no.to_i, branch_no.to_i, covered.to_i]
80
85
  when /^end_of_record$/, /^$/
81
86
  @current_filename = nil
87
+ when /^LF:(\d+)|LH:(\d+)/ # lines found, lines hit; no-op
82
88
  else
83
89
  raise LcovParseError, "could not recognise '#{line}' as valid LCOV"
84
90
  end
@@ -43,7 +43,7 @@ module Undercover
43
43
 
44
44
  # check branch coverage for line_no
45
45
  coverage.each do |ln, _block, _branch, cov|
46
- return true if ln == line_no && cov && cov.zero?
46
+ return true if ln == line_no && cov && cov != 'ignored' && cov.zero?
47
47
  end
48
48
 
49
49
  # check line coverage for line_no
@@ -13,12 +13,60 @@ module SimpleCovJSONFormatter
13
13
  end
14
14
  end
15
15
 
16
+ module SimpleCov
17
+ class << self
18
+ attr_accessor :filter_definitions
19
+
20
+ alias filtered_uncached filtered
21
+
22
+ def filtered(files)
23
+ @filter_definitions ||= extract_filter_definitions
24
+ original_files = files.dup
25
+ filtered_uncached(files).tap do |filtered_files|
26
+ filtered_file_paths = (original_files.map(&:filename) - filtered_files.map(&:filename))
27
+ filtered_file_paths.each do |file|
28
+ relative_path = file.delete_prefix("#{SimpleCov.root}/")
29
+ @filter_definitions << {file: relative_path} unless covered_by_serializable_filters?(relative_path)
30
+ end
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def extract_filter_definitions
37
+ filter_array = []
38
+
39
+ filters.each do |filter|
40
+ case filter
41
+ when SimpleCov::StringFilter
42
+ filter_array << {string: filter.filter_argument}
43
+ when SimpleCov::RegexFilter
44
+ filter_array << {regex: filter.filter_argument.source}
45
+ end
46
+ end
47
+
48
+ filter_array
49
+ end
50
+
51
+ def covered_by_serializable_filters?(relative_path)
52
+ @filter_definitions.any? do |filter_def|
53
+ if filter_def[:string]
54
+ relative_path.include?(filter_def[:string])
55
+ elsif filter_def[:regex]
56
+ relative_path.match?(Regexp.new(filter_def[:regex]))
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+
16
63
  module Undercover
17
64
  class ResultHashFormatterWithRoot < SimpleCovJSONFormatter::ResultHashFormatter
18
65
  def format
19
66
  formatted_result[:meta] = {timestamp: @result.created_at.to_i}
20
67
  format_files
21
68
  add_undercover_meta_fields
69
+ add_ignored_files
22
70
  formatted_result
23
71
  end
24
72
 
@@ -30,6 +78,10 @@ module Undercover
30
78
  end
31
79
  end
32
80
 
81
+ def add_ignored_files
82
+ formatted_result[:meta][:ignored_files] = SimpleCov.filter_definitions || []
83
+ end
84
+
33
85
  # format_files uses relative path as keys, as opposed to the superclass method
34
86
  def format_files
35
87
  formatted_result[:coverage] ||= {}
@@ -56,6 +56,10 @@ module Undercover
56
56
  def total_coverage; end
57
57
  def total_branch_coverage; end
58
58
 
59
+ def ignored_files
60
+ @ignored_files ||= simplecov_result.dig('meta', 'ignored_files') || []
61
+ end
62
+
59
63
  private
60
64
 
61
65
  def find_file(filepath)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Undercover
4
- VERSION = '0.7.4'
4
+ VERSION = '0.8.0'
5
5
  end
data/lib/undercover.rb CHANGED
@@ -21,11 +21,12 @@ require 'undercover/version'
21
21
  module Undercover
22
22
  class Report
23
23
  extend Forwardable
24
+
24
25
  def_delegators :changeset, :validate
25
26
 
26
27
  attr_reader :changeset,
27
28
  :lcov,
28
- :simplecov_resultset,
29
+ :coverage_adapter,
29
30
  :results,
30
31
  :code_dir,
31
32
  :filter_set,
@@ -35,15 +36,16 @@ module Undercover
35
36
  #
36
37
  # @param changeset [Undercover::Changeset]
37
38
  # @param opts [Undercover::Options]
38
- def initialize(changeset, opts)
39
- if opts.simplecov_resultset
40
- @simplecov_resultset = SimplecovResultAdapter.parse(File.open(opts.simplecov_resultset), opts)
41
- end
42
- @lcov = LcovParser.parse(File.open(opts.lcov), opts) if opts.lcov
39
+ # @param coverage_adapter [Undercover::SimplecovResultAdapter|Undercover::LcovParser] pre-parsed coverage adapter
40
+ def initialize(changeset, opts, coverage_adapter)
41
+ @coverage_adapter = coverage_adapter
43
42
 
44
43
  @code_dir = opts.path
45
44
  @changeset = changeset
46
- @filter_set = FilterSet.new(opts.glob_allow_filters, opts.glob_reject_filters)
45
+
46
+ ignored_files = coverage_adapter.ignored_files || []
47
+ @filter_set = FilterSet.new(opts.glob_allow_filters, opts.glob_reject_filters, ignored_files)
48
+ changeset.filter_with(filter_set)
47
49
  @max_warnings_limit = opts.max_warnings_limit
48
50
  @loaded_files = {}
49
51
  @results = {}
@@ -106,29 +108,21 @@ module Undercover
106
108
 
107
109
  attr_reader :loaded_files
108
110
 
109
- # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
111
+ # rubocop:disable Metrics/AbcSize
110
112
  def load_and_parse_file(filepath)
111
113
  key = filepath.gsub(/^\.\//, '')
112
114
  return if loaded_files[key]
113
- return unless include_file?(filepath)
114
115
 
115
116
  root_ast = Imagen::Node::Root.new.build_from_file(
116
117
  File.join(code_dir, filepath)
117
118
  )
118
119
  return if root_ast.children.empty?
119
120
 
120
- # lcov will be deprecated at some point and we'll be able to refactor harder
121
- coverage = simplecov_resultset || lcov
122
-
123
121
  loaded_files[key] = []
124
122
  root_ast.find_all(->(node) { !node.is_a?(Imagen::Node::Root) }).each do |imagen_node|
125
- loaded_files[key] << Result.new(imagen_node, coverage, filepath)
123
+ loaded_files[key] << Result.new(imagen_node, coverage_adapter, filepath)
126
124
  end
127
125
  end
128
- # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
129
-
130
- def include_file?(filepath)
131
- filter_set.include?(filepath)
132
- end
126
+ # rubocop:enable Metrics/AbcSize
133
127
  end
134
128
  end
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.7.4
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jan Grodowski
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-07-13 00:00:00.000000000 Z
10
+ date: 2025-08-28 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: base64