simplecov 1.0.3 → 1.1.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 (109) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +65 -1439
  3. data/lib/simplecov/atomic_file.rb +70 -0
  4. data/lib/simplecov/cli/clean.rb +43 -3
  5. data/lib/simplecov/cli/command_helpers.rb +55 -0
  6. data/lib/simplecov/cli/coverage.rb +21 -28
  7. data/lib/simplecov/cli/coverage_file.rb +65 -0
  8. data/lib/simplecov/cli/diff.rb +43 -48
  9. data/lib/simplecov/cli/dotfile.rb +12 -6
  10. data/lib/simplecov/cli/merge.rb +12 -10
  11. data/lib/simplecov/cli/open.rb +3 -5
  12. data/lib/simplecov/cli/report.rb +31 -23
  13. data/lib/simplecov/cli/serve/report_preparer.rb +29 -0
  14. data/lib/simplecov/cli/serve/static_file_handler.rb +122 -0
  15. data/lib/simplecov/cli/serve.rb +30 -94
  16. data/lib/simplecov/cli/uncovered.rb +20 -25
  17. data/lib/simplecov/cli.rb +15 -2
  18. data/lib/simplecov/combine/branches_combiner.rb +43 -19
  19. data/lib/simplecov/combine/coverage_accumulator.rb +268 -0
  20. data/lib/simplecov/combine/identity_interner.rb +30 -0
  21. data/lib/simplecov/combine/interned_counts.rb +30 -0
  22. data/lib/simplecov/combine/lines_combiner.rb +48 -20
  23. data/lib/simplecov/combine/methods_combiner.rb +36 -12
  24. data/lib/simplecov/combine/results_combiner.rb +12 -37
  25. data/lib/simplecov/combine.rb +5 -23
  26. data/lib/simplecov/command_guesser.rb +66 -9
  27. data/lib/simplecov/configuration/coverage.rb +12 -15
  28. data/lib/simplecov/configuration/coverage_criteria.rb +34 -38
  29. data/lib/simplecov/configuration/eval_coverage.rb +41 -0
  30. data/lib/simplecov/configuration/filters.rb +11 -44
  31. data/lib/simplecov/configuration/formatting.rb +6 -3
  32. data/lib/simplecov/configuration/groups.rb +42 -0
  33. data/lib/simplecov/configuration/merging.rb +8 -7
  34. data/lib/simplecov/configuration/thresholds.rb +14 -13
  35. data/lib/simplecov/configuration.rb +13 -43
  36. data/lib/simplecov/coverage_json.rb +24 -0
  37. data/lib/simplecov/coverage_statistics.rb +1 -1
  38. data/lib/simplecov/coverage_violations.rb +15 -5
  39. data/lib/simplecov/defaults.rb +7 -3
  40. data/lib/simplecov/directive.rb +1 -1
  41. data/lib/simplecov/exit_codes/check.rb +33 -0
  42. data/lib/simplecov/exit_codes/maximum_coverage_drop_check.rb +11 -20
  43. data/lib/simplecov/exit_codes/maximum_overall_coverage_check.rb +3 -16
  44. data/lib/simplecov/exit_codes/minimum_coverage_by_file_check.rb +14 -23
  45. data/lib/simplecov/exit_codes/minimum_coverage_by_group_check.rb +14 -25
  46. data/lib/simplecov/exit_codes/minimum_overall_coverage_check.rb +4 -17
  47. data/lib/simplecov/exit_codes.rb +1 -0
  48. data/lib/simplecov/exit_handling.rb +11 -41
  49. data/lib/simplecov/file_list.rb +5 -10
  50. data/lib/simplecov/filter.rb +43 -9
  51. data/lib/simplecov/formatter/base.rb +11 -0
  52. data/lib/simplecov/formatter/coverage_json_writer.rb +97 -0
  53. data/lib/simplecov/formatter/html_formatter/public/index.html +35 -5
  54. data/lib/simplecov/formatter/html_formatter/viewer_data_validator.rb +96 -0
  55. data/lib/simplecov/formatter/html_formatter.rb +67 -47
  56. data/lib/simplecov/formatter/json_formatter/errors_formatter.rb +54 -56
  57. data/lib/simplecov/formatter/json_formatter/result_hash_formatter.rb +78 -88
  58. data/lib/simplecov/formatter/json_formatter/source_file_formatter.rb +70 -76
  59. data/lib/simplecov/formatter/json_formatter.rb +5 -48
  60. data/lib/simplecov/formatter/multi_formatter.rb +1 -1
  61. data/lib/simplecov/formatter/simple_formatter.rb +8 -5
  62. data/lib/simplecov/formatter.rb +5 -0
  63. data/lib/simplecov/group_names.rb +32 -0
  64. data/lib/simplecov/last_run.rb +16 -9
  65. data/lib/simplecov/lines_classifier.rb +29 -8
  66. data/lib/simplecov/load_global_config.rb +5 -2
  67. data/lib/simplecov/parallel_adapters/base.rb +17 -0
  68. data/lib/simplecov/parallel_adapters/generic.rb +2 -2
  69. data/lib/simplecov/parallel_adapters/parallel_tests.rb +2 -2
  70. data/lib/simplecov/parallel_coordination.rb +6 -1
  71. data/lib/simplecov/parallel_result_merger.rb +230 -0
  72. data/lib/simplecov/report_deferral.rb +49 -0
  73. data/lib/simplecov/report_stamp.rb +28 -0
  74. data/lib/simplecov/result.rb +40 -10
  75. data/lib/simplecov/result_merger/resultset_file.rb +43 -7
  76. data/lib/simplecov/result_merger/resultset_run_identity.rb +67 -0
  77. data/lib/simplecov/result_merger/resultset_store.rb +15 -12
  78. data/lib/simplecov/result_merger/unloaded_files.rb +103 -0
  79. data/lib/simplecov/result_merger.rb +69 -42
  80. data/lib/simplecov/result_processing.rb +82 -43
  81. data/lib/simplecov/run_identity.rb +77 -0
  82. data/lib/simplecov/simulate_coverage.rb +35 -11
  83. data/lib/simplecov/source_file/method.rb +7 -1
  84. data/lib/simplecov/source_file/ruby_data_parser.rb +6 -2
  85. data/lib/simplecov/source_file/skip_chunks.rb +7 -10
  86. data/lib/simplecov/source_file/source_loader.rb +23 -7
  87. data/lib/simplecov/source_file/statistics.rb +24 -16
  88. data/lib/simplecov/static_coverage_extractor/condition_folding.rb +203 -13
  89. data/lib/simplecov/static_coverage_extractor/location_conventions.rb +19 -30
  90. data/lib/simplecov/static_coverage_extractor/method_collector.rb +7 -0
  91. data/lib/simplecov/static_coverage_extractor/prism_compat.rb +55 -0
  92. data/lib/simplecov/static_coverage_extractor/value_position.rb +6 -14
  93. data/lib/simplecov/static_coverage_extractor/visitor.rb +23 -36
  94. data/lib/simplecov/unloaded_file_injector.rb +75 -0
  95. data/lib/simplecov/version.rb +1 -1
  96. data/lib/simplecov.rb +11 -5
  97. data/schemas/coverage-v1.0.schema.json +2 -2
  98. data/schemas/coverage.schema.json +2 -2
  99. data/sig/simplecov.rbs +200 -66
  100. metadata +28 -14
  101. data/doc/alternate-formatters.md +0 -66
  102. data/doc/commercial-services.md +0 -25
  103. data/doc/editor-integration.md +0 -18
  104. data/lib/simplecov/combine/files_combiner.rb +0 -70
  105. data/lib/simplecov/formatter/html_formatter/public/application.css +0 -1
  106. data/lib/simplecov/formatter/html_formatter/public/application.js +0 -18
  107. data/lib/simplecov/formatter/html_formatter/public/favicon_green.png +0 -0
  108. data/lib/simplecov/formatter/html_formatter/public/favicon_red.png +0 -0
  109. data/lib/simplecov/formatter/html_formatter/public/favicon_yellow.png +0 -0
