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.
- checksums.yaml +4 -4
- data/.beads/interactions.jsonl +20 -0
- data/CHANGELOG.md +39 -0
- data/README.md +141 -11
- data/docs/architecture.md +20 -5
- data/docs/isolation.md +3 -4
- data/exe/evil +8 -1
- data/exe/evilution +8 -1
- data/lib/evilution/ast/local_reads.rb +43 -0
- data/lib/evilution/cli/exit_guard.rb +59 -0
- data/lib/evilution/cli/parser/options_builder.rb +1 -0
- data/lib/evilution/cli.rb +1 -0
- data/lib/evilution/config.rb +3 -2
- data/lib/evilution/diagnostic.rb +22 -0
- data/lib/evilution/integration/loading/concern_state_cleaner.rb +20 -4
- data/lib/evilution/integration/loading/reeval_warning_filter.rb +72 -0
- data/lib/evilution/integration/loading/source_evaluator.rb +4 -1
- data/lib/evilution/integration/minitest.rb +2 -1
- data/lib/evilution/integration/rspec/crash_detector_lifecycle.rb +9 -1
- data/lib/evilution/integration/rspec/state_guard/configuration_streams.rb +4 -1
- data/lib/evilution/integration/rspec/unresolved_spec_warner.rb +2 -1
- data/lib/evilution/integration/rspec.rb +48 -2
- data/lib/evilution/integration/test_unit/test_file_resolver.rb +2 -1
- data/lib/evilution/isolation/fork.rb +29 -8
- data/lib/evilution/mutator/operator/block_destructuring_expansion.rb +85 -0
- data/lib/evilution/mutator/operator/block_parameter_drop.rb +96 -0
- data/lib/evilution/mutator/operator/forwarding_super_to_explicit.rb +71 -0
- data/lib/evilution/mutator/operator/method_body_replacement.rb +10 -1
- data/lib/evilution/mutator/operator/method_body_to_raise.rb +59 -0
- data/lib/evilution/mutator/operator/method_body_to_super.rb +150 -0
- data/lib/evilution/mutator/operator/optional_default_injection.rb +71 -0
- data/lib/evilution/mutator/operator/optional_parameter_to_required.rb +41 -0
- data/lib/evilution/mutator/operator/typed_default_return.rb +84 -0
- data/lib/evilution/mutator/registry.rb +8 -0
- data/lib/evilution/process_supervisor.rb +20 -8
- data/lib/evilution/reporter/cli/item_formatters/neutral_group.rb +23 -0
- data/lib/evilution/reporter/cli/item_formatters/subject_score.rb +42 -0
- data/lib/evilution/reporter/cli/item_formatters/subject_score_group.rb +16 -0
- data/lib/evilution/reporter/cli/line_formatters/infra_retry_notice.rb +19 -0
- data/lib/evilution/reporter/cli/line_formatters/result_line.rb +27 -3
- data/lib/evilution/reporter/cli/line_formatters/score.rb +19 -1
- data/lib/evilution/reporter/cli/line_formatters/unresolved_targets.rb +35 -0
- data/lib/evilution/reporter/cli/metrics_block.rb +4 -0
- data/lib/evilution/reporter/cli/trailer.rb +11 -7
- data/lib/evilution/reporter/cli.rb +20 -4
- data/lib/evilution/reporter/json/subjects.rb +29 -0
- data/lib/evilution/reporter/json.rb +23 -1
- data/lib/evilution/result/mutation_result.rb +3 -2
- data/lib/evilution/result/neutral_reason.rb +35 -0
- data/lib/evilution/result/subject_score.rb +28 -0
- data/lib/evilution/result/subject_scorer.rb +37 -0
- data/lib/evilution/result/summary.rb +44 -2
- data/lib/evilution/runner/canary.rb +52 -5
- data/lib/evilution/runner/mutation_executor/infra_retry.rb +54 -0
- data/lib/evilution/runner/mutation_executor/neutralizer/baseline_failed.rb +9 -3
- data/lib/evilution/runner/mutation_executor/neutralizer/infra_error.rb +18 -1
- data/lib/evilution/runner/mutation_executor/result_cache.rb +11 -0
- data/lib/evilution/runner/mutation_executor/strategy/parallel.rb +19 -1
- data/lib/evilution/runner/mutation_executor.rb +32 -4
- data/lib/evilution/runner/report_publisher.rb +31 -9
- data/lib/evilution/runner/target_spec_audit.rb +43 -0
- data/lib/evilution/runner.rb +10 -1
- data/lib/evilution/version.rb +1 -1
- data/lib/evilution.rb +12 -0
- 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
|
-
|
|
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
|
|
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
|
-
|
|
37
|
+
return sequential_run(notifier, pipeline, mutations, baseline_result, integration) if @config.jobs <= 1
|
|
33
38
|
|
|
34
|
-
|
|
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:
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
data/lib/evilution/runner.rb
CHANGED
|
@@ -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"
|
data/lib/evilution/version.rb
CHANGED
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.
|
|
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-
|
|
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
|