mutation_tester 1.4.2 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c743765bd0adb455bf1162627a35cb5b3232d45cfb83dca91ab139248a3acac1
4
- data.tar.gz: 71eb3f98c61271896c6c4cc2e3b9b69c62fee77d50b3d94c0237b0dac9eefd53
3
+ metadata.gz: 95884456f3cb22f463001c227181cbb9250fe09c3e94eec4039cc0fac54c6e18
4
+ data.tar.gz: 984f636d787af8f79d4d117684f57a69bdd48347651ecbb6421468d2730522e8
5
5
  SHA512:
6
- metadata.gz: 3fac687f4fd5496daced6e16cf30e3dc39a0816fc5be7fe9f37caae4a05d3e088bdf1f80ee5b23f72610741d767cd410de81c6fcec09b4456d2936549f32266c
7
- data.tar.gz: 13842db8fe228decfe6d3e165e5f80ea3c18af549e09051a3b12342f1ff3251ea5f81f8d375d1f0bdc5e76b6320f101ce791900016b8054630a3e0c2e14f3227
6
+ metadata.gz: 890a83603e65df88fcad21e62a27faa57c8540c403d8ca92ae218b9471d04471272f01f409f7c32cff8bf567feda042167c75b7be83962315c06976626ec142b
7
+ data.tar.gz: 943bb88ba38f80d37c78ecde8f91567000e952a4b68209cfac9d94aa336129b8ce2bdec2eaac4759fa328fb334daa79fd34a8a7a587d069804fcf1d4c907c943
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.5.0] - 2026-08-08
4
+
5
+ - Added `--after-fork FILE` (and `config.after_fork_file` / the `MUTATION_TESTER_AFTER_FORK` environment variable): a Ruby file loaded inside each preloaded in-memory clone right after it forks and receives its per-worker environment, so the app can re-establish per-worker state such as its ActiveRecord connection. With `--worker-env` set, this keeps the `in_memory` runner available in parallel runs instead of falling back to `fork`: each clone now receives its own per-worker value of the variable through the existing per-clone environment plumbing and runs the after-fork file once, giving in-memory execution and per-worker database isolation at the same time. Failure modes stay loud: a clone whose after-fork file raises reports the error on stderr and is dropped, its worker deciding its share of mutants file-based, and a missing after-fork file makes the whole run fall back to file-based execution with a warning. A serial in-memory run (`-p 1`) has only one worker and now stays in memory with `--worker-env` even without the hook.
6
+ - A serial file-based run (`-p 1`) with `--worker-env` now decides each mutant in a shadow workspace instead of writing mutants into the real source file with a `.mutation_backup` alongside, so a process killed mid-run can no longer leave a mutated source file in the checkout. This combination occurs naturally when a caller uses one code path for both a parallel run and a serial re-run. Without `--worker-env` serial file-based runs keep the in-place strategy (the next run still restores a leftover backup automatically).
7
+
3
8
  ## [1.4.2] - 2026-08-05
4
9
 
5
10
  - Added `--spec-map 'PATTERN=>REPLACEMENT'`, a repeatable rule that builds the whole spec path from the whole source path with a regular expression, for the layouts the `{name}` template cannot express. `{name}` is the source path minus a leading `lib/`, so a template can only wrap it with a prefix and a suffix; every mapping that substitutes *inside* the path, after a variable-length prefix, was out of reach: `packs/identity/app/models/party.rb -> packs/identity/test/models/party_test.rb` (Packwerk / packs-rails), the same shape with Rails engines, and the plain Rails `app/ -> test/` rule. Because `--since` requires `--glob` and `--glob` maps through `--spec-glob`, such a layout previously lost the entire batch tier at once and had to be reimplemented in a wrapper script. The first matching rule wins and a source matching no rule falls back to `--spec-glob`, so one run can cover `app/` through a rule and `lib/` through the template. The flag works with `--glob`, a positional `FILE` list and `--staged`; a malformed rule is a usage error (exit code `2`) reported before any mutation runs.
@@ -134,6 +134,18 @@ no fallback is possible.
134
134
  in a fresh fork of its own clone. If a pooled worker dies mid-run, that
135
135
  worker finishes its share of mutants through the file-based path with a
136
136
  warning; the other workers stay in memory.
