deep-cover 0.4.0 → 0.5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 23dab9f10f66926115fc7a84107eaaa364ea232f
4
- data.tar.gz: a5e5a3e0d3b39829928acd724702f306282d03b4
3
+ metadata.gz: cd810c9ed86848f16da708afa29617606737f5d5
4
+ data.tar.gz: fa5f6561e9b952e24f43fa7b06eed8c2a21bd0d4
5
5
  SHA512:
6
- metadata.gz: 746c9f0d6ee8bd832e2629d37996050f309bcc7724be618f72da0c3851705b714f6c56b458bc4727bdb5b7fe62f64eda6b99f61e6ca21e306b3ed8f339a526c5
7
- data.tar.gz: 3aa28c66195e1cd61bfeafe38df9caa82e4e5ffd55bf6fdb3b3c4fcee8aa8ac0efb6a369b74c6656954e761e7e5f659aea36d9d25d582049e0d04b92c20a0f4d
6
+ metadata.gz: 4f26c1f20f3268174c55f2b3bfe17dbf42cecf2e70f1456b395006a8d03545e95388cbe0f3fc18b6b38c5c7413b88327f74c2c921fd55ced04771379a01fb6b0
7
+ data.tar.gz: 5b220912488dde079c748c4376c9a94adbfeac5a144f6d27c362eb9ff29fabc28247990354a646aa9f2b6f449d2c4de88f94f8d6dbe86f83181da06bd2389732
data/.gitignore CHANGED
@@ -14,3 +14,4 @@
14
14
  .rspec_status
15
15
  /Gemfile.local
16
16
  /spec/cli_fixtures/covered_trivial_gem/deep_cover/
17
+ /deep_cover
@@ -1,9 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.5
4
+
5
+ * Added custom filters
6
+
3
7
  ## 0.4
4
8
 
5
9
  * Added `deep-cover exec`
6
- * Support for # nocov
10
+ * Automatic loading of `.deep-cover.rb`
11
+ * Support for `# nocov`
7
12
 
8
13
  ## 0.3
9
14
 
data/Gemfile CHANGED
@@ -9,5 +9,4 @@ gemspec
9
9
 
10
10
  eval_gemfile 'Gemfile.local' if File.exist?('Gemfile.local')
11
11
 
12
- gem 'parser', git: 'https://github.com/marcandre/parser.git', branch: 'tree_rewriter_release'
13
12
  gem 'ruby-prof', platforms: :mri
data/README.md CHANGED
@@ -145,6 +145,24 @@ The file `.deep-cover.rb` is loaded automatically when requiring `deep-cover` an
145
145
 
146
146
  *Note*: The configuration block is only executed when `deep-cover` is actually started.
147
147
 
148
+ #### Custom filters
149
+
150
+ `deep-cover` comes with a few filters that make it possible to ignore certain uncovered codes.
151
+
152
+ It is easy to add you own filters.
153
+
154
+ For example, if one wants to ignore uncovered calls to `raise` but the code uses `our_custom_raise` instead, the following with work:
155
+
156
+ ```
157
+ DeepCover.configure do
158
+ ignore_uncovered do
159
+ type == :send &&
160
+ receiver == nil &&
161
+ message == :our_custom_raise
162
+ end
163
+ end
164
+ ```
165
+
148
166
  ## Development
149
167
 
150
168
  After checking out the repo, run `bundle` then `rake dev:install` to install dependencies. Then, run `rake` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -27,6 +27,7 @@ Gem::Specification.new do |spec|
27
27
 
28
28
  # Main dependency
29
29
  spec.add_runtime_dependency 'parser'
30
+ spec.add_runtime_dependency 'parser_tree_rewriter'
30
31
 
31
32
  # Support
32
33
  spec.add_runtime_dependency 'backports', '>= 3.11.0'
@@ -11,7 +11,7 @@ module DeepCover
11
11
  def initialize(source, ignore_uncovered: [], **options)
12
12
  @cache = {}.compare_by_identity
13
13
  super
14
- @allow_filters = Array(ignore_uncovered).map { |kind| :"is_#{kind}?" }
14
+ @allow_filters = Array(ignore_uncovered).map { |kind| Node.filter_to_method_name(kind) }
15
15
  @nocov_ranges = FlagCommentAssociator.new(covered_code)
16
16
  end
17
17
 
@@ -1,6 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # We use a few features newer than our target of Ruby 2.1+:
4
+ class Module
5
+ public :define_method
6
+ end
4
7
  require 'pathname'
5
8
  class Pathname
6
9
  def write(*args)
@@ -18,5 +18,5 @@ module DeepCover
18
18
  open: false,
