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
@@ -1,10 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SimpleCov
4
- # Selection and validation of the coverage criteria Ruby's `Coverage`
5
- # library should track. Supports `:line` (the historical default),
6
- # `:branch`, `:method`, and `:oneshot_line`. The standalone `:eval`
7
- # toggle lives in `eval_coverage.rb`.
8
4
  module Configuration
9
5
  SUPPORTED_COVERAGE_CRITERIA = %i[line branch method oneshot_line].freeze
10
6
  DEFAULT_COVERAGE_CRITERION = :line
@@ -12,34 +8,36 @@ module SimpleCov
12
8
  LINE_COVERAGE_ALTERNATIVES = {line: :oneshot_line, oneshot_line: :line}.freeze #: line_coverage_alternatives
13
9
  private_constant :LINE_COVERAGE_ALTERNATIVES
14
10
 
15
- # Enable one or more coverage criteria. `:eval` is accepted as a
16
- # shorthand for the standalone eval-coverage toggle.
17
11
  def enable_coverage(*criteria)
18
- criteria.each { |criterion| criterion == :eval ? enable_eval_coverage : add_coverage_criterion(criterion) }
12
+ criteria.each do |criterion|
13
+ criterion.equal?(:eval) ? enable_eval_coverage : add_coverage_criterion(_ = criterion)
14
+ end
19
15
  end
20
16
 
21
- # Remove `criterion` from the set of enabled coverage criteria.
22
- # `:eval` turns the standalone eval-coverage toggle back off,
23
- # mirroring `enable_coverage`. Disabling every criterion raises at
24
- # `start_tracking` (not here), so config files that toggle criteria
25
- # in arbitrary order don't have to worry about transient empty states.
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.
26
20
  def disable_coverage(criterion)
27
- return disable_eval_coverage if criterion == :eval
21
+ return disable_eval_coverage if criterion.equal?(:eval)
28
22
 
29
- raise_if_criterion_unsupported(criterion)
30
- coverage_criteria.delete(criterion)
31
- @primary_coverage = nil if @primary_coverage == criterion
23
+ raise_if_criterion_unsupported(_ = criterion)
24
+ coverage_criteria.delete(_ = criterion)
25
+ @primary_coverage = nil if @primary_coverage.equal?(criterion)
32
26
  end
33
27
 
34
28
  def primary_coverage(criterion = nil)
35
29
  if criterion.nil?
36
30
  @primary_coverage ||= default_primary_coverage
37
31
  else
38
- raise_if_criterion_disabled(criterion)
39
- @primary_coverage = criterion
32
+ self.primary_coverage = criterion
40
33
  end
41
34
  end
42
35
 
36
+ def primary_coverage=(criterion)
37
+ raise_if_criterion_disabled(criterion)
38
+ @primary_coverage = criterion
39
+ end
40
+
43
41
  def coverage_criteria
44
42
  @coverage_criteria ||= Set[DEFAULT_COVERAGE_CRITERION]
45
43
  end
@@ -48,20 +46,17 @@ module SimpleCov
48
46
  coverage_criteria.member?(criterion)
49
47
  end
50
48
 
51
- # Reset the criteria back to the lazy default (`Set[:line]`).
52
49
  def clear_coverage_criteria
53
50
  @coverage_criteria = nil
54
51
  @primary_coverage = nil
55
52
  end
56
53
 
57
- # @api private — called from `SimpleCov.start_tracking` to fail
58
- # fast when the user has disabled every coverage criterion.
59
54
  def validate_coverage_criteria!
60
55
  return unless coverage_criteria.empty?
61
56
 
62
- raise SimpleCov::ConfigurationError,
63
- "At least one coverage criterion must be enabled. " \
64
- "Re-enable one with `enable_coverage :line`, `:branch`, or `:method`."
57
+ raise ConfigurationError,
58
+ "At least one coverage criterion must be enabled. " \
59
+ "Re-enable one with `enable_coverage :line`, `:branch`, or `:method`."
65
60
  end
66
61
 
67
62
  # Whether this run produces line data at all. The oneshot variant counts:
