use crate::std_facade::{fmt, Arc, Box, Rc};
use core::cmp;
use crate::strategy::*;
use crate::test_runner::*;
pub type NewTree<S> = Result<<S as Strategy>::Tree, Reason>;
#[must_use = "strategies do nothing unless used"]
pub trait Strategy: fmt::Debug {
type Tree: ValueTree<Value = Self::Value>;
type Value: fmt::Debug;
fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self>;
fn prop_map<O: fmt::Debug, F: Fn(Self::Value) -> 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,
Self::Value: Into<O>,
{
MapInto::new(self)
}
fn prop_perturb<O: fmt::Debug, F: Fn(Self::Value, TestRng) -> 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(Self::Value) -> 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(Self::Value) -> 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(Self::Value) -> 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(&Self::Value) -> bool>(
self,
whence: R,
fun: F,
) -> Filter<Self, F>
where
Self: Sized,
{
Filter::new(self, whence.into(), fun)
}
fn prop_filter_map<F: Fn(Self::Value) -> Option<O>, O: fmt::Debug>(
self,
whence: impl Into<Reason>,
fun: F,
) -> FilterMap<Self, F>
where
Self: Sized,
{
FilterMap::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: Strategy<Value = Self::Value> + 'static,
F: Fn(BoxedStrategy<Self::Value>) -> R,
>(
self,
depth: u32,
desired_size: u32,
expected_branch_size: u32,
recurse: F,
) -> Recursive<Self::Value, F>
where
Self: Sized + 'static,
{
Recursive::new(self, depth, desired_size, expected_branch_size, recurse)
}
fn prop_shuffle(self) -> Shuffle<Self>
where
Self: Sized,
Self::Value: Shuffleable,
{
Shuffle(self)
}
fn boxed(self) -> BoxedStrategy<Self::Value>
where
Self: Sized + 'static,
{
BoxedStrategy(Arc::new(BoxedStrategyWrapper(self)))
}
fn sboxed(self) -> SBoxedStrategy<Self::Value>
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)]
#[must_use = "strategies do nothing unless used"]
pub struct NoShrink<T>(T);
impl<T: Strategy> Strategy for NoShrink<T> {
type Tree = NoShrink<T::Tree>;
type Value = T::Value;
fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
self.0.new_tree(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 Tree = S::Tree;
type Value = S::Value;
fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
(**self).new_tree(runner)
}
}
};
}
proxy_strategy!(Box<S>);
proxy_strategy!(&'a S, 'a);
proxy_strategy!(&'a mut S, 'a);
proxy_strategy!(Rc<S>);
proxy_strategy!(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<dyn ValueTree<Value = T>>;
#[derive(Debug)]
#[must_use = "strategies do nothing unless used"]
pub struct BoxedStrategy<T>(Arc<dyn Strategy<Value = T, Tree = BoxedVT<T>>>);
#[derive(Debug)]
#[must_use = "strategies do nothing unless used"]
pub struct SBoxedStrategy<T>(
Arc<dyn Strategy<Value = T, Tree = 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 Tree = BoxedVT<T>;
type Value = T;
fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
self.0.new_tree(runner)
}
fn boxed(self) -> BoxedStrategy<Self::Value>
where
Self: Sized + 'static,
{
self
}
}
impl<T: fmt::Debug> Strategy for SBoxedStrategy<T> {
type Tree = BoxedVT<T>;
type Value = T;
fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
self.0.new_tree(runner)
}
fn sboxed(self) -> SBoxedStrategy<Self::Value>
where
Self: Sized + Send + Sync + 'static,
{
self
}
fn boxed(self) -> BoxedStrategy<Self::Value>
where
Self: Sized + 'static,
{
BoxedStrategy(self.0)
}
}
#[derive(Debug)]
struct BoxedStrategyWrapper<T>(T);
impl<T: Strategy> Strategy for BoxedStrategyWrapper<T>
where
T::Tree: 'static,
{
type Tree = Box<dyn ValueTree<Value = T::Value>>;
type Value = T::Value;
fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
Ok(Box::new(self.0.new_tree(runner)?))
}
}
#[derive(Clone, Copy, Debug)]
pub struct CheckStrategySanityOptions {
pub strict_complicate_after_simplify: bool,
pub error_on_local_rejects: bool,
#[allow(missing_docs)]
#[doc(hidden)]
pub _non_exhaustive: (),
}
impl Default for CheckStrategySanityOptions {
fn default() -> Self {
CheckStrategySanityOptions {
strict_complicate_after_simplify: true,
error_on_local_rejects: false,
_non_exhaustive: (),
}
}
}
pub fn check_strategy_sanity<S: Strategy>(
strategy: S,
options: Option<CheckStrategySanityOptions>,
) where
S::Tree: Clone + fmt::Debug,
S::Value: 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 config = Config::default();
if options.error_on_local_rejects {
config.max_local_rejects = 0;
}
let mut runner = TestRunner::new(config);
for _ in 0..1024 {
let mut gen_tries = 0;
let mut state;
loop {
let err = match strategy.new_tree(&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
);
}
}
}
}