19
19
  }.freeze
20
20
 
21
- OPTIONALLY_COVERED = %i[case_implicit_else default_argument raise trivial_if].freeze
21
+ OPTIONALLY_COVERED = %i[case_implicit_else default_argument raise trivial_if]
22
22
  end
@@ -7,6 +7,21 @@ module DeepCover
7
7
 
8
8
  module CLI
9
9
  end
10
+
11
+ module CLI::SlopExtension
12
+ attr_accessor :stopped
13
+ attr_reader :ignored
14
+
15
+ def try_process(*)
16
+ @ignored ||= 0
17
+ return if stopped
18
+ o = super
19
+ @ignored += 1 unless o
20
+ o
21
+ end
22
+ end
23
+ Slop::Parser.prepend CLI::SlopExtension
24
+
10
25
  module CLI::DeepCover
11
26
  extend self
12
27
 
@@ -53,7 +68,7 @@ module DeepCover
53
68
  [:"ignore_#{option}", option]
54
69
  end.to_h
55
70
 
56
- o.separator '\nWhen not using ’exec’:'
71
+ o.separator "\nWhen not using ’exec’:"
57
72
  o.string '-c', '--command', 'command to run tests', default: CLI_DEFAULTS[:command]
58
73
  o.bool '--bundle', 'run bundle before the tests', default: CLI_DEFAULTS[:bundle]
59
74
  o.bool '--process', 'turn off to only redo the reporting', default: CLI_DEFAULTS[:process]
@@ -69,6 +84,10 @@ module DeepCover
69
84
  exit
70
85
  end
71
86
  o.boolean('-h', '--help')
87
+
88
+ o.boolean('exec', '', help: false) do
89
+ o.parser.stopped = true if o.parser.ignored == 0
90
+ end
72
91
  end
73
92
  end
74
93
 
@@ -83,18 +102,17 @@ module DeepCover
83
102
 
84
103
  def go
85
104
  options = convert_options(menu.to_h)
86
- first, *rest = menu.arguments
87
105
  if options[:help]
88
106
  show_help
89
107
  elsif options[:expression]
90
108
  require_relative 'debugger'
91
109
  CLI::Debugger.new(options[:expression], **options).show
92
- elsif first == 'exec'
110
+ elsif menu.parser.stopped
93
111
  require_relative 'exec'
94
- CLI::Exec.new(rest, **options).run
112
+ CLI::Exec.new(menu.arguments, **options).run
95
113
  else
96
114
  require_relative 'instrumented_clone_reporter'
97
- path = first || '.'
115
+ path = menu.arguments.first || '.'
98
116
  CLI::InstrumentedCloneReporter.new(path, **options).run
99
117
  end
100
118
  end
@@ -12,7 +12,12 @@ module DeepCover
12
12
  end
13
13
  alias_method :to_h, :to_hash
14
14
 
15
- def ignore_uncovered(*keywords)
15
+ def ignore_uncovered(*keywords, &block)
16
+ if block
17
+ raise ArgumentError, "wrong number of arguments (given #{keywords.size}, expected 0..1)" if keywords.size > 1
18
+ keywords << Node.unique_filter if keywords.empty?
19
+ Node.create_filter(keywords.first, &block)
20
+ end
16
21
  if keywords.empty?
17
22
  @options[:ignore_uncovered]
18
23
  else
@@ -22,6 +27,7 @@ module DeepCover
22
27
  end
23
28
 
24
29
  def detect_uncovered(*keywords)
30
+ raise ArgumentError, 'No block is accepted' if block_given?
25
31
  if keywords.empty?
26
32
  OPTIONALLY_COVERED - @options[:ignore_uncovered]
27
33
  else
@@ -2,7 +2,8 @@
2
2
 
3
3
  module DeepCover
4
4
  module Load