@@ -89,23 +84,23 @@ module SimpleCov
89
84
  coverage_criterion_supported?(:methods)
90
85
  end
91
86
 
92
- # Ask the Coverage runtime itself whether a criterion is supported
93
- # (Ruby >= 3.2). Older Rubies don't expose `Coverage.supported?`, so
94
- # fall back to the historical engine check that line/branch/method
95
- # were unavailable on JRuby. `:eval` was added later, so on older
96
- # Rubies its fallback is "always unsupported" rather than the
97
- # JRuby-only one above. The fallback arm is unreachable from the
98
- # dogfood report, which runs on a newer Ruby.
99
- # simplecov:disable
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".
100
90
  def coverage_criterion_supported?(criterion)
101
- require "coverage"
91
+ load_coverage
102
92
  return Coverage.supported?(criterion) if Coverage.respond_to?(:supported?)
103
93
 
104
- criterion != :eval && RUBY_ENGINE != "jruby"
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"
105
101
  end
106
- # simplecov:enable
107
102
 
108
- private
103
+ private
109
104
 
110
105
  def add_coverage_criterion(criterion)
111
106
  raise_if_criterion_unsupported(criterion)
@@ -114,41 +109,35 @@ module SimpleCov
114
109
  coverage_criteria << criterion
115
110
  end
116
111
 
117
- # If `:line` is enabled, it's the default primary; otherwise fall
118
- # back to whichever criterion the user actually enabled (in
119
- # insertion order). Returning `:line` even when disabled would
120
- # propagate broken state into `minimum_coverage 90`.
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.
121
115
  def default_primary_coverage
122
116
  return DEFAULT_COVERAGE_CRITERION if coverage_criterion_enabled?(DEFAULT_COVERAGE_CRITERION)
123
117
 
124
- # Set#first types as nilable, but an empty criteria set is rejected at
125
- # start_tracking before any caller can observe a nil here.
126
118
  _ = coverage_criteria.first
127
119
  end
128
120
 
129
121
  def raise_if_criterion_disabled(criterion)
130
- # `coverage :eval` by itself IS supported — it's a standalone
131
- # toggle, never in the enabled-criteria set — so the generic
132
- # "unsupported criterion" message below would mislead here.
133
- if criterion == :eval
134
- raise SimpleCov::ConfigurationError,
135
- "Coverage criterion :eval only toggles measuring eval'd code; " \
136
- "it cannot carry thresholds or serve as the primary 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"
137
126
  end
138
127
 
139
128
  raise_if_criterion_unsupported(criterion)
140
129
  return if coverage_criterion_enabled?(criterion)
141
130
 
142
- raise SimpleCov::ConfigurationError,
143
- "Coverage criterion #{criterion}, is disabled! " \
144
- "Please enable it first through enable_coverage #{criterion} (if supported)"
131
+ raise ConfigurationError,
132
+ "Coverage criterion #{criterion}, is disabled! " \
133
+ "Please enable it first through enable_coverage #{criterion} (if supported)"
145
134
  end
146
135
 
147
136
  def raise_if_criterion_unsupported(criterion)
148
137
  return if SUPPORTED_COVERAGE_CRITERIA.member?(criterion)
149
138
 
150
- raise SimpleCov::ConfigurationError,
151
- "Unsupported coverage criterion #{criterion}, supported values are #{SUPPORTED_COVERAGE_CRITERIA}"
139
+ raise ConfigurationError,
140
+ "Unsupported coverage criterion #{criterion}, supported values are #{SUPPORTED_COVERAGE_CRITERIA}"
152
141
  end
153
142
  end
154
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
@@ -1,11 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SimpleCov
4
- # The standalone eval-coverage toggle (`enable_coverage :eval`),
5
- # instrumenting `eval`'d code on Rubies that support it. Kept separate
6
- # from the criteria-set logic in `coverage_criteria.rb` because `:eval`
7
- # is a boolean toggle, never a member of the enabled-criteria set.
8
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.
9
8
  def coverage_for_eval_supported?
10
9
  coverage_criterion_supported?(:eval)
11
10
  end
@@ -14,22 +13,19 @@ module SimpleCov
14
13
  @coverage_for_eval_enabled ||= false
