Muex.Mutator behaviour
(Muex v0.6.1)
View Source
Behaviour for mutation operators that transform AST nodes.
Mutators implement specific mutation strategies (e.g., arithmetic operators, boolean operators, literals) and return a list of possible mutations for a given AST.
Each mutator declares which languages it supports via supported_languages/0.
Mutators targeting the same AST family (e.g., Elixir and Erlang both use BEAM
AST) can declare support for multiple languages.
Equivalent Mutations
Mutators can declare that a generated mutation is semantically equivalent to the original code — meaning no test can ever kill it. This avoids polluting mutation scores with false negatives.
There are two ways to mark equivalence:
At generation time — set
equivalent: truein the mutation map returned bymutate/2. Use this when the mutator knows at generation time that the mutation is equivalent (e.g., swapping arguments to a commutative operator).Via the
equivalent?/1callback — implement this for more complex analysis that needs to inspect the full mutation map. The default implementation checks the:equivalentkey.
Example
defmodule Muex.Mutator.MyMutator do
@behaviour Muex.Mutator
@impl true
def mutate(ast, _context) do
# Return list of mutated AST variants
[mutated_ast_1, mutated_ast_2]
end
@impl true
def name, do: "My Mutator"
@impl true
def description, do: "Mutates specific AST patterns"
@impl true
def supported_languages, do: [Muex.Language.Elixir, Muex.Language.Erlang]
# Optional: override for complex equivalence detection
@impl true
def equivalent?(%{description: "swap arguments in +()" <> _}), do: true
def equivalent?(_mutation), do: false
end
Summary
Types
Represents a single mutation with its metadata.
Callbacks
Returns a description of what this mutator does.
Returns whether a mutation is semantically equivalent to the original code.
Applies mutations to the given AST.
Returns the name of the mutator.
Returns the list of language adapter modules this mutator supports.
Functions
Checks whether a mutation is equivalent, delegating to the mutator module.
Walks through an AST and applies all registered mutators.
Types
@type mutation() :: %{ ast: term(), original_ast: term(), mutator: module(), description: String.t(), location: %{file: String.t(), line: non_neg_integer()} }
Represents a single mutation with its metadata.
The :equivalent key is optional. When true, the mutation is considered
semantically equivalent to the original and will be filtered out by the optimizer.
Callbacks
@callback description() :: String.t()
Returns a description of what this mutator does.
Returns whether a mutation is semantically equivalent to the original code.
Equivalent mutations can never be killed by any test and should be filtered out to avoid inflating the "survived" count.
The default implementation checks for equivalent: true in the mutation map.
Override this callback in your mutator for more sophisticated detection.
Applies mutations to the given AST.
Parameters
ast- The AST to mutatecontext- Map containing additional context (file path, line number, etc.)
Returns
List of mutation maps, each representing a possible mutation
@callback name() :: String.t()
Returns the name of the mutator.
@callback supported_languages() :: [module()]
Returns the list of language adapter modules this mutator supports.
Mutators that work with the same AST format (e.g., BEAM languages like Elixir and Erlang) can declare multiple languages. Discovery will filter mutators based on the active language.
Returns
List of language adapter modules (e.g., [Muex.Language.Elixir, Muex.Language.Erlang])
Functions
Checks whether a mutation is equivalent, delegating to the mutator module.
Falls back to checking the :equivalent key in the mutation map if the
mutator does not implement equivalent?/1.
Walks through an AST and applies all registered mutators.
Parameters
ast- The AST to traversemutators- List of mutator modules to applycontext- Context map with file information
Returns
List of all possible mutations found in the AST