blob: a15e28ca26ab73704a1bcd181220bf6d0d0ed73f [file] [edit]
use rand::RngExt;
use std::fmt;
use super::Domain;
const DEFAULT_MAX_LEN: usize = 5000;
macro_rules! choose_one {
($rng:expr, $( $cond:expr $(; w = $weight:expr)? => $action:expr ),* $(,)? ) => {{
struct Branch {
cond: bool,
weight: usize,
}
let branches = [
$( Branch { cond: $cond, weight: choose_one!(@default 1usize, $($weight)?) } ),*
];
let total_weight: usize = branches.iter()
.map(|&Branch { cond, weight }| if cond { weight } else { 0 })
.sum();
if total_weight > 0 {
let choice = ::rand::RngExt::random_range($rng, 0..total_weight);
let mut current = 0usize;
let mut i = 0usize;
'choose_one_loop: {
$(
if branches[i].cond {
let w = branches[i].weight;
if choice >= current && choice < current + w {
let _ = { $action };
break 'choose_one_loop;
}
#[allow(unused_assignments)]
{ current += w; }
}
i += 1;
)*
}
}
}};
(@default $default:expr, ) => { $default };
(@default $default:expr, $val:expr) => { $val };
}
/// A trait for configuring the length constraints of container domains.
///
/// This trait provides a fluent interface to specify the length requirements
/// for domains that generate collections, such as [`VecOf`].
pub trait ContainerDomain: Sized {
/// Sets the length of the container to be exactly `len`.
fn with_len(self, len: usize) -> Self;
/// Sets the minimum length of the container.
///
/// # Panics
///
/// Panics if the specified `min_len` is greater than the current maximum length (if set).
fn with_min_len(self, min_len: usize) -> Self;
/// Sets the maximum length of the container.
///
/// Using this API will override any previous `with_soft_max_len` call.
///
/// # Panics
///
/// Panics if the specified `max_len` is less than the current minimum length.
fn with_max_len(self, max_len: usize) -> Self;
/// Sets a "soft" maximum length of the container.
///
/// Using this API will override any previous `with_max_len` call.
///
/// With this constraint, the domain will consider containers longer than
/// `soft_max_len` as valid. When mutating the containers, the domain will
/// not increase their size further if it is already greater or equal to
/// `soft_max_len`.
///
/// # Panics
///
/// Panics if the specified `soft_max_len` is less than the current minimum length.
fn with_soft_max_len(self, soft_max_len: usize) -> Self;
}
pub struct VecOf<T> {
inner: T,
min_len: usize,
max_len: Option<usize>,
max_len_is_soft: bool,
}
impl<T: Clone> Clone for VecOf<T> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
min_len: self.min_len,
max_len: self.max_len,
max_len_is_soft: self.max_len_is_soft,
}
}
}
impl<T: fmt::Debug> fmt::Debug for VecOf<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("VecOf")
.field("inner", &self.inner)
.field("min_len", &self.min_len)
.field("max_len", &self.max_len)
.field("max_len_is_soft", &self.max_len_is_soft)
.finish()
}
}
impl<T> VecOf<T> {
pub fn new(inner: T) -> Self {
Self { inner, min_len: 0, max_len: None, max_len_is_soft: false }
}
fn max_len(&self) -> usize {
self.max_len.unwrap_or(self.min_len.max(DEFAULT_MAX_LEN))
}
}
impl<T> Domain for VecOf<T>
where
T: Domain,
{
type CorpusValue = Vec<T::CorpusValue>;
type UserValue<'user> = Vec<T::UserValue<'user>>;
fn init(&self, rng: &mut dyn rand::Rng) -> anyhow::Result<Self::CorpusValue> {
if self.max_len() == 0 {
return Ok(Vec::new());
}
let initial_size = if self.min_len == 0 { rng.random_range(0..=1) } else { self.min_len };
let mut val = Vec::with_capacity(initial_size);
for _ in 0..initial_size {
val.push(self.inner.init(rng)?);
}
Ok(val)
}
fn mutate(
&self,
val: &mut Self::CorpusValue,
rng: &mut dyn rand::Rng,
only_shrink: bool,
) -> anyhow::Result<()> {
if self.max_len_is_soft {
anyhow::ensure!(
self.min_len <= val.len(),
"Length {} is less than the minimum length {}",
val.len(),
self.min_len
);
} else {
anyhow::ensure!(
self.min_len <= val.len() && val.len() <= self.max_len(),
"Length {} is not between the minimum length {} and maximum length {}",
val.len(),
self.min_len,
self.max_len()
);
}
choose_one!(rng,
val.len() > self.min_len => {
let idx = rng.random_range(0..val.len());
val.remove(idx);
},
!only_shrink && val.len() < self.max_len() => {
let idx = rng.random_range(0..=val.len());
val.insert(idx, self.inner.init(rng)?);
},
!val.is_empty() => {
let idx = rng.random_range(0..val.len());
self.inner.mutate(&mut val[idx], rng, only_shrink)?;
},
);
Ok(())
}
fn get_user_value<'a>(
&self,
corpus_value: &'a Self::CorpusValue,
) -> anyhow::Result<Self::UserValue<'a>> {
let mut user_values = Vec::with_capacity(corpus_value.len());
for item in corpus_value {
user_values.push(self.inner.get_user_value(item)?);
}
Ok(user_values)
}
}
impl<T> ContainerDomain for VecOf<T> {
fn with_len(self, len: usize) -> Self {
Self { min_len: len, max_len: Some(len), ..self }
}
fn with_min_len(self, min_len: usize) -> Self {
assert!(
self.max_len.is_none_or(|max| min_len <= max),
"Minimum length {} cannot be greater than the maximum length {}",
min_len,
self.max_len.unwrap()
);
Self { min_len, ..self }
}
fn with_max_len(self, max_len: usize) -> Self {
assert!(
max_len >= self.min_len,
"Maximum length {} cannot be less than the minimum length {}",
max_len,
self.min_len
);
Self { max_len_is_soft: false, max_len: Some(max_len), ..self }
}
fn with_soft_max_len(self, soft_max_len: usize) -> Self {
assert!(
soft_max_len >= self.min_len,
"Soft maximum length {} cannot be less than the minimum length {}",
soft_max_len,
self.min_len
);
Self { max_len_is_soft: true, max_len: Some(soft_max_len), ..self }
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::domains::arbitrary::Arbitrary;
use googletest::prelude::*;
use rand::rngs::{SmallRng, SysRng};
use rand::SeedableRng;
fn get_rng() -> SmallRng {
SmallRng::try_from_rng(&mut SysRng).expect("Failed to create RNG")
}
#[gtest]
fn test_vec_of_mutate_shrink() {
let domain = VecOf::new(Arbitrary::<u32>::default()).with_max_len(10);
let mut rng = get_rng();
let mut val = vec![1, 2, 3, 4, 5];
// Test shrinking length
for _ in 0..100 {
let orig_len = val.len();
if orig_len == 0 {
break;
}
domain.mutate(&mut val, &mut rng, true).unwrap();
expect_that!(val.len(), le(orig_len));
}
}
#[gtest]
fn test_vec_of_mutate_grow_and_change() {
let domain = VecOf::new(Arbitrary::<u32>::default()).with_max_len(10);
let mut rng = get_rng();
let mut val = vec![1];
let mut grew = false;
for _ in 0..100 {
let orig_len = val.len();
domain.mutate(&mut val, &mut rng, false).unwrap();
expect_that!(val.len(), le(10));
if val.len() > orig_len {
grew = true;
}
}
expect_that!(grew, eq(true), "VecOf should be able to grow during normal mutation");
}
#[gtest]
fn test_vec_of_init_respects_min_len() {
let domain = VecOf::new(Arbitrary::<u32>::default()).with_min_len(5);
let mut rng = get_rng();
for _ in 0..100 {
let val = domain.init(&mut rng).unwrap();
expect_that!(val.len(), ge(5));
}
}
#[gtest]
fn test_vec_of_init_fixed_len() {
let domain = VecOf::new(Arbitrary::<u32>::default()).with_len(7);
let mut rng = get_rng();
for _ in 0..100 {
let val = domain.init(&mut rng).unwrap();
expect_that!(val.len(), eq(7));
}
}
#[gtest]
fn test_vec_of_init_default_max_len() {
let domain = VecOf::new(Arbitrary::<u32>::default());
let mut rng = get_rng();
for _ in 0..100 {
let val = domain.init(&mut rng).unwrap();
expect_that!(val.len(), le(DEFAULT_MAX_LEN));
}
}
#[gtest]
fn test_vec_of_mutate_respects_min_len() {
let domain = VecOf::new(Arbitrary::<u32>::default()).with_min_len(3);
let mut rng = get_rng();
let mut val = vec![1, 2, 3];
for _ in 0..100 {
domain.mutate(&mut val, &mut rng, true).unwrap();
expect_that!(val.len(), ge(3));
}
}
#[gtest]
fn test_vec_of_mutate_respects_max_len() {
let domain = VecOf::new(Arbitrary::<u32>::default()).with_max_len(3);
let mut rng = get_rng();
let mut val = vec![1, 2, 3];
for _ in 0..100 {
domain.mutate(&mut val, &mut rng, false).unwrap();
expect_that!(val.len(), le(3));
}
}
#[gtest]
fn test_vec_of_mutate_min_len_validation() {
let domain = VecOf::new(Arbitrary::<u32>::default()).with_min_len(5);
let mut rng = get_rng();
let mut val = vec![1, 2, 3]; // Length 3, which is < 5
let result = domain.mutate(&mut val, &mut rng, false);
expect_that!(result.is_err(), eq(true));
let err_msg = format!("{}", result.unwrap_err());
expect_that!(
err_msg,
contains_substring(
"Length 3 is not between the minimum length 5 and maximum length 5000"
)
);
}
#[gtest]
fn test_vec_of_mutate_soft_max_len_behavior() {
let domain = VecOf::new(Arbitrary::<u32>::default()).with_soft_max_len(5);
let mut rng = get_rng();
// Valid mutation within bounds
let mut valid_val = vec![1, 2, 3, 4, 5];
let valid_result = domain.mutate(&mut valid_val, &mut rng, false);
expect_that!(valid_result.is_ok(), eq(true));
// In soft max mode, we DON'T error if we are already over the limit on entry
let mut over_val = vec![1, 2, 3, 4, 5, 6];
let result = domain.mutate(&mut over_val, &mut rng, false);
expect_that!(result.is_ok(), eq(true));
// But it should not have grown further
expect_that!(over_val.len(), le(6));
}
#[gtest]
#[should_panic(expected = "Minimum length 6 cannot be greater than the maximum length 5")]
fn test_vec_of_with_invalid_min_len() {
VecOf::new(Arbitrary::<u32>::default()).with_max_len(5).with_min_len(6);
}
#[gtest]
#[should_panic(expected = "Maximum length 4 cannot be less than the minimum length 5")]
fn test_vec_of_with_invalid_max_len() {
VecOf::new(Arbitrary::<u32>::default()).with_min_len(5).with_max_len(4);
}
#[gtest]
#[should_panic(expected = "Soft maximum length 4 cannot be less than the minimum length 5")]
fn test_vec_of_with_invalid_soft_max_len() {
VecOf::new(Arbitrary::<u32>::default()).with_min_len(5).with_soft_max_len(4);
}
#[gtest]
fn test_vec_of_mutate_no_action_at_bounds() {
let domain = VecOf::new(Arbitrary::<u32>::default()).with_len(1);
let mut rng = get_rng();
let mut val = vec![100u32];
for _ in 0..100 {
// with_len(1) means min=1, max=1.
// insert/remove should be disabled.
// mutate inner should still be possible.
let orig_val = val[0];
domain.mutate(&mut val, &mut rng, false).unwrap();
expect_that!(val.len(), eq(1));
// In this case, since action_count will be 1 (can_change), it MUST call inner.mutate
// Arbitrary::<u32> mutate should eventually change the value.
if val[0] != orig_val {
return;
}
}
}
#[gtest]
fn test_vec_of_zero_len() {
let domain = VecOf::new(Arbitrary::<u32>::default()).with_len(0);
let mut rng = get_rng();
let val = domain.init(&mut rng).unwrap();
expect_that!(val, is_empty());
let mut val = Vec::new();
domain.mutate(&mut val, &mut rng, false).unwrap();
expect_that!(val, is_empty());
}
#[gtest]
fn test_vec_of_get_user_value() {
let domain = VecOf::new(Arbitrary::<u32>::default());
let corpus_val = vec![1u32, 2u32, 3u32];
let user_val = domain.get_user_value(&corpus_val).unwrap();
expect_that!(user_val, container_eq(vec![1u32, 2u32, 3u32]));
}
}