15
14
  end
16
15
 
17
- # DEPRECATED: prefer `enable_coverage :eval`.
18
16
  def enable_coverage_for_eval
19
- SimpleCov::Deprecation.warn("`SimpleCov.enable_coverage_for_eval` is deprecated. " \
20
- "Replace with `SimpleCov.enable_coverage :eval`.")
17
+ Deprecation.warn("`SimpleCov.enable_coverage_for_eval` is deprecated. " \
18
+ "Replace with `SimpleCov.enable_coverage :eval`.")
21
19
  enable_eval_coverage
22
20
  end
23
21
 
24
- private
22
+ private
25
23
 
26
- # Shared implementation backing both `enable_coverage :eval` and
27
- # the deprecated `enable_coverage_for_eval`.
28
24
  def enable_eval_coverage
29
25
  if coverage_for_eval_supported?
30
26
  @coverage_for_eval_enabled = true
31
27
  else
32
- warn "Coverage for eval is not available; Use Ruby 3.2.0 or later"
28
+ warn "Coverage for eval is not available on this Ruby"
33
29
  end
34
30
  end
35
31
 
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SimpleCov
4
- # Inclusion and exclusion methods: `cover` and `skip`, plus the deprecated
5
- # `track_files` and `add_filter` aliases.
6
4
  module Configuration
7
5
  attr_writer :filters
8
6
 
@@ -12,59 +10,38 @@ module SimpleCov
12
10
  # when any `cover` matcher is configured the report drops every file that
13
11
  # doesn't match at least one of them.
14
12
  #
15
- # Strings are interpreted as shell globs (e.g. "lib/**/*.rb"), not
16
- # substring matches — this is a deliberate departure from the legacy
17
- # `add_filter` semantics and matches the way `track_files` already
18
- # interprets its argument.
13
+ # Strings are interpreted as shell globs, not substring matches, a
14
+ # deliberate departure from the legacy `add_filter` semantics.
19
15
  #
20
- # When the matcher is a string-glob, `cover` also expands the glob on
21
- # disk so files that exist but were never required during the run still
22
- # appear in the report (at 0% coverage). This is the "include unloaded
23
- # files" half of the legacy `track_files` behavior, rolled into the
24
- # same call.
25
- #
26
- # SimpleCov.start do
27
- # cover "lib/**/*.rb", "app/**/*.rb"
28
- # cover(/_helper\.rb\z/)
29
- # cover { |sf| sf.lines.count > 5 }
30
- # end
16
+ # A string glob is also expanded on disk, so files that exist but were
17
+ # never required during the run still appear in the report at 0%. This is
18
+ # the "include unloaded files" half of the legacy `track_files` behavior.
31
19
  #
32
20
  def cover(*args, &block)
33
21
  args.each { |arg| cover_filters << build_cover_filter(arg) }
34
- cover_filters << SimpleCov::BlockFilter.new(block) if block
22
+ cover_filters << BlockFilter.new(block) if block
35
23
  cover_filters
36
24
  end
37
25
 
38
- # Returns the list of configured inclusion filters added via `cover`.
39
26
  def cover_filters
40
27
  @cover_filters ||= []
41
28
  end
42
29
 
43
- # Returns the list of string globs passed to `cover` used by the
44
- # disk-discovery pass in `SimpleCov.tracked_file_paths` so files
45
- # matching a `cover` glob appear in the report even when they were
46
- # never required during the suite.
47
- #
48
- # Walks into `ArrayFilter` entries (built when a caller passes an
49
- # array to `cover`) so a glob nested inside `cover(["lib/**/*.rb",
50
- # /helper\.rb\z/])` still drives unloaded-file discovery.
30
+ # The string globs passed to `cover`, used by the disk-discovery pass in
31
+ # `SimpleCov.tracked_file_paths` so files matching a `cover` glob appear in
32
+ # the report even when they were never required during the suite.
51
33
  def cover_globs
52
34
  collect_cover_globs(cover_filters)
53
35
  end
54
36
 
