use crate::internal::sample_target::Ratio;
use crate::internal::sample_target::SampleTarget;
use crate::BoxGen;
use crate::BoxShrink;
use rand::Rng;
use rand::SeedableRng;
pub fn mix_evenly<E>(generators: &[BoxGen<E>]) -> BoxGen<E>
where
E: Clone + 'static + core::fmt::Debug,
{
mix_with_sample_target(
generators
.first()
.map(|g| g.shrinker())
.unwrap_or(crate::shrinks::none()),
SampleTarget::evenly(generators),
)
}
pub fn mix_with_ratio<E>(ratios_and_gens: &[(Ratio, BoxGen<E>)]) -> BoxGen<E>
where
E: Clone + 'static + core::fmt::Debug,
{
mix_with_sample_target(
ratios_and_gens
.first()
.map(|pair| pair.1.shrinker())
.unwrap_or(crate::shrinks::none()),
SampleTarget::with_ratios(ratios_and_gens),
)
}
fn mix_with_sample_target<E>(
shrinker: BoxShrink<E>,
sample_target: SampleTarget<BoxGen<E>>,
) -> BoxGen<E>
where
E: Clone + 'static + core::fmt::Debug,
{
crate::gens::from_fn(move |seed, size| {
let high = sample_target.sample_domain_max();
let distr = rand::distr::Uniform::new_inclusive(1usize, high).unwrap();
let rng = rand_chacha::ChaCha8Rng::seed_from_u64(seed);
let mut sample_iterators = sample_target
.clone()
.map(|g| g.examples(seed, size.clone()));
rng.sample_iter(distr)
.map(move |sample| {
sample_iterators
.target_from_sample_mut(sample)
.map(|it| it.next())
.unwrap()
})
.take_while(|opt| opt.is_some())
.flatten()
})
.with_shrinker(shrinker)
}
#[cfg(test)]
mod test {
use crate::testing::assert_generator_can_shrink;
use crate::testing::assert_generator_cannot_shrink;
use crate::testing::assert_iter_eq;
use crate::testing::distribution::assert_generator_has_distribution_within_percent;
use crate::testing::distribution::distribution_from_pairs;
#[test]
fn all_values_from_generator_are_returned() {
let mixer = super::mix_with_ratio(&[(
42,
crate::gens::fixed::sequence(&[1, 2, 3, 4, 5, 6]),
)]);
assert_iter_eq(
mixer.examples(1337,0..=1000),
vec![1, 2, 3, 4, 5, 6],
"Mixing with a single generator should use values from that generator \
every time",
);
}
#[test]
fn mixer_be_used_more_than_once() {
let mixer = super::mix_with_ratio(&[(
42,
crate::gens::fixed::sequence(&['a', 'b', 'c']),
)]);
assert_iter_eq(
mixer.examples(1337, 0..=1000),
vec!['a', 'b', 'c'],
"should choose from singel given generator disgregarding of ratio",
);
assert_iter_eq(
mixer.examples(1337, 0..=1000),
vec!['a', 'b', 'c'],
"should get the same result even the second time around",
);
assert!(mixer.examples(1337, 0..=1000).next() == Some('a'));
assert!(mixer.examples(1337, 0..=1000).next() == Some('a'));
assert!(mixer.examples(1337, 0..=1000).next() == Some('a'));
}
#[test]
fn values_are_distributed_according_to_ratio() {
let mixer = super::mix_with_ratio(&[
(64, crate::gens::pick_evenly(&['A', 'B'])),
(32, crate::gens::pick_evenly(&['1', '2', '3', '4'])),
]);
let expected = distribution_from_pairs(&[
(4, 'A'),
(4, 'B'),
(1, '1'),
(1, '2'),
(1, '3'),
(1, '4'),
]);
assert_generator_has_distribution_within_percent(mixer, expected, 1.0)
}
#[test]
fn mix_evenly_has_uniform_distribution() {
let mixer = super::mix_evenly(&[
crate::gens::pick_evenly(&['A', 'B']),
crate::gens::pick_evenly(&['1', '2', '3', '4']),
]);
let expected = distribution_from_pairs(&[
(2, 'A'),
(2, 'B'),
(1, '1'),
(1, '2'),
(1, '3'),
(1, '4'),
]);
assert_generator_has_distribution_within_percent(mixer, expected, 1.5)
}
#[test]
fn mix_should_reuse_shrinker_from_first_generator() {
let with_shrinker = super::mix_evenly(&[
crate::gens::u8::any().with_shrinker(crate::shrinks::int_to_zero()),
crate::gens::u8::any().with_shrinker(crate::shrinks::none()),
]);
let without_shrinker = super::mix_evenly(&[
crate::gens::u8::any().with_shrinker(crate::shrinks::none()),
crate::gens::u8::any().with_shrinker(crate::shrinks::int_to_zero()),
]);
assert_generator_can_shrink(with_shrinker, 10);
assert_generator_cannot_shrink(without_shrinker, 10);
}
}