evilution 1.1.0 → 1.2.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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/.beads/interactions.jsonl +20 -0
  3. data/CHANGELOG.md +39 -0
  4. data/README.md +141 -11
  5. data/docs/architecture.md +20 -5
  6. data/docs/isolation.md +3 -4
  7. data/exe/evil +8 -1
  8. data/exe/evilution +8 -1
  9. data/lib/evilution/ast/local_reads.rb +43 -0
  10. data/lib/evilution/cli/exit_guard.rb +59 -0
  11. data/lib/evilution/cli/parser/options_builder.rb +1 -0
  12. data/lib/evilution/cli.rb +1 -0
  13. data/lib/evilution/config.rb +3 -2
  14. data/lib/evilution/diagnostic.rb +22 -0
  15. data/lib/evilution/integration/loading/concern_state_cleaner.rb +20 -4
  16. data/lib/evilution/integration/loading/reeval_warning_filter.rb +72 -0
  17. data/lib/evilution/integration/loading/source_evaluator.rb +4 -1
  18. data/lib/evilution/integration/minitest.rb +2 -1
  19. data/lib/evilution/integration/rspec/crash_detector_lifecycle.rb +9 -1
  20. data/lib/evilution/integration/rspec/state_guard/configuration_streams.rb +4 -1
  21. data/lib/evilution/integration/rspec/unresolved_spec_warner.rb +2 -1
  22. data/lib/evilution/integration/rspec.rb +48 -2
  23. data/lib/evilution/integration/test_unit/test_file_resolver.rb +2 -1
  24. data/lib/evilution/isolation/fork.rb +29 -8
  25. data/lib/evilution/mutator/operator/block_destructuring_expansion.rb +85 -0
  26. data/lib/evilution/mutator/operator/block_parameter_drop.rb +96 -0
  27. data/lib/evilution/mutator/operator/forwarding_super_to_explicit.rb +71 -0
  28. data/lib/evilution/mutator/operator/method_body_replacement.rb +10 -1
  29. data/lib/evilution/mutator/operator/method_body_to_raise.rb +59 -0
  30. data/lib/evilution/mutator/operator/method_body_to_super.rb +150 -0
  31. data/lib/evilution/mutator/operator/optional_default_injection.rb +71 -0
  32. data/lib/evilution/mutator/operator/optional_parameter_to_required.rb +41 -0
  33. data/lib/evilution/mutator/operator/typed_default_return.rb +84 -0
  34. data/lib/evilution/mutator/registry.rb +8 -0
  35. data/lib/evilution/process_supervisor.rb +20 -8
  36. data/lib/evilution/reporter/cli/item_formatters/neutral_group.rb +23 -0
  37. data/lib/evilution/reporter/cli/item_formatters/subject_score.rb +42 -0
  38. data/lib/evilution/reporter/cli/item_formatters/subject_score_group.rb +16 -0
  39. data/lib/evilution/reporter/cli/line_formatters/infra_retry_notice.rb +19 -0
  40. data/lib/evilution/reporter/cli/line_formatters/result_line.rb +27 -3
  41. data/lib/evilution/reporter/cli/line_formatters/score.rb +19 -1
  42. data/lib/evilution/reporter/cli/line_formatters/unresolved_targets.rb +35 -0
  43. data/lib/evilution/reporter/cli/metrics_block.rb +4 -0
  44. data/lib/evilution/reporter/cli/trailer.rb +11 -7
  45. data/lib/evilution/reporter/cli.rb +20 -4
  46. data/lib/evilution/reporter/json/subjects.rb +29 -0
  47. data/lib/evilution/reporter/json.rb +23 -1
  48. data/lib/evilution/result/mutation_result.rb +3 -2
  49. data/lib/evilution/result/neutral_reason.rb +35 -0
  50. data/lib/evilution/result/subject_score.rb +28 -0
  51. data/lib/evilution/result/subject_scorer.rb +37 -0
  52. data/lib/evilution/result/summary.rb +44 -2
  53. data/lib/evilution/runner/canary.rb +52 -5
  54. data/lib/evilution/runner/mutation_executor/infra_retry.rb +54 -0
  55. data/lib/evilution/runner/mutation_executor/neutralizer/baseline_failed.rb +9 -3
  56. data/lib/evilution/runner/mutation_executor/neutralizer/infra_error.rb +18 -1
  57. data/lib/evilution/runner/mutation_executor/result_cache.rb +11 -0
  58. data/lib/evilution/runner/mutation_executor/strategy/parallel.rb +19 -1
  59. data/lib/evilution/runner/mutation_executor.rb +32 -4
  60. data/lib/evilution/runner/report_publisher.rb +31 -9
  61. data/lib/evilution/runner/target_spec_audit.rb +43 -0
  62. data/lib/evilution/runner.rb +10 -1
  63. data/lib/evilution/version.rb +1 -1
  64. data/lib/evilution.rb +12 -0
  65. metadata +25 -2
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative "../neutralizer"
4
4
  require_relative "../../../result/mutation_result"