55
- # DEPRECATED: prefer `cover`, which both includes unloaded files (the
56
- # historical `track_files` behavior) and restricts the report to the
57
- # matching set.
58
37
  def track_files(glob)
59
- SimpleCov::Deprecation.warn("`SimpleCov.track_files` is deprecated. " \
60
- "#{track_files_replacement_hint(glob)}")
38
+ Deprecation.warn("`SimpleCov.track_files` is deprecated. " \
39
+ "#{track_files_replacement_hint(glob)}")
61
40
  @tracked_files = glob
62
41
  end
63
42
 
64
- # `track_files(nil)` is the documented way to clear a previously-set
65
- # glob, but `cover(nil)` raises `ConfigurationError`, so don't point
66
- # users at it. The `cover` API has no direct equivalent for "reset
67
- # the inclusion list" — point users at the `@cover_filters` reset.
43
+ # `track_files(nil)` is the documented way to clear a previously-set glob,
44
+ # but `cover(nil)` raises `ConfigurationError`, so don't point users at it.
68
45
  def track_files_replacement_hint(glob)
69
46
  if glob.nil?
70
47
  "Replace with `SimpleCov.cover_filters.clear` — clearing the inclusion list."
@@ -76,86 +53,76 @@ module SimpleCov
76
53
  end
77
54
  end
78
55
 
79
- # Returns the glob used to include files that were not explicitly required.
56
+ # Nil until `cover` names a glob: an unset ivar reads as nil, so the absence
57
+ # needs no guard of its own.
80
58
  def tracked_files
81
- @tracked_files if defined?(@tracked_files)
59
+ @tracked_files
82
60
  end
83
61
 
84
- # Returns the list of configured exclusion filters added via `skip`
85
- # (or the deprecated `add_filter`).
86
62
  def filters
87
63
  @filters ||= []
88
64
  end
89
65
 
90
66
  #
91
67
  # Drop matching files from the coverage report. The inverse of `cover`.
92
- #
93
- # See README for the full grammar; `skip` accepts a String (path-segment
94
- # substring), Regexp, block predicate, or Array of any of those.
68
+ # Accepts a String (path-segment substring), Regexp, block predicate, or
69
+ # Array of any of those.
95
70
  #
96
71
  def skip(filter_argument = nil, &)
97
72
  filters << parse_filter(filter_argument, &)
98
73
  end
99
74
 
100
- # DEPRECATED: alias for `skip`. Same matcher grammar, identical behavior.
101
75
  def add_filter(filter_argument = nil, &block)
102
76
  example = block ? "`SimpleCov.skip { ... }`" : "`SimpleCov.skip #{filter_argument.inspect}`"
103
- SimpleCov::Deprecation.warn("`SimpleCov.add_filter` is deprecated. " \
104
- "Replace with `SimpleCov.skip` (same arguments, same behavior). Example: #{example}.")
77
+ Deprecation.warn("`SimpleCov.add_filter` is deprecated. " \
78
+ "Replace with `SimpleCov.skip` (same arguments, same behavior). Example: #{example}.")
105
79
  skip(filter_argument, &block)
106
80
  end
107
81
 
108
- # Remove any filters whose `filter_argument` equals the given value.
109
- # Returns true when at least one filter was removed, false otherwise.
110
- def remove_filter(filter_argument) # rubocop:disable Naming/PredicateMethod
111
- before = filters.size
112
- filters.reject! { |filter| filter.respond_to?(:filter_argument) && filter.filter_argument == filter_argument }
113
- filters.size != before
82
+ # `reject!` answers nil when it rejected nothing, which is the whole of
83
+ # "was anything removed".
84
+ def remove_filter(filter_argument)
85
+ rejected = filters.reject! do |filter|
86
+ filter.respond_to?(:filter_argument) && filter.filter_argument.eql?(filter_argument)
87
+ end
88
+ !rejected.nil?
114
89
  end
115
90
 
116
- # Remove every filter from the chain, including the defaults installed
117
- # by `SimpleCov.start`.
118
91
  def clear_filters
119
92
  @filters = []
120
93
  end
121
94
 