5
- AUTOLOAD = %i[analyser autoload_tracker config coverage covered_code custom_requirer
5
+ AUTOLOAD = %i[analyser autoload_tracker auto_run config
6
+ coverage covered_code custom_requirer
6
7
  flag_comment_associator memoize module_override node
7
8
  problem_with_diagnostic reporter
8
9
  ]
@@ -35,6 +36,7 @@ module DeepCover
35
36
  silence_warnings do
36
37
  require 'parser/current'
37
38
  end
39
+ require 'parser_tree_rewriter'
38
40
  require_relative_dir 'parser_ext'
39
41
  @parser_loaded
40
42
  end
@@ -15,6 +15,7 @@ module DeepCover
15
15
  include ExecutionLocation
16
16
  include ChildCanBeEmpty
17
17
  include Filters
18
+ extend Filters::ClassMethods
18
19
 
19
20
  attr_reader :index, :parent, :children, :base_node
20
21
 
@@ -16,7 +16,7 @@ module DeepCover
16
16
  # See https://github.com/jruby/jruby/issues/4804
17
17
  # This is solved in jruby 9.2.0.0, better keep the workaround
18
18
  # for compatibility.
19
- has_child condition: Node, rewrite: '(((%{entry_tracker}) && %{node}))',
19
+ has_child condition: Node, rewrite: '((%{entry_tracker}) && %{node})',
20
20
  flow_entry_count: :entry_tracker_hits
21
21
  executed_loc_keys []
22
22
 
@@ -78,7 +78,7 @@ module DeepCover
78
78
  has_extra_children whens: When
79
79
  has_child else: Node,
80
80
  can_be_empty: -> { base_node.loc.end.begin },
81
- rewrite: -> { "#{'else;' unless has_else?}((%{else_entry_tracker};%{local}=nil;%{node}))" },
81
+ rewrite: -> { "#{'else;' unless has_else?}(%{else_entry_tracker};%{local}=nil;%{node})" },
82
82
  executed_loc_keys: [:else],
83
83
  is_statement: true,
84
84
  flow_entry_count: :else_entry_tracker_hits
@@ -13,7 +13,7 @@ module DeepCover
13
13
  can_be_empty: -> { (base_node.loc.begin || base_node.loc.expression.succ).end },
14
14
  flow_entry_count: :entered_body_tracker_hits,
15
15
  is_statement: true,
16
- rewrite: '((%{entered_body_tracker};%{local}=nil;%{node}))'
16
+ rewrite: '(%{entered_body_tracker};%{local}=nil;%{node})'
17
17
 
18
18
  def is_statement
19
19
  false
@@ -9,7 +9,7 @@ module DeepCover
9
9
  has_child body: Node,
10
10
  can_be_empty: -> { base_node.loc.end.begin },
11
11
  flow_entry_count: :body_tracker_hits,
12
- rewrite: '((%{body_tracker};%{local}=nil;%{node}))'
12
+ rewrite: '(%{body_tracker};%{local}=nil;%{node})'
13
13
  check_completion
14
14
 
15
15
  def execution_count
@@ -3,6 +3,24 @@
3
3
  module DeepCover
4
4
  module Node::Mixin
5
5
  module Filters
6
+ module ClassMethods
7
+ def filter_to_method_name(kind)
8
+ :"is_#{kind}?"
9
+ end
10
+
11
+ def create_filter(name, &block)
12
+ Filters.define_method(filter_to_method_name(name), &block)
13
+ OPTIONALLY_COVERED << name
14
+ end
15
+
16
+ def unique_filter
17
+ (1..Float::INFINITY).each do |i|
18
+ name = :"custom_filter_#{i}"
19
+ return name unless Filters.method_defined?(filter_to_method_name(name))
20
+ end
21
+ end
22
+ end
23
+
6
24
  RAISING_MESSAGES = %i[raise exit].freeze
7
25
  def is_raise?
8
26
  is_a?(Node::Send) && RAISING_MESSAGES.include?(message) && receiver == nil
@@ -9,7 +9,7 @@ module DeepCover
9
9
  has_tracker :conditional
10
10
  has_child lhs: Node
11
11
  has_child conditional: Node, flow_entry_count: :conditional_tracker_hits,
12
- rewrite: '((%{conditional_tracker};%{node}))'
12
+ rewrite: '(%{conditional_tracker};%{node})'
13
13
 
14
14
  def branches
15
15
  [
@@ -34,7 +34,7 @@ module DeepCover
34
34
  # {a: {b: {d: {} } } }]
35
35
  # => {a: {b: {c: {}, d: {} }}}
36
36
  def deep_merge(trees)
37
- trees.inject do |result, h|
37
+ trees.inject({}) do |result, h|
38
38
  result.merge(h) { |k, val, val_b| deep_merge([val, val_b]) }
39
39
  end
40
40
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DeepCover
4
- VERSION = '0.4.0'
4
+ VERSION = '0.5.0'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: deep-cover
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marc-André Lafortune
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2018-02-07 00:00:00.000000000 Z
12
+ date: 2018-02-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: parser
@@ -25,6 +25,20 @@ dependencies:
25
25
  - - ">="
26
26
  - !ruby/object:Gem::Version
27
27
  version: '0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: parser_tree_rewriter
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
28
42
  - !ruby/object:Gem::Dependency
29
43
  name: backports
30
44
  requirement: !ruby/object:Gem::Requirement