deep-cover 0.6.2 → 0.6.3.pre

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