evilution 1.2.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.
- checksums.yaml +4 -4
- data/.beads/interactions.jsonl +38 -0
- data/.rubocop_todo.yml +9 -0
- data/CHANGELOG.md +47 -0
- data/README.md +65 -7
- data/docs/architecture.md +35 -1
- data/lib/evilution/ast/parser.rb +1 -0
- data/lib/evilution/ast/pattern/filter.rb +1 -0
- data/lib/evilution/ast/pattern/parser.rb +1 -0
- data/lib/evilution/ast/pattern.rb +2 -0
- data/lib/evilution/ast/sorbet_sig_detector.rb +1 -0
- data/lib/evilution/baseline.rb +37 -15
- data/lib/evilution/child_output.rb +1 -1
- data/lib/evilution/cli/command.rb +1 -0
- data/lib/evilution/cli/commands/tests_list.rb +4 -3
- data/lib/evilution/cli/commands.rb +2 -0
- data/lib/evilution/cli/dispatcher.rb +2 -0
- data/lib/evilution/cli/exit_guard.rb +2 -0
- data/lib/evilution/cli/parsed_args.rb +2 -0
- data/lib/evilution/cli/parser/command_extractor.rb +2 -0
- data/lib/evilution/cli/parser/file_args.rb +2 -0
- data/lib/evilution/cli/parser/options_builder.rb +3 -0
- data/lib/evilution/cli/parser/stdin_reader.rb +1 -0
- data/lib/evilution/cli/parser.rb +1 -0
- data/lib/evilution/cli/printers/tests_list.rb +5 -4
- data/lib/evilution/cli/printers.rb +2 -0
- data/lib/evilution/cli/result.rb +2 -0
- data/lib/evilution/cli.rb +5 -0
- data/lib/evilution/config/builders.rb +2 -0
- data/lib/evilution/config/env_loader.rb +2 -0
- data/lib/evilution/config/file_loader.rb +1 -0
- data/lib/evilution/config/sources.rb +1 -0
- data/lib/evilution/config/validators/example_targeting_cache.rb +1 -0
- data/lib/evilution/config/validators/example_targeting_fallback.rb +1 -0
- data/lib/evilution/config/validators/example_targeting_strategy.rb +1 -0
- data/lib/evilution/config/validators/fail_fast.rb +1 -0
- data/lib/evilution/config/validators/hooks.rb +1 -0
- data/lib/evilution/config/validators/ignore_patterns.rb +1 -0
- data/lib/evilution/config/validators/integration.rb +1 -0
- data/lib/evilution/config/validators/isolation.rb +1 -0
- data/lib/evilution/config/validators/jobs.rb +1 -0
- data/lib/evilution/config/validators/preload.rb +1 -0
- data/lib/evilution/config/validators/profile.rb +1 -0
- data/lib/evilution/config/validators/spec_mappings.rb +1 -0
- data/lib/evilution/config/validators/spec_pattern.rb +1 -0
- data/lib/evilution/config/validators/warmup.rb +17 -0
- data/lib/evilution/config/validators.rb +2 -0
- data/lib/evilution/config.rb +4 -3
- data/lib/evilution/coverage.rb +1 -1
- data/lib/evilution/coverage_example_filter.rb +1 -1
- data/lib/evilution/diagnostic.rb +1 -1
- data/lib/evilution/example_filter.rb +1 -1
- data/lib/evilution/integration/loading/body_call_neutralizer.rb +10 -2
- data/lib/evilution/integration/loading/test_load_path.rb +21 -13
- data/lib/evilution/integration/rspec/state_guard/configuration_state.rb +1 -0
- data/lib/evilution/integration/rspec/state_guard/configuration_streams.rb +1 -0
- data/lib/evilution/integration/rspec/state_guard/example_groups_constants.rb +1 -2
- data/lib/evilution/integration/rspec/state_guard/internals.rb +1 -2
- data/lib/evilution/integration/rspec/state_guard/object_space_example_groups.rb +1 -2
- data/lib/evilution/integration/rspec/state_guard/reporter_arrays.rb +1 -0
- data/lib/evilution/integration/rspec/state_guard/world_example_groups.rb +1 -0
- data/lib/evilution/integration/rspec/state_guard/world_filtered_examples.rb +1 -0
- data/lib/evilution/integration/rspec/state_guard/world_sources_by_path.rb +1 -0
- data/lib/evilution/integration/rspec/state_guard.rb +6 -0
- data/lib/evilution/mcp/info_tool/actions/environment.rb +1 -0
- data/lib/evilution/mcp/info_tool/actions/feedback.rb +1 -0
- data/lib/evilution/mcp/info_tool/actions/statuses.rb +1 -0
- data/lib/evilution/mcp/info_tool/actions/subjects.rb +1 -0
- data/lib/evilution/mcp/info_tool/actions/tests.rb +1 -0
- data/lib/evilution/mutation.rb +1 -1
- data/lib/evilution/mutator/operator/argument_list_removal.rb +52 -0
- data/lib/evilution/mutator/operator/argument_propagation.rb +54 -0
- data/lib/evilution/mutator/operator/array_coercion_to_literal.rb +34 -0
- data/lib/evilution/mutator/operator/attribute_write_to_read.rb +34 -0
- data/lib/evilution/mutator/operator/bang_method.rb +11 -3
- data/lib/evilution/mutator/operator/binary_operand_promotion.rb +63 -0
- data/lib/evilution/mutator/operator/block_body_promotion.rb +45 -0
- data/lib/evilution/mutator/operator/block_body_to_nil.rb +52 -0
- data/lib/evilution/mutator/operator/block_body_to_raise.rb +34 -0
- data/lib/evilution/mutator/operator/call_to_nil.rb +34 -0
- data/lib/evilution/mutator/operator/coercion_emptying.rb +60 -0
- data/lib/evilution/mutator/operator/collection_replacement.rb +19 -9
- data/lib/evilution/mutator/operator/comparison_replacement.rb +37 -12
- data/lib/evilution/mutator/operator/const_get_to_constant_path.rb +50 -0
- data/lib/evilution/mutator/operator/dig_to_fetch_chain.rb +45 -0
- data/lib/evilution/mutator/operator/double_negation_removal.rb +22 -0
- data/lib/evilution/mutator/operator/dynamic_dispatch_resolution.rb +79 -0
- data/lib/evilution/mutator/operator/inequality_to_negated_identity.rb +43 -0
- data/lib/evilution/mutator/operator/keyword_argument_removal.rb +47 -0
- data/lib/evilution/mutator/operator/proc_to_lambda.rb +43 -0
- data/lib/evilution/mutator/operator/receiver_constructor_swap.rb +51 -0
- data/lib/evilution/mutator/operator/reduce_to_sum.rb +58 -0
- data/lib/evilution/mutator/operator/regexp_anchor_to_predicate.rb +136 -0
- data/lib/evilution/mutator/operator/safe_navigation_removal.rb +67 -0
- data/lib/evilution/mutator/operator/send_mutation.rb +42 -6
- data/lib/evilution/mutator/operator/symbol_to_proc_replacement.rb +59 -0
- data/lib/evilution/mutator/operator/to_i_to_integer.rb +38 -0
- data/lib/evilution/mutator/primitives.rb +25 -0
- data/lib/evilution/mutator/registry.rb +24 -1
- data/lib/evilution/parallel/pool.rb +1 -0
- data/lib/evilution/parallel_db_warning.rb +1 -1
- data/lib/evilution/rails_warmup.rb +61 -0
- data/lib/evilution/reporter/html/report.rb +1 -0
- data/lib/evilution/result/coverage_gap_grouper.rb +1 -0
- data/lib/evilution/result/subject_scorer.rb +1 -0
- data/lib/evilution/runner/baseline_runner.rb +17 -7
- data/lib/evilution/runner/isolation_resolver.rb +23 -2
- data/lib/evilution/runner/mutation_executor/neutralizer/baseline_failed.rb +17 -10
- data/lib/evilution/source_ast_cache.rb +1 -1
- data/lib/evilution/spec_ast_cache.rb +1 -1
- data/lib/evilution/version.rb +1 -1
- data/lib/evilution.rb +23 -0
- metadata +27 -2
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../operator"
|
|
4
|
+
|
|
5
|
+
# Replace a lenient `to_i` with the strict `Integer()` conversion:
|
|
6
|
+
# `value.to_i` becomes `Integer(value)`, `value.to_i(16)` becomes
|
|
7
|
+
# `Integer(value, 16)`.
|
|
8
|
+
#
|
|
9
|
+
# `to_i` never fails — malformed input ("abc", "12abc", "") and nil become 0
|
|
10
|
+
# or a leading-digits prefix — where `Integer()` raises. `Integer()` also
|
|
11
|
+
# honours radix prefixes ("0x1A" is 26, "012" is octal 10) that `to_i`
|
|
12
|
+
# ignores. A survivor means no test feeds invalid or prefixed input.
|
|
13
|
+
#
|
|
14
|
+
# Numeric literal receivers are skipped: both conversions agree on them.
|
|
15
|
+
class Evilution::Mutator::Operator::ToIToInteger < Evilution::Mutator::Base
|
|
16
|
+
NUMERIC_LITERALS = [Prism::IntegerNode, Prism::FloatNode, Prism::RationalNode, Prism::ImaginaryNode].freeze
|
|
17
|
+
private_constant :NUMERIC_LITERALS
|
|
18
|
+
|
|
19
|
+
def visit_call_node(node)
|
|
20
|
+
rewrite(node) if node.name == :to_i && convertible?(node)
|
|
21
|
+
super
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def convertible?(node)
|
|
27
|
+
receiver = node.receiver
|
|
28
|
+
receiver && node.block.nil? && NUMERIC_LITERALS.none? { |type| receiver.is_a?(type) }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def rewrite(node)
|
|
32
|
+
arguments = node.arguments ? node.arguments.arguments : []
|
|
33
|
+
return if arguments.length > 1 || arguments.any?(Prism::SplatNode)
|
|
34
|
+
|
|
35
|
+
parts = [node.receiver, *arguments].map { |part| source_of(part) }
|
|
36
|
+
replace_span(node: node, target: node, replacement: "Integer(#{parts.join(", ")})")
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -49,4 +49,29 @@ module Evilution::Mutator::Primitives
|
|
|
49
49
|
location = child.location
|
|
50
50
|
byteslice_source(location.start_offset, location.length)
|
|
51
51
|
end
|
|
52
|
+
|
|
53
|
+
# `operand`'s source, parenthesized unless it is a primary expression — so
|
|
54
|
+
# it can take a method call: `a + b` becomes `(a + b)` in `(a + b).eql?(c)`.
|
|
55
|
+
def receiver_source(operand)
|
|
56
|
+
primary_operand?(operand) ? source_of(operand) : "(#{source_of(operand)})"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
PRIMARY_OPERANDS = [
|
|
60
|
+
Prism::LocalVariableReadNode, Prism::InstanceVariableReadNode, Prism::ClassVariableReadNode,
|
|
61
|
+
Prism::GlobalVariableReadNode, Prism::ConstantReadNode, Prism::ConstantPathNode, Prism::SelfNode,
|
|
62
|
+
Prism::ParenthesesNode, Prism::StringNode, Prism::IntegerNode, Prism::FloatNode, Prism::ArrayNode,
|
|
63
|
+
Prism::RegularExpressionNode
|
|
64
|
+
].freeze
|
|
65
|
+
private_constant :PRIMARY_OPERANDS
|
|
66
|
+
|
|
67
|
+
METHOD_NAME = /\A[[:alpha:]_]/
|
|
68
|
+
private_constant :METHOD_NAME
|
|
69
|
+
|
|
70
|
+
# A named method call or an index (`a.size`, `a[0]`) binds tighter than a
|
|
71
|
+
# following `.`; an operator send (`a + b`, `!a`, `-a`) does not.
|
|
72
|
+
def primary_operand?(operand)
|
|
73
|
+
return true if PRIMARY_OPERANDS.any? { |type| operand.is_a?(type) }
|
|
74
|
+
|
|
75
|
+
operand.is_a?(Prism::CallNode) && (operand.name.match?(METHOD_NAME) || operand.name == :[])
|
|
76
|
+
end
|
|
52
77
|
end
|
|
@@ -113,7 +113,30 @@ class Evilution::Mutator::Registry
|
|
|
113
113
|
Evilution::Mutator::Operator::EqualityToIdentity,
|
|
114
114
|
Evilution::Mutator::Operator::LambdaBody,
|
|
115
115
|
Evilution::Mutator::Operator::BeginUnwrap,
|
|
116
|
-
Evilution::Mutator::Operator::BlockParamRemoval
|
|
116
|
+
Evilution::Mutator::Operator::BlockParamRemoval,
|
|
117
|
+
Evilution::Mutator::Operator::CallToNil,
|
|
118
|
+
Evilution::Mutator::Operator::SafeNavigationRemoval,
|
|
119
|
+
Evilution::Mutator::Operator::AttributeWriteToRead,
|
|
120
|
+
Evilution::Mutator::Operator::ArgumentPropagation,
|
|
121
|
+
Evilution::Mutator::Operator::ArgumentListRemoval,
|
|
122
|
+
Evilution::Mutator::Operator::SymbolToProcReplacement,
|
|
123
|
+
Evilution::Mutator::Operator::DynamicDispatchResolution,
|
|
124
|
+
Evilution::Mutator::Operator::KeywordArgumentRemoval,
|
|
125
|
+
Evilution::Mutator::Operator::DoubleNegationRemoval,
|
|
126
|
+
Evilution::Mutator::Operator::RegexpAnchorToPredicate,
|
|
127
|
+
Evilution::Mutator::Operator::ReduceToSum,
|
|
128
|
+
Evilution::Mutator::Operator::ArrayCoercionToLiteral,
|
|
129
|
+
Evilution::Mutator::Operator::ToIToInteger,
|
|
130
|
+
Evilution::Mutator::Operator::CoercionEmptying,
|
|
131
|
+
Evilution::Mutator::Operator::ConstGetToConstantPath,
|
|
132
|
+
Evilution::Mutator::Operator::ProcToLambda,
|
|
133
|
+
Evilution::Mutator::Operator::DigToFetchChain,
|
|
134
|
+
Evilution::Mutator::Operator::InequalityToNegatedIdentity,
|
|
135
|
+
Evilution::Mutator::Operator::BinaryOperandPromotion,
|
|
136
|
+
Evilution::Mutator::Operator::ReceiverConstructorSwap,
|
|
137
|
+
Evilution::Mutator::Operator::BlockBodyToNil,
|
|
138
|
+
Evilution::Mutator::Operator::BlockBodyToRaise,
|
|
139
|
+
Evilution::Mutator::Operator::BlockBodyPromotion
|
|
117
140
|
].each { |op| registry.register(op) }
|
|
118
141
|
registry
|
|
119
142
|
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "diagnostic"
|
|
4
|
+
|
|
5
|
+
# Warms Rails state that initialises lazily on a process's first request, once
|
|
6
|
+
# in the parent after the preload, so forked mutations inherit it instead of
|
|
7
|
+
# each paying the cold start.
|
|
8
|
+
#
|
|
9
|
+
# Every step is side-effect free. A real request is deliberately NOT used:
|
|
10
|
+
# controller callbacks change process-global state -- I18n.locale,
|
|
11
|
+
# PaperTrail.request, a session row -- that every fork would then inherit,
|
|
12
|
+
# failing unrelated tests and reporting them as kills.
|
|
13
|
+
#
|
|
14
|
+
# A step whose library is not loaded is skipped silently; a step that raises
|
|
15
|
+
# (no application stylesheet, an unusual asset setup) is skipped with a note.
|
|
16
|
+
# Warm-up is an optimisation and must never fail or skew the run.
|
|
17
|
+
class Evilution::RailsWarmup
|
|
18
|
+
def initialize(application, stream: $stderr)
|
|
19
|
+
@application = application
|
|
20
|
+
@stream = stream
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def call
|
|
24
|
+
step("i18n") { warm_i18n }
|
|
25
|
+
step("routes") { warm_routes }
|
|
26
|
+
step("assets") { warm_assets }
|
|
27
|
+
step("templates") { warm_templates }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def step(name)
|
|
33
|
+
yield
|
|
34
|
+
rescue StandardError, ScriptError => e
|
|
35
|
+
Evilution::Diagnostic.warn("[evilution] rails warmup: skipped #{name} (#{e.class}: #{e.message})", stream: @stream)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def warm_i18n
|
|
39
|
+
::I18n.eager_load! if defined?(::I18n) && ::I18n.respond_to?(:eager_load!)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Rails 8 can draw routes lazily; building url_helpers compiles the helpers
|
|
43
|
+
# module every request would otherwise build on first use.
|
|
44
|
+
def warm_routes
|
|
45
|
+
routes = @application.routes
|
|
46
|
+
routes.eager_load! if routes.respond_to?(:eager_load!)
|
|
47
|
+
routes.url_helpers
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def warm_assets
|
|
51
|
+
::ActionController::Base.helpers.stylesheet_path("application") if defined?(::ActionController::Base)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Optional: jhawthorn/actionview_precompiler compiles templates up front.
|
|
55
|
+
def warm_templates
|
|
56
|
+
return unless Gem.loaded_specs.key?("actionview_precompiler")
|
|
57
|
+
|
|
58
|
+
require "actionview_precompiler"
|
|
59
|
+
::ActionviewPrecompiler.precompile
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -60,18 +60,15 @@ class Evilution::Runner::BaselineRunner
|
|
|
60
60
|
return nil unless config.baseline? && subjects.any?
|
|
61
61
|
|
|
62
62
|
log_start
|
|
63
|
-
|
|
64
|
-
timeout: config.timeout,
|
|
65
|
-
test_files: config.spec_files.empty? ? nil : config.spec_files,
|
|
66
|
-
**integration_class.baseline_options
|
|
67
|
-
)
|
|
68
|
-
result = baseline.call(subjects)
|
|
63
|
+
result = build_baseline.call(subjects)
|
|
69
64
|
log_complete(result)
|
|
70
65
|
result
|
|
71
66
|
end
|
|
72
67
|
|
|
68
|
+
# The same selector the baseline resolves with, so a surviving mutation is
|
|
69
|
+
# matched against exactly the spec files the baseline ran.
|
|
73
70
|
def neutralization_resolver
|
|
74
|
-
|
|
71
|
+
config.spec_selector
|
|
75
72
|
end
|
|
76
73
|
|
|
77
74
|
def neutralization_fallback_dir
|
|
@@ -82,6 +79,19 @@ class Evilution::Runner::BaselineRunner
|
|
|
82
79
|
|
|
83
80
|
attr_reader :config, :hooks
|
|
84
81
|
|
|
82
|
+
# Resolves through config.spec_selector -- the object `run` uses, so
|
|
83
|
+
# spec_mappings / spec_pattern apply -- and skips unresolved sources unless
|
|
84
|
+
# the run falls back to the full suite.
|
|
85
|
+
def build_baseline
|
|
86
|
+
Evilution::Baseline.new(
|
|
87
|
+
timeout: config.timeout,
|
|
88
|
+
test_files: config.spec_files.empty? ? nil : config.spec_files,
|
|
89
|
+
spec_selector: config.spec_selector,
|
|
90
|
+
fallback_to_full_suite: config.fallback_to_full_suite?,
|
|
91
|
+
**integration_class.baseline_options
|
|
92
|
+
)
|
|
93
|
+
end
|
|
94
|
+
|
|
85
95
|
def build_example_filter
|
|
86
96
|
return nil unless config.example_targeting?
|
|
87
97
|
|
|
@@ -6,6 +6,7 @@ require_relative "../isolation/in_process"
|
|
|
6
6
|
require_relative "../rails_detector"
|
|
7
7
|
require_relative "../gem_detector"
|
|
8
8
|
require_relative "../integration/loading/test_load_path"
|
|
9
|
+
require_relative "../rails_warmup"
|
|
9
10
|
|
|
10
11
|
class Evilution::Runner::IsolationResolver
|
|
11
12
|
PRELOAD_CANDIDATES = [
|
|
@@ -48,6 +49,7 @@ class Evilution::Runner::IsolationResolver
|
|
|
48
49
|
prepare_load_path_for_preload(path)
|
|
49
50
|
prepare_integration_for_preload
|
|
50
51
|
require File.expand_path(path)
|
|
52
|
+
warm_up_rails
|
|
51
53
|
rescue Evilution::ConfigError
|
|
52
54
|
raise
|
|
53
55
|
rescue ScriptError, StandardError => e
|
|
@@ -61,6 +63,21 @@ class Evilution::Runner::IsolationResolver
|
|
|
61
63
|
|
|
62
64
|
attr_reader :config, :hooks
|
|
63
65
|
|
|
66
|
+
# Runs only after a preload (which is what loads Rails). This is primarily an
|
|
67
|
+
# optimisation for :fork isolation, where children inherit the warmed state.
|
|
68
|
+
def warm_up_rails
|
|
69
|
+
return unless config.warmup == :rails
|
|
70
|
+
|
|
71
|
+
application = ::Rails.application if defined?(::Rails) && ::Rails.respond_to?(:application)
|
|
72
|
+
unless application
|
|
73
|
+
warn_message = "[evilution] warmup: rails requested but Rails is not loaded (or has no application) after preload; skipping"
|
|
74
|
+
Evilution::Diagnostic.warn(warn_message)
|
|
75
|
+
return
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
Evilution::RailsWarmup.new(application).call
|
|
79
|
+
end
|
|
80
|
+
|
|
64
81
|
# Under :fork, allow preloading — caller resolves whether a path exists (an
|
|
65
82
|
# explicit --preload / preload: value, or an auto-detected rails_helper) and
|
|
66
83
|
# bails early when none does. Under :in_process, only allow preloading when
|
|
@@ -131,7 +148,11 @@ class Evilution::Runner::IsolationResolver
|
|
|
131
148
|
# have both (a bare `require "support/foo"` must still resolve from spec/).
|
|
132
149
|
# - minitest/test-unit: a test/test_helper.rb doing a non-relative
|
|
133
150
|
# `require "support/..."` needs test/ on $LOAD_PATH. Route through
|
|
134
|
-
# TestLoadPath
|
|
151
|
+
# TestLoadPath, the policy the per-mutation test load uses, with one
|
|
152
|
+
# deliberate difference: outside_root_dirs: false. A resolved test file
|
|
153
|
+
# outside test/ and spec/ keeps its own directory, but a preload there
|
|
154
|
+
# (config/evilution_preload.rb) must not, or config/ lands ahead of the
|
|
155
|
+
# gems and config/puma.rb shadows the puma gem (GH #1597).
|
|
135
156
|
def prepare_load_path_for_preload(preload_path)
|
|
136
157
|
if config.integration == :rspec
|
|
137
158
|
prepare_rspec_preload_load_path
|
|
@@ -148,7 +169,7 @@ class Evilution::Runner::IsolationResolver
|
|
|
148
169
|
|
|
149
170
|
def prepare_test_preload_load_path(preload_path)
|
|
150
171
|
base = detected_rails_root || Evilution.project_base_dir
|
|
151
|
-
Evilution::Integration::Loading::TestLoadPath.add!([preload_path], base: base)
|
|
172
|
+
Evilution::Integration::Loading::TestLoadPath.add!([preload_path], base: base, outside_root_dirs: false)
|
|
152
173
|
end
|
|
153
174
|
|
|
154
175
|
def resolve_spec_dir
|
|
@@ -14,23 +14,30 @@ class Evilution::Runner::MutationExecutor::Neutralizer::BaselineFailed
|
|
|
14
14
|
def call(result, baseline_result:)
|
|
15
15
|
return result unless result.survived? && baseline_result && baseline_result.failed?
|
|
16
16
|
|
|
17
|
-
if @config.spec_files.any?
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
should_neutralize = baseline_result.failed_spec_files.include?(spec_file)
|
|
23
|
-
end
|
|
24
|
-
return result unless should_neutralize
|
|
17
|
+
return neutralize(result, nil) if @config.spec_files.any?
|
|
18
|
+
|
|
19
|
+
failed = baseline_result.failed_spec_files
|
|
20
|
+
spec_file = covering_specs(result.mutation.file_path).find { |spec| failed.include?(spec) }
|
|
21
|
+
return result unless spec_file
|
|
25
22
|
|
|
26
23
|
neutralize(result, spec_file)
|
|
27
24
|
end
|
|
28
25
|
|
|
29
26
|
private
|
|
30
27
|
|
|
28
|
+
# The spec files the baseline ran for this source: every file the resolver
|
|
29
|
+
# returns (a spec_selector may map one source to several), or the fallback
|
|
30
|
+
# directory when nothing resolves -- but only if the run falls back to the
|
|
31
|
+
# full suite; otherwise the baseline never ran it.
|
|
32
|
+
def covering_specs(file_path)
|
|
33
|
+
specs = Array(@spec_resolver.call(file_path))
|
|
34
|
+
return specs unless specs.empty?
|
|
35
|
+
|
|
36
|
+
@config.fallback_to_full_suite? ? [@fallback_dir] : []
|
|
37
|
+
end
|
|
38
|
+
|
|
31
39
|
# 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).
|
|
40
|
+
# fix before the run can say anything about these mutations.
|
|
34
41
|
def neutralize(result, spec_file)
|
|
35
42
|
Evilution::Result::MutationResult.new(
|
|
36
43
|
mutation: result.mutation,
|
data/lib/evilution/version.rb
CHANGED
data/lib/evilution.rb
CHANGED
|
@@ -30,6 +30,29 @@ require_relative "evilution/mutator/operator/nil_replacement"
|
|
|
30
30
|
require_relative "evilution/mutator/operator/boolean_operator_replacement"
|
|
31
31
|
require_relative "evilution/mutator/operator/boolean_operand_promotion"
|
|
32
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"
|
|
33
56
|
require_relative "evilution/mutator/operator/arithmetic_replacement"
|
|
34
57
|
require_relative "evilution/mutator/operator/string_literal"
|
|
35
58
|
require_relative "evilution/mutator/operator/array_literal"
|
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.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-09-
|
|
11
|
+
date: 2026-09-28 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: diff-lcs
|
|
@@ -201,6 +201,7 @@ files:
|
|
|
201
201
|
- lib/evilution/config/validators/profile.rb
|
|
202
202
|
- lib/evilution/config/validators/spec_mappings.rb
|
|
203
203
|
- lib/evilution/config/validators/spec_pattern.rb
|
|
204
|
+
- lib/evilution/config/validators/warmup.rb
|
|
204
205
|
- lib/evilution/coverage.rb
|
|
205
206
|
- lib/evilution/coverage/digest.rb
|
|
206
207
|
- lib/evilution/coverage/map.rb
|
|
@@ -306,15 +307,23 @@ files:
|
|
|
306
307
|
- lib/evilution/mutator.rb
|
|
307
308
|
- lib/evilution/mutator/base.rb
|
|
308
309
|
- lib/evilution/mutator/operator.rb
|
|
310
|
+
- lib/evilution/mutator/operator/argument_list_removal.rb
|
|
309
311
|
- lib/evilution/mutator/operator/argument_method_call_replacement.rb
|
|
310
312
|
- lib/evilution/mutator/operator/argument_nil_substitution.rb
|
|
313
|
+
- lib/evilution/mutator/operator/argument_propagation.rb
|
|
311
314
|
- lib/evilution/mutator/operator/argument_removal.rb
|
|
312
315
|
- lib/evilution/mutator/operator/arithmetic_replacement.rb
|
|
316
|
+
- lib/evilution/mutator/operator/array_coercion_to_literal.rb
|
|
313
317
|
- lib/evilution/mutator/operator/array_literal.rb
|
|
318
|
+
- lib/evilution/mutator/operator/attribute_write_to_read.rb
|
|
314
319
|
- lib/evilution/mutator/operator/bang_method.rb
|
|
315
320
|
- lib/evilution/mutator/operator/begin_unwrap.rb
|
|
321
|
+
- lib/evilution/mutator/operator/binary_operand_promotion.rb
|
|
316
322
|
- lib/evilution/mutator/operator/bitwise_complement.rb
|
|
317
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
|
|
318
327
|
- lib/evilution/mutator/operator/block_destructuring_expansion.rb
|
|
319
328
|
- lib/evilution/mutator/operator/block_param_removal.rb
|
|
320
329
|
- lib/evilution/mutator/operator/block_parameter_drop.rb
|
|
@@ -325,9 +334,11 @@ files:
|
|
|
325
334
|
- lib/evilution/mutator/operator/boolean_operand_promotion.rb
|
|
326
335
|
- lib/evilution/mutator/operator/boolean_operator_replacement.rb
|
|
327
336
|
- lib/evilution/mutator/operator/break_statement.rb
|
|
337
|
+
- lib/evilution/mutator/operator/call_to_nil.rb
|
|
328
338
|
- lib/evilution/mutator/operator/case_in.rb
|
|
329
339
|
- lib/evilution/mutator/operator/case_when.rb
|
|
330
340
|
- lib/evilution/mutator/operator/class_variable_write.rb
|
|
341
|
+
- lib/evilution/mutator/operator/coercion_emptying.rb
|
|
331
342
|
- lib/evilution/mutator/operator/collection_replacement.rb
|
|
332
343
|
- lib/evilution/mutator/operator/collection_return.rb
|
|
333
344
|
- lib/evilution/mutator/operator/comparison_replacement.rb
|
|
@@ -335,7 +346,11 @@ files:
|
|
|
335
346
|
- lib/evilution/mutator/operator/conditional_branch.rb
|
|
336
347
|
- lib/evilution/mutator/operator/conditional_flip.rb
|
|
337
348
|
- lib/evilution/mutator/operator/conditional_negation.rb
|
|
349
|
+
- lib/evilution/mutator/operator/const_get_to_constant_path.rb
|
|
338
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
|
|
339
354
|
- lib/evilution/mutator/operator/ensure_removal.rb
|
|
340
355
|
- lib/evilution/mutator/operator/equality_to_identity.rb
|
|
341
356
|
- lib/evilution/mutator/operator/explicit_super_mutation.rb
|
|
@@ -348,10 +363,12 @@ files:
|
|
|
348
363
|
- lib/evilution/mutator/operator/index_to_at.rb
|
|
349
364
|
- lib/evilution/mutator/operator/index_to_dig.rb
|
|
350
365
|
- lib/evilution/mutator/operator/index_to_fetch.rb
|
|
366
|
+
- lib/evilution/mutator/operator/inequality_to_negated_identity.rb
|
|
351
367
|
- lib/evilution/mutator/operator/inline_rescue.rb
|
|
352
368
|
- lib/evilution/mutator/operator/instance_variable_write.rb
|
|
353
369
|
- lib/evilution/mutator/operator/integer_literal.rb
|
|
354
370
|
- lib/evilution/mutator/operator/keyword_argument.rb
|
|
371
|
+
- lib/evilution/mutator/operator/keyword_argument_removal.rb
|
|
355
372
|
- lib/evilution/mutator/operator/lambda_body.rb
|
|
356
373
|
- lib/evilution/mutator/operator/last_expression_removal.rb
|
|
357
374
|
- lib/evilution/mutator/operator/local_variable_assignment.rb
|
|
@@ -374,16 +391,21 @@ files:
|
|
|
374
391
|
- lib/evilution/mutator/operator/pattern_predicate.rb
|
|
375
392
|
- lib/evilution/mutator/operator/predicate_replacement.rb
|
|
376
393
|
- lib/evilution/mutator/operator/predicate_to_nil.rb
|
|
394
|
+
- lib/evilution/mutator/operator/proc_to_lambda.rb
|
|
377
395
|
- lib/evilution/mutator/operator/range_replacement.rb
|
|
396
|
+
- lib/evilution/mutator/operator/receiver_constructor_swap.rb
|
|
378
397
|
- lib/evilution/mutator/operator/receiver_replacement.rb
|
|
379
398
|
- lib/evilution/mutator/operator/redo_statement.rb
|
|
399
|
+
- lib/evilution/mutator/operator/reduce_to_sum.rb
|
|
380
400
|
- lib/evilution/mutator/operator/regex_capture.rb
|
|
381
401
|
- lib/evilution/mutator/operator/regex_simplification.rb
|
|
402
|
+
- lib/evilution/mutator/operator/regexp_anchor_to_predicate.rb
|
|
382
403
|
- lib/evilution/mutator/operator/regexp_mutation.rb
|
|
383
404
|
- lib/evilution/mutator/operator/rescue_body_replacement.rb
|
|
384
405
|
- lib/evilution/mutator/operator/rescue_removal.rb
|
|
385
406
|
- lib/evilution/mutator/operator/retry_removal.rb
|
|
386
407
|
- lib/evilution/mutator/operator/return_value_removal.rb
|
|
408
|
+
- lib/evilution/mutator/operator/safe_navigation_removal.rb
|
|
387
409
|
- lib/evilution/mutator/operator/scalar_return.rb
|
|
388
410
|
- lib/evilution/mutator/operator/send_mutation.rb
|
|
389
411
|
- lib/evilution/mutator/operator/splat_operator.rb
|
|
@@ -392,6 +414,8 @@ files:
|
|
|
392
414
|
- lib/evilution/mutator/operator/string_literal.rb
|
|
393
415
|
- lib/evilution/mutator/operator/superclass_removal.rb
|
|
394
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
|
|
395
419
|
- lib/evilution/mutator/operator/typed_default_return.rb
|
|
396
420
|
- lib/evilution/mutator/operator/yield_statement.rb
|
|
397
421
|
- lib/evilution/mutator/operator/zsuper_removal.rb
|
|
@@ -416,6 +440,7 @@ files:
|
|
|
416
440
|
- lib/evilution/process_cleanup.rb
|
|
417
441
|
- lib/evilution/process_supervisor.rb
|
|
418
442
|
- lib/evilution/rails_detector.rb
|
|
443
|
+
- lib/evilution/rails_warmup.rb
|
|
419
444
|
- lib/evilution/related_spec_heuristic.rb
|
|
420
445
|
- lib/evilution/reporter.rb
|
|
421
446
|
- lib/evilution/reporter/cli.rb
|