Skip to main content

tinyrand_alloc/
mock.rs

1//! Mock RNG for testing.
2
3use alloc::boxed::Box;
4use core::cell::RefCell;
5use core::ops::Range;
6use tinyrand::{Probability, Rand};
7
8/// Mock invocation state.
9#[derive(Default)]
10pub struct State {
11    next_u128_invocations: u64,
12    next_bool_invocations: u64,
13    next_lim_u128_invocations: u64,
14}
15
16impl State {
17    /// Obtains the number of invocations of the [`Rand::next_u128`] method.
18    pub fn next_u128_invocations(&self) -> u64 {
19        self.next_u128_invocations
20    }
21
22    /// Obtains the number of invocations of the [`Rand::next_bool`] method.
23    pub fn next_bool_invocations(&self) -> u64 {
24        self.next_bool_invocations
25    }
26
27    /// Obtains the number of invocations of the [`Rand::next_lim_u128`] method.
28    pub fn next_lim_u128_invocations(&self) -> u64 {
29        self.next_lim_u128_invocations
30    }
31}
32
33/// Encapsulates the state of the mock and a reference to the `next_u128` delegate, so
34/// that it can be invoked from inside the mock by, for example, another delegate.
35pub struct Surrogate<'a, 'd> {
36    state: &'a mut State,
37    next_u128_delegate: &'a mut Box<dyn FnMut(&State) -> u128 + 'd>,
38}
39
40impl Surrogate<'_, '_> {
41    /// Obtains the mock state.
42    pub fn state(&self) -> &State {
43        self.state
44    }
45}
46
47impl<'a, 'd> Rand for Surrogate<'a, 'd> {
48    fn next_u64(&mut self) -> u64 {
49        self.next_u128() as u64
50    }
51
52    fn next_u128(&mut self) -> u128 {
53        let r = (self.next_u128_delegate)(self.state);
54        self.state.next_u128_invocations += 1;
55        r
56    }
57}
58
59/// Mock RNG, containing invocation state and delegate closures.
60pub struct Mock<'d> {
61    state: State,
62    next_u128_delegate: Box<dyn FnMut(&State) -> u128 + 'd>,
63    next_bool_delegate: Box<dyn FnMut(Surrogate, Probability) -> bool + 'd>,
64    next_lim_u128_delegate: Box<dyn FnMut(Surrogate, u128) -> u128 + 'd>,
65}
66
67impl<'a> Default for Mock<'a> {
68    fn default() -> Self {
69        Self {
70            state: State::default(),
71            next_u128_delegate: Box::new(fixed(0)),
72            next_bool_delegate: Box::new(|mut surrogate, p| Rand::next_bool(&mut surrogate, p)),
73            next_lim_u128_delegate: Box::new(|mut surrogate, lim| {
74                Rand::next_lim_u128(&mut surrogate, lim)
75            }),
76        }
77    }
78}
79
80impl<'d> Mock<'d> {
81    /// Assigns a [`Rand::next_u128`] delegate to the mock. I.e., when the [`Rand::next_u128`]
82    /// method is invoked on the mock (directly, or via another method), it will delegate to
83    /// the given closure.
84    ///
85    /// # Examples
86    /// ```
87    /// use tinyrand::Rand;
88    /// use tinyrand_alloc::Mock;
89    /// let mut mock = Mock::default()
90    ///     .with_next_u128(|_| 42);
91    /// assert_eq!(42, mock.next_usize());
92    /// assert_eq!(42, mock.next_u64());
93    /// assert_eq!(42, mock.next_u128());
94    /// ```
95    #[must_use]
96    pub fn with_next_u128(mut self, delegate: impl FnMut(&State) -> u128 + 'd) -> Self {
97        self.next_u128_delegate = Box::new(delegate);
98        self
99    }
100
101    /// Assigns a [`Rand::next_bool`] delegate to the mock. I.e., when the [`Rand::next_bool`]
102    /// method is invoked on the mock, it will delegate to the given closure.
103    ///
104    /// # Examples
105    /// ```
106    /// use tinyrand::{Probability, Rand};
107    /// use tinyrand_alloc::Mock;
108    /// let mut mock = Mock::default()
109    ///     .with_next_bool(|_, _| true);
110    /// assert!(mock.next_bool(Probability::new(0.01)));
111    /// ```
112    #[must_use]
113    pub fn with_next_bool(
114        mut self,
115        delegate: impl FnMut(Surrogate, Probability) -> bool + 'd,
116    ) -> Self {
117        self.next_bool_delegate = Box::new(delegate);
118        self
119    }
120
121    /// Assigns a [`Rand::next_lim_u128`] delegate to the mock. I.e., when the [`Rand::next_lim_u128`]
122    /// method is invoked on the mock, it will delegate to the given closure. This delegate can be
123    /// used to effectively mock `Rand::next_lim` and `Rand::next_range` methods.
124    ///
125    /// # Examples
126    /// ```
127    /// use tinyrand::{Rand, RandRange};
128    /// use tinyrand_alloc::Mock;
129    /// let mut mock = Mock::default()
130    ///     .with_next_lim_u128(|_, _| 17);
131    /// assert_eq!(17, mock.next_lim_u64(66));
132    /// assert_eq!(27, mock.next_range(10..100u16));
133    /// ```
134    #[must_use]
135    pub fn with_next_lim_u128(
136        mut self,
137        delegate: impl FnMut(Surrogate, u128) -> u128 + 'd,
138    ) -> Self {
139        self.next_lim_u128_delegate = Box::new(delegate);
140        self
141    }
142
143    /// Obtains the underlying mock state.
144    pub fn state(&self) -> &State {
145        &self.state
146    }
147}
148
149impl Rand for Mock<'_> {
150    fn next_u64(&mut self) -> u64 {
151        self.next_u128() as u64
152    }
153
154    /// Delegates to the underlying closure and increments the `state.next_u128_invocations` counter
155    /// _after_ the closure returns.
156    fn next_u128(&mut self) -> u128 {
157        let next_u64_delegate = &mut self.next_u128_delegate;
158        let r = next_u64_delegate(&self.state);
159        self.state.next_u128_invocations += 1;
160        r
161    }
162
163    /// Delegates to the underlying closure and increments the `state.next_bool_invocations` counter
164    /// _after_ the closure returns.
165    fn next_bool(&mut self, p: Probability) -> bool {
166        let surrogate = Surrogate {
167            next_u128_delegate: &mut self.next_u128_delegate,
168            state: &mut self.state,
169        };
170        let next_bool_delegate = &mut self.next_bool_delegate;
171        let r = next_bool_delegate(surrogate, p);
172        self.state.next_bool_invocations += 1;
173        r
174    }
175
176    fn next_lim_u16(&mut self, lim: u16) -> u16 {
177        self.next_lim_u128(u128::from(lim)) as u16
178    }
179
180    fn next_lim_u32(&mut self, lim: u32) -> u32 {
181        self.next_lim_u128(u128::from(lim)) as u32
182    }
183
184    fn next_lim_u64(&mut self, lim: u64) -> u64 {
185        self.next_lim_u128(u128::from(lim)) as u64
186    }
187
188    /// Delegates to the underlying closure and increments the `state.next_lim_u128_invocations` counter
189    /// _after_ the closure returns.
190    fn next_lim_u128(&mut self, lim: u128) -> u128 {
191        let surrogate = Surrogate {
192            next_u128_delegate: &mut self.next_u128_delegate,
193            state: &mut self.state,
194        };
195        let next_lim_delegate = &mut self.next_lim_u128_delegate;
196        let r = next_lim_delegate(surrogate, lim);
197        self.state.next_lim_u128_invocations += 1;
198        r
199    }
200}
201
202/// A pre-canned delegate that counts in the given range, wrapping around when it reaches
203/// the end.
204///
205/// # Examples
206/// ```
207/// use tinyrand::Rand;
208/// use tinyrand_alloc::{Mock, counter};
209///
210/// let mut mock = Mock::default().with_next_u128(counter(5..8));
211/// assert_eq!(5, mock.next_u64());
212/// assert_eq!(6, mock.next_u64());
213/// assert_eq!(7, mock.next_u64());
214/// assert_eq!(5, mock.next_u64());
215/// ```
216pub fn counter(range: Range<u128>) -> impl FnMut(&State) -> u128 {
217    let mut current = range.start;
218    move |_| {
219        let c = current;
220        let next = current + 1;
221        current = if next == range.end { range.start } else { next };
222        c
223    }
224}
225
226/// A pre-canned delegate that always parrots a given value.
227///
228/// # Examples
229/// ```
230/// use tinyrand::Rand;
231/// use tinyrand_alloc::{Mock, fixed};
232///
233/// let mut mock = Mock::default().with_next_u128(fixed(42));
234/// assert_eq!(42, mock.next_u64());
235/// assert_eq!(42, mock.next_u64());
236/// ```
237pub fn fixed(val: u128) -> impl FnMut(&State) -> u128 {
238    move |_| val
239}
240
241/// A pre-canned delegate that parrots the value in the given cell.
242///
243/// # Examples
244/// ```
245/// use std::cell::RefCell;
246/// use tinyrand::{Rand, RefCellExt};
247/// use tinyrand_alloc::{Mock, echo};
248///
249/// let cell = RefCell::default();
250/// let mut mock = Mock::default().with_next_u128(echo(&cell));
251/// assert_eq!(0, mock.next_u64());
252/// cell.set(42);
253/// assert_eq!(42, mock.next_u64());
254/// ```
255pub fn echo(cell: &RefCell<u128>) -> impl FnMut(&State) -> u128 + '_ {
256    move |_| *cell.borrow()
257}
258
259#[cfg(test)]
260mod tests;