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.
Files changed (200) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +148 -5
  3. data/exe/simplecov +0 -3
  4. data/lib/minitest/simplecov_plugin.rb +12 -9
  5. data/lib/simplecov/atomic_file.rb +16 -19
  6. data/lib/simplecov/autostart.rb +5 -7
  7. data/lib/simplecov/baseline/parser.rb +58 -0
  8. data/lib/simplecov/baseline.rb +170 -0
  9. data/lib/simplecov/cli/affected/changed_files.rb +57 -0
  10. data/lib/simplecov/cli/affected/selection.rb +117 -0
  11. data/lib/simplecov/cli/affected.rb +148 -0
  12. data/lib/simplecov/cli/annotations.rb +166 -0
  13. data/lib/simplecov/cli/badge/svg.rb +70 -0
  14. data/lib/simplecov/cli/badge.rb +62 -0
  15. data/lib/simplecov/cli/clean.rb +23 -24
  16. data/lib/simplecov/cli/command_helpers.rb +55 -19
  17. data/lib/simplecov/cli/completions/scripts.rb +114 -0
  18. data/lib/simplecov/cli/completions.rb +83 -0
  19. data/lib/simplecov/cli/coverage.rb +25 -28
  20. data/lib/simplecov/cli/coverage_file.rb +58 -14
  21. data/lib/simplecov/cli/dead_code/output.rb +100 -0
  22. data/lib/simplecov/cli/dead_code.rb +139 -0
  23. data/lib/simplecov/cli/diff/output.rb +45 -0
  24. data/lib/simplecov/cli/diff.rb +35 -67
  25. data/lib/simplecov/cli/dotfile.rb +83 -38
  26. data/lib/simplecov/cli/git.rb +41 -0
  27. data/lib/simplecov/cli/history/output.rb +135 -0
  28. data/lib/simplecov/cli/history.rb +67 -0
  29. data/lib/simplecov/cli/merge.rb +34 -39
  30. data/lib/simplecov/cli/open.rb +12 -15
  31. data/lib/simplecov/cli/patch/changed_lines.rb +149 -0
  32. data/lib/simplecov/cli/patch/output.rb +151 -0
  33. data/lib/simplecov/cli/patch.rb +175 -0
  34. data/lib/simplecov/cli/ratchet/output.rb +44 -0
  35. data/lib/simplecov/cli/ratchet.rb +83 -0
  36. data/lib/simplecov/cli/report.rb +18 -24
  37. data/lib/simplecov/cli/run.rb +8 -9
  38. data/lib/simplecov/cli/serve/report_preparer.rb +7 -6
  39. data/lib/simplecov/cli/serve/static_file_handler.rb +62 -56
  40. data/lib/simplecov/cli/serve.rb +27 -24
  41. data/lib/simplecov/cli/show/annotator.rb +87 -0
  42. data/lib/simplecov/cli/show/sweep.rb +36 -0
  43. data/lib/simplecov/cli/show.rb +133 -0
  44. data/lib/simplecov/cli/status/facts.rb +87 -0
  45. data/lib/simplecov/cli/status.rb +93 -0
  46. data/lib/simplecov/cli/tests/redundancy.rb +78 -0
  47. data/lib/simplecov/cli/tests.rb +148 -0
  48. data/lib/simplecov/cli/uncovered/misses.rb +33 -0
  49. data/lib/simplecov/cli/uncovered.rb +58 -34
  50. data/lib/simplecov/cli/usage.rb +153 -0
  51. data/lib/simplecov/cli/watch/live_report.rb +73 -0
  52. data/lib/simplecov/cli/watch/narrator.rb +55 -0
  53. data/lib/simplecov/cli/watch/poller.rb +45 -0
  54. data/lib/simplecov/cli/watch/session.rb +134 -0
  55. data/lib/simplecov/cli/watch/test_plan.rb +43 -0
  56. data/lib/simplecov/cli/watch.rb +85 -0
  57. data/lib/simplecov/cli.rb +43 -92
  58. data/lib/simplecov/color.rb +19 -37
  59. data/lib/simplecov/combine/branches_combiner.rb +7 -31
  60. data/lib/simplecov/combine/coverage_accumulator.rb +49 -142
  61. data/lib/simplecov/combine/identity_interner.rb +10 -13
  62. data/lib/simplecov/combine/interned_counts.rb +7 -9
  63. data/lib/simplecov/combine/lines_combiner.rb +20 -44
  64. data/lib/simplecov/combine/methods_combiner.rb +9 -35
  65. data/lib/simplecov/combine/results_combiner.rb +4 -21
  66. data/lib/simplecov/combine.rb +0 -6
  67. data/lib/simplecov/command_guesser.rb +20 -34
  68. data/lib/simplecov/configuration/baseline.rb +31 -0
  69. data/lib/simplecov/configuration/coverage.rb +91 -79
  70. data/lib/simplecov/configuration/coverage_criteria.rb +43 -54
  71. data/lib/simplecov/configuration/deprecations.rb +21 -0
  72. data/lib/simplecov/configuration/eval_coverage.rb +7 -11
  73. data/lib/simplecov/configuration/filters.rb +40 -73
  74. data/lib/simplecov/configuration/formatting.rb +74 -83
  75. data/lib/simplecov/configuration/groups.rb +19 -13
  76. data/lib/simplecov/configuration/history.rb +44 -0
  77. data/lib/simplecov/configuration/ignored_entries.rb +31 -22
  78. data/lib/simplecov/configuration/merging.rb +94 -62
  79. data/lib/simplecov/configuration/missed_caps.rb +51 -0
  80. data/lib/simplecov/configuration/production.rb +23 -0
  81. data/lib/simplecov/configuration/test_tracking.rb +48 -0
  82. data/lib/simplecov/configuration/thresholds.rb +33 -62
  83. data/lib/simplecov/configuration/view_coverage.rb +37 -0
  84. data/lib/simplecov/configuration.rb +70 -76
  85. data/lib/simplecov/configuration_error.rb +5 -0
  86. data/lib/simplecov/context_map/union.rb +81 -0
  87. data/lib/simplecov/context_map.rb +187 -0
  88. data/lib/simplecov/coverage_json.rb +2 -5
  89. data/lib/simplecov/coverage_statistics.rb +14 -24
  90. data/lib/simplecov/coverage_violations.rb +160 -65
  91. data/lib/simplecov/current_run.rb +61 -0
  92. data/lib/simplecov/defaults.rb +4 -23
  93. data/lib/simplecov/deprecation.rb +45 -27
  94. data/lib/simplecov/directive/erb.rb +44 -0
  95. data/lib/simplecov/directive/haml.rb +33 -0
  96. data/lib/simplecov/directive/indented_template.rb +37 -0
  97. data/lib/simplecov/directive/slim.rb +35 -0
  98. data/lib/simplecov/directive/template.rb +29 -0
  99. data/lib/simplecov/directive.rb +47 -62
  100. data/lib/simplecov/exit_codes/baseline_check.rb +46 -0
  101. data/lib/simplecov/exit_codes/check.rb +17 -11
  102. data/lib/simplecov/exit_codes/exit_code_handling.rb +21 -8
  103. data/lib/simplecov/exit_codes/maximum_coverage_drop_check.rb +7 -10
  104. data/lib/simplecov/exit_codes/maximum_missed_check.rb +26 -0
  105. data/lib/simplecov/exit_codes/maximum_missed_per_file_check.rb +34 -0
  106. data/lib/simplecov/exit_codes/maximum_overall_coverage_check.rb +7 -12
  107. data/lib/simplecov/exit_codes/minimum_coverage_by_file_check.rb +11 -10
  108. data/lib/simplecov/exit_codes/minimum_coverage_by_group_check.rb +7 -9
  109. data/lib/simplecov/exit_codes/minimum_overall_coverage_check.rb +16 -17
  110. data/lib/simplecov/exit_codes.rb +16 -10
  111. data/lib/simplecov/exit_handling.rb +94 -59
  112. data/lib/simplecov/file_list.rb +20 -51
  113. data/lib/simplecov/filter.rb +43 -66
  114. data/lib/simplecov/formatter/base.rb +37 -55
  115. data/lib/simplecov/formatter/baseline_formatter.rb +98 -0
  116. data/lib/simplecov/formatter/coverage_json_writer.rb +29 -31
  117. data/lib/simplecov/formatter/html_formatter/public/index.html +15 -9
  118. data/lib/simplecov/formatter/html_formatter/viewer_data_validator.rb +74 -17
  119. data/lib/simplecov/formatter/html_formatter.rb +24 -41
  120. data/lib/simplecov/formatter/json_formatter/errors_formatter.rb +50 -16
  121. data/lib/simplecov/formatter/json_formatter/production_section_formatter.rb +48 -0
  122. data/lib/simplecov/formatter/json_formatter/result_hash_formatter.rb +43 -29
  123. data/lib/simplecov/formatter/json_formatter/source_file_formatter.rb +14 -10
  124. data/lib/simplecov/formatter/json_formatter.rb +7 -13
  125. data/lib/simplecov/formatter/multi_formatter.rb +5 -11
  126. data/lib/simplecov/formatter/simple_formatter.rb +2 -8
  127. data/lib/simplecov/formatter.rb +14 -10
  128. data/lib/simplecov/group_names.rb +13 -12
  129. data/lib/simplecov/history.rb +115 -0
  130. data/lib/simplecov/last_run.rb +6 -9
  131. data/lib/simplecov/lines_classifier.rb +31 -16
  132. data/lib/simplecov/load_global_config.rb +7 -7
  133. data/lib/simplecov/parallel_adapters/base.rb +13 -41
  134. data/lib/simplecov/parallel_adapters/generic.rb +7 -19
  135. data/lib/simplecov/parallel_adapters/parallel_tests.rb +21 -42
  136. data/lib/simplecov/parallel_adapters.rb +24 -55
  137. data/lib/simplecov/parallel_coordination.rb +39 -49
  138. data/lib/simplecov/parallel_result_merger/worker_payload.rb +33 -0
  139. data/lib/simplecov/parallel_result_merger.rb +67 -126
  140. data/lib/simplecov/process.rb +14 -32
  141. data/lib/simplecov/production/error.rb +11 -0
  142. data/lib/simplecov/production/file_sink.rb +130 -0
  143. data/lib/simplecov/production.rb +249 -0
  144. data/lib/simplecov/profiles/rails.rb +4 -15
  145. data/lib/simplecov/profiles/root_filter.rb +0 -4
  146. data/lib/simplecov/profiles/strict.rb +11 -21
  147. data/lib/simplecov/profiles.rb +11 -33
  148. data/lib/simplecov/report_deferral.rb +11 -15
  149. data/lib/simplecov/report_stamp.rb +6 -9
  150. data/lib/simplecov/result/filter_config.rb +19 -0
  151. data/lib/simplecov/result/missing_source_files_reporter.rb +8 -9
  152. data/lib/simplecov/result/serialization.rb +43 -0
  153. data/lib/simplecov/result/source_file_builder.rb +7 -12
  154. data/lib/simplecov/result.rb +74 -117
  155. data/lib/simplecov/result_adapter.rb +56 -68
  156. data/lib/simplecov/result_merger/contexts.rb +22 -0
  157. data/lib/simplecov/result_merger/legacy_format_adapter.rb +4 -8
  158. data/lib/simplecov/result_merger/resultset_file.rb +20 -27
  159. data/lib/simplecov/result_merger/resultset_run_identity.rb +23 -25
  160. data/lib/simplecov/result_merger/resultset_store.rb +23 -14
  161. data/lib/simplecov/result_merger/unloaded_files.rb +24 -32
  162. data/lib/simplecov/result_merger.rb +59 -58
  163. data/lib/simplecov/result_processing.rb +92 -122
  164. data/lib/simplecov/run_identity.rb +16 -18
  165. data/lib/simplecov/simulate_coverage.rb +34 -50
  166. data/lib/simplecov/source_file/branch.rb +8 -33
  167. data/lib/simplecov/source_file/branch_builder.rb +28 -47
  168. data/lib/simplecov/source_file/builder_context.rb +13 -13
  169. data/lib/simplecov/source_file/line.rb +13 -33
  170. data/lib/simplecov/source_file/line_builder.rb +9 -15
  171. data/lib/simplecov/source_file/method.rb +3 -7
  172. data/lib/simplecov/source_file/method_builder.rb +6 -15
  173. data/lib/simplecov/source_file/ruby_data_parser.rb +59 -66
  174. data/lib/simplecov/source_file/skip_chunks.rb +29 -29
  175. data/lib/simplecov/source_file/source_loader.rb +45 -35
  176. data/lib/simplecov/source_file/statistics.rb +20 -9
  177. data/lib/simplecov/source_file.rb +21 -51
  178. data/lib/simplecov/static_coverage_extractor/condition_folding.rb +55 -139
  179. data/lib/simplecov/static_coverage_extractor/location_conventions.rb +76 -121
  180. data/lib/simplecov/static_coverage_extractor/method_collector.rb +9 -26
  181. data/lib/simplecov/static_coverage_extractor/prism_compat.rb +15 -24
  182. data/lib/simplecov/static_coverage_extractor/value_position.rb +25 -38
  183. data/lib/simplecov/static_coverage_extractor/visitor.rb +45 -65
  184. data/lib/simplecov/static_coverage_extractor.rb +43 -75
  185. data/lib/simplecov/test_tracker/accessors.rb +34 -0
  186. data/lib/simplecov/test_tracker/constant_watch.rb +49 -0
  187. data/lib/simplecov/test_tracker/delta.rb +69 -0
  188. data/lib/simplecov/test_tracker/framework_hooks.rb +130 -0
  189. data/lib/simplecov/test_tracker.rb +162 -0
  190. data/lib/simplecov/unloaded_file_injector.rb +18 -31
  191. data/lib/simplecov/useless_results_remover.rb +7 -6
  192. data/lib/simplecov/version.rb +1 -1
  193. data/lib/simplecov/view_coverage/template_compiler.rb +83 -0
  194. data/lib/simplecov/view_coverage.rb +55 -0
  195. data/lib/simplecov-html.rb +1 -0
  196. data/lib/simplecov.rb +57 -113
  197. data/lib/simplecov_json_formatter.rb +1 -0
  198. data/man/simplecov.1 +354 -0
  199. data/sig/simplecov.rbs +528 -595
  200. 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
