use core::cmp;
use core::fmt;
#[cfg(all(feature = "alloc", not(feature="std")))]
use alloc::arc::Arc;
#[cfg(feature = "std")]
use std::sync::Arc;
#[cfg(all(feature = "alloc", not(feature="std")))]
use alloc::boxed::Box;
#[cfg(feature = "std")]
use std::boxed::Box;
use rand::XorShiftRng;
use strategy::*;
use test_runner::*;
pub type NewTree<S> = Result<<S as Strategy>::Value, Reason>;
pub type ValueFor<S> = <<S as Strategy>::Value as ValueTree>::Value;
pub trait Strategy : fmt::Debug {
type Value : ValueTree;
fn new_value(&self, runner: &mut TestRunner) -> NewTree<Self>;
fn prop_map<O : fmt::Debug,
F : Fn (ValueFor<Self>) -> O>
(self, fun: F) -> Map<Self, F>
where Self : Sized {
Map { source: self, fun: Arc::new(fun) }
}
fn prop_map_into<O : fmt::Debug>(self) -> MapInto<Self, O>
where
Self : Sized,
ValueFor<Self>: Into<O>
{
MapInto::new(self)
}
fn prop_perturb<O : fmt::Debug,
F : Fn (ValueFor<Self>, XorShiftRng) -> O>
(self, fun: F) -> Perturb<Self, F>
where Self : Sized {
Perturb { source: self, fun: Arc::new(fun) }
}
fn prop_flat_map<S : Strategy,
F : Fn (ValueFor<Self>) -> S>
(self, fun: F) -> Flatten<Map<Self, F>>
where Self : Sized {
Flatten::new(Map { source: self, fun: Arc::new(fun) })
}
fn prop_ind_flat_map<S : Strategy,
F : Fn (ValueFor<Self>) -> S>
(self, fun: F) -> IndFlatten<Map<Self, F>>
where Self : Sized {
IndFlatten(Map { source: self, fun: Arc::new(fun) })
}
fn prop_ind_flat_map2<S : Strategy,
F : Fn (ValueFor<Self>) -> S>
(self, fun: F) -> IndFlattenMap<Self, F>
where Self : Sized {
IndFlattenMap { source: self, fun: Arc::new(fun) }
}
fn prop_filter<R: Into<Reason>, F : Fn (&ValueFor<Self>) -> bool>
(self, whence: R, fun: F) -> Filter<Self, F>
where Self : Sized {
Filter::new(self, whence.into(), fun)
}
fn prop_union(self, other: Self) -> Union<Self>
where Self : Sized {
Union::new(vec![self, other])
}
fn prop_recursive<R, F>
(self, depth: u32, desired_size: u32, expected_branch_size: u32, recurse: F)
-> Recursive<ValueFor<Self>, F>
where
Self : Sized + 'static,
F : Fn(BoxedStrategy<ValueFor<Self>>) -> R,
R : Strategy + 'static,
R::Value : ValueTree<Value = ValueFor<Self>>
{
Recursive::new(self, depth, desired_size, expected_branch_size, recurse)
}
fn prop_shuffle(self) -> Shuffle<Self>
where Self : Sized, ValueFor<Self> : Shuffleable {
Shuffle(self)
}
fn boxed(self) -> BoxedStrategy<ValueFor<Self>>
where Self : Sized + 'static {
BoxedStrategy(Arc::new(BoxedStrategyWrapper(self)))
}
fn sboxed(self) -> SBoxedStrategy<ValueFor<Self>>
where Self : Sized + Send + Sync + 'static {
SBoxedStrategy(Arc::new(BoxedStrategyWrapper(self)))
}
fn no_shrink(self) -> NoShrink<Self> where Self : Sized {
NoShrink(self)
}
}
pub trait ValueTree {
type Value : fmt::Debug;
fn current(&self) -> Self::Value;
fn simplify(&mut self) -> bool;
fn complicate(&mut self) -> bool;
}
#[derive(Clone, Copy, Debug)]
pub struct NoShrink<T>(T);
impl<T : Strategy> Strategy for NoShrink<T> {
type Value = NoShrink<T::Value>;
fn new_value(&self, runner: &mut TestRunner) -> NewTree<Self> {
self.0.new_value(runner).map(NoShrink)
}
}
impl<T : ValueTree> ValueTree for NoShrink<T> {
type Value = T::Value;
fn current(&self) -> T::Value {
self.0.current()
}
fn simplify(&mut self) -> bool { false }
fn complicate(&mut self) -> bool { false }
}
macro_rules! proxy_strategy {
($typ:ty $(, $lt:tt)*) => {
impl<$($lt,)* S : Strategy + ?Sized> Strategy for $typ {
type Value = S::Value;
fn new_value(&self, runner: &mut TestRunner) -> NewTree<Self> {
(**self).new_value(runner)
}
}
};
}
proxy_strategy!(Box<S>);
proxy_strategy!(&'a S, 'a);
proxy_strategy!(&'a mut S, 'a);
#[cfg(all(feature = "alloc", not(feature="std")))]
proxy_strategy!(::alloc::rc::Rc<S>);
#[cfg(feature = "std")]
proxy_strategy!(::std::rc::Rc<S>);
#[cfg(all(feature = "alloc", not(feature="std")))]
proxy_strategy!(::alloc::arc::Arc<S>);
#[cfg(feature = "std")]
proxy_strategy!(::std::sync::Arc<S>);
impl<T : ValueTree + ?Sized> ValueTree for Box<T> {
type Value = T::Value;
fn current(&self) -> Self::Value { (**self).current() }
fn simplify(&mut self) -> bool { (**self).simplify() }
fn complicate(&mut self) -> bool { (**self).complicate() }
}
type BoxedVT<T> = Box<ValueTree<Value = T>>;
#[derive(Debug)]
pub struct BoxedStrategy<T>(Arc<Strategy<Value = BoxedVT<T>>>);
#[derive(Debug)]
pub struct SBoxedStrategy<T>(Arc<Strategy<Value = BoxedVT<T>> + Sync + Send>);
impl<T> Clone for BoxedStrategy<T> {
fn clone(&self) -> Self {
BoxedStrategy(Arc::clone(&self.0))
}
}
impl<T> Clone for SBoxedStrategy<T> {
fn clone(&self) -> Self {
SBoxedStrategy(Arc::clone(&self.0))
}
}
impl<T: fmt::Debug> Strategy for BoxedStrategy<T> {
type Value = BoxedVT<T>;
fn new_value(&self, runner: &mut TestRunner) -> NewTree<Self> {
self.0.new_value(runner)
}
fn boxed(self) -> BoxedStrategy<ValueFor<Self>>
where Self : Sized + 'static {
self
}
}
impl<T: fmt::Debug> Strategy for SBoxedStrategy<T> {
type Value = BoxedVT<T>;
fn new_value(&self, runner: &mut TestRunner) -> NewTree<Self> {
self.0.new_value(runner)
}
fn sboxed(self) -> SBoxedStrategy<ValueFor<Self>>
where Self : Sized + Send + Sync + 'static {
self
}
fn boxed(self) -> BoxedStrategy<ValueFor<Self>>
where Self : Sized + 'static {
BoxedStrategy(self.0)
}
}
#[derive(Debug)]
struct BoxedStrategyWrapper<T>(T);
impl<T : Strategy> Strategy for BoxedStrategyWrapper<T>
where T::Value : 'static {
type Value = Box<ValueTree<Value = ValueFor<T>>>;
fn new_value(&self, runner: &mut TestRunner) -> NewTree<Self> {
Ok(Box::new(self.0.new_value(runner)?))
}
}
#[derive(Clone, Copy, Debug)]
pub struct CheckStrategySanityOptions {
pub strict_complicate_after_simplify: bool,
#[allow(missing_docs)]
#[doc(hidden)]
pub _non_exhaustive: (),
}
impl Default for CheckStrategySanityOptions {
fn default() -> Self {
CheckStrategySanityOptions {
strict_complicate_after_simplify: true,
_non_exhaustive: (),
}
}
}
pub fn check_strategy_sanity<S : Strategy>(
strategy: S, options: Option<CheckStrategySanityOptions>)
where S::Value : Clone + fmt::Debug, ValueFor<S> : cmp::PartialEq {
macro_rules! assert_same {
($a:expr, $b:expr, $($stuff:tt)*) => { {
let a = $a;
let b = $b;
if a == a || b == b {
assert_eq!(a, b, $($stuff)*);
}
} }
}
let options = options.unwrap_or_else(CheckStrategySanityOptions::default);
let mut runner = TestRunner::default();
for _ in 0..1024 {
let mut gen_tries = 0;
let mut state;
loop {
let err = match strategy.new_value(&mut runner) {
Ok(s) => { state = s; break; },
Err(e) => e,
};
gen_tries += 1;
if gen_tries > 100 {
panic!("Strategy passed to check_strategy_sanity failed \
to generate a value over 100 times in a row; \
last failure reason: {}", err);
}
}
{
let mut state = state.clone();
let mut count = 0;
while state.simplify() || state.complicate() {
count += 1;
if count > 65536 {
panic!("Failed to converge on any value. State:\n{:#?}",
state);
}
}
}
let mut num_simplifies = 0;
let mut before_simplified;
loop {
before_simplified = state.clone();
if !state.simplify() {
break;
}
let mut complicated = state.clone();
let before_complicated = state.clone();
if options.strict_complicate_after_simplify {
assert!(complicated.complicate(),
"complicate() returned false immediately after \
simplify() returned true. internal state after \
{} calls to simplify():\n\
{:#?}\n\
simplified to:\n\
{:#?}\n\
complicated to:\n\
{:#?}", num_simplifies, before_simplified, state,
complicated);
}
let mut prev_complicated = complicated.clone();
let mut num_complications = 0;
loop {
if !complicated.complicate() {
break;
}
prev_complicated = complicated.clone();
num_complications += 1;
if num_complications > 65_536 {
panic!("complicate() returned true over 65536 times in a \
row; aborting due to possible infinite loop. \
If this is not an infinite loop, it may be \
necessary to reconsider how shrinking is \
implemented or use a simpler test strategy. \
Internal state:\n{:#?}", state);
}
}
assert_same!(before_simplified.current(), complicated.current(),
"Calling simplify(), then complicate() until it \
returned false, did not return to the value before \
simplify. Expected:\n\
{:#?}\n\
Actual:\n\
{:#?}\n\
Internal state after {} calls to simplify():\n\
{:#?}\n\
Internal state after another call to simplify():\n\
{:#?}\n\
Internal state after {} subsequent calls to \
complicate():\n\
{:#?}",
before_simplified.current(), complicated.current(),
num_simplifies, before_simplified, before_complicated,
num_complications + 1, complicated);
for iter in 1..16 {
assert_same!(prev_complicated.current(), complicated.current(),
"complicate() returned false but changed the output \
value anyway.\n\
Old value:\n\
{:#?}\n\
New value:\n\
{:#?}\n\
Old internal state:\n\
{:#?}\n\
New internal state after {} calls to complicate()\
including the :\n\
{:#?}",
prev_complicated.current(),
complicated.current(),
prev_complicated, iter, complicated);
assert!(!complicated.complicate(),
"complicate() returned true after having returned \
false;\n\
Internal state before:\n{:#?}\n\
Internal state after calling complicate() {} times:\n\
{:#?}", prev_complicated, iter + 1, complicated);
}
num_simplifies += 1;
if num_simplifies > 65_536 {
panic!("simplify() returned true over 65536 times in a row, \
aborting due to possible infinite loop. If this is not \
an infinite loop, it may be necessary to reconsider \
how shrinking is implemented or use a simpler test \
strategy. Internal state:\n{:#?}", state);
}
}
for iter in 0..16 {
assert_same!(before_simplified.current(), state.current(),
"simplify() returned false but changed the output \
value anyway.\n\
Old value:\n\
{:#?}\n\
New value:\n\
{:#?}\n\
Previous internal state:\n\
{:#?}\n\
New internal state after calling simplify() {} times:\n\
{:#?}",
before_simplified.current(),
state.current(),
before_simplified, iter, state);
if state.simplify() {
panic!("simplify() returned true after having returned false. \
Previous internal state:\n\
{:#?}\n\
New internal state after calling simplify() {} times:\n\
{:#?}", before_simplified, iter + 1, state);
}
}
}
}