deep-cover 0.1.1 → 0.1.2

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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +1 -0
  3. data/.travis.yml +3 -1
  4. data/Rakefile +1 -1
  5. data/bin/selfcov +1 -1
  6. data/deep_cover.gemspec +2 -1
  7. data/lib/deep_cover/analyser.rb +1 -2
  8. data/lib/deep_cover/analyser/base.rb +20 -6
  9. data/lib/deep_cover/analyser/covered_code_source.rb +0 -12
  10. data/lib/deep_cover/analyser/node.rb +11 -1
  11. data/lib/deep_cover/analyser/optionally_covered.rb +14 -0
  12. data/lib/deep_cover/auto_run.rb +36 -32
  13. data/lib/deep_cover/backports.rb +9 -0
  14. data/lib/deep_cover/base.rb +8 -1
  15. data/lib/deep_cover/cli/debugger.rb +6 -4
  16. data/lib/deep_cover/cli/deep_cover.rb +37 -6
  17. data/lib/deep_cover/cli/instrumented_clone_reporter.rb +50 -32
  18. data/lib/deep_cover/config.rb +16 -7
  19. data/lib/deep_cover/coverage.rb +8 -9
  20. data/lib/deep_cover/covered_code.rb +17 -18
  21. data/lib/deep_cover/node/base.rb +28 -4
  22. data/lib/deep_cover/node/branch.rb +10 -7
  23. data/lib/deep_cover/node/def.rb +2 -2
  24. data/lib/deep_cover/node/empty_body.rb +1 -1
  25. data/lib/deep_cover/node/mixin/can_augment_children.rb +1 -2
  26. data/lib/deep_cover/node/mixin/execution_location.rb +9 -6
  27. data/lib/deep_cover/node/mixin/has_child.rb +16 -17
  28. data/lib/deep_cover/node/mixin/has_tracker.rb +2 -6
  29. data/lib/deep_cover/node/mixin/rewriting.rb +9 -8
  30. data/lib/deep_cover/node/send.rb +57 -48
  31. data/lib/deep_cover/node/{boolean.rb → short_circuit.rb} +0 -0
  32. data/lib/deep_cover/reporter/istanbul.rb +2 -3
  33. data/lib/deep_cover/tools.rb +2 -1
  34. data/lib/deep_cover/tools/dasherize.rb +8 -0
  35. data/lib/deep_cover/tools/dump_covered_code.rb +12 -6
  36. data/lib/deep_cover/tools/format_char_cover.rb +2 -3
  37. data/lib/deep_cover/tools/slice.rb +7 -0
  38. data/lib/deep_cover/version.rb +1 -1
  39. metadata +7 -18
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e0bfde5bb75505e45c28d6e28a2cf89464a0c5ad
4
- data.tar.gz: effb8e5d1322f994532f65406ad867e5f6b0c30c
3
+ metadata.gz: cbe670bee3fca431efa3a04244d724b26deb7ede
4
+ data.tar.gz: a8f57f090a6fb004bb0b328901885b84f00548ca
5
5
  SHA512:
6
- metadata.gz: 267136d48b010079e4ddd88c35044757aa6eeafb8619e087b52669754e105ef66b7ecc34c6e5f4b3b53f0d94dc66190ea7a6f0f7c20add4a437511560621fd44
7
- data.tar.gz: 70bb129eecdc023b306308076b722ccb07d6200fa38e0d305a00eb844d7ebb7bcbc2e7fb7f4c165793350a1880a9d285a077d5a3a3f0220f94b203c88ccbe1f7
6
+ metadata.gz: d7d097733ac9da1e9e482f9c56ad96533f18da22f90c60ed3c009fcc7f2f8c4db1a099d5fb0051682157cd9a0d322c546e060f6b17438609ab839901b19a9aa2
7
+ data.tar.gz: 6e0f37c3293bed959adba222707bc77b31c233cf02e62f65ecef70bdfb24b8acc600b9b1ceccaf818b7f094dcb450e35d2ef9ff4bc6dc96b58582a14e0389dfb
data/.rspec CHANGED
@@ -1,2 +1,3 @@
1
1
  --color
2
+ --exclude-pattern "spec/cli_fixtures/**/*.rb"
2
3
  --tag ~skip
data/.travis.yml CHANGED
@@ -7,4 +7,6 @@ rvm:
7
7
  - 2.1.10
8
8
  - 2.0.0
9
9
  - jruby-9.1.9.0
10
- before_install: gem install bundler -v 1.15.4
10
+ before_install:
11
+ - gem install bundler -v 1.15.4
12
+ - npm install -g nyc
data/Rakefile CHANGED
@@ -1,6 +1,6 @@
1
1
  require "bundler/gem_tasks"
