Skip to main content

commonware_runtime/
mocks.rs

1//! Mock implementations of runtime primitives for testing.
2
3use crate::{
4    signal::Signal,
5    telemetry::metrics::{Metric, Registered},
6    Blob, BufMut, BufferPool, BufferPooler, Clock, Error, Handle, IoBufs, IoBufsMut, Metrics, Name,
7    Spawner, Storage, Supervisor,
8};
9use bytes::{Bytes, BytesMut};
10use commonware_utils::{
11    channel::{fallible::OneshotExt, oneshot},
12    sync::Mutex,
13};
14use governor::clock::{Clock as GovernorClock, ReasonablyRealtime};
15use rand::{TryCryptoRng, TryRng};
16use std::{future::Future, mem, sync::Arc};
17
18/// Default buffer size (64 KB). Controls both how much data the stream
19/// pulls per recv and the backpressure threshold for send.
20const DEFAULT_BUFFER_SIZE: usize = 64 * 1024;
21
22/// A mock channel struct that is used internally by Sink and Stream.
23pub struct Channel {
24    /// Stores the bytes sent by the sink that are not yet read by the stream.
25    buffer: BytesMut,
26
27    /// If the stream is waiting to read bytes, the waiter stores the number of
28    /// bytes that the stream is waiting for, as well as the oneshot sender that
29    /// the sink uses to send the bytes to the stream directly.
30    waiter: Option<(usize, oneshot::Sender<Bytes>)>,
31
32    /// Target buffer size, used to bound both the stream's local buffer
33    /// and the shared buffer (backpressure threshold).
34    buffer_size: usize,
35
36    /// If the sink is blocked waiting for the buffer to drain, this holds
37    /// the oneshot sender that the stream uses to wake the sink.
38    drain_waiter: Option<oneshot::Sender<()>>,
39
40    /// Tracks whether the sink is still alive and able to send messages.
41    sink_alive: bool,
42
43    /// Tracks whether the stream is still alive and able to receive messages.
44    stream_alive: bool,
45}
46
47impl Channel {
48    /// Returns an async-safe Sink/Stream pair with default buffer size.
49    pub fn init() -> (Sink, Stream) {
50        Self::init_with_buffer_size(DEFAULT_BUFFER_SIZE)
51    }
52
53    /// Returns an async-safe Sink/Stream pair with the specified buffer size.
54    pub fn init_with_buffer_size(buffer_size: usize) -> (Sink, Stream) {
55        let channel = Arc::new(Mutex::new(Self {
56            buffer: BytesMut::new(),
57            waiter: None,
58            buffer_size,
59            drain_waiter: None,
60            sink_alive: true,
61            stream_alive: true,
62        }));
63        (
64            Sink {
65                channel: channel.clone(),
66                state: SinkState::Open,
67            },
68            Stream {
69                channel,
70                buffer: BytesMut::new(),
71                poisoned: false,
72            },
73        )
74    }
75
76    /// Restores bytes that were detached from the front of the shared buffer.
77    fn restore_front(&mut self, data: Bytes) {
78        if data.is_empty() {
79            return;
80        }
81
82        let mut restored = BytesMut::with_capacity(data.len() + self.buffer.len());
83        restored.extend_from_slice(&data);
84        restored.extend_from_slice(&self.buffer);
85        self.buffer = restored;
86    }
87
88    /// Marks the sink as closed and wakes any waiter.
89    fn close_sink(&mut self) {
90        self.sink_alive = false;
91
92        // If there is a waiter, resolve it by dropping the oneshot sender.
93        self.waiter.take();
94    }
95}
96
97struct RecvWaiterGuard {
98    channel: Arc<Mutex<Channel>>,
99    active: bool,
100}
101
102impl RecvWaiterGuard {
103    const fn new(channel: Arc<Mutex<Channel>>) -> Self {
104        Self {
105            channel,
106            active: true,
107        }
108    }
109
110    const fn disarm(&mut self) {
111        self.active = false;
112    }
113}
114
115impl Drop for RecvWaiterGuard {
116    fn drop(&mut self) {
117        if !self.active {
118            return;
119        }
120
121        self.channel.lock().waiter.take();
122    }
123}
124
125/// A mock sink that implements the Sink trait.
126pub struct Sink {
127    channel: Arc<Mutex<Channel>>,
128    state: SinkState,
129}
130
131/// Lifecycle state for the mock sink half.
132enum SinkState {
133    /// Sends may be attempted.
134    Open,
135    /// A send is currently in progress.
136    Sending,
137    /// The sink has been closed.
138    Closed,
139}
140
141impl Sink {
142    fn close(&mut self) {
143        if matches!(self.state, SinkState::Closed) {
144            return;
145        }
146        self.channel.lock().close_sink();
147        self.state = SinkState::Closed;
148    }
149}
150
151impl crate::Sink for Sink {
152    async fn send(&mut self, bufs: impl Into<IoBufs> + Send) -> Result<(), Error> {
153        match self.state {
154            SinkState::Open => {}
155            SinkState::Sending => {
156                self.close();
157                return Err(Error::Closed);
158            }
159            SinkState::Closed => return Err(Error::Closed),
160        }
161
162        let drain_recv = {
163            let mut channel = self.channel.lock();
164
165            // If the receiver is dead, we cannot send any more messages.
166            if !channel.stream_alive {
167                channel.close_sink();
168                self.state = SinkState::Closed;
169                return Err(Error::SendFailed);
170            }
171
172            channel.buffer.put(bufs.into());
173
174            // If there is a waiter and the buffer is large enough,
175            // resolve the waiter (while clearing the waiter field).
176            if channel
177                .waiter
178                .as_ref()
179                .is_some_and(|(requested, _)| *requested <= channel.buffer.len())
180            {
181                // Send up to buffer_size bytes (but at least requested amount)
182                let (requested, os_send) = channel.waiter.take().unwrap();
183                let send_amount = channel.buffer.len().min(requested.max(channel.buffer_size));
184                let data = channel.buffer.split_to(send_amount).freeze();
185
186                // A canceled recv should behave like a buffered transport:
187                // preserve the bytes and allow a subsequent recv to consume them.
188                if let Err(data) = os_send.send(data) {
189                    channel.restore_front(data);
190                    if !channel.stream_alive {
191                        channel.close_sink();
192                        self.state = SinkState::Closed;
193                        return Err(Error::SendFailed);
194                    }
195                }
196            }
197
198            // If the buffer exceeds the write limit, block until the
199            // receiver drains enough data.
200            if channel.buffer.len() > channel.buffer_size {
201                assert!(channel.drain_waiter.is_none());
202                let (os_send, os_recv) = oneshot::channel();
203                channel.drain_waiter = Some(os_send);
204                os_recv
205            } else {
206                return Ok(());
207            }
208        };
209
210        // Mark the sink as sending before awaiting so cancellation can be
211        // detected by the next send.
212        self.state = SinkState::Sending;
213
214        // Wait for the receiver to drain the buffer.
215        match drain_recv.await {
216            Ok(()) => {
217                self.state = SinkState::Open;
218                Ok(())
219            }
220            Err(_) => {
221                self.close();
222                Err(Error::SendFailed)
223            }
224        }
225    }
226}
227
228impl Drop for Sink {
229    fn drop(&mut self) {
230        self.close();
231    }
232}
233
234/// A mock stream that implements the Stream trait.
235pub struct Stream {
236    channel: Arc<Mutex<Channel>>,
237    /// Local buffer for data that has been received but not yet consumed.
238    buffer: BytesMut,
239    poisoned: bool,
240}
241
242impl crate::Stream for Stream {
243    async fn recv(&mut self, len: usize) -> Result<IoBufs, Error> {
244        if self.poisoned {
245            return Err(Error::Closed);
246        }
247
248        let os_recv = {
249            let mut channel = self.channel.lock();
250
251            // Pull data from channel buffer into local buffer.
252            let target = len.max(channel.buffer_size);
253            let pull_amount = channel
254                .buffer
255                .len()
256                .min(target.saturating_sub(self.buffer.len()));
257            if pull_amount > 0 {
258                let data = channel.buffer.split_to(pull_amount);
259                self.buffer.extend_from_slice(&data);
260
261                // Wake a blocked sender if the buffer drained below the limit.
262                if channel.buffer.len() <= channel.buffer_size {
263                    if let Some(sender) = channel.drain_waiter.take() {
264                        sender.send_lossy(());
265                    }
266                }
267            }
268
269            // If we have enough, return immediately.
270            if self.buffer.len() >= len {
271                return Ok(IoBufs::from(self.buffer.split_to(len).freeze()));
272            }
273
274            // If the sink is dead, we cannot receive any more messages.
275            if !channel.sink_alive {
276                self.poisoned = true;
277                return Err(Error::RecvFailed);
278            }
279
280            // Set up waiter for remaining amount.
281            let remaining = len - self.buffer.len();
282            assert!(channel.waiter.is_none());
283            let (os_send, os_recv) = oneshot::channel();
284            channel.waiter = Some((remaining, os_send));
285            os_recv
286        };
287
288        let mut waiter_guard = RecvWaiterGuard::new(self.channel.clone());
289
290        // Pre-poison so that cancellation  leaves the stream permanently closed.
291        self.poisoned = true;
292
293        // Wait for the waiter to be resolved.
294        let data = match os_recv.await {
295            Ok(data) => {
296                waiter_guard.disarm();
297                self.poisoned = false;
298                data
299            }
300            Err(_) => {
301                waiter_guard.disarm();
302                return Err(Error::RecvFailed);
303            }
304        };
305        self.buffer.extend_from_slice(&data);
306
307        assert!(self.buffer.len() >= len);
308        Ok(IoBufs::from(self.buffer.split_to(len).freeze()))
309    }
310
311    fn peek(&self, max_len: usize) -> &[u8] {
312        let len = max_len.min(self.buffer.len());
313        &self.buffer[..len]
314    }
315}
316
317impl Drop for Stream {
318    fn drop(&mut self) {
319        let mut channel = self.channel.lock();
320        channel.stream_alive = false;
321
322        // Wake a blocked sender so it can observe the closed stream.
323        channel.drain_waiter.take();
324    }
325}
326
327/// A sync deferred by a [DelayedSyncBlob], held open until explicitly completed.
328pub struct DeferredSync {
329    /// Completes the sync with the provided result (success runs the inner blob's sync).
330    pub release: oneshot::Sender<Result<(), Error>>,
331
332    /// Resolves once the deferred sync's handle begins waiting on `release`.
333    pub blocked: oneshot::Receiver<()>,
334}
335
336/// Coordinates durability operations for a [DelayedSyncContext] or [DelayedSyncBlob].
337///
338/// Every started sync parks in a deferred queue (in start order) until a test
339/// releases it. [Self::arm] additionally installs a one-shot gate that blocks
340/// the next durability operation and counts operations from that point on
341/// ([Self::calls]). The gate is pushed onto the deferred queue when [Self::arm]
342/// is called, before any operation reaches it.
343#[derive(Clone, Default)]
344pub struct PendingSyncs {
345    syncs: Arc<Mutex<Vec<DeferredSync>>>,
346    gate: Arc<Mutex<SyncGateState>>,
347}
348
349/// Forwards [Supervisor], [Clock], [GovernorClock], [ReasonablyRealtime],
350/// [Metrics], [BufferPooler], [TryRng], and [TryCryptoRng] to the wrapped
351/// context for test context wrappers with one extra field (named by the
352/// second argument).
353macro_rules! forward_context {
354    ($wrapper:ident, $field:ident) => {
355        impl<E: Supervisor> Supervisor for $wrapper<E> {
356            fn name(&self) -> Name {
357                self.inner.name()
358            }
359
360            fn child(&self, label: &'static str) -> Self {
361                Self {
362                    inner: self.inner.child(label),
363                    $field: self.$field.clone(),
364                }
365            }
366
367            fn with_attribute(self, key: &'static str, value: impl std::fmt::Display) -> Self {
368                Self {
369                    inner: self.inner.with_attribute(key, value),
370                    $field: self.$field,
371                }
372            }
373        }
374
375        impl<E: Clock> Clock for $wrapper<E> {
376            fn current(&self) -> std::time::SystemTime {
377                self.inner.current()
378            }
379
380            fn sleep(
381                &self,
382                duration: std::time::Duration,
383            ) -> impl Future<Output = ()> + Send + 'static {
384                self.inner.sleep(duration)
385            }
386
387            fn sleep_until(
388                &self,
389                deadline: std::time::SystemTime,
390            ) -> impl Future<Output = ()> + Send + 'static {
391                self.inner.sleep_until(deadline)
392            }
393        }
394
395        impl<E: Clock> GovernorClock for $wrapper<E> {
396            type Instant = std::time::SystemTime;
397
398            fn now(&self) -> Self::Instant {
399                self.current()
400            }
401        }
402
403        impl<E: Clock> ReasonablyRealtime for $wrapper<E> {}
404
405        impl<E: Metrics> Metrics for $wrapper<E> {
406            fn register<N: Into<String>, H: Into<String>, M: Metric>(
407                &self,
408                name: N,
409                help: H,
410                metric: M,
411            ) -> Registered<M> {
412                self.inner.register(name, help, metric)
413            }
414
415            fn encode(&self) -> String {
416                self.inner.encode()
417            }
418        }
419
420        impl<E: BufferPooler> BufferPooler for $wrapper<E> {
421            fn network_buffer_pool(&self) -> &BufferPool {
422                self.inner.network_buffer_pool()
423            }
424
425            fn storage_buffer_pool(&self) -> &BufferPool {
426                self.inner.storage_buffer_pool()
427            }
428        }
429
430        impl<E: TryRng> TryRng for $wrapper<E> {
431            type Error = E::Error;
432
433            fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
434                self.inner.try_next_u32()
435            }
436
437            fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
438                self.inner.try_next_u64()
439            }
440
441            fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Self::Error> {
442                self.inner.try_fill_bytes(dest)
443            }
444        }
445
446        impl<E: TryCryptoRng> TryCryptoRng for $wrapper<E> {}
447    };
448}
449
450/// Context wrapper whose blobs defer [Blob::start_sync] and can gate blocking syncs in tests.
451#[derive(Clone)]
452pub struct DelayedSyncContext<E> {
453    pub inner: E,
454    pub pending: PendingSyncs,
455}
456
457forward_context!(DelayedSyncContext, pending);
458
459impl<E: Spawner> Spawner for DelayedSyncContext<E> {
460    fn shared(mut self, blocking: bool) -> Self {
461        self.inner = self.inner.shared(blocking);
462        self
463    }
464
465    fn dedicated(mut self) -> Self {
466        self.inner = self.inner.dedicated();
467        self
468    }
469
470    fn spawn<F, Fut, T>(self, f: F) -> Handle<T>
471    where
472        F: FnOnce(Self) -> Fut + Send + 'static,
473        Fut: Future<Output = T> + Send + 'static,
474        T: Send + 'static,
475    {
476        let pending = self.pending;
477        self.inner.spawn(move |inner| f(Self { inner, pending }))
478    }
479
480    async fn stop(self, value: i32, timeout: Option<std::time::Duration>) -> Result<(), Error> {
481        self.inner.stop(value, timeout).await
482    }
483
484    fn stopped(&self) -> Signal {
485        self.inner.stopped()
486    }
487}
488
489impl<E: Storage> Storage for DelayedSyncContext<E> {
490    type Blob = DelayedSyncBlob<E::Blob>;
491
492    async fn open_versioned(
493        &self,
494        partition: &str,
495        name: &[u8],
496        versions: std::ops::RangeInclusive<u16>,
497    ) -> Result<(Self::Blob, u64, u16), Error> {
498        let (inner, len, version) = self.inner.open_versioned(partition, name, versions).await?;
499        Ok((
500            DelayedSyncBlob {
501                inner,
502                pending: self.pending.clone(),
503            },
504            len,
505            version,
506        ))
507    }
508
509    async fn remove(&self, partition: &str, name: Option<&[u8]>) -> Result<(), Error> {
510        self.inner.remove(partition, name).await
511    }
512
513    async fn scan(&self, partition: &str) -> Result<Vec<Vec<u8>>, Error> {
514        self.inner.scan(partition).await
515    }
516}
517
518/// Blob wrapper that parks each started sync and supports one-shot blocking sync tracking.
519#[derive(Clone)]
520pub struct DelayedSyncBlob<B> {
521    inner: B,
522    pending: PendingSyncs,
523}
524
525impl<B> DelayedSyncBlob<B> {
526    /// Wrap `inner`, returning the blob and the list its deferred syncs are pushed onto.
527    pub fn new(inner: B) -> (Self, PendingSyncs) {
528        let pending = PendingSyncs::default();
529        (
530            Self {
531                inner,
532                pending: pending.clone(),
533            },
534            pending,
535        )
536    }
537}
538
539impl<B: Blob> Blob for DelayedSyncBlob<B> {
540    async fn read_at_buf(
541        &self,
542        offset: u64,
543        len: usize,
544        bufs: impl Into<IoBufsMut> + Send,
545    ) -> Result<IoBufsMut, Error> {
546        self.inner.read_at_buf(offset, len, bufs).await
547    }
548
549    async fn read_at(&self, offset: u64, len: usize) -> Result<IoBufsMut, Error> {
550        self.inner.read_at(offset, len).await
551    }
552
553    async fn write_at(&self, offset: u64, bufs: impl Into<IoBufs> + Send) -> Result<(), Error> {
554        self.inner.write_at(offset, bufs).await
555    }
556
557    async fn write_at_sync(
558        &self,
559        offset: u64,
560        bufs: impl Into<IoBufs> + Send,
561    ) -> Result<(), Error> {
562        if !self.pending.tracking() {
563            return self.inner.write_at_sync(offset, bufs).await;
564        }
565        self.inner.write_at(offset, bufs).await?;
566        self.pending.wait().await?;
567        self.inner.sync().await
568    }
569
570    async fn resize(&self, len: u64) -> Result<(), Error> {
571        self.inner.resize(len).await
572    }
573
574    async fn sync(&self) -> Result<(), Error> {
575        self.pending.wait().await?;
576        self.inner.sync().await
577    }
578
579    async fn start_sync(&self) -> Handle<()> {
580        let inner = self.inner.clone();
581        let waiter = self
582            .pending
583            .observe()
584            .unwrap_or_else(|| self.pending.defer());
585        Handle::from_future(async move {
586            waiter.wait().await?;
587            inner.sync().await
588        })
589    }
590}
591
592/// Take the oldest pending sync, panicking if none was started.
593pub fn next_pending_sync(pending: &PendingSyncs) -> DeferredSync {
594    let mut pending = pending.lock();
595    assert!(!pending.is_empty(), "no pending sync was started");
596    pending.remove(0)
597}
598
599/// Complete the oldest `count` pending syncs successfully.
600pub fn release_next_pending_syncs(pending: &PendingSyncs, count: usize) {
601    let syncs = {
602        let mut pending = pending.lock();
603        assert!(
604            pending.len() >= count,
605            "not enough pending syncs: have {}, need {count}",
606            pending.len()
607        );
608        pending.drain(..count).collect::<Vec<_>>()
609    };
610    for sync in syncs {
611        let _ = sync.release.send(Ok(()));
612    }
613}
614
615/// Complete all pending syncs successfully.
616pub fn release_pending_syncs(pending: &PendingSyncs) {
617    for sync in mem::take(&mut *pending.lock()) {
618        let _ = sync.release.send(Ok(()));
619    }
620}
621
622/// Fail all pending syncs with an injected I/O error.
623pub fn fail_pending_syncs(pending: &PendingSyncs) {
624    for sync in mem::take(&mut *pending.lock()) {
625        let err = std::io::Error::other("injected sync failure");
626        let _ = sync.release.send(Err(Error::Io(err.into())));
627    }
628}
629
630struct SyncWaiter {
631    entered: oneshot::Sender<()>,
632    release: oneshot::Receiver<Result<(), Error>>,
633}
634
635impl SyncWaiter {
636    async fn wait(self) -> Result<(), Error> {
637        self.entered.send_lossy(());
638        self.release.await.map_err(|_| Error::Closed)??;
639        Ok(())
640    }
641}
642
643#[derive(Default)]
644struct SyncGateState {
645    tracking: bool,
646    calls: usize,
647    waiter: Option<SyncWaiter>,
648}
649
650impl PendingSyncs {
651    /// Locks the deferred sync queue.
652    pub fn lock(&self) -> commonware_utils::sync::MutexGuard<'_, Vec<DeferredSync>> {
653        self.syncs.lock()
654    }
655
656    /// Begins counting durability operations and blocks the next one behind a
657    /// one-shot gate (pushed onto the deferred queue so tests can release it).
658    ///
659    /// Once the gate is consumed, started syncs park in the deferred queue as
660    /// usual while [Self::calls] keeps counting.
661    pub fn arm(&self) {
662        let mut state = self.gate.lock();
663        assert!(!state.tracking, "sync gate already armed");
664        assert!(state.waiter.is_none(), "sync gate already has a waiter");
665        state.tracking = true;
666        state.calls = 0;
667        state.waiter = Some(self.defer());
668    }
669
670    /// Returns the number of durability operations observed since [Self::arm].
671    pub fn calls(&self) -> usize {
672        self.gate.lock().calls
673    }
674
675    fn tracking(&self) -> bool {
676        self.gate.lock().tracking
677    }
678
679    fn defer(&self) -> SyncWaiter {
680        let (release, release_rx) = oneshot::channel();
681        let (entered, blocked) = oneshot::channel();
682        self.syncs.lock().push(DeferredSync { release, blocked });
683        SyncWaiter {
684            entered,
685            release: release_rx,
686        }
687    }
688
689    /// Records a durability operation if the gate is armed, returning the
690    /// one-shot gate waiter if it has not been consumed yet.
691    fn observe(&self) -> Option<SyncWaiter> {
692        let mut state = self.gate.lock();
693        if !state.tracking {
694            return None;
695        }
696        state.calls += 1;
697        state.waiter.take()
698    }
699
700    async fn wait(&self) -> Result<(), Error> {
701        match self.observe() {
702            Some(waiter) => waiter.wait().await,
703            None => Ok(()),
704        }
705    }
706}
707
708/// Context wrapper whose blobs fail `sync` and `start_sync` for a single partition.
709#[derive(Clone)]
710pub struct SyncFaultContext<E> {
711    pub inner: E,
712    pub fail_partition: String,
713}
714
715forward_context!(SyncFaultContext, fail_partition);
716
717impl<E: Storage> Storage for SyncFaultContext<E> {
718    type Blob = SyncFaultBlob<E::Blob>;
719
720    async fn open_versioned(
721        &self,
722        partition: &str,
723        name: &[u8],
724        versions: std::ops::RangeInclusive<u16>,
725    ) -> Result<(Self::Blob, u64, u16), Error> {
726        let (inner, len, version) = self.inner.open_versioned(partition, name, versions).await?;
727        Ok((
728            SyncFaultBlob {
729                inner,
730                faulty: partition == self.fail_partition,
731            },
732            len,
733            version,
734        ))
735    }
736
737    async fn remove(&self, partition: &str, name: Option<&[u8]>) -> Result<(), Error> {
738        self.inner.remove(partition, name).await
739    }
740
741    async fn scan(&self, partition: &str) -> Result<Vec<Vec<u8>>, Error> {
742        self.inner.scan(partition).await
743    }
744}
745
746/// Blob wrapper that fails `sync` and `start_sync` when marked faulty.
747#[derive(Clone)]
748pub struct SyncFaultBlob<B> {
749    inner: B,
750    faulty: bool,
751}
752
753impl<B: Blob> Blob for SyncFaultBlob<B> {
754    async fn read_at_buf(
755        &self,
756        offset: u64,
757        len: usize,
758        bufs: impl Into<IoBufsMut> + Send,
759    ) -> Result<IoBufsMut, Error> {
760        self.inner.read_at_buf(offset, len, bufs).await
761    }
762
763    async fn read_at(&self, offset: u64, len: usize) -> Result<IoBufsMut, Error> {
764        self.inner.read_at(offset, len).await
765    }
766
767    async fn write_at(&self, offset: u64, bufs: impl Into<IoBufs> + Send) -> Result<(), Error> {
768        self.inner.write_at(offset, bufs).await
769    }
770
771    async fn write_at_sync(
772        &self,
773        offset: u64,
774        bufs: impl Into<IoBufs> + Send,
775    ) -> Result<(), Error> {
776        self.inner.write_at_sync(offset, bufs).await
777    }
778
779    async fn resize(&self, len: u64) -> Result<(), Error> {
780        self.inner.resize(len).await
781    }
782
783    async fn sync(&self) -> Result<(), Error> {
784        if self.faulty {
785            let err = std::io::Error::other("injected partition sync fault");
786            return Err(Error::Io(err.into()));
787        }
788        self.inner.sync().await
789    }
790
791    async fn start_sync(&self) -> Handle<()> {
792        if self.faulty {
793            return Handle::ready(self.sync().await);
794        }
795        self.inner.start_sync().await
796    }
797}
798
799#[cfg(test)]
800mod tests {
801    use super::*;
802    use crate::{deterministic, Clock, Runner, Sink, Spawner, Stream};
803    use commonware_macros::select;
804    use std::{thread::sleep, time::Duration};
805
806    #[test]
807    fn test_send_recv() {
808        let (mut sink, mut stream) = Channel::init();
809        let data = b"hello world";
810
811        let executor = deterministic::Runner::default();
812        executor.start(|_| async move {
813            sink.send(data.as_slice()).await.unwrap();
814            let received = stream.recv(data.len()).await.unwrap();
815            assert_eq!(received.coalesce(), data);
816        });
817    }
818
819    #[test]
820    fn test_send_recv_partial_multiple() {
821        let (mut sink, mut stream) = Channel::init();
822        let data = b"hello";
823        let data2 = b" world";
824
825        let executor = deterministic::Runner::default();
826        executor.start(|_| async move {
827            sink.send(data.as_slice()).await.unwrap();
828            sink.send(data2.as_slice()).await.unwrap();
829            let received = stream.recv(5).await.unwrap();
830            assert_eq!(received.coalesce(), b"hello");
831            let received = stream.recv(5).await.unwrap();
832            assert_eq!(received.coalesce(), b" worl");
833            let received = stream.recv(1).await.unwrap();
834            assert_eq!(received.coalesce(), b"d");
835        });
836    }
837
838    #[test]
839    fn test_send_recv_async() {
840        let (mut sink, mut stream) = Channel::init();
841        let data = b"hello world";
842
843        let executor = deterministic::Runner::default();
844        executor.start(|_| async move {
845            let (received, _) = futures::try_join!(stream.recv(data.len()), async {
846                sleep(Duration::from_millis(50));
847                sink.send(data.as_slice()).await
848            })
849            .unwrap();
850            assert_eq!(received.coalesce(), data);
851        });
852    }
853
854    #[test]
855    fn test_recv_error_sink_dropped_while_waiting() {
856        let (sink, mut stream) = Channel::init();
857
858        let executor = deterministic::Runner::default();
859        executor.start(|context| async move {
860            futures::join!(
861                async {
862                    let result = stream.recv(5).await;
863                    assert!(matches!(result, Err(Error::RecvFailed)));
864                    let result = stream.recv(5).await;
865                    assert!(matches!(result, Err(Error::Closed)));
866                },
867                async {
868                    // Wait for the stream to start waiting
869                    context.sleep(Duration::from_millis(50)).await;
870                    drop(sink);
871                }
872            );
873        });
874    }
875
876    #[test]
877    fn test_recv_error_sink_dropped_before_recv() {
878        let (sink, mut stream) = Channel::init();
879        drop(sink); // Drop sink immediately
880
881        let executor = deterministic::Runner::default();
882        executor.start(|_| async move {
883            let result = stream.recv(5).await;
884            assert!(matches!(result, Err(Error::RecvFailed)));
885            let result = stream.recv(5).await;
886            assert!(matches!(result, Err(Error::Closed)));
887        });
888    }
889
890    #[test]
891    fn test_send_error_stream_dropped() {
892        let (mut sink, mut stream) = Channel::init();
893
894        let executor = deterministic::Runner::default();
895        executor.start(|context| async move {
896            // Send some bytes
897            assert!(sink.send(b"7 bytes".as_slice()).await.is_ok());
898
899            // Spawn a task to initiate recv's where the first one will succeed and then will drop.
900            let handle = context.child("recv").spawn(|_| async move {
901                let _ = stream.recv(5).await;
902                let _ = stream.recv(5).await;
903            });
904
905            // Give the async task a moment to start
906            context.sleep(Duration::from_millis(50)).await;
907
908            // Drop the stream by aborting the handle
909            handle.abort();
910            assert!(matches!(handle.await, Err(Error::Closed)));
911
912            // Try to send a message. The stream is dropped, so this should fail.
913            let result = sink.send(b"hello world".as_slice()).await;
914            assert!(matches!(result, Err(Error::SendFailed)));
915            let result = sink.send(b"hello world".as_slice()).await;
916            assert!(matches!(result, Err(Error::Closed)));
917        });
918    }
919
920    #[test]
921    fn test_send_error_stream_dropped_before_send() {
922        let (mut sink, stream) = Channel::init();
923        drop(stream); // Drop stream immediately
924
925        let executor = deterministic::Runner::default();
926        executor.start(|_| async move {
927            let result = sink.send(b"hello world".as_slice()).await;
928            assert!(matches!(result, Err(Error::SendFailed)));
929            let result = sink.send(b"hello world".as_slice()).await;
930            assert!(matches!(result, Err(Error::Closed)));
931        });
932    }
933
934    #[test]
935    fn test_recv_timeout() {
936        let (_sink, mut stream) = Channel::init();
937
938        // If there is no data to read, test that the recv function just blocks.
939        // The timeout should return first.
940        let executor = deterministic::Runner::default();
941        executor.start(|context| async move {
942            select! {
943                v = stream.recv(5) => {
944                    panic!("unexpected value: {v:?}");
945                },
946                _ = context.sleep(Duration::from_millis(100)) => "timeout",
947            };
948        });
949    }
950
951    #[test]
952    fn test_peek_empty() {
953        let (_sink, stream) = Channel::init();
954
955        // Peek on a fresh stream should return empty slice
956        assert!(stream.peek(10).is_empty());
957    }
958
959    #[test]
960    fn test_peek_after_partial_recv() {
961        let (mut sink, mut stream) = Channel::init();
962
963        let executor = deterministic::Runner::default();
964        executor.start(|_| async move {
965            // Send more data than we'll consume
966            sink.send(b"hello world".as_slice()).await.unwrap();
967
968            // Recv only part of it
969            let received = stream.recv(5).await.unwrap();
970            assert_eq!(received.coalesce(), b"hello");
971
972            // Peek should show the remaining data
973            assert_eq!(stream.peek(100), b" world");
974
975            // Peek with smaller max_len
976            assert_eq!(stream.peek(3), b" wo");
977
978            // Peek doesn't consume - can peek again
979            assert_eq!(stream.peek(100), b" world");
980
981            // Recv consumes the peeked data
982            let received = stream.recv(6).await.unwrap();
983            assert_eq!(received.coalesce(), b" world");
984
985            // Peek is now empty
986            assert!(stream.peek(100).is_empty());
987        });
988    }
989
990    #[test]
991    fn test_peek_after_recv_wakeup() {
992        let (mut sink, mut stream) = Channel::init_with_buffer_size(64);
993
994        let executor = deterministic::Runner::default();
995        executor.start(|context| async move {
996            // Spawn recv that will block waiting
997            let (tx, rx) = oneshot::channel();
998            let recv_handle = context.child("recv").spawn(|_| async move {
999                let data = stream.recv(3).await.unwrap();
1000                tx.send(stream).ok();
1001                data
1002            });
1003
1004            // Let recv set up waiter
1005            context.sleep(Duration::from_millis(10)).await;
1006
1007            // Send more than requested
1008            sink.send(b"ABCDEFGHIJ".as_slice()).await.unwrap();
1009
1010            // Recv gets its 3 bytes
1011            let received = recv_handle.await.unwrap();
1012            assert_eq!(received.coalesce(), b"ABC");
1013
1014            // Get stream back and verify peek sees remaining data
1015            let stream = rx.await.unwrap();
1016            assert_eq!(stream.peek(100), b"DEFGHIJ");
1017        });
1018    }
1019
1020    #[test]
1021    fn test_peek_multiple_sends() {
1022        let (mut sink, mut stream) = Channel::init();
1023
1024        let executor = deterministic::Runner::default();
1025        executor.start(|_| async move {
1026            // Send multiple chunks
1027            sink.send(b"aaa".as_slice()).await.unwrap();
1028            sink.send(b"bbb".as_slice()).await.unwrap();
1029            sink.send(b"ccc".as_slice()).await.unwrap();
1030
1031            // Recv less than total
1032            let received = stream.recv(4).await.unwrap();
1033            assert_eq!(received.coalesce(), b"aaab");
1034
1035            // Peek should show remaining
1036            assert_eq!(stream.peek(100), b"bbccc");
1037        });
1038    }
1039
1040    #[test]
1041    fn test_buffer_size_limit() {
1042        // Use a small buffer capacity for testing
1043        let (mut sink, mut stream) = Channel::init_with_buffer_size(10);
1044
1045        let executor = deterministic::Runner::default();
1046        executor.start(|context| async move {
1047            // Send more than buffer capacity concurrently with recv
1048            // so the sender can drain via backpressure.
1049            let send_handle = context.child("sender").spawn(|_| async move {
1050                sink.send(b"0123456789ABCDEF".as_slice()).await.unwrap();
1051                sink
1052            });
1053
1054            // Recv a small amount - should only pull up to capacity (10 bytes)
1055            let received = stream.recv(2).await.unwrap();
1056            assert_eq!(received.coalesce(), b"01");
1057
1058            // Peek should show remaining buffered data (8 bytes, not 14)
1059            assert_eq!(stream.peek(100), b"23456789");
1060
1061            // The rest should still be in the channel buffer
1062            // Recv more to pull the remaining data
1063            let received = stream.recv(8).await.unwrap();
1064            assert_eq!(received.coalesce(), b"23456789");
1065
1066            // Now peek should show next chunk from channel (up to capacity)
1067            let received = stream.recv(2).await.unwrap();
1068            assert_eq!(received.coalesce(), b"AB");
1069
1070            assert_eq!(stream.peek(100), b"CDEF");
1071
1072            // Ensure the sender completes
1073            send_handle.await.unwrap();
1074        });
1075    }
1076
1077    #[test]
1078    fn test_recv_before_send() {
1079        // Use a small buffer capacity for testing
1080        let (mut sink, mut stream) = Channel::init_with_buffer_size(10);
1081
1082        let executor = deterministic::Runner::default();
1083        executor.start(|context| async move {
1084            // Start recv before send (will wait)
1085            let recv_handle = context
1086                .child("recv")
1087                .spawn(|_| async move { stream.recv(3).await.unwrap() });
1088
1089            // Give recv time to set up waiter
1090            context.sleep(Duration::from_millis(10)).await;
1091
1092            // Send more than capacity
1093            sink.send(b"ABCDEFGHIJKLMNOP".as_slice()).await.unwrap();
1094
1095            // Recv should get its 3 bytes
1096            let received = recv_handle.await.unwrap();
1097            assert_eq!(received.coalesce(), b"ABC");
1098        });
1099    }
1100}