deep-cover-core 0.6.3.pre

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.
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