smoke 0.4.0

A framework for testing
Documentation
use super::super::rand::R;

/// Generator for an Item
///
/// The interface is very similar to an Iterator, except `next` is `gen`
pub trait Generator {
    /// Type generated by the generator
    type Item;

    /// Generate the next item
    fn generate(&self, r: &mut R) -> Self::Item;

    /// Map the output of a generator through a function
    ///
    /// ```
    /// use smoke::{Generator, generator::num};
    ///
    /// let generator = num::<u32>().map(|n| n + 1);
    /// ```
    fn map<O, F>(self, f: F) -> Map<Self, F>
    where
        Self: Sized,
        F: Fn(Self::Item) -> O,
    {
        Map { generator: self, f }
    }

    /// Filter the generated items such that only the item
    /// that matches the predicate 'f' are returned.
    ///
    /// Due to being an user controlled callback, it's not
    /// recommended to do heavy filtering with this function
    /// and instead use a generator that generate data
    /// that is closer to the end result. A general
    /// rule of thumb, is that if the callback accept
    /// less than half the generated value, then it should
    /// probably be refined at the source generator.
    ///
    /// ```
    /// use smoke::{Generator, generator::range};
    /// // u32 number between 1 and 1000 that are odd only
    /// let odd_gen = range(1u32..1000).such_that(|n| (n & 0x1) == 1);
    /// ```
    fn such_that<F>(self, f: F) -> SuchThat<Self, F>
    where
        Self: Sized,
        F: Fn(Self::Item) -> bool + Clone,
    {
        SuchThat {
            retry: 1000,
            generator: self,
            f,
        }
    }

    /// Combine two arbitrary generators into one that generate tuple item of both generators,
    /// transforming generator for A and generator for B into one generator of (A,B)
    ///
    /// ```
    /// use smoke::{Generator, generator::{Num, num}};
    ///
    /// let generator_a : Num<u32> = num();
    /// let generator_b : Num<u64> = num();
    ///
    /// let generator = generator_a.and(generator_b);
    /// ```
    fn and<G>(self, other: G) -> And<Self, G>
    where
        Self: Sized,
    {
        And {
            gen_a: self,
            gen_b: other,
        }
    }

    /// This generator or another one.
    ///
    /// It's not recommended to use this combinator to chain more than
    /// one generator, as the generator on the "left" will have
    /// a 1/2 handicapped at each iteration.
    ///
    /// Prefered `choose()` to do a unbiased choice or `frequency()` to
    /// control the distribution between generator.
    fn or<G>(self, other: G) -> Or<Self, G>
    where
        Self: Sized,
        G: Generator<Item = Self::Item>,
    {
        Or {
            gen_a: self,
            gen_b: other,
        }
    }

    /// Box a generator into a monomorphic fixed-sized type, that is easier to handle
    fn into_boxed(self) -> BoxGenerator<Self::Item>
    where
        Self: Sized + 'static,
    {
        BoxGenerator(Box::new(self))
    }
}

/// A generic generator
pub struct BoxGenerator<T>(pub(crate) Box<dyn Generator<Item = T>>);

impl<T> Generator for BoxGenerator<T> {
    type Item = T;
    fn generate(&self, r: &mut R) -> Self::Item {
        self.0.generate(r)
    }
    fn into_boxed(self) -> BoxGenerator<Self::Item> {
        self
    }
}

/// A product generator of one and another
#[derive(Clone)]
pub struct And<A, B> {
    gen_a: A,
    gen_b: B,
}

impl<A, B, T, U> Generator for And<A, B>
where
    A: Generator<Item = T>,
    B: Generator<Item = U>,
{
    type Item = (T, U);
    fn generate(&self, r: &mut R) -> Self::Item {
        let a = self.gen_a.generate(&mut r.sub());
        let b = self.gen_b.generate(&mut r.sub());
        (a, b)
    }
}

/// An alternative generator between one or another
#[derive(Clone)]
pub struct Or<A, B> {
    gen_a: A,
    gen_b: B,
}

impl<A, B, T> Generator for Or<A, B>
where
    A: Generator<Item = T>,
    B: Generator<Item = T>,
{
    type Item = T;
    fn generate(&self, r: &mut R) -> Self::Item {
        if r.bool() {
            self.gen_a.generate(&mut r.sub())
        } else {
            self.gen_b.generate(&mut r.sub())
        }
    }
}

/// Application of a closure on the generated value
#[derive(Clone)]
pub struct Map<G, F> {
    generator: G,
    f: F,
}

impl<O, G: Generator, F> Generator for Map<G, F>
where
    F: Fn(G::Item) -> O + Clone,
{
    type Item = O;
    fn generate(&self, r: &mut R) -> O {
        let x = self.generator.generate(r);
        (self.f)(x)
    }
}

/// Generator filtering mechanisms, such that the resulting generator,
/// generate Item elements where the predicate is valid only.
#[derive(Clone)]
pub struct SuchThat<G, F> {
    retry: u32,
    generator: G,
    f: F,
}

#[derive(Debug, Clone, Copy)]
pub(crate) struct SuchThatRetryFailure;

impl<G: Generator, F> Generator for SuchThat<G, F>
where
    F: Fn(&G::Item) -> bool + Clone,
{
    type Item = G::Item;
    fn generate(&self, r: &mut R) -> Self::Item {
        let mut retry = self.retry;
        loop {
            let x = self.generator.generate(r);
            if (self.f)(&x) {
                break x;
            }
            if retry == 0 {
                std::panic::panic_any(SuchThatRetryFailure);
            } else {
                retry -= 1;
            }
        }
    }
}

/// Constant generator, always return the same value
#[derive(Clone)]
pub struct Constant<T>(T);

impl<T: Clone> Generator for Constant<T> {
    type Item = T;
    fn generate(&self, _: &mut R) -> Self::Item {
        self.0.clone()
    }
}

/// The constant generator: always yield the same value
pub fn constant<T: Clone>(t: T) -> Constant<T> {
    Constant(t)
}