deep-cover 0.5.2 → 0.5.3

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 (84) hide show
  1. checksums.yaml +5 -5
  2. data/.deep_cover.rb +8 -0
  3. data/.gitignore +1 -0
  4. data/.rubocop.yml +15 -1
  5. data/.travis.yml +1 -0
  6. data/README.md +30 -1
  7. data/Rakefile +10 -1
  8. data/bin/cov +1 -1
  9. data/deep_cover.gemspec +4 -5
  10. data/exe/deep-cover +5 -3
  11. data/lib/deep_cover.rb +1 -1
  12. data/lib/deep_cover/analyser/node.rb +1 -1
  13. data/lib/deep_cover/analyser/ruby25_like_branch.rb +209 -0
  14. data/lib/deep_cover/auto_run.rb +19 -19
  15. data/lib/deep_cover/autoload_tracker.rb +181 -44
  16. data/lib/deep_cover/backports.rb +3 -1
  17. data/lib/deep_cover/base.rb +13 -8
  18. data/lib/deep_cover/basics.rb +1 -1
  19. data/lib/deep_cover/cli/debugger.rb +2 -2
  20. data/lib/deep_cover/cli/instrumented_clone_reporter.rb +21 -8
  21. data/lib/deep_cover/cli/runner.rb +126 -0
  22. data/lib/deep_cover/config_setter.rb +1 -0
  23. data/lib/deep_cover/core_ext/autoload_overrides.rb +82 -14
  24. data/lib/deep_cover/core_ext/coverage_replacement.rb +34 -5
  25. data/lib/deep_cover/core_ext/exec_callbacks.rb +27 -0
  26. data/lib/deep_cover/core_ext/load_overrides.rb +4 -6
  27. data/lib/deep_cover/core_ext/require_overrides.rb +1 -3
  28. data/lib/deep_cover/coverage.rb +105 -2
  29. data/lib/deep_cover/coverage/analysis.rb +30 -28
  30. data/lib/deep_cover/coverage/persistence.rb +60 -70
  31. data/lib/deep_cover/covered_code.rb +16 -49
  32. data/lib/deep_cover/custom_requirer.rb +112 -51
  33. data/lib/deep_cover/load.rb +10 -6
  34. data/lib/deep_cover/memoize.rb +1 -3
  35. data/lib/deep_cover/module_override.rb +7 -0
  36. data/lib/deep_cover/node/assignments.rb +2 -1
  37. data/lib/deep_cover/node/base.rb +6 -6
  38. data/lib/deep_cover/node/block.rb +10 -8
  39. data/lib/deep_cover/node/case.rb +3 -3
  40. data/lib/deep_cover/node/collections.rb +8 -0
  41. data/lib/deep_cover/node/if.rb +19 -3
  42. data/lib/deep_cover/node/literals.rb +28 -7
  43. data/lib/deep_cover/node/mixin/can_augment_children.rb +4 -4
  44. data/lib/deep_cover/node/mixin/child_can_be_empty.rb +1 -1
  45. data/lib/deep_cover/node/mixin/filters.rb +6 -2
  46. data/lib/deep_cover/node/mixin/has_child.rb +8 -8
  47. data/lib/deep_cover/node/mixin/has_child_handler.rb +3 -3
  48. data/lib/deep_cover/node/mixin/has_tracker.rb +7 -3
  49. data/lib/deep_cover/node/root.rb +1 -1
  50. data/lib/deep_cover/node/send.rb +53 -7
  51. data/lib/deep_cover/node/short_circuit.rb +11 -3
  52. data/lib/deep_cover/parser_ext/range.rb +11 -27
  53. data/lib/deep_cover/problem_with_diagnostic.rb +1 -1
  54. data/lib/deep_cover/reporter.rb +0 -1
  55. data/lib/deep_cover/reporter/base.rb +68 -0
  56. data/lib/deep_cover/reporter/html.rb +1 -1
  57. data/lib/deep_cover/reporter/html/index.rb +4 -8
  58. data/lib/deep_cover/reporter/html/site.rb +10 -18
  59. data/lib/deep_cover/reporter/html/source.rb +3 -3
  60. data/lib/deep_cover/reporter/html/template/source.html.erb +1 -1
  61. data/lib/deep_cover/reporter/istanbul.rb +86 -56
  62. data/lib/deep_cover/reporter/text.rb +5 -13
  63. data/lib/deep_cover/reporter/{util/tree.rb → tree/util.rb} +19 -21
  64. data/lib/deep_cover/tools/blank.rb +25 -0
  65. data/lib/deep_cover/tools/builtin_coverage.rb +8 -8
  66. data/lib/deep_cover/tools/dump_covered_code.rb +2 -9
  67. data/lib/deep_cover/tools/execute_sample.rb +17 -6
  68. data/lib/deep_cover/tools/format_generated_code.rb +1 -1
  69. data/lib/deep_cover/tools/indent_string.rb +26 -0
  70. data/lib/deep_cover/tools/our_coverage.rb +2 -2
  71. data/lib/deep_cover/tools/strip_heredoc.rb +18 -0
  72. data/lib/deep_cover/tracker_bucket.rb +50 -0
  73. data/lib/deep_cover/tracker_hits_per_path.rb +35 -0
  74. data/lib/deep_cover/tracker_storage.rb +76 -0
  75. data/lib/deep_cover/tracker_storage_per_path.rb +34 -0
  76. data/lib/deep_cover/version.rb +1 -1
  77. data/lib/deep_cover_entry.rb +3 -0
  78. metadata +30 -37
  79. data/bin/gemcov +0 -8
  80. data/bin/selfcov +0 -21
  81. data/lib/deep_cover/cli/deep_cover.rb +0 -126
  82. data/lib/deep_cover/coverage/base.rb +0 -81
  83. data/lib/deep_cover/coverage/istanbul.rb +0 -34
  84. data/lib/deep_cover/tools/transform_keys.rb +0 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: c66eb6bae41cc00df8fab37daf462d07a7ca032f
