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 SummarizeEventsHandler = Box<
136 dyn FnOnce(&mut ContractSyncRuntimeHandle, Vec<StreamUpdate>) -> Result<(), ExecutionError>
137 + Send
138 + Sync,
139>;
140type FinalizeHandler =
141 Box<dyn FnOnce(&mut ContractSyncRuntimeHandle) -> Result<(), ExecutionError> + Send + Sync>;
142type HandleQueryHandler = Box<
143 dyn FnOnce(&mut ServiceSyncRuntimeHandle, Vec<u8>) -> Result<Vec<u8>, ExecutionError>
144 + Send
145 + Sync,
146>;
147
148#[derive(custom_debug_derive::Debug)]
150pub enum ExpectedCall {
151 Instantiate(#[debug(skip)] InstantiateHandler),
153 ExecuteOperation(#[debug(skip)] ExecuteOperationHandler),
155 ExecuteMessage(#[debug(skip)] ExecuteMessageHandler),
157 ProcessStreams(#[debug(skip)] ProcessStreamHandler),
159 SummarizeEvents(#[debug(skip)] SummarizeEventsHandler),
161 Finalize(#[debug(skip)] FinalizeHandler),
163 HandleQuery(#[debug(skip)] HandleQueryHandler),
165}
166
167impl Display for ExpectedCall {
168 fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
169 let name = match self {
170 ExpectedCall::Instantiate(_) => "instantiate",
171 ExpectedCall::ExecuteOperation(_) => "execute_operation",
172 ExpectedCall::ExecuteMessage(_) => "execute_message",
173 ExpectedCall::ProcessStreams(_) => "process_streams",
174 ExpectedCall::SummarizeEvents(_) => "summarize_events",
175 ExpectedCall::Finalize(_) => "finalize",
176 ExpectedCall::HandleQuery(_) => "handle_query",
177 };
178
179 write!(formatter, "{name}")
180 }
181}
182
183impl ExpectedCall {
184 pub fn instantiate(
187 handler: impl FnOnce(&mut ContractSyncRuntimeHandle, Vec<u8>) -> Result<(), ExecutionError>
188 + Send
189 + Sync
190 + 'static,
191 ) -> Self {
192 ExpectedCall::Instantiate(Box::new(handler))
193 }
194
195 pub fn execute_operation(
199 handler: impl FnOnce(&mut ContractSyncRuntimeHandle, Vec<u8>) -> Result<Vec<u8>, ExecutionError>
200 + Send
201 + Sync
202 + 'static,
203 ) -> Self {
204 ExpectedCall::ExecuteOperation(Box::new(handler))
205 }
206
207 pub fn execute_message(
211 handler: impl FnOnce(&mut ContractSyncRuntimeHandle, Vec<u8>) -> Result<(), ExecutionError>
212 + Send
213 + Sync
214 + 'static,
215 ) -> Self {
216 ExpectedCall::ExecuteMessage(Box::new(handler))
217 }
218
219 pub fn process_streams(
223 handler: impl FnOnce(&mut ContractSyncRuntimeHandle, Vec<StreamUpdate>) -> Result<(), ExecutionError>
224 + Send
225 + Sync
226 + 'static,
227 ) -> Self {
228 ExpectedCall::ProcessStreams(Box::new(handler))
229 }
230
231 pub fn summarize_events(
235 handler: impl FnOnce(&mut ContractSyncRuntimeHandle, Vec<StreamUpdate>) -> Result<(), ExecutionError>
236 + Send
237 + Sync
238 + 'static,
239 ) -> Self {
240 ExpectedCall::SummarizeEvents(Box::new(handler))
241 }
242
243 pub fn finalize(
246 handler: impl FnOnce(&mut ContractSyncRuntimeHandle) -> Result<(), ExecutionError>
247 + Send
248 + Sync
249 + 'static,
250 ) -> Self {
251 ExpectedCall::Finalize(Box::new(handler))
252 }
253
254 pub fn default_finalize() -> Self {
257 Self::finalize(|_| Ok(()))
258 }
259
260 pub fn handle_query(
263 handler: impl FnOnce(&mut ServiceSyncRuntimeHandle, Vec<u8>) -> Result<Vec<u8>, ExecutionError>
264 + Send
265 + Sync
266 + 'static,
267 ) -> Self {
268 ExpectedCall::HandleQuery(Box::new(handler))
269 }
270}
271
272impl UserContractModule for MockApplication {
273 fn instantiate(
274 &self,
275 runtime: ContractSyncRuntimeHandle,
276 ) -> Result<Box<dyn UserContract + 'static>, ExecutionError> {
277 Ok(Box::new(self.create_mock_instance(runtime)))
278 }
279}
280
281impl UserServiceModule for MockApplication {
282 fn instantiate(
283 &self,
284 runtime: ServiceSyncRuntimeHandle,
285 ) -> Result<Box<dyn UserService + 'static>, ExecutionError> {
286 Ok(Box::new(self.create_mock_instance(runtime)))
287 }
288}
289
290impl<Runtime> MockApplicationInstance<Runtime> {
291 fn next_expected_call(&self) -> Option<ExpectedCall> {
293 self.expected_calls
294 .lock()
295 .expect("Queue of expected calls was poisoned")
296 .pop_front()
297 }
298}
299
300impl UserContract for MockApplicationInstance<ContractSyncRuntimeHandle> {
301 fn instantiate(&mut self, argument: Vec<u8>) -> Result<(), ExecutionError> {
302 match self.next_expected_call() {
303 Some(ExpectedCall::Instantiate(handler)) => handler(&mut self.runtime, argument),
304 Some(unexpected_call) => panic!(
305 "Expected a call to `instantiate`, got a call to `{unexpected_call}` instead."
306 ),
307 None => panic!("Unexpected call to `instantiate`"),
308 }
309 }
310
311 fn execute_operation(&mut self, operation: Vec<u8>) -> Result<Vec<u8>, ExecutionError> {
312 match self.next_expected_call() {
313 Some(ExpectedCall::ExecuteOperation(handler)) => handler(&mut self.runtime, operation),
314 Some(unexpected_call) => panic!(
315 "Expected a call to `execute_operation`, got a call to `{unexpected_call}` instead."
316 ),
317 None => panic!("Unexpected call to `execute_operation`"),
318 }
319 }
320
321 fn execute_message(&mut self, message: Vec<u8>) -> Result<(), ExecutionError> {
322 match self.next_expected_call() {
323 Some(ExpectedCall::ExecuteMessage(handler)) => handler(&mut self.runtime, message),
324 Some(unexpected_call) => panic!(
325 "Expected a call to `execute_message`, got a call to `{unexpected_call}` instead."
326 ),
327 None => panic!("Unexpected call to `execute_message`"),
328 }
329 }
330
331 fn process_streams(&mut self, updates: Vec<StreamUpdate>) -> Result<(), ExecutionError> {
332 match self.next_expected_call() {
333 Some(ExpectedCall::ProcessStreams(handler)) => handler(&mut self.runtime, updates),
334 Some(unexpected_call) => panic!(
335 "Expected a call to `process_streams`, got a call to `{unexpected_call}` instead."
336 ),
337 None => panic!("Unexpected call to `process_streams`"),
338 }
339 }
340
341 fn summarize_events(&mut self, updates: Vec<StreamUpdate>) -> Result<(), ExecutionError> {
342 match self.next_expected_call() {
343 Some(ExpectedCall::SummarizeEvents(handler)) => handler(&mut self.runtime, updates),
344 Some(unexpected_call) => panic!(
345 "Expected a call to `summarize_events`, got a call to `{unexpected_call}` instead."
346 ),
347 None => panic!("Unexpected call to `summarize_events`"),
348 }
349 }
350
351 fn finalize(&mut self) -> Result<(), ExecutionError> {
352 match self.next_expected_call() {
353 Some(ExpectedCall::Finalize(handler)) => handler(&mut self.runtime),
354 Some(unexpected_call) => {
355 panic!("Expected a call to `finalize`, got a call to `{unexpected_call}` instead.")
356 }
357 None => panic!("Unexpected call to `finalize`"),
358 }
359 }
360}
361
362impl UserService for MockApplicationInstance<ServiceSyncRuntimeHandle> {
363 fn handle_query(&mut self, query: Vec<u8>) -> Result<Vec<u8>, ExecutionError> {
364 match self.next_expected_call() {
365 Some(ExpectedCall::HandleQuery(handler)) => handler(&mut self.runtime, query),
366 Some(unexpected_call) => panic!(
367 "Expected a call to `handle_query`, got a call to `{unexpected_call}` instead."
368 ),
369 None => panic!("Unexpected call to `handle_query`"),
370 }
371 }
372}