deep-cover 0.5.7 → 0.6.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/lib/deep_cover/base.rb +29 -11
- data/lib/deep_cover/core_ext/instruction_sequence_load_iseq.rb +32 -0
- data/lib/deep_cover/coverage.rb +12 -0
- data/lib/deep_cover/covered_code.rb +27 -0
- data/lib/deep_cover/custom_requirer.rb +8 -80
- data/lib/deep_cover/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f3b9feb6fdad25312b0111faf653599e6019c51ddbcd31446ec2c45d7c5403f3
|
4
|
+
data.tar.gz: 18c532649cdc00a98690fc3d66ced732e11b1c97a2787ab36d0d5e3151e20601
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1b68e3fc7a62f263f1971b7b50b753d5ca5472bee6231eba897771ff1e036efcc6f8cc5625b1e5b6ef3422a978f1e2876ba7b6f8488d176705e0b8f465d9786d
|
7
|
+
data.tar.gz: 9fcf65daa4618b3c9d056ed95fe1a3f7e9ae1e4d67768940989630fa9c1a45ff05c7504bffae5058e0597fea8f661e730d59fa0ec089446d2ad28ec5f4ab963c
|
data/lib/deep_cover/base.rb
CHANGED
@@ -9,27 +9,33 @@ module DeepCover
|
|
9
9
|
def start
|
10
10
|
return if running?
|
11
11
|
if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
|
12
|
-
#
|
13
|
-
#
|
12
|
+
# Autoload is not supported in JRuby. We currently need to use binding_of_caller
|
13
|
+
# and that is not available in JRuby. An extension may be able to replace this requirement.
|
14
|
+
# require_relative 'core_ext/autoload_overrides'
|
15
|
+
# AutoloadOverride.active = true
|
16
|
+
require_relative 'core_ext/require_overrides'
|
17
|
+
RequireOverride.active = true
|
18
|
+
elsif RUBY_VERSION >= '2.3.0'
|
19
|
+
require_relative 'core_ext/instruction_sequence_load_iseq'
|
14
20
|
else
|
15
21
|
require_relative 'core_ext/autoload_overrides'
|
16
|
-
|
22
|
+
require_relative 'core_ext/require_overrides'
|
23
|
+
AutoloadOverride.active = RequireOverride.active = true
|
17
24
|
autoload_tracker.initialize_autoloaded_paths { |mod, name, path| mod.autoload_without_deep_cover(name, path) }
|
18
25
|
end
|
19
|
-
|
20
|
-
RequireOverride.active = true
|
26
|
+
|
21
27
|
config # actualize configuration
|
22
|
-
@
|
28
|
+
@lookup_paths = nil
|
23
29
|
@started = true
|
24
30
|
end
|
25
31
|
|
26
32
|
def stop
|
27
|
-
require_relative 'core_ext/require_overrides'
|
28
33
|
if defined? AutoloadOverride
|
29
34
|
AutoloadOverride.active = false
|
30
35
|
autoload_tracker.remove_interceptors { |mod, name, path| mod.autoload_without_deep_cover(name, path) }
|
31
36
|
end
|
32
|
-
RequireOverride.active = false
|
37
|
+
RequireOverride.active = false if defined? RequireOverride
|
38
|
+
|
33
39
|
@started = false
|
34
40
|
end
|
35
41
|
|
@@ -57,7 +63,7 @@ module DeepCover
|
|
57
63
|
case what
|
58
64
|
when :paths
|
59
65
|
warn "Changing DeepCover's paths after starting coverage is highly discouraged" if running?
|
60
|
-
@
|
66
|
+
@lookup_paths = nil
|
61
67
|
when :tracker_global
|
62
68
|
raise NotImplementedError, "Changing DeepCover's tracker global after starting coverage is not supported" if running?
|
63
69
|
@coverage = nil
|
@@ -66,7 +72,7 @@ module DeepCover
|
|
66
72
|
|
67
73
|
def reset
|
68
74
|
stop if running?
|
69
|
-
@coverage = @custom_requirer = @autoload_tracker = nil
|
75
|
+
@coverage = @custom_requirer = @autoload_tracker = @lookup_paths = nil
|
70
76
|
config.reset
|
71
77
|
self
|
72
78
|
end
|
@@ -75,8 +81,20 @@ module DeepCover
|
|
75
81
|
@coverage ||= Coverage.new(tracker_global: config.tracker_global)
|
76
82
|
end
|
77
83
|
|
84
|
+
def lookup_paths
|
85
|
+
return @lookup_paths if @lookup_paths
|
86
|
+
lookup_paths = config.paths || Dir.getwd
|
87
|
+
lookup_paths = Array(lookup_paths).map { |p| File.expand_path(p) }
|
88
|
+
lookup_paths = ['/'] if lookup_paths.include?('/')
|
89
|
+
@lookup_paths = lookup_paths
|
90
|
+
end
|
91
|
+
|
92
|
+
def within_lookup_paths?(path)
|
93
|
+
lookup_paths.any? { |lookup_path| path.start_with?(lookup_path) }
|
94
|
+
end
|
95
|
+
|
78
96
|
def custom_requirer
|
79
|
-
@custom_requirer ||= CustomRequirer.new
|
97
|
+
@custom_requirer ||= CustomRequirer.new
|
80
98
|
end
|
81
99
|
|
82
100
|
def autoload_tracker
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DeepCover
|
4
|
+
load_all
|
5
|
+
|
6
|
+
module InstructionSequenceLoadIseq
|
7
|
+
def load_iseq(path)
|
8
|
+
compiled = InstructionSequenceLoadIseq.load_iseq_logic(path)
|
9
|
+
|
10
|
+
return compiled if compiled
|
11
|
+
|
12
|
+
# By default there is no super, but if bootsnap is there, and things are in the right order,
|
13
|
+
# we could possibly fallback to it as usual to keep the perf gain. Same for other possible
|
14
|
+
# tools using #load_iseq
|
15
|
+
super if defined?(super)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.load_iseq_logic(path)
|
19
|
+
return unless DeepCover.running?
|
20
|
+
return unless DeepCover.within_lookup_paths?(path)
|
21
|
+
|
22
|
+
covered_code = DeepCover.coverage.covered_code_or_warn(path)
|
23
|
+
return unless covered_code
|
24
|
+
|
25
|
+
covered_code.compile_or_warn
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class << RubyVM::InstructionSequence
|
31
|
+
prepend DeepCover::InstructionSequenceLoadIseq
|
32
|
+
end
|
data/lib/deep_cover/coverage.rb
CHANGED
@@ -37,6 +37,18 @@ module DeepCover
|
|
37
37
|
**@options)
|
38
38
|
end
|
39
39
|
|
40
|
+
def covered_code_or_warn(path, **options)
|
41
|
+
covered_code(path, **options)
|
42
|
+
rescue Parser::SyntaxError => e
|
43
|
+
if e.message =~ /contains escape sequences incompatible with UTF-8/
|
44
|
+
warn "Can't cover #{path} because of incompatible encoding (see issue #9)"
|
45
|
+
else
|
46
|
+
warn "The file #{path} can't be instrumented"
|
47
|
+
end
|
48
|
+
nil
|
49
|
+
end
|
50
|
+
|
51
|
+
|
40
52
|
def each
|
41
53
|
return to_enum unless block_given?
|
42
54
|
@tracker_storage_per_path.each_key do |path|
|
@@ -44,6 +44,12 @@ module DeepCover
|
|
44
44
|
self
|
45
45
|
end
|
46
46
|
|
47
|
+
def execute_code_or_warn(*args)
|
48
|
+
warn_instead_of_syntax_error do
|
49
|
+
execute_code(*args)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
47
53
|
def cover
|
48
54
|
global[nb] ||= Array.new(@tracker_count, 0)
|
49
55
|
end
|
@@ -93,6 +99,16 @@ module DeepCover
|
|
93
99
|
rewriter.process
|
94
100
|
end
|
95
101
|
|
102
|
+
def compile
|
103
|
+
RubyVM::InstructionSequence.compile(covered_source, path.to_s, path.to_s)
|
104
|
+
end
|
105
|
+
|
106
|
+
def compile_or_warn
|
107
|
+
warn_instead_of_syntax_error do
|
108
|
+
compile
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
96
112
|
def freeze
|
97
113
|
unless frozen? # Guard against reentrance
|
98
114
|
super
|
@@ -106,6 +122,17 @@ module DeepCover
|
|
106
122
|
end
|
107
123
|
alias_method :to_s, :inspect
|
108
124
|
|
125
|
+
def warn_instead_of_syntax_error # &block
|
126
|
+
yield
|
127
|
+
rescue ::SyntaxError => e
|
128
|
+
warn Tools.strip_heredoc(<<-MSG)
|
129
|
+
DeepCover is getting confused with the file #{path} and it won't be instrumented.
|
130
|
+
Please report this error and provide the source code around the following lines:
|
131
|
+
#{e}
|
132
|
+
MSG
|
133
|
+
nil
|
134
|
+
end
|
135
|
+
|
109
136
|
private
|
110
137
|
|
111
138
|
def parser
|
@@ -2,72 +2,9 @@
|
|
2
2
|
|
3
3
|
module DeepCover
|
4
4
|
class CustomRequirer
|
5
|
-
class LoadPathsSubset
|
6
|
-
def initialize(load_paths:, lookup_paths:)
|
7
|
-
@original_load_paths = load_paths
|
8
|
-
@cached_load_paths_subset = []
|
9
|
-
@cached_load_paths_hash = nil
|
10
|
-
@lookup_paths = lookup_paths.map { |p| File.expand_path(p) }
|
11
|
-
end
|
12
|
-
|
13
|
-
def load_paths
|
14
|
-
if @cached_load_paths_hash != (h = @original_load_paths.hash)
|
15
|
-
@cached_load_paths_subset = compute_subset
|
16
|
-
@cached_load_paths_hash = h
|
17
|
-
end
|
18
|
-
@cached_load_paths_subset
|
19
|
-
end
|
20
|
-
|
21
|
-
# E.g. '/a/b/' => true if a lookup path is '/a/b/c/', because '/a/b/' + 'c/ok' is within lookup.
|
22
|
-
def potentially_within_lookup?(full_dir_path)
|
23
|
-
@lookup_paths.any? { |p| p.start_with? full_dir_path }
|
24
|
-
end
|
25
|
-
|
26
|
-
# E.g. '/a/b' => true when a lookup path is '/a/'
|
27
|
-
def within_lookup?(full_path)
|
28
|
-
@lookup_paths.any? { |p| full_path.start_with?(p) }
|
29
|
-
end
|
30
|
-
|
31
|
-
def exist?(full_path)
|
32
|
-
within_lookup?(full_path) && File.exist?(full_path)
|
33
|
-
end
|
34
|
-
|
35
|
-
private
|
36
|
-
|
37
|
-
def compute_subset
|
38
|
-
@original_load_paths.map { |p| File.expand_path(p) }
|
39
|
-
.select { |p| within_lookup?(p) || potentially_within_lookup?(p) }
|
40
|
-
.freeze
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
class EveryLoadPaths
|
45
|
-
attr_reader :load_paths
|
46
|
-
def initialize(load_paths)
|
47
|
-
@load_paths = load_paths
|
48
|
-
end
|
49
|
-
|
50
|
-
def exist?(full_path)
|
51
|
-
File.exist?(full_path)
|
52
|
-
end
|
53
|
-
|
54
|
-
def within_lookup?(full_path)
|
55
|
-
true
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
5
|
attr_reader :load_paths, :loaded_features, :filter
|
60
|
-
def initialize(load_paths: $LOAD_PATH, loaded_features: $LOADED_FEATURES,
|
6
|
+
def initialize(load_paths: $LOAD_PATH, loaded_features: $LOADED_FEATURES, &filter)
|
61
7
|
@load_paths = load_paths
|
62
|
-
lookup_paths ||= Dir.getwd
|
63
|
-
lookup_paths = Array(lookup_paths)
|
64
|
-
|
65
|
-
if lookup_paths.include?('/')
|
66
|
-
@load_paths_subset = EveryLoadPaths.new(load_paths)
|
67
|
-
else
|
68
|
-
@load_paths_subset = LoadPathsSubset.new(load_paths: load_paths, lookup_paths: lookup_paths)
|
69
|
-
end
|
70
|
-
|
71
8
|
@loaded_features = loaded_features
|
72
9
|
@filter = filter
|
73
10
|
@paths_being_required = Set.new
|
@@ -146,7 +83,7 @@ module DeepCover
|
|
146
83
|
|
147
84
|
begin
|
148
85
|
@paths_being_required.add(found_path)
|
149
|
-
return yield(:not_in_covered_paths) unless
|
86
|
+
return yield(:not_in_covered_paths) unless DeepCover.within_lookup_paths?(found_path)
|
150
87
|
return yield(:not_supported) if found_path.end_with?('.so')
|
151
88
|
return yield(:skipped) if filter && filter.call(found_path)
|
152
89
|
|
@@ -197,27 +134,18 @@ module DeepCover
|
|
197
134
|
end
|
198
135
|
|
199
136
|
def cover_and_execute(path) # &fallback_block
|
200
|
-
|
201
|
-
|
202
|
-
rescue Parser::SyntaxError => e
|
203
|
-
if e.message =~ /contains escape sequences incompatible with UTF-8/
|
204
|
-
warn "Can't cover #{path} because of incompatible encoding (see issue #9)"
|
205
|
-
else
|
206
|
-
warn "The file #{path} can't be instrumented"
|
207
|
-
end
|
137
|
+
covered_code = DeepCover.coverage.covered_code_or_warn(path)
|
138
|
+
if covered_code.nil?
|
208
139
|
yield(:cover_failed)
|
209
140
|
raise "The fallback_block is supposed to either return or break, but didn't do either"
|
210
141
|
end
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
warn ["DeepCover is getting confused with the file #{path} and it won't be instrumented.",
|
215
|
-
'Please report this error and provide the source code around the following:',
|
216
|
-
e,
|
217
|
-
].join("\n")
|
142
|
+
|
143
|
+
success = covered_code.execute_code_or_warn
|
144
|
+
unless success
|
218
145
|
yield(:cover_failed)
|
219
146
|
raise "The fallback_block is supposed to either return or break, but didn't do either"
|
220
147
|
end
|
148
|
+
|
221
149
|
covered_code
|
222
150
|
end
|
223
151
|
end
|
data/lib/deep_cover/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: deep-cover
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Marc-André Lafortune
|
@@ -294,6 +294,7 @@ files:
|
|
294
294
|
- lib/deep_cover/core_ext/autoload_overrides.rb
|
295
295
|
- lib/deep_cover/core_ext/coverage_replacement.rb
|
296
296
|
- lib/deep_cover/core_ext/exec_callbacks.rb
|
297
|
+
- lib/deep_cover/core_ext/instruction_sequence_load_iseq.rb
|
297
298
|
- lib/deep_cover/core_ext/load_overrides.rb
|
298
299
|
- lib/deep_cover/core_ext/require_overrides.rb
|
299
300
|
- lib/deep_cover/coverage.rb
|
@@ -412,7 +413,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
412
413
|
version: '0'
|
413
414
|
requirements: []
|
414
415
|
rubyforge_project:
|
415
|
-
rubygems_version: 2.7.
|
416
|
+
rubygems_version: 2.7.6
|
416
417
|
signing_key:
|
417
418
|
specification_version: 4
|
418
419
|
summary: In depth coverage of your Ruby code.
|