simplecov 1.0.0.rc1 → 1.0.0.rc2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: aa29d54833537aa0434a697ada8befbfbb81c97c608afc1e7cddd6971009ec79
4
- data.tar.gz: df39d6ee8257c5cc7575621d83a428a96f80fc1db762bf3e247927ace5d1050d
3
+ metadata.gz: 95cc757bf172ce50cd60e636048c88e30fade408c962d8fe8c9478465b6b2d67
4
+ data.tar.gz: 9ee4b98d8f3f790289415b03c3e37c436b32d1e2a9d193fd435dfdb966d4b658
5
5
  SHA512:
6
- metadata.gz: be38ab39b2d235b3a2fbdd7fcd298684f1ff43d40e966a43174489da2b2680ac99dfba059b70ebcd774353265f5feab852a126ca242c97703f6e0bffea29f3c1
7
- data.tar.gz: 91e057b46a0f7be3d914af750bedd7fcc3abe174db97970a7babedd85b047bfe69a682b34723f0d60115d6d4b1f674e854c431f854ff4f054b6194c6e9ab07d3
6
+ metadata.gz: 719bcdfee0e3e79de52e7da4fdd955d85416b2c33c3b8bcd437d13187b9d883c6430ea55e0d52ca17c392cfe4e506f6a433278d5d285091858702bd669eb43bd
7
+ data.tar.gz: 4e70c6fabe5ed02ea4ece1ca811434c96b760e871507cc607a05af64710a9f0c14ee87c16dbcf82d19cdaaf23f3896d78ea0500e051130dbc15b7c6046e4b6b8
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ 1.0.0.rc2 (2026-06-10)
2
+ ======================
3
+
4
+ ## Bugfixes
5
+ * Forked subprocesses (for example Rails' `parallelize`) are now named by a stable per-run serial rather than the child's OS process id. Because the pid changed every run, a re-run's worker results were never named the same as the previous run's and so never overwrote them. They accumulated in `.resultset.json` until `merge_timeout` dropped them, and while several runs sat inside that window together SimpleCov merged them all. When the set of files had drifted between those runs (a deleted file, a changed filter) the stale results leaked into the report, inflating the denominator and changing the coverage percentage from one run to the next. The serial sequence is identical from one run to the next, so a re-run's workers now overwrite the previous run's entries and the resultset stays bounded. See #1171.
6
+ * The "Excluded N result(s) older than `merge_timeout`" warning is now emitted at most once, from the reporting process, instead of once per forked worker. Every worker merges the resultset too, and the default `at_fork` sets `print_errors false` for them, but this one warning did not honor the flag, so an eight-worker run printed eight copies. See #1171.
7
+ * Legacy-API deprecation warnings (`track_files`, `add_filter`, `add_group`, and the rest) are now deduplicated by call site and emitted at most once per source location. A deprecated method called in a loop, or a configuration block re-evaluated once per parallel worker or spec file, previously repeated the same notice until the surrounding output was unreadable. See #1204.
8
+
1
9
  1.0.0.rc1 (2026-06-02)
2
10
  ======================
3
11
 
@@ -110,8 +110,8 @@ module SimpleCov
110
110
 
111
111
  # DEPRECATED: prefer `enable_coverage :eval`.
112
112
  def enable_coverage_for_eval
113
- warn "#{Kernel.caller.first}: [DEPRECATION] `SimpleCov.enable_coverage_for_eval` is deprecated. " \
114
- "Replace with `SimpleCov.enable_coverage :eval`."
113
+ SimpleCov::Deprecation.warn("`SimpleCov.enable_coverage_for_eval` is deprecated. " \
114
+ "Replace with `SimpleCov.enable_coverage :eval`.")
115
115
  enable_eval_coverage
116
116
  end
117
117
 
@@ -58,8 +58,8 @@ module SimpleCov
58
58
  # historical `track_files` behavior) and restricts the report to the
59
59
  # matching set.
60
60
  def track_files(glob)
61
- warn "#{Kernel.caller.first}: [DEPRECATION] `SimpleCov.track_files` is deprecated. " \
62
- "#{track_files_replacement_hint(glob)}"
61
+ SimpleCov::Deprecation.warn("`SimpleCov.track_files` is deprecated. " \
62
+ "#{track_files_replacement_hint(glob)}")
63
63
  @tracked_files = glob
64
64
  end
65
65
 
@@ -102,8 +102,8 @@ module SimpleCov
102
102
  # DEPRECATED: alias for `skip`. Same matcher grammar, identical behavior.
103
103
  def add_filter(filter_argument = nil, &block)
104
104
  example = block ? "`SimpleCov.skip { ... }`" : "`SimpleCov.skip #{filter_argument.inspect}`"
105
- warn "#{Kernel.caller.first}: [DEPRECATION] `SimpleCov.add_filter` is deprecated. " \
106
- "Replace with `SimpleCov.skip` (same arguments, same behavior). Example: #{example}."
105
+ SimpleCov::Deprecation.warn("`SimpleCov.add_filter` is deprecated. " \
106
+ "Replace with `SimpleCov.skip` (same arguments, same behavior). Example: #{example}.")
107
107
  skip(filter_argument, &block)
108
108
  end
109
109
 
@@ -141,8 +141,10 @@ module SimpleCov
141
141
  else
142
142
  "`SimpleCov.group #{group_name.inspect}, #{filter_argument.inspect}`"
143
143
  end
144
- warn "#{Kernel.caller.first}: [DEPRECATION] `SimpleCov.add_group` is deprecated. " \
145
- "Replace with `SimpleCov.group` (same arguments, same behavior). Example: #{example}."
144
+ SimpleCov::Deprecation.warn(
145
+ "`SimpleCov.add_group` is deprecated. " \
146
+ "Replace with `SimpleCov.group` (same arguments, same behavior). Example: #{example}."
147
+ )
146
148
  group(group_name, filter_argument, &block)
147
149
  end
148
150
 
@@ -89,8 +89,8 @@ module SimpleCov
89
89
 
90
90
  # DEPRECATED: alias for `print_errors`. Same value, same behavior.
91
91
  def print_error_status
92
- warn "#{Kernel.caller.first}: [DEPRECATION] `SimpleCov.print_error_status` is deprecated. " \
93
- "Replace with `SimpleCov.print_errors` (same value)."
92
+ SimpleCov::Deprecation.warn("`SimpleCov.print_error_status` is deprecated. " \
93
+ "Replace with `SimpleCov.print_errors` (same value).")
94
94
  defined?(@print_error_status) ? @print_error_status : true
95
95
  end
96
96
 
@@ -101,8 +101,8 @@ module SimpleCov
101
101
  # be removed in a future release.
102
102
  #
103
103
  def nocov_token(nocov_token = nil)
104
- warn "#{Kernel.caller.first}: [DEPRECATION] `SimpleCov.nocov_token` and `SimpleCov.skip_token` are deprecated. " \
105
- "Replace with `# simplecov:disable` / `# simplecov:enable` block comments."
104
+ SimpleCov::Deprecation.warn("`SimpleCov.nocov_token` and `SimpleCov.skip_token` are deprecated. " \
105
+ "Replace with `# simplecov:disable` / `# simplecov:enable` block comments.")
106
106
  current_nocov_token(nocov_token)
107
107
  end
108
108
  alias skip_token nocov_token
@@ -36,8 +36,8 @@ module SimpleCov
36
36
 
37
37
  # DEPRECATED: alias for `merge_subprocesses`. Same value/behavior.
38
38
  def enable_for_subprocesses(value = nil)
39
- warn "#{Kernel.caller.first}: [DEPRECATION] `SimpleCov.enable_for_subprocesses` is deprecated. " \
40
- "Replace with `SimpleCov.merge_subprocesses` (same value, same behavior)."
39
+ SimpleCov::Deprecation.warn("`SimpleCov.enable_for_subprocesses` is deprecated. " \
40
+ "Replace with `SimpleCov.merge_subprocesses` (same value, same behavior).")
41
41
  return @enable_for_subprocesses if defined?(@enable_for_subprocesses) && value.nil?
42
42
 
43
43
  @enable_for_subprocesses = value || false
@@ -56,8 +56,8 @@ module SimpleCov
56
56
 
57
57
  # DEPRECATED: alias for `merging`. Same value, same behavior.
58
58
  def use_merging(use = nil)
59
- warn "#{Kernel.caller.first}: [DEPRECATION] `SimpleCov.use_merging` is deprecated. " \
60
- "Replace with `SimpleCov.merging` (same value, same behavior)."
59
+ SimpleCov::Deprecation.warn("`SimpleCov.use_merging` is deprecated. " \
60
+ "Replace with `SimpleCov.merging` (same value, same behavior).")
61
61
  @use_merging = use unless use.nil?
62
62
  @use_merging = true unless defined?(@use_merging) && @use_merging == false
63
63
  end
@@ -76,8 +76,8 @@ module SimpleCov
76
76
  coverage = {primary_coverage => coverage} if coverage.is_a?(Numeric)
77
77
  defaults, overrides = partition_per_file_thresholds(coverage)
78
78
 
79
- warn "#{Kernel.caller.first}: [DEPRECATION] `SimpleCov.minimum_coverage_by_file` is deprecated. " \
80
- "Replace it with:\n#{per_file_coverage_replacement(defaults, overrides)}"
79
+ SimpleCov::Deprecation.warn("`SimpleCov.minimum_coverage_by_file` is deprecated. " \
80
+ "Replace it with:\n#{per_file_coverage_replacement(defaults, overrides)}")
81
81
 
82
82
  raise_on_invalid_coverage(defaults, "minimum_coverage_by_file")
83
83
  overrides.each_value { |criteria| raise_on_invalid_coverage(criteria, "minimum_coverage_by_file") }
@@ -98,8 +98,8 @@ module SimpleCov
98
98
  def minimum_coverage_by_group(coverage = nil)
99
99
  return @minimum_coverage_by_group ||= {} unless coverage
100
100
 
101
- warn "#{Kernel.caller.first}: [DEPRECATION] `SimpleCov.minimum_coverage_by_group` is deprecated. " \
102
- "Replace it with:\n#{per_group_coverage_replacement(coverage)}"
101
+ SimpleCov::Deprecation.warn("`SimpleCov.minimum_coverage_by_group` is deprecated. " \
102
+ "Replace it with:\n#{per_group_coverage_replacement(coverage)}")
103
103
  @minimum_coverage_by_group = coverage.dup.transform_values do |group_coverage|
104
104
  group_coverage = {primary_coverage => group_coverage} if group_coverage.is_a?(Numeric)
105
105
  raise_on_invalid_coverage(group_coverage, "minimum_coverage_by_group")
@@ -113,14 +113,19 @@ module SimpleCov
113
113
 
114
114
  #
115
115
  # Gets or sets the behavior to start a new forked Process.
116
- # Defaults to adding " (subprocess: #{pid})" to command_name and
116
+ # Defaults to adding " (subprocess: #{serial})" to command_name and
117
117
  # starting SimpleCov in quiet mode.
118
118
  #
119
119
  def at_fork(&block)
120
120
  @at_fork = block if block
121
- @at_fork ||= lambda { |pid|
122
- # This needs a unique name so it won't be overwritten
123
- SimpleCov.command_name "#{SimpleCov.command_name} (subprocess: #{pid})"
121
+ @at_fork ||= lambda { |_pid|
122
+ # Needs a name that's unique per worker within a run yet identical
123
+ # across runs. Build it from SimpleCov's stable fork serial rather
124
+ # than the OS pid: with the pid, every run produced uniquely-named
125
+ # results that never overwrote the previous run's, so they piled up
126
+ # in .resultset.json until merge_timeout and the merged report's
127
+ # file set drifted from run to run. See issue #1171.
128
+ SimpleCov.command_name "#{SimpleCov.command_name} (subprocess: #{SimpleCov.subprocess_serial})"
124
129
  # be quiet, the parent process will use the regular formatter
125
130
  SimpleCov.print_errors false
126
131
  SimpleCov.formatter SimpleCov::Formatter::SimpleFormatter
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+
5
+ module SimpleCov
6
+ # Emits legacy-API deprecation warnings, deduplicated by the source
7
+ # location that triggered them. A deprecated method called in a loop —
8
+ # or a config block re-evaluated once per parallel worker / spec file —
9
+ # otherwise repeats the same notice until stderr is unreadable. Keying
10
+ # on the caller location collapses those repeats to a single line while
11
+ # still warning separately about each distinct call site the user needs
12
+ # to fix. See issue #1204.
13
+ module Deprecation
14
+ module_function
15
+
16
+ # Warn about a deprecated API. `message` is the notice without the
17
+ # `[DEPRECATION]` tag or location prefix (both are added here).
18
+ #
19
+ # `location` defaults to the caller of the deprecated method that
20
+ # called us — every shipped call site is a one-level alias such as
21
+ # `track_files`, so the frame two up is the user code. Pass `location:`
22
+ # explicitly when the relevant site isn't that frame (e.g. a source
23
+ # file and line discovered while parsing).
24
+ # `Array(...)` coerces a missing backtrace (nil) to `[]` so `.first`
25
+ # yields nil rather than raising — and, unlike `&.`, adds no branch for
26
+ # the unreachable no-caller case to the project's 100% coverage target.
27
+ def warn(message, location: Array(Kernel.caller(2..2)).first)
28
+ # Key on location when we have one (collapses a deprecated call in a
29
+ # loop to a single warning); fall back to the message so a missing
30
+ # backtrace never silently swallows every notice.
31
+ return unless emitted.add?(location || message)
32
+
33
+ Kernel.warn "#{"#{location}: " if location}[DEPRECATION] #{message}"
34
+ end
35
+
36
+ # Already-emitted dedup keys for this process. Parallel workers are
37
+ # separate processes with their own set, so each warns at most once.
38
+ def emitted
39
+ @emitted ||= Set.new
40
+ end
41
+
42
+ # @api private — reset emitted state between tests.
43
+ def reset!
44
+ @emitted = Set.new
45
+ end
46
+ end
47
+ end
@@ -15,8 +15,14 @@ module SimpleCov
15
15
  # the child.
16
16
  module ProcessForkHook
17
17
  def _fork
18
+ active = defined?(SimpleCov) && Coverage.running?
19
+ # Assign the next serial in the PARENT, before the fork, so the child
20
+ # inherits its own stable ordinal via copy-on-write. The default
21
+ # at_fork uses it (instead of the child pid) to name the subprocess's
22
+ # result, keeping that name identical across runs. See issue #1171.
23
+ SimpleCov.next_subprocess_serial! if active
18
24
  pid = super
19
- SimpleCov.at_fork.call(::Process.pid) if pid.zero? && defined?(SimpleCov) && Coverage.running?
25
+ SimpleCov.at_fork.call(::Process.pid) if pid.zero? && active
20
26
  pid
21
27
  end
22
28
  end
@@ -68,6 +68,14 @@ module SimpleCov
68
68
  end
69
69
 
70
70
  def warn_about_expired_results(expired_command_names)
71
+ # Subprocesses merge the resultset too (each forked worker calls
72
+ # `SimpleCov.result` to store its slice), and the default `at_fork`
73
+ # sets `print_errors false` for them. Without this guard the warning
74
+ # is emitted once per worker — N copies of the same message for an
75
+ # N-worker run. Gate on `print_errors` like every other SimpleCov
76
+ # warning so only the reporting process speaks up.
77
+ return unless SimpleCov.print_errors
78
+
71
79
  warn "[SimpleCov]: Excluded #{expired_command_names.size} result(s) older than " \
72
80
  "merge_timeout (#{SimpleCov.merge_timeout}s) from the merged report: " \
73
81
  "#{expired_command_names.sort.join(', ')}. " \
@@ -123,8 +123,8 @@ module SimpleCov
123
123
 
124
124
  # DEPRECATED: use `covered_percent(:branch)`.
125
125
  def branches_coverage_percent
126
- warn "#{Kernel.caller.first}: [DEPRECATION] `SimpleCov::SourceFile#branches_coverage_percent` is deprecated. " \
127
- "Use `covered_percent(:branch)`."
126
+ SimpleCov::Deprecation.warn("`SimpleCov::SourceFile#branches_coverage_percent` is deprecated. " \
127
+ "Use `covered_percent(:branch)`.")
128
128
  covered_percent(:branch)
129
129
  end
130
130
 
@@ -176,8 +176,8 @@ module SimpleCov
176
176
 
177
177
  # DEPRECATED: use `covered_percent(:method)`.
178
178
  def methods_coverage_percent
179
- warn "#{Kernel.caller.first}: [DEPRECATION] `SimpleCov::SourceFile#methods_coverage_percent` is deprecated. " \
180
- "Use `covered_percent(:method)`."
179
+ SimpleCov::Deprecation.warn("`SimpleCov::SourceFile#methods_coverage_percent` is deprecated. " \
180
+ "Use `covered_percent(:method)`.")
181
181
  covered_percent(:method)
182
182
  end
183
183
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SimpleCov
4
- VERSION = "1.0.0.rc1"
4
+ VERSION = "1.0.0.rc2"
5
5
  end
data/lib/simplecov.rb CHANGED
@@ -21,6 +21,22 @@ module SimpleCov
21
21
  # so JSONFormatter can detect when an existing coverage.json was written
22
22
  # by a sibling process running concurrently.
23
23
  attr_accessor :process_start_time
24
+
25
+ # A monotonically increasing serial the parent assigns to each forked
26
+ # subprocess (see SimpleCov::ProcessForkHook). The default `at_fork`
27
+ # builds the worker's command_name from this rather than the OS pid:
28
+ # the serial sequence is the same from one run to the next, so a re-run
29
+ # overwrites the previous run's resultset entries instead of writing
30
+ # uniquely-named ones that pile up until merge_timeout. See issue #1171.
31
+ def subprocess_serial
32
+ @subprocess_serial ||= 0
33
+ end
34
+
35
+ # @api private — bump the serial in the parent before a fork so the
36
+ # child inherits its own ordinal via copy-on-write.
37
+ def next_subprocess_serial!
38
+ @subprocess_serial = subprocess_serial + 1
39
+ end
24
40
  # Should we take care of at_exit behavior or something else? Used by the
25
41
  # minitest plugin. See lib/minitest/simplecov_plugin.rb.
26
42
  attr_accessor :external_at_exit
@@ -186,6 +202,7 @@ end
186
202
  require "set"
187
203
  require "forwardable"
188
204
  require_relative "simplecov/color"
205
+ require_relative "simplecov/deprecation"
189
206
  require_relative "simplecov/configuration"
190
207
  SimpleCov.extend SimpleCov::Configuration
191
208
  require_relative "simplecov/coverage_statistics"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simplecov
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.rc1
4
+ version: 1.0.0.rc2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Erik Berlin
@@ -71,6 +71,7 @@ files:
71
71
  - lib/simplecov/coverage_statistics.rb
72
72
  - lib/simplecov/coverage_violations.rb
73
73
  - lib/simplecov/defaults.rb
74
+ - lib/simplecov/deprecation.rb
74
75
  - lib/simplecov/directive.rb
75
76
  - lib/simplecov/exit_codes.rb
76
77
  - lib/simplecov/exit_codes/exit_code_handling.rb
@@ -149,9 +150,9 @@ licenses:
149
150
  metadata:
150
151
  bug_tracker_uri: https://github.com/simplecov-ruby/simplecov/issues
151
152
  changelog_uri: https://github.com/simplecov-ruby/simplecov/blob/main/CHANGELOG.md
152
- documentation_uri: https://www.rubydoc.info/gems/simplecov/1.0.0.rc1
153
+ documentation_uri: https://www.rubydoc.info/gems/simplecov/1.0.0.rc2
153
154
  mailing_list_uri: https://groups.google.com/forum/#!forum/simplecov
154
- source_code_uri: https://github.com/simplecov-ruby/simplecov/tree/v1.0.0.rc1
155
+ source_code_uri: https://github.com/simplecov-ruby/simplecov/tree/v1.0.0.rc2
155
156
  rubygems_mfa_required: 'true'
156
157
  rdoc_options: []
157
158
  require_paths:
@@ -167,7 +168,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
167
168
  - !ruby/object:Gem::Version
168
169
  version: '0'
169
170
  requirements: []
170
- rubygems_version: 4.0.12
171
+ rubygems_version: 4.0.14
171
172
  specification_version: 4
172
173
  summary: Code coverage for Ruby
173
174
  test_files: []