Skip to content

Commit

Permalink
test(vrd): 🎨 add SeedableRng unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
sebastienrousseau committed Aug 27, 2024
1 parent ee40f30 commit 374938f
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 2 deletions.
43 changes: 42 additions & 1 deletion src/random.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// See LICENSE-APACHE.md and LICENSE-MIT.md in the repository root for full license information.

use crate::MersenneTwisterConfig;
use rand::RngCore;
use rand::{RngCore, SeedableRng};
use serde::{Deserialize, Serialize};
use serde_big_array::BigArray;

Expand Down Expand Up @@ -868,3 +868,44 @@ impl RngCore for Random {
Ok(())
}
}

impl SeedableRng for Random {
type Seed = [u8; 16]; // Adjust as necessary

fn from_seed(seed: Self::Seed) -> Self {
let mut mt = [0u32; 624];

// Initialize the state with a non-zero value
mt[0] = u32::from_le_bytes(seed[0..4].try_into().unwrap());

for i in 1..624 {
mt[i] = 0x6C078965u32
.wrapping_mul(mt[i - 1] ^ (mt[i - 1] >> 30))
.wrapping_add(i as u32);
}

// Further mix in the seed into the state array
let mut i = 1;
let mut j = 0;
for _ in 0..624.max(16) {
mt[i] = (mt[i]
^ ((mt[i - 1] ^ (mt[i - 1] >> 30))
.wrapping_mul(0x6C078965u32)))
.wrapping_add(u32::from_le_bytes(
seed[j..j + 4].try_into().unwrap(),
))
.wrapping_add(j as u32); // Add seed and its index
i += 1;
j += 4;
if i >= 624 {
mt[0] = mt[623];
i = 1;
}
if j >= 16 {
j = 0;
}
}

Random { mt, mti: 624 }
}
}
96 changes: 95 additions & 1 deletion tests/test_random.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

#[cfg(test)]
mod tests {
use rand::RngCore;
use rand::{RngCore, SeedableRng};
use vrd::random::Random;

// Initialization tests
Expand Down Expand Up @@ -618,4 +618,98 @@ mod tests {
assert_eq!(rng1.rand(), rng2.rand());
}
}

#[test]
fn test_from_seed_generates_deterministic_rng() {
let seed = [0xABu8; 16];
let mut rng1 = Random::from_seed(seed);
let mut rng2 = Random::from_seed(seed);

assert_eq!(rng1.mt, rng2.mt);
assert_eq!(rng1.mti, rng2.mti);

// Test that both RNGs produce the same sequence of numbers
for _ in 0..10 {
assert_eq!(rng1.next_u32(), rng2.next_u32());
}
}

#[test]
fn test_from_different_seeds_generates_different_rngs() {
let seed1 = [0xABu8; 16];
let seed2 = [0xCDu8; 16];
let mut rng1 = Random::from_seed(seed1);
let mut rng2 = Random::from_seed(seed2);

// Print the initial state for debugging
println!("rng1.mt: {:?}", &rng1.mt[0..10]); // print the first 10 values for brevity
println!("rng2.mt: {:?}", &rng2.mt[0..10]);

// Test that both RNGs produce different sequences of numbers
let mut different = false;
for _ in 0..10 {
let val1 = rng1.next_u32();
let val2 = rng2.next_u32();
println!(
"rng1.next_u32(): {}, rng2.next_u32(): {}",
val1, val2
); // Debug output
if val1 != val2 {
different = true;
break;
}
}
assert!(different, "RNGs with different seeds should produce different sequences");
}

#[test]
fn test_seed_size_incorrect() {
// This test will not compile because the SeedableRng trait ensures the correct seed size.
// It is included here to demonstrate the intention behind testing.
// let incorrect_seed = [0xABu8; 15]; // Should be of size 16
// let rng = Random::from_seed(incorrect_seed);
// assert!(false, "This should not compile");
}

#[test]
fn test_reseeding_changes_rng_state() {
let seed1 = [0xABu8; 16];
let seed2 = [0xCDu8; 16];
let mut rng = Random::from_seed(seed1);

let initial_state = rng.mt;

// Reseed with a different seed
rng = Random::from_seed(seed2);
let reseeded_state = rng.mt;

assert_ne!(
initial_state, reseeded_state,
"Reseeding should change the RNG state"
);
}

#[test]
fn test_from_seed_with_extreme_values() {
let min_seed = [0x00u8; 16];
let max_seed = [0xFFu8; 16];

let mut rng_min = Random::from_seed(min_seed);
let mut rng_max = Random::from_seed(max_seed);

// Test that RNGs produce sequences of numbers (no need to compare, just ensure no panics)
for _ in 0..10 {
let val_min = rng_min.next_u32();
let val_max = rng_max.next_u32();

assert!(
val_min != 0xFFFFFFFF,
"RNG with all-zero seed should not produce all ones"
);
assert!(
val_max != 0,
"RNG with all-ones seed should not produce all zeroes"
);
}
}
}

0 comments on commit 374938f

Please sign in to comment.