Rust Mutation Testing Extension

This extension provides rust_mutation_test, a rule that mutation-tests a rust_library by compiling and running tests against automatically-mutated source variants.

Primary API docs are in defs.bzl and generated site docs (rust_mutation.md in the rules_rust docs book).

Overview

rust_mutation_test:

  1. Reads crate metadata from the target rust_library.
  2. Builds rustc arguments via rules_rust's canonical compile wiring (collect_inputs + construct_arguments).
  3. Enumerates mutants with cargo-mutants JSON output.
  4. Stages crate sources and compile-time input files.
  5. Runs a baseline compile+test.
  6. Runs compile+test for each mutant.
  7. Reports mutants as CAUGHT or SURVIVED.

Status meanings:

  • CAUGHT: mutant fails to compile or tests fail.
  • SURVIVED: mutant compiles and tests pass.
  • Baseline/infrastructure failures fail the Bazel test immediately.

Current behavior:

  • By default, any survived mutant fails the Bazel target.
  • Set allow_survivors = True to report survivors without failing the target.
  • If mutation generation produces zero mutants, the Bazel target succeeds and prints No mutations generated..

Setup

Bzlmod

bazel_dep(name = "rules_rust_mutants", version = "{SEE_RELEASE_NOTES}")

WORKSPACE

load("@rules_rust_mutants//:repositories.bzl", "rust_mutation_dependencies")

rust_mutation_dependencies()

Usage

load("@rules_rust//rust:defs.bzl", "rust_library")
load("@rules_rust_mutants//:defs.bzl", "rust_mutation_test")

rust_library(
    name = "my_lib",
    srcs = ["lib.rs"],
    edition = "2021",
)

rust_mutation_test(
    name = "my_lib_mutation_test",
    crate = ":my_lib",
)

Run:

bazel test //:my_lib_mutation_test --test_output=all

cargo-mutants Compatibility

Mutation enumeration uses cargo mutants --list --json --diff --Zmutate-file ....

Configuration

mutants_config = "path/to/mutants.toml" is forwarded as cargo mutants --config <path>.

allow_survivors = True changes survivor handling:

  • Default (False): if any mutant survives, the Bazel test fails.
  • True: survivors are still reported, but the Bazel test exits successfully.

Tooling Model

  • This extension builds a hermetic cargo-mutants binary from source.
  • cargo-mutants internally invokes Cargo; the runner provides Cargo from the active Rust toolchain.
  • Rustc params are generated by a dedicated writer action that materializes canonical rustc flags into a runfiles-compatible params file consumed by the runtime runner.

Output Example

Running baseline compile+test...
Generated 5 mutants
  mutant_0_src/lib.rs_binop - CAUGHT
  mutant_1_src/lib.rs_binop - SURVIVED

Mutation Testing Summary
========================
Total: 5  Caught: 4 (80%)  Survived: 1 (20%)

For survivors, a unified diff is printed for each surviving mutant.

Local Development

  • Run extension-local tests from this repository root.
  • Useful targets:
    • //:clippy
    • //private:runner_unit_test
    • //test:all

rules_rust_mutants

Mutation testing for Rust crates built with rules_rust.

rust_mutation_test enumerates source-level mutations for a rust_library, compiles each mutant with the same rustc configuration as rust_test, and runs the crate's inline #[cfg(test)] tests against each mutant.

Rules

Setup

Bzlmod

Add the following to your MODULE.bazel file:

bazel_dep(name = "rules_rust_mutants", version = "{SEE_RELEASE_NOTES}")

WORKSPACE

If you're using WORKSPACE, load repositories with:

load("@rules_rust_mutants//:repositories.bzl", "rust_mutation_dependencies")

rust_mutation_dependencies()

Usage

load("@rules_rust_mutants//:defs.bzl", "rust_mutation_test")
load("@rules_rust//rust:defs.bzl", "rust_library")

rust_library(
    name = "my_lib",
    srcs = ["lib.rs"],
    edition = "2021",
)

rust_mutation_test(
    name = "my_lib_mutation_test",
    crate = ":my_lib",
)

Run with:

bazel test //:my_lib_mutation_test --test_output=all

Behavior Notes

  • Mutation generation uses cargo-mutants JSON output.
  • Mutation enumeration uses cargo mutants --list --json --diff --Zmutate-file ....
  • Rustc params are generated from rules_rust's canonical argument-construction pipeline (collect_inputs + construct_arguments).
  • mutants_config is forwarded as cargo mutants --config <path>.
  • By default, survived mutants fail the Bazel test target.
  • allow_survivors = True reports survivors without failing.
  • If mutation generation produces zero mutants, the Bazel target succeeds and prints No mutations generated..

Tooling

  • A hermetic cargo-mutants binary is built from source by this extension.
  • A Cargo binary from the active Rust toolchain is used for cargo-mutants internals.


Rules

rust_mutation_test

load("@rules_rust_mutants//:defs.bzl", "rust_mutation_test")

rust_mutation_test(name, allow_survivors, crate, exclude_re, mutants_config, rustc_flags)

Mutation testing for a Rust library crate.

rust_mutation_test:

  1. Enumerates source-level mutants with cargo-mutants.
  2. Builds rustc params from rules_rust's canonical argument construction pipeline (collect_inputs + construct_arguments).
  3. Runs baseline and per-mutant compile+test cycles against inline #[cfg(test)] tests from the crate.
  4. Fails if any mutant survives (unless allow_survivors = True).
  5. Succeeds and prints No mutations generated. when enumeration yields zero mutants.

Mutation enumeration mode:

  • Uses cargo mutants --list --json --diff --Zmutate-file ....

Example:

load("@rules_rust_mutants//:defs.bzl", "rust_mutation_test")

rust_library(
    name = "my_lib",
    srcs = ["lib.rs"],
)

rust_mutation_test(
    name = "my_lib_mutation_test",
    crate = ":my_lib",
)

Run with: bazel test //:my_lib_mutation_test --test_output=all

ATTRIBUTES

NameDescriptionTypeMandatoryDefault
nameA unique name for this target.Namerequired
allow_survivorsIf True, survived mutants are reported but do not fail the test. Default is False (survivors fail).BooleanoptionalFalse
crateThe rust_library crate to mutation-test. The crate's inline #[cfg(test)] tests are compiled and run against each mutation.Labelrequired
exclude_reRegular expression patterns to exclude mutants. Each pattern is forwarded as cargo mutants --exclude-re <pattern>.List of stringsoptional[]
mutants_configOptional cargo-mutants configuration file. It is forwarded as cargo mutants --config <path>. If all mutants are filtered out, the target succeeds and reports no mutants.LabeloptionalNone
rustc_flagsAdditional flags passed through to rustc when compiling the baseline and mutant test binaries.List of stringsoptional[]