deep-cover 0.1.1 → 0.1.2

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