122
- # Drop every filter previously installed (defaults plus anything
123
- # earlier in this block) so subsequent `skip` calls start from a
124
- # clean slate. Order matters — call this before your own `skip`
125
- # invocations.
95
+ # Order matters: call this before your own `skip` invocations.
126
96
  def no_default_skips
127
97
  clear_filters
128
98
  end
129
99
 
130
- private
100
+ private
131
101
 
132
- # The actual filter processor. Not meant for direct use.
133
102
  def parse_filter(filter_argument = nil, &filter_proc)
134
103
  filter = filter_argument || filter_proc
135
104
 
136
105
  raise ArgumentError, "Please specify either a filter or a block to filter with" unless filter
137
106
 
138
- SimpleCov::Filter.build_filter(filter)
107
+ Filter.build_filter(filter)
139
108
  end
140
109
 
141
- # Build a filter for a `cover` argument. Strings are treated as
142
- # globs (not substrings that's `skip`/`add_filter`'s semantics);
143
- # everything else dispatches exactly like `add_filter`.
110
+ # Strings are treated as globs, not substrings (that's `skip`'s
111
+ # semantics); everything else dispatches exactly like `add_filter`.
144
112
  def build_cover_filter(arg)
145
- SimpleCov::Filter.build_filter(arg, string_filter: SimpleCov::GlobFilter)
146
- rescue SimpleCov::ConfigurationError
147
- raise SimpleCov::ConfigurationError, "Unsupported `cover` argument #{arg.inspect}; " \
148
- "expected a String glob, Regexp, Proc, " \
149
- "SimpleCov::Filter, or Array of those."
113
+ Filter.build_filter(arg, string_filter: GlobFilter)
114
+ rescue ConfigurationError
115
+ raise ConfigurationError, "Unsupported `cover` argument #{arg.inspect}; " \
116
+ "expected a String glob, Regexp, Proc, " \
117
+ "SimpleCov::Filter, or Array of those."
150
118
  end
151
119
 
152
- # Walk a list of cover filters and return the string globs they hold,
153
- # descending into `ArrayFilter` wrappers built by `cover(["a", "b"])`.
120
+ # Descends into `ArrayFilter` wrappers built by `cover(["a", "b"])`.
154
121
  def collect_cover_globs(filter_list)
155
122
  filter_list.flat_map do |filter|
156
123
  case filter
157
- when SimpleCov::GlobFilter then filter.filter_argument
158
- when SimpleCov::ArrayFilter then collect_cover_globs(filter.filter_argument)
124
+ when GlobFilter then filter.filter_argument
125
+ when ArrayFilter then collect_cover_globs(filter.filter_argument)
159
126
  else []
160
127
  end
161
128
  end
@@ -3,35 +3,33 @@
3
3
  require_relative "../formatter/multi_formatter"
4
4
 
5
5
  module SimpleCov
6
- # Formatter selection (`formatter` / `formatters`), reporting toggles
7
- # (`print_errors`), and the deprecated `# :nocov:` token hook.
8
6
  module Configuration
9
7
  attr_writer :formatter, :print_error_status
10
8
 
11
- #
12
- # Gets or sets the configured formatter. Accepts a formatter class
13
- # (instantiated fresh for every report) or a ready-built instance,
14
- # which is how constructor options are passed — e.g.
15
- # `formatter SimpleCov::Formatter::HTMLFormatter.new(silent: true)`
16
- # to suppress the "Coverage report generated" status line (see
17
- # #1240). Pass `false` (or `nil`) to opt out of formatting
18
- # entirely — worker processes in big parallel CI setups (see #964)
19
- # only need their `.resultset.json` on disk so a final
20
- # `SimpleCov.collate` job can produce the report; running them
21
- # without a formatter saves the per-job HTML/multi-formatter
22
- # overhead.
23
- #
9
+ BUILT_IN_FORMATS = {
10
+ html: :HTMLFormatter, json: :JSONFormatter, simple: :SimpleFormatter, baseline: :BaselineFormatter
11
+ }.freeze
12
+ private_constant :BUILT_IN_FORMATS
13
+
14
+ LAZY_FORMAT_REQUIRES = {html: "../../simplecov-html"}.freeze
15
+ private_constant :LAZY_FORMAT_REQUIRES
16
+
17
+ def formats(*names)
18
+ return formatters if names.empty?
19
+
20
+ self.formatters = names.map { |name| resolve_format(name) }
21
+ formatters
22
+ end
23
+
24
24
  def formatter(formatter = :__no_arg__)
