deep-cover 0.1.1
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 +7 -0
- data/.gitignore +13 -0
- data/.rspec +2 -0
- data/.travis.yml +10 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +21 -0
- data/README.md +127 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/cov +43 -0
- data/bin/gemcov +8 -0
- data/bin/selfcov +21 -0
- data/bin/setup +8 -0
- data/bin/testall +88 -0
- data/deep_cover.gemspec +44 -0
- data/exe/deep-cover +6 -0
- data/future_read_me.md +108 -0
- data/lib/deep-cover.rb +1 -0
- data/lib/deep_cover.rb +11 -0
- data/lib/deep_cover/analyser.rb +24 -0
- data/lib/deep_cover/analyser/base.rb +51 -0
- data/lib/deep_cover/analyser/branch.rb +20 -0
- data/lib/deep_cover/analyser/covered_code_source.rb +31 -0
- data/lib/deep_cover/analyser/function.rb +12 -0
- data/lib/deep_cover/analyser/ignore_uncovered.rb +19 -0
- data/lib/deep_cover/analyser/node.rb +11 -0
- data/lib/deep_cover/analyser/per_char.rb +20 -0
- data/lib/deep_cover/analyser/per_line.rb +23 -0
- data/lib/deep_cover/analyser/statement.rb +31 -0
- data/lib/deep_cover/analyser/subset.rb +24 -0
- data/lib/deep_cover/auto_run.rb +49 -0
- data/lib/deep_cover/autoload_tracker.rb +75 -0
- data/lib/deep_cover/backports.rb +9 -0
- data/lib/deep_cover/base.rb +55 -0
- data/lib/deep_cover/builtin_takeover.rb +2 -0
- data/lib/deep_cover/cli/debugger.rb +93 -0
- data/lib/deep_cover/cli/deep_cover.rb +49 -0
- data/lib/deep_cover/cli/instrumented_clone_reporter.rb +105 -0
- data/lib/deep_cover/config.rb +52 -0
- data/lib/deep_cover/core_ext/autoload_overrides.rb +40 -0
- data/lib/deep_cover/core_ext/coverage_replacement.rb +26 -0
- data/lib/deep_cover/core_ext/load_overrides.rb +24 -0
- data/lib/deep_cover/core_ext/require_overrides.rb +36 -0
- data/lib/deep_cover/coverage.rb +198 -0
- data/lib/deep_cover/covered_code.rb +138 -0
- data/lib/deep_cover/custom_requirer.rb +93 -0
- data/lib/deep_cover/node.rb +8 -0
- data/lib/deep_cover/node/arguments.rb +50 -0
- data/lib/deep_cover/node/assignments.rb +250 -0
- data/lib/deep_cover/node/base.rb +99 -0
- data/lib/deep_cover/node/begin.rb +25 -0
- data/lib/deep_cover/node/block.rb +53 -0
- data/lib/deep_cover/node/boolean.rb +22 -0
- data/lib/deep_cover/node/branch.rb +28 -0
- data/lib/deep_cover/node/case.rb +94 -0
- data/lib/deep_cover/node/collections.rb +21 -0
- data/lib/deep_cover/node/const.rb +10 -0
- data/lib/deep_cover/node/def.rb +38 -0
- data/lib/deep_cover/node/empty_body.rb +21 -0
- data/lib/deep_cover/node/exceptions.rb +74 -0
- data/lib/deep_cover/node/if.rb +36 -0
- data/lib/deep_cover/node/keywords.rb +84 -0
- data/lib/deep_cover/node/literals.rb +77 -0
- data/lib/deep_cover/node/loops.rb +72 -0
- data/lib/deep_cover/node/mixin/can_augment_children.rb +65 -0
- data/lib/deep_cover/node/mixin/check_completion.rb +16 -0
- data/lib/deep_cover/node/mixin/child_can_be_empty.rb +25 -0
- data/lib/deep_cover/node/mixin/executed_after_children.rb +13 -0
- data/lib/deep_cover/node/mixin/execution_location.rb +56 -0
- data/lib/deep_cover/node/mixin/flow_accounting.rb +63 -0
- data/lib/deep_cover/node/mixin/has_child.rb +138 -0
- data/lib/deep_cover/node/mixin/has_child_handler.rb +73 -0
- data/lib/deep_cover/node/mixin/has_tracker.rb +44 -0
- data/lib/deep_cover/node/mixin/is_statement.rb +18 -0
- data/lib/deep_cover/node/mixin/rewriting.rb +32 -0
- data/lib/deep_cover/node/mixin/wrapper.rb +13 -0
- data/lib/deep_cover/node/module.rb +64 -0
- data/lib/deep_cover/node/root.rb +18 -0
- data/lib/deep_cover/node/send.rb +83 -0
- data/lib/deep_cover/node/splat.rb +13 -0
- data/lib/deep_cover/node/variables.rb +14 -0
- data/lib/deep_cover/parser_ext/range.rb +40 -0
- data/lib/deep_cover/reporter.rb +6 -0
- data/lib/deep_cover/reporter/istanbul.rb +151 -0
- data/lib/deep_cover/tools.rb +18 -0
- data/lib/deep_cover/tools/builtin_coverage.rb +50 -0
- data/lib/deep_cover/tools/camelize.rb +8 -0
- data/lib/deep_cover/tools/dump_covered_code.rb +32 -0
- data/lib/deep_cover/tools/execute_sample.rb +23 -0
- data/lib/deep_cover/tools/format.rb +16 -0
- data/lib/deep_cover/tools/format_char_cover.rb +18 -0
- data/lib/deep_cover/tools/format_generated_code.rb +25 -0
- data/lib/deep_cover/tools/number_lines.rb +18 -0
- data/lib/deep_cover/tools/our_coverage.rb +9 -0
- data/lib/deep_cover/tools/require_relative_dir.rb +10 -0
- data/lib/deep_cover/tools/silence_warnings.rb +15 -0
- data/lib/deep_cover/version.rb +3 -0
- metadata +326 -0
@@ -0,0 +1,73 @@
|
|
1
|
+
module DeepCover
|
2
|
+
module Node::Mixin
|
3
|
+
module HasChildHandler
|
4
|
+
def self.included(base)
|
5
|
+
base.extend ClassMethods
|
6
|
+
end
|
7
|
+
|
8
|
+
def call_child_handler template, child, child_name = nil
|
9
|
+
child_name ||= self.class.child_index_to_name(child.index, children.size) rescue binding.pry
|
10
|
+
method_name = template % {name: child_name}
|
11
|
+
if respond_to?(method_name)
|
12
|
+
args = [child, child_name]
|
13
|
+
arity = method(method_name).arity
|
14
|
+
if arity >= 0
|
15
|
+
args = args[0...arity]
|
16
|
+
end
|
17
|
+
answer = send(method_name, *args)
|
18
|
+
end
|
19
|
+
answer
|
20
|
+
end
|
21
|
+
private :call_child_handler
|
22
|
+
|
23
|
+
module ClassMethods
|
24
|
+
def has_child_handler(template)
|
25
|
+
child_method_name = template % {name: 'child'}
|
26
|
+
action = template.gsub(/_%{name}/, '').gsub(/%{name}_/, '')
|
27
|
+
const_name = "#{Tools.camelize(action)}Handler"
|
28
|
+
class_eval <<-end_eval, __FILE__, __LINE__
|
29
|
+
module #{const_name} # module RewriteHandler
|
30
|
+
module ClassMethods # module ClassMethods
|
31
|
+
def has_child(#{action}: nil, **h) # def has_child(rewrite: nil, **h)
|
32
|
+
name, _types = h.first # name, _types = h.first
|
33
|
+
define_child_handler(#{template.inspect}, # define_child_handler('rewrite_%{child}',
|
34
|
+
name, #{action}) # name, rewrite)
|
35
|
+
super(**h) # super(**h)
|
36
|
+
end # end
|
37
|
+
end # end
|
38
|
+
|
39
|
+
def #{child_method_name}(child, name = nil) # def rewrite_child(child, name = nil)
|
40
|
+
call_child_handler(#{template.inspect}, child, # call_child_handler('rewrite_%{child}', child,
|
41
|
+
name) || super # name) || super
|
42
|
+
end # end
|
43
|
+
end # end
|
44
|
+
include #{const_name} # include RewriteHandler
|
45
|
+
singleton_class.prepend #{const_name}::ClassMethods # singleton_class.prepend RewriteHandler::ClassMethods
|
46
|
+
end_eval
|
47
|
+
end
|
48
|
+
|
49
|
+
def define_child_handler(template, name, action)
|
50
|
+
method_name = template % {name: name}
|
51
|
+
case action
|
52
|
+
when nil
|
53
|
+
# Nothing to do
|
54
|
+
when Symbol
|
55
|
+
define_method(method_name) do |*args|
|
56
|
+
arity = method(action).arity
|
57
|
+
if arity < 0
|
58
|
+
send(action, *args)
|
59
|
+
else
|
60
|
+
send(action, *args[0...arity])
|
61
|
+
end
|
62
|
+
end
|
63
|
+
when Proc
|
64
|
+
define_method(method_name, &action)
|
65
|
+
else
|
66
|
+
define_method(method_name) {|*| action }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
private :define_child_handler
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module DeepCover
|
2
|
+
module Node::Mixin
|
3
|
+
module HasTracker
|
4
|
+
def self.included(base)
|
5
|
+
base.extend ClassMethods
|
6
|
+
setup_constants(base)
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.setup_constants(base)
|
10
|
+
base.const_set :TRACKERS, {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(*)
|
14
|
+
@tracker_offset = covered_code.allocate_trackers(self.class::TRACKERS.size).begin
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
18
|
+
def tracker_sources
|
19
|
+
self.class::TRACKERS.map do |name, _|
|
20
|
+
[:"#{name}_tracker", send(:"#{name}_tracker_source")]
|
21
|
+
end.to_h
|
22
|
+
end
|
23
|
+
|
24
|
+
module ClassMethods
|
25
|
+
def inherited(base)
|
26
|
+
super
|
27
|
+
HasTracker.setup_constants(base)
|
28
|
+
end
|
29
|
+
|
30
|
+
def has_tracker(name)
|
31
|
+
i = self::TRACKERS[name] = self::TRACKERS.size
|
32
|
+
class_eval <<-end_eval, __FILE__, __LINE__ + 1
|
33
|
+
def #{name}_tracker_source
|
34
|
+
covered_code.tracker_source(@tracker_offset + #{i})
|
35
|
+
end
|
36
|
+
def #{name}_tracker_hits
|
37
|
+
covered_code.tracker_hits(@tracker_offset + #{i})
|
38
|
+
end
|
39
|
+
end_eval
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module DeepCover
|
2
|
+
module Node::Mixin
|
3
|
+
module IsStatement
|
4
|
+
def self.included(base)
|
5
|
+
base.has_child_handler('is_%{name}_statement')
|
6
|
+
end
|
7
|
+
|
8
|
+
def is_statement
|
9
|
+
parent.is_child_statement(self)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Default child rewriting rule
|
13
|
+
def is_child_statement(child, name=nil)
|
14
|
+
:if_incompatible
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module DeepCover
|
2
|
+
module Node::Mixin
|
3
|
+
module Rewriting
|
4
|
+
def self.included(base)
|
5
|
+
base.has_child_handler('rewrite_%{name}')
|
6
|
+
end
|
7
|
+
|
8
|
+
# Code to add before and after the node for covering purposes
|
9
|
+
def rewrite
|
10
|
+
end
|
11
|
+
|
12
|
+
# Default child rewriting rule
|
13
|
+
def rewrite_child(child, name=nil)
|
14
|
+
end
|
15
|
+
|
16
|
+
def resolve_rewrite(rule, context)
|
17
|
+
rule ||= '%{node}'
|
18
|
+
sources = context.tracker_sources
|
19
|
+
rule.split('%{node}').map{|s| s % {local: covered_code.local_var, **sources} }
|
20
|
+
end
|
21
|
+
|
22
|
+
def rewrite_prefix_suffix
|
23
|
+
parent_prefix, parent_suffix = resolve_rewrite(parent.rewrite_child(self), parent)
|
24
|
+
prefix, suffix = resolve_rewrite(rewrite, self)
|
25
|
+
[
|
26
|
+
"#{parent_prefix}#{prefix}",
|
27
|
+
"#{suffix}#{parent_suffix}"
|
28
|
+
]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require_relative 'const'
|
2
|
+
|
3
|
+
module DeepCover
|
4
|
+
class Node
|
5
|
+
class ModuleName < Node
|
6
|
+
has_child scope: [Node, nil]
|
7
|
+
has_child const_name: Symbol
|
8
|
+
|
9
|
+
def flow_completion_count
|
10
|
+
parent.execution_count
|
11
|
+
end
|
12
|
+
|
13
|
+
def execution_count
|
14
|
+
if scope
|
15
|
+
scope.flow_completion_count
|
16
|
+
else
|
17
|
+
super
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class Module < Node
|
23
|
+
check_completion
|
24
|
+
has_tracker :body_entry
|
25
|
+
has_child const: {const: ModuleName}
|
26
|
+
has_child body: Node,
|
27
|
+
can_be_empty: -> { base_node.loc.end.begin },
|
28
|
+
rewrite: '%{body_entry_tracker};%{local}=nil;%{node}',
|
29
|
+
is_statement: true,
|
30
|
+
flow_entry_count: :body_entry_tracker_hits
|
31
|
+
executed_loc_keys :keyword
|
32
|
+
|
33
|
+
def execution_count
|
34
|
+
body_entry_tracker_hits
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class Class < Node
|
39
|
+
check_completion
|
40
|
+
has_tracker :body_entry
|
41
|
+
has_child const: {const: ModuleName}
|
42
|
+
has_child inherit: [Node, nil] # TODO
|
43
|
+
has_child body: Node,
|
44
|
+
can_be_empty: -> { base_node.loc.end.begin },
|
45
|
+
rewrite: '%{body_entry_tracker};%{node}',
|
46
|
+
is_statement: true,
|
47
|
+
flow_entry_count: :body_entry_tracker_hits
|
48
|
+
executed_loc_keys :keyword
|
49
|
+
|
50
|
+
def execution_count
|
51
|
+
body_entry_tracker_hits
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# class << foo
|
56
|
+
class Sclass < Node
|
57
|
+
has_child object: Node
|
58
|
+
has_child body: Node,
|
59
|
+
can_be_empty: -> { base_node.loc.end.begin },
|
60
|
+
is_statement: true
|
61
|
+
# TODO
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module DeepCover
|
2
|
+
class Node::Root < Node
|
3
|
+
has_tracker :root
|
4
|
+
has_child main: Node,
|
5
|
+
can_be_empty: -> { ::Parser::Source::Range.new(covered_code.buffer, 0, 0) },
|
6
|
+
is_statement: true,
|
7
|
+
rewrite: -> {
|
8
|
+
"#{covered_code.trackers_setup_source};%{root_tracker};%{local}=nil;%{node}"
|
9
|
+
}
|
10
|
+
attr_reader :covered_code
|
11
|
+
alias_method :flow_entry_count, :root_tracker_hits
|
12
|
+
|
13
|
+
def initialize(child_ast, covered_code)
|
14
|
+
@covered_code = covered_code
|
15
|
+
super(nil, parent: nil, base_children: [child_ast])
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require_relative 'literals'
|
2
|
+
|
3
|
+
module DeepCover
|
4
|
+
class Node
|
5
|
+
class MethodName < Node
|
6
|
+
has_child name: Symbol
|
7
|
+
|
8
|
+
def initialize(name, parent: raise, **kwargs)
|
9
|
+
super(parent, **kwargs, parent: parent, base_children: [name])
|
10
|
+
end
|
11
|
+
|
12
|
+
def loc_hash
|
13
|
+
# Expression is used in the rewriting
|
14
|
+
# if selector_end is present, then this won't be needed
|
15
|
+
{expression: parent.loc_hash[:selector_begin]}
|
16
|
+
end
|
17
|
+
|
18
|
+
def executable?
|
19
|
+
false
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class Send < Node
|
24
|
+
check_completion
|
25
|
+
has_child receiver: [Node, nil]
|
26
|
+
has_child method_name_wrapper: {Symbol => MethodName}, rewrite: :add_opening_parentheses
|
27
|
+
has_extra_children arguments: Node, rewrite: :add_closing_parentheses
|
28
|
+
executed_loc_keys :dot, :selector_begin, :selector_end, :operator
|
29
|
+
|
30
|
+
def method_name
|
31
|
+
method_name_wrapper.name
|
32
|
+
end
|
33
|
+
|
34
|
+
def loc_hash
|
35
|
+
base = super
|
36
|
+
hash = { expression: base[:expression], begin: base[:begin], end: base[:end], dot: base[:dot]}
|
37
|
+
selector = base[:selector]
|
38
|
+
|
39
|
+
if [:[], :[]=].include?(method_name)
|
40
|
+
hash[:selector_begin] = selector.resize(1)
|
41
|
+
hash[:selector_end] = Parser::Source::Range.new(selector.source_buffer, selector.end_pos - 1, selector.end_pos)
|
42
|
+
else
|
43
|
+
hash[:selector_begin] = base[:selector]
|
44
|
+
end
|
45
|
+
|
46
|
+
hash
|
47
|
+
end
|
48
|
+
|
49
|
+
# Only need to add them to deal with ambiguous cases where a method is hidden by a local. Ex:
|
50
|
+
# raise TypeError, 'hello' #=> Works
|
51
|
+
# raise (TypeError), 'hello' #=> Simplification of what DeepCover generates, still works
|
52
|
+
# raise = 1; raise TypeError, 'hello' #=> works
|
53
|
+
# raise = 1; raise (TypeError), 'hello' #=> syntax error.
|
54
|
+
# raise = 1; raise((TypeError), 'hello'0 #=> works
|
55
|
+
def add_parentheses?
|
56
|
+
return if arguments.empty?
|
57
|
+
# No ambiguity if there is a receiver
|
58
|
+
return if receiver
|
59
|
+
# Already has parentheses
|
60
|
+
return if self.loc_hash[:begin]
|
61
|
+
true
|
62
|
+
end
|
63
|
+
|
64
|
+
def add_opening_parentheses
|
65
|
+
return unless add_parentheses?
|
66
|
+
"%{node}("
|
67
|
+
end
|
68
|
+
|
69
|
+
def add_closing_parentheses(child)
|
70
|
+
return unless add_parentheses?
|
71
|
+
return unless child.index == children.size - 1
|
72
|
+
"%{node})"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
class MatchWithLvasgn < Node
|
77
|
+
check_completion
|
78
|
+
has_child receiver: Regexp
|
79
|
+
has_child compare_to: Node
|
80
|
+
# TODO: test
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module DeepCover
|
2
|
+
class Node
|
3
|
+
class Splat < Node
|
4
|
+
check_completion inner: '[%{node}]', outer: '*%{node}'
|
5
|
+
has_child receiver: Node
|
6
|
+
end
|
7
|
+
|
8
|
+
class Kwsplat < Node
|
9
|
+
check_completion inner: '{%{node}}', outer: '**%{node}'
|
10
|
+
has_child receiver: Node
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
class Parser::Source::Range
|
2
|
+
def with(begin_pos: @begin_pos, end_pos: @end_pos)
|
3
|
+
Parser::Source::Range.new(@source_buffer, begin_pos, end_pos)
|
4
|
+
end
|
5
|
+
|
6
|
+
# Similar to `end`, but is just after the current Range
|
7
|
+
def succ
|
8
|
+
with begin_pos: @end_pos+1, end_pos: @end_pos+1
|
9
|
+
end
|
10
|
+
|
11
|
+
# (1...10).split(2...3, 6...8) => [1...2, 3...6, 7...10]
|
12
|
+
# Assumes inner_ranges are exclusive, and included in self
|
13
|
+
def split(*inner_ranges)
|
14
|
+
inner_ranges.sort_by!(&:begin_pos)
|
15
|
+
[self.begin, *inner_ranges, self.end]
|
16
|
+
.each_cons(2)
|
17
|
+
.map{|i, j| with(begin_pos: i.end_pos, end_pos: j.begin_pos)}
|
18
|
+
.reject(&:empty?)
|
19
|
+
end
|
20
|
+
|
21
|
+
def lstrip(pattern = /\s*/)
|
22
|
+
if (match = /^#{pattern}/.match(source))
|
23
|
+
with(begin_pos: @begin_pos + match[0].length)
|
24
|
+
else
|
25
|
+
self
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def rstrip(pattern = /\s*/)
|
30
|
+
if (match = /#{pattern}$/.match(source))
|
31
|
+
with(end_pos: @end_pos - match[0].length)
|
32
|
+
else
|
33
|
+
self
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def strip(pattern = /\s*/)
|
38
|
+
lstrip(pattern).rstrip(pattern)
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module DeepCover
|
4
|
+
module Reporter
|
5
|
+
class Istanbul < Struct.new(:covered_code, :options)
|
6
|
+
# Converters has no dependency on the including class.
|
7
|
+
module Converters
|
8
|
+
def convert_range(range)
|
9
|
+
{ start: {
|
10
|
+
line: range.line,
|
11
|
+
column: range.column,
|
12
|
+
},
|
13
|
+
end: {
|
14
|
+
line: range.last_line,
|
15
|
+
column: range.last_column-1, # Our ranges are exclusive, Istanbul's are inclusive
|
16
|
+
},
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
# [:a, :b, :c] => {'1': :a, '2': :b, '3': :c}
|
21
|
+
def convert_list(list)
|
22
|
+
list.map.with_index{ |val, i| [i.succ.to_s, val] }.to_h
|
23
|
+
end
|
24
|
+
|
25
|
+
def convert_def(node)
|
26
|
+
ends_at = node.signature.loc_hash[:end] || node.loc_hash[:name]
|
27
|
+
decl = node.loc_hash[:keyword].with(end_pos: ends_at.end_pos)
|
28
|
+
_convert_function(node, node.method_name, decl)
|
29
|
+
end
|
30
|
+
|
31
|
+
def convert_block(node)
|
32
|
+
decl = node.loc_hash[:begin]
|
33
|
+
if (args = node.args.expression)
|
34
|
+
decl = decl.join(args) rescue binding.pry
|
35
|
+
end
|
36
|
+
_convert_function(node, '(block)', decl)
|
37
|
+
end
|
38
|
+
|
39
|
+
def convert_function(node)
|
40
|
+
if node.is_a?(Node::Block)
|
41
|
+
convert_block(node)
|
42
|
+
else
|
43
|
+
convert_def(node)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def convert_branch(node, branches = node.branches)
|
48
|
+
# Currently, nyc seems to outputs the same location over and over...
|
49
|
+
loc = convert_range(node.expression)
|
50
|
+
{
|
51
|
+
loc: loc,
|
52
|
+
type: node.type,
|
53
|
+
line: node.expression.line,
|
54
|
+
locations: branches.map{|n| loc}
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
def _convert_function(node, name, decl)
|
60
|
+
loc = node.body ? node.body.expression : decl.end
|
61
|
+
{
|
62
|
+
name: name,
|
63
|
+
line: node.expression.line,
|
64
|
+
decl: convert_range(decl),
|
65
|
+
loc: convert_range(loc),
|
66
|
+
}
|
67
|
+
end
|
68
|
+
end
|
69
|
+
include Converters
|
70
|
+
|
71
|
+
def node_analyser
|
72
|
+
@node_analyser ||= Analyser::Node.new(covered_code, **options)
|
73
|
+
end
|
74
|
+
|
75
|
+
def node_runs
|
76
|
+
@node_runs ||= node_analyser.results
|
77
|
+
end
|
78
|
+
|
79
|
+
def functions
|
80
|
+
@functions ||= Analyser::Function.new(node_analyser, **options).results
|
81
|
+
end
|
82
|
+
|
83
|
+
def statements
|
84
|
+
@statements ||= Analyser::Statement.new(node_analyser, **options).results
|
85
|
+
end
|
86
|
+
|
87
|
+
def branches
|
88
|
+
@branches ||= Analyser::Branch.new(node_analyser, **options).results
|
89
|
+
end
|
90
|
+
|
91
|
+
def branch_map
|
92
|
+
branches.map do |node, branches_runs|
|
93
|
+
convert_branch(node, branches_runs.keys)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Istanbul doesn't understand how to ignore a branch...
|
98
|
+
def zero_to_something(values)
|
99
|
+
values.map{|v| v || 1}
|
100
|
+
end
|
101
|
+
|
102
|
+
def branch_runs
|
103
|
+
branches.values.map{|r| zero_to_something(r.values) }
|
104
|
+
end
|
105
|
+
|
106
|
+
def statement_map
|
107
|
+
statements.keys.map{ |range| convert_range(range) }
|
108
|
+
end
|
109
|
+
|
110
|
+
def statement_runs
|
111
|
+
statements.values
|
112
|
+
end
|
113
|
+
|
114
|
+
def function_map
|
115
|
+
functions.keys.map{|n| convert_function(n) }
|
116
|
+
end
|
117
|
+
|
118
|
+
def function_runs
|
119
|
+
functions.values
|
120
|
+
end
|
121
|
+
|
122
|
+
def data
|
123
|
+
{
|
124
|
+
statementMap: statement_map ,
|
125
|
+
s: statement_runs,
|
126
|
+
fnMap: function_map ,
|
127
|
+
f: function_runs ,
|
128
|
+
branchMap: branch_map ,
|
129
|
+
b: branch_runs ,
|
130
|
+
}
|
131
|
+
end
|
132
|
+
|
133
|
+
def convert
|
134
|
+
{ covered_code.name => {
|
135
|
+
path: covered_code.path,
|
136
|
+
**data.transform_values{|l| convert_list(l)},
|
137
|
+
} }
|
138
|
+
end
|
139
|
+
|
140
|
+
def report
|
141
|
+
convert.to_json
|
142
|
+
end
|
143
|
+
|
144
|
+
class << self
|
145
|
+
def available?
|
146
|
+
`nyc --version` >= '11.' rescue false
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|