rspec-cover_it 0.0.2 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 528de4fe406f539d93d8632624714721d8cded1463a35776c16423d387c13c30
4
- data.tar.gz: 57fc958451fb2dfeafb94aaaed56aba38befd938a4ea24d2f82d0ac09a46ee4e
3
+ metadata.gz: 66c4b3e0c88dffe53a1d435d40d4e1bdb52038661d8abb89ccc29ef6974398e2
4
+ data.tar.gz: 563d49e4e8656ea8da53defeb9e2d5f7ed9a91d36e5de0df126213f35dee6813
5
5
  SHA512:
6
- metadata.gz: 6c636b00bd14fd976d53f9f97cbd2d53334ecb5677ad480ef00e737b51144da2f0ae76fffc1f6d1fb0366381961311e8aec93dfe6453cc2942c10dc652d87a88
7
- data.tar.gz: 93c2432a8ce2725b2ca3ff9f1c8115a38154dc7c4426f7aa5667d12ed9546b9890d83885bcbe727064ea78972e58ab4e721b6dcef82d93c004d95f8a8ae6609f
6
+ metadata.gz: e17ac79db233e4a1f9571f98d025c37cf2e3c304155838fc221507ef5ac6a24aecb47be2f0a6678423009254a0bb5eac442ce775f4ad8bb3f9a66554e9f09fd6
7
+ data.tar.gz: 0c9afcc6b5f019c185365e5c40925a3b96b4108133d1dcc6150edb182780c35e2a946693106fdf9299e507007a1adfb1dc33cbc341b95f729cea1faf202bbab2
data/README.md CHANGED
@@ -78,7 +78,30 @@ We use `Object.const_source_location` to find the file that defines the
78
78
  means that, if you are reopening the class somewhere else, that coverage won't
79
79
  be checked; if you are including 15 Concerns, and don't intend to write separate
80
80
  specs for them, be sure to list them as `covers:` metadata on the test. Also,
