simplecov 0.18.0.beta3 → 0.18.1

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
  SHA256:
3
- metadata.gz: a5ac4654ad44859d2e7b68cc5375314766127720e33d51080cc02120d35cc9cb
4
- data.tar.gz: 89ceeaf8b9b1a76bd05a4f078c50a80050bd247ef067164201f9721ce7378b5b
3
+ metadata.gz: 6139c9ec905e1f20dec5bdfd8fef48fe8b9091ce24ae1b55e89b850093cfcf69
4
+ data.tar.gz: 3e15d4dbbc0f68b9453241603c0a1beade1b29ca2fbf64190f742f69db307b13
5
5
  SHA512:
6
- metadata.gz: 8afee26e98b841b002d097d589afbbc8b892452897423c927b817b51696bb7063e3f2e849f462434b765fb09970fbb6759d31d5362ea5c6e90eb65e82824eb78
7
- data.tar.gz: ed2f4ffab150fd1da883604eccec94a087184aeaadb4b4d6f1f7273efa6020d1034b9e2eebbc8b88a9c63c54d545ccd6db4f5db58ee29f8182703ed40281342e
6
+ metadata.gz: 3af5aa2cad84980d28f95c606d0e7cc8282d5c81689bdaa637cb29a971ffe2842b7cd91ba2d602a6214f7c917851886ead8a62b50a8e071a12edbddbb7355a45
7
+ data.tar.gz: a3afd317b84ae5b11b907384c3ee17436e3a033c956c7ebdc000db0fca852442e4e658b72dc09c07f3c510f659297ab386f07af4fbee1c43b8a81794da595f6a
data/CHANGELOG.md CHANGED
@@ -1,3 +1,31 @@
1
+ 0.18.1 (2020-01-31)
2
+ ===================
3
+
4
+ Small Bugfix release.
5
+
6
+ ## Bugfixes
7
+ * Just putting `# :nocov:` on top of a file or having an uneven number of them in general works again and acts as if ignoring until the end of the file. See [#846](https://github.com/colszowka/simplecov/issues/846) and thanks [@DannyBen](https://github.com/DannyBen) for the report.
8
+
9
+ 0.18.0 (2020-01-28)
10
+ ===================
11
+
12
+ Huge release! Highlights are support for branch coverage (Ruby 2.5+) and dropping support for EOL'ed Ruby versions (< 2.4).
13
+ Please also read the other beta patch notes.
14
+
15
+ You can run with branch coverage by putting `enable_coverage :branch` into your SimpleCov configuration (like the `SimpleCov.start do .. end` block)
16
+
17
+ ## Enhancements
18
+ * You can now define the minimum expected coverage by criterion like `minimum_coverage line: 90, branch: 80`
19
+ * Memoized some internal data structures that didn't change to reduce SimpleCov overhead
20
+ * Both `FileList` and `SourceFile` now have a `coverage` method that returns a hash that points from a coverage criterion to a `CoverageStatistics` object for uniform access to overall coverage statistics for both line and branch coverage
21
+
22
+ ## Bugfixes
23
+ * we were losing precision by rounding the covered strength early, that has been removed. **For Formatters** this also means that you may need to round it yourself now.
24
+ * Removed an inconsistency in how we treat skipped vs. irrelevant lines (see [#565](https://github.com/colszowka/simplecov/issues/565)) - SimpleCov's definition of 100% is now "You covered everything that you could" so if coverage is 0/0 that's counted as a 100% no matter if the lines were irrelevant or ignored/skipped
25
+
26
+ ## Noteworthy
27
+ * `FileList` stopped inheriting from Array, it includes Enumerable so if you didn't use Array specific methods on it in formatters you should be fine
28
+
1
29
  0.18.0.beta3 (2020-01-20)
2
30
  ========================
3
31
 
data/README.md CHANGED
@@ -1,5 +1,6 @@
1
- SimpleCov [![Gem Version](https://badge.fury.io/rb/simplecov.svg)](https://badge.fury.io/rb/simplecov) [![Build Status](https://travis-ci.org/colszowka/simplecov.svg?branch=master)][Continuous Integration] [![Code Climate](https://codeclimate.com/github/colszowka/simplecov.svg)](https://codeclimate.com/github/colszowka/simplecov) [![Inline docs](http://inch-ci.org/github/colszowka/simplecov.svg)](http://inch-ci.org/github/colszowka/simplecov)
1
+ SimpleCov [![Gem Version](https://badge.fury.io/rb/simplecov.svg)](https://badge.fury.io/rb/simplecov) [![Build Status](https://github.com/colszowka/simplecov/workflows/stable/badge.svg?branch=master)][Continuous Integration] [![Code Climate](https://codeclimate.com/github/colszowka/simplecov.svg)](https://codeclimate.com/github/colszowka/simplecov) [![Inline docs](http://inch-ci.org/github/colszowka/simplecov.svg)](http://inch-ci.org/github/colszowka/simplecov)
2
2
  =========
3
+
3
4
  **Code coverage for Ruby**
4
5
 
5
6
  * [Source Code]
@@ -14,7 +15,7 @@ SimpleCov [![Gem Version](https://badge.fury.io/rb/simplecov.svg)](https://badge
14
15
  [Configuration]: http://rubydoc.info/gems/simplecov/SimpleCov/Configuration "Configuration options API documentation"
15
16
  [Changelog]: https://github.com/colszowka/simplecov/blob/master/CHANGELOG.md "Project Changelog"
16
17
  [Rubygem]: http://rubygems.org/gems/simplecov "SimpleCov @ rubygems.org"
17
- [Continuous Integration]: http://travis-ci.org/colszowka/simplecov "SimpleCov is built around the clock by travis-ci.org"
18
+ [Continuous Integration]: https://github.com/colszowka/simplecov/actions?query=workflow%3Astable "SimpleCov is built around the clock by github.com"
18
19
  [Dependencies]: https://gemnasium.com/colszowka/simplecov "SimpleCov dependencies on Gemnasium"
19
20
  [simplecov-html]: https://github.com/colszowka/simplecov-html "SimpleCov HTML Formatter Source Code @ GitHub"
20
21
 
@@ -725,6 +726,10 @@ You can define the minimum coverage percentage expected. SimpleCov will return n
725
726
 
726
727
  ```ruby
727
728
  SimpleCov.minimum_coverage 90
729
+ # same as above (the default is to check line coverage)
730
+ SimpleCov.minimum_coverage line: 90
731
+ # check for a minimum line coverage of 90% and minimum 80% branch coverage
732
+ SimpleCov.minimum_coverage line: 90, branch: 80
728
733
  ```
729
734
 
730
735
  ### Minimum coverage by file
@@ -736,6 +741,8 @@ to help ensure coverage is relatively consistent, rather than being skewed by pa
736
741
  SimpleCov.minimum_coverage_by_file 80
737
742
  ```
738
743
 
744
+ (not yet supported for branch coverage)
745
+
739
746
  ### Maximum coverage drop
740
747
 
741
748
  You can define the maximum coverage drop percentage at once. SimpleCov will return non-zero if exceeded.
@@ -744,6 +751,8 @@ You can define the maximum coverage drop percentage at once. SimpleCov will retu
744
751
  SimpleCov.maximum_coverage_drop 5
745
752
  ```
746
753
 
754
+ (not yet supported for branch coverage)
755
+
747
756
  ### Refuse dropping coverage
748
757
 
749
758
  You can also entirely refuse dropping coverage between test runs:
@@ -752,6 +761,8 @@ You can also entirely refuse dropping coverage between test runs:
752
761
  SimpleCov.refuse_coverage_drop
753
762
  ```
754
763
 
764
+ (not yet supported for branch coverage)
765
+
755
766
  ## Using your own formatter
756
767
 
757
768
  You can use your own formatter with:
@@ -240,8 +240,15 @@ module SimpleCov
240
240
  # Default is 0% (disabled)
241
241
  #
242
242
  def minimum_coverage(coverage = nil)
243
- minimum_possible_coverage_exceeded("minimum_coverage") if coverage && coverage > 100
244
- @minimum_coverage ||= (coverage || 0).to_f.round(2)
243
+ return @minimum_coverage ||= {} unless coverage
244
+
245
+ coverage = {DEFAULT_COVERAGE_CRITERION => coverage} if coverage.is_a?(Numeric)
246
+ coverage.keys.each { |criterion| raise_if_criterion_disabled(criterion) }
247
+ coverage.values.each do |percent|
248
+ minimum_possible_coverage_exceeded("minimum_coverage") if percent && percent > 100
249
+ end
250
+
251
+ @minimum_coverage = coverage
245
252
  end
246
253
 
247
254
  #
@@ -362,12 +369,21 @@ module SimpleCov
362
369
 
363
370
  private
364
371
 
365
- def raise_if_criterion_unsupported(criterion)
366
- raise_criterion_unsupported(criterion) unless SUPPORTED_COVERAGE_CRITERIA.member?(criterion)
372
+ def raise_if_criterion_disabled(criterion)
373
+ raise_if_criterion_unsupported(criterion)
374
+ # rubocop:disable Style/IfUnlessModifier
375
+ unless coverage_criterion_enabled?(criterion)
376
+ raise "Coverage criterion #{criterion}, is disabled! Please enable it first through enable_coverage #{criterion} (if supported)"
377
+ end
378
+ # rubocop:enable Style/IfUnlessModifier
367
379
  end
368
380
 
369
- def raise_criterion_unsupported(criterion)
370
- raise "Unsupported coverage criterion #{criterion}, supported values are #{SUPPORTED_COVERAGE_CRITERIA}"
381
+ def raise_if_criterion_unsupported(criterion)
382
+ # rubocop:disable Style/IfUnlessModifier
383
+ unless SUPPORTED_COVERAGE_CRITERIA.member?(criterion)
384
+ raise "Unsupported coverage criterion #{criterion}, supported values are #{SUPPORTED_COVERAGE_CRITERIA}"
385
+ end
386
+ # rubocop:enable Style/IfUnlessModifier
371
387
  end
372
388
 
373
389
  def minimum_possible_coverage_exceeded(coverage_option)
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleCov
4
+ # Holds the individual data of a coverage result.
5
+ #
6
+ # This is uniform across coverage criteria as they all have:
7
+ #
8
+ # * total - how many things to cover there are (total relevant loc/branches)
9
+ # * covered - how many of the coverables are hit
10
+ # * missed - how many of the coverables are missed
11
+ # * percent - percentage as covered/missed
12
+ # * strength - average hits per/coverable (will not exist for one shot lines format)
13
+ class CoverageStatistics
14
+ attr_reader :total, :covered, :missed, :strength, :percent
15
+
16
+ def self.from(coverage_statistics)
17
+ sum_covered, sum_missed, sum_total_strength =
18
+ coverage_statistics.reduce([0, 0, 0.0]) do |(covered, missed, total_strength), file_coverage_statistics|
19
+ [
20
+ covered + file_coverage_statistics.covered,
21
+ missed + file_coverage_statistics.missed,
22
+ # gotta remultiply with loc because files have different strenght and loc
23
+ # giving them a different "weight" in total
24
+ total_strength + (file_coverage_statistics.strength * file_coverage_statistics.total)
25
+ ]
26
+ end
27
+
28
+ new(covered: sum_covered, missed: sum_missed, total_strength: sum_total_strength)
29
+ end
30
+
31
+ # Requires only covered, missed and strength to be initialized.
32
+ #
33
+ # Other values are computed by this class.
34
+ def initialize(covered:, missed:, total_strength: 0.0)
35
+ @covered = covered
36
+ @missed = missed
37
+ @total = covered + missed
38
+ @percent = compute_percent(covered, total)
39
+ @strength = compute_strength(total_strength, @total)
40
+ end
41
+
42
+ private
43
+
44
+ def compute_percent(covered, total)
45
+ return 100.0 if total.zero?
46
+
47
+ covered * 100.0 / total
48
+ end
49
+
50
+ def compute_strength(total_strength, total)
51
+ return 0.0 if total.zero?
52
+
53
+ total_strength.to_f / total
54
+ end
55
+ end
56
+ end
@@ -3,19 +3,38 @@
3
3
  module SimpleCov
4
4
  # An array of SimpleCov SourceFile instances with additional collection helper
5
5
  # methods for calculating coverage across them etc.
6
- class FileList < Array
6
+ class FileList
7
+ include Enumerable
8
+ extend Forwardable
9
+
10
+ def_delegators :@files,
11
+ # For Enumerable
12
+ :each,
13
+ # also delegating methods implemented in Enumerable as they have
14
+ # custom Array implementations which are presumably better/more
15
+ # resource efficient
16
+ :size, :map, :count,
17
+ # surprisingly not in Enumerable
18
+ :empty?, :length,
19
+ # still act like we're kinda an array
20
+ :to_a, :to_ary
21
+
22
+ def initialize(files)
23
+ @files = files
24
+ end
25
+
26
+ def coverage_statistics
27
+ @coverage_statistics ||= compute_coverage_statistics
28
+ end
29
+
7
30
  # Returns the count of lines that have coverage
8
31
  def covered_lines
9
- return 0.0 if empty?
10
-
11
- map { |f| f.covered_lines.count }.inject(:+)
32
+ coverage_statistics[:line]&.covered
12
33
  end
13
34
 
14
35
  # Returns the count of lines that have been missed
15
36
  def missed_lines
16
- return 0.0 if empty?
17
-
18
- map { |f| f.missed_lines.count }.inject(:+)
37
+ coverage_statistics[:line]&.missed
19
38
  end
20
39
 
21
40
  # Returns the count of lines that are not relevant for coverage
@@ -45,44 +64,51 @@ module SimpleCov
45
64
 
46
65
  # Returns the overall amount of relevant lines of code across all files in this list
47
66
  def lines_of_code
48
- covered_lines + missed_lines
67
+ coverage_statistics[:line]&.total
49
68
  end
50
69
 
51
70
  # Computes the coverage based upon lines covered and lines missed
52
71
  # @return [Float]
53
72
  def covered_percent
54
- return 100.0 if empty? || lines_of_code.zero?
55
-
56
- Float(covered_lines * 100.0 / lines_of_code)
73
+ coverage_statistics[:line]&.percent
57
74
  end
58
75
 
59
76
  # Computes the strength (hits / line) based upon lines covered and lines missed
60
77
  # @return [Float]
61
78
  def covered_strength
62
- return 0.0 if empty? || lines_of_code.zero?
63
-
64
- Float(map { |f| f.covered_strength * f.lines_of_code }.inject(:+) / lines_of_code)
79
+ coverage_statistics[:line]&.strength
65
80
  end
66
81
 
67
82
  # Return total count of branches in all files
68
83
  def total_branches
69
- return 0 if empty?
70
-
71
- map { |file| file.total_branches.count }.inject(:+)
84
+ coverage_statistics[:branch]&.total
72
85
  end
73
86
 
74
87
  # Return total count of covered branches
75
88
  def covered_branches
76
- return 0 if empty?
77
-
78
- map { |file| file.covered_branches.count }.inject(:+)
89
+ coverage_statistics[:branch]&.covered
79
90
  end
80
91
 
81
92
  # Return total count of covered branches
82
93
  def missed_branches
83
- return 0 if empty?
94
+ coverage_statistics[:branch]&.missed
95
+ end
96
+
97
+ def branch_covered_percent
98
+ coverage_statistics[:branch]&.percent
99
+ end
100
+
101
+ private
102
+
103
+ def compute_coverage_statistics
104
+ total_coverage_statistics = @files.each_with_object(line: [], branch: []) do |file, together|
105
+ together[:line] << file.coverage_statistics[:line]
106
+ together[:branch] << file.coverage_statistics[:branch] if SimpleCov.branch_coverage?
107
+ end
84
108
 
85
- map { |file| file.missed_branches.count }.inject(:+)
109
+ coverage_statistics = {line: CoverageStatistics.from(total_coverage_statistics[:line])}
110
+ coverage_statistics[:branch] = CoverageStatistics.from(total_coverage_statistics[:branch]) if SimpleCov.branch_coverage?
111
+ coverage_statistics
86
112
  end
87
113
  end
88
114
  end
@@ -20,7 +20,7 @@ module SimpleCov
20
20
  # Explicitly set the command name that was used for this coverage result. Defaults to SimpleCov.command_name
21
21
  attr_writer :command_name
22
22
 
23
- def_delegators :files, :covered_percent, :covered_percentages, :least_covered_file, :covered_strength, :covered_lines, :missed_lines, :total_branches, :covered_branches, :missed_branches
23
+ def_delegators :files, :covered_percent, :covered_percentages, :least_covered_file, :covered_strength, :covered_lines, :missed_lines, :total_branches, :covered_branches, :missed_branches, :coverage_statistics
24
24
  def_delegator :files, :lines_of_code, :total_lines
25
25
 
26
26
  # Initialize a new SimpleCov::Result from given Coverage.result (a Hash of filenames each containing an array of
@@ -9,11 +9,11 @@ module SimpleCov
9
9
  # The full path to this source file (e.g. /User/colszowka/projects/simplecov/lib/simplecov/source_file.rb)
10
10
  attr_reader :filename
11
11
  # The array of coverage data received from the Coverage.result
12
- attr_reader :coverage
12
+ attr_reader :coverage_data
13
13
 
14
- def initialize(filename, coverage)
14
+ def initialize(filename, coverage_data)
15
15
  @filename = filename
16
- @coverage = coverage
16
+ @coverage_data = coverage_data
17
17
  end
18
18
 
19
19
  # The path to this source file relative to the projects directory
@@ -29,6 +29,14 @@ module SimpleCov
29
29
  end
30
30
  alias source src
31
31
 
32
+ def coverage_statistics
33
+ @coverage_statistics ||=
34
+ {
35
+ **line_coverage_statistics,
36
+ **branch_coverage_statistics
37
+ }
38
+ end
39
+
32
40
  # Returns all source lines for this file as instances of SimpleCov::SourceFile::Line,
33
41
  # and thus including coverage data. Aliased as :source_lines
34
42
  def lines
@@ -60,44 +68,7 @@ module SimpleCov
60
68
 
61
69
  # Returns the number of relevant lines (covered + missed)
62
70
  def lines_of_code
63
- covered_lines.size + missed_lines.size
64
- end
65
-
66
- def build_lines
67
- coverage_exceeding_source_warn if coverage["lines"].size > src.size
68
- lines = src.map.with_index(1) do |src, i|
69
- SimpleCov::SourceFile::Line.new(src, i, coverage["lines"][i - 1])
70
- end
71
- process_skipped_lines(lines)
72
- end
73
-
74
- # no_cov_chunks is zero indexed to work directly with the array holding the lines
75
- def no_cov_chunks
76
- @no_cov_chunks ||= build_no_cov_chunks
77
- end
78
-
79
- def build_no_cov_chunks
80
- no_cov_lines = src.map.with_index(1).select { |line, _index| LinesClassifier.no_cov_line?(line) }
81
-
82
- warn "uneven number of nocov comments detected" if no_cov_lines.size.odd?
83
-
84
- no_cov_lines.each_slice(2).map do |(_line_start, index_start), (_line_end, index_end)|
85
- index_start..index_end
86
- end
87
- end
88
-
89
- def process_skipped_lines(lines)
90
- # the array the lines are kept in is 0-based whereas the line numbers in the nocov
91
- # chunks are 1-based and are expected to be like this in other parts (and it's also
92
- # arguably more understandable)
93
- no_cov_chunks.each { |chunk| lines[(chunk.begin - 1)..(chunk.end - 1)].each(&:skipped!) }
94
-
95
- lines
96
- end
97
-
98
- # Warning to identify condition from Issue #56
99
- def coverage_exceeding_source_warn
100
- warn "Warning: coverage data provided by Coverage [#{coverage['lines'].size}] exceeds number of lines in #{filename} [#{src.size}]"
71
+ coverage_statistics[:line]&.total
101
72
  end
102
73
 
103
74
  # Access SimpleCov::SourceFile::Line source lines by line number
@@ -107,27 +78,17 @@ module SimpleCov
107
78
 
108
79
  # The coverage for this file in percent. 0 if the file has no coverage lines
109
80
  def covered_percent
110
- return 100.0 if no_lines?
111
-
112
- return 0.0 if relevant_lines.zero?
113
-
114
- Float(covered_lines.size * 100.0 / relevant_lines.to_f)
81
+ coverage_statistics[:line]&.percent
115
82
  end
116
83
 
117
84
  def covered_strength
118
- return 0.0 if relevant_lines.zero?
119
-
120
- (lines_strength / relevant_lines.to_f).round(1)
85
+ coverage_statistics[:line]&.strength
121
86
  end
122
87
 
123
88
  def no_lines?
124
89
  lines.length.zero? || (lines.length == never_lines.size)
125
90
  end
126
91
 
127
- def lines_strength
128
- lines.map(&:coverage).compact.reduce(:+)
129
- end
130
-
131
92
  def relevant_lines
132
93
  lines.size - never_lines.size - skipped_lines.size
133
94
  end
@@ -143,16 +104,13 @@ module SimpleCov
143
104
  end
144
105
 
145
106
  def branches_coverage_percent
146
- return 100.0 if no_branches?
147
- return 0.0 if covered_branches.empty?
148
-
149
- Float(covered_branches.size * 100.0 / total_branches.size.to_f)
107
+ coverage_statistics[:branch]&.percent
150
108
  end
151
109
 
152
110
  #
153
111
  # Return the relevant branches to source file
154
112
  def total_branches
155
- covered_branches + missed_branches
113
+ @total_branches ||= covered_branches + missed_branches
156
114
  end
157
115
 
158
116
  #
@@ -161,14 +119,108 @@ module SimpleCov
161
119
  @branches_report ||= build_branches_report
162
120
  end
163
121
 
164
- ## Related to source file branches statistics
122
+ #
123
+ # Select the covered branches
124
+ # Here we user tree schema because some conditions like case may have additional
125
+ # else that is not in declared inside the code but given by default by coverage report
126
+ #
127
+ # @return [Array]
128
+ #
129
+ def covered_branches
130
+ @covered_branches ||= branches.select(&:covered?)
131
+ end
132
+
133
+ #
134
+ # Select the missed branches with coverage equal to zero
135
+ #
136
+ # @return [Array]
137
+ #
138
+ def missed_branches
139
+ @missed_branches ||= branches.select(&:missed?)
140
+ end
141
+
142
+ def branches_for_line(line_number)
143
+ branches_report.fetch(line_number, [])
144
+ end
145
+
146
+ #
147
+ # Check if any branches missing on given line number
148
+ #
149
+ # @param [Integer] line_number
150
+ #
151
+ # @return [Boolean]
152
+ #
153
+ def line_with_missed_branch?(line_number)
154
+ branches_for_line(line_number).select { |_type, count| count.zero? }.any?
155
+ end
156
+
157
+ private
158
+
159
+ # no_cov_chunks is zero indexed to work directly with the array holding the lines
160
+ def no_cov_chunks
161
+ @no_cov_chunks ||= build_no_cov_chunks
162
+ end
163
+
164
+ def build_no_cov_chunks
165
+ no_cov_lines = src.map.with_index(1).select { |line_src, _index| LinesClassifier.no_cov_line?(line_src) }
166
+
167
+ # if we have an uneven number of nocovs we assume they go to the
168
+ # end of the file, the source doesn't really matter
169
+ # Can't deal with this within the each_slice due to differing
170
+ # behavior in JRuby: jruby/jruby#6048
171
+ no_cov_lines << ["", src.size] if no_cov_lines.size.odd?
172
+
173
+ no_cov_lines.each_slice(2).map do |(_line_src_start, index_start), (_line_src_end, index_end)|
174
+ index_start..index_end
175
+ end
176
+ end
177
+
178
+ def build_lines
179
+ coverage_exceeding_source_warn if coverage_data["lines"].size > src.size
180
+ lines = src.map.with_index(1) do |src, i|
181
+ SimpleCov::SourceFile::Line.new(src, i, coverage_data["lines"][i - 1])
182
+ end
183
+ process_skipped_lines(lines)
184
+ end
185
+
186
+ def process_skipped_lines(lines)
187
+ # the array the lines are kept in is 0-based whereas the line numbers in the nocov
188
+ # chunks are 1-based and are expected to be like this in other parts (and it's also
189
+ # arguably more understandable)
190
+ no_cov_chunks.each { |chunk| lines[(chunk.begin - 1)..(chunk.end - 1)].each(&:skipped!) }
191
+
192
+ lines
193
+ end
194
+
195
+ def lines_strength
196
+ lines.map(&:coverage).compact.reduce(:+)
197
+ end
198
+
199
+ # Warning to identify condition from Issue #56
200
+ def coverage_exceeding_source_warn
201
+ warn "Warning: coverage data provided by Coverage [#{coverage_data['lines'].size}] exceeds number of lines in #{filename} [#{src.size}]"
202
+ end
203
+
204
+ #
205
+ # Build full branches report
206
+ # Root branches represent the wrapper of all condition state that
207
+ # have inside the branches
208
+ #
209
+ # @return [Hash]
210
+ #
211
+ def build_branches_report
212
+ branches.reject(&:skipped?).each_with_object({}) do |branch, coverage_statistics|
213
+ coverage_statistics[branch.report_line] ||= []
214
+ coverage_statistics[branch.report_line] << branch.report
215
+ end
216
+ end
165
217
 
166
218
  #
167
219
  # Call recursive method that transform our static hash to array of objects
168
220
  # @return [Array]
169
221
  #
170
222
  def build_branches
171
- coverage_branch_data = coverage.fetch("branches", {})
223
+ coverage_branch_data = coverage_data.fetch("branches", {})
172
224
  branches = coverage_branch_data.flat_map do |condition, coverage_branches|
173
225
  build_branches_from(condition, coverage_branches)
174
226
  end
@@ -230,53 +282,23 @@ module SimpleCov
230
282
  )
231
283
  end
232
284
 
233
- #
234
- # Select the covered branches
235
- # Here we user tree schema because some conditions like case may have additional
236
- # else that is not in declared inside the code but given by default by coverage report
237
- #
238
- # @return [Array]
239
- #
240
- def covered_branches
241
- @covered_branches ||= branches.select(&:covered?)
242
- end
243
-
244
- #
245
- # Select the missed branches with coverage equal to zero
246
- #
247
- # @return [Array]
248
- #
249
- def missed_branches
250
- @missed_branches ||= branches.select(&:missed?)
251
- end
252
-
253
- def branches_for_line(line_number)
254
- branches_report.fetch(line_number, [])
255
- end
256
-
257
- #
258
- # Check if any branches missing on given line number
259
- #
260
- # @param [Integer] line_number
261
- #
262
- # @return [Boolean]
263
- #
264
- def line_with_missed_branch?(line_number)
265
- branches_for_line(line_number).select { |_type, count| count.zero? }.any?
285
+ def line_coverage_statistics
286
+ {
287
+ line: CoverageStatistics.new(
288
+ total_strength: lines_strength,
289
+ covered: covered_lines.size,
290
+ missed: missed_lines.size
291
+ )
292
+ }
266
293
  end
267
294
 
268
- #
269
- # Build full branches report
270
- # Root branches represent the wrapper of all condition state that
271
- # have inside the branches
272
- #
273
- # @return [Hash]
274
- #
275
- def build_branches_report
276
- branches.reject(&:skipped?).each_with_object({}) do |branch, coverage_statistics|
277
- coverage_statistics[branch.report_line] ||= []
278
- coverage_statistics[branch.report_line] << branch.report
279
- end
295
+ def branch_coverage_statistics
296
+ {
297
+ branch: CoverageStatistics.new(
298
+ covered: covered_branches.size,
299
+ missed: missed_branches.size
300
+ )
301
+ }
280
302
  end
281
303
  end
282
304
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SimpleCov
4
- VERSION = "0.18.0.beta3"
4
+ VERSION = "0.18.1"
5
5
  end
data/lib/simplecov.rb CHANGED
@@ -231,12 +231,8 @@ module SimpleCov
231
231
  # rubocop:disable Metrics/MethodLength
232
232
  def result_exit_status(result, covered_percent)
233
233
  covered_percentages = result.covered_percentages.map { |percentage| percentage.floor(2) }
234
- if covered_percent < SimpleCov.minimum_coverage
235
- $stderr.printf(
236
- "Coverage (%<covered>.2f%%) is below the expected minimum coverage (%<minimum_coverage>.2f%%).\n",
237
- covered: covered_percent,
238
- minimum_coverage: SimpleCov.minimum_coverage
239
- )
234
+ if (minimum_violations = minimum_coverage_violated(result)).any?
235
+ report_minimum_violated(minimum_violations)
240
236
  SimpleCov::ExitCodes::MINIMUM_COVERAGE
241
237
  elsif covered_percentages.any? { |p| p < SimpleCov.minimum_coverage_by_file }
242
238
  $stderr.printf(
@@ -409,13 +405,40 @@ module SimpleCov
409
405
  def result_with_not_loaded_files
410
406
  @result = SimpleCov::Result.new(add_not_loaded_files(@result))
411
407
  end
408
+
409
+ def minimum_coverage_violated(result)
410
+ coverage_achieved = minimum_coverage.map do |criterion, percent|
411
+ {
412
+ criterion: criterion,
413
+ minimum_expected: percent,
414
+ actual: result.coverage_statistics[criterion].percent
415
+ }
416
+ end
417
+
418
+ coverage_achieved.select do |achieved|
419
+ achieved.fetch(:actual) < achieved.fetch(:minimum_expected)
420
+ end
421
+ end
422
+
423
+ def report_minimum_violated(violations)
424
+ violations.each do |violation|
425
+ $stderr.printf(
426
+ "%<criterion>s coverage (%<covered>.2f%%) is below the expected minimum coverage (%<minimum_coverage>.2f%%).\n",
427
+ covered: violation.fetch(:actual).floor(2),
428
+ minimum_coverage: violation.fetch(:minimum_expected),
429
+ criterion: violation.fetch(:criterion).capitalize
430
+ )
431
+ end
432
+ end
412
433
  end
413
434
  end
414
435
 
415
436
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__)))
416
437
  require "set"
438
+ require "forwardable"
417
439
  require "simplecov/configuration"
418
440
  SimpleCov.extend SimpleCov::Configuration
441
+ require "simplecov/coverage_statistics"
419
442
  require "simplecov/exit_codes"
420
443
  require "simplecov/profiles"
421
444
  require "simplecov/source_file/line"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simplecov
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.18.0.beta3
4
+ version: 0.18.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Christoph Olszowka
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-01-20 00:00:00.000000000 Z
11
+ date: 2020-01-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: docile
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 0.11.0.beta2
33
+ version: 0.11.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 0.11.0.beta2
40
+ version: 0.11.0
41
41
  description: Code coverage for Ruby with a powerful configuration library and automatic
42
42
  merging of coverage across test suites
43
43
  email:
@@ -63,6 +63,7 @@ files:
63
63
  - lib/simplecov/combine/results_combiner.rb
64
64
  - lib/simplecov/command_guesser.rb
65
65
  - lib/simplecov/configuration.rb
66
+ - lib/simplecov/coverage_statistics.rb
66
67
  - lib/simplecov/defaults.rb
67
68
  - lib/simplecov/exit_codes.rb
68
69
  - lib/simplecov/file_list.rb
@@ -95,9 +96,9 @@ licenses:
95
96
  metadata:
96
97
  bug_tracker_uri: https://github.com/colszowka/simplecov/issues
97
98
  changelog_uri: https://github.com/colszowka/simplecov/blob/master/CHANGELOG.md
98
- documentation_uri: https://www.rubydoc.info/gems/simplecov/0.18.0.beta3
99
+ documentation_uri: https://www.rubydoc.info/gems/simplecov/0.18.1
99
100
  mailing_list_uri: https://groups.google.com/forum/#!forum/simplecov
100
- source_code_uri: https://github.com/colszowka/simplecov/tree/v0.18.0.beta3
101
+ source_code_uri: https://github.com/colszowka/simplecov/tree/v0.18.1
101
102
  post_install_message:
102
103
  rdoc_options: []
103
104
  require_paths:
@@ -109,9 +110,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
109
110
  version: 2.4.0
110
111
  required_rubygems_version: !ruby/object:Gem::Requirement
111
112
  requirements:
112
- - - ">"
113
+ - - ">="
113
114
  - !ruby/object:Gem::Version
114
- version: 1.3.1
115
+ version: '0'
115
116
  requirements: []
116
117
  rubygems_version: 3.1.2
117
118
  signing_key: