simplecov 0.20.0 → 0.21.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 +23 -506
- data/README.md +28 -7
- data/lib/simplecov.rb +16 -15
- data/lib/simplecov/configuration.rb +40 -16
- data/lib/simplecov/exit_codes/maximum_coverage_drop_check.rb +34 -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/result.rb +2 -31
- data/lib/simplecov/result_merger.rb +121 -52
- data/lib/simplecov/version.rb +1 -1
- metadata +5 -8
- data/CODE_OF_CONDUCT.md +0 -76
- data/CONTRIBUTING.md +0 -51
- data/ISSUE_TEMPLATE.md +0 -23
data/README.md
CHANGED
@@ -295,7 +295,6 @@ information to be lost.
|
|
295
295
|
Add branch coverage measurement statistics to your results. Supported in CRuby versions 2.5+.
|
296
296
|
|
297
297
|
```ruby
|
298
|
-
# or in configure or just SimpleCov.enable_coverage :branch
|
299
298
|
SimpleCov.start do
|
300
299
|
enable_coverage :branch
|
301
300
|
end
|
@@ -341,6 +340,22 @@ Hence, we recommend looking at both metrics together. Branch coverage might also
|
|
341
340
|
overall metric to look at - while you might be missing only 10% of your lines that might
|
342
341
|
account for 50% of your branches for instance.
|
343
342
|
|
343
|
+
## Primary Coverage
|
344
|
+
|
345
|
+
By default, the primary coverage type is `line`. To set the primary coverage to something else, use the following:
|
346
|
+
|
347
|
+
```ruby
|
348
|
+
# or in configure SimpleCov.primary_coverage :branch
|
349
|
+
SimpleCov.start do
|
350
|
+
enable_coverage :branch
|
351
|
+
primary_coverage :branch
|
352
|
+
end
|
353
|
+
```
|
354
|
+
|
355
|
+
Primary coverage determines what will come in first all output, and the type of coverage to check if you don't specify the type of coverage when customizing exit behavior (`SimpleCov.minimum_coverage 90`).
|
356
|
+
|
357
|
+
Note that coverage must first be enabled for non-default coverage types.
|
358
|
+
|
344
359
|
## Filters
|
345
360
|
|
346
361
|
Filters can be used to remove selected files from your coverage data. By default, a filter is applied that removes all
|
@@ -787,30 +802,36 @@ to help ensure coverage is relatively consistent, rather than being skewed by pa
|
|
787
802
|
|
788
803
|
```ruby
|
789
804
|
SimpleCov.minimum_coverage_by_file 80
|
805
|
+
# same as above (the default is to check line coverage by file)
|
806
|
+
SimpleCov.minimum_coverage_by_file line: 80
|
807
|
+
# check for a minimum line coverage by file of 90% and minimum 80% branch coverage
|
808
|
+
SimpleCov.minimum_coverage_by_file line: 90, branch: 80
|
790
809
|
```
|
791
810
|
|
792
|
-
(not yet supported for branch coverage)
|
793
|
-
|
794
811
|
### Maximum coverage drop
|
795
812
|
|
796
813
|
You can define the maximum coverage drop percentage at once. SimpleCov will return non-zero if exceeded.
|
797
814
|
|
798
815
|
```ruby
|
799
816
|
SimpleCov.maximum_coverage_drop 5
|
817
|
+
# same as above (the default is to check line drop)
|
818
|
+
SimpleCov.maximum_coverage_drop line: 5
|
819
|
+
# check for a maximum line drop of 5% and maximum 10% branch drop
|
820
|
+
SimpleCov.maximum_coverage_drop line: 5, branch: 10
|
800
821
|
```
|
801
822
|
|
802
|
-
(not yet supported for branch coverage)
|
803
|
-
|
804
823
|
### Refuse dropping coverage
|
805
824
|
|
806
825
|
You can also entirely refuse dropping coverage between test runs:
|
807
826
|
|
808
827
|
```ruby
|
809
828
|
SimpleCov.refuse_coverage_drop
|
829
|
+
# same as above (the default is to only refuse line drop)
|
830
|
+
SimpleCov.refuse_coverage_drop :line
|
831
|
+
# refuse drop for line and branch
|
832
|
+
SimpleCov.refuse_coverage_drop :line, :branch
|
810
833
|
```
|
811
834
|
|
812
|
-
(not yet supported for branch coverage)
|
813
|
-
|
814
835
|
## Using your own formatter
|
815
836
|
|
816
837
|
You can use your own formatter with:
|
data/lib/simplecov.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
require "English"
|
4
4
|
|
5
5
|
# Coverage may be inaccurate under JRUBY.
|
6
|
-
if defined?(JRUBY_VERSION) && defined?(JRuby)
|
6
|
+
if defined?(JRUBY_VERSION) && defined?(JRuby) && !org.jruby.RubyInstanceConfig.FULL_TRACE_ENABLED
|
7
7
|
|
8
8
|
# @see https://github.com/jruby/jruby/issues/1196
|
9
9
|
# @see https://github.com/metricfu/metric_fu/pull/226
|
@@ -11,11 +11,9 @@ if defined?(JRUBY_VERSION) && defined?(JRuby)
|
|
11
11
|
# @see https://github.com/simplecov-ruby/simplecov/issues/86
|
12
12
|
# @see https://jira.codehaus.org/browse/JRUBY-6106
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
' or set the "debug.fullTrace=true" option in your .jrubyrc'
|
18
|
-
end
|
14
|
+
warn 'Coverage may be inaccurate; set the "--debug" command line option,' \
|
15
|
+
' or do JRUBY_OPTS="--debug"' \
|
16
|
+
' or set the "debug.fullTrace=true" option in your .jrubyrc'
|
19
17
|
end
|
20
18
|
|
21
19
|
#
|
@@ -63,6 +61,7 @@ module SimpleCov
|
|
63
61
|
|
64
62
|
#
|
65
63
|
# Collate a series of SimpleCov result files into a single SimpleCov output.
|
64
|
+
#
|
66
65
|
# You can optionally specify configuration with a block:
|
67
66
|
# SimpleCov.collate Dir["simplecov-resultset-*/.resultset.json"]
|
68
67
|
# OR
|
@@ -80,18 +79,17 @@ module SimpleCov
|
|
80
79
|
# available config options, or checkout the README for more in-depth
|
81
80
|
# information about coverage collation
|
82
81
|
#
|
83
|
-
|
84
|
-
|
82
|
+
# By default `collate` ignores the merge_timeout so all results of all files specified will be
|
83
|
+
# merged together. If you want to honor the merge_timeout then provide the keyword argument
|
84
|
+
# `ignore_timeout: false`.
|
85
|
+
#
|
86
|
+
def collate(result_filenames, profile = nil, ignore_timeout: true, &block)
|
87
|
+
raise "There are no reports to be merged" if result_filenames.empty?
|
85
88
|
|
86
89
|
initial_setup(profile, &block)
|
87
90
|
|
88
|
-
results = result_filenames.flat_map do |filename|
|
89
|
-
# Re-create each included instance of SimpleCov::Result from the stored run data.
|
90
|
-
Result.from_hash(JSON.parse(File.read(filename)) || {})
|
91
|
-
end
|
92
|
-
|
93
91
|
# Use the ResultMerger to produce a single, merged result, ready to use.
|
94
|
-
@result = ResultMerger.merge_and_store(*
|
92
|
+
@result = ResultMerger.merge_and_store(*result_filenames, ignore_timeout: ignore_timeout)
|
95
93
|
|
96
94
|
run_exit_tasks!
|
97
95
|
end
|
@@ -285,7 +283,10 @@ module SimpleCov
|
|
285
283
|
# @api private
|
286
284
|
#
|
287
285
|
def write_last_run(result)
|
288
|
-
SimpleCov::LastRun.write(result:
|
286
|
+
SimpleCov::LastRun.write(result:
|
287
|
+
result.coverage_statistics.transform_values do |stats|
|
288
|
+
round_coverage(stats.percent)
|
289
|
+
end)
|
289
290
|
end
|
290
291
|
|
291
292
|
#
|
@@ -10,7 +10,7 @@ module SimpleCov
|
|
10
10
|
# defined here are usable from SimpleCov directly. Please check out
|
11
11
|
# SimpleCov documentation for further info.
|
12
12
|
#
|
13
|
-
module Configuration
|
13
|
+
module Configuration
|
14
14
|
attr_writer :filters, :groups, :formatter, :print_error_status
|
15
15
|
|
16
16
|
#
|
@@ -191,7 +191,7 @@ module SimpleCov
|
|
191
191
|
# end
|
192
192
|
#
|
193
193
|
def at_exit(&block)
|
194
|
-
return
|
194
|
+
return Proc.new unless running || block_given?
|
195
195
|
|
196
196
|
@at_exit = block if block_given?
|
197
197
|
@at_exit ||= proc { SimpleCov.result.format! }
|
@@ -287,20 +287,22 @@ module SimpleCov
|
|
287
287
|
#
|
288
288
|
# Default is 0% (disabled)
|
289
289
|
#
|
290
|
-
|
291
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
292
290
|
def minimum_coverage(coverage = nil)
|
293
291
|
return @minimum_coverage ||= {} unless coverage
|
294
292
|
|
295
|
-
coverage = {
|
293
|
+
coverage = {primary_coverage => coverage} if coverage.is_a?(Numeric)
|
294
|
+
|
295
|
+
raise_on_invalid_coverage(coverage, "minimum_coverage")
|
296
|
+
|
297
|
+
@minimum_coverage = coverage
|
298
|
+
end
|
299
|
+
|
300
|
+
def raise_on_invalid_coverage(coverage, coverage_setting)
|
296
301
|
coverage.each_key { |criterion| raise_if_criterion_disabled(criterion) }
|
297
302
|
coverage.each_value do |percent|
|
298
|
-
minimum_possible_coverage_exceeded(
|
303
|
+
minimum_possible_coverage_exceeded(coverage_setting) if percent && percent > 100
|
299
304
|
end
|
300
|
-
|
301
|
-
@minimum_coverage = coverage
|
302
305
|
end
|
303
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
304
306
|
|
305
307
|
#
|
306
308
|
# Defines the maximum coverage drop at once allowed for the testsuite to pass.
|
@@ -309,7 +311,13 @@ module SimpleCov
|
|
309
311
|
# Default is 100% (disabled)
|
310
312
|
#
|
311
313
|
def maximum_coverage_drop(coverage_drop = nil)
|
312
|
-
@maximum_coverage_drop ||=
|
314
|
+
return @maximum_coverage_drop ||= {} unless coverage_drop
|
315
|
+
|
316
|
+
coverage_drop = {primary_coverage => coverage_drop} if coverage_drop.is_a?(Numeric)
|
317
|
+
|
318
|
+
raise_on_invalid_coverage(coverage_drop, "maximum_coverage_drop")
|
319
|
+
|
320
|
+
@maximum_coverage_drop = coverage_drop
|
313
321
|
end
|
314
322
|
|
315
323
|
#
|
@@ -320,16 +328,23 @@ module SimpleCov
|
|
320
328
|
# Default is 0% (disabled)
|
321
329
|
#
|
322
330
|
def minimum_coverage_by_file(coverage = nil)
|
323
|
-
|
324
|
-
|
331
|
+
return @minimum_coverage_by_file ||= {} unless coverage
|
332
|
+
|
333
|
+
coverage = {primary_coverage => coverage} if coverage.is_a?(Numeric)
|
334
|
+
|
335
|
+
raise_on_invalid_coverage(coverage, "minimum_coverage_by_file")
|
336
|
+
|
337
|
+
@minimum_coverage_by_file = coverage
|
325
338
|
end
|
326
339
|
|
327
340
|
#
|
328
341
|
# Refuses any coverage drop. That is, coverage is only allowed to increase.
|
329
342
|
# SimpleCov will return non-zero if the coverage decreases.
|
330
343
|
#
|
331
|
-
def refuse_coverage_drop
|
332
|
-
|
344
|
+
def refuse_coverage_drop(*criteria)
|
345
|
+
criteria = coverage_criteria if criteria.empty?
|
346
|
+
|
347
|
+
maximum_coverage_drop(criteria.map { |c| [c, 0] }.to_h)
|
333
348
|
end
|
334
349
|
|
335
350
|
#
|
@@ -376,7 +391,7 @@ module SimpleCov
|
|
376
391
|
# @param [Symbol] criterion
|
377
392
|
#
|
378
393
|
def coverage_criterion(criterion = nil)
|
379
|
-
return @coverage_criterion ||=
|
394
|
+
return @coverage_criterion ||= primary_coverage unless criterion
|
380
395
|
|
381
396
|
raise_if_criterion_unsupported(criterion)
|
382
397
|
|
@@ -389,8 +404,17 @@ module SimpleCov
|
|
389
404
|
coverage_criteria << criterion
|
390
405
|
end
|
391
406
|
|
407
|
+
def primary_coverage(criterion = nil)
|
408
|
+
if criterion.nil?
|
409
|
+
@primary_coverage ||= DEFAULT_COVERAGE_CRITERION
|
410
|
+
else
|
411
|
+
raise_if_criterion_disabled(criterion)
|
412
|
+
@primary_coverage = criterion
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
392
416
|
def coverage_criteria
|
393
|
-
@coverage_criteria ||= Set[
|
417
|
+
@coverage_criteria ||= Set[primary_coverage]
|
394
418
|
end
|
395
419
|
|
396
420
|
def coverage_criterion_enabled?(criterion)
|
@@ -11,15 +11,18 @@ module SimpleCov
|
|
11
11
|
def failing?
|
12
12
|
return false unless maximum_coverage_drop && last_run
|
13
13
|
|
14
|
-
|
14
|
+
coverage_drop_violations.any?
|
15
15
|
end
|
16
16
|
|
17
17
|
def report
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
18
|
+
coverage_drop_violations.each do |violation|
|
19
|
+
$stderr.printf(
|
20
|
+
"%<criterion>s coverage has dropped by %<drop_percent>.2f%% since the last time (maximum allowed: %<max_drop>.2f%%).\n",
|
21
|
+
criterion: violation[:criterion].capitalize,
|
22
|
+
drop_percent: SimpleCov.round_coverage(violation[:drop_percent]),
|
23
|
+
max_drop: violation[:max_drop]
|
24
|
+
)
|
25
|
+
end
|
23
26
|
end
|
24
27
|
|
25
28
|
def exit_code
|
@@ -36,14 +39,34 @@ module SimpleCov
|
|
36
39
|
@last_run = SimpleCov::LastRun.read
|
37
40
|
end
|
38
41
|
|
39
|
-
def
|
40
|
-
|
42
|
+
def coverage_drop_violations
|
43
|
+
@coverage_drop_violations ||=
|
44
|
+
compute_coverage_drop_data.select do |achieved|
|
45
|
+
achieved.fetch(:max_drop) < achieved.fetch(:drop_percent)
|
46
|
+
end
|
47
|
+
end
|
41
48
|
|
42
|
-
|
49
|
+
def compute_coverage_drop_data
|
50
|
+
maximum_coverage_drop.map do |criterion, percent|
|
51
|
+
{
|
52
|
+
criterion: criterion,
|
53
|
+
max_drop: percent,
|
54
|
+
drop_percent: last_coverage(criterion) -
|
55
|
+
SimpleCov.round_coverage(
|
56
|
+
result.coverage_statistics.fetch(criterion).percent
|
57
|
+
)
|
58
|
+
}
|
59
|
+
end
|
43
60
|
end
|
44
61
|
|
45
|
-
def
|
46
|
-
|
62
|
+
def last_coverage(criterion)
|
63
|
+
last_coverage_percent = last_run[:result][criterion]
|
64
|
+
|
65
|
+
if !last_coverage_percent && criterion == "line"
|
66
|
+
last_run[:result][:covered_percent]
|
67
|
+
else
|
68
|
+
last_coverage_percent
|
69
|
+
end
|
47
70
|
end
|
48
71
|
end
|
49
72
|
end
|
@@ -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
|
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))]
|