evilution 1.1.0 → 1.3.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.
Files changed (161) hide show
  1. checksums.yaml +4 -4
  2. data/.beads/interactions.jsonl +58 -0
  3. data/.rubocop_todo.yml +9 -0
  4. data/CHANGELOG.md +86 -0
  5. data/README.md +202 -14
  6. data/docs/architecture.md +54 -5
  7. data/docs/isolation.md +3 -4
  8. data/exe/evil +8 -1
  9. data/exe/evilution +8 -1
  10. data/lib/evilution/ast/local_reads.rb +43 -0
  11. data/lib/evilution/ast/parser.rb +1 -0
  12. data/lib/evilution/ast/pattern/filter.rb +1 -0
  13. data/lib/evilution/ast/pattern/parser.rb +1 -0
  14. data/lib/evilution/ast/pattern.rb +2 -0
  15. data/lib/evilution/ast/sorbet_sig_detector.rb +1 -0
  16. data/lib/evilution/baseline.rb +37 -15
  17. data/lib/evilution/child_output.rb +1 -1
  18. data/lib/evilution/cli/command.rb +1 -0
  19. data/lib/evilution/cli/commands/tests_list.rb +4 -3
  20. data/lib/evilution/cli/commands.rb +2 -0
  21. data/lib/evilution/cli/dispatcher.rb +2 -0
  22. data/lib/evilution/cli/exit_guard.rb +61 -0
  23. data/lib/evilution/cli/parsed_args.rb +2 -0
  24. data/lib/evilution/cli/parser/command_extractor.rb +2 -0
  25. data/lib/evilution/cli/parser/file_args.rb +2 -0
  26. data/lib/evilution/cli/parser/options_builder.rb +4 -0
  27. data/lib/evilution/cli/parser/stdin_reader.rb +1 -0
  28. data/lib/evilution/cli/parser.rb +1 -0
  29. data/lib/evilution/cli/printers/tests_list.rb +5 -4
  30. data/lib/evilution/cli/printers.rb +2 -0
  31. data/lib/evilution/cli/result.rb +2 -0
  32. data/lib/evilution/cli.rb +6 -0
  33. data/lib/evilution/config/builders.rb +2 -0
  34. data/lib/evilution/config/env_loader.rb +2 -0
  35. data/lib/evilution/config/file_loader.rb +1 -0
  36. data/lib/evilution/config/sources.rb +1 -0
  37. data/lib/evilution/config/validators/example_targeting_cache.rb +1 -0
  38. data/lib/evilution/config/validators/example_targeting_fallback.rb +1 -0
  39. data/lib/evilution/config/validators/example_targeting_strategy.rb +1 -0
  40. data/lib/evilution/config/validators/fail_fast.rb +1 -0
  41. data/lib/evilution/config/validators/hooks.rb +1 -0
  42. data/lib/evilution/config/validators/ignore_patterns.rb +1 -0
  43. data/lib/evilution/config/validators/integration.rb +1 -0
  44. data/lib/evilution/config/validators/isolation.rb +1 -0
  45. data/lib/evilution/config/validators/jobs.rb +1 -0
  46. data/lib/evilution/config/validators/preload.rb +1 -0
  47. data/lib/evilution/config/validators/profile.rb +1 -0
  48. data/lib/evilution/config/validators/spec_mappings.rb +1 -0
  49. data/lib/evilution/config/validators/spec_pattern.rb +1 -0
  50. data/lib/evilution/config/validators/warmup.rb +17 -0
  51. data/lib/evilution/config/validators.rb +2 -0
  52. data/lib/evilution/config.rb +7 -5
  53. data/lib/evilution/coverage.rb +1 -1
  54. data/lib/evilution/coverage_example_filter.rb +1 -1
  55. data/lib/evilution/diagnostic.rb +22 -0
  56. data/lib/evilution/example_filter.rb +1 -1
  57. data/lib/evilution/integration/loading/body_call_neutralizer.rb +10 -2
  58. data/lib/evilution/integration/loading/concern_state_cleaner.rb +20 -4
  59. data/lib/evilution/integration/loading/reeval_warning_filter.rb +72 -0
  60. data/lib/evilution/integration/loading/source_evaluator.rb +4 -1
  61. data/lib/evilution/integration/loading/test_load_path.rb +21 -13
  62. data/lib/evilution/integration/minitest.rb +2 -1
  63. data/lib/evilution/integration/rspec/crash_detector_lifecycle.rb +9 -1
  64. data/lib/evilution/integration/rspec/state_guard/configuration_state.rb +1 -0
  65. data/lib/evilution/integration/rspec/state_guard/configuration_streams.rb +5 -1
  66. data/lib/evilution/integration/rspec/state_guard/example_groups_constants.rb +1 -2
  67. data/lib/evilution/integration/rspec/state_guard/internals.rb +1 -2
  68. data/lib/evilution/integration/rspec/state_guard/object_space_example_groups.rb +1 -2
  69. data/lib/evilution/integration/rspec/state_guard/reporter_arrays.rb +1 -0
  70. data/lib/evilution/integration/rspec/state_guard/world_example_groups.rb +1 -0
  71. data/lib/evilution/integration/rspec/state_guard/world_filtered_examples.rb +1 -0
  72. data/lib/evilution/integration/rspec/state_guard/world_sources_by_path.rb +1 -0
  73. data/lib/evilution/integration/rspec/state_guard.rb +6 -0
  74. data/lib/evilution/integration/rspec/unresolved_spec_warner.rb +2 -1
  75. data/lib/evilution/integration/rspec.rb +48 -2
  76. data/lib/evilution/integration/test_unit/test_file_resolver.rb +2 -1
  77. data/lib/evilution/isolation/fork.rb +29 -8
  78. data/lib/evilution/mcp/info_tool/actions/environment.rb +1 -0
  79. data/lib/evilution/mcp/info_tool/actions/feedback.rb +1 -0
  80. data/lib/evilution/mcp/info_tool/actions/statuses.rb +1 -0
  81. data/lib/evilution/mcp/info_tool/actions/subjects.rb +1 -0
  82. data/lib/evilution/mcp/info_tool/actions/tests.rb +1 -0
  83. data/lib/evilution/mutation.rb +1 -1
  84. data/lib/evilution/mutator/operator/argument_list_removal.rb +52 -0
  85. data/lib/evilution/mutator/operator/argument_propagation.rb +54 -0
  86. data/lib/evilution/mutator/operator/array_coercion_to_literal.rb +34 -0
  87. data/lib/evilution/mutator/operator/attribute_write_to_read.rb +34 -0
  88. data/lib/evilution/mutator/operator/bang_method.rb +11 -3
  89. data/lib/evilution/mutator/operator/binary_operand_promotion.rb +63 -0
  90. data/lib/evilution/mutator/operator/block_body_promotion.rb +45 -0
  91. data/lib/evilution/mutator/operator/block_body_to_nil.rb +52 -0
  92. data/lib/evilution/mutator/operator/block_body_to_raise.rb +34 -0
  93. data/lib/evilution/mutator/operator/block_destructuring_expansion.rb +85 -0
  94. data/lib/evilution/mutator/operator/block_parameter_drop.rb +96 -0
  95. data/lib/evilution/mutator/operator/call_to_nil.rb +34 -0
  96. data/lib/evilution/mutator/operator/coercion_emptying.rb +60 -0
  97. data/lib/evilution/mutator/operator/collection_replacement.rb +19 -9
  98. data/lib/evilution/mutator/operator/comparison_replacement.rb +37 -12
  99. data/lib/evilution/mutator/operator/const_get_to_constant_path.rb +50 -0
  100. data/lib/evilution/mutator/operator/dig_to_fetch_chain.rb +45 -0
  101. data/lib/evilution/mutator/operator/double_negation_removal.rb +22 -0
  102. data/lib/evilution/mutator/operator/dynamic_dispatch_resolution.rb +79 -0
  103. data/lib/evilution/mutator/operator/forwarding_super_to_explicit.rb +71 -0
  104. data/lib/evilution/mutator/operator/inequality_to_negated_identity.rb +43 -0
  105. data/lib/evilution/mutator/operator/keyword_argument_removal.rb +47 -0
  106. data/lib/evilution/mutator/operator/method_body_replacement.rb +10 -1
  107. data/lib/evilution/mutator/operator/method_body_to_raise.rb +59 -0
  108. data/lib/evilution/mutator/operator/method_body_to_super.rb +150 -0
  109. data/lib/evilution/mutator/operator/optional_default_injection.rb +71 -0
  110. data/lib/evilution/mutator/operator/optional_parameter_to_required.rb +41 -0
  111. data/lib/evilution/mutator/operator/proc_to_lambda.rb +43 -0
  112. data/lib/evilution/mutator/operator/receiver_constructor_swap.rb +51 -0
  113. data/lib/evilution/mutator/operator/reduce_to_sum.rb +58 -0
  114. data/lib/evilution/mutator/operator/regexp_anchor_to_predicate.rb +136 -0
  115. data/lib/evilution/mutator/operator/safe_navigation_removal.rb +67 -0
  116. data/lib/evilution/mutator/operator/send_mutation.rb +42 -6
  117. data/lib/evilution/mutator/operator/symbol_to_proc_replacement.rb +59 -0
  118. data/lib/evilution/mutator/operator/to_i_to_integer.rb +38 -0
  119. data/lib/evilution/mutator/operator/typed_default_return.rb +84 -0
  120. data/lib/evilution/mutator/primitives.rb +25 -0
  121. data/lib/evilution/mutator/registry.rb +32 -1
  122. data/lib/evilution/parallel/pool.rb +1 -0
  123. data/lib/evilution/parallel_db_warning.rb +1 -1
  124. data/lib/evilution/process_supervisor.rb +20 -8
  125. data/lib/evilution/rails_warmup.rb +61 -0
  126. data/lib/evilution/reporter/cli/item_formatters/neutral_group.rb +23 -0
  127. data/lib/evilution/reporter/cli/item_formatters/subject_score.rb +42 -0
  128. data/lib/evilution/reporter/cli/item_formatters/subject_score_group.rb +16 -0
  129. data/lib/evilution/reporter/cli/line_formatters/infra_retry_notice.rb +19 -0
  130. data/lib/evilution/reporter/cli/line_formatters/result_line.rb +27 -3
  131. data/lib/evilution/reporter/cli/line_formatters/score.rb +19 -1
  132. data/lib/evilution/reporter/cli/line_formatters/unresolved_targets.rb +35 -0
  133. data/lib/evilution/reporter/cli/metrics_block.rb +4 -0
  134. data/lib/evilution/reporter/cli/trailer.rb +11 -7
  135. data/lib/evilution/reporter/cli.rb +20 -4
  136. data/lib/evilution/reporter/html/report.rb +1 -0
  137. data/lib/evilution/reporter/json/subjects.rb +29 -0
  138. data/lib/evilution/reporter/json.rb +23 -1
  139. data/lib/evilution/result/coverage_gap_grouper.rb +1 -0
  140. data/lib/evilution/result/mutation_result.rb +3 -2
  141. data/lib/evilution/result/neutral_reason.rb +35 -0
  142. data/lib/evilution/result/subject_score.rb +28 -0
  143. data/lib/evilution/result/subject_scorer.rb +38 -0
  144. data/lib/evilution/result/summary.rb +44 -2
  145. data/lib/evilution/runner/baseline_runner.rb +17 -7
  146. data/lib/evilution/runner/canary.rb +52 -5
  147. data/lib/evilution/runner/isolation_resolver.rb +23 -2
  148. data/lib/evilution/runner/mutation_executor/infra_retry.rb +54 -0
  149. data/lib/evilution/runner/mutation_executor/neutralizer/baseline_failed.rb +23 -10
  150. data/lib/evilution/runner/mutation_executor/neutralizer/infra_error.rb +18 -1
  151. data/lib/evilution/runner/mutation_executor/result_cache.rb +11 -0
  152. data/lib/evilution/runner/mutation_executor/strategy/parallel.rb +19 -1
  153. data/lib/evilution/runner/mutation_executor.rb +32 -4
  154. data/lib/evilution/runner/report_publisher.rb +31 -9
  155. data/lib/evilution/runner/target_spec_audit.rb +43 -0
  156. data/lib/evilution/runner.rb +10 -1
  157. data/lib/evilution/source_ast_cache.rb +1 -1
  158. data/lib/evilution/spec_ast_cache.rb +1 -1
  159. data/lib/evilution/version.rb +1 -1
  160. data/lib/evilution.rb +35 -0
  161. metadata +50 -2
