simplecov 1.0.2 → 1.1.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/README.md +65 -1425
- data/lib/simplecov/atomic_file.rb +70 -0
- data/lib/simplecov/cli/clean.rb +43 -3
- data/lib/simplecov/cli/command_helpers.rb +55 -0
- data/lib/simplecov/cli/coverage.rb +21 -28
- data/lib/simplecov/cli/coverage_file.rb +65 -0
- data/lib/simplecov/cli/diff.rb +43 -48
- data/lib/simplecov/cli/dotfile.rb +12 -6
- data/lib/simplecov/cli/merge.rb +12 -10
- data/lib/simplecov/cli/open.rb +3 -5
- data/lib/simplecov/cli/report.rb +31 -23
- data/lib/simplecov/cli/serve/report_preparer.rb +29 -0
- data/lib/simplecov/cli/serve/static_file_handler.rb +122 -0
- data/lib/simplecov/cli/serve.rb +30 -94
- data/lib/simplecov/cli/uncovered.rb +20 -25
- data/lib/simplecov/cli.rb +15 -2
- data/lib/simplecov/combine/branches_combiner.rb +43 -19
- data/lib/simplecov/combine/coverage_accumulator.rb +268 -0
- data/lib/simplecov/combine/identity_interner.rb +30 -0
- data/lib/simplecov/combine/interned_counts.rb +30 -0
- data/lib/simplecov/combine/lines_combiner.rb +48 -20
- data/lib/simplecov/combine/methods_combiner.rb +46 -21
- data/lib/simplecov/combine/results_combiner.rb +12 -37
- data/lib/simplecov/combine.rb +5 -23
- data/lib/simplecov/command_guesser.rb +66 -9
- data/lib/simplecov/configuration/coverage.rb +12 -15
- data/lib/simplecov/configuration/coverage_criteria.rb +34 -38
- data/lib/simplecov/configuration/eval_coverage.rb +41 -0
- data/lib/simplecov/configuration/filters.rb +11 -44
- data/lib/simplecov/configuration/formatting.rb +18 -9
- data/lib/simplecov/configuration/groups.rb +42 -0
- data/lib/simplecov/configuration/merging.rb +8 -7
- data/lib/simplecov/configuration/thresholds.rb +14 -13
- data/lib/simplecov/configuration.rb +13 -43
- data/lib/simplecov/coverage_json.rb +24 -0
- data/lib/simplecov/coverage_statistics.rb +1 -1
- data/lib/simplecov/coverage_violations.rb +15 -5
- data/lib/simplecov/defaults.rb +7 -3
- data/lib/simplecov/directive.rb +1 -1
- data/lib/simplecov/exit_codes/check.rb +33 -0
- data/lib/simplecov/exit_codes/maximum_coverage_drop_check.rb +11 -20
- data/lib/simplecov/exit_codes/maximum_overall_coverage_check.rb +3 -16
- data/lib/simplecov/exit_codes/minimum_coverage_by_file_check.rb +14 -23
- data/lib/simplecov/exit_codes/minimum_coverage_by_group_check.rb +14 -25
- data/lib/simplecov/exit_codes/minimum_overall_coverage_check.rb +4 -17
- data/lib/simplecov/exit_codes.rb +1 -0
- data/lib/simplecov/exit_handling.rb +11 -41
- data/lib/simplecov/file_list.rb +5 -10
- data/lib/simplecov/filter.rb +43 -9
- data/lib/simplecov/formatter/base.rb +11 -0
- data/lib/simplecov/formatter/coverage_json_writer.rb +97 -0
- data/lib/simplecov/formatter/html_formatter/public/index.html +35 -5
- data/lib/simplecov/formatter/html_formatter/viewer_data_validator.rb +96 -0
- data/lib/simplecov/formatter/html_formatter.rb +67 -47
- data/lib/simplecov/formatter/json_formatter/errors_formatter.rb +54 -56
- data/lib/simplecov/formatter/json_formatter/result_hash_formatter.rb +78 -88
- data/lib/simplecov/formatter/json_formatter/source_file_formatter.rb +70 -76
- data/lib/simplecov/formatter/json_formatter.rb +5 -48
- data/lib/simplecov/formatter/multi_formatter.rb +1 -1
- data/lib/simplecov/formatter/simple_formatter.rb +8 -5
- data/lib/simplecov/formatter.rb +12 -0
- data/lib/simplecov/group_names.rb +32 -0
- data/lib/simplecov/last_run.rb +16 -9
- data/lib/simplecov/lines_classifier.rb +29 -8
- data/lib/simplecov/load_global_config.rb +5 -2
- data/lib/simplecov/parallel_adapters/base.rb +17 -0
- data/lib/simplecov/parallel_adapters/generic.rb +2 -2
- data/lib/simplecov/parallel_adapters/parallel_tests.rb +2 -2
- data/lib/simplecov/parallel_coordination.rb +6 -1
- data/lib/simplecov/parallel_result_merger.rb +230 -0
- data/lib/simplecov/report_deferral.rb +49 -0
- data/lib/simplecov/report_stamp.rb +28 -0
- data/lib/simplecov/result.rb +40 -10
- data/lib/simplecov/result_adapter.rb +48 -16
- data/lib/simplecov/result_merger/resultset_file.rb +43 -7
- data/lib/simplecov/result_merger/resultset_run_identity.rb +67 -0
- data/lib/simplecov/result_merger/resultset_store.rb +15 -12
- data/lib/simplecov/result_merger/unloaded_files.rb +103 -0
- data/lib/simplecov/result_merger.rb +69 -42
- data/lib/simplecov/result_processing.rb +82 -43
- data/lib/simplecov/run_identity.rb +77 -0
- data/lib/simplecov/simulate_coverage.rb +35 -11
- data/lib/simplecov/source_file/method.rb +7 -1
- data/lib/simplecov/source_file/ruby_data_parser.rb +25 -3
- data/lib/simplecov/source_file/skip_chunks.rb +7 -10
- data/lib/simplecov/source_file/source_loader.rb +23 -7
- data/lib/simplecov/source_file/statistics.rb +24 -16
- data/lib/simplecov/static_coverage_extractor/condition_folding.rb +203 -13
- data/lib/simplecov/static_coverage_extractor/location_conventions.rb +19 -30
- data/lib/simplecov/static_coverage_extractor/method_collector.rb +7 -0
- data/lib/simplecov/static_coverage_extractor/prism_compat.rb +55 -0
- data/lib/simplecov/static_coverage_extractor/value_position.rb +6 -14
- data/lib/simplecov/static_coverage_extractor/visitor.rb +23 -36
- data/lib/simplecov/unloaded_file_injector.rb +75 -0
- data/lib/simplecov/version.rb +1 -1
- data/lib/simplecov.rb +11 -5
- data/schemas/coverage-v1.0.schema.json +2 -2
- data/schemas/coverage.schema.json +2 -2
- data/sig/simplecov.rbs +206 -67
- metadata +28 -14
- data/doc/alternate-formatters.md +0 -66
- data/doc/commercial-services.md +0 -25
- data/doc/editor-integration.md +0 -18
- data/lib/simplecov/combine/files_combiner.rb +0 -70
- data/lib/simplecov/formatter/html_formatter/public/application.css +0 -1
- data/lib/simplecov/formatter/html_formatter/public/application.js +0 -18
- data/lib/simplecov/formatter/html_formatter/public/favicon_green.png +0 -0
- data/lib/simplecov/formatter/html_formatter/public/favicon_red.png +0 -0
- data/lib/simplecov/formatter/html_formatter/public/favicon_yellow.png +0 -0
|
@@ -11,10 +11,30 @@ module SimpleCov
|
|
|
11
11
|
# have a habit of tampering with ARGV, which makes i.e. the automatic distinction
|
|
12
12
|
# between rails unit/functional/integration tests impossible without this cached
|
|
13
13
|
# item.
|
|
14
|
-
|
|
14
|
+
attr_reader :original_run_command
|
|
15
|
+
|
|
16
|
+
# Assigning the flattened command clears any separately recorded program
|
|
17
|
+
# name so the two can never describe different runs. Callers that know the
|
|
18
|
+
# real one assign it immediately afterwards, as `defaults.rb` does.
|
|
19
|
+
def original_run_command=(command)
|
|
20
|
+
@original_program_name = nil
|
|
21
|
+
@original_run_command = command
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# The invoked program on its own. `original_run_command` joins
|
|
25
|
+
# `$PROGRAM_NAME` and `ARGV` with a space, so a program path containing
|
|
26
|
+
# one (`/opt/My Ruby/bin/rspec`) cannot be recovered from it afterwards.
|
|
27
|
+
# Falls back to the first token for callers that set only the flattened
|
|
28
|
+
# command, which is every caller outside `defaults.rb`.
|
|
29
|
+
attr_writer :original_program_name
|
|
30
|
+
|
|
31
|
+
def original_program_name
|
|
32
|
+
@original_program_name || original_run_command.to_s[/\A\s*(\S+)/, 1]
|
|
33
|
+
end
|
|
15
34
|
|
|
16
35
|
def guess
|
|
17
|
-
|
|
36
|
+
framework = from_executable_name || from_command_line_options || from_defined_constants
|
|
37
|
+
[framework, parallel_data].compact.join(" ")
|
|
18
38
|
end
|
|
19
39
|
|
|
20
40
|
private
|
|
@@ -31,14 +51,31 @@ module SimpleCov
|
|
|
31
51
|
"(#{number}/#{groups})"
|
|
32
52
|
end
|
|
33
53
|
|
|
54
|
+
# The command is `"#{$PROGRAM_NAME} #{ARGV.join(' ')}"`, so a keyword only
|
|
55
|
+
# carries meaning when it begins a path segment or an argument. Matching it
|
|
56
|
+
# as a bare substring lets any part of the *interpreter* path decide the
|
|
57
|
+
# suite name: a Ruby installed under `latest/bin` (as mise does) contains
|
|
58
|
+
# "test/", so every run through it was labelled "Unit Tests". The same
|
|
59
|
+
# applies within ARGV, where `spec/greatest/` used to match just as wrongly.
|
|
60
|
+
#
|
|
61
|
+
# A backslash bounds a segment too, or a Windows `spec\foo_spec.rb` would
|
|
62
|
+
# lose a match the bare substrings used to make. The `test/` patterns below
|
|
63
|
+
# still spell their separator as `/` only, so a backslash `test\functional\`
|
|
64
|
+
# goes unrecognised there, but that is how it has always been rather than
|
|
65
|
+
# something the anchoring took away.
|
|
66
|
+
SEGMENT_START = %r{(?<![^\s/\\])}
|
|
67
|
+
private_constant :SEGMENT_START
|
|
68
|
+
SEGMENT_END = %r{(?=[\s/\\]|\z)}
|
|
69
|
+
private_constant :SEGMENT_END
|
|
70
|
+
|
|
34
71
|
COMMAND_LINE_FRAMEWORKS = {
|
|
35
|
-
%r{test/functional/} => "Functional Tests",
|
|
36
|
-
%r{test/\{.*functional.*\}/} => "Functional Tests",
|
|
37
|
-
%r{test/integration/} => "Integration Tests",
|
|
38
|
-
%r{test/} => "Unit Tests",
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
72
|
+
%r{#{SEGMENT_START}test/functional/} => "Functional Tests",
|
|
73
|
+
%r{#{SEGMENT_START}test/\{.*functional.*\}/} => "Functional Tests",
|
|
74
|
+
%r{#{SEGMENT_START}test/integration/} => "Integration Tests",
|
|
75
|
+
%r{#{SEGMENT_START}test/} => "Unit Tests",
|
|
76
|
+
/#{SEGMENT_START}spec#{SEGMENT_END}/ => "RSpec",
|
|
77
|
+
/#{SEGMENT_START}cucumber#{SEGMENT_END}/ => "Cucumber Features",
|
|
78
|
+
/#{SEGMENT_START}features#{SEGMENT_END}/ => "Cucumber Features"
|
|
42
79
|
}.freeze
|
|
43
80
|
private_constant :COMMAND_LINE_FRAMEWORKS
|
|
44
81
|
|
|
@@ -46,6 +83,26 @@ module SimpleCov
|
|
|
46
83
|
COMMAND_LINE_FRAMEWORKS.find { |pattern, _| pattern.match?(original_run_command.to_s) }&.last
|
|
47
84
|
end
|
|
48
85
|
|
|
86
|
+
# Which binary was invoked outranks everything else on the command line.
|
|
87
|
+
# `rspec features` is an RSpec suite whose examples live in features/, not
|
|
88
|
+
# a Cucumber run, and only the executable name says so. Generic runners
|
|
89
|
+
# (`ruby`, `rake`, a bare `*_test.rb` script) are absent on purpose: they
|
|
90
|
+
# name no framework, so those runs fall through to the path patterns, which
|
|
91
|
+
# is what keeps the test/functional vs test/integration split working.
|
|
92
|
+
EXECUTABLE_FRAMEWORKS = {
|
|
93
|
+
"rspec" => "RSpec",
|
|
94
|
+
"cucumber" => "Cucumber Features"
|
|
95
|
+
}.freeze
|
|
96
|
+
private_constant :EXECUTABLE_FRAMEWORKS
|
|
97
|
+
|
|
98
|
+
def from_executable_name
|
|
99
|
+
program = original_program_name
|
|
100
|
+
return unless program
|
|
101
|
+
|
|
102
|
+
# Trim any extension so Windows' rspec.bat is recognized too.
|
|
103
|
+
EXECUTABLE_FRAMEWORKS[File.basename(program, ".*")]
|
|
104
|
+
end
|
|
105
|
+
|
|
49
106
|
# Inner array literals after the first are flagged uncovered by Ruby's
|
|
50
107
|
# Coverage module even though the constant evaluates as a whole — known
|
|
51
108
|
# quirk with multi-line array literals.
|
|
@@ -38,12 +38,12 @@ module SimpleCov
|
|
|
38
38
|
#
|
|
39
39
|
# `primary: true` makes this the report's leading criterion (and the one a
|
|
40
40
|
# bare `minimum_coverage 90` targets). `oneshot: true` (valid only for
|
|
41
|
-
# `:line`) selects the faster oneshot-lines mode.
|
|
41
|
+
# `:line`) selects the faster oneshot-lines mode.
|
|
42
42
|
#
|
|
43
43
|
def coverage(criterion, primary: false, enabled: true, oneshot: false, **thresholds, &block)
|
|
44
44
|
criterion = enable_coverage_criterion(criterion, enabled: enabled, oneshot: oneshot)
|
|
45
45
|
# The cast admits :eval, which primary_coverage rejects at runtime
|
|
46
|
-
# (it is
|
|
46
|
+
# (it is a standalone toggle, never in the enabled-criteria set).
|
|
47
47
|
primary_coverage(_ = criterion) if primary
|
|
48
48
|
|
|
49
49
|
configurator = CoverageCriterion.new(self, criterion)
|
|
@@ -70,30 +70,24 @@ module SimpleCov
|
|
|
70
70
|
end
|
|
71
71
|
end
|
|
72
72
|
|
|
73
|
-
# Enable the criterion (or its oneshot / eval variant) and
|
|
74
|
-
# criterion symbol that thresholds should be stored under.
|
|
73
|
+
# Enable or disable the criterion (or its oneshot / eval variant) and
|
|
74
|
+
# return the criterion symbol that thresholds should be stored under.
|
|
75
75
|
def enable_coverage_criterion(criterion, enabled:, oneshot:)
|
|
76
|
-
|
|
77
|
-
return enable_eval_coverage_criterion if criterion == :eval
|
|
78
|
-
|
|
76
|
+
criterion = resolve_criterion_variant(criterion, oneshot)
|
|
79
77
|
enabled ? enable_coverage(criterion) : disable_coverage(criterion)
|
|
80
78
|
criterion
|
|
81
79
|
end
|
|
82
80
|
|
|
83
|
-
def
|
|
81
|
+
def resolve_criterion_variant(criterion, oneshot)
|
|
82
|
+
return criterion unless oneshot
|
|
83
|
+
|
|
84
84
|
unless criterion == :line
|
|
85
85
|
raise SimpleCov::ConfigurationError, "`oneshot: true` is only valid for `coverage :line`"
|
|
86
86
|
end
|
|
87
87
|
|
|
88
|
-
enable_coverage(ONESHOT_LINE_COVERAGE_CRITERION)
|
|
89
88
|
ONESHOT_LINE_COVERAGE_CRITERION
|
|
90
89
|
end
|
|
91
90
|
|
|
92
|
-
def enable_eval_coverage_criterion
|
|
93
|
-
enable_coverage(:eval)
|
|
94
|
-
:eval
|
|
95
|
-
end
|
|
96
|
-
|
|
97
91
|
# @api private — threshold-store writers used by CoverageCriterion. They
|
|
98
92
|
# write the same `@minimum_coverage` / `@maximum_coverage` / ... hashes the
|
|
99
93
|
# flat threshold methods populate, so the exit-code checks are unchanged.
|
|
@@ -115,7 +109,10 @@ module SimpleCov
|
|
|
115
109
|
|
|
116
110
|
def store_minimum_per_group(criterion, percent, group_name)
|
|
117
111
|
raise_on_invalid_coverage({criterion => percent}, "minimum_coverage_by_group")
|
|
118
|
-
|
|
112
|
+
# Normalize like `group` does, so `only: :Models` finds the group
|
|
113
|
+
# defined as `group "Models"` at check time instead of storing a
|
|
114
|
+
# Symbol key no lookup ever matches.
|
|
115
|
+
(minimum_coverage_by_group[GroupNames.normalize(group_name)] ||= {})[criterion] = percent
|
|
119
116
|
end
|
|
120
117
|
|
|
121
118
|
#
|
|
@@ -3,33 +3,29 @@
|
|
|
3
3
|
module SimpleCov
|
|
4
4
|
# Selection and validation of the coverage criteria Ruby's `Coverage`
|
|
5
5
|
# library should track. Supports `:line` (the historical default),
|
|
6
|
-
# `:branch`, `:method`, and `:oneshot_line
|
|
7
|
-
#
|
|
6
|
+
# `:branch`, `:method`, and `:oneshot_line`. The standalone `:eval`
|
|
7
|
+
# toggle lives in `eval_coverage.rb`.
|
|
8
8
|
module Configuration
|
|
9
9
|
SUPPORTED_COVERAGE_CRITERIA = %i[line branch method oneshot_line].freeze
|
|
10
10
|
DEFAULT_COVERAGE_CRITERION = :line
|
|
11
11
|
ONESHOT_LINE_COVERAGE_CRITERION = :oneshot_line
|
|
12
|
+
LINE_COVERAGE_ALTERNATIVES = {line: :oneshot_line, oneshot_line: :line}.freeze #: line_coverage_alternatives
|
|
13
|
+
private_constant :LINE_COVERAGE_ALTERNATIVES
|
|
12
14
|
|
|
13
15
|
# Enable one or more coverage criteria. `:eval` is accepted as a
|
|
14
16
|
# shorthand for the standalone eval-coverage toggle.
|
|
15
17
|
def enable_coverage(*criteria)
|
|
16
|
-
criteria.each
|
|
17
|
-
if criterion == :eval
|
|
18
|
-
enable_eval_coverage
|
|
19
|
-
else
|
|
20
|
-
raise_if_criterion_unsupported(criterion)
|
|
21
|
-
# :oneshot_lines can not be combined with :lines
|
|
22
|
-
coverage_criteria.delete(DEFAULT_COVERAGE_CRITERION) if criterion == ONESHOT_LINE_COVERAGE_CRITERION
|
|
23
|
-
coverage_criteria << criterion
|
|
24
|
-
end
|
|
25
|
-
end
|
|
18
|
+
criteria.each { |criterion| criterion == :eval ? enable_eval_coverage : add_coverage_criterion(criterion) }
|
|
26
19
|
end
|
|
27
20
|
|
|
28
21
|
# Remove `criterion` from the set of enabled coverage criteria.
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
22
|
+
# `:eval` turns the standalone eval-coverage toggle back off,
|
|
23
|
+
# mirroring `enable_coverage`. Disabling every criterion raises at
|
|
24
|
+
# `start_tracking` (not here), so config files that toggle criteria
|
|
25
|
+
# in arbitrary order don't have to worry about transient empty states.
|
|
32
26
|
def disable_coverage(criterion)
|
|
27
|
+
return disable_eval_coverage if criterion == :eval
|
|
28
|
+
|
|
33
29
|
raise_if_criterion_unsupported(criterion)
|
|
34
30
|
coverage_criteria.delete(criterion)
|
|
35
31
|
@primary_coverage = nil if @primary_coverage == criterion
|
|
@@ -68,6 +64,15 @@ module SimpleCov
|
|
|
68
64
|
"Re-enable one with `enable_coverage :line`, `:branch`, or `:method`."
|
|
69
65
|
end
|
|
70
66
|
|
|
67
|
+
# Whether this run produces line data at all. The oneshot variant counts:
|
|
68
|
+
# `ResultAdapter` turns its executed-line list back into a line array. A
|
|
69
|
+
# branch-only or method-only run produces none, and `Coverage.result`
|
|
70
|
+
# entries for the files it loaded carry no `"lines"` key.
|
|
71
|
+
def line_coverage?
|
|
72
|
+
coverage_criterion_enabled?(DEFAULT_COVERAGE_CRITERION) ||
|
|
73
|
+
coverage_criterion_enabled?(ONESHOT_LINE_COVERAGE_CRITERION)
|
|
74
|
+
end
|
|
75
|
+
|
|
71
76
|
def branch_coverage?
|
|
72
77
|
branch_coverage_supported? && coverage_criterion_enabled?(:branch)
|
|
73
78
|
end
|
|
@@ -84,10 +89,6 @@ module SimpleCov
|
|
|
84
89
|
coverage_criterion_supported?(:methods)
|
|
85
90
|
end
|
|
86
91
|
|
|
87
|
-
def coverage_for_eval_supported?
|
|
88
|
-
coverage_criterion_supported?(:eval)
|
|
89
|
-
end
|
|
90
|
-
|
|
91
92
|
# Ask the Coverage runtime itself whether a criterion is supported
|
|
92
93
|
# (Ruby >= 3.2). Older Rubies don't expose `Coverage.supported?`, so
|
|
93
94
|
# fall back to the historical engine check that line/branch/method
|
|
@@ -104,27 +105,13 @@ module SimpleCov
|
|
|
104
105
|
end
|
|
105
106
|
# simplecov:enable
|
|
106
107
|
|
|
107
|
-
def coverage_for_eval_enabled?
|
|
108
|
-
@coverage_for_eval_enabled ||= false
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
# DEPRECATED: prefer `enable_coverage :eval`.
|
|
112
|
-
def enable_coverage_for_eval
|
|
113
|
-
SimpleCov::Deprecation.warn("`SimpleCov.enable_coverage_for_eval` is deprecated. " \
|
|
114
|
-
"Replace with `SimpleCov.enable_coverage :eval`.")
|
|
115
|
-
enable_eval_coverage
|
|
116
|
-
end
|
|
117
|
-
|
|
118
108
|
private
|
|
119
109
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
if
|
|
124
|
-
|
|
125
|
-
else
|
|
126
|
-
warn "Coverage for eval is not available; Use Ruby 3.2.0 or later"
|
|
127
|
-
end
|
|
110
|
+
def add_coverage_criterion(criterion)
|
|
111
|
+
raise_if_criterion_unsupported(criterion)
|
|
112
|
+
incompatible = LINE_COVERAGE_ALTERNATIVES[criterion]
|
|
113
|
+
disable_coverage(incompatible) if incompatible
|
|
114
|
+
coverage_criteria << criterion
|
|
128
115
|
end
|
|
129
116
|
|
|
130
117
|
# If `:line` is enabled, it's the default primary; otherwise fall
|
|
@@ -140,6 +127,15 @@ module SimpleCov
|
|
|
140
127
|
end
|
|
141
128
|
|
|
142
129
|
def raise_if_criterion_disabled(criterion)
|
|
130
|
+
# `coverage :eval` by itself IS supported — it's a standalone
|
|
131
|
+
# toggle, never in the enabled-criteria set — so the generic
|
|
132
|
+
# "unsupported criterion" message below would mislead here.
|
|
133
|
+
if criterion == :eval
|
|
134
|
+
raise SimpleCov::ConfigurationError,
|
|
135
|
+
"Coverage criterion :eval only toggles measuring eval'd code; " \
|
|
136
|
+
"it cannot carry thresholds or serve as the primary criterion"
|
|
137
|
+
end
|
|
138
|
+
|
|
143
139
|
raise_if_criterion_unsupported(criterion)
|
|
144
140
|
return if coverage_criterion_enabled?(criterion)
|
|
145
141
|
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SimpleCov
|
|
4
|
+
# The standalone eval-coverage toggle (`enable_coverage :eval`),
|
|
5
|
+
# instrumenting `eval`'d code on Rubies that support it. Kept separate
|
|
6
|
+
# from the criteria-set logic in `coverage_criteria.rb` because `:eval`
|
|
7
|
+
# is a boolean toggle, never a member of the enabled-criteria set.
|
|
8
|
+
module Configuration
|
|
9
|
+
def coverage_for_eval_supported?
|
|
10
|
+
coverage_criterion_supported?(:eval)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def coverage_for_eval_enabled?
|
|
14
|
+
@coverage_for_eval_enabled ||= false
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# DEPRECATED: prefer `enable_coverage :eval`.
|
|
18
|
+
def enable_coverage_for_eval
|
|
19
|
+
SimpleCov::Deprecation.warn("`SimpleCov.enable_coverage_for_eval` is deprecated. " \
|
|
20
|
+
"Replace with `SimpleCov.enable_coverage :eval`.")
|
|
21
|
+
enable_eval_coverage
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
# Shared implementation backing both `enable_coverage :eval` and
|
|
27
|
+
# the deprecated `enable_coverage_for_eval`.
|
|
28
|
+
def enable_eval_coverage
|
|
29
|
+
if coverage_for_eval_supported?
|
|
30
|
+
@coverage_for_eval_enabled = true
|
|
31
|
+
else
|
|
32
|
+
warn "Coverage for eval is not available; Use Ruby 3.2.0 or later"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# No support check needed: off is a safe state on every Ruby.
|
|
37
|
+
def disable_eval_coverage
|
|
38
|
+
@coverage_for_eval_enabled = false
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module SimpleCov
|
|
4
|
-
# Inclusion
|
|
5
|
-
#
|
|
6
|
-
# aliases. Mutates the same `filters`, `groups`, and `cover_filters`
|
|
7
|
-
# collections the main Configuration module exposes.
|
|
4
|
+
# Inclusion and exclusion methods: `cover` and `skip`, plus the deprecated
|
|
5
|
+
# `track_files` and `add_filter` aliases.
|
|
8
6
|
module Configuration
|
|
9
|
-
attr_writer :filters
|
|
7
|
+
attr_writer :filters
|
|
10
8
|
|
|
11
9
|
#
|
|
12
10
|
# Restrict the universe of files in the coverage report to those matching
|
|
@@ -43,7 +41,7 @@ module SimpleCov
|
|
|
43
41
|
end
|
|
44
42
|
|
|
45
43
|
# Returns the list of string globs passed to `cover` — used by the
|
|
46
|
-
# disk-discovery pass in `SimpleCov.
|
|
44
|
+
# disk-discovery pass in `SimpleCov.tracked_file_paths` so files
|
|
47
45
|
# matching a `cover` glob appear in the report even when they were
|
|
48
46
|
# never required during the suite.
|
|
49
47
|
#
|
|
@@ -121,33 +119,6 @@ module SimpleCov
|
|
|
121
119
|
@filters = []
|
|
122
120
|
end
|
|
123
121
|
|
|
124
|
-
# Returns the configured groups. Add groups using SimpleCov.group.
|
|
125
|
-
def groups
|
|
126
|
-
@groups ||= {}
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
# Define a display group for files. Same matcher grammar as `skip`,
|
|
130
|
-
# but instead of dropping the matching files it bins them under
|
|
131
|
-
# `group_name` for the formatter. Files matched by no group fall
|
|
132
|
-
# into the implicit "Ungrouped" bucket.
|
|
133
|
-
def group(group_name, filter_argument = nil, &)
|
|
134
|
-
groups[group_name] = parse_filter(filter_argument, &)
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
# DEPRECATED: alias for `group`. Same arguments, same behavior.
|
|
138
|
-
def add_group(group_name, filter_argument = nil, &block)
|
|
139
|
-
example = if block
|
|
140
|
-
"`SimpleCov.group #{group_name.inspect} { ... }`"
|
|
141
|
-
else
|
|
142
|
-
"`SimpleCov.group #{group_name.inspect}, #{filter_argument.inspect}`"
|
|
143
|
-
end
|
|
144
|
-
SimpleCov::Deprecation.warn(
|
|
145
|
-
"`SimpleCov.add_group` is deprecated. " \
|
|
146
|
-
"Replace with `SimpleCov.group` (same arguments, same behavior). Example: #{example}."
|
|
147
|
-
)
|
|
148
|
-
group(group_name, filter_argument, &block)
|
|
149
|
-
end
|
|
150
|
-
|
|
151
122
|
# Drop every filter previously installed (defaults plus anything
|
|
152
123
|
# earlier in this block) so subsequent `skip` calls start from a
|
|
153
124
|
# clean slate. Order matters — call this before your own `skip`
|
|
@@ -168,18 +139,14 @@ module SimpleCov
|
|
|
168
139
|
end
|
|
169
140
|
|
|
170
141
|
# Build a filter for a `cover` argument. Strings are treated as
|
|
171
|
-
# globs (not substrings — that's `skip`/`add_filter`'s semantics)
|
|
142
|
+
# globs (not substrings — that's `skip`/`add_filter`'s semantics);
|
|
143
|
+
# everything else dispatches exactly like `add_filter`.
|
|
172
144
|
def build_cover_filter(arg)
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
when Array then SimpleCov::ArrayFilter.new(arg.map { |a| build_cover_filter(a) })
|
|
179
|
-
else raise SimpleCov::ConfigurationError, "Unsupported `cover` argument #{arg.inspect}; " \
|
|
180
|
-
"expected a String glob, Regexp, Proc, " \
|
|
181
|
-
"SimpleCov::Filter, or Array of those."
|
|
182
|
-
end
|
|
145
|
+
SimpleCov::Filter.build_filter(arg, string_filter: SimpleCov::GlobFilter)
|
|
146
|
+
rescue SimpleCov::ConfigurationError
|
|
147
|
+
raise SimpleCov::ConfigurationError, "Unsupported `cover` argument #{arg.inspect}; " \
|
|
148
|
+
"expected a String glob, Regexp, Proc, " \
|
|
149
|
+
"SimpleCov::Filter, or Array of those."
|
|
183
150
|
end
|
|
184
151
|
|
|
185
152
|
# Walk a list of cover filters and return the string globs they hold,
|
|
@@ -9,11 +9,16 @@ module SimpleCov
|
|
|
9
9
|
attr_writer :formatter, :print_error_status
|
|
10
10
|
|
|
11
11
|
#
|
|
12
|
-
# Gets or sets the configured formatter.
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
12
|
+
# Gets or sets the configured formatter. Accepts a formatter class
|
|
13
|
+
# (instantiated fresh for every report) or a ready-built instance,
|
|
14
|
+
# which is how constructor options are passed — e.g.
|
|
15
|
+
# `formatter SimpleCov::Formatter::HTMLFormatter.new(silent: true)`
|
|
16
|
+
# to suppress the "Coverage report generated" status line (see
|
|
17
|
+
# #1240). Pass `false` (or `nil`) to opt out of formatting
|
|
18
|
+
# entirely — worker processes in big parallel CI setups (see #964)
|
|
19
|
+
# only need their `.resultset.json` on disk so a final
|
|
20
|
+
# `SimpleCov.collate` job can produce the report; running them
|
|
21
|
+
# without a formatter saves the per-job HTML/multi-formatter
|
|
17
22
|
# overhead.
|
|
18
23
|
#
|
|
19
24
|
def formatter(formatter = :__no_arg__)
|
|
@@ -40,9 +45,13 @@ module SimpleCov
|
|
|
40
45
|
|
|
41
46
|
# Sets the configured formatters. Equivalent to `formatters [...]`.
|
|
42
47
|
# Accepts a single formatter as well as an Array, matching the pre-1.0 behavior
|
|
43
|
-
# where `MultiFormatter.new` normalized its input.
|
|
48
|
+
# where `MultiFormatter.new` normalized its input. Elements may be
|
|
49
|
+
# formatter classes or ready-built instances; see `formatter`.
|
|
50
|
+
# `nil`, `false`, and `[]` all opt out of formatting entirely —
|
|
51
|
+
# `false` normalized like `formatter false` does, since `Array(false)`
|
|
52
|
+
# would otherwise smuggle it in as a "formatter" that can only fail.
|
|
44
53
|
def formatters=(formatters)
|
|
45
|
-
formatters = Array(formatters)
|
|
54
|
+
formatters = Array(formatters || nil)
|
|
46
55
|
@formatter = formatters.empty? ? nil : SimpleCov::Formatter::MultiFormatter.new(formatters)
|
|
47
56
|
end
|
|
48
57
|
|
|
@@ -83,8 +92,8 @@ module SimpleCov
|
|
|
83
92
|
# a copy of the source tree (which dominates the payload on larger
|
|
84
93
|
# projects).
|
|
85
94
|
#
|
|
86
|
-
# The HTML
|
|
87
|
-
#
|
|
95
|
+
# The HTML report's embedded data always includes source — the
|
|
96
|
+
# client-side renderer needs it. Only `coverage.json` honors
|
|
88
97
|
# this setting.
|
|
89
98
|
#
|
|
90
99
|
# SimpleCov.start do
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SimpleCov
|
|
4
|
+
# Group configuration is kept separate from inclusion and exclusion filters,
|
|
5
|
+
# while sharing their matcher parser through the main Configuration module.
|
|
6
|
+
module Configuration
|
|
7
|
+
# Returns the configured groups. Add groups using SimpleCov.group.
|
|
8
|
+
def groups
|
|
9
|
+
@groups ||= {}
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def groups=(new_groups)
|
|
13
|
+
new_groups = new_groups.to_h { |name, filter| [GroupNames.normalize(name), filter] }
|
|
14
|
+
GroupNames.validate!(new_groups.keys)
|
|
15
|
+
@groups = new_groups
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Define a display group for files. Same matcher grammar as `skip`,
|
|
19
|
+
# but instead of dropping the matching files it bins them under
|
|
20
|
+
# `group_name` for the formatter. Files matched by no group fall
|
|
21
|
+
# into the implicit "Ungrouped" bucket.
|
|
22
|
+
def group(group_name, filter_argument = nil, &)
|
|
23
|
+
group_name = GroupNames.normalize(group_name)
|
|
24
|
+
GroupNames.validate!([group_name])
|
|
25
|
+
groups[group_name] = parse_filter(filter_argument, &)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# DEPRECATED: alias for `group`. Same arguments, same behavior.
|
|
29
|
+
def add_group(group_name, filter_argument = nil, &block)
|
|
30
|
+
example = if block
|
|
31
|
+
"`SimpleCov.group #{group_name.inspect} { ... }`"
|
|
32
|
+
else
|
|
33
|
+
"`SimpleCov.group #{group_name.inspect}, #{filter_argument.inspect}`"
|
|
34
|
+
end
|
|
35
|
+
SimpleCov::Deprecation.warn(
|
|
36
|
+
"`SimpleCov.add_group` is deprecated. " \
|
|
37
|
+
"Replace with `SimpleCov.group` (same arguments, same behavior). Example: #{example}."
|
|
38
|
+
)
|
|
39
|
+
group(group_name, filter_argument, &block)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -38,9 +38,7 @@ module SimpleCov
|
|
|
38
38
|
def enable_for_subprocesses(value = nil)
|
|
39
39
|
SimpleCov::Deprecation.warn("`SimpleCov.enable_for_subprocesses` is deprecated. " \
|
|
40
40
|
"Replace with `SimpleCov.merge_subprocesses` (same value, same behavior).")
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
@enable_for_subprocesses = value || false
|
|
41
|
+
merge_subprocesses(value)
|
|
44
42
|
end
|
|
45
43
|
|
|
46
44
|
#
|
|
@@ -55,7 +53,7 @@ module SimpleCov
|
|
|
55
53
|
end
|
|
56
54
|
|
|
57
55
|
#
|
|
58
|
-
# Get or set whether
|
|
56
|
+
# Get or set whether SimpleCov's selected final process owns merge processing:
|
|
59
57
|
# waiting for sibling workers, building the merged result, formatting,
|
|
60
58
|
# enforcing thresholds, and writing `.last_run.json`.
|
|
61
59
|
#
|
|
@@ -83,15 +81,18 @@ module SimpleCov
|
|
|
83
81
|
|
|
84
82
|
# @api private
|
|
85
83
|
def merge_finalization_owner?
|
|
86
|
-
collating_result? || finalize_merge?
|
|
84
|
+
collating_result? || (finalize_merge? && final_result_process?)
|
|
87
85
|
end
|
|
88
86
|
|
|
89
87
|
# DEPRECATED: alias for `merging`. Same value, same behavior.
|
|
88
|
+
# Delegating (rather than duplicating the body) also fixes the
|
|
89
|
+
# return value: this used to return nil where `merging` returns
|
|
90
|
+
# false, because the final expression was the skipped guard
|
|
91
|
+
# assignment rather than the stored value.
|
|
90
92
|
def use_merging(use = nil)
|
|
91
93
|
SimpleCov::Deprecation.warn("`SimpleCov.use_merging` is deprecated. " \
|
|
92
94
|
"Replace with `SimpleCov.merging` (same value, same behavior).")
|
|
93
|
-
|
|
94
|
-
@use_merging = true unless defined?(@use_merging) && @use_merging == false
|
|
95
|
+
merging(use)
|
|
95
96
|
end
|
|
96
97
|
|
|
97
98
|
#
|
|
@@ -14,9 +14,7 @@ module SimpleCov
|
|
|
14
14
|
def minimum_coverage(coverage = nil)
|
|
15
15
|
return @minimum_coverage ||= {} unless coverage
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
raise_on_invalid_coverage(coverage, "minimum_coverage")
|
|
19
|
-
@minimum_coverage = coverage
|
|
17
|
+
@minimum_coverage = normalized_threshold(coverage, "minimum_coverage")
|
|
20
18
|
end
|
|
21
19
|
|
|
22
20
|
def raise_on_invalid_coverage(coverage, coverage_setting)
|
|
@@ -35,9 +33,7 @@ module SimpleCov
|
|
|
35
33
|
def maximum_coverage(coverage = nil)
|
|
36
34
|
return @maximum_coverage ||= {} unless coverage
|
|
37
35
|
|
|
38
|
-
|
|
39
|
-
raise_on_invalid_coverage(coverage, "maximum_coverage")
|
|
40
|
-
@maximum_coverage = coverage
|
|
36
|
+
@maximum_coverage = normalized_threshold(coverage, "maximum_coverage")
|
|
41
37
|
end
|
|
42
38
|
|
|
43
39
|
#
|
|
@@ -58,9 +54,7 @@ module SimpleCov
|
|
|
58
54
|
def maximum_coverage_drop(coverage_drop = nil)
|
|
59
55
|
return @maximum_coverage_drop ||= {} unless coverage_drop
|
|
60
56
|
|
|
61
|
-
|
|
62
|
-
raise_on_invalid_coverage(coverage_drop, "maximum_coverage_drop")
|
|
63
|
-
@maximum_coverage_drop = coverage_drop
|
|
57
|
+
@maximum_coverage_drop = normalized_threshold(coverage_drop, "maximum_coverage_drop")
|
|
64
58
|
end
|
|
65
59
|
|
|
66
60
|
#
|
|
@@ -100,10 +94,8 @@ module SimpleCov
|
|
|
100
94
|
|
|
101
95
|
SimpleCov::Deprecation.warn("`SimpleCov.minimum_coverage_by_group` is deprecated. " \
|
|
102
96
|
"Replace it with:\n#{per_group_coverage_replacement(coverage)}")
|
|
103
|
-
@minimum_coverage_by_group = coverage.
|
|
104
|
-
|
|
105
|
-
raise_on_invalid_coverage(group_coverage, "minimum_coverage_by_group")
|
|
106
|
-
group_coverage
|
|
97
|
+
@minimum_coverage_by_group = coverage.to_h do |group_name, group_coverage|
|
|
98
|
+
[GroupNames.normalize(group_name), normalized_threshold(group_coverage, "minimum_coverage_by_group")]
|
|
107
99
|
end
|
|
108
100
|
end
|
|
109
101
|
|
|
@@ -117,6 +109,15 @@ module SimpleCov
|
|
|
117
109
|
|
|
118
110
|
private
|
|
119
111
|
|
|
112
|
+
# Shared normalize-and-validate step behind every threshold setter:
|
|
113
|
+
# a bare Numeric targets the primary criterion, and the resulting
|
|
114
|
+
# per-criterion hash is validated before it is stored.
|
|
115
|
+
def normalized_threshold(coverage, setting)
|
|
116
|
+
coverage = {primary_coverage => coverage} if coverage.is_a?(Numeric)
|
|
117
|
+
raise_on_invalid_coverage(coverage, setting)
|
|
118
|
+
coverage
|
|
119
|
+
end
|
|
120
|
+
|
|
120
121
|
# Split a `minimum_coverage_by_file` argument into Symbol-keyed
|
|
121
122
|
# criterion defaults and String/Regexp-keyed per-path overrides;
|
|
122
123
|
# normalize Numeric override values to `{primary_coverage => N}`
|