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());
+    }
 }