- module_function
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 SOURCE
21
- # identity — the location, ignoring the class and name elements
22
- # because Ruby records one entry per defined method: the same
23
- # `define_method` block defined onto different classes, or under
24
- # different names, in different processes arrives with different
25
- # receivers or names for the same source method, and matching on the
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
- # "method data that covers nothing".
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
- module_function
8
+ extend self
14
9
 
15
- #
16
- # Combine process explanation
17
- # => ResultsCombiner: hand every result to one accumulator.
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) }
@@ -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
- # Storage for the original command line call that invoked the test suite.
10
- # This has got to be stored as early as possible because i.e. rake and test/unit 2
11
- # have a habit of tampering with ARGV, which makes i.e. the automatic distinction
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
- private
35
+ private
41
36
 
42
- # When parallel_tests (or a compatible runner) is driving the suite,
43
- # tag the command name with this worker's position in the pool.
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 it
56
- # as a bare substring lets any part of the *interpreter* path decide 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". The same
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 the bare substrings used to make. The `test/` patterns below
63
- # still spell their separator as `/` only, so a backslash `test\functional\`
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
- COMMAND_LINE_FRAMEWORKS.find { |pattern, _| pattern.match?(original_run_command.to_s) }&.last
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, which
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
- # Trim any extension so Windows' rspec.bat is recognized too.
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 known
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", -> { defined?(::RSpec) }],
103
+ ["RSpec", -> { defined?(::RSpec) }],
111
104
  ["Unit Tests", -> { defined?(Test::Unit) }], # simplecov:disable
