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;