undercover 0.3.4 → 0.4.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 876068f41c32798bbc3e6d2e37980021c5afebf2d7316700671741b64e4959ae
4
- data.tar.gz: 55fea89d18b7e6feeac2821f97a008999c2442aac83b53239c353fb05d6875f2
3
+ metadata.gz: f6cd3608abfe38ef691799586ac21e0818846ea2b67e7867af9c917762c28de7
4
+ data.tar.gz: 434a7428414fe21d31e5cf691255f39f5abdc9a0e370f39a362d7ac6f90ae7a4
5
5
  SHA512:
6
- metadata.gz: 1210f2973aee4c7bea1484c9eef36dce6bf8582eec0f9f95a94585bdc03cce9df7e5f8d750f33d2838f0329fd466e636fba7f05d2080c998717048a09520172f
7
- data.tar.gz: 0cc9be1fa5e3fe60009b71a9b2212c24f2f3c7f20a8df674821dcbd2c2ca5e1157dc04ece674b221d8bbbcaab3f4cfb6084846241474d664e98b644484e77e08
6
+ metadata.gz: 18574633620761accff515e582f79949c7bd4deaae9f5e44dfdba03cddfeed39c76d271214be0bbccf6c51da1c0231bd290693679cfd182753b14102bc4dac0b
7
+ data.tar.gz: 05f18a34ffa46f66a4cb73c9143c0851e295ad60129e7969d2e72a6350af5a874713201797f75ff8bbcaebfe27918a322b75e3b5eb9508eb20140ca5ad82d0bf
@@ -0,0 +1,16 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: bundler
4
+ directory: "/"
5
+ schedule:
6
+ interval: daily
7
+ time: "04:00"
8
+ open-pull-requests-limit: 10
9
+ ignore:
10
+ - dependency-name: rubocop
11
+ versions:
12
+ - 1.10.0
13
+ - 1.12.0
14
+ - 1.12.1
15
+ - 1.8.1
16
+ - 1.9.0
@@ -5,7 +5,7 @@ jobs:
5
5
  runs-on: ubuntu-latest
6
6
  strategy:
7
7
  matrix:
8
- ruby: ['2.7.x', '2.6.x', '2.5.x']
8
+ ruby: ['3.0.x', '2.7.x', '2.6.x', '2.5.x']
9
9
  steps:
10
10
  - uses: actions/checkout@v1
11
11
  - name: Set up Ruby ${{ matrix.ruby }}
data/.rubocop.yml CHANGED
@@ -1,5 +1,6 @@
1
1
  AllCops:
2
- TargetRubyVersion: 2.7.0
2
+ NewCops: enable
3
+ TargetRubyVersion: 2.5.0
3
4
  Exclude:
4
5
  - spec/fixtures/**/*
5
6
 
