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.
- checksums.yaml +4 -4
- data/.gitignore +3 -1
- data/.rspec +2 -1
- data/.rspec_all +2 -1
- data/.rubocop.yml +8 -9
- data/Gemfile +2 -0
- data/Rakefile +32 -6
- data/bin/cov +3 -3
- data/deep_cover.gemspec +3 -16
- data/exe/deep-cover +5 -0
- data/lib/deep_cover/cli/debugger.rb +1 -1
- data/lib/deep_cover/cli/exec.rb +1 -1
- data/lib/deep_cover/cli/instrumented_clone_reporter.rb +5 -3
- data/lib/deep_cover/cli/runner.rb +2 -2
- data/lib/deep_cover/{tools/dump_covered_code.rb → dump_covered_code.rb} +2 -0
- metadata +10 -203
- data/lib/deep-cover.rb +0 -3
- data/lib/deep_cover.rb +0 -22
- data/lib/deep_cover/analyser.rb +0 -23
- data/lib/deep_cover/analyser/base.rb +0 -104
- data/lib/deep_cover/analyser/branch.rb +0 -41
- data/lib/deep_cover/analyser/covered_code_source.rb +0 -21
- data/lib/deep_cover/analyser/function.rb +0 -14
- data/lib/deep_cover/analyser/node.rb +0 -54
- data/lib/deep_cover/analyser/per_char.rb +0 -38
- data/lib/deep_cover/analyser/per_line.rb +0 -41
- data/lib/deep_cover/analyser/ruby25_like_branch.rb +0 -211
- data/lib/deep_cover/analyser/statement.rb +0 -33
- data/lib/deep_cover/analyser/stats.rb +0 -54
- data/lib/deep_cover/analyser/subset.rb +0 -27
- data/lib/deep_cover/auto_run.rb +0 -71
- data/lib/deep_cover/autoload_tracker.rb +0 -215
- data/lib/deep_cover/backports.rb +0 -22
- data/lib/deep_cover/base.rb +0 -117
- data/lib/deep_cover/basics.rb +0 -22
- data/lib/deep_cover/builtin_takeover.rb +0 -10
- data/lib/deep_cover/config.rb +0 -104
- data/lib/deep_cover/config_setter.rb +0 -33
- data/lib/deep_cover/core_ext/autoload_overrides.rb +0 -112
- data/lib/deep_cover/core_ext/coverage_replacement.rb +0 -61
- data/lib/deep_cover/core_ext/exec_callbacks.rb +0 -27
- data/lib/deep_cover/core_ext/instruction_sequence_load_iseq.rb +0 -32
- data/lib/deep_cover/core_ext/load_overrides.rb +0 -19
- data/lib/deep_cover/core_ext/require_overrides.rb +0 -28
- data/lib/deep_cover/coverage.rb +0 -125
- data/lib/deep_cover/coverage/analysis.rb +0 -42
- data/lib/deep_cover/coverage/persistence.rb +0 -84
- data/lib/deep_cover/covered_code.rb +0 -145
- data/lib/deep_cover/custom_requirer.rb +0 -187
- data/lib/deep_cover/flag_comment_associator.rb +0 -68
- data/lib/deep_cover/load.rb +0 -66
- data/lib/deep_cover/memoize.rb +0 -48
- data/lib/deep_cover/module_override.rb +0 -39
- data/lib/deep_cover/node.rb +0 -23
- data/lib/deep_cover/node/arguments.rb +0 -51
- data/lib/deep_cover/node/assignments.rb +0 -273
- data/lib/deep_cover/node/base.rb +0 -155
- data/lib/deep_cover/node/begin.rb +0 -27
- data/lib/deep_cover/node/block.rb +0 -61
- data/lib/deep_cover/node/branch.rb +0 -32
- data/lib/deep_cover/node/case.rb +0 -113
- data/lib/deep_cover/node/collections.rb +0 -31
- data/lib/deep_cover/node/const.rb +0 -12
- data/lib/deep_cover/node/def.rb +0 -40
- data/lib/deep_cover/node/empty_body.rb +0 -32
- data/lib/deep_cover/node/exceptions.rb +0 -79
- data/lib/deep_cover/node/if.rb +0 -73
- data/lib/deep_cover/node/keywords.rb +0 -86
- data/lib/deep_cover/node/literals.rb +0 -100
- data/lib/deep_cover/node/loops.rb +0 -74
- data/lib/deep_cover/node/mixin/can_augment_children.rb +0 -65
- data/lib/deep_cover/node/mixin/check_completion.rb +0 -18
- data/lib/deep_cover/node/mixin/child_can_be_empty.rb +0 -27
- data/lib/deep_cover/node/mixin/executed_after_children.rb +0 -15
- data/lib/deep_cover/node/mixin/execution_location.rb +0 -66
- data/lib/deep_cover/node/mixin/filters.rb +0 -47
- data/lib/deep_cover/node/mixin/flow_accounting.rb +0 -71
- data/lib/deep_cover/node/mixin/has_child.rb +0 -145
- data/lib/deep_cover/node/mixin/has_child_handler.rb +0 -75
- data/lib/deep_cover/node/mixin/has_tracker.rb +0 -46
- data/lib/deep_cover/node/mixin/is_statement.rb +0 -20
- data/lib/deep_cover/node/mixin/rewriting.rb +0 -35
- data/lib/deep_cover/node/mixin/wrapper.rb +0 -15
- data/lib/deep_cover/node/module.rb +0 -66
- data/lib/deep_cover/node/root.rb +0 -20
- data/lib/deep_cover/node/send.rb +0 -161
- data/lib/deep_cover/node/short_circuit.rb +0 -42
- data/lib/deep_cover/node/splat.rb +0 -15
- data/lib/deep_cover/node/variables.rb +0 -16
- data/lib/deep_cover/parser_ext/range.rb +0 -21
- data/lib/deep_cover/problem_with_diagnostic.rb +0 -63
- data/lib/deep_cover/reporter.rb +0 -10
- data/lib/deep_cover/reporter/base.rb +0 -68
- data/lib/deep_cover/reporter/html.rb +0 -15
- data/lib/deep_cover/reporter/html/base.rb +0 -14
- data/lib/deep_cover/reporter/html/index.rb +0 -59
- data/lib/deep_cover/reporter/html/site.rb +0 -70
- data/lib/deep_cover/reporter/html/source.rb +0 -136
- data/lib/deep_cover/reporter/html/template/assets/32px.png +0 -0
- data/lib/deep_cover/reporter/html/template/assets/40px.png +0 -0
- data/lib/deep_cover/reporter/html/template/assets/deep_cover.css.sass +0 -336
- data/lib/deep_cover/reporter/html/template/assets/jquery-3.2.1.min.js +0 -4
- data/lib/deep_cover/reporter/html/template/assets/jquery-3.2.1.min.map +0 -1
- data/lib/deep_cover/reporter/html/template/assets/jstree.css +0 -1108
- data/lib/deep_cover/reporter/html/template/assets/jstree.js +0 -8424
- data/lib/deep_cover/reporter/html/template/assets/jstreetable.js +0 -1069
- data/lib/deep_cover/reporter/html/template/assets/throbber.gif +0 -0
- data/lib/deep_cover/reporter/html/template/index.html.erb +0 -75
- data/lib/deep_cover/reporter/html/template/source.html.erb +0 -35
- data/lib/deep_cover/reporter/istanbul.rb +0 -184
- data/lib/deep_cover/reporter/text.rb +0 -58
- data/lib/deep_cover/reporter/tree/util.rb +0 -86
- data/lib/deep_cover/tools.rb +0 -22
- data/lib/deep_cover/tools/blank.rb +0 -25
- data/lib/deep_cover/tools/builtin_coverage.rb +0 -55
- data/lib/deep_cover/tools/camelize.rb +0 -13
- data/lib/deep_cover/tools/content_tag.rb +0 -11
- data/lib/deep_cover/tools/covered.rb +0 -9
- data/lib/deep_cover/tools/execute_sample.rb +0 -34
- data/lib/deep_cover/tools/format.rb +0 -18
- data/lib/deep_cover/tools/format_char_cover.rb +0 -19
- data/lib/deep_cover/tools/format_generated_code.rb +0 -27
- data/lib/deep_cover/tools/indent_string.rb +0 -26
- data/lib/deep_cover/tools/merge.rb +0 -16
- data/lib/deep_cover/tools/number_lines.rb +0 -22
- data/lib/deep_cover/tools/our_coverage.rb +0 -11
- data/lib/deep_cover/tools/profiling.rb +0 -68
- data/lib/deep_cover/tools/render_template.rb +0 -13
- data/lib/deep_cover/tools/require_relative_dir.rb +0 -12
- data/lib/deep_cover/tools/scan_match_datas.rb +0 -10
- data/lib/deep_cover/tools/silence_warnings.rb +0 -18
- data/lib/deep_cover/tools/slice.rb +0 -9
- data/lib/deep_cover/tools/strip_heredoc.rb +0 -18
- data/lib/deep_cover/tools/truncate_backtrace.rb +0 -32
- data/lib/deep_cover/tracker_bucket.rb +0 -50
- data/lib/deep_cover/tracker_hits_per_path.rb +0 -35
- data/lib/deep_cover/tracker_storage.rb +0 -76
- data/lib/deep_cover/tracker_storage_per_path.rb +0 -34
- data/lib/deep_cover/version.rb +0 -5
data/lib/deep_cover/coverage.rb
DELETED
@@ -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
|