deep-cover 0.6.2 → 0.6.3.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (139) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -1
  3. data/.rspec +2 -1
  4. data/.rspec_all +2 -1
  5. data/.rubocop.yml +8 -9
  6. data/Gemfile +2 -0
  7. data/Rakefile +32 -6
  8. data/bin/cov +3 -3
  9. data/deep_cover.gemspec +3 -16
  10. data/exe/deep-cover +5 -0
  11. data/lib/deep_cover/cli/debugger.rb +1 -1
  12. data/lib/deep_cover/cli/exec.rb +1 -1
  13. data/lib/deep_cover/cli/instrumented_clone_reporter.rb +5 -3
  14. data/lib/deep_cover/cli/runner.rb +2 -2
  15. data/lib/deep_cover/{tools/dump_covered_code.rb → dump_covered_code.rb} +2 -0
  16. metadata +10 -203
  17. data/lib/deep-cover.rb +0 -3
  18. data/lib/deep_cover.rb +0 -22
  19. data/lib/deep_cover/analyser.rb +0 -23
  20. data/lib/deep_cover/analyser/base.rb +0 -104
  21. data/lib/deep_cover/analyser/branch.rb +0 -41
  22. data/lib/deep_cover/analyser/covered_code_source.rb +0 -21
  23. data/lib/deep_cover/analyser/function.rb +0 -14
  24. data/lib/deep_cover/analyser/node.rb +0 -54
  25. data/lib/deep_cover/analyser/per_char.rb +0 -38
  26. data/lib/deep_cover/analyser/per_line.rb +0 -41
  27. data/lib/deep_cover/analyser/ruby25_like_branch.rb +0 -211
  28. data/lib/deep_cover/analyser/statement.rb +0 -33
  29. data/lib/deep_cover/analyser/stats.rb +0 -54
  30. data/lib/deep_cover/analyser/subset.rb +0 -27
  31. data/lib/deep_cover/auto_run.rb +0 -71
  32. data/lib/deep_cover/autoload_tracker.rb +0 -215
  33. data/lib/deep_cover/backports.rb +0 -22
  34. data/lib/deep_cover/base.rb +0 -117
  35. data/lib/deep_cover/basics.rb +0 -22
  36. data/lib/deep_cover/builtin_takeover.rb +0 -10
  37. data/lib/deep_cover/config.rb +0 -104
  38. data/lib/deep_cover/config_setter.rb +0 -33
  39. data/lib/deep_cover/core_ext/autoload_overrides.rb +0 -112
  40. data/lib/deep_cover/core_ext/coverage_replacement.rb +0 -61
  41. data/lib/deep_cover/core_ext/exec_callbacks.rb +0 -27
  42. data/lib/deep_cover/core_ext/instruction_sequence_load_iseq.rb +0 -32
  43. data/lib/deep_cover/core_ext/load_overrides.rb +0 -19
  44. data/lib/deep_cover/core_ext/require_overrides.rb +0 -28
  45. data/lib/deep_cover/coverage.rb +0 -125
  46. data/lib/deep_cover/coverage/analysis.rb +0 -42
  47. data/lib/deep_cover/coverage/persistence.rb +0 -84
  48. data/lib/deep_cover/covered_code.rb +0 -145
  49. data/lib/deep_cover/custom_requirer.rb +0 -187
  50. data/lib/deep_cover/flag_comment_associator.rb +0 -68
  51. data/lib/deep_cover/load.rb +0 -66
  52. data/lib/deep_cover/memoize.rb +0 -48
  53. data/lib/deep_cover/module_override.rb +0 -39
  54. data/lib/deep_cover/node.rb +0 -23
  55. data/lib/deep_cover/node/arguments.rb +0 -51
  56. data/lib/deep_cover/node/assignments.rb +0 -273
  57. data/lib/deep_cover/node/base.rb +0 -155
  58. data/lib/deep_cover/node/begin.rb +0 -27
  59. data/lib/deep_cover/node/block.rb +0 -61
  60. data/lib/deep_cover/node/branch.rb +0 -32
  61. data/lib/deep_cover/node/case.rb +0 -113
  62. data/lib/deep_cover/node/collections.rb +0 -31
  63. data/lib/deep_cover/node/const.rb +0 -12
  64. data/lib/deep_cover/node/def.rb +0 -40
  65. data/lib/deep_cover/node/empty_body.rb +0 -32
  66. data/lib/deep_cover/node/exceptions.rb +0 -79
  67. data/lib/deep_cover/node/if.rb +0 -73
  68. data/lib/deep_cover/node/keywords.rb +0 -86
  69. data/lib/deep_cover/node/literals.rb +0 -100
  70. data/lib/deep_cover/node/loops.rb +0 -74
  71. data/lib/deep_cover/node/mixin/can_augment_children.rb +0 -65
  72. data/lib/deep_cover/node/mixin/check_completion.rb +0 -18
  73. data/lib/deep_cover/node/mixin/child_can_be_empty.rb +0 -27
  74. data/lib/deep_cover/node/mixin/executed_after_children.rb +0 -15
  75. data/lib/deep_cover/node/mixin/execution_location.rb +0 -66
  76. data/lib/deep_cover/node/mixin/filters.rb +0 -47
  77. data/lib/deep_cover/node/mixin/flow_accounting.rb +0 -71
  78. data/lib/deep_cover/node/mixin/has_child.rb +0 -145
  79. data/lib/deep_cover/node/mixin/has_child_handler.rb +0 -75
  80. data/lib/deep_cover/node/mixin/has_tracker.rb +0 -46
  81. data/lib/deep_cover/node/mixin/is_statement.rb +0 -20
  82. data/lib/deep_cover/node/mixin/rewriting.rb +0 -35
  83. data/lib/deep_cover/node/mixin/wrapper.rb +0 -15
  84. data/lib/deep_cover/node/module.rb +0 -66
  85. data/lib/deep_cover/node/root.rb +0 -20
  86. data/lib/deep_cover/node/send.rb +0 -161
  87. data/lib/deep_cover/node/short_circuit.rb +0 -42
  88. data/lib/deep_cover/node/splat.rb +0 -15
  89. data/lib/deep_cover/node/variables.rb +0 -16
  90. data/lib/deep_cover/parser_ext/range.rb +0 -21
  91. data/lib/deep_cover/problem_with_diagnostic.rb +0 -63
  92. data/lib/deep_cover/reporter.rb +0 -10
  93. data/lib/deep_cover/reporter/base.rb +0 -68
  94. data/lib/deep_cover/reporter/html.rb +0 -15
  95. data/lib/deep_cover/reporter/html/base.rb +0 -14
  96. data/lib/deep_cover/reporter/html/index.rb +0 -59
  97. data/lib/deep_cover/reporter/html/site.rb +0 -70
  98. data/lib/deep_cover/reporter/html/source.rb +0 -136
  99. data/lib/deep_cover/reporter/html/template/assets/32px.png +0 -0
  100. data/lib/deep_cover/reporter/html/template/assets/40px.png +0 -0
  101. data/lib/deep_cover/reporter/html/template/assets/deep_cover.css.sass +0 -336
  102. data/lib/deep_cover/reporter/html/template/assets/jquery-3.2.1.min.js +0 -4
  103. data/lib/deep_cover/reporter/html/template/assets/jquery-3.2.1.min.map +0 -1
  104. data/lib/deep_cover/reporter/html/template/assets/jstree.css +0 -1108
  105. data/lib/deep_cover/reporter/html/template/assets/jstree.js +0 -8424
  106. data/lib/deep_cover/reporter/html/template/assets/jstreetable.js +0 -1069
  107. data/lib/deep_cover/reporter/html/template/assets/throbber.gif +0 -0
  108. data/lib/deep_cover/reporter/html/template/index.html.erb +0 -75
  109. data/lib/deep_cover/reporter/html/template/source.html.erb +0 -35
  110. data/lib/deep_cover/reporter/istanbul.rb +0 -184
  111. data/lib/deep_cover/reporter/text.rb +0 -58
  112. data/lib/deep_cover/reporter/tree/util.rb +0 -86
  113. data/lib/deep_cover/tools.rb +0 -22
  114. data/lib/deep_cover/tools/blank.rb +0 -25
  115. data/lib/deep_cover/tools/builtin_coverage.rb +0 -55
  116. data/lib/deep_cover/tools/camelize.rb +0 -13
  117. data/lib/deep_cover/tools/content_tag.rb +0 -11
  118. data/lib/deep_cover/tools/covered.rb +0 -9
  119. data/lib/deep_cover/tools/execute_sample.rb +0 -34
  120. data/lib/deep_cover/tools/format.rb +0 -18
  121. data/lib/deep_cover/tools/format_char_cover.rb +0 -19
  122. data/lib/deep_cover/tools/format_generated_code.rb +0 -27
  123. data/lib/deep_cover/tools/indent_string.rb +0 -26
  124. data/lib/deep_cover/tools/merge.rb +0 -16
  125. data/lib/deep_cover/tools/number_lines.rb +0 -22
  126. data/lib/deep_cover/tools/our_coverage.rb +0 -11
  127. data/lib/deep_cover/tools/profiling.rb +0 -68
  128. data/lib/deep_cover/tools/render_template.rb +0 -13
  129. data/lib/deep_cover/tools/require_relative_dir.rb +0 -12
  130. data/lib/deep_cover/tools/scan_match_datas.rb +0 -10
  131. data/lib/deep_cover/tools/silence_warnings.rb +0 -18
  132. data/lib/deep_cover/tools/slice.rb +0 -9
  133. data/lib/deep_cover/tools/strip_heredoc.rb +0 -18
  134. data/lib/deep_cover/tools/truncate_backtrace.rb +0 -32
  135. data/lib/deep_cover/tracker_bucket.rb +0 -50
  136. data/lib/deep_cover/tracker_hits_per_path.rb +0 -35
  137. data/lib/deep_cover/tracker_storage.rb +0 -76
  138. data/lib/deep_cover/tracker_storage_per_path.rb +0 -34
  139. data/lib/deep_cover/version.rb +0 -5