data/lib/simplecov.rb CHANGED
@@ -158,10 +158,8 @@ module SimpleCov
158
158
  ::Process.respond_to?(:_fork)
159
159
  # simplecov:enable
160
160
 
161
- # Trigger adapter selection now so the (possibly lazy) parallel_tests
162
- # gem load happens at start_tracking time rather than mid-suite.
163
- # `current` is memoized; subsequent calls are cheap.
164
- SimpleCov::ParallelAdapters.current
161
+ # Select the parallel adapter and generate identities before any forks.
162
+ SimpleCov::RunIdentity.prepare
165
163
 
166
164
  @result = nil
167
165
  self.pid = Process.pid
@@ -230,6 +228,7 @@ end
230
228
  require "forwardable"
231
229
  require_relative "simplecov/color"
232
230
  require_relative "simplecov/deprecation"
231
+ require_relative "simplecov/group_names"
233
232
  require_relative "simplecov/configuration"
234
233
  SimpleCov.extend SimpleCov::Configuration
235
234
  require_relative "simplecov/coverage_statistics"
@@ -245,22 +244,29 @@ require_relative "simplecov/result"
245
244
  require_relative "simplecov/filter"
246
245
  require_relative "simplecov/formatter"
247
246
  require_relative "simplecov/last_run"
247
+ require_relative "simplecov/report_stamp"
248
248
  require_relative "simplecov/lines_classifier"
249
249
  require_relative "simplecov/result_merger"
250
+ require_relative "simplecov/parallel_result_merger"
250
251
  require_relative "simplecov/parallel_adapters"
252
+ require_relative "simplecov/run_identity"
253
+ SimpleCov.extend SimpleCov::RunIdentity::Accessors
251
254
  require_relative "simplecov/command_guesser"
252
255
  require_relative "simplecov/version"
253
256
  require_relative "simplecov/result_adapter"
254
257
  require_relative "simplecov/combine"
258
+ require_relative "simplecov/combine/identity_interner"
255
259
  require_relative "simplecov/combine/branches_combiner"
256
260
  require_relative "simplecov/combine/methods_combiner"
257
- require_relative "simplecov/combine/files_combiner"
258
261
  require_relative "simplecov/combine/lines_combiner"
262
+ require_relative "simplecov/combine/coverage_accumulator"
259
263
  require_relative "simplecov/combine/results_combiner"
260
264
  require_relative "simplecov/useless_results_remover"
261
265
  require_relative "simplecov/simulate_coverage"
266
+ require_relative "simplecov/unloaded_file_injector"
262
267
  require_relative "simplecov/result_processing"
263
268
  require_relative "simplecov/exit_handling"