137
+ - With `--worker-env` the preloaded clones would all share the primary
138
+ worker's already-established database connection, so a parallel run
139
+ (`-p N`, N > 1) skips the in-memory runner with a warning unless
140
+ `--after-fork FILE` is also given. With `--after-fork`, each clone
141
+ receives its per-worker value of the `--worker-env` variable and loads
142
+ `FILE` right after forking, and that file re-establishes the per-worker
143
+ state (typically the ActiveRecord connection), which keeps the whole run
144
+ in memory with per-worker database isolation. A serial run (`-p 1`) has
145
+ only one worker and stays in memory without any hook. A clone whose
146
+ after-fork file raises reports the error on stderr and is dropped, and
147
+ its worker falls back to the file-based path; a missing after-fork file
148
+ makes the whole run fall back with a warning.
137
149
  - Before any mutant runs, the runner re-applies the **unmutated** source in a
138
150
  probe child and runs the suite. If that probe fails (for example the file has
139
151
  top-level side effects that break on a second execution, or the class is
data/exe/mutation_test CHANGED
@@ -88,10 +88,14 @@ OptionParser.new do |opts|
88
88
  options[:strict_equality] = true
89
89
  end
90
90
 
91
- opts.on('--worker-env NAME', 'Set environment variable NAME to a distinct per-worker value before each parallel worker boots (parallel_tests TEST_ENV_NUMBER convention: worker 0 -> "", worker N -> N+1), so a parallel_tests-style database.yml selects a per-worker database. Provision the databases yourself (e.g. rake parallel:prepare). The in_memory runner cannot isolate a per-worker database, so this flag makes the runner fall back to fork.') do |name|
91
+ opts.on('--worker-env NAME', 'Set environment variable NAME to a distinct per-worker value before each parallel worker boots (parallel_tests TEST_ENV_NUMBER convention: worker 0 -> "", worker N -> N+1), so a parallel_tests-style database.yml selects a per-worker database. Provision the databases yourself (e.g. rake parallel:prepare). Without --after-fork a parallel in_memory run falls back to fork (preloaded clones share one database connection); pair it with --after-fork to keep the in_memory runner. A serial run (-p 1) decides mutants in a shadow workspace instead of mutating the checkout in place.') do |name|
92
92
  options[:worker_env] = name
93
93
  end
94
94
 
95
+ opts.on('--after-fork FILE', 'Ruby file loaded inside each preloaded in-memory clone right after it forks and receives its per-worker environment (see --worker-env), so the app can re-establish per-worker state such as its database connection (e.g. a file calling ActiveRecord::Base.establish_connection). With --worker-env set, this keeps the in_memory runner available in parallel runs.') do |file|
96
+ options[:after_fork] = file
97
+ end
98
+
95
99
  opts.on('-h', '--help', 'Show this help message') do
96
100
  puts opts
97
101
  exit
@@ -178,6 +182,7 @@ apply_configuration = lambda do
178
182
  config.timeout_factor = options[:timeout_factor] if options[:timeout_factor]
179
183
  config.timeout_policy = options[:timeout_policy] if options[:timeout_policy]
180
184
  config.worker_env_var = options[:worker_env] if options.key?(:worker_env)
185
+ config.after_fork_file = options[:after_fork] if options.key?(:after_fork)
181
186
  config.fail_fast = options.fetch(:fail_fast, false)
182
187
  config.mutation_types[:strict_equality] = true if options[:strict_equality]
183
188
 
@@ -19,7 +19,8 @@ module MutationTester
19
19
  number <= 0 ? '' : (number + 1).to_s
20
20
  end
21
21
 
22
- attr_reader :parallel_processes, :runner, :worker_env_var, :timeout, :timeout_factor, :timeout_policy
22
+ attr_reader :parallel_processes, :runner, :worker_env_var, :after_fork_file, :timeout, :timeout_factor,
23
+ :timeout_policy
23
24
 
24
25
  attr_accessor :baseline_duration,
25
26
  :baseline_timeout,
@@ -38,6 +39,7 @@ module MutationTester
38
39
  self.parallel_processes = ENV['MUTATION_TESTER_PARALLEL_PROCESSES'] || self.class.auto_parallel_processes
39
40
  self.runner = ENV['MUTATION_TESTER_RUNNER'] || :auto
40
41
  self.worker_env_var = ENV['MUTATION_TESTER_WORKER_ENV']
42
+ self.after_fork_file = ENV['MUTATION_TESTER_AFTER_FORK']
41
43
  @timeout = DEFAULT_TIMEOUT
42
44
  @timeout_factor = DEFAULT_TIMEOUT_FACTOR
43
45
  @timeout_policy = :killed
@@ -136,6 +138,11 @@ module MutationTester
136
138
  @worker_env_var = normalized.empty? ? nil : normalized
137
139
  end
138
140
 
141
+ def after_fork_file=(value)
142
+ normalized = value.to_s.strip
143
+ @after_fork_file = normalized.empty? ? nil : File.expand_path(normalized)
144
+ end
145
+
139
146
  def worker_env_assignment(index)
140
147
  return nil unless @worker_env_var
141
148
 
@@ -238,6 +238,17 @@ spawn_clone = lambda do |request, out|
238
238
  out.close
239
239
  STDIN.reopen(File::NULL)
240
240
  request['env']&.each { |name, value| ENV[name] = value }
241
+ if request['after_fork']
242
+ begin
243
+ load(request['after_fork'])
244
+ rescue ScriptError, StandardError => e
245
+ clone_out.puts(JSON.generate(
246
+ 'event' => 'clone_error',
247
+ 'message' => "the after-fork file #{request['after_fork']} raised #{e.class}: #{e.message}; this clone is unavailable"
248
+ ))
249
+ raise
250
+ end
251
+ end
241
252
  serve.call(input, clone_out)
242
253
  end
243
254
  Process.detach(child)
@@ -39,10 +39,10 @@ module MutationTester
39
39
  refill_pool(pool_key(use_bundle_exec, framework), count, primary, env_for: env_for)
40
40
  end
41
41
 
42
- def prepare_in_memory_pool(count, primary)
42
+ def prepare_in_memory_pool(count, primary, env_for: nil, after_fork: nil)
43
43
  return [] unless available? && primary&.ready?
44
44
 
45
- refill_pool(IN_MEMORY_POOL_KEY, count, primary)
45
+ refill_pool(IN_MEMORY_POOL_KEY, count, primary, env_for: env_for, after_fork: after_fork)
46
46
  end
47
47
 
48
48
  def in_memory_pool_prepared?
@@ -116,13 +116,13 @@ module MutationTester
116
116
  Parallel.worker_number if defined?(Parallel) && Parallel.respond_to?(:worker_number)
117
117
  end
118
118
 
119
- def refill_pool(key, count, primary, env_for: nil)
119
+ def refill_pool(key, count, primary, env_for: nil, after_fork: nil)
120
120
  entry = (pool[key] ||= { owner: Process.pid, runners: [] })
121
121
  entry[:runners] = entry[:runners].each_with_index.map do |runner, index|
122
- runner&.ready? ? runner : primary.fork_clone(env: env_for&.call(index))
122
+ runner&.ready? ? runner : primary.fork_clone(env: env_for&.call(index), after_fork: after_fork)
123
123
  end
124
124
  entry[:runners].size.upto(count - 1) do |index|
125
- entry[:runners] << primary.fork_clone(env: env_for&.call(index))
125
+ entry[:runners] << primary.fork_clone(env: env_for&.call(index), after_fork: after_fork)
126
126
  end
127
127
  entry[:runners]
128
128
  end
@@ -198,7 +198,7 @@ module MutationTester
198
198
  fail_worker
199
199
  end
200
200
 
201
- def fork_clone(env: nil)
201
+ def fork_clone(env: nil, after_fork: nil)
202
202
  return nil unless ready?
203
203
  return nil unless File.respond_to?(:mkfifo)
204
204
 
@@ -210,6 +210,7 @@ module MutationTester
210
210
 
211
211
  clone_request = { 'job' => job_path, 'events' => events_path }
212
212
  clone_request['env'] = env if env
213
+ clone_request['after_fork'] = after_fork if after_fork
213
214
  @job_writer.puts(JSON.generate('clone' => clone_request))
214
215
  event = read_event(monotonic_time + CLONE_TIMEOUT)
215
216
  return nil unless event.is_a?(Hash) && event['event'] == 'cloned'
@@ -264,6 +265,11 @@ module MutationTester
264
265
  next
265
266
  end
266
267
 
268
+ if event['event'] == 'clone_error'
269
+ warn("[MutationTester] #{event['message']}")
270
+ return false
271
+ end
272
+
267
273
  return event['event'] == 'ready'
268
274
  end
269
275
  end
@@ -56,16 +56,17 @@ module MutationTester
56
56
  elsif in_memory_first?
57
57
  run_in_memory_series(mutations, &progress_callback)
58
58
  elsif @config.parallel_processes == 1
59
- run_in_place_series(mutations, &progress_callback)
59
+ run_file_based_series(mutations, &progress_callback)
60
60
  else
61
61
  run_in_shadow_parallel(mutations, &progress_callback)
62
62
  end
63
63
  end
64
64
 
65
65
  def in_memory_first?
66
- return false if @config.worker_env_var
66
+ return false unless %i[in_memory auto].include?(@config.runner)
67
+ return true unless @config.worker_env_var
67
68
 
68
- %i[in_memory auto].include?(@config.runner)
69
+ @config.parallel_processes == 1 || !@config.after_fork_file.nil?
69
70
  end
70
71
 
71
72
  def run_in_memory_series(mutations, &progress_callback)
@@ -100,7 +101,12 @@ module MutationTester
100
101
  blocker = prepare_in_memory_execution
101
102
  return fall_back_to_parallel_file_based(blocker, mutations, &progress_callback) if blocker
102
103
 
103
- pool = ForkRunner.prepare_in_memory_pool([@config.parallel_processes, mutations.size].min, @in_memory_runner)
104
+ pool = ForkRunner.prepare_in_memory_pool(
105
+ [@config.parallel_processes, mutations.size].min,
106
+ @in_memory_runner,
107
+ env_for: worker_env_for,
108
+ after_fork: @config.after_fork_file
109
+ )
104
110
  if pool.compact.empty?
105
111
  return fall_back_to_parallel_file_based('the preloaded worker pool could not be cloned', mutations, &progress_callback)
106
112
  end
@@ -161,6 +167,28 @@ module MutationTester
161
167
  cleanup_shadow_workspaces
162
168
  end
163
169
 
170
+ def run_file_based_series(mutations, &progress_callback)
171
+ project_root = @config.worker_env_var ? discoverable_project_root : nil
172
+ return run_in_place_series(mutations, &progress_callback) unless project_root
173
+
174
+ run_in_shadow_series(mutations, project_root, &progress_callback)
175
+ end
176
+
177
+ def run_in_shadow_series(mutations, project_root, &progress_callback)
178
+ warn "[MutationTester] --worker-env #{@config.worker_env_var} is set, so the serial run decides each mutant in a shadow workspace and never mutates the checkout in place."
179
+ shadow_run_root
180
+ results = []
181
+ mutations.each_with_index do |mutation, index|
182
+ result = run_single_mutation(mutation, :shadow, project_root)
183
+ progress_callback.call(mutation, index + 1) if progress_callback
184
+ results << result
185
+ break if stop_early?(result)
186
+ end
187
+ results
188
+ ensure
189
+ cleanup_shadow_workspaces
190
+ end
191
+
164
192
  def run_in_place_series(mutations, &progress_callback)
165
193
  write_in_place_backup
166
194
  results = []
@@ -401,6 +429,9 @@ module MutationTester
401
429
 
402
430
  def prepare_in_memory_execution
403
431
  return 'Process.fork is not supported on this platform' unless ForkRunner.available?
432
+ if @config.after_fork_file && !File.exist?(@config.after_fork_file)
433
+ return "the after-fork file #{@config.after_fork_file} does not exist"
434
+ end
404
435
  if InMemoryLoader.load_time_defined_guard?(@original_content)
405
436
  return 'the source file uses defined? at load time, so redefinition would silently skip the guarded code'
406
437
  end
@@ -451,7 +482,7 @@ module MutationTester
451
482
  offset_callback = progress_callback && lambda do |mutation, index|
452
483
  progress_callback.call(mutation, completed + index)
453
484
  end
454
- run_in_place_series(mutations, &offset_callback)
485
+ run_file_based_series(mutations, &offset_callback)
455
486
  end
456
487
 
457
488
  def fall_back_to_parallel_file_based(reason, mutations, &progress_callback)
@@ -470,8 +501,9 @@ module MutationTester
470
501
  def announce_worker_env_in_memory_opt_out
471
502
  return unless @config.worker_env_var
472
503
  return unless %i[in_memory auto].include?(@config.runner)
504
+ return if @config.parallel_processes == 1 || @config.after_fork_file
473
505
 
474
- warn "[MutationTester] --worker-env #{@config.worker_env_var} is set, so the in-memory runner is skipped (its clones share one preloaded database connection); using the fork runner for per-worker database isolation."
506
+ warn "[MutationTester] --worker-env #{@config.worker_env_var} is set without --after-fork, so the in-memory runner is skipped (its clones share one preloaded database connection); using the fork runner for per-worker database isolation. Pass --after-fork FILE to keep the in-memory runner and re-establish per-worker connections inside each clone."
475
507
  end
476
508
 
477
509
  def run_mutation_load_time(mutation, result, project_root)
@@ -1,3 +1,3 @@
1
1
  module MutationTester
2
- VERSION = '1.4.2'.freeze
2
+ VERSION = '1.5.0'.freeze
3
3
  end
data/readme.md CHANGED
@@ -210,7 +210,8 @@ mutation_test [OPTIONS] --glob 'lib/**/*.rb'
210
210
  | `--fail-fast` | Stop the run at the first surviving mutant and finish with a failing status. Works in single-file mode and with `--glob`. |
211
211
  | `--timeout-factor N` | Per-mutant timeout budget as `N` times the measured baseline test run, never below 5 s (default: 5, must be > 0). Ignored when `config.timeout` is set explicitly, which keeps a fixed budget. See [Configuration](#configuration). |
212
212
  | `--timeout-policy MODE` | Scoring policy for timed-out mutants: `killed` (default) counts a timeout as a kill; `separate` keeps timeouts out of the score entirely (`killed / (killed + survived)`) and reports them only as their own category in the console, JSON and HTML reports. |
213
- | `--worker-env NAME` | Set environment variable `NAME` to a distinct per-worker value before each parallel worker boots (`parallel_tests` `TEST_ENV_NUMBER` convention: worker 0 -> `""`, worker N -> `N+1`), so a `parallel_tests`-style `database.yml` selects a per-worker database. You provision the databases (e.g. `rake parallel:prepare`). Not supported by the `in_memory` runner (it falls back to `fork`). See [Making parallelism work with Rails](#making-parallelism-work-with-rails). |
213
+ | `--worker-env NAME` | Set environment variable `NAME` to a distinct per-worker value before each parallel worker boots (`parallel_tests` `TEST_ENV_NUMBER` convention: worker 0 -> `""`, worker N -> `N+1`), so a `parallel_tests`-style `database.yml` selects a per-worker database. You provision the databases (e.g. `rake parallel:prepare`). A parallel `in_memory` run falls back to `fork` unless `--after-fork` is also given; a serial run (`-p 1`) decides mutants in a shadow workspace instead of mutating the checkout in place. See [Making parallelism work with Rails](#making-parallelism-work-with-rails). |
214
+ | `--after-fork FILE` | Ruby file loaded inside each preloaded in-memory clone right after it forks and receives its per-worker environment (see `--worker-env`), so the app can re-establish per-worker state such as its database connection. With `--worker-env` set, this keeps the `in_memory` runner available in parallel runs. A clone whose after-fork file raises is dropped with a stderr warning and its worker falls back to file-based execution. See [Making parallelism work with Rails](#making-parallelism-work-with-rails). |
214
215
  | `--strict-equality` | Enable the opt-in strict-equality probes (`==` → `eql?` and `==` → `equal?`). Default off; expect noise on code that does not distinguish numeric types or object identity. See [Strict Equality Mutations](docs/mutation-types.md#strict-equality-mutations-opt-in). |
215
216
  | `-h, --help` | Show help message. |
216
217
  | `-v, --version` | Show version. |
@@ -510,6 +511,19 @@ MutationTester.configure do |config|
510
511
  # setting a specific mode forces it. See the Execution model section below.
511
512
  config.runner = :auto
512
513
 
514
+ # Per-worker database isolation (parallel_tests TEST_ENV_NUMBER convention).
515
+ # Set to an environment variable name to give each parallel worker a distinct
516
+ # value before it boots its test environment. Equivalent to the --worker-env
517
+ # CLI flag. See Making parallelism work with Rails below.
518
+ # config.worker_env_var = "TEST_ENV_NUMBER"
519
+
520
+ # Ruby file loaded inside each preloaded in-memory clone right after it forks
521
+ # and receives its per-worker environment, so the app can re-establish
522
+ # per-worker state such as its database connection. Keeps the in_memory
523
+ # runner available in parallel worker-env runs. Equivalent to the
524
+ # --after-fork CLI flag.
525
+ # config.after_fork_file = "db/mutation_after_fork.rb"
526
+
513
527
  # Two-phase test selection (RSpec only): fast-kill on a matching example
514
528
  # subset, always confirmed by the full file before a mutant is reported as
515
529
  # survived. Set to false to always run the full file. See Execution model.
@@ -651,10 +665,41 @@ bundle exec mutation_test --glob 'app/models/**/*.rb' \
651
665
 
652
666
  `MUTATION_TESTER_WORKER_ENV=TEST_ENV_NUMBER` is equivalent to passing the flag.
653
667
 
654
- **Runner support.** `--worker-env` works with the `fork` and `spawn` runners, where each mutant boots its test
655
- environment freshly and picks up the variable. The `in_memory` runner clones a single preloaded worker that has already
656
- connected to one database, so it cannot isolate a per-worker database; when `--worker-env` is set the runner selection
657
- skips `in_memory` and uses `fork`, announcing the reason on stderr.
668
+ **Runner support.** `--worker-env` works with the `fork` and `spawn` runners out of the box, because each mutant boots
669
+ its test environment freshly and picks up the variable. The `in_memory` runner clones a single preloaded worker that
670
+ has already connected to one database, so setting the variable alone cannot re-point an existing connection; in a
671
+ parallel run without `--after-fork` the runner selection therefore skips `in_memory` and uses `fork`, announcing the
672
+ reason on stderr. A serial run (`-p 1`) has only one worker and stays in memory.
673
+
674
+ **Keeping the in-memory runner with `--after-fork`.** Pass `--after-fork FILE` (or
675
+ `MUTATION_TESTER_AFTER_FORK=FILE`) to keep the `in_memory` runner in parallel worker-env runs. Each preloaded clone
676
+ then receives its per-worker value of the `--worker-env` variable and loads `FILE` right after forking, and that file
677
+ is where your app re-establishes its per-worker state. For a Rails app with a `TEST_ENV_NUMBER`-keyed `database.yml`
678
+ that usually means reconnecting ActiveRecord:
679
+
680
+ ```ruby
681
+ # db/mutation_after_fork.rb
682
+ ActiveRecord::Base.establish_connection(
683
+ ActiveRecord::Base.configurations
684
+ .configs_for(env_name: 'test', name: 'primary')
685
+ .configuration_hash
686
+ .merge(database: "myapp_test#{ENV['TEST_ENV_NUMBER']}")
687
+ )
688
+ ```
689
+
690
+ ```bash
691
+ bundle exec mutation_test app/models/user.rb spec/models/user_spec.rb \
692
+ -p 4 --worker-env TEST_ENV_NUMBER --after-fork db/mutation_after_fork.rb
693
+ ```
694
+
695
+ The file runs once per clone, inside the clone only (never in the primary preloaded worker or in your shell process).
696
+ If it raises, the clone reports the error on stderr and is dropped, and its worker decides its share of mutants through
697
+ the file-based path; if the file does not exist, the whole run falls back to file-based execution with a warning.
698
+
699
+ **Serial runs never mutate your checkout.** With `--worker-env` set, a `-p 1` run on the file-based runners decides
700
+ each mutant in a shadow workspace instead of writing mutants into the real source file, so a killed process cannot
701
+ leave a mutated file behind. (Without `--worker-env`, a serial file-based run still uses in-place mutation with a
702
+ `.mutation_backup` file that the next run restores automatically.)
658
703
 
659
704
  **Still simplest without a parallel database setup:** if you have not provisioned per-worker databases, keep Rails
660
705
  model runs on serial `-p 1`. `--worker-env` is only useful once the databases exist.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mutation_tester
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.2
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kamil Dzierbicki
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-08-05 00:00:00.000000000 Z
11
+ date: 2026-08-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parallel