How cargo-mutants works
The basic approach is:
-
Build a list of mutations:
- Run
cargo metadata
to find directories containing Rust source files. - Walk all source files and parse each one looking for functions.
- Skip functions that should not be mutated for any of several reasons:
because they're tests, because they have a
#[mutants::skip]
attribute, etc. - For each function, depending on its return type, generate every mutation pattern that produces a result of that type.
- Run
-
Make a copy of the source tree into a scratch directory, excluding version-control directories like
.git
and the/target
directory. The same directory is reused across all the mutations to benefit from incremental builds.- After copying the tree, cargo-mutants scans the top-level
Cargo.toml
and any.cargo/config.toml
for relative dependencies. If there are any, the paths are rewritten to be absolute, so that they still work when cargo is run in the scratch directory. - Before applying any mutations, check that
cargo test
succeeds in the scratch directory: perhaps a test is already broken, or perhaps the tree doesn't build when copied because it relies on relative paths to find dependencies, etc. This is called the "baseline" test. - If running more than one parallel job, make the appropriate number of additional scratch directories.
- After copying the tree, cargo-mutants scans the top-level
-
For each mutation:
- Apply the mutation to the scratch tree by patching the affected file.
- Run
cargo test --no-run
: if this fails, the mutant is unviable, and that's ok. - Run
cargo test
in the tree, saving output to a log file. - If the the tests fail, that's good: the mutation was somehow caught.
- If the tests succeed, that might mean test coverage was inadequate, or it might mean we accidentally generated a no-op mutation.
- Revert the mutation to return the tree to its clean state.
The file is parsed using the syn
crate, but mutations
are applied textually, rather than to the token stream, so that unmutated code
retains its prior formatting, comments, line numbers, etc. This makes it
possible to show a text diff of the mutation and should make it easier to
understand any error messages from the build of the mutated code.
For more details, see DESIGN.md.