269
+ require_relative "simplecov/report_deferral"
264
270
  require_relative "simplecov/parallel_coordination"
265
271
 
266
272
  # Load default config
@@ -254,7 +254,7 @@
254
254
  },
255
255
  "total": {"type": "integer", "minimum": 0, "description": "Executable lines: `covered + missed`. Does not include `omitted`."},
256
256
  "percent": {"type": "number"},
257
- "strength": {"type": "number", "description": "Average number of executions across covered lines (hits per covered line)."}
257
+ "strength": {"type": "number", "description": "Average executions per coverable line: the sum of hit counts divided by `covered + missed`."}
258
258
  }
259
259
  },
260
260
  "coverage_statistic": {
@@ -266,7 +266,7 @@
266
266
  "missed": {"type": "integer", "minimum": 0},
267
267
  "total": {"type": "integer", "minimum": 0, "description": "Total branches or methods: `covered + missed`."},
268
268
  "percent": {"type": "number"},
269
- "strength": {"type": "number", "description": "Average number of executions across covered branches or methods (hits per covered item)."}
269
+ "strength": {"type": "number", "description": "Average executions per coverable branch or method: the sum of hit counts divided by `covered + missed`."}
270
270
  }
271
271
  },
272
272
  "line_coverage": {
@@ -254,7 +254,7 @@
254
254
  },
255
255
  "total": {"type": "integer", "minimum": 0, "description": "Executable lines: `covered + missed`. Does not include `omitted`."},
256
256
  "percent": {"type": "number"},
257
- "strength": {"type": "number", "description": "Average number of executions across covered lines (hits per covered line)."}
257
+ "strength": {"type": "number", "description": "Average executions per coverable line: the sum of hit counts divided by `covered + missed`."}
258
258
  }
259
259
  },
260
260
  "coverage_statistic": {
@@ -266,7 +266,7 @@
266
266
  "missed": {"type": "integer", "minimum": 0},
267
267
  "total": {"type": "integer", "minimum": 0, "description": "Total branches or methods: `covered + missed`."},
268
268
  "percent": {"type": "number"},
269
- "strength": {"type": "number", "description": "Average number of executions across covered branches or methods (hits per covered item)."}
269
+ "strength": {"type": "number", "description": "Average executions per coverable branch or method: the sum of hit counts divided by `covered + missed`."}
270
270
  }
271
271
  },
