1use std::{cell::RefCell, future::Future, rc::Rc, time::Duration};
8
9use futures::{channel::oneshot, future::LocalBoxFuture, task::SpawnError};
10use lenso_kernel::{DeterministicDriver, DriverTask, RuntimeDriver};
11
12use crate::FaultInjector;
13
14#[derive(Clone, Debug, Default)]
16pub struct TestSimulator {
17 driver: DeterministicDriver,
18 faults: FaultInjector,
19}
20
21impl TestSimulator {
22 pub fn new() -> Self {
24 Self::default()
25 }
26
27 pub fn gate(&self, name: impl Into<String>) -> SimulatorGate {
29 SimulatorGate {
30 name: name.into(),
31 state: Rc::new(RefCell::new(GateState::default())),
32 }
33 }
34
35 pub fn resource(&self, name: impl Into<String>) -> SimulatorResource {
42 SimulatorResource {
43 name: name.into(),
44 state: Rc::new(RefCell::new(ResourceState::default())),
45 }
46 }
47
48 pub fn faults(&self) -> FaultInjector {
54 self.faults.clone()
55 }
56
57 pub fn run<F: Future>(&self, future: F) -> F::Output {
63 self.driver.run(future)
64 }
65
66 pub fn pump(&self) {
72 self.run(self.driver.yield_now());
73 }
74
75 pub fn spawn<F>(&self, future: F) -> Result<DriverTask, SpawnError>
77 where
78 F: Future<Output = ()> + 'static,
79 {
80 self.driver.spawn_local(Box::pin(future))
81 }
82
83 pub fn advance(&self, duration: Duration) {
85 self.driver.advance(duration);
86 }
87
88 pub fn now(&self) -> Duration {
90 self.driver.now()
91 }
92
93 pub fn sleep_until(&self, deadline: Duration) -> LocalBoxFuture<'static, ()> {
101 self.driver.sleep_until(deadline)
102 }
103
104 pub fn driver(&self) -> DeterministicDriver {
107 self.driver.clone()
108 }
109}
110
111#[derive(Debug, Default)]
112struct ResourceState {
113 acquisitions: usize,
114 frozen: bool,
115 thaw_notifiers: Vec<oneshot::Sender<()>>,
116}
117
118#[derive(Clone, Debug)]
124pub struct SimulatorResource {
125 name: String,
126 state: Rc<RefCell<ResourceState>>,
127}
128
129impl SimulatorResource {
130 pub fn name(&self) -> &str {
132 &self.name
133 }
134
135 pub async fn acquire(&self) {
137 loop {
138 let thaw = {
139 let mut state = self.state.borrow_mut();
140 if state.frozen {
141 let (sender, receiver) = oneshot::channel();
142 state.thaw_notifiers.push(sender);
143 Some(receiver)
144 } else {
145 state.acquisitions += 1;
146 None
147 }
148 };
149
150 let Some(thaw) = thaw else {
151 return;
152 };
153 let _ = thaw.await;
154 }
155 }
156
157 #[must_use]
161 pub fn freeze(&self) -> bool {
162 let mut state = self.state.borrow_mut();
163 if state.frozen {
164 return false;
165 }
166 state.frozen = true;
167 true
168 }
169
170 #[must_use]
174 pub fn thaw(&self) -> bool {
175 let notifiers = {
176 let mut state = self.state.borrow_mut();
177 if !state.frozen {
178 return false;
179 }
180 state.frozen = false;
181 std::mem::take(&mut state.thaw_notifiers)
182 };
183 for notifier in notifiers {
184 let _ = notifier.send(());
185 }
186 true
187 }
188
189 pub fn acquisition_count(&self) -> usize {
191 self.state.borrow().acquisitions
192 }
193
194 pub fn is_frozen(&self) -> bool {
196 self.state.borrow().frozen
197 }
198}
199
200#[derive(Debug, Default)]
201struct GateState {
202 reached: usize,
203 released: bool,
204 reached_notifiers: Vec<oneshot::Sender<()>>,
205 release_notifiers: Vec<oneshot::Sender<()>>,
206}
207
208#[derive(Clone, Debug)]
210pub struct SimulatorGate {
211 name: String,
212 state: Rc<RefCell<GateState>>,
213}
214
215impl SimulatorGate {
216 pub fn name(&self) -> &str {
218 &self.name
219 }
220
221 pub async fn wait(&self) {
223 let release = {
224 let mut state = self.state.borrow_mut();
225 state.reached += 1;
226 for notifier in state.reached_notifiers.drain(..) {
227 let _ = notifier.send(());
228 }
229 if state.released {
230 None
231 } else {
232 let (sender, receiver) = oneshot::channel();
233 state.release_notifiers.push(sender);
234 Some(receiver)
235 }
236 };
237
238 if let Some(release) = release {
239 let _ = release.await;
240 }
241 }
242
243 pub async fn reached(&self) {
245 let notification = {
246 let mut state = self.state.borrow_mut();
247 if state.reached > 0 {
248 None
249 } else {
250 let (sender, receiver) = oneshot::channel();
251 state.reached_notifiers.push(sender);
252 Some(receiver)
253 }
254 };
255
256 if let Some(notification) = notification {
257 let _ = notification.await;
258 }
259 }
260
261 #[must_use]
265 pub fn release(&self) -> bool {
266 let notifiers = {
267 let mut state = self.state.borrow_mut();
268 if state.released {
269 return false;
270 }
271 state.released = true;
272 std::mem::take(&mut state.release_notifiers)
273 };
274 for notifier in notifiers {
275 let _ = notifier.send(());
276 }
277 true
278 }
279
280 pub fn reached_count(&self) -> usize {
282 self.state.borrow().reached
283 }
284
285 pub fn is_released(&self) -> bool {
287 self.state.borrow().released
288 }
289}
290
291#[cfg(test)]
292mod tests {
293 use std::{cell::RefCell, rc::Rc};
294
295 use futures::FutureExt;
296 use lenso_kernel::TaskOutcome;
297
298 use super::*;
299
300 #[test]
301 fn gate_blocks_work_until_the_test_releases_it() {
302 let simulator = TestSimulator::new();
303 let gate = simulator.gate("provider.before-response");
304 let task = simulator
305 .spawn({
306 let gate = gate.clone();
307 async move {
308 gate.wait().await;
309 }
310 })
311 .unwrap();
312
313 simulator.run(gate.reached());
314 assert_eq!(gate.name(), "provider.before-response");
315 assert_eq!(gate.reached_count(), 1);
316 assert!(!gate.is_released());
317 assert!(gate.release());
318 assert!(!gate.release());
319 assert_eq!(simulator.run(task), TaskOutcome::Completed);
320 }
321
322 #[test]
323 fn gates_allow_test_chosen_completion_order() {
324 let simulator = TestSimulator::new();
325 let first = simulator.gate("operation.first");
326 let second = simulator.gate("operation.second");
327 let completed = Rc::new(RefCell::new(Vec::new()));
328
329 let first_task = simulator
330 .spawn({
331 let first = first.clone();
332 let completed = completed.clone();
333 async move {
334 first.wait().await;
335 completed.borrow_mut().push("first");
336 }
337 })
338 .unwrap();
339 let second_task = simulator
340 .spawn({
341 let second = second.clone();
342 let completed = completed.clone();
343 async move {
344 second.wait().await;
345 completed.borrow_mut().push("second");
346 }
347 })
348 .unwrap();
349
350 simulator.run(async {
351 first.reached().await;
352 second.reached().await;
353 });
354 assert!(second.release());
355 assert_eq!(simulator.run(second_task), TaskOutcome::Completed);
356 assert_eq!(&*completed.borrow(), &["second"]);
357
358 assert!(first.release());
359 assert_eq!(simulator.run(first_task), TaskOutcome::Completed);
360 assert_eq!(&*completed.borrow(), &["second", "first"]);
361 }
362
363 #[test]
364 fn cancelling_one_blocked_operation_does_not_release_its_peer() {
365 let simulator = TestSimulator::new();
366 let cancelled = simulator.gate("operation.cancelled");
367 let peer = simulator.gate("operation.peer");
368 let cancelled_task = simulator
369 .spawn({
370 let cancelled = cancelled.clone();
371 async move {
372 cancelled.wait().await;
373 }
374 })
375 .unwrap();
376 let peer_task = simulator
377 .spawn({
378 let peer = peer.clone();
379 async move {
380 peer.wait().await;
381 }
382 })
383 .unwrap();
384
385 simulator.run(async {
386 cancelled.reached().await;
387 peer.reached().await;
388 });
389 cancelled_task.cancel();
390 assert_eq!(simulator.run(cancelled_task), TaskOutcome::Cancelled);
391 assert!(!peer.is_released());
392
393 assert!(peer.release());
394 assert_eq!(simulator.run(peer_task), TaskOutcome::Completed);
395 }
396
397 #[test]
398 fn advancing_virtual_time_wakes_elapsed_work_when_the_test_pumps() {
399 let simulator = TestSimulator::new();
400 let completed = Rc::new(RefCell::new(false));
401 let sleep = simulator.sleep_until(Duration::from_millis(5));
402 let task = simulator
403 .spawn({
404 let completed = completed.clone();
405 async move {
406 sleep.await;
407 *completed.borrow_mut() = true;
408 }
409 })
410 .unwrap();
411
412 simulator.pump();
413 assert!(!*completed.borrow());
414 simulator.advance(Duration::from_millis(4));
415 simulator.pump();
416 assert!(!*completed.borrow());
417 simulator.advance(Duration::from_millis(1));
418 assert_eq!(simulator.now(), Duration::from_millis(5));
419 assert!(!*completed.borrow());
420 simulator.pump();
421 assert!(*completed.borrow());
422 assert_eq!(simulator.run(task), TaskOutcome::Completed);
423 }
424
425 #[test]
426 fn sleeping_at_or_before_now_completes_without_advancing_time() {
427 let simulator = TestSimulator::new();
428 simulator.advance(Duration::from_millis(5));
429
430 for deadline in [Duration::from_millis(4), simulator.now()] {
431 assert_eq!(simulator.sleep_until(deadline).now_or_never(), Some(()));
432 }
433 assert_eq!(simulator.now(), Duration::from_millis(5));
434 }
435
436 #[test]
437 fn dropping_and_cancelling_sleepers_does_not_complete_peer_work() {
438 let simulator = TestSimulator::new();
439 let completed = Rc::new(RefCell::new(Vec::new()));
440 let deadline = Duration::from_millis(5);
441 drop(simulator.sleep_until(deadline));
442
443 let cancelled = simulator
444 .spawn({
445 let sleep = simulator.sleep_until(deadline);
446 let completed = completed.clone();
447 async move {
448 sleep.await;
449 completed.borrow_mut().push("cancelled");
450 }
451 })
452 .unwrap();
453 let peer = simulator
454 .spawn({
455 let sleep = simulator.sleep_until(deadline);
456 let completed = completed.clone();
457 async move {
458 sleep.await;
459 completed.borrow_mut().push("peer");
460 }
461 })
462 .unwrap();
463
464 simulator.pump();
465 cancelled.cancel();
466 assert_eq!(simulator.run(cancelled), TaskOutcome::Cancelled);
467 simulator.advance(Duration::from_millis(4));
468 simulator.pump();
469 assert!(completed.borrow().is_empty());
470
471 simulator.advance(Duration::from_millis(1));
472 assert_eq!(simulator.run(peer), TaskOutcome::Completed);
473 assert_eq!(&*completed.borrow(), &["peer"]);
474 }
475
476 #[test]
477 fn freezing_one_resource_owner_does_not_block_another() {
478 let simulator = TestSimulator::new();
479 let auth_store = simulator.resource("auth-store");
480 let object_store = simulator.resource("object-store");
481 assert!(auth_store.freeze());
482 assert!(!auth_store.freeze());
483
484 let blocked = simulator
485 .spawn({
486 let auth_store = auth_store.clone();
487 async move {
488 auth_store.acquire().await;
489 }
490 })
491 .unwrap();
492 let independent = simulator
493 .spawn({
494 let object_store = object_store.clone();
495 async move {
496 object_store.acquire().await;
497 }
498 })
499 .unwrap();
500
501 simulator.pump();
502 assert_eq!(auth_store.acquisition_count(), 0);
503 assert_eq!(object_store.acquisition_count(), 1);
504 assert_eq!(simulator.run(independent), TaskOutcome::Completed);
505 assert!(auth_store.is_frozen());
506
507 assert!(auth_store.thaw());
508 assert!(!auth_store.thaw());
509 assert_eq!(simulator.run(blocked), TaskOutcome::Completed);
510 assert_eq!(auth_store.acquisition_count(), 1);
511 }
512}