undercover 0.8.4 → 0.9.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 +4 -4
- data/.github/FUNDING.yml +15 -0
- data/.github/workflows/ruby.yml +4 -4
- data/.rubocop.yml +1 -1
- data/CHANGELOG.md +28 -1
- data/Gemfile +3 -1
- data/README.md +149 -9
- data/bin/undercover +1 -9
- data/lib/undercover/ast_branch_annotator.rb +62 -0
- data/lib/undercover/changeset.rb +29 -5
- data/lib/undercover/cli.rb +106 -28
- data/lib/undercover/coverage_index.rb +19 -0
- data/lib/undercover/coverage_root.rb +73 -0
- data/lib/undercover/erb_extractor.rb +32 -0
- data/lib/undercover/erb_node.rb +20 -0
- data/lib/undercover/erb_visitor.rb +42 -0
- data/lib/undercover/file_loader.rb +51 -0
- data/lib/undercover/filter_set.rb +34 -9
- data/lib/undercover/formatter.rb +18 -1
- data/lib/undercover/json_formatter.rb +84 -0
- data/lib/undercover/lcov_parser.rb +39 -16
- data/lib/undercover/node.rb +78 -0
- data/lib/undercover/node_visitor.rb +86 -0
- data/lib/undercover/options.rb +50 -17
- data/lib/undercover/result.rb +43 -24
- data/lib/undercover/simplecov_formatter.rb +50 -19
- data/lib/undercover/simplecov_result_adapter.rb +49 -11
- data/lib/undercover/version.rb +1 -1
- data/lib/undercover.rb +73 -35
- data/undercover.gemspec +9 -6
- metadata +43 -13
- data/lib/undercover/root_to_relative_paths.rb +0 -24
data/lib/undercover.rb
CHANGED
|
@@ -2,25 +2,33 @@
|
|
|
2
2
|
|
|
3
3
|
$LOAD_PATH << 'lib'
|
|
4
4
|
require 'json'
|
|
5
|
-
require 'imagen'
|
|
6
5
|
require 'rainbow'
|
|
7
|
-
require 'bigdecimal'
|
|
8
6
|
require 'forwardable'
|
|
9
7
|
require 'simplecov'
|
|
10
8
|
|
|
11
9
|
require 'undercover/lcov_parser'
|
|
10
|
+
require 'undercover/coverage_index'
|
|
12
11
|
require 'undercover/result'
|
|
13
12
|
require 'undercover/cli'
|
|
14
13
|
require 'undercover/changeset'
|
|
15
14
|
require 'undercover/formatter'
|
|
15
|
+
require 'undercover/json_formatter'
|
|
16
16
|
require 'undercover/options'
|
|
17
|
+
require 'undercover/coverage_root'
|
|
17
18
|
require 'undercover/filter_set'
|
|
18
19
|
require 'undercover/simplecov_result_adapter'
|
|
19
20
|
require 'undercover/version'
|
|
21
|
+
require 'undercover/node'
|
|
22
|
+
require 'undercover/node_visitor'
|
|
23
|
+
require 'undercover/erb_node'
|
|
24
|
+
require 'undercover/erb_visitor'
|
|
25
|
+
require 'undercover/erb_extractor'
|
|
26
|
+
require 'undercover/file_loader'
|
|
20
27
|
|
|
21
28
|
module Undercover
|
|
22
29
|
class Report
|
|
23
30
|
extend Forwardable
|
|
31
|
+
include FileLoader
|
|
24
32
|
|
|
25
33
|
def_delegators :changeset, :validate
|
|
26
34
|
|
|
@@ -29,6 +37,7 @@ module Undercover
|
|
|
29
37
|
:coverage_adapter,
|
|
30
38
|
:results,
|
|
31
39
|
:code_dir,
|
|
40
|
+
:coverage_root,
|
|
32
41
|
:filter_set,
|
|
33
42
|
:max_warnings_limit
|
|
34
43
|
|
|
@@ -39,41 +48,28 @@ module Undercover
|
|
|
39
48
|
# @param coverage_adapter [Undercover::SimplecovResultAdapter|Undercover::LcovParser] pre-parsed coverage adapter
|
|
40
49
|
def initialize(changeset, opts, coverage_adapter)
|
|
41
50
|
@coverage_adapter = coverage_adapter
|
|
42
|
-
|
|
43
|
-
@code_dir = opts.path
|
|
44
51
|
@changeset = changeset
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
52
|
+
@code_dir = changeset.repo_workdir
|
|
53
|
+
@coverage_root = resolve_coverage_root
|
|
54
|
+
coverage_adapter.coverage_root = @coverage_root
|
|
55
|
+
@filter_set = build_filter_set(opts)
|
|
48
56
|
changeset.filter_with(filter_set)
|
|
49
57
|
@max_warnings_limit = opts.max_warnings_limit
|
|
50
58
|
@loaded_files = {}
|
|
51
59
|
@results = {}
|
|
52
60
|
end
|
|
53
61
|
|
|
54
|
-
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
|
62
|
+
# rubocop:disable-next Metrics/MethodLength, Metrics/AbcSize
|
|
55
63
|
def build
|
|
56
64
|
flag_count = 0
|
|
57
65
|
changeset.each_changed_line do |filepath, line_no|
|
|
58
66
|
break if max_warnings_limit && flag_count >= max_warnings_limit
|
|
59
67
|
|
|
60
|
-
dist_from_line_no = lambda do |res|
|
|
61
|
-
return BigDecimal::INFINITY if line_no < res.first_line
|
|
62
|
-
|
|
63
|
-
res_lines = res.first_line..res.last_line
|
|
64
|
-
return BigDecimal::INFINITY unless res_lines.cover?(line_no)
|
|
65
|
-
|
|
66
|
-
line_no - res.first_line
|
|
67
|
-
end
|
|
68
|
-
dist_from_line_no_sorter = lambda do |res1, res2|
|
|
69
|
-
dist_from_line_no[res1] <=> dist_from_line_no[res2]
|
|
70
|
-
end
|
|
71
|
-
|
|
72
68
|
load_and_parse_file(filepath)
|
|
73
69
|
|
|
74
70
|
next unless loaded_files[filepath]
|
|
75
71
|
|
|
76
|
-
res =
|
|
72
|
+
res = result_for_line(filepath, line_no)
|
|
77
73
|
if res.uncovered?(line_no) && !res.flagged?
|
|
78
74
|
res.flag
|
|
79
75
|
flag_count += 1
|
|
@@ -83,7 +79,6 @@ module Undercover
|
|
|
83
79
|
end
|
|
84
80
|
self
|
|
85
81
|
end
|
|
86
|
-
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
|
|
87
82
|
|
|
88
83
|
def build_warnings
|
|
89
84
|
warn('Undercover::Report#build_warnings is deprecated! ' \
|
|
@@ -99,6 +94,29 @@ module Undercover
|
|
|
99
94
|
all_results.select(&:flagged?)
|
|
100
95
|
end
|
|
101
96
|
|
|
97
|
+
# Changed files undercover would have reported on, had --include-files /
|
|
98
|
+
# --exclude-files not rejected them. Everything rejected and nothing reported usually
|
|
99
|
+
# means the globs are written against the wrong root.
|
|
100
|
+
def globbed_out_files
|
|
101
|
+
@globbed_out_files ||= changeset.file_paths.select { |path| filter_set.rejected_by_globs?(path) }
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Changed ERB templates left unchecked because the coverage report has no ERB coverage
|
|
105
|
+
# at all, meaning SimpleCov ran without enable_coverage(:eval). Checking them anyway
|
|
106
|
+
# would flag every block at 0%.
|
|
107
|
+
def erb_files_without_coverage
|
|
108
|
+
return [] if coverage_adapter.erb_coverage?
|
|
109
|
+
|
|
110
|
+
@erb_files_without_coverage ||= changeset.file_paths.select do |path|
|
|
111
|
+
path.end_with?(FileLoader::ERB_EXTENSION) && filter_set.include?(path)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Changed ERB templates left unchecked because they could not be parsed
|
|
116
|
+
def erb_parse_failures
|
|
117
|
+
@erb_parse_failures ||= []
|
|
118
|
+
end
|
|
119
|
+
|
|
102
120
|
def inspect
|
|
103
121
|
"#<Undercover::Report:#{object_id} results: #{results.size}>"
|
|
104
122
|
end
|
|
@@ -108,21 +126,41 @@ module Undercover
|
|
|
108
126
|
|
|
109
127
|
attr_reader :loaded_files
|
|
110
128
|
|
|
111
|
-
#
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
)
|
|
119
|
-
return if root_ast.children.empty?
|
|
129
|
+
# The innermost result whose lines cover line_no: the one starting closest above it,
|
|
130
|
+
# the first one listed on a tie. Lines outside every result fall back to the first.
|
|
131
|
+
def result_for_line(filepath, line_no)
|
|
132
|
+
@line_results ||= {}
|
|
133
|
+
by_line = @line_results[filepath] ||= index_results_by_line(loaded_files[filepath])
|
|
134
|
+
by_line[line_no] || loaded_files[filepath].first
|
|
135
|
+
end
|
|
120
136
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
137
|
+
def index_results_by_line(file_results)
|
|
138
|
+
by_line = {}
|
|
139
|
+
file_results.each do |res|
|
|
140
|
+
(res.first_line..res.last_line).each do |line_no|
|
|
141
|
+
current = by_line[line_no]
|
|
142
|
+
by_line[line_no] = res if current.nil? || res.first_line > current.first_line
|
|
143
|
+
end
|
|
124
144
|
end
|
|
145
|
+
by_line
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def build_filter_set(opts)
|
|
149
|
+
FilterSet.new(opts.glob_allow_filters, opts.glob_reject_filters, coverage_adapter.ignored_files || [],
|
|
150
|
+
coverage_root: coverage_root)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# LCOV reports carry no SimpleCov.root, so fall back to where undercover runs, which
|
|
154
|
+
# is where SimpleCov ran for anyone invoking the CLI by hand. A wrong guess is caught
|
|
155
|
+
# by the covered-file check in CoverageRoot.
|
|
156
|
+
def resolve_coverage_root
|
|
157
|
+
simplecov_root = coverage_adapter.simplecov_root || Dir.pwd
|
|
158
|
+
CoverageRoot.derive(code_dir, simplecov_root, coverage_adapter.coverage_keys, exists: method(:repository_path?))
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# On disk, or committed at HEAD for files a sparse checkout left out
|
|
162
|
+
def repository_path?(path)
|
|
163
|
+
File.exist?(File.join(code_dir, path)) || changeset.head_path?(path)
|
|
125
164
|
end
|
|
126
|
-
# rubocop:enable Metrics/AbcSize
|
|
127
165
|
end
|
|
128
166
|
end
|
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']
|
|
@@ -25,13 +25,16 @@ Gem::Specification.new do |spec|
|
|
|
25
25
|
spec.executables = spec.files.grep(/^bin\//) { |f| File.basename(f) }
|
|
26
26
|
spec.require_paths = ['lib']
|
|
27
27
|
|
|
28
|
-
spec.required_ruby_version = '>= 3.
|
|
28
|
+
spec.required_ruby_version = '>= 3.2.0'
|
|
29
29
|
|
|
30
30
|
spec.add_dependency 'base64'
|
|
31
|
-
spec.add_dependency '
|
|
32
|
-
spec.add_dependency '
|
|
31
|
+
spec.add_dependency 'benchmark'
|
|
32
|
+
spec.add_dependency 'erubi', '>= 1.13'
|
|
33
|
+
spec.add_dependency 'prism', '>= 1.8', '< 3'
|
|
33
34
|
spec.add_dependency 'rainbow', '>= 2.1', '< 4.0'
|
|
34
35
|
spec.add_dependency 'rugged', '>= 0.27', '< 1.10'
|
|
35
|
-
spec.add_dependency 'simplecov'
|
|
36
|
-
|
|
36
|
+
spec.add_dependency 'simplecov', '>= 1.0'
|
|
37
|
+
# Pinned: undercover requires its internal files by path and subclasses
|
|
38
|
+
# ResultHashFormatter, so a file reorganisation would break loading.
|
|
39
|
+
spec.add_dependency 'simplecov_json_formatter', '~> 0.1'
|
|
37
40
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: undercover
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.9.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jan Grodowski
|
|
@@ -24,7 +24,7 @@ dependencies:
|
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: '0'
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
|
-
name:
|
|
27
|
+
name: benchmark
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
29
29
|
requirements:
|
|
30
30
|
- - ">="
|
|
@@ -38,19 +38,39 @@ dependencies:
|
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
39
|
version: '0'
|
|
40
40
|
- !ruby/object:Gem::Dependency
|
|
41
|
-
name:
|
|
41
|
+
name: erubi
|
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|
|
43
43
|
requirements:
|
|
44
44
|
- - ">="
|
|
45
45
|
- !ruby/object:Gem::Version
|
|
46
|
-
version:
|
|
46
|
+
version: '1.13'
|
|
47
47
|
type: :runtime
|
|
48
48
|
prerelease: false
|
|
49
49
|
version_requirements: !ruby/object:Gem::Requirement
|
|
50
50
|
requirements:
|
|
51
51
|
- - ">="
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
|
-
version:
|
|
53
|
+
version: '1.13'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: prism
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '1.8'
|
|
61
|
+
- - "<"
|
|
62
|
+
- !ruby/object:Gem::Version
|
|
63
|
+
version: '3'
|
|
64
|
+
type: :runtime
|
|
65
|
+
prerelease: false
|
|
66
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
67
|
+
requirements:
|
|
68
|
+
- - ">="
|
|
69
|
+
- !ruby/object:Gem::Version
|
|
70
|
+
version: '1.8'
|
|
71
|
+
- - "<"
|
|
72
|
+
- !ruby/object:Gem::Version
|
|
73
|
+
version: '3'
|
|
54
74
|
- !ruby/object:Gem::Dependency
|
|
55
75
|
name: rainbow
|
|
56
76
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -97,28 +117,28 @@ dependencies:
|
|
|
97
117
|
requirements:
|
|
98
118
|
- - ">="
|
|
99
119
|
- !ruby/object:Gem::Version
|
|
100
|
-
version: '0'
|
|
120
|
+
version: '1.0'
|
|
101
121
|
type: :runtime
|
|
102
122
|
prerelease: false
|
|
103
123
|
version_requirements: !ruby/object:Gem::Requirement
|
|
104
124
|
requirements:
|
|
105
125
|
- - ">="
|
|
106
126
|
- !ruby/object:Gem::Version
|
|
107
|
-
version: '0'
|
|
127
|
+
version: '1.0'
|
|
108
128
|
- !ruby/object:Gem::Dependency
|
|
109
129
|
name: simplecov_json_formatter
|
|
110
130
|
requirement: !ruby/object:Gem::Requirement
|
|
111
131
|
requirements:
|
|
112
|
-
- - "
|
|
132
|
+
- - "~>"
|
|
113
133
|
- !ruby/object:Gem::Version
|
|
114
|
-
version: '0'
|
|
134
|
+
version: '0.1'
|
|
115
135
|
type: :runtime
|
|
116
136
|
prerelease: false
|
|
117
137
|
version_requirements: !ruby/object:Gem::Requirement
|
|
118
138
|
requirements:
|
|
119
|
-
- - "
|
|
139
|
+
- - "~>"
|
|
120
140
|
- !ruby/object:Gem::Version
|
|
121
|
-
version: '0'
|
|
141
|
+
version: '0.1'
|
|
122
142
|
email:
|
|
123
143
|
- jgrodowski@gmail.com
|
|
124
144
|
executables:
|
|
@@ -127,6 +147,7 @@ extensions: []
|
|
|
127
147
|
extra_rdoc_files: []
|
|
128
148
|
files:
|
|
129
149
|
- ".codebeatsettings"
|
|
150
|
+
- ".github/FUNDING.yml"
|
|
130
151
|
- ".github/dependabot.yml"
|
|
131
152
|
- ".github/workflows/ruby.yml"
|
|
132
153
|
- ".gitignore"
|
|
@@ -147,14 +168,23 @@ files:
|
|
|
147
168
|
- docs/screenshot_warnings.png
|
|
148
169
|
- docs/semaphore.yml
|
|
149
170
|
- lib/undercover.rb
|
|
171
|
+
- lib/undercover/ast_branch_annotator.rb
|
|
150
172
|
- lib/undercover/changeset.rb
|
|
151
173
|
- lib/undercover/cli.rb
|
|
174
|
+
- lib/undercover/coverage_index.rb
|
|
175
|
+
- lib/undercover/coverage_root.rb
|
|
176
|
+
- lib/undercover/erb_extractor.rb
|
|
177
|
+
- lib/undercover/erb_node.rb
|
|
178
|
+
- lib/undercover/erb_visitor.rb
|
|
179
|
+
- lib/undercover/file_loader.rb
|
|
152
180
|
- lib/undercover/filter_set.rb
|
|
153
181
|
- lib/undercover/formatter.rb
|
|
182
|
+
- lib/undercover/json_formatter.rb
|
|
154
183
|
- lib/undercover/lcov_parser.rb
|
|
184
|
+
- lib/undercover/node.rb
|
|
185
|
+
- lib/undercover/node_visitor.rb
|
|
155
186
|
- lib/undercover/options.rb
|
|
156
187
|
- lib/undercover/result.rb
|
|
157
|
-
- lib/undercover/root_to_relative_paths.rb
|
|
158
188
|
- lib/undercover/simplecov_formatter.rb
|
|
159
189
|
- lib/undercover/simplecov_result_adapter.rb
|
|
160
190
|
- lib/undercover/version.rb
|
|
@@ -171,7 +201,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
171
201
|
requirements:
|
|
172
202
|
- - ">="
|
|
173
203
|
- !ruby/object:Gem::Version
|
|
174
|
-
version: 3.
|
|
204
|
+
version: 3.2.0
|
|
175
205
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
176
206
|
requirements:
|
|
177
207
|
- - ">="
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Undercover
|
|
4
|
-
module RootToRelativePaths
|
|
5
|
-
# Needed if undercover is running inside nested subdirectories (e.g. in a monorepo app), where
|
|
6
|
-
# the git paths are rooted deeper than the paths in the coverage report.
|
|
7
|
-
# If that is the case, trim the git filepath to match the local relative path, assumming undercover is
|
|
8
|
-
# running in the correct directory (has to be equal to SimpleCov.root for paths to match)
|
|
9
|
-
# @param filepath[String]
|
|
10
|
-
# @return String
|
|
11
|
-
def fix_relative_filepath(filepath)
|
|
12
|
-
return filepath unless @code_dir
|
|
13
|
-
|
|
14
|
-
code_dir_abs = File.expand_path(@code_dir)
|
|
15
|
-
|
|
16
|
-
if Pathname.new(Dir.pwd).ascend.any? { |p| p.to_s == code_dir_abs }
|
|
17
|
-
prefix_to_skip = Pathname.new(Dir.pwd).relative_path_from(code_dir_abs).to_s
|
|
18
|
-
return filepath.delete_prefix(prefix_to_skip).gsub(/\A\//, '')
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
filepath
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|