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 +4 -4
- data/CHANGELOG.md +28 -0
- data/README.md +13 -2
- data/lib/simplecov/configuration.rb +22 -6
- data/lib/simplecov/coverage_statistics.rb +56 -0
- data/lib/simplecov/file_list.rb +48 -22
- data/lib/simplecov/result.rb +1 -1
- data/lib/simplecov/source_file.rb +127 -105
- data/lib/simplecov/version.rb +1 -1
- data/lib/simplecov.rb +29 -6
- metadata +9 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6139c9ec905e1f20dec5bdfd8fef48fe8b9091ce24ae1b55e89b850093cfcf69
|
|
4
|
+
data.tar.gz: 3e15d4dbbc0f68b9453241603c0a1beade1b29ca2fbf64190f742f69db307b13
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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 [](https://badge.fury.io/rb/simplecov) [](https://badge.fury.io/rb/simplecov) [][Continuous Integration] [](https://codeclimate.com/github/colszowka/simplecov) [](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 [](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]:
|
|
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
|
-
|
|
244
|
-
|
|
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
|
|
366
|
-
|
|
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
|
|
370
|
-
|
|
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
|
data/lib/simplecov/file_list.rb
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
data/lib/simplecov/result.rb
CHANGED
|
@@ -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 :
|
|
12
|
+
attr_reader :coverage_data
|
|
13
13
|
|
|
14
|
-
def initialize(filename,
|
|
14
|
+
def initialize(filename, coverage_data)
|
|
15
15
|
@filename = filename
|
|
16
|
-
@
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
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
|
data/lib/simplecov/version.rb
CHANGED
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
|
|
235
|
-
|
|
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.
|
|
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-
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
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:
|
|
115
|
+
version: '0'
|
|
115
116
|
requirements: []
|
|
116
117
|
rubygems_version: 3.1.2
|
|
117
118
|
signing_key:
|