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 /// Override `optimizer_runs` for mutation testing compile-and-test runs.
64 ///
65 /// This lets mutation campaigns use a faster compiler profile without
66 /// changing the project's normal build settings.
67 pub optimizer_runs: Option<u32>,
68 /// Override `via_ir` for mutation testing compile-and-test runs.
69 ///
70 /// This lets mutation campaigns disable the IR pipeline without changing
71 /// the project's normal build settings.
72 pub via_ir: Option<bool>,
73}
74
75impl MutationConfig {
76 /// Returns the list of operators that are currently enabled.
77 ///
78 /// Effective set: `all() - default_excluded - exclude_operators + include_operators`
79 pub fn enabled_operators(&self) -> Vec<MutatorType> {
80 let default_excluded = MutatorType::default_excluded();
81 MutatorType::all()
82 .into_iter()
83 .filter(|op| {
84 let excluded = default_excluded.contains(op) || self.exclude_operators.contains(op);
85 let included = self.include_operators.contains(op);
86 !excluded || included
87 })
88 .collect()
89 }
90}