81
- shame!.
81
+ shame!
82
+
83
+ ## Output
84
+
85
+ When there's missing coverage on the class under test, you'll currently see
86
+ output like this:
87
+
88
+ ```
89
+ ❯ rspec
90
+
91
+ Randomized with seed 29392
92
+ ...............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
93
+ An error occurred in an `after(:context)` hook.
94
+ Failure/Error: fail(MissingCoverage, message)
95
+ Missing coverage in /Users/emueller/src/quiet_quality/lib/quiet_quality/message.rb on line 7
96
+ # /Users/emueller/src/rspec-cover_it/lib/rspec/cover_it/context_coverage.rb:40:in `enforce!'
97
+ # /Users/emueller/src/rspec-cover_it/lib/rspec/cover_it/coverage_state.rb:37:in `block in finish_tracking_for'
98
+ # /Users/emueller/src/rspec-cover_it/lib/rspec/cover_it/coverage_state.rb:35:in `finish_tracking_for'
99
+ # /Users/emueller/src/rspec-cover_it/lib/rspec/cover_it.rb:26:in `block (2 levels) in setup'
100
+ .....................................................................................................................................................................................................................................................................................................
101
+
102
+ Finished in 1.06 seconds (files took 0.28925 seconds to load)
103
+ 852 examples, 0 failures, 1 error occurred outside of examples
104
+ ```
82
105
 
83
106
  ## Drawbacks and Shortcomings
84
107
 
@@ -103,3 +126,13 @@ aren't a lot of controls - it works, but it's a bit ugly and doesn't really
103
126
  give the right immediate impression. I'm contemplating using an `after(:suite)`
104
127
  hook and aggregating them myself, but at the end of the day RSpec is in control
105
128
  of the output stream, and we don't entirely fit its metaphor.
129
+
130
+ We're using `Object.const_source_location` to find the path of the source file
131
+ defining a given constant. That _mostly_ works, but it actually gives the path
132
+ of the _first_ source file that defined that constant. So if your gem defines
133
+ its version in `lib/foo/version.rb` (as an example), in a separate file from
134
+ lib/foo.rb, the _path_ for `Foo` may end up being the former. Which is.. not
135
+ going to have much coverable code, of course. This is an edge case, but one
136
+ that is likely to occur fairly regularly. I haven't thought of a _good_ solution
137
+ yet. Perhaps if the `covers` array includes a string, we should treat it as a
138
+ relative path?
@@ -1,12 +1,12 @@
1
1
  module RSpec
2
2
  module CoverIt
3
3
  class Context
4
- def initialize(scope:, rspec_context:)
5
- @scope, @rspec_context = scope, rspec_context
4
+ def initialize(scope:, rspec_context:, autoenforce:)
5
+ @scope, @rspec_context, @autoenforce = scope, rspec_context, autoenforce
6
6
  end
7
7
 
8
8
  def cover_it?
9
- target_class && metadata.fetch(:cover_it, nil)
9
+ target_class && metadata.fetch(:cover_it, autoenforce?)
10
10
  end
11
11
 
12
12
  def target_path
@@ -25,6 +25,10 @@ module RSpec
25
25
 
26
26
  attr_reader :scope, :rspec_context
27
27
 
28
+ def autoenforce?
29
+ @autoenforce
30
+ end
31
+
28
32
  def metadata
29
33
  scope.metadata
30
34
  end
@@ -8,6 +8,10 @@ module RSpec
8
8
 
9
9
  attr_accessor :precontext_coverage, :postcontext_coverage
10
10
 
11
+ def pretest_coverage
12
+ @_pretest_coverage ||= pretest_results[target_path]
13
+ end
14
+
11
15
  def local_coverage
12
16
  return nil unless precontext_coverage && postcontext_coverage
13
17
  @_local_coverage ||= pretest_coverage
@@ -20,18 +24,30 @@ module RSpec
20
24
  covered_line_count.to_f / coverable_line_count.to_f
21
25
  end
22
26
 
27
+ def enforce!
28
+ return if local_coverage.nil? || local_coverage_rate >= 1.0
29
+ lines = local_coverage.each_with_index.select { |v, _i| v&.zero? }.map(&:last)
30
+
31
+ summary =
32
+ if lines.length == 1
33
+ "on line #{lines.first}"
34
+ elsif lines.length <= 10
35
+ "on lines #{lines.map(&:to_s).join(", ")}"
36
+ else
37
+ "on #{lines.length} lines, including #{lines.first(10).map(&:to_s).join(", ")}"
38
+ end
39
+ message = "Missing coverage in #{context.target_path} #{summary}"
40
+ fail(MissingCoverage, message)
41
+ end
42
+
23
43
  private
24
44
 
25
- attr_reader :context, :pretest_results, :precontext_coverage, :postcontext_coverage
45
+ attr_reader :context, :pretest_results
26
46
 
27
47
  def target_path
28
48
  @_target_path ||= context.target_path
29
49
  end
30
50
 
31
- def pretest_coverage
32
- @_pretest_coverage ||= pretest_results[target_path]
33
- end
34
-
35
51
  # Really, we shouldn't see nil for any of these values unless they are all
36
52
  # nil. We want the coverage we'd expect to have seen if we ran _just_ this
37
53
  # groups of examples, which ought boe the pretest coverage, plus the
@@ -3,8 +3,8 @@ module RSpec
3
3
  class CoverageState
4
4
  attr_reader :filter
5
5
 
6
- def initialize(filter:)
7
- @filter = filter
6
+ def initialize(filter: nil, autoenforce: false)
7
+ @filter, @autoenforce = filter, autoenforce
8
8
  @pretest_results = nil
9
9
  @context_coverages = {}
10
10
  end
@@ -20,7 +20,7 @@ module RSpec
20
20
  end
21
21
 
22
22
  def start_tracking_for(scope, rspec_context)
23
- context = Context.new(scope: scope, rspec_context: rspec_context)
23
+ context = context_for(scope, rspec_context)
24
24
  return unless context.cover_it?
25
25
 
26
26
  context_coverage_for(context).tap do |context_coverage|
@@ -29,12 +29,12 @@ module RSpec
29
29
  end
30
30
 
31
31
  def finish_tracking_for(scope, rspec_context)
32
- context = Context.new(scope: scope, rspec_context: rspec_context)
32
+ context = context_for(scope, rspec_context)
33
33
  return unless context.cover_it?
34
34
 
35
35
  context_coverage_for(context).tap do |context_coverage|
36
36
  context_coverage.postcontext_coverage = Coverage.peek_result[context.target_path]
37
- fail("missing coverage!") if context_coverage.local_coverage_rate < 1.0
37
+ context_coverage.enforce!
38
38
  end
39
39
  end
40
40
 
@@ -42,6 +42,14 @@ module RSpec
42
42
 
43
43
  attr_reader :pretest_results
44
44
 
45
+ def autoenforce?
46
+ @autoenforce
47
+ end
48
+
49
+ def context_for(scope, rspec_context)
50
+ Context.new(scope: scope, rspec_context: rspec_context, autoenforce: autoenforce?)
51
+ end
52
+
45
53
  def context_coverage_for(context)
46
54
  @context_coverages[context.target_class] ||= ContextCoverage.new(
47
55
  context: context,
@@ -4,7 +4,7 @@ module RSpec
4
4
  def initialize(filter:, results:)
5
5
  @filter = filter
6
6
  @results = results
7
- .select { |k, _v| k.start_with?(filter) }
7
+ .select { |k, _v| filter.nil? || k.start_with?(filter) }
8
8
  .map { |k, v| [k, v.dup] }
9
9
  .to_h
10
10
  end
@@ -1,5 +1,5 @@
1
1
  module RSpec
2
2
  module CoverIt
3
- VERSION = "0.0.2"
3
+ VERSION = "0.0.4"
4
4
  end
5
5
  end
@@ -16,8 +16,8 @@ module RSpec
16
16
  attr_accessor :state
17
17
  end
18
18
 
19
- def self.setup(filter:)
20
- RSpec::CoverIt.state = CoverageState.new(filter: filter)
19
+ def self.setup(filter: nil, autoenforce: false)
20
+ RSpec::CoverIt.state = CoverageState.new(filter: filter, autoenforce: autoenforce)
21
21
  RSpec::CoverIt.state.start_tracking
22
22
 
23
23
  RSpec.configure do |config|
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-cover_it
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric Mueller