undercover 0.5.0 → 0.6.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: ba2a988ce8b2ef9451615675538b268f4d663cb141288a646b2604785ea92dc6
4
- data.tar.gz: 6822d32d0b2000e711194c0d595b2f69dcaa3020740db9613be8f6fceea1e4e9
3
+ metadata.gz: c860e600589d92698fa5df94b1b8fe9283cbb00cdc595410b9c3a413d74b3095
4
+ data.tar.gz: e2c26b709c691201f473846e0cb6a7fa6844c5d1dc73169ca533d95482182f6c
5
5
  SHA512:
6
- metadata.gz: 8ada6a585eee4ff30f9a520dde96f0b3b74bd11ab05a9e7857b356da7c00519e81d36dda12ddd4c7afde81b23cb73bc1c88a35f2f559f1cf9a638c42eab48052
7
- data.tar.gz: 6c846695987c6126e7db5c7c363e44756f57aaf34374157bd35c0e723469f705a625244e90818a39b07e5626df8b34f248796405f95a5c9ed884e4760af304f3
6
+ metadata.gz: ace70eb4f75d2d57bda9d906925a7b5525c5a67520380ba0327a5344ed6718b79ea2587a5a0fb4d2a09d430d09b30eb2d1ac63c503be17197ff9c6455ab12b00
7
+ data.tar.gz: a2ec9a3b4d27cb9eaa7b4595f71ff9b12108e939864a6f5279aece22cd728e358dd50b16e63d0537780154e3ffd20e0712bce8322a8591e4f08f70d9d6818145
@@ -14,3 +14,7 @@ updates:
14
14
  - 1.12.1
15
15
  - 1.8.1
16
16
  - 1.9.0
17
+ - package-ecosystem: "github-actions"
18
+ directory: "/"
19
+ schedule:
20
+ interval: "weekly"
@@ -7,7 +7,7 @@ jobs:
7
7
  matrix:
8
8
  ruby: ['3.3', '3.0']
9
9
  steps:
10
- - uses: actions/checkout@v3
10
+ - uses: actions/checkout@v4
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,20 +23,20 @@ jobs:
23
23
  run: |
24
24
  git fetch --update-head-ok origin master:master
25
25
  undercover --compare master
26
- - uses: actions/upload-artifact@v3
26
+ - uses: actions/upload-artifact@v4
27
27
  with:
28
- name: undercover.lcov
28
+ name: undercover-${{ matrix.ruby }}.lcov
29
29
  path: coverage/lcov/undercover.lcov
30
30
  coverage:
31
31
  runs-on: ubuntu-latest
32
32
  needs: build
33
33
  steps:
34
- - uses: actions/download-artifact@v3
34
+ - uses: actions/download-artifact@v4
35
35
  with:
36
- name: undercover.lcov
36
+ name: undercover-3.3.lcov
37
37
  - name: Upload coverage
38
38
  run: |
39
39
  ruby -e "$(curl -s https://undercover-ci.com/uploader.rb)" -- \
40
40
  --repo grodowski/undercover \
41
- --commit $GITHUB_SHA \
41
+ --commit ${{ github.event.pull_request.head.sha || github.sha }} \
42
42
  --lcov /home/runner/work/undercover/undercover/undercover.lcov
data/.rubocop.yml CHANGED
@@ -24,6 +24,12 @@ Metrics/BlockLength:
24
24
  Exclude:
25
25
  - spec/**/*
26
26
 
27
+ Metrics/CyclomaticComplexity:
28
+ Max: 10
29
+
30
+ Metrics/PerceivedComplexity:
31
+ Max: 10
32
+
27
33
  Style/HashEachMethods:
28
34
  Enabled: true
29
35
 
