#![cfg_attr(feature = "cargo-clippy", allow(expl_impl_clone_on_copy))]
use core::fmt;
use core::marker::PhantomData;
use crate::std_facade::Arc;
use crate::strategy::*;
use crate::test_runner::*;
pub use crate::option::{prob, Probability};
struct WrapOk<T, E>(PhantomData<T>, PhantomData<E>);
impl<T, E> Clone for WrapOk<T, E> {
fn clone(&self) -> Self {
*self
}
}
impl<T, E> Copy for WrapOk<T, E> {}
impl<T, E> fmt::Debug for WrapOk<T, E> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "WrapOk")
}
}
impl<T: fmt::Debug, E: fmt::Debug> statics::MapFn<T> for WrapOk<T, E> {
type Output = Result<T, E>;
fn apply(&self, t: T) -> Result<T, E> {
Ok(t)
}
}
struct WrapErr<T, E>(PhantomData<T>, PhantomData<E>);
impl<T, E> Clone for WrapErr<T, E> {
fn clone(&self) -> Self {
*self
}
}
impl<T, E> Copy for WrapErr<T, E> {}
impl<T, E> fmt::Debug for WrapErr<T, E> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "WrapErr")
}
}
impl<T: fmt::Debug, E: fmt::Debug> statics::MapFn<E> for WrapErr<T, E> {
type Output = Result<T, E>;
fn apply(&self, e: E) -> Result<T, E> {
Err(e)
}
}
type MapErr<T, E> =
statics::Map<E, WrapErr<<T as Strategy>::Value, <E as Strategy>::Value>>;
type MapOk<T, E> =
statics::Map<T, WrapOk<<T as Strategy>::Value, <E as Strategy>::Value>>;
opaque_strategy_wrapper! {
#[derive(Clone)]
pub struct MaybeOk[<T, E>][where T : Strategy, E : Strategy]
(TupleUnion<(WA<MapErr<T, E>>, WA<MapOk<T, E>>)>)
-> MaybeOkValueTree<T, E>;
pub struct MaybeOkValueTree[<T, E>][where T : Strategy, E : Strategy]
(TupleUnionValueTree<(
LazyValueTree<statics::Map<E, WrapErr<T::Value, E::Value>>>,
Option<LazyValueTree<statics::Map<T, WrapOk<T::Value, E::Value>>>>,
)>)
-> Result<T::Value, E::Value>;
}
opaque_strategy_wrapper! {
#[derive(Clone)]
pub struct MaybeErr[<T, E>][where T : Strategy, E : Strategy]
(TupleUnion<(WA<MapOk<T, E>>, WA<MapErr<T, E>>)>)
-> MaybeErrValueTree<T, E>;
pub struct MaybeErrValueTree[<T, E>][where T : Strategy, E : Strategy]
(TupleUnionValueTree<(
LazyValueTree<statics::Map<T, WrapOk<T::Value, E::Value>>>,
Option<LazyValueTree<statics::Map<E, WrapErr<T::Value, E::Value>>>>,
)>)
-> Result<T::Value, E::Value>;
}
impl<T: Strategy + fmt::Debug, E: Strategy + fmt::Debug> fmt::Debug
for MaybeOk<T, E>
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "MaybeOk({:?})", self.0)
}
}
impl<T: Strategy + fmt::Debug, E: Strategy + fmt::Debug> fmt::Debug
for MaybeErr<T, E>
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "MaybeErr({:?})", self.0)
}
}
impl<T: Strategy, E: Strategy> Clone for MaybeOkValueTree<T, E>
where
T::Tree: Clone,
E::Tree: Clone,
{
fn clone(&self) -> Self {
MaybeOkValueTree(self.0.clone())
}
}
impl<T: Strategy, E: Strategy> fmt::Debug for MaybeOkValueTree<T, E>
where
T::Tree: fmt::Debug,
E::Tree: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "MaybeOkValueTree({:?})", self.0)
}
}
impl<T: Strategy, E: Strategy> Clone for MaybeErrValueTree<T, E>
where
T::Tree: Clone,
E::Tree: Clone,
{
fn clone(&self) -> Self {
MaybeErrValueTree(self.0.clone())
}
}
impl<T: Strategy, E: Strategy> fmt::Debug for MaybeErrValueTree<T, E>
where
T::Tree: fmt::Debug,
E::Tree: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "MaybeErrValueTree({:?})", self.0)
}
}
pub fn maybe_ok<T: Strategy, E: Strategy>(t: T, e: E) -> MaybeOk<T, E> {
maybe_ok_weighted(0.5, t, e)
}
pub fn maybe_ok_weighted<T: Strategy, E: Strategy>(
probability_of_ok: impl Into<Probability>,
t: T,
e: E,
) -> MaybeOk<T, E> {
let prob = probability_of_ok.into().into();
let (ok_weight, err_weight) = float_to_weight(prob);
MaybeOk(TupleUnion::new((
(
err_weight,
Arc::new(statics::Map::new(e, WrapErr(PhantomData, PhantomData))),
),
(
ok_weight,
Arc::new(statics::Map::new(t, WrapOk(PhantomData, PhantomData))),
),
)))
}
pub fn maybe_err<T: Strategy, E: Strategy>(t: T, e: E) -> MaybeErr<T, E> {
maybe_err_weighted(0.5, t, e)
}
pub fn maybe_err_weighted<T: Strategy, E: Strategy>(
probability_of_err: impl Into<Probability>,
t: T,
e: E,
) -> MaybeErr<T, E> {
let prob = probability_of_err.into().into();
let (err_weight, ok_weight) = float_to_weight(prob);
MaybeErr(TupleUnion::new((
(
ok_weight,
Arc::new(statics::Map::new(t, WrapOk(PhantomData, PhantomData))),
),
(
err_weight,
Arc::new(statics::Map::new(e, WrapErr(PhantomData, PhantomData))),
),
)))
}
#[cfg(test)]
mod test {
use super::*;
fn count_ok_of_1000(s: impl Strategy<Value = Result<(), ()>>) -> u32 {
let mut runner = TestRunner::deterministic();
let mut count = 0;
for _ in 0..1000 {
count += s.new_tree(&mut runner).unwrap().current().is_ok() as u32;
}
count
}
#[test]
fn probability_defaults_to_0p5() {
let count = count_ok_of_1000(maybe_err(Just(()), Just(())));
assert!(count > 400 && count < 600);
let count = count_ok_of_1000(maybe_ok(Just(()), Just(())));
assert!(count > 400 && count < 600);
}
#[test]
fn probability_handled_correctly() {
let count =
count_ok_of_1000(maybe_err_weighted(0.1, Just(()), Just(())));
assert!(count > 800 && count < 950);
let count =
count_ok_of_1000(maybe_err_weighted(0.9, Just(()), Just(())));
assert!(count > 50 && count < 150);
let count =
count_ok_of_1000(maybe_ok_weighted(0.9, Just(()), Just(())));
assert!(count > 800 && count < 950);
let count =
count_ok_of_1000(maybe_ok_weighted(0.1, Just(()), Just(())));
assert!(count > 50 && count < 150);
}
#[test]
fn shrink_to_correct_case() {
let mut runner = TestRunner::default();
{
let input = maybe_err(Just(()), Just(()));
for _ in 0..64 {
let mut val = input.new_tree(&mut runner).unwrap();
if val.current().is_ok() {
assert!(!val.simplify());
assert!(val.current().is_ok());
} else {
assert!(val.simplify());
assert!(val.current().is_ok());
}
}
}
{
let input = maybe_ok(Just(()), Just(()));
for _ in 0..64 {
let mut val = input.new_tree(&mut runner).unwrap();
if val.current().is_err() {
assert!(!val.simplify());
assert!(val.current().is_err());
} else {
assert!(val.simplify());
assert!(val.current().is_err());
}
}
}
}
#[test]
fn test_sanity() {
check_strategy_sanity(maybe_ok(0i32..100i32, 0i32..100i32), None);
check_strategy_sanity(maybe_err(0i32..100i32, 0i32..100i32), None);
}
}