Skip to main content

bytesbuf_io/testing/
fake_write.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use std::convert::Infallible;
5
6use bytesbuf::mem::testing::TransparentMemory;
7use bytesbuf::mem::{HasMemory, Memory, MemoryShared, OpaqueMemory};
8use bytesbuf::{BytesBuf, BytesView};
9
10use crate::Write;
11
12/// A [`Write`] that collects all written data into itself.
13///
14/// This is for test and example purposes only and is not optimized for performance.
15#[derive(Debug)]
16pub struct FakeWrite {
17    contents: BytesBuf,
18
19    memory: OpaqueMemory,
20}
21
22impl FakeWrite {
23    /// Starts building a new `FakeWrite`.
24    #[must_use]
25    pub fn builder() -> FakeWriteBuilder {
26        FakeWriteBuilder {
27            memory: OpaqueMemory::new(TransparentMemory::new()),
28        }
29    }
30
31    /// Creates a new `FakeWrite` with the default configuration.
32    #[must_use]
33    pub fn new() -> Self {
34        Self::builder().build()
35    }
36
37    /// Consumes the instance and returns a [`BytesView`] with the contents that were written to it.
38    #[must_use]
39    pub fn into_contents(mut self) -> BytesView {
40        self.contents.consume_all()
41    }
42
43    /// References the contents written into the stream so far.
44    ///
45    /// The contents are stored in a `BytesBuf` which may be inspected by the caller.
46    #[must_use]
47    pub fn contents(&self) -> &BytesBuf {
48        &self.contents
49    }
50
51    /// Writes the provided byte sequence to the stream.
52    ///
53    /// # Errors
54    ///
55    /// This call never fails.
56    #[expect(clippy::unused_async, reason = "API compatibility between trait and inherent fn")]
57    pub async fn write(&mut self, data: BytesView) -> Result<(), Infallible> {
58        self.contents.put_bytes(data);
59        Ok(())
60    }
61
62    /// Returns the memory provider that was configured in the builder.
63    #[must_use]
64    pub fn memory(&self) -> impl MemoryShared {
65        self.memory.clone()
66    }
67
68    /// Reserves at least `min_bytes` bytes of memory capacity.
69    ///
70    /// Returns an empty [`BytesBuf`] that can be used to fill the reserved memory with data.
71    ///
72    /// The memory provider may provide more memory than requested.
73    ///
74    /// The memory reservation request will always be fulfilled, obtaining more memory from the
75    /// operating system if necessary.
76    ///
77    /// # Zero-sized reservations
78    ///
79    /// Reserving zero bytes of memory is a valid operation and will return a [`BytesBuf`]
80    /// with zero or more bytes of capacity.
81    ///
82    /// # Panics
83    ///
84    /// May panic if the operating system runs out of memory.
85    #[must_use]
86    pub fn reserve(&self, min_bytes: usize) -> BytesBuf {
87        self.memory.reserve(min_bytes)
88    }
89}
90
91impl Default for FakeWrite {
92    fn default() -> Self {
93        Self::new()
94    }
95}
96
97#[cfg_attr(coverage_nightly, coverage(off))] // Trivial forwarder.
98impl Write for FakeWrite {
99    type Error = Infallible;
100
101    #[cfg_attr(test, mutants::skip)] // Trivial forwarder.
102    async fn write(&mut self, data: BytesView) -> Result<(), Infallible> {
103        self.write(data).await
104    }
105}
106
107#[cfg_attr(coverage_nightly, coverage(off))] // Trivial forwarder.
108impl Memory for FakeWrite {
109    #[cfg_attr(test, mutants::skip)] // Trivial forwarder.
110    fn reserve(&self, min_bytes: usize) -> BytesBuf {
111        self.reserve(min_bytes)
112    }
113}
114
115#[cfg_attr(coverage_nightly, coverage(off))] // Trivial forwarder.
116impl HasMemory for FakeWrite {
117    #[cfg_attr(test, mutants::skip)] // Trivial forwarder.
118    fn memory(&self) -> impl MemoryShared {
119        self.memory()
120    }
121}
122
123/// Creates an instance of [`FakeWrite`].
124///
125/// Access through [`FakeWrite::builder()`][FakeWrite::builder].
126#[derive(Debug)]
127pub struct FakeWriteBuilder {
128    memory: OpaqueMemory,
129}
130
131impl FakeWriteBuilder {
132    /// The memory provider to use in memory-related stream operations.
133    ///
134    /// Optional. Defaults to using the Rust global allocator.
135    #[must_use]
136    pub fn memory(mut self, memory: OpaqueMemory) -> Self {
137        self.memory = memory;
138        self
139    }
140
141    /// Builds the `FakeWrite` with the provided configuration.
142    #[must_use]
143    pub fn build(self) -> FakeWrite {
144        FakeWrite {
145            contents: BytesBuf::new(),
146            memory: self.memory,
147        }
148    }
149}
150
151#[cfg(test)]
152#[cfg_attr(coverage_nightly, coverage(off))]
153mod tests {
154    use std::sync::Arc;
155    use std::sync::atomic::{AtomicBool, Ordering};
156
157    use bytesbuf::mem::CallbackMemory;
158    use testing_aids::async_test;
159
160    use super::*;
161    use crate::WriteExt;
162
163    #[test]
164    fn smoke_test() {
165        async_test(async || {
166            let mut write_stream = FakeWrite::new();
167
168            write_stream
169                .prepare_and_write(1234, |mut buf| {
170                    buf.put_byte(1);
171                    buf.put_byte(2);
172                    buf.put_byte(3);
173                    Ok::<BytesView, Infallible>(buf.consume_all())
174                })
175                .await
176                .unwrap();
177
178            assert_eq!(write_stream.contents().len(), 3);
179
180            let mut contents = write_stream.into_contents();
181            assert_eq!(contents.len(), 3);
182
183            assert_eq!(contents.get_byte(), 1);
184            assert_eq!(contents.get_byte(), 2);
185            assert_eq!(contents.get_byte(), 3);
186            assert_eq!(contents.len(), 0);
187        });
188    }
189
190    #[test]
191    fn default_returns_working_instance() {
192        async_test(async || {
193            let mut write_stream = FakeWrite::default();
194
195            write_stream
196                .prepare_and_write(10, |mut buf| {
197                    buf.put_byte(42);
198                    Ok::<BytesView, Infallible>(buf.consume_all())
199                })
200                .await
201                .unwrap();
202
203            assert_eq!(write_stream.contents().len(), 1);
204
205            let mut contents = write_stream.into_contents();
206            assert_eq!(contents.get_byte(), 42);
207        });
208    }
209
210    #[test]
211    fn memory_returns_configured_provider() {
212        let callback_called = Arc::new(AtomicBool::new(false));
213
214        let custom_memory = OpaqueMemory::new(CallbackMemory::new({
215            let callback_called = Arc::clone(&callback_called);
216            move |min_bytes| {
217                callback_called.store(true, Ordering::SeqCst);
218                TransparentMemory::new().reserve(min_bytes)
219            }
220        }));
221
222        let write_stream = FakeWrite::builder().memory(custom_memory).build();
223
224        // Get memory from stream and use it
225        let stream_memory = write_stream.memory();
226        let _buf = stream_memory.reserve(10);
227
228        assert!(
229            callback_called.load(Ordering::SeqCst),
230            "Custom memory callback should have been called"
231        );
232    }
233}