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