272
272
  "line_coverage": {
data/sig/simplecov.rbs CHANGED
@@ -2,6 +2,7 @@
2
2
  module SimpleCov
3
3
  # A coverage criterion that can be enabled, measured, and reported.
4
4
  type criterion = :line | :branch | :method
5
+ type line_coverage_alternatives = Hash[criterion | :oneshot_line, :line | :oneshot_line]
5
6
 
6
7
  # Anything accepted by `skip` / `group` (and the deprecated
7
8
  # `add_filter` / `add_group`): a path-segment substring, a Regexp, a
@@ -17,6 +18,14 @@ module SimpleCov
17
18
  class ConfigurationError < StandardError
18
19
  end
19
20
 
21
+ module GroupNames
22
+ UNGROUPED: String
23
+
24
+ def self?.normalize: (untyped group_name) -> String
25
+
26
+ def self?.validate!: (Array[String] group_names) -> Array[String]
27
+ end
28
+
20
29
  # Maps SimpleCov's criterion names to the keys `Coverage.start` expects.
21
30
  CRITERION_TO_RUBY_COVERAGE: Hash[Symbol, Symbol]
22
31
 
@@ -27,12 +36,18 @@ module SimpleCov
27
36
  self.@autoloading_dot_simplecov: bool?
28
37
  self.@dot_simplecov_start_warned: bool?
29
38
  self.@at_exit_hook_installed: bool?
39
+ self.@run_id: String?
40
+ self.@worker_id: String?
30
41
 
31
42
  attr_accessor self.pid: Integer?
32
43
 
33
44
  # When this process started tracking coverage.
34
45
  attr_accessor self.process_start_time: Time?
35
46
 
47
+ def self.run_id: () -> String
48
+
49
+ def self.worker_id: () -> String
50
+
36
51
  # Should something external (e.g. the Minitest plugin) own the
37
52
  # at_exit behavior instead of SimpleCov's own hook?
38
53
  attr_accessor self.external_at_exit: bool?
@@ -107,8 +122,10 @@ module SimpleCov
107
122
 
108
123
  # Collate a series of resultset files into a single merged report. By
109
124
  # default ignores `merge_timeout` so every listed resultset merges;
110
- # pass `ignore_timeout: false` to honor it.
111
- def self.collate: (Array[String] result_filenames, ?(String | Symbol)? profile, ?ignore_timeout: bool) ?{ () [self: singleton(SimpleCov)] -> void } -> void
125
+ # pass `ignore_timeout: false` to honor it. `processes:` above 1 fans the
126
+ # merge out across that many forked workers, for an identical report;
127
+ # anything below 1 is taken as 1.
128
+ def self.collate: (Array[String] result_filenames, ?(String | Symbol)? profile, ?processes: Integer, ?ignore_timeout: bool) ?{ () [self: singleton(SimpleCov)] -> void } -> void
112
129
 
113
130
  # The result for the current coverage run, merged across test suites
114
131
  # when merging is active. nil when no coverage was tracked.
@@ -143,15 +160,27 @@ module SimpleCov
143
160
 
144
161
  def self.grouped_file_set: (Hash[String, FileList] grouped) -> Set[SourceFile]
145
162
 
146
- def self.add_not_loaded_files: (Hash[String, untyped] result) -> [Hash[String, untyped], Set[String]]
147
-
148
163
  def self.unloaded_file_discovery_globs: () -> Array[String]
149
164
 
150
- def self.discover_unloaded_paths: (Array[String] globs) -> Array[String]
165
+ # Every path this process was told to track, whether or not it loaded them.
166
+ # Recorded on the result (and from there into the resultset) so that a merge
167
+ # in another process can inject the ones nobody loaded without needing this
168
+ # process's `cover` / `track_files` configuration.
169
+ def self.tracked_file_paths: () -> Array[String]
170
+
171
+ # Add simulated coverage for each of `candidate_paths` the result doesn't
172
+ # already carry, and return it with the set of paths added.
173
+ #
174
+ # @api private — the seam `SimpleCov::ResultMerger` injects through.
175
+ def self.inject_unloaded_files: (Hash[String, untyped] result, Array[String] candidate_paths, ?synthesize: bool?, ?lines: bool?) -> [Hash[String, untyped], Set[String]]
151
176
 
152
- def self.inject_unloaded_files: (Hash[String, untyped] result, Array[String] candidate_paths) -> [Hash[String, untyped], Set[String]]
177
+ # Build this process's slice of the coverage result. `standalone` is true
178
+ # when no merge step follows, which makes this the final result: it is the
179
+ # one that reports dropped source files, and the one that injects unloaded
180
+ # files. When a merge does follow, both jobs belong to the merged result.
181
+ def self.collect_own_coverage: (standalone: bool) -> void
153
182
 
154
- def self.process_coverage_result: (report: bool) -> void
183
+ def self.process_coverage_result: (report: bool, ?inject_unloaded: bool) -> void
155
184
  end
156
185
 
157
186
  # ----- configuration -----
@@ -190,12 +219,15 @@ module SimpleCov
190
219
  # The hash of available profiles.
191
220
  def profiles: () -> Profiles
192
221
 
193
- # Configure SimpleCov in a block, evaluated with `self` bound to
194
- # the configuration target (usually the SimpleCov module itself):
222
+ # Configure SimpleCov in a block. A zero-argument block runs with the
223
+ # configuration target as `self`; a parameterized block keeps its lexical
224
+ # `self` and receives the target explicitly:
195
225
  #
196
226
  # SimpleCov.configure { skip "test" }
227
+ # SimpleCov.configure { |config| config.skip caller_filter }
197
228
  #
198
- def configure: () { () [self: self] -> void } -> void
229
+ def configure: () { () [self: self] -> untyped } -> self
230
+ | () { (self config) -> untyped } -> self
199
231
 
200
232
  # Gets or sets the behavior that processes coverage results at
201
233
  # process exit. The default stores/merges the current result and
@@ -215,14 +247,6 @@ module SimpleCov
215
247
  # capitalized, with underscores replaced by spaces.
216
248
  def project_name: (?String? new_name) -> String
217
249
 
218
- private
219
-
220
- # Copy instance variables from block_context into self, saving any
221
- # of ours that would be clobbered.
222
- def swap_ivars_from: (untyped block_context) -> Hash[Symbol, untyped]
223
-
224
- # Copy instance variables back to block_context and restore ours.
225
- def restore_ivars: (untyped block_context, Hash[Symbol, untyped] saved) -> void
226
250
  end
227
251
  end
228
252
 
@@ -230,7 +254,8 @@ end
230
254
 
231
255
  # Selection and validation of the coverage criteria Ruby's `Coverage`
232
256
  # library should track: `:line` (default), `:branch`, `:method`, and
233
- # `:oneshot_line`, plus the standalone `:eval` toggle.
257
+ # `:oneshot_line`. The standalone `:eval` toggle lives in
258
+ # `configuration/eval_coverage.rb`.
234
259
  module SimpleCov
235
260
  module Configuration
236
261
  # Members are line, branch, method, and oneshot_line (%i[] literals
@@ -241,18 +266,21 @@ module SimpleCov
241
266
 
242
267
  ONESHOT_LINE_COVERAGE_CRITERION: :oneshot_line
243
268
 
269
+ LINE_COVERAGE_ALTERNATIVES: line_coverage_alternatives
270
+
244
271
  @primary_coverage: (criterion | :oneshot_line)?
245
272
  @coverage_criteria: Set[criterion | :oneshot_line]?
246
273
  @coverage_for_eval_enabled: bool?
247
274
 
248
275
  # Enable one or more coverage criteria. `:eval` is accepted as a
249
- # shorthand for the standalone eval-coverage toggle;
250
- # `:oneshot_line` replaces `:line`.
276
+ # shorthand for the standalone eval-coverage toggle. `:line` and
277
+ # `:oneshot_line` replace each other; the last enabled mode wins.
251
278
  def enable_coverage: (*(criterion | :oneshot_line | :eval) criteria) -> void
252
279
 
253
280
  # Remove `criterion` from the set of enabled coverage criteria.
281
+ # `:eval` turns the standalone eval-coverage toggle back off.
254
282
  # Disabling every criterion raises at `start_tracking`, not here.
255
- def disable_coverage: ((criterion | :oneshot_line) criterion) -> void
283
+ def disable_coverage: ((criterion | :oneshot_line | :eval) criterion) -> void
256
284
 
257
285
  # Gets the report's leading criterion, or sets it. The setter
258
286
  # validates at runtime that the criterion is enabled; the parameter
@@ -272,6 +300,12 @@ module SimpleCov
272
300
 
273
301
  def validate_coverage_criteria!: () -> void
274
302
 
303
+ # Whether this run produces line data at all. The oneshot variant counts:
304
+ # `ResultAdapter` turns its executed-line list back into a line array. A
305
+ # branch-only or method-only run produces none, and `Coverage.result`
306
+ # entries for the files it loaded carry no `"lines"` key.
307
+ def line_coverage?: () -> bool
308
+
275
309
  def branch_coverage?: () -> bool
276
310
 
277
311
  def branch_coverage_supported?: () -> bool
@@ -280,21 +314,14 @@ module SimpleCov
280
314
 
281
315
  def method_coverage_supported?: () -> bool
282
316
 
283
- def coverage_for_eval_supported?: () -> bool
284
-
285
317
  # Whether the running Ruby's Coverage library supports the given
286
318
  # criterion, named in Coverage's own plural vocabulary
287
319
  # (`:lines`, `:branches`, `:methods`, `:oneshot_lines`, `:eval`).
288
320
  def coverage_criterion_supported?: (Symbol criterion) -> bool
289
321
 
290
- def coverage_for_eval_enabled?: () -> bool
291
-
292
- # DEPRECATED: use `enable_coverage :eval` instead.
293
- def enable_coverage_for_eval: () -> void
294
-
295
322
  private
296
323
 
297
- def enable_eval_coverage: () -> void
324
+ def add_coverage_criterion: ((criterion | :oneshot_line) criterion) -> void
298
325
 
299
326
  def default_primary_coverage: () -> (criterion | :oneshot_line)
300
327
 
@@ -306,6 +333,28 @@ module SimpleCov
306
333
  end
307
334
  end
308
335
 
336
+ # ----- configuration/eval_coverage -----
337
+
338
+ # The standalone eval-coverage toggle (`enable_coverage :eval`),
339
+ # instrumenting `eval`'d code on Rubies that support it. A boolean
340
+ # toggle, never a member of the enabled-criteria set.
341
+ module SimpleCov
342
+ module Configuration
343
+ def coverage_for_eval_supported?: () -> bool
344
+
345
+ def coverage_for_eval_enabled?: () -> bool
346
+
347
+ # DEPRECATED: use `enable_coverage :eval` instead.
348
+ def enable_coverage_for_eval: () -> void
349
+
350
+ private
351
+
352
+ def enable_eval_coverage: () -> void
353
+
354
+ def disable_eval_coverage: () -> void
355
+ end
356
+ end
357
+
309
358
  # ----- configuration/coverage -----
310
359
 
311
360
  # The criterion-first `coverage` configuration method: naming a
@@ -331,9 +380,9 @@ module SimpleCov
331
380
  # Configure (and, unless `enabled: false`, enable) a coverage
332
381
  # criterion. `primary: true` makes it the report's leading
333
382
  # criterion; `oneshot: true` (only for `:line`) selects
334
- # oneshot-lines mode; `:eval` is enable-only. Threshold keywords
335
- # mirror the block verbs for one-liner use. Returns the criterion
336
- # symbol the thresholds were stored under.
383
+ # oneshot-lines mode. Threshold keywords mirror the block verbs
384
+ # for one-liner use. Returns the criterion symbol the thresholds
385
+ # were stored under.
337
386
  # The threshold keywords (minimum:, maximum:, exact:, maximum_drop:,
338
387
  # minimum_per_file:) arrive via **thresholds in the implementation, so
339
388
  # they are typed as a Numeric kwarg splat. The block runs under
@@ -346,9 +395,7 @@ module SimpleCov
346
395
 
347
396
  def enable_coverage_criterion: ((criterion | :eval) criterion, enabled: bool, oneshot: bool) -> (criterion | :oneshot_line | :eval)
348
397
 
349
- def enable_oneshot_line: ((criterion | :eval) criterion) -> :oneshot_line
350
-
351
- def enable_eval_coverage_criterion: () -> :eval
398
+ def resolve_criterion_variant: ((criterion | :eval) criterion, bool oneshot) -> (criterion | :oneshot_line | :eval)
352
399
 
353
400
  # Threshold-store writers used by CoverageCriterion via `send`.
354
401
  # They accept any criterion Symbol; `raise_on_invalid_coverage`
@@ -604,7 +651,7 @@ module SimpleCov
604
651
  # into a single coverage report. Defaults to true.
605
652
  def merging: (?bool? use) -> bool
606
653
 
607
- # Get or set whether this process owns final merge processing:
654
+ # Get or set whether SimpleCov's selected final process owns merge processing:
608
655
  # waiting for sibling workers, merging, formatting, enforcing
609
656
  # thresholds, and writing `.last_run.json`. Defaults to true except
610
657
  # for recognized multi-worker parallel runs writing to a custom
@@ -618,7 +665,7 @@ module SimpleCov
618
665
 
619
666
  # DEPRECATED: use `merging` instead (same value, same behavior).
620
667
  # Returns nil when merging had already been disabled.
621
- def use_merging: (?bool? use) -> bool?
668
+ def use_merging: (?bool? use) -> bool
622
669
 
623
670
  # The maximum age (in seconds) of a resultset to still be included
624
671
  # in merged results. Defaults to 600.
@@ -644,9 +691,11 @@ module SimpleCov
644
691
  def inferred_finalize_merge_warning: () -> String
645
692
 
646
693
  # Host contract: defined on SimpleCov itself (result_processing.rb).
647
- # This module is only ever extended onto SimpleCov, where the
648
- # method is available to `merge_finalization_owner?`.
694
+ # This module is only ever extended onto SimpleCov, where both methods
695
+ # are available to `merge_finalization_owner?`.
649
696
  def collating_result?: () -> boolish
697
+
698
+ def final_result_process?: () -> boolish
650
699
  end
651
700
  end
652
701
 
@@ -710,6 +759,9 @@ module SimpleCov
710
759
 
711
760
  private
712
761
 
762
+ # Shared normalize-and-validate step behind every threshold setter.
763
+ def normalized_threshold: ((Numeric | coverage_thresholds) coverage, String setting) -> coverage_thresholds
764
+
713
765
  def partition_per_file_thresholds: (Hash[Symbol | String | Regexp, Numeric | coverage_thresholds] coverage) -> [coverage_thresholds, Hash[String | Regexp, coverage_thresholds]]
714
766
 
715
767
  def validate_per_file_key: ((Symbol | String | Regexp) key) -> void
@@ -733,6 +785,11 @@ module SimpleCov
733
785
  extend Forwardable
734
786
 
735
787
  attr_reader original_result: Hash[String, untyped]
788
+
789
+ # Every path the producing process was told to track, loaded or not.
790
+ attr_reader tracked_files: Array[String]
791
+ attr_reader run_id: String?
792
+ attr_reader worker_id: String?
736
793
  attr_reader files: FileList
737
794
  alias source_files files
738
795
  # The writers accept the strict types; the ivars stay nilable because
@@ -756,7 +813,7 @@ module SimpleCov
756
813
  def initialize: (?filters: Array[Filter[untyped]], ?cover_filters: Array[Filter[untyped]], ?groups: Hash[String, Filter[untyped]]) -> void
757
814
  end
758
815
 
759
- def initialize: (Hash[String, untyped] original_result, ?command_name: String?, ?created_at: Time?, ?not_loaded_files: Set[String], ?report: bool, ?filter_config: FilterConfig) -> void
816
+ def initialize: (Hash[String, untyped] original_result, ?command_name: String?, ?created_at: Time?, ?not_loaded_files: Set[String], ?tracked_files: _ToA[String], ?run_id: String?, ?worker_id: String?, ?report: bool, ?filter_config: FilterConfig) -> void
760
817
 
761
818
  def filenames: () -> Array[String]
762
819
  # Accepts an absolute or project-relative path (resolved against SimpleCov.root).
@@ -768,13 +825,13 @@ module SimpleCov
768
825
  def format!: () -> untyped
769
826
  def created_at: () -> Time
770
827
  def command_name: () -> String
771
- def to_hash: () -> Hash[String, { "coverage" => Hash[String, untyped], "timestamp" => Integer }]
828
+ def to_hash: () -> Hash[String, Hash[String, untyped]]
772
829
  def self.from_hash: (Hash[String, untyped] hash) -> Array[Result]
773
830
 
774
831
  # Delegated to #files (see FileList).
775
832
  def covered_percent: (?criterion) -> Float?
776
833
  def covered_percentages: () -> Array[Float?]
777
- def least_covered_file: () -> String
834
+ def least_covered_file: () -> String?
778
835
  def covered_strength: (?criterion) -> Float?
779
836
  def covered_lines: () -> Integer?
780
837
  def missed_lines: () -> Integer?
@@ -791,6 +848,7 @@ module SimpleCov
791
848
 
792
849
  private
793
850
 
851
+ def initialize_coordination_metadata: (_ToA[String] tracked_files, String? run_id, String? worker_id) -> void
794
852
  def warn_about_missing_source_files: (Array[String] missing, Integer input_size) -> void
795
853
  def coverage: () -> Hash[String, untyped]
796
854
  def apply_filters!: (Array[Filter[untyped]] filters) -> void
@@ -833,11 +891,10 @@ module SimpleCov
833
891
 
834
892
  def covered_lines: () -> Integer?
835
893
  def missed_lines: () -> Integer?
836
- # 0.0 when the list is empty, an Integer sum otherwise.
837
- def never_lines: () -> (Integer | Float)
838
- def skipped_lines: () -> (Integer | Float)
894
+ def never_lines: () -> Integer
895
+ def skipped_lines: () -> Integer
839
896
  def covered_percentages: () -> Array[Float?]
840
- def least_covered_file: () -> String
897
+ def least_covered_file: () -> String?
841
898
  def lines_of_code: () -> Integer?
842
899
  def covered_percent: (?criterion) -> Float?
843
900
  def covered_strength: (?criterion) -> Float?
@@ -1064,6 +1121,8 @@ module SimpleCov
1064
1121
  def branch_statistics: () -> Hash[criterion, CoverageStatistics]
1065
1122
 
1066
1123
  def method_statistics: () -> Hash[criterion, CoverageStatistics]
1124
+
1125
+ def coverage_statistics: (Array[Line | Branch | Method] covered, Array[Line | Branch | Method] missed, ?omitted: Integer, ?percent: Float?) -> CoverageStatistics
1067
1126
  end
1068
1127
  end
1069
1128
  end
@@ -1141,10 +1200,13 @@ module SimpleCov
1141
1200
  module LastRun
1142
1201
  def self.last_run_path: () -> String
1143
1202
 
1144
- # nil when the file does not exist or is blank. Keys are symbolized.
1203
+ # nil when the file does not exist, is blank, or does not hold a
1204
+ # JSON object. Keys are symbolized.
1145
1205
  def self.read: () -> Hash[Symbol, untyped]?
1146
1206
 
1147
1207
  def self.write: (Hash[Symbol, untyped] json) -> void
1208
+
1209
+ def self.invalid_last_run: () -> nil
1148
1210
  end
1149
1211
  end
1150
1212
 
@@ -1161,7 +1223,11 @@ module SimpleCov
1161
1223
  # The base implementation raises NotImplementedError.
1162
1224
  def matches?: (SourceFile source_file) -> boolish
1163
1225
 
1164
- def self.build_filter: (filter_arg filter_argument) -> Filter[untyped]
1226
+ # Whether the verdict depends only on the path, so it can be decided
1227
+ # before the file has any coverage.
1228
+ def path_only?: () -> bool
1229
+
1230
+ def self.build_filter: (filter_arg filter_argument, ?string_filter: singleton(Filter)) -> Filter[untyped]
1165
1231
 
1166
1232
  def self.class_for_argument: (filter_arg filter_argument) -> singleton(Filter)
1167
1233
 
@@ -1177,6 +1243,10 @@ module SimpleCov
1177
1243
  class StringFilter < Filter[String]
1178
1244
  def matches?: (SourceFile source_file) -> bool
1179
1245
 
1246
+ # Whether the verdict depends only on the path, so it can be decided
1247
+ # before the file has any coverage.
1248
+ def path_only?: () -> bool
1249
+
1180
1250
  private
1181
1251
 
1182
1252
  @segment_pattern: Regexp?
@@ -1189,17 +1259,29 @@ module SimpleCov
1189
1259
  # Matches when the project path matches the configured Regexp.
1190
1260
  class RegexFilter < Filter[Regexp]
1191
1261
  def matches?: (SourceFile source_file) -> bool
1262
+
1263
+ # Whether the verdict depends only on the path, so it can be decided
1264
+ # before the file has any coverage.
1265
+ def path_only?: () -> bool
1192
1266
  end
1193
1267
 
1194
1268
  # Matches when the configured block returns truthy for the source file.
1195
1269
  class BlockFilter < Filter[^(SourceFile) -> boolish]
1196
1270
  def matches?: (SourceFile source_file) -> boolish
1271
+
1272
+ # Whether the verdict depends only on the path, so it can be decided
1273
+ # before the file has any coverage.
1274
+ def path_only?: () -> bool
1197
1275
  end
1198
1276
 
1199
1277
  # Matches when the project path matches the configured shell glob
1200
1278
  # (e.g. "lib/**/*.rb").
1201
1279
  class GlobFilter < Filter[String]
1202
1280
  def matches?: (SourceFile source_file) -> bool
1281
+
1282
+ # Whether the verdict depends only on the path, so it can be decided
1283
+ # before the file has any coverage.
1284
+ def path_only?: () -> bool
1203
1285
  end
1204
1286
 
1205
1287
  # Matches when any of the component filters built from the array's
@@ -1209,6 +1291,10 @@ module SimpleCov
1209
1291
  def initialize: (Array[filter_arg] filter_argument) -> void
1210
1292
 
1211
1293
  def matches?: (SourceFile source_file) -> boolish
1294
+
1295
+ # Whether the verdict depends only on the path, so it can be decided
1296
+ # before the file has any coverage.
1297
+ def path_only?: () -> bool
1212
1298
  end
1213
1299
  end
1214
1300
 
@@ -1229,6 +1315,9 @@ module SimpleCov
1229
1315
  # Normalizes a configured formatter — class or ready-built
1230
1316
  # instance — into an instance. See #1240.
1231
1317
  def self.instance_for: (untyped formatter) -> untyped
1318
+
1319
+ # Instantiates a formatter class when necessary and dispatches the result.
1320
+ def self.format: (untyped formatter, SimpleCov::Result result) -> untyped
1232
1321
  end
1233
1322
  end
1234
1323
 
@@ -1264,6 +1353,10 @@ module SimpleCov
1264
1353
  # `coverage.json`); nil leaves the bare directory in the status line.
1265
1354
  def entry_point_filename: () -> String?
1266
1355
 
1356
+ # Prints `output_message` to stderr unless silent; the one home of
1357
+ # the "status lines go to stderr, not through warn" decision (#1225).
1358
+ def emit_status: (SimpleCov::Result result) -> void
1359
+
1267
1360
  def output_message: (SimpleCov::Result result) -> String
1268
1361
 
1269
1362
  # nil for branch/method criteria with nothing to measure, so the
@@ -1284,7 +1377,7 @@ module SimpleCov
1284
1377
 
1285
1378
  private
1286
1379
 
1287
- def format_group: (String name, SimpleCov::FileList files) -> String
1380
+ def format_group: (String name, SimpleCov::FileList files, SimpleCov::criterion criterion) -> String
1288
1381
  end
1289
1382
  end
1290
1383
  end
@@ -1320,11 +1413,16 @@ end
1320
1413
 
1321
1414
  module SimpleCov
1322
1415
  module Formatter
1323
- # Writes the HTML report: coverage_data.js plus pre-compiled static
1324
- # assets, and coverage.json as a side artifact (shared serialization
1416
+ # Writes the HTML report as a single self-contained index.html (the
1417
+ # compiled template with the coverage JSON substituted at the data
1418
+ # marker), and coverage.json as a side artifact (shared serialization
1325
1419
  # via JSONFormatter.build_hash).
1326
1420
  class HTMLFormatter < Base
1327
- DATA_FILENAME: "coverage_data.js"
1421
+ DATA_MARKER: "<!-- SIMPLECOV_COVERAGE_DATA -->"
1422
+
1423
+ # The sibling files a 1.0.0 through 1.0.3 report wrote next to
1424
+ # index.html, removed by format when an earlier version left them.
1425
+ LEGACY_REPORT_FILES: Array[String]
1328
1426
 
1329
1427
  def format: (SimpleCov::Result result) -> void
1330
1428
 
@@ -1336,17 +1434,55 @@ module SimpleCov
1336
1434
 
1337
1435
  def entry_point_filename: () -> String
1338
1436
 
1339
- def copy_static_assets: (?String dest_dir) -> void
1437
+ def source_less_hash: (Hash[Symbol, untyped] hash) -> Hash[Symbol, untyped]
1438
+
1439
+ # Write coverage.json and index.html, then clear any legacy
1440
+ # sibling files an earlier version left in the output directory.
1441
+ def write_report_files: (Hash[untyped, untyped] json_hash, Hash[untyped, untyped] viewer_hash, SimpleCov::Result result) -> void
1340
1442
 
1341
- # Write via temp file + File.rename so parallel writers can't race
1342
- # and read-only existing assets are replaced without EACCES.
1343
- def atomic_write: (String dest, String content) -> void
1443
+ # Substitute the coverage JSON into the compiled template.
1444
+ def render_report: (String json) -> String
1344
1445
 
1345
1446
  def public_dir: () -> String
1346
1447
  end
1347
1448
  end
1348
1449
  end
1349
1450
 
1451
+ # ----- formatter/coverage_json_writer -----
1452
+
1453
+ module SimpleCov
1454
+ module Formatter
1455
+ # Shared writer for the coverage.json artifact (JSONFormatter's
1456
+ # report and HTMLFormatter's side file): one pretty-printed binary
1457
+ # write plus the issue-#1171 concurrent-overwrite warning.
1458
+ module CoverageJSONWriter
1459
+ FILENAME: "coverage.json"
1460
+
1461
+ META_SCAN_BYTES: Integer
1462
+
1463
+ def self?.write: (untyped output_path, Hash[Symbol, untyped] hash, SimpleCov::Result result) -> String
1464
+
1465
+ # Warns when an existing coverage.json is newer than this process's
1466
+ # start time (a concurrent sibling writer). See issue #1171.
1467
+ def self?.warn_if_concurrent_overwrite: (String path, SimpleCov::Result result) -> void
1468
+
1469
+ # timestamp stays untyped (not Time) so the comparison sites don't
1470
+ # require the stdlib `time` extension sigs; the value is produced by
1471
+ # Time.iso8601 at runtime.
1472
+ def self?.existing_meta: (String path) -> ({ timestamp: untyped, command_name: untyped })?
1473
+
1474
+ def self?.parse_meta: (String path) -> Hash[Symbol, untyped]?
1475
+
1476
+ # Bounded head read that parses just the flat meta object of files
1477
+ # this module wrote; nil on any miss so parse_meta falls back to a
1478
+ # full parse.
1479
+ def self?.parse_meta_head: (String path) -> Hash[Symbol, untyped]?
1480
+
1481
+ def self?.parse_meta_full: (String path) -> Hash[Symbol, untyped]?
1482
+ end
1483
+ end
1484
+ end
1485
+
1350
1486
  # ----- formatter/json_formatter -----
1351
1487
 
1352
1488
  module SimpleCov
@@ -1368,15 +1504,6 @@ module SimpleCov
1368
1504
  def message_prefix: () -> String
1369
1505
 
1370
1506
  def entry_point_filename: () -> String
1371
-
1372
- # Warns when an existing coverage.json is newer than this process's
1373
- # start time (a concurrent sibling writer). See issue #1171.
1374
- def warn_if_concurrent_overwrite: (String path, SimpleCov::Result result) -> void
1375
-
1376
- # timestamp stays untyped (not Time) so the comparison sites don't
1377
- # require the stdlib `time` extension sigs; the value is produced by
1378
- # Time.iso8601 at runtime.
1379
- def existing_meta: (String path) -> ({ timestamp: untyped, command_name: untyped })?
1380
1507
  end
1381
1508
  end
1382
1509
  end
@@ -1556,6 +1683,13 @@ module SimpleCov
1556
1683
 
1557
1684
  # How many parallel workers participate in this run. Defaults to 1.
1558
1685
  def self.expected_worker_count: () -> Integer
1686
+
1687
+ # The user's explicit `SimpleCov.parallel_tests false` opt-out.
1688
+ def self.forced_off?: () -> bool
1689
+
1690
+ # Shared PARALLEL_TEST_GROUPS parsing; 1 for unset, empty,
1691
+ # non-numeric, or non-positive values.
1692
+ def self.parallel_test_groups_count: () -> Integer
1559
1693
  end
1560
1694
  end
1561
1695
  end