deep-cover-core 0.6.4 → 0.7.0
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/deep_cover_core.gemspec +1 -0
- data/lib/deep_cover.rb +3 -20
- data/lib/deep_cover/auto_run.rb +10 -35
- data/lib/deep_cover/autoload_tracker.rb +9 -5
- data/lib/deep_cover/base.rb +85 -12
- data/lib/deep_cover/basics.rb +15 -3
- data/lib/deep_cover/config.rb +36 -2
- data/lib/deep_cover/config_setter.rb +1 -1
- data/lib/deep_cover/core_ext/exec_callbacks.rb +15 -9
- data/lib/deep_cover/core_ext/instruction_sequence_load_iseq.rb +1 -1
- data/lib/deep_cover/coverage.rb +51 -41
- data/lib/deep_cover/covered_code.rb +54 -17
- data/lib/deep_cover/custom_requirer.rb +10 -10
- data/lib/deep_cover/global_variables.rb +32 -0
- data/lib/deep_cover/load.rb +25 -9
- data/lib/deep_cover/node/base.rb +6 -6
- data/lib/deep_cover/node/begin.rb +1 -2
- data/lib/deep_cover/node/block.rb +5 -1
- data/lib/deep_cover/node/case.rb +1 -1
- data/lib/deep_cover/node/empty_body.rb +1 -5
- data/lib/deep_cover/node/if.rb +3 -4
- data/lib/deep_cover/node/loops.rb +1 -1
- data/lib/deep_cover/node/mixin/flow_accounting.rb +1 -8
- data/lib/deep_cover/node/mixin/has_child.rb +3 -12
- data/lib/deep_cover/node/mixin/has_child_handler.rb +2 -5
- data/lib/deep_cover/node/mixin/has_tracker.rb +3 -7
- data/lib/deep_cover/node/module.rb +20 -23
- data/lib/deep_cover/node/root.rb +1 -1
- data/lib/deep_cover/node/send.rb +4 -5
- data/lib/deep_cover/node/short_circuit.rb +1 -1
- data/lib/deep_cover/persistence.rb +100 -0
- data/lib/deep_cover/problem_with_diagnostic.rb +2 -2
- data/lib/deep_cover/setup/clone_mode_entry_template.rb +66 -0
- data/lib/deep_cover/setup/deep_cover_auto_run.rb +21 -0
- data/lib/deep_cover/setup/deep_cover_config.rb +19 -0
- data/lib/deep_cover/setup/deep_cover_without_config.rb +15 -0
- data/lib/deep_cover/tools.rb +0 -2
- data/lib/deep_cover/tools/after_tests.rb +33 -0
- data/lib/deep_cover/tools/looks_like_rails_project.rb +13 -0
- data/lib/deep_cover/tools/our_coverage.rb +1 -1
- data/lib/deep_cover/tools/scan_match_datas.rb +1 -1
- data/lib/deep_cover/version.rb +4 -2
- metadata +24 -7
- data/lib/deep_cover/coverage/persistence.rb +0 -84
- data/lib/deep_cover/tracker_bucket.rb +0 -50
- data/lib/deep_cover/tracker_hits_per_path.rb +0 -35
- data/lib/deep_cover/tracker_storage.rb +0 -76
- data/lib/deep_cover/tracker_storage_per_path.rb +0 -34
data/lib/deep_cover/node/case.rb
CHANGED
@@ -7,10 +7,6 @@ module DeepCover
|
|
7
7
|
super(base_node, parent: parent, index: index, base_children: [])
|
8
8
|
end
|
9
9
|
|
10
|
-
def type
|
11
|
-
:EmptyBody
|
12
|
-
end
|
13
|
-
|
14
10
|
def loc_hash
|
15
11
|
return {} if @position == true
|
16
12
|
{expression: @position}
|
@@ -21,7 +17,7 @@ module DeepCover
|
|
21
17
|
end
|
22
18
|
|
23
19
|
# When parent rewrites us, the %{node} must always be at the beginning because our location can
|
24
|
-
# also be rewritten by
|
20
|
+
# also be rewritten by our parent, and we want the rewrite to be after it.
|
25
21
|
def rewriting_rules
|
26
22
|
rules = super
|
27
23
|
rules.map do |expression, rule|
|
data/lib/deep_cover/node/if.rb
CHANGED
@@ -19,7 +19,7 @@ module DeepCover
|
|
19
19
|
executed_loc_keys :keyword, :question
|
20
20
|
|
21
21
|
def child_can_be_empty(child, name)
|
22
|
-
|
22
|
+
raise 'Unexpected empty body' if name == :condition || style == :ternary
|
23
23
|
if (name == :true_branch) == [:if, :elsif].include?(style)
|
24
24
|
(base_node.loc.begin || base_node.children[0].loc.expression.succ).end
|
25
25
|
elsif has_else?
|
@@ -33,7 +33,7 @@ module DeepCover
|
|
33
33
|
[true_branch, false_branch]
|
34
34
|
end
|
35
35
|
|
36
|
-
def branches_summary(of_branches
|
36
|
+
def branches_summary(of_branches)
|
37
37
|
of_branches.map do |jump|
|
38
38
|
"#{'implicit ' if jump.is_a?(EmptyBody) && !has_else?}#{jump == false_branch ? 'falsy' : 'truthy'} branch"
|
39
39
|
end.join(' and ')
|
@@ -58,13 +58,12 @@ module DeepCover
|
|
58
58
|
end
|
59
59
|
|
60
60
|
def deepest_elsif_node
|
61
|
-
|
61
|
+
raise 'Not an elsif' if style != :elsif
|
62
62
|
return self if loc_hash[:else] && loc_hash[:else].source == 'else'
|
63
63
|
return self if false_branch.is_a?(EmptyBody)
|
64
64
|
false_branch.deepest_elsif_node
|
65
65
|
end
|
66
66
|
|
67
|
-
|
68
67
|
def has_else?
|
69
68
|
!!base_node.loc.to_hash[:else]
|
70
69
|
end
|
@@ -4,7 +4,7 @@ module DeepCover
|
|
4
4
|
class Node
|
5
5
|
class For < Node
|
6
6
|
has_tracker :body
|
7
|
-
has_child assignments: [Mlhs, VariableAssignment], flow_entry_count: -> { body.flow_entry_count
|
7
|
+
has_child assignments: [Mlhs, VariableAssignment], flow_entry_count: -> { body.flow_entry_count }
|
8
8
|
has_child iterable: [Node], flow_entry_count: -> { flow_entry_count }
|
9
9
|
has_child body: Node,
|
10
10
|
can_be_empty: -> { base_node.loc.end.begin },
|
@@ -9,9 +9,7 @@ module DeepCover
|
|
9
9
|
|
10
10
|
# Returns true iff it is executable and if was successfully executed
|
11
11
|
def was_executed?
|
12
|
-
|
13
|
-
# like `if cond; end`, so make sure it's actually executable first...
|
14
|
-
executable? && execution_count > 0
|
12
|
+
execution_count > 0
|
15
13
|
end
|
16
14
|
|
17
15
|
# Returns the control flow entered the node.
|
@@ -61,11 +59,6 @@ module DeepCover
|
|
61
59
|
flow_entry_count
|
62
60
|
end
|
63
61
|
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
62
|
end
|
70
63
|
end
|
71
64
|
end
|
@@ -26,12 +26,12 @@ module DeepCover
|
|
26
26
|
end
|
27
27
|
|
28
28
|
module ClassMethods
|
29
|
-
def has_child(rest_: false,
|
29
|
+
def has_child(rest_: false, **args)
|
30
30
|
raise "Needs exactly one custom named argument, got #{args.size}" if args.size != 1
|
31
31
|
name, types = args.first
|
32
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_)
|
34
|
-
define_accessor(name)
|
33
|
+
update_children_const(name, rest: rest_)
|
34
|
+
define_accessor(name)
|
35
35
|
add_runtime_check(name, types)
|
36
36
|
self
|
37
37
|
end
|
@@ -40,13 +40,6 @@ module DeepCover
|
|
40
40
|
has_child(**args, rest_: true)
|
41
41
|
end
|
42
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
43
|
def child_index_to_name(index, nb_children)
|
51
44
|
self::CHILDREN.each do |name, i|
|
52
45
|
return name if i == index || (i == index - nb_children) ||
|
@@ -91,8 +84,6 @@ module DeepCover
|
|
91
84
|
expected.any? { |exp| node_matches_type?(node, exp) }
|
92
85
|
when Class
|
93
86
|
node.is_a?(expected)
|
94
|
-
when Symbol
|
95
|
-
node.is_a?(Node) && node.type == expected
|
96
87
|
else
|
97
88
|
raise "Unrecognized expected type #{expected}"
|
98
89
|
end
|
@@ -56,11 +56,8 @@ module DeepCover
|
|
56
56
|
when Symbol
|
57
57
|
define_method(method_name) do |*args|
|
58
58
|
arity = method(action).arity
|
59
|
-
if arity < 0
|
60
|
-
|
61
|
-
else
|
62
|
-
send(action, *args[0...arity])
|
63
|
-
end
|
59
|
+
raise NotImplementedError if arity < 0
|
60
|
+
send(action, *args[0...arity])
|
64
61
|
end
|
65
62
|
when Proc
|
66
63
|
define_method(method_name, &action)
|
@@ -9,14 +9,10 @@ module DeepCover
|
|
9
9
|
TRACKERS = {}
|
10
10
|
|
11
11
|
def initialize(*)
|
12
|
-
@tracker_offset =
|
12
|
+
@tracker_offset = covered_code.allocate_trackers(self.class::TRACKERS.size).begin
|
13
13
|
super
|
14
14
|
end
|
15
15
|
|
16
|
-
def tracker_storage
|
17
|
-
covered_code.tracker_storage
|
18
|
-
end
|
19
|
-
|
20
16
|
def tracker_sources
|
21
17
|
self.class::TRACKERS.map do |name, _|
|
22
18
|
[:"#{name}_tracker", send(:"#{name}_tracker_source")]
|
@@ -33,10 +29,10 @@ module DeepCover
|
|
33
29
|
i = self::TRACKERS[name] = self::TRACKERS.size
|
34
30
|
class_eval <<-EVAL, __FILE__, __LINE__ + 1
|
35
31
|
def #{name}_tracker_source
|
36
|
-
|
32
|
+
covered_code.increment_tracker_source(@tracker_offset + #{i})
|
37
33
|
end
|
38
34
|
def #{name}_tracker_hits
|
39
|
-
|
35
|
+
covered_code.tracker_hits[@tracker_offset + #{i}]
|
40
36
|
end
|
41
37
|
EVAL
|
42
38
|
end
|
@@ -21,46 +21,43 @@ module DeepCover
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
-
|
24
|
+
def self.define_module_class
|
25
25
|
check_completion
|
26
26
|
has_tracker :body_entry
|
27
|
-
|
27
|
+
yield
|
28
28
|
has_child body: Node,
|
29
29
|
can_be_empty: -> { base_node.loc.end.begin },
|
30
|
-
rewrite: '%{body_entry_tracker};%{
|
30
|
+
rewrite: '%{body_entry_tracker};%{node}',
|
31
31
|
is_statement: true,
|
32
32
|
flow_entry_count: :body_entry_tracker_hits
|
33
33
|
executed_loc_keys :keyword
|
34
34
|
|
35
|
-
|
36
|
-
|
35
|
+
class_eval do
|
36
|
+
def execution_count # Overrides ExecutedAfterChildren
|
37
|
+
body_entry_tracker_hits
|
38
|
+
end
|
37
39
|
end
|
38
40
|
end
|
39
41
|
|
40
|
-
class
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
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
|
42
|
+
class Module < Node
|
43
|
+
define_module_class do
|
44
|
+
has_child const: {const: ModuleName}
|
45
|
+
end
|
46
|
+
end
|
51
47
|
|
52
|
-
|
53
|
-
|
48
|
+
class Class < Node
|
49
|
+
define_module_class do
|
50
|
+
has_child const: {const: ModuleName}
|
51
|
+
has_child inherit: [Node, nil] # TODO
|
54
52
|
end
|
55
53
|
end
|
56
54
|
|
57
55
|
# class << foo
|
58
56
|
class Sclass < Node
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
# TODO
|
57
|
+
define_module_class do
|
58
|
+
has_child object: Node
|
59
|
+
end
|
60
|
+
executed_loc_keys :keyword, :operator
|
64
61
|
end
|
65
62
|
end
|
66
63
|
end
|
data/lib/deep_cover/node/root.rb
CHANGED
@@ -7,7 +7,7 @@ module DeepCover
|
|
7
7
|
can_be_empty: -> { Parser::Source::Range.new(covered_code.buffer, 0, 0) },
|
8
8
|
is_statement: true,
|
9
9
|
rewrite: -> {
|
10
|
-
"#{
|
10
|
+
"#{covered_code.setup_tracking_source};%{root_tracker};%{local}=nil;%{node}"
|
11
11
|
}
|
12
12
|
attr_reader :covered_code
|
13
13
|
alias_method :flow_entry_count, :root_tracker_hits
|
data/lib/deep_cover/node/send.rb
CHANGED
@@ -122,6 +122,9 @@ module DeepCover
|
|
122
122
|
|
123
123
|
executed_loc_keys :dot
|
124
124
|
|
125
|
+
extend Forwardable
|
126
|
+
def_delegators :actual_send, :message, :arguments
|
127
|
+
|
125
128
|
def has_block?
|
126
129
|
parent.is_a?(Block) && parent.child_index_to_name(index) == :call
|
127
130
|
end
|
@@ -134,17 +137,13 @@ module DeepCover
|
|
134
137
|
receiver.flow_completion_count
|
135
138
|
end
|
136
139
|
|
137
|
-
def message
|
138
|
-
actual_send.message
|
139
|
-
end
|
140
|
-
|
141
140
|
def branches
|
142
141
|
[TrivialBranch.new(condition: receiver, other_branch: actual_send),
|
143
142
|
actual_send,
|
144
143
|
]
|
145
144
|
end
|
146
145
|
|
147
|
-
def branches_summary(of_branches
|
146
|
+
def branches_summary(of_branches)
|
148
147
|
of_branches.map do |jump|
|
149
148
|
jump == actual_send ? 'safe send' : 'nil shortcut'
|
150
149
|
end.join(' and ')
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This file is used by projects cloned with clone mode. As such, special care must be taken to
|
4
|
+
# be compatible with any projects.
|
5
|
+
# THERE MUST NOT BE ANY USE/REQUIRE OF DEPENDENCIES OF DeepCover HERE
|
6
|
+
# See deep-cover/core_gem/lib/deep_cover/setup/clone_mode_entry_template.rb for explanation of
|
7
|
+
# clone mode and of this top_level_module stuff.
|
8
|
+
top_level_module = Thread.current['_deep_cover_top_level_module'] || Object # rubocop:disable Lint/UselessAssignment
|
9
|
+
|
10
|
+
module top_level_module::DeepCover # rubocop:disable Naming/ClassAndModuleCamelCase
|
11
|
+
require 'securerandom'
|
12
|
+
class Persistence
|
13
|
+
BASENAME = 'coverage.dc'
|
14
|
+
TRACKER_TEMPLATE = 'trackers%{unique}.dct'
|
15
|
+
|
16
|
+
attr_reader :dir_path
|
17
|
+
def initialize(cache_directory)
|
18
|
+
@dir_path = Pathname(cache_directory).expand_path
|
19
|
+
end
|
20
|
+
|
21
|
+
def save_trackers(tracker_hits_per_path)
|
22
|
+
create_directory_if_needed
|
23
|
+
basename = format(TRACKER_TEMPLATE, unique: SecureRandom.urlsafe_base64)
|
24
|
+
|
25
|
+
dir_path.join(basename).binwrite(JSON.dump(
|
26
|
+
version: VERSION,
|
27
|
+
tracker_hits_per_path: tracker_hits_per_path,
|
28
|
+
))
|
29
|
+
end
|
30
|
+
|
31
|
+
# returns a TrackerHitsPerPath
|
32
|
+
def load_trackers
|
33
|
+
tracker_hits_per_path_hashes = tracker_files.map do |full_path|
|
34
|
+
JSON.parse(full_path.binread).transform_keys(&:to_sym).yield_self do |version:, tracker_hits_per_path:|
|
35
|
+
raise "dump version mismatch: #{version}, currently #{VERSION}" unless version == VERSION
|
36
|
+
tracker_hits_per_path
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
self.class.merge_tracker_hits_per_paths(*tracker_hits_per_path_hashes)
|
41
|
+
end
|
42
|
+
|
43
|
+
def merge_persisted_trackers
|
44
|
+
tracker_hits_per_path = load_trackers
|
45
|
+
return if tracker_hits_per_path.empty?
|
46
|
+
tracker_files_before = tracker_files
|
47
|
+
save_trackers(tracker_hits_per_path)
|
48
|
+
tracker_files_before.each(&:delete)
|
49
|
+
end
|
50
|
+
|
51
|
+
def delete_trackers
|
52
|
+
tracker_files.each(&:delete)
|
53
|
+
end
|
54
|
+
|
55
|
+
def clear_directory
|
56
|
+
delete_trackers
|
57
|
+
begin
|
58
|
+
dir_path.rmdir
|
59
|
+
rescue SystemCallError # rubocop:disable Lint/HandleExceptions
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.merge_tracker_hits_per_paths(*tracker_hits_per_path_hashes)
|
64
|
+
return {} if tracker_hits_per_path_hashes.empty?
|
65
|
+
|
66
|
+
result = tracker_hits_per_path_hashes[0].transform_values(&:dup)
|
67
|
+
|
68
|
+
tracker_hits_per_path_hashes[1..-1].each do |tracker_hits_per_path|
|
69
|
+
tracker_hits_per_path.each do |path, tracker_hits|
|
70
|
+
matching_result = result[path]
|
71
|
+
if matching_result.nil?
|
72
|
+
result[path] = tracker_hits.dup
|
73
|
+
next
|
74
|
+
end
|
75
|
+
|
76
|
+
if matching_result.size != tracker_hits.size
|
77
|
+
raise "Attempting to merge trackers of different sizes: #{matching_result.size} vs #{tracker_hits.size}, for path #{path}"
|
78
|
+
end
|
79
|
+
|
80
|
+
tracker_hits.each_with_index do |nb_hits, i|
|
81
|
+
matching_result[i] += nb_hits
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
result
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def create_directory_if_needed
|
92
|
+
dir_path.mkpath
|
93
|
+
end
|
94
|
+
|
95
|
+
def tracker_files
|
96
|
+
basename = format(TRACKER_TEMPLATE, unique: '*')
|
97
|
+
Pathname.glob(dir_path.join(basename))
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -49,11 +49,11 @@ module DeepCover
|
|
49
49
|
first_index = line_range.begin - nb_context_line - buffer.first_line
|
50
50
|
first_index = 0 if first_index < 0
|
51
51
|
last_index = line_range.end + nb_context_line - buffer.first_line
|
52
|
-
last_index = 0 if last_index < 0
|
53
52
|
|
54
53
|
lines = buffer.source_lines[first_index..last_index]
|
54
|
+
lines.pop if lines.last.empty?
|
55
55
|
|
56
|
-
Tools.number_lines(lines, lineno: buffer.first_line, bad_linenos: line_range.to_a)
|
56
|
+
Tools.number_lines(lines, lineno: buffer.first_line + first_index, bad_linenos: line_range.to_a)
|
57
57
|
end
|
58
58
|
|
59
59
|
def buffer
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This file is used by `deep-cover clone`.
|
4
|
+
#
|
5
|
+
# This is a template for the setup code that clone mode makes the cloned code execute.
|
6
|
+
# We don't actually load deep-cover at all. We do need to little parts of it in a non name-clashing way,
|
7
|
+
# so this file takes care of this.
|
8
|
+
#
|
9
|
+
# Clone mode will copy this file and gsub some "global variables" with their actual values.
|
10
|
+
# The the fake global variables are: $_cache_directory, $_core_gem_lib_directory, $_global_name
|
11
|
+
# This template is important because with the anonymous top-level module, it becomes problematic
|
12
|
+
# to call the content of this file from the place that required it. Doing it with a template means
|
13
|
+
# that it is not necessary to do anything beyond requiring the file. It feels cleaner than the
|
14
|
+
# alternatives I thought of.
|
15
|
+
#
|
16
|
+
# It is important to avoid using any of deep-cover's dependencies (from other gems), because they may not be in
|
17
|
+
# the Gemfile of the project being cloned, and so will not be found.
|
18
|
+
#
|
19
|
+
# In order to avoid any possible name clashing with a DeepCover that would be used in the program
|
20
|
+
# being covered with clone mode, we create a unique top-level module under which we nest everything
|
21
|
+
# that we need. (The main use-case for this is when doing coverage of DeepCover itself)
|
22
|
+
top_level_module = Module.new
|
23
|
+
|
24
|
+
module top_level_module::DeepCover # rubocop:disable Naming/ClassAndModuleCamelCase
|
25
|
+
def self.setup
|
26
|
+
Tools::AfterTests.after_tests { save }
|
27
|
+
ExecCallbacks.before_exec { save }
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.save
|
31
|
+
return if saved?
|
32
|
+
|
33
|
+
Persistence.new($_cache_directory).save_trackers(GlobalVariables.tracker_hits_per_path($_global_name))
|
34
|
+
@saved = true
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.saved?
|
38
|
+
@saved ||= false
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# The files that we need all use this top-level module when it is defined. We avoid any kind of race-condition
|
43
|
+
# by using Thread.current instead of a global variable. (Think of what would happen if another thread decided to
|
44
|
+
# require 'deep-cover' while this thread is loading it's special top-level dependencies)
|
45
|
+
Thread.current['_deep_cover_top_level_module'] = top_level_module
|
46
|
+
# To avoid any issue, we load the files instead of requiring them. Since this file is being required, the
|
47
|
+
# result is the same, but if, for any reason, this file or another template gets executed again, then there will be
|
48
|
+
# a new top-level module, so we must fill it correctly, which means loading the files again.
|
49
|
+
load $_core_gem_lib_directory + '/deep_cover/global_variables.rb'
|
50
|
+
load $_core_gem_lib_directory + '/deep_cover/persistence.rb'
|
51
|
+
load $_core_gem_lib_directory + '/deep_cover/version.rb'
|
52
|
+
load $_core_gem_lib_directory + '/deep_cover/core_ext/exec_callbacks.rb'
|
53
|
+
load $_core_gem_lib_directory + '/deep_cover/tools/after_tests.rb'
|
54
|
+
|
55
|
+
Thread.current['_deep_cover_top_level_module'] = nil
|
56
|
+
|
57
|
+
Object.autoload :JSON, 'json'
|
58
|
+
|
59
|
+
# This is really just to make debugging less of a pain, it gives a way to the code to access the anonymous top-level module
|
60
|
+
module DeepCover
|
61
|
+
CLONE_MODE_ENTRY_TOP_LEVEL_MODULES ||= []
|
62
|
+
end
|
63
|
+
DeepCover::CLONE_MODE_ENTRY_TOP_LEVEL_MODULES << top_level_module
|
64
|
+
|
65
|
+
# Activate everything!
|
66
|
+
top_level_module::DeepCover.setup
|