linera_execution/test_utils/
mock_application.rs1use std::{
7 collections::VecDeque,
8 fmt::{self, Debug, Display, Formatter},
9 sync::{
10 atomic::{AtomicUsize, Ordering},
11 Arc, Mutex,
12 },
13};
14
15#[cfg(web)]
16use js_sys::wasm_bindgen;
17use linera_base::data_types::StreamUpdate;
18
19use crate::{
20 ContractSyncRuntimeHandle, ExecutionError, ServiceSyncRuntimeHandle, UserContract,
21 UserContractModule, UserService, UserServiceModule,
22};
23
24#[cfg_attr(web, wasm_bindgen::prelude::wasm_bindgen)]
29#[derive(Clone, Default)]
30pub struct MockApplication {
31 expected_calls: Arc<Mutex<VecDeque<ExpectedCall>>>,
32 active_instances: Arc<AtomicUsize>,
33}
34
35pub struct MockApplicationInstance<Runtime> {
39 expected_calls: Arc<Mutex<VecDeque<ExpectedCall>>>,
40 runtime: Runtime,
41 active_instances: Arc<AtomicUsize>,
42}
43
44impl MockApplication {
45 pub fn expect_call(&self, expected_call: ExpectedCall) {
47 self.expected_calls
48 .lock()
49 .expect("Mutex is poisoned")
50 .push_back(expected_call);
51 }
52
53 pub fn create_mock_instance<Runtime>(
55 &self,
56 runtime: Runtime,
57 ) -> MockApplicationInstance<Runtime> {
58 self.active_instances.fetch_add(1, Ordering::AcqRel);
59
60 MockApplicationInstance {
61 expected_calls: self.expected_calls.clone(),
62 runtime,
63 active_instances: self.active_instances.clone(),
64 }
65 }
66
67 pub fn assert_no_more_expected_calls(&self) {
69 assert!(
70 self.expected_calls.lock().unwrap().is_empty(),
71 "Missing call to instantiate a `MockApplicationInstance`"
72 );
73 }
74
75 pub fn assert_no_active_instances(&self) {
78 assert_eq!(
79 self.active_instances.load(Ordering::Acquire),
80 0,
81 "At least one of `MockApplicationInstance` is still waiting for expected calls"
82 );
83 }
84}
85
86impl Debug for MockApplication {
87 fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
88 let mut struct_formatter = formatter.debug_struct("MockApplication");
89
90 match self.expected_calls.lock() {
91 Ok(expected_calls) => struct_formatter.field("expected_calls", &*expected_calls),
92 Err(_) => struct_formatter.field("expected_calls", &"[POISONED]"),
93 };
94
95 struct_formatter
96 .field(
97 "active_instances",
98 &self.active_instances.load(Ordering::Acquire),
99 )
100 .finish()
101 }
102}
103
104impl PartialEq for MockApplication {
105 fn eq(&self, other: &Self) -> bool {
106 Arc::ptr_eq(&self.expected_calls, &other.expected_calls)
107 && Arc::ptr_eq(&self.active_instances, &other.active_instances)
108 }
109}
110
111impl Eq for MockApplication {}
112
113impl<Runtime> Drop for MockApplicationInstance<Runtime> {
114 fn drop(&mut self) {
115 self.active_instances.fetch_sub(1, Ordering::AcqRel);
116 }
117}
118
119type InstantiateHandler = Box<
120 dyn FnOnce(&mut ContractSyncRuntimeHandle, Vec<u8>) -> Result<(), ExecutionError> + Send + Sync,
121>;
122type ExecuteOperationHandler = Box<
123 dyn FnOnce(&mut ContractSyncRuntimeHandle, Vec<u8>) -> Result<Vec<u8>, ExecutionError>
124 + Send
125 + Sync,
126>;
127type ExecuteMessageHandler = Box<
128 dyn FnOnce(&mut ContractSyncRuntimeHandle, Vec<u8>) -> Result<(), ExecutionError> + Send + Sync,
129>;
130type ProcessStreamHandler = Box<
131 dyn FnOnce(&mut ContractSyncRuntimeHandle, Vec<StreamUpdate>) -> Result<(), ExecutionError>
132 + Send
133 + Sync,
134>;
135type FinalizeHandler =
136 Box<dyn FnOnce(&mut ContractSyncRuntimeHandle) -> Result<(), ExecutionError> + Send + Sync>;
137type HandleQueryHandler = Box<
138 dyn FnOnce(&mut ServiceSyncRuntimeHandle, Vec<u8>) -> Result<Vec<u8>, ExecutionError>
139 + Send
140 + Sync,
141>;
142
143#[derive(custom_debug_derive::Debug)]
145pub enum ExpectedCall {
146 Instantiate(#[debug(skip)] InstantiateHandler),
148 ExecuteOperation(#[debug(skip)] ExecuteOperationHandler),
150 ExecuteMessage(#[debug(skip)] ExecuteMessageHandler),
152 ProcessStreams(#[debug(skip)] ProcessStreamHandler),
154 Finalize(#[debug(skip)] FinalizeHandler),
156 HandleQuery(#[debug(skip)] HandleQueryHandler),
158}
159
160impl Display for ExpectedCall {
161 fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
162 let name = match self {
163 ExpectedCall::Instantiate(_) => "instantiate",
164 ExpectedCall::ExecuteOperation(_) => "execute_operation",
165 ExpectedCall::ExecuteMessage(_) => "execute_message",
166 ExpectedCall::ProcessStreams(_) => "process_streams",
167 ExpectedCall::Finalize(_) => "finalize",
168 ExpectedCall::HandleQuery(_) => "handle_query",
169 };
170
171 write!(formatter, "{name}")
172 }
173}
174
175impl ExpectedCall {
176 pub fn instantiate(
179 handler: impl FnOnce(&mut ContractSyncRuntimeHandle, Vec<u8>) -> Result<(), ExecutionError>
180 + Send
181 + Sync
182 + 'static,
183 ) -> Self {
184 ExpectedCall::Instantiate(Box::new(handler))
185 }
186
187 pub fn execute_operation(
191 handler: impl FnOnce(&mut ContractSyncRuntimeHandle, Vec<u8>) -> Result<Vec<u8>, ExecutionError>
192 + Send
193 + Sync
194 + 'static,
195 ) -> Self {
196 ExpectedCall::ExecuteOperation(Box::new(handler))
197 }
198
199 pub fn execute_message(
203 handler: impl FnOnce(&mut ContractSyncRuntimeHandle, Vec<u8>) -> Result<(), ExecutionError>
204 + Send
205 + Sync
206 + 'static,
207 ) -> Self {
208 ExpectedCall::ExecuteMessage(Box::new(handler))
209 }
210
211 pub fn process_streams(
215 handler: impl FnOnce(&mut ContractSyncRuntimeHandle, Vec<StreamUpdate>) -> Result<(), ExecutionError>
216 + Send
217 + Sync
218 + 'static,
219 ) -> Self {
220 ExpectedCall::ProcessStreams(Box::new(handler))
221 }
222
223 pub fn finalize(
226 handler: impl FnOnce(&mut ContractSyncRuntimeHandle) -> Result<(), ExecutionError>
227 + Send
228 + Sync
229 + 'static,
230 ) -> Self {
231 ExpectedCall::Finalize(Box::new(handler))
232 }
233
234 pub fn default_finalize() -> Self {
237 Self::finalize(|_| Ok(()))
238 }
239
240 pub fn handle_query(
243 handler: impl FnOnce(&mut ServiceSyncRuntimeHandle, Vec<u8>) -> Result<Vec<u8>, ExecutionError>
244 + Send
245 + Sync
246 + 'static,
247 ) -> Self {
248 ExpectedCall::HandleQuery(Box::new(handler))
249 }
250}
251
252impl UserContractModule for MockApplication {
253 fn instantiate(
254 &self,
255 runtime: ContractSyncRuntimeHandle,
256 ) -> Result<Box<dyn UserContract + 'static>, ExecutionError> {
257 Ok(Box::new(self.create_mock_instance(runtime)))
258 }
259}
260
261impl UserServiceModule for MockApplication {
262 fn instantiate(
263 &self,
264 runtime: ServiceSyncRuntimeHandle,
265 ) -> Result<Box<dyn UserService + 'static>, ExecutionError> {
266 Ok(Box::new(self.create_mock_instance(runtime)))
267 }
268}
269
270impl<Runtime> MockApplicationInstance<Runtime> {
271 fn next_expected_call(&mut self) -> Option<ExpectedCall> {
273 self.expected_calls
274 .lock()
275 .expect("Queue of expected calls was poisoned")
276 .pop_front()
277 }
278}
279
280impl UserContract for MockApplicationInstance<ContractSyncRuntimeHandle> {
281 fn instantiate(&mut self, argument: Vec<u8>) -> Result<(), ExecutionError> {
282 match self.next_expected_call() {
283 Some(ExpectedCall::Instantiate(handler)) => handler(&mut self.runtime, argument),
284 Some(unexpected_call) => panic!(
285 "Expected a call to `instantiate`, got a call to `{unexpected_call}` instead."
286 ),
287 None => panic!("Unexpected call to `instantiate`"),
288 }
289 }
290
291 fn execute_operation(&mut self, operation: Vec<u8>) -> Result<Vec<u8>, ExecutionError> {
292 match self.next_expected_call() {
293 Some(ExpectedCall::ExecuteOperation(handler)) => handler(&mut self.runtime, operation),
294 Some(unexpected_call) => panic!(
295 "Expected a call to `execute_operation`, got a call to `{unexpected_call}` instead."
296 ),
297 None => panic!("Unexpected call to `execute_operation`"),
298 }
299 }
300
301 fn execute_message(&mut self, message: Vec<u8>) -> Result<(), ExecutionError> {
302 match self.next_expected_call() {
303 Some(ExpectedCall::ExecuteMessage(handler)) => handler(&mut self.runtime, message),
304 Some(unexpected_call) => panic!(
305 "Expected a call to `execute_message`, got a call to `{unexpected_call}` instead."
306 ),
307 None => panic!("Unexpected call to `execute_message`"),
308 }
309 }
310
311 fn process_streams(&mut self, updates: Vec<StreamUpdate>) -> Result<(), ExecutionError> {
312 match self.next_expected_call() {
313 Some(ExpectedCall::ProcessStreams(handler)) => handler(&mut self.runtime, updates),
314 Some(unexpected_call) => panic!(
315 "Expected a call to `process_streams`, got a call to `{unexpected_call}` instead."
316 ),
317 None => panic!("Unexpected call to `process_streams`"),
318 }
319 }
320
321 fn finalize(&mut self) -> Result<(), ExecutionError> {
322 match self.next_expected_call() {
323 Some(ExpectedCall::Finalize(handler)) => handler(&mut self.runtime),
324 Some(unexpected_call) => {
325 panic!("Expected a call to `finalize`, got a call to `{unexpected_call}` instead.")
326 }
327 None => panic!("Unexpected call to `finalize`"),
328 }
329 }
330}
331
332impl UserService for MockApplicationInstance<ServiceSyncRuntimeHandle> {
333 fn handle_query(&mut self, query: Vec<u8>) -> Result<Vec<u8>, ExecutionError> {
334 match self.next_expected_call() {
335 Some(ExpectedCall::HandleQuery(handler)) => handler(&mut self.runtime, query),
336 Some(unexpected_call) => panic!(
337 "Expected a call to `handle_query`, got a call to `{unexpected_call}` instead."
338 ),
339 None => panic!("Unexpected call to `handle_query`"),
340 }
341 }
342}