multiversx_sc_scenario/scenario/run_vm/
executor_config.rs

1use multiversx_chain_vm::host::runtime::RuntimeWeakRef;
2use multiversx_chain_vm_executor::Executor;
3
4/// Function that creates an executor from a runtime reference.
5///
6/// Created specifically to avoid referencing the Wasmer 2.2 crate from the VM.
7pub type CustomExecutorFn = fn(RuntimeWeakRef) -> Box<dyn Executor + Send + Sync>;
8
9#[derive(Default, Clone, Debug)]
10pub enum ExecutorConfig {
11    /// Uses the debugger infrastructure: testing the smart contract code directly.
12    #[default]
13    Debugger,
14
15    /// Use to add a custom executor builder function in the contract crate (via dependency inversion).
16    ///
17    /// Created specifically to avoid referencing the Wasmer 2.2 crate from the VM.
18    Custom(CustomExecutorFn),
19
20    /// Use the compiled contract in the experimental Wasmer 6 executor.
21    Experimental,
22
23    /// If feature `compiled-sc-tests` is active, use the given config. Otherwise use fallback config.
24    CompiledFeatureIfElse {
25        if_compiled: Box<ExecutorConfig>,
26        fallback: Box<ExecutorConfig>,
27    },
28
29    /// Defines a list of executors, to be used in order.
30    /// If one of them refuses to execute, the next one is used as fallback.
31    Composite(Vec<ExecutorConfig>),
32}
33
34impl ExecutorConfig {
35    /// Try using the current config, if it cannot be used, attempt the same with the next one.
36    pub fn then(self, next: Self) -> Self {
37        match self {
38            Self::Composite(mut list) => {
39                next.append_flattened_to_vec(&mut list);
40                Self::from_list(list)
41            }
42            _ => {
43                let mut list = Vec::new();
44                self.append_flattened_to_vec(&mut list);
45                next.append_flattened_to_vec(&mut list);
46                Self::from_list(list)
47            }
48        }
49    }
50
51    /// Use the compiled contract only if feature `compiled-sc-tests` is active. Otherwise use fallback config.
52    pub fn compiled_tests_if_else(if_compiled: Self, fallback: Self) -> Self {
53        Self::CompiledFeatureIfElse {
54            if_compiled: Box::new(if_compiled),
55            fallback: Box::new(fallback),
56        }
57    }
58
59    /// Run the compiled contract with the experimental executor if feature `compiled-sc-tests` is active. Otherwise use fallback config.
60    pub fn compiled_tests_or(fallback: Self) -> Self {
61        Self::compiled_tests_if_else(Self::Experimental, fallback)
62    }
63
64    /// Tests with:
65    /// - compiled tests (if feature `compiled-sc-tests` is active),
66    /// - otherwise:
67    ///     - first try the debugger,
68    ///     - then finally the experimental Wasmer.
69    ///
70    /// This means contracts will be tested natively in the wasm tests.
71    pub fn full_suite() -> Self {
72        Self::compiled_tests_or(Self::Debugger.then(Self::Experimental))
73    }
74
75    fn from_list(mut list: Vec<ExecutorConfig>) -> Self {
76        if list.len() == 1 {
77            list.pop().unwrap()
78        } else {
79            Self::Composite(list)
80        }
81    }
82
83    fn append_flattened_to_vec(self, destination: &mut Vec<ExecutorConfig>) {
84        match self {
85            Self::Composite(list) => {
86                for item in list {
87                    item.append_flattened_to_vec(destination);
88                }
89            }
90            _ => {
91                destination.push(self);
92            }
93        }
94    }
95}
96
97impl PartialEq for ExecutorConfig {
98    fn eq(&self, other: &Self) -> bool {
99        use ExecutorConfig::*;
100        match (self, other) {
101            (Debugger, Debugger) => true,
102            (Experimental, Experimental) => true,
103            (
104                CompiledFeatureIfElse {
105                    if_compiled: a1,
106                    fallback: b1,
107                },
108                CompiledFeatureIfElse {
109                    if_compiled: a2,
110                    fallback: b2,
111                },
112            ) => a1 == a2 && b1 == b2,
113            (Composite(v1), Composite(v2)) => v1 == v2,
114            // Custom executor functions cannot be compared, so not considered equal
115            (Custom(_), Custom(_)) => false,
116            _ => false,
117        }
118    }
119}
120
121impl Eq for ExecutorConfig {}
122
123#[cfg(test)]
124mod tests {
125    use super::*;
126
127    #[test]
128    fn executor_config_then_test() {
129        assert_eq!(
130            ExecutorConfig::Debugger.then(ExecutorConfig::Experimental),
131            ExecutorConfig::Composite(vec![ExecutorConfig::Debugger, ExecutorConfig::Experimental])
132        );
133
134        assert_eq!(
135            ExecutorConfig::Debugger
136                .then(ExecutorConfig::Experimental)
137                .then(ExecutorConfig::Debugger),
138            ExecutorConfig::Composite(vec![
139                ExecutorConfig::Debugger,
140                ExecutorConfig::Experimental,
141                ExecutorConfig::Debugger
142            ])
143        );
144    }
145
146    #[test]
147    fn executor_config_flatten_test() {
148        assert_eq!(
149            ExecutorConfig::Debugger
150                .then(ExecutorConfig::Experimental.then(ExecutorConfig::Debugger)),
151            ExecutorConfig::Composite(vec![
152                ExecutorConfig::Debugger,
153                ExecutorConfig::Experimental,
154                ExecutorConfig::Debugger
155            ])
156        );
157    }
158
159    #[test]
160    fn executor_config_full_suite() {
161        assert_eq!(
162            ExecutorConfig::full_suite(),
163            ExecutorConfig::CompiledFeatureIfElse {
164                if_compiled: Box::new(ExecutorConfig::Experimental),
165                fallback: Box::new(ExecutorConfig::Composite(vec![
166                    ExecutorConfig::Debugger,
167                    ExecutorConfig::Experimental,
168                ]))
169            }
170        );
171    }
172}