25
25
  case formatter
26
26
  when :__no_arg__
27
27
  @formatter
28
28
  else
29
- @formatter = formatter || nil # normalize `false` to `nil`
29
+ @formatter = formatter || nil
30
30
  end
31
31
  end
32
32
 
33
- # Sets the configured formatters. Pass `[]` to opt out of
34
- # formatting entirely; see `formatter` for the rationale.
35
33
  def formatters(formatters = :__no_arg__)
36
34
  case formatters
37
35
  when :__no_arg__
@@ -39,100 +37,93 @@ module SimpleCov
39
37
  configured ? [configured] : []
40
38
  else
41
39
  self.formatters = formatters
42
- formatters
43
40
  end
44
41
  end
45
42
 
46
- # Sets the configured formatters. Equivalent to `formatters [...]`.
47
- # Accepts a single formatter as well as an Array, matching the pre-1.0 behavior
48
- # where `MultiFormatter.new` normalized its input. Elements may be
49
- # formatter classes or ready-built instances; see `formatter`.
50
- # `nil`, `false`, and `[]` all opt out of formatting entirely —
51
- # `false` normalized like `formatter false` does, since `Array(false)`
52
- # would otherwise smuggle it in as a "formatter" that can only fail.
43
+ # `nil`, `false`, and `[]` all opt out of formatting entirely. `false` is
44
+ # normalized first, since `Array(false)` would otherwise smuggle it in as a
45
+ # "formatter" that can only fail.
53
46
  def formatters=(formatters)
54
- formatters = Array(formatters || nil)
55
- @formatter = formatters.empty? ? nil : SimpleCov::Formatter::MultiFormatter.new(formatters)
56
- end
57
-
58
- #
59
- # Get or set whether SimpleCov colorizes its stderr diagnostics. Accepts
60
- # `true` (always on), `false` (always off), or `:auto` (default: defer
61
- # to `SimpleCov::Color`, which checks `$stderr.tty?` with `NO_COLOR`
62
- # and `FORCE_COLOR` overrides). An explicit `true`/`false` wins over
63
- # both auto-detection and the env vars, which is the right escape
64
- # hatch when stderr is being piped through a wrapper that still
65
- # renders ANSI in its own terminal (parallel_tests with
66
- # `--combine-stderr`, log multiplexers, some CI runners). See #1157.
67
- #
47
+ @formatter = combined_formatter(Array(formatters || nil))
48
+ end
49
+
68
50
  def color(value = :__no_arg__)
69
- return defined?(@color) ? @color : :auto if value == :__no_arg__
51
+ return instance_variable_defined?(:@color) ? @color : :auto if value.eql?(:__no_arg__)
52
+
53
+ self.color = _ = value
54
+ end
70
55
 
56
+ def color=(value)
71
57
  @color = value
72
58
  end
73
59
 
74
- #
75
- # Get or set whether SimpleCov prints its own diagnostic warnings to
76
- # stderr. Covers per-check threshold violations, the trailing
77
- # "SimpleCov failed with exit ..." summary, and the deferred-report /
78
- # previous-error notices. Defaults to true. Set to false to silence
79
- # SimpleCov entirely when parsing tooling output (see issue #1155).
80
- #
81
60
  def print_errors(value = :__no_arg__)
82
- return defined?(@print_error_status) ? @print_error_status : true if value == :__no_arg__
61
+ return instance_variable_defined?(:@print_error_status) ? @print_error_status : true if value.eql?(:__no_arg__)
83
62
 
63
+ self.print_errors = _ = value
64
+ end
65
+
66
+ def print_errors=(value)
84
67
  @print_error_status = value
85
68
  end
86
69
 
