deep-cover 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|