@@ -1,125 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DeepCover
4
- bootstrap
5
-
6
- require_relative_dir 'coverage'
7
-
8
- # A collection of CoveredCode
9
- class Coverage
10
- include Enumerable
11
-
12
- attr_reader :tracker_storage_per_path
13
-
14
- def initialize(**options)
15
- @covered_code_index = {}
16
- @options = options
17
- @tracker_storage_per_path = TrackerStoragePerPath.new(TrackerBucket[tracker_global])
18
- end
19
-
20
- def covered_codes
21
- each.to_a
22
- end
23
-
24
- def line_coverage(filename, **options)
25
- covered_code(filename).line_coverage(**options)
26
- end
27
-
28
- def covered_code?(path)
29
- @covered_code_index.include?(path)
30
- end
31
-
32
- def covered_code(path, **options)
33
- raise 'path must be an absolute path' unless Pathname.new(path).absolute?
34
- @covered_code_index[path] ||= CoveredCode.new(path: path,
35
- tracker_storage: @tracker_storage_per_path[path],
36
- **options,
37
- **@options)
38
- end
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
-
52
- def each
53
- return to_enum unless block_given?
54
- @tracker_storage_per_path.each_key do |path|
55
- begin
56
- cov_code = covered_code(path)
57
- rescue Parser::SyntaxError
58
- next
59
- end
60
- yield cov_code
61
- end
62
- self
63
- end
64
-
65
- def report(**options)
66
- case (reporter = options.fetch(:reporter, DEFAULTS[:reporter]).to_sym)
67
- when :html
68
- msg = if (output = options.fetch(:output, DEFAULTS[:output]))
69
- Reporter::HTML.report(self, **options)
70
- "HTML generated: open #{output}/index.html"
71
- else
72
- 'No HTML generated'
73
- end
74
- Reporter::Text.report(self, **options) + "\n\n" + msg
75
- when :istanbul
76
- if Reporter::Istanbul.available?
77
- Reporter::Istanbul.report(self, **options)
78
- else
79
- warn 'nyc not available. Please install `nyc` using `yarn global add nyc` or `npm i nyc -g`'
80
- end
81
- when :text
82
- Reporter::Text.report(self, **options)
83
- else
84
- raise ArgumentError, "Unknown reporter: #{reporter}"
85
- end
86
- end
87
-
88
- def self.load(dest_path, dirname = 'deep_cover', with_trackers: true)
89
- Persistence.new(dest_path, dirname).load(with_trackers: with_trackers)
90
- end
91
-
92
- def self.saved?(dest_path, dirname = 'deep_cover')
93
- Persistence.new(dest_path, dirname).saved?
94
- end
95
-
96
- def save(dest_path, dirname = 'deep_cover')
97
- Persistence.new(dest_path, dirname).save(self)
98
- self
99
- end
100
-
101
- def save_trackers(dest_path, dirname = 'deep_cover')
102
- Persistence.new(dest_path, dirname).save_trackers(@tracker_storage_per_path.tracker_hits_per_path)
103
- self
104
- end
105
-
106
- def tracker_global
107
- @options.fetch(:tracker_global, DEFAULTS[:tracker_global])
108
- end
109
-
110
- def analysis(**options)
111
- Analysis.new(covered_codes, options)
112
- end
113
-
114
- private
115
-
116
- def marshal_dump
117
- {options: @options, tracker_storage_per_path: @tracker_storage_per_path}
118
- end
119
-
120
- def marshal_load(options:, tracker_storage_per_path:)
121
- initialize(**options)
122
- @tracker_storage_per_path = tracker_storage_per_path
123
- end
124
- end
125
- end
@@ -1,42 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DeepCover
4
- class Coverage
5
- class Analysis < Struct.new(:covered_codes, :options)
6
- include Memoize
7
- memoize :analyser_map, :stat_map
8
-
9
- def analyser_map
10
- covered_codes.map do |covered_code|
11
- [covered_code, compute_analysers(covered_code)]
12
- end.to_h
13
- end
14
-
15
- def stat_map
16
- analyser_map.transform_values { |a| a.transform_values(&:stats) }
17
- end
18
-
19
- def overall
20
- return 100 if stat_map.empty?
21
- node, branch = Tools.merge(*stat_map.values, :+).values_at(:node, :branch)
22
- (node + branch).percent_covered
23
- end
24
-
25
- def self.template
26
- {node: Analyser::Node, per_char: Analyser::PerChar, branch: Analyser::Branch}
27
- end
28
-
29
- private
30
-
31
- def compute_analysers(covered_code)
32
- base = Analyser::Node.new(covered_code, **options)
33
- {node: base}.merge!(
34
- {
35
- per_char: Analyser::PerChar,
36
- branch: Analyser::Branch,
37
- }.transform_values { |klass| klass.new(base, **options) }
38
- )
39
- end
40
- end
41
- end
42
- end
@@ -1,84 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DeepCover
4
- require 'securerandom'
5
- class Coverage
6
- class Persistence
7
- BASENAME = 'coverage.dc'
8
- TRACKER_TEMPLATE = 'trackers%{unique}.dct'
9
-
10
- attr_reader :dir_path
11
- def initialize(dest_path, dirname)
12
- @dir_path = Pathname(dest_path).join(dirname).expand_path
13
- end
14
-
15
- def load(with_trackers: true)
16
- saved?
17
- cov = load_coverage
18
- cov.tracker_storage_per_path.tracker_hits_per_path = load_trackers if with_trackers
19
- cov
20
- end
21
-
22
- def save(coverage)
23
- create_if_needed
24
- delete_trackers
25
- save_coverage(coverage)
26
- end
27
-
28
- def save_trackers(tracker_hits_per_path)
29
- saved?
30
- basename = format(TRACKER_TEMPLATE, unique: SecureRandom.urlsafe_base64)
31
- dir_path.join(basename).binwrite(Marshal.dump(
32
- version: DeepCover::VERSION,
33
- tracker_hits_per_path: tracker_hits_per_path,
34
- ))
35
- end
36
-
37
- def saved?
38
- raise "Can't find folder '#{dir_path}'" unless dir_path.exist?
39
- self
40
- end
41
-
42
- private
43
-
44
- def create_if_needed
45
- dir_path.mkpath
46
- end
47
-
48
- def save_coverage(coverage)
49
- dir_path.join(BASENAME).binwrite(Marshal.dump(
50
- version: DeepCover::VERSION,
51
- coverage: coverage,
52
- ))
53
- end
54
-
55
- # rubocop:disable Security/MarshalLoad
56
- def load_coverage
57
- Marshal.load(dir_path.join(BASENAME).binread).tap do |version:, coverage:|
58
- raise "dump version mismatch: #{version}, currently #{DeepCover::VERSION}" unless version == DeepCover::VERSION
59
- return coverage
60
- end
61
- end
62
-
63
- # returns a TrackerHitsPerPath
64
- def load_trackers
65
- tracker_files.map do |full_path|
66
- Marshal.load(full_path.binread).yield_self do |version:, tracker_hits_per_path:|
67
- raise "dump version mismatch: #{version}, currently #{DeepCover::VERSION}" unless version == DeepCover::VERSION
68
- tracker_hits_per_path
69
- end
70
- end.inject(:merge!)
71
- end
72
- # rubocop:enable Security/MarshalLoad
73
-
74
- def tracker_files
75
- basename = format(TRACKER_TEMPLATE, unique: '*')
76
- Pathname.glob(dir_path.join(basename))
77
- end
78
-
79
- def delete_trackers
80
- tracker_files.each(&:delete)
81
- end
82
- end
83
- end
84
- end
@@ -1,145 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DeepCover
4
- bootstrap
5
- load_parser
6
-
7
- class CoveredCode
8
- attr_accessor :covered_source, :buffer, :tracker_storage, :local_var, :path
9
-
10
- def initialize(
11
- path: nil,
12
- source: nil,
13
- lineno: 1,
14
- tracker_global: DEFAULTS[:tracker_global],
15
- tracker_storage: TrackerBucket[tracker_global].create_storage,
16
- local_var: '_temp'
17
- )
18
- raise 'Must provide either path or source' unless path || source
19
-
20
- @path = path &&= Pathname(path)
21
- @buffer = Parser::Source::Buffer.new('', lineno)
22
- @buffer.source = source || path.read
23
- @tracker_count = 0
24
- @tracker_storage = tracker_storage
25
- @local_var = local_var
26
- @covered_source = instrument_source
27
- end
28
-
29
- def lineno
30
- @buffer.first_line
31
- end
32
-
33
- def nb_lines
34
- lines = buffer.source_lines
35
- if lines.empty?
36
- 0
37
- else
38
- lines.size - (lines.last.empty? ? 1 : 0)
39
- end
40
- end
41
-
42
- def execute_code(binding: DeepCover::GLOBAL_BINDING.dup)
43
- eval(@covered_source, binding, (@path || '<raw_code>').to_s, lineno) # rubocop:disable Security/Eval
44
- self
45
- end
46
-
47
- def execute_code_or_warn(*args)
48
- warn_instead_of_syntax_error do
49
- execute_code(*args)
50
- end
51
- end
52
-
53
- def cover
54
- global[nb] ||= Array.new(@tracker_count, 0)
55
- end
56
-
57
- def line_coverage(**options)
58
- Analyser::PerLine.new(self, **options).results
59
- end
60
-
61
- def char_cover(**options)
62
- Analyser::PerChar.new(self, **options).results
63
- end
64
-
65
- def tracker_hits(tracker_id)
66
- cover.fetch(tracker_id)
67
- end
68
-
69
- def covered_ast
70
- root.main
71
- end
72
-
73
- def comments
74
- root
75
- @comments
76
- end
77
-
78
- def root
79
- @root ||= begin
80
- ast, @comments = parser.parse_with_comments(@buffer)
81
- Node::Root.new(ast, self)
82
- end
83
- end
84
-
85
- def each_node(*args, &block)
86
- covered_ast.each_node(*args, &block)
87
- end
88
-
89
- def instrument_source
90
- rewriter = Parser::Source::TreeRewriter.new(@buffer)
91
- covered_ast.each_node(:postorder) do |node|
92
- node.rewriting_rules.each do |range, rule|
93
- prefix, _node, suffix = rule.partition('%{node}')
94
- prefix = yield prefix, node, range.begin, :prefix if block_given? && !prefix.empty?
95
- suffix = yield suffix, node, range.end, :suffix if block_given? && !suffix.empty?
96
- rewriter.wrap(range, prefix, suffix)
97
- end
98
- end
99
- rewriter.process
100
- end
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
-
112
- def freeze
113
- unless frozen? # Guard against reentrance
114
- super
115
- root.each_node(&:freeze)
116
- end
117
- self
118
- end
119
-
120
- def inspect
121
- %{#<DeepCover::CoveredCode "#{path}">}
122
- end
123
- alias_method :to_s, :inspect
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
-
136
- private
137
-
138
- def parser
139
- Parser::CurrentRuby.new.tap do |parser|
140
- parser.diagnostics.all_errors_are_fatal = true
141
- parser.diagnostics.ignore_warnings = true
142
- end
143
- end
144
- end
145
- end
@@ -1,187 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DeepCover
4
- class CustomRequirer
5
- attr_reader :load_paths, :loaded_features, :filter
6
- def initialize(load_paths: $LOAD_PATH, loaded_features: $LOADED_FEATURES, &filter)
7
- @load_paths = load_paths
8
- @loaded_features = loaded_features
9
- @filter = filter
10
- @paths_being_required = Set.new
11
-
12
- # A Set of the loaded_features for faster access
13
- @loaded_features_set = Set.new
14
- # A dup of the loaded_features as they are expected to be for the Set to be valid
15
- # If this is different from loaded_features, the set should be refreshed
16
- @duped_loaded_features_used_for_set = []
17
- end
18
-
19
- # Returns a path to an existing file or nil if none can be found.
20
- # The search follows how ruby search for files using the $LOAD_PATH, but limits
21
- # those it checks based on the LoadPathsSubset.
22
- #
23
- # An absolute path is returned directly if it exists, otherwise nil is returned
24
- # without searching anywhere else.
25
- def resolve_path(path, extensions_to_try = ['.rb', '.so'])
26
- if extensions_to_try
27
- extensions_to_try = [''] if extensions_to_try.any? { |ext| path.end_with?(ext) }
28
- else
29
- extensions_to_try = ['']
30
- end
31
-
32
- abs_path = File.absolute_path(path)
33
- path = abs_path if path.start_with?('./', '../')
34
-
35
- paths_with_ext = extensions_to_try.map { |ext| path + ext }
36
-
37
- refresh_loaded_features_set
38
-
39
- # Doing this check in every case instead of only for absolute_path because ruby has some
40
- # built-in $LOADED_FEATURES which aren't an absolute path. Ex: enumerator.so, thread.rb
41
- path_from_loaded_features = first_path_from_loaded_features_set(paths_with_ext)
42
- return path_from_loaded_features if path_from_loaded_features
43
-
44
- if path == abs_path
45
- paths_with_ext.each do |path_with_ext|
46
- return path_with_ext if File.exist?(path_with_ext)
47
- end
48
- else
49
- possible_paths = paths_with_load_paths(paths_with_ext)
50
- path_from_loaded_features = first_path_from_loaded_features_set(possible_paths)
51
- return path_from_loaded_features if path_from_loaded_features
52
-
53
- possible_paths.each do |possible_path|
54
- next unless File.exist?(possible_path)
55
- # Ruby 2.5 changed some behaviors of require related to symlinks in $LOAD_PATH
56
- # https://bugs.ruby-lang.org/issues/10222
57
- return File.realpath(possible_path) if RUBY_VERSION >= '2.5'
58
- return possible_path
59
- end
60
- end
61
- nil
62
- end
63
-
64
- # Homemade #require to be able to instrument the code before it gets executed.
65
- # Returns true when everything went right. (Same as regular ruby)
66
- # Returns false when the found file was already required. (Same as regular ruby)
67
- # Calls &fallback_block with the reason as parameter if the work couldn't be done.
68
- # The possible reasons are:
69
- # - :not_found if the file couldn't be found.
70
- # - :not_in_covered_paths if the file is not in the paths to cover
71
- # - :cover_failed if DeepCover couldn't apply instrumentation the file found.
72
- # - :not_supported for files that are not supported (such as .so files)
73
- # - :skipped if the filter block returned `true`
74
- # Exceptions raised by the required code bubble up as normal, except for
75
- # SyntaxError, which is turned into a :cover_failed which calls the fallback_block.
76
- def require(path) # &fallback_block
77
- path = path.to_s
78
-
79
- found_path = resolve_path(path)
80
-
81
- if found_path
82
- return false if @loaded_features.include?(found_path)
83
- return false if @paths_being_required.include?(found_path)
84
- end
85
-
86
- DeepCover.autoload_tracker.wrap_require(path, found_path) do
87
- begin
88
- # Either a problem with resolve_path, or a gem that will be added to the load_path by RubyGems
89
- return yield(:not_found) unless found_path
90
-
91
- @paths_being_required.add(found_path)
92
- return yield(:not_in_covered_paths) unless DeepCover.within_lookup_paths?(found_path)
93
- return yield(:not_supported) if found_path.end_with?('.so')
94
- return yield(:skipped) if filter && filter.call(found_path)
95
-
96
- cover_and_execute(found_path) { |reason| return yield(reason) }
97
-
98
- @loaded_features << found_path
99
- ensure
100
- @paths_being_required.delete(found_path)
101
- add_last_loaded_feature_to_set
102
- end
103
- end
104
- true
105
- end
106
-
107
- # Homemade #load to be able to instrument the code before it gets executed.
108
- # Note, this doesn't support the `wrap` parameter that ruby's #load has.
109
- # Same yield/return behavior as CustomRequirer#require, except that it
110
- # cannot return false #load doesn't care about a file already being executed.
111
- def load(path) # &fallback_block
112
- path = path.to_s
113
-
114
- found_path = resolve_path(path, nil)
115
-
116
- if found_path.nil?
117
- # #load has a final fallback of always trying relative to current work directory
118
- possible_path = File.absolute_path(path)
119
- found_path = possible_path if File.exist?(possible_path)
120
- end
121
-
122
- return yield(:not_found) unless found_path
123
- return yield(:not_in_covered_paths) unless DeepCover.within_lookup_paths?(found_path)
124
-
125
- cover_and_execute(found_path) { |reason| return yield(reason) }
126
-
127
- true
128
- end
129
-
130
- def is_being_required?(path)
131
- found_path = resolve_path(path)
132
- @paths_being_required.include?(found_path)
133
- end
134
-
135
- protected
136
-
137
- # updates the loaded_features_set if it needs it
138
- def refresh_loaded_features_set
139
- return if @duped_loaded_features_used_for_set == @loaded_features
140
-
141
- @duped_loaded_features_used_for_set = @loaded_features.dup
142
- @loaded_features_set = Set.new(@duped_loaded_features_used_for_set)
143
- end
144
-
145
- # Returns the first path found in the loaded_features_set
146
- # Should be called after doing a #refresh_loaded_features_set
147
- def first_path_from_loaded_features_set(paths)
148
- paths.detect { |path| @loaded_features_set.include?(path) }
149
- end
150
-
151
- # Called after a require, adds the last entry of loaded_features to the
152
- # loaded_features_set and the clone used to check for a need to refresh
153
- # the loaded_features_set. Doing this allows us to never need to update
154
- # the loaded_feature_set from scratch (almost? this is a safety precaution)
155
- def add_last_loaded_feature_to_set
156
- loaded_feature = @loaded_features.last
157
- unless @loaded_features_set.include?(loaded_feature)
158
- @duped_loaded_features_used_for_set << loaded_feature
159
- @loaded_features_set << loaded_feature
160
- end
161
- end
162
-
163
- def paths_with_load_paths(paths)
164
- paths.flat_map do |path|
165
- @load_paths.map do |load_path|
166
- File.absolute_path(path, load_path)
167
- end
168
- end
169
- end
170
-
171
- def cover_and_execute(path) # &fallback_block
172
- covered_code = DeepCover.coverage.covered_code_or_warn(path)
173
- if covered_code.nil?
174
- yield(:cover_failed)
175
- raise "The fallback_block is supposed to either return or break, but didn't do either"
176
- end
177
-
178
- success = covered_code.execute_code_or_warn
179
- unless success
180
- yield(:cover_failed)
181
- raise "The fallback_block is supposed to either return or break, but didn't do either"
182
- end
183
-
184
- covered_code
185
- end
186
- end
187
- end