data/.tool-versions CHANGED
@@ -1 +1 @@
1
- ruby 3.3.0
1
+ ruby 3.3.3
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.6.0] - 2024-12-12
10
+ ### Added
11
+ - Add support for including and exluding files by glob patterns, supplied through CLI args and the configuration file (#146)
12
+
13
+ ### Fixed
14
+ - Files that were changed but don't appear in the coverage report at all will now be reported as uncovered, as expected.
15
+ - Fixed an issue where top-level methods were not being considered [#135](https://github.com/grodowski/undercover/issues/135). This was caused by a bug in the tree traversal logic.
16
+ - Fixed a bug where `--compare` didn't work with grafted commits as there was no merge base available ([#175](https://github.com/grodowski/undercover/issues/175)). Now it's possible to pass a graft commit as `--compare` which enables `undercover` to work with shallow clones.
17
+
9
18
  # [0.5.0] - 2024-01-09
10
19
  ### Changed
11
20
  - Drop ruby 2.x support, require ruby 3.x in gemspec
@@ -128,7 +137,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
128
137
  ### Added
129
138
  - First release of `undercover` 🎉
130
139
 
131
- [Unreleased]: https://github.com/grodowski/undercover/compare/v0.5.0...HEAD
140
+ [Unreleased]: https://github.com/grodowski/undercover/compare/v0.6.0...HEAD
141
+ [0.6.0]: https://github.com/grodowski/undercover/compare/v0.6.0...v0.5.0
132
142
  [0.5.0]: https://github.com/grodowski/undercover/compare/v0.4.7...v0.5.0
133
143
  [0.4.7]: https://github.com/grodowski/undercover/compare/v0.4.6...v0.4.7
134
144
  [0.4.6]: https://github.com/grodowski/undercover/compare/v0.4.5...v0.4.6
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  ![logo](https://github.com/grodowski/undercover/assets/4991698/c4bf038b-4472-4406-8f1f-5ddc812908d6)
2
2
 
3
- `undercover` warns about methods, classes and blocks that **were changed without tests**, to help you easily find untested code and reduce the number of bugs. It does so by analysing data from git diffs, code structure and SimpleCov coverage reports.
3
+ `undercover` warns about methods, classes and blocks that **were changed without tests**, to help you easily find untested code and reduce the number of bugs. It does so by analysing data from git diffs, code structure and SimpleCov coverage reports.
4
4
 
5
5
  Works with any Ruby CI pipeline as well as locally as a CLI.
6
6
 
@@ -104,12 +104,14 @@ A few options exist to provide automated comments from `undercover` in Pull Requ
104
104
  Options can be passed when running the command from the command line:
105
105
 
106
106
  ```sh
107
- undercover -h
108
107
  Usage: undercover [options]
109
108
  -l, --lcov path LCOV report file path
110
109
  -p, --path path Project directory
111
110
  -g, --git-dir dir Override `.git` with a custom directory
112
111
  -c, --compare ref Generate coverage warnings for all changes after `ref`
112
+ -r, --ruby-syntax ver Ruby syntax version, one of: current, ruby18, ruby19, ruby20, ruby21, ruby22, ruby23, ruby24, ruby25, ruby26, ruby30, ruby31, ruby32, ruby33
113
+ -f, --include-files globs Include files matching specified glob patterns (comma separated). Defaults to '*.rb,*.rake,*.ru,Rakefile'
114
+ -x, --exclude-files globs Skip files matching specified glob patterns (comma separated). Empty by default.
113
115
  -h, --help Prints this help
114
116
  --version Show version
115
117
  ```
@@ -74,7 +74,9 @@ module Undercover
74
74
  def compare_base_obj
75
75
  return nil unless compare_base
76
76
 
77
- repo.lookup(repo.merge_base(compare_base.to_s, head))
77
+ merge_base = repo.merge_base(compare_base.to_s, head)
78
+ # merge_base may be nil with --depth 1, compare two refs directly
79
+ merge_base ? repo.lookup(merge_base) : repo.rev_parse(compare_base)
78
80
  end
79
81
 
80
82
  def head
@@ -4,7 +4,7 @@ require 'optparse'
4
4
  require 'pathname'
5
5
 
6
6
  module Undercover
7
- class Options
7
+ class Options # rubocop:disable Metrics/ClassLength
8
8
  RUN_MODE = [
9
9
  RUN_MODE_DIFF_STRICT = :diff_strict, # warn for changed lines
10
10
  # RUN_MODE_DIFF_FILES = :diff_files, # warn for changed whole files
@@ -17,7 +17,15 @@ module Undercover
17
17
  # OUTPUT_CIRCLEMATOR = :circlemator # posts warnings as review comments
18
18
  ].freeze
19
19
 
20
- attr_accessor :lcov, :path, :git_dir, :compare, :syntax_version
20
+ DEFAULT_FILE_INCLUDE_GLOBS = %w[*.rb *.rake *.ru Rakefile].freeze
21
+
22
+ attr_accessor :lcov,
23
+ :path,
24
+ :git_dir,
25
+ :compare,
26
+ :syntax_version,
27
+ :glob_allow_filters,
28
+ :glob_reject_filters
21
29
 
22
30
  def initialize
23
31
  # TODO: use run modes
@@ -27,6 +35,8 @@ module Undercover
27
35
  # set defaults
28
36
  self.path = '.'
29
37
  self.git_dir = '.git'
38
+ self.glob_allow_filters = DEFAULT_FILE_INCLUDE_GLOBS
39
+ self.glob_reject_filters = []
30
40
  end
31
41
 
32
42
  # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
@@ -51,9 +61,7 @@ module Undercover
51
61
  git_dir_option(opts)
52
62
  compare_option(opts)
53
63
  ruby_syntax_option(opts)
54
- # TODO: parse dem other options and assign to self
55
- # --quiet (skip progress bar)
56
- # --exit-status (do not print report, just exit)
64
+ file_filters(opts)
57
65
  end.parse(args)
58
66
 
59
67
  guess_lcov_path unless lcov
@@ -119,5 +127,18 @@ module Undercover
119
127
  cwd = Pathname.new(File.expand_path(path))
120
128
  self.lcov = File.join(cwd, 'coverage', 'lcov', "#{cwd.split.last}.lcov")
121
129
  end
130
+
131
+ def file_filters(parser)
132
+ desc = 'Include files matching specified glob patterns (comma separated). ' \
133
+ "Defaults to '#{DEFAULT_FILE_INCLUDE_GLOBS.join(',')}'"
134
+ parser.on('-f', '--include-files globs', desc) do |comma_separated_globs|
135
+ self.glob_allow_filters = comma_separated_globs.strip.split(',')
136
+ end
137
+
138
+ desc = 'Skip files matching specified glob patterns (comma separated). Empty by default.'
139
+ parser.on('-x', '--exclude-files globs', desc) do |comma_separated_globs|
140
+ self.glob_reject_filters = comma_separated_globs.strip.split(',')
141
+ end
142
+ end
122
143
  end
123
144
  end
@@ -13,7 +13,7 @@ module Undercover
13
13
  def initialize(node, file_cov, file_path)
14
14
  @node = node
15
15
  @coverage = file_cov.select do |ln, _|
16
- first_line == last_line ? ln == first_line : ln > first_line && ln < last_line
16
+ (node.empty_def? ? ln >= first_line : ln > first_line) && ln < last_line
17
17
  end
18
18
  @file_path = file_path
19
19
  @flagged = false
@@ -27,8 +27,9 @@ module Undercover
27
27
  @flagged
28
28
  end
29
29
 
30
- # rubocop:disable Metrics/CyclomaticComplexity
31
30
  def uncovered?(line_no)
31
+ return true if coverage.empty?
32
+
32
33
  # check branch coverage for line_no
33
34
  coverage.each do |ln, _block, _branch, cov|
34
35
  return true if ln == line_no && cov && cov.zero?
@@ -38,13 +39,14 @@ module Undercover
38
39
  line_cov = coverage.select { |cov| cov.size == 2 }.find { |ln, _cov| ln == line_no }
39
40
  line_cov && line_cov[1].zero?
40
41
  end
41
- # rubocop:enable Metrics/CyclomaticComplexity
42
42
 
43
43
  # Method `coverage_f` returns the total coverage of this Undercover::Result
44
44
  # as a % value, taking into account missing branches. Line coverage will be counted
45
45
  # as 0 if any branch is untested.
46
46
  # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
47
47
  def coverage_f
48
+ return 0.0 if coverage.empty?
49
+
48
50
  lines = {}
49
51
  coverage.each do |ln, block_or_line_cov, _, branch_cov|
50
52
  lines[ln] = 1 unless lines.key?(ln)
@@ -54,7 +56,6 @@ module Undercover
54
56
  lines[ln] = 0
55
57
  end
56
58
  end
57
- return 1.0 if lines.keys.empty?
58
59
 
59
60
  (lines.values.sum.to_f / lines.keys.size).round(4)
60
61
  end
@@ -87,14 +88,14 @@ module Undercover
87
88
  if line.strip.empty?
88
89
  Rainbow(formatted_line).darkgray.dark
89
90
  elsif covered.nil?
90
- Rainbow(formatted_line).darkgray.dark + \
91
+ Rainbow(formatted_line).darkgray.dark +
91
92
  Rainbow(' hits: n/a').italic.darkgray.dark
92
93
  elsif covered.positive?
93
- Rainbow(formatted_line).green + \
94
+ Rainbow(formatted_line).green +
94
95
  Rainbow(" hits: #{covered}").italic.darkgray.dark + \
95
96
  count_covered_branches(num)
96
97
  elsif covered.zero?
97
- Rainbow(formatted_line).red + \
98
+ Rainbow(formatted_line).red +
98
99
  Rainbow(" hits: #{covered}").italic.darkgray.dark + \
99
100
  count_covered_branches(num)
100
101
  end
@@ -122,7 +123,7 @@ module Undercover
122
123
  return '' if branches.empty?
123
124
 
124
125
  if count_covered < branches.size
125
- Rainbow(' branches: ').italic.darkgray.dark + \
126
+ Rainbow(' branches: ').italic.darkgray.dark +
126
127
  Rainbow("#{count_covered}/#{branches.size}").italic.red
127
128
  else
128
129
  Rainbow(" branches: #{count_covered}/#{branches.size}").italic.darkgray.dark
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Undercover
4
- VERSION = '0.5.0'
4
+ VERSION = '0.6.0'
5
5
  end
data/lib/undercover.rb CHANGED
@@ -22,7 +22,8 @@ module Undercover
22
22
  attr_reader :changeset,
23
23
  :lcov,
24
24
  :results,
25
- :code_dir
25
+ :code_dir,
26
+ :glob_filters
26
27
 
27
28
  # Initializes a new Undercover::Report
28
29
  #
@@ -32,11 +33,15 @@ module Undercover
32
33
  @lcov = LcovParser.parse(File.open(opts.lcov))
33
34
  @code_dir = opts.path
34
35
  @changeset = changeset.update
36
+ @glob_filters = {
37
+ allow: opts.glob_allow_filters,
38
+ reject: opts.glob_reject_filters
39
+ }
35
40
  @loaded_files = {}
36
41
  @results = {}
37
42
  end
38
43
 
39
- # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity
44
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
40
45
  def build
41
46
  changeset.each_changed_line do |filepath, line_no|
42
47
  dist_from_line_no = lambda do |res|
@@ -50,6 +55,7 @@ module Undercover
50
55
  dist_from_line_no_sorter = lambda do |res1, res2|
51
56
  dist_from_line_no[res1] <=> dist_from_line_no[res2]
52
57
  end
58
+
53
59
  load_and_parse_file(filepath)
54
60
 
55
61
  next unless loaded_files[filepath]
@@ -61,7 +67,7 @@ module Undercover
61
67
  end
62
68
  self
63
69
  end
64
- # rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity
70
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
65
71
 
66
72
  def build_warnings
67
73
  warn('Undercover::Report#build_warnings is deprecated! ' \
@@ -92,7 +98,8 @@ module Undercover
92
98
  return if loaded_files[key]
93
99
 
94
100
  coverage = lcov.coverage(filepath)
95
- return if coverage.empty?
101
+
102
+ return unless include_file?(filepath)
96
103
 
97
104
  root_ast = Imagen::Node::Root.new.build_from_file(
98
105
  File.join(code_dir, filepath)
@@ -106,5 +113,10 @@ module Undercover
106
113
  end
107
114
  end
108
115
  # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
116
+
117
+ def include_file?(filepath)
118
+ fnmatch = proc { |glob| File.fnmatch(glob, filepath) }
119
+ glob_filters[:allow].any?(fnmatch) && glob_filters[:reject].none?(fnmatch)
120
+ end
109
121
  end
110
122
  end
data/undercover.gemspec CHANGED
@@ -28,7 +28,7 @@ Gem::Specification.new do |spec|
28
28
  spec.required_ruby_version = '>= 3.0.0'
29
29
 
30
30
  spec.add_dependency 'bigdecimal'
31
- spec.add_dependency 'imagen', '>= 0.1.8'
31
+ spec.add_dependency 'imagen', '>= 0.2.0'
32
32
  spec.add_dependency 'rainbow', '>= 2.1', '< 4.0'
33
33
  spec.add_dependency 'rugged', '>= 0.27', '< 1.8'
34
34
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: undercover
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jan Grodowski
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-01-09 00:00:00.000000000 Z
11
+ date: 2024-12-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bigdecimal
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: 0.1.8
33
+ version: 0.2.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: 0.1.8
40
+ version: 0.2.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rainbow
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -136,7 +136,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
136
136
  - !ruby/object:Gem::Version
137
137
  version: '0'
138
138
  requirements: []
139
- rubygems_version: 3.5.4
139
+ rubygems_version: 3.5.11
140
140
  signing_key:
141
141
  specification_version: 4
142
142
  summary: Actionable code coverage - detects untested code blocks in recent changes