use std::marker::PhantomData;
use quickcheck::Arbitrary;
use crate::strategy::{GenError, Runner, Strategy, ValueTree};
const DEFAULT_GEN_SIZE: usize = 100;
#[must_use]
pub fn arbitrary<T: Arbitrary>() -> ArbitraryStrategy<T> {
ArbitraryStrategy {
size: DEFAULT_GEN_SIZE,
_marker: PhantomData,
}
}
#[derive(Debug, Clone, Copy)]
pub struct ArbitraryStrategy<T> {
size: usize,
_marker: PhantomData<fn() -> T>,
}
impl<T: Arbitrary> Strategy<T> for ArbitraryStrategy<T> {
type Tree = QuickcheckTree<T>;
fn new_tree(&self, _runner: &mut Runner) -> Result<Self::Tree, GenError> {
let mut generator = quickcheck::Gen::new(self.size);
let value = T::arbitrary(&mut generator);
Ok(QuickcheckTree::new(value))
}
}
pub struct QuickcheckTree<T> {
accepted: T,
siblings: Box<dyn Iterator<Item = T>>,
candidate: Option<T>,
}
impl<T: Arbitrary> QuickcheckTree<T> {
fn new(value: T) -> Self {
let siblings = value.shrink();
Self {
accepted: value,
siblings,
candidate: None,
}
}
}
impl<T: Arbitrary> ValueTree<T> for QuickcheckTree<T> {
fn current(&self) -> T {
self.candidate
.clone()
.unwrap_or_else(|| self.accepted.clone())
}
fn simplify(&mut self) -> bool {
if let Some(accepted) = self.candidate.take() {
self.siblings = accepted.shrink();
self.accepted = accepted;
}
match self.siblings.next() {
Some(next) => {
self.candidate = Some(next);
true
}
None => false,
}
}
fn complicate(&mut self) -> bool {
match self.siblings.next() {
Some(next) => {
self.candidate = Some(next);
true
}
None => {
self.candidate = None;
false
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use test_better_core::{OrFail, TestResult};
use test_better_matchers::{check, eq, is_true, le};
#[test]
fn an_arbitrary_type_is_a_seam_strategy() -> TestResult {
let mut runner = Runner::deterministic();
let tree = arbitrary::<u32>().new_tree(&mut runner).or_fail()?;
check!(tree.current()).satisfies(le(u32::MAX))
}
#[test]
fn simplify_walks_quickcheck_shrink_toward_zero() -> TestResult {
let mut tree = QuickcheckTree::new(500u32);
let start = tree.current();
while tree.simplify() {}
check!(tree.current() <= start).satisfies(is_true())
}
#[test]
fn complicate_advances_to_the_next_sibling_candidate() -> TestResult {
let mut tree = QuickcheckTree::new(8u32);
check!(tree.simplify()).satisfies(is_true())?;
let first = tree.current();
if tree.complicate() {
check!(tree.current() != first).satisfies(is_true())?;
}
check!(tree.accepted).satisfies(eq(8u32))
}
}