Skip to main content

hegel/extras/rand/
generators.rs

1use std::convert::Infallible;
2
3use rand::Rng;
4use rand::SeedableRng;
5use rand::rand_core::TryRng;
6use rand::rngs::StdRng;
7
8use crate::generators::{Generator, TestCase, binary, integers};
9
10/// Generator for random number generators. Created by [`randoms()`].
11///
12/// By default, produces a [`HegelRandom::ArtificialRandom`] backed by the
13/// test case data, which allows Hegel to shrink the randomness. Use
14/// [`use_true_random()`](Self::use_true_random) to get a seeded `StdRng` instead.
15pub struct RandomsGenerator {
16    use_true_random: bool,
17}
18
19impl RandomsGenerator {
20    /// Set whether to use a seeded `StdRng` instead of test-case-backed randomness.
21    ///
22    /// True random values are not shrinkable.
23    pub fn use_true_random(mut self, use_true_random: bool) -> Self {
24        self.use_true_random = use_true_random;
25        self
26    }
27}
28
29impl Generator<HegelRandom> for RandomsGenerator {
30    fn do_draw(&self, tc: &TestCase) -> HegelRandom {
31        if self.use_true_random {
32            let seed: u64 = integers().do_draw(tc);
33            HegelRandom::TrueRandom(Box::new(StdRng::seed_from_u64(seed)))
34        } else {
35            HegelRandom::ArtificialRandom(tc.clone())
36        }
37    }
38}
39
40/// A random number generator produced by [`randoms()`].
41///
42/// Implements [`Rng`] from the `rand` crate.
43#[derive(Debug)]
44pub enum HegelRandom {
45    /// Backed by test case data. Shrinkable.
46    ArtificialRandom(TestCase),
47    /// Backed by a seeded `StdRng`. Not shrinkable.
48    TrueRandom(Box<StdRng>),
49}
50
51impl TryRng for HegelRandom {
52    type Error = Infallible;
53
54    fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
55        Ok(match self {
56            Self::ArtificialRandom(tc) => integers().do_draw(tc),
57            Self::TrueRandom(rng) => rng.next_u32(),
58        })
59    }
60
61    fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
62        Ok(match self {
63            Self::ArtificialRandom(tc) => integers().do_draw(tc),
64            Self::TrueRandom(rng) => rng.next_u64(),
65        })
66    }
67
68    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Self::Error> {
69        match self {
70            Self::ArtificialRandom(tc) => {
71                let bytes: Vec<u8> = binary()
72                    .min_size(dest.len())
73                    .max_size(dest.len())
74                    .do_draw(tc);
75                dest.copy_from_slice(&bytes);
76            }
77            Self::TrueRandom(rng) => rng.fill_bytes(dest),
78        }
79        Ok(())
80    }
81}
82
83/// Creates a generator for random number generators.
84///
85/// See [`RandomsGenerator`] for builder methods.
86///
87/// ```no_run
88/// use hegel::extras::rand as rand_gs;
89/// use rand::RngExt;
90/// use rand::prelude::{IndexedRandom, SliceRandom};
91///
92/// #[hegel::test]
93/// fn my_test(tc: hegel::TestCase) {
94///     let mut rng = tc.draw(rand_gs::randoms());
95///
96///     let a: i32 = rng.random_range(1..=100);
97///     let b: bool = rng.random();
98///     let c = vec![1, 2, 3, 4, 5].choose(&mut rng);
99///     vec![1, 2, 3].shuffle(&mut rng);
100/// }
101/// ```
102pub fn randoms() -> RandomsGenerator {
103    RandomsGenerator {
104        use_true_random: false,
105    }
106}