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.
@@ -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
@@ -1,3 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # This empty file is just a hack so we can self-cover our exe/deep-cover