deep-cover-core 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 +7 -0
- data/.rspec +4 -0
- data/.rspec_all +3 -0
- data/.rubocop.yml +1 -0
- data/Gemfile +11 -0
- data/deep_cover_core.gemspec +46 -0
- data/lib/deep-cover.rb +3 -0
- data/lib/deep_cover/analyser/base.rb +104 -0
- data/lib/deep_cover/analyser/branch.rb +41 -0
- data/lib/deep_cover/analyser/covered_code_source.rb +21 -0
- data/lib/deep_cover/analyser/function.rb +14 -0
- data/lib/deep_cover/analyser/node.rb +54 -0
- data/lib/deep_cover/analyser/per_char.rb +38 -0
- data/lib/deep_cover/analyser/per_line.rb +41 -0
- data/lib/deep_cover/analyser/ruby25_like_branch.rb +211 -0
- data/lib/deep_cover/analyser/statement.rb +33 -0
- data/lib/deep_cover/analyser/stats.rb +54 -0
- data/lib/deep_cover/analyser/subset.rb +27 -0
- data/lib/deep_cover/analyser.rb +23 -0
- data/lib/deep_cover/auto_run.rb +71 -0
- data/lib/deep_cover/autoload_tracker.rb +215 -0
- data/lib/deep_cover/backports.rb +22 -0
- data/lib/deep_cover/base.rb +117 -0
- data/lib/deep_cover/basics.rb +22 -0
- data/lib/deep_cover/builtin_takeover.rb +10 -0
- data/lib/deep_cover/config.rb +104 -0
- data/lib/deep_cover/config_setter.rb +33 -0
- data/lib/deep_cover/core_ext/autoload_overrides.rb +112 -0
- data/lib/deep_cover/core_ext/coverage_replacement.rb +61 -0
- data/lib/deep_cover/core_ext/exec_callbacks.rb +27 -0
- data/lib/deep_cover/core_ext/instruction_sequence_load_iseq.rb +32 -0
- data/lib/deep_cover/core_ext/load_overrides.rb +19 -0
- data/lib/deep_cover/core_ext/require_overrides.rb +28 -0
- data/lib/deep_cover/coverage/analysis.rb +42 -0
- data/lib/deep_cover/coverage/persistence.rb +84 -0
- data/lib/deep_cover/coverage.rb +125 -0
- data/lib/deep_cover/covered_code.rb +145 -0
- data/lib/deep_cover/custom_requirer.rb +187 -0
- data/lib/deep_cover/flag_comment_associator.rb +68 -0
- data/lib/deep_cover/load.rb +66 -0
- data/lib/deep_cover/memoize.rb +48 -0
- data/lib/deep_cover/module_override.rb +39 -0
- data/lib/deep_cover/node/arguments.rb +51 -0
- data/lib/deep_cover/node/assignments.rb +273 -0
- data/lib/deep_cover/node/base.rb +155 -0
- data/lib/deep_cover/node/begin.rb +27 -0
- data/lib/deep_cover/node/block.rb +61 -0
- data/lib/deep_cover/node/branch.rb +32 -0
- data/lib/deep_cover/node/case.rb +113 -0
- data/lib/deep_cover/node/collections.rb +31 -0
- data/lib/deep_cover/node/const.rb +12 -0
- data/lib/deep_cover/node/def.rb +40 -0
- data/lib/deep_cover/node/empty_body.rb +32 -0
- data/lib/deep_cover/node/exceptions.rb +79 -0
- data/lib/deep_cover/node/if.rb +73 -0
- data/lib/deep_cover/node/keywords.rb +86 -0
- data/lib/deep_cover/node/literals.rb +100 -0
- data/lib/deep_cover/node/loops.rb +74 -0
- data/lib/deep_cover/node/mixin/can_augment_children.rb +65 -0
- data/lib/deep_cover/node/mixin/check_completion.rb +18 -0
- data/lib/deep_cover/node/mixin/child_can_be_empty.rb +27 -0
- data/lib/deep_cover/node/mixin/executed_after_children.rb +15 -0
- data/lib/deep_cover/node/mixin/execution_location.rb +66 -0
- data/lib/deep_cover/node/mixin/filters.rb +47 -0
- data/lib/deep_cover/node/mixin/flow_accounting.rb +71 -0
- data/lib/deep_cover/node/mixin/has_child.rb +145 -0
- data/lib/deep_cover/node/mixin/has_child_handler.rb +75 -0
- data/lib/deep_cover/node/mixin/has_tracker.rb +46 -0
- data/lib/deep_cover/node/mixin/is_statement.rb +20 -0
- data/lib/deep_cover/node/mixin/rewriting.rb +35 -0
- data/lib/deep_cover/node/mixin/wrapper.rb +15 -0
- data/lib/deep_cover/node/module.rb +66 -0
- data/lib/deep_cover/node/root.rb +20 -0
- data/lib/deep_cover/node/send.rb +161 -0
- data/lib/deep_cover/node/short_circuit.rb +42 -0
- data/lib/deep_cover/node/splat.rb +15 -0
- data/lib/deep_cover/node/variables.rb +16 -0
- data/lib/deep_cover/node.rb +23 -0
- data/lib/deep_cover/parser_ext/range.rb +21 -0
- data/lib/deep_cover/problem_with_diagnostic.rb +63 -0
- data/lib/deep_cover/reporter/base.rb +68 -0
- data/lib/deep_cover/reporter/html/base.rb +14 -0
- data/lib/deep_cover/reporter/html/index.rb +59 -0
- data/lib/deep_cover/reporter/html/site.rb +68 -0
- data/lib/deep_cover/reporter/html/source.rb +136 -0
- 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 +291 -0
- data/lib/deep_cover/reporter/html/template/assets/deep_cover.css.sass +336 -0
- data/lib/deep_cover/reporter/html/template/assets/jquery-3.2.1.min.js +4 -0
- data/lib/deep_cover/reporter/html/template/assets/jquery-3.2.1.min.map +1 -0
- data/lib/deep_cover/reporter/html/template/assets/jstree.css +1108 -0
- data/lib/deep_cover/reporter/html/template/assets/jstree.js +8424 -0
- data/lib/deep_cover/reporter/html/template/assets/jstreetable.js +1069 -0
- data/lib/deep_cover/reporter/html/template/assets/throbber.gif +0 -0
- data/lib/deep_cover/reporter/html/template/index.html.erb +75 -0
- data/lib/deep_cover/reporter/html/template/source.html.erb +35 -0
- data/lib/deep_cover/reporter/html.rb +15 -0
- data/lib/deep_cover/reporter/istanbul.rb +184 -0
- data/lib/deep_cover/reporter/text.rb +58 -0
- data/lib/deep_cover/reporter/tree/util.rb +86 -0
- data/lib/deep_cover/reporter.rb +10 -0
- data/lib/deep_cover/tools/blank.rb +25 -0
- data/lib/deep_cover/tools/builtin_coverage.rb +55 -0
- data/lib/deep_cover/tools/camelize.rb +13 -0
- data/lib/deep_cover/tools/content_tag.rb +11 -0
- data/lib/deep_cover/tools/covered.rb +9 -0
- data/lib/deep_cover/tools/execute_sample.rb +34 -0
- data/lib/deep_cover/tools/format.rb +18 -0
- data/lib/deep_cover/tools/format_char_cover.rb +19 -0
- data/lib/deep_cover/tools/format_generated_code.rb +27 -0
- data/lib/deep_cover/tools/indent_string.rb +26 -0
- data/lib/deep_cover/tools/merge.rb +16 -0
- data/lib/deep_cover/tools/number_lines.rb +22 -0
- data/lib/deep_cover/tools/our_coverage.rb +11 -0
- data/lib/deep_cover/tools/profiling.rb +68 -0
- data/lib/deep_cover/tools/render_template.rb +13 -0
- data/lib/deep_cover/tools/require_relative_dir.rb +12 -0
- data/lib/deep_cover/tools/scan_match_datas.rb +10 -0
- data/lib/deep_cover/tools/silence_warnings.rb +18 -0
- data/lib/deep_cover/tools/slice.rb +9 -0
- data/lib/deep_cover/tools/strip_heredoc.rb +18 -0
- data/lib/deep_cover/tools/truncate_backtrace.rb +32 -0
- data/lib/deep_cover/tools.rb +22 -0
- data/lib/deep_cover/tracker_bucket.rb +50 -0
- data/lib/deep_cover/tracker_hits_per_path.rb +35 -0
- data/lib/deep_cover/tracker_storage.rb +76 -0
- data/lib/deep_cover/tracker_storage_per_path.rb +34 -0
- data/lib/deep_cover/version.rb +5 -0
- data/lib/deep_cover.rb +22 -0
- metadata +329 -0
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DeepCover
|
4
|
+
module Tools::IndentString
|
5
|
+
# In-place implementation copied from active-support.
|
6
|
+
IMPLEMENTATION = ->(amount, indent_string = nil, indent_empty_lines = false) do
|
7
|
+
indent_string = indent_string || self[/^[ \t]/] || ' '
|
8
|
+
re = indent_empty_lines ? /^/ : /^(?!$)/
|
9
|
+
gsub!(re, indent_string * amount)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Same as #indent! from active-support
|
13
|
+
# https://github.com/rails/rails/blob/10e1f1f9a129f2f197a44009a99b73b8ff9dbc0d/activesupport/lib/active_support/core_ext/string/indent.rb#L7
|
14
|
+
def indent_string!(string, *args)
|
15
|
+
string.instance_exec(*args, &IMPLEMENTATION)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Same as #indent from active-support
|
19
|
+
# https://github.com/rails/rails/blob/10e1f1f9a129f2f197a44009a99b73b8ff9dbc0d/activesupport/lib/active_support/core_ext/string/indent.rb#L42
|
20
|
+
def indent_string(string, *args)
|
21
|
+
string = string.dup
|
22
|
+
indent_string!(string, *args)
|
23
|
+
string
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DeepCover
|
4
|
+
module Tools::Merge
|
5
|
+
def merge(*hashes)
|
6
|
+
if hashes.last.is_a?(Symbol)
|
7
|
+
oper = hashes.pop
|
8
|
+
merge(*hashes) { |a, b| a.public_send(oper, b) }
|
9
|
+
elsif !block_given?
|
10
|
+
merge(*hashes, &:last)
|
11
|
+
else
|
12
|
+
hashes.inject { |result, h| result.merge(h) { |key, a, b| yield [a, b] } }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DeepCover
|
4
|
+
module Tools::NumberLines
|
5
|
+
def number_lines(lines, lineno: 1, bad_linenos: [])
|
6
|
+
max_lineno = lineno + lines.size - 1
|
7
|
+
nb_lineno_digits = max_lineno.to_s.size
|
8
|
+
lines.map.with_index do |line, i|
|
9
|
+
cur_lineno = lineno + i
|
10
|
+
cur_lineno_s = cur_lineno.to_s.rjust(nb_lineno_digits)
|
11
|
+
if bad_linenos.include?(cur_lineno)
|
12
|
+
cur_lineno_s = "*#{cur_lineno_s}" unless bad_linenos.empty?
|
13
|
+
prefix = Term::ANSIColor.red("#{cur_lineno_s} | ")
|
14
|
+
else
|
15
|
+
cur_lineno_s = " #{cur_lineno_s}" unless bad_linenos.empty?
|
16
|
+
prefix = Term::ANSIColor.white("#{cur_lineno_s} | ")
|
17
|
+
end
|
18
|
+
"#{prefix}#{line}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DeepCover
|
4
|
+
module Tools::OurCoverage
|
5
|
+
def our_coverage(source, filename, lineno, **options)
|
6
|
+
covered_code = CoveredCode.new(source: source, path: filename, lineno: lineno)
|
7
|
+
Tools.execute_sample(covered_code)
|
8
|
+
covered_code.line_coverage(options)[(lineno - 1)..-1]
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'delegate'
|
4
|
+
|
5
|
+
module DeepCover
|
6
|
+
module Tools::Profiling
|
7
|
+
# Simple forwarding to implementation
|
8
|
+
def start
|
9
|
+
profiler.start
|
10
|
+
end
|
11
|
+
|
12
|
+
def stop
|
13
|
+
@results = profiler.stop
|
14
|
+
end
|
15
|
+
|
16
|
+
def pause
|
17
|
+
profiler.pause
|
18
|
+
end
|
19
|
+
|
20
|
+
def resume
|
21
|
+
profiler.resume
|
22
|
+
end
|
23
|
+
|
24
|
+
def report
|
25
|
+
profiler.report(@results)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Basic utilities using forwarding methods
|
29
|
+
def profile(do_start = true)
|
30
|
+
return yield unless do_start
|
31
|
+
start
|
32
|
+
yield
|
33
|
+
stop
|
34
|
+
report
|
35
|
+
end
|
36
|
+
|
37
|
+
def dont_profile
|
38
|
+
pause if profiler_loaded?
|
39
|
+
yield
|
40
|
+
ensure
|
41
|
+
resume if profiler_loaded?
|
42
|
+
end
|
43
|
+
|
44
|
+
def profiler_loaded?
|
45
|
+
!!@profiler
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
# Dependency injection
|
51
|
+
def profiler
|
52
|
+
@profiler = RubyProfProfiler.new
|
53
|
+
end
|
54
|
+
|
55
|
+
class RubyProfProfiler < SimpleDelegator
|
56
|
+
def initialize
|
57
|
+
raise 'Profiling is not available when using JRuby' if RUBY_PLATFORM == 'java'
|
58
|
+
require 'ruby-prof'
|
59
|
+
super(RubyProf)
|
60
|
+
end
|
61
|
+
|
62
|
+
def report(results)
|
63
|
+
printer = RubyProf::GraphPrinter.new(results)
|
64
|
+
printer.print(STDOUT, {})
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DeepCover
|
4
|
+
module Tools::RenderTemplate
|
5
|
+
def render_template(template, bound_object)
|
6
|
+
require 'erb'
|
7
|
+
caller_path = Pathname.new(caller(1..1).first.partition(/\.rb:\d/).first).dirname
|
8
|
+
template = caller_path.join("template/#{template}.html.erb").read
|
9
|
+
erb = ERB.new(template)
|
10
|
+
erb.result(bound_object.instance_eval { binding })
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DeepCover
|
4
|
+
module Tools::RequireRelativeDir
|
5
|
+
def require_relative_dir(dir_name, except: [])
|
6
|
+
dir = File.dirname(caller(1..1).first.partition(/\.rb:\d/).first)
|
7
|
+
Dir["#{dir}/#{dir_name}/*.rb"].sort.each do |file|
|
8
|
+
require file unless except.include? File.basename(file, '.rb')
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DeepCover
|
4
|
+
module Tools::RequireRelativeDir
|
5
|
+
# Like String#scan, but return the MatchData object instead
|
6
|
+
def scan_match_datas(source, matcher)
|
7
|
+
source.to_enum(:scan, matcher).map { Regexp.last_match }
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DeepCover
|
4
|
+
module Tools::SilenceWarnings
|
5
|
+
# copied from: activesupport/lib/active_support/core_ext/kernel/reporting.rb
|
6
|
+
def silence_warnings
|
7
|
+
with_warnings(nil) { yield }
|
8
|
+
end
|
9
|
+
|
10
|
+
def with_warnings(flag)
|
11
|
+
old_verbose = $VERBOSE
|
12
|
+
$VERBOSE = flag
|
13
|
+
yield
|
14
|
+
ensure
|
15
|
+
$VERBOSE = old_verbose
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DeepCover
|
4
|
+
module Tools::StripHeredoc
|
5
|
+
# In-place implementation copied from active-support.
|
6
|
+
IMPLEMENTATION = -> do
|
7
|
+
gsub(/^#{scan(/^[ \t]*(?=\S)/).min}/, ''.freeze).tap do |stripped|
|
8
|
+
stripped.freeze if frozen?
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# Same as #strip_heredoc from active-support
|
13
|
+
# https://github.com/rails/rails/blob/16574409f813e2197f88e4a06b527618d64d9ff0/activesupport/lib/active_support/core_ext/string/strip.rb#L22
|
14
|
+
def strip_heredoc(string)
|
15
|
+
string.instance_exec(&IMPLEMENTATION)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DeepCover
|
4
|
+
module Tools::TruncateBacktrace
|
5
|
+
def truncate_backtrace(backtrace, extra_context: 10)
|
6
|
+
backtrace = backtrace.backtrace if backtrace.is_a?(Exception)
|
7
|
+
trace_lines = backtrace.uniq
|
8
|
+
|
9
|
+
keep_from_begin = 0
|
10
|
+
keep_from_end = backtrace.size - 1
|
11
|
+
|
12
|
+
trace_lines.each do |line|
|
13
|
+
from_begin = backtrace.index(line)
|
14
|
+
from_end = backtrace.rindex(line)
|
15
|
+
if from_begin <= backtrace.size - 1 - from_end
|
16
|
+
keep_from_begin = [keep_from_begin, from_begin].max
|
17
|
+
else
|
18
|
+
keep_from_end = [keep_from_end, from_end].min
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
keep_from_begin += extra_context
|
23
|
+
keep_from_end -= extra_context
|
24
|
+
|
25
|
+
return backtrace if keep_from_begin + 5 >= keep_from_end
|
26
|
+
|
27
|
+
result = backtrace[0..keep_from_begin]
|
28
|
+
result << "... #{keep_from_end - keep_from_begin - 1} levels..."
|
29
|
+
result.concat backtrace[keep_from_end..-1]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DeepCover
|
4
|
+
module Tools
|
5
|
+
end
|
6
|
+
|
7
|
+
require_relative 'tools/require_relative_dir'
|
8
|
+
extend Tools::RequireRelativeDir
|
9
|
+
require_relative 'tools/silence_warnings'
|
10
|
+
extend Tools::SilenceWarnings
|
11
|
+
require_relative_dir 'tools'
|
12
|
+
|
13
|
+
# The functions defined in the submodules of Tools can be accessed
|
14
|
+
# either by extending the desired module, or all of them by extending
|
15
|
+
# Tools, or by calling them directly Tool.my_function.
|
16
|
+
module Tools
|
17
|
+
constants.each do |module_name|
|
18
|
+
include const_get(module_name)
|
19
|
+
end
|
20
|
+
extend self
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DeepCover
|
4
|
+
bootstrap
|
5
|
+
|
6
|
+
require_relative 'tracker_storage'
|
7
|
+
|
8
|
+
# A holder for TrackerStorages, using some `global_name`.
|
9
|
+
class TrackerBucket
|
10
|
+
@@index = {}
|
11
|
+
|
12
|
+
def self.[](global_name)
|
13
|
+
raise ArgumentError, "'#{global_name}' is not a valid global name" unless global_name.start_with? '$'
|
14
|
+
@@index[global_name] ||= new(global_name)
|
15
|
+
end
|
16
|
+
|
17
|
+
def setup_source
|
18
|
+
"#{source} ||= {}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def source
|
22
|
+
@global_name
|
23
|
+
end
|
24
|
+
|
25
|
+
class << self
|
26
|
+
alias_method :_load, :[]
|
27
|
+
private :_load, :new
|
28
|
+
end
|
29
|
+
|
30
|
+
def inspect
|
31
|
+
%{#<DeepCover::TrackerBucket "#{@global_name}">}
|
32
|
+
end
|
33
|
+
|
34
|
+
def create_storage(index = nil)
|
35
|
+
index ||= @global.size
|
36
|
+
TrackerStorage.new(bucket: self, array: @global[index] ||= [], index: index)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def initialize(global_name)
|
42
|
+
@global_name = global_name
|
43
|
+
@global = eval(setup_source) # rubocop:disable Security/Eval
|
44
|
+
end
|
45
|
+
|
46
|
+
def _dump(_level)
|
47
|
+
@global_name
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DeepCover
|
4
|
+
bootstrap
|
5
|
+
|
6
|
+
# Should be seen as a hash like {path => tracker_hits, ...},
|
7
|
+
# where tracker_hits is simply an array of integers returned from
|
8
|
+
# TrackerStorage#tracker_hits.
|
9
|
+
# Make it easier to separate some concerns, as well as marshalling.
|
10
|
+
#
|
11
|
+
class TrackerHitsPerPath
|
12
|
+
extend Forwardable
|
13
|
+
def_delegators :@index, :each, :each_key, :map, :transform_values, :to_h, :to_hash
|
14
|
+
|
15
|
+
def initialize(index = {})
|
16
|
+
@index = index
|
17
|
+
end
|
18
|
+
|
19
|
+
def [](val)
|
20
|
+
@index[val] ||= []
|
21
|
+
end
|
22
|
+
|
23
|
+
def merge!(tracker_hits_per_path)
|
24
|
+
@index.merge!(tracker_hits_per_path) { |_h, actual, to_merge| merge_tracker_hits(actual, to_merge) }
|
25
|
+
self
|
26
|
+
end
|
27
|
+
|
28
|
+
private def merge_tracker_hits(hits, to_merge)
|
29
|
+
unless hits.size == to_merge.size
|
30
|
+
raise "Attempting to merge trackers of different sizes: #{hits.size} vs #{to_merge.size}"
|
31
|
+
end
|
32
|
+
hits.map!.with_index { |val, i| val + to_merge[i] }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DeepCover
|
4
|
+
bootstrap
|
5
|
+
|
6
|
+
# List of allocated trackers from a bucket.
|
7
|
+
# Should be thought of as a simple array of integers with
|
8
|
+
# a limited interface.
|
9
|
+
class TrackerBucket
|
10
|
+
class TrackerStorage
|
11
|
+
extend Forwardable
|
12
|
+
def_delegators :@array, :[], :size, :each, :map, :fetch
|
13
|
+
|
14
|
+
attr_reader :bucket
|
15
|
+
|
16
|
+
def initialize(bucket:, array:, index:)
|
17
|
+
@bucket = bucket
|
18
|
+
@array = array
|
19
|
+
@index = index
|
20
|
+
@allocated = 0
|
21
|
+
end
|
22
|
+
|
23
|
+
# Returns a range of tracker ids
|
24
|
+
def allocate_trackers(nb_needed)
|
25
|
+
prev = @allocated
|
26
|
+
@allocated += nb_needed
|
27
|
+
missing = @allocated - @array.size
|
28
|
+
@array.concat(Array.new(missing, 0)) if missing > 0
|
29
|
+
prev...@allocated
|
30
|
+
end
|
31
|
+
|
32
|
+
def setup_source
|
33
|
+
"(#{bucket.setup_source})[#{@index}]||=Array.new(#{size},0)"
|
34
|
+
end
|
35
|
+
|
36
|
+
def tracker_source(tracker_id)
|
37
|
+
"#{bucket.source}[#{@index}][#{tracker_id}]+=1"
|
38
|
+
end
|
39
|
+
|
40
|
+
def tracker_hits
|
41
|
+
@array.dup.freeze
|
42
|
+
end
|
43
|
+
|
44
|
+
def tracker_hits=(new_hits)
|
45
|
+
if new_hits.size != @array.size
|
46
|
+
warn 'Replacing tracker hits with array of different size'
|
47
|
+
end
|
48
|
+
@array.replace(new_hits)
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def dump
|
54
|
+
{bucket: @bucket, index: @index, size: @array.size}
|
55
|
+
end
|
56
|
+
|
57
|
+
def _dump(_level)
|
58
|
+
Marshal.dump(dump)
|
59
|
+
end
|
60
|
+
|
61
|
+
class << self
|
62
|
+
private def load(bucket:, index:, size:)
|
63
|
+
storage = bucket.create_storage(index)
|
64
|
+
storage.allocate_trackers(size - storage.size)
|
65
|
+
storage
|
66
|
+
end
|
67
|
+
|
68
|
+
private def _load(data)
|
69
|
+
load(Marshal.load(data)) # rubocop:disable Security/MarshalLoad
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
private_constant :TrackerStorage
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DeepCover
|
4
|
+
bootstrap
|
5
|
+
|
6
|
+
# Should be seen as a hash like {path => tracker_storage, ...}
|
7
|
+
# Make it easier to separate some concerns, as well as marshalling
|
8
|
+
#
|
9
|
+
class TrackerStoragePerPath
|
10
|
+
extend Forwardable
|
11
|
+
def_delegators :@index, :each, :each_key, :map, :transform_values
|
12
|
+
|
13
|
+
attr_reader :bucket
|
14
|
+
|
15
|
+
def initialize(bucket)
|
16
|
+
@bucket = bucket
|
17
|
+
@index = {}
|
18
|
+
end
|
19
|
+
|
20
|
+
def [](path)
|
21
|
+
@index[path] ||= @bucket.create_storage
|
22
|
+
end
|
23
|
+
|
24
|
+
def tracker_hits_per_path
|
25
|
+
TrackerHitsPerPath.new(@index.transform_values(&:tracker_hits))
|
26
|
+
end
|
27
|
+
|
28
|
+
def tracker_hits_per_path=(tracker_hits_per_path)
|
29
|
+
tracker_hits_per_path.each do |path, tracker_hits|
|
30
|
+
self[path].tracker_hits = tracker_hits
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/deep_cover.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DeepCover
|
4
|
+
require_relative 'deep_cover/load'
|
5
|
+
|
6
|
+
load_absolute_basics
|
7
|
+
|
8
|
+
extend Base
|
9
|
+
extend ConfigSetter
|
10
|
+
end
|
11
|
+
DeepCover::GLOBAL_BINDING = binding
|
12
|
+
|
13
|
+
require './.deep_cover.rb' if File.exist?('./.deep_cover.rb')
|
14
|
+
|
15
|
+
if ENV['DEEP_COVER_OPTIONS']
|
16
|
+
DeepCover.config.set(YAML.load(ENV['DEEP_COVER_OPTIONS']))
|
17
|
+
end
|
18
|
+
if %w[1 t true].include?(ENV['DEEP_COVER'])
|
19
|
+
DeepCover.start
|
20
|
+
require_relative 'deep_cover/auto_run'
|
21
|
+
DeepCover::AutoRun.run!('.').report!(**DeepCover.config)
|
22
|
+
end
|