deep-cover 0.1.1
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/.gitignore +13 -0
- data/.rspec +2 -0
- data/.travis.yml +10 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +21 -0
- data/README.md +127 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/cov +43 -0
- data/bin/gemcov +8 -0
- data/bin/selfcov +21 -0
- data/bin/setup +8 -0
- data/bin/testall +88 -0
- data/deep_cover.gemspec +44 -0
- data/exe/deep-cover +6 -0
- data/future_read_me.md +108 -0
- data/lib/deep-cover.rb +1 -0
- data/lib/deep_cover.rb +11 -0
- data/lib/deep_cover/analyser.rb +24 -0
- data/lib/deep_cover/analyser/base.rb +51 -0
- data/lib/deep_cover/analyser/branch.rb +20 -0
- data/lib/deep_cover/analyser/covered_code_source.rb +31 -0
- data/lib/deep_cover/analyser/function.rb +12 -0
- data/lib/deep_cover/analyser/ignore_uncovered.rb +19 -0
- data/lib/deep_cover/analyser/node.rb +11 -0
- data/lib/deep_cover/analyser/per_char.rb +20 -0
- data/lib/deep_cover/analyser/per_line.rb +23 -0
- data/lib/deep_cover/analyser/statement.rb +31 -0
- data/lib/deep_cover/analyser/subset.rb +24 -0
- data/lib/deep_cover/auto_run.rb +49 -0
- data/lib/deep_cover/autoload_tracker.rb +75 -0
- data/lib/deep_cover/backports.rb +9 -0
- data/lib/deep_cover/base.rb +55 -0
- data/lib/deep_cover/builtin_takeover.rb +2 -0
- data/lib/deep_cover/cli/debugger.rb +93 -0
- data/lib/deep_cover/cli/deep_cover.rb +49 -0
- data/lib/deep_cover/cli/instrumented_clone_reporter.rb +105 -0
- data/lib/deep_cover/config.rb +52 -0
- data/lib/deep_cover/core_ext/autoload_overrides.rb +40 -0
- data/lib/deep_cover/core_ext/coverage_replacement.rb +26 -0
- data/lib/deep_cover/core_ext/load_overrides.rb +24 -0
- data/lib/deep_cover/core_ext/require_overrides.rb +36 -0
- data/lib/deep_cover/coverage.rb +198 -0
- data/lib/deep_cover/covered_code.rb +138 -0
- data/lib/deep_cover/custom_requirer.rb +93 -0
- data/lib/deep_cover/node.rb +8 -0
- data/lib/deep_cover/node/arguments.rb +50 -0
- data/lib/deep_cover/node/assignments.rb +250 -0
- data/lib/deep_cover/node/base.rb +99 -0
- data/lib/deep_cover/node/begin.rb +25 -0
- data/lib/deep_cover/node/block.rb +53 -0
- data/lib/deep_cover/node/boolean.rb +22 -0
- data/lib/deep_cover/node/branch.rb +28 -0
- data/lib/deep_cover/node/case.rb +94 -0
- data/lib/deep_cover/node/collections.rb +21 -0
- data/lib/deep_cover/node/const.rb +10 -0
- data/lib/deep_cover/node/def.rb +38 -0
- data/lib/deep_cover/node/empty_body.rb +21 -0
- data/lib/deep_cover/node/exceptions.rb +74 -0
- data/lib/deep_cover/node/if.rb +36 -0
- data/lib/deep_cover/node/keywords.rb +84 -0
- data/lib/deep_cover/node/literals.rb +77 -0
- data/lib/deep_cover/node/loops.rb +72 -0
- data/lib/deep_cover/node/mixin/can_augment_children.rb +65 -0
- data/lib/deep_cover/node/mixin/check_completion.rb +16 -0
- data/lib/deep_cover/node/mixin/child_can_be_empty.rb +25 -0
- data/lib/deep_cover/node/mixin/executed_after_children.rb +13 -0
- data/lib/deep_cover/node/mixin/execution_location.rb +56 -0
- data/lib/deep_cover/node/mixin/flow_accounting.rb +63 -0
- data/lib/deep_cover/node/mixin/has_child.rb +138 -0
- data/lib/deep_cover/node/mixin/has_child_handler.rb +73 -0
- data/lib/deep_cover/node/mixin/has_tracker.rb +44 -0
- data/lib/deep_cover/node/mixin/is_statement.rb +18 -0
- data/lib/deep_cover/node/mixin/rewriting.rb +32 -0
- data/lib/deep_cover/node/mixin/wrapper.rb +13 -0
- data/lib/deep_cover/node/module.rb +64 -0
- data/lib/deep_cover/node/root.rb +18 -0
- data/lib/deep_cover/node/send.rb +83 -0
- data/lib/deep_cover/node/splat.rb +13 -0
- data/lib/deep_cover/node/variables.rb +14 -0
- data/lib/deep_cover/parser_ext/range.rb +40 -0
- data/lib/deep_cover/reporter.rb +6 -0
- data/lib/deep_cover/reporter/istanbul.rb +151 -0
- data/lib/deep_cover/tools.rb +18 -0
- data/lib/deep_cover/tools/builtin_coverage.rb +50 -0
- data/lib/deep_cover/tools/camelize.rb +8 -0
- data/lib/deep_cover/tools/dump_covered_code.rb +32 -0
- data/lib/deep_cover/tools/execute_sample.rb +23 -0
- data/lib/deep_cover/tools/format.rb +16 -0
- data/lib/deep_cover/tools/format_char_cover.rb +18 -0
- data/lib/deep_cover/tools/format_generated_code.rb +25 -0
- data/lib/deep_cover/tools/number_lines.rb +18 -0
- data/lib/deep_cover/tools/our_coverage.rb +9 -0
- data/lib/deep_cover/tools/require_relative_dir.rb +10 -0
- data/lib/deep_cover/tools/silence_warnings.rb +15 -0
- data/lib/deep_cover/version.rb +3 -0
- metadata +326 -0
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'deep_cover'
|
2
|
+
require 'pry'
|
3
|
+
|
4
|
+
module DeepCover
|
5
|
+
module AutoRun
|
6
|
+
extend self
|
7
|
+
|
8
|
+
def detect
|
9
|
+
@covered_path = File.expand_path('./lib')
|
10
|
+
Coverage.saved? @covered_path
|
11
|
+
end
|
12
|
+
|
13
|
+
def load
|
14
|
+
@coverage = Coverage.load(@covered_path)
|
15
|
+
end
|
16
|
+
|
17
|
+
def save
|
18
|
+
@coverage.save_trackers(@covered_path)
|
19
|
+
end
|
20
|
+
|
21
|
+
def after_tests
|
22
|
+
use_at_exit = true
|
23
|
+
if defined?(Minitest)
|
24
|
+
puts "Registering with Minitest"
|
25
|
+
use_at_exit = false
|
26
|
+
Minitest.after_run { yield }
|
27
|
+
end
|
28
|
+
if defined?(Rspec)
|
29
|
+
use_at_exit = false
|
30
|
+
puts "Registering with Rspec"
|
31
|
+
RSpec.configure do |config|
|
32
|
+
config.after(:suite) { yield }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
if use_at_exit
|
36
|
+
puts "Using at_exit"
|
37
|
+
at_exit { yield }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def run!
|
42
|
+
detect
|
43
|
+
load
|
44
|
+
after_tests { save }
|
45
|
+
end
|
46
|
+
|
47
|
+
run!
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'weakref'
|
2
|
+
|
3
|
+
module DeepCover
|
4
|
+
class AutoloadTracker
|
5
|
+
def initialize(autoloaded_paths = {})
|
6
|
+
@autoloaded_paths = autoloaded_paths
|
7
|
+
end
|
8
|
+
|
9
|
+
def add(const, name, path)
|
10
|
+
ext = File.extname(path)
|
11
|
+
# We don't care about .so files
|
12
|
+
return if ext == '.so'
|
13
|
+
path = path + '.rb' if ext != '.rb'
|
14
|
+
|
15
|
+
pairs = @autoloaded_paths[path] ||= []
|
16
|
+
pairs << [WeakRef.new(const), name]
|
17
|
+
end
|
18
|
+
|
19
|
+
def pairs_for_absolute_path(absolute_path)
|
20
|
+
paths = autoloaded_paths_matching_absolute(absolute_path)
|
21
|
+
|
22
|
+
paths.flat_map do |path|
|
23
|
+
pairs = @autoloaded_paths[path] || []
|
24
|
+
pairs = pairs.map{|weak_const, name| [self.class.value_from_weak_ref(weak_const), name] }
|
25
|
+
pairs.select!(&:first)
|
26
|
+
pairs
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def wrap_require(absolute_path)
|
31
|
+
pairs = pairs_for_absolute_path(absolute_path)
|
32
|
+
|
33
|
+
begin
|
34
|
+
pairs.each do |const, name|
|
35
|
+
# Changing the autoload to an already loaded file (this one)
|
36
|
+
const.autoload_without_coverage(name, __FILE__)
|
37
|
+
end
|
38
|
+
|
39
|
+
yield
|
40
|
+
rescue Exception
|
41
|
+
pairs.each do |const, name|
|
42
|
+
# Changing the autoload to an already loaded file (this one)
|
43
|
+
const.autoload_without_coverage(name, absolute_path)
|
44
|
+
end
|
45
|
+
|
46
|
+
raise
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def initialize_autoloaded_paths
|
51
|
+
@autoloaded_paths = {}
|
52
|
+
ObjectSpace.each_object(Module) do |mod|
|
53
|
+
mod.constants.each do |name|
|
54
|
+
if path = mod.autoload?(name)
|
55
|
+
add(mod, name, path)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# We need all the paths of autoloaded_path that match a given absolute_path
|
62
|
+
# Since this can happen a lot, a cache is made which only chan
|
63
|
+
def autoloaded_paths_matching_absolute(absolute_path)
|
64
|
+
@autoloaded_paths.keys.select do |path|
|
65
|
+
absolute_path == DeepCover.custom_requirer.resolve_path(path)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# A simple if the ref is dead, return nil.
|
70
|
+
# WTF ruby, why is there no such simple interface ?!
|
71
|
+
def self.value_from_weak_ref(weak_ref)
|
72
|
+
WeakRef.class_variable_get(:@@__map)[weak_ref]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# We use a few features newer than our target of Ruby 2.0+:
|
2
|
+
class Module
|
3
|
+
public :prepend # Public in Ruby 2.1+.
|
4
|
+
end
|
5
|
+
require 'backports/2.1.0/module/include'
|
6
|
+
require 'backports/2.1.0/enumerable/to_h'
|
7
|
+
require 'backports/2.4.0/false_class/dup'
|
8
|
+
require 'backports/2.4.0/true_class/dup'
|
9
|
+
require 'backports/2.4.0/hash/transform_values'
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module DeepCover
|
2
|
+
module Base
|
3
|
+
def start
|
4
|
+
return if @started
|
5
|
+
if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
|
6
|
+
# No issues with autoload in jruby, so no need to override it!
|
7
|
+
else
|
8
|
+
require_relative 'core_ext/autoload_overrides'
|
9
|
+
autoload_tracker.initialize_autoloaded_paths
|
10
|
+
end
|
11
|
+
require_relative 'core_ext/require_overrides'
|
12
|
+
@started = true
|
13
|
+
end
|
14
|
+
|
15
|
+
def stop
|
16
|
+
# TODO
|
17
|
+
end
|
18
|
+
|
19
|
+
def line_coverage(filename)
|
20
|
+
coverage.line_coverage(handle_relative_filename(filename), **@config)
|
21
|
+
end
|
22
|
+
|
23
|
+
def covered_code(filename)
|
24
|
+
coverage.covered_code(handle_relative_filename(filename))
|
25
|
+
end
|
26
|
+
|
27
|
+
def cover
|
28
|
+
start
|
29
|
+
yield
|
30
|
+
ensure
|
31
|
+
stop
|
32
|
+
end
|
33
|
+
|
34
|
+
def coverage
|
35
|
+
@coverage ||= Coverage.new
|
36
|
+
end
|
37
|
+
|
38
|
+
def custom_requirer
|
39
|
+
@custom_requirer ||= CustomRequirer.new
|
40
|
+
end
|
41
|
+
|
42
|
+
def autoload_tracker
|
43
|
+
@autoload_tracker ||= AutoloadTracker.new
|
44
|
+
end
|
45
|
+
|
46
|
+
def handle_relative_filename(filename)
|
47
|
+
unless Pathname.new(filename).absolute?
|
48
|
+
relative_to = File.dirname(caller[1].partition(/\.rb:\d/).first)
|
49
|
+
filename = File.absolute_path(filename, relative_to)
|
50
|
+
end
|
51
|
+
filename += '.rb' unless filename =~ /\.rb$/
|
52
|
+
filename
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'term/ansicolor'
|
2
|
+
|
3
|
+
module DeepCover
|
4
|
+
module CLI
|
5
|
+
class Debugger
|
6
|
+
include Tools
|
7
|
+
|
8
|
+
module ColorAST
|
9
|
+
def fancy_type
|
10
|
+
color = case
|
11
|
+
when !executable?
|
12
|
+
:faint
|
13
|
+
when !was_executed?
|
14
|
+
:red
|
15
|
+
when flow_interrupt_count > 0
|
16
|
+
:yellow
|
17
|
+
else
|
18
|
+
:green
|
19
|
+
end
|
20
|
+
Term::ANSIColor.send(color, super)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(source, filename: '(source)', lineno: 1, pry: false)
|
25
|
+
@source = source
|
26
|
+
@filename = filename
|
27
|
+
@lineno = lineno
|
28
|
+
@pry = pry
|
29
|
+
end
|
30
|
+
|
31
|
+
def show
|
32
|
+
show_line_coverage
|
33
|
+
show_instrumented_code
|
34
|
+
show_ast
|
35
|
+
show_char_coverage
|
36
|
+
pry if @pry
|
37
|
+
finish
|
38
|
+
end
|
39
|
+
|
40
|
+
def show_line_coverage
|
41
|
+
puts "Line Coverage: Builtin | DeepCover | DeepCover Strict:\n"
|
42
|
+
begin
|
43
|
+
builtin_line_coverage = builtin_coverage(@source, @filename, @lineno)
|
44
|
+
our_line_coverage = our_coverage(@source, @filename, @lineno)
|
45
|
+
our_strict_line_coverage = our_coverage(@source, @filename, @lineno, allow_partial: false)
|
46
|
+
lines = format(builtin_line_coverage, our_line_coverage, our_strict_line_coverage, source: @source)
|
47
|
+
puts number_lines(lines, lineno: @lineno)
|
48
|
+
rescue Exception => e
|
49
|
+
puts "Can't run coverage: #{e.class.name}: #{e}\n#{e.backtrace.join("\n")}"
|
50
|
+
@failed = true
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def show_instrumented_code
|
55
|
+
puts "\nInstrumented code:\n"
|
56
|
+
puts format_generated_code(covered_code)
|
57
|
+
end
|
58
|
+
|
59
|
+
def show_ast
|
60
|
+
puts "\nParsed code:\n"
|
61
|
+
begin
|
62
|
+
execute_sample(covered_code)
|
63
|
+
rescue Exception => e
|
64
|
+
puts "Can't `execute_sample`:#{e.class.name}: #{e}\n#{e.backtrace.join("\n")}"
|
65
|
+
@failed = true
|
66
|
+
end
|
67
|
+
|
68
|
+
Node.prepend ColorAST
|
69
|
+
puts covered_code.covered_ast
|
70
|
+
end
|
71
|
+
|
72
|
+
def show_char_coverage
|
73
|
+
puts "\nChar coverage:\n"
|
74
|
+
|
75
|
+
puts format_char_cover(covered_code, show_whitespace: !!ENV['W'])
|
76
|
+
end
|
77
|
+
|
78
|
+
def pry
|
79
|
+
a = covered_code.covered_ast
|
80
|
+
b = a.children.first
|
81
|
+
binding.pry
|
82
|
+
end
|
83
|
+
|
84
|
+
def finish
|
85
|
+
exit(!@failed)
|
86
|
+
end
|
87
|
+
|
88
|
+
def covered_code
|
89
|
+
@covered_code ||= CoveredCode.new(source: @source, path: @filename, lineno: @lineno)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module DeepCover
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'slop'
|
4
|
+
require 'deep_cover'
|
5
|
+
require_relative_dir '.'
|
6
|
+
|
7
|
+
module CLI
|
8
|
+
module DeepCover
|
9
|
+
extend self
|
10
|
+
|
11
|
+
def show_version
|
12
|
+
puts "deep-cover v#{DeepCover::VERSION}; parser v#{Parser::Version}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def show_help
|
16
|
+
puts options
|
17
|
+
end
|
18
|
+
|
19
|
+
def options
|
20
|
+
@options ||= Slop.parse do |o|
|
21
|
+
o.banner = "usage: deep-cover [options] [path/to/app/or/gem]"
|
22
|
+
o.separator ''
|
23
|
+
o.string '-o', '--output', 'output folder', default: './coverage'
|
24
|
+
o.string '-c', '--command', 'command to run tests', default: 'rake'
|
25
|
+
|
26
|
+
o.separator ''
|
27
|
+
o.separator 'For testing purposes:'
|
28
|
+
o.string '-e', '--expression', 'test ruby expression instead of a covering a path'
|
29
|
+
o.bool '-d', '--debug', 'enter debugging after cover'
|
30
|
+
|
31
|
+
o.separator ''
|
32
|
+
o.separator 'Other available commands:'
|
33
|
+
o.on('--version', 'print the version') { version; exit }
|
34
|
+
o.on('-h', '--help') { help; exit }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def go
|
39
|
+
if options[:expression]
|
40
|
+
Debugger.new(options[:expression], pry: options[:debug]).show
|
41
|
+
elsif (path = options.arguments.first)
|
42
|
+
InstrumentedCloneReporter.new(path, **options).run
|
43
|
+
else
|
44
|
+
show_help
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'tmpdir'
|
3
|
+
|
4
|
+
module DeepCover
|
5
|
+
module CLI
|
6
|
+
class InstrumentedCloneReporter
|
7
|
+
include Tools
|
8
|
+
attr_reader :dest_path
|
9
|
+
|
10
|
+
def initialize(gem_path, command: 'rake', **options)
|
11
|
+
@command = command
|
12
|
+
@options = options
|
13
|
+
@root_path = File.expand_path(gem_path)
|
14
|
+
if File.exist?(File.join(@root_path, 'Gemfile'))
|
15
|
+
@gem_relative_path = '' # Typical case
|
16
|
+
else
|
17
|
+
# E.g. rails/activesupport
|
18
|
+
@gem_relative_path = File.basename(@root_path)
|
19
|
+
@root_path = File.dirname(@root_path)
|
20
|
+
raise "Can't find Gemfile" unless File.exist?(File.join(@root_path, 'Gemfile'))
|
21
|
+
end
|
22
|
+
@dest_root = File.expand_path('~/test_deep_cover')
|
23
|
+
@dest_root = Dir.mktmpdir("deep_cover_test") unless Dir.exist?(@dest_root)
|
24
|
+
`rm -rf #{@dest_root}/* #{@dest_root}/.*`
|
25
|
+
@dest_path = File.expand_path(File.join(@dest_root, @gem_relative_path))
|
26
|
+
end
|
27
|
+
|
28
|
+
def copy
|
29
|
+
@copy ||= `cp -r #{@root_path}/* #{@dest_root} && cp #{@root_path}/.* #{@dest_root}`
|
30
|
+
end
|
31
|
+
|
32
|
+
def patch_ruby_file(ruby_file)
|
33
|
+
content = File.read(ruby_file)
|
34
|
+
# Insert our code after leading comments:
|
35
|
+
content.sub!(/^((#.*\n+)*)/, '\1require "deep_cover/auto_run";')
|
36
|
+
File.write(ruby_file, content)
|
37
|
+
end
|
38
|
+
|
39
|
+
def patch_main_ruby_files
|
40
|
+
main = File.join(dest_path, 'lib/*.rb')
|
41
|
+
Dir.glob(main).each do |main|
|
42
|
+
puts "Patching #{main}"
|
43
|
+
patch_ruby_file(main)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def patch_gemfile
|
48
|
+
gemfile = File.expand_path(File.join(dest_path, 'Gemfile'))
|
49
|
+
gemfile = File.expand_path(File.join(dest_path, '..', 'Gemfile')) unless File.exist?(gemfile)
|
50
|
+
content = File.read(gemfile)
|
51
|
+
unless content =~ /gem 'deep-cover'/
|
52
|
+
puts "Patching Gemfile"
|
53
|
+
File.write(gemfile, [
|
54
|
+
"# This file was modified by DeepCover",
|
55
|
+
content,
|
56
|
+
"gem 'deep-cover', path: '#{File.expand_path(__dir__ + '/../../../')}'",
|
57
|
+
'',
|
58
|
+
].join("\n"))
|
59
|
+
end
|
60
|
+
Bundler.with_clean_env do
|
61
|
+
`cd #{dest_path} && bundle`
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def patch_rubocop
|
66
|
+
path = File.expand_path(File.join(dest_path, '.rubocop.yml'))
|
67
|
+
return unless File.exists?(path)
|
68
|
+
puts "Patching .rubocop.yml"
|
69
|
+
config = YAML.load(File.read(path).gsub(/(?<!\w)lib(?!\w)/, 'lib_original'))
|
70
|
+
((config['AllCops'] ||= {})['Exclude'] ||= []) << 'lib/**/*'
|
71
|
+
File.write(path, "# This file was modified by DeepCover\n" + YAML.dump(config))
|
72
|
+
end
|
73
|
+
|
74
|
+
def patch
|
75
|
+
patch_gemfile
|
76
|
+
patch_rubocop
|
77
|
+
patch_main_ruby_files
|
78
|
+
end
|
79
|
+
|
80
|
+
def cover
|
81
|
+
`cp -R #{dest_path}/lib #{dest_path}/lib_original`
|
82
|
+
@covered_path = Tools.dump_covered_code(File.join(dest_path, 'lib_original'), File.join(dest_path, 'lib'))
|
83
|
+
end
|
84
|
+
|
85
|
+
def process
|
86
|
+
Bundler.with_clean_env do
|
87
|
+
system("cd #{dest_path} && #{@command}", out: $stdout, err: :out)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def report
|
92
|
+
coverage = Coverage.load @covered_path
|
93
|
+
puts coverage.report(dir: @covered_path, **@options)
|
94
|
+
end
|
95
|
+
|
96
|
+
def run
|
97
|
+
copy
|
98
|
+
cover
|
99
|
+
patch
|
100
|
+
process
|
101
|
+
report
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module DeepCover
|
2
|
+
class Config
|
3
|
+
DEFAULTS = {
|
4
|
+
ignore_uncovered: [],
|
5
|
+
paths: %w[./app ./lib],
|
6
|
+
allow_partial: false,
|
7
|
+
}
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@options = copy(DEFAULTS)
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_hash
|
14
|
+
copy(@options)
|
15
|
+
end
|
16
|
+
alias_method :to_h, :to_hash
|
17
|
+
|
18
|
+
def ignore_uncovered(*keywords)
|
19
|
+
@options[:ignore_uncovered] -= keywords
|
20
|
+
self
|
21
|
+
end
|
22
|
+
|
23
|
+
def detect_uncovered(*keywords)
|
24
|
+
@options[:ignore_uncovered] += keywords
|
25
|
+
self
|
26
|
+
end
|
27
|
+
|
28
|
+
def paths(paths)
|
29
|
+
@options[:paths] = paths
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
def copy(h)
|
35
|
+
h.dup.transform_values(&:dup)
|
36
|
+
end
|
37
|
+
|
38
|
+
module Setter
|
39
|
+
def configure(&block)
|
40
|
+
@config ||= Config.new
|
41
|
+
|
42
|
+
raise "Must provide a block" unless block
|
43
|
+
case block.arity
|
44
|
+
when 0
|
45
|
+
@config.instance_eval(&block)
|
46
|
+
when 1
|
47
|
+
block.call(@config)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|