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
|
@@ -2,10 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
module SimpleCov
|
|
4
4
|
module ExitCodes
|
|
5
|
-
# Runs every coverage check against the result and returns the exit
|
|
6
|
-
# code from the first failing one (or SUCCESS if all pass).
|
|
7
5
|
module ExitCodeHandling
|
|
8
|
-
|
|
6
|
+
extend self
|
|
9
7
|
|
|
10
8
|
def call(result, coverage_limits:)
|
|
11
9
|
checks = coverage_checks(result, coverage_limits)
|
|
@@ -15,21 +13,36 @@ module SimpleCov
|
|
|
15
13
|
failing_check.report if SimpleCov.print_errors
|
|
16
14
|
failing_check.exit_code
|
|
17
15
|
else
|
|
18
|
-
|
|
16
|
+
SUCCESS
|
|
19
17
|
end
|
|
20
18
|
end
|
|
21
19
|
|
|
22
20
|
def coverage_checks(result, coverage_limits)
|
|
23
21
|
[
|
|
24
22
|
MinimumOverallCoverageCheck.new(result, coverage_limits.minimum_coverage),
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
),
|
|
23
|
+
minimum_by_file_check(result, coverage_limits),
|
|
24
|
+
BaselineCheck.new(result, coverage_limits.baseline),
|
|
28
25
|
MinimumCoverageByGroupCheck.new(result, coverage_limits.minimum_coverage_by_group),
|
|
29
26
|
MaximumOverallCoverageCheck.new(result, coverage_limits.maximum_coverage),
|
|
30
|
-
MaximumCoverageDropCheck.new(result, coverage_limits.maximum_coverage_drop)
|
|
27
|
+
MaximumCoverageDropCheck.new(result, coverage_limits.maximum_coverage_drop),
|
|
28
|
+
MaximumMissedCheck.new(result, coverage_limits.maximum_missed),
|
|
29
|
+
maximum_missed_per_file_check(result, coverage_limits)
|
|
31
30
|
]
|
|
32
31
|
end
|
|
32
|
+
|
|
33
|
+
def minimum_by_file_check(result, coverage_limits)
|
|
34
|
+
MinimumCoverageByFileCheck.new(
|
|
35
|
+
result, coverage_limits.minimum_coverage_by_file, coverage_limits.minimum_coverage_by_file_overrides,
|
|
36
|
+
baseline: coverage_limits.baseline
|
|
37
|
+
)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def maximum_missed_per_file_check(result, coverage_limits)
|
|
41
|
+
MaximumMissedPerFileCheck.new(
|
|
42
|
+
result, coverage_limits.maximum_missed_per_file, coverage_limits.maximum_missed_per_file_overrides,
|
|
43
|
+
baseline: coverage_limits.baseline
|
|
44
|
+
)
|
|
45
|
+
end
|
|
33
46
|
end
|
|
34
47
|
end
|
|
35
48
|
end
|
|
@@ -2,24 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
module SimpleCov
|
|
4
4
|
module ExitCodes
|
|
5
|
-
# Fails when any coverage criterion has dropped by more than the
|
|
6
|
-
# configured maximum since the last recorded run.
|
|
7
5
|
class MaximumCoverageDropCheck < Check
|
|
8
6
|
def exit_code
|
|
9
|
-
|
|
7
|
+
MAXIMUM_COVERAGE_DROP
|
|
10
8
|
end
|
|
11
9
|
|
|
12
|
-
|
|
10
|
+
private
|
|
13
11
|
|
|
14
12
|
def compute_violations
|
|
15
|
-
|
|
13
|
+
CoverageViolations.maximum_drop(result, thresholds)
|
|
16
14
|
end
|
|
17
15
|
|
|
18
|
-
# The
|
|
19
|
-
#
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
ExitCodes.print_error SimpleCov::Color.colorize(message_for(violation), :red)
|
|
16
|
+
# The drop percent is a delta, not a coverage level, so it has no natural
|
|
17
|
+
# green/yellow/red mapping and the whole line goes red.
|
|
18
|
+
def violation_lines(violation)
|
|
19
|
+
[Color.colorize(message_for(violation), :red)]
|
|
23
20
|
end
|
|
24
21
|
|
|
25
22
|
def message_for(violation)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SimpleCov
|
|
4
|
+
module ExitCodes
|
|
5
|
+
class MaximumMissedCheck < Check
|
|
6
|
+
def exit_code
|
|
7
|
+
MINIMUM_COVERAGE
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
def compute_violations
|
|
13
|
+
CoverageViolations.maximum_missed(result, thresholds)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def violation_lines(violation)
|
|
17
|
+
[format(
|
|
18
|
+
"Missed %<units>s (%<actual>d) exceed the configured maximum_missed (%<maximum>d).",
|
|
19
|
+
units: UNITS.fetch(SimpleCov.coverage_statistics_key(violation.fetch(:criterion))),
|
|
20
|
+
actual: violation.fetch(:actual),
|
|
21
|
+
maximum: violation.fetch(:maximum)
|
|
22
|
+
)]
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SimpleCov
|
|
4
|
+
module ExitCodes
|
|
5
|
+
class MaximumMissedPerFileCheck < Check
|
|
6
|
+
def initialize(result, maximum_missed_per_file, overrides = {}, baseline: nil)
|
|
7
|
+
super(result, maximum_missed_per_file)
|
|
8
|
+
@overrides = overrides
|
|
9
|
+
@baseline = baseline
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def exit_code
|
|
13
|
+
MINIMUM_COVERAGE
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def compute_violations
|
|
19
|
+
CoverageViolations.maximum_missed_by_file(result, thresholds, @overrides, baseline: @baseline)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def violation_lines(violation)
|
|
23
|
+
[format(
|
|
24
|
+
"Missed %<units>s (%<actual>d) exceed the configured maximum_missed_per_file (%<maximum>d) " \
|
|
25
|
+
"in %<filename>s.",
|
|
26
|
+
units: UNITS.fetch(SimpleCov.coverage_statistics_key(violation.fetch(:criterion))),
|
|
27
|
+
actual: violation.fetch(:actual),
|
|
28
|
+
maximum: violation.fetch(:maximum),
|
|
29
|
+
filename: violation.fetch(:project_filename)
|
|
30
|
+
)]
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -2,30 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
module SimpleCov
|
|
4
4
|
module ExitCodes
|
|
5
|
-
# Fails when the overall (project-wide) coverage for any criterion is
|
|
6
|
-
# above the configured maximum. Pair with
|
|
7
|
-
# `SimpleCov::ExitCodes::MinimumOverallCoverageCheck` (or use
|
|
8
|
-
# `SimpleCov.expected_coverage`) to pin coverage to an exact value
|
|
9
|
-
# and surface unexpected increases instead of silently absorbing them.
|
|
10
5
|
class MaximumOverallCoverageCheck < Check
|
|
11
6
|
def exit_code
|
|
12
|
-
|
|
7
|
+
MAXIMUM_COVERAGE
|
|
13
8
|
end
|
|
14
9
|
|
|
15
|
-
|
|
10
|
+
private
|
|
16
11
|
|
|
17
12
|
def compute_violations
|
|
18
|
-
|
|
13
|
+
CoverageViolations.maximum_overall(result, thresholds)
|
|
19
14
|
end
|
|
20
15
|
|
|
21
|
-
def
|
|
22
|
-
|
|
16
|
+
def violation_lines(violation)
|
|
17
|
+
[format(
|
|
23
18
|
"%<criterion>s coverage (%<actual>s) is above the expected maximum coverage (%<expected>.2f%%). " \
|
|
24
19
|
"Time to bump the threshold!",
|
|
25
20
|
criterion: violation.fetch(:criterion).capitalize,
|
|
26
|
-
actual:
|
|
21
|
+
actual: Color.colorize_percent(violation.fetch(:actual)),
|
|
27
22
|
expected: violation.fetch(:expected)
|
|
28
|
-
)
|
|
23
|
+
)]
|
|
29
24
|
end
|
|
30
25
|
end
|
|
31
26
|
end
|
|
@@ -2,33 +2,34 @@
|
|
|
2
2
|
|
|
3
3
|
module SimpleCov
|
|
4
4
|
module ExitCodes
|
|
5
|
-
# Fails when any individual file falls below the configured minimum
|
|
6
|
-
# coverage for any criterion.
|
|
7
5
|
class MinimumCoverageByFileCheck < Check
|
|
8
|
-
|
|
6
|
+
# `baseline:` exempts the file-and-criterion pairs it covers: those answer to
|
|
7
|
+
# their own floor via `BaselineCheck` instead.
|
|
8
|
+
def initialize(result, minimum_coverage_by_file, overrides = {}, baseline: nil)
|
|
9
9
|
super(result, minimum_coverage_by_file)
|
|
10
10
|
@overrides = overrides
|
|
11
|
+
@baseline = baseline
|
|
11
12
|
end
|
|
12
13
|
|
|
13
14
|
def exit_code
|
|
14
|
-
|
|
15
|
+
MINIMUM_COVERAGE
|
|
15
16
|
end
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
private
|
|
18
19
|
|
|
19
20
|
def compute_violations
|
|
20
|
-
|
|
21
|
+
CoverageViolations.minimum_by_file(result, thresholds, @overrides, baseline: @baseline)
|
|
21
22
|
end
|
|
22
23
|
|
|
23
|
-
def
|
|
24
|
-
|
|
24
|
+
def violation_lines(violation)
|
|
25
|
+
[format(
|
|
25
26
|
"%<criterion>s coverage by file (%<actual>s) is below the expected minimum coverage " \
|
|
26
27
|
"(%<expected>.2f%%) in %<filename>s.",
|
|
27
28
|
criterion: violation.fetch(:criterion).capitalize,
|
|
28
|
-
actual:
|
|
29
|
+
actual: Color.colorize_percent(violation.fetch(:actual)),
|
|
29
30
|
expected: violation.fetch(:expected),
|
|
30
31
|
filename: violation.fetch(:project_filename)
|
|
31
|
-
)
|
|
32
|
+
)]
|
|
32
33
|
end
|
|
33
34
|
end
|
|
34
35
|
end
|
|
@@ -2,28 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
module SimpleCov
|
|
4
4
|
module ExitCodes
|
|
5
|
-
# Fails when any configured group falls below its minimum coverage
|
|
6
|
-
# threshold for any criterion.
|
|
7
5
|
class MinimumCoverageByGroupCheck < Check
|
|
8
6
|
def exit_code
|
|
9
|
-
|
|
7
|
+
MINIMUM_COVERAGE
|
|
10
8
|
end
|
|
11
9
|
|
|
12
|
-
|
|
10
|
+
private
|
|
13
11
|
|
|
14
12
|
def compute_violations
|
|
15
|
-
|
|
13
|
+
CoverageViolations.minimum_by_group(result, thresholds)
|
|
16
14
|
end
|
|
17
15
|
|
|
18
|
-
def
|
|
19
|
-
|
|
16
|
+
def violation_lines(violation)
|
|
17
|
+
[format(
|
|
20
18
|
"%<criterion>s coverage by group (%<actual>s) is below the expected minimum coverage " \
|
|
21
19
|
"(%<expected>.2f%%) in %<group_name>s.",
|
|
22
20
|
criterion: violation.fetch(:criterion).capitalize,
|
|
23
|
-
actual:
|
|
21
|
+
actual: Color.colorize_percent(violation.fetch(:actual)),
|
|
24
22
|
expected: violation.fetch(:expected),
|
|
25
23
|
group_name: violation.fetch(:group_name)
|
|
26
|
-
)
|
|
24
|
+
)]
|
|
27
25
|
end
|
|
28
26
|
end
|
|
29
27
|
end
|
|
@@ -2,53 +2,52 @@
|
|
|
2
2
|
|
|
3
3
|
module SimpleCov
|
|
4
4
|
module ExitCodes
|
|
5
|
-
# Fails when the overall (project-wide) coverage for any criterion is
|
|
6
|
-
# below the configured minimum.
|
|
7
5
|
class MinimumOverallCoverageCheck < Check
|
|
8
6
|
def exit_code
|
|
9
|
-
|
|
7
|
+
MINIMUM_COVERAGE
|
|
10
8
|
end
|
|
11
9
|
|
|
12
|
-
|
|
10
|
+
private
|
|
13
11
|
|
|
14
12
|
WORST_FILES_LIMIT = 5
|
|
15
13
|
private_constant :WORST_FILES_LIMIT
|
|
16
14
|
|
|
17
15
|
def compute_violations
|
|
18
|
-
|
|
16
|
+
CoverageViolations.minimum_overall(result, thresholds)
|
|
19
17
|
end
|
|
20
18
|
|
|
21
|
-
def
|
|
19
|
+
def violation_lines(violation)
|
|
22
20
|
criterion = violation.fetch(:criterion)
|
|
23
|
-
|
|
24
|
-
ExitCodes.print_error format(
|
|
21
|
+
headline = format(
|
|
25
22
|
"%<criterion>s coverage (%<actual>s) is below the expected minimum coverage (%<expected>.2f%%).",
|
|
26
23
|
criterion: criterion.capitalize,
|
|
27
|
-
actual:
|
|
24
|
+
actual: Color.colorize_percent(violation.fetch(:actual)),
|
|
28
25
|
expected: violation.fetch(:expected)
|
|
29
26
|
)
|
|
30
|
-
|
|
27
|
+
[headline, *worst_files_lines(criterion)]
|
|
31
28
|
end
|
|
32
29
|
|
|
33
|
-
def
|
|
30
|
+
def worst_files_lines(criterion)
|
|
34
31
|
worst = worst_files_for(criterion)
|
|
35
|
-
return if worst.empty?
|
|
32
|
+
return [] if worst.empty?
|
|
36
33
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
ExitCodes.print_error format(
|
|
34
|
+
[" Lowest-coverage files (#{criterion}):"] + worst.map do |path, percent|
|
|
35
|
+
format(
|
|
40
36
|
" %<percent>s %<path>s",
|
|
41
|
-
percent:
|
|
37
|
+
percent: Color.colorize_percent(percent, format("%6.2f%%", percent)),
|
|
42
38
|
path: path
|
|
43
39
|
)
|
|
44
40
|
end
|
|
45
41
|
end
|
|
46
42
|
|
|
43
|
+
# The list is a hint at where to add tests, so a fully covered file has no
|
|
44
|
+
# business on it: without the filter, files at 100% padded the list out to
|
|
45
|
+
# its five entries (#1286).
|
|
47
46
|
def worst_files_for(criterion)
|
|
48
47
|
stats_key = SimpleCov.coverage_statistics_key(criterion)
|
|
49
48
|
with_stats = result.files.filter_map do |source_file|
|
|
50
49
|
stats = source_file.coverage_statistics[stats_key]
|
|
51
|
-
[source_file.project_filename, stats.percent] if stats
|
|
50
|
+
[source_file.project_filename, stats.percent] if stats && stats.percent < 100
|
|
52
51
|
end
|
|
53
52
|
with_stats.sort_by { |_path, percent| percent }.first(WORST_FILES_LIMIT)
|
|
54
53
|
end
|
data/lib/simplecov/exit_codes.rb
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module SimpleCov
|
|
4
|
-
# Exit statuses SimpleCov sets when coverage checks fail, and the output
|
|
5
|
-
# helper the enforcement machinery reports through.
|
|
6
4
|
module ExitCodes
|
|
7
5
|
SUCCESS = 0
|
|
8
6
|
EXCEPTION = 1
|
|
@@ -10,25 +8,33 @@ module SimpleCov
|
|
|
10
8
|
MAXIMUM_COVERAGE_DROP = 3
|
|
11
9
|
MAXIMUM_COVERAGE = 4
|
|
12
10
|
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
#
|
|
11
|
+
# The plural unit each criterion's misses count in, for the checks that report
|
|
12
|
+
# counts rather than percents.
|
|
13
|
+
UNITS = {line: "lines", branch: "branches", method: "methods"}.freeze
|
|
14
|
+
|
|
15
|
+
# Threshold-violation reports and exit-status notices are the output of the
|
|
16
|
+
# enforcement feature, not Ruby warnings: routing them through `Kernel#warn`
|
|
17
|
+
# made `-W0` swallow the explanation for a failing exit code, let
|
|
18
|
+
# `Warning.warn` hooks intercept them mid-at_exit, and fed colorized text to
|
|
18
19
|
# warning logs. `print_errors false` remains the intended opt-out.
|
|
20
|
+
#
|
|
21
|
+
# A parallel runner can close a worker's stderr before its at_exit hooks
|
|
22
|
+
# run, hence the rescue.
|
|
19
23
|
def self.print_error(message)
|
|
20
24
|
$stderr.puts message # rubocop:disable Style/StderrPuts
|
|
21
25
|
rescue IOError
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
# the exit status even when its explanation cannot be printed.
|
|
26
|
+
# The violation still has to set the exit status even when its
|
|
27
|
+
# explanation cannot be printed.
|
|
25
28
|
end
|
|
26
29
|
end
|
|
27
30
|
end
|
|
28
31
|
|
|
29
32
|
require_relative "exit_codes/check"
|
|
33
|
+
require_relative "exit_codes/baseline_check"
|
|
30
34
|
require_relative "exit_codes/exit_code_handling"
|
|
31
35
|
require_relative "exit_codes/maximum_coverage_drop_check"
|
|
36
|
+
require_relative "exit_codes/maximum_missed_check"
|
|
37
|
+
require_relative "exit_codes/maximum_missed_per_file_check"
|
|
32
38
|
require_relative "exit_codes/maximum_overall_coverage_check"
|
|
33
39
|
require_relative "exit_codes/minimum_coverage_by_file_check"
|
|
34
40
|
require_relative "exit_codes/minimum_coverage_by_group_check"
|
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
require "English"
|
|
4
4
|
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
# report, and
|
|
5
|
+
#
|
|
6
|
+
# `at_exit` orchestration: post-suite report generation, threshold checks,
|
|
7
|
+
# deferral when a sibling subprocess already wrote a fresher report, and
|
|
8
|
+
# exit-status propagation.
|
|
9
|
+
#
|
|
8
10
|
module SimpleCov
|
|
9
11
|
class << self
|
|
10
12
|
# @api private
|
|
@@ -14,38 +16,59 @@ module SimpleCov
|
|
|
14
16
|
:minimum_coverage_by_file_overrides,
|
|
15
17
|
:minimum_coverage_by_group,
|
|
16
18
|
:maximum_coverage,
|
|
17
|
-
:maximum_coverage_drop
|
|
19
|
+
:maximum_coverage_drop,
|
|
20
|
+
:maximum_missed,
|
|
21
|
+
:maximum_missed_per_file,
|
|
22
|
+
:maximum_missed_per_file_overrides,
|
|
23
|
+
:baseline
|
|
18
24
|
)
|
|
19
25
|
|
|
20
26
|
def at_exit_behavior
|
|
21
|
-
#
|
|
22
|
-
return
|
|
27
|
+
# A different process than the one that called start; don't interfere.
|
|
28
|
+
return unless started_in_this_process?
|
|
23
29
|
|
|
24
|
-
#
|
|
25
|
-
#
|
|
30
|
+
# Coverage is no longer running (someone stopped it manually, or a test
|
|
31
|
+
# consumed the result), so don't run exit tasks.
|
|
26
32
|
return unless Coverage.running?
|
|
27
33
|
|
|
28
|
-
#
|
|
29
|
-
#
|
|
30
|
-
#
|
|
34
|
+
# Captured BEFORE the deferral probe: its freshness check rescues
|
|
35
|
+
# filesystem errors, and completing any rescue inside an at_exit handler
|
|
36
|
+
# resets $ERROR_INFO to nil.
|
|
31
37
|
error_exit_status = exit_status_from_exception
|
|
32
38
|
|
|
33
39
|
# Stand down when we'd only clobber a fresher report (#581).
|
|
34
40
|
return if defer_to_existing_report?
|
|
35
41
|
|
|
36
|
-
|
|
42
|
+
run_exit_tasks!(error_exit_status)
|
|
37
43
|
end
|
|
38
44
|
|
|
39
|
-
# @api private
|
|
40
|
-
#
|
|
41
|
-
|
|
45
|
+
# @api private -- everything the end of a measured run does, answered as the
|
|
46
|
+
# exit status the process should end with rather than performed as an exit.
|
|
47
|
+
# The one side effect left to the caller is ending the process, which is
|
|
48
|
+
# what makes this whole path answerable from inside a test.
|
|
49
|
+
def run_exit_tasks(error_exit_status = exit_status_from_exception)
|
|
42
50
|
at_exit.call
|
|
43
51
|
|
|
44
|
-
|
|
45
|
-
|
|
52
|
+
if previous_error?(error_exit_status)
|
|
53
|
+
report_previous_error
|
|
54
|
+
error_exit_status
|
|
55
|
+
elsif ready_to_process_results?
|
|
56
|
+
report_processing_failure(process_result(result))
|
|
57
|
+
else
|
|
58
|
+
ExitCodes::SUCCESS
|
|
59
|
+
end
|
|
46
60
|
end
|
|
47
61
|
|
|
48
|
-
# @api private
|
|
62
|
+
# @api private -- the exiting adapter over `run_exit_tasks`, called from the
|
|
63
|
+
# at_exit block (which pre-captures the status, see there) and by `collate`.
|
|
64
|
+
def run_exit_tasks!(error_exit_status = exit_status_from_exception)
|
|
65
|
+
status = run_exit_tasks(error_exit_status)
|
|
66
|
+
|
|
67
|
+
# Force exit with stored status (#5).
|
|
68
|
+
Kernel.exit(status) unless successful?(status)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# @api private
|
|
49
72
|
def exit_status_from_exception
|
|
50
73
|
@exit_exception = $ERROR_INFO
|
|
51
74
|
return nil unless @exit_exception
|
|
@@ -53,76 +76,88 @@ module SimpleCov
|
|
|
53
76
|
if @exit_exception.is_a?(SystemExit)
|
|
54
77
|
@exit_exception.status
|
|
55
78
|
else
|
|
56
|
-
|
|
79
|
+
ExitCodes::EXCEPTION
|
|
57
80
|
end
|
|
58
81
|
end
|
|
59
82
|
|
|
60
|
-
# @api private
|
|
61
|
-
# accepts it. test_unit sets status 0 on success, so SUCCESS must
|
|
62
|
-
#
|
|
83
|
+
# @api private -- strict boolean so rspec-mocks 4's predicate matcher
|
|
84
|
+
# accepts it. test_unit sets status 0 on success, so SUCCESS must also be
|
|
85
|
+
# treated as "not a previous error".
|
|
63
86
|
def previous_error?(error_exit_status)
|
|
64
|
-
|
|
87
|
+
return false unless error_exit_status
|
|
88
|
+
|
|
89
|
+
!successful?(error_exit_status)
|
|
65
90
|
end
|
|
66
91
|
|
|
67
|
-
# @api private
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
92
|
+
# @api private -- the notice alone; the status it accompanies is the
|
|
93
|
+
# caller's to answer with.
|
|
94
|
+
def report_previous_error
|
|
95
|
+
return unless print_errors
|
|
96
|
+
|
|
97
|
+
ExitCodes.print_error Color.colorize(
|
|
98
|
+
"Stopped processing SimpleCov as a previous error not related to SimpleCov has been detected",
|
|
99
|
+
:yellow
|
|
100
|
+
)
|
|
76
101
|
end
|
|
77
102
|
|
|
78
|
-
# @api private
|
|
79
|
-
#
|
|
80
|
-
# `wait_for_other_processes` confirmed every sibling reported.
|
|
81
|
-
#
|
|
82
|
-
#
|
|
83
|
-
# would surface a spurious "below minimum" violation about the
|
|
84
|
-
# missing slice rather than a real shortfall.
|
|
103
|
+
# @api private. The process that owns final merge processing is the only one
|
|
104
|
+
# that reports against thresholds, and only when its
|
|
105
|
+
# `wait_for_other_processes` confirmed every sibling reported. When the wait
|
|
106
|
+
# times out the merged total is partial, and comparing it against the
|
|
107
|
+
# thresholds would surface a spurious violation about the missing slice.
|
|
85
108
|
def ready_to_process_results?
|
|
86
109
|
merge_finalization_owner? && result? &&
|
|
87
110
|
(collating_result? || parallel_results_complete?)
|
|
88
111
|
end
|
|
89
112
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if print_errors
|
|
97
|
-
ExitCodes.print_error SimpleCov::Color.colorize(
|
|
113
|
+
# @api private -- answers the status it was handed, explaining it first when
|
|
114
|
+
# it is a coverage failure worth explaining.
|
|
115
|
+
def report_processing_failure(exit_status)
|
|
116
|
+
if exit_status.positive? && print_errors
|
|
117
|
+
ExitCodes.print_error Color.colorize(
|
|
98
118
|
"SimpleCov failed with exit #{exit_status} due to a coverage related error", :red
|
|
99
119
|
)
|
|
100
120
|
end
|
|
101
|
-
|
|
121
|
+
exit_status
|
|
102
122
|
end
|
|
103
123
|
|
|
104
|
-
# @api private
|
|
124
|
+
# @api private. The history entry is recorded on the same successful-run
|
|
125
|
+
# condition `.last_run.json` uses, so a run that failed its thresholds
|
|
126
|
+
# becomes neither the drop baseline nor a data point in the trend.
|
|
105
127
|
def process_result(result)
|
|
106
128
|
result_exit_status = result_exit_status(result)
|
|
107
|
-
|
|
129
|
+
if successful?(result_exit_status)
|
|
130
|
+
write_last_run(result)
|
|
131
|
+
History.record(result)
|
|
132
|
+
end
|
|
108
133
|
result_exit_status
|
|
109
134
|
end
|
|
110
135
|
|
|
136
|
+
# mutant:disable -- a pid is an Integer, and Integers answer ==, eql? and
|
|
137
|
+
# equal? alike for the same value.
|
|
138
|
+
def started_in_this_process?
|
|
139
|
+
SimpleCov.pid == Process.pid
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Asked as a predicate rather than compared against `ExitCodes::SUCCESS`,
|
|
143
|
+
# because two equal Integers are equal through every spelling of the
|
|
144
|
+
# comparison.
|
|
145
|
+
def successful?(status)
|
|
146
|
+
status.zero?
|
|
147
|
+
end
|
|
148
|
+
|
|
111
149
|
def result_exit_status(result)
|
|
112
150
|
ExitCodes::ExitCodeHandling.call(result, coverage_limits: build_coverage_limits)
|
|
113
151
|
end
|
|
114
152
|
|
|
115
|
-
|
|
153
|
+
private
|
|
116
154
|
|
|
155
|
+
# Every CoverageLimits member is named after the configuration reader that
|
|
156
|
+
# supplies it, so a new limit only has to be added to the Data definition
|
|
157
|
+
# and the checks.
|
|
117
158
|
def build_coverage_limits
|
|
118
|
-
CoverageLimits.
|
|
119
|
-
|
|
120
|
-
minimum_coverage_by_file: minimum_coverage_by_file,
|
|
121
|
-
minimum_coverage_by_file_overrides: minimum_coverage_by_file_overrides,
|
|
122
|
-
minimum_coverage_by_group: minimum_coverage_by_group,
|
|
123
|
-
maximum_coverage: maximum_coverage,
|
|
124
|
-
maximum_coverage_drop: maximum_coverage_drop
|
|
125
|
-
)
|
|
159
|
+
limits = CoverageLimits.members.to_h { |name| [name, public_send(name)] } #: Hash[Symbol, untyped]
|
|
160
|
+
CoverageLimits.new(**limits)
|
|
126
161
|
end
|
|
127
162
|
end
|
|
128
163
|
end
|