rspec-cover_it 0.0.4 → 0.0.5
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 +4 -4
- data/README.md +9 -10
- data/lib/rspec/cover_it/context.rb +27 -2
- data/lib/rspec/cover_it/context_coverage.rb +28 -4
- data/lib/rspec/cover_it/coverage_state.rb +7 -3
- data/lib/rspec/cover_it/example_group_completeness_checker.rb +34 -0
- data/lib/rspec/cover_it/version.rb +1 -1
- data/lib/rspec/cover_it.rb +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b3efab53f0b9f05be2f5a8ae7f170e4a3a8e886439a751e025926c61bf9ac8f1
|
4
|
+
data.tar.gz: 569fbdcc5f328d5d0656eb3fe20265c9cd164455cfbf290c7764b92d15454fbf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e9d198749c87a093cf8cbf5d2e5a4f20bfdb1a4c312579a541d8335fa4ccab88781fdbee073195cf8d5c682d921019c9fb86ede9fe951e57f83df24722934c59
|
7
|
+
data.tar.gz: 604d4808f4a3d8afacf52cfe0651d4bbae46ee510ca95e123cd14304b071cbf1c726bf37741002efdd0d9dc636625b47d149f0a17226c26f42cdad0ba531be7f
|
data/README.md
CHANGED
@@ -58,13 +58,20 @@ In example groups, you can use metadata to control the behavior of
|
|
58
58
|
this ExampleGroup. If numeric, it's enabling, and specifying the coverage
|
59
59
|
threshold at the same time (as a percentage - `cover_it: 95` requires 95%
|
60
60
|
coverage of the target class by this example group).
|
61
|
+
* `covers_path`: The path (relative to the spec file!) of the code the spec is
|
62
|
+
intending to cover. Later, this can be an array of paths, for the multi-spec
|
63
|
+
case `covers` is intended for as well. This is an annoying work-around for
|
64
|
+
the fact that we can't perfectly infer the location of the source code in
|
65
|
+
some cases - in particular, `lib/foo/version.rb` tends to cause a problem
|
66
|
+
for specs on `foo.rb`, since the version file is invariably loaded first.
|
61
67
|
* `covers`: An array of classes and modules that this example groups _thinks
|
62
68
|
it is completely testing_. Ideally, you'd have a separate test file for each,
|
63
69
|
but sometimes that's hard to do - you can let one spec claim responsibility
|
64
70
|
for multiple classes and modules (such as Concerns) using this. Be default
|
65
71
|
it is just `[described_class]`. Additionally, if your top-level example
|
66
72
|
group _does not describe a Class or Module_, you may use `covers` to let it
|
67
|
-
invoke `rspec-cover_it` anyway
|
73
|
+
invoke `rspec-cover_it` anyway - some people `describe "a descriptive string"`
|
74
|
+
instead of `describe AClass`, and .. fine.
|
68
75
|
|
69
76
|
## Implementation
|
70
77
|
|
@@ -115,18 +122,10 @@ That's not the goal here, and I'm not going to worry about it.
|
|
115
122
|
As initially implemented, it fails your tests if you don't run the entire test
|
116
123
|
file. `rspec spec/foo_spec.rb:32` will error, because .. running only one of
|
117
124
|
your tests _doesn't cover the class_. I have a solution for this, but it uses
|
118
|
-
some non-public bits of RSpec, so I'm trying to find a better answer
|
125
|
+
some non-public bits of RSpec, so I'm trying to find a better answer still.
|
119
126
|
(Conversation started in their
|
120
127
|
[issue tracker](https://github.com/rspec/rspec-core/issues/3037))
|
121
128
|
|
122
|
-
So far, I haven't found a _great_ way to report that there's a problem. The
|
123
|
-
output from having insufficient coverage is _raising an exception from an
|
124
|
-
`after(:context)` hook_, which RSpec rescues and formats for itself, and there
|
125
|
-
aren't a lot of controls - it works, but it's a bit ugly and doesn't really
|
126
|
-
give the right immediate impression. I'm contemplating using an `after(:suite)`
|
127
|
-
hook and aggregating them myself, but at the end of the day RSpec is in control
|
128
|
-
of the output stream, and we don't entirely fit its metaphor.
|
129
|
-
|
130
129
|
We're using `Object.const_source_location` to find the path of the source file
|
131
130
|
defining a given constant. That _mostly_ works, but it actually gives the path
|
132
131
|
of the _first_ source file that defined that constant. So if your gem defines
|
@@ -6,11 +6,18 @@ module RSpec
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def cover_it?
|
9
|
-
target_class &&
|
9
|
+
target_class &&
|
10
|
+
metadata.fetch(:cover_it, autoenforce?) &&
|
11
|
+
completeness_checker.running_all_examples?
|
12
|
+
end
|
13
|
+
|
14
|
+
def specific_threshold
|
15
|
+
meta_value = metadata.fetch(:cover_it, nil)
|
16
|
+
meta_value.is_a?(Numeric) ? meta_value / 100.0 : nil
|
10
17
|
end
|
11
18
|
|
12
19
|
def target_path
|
13
|
-
|
20
|
+
metadata.key?(:covers_path) ? metadata_path : inferred_path
|
14
21
|
end
|
15
22
|
|
16
23
|
def target_class
|
@@ -21,6 +28,10 @@ module RSpec
|
|
21
28
|
target_class.name
|
22
29
|
end
|
23
30
|
|
31
|
+
def scope_name
|
32
|
+
scope.file_path
|
33
|
+
end
|
34
|
+
|
24
35
|
private
|
25
36
|
|
26
37
|
attr_reader :scope, :rspec_context
|
@@ -32,6 +43,20 @@ module RSpec
|
|
32
43
|
def metadata
|
33
44
|
scope.metadata
|
34
45
|
end
|
46
|
+
|
47
|
+
def completeness_checker
|
48
|
+
@_completeness_checker ||= ExampleGroupCompletenessChecker.new(scope)
|
49
|
+
end
|
50
|
+
|
51
|
+
def metadata_path
|
52
|
+
supplied_path = metadata.fetch(:covers_path)
|
53
|
+
spec_directory = File.dirname(scope.file_path)
|
54
|
+
File.expand_path(supplied_path, spec_directory)
|
55
|
+
end
|
56
|
+
|
57
|
+
def inferred_path
|
58
|
+
Object.const_source_location(target_class_name).first
|
59
|
+
end
|
35
60
|
end
|
36
61
|
end
|
37
62
|
end
|
@@ -24,8 +24,29 @@ module RSpec
|
|
24
24
|
covered_line_count.to_f / coverable_line_count.to_f
|
25
25
|
end
|
26
26
|
|
27
|
-
def enforce!
|
28
|
-
|
27
|
+
def enforce!(default_threshold:)
|
28
|
+
if precovered?
|
29
|
+
fail_with_missing_code!
|
30
|
+
elsif local_coverage_rate < (context.specific_threshold || default_threshold)
|
31
|
+
fail_with_missing_coverage!
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def fail_with_missing_code!
|
38
|
+
fail(MissingCode, <<~MESSAGE.tr("\n", " "))
|
39
|
+
Example group `#{context.scope_name}` is attempting to cover the code for class
|
40
|
+
`#{context.target_class}`, but it was located at `#{context.target_path}`,
|
41
|
+
and does not appear to have any code to cover (or it was all executed before the
|
42
|
+
tests started). If this is not the correct path for the code under test, please
|
43
|
+
specify the correct path using the `covers:` spec metadata - sometimes the
|
44
|
+
rspec-cover_it gem isn't properly able to infer the correct source path for a
|
45
|
+
class.
|
46
|
+
MESSAGE
|
47
|
+
end
|
48
|
+
|
49
|
+
def fail_with_missing_coverage!
|
29
50
|
lines = local_coverage.each_with_index.select { |v, _i| v&.zero? }.map(&:last)
|
30
51
|
|
31
52
|
summary =
|
@@ -40,8 +61,6 @@ module RSpec
|
|
40
61
|
fail(MissingCoverage, message)
|
41
62
|
end
|
42
63
|
|
43
|
-
private
|
44
|
-
|
45
64
|
attr_reader :context, :pretest_results
|
46
65
|
|
47
66
|
def target_path
|
@@ -65,6 +84,11 @@ module RSpec
|
|
65
84
|
return nil unless local_coverage
|
66
85
|
local_coverage.count { |executions| executions && executions > 0 }
|
67
86
|
end
|
87
|
+
|
88
|
+
def precovered?
|
89
|
+
return @_precovered if defined?(@_precovered)
|
90
|
+
@_precovered = pretest_coverage.nil? || pretest_coverage.none? { |n| n&.zero? }
|
91
|
+
end
|
68
92
|
end
|
69
93
|
end
|
70
94
|
end
|
@@ -3,8 +3,8 @@ module RSpec
|
|
3
3
|
class CoverageState
|
4
4
|
attr_reader :filter
|
5
5
|
|
6
|
-
def initialize(filter: nil, autoenforce: false)
|
7
|
-
@filter, @autoenforce = filter, autoenforce
|
6
|
+
def initialize(filter: nil, autoenforce: false, default_threshold: 100.0)
|
7
|
+
@filter, @autoenforce, @default_threshold = filter, autoenforce, default_threshold
|
8
8
|
@pretest_results = nil
|
9
9
|
@context_coverages = {}
|
10
10
|
end
|
@@ -34,7 +34,7 @@ module RSpec
|
|
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
|
-
context_coverage.enforce!
|
37
|
+
context_coverage.enforce!(default_threshold: default_threshold_rate)
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
@@ -46,6 +46,10 @@ module RSpec
|
|
46
46
|
@autoenforce
|
47
47
|
end
|
48
48
|
|
49
|
+
def default_threshold_rate
|
50
|
+
@default_threshold / 100.0
|
51
|
+
end
|
52
|
+
|
49
53
|
def context_for(scope, rspec_context)
|
50
54
|
Context.new(scope: scope, rspec_context: rspec_context, autoenforce: autoenforce?)
|
51
55
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require "set"
|
2
|
+
|
3
|
+
module RSpec
|
4
|
+
module CoverIt
|
5
|
+
class ExampleGroupCompletenessChecker
|
6
|
+
# This class uses some bits of the RSpec::Core::ExampleGroup api that are
|
7
|
+
# not documented, and are marked `@private` using YARD notation. But I
|
8
|
+
# found no other reasonable way to answer this question, so I've isolated
|
9
|
+
# my intrusion into this class - hopefully, there will be a more
|
10
|
+
# appropriate way to determine this information in the future; I've begun
|
11
|
+
# that conversation here: https://github.com/rspec/rspec-core/issues/3037
|
12
|
+
|
13
|
+
def initialize(example_group)
|
14
|
+
@example_group = example_group
|
15
|
+
end
|
16
|
+
|
17
|
+
def running_all_examples?
|
18
|
+
all_examples == filtered_examples
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
attr_reader :example_group
|
24
|
+
|
25
|
+
def all_examples
|
26
|
+
@_all_examples ||= example_group.descendants.flat_map(&:examples).to_set
|
27
|
+
end
|
28
|
+
|
29
|
+
def filtered_examples
|
30
|
+
@_filtered_examples ||= example_group.descendant_filtered_examples.to_set
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/rspec/cover_it.rb
CHANGED
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.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eric Mueller
|
@@ -99,6 +99,7 @@ files:
|
|
99
99
|
- lib/rspec/cover_it/context.rb
|
100
100
|
- lib/rspec/cover_it/context_coverage.rb
|
101
101
|
- lib/rspec/cover_it/coverage_state.rb
|
102
|
+
- lib/rspec/cover_it/example_group_completeness_checker.rb
|
102
103
|
- lib/rspec/cover_it/pretest_coverage.rb
|
103
104
|
- lib/rspec/cover_it/version.rb
|
104
105
|
- rspec-cover_it.gemspec
|