2
2
  require "rspec/core/rake_task"
3
3
 
4
- RSpec::Core::RakeTask.new(:spec)
4
+ RSpec::Core::RakeTask.new(:spec).tap{|task| task.pattern = "spec/*_spec.rb, spec/*/*_spec.rb"}
5
5
 
6
6
  task :default => :spec
data/bin/selfcov CHANGED
@@ -9,7 +9,7 @@ end
9
9
  require "deep_cover"
10
10
 
11
11
  if covered_path.nil?
12
- covered_path = DeepCover::Tools.dump_covered_code('./lib', '../covered_deep_cover')
12
+ covered_path = DeepCover::Tools.dump_covered_code_and_save('./lib', dest_path: '../covered_deep_cover')
13
13
  puts "Covered code generation done. Output in", covered_path
14
14
  exec 'bin/selfcov', covered_path
15
15
  else
data/deep_cover.gemspec CHANGED
@@ -22,6 +22,8 @@ Gem::Specification.new do |spec|
22
22
  spec.require_paths = ["lib"]
23
23
 
24
24
  ### Runtime dependencies
25
+ spec.required_ruby_version = ">= 2.0.0"
26
+
25
27
  spec.add_runtime_dependency 'parser'
26
28
  spec.add_runtime_dependency 'backports', '>= 3.10.1'
27
29
  spec.add_runtime_dependency 'binding_of_caller'
@@ -29,7 +31,6 @@ Gem::Specification.new do |spec|
29
31
  # CLI
30
32
  spec.add_runtime_dependency "term-ansicolor"
31
33
  spec.add_runtime_dependency "highline"
32
- spec.add_runtime_dependency "ruby-progressbar", '<1.9.0'
33
34
  spec.add_runtime_dependency 'with_progress'
34
35
  spec.add_runtime_dependency 'slop', '~> 4.0'
35
36
 
@@ -19,6 +19,5 @@ module DeepCover
19
19
  require_relative_dir 'analyser'
20
20
 
21
21
  Analyser.include Analyser::IgnoreUncovered, Analyser::Base
22
- Node.include Analyser::CoveredCodeSource::NodeExtension
23
- CoveredCode.include Analyser::CoveredCodeSource::CoveredCodeExtension
22
+ Analyser.extend Analyser::OptionallyCovered
24
23
  end
@@ -3,15 +3,10 @@ module DeepCover
3
3
  attr_reader :source, :options
4
4
 
5
5
  def initialize(source, **options)
6
- @source = source.to_analyser
6
+ @source = to_source(source, **options)
7
7
  @options = options
8
8
  end
9
9
 
10
- # Basic coercion mechanism
11
- def to_analyser
12
- self
13
- end
14
-
15
10
  # Looking exclusively at our subset of nodes, returns the node's direct descendants
16
11
  def node_children(node)
17
12
  @source.node_children(node)
@@ -47,5 +42,24 @@ module DeepCover
47
42
  def covered_code
48
43
  @source.covered_code
49
44
  end
45
+
46
+ protected
47
+
48
+ def convert(covered_code, **options)
49
+ Analyser::Node.new(covered_code, **options)
50
+ end
51
+
52
+ def to_source(source, **options)
53
+ case source
54
+ when Analyser
55
+ source
56
+ when CoveredCode
57
+ convert(source, **options)
58
+ when Node
59
+ convert(source.covered_code, **options)
60
+ else
61
+ raise ArgumentError, "expected Analyser, Node or CoveredCode, got #{source.class}"
62
+ end
63
+ end
50
64
  end
51
65
  end
@@ -15,17 +15,5 @@ module DeepCover
15
15
  def node_runs(node)
16
16
  node.execution_count if node.executable?
17
17
  end
18
-
19
- module NodeExtension
20
- def to_analyser
21
- Analyser::CoveredCodeSource.new(covered_code)
22
- end
23
- end
24
-
25
- module CoveredCodeExtension
26
- def to_analyser
27
- Analyser::CoveredCodeSource.new(self)
28
- end
29
- end
30
18
  end
31
19
  end
@@ -1,11 +1,21 @@
1
1
  module DeepCover
2
2
  class Analyser::Node < Analyser
3
3
  def is_raise?(node)
4
- node.is_a?(Node::Send) && (node.method_name == :raise || node.method_name == :exit)
4
+ node.is_a?(Node::Send) && (node.message == :raise || node.message == :exit)
5
5
  end
6
6
 
7
7
  def is_default_argument?(node)
8
8
  node.parent.is_a?(Node::Optarg)