@@ -3,13 +3,18 @@
3
3
  require_relative "../runner"
4
4
 
5
5
  class Evilution::Runner::MutationExecutor
6
- ExecutionResult = Data.define(:results, :truncated)
6
+ ExecutionResult = Data.define(:results, :truncated, :infra_retried) do
7
+ def initialize(results:, truncated:, infra_retried: 0)
8
+ super
9
+ end
10
+ end
7
11
 
8
12
  autoload :ResultCache, File.expand_path("mutation_executor/result_cache", __dir__)
9
13
  autoload :ResultPacker, File.expand_path("mutation_executor/result_packer", __dir__)
10
14
  autoload :ResultNotifier, File.expand_path("mutation_executor/result_notifier", __dir__)
11
15
  autoload :MutationRunner, File.expand_path("mutation_executor/mutation_runner", __dir__)
12
16
  autoload :NeutralizationPipeline, File.expand_path("mutation_executor/neutralization_pipeline", __dir__)
17
+ autoload :InfraRetry, File.expand_path("mutation_executor/infra_retry", __dir__)
13
18
  autoload :Strategy, File.expand_path("mutation_executor/strategy", __dir__)
14
19
  autoload :Neutralizer, File.expand_path("mutation_executor/neutralizer", __dir__)
15
20
 
