use once_cell::sync::Lazy;
use rand::{distributions::Alphanumeric, Rng, RngCore};
use rand_seeder::Seeder;
use std::{cell::RefCell, io::Write, path::Path};
pub type TestRng = rand_chacha::ChaCha12Rng;
static SEED: Lazy<String> = Lazy::new(|| {
if let Ok(seed) = std::env::var("TEST_RNG_SEED") {
println!("EnvVar test seed is \"{seed}\".");
seed
} else {
let mut rng = rand::thread_rng();
let seed: String = (&mut rng)
.sample_iter(Alphanumeric)
.take(7)
.map(char::from)
.collect();
println!("Generated test seed is \"{seed}\".");
seed
}
});
static RNG: Lazy<TestRng> = Lazy::new(|| {
let seed = SEED.clone();
Seeder::from(seed).make_rng()
});
thread_local! {
static RNG_ALREADY_USED: RefCell<bool> = const { RefCell::new(false) };
}
pub fn get() -> TestRng {
if RNG_ALREADY_USED.with_borrow(|b| *b) {
panic!("RNG should only be got once per test.");
}
RNG_ALREADY_USED.with_borrow_mut(|b| *b = true);
println!("The seed is {}.", SEED.as_str());
RNG.clone()
}
pub fn save_to_file(rng: &TestRng, path: impl AsRef<Path>) {
let data = bincode::serialize(rng).unwrap();
let mut file = std::fs::File::create(path).unwrap();
file.write_all(&data).unwrap();
}
pub fn load_from_file(path: impl AsRef<Path>) -> TestRng {
let data = std::fs::read(path).unwrap();
bincode::deserialize(&data).unwrap()
}
pub struct AssertNoRandomness;
impl RngCore for AssertNoRandomness {
fn next_u32(&mut self) -> u32 {
panic!("AssertNoRandomness: Randomness is used")
}
fn next_u64(&mut self) -> u64 {
panic!("AssertNoRandomness: Randomness is used")
}
fn fill_bytes(&mut self, _dest: &mut [u8]) {
panic!("AssertNoRandomness: Randomness is used")
}
fn try_fill_bytes(&mut self, _dest: &mut [u8]) -> Result<(), rand::Error> {
Err(rand::Error::new("AssertNoRandomness cannot fill bytes."))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[should_panic]
fn cannot_be_called_twice() {
get();
get();
}
}