data/CHANGELOG.md CHANGED
@@ -6,6 +6,27 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ # [0.4.4] - 2021-11-29
10
+ ### Changed
11
+ - Dependency updates
12
+
13
+ # [0.4.3] - 2021-03-16
14
+ ### Fixed
15
+ - Branch coverage without line coverage marked as uncovered - fix by @GCorbel
16
+
17
+ # [0.4.1] - 2021-03-11
18
+ ### Fixed
19
+ - Fix zero-division edge case resulting in NaN from Result#coverage_f
20
+
21
+ # [0.4.0] - 2021-02-06
22
+ ### Added
23
+ - [Minimal implementation of branch coverage in LCOV parser](https://github.com/grodowski/undercover/pull/112) by [@magneland](https://github.com/magneland)
24
+ - Branch coverage output support in Undercover::Formatter
25
+ ### Changed
26
+ - Min Ruby requirement bumped to 2.5.0
27
+ - Dependency updates: Rubocop 1.0 and Rugged 1.1.0
28
+
29
+
9
30
  ## [0.3.4] - 2020-04-05
10
31
  ### Changed
11
32
  - Updated parsing performance by scoping `all_results` to git diff
@@ -86,7 +107,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
86
107
  ### Added
87
108
  - First release of `undercover` 🎉
88
109
 
89
- [Unreleased]: https://github.com/grodowski/undercover/compare/v0.3.4...HEAD
110
+ [Unreleased]: https://github.com/grodowski/undercover/compare/v0.4.4...HEAD
111
+ [0.4.3]: https://github.com/grodowski/undercover/compare/v0.4.3...v0.4.4
112
+ [0.4.3]: https://github.com/grodowski/undercover/compare/v0.4.1...v0.4.3
113
+ [0.4.1]: https://github.com/grodowski/undercover/compare/v0.4.0...v0.4.1
114
+ [0.4.0]: https://github.com/grodowski/undercover/compare/v0.3.4...v0.4.0
90
115
  [0.3.4]: https://github.com/grodowski/undercover/compare/v0.3.3...v0.3.4
91
116
  [0.3.3]: https://github.com/grodowski/undercover/compare/v0.3.2...v0.3.3
92
117
  [0.3.2]: https://github.com/grodowski/undercover/compare/v0.3.1...v0.3.2
data/README.md CHANGED
@@ -1,8 +1,9 @@
1
1
  # undercover 👮‍♂️
2
2
 
3
- **Like RuboCop but for code coverage**
3
+ **Like RuboCop but for code coverage**. Inspects files in a git diff and warns on changed methods, classes and blocks which need to be tested. Use it locally or as part of an automated build to shorten your code coverage feedback loop!
4
4
 
5
- **Inspects files in a git diff and warns on methods, classes and blocks which need test coverage.** Use it locally or as part of an automated build to shorten your code coverage feedback loop!
5
+ - Visit **[https://undercover-ci.com](https://undercover-ci.com)** to set up actionable GitHub code review checks, or use one of the [integrations](#code-review-integrations)
6
+ - Learn how to find untested code changes locally with the [CLI](#usage)
6
7
 
7
8
  A sample output of `undercover` ran before a commit may look like this:
8
9
 
@@ -14,7 +15,6 @@ And like this, given that specs were added:
14
15
 
15
16
  [![Build Status](https://action-badges.now.sh/grodowski/undercover)](https://github.com/grodowski/undercover/actions)
16
17
  [![Maintainability](https://api.codeclimate.com/v1/badges/b403feed68a18c072ec5/maintainability)](https://codeclimate.com/github/grodowski/undercover/maintainability)
17
- [![codebeat badge](https://codebeat.co/badges/be548247-2421-4448-bdab-896d13eb02e9)](https://codebeat.co/projects/github-com-grodowski-undercover-master)
18
18
 
19
19
  ## Installation
20
20
 
@@ -51,6 +51,7 @@ SimpleCov.formatter = SimpleCov::Formatter::LcovFormatter
51
51
  SimpleCov.start do
52
52
  add_filter(/^\/spec\//) # For RSpec
53
53
  add_filter(/^\/test\//) # For Minitest
54
+ enable_coverage(:branch) # Report branch coverage to trigger branch-level undercover warnings
54
55
  end
55
56
 
56
57
  require 'undercover'
@@ -83,6 +84,15 @@ Check out `docs/` for CI configuration examples:
83
84
 
84
85
  Merging coverage results ([sample gist](https://gist.github.com/grodowski/9744ff91034dce8df20c2a8210409fb0)) is required for parallel tests before processing with `undercover`.
85
86
 
87
+ ## Code review integrations
88
+
89
+ A few options exist to provide automated comments from `undercover` in Pull Request reviews, which is the most streamlined way to add Undercover to your development workflow.
90
+
91
+ - [UndercoverCI](https://undercover-ci.com) - `undercover` Pull Request feedback delivered natively with GitHub Checks
92
+ - [pronto-undercover](https://github.com/grodowski/pronto-undercover)
93
+ - [danger-undercover](https://github.com/nimblehq/danger-undercover)
94
+ - [undercover-checkstyle](https://github.com/aki77/undercover-checkstyle)
95
+
86
96
  ## Configuration
87
97
 
88
98
  ### CLI Options
@@ -12,7 +12,8 @@ module Undercover
12
12
  include Enumerable
13
13
 
14
14
  attr_reader :files
15
- def_delegators :files, :each, :'<=>'
15
+
16
+ def_delegators :files, :each, :<=>
16
17
 
17
18
  def initialize(dir, compare_base = nil)
18
19
  @dir = dir
@@ -10,8 +10,8 @@ module Undercover
10
10
 
11
11
  WARNINGS_TO_S = {
12
12
  stale_coverage: Rainbow('🚨 WARNING: Coverage data is older than your ' \
13
- 'latest changes and results might be incomplete. ' \
14
- 'Re-run tests to update').yellow,
13
+ 'latest changes and results might be incomplete. ' \
14
+ 'Re-run tests to update').yellow,
15
15
  no_changes: Rainbow('✅ No reportable changes').green
16
16
  }.freeze
17
17
  def self.run(args)
@@ -18,20 +18,20 @@ module Undercover
18
18
  @results.map.with_index(1) do |res, idx|
19
19
  "🚨 #{idx}) node `#{res.node.name}` type: #{res.node.human_name},\n" +
20
20
  (' ' * pad_size) + "loc: #{res.file_path_with_lines}," \
21
- " coverage: #{res.coverage_f * 100}%\n" +
21
+ " coverage: #{res.coverage_f * 100}%\n" +
22
22
  res.pretty_print
23
23
  end
24
24
  end
25
25
 
26
26
  def success
27
27
  "#{Rainbow('undercover').bold.green}: ✅ No coverage" \
28
- ' is missing in latest changes'
28
+ ' is missing in latest changes'
29
29
  end
30
30
 
31
31
  def warnings_header
32
32
  "#{Rainbow('undercover').bold.red}: " \
33
- '👮‍♂️ some methods have no test coverage! Please add specs for' \
34
- ' methods listed below'
33
+ '👮‍♂️ some methods have no test coverage! Please add specs for' \
34
+ ' methods listed below'
35
35
  end
36
36
 
37
37
  def pad_size
@@ -31,7 +31,7 @@ module Undercover
31
31
 
32
32
  private
33
33
 
34
- # rubocop:disable Metrics/MethodLength, Style/SpecialGlobalVars
34
+ # rubocop:disable Metrics/MethodLength, Style/SpecialGlobalVars, Metrics/AbcSize
35
35
  def parse_line(line)
36
36
  case line
37
37
  when /^SF:(.+)/
@@ -41,12 +41,20 @@ module Undercover
41
41
  line_no = $~[1]
42
42
  covered = $~[2]
43
43
  source_files[@current_filename] << [line_no.to_i, covered.to_i]
44
+ when /^(BRF|BRH):(\d+)/
45
+ # branches found/hit; no-op
46
+ when /^BRDA:(\d+),(\d+),(\d+),(-|\d+)/
47
+ line_no = $~[1]
48
+ block_no = $~[2]
49
+ branch_no = $~[3]
50
+ covered = ($~[4] == '-' ? '0' : $~[4])
51
+ source_files[@current_filename] << [line_no.to_i, block_no.to_i, branch_no.to_i, covered.to_i]
44
52
  when /^end_of_record$/, /^$/
45
53
  @current_filename = nil
46
54
  else
47
55
  raise LcovParseError, "could not recognise '#{line}' as valid LCOV"
48
56
  end
49
57
  end
50
- # rubocop:enable Metrics/MethodLength, Style/SpecialGlobalVars
58
+ # rubocop:enable Metrics/MethodLength, Style/SpecialGlobalVars, Metrics/AbcSize
51
59
  end
52
60
  end
@@ -74,7 +74,7 @@ module Undercover
74
74
  def args_from_options_file(path)
75
75
  return [] unless File.exist?(path)
76
76
 
77
- File.read(path).split('\n').flat_map { |line| line.split(' ') }
77
+ File.read(path).split('\n').flat_map(&:split)
78
78
  end
79
79
 
80
80
  def project_options_file
@@ -27,28 +27,38 @@ module Undercover
27
27
  @flagged
28
28
  end
29
29
 
30
- # TODO: make DRY
31
- def non_code?(line_no)
32
- line_cov = coverage.find { |ln, _cov| ln == line_no }
33
- !line_cov
34
- end
35
-
36
- def covered?(line_no)
37
- line_cov = coverage.find { |ln, _cov| ln == line_no }
38
- line_cov && line_cov[1].positive?
39
- end
40
-
30
+ # rubocop:disable Metrics/CyclomaticComplexity
41
31
  def uncovered?(line_no)
42
- line_cov = coverage.find { |ln, _cov| ln == line_no }
32
+ # check branch coverage for line_no
33
+ coverage.each do |ln, _block, _branch, cov|
34
+ return true if ln == line_no && cov && cov.zero?
35
+ end
36
+
37
+ # check line coverage for line_no
38
+ line_cov = coverage.select { |cov| cov.size == 2 }.find { |ln, _cov| ln == line_no }
43
39
  line_cov && line_cov[1].zero?
44
40
  end
41
+ # rubocop:enable Metrics/CyclomaticComplexity
45
42
 
43
+ # Method `coverage_f` returns the total coverage of this Undercover::Result
44
+ # as a % value, taking into account missing branches. Line coverage will be counted
45
+ # as 0 if any branch is untested.
46
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
46
47
  def coverage_f
47
- covered = coverage.reduce(0) do |sum, (_, cov)|
48
- sum + [[0, cov].max, 1].min
48
+ lines = {}
49
+ coverage.each do |ln, block_or_line_cov, _, branch_cov|
50
+ lines[ln] = 1 unless lines.key?(ln)
51
+ if branch_cov
52
+ lines[ln] = 0 if branch_cov.zero?
53
+ elsif block_or_line_cov.zero?
54
+ lines[ln] = 0
55
+ end
49
56
  end
50
- (covered.to_f / coverage.size).round(4)
57
+ return 1.0 if lines.keys.size.zero?
58
+
59
+ (lines.values.sum.to_f / lines.keys.size).round(4)
51
60
  end
61
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
52
62
 
53
63
  # TODO: create a formatter interface instead and add some tests.
54
64
  # TODO: re-enable rubocops
@@ -60,10 +70,10 @@ module Undercover
60
70
  cov_enum = coverage.each
61
71
  cov_source_lines = (node.first_line..node.last_line).map do |line_no|
62
72
  cov_line_no = begin
63
- cov_enum.peek[0]
64
- rescue StopIteration
65
- -1
66
- end
73
+ cov_enum.peek[0]
74
+ rescue StopIteration
75
+ -1
76
+ end
67
77
  cov_enum.next[1] if cov_line_no == line_no
68
78
  end
69
79
  cov_source_lines.zip(node.source_lines_with_numbers)
@@ -81,10 +91,12 @@ module Undercover
81
91
  Rainbow(' hits: n/a').italic.darkgray.dark
82
92
  elsif covered.positive?
83
93
  Rainbow(formatted_line).green + \
84
- Rainbow(" hits: #{covered}").italic.darkgray.dark
94
+ Rainbow(" hits: #{covered}").italic.darkgray.dark + \
95
+ count_covered_branches(num)
85
96
  elsif covered.zero?
86
97
  Rainbow(formatted_line).red + \
87
- Rainbow(" hits: #{covered}").italic.darkgray.dark
98
+ Rainbow(" hits: #{covered}").italic.darkgray.dark + \
99
+ count_covered_branches(num)
88
100
  end
89
101
  end.join("\n")
90
102
  end
@@ -99,5 +111,23 @@ module Undercover
99
111
  " name: #{node.name}, coverage: #{coverage_f}>"
100
112
  end
101
113
  alias to_s inspect
114
+
115
+ private
116
+
117
+ # rubocop:disable Metrics/AbcSize
118
+ def count_covered_branches(line_number)
119
+ branches = coverage.select { |cov| cov.size == 4 && cov[0] == line_number }
120
+ count_covered = branches.count { |cov| cov[3].positive? }
121
+
122
+ return '' if branches.size.zero?
123
+
124
+ if count_covered < branches.size
125
+ Rainbow(' branches: ').italic.darkgray.dark + \
126
+ Rainbow("#{count_covered}/#{branches.size}").italic.red
127
+ else
128
+ Rainbow(" branches: #{count_covered}/#{branches.size}").italic.darkgray.dark
129
+ end
130
+ end
131
+ # rubocop:enable Metrics/AbcSize
102
132
  end
103
133
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Undercover
4
- VERSION = '0.3.4'
4
+ VERSION = '0.4.4'
5
5
  end
data/lib/undercover.rb CHANGED
@@ -36,7 +36,7 @@ module Undercover
36
36
  @results = {}
37
37
  end
38
38
 
39
- # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
39
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity
40
40
  def build
41
41
  changeset.each_changed_line do |filepath, line_no|
42
42
  dist_from_line_no = lambda do |res|
@@ -61,7 +61,7 @@ module Undercover
61
61
  end
62
62
  self
63
63
  end
64
- # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
64
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity
65
65
 
66
66
  def build_warnings
67
67
  warn('Undercover::Report#build_warnings is deprecated! ' \
data/undercover.gemspec CHANGED
@@ -23,18 +23,20 @@ Gem::Specification.new do |spec|
23
23
  spec.executables = spec.files.grep(/^bin\//) { |f| File.basename(f) }
24
24
  spec.require_paths = ['lib']
25
25
 
26
+ spec.required_ruby_version = '>= 2.5.0'
27
+
26
28
  spec.add_dependency 'imagen', '>= 0.1.8'
27
29
  spec.add_dependency 'rainbow', '>= 2.1', '< 4.0'
28
- spec.add_dependency 'rugged', '>= 0.27', '< 1.1'
30
+ spec.add_dependency 'rugged', '>= 0.27', '< 1.3'
29
31
 
30
32
  spec.add_development_dependency 'bundler'
31
33
  spec.add_development_dependency 'pry'
32
34
  spec.add_development_dependency 'rake', '~> 13.0'
33
35
  spec.add_development_dependency 'rspec', '~> 3.0'
34
- spec.add_development_dependency 'rubocop', '~> 0.81.0'
36
+ spec.add_development_dependency 'rubocop', '~> 1.22.1'
35
37
  spec.add_development_dependency 'simplecov'
36
38
  spec.add_development_dependency 'simplecov-html'
37
- spec.add_development_dependency 'simplecov-lcov'
39
+ spec.add_development_dependency 'simplecov-lcov', '~> 0.8'
38
40
  spec.add_development_dependency 'timecop'
39
41
  end
40
42
  # rubocop:enable Metrics/BlockLength
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.3.4
4
+ version: 0.4.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jan Grodowski
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-04-05 00:00:00.000000000 Z
11
+ date: 2021-11-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: imagen
@@ -53,7 +53,7 @@ dependencies:
53
53
  version: '0.27'
54
54
  - - "<"
55
55
  - !ruby/object:Gem::Version
56
- version: '1.1'
56
+ version: '1.3'
57
57
  type: :runtime
58
58
  prerelease: false
59
59
  version_requirements: !ruby/object:Gem::Requirement
@@ -63,7 +63,7 @@ dependencies:
63
63
  version: '0.27'
64
64
  - - "<"
65
65
  - !ruby/object:Gem::Version
66
- version: '1.1'
66
+ version: '1.3'
67
67
  - !ruby/object:Gem::Dependency
68
68
  name: bundler
69
69
  requirement: !ruby/object:Gem::Requirement
@@ -126,14 +126,14 @@ dependencies:
126
126
  requirements:
127
127
  - - "~>"
128
128
  - !ruby/object:Gem::Version
129
- version: 0.81.0
129
+ version: 1.22.1
130
130
  type: :development
131
131
  prerelease: false
132
132
  version_requirements: !ruby/object:Gem::Requirement
133
133
  requirements:
134
134
  - - "~>"
135
135
  - !ruby/object:Gem::Version
136
- version: 0.81.0
136
+ version: 1.22.1
137
137
  - !ruby/object:Gem::Dependency
138
138
  name: simplecov
139
139
  requirement: !ruby/object:Gem::Requirement
@@ -166,16 +166,16 @@ dependencies:
166
166
  name: simplecov-lcov
167
167
  requirement: !ruby/object:Gem::Requirement
168
168
  requirements:
169
- - - ">="
169
+ - - "~>"
170
170
  - !ruby/object:Gem::Version
171
- version: '0'
171
+ version: '0.8'
172
172
  type: :development
173
173
  prerelease: false
174
174
  version_requirements: !ruby/object:Gem::Requirement
175
175
  requirements:
176
- - - ">="
176
+ - - "~>"
177
177
  - !ruby/object:Gem::Version
178
- version: '0'
178
+ version: '0.8'
179
179
  - !ruby/object:Gem::Dependency
180
180
  name: timecop
181
181
  requirement: !ruby/object:Gem::Requirement
@@ -199,6 +199,7 @@ extensions: []
199
199
  extra_rdoc_files: []
200
200
  files:
201
201
  - ".codebeatsettings"
202
+ - ".github/dependabot.yml"
202
203
  - ".github/workflows/ruby.yml"
203
204
  - ".gitignore"
204
205
  - ".overcommit.yml"
@@ -238,14 +239,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
238
239
  requirements:
239
240
  - - ">="
240
241
  - !ruby/object:Gem::Version
241
- version: '0'
242
+ version: 2.5.0
242
243
  required_rubygems_version: !ruby/object:Gem::Requirement
243
244
  requirements:
244
245
  - - ">="
245
246
  - !ruby/object:Gem::Version
246
247
  version: '0'
247
248
  requirements: []
248
- rubygems_version: 3.1.2
249
+ rubygems_version: 3.2.22
249
250
  signing_key:
250
251
  specification_version: 4
251
252
  summary: Actionable code coverage - detects untested code blocks in recent changes