deep-cover 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|