Skip to main content

forge/mutation/mutators/
mutator_registry.rs

1use crate::mutation::mutant::Mutant;
2use eyre::Report;
3use foundry_config::MutatorType;
4
5use super::{
6    MutationContext, Mutator, assembly_mutator, assignment_mutator, binary_op_mutator,
7    delete_expression_mutator, elim_delegate_mutator, require_mutator, unary_op_mutator,
8};
9
10/// Registry of all available mutators (ie implementing the Mutator trait)
11pub struct MutatorRegistry {
12    mutators: Vec<Box<dyn Mutator>>,
13}
14
15pub struct MutationGenerationResult {
16    pub mutations: Vec<Mutant>,
17    pub errors: Vec<Report>,
18}
19
20impl MutatorRegistry {
21    #[cfg(test)]
22    pub fn default() -> Self {
23        Self::from_enabled(&MutatorType::all())
24    }
25
26    pub fn from_enabled(enabled: &[MutatorType]) -> Self {
27        let mut registry = Self { mutators: Vec::new() };
28
29        for ty in enabled {
30            match ty {
31                MutatorType::Assembly => {
32                    registry.mutators.push(Box::new(assembly_mutator::AssemblyMutator::new()));
33                }
34                MutatorType::Assignment => {
35                    registry.mutators.push(Box::new(assignment_mutator::AssignmentMutator));
36                }
37                MutatorType::BinaryOp => {
38                    registry.mutators.push(Box::new(binary_op_mutator::BinaryOpMutator));
39                }
40                MutatorType::DeleteExpression => {
41                    registry
42                        .mutators
43                        .push(Box::new(delete_expression_mutator::DeleteExpressionMutator));
44                }
45                MutatorType::ElimDelegate => {
46                    registry.mutators.push(Box::new(elim_delegate_mutator::ElimDelegateMutator));
47                }
48                MutatorType::Require => {
49                    registry.mutators.push(Box::new(require_mutator::RequireMutator));
50                }
51                MutatorType::UnaryOp => {
52                    registry.mutators.push(Box::new(unary_op_mutator::UnaryOpMutator));
53                }
54            }
55        }
56
57        registry
58    }
59
60    #[cfg(test)]
61    pub fn new_with_mutators(mutators: Vec<Box<dyn Mutator>>) -> Self {
62        Self { mutators }
63    }
64
65    /// Find all applicable mutators for a given context and return the corresponding mutations
66    /// and any mutator errors encountered while generating them.
67    pub fn generate_mutations(&self, context: &MutationContext<'_>) -> MutationGenerationResult {
68        let mut mutations = Vec::new();
69        let mut errors = Vec::new();
70        for mutator in self.mutators.iter().filter(|mutator| mutator.is_applicable(context)) {
71            match mutator.generate_mutants(context) {
72                Ok(generated) => mutations.extend(generated),
73                Err(err) => errors.push(err),
74            }
75        }
76        MutationGenerationResult { mutations, errors }
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use eyre::{Result, eyre};
83    use solar::ast::Span;
84
85    use super::*;
86
87    struct FailingMutator;
88
89    impl Mutator for FailingMutator {
90        fn generate_mutants(&self, _ctxt: &MutationContext<'_>) -> Result<Vec<Mutant>> {
91            Err(eyre!("synthetic mutator failure"))
92        }
93
94        fn is_applicable(&self, _ctxt: &MutationContext<'_>) -> bool {
95            true
96        }
97    }
98
99    #[test]
100    fn generate_mutations_collects_mutator_errors() {
101        let registry = MutatorRegistry::new_with_mutators(vec![Box::new(FailingMutator)]);
102        let context = MutationContext::builder()
103            .with_path("test.sol".into())
104            .with_span(Span::DUMMY)
105            .build()
106            .unwrap();
107
108        let result = registry.generate_mutations(&context);
109        assert!(result.mutations.is_empty());
110        let err = result.errors.into_iter().next().unwrap();
111        assert!(err.to_string().contains("synthetic mutator failure"));
112    }
113}