9
9
  end
10
+
11
+ def is_case_implicit_else?(node)
12
+ parent = node.parent
13
+ node.is_a?(Node::EmptyBody) && parent.is_a?(Node::Case) && !parent.has_else?
14
+ end
15
+
16
+ protected
17
+ def convert(node, **)
18
+ Analyser::CoveredCodeSource.new(node)
19
+ end
10
20
  end
11
21
  end
@@ -0,0 +1,14 @@
1
+ module DeepCover
2
+ module Analyser::OptionallyCovered
3
+ def optionally_covered
4
+ @optionally_covered ||= Analyser
5
+ .constants.map{|c| Analyser.const_get(c)}
6
+ .select{|klass| klass < Analyser }
7
+ .flat_map do |klass|
8
+ klass.instance_methods(false).map {|m| m.match(/^is_(.*)\?$/); $1 }
9
+ end
10
+ .compact
11
+ .map(&:to_sym)
12
+ end
13
+ end
14
+ end
@@ -3,47 +3,51 @@ require 'pry'
3
3
 
4
4
  module DeepCover
5
5
  module AutoRun
6
- extend self
6
+ class Runner
7
+ def initialize(covered_path)
8
+ @covered_path = covered_path
9
+ end
7
10
 
8
- def detect
9
- @covered_path = File.expand_path('./lib')
10
- Coverage.saved? @covered_path
11
- end
11
+ def run!
12
+ detect
13
+ load
14
+ after_tests { save }
15
+ end
12
16
 
13
- def load
14
- @coverage = Coverage.load(@covered_path)
15
- end
17
+ private
18
+ def detect
19
+ Coverage.saved? @covered_path
20
+ end
16
21
 
17
- def save
18
- @coverage.save_trackers(@covered_path)
19
- end
22
+ def load
23
+ @coverage = Coverage.load(@covered_path, with_trackers: false)
24
+ end
20
25
 
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 }
26
+ def save
27
+ @coverage.save_trackers(@covered_path)
27
28
  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 }
29
+
30
+ def after_tests
31
+ use_at_exit = true
32
+ if defined?(Minitest)
33
+ use_at_exit = false
34
+ Minitest.after_run { yield }
35
+ end
36
+ if defined?(Rspec)
37
+ use_at_exit = false
38
+ RSpec.configure do |config|
39
+ config.after(:suite) { yield }
40
+ end
41
+ end
42
+ if use_at_exit
43
+ at_exit { yield }
33
44
  end
34
- end
35
- if use_at_exit
36
- puts "Using at_exit"
37
- at_exit { yield }
38
45
  end
39
46
  end
40
47
 
41
- def run!
42
- detect
43
- load
44
- after_tests { save }
48
+ def self.run!(covered_path)
49
+ Runner.new(covered_path).run! unless @already_setup
50
+ @already_setup = true
45
51
  end
46
-
47
- run!
48
52
  end
49
53
  end
@@ -2,6 +2,15 @@
2
2
  class Module
3
3
  public :prepend # Public in Ruby 2.1+.
4
4
  end
5
+ require 'pathname'
6
+ class Pathname
7
+ def write(*args)
8
+ File.write(to_path, *args)
9
+ end unless method_defined? :write
10
+ def binwrite(*args)
11
+ File.binwrite(to_path, *args)
12
+ end unless method_defined? :binwrite
13
+ end
5
14
  require 'backports/2.1.0/module/include'
6
15
  require 'backports/2.1.0/enumerable/to_h'
7
16
  require 'backports/2.4.0/false_class/dup'
@@ -17,7 +17,7 @@ module DeepCover
17
17
  end
18
18
 
19
19
  def line_coverage(filename)
20
- coverage.line_coverage(handle_relative_filename(filename), **@config)
20
+ coverage.line_coverage(handle_relative_filename(filename), **config)
21
21
  end
22
22
 
23
23
  def covered_code(filename)
@@ -51,5 +51,12 @@ module DeepCover
51
51
  filename += '.rb' unless filename =~ /\.rb$/
52
52
  filename
53
53
  end
54
+
55
+ def parser
56
+ Parser::CurrentRuby.new.tap do |parser|
57
+ parser.diagnostics.all_errors_are_fatal = true
58
+ parser.diagnostics.ignore_warnings = true
59
+ end
60
+ end
54
61
  end
55
62
  end
@@ -21,11 +21,13 @@ module DeepCover
21
21
  end
22
22
  end
23
23
 
24
- def initialize(source, filename: '(source)', lineno: 1, pry: false)
24
+ attr_reader :options
25
+ def initialize(source, filename: '(source)', lineno: 1, pry: false, **options)
25
26
  @source = source
