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
|
@@ -6,44 +6,24 @@ require_relative "interned_counts"
|
|
|
6
6
|
|
|
7
7
|
module SimpleCov
|
|
8
8
|
module Combine
|
|
9
|
-
#
|
|
10
|
-
# Combine different method coverage results on a single file.
|
|
11
|
-
#
|
|
12
|
-
# Should be called through `CoverageAccumulator`.
|
|
13
9
|
module MethodsCombiner
|
|
14
|
-
|
|
10
|
+
extend self
|
|
15
11
|
|
|
16
|
-
#
|
|
17
|
-
# Return merged methods or the existing methods if other is missing.
|
|
18
|
-
#
|
|
19
12
|
# Method coverage maps `[class, name, start_line, start_col, end_line,
|
|
20
|
-
# end_col]` keys to hit counts. Keys are matched on their
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
# full key would keep both, letting a never-called copy's 0 shadow a
|
|
27
|
-
# covered method after merge (issue #1234). Combining sums the hit
|
|
28
|
-
# counts for matching methods and preserves methods that only appear
|
|
29
|
-
# in one result.
|
|
30
|
-
#
|
|
31
|
-
# @return [Hash]
|
|
32
|
-
#
|
|
13
|
+
# end_col]` keys to hit counts. Keys are matched on their source identity,
|
|
14
|
+
# the location alone, because Ruby records one entry per defined method: the
|
|
15
|
+
# same `define_method` block defined onto different classes, or under
|
|
16
|
+
# different names, in different processes arrives with different receivers
|
|
17
|
+
# for the same source method, and matching on the full key would keep both,
|
|
18
|
+
# letting a never-called copy's 0 shadow a covered method (#1234).
|
|
33
19
|
def combine(coverage_a, coverage_b)
|
|
34
20
|
merged = absorb({}, coverage_a) #: Hash[untyped, [untyped, Integer]]
|
|
35
21
|
materialize(absorb(merged, coverage_b))
|
|
36
22
|
end
|
|
37
23
|
|
|
38
|
-
# Folds `coverage` into `target`, an interned table keyed by method
|
|
39
|
-
# source identity. See `BranchesCombiner.absorb` for why a fold keeps
|
|
40
|
-
# its accumulator interned.
|
|
41
|
-
#
|
|
42
24
|
# A `nil` `target` stays `nil` until some resultset actually carries
|
|
43
|
-
# methods, so a merge can tell "no method data anywhere" apart from
|
|
44
|
-
#
|
|
45
|
-
#
|
|
46
|
-
# @return [Hash, nil] `target`, created on the first coverage seen
|
|
25
|
+
# methods, so a merge can tell "no method data anywhere" apart from "method
|
|
26
|
+
# data that covers nothing".
|
|
47
27
|
def absorb(target, coverage)
|
|
48
28
|
return target unless coverage
|
|
49
29
|
|
|
@@ -51,16 +31,10 @@ module SimpleCov
|
|
|
51
31
|
InternedCounts.absorb_counts(target, coverage, identities)
|
|
52
32
|
end
|
|
53
33
|
|
|
54
|
-
# Turns an interned table back into the tuple-keyed hash the rest of
|
|
55
|
-
# SimpleCov reads. Done once, at the end of a fold.
|
|
56
|
-
#
|
|
57
|
-
# @return [Hash]
|
|
58
34
|
def materialize(target)
|
|
59
35
|
target.values.to_h
|
|
60
36
|
end
|
|
61
37
|
|
|
62
|
-
# Bounded by the project's method count, like `RubyDataParser`'s parse
|
|
63
|
-
# cache.
|
|
64
38
|
def identities
|
|
65
39
|
@identities ||= IdentityInterner.build { |key| source_identity(key) }
|
|
66
40
|
end
|
|
@@ -4,29 +4,12 @@ require_relative "coverage_accumulator"
|
|
|
4
4
|
|
|
5
5
|
module SimpleCov
|
|
6
6
|
module Combine
|
|
7
|
-
# There might be reports from different kinds of tests,
|
|
8
|
-
# e.g. RSpec and Cucumber. We need to combine their results
|
|
9
|
-
# into unified one. This class does that.
|
|
10
|
-
# To unite the results on file basis, it leverages
|
|
11
|
-
# the combine of lines and branches inside each file within given results.
|
|
12
7
|
module ResultsCombiner
|
|
13
|
-
|
|
8
|
+
extend self
|
|
14
9
|
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
# ==> CoverageAccumulator::MergedFile: hold one file's merged coverage.
|
|
19
|
-
# ===> LinesCombiner: combine lines results.
|
|
20
|
-
# ===> BranchesCombiner: combine branches results.
|
|
21
|
-
# ===> MethodsCombiner: combine methods results.
|
|
22
|
-
#
|
|
23
|
-
# Callers that read their results one at a time (`ResultMerger`, to
|
|
24
|
-
# keep a big CI run's resultsets out of memory all at once) should
|
|
25
|
-
# drive a `CoverageAccumulator` directly instead of collecting the
|
|
26
|
-
# results to pass here.
|
|
27
|
-
#
|
|
28
|
-
# @return [Hash]
|
|
29
|
-
#
|
|
10
|
+
# Callers that read their results one at a time, to keep a big CI run's
|
|
11
|
+
# resultsets out of memory at once, should drive a `CoverageAccumulator`
|
|
12
|
+
# directly instead of collecting the results to pass here.
|
|
30
13
|
def combine(*results)
|
|
31
14
|
accumulator = CoverageAccumulator.new
|
|
32
15
|
results.each { |result| accumulator.absorb(result) }
|
data/lib/simplecov/combine.rb
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module SimpleCov
|
|
4
|
-
# Namespace for the pieces that combine coverage results.
|
|
5
|
-
#
|
|
6
|
-
# `CoverageAccumulator` is the entry point: it folds any number of
|
|
7
|
-
# resultsets together, delegating one file's lines, branches and methods to
|
|
8
|
-
# the combiner modules alongside it.
|
|
9
|
-
#
|
|
10
4
|
module Combine
|
|
11
5
|
end
|
|
12
6
|
end
|
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module SimpleCov
|
|
4
|
-
#
|
|
5
|
-
# Helper that tries to find out what test suite is running (for SimpleCov.command_name)
|
|
6
|
-
#
|
|
7
4
|
module CommandGuesser
|
|
8
5
|
class << self
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
# between rails unit/functional/integration tests impossible without this cached
|
|
13
|
-
# item.
|
|
6
|
+
# The original command line call that invoked the test suite. This has to
|
|
7
|
+
# be stored as early as possible because rake and test/unit 2 have a habit
|
|
8
|
+
# of tampering with ARGV.
|
|
14
9
|
attr_reader :original_run_command
|
|
15
10
|
|
|
16
11
|
# Assigning the flattened command clears any separately recorded program
|
|
@@ -37,10 +32,10 @@ module SimpleCov
|
|
|
37
32
|
[framework, parallel_data].compact.join(" ")
|
|
38
33
|
end
|
|
39
34
|
|
|
40
|
-
|
|
35
|
+
private
|
|
41
36
|
|
|
42
|
-
# When parallel_tests (or a compatible runner) is driving the suite,
|
|
43
|
-
#
|
|
37
|
+
# When parallel_tests (or a compatible runner) is driving the suite, tag
|
|
38
|
+
# the command name with this worker's position in the pool.
|
|
44
39
|
def parallel_data
|
|
45
40
|
groups, number = ENV.values_at("PARALLEL_TEST_GROUPS", "TEST_ENV_NUMBER")
|
|
46
41
|
return unless groups && number
|
|
@@ -52,17 +47,14 @@ module SimpleCov
|
|
|
52
47
|
end
|
|
53
48
|
|
|
54
49
|
# The command is `"#{$PROGRAM_NAME} #{ARGV.join(' ')}"`, so a keyword only
|
|
55
|
-
# carries meaning when it begins a path segment or an argument. Matching
|
|
56
|
-
# as a bare substring lets any part of the
|
|
50
|
+
# carries meaning when it begins a path segment or an argument. Matching
|
|
51
|
+
# it as a bare substring lets any part of the interpreter path decide the
|
|
57
52
|
# suite name: a Ruby installed under `latest/bin` (as mise does) contains
|
|
58
|
-
# "test/", so every run through it was labelled "Unit Tests".
|
|
59
|
-
# applies within ARGV, where `spec/greatest/` used to match just as wrongly.
|
|
53
|
+
# "test/", so every run through it was labelled "Unit Tests".
|
|
60
54
|
#
|
|
61
55
|
# A backslash bounds a segment too, or a Windows `spec\foo_spec.rb` would
|
|
62
|
-
# lose a match
|
|
63
|
-
#
|
|
64
|
-
# goes unrecognised there, but that is how it has always been rather than
|
|
65
|
-
# something the anchoring took away.
|
|
56
|
+
# lose a match. The `test/` patterns below still spell their separator as
|
|
57
|
+
# `/` only, which is how it has always been.
|
|
66
58
|
SEGMENT_START = %r{(?<![^\s/\\])}
|
|
67
59
|
private_constant :SEGMENT_START
|
|
68
60
|
SEGMENT_END = %r{(?=[\s/\\]|\z)}
|
|
@@ -80,15 +72,16 @@ module SimpleCov
|
|
|
80
72
|
private_constant :COMMAND_LINE_FRAMEWORKS
|
|
81
73
|
|
|
82
74
|
def from_command_line_options
|
|
83
|
-
|
|
75
|
+
# A command that was never recorded matches nothing, which `match?`
|
|
76
|
+
# answers for a nil of its own accord.
|
|
77
|
+
COMMAND_LINE_FRAMEWORKS.find { |pattern, _| pattern.match?(original_run_command) }&.last
|
|
84
78
|
end
|
|
85
79
|
|
|
86
80
|
# Which binary was invoked outranks everything else on the command line.
|
|
87
81
|
# `rspec features` is an RSpec suite whose examples live in features/, not
|
|
88
82
|
# a Cucumber run, and only the executable name says so. Generic runners
|
|
89
83
|
# (`ruby`, `rake`, a bare `*_test.rb` script) are absent on purpose: they
|
|
90
|
-
# name no framework, so those runs fall through to the path patterns
|
|
91
|
-
# is what keeps the test/functional vs test/integration split working.
|
|
84
|
+
# name no framework, so those runs fall through to the path patterns.
|
|
92
85
|
EXECUTABLE_FRAMEWORKS = {
|
|
93
86
|
"rspec" => "RSpec",
|
|
94
87
|
"cucumber" => "Cucumber Features"
|
|
@@ -99,36 +92,29 @@ module SimpleCov
|
|
|
99
92
|
program = original_program_name
|
|
100
93
|
return unless program
|
|
101
94
|
|
|
102
|
-
#
|
|
95
|
+
# The extension is trimmed so Windows' rspec.bat is recognized too.
|
|
103
96
|
EXECUTABLE_FRAMEWORKS[File.basename(program, ".*")]
|
|
104
97
|
end
|
|
105
98
|
|
|
106
99
|
# Inner array literals after the first are flagged uncovered by Ruby's
|
|
107
|
-
# Coverage module even though the constant evaluates as a whole
|
|
100
|
+
# Coverage module even though the constant evaluates as a whole, a known
|
|
108
101
|
# quirk with multi-line array literals.
|
|
109
102
|
DEFINED_CONSTANT_FRAMEWORKS = [
|
|
110
|
-
["RSpec",
|
|
103
|
+
["RSpec", -> { defined?(::RSpec) }],
|
|
111
104
|
["Unit Tests", -> { defined?(Test::Unit) }], # simplecov:disable
|
|
112
|
-
["Minitest",
|
|
113
|
-
["MiniTest",
|
|
105
|
+
["Minitest", -> { defined?(::Minitest) }], # simplecov:disable
|
|
106
|
+
["MiniTest", -> { defined?(MiniTest) }] # simplecov:disable
|
|
114
107
|
].freeze
|
|
115
108
|
private_constant :DEFINED_CONSTANT_FRAMEWORKS
|
|
116
109
|
|
|
117
|
-
# If the command regexps fail, let's try checking defined constants.
|
|
118
110
|
def from_defined_constants
|
|
119
|
-
# simplecov:disable branch — first iter returns when ::RSpec is defined; later branches unreachable
|
|
120
111
|
DEFINED_CONSTANT_FRAMEWORKS.each { |name, defined_check| return name if defined_check.call }
|
|
121
|
-
# simplecov:enable branch
|
|
122
112
|
|
|
123
|
-
# TODO: Provide link to docs/wiki article
|
|
124
|
-
# simplecov:disable — only fires when no framework is detected, which
|
|
125
|
-
# is impossible while our own specs are running under rspec
|
|
126
113
|
warn(
|
|
127
114
|
"SimpleCov failed to recognize the test framework and/or suite used. " \
|
|
128
115
|
"Please specify manually using SimpleCov.command_name 'Unit Tests'."
|
|
129
116
|
)
|
|
130
117
|
"Unknown Test Framework"
|
|
131
|
-
# simplecov:enable
|
|
132
118
|
end
|
|
133
119
|
end
|
|
134
120
|
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SimpleCov
|
|
4
|
+
module Configuration
|
|
5
|
+
# The baseline's path, relative to `SimpleCov.root`; an absolute path is
|
|
6
|
+
# honored as given. The file's presence is the opt-in: generate it with
|
|
7
|
+
# `simplecov ratchet`, check it in, and the exit check holds every listed
|
|
8
|
+
# file to its own floor (#1268).
|
|
9
|
+
def baseline_file(path = nil)
|
|
10
|
+
return @baseline_file ||= Baseline::DEFAULT_FILENAME unless path
|
|
11
|
+
|
|
12
|
+
self.baseline_file = path
|
|
13
|
+
_ = @baseline_file
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def baseline_file=(path)
|
|
17
|
+
@baseline_file = path
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Memoized per resolved path so the exit check and the JSON formatter's
|
|
21
|
+
# errors section read the file once, while a `baseline_file` or `root` change
|
|
22
|
+
# between reads is still picked up.
|
|
23
|
+
def baseline
|
|
24
|
+
resolved = File.expand_path(baseline_file, root)
|
|
25
|
+
return @baseline if @baseline_path.eql?(resolved)
|
|
26
|
+
|
|
27
|
+
@baseline_path = resolved
|
|
28
|
+
@baseline = Baseline.read_if_exists(resolved)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -1,49 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module SimpleCov
|
|
4
|
-
# The `coverage` configuration method configures each coverage criterion
|
|
5
|
-
# (`:line`, `:branch`, `:method`, `:eval`) uniformly in one place: naming a
|
|
6
|
-
# criterion enables it,
|
|
7
|
-
# and every threshold is declared with the same syntax regardless of which
|
|
8
|
-
# criterion it applies to. Because the criterion is fixed by the enclosing
|
|
9
|
-
# `coverage` call, threshold values are always plain percentages — there is
|
|
10
|
-
# no per-criterion Hash competing with the value for a slot.
|
|
11
|
-
#
|
|
12
|
-
# SimpleCov.start do
|
|
13
|
-
# coverage :line do
|
|
14
|
-
# minimum 90
|
|
15
|
-
# minimum_per_file 80
|
|
16
|
-
# minimum_per_file 100, only: "app/mailers/request_mailer.rb"
|
|
17
|
-
# maximum_drop 5
|
|
18
|
-
# end
|
|
19
|
-
#
|
|
20
|
-
# coverage :branch, minimum: 80
|
|
21
|
-
# coverage :method, minimum: 100
|
|
22
|
-
# end
|
|
23
|
-
#
|
|
24
|
-
# Line coverage is enabled by default, so `coverage :line` is only needed to
|
|
25
|
-
# set line thresholds or options. Thresholds feed the same internal stores
|
|
26
|
-
# as the flat `minimum_coverage` family, so enforcement is unchanged.
|
|
27
4
|
module Configuration
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
# because it needs an `only:` target, so it's block-only.
|
|
31
|
-
COVERAGE_THRESHOLD_OPTIONS = %i[minimum maximum exact maximum_drop minimum_per_file].freeze
|
|
5
|
+
COVERAGE_THRESHOLD_OPTIONS = %i[minimum maximum exact maximum_drop ignore minimum_per_file
|
|
6
|
+
maximum_missed maximum_missed_per_file].freeze
|
|
32
7
|
|
|
33
|
-
#
|
|
34
|
-
# Configure (and, unless `enabled: false`, enable) a coverage criterion.
|
|
35
|
-
#
|
|
36
|
-
# Threshold options mirror the block verbs for one-liner use:
|
|
37
|
-
# coverage :branch, minimum: 80, maximum_drop: 5
|
|
38
|
-
#
|
|
39
|
-
# `primary: true` makes this the report's leading criterion (and the one a
|
|
40
|
-
# bare `minimum_coverage 90` targets). `oneshot: true` (valid only for
|
|
41
|
-
# `:line`) selects the faster oneshot-lines mode.
|
|
42
|
-
#
|
|
43
8
|
def coverage(criterion, primary: false, enabled: true, oneshot: false, **thresholds, &block)
|
|
44
9
|
criterion = enable_coverage_criterion(criterion, enabled: enabled, oneshot: oneshot)
|
|
45
|
-
# The cast admits :eval, which primary_coverage rejects at runtime
|
|
46
|
-
# (it is a standalone toggle, never in the enabled-criteria set).
|
|
10
|
+
# The cast admits :eval, which primary_coverage rejects at runtime.
|
|
47
11
|
primary_coverage(_ = criterion) if primary
|
|
48
12
|
|
|
49
13
|
configurator = CoverageCriterion.new(self, criterion)
|
|
@@ -53,25 +17,20 @@ module SimpleCov
|
|
|
53
17
|
criterion
|
|
54
18
|
end
|
|
55
19
|
|
|
56
|
-
|
|
20
|
+
private
|
|
57
21
|
|
|
58
|
-
# Forward the one-liner threshold keywords (`coverage :branch, minimum: 80`)
|
|
59
|
-
# to the matching `CoverageCriterion` verbs, rejecting anything that isn't a
|
|
60
|
-
# recognized threshold option.
|
|
61
22
|
def apply_threshold_options(configurator, options)
|
|
62
23
|
options.each do |verb, value|
|
|
63
24
|
unless COVERAGE_THRESHOLD_OPTIONS.include?(verb)
|
|
64
|
-
raise
|
|
65
|
-
|
|
66
|
-
|
|
25
|
+
raise ConfigurationError,
|
|
26
|
+
"Unknown `coverage` option #{verb.inspect}. " \
|
|
27
|
+
"Supported options are #{COVERAGE_THRESHOLD_OPTIONS.inspect}."
|
|
67
28
|
end
|
|
68
29
|
|
|
69
30
|
configurator.public_send(verb, value)
|
|
70
31
|
end
|
|
71
32
|
end
|
|
72
33
|
|
|
73
|
-
# Enable or disable the criterion (or its oneshot / eval variant) and
|
|
74
|
-
# return the criterion symbol that thresholds should be stored under.
|
|
75
34
|
def enable_coverage_criterion(criterion, enabled:, oneshot:)
|
|
76
35
|
criterion = resolve_criterion_variant(criterion, oneshot)
|
|
77
36
|
enabled ? enable_coverage(criterion) : disable_coverage(criterion)
|
|
@@ -81,27 +40,41 @@ module SimpleCov
|
|
|
81
40
|
def resolve_criterion_variant(criterion, oneshot)
|
|
82
41
|
return criterion unless oneshot
|
|
83
42
|
|
|
84
|
-
|
|
85
|
-
raise SimpleCov::ConfigurationError, "`oneshot: true` is only valid for `coverage :line`"
|
|
86
|
-
end
|
|
43
|
+
raise ConfigurationError, "`oneshot: true` is only valid for `coverage :line`" unless criterion.equal?(:line)
|
|
87
44
|
|
|
88
45
|
ONESHOT_LINE_COVERAGE_CRITERION
|
|
89
46
|
end
|
|
90
47
|
|
|
91
|
-
# @api private
|
|
92
|
-
# write the same `@minimum_coverage` / `@maximum_coverage` / ... hashes the
|
|
93
|
-
# flat threshold methods populate, so the exit-code checks are unchanged.
|
|
48
|
+
# @api private
|
|
94
49
|
def store_overall_threshold(setting, criterion, percent)
|
|
95
|
-
raise_on_invalid_coverage({criterion => percent}, setting
|
|
50
|
+
raise_on_invalid_coverage({criterion => percent}, setting)
|
|
96
51
|
public_send(setting)[criterion] = percent
|
|
97
52
|
end
|
|
98
53
|
|
|
54
|
+
def store_missed_cap(setting, criterion, count)
|
|
55
|
+
raise_if_criterion_disabled(criterion)
|
|
56
|
+
raise_on_invalid_missed_cap(count, setting)
|
|
57
|
+
public_send(setting)[criterion] = count
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def store_maximum_missed_per_file(criterion, count, target)
|
|
61
|
+
raise_if_criterion_disabled(criterion)
|
|
62
|
+
raise_on_invalid_missed_cap(count, "maximum_missed_per_file")
|
|
63
|
+
return maximum_missed_per_file[criterion] = count if target.nil?
|
|
64
|
+
|
|
65
|
+
unless target.is_a?(String) || target.is_a?(Regexp)
|
|
66
|
+
raise ConfigurationError, "`only:` must be a String path or Regexp, got #{target.inspect}"
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
(maximum_missed_per_file_overrides[target] ||= {})[criterion] = count
|
|
70
|
+
end
|
|
71
|
+
|
|
99
72
|
def store_minimum_per_file(criterion, percent, target)
|
|
100
73
|
raise_on_invalid_coverage({criterion => percent}, "minimum_coverage_by_file")
|
|
101
74
|
return minimum_coverage_by_file[criterion] = percent if target.nil?
|
|
102
75
|
|
|
103
76
|
unless target.is_a?(String) || target.is_a?(Regexp)
|
|
104
|
-
raise
|
|
77
|
+
raise ConfigurationError, "`only:` must be a String path or Regexp, got #{target.inspect}"
|
|
105
78
|
end
|
|
106
79
|
|
|
107
80
|
(minimum_coverage_by_file_overrides[target] ||= {})[criterion] = percent
|
|
@@ -109,17 +82,13 @@ module SimpleCov
|
|
|
109
82
|
|
|
110
83
|
def store_minimum_per_group(criterion, percent, group_name)
|
|
111
84
|
raise_on_invalid_coverage({criterion => percent}, "minimum_coverage_by_group")
|
|
112
|
-
#
|
|
113
|
-
# defined as `group "Models"` at check time
|
|
114
|
-
# Symbol key no lookup ever matches.
|
|
85
|
+
# Normalized like `group` does, so `per: group(:Models)` finds the group
|
|
86
|
+
# defined as `group "Models"` at check time.
|
|
115
87
|
(minimum_coverage_by_group[GroupNames.normalize(group_name)] ||= {})[criterion] = percent
|
|
116
88
|
end
|
|
117
89
|
|
|
118
90
|
#
|
|
119
|
-
# Receiver for a `coverage <criterion> do ... end` block.
|
|
120
|
-
# threshold for the single criterion the block configures, so the value is
|
|
121
|
-
# always a plain percentage (`minimum_per_file 100` is unambiguous) and the
|
|
122
|
-
# syntax is identical across line, branch, and method coverage.
|
|
91
|
+
# Receiver for a `coverage <criterion> do ... end` block.
|
|
123
92
|
#
|
|
124
93
|
class CoverageCriterion
|
|
125
94
|
def initialize(config, criterion)
|
|
@@ -127,46 +96,89 @@ module SimpleCov
|
|
|
127
96
|
@criterion = criterion
|
|
128
97
|
end
|
|
129
98
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
@config.
|
|
99
|
+
def minimum(percent, per: nil)
|
|
100
|
+
case per
|
|
101
|
+
when nil then @config.__send__(:store_overall_threshold, :minimum_coverage, @criterion, percent)
|
|
102
|
+
when :file then @config.__send__(:store_minimum_per_file, @criterion, percent, nil)
|
|
103
|
+
when String, Regexp then @config.__send__(:store_minimum_per_file, @criterion, percent, per)
|
|
104
|
+
when GroupTarget then @config.__send__(:store_minimum_per_group, @criterion, percent, per.name)
|
|
105
|
+
else raise_invalid_per(per)
|
|
106
|
+
end
|
|
133
107
|
end
|
|
134
108
|
|
|
135
|
-
# Overall maximum: fails the build if coverage rises above it. Paired with
|
|
136
|
-
# `minimum` (or via `exact`) this pins coverage so an unexpected jump fails.
|
|
137
109
|
def maximum(percent)
|
|
138
|
-
@config.
|
|
110
|
+
@config.__send__(:store_overall_threshold, :maximum_coverage, @criterion, percent)
|
|
139
111
|
end
|
|
140
112
|
|
|
141
|
-
# Pin coverage to an exact figure (sets both `minimum` and `maximum`).
|
|
142
113
|
def exact(percent)
|
|
143
114
|
minimum(percent)
|
|
144
115
|
maximum(percent)
|
|
145
116
|
end
|
|
146
117
|
|
|
147
|
-
# Maximum allowed drop between runs (`maximum_drop 0` refuses any drop).
|
|
148
118
|
def maximum_drop(percent)
|
|
149
|
-
@config.
|
|
119
|
+
@config.__send__(:store_overall_threshold, :maximum_coverage_drop, @criterion, percent)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def maximum_missed(count, per: nil)
|
|
123
|
+
case per
|
|
124
|
+
when nil then @config.__send__(:store_missed_cap, :maximum_missed, @criterion, count)
|
|
125
|
+
when :file then @config.__send__(:store_maximum_missed_per_file, @criterion, count, nil)
|
|
126
|
+
when String, Regexp then @config.__send__(:store_maximum_missed_per_file, @criterion, count, per)
|
|
127
|
+
when GroupTarget
|
|
128
|
+
raise ConfigurationError,
|
|
129
|
+
"maximum_missed does not support `per: group(...)` yet; see docs/Roadmap.md"
|
|
130
|
+
else raise_invalid_per(per)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def group(name)
|
|
135
|
+
GroupTarget.new(name: name)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# The flatten serves the one-liner keyword form, whose value arrives as a
|
|
139
|
+
# single argument (`ignore: %i[implicit_else]`).
|
|
140
|
+
def ignore(*types)
|
|
141
|
+
case @criterion
|
|
142
|
+
when :branch then @config.__send__(:store_ignored_branches, types.flatten)
|
|
143
|
+
when :method then @config.__send__(:store_ignored_methods, types.flatten)
|
|
144
|
+
else
|
|
145
|
+
raise ConfigurationError,
|
|
146
|
+
"`ignore` is supported for `coverage :branch` and `coverage :method`, not #{@criterion.inspect}"
|
|
147
|
+
end
|
|
150
148
|
end
|
|
151
149
|
|
|
152
|
-
# Per-file minimum. With no `only:`, sets the default applied to every
|
|
153
|
-
# file; with `only:` (a String path or Regexp), overrides that default
|
|
154
|
-
# for the matching files.
|
|
155
150
|
def minimum_per_file(percent, only: nil)
|
|
156
|
-
|
|
151
|
+
Deprecation.warn("`minimum_per_file` is deprecated. " \
|
|
152
|
+
"Replace with `minimum #{percent}, per: #{(only || :file).inspect}`.")
|
|
153
|
+
@config.__send__(:store_minimum_per_file, @criterion, percent, only)
|
|
157
154
|
end
|
|
158
155
|
|
|
159
|
-
# Per-group minimum for the named group (defined via `group`).
|
|
160
156
|
def minimum_per_group(percent, only:)
|
|
161
|
-
|
|
157
|
+
Deprecation.warn("`minimum_per_group` is deprecated. " \
|
|
158
|
+
"Replace with `minimum #{percent}, per: group(#{only.inspect})`.")
|
|
159
|
+
@config.__send__(:store_minimum_per_group, @criterion, percent, only)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def maximum_missed_per_file(count, only: nil)
|
|
163
|
+
Deprecation.warn("`maximum_missed_per_file` is deprecated. " \
|
|
164
|
+
"Replace with `maximum_missed #{count}, per: #{(only || :file).inspect}`.")
|
|
165
|
+
@config.__send__(:store_maximum_missed_per_file, @criterion, count, only)
|
|
162
166
|
end
|
|
163
167
|
|
|
164
|
-
# Make this criterion the report's primary (leading) criterion.
|
|
165
168
|
def primary
|
|
166
169
|
# @criterion is Symbol-wide because this receiver is also built for
|
|
167
170
|
# :eval; primary_coverage validates at runtime.
|
|
168
171
|
@config.primary_coverage(_ = @criterion)
|
|
169
172
|
end
|
|
173
|
+
|
|
174
|
+
GroupTarget = Data.define(:name)
|
|
175
|
+
|
|
176
|
+
private
|
|
177
|
+
|
|
178
|
+
def raise_invalid_per(per)
|
|
179
|
+
raise ConfigurationError,
|
|
180
|
+
"`per:` must be :file, a String path, a Regexp, or group(\"Name\"), got #{per.inspect}"
|
|
181
|
+
end
|
|
170
182
|
end
|
|
171
183
|
end
|
|
172
184
|
end
|