87
- #
88
- # Get or set whether `coverage.json` includes the full source-text
89
- # array for every file. Defaults to true. Set to false when a
90
- # downstream tool reads the project's source files directly and
91
- # only needs the coverage metrics, so `coverage.json` doesn't carry
92
- # a copy of the source tree (which dominates the payload on larger
93
- # projects).
94
- #
95
- # The HTML report's embedded data always includes source — the
96
- # client-side renderer needs it. Only `coverage.json` honors
97
- # this setting.
98
- #
99
- # SimpleCov.start do
100
- # source_in_json false
101
- # end
102
- #
103
70
  def source_in_json(value = :__no_arg__)
104
- return defined?(@source_in_json) ? @source_in_json : true if value == :__no_arg__
71
+ return instance_variable_defined?(:@source_in_json) ? @source_in_json : true if value.eql?(:__no_arg__)
72
+
73
+ self.source_in_json = _ = value
74
+ end
105
75
 
76
+ def source_in_json=(value)
106
77
  @source_in_json = value
107
78
  end
108
79
 
109
- # DEPRECATED: alias for `print_errors`. Same value, same behavior.
110
80
  def print_error_status
111
- SimpleCov::Deprecation.warn("`SimpleCov.print_error_status` is deprecated. " \
112
- "Replace with `SimpleCov.print_errors` (same value).")
113
- defined?(@print_error_status) ? @print_error_status : true
81
+ Deprecation.warn("`SimpleCov.print_error_status` is deprecated. " \
82
+ "Replace with `SimpleCov.print_errors` (same value).")
83
+ instance_variable_defined?(:@print_error_status) ? @print_error_status : true
114
84
  end
115
85
 
116
- #
117
- # DEPRECATED: configure `# :nocov:` token override. Prefer
118
- # `# simplecov:disable` / `# simplecov:enable` block comments (see
119
- # SimpleCov::Directive). The `# :nocov:` toggle and this hook will
120
- # be removed in a future release.
121
- #
122
86
  def nocov_token(nocov_token = nil)
123
- SimpleCov::Deprecation.warn("`SimpleCov.nocov_token` and `SimpleCov.skip_token` are deprecated. " \
124
- "Replace with `# simplecov:disable` / `# simplecov:enable` block comments.")
87
+ Deprecation.warn("`SimpleCov.nocov_token` and `SimpleCov.skip_token` are deprecated. " \
88
+ "Replace with `# simplecov:disable` / `# simplecov:enable` block comments.")
125
89
  current_nocov_token(nocov_token)
126
90
  end
127
- alias skip_token nocov_token
91
+ alias_method :skip_token, :nocov_token
128
92
 
129
- # Internal accessor used by SimpleCov to recognise `# :nocov:`
130
- # markers without emitting the public-API deprecation warning. Will
131
- # be removed alongside the deprecated `nocov_token` setter.
132
93
  def current_nocov_token(value = nil)
133
- return @nocov_token if defined?(@nocov_token) && value.nil?
94
+ return @nocov_token if instance_variable_defined?(:@nocov_token) && value.nil?
134
95
 
135
96
  @nocov_token = value || "nocov"
136
97
  end
98
+
99
+ private
100
+
101
+ # An empty list is how a project opts out of formatting.
102
+ def combined_formatter(formatters)
103
+ return if formatters.empty?
104
+
105
+ Formatter::MultiFormatter.new(formatters)
106
+ end
107
+
108
+ def resolve_format(name)
109
+ return name unless name.instance_of?(Symbol)
110
+
111
+ constant = BUILT_IN_FORMATS[name]
112
+ unless constant
113
+ raise ConfigurationError,
114
+ "Unknown format #{name.inspect}. Built-in formats are :html, :json, :simple, and :baseline; " \
115
+ "pass a formatter class or instance for anything else."
116
+ end
117
+
118
+ require_html_formatter(name)
119
+ Formatter.const_get(constant)
120
+ end
121
+
122
+ # `formats :html` has to work in a process that never required the HTML
123
+ # formatter, which under SIMPLECOV_NO_DEFAULTS nothing else has.
124
+ def require_html_formatter(name)
125
+ path = LAZY_FORMAT_REQUIRES[name]
126
+ require_relative(path) if path
127
+ end
137
128
  end
138
129
  end