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