evilution 0.30.1 → 0.30.2

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: df5ef86d0b451e9629f95ab8115ebfbf4cab99b6a16dfd543582e51fab368158
4
- data.tar.gz: 924099a4fec1e9b1af0d84d5daf67b26883476112ea4346556086882e198d2fc
3
+ metadata.gz: af414a5edcfb3e25beea8ef2dea4235f7f9cadc1d8e086a526846ff456639235
4
+ data.tar.gz: a32bc1f4dcf1898dc8be020356ca746354384894994cab0dac5e8d0b25692ebb
5
5
  SHA512:
6
- metadata.gz: 987dfe13f4d495e220fd90fb207ee17fbede833d97cf1f62b109601c2009fa3e68dd68f8b56e8f457ed68139af6f5743902e1562dcd9b73335945d11155b83ea
7
- data.tar.gz: 154501f04dd8c867506ebb479934af83909e2568a175f7cbf5d2d5c59f68d50143da062bae16599c71e97edb3f43bc0158d31af2ca01febe2fc32d7634203f23
6
+ metadata.gz: b65c3fd7ef73fccbf08a9d3dcf63c6e2160fa4ef3625139c7a8c4be137b52dcf55bd4b84da58d80862bf2f34cbf022bd2a1c937f474a292eb5368dde816fd0fd
7
+ data.tar.gz: 80b60c39df9dadb4193a7d65ff0fe22bfa3e395f658b13da53a8042699e7e9d869920a4ab127759d3e975cecdcd0821bc3bdfff6c2f482f6672383b2e924c3a4
@@ -370,3 +370,10 @@
370
370
  {"id":"int-5c439789","kind":"field_change","created_at":"2026-05-15T04:10:04.253005681Z","actor":"Denis Kiselev","issue_id":"EV-m47s","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"Closed"}}
371
371
  {"id":"int-9c968bc0","kind":"field_change","created_at":"2026-05-15T04:10:02.826901138Z","actor":"Denis Kiselev","issue_id":"EV-74e3","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"Closed"}}
372
372
  {"id":"int-4677d561","kind":"field_change","created_at":"2026-05-15T05:18:42.843114482Z","actor":"Denis Kiselev","issue_id":"EV-ju3o","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
373
+ {"id":"int-f977cb7c","kind":"field_change","created_at":"2026-05-15T06:37:09.06468763Z","actor":"Denis Kiselev","issue_id":"EV-mn3p","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"Canary PASS 0.30.1. client.rb 882 mutations 100% score, 3 equivalent. capsule.rb 6.85% — upstream test debt, not evilution bug. No evilution bugs surfaced. Artifact .artifacts/sidekiq_sidekiq.yml."}}
374
+ {"id":"int-1b07bd8c","kind":"field_change","created_at":"2026-05-15T06:50:28.638714936Z","actor":"Denis Kiselev","issue_id":"EV-7764","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"Canary infra mismatch — pagy uses thor-driven multi-file test orchestration that single-spec evilution invocation can't replicate. No evilution bug surfaced. Documented in artifact."}}
375
+ {"id":"int-47573a1f","kind":"field_change","created_at":"2026-05-15T06:51:44.810876255Z","actor":"Denis Kiselev","issue_id":"EV-ff13","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"Canary PASS 0.30.1+EV-5nxs. Baseline 509/0/0. object_utils.rb 57 mutations 100%, candidates.rb 100% (21 muts/s). No evilution bugs. Artifact .artifacts/norman_friendly_id.yml."}}
376
+ {"id":"int-296f712f","kind":"field_change","created_at":"2026-05-15T06:57:54.666163064Z","actor":"Denis Kiselev","issue_id":"EV-9cd2","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Canary smoke_fail 2026-05-15. Baseline 431/0/0. All targeted mutations 0% kill. Same pattern as pagy. Suspected ActiveSupport::TestCase + reset_state + class-reopen gap in evilution Minitest integration but no minimal repro yet. Not filed as separate bug — needs smaller reproducer. Artifact .artifacts/kaminari_kaminari.yml."}}
377
+ {"id":"int-90ffe424","kind":"field_change","created_at":"2026-05-15T06:58:55.664447898Z","actor":"Denis Kiselev","issue_id":"EV-225l","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"Canary SKIPPED 2026-05-15. test_helper.rb spawns its own redis-server (no env override); host has no redis-server binary. Sidekiq EV-mn3p already covers concurrency tier signal (100% PASS on client.rb). Resque revisit only justified if a resque-specific bug surfaces later. Artifact .artifacts/resque_resque.yml."}}
378
+ {"id":"int-0648236b","kind":"field_change","created_at":"2026-05-15T07:00:59.017162278Z","actor":"Denis Kiselev","issue_id":"EV-ks6i","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"Canary PASS 0.30.1+EV-5nxs. configuration.rb 40.69% upstream gap. body.rb 66.78% upstream gap. 0 errored mutations across both targets. No evilution bugs. Artifact .artifacts/mikel_mail.yml."}}
379
+ {"id":"int-8a14f28f","kind":"field_change","created_at":"2026-05-15T08:24:58.326924498Z","actor":"Denis Kiselev","issue_id":"EV-5nxs","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Closed"}}
data/.rubocop_todo.yml CHANGED
@@ -11,3 +11,4 @@ Metrics/ClassLength:
11
11
  - "lib/evilution/isolation/fork.rb"
12
12
  - "lib/evilution/integration/minitest.rb"
13
13
  - "lib/evilution/mcp/mutate_tool.rb"
14
+ - "lib/evilution/runner/isolation_resolver.rb"
data/CHANGELOG.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  Versioning policy: see [docs/versioning.md](docs/versioning.md).
4
4
 
5
+ ## [0.30.2] - 2026-05-15
6
+
7
+ ### Fixed
8
+
9
+ - **`method_body_replacement` no longer corrupts methods with a method-level `rescue`/`ensure`** — for the shorthand form `def foo; stmts; rescue => e; ...; end` (no explicit `begin`), Prism makes `DefNode#body` a `BeginNode` whose `location` spans the *entire* `def...end` block — the `def` keyword and matching `end` included. The operator replaced that whole range with `nil` / `self` / `super`, obliterating the method framing and leaving the replacement dangling at the enclosing class/module scope; `super` replacements then raised `NoMethodError: super called outside of method` at load time, and the run miscounted every such mutation as an error. The operator now targets only the leading statements (`body.statements`) when the body is a `BeginNode`, preserving the `rescue`/`else`/`ensure` clauses and the `def` framing; methods with a rescue/ensure-only body (no leading statements) emit no mutation. Super *detection* still scans the full body, so a `super` call in any clause keeps the bare-super replacement candidate. Surfaced by the EV-5rtm redis-rb stability canary — 5 methods in `lib/redis/client.rb` (`ensure_connected`, `call_v`, `blocking_call_v`, `pipelined`, `multi`) all-errored before this fix (EV-9a6c, PR #1251, GH #1247)
10
+ - **`Minitest.autorun` stub now runs before the user `--preload` file** — the EV-7u9c fix (0.30.1) stubbed `Minitest.autorun` from `Integration::Minitest#ensure_framework_loaded` and `run_baseline_test_file`, both of which execute *after* `Runner#perform_preload`. When the user passed `--preload <file>` and that file required `minitest/autorun` (typical for `spec_helper.rb` / `test_helper.rb`), the `at_exit` handler was installed during preload — before the stub took effect — and the misleading "invalid option: --integration" banner returned at process exit. `Evilution::Runner::IsolationResolver#perform_preload` now invokes `Evilution::Integration::Minitest.stub_autorun!` immediately before requiring the preload file when `config.integration == :minitest`; it is a no-op for the RSpec integration. Surfaced by the EV-xqv3 rouge stability canary, whose `spec/spec_helper.rb` requires `minitest/autorun` (EV-5nxs, PR #1249, GH #1248)
11
+
5
12
  ## [0.30.1] - 2026-05-15
6
13
 
7
14
  ### Fixed
@@ -7,14 +7,18 @@ class Evilution::Mutator::Operator::MethodBodyReplacement < Evilution::Mutator::
7
7
  SUPER_REPLACEMENT = "super"
8
8
 
9
9
  def visit_def_node(node)
10
- if node.body
10
+ target = mutation_target(node.body)
11
+ if target
11
12
  replacements = ALWAYS_SAFE_REPLACEMENTS.dup
13
+ # Super detection scans the whole body (rescue/else/ensure clauses
14
+ # included) — a super call in any clause means a parent target exists,
15
+ # so a bare-super replacement of the statements is meaningful.
12
16
  replacements << SUPER_REPLACEMENT if body_calls_super?(node.body)
13
17
 
14
18
  replacements.each do |replacement|
15
19
  add_mutation(
16
- offset: node.body.location.start_offset,
17
- length: node.body.location.length,
20
+ offset: target.location.start_offset,
21
+ length: target.location.length,
18
22
  replacement: replacement,
19
23
  node: node
20
24
  )
@@ -26,6 +30,18 @@ class Evilution::Mutator::Operator::MethodBodyReplacement < Evilution::Mutator::
26
30
 
27
31
  private
28
32
 
33
+ # A method-level rescue/ensure (`def foo; stmts; rescue; ...; end`) makes
34
+ # node.body a BeginNode whose location spans the entire `def...end` — the
35
+ # `def` keyword and matching `end` included. Replacing that range obliterates
36
+ # the method framing, leaving the replacement (`nil`/`self`/`super`) dangling
37
+ # at the enclosing scope. The replaceable region is only the leading
38
+ # statements; returns nil when there are none (rescue/ensure-only body).
39
+ def mutation_target(body)
40
+ return body unless body.is_a?(Prism::BeginNode)
41
+
42
+ body.statements
43
+ end
44
+
29
45
  # The bare-super replacement raises NoMethodError at runtime when the enclosing
30
46
  # class has no parent implementation of the method. We emit it only when the
31
47
  # original body already calls super, using that as a heuristic that a super
@@ -37,6 +37,7 @@ class Evilution::Runner::IsolationResolver
37
37
  return unless path
38
38
 
39
39
  prepare_load_path_for_preload
40
+ prepare_integration_for_preload
40
41
  require File.expand_path(path)
41
42
  rescue Evilution::ConfigError
42
43
  raise
@@ -186,6 +187,19 @@ class Evilution::Runner::IsolationResolver
186
187
  raise Evilution::ConfigError.new("preload file not found: #{path.inspect}", file: path)
187
188
  end
188
189
 
190
+ # User preload files (spec_helper.rb, test_helper.rb) typically require
191
+ # 'minitest/autorun', which installs an at_exit handler that re-parses ARGV
192
+ # at process exit. The stub from Integration::Minitest#ensure_framework_loaded
193
+ # only fires during baseline — too late to prevent the handler from being
194
+ # registered. Stub before user code runs.
195
+ def prepare_integration_for_preload
196
+ return unless config.integration.to_s == "minitest"
197
+
198
+ require "minitest"
199
+ require_relative "../integration/minitest"
200
+ Evilution::Integration::Minitest.stub_autorun!
201
+ end
202
+
189
203
  def warn_missing_explicit_preload(path)
190
204
  return if config.quiet
191
205
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Evilution
4
- VERSION = "0.30.1"
4
+ VERSION = "0.30.2"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: evilution
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.30.1
4
+ version: 0.30.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Denis Kiselev