5
+ require_relative "../../../result/neutral_reason"
5
6
 
6
7
  class Evilution::Runner::MutationExecutor::Neutralizer::BaselineFailed
7
8
  def initialize(config:, spec_resolver:, fallback_dir:)
@@ -15,25 +16,30 @@ class Evilution::Runner::MutationExecutor::Neutralizer::BaselineFailed
15
16
 
16
17
  if @config.spec_files.any?
17
18
  should_neutralize = true
19
+ spec_file = nil
18
20
  else
19
21
  spec_file = @spec_resolver.call(result.mutation.file_path) || @fallback_dir
20
22
  should_neutralize = baseline_result.failed_spec_files.include?(spec_file)
21
23
  end
22
24
  return result unless should_neutralize
23
25
 
24
- neutralize(result)
26
+ neutralize(result, spec_file)
25
27
  end
26
28
 
27
29
  private
28
30
 
29
- def neutralize(result)
31
+ # The spec that was already red is the fact a reader needs: it says what to
32
+ # fix before the run can say anything about these mutations
33
+ # (EV-5pob / GH #1606).
34
+ def neutralize(result, spec_file)
30
35
  Evilution::Result::MutationResult.new(
31
36
  mutation: result.mutation,
32
37
  status: :neutral,
33
38
  duration: result.duration,
34
39
  test_command: result.test_command,
35
40
  memory: result.memory,
36
- error: result.error
41
+ error: result.error,
42
+ neutral_reason: Evilution::Result::NeutralReason.baseline_failure(spec_file)
37
43
  )
38
44
  end
39
45
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative "../neutralizer"
4
4
  require_relative "../../../result/mutation_result"
5
+ require_relative "../../../result/neutral_reason"
5
6
 
6
7
  # Reclassify results as :neutral when the failure was caused by test
7
8
  # infrastructure rather than by the mutation. Two independent paths:
@@ -33,6 +34,21 @@ class Evilution::Runner::MutationExecutor::Neutralizer::InfraError
33
34
  ].freeze
34
35
  private_constant :INFRA_ERROR_CLASSES, :INFRA_BACKTRACE_PATHS, :INFRA_CRASH_CLASSES
35
36
 
37
+ # Whether a crash class is one of the infrastructure failures that say
38
+ # nothing about the mutation. The parallel strategy asks this before the
39
+ # neutralisation pipeline has run, while the result is still a `:killed`
40
+ # crash (EV-j0bv / GH #1607).
41
+ def self.infra_crash_class?(error_class)
42
+ INFRA_CRASH_CLASSES.include?(error_class)
43
+ end
44
+
45
+ # Whether this result is a kill that was demoted because the test process
46
+ # crashed on infrastructure rather than on the mutation. Such a mutation got
47
+ # no verdict, so a parallel run can re-run it once the contention is over.
48
+ def self.infra_neutral?(result)
49
+ result.neutral? && infra_crash_class?(result.error_class)
50
+ end
51
+
36
52
  def call(result, **_ctx)
37
53
  return neutralize(result) if infra_crash?(result)
38
54
  return result unless result.error?
@@ -62,7 +78,8 @@ class Evilution::Runner::MutationExecutor::Neutralizer::InfraError
62
78
  duration: result.duration,
63
79
  test_command: result.test_command,
64
80
  memory: result.memory,
65
- error: result.error
81
+ error: result.error,
82
+ neutral_reason: Evilution::Result::NeutralReason.infra_error(result.error_class)
66
83
  )
67
84
  end
68
85
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative "../mutation_executor"
4
4
  require_relative "../../result/mutation_result"
5
+ require_relative "neutralizer/infra_error"
5
6
 
