simplecov 1.1.1 → 1.3.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/annotations.rb +166 -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 +151 -0
- data/lib/simplecov/cli/patch.rb +175 -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 +33 -0
- data/lib/simplecov/cli/uncovered.rb +58 -34
- data/lib/simplecov/cli/usage.rb +153 -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 +7 -11
- 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 +37 -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 +160 -65
- 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/erb.rb +44 -0
- data/lib/simplecov/directive/haml.rb +33 -0
- data/lib/simplecov/directive/indented_template.rb +37 -0
- data/lib/simplecov/directive/slim.rb +35 -0
- data/lib/simplecov/directive/template.rb +29 -0
- data/lib/simplecov/directive.rb +47 -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 +4 -15
- data/lib/simplecov/profiles/root_filter.rb +0 -4
- data/lib/simplecov/profiles/strict.rb +11 -21
- 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 +74 -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 +92 -122
- 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 +13 -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 +29 -29
- data/lib/simplecov/source_file/source_loader.rb +45 -35
- data/lib/simplecov/source_file/statistics.rb +20 -9
- data/lib/simplecov/source_file.rb +21 -51
- data/lib/simplecov/static_coverage_extractor/condition_folding.rb +55 -139
- data/lib/simplecov/static_coverage_extractor/location_conventions.rb +76 -121
- data/lib/simplecov/static_coverage_extractor/method_collector.rb +9 -26
- 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 +45 -65
- data/lib/simplecov/static_coverage_extractor.rb +43 -75
- 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 +354 -0
- data/sig/simplecov.rbs +528 -595
- metadata +79 -6
|
@@ -1,38 +1,44 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
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
4
|
module Configuration
|
|
7
|
-
# Returns the configured groups. Add groups using SimpleCov.group.
|
|
8
5
|
def groups
|
|
9
6
|
@groups ||= {}
|
|
10
7
|
end
|
|
11
8
|
|
|
9
|
+
# The group set `SimpleCov.grouped` bins against when its caller names none.
|
|
10
|
+
# It reads through a method of its own because the keyword it defaults
|
|
11
|
+
# shadows `groups`, so the default has to name a receiver, and every
|
|
12
|
+
# spelling of that receiver is the same call.
|
|
13
|
+
#
|
|
14
|
+
# mutant:disable — for that reason: `SimpleCov.groups`, `self.groups` and
|
|
15
|
+
# `groups()` cannot be told apart.
|
|
16
|
+
def default_groups
|
|
17
|
+
groups
|
|
18
|
+
end
|
|
19
|
+
|
|
12
20
|
def groups=(new_groups)
|
|
13
21
|
new_groups = new_groups.to_h { |name, filter| [GroupNames.normalize(name), filter] }
|
|
14
22
|
GroupNames.validate!(new_groups.keys)
|
|
15
23
|
@groups = new_groups
|
|
16
24
|
end
|
|
17
25
|
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
# into the implicit "Ungrouped" bucket.
|
|
26
|
+
# Same matcher grammar as `skip`, but instead of dropping the matching files
|
|
27
|
+
# it bins them under `group_name` for the formatter. Files matched by no
|
|
28
|
+
# group fall into the implicit "Ungrouped" bucket.
|
|
22
29
|
def group(group_name, filter_argument = nil, &)
|
|
23
30
|
group_name = GroupNames.normalize(group_name)
|
|
24
31
|
GroupNames.validate!([group_name])
|
|
25
32
|
groups[group_name] = parse_filter(filter_argument, &)
|
|
26
33
|
end
|
|
27
34
|
|
|
28
|
-
# DEPRECATED: alias for `group`. Same arguments, same behavior.
|
|
29
35
|
def add_group(group_name, filter_argument = nil, &block)
|
|
30
36
|
example = if block
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
37
|
+
"`SimpleCov.group #{group_name.inspect} { ... }`"
|
|
38
|
+
else
|
|
39
|
+
"`SimpleCov.group #{group_name.inspect}, #{filter_argument.inspect}`"
|
|
40
|
+
end
|
|
41
|
+
Deprecation.warn(
|
|
36
42
|
"`SimpleCov.add_group` is deprecated. " \
|
|
37
43
|
"Replace with `SimpleCov.group` (same arguments, same behavior). Example: #{example}."
|
|
38
44
|
)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SimpleCov
|
|
4
|
+
module Configuration
|
|
5
|
+
DROP_BASELINES = %i[last_run median branch].freeze
|
|
6
|
+
|
|
7
|
+
# How many runs coverage/.history.json keeps, newest kept. 0 disables
|
|
8
|
+
# recording entirely, and anything but a count of runs is refused.
|
|
9
|
+
def history_limit(limit = nil)
|
|
10
|
+
return @history_limit ||= 100 if limit.nil?
|
|
11
|
+
|
|
12
|
+
self.history_limit = limit
|
|
13
|
+
_ = @history_limit
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def history_limit=(limit)
|
|
17
|
+
unless limit.instance_of?(Integer) && limit >= 0
|
|
18
|
+
raise ConfigurationError, "history_limit takes a non-negative integer, got #{limit.inspect}"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
@history_limit = limit
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# What `maximum_coverage_drop` measures the drop against: `:last_run` (the
|
|
25
|
+
# default) the previous run whatever it was, `:median` the median of the
|
|
26
|
+
# recorded history so one run that dipped for an unrelated reason cannot
|
|
27
|
+
# quietly become the baseline, and `:branch` the newest recorded run on the
|
|
28
|
+
# current git branch so a feature branch is compared with itself.
|
|
29
|
+
#
|
|
30
|
+
# The latter two read coverage/.history.json and find nothing to compare
|
|
31
|
+
# against while it is empty, which counts as "no previous run" the way a
|
|
32
|
+
# missing .last_run.json does.
|
|
33
|
+
def drop_baseline(mode = nil)
|
|
34
|
+
return @drop_baseline ||= :last_run if mode.nil?
|
|
35
|
+
|
|
36
|
+
unless DROP_BASELINES.include?(mode)
|
|
37
|
+
raise ConfigurationError,
|
|
38
|
+
"drop_baseline takes one of #{DROP_BASELINES}, got #{mode.inspect}"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
@drop_baseline = mode
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -1,21 +1,18 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module SimpleCov
|
|
4
|
-
# Coverage-entry filters scoped to branch / method criteria. See
|
|
5
|
-
# `ignore_branches` and `ignore_methods`, and #1033 /
|
|
6
|
-
# #1046 for the synthetic-entry detection rationale.
|
|
7
4
|
module Configuration
|
|
8
|
-
# Branch types accepted by `ignore_branches`.
|
|
9
5
|
IGNORABLE_BRANCH_TYPES = %i[implicit_else eval_generated].freeze
|
|
10
|
-
# Method types accepted by `ignore_methods`.
|
|
11
6
|
IGNORABLE_METHOD_TYPES = %i[eval_generated].freeze
|
|
12
7
|
|
|
13
|
-
#
|
|
14
|
-
#
|
|
8
|
+
# Deprecated in favour of `coverage(:branch) { ignore :implicit_else }`,
|
|
9
|
+
# which fixes the criterion by context the way the threshold verbs do. One
|
|
10
|
+
# difference rides out the deprecation period: the coverage block enables the
|
|
11
|
+
# criterion it names, while this legacy setter records without enabling.
|
|
15
12
|
def ignore_branches(*types)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
Deprecation.warn("`SimpleCov.ignore_branches` is deprecated. " \
|
|
14
|
+
"Replace with `coverage(:branch) { ignore #{types.map(&:inspect).join(", ")} }`.")
|
|
15
|
+
store_ignored_branches(types)
|
|
19
16
|
end
|
|
20
17
|
|
|
21
18
|
def ignored_branches
|
|
@@ -26,12 +23,10 @@ module SimpleCov
|
|
|
26
23
|
ignored_branches.include?(type)
|
|
27
24
|
end
|
|
28
25
|
|
|
29
|
-
# See `ignore_branches`. The only supported method-type token today
|
|
30
|
-
# is `:eval_generated`; see #1046 for the rationale.
|
|
31
26
|
def ignore_methods(*types)
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
27
|
+
Deprecation.warn("`SimpleCov.ignore_methods` is deprecated. " \
|
|
28
|
+
"Replace with `coverage(:method) { ignore #{types.map(&:inspect).join(", ")} }`.")
|
|
29
|
+
store_ignored_methods(types)
|
|
35
30
|
end
|
|
36
31
|
|
|
37
32
|
def ignored_methods
|
|
@@ -42,22 +37,36 @@ module SimpleCov
|
|
|
42
37
|
ignored_methods.include?(type)
|
|
43
38
|
end
|
|
44
39
|
|
|
45
|
-
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
# Variadic semantics: multiple calls union, duplicates are no-ops, unknown
|
|
43
|
+
# tokens raise.
|
|
44
|
+
def store_ignored_branches(types)
|
|
45
|
+
types.each { |type| raise_if_branch_type_unsupported(type) }
|
|
46
|
+
ignored_branches.concat(types).uniq!
|
|
47
|
+
ignored_branches
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def store_ignored_methods(types)
|
|
51
|
+
types.each { |type| raise_if_method_type_unsupported(type) }
|
|
52
|
+
ignored_methods.concat(types).uniq!
|
|
53
|
+
ignored_methods
|
|
54
|
+
end
|
|
46
55
|
|
|
47
56
|
def raise_if_branch_type_unsupported(type)
|
|
48
57
|
return if IGNORABLE_BRANCH_TYPES.member?(type)
|
|
49
58
|
|
|
50
|
-
raise
|
|
51
|
-
|
|
52
|
-
|
|
59
|
+
raise ConfigurationError,
|
|
60
|
+
"Unsupported branch type #{type.inspect} for `ignore_branches`. " \
|
|
61
|
+
"Supported values are #{IGNORABLE_BRANCH_TYPES.inspect}"
|
|
53
62
|
end
|
|
54
63
|
|
|
55
64
|
def raise_if_method_type_unsupported(type)
|
|
56
65
|
return if IGNORABLE_METHOD_TYPES.member?(type)
|
|
57
66
|
|
|
58
|
-
raise
|
|
59
|
-
|
|
60
|
-
|
|
67
|
+
raise ConfigurationError,
|
|
68
|
+
"Unsupported method type #{type.inspect} for `ignore_methods`. " \
|
|
69
|
+
"Supported values are #{IGNORABLE_METHOD_TYPES.inspect}"
|
|
61
70
|
end
|
|
62
71
|
end
|
|
63
72
|
end
|
|
@@ -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
|