112
- ["Minitest", -> { defined?(::Minitest) }], # simplecov:disable
113
- ["MiniTest", -> { defined?(MiniTest) }] # simplecov:disable
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
- # One-liner keyword options `coverage` accepts, each forwarding to the
29
- # `CoverageCriterion` verb of the same name. `minimum_per_group` is omitted
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
- private
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 SimpleCov::ConfigurationError,
65
- "Unknown `coverage` option #{verb.inspect}. " \
66
- "Supported options are #{COVERAGE_THRESHOLD_OPTIONS.inspect}."
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
- unless criterion == :line
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 — threshold-store writers used by CoverageCriterion. They
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.to_s)
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 SimpleCov::ConfigurationError, "`only:` must be a String path or Regexp, got #{target.inspect}"
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
- # Normalize like `group` does, so `only: :Models` finds the group
113
- # defined as `group "Models"` at check time instead of storing a
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. Each verb writes a
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
- # Overall (suite-wide) minimum for this criterion.
131
- def minimum(percent)
132
- @config.send(:store_overall_threshold, :minimum_coverage, @criterion, percent)
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.send(:store_overall_threshold, :maximum_coverage, @criterion, percent)
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.send(:store_overall_threshold, :maximum_coverage_drop, @criterion, 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
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
- @config.send(:store_minimum_per_file, @criterion, percent, only)
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
- @config.send(:store_minimum_per_group, @criterion, 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)
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