deep-cover-core 0.6.3.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (131) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +4 -0
  3. data/.rspec_all +3 -0
  4. data/.rubocop.yml +1 -0
  5. data/Gemfile +11 -0
  6. data/deep_cover_core.gemspec +46 -0
  7. data/lib/deep-cover.rb +3 -0
  8. data/lib/deep_cover/analyser/base.rb +104 -0
  9. data/lib/deep_cover/analyser/branch.rb +41 -0
  10. data/lib/deep_cover/analyser/covered_code_source.rb +21 -0
  11. data/lib/deep_cover/analyser/function.rb +14 -0
  12. data/lib/deep_cover/analyser/node.rb +54 -0
  13. data/lib/deep_cover/analyser/per_char.rb +38 -0
  14. data/lib/deep_cover/analyser/per_line.rb +41 -0
  15. data/lib/deep_cover/analyser/ruby25_like_branch.rb +211 -0
  16. data/lib/deep_cover/analyser/statement.rb +33 -0
  17. data/lib/deep_cover/analyser/stats.rb +54 -0
  18. data/lib/deep_cover/analyser/subset.rb +27 -0
  19. data/lib/deep_cover/analyser.rb +23 -0
  20. data/lib/deep_cover/auto_run.rb +71 -0
  21. data/lib/deep_cover/autoload_tracker.rb +215 -0
  22. data/lib/deep_cover/backports.rb +22 -0
  23. data/lib/deep_cover/base.rb +117 -0
  24. data/lib/deep_cover/basics.rb +22 -0
  25. data/lib/deep_cover/builtin_takeover.rb +10 -0
  26. data/lib/deep_cover/config.rb +104 -0
  27. data/lib/deep_cover/config_setter.rb +33 -0
  28. data/lib/deep_cover/core_ext/autoload_overrides.rb +112 -0
  29. data/lib/deep_cover/core_ext/coverage_replacement.rb +61 -0
  30. data/lib/deep_cover/core_ext/exec_callbacks.rb +27 -0
  31. data/lib/deep_cover/core_ext/instruction_sequence_load_iseq.rb +32 -0
  32. data/lib/deep_cover/core_ext/load_overrides.rb +19 -0
  33. data/lib/deep_cover/core_ext/require_overrides.rb +28 -0
  34. data/lib/deep_cover/coverage/analysis.rb +42 -0
  35. data/lib/deep_cover/coverage/persistence.rb +84 -0
  36. data/lib/deep_cover/coverage.rb +125 -0
  37. data/lib/deep_cover/covered_code.rb +145 -0
  38. data/lib/deep_cover/custom_requirer.rb +187 -0
  39. data/lib/deep_cover/flag_comment_associator.rb +68 -0
  40. data/lib/deep_cover/load.rb +66 -0
  41. data/lib/deep_cover/memoize.rb +48 -0
  42. data/lib/deep_cover/module_override.rb +39 -0
  43. data/lib/deep_cover/node/arguments.rb +51 -0
  44. data/lib/deep_cover/node/assignments.rb +273 -0
  45. data/lib/deep_cover/node/base.rb +155 -0
  46. data/lib/deep_cover/node/begin.rb +27 -0
  47. data/lib/deep_cover/node/block.rb +61 -0
  48. data/lib/deep_cover/node/branch.rb +32 -0
  49. data/lib/deep_cover/node/case.rb +113 -0
  50. data/lib/deep_cover/node/collections.rb +31 -0
  51. data/lib/deep_cover/node/const.rb +12 -0
  52. data/lib/deep_cover/node/def.rb +40 -0
  53. data/lib/deep_cover/node/empty_body.rb +32 -0
  54. data/lib/deep_cover/node/exceptions.rb +79 -0
  55. data/lib/deep_cover/node/if.rb +73 -0
  56. data/lib/deep_cover/node/keywords.rb +86 -0
  57. data/lib/deep_cover/node/literals.rb +100 -0
  58. data/lib/deep_cover/node/loops.rb +74 -0
  59. data/lib/deep_cover/node/mixin/can_augment_children.rb +65 -0
  60. data/lib/deep_cover/node/mixin/check_completion.rb +18 -0
  61. data/lib/deep_cover/node/mixin/child_can_be_empty.rb +27 -0
  62. data/lib/deep_cover/node/mixin/executed_after_children.rb +15 -0
  63. data/lib/deep_cover/node/mixin/execution_location.rb +66 -0
  64. data/lib/deep_cover/node/mixin/filters.rb +47 -0
  65. data/lib/deep_cover/node/mixin/flow_accounting.rb +71 -0
  66. data/lib/deep_cover/node/mixin/has_child.rb +145 -0
  67. data/lib/deep_cover/node/mixin/has_child_handler.rb +75 -0
  68. data/lib/deep_cover/node/mixin/has_tracker.rb +46 -0
  69. data/lib/deep_cover/node/mixin/is_statement.rb +20 -0
  70. data/lib/deep_cover/node/mixin/rewriting.rb +35 -0
  71. data/lib/deep_cover/node/mixin/wrapper.rb +15 -0
  72. data/lib/deep_cover/node/module.rb +66 -0
  73. data/lib/deep_cover/node/root.rb +20 -0
  74. data/lib/deep_cover/node/send.rb +161 -0
  75. data/lib/deep_cover/node/short_circuit.rb +42 -0
  76. data/lib/deep_cover/node/splat.rb +15 -0
  77. data/lib/deep_cover/node/variables.rb +16 -0
  78. data/lib/deep_cover/node.rb +23 -0
  79. data/lib/deep_cover/parser_ext/range.rb +21 -0
  80. data/lib/deep_cover/problem_with_diagnostic.rb +63 -0
  81. data/lib/deep_cover/reporter/base.rb +68 -0
  82. data/lib/deep_cover/reporter/html/base.rb +14 -0
  83. data/lib/deep_cover/reporter/html/index.rb +59 -0
  84. data/lib/deep_cover/reporter/html/site.rb +68 -0
  85. data/lib/deep_cover/reporter/html/source.rb +136 -0
  86. data/lib/deep_cover/reporter/html/template/assets/32px.png +0 -0
  87. data/lib/deep_cover/reporter/html/template/assets/40px.png +0 -0
  88. data/lib/deep_cover/reporter/html/template/assets/deep_cover.css +291 -0
  89. data/lib/deep_cover/reporter/html/template/assets/deep_cover.css.sass +336 -0
  90. data/lib/deep_cover/reporter/html/template/assets/jquery-3.2.1.min.js +4 -0
  91. data/lib/deep_cover/reporter/html/template/assets/jquery-3.2.1.min.map +1 -0
  92. data/lib/deep_cover/reporter/html/template/assets/jstree.css +1108 -0
  93. data/lib/deep_cover/reporter/html/template/assets/jstree.js +8424 -0
  94. data/lib/deep_cover/reporter/html/template/assets/jstreetable.js +1069 -0
  95. data/lib/deep_cover/reporter/html/template/assets/throbber.gif +0 -0
  96. data/lib/deep_cover/reporter/html/template/index.html.erb +75 -0
  97. data/lib/deep_cover/reporter/html/template/source.html.erb +35 -0
  98. data/lib/deep_cover/reporter/html.rb +15 -0
  99. data/lib/deep_cover/reporter/istanbul.rb +184 -0
  100. data/lib/deep_cover/reporter/text.rb +58 -0
  101. data/lib/deep_cover/reporter/tree/util.rb +86 -0
  102. data/lib/deep_cover/reporter.rb +10 -0
  103. data/lib/deep_cover/tools/blank.rb +25 -0
  104. data/lib/deep_cover/tools/builtin_coverage.rb +55 -0
  105. data/lib/deep_cover/tools/camelize.rb +13 -0
  106. data/lib/deep_cover/tools/content_tag.rb +11 -0
  107. data/lib/deep_cover/tools/covered.rb +9 -0
  108. data/lib/deep_cover/tools/execute_sample.rb +34 -0
  109. data/lib/deep_cover/tools/format.rb +18 -0
  110. data/lib/deep_cover/tools/format_char_cover.rb +19 -0
  111. data/lib/deep_cover/tools/format_generated_code.rb +27 -0
  112. data/lib/deep_cover/tools/indent_string.rb +26 -0
  113. data/lib/deep_cover/tools/merge.rb +16 -0
  114. data/lib/deep_cover/tools/number_lines.rb +22 -0
  115. data/lib/deep_cover/tools/our_coverage.rb +11 -0
  116. data/lib/deep_cover/tools/profiling.rb +68 -0
  117. data/lib/deep_cover/tools/render_template.rb +13 -0
  118. data/lib/deep_cover/tools/require_relative_dir.rb +12 -0
  119. data/lib/deep_cover/tools/scan_match_datas.rb +10 -0
  120. data/lib/deep_cover/tools/silence_warnings.rb +18 -0
  121. data/lib/deep_cover/tools/slice.rb +9 -0
  122. data/lib/deep_cover/tools/strip_heredoc.rb +18 -0
  123. data/lib/deep_cover/tools/truncate_backtrace.rb +32 -0
  124. data/lib/deep_cover/tools.rb +22 -0
  125. data/lib/deep_cover/tracker_bucket.rb +50 -0
  126. data/lib/deep_cover/tracker_hits_per_path.rb +35 -0
  127. data/lib/deep_cover/tracker_storage.rb +76 -0
  128. data/lib/deep_cover/tracker_storage_per_path.rb +34 -0
  129. data/lib/deep_cover/version.rb +5 -0
  130. data/lib/deep_cover.rb +22 -0
  131. metadata +329 -0
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DeepCover
4
+ module Node::Mixin
5
+ module Filters
6
+ module ClassMethods
7
+ def filter_to_method_name(kind)
8
+ :"is_#{kind}?"
9
+ end
10
+
11
+ def create_filter(name, &block)
12
+ Filters.define_method(filter_to_method_name(name), &block)
13
+ OPTIONALLY_COVERED << name
14
+ end
15
+
16
+ def unique_filter
17
+ (1..Float::INFINITY).each do |i|
18
+ name = :"custom_filter_#{i}"
19
+ return name unless Filters.method_defined?(filter_to_method_name(name))
20
+ end
21
+ end
22
+ end
23
+
24
+ RAISING_MESSAGES = %i[raise exit].freeze
25
+ def is_raise?
26
+ is_a?(Node::Send) && RAISING_MESSAGES.include?(message) && receiver == nil
27
+ end
28
+
29
+ def is_warn?
30
+ is_a?(Node::Send) && message == :warn
31
+ end
32
+
33
+ def is_default_argument?
34
+ parent.is_a?(Node::Optarg) && simple_literal?
35
+ end
36
+
37
+ def is_case_implicit_else?
38
+ is_a?(Node::EmptyBody) && parent.is_a?(Node::Case) && !parent.has_else?
39
+ end
40
+
41
+ def is_trivial_if?
42
+ # Supports only node being a branch or the fork itself
43
+ parent.is_a?(Node::If) && parent.condition.simple_literal?
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DeepCover
4
+ module Node::Mixin
5
+ module FlowAccounting
6
+ def self.included(base)
7
+ base.has_child_handler('%{name}_flow_entry_count')
8
+ end
9
+
10
+ # Returns true iff it is executable and if was successfully executed
11
+ def was_executed?
12
+ # There is a rare case of non executable nodes that have important data in flow_entry_count / flow_completion_count,
13
+ # like `if cond; end`, so make sure it's actually executable first...
14
+ executable? && execution_count > 0
15
+ end
16
+
17
+ # Returns the control flow entered the node.
18
+ # The control flow can then either complete normally or be interrupted
19
+ #
20
+ # Implementation: This is always the responsibility of the parent; Nodes should not override.
21
+ def flow_entry_count
22
+ parent.child_flow_entry_count(self)
23
+ end
24
+
25
+ # Returns the number of times it changed the usual control flow (e.g. raised, returned, ...)
26
+ # Implementation: This is always deduced; Nodes should not override.
27
+ def flow_interrupt_count
28
+ flow_entry_count - flow_completion_count
29
+ end
30
+
31
+ ### These are refined by subclasses
32
+
33
+ # Returns true iff it is executable. Keywords like `end` are not executable, but literals like `42` are executable.
34
+ def executable?
35
+ true
36
+ end
37
+
38
+ # Returns number of times the node itself was "executed". Definition of executed depends on the node.
39
+ # For now at least, don't return `nil`, instead return `false` in `executable?`
40
+ def execution_count
41
+ flow_entry_count
42
+ end
43
+
44
+ # Returns the number of times the control flow succesfully left the node.
45
+ # This is the responsability of the child Node, never of the parent.
46
+ # Must be refined if the child node may have an impact on control flow (raising, branching, ...)
47
+ def flow_completion_count
48
+ last = children_nodes_in_flow_order.last
49
+ return last.flow_completion_count if last
50
+ flow_entry_count
51
+ end
52
+
53
+ # Returns the number of time the control flow entered this child_node.
54
+ # This is the responsability of the Node, not of the child.
55
+ # Must be refined if the parent node may have an impact on control flow (raising, branching, ...)
56
+ def child_flow_entry_count(child, _name = nil)
57
+ prev = child.previous_sibling
58
+ if prev
59
+ prev.flow_completion_count
60
+ else
61
+ flow_entry_count
62
+ end
63
+ end
64
+
65
+ # Returns the counts in a hash
66
+ def counts
67
+ {flow_entry: flow_entry_count, flow_completion: flow_completion_count, execution: execution_count}
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,145 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DeepCover
4
+ module Node::Mixin
5
+ module HasChild
6
+ def self.included(base)
7
+ base.extend ClassMethods
8
+ end
9
+ CHILDREN = {}.freeze
10
+ CHILDREN_TYPES = {}.freeze
11
+
12
+ def initialize(*)
13
+ super
14
+ self.validate_children_types(children)
15
+ end
16
+
17
+ def validate_children_types(nodes)
18
+ mismatches = self.class.check_children_types(nodes)
19
+ unless mismatches.empty?
20
+ raise TypeError, "Invalid children types for #{self.class}(type: #{self.type}): #{mismatches}"
21
+ end
22
+ end
23
+
24
+ def child_index_to_name(index)
25
+ self.class.child_index_to_name(index, children.size)
26
+ end
27
+
28
+ module ClassMethods
29
+ def has_child(rest_: false, refine_: false, **args)
30
+ raise "Needs exactly one custom named argument, got #{args.size}" if args.size != 1
31
+ name, types = args.first
32
+ raise TypeError, "Expect a Symbol for name, got a #{name.class} (#{name.inspect})" unless name.is_a?(Symbol)
33
+ update_children_const(name, rest: rest_) unless refine_
34
+ define_accessor(name) unless refine_
35
+ add_runtime_check(name, types)
36
+ self
37
+ end
38
+
39
+ def has_extra_children(**args)
40
+ has_child(**args, rest_: true)
41
+ end
42
+
43
+ def refine_child(child_name = nil, **args)
44
+ if child_name
45
+ args = {child_name => self::CHILDREN_TYPES.fetch(child_name), **args}
46
+ end
47
+ has_child(**args, refine_: true)
48
+ end
49
+
50
+ def child_index_to_name(index, nb_children)
51
+ self::CHILDREN.each do |name, i|
52
+ return name if i == index || (i == index - nb_children) ||
53
+ (i.is_a?(Range) && i.begin <= index && i.end + nb_children >= index)
54
+ end
55
+ raise IndexError, "index #{index} does not correspond to any child of #{self}"
56
+ end
57
+
58
+ def check_children_types(nodes)
59
+ types = expected_types(nodes)
60
+ nodes_mismatches(nodes, types)
61
+ end
62
+
63
+ def min_children
64
+ self::CHILDREN.values.grep(Integer).size
65
+ end
66
+
67
+ private
68
+
69
+ def expected_types(nodes)
70
+ self::CHILDREN.flat_map do |name, i|
71
+ type = self::CHILDREN_TYPES[name]
72
+ Array.new(nodes.values_at(i).size, type)
73
+ end
74
+ end
75
+
76
+ def nodes_mismatches(nodes, types)
77
+ nodes = nodes.dup
78
+ nodes[nodes.size...types.size] = nil
79
+ nodes.zip(types).reject do |node, type|
80
+ node_matches_type?(node, type)
81
+ end
82
+ end
83
+
84
+ def node_matches_type?(node, expected)
85
+ case expected
86
+ when :any
87
+ true
88
+ when nil
89
+ node.nil?
90
+ when Array
91
+ expected.any? { |exp| node_matches_type?(node, exp) }
92
+ when Class
93
+ node.is_a?(expected)
94
+ when Symbol
95
+ node.is_a?(Node) && node.type == expected
96
+ else
97
+ raise "Unrecognized expected type #{expected}"
98
+ end
99
+ end
100
+
101
+ def inherited(subclass)
102
+ subclass.const_set :CHILDREN, self::CHILDREN.dup
103
+ subclass.const_set :CHILDREN_TYPES, self::CHILDREN_TYPES.dup
104
+ super
105
+ end
106
+
107
+ def update_children_const(name, rest: false)
108
+ children_map = self::CHILDREN
109
+ already_has_rest = false
110
+ children_map.each do |key, value|
111
+ if value.is_a? Range
112
+ children_map[key] = children_map[key].begin..(children_map[key].end - 1)
113
+ already_has_rest = key
114
+ elsif value < 0
115
+ children_map[key] -= 1
116
+ end
117
+ end
118
+ children_map[name] = if rest
119
+ if already_has_rest
120
+ raise "Class #{self} can't have extra children '#{name}' because it already has '#{name}' (#{children_map})"
121
+ end
122
+ children_map.size..-1
123
+ elsif already_has_rest
124
+ -1
125
+ else
126
+ children_map.size
127
+ end
128
+ end
129
+
130
+ def define_accessor(name)
131
+ warn "child name '#{name}' conflicts with existing method for #{self}" if method_defined? name
132
+ class_eval <<-EVAL, __FILE__, __LINE__ + 1
133
+ def #{name}
134
+ children[CHILDREN.fetch(#{name.inspect})]
135
+ end
136
+ EVAL
137
+ end
138
+
139
+ def add_runtime_check(name, type)
140
+ self::CHILDREN_TYPES[name] = type
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DeepCover
4
+ module Node::Mixin
5
+ module HasChildHandler
6
+ def self.included(base)
7
+ base.extend ClassMethods
8
+ end
9
+
10
+ def call_child_handler(template, child, child_name = nil)
11
+ child_name ||= self.child_index_to_name(child.index)
12
+ method_name = format(template, name: child_name)
13
+ if respond_to?(method_name)
14
+ args = [child, child_name]
15
+ arity = method(method_name).arity
16
+ if arity >= 0
17
+ args = args[0...arity]
18
+ end
19
+ answer = send(method_name, *args)
20
+ end
21
+ answer
22
+ end
23
+ private :call_child_handler
24
+
25
+ module ClassMethods
26
+ def has_child_handler(template)
27
+ child_method_name = format(template, name: 'child')
28
+ action = template.gsub(/_%{name}/, '').gsub(/%{name}_/, '')
29
+ const_name = "#{Tools.camelize(action)}Handler"
30
+ class_eval <<-EVAL, __FILE__, __LINE__ + 1
31
+ module #{const_name} # module RewriteHandler
32
+ module ClassMethods # module ClassMethods
33
+ def has_child(#{action}: nil, **args) # def has_child(rewrite: nil, **args)
34
+ name, _types = args.first # name, _types = args.first
35
+ define_child_handler(#{template.inspect}, # define_child_handler('rewrite_%{child}',
36
+ name, #{action}) # name, rewrite)
37
+ super(**args) # super(**args)
38
+ end # end
39
+ end # end
40
+
41
+ def #{child_method_name}(child, name = nil) # def rewrite_child(child, name = nil)
42
+ call_child_handler(#{template.inspect}, child, # call_child_handler('rewrite_%{child}', child,
43
+ name) || super # name) || super
44
+ end # end
45
+ end # end
46
+ include #{const_name} # include RewriteHandler
47
+ singleton_class.prepend #{const_name}::ClassMethods # singleton_class.prepend RewriteHandler::ClassMethods
48
+ EVAL
49
+ end
50
+
51
+ def define_child_handler(template, name, action)
52
+ method_name = format(template, name: name)
53
+ case action
54
+ when nil
55
+ # Nothing to do
56
+ when Symbol
57
+ define_method(method_name) do |*args|
58
+ arity = method(action).arity
59
+ if arity < 0
60
+ send(action, *args)
61
+ else
62
+ send(action, *args[0...arity])
63
+ end
64
+ end
65
+ when Proc
66
+ define_method(method_name, &action)
67
+ else
68
+ define_method(method_name) { |*| action }
69
+ end
70
+ end
71
+ private :define_child_handler
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DeepCover
4
+ module Node::Mixin
5
+ module HasTracker
6
+ def self.included(base)
7
+ base.extend ClassMethods
8
+ end
9
+ TRACKERS = {}
10
+
11
+ def initialize(*)
12
+ @tracker_offset = tracker_storage.allocate_trackers(self.class::TRACKERS.size).begin
13
+ super
14
+ end
15
+
16
+ def tracker_storage
17
+ covered_code.tracker_storage
18
+ end
19
+
20
+ def tracker_sources
21
+ self.class::TRACKERS.map do |name, _|
22
+ [:"#{name}_tracker", send(:"#{name}_tracker_source")]
23
+ end.to_h
24
+ end
25
+
26
+ module ClassMethods
27
+ def inherited(base)
28
+ base.const_set :TRACKERS, self::TRACKERS.dup
29
+ super
30
+ end
31
+
32
+ def has_tracker(name)
33
+ i = self::TRACKERS[name] = self::TRACKERS.size
34
+ class_eval <<-EVAL, __FILE__, __LINE__ + 1
35
+ def #{name}_tracker_source
36
+ tracker_storage.tracker_source(@tracker_offset + #{i})
37
+ end
38
+ def #{name}_tracker_hits
39
+ tracker_storage[@tracker_offset + #{i}]
40
+ end
41
+ EVAL
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DeepCover
4
+ module Node::Mixin
5
+ module IsStatement
6
+ def self.included(base)
7
+ base.has_child_handler('is_%{name}_statement')
8
+ end
9
+
10
+ def is_statement
11
+ parent.is_child_statement(self)
12
+ end
13
+
14
+ # Default child rewriting rule
15
+ def is_child_statement(child, name = nil)
16
+ :if_incompatible
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DeepCover
4
+ module Node::Mixin
5
+ module Rewriting
6
+ def self.included(base)
7
+ base.has_child_handler('rewrite_%{name}')
8
+ end
9
+
10
+ # Code to add before and after the node for covering purposes
11
+ def rewrite
12
+ end
13
+
14
+ # Default child rewriting rule
15
+ def rewrite_child(child, name = nil)
16
+ end
17
+
18
+ # Replaces all the '%{local}' or '%{some_tracker}' in rewriting rules
19
+ def resolve_rewrite(rule, context)
20
+ return if rule == nil
21
+ sources = context.tracker_sources
22
+ format(rule, local: covered_code.local_var, node: '%{node}', **sources)
23
+ end
24
+
25
+ # Returns an array of [range, rule], where rule is a string containing '%{node}'
26
+ # Rules must be ordered inner-most first
27
+ def rewriting_rules
28
+ [
29
+ resolve_rewrite(rewrite, self),
30
+ resolve_rewrite(parent.rewrite_child(self), parent),
31
+ ].compact.map { |rule| [expression, rule] }
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DeepCover
4
+ module Node::Mixin
5
+ module Wrapper
6
+ def initialize(base_node, **kwargs)
7
+ super(base_node, base_children: [base_node], **kwargs)
8
+ end
9
+
10
+ def executed_loc_keys
11
+ []
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'const'
4
+
5
+ module DeepCover
6
+ class Node
7
+ class ModuleName < Node
8
+ has_child scope: [Node, nil]
9
+ has_child const_name: Symbol
10
+
11
+ def flow_completion_count
12
+ parent.execution_count
13
+ end
14
+
15
+ def execution_count
16
+ if scope
17
+ scope.flow_completion_count
18
+ else
19
+ super
20
+ end
21
+ end
22
+ end
23
+
24
+ class Module < Node
25
+ check_completion
26
+ has_tracker :body_entry
27
+ has_child const: {const: ModuleName}
28
+ has_child body: Node,
29
+ can_be_empty: -> { base_node.loc.end.begin },
30
+ rewrite: '%{body_entry_tracker};%{local}=nil;%{node}',
31
+ is_statement: true,
32
+ flow_entry_count: :body_entry_tracker_hits
33
+ executed_loc_keys :keyword
34
+
35
+ def execution_count
36
+ body_entry_tracker_hits
37
+ end
38
+ end
39
+
40
+ class Class < Node
41
+ check_completion
42
+ has_tracker :body_entry
43
+ has_child const: {const: ModuleName}
44
+ has_child inherit: [Node, nil] # TODO
45
+ has_child body: Node,
46
+ can_be_empty: -> { base_node.loc.end.begin },
47
+ rewrite: '%{body_entry_tracker};%{node}',
48
+ is_statement: true,
49
+ flow_entry_count: :body_entry_tracker_hits
50
+ executed_loc_keys :keyword
51
+
52
+ def execution_count
53
+ body_entry_tracker_hits
54
+ end
55
+ end
56
+
57
+ # class << foo
58
+ class Sclass < Node
59
+ has_child object: Node
60
+ has_child body: Node,
61
+ can_be_empty: -> { base_node.loc.end.begin },
62
+ is_statement: true
63
+ # TODO
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DeepCover
4
+ class Node::Root < Node
5
+ has_tracker :root
6
+ has_child main: Node,
7
+ can_be_empty: -> { Parser::Source::Range.new(covered_code.buffer, 0, 0) },
8
+ is_statement: true,
9
+ rewrite: -> {
10
+ "#{tracker_storage.setup_source};%{root_tracker};%{local}=nil;%{node}"
11
+ }
12
+ attr_reader :covered_code
13
+ alias_method :flow_entry_count, :root_tracker_hits
14
+
15
+ def initialize(child_ast, covered_code)
16
+ @covered_code = covered_code
17
+ super(nil, parent: nil, base_children: [child_ast])
18
+ end
19
+ end
20
+ end