Skip to main content

commonware_runtime/
mocks.rs

1//! Mock implementations of runtime primitives for testing.
2
3#[cfg(any(test, feature = "test-utils"))]
4pub use crate::storage::memory::Storage as MemoryStorage;
5use crate::{
6    Blob, BlobVersion, BufMut, BufferPool, BufferPooler, Clock, Error, Handle, IoBufs, IoBufsMut,
7    Metrics, Name, ReadOptions, Spawner, Storage, Supervisor, WriteOptions,
8    signal::Signal,
9    telemetry::metrics::{Metric, Registered},
10};
11use bytes::{Bytes, BytesMut};
12use commonware_utils::{
13    channel::{fallible::OneshotExt, oneshot},
14    sync::Mutex,
15};
16use governor::clock::{Clock as GovernorClock, ReasonablyRealtime};
17use rand::{TryCryptoRng, TryRng};
18use std::{
19    future::{Future, poll_fn},
20    mem,
21    sync::Arc,
22    task::Poll,
23};
24
25/// Default buffer size (64 KB). Controls both how much data the stream
26/// pulls per recv and the backpressure threshold for send.
27const DEFAULT_BUFFER_SIZE: usize = 64 * 1024;
28
29/// A mock channel struct that is used internally by Sink and Stream.
30pub struct Channel {
31    /// Stores the bytes sent by the sink that are not yet read by the stream.
32    buffer: BytesMut,
33
34    /// If the stream is waiting to read bytes, the waiter stores the number of
35    /// bytes that the stream is waiting for, as well as the oneshot sender that
36    /// the sink uses to send the bytes to the stream directly.
37    waiter: Option<(usize, oneshot::Sender<Bytes>)>,
38
39    /// Target buffer size, used to bound both the stream's local buffer
40    /// and the shared buffer (backpressure threshold).
41    buffer_size: usize,
42
43    /// If the sink is blocked waiting for the buffer to drain, this holds
44    /// the oneshot sender that the stream uses to wake the sink.
45    drain_waiter: Option<oneshot::Sender<()>>,
46
47    /// Tracks whether the sink is still alive and able to send messages.
48    sink_alive: bool,
49
50    /// Tracks whether the stream is still alive and able to receive messages.
51    stream_alive: bool,
52}
53
54impl Channel {
55    /// Returns an async-safe Sink/Stream pair with default buffer size.
56    pub fn init() -> (Sink, Stream) {
57        Self::init_with_buffer_size(DEFAULT_BUFFER_SIZE)
58    }
59
60    /// Returns an async-safe Sink/Stream pair with the specified buffer size.
61    pub fn init_with_buffer_size(buffer_size: usize) -> (Sink, Stream) {
62        let channel = Arc::new(Mutex::new(Self {
63            buffer: BytesMut::new(),
64            waiter: None,
65            buffer_size,
66            drain_waiter: None,
67            sink_alive: true,
68            stream_alive: true,
69        }));
70        (
71            Sink {
72                channel: channel.clone(),
73                state: SinkState::Open,
74            },
75            Stream {
76                channel,
77                buffer: BytesMut::new(),
78                poisoned: false,
79            },
80        )
81    }
82
83    /// Restores bytes that were detached from the front of the shared buffer.
84    fn restore_front(&mut self, data: Bytes) {
85        if data.is_empty() {
86            return;
87        }
88
89        let mut restored = BytesMut::with_capacity(data.len() + self.buffer.len());
90        restored.extend_from_slice(&data);
91        restored.extend_from_slice(&self.buffer);
92        self.buffer = restored;
93    }
94
95    /// Marks the sink as closed and wakes any waiter.
96    fn close_sink(&mut self) {
97        self.sink_alive = false;
98
99        // If there is a waiter, resolve it by dropping the oneshot sender.
100        self.waiter.take();
101    }
102}
103
104struct RecvWaiterGuard {
105    channel: Arc<Mutex<Channel>>,
106    active: bool,
107}
108
109impl RecvWaiterGuard {
110    const fn new(channel: Arc<Mutex<Channel>>) -> Self {
111        Self {
112            channel,
113            active: true,
114        }
115    }
116
117    const fn disarm(&mut self) {
118        self.active = false;
119    }
120}
121
122impl Drop for RecvWaiterGuard {
123    fn drop(&mut self) {
124        if !self.active {
125            return;
126        }
127
128        self.channel.lock().waiter.take();
129    }
130}
131
132/// A mock sink that implements the Sink trait.
133pub struct Sink {
134    channel: Arc<Mutex<Channel>>,
135    state: SinkState,
136}
137
138/// Lifecycle state for the mock sink half.
139enum SinkState {
140    /// Sends may be attempted.
141    Open,
142    /// A send is currently in progress.
143    Sending,
144    /// The sink has been closed.
145    Closed,
146}
147
148impl Sink {
149    fn close(&mut self) {
150        if matches!(self.state, SinkState::Closed) {
151            return;
152        }
153        self.channel.lock().close_sink();
154        self.state = SinkState::Closed;
155    }
156}
157
158impl crate::Sink for Sink {
159    async fn send(&mut self, bufs: impl Into<IoBufs> + Send) -> Result<(), Error> {
160        match self.state {
161            SinkState::Open => {}
162            SinkState::Sending => {
163                self.close();
164                return Err(Error::Closed);
165            }
166            SinkState::Closed => return Err(Error::Closed),
167        }
168
169        let drain_recv = {
170            let mut channel = self.channel.lock();
171
172            // If the receiver is dead, we cannot send any more messages.
173            if !channel.stream_alive {
174                channel.close_sink();
175                self.state = SinkState::Closed;
176                return Err(Error::SendFailed);
177            }
178
179            channel.buffer.put(bufs.into());
180
181            // If there is a waiter and the buffer is large enough,
182            // resolve the waiter (while clearing the waiter field).
183            if channel
184                .waiter
185                .as_ref()
186                .is_some_and(|(requested, _)| *requested <= channel.buffer.len())
187            {
188                // Send up to buffer_size bytes (but at least requested amount)
189                let (requested, os_send) = channel.waiter.take().unwrap();
190                let send_amount = channel.buffer.len().min(requested.max(channel.buffer_size));
191                let data = channel.buffer.split_to(send_amount).freeze();
192
193                // A canceled recv should behave like a buffered transport:
194                // preserve the bytes and allow a subsequent recv to consume them.
195                if let Err(data) = os_send.send(data) {
196                    channel.restore_front(data);
197                    if !channel.stream_alive {
198                        channel.close_sink();
199                        self.state = SinkState::Closed;
200                        return Err(Error::SendFailed);
201                    }
202                }
203            }
204
205            // If the buffer exceeds the write limit, block until the
206            // receiver drains enough data.
207            if channel.buffer.len() > channel.buffer_size {
208                assert!(channel.drain_waiter.is_none());
209                let (os_send, os_recv) = oneshot::channel();
210                channel.drain_waiter = Some(os_send);
211                os_recv
212            } else {
213                return Ok(());
214            }
215        };
216
217        // Mark the sink as sending before awaiting so cancellation can be
218        // detected by the next send.
219        self.state = SinkState::Sending;
220
221        // Wait for the receiver to drain the buffer.
222        match drain_recv.await {
223            Ok(()) => {
224                self.state = SinkState::Open;
225                Ok(())
226            }
227            Err(_) => {
228                self.close();
229                Err(Error::SendFailed)
230            }
231        }
232    }
233}
234
235impl Drop for Sink {
236    fn drop(&mut self) {
237        self.close();
238    }
239}
240
241/// A mock stream that implements the Stream trait.
242pub struct Stream {
243    channel: Arc<Mutex<Channel>>,
244    /// Local buffer for data that has been received but not yet consumed.
245    buffer: BytesMut,
246    poisoned: bool,
247}
248
249impl crate::Stream for Stream {
250    async fn recv(&mut self, len: usize) -> Result<IoBufs, Error> {
251        if self.poisoned {
252            return Err(Error::Closed);
253        }
254
255        let os_recv = {
256            let mut channel = self.channel.lock();
257
258            // Pull data from channel buffer into local buffer.
259            let target = len.max(channel.buffer_size);
260            let pull_amount = channel
261                .buffer
262                .len()
263                .min(target.saturating_sub(self.buffer.len()));
264            if pull_amount > 0 {
265                let data = channel.buffer.split_to(pull_amount);
266                self.buffer.extend_from_slice(&data);
267
268                // Wake a blocked sender if the buffer drained below the limit.
269                if channel.buffer.len() <= channel.buffer_size
270                    && let Some(sender) = channel.drain_waiter.take()
271                {
272                    sender.send_lossy(());
273                }
274            }
275
276            // If we have enough, return immediately.
277            if self.buffer.len() >= len {
278                return Ok(IoBufs::from(self.buffer.split_to(len).freeze()));
279            }
280
281            // If the sink is dead, we cannot receive any more messages.
282            if !channel.sink_alive {
283                self.poisoned = true;
284                return Err(Error::RecvFailed);
285            }
286
287            // Set up waiter for remaining amount.
288            let remaining = len - self.buffer.len();
289            assert!(channel.waiter.is_none());
290            let (os_send, os_recv) = oneshot::channel();
291            channel.waiter = Some((remaining, os_send));
292            os_recv
293        };
294
295        let mut waiter_guard = RecvWaiterGuard::new(self.channel.clone());
296
297        // Pre-poison so that cancellation  leaves the stream permanently closed.
298        self.poisoned = true;
299
300        // Wait for the waiter to be resolved.
301        let data = match os_recv.await {
302            Ok(data) => {
303                waiter_guard.disarm();
304                self.poisoned = false;
305                data
306            }
307            Err(_) => {
308                waiter_guard.disarm();
309                return Err(Error::RecvFailed);
310            }
311        };
312        self.buffer.extend_from_slice(&data);
313
314        assert!(self.buffer.len() >= len);
315        Ok(IoBufs::from(self.buffer.split_to(len).freeze()))
316    }
317
318    fn peek(&self, max_len: usize) -> &[u8] {
319        let len = max_len.min(self.buffer.len());
320        &self.buffer[..len]
321    }
322}
323
324impl Drop for Stream {
325    fn drop(&mut self) {
326        let mut channel = self.channel.lock();
327        channel.stream_alive = false;
328
329        // Wake a blocked sender so it can observe the closed stream.
330        channel.drain_waiter.take();
331    }
332}
333
334/// A sync deferred by a [DelayedSyncBlob], held open until explicitly completed.
335pub struct DeferredSync {
336    /// Completes the sync with the provided result (success runs the inner blob's sync).
337    pub release: oneshot::Sender<Result<(), Error>>,
338
339    /// Resolves once the deferred sync's handle begins waiting on `release`.
340    pub blocked: oneshot::Receiver<()>,
341}
342
343/// Coordinates durability operations for a [DelayedSyncContext] or [DelayedSyncBlob].
344///
345/// Every started sync parks in a deferred queue (in start order) until a test
346/// releases it or [Self::unblock] runs. [Self::arm] additionally installs a one-shot gate that blocks
347/// the next durability operation and counts operations from that point on
348/// ([Self::calls]). The gate is pushed onto the deferred queue when [Self::arm]
349/// is called, before any operation reaches it.
350#[derive(Clone, Default)]
351pub struct PendingSyncs {
352    state: Arc<Mutex<State>>,
353}
354
355/// State shared by all clones of a [PendingSyncs].
356#[derive(Default)]
357struct State {
358    /// Deferred syncs in start order.
359    syncs: Vec<DeferredSync>,
360    /// One-shot gate blocking the next durability operation (see [PendingSyncs::arm]).
361    gate: SyncGateState,
362    /// Sticky: stop parking started syncs (see [PendingSyncs::unblock]).
363    unblocked: bool,
364    /// Sticky: syncs resolve to an injected error (see [PendingSyncs::arm_fail]).
365    fail: bool,
366    /// Started syncs issued.
367    starts: usize,
368    /// Started syncs whose completion futures have begun executing.
369    entered: usize,
370    /// Started syncs that completed durably.
371    completions: usize,
372}
373
374impl State {
375    /// Creates a waiter parked in the deferred queue.
376    fn defer(&mut self) -> SyncWaiter {
377        let (release, release_rx) = oneshot::channel();
378        let (entered, blocked) = oneshot::channel();
379        self.syncs.push(DeferredSync { release, blocked });
380        SyncWaiter {
381            entered,
382            release: release_rx,
383        }
384    }
385
386    /// Records a durability operation if the gate is armed, returning the
387    /// one-shot gate waiter if it has not been consumed yet.
388    const fn observe(&mut self) -> Option<SyncWaiter> {
389        if !self.gate.tracking {
390            return None;
391        }
392        self.gate.calls += 1;
393        self.gate.waiter.take()
394    }
395
396    /// Parks a new deferred sync, unless [PendingSyncs::unblock] already ran.
397    fn park(&mut self) -> Option<SyncWaiter> {
398        if self.unblocked {
399            return None;
400        }
401        Some(self.defer())
402    }
403}
404
405/// Forwards [Supervisor], [Clock], [GovernorClock], [ReasonablyRealtime],
406/// [Metrics], [BufferPooler], [TryRng], and [TryCryptoRng] to the wrapped
407/// context for test context wrappers with one extra field (named by the
408/// second argument).
409macro_rules! forward_context {
410    ($wrapper:ident, $field:ident) => {
411        impl<E: Supervisor> Supervisor for $wrapper<E> {
412            fn name(&self) -> Name {
413                self.inner.name()
414            }
415
416            fn child(&self, label: &'static str) -> Self {
417                Self {
418                    inner: self.inner.child(label),
419                    $field: self.$field.clone(),
420                }
421            }
422
423            fn with_attribute(self, key: &'static str, value: impl std::fmt::Display) -> Self {
424                Self {
425                    inner: self.inner.with_attribute(key, value),
426                    $field: self.$field,
427                }
428            }
429        }
430
431        impl<E: Clock> Clock for $wrapper<E> {
432            fn current(&self) -> std::time::SystemTime {
433                self.inner.current()
434            }
435
436            fn sleep(
437                &self,
438                duration: std::time::Duration,
439            ) -> impl Future<Output = ()> + Send + 'static {
440                self.inner.sleep(duration)
441            }
442
443            fn sleep_until(
444                &self,
445                deadline: std::time::SystemTime,
446            ) -> impl Future<Output = ()> + Send + 'static {
447                self.inner.sleep_until(deadline)
448            }
449        }
450
451        impl<E: Clock> GovernorClock for $wrapper<E> {
452            type Instant = std::time::SystemTime;
453
454            fn now(&self) -> Self::Instant {
455                self.current()
456            }
457        }
458
459        impl<E: Clock> ReasonablyRealtime for $wrapper<E> {}
460
461        impl<E: Metrics> Metrics for $wrapper<E> {
462            fn register<N: Into<String>, H: Into<String>, M: Metric>(
463                &self,
464                name: N,
465                help: H,
466                metric: M,
467            ) -> Registered<M> {
468                self.inner.register(name, help, metric)
469            }
470
471            fn encode(&self) -> String {
472                self.inner.encode()
473            }
474        }
475
476        impl<E: BufferPooler> BufferPooler for $wrapper<E> {
477            fn network_buffer_pool(&self) -> &BufferPool {
478                self.inner.network_buffer_pool()
479            }
480
481            fn storage_buffer_pool(&self) -> &BufferPool {
482                self.inner.storage_buffer_pool()
483            }
484        }
485
486        impl<E: TryRng> TryRng for $wrapper<E> {
487            type Error = E::Error;
488
489            fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
490                self.inner.try_next_u32()
491            }
492
493            fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
494                self.inner.try_next_u64()
495            }
496
497            fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Self::Error> {
498                self.inner.try_fill_bytes(dest)
499            }
500        }
501
502        impl<E: TryCryptoRng> TryCryptoRng for $wrapper<E> {}
503    };
504}
505
506/// Snapshot of the options observed by a [RecordingContext] or [RecordingBlob].
507#[cfg(any(test, feature = "test-utils"))]
508#[derive(Clone, Debug, Default, Eq, PartialEq)]
509pub struct RecordingSnapshot {
510    /// Options supplied to read operations, in call order.
511    pub reads: Vec<ReadOptions>,
512    /// Options supplied to write operations, in call order.
513    pub writes: Vec<WriteOptions>,
514}
515
516/// Shared observations produced by recording storage wrappers.
517#[cfg(any(test, feature = "test-utils"))]
518#[derive(Clone, Default)]
519pub struct Recordings {
520    state: Arc<Mutex<RecordingSnapshot>>,
521}
522
523#[cfg(any(test, feature = "test-utils"))]
524impl Recordings {
525    /// Return a snapshot of all observations recorded so far.
526    pub fn snapshot(&self) -> RecordingSnapshot {
527        self.state.lock().clone()
528    }
529
530    /// Remove all recorded observations.
531    pub fn clear(&self) {
532        *self.state.lock() = RecordingSnapshot::default();
533    }
534
535    fn read(&self, options: ReadOptions) {
536        self.state.lock().reads.push(options);
537    }
538
539    fn write(&self, options: WriteOptions) {
540        self.state.lock().writes.push(options);
541    }
542}
543
544/// Context wrapper that records options supplied to every opened blob.
545#[cfg(any(test, feature = "test-utils"))]
546#[derive(Clone)]
547pub struct RecordingContext<E> {
548    /// Wrapped context.
549    pub inner: E,
550    /// Observations shared by this context and all blobs opened through it.
551    pub recordings: Recordings,
552}
553
554#[cfg(any(test, feature = "test-utils"))]
555impl<E> RecordingContext<E> {
556    /// Wrap `inner` and return both the context and its shared observations.
557    pub fn new(inner: E) -> (Self, Recordings) {
558        let recordings = Recordings::default();
559        (
560            Self {
561                inner,
562                recordings: recordings.clone(),
563            },
564            recordings,
565        )
566    }
567}
568
569#[cfg(any(test, feature = "test-utils"))]
570forward_context!(RecordingContext, recordings);
571
572#[cfg(any(test, feature = "test-utils"))]
573impl<E: Spawner> Spawner for RecordingContext<E> {
574    fn shared(mut self, blocking: bool) -> Self {
575        self.inner = self.inner.shared(blocking);
576        self
577    }
578
579    fn dedicated(mut self) -> Self {
580        self.inner = self.inner.dedicated();
581        self
582    }
583
584    fn spawn<F, Fut, T>(self, f: F) -> Handle<T>
585    where
586        F: FnOnce(Self) -> Fut + Send + 'static,
587        Fut: Future<Output = T> + Send + 'static,
588        T: Send + 'static,
589    {
590        let recordings = self.recordings;
591        self.inner.spawn(move |inner| f(Self { inner, recordings }))
592    }
593
594    async fn stop(self, value: i32, timeout: Option<std::time::Duration>) -> Result<(), Error> {
595        self.inner.stop(value, timeout).await
596    }
597
598    fn stopped(&self) -> Signal {
599        self.inner.stopped()
600    }
601}
602
603#[cfg(any(test, feature = "test-utils"))]
604impl<E: Storage> Storage for RecordingContext<E> {
605    type Blob = RecordingBlob<E::Blob>;
606
607    async fn open_versioned(
608        &self,
609        partition: &str,
610        name: &[u8],
611        versions: std::ops::RangeInclusive<BlobVersion>,
612    ) -> Result<(Self::Blob, u64, BlobVersion), Error> {
613        let (inner, len, version) = self.inner.open_versioned(partition, name, versions).await?;
614        Ok((
615            RecordingBlob {
616                inner,
617                recordings: self.recordings.clone(),
618            },
619            len,
620            version,
621        ))
622    }
623
624    async fn remove(&self, partition: &str, name: Option<&[u8]>) -> Result<(), Error> {
625        self.inner.remove(partition, name).await
626    }
627
628    async fn scan(&self, partition: &str) -> Result<Vec<Vec<u8>>, Error> {
629        self.inner.scan(partition).await
630    }
631}
632
633/// Blob wrapper that records read and write options before delegating each operation.
634#[cfg(any(test, feature = "test-utils"))]
635#[derive(Clone)]
636pub struct RecordingBlob<B> {
637    inner: B,
638    recordings: Recordings,
639}
640
641#[cfg(any(test, feature = "test-utils"))]
642impl<B: Blob> Blob for RecordingBlob<B> {
643    async fn read_at_buf(
644        &self,
645        offset: u64,
646        len: usize,
647        bufs: impl Into<IoBufsMut> + Send,
648        options: ReadOptions,
649    ) -> Result<IoBufsMut, Error> {
650        self.recordings.read(options);
651        self.inner.read_at_buf(offset, len, bufs, options).await
652    }
653
654    async fn read_at(
655        &self,
656        offset: u64,
657        len: usize,
658        options: ReadOptions,
659    ) -> Result<IoBufsMut, Error> {
660        self.recordings.read(options);
661        self.inner.read_at(offset, len, options).await
662    }
663
664    async fn write_at(
665        &self,
666        offset: u64,
667        bufs: impl Into<IoBufs> + Send,
668        options: WriteOptions,
669    ) -> Result<(), Error> {
670        self.recordings.write(options);
671        self.inner.write_at(offset, bufs, options).await
672    }
673
674    async fn resize(&self, len: u64) -> Result<(), Error> {
675        self.inner.resize(len).await
676    }
677
678    async fn sync(&self) -> Result<(), Error> {
679        self.inner.sync().await
680    }
681
682    async fn start_sync(&self) -> Handle<()> {
683        self.inner.start_sync().await
684    }
685}
686
687/// Context wrapper whose blobs defer [Blob::start_sync] and can gate blocking syncs in tests.
688#[derive(Clone)]
689pub struct DelayedSyncContext<E> {
690    pub inner: E,
691    pub pending: PendingSyncs,
692}
693
694forward_context!(DelayedSyncContext, pending);
695
696impl<E: Spawner> Spawner for DelayedSyncContext<E> {
697    fn shared(mut self, blocking: bool) -> Self {
698        self.inner = self.inner.shared(blocking);
699        self
700    }
701
702    fn dedicated(mut self) -> Self {
703        self.inner = self.inner.dedicated();
704        self
705    }
706
707    fn spawn<F, Fut, T>(self, f: F) -> Handle<T>
708    where
709        F: FnOnce(Self) -> Fut + Send + 'static,
710        Fut: Future<Output = T> + Send + 'static,
711        T: Send + 'static,
712    {
713        let pending = self.pending;
714        self.inner.spawn(move |inner| f(Self { inner, pending }))
715    }
716
717    async fn stop(self, value: i32, timeout: Option<std::time::Duration>) -> Result<(), Error> {
718        self.inner.stop(value, timeout).await
719    }
720
721    fn stopped(&self) -> Signal {
722        self.inner.stopped()
723    }
724}
725
726impl<E: Storage> Storage for DelayedSyncContext<E> {
727    type Blob = DelayedSyncBlob<E::Blob>;
728
729    async fn open_versioned(
730        &self,
731        partition: &str,
732        name: &[u8],
733        versions: std::ops::RangeInclusive<BlobVersion>,
734    ) -> Result<(Self::Blob, u64, BlobVersion), Error> {
735        let (inner, len, version) = self.inner.open_versioned(partition, name, versions).await?;
736        Ok((
737            DelayedSyncBlob {
738                inner,
739                pending: self.pending.clone(),
740            },
741            len,
742            version,
743        ))
744    }
745
746    async fn remove(&self, partition: &str, name: Option<&[u8]>) -> Result<(), Error> {
747        self.inner.remove(partition, name).await
748    }
749
750    async fn scan(&self, partition: &str) -> Result<Vec<Vec<u8>>, Error> {
751        self.inner.scan(partition).await
752    }
753}
754
755/// Blob wrapper that parks each started sync and supports one-shot blocking sync tracking.
756#[derive(Clone)]
757pub struct DelayedSyncBlob<B> {
758    inner: B,
759    pending: PendingSyncs,
760}
761
762impl<B> DelayedSyncBlob<B> {
763    /// Wrap `inner`, returning the blob and the list its deferred syncs are pushed onto.
764    pub fn new(inner: B) -> (Self, PendingSyncs) {
765        let pending = PendingSyncs::default();
766        (
767            Self {
768                inner,
769                pending: pending.clone(),
770            },
771            pending,
772        )
773    }
774}
775
776impl<B: Blob> Blob for DelayedSyncBlob<B> {
777    async fn read_at_buf(
778        &self,
779        offset: u64,
780        len: usize,
781        bufs: impl Into<IoBufsMut> + Send,
782        options: ReadOptions,
783    ) -> Result<IoBufsMut, Error> {
784        self.inner.read_at_buf(offset, len, bufs, options).await
785    }
786
787    async fn read_at(
788        &self,
789        offset: u64,
790        len: usize,
791        options: ReadOptions,
792    ) -> Result<IoBufsMut, Error> {
793        self.inner.read_at(offset, len, options).await
794    }
795
796    async fn write_at(
797        &self,
798        offset: u64,
799        bufs: impl Into<IoBufs> + Send,
800        options: WriteOptions,
801    ) -> Result<(), Error> {
802        if !options.contains(WriteOptions::SYNC) || !self.pending.tracking() {
803            return self.inner.write_at(offset, bufs, options).await;
804        }
805        self.inner
806            .write_at(offset, bufs, options.without(WriteOptions::SYNC))
807            .await?;
808        self.sync().await
809    }
810
811    async fn resize(&self, len: u64) -> Result<(), Error> {
812        self.inner.resize(len).await
813    }
814
815    async fn sync(&self) -> Result<(), Error> {
816        self.pending.wait().await?;
817        self.inner.sync().await
818    }
819
820    async fn start_sync(&self) -> Handle<()> {
821        let pending = self.pending.clone();
822        let inner = self.inner.clone();
823        let waiter = {
824            let mut state = pending.state.lock();
825            state.starts += 1;
826            // An armed gate takes precedence over parking.
827            state.observe().or_else(|| state.park())
828        };
829        Handle::from_future(async move {
830            let fail = {
831                let mut state = pending.state.lock();
832                state.entered += 1;
833                state.fail
834            };
835            match waiter {
836                Some(waiter) => waiter.wait().await?,
837                None if fail => return Err(injected_sync_failure()),
838                None => {}
839            }
840            inner.sync().await?;
841            pending.state.lock().completions += 1;
842            Ok(())
843        })
844    }
845}
846
847/// Take the oldest pending sync, panicking if none was started.
848pub fn next_pending_sync(pending: &PendingSyncs) -> DeferredSync {
849    let mut pending = pending.lock();
850    assert!(!pending.is_empty(), "no pending sync was started");
851    pending.remove(0)
852}
853
854/// Complete the oldest `count` pending syncs successfully.
855pub fn release_next_pending_syncs(pending: &PendingSyncs, count: usize) {
856    let syncs = {
857        let mut pending = pending.lock();
858        assert!(
859            pending.len() >= count,
860            "not enough pending syncs: have {}, need {count}",
861            pending.len()
862        );
863        pending.drain(..count).collect::<Vec<_>>()
864    };
865    for sync in syncs {
866        let _ = sync.release.send(Ok(()));
867    }
868}
869
870/// Complete all pending syncs successfully.
871pub fn release_pending_syncs(pending: &PendingSyncs) {
872    for sync in mem::take(&mut *pending.lock()) {
873        let _ = sync.release.send(Ok(()));
874    }
875}
876
877/// Drive `fut` to completion, releasing any parked syncs each time it stalls.
878pub async fn drive_pending_syncs<T>(pending: &PendingSyncs, fut: impl Future<Output = T>) -> T {
879    let mut fut = std::pin::pin!(fut);
880    poll_fn(|cx| match fut.as_mut().poll(cx) {
881        Poll::Ready(out) => Poll::Ready(out),
882        Poll::Pending => {
883            // A concurrent task may park a new sync after this release, so
884            // self-wake to check again on the next scheduler tick.
885            release_pending_syncs(pending);
886            cx.waker().wake_by_ref();
887            Poll::Pending
888        }
889    })
890    .await
891}
892
893/// Fail all pending syncs with an injected I/O error.
894pub fn fail_pending_syncs(pending: &PendingSyncs) {
895    for sync in mem::take(&mut *pending.lock()) {
896        let _ = sync.release.send(Err(injected_sync_failure()));
897    }
898}
899
900/// The error injected by [fail_pending_syncs] and [PendingSyncs::arm_fail].
901fn injected_sync_failure() -> Error {
902    Error::Io(std::io::Error::other("injected sync failure").into())
903}
904
905struct SyncWaiter {
906    entered: oneshot::Sender<()>,
907    release: oneshot::Receiver<Result<(), Error>>,
908}
909
910impl SyncWaiter {
911    async fn wait(self) -> Result<(), Error> {
912        self.entered.send_lossy(());
913        self.release.await.map_err(|_| Error::Closed)??;
914        Ok(())
915    }
916}
917
918#[derive(Default)]
919struct SyncGateState {
920    tracking: bool,
921    calls: usize,
922    waiter: Option<SyncWaiter>,
923}
924
925impl PendingSyncs {
926    /// Locks the deferred sync queue.
927    pub fn lock(&self) -> commonware_utils::sync::MappedMutexGuard<'_, Vec<DeferredSync>> {
928        commonware_utils::sync::MutexGuard::map(self.state.lock(), |state| &mut state.syncs)
929    }
930
931    /// Begins counting durability operations and blocks the next one behind a
932    /// one-shot gate (pushed onto the deferred queue so tests can release it).
933    ///
934    /// Once the gate is consumed, started syncs park in the deferred queue as
935    /// usual while [Self::calls] keeps counting.
936    pub fn arm(&self) {
937        let mut state = self.state.lock();
938        assert!(!state.gate.tracking, "sync gate already armed");
939        assert!(
940            state.gate.waiter.is_none(),
941            "sync gate already has a waiter"
942        );
943        state.gate.tracking = true;
944        state.gate.calls = 0;
945        let waiter = state.defer();
946        state.gate.waiter = Some(waiter);
947    }
948
949    /// Returns the number of durability operations observed since [Self::arm].
950    pub fn calls(&self) -> usize {
951        self.state.lock().gate.calls
952    }
953
954    fn tracking(&self) -> bool {
955        self.state.lock().gate.tracking
956    }
957
958    /// Releases every parked sync and permanently stops parking new ones: future started syncs
959    /// proceed immediately (or fail, after [Self::arm_fail]). Unlike [release_pending_syncs],
960    /// which drains the queue once, this is sticky. A gate armed via [Self::arm] parks in the
961    /// same queue, so this releases it too.
962    pub fn unblock(&self) {
963        let (drained, fail) = {
964            let mut state = self.state.lock();
965            state.unblocked = true;
966            (mem::take(&mut state.syncs), state.fail)
967        };
968        for sync in drained {
969            let result = if fail {
970                Err(injected_sync_failure())
971            } else {
972                Ok(())
973            };
974            let _ = sync.release.send(result);
975        }
976    }
977
978    /// Arms every parked and future started sync to resolve to an injected error once released
979    /// via [Self::unblock]. The release helpers send explicit results and ignore this.
980    pub fn arm_fail(&self) {
981        self.state.lock().fail = true;
982    }
983
984    /// Number of started syncs issued.
985    pub fn starts(&self) -> usize {
986        self.state.lock().starts
987    }
988
989    /// Number of started syncs whose completion futures have begun executing, parked or not.
990    pub fn entered(&self) -> usize {
991        self.state.lock().entered
992    }
993
994    /// Number of started syncs that completed durably.
995    pub fn completions(&self) -> usize {
996        self.state.lock().completions
997    }
998
999    async fn wait(&self) -> Result<(), Error> {
1000        let waiter = self.state.lock().observe();
1001        match waiter {
1002            Some(waiter) => waiter.wait().await,
1003            None => Ok(()),
1004        }
1005    }
1006}
1007
1008/// Controls a [WriteFaultContext]: while armed, every `write_at` fails with an
1009/// injected error. Successful writes are counted.
1010#[derive(Clone, Default)]
1011pub struct WriteFaults {
1012    state: Arc<Mutex<WriteFaultState>>,
1013}
1014
1015#[derive(Default)]
1016struct WriteFaultState {
1017    fail: bool,
1018    writes: u64,
1019}
1020
1021impl WriteFaults {
1022    /// Start failing writes.
1023    pub fn arm(&self) {
1024        self.state.lock().fail = true;
1025    }
1026
1027    /// Stop failing writes.
1028    pub fn disarm(&self) {
1029        self.state.lock().fail = false;
1030    }
1031
1032    /// The number of successful writes so far.
1033    pub fn writes(&self) -> u64 {
1034        self.state.lock().writes
1035    }
1036
1037    fn check(&self) -> Result<(), Error> {
1038        if self.state.lock().fail {
1039            return Err(Error::Io(
1040                std::io::Error::other("injected write failure").into(),
1041            ));
1042        }
1043        Ok(())
1044    }
1045
1046    fn note(&self) {
1047        self.state.lock().writes += 1;
1048    }
1049}
1050
1051/// Context wrapper whose blobs fail `write_at` while the shared [WriteFaults]
1052/// is armed, counting successful writes. Unlike [DelayedSyncContext], this injects failures
1053/// into inline writes issued before any blob sync starts.
1054#[derive(Clone)]
1055pub struct WriteFaultContext<E> {
1056    pub inner: E,
1057    pub faults: WriteFaults,
1058}
1059
1060forward_context!(WriteFaultContext, faults);
1061
1062impl<E: Storage> Storage for WriteFaultContext<E> {
1063    type Blob = WriteFaultBlob<E::Blob>;
1064
1065    async fn open_versioned(
1066        &self,
1067        partition: &str,
1068        name: &[u8],
1069        versions: std::ops::RangeInclusive<BlobVersion>,
1070    ) -> Result<(Self::Blob, u64, BlobVersion), Error> {
1071        let (inner, len, version) = self.inner.open_versioned(partition, name, versions).await?;
1072        Ok((
1073            WriteFaultBlob {
1074                inner,
1075                faults: self.faults.clone(),
1076            },
1077            len,
1078            version,
1079        ))
1080    }
1081
1082    async fn remove(&self, partition: &str, name: Option<&[u8]>) -> Result<(), Error> {
1083        self.inner.remove(partition, name).await
1084    }
1085
1086    async fn scan(&self, partition: &str) -> Result<Vec<Vec<u8>>, Error> {
1087        self.inner.scan(partition).await
1088    }
1089}
1090
1091/// Blob wrapper that fails `write_at` while its [WriteFaults] is armed.
1092#[derive(Clone)]
1093pub struct WriteFaultBlob<B> {
1094    inner: B,
1095    faults: WriteFaults,
1096}
1097
1098impl<B: Blob> Blob for WriteFaultBlob<B> {
1099    async fn read_at_buf(
1100        &self,
1101        offset: u64,
1102        len: usize,
1103        bufs: impl Into<IoBufsMut> + Send,
1104        options: ReadOptions,
1105    ) -> Result<IoBufsMut, Error> {
1106        self.inner.read_at_buf(offset, len, bufs, options).await
1107    }
1108
1109    async fn read_at(
1110        &self,
1111        offset: u64,
1112        len: usize,
1113        options: ReadOptions,
1114    ) -> Result<IoBufsMut, Error> {
1115        self.inner.read_at(offset, len, options).await
1116    }
1117
1118    async fn write_at(
1119        &self,
1120        offset: u64,
1121        bufs: impl Into<IoBufs> + Send,
1122        options: WriteOptions,
1123    ) -> Result<(), Error> {
1124        self.faults.check()?;
1125        self.inner.write_at(offset, bufs, options).await?;
1126        self.faults.note();
1127        Ok(())
1128    }
1129
1130    async fn resize(&self, len: u64) -> Result<(), Error> {
1131        self.inner.resize(len).await
1132    }
1133
1134    async fn sync(&self) -> Result<(), Error> {
1135        self.inner.sync().await
1136    }
1137
1138    async fn start_sync(&self) -> Handle<()> {
1139        self.inner.start_sync().await
1140    }
1141}
1142
1143/// Context wrapper whose blobs fail `sync` and `start_sync` for a single partition.
1144#[derive(Clone)]
1145pub struct SyncFaultContext<E> {
1146    pub inner: E,
1147    pub fail_partition: String,
1148}
1149
1150forward_context!(SyncFaultContext, fail_partition);
1151
1152impl<E: Storage> Storage for SyncFaultContext<E> {
1153    type Blob = SyncFaultBlob<E::Blob>;
1154
1155    async fn open_versioned(
1156        &self,
1157        partition: &str,
1158        name: &[u8],
1159        versions: std::ops::RangeInclusive<BlobVersion>,
1160    ) -> Result<(Self::Blob, u64, BlobVersion), Error> {
1161        let (inner, len, version) = self.inner.open_versioned(partition, name, versions).await?;
1162        Ok((
1163            SyncFaultBlob {
1164                inner,
1165                faulty: partition == self.fail_partition,
1166            },
1167            len,
1168            version,
1169        ))
1170    }
1171
1172    async fn remove(&self, partition: &str, name: Option<&[u8]>) -> Result<(), Error> {
1173        self.inner.remove(partition, name).await
1174    }
1175
1176    async fn scan(&self, partition: &str) -> Result<Vec<Vec<u8>>, Error> {
1177        self.inner.scan(partition).await
1178    }
1179}
1180
1181/// Blob wrapper that fails `sync` and `start_sync` when marked faulty.
1182#[derive(Clone)]
1183pub struct SyncFaultBlob<B> {
1184    inner: B,
1185    faulty: bool,
1186}
1187
1188impl<B: Blob> Blob for SyncFaultBlob<B> {
1189    async fn read_at_buf(
1190        &self,
1191        offset: u64,
1192        len: usize,
1193        bufs: impl Into<IoBufsMut> + Send,
1194        options: ReadOptions,
1195    ) -> Result<IoBufsMut, Error> {
1196        self.inner.read_at_buf(offset, len, bufs, options).await
1197    }
1198
1199    async fn read_at(
1200        &self,
1201        offset: u64,
1202        len: usize,
1203        options: ReadOptions,
1204    ) -> Result<IoBufsMut, Error> {
1205        self.inner.read_at(offset, len, options).await
1206    }
1207
1208    async fn write_at(
1209        &self,
1210        offset: u64,
1211        bufs: impl Into<IoBufs> + Send,
1212        options: WriteOptions,
1213    ) -> Result<(), Error> {
1214        self.inner.write_at(offset, bufs, options).await
1215    }
1216
1217    async fn resize(&self, len: u64) -> Result<(), Error> {
1218        self.inner.resize(len).await
1219    }
1220
1221    async fn sync(&self) -> Result<(), Error> {
1222        if self.faulty {
1223            let err = std::io::Error::other("injected partition sync fault");
1224            return Err(Error::Io(err.into()));
1225        }
1226        self.inner.sync().await
1227    }
1228
1229    async fn start_sync(&self) -> Handle<()> {
1230        if self.faulty {
1231            return Handle::ready(self.sync().await);
1232        }
1233        self.inner.start_sync().await
1234    }
1235}
1236
1237#[cfg(test)]
1238mod tests {
1239    use super::*;
1240    use crate::{Clock, IoBufMut, Runner, Sink, Spawner, Stream, deterministic};
1241    use commonware_macros::select;
1242    use std::{thread::sleep, time::Duration};
1243
1244    #[test]
1245    fn recording_context_preserves_data_and_records_options() {
1246        deterministic::Runner::default().start(|context| async move {
1247            let (context, recordings) = RecordingContext::new(context);
1248            let (blob, _) = context.open("recording", b"blob").await.unwrap();
1249
1250            blob.write_at(0, b"data", WriteOptions::DONT_CACHE)
1251                .await
1252                .unwrap();
1253            let read = blob.read_at(0, 4, ReadOptions::DONT_CACHE).await.unwrap();
1254            assert_eq!(read.coalesce(), b"data");
1255
1256            let read = blob
1257                .read_at_buf(0, 4, IoBufMut::with_capacity(4), ReadOptions::default())
1258                .await
1259                .unwrap();
1260            assert_eq!(read.coalesce(), b"data");
1261
1262            assert_eq!(
1263                recordings.snapshot(),
1264                RecordingSnapshot {
1265                    reads: vec![ReadOptions::DONT_CACHE, ReadOptions::default()],
1266                    writes: vec![WriteOptions::DONT_CACHE],
1267                }
1268            );
1269            recordings.clear();
1270            assert_eq!(recordings.snapshot(), RecordingSnapshot::default());
1271        });
1272    }
1273
1274    async fn assert_read_options_forwarded<E: Storage>(
1275        context: &E,
1276        recordings: &Recordings,
1277        partition: &str,
1278    ) {
1279        let (blob, _) = context.open(partition, b"blob").await.unwrap();
1280        blob.write_at(0, b"data", WriteOptions::default())
1281            .await
1282            .unwrap();
1283        recordings.clear();
1284
1285        let read = blob.read_at(0, 4, ReadOptions::DONT_CACHE).await.unwrap();
1286        assert_eq!(read.coalesce(), b"data");
1287
1288        let read = blob
1289            .read_at_buf(0, 4, IoBufMut::with_capacity(4), ReadOptions::DONT_CACHE)
1290            .await
1291            .unwrap();
1292        assert_eq!(read.coalesce(), b"data");
1293
1294        assert_eq!(
1295            recordings.snapshot(),
1296            RecordingSnapshot {
1297                reads: vec![ReadOptions::DONT_CACHE, ReadOptions::DONT_CACHE],
1298                writes: Vec::new(),
1299            }
1300        );
1301    }
1302
1303    #[test]
1304    fn delayed_sync_blob_forwards_read_options() {
1305        deterministic::Runner::default().start(|context| async move {
1306            let (inner, recordings) = RecordingContext::new(context);
1307            let context = DelayedSyncContext {
1308                inner,
1309                pending: PendingSyncs::default(),
1310            };
1311
1312            assert_read_options_forwarded(&context, &recordings, "delayed_sync").await;
1313        });
1314    }
1315
1316    #[test]
1317    fn write_fault_blob_forwards_read_options() {
1318        deterministic::Runner::default().start(|context| async move {
1319            let (inner, recordings) = RecordingContext::new(context);
1320            let context = WriteFaultContext {
1321                inner,
1322                faults: WriteFaults::default(),
1323            };
1324
1325            assert_read_options_forwarded(&context, &recordings, "write_fault").await;
1326        });
1327    }
1328
1329    #[test]
1330    fn sync_fault_blob_forwards_read_options() {
1331        deterministic::Runner::default().start(|context| async move {
1332            let (inner, recordings) = RecordingContext::new(context);
1333            let context = SyncFaultContext {
1334                inner,
1335                fail_partition: "sync_fault".to_string(),
1336            };
1337
1338            assert_read_options_forwarded(&context, &recordings, "sync_fault").await;
1339        });
1340    }
1341
1342    #[test]
1343    fn test_send_recv() {
1344        let (mut sink, mut stream) = Channel::init();
1345        let data = b"hello world";
1346
1347        let executor = deterministic::Runner::default();
1348        executor.start(|_| async move {
1349            sink.send(data.as_slice()).await.unwrap();
1350            let received = stream.recv(data.len()).await.unwrap();
1351            assert_eq!(received.coalesce(), data);
1352        });
1353    }
1354
1355    #[test]
1356    fn test_send_recv_partial_multiple() {
1357        let (mut sink, mut stream) = Channel::init();
1358        let data = b"hello";
1359        let data2 = b" world";
1360
1361        let executor = deterministic::Runner::default();
1362        executor.start(|_| async move {
1363            sink.send(data.as_slice()).await.unwrap();
1364            sink.send(data2.as_slice()).await.unwrap();
1365            let received = stream.recv(5).await.unwrap();
1366            assert_eq!(received.coalesce(), b"hello");
1367            let received = stream.recv(5).await.unwrap();
1368            assert_eq!(received.coalesce(), b" worl");
1369            let received = stream.recv(1).await.unwrap();
1370            assert_eq!(received.coalesce(), b"d");
1371        });
1372    }
1373
1374    #[test]
1375    fn test_send_recv_async() {
1376        let (mut sink, mut stream) = Channel::init();
1377        let data = b"hello world";
1378
1379        let executor = deterministic::Runner::default();
1380        executor.start(|_| async move {
1381            let (received, _) = futures::try_join!(stream.recv(data.len()), async {
1382                sleep(Duration::from_millis(50));
1383                sink.send(data.as_slice()).await
1384            })
1385            .unwrap();
1386            assert_eq!(received.coalesce(), data);
1387        });
1388    }
1389
1390    #[test]
1391    fn test_recv_error_sink_dropped_while_waiting() {
1392        let (sink, mut stream) = Channel::init();
1393
1394        let executor = deterministic::Runner::default();
1395        executor.start(|context| async move {
1396            futures::join!(
1397                async {
1398                    let result = stream.recv(5).await;
1399                    assert!(matches!(result, Err(Error::RecvFailed)));
1400                    let result = stream.recv(5).await;
1401                    assert!(matches!(result, Err(Error::Closed)));
1402                },
1403                async {
1404                    // Wait for the stream to start waiting
1405                    context.sleep(Duration::from_millis(50)).await;
1406                    drop(sink);
1407                }
1408            );
1409        });
1410    }
1411
1412    #[test]
1413    fn test_recv_error_sink_dropped_before_recv() {
1414        let (sink, mut stream) = Channel::init();
1415        drop(sink); // Drop sink immediately
1416
1417        let executor = deterministic::Runner::default();
1418        executor.start(|_| async move {
1419            let result = stream.recv(5).await;
1420            assert!(matches!(result, Err(Error::RecvFailed)));
1421            let result = stream.recv(5).await;
1422            assert!(matches!(result, Err(Error::Closed)));
1423        });
1424    }
1425
1426    #[test]
1427    fn test_send_error_stream_dropped() {
1428        let (mut sink, mut stream) = Channel::init();
1429
1430        let executor = deterministic::Runner::default();
1431        executor.start(|context| async move {
1432            // Send some bytes
1433            assert!(sink.send(b"7 bytes".as_slice()).await.is_ok());
1434
1435            // Spawn a task to initiate recv's where the first one will succeed and then will drop.
1436            let handle = context.child("recv").spawn(|_| async move {
1437                let _ = stream.recv(5).await;
1438                let _ = stream.recv(5).await;
1439            });
1440
1441            // Give the async task a moment to start
1442            context.sleep(Duration::from_millis(50)).await;
1443
1444            // Drop the stream by aborting the handle
1445            handle.abort();
1446            assert!(matches!(handle.await, Err(Error::Closed)));
1447
1448            // Try to send a message. The stream is dropped, so this should fail.
1449            let result = sink.send(b"hello world".as_slice()).await;
1450            assert!(matches!(result, Err(Error::SendFailed)));
1451            let result = sink.send(b"hello world".as_slice()).await;
1452            assert!(matches!(result, Err(Error::Closed)));
1453        });
1454    }
1455
1456    #[test]
1457    fn test_send_error_stream_dropped_before_send() {
1458        let (mut sink, stream) = Channel::init();
1459        drop(stream); // Drop stream immediately
1460
1461        let executor = deterministic::Runner::default();
1462        executor.start(|_| async move {
1463            let result = sink.send(b"hello world".as_slice()).await;
1464            assert!(matches!(result, Err(Error::SendFailed)));
1465            let result = sink.send(b"hello world".as_slice()).await;
1466            assert!(matches!(result, Err(Error::Closed)));
1467        });
1468    }
1469
1470    #[test]
1471    fn test_recv_timeout() {
1472        let (_sink, mut stream) = Channel::init();
1473
1474        // If there is no data to read, test that the recv function just blocks.
1475        // The timeout should return first.
1476        let executor = deterministic::Runner::default();
1477        executor.start(|context| async move {
1478            select! {
1479                v = stream.recv(5) => {
1480                    panic!("unexpected value: {v:?}");
1481                },
1482                _ = context.sleep(Duration::from_millis(100)) => "timeout",
1483            };
1484        });
1485    }
1486
1487    #[test]
1488    fn test_peek_empty() {
1489        let (_sink, stream) = Channel::init();
1490
1491        // Peek on a fresh stream should return empty slice
1492        assert!(stream.peek(10).is_empty());
1493    }
1494
1495    #[test]
1496    fn test_peek_after_partial_recv() {
1497        let (mut sink, mut stream) = Channel::init();
1498
1499        let executor = deterministic::Runner::default();
1500        executor.start(|_| async move {
1501            // Send more data than we'll consume
1502            sink.send(b"hello world".as_slice()).await.unwrap();
1503
1504            // Recv only part of it
1505            let received = stream.recv(5).await.unwrap();
1506            assert_eq!(received.coalesce(), b"hello");
1507
1508            // Peek should show the remaining data
1509            assert_eq!(stream.peek(100), b" world");
1510
1511            // Peek with smaller max_len
1512            assert_eq!(stream.peek(3), b" wo");
1513
1514            // Peek doesn't consume - can peek again
1515            assert_eq!(stream.peek(100), b" world");
1516
1517            // Recv consumes the peeked data
1518            let received = stream.recv(6).await.unwrap();
1519            assert_eq!(received.coalesce(), b" world");
1520
1521            // Peek is now empty
1522            assert!(stream.peek(100).is_empty());
1523        });
1524    }
1525
1526    #[test]
1527    fn test_peek_after_recv_wakeup() {
1528        let (mut sink, mut stream) = Channel::init_with_buffer_size(64);
1529
1530        let executor = deterministic::Runner::default();
1531        executor.start(|context| async move {
1532            // Spawn recv that will block waiting
1533            let (tx, rx) = oneshot::channel();
1534            let recv_handle = context.child("recv").spawn(|_| async move {
1535                let data = stream.recv(3).await.unwrap();
1536                tx.send(stream).ok();
1537                data
1538            });
1539
1540            // Let recv set up waiter
1541            context.sleep(Duration::from_millis(10)).await;
1542
1543            // Send more than requested
1544            sink.send(b"ABCDEFGHIJ".as_slice()).await.unwrap();
1545
1546            // Recv gets its 3 bytes
1547            let received = recv_handle.await.unwrap();
1548            assert_eq!(received.coalesce(), b"ABC");
1549
1550            // Get stream back and verify peek sees remaining data
1551            let stream = rx.await.unwrap();
1552            assert_eq!(stream.peek(100), b"DEFGHIJ");
1553        });
1554    }
1555
1556    #[test]
1557    fn test_peek_multiple_sends() {
1558        let (mut sink, mut stream) = Channel::init();
1559
1560        let executor = deterministic::Runner::default();
1561        executor.start(|_| async move {
1562            // Send multiple chunks
1563            sink.send(b"aaa".as_slice()).await.unwrap();
1564            sink.send(b"bbb".as_slice()).await.unwrap();
1565            sink.send(b"ccc".as_slice()).await.unwrap();
1566
1567            // Recv less than total
1568            let received = stream.recv(4).await.unwrap();
1569            assert_eq!(received.coalesce(), b"aaab");
1570
1571            // Peek should show remaining
1572            assert_eq!(stream.peek(100), b"bbccc");
1573        });
1574    }
1575
1576    #[test]
1577    fn test_buffer_size_limit() {
1578        // Use a small buffer capacity for testing
1579        let (mut sink, mut stream) = Channel::init_with_buffer_size(10);
1580
1581        let executor = deterministic::Runner::default();
1582        executor.start(|context| async move {
1583            // Send more than buffer capacity concurrently with recv
1584            // so the sender can drain via backpressure.
1585            let send_handle = context.child("sender").spawn(|_| async move {
1586                sink.send(b"0123456789ABCDEF".as_slice()).await.unwrap();
1587                sink
1588            });
1589
1590            // Recv a small amount - should only pull up to capacity (10 bytes)
1591            let received = stream.recv(2).await.unwrap();
1592            assert_eq!(received.coalesce(), b"01");
1593
1594            // Peek should show remaining buffered data (8 bytes, not 14)
1595            assert_eq!(stream.peek(100), b"23456789");
1596
1597            // The rest should still be in the channel buffer
1598            // Recv more to pull the remaining data
1599            let received = stream.recv(8).await.unwrap();
1600            assert_eq!(received.coalesce(), b"23456789");
1601
1602            // Now peek should show next chunk from channel (up to capacity)
1603            let received = stream.recv(2).await.unwrap();
1604            assert_eq!(received.coalesce(), b"AB");
1605
1606            assert_eq!(stream.peek(100), b"CDEF");
1607
1608            // Ensure the sender completes
1609            send_handle.await.unwrap();
1610        });
1611    }
1612
1613    #[test]
1614    fn test_recv_before_send() {
1615        // Use a small buffer capacity for testing
1616        let (mut sink, mut stream) = Channel::init_with_buffer_size(10);
1617
1618        let executor = deterministic::Runner::default();
1619        executor.start(|context| async move {
1620            // Start recv before send (will wait)
1621            let recv_handle = context
1622                .child("recv")
1623                .spawn(|_| async move { stream.recv(3).await.unwrap() });
1624
1625            // Give recv time to set up waiter
1626            context.sleep(Duration::from_millis(10)).await;
1627
1628            // Send more than capacity
1629            sink.send(b"ABCDEFGHIJKLMNOP".as_slice()).await.unwrap();
1630
1631            // Recv should get its 3 bytes
1632            let received = recv_handle.await.unwrap();
1633            assert_eq!(received.coalesce(), b"ABC");
1634        });
1635    }
1636}