Class: Evilution::Reporter::Suggestion
- Inherits:
-
Object
- Object
- Evilution::Reporter::Suggestion
- Defined in:
- lib/evilution/reporter/suggestion.rb
Constant Summary collapse
- TEMPLATES =
{ "comparison_replacement" => "Add a test for the boundary condition where the comparison operand equals the threshold exactly", "arithmetic_replacement" => "Add a test that verifies the arithmetic result, not just truthiness of the outcome", "boolean_operator_replacement" => "Add a test where only one of the boolean conditions is true to distinguish && from ||", "boolean_literal_replacement" => "Add a test that exercises the false/true branch explicitly", "nil_replacement" => "Add a test that asserts the return value is not nil", "integer_literal" => "Add a test that checks the exact numeric value, not just > 0 or truthy", "float_literal" => "Add a test that checks the exact floating-point value returned", "string_literal" => "Add a test that asserts the string content, not just its presence", "array_literal" => "Add a test that verifies the array contents or length", "hash_literal" => "Add a test that verifies the hash keys and values", "symbol_literal" => "Add a test that checks the exact symbol returned", "conditional_negation" => "Add tests for both the true and false branches of this conditional", "conditional_branch" => "Add a test that exercises the removed branch of this conditional", "statement_deletion" => "Add a test that depends on the side effect of this statement", "method_body_replacement" => "Add a test that checks the method's return value or side effects", "negation_insertion" => "Add a test where the predicate result matters (not just truthiness)", "return_value_removal" => "Add a test that uses the return value of this method", "collection_replacement" => "Add a test that checks the return value of the collection operation, not just side effects", "method_call_removal" => "Add a test that depends on the return value or side effect of this method call", "argument_removal" => "Add a test that verifies the correct arguments are passed to this method call", "compound_assignment" => "Add a test that verifies the side effect of this compound assignment (the accumulated value matters)", "superclass_removal" => "Add a test that exercises inherited behavior from the superclass", "mixin_removal" => "Add a test that exercises behavior provided by the included/extended module", "local_variable_assignment" => "Add a test that depends on the assigned variable being stored, not just the value expression", "instance_variable_write" => "Add a test that verifies the instance variable is set correctly, not just the return value", "class_variable_write" => "Add a test that verifies the class variable is set correctly and affects shared state", "global_variable_write" => "Add a test that verifies the global variable is set correctly, not just the value expression", "rescue_removal" => "Add a test that triggers the rescued exception and verifies the rescue handler behavior", "rescue_body_replacement" => "Add a test that triggers the rescued exception and verifies the rescue body produces the correct result", "inline_rescue" => "Add a test that triggers the inline rescue and verifies the fallback value is used correctly", "ensure_removal" => "Add a test that verifies the ensure cleanup code runs and its side effects are observable", "break_statement" => "Add a test that verifies the break condition and the value returned when the loop exits early", "next_statement" => "Add a test that verifies the next condition and the value yielded when the iteration skips", "redo_statement" => "Add a test that verifies the redo restarts the iteration and the retry logic is necessary", "bang_method" => "Add a test that distinguishes in-place mutation from copy semantics (bang vs non-bang)", "bitwise_replacement" => "Add a test that checks the exact bitwise result to distinguish &, |, and ^ operators", "bitwise_complement" => "Add a test that verifies the bitwise complement (~) result, not just the sign or magnitude", "zsuper_removal" => "Add a test that verifies inherited behavior from super is needed, not just the subclass logic", "explicit_super_mutation" => "Add a test that verifies the correct arguments are passed to super and the inherited result matters", "index_to_fetch" => "Add a test that distinguishes [] (returns nil for missing keys) from .fetch (raises KeyError)", "index_to_dig" => "Add a test that verifies chained [] access returns the correct nested value", "index_assignment_removal" => "Add a test that verifies the []= assignment side effect is observable (the collection is modified)", "pattern_matching_guard" => "Add a test with input that matches the pattern but fails the guard to verify filtering", "pattern_matching_alternative" => "Add a test with input that matches only one specific alternative to verify each branch is reachable", "pattern_matching_array" => "Add a test that verifies each element position in the array pattern matches the expected type or value", "collection_return" => "Add a test that verifies the method returns a non-empty collection, not just any array or hash", "scalar_return" => "Add a test that verifies the method returns a non-zero/non-empty scalar value, not just any type" }.freeze
- CONCRETE_TEMPLATES =
{ "comparison_replacement" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'returns the correct result at the comparison boundary in ##{method_name}' do # Test with values where the original operator and mutated operator # produce different results (e.g., equal values for > vs >=) result = subject.#{method_name}(boundary_value) expect(result).to eq(expected) end RSPEC }, "arithmetic_replacement" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'computes the correct arithmetic result in ##{method_name}' do # Assert the exact numeric result, not just truthiness or sign result = subject.#{method_name}(input_value) expect(result).to eq(expected) end RSPEC }, "boolean_operator_replacement" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'returns the correct result when one condition is true and one is false in ##{method_name}' do # Use inputs where only one operand is truthy to distinguish && from || result = subject.#{method_name}(input_value) expect(result).to eq(expected) end RSPEC }, "boolean_literal_replacement" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'returns the expected boolean value from ##{method_name}' do # Assert the exact true/false/nil value, not just truthiness result = subject.#{method_name}(input_value) expect(result).to eq(expected) end RSPEC }, "negation_insertion" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'returns the correct boolean from the predicate in ##{method_name}' do # Assert the exact true/false result, not just truthiness result = subject.#{method_name}(input_value) expect(result).to eq(true).or eq(false) end RSPEC }, "integer_literal" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'returns the exact integer value from ##{method_name}' do # Assert the exact numeric value, not just > 0 or truthy result = subject.#{method_name}(input_value) expect(result).to eq(expected) end RSPEC }, "float_literal" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'returns the exact float value from ##{method_name}' do # Assert the exact floating-point result result = subject.#{method_name}(input_value) expect(result).to eq(expected) end RSPEC }, "string_literal" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'returns the exact string content from ##{method_name}' do # Assert the exact string value, not just presence or non-empty result = subject.#{method_name}(input_value) expect(result).to eq(expected) end RSPEC }, "symbol_literal" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'returns the exact symbol from ##{method_name}' do # Assert the exact symbol value, not just that it is a Symbol result = subject.#{method_name}(input_value) expect(result).to eq(expected) end RSPEC }, "array_literal" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'returns the expected array contents from ##{method_name}' do # Assert the exact array elements, not just non-empty or truthy result = subject.#{method_name}(input_value) expect(result).to eq(expected) end RSPEC }, "hash_literal" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'returns the expected hash contents from ##{method_name}' do # Assert the exact keys and values, not just non-empty or truthy result = subject.#{method_name}(input_value) expect(result).to eq(expected) end RSPEC }, "collection_replacement" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'uses the return value of the collection operation in ##{method_name}' do # Assert the return value of the collection method, not just side effects result = subject.#{method_name}(input_value) expect(result).to eq(expected) end RSPEC }, "conditional_negation" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'exercises both branches of the conditional in ##{method_name}' do # Test with inputs that make the condition true AND false result = subject.#{method_name}(input_value) expect(result).to eq(expected) end RSPEC }, "conditional_branch" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'exercises the removed branch of the conditional in ##{method_name}' do # Test with inputs that trigger the branch removed by this mutation result = subject.#{method_name}(input_value) expect(result).to eq(expected) end RSPEC }, "statement_deletion" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, _mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: deleted `#{original_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'depends on the side effect of the deleted statement in ##{method_name}' do # Assert a side effect or return value that changes when this statement is removed subject.#{method_name}(input_value) expect(observable_side_effect).to eq(expected) end RSPEC }, "method_body_replacement" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'verifies the return value or side effects of ##{method_name}' do # Assert the method produces a meaningful result, not just nil result = subject.#{method_name}(input_value) expect(result).to eq(expected) end RSPEC }, "return_value_removal" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'uses the return value of ##{method_name}' do # Assert the caller depends on the return value, not just side effects result = subject.#{method_name}(input_value) expect(result).to eq(expected) end RSPEC }, "method_call_removal" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'depends on the return value or side effect of the call in ##{method_name}' do # Assert the method call's effect is observable result = subject.#{method_name}(input_value) expect(result).to eq(expected) end RSPEC }, "compound_assignment" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'verifies the compound assignment side effect in ##{method_name}' do # Assert the accumulated value after the compound assignment # The mutation changes the operator, so the final value will differ subject.#{method_name}(input_value) expect(observable_side_effect).to eq(expected) end RSPEC }, "nil_replacement" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'asserts the nil return value from ##{method_name}' do # Assert the method returns nil, not a substituted value result = subject.#{method_name}(input_value) expect(result).to be_nil end RSPEC }, "superclass_removal" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, _mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: removed superclass from `#{original_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'depends on inherited behavior in ##{method_name}' do # Assert behavior that comes from the superclass result = subject.#{method_name}(input_value) expect(result).to eq(expected) end RSPEC }, "local_variable_assignment" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'verifies the local variable assignment is used in ##{method_name}' do # Assert that the assigned variable is read later, not just the value expression result = subject.#{method_name}(input_value) expect(result).to eq(expected) end RSPEC }, "instance_variable_write" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'verifies the instance variable @state is set correctly in ##{method_name}' do # Assert that the instance variable holds the expected value after the method runs subject.#{method_name}(input_value) expect(subject.instance_variable_get(:@variable)).to eq(expected) end RSPEC }, "class_variable_write" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'verifies the class variable @@shared state is set correctly in ##{method_name}' do # Assert that the class variable holds the expected value and affects shared state subject.#{method_name}(input_value) expect(described_class.class_variable_get(:@@variable)).to eq(expected) end RSPEC }, "global_variable_write" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'verifies the global variable $state is set correctly in ##{method_name}' do # Assert that the global variable holds the expected value after the method runs subject.#{method_name}(input_value) expect($variable).to eq(expected) end RSPEC }, "mixin_removal" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, _mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: removed `#{original_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'depends on behavior from the included module in ##{method_name}' do # Assert behavior provided by the mixin result = subject.#{method_name}(input_value) expect(result).to eq(expected) end RSPEC }, "rescue_removal" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, _mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: removed `#{original_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'verifies the rescue handler is needed in ##{method_name}' do # Trigger the rescued exception and assert the handler's effect result = subject.#{method_name}(input_that_raises) expect(result).to eq(expected) end RSPEC }, "rescue_body_replacement" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'verifies the rescue handler produces the correct result in ##{method_name}' do # Trigger the exception and assert the rescue body's return value or side effect result = subject.#{method_name}(input_that_raises) expect(result).to eq(expected) end RSPEC }, "inline_rescue" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'verifies the inline rescue fallback value in ##{method_name}' do # Trigger the exception and assert the fallback value is correct result = subject.#{method_name}(input_that_raises) expect(result).to eq(expected) end RSPEC }, "ensure_removal" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, _mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: removed ensure block `#{original_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'verifies the ensure cleanup runs in ##{method_name}' do # Assert that the cleanup side effect is observable after the method runs subject.#{method_name}(input_value) expect(observable_cleanup_effect).to eq(expected) end RSPEC }, "break_statement" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'verifies the break exits the loop correctly in ##{method_name}' do # Assert the loop exits early and returns the expected value result = subject.#{method_name}(input_value) expect(result).to eq(expected) end RSPEC }, "next_statement" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'verifies the next skips the iteration correctly in ##{method_name}' do # Assert the iteration is skipped and the expected value is yielded result = subject.#{method_name}(input_value) expect(result).to eq(expected) end RSPEC }, "redo_statement" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'verifies the redo retry logic is necessary in ##{method_name}' do # Assert the iteration restart changes the outcome result = subject.#{method_name}(input_value) expect(result).to eq(expected) end RSPEC }, "bitwise_replacement" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'verifies the exact bitwise result in ##{method_name}' do # Assert the exact bit-level result to distinguish &, |, and ^ operators result = subject.#{method_name}(input_value) expect(result).to eq(expected) end RSPEC }, "bitwise_complement" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'verifies the bitwise complement result in ##{method_name}' do # Assert the exact complement (~) value, not just sign or magnitude result = subject.#{method_name}(input_value) expect(result).to eq(expected) end RSPEC }, "bang_method" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'verifies in-place vs copy semantics matter in ##{method_name}' do # Assert that the original object is or is not modified result = subject.#{method_name}(input_value) expect(result).to eq(expected) end RSPEC }, "zsuper_removal" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'verifies inherited behavior from super is needed in ##{method_name}' do # Assert that the result depends on the superclass implementation result = subject.#{method_name}(input_value) expect(result).to eq(expected) end RSPEC }, "explicit_super_mutation" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'verifies the correct arguments are passed to super in ##{method_name}' do # Assert the inherited method receives the expected arguments result = subject.#{method_name}(input_value) expect(result).to eq(expected) end RSPEC }, "index_to_fetch" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'distinguishes [] from .fetch for missing keys in ##{method_name}' do # Access a missing key: [] returns nil, .fetch raises KeyError expect { subject.#{method_name}(collection_with_missing_key) }.to raise_error(KeyError) end RSPEC }, "index_to_dig" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'verifies the chained [] access returns the correct nested value in ##{method_name}' do # Assert the nested lookup produces the expected value result = subject.#{method_name}(nested_collection) expect(result).to eq(expected) end RSPEC }, "index_assignment_removal" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'verifies the []= assignment modifies the collection in ##{method_name}' do # Assert the collection contains the assigned value after the method runs result = subject.#{method_name}(collection) expect(result).to include(expected_key => expected_value) end RSPEC }, "pattern_matching_guard" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'verifies the pattern guard filters correctly in ##{method_name}' do # Test with input that matches the pattern but fails the guard condition # The guard should prevent matching, routing to a different branch result = subject.#{method_name}(input_matching_pattern_but_failing_guard) expect(result).to eq(expected) end RSPEC }, "pattern_matching_alternative" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'verifies each pattern alternative is reachable in ##{method_name}' do # Test with input that matches only one specific alternative # Each alternative should have a dedicated test case result = subject.#{method_name}(input_for_specific_alternative) expect(result).to eq(expected) end RSPEC }, "collection_return" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'returns a non-empty collection from ##{method_name}' do # Assert the collection has the expected elements, not just non-empty result = subject.#{method_name}(input_value) expect(result).to eq(expected) end RSPEC }, "scalar_return" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'returns a non-zero/non-empty value from ##{method_name}' do # Assert the exact scalar value, not just presence or type result = subject.#{method_name}(input_value) expect(result).to eq(expected) end RSPEC }, "pattern_matching_array" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~RSPEC.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} it 'verifies each array pattern element matters in ##{method_name}' do # Test with input where changing one element type causes a different match # Each position in the array pattern should be validated result = subject.#{method_name}(input_with_wrong_element_type) expect(result).to eq(expected) end RSPEC } }.freeze
- MINITEST_CONCRETE_TEMPLATES =
{ "comparison_replacement" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_returns_correct_result_at_comparison_boundary_in_#{safe_name} # Test with values where the original operator and mutated operator # produce different results (e.g., equal values for > vs >=) result = subject.#{method_name}(boundary_value) assert_equal expected, result end MINITEST }, "arithmetic_replacement" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_computes_correct_arithmetic_result_in_#{safe_name} # Assert the exact numeric result, not just truthiness or sign result = subject.#{method_name}(input_value) assert_equal expected, result end MINITEST }, "boolean_operator_replacement" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_returns_correct_result_when_one_condition_differs_in_#{safe_name} # Use inputs where only one operand is truthy to distinguish && from || result = subject.#{method_name}(input_value) assert_equal expected, result end MINITEST }, "boolean_literal_replacement" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_returns_expected_boolean_value_from_#{safe_name} # Assert the exact true/false/nil value, not just truthiness result = subject.#{method_name}(input_value) assert_equal expected, result end MINITEST }, "negation_insertion" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_returns_correct_boolean_from_predicate_in_#{safe_name} # Assert the exact true/false result, not just truthiness result = subject.#{method_name}(input_value) assert_includes [true, false], result end MINITEST }, "integer_literal" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_returns_exact_integer_value_from_#{safe_name} # Assert the exact numeric value, not just > 0 or truthy result = subject.#{method_name}(input_value) assert_equal expected, result end MINITEST }, "float_literal" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_returns_exact_float_value_from_#{safe_name} # Assert the exact floating-point result result = subject.#{method_name}(input_value) assert_in_delta expected, result end MINITEST }, "string_literal" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_returns_exact_string_content_from_#{safe_name} # Assert the exact string value, not just presence or non-empty result = subject.#{method_name}(input_value) assert_equal expected, result end MINITEST }, "symbol_literal" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_returns_exact_symbol_from_#{safe_name} # Assert the exact symbol value, not just that it is a Symbol result = subject.#{method_name}(input_value) assert_equal expected, result end MINITEST }, "array_literal" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_returns_expected_array_contents_from_#{safe_name} # Assert the exact array elements, not just non-empty or truthy result = subject.#{method_name}(input_value) assert_equal expected, result end MINITEST }, "hash_literal" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_returns_expected_hash_contents_from_#{safe_name} # Assert the exact keys and values, not just non-empty or truthy result = subject.#{method_name}(input_value) assert_equal expected, result end MINITEST }, "collection_replacement" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_uses_return_value_of_collection_operation_in_#{safe_name} # Assert the return value of the collection method, not just side effects result = subject.#{method_name}(input_value) assert_equal expected, result end MINITEST }, "conditional_negation" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_exercises_both_branches_of_conditional_in_#{safe_name} # Test with inputs that make the condition true AND false result = subject.#{method_name}(input_value) assert_equal expected, result end MINITEST }, "conditional_branch" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_exercises_removed_branch_of_conditional_in_#{safe_name} # Test with inputs that trigger the branch removed by this mutation result = subject.#{method_name}(input_value) assert_equal expected, result end MINITEST }, "statement_deletion" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, _mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: deleted `#{original_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_depends_on_side_effect_of_deleted_statement_in_#{safe_name} # Assert a side effect or return value that changes when this statement is removed subject.#{method_name}(input_value) assert_equal expected, observable_side_effect end MINITEST }, "method_body_replacement" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_verifies_return_value_or_side_effects_of_#{safe_name} # Assert the method produces a meaningful result, not just nil result = subject.#{method_name}(input_value) assert_equal expected, result end MINITEST }, "return_value_removal" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_uses_return_value_of_#{safe_name} # Assert the caller depends on the return value, not just side effects result = subject.#{method_name}(input_value) assert_equal expected, result end MINITEST }, "method_call_removal" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_depends_on_return_value_or_side_effect_of_call_in_#{safe_name} # Assert the method call's effect is observable result = subject.#{method_name}(input_value) assert_equal expected, result end MINITEST }, "compound_assignment" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_verifies_compound_assignment_side_effect_in_#{safe_name} # Assert the accumulated value after the compound assignment # The mutation changes the operator, so the final value will differ subject.#{method_name}(input_value) assert_equal expected, observable_side_effect end MINITEST }, "nil_replacement" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_asserts_nil_return_value_from_#{safe_name} # Assert the method returns nil, not a substituted value result = subject.#{method_name}(input_value) assert_nil result end MINITEST }, "superclass_removal" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, _mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: removed superclass from `#{original_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_depends_on_inherited_behavior_in_#{safe_name} # Assert behavior that comes from the superclass result = subject.#{method_name}(input_value) assert_equal expected, result end MINITEST }, "local_variable_assignment" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_verifies_local_variable_assignment_is_used_in_#{safe_name} # Assert that the assigned variable is read later, not just the value expression result = subject.#{method_name}(input_value) assert_equal expected, result end MINITEST }, "instance_variable_write" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_verifies_instance_variable_is_set_correctly_in_#{safe_name} # Assert that the instance variable holds the expected value after the method runs subject.#{method_name}(input_value) assert_equal expected, subject.instance_variable_get(:@variable) end MINITEST }, "class_variable_write" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_verifies_class_variable_shared_state_in_#{safe_name} # Assert that the class variable holds the expected value and affects shared state subject.#{method_name}(input_value) assert_equal expected, klass.class_variable_get(:@@variable) end MINITEST }, "global_variable_write" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_verifies_global_variable_is_set_correctly_in_#{safe_name} # Assert that the global variable holds the expected value after the method runs subject.#{method_name}(input_value) assert_equal expected, $variable end MINITEST }, "mixin_removal" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, _mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: removed `#{original_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_depends_on_behavior_from_included_module_in_#{safe_name} # Assert behavior provided by the mixin result = subject.#{method_name}(input_value) assert_equal expected, result end MINITEST }, "rescue_removal" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, _mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: removed `#{original_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_verifies_rescue_handler_is_needed_in_#{safe_name} # Trigger the rescued exception and assert the handler's effect result = subject.#{method_name}(input_that_raises) assert_equal expected, result end MINITEST }, "rescue_body_replacement" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_verifies_rescue_handler_produces_correct_result_in_#{safe_name} # Trigger the exception and assert the rescue body's return value or side effect result = subject.#{method_name}(input_that_raises) assert_equal expected, result end MINITEST }, "inline_rescue" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_verifies_inline_rescue_fallback_value_in_#{safe_name} # Trigger the exception and assert the fallback value is correct result = subject.#{method_name}(input_that_raises) assert_equal expected, result end MINITEST }, "ensure_removal" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, _mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: removed ensure block `#{original_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_verifies_ensure_cleanup_runs_in_#{safe_name} # Assert that the cleanup side effect is observable after the method runs subject.#{method_name}(input_value) assert_equal expected, observable_cleanup_effect end MINITEST }, "break_statement" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_verifies_break_exits_loop_correctly_in_#{safe_name} # Assert the loop exits early and returns the expected value result = subject.#{method_name}(input_value) assert_equal expected, result end MINITEST }, "next_statement" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_verifies_next_skips_iteration_correctly_in_#{safe_name} # Assert the iteration is skipped and the expected value is yielded result = subject.#{method_name}(input_value) assert_equal expected, result end MINITEST }, "redo_statement" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_verifies_redo_retry_logic_is_necessary_in_#{safe_name} # Assert the iteration restart changes the outcome result = subject.#{method_name}(input_value) assert_equal expected, result end MINITEST }, "bitwise_replacement" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_verifies_exact_bitwise_result_in_#{safe_name} # Assert the exact bit-level result to distinguish &, |, and ^ operators result = subject.#{method_name}(input_value) assert_equal expected, result end MINITEST }, "bitwise_complement" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_verifies_bitwise_complement_result_in_#{safe_name} # Assert the exact complement (~) value, not just sign or magnitude result = subject.#{method_name}(input_value) assert_equal expected, result end MINITEST }, "bang_method" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_verifies_in_place_vs_copy_semantics_matter_in_#{safe_name} # Assert that the original object is or is not modified result = subject.#{method_name}(input_value) assert_equal expected, result end MINITEST }, "zsuper_removal" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_verifies_inherited_behavior_from_super_in_#{safe_name} # Assert that the result depends on the superclass implementation result = subject.#{method_name}(input_value) assert_equal expected, result end MINITEST }, "explicit_super_mutation" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_verifies_correct_arguments_passed_to_super_in_#{safe_name} # Assert the inherited method receives the expected arguments result = subject.#{method_name}(input_value) assert_equal expected, result end MINITEST }, "index_to_fetch" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_distinguishes_bracket_from_fetch_for_missing_keys_in_#{safe_name} # Access a missing key: [] returns nil, .fetch raises KeyError assert_raises(KeyError) { subject.#{method_name}(collection_with_missing_key) } end MINITEST }, "index_to_dig" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_verifies_chained_bracket_access_returns_correct_nested_value_in_#{safe_name} # Assert the nested lookup produces the expected value result = subject.#{method_name}(nested_collection) assert_equal expected, result end MINITEST }, "index_assignment_removal" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_verifies_bracket_assignment_modifies_collection_in_#{safe_name} # Assert the collection contains the assigned value after the method runs result = subject.#{method_name}(collection) assert_includes result, expected_value end MINITEST }, "pattern_matching_guard" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_verifies_pattern_guard_filters_correctly_in_#{safe_name} # Test with input that matches the pattern but fails the guard condition # The guard should prevent matching, routing to a different branch result = subject.#{method_name}(input_matching_pattern_but_failing_guard) assert_equal expected, result end MINITEST }, "pattern_matching_alternative" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_verifies_each_pattern_alternative_is_reachable_in_#{safe_name} # Test with input that matches only one specific alternative # Each alternative should have a dedicated test case result = subject.#{method_name}(input_for_specific_alternative) assert_equal expected, result end MINITEST }, "collection_return" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_returns_non_empty_collection_from_#{safe_name} # Assert the collection has the expected elements, not just non-empty result = subject.#{method_name}(input_value) assert_equal expected, result end MINITEST }, "scalar_return" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_returns_non_zero_non_empty_value_from_#{safe_name} # Assert the exact scalar value, not just presence or type result = subject.#{method_name}(input_value) assert_equal expected, result end MINITEST }, "pattern_matching_array" => lambda { |mutation| method_name = parse_method_name(mutation.subject.name) safe_name = sanitize_method_name(method_name) original_line, mutated_line = extract_diff_lines(mutation.diff) <<~MINITEST.strip # Mutation: changed `#{original_line}` to `#{mutated_line}` in #{mutation.subject.name} # #{mutation.file_path}:#{mutation.line} def test_verifies_each_array_pattern_element_matters_in_#{safe_name} # Test with input where changing one element type causes a different match # Each position in the array pattern should be validated result = subject.#{method_name}(input_with_wrong_element_type) assert_equal expected, result end MINITEST } }.freeze
- DEFAULT_SUGGESTION =
"Add a more specific test that detects this mutation"
Class Method Summary collapse
- .extract_diff_lines(diff) ⇒ Object
- .parse_method_name(subject_name) ⇒ Object
- .sanitize_method_name(name) ⇒ Object
Instance Method Summary collapse
-
#call(summary) ⇒ Array<Hash>
Generate suggestions for survived mutations.
-
#initialize(suggest_tests: false, integration: :rspec) ⇒ Suggestion
constructor
A new instance of Suggestion.
-
#suggestion_for(mutation) ⇒ String
Generate a suggestion for a single mutation.
Constructor Details
#initialize(suggest_tests: false, integration: :rspec) ⇒ Suggestion
Returns a new instance of Suggestion.
1314 1315 1316 1317 |
# File 'lib/evilution/reporter/suggestion.rb', line 1314 def initialize(suggest_tests: false, integration: :rspec) @suggest_tests = suggest_tests @integration = integration end |
Class Method Details
.extract_diff_lines(diff) ⇒ Object
1355 1356 1357 1358 1359 1360 |
# File 'lib/evilution/reporter/suggestion.rb', line 1355 def extract_diff_lines(diff) lines = diff.split("\n") original = lines.find { |l| l.start_with?("- ") } mutated = lines.find { |l| l.start_with?("+ ") } [original&.sub(/^- /, "")&.strip, mutated&.sub(/^\+ /, "")&.strip] end |
.parse_method_name(subject_name) ⇒ Object
1347 1348 1349 |
# File 'lib/evilution/reporter/suggestion.rb', line 1347 def parse_method_name(subject_name) subject_name.split(/[#.]/).last end |
.sanitize_method_name(name) ⇒ Object
1351 1352 1353 |
# File 'lib/evilution/reporter/suggestion.rb', line 1351 def sanitize_method_name(name) name.gsub(/[^a-zA-Z0-9_]/, "_").gsub(/_+/, "_").gsub(/\A_|_\z/, "") end |
Instance Method Details
#call(summary) ⇒ Array<Hash>
Generate suggestions for survived mutations.
1323 1324 1325 1326 1327 1328 1329 1330 |
# File 'lib/evilution/reporter/suggestion.rb', line 1323 def call(summary) summary.survived_results.map do |result| { mutation: result.mutation, suggestion: suggestion_for(result.mutation) } end end |
#suggestion_for(mutation) ⇒ String
Generate a suggestion for a single mutation.
1336 1337 1338 1339 1340 1341 1342 1343 1344 |
# File 'lib/evilution/reporter/suggestion.rb', line 1336 def suggestion_for(mutation) if @suggest_tests templates = @integration == :minitest ? MINITEST_CONCRETE_TEMPLATES : CONCRETE_TEMPLATES concrete = templates[mutation.operator_name] return concrete.call(mutation) if concrete end TEMPLATES.fetch(mutation.operator_name, DEFAULT_SUGGESTION) end |