26
27
  @filename = filename
27
28
  @lineno = lineno
28
29
  @pry = pry
30
+ @options = options
29
31
  end
30
32
 
31
33
  def show
@@ -41,8 +43,8 @@ module DeepCover
41
43
  puts "Line Coverage: Builtin | DeepCover | DeepCover Strict:\n"
42
44
  begin
43
45
  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
+ our_line_coverage = our_coverage(@source, @filename, @lineno, **options)
47
+ our_strict_line_coverage = our_coverage(@source, @filename, @lineno, allow_partial: false, **options)
46
48
  lines = format(builtin_line_coverage, our_line_coverage, our_strict_line_coverage, source: @source)
47
49
  puts number_lines(lines, lineno: @lineno)
48
50
  rescue Exception => e
@@ -72,7 +74,7 @@ module DeepCover
72
74
  def show_char_coverage
73
75
  puts "\nChar coverage:\n"
74
76
 
75
- puts format_char_cover(covered_code, show_whitespace: !!ENV['W'])
77
+ puts format_char_cover(covered_code, show_whitespace: !!ENV['W'], **options)
76
78
  end
77
79
 
78
80
  def pry
@@ -13,16 +13,38 @@ module DeepCover
13
13
  end
14
14
 
15
15
  def show_help
16
- puts options
16
+ puts menu
17
17
  end
18
18
 
19
- def options
20
- @options ||= Slop.parse do |o|
19
+ class Parser < Struct.new(:delegate)
20
+ def method_missing(method, *args, &block)
21
+ options = args.last
22
+ if options.is_a?(Hash) && options.has_key?(:default)
23
+ args[-2] += " [#{options[:default]}]"
24
+ end
25
+ delegate.public_send(method, *args, &block)
26
+ end
27
+ end
28
+
29
+ def parse
30
+ Slop.parse do |o|
31
+ yield Parser.new(o)
32
+ end
33
+ end
34
+
35
+ def menu
36
+ @menu ||= parse do |o|
21
37
  o.banner = "usage: deep-cover [options] [path/to/app/or/gem]"
22
38
  o.separator ''
23
39
  o.string '-o', '--output', 'output folder', default: './coverage'
24
40
  o.string '-c', '--command', 'command to run tests', default: 'rake'
25
-
41
+ o.bool '--bundle', 'run bundle before the tests', default: true
42
+ o.separator 'Coverage options'
43
+ @ignore_uncovered_map = Analyser.optionally_covered.map do |option|
44
+ default = Config::DEFAULTS[:ignore_uncovered].include?(option)
45
+ o.bool "--ignore-#{Tools.dasherize(option)}", "", default: default
46
+ [:"ignore_#{option}", option]
47
+ end.to_h
26
48
  o.separator ''
27
49
  o.separator 'For testing purposes:'
28
50
  o.string '-e', '--expression', 'test ruby expression instead of a covering a path'
@@ -35,10 +57,19 @@ module DeepCover
35
57
  end
36
58
  end
37
59
 
60
+ def convert_options(options)
61
+ iu = options[:ignore_uncovered] = []
62
+ @ignore_uncovered_map.each do |cli_option, option|
63
+ iu << option if options.delete(cli_option)
64
+ end
65
+ options
66
+ end
67
+
38
68
  def go
69
+ options = convert_options(menu.to_h)
39
70
  if options[:expression]
40
- Debugger.new(options[:expression], pry: options[:debug]).show
41
- elsif (path = options.arguments.first)
71
+ Debugger.new(options[:expression], pry: options[:debug], **options).show
72
+ elsif (path = menu.arguments.first)
42
73
  InstrumentedCloneReporter.new(path, **options).run
43
74
  else
44
75
  show_help
@@ -5,24 +5,21 @@ module DeepCover
5
5
  module CLI
6
6
  class InstrumentedCloneReporter
7
7
  include Tools
8
- attr_reader :dest_path
9
8
 
10
9
  def initialize(gem_path, command: 'rake', **options)
11
10
  @command = command
12
11
  @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
12
+ @root_path = gem_path = Pathname.new(gem_path).expand_path
13
+ unless @root_path.join('Gemfile').exist?
17
14
  # 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'))
15
+ @root_path = @root_path.dirname
16
+ raise "Can't find Gemfile" unless @root_path.join('Gemfile').exist?
21
17
  end
