deep-cover 0.5.7 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f2b3fc6dc22a4c501fd7090466901a9174a9ed0a782218970d1c23bcacb9ea56
4
- data.tar.gz: 4ea988ee9e3feea82db9df67cf3da0dd4d44229fb233becbff97953e4833730e
3
+ metadata.gz: f3b9feb6fdad25312b0111faf653599e6019c51ddbcd31446ec2c45d7c5403f3
4
+ data.tar.gz: 18c532649cdc00a98690fc3d66ced732e11b1c97a2787ab36d0d5e3151e20601
5
5
  SHA512:
6
- metadata.gz: 5c0737e921cdc4c6cd4cb4461bb2f7bdd95c4f9b71d0132cda39b6005fd16cd062df45042f8fd0cb7474cf3320281feef60e4903e41d62bf6b5269c344169020
7
- data.tar.gz: 752569da3e00e55e136f797903e8686e2a6f5fac70050dcc42c525a104d93dd9174ccd11d17b804b83f9b76c645242d6da823a7e879ca4932a81adca23a2cdab
6
+ metadata.gz: 1b68e3fc7a62f263f1971b7b50b753d5ca5472bee6231eba897771ff1e036efcc6f8cc5625b1e5b6ef3422a978f1e2876ba7b6f8488d176705e0b8f465d9786d
7
+ data.tar.gz: 9fcf65daa4618b3c9d056ed95fe1a3f7e9ae1e4d67768940989630fa9c1a45ff05c7504bffae5058e0597fea8f661e730d59fa0ec089446d2ad28ec5f4ab963c
@@ -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
- # Autoloaded files are not supported on jruby. We need to use binding_of_caller
13
- # And that appears to be unavailable in jruby.
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
- AutoloadOverride.active = true
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
- require_relative 'core_ext/require_overrides'
20
- RequireOverride.active = true
26
+
21
27
  config # actualize configuration
22
- @custom_requirer = nil
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
- @custom_requirer = nil
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(lookup_paths: config.paths)
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
@@ -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, lookup_paths: nil, &filter)
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 @load_paths_subset.within_lookup?(found_path)
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
- begin
201
- covered_code = DeepCover.coverage.covered_code(path)
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
- begin
212
- covered_code.execute_code
213
- rescue ::SyntaxError => e
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DeepCover
4
- VERSION = '0.5.7'
4
+ VERSION = '0.6.0'
5
5
  end
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.5.7
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.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.