simplecov 0.22.0 → 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/LICENSE +1 -1
- data/README.md +173 -875
- data/exe/simplecov +8 -0
- data/lib/minitest/simplecov_plugin.rb +18 -7
- data/lib/simplecov/atomic_file.rb +67 -0
- data/lib/simplecov/autostart.rb +9 -0
- 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 +86 -0
- data/lib/simplecov/cli/command_helpers.rb +91 -0
- data/lib/simplecov/cli/completions/scripts.rb +114 -0
- data/lib/simplecov/cli/completions.rb +83 -0
- data/lib/simplecov/cli/coverage.rb +83 -0
- data/lib/simplecov/cli/coverage_file.rb +109 -0
- 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 +115 -0
- data/lib/simplecov/cli/dotfile.rb +156 -0
- 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 +115 -0
- data/lib/simplecov/cli/open.rb +45 -0
- 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 +91 -0
- data/lib/simplecov/cli/run.rb +35 -0
- data/lib/simplecov/cli/serve/report_preparer.rb +30 -0
- data/lib/simplecov/cli/serve/static_file_handler.rb +128 -0
- data/lib/simplecov/cli/serve.rb +88 -0
- 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 +127 -0
- 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 +114 -0
- data/lib/simplecov/color.rb +65 -0
- data/lib/simplecov/combine/branches_combiner.rb +44 -21
- data/lib/simplecov/combine/coverage_accumulator.rb +175 -0
- data/lib/simplecov/combine/identity_interner.rb +27 -0
- data/lib/simplecov/combine/interned_counts.rb +28 -0
- data/lib/simplecov/combine/lines_combiner.rb +35 -29
- data/lib/simplecov/combine/methods_combiner.rb +48 -0
- data/lib/simplecov/combine/results_combiner.rb +9 -49
- data/lib/simplecov/combine.rb +0 -24
- data/lib/simplecov/command_guesser.rb +99 -42
- data/lib/simplecov/configuration/baseline.rb +31 -0
- data/lib/simplecov/configuration/coverage.rb +184 -0
- data/lib/simplecov/configuration/coverage_criteria.rb +143 -0
- data/lib/simplecov/configuration/deprecations.rb +21 -0
- data/lib/simplecov/configuration/eval_coverage.rb +37 -0
- data/lib/simplecov/configuration/filters.rb +131 -0
- data/lib/simplecov/configuration/formatting.rb +129 -0
- data/lib/simplecov/configuration/groups.rb +48 -0
- data/lib/simplecov/configuration/history.rb +44 -0
- data/lib/simplecov/configuration/ignored_entries.rb +72 -0
- data/lib/simplecov/configuration/merging.rb +197 -0
- 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 +150 -0
- data/lib/simplecov/configuration/view_coverage.rb +37 -0
- data/lib/simplecov/configuration.rb +100 -443
- 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 +21 -0
- data/lib/simplecov/coverage_statistics.rb +24 -28
- data/lib/simplecov/coverage_violations.rb +254 -0
- data/lib/simplecov/current_run.rb +61 -0
- data/lib/simplecov/defaults.rb +20 -28
- data/lib/simplecov/deprecation.rb +63 -0
- 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 +152 -0
- data/lib/simplecov/exit_codes/baseline_check.rb +46 -0
- data/lib/simplecov/exit_codes/check.rb +39 -0
- data/lib/simplecov/exit_codes/exit_code_handling.rb +24 -5
- data/lib/simplecov/exit_codes/maximum_coverage_drop_check.rb +17 -67
- 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 +27 -0
- data/lib/simplecov/exit_codes/minimum_coverage_by_file_check.rb +20 -38
- data/lib/simplecov/exit_codes/minimum_coverage_by_group_check.rb +28 -0
- data/lib/simplecov/exit_codes/minimum_overall_coverage_check.rb +37 -34
- data/lib/simplecov/exit_codes.rb +26 -0
- data/lib/simplecov/exit_handling.rb +163 -0
- data/lib/simplecov/file_list.rb +63 -48
- data/lib/simplecov/filter.rb +98 -42
- data/lib/simplecov/formatter/base.rb +100 -0
- data/lib/simplecov/formatter/baseline_formatter.rb +98 -0
- data/lib/simplecov/formatter/coverage_json_writer.rb +95 -0
- data/lib/simplecov/formatter/html_formatter/public/index.html +93 -0
- data/lib/simplecov/formatter/html_formatter/viewer_data_validator.rb +153 -0
- data/lib/simplecov/formatter/html_formatter.rb +88 -0
- data/lib/simplecov/formatter/json_formatter/errors_formatter.rb +122 -0
- data/lib/simplecov/formatter/json_formatter/production_section_formatter.rb +48 -0
- data/lib/simplecov/formatter/json_formatter/result_hash_formatter.rb +132 -0
- data/lib/simplecov/formatter/json_formatter/source_file_formatter.rb +97 -0
- data/lib/simplecov/formatter/json_formatter.rb +40 -0
- data/lib/simplecov/formatter/multi_formatter.rb +8 -10
- data/lib/simplecov/formatter/simple_formatter.rb +12 -15
- data/lib/simplecov/formatter.rb +21 -1
- data/lib/simplecov/group_names.rb +33 -0
- data/lib/simplecov/history.rb +115 -0
- data/lib/simplecov/last_run.rb +16 -5
- data/lib/simplecov/lines_classifier.rb +67 -19
- data/lib/simplecov/load_global_config.rb +12 -4
- data/lib/simplecov/parallel_adapters/base.rb +50 -0
- data/lib/simplecov/parallel_adapters/generic.rb +33 -0
- data/lib/simplecov/parallel_adapters/parallel_tests.rb +72 -0
- data/lib/simplecov/parallel_adapters.rb +54 -0
- data/lib/simplecov/parallel_coordination.rb +124 -0
- data/lib/simplecov/parallel_result_merger/worker_payload.rb +33 -0
- data/lib/simplecov/parallel_result_merger.rb +171 -0
- data/lib/simplecov/process.rb +34 -12
- 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/bundler_filter.rb +1 -1
- data/lib/simplecov/profiles/hidden_filter.rb +1 -1
- data/lib/simplecov/profiles/rails.rb +13 -10
- data/lib/simplecov/profiles/root_filter.rb +2 -5
- data/lib/simplecov/profiles/strict.rb +22 -0
- data/lib/simplecov/profiles/test_frameworks.rb +1 -4
- data/lib/simplecov/profiles.rb +26 -19
- data/lib/simplecov/report_deferral.rb +45 -0
- data/lib/simplecov/report_stamp.rb +25 -0
- data/lib/simplecov/result/filter_config.rb +19 -0
- data/lib/simplecov/result/missing_source_files_reporter.rb +48 -0
- data/lib/simplecov/result/serialization.rb +43 -0
- data/lib/simplecov/result/source_file_builder.rb +46 -0
- data/lib/simplecov/result.rb +123 -47
- data/lib/simplecov/result_adapter.rb +130 -11
- data/lib/simplecov/result_merger/contexts.rb +22 -0
- data/lib/simplecov/result_merger/legacy_format_adapter.rb +24 -0
- data/lib/simplecov/result_merger/resultset_file.rb +67 -0
- data/lib/simplecov/result_merger/resultset_run_identity.rb +65 -0
- data/lib/simplecov/result_merger/resultset_store.rb +62 -0
- data/lib/simplecov/result_merger/unloaded_files.rb +95 -0
- data/lib/simplecov/result_merger.rb +120 -124
- data/lib/simplecov/result_processing.rb +188 -0
- data/lib/simplecov/run_identity.rb +75 -0
- data/lib/simplecov/simulate_coverage.rb +72 -17
- data/lib/simplecov/source_file/branch.rb +8 -35
- data/lib/simplecov/source_file/branch_builder.rb +95 -0
- data/lib/simplecov/source_file/builder_context.rb +28 -0
- data/lib/simplecov/source_file/line.rb +17 -32
- data/lib/simplecov/source_file/line_builder.rb +37 -0
- data/lib/simplecov/source_file/method.rb +54 -0
- data/lib/simplecov/source_file/method_builder.rb +51 -0
- data/lib/simplecov/source_file/ruby_data_parser.rb +103 -0
- data/lib/simplecov/source_file/skip_chunks.rb +72 -0
- data/lib/simplecov/source_file/source_loader.rb +89 -0
- data/lib/simplecov/source_file/statistics.rb +76 -0
- data/lib/simplecov/source_file.rb +64 -260
- data/lib/simplecov/static_coverage_extractor/condition_folding.rb +155 -0
- data/lib/simplecov/static_coverage_extractor/location_conventions.rb +191 -0
- data/lib/simplecov/static_coverage_extractor/method_collector.rb +45 -0
- data/lib/simplecov/static_coverage_extractor/prism_compat.rb +46 -0
- data/lib/simplecov/static_coverage_extractor/value_position.rb +61 -0
- data/lib/simplecov/static_coverage_extractor/visitor.rb +163 -0
- data/lib/simplecov/static_coverage_extractor.rb +78 -0
- 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 +62 -0
- data/lib/simplecov/useless_results_remover.rb +17 -7
- 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 +5 -0
- data/lib/simplecov.rb +143 -394
- data/lib/simplecov_json_formatter.rb +5 -0
- data/man/simplecov.1 +354 -0
- data/sig/simplecov.rbs +1710 -0
- metadata +183 -61
- data/CHANGELOG.md +0 -191
- data/doc/alternate-formatters.md +0 -71
- data/doc/commercial-services.md +0 -25
- data/doc/editor-integration.md +0 -18
- data/lib/simplecov/combine/files_combiner.rb +0 -24
- data/lib/simplecov/default_formatter.rb +0 -20
data/lib/simplecov/combine.rb
CHANGED
|
@@ -1,30 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module SimpleCov
|
|
4
|
-
# Functionally for combining coverage results
|
|
5
|
-
#
|
|
6
4
|
module Combine
|
|
7
|
-
module_function
|
|
8
|
-
|
|
9
|
-
#
|
|
10
|
-
# Combine two coverage based on the given combiner_module.
|
|
11
|
-
#
|
|
12
|
-
# Combiners should always be called through this interface,
|
|
13
|
-
# as it takes care of short-circuiting of one of the coverages is nil.
|
|
14
|
-
#
|
|
15
|
-
# @return [Hash]
|
|
16
|
-
def combine(combiner_module, coverage_a, coverage_b)
|
|
17
|
-
return existing_coverage(coverage_a, coverage_b) if empty_coverage?(coverage_a, coverage_b)
|
|
18
|
-
|
|
19
|
-
combiner_module.combine(coverage_a, coverage_b)
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def empty_coverage?(coverage_a, coverage_b)
|
|
23
|
-
!(coverage_a && coverage_b)
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def existing_coverage(coverage_a, coverage_b)
|
|
27
|
-
coverage_a || coverage_b
|
|
28
|
-
end
|
|
29
5
|
end
|
|
30
6
|
end
|
|
@@ -1,63 +1,120 @@
|
|
|
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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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.
|
|
9
|
+
attr_reader :original_run_command
|
|
10
|
+
|
|
11
|
+
# Assigning the flattened command clears any separately recorded program
|
|
12
|
+
# name so the two can never describe different runs. Callers that know the
|
|
13
|
+
# real one assign it immediately afterwards, as `defaults.rb` does.
|
|
14
|
+
def original_run_command=(command)
|
|
15
|
+
@original_program_name = nil
|
|
16
|
+
@original_run_command = command
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# The invoked program on its own. `original_run_command` joins
|
|
20
|
+
# `$PROGRAM_NAME` and `ARGV` with a space, so a program path containing
|
|
21
|
+
# one (`/opt/My Ruby/bin/rspec`) cannot be recovered from it afterwards.
|
|
22
|
+
# Falls back to the first token for callers that set only the flattened
|
|
23
|
+
# command, which is every caller outside `defaults.rb`.
|
|
24
|
+
attr_writer :original_program_name
|
|
25
|
+
|
|
26
|
+
def original_program_name
|
|
27
|
+
@original_program_name || original_run_command.to_s[/\A\s*(\S+)/, 1]
|
|
28
|
+
end
|
|
15
29
|
|
|
16
30
|
def guess
|
|
17
|
-
|
|
31
|
+
framework = from_executable_name || from_command_line_options || from_defined_constants
|
|
32
|
+
[framework, parallel_data].compact.join(" ")
|
|
18
33
|
end
|
|
19
34
|
|
|
20
|
-
|
|
35
|
+
private
|
|
21
36
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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.
|
|
39
|
+
def parallel_data
|
|
40
|
+
groups, number = ENV.values_at("PARALLEL_TEST_GROUPS", "TEST_ENV_NUMBER")
|
|
41
|
+
return unless groups && number
|
|
25
42
|
|
|
26
|
-
|
|
43
|
+
# parallel_tests sets the first worker's TEST_ENV_NUMBER to "" rather
|
|
44
|
+
# than "1"; restore the position so the rendered label reads cleanly.
|
|
27
45
|
number = "1" if number.empty?
|
|
28
|
-
"(#{number}/#{
|
|
46
|
+
"(#{number}/#{groups})"
|
|
29
47
|
end
|
|
30
48
|
|
|
49
|
+
# The command is `"#{$PROGRAM_NAME} #{ARGV.join(' ')}"`, so a keyword only
|
|
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
|
|
52
|
+
# suite name: a Ruby installed under `latest/bin` (as mise does) contains
|
|
53
|
+
# "test/", so every run through it was labelled "Unit Tests".
|
|
54
|
+
#
|
|
55
|
+
# A backslash bounds a segment too, or a Windows `spec\foo_spec.rb` would
|
|
56
|
+
# lose a match. The `test/` patterns below still spell their separator as
|
|
57
|
+
# `/` only, which is how it has always been.
|
|
58
|
+
SEGMENT_START = %r{(?<![^\s/\\])}
|
|
59
|
+
private_constant :SEGMENT_START
|
|
60
|
+
SEGMENT_END = %r{(?=[\s/\\]|\z)}
|
|
61
|
+
private_constant :SEGMENT_END
|
|
62
|
+
|
|
63
|
+
COMMAND_LINE_FRAMEWORKS = {
|
|
64
|
+
%r{#{SEGMENT_START}test/functional/} => "Functional Tests",
|
|
65
|
+
%r{#{SEGMENT_START}test/\{.*functional.*\}/} => "Functional Tests",
|
|
66
|
+
%r{#{SEGMENT_START}test/integration/} => "Integration Tests",
|
|
67
|
+
%r{#{SEGMENT_START}test/} => "Unit Tests",
|
|
68
|
+
/#{SEGMENT_START}spec#{SEGMENT_END}/ => "RSpec",
|
|
69
|
+
/#{SEGMENT_START}cucumber#{SEGMENT_END}/ => "Cucumber Features",
|
|
70
|
+
/#{SEGMENT_START}features#{SEGMENT_END}/ => "Cucumber Features"
|
|
71
|
+
}.freeze
|
|
72
|
+
private_constant :COMMAND_LINE_FRAMEWORKS
|
|
73
|
+
|
|
31
74
|
def from_command_line_options
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
when /test\/integration\//
|
|
36
|
-
"Integration Tests"
|
|
37
|
-
when /test\//
|
|
38
|
-
"Unit Tests"
|
|
39
|
-
when /spec/
|
|
40
|
-
"RSpec"
|
|
41
|
-
when /cucumber/, /features/
|
|
42
|
-
"Cucumber Features"
|
|
43
|
-
end
|
|
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
|
|
44
78
|
end
|
|
45
79
|
|
|
80
|
+
# Which binary was invoked outranks everything else on the command line.
|
|
81
|
+
# `rspec features` is an RSpec suite whose examples live in features/, not
|
|
82
|
+
# a Cucumber run, and only the executable name says so. Generic runners
|
|
83
|
+
# (`ruby`, `rake`, a bare `*_test.rb` script) are absent on purpose: they
|
|
84
|
+
# name no framework, so those runs fall through to the path patterns.
|
|
85
|
+
EXECUTABLE_FRAMEWORKS = {
|
|
86
|
+
"rspec" => "RSpec",
|
|
87
|
+
"cucumber" => "Cucumber Features"
|
|
88
|
+
}.freeze
|
|
89
|
+
private_constant :EXECUTABLE_FRAMEWORKS
|
|
90
|
+
|
|
91
|
+
def from_executable_name
|
|
92
|
+
program = original_program_name
|
|
93
|
+
return unless program
|
|
94
|
+
|
|
95
|
+
# The extension is trimmed so Windows' rspec.bat is recognized too.
|
|
96
|
+
EXECUTABLE_FRAMEWORKS[File.basename(program, ".*")]
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Inner array literals after the first are flagged uncovered by Ruby's
|
|
100
|
+
# Coverage module even though the constant evaluates as a whole, a known
|
|
101
|
+
# quirk with multi-line array literals.
|
|
102
|
+
DEFINED_CONSTANT_FRAMEWORKS = [
|
|
103
|
+
["RSpec", -> { defined?(::RSpec) }],
|
|
104
|
+
["Unit Tests", -> { defined?(Test::Unit) }], # simplecov:disable
|
|
105
|
+
["Minitest", -> { defined?(::Minitest) }], # simplecov:disable
|
|
106
|
+
["MiniTest", -> { defined?(MiniTest) }] # simplecov:disable
|
|
107
|
+
].freeze
|
|
108
|
+
private_constant :DEFINED_CONSTANT_FRAMEWORKS
|
|
109
|
+
|
|
46
110
|
def from_defined_constants
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"Unit Tests"
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
elsif defined?(MiniTest)
|
|
55
|
-
"MiniTest"
|
|
56
|
-
else
|
|
57
|
-
# TODO: Provide link to docs/wiki article
|
|
58
|
-
warn "SimpleCov failed to recognize the test framework and/or suite used. Please specify manually using SimpleCov.command_name 'Unit Tests'."
|
|
59
|
-
"Unknown Test Framework"
|
|
60
|
-
end
|
|
111
|
+
DEFINED_CONSTANT_FRAMEWORKS.each { |name, defined_check| return name if defined_check.call }
|
|
112
|
+
|
|
113
|
+
warn(
|
|
114
|
+
"SimpleCov failed to recognize the test framework and/or suite used. " \
|
|
115
|
+
"Please specify manually using SimpleCov.command_name 'Unit Tests'."
|
|
116
|
+
)
|
|
117
|
+
"Unknown Test Framework"
|
|
61
118
|
end
|
|
62
119
|
end
|
|
63
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
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SimpleCov
|
|
4
|
+
module Configuration
|
|
5
|
+
COVERAGE_THRESHOLD_OPTIONS = %i[minimum maximum exact maximum_drop ignore minimum_per_file
|
|
6
|
+
maximum_missed maximum_missed_per_file].freeze
|
|
7
|
+
|
|
8
|
+
def coverage(criterion, primary: false, enabled: true, oneshot: false, **thresholds, &block)
|
|
9
|
+
criterion = enable_coverage_criterion(criterion, enabled: enabled, oneshot: oneshot)
|
|
10
|
+
# The cast admits :eval, which primary_coverage rejects at runtime.
|
|
11
|
+
primary_coverage(_ = criterion) if primary
|
|
12
|
+
|
|
13
|
+
configurator = CoverageCriterion.new(self, criterion)
|
|
14
|
+
apply_threshold_options(configurator, thresholds)
|
|
15
|
+
configurator.instance_eval(&block) if block
|
|
16
|
+
|
|
17
|
+
criterion
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def apply_threshold_options(configurator, options)
|
|
23
|
+
options.each do |verb, value|
|
|
24
|
+
unless COVERAGE_THRESHOLD_OPTIONS.include?(verb)
|
|
25
|
+
raise ConfigurationError,
|
|
26
|
+
"Unknown `coverage` option #{verb.inspect}. " \
|
|
27
|
+
"Supported options are #{COVERAGE_THRESHOLD_OPTIONS.inspect}."
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
configurator.public_send(verb, value)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def enable_coverage_criterion(criterion, enabled:, oneshot:)
|
|
35
|
+
criterion = resolve_criterion_variant(criterion, oneshot)
|
|
36
|
+
enabled ? enable_coverage(criterion) : disable_coverage(criterion)
|
|
37
|
+
criterion
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def resolve_criterion_variant(criterion, oneshot)
|
|
41
|
+
return criterion unless oneshot
|
|
42
|
+
|
|
43
|
+
raise ConfigurationError, "`oneshot: true` is only valid for `coverage :line`" unless criterion.equal?(:line)
|
|
44
|
+
|
|
45
|
+
ONESHOT_LINE_COVERAGE_CRITERION
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# @api private
|
|
49
|
+
def store_overall_threshold(setting, criterion, percent)
|
|
50
|
+
raise_on_invalid_coverage({criterion => percent}, setting)
|
|
51
|
+
public_send(setting)[criterion] = percent
|
|
52
|
+
end
|
|
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
|
+
|
|
72
|
+
def store_minimum_per_file(criterion, percent, target)
|
|
73
|
+
raise_on_invalid_coverage({criterion => percent}, "minimum_coverage_by_file")
|
|
74
|
+
return minimum_coverage_by_file[criterion] = percent if target.nil?
|
|
75
|
+
|
|
76
|
+
unless target.is_a?(String) || target.is_a?(Regexp)
|
|
77
|
+
raise ConfigurationError, "`only:` must be a String path or Regexp, got #{target.inspect}"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
(minimum_coverage_by_file_overrides[target] ||= {})[criterion] = percent
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def store_minimum_per_group(criterion, percent, group_name)
|
|
84
|
+
raise_on_invalid_coverage({criterion => percent}, "minimum_coverage_by_group")
|
|
85
|
+
# Normalized like `group` does, so `per: group(:Models)` finds the group
|
|
86
|
+
# defined as `group "Models"` at check time.
|
|
87
|
+
(minimum_coverage_by_group[GroupNames.normalize(group_name)] ||= {})[criterion] = percent
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
#
|
|
91
|
+
# Receiver for a `coverage <criterion> do ... end` block.
|
|
92
|
+
#
|
|
93
|
+
class CoverageCriterion
|
|
94
|
+
def initialize(config, criterion)
|
|
95
|
+
@config = config
|
|
96
|
+
@criterion = criterion
|
|
97
|
+
end
|
|
98
|
+
|
|
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
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def maximum(percent)
|
|
110
|
+
@config.__send__(:store_overall_threshold, :maximum_coverage, @criterion, percent)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def exact(percent)
|
|
114
|
+
minimum(percent)
|
|
115
|
+
maximum(percent)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def maximum_drop(percent)
|
|
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
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def minimum_per_file(percent, only: nil)
|
|
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)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def minimum_per_group(percent, only:)
|
|
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)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def primary
|
|
169
|
+
# @criterion is Symbol-wide because this receiver is also built for
|
|
170
|
+
# :eval; primary_coverage validates at runtime.
|
|
171
|
+
@config.primary_coverage(_ = @criterion)
|
|
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
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SimpleCov
|
|
4
|
+
module Configuration
|
|
5
|
+
SUPPORTED_COVERAGE_CRITERIA = %i[line branch method oneshot_line].freeze
|
|
6
|
+
DEFAULT_COVERAGE_CRITERION = :line
|
|
7
|
+
ONESHOT_LINE_COVERAGE_CRITERION = :oneshot_line
|
|
8
|
+
LINE_COVERAGE_ALTERNATIVES = {line: :oneshot_line, oneshot_line: :line}.freeze #: line_coverage_alternatives
|
|
9
|
+
private_constant :LINE_COVERAGE_ALTERNATIVES
|
|
10
|
+
|
|
11
|
+
def enable_coverage(*criteria)
|
|
12
|
+
criteria.each do |criterion|
|
|
13
|
+
criterion.equal?(:eval) ? enable_eval_coverage : add_coverage_criterion(_ = criterion)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Disabling every criterion raises at `start_tracking`, not here, so config
|
|
18
|
+
# files that toggle criteria in arbitrary order don't have to worry about
|
|
19
|
+
# transient empty states.
|
|
20
|
+
def disable_coverage(criterion)
|
|
21
|
+
return disable_eval_coverage if criterion.equal?(:eval)
|
|
22
|
+
|
|
23
|
+
raise_if_criterion_unsupported(_ = criterion)
|
|
24
|
+
coverage_criteria.delete(_ = criterion)
|
|
25
|
+
@primary_coverage = nil if @primary_coverage.equal?(criterion)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def primary_coverage(criterion = nil)
|
|
29
|
+
if criterion.nil?
|
|
30
|
+
@primary_coverage ||= default_primary_coverage
|
|
31
|
+
else
|
|
32
|
+
self.primary_coverage = criterion
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def primary_coverage=(criterion)
|
|
37
|
+
raise_if_criterion_disabled(criterion)
|
|
38
|
+
@primary_coverage = criterion
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def coverage_criteria
|
|
42
|
+
@coverage_criteria ||= Set[DEFAULT_COVERAGE_CRITERION]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def coverage_criterion_enabled?(criterion)
|
|
46
|
+
coverage_criteria.member?(criterion)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def clear_coverage_criteria
|
|
50
|
+
@coverage_criteria = nil
|
|
51
|
+
@primary_coverage = nil
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def validate_coverage_criteria!
|
|
55
|
+
return unless coverage_criteria.empty?
|
|
56
|
+
|
|
57
|
+
raise ConfigurationError,
|
|
58
|
+
"At least one coverage criterion must be enabled. " \
|
|
59
|
+
"Re-enable one with `enable_coverage :line`, `:branch`, or `:method`."
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Whether this run produces line data at all. The oneshot variant counts:
|
|
63
|
+
# `ResultAdapter` turns its executed-line list back into a line array. A
|
|
64
|
+
# branch-only or method-only run produces none, and `Coverage.result`
|
|
65
|
+
# entries for the files it loaded carry no `"lines"` key.
|
|
66
|
+
def line_coverage?
|
|
67
|
+
coverage_criterion_enabled?(DEFAULT_COVERAGE_CRITERION) ||
|
|
68
|
+
coverage_criterion_enabled?(ONESHOT_LINE_COVERAGE_CRITERION)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def branch_coverage?
|
|
72
|
+
branch_coverage_supported? && coverage_criterion_enabled?(:branch)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def branch_coverage_supported?
|
|
76
|
+
coverage_criterion_supported?(:branches)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def method_coverage?
|
|
80
|
+
method_coverage_supported? && coverage_criterion_enabled?(:method)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def method_coverage_supported?
|
|
84
|
+
coverage_criterion_supported?(:methods)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Older Rubies don't expose `Coverage.supported?`, so fall back to the
|
|
88
|
+
# historical engine check that line/branch/method were unavailable on JRuby.
|
|
89
|
+
# `:eval` was added later, so its fallback is "always unsupported".
|
|
90
|
+
def coverage_criterion_supported?(criterion)
|
|
91
|
+
load_coverage
|
|
92
|
+
return Coverage.supported?(criterion) if Coverage.respond_to?(:supported?)
|
|
93
|
+
|
|
94
|
+
!criterion.eql?(:eval) && !RUBY_ENGINE.eql?("jruby")
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Loading `simplecov/configuration` on its own leaves Coverage undefined, and
|
|
98
|
+
# this question is asked at configuration time.
|
|
99
|
+
def load_coverage
|
|
100
|
+
require "coverage"
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
private
|
|
104
|
+
|
|
105
|
+
def add_coverage_criterion(criterion)
|
|
106
|
+
raise_if_criterion_unsupported(criterion)
|
|
107
|
+
incompatible = LINE_COVERAGE_ALTERNATIVES[criterion]
|
|
108
|
+
disable_coverage(incompatible) if incompatible
|
|
109
|
+
coverage_criteria << criterion
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Answering `:line` even when it is disabled would propagate broken state
|
|
113
|
+
# into `minimum_coverage 90`, so fall back to whichever criterion the user
|
|
114
|
+
# actually enabled, in insertion order.
|
|
115
|
+
def default_primary_coverage
|
|
116
|
+
return DEFAULT_COVERAGE_CRITERION if coverage_criterion_enabled?(DEFAULT_COVERAGE_CRITERION)
|
|
117
|
+
|
|
118
|
+
_ = coverage_criteria.first
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def raise_if_criterion_disabled(criterion)
|
|
122
|
+
if criterion.equal?(:eval)
|
|
123
|
+
raise ConfigurationError,
|
|
124
|
+
"Coverage criterion :eval only toggles measuring eval'd code; " \
|
|
125
|
+
"it cannot carry thresholds or serve as the primary criterion"
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
raise_if_criterion_unsupported(criterion)
|
|
129
|
+
return if coverage_criterion_enabled?(criterion)
|
|
130
|
+
|
|
131
|
+
raise ConfigurationError,
|
|
132
|
+
"Coverage criterion #{criterion}, is disabled! " \
|
|
133
|
+
"Please enable it first through enable_coverage #{criterion} (if supported)"
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def raise_if_criterion_unsupported(criterion)
|
|
137
|
+
return if SUPPORTED_COVERAGE_CRITERIA.member?(criterion)
|
|
138
|
+
|
|
139
|
+
raise ConfigurationError,
|
|
140
|
+
"Unsupported coverage criterion #{criterion}, supported values are #{SUPPORTED_COVERAGE_CRITERIA}"
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../deprecation"
|
|
4
|
+
|
|
5
|
+
module SimpleCov
|
|
6
|
+
module Configuration
|
|
7
|
+
DEPRECATION_MODES = Deprecation::MODES
|
|
8
|
+
|
|
9
|
+
# The configuration DSL migrates by warn-and-delegate, which means a project
|
|
10
|
+
# can run on deprecated spellings indefinitely without noticing.
|
|
11
|
+
# `deprecations :raise` turns every deprecated API into a ConfigurationError,
|
|
12
|
+
# so a project that has migrated can guard in CI against old spellings
|
|
13
|
+
# creeping back. There is deliberately no silencing mode: a deprecation you
|
|
14
|
+
# cannot see is a migration you never make.
|
|
15
|
+
def deprecations(mode = nil)
|
|
16
|
+
return Deprecation.mode unless mode
|
|
17
|
+
|
|
18
|
+
Deprecation.mode = mode
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SimpleCov
|
|
4
|
+
module Configuration
|
|
5
|
+
# The standalone eval-coverage toggle, kept apart from the criteria-set logic
|
|
6
|
+
# in `coverage_criteria.rb` because `:eval` is a boolean toggle, never a
|
|
7
|
+
# member of the enabled-criteria set.
|
|
8
|
+
def coverage_for_eval_supported?
|
|
9
|
+
coverage_criterion_supported?(:eval)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def coverage_for_eval_enabled?
|
|
13
|
+
@coverage_for_eval_enabled ||= false
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def enable_coverage_for_eval
|
|
17
|
+
Deprecation.warn("`SimpleCov.enable_coverage_for_eval` is deprecated. " \
|
|
18
|
+
"Replace with `SimpleCov.enable_coverage :eval`.")
|
|
19
|
+
enable_eval_coverage
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def enable_eval_coverage
|
|
25
|
+
if coverage_for_eval_supported?
|
|
26
|
+
@coverage_for_eval_enabled = true
|
|
27
|
+
else
|
|
28
|
+
warn "Coverage for eval is not available on this Ruby"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# No support check needed: off is a safe state on every Ruby.
|
|
33
|
+
def disable_eval_coverage
|
|
34
|
+
@coverage_for_eval_enabled = false
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|