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