@@ -29,13 +34,36 @@ class Evilution::Runner::MutationExecutor
29
34
  spec_resolver = baseline_failed?(baseline_result) ? @baseline_runner.neutralization_resolver : nil
30
35
  notifier = build_notifier
31
36
  pipeline = build_pipeline(spec_resolver)
32
- strategy = @config.jobs > 1 ? build_parallel(notifier, pipeline) : build_sequential(notifier, pipeline)
37
+ return sequential_run(notifier, pipeline, mutations, baseline_result, integration) if @config.jobs <= 1
33
38
 
34
- strategy.call(mutations, baseline_result: baseline_result, integration: integration)
39
+ parallel_run(notifier, pipeline, mutations, baseline_result, integration)
35
40
  end
36
41
 
37
42
  private
38
43
 
44
+ def sequential_run(notifier, pipeline, mutations, baseline_result, integration)
45
+ build_sequential(notifier, pipeline)
46
+ .call(mutations, baseline_result: baseline_result, integration: integration)
47
+ end
48
+
49
+ # Infrastructure contention only exists while the pool is running, so the
50
+ # mutations it left without a verdict are re-run once it is done. Without
51
+ # this the neutral bucket swings with --jobs on identical input
52
+ # (EV-j0bv / GH #1607).
53
+ def parallel_run(notifier, pipeline, mutations, baseline_result, integration)
54
+ execution = build_parallel(notifier, pipeline)
55
+ .call(mutations, baseline_result: baseline_result, integration: integration)
56
+ retry_pass = InfraRetry.new(runner: build_mutation_runner, pipeline: pipeline)
57
+ results = retry_pass.call(execution.results, baseline_result: baseline_result, integration: integration)
58
+
59
+ ExecutionResult.new(results: results, truncated: execution.truncated,
60
+ infra_retried: retry_pass.retried_count)
61
+ end
62
+
63
+ def build_mutation_runner
64
+ MutationRunner.new(config: @config, cache: @cache, isolator: @isolator)
65
+ end
66
+
39
67
  def baseline_failed?(baseline_result)
40
68
  baseline_result && baseline_result.failed?
41
69
  end
@@ -59,7 +87,7 @@ class Evilution::Runner::MutationExecutor
59
87
 
60
88
  def build_sequential(notifier, pipeline)
