evilution 0.18.0 → 0.20.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 +4 -4
- data/.beads/.migration-hint-ts +1 -1
- data/.beads/issues.jsonl +85 -15
- data/CHANGELOG.md +37 -0
- data/README.md +24 -4
- data/lib/evilution/baseline.rb +9 -1
- data/lib/evilution/cli.rb +11 -0
- data/lib/evilution/equivalent/detector.rb +3 -1
- data/lib/evilution/equivalent/heuristic/alias_swap.rb +2 -1
- data/lib/evilution/equivalent/heuristic/void_context.rb +77 -0
- data/lib/evilution/integration/crash_detector.rb +55 -0
- data/lib/evilution/integration/rspec.rb +102 -6
- data/lib/evilution/isolation/fork.rb +10 -6
- data/lib/evilution/isolation/in_process.rb +14 -10
- data/lib/evilution/mutator/operator/begin_unwrap.rb +21 -0
- data/lib/evilution/mutator/operator/block_param_removal.rb +57 -0
- data/lib/evilution/mutator/operator/case_when.rb +55 -0
- data/lib/evilution/mutator/operator/equality_to_identity.rb +22 -0
- data/lib/evilution/mutator/operator/lambda_body.rb +18 -0
- data/lib/evilution/mutator/operator/loop_flip.rb +27 -0
- data/lib/evilution/mutator/operator/method_body_replacement.rb +10 -6
- data/lib/evilution/mutator/operator/predicate_replacement.rb +27 -0
- data/lib/evilution/mutator/operator/retry_removal.rb +16 -0
- data/lib/evilution/mutator/operator/send_mutation.rb +8 -1
- data/lib/evilution/mutator/operator/string_interpolation.rb +32 -0
- data/lib/evilution/mutator/registry.rb +10 -1
- data/lib/evilution/parallel/pool.rb +2 -2
- data/lib/evilution/parallel/work_queue.rb +54 -13
- data/lib/evilution/related_spec_heuristic.rb +63 -0
- data/lib/evilution/reporter/cli.rb +14 -8
- data/lib/evilution/reporter/html.rb +32 -2
- data/lib/evilution/reporter/json.rb +15 -0
- data/lib/evilution/result/coverage_gap.rb +35 -0
- data/lib/evilution/result/coverage_gap_grouper.rb +22 -0
- data/lib/evilution/result/mutation_result.rb +5 -2
- data/lib/evilution/result/summary.rb +5 -0
- data/lib/evilution/runner.rb +7 -4
- data/lib/evilution/session/store.rb +13 -0
- data/lib/evilution/spec_resolver.rb +13 -1
- data/lib/evilution/version.rb +1 -1
- data/lib/evilution.rb +9 -0
- data/script/memory_check +22 -0
- metadata +16 -2
data/lib/evilution/runner.rb
CHANGED
|
@@ -325,7 +325,7 @@ class Evilution::Runner
|
|
|
325
325
|
|
|
326
326
|
def run_mutations_parallel(mutations, baseline_result = nil)
|
|
327
327
|
integration = build_integration
|
|
328
|
-
pool = Evilution::Parallel::Pool.new(size: config.jobs, hooks: @hooks)
|
|
328
|
+
pool = Evilution::Parallel::Pool.new(size: config.jobs, hooks: @hooks, item_timeout: config.timeout ? config.timeout * 2 : nil)
|
|
329
329
|
worker_isolator = Evilution::Isolation::InProcess.new
|
|
330
330
|
spec_resolver = baseline_result&.failed? ? Evilution::SpecResolver.new : nil
|
|
331
331
|
state = { results: [], survived_count: 0, truncated: false, completed: 0 }
|
|
@@ -396,7 +396,8 @@ class Evilution::Runner
|
|
|
396
396
|
duration: result.duration,
|
|
397
397
|
test_command: result.test_command,
|
|
398
398
|
child_rss_kb: result.child_rss_kb,
|
|
399
|
-
memory_delta_kb: result.memory_delta_kb
|
|
399
|
+
memory_delta_kb: result.memory_delta_kb,
|
|
400
|
+
parent_rss_kb: result.parent_rss_kb
|
|
400
401
|
)
|
|
401
402
|
end
|
|
402
403
|
|
|
@@ -407,7 +408,8 @@ class Evilution::Runner
|
|
|
407
408
|
killing_test: result.killing_test,
|
|
408
409
|
test_command: result.test_command,
|
|
409
410
|
child_rss_kb: result.child_rss_kb,
|
|
410
|
-
memory_delta_kb: result.memory_delta_kb
|
|
411
|
+
memory_delta_kb: result.memory_delta_kb,
|
|
412
|
+
parent_rss_kb: result.parent_rss_kb
|
|
411
413
|
}
|
|
412
414
|
end
|
|
413
415
|
|
|
@@ -420,7 +422,8 @@ class Evilution::Runner
|
|
|
420
422
|
killing_test: data[:killing_test],
|
|
421
423
|
test_command: data[:test_command],
|
|
422
424
|
child_rss_kb: data[:child_rss_kb],
|
|
423
|
-
memory_delta_kb: data[:memory_delta_kb]
|
|
425
|
+
memory_delta_kb: data[:memory_delta_kb],
|
|
426
|
+
parent_rss_kb: data[:parent_rss_kb]
|
|
424
427
|
)
|
|
425
428
|
end
|
|
426
429
|
end
|
|
@@ -65,6 +65,7 @@ class Evilution::Session::Store
|
|
|
65
65
|
git: git_context,
|
|
66
66
|
summary: build_summary(summary),
|
|
67
67
|
survived: summary.survived_results.map { |r| build_mutation_detail(r) },
|
|
68
|
+
coverage_gaps: build_coverage_gaps(summary),
|
|
68
69
|
killed_count: summary.killed,
|
|
69
70
|
timed_out_count: summary.timed_out,
|
|
70
71
|
error_count: summary.errors,
|
|
@@ -101,6 +102,18 @@ class Evilution::Session::Store
|
|
|
101
102
|
}
|
|
102
103
|
end
|
|
103
104
|
|
|
105
|
+
def build_coverage_gaps(summary)
|
|
106
|
+
summary.coverage_gaps.map do |gap|
|
|
107
|
+
{
|
|
108
|
+
file: gap.file_path,
|
|
109
|
+
subject: gap.subject_name,
|
|
110
|
+
line: gap.line,
|
|
111
|
+
operators: gap.operator_names,
|
|
112
|
+
count: gap.count
|
|
113
|
+
}
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
104
117
|
def git_context
|
|
105
118
|
sha = `git rev-parse HEAD 2>/dev/null`.strip
|
|
106
119
|
branch = `git rev-parse --abbrev-ref HEAD 2>/dev/null`.strip
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
class Evilution::SpecResolver
|
|
4
4
|
STRIPPABLE_PREFIXES = %w[lib/ app/].freeze
|
|
5
|
+
CONTROLLER_PREFIX = "controllers/"
|
|
5
6
|
|
|
6
7
|
def call(source_path)
|
|
7
8
|
return nil if source_path.nil? || source_path.empty?
|
|
@@ -29,7 +30,8 @@ class Evilution::SpecResolver
|
|
|
29
30
|
|
|
30
31
|
candidates = if prefix
|
|
31
32
|
stripped = base.delete_prefix(prefix)
|
|
32
|
-
|
|
33
|
+
request_spec = controller_to_request_spec(stripped)
|
|
34
|
+
[request_spec, "spec/#{stripped}", "spec/#{base}"].compact
|
|
33
35
|
else
|
|
34
36
|
["spec/#{base}"]
|
|
35
37
|
end
|
|
@@ -39,6 +41,16 @@ class Evilution::SpecResolver
|
|
|
39
41
|
candidates + fallbacks
|
|
40
42
|
end
|
|
41
43
|
|
|
44
|
+
def controller_to_request_spec(stripped_path)
|
|
45
|
+
return nil unless stripped_path.start_with?(CONTROLLER_PREFIX)
|
|
46
|
+
return nil unless stripped_path.end_with?("_controller_spec.rb")
|
|
47
|
+
|
|
48
|
+
request_path = stripped_path
|
|
49
|
+
.delete_prefix(CONTROLLER_PREFIX)
|
|
50
|
+
.sub(/_controller_spec\.rb\z/, "_spec.rb")
|
|
51
|
+
"spec/requests/#{request_path}"
|
|
52
|
+
end
|
|
53
|
+
|
|
42
54
|
def parent_fallback_candidates(spec_path)
|
|
43
55
|
parts = spec_path.split("/")
|
|
44
56
|
# parts: ["spec", "foo", "bar_spec.rb"] — need at least 3 parts for fallback
|
data/lib/evilution/version.rb
CHANGED
data/lib/evilution.rb
CHANGED
|
@@ -81,6 +81,15 @@ require_relative "evilution/mutator/operator/yield_statement"
|
|
|
81
81
|
require_relative "evilution/mutator/operator/splat_operator"
|
|
82
82
|
require_relative "evilution/mutator/operator/defined_check"
|
|
83
83
|
require_relative "evilution/mutator/operator/regex_capture"
|
|
84
|
+
require_relative "evilution/mutator/operator/loop_flip"
|
|
85
|
+
require_relative "evilution/mutator/operator/string_interpolation"
|
|
86
|
+
require_relative "evilution/mutator/operator/retry_removal"
|
|
87
|
+
require_relative "evilution/mutator/operator/case_when"
|
|
88
|
+
require_relative "evilution/mutator/operator/predicate_replacement"
|
|
89
|
+
require_relative "evilution/mutator/operator/equality_to_identity"
|
|
90
|
+
require_relative "evilution/mutator/operator/lambda_body"
|
|
91
|
+
require_relative "evilution/mutator/operator/begin_unwrap"
|
|
92
|
+
require_relative "evilution/mutator/operator/block_param_removal"
|
|
84
93
|
require_relative "evilution/mutator/registry"
|
|
85
94
|
require_relative "evilution/equivalent"
|
|
86
95
|
require_relative "evilution/equivalent/heuristic"
|
data/script/memory_check
CHANGED
|
@@ -3,8 +3,12 @@
|
|
|
3
3
|
|
|
4
4
|
require_relative "../lib/evilution"
|
|
5
5
|
require_relative "../lib/evilution/memory/leak_check"
|
|
6
|
+
require_relative "../lib/evilution/integration/rspec"
|
|
6
7
|
|
|
7
8
|
FIXTURE = File.expand_path("../spec/support/fixtures/simple_class.rb", __dir__)
|
|
9
|
+
FIXTURE_SPEC = File.expand_path("../spec/support/fixtures/simple_class_spec.rb", __dir__)
|
|
10
|
+
COMPLEX_FIXTURE = File.expand_path("../lib/evilution/config.rb", __dir__)
|
|
11
|
+
COMPLEX_FIXTURE_SPEC = File.expand_path("../spec/evilution/config_spec.rb", __dir__)
|
|
8
12
|
ITERATIONS = Integer(ENV.fetch("MEMORY_CHECK_ITERATIONS", 50))
|
|
9
13
|
MAX_GROWTH_KB = Integer(ENV.fetch("MEMORY_CHECK_MAX_GROWTH_KB", 10_240))
|
|
10
14
|
|
|
@@ -90,5 +94,23 @@ if mutations.size >= 2
|
|
|
90
94
|
end
|
|
91
95
|
end
|
|
92
96
|
|
|
97
|
+
# 5. RSpec integration per-mutation with complex fixture
|
|
98
|
+
# Uses Config (227 LOC, 73 specs, 564 mutations) for realistic per-mutation load:
|
|
99
|
+
# more ExampleGroup subclasses, deeper spec nesting, heavier metadata.
|
|
100
|
+
complex_parser = Evilution::AST::Parser.new
|
|
101
|
+
complex_registry = Evilution::Mutator::Registry.default
|
|
102
|
+
complex_subjects = complex_parser.call(COMPLEX_FIXTURE)
|
|
103
|
+
complex_mutations = complex_subjects.flat_map { |s| complex_registry.mutations_for(s) }
|
|
104
|
+
|
|
105
|
+
integration = Evilution::Integration::RSpec.new(test_files: [COMPLEX_FIXTURE_SPEC])
|
|
106
|
+
|
|
107
|
+
all_passed &= run_check("RSpec integration per-mutation (Config)", iterations: 20, max_growth_kb: 20_480) do
|
|
108
|
+
mutation = complex_mutations.sample
|
|
109
|
+
result = integration.call(mutation)
|
|
110
|
+
raise "RSpec integration memory check failed: #{result[:error]}" if result[:error]
|
|
111
|
+
|
|
112
|
+
result
|
|
113
|
+
end
|
|
114
|
+
|
|
93
115
|
puts all_passed ? "All memory checks passed." : "Some memory checks failed!"
|
|
94
116
|
exit(all_passed ? 0 : 1)
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: evilution
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.20.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Denis Kiselev
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-04-
|
|
11
|
+
date: 2026-04-08 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: diff-lcs
|
|
@@ -102,6 +102,7 @@ files:
|
|
|
102
102
|
- lib/evilution/equivalent/heuristic/dead_code.rb
|
|
103
103
|
- lib/evilution/equivalent/heuristic/method_body_nil.rb
|
|
104
104
|
- lib/evilution/equivalent/heuristic/noop_source.rb
|
|
105
|
+
- lib/evilution/equivalent/heuristic/void_context.rb
|
|
105
106
|
- lib/evilution/git.rb
|
|
106
107
|
- lib/evilution/git/changed_files.rb
|
|
107
108
|
- lib/evilution/hooks.rb
|
|
@@ -109,6 +110,7 @@ files:
|
|
|
109
110
|
- lib/evilution/hooks/registry.rb
|
|
110
111
|
- lib/evilution/integration.rb
|
|
111
112
|
- lib/evilution/integration/base.rb
|
|
113
|
+
- lib/evilution/integration/crash_detector.rb
|
|
112
114
|
- lib/evilution/integration/rspec.rb
|
|
113
115
|
- lib/evilution/isolation.rb
|
|
114
116
|
- lib/evilution/isolation/fork.rb
|
|
@@ -130,12 +132,15 @@ files:
|
|
|
130
132
|
- lib/evilution/mutator/operator/arithmetic_replacement.rb
|
|
131
133
|
- lib/evilution/mutator/operator/array_literal.rb
|
|
132
134
|
- lib/evilution/mutator/operator/bang_method.rb
|
|
135
|
+
- lib/evilution/mutator/operator/begin_unwrap.rb
|
|
133
136
|
- lib/evilution/mutator/operator/bitwise_complement.rb
|
|
134
137
|
- lib/evilution/mutator/operator/bitwise_replacement.rb
|
|
138
|
+
- lib/evilution/mutator/operator/block_param_removal.rb
|
|
135
139
|
- lib/evilution/mutator/operator/block_removal.rb
|
|
136
140
|
- lib/evilution/mutator/operator/boolean_literal_replacement.rb
|
|
137
141
|
- lib/evilution/mutator/operator/boolean_operator_replacement.rb
|
|
138
142
|
- lib/evilution/mutator/operator/break_statement.rb
|
|
143
|
+
- lib/evilution/mutator/operator/case_when.rb
|
|
139
144
|
- lib/evilution/mutator/operator/class_variable_write.rb
|
|
140
145
|
- lib/evilution/mutator/operator/collection_replacement.rb
|
|
141
146
|
- lib/evilution/mutator/operator/collection_return.rb
|
|
@@ -146,6 +151,7 @@ files:
|
|
|
146
151
|
- lib/evilution/mutator/operator/conditional_negation.rb
|
|
147
152
|
- lib/evilution/mutator/operator/defined_check.rb
|
|
148
153
|
- lib/evilution/mutator/operator/ensure_removal.rb
|
|
154
|
+
- lib/evilution/mutator/operator/equality_to_identity.rb
|
|
149
155
|
- lib/evilution/mutator/operator/explicit_super_mutation.rb
|
|
150
156
|
- lib/evilution/mutator/operator/float_literal.rb
|
|
151
157
|
- lib/evilution/mutator/operator/global_variable_write.rb
|
|
@@ -157,7 +163,9 @@ files:
|
|
|
157
163
|
- lib/evilution/mutator/operator/instance_variable_write.rb
|
|
158
164
|
- lib/evilution/mutator/operator/integer_literal.rb
|
|
159
165
|
- lib/evilution/mutator/operator/keyword_argument.rb
|
|
166
|
+
- lib/evilution/mutator/operator/lambda_body.rb
|
|
160
167
|
- lib/evilution/mutator/operator/local_variable_assignment.rb
|
|
168
|
+
- lib/evilution/mutator/operator/loop_flip.rb
|
|
161
169
|
- lib/evilution/mutator/operator/method_body_replacement.rb
|
|
162
170
|
- lib/evilution/mutator/operator/method_call_removal.rb
|
|
163
171
|
- lib/evilution/mutator/operator/mixin_removal.rb
|
|
@@ -168,6 +176,7 @@ files:
|
|
|
168
176
|
- lib/evilution/mutator/operator/pattern_matching_alternative.rb
|
|
169
177
|
- lib/evilution/mutator/operator/pattern_matching_array.rb
|
|
170
178
|
- lib/evilution/mutator/operator/pattern_matching_guard.rb
|
|
179
|
+
- lib/evilution/mutator/operator/predicate_replacement.rb
|
|
171
180
|
- lib/evilution/mutator/operator/range_replacement.rb
|
|
172
181
|
- lib/evilution/mutator/operator/receiver_replacement.rb
|
|
173
182
|
- lib/evilution/mutator/operator/redo_statement.rb
|
|
@@ -175,11 +184,13 @@ files:
|
|
|
175
184
|
- lib/evilution/mutator/operator/regexp_mutation.rb
|
|
176
185
|
- lib/evilution/mutator/operator/rescue_body_replacement.rb
|
|
177
186
|
- lib/evilution/mutator/operator/rescue_removal.rb
|
|
187
|
+
- lib/evilution/mutator/operator/retry_removal.rb
|
|
178
188
|
- lib/evilution/mutator/operator/return_value_removal.rb
|
|
179
189
|
- lib/evilution/mutator/operator/scalar_return.rb
|
|
180
190
|
- lib/evilution/mutator/operator/send_mutation.rb
|
|
181
191
|
- lib/evilution/mutator/operator/splat_operator.rb
|
|
182
192
|
- lib/evilution/mutator/operator/statement_deletion.rb
|
|
193
|
+
- lib/evilution/mutator/operator/string_interpolation.rb
|
|
183
194
|
- lib/evilution/mutator/operator/string_literal.rb
|
|
184
195
|
- lib/evilution/mutator/operator/superclass_removal.rb
|
|
185
196
|
- lib/evilution/mutator/operator/symbol_literal.rb
|
|
@@ -189,6 +200,7 @@ files:
|
|
|
189
200
|
- lib/evilution/parallel.rb
|
|
190
201
|
- lib/evilution/parallel/pool.rb
|
|
191
202
|
- lib/evilution/parallel/work_queue.rb
|
|
203
|
+
- lib/evilution/related_spec_heuristic.rb
|
|
192
204
|
- lib/evilution/reporter.rb
|
|
193
205
|
- lib/evilution/reporter/cli.rb
|
|
194
206
|
- lib/evilution/reporter/html.rb
|
|
@@ -196,6 +208,8 @@ files:
|
|
|
196
208
|
- lib/evilution/reporter/progress_bar.rb
|
|
197
209
|
- lib/evilution/reporter/suggestion.rb
|
|
198
210
|
- lib/evilution/result.rb
|
|
211
|
+
- lib/evilution/result/coverage_gap.rb
|
|
212
|
+
- lib/evilution/result/coverage_gap_grouper.rb
|
|
199
213
|
- lib/evilution/result/mutation_result.rb
|
|
200
214
|
- lib/evilution/result/summary.rb
|
|
201
215
|
- lib/evilution/runner.rb
|