6
7
  class Evilution::Runner::MutationExecutor::ResultCache
7
8
  CACHEABLE_STATUSES = %i[killed timeout].freeze
@@ -32,6 +33,7 @@ class Evilution::Runner::MutationExecutor::ResultCache
32
33
  def store(mutation, result)
33
34
  return unless @backend
34
35
  return unless result.killed? || result.timeout?
36
+ return if infra_crash?(result)
35
37
 
36
38
  @backend.store(mutation,
37
39
  status: result.status,
@@ -40,6 +42,15 @@ class Evilution::Runner::MutationExecutor::ResultCache
40
42
  test_command: result.test_command)
41
43
  end
42
44
 
45
+ # A kill that came from an infrastructure crash is not a verdict about the
46
+ # mutation, and the cache keeps no error class to tell them apart later. It
47
+ # is left out so the next fetch does not hand back a plain `:killed`, which
48
+ # would defeat both the demotion to neutral and the serial retry
49
+ # (EV-j0bv / GH #1607).
50
+ def infra_crash?(result)
51
+ Evilution::Runner::MutationExecutor::Neutralizer::InfraError.infra_crash_class?(result.error_class)
52
+ end
53
+
43
54
  def partition(batch, packer:)
44
55
  uncached_indices = []
45
56
  cached_results = {}
@@ -65,10 +65,28 @@ class Evilution::Runner::MutationExecutor::Strategy::Parallel
65
65
  compact_results = merge(batch, partition.uncached_indices, partition.cached_results, worker_results)
66
66
  batch_results = rebuild_results(batch, compact_results)
67
67
  cache_results(batch_results, partition.uncached_indices)
68
- batch.each(&:strip_sources!)
68
+ release_sources(batch, batch_results)
69
69
  batch_results
70
70
  end
71
71
 
72
+ # Sources are dropped as soon as a mutation has been judged, to keep a long
73
+ # run's memory flat. A mutation the workers could not judge because of
74
+ # infrastructure contention is re-run serially once the pool is done
75
+ # (InfraRetry), which needs its sources, so it keeps them until then. The
76
+ # neutralisation pipeline has not run yet here, so the crash is still a kill.
77
+ def release_sources(batch, batch_results)
78
+ batch.each_with_index do |mutation, index|
79
+ result = batch_results[index]
80
+ next if result && infra_crash?(result)
81
+
82
+ mutation.strip_sources!
83
+ end
84
+ end
85
+
86
+ def infra_crash?(result)
87
+ Evilution::Runner::MutationExecutor::Neutralizer::InfraError.infra_crash_class?(result.error_class)
88
+ end
89
+
72
90
  def rebuild_results(batch, compact_results)
73
91
  batch.zip(compact_results).map { |m, h| @packer.rebuild(m, h) }
74
92
  end
@@ -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"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Evilution
4
- VERSION = "1.1.0"
4
+ VERSION = "1.2.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"
@@ -89,6 +90,14 @@ require_relative "evilution/mutator/operator/defined_check"
89
90
  require_relative "evilution/mutator/operator/regex_capture"
90
91
  require_relative "evilution/mutator/operator/loop_flip"
91
92
  require_relative "evilution/mutator/operator/loop_body_to_raise"
93
+ require_relative "evilution/mutator/operator/method_body_to_raise"
94
+ require_relative "evilution/mutator/operator/method_body_to_super"
95
+ require_relative "evilution/mutator/operator/typed_default_return"
96
+ require_relative "evilution/mutator/operator/block_parameter_drop"
97
+ require_relative "evilution/mutator/operator/optional_parameter_to_required"
98
+ require_relative "evilution/mutator/operator/optional_default_injection"
99
+ require_relative "evilution/mutator/operator/block_destructuring_expansion"
100
+ require_relative "evilution/mutator/operator/forwarding_super_to_explicit"
92
101
  require_relative "evilution/mutator/operator/string_interpolation"
93
102
  require_relative "evilution/mutator/operator/retry_removal"
94
103
  require_relative "evilution/mutator/operator/case_when"
@@ -121,6 +130,9 @@ require_relative "evilution/integration/base"
121
130
  require_relative "evilution/integration/rspec"
122
131
  require_relative "evilution/integration/minitest"
123
132
  require_relative "evilution/result/mutation_result"