22
- @dest_root = File.expand_path('~/test_deep_cover')
23
- @dest_root = Dir.mktmpdir("deep_cover_test") unless Dir.exist?(@dest_root)
18
+ @dest_root = Pathname('~/test_deep_cover').expand_path
19
+ @dest_root = Pathname.new(Dir.mktmpdir("deep_cover_test")) unless @dest_root.exist?
24
20
  `rm -rf #{@dest_root}/* #{@dest_root}/.*`
25
- @dest_path = File.expand_path(File.join(@dest_root, @gem_relative_path))
21
+ gem_relative_path = gem_path.relative_path_from(@root_path)
22
+ @main_path = @dest_root.join(gem_relative_path)
26
23
  end
27
24
 
28
25
  def copy
@@ -32,24 +29,34 @@ module DeepCover
32
29
  def patch_ruby_file(ruby_file)
33
30
  content = File.read(ruby_file)
34
31
  # Insert our code after leading comments:
35
- content.sub!(/^((#.*\n+)*)/, '\1require "deep_cover/auto_run";')
32
+ content.sub!(/^((#.*\n+)*)/, "#{$1}require 'deep_cover/auto_run';DeepCover::AutoRun.run! '#{@dest_root}';")
36
33
  File.write(ruby_file, content)
37
34
  end
38
35
 
36
+ def each_gem_path
37
+ return to_enum __method__ unless block_given?
38
+ if @main_path.join('lib').exist?
39
+ yield @main_path
40
+ else # Rails style
41
+ Pathname.glob(@main_path.join('*/lib')).each{|p| yield p.dirname}
42
+ end
43
+ end
44
+
39
45
  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)
46
+ each_gem_path do |dest_path|
47
+ main = dest_path.join('lib/*.rb')
48
+ Dir.glob(main).each do |main|
49
+ puts "Patching #{main}"
50
+ patch_ruby_file(main)
51
+ end
44
52
  end
45
53
  end
46
54
 
47
55
  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)
56
+ gemfile = @dest_root.join('Gemfile')
50
57
  content = File.read(gemfile)
51
58
  unless content =~ /gem 'deep-cover'/
52
- puts "Patching Gemfile"
59
+ puts "Patching Gemfile #{gemfile}"
53
60
  File.write(gemfile, [
54
61
  "# This file was modified by DeepCover",
55
62
  content,
@@ -57,18 +64,15 @@ module DeepCover
57
64
  '',
58
65
  ].join("\n"))
59
66
  end
60
- Bundler.with_clean_env do
61
- `cd #{dest_path} && bundle`
62
- end
63
67
  end
64
68
 
65
69
  def patch_rubocop
66
- path = File.expand_path(File.join(dest_path, '.rubocop.yml'))
67
- return unless File.exists?(path)
70
+ path = @dest_root.join('.rubocop.yml')
71
+ return unless path.exist?
68
72
  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))
73
+ config = YAML.load(path.read.gsub(/(?<!\w)lib(?!\w)/, 'lib_original'))
74
+ ((config['AllCops'] ||= {})['Exclude'] ||= []) << 'lib/**/*' << 'app/**/*'
75
+ path.write("# This file was modified by DeepCover\n" + YAML.dump(config))
72
76
  end
73
77
 
74
78
  def patch
@@ -78,25 +82,39 @@ module DeepCover
78
82
  end
79
83
 
80
84
  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'))
85
+ coverage = Coverage.new
86
+ each_gem_path do |dest_path|
87
+ `cp -R #{dest_path}/lib #{dest_path}/lib_original`
88
+ Tools.dump_covered_code(File.join(dest_path, 'lib_original'),
89
+ coverage: coverage, root_path: @dest_root.to_s,
90
+ dest_path: File.join(dest_path, 'lib'))
91
+ end
92
+ coverage.save(@dest_root.to_s)
83
93
  end
84
94
 
85
95
  def process
86
96
  Bundler.with_clean_env do
87
- system("cd #{dest_path} && #{@command}", out: $stdout, err: :out)
97
+ system("cd #{@main_path} && #{@command}")
88
98
  end
89
99
  end
90
100
 
91
101
  def report
92
- coverage = Coverage.load @covered_path
93
- puts coverage.report(dir: @covered_path, **@options)
102
+ coverage = Coverage.load @dest_root.to_s
103
+ puts coverage.report(dir: @dest_root.to_s, **@options)
104
+ end
105
+
106
+ def bundle
107
+ puts "Running `bundle install`"
108
+ Bundler.with_clean_env do
109
+ `cd #{@dest_root} && bundle`
110
+ end
94
111
  end
95
112
 
96
113
  def run
97
114
  copy
98
115
  cover
99
116
  patch
117
+ bundle if @options.fetch(:bundle, true)
100
118
  process
101
119
  report
102
120
  end