simplecov 1.1.1 → 1.2.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 +148 -5
- data/exe/simplecov +0 -3
- data/lib/minitest/simplecov_plugin.rb +12 -9
- data/lib/simplecov/atomic_file.rb +16 -19
- data/lib/simplecov/autostart.rb +5 -7
- data/lib/simplecov/baseline/parser.rb +58 -0
- data/lib/simplecov/baseline.rb +170 -0
- data/lib/simplecov/cli/affected/changed_files.rb +57 -0
- data/lib/simplecov/cli/affected/selection.rb +117 -0
- data/lib/simplecov/cli/affected.rb +148 -0
- data/lib/simplecov/cli/badge/svg.rb +70 -0
- data/lib/simplecov/cli/badge.rb +62 -0
- data/lib/simplecov/cli/clean.rb +23 -24
- data/lib/simplecov/cli/command_helpers.rb +55 -19
- data/lib/simplecov/cli/completions/scripts.rb +114 -0
- data/lib/simplecov/cli/completions.rb +83 -0
- data/lib/simplecov/cli/coverage.rb +25 -28
- data/lib/simplecov/cli/coverage_file.rb +58 -14
- data/lib/simplecov/cli/dead_code/output.rb +100 -0
- data/lib/simplecov/cli/dead_code.rb +139 -0
- data/lib/simplecov/cli/diff/output.rb +45 -0
- data/lib/simplecov/cli/diff.rb +35 -67
- data/lib/simplecov/cli/dotfile.rb +83 -38
- data/lib/simplecov/cli/git.rb +41 -0
- data/lib/simplecov/cli/history/output.rb +135 -0
- data/lib/simplecov/cli/history.rb +67 -0
- data/lib/simplecov/cli/merge.rb +34 -39
- data/lib/simplecov/cli/open.rb +12 -15
- data/lib/simplecov/cli/patch/changed_lines.rb +149 -0
- data/lib/simplecov/cli/patch/output.rb +135 -0
- data/lib/simplecov/cli/patch.rb +169 -0
- data/lib/simplecov/cli/ratchet/output.rb +44 -0
- data/lib/simplecov/cli/ratchet.rb +83 -0
- data/lib/simplecov/cli/report.rb +18 -24
- data/lib/simplecov/cli/run.rb +8 -9
- data/lib/simplecov/cli/serve/report_preparer.rb +7 -6
- data/lib/simplecov/cli/serve/static_file_handler.rb +62 -56
- data/lib/simplecov/cli/serve.rb +27 -24
- data/lib/simplecov/cli/show/annotator.rb +87 -0
- data/lib/simplecov/cli/show/sweep.rb +36 -0
- data/lib/simplecov/cli/show.rb +133 -0
- data/lib/simplecov/cli/status/facts.rb +87 -0
- data/lib/simplecov/cli/status.rb +93 -0
- data/lib/simplecov/cli/tests/redundancy.rb +78 -0
- data/lib/simplecov/cli/tests.rb +148 -0
- data/lib/simplecov/cli/uncovered/misses.rb +37 -0
- data/lib/simplecov/cli/uncovered.rb +70 -32
- data/lib/simplecov/cli/usage.rb +152 -0
- data/lib/simplecov/cli/watch/live_report.rb +73 -0
- data/lib/simplecov/cli/watch/narrator.rb +55 -0
- data/lib/simplecov/cli/watch/poller.rb +45 -0
- data/lib/simplecov/cli/watch/session.rb +134 -0
- data/lib/simplecov/cli/watch/test_plan.rb +43 -0
- data/lib/simplecov/cli/watch.rb +85 -0
- data/lib/simplecov/cli.rb +43 -92
- data/lib/simplecov/color.rb +19 -37
- data/lib/simplecov/combine/branches_combiner.rb +7 -31
- data/lib/simplecov/combine/coverage_accumulator.rb +49 -142
- data/lib/simplecov/combine/identity_interner.rb +10 -13
- data/lib/simplecov/combine/interned_counts.rb +7 -9
- data/lib/simplecov/combine/lines_combiner.rb +20 -44
- data/lib/simplecov/combine/methods_combiner.rb +9 -35
- data/lib/simplecov/combine/results_combiner.rb +4 -21
- data/lib/simplecov/combine.rb +0 -6
- data/lib/simplecov/command_guesser.rb +20 -34
- data/lib/simplecov/configuration/baseline.rb +31 -0
- data/lib/simplecov/configuration/coverage.rb +91 -79
- data/lib/simplecov/configuration/coverage_criteria.rb +43 -54
- data/lib/simplecov/configuration/deprecations.rb +21 -0
- data/lib/simplecov/configuration/eval_coverage.rb +6 -10
- data/lib/simplecov/configuration/filters.rb +40 -73
- data/lib/simplecov/configuration/formatting.rb +74 -83
- data/lib/simplecov/configuration/groups.rb +19 -13
- data/lib/simplecov/configuration/history.rb +44 -0
- data/lib/simplecov/configuration/ignored_entries.rb +31 -22
- data/lib/simplecov/configuration/merging.rb +94 -62
- data/lib/simplecov/configuration/missed_caps.rb +51 -0
- data/lib/simplecov/configuration/production.rb +23 -0
- data/lib/simplecov/configuration/test_tracking.rb +48 -0
- data/lib/simplecov/configuration/thresholds.rb +33 -62
- data/lib/simplecov/configuration/view_coverage.rb +38 -0
- data/lib/simplecov/configuration.rb +70 -76
- data/lib/simplecov/configuration_error.rb +5 -0
- data/lib/simplecov/context_map/union.rb +81 -0
- data/lib/simplecov/context_map.rb +187 -0
- data/lib/simplecov/coverage_json.rb +2 -5
- data/lib/simplecov/coverage_statistics.rb +14 -24
- data/lib/simplecov/coverage_violations.rb +157 -64
- data/lib/simplecov/current_run.rb +61 -0
- data/lib/simplecov/defaults.rb +4 -23
- data/lib/simplecov/deprecation.rb +45 -27
- data/lib/simplecov/directive.rb +46 -62
- data/lib/simplecov/exit_codes/baseline_check.rb +46 -0
- data/lib/simplecov/exit_codes/check.rb +17 -11
- data/lib/simplecov/exit_codes/exit_code_handling.rb +21 -8
- data/lib/simplecov/exit_codes/maximum_coverage_drop_check.rb +7 -10
- data/lib/simplecov/exit_codes/maximum_missed_check.rb +26 -0
- data/lib/simplecov/exit_codes/maximum_missed_per_file_check.rb +34 -0
- data/lib/simplecov/exit_codes/maximum_overall_coverage_check.rb +7 -12
- data/lib/simplecov/exit_codes/minimum_coverage_by_file_check.rb +11 -10
- data/lib/simplecov/exit_codes/minimum_coverage_by_group_check.rb +7 -9
- data/lib/simplecov/exit_codes/minimum_overall_coverage_check.rb +16 -17
- data/lib/simplecov/exit_codes.rb +16 -10
- data/lib/simplecov/exit_handling.rb +94 -59
- data/lib/simplecov/file_list.rb +20 -51
- data/lib/simplecov/filter.rb +43 -66
- data/lib/simplecov/formatter/base.rb +37 -55
- data/lib/simplecov/formatter/baseline_formatter.rb +98 -0
- data/lib/simplecov/formatter/coverage_json_writer.rb +29 -31
- data/lib/simplecov/formatter/html_formatter/public/index.html +15 -9
- data/lib/simplecov/formatter/html_formatter/viewer_data_validator.rb +74 -17
- data/lib/simplecov/formatter/html_formatter.rb +24 -41
- data/lib/simplecov/formatter/json_formatter/errors_formatter.rb +50 -16
- data/lib/simplecov/formatter/json_formatter/production_section_formatter.rb +48 -0
- data/lib/simplecov/formatter/json_formatter/result_hash_formatter.rb +43 -29
- data/lib/simplecov/formatter/json_formatter/source_file_formatter.rb +14 -10
- data/lib/simplecov/formatter/json_formatter.rb +7 -13
- data/lib/simplecov/formatter/multi_formatter.rb +5 -11
- data/lib/simplecov/formatter/simple_formatter.rb +2 -8
- data/lib/simplecov/formatter.rb +14 -10
- data/lib/simplecov/group_names.rb +13 -12
- data/lib/simplecov/history.rb +115 -0
- data/lib/simplecov/last_run.rb +6 -9
- data/lib/simplecov/lines_classifier.rb +31 -16
- data/lib/simplecov/load_global_config.rb +7 -7
- data/lib/simplecov/parallel_adapters/base.rb +13 -41
- data/lib/simplecov/parallel_adapters/generic.rb +7 -19
- data/lib/simplecov/parallel_adapters/parallel_tests.rb +21 -42
- data/lib/simplecov/parallel_adapters.rb +24 -55
- data/lib/simplecov/parallel_coordination.rb +39 -49
- data/lib/simplecov/parallel_result_merger/worker_payload.rb +33 -0
- data/lib/simplecov/parallel_result_merger.rb +67 -126
- data/lib/simplecov/process.rb +14 -32
- data/lib/simplecov/production/error.rb +11 -0
- data/lib/simplecov/production/file_sink.rb +130 -0
- data/lib/simplecov/production.rb +249 -0
- data/lib/simplecov/profiles/rails.rb +1 -12
- data/lib/simplecov/profiles/root_filter.rb +0 -4
- data/lib/simplecov/profiles/strict.rb +9 -19
- data/lib/simplecov/profiles.rb +11 -33
- data/lib/simplecov/report_deferral.rb +11 -15
- data/lib/simplecov/report_stamp.rb +6 -9
- data/lib/simplecov/result/filter_config.rb +19 -0
- data/lib/simplecov/result/missing_source_files_reporter.rb +8 -9
- data/lib/simplecov/result/serialization.rb +43 -0
- data/lib/simplecov/result/source_file_builder.rb +7 -12
- data/lib/simplecov/result.rb +68 -117
- data/lib/simplecov/result_adapter.rb +56 -68
- data/lib/simplecov/result_merger/contexts.rb +22 -0
- data/lib/simplecov/result_merger/legacy_format_adapter.rb +4 -8
- data/lib/simplecov/result_merger/resultset_file.rb +20 -27
- data/lib/simplecov/result_merger/resultset_run_identity.rb +23 -25
- data/lib/simplecov/result_merger/resultset_store.rb +23 -14
- data/lib/simplecov/result_merger/unloaded_files.rb +24 -32
- data/lib/simplecov/result_merger.rb +59 -58
- data/lib/simplecov/result_processing.rb +74 -119
- data/lib/simplecov/run_identity.rb +16 -18
- data/lib/simplecov/simulate_coverage.rb +34 -50
- data/lib/simplecov/source_file/branch.rb +8 -33
- data/lib/simplecov/source_file/branch_builder.rb +28 -47
- data/lib/simplecov/source_file/builder_context.rb +6 -13
- data/lib/simplecov/source_file/line.rb +13 -33
- data/lib/simplecov/source_file/line_builder.rb +9 -15
- data/lib/simplecov/source_file/method.rb +3 -7
- data/lib/simplecov/source_file/method_builder.rb +6 -15
- data/lib/simplecov/source_file/ruby_data_parser.rb +59 -66
- data/lib/simplecov/source_file/skip_chunks.rb +19 -28
- data/lib/simplecov/source_file/source_loader.rb +45 -35
- data/lib/simplecov/source_file/statistics.rb +10 -9
- data/lib/simplecov/source_file.rb +21 -51
- data/lib/simplecov/static_coverage_extractor/condition_folding.rb +63 -114
- data/lib/simplecov/static_coverage_extractor/location_conventions.rb +76 -121
- data/lib/simplecov/static_coverage_extractor/method_collector.rb +11 -24
- data/lib/simplecov/static_coverage_extractor/prism_compat.rb +15 -24
- data/lib/simplecov/static_coverage_extractor/value_position.rb +25 -38
- data/lib/simplecov/static_coverage_extractor/visitor.rb +47 -64
- data/lib/simplecov/static_coverage_extractor.rb +46 -62
- data/lib/simplecov/test_tracker/accessors.rb +34 -0
- data/lib/simplecov/test_tracker/constant_watch.rb +49 -0
- data/lib/simplecov/test_tracker/delta.rb +69 -0
- data/lib/simplecov/test_tracker/framework_hooks.rb +130 -0
- data/lib/simplecov/test_tracker.rb +162 -0
- data/lib/simplecov/unloaded_file_injector.rb +18 -31
- data/lib/simplecov/useless_results_remover.rb +7 -6
- data/lib/simplecov/version.rb +1 -1
- data/lib/simplecov/view_coverage/template_compiler.rb +83 -0
- data/lib/simplecov/view_coverage.rb +55 -0
- data/lib/simplecov-html.rb +1 -0
- data/lib/simplecov.rb +57 -113
- data/lib/simplecov_json_formatter.rb +1 -0
- data/man/simplecov.1 +349 -0
- data/sig/simplecov.rbs +523 -595
- metadata +72 -5
|
@@ -1,80 +1,93 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module SimpleCov
|
|
4
|
-
# Result merging and subprocess / parallel-test coordination:
|
|
5
|
-
# `merging`, `merge_subprocesses`, `merge_timeout`, `parallel_tests`.
|
|
6
4
|
module Configuration
|
|
7
5
|
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
# Defaults to false.
|
|
6
|
+
# Whether SimpleCov should hook `Process._fork` to attach itself to
|
|
7
|
+
# subprocesses. Required when the suite uses parallel test workers (Rails'
|
|
8
|
+
# `parallelize(workers:)`). Defaults to false.
|
|
12
9
|
#
|
|
13
10
|
def merge_subprocesses(value = nil)
|
|
14
|
-
return @enable_for_subprocesses if
|
|
11
|
+
return @enable_for_subprocesses if instance_variable_defined?(:@enable_for_subprocesses) && value.nil?
|
|
15
12
|
|
|
13
|
+
self.merge_subprocesses = value
|
|
14
|
+
@enable_for_subprocesses
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# false stands in for nothing, so the answer is always a real opt-in or
|
|
18
|
+
# opt-out.
|
|
19
|
+
def merge_subprocesses=(value)
|
|
16
20
|
@enable_for_subprocesses = value || false
|
|
17
21
|
end
|
|
18
22
|
|
|
19
|
-
# @api private
|
|
20
|
-
# whether to install the fork hook.
|
|
23
|
+
# @api private
|
|
21
24
|
def enabled_for_subprocesses?
|
|
22
|
-
|
|
25
|
+
!!@enable_for_subprocesses
|
|
23
26
|
end
|
|
24
27
|
|
|
25
28
|
#
|
|
26
|
-
#
|
|
27
|
-
# `
|
|
28
|
-
#
|
|
29
|
-
# (nil). See #1018.
|
|
29
|
+
# Whether SimpleCov should auto-require the `parallel_tests` gem when it
|
|
30
|
+
# sees `TEST_ENV_NUMBER` / `PARALLEL_TEST_GROUPS` in the environment.
|
|
31
|
+
# Defaults to auto-detect (nil). See #1018.
|
|
30
32
|
#
|
|
31
33
|
def parallel_tests(value = :__no_arg__)
|
|
32
|
-
return
|
|
34
|
+
return @parallel_tests if value.eql?(:__no_arg__)
|
|
35
|
+
|
|
36
|
+
self.parallel_tests = _ = value
|
|
37
|
+
end
|
|
33
38
|
|
|
39
|
+
def parallel_tests=(value)
|
|
34
40
|
@parallel_tests = value
|
|
35
41
|
end
|
|
36
42
|
|
|
37
|
-
# DEPRECATED: alias for `merge_subprocesses`. Same value/behavior.
|
|
38
43
|
def enable_for_subprocesses(value = nil)
|
|
39
|
-
|
|
40
|
-
|
|
44
|
+
Deprecation.warn("`SimpleCov.enable_for_subprocesses` is deprecated. " \
|
|
45
|
+
"Replace with `SimpleCov.merge_subprocesses` (same value, same behavior).")
|
|
41
46
|
merge_subprocesses(value)
|
|
42
47
|
end
|
|
43
48
|
|
|
44
|
-
#
|
|
45
|
-
# Get or set whether to merge results from multiple test suites
|
|
46
|
-
# (test:units, test:functionals, cucumber, ...) into a single
|
|
47
|
-
# coverage report. Defaults to true.
|
|
48
|
-
#
|
|
49
49
|
def merging(use = nil)
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
self.merging = use unless use.nil?
|
|
51
|
+
# Unset reads as nil, and only an explicit `merging false` turns it off.
|
|
52
|
+
@use_merging = true if @use_merging.nil?
|
|
52
53
|
@use_merging
|
|
53
54
|
end
|
|
54
55
|
|
|
56
|
+
def merging=(use)
|
|
57
|
+
@use_merging = use
|
|
58
|
+
end
|
|
59
|
+
|
|
55
60
|
#
|
|
56
|
-
#
|
|
57
|
-
#
|
|
58
|
-
#
|
|
61
|
+
# Whether SimpleCov's selected final process owns merge processing: waiting
|
|
62
|
+
# for sibling workers, building the merged result, formatting, enforcing
|
|
63
|
+
# thresholds, and writing `.last_run.json`.
|
|
59
64
|
#
|
|
60
|
-
# Defaults to true, except for recognized multi-worker parallel runs
|
|
61
|
-
#
|
|
62
|
-
#
|
|
63
|
-
#
|
|
65
|
+
# Defaults to true, except for recognized multi-worker parallel runs that
|
|
66
|
+
# explicitly write to a custom coverage destination while merging is
|
|
67
|
+
# enabled. Those runs are likely using an external `SimpleCov.collate` step
|
|
68
|
+
# to finalize the merge.
|
|
64
69
|
#
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
70
|
+
# Splatted rather than defaulted: false is a value someone sets on purpose,
|
|
71
|
+
# so "given nothing" cannot be told from it by looking at the argument.
|
|
72
|
+
def finalize_merge(*value)
|
|
73
|
+
unless value.empty?
|
|
74
|
+
explicit, = value
|
|
75
|
+
self.finalize_merge = _ = explicit
|
|
69
76
|
end
|
|
70
77
|
|
|
71
|
-
return @finalize_merge if
|
|
78
|
+
return @finalize_merge if @finalize_merge_explicit
|
|
72
79
|
|
|
73
80
|
inferred = inferred_finalize_merge?
|
|
74
81
|
warn_about_inferred_finalize_merge unless inferred
|
|
75
82
|
inferred
|
|
76
83
|
end
|
|
77
84
|
|
|
85
|
+
# Writing is what makes the answer explicit rather than inferred.
|
|
86
|
+
def finalize_merge=(value)
|
|
87
|
+
@finalize_merge = value
|
|
88
|
+
@finalize_merge_explicit = true
|
|
89
|
+
end
|
|
90
|
+
|
|
78
91
|
def finalize_merge?
|
|
79
92
|
finalize_merge
|
|
80
93
|
end
|
|
@@ -84,45 +97,65 @@ module SimpleCov
|
|
|
84
97
|
collating_result? || (finalize_merge? && final_result_process?)
|
|
85
98
|
end
|
|
86
99
|
|
|
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.
|
|
92
100
|
def use_merging(use = nil)
|
|
93
|
-
|
|
94
|
-
|
|
101
|
+
Deprecation.warn("`SimpleCov.use_merging` is deprecated. " \
|
|
102
|
+
"Replace with `SimpleCov.merging` (same value, same behavior).")
|
|
95
103
|
merging(use)
|
|
96
104
|
end
|
|
97
105
|
|
|
98
106
|
#
|
|
99
|
-
#
|
|
100
|
-
#
|
|
107
|
+
# The maximum age (in seconds) of a resultset to still be included in
|
|
108
|
+
# merged results. Default is 600 seconds.
|
|
109
|
+
#
|
|
110
|
+
# A numeric SIMPLECOV_MERGE_TIMEOUT in the environment takes precedence
|
|
111
|
+
# over the configured value: `simplecov watch` re-runs test subsets across
|
|
112
|
+
# a long session and extends its child runs' merge window this way, where
|
|
113
|
+
# editing every watched project's configuration is not an option.
|
|
101
114
|
#
|
|
102
115
|
def merge_timeout(seconds = nil)
|
|
103
|
-
|
|
104
|
-
@merge_timeout ||= 600
|
|
116
|
+
self.merge_timeout = seconds
|
|
117
|
+
# Memoized through a local rather than `|| (@merge_timeout ||= 600)`:
|
|
118
|
+
# steep 2.0's logic-type interpreter crashes on an or-assignment nested in
|
|
119
|
+
# a logical operand, and ivar narrowing doesn't carry across statements
|
|
120
|
+
# the way a local's does.
|
|
121
|
+
configured = @merge_timeout || 600
|
|
122
|
+
@merge_timeout = configured
|
|
123
|
+
env_merge_timeout || configured
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Anything but an Integer is ignored, the way the dual method always did.
|
|
127
|
+
def merge_timeout=(seconds)
|
|
128
|
+
@merge_timeout = seconds if seconds.instance_of?(Integer)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def env_merge_timeout
|
|
132
|
+
value = ENV.fetch("SIMPLECOV_MERGE_TIMEOUT", nil)
|
|
133
|
+
Integer(value, 10) if value&.match?(/\A\d+\z/)
|
|
105
134
|
end
|
|
106
135
|
|
|
107
136
|
#
|
|
108
|
-
#
|
|
109
|
-
#
|
|
110
|
-
#
|
|
111
|
-
#
|
|
112
|
-
#
|
|
113
|
-
# skipped against a partial total.
|
|
137
|
+
# How long (in seconds) the reporting process waits for the remaining
|
|
138
|
+
# parallel-test workers to write their resultsets before it proceeds with a
|
|
139
|
+
# partial merge. Default is 60 seconds. Raise it when a slow worker
|
|
140
|
+
# routinely finishes well after the others, so its coverage is still
|
|
141
|
+
# included and the threshold checks aren't skipped against a partial total.
|
|
114
142
|
#
|
|
115
143
|
def parallel_wait_timeout(seconds = nil)
|
|
116
|
-
|
|
144
|
+
self.parallel_wait_timeout = seconds
|
|
117
145
|
@parallel_wait_timeout ||= 60
|
|
118
146
|
end
|
|
119
147
|
|
|
120
|
-
|
|
148
|
+
# Anything but an Integer is ignored, the way the dual method always did.
|
|
149
|
+
def parallel_wait_timeout=(seconds)
|
|
150
|
+
@parallel_wait_timeout = seconds if seconds.instance_of?(Integer)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
private
|
|
121
154
|
|
|
122
155
|
def inferred_finalize_merge?
|
|
123
156
|
return true unless merging
|
|
124
157
|
|
|
125
|
-
adapter =
|
|
158
|
+
adapter = ParallelAdapters.current
|
|
126
159
|
return true unless adapter
|
|
127
160
|
return true unless adapter.expected_worker_count > 1
|
|
128
161
|
return true unless parallel_worker_environment?
|
|
@@ -138,20 +171,19 @@ module SimpleCov
|
|
|
138
171
|
def explicit_custom_coverage_destination?
|
|
139
172
|
return false unless explicit_coverage_destination?
|
|
140
173
|
|
|
141
|
-
coverage_path
|
|
174
|
+
!coverage_path.eql?(File.expand_path("coverage", root))
|
|
142
175
|
end
|
|
143
176
|
|
|
144
177
|
def explicit_coverage_destination?
|
|
145
|
-
|
|
146
|
-
(defined?(@coverage_dir_explicit) && @coverage_dir_explicit)
|
|
178
|
+
@coverage_path_explicit || @coverage_dir_explicit
|
|
147
179
|
end
|
|
148
180
|
|
|
149
181
|
def warn_about_inferred_finalize_merge
|
|
150
|
-
return if
|
|
182
|
+
return if @finalize_merge_inference_warned
|
|
151
183
|
return unless print_errors
|
|
152
184
|
|
|
153
185
|
@finalize_merge_inference_warned = true
|
|
154
|
-
warn
|
|
186
|
+
warn Color.colorize(inferred_finalize_merge_warning, :yellow)
|
|
155
187
|
end
|
|
156
188
|
|
|
157
189
|
def inferred_finalize_merge_warning
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SimpleCov
|
|
4
|
+
module Configuration
|
|
5
|
+
# Caps the total number of misses per criterion. Where a percent minimum
|
|
6
|
+
# asks for a ratio, this asks for an absolute burn-down number, which stays
|
|
7
|
+
# meaningful as the codebase grows and shrinks.
|
|
8
|
+
def maximum_missed(counts = nil)
|
|
9
|
+
return @maximum_missed ||= {} unless counts
|
|
10
|
+
|
|
11
|
+
@maximum_missed = normalized_missed_caps(counts, "maximum_missed")
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Unlike a percent minimum, which systematically flatters big files, this
|
|
15
|
+
# holds every file to the same absolute budget. Files with a baseline entry
|
|
16
|
+
# are exempt per covered criterion, as they are from the per-file minimum.
|
|
17
|
+
def maximum_missed_per_file(counts = nil)
|
|
18
|
+
return @maximum_missed_per_file ||= {} unless counts
|
|
19
|
+
|
|
20
|
+
Deprecation.warn("`SimpleCov.maximum_missed_per_file` is deprecated. " \
|
|
21
|
+
"Replace it with:\n#{missed_per_file_replacement(counts)}")
|
|
22
|
+
@maximum_missed_per_file = normalized_missed_caps(counts, "maximum_missed_per_file")
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def maximum_missed_per_file_overrides
|
|
26
|
+
@maximum_missed_per_file_overrides ||= {}
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def normalized_missed_caps(counts, setting)
|
|
32
|
+
counts = {primary_coverage => counts} if counts.is_a?(Numeric)
|
|
33
|
+
counts.each_key { |criterion| raise_if_criterion_disabled(criterion) }
|
|
34
|
+
counts.each_value { |cap| raise_on_invalid_missed_cap(cap, setting) }
|
|
35
|
+
_ = counts
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def raise_on_invalid_missed_cap(cap, setting)
|
|
39
|
+
return if cap.instance_of?(Integer) && cap >= 0
|
|
40
|
+
|
|
41
|
+
raise ConfigurationError,
|
|
42
|
+
"#{setting} takes a non-negative integer count of misses, got #{cap.inspect}"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def missed_per_file_replacement(counts)
|
|
46
|
+
counts = {primary_coverage => counts} if counts.is_a?(Numeric)
|
|
47
|
+
counts.map { |criterion, cap| " coverage(#{criterion.inspect}) { maximum_missed #{cap}, per: :file }" }
|
|
48
|
+
.join("\n")
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SimpleCov
|
|
4
|
+
module Configuration
|
|
5
|
+
# The path to a production coverage store, the file a `SimpleCov::Production`
|
|
6
|
+
# sink wrote. Relative paths resolve against `SimpleCov.root`. The store is
|
|
7
|
+
# read at report time, and an unreadable or invalid file warns and leaves the
|
|
8
|
+
# section out, because a missing night's data should not fail the suite that
|
|
9
|
+
# measured the tests.
|
|
10
|
+
def production_coverage(path = nil)
|
|
11
|
+
return @production_coverage if path.nil?
|
|
12
|
+
|
|
13
|
+
self.production_coverage = path
|
|
14
|
+
@production_coverage
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def production_coverage=(path)
|
|
18
|
+
raise ConfigurationError, "production_coverage takes a path, got #{path.inspect}" unless path.is_a?(String)
|
|
19
|
+
|
|
20
|
+
@production_coverage = File.expand_path(path, root)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SimpleCov
|
|
4
|
+
module Configuration
|
|
5
|
+
TRACK_TESTS_GRANULARITIES = %i[test file].freeze
|
|
6
|
+
|
|
7
|
+
# Enables recording of which test covered which line. Off by default:
|
|
8
|
+
# sampling coverage around every test costs run time, and the recorded map
|
|
9
|
+
# costs space in `.resultset.json`. RSpec examples and Minitest tests are
|
|
10
|
+
# wrapped automatically; other runners wrap their own units of work with
|
|
11
|
+
# `SimpleCov.track_test`. A bare `track_tests` enables, and only an explicit
|
|
12
|
+
# `track_tests false` turns it back off.
|
|
13
|
+
#
|
|
14
|
+
# `granularity` decides what one recorded context stands for: a test
|
|
15
|
+
# (`:test`, the default) or a whole test file (`:file`). File granularity is
|
|
16
|
+
# the batching lever for suites where per-test sampling costs too much.
|
|
17
|
+
def track_tests(enabled = nil, granularity: nil)
|
|
18
|
+
if granularity && !TRACK_TESTS_GRANULARITIES.include?(granularity)
|
|
19
|
+
raise ConfigurationError,
|
|
20
|
+
"Unsupported track_tests granularity #{granularity.inspect}, " \
|
|
21
|
+
"supported values are #{TRACK_TESTS_GRANULARITIES.inspect}"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
@track_tests_granularity = granularity if granularity
|
|
25
|
+
@track_tests = enabled.nil? || enabled
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def track_tests?
|
|
29
|
+
!!@track_tests
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def track_tests_granularity
|
|
33
|
+
@track_tests_granularity || :test
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# @api private. The map is built from per-line execution count deltas, which
|
|
37
|
+
# `:oneshot_line` does not produce: a line reports only its first hit ever,
|
|
38
|
+
# so every test after the first would show no delta.
|
|
39
|
+
def validate_test_tracking!
|
|
40
|
+
return unless track_tests?
|
|
41
|
+
return if coverage_criterion_enabled?(DEFAULT_COVERAGE_CRITERION)
|
|
42
|
+
|
|
43
|
+
raise ConfigurationError,
|
|
44
|
+
"`track_tests` needs line coverage with execution counts. " \
|
|
45
|
+
"Enable it with `enable_coverage :line` (`:oneshot_line` cannot record per-test data)."
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -1,16 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module SimpleCov
|
|
4
|
-
# Coverage threshold configuration: `minimum_coverage`,
|
|
5
|
-
# `maximum_coverage`, `expected_coverage`, `maximum_coverage_drop`,
|
|
6
|
-
# `minimum_coverage_by_file`, `minimum_coverage_by_group`,
|
|
7
|
-
# `refuse_coverage_drop`, and friends.
|
|
8
4
|
module Configuration
|
|
9
|
-
#
|
|
10
|
-
# Defines the minimum overall coverage required for the testsuite to pass.
|
|
11
|
-
# Returns non-zero if the current coverage is below this threshold.
|
|
12
|
-
# Default is 0% (disabled).
|
|
13
|
-
#
|
|
14
5
|
def minimum_coverage(coverage = nil)
|
|
15
6
|
return @minimum_coverage ||= {} unless coverage
|
|
16
7
|
|
|
@@ -25,10 +16,10 @@ module SimpleCov
|
|
|
25
16
|
end
|
|
26
17
|
|
|
27
18
|
#
|
|
28
|
-
#
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
19
|
+
# The maximum overall coverage allowed for the testsuite to pass. Useful
|
|
20
|
+
# paired with `minimum_coverage` (or via `expected_coverage`) to pin
|
|
21
|
+
# coverage to an exact value, so an unexpected jump up fails the build
|
|
22
|
+
# (#187).
|
|
32
23
|
#
|
|
33
24
|
def maximum_coverage(coverage = nil)
|
|
34
25
|
return @maximum_coverage ||= {} unless coverage
|
|
@@ -36,10 +27,6 @@ module SimpleCov
|
|
|
36
27
|
@maximum_coverage = normalized_threshold(coverage, "maximum_coverage")
|
|
37
28
|
end
|
|
38
29
|
|
|
39
|
-
#
|
|
40
|
-
# Pins the suite to an exact coverage figure by setting both
|
|
41
|
-
# `minimum_coverage` and `maximum_coverage`. See #187.
|
|
42
|
-
#
|
|
43
30
|
def expected_coverage(coverage = nil)
|
|
44
31
|
return minimum_coverage if coverage.nil?
|
|
45
32
|
|
|
@@ -47,10 +34,6 @@ module SimpleCov
|
|
|
47
34
|
maximum_coverage(coverage)
|
|
48
35
|
end
|
|
49
36
|
|
|
50
|
-
#
|
|
51
|
-
# Defines the maximum coverage drop at once allowed for the
|
|
52
|
-
# testsuite to pass. Default is 100% (disabled).
|
|
53
|
-
#
|
|
54
37
|
def maximum_coverage_drop(coverage_drop = nil)
|
|
55
38
|
return @maximum_coverage_drop ||= {} unless coverage_drop
|
|
56
39
|
|
|
@@ -58,11 +41,10 @@ module SimpleCov
|
|
|
58
41
|
end
|
|
59
42
|
|
|
60
43
|
#
|
|
61
|
-
#
|
|
62
|
-
#
|
|
63
|
-
#
|
|
64
|
-
#
|
|
65
|
-
# per-path overrides. See README and #575.
|
|
44
|
+
# The minimum coverage per file required for the testsuite to pass.
|
|
45
|
+
# Accepts a Numeric (global threshold on the primary criterion), a
|
|
46
|
+
# Symbol-keyed Hash (per-criterion globals), or a Hash mixing Symbol keys
|
|
47
|
+
# with String / Regexp keys to declare per-path overrides (#575).
|
|
66
48
|
#
|
|
67
49
|
def minimum_coverage_by_file(coverage = nil)
|
|
68
50
|
return @minimum_coverage_by_file ||= {} unless coverage
|
|
@@ -70,8 +52,8 @@ module SimpleCov
|
|
|
70
52
|
coverage = {primary_coverage => coverage} if coverage.is_a?(Numeric)
|
|
71
53
|
defaults, overrides = partition_per_file_thresholds(coverage)
|
|
72
54
|
|
|
73
|
-
|
|
74
|
-
|
|
55
|
+
Deprecation.warn("`SimpleCov.minimum_coverage_by_file` is deprecated. " \
|
|
56
|
+
"Replace it with:\n#{per_file_coverage_replacement(defaults, overrides)}")
|
|
75
57
|
|
|
76
58
|
raise_on_invalid_coverage(defaults, "minimum_coverage_by_file")
|
|
77
59
|
overrides.each_value { |criteria| raise_on_invalid_coverage(criteria, "minimum_coverage_by_file") }
|
|
@@ -80,91 +62,80 @@ module SimpleCov
|
|
|
80
62
|
@minimum_coverage_by_file_overrides = overrides
|
|
81
63
|
end
|
|
82
64
|
|
|
83
|
-
# Returns the per-path overrides set via `minimum_coverage_by_file`.
|
|
84
65
|
def minimum_coverage_by_file_overrides
|
|
85
66
|
@minimum_coverage_by_file_overrides ||= {}
|
|
86
67
|
end
|
|
87
68
|
|
|
88
|
-
#
|
|
89
|
-
# Defines the minimum coverage per group required for the testsuite
|
|
90
|
-
# to pass. Default is 0% (disabled).
|
|
91
|
-
#
|
|
92
69
|
def minimum_coverage_by_group(coverage = nil)
|
|
93
70
|
return @minimum_coverage_by_group ||= {} unless coverage
|
|
94
71
|
|
|
95
|
-
|
|
96
|
-
|
|
72
|
+
Deprecation.warn("`SimpleCov.minimum_coverage_by_group` is deprecated. " \
|
|
73
|
+
"Replace it with:\n#{per_group_coverage_replacement(coverage)}")
|
|
97
74
|
@minimum_coverage_by_group = coverage.to_h do |group_name, group_coverage|
|
|
98
75
|
[GroupNames.normalize(group_name), normalized_threshold(group_coverage, "minimum_coverage_by_group")]
|
|
99
76
|
end
|
|
100
77
|
end
|
|
101
78
|
|
|
102
|
-
#
|
|
103
|
-
# Refuses any coverage drop. Coverage is only allowed to increase.
|
|
104
|
-
#
|
|
105
79
|
def refuse_coverage_drop(*criteria)
|
|
106
80
|
criteria = coverage_criteria if criteria.empty?
|
|
107
81
|
maximum_coverage_drop(criteria.to_h { |c| [c, 0] })
|
|
108
82
|
end
|
|
109
83
|
|
|
110
|
-
|
|
84
|
+
private
|
|
111
85
|
|
|
112
|
-
#
|
|
113
|
-
# a bare Numeric targets the primary criterion, and the resulting
|
|
86
|
+
# A bare Numeric targets the primary criterion, and the resulting
|
|
114
87
|
# per-criterion hash is validated before it is stored.
|
|
115
88
|
def normalized_threshold(coverage, setting)
|
|
116
89
|
coverage = {primary_coverage => coverage} if coverage.is_a?(Numeric)
|
|
117
|
-
raise_on_invalid_coverage(
|
|
118
|
-
coverage
|
|
90
|
+
coverage.tap { |thresholds| raise_on_invalid_coverage(thresholds, setting) }
|
|
119
91
|
end
|
|
120
92
|
|
|
121
|
-
#
|
|
122
|
-
#
|
|
123
|
-
#
|
|
124
|
-
# so downstream code only has one shape to handle.
|
|
93
|
+
# Splits into Symbol-keyed criterion defaults and String/Regexp-keyed
|
|
94
|
+
# per-path overrides, normalizing Numeric override values to
|
|
95
|
+
# `{primary_coverage => N}` so downstream code has one shape to handle.
|
|
125
96
|
def partition_per_file_thresholds(coverage)
|
|
126
97
|
coverage.each_key { |key| validate_per_file_key(key) }
|
|
127
|
-
|
|
128
|
-
#
|
|
129
|
-
|
|
130
|
-
defaults =
|
|
131
|
-
raw =
|
|
98
|
+
# The assertions restate what the partition predicate guarantees: Symbol
|
|
99
|
+
# keys carry per-criterion Numeric defaults, the rest are paths.
|
|
100
|
+
symbol_pairs, path_pairs = coverage.partition { |key, _| key.instance_of?(Symbol) }
|
|
101
|
+
defaults = symbol_pairs.to_h #: coverage_thresholds
|
|
102
|
+
raw = path_pairs.to_h #: Hash[String | Regexp, Numeric | coverage_thresholds]
|
|
132
103
|
overrides = raw.transform_values { |value| value.is_a?(Numeric) ? {primary_coverage => value} : value }
|
|
133
104
|
[defaults, overrides]
|
|
134
105
|
end
|
|
135
106
|
|
|
136
107
|
def validate_per_file_key(key)
|
|
137
|
-
|
|
108
|
+
# Symbols have no subclasses; paths and patterns may.
|
|
109
|
+
return if key.instance_of?(Symbol) || key.is_a?(String) || key.is_a?(Regexp)
|
|
138
110
|
|
|
139
|
-
raise
|
|
140
|
-
|
|
111
|
+
raise ConfigurationError,
|
|
112
|
+
"minimum_coverage_by_file keys must be Symbol (criterion), String, or Regexp; got #{key.inspect}"
|
|
141
113
|
end
|
|
142
114
|
|
|
143
115
|
def minimum_possible_coverage_exceeded(coverage_option)
|
|
144
116
|
warn "The coverage you set for #{coverage_option} is greater than 100%"
|
|
145
117
|
end
|
|
146
118
|
|
|
147
|
-
#
|
|
148
|
-
# `minimum_coverage_by_file` argument so the deprecation warning can be
|
|
119
|
+
# Renders the `coverage` configuration equivalent to a deprecated
|
|
120
|
+
# `minimum_coverage_by_file` argument, so the deprecation warning can be
|
|
149
121
|
# copy-pasted verbatim into the user's config.
|
|
150
122
|
def per_file_coverage_replacement(defaults, overrides)
|
|
151
123
|
by_criterion = {} #: Hash[Symbol, Array[String]]
|
|
152
|
-
defaults.each { |criterion, percent| (by_criterion[criterion] ||= []) << "
|
|
124
|
+
defaults.each { |criterion, percent| (by_criterion[criterion] ||= []) << "minimum #{percent}, per: :file" }
|
|
153
125
|
overrides.each do |target, criteria|
|
|
154
126
|
criteria.each do |criterion, percent|
|
|
155
|
-
(by_criterion[criterion] ||= []) << "
|
|
127
|
+
(by_criterion[criterion] ||= []) << "minimum #{percent}, per: #{target.inspect}"
|
|
156
128
|
end
|
|
157
129
|
end
|
|
158
130
|
render_coverage_blocks(by_criterion)
|
|
159
131
|
end
|
|
160
132
|
|
|
161
|
-
# Same, for a (deprecated) `minimum_coverage_by_group` argument.
|
|
162
133
|
def per_group_coverage_replacement(coverage)
|
|
163
134
|
by_criterion = {} #: Hash[Symbol, Array[String]]
|
|
164
135
|
coverage.each do |group_name, thresholds|
|
|
165
136
|
normalized = (thresholds.is_a?(Numeric) ? {primary_coverage => thresholds} : thresholds) #: coverage_thresholds
|
|
166
137
|
normalized.each do |criterion, percent|
|
|
167
|
-
(by_criterion[criterion] ||= []) << "
|
|
138
|
+
(by_criterion[criterion] ||= []) << "minimum #{percent}, per: group(#{group_name.inspect})"
|
|
168
139
|
end
|
|
169
140
|
end
|
|
170
141
|
render_coverage_blocks(by_criterion)
|
|
@@ -172,7 +143,7 @@ module SimpleCov
|
|
|
172
143
|
|
|
173
144
|
def render_coverage_blocks(by_criterion)
|
|
174
145
|
by_criterion.map do |criterion, statements|
|
|
175
|
-
" coverage(#{criterion.inspect}) { #{statements.join(
|
|
146
|
+
" coverage(#{criterion.inspect}) { #{statements.join("; ")} }"
|
|
176
147
|
end.join("\n")
|
|
177
148
|
end
|
|
178
149
|
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SimpleCov
|
|
4
|
+
module Configuration
|
|
5
|
+
DEFAULT_VIEW_GLOBS = %w[app/views/**/*.{erb,haml,slim}].freeze
|
|
6
|
+
|
|
7
|
+
# Reports coverage for ActionView templates, defaulting to a Rails app's
|
|
8
|
+
# views. Globs are expanded against `SimpleCov.root`, and one naming an
|
|
9
|
+
# extension no handler is registered for (as the default does whenever a
|
|
10
|
+
# project has no Haml or Slim) leaves those files out rather than reporting
|
|
11
|
+
# them as empty.
|
|
12
|
+
#
|
|
13
|
+
# Templates the suite renders are measured through eval coverage, which this
|
|
14
|
+
# enables, so it needs Ruby 3.2 or later. Templates it never renders are
|
|
15
|
+
# compiled at the end of the run so they appear at 0% instead of going
|
|
16
|
+
# missing.
|
|
17
|
+
def cover_views(*globs)
|
|
18
|
+
globs = globs.flatten.compact
|
|
19
|
+
globs = DEFAULT_VIEW_GLOBS.dup if globs.empty?
|
|
20
|
+
@view_globs = globs
|
|
21
|
+
enable_eval_coverage
|
|
22
|
+
globs
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Nil rather than an empty Array when `cover_views` was never called, so
|
|
26
|
+
# "not asked for" stays distinguishable from "asked for, with nothing to
|
|
27
|
+
# match".
|
|
28
|
+
def view_globs
|
|
29
|
+
@view_globs
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# False when eval coverage couldn't be enabled, where `cover_views` has
|
|
33
|
+
# already warned and the templates would come back empty rather than at 0%.
|
|
34
|
+
def view_coverage?
|
|
35
|
+
!view_globs.nil? && coverage_for_eval_enabled?
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|