deep-cover 0.6.4 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.deep_cover.rb +5 -0
- data/.gitignore +7 -4
- data/.rubocop.yml +7 -0
- data/CHANGELOG.md +27 -0
- data/Gemfile +5 -1
- data/README.md +47 -53
- data/Rakefile +20 -15
- data/deep_cover.gemspec +2 -3
- data/exe/deep-cover +2 -4
- data/lib/deep_cover/cli.rb +85 -0
- data/lib/deep_cover/cli/commands/clear.rb +10 -0
- data/lib/deep_cover/cli/commands/clone.rb +22 -0
- data/lib/deep_cover/cli/commands/exec.rb +37 -0
- data/lib/deep_cover/cli/commands/gather.rb +36 -0
- data/lib/deep_cover/cli/commands/help.rb +34 -0
- data/lib/deep_cover/cli/commands/merge.rb +10 -0
- data/lib/deep_cover/cli/commands/report.rb +14 -0
- data/lib/deep_cover/cli/commands/run_expression.rb +13 -0
- data/lib/deep_cover/cli/commands/short_help.rb +26 -0
- data/lib/deep_cover/cli/commands/version.rb +15 -0
- data/lib/deep_cover/cli/tools.rb +18 -0
- data/lib/deep_cover/cover_cloned_tree.rb +58 -0
- data/lib/deep_cover/expression_debugger.rb +122 -0
- data/lib/deep_cover/instrumented_clone_reporter.rb +127 -0
- metadata +22 -33
- data/bin/console +0 -14
- data/bin/cov +0 -43
- data/bin/setup +0 -8
- data/bin/test_gems +0 -54
- data/bin/testall +0 -88
- data/future_read_me.md +0 -108
- data/lib/deep_cover/cli/debugger.rb +0 -127
- data/lib/deep_cover/cli/exec.rb +0 -27
- data/lib/deep_cover/cli/instrumented_clone_reporter.rb +0 -197
- data/lib/deep_cover/cli/runner.rb +0 -126
- data/lib/deep_cover/dump_covered_code.rb +0 -36
- data/lib/deep_cover_entry.rb +0 -3
data/lib/deep_cover/cli/exec.rb
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module DeepCover
|
4
|
-
module CLI
|
5
|
-
class Exec
|
6
|
-
class Option
|
7
|
-
def keep_file_descriptors?
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
def initialize(argv, **options)
|
12
|
-
@argv = argv
|
13
|
-
@options = options
|
14
|
-
end
|
15
|
-
|
16
|
-
def run
|
17
|
-
require 'yaml'
|
18
|
-
require 'deep_cover/backports'
|
19
|
-
env_var = {'DEEP_COVER' => 't',
|
20
|
-
'DEEP_COVER_OPTIONS' => YAML.dump(@options.slice(*DEFAULTS.keys)),
|
21
|
-
}
|
22
|
-
|
23
|
-
system(env_var, *@argv)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
@@ -1,197 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'tmpdir'
|
4
|
-
|
5
|
-
module DeepCover
|
6
|
-
require 'deep-cover'
|
7
|
-
require_relative '../dump_covered_code'
|
8
|
-
bootstrap
|
9
|
-
|
10
|
-
module CLI
|
11
|
-
class InstrumentedCloneReporter
|
12
|
-
include Tools
|
13
|
-
# matches regular files, .files, ..files, but not '.' or '..'
|
14
|
-
GLOB_ALL_CONTENT = '{,.[^.],..?}*'
|
15
|
-
|
16
|
-
def initialize(source_path, **options)
|
17
|
-
@options = CLI_DEFAULTS.merge(options)
|
18
|
-
@root_path = @source_path = Pathname.new(source_path).expand_path
|
19
|
-
unless @root_path.join('Gemfile').exist?
|
20
|
-
# E.g. rails/activesupport
|
21
|
-
@root_path = @root_path.dirname
|
22
|
-
raise "Can't find Gemfile in #{@root_path}" unless @root_path.join('Gemfile').exist?
|
23
|
-
end
|
24
|
-
path = Pathname('~/test_deep_cover').expand_path
|
25
|
-
if path.exist?
|
26
|
-
@dest_root = path.join(@source_path.basename)
|
27
|
-
@dest_root.mkpath
|
28
|
-
else
|
29
|
-
@dest_root = Pathname.new(Dir.mktmpdir('deep_cover_test'))
|
30
|
-
end
|
31
|
-
|
32
|
-
gem_relative_path = @source_path.relative_path_from(@root_path)
|
33
|
-
@main_path = @dest_root.join(gem_relative_path)
|
34
|
-
singleton_class.include self.class.const_get(Tools.camelize(style))
|
35
|
-
end
|
36
|
-
|
37
|
-
def clear
|
38
|
-
FileUtils.rm_rf(Dir.glob("#{@dest_root}/#{GLOB_ALL_CONTENT}"))
|
39
|
-
end
|
40
|
-
|
41
|
-
def copy
|
42
|
-
return true if @copied
|
43
|
-
puts 'Cloning...'
|
44
|
-
FileUtils.cp_r(Dir.glob("#{@root_path}/#{GLOB_ALL_CONTENT}"), @dest_root)
|
45
|
-
@copied = true
|
46
|
-
end
|
47
|
-
|
48
|
-
def patch_ruby_file(ruby_file)
|
49
|
-
content = ruby_file.read
|
50
|
-
# Insert our code after leading comments:
|
51
|
-
content.sub!(/^(#.*\n+)*/) { |header| "#{header}require 'deep_cover/auto_run';DeepCover::AutoRun.run! '#{@dest_root}';" }
|
52
|
-
ruby_file.write(content)
|
53
|
-
end
|
54
|
-
|
55
|
-
def style
|
56
|
-
if @source_path.join('config/environments/test.rb').exist?
|
57
|
-
:rails
|
58
|
-
elsif @source_path.join('lib').exist?
|
59
|
-
:single_gem
|
60
|
-
else # Rails style
|
61
|
-
:gem_collection
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
# Style specific functionality
|
66
|
-
module Gem
|
67
|
-
def each_main_ruby_files(&block)
|
68
|
-
each_gem_path do |dest_path|
|
69
|
-
main = dest_path.join('lib/*.rb')
|
70
|
-
Pathname.glob(main).select(&:file?).each(&block)
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
def each_dir_to_cover
|
75
|
-
each_gem_path do |dest_path|
|
76
|
-
yield dest_path.join('lib')
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
module SingleGem
|
82
|
-
include Gem
|
83
|
-
def each_gem_path
|
84
|
-
yield @main_path
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
module GemCollection
|
89
|
-
include Gem
|
90
|
-
def each_gem_path
|
91
|
-
Pathname.glob(@main_path.join('*/lib')).each { |p| yield p.dirname }
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
module Rails
|
96
|
-
def each_main_ruby_files
|
97
|
-
yield @main_path.join('config/environments/test.rb')
|
98
|
-
end
|
99
|
-
|
100
|
-
def each_dir_to_cover
|
101
|
-
yield @main_path.join('app')
|
102
|
-
yield @main_path.join('lib')
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
# Back to global functionality
|
107
|
-
def patch_main_ruby_files
|
108
|
-
each_main_ruby_files do |main|
|
109
|
-
puts "Patching #{main}"
|
110
|
-
patch_ruby_file(main)
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
def patch_gemfile
|
115
|
-
gemfile = @dest_root.join('Gemfile')
|
116
|
-
require 'bundler'
|
117
|
-
deps = Bundler::Definition.build(gemfile, nil, nil).dependencies
|
118
|
-
|
119
|
-
return if deps.find { |e| e.name.start_with? 'deep-cover' }
|
120
|
-
|
121
|
-
content = File.read(gemfile)
|
122
|
-
puts "Patching Gemfile #{gemfile}"
|
123
|
-
File.write(gemfile, [
|
124
|
-
'# This file was modified by DeepCover',
|
125
|
-
content,
|
126
|
-
"gem 'deep-cover', path: '#{File.expand_path(__dir__ + '/../../../')}'",
|
127
|
-
"gem 'deep-cover-core', path: '#{File.expand_path(__dir__ + '/../../../core_gem')}'",
|
128
|
-
'',
|
129
|
-
].join("\n"))
|
130
|
-
end
|
131
|
-
|
132
|
-
def patch_rubocop
|
133
|
-
path = @dest_root.join('.rubocop.yml')
|
134
|
-
return unless path.exist?
|
135
|
-
puts 'Patching .rubocop.yml'
|
136
|
-
config = YAML.load(path.read.gsub(/(?<!\w)lib(?!\w)/, 'lib_original'))
|
137
|
-
((config['AllCops'] ||= {})['Exclude'] ||= []) << 'lib/**/*' << 'app/**/*'
|
138
|
-
path.write("# This file was modified by DeepCover\n" + YAML.dump(config))
|
139
|
-
end
|
140
|
-
|
141
|
-
def patch
|
142
|
-
patch_gemfile
|
143
|
-
patch_rubocop
|
144
|
-
patch_main_ruby_files
|
145
|
-
end
|
146
|
-
|
147
|
-
def cover
|
148
|
-
coverage = Coverage.new(tracker_global: ::DeepCover.config.tracker_global)
|
149
|
-
each_dir_to_cover do |to_cover|
|
150
|
-
FileUtils.cp_r(to_cover, to_cover.sub_ext('_original'))
|
151
|
-
Tools.dump_covered_code(to_cover,
|
152
|
-
coverage: coverage,
|
153
|
-
dest_path: to_cover)
|
154
|
-
end
|
155
|
-
coverage.save(@dest_root.to_s)
|
156
|
-
end
|
157
|
-
|
158
|
-
def process
|
159
|
-
Bundler.with_clean_env do
|
160
|
-
system({'DISABLE_SPRING' => 'true'}, "cd #{@main_path} && #{@options[:command]}")
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
|
-
def restore
|
165
|
-
each_dir_to_cover do |to_cover|
|
166
|
-
FileUtils.mv(to_cover, to_cover.sub_ext('_instrumented'))
|
167
|
-
FileUtils.mv(to_cover.sub_ext('_original'), to_cover)
|
168
|
-
end
|
169
|
-
end
|
170
|
-
|
171
|
-
def report
|
172
|
-
coverage = Coverage.load @dest_root.to_s
|
173
|
-
puts coverage.report(dir: @dest_root.to_s, **@options)
|
174
|
-
end
|
175
|
-
|
176
|
-
def bundle
|
177
|
-
puts 'Running `bundle install`'
|
178
|
-
Bundler.with_clean_env do
|
179
|
-
`cd #{@dest_root} && bundle`
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
def run
|
184
|
-
if @options[:process]
|
185
|
-
clear
|
186
|
-
copy
|
187
|
-
cover
|
188
|
-
patch
|
189
|
-
bundle if @options[:bundle]
|
190
|
-
process
|
191
|
-
restore
|
192
|
-
end
|
193
|
-
report
|
194
|
-
end
|
195
|
-
end
|
196
|
-
end
|
197
|
-
end
|
@@ -1,126 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module DeepCover
|
4
|
-
require 'slop'
|
5
|
-
require 'deep-cover'
|
6
|
-
bootstrap
|
7
|
-
|
8
|
-
module CLI
|
9
|
-
module SlopExtension
|
10
|
-
attr_accessor :stopped
|
11
|
-
attr_reader :ignored
|
12
|
-
|
13
|
-
def try_process(*)
|
14
|
-
@ignored ||= 0
|
15
|
-
return if stopped
|
16
|
-
o = super
|
17
|
-
@ignored += 1 unless o
|
18
|
-
o
|
19
|
-
end
|
20
|
-
end
|
21
|
-
::Slop::Parser.prepend SlopExtension
|
22
|
-
|
23
|
-
module Runner
|
24
|
-
extend self
|
25
|
-
|
26
|
-
def show_version
|
27
|
-
require 'deep_cover/version'
|
28
|
-
require 'parser'
|
29
|
-
puts "deep-cover v#{DeepCover::VERSION}; parser v#{Parser::VERSION}"
|
30
|
-
end
|
31
|
-
|
32
|
-
def show_help
|
33
|
-
puts menu
|
34
|
-
end
|
35
|
-
|
36
|
-
class OptionParser < Struct.new(:delegate)
|
37
|
-
def method_missing(method, *args, &block) # rubocop:disable Style/MethodMissing
|
38
|
-
options = args.last
|
39
|
-
if options.is_a?(Hash) && options.has_key?(:default)
|
40
|
-
args[-2] += " [#{options[:default]}]"
|
41
|
-
end
|
42
|
-
delegate.public_send(method, *args, &block)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
def parse
|
47
|
-
Slop.parse do |o|
|
48
|
-
yield OptionParser.new(o)
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
def menu
|
53
|
-
@menu ||= parse do |o|
|
54
|
-
o.banner = ['usage: deep-cover [options] exec <command ...>',
|
55
|
-
' or deep-cover [options] [path/to/app/or/gem]',
|
56
|
-
].join("\n")
|
57
|
-
o.separator ''
|
58
|
-
o.string '-o', '--output', 'output folder', default: DeepCover.config.output
|
59
|
-
o.string '--reporter', 'reporter', default: DeepCover.config.reporter
|
60
|
-
o.bool '--open', 'open the output coverage', default: CLI_DEFAULTS[:open]
|
61
|
-
|
62
|
-
o.separator 'Coverage options'
|
63
|
-
@ignore_uncovered_map = OPTIONALLY_COVERED.map do |option|
|
64
|
-
default = DeepCover.config.ignore_uncovered.include?(option)
|
65
|
-
o.bool "--ignore-#{dasherize(option)}", '', default: default
|
66
|
-
[:"ignore_#{option}", option]
|
67
|
-
end.to_h
|
68
|
-
|
69
|
-
o.separator "\nWhen not using ’exec’:"
|
70
|
-
o.string '-c', '--command', 'command to run tests', default: CLI_DEFAULTS[:command]
|
71
|
-
o.bool '--bundle', 'run bundle before the tests', default: CLI_DEFAULTS[:bundle]
|
72
|
-
o.bool '--process', 'turn off to only redo the reporting', default: CLI_DEFAULTS[:process]
|
73
|
-
|
74
|
-
o.separator "\nFor testing purposes:"
|
75
|
-
o.bool '--profile', 'use profiler' unless RUBY_PLATFORM == 'java'
|
76
|
-
o.string '-e', '--expression', 'test ruby expression instead of a covering a path'
|
77
|
-
o.bool '-d', '--debug', 'enter debugging after cover'
|
78
|
-
|
79
|
-
o.separator "\nOther available commands:"
|
80
|
-
o.on('--version', 'print the version') do
|
81
|
-
show_version
|
82
|
-
exit
|
83
|
-
end
|
84
|
-
o.boolean('-h', '--help')
|
85
|
-
|
86
|
-
o.boolean('exec', '', help: false) do
|
87
|
-
o.parser.stopped = true if o.parser.ignored == 0
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
def convert_options(options)
|
93
|
-
iu = options[:ignore_uncovered] = []
|
94
|
-
@ignore_uncovered_map.each do |cli_option, option|
|
95
|
-
iu << option if options.delete(cli_option)
|
96
|
-
end
|
97
|
-
options[:output] = false if ['false', 'f', ''].include?(options[:output])
|
98
|
-
options
|
99
|
-
end
|
100
|
-
|
101
|
-
def go
|
102
|
-
options = convert_options(menu.to_h)
|
103
|
-
if options[:help]
|
104
|
-
show_help
|
105
|
-
elsif options[:expression]
|
106
|
-
require_relative 'debugger'
|
107
|
-
Debugger.new(options[:expression], **options).show
|
108
|
-
elsif menu.parser.stopped
|
109
|
-
require_relative 'exec'
|
110
|
-
Exec.new(menu.arguments, **options).run
|
111
|
-
else
|
112
|
-
require_relative 'instrumented_clone_reporter'
|
113
|
-
path = menu.arguments.first || '.'
|
114
|
-
InstrumentedCloneReporter.new(path, **options).run
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
private
|
119
|
-
|
120
|
-
# Poor man's dasherize. 'an_example' => 'an-example'
|
121
|
-
def dasherize(string)
|
122
|
-
string.to_s.tr('_', '-')
|
123
|
-
end
|
124
|
-
end
|
125
|
-
end
|
126
|
-
end
|
@@ -1,36 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module DeepCover
|
4
|
-
silence_warnings do
|
5
|
-
require 'with_progress'
|
6
|
-
end
|
7
|
-
module Tools::DumpCoveredCode
|
8
|
-
def dump_covered_code(source_path, coverage:, dest_path: Dir.mktmpdir)
|
9
|
-
source_path = File.join(File.expand_path(source_path), '')
|
10
|
-
dest_path = File.join(File.expand_path(dest_path), '')
|
11
|
-
skipped = []
|
12
|
-
file_paths = Dir.glob("#{source_path}**/*.rb").select { |p| File.file?(p) }
|
13
|
-
file_paths.each.with_progress(title: 'Rewriting') do |path|
|
14
|
-
new_path = Pathname(path.gsub(source_path, dest_path))
|
15
|
-
begin
|
16
|
-
covered_code = coverage.covered_code(path)
|
17
|
-
rescue Parser::SyntaxError
|
18
|
-
skipped << path
|
19
|
-
next
|
20
|
-
end
|
21
|
-
new_path.dirname.mkpath
|
22
|
-
new_path.write(covered_code.covered_source)
|
23
|
-
end
|
24
|
-
unless skipped.empty?
|
25
|
-
warn [
|
26
|
-
"#{skipped.size} files could not be instrumented because of syntax errors:",
|
27
|
-
*skipped.first(3),
|
28
|
-
('...' if skipped.size > 3),
|
29
|
-
].compact.join("\n")
|
30
|
-
end
|
31
|
-
dest_path
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
Tools.extend Tools::DumpCoveredCode
|
36
|
-
end
|
data/lib/deep_cover_entry.rb
DELETED