133
+ require_relative "evilution/result/neutral_reason"
134
+ require_relative "evilution/result/subject_score"
135
+ require_relative "evilution/result/subject_scorer"
124
136
  require_relative "evilution/result/summary"
125
137
  require_relative "evilution/reporter"
126
138
  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.2.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-21 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
@@ -206,6 +208,7 @@ files:
206
208
  - lib/evilution/coverage/map_store.rb
207
209
  - lib/evilution/coverage/recorder.rb
208
210
  - lib/evilution/coverage_example_filter.rb
211
+ - lib/evilution/diagnostic.rb
209
212
  - lib/evilution/disable_comment.rb
210
213
  - lib/evilution/equivalent.rb
211
214
  - lib/evilution/equivalent/detector.rb
@@ -237,6 +240,7 @@ files:
237
240
  - lib/evilution/integration/loading/constant_pinner.rb
238
241
  - lib/evilution/integration/loading/mutation_applier.rb
239
242
  - lib/evilution/integration/loading/redefinition_recovery.rb
243
+ - lib/evilution/integration/loading/reeval_warning_filter.rb
240
244
  - lib/evilution/integration/loading/source_evaluator.rb
241
245
  - lib/evilution/integration/loading/syntax_validator.rb
242
246
  - lib/evilution/integration/loading/test_load_path.rb
@@ -311,7 +315,9 @@ files:
311
315
  - lib/evilution/mutator/operator/begin_unwrap.rb
312
316
  - lib/evilution/mutator/operator/bitwise_complement.rb
313
317
  - lib/evilution/mutator/operator/bitwise_replacement.rb
318
+ - lib/evilution/mutator/operator/block_destructuring_expansion.rb
314
319
  - lib/evilution/mutator/operator/block_param_removal.rb
320
+ - lib/evilution/mutator/operator/block_parameter_drop.rb
315
321
  - lib/evilution/mutator/operator/block_pass_removal.rb
316
322
  - lib/evilution/mutator/operator/block_removal.rb
317
323
  - lib/evilution/mutator/operator/boolean_expression_to_nil.rb
@@ -334,6 +340,7 @@ files:
334
340
  - lib/evilution/mutator/operator/equality_to_identity.rb
335
341
  - lib/evilution/mutator/operator/explicit_super_mutation.rb
336
342
  - lib/evilution/mutator/operator/float_literal.rb
343
+ - lib/evilution/mutator/operator/forwarding_super_to_explicit.rb
337
344
  - lib/evilution/mutator/operator/global_variable_write.rb
338
345
  - lib/evilution/mutator/operator/hash_literal.rb
339
346
  - lib/evilution/mutator/operator/if_branch_swap.rb
@@ -351,12 +358,16 @@ files:
351
358
  - lib/evilution/mutator/operator/loop_body_to_raise.rb
352
359
  - lib/evilution/mutator/operator/loop_flip.rb
353
360
  - lib/evilution/mutator/operator/method_body_replacement.rb
361
+ - lib/evilution/mutator/operator/method_body_to_raise.rb
362
+ - lib/evilution/mutator/operator/method_body_to_super.rb
354
363
  - lib/evilution/mutator/operator/method_call_removal.rb
355
364
  - lib/evilution/mutator/operator/mixin_removal.rb
356
365
  - lib/evilution/mutator/operator/multiple_assignment.rb
357
366
  - lib/evilution/mutator/operator/negation_insertion.rb
358
367
  - lib/evilution/mutator/operator/next_statement.rb
359
368
  - lib/evilution/mutator/operator/nil_replacement.rb
369
+ - lib/evilution/mutator/operator/optional_default_injection.rb
370
+ - lib/evilution/mutator/operator/optional_parameter_to_required.rb
360
371
  - lib/evilution/mutator/operator/pattern_matching_alternative.rb
361
372
  - lib/evilution/mutator/operator/pattern_matching_array.rb
362
373
  - lib/evilution/mutator/operator/pattern_matching_guard.rb
@@ -381,6 +392,7 @@ files:
381
392
  - lib/evilution/mutator/operator/string_literal.rb
382
393
  - lib/evilution/mutator/operator/superclass_removal.rb
383
394
  - lib/evilution/mutator/operator/symbol_literal.rb
395
+ - lib/evilution/mutator/operator/typed_default_return.rb
384
396
  - lib/evilution/mutator/operator/yield_statement.rb
