deep-cover 0.6.4 → 0.7.0

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.
@@ -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