deep-cover 0.1.14 → 0.1.15
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/.rubocop.yml +227 -0
- data/Gemfile +5 -2
- data/Rakefile +9 -6
- data/bin/console +3 -3
- data/bin/cov +8 -8
- data/bin/gemcov +2 -2
- data/bin/selfcov +5 -5
- data/bin/test_gems +11 -10
- data/bin/testall +6 -6
- data/deep_cover.gemspec +26 -21
- data/exe/deep-cover +1 -0
- data/lib/deep-cover.rb +2 -0
- data/lib/deep_cover.rb +3 -0
- data/lib/deep_cover/analyser.rb +2 -0
- data/lib/deep_cover/analyser/base.rb +2 -0
- data/lib/deep_cover/analyser/branch.rb +4 -2
- data/lib/deep_cover/analyser/covered_code_source.rb +3 -1
- data/lib/deep_cover/analyser/function.rb +3 -1
- data/lib/deep_cover/analyser/ignore_uncovered.rb +6 -4
- data/lib/deep_cover/analyser/node.rb +3 -0
- data/lib/deep_cover/analyser/optionally_covered.rb +12 -7
- data/lib/deep_cover/analyser/per_char.rb +7 -6
- data/lib/deep_cover/analyser/per_line.rb +9 -8
- data/lib/deep_cover/analyser/statement.rb +2 -0
- data/lib/deep_cover/analyser/subset.rb +4 -1
- data/lib/deep_cover/auto_run.rb +3 -0
- data/lib/deep_cover/autoload_tracker.rb +6 -3
- data/lib/deep_cover/backports.rb +2 -0
- data/lib/deep_cover/base.rb +11 -2
- data/lib/deep_cover/builtin_takeover.rb +2 -0
- data/lib/deep_cover/cli/debugger.rb +55 -30
- data/lib/deep_cover/cli/deep_cover.rb +17 -11
- data/lib/deep_cover/cli/instrumented_clone_reporter.rb +16 -14
- data/lib/deep_cover/config.rb +29 -16
- data/lib/deep_cover/core_ext/autoload_overrides.rb +2 -0
- data/lib/deep_cover/core_ext/coverage_replacement.rb +2 -0
- data/lib/deep_cover/core_ext/load_overrides.rb +5 -6
- data/lib/deep_cover/core_ext/require_overrides.rb +6 -7
- data/lib/deep_cover/coverage.rb +21 -18
- data/lib/deep_cover/covered_code.rb +22 -12
- data/lib/deep_cover/custom_requirer.rb +82 -35
- data/lib/deep_cover/memoize.rb +48 -0
- data/lib/deep_cover/module_override.rb +2 -0
- data/lib/deep_cover/node.rb +14 -1
- data/lib/deep_cover/node/arguments.rb +2 -0
- data/lib/deep_cover/node/assignments.rb +32 -30
- data/lib/deep_cover/node/base.rb +30 -29
- data/lib/deep_cover/node/begin.rb +3 -1
- data/lib/deep_cover/node/block.rb +5 -2
- data/lib/deep_cover/node/branch.rb +2 -1
- data/lib/deep_cover/node/case.rb +15 -13
- data/lib/deep_cover/node/collections.rb +2 -0
- data/lib/deep_cover/node/const.rb +2 -0
- data/lib/deep_cover/node/def.rb +10 -8
- data/lib/deep_cover/node/empty_body.rb +2 -0
- data/lib/deep_cover/node/exceptions.rb +3 -1
- data/lib/deep_cover/node/if.rb +3 -1
- data/lib/deep_cover/node/keywords.rb +4 -2
- data/lib/deep_cover/node/literals.rb +2 -0
- data/lib/deep_cover/node/loops.rb +5 -3
- data/lib/deep_cover/node/mixin/can_augment_children.rb +8 -7
- data/lib/deep_cover/node/mixin/check_completion.rb +3 -1
- data/lib/deep_cover/node/mixin/child_can_be_empty.rb +4 -2
- data/lib/deep_cover/node/mixin/executed_after_children.rb +2 -0
- data/lib/deep_cover/node/mixin/execution_location.rb +4 -2
- data/lib/deep_cover/node/mixin/flow_accounting.rb +2 -0
- data/lib/deep_cover/node/mixin/has_child.rb +22 -18
- data/lib/deep_cover/node/mixin/has_child_handler.rb +10 -8
- data/lib/deep_cover/node/mixin/has_tracker.rb +4 -2
- data/lib/deep_cover/node/mixin/is_statement.rb +3 -1
- data/lib/deep_cover/node/mixin/rewriting.rb +5 -3
- data/lib/deep_cover/node/mixin/wrapper.rb +2 -0
- data/lib/deep_cover/node/module.rb +11 -9
- data/lib/deep_cover/node/root.rb +2 -0
- data/lib/deep_cover/node/send.rb +4 -2
- data/lib/deep_cover/node/short_circuit.rb +4 -2
- data/lib/deep_cover/node/splat.rb +2 -0
- data/lib/deep_cover/node/variables.rb +2 -0
- data/lib/deep_cover/parser_ext/range.rb +3 -1
- data/lib/deep_cover/problem_with_diagnostic.rb +11 -9
- data/lib/deep_cover/reporter.rb +2 -0
- data/lib/deep_cover/reporter/istanbul.rb +28 -24
- data/lib/deep_cover/tools.rb +2 -0
- data/lib/deep_cover/tools/builtin_coverage.rb +6 -4
- data/lib/deep_cover/tools/camelize.rb +3 -1
- data/lib/deep_cover/tools/dasherize.rb +3 -1
- data/lib/deep_cover/tools/dump_covered_code.rb +7 -6
- data/lib/deep_cover/tools/execute_sample.rb +13 -13
- data/lib/deep_cover/tools/format.rb +3 -1
- data/lib/deep_cover/tools/format_char_cover.rb +4 -2
- data/lib/deep_cover/tools/format_generated_code.rb +3 -1
- data/lib/deep_cover/tools/number_lines.rb +2 -0
- data/lib/deep_cover/tools/our_coverage.rb +5 -3
- data/lib/deep_cover/tools/profiling.rb +66 -0
- data/lib/deep_cover/tools/require_relative_dir.rb +3 -1
- data/lib/deep_cover/tools/silence_warnings.rb +4 -1
- data/lib/deep_cover/tools/slice.rb +3 -1
- data/lib/deep_cover/tools/truncate_backtrace.rb +2 -0
- data/lib/deep_cover/version.rb +3 -1
- metadata +47 -30
data/exe/deep-cover
CHANGED
data/lib/deep-cover.rb
CHANGED
data/lib/deep_cover.rb
CHANGED
data/lib/deep_cover/analyser.rb
CHANGED
@@ -1,13 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'subset'
|
2
4
|
|
3
5
|
module DeepCover
|
4
6
|
class Analyser::Branch < Analyser
|
5
7
|
include Analyser::Subset
|
6
|
-
SUBSET_CLASSES = [Node::Branch]
|
8
|
+
SUBSET_CLASSES = [Node::Branch].freeze
|
7
9
|
|
8
10
|
def results
|
9
11
|
each_node.map do |node, _children|
|
10
|
-
branches_runs = node.branches.map{|b| [b, node_runs(b)]}.to_h
|
12
|
+
branches_runs = node.branches.map { |b| [b, node_runs(b)] }.to_h
|
11
13
|
[node, branches_runs]
|
12
14
|
end.to_h
|
13
15
|
end
|
@@ -1,9 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module DeepCover
|
2
4
|
class Analyser::CoveredCodeSource < Analyser
|
3
5
|
attr_reader :covered_code
|
4
6
|
|
5
7
|
def initialize(covered_code)
|
6
|
-
@covered_code = covered_code
|
8
|
+
@covered_code = covered_code.freeze
|
7
9
|
end
|
8
10
|
|
9
11
|
# Looking exclusively at our subset of nodes, returns the node's direct descendants
|
@@ -1,9 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'subset'
|
2
4
|
|
3
5
|
module DeepCover
|
4
6
|
class Analyser::Function < Analyser
|
5
7
|
include Analyser::Subset
|
6
|
-
SUBSET_CLASSES = [Node::Block, Node::Defs, Node::Def]
|
8
|
+
SUBSET_CLASSES = [Node::Block, Node::Defs, Node::Def].freeze
|
7
9
|
|
8
10
|
def node_runs(node)
|
9
11
|
super(node.body)
|
@@ -1,16 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module DeepCover
|
2
4
|
module Analyser::IgnoreUncovered
|
3
5
|
def initialize(source, ignore_uncovered: [], **options)
|
4
6
|
super
|
5
7
|
@allow_filters = Array(ignore_uncovered)
|
6
|
-
|
7
|
-
|
8
|
-
|
8
|
+
.map { |kind| :"is_#{kind}?" }
|
9
|
+
.select { |name| respond_to?(name) }
|
10
|
+
.map { |name| method(name) } # So was tempted to write `.map(&method(:method))`!
|
9
11
|
end
|
10
12
|
|
11
13
|
def node_runs(node)
|
12
14
|
runs = super
|
13
|
-
if runs == 0 && @allow_filters.any?{ |f| f[node] }
|
15
|
+
if runs == 0 && @allow_filters.any? { |f| f[node] }
|
14
16
|
runs = nil
|
15
17
|
end
|
16
18
|
runs
|
@@ -1,14 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module DeepCover
|
2
4
|
module Analyser::OptionallyCovered
|
3
5
|
def optionally_covered
|
4
6
|
@optionally_covered ||= Analyser
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
7
|
+
.constants.map { |c| Analyser.const_get(c) }
|
8
|
+
.select { |klass| klass < Analyser }
|
9
|
+
.flat_map do |klass|
|
10
|
+
klass.instance_methods(false).map do |method|
|
11
|
+
method =~ /^is_(.*)\?$/
|
12
|
+
Regexp.last_match(1)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
.compact
|
16
|
+
.map(&:to_sym)
|
12
17
|
end
|
13
18
|
end
|
14
19
|
end
|
@@ -1,19 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module DeepCover
|
2
4
|
class Analyser::PerChar < Analyser
|
3
5
|
# Returns an array of characters for each line of code.
|
4
6
|
# Each character is either ' ' (executed), '-' (not executable) or 'x' (not covered)
|
5
7
|
def results
|
6
8
|
buffer = covered_code.buffer
|
7
|
-
bc = buffer.source_lines.map{|line| '-' * line.size}
|
9
|
+
bc = buffer.source_lines.map { |line| '-' * line.size }
|
8
10
|
each_node do |node, _children|
|
9
11
|
runs = node_runs(node)
|
10
|
-
if runs
|
11
|
-
|
12
|
-
|
13
|
-
end
|
12
|
+
next if runs == nil
|
13
|
+
node.proper_range.each do |pos|
|
14
|
+
bc[buffer.line_for_position(pos) - buffer.first_line][buffer.column_for_position(pos)] = runs > 0 ? ' ' : 'x'
|
14
15
|
end
|
15
16
|
end
|
16
|
-
bc.zip(buffer.source_lines){|cov, line| cov[line.size..-1] = ''} # remove extraneous character for end lines, in any
|
17
|
+
bc.zip(buffer.source_lines) { |cov, line| cov[line.size..-1] = '' } # remove extraneous character for end lines, in any
|
17
18
|
bc
|
18
19
|
end
|
19
20
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module DeepCover
|
2
4
|
class Analyser::PerLine < Analyser
|
3
5
|
# Returns an array of runs, one per line.
|
@@ -5,15 +7,14 @@ module DeepCover
|
|
5
7
|
disallow_partial = !options.fetch(:allow_partial, true)
|
6
8
|
line_hits = Array.new(covered_code.nb_lines + covered_code.lineno - 1)
|
7
9
|
each_node do |node, _children|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
end
|
15
|
-
line_hits[lineno] = [line_hits[lineno] || 0, runs].max
|
10
|
+
next unless (runs = node_runs(node))
|
11
|
+
node.executed_locs.each do |loc|
|
12
|
+
lineno = loc.line - 1
|
13
|
+
if disallow_partial
|
14
|
+
line_hits[lineno] = 0 if runs == 0
|
15
|
+
next if line_hits[lineno] == 0
|
16
16
|
end
|
17
|
+
line_hits[lineno] = [line_hits[lineno] || 0, runs].max
|
17
18
|
end
|
18
19
|
end
|
19
20
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module DeepCover
|
2
4
|
# A module to create a subset from a criteria called `in_subset?`
|
3
5
|
# Including classes can refine it, or specify SUBSET_CLASSES
|
@@ -7,6 +9,7 @@ module DeepCover
|
|
7
9
|
end
|
8
10
|
|
9
11
|
private
|
12
|
+
|
10
13
|
def find_children(from, parent = from)
|
11
14
|
@source.node_children(from).flat_map do |node|
|
12
15
|
if in_subset?(node, parent)
|
@@ -18,7 +21,7 @@ module DeepCover
|
|
18
21
|
end
|
19
22
|
|
20
23
|
def in_subset?(node, _parent)
|
21
|
-
self.class::SUBSET_CLASSES.any?{|klass| node.is_a?(klass)}
|
24
|
+
self.class::SUBSET_CLASSES.any? { |klass| node.is_a?(klass) }
|
22
25
|
end
|
23
26
|
end
|
24
27
|
end
|
data/lib/deep_cover/auto_run.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'weakref'
|
2
4
|
|
3
5
|
module DeepCover
|
@@ -10,7 +12,7 @@ module DeepCover
|
|
10
12
|
ext = File.extname(path)
|
11
13
|
# We don't care about .so files
|
12
14
|
return if ext == '.so'
|
13
|
-
path
|
15
|
+
path += '.rb' if ext != '.rb'
|
14
16
|
|
15
17
|
pairs = @autoloaded_paths[path] ||= []
|
16
18
|
pairs << [WeakRef.new(const), name]
|
@@ -21,7 +23,7 @@ module DeepCover
|
|
21
23
|
|
22
24
|
paths.flat_map do |path|
|
23
25
|
pairs = @autoloaded_paths[path] || []
|
24
|
-
pairs = pairs.map{|weak_const, name| [self.class.value_from_weak_ref(weak_const), name] }
|
26
|
+
pairs = pairs.map { |weak_const, name| [self.class.value_from_weak_ref(weak_const), name] }
|
25
27
|
pairs.select!(&:first)
|
26
28
|
pairs
|
27
29
|
end
|
@@ -49,9 +51,10 @@ module DeepCover
|
|
49
51
|
|
50
52
|
def initialize_autoloaded_paths
|
51
53
|
@autoloaded_paths = {}
|
54
|
+
# This is only used on MRI, so ObjectSpace is alright.
|
52
55
|
ObjectSpace.each_object(Module) do |mod|
|
53
56
|
mod.constants.each do |name|
|
54
|
-
if path = mod.autoload?(name)
|
57
|
+
if (path = mod.autoload?(name))
|
55
58
|
add(mod, name, path)
|
56
59
|
end
|
57
60
|
end
|
data/lib/deep_cover/backports.rb
CHANGED
data/lib/deep_cover/base.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module DeepCover
|
2
4
|
module Base
|
3
5
|
def start
|
@@ -35,12 +37,19 @@ module DeepCover
|
|
35
37
|
stop
|
36
38
|
end
|
37
39
|
|
40
|
+
def config_changed(what)
|
41
|
+
if what == :paths
|
42
|
+
warn "Changing DeepCover's paths after starting coverage is highly discouraged" if @started
|
43
|
+
@custom_requirer = nil
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
38
47
|
def coverage
|
39
48
|
@coverage ||= Coverage.new
|
40
49
|
end
|
41
50
|
|
42
51
|
def custom_requirer
|
43
|
-
@custom_requirer ||= CustomRequirer.new
|
52
|
+
@custom_requirer ||= CustomRequirer.new(lookup_paths: config.paths)
|
44
53
|
end
|
45
54
|
|
46
55
|
def autoload_tracker
|
@@ -49,7 +58,7 @@ module DeepCover
|
|
49
58
|
|
50
59
|
def handle_relative_filename(filename)
|
51
60
|
unless Pathname.new(filename).absolute?
|
52
|
-
relative_to = File.dirname(caller
|
61
|
+
relative_to = File.dirname(caller(2..2).first.partition(/\.rb:\d/).first)
|
53
62
|
filename = File.absolute_path(filename, relative_to)
|
54
63
|
end
|
55
64
|
filename += '.rb' unless filename =~ /\.rb$/
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module DeepCover
|
2
4
|
module CLI
|
3
5
|
class Debugger
|
@@ -6,15 +8,15 @@ module DeepCover
|
|
6
8
|
module ColorAST
|
7
9
|
def fancy_type
|
8
10
|
color = case
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
11
|
+
when !executable?
|
12
|
+
:faint
|
13
|
+
when !was_executed?
|
14
|
+
:red
|
15
|
+
when flow_interrupt_count > 0
|
16
|
+
:yellow
|
17
|
+
else
|
18
|
+
:green
|
19
|
+
end
|
18
20
|
Term::ANSIColor.send(color, super)
|
19
21
|
end
|
20
22
|
end
|
@@ -29,46 +31,51 @@ module DeepCover
|
|
29
31
|
end
|
30
32
|
|
31
33
|
def show
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
34
|
+
Tools.profile(options[:profile]) do
|
35
|
+
execute
|
36
|
+
covered_code.freeze # Our output relies on the counts, so better freeze. See [#13]
|
37
|
+
if @debug
|
38
|
+
show_line_coverage
|
39
|
+
show_instrumented_code
|
40
|
+
show_ast
|
41
|
+
end
|
42
|
+
show_char_coverage
|
37
43
|
end
|
38
|
-
show_char_coverage
|
39
44
|
pry if @debug
|
40
45
|
finish
|
41
46
|
end
|
42
47
|
|
43
48
|
def show_line_coverage
|
44
|
-
|
49
|
+
output { "Line Coverage: Builtin | DeepCover | DeepCover Strict:\n" }
|
45
50
|
begin
|
46
51
|
builtin_line_coverage = builtin_coverage(@source, @filename, @lineno)
|
47
52
|
our_line_coverage = our_coverage(@source, @filename, @lineno, **options)
|
48
53
|
our_strict_line_coverage = our_coverage(@source, @filename, @lineno, allow_partial: false, **options)
|
49
|
-
|
50
|
-
|
54
|
+
output do
|
55
|
+
lines = format(builtin_line_coverage, our_line_coverage, our_strict_line_coverage, source: @source)
|
56
|
+
number_lines(lines, lineno: @lineno)
|
57
|
+
end
|
51
58
|
rescue Exception => e
|
52
|
-
|
59
|
+
output { "Can't run coverage: #{e.class.name}: #{e}\n#{e.backtrace.join("\n")}" }
|
53
60
|
@failed = true
|
54
61
|
end
|
55
62
|
end
|
56
63
|
|
57
64
|
def show_instrumented_code
|
58
|
-
|
59
|
-
|
65
|
+
output { "\nInstrumented code:\n" }
|
66
|
+
output { format_generated_code(covered_code) }
|
60
67
|
end
|
61
68
|
|
62
69
|
def show_ast
|
63
|
-
|
70
|
+
output { "\nParsed code:\n" }
|
64
71
|
Node.prepend ColorAST
|
65
|
-
|
72
|
+
output { covered_code.covered_ast }
|
66
73
|
end
|
67
74
|
|
68
75
|
def show_char_coverage
|
69
|
-
|
76
|
+
output { "\nChar coverage:\n" }
|
70
77
|
|
71
|
-
|
78
|
+
output { format_char_cover(covered_code, show_whitespace: !!ENV['W'], **options) }
|
72
79
|
end
|
73
80
|
|
74
81
|
def pry
|
@@ -86,11 +93,29 @@ module DeepCover
|
|
86
93
|
end
|
87
94
|
|
88
95
|
def execute
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
96
|
+
execute_sample(covered_code)
|
97
|
+
# output { trace_counts } # Keep for low-level debugging purposes
|
98
|
+
rescue Exception => e
|
99
|
+
output { "Can't `execute_sample`:#{e.class.name}: #{e}\n#{e.backtrace.join("\n")}" }
|
100
|
+
@failed = true
|
101
|
+
end
|
102
|
+
|
103
|
+
def trace_counts
|
104
|
+
all = []
|
105
|
+
trace = TracePoint.new(:call) do |tr|
|
106
|
+
if %i[flow_entry_count flow_completion_count execution_count].include? tr.method_id
|
107
|
+
node = tr.self
|
108
|
+
str = "#{node.type} #{(node.value if node.respond_to?(:value))} #{tr.method_id}"
|
109
|
+
all << str unless all.last == str
|
110
|
+
end
|
111
|
+
end
|
112
|
+
trace.enable { covered_code.freeze }
|
113
|
+
all
|
114
|
+
end
|
115
|
+
|
116
|
+
def output
|
117
|
+
Tools.dont_profile do
|
118
|
+
puts yield
|
94
119
|
end
|
95
120
|
end
|
96
121
|
end
|