Skip to main content

foundry_config/
mutation.rs

1//! Configuration for mutation testing.
2
3use serde::{Deserialize, Serialize};
4use strum::IntoEnumIterator;
5
6/// Represents each available mutation operator.
7#[derive(
8    Debug,
9    Clone,
10    Copy,
11    PartialEq,
12    Eq,
13    Hash,
14    Serialize,
15    Deserialize,
16    strum::Display,
17    strum::EnumString,
18    strum::EnumIter,
19)]
20#[serde(rename_all = "kebab-case")]
21#[strum(serialize_all = "kebab-case")]
22pub enum MutatorType {
23    Assembly,
24    Assignment,
25    BinaryOp,
26    DeleteExpression,
27    ElimDelegate,
28    Require,
29    UnaryOp,
30}
31
32impl MutatorType {
33    /// Returns a list of all available mutator types.
34    pub fn all() -> Vec<Self> {
35        Self::iter().collect()
36    }
37
38    /// Returns the operators that are excluded by default.
39    pub const fn default_excluded() -> Vec<Self> {
40        Vec::new()
41    }
42}
43
44/// Configuration for mutation testing.
45#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
46pub struct MutationConfig {
47    /// Re-enable operators that are excluded by default.
48    pub include_operators: Vec<MutatorType>,
49    /// Exclude additional operators beyond the defaults.
50    pub exclude_operators: Vec<MutatorType>,
51    /// Per-mutant wall-clock timeout, in seconds.
52    ///
53    /// When set, each mutant's compile-and-test work is bounded by this
54    /// duration; mutants that exceed it are recorded as `TimedOut`. This is
55    /// the analog of `invariant.timeout` for mutation campaigns.
56    ///
57    /// Note: enforcement is best-effort. Background work for a timed-out
58    /// mutant may continue briefly until the underlying compile / test loop
59    /// reaches a checkpoint, but the worker slot is freed immediately so
60    /// other mutants can proceed. Cleanup backlog is bounded by the configured
61    /// mutation worker count.
62    pub timeout: Option<u32>,
63}
64
65impl MutationConfig {
66    /// Returns the list of operators that are currently enabled.
67    ///
68    /// Effective set: `all() - default_excluded - exclude_operators + include_operators`
69    pub fn enabled_operators(&self) -> Vec<MutatorType> {
70        let default_excluded = MutatorType::default_excluded();
71        MutatorType::all()
72            .into_iter()
73            .filter(|op| {
74                let excluded = default_excluded.contains(op) || self.exclude_operators.contains(op);
75                let included = self.include_operators.contains(op);
76                !excluded || included
77            })
78            .collect()
79    }
80}