evilution 0.7.0 → 0.8.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: c22da4a2f18a35a0947f708163505e560fef557fb50595cbb735676d220fb17c
4
- data.tar.gz: a3427ee2af916eed4e59a73eb6431626e0089b7fb826136675e228ed712ddf08
3
+ metadata.gz: 134f193b4bce22007d6af782bd885129aafb9104a763cfec5e9a70c322be25ec
4
+ data.tar.gz: 7031e18d9eacd96886200b1c598b346d4256177045c7d639c54c3020c3f61f63
5
5
  SHA512:
6
- metadata.gz: ebf55a8e9623bededf357bb684ff2a3d3a7e3af2e743e91d5d0875488118184cd1a2699905df5752f4784c8c7e6bef8c24f766430adf325850690ed0cd70ed45
7
- data.tar.gz: c06b6ecece15c2695bc86f0baaed67217887777336abeb6635b263d3aa68bc17f4d2b68be826ccc4fa8ae9ae2794d167174b24e380675e5660c19b759bd54c1c
6
+ metadata.gz: ab72d51bfae90aca49ccca456a4d576a3caf30ceaa6c1d51b6f489548458f56cf86c7038fd9bfb69aa7452fe4ec81efbebbddaf0e4e5961f82262f31bc501690
7
+ data.tar.gz: 3ca5fa2951e70df43e4e56b611115ae1d4c2611132c53e5a26af92a90737be231a0bb3c62d3bab7444fc0de22f1210284ec8365d7134353eddf550ddbb417522
data/.beads/issues.jsonl CHANGED
@@ -83,14 +83,14 @@
83
83
  {"id":"EV-4.9","title":"Integrate diff-based targeting into Runner","description":"Update Runner to optionally use Diff::Parser + FileFilter when --diff flag is set. Filter subjects before mutation generation. File: lib/evilution/runner.rb (edit).","status":"closed","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-02T00:06:45.851982561+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-02T11:54:25.596280246+07:00","closed_at":"2026-03-02T11:54:25.596280246+07:00","close_reason":"Closed","dependencies":[{"issue_id":"EV-4.9","depends_on_id":"EV-4","type":"parent-child","created_at":"0001-01-01T00:00:00Z"},{"issue_id":"EV-4.9","depends_on_id":"EV-4.6","type":"blocks","created_at":"0001-01-01T00:00:00Z"},{"issue_id":"EV-4.9","depends_on_id":"EV-2.12","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
84
84
  {"id":"EV-40","title":"Separate rspec noise from evilution output (stdout/stderr)","description":"RSpec warnings flood stdout and corrupt JSON output. When using --format json, the JSON gets buried in hundreds of lines of rspec warnings. Fix: redirect rspec subprocess output to stderr (or /dev/null), keep evilution results on stdout. Critical for piping JSON output.","status":"closed","priority":1,"issue_type":"bug","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-13T09:56:58.604909176+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-16T11:15:15.02691418+07:00","closed_at":"2026-03-16T11:15:15.02691418+07:00","close_reason":"Fixed and merged via PR #86"}
85
85
  {"id":"EV-41","title":"Progress indicator during mutation runs","description":"Zero output during multi-minute runs makes it look stuck. Add a simple 'mutation 3/19 killed...' progress line to stderr so users know work is happening. Only show in text mode or when stderr is a TTY.","status":"closed","priority":2,"issue_type":"feature","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-13T09:57:01.023293323+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-16T11:38:40.674664201+07:00","closed_at":"2026-03-16T11:38:40.674664201+07:00","close_reason":"Fixed and merged via PR #87"}
86
- {"id":"EV-42","title":"Expand mutation operators (method call removal, receiver replacement, etc.)","description":"Currently 18 operators generating ~19 mutations vs mutant's 78 for the same method. Missing operators: method call removal, self receiver replacement, additional boolean logic mutations, nil substitutions. Higher operator count catches more subtle bugs and produces a more meaningful mutation score.","status":"open","priority":2,"issue_type":"feature","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-13T09:57:03.039944039+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-13T09:57:03.039944039+07:00"}
86
+ {"id":"EV-42","title":"Expand mutation operators (method call removal, receiver replacement, etc.)","description":"Currently 18 operators generating ~19 mutations vs mutant's 78 for the same method. Missing operators: method call removal, self receiver replacement, additional boolean logic mutations, nil substitutions. Higher operator count catches more subtle bugs and produces a more meaningful mutation score.","status":"open","priority":2,"issue_type":"feature","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-13T09:57:03.039944039+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-19T15:45:33.1262644+07:00","external_ref":"gh-76","dependencies":[{"issue_id":"EV-42","depends_on_id":"EV-45","type":"parent-child","created_at":"0001-01-01T00:00:00Z"}]}
87
87
  {"id":"EV-43","title":"Fix --version flag (returns 'version unknown')","description":"Running 'evilution --version' returns 'version unknown' instead of the actual version. The 'evilution version' subcommand works, but --version as a flag does not. Users expect --version to work.","status":"closed","priority":1,"issue_type":"bug","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-13T09:57:05.048211823+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-16T10:31:01.045381336+07:00","closed_at":"2026-03-16T10:31:01.045381336+07:00","close_reason":"Fixed and merged"}
88
88
  {"id":"EV-44","title":"Re-introduce parallel execution","description":"Bring back parallel mutation execution. Consider whether two-level fork model is still right, temp-file isolation needs, and auto-detecting when parallelism is worthwhile. GH #35.","status":"closed","priority":2,"issue_type":"feature","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-16T10:15:42.050318104+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-16T16:26:30.341909415+07:00","closed_at":"2026-03-16T16:26:30.341909415+07:00","close_reason":"PR #90 merged — process-based parallel pool with --jobs flag"}
89
- {"id":"EV-45","title":"Epic: Close mutation operator gap with mutant","description":"Track all missing mutation operators to approach mutant's ~78 mutations per method. Currently at ~19 with 18 operators. This is a long-term effort — prioritize high-value operators first.","status":"open","priority":2,"issue_type":"feature","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-16T21:50:07.597313227+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-17T10:14:41.815885279+07:00","dependencies":[{"issue_id":"EV-45","depends_on_id":"EV-46","type":"blocks","created_at":"0001-01-01T00:00:00Z"},{"issue_id":"EV-45","depends_on_id":"EV-47","type":"blocks","created_at":"0001-01-01T00:00:00Z"},{"issue_id":"EV-45","depends_on_id":"EV-48","type":"blocks","created_at":"0001-01-01T00:00:00Z"},{"issue_id":"EV-45","depends_on_id":"EV-49","type":"blocks","created_at":"0001-01-01T00:00:00Z"},{"issue_id":"EV-45","depends_on_id":"EV-50","type":"blocks","created_at":"0001-01-01T00:00:00Z"},{"issue_id":"EV-45","depends_on_id":"EV-51","type":"blocks","created_at":"0001-01-01T00:00:00Z"},{"issue_id":"EV-45","depends_on_id":"EV-52","type":"blocks","created_at":"0001-01-01T00:00:00Z"},{"issue_id":"EV-45","depends_on_id":"EV-53","type":"blocks","created_at":"0001-01-01T00:00:00Z"},{"issue_id":"EV-45","depends_on_id":"EV-54","type":"blocks","created_at":"0001-01-01T00:00:00Z"},{"issue_id":"EV-45","depends_on_id":"EV-42","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
90
- {"id":"EV-46","title":"Operator: MethodCallRemoval","description":"Remove method call, keep receiver. e.g. obj.foo(x) → obj. High bug-finding value — catches untested side effects.","status":"closed","priority":3,"issue_type":"feature","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-16T21:50:22.890467537+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-17T09:29:15.588415782+07:00","closed_at":"2026-03-17T09:29:15.588415782+07:00","close_reason":"PR merged — MethodCallRemoval operator (19th operator)"}
91
- {"id":"EV-47","title":"Operator: BlockRemoval","description":"Remove block from method call. e.g. items.map { |x| x * 2 } → items.map. Catches untested block logic.","status":"open","priority":3,"issue_type":"feature","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-16T21:50:23.067266925+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-16T21:50:23.067266925+07:00"}
92
- {"id":"EV-48","title":"Operator: RangeReplacement","description":"Swap inclusive/exclusive ranges. e.g. 1..10 → 1...10 and vice versa. Trivial to implement.","status":"open","priority":3,"issue_type":"feature","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-16T21:50:23.172934653+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-16T21:50:23.172934653+07:00"}
93
- {"id":"EV-49","title":"Operator: RegexpMutation","description":"Replace regexp with always-failing pattern. e.g. /pattern/ → /a\\A/. Catches untested regex matching.","status":"open","priority":3,"issue_type":"feature","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-16T21:50:23.274989762+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-16T21:50:23.274989762+07:00"}
89
+ {"id":"EV-45","title":"Epic: Close mutation operator gap with mutant","description":"Track all missing mutation operators to approach mutant's ~78 mutations per method. Currently at ~19 with 18 operators. This is a long-term effort — prioritize high-value operators first.","status":"open","priority":2,"issue_type":"epic","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-16T21:50:07.597313227+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-19T15:29:57.905178598+07:00"}
90
+ {"id":"EV-46","title":"Operator: MethodCallRemoval","description":"Remove method call, keep receiver. e.g. obj.foo(x) → obj. High bug-finding value — catches untested side effects.","status":"closed","priority":3,"issue_type":"feature","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-16T21:50:22.890467537+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-19T15:45:33.224167585+07:00","closed_at":"2026-03-17T09:29:15.588415782+07:00","close_reason":"PR merged — MethodCallRemoval operator (19th operator)","external_ref":"gh-94","dependencies":[{"issue_id":"EV-46","depends_on_id":"EV-45","type":"parent-child","created_at":"0001-01-01T00:00:00Z"}]}
91
+ {"id":"EV-47","title":"Operator: BlockRemoval","description":"Remove block from method call. e.g. items.map { |x| x * 2 } → items.map. Catches untested block logic.","status":"closed","priority":3,"issue_type":"feature","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-16T21:50:23.067266925+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-19T16:07:53.696798964+07:00","closed_at":"2026-03-19T16:07:53.696798964+07:00","close_reason":"Implemented BlockRemoval operator with 8 specs","external_ref":"gh-95","dependencies":[{"issue_id":"EV-47","depends_on_id":"EV-45","type":"parent-child","created_at":"0001-01-01T00:00:00Z"}]}
92
+ {"id":"EV-48","title":"Operator: RangeReplacement","description":"Swap inclusive/exclusive ranges. e.g. 1..10 → 1...10 and vice versa. Trivial to implement.","status":"closed","priority":3,"issue_type":"feature","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-16T21:50:23.172934653+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-19T18:45:22.811573395+07:00","closed_at":"2026-03-19T18:45:22.811573395+07:00","close_reason":"Implemented RangeReplacement operator with 6 specs, 100% mutation score","external_ref":"gh-96","dependencies":[{"issue_id":"EV-48","depends_on_id":"EV-45","type":"parent-child","created_at":"0001-01-01T00:00:00Z"}]}
93
+ {"id":"EV-49","title":"Operator: RegexpMutation","description":"Replace regexp with always-failing pattern. e.g. /pattern/ → /a\\A/. Catches untested regex matching.","status":"closed","priority":3,"issue_type":"feature","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-16T21:50:23.274989762+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-19T20:02:42.413023336+07:00","closed_at":"2026-03-19T20:02:42.413023336+07:00","close_reason":"Implemented RegexpMutation operator with 7 specs, 100% mutation score","external_ref":"gh-97","dependencies":[{"issue_id":"EV-49","depends_on_id":"EV-45","type":"parent-child","created_at":"0001-01-01T00:00:00Z"}]}
94
94
  {"id":"EV-5","title":"Phase 4: Polish","description":"Suggestion generator, .evilution.yml config file loading, evilution init subcommand, error handling, README","status":"closed","priority":2,"issue_type":"epic","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-02T00:05:03.497091872+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-02T11:54:32.782883746+07:00","closed_at":"2026-03-02T11:54:32.782883746+07:00","close_reason":"Closed","dependencies":[{"issue_id":"EV-5","depends_on_id":"EV-4","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
95
95
  {"id":"EV-5.1","title":"Implement Evilution::Reporter::Suggestion","description":"Generates actionable fix suggestions per surviving mutant. Each operator type has a suggestion template. E.g., ComparisonReplacement >= -> > suggests 'Add a test for the boundary case where value equals exactly [threshold]'. File: lib/evilution/reporter/suggestion.rb + spec.","status":"closed","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-02T00:06:58.038411009+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-02T11:54:21.518470747+07:00","closed_at":"2026-03-02T11:54:21.518470747+07:00","close_reason":"Closed","dependencies":[{"issue_id":"EV-5.1","depends_on_id":"EV-5","type":"parent-child","created_at":"0001-01-01T00:00:00Z"},{"issue_id":"EV-5.1","depends_on_id":"EV-2.8","type":"blocks","created_at":"0001-01-01T00:00:00Z"},{"issue_id":"EV-5.1","depends_on_id":"EV-3.21","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
96
96
  {"id":"EV-5.2","title":"Implement .evilution.yml config file loading","description":"Load config from .evilution.yml / config/evilution.yml if present. YAML keys map to Config fields. CLI flags override file values. File: update lib/evilution/config.rb + spec.","status":"closed","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-02T00:06:58.142538793+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-02T11:54:21.518473527+07:00","closed_at":"2026-03-02T11:54:21.518473527+07:00","close_reason":"Closed","dependencies":[{"issue_id":"EV-5.2","depends_on_id":"EV-5","type":"parent-child","created_at":"0001-01-01T00:00:00Z"},{"issue_id":"EV-5.2","depends_on_id":"EV-2.1","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
@@ -98,11 +98,11 @@
98
98
  {"id":"EV-5.4","title":"Add error handling and edge cases","description":"Handle: no test files found, no subjects found, fork failures, marshal errors, invalid config, files that fail to parse. Use Evilution::Error subclasses. Ensure clean exit codes (2 for tool errors).","status":"closed","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-02T00:06:58.353265504+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-02T11:54:25.59628785+07:00","closed_at":"2026-03-02T11:54:25.59628785+07:00","close_reason":"Closed","dependencies":[{"issue_id":"EV-5.4","depends_on_id":"EV-5","type":"parent-child","created_at":"0001-01-01T00:00:00Z"},{"issue_id":"EV-5.4","depends_on_id":"EV-2.12","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
99
99
  {"id":"EV-5.5","title":"Write README.md","description":"Replace placeholder README with: gem description, installation, quick start, CLI usage, configuration, output formats (JSON schema), operator list, comparison with mutant, contributing guide, license.","status":"closed","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-02T00:06:58.454635118+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-02T11:54:29.226280116+07:00","closed_at":"2026-03-02T11:54:29.226280116+07:00","close_reason":"Closed","dependencies":[{"issue_id":"EV-5.5","depends_on_id":"EV-5","type":"parent-child","created_at":"0001-01-01T00:00:00Z"},{"issue_id":"EV-5.5","depends_on_id":"EV-4.9","type":"blocks","created_at":"0001-01-01T00:00:00Z"},{"issue_id":"EV-5.5","depends_on_id":"EV-5.1","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
100
100
  {"id":"EV-5.6","title":"Update CHANGELOG.md for v0.1.0","description":"Document all features in the initial release: operator list, RSpec integration, JSON/CLI output, parallel execution, coverage-based selection, diff-based targeting.","status":"closed","priority":2,"issue_type":"task","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-02T00:06:58.571499415+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-02T11:54:29.22634981+07:00","closed_at":"2026-03-02T11:54:29.22634981+07:00","close_reason":"Closed","dependencies":[{"issue_id":"EV-5.6","depends_on_id":"EV-5","type":"parent-child","created_at":"0001-01-01T00:00:00Z"},{"issue_id":"EV-5.6","depends_on_id":"EV-5.5","type":"blocks","created_at":"0001-01-01T00:00:00Z"}]}
101
- {"id":"EV-50","title":"Operator: ConditionalFlip","description":"Flip if/unless. e.g. if cond → unless cond. Catches single-branch conditional testing.","status":"open","priority":3,"issue_type":"feature","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-16T21:50:23.379431887+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-16T21:50:23.379431887+07:00"}
102
- {"id":"EV-51","title":"Operator: SendMutation","description":"Replace known method families. e.g. flat_map → map, public_send → send, gsub → sub. Catches untested method semantics.","status":"open","priority":4,"issue_type":"feature","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-16T21:50:23.483414117+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-16T21:50:23.483414117+07:00"}
103
- {"id":"EV-52","title":"Operator: ArgumentRemoval","description":"Remove, permute, and replace individual method call arguments. e.g. foo(a, b) → foo(a), foo(b, a), foo(a, nil). This is the #1 cited gap vs mutant — where mutant catches the most bugs evilution misses. Includes: removing each argument individually, swapping argument order, replacing with nil.","status":"closed","priority":2,"issue_type":"feature","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-16T21:50:23.587291445+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-17T23:16:28.920737471+07:00","closed_at":"2026-03-17T23:16:28.920737471+07:00","close_reason":"Merged"}
104
- {"id":"EV-53","title":"Operator: ReceiverReplacement","description":"Drop explicit self receiver. e.g. self.foo → foo. Low bug-finding value.","status":"open","priority":3,"issue_type":"feature","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-16T21:50:23.692244528+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-17T10:14:41.617278357+07:00"}
105
- {"id":"EV-54","title":"Deepen existing operators with more replacement variants","description":"Existing operators generate limited variants per node. E.g. integer 5 only produces 0 and 1, but could also produce -1 and nil. Expanding variant sets across all 18 operators would significantly increase mutation coverage.","status":"open","priority":3,"issue_type":"feature","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-16T21:50:23.797359784+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-17T10:14:41.719145856+07:00"}
101
+ {"id":"EV-50","title":"Operator: ConditionalFlip","description":"Flip if/unless. e.g. if cond → unless cond. Catches single-branch conditional testing.","status":"closed","priority":3,"issue_type":"feature","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-16T21:50:23.379431887+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-19T16:25:00.912700206+07:00","closed_at":"2026-03-19T16:25:00.912700206+07:00","close_reason":"Implemented ConditionalFlip operator with 10 specs","external_ref":"gh-98","dependencies":[{"issue_id":"EV-50","depends_on_id":"EV-45","type":"parent-child","created_at":"0001-01-01T00:00:00Z"}]}
102
+ {"id":"EV-51","title":"Operator: SendMutation","description":"Replace known method families. e.g. flat_map → map, public_send → send, gsub → sub. Catches untested method semantics.","status":"open","priority":4,"issue_type":"feature","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-16T21:50:23.483414117+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-19T15:45:33.737392246+07:00","external_ref":"gh-99","dependencies":[{"issue_id":"EV-51","depends_on_id":"EV-45","type":"parent-child","created_at":"0001-01-01T00:00:00Z"}]}
103
+ {"id":"EV-52","title":"Operator: ArgumentRemoval","description":"Remove, permute, and replace individual method call arguments. e.g. foo(a, b) → foo(a), foo(b, a), foo(a, nil). This is the #1 cited gap vs mutant — where mutant catches the most bugs evilution misses. Includes: removing each argument individually, swapping argument order, replacing with nil.","status":"closed","priority":2,"issue_type":"feature","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-16T21:50:23.587291445+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-19T15:45:33.832675896+07:00","closed_at":"2026-03-17T23:16:28.920737471+07:00","close_reason":"Merged","external_ref":"gh-100","dependencies":[{"issue_id":"EV-52","depends_on_id":"EV-45","type":"parent-child","created_at":"0001-01-01T00:00:00Z"}]}
104
+ {"id":"EV-53","title":"Operator: ReceiverReplacement","description":"Drop explicit self receiver. e.g. self.foo → foo. Low bug-finding value.","status":"open","priority":3,"issue_type":"feature","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-16T21:50:23.692244528+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-19T15:45:33.9293301+07:00","external_ref":"gh-101","dependencies":[{"issue_id":"EV-53","depends_on_id":"EV-45","type":"parent-child","created_at":"0001-01-01T00:00:00Z"}]}
105
+ {"id":"EV-54","title":"Deepen existing operators with more replacement variants","description":"Existing operators generate limited variants per node. E.g. integer 5 only produces 0 and 1, but could also produce -1 and nil. Expanding variant sets across all 18 operators would significantly increase mutation coverage.","status":"open","priority":3,"issue_type":"feature","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-16T21:50:23.797359784+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-19T15:45:34.022534916+07:00","external_ref":"gh-102","dependencies":[{"issue_id":"EV-54","depends_on_id":"EV-45","type":"parent-child","created_at":"0001-01-01T00:00:00Z"}]}
106
106
  {"id":"EV-55","title":"Concrete suggestions: Comparison & Arithmetic operators","description":"Generate concrete RSpec it blocks for ComparisonReplacement and ArithmeticReplacement mutations. Test should assert exact return value at boundary conditions.","status":"open","priority":3,"issue_type":"feature","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-17T09:58:33.26716126+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-17T09:58:33.26716126+07:00"}
107
107
  {"id":"EV-56","title":"Concrete suggestions: Boolean operators","description":"Generate concrete RSpec it blocks for BooleanOperatorReplacement, BooleanLiteralReplacement, and NegationInsertion mutations. Test should assert true/false explicitly.","status":"open","priority":3,"issue_type":"feature","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-17T09:58:33.370014645+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-17T09:58:33.370014645+07:00"}
108
108
  {"id":"EV-57","title":"Concrete suggestions: Literal operators","description":"Generate concrete RSpec it blocks for IntegerLiteral, FloatLiteral, StringLiteral, and SymbolLiteral mutations. Test should assert exact value returned.","status":"open","priority":3,"issue_type":"feature","owner":"denis.kiselyov@gmail.com","created_at":"2026-03-17T09:58:33.473990949+07:00","created_by":"Denis Kiselev","updated_at":"2026-03-17T09:58:33.473990949+07:00"}
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.8.0] - 2026-03-19
4
+
5
+ ### Added
6
+
7
+ - **BlockRemoval operator** — new mutation operator that removes blocks from method calls (e.g. `items.map { |x| x * 2 }` → `items.map`); catches untested block logic
8
+ - **ConditionalFlip operator** — new mutation operator that flips `if` to `unless` and vice versa (e.g. `if cond` → `unless cond`); skips ternaries and `elsif` branches; catches single-branch conditional testing
9
+ - **RangeReplacement operator** — new mutation operator that swaps inclusive/exclusive ranges (e.g. `1..10` → `1...10` and vice versa)
10
+ - **RegexpMutation operator** — new mutation operator that replaces regexp patterns with a never-matching pattern (`/a\A/`), preserving flags; catches untested regex matching
11
+
3
12
  ## [0.7.0] - 2026-03-19
4
13
 
5
14
  ### Added
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Evilution
4
+ module Mutator
5
+ module Operator
6
+ class BlockRemoval < Base
7
+ def visit_call_node(node)
8
+ if node.block
9
+ block_node = node.block
10
+ call_end = block_node.location.start_offset
11
+ call_start = node.location.start_offset
12
+ call_without_block = @file_source.byteslice(call_start...call_end).rstrip
13
+
14
+ add_mutation(
15
+ offset: call_start,
16
+ length: node.location.length,
17
+ replacement: call_without_block,
18
+ node: node
19
+ )
20
+ end
21
+
22
+ super
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Evilution
4
+ module Mutator
5
+ module Operator
6
+ class ConditionalFlip < Base
7
+ def visit_if_node(node)
8
+ if node.if_keyword == "if" && !elsif?(node)
9
+ add_mutation(
10
+ offset: node.if_keyword_loc.start_offset,
11
+ length: node.if_keyword_loc.length,
12
+ replacement: "unless",
13
+ node: node
14
+ )
15
+ end
16
+
17
+ super
18
+ end
19
+
20
+ def visit_unless_node(node)
21
+ add_mutation(
22
+ offset: node.keyword_loc.start_offset,
23
+ length: node.keyword_loc.length,
24
+ replacement: "if",
25
+ node: node
26
+ )
27
+
28
+ super
29
+ end
30
+
31
+ private
32
+
33
+ def elsif?(node)
34
+ node.subsequent.is_a?(Prism::IfNode)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Evilution
4
+ module Mutator
5
+ module Operator
6
+ class RangeReplacement < Base
7
+ def visit_range_node(node)
8
+ replacement = node.operator == ".." ? "..." : ".."
9
+
10
+ add_mutation(
11
+ offset: node.operator_loc.start_offset,
12
+ length: node.operator_loc.length,
13
+ replacement: replacement,
14
+ node: node
15
+ )
16
+
17
+ super
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Evilution
4
+ module Mutator
5
+ module Operator
6
+ class RegexpMutation < Base
7
+ NEVER_MATCH = 'a\A'
8
+
9
+ def visit_regular_expression_node(node)
10
+ add_mutation(
11
+ offset: node.content_loc.start_offset,
12
+ length: node.content_loc.length,
13
+ replacement: NEVER_MATCH,
14
+ node: node
15
+ )
16
+
17
+ super
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -25,7 +25,11 @@ module Evilution
25
25
  Operator::ReturnValueRemoval,
26
26
  Operator::CollectionReplacement,
27
27
  Operator::MethodCallRemoval,
28
- Operator::ArgumentRemoval
28
+ Operator::ArgumentRemoval,
29
+ Operator::BlockRemoval,
30
+ Operator::ConditionalFlip,
31
+ Operator::RangeReplacement,
32
+ Operator::RegexpMutation
29
33
  ].each { |op| registry.register(op) }
30
34
  registry
31
35
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Evilution
4
- VERSION = "0.7.0"
4
+ VERSION = "0.8.0"
5
5
  end
data/lib/evilution.rb CHANGED
@@ -28,6 +28,10 @@ require_relative "evilution/mutator/operator/return_value_removal"
28
28
  require_relative "evilution/mutator/operator/collection_replacement"
29
29
  require_relative "evilution/mutator/operator/method_call_removal"
30
30
  require_relative "evilution/mutator/operator/argument_removal"
31
+ require_relative "evilution/mutator/operator/block_removal"
32
+ require_relative "evilution/mutator/operator/conditional_flip"
33
+ require_relative "evilution/mutator/operator/range_replacement"
34
+ require_relative "evilution/mutator/operator/regexp_mutation"
31
35
  require_relative "evilution/mutator/registry"
32
36
  require_relative "evilution/isolation/fork"
33
37
  require_relative "evilution/isolation/in_process"
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.7.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Denis Kiselev
@@ -101,11 +101,13 @@ files:
101
101
  - lib/evilution/mutator/operator/argument_removal.rb
102
102
  - lib/evilution/mutator/operator/arithmetic_replacement.rb
103
103
  - lib/evilution/mutator/operator/array_literal.rb
104
+ - lib/evilution/mutator/operator/block_removal.rb
104
105
  - lib/evilution/mutator/operator/boolean_literal_replacement.rb
105
106
  - lib/evilution/mutator/operator/boolean_operator_replacement.rb
106
107
  - lib/evilution/mutator/operator/collection_replacement.rb
107
108
  - lib/evilution/mutator/operator/comparison_replacement.rb
108
109
  - lib/evilution/mutator/operator/conditional_branch.rb
110
+ - lib/evilution/mutator/operator/conditional_flip.rb
109
111
  - lib/evilution/mutator/operator/conditional_negation.rb
110
112
  - lib/evilution/mutator/operator/float_literal.rb
111
113
  - lib/evilution/mutator/operator/hash_literal.rb
@@ -114,6 +116,8 @@ files:
114
116
  - lib/evilution/mutator/operator/method_call_removal.rb
115
117
  - lib/evilution/mutator/operator/negation_insertion.rb
116
118
  - lib/evilution/mutator/operator/nil_replacement.rb
119
+ - lib/evilution/mutator/operator/range_replacement.rb
120
+ - lib/evilution/mutator/operator/regexp_mutation.rb
117
121
  - lib/evilution/mutator/operator/return_value_removal.rb
118
122
  - lib/evilution/mutator/operator/statement_deletion.rb
119
123
  - lib/evilution/mutator/operator/string_literal.rb