deep-cover 0.5.2 → 0.5.3

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