385
397
  - lib/evilution/mutator/operator/zsuper_removal.rb
386
398
  - lib/evilution/mutator/primitives.rb
@@ -411,19 +423,24 @@ files:
411
423
  - lib/evilution/reporter/cli/item_formatters/coverage_gap.rb
412
424
  - lib/evilution/reporter/cli/item_formatters/disabled.rb
413
425
  - lib/evilution/reporter/cli/item_formatters/error.rb
426
+ - lib/evilution/reporter/cli/item_formatters/neutral_group.rb
414
427
  - lib/evilution/reporter/cli/item_formatters/result_location.rb
428
+ - lib/evilution/reporter/cli/item_formatters/subject_score.rb
429
+ - lib/evilution/reporter/cli/item_formatters/subject_score_group.rb
415
430
  - lib/evilution/reporter/cli/line_formatters.rb
416
431
  - lib/evilution/reporter/cli/line_formatters/duration.rb
417
432
  - lib/evilution/reporter/cli/line_formatters/efficiency.rb
418
433
  - lib/evilution/reporter/cli/line_formatters/error_rate_warning.rb
419
434
  - lib/evilution/reporter/cli/line_formatters/feedback_footer.rb
420
435
  - lib/evilution/reporter/cli/line_formatters/header.rb
436
+ - lib/evilution/reporter/cli/line_formatters/infra_retry_notice.rb
421
437
  - lib/evilution/reporter/cli/line_formatters/mutations.rb
422
438
  - lib/evilution/reporter/cli/line_formatters/peak_memory.rb
423
439
  - lib/evilution/reporter/cli/line_formatters/result_line.rb
424
440
  - lib/evilution/reporter/cli/line_formatters/score.rb
425
441
  - lib/evilution/reporter/cli/line_formatters/truncation_notice.rb
426
442
  - lib/evilution/reporter/cli/line_formatters/unresolved_rate_warning.rb
443
+ - lib/evilution/reporter/cli/line_formatters/unresolved_targets.rb
427
444
  - lib/evilution/reporter/cli/metrics_block.rb
428
445
  - lib/evilution/reporter/cli/pct.rb
429
446
  - lib/evilution/reporter/cli/section.rb
@@ -466,6 +483,7 @@ files:
466
483
  - lib/evilution/reporter/html/templates/unparseable_details.html.erb
467
484
  - lib/evilution/reporter/html/templates/unresolved_details.html.erb
468
485
  - lib/evilution/reporter/json.rb
486
+ - lib/evilution/reporter/json/subjects.rb
469
487
  - lib/evilution/reporter/progress_bar.rb
470
488
  - lib/evilution/reporter/suggestion.rb
471
489
  - lib/evilution/reporter/suggestion/diff_helpers.rb
@@ -481,6 +499,9 @@ files:
481
499
  - lib/evilution/result/error_info.rb
482
500
  - lib/evilution/result/memory_stats.rb
483
501
  - lib/evilution/result/mutation_result.rb
502
+ - lib/evilution/result/neutral_reason.rb
503
+ - lib/evilution/result/subject_score.rb
504
+ - lib/evilution/result/subject_scorer.rb
484
505
  - lib/evilution/result/summary.rb
485
506
  - lib/evilution/runner.rb
486
507
  - lib/evilution/runner/baseline_runner.rb
@@ -488,6 +509,7 @@ files:
488
509
  - lib/evilution/runner/diagnostics.rb
489
510
  - lib/evilution/runner/isolation_resolver.rb
490
511
  - lib/evilution/runner/mutation_executor.rb
512
+ - lib/evilution/runner/mutation_executor/infra_retry.rb
491
513
  - lib/evilution/runner/mutation_executor/mutation_runner.rb
492
514
  - lib/evilution/runner/mutation_executor/neutralization_pipeline.rb
493
515
  - lib/evilution/runner/mutation_executor/neutralizer.rb
@@ -502,6 +524,7 @@ files:
502
524
  - lib/evilution/runner/mutation_planner.rb
503
525
  - lib/evilution/runner/report_publisher.rb
504
526
  - lib/evilution/runner/subject_pipeline.rb
527
+ - lib/evilution/runner/target_spec_audit.rb
505
528
  - lib/evilution/session.rb
506
529
  - lib/evilution/session/diff.rb
507
530
  - lib/evilution/session/schema.rb