antithesis_sdk/
random.rs

1use rand::{Error, RngCore};
2use crate::internal;
3
4/// Returns a u64 value chosen by Antithesis. You should not
5/// store this value or use it to seed a PRNG, but should use it
6/// immediately.
7///
8/// # Example
9///
10/// ```
11/// use antithesis_sdk::random;
12///
13/// let value = random::get_random();
14/// println!("Random value(u64): {value}");
15/// ```
16pub fn get_random() -> u64 {
17    internal::dispatch_random()
18}
19
20/// Returns a randomly chosen item from a list of options. You
21/// should not store this value, but should use it immediately.
22///
23/// This function is not purely for convenience. Signaling to
24/// the Antithesis platform that you intend to use a random value
25/// in a structured way enables it to provide more interesting
26/// choices over time.
27///
28/// # Example
29///
30/// ```
31/// use antithesis_sdk::random;
32///
33/// let choices: Vec<&str> = vec!["abc", "def", "xyz", "qrs"];
34/// if let Some(s) = random::random_choice(choices.as_slice()) {
35///     println!("Choice: '{s}'");
36/// };
37/// ```
38pub fn random_choice<T>(slice: &[T]) -> Option<&T> {
39    match slice {
40        [] => None,
41        [x] => Some(x),
42        _ => {
43            let idx: usize = (get_random() as usize) % slice.len();
44            Some(&slice[idx])
45        }
46    }
47}
48
49/// A random number generator that uses Antithesis's random number generation.
50///
51/// This implements the `RngCore` trait from the `rand` crate, allowing it to be used
52/// with any code that expects a random number generator from that ecosystem.
53///
54/// # Example
55///
56/// ```
57/// use antithesis_sdk::random::AntithesisRng;
58/// use rand::{Rng, RngCore};
59///
60/// let mut rng = AntithesisRng;
61/// let random_u32: u32 = rng.gen();
62/// let random_u64: u64 = rng.gen();
63/// let random_char: char = rng.gen();
64///
65/// let mut bytes = [0u8; 16];
66/// rng.fill_bytes(&mut bytes);
67/// ```
68pub struct AntithesisRng;
69
70impl RngCore for AntithesisRng {
71    fn next_u32(&mut self) -> u32 {
72        get_random() as u32
73    }
74
75    fn next_u64(&mut self) -> u64 {
76        get_random()
77    }
78
79    fn fill_bytes(&mut self, dest: &mut [u8]) {
80        // Split the destination buffer into chunks of 8 bytes each
81        // (since we'll fill each chunk with a u64/8 bytes of random data)
82        let mut chunks = dest.chunks_exact_mut(8);
83
84        // Fill each complete 8-byte chunk with random bytes
85        for chunk in chunks.by_ref() {
86            // Generate 8 random bytes from a u64 in native endian order
87            let random_bytes = self.next_u64().to_ne_bytes();
88            // Copy those random bytes into this chunk
89            chunk.copy_from_slice(&random_bytes);
90        }
91
92        // Get any remaining bytes that didn't fit in a complete 8-byte chunk
93        let remainder = chunks.into_remainder();
94
95        if !remainder.is_empty() {
96            // Generate 8 more random bytes
97            let random_bytes = self.next_u64().to_ne_bytes();
98            // Copy just enough random bytes to fill the remainder
99            remainder.copy_from_slice(&random_bytes[..remainder.len()]);
100        }
101    }
102
103    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
104        self.fill_bytes(dest);
105        Ok(())
106    }
107}
108
109#[cfg(test)]
110mod tests {
111    use super::*;
112    use std::collections::{HashMap, HashSet};
113    use rand::Rng;
114    use rand::seq::SliceRandom;
115
116    #[test]
117    fn random_choice_no_choices() {
118        let array = [""; 0];
119        assert_eq!(0, array.len());
120        assert_eq!(None, random_choice(&array))
121    }
122
123    #[test]
124    fn random_choice_one_choice() {
125        let array = ["ABc"; 1];
126        assert_eq!(1, array.len());
127        assert_eq!(Some(&"ABc"), random_choice(&array))
128    }
129
130    #[test]
131    fn random_choice_few_choices() {
132        // For each map key, the value is the count of the number of
133        // random_choice responses received matching that key
134        let mut counted_items: HashMap<&str, i64> = HashMap::new();
135        counted_items.insert("a", 0);
136        counted_items.insert("b", 0);
137        counted_items.insert("c", 0);
138
139        let all_keys: Vec<&str> = counted_items.keys().cloned().collect();
140        assert_eq!(counted_items.len(), all_keys.len());
141        for _i in 0..15 {
142            let rc = random_choice(all_keys.as_slice());
143            if let Some(choice) = rc {
144                if let Some(x) = counted_items.get_mut(choice) {
145                    *x += 1;
146                }
147            }
148        }
149        for (key, val) in counted_items.iter() {
150            assert_ne!(*val, 0, "Did not produce the choice: {}", key);
151        }
152    }
153
154    #[test]
155    fn get_random_100k() {
156        let mut random_numbers: HashSet<u64> = HashSet::new();
157        for _i in 0..100000 {
158            let rn = get_random();
159            assert!(!random_numbers.contains(&rn));
160            random_numbers.insert(rn);
161        }
162    }
163
164    #[test]
165    fn rng_no_choices() {
166        let mut rng = AntithesisRng;
167        let array = [""; 0];
168        assert_eq!(0, array.len());
169        assert_eq!(None, array.choose(&mut rng));
170    }
171
172    #[test]
173    fn rng_one_choice() {
174        let mut rng = AntithesisRng;
175        let array = ["ABc"; 1];
176        assert_eq!(1, array.len());
177        assert_eq!(Some(&"ABc"), array.choose(&mut rng));
178    }
179
180    #[test]
181    fn rng_few_choices() {
182        let mut rng = AntithesisRng;
183        // For each map key, the value is the count of the number of
184        // random_choice responses received matching that key
185        let mut counted_items: HashMap<&str, i64> = HashMap::new();
186        counted_items.insert("a", 0);
187        counted_items.insert("b", 0);
188        counted_items.insert("c", 0);
189
190        let all_keys: Vec<&str> = counted_items.keys().cloned().collect();
191        assert_eq!(counted_items.len(), all_keys.len());
192        for _i in 0..15 {
193            let rc = all_keys.choose(&mut rng);
194            if let Some(choice) = rc {
195                if let Some(x) = counted_items.get_mut(choice) {
196                    *x += 1;
197                }
198            }
199        }
200        for (key, val) in counted_items.iter() {
201            assert_ne!(*val, 0, "Did not produce the choice: {}", key);
202        }
203    }
204
205    #[test]
206    fn rng_100k() {
207        let mut rng = AntithesisRng;
208        let mut random_numbers: HashSet<u64> = HashSet::new();
209        for _i in 0..100000 {
210            let rn: u64 = rng.gen();
211            assert!(!random_numbers.contains(&rn));
212            random_numbers.insert(rn);
213        }
214    }
215}