deep-cover 0.5.2 → 0.5.3
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 +5 -5
- data/.deep_cover.rb +8 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +15 -1
- data/.travis.yml +1 -0
- data/README.md +30 -1
- data/Rakefile +10 -1
- data/bin/cov +1 -1
- data/deep_cover.gemspec +4 -5
- data/exe/deep-cover +5 -3
- data/lib/deep_cover.rb +1 -1
- data/lib/deep_cover/analyser/node.rb +1 -1
- data/lib/deep_cover/analyser/ruby25_like_branch.rb +209 -0
- data/lib/deep_cover/auto_run.rb +19 -19
- data/lib/deep_cover/autoload_tracker.rb +181 -44
- data/lib/deep_cover/backports.rb +3 -1
- data/lib/deep_cover/base.rb +13 -8
- data/lib/deep_cover/basics.rb +1 -1
- data/lib/deep_cover/cli/debugger.rb +2 -2
- data/lib/deep_cover/cli/instrumented_clone_reporter.rb +21 -8
- data/lib/deep_cover/cli/runner.rb +126 -0
- data/lib/deep_cover/config_setter.rb +1 -0
- data/lib/deep_cover/core_ext/autoload_overrides.rb +82 -14
- data/lib/deep_cover/core_ext/coverage_replacement.rb +34 -5
- data/lib/deep_cover/core_ext/exec_callbacks.rb +27 -0
- data/lib/deep_cover/core_ext/load_overrides.rb +4 -6
- data/lib/deep_cover/core_ext/require_overrides.rb +1 -3
- data/lib/deep_cover/coverage.rb +105 -2
- data/lib/deep_cover/coverage/analysis.rb +30 -28
- data/lib/deep_cover/coverage/persistence.rb +60 -70
- data/lib/deep_cover/covered_code.rb +16 -49
- data/lib/deep_cover/custom_requirer.rb +112 -51
- data/lib/deep_cover/load.rb +10 -6
- data/lib/deep_cover/memoize.rb +1 -3
- data/lib/deep_cover/module_override.rb +7 -0
- data/lib/deep_cover/node/assignments.rb +2 -1
- data/lib/deep_cover/node/base.rb +6 -6
- data/lib/deep_cover/node/block.rb +10 -8
- data/lib/deep_cover/node/case.rb +3 -3
- data/lib/deep_cover/node/collections.rb +8 -0
- data/lib/deep_cover/node/if.rb +19 -3
- data/lib/deep_cover/node/literals.rb +28 -7
- data/lib/deep_cover/node/mixin/can_augment_children.rb +4 -4
- data/lib/deep_cover/node/mixin/child_can_be_empty.rb +1 -1
- data/lib/deep_cover/node/mixin/filters.rb +6 -2
- data/lib/deep_cover/node/mixin/has_child.rb +8 -8
- data/lib/deep_cover/node/mixin/has_child_handler.rb +3 -3
- data/lib/deep_cover/node/mixin/has_tracker.rb +7 -3
- data/lib/deep_cover/node/root.rb +1 -1
- data/lib/deep_cover/node/send.rb +53 -7
- data/lib/deep_cover/node/short_circuit.rb +11 -3
- data/lib/deep_cover/parser_ext/range.rb +11 -27
- data/lib/deep_cover/problem_with_diagnostic.rb +1 -1
- data/lib/deep_cover/reporter.rb +0 -1
- data/lib/deep_cover/reporter/base.rb +68 -0
- data/lib/deep_cover/reporter/html.rb +1 -1
- data/lib/deep_cover/reporter/html/index.rb +4 -8
- data/lib/deep_cover/reporter/html/site.rb +10 -18
- data/lib/deep_cover/reporter/html/source.rb +3 -3
- data/lib/deep_cover/reporter/html/template/source.html.erb +1 -1
- data/lib/deep_cover/reporter/istanbul.rb +86 -56
- data/lib/deep_cover/reporter/text.rb +5 -13
- data/lib/deep_cover/reporter/{util/tree.rb → tree/util.rb} +19 -21
- data/lib/deep_cover/tools/blank.rb +25 -0
- data/lib/deep_cover/tools/builtin_coverage.rb +8 -8
- data/lib/deep_cover/tools/dump_covered_code.rb +2 -9
- data/lib/deep_cover/tools/execute_sample.rb +17 -6
- data/lib/deep_cover/tools/format_generated_code.rb +1 -1
- data/lib/deep_cover/tools/indent_string.rb +26 -0
- data/lib/deep_cover/tools/our_coverage.rb +2 -2
- data/lib/deep_cover/tools/strip_heredoc.rb +18 -0
- data/lib/deep_cover/tracker_bucket.rb +50 -0
- data/lib/deep_cover/tracker_hits_per_path.rb +35 -0
- data/lib/deep_cover/tracker_storage.rb +76 -0
- data/lib/deep_cover/tracker_storage_per_path.rb +34 -0
- data/lib/deep_cover/version.rb +1 -1
- data/lib/deep_cover_entry.rb +3 -0
- metadata +30 -37
- data/bin/gemcov +0 -8
- data/bin/selfcov +0 -21
- data/lib/deep_cover/cli/deep_cover.rb +0 -126
- data/lib/deep_cover/coverage/base.rb +0 -81
- data/lib/deep_cover/coverage/istanbul.rb +0 -34
- data/lib/deep_cover/tools/transform_keys.rb +0 -9
@@ -18,8 +18,8 @@ module DeepCover
|
|
18
18
|
]
|
19
19
|
end
|
20
20
|
|
21
|
-
def branches_summary(
|
22
|
-
|
21
|
+
def branches_summary(of_branches = branches)
|
22
|
+
of_branches.map do |jump|
|
23
23
|
if jump == conditional
|
24
24
|
'left-hand side'
|
25
25
|
else
|
@@ -27,8 +27,16 @@ module DeepCover
|
|
27
27
|
end
|
28
28
|
end.join(' and ')
|
29
29
|
end
|
30
|
+
|
31
|
+
def operator
|
32
|
+
loc_hash[:operator].source.to_sym
|
33
|
+
end
|
30
34
|
end
|
31
35
|
|
32
|
-
And
|
36
|
+
class And < ShortCircuit
|
37
|
+
end
|
38
|
+
|
39
|
+
class Or < ShortCircuit
|
40
|
+
end
|
33
41
|
end
|
34
42
|
end
|
@@ -1,37 +1,21 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Parser::Source::Range
|
4
|
-
|
5
|
-
|
6
|
-
def split(*inner_ranges)
|
7
|
-
inner_ranges.sort_by!(&:begin_pos)
|
8
|
-
[self.begin, *inner_ranges, self.end]
|
9
|
-
.each_cons(2)
|
10
|
-
.map { |i, j| with(begin_pos: i.end_pos, end_pos: j.begin_pos) }
|
11
|
-
.reject(&:empty?)
|
4
|
+
def succ
|
5
|
+
adjust(begin_pos: +1, end_pos: +1)
|
12
6
|
end
|
13
7
|
|
14
|
-
def
|
15
|
-
|
16
|
-
|
17
|
-
else
|
18
|
-
self
|
19
|
-
end
|
8
|
+
def wrap_rwhitespace
|
9
|
+
whitespace = @source_buffer.slice(end_pos..-1)[/\A\s+/] || ''
|
10
|
+
adjust(end_pos: whitespace.size)
|
20
11
|
end
|
21
12
|
|
22
|
-
def
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
13
|
+
def wrap_rwhitespace_and_comments
|
14
|
+
current = wrap_rwhitespace
|
15
|
+
while @source_buffer.slice(current.end_pos) == '#'
|
16
|
+
comment = @source_buffer.slice(current.end_pos..-1)[/\A[^\n]+/] || ''
|
17
|
+
current = current.adjust(end_pos: comment.size).wrap_rwhitespace
|
27
18
|
end
|
28
|
-
|
29
|
-
|
30
|
-
def strip(pattern = /\s*/)
|
31
|
-
lstrip(pattern).rstrip(pattern)
|
32
|
-
end
|
33
|
-
|
34
|
-
def succ
|
35
|
-
adjust(begin_pos: +1, end_pos: +1)
|
19
|
+
current
|
36
20
|
end
|
37
21
|
end
|
@@ -38,7 +38,7 @@ module DeepCover
|
|
38
38
|
lines.concat(source_lines.map { |line| " #{line}" })
|
39
39
|
if original_exception
|
40
40
|
lines << 'Original exception:'
|
41
|
-
lines << " #{original_exception.class
|
41
|
+
lines << " #{original_exception.class}: #{original_exception.message}"
|
42
42
|
backtrace = Tools.truncate_backtrace(original_exception)
|
43
43
|
lines.concat(backtrace.map { |line| " #{line}" })
|
44
44
|
end
|
data/lib/deep_cover/reporter.rb
CHANGED
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DeepCover
|
4
|
+
module Reporter
|
5
|
+
require_relative 'tree/util'
|
6
|
+
|
7
|
+
class Base
|
8
|
+
include Memoize
|
9
|
+
memoize :map, :tree, :root_path
|
10
|
+
|
11
|
+
attr_reader :options
|
12
|
+
|
13
|
+
def initialize(coverage, **options)
|
14
|
+
@coverage = coverage
|
15
|
+
@options = options
|
16
|
+
end
|
17
|
+
|
18
|
+
def analysis
|
19
|
+
@analysis ||= Coverage::Analysis.new(@coverage.covered_codes, **options)
|
20
|
+
end
|
21
|
+
|
22
|
+
def each(&block)
|
23
|
+
return to_enum :each unless block_given?
|
24
|
+
@coverage.each do |covered_code|
|
25
|
+
yield relative_path(covered_code.path), covered_code
|
26
|
+
end
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
# Same as populate, but also yields data, which is either the analysis data (for leaves)
|
31
|
+
# of the sum of the children (for subtrees)
|
32
|
+
def populate_stats
|
33
|
+
return to_enum(__method__) unless block_given?
|
34
|
+
Tree::Util.populate_from_map(
|
35
|
+
tree: tree,
|
36
|
+
map: map,
|
37
|
+
merge: ->(child_data) { Tools.merge(*child_data, :+) }
|
38
|
+
) do |full_path, partial_path, data, children|
|
39
|
+
yield relative_path(full_path), relative_path(partial_path), data, children
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def relative_path(path)
|
46
|
+
path = path.to_s
|
47
|
+
path = path.slice(root_path.length + 1..-1) if path.start_with?(root_path)
|
48
|
+
path
|
49
|
+
end
|
50
|
+
|
51
|
+
def root_path
|
52
|
+
return '' if tree.size > 1
|
53
|
+
path = tree.first.first
|
54
|
+
root = File.dirname(path)
|
55
|
+
root = File.dirname(root) if File.basename(path) == 'dir'
|
56
|
+
root
|
57
|
+
end
|
58
|
+
|
59
|
+
def map
|
60
|
+
analysis.stat_map.transform_keys(&:path).transform_keys(&:to_s)
|
61
|
+
end
|
62
|
+
|
63
|
+
def tree
|
64
|
+
Tree::Util.paths_to_tree(map.keys)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -4,17 +4,13 @@ module DeepCover
|
|
4
4
|
require_relative 'base'
|
5
5
|
|
6
6
|
module Reporter
|
7
|
-
class HTML::Index < Struct.new(:
|
8
|
-
def initialize(analysis, **options)
|
9
|
-
raise ArgumentError unless analysis.is_a? Coverage::Analysis
|
10
|
-
super
|
11
|
-
end
|
12
|
-
|
13
|
-
include Util::Tree
|
7
|
+
class HTML::Index < Struct.new(:base)
|
14
8
|
include HTML::Base
|
9
|
+
extend Forwardable
|
10
|
+
def_delegators :base, :analysis, :options, :populate_stats
|
15
11
|
|
16
12
|
def stats_to_data
|
17
|
-
populate_stats
|
13
|
+
populate_stats do |full_path, partial_path, data, children|
|
18
14
|
data = transform_data(data)
|
19
15
|
if children.empty?
|
20
16
|
{
|
@@ -1,23 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module DeepCover
|
4
|
+
require_relative 'base'
|
4
5
|
require_relative 'index'
|
5
6
|
require_relative 'source'
|
6
7
|
|
7
8
|
module Reporter::HTML
|
8
|
-
class Site <
|
9
|
-
def initialize(covered_codes, **options)
|
10
|
-
raise ArgumentError unless covered_codes.all? { |c| c.is_a? CoveredCode }
|
11
|
-
super
|
12
|
-
end
|
13
|
-
|
9
|
+
class Site < Reporter::Base
|
14
10
|
include Memoize
|
15
11
|
memoize :analysis
|
16
12
|
|
17
|
-
def analysis
|
18
|
-
Coverage::Analysis.new(covered_codes, **options)
|
19
|
-
end
|
20
|
-
|
21
13
|
def path
|
22
14
|
Pathname(options[:output])
|
23
15
|
end
|
@@ -42,7 +34,7 @@ module DeepCover
|
|
42
34
|
end
|
43
35
|
|
44
36
|
def render_index
|
45
|
-
Tools.render_template(:index, Index.new(
|
37
|
+
Tools.render_template(:index, Index.new(self))
|
46
38
|
end
|
47
39
|
|
48
40
|
def save_index
|
@@ -58,20 +50,20 @@ module DeepCover
|
|
58
50
|
dest.join('deep_cover.css.sass').delete
|
59
51
|
end
|
60
52
|
|
61
|
-
def render_source(covered_code)
|
62
|
-
Tools.render_template(:source, Source.new(analysis.analyser_map.fetch(covered_code)))
|
53
|
+
def render_source(partial_path, covered_code)
|
54
|
+
Tools.render_template(:source, Source.new(analysis.analyser_map.fetch(covered_code), partial_path))
|
63
55
|
end
|
64
56
|
|
65
57
|
def save_pages
|
66
|
-
|
67
|
-
dest = path.join("#{
|
58
|
+
each do |partial_path, covered_code|
|
59
|
+
dest = path.join("#{partial_path}.html")
|
68
60
|
dest.dirname.mkpath
|
69
|
-
dest.write(render_source(covered_code))
|
61
|
+
dest.write(render_source(partial_path, covered_code))
|
70
62
|
end
|
71
63
|
end
|
72
64
|
|
73
|
-
def self.save(
|
74
|
-
Site.new(
|
65
|
+
def self.save(coverage, output:, **options)
|
66
|
+
Site.new(coverage, output: output, **options).save
|
75
67
|
end
|
76
68
|
end
|
77
69
|
end
|
@@ -4,10 +4,10 @@ module DeepCover
|
|
4
4
|
module Reporter
|
5
5
|
require_relative 'base'
|
6
6
|
|
7
|
-
class HTML::Source < Struct.new(:analyser_map)
|
7
|
+
class HTML::Source < Struct.new(:analyser_map, :partial_path)
|
8
8
|
include Tools::Covered
|
9
9
|
|
10
|
-
def initialize(analyser_map)
|
10
|
+
def initialize(analyser_map, partial_path)
|
11
11
|
raise ArgumentError unless analyser_map.values.all? { |a| a.is_a?(Analyser) }
|
12
12
|
super
|
13
13
|
end
|
@@ -33,7 +33,7 @@ module DeepCover
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def root_path
|
36
|
-
Pathname('.').relative_path_from(Pathname(
|
36
|
+
Pathname('.').relative_path_from(Pathname(partial_path).dirname)
|
37
37
|
end
|
38
38
|
|
39
39
|
def stats
|
@@ -4,8 +4,7 @@ require 'json'
|
|
4
4
|
|
5
5
|
module DeepCover
|
6
6
|
module Reporter
|
7
|
-
class Istanbul <
|
8
|
-
# Converters has no dependency on the including class.
|
7
|
+
class Istanbul < Base
|
9
8
|
module Converters
|
10
9
|
def convert_range(range)
|
11
10
|
{start: {
|
@@ -68,83 +67,114 @@ module DeepCover
|
|
68
67
|
}
|
69
68
|
end
|
70
69
|
end
|
71
|
-
include Converters
|
72
70
|
|
73
|
-
|
74
|
-
|
75
|
-
end
|
71
|
+
class CoveredCodeConverter < Struct.new(:covered_code, :options)
|
72
|
+
include Converters
|
76
73
|
|
77
|
-
|
78
|
-
|
79
|
-
|
74
|
+
def node_analyser
|
75
|
+
@node_analyser ||= Analyser::Node.new(covered_code, **options)
|
76
|
+
end
|
80
77
|
|
81
|
-
|
82
|
-
|
83
|
-
|
78
|
+
def node_runs
|
79
|
+
@node_runs ||= node_analyser.results
|
80
|
+
end
|
84
81
|
|
85
|
-
|
86
|
-
|
87
|
-
|
82
|
+
def functions
|
83
|
+
@functions ||= Analyser::Function.new(node_analyser, **options).results
|
84
|
+
end
|
88
85
|
|
89
|
-
|
90
|
-
|
91
|
-
|
86
|
+
def statements
|
87
|
+
@statements ||= Analyser::Statement.new(node_analyser, **options).results
|
88
|
+
end
|
92
89
|
|
93
|
-
|
94
|
-
|
95
|
-
convert_branch(node, branches_runs.keys)
|
90
|
+
def branches
|
91
|
+
@branches ||= Analyser::Branch.new(node_analyser, **options).results
|
96
92
|
end
|
97
|
-
end
|
98
93
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
94
|
+
def branch_map
|
95
|
+
branches.map do |node, branches_runs|
|
96
|
+
convert_branch(node, branches_runs.keys)
|
97
|
+
end
|
98
|
+
end
|
103
99
|
|
104
|
-
|
105
|
-
|
106
|
-
|
100
|
+
# Istanbul doesn't understand how to ignore a branch...
|
101
|
+
def zero_to_something(values)
|
102
|
+
values.map { |v| v || 1 }
|
103
|
+
end
|
107
104
|
|
108
|
-
|
109
|
-
|
110
|
-
|
105
|
+
def branch_runs
|
106
|
+
branches.values.map { |r| zero_to_something(r.values) }
|
107
|
+
end
|
111
108
|
|
112
|
-
|
113
|
-
|
114
|
-
|
109
|
+
def statement_map
|
110
|
+
statements.keys.map { |range| convert_range(range) }
|
111
|
+
end
|
115
112
|
|
116
|
-
|
117
|
-
|
118
|
-
|
113
|
+
def statement_runs
|
114
|
+
statements.values
|
115
|
+
end
|
119
116
|
|
120
|
-
|
121
|
-
|
122
|
-
|
117
|
+
def function_map
|
118
|
+
functions.keys.map { |n| convert_function(n) }
|
119
|
+
end
|
120
|
+
|
121
|
+
def function_runs
|
122
|
+
functions.values
|
123
|
+
end
|
123
124
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
125
|
+
def data
|
126
|
+
{
|
127
|
+
statementMap: statement_map,
|
128
|
+
s: statement_runs,
|
129
|
+
fnMap: function_map,
|
130
|
+
f: function_runs,
|
131
|
+
branchMap: branch_map,
|
132
|
+
b: branch_runs,
|
133
|
+
}
|
134
|
+
end
|
135
|
+
|
136
|
+
def convert
|
137
|
+
{
|
138
|
+
path: covered_code.path,
|
139
|
+
**data.transform_values { |l| convert_list(l) },
|
140
|
+
}
|
141
|
+
end
|
133
142
|
end
|
134
143
|
|
135
144
|
def convert
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
145
|
+
each.to_a.to_h.transform_values do |covered_code|
|
146
|
+
CoveredCodeConverter.new(covered_code, **@options).convert
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def save(dir: '.', name: '.nyc_output')
|
151
|
+
path = Pathname.new(dir).expand_path.join(name)
|
152
|
+
path.mkpath
|
153
|
+
path.each_child(&:delete)
|
154
|
+
path.join('deep_cover.json').write(JSON.pretty_generate(convert))
|
155
|
+
path
|
141
156
|
end
|
142
157
|
|
143
158
|
def report
|
144
|
-
|
159
|
+
output = @options[:output]
|
160
|
+
dir = save.dirname
|
161
|
+
unless [nil, false, '', 'false'].include? output
|
162
|
+
output = File.expand_path(output)
|
163
|
+
html = "--reporter=html --report-dir='#{output}'"
|
164
|
+
if @options[:open]
|
165
|
+
html += " && open '#{output}/index.html'"
|
166
|
+
else
|
167
|
+
msg = "\nHTML coverage written to: '#{output}/index.html'"
|
168
|
+
end
|
169
|
+
end
|
170
|
+
`cd #{dir} && nyc report --reporter=text #{html}` + msg.to_s
|
145
171
|
end
|
146
172
|
|
147
173
|
class << self
|
174
|
+
def report(coverage, **options)
|
175
|
+
new(coverage, options).report
|
176
|
+
end
|
177
|
+
|
148
178
|
def available?
|
149
179
|
`nyc --version` >= '11.' rescue false
|
150
180
|
end
|