From 15bf1b7153cd9b1aff6db17f66218f3c6744cbb4 Mon Sep 17 00:00:00 2001 From: DaPorkchop_ Date: Wed, 26 Jun 2024 10:25:45 +0200 Subject: [PATCH 1/2] add traits for generating placeholder values for use in tests --- src/utils/mod.rs | 65 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/src/utils/mod.rs b/src/utils/mod.rs index adb6987..5c0840b 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -51,3 +51,68 @@ pub fn add_btreemap( }); m1 } + +/// A trait which indicates that it is possible to acquire a "placeholder" value +/// of a type, which can be used for test purposes. +#[cfg(test)] +pub trait Placeholder : Sized { + /// Gets a placeholder value of this type which can be used for test purposes. + fn placeholder() -> Self; + + /// Gets an array of placeholder values of this type which can be used for test purposes. + fn placeholder_array() -> [Self; N] { + core::array::from_fn(|_| Self::placeholder()) + } +} + +/// A trait which indicates that it is possible to acquire a "placeholder" value +/// of a type, which can be used for test purposes. These placeholder values are consistent +/// across program runs. +#[cfg(test)] +pub trait PlaceholderIndexed : Sized { + /// Gets a dummy valid of this type which can be used for test purposes. + /// + /// This allows acquiring multiple distinct placeholder values which are still consistent + /// between runs. + /// + /// ### Arguments + /// + /// * `index` - the index of the placeholder value to obtain. Two placeholder values generated + /// from the same index are guaranteed to be equal (even across multiple test runs, + /// so long as the value format doesn't change). + fn placeholder_indexed(index: u64) -> Self; + + /// Gets an array of placeholder values of this type which can be used for test purposes. + fn placeholder_array_indexed(base_index: u64) -> [Self; N] { + core::array::from_fn(|n| Self::placeholder_indexed(base_index.wrapping_add(n as u64))) + } +} + +#[cfg(test)] +impl Placeholder for T { + fn placeholder() -> Self { + Self::placeholder_indexed(0) + } +} + +/// Generates the given number of pseudorandom bytes based on the given seed. +/// +/// This is intended to be used in tests, where random but reproducible placeholder values are often +/// required. +/// +/// ### Arguments +/// +/// * `seed_parts` - the parts of the seed, which will be concatenated to form the RNG seed +#[cfg(test)] +pub fn placeholder_bytes(seed_parts: &[&[u8]]) -> [u8; N] { + // Use Shake-256 to generate an arbitrarily large number of random bytes based on the given seed. + let mut shake256 = sha3::Shake256::default(); + for slice in seed_parts { + sha3::digest::Update::update(&mut shake256, slice); + } + let mut reader = sha3::digest::ExtendableOutput::finalize_xof(shake256); + + let mut res = [0u8; N]; + sha3::digest::XofReader::read(&mut reader, &mut res); + res +} From 0e148be1945cb8427cf61a947d1efcf9ae14bfce Mon Sep 17 00:00:00 2001 From: DaPorkchop_ Date: Thu, 11 Jul 2024 16:41:31 +0200 Subject: [PATCH 2/2] PlaceholderIndexed -> PlaceholderSeed --- src/utils/mod.rs | 49 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 5c0840b..aa17b53 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -69,7 +69,33 @@ pub trait Placeholder : Sized { /// of a type, which can be used for test purposes. These placeholder values are consistent /// across program runs. #[cfg(test)] -pub trait PlaceholderIndexed : Sized { +pub trait PlaceholderSeed: Sized + PartialEq { + /// Gets a dummy valid of this type which can be used for test purposes. + /// + /// This allows acquiring multiple distinct placeholder values which are still consistent + /// between runs. + /// + /// ### Arguments + /// + /// * `seed_parts` - the parts of the seed for the placeholder value to obtain. Two placeholder + /// values generated from the same seed are guaranteed to be equal (even + /// across multiple test runs, so long as the value format doesn't change). + fn placeholder_seed_parts<'a>(seed_parts: impl IntoIterator) -> Self; + + /// Gets a dummy valid of this type which can be used for test purposes. + /// + /// This allows acquiring multiple distinct placeholder values which are still consistent + /// between runs. + /// + /// ### Arguments + /// + /// * `seed` - the seed for the placeholder value to obtain. Two placeholder + /// values generated from the same seed are guaranteed to be equal (even + /// across multiple test runs, so long as the value format doesn't change). + fn placeholder_seed(seed: impl AsRef<[u8]>) -> Self { + Self::placeholder_seed_parts([ seed.as_ref() ]) + } + /// Gets a dummy valid of this type which can be used for test purposes. /// /// This allows acquiring multiple distinct placeholder values which are still consistent @@ -80,18 +106,27 @@ pub trait PlaceholderIndexed : Sized { /// * `index` - the index of the placeholder value to obtain. Two placeholder values generated /// from the same index are guaranteed to be equal (even across multiple test runs, /// so long as the value format doesn't change). - fn placeholder_indexed(index: u64) -> Self; + fn placeholder_indexed(index: u64) -> Self { + Self::placeholder_seed_parts([ index.to_le_bytes().as_slice() ]) + } + + /// Gets an array of placeholder values of this type which can be used for test purposes. + fn placeholder_array_seed(seed: impl AsRef<[u8]>) -> [Self; N] { + core::array::from_fn(|n| Self::placeholder_seed_parts( + [ seed.as_ref(), &(n as u64).to_le_bytes() ] + )) + } /// Gets an array of placeholder values of this type which can be used for test purposes. fn placeholder_array_indexed(base_index: u64) -> [Self; N] { - core::array::from_fn(|n| Self::placeholder_indexed(base_index.wrapping_add(n as u64))) + Self::placeholder_array_seed(base_index.to_le_bytes()) } } #[cfg(test)] -impl Placeholder for T { +impl Placeholder for T { fn placeholder() -> Self { - Self::placeholder_indexed(0) + ::placeholder_seed_parts([]) } } @@ -104,7 +139,9 @@ impl Placeholder for T { /// /// * `seed_parts` - the parts of the seed, which will be concatenated to form the RNG seed #[cfg(test)] -pub fn placeholder_bytes(seed_parts: &[&[u8]]) -> [u8; N] { +pub fn placeholder_bytes<'a, const N: usize>( + seed_parts: impl IntoIterator +) -> [u8; N] { // Use Shake-256 to generate an arbitrarily large number of random bytes based on the given seed. let mut shake256 = sha3::Shake256::default(); for slice in seed_parts {