simplecov 0.18.0.beta2 → 0.18.0
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 +29 -2
- data/README.md +3 -2
- data/lib/simplecov/combine/results_combiner.rb +0 -40
- 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 +29 -3
- data/lib/simplecov/source_file.rb +123 -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: ce12d99507e5e8bd75399d80855c495907b8dda937fb5e2a33d2895e9f74e370
|
|
4
|
+
data.tar.gz: ae53aefe53c30c3b1d6c1c0b70aa34c1193642d4a879f77a0c3522960fc92773
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e9a60e36936b9b5018a6998ade1742373bfa67b96688411ab42c90ac82ca6280abe9d407dcef4d45398ed28803ca876d6f82ce0cabc878d6141897cca4dab91e
|
|
7
|
+
data.tar.gz: 7056045c2385516ce0029af14558bd95c9c84bc85319d82bddfcf2011b7db87a5291168bb739c1ff02d8830e133065b3f2fda605e03b7c2e6ed094b61bff9240
|
data/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,37 @@
|
|
|
1
|
-
0.18.0
|
|
1
|
+
0.18.0 (2020-01-28)
|
|
2
|
+
===================
|
|
3
|
+
|
|
4
|
+
Huge release! Highlights are support for branch coverage (Ruby 2.5+) and dropping support for EOL'ed Ruby versions (< 2.4).
|
|
5
|
+
Please also read the other beta patch notes.
|
|
6
|
+
|
|
7
|
+
## Enhancements
|
|
8
|
+
* You can now define the minimum expected coverage by criterion like `minimum_coverage line: 90, branch: 80`
|
|
9
|
+
* Memoized some internal data structures that didn't change to reduce SimpleCov overhead
|
|
10
|
+
* 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
|
|
11
|
+
|
|
12
|
+
## Bugfixes
|
|
13
|
+
* 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.
|
|
14
|
+
* 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
|
|
15
|
+
|
|
16
|
+
## Noteworthy
|
|
17
|
+
* `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
|
|
18
|
+
|
|
19
|
+
0.18.0.beta3 (2020-01-20)
|
|
20
|
+
========================
|
|
21
|
+
|
|
22
|
+
## Enhancements
|
|
23
|
+
* Instead of ignoring old `.resultset.json`s that are inside the merge timeout, adapt and respect them
|
|
24
|
+
|
|
25
|
+
## Bugfixes
|
|
26
|
+
* Remove the constant warning printing if you still have a `.resultset.json` in pre 0.18 layout that is within your merge timeout
|
|
27
|
+
|
|
28
|
+
0.18.0.beta2 (2020-01-19)
|
|
2
29
|
===================
|
|
3
30
|
|
|
4
31
|
## Enhancements
|
|
5
32
|
* only turn on the requested coverage criteria (when activating branch coverage before SimpleCov would also instruct Ruby to take Method coverage)
|
|
6
33
|
* Change how branch coverage is displayed, now it's `branch_type: hit_count` which should be more self explanatory. See [#830](https://github.com/colszowka/simplecov/pull/830) for an example and feel free to give feedback!
|
|
7
|
-
* Allow early running exit tasks and avoid the `at_exit` hook through the `SimpleCov.run_exit_tasks!` method. (thanks [@macumber]
|
|
34
|
+
* Allow early running exit tasks and avoid the `at_exit` hook through the `SimpleCov.run_exit_tasks!` method. (thanks [@macumber](https://github.com/macumber))
|
|
8
35
|
* Allow manual collation of result sets through the `SimpleCov.collate` entrypoint. See the README for more details (thanks [@ticky](https://github.com/ticky))
|
|
9
36
|
* Within `case`, even if there is no `else` branch declared show missing coverage for it (aka no branch of it). See [#825](https://github.com/colszowka/simplecov/pull/825)
|
|
10
37
|
* Stop symbolizing all keys when loading cache (should lead to be faster and consume less memory)
|
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
|
|
|
@@ -34,11 +34,6 @@ module SimpleCov
|
|
|
34
34
|
# @return [Hash]
|
|
35
35
|
#
|
|
36
36
|
def combine_result_sets(combined_results, result)
|
|
37
|
-
unless correct_format?(result)
|
|
38
|
-
warn_wrong_format
|
|
39
|
-
return combined_results
|
|
40
|
-
end
|
|
41
|
-
|
|
42
37
|
results_files = combined_results.keys | result.keys
|
|
43
38
|
|
|
44
39
|
results_files.each_with_object({}) do |file_name, file_combination|
|
|
@@ -49,41 +44,6 @@ module SimpleCov
|
|
|
49
44
|
end
|
|
50
45
|
end
|
|
51
46
|
|
|
52
|
-
# We might start a run of a new simplecov version with a new format stored while
|
|
53
|
-
# there is still a recent file like this lying around. If it's recent enough (
|
|
54
|
-
# see merge_timeout) it will end up here. In order not to crash against this
|
|
55
|
-
# we need to do some basic checking of the format of data we expect and
|
|
56
|
-
# otherwise ignore it. See #820
|
|
57
|
-
#
|
|
58
|
-
# Currently correct format is:
|
|
59
|
-
# { file_path_string => {coverage_criterion => coverage_date}}
|
|
60
|
-
#
|
|
61
|
-
# Internal use/reliance only.
|
|
62
|
-
def correct_format?(result)
|
|
63
|
-
result.empty? || matches_current_format?(result)
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def matches_current_format?(result)
|
|
67
|
-
# I so wish I could already use pattern matching
|
|
68
|
-
key, data = result.first
|
|
69
|
-
|
|
70
|
-
key.is_a?(String) && second_level_choice_of_criterion?(data)
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
SECOND_LEVEL_KEYS = %w[lines branches].freeze
|
|
74
|
-
def second_level_choice_of_criterion?(data)
|
|
75
|
-
second_level_key, = data.first
|
|
76
|
-
|
|
77
|
-
SECOND_LEVEL_KEYS.member?(second_level_key)
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def warn_wrong_format
|
|
81
|
-
warn "Merging results, encountered an incorrectly formatted value. "\
|
|
82
|
-
"This value was ignored.\nIf you just upgraded simplecov this is "\
|
|
83
|
-
"likely due to a changed file format. If this happens again please "\
|
|
84
|
-
"file a bug. https://github.com/colszowka/simplecov/issues"
|
|
85
|
-
end
|
|
86
|
-
|
|
87
47
|
#
|
|
88
48
|
# Combine two files coverage results
|
|
89
49
|
#
|
|
@@ -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,14 +20,15 @@ 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
|
|
27
27
|
# coverage data)
|
|
28
28
|
def initialize(original_result)
|
|
29
|
-
|
|
30
|
-
@
|
|
29
|
+
result = adapt_result(original_result)
|
|
30
|
+
@original_result = result.freeze
|
|
31
|
+
@files = SimpleCov::FileList.new(result.map do |filename, coverage|
|
|
31
32
|
SimpleCov::SourceFile.new(filename, JSON.parse(JSON.dump(coverage))) if File.file?(filename)
|
|
32
33
|
end.compact.sort_by(&:filename))
|
|
33
34
|
filter!
|
|
@@ -82,6 +83,31 @@ module SimpleCov
|
|
|
82
83
|
|
|
83
84
|
private
|
|
84
85
|
|
|
86
|
+
# We changed the format of the raw result data in simplecov, as people are likely
|
|
87
|
+
# to have "old" resultsets lying around (but not too old so that they're still
|
|
88
|
+
# considered we can adapt them).
|
|
89
|
+
# See https://github.com/colszowka/simplecov/pull/824#issuecomment-576049747
|
|
90
|
+
def adapt_result(result)
|
|
91
|
+
if pre_simplecov_0_18_result?(result)
|
|
92
|
+
adapt_pre_simplecov_0_18_result(result)
|
|
93
|
+
else
|
|
94
|
+
result
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# pre 0.18 coverage data pointed from file directly to an array of line coverage
|
|
99
|
+
def pre_simplecov_0_18_result?(result)
|
|
100
|
+
_key, data = result.first
|
|
101
|
+
|
|
102
|
+
data.is_a?(Array)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def adapt_pre_simplecov_0_18_result(result)
|
|
106
|
+
result.map do |file_path, line_coverage_data|
|
|
107
|
+
[file_path, {"lines" => line_coverage_data}]
|
|
108
|
+
end.to_h
|
|
109
|
+
end
|
|
110
|
+
|
|
85
111
|
def coverage
|
|
86
112
|
keys = original_result.keys & filenames
|
|
87
113
|
Hash[keys.zip(original_result.values_at(*keys))]
|
|
@@ -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,104 @@ 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, _index| LinesClassifier.no_cov_line?(line) }
|
|
166
|
+
|
|
167
|
+
warn "uneven number of nocov comments detected" if no_cov_lines.size.odd?
|
|
168
|
+
|
|
169
|
+
no_cov_lines.each_slice(2).map do |(_line_start, index_start), (_line_end, index_end)|
|
|
170
|
+
index_start..index_end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def build_lines
|
|
175
|
+
coverage_exceeding_source_warn if coverage_data["lines"].size > src.size
|
|
176
|
+
lines = src.map.with_index(1) do |src, i|
|
|
177
|
+
SimpleCov::SourceFile::Line.new(src, i, coverage_data["lines"][i - 1])
|
|
178
|
+
end
|
|
179
|
+
process_skipped_lines(lines)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def process_skipped_lines(lines)
|
|
183
|
+
# the array the lines are kept in is 0-based whereas the line numbers in the nocov
|
|
184
|
+
# chunks are 1-based and are expected to be like this in other parts (and it's also
|
|
185
|
+
# arguably more understandable)
|
|
186
|
+
no_cov_chunks.each { |chunk| lines[(chunk.begin - 1)..(chunk.end - 1)].each(&:skipped!) }
|
|
187
|
+
|
|
188
|
+
lines
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def lines_strength
|
|
192
|
+
lines.map(&:coverage).compact.reduce(:+)
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Warning to identify condition from Issue #56
|
|
196
|
+
def coverage_exceeding_source_warn
|
|
197
|
+
warn "Warning: coverage data provided by Coverage [#{coverage_data['lines'].size}] exceeds number of lines in #{filename} [#{src.size}]"
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
#
|
|
201
|
+
# Build full branches report
|
|
202
|
+
# Root branches represent the wrapper of all condition state that
|
|
203
|
+
# have inside the branches
|
|
204
|
+
#
|
|
205
|
+
# @return [Hash]
|
|
206
|
+
#
|
|
207
|
+
def build_branches_report
|
|
208
|
+
branches.reject(&:skipped?).each_with_object({}) do |branch, coverage_statistics|
|
|
209
|
+
coverage_statistics[branch.report_line] ||= []
|
|
210
|
+
coverage_statistics[branch.report_line] << branch.report
|
|
211
|
+
end
|
|
212
|
+
end
|
|
165
213
|
|
|
166
214
|
#
|
|
167
215
|
# Call recursive method that transform our static hash to array of objects
|
|
168
216
|
# @return [Array]
|
|
169
217
|
#
|
|
170
218
|
def build_branches
|
|
171
|
-
coverage_branch_data =
|
|
219
|
+
coverage_branch_data = coverage_data.fetch("branches", {})
|
|
172
220
|
branches = coverage_branch_data.flat_map do |condition, coverage_branches|
|
|
173
221
|
build_branches_from(condition, coverage_branches)
|
|
174
222
|
end
|
|
@@ -230,53 +278,23 @@ module SimpleCov
|
|
|
230
278
|
)
|
|
231
279
|
end
|
|
232
280
|
|
|
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?
|
|
281
|
+
def line_coverage_statistics
|
|
282
|
+
{
|
|
283
|
+
line: CoverageStatistics.new(
|
|
284
|
+
total_strength: lines_strength,
|
|
285
|
+
covered: covered_lines.size,
|
|
286
|
+
missed: missed_lines.size
|
|
287
|
+
)
|
|
288
|
+
}
|
|
266
289
|
end
|
|
267
290
|
|
|
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
|
|
291
|
+
def branch_coverage_statistics
|
|
292
|
+
{
|
|
293
|
+
branch: CoverageStatistics.new(
|
|
294
|
+
covered: covered_branches.size,
|
|
295
|
+
missed: missed_branches.size
|
|
296
|
+
)
|
|
297
|
+
}
|
|
280
298
|
end
|
|
281
299
|
end
|
|
282
300
|
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.0
|
|
4
|
+
version: 0.18.0
|
|
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-28 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.0
|
|
99
|
+
documentation_uri: https://www.rubydoc.info/gems/simplecov/0.18.0
|
|
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
|
|
101
|
+
source_code_uri: https://github.com/colszowka/simplecov/tree/v0.18.0
|
|
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:
|