4
- data.tar.gz: c0df6acbd831fc6b3ce08ee8586c559d7f929e58
2
+ SHA256:
3
+ metadata.gz: f6177553f2233c0751899ecb895cc73ae75777977f26330b76e51507e875eb05
4
+ data.tar.gz: 213472177be49d89c862b8e0c334c6e5d86ec97d9abc193f201b9ca863092d53
5
5
  SHA512:
6
- metadata.gz: d1f1252d95b2078f50344e7403f7bb093784a9e5f94b27dfc0c2d11f905d954a321bfc2a99af7c32b0377b108accf550466bc2ca1a2a2d281cd55d90fa72d74c
7
- data.tar.gz: 28b4540147ff286db023c82b6bfc477f8d87913eb46e3954622970d5b70e614ed5c1331da80e6a34a9697c9d93ef47684d625466a6baf515e58868a45f210daa
6
+ metadata.gz: 375e33c302f38bda95139adaf8ee2d4500e935ce980794e6dcd382bcd6af42ba8a58c1aa98c830cfd0974ed21763f17bc8341b5720f40900f855a9e7a584e9fd
7
+ data.tar.gz: b62f8dd1c0978a69b2c0b80850def8adcfda8c8c861dc8c33e803b670ea6cd779c726678f703037f53c0b93ec72c2868dfb73a447baa2bd5799e7d030b295bb7
data/.deep_cover.rb ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is executed only when deep_cover is covering itself.
4
+
5
+ DeepCover.configure do
6
+ tracker_global '$_dcg'
7
+ ignore_uncovered :warn, :raise, :default_argument
8
+ end
data/.gitignore CHANGED
@@ -15,3 +15,4 @@
15
15
  /Gemfile.local
16
16
  /spec/cli_fixtures/covered_trivial_gem/deep_cover/
17
17
  /deep_cover
18
+ /exe/deep_cover/
data/.rubocop.yml CHANGED
@@ -26,6 +26,9 @@ Layout/IndentArray:
26
26
  Layout/IndentHash:
27
27
  EnforcedStyle: align_braces
28
28
 
29
+ Layout/IndentHeredoc:
30
+ Enabled: false
31
+
29
32
  Layout/MultilineArrayBraceLayout:
30
33
  EnforcedStyle: new_line
31
34
 
@@ -127,7 +130,10 @@ Style/StringLiterals:
127
130
  Style/TrailingCommaInArguments:
128
131
  Enabled: false
129
132
 
130
- Style/TrailingCommaInLiteral:
133
+ Style/TrailingCommaInArrayLiteral:
134
+ EnforcedStyleForMultiline: consistent_comma
135
+
136
+ Style/TrailingCommaInHashLiteral:
131
137
  EnforcedStyleForMultiline: consistent_comma
132
138
 
133
139
  Naming/VariableNumber:
@@ -236,3 +242,11 @@ Style/EvalWithLocation:
236
242
 
237
243
  Lint/BooleanSymbol:
238
244
  Enabled: false
245
+
246
+ Naming/UncommunicativeMethodParamName:
247
+ Exclude:
248
+ - spec/**/*
249
+
250
+ Layout/SpaceInsideReferenceBrackets:
251
+ Exclude:
252
+ - spec/**/*
data/.travis.yml CHANGED
@@ -10,6 +10,7 @@ rvm:
10
10
  - jruby-9.1.9.0
11
11
  before_install:
12
12
  - gem update --system
13
+ - gem install bundler
13
14
  - npm install -g nyc
14
15
  before_script:
15
16
  - bundle exec rake dev:install
data/README.md CHANGED
@@ -41,6 +41,35 @@ These examples are direct outputs from our HTML reporter:
41
41
  * [Rails' `activesupport`](https://deep-cover.github.io/rails-cover/activesupport/)
42
42
  * [Rails' `activerecord`](https://deep-cover.github.io/rails-cover/activerecord/)
43
43
 
44
+ ## DeepCover vs builtin coverage
45
+
46
+ Feature | MRI | DeepCover
47
+ -------------------|:----------:|:--------:
48
+ Line coverage | partial | ✓
49
+ Statement coverage | no | ✓
50
+ Branch coverage | partial | ✓
51
+ Method coverage | ✓ | ~
52
+ Slowdown | < 1% | ~20%
53
+ Platform support | Ruby 2.5+ | Ruby 2.1+, JRuby
54
+
55
+ **Line coverage**: MRI doesn't cover some lines (e.g. `when some_value`).
56
+
57
+ **Statement coverage**: MRI provides no way to tell which parts of any line is evaluated. DeepCover covers everything.
58
+
59
+ **Method coverage**: MRI considers every method defined, including methods defined on objects or via `define_method`, `class_eval`, etc. For Istanbul output, DeepCover has a different approach and covers all `def` and all blocks.
60
+
61
+ **Branch coverage** | MRI | DeepCover
62
+ ------------------------|:---:|:--------:
63
+ `if` / `unless` / `?:` | ✓ | ✓
64
+ `case` / `when` | ✓ | ✓
65
+ `❘❘` / `&&` | no | ✓
66
+ `foo&.bar` | ✓ | ✓
67
+ `{❘foo = 42, bar: 43❘}` | no | ✓
68
+ `while` / `until` | ✓ | !
69
+
70
+ *Note on loops (!)*: DeepCover doesn't consider loops to be branches, but it's
71
+ easy to support it if needed.
72
+
44
73
  ## Installation
45
74
 
46
75
  gem install deep-cover
@@ -115,7 +144,7 @@ To make it easier to transition for projects already using the builtin `Coverage
115
144
 
116
145
  Add to your Gemfile `gem 'deep-cover'`, then run `bundle`.
117
146
 
118
- Before you require `coverage` or `simplecov`, do a `require 'deep-cover/builtin_takeover'`.
147
+ Before you require `coverage` or `simplecov`, do a `require 'deep_cover/builtin_takeover'`.
119
148
 
120
149
  For example, the `test/test_helper.rb` file for `simplecov` users will look like
121
150
 
data/Rakefile CHANGED
@@ -4,7 +4,9 @@ require 'bundler/gem_tasks'
4
4
  require 'rspec/core/rake_task'
5
5
  require 'rubocop/rake_task'
6
6
 
7
- RuboCop::RakeTask.new
7
+ RuboCop::RakeTask.new(:rubocop) do |t|
8
+ t.options = ['-a']
9
+ end
8
10
 
9
11
  RSpec::Core::RakeTask.new(:spec).tap { |task| task.pattern = 'spec/*_spec.rb, spec/*/*_spec.rb' }
10
12
 
@@ -18,6 +20,13 @@ multitask default: RUBY_VERSION > '2.1' ? [:rubocop, :spec] : :spec
18
20
  multitask 'test:all' => RUBY_VERSION > '2.1' ? [:rubocop, 'spec:all'] : 'spec:all'
19
21
 
20
22
  namespace :dev do
23
+ desc 'Self cover'
24
+ task :cov do
25
+ command = "exe/deep-cover . --no-bundle --command 'rake spec:all'"
26
+ puts command
27
+ system command
28
+ end
29
+
21
30
  desc 'Setup extra things required to run the spec suite'
22
31
  task :install do
23
32
  commands = []
data/bin/cov CHANGED
@@ -2,7 +2,6 @@
2
2
  module DeepCover
3
3
  require 'bundler/setup'
4
4
 
5
- $LOAD_PATH.unshift('../covered_deep_cover') if ENV['CC']
6
5
  require 'deep_cover'
7
6
  require_relative '../spec/specs_tools'
8
7
  require 'deep_cover/cli/debugger'
@@ -10,6 +9,7 @@ module DeepCover
10
9
  example = ARGV[0] || 'simple_if'
11
10
  fn = "./spec/samples/#{example}.rb"
12
11
  fn = "./spec/char_cover/#{example}.rb" unless File.exist?(fn)
12
+ fn = example unless File.exist?(fn)
13
13
  if File.exist?(fn)
14
14
  fn = File.absolute_path(fn)
15
15
  source = File.read(fn)
data/deep_cover.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- lib = File.expand_path('../lib', __FILE__)
3
+ lib = File.expand_path('lib', __dir__)
4
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
5
  require 'deep_cover/version'
6
6
 
@@ -26,14 +26,14 @@ Gem::Specification.new do |spec|
26
26
  spec.required_ruby_version = '>= 2.1.0'
27
27
 
28
28
  # Main dependency
29
- spec.add_runtime_dependency 'parser'
30
- spec.add_runtime_dependency 'parser_tree_rewriter'
29
+ spec.add_runtime_dependency 'parser', '~> 2.5.0'
31
30
 
32
31
  # Support
33
32
  spec.add_runtime_dependency 'backports', '>= 3.11.0'
34
33
  spec.add_runtime_dependency 'binding_of_caller'
35
34
 
36
35
  # CLI
36
+ spec.add_runtime_dependency 'bundler'
37
37
  spec.add_runtime_dependency 'highline'
38
38
  spec.add_runtime_dependency 'slop', '~> 4.0'
39
39
  spec.add_runtime_dependency 'term-ansicolor'
@@ -46,10 +46,9 @@ Gem::Specification.new do |spec|
46
46
  spec.add_runtime_dependency 'pry'
47
47
 
48
48
  ### Dev dependencies
49
- spec.add_development_dependency 'activesupport', '~> 4.0'
50
49
  spec.add_development_dependency 'bundler', '~> 1.15'
51
50
  spec.add_development_dependency 'psych', '>= 2.0'
52
51
  spec.add_development_dependency 'rake', '~> 12.0'
53
52
  spec.add_development_dependency 'rspec', '~> 3.0'
54
- spec.add_development_dependency 'rubocop', '0.52.1' # About every single release breaks something
53
+ spec.add_development_dependency 'rubocop', '0.53.0' # About every single release breaks something
55
54
  end
data/exe/deep-cover CHANGED
@@ -1,7 +1,9 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- $LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
5
- require 'deep_cover/cli/deep_cover'
4
+ $LOAD_PATH.unshift(File.expand_path('../lib', __dir__))
6
5
 
7
- DeepCover::CLI::DeepCover.go
6
+ require_relative '../lib/deep_cover_entry'
7
+ require 'deep_cover/cli/runner'
8
+
9
+ DeepCover::CLI::Runner.go
data/lib/deep_cover.rb CHANGED
@@ -18,5 +18,5 @@ end
18
18
  if %w[1 t true].include?(ENV['DEEP_COVER'])
19
19
  DeepCover.start
20
20
  require_relative 'deep_cover/auto_run'
21
- DeepCover::AutoRun.run!('.').and_report!(**DeepCover.config)
21
+ DeepCover::AutoRun.run!('.').report!(**DeepCover.config)
22
22
  end
@@ -29,7 +29,7 @@ module DeepCover
29
29
 
30
30
  protected
31
31
 
32
- def convert(node, **)
32
+ def convert(node, **) # rubocop:disable Naming/UncommunicativeMethodParamName [#5436]
33
33
  Analyser::CoveredCodeSource.new(node)
34
34
  end
35
35
 
@@ -0,0 +1,209 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'subset'
4
+
5
+ module DeepCover
6
+ class Analyser::Ruby25LikeBranch < Analyser
7
+ def self.human_name
8
+ 'Ruby25 branches'
9
+ end
10
+ include Analyser::Subset
11
+ SUBSET_CLASSES = [Node::Case, Node::Csend, Node::If, Node::ShortCircuit,
12
+ Node::Until, Node::UntilPost, Node::While, Node::WhilePost,
13
+ ].freeze
14
+
15
+ def initialize(*args)
16
+ super
17
+ @loc_index = 0
18
+ end
19
+
20
+ def results
21
+ extractor = NodeCoverageExtrator.new
22
+ each_node.map do |node|
23
+ extractor.branch_coverage(node)
24
+ end.to_h
25
+ end
26
+
27
+ # This is the class doing the work. Since everything is about the node, the class delegates
28
+ # missing methods to the node, simplifying the code.
29
+ class NodeCoverageExtrator < SimpleDelegator
30
+ def initialize(node = nil)
31
+ self.node = node
32
+ @loc_index = 0
33
+ end
34
+
35
+ alias_method :node=, :__setobj__
36
+ alias_method :node, :__getobj__
37
+
38
+ def branch_coverage(node)
39
+ self.node = node
40
+ case node
41
+ when Node::Case
42
+ handle_case
43
+ when Node::Csend
44
+ handle_csend
45
+ when Node::If
46
+ handle_if
47
+ when Node::ShortCircuit
48
+ handle_short_circuit
49
+ when Node::Until, Node::While, Node::UntilPost, Node::WhilePost
50
+ handle_until_while
51
+ end
52
+ end
53
+
54
+ def handle_case
55
+ cond_info = [:case, *node_loc_infos]
56
+
57
+ sub_keys = [:when] * (branches.size - 1) + [:else]
58
+ empty_fallbacks = whens.map { |w| (w.loc_hash[:begin] || w.loc_hash[:expression]).wrap_rwhitespace_and_comments.end }
59
+ empty_fallbacks.map!(&:begin)
60
+
61
+ if loc_hash[:else]
62
+ empty_fallbacks << loc_hash[:end].begin
63
+ else
64
+ # DeepCover manually inserts a `else` for Case when there isn't one for tracker purposes.
65
+ # The normal behavior of ruby25's branch coverage when there is no else is to return the loc of the node
66
+ # So we sent that fallback.
67
+ empty_fallbacks << expression
68
+ end
69
+
70
+ branches_locs = whens.map do |when_node|
71
+ next when_node.body if when_node.body.is_a?(Node::EmptyBody)
72
+
73
+ start_at = when_node.loc_hash[:begin]
74
+ start_at = start_at.wrap_rwhitespace_and_comments.end if start_at
75
+ start_at ||= when_node.body.expression.begin
76
+
77
+ end_at = when_node.body.expression.end
78
+ start_at.with(end_pos: end_at.end_pos)
79
+ end
80
+
81
+ branches_locs << node.else
82
+ clauses_infos = infos_for_branches(branches_locs, sub_keys, empty_fallbacks, execution_counts: branches.map(&:execution_count))
83
+
84
+ [cond_info, clauses_infos]
85
+ end
86
+
87
+ def handle_csend
88
+ cond_info = [:"&.", *node_loc_infos]
89
+ false_branch, true_branch = branches
90
+ [cond_info, {[:then, *node_loc_infos] => true_branch.execution_count,
91
+ [:else, *node_loc_infos] => false_branch.execution_count,
92
+ },
93
+ ]
94
+ end
95
+
96
+ def handle_if
97
+ key = style == :unless ? :unless : :if
98
+
99
+ node_range = extend_elsif_range
100
+ cond_info = [key, *node_loc_infos(node_range)]
101
+
102
+ sub_keys = [:then, :else]
103
+ if style == :ternary
104
+ empty_fallback_locs = [nil, nil]
105
+ else
106
+ else_loc = loc_hash[:else]
107
+
108
+ first_clause_fallback = loc_hash[:begin]
109
+ if first_clause_fallback
110
+ first_clause_fallback = first_clause_fallback.wrap_rwhitespace_and_comments.end
111
+ elsif else_loc
112
+ first_clause_fallback = else_loc.begin
113
+ end
114
+
115
+ if else_loc
116
+ second_clause_fallback = else_loc.wrap_rwhitespace_and_comments.end
117
+ end
118
+ end_loc = root_if_node.loc_hash[:end]
119
+ end_loc = end_loc.begin if end_loc
120
+
121
+ empty_fallback_locs = [first_clause_fallback || end_loc, second_clause_fallback || end_loc]
122
+ end
123
+ # loc can be nil if the clause can't be empty, such as ternary and modifer if/unless
124
+
125
+ if key == :unless
126
+ sub_keys.reverse!
127
+ empty_fallback_locs.reverse!
128
+ end
129
+
130
+ branches_locs = branches
131
+ execution_counts = branches_locs.map(&:execution_count)
132
+ branches_locs[1] = extend_elsif_range(branches_locs[1])
133
+
134
+ clauses_infos = infos_for_branches(branches_locs, sub_keys, empty_fallback_locs, execution_counts: execution_counts, node_range: node_range)
135
+ [cond_info, clauses_infos]
136
+ end
137
+
138
+ def handle_short_circuit
139
+ cond_info = [operator, *node_loc_infos]
140
+ sub_keys = [:then, :else]
141
+ sub_keys.reverse! if node.is_a?(Node::Or)
142
+
143
+ [cond_info, infos_for_branches(branches, sub_keys, [nil, nil])]
144
+ end
145
+
146
+ def handle_until_while
147
+ key = loc_hash[:keyword].source.to_sym
148
+ base_info = [key, *node_loc_infos]
149
+ body_node = if node.is_a?(Node::WhilePost) || node.is_a?(Node::UntilPost)
150
+ if !body.instructions.empty?
151
+ end_pos = body.instructions.last.expression.end_pos
152
+ body.instructions.first.expression.with(end_pos: end_pos)
153
+ else
154
+ body.loc_hash[:end].begin
155
+ end
156
+ elsif body.is_a?(Node::Begin) && !node.body.expressions.empty?
157
+ end_pos = body.expressions.last.expression.end_pos
158
+ body.expressions.first.expression.with(end_pos: end_pos)
159
+ else
160
+ body
161
+ end
162
+
163
+ [base_info, {[:body, *node_loc_infos(body_node)] => body.execution_count}]
164
+ end
165
+
166
+ protected
167
+
168
+ # If the actual else clause (final one) of an if...elsif...end is empty, then Ruby makes the
169
+ # node reach the `end` in its branch coverage output
170
+ def extend_elsif_range(possible_elsif = node)
171
+ return possible_elsif unless possible_elsif.is_a?(Node::If) && possible_elsif.style == :elsif
172
+ deepest_if = possible_elsif.deepest_elsif_node
173
+ if deepest_if.false_branch.is_a?(Node::EmptyBody)
174
+ return possible_elsif.expression.with(end_pos: possible_elsif.root_if_node.loc_hash[:end].begin_pos)
175
+ end
176
+ possible_elsif
177
+ end
178
+
179
+ def infos_for_branch(branch, key, empty_fallback_loc, execution_count: nil, node_range: node)
180
+ if !branch.is_a?(Node::EmptyBody)
181
+ loc = branch
182
+ elsif branch.expression
183
+ # There is clause, but it is empty
184
+ loc = empty_fallback_loc
185
+ else
186
+ # There is no clause
187
+ loc = node_range
188
+ end
189
+
190
+ execution_count ||= branch.execution_count
191
+ [[key, *node_loc_infos(loc)], execution_count]
192
+ end
193
+
194
+ def infos_for_branches(branches, keys, empty_fallback_locs, execution_counts: [], node_range: node)
195
+ branches_infos = branches.map.with_index do |branch, i|
196
+ infos_for_branch(branch, keys[i], empty_fallback_locs[i], execution_count: execution_counts[i], node_range: node_range)
197
+ end
198
+ branches_infos.to_h
199
+ end
200
+
201
+ def node_loc_infos(node_or_range = node)
202
+ source_range = node_or_range.is_a?(Node) ? node_or_range.expression : node_or_range
203
+
204
+ @loc_index += 1
205
+ [@loc_index, source_range.line, source_range.column, source_range.last_line, source_range.last_column]
206
+ end
207
+ end
208
+ end
209
+ end
@@ -1,18 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../deep_cover'
4
- require 'pry'
3
+ require_relative 'core_ext/exec_callbacks'
5
4
 
6
5
  module DeepCover
7
6
  module AutoRun
8
7
  class Runner
9
8
  def initialize(covered_path)
10
9
  @covered_path = covered_path
10
+ @saved = !(DeepCover.respond_to?(:running?) && DeepCover.running?)
11
11
  end
12
12
 
13
13
  def run!
14
- @coverage = load_coverage
15
14
  after_tests { save }
15
+ ExecCallbacks.before_exec { save }
16
16
  self
17
17
  end
18
18
 
@@ -23,22 +23,26 @@ module DeepCover
23
23
 
24
24
  private
25
25
 
26
- def load_coverage
27
- @not_saved = DeepCover.respond_to?(:running?) && DeepCover.running?
28
- if @not_saved
29
- DeepCover.coverage
30
- else
31
- Coverage.load(@covered_path, with_trackers: false)
32
- end
26
+ def saved?
27
+ @saved
28
+ end
29
+
30
+ def coverage
31
+ @coverage ||= if saved?
32
+ Coverage.load(@covered_path, with_trackers: false)
33
+ else
34
+ DeepCover.coverage
35
+ end
33
36
  end
34
37
 
35
38
  def save
36
- @coverage.save(@covered_path) if @not_saved
37
- @coverage.save_trackers(@covered_path)
39
+ require_relative '../deep_cover'
40
+ coverage.save(@covered_path) unless saved?
41
+ coverage.save_trackers(@covered_path)
38
42
  end
39
43
 
40
44
  def report(**options)
41
- @coverage.report(**options)
45
+ coverage.report(**options)
42
46
  end
43
47
 
44
48
  def after_tests
@@ -60,12 +64,8 @@ module DeepCover
60
64
  end
61
65
 
62
66
  def self.run!(covered_path)
63
- @runner ||= Runner.new(covered_path).run!
64
- self
65
- end
66
-
67
- def self.and_report!(**options)
68
- @runner.report!(**options)
67
+ @runners ||= {}
68
+ @runners[covered_path] ||= Runner.new(covered_path).run!
69
69
  end
70
70
  end
71
71
  end