No public description PiperOrigin-RevId: 1007111373
diff --git a/rust/src/domains/arbitrary.rs b/rust/src/domains/arbitrary.rs index df1023b..dbcdae4 100644 --- a/rust/src/domains/arbitrary.rs +++ b/rust/src/domains/arbitrary.rs
@@ -19,6 +19,7 @@ use super::utility::shrink_towards; use super::utility::NUM_VALID_CODEPOINTS; use super::Domain; +use super::ReversibleDomain; use std::fmt; use std::marker::PhantomData; @@ -109,6 +110,18 @@ } } +impl ReversibleDomain<bool> for Arbitrary<bool> { + fn to_unvalidated_corpus_value(&self, value: bool) -> anyhow::Result<Self::CorpusValue> { + Ok(value) + } +} + +impl<'a> ReversibleDomain<&'a bool> for Arbitrary<bool> { + fn to_unvalidated_corpus_value(&self, value: &'a bool) -> anyhow::Result<Self::CorpusValue> { + Ok(*value) + } +} + macro_rules! impl_domain_for_integer { // We need an additional "integer type" for this, because // rand::distr::Distribution<> is not implemented for StandardUniform for isize and usize. @@ -158,6 +171,21 @@ Ok(()) } } + + impl ReversibleDomain<$ty> for Arbitrary<$ty> { + fn to_unvalidated_corpus_value(&self, value: $ty) -> anyhow::Result<Self::CorpusValue> { + Ok(value) + } + } + + impl<'a> ReversibleDomain<&'a $ty> for Arbitrary<$ty> { + fn to_unvalidated_corpus_value( + &self, + value: &'a $ty, + ) -> anyhow::Result<Self::CorpusValue> { + Ok(*value) + } + } }; } @@ -236,6 +264,21 @@ Ok(()) } } + + impl ReversibleDomain<$ty> for Arbitrary<$ty> { + fn to_unvalidated_corpus_value(&self, value: $ty) -> anyhow::Result<Self::CorpusValue> { + Ok(value) + } + } + + impl<'a> ReversibleDomain<&'a $ty> for Arbitrary<$ty> { + fn to_unvalidated_corpus_value( + &self, + value: &'a $ty, + ) -> anyhow::Result<Self::CorpusValue> { + Ok(*value) + } + } }; } @@ -289,6 +332,18 @@ } } +impl ReversibleDomain<char> for Arbitrary<char> { + fn to_unvalidated_corpus_value(&self, value: char) -> anyhow::Result<Self::CorpusValue> { + Ok(value) + } +} + +impl<'a> ReversibleDomain<&'a char> for Arbitrary<char> { + fn to_unvalidated_corpus_value(&self, value: &'a char) -> anyhow::Result<Self::CorpusValue> { + Ok(*value) + } +} + impl Domain for Arbitrary<()> { type UserValue<'user> = (); type CorpusValue = (); @@ -319,18 +374,35 @@ } } +impl ReversibleDomain<()> for Arbitrary<()> { + fn to_unvalidated_corpus_value(&self, value: ()) -> anyhow::Result<Self::CorpusValue> { + Ok(value) + } +} + +impl<'a> ReversibleDomain<&'a ()> for Arbitrary<()> { + fn to_unvalidated_corpus_value(&self, value: &'a ()) -> anyhow::Result<Self::CorpusValue> { + Ok(*value) + } +} + #[cfg(test)] mod tests { use std::collections::HashSet; + use std::fmt::Debug; + use std::hash::Hash; - use crate::domains::utility::SpecialValues; + use googletest::prelude::*; use num_traits::{Float, One, Zero}; use rand::distr::uniform::SampleUniform; use rand::distr::{Distribution, StandardUniform}; use rand::rngs::{SmallRng, SysRng}; use rand::SeedableRng; + use crate::domains::utility::SpecialValues; use crate::domains::Domain; + use crate::domains::ReversibleDomain; + use crate::domains::SeedableDomainExt; use super::*; @@ -755,4 +827,102 @@ ); } } + + fn test_arbitrary_seeding<T>(eager_seeds: [T; 2], provider_seeds: [T; 2]) + where + T: Copy + Eq + Hash + Debug + Send + 'static, + for<'u> Arbitrary<T>: ReversibleDomain<T, UserValue<'u> = T, CorpusValue = T>, + { + let mut domain = Arbitrary::<T>::default() + .with_seeds(eager_seeds) + .with_seed_provider(move || provider_seeds); + let mut rng = get_rng(); + let mut observed = HashSet::new(); + for _ in 0..200 { + observed.insert(domain.init(&mut rng).unwrap()); + } + for seed in eager_seeds { + expect_that!(observed, contains(eq(&seed))); + } + for seed in provider_seeds { + expect_that!(observed, contains(eq(&seed))); + } + } + + #[gtest] + fn test_arbitrary_seeding_integers_and_char() { + test_arbitrary_seeding::<i8>([-42, 42], [i8::MIN, i8::MAX]); + test_arbitrary_seeding::<u8>([7, 42], [u8::MIN, u8::MAX]); + test_arbitrary_seeding::<i16>([-1234, 1234], [i16::MIN, i16::MAX]); + test_arbitrary_seeding::<u16>([1234, 5678], [u16::MIN, u16::MAX]); + test_arbitrary_seeding::<i32>([-123456, 123456], [i32::MIN, i32::MAX]); + test_arbitrary_seeding::<u32>([123456, 654321], [u32::MIN, u32::MAX]); + test_arbitrary_seeding::<i64>([-123456789, 123456789], [i64::MIN, i64::MAX]); + test_arbitrary_seeding::<u64>([123456789, 987654321], [u64::MIN, u64::MAX]); + test_arbitrary_seeding::<i128>([-123456789, 123456789], [i128::MIN, i128::MAX]); + test_arbitrary_seeding::<u128>([123456789, 987654321], [u128::MIN, u128::MAX]); + test_arbitrary_seeding::<isize>([-12345, 12345], [isize::MIN, isize::MAX]); + test_arbitrary_seeding::<usize>([12345, 54321], [usize::MIN, usize::MAX]); + test_arbitrary_seeding::<char>(['a', 'Z'], ['\0', '\u{10FFFF}']); + } + + #[gtest] + fn test_arbitrary_seeding_bool() { + let mut domain = + Arbitrary::<bool>::default().with_seeds([true]).with_seed_provider(|| [false]); + let mut rng = get_rng(); + let mut observed = HashSet::new(); + for _ in 0..200 { + observed.insert(domain.init(&mut rng).unwrap()); + } + expect_true!(observed.contains(&true)); + expect_true!(observed.contains(&false)); + } + + #[gtest] + fn test_arbitrary_seeding_floats() { + { + let seeds = [42.5f32, f32::INFINITY, f32::NEG_INFINITY]; + let provider_seeds = [f32::NAN, -0.0f32]; + let mut domain = Arbitrary::<f32>::default() + .with_seeds(seeds) + .with_seed_provider(move || provider_seeds); + let mut rng = get_rng(); + let mut observed_bits = HashSet::new(); + for _ in 0..300 { + observed_bits.insert(domain.init(&mut rng).unwrap().to_bits()); + } + for expected in seeds.into_iter().chain(provider_seeds) { + expect_that!(observed_bits, contains(eq(&expected.to_bits()))); + } + } + + { + let seeds = [42.5f64, f64::INFINITY, f64::NEG_INFINITY]; + let provider_seeds = [f64::NAN, -0.0f64]; + let mut domain = Arbitrary::<f64>::default() + .with_seeds(seeds) + .with_seed_provider(move || provider_seeds); + let mut rng = get_rng(); + let mut observed_bits = HashSet::new(); + for _ in 0..300 { + observed_bits.insert(domain.init(&mut rng).unwrap().to_bits()); + } + for expected in seeds.into_iter().chain(provider_seeds) { + expect_that!(observed_bits, contains(eq(&expected.to_bits()))); + } + } + } + + #[gtest] + fn test_arbitrary_seeding_unit() { + let mut domain = Arbitrary::<()>::default() + .with_seeds([()]) + .with_seeds(&[()]) + .with_seed_provider(|| [()]); + let mut rng = get_rng(); + for _ in 0..10 { + expect_that!(domain.init(&mut rng).unwrap(), eq(())); + } + } }
diff --git a/rust/src/domains/containers.rs b/rust/src/domains/containers.rs index 4c816a1..6aab943 100644 --- a/rust/src/domains/containers.rs +++ b/rust/src/domains/containers.rs
@@ -1,4 +1,5 @@ use super::Domain; +use super::ReversibleDomain; use rand::RngExt; use std::fmt; @@ -253,13 +254,26 @@ } } +impl<D, I> ReversibleDomain<I> for VecOf<D> +where + I: IntoIterator, + D: ReversibleDomain<I::Item>, +{ + fn to_unvalidated_corpus_value(&self, value: I) -> anyhow::Result<Self::CorpusValue> { + value.into_iter().map(|item| self.inner.to_unvalidated_corpus_value(item)).collect() + } +} + #[cfg(test)] mod tests { use super::*; use crate::domains::arbitrary::Arbitrary; + use crate::domains::range::InRange; + use crate::domains::SeedableDomainExt; use googletest::prelude::*; use rand::rngs::{SmallRng, SysRng}; use rand::SeedableRng; + use std::collections::HashSet; fn get_rng() -> SmallRng { SmallRng::try_from_rng(&mut SysRng).expect("Failed to create RNG") @@ -455,4 +469,92 @@ let user_val = domain.corpus_to_user_value(&corpus_val).unwrap(); expect_that!(user_val, container_eq(vec![1u32, 2u32, 3u32])); } + + #[gtest] + fn test_vec_of_seeding_samples_seeds() { + let mut domain = VecOf::new(Arbitrary::<i32>::default()) + .with_seeds([vec![10, 20], vec![30]]) + .with_seed_provider(|| vec![vec![40, 50, 60]]); + let mut rng = get_rng(); + let mut observed = HashSet::new(); + for _ in 0..200 { + observed.insert(domain.init(&mut rng).unwrap()); + } + expect_that!(observed, contains(eq(&vec![10, 20]))); + expect_that!(observed, contains(eq(&vec![30]))); + expect_that!(observed, contains(eq(&vec![40, 50, 60]))); + } + + #[gtest] + #[should_panic(expected = "Failed to add seeds")] + fn test_vec_of_seed_shorter_than_min_len_panics() { + let _ = VecOf::new(Arbitrary::<i32>::default()).with_min_len(3).with_seeds([vec![1, 2]]); + } + + #[gtest] + #[should_panic(expected = "Failed to add seeds")] + fn test_vec_of_seed_longer_than_max_len_panics() { + let _ = VecOf::new(Arbitrary::<i32>::default()).with_max_len(2).with_seeds([vec![1, 2, 3]]); + } + + #[gtest] + #[should_panic(expected = "Failed to add seeds")] + fn test_vec_of_seed_shorter_than_min_len_with_soft_max_len_panics() { + let _ = VecOf::new(Arbitrary::<i32>::default()) + .with_min_len(2) + .with_soft_max_len(4) + .with_seeds([vec![1]]); + } + + #[gtest] + fn test_vec_of_seed_longer_than_soft_max_len_succeeds() { + let mut domain = VecOf::new(Arbitrary::<i32>::default()) + .with_soft_max_len(2) + .with_seeds([vec![1, 2, 3, 4]]); + let mut rng = get_rng(); + let mut observed = HashSet::new(); + for _ in 0..100 { + observed.insert(domain.init(&mut rng).unwrap()); + } + expect_that!(observed, contains(eq(&vec![1, 2, 3, 4]))); + } + + #[gtest] + #[should_panic(expected = "Failed to add seeds")] + fn test_vec_of_seed_with_invalid_inner_element_panics() { + let _ = VecOf::new(InRange::new(0i32, 10)).with_seeds([vec![5, 20]]); + } + + #[gtest] + #[should_panic(expected = "Failed to retrieve seeds from seed provider")] + fn test_vec_of_seed_provider_with_invalid_inner_element_panics_on_init() { + let mut domain = + VecOf::new(InRange::new(0i32, 10)).with_seed_provider(|| vec![vec![5, 20]]); + let _ = domain.init(&mut get_rng()); + } + + #[gtest] + fn test_vec_of_nested_seeding() { + let mut domain = VecOf::new(VecOf::new(InRange::new(1i32, 5)).with_max_len(2)) + .with_seeds([vec![vec![1, 5], vec![3]]]); + let mut rng = get_rng(); + let mut observed = HashSet::new(); + for _ in 0..100 { + observed.insert(domain.init(&mut rng).unwrap()); + } + expect_that!(observed, contains(eq(&vec![vec![1, 5], vec![3]]))); + } + + #[gtest] + fn test_vec_of_with_seeded_inner_domain() { + let mut domain = + VecOf::new(InRange::new(1i32, 5).with_seeds([3])).with_len(1).with_seeds([vec![5]]); + let mut rng = get_rng(); + let mut observed = HashSet::new(); + for _ in 0..200 { + observed.insert(domain.init(&mut rng).unwrap()); + } + expect_that!(observed, contains(eq(&vec![5]))); + expect_that!(observed, contains(eq(&vec![3]))); + } }
diff --git a/rust/src/domains/range.rs b/rust/src/domains/range.rs index 0adb0af..3fd57a0 100644 --- a/rust/src/domains/range.rs +++ b/rust/src/domains/range.rs
@@ -19,6 +19,7 @@ use super::utility::shrink_towards; use super::utility::SpecialValues; use super::Domain; +use super::ReversibleDomain; use std::fmt::Display; use anyhow; @@ -206,6 +207,21 @@ Ok(()) } } + + impl ReversibleDomain<$ty> for InRange<$ty> { + fn to_unvalidated_corpus_value(&self, value: $ty) -> anyhow::Result<Self::CorpusValue> { + Ok(value) + } + } + + impl<'a> ReversibleDomain<&'a $ty> for InRange<$ty> { + fn to_unvalidated_corpus_value( + &self, + value: &'a $ty, + ) -> anyhow::Result<Self::CorpusValue> { + Ok(*value) + } + } }; } @@ -279,6 +295,21 @@ Ok(()) } } + + impl ReversibleDomain<$ty> for InRange<$ty> { + fn to_unvalidated_corpus_value(&self, value: $ty) -> anyhow::Result<Self::CorpusValue> { + Ok(value) + } + } + + impl<'a> ReversibleDomain<&'a $ty> for InRange<$ty> { + fn to_unvalidated_corpus_value( + &self, + value: &'a $ty, + ) -> anyhow::Result<Self::CorpusValue> { + Ok(*value) + } + } }; } @@ -355,11 +386,26 @@ } } +impl ReversibleDomain<char> for InRange<char> { + fn to_unvalidated_corpus_value(&self, value: char) -> anyhow::Result<Self::CorpusValue> { + Ok(value) + } +} + +impl<'a> ReversibleDomain<&'a char> for InRange<char> { + fn to_unvalidated_corpus_value(&self, value: &'a char) -> anyhow::Result<Self::CorpusValue> { + Ok(*value) + } +} + #[cfg(test)] mod tests { use std::any::type_name; + use std::collections::HashSet; use std::fmt::Debug; + use crate::domains::SeedableDomainExt; + use googletest::prelude::*; use rand::rngs::{SmallRng, SysRng}; use rand::SeedableRng; @@ -616,4 +662,107 @@ char_range.mutate(&mut char_val, &mut rng, true).unwrap(); assert_eq!(char_val, 'a'); } + + fn test_in_range_seeding_property<T>(lower: T, mid: T, upper: T) + where + T: DomainRangeBounds + PartialOrd + Debug + Copy + PartialEq + Display + Send + 'static, + for<'u> InRange<T>: ReversibleDomain<T, UserValue<'u> = T, CorpusValue = T>, + { + let mut domain = InRange::new(lower, upper) + .with_seeds([lower, upper]) + .with_seed_provider(move || vec![mid]); + let mut rng = get_rng(); + let mut observed = HashSet::new(); + for _ in 0..200 { + let val = domain.init(&mut rng).unwrap(); + expect_that!(val, ge(lower)); + expect_that!(val, le(upper)); + if val == lower { + observed.insert("lower"); + } + if val == mid { + observed.insert("mid"); + } + if val == upper { + observed.insert("upper"); + } + } + expect_that!(observed, contains(eq(&"lower"))); + expect_that!(observed, contains(eq(&"mid"))); + expect_that!(observed, contains(eq(&"upper"))); + } + + #[gtest] + fn test_in_range_seeding_all_types() { + test_in_range_seeding_property::<i8>(-10, 0, 10); + test_in_range_seeding_property::<u8>(10, 25, 50); + test_in_range_seeding_property::<i16>(-100, 0, 100); + test_in_range_seeding_property::<u16>(100, 250, 500); + test_in_range_seeding_property::<i32>(-1000, 42, 1000); + test_in_range_seeding_property::<u32>(1000, 2500, 5000); + test_in_range_seeding_property::<i64>(-10000, 42, 10000); + test_in_range_seeding_property::<u64>(10000, 25000, 50000); + test_in_range_seeding_property::<i128>(-100000, 42, 100000); + test_in_range_seeding_property::<u128>(100000, 250000, 500000); + test_in_range_seeding_property::<isize>(-1000, 42, 1000); + test_in_range_seeding_property::<usize>(1000, 2500, 5000); + test_in_range_seeding_property::<f32>(-10.0, 2.5, 10.0); + test_in_range_seeding_property::<f64>(-100.0, 42.5, 100.0); + test_in_range_seeding_property::<char>('a', 'm', 'z'); + } + + #[gtest] + #[should_panic(expected = "Failed to add seeds")] + fn test_in_range_integer_seed_below_lower_panics() { + let _ = InRange::<i32>::new(10, 20).with_seeds([9]); + } + + #[gtest] + #[should_panic(expected = "Failed to add seeds")] + fn test_in_range_integer_seed_above_upper_panics() { + let _ = InRange::<i32>::new(10, 20).with_seeds([21]); + } + + #[gtest] + #[should_panic(expected = "Failed to add seeds")] + fn test_in_range_float_seed_below_lower_panics() { + let _ = InRange::<f64>::new(0.0, 1.0).with_seeds([-0.1]); + } + + #[gtest] + #[should_panic(expected = "Failed to add seeds")] + fn test_in_range_float_seed_above_upper_panics() { + let _ = InRange::<f64>::new(0.0, 1.0).with_seeds([1.1]); + } + + #[gtest] + #[should_panic(expected = "Failed to add seeds")] + fn test_in_range_float_seed_nan_panics() { + let _ = InRange::<f64>::new(0.0, 1.0).with_seeds([f64::NAN]); + } + + #[gtest] + #[should_panic(expected = "Failed to add seeds")] + fn test_in_range_float_seed_infinity_panics() { + let _ = InRange::<f32>::new(0.0, 1.0).with_seeds([f32::INFINITY]); + } + + #[gtest] + #[should_panic(expected = "Failed to add seeds")] + fn test_in_range_char_seed_below_lower_panics() { + let _ = InRange::<char>::new('b', 'y').with_seeds(['a']); + } + + #[gtest] + #[should_panic(expected = "Failed to add seeds")] + fn test_in_range_char_seed_above_upper_panics() { + let _ = InRange::<char>::new('b', 'y').with_seeds(['z']); + } + + #[gtest] + #[should_panic(expected = "Failed to retrieve seeds from seed provider")] + fn test_in_range_seed_provider_out_of_bounds_panics_on_init() { + let mut domain = InRange::<i32>::new(10, 20).with_seed_provider(|| vec![25]); + let _ = domain.init(&mut get_rng()); + } }