simplecov 0.19.0 → 0.21.2
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 +49 -506
- data/README.md +45 -9
- data/doc/alternate-formatters.md +5 -0
- data/lib/simplecov.rb +19 -16
- data/lib/simplecov/configuration.rb +44 -18
- data/lib/simplecov/default_formatter.rb +20 -0
- data/lib/simplecov/defaults.rb +5 -2
- data/lib/simplecov/exit_codes/maximum_coverage_drop_check.rb +44 -11
- data/lib/simplecov/exit_codes/minimum_coverage_by_file_check.rb +26 -10
- data/lib/simplecov/file_list.rb +12 -6
- data/lib/simplecov/lines_classifier.rb +2 -2
- data/lib/simplecov/result.rb +2 -31
- data/lib/simplecov/result_merger.rb +121 -52
- data/lib/simplecov/source_file/line.rb +1 -1
- data/lib/simplecov/version.rb +1 -1
- metadata +20 -8
- data/CODE_OF_CONDUCT.md +0 -76
- data/CONTRIBUTING.md +0 -51
- data/ISSUE_TEMPLATE.md +0 -23
@@ -9,16 +9,18 @@ module SimpleCov
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def failing?
|
12
|
-
|
12
|
+
minimum_violations.any?
|
13
13
|
end
|
14
14
|
|
15
15
|
def report
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
16
|
+
minimum_violations.each do |violation|
|
17
|
+
$stderr.printf(
|
18
|
+
"%<criterion>s coverage by file (%<covered>.2f%%) is below the expected minimum coverage (%<minimum_coverage>.2f%%).\n",
|
19
|
+
covered: SimpleCov.round_coverage(violation.fetch(:actual)),
|
20
|
+
minimum_coverage: violation.fetch(:minimum_expected),
|
21
|
+
criterion: violation.fetch(:criterion).capitalize
|
22
|
+
)
|
23
|
+
end
|
22
24
|
end
|
23
25
|
|
24
26
|
def exit_code
|
@@ -29,9 +31,23 @@ module SimpleCov
|
|
29
31
|
|
30
32
|
attr_reader :result, :minimum_coverage_by_file
|
31
33
|
|
32
|
-
def
|
33
|
-
@
|
34
|
-
|
34
|
+
def minimum_violations
|
35
|
+
@minimum_violations ||=
|
36
|
+
compute_minimum_coverage_data.select do |achieved|
|
37
|
+
achieved.fetch(:actual) < achieved.fetch(:minimum_expected)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def compute_minimum_coverage_data
|
42
|
+
minimum_coverage_by_file.flat_map do |criterion, expected_percent|
|
43
|
+
result.coverage_statistics_by_file.fetch(criterion).map do |actual_coverage|
|
44
|
+
{
|
45
|
+
criterion: criterion,
|
46
|
+
minimum_expected: expected_percent,
|
47
|
+
actual: SimpleCov.round_coverage(actual_coverage.percent)
|
48
|
+
}
|
49
|
+
end
|
50
|
+
end
|
35
51
|
end
|
36
52
|
end
|
37
53
|
end
|
data/lib/simplecov/file_list.rb
CHANGED
@@ -27,6 +27,10 @@ module SimpleCov
|
|
27
27
|
@coverage_statistics ||= compute_coverage_statistics
|
28
28
|
end
|
29
29
|
|
30
|
+
def coverage_statistics_by_file
|
31
|
+
@coverage_statistics_by_file ||= compute_coverage_statistics_by_file
|
32
|
+
end
|
33
|
+
|
30
34
|
# Returns the count of lines that have coverage
|
31
35
|
def covered_lines
|
32
36
|
coverage_statistics[:line]&.covered
|
@@ -100,14 +104,16 @@ module SimpleCov
|
|
100
104
|
|
101
105
|
private
|
102
106
|
|
103
|
-
def
|
104
|
-
|
105
|
-
together[:line] << file.coverage_statistics
|
106
|
-
together[:branch] << file.coverage_statistics
|
107
|
+
def compute_coverage_statistics_by_file
|
108
|
+
@files.each_with_object(line: [], branch: []) do |file, together|
|
109
|
+
together[:line] << file.coverage_statistics.fetch(:line)
|
110
|
+
together[:branch] << file.coverage_statistics.fetch(:branch) if SimpleCov.branch_coverage?
|
107
111
|
end
|
112
|
+
end
|
108
113
|
|
109
|
-
|
110
|
-
coverage_statistics
|
114
|
+
def compute_coverage_statistics
|
115
|
+
coverage_statistics = {line: CoverageStatistics.from(coverage_statistics_by_file[:line])}
|
116
|
+
coverage_statistics[:branch] = CoverageStatistics.from(coverage_statistics_by_file[:branch]) if SimpleCov.branch_coverage?
|
111
117
|
coverage_statistics
|
112
118
|
end
|
113
119
|
end
|
@@ -17,14 +17,14 @@ module SimpleCov
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def self.no_cov_line?(line)
|
20
|
-
line
|
20
|
+
no_cov_line.match?(line)
|
21
21
|
rescue ArgumentError
|
22
22
|
# E.g., line contains an invalid byte sequence in UTF-8
|
23
23
|
false
|
24
24
|
end
|
25
25
|
|
26
26
|
def self.whitespace_line?(line)
|
27
|
-
line
|
27
|
+
WHITESPACE_OR_COMMENT_LINE.match?(line)
|
28
28
|
rescue ArgumentError
|
29
29
|
# E.g., line contains an invalid byte sequence in UTF-8
|
30
30
|
false
|
data/lib/simplecov/result.rb
CHANGED
@@ -20,13 +20,13 @@ 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, :coverage_statistics
|
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, :coverage_statistics_by_file
|
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, command_name: nil, created_at: nil)
|
29
|
-
result =
|
29
|
+
result = original_result
|
30
30
|
@original_result = result.freeze
|
31
31
|
@command_name = command_name
|
32
32
|
@created_at = created_at
|
@@ -72,10 +72,6 @@ module SimpleCov
|
|
72
72
|
}
|
73
73
|
end
|
74
74
|
|
75
|
-
def time_since_creation
|
76
|
-
Time.now - created_at
|
77
|
-
end
|
78
|
-
|
79
75
|
# Loads a SimpleCov::Result#to_hash dump
|
80
76
|
def self.from_hash(hash)
|
81
77
|
hash.map do |command_name, data|
|
@@ -85,31 +81,6 @@ module SimpleCov
|
|
85
81
|
|
86
82
|
private
|
87
83
|
|
88
|
-
# We changed the format of the raw result data in simplecov, as people are likely
|
89
|
-
# to have "old" resultsets lying around (but not too old so that they're still
|
90
|
-
# considered we can adapt them).
|
91
|
-
# See https://github.com/simplecov-ruby/simplecov/pull/824#issuecomment-576049747
|
92
|
-
def adapt_result(result)
|
93
|
-
if pre_simplecov_0_18_result?(result)
|
94
|
-
adapt_pre_simplecov_0_18_result(result)
|
95
|
-
else
|
96
|
-
result
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
# pre 0.18 coverage data pointed from file directly to an array of line coverage
|
101
|
-
def pre_simplecov_0_18_result?(result)
|
102
|
-
_key, data = result.first
|
103
|
-
|
104
|
-
data.is_a?(Array)
|
105
|
-
end
|
106
|
-
|
107
|
-
def adapt_pre_simplecov_0_18_result(result)
|
108
|
-
result.transform_values do |line_coverage_data|
|
109
|
-
{"lines" => line_coverage_data}
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
84
|
def coverage
|
114
85
|
keys = original_result.keys & filenames
|
115
86
|
Hash[keys.zip(original_result.values_at(*keys))]
|
@@ -19,81 +19,130 @@ module SimpleCov
|
|
19
19
|
File.join(SimpleCov.coverage_path, ".resultset.json.lock")
|
20
20
|
end
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
22
|
+
def merge_and_store(*file_paths, ignore_timeout: false)
|
23
|
+
result = merge_results(*file_paths, ignore_timeout: ignore_timeout)
|
24
|
+
store_result(result) if result
|
25
|
+
result
|
26
|
+
end
|
27
|
+
|
28
|
+
def merge_results(*file_paths, ignore_timeout: false)
|
29
|
+
# It is intentional here that files are only read in and parsed one at a time.
|
30
|
+
#
|
31
|
+
# In big CI setups you might deal with 100s of CI jobs and each one producing Megabytes
|
32
|
+
# of data. Reading them all in easily produces Gigabytes of memory consumption which
|
33
|
+
# we want to avoid.
|
34
|
+
#
|
35
|
+
# For similar reasons a SimpleCov::Result is only created in the end as that'd create
|
36
|
+
# even more data especially when it also reads in all source files.
|
37
|
+
initial_memo = valid_results(file_paths.shift, ignore_timeout: ignore_timeout)
|
38
|
+
|
39
|
+
command_names, coverage = file_paths.reduce(initial_memo) do |memo, file_path|
|
40
|
+
merge_coverage(memo, valid_results(file_path, ignore_timeout: ignore_timeout))
|
36
41
|
end
|
42
|
+
|
43
|
+
create_result(command_names, coverage)
|
37
44
|
end
|
38
45
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
46
|
+
def valid_results(file_path, ignore_timeout: false)
|
47
|
+
results = parse_file(file_path)
|
48
|
+
merge_valid_results(results, ignore_timeout: ignore_timeout)
|
49
|
+
end
|
50
|
+
|
51
|
+
def parse_file(path)
|
52
|
+
data = read_file(path)
|
53
|
+
parse_json(data)
|
54
|
+
end
|
43
55
|
|
44
|
-
|
45
|
-
|
56
|
+
def read_file(path)
|
57
|
+
return unless File.exist?(path)
|
46
58
|
|
47
|
-
|
59
|
+
data = File.read(path)
|
60
|
+
return if data.nil? || data.length < 2
|
61
|
+
|
62
|
+
data
|
63
|
+
end
|
64
|
+
|
65
|
+
def parse_json(content)
|
66
|
+
return {} unless content
|
67
|
+
|
68
|
+
JSON.parse(content) || {}
|
69
|
+
rescue StandardError
|
70
|
+
warn "[SimpleCov]: Warning! Parsing JSON content of resultset file failed"
|
71
|
+
{}
|
72
|
+
end
|
73
|
+
|
74
|
+
def merge_valid_results(results, ignore_timeout: false)
|
75
|
+
results = results.select { |_command_name, data| within_merge_timeout?(data) } unless ignore_timeout
|
76
|
+
|
77
|
+
command_plus_coverage = results.map do |command_name, data|
|
78
|
+
[[command_name], adapt_result(data.fetch("coverage"))]
|
48
79
|
end
|
80
|
+
|
81
|
+
# one file itself _might_ include multiple test runs
|
82
|
+
merge_coverage(*command_plus_coverage)
|
49
83
|
end
|
50
84
|
|
51
|
-
|
52
|
-
|
53
|
-
# All results that are above the SimpleCov.merge_timeout will be
|
54
|
-
# dropped. Returns an array of SimpleCov::Result items.
|
55
|
-
def results
|
56
|
-
results = Result.from_hash(resultset)
|
57
|
-
results.select { |result| result.time_since_creation < SimpleCov.merge_timeout }
|
85
|
+
def within_merge_timeout?(data)
|
86
|
+
time_since_result_creation(data) < SimpleCov.merge_timeout
|
58
87
|
end
|
59
88
|
|
60
|
-
def
|
61
|
-
|
62
|
-
store_result(result) if result
|
63
|
-
result
|
89
|
+
def time_since_result_creation(data)
|
90
|
+
Time.now - Time.at(data.fetch("timestamp"))
|
64
91
|
end
|
65
92
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
93
|
+
def create_result(command_names, coverage)
|
94
|
+
return nil unless coverage
|
95
|
+
|
96
|
+
command_name = command_names.reject(&:empty?).sort.join(", ")
|
97
|
+
SimpleCov::Result.new(coverage, command_name: command_name)
|
98
|
+
end
|
99
|
+
|
100
|
+
def merge_coverage(*results)
|
101
|
+
return [[""], nil] if results.empty?
|
102
|
+
return results.first if results.size == 1
|
103
|
+
|
104
|
+
results.reduce do |(memo_command, memo_coverage), (command, coverage)|
|
105
|
+
# timestamp is dropped here, which is intentional (we merge it, it gets a new time stamp as of now)
|
106
|
+
merged_coverage = Combine.combine(Combine::ResultsCombiner, memo_coverage, coverage)
|
107
|
+
merged_command = memo_command + command
|
108
|
+
|
109
|
+
[merged_command, merged_coverage]
|
110
|
+
end
|
76
111
|
end
|
77
112
|
|
78
113
|
#
|
79
|
-
# Gets all SimpleCov::Results
|
114
|
+
# Gets all SimpleCov::Results stored in resultset, merges them and produces a new
|
80
115
|
# SimpleCov::Result with merged coverage data and the command_name
|
81
116
|
# for the result consisting of a join on all source result's names
|
82
|
-
#
|
83
117
|
def merged_result
|
84
|
-
merge_results(
|
118
|
+
# conceptually this is just doing `merge_results(resultset_path)`
|
119
|
+
# it's more involved to make syre `synchronize_resultset` is only used around reading
|
120
|
+
resultset_hash = read_resultset
|
121
|
+
command_names, coverage = merge_valid_results(resultset_hash)
|
122
|
+
|
123
|
+
create_result(command_names, coverage)
|
124
|
+
end
|
125
|
+
|
126
|
+
def read_resultset
|
127
|
+
resultset_content =
|
128
|
+
synchronize_resultset do
|
129
|
+
read_file(resultset_path)
|
130
|
+
end
|
131
|
+
|
132
|
+
parse_json(resultset_content)
|
85
133
|
end
|
86
134
|
|
87
135
|
# Saves the given SimpleCov::Result in the resultset cache
|
88
136
|
def store_result(result)
|
89
137
|
synchronize_resultset do
|
90
138
|
# Ensure we have the latest, in case it was already cached
|
91
|
-
|
92
|
-
|
139
|
+
new_resultset = read_resultset
|
140
|
+
|
141
|
+
# A single result only ever has one command_name, see `SimpleCov::Result#to_hash`
|
93
142
|
command_name, data = result.to_hash.first
|
94
|
-
|
143
|
+
new_resultset[command_name] = data
|
95
144
|
File.open(resultset_path, "w+") do |f_|
|
96
|
-
f_.puts JSON.pretty_generate(
|
145
|
+
f_.puts JSON.pretty_generate(new_resultset)
|
97
146
|
end
|
98
147
|
end
|
99
148
|
true
|
@@ -116,9 +165,29 @@ module SimpleCov
|
|
116
165
|
end
|
117
166
|
end
|
118
167
|
|
119
|
-
#
|
120
|
-
|
121
|
-
|
168
|
+
# We changed the format of the raw result data in simplecov, as people are likely
|
169
|
+
# to have "old" resultsets lying around (but not too old so that they're still
|
170
|
+
# considered we can adapt them).
|
171
|
+
# See https://github.com/simplecov-ruby/simplecov/pull/824#issuecomment-576049747
|
172
|
+
def adapt_result(result)
|
173
|
+
if pre_simplecov_0_18_result?(result)
|
174
|
+
adapt_pre_simplecov_0_18_result(result)
|
175
|
+
else
|
176
|
+
result
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
# pre 0.18 coverage data pointed from file directly to an array of line coverage
|
181
|
+
def pre_simplecov_0_18_result?(result)
|
182
|
+
_key, data = result.first
|
183
|
+
|
184
|
+
data.is_a?(Array)
|
185
|
+
end
|
186
|
+
|
187
|
+
def adapt_pre_simplecov_0_18_result(result)
|
188
|
+
result.transform_values do |line_coverage_data|
|
189
|
+
{"lines" => line_coverage_data}
|
190
|
+
end
|
122
191
|
end
|
123
192
|
end
|
124
193
|
end
|
@@ -56,7 +56,7 @@ module SimpleCov
|
|
56
56
|
# Returns true if this line was skipped, false otherwise. Lines are skipped if they are wrapped with
|
57
57
|
# # :nocov: comment lines.
|
58
58
|
def skipped?
|
59
|
-
|
59
|
+
skipped
|
60
60
|
end
|
61
61
|
|
62
62
|
# The status of this line - either covered, missed, skipped or never. Useful i.e. for direct use
|
data/lib/simplecov/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: simplecov
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.21.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Christoph Olszowka
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2021-01-09 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: docile
|
@@ -39,6 +39,20 @@ dependencies:
|
|
39
39
|
- - "~>"
|
40
40
|
- !ruby/object:Gem::Version
|
41
41
|
version: '0.11'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: simplecov_json_formatter
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0.1'
|
49
|
+
type: :runtime
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0.1'
|
42
56
|
description: Code coverage for Ruby with a powerful configuration library and automatic
|
43
57
|
merging of coverage across test suites
|
44
58
|
email:
|
@@ -49,9 +63,6 @@ extensions: []
|
|
49
63
|
extra_rdoc_files: []
|
50
64
|
files:
|
51
65
|
- CHANGELOG.md
|
52
|
-
- CODE_OF_CONDUCT.md
|
53
|
-
- CONTRIBUTING.md
|
54
|
-
- ISSUE_TEMPLATE.md
|
55
66
|
- LICENSE
|
56
67
|
- README.md
|
57
68
|
- doc/alternate-formatters.md
|
@@ -67,6 +78,7 @@ files:
|
|
67
78
|
- lib/simplecov/command_guesser.rb
|
68
79
|
- lib/simplecov/configuration.rb
|
69
80
|
- lib/simplecov/coverage_statistics.rb
|
81
|
+
- lib/simplecov/default_formatter.rb
|
70
82
|
- lib/simplecov/defaults.rb
|
71
83
|
- lib/simplecov/exit_codes.rb
|
72
84
|
- lib/simplecov/exit_codes/exit_code_handling.rb
|
@@ -104,9 +116,9 @@ licenses:
|
|
104
116
|
metadata:
|
105
117
|
bug_tracker_uri: https://github.com/simplecov-ruby/simplecov/issues
|
106
118
|
changelog_uri: https://github.com/simplecov-ruby/simplecov/blob/main/CHANGELOG.md
|
107
|
-
documentation_uri: https://www.rubydoc.info/gems/simplecov/0.
|
119
|
+
documentation_uri: https://www.rubydoc.info/gems/simplecov/0.21.2
|
108
120
|
mailing_list_uri: https://groups.google.com/forum/#!forum/simplecov
|
109
|
-
source_code_uri: https://github.com/simplecov-ruby/simplecov/tree/v0.
|
121
|
+
source_code_uri: https://github.com/simplecov-ruby/simplecov/tree/v0.21.2
|
110
122
|
post_install_message:
|
111
123
|
rdoc_options: []
|
112
124
|
require_paths:
|
@@ -122,7 +134,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
122
134
|
- !ruby/object:Gem::Version
|
123
135
|
version: '0'
|
124
136
|
requirements: []
|
125
|
-
rubygems_version: 3.
|
137
|
+
rubygems_version: 3.2.3
|
126
138
|
signing_key:
|
127
139
|
specification_version: 4
|
128
140
|
summary: Code coverage for Ruby
|