61
89
  Strategy::Sequential.new(
62
- runner: MutationRunner.new(config: @config, cache: @cache, isolator: @isolator),
90
+ runner: build_mutation_runner,
63
91
  pipeline: pipeline,
64
92
  notifier: notifier
65
93
  )
@@ -11,20 +11,42 @@ class Evilution::Runner::ReportPublisher
11
11
  @config = config
12
12
  end
13
13
 
14
- def publish(summary)
14
+ def publish(summary, stdout: $stdout)
15
15
  reporter = build_reporter
16
16
  return unless reporter
17
17
 
18
18
  output = reporter.call(summary)
19
19
  return if config.quiet
20
20
 
21
- if config.html?
22
- path = "evilution-report.html"
23
- File.write(path, output)
24
- warn "HTML report written to #{path}"
25
- else
26
- $stdout.puts(output)
27
- end
21
+ deliver(output, stdout)
22
+ end
23
+
24
+ def deliver(output, stdout)
25
+ return publish_html(output) if config.html?
26
+ return File.write(config.output, output) if config.output
27
+
28
+ stdout.puts(output)
29
+ claim_stdout(stdout) if config.json?
30
+ end
31
+
32
+ # `--preload` brings the project's at_exit hooks into this process, and
33
+ # SimpleCov's prints its coverage report on the way out. Once the document is
34
+ # written, stdout is pointed at stderr so nothing can append to it and leave a
35
+ # consumer with unparseable JSON (EV-g8ya / GH #1608). Text reports are read
36
+ # by people, not parsers, so they keep the stream as it was.
37
+ def claim_stdout(stdout)
38
+ stdout.flush
39
+ stdout.reopen($stderr)
40
+ rescue TypeError
41
+ # A harness that captures output swaps $stdout for a StringIO, which cannot
42
+ # be reopened onto an IO. There is no file descriptor to protect there.
43
+ nil
44
+ end
45
+
46
+ def publish_html(output)
47
+ path = "evilution-report.html"
48
+ File.write(path, output)
49
+ warn "HTML report written to #{path}"
28
50
  end
29
51
 
30
52
  def save_session(summary)
@@ -44,7 +66,7 @@ class Evilution::Runner::ReportPublisher
44
66
  when :json
45
67
  Evilution::Reporter::JSON.new(integration: config.integration)
46
68
  when :text
47
- Evilution::Reporter::CLI.new
69
+ Evilution::Reporter::CLI.new(min_score: config.min_score)
48
70
  when :html
49
71
  Evilution::Reporter::HTML.new(baseline: load_baseline_session, integration: config.integration)
50
72
  end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../runner"
4
+
5
+ # Answers, in the parent process and before any mutation runs, which of the
6
+ # files evilution was pointed at resolve to no test file at all.
7
+ #
8
+ # The integrations resolve specs per mutation inside the worker, which is what
9
+ # produces `:unresolved` results, but that is a per-mutation fact diluted by
10
+ # every other file in the run: a target with no spec at all contributes a
11
+ # handful of unresolved mutations and disappears behind a healthy score
12
+ # (EV-p4sm / GH #1603). Asking the same selector once per file, up front, keeps
13
+ # the stronger fact — "you named this file and it was never tested" — intact.
14
+ #
15
+ # Only files that produced a subject are audited: a file evilution found
16
+ # nothing to mutate in — a constants-only file, say — has no untested behaviour
17
+ # to report, and failing a run over one would be noise.
18
+ #
19
+ # With `fallback_to_full_suite` a file without its own spec is run against the
20
+ # whole suite rather than skipped, so nothing goes untested and there is
21
+ # nothing to report.
22
+ class Evilution::Runner::TargetSpecAudit
23
+ def initialize(config)
24
+ @config = config
25
+ end
26
+
27
+ def call(file_paths)
28
+ return [] if @config.fallback_to_full_suite?
29
+
30
+ file_paths.uniq.sort.reject { |path| resolved?(path) }
31
+ end
32
+
33
+ private
34
+
35
+ # SpecSelector answers nil when nothing resolves; a custom resolver plugged
36
+ # in through it may answer an empty list instead.
37
+ def resolved?(path)
38
+ specs = @config.spec_selector.call(path)
39
+ return false if specs.nil?
40
+
41
+ !specs.empty?
42
+ end
43
+ end
@@ -46,9 +46,13 @@ class Evilution::Runner
46
46
 
47
47
  duration = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time
48
48
 
49
+ audited_files = subjects.map(&:file_path).uniq.sort
49
50
  summary = Evilution::Result::Summary.new(results: results, duration: duration, truncated: execution.truncated,
50
51
  skipped: plan.skipped_count,
51
- disabled_mutations: plan.disabled_mutations)
52
+ disabled_mutations: plan.disabled_mutations,
53
+ unresolved_target_files: target_spec_audit.call(audited_files),
54
+ target_file_count: audited_files.length,
55
+ infra_retried: execution.infra_retried)
52
56
  output_report(summary)
53
57
  save_session(summary)
54
58
 
@@ -71,6 +75,10 @@ class Evilution::Runner
71
75
  @mutation_planner ||= Evilution::Runner::MutationPlanner.new(config, registry: registry)
72
76
  end
73
77
 
78
+ def target_spec_audit
79
+ @target_spec_audit ||= Evilution::Runner::TargetSpecAudit.new(config)
80
+ end
81
+
74
82
  def isolation_resolver
75
83
  @isolation_resolver ||= Evilution::Runner::IsolationResolver.new(
76
84
  config,
@@ -240,6 +248,7 @@ require_relative "rails_detector"
240
248
  require_relative "parallel_db_warning"
241
249
  require_relative "child_output"
242
250
  require_relative "runner/subject_pipeline"
251
+ require_relative "runner/target_spec_audit"
243
252
  require_relative "runner/mutation_planner"
244
253
  require_relative "runner/isolation_resolver"
245
254
  require_relative "runner/baseline_runner"
@@ -2,7 +2,7 @@
2
2
 
3
3
  require "digest"
4
4
  require "prism"
5
- require_relative "../evilution"
5
+ require_relative "version"
6
6
 
7
7
  # Content-hash-keyed LRU of Prism::ParseResult. Different source bytes always
8
8
  # yield a different key, so the cache is valid for the lifetime of the process.
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "prism"
4
- require_relative "../evilution"
4
+ require_relative "version"
5
5
 
6
6
  class Evilution::SpecAstCache
7
7
  Block = Struct.new(:kind, :line, :end_line, :body_text)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Evilution
4
- VERSION = "1.1.0"
4
+ VERSION = "1.3.0"
5
5
  end
data/lib/evilution.rb CHANGED
@@ -13,6 +13,7 @@ require_relative "evilution/ast/parser"
13
13
  require_relative "evilution/ast/inheritance_scanner"
14
14
  require_relative "evilution/ast/pattern"
15
15
  require_relative "evilution/ast/sorbet_sig_detector"
16
+ require_relative "evilution/ast/local_reads"
16
17
  require_relative "evilution/ast/pattern/matcher"
17
18
  require_relative "evilution/ast/pattern/parser"
18
19
  require_relative "evilution/hooks"
@@ -29,6 +30,29 @@ require_relative "evilution/mutator/operator/nil_replacement"
29
30
  require_relative "evilution/mutator/operator/boolean_operator_replacement"
30
31
  require_relative "evilution/mutator/operator/boolean_operand_promotion"
31
32
  require_relative "evilution/mutator/operator/boolean_expression_to_nil"
33
+ require_relative "evilution/mutator/operator/call_to_nil"
34
+ require_relative "evilution/mutator/operator/safe_navigation_removal"
35
+ require_relative "evilution/mutator/operator/attribute_write_to_read"
36
+ require_relative "evilution/mutator/operator/argument_propagation"
37
+ require_relative "evilution/mutator/operator/argument_list_removal"
38
+ require_relative "evilution/mutator/operator/symbol_to_proc_replacement"
39
+ require_relative "evilution/mutator/operator/dynamic_dispatch_resolution"
40
+ require_relative "evilution/mutator/operator/keyword_argument_removal"
41
+ require_relative "evilution/mutator/operator/double_negation_removal"
42
+ require_relative "evilution/mutator/operator/regexp_anchor_to_predicate"
43
+ require_relative "evilution/mutator/operator/reduce_to_sum"
44
+ require_relative "evilution/mutator/operator/array_coercion_to_literal"
45
+ require_relative "evilution/mutator/operator/to_i_to_integer"
46
+ require_relative "evilution/mutator/operator/coercion_emptying"
47
+ require_relative "evilution/mutator/operator/const_get_to_constant_path"
48
+ require_relative "evilution/mutator/operator/proc_to_lambda"
49
+ require_relative "evilution/mutator/operator/dig_to_fetch_chain"
50
+ require_relative "evilution/mutator/operator/inequality_to_negated_identity"
51
+ require_relative "evilution/mutator/operator/binary_operand_promotion"
52
+ require_relative "evilution/mutator/operator/receiver_constructor_swap"
53
+ require_relative "evilution/mutator/operator/block_body_to_nil"
54
+ require_relative "evilution/mutator/operator/block_body_to_raise"
55
+ require_relative "evilution/mutator/operator/block_body_promotion"
32
56
  require_relative "evilution/mutator/operator/arithmetic_replacement"
33
57
  require_relative "evilution/mutator/operator/string_literal"
34
58
  require_relative "evilution/mutator/operator/array_literal"
@@ -89,6 +113,14 @@ require_relative "evilution/mutator/operator/defined_check"
89
113
  require_relative "evilution/mutator/operator/regex_capture"
90
114
  require_relative "evilution/mutator/operator/loop_flip"
91
115
  require_relative "evilution/mutator/operator/loop_body_to_raise"
116
+ require_relative "evilution/mutator/operator/method_body_to_raise"
117
+ require_relative "evilution/mutator/operator/method_body_to_super"
118
+ require_relative "evilution/mutator/operator/typed_default_return"
119
+ require_relative "evilution/mutator/operator/block_parameter_drop"
120
+ require_relative "evilution/mutator/operator/optional_parameter_to_required"
121
+ require_relative "evilution/mutator/operator/optional_default_injection"
122
+ require_relative "evilution/mutator/operator/block_destructuring_expansion"
123
+ require_relative "evilution/mutator/operator/forwarding_super_to_explicit"
92
124
  require_relative "evilution/mutator/operator/string_interpolation"
93
125
  require_relative "evilution/mutator/operator/retry_removal"
94
126
  require_relative "evilution/mutator/operator/case_when"
@@ -121,6 +153,9 @@ require_relative "evilution/integration/base"
121
153
  require_relative "evilution/integration/rspec"
122
154
  require_relative "evilution/integration/minitest"
123
155
  require_relative "evilution/result/mutation_result"
156
+ require_relative "evilution/result/neutral_reason"
157
+ require_relative "evilution/result/subject_score"
158
+ require_relative "evilution/result/subject_scorer"
124
159
  require_relative "evilution/result/summary"
125
160
  require_relative "evilution/reporter"
126
161
  require_relative "evilution/reporter/json"
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: 1.1.0
4
+ version: 1.3.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-08-23 00:00:00.000000000 Z
11
+ date: 2026-09-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: diff-lcs
@@ -123,6 +123,7 @@ files:
123
123
  - lib/evilution/ast/constant_names.rb
124
124
  - lib/evilution/ast/heredoc_span.rb
125
125
  - lib/evilution/ast/inheritance_scanner.rb
126
+ - lib/evilution/ast/local_reads.rb
126
127
  - lib/evilution/ast/parser.rb
127
128
  - lib/evilution/ast/pattern.rb
128
129
  - lib/evilution/ast/pattern/filter.rb
@@ -150,6 +151,7 @@ files:
150
151
  - lib/evilution/cli/commands/util_mutation.rb
151
152
  - lib/evilution/cli/commands/version.rb
152
153
  - lib/evilution/cli/dispatcher.rb
154
+ - lib/evilution/cli/exit_guard.rb
153
155
  - lib/evilution/cli/parsed_args.rb
154
156
  - lib/evilution/cli/parser.rb
155
157
  - lib/evilution/cli/parser/command_extractor.rb
@@ -199,6 +201,7 @@ files:
199
201
  - lib/evilution/config/validators/profile.rb
200
202
  - lib/evilution/config/validators/spec_mappings.rb
201
203
  - lib/evilution/config/validators/spec_pattern.rb
204
+ - lib/evilution/config/validators/warmup.rb
202
205
  - lib/evilution/coverage.rb
203
206
  - lib/evilution/coverage/digest.rb
204
207
  - lib/evilution/coverage/map.rb
@@ -206,6 +209,7 @@ files:
206
209
  - lib/evilution/coverage/map_store.rb
207
210
  - lib/evilution/coverage/recorder.rb
208
211
  - lib/evilution/coverage_example_filter.rb
212
+ - lib/evilution/diagnostic.rb
209
213
  - lib/evilution/disable_comment.rb
210
214
  - lib/evilution/equivalent.rb
211
215
  - lib/evilution/equivalent/detector.rb
@@ -237,6 +241,7 @@ files:
237
241
  - lib/evilution/integration/loading/constant_pinner.rb
238
242
  - lib/evilution/integration/loading/mutation_applier.rb
239
243
  - lib/evilution/integration/loading/redefinition_recovery.rb
244
+ - lib/evilution/integration/loading/reeval_warning_filter.rb
240
245
  - lib/evilution/integration/loading/source_evaluator.rb
241
246
  - lib/evilution/integration/loading/syntax_validator.rb
242
247
  - lib/evilution/integration/loading/test_load_path.rb
@@ -302,16 +307,26 @@ files:
302
307
  - lib/evilution/mutator.rb
303
308
  - lib/evilution/mutator/base.rb
304
309
  - lib/evilution/mutator/operator.rb
310
+ - lib/evilution/mutator/operator/argument_list_removal.rb
305
311
  - lib/evilution/mutator/operator/argument_method_call_replacement.rb
306
312
  - lib/evilution/mutator/operator/argument_nil_substitution.rb
313
+ - lib/evilution/mutator/operator/argument_propagation.rb
307
314
  - lib/evilution/mutator/operator/argument_removal.rb
308
315
  - lib/evilution/mutator/operator/arithmetic_replacement.rb
316
+ - lib/evilution/mutator/operator/array_coercion_to_literal.rb
309
317
  - lib/evilution/mutator/operator/array_literal.rb
318
+ - lib/evilution/mutator/operator/attribute_write_to_read.rb
310
319
  - lib/evilution/mutator/operator/bang_method.rb
311
320
  - lib/evilution/mutator/operator/begin_unwrap.rb
321
+ - lib/evilution/mutator/operator/binary_operand_promotion.rb
312
322
  - lib/evilution/mutator/operator/bitwise_complement.rb
313
323
  - lib/evilution/mutator/operator/bitwise_replacement.rb
324
+ - lib/evilution/mutator/operator/block_body_promotion.rb
325
+ - lib/evilution/mutator/operator/block_body_to_nil.rb
326
+ - lib/evilution/mutator/operator/block_body_to_raise.rb
327
+ - lib/evilution/mutator/operator/block_destructuring_expansion.rb
314
328
  - lib/evilution/mutator/operator/block_param_removal.rb
329
+ - lib/evilution/mutator/operator/block_parameter_drop.rb
315
330
  - lib/evilution/mutator/operator/block_pass_removal.rb
316
331
  - lib/evilution/mutator/operator/block_removal.rb
317
332
  - lib/evilution/mutator/operator/boolean_expression_to_nil.rb
@@ -319,9 +334,11 @@ files:
319
334
  - lib/evilution/mutator/operator/boolean_operand_promotion.rb
320
335
  - lib/evilution/mutator/operator/boolean_operator_replacement.rb
321
336
  - lib/evilution/mutator/operator/break_statement.rb
337
+ - lib/evilution/mutator/operator/call_to_nil.rb
322
338
  - lib/evilution/mutator/operator/case_in.rb
323
339
  - lib/evilution/mutator/operator/case_when.rb
324
340
  - lib/evilution/mutator/operator/class_variable_write.rb
341
+ - lib/evilution/mutator/operator/coercion_emptying.rb
325
342
  - lib/evilution/mutator/operator/collection_replacement.rb
326
343
  - lib/evilution/mutator/operator/collection_return.rb
327
344
  - lib/evilution/mutator/operator/comparison_replacement.rb
@@ -329,11 +346,16 @@ files:
329
346
  - lib/evilution/mutator/operator/conditional_branch.rb
330
347
  - lib/evilution/mutator/operator/conditional_flip.rb
331
348
  - lib/evilution/mutator/operator/conditional_negation.rb
349
+ - lib/evilution/mutator/operator/const_get_to_constant_path.rb
332
350
  - lib/evilution/mutator/operator/defined_check.rb
351
+ - lib/evilution/mutator/operator/dig_to_fetch_chain.rb
352
+ - lib/evilution/mutator/operator/double_negation_removal.rb
353
+ - lib/evilution/mutator/operator/dynamic_dispatch_resolution.rb
333
354
  - lib/evilution/mutator/operator/ensure_removal.rb
334
355
  - lib/evilution/mutator/operator/equality_to_identity.rb
335
356
  - lib/evilution/mutator/operator/explicit_super_mutation.rb
336
357
  - lib/evilution/mutator/operator/float_literal.rb
358
+ - lib/evilution/mutator/operator/forwarding_super_to_explicit.rb
337
359
  - lib/evilution/mutator/operator/global_variable_write.rb
338
360
  - lib/evilution/mutator/operator/hash_literal.rb
339
361
  - lib/evilution/mutator/operator/if_branch_swap.rb
@@ -341,38 +363,49 @@ files:
341
363
  - lib/evilution/mutator/operator/index_to_at.rb
342
364
  - lib/evilution/mutator/operator/index_to_dig.rb
343
365
  - lib/evilution/mutator/operator/index_to_fetch.rb
366
+ - lib/evilution/mutator/operator/inequality_to_negated_identity.rb
344
367
  - lib/evilution/mutator/operator/inline_rescue.rb
345
368
  - lib/evilution/mutator/operator/instance_variable_write.rb
346
369
  - lib/evilution/mutator/operator/integer_literal.rb
347
370
  - lib/evilution/mutator/operator/keyword_argument.rb
371
+ - lib/evilution/mutator/operator/keyword_argument_removal.rb
348
372
  - lib/evilution/mutator/operator/lambda_body.rb
349
373
  - lib/evilution/mutator/operator/last_expression_removal.rb
350
374
  - lib/evilution/mutator/operator/local_variable_assignment.rb
351
375
  - lib/evilution/mutator/operator/loop_body_to_raise.rb
352
376
  - lib/evilution/mutator/operator/loop_flip.rb
353
377
  - lib/evilution/mutator/operator/method_body_replacement.rb
378
+ - lib/evilution/mutator/operator/method_body_to_raise.rb
379
+ - lib/evilution/mutator/operator/method_body_to_super.rb
354
380
  - lib/evilution/mutator/operator/method_call_removal.rb
355
381
  - lib/evilution/mutator/operator/mixin_removal.rb
356
382
  - lib/evilution/mutator/operator/multiple_assignment.rb
357
383
  - lib/evilution/mutator/operator/negation_insertion.rb
358
384
  - lib/evilution/mutator/operator/next_statement.rb
359
385
  - lib/evilution/mutator/operator/nil_replacement.rb
386
+ - lib/evilution/mutator/operator/optional_default_injection.rb
387
+ - lib/evilution/mutator/operator/optional_parameter_to_required.rb
360
388
  - lib/evilution/mutator/operator/pattern_matching_alternative.rb
361
389
  - lib/evilution/mutator/operator/pattern_matching_array.rb
362
390
  - lib/evilution/mutator/operator/pattern_matching_guard.rb
363
391
  - lib/evilution/mutator/operator/pattern_predicate.rb
364
392
  - lib/evilution/mutator/operator/predicate_replacement.rb
365
393
  - lib/evilution/mutator/operator/predicate_to_nil.rb
394
+ - lib/evilution/mutator/operator/proc_to_lambda.rb
366
395
  - lib/evilution/mutator/operator/range_replacement.rb
396
+ - lib/evilution/mutator/operator/receiver_constructor_swap.rb
367
397
  - lib/evilution/mutator/operator/receiver_replacement.rb
368
398
  - lib/evilution/mutator/operator/redo_statement.rb
399
+ - lib/evilution/mutator/operator/reduce_to_sum.rb
369
400
  - lib/evilution/mutator/operator/regex_capture.rb
370
401
  - lib/evilution/mutator/operator/regex_simplification.rb
402
+ - lib/evilution/mutator/operator/regexp_anchor_to_predicate.rb
371
403
  - lib/evilution/mutator/operator/regexp_mutation.rb
372
404
  - lib/evilution/mutator/operator/rescue_body_replacement.rb
373
405
  - lib/evilution/mutator/operator/rescue_removal.rb
374
406
  - lib/evilution/mutator/operator/retry_removal.rb
375
407
  - lib/evilution/mutator/operator/return_value_removal.rb
408
+ - lib/evilution/mutator/operator/safe_navigation_removal.rb
376
409
  - lib/evilution/mutator/operator/scalar_return.rb
377
410
  - lib/evilution/mutator/operator/send_mutation.rb
378
411
  - lib/evilution/mutator/operator/splat_operator.rb
@@ -381,6 +414,9 @@ files:
381
414
  - lib/evilution/mutator/operator/string_literal.rb
382
415
  - lib/evilution/mutator/operator/superclass_removal.rb
383
416
  - lib/evilution/mutator/operator/symbol_literal.rb
417
+ - lib/evilution/mutator/operator/symbol_to_proc_replacement.rb
418
+ - lib/evilution/mutator/operator/to_i_to_integer.rb
419
+ - lib/evilution/mutator/operator/typed_default_return.rb
384
420
  - lib/evilution/mutator/operator/yield_statement.rb
385
421
  - lib/evilution/mutator/operator/zsuper_removal.rb
386
422
  - lib/evilution/mutator/primitives.rb
@@ -404,6 +440,7 @@ files:
404
440
  - lib/evilution/process_cleanup.rb
405
441
  - lib/evilution/process_supervisor.rb
406
442
  - lib/evilution/rails_detector.rb
443
+ - lib/evilution/rails_warmup.rb
407
444
  - lib/evilution/related_spec_heuristic.rb
408
445
  - lib/evilution/reporter.rb
409
446
  - lib/evilution/reporter/cli.rb
@@ -411,19 +448,24 @@ files:
411
448
  - lib/evilution/reporter/cli/item_formatters/coverage_gap.rb
412
449
  - lib/evilution/reporter/cli/item_formatters/disabled.rb
413
450
  - lib/evilution/reporter/cli/item_formatters/error.rb
451
+ - lib/evilution/reporter/cli/item_formatters/neutral_group.rb
414
452
  - lib/evilution/reporter/cli/item_formatters/result_location.rb
453
+ - lib/evilution/reporter/cli/item_formatters/subject_score.rb
454
+ - lib/evilution/reporter/cli/item_formatters/subject_score_group.rb
415
455
  - lib/evilution/reporter/cli/line_formatters.rb
416
456
  - lib/evilution/reporter/cli/line_formatters/duration.rb
417
457
  - lib/evilution/reporter/cli/line_formatters/efficiency.rb
418
458
  - lib/evilution/reporter/cli/line_formatters/error_rate_warning.rb
419
459
  - lib/evilution/reporter/cli/line_formatters/feedback_footer.rb
420
460
  - lib/evilution/reporter/cli/line_formatters/header.rb
461
+ - lib/evilution/reporter/cli/line_formatters/infra_retry_notice.rb
421
462
  - lib/evilution/reporter/cli/line_formatters/mutations.rb
422
463
  - lib/evilution/reporter/cli/line_formatters/peak_memory.rb
423
464
  - lib/evilution/reporter/cli/line_formatters/result_line.rb
424
465
  - lib/evilution/reporter/cli/line_formatters/score.rb
425
466
  - lib/evilution/reporter/cli/line_formatters/truncation_notice.rb
426
467
  - lib/evilution/reporter/cli/line_formatters/unresolved_rate_warning.rb
468
+ - lib/evilution/reporter/cli/line_formatters/unresolved_targets.rb
427
469
  - lib/evilution/reporter/cli/metrics_block.rb
428
470
  - lib/evilution/reporter/cli/pct.rb
429
471
  - lib/evilution/reporter/cli/section.rb
@@ -466,6 +508,7 @@ files:
466
508
  - lib/evilution/reporter/html/templates/unparseable_details.html.erb
467
509
  - lib/evilution/reporter/html/templates/unresolved_details.html.erb
468
510
  - lib/evilution/reporter/json.rb
511
+ - lib/evilution/reporter/json/subjects.rb
469
512
  - lib/evilution/reporter/progress_bar.rb
470
513
  - lib/evilution/reporter/suggestion.rb
471
514
  - lib/evilution/reporter/suggestion/diff_helpers.rb
@@ -481,6 +524,9 @@ files:
481
524
  - lib/evilution/result/error_info.rb
482
525
  - lib/evilution/result/memory_stats.rb
483
526
  - lib/evilution/result/mutation_result.rb
527
+ - lib/evilution/result/neutral_reason.rb
528
+ - lib/evilution/result/subject_score.rb
529
+ - lib/evilution/result/subject_scorer.rb
484
530
  - lib/evilution/result/summary.rb
485
531
  - lib/evilution/runner.rb
486
532
  - lib/evilution/runner/baseline_runner.rb
@@ -488,6 +534,7 @@ files:
488
534
  - lib/evilution/runner/diagnostics.rb
489
535
  - lib/evilution/runner/isolation_resolver.rb
490
536
  - lib/evilution/runner/mutation_executor.rb
537
+ - lib/evilution/runner/mutation_executor/infra_retry.rb
491
538
  - lib/evilution/runner/mutation_executor/mutation_runner.rb
492
539
  - lib/evilution/runner/mutation_executor/neutralization_pipeline.rb
493
540
  - lib/evilution/runner/mutation_executor/neutralizer.rb
@@ -502,6 +549,7 @@ files:
502
549
  - lib/evilution/runner/mutation_planner.rb
503
550
  - lib/evilution/runner/report_publisher.rb
504
551
  - lib/evilution/runner/subject_pipeline.rb
552
+ - lib/evilution/runner/target_spec_audit.rb
505
553
  - lib/evilution/session.rb
506
554
  - lib/evilution/session/diff.rb
507
555
  - lib/evilution/session/schema.rb