use crate::config::GeneratorConfig;
use crate::generator::Generator;
pub trait Strategy {
type Value: 'static;
fn generate<R: rand::Rng>(&self, rng: &mut R, config: &GeneratorConfig) -> Self::Value;
fn shrink(&self, value: &Self::Value) -> Box<dyn Iterator<Item = Self::Value>>;
fn map<F, U>(self, f: F) -> Map<Self, F>
where
Self: Sized,
F: Fn(Self::Value) -> U,
U: 'static,
{
Map {
strategy: self,
mapper: f,
}
}
fn filter<F>(self, predicate: F) -> Filter<Self, F>
where
Self: Sized,
F: Fn(&Self::Value) -> bool,
{
Filter {
strategy: self,
predicate,
}
}
fn zip<S>(self, other: S) -> Zip<Self, S>
where
Self: Sized,
S: Strategy,
{
Zip {
left: self,
right: other,
}
}
fn flat_map<F, S>(self, f: F) -> FlatMap<Self, F>
where
Self: Sized,
F: Fn(Self::Value) -> S,
S: Strategy,
{
FlatMap {
strategy: self,
flat_mapper: f,
}
}
}
pub struct Map<S, F> {
strategy: S,
mapper: F,
}
#[derive(Debug, Clone, Copy)]
pub struct MappedValue<T, U> {
pub input: T,
pub output: U,
}
impl<T, U> std::ops::Deref for MappedValue<T, U> {
type Target = U;
fn deref(&self) -> &Self::Target {
&self.output
}
}
impl<T, U, V> PartialEq<V> for MappedValue<T, U>
where
U: PartialEq<V>,
{
fn eq(&self, other: &V) -> bool {
self.output.eq(other)
}
}
impl<T, U, V> PartialOrd<V> for MappedValue<T, U>
where
U: PartialOrd<V>,
{
fn partial_cmp(&self, other: &V) -> Option<std::cmp::Ordering> {
self.output.partial_cmp(other)
}
}
impl<T, U: std::fmt::Display> std::fmt::Display for MappedValue<T, U> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.output.fmt(f)
}
}
impl<T, U: std::ops::Add<Output = V>, V> std::ops::Add for MappedValue<T, U> {
type Output = V;
fn add(self, rhs: Self) -> Self::Output {
self.output + rhs.output
}
}
impl<T, U: std::ops::Sub<Output = V>, V> std::ops::Sub for MappedValue<T, U> {
type Output = V;
fn sub(self, rhs: Self) -> Self::Output {
self.output - rhs.output
}
}
impl<T, U: std::ops::Mul<Output = V>, V> std::ops::Mul for MappedValue<T, U> {
type Output = V;
fn mul(self, rhs: Self) -> Self::Output {
self.output * rhs.output
}
}
impl<T, U: std::ops::Div<Output = V>, V> std::ops::Div for MappedValue<T, U> {
type Output = V;
fn div(self, rhs: Self) -> Self::Output {
self.output / rhs.output
}
}
impl<T, U, V, W> std::ops::Rem<V> for MappedValue<T, U>
where
U: std::ops::Rem<V, Output = W>,
{
type Output = W;
fn rem(self, rhs: V) -> Self::Output {
self.output % rhs
}
}
impl<S, F, U> Strategy for Map<S, F>
where
S: Strategy,
F: Fn(S::Value) -> U + Clone + 'static,
U: 'static,
S::Value: Clone,
{
type Value = MappedValue<S::Value, U>;
fn generate<R: rand::Rng>(&self, rng: &mut R, config: &GeneratorConfig) -> Self::Value {
let input = self.strategy.generate(rng, config);
let output = (self.mapper)(input.clone());
MappedValue { input, output }
}
fn shrink(&self, value: &Self::Value) -> Box<dyn Iterator<Item = Self::Value>> {
let shrunk_inputs = self.strategy.shrink(&value.input);
let mapper = self.mapper.clone();
Box::new(shrunk_inputs.map(move |input| {
let output = mapper(input.clone());
MappedValue { input, output }
}))
}
}
pub struct Filter<S, F> {
strategy: S,
predicate: F,
}
impl<S, F> Strategy for Filter<S, F>
where
S: Strategy,
F: Fn(&S::Value) -> bool + Clone + 'static,
S::Value: Clone,
{
type Value = S::Value;
fn generate<R: rand::Rng>(&self, rng: &mut R, config: &GeneratorConfig) -> Self::Value {
for _ in 0..1000 {
let value = self.strategy.generate(rng, config);
if (self.predicate)(&value) {
return value;
}
}
panic!("Filter strategy failed to generate a valid value after 1000 attempts");
}
fn shrink(&self, value: &Self::Value) -> Box<dyn Iterator<Item = Self::Value>> {
let shrunk_values = self.strategy.shrink(value);
let predicate = self.predicate.clone();
Box::new(shrunk_values.filter(move |v| predicate(v)))
}
}
pub struct Zip<L, R> {
left: L,
right: R,
}
impl<L, R> Strategy for Zip<L, R>
where
L: Strategy,
R: Strategy,
L::Value: Clone,
R::Value: Clone,
{
type Value = (L::Value, R::Value);
fn generate<RNG: rand::Rng>(&self, rng: &mut RNG, config: &GeneratorConfig) -> Self::Value {
let left_value = self.left.generate(rng, config);
let right_value = self.right.generate(rng, config);
(left_value, right_value)
}
fn shrink(&self, value: &Self::Value) -> Box<dyn Iterator<Item = Self::Value>> {
let (left_val, right_val) = value;
let left_shrinks = self.left.shrink(left_val);
let right_val_for_left = right_val.clone();
let left_combinations = left_shrinks.map(move |l| (l, right_val_for_left.clone()));
let right_shrinks = self.right.shrink(right_val);
let left_val_for_right = left_val.clone();
let right_combinations = right_shrinks.map(move |r| (left_val_for_right.clone(), r));
Box::new(left_combinations.chain(right_combinations))
}
}
pub struct FlatMap<S, F> {
strategy: S,
flat_mapper: F,
}
#[derive(Debug, Clone)]
pub struct FlatMappedValue<T, U> {
pub first: T,
pub second: U,
}
impl<S, F, S2> Strategy for FlatMap<S, F>
where
S: Strategy,
F: Fn(S::Value) -> S2 + Clone + 'static,
S2: Strategy,
S::Value: Clone,
S2::Value: Clone,
{
type Value = FlatMappedValue<S::Value, S2::Value>;
fn generate<R: rand::Rng>(&self, rng: &mut R, config: &GeneratorConfig) -> Self::Value {
let first = self.strategy.generate(rng, config);
let second_strategy = (self.flat_mapper)(first.clone());
let second = second_strategy.generate(rng, config);
FlatMappedValue { first, second }
}
fn shrink(&self, value: &Self::Value) -> Box<dyn Iterator<Item = Self::Value>> {
let flat_mapper = self.flat_mapper.clone();
let second_strategy = (flat_mapper.clone())(value.first.clone());
let second_shrinks = second_strategy.shrink(&value.second);
let first_val = value.first.clone();
let second_only_shrinks: Vec<_> = second_shrinks
.map(move |second_shrunk| FlatMappedValue {
first: first_val.clone(),
second: second_shrunk,
})
.collect();
let first_shrinks = self.strategy.shrink(&value.first);
let second_val = value.second.clone();
let first_shrink_combinations: Vec<_> = first_shrinks
.flat_map({
let flat_mapper = flat_mapper.clone();
let second_val = second_val.clone();
move |first_shrunk| {
let second_strategy = flat_mapper(first_shrunk.clone());
let second_shrinks = second_strategy.shrink(&second_val);
second_shrinks
.map(move |second_shrunk| FlatMappedValue {
first: first_shrunk.clone(),
second: second_shrunk,
})
.collect::<Vec<_>>()
}
})
.collect();
Box::new(
second_only_shrinks
.into_iter()
.chain(first_shrink_combinations),
)
}
}
#[derive(Debug, Clone)]
pub struct Just<T> {
value: T,
}
impl<T: Clone + 'static> Strategy for Just<T> {
type Value = T;
fn generate<R: rand::Rng>(&self, _rng: &mut R, _config: &GeneratorConfig) -> Self::Value {
self.value.clone()
}
fn shrink(&self, _value: &Self::Value) -> Box<dyn Iterator<Item = Self::Value>> {
Box::new(std::iter::empty())
}
}
impl<T> Generator<T> for Just<T>
where
T: Clone + std::fmt::Debug + PartialEq + 'static,
{
fn generate(&self, _rng: &mut dyn rand::RngCore, _config: &GeneratorConfig) -> T {
self.value.clone()
}
fn shrink(&self, _value: &T) -> Box<dyn Iterator<Item = T>> {
Box::new(std::iter::empty())
}
}
#[derive(Debug, Clone)]
pub struct OneOf<T> {
values: Vec<T>,
}
impl<T: Clone + PartialEq + 'static> Strategy for OneOf<T> {
type Value = T;
fn generate<R: rand::Rng>(&self, rng: &mut R, _config: &GeneratorConfig) -> Self::Value {
if self.values.is_empty() {
panic!("OneOf strategy cannot generate from empty collection");
}
let index = rng.gen_range(0..self.values.len());
self.values[index].clone()
}
fn shrink(&self, value: &Self::Value) -> Box<dyn Iterator<Item = Self::Value>> {
let shrinks: Vec<T> = self
.values
.iter()
.filter(|v| *v != value)
.cloned()
.collect();
Box::new(shrinks.into_iter())
}
}
#[derive(Debug, Clone)]
pub struct Range<T> {
start: T,
end: T,
}
macro_rules! impl_range_strategy {
($($t:ty),*) => {
$(
impl Strategy for Range<$t> {
type Value = $t;
fn generate<R: rand::Rng>(&self, rng: &mut R, _config: &GeneratorConfig) -> Self::Value {
rng.gen_range(self.start..=self.end)
}
fn shrink(&self, value: &Self::Value) -> Box<dyn Iterator<Item = Self::Value>> {
let value = *value;
if value == self.start {
return Box::new(std::iter::empty());
}
#[allow(unused_comparisons)]
let target = if self.start <= 0 && 0 <= self.end && 0 <= value {
0
} else {
self.start
};
let mut shrinks = Vec::new();
let mut current = value;
while current != target {
let diff = if current > target {
current - target
} else {
target - current
};
let step = if diff == 1 { 1 } else { diff / 2 };
current = if current > target {
current - step
} else {
current + step
};
if current >= self.start && current <= self.end && current != value {
shrinks.push(current);
}
if current == target {
break;
}
}
Box::new(shrinks.into_iter())
}
}
)*
};
}
impl_range_strategy!(
i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize
);
macro_rules! impl_range_generator {
($($t:ty),*) => {
$(
impl Generator<$t> for Range<$t> {
fn generate(&self, rng: &mut dyn rand::RngCore, _config: &GeneratorConfig) -> $t {
use rand::Rng;
rng.gen_range(self.start..=self.end)
}
fn shrink(&self, value: &$t) -> Box<dyn Iterator<Item = $t>> {
Strategy::shrink(self, value)
}
}
)*
};
}
impl_range_generator!(
i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize
);
impl Strategy for Range<f32> {
type Value = f32;
fn generate<R: rand::Rng>(&self, rng: &mut R, _config: &GeneratorConfig) -> Self::Value {
rng.gen_range(self.start..=self.end)
}
fn shrink(&self, value: &Self::Value) -> Box<dyn Iterator<Item = Self::Value>> {
let value = *value;
if value.is_nan() || value.is_infinite() {
return Box::new(std::iter::empty());
}
if (value - self.start).abs() < f32::EPSILON {
return Box::new(std::iter::empty());
}
let target = if self.start <= 0.0 && 0.0 <= self.end && 0.0 <= value {
0.0
} else {
self.start
};
let mut shrinks = Vec::new();
let mut current = value;
while (current - target).abs() > f32::EPSILON {
let diff = current - target;
let step = diff / 2.0;
if step.abs() < f32::EPSILON * 10.0 {
if target >= self.start
&& target <= self.end
&& (target - value).abs() > f32::EPSILON
{
shrinks.push(target);
}
break;
}
current -= step;
if current >= self.start
&& current <= self.end
&& (current - value).abs() > f32::EPSILON
{
shrinks.push(current);
}
if (current - target).abs() < f32::EPSILON {
break;
}
}
Box::new(shrinks.into_iter())
}
}
impl Strategy for Range<f64> {
type Value = f64;
fn generate<R: rand::Rng>(&self, rng: &mut R, _config: &GeneratorConfig) -> Self::Value {
rng.gen_range(self.start..=self.end)
}
fn shrink(&self, value: &Self::Value) -> Box<dyn Iterator<Item = Self::Value>> {
let value = *value;
if value.is_nan() || value.is_infinite() {
return Box::new(std::iter::empty());
}
if (value - self.start).abs() < f64::EPSILON {
return Box::new(std::iter::empty());
}
let target = if self.start <= 0.0 && 0.0 <= self.end && 0.0 <= value {
0.0
} else {
self.start
};
let mut shrinks = Vec::new();
let mut current = value;
while (current - target).abs() > f64::EPSILON {
let diff = current - target;
let step = diff / 2.0;
if step.abs() < f64::EPSILON * 10.0 {
if target >= self.start
&& target <= self.end
&& (target - value).abs() > f64::EPSILON
{
shrinks.push(target);
}
break;
}
current -= step;
if current >= self.start
&& current <= self.end
&& (current - value).abs() > f64::EPSILON
{
shrinks.push(current);
}
if (current - target).abs() < f64::EPSILON {
break;
}
}
Box::new(shrinks.into_iter())
}
}
impl Generator<f32> for Range<f32> {
fn generate(&self, rng: &mut dyn rand::RngCore, _config: &GeneratorConfig) -> f32 {
use rand::Rng;
rng.gen_range(self.start..=self.end)
}
fn shrink(&self, value: &f32) -> Box<dyn Iterator<Item = f32>> {
Strategy::shrink(self, value)
}
}
impl Generator<f64> for Range<f64> {
fn generate(&self, rng: &mut dyn rand::RngCore, _config: &GeneratorConfig) -> f64 {
use rand::Rng;
rng.gen_range(self.start..=self.end)
}
fn shrink(&self, value: &f64) -> Box<dyn Iterator<Item = f64>> {
Strategy::shrink(self, value)
}
}
pub struct Tuple3<A, B, C> {
a: A,
b: B,
c: C,
}
impl<A, B, C> Strategy for Tuple3<A, B, C>
where
A: Strategy,
B: Strategy,
C: Strategy,
A::Value: Clone,
B::Value: Clone,
C::Value: Clone,
{
type Value = (A::Value, B::Value, C::Value);
fn generate<RNG: rand::Rng>(&self, rng: &mut RNG, config: &GeneratorConfig) -> Self::Value {
let a_val = self.a.generate(rng, config);
let b_val = self.b.generate(rng, config);
let c_val = self.c.generate(rng, config);
(a_val, b_val, c_val)
}
fn shrink(&self, value: &Self::Value) -> Box<dyn Iterator<Item = Self::Value>> {
let (a_val, b_val, c_val) = value;
let a_shrinks = self.a.shrink(a_val);
let b_val_for_a = b_val.clone();
let c_val_for_a = c_val.clone();
let a_combinations = a_shrinks.map(move |a| (a, b_val_for_a.clone(), c_val_for_a.clone()));
let b_shrinks = self.b.shrink(b_val);
let a_val_for_b = a_val.clone();
let c_val_for_b = c_val.clone();
let b_combinations = b_shrinks.map(move |b| (a_val_for_b.clone(), b, c_val_for_b.clone()));
let c_shrinks = self.c.shrink(c_val);
let a_val_for_c = a_val.clone();
let b_val_for_c = b_val.clone();
let c_combinations = c_shrinks.map(move |c| (a_val_for_c.clone(), b_val_for_c.clone(), c));
Box::new(a_combinations.chain(b_combinations).chain(c_combinations))
}
}
pub struct Tuple4<A, B, C, D> {
a: A,
b: B,
c: C,
d: D,
}
impl<A, B, C, D> Strategy for Tuple4<A, B, C, D>
where
A: Strategy,
B: Strategy,
C: Strategy,
D: Strategy,
A::Value: Clone,
B::Value: Clone,
C::Value: Clone,
D::Value: Clone,
{
type Value = (A::Value, B::Value, C::Value, D::Value);
fn generate<RNG: rand::Rng>(&self, rng: &mut RNG, config: &GeneratorConfig) -> Self::Value {
let a_val = self.a.generate(rng, config);
let b_val = self.b.generate(rng, config);
let c_val = self.c.generate(rng, config);
let d_val = self.d.generate(rng, config);
(a_val, b_val, c_val, d_val)
}
fn shrink(&self, value: &Self::Value) -> Box<dyn Iterator<Item = Self::Value>> {
let (a_val, b_val, c_val, d_val) = value;
let a_shrinks = self.a.shrink(a_val);
let b_val_for_a = b_val.clone();
let c_val_for_a = c_val.clone();
let d_val_for_a = d_val.clone();
let a_combinations = a_shrinks.map(move |a| {
(
a,
b_val_for_a.clone(),
c_val_for_a.clone(),
d_val_for_a.clone(),
)
});
let b_shrinks = self.b.shrink(b_val);
let a_val_for_b = a_val.clone();
let c_val_for_b = c_val.clone();
let d_val_for_b = d_val.clone();
let b_combinations = b_shrinks.map(move |b| {
(
a_val_for_b.clone(),
b,
c_val_for_b.clone(),
d_val_for_b.clone(),
)
});
let c_shrinks = self.c.shrink(c_val);
let a_val_for_c = a_val.clone();
let b_val_for_c = b_val.clone();
let d_val_for_c = d_val.clone();
let c_combinations = c_shrinks.map(move |c| {
(
a_val_for_c.clone(),
b_val_for_c.clone(),
c,
d_val_for_c.clone(),
)
});
let d_shrinks = self.d.shrink(d_val);
let a_val_for_d = a_val.clone();
let b_val_for_d = b_val.clone();
let c_val_for_d = c_val.clone();
let d_combinations = d_shrinks.map(move |d| {
(
a_val_for_d.clone(),
b_val_for_d.clone(),
c_val_for_d.clone(),
d,
)
});
Box::new(
a_combinations
.chain(b_combinations)
.chain(c_combinations)
.chain(d_combinations),
)
}
}
pub struct Tuple5<A, B, C, D, E> {
a: A,
b: B,
c: C,
d: D,
e: E,
}
impl<A, B, C, D, E> Strategy for Tuple5<A, B, C, D, E>
where
A: Strategy,
B: Strategy,
C: Strategy,
D: Strategy,
E: Strategy,
A::Value: Clone,
B::Value: Clone,
C::Value: Clone,
D::Value: Clone,
E::Value: Clone,
{
type Value = (A::Value, B::Value, C::Value, D::Value, E::Value);
fn generate<RNG: rand::Rng>(&self, rng: &mut RNG, config: &GeneratorConfig) -> Self::Value {
let a_val = self.a.generate(rng, config);
let b_val = self.b.generate(rng, config);
let c_val = self.c.generate(rng, config);
let d_val = self.d.generate(rng, config);
let e_val = self.e.generate(rng, config);
(a_val, b_val, c_val, d_val, e_val)
}
fn shrink(&self, value: &Self::Value) -> Box<dyn Iterator<Item = Self::Value>> {
let (a_val, b_val, c_val, d_val, e_val) = value;
let a_shrinks = self.a.shrink(a_val);
let b_val_for_a = b_val.clone();
let c_val_for_a = c_val.clone();
let d_val_for_a = d_val.clone();
let e_val_for_a = e_val.clone();
let a_combinations = a_shrinks.map(move |a| {
(
a,
b_val_for_a.clone(),
c_val_for_a.clone(),
d_val_for_a.clone(),
e_val_for_a.clone(),
)
});
let b_shrinks = self.b.shrink(b_val);
let a_val_for_b = a_val.clone();
let c_val_for_b = c_val.clone();
let d_val_for_b = d_val.clone();
let e_val_for_b = e_val.clone();
let b_combinations = b_shrinks.map(move |b| {
(
a_val_for_b.clone(),
b,
c_val_for_b.clone(),
d_val_for_b.clone(),
e_val_for_b.clone(),
)
});
let c_shrinks = self.c.shrink(c_val);
let a_val_for_c = a_val.clone();
let b_val_for_c = b_val.clone();
let d_val_for_c = d_val.clone();
let e_val_for_c = e_val.clone();
let c_combinations = c_shrinks.map(move |c| {
(
a_val_for_c.clone(),
b_val_for_c.clone(),
c,
d_val_for_c.clone(),
e_val_for_c.clone(),
)
});
let d_shrinks = self.d.shrink(d_val);
let a_val_for_d = a_val.clone();
let b_val_for_d = b_val.clone();
let c_val_for_d = c_val.clone();
let e_val_for_d = e_val.clone();
let d_combinations = d_shrinks.map(move |d| {
(
a_val_for_d.clone(),
b_val_for_d.clone(),
c_val_for_d.clone(),
d,
e_val_for_d.clone(),
)
});
let e_shrinks = self.e.shrink(e_val);
let a_val_for_e = a_val.clone();
let b_val_for_e = b_val.clone();
let c_val_for_e = c_val.clone();
let d_val_for_e = d_val.clone();
let e_combinations = e_shrinks.map(move |e| {
(
a_val_for_e.clone(),
b_val_for_e.clone(),
c_val_for_e.clone(),
d_val_for_e.clone(),
e,
)
});
Box::new(
a_combinations
.chain(b_combinations)
.chain(c_combinations)
.chain(d_combinations)
.chain(e_combinations),
)
}
}
pub fn tuple3<A, B, C>(a: A, b: B, c: C) -> Tuple3<A, B, C> {
Tuple3 { a, b, c }
}
pub fn tuple4<A, B, C, D>(a: A, b: B, c: C, d: D) -> Tuple4<A, B, C, D> {
Tuple4 { a, b, c, d }
}
pub fn tuple5<A, B, C, D, E>(a: A, b: B, c: C, d: D, e: E) -> Tuple5<A, B, C, D, E> {
Tuple5 { a, b, c, d, e }
}
pub fn just<T: Clone>(value: T) -> Just<T> {
Just { value }
}
pub fn one_of<T: Clone>(values: Vec<T>) -> OneOf<T> {
OneOf { values }
}
pub fn range<T>(start: T, end: T) -> Range<T>
where
T: rand::distributions::uniform::SampleUniform + PartialOrd + Copy,
{
Range { start, end }
}
#[cfg(test)]
mod tests {
use super::*;
use rand::thread_rng;
#[test]
fn test_just_strategy() {
let strategy = just(42);
let mut rng = thread_rng();
let config = GeneratorConfig::default();
let value = Strategy::generate(&strategy, &mut rng, &config);
assert_eq!(value, 42);
let shrinks: Vec<_> = Strategy::shrink(&strategy, &value).collect();
assert!(shrinks.is_empty());
}
#[test]
fn test_one_of_strategy() {
let values = vec![1, 2, 3, 4, 5];
let strategy = one_of(values.clone());
let mut rng = thread_rng();
let config = GeneratorConfig::default();
let value = Strategy::generate(&strategy, &mut rng, &config);
assert!(values.contains(&value));
}
#[test]
fn test_range_strategy() {
let strategy = range(1, 10);
let mut rng = thread_rng();
let config = GeneratorConfig::default();
let value = Strategy::generate(&strategy, &mut rng, &config);
assert!((1..=10).contains(&value));
}
#[test]
fn test_strategy_map() {
let strategy = just(5).map(|x| x * 2);
let mut rng = thread_rng();
let config = GeneratorConfig::default();
let value = strategy.generate(&mut rng, &config);
assert_eq!(value.input, 5);
assert_eq!(value.output, 10);
let shrinks: Vec<_> = strategy.shrink(&value).collect();
assert!(shrinks.is_empty());
}
#[test]
fn test_strategy_zip() {
let strategy = just(42).zip(range(1, 5));
let mut rng = thread_rng();
let config = GeneratorConfig::default();
let (left, right) = strategy.generate(&mut rng, &config);
assert_eq!(left, 42);
assert!((1..=5).contains(&right));
}
#[test]
fn test_strategy_filter() {
let strategy = range(1, 100).filter(|x| x % 2 == 0);
let mut rng = thread_rng();
let config = GeneratorConfig::default();
let value = strategy.generate(&mut rng, &config);
assert!((1..=100).contains(&value));
assert_eq!(value % 2, 0); }
#[test]
fn test_strategy_composition() {
let strategy = range(1, 5).map(|x| format!("value_{}", x)).zip(just(true));
let mut rng = thread_rng();
let config = GeneratorConfig::default();
let (mapped_val, bool_val) = strategy.generate(&mut rng, &config);
assert!(mapped_val.output.starts_with("value_"));
assert!(bool_val);
}
#[test]
fn test_map_shrinking() {
let strategy = range(1, 100).map(|x| x * 2);
let mut rng = thread_rng();
let config = GeneratorConfig::default();
let value = strategy.generate(&mut rng, &config);
let _shrinks: Vec<_> = strategy.shrink(&value).take(5).collect();
}
#[test]
fn test_filter_shrinking() {
let strategy = range(10, 100).filter(|x| x % 2 == 0);
let mut rng = thread_rng();
let config = GeneratorConfig::default();
let value = strategy.generate(&mut rng, &config);
assert_eq!(value % 2, 0);
let shrinks: Vec<_> = strategy.shrink(&value).take(10).collect();
for shrink in shrinks {
assert_eq!(shrink % 2, 0, "Shrink {} should be even", shrink);
}
}
#[test]
fn test_zip_shrinking() {
let strategy = just(10).zip(just(20));
let mut rng = thread_rng();
let config = GeneratorConfig::default();
let value = strategy.generate(&mut rng, &config);
assert_eq!(value, (10, 20));
let shrinks: Vec<_> = strategy.shrink(&value).collect();
assert!(shrinks.is_empty());
}
#[test]
fn test_flat_map_basic() {
let strategy = range(1, 5).flat_map(|n| just(vec![42; n as usize]));
let mut rng = thread_rng();
let config = GeneratorConfig::default();
let value = strategy.generate(&mut rng, &config);
assert!((1..=5).contains(&(value.first as i32)));
assert_eq!(value.second.len(), value.first as usize);
assert!(value.second.iter().all(|&x| x == 42));
}
#[test]
fn test_flat_map_shrinking() {
let strategy = just(3).flat_map(|n| just(vec![1; n as usize]));
let mut rng = thread_rng();
let config = GeneratorConfig::default();
let value = strategy.generate(&mut rng, &config);
assert_eq!(value.first, 3);
assert_eq!(value.second, vec![1, 1, 1]);
let shrinks: Vec<_> = strategy.shrink(&value).collect();
assert!(shrinks.is_empty());
}
#[test]
fn test_range_shrinking_positive_to_zero() {
let strategy = range(0, 200);
let value = 100i32;
let shrinks: Vec<i32> = Strategy::shrink(&strategy, &value).collect();
assert!(!shrinks.is_empty(), "Should produce shrinks");
assert_eq!(shrinks[0], 50, "First shrink should be halfway to zero");
assert!(shrinks.contains(&0), "Should eventually reach zero");
for shrink in &shrinks {
assert!(
*shrink < value,
"Shrink {} should be less than {}",
shrink,
value
);
}
}
#[test]
fn test_range_shrinking_sequence() {
let strategy = range(0, 100);
let value = 64i32;
let shrinks: Vec<i32> = Strategy::shrink(&strategy, &value).collect();
assert_eq!(shrinks[0], 32);
assert_eq!(shrinks[1], 16);
assert_eq!(shrinks[2], 8);
assert_eq!(shrinks[3], 4);
assert_eq!(shrinks[4], 2);
assert_eq!(shrinks[5], 1);
assert_eq!(shrinks[6], 0);
}
#[test]
fn test_range_shrinking_negative_to_zero() {
let strategy = range(-100, 0);
let value = -50i32;
let shrinks: Vec<i32> = Strategy::shrink(&strategy, &value).collect();
assert!(!shrinks.is_empty());
assert!(shrinks[0] < value, "Should shrink toward range start");
}
#[test]
fn test_range_shrinking_respects_bounds() {
let strategy = range(10, 100);
let value = 50i32;
let shrinks: Vec<i32> = Strategy::shrink(&strategy, &value).collect();
for shrink in &shrinks {
assert!(
*shrink >= 10,
"Shrink {} should be >= range start 10",
shrink
);
assert!(
*shrink <= 100,
"Shrink {} should be <= range end 100",
shrink
);
}
assert!(shrinks.contains(&10));
}
#[test]
fn test_range_shrinking_at_start() {
let strategy = range(5, 100);
let value = 5i32;
let shrinks: Vec<i32> = Strategy::shrink(&strategy, &value).collect();
assert!(shrinks.is_empty(), "Value at range start should not shrink");
}
#[test]
fn test_range_shrinking_unsigned() {
let strategy = range(0u32, 200u32);
let value = 100u32;
let shrinks: Vec<u32> = Strategy::shrink(&strategy, &value).collect();
assert!(!shrinks.is_empty());
assert_eq!(shrinks[0], 50);
assert!(shrinks.contains(&0));
}
#[test]
fn test_one_of_shrinking_tries_alternatives() {
let strategy = one_of(vec![1, 2, 3, 4, 5]);
let value = 3;
let shrinks: Vec<i32> = Strategy::shrink(&strategy, &value).collect();
assert_eq!(shrinks.len(), 4, "Should have 4 alternatives");
assert!(shrinks.contains(&1));
assert!(shrinks.contains(&2));
assert!(shrinks.contains(&4));
assert!(shrinks.contains(&5));
assert!(!shrinks.contains(&3), "Should not contain current value");
}
#[test]
fn test_one_of_shrinking_order() {
let strategy = one_of(vec![10, 20, 30, 40, 50]);
let value = 50;
let shrinks: Vec<i32> = Strategy::shrink(&strategy, &value).collect();
assert_eq!(shrinks[0], 10);
assert_eq!(shrinks[1], 20);
assert_eq!(shrinks[2], 30);
assert_eq!(shrinks[3], 40);
}
#[test]
fn test_map_shrinking_with_range() {
let strategy = range(0, 100).map(|x| x * 2);
let value = strategy.generate(&mut thread_rng(), &GeneratorConfig::default());
if value.input == 0 {
return;
}
let _shrinks: Vec<_> = Strategy::shrink(&strategy, &value).take(5).collect();
assert!(!_shrinks.is_empty(), "Should produce shrinks if input > 0");
for shrink in &_shrinks {
assert_eq!(shrink.output, shrink.input * 2);
assert!(shrink.input < value.input);
}
}
#[test]
fn test_filter_shrinking_maintains_predicate() {
let strategy = range(10, 100).filter(|&x| x % 10 == 0);
let value = strategy.generate(&mut thread_rng(), &GeneratorConfig::default());
assert_eq!(value % 10, 0);
let shrinks: Vec<i32> = Strategy::shrink(&strategy, &value).take(20).collect();
for shrink in &shrinks {
assert_eq!(
shrink % 10,
0,
"Shrink {} should be divisible by 10",
shrink
);
}
}
#[test]
fn test_zip_shrinking_both_sides() {
let left_strategy = range(0, 100);
let right_strategy = range(0, 50);
let strategy = left_strategy.zip(right_strategy);
let value = (80i32, 40i32);
let shrinks: Vec<(i32, i32)> = Strategy::shrink(&strategy, &value).take(20).collect();
assert!(!shrinks.is_empty());
assert!(shrinks.iter().any(|(l, r)| *l < 80 && *r == 40));
assert!(shrinks.iter().any(|(l, r)| *l == 80 && *r < 40));
}
#[test]
fn test_f32_shrinking_positive_to_zero() {
let strategy = range(0.0f32, 200.0f32);
let value = 100.0f32;
let shrinks: Vec<f32> = Strategy::shrink(&strategy, &value).collect();
assert!(!shrinks.is_empty(), "Should produce shrinks for f32");
assert!(
(shrinks[0] - 50.0).abs() < 1.0,
"First shrink should be near 50.0, got {}",
shrinks[0]
);
assert!(
shrinks.iter().any(|&s| s < 1.0),
"Should shrink close to zero"
);
for shrink in &shrinks {
assert!(
*shrink < value,
"Shrink {} should be less than {}",
shrink,
value
);
}
}
#[test]
fn test_f64_shrinking_positive_to_zero() {
let strategy = range(0.0f64, 200.0f64);
let value = 100.0f64;
let shrinks: Vec<f64> = Strategy::shrink(&strategy, &value).collect();
assert!(!shrinks.is_empty(), "Should produce shrinks for f64");
assert!(
(shrinks[0] - 50.0).abs() < 1.0,
"First shrink should be near 50.0, got {}",
shrinks[0]
);
assert!(
shrinks.iter().any(|&s| s < 1.0),
"Should shrink close to zero"
);
for shrink in &shrinks {
assert!(
*shrink < value,
"Shrink {} should be less than {}",
shrink,
value
);
}
}
#[test]
fn test_f32_shrinking_respects_bounds() {
let strategy = range(10.0f32, 100.0f32);
let value = 50.0f32;
let shrinks: Vec<f32> = Strategy::shrink(&strategy, &value).collect();
for shrink in &shrinks {
assert!(
*shrink >= 10.0 - f32::EPSILON,
"Shrink {} should be >= range start 10.0",
shrink
);
assert!(
*shrink <= 100.0 + f32::EPSILON,
"Shrink {} should be <= range end 100.0",
shrink
);
}
assert!(
shrinks.iter().any(|&s| (s - 10.0).abs() < 1.0),
"Should shrink close to range start"
);
}
#[test]
fn test_f64_shrinking_respects_bounds() {
let strategy = range(10.0f64, 100.0f64);
let value = 50.0f64;
let shrinks: Vec<f64> = Strategy::shrink(&strategy, &value).collect();
for shrink in &shrinks {
assert!(
*shrink >= 10.0 - f64::EPSILON,
"Shrink {} should be >= range start 10.0",
shrink
);
assert!(
*shrink <= 100.0 + f64::EPSILON,
"Shrink {} should be <= range end 100.0",
shrink
);
}
assert!(
shrinks.iter().any(|&s| (s - 10.0).abs() < 1.0),
"Should shrink close to range start"
);
}
#[test]
fn test_f32_shrinking_at_start() {
let strategy = range(5.0f32, 100.0f32);
let value = 5.0f32;
let shrinks: Vec<f32> = Strategy::shrink(&strategy, &value).collect();
assert!(shrinks.is_empty(), "Value at range start should not shrink");
}
#[test]
fn test_f64_shrinking_at_start() {
let strategy = range(5.0f64, 100.0f64);
let value = 5.0f64;
let shrinks: Vec<f64> = Strategy::shrink(&strategy, &value).collect();
assert!(shrinks.is_empty(), "Value at range start should not shrink");
}
#[test]
fn test_f32_shrinking_nan() {
let strategy = range(0.0f32, 100.0f32);
let value = f32::NAN;
let shrinks: Vec<f32> = Strategy::shrink(&strategy, &value).collect();
assert!(shrinks.is_empty(), "NaN should not produce shrinks");
}
#[test]
fn test_f32_shrinking_infinity() {
let strategy = range(0.0f32, 100.0f32);
let value = f32::INFINITY;
let shrinks: Vec<f32> = Strategy::shrink(&strategy, &value).collect();
assert!(shrinks.is_empty(), "Infinity should not produce shrinks");
}
#[test]
fn test_f64_shrinking_special_values() {
let strategy = range(0.0f64, 100.0f64);
let nan_shrinks: Vec<f64> = Strategy::shrink(&strategy, &f64::NAN).collect();
assert!(nan_shrinks.is_empty(), "NaN should not produce shrinks");
let inf_shrinks: Vec<f64> = Strategy::shrink(&strategy, &f64::INFINITY).collect();
assert!(
inf_shrinks.is_empty(),
"Infinity should not produce shrinks"
);
}
#[test]
fn test_f32_shrinking_small_values() {
let strategy = range(0.0f32, 1.0f32);
let value = 0.5f32;
let shrinks: Vec<f32> = Strategy::shrink(&strategy, &value).collect();
assert!(
!shrinks.is_empty(),
"Should produce shrinks for small values"
);
for shrink in &shrinks {
assert!(
*shrink < value,
"Shrink {} should be less than {}",
shrink,
value
);
assert!(*shrink >= 0.0, "Shrink {} should be >= 0.0", shrink);
}
}
#[test]
fn test_f64_shrinking_negative_range() {
let strategy = range(-100.0f64, -10.0f64);
let value = -50.0f64;
let shrinks: Vec<f64> = Strategy::shrink(&strategy, &value).collect();
for shrink in &shrinks {
assert!(
*shrink >= -100.0 - f64::EPSILON,
"Shrink {} should be >= -100.0",
shrink
);
assert!(
*shrink <= -10.0 + f64::EPSILON,
"Shrink {} should be <= -10.0",
shrink
);
}
}
#[test]
fn test_tuple3_generation() {
let strategy = tuple3(range(1, 10), range(20, 30), range(100, 200));
let mut rng = thread_rng();
let config = GeneratorConfig::default();
let (a, b, c) = Strategy::generate(&strategy, &mut rng, &config);
assert!((1..=10).contains(&a));
assert!((20..=30).contains(&b));
assert!((100..=200).contains(&c));
}
#[test]
fn test_tuple3_shrinking_first_element() {
let strategy = tuple3(range(1, 100), just(50), just(200));
let value = (100, 50, 200);
let shrinks: Vec<_> = Strategy::shrink(&strategy, &value).collect();
assert!(!shrinks.is_empty());
for (a, b, c) in &shrinks {
assert!(*a < 100, "First element should shrink");
assert_eq!(*b, 50, "Second element should stay constant");
assert_eq!(*c, 200, "Third element should stay constant");
}
}
#[test]
fn test_tuple3_shrinking_all_elements() {
let strategy = tuple3(range(1, 100), range(1, 100), range(1, 100));
let value = (50, 60, 70);
let shrinks: Vec<_> = Strategy::shrink(&strategy, &value).collect();
assert!(!shrinks.is_empty());
let has_first_shrink = shrinks
.iter()
.any(|(a, b, c)| *a != 50 && *b == 60 && *c == 70);
let has_second_shrink = shrinks
.iter()
.any(|(a, b, c)| *a == 50 && *b != 60 && *c == 70);
let has_third_shrink = shrinks
.iter()
.any(|(a, b, c)| *a == 50 && *b == 60 && *c != 70);
assert!(has_first_shrink, "Should have shrinks for first element");
assert!(has_second_shrink, "Should have shrinks for second element");
assert!(has_third_shrink, "Should have shrinks for third element");
}
#[test]
fn test_tuple4_generation() {
let strategy = tuple4(
range(1, 10),
range(20, 30),
range(100, 200),
range(500, 1000),
);
let mut rng = thread_rng();
let config = GeneratorConfig::default();
let (a, b, c, d) = Strategy::generate(&strategy, &mut rng, &config);
assert!((1..=10).contains(&a));
assert!((20..=30).contains(&b));
assert!((100..=200).contains(&c));
assert!((500..=1000).contains(&d));
}
#[test]
fn test_tuple4_shrinking() {
let strategy = tuple4(range(1, 100), range(1, 100), just(50), just(200));
let value = (100, 80, 50, 200);
let shrinks: Vec<_> = Strategy::shrink(&strategy, &value).collect();
assert!(!shrinks.is_empty());
for (a, b, c, d) in &shrinks {
assert_eq!(*c, 50, "Third element should stay constant");
assert_eq!(*d, 200, "Fourth element should stay constant");
assert!(
*a <= 100 && *b <= 80,
"First two elements should shrink or stay same"
);
}
}
#[test]
fn test_tuple5_generation() {
let strategy = tuple5(
range(1, 10),
range(20, 30),
range(100, 200),
range(500, 1000),
range(5000, 10000),
);
let mut rng = thread_rng();
let config = GeneratorConfig::default();
let (a, b, c, d, e) = Strategy::generate(&strategy, &mut rng, &config);
assert!((1..=10).contains(&a));
assert!((20..=30).contains(&b));
assert!((100..=200).contains(&c));
assert!((500..=1000).contains(&d));
assert!((5000..=10000).contains(&e));
}
#[test]
fn test_tuple5_shrinking() {
let strategy = tuple5(range(1, 100), just(20), just(30), just(40), range(1, 100));
let value = (100, 20, 30, 40, 80);
let shrinks: Vec<_> = Strategy::shrink(&strategy, &value).collect();
assert!(!shrinks.is_empty());
for (a, b, c, d, e) in &shrinks {
assert_eq!(*b, 20, "Second element should stay constant");
assert_eq!(*c, 30, "Third element should stay constant");
assert_eq!(*d, 40, "Fourth element should stay constant");
assert!(
*a <= 100 && *e <= 80,
"First and last elements should shrink or stay same"
);
}
}
#[test]
fn test_tuple5_shrinking_all_elements() {
let strategy = tuple5(
range(1, 100),
range(1, 100),
range(1, 100),
range(1, 100),
range(1, 100),
);
let value = (50, 60, 70, 80, 90);
let shrinks: Vec<_> = Strategy::shrink(&strategy, &value).collect();
assert!(!shrinks.is_empty());
let has_first = shrinks
.iter()
.any(|(a, b, c, d, e)| *a != 50 && *b == 60 && *c == 70 && *d == 80 && *e == 90);
let has_second = shrinks
.iter()
.any(|(a, b, c, d, e)| *a == 50 && *b != 60 && *c == 70 && *d == 80 && *e == 90);
let has_third = shrinks
.iter()
.any(|(a, b, c, d, e)| *a == 50 && *b == 60 && *c != 70 && *d == 80 && *e == 90);
let has_fourth = shrinks
.iter()
.any(|(a, b, c, d, e)| *a == 50 && *b == 60 && *c == 70 && *d != 80 && *e == 90);
let has_fifth = shrinks
.iter()
.any(|(a, b, c, d, e)| *a == 50 && *b == 60 && *c == 70 && *d == 80 && *e != 90);
assert!(has_first, "Should have shrinks for first element");
assert!(has_second, "Should have shrinks for second element");
assert!(has_third, "Should have shrinks for third element");
assert!(has_fourth, "Should have shrinks for fourth element");
assert!(has_fifth, "Should have shrinks for fifth element");
}
#[test]
fn test_tuple3_shrinking_order() {
let strategy = tuple3(range(1, 50), range(1, 50), range(1, 50));
let value = (25, 25, 25);
let shrinks: Vec<_> = Strategy::shrink(&strategy, &value).collect();
let first_batch: Vec<_> = shrinks.iter().take(5).collect();
for (a, b, c) in &first_batch {
assert!(
*a < 25,
"First batch should shrink first element: ({}, {}, {})",
a,
b,
c
);
assert_eq!(*b, 25, "First batch should keep second element constant");
assert_eq!(*c, 25, "First batch should keep third element constant");
}
}
}