#![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 fn prob(from: impl Into<Probability>) -> Probability {
from.into()
}
impl Default for Probability {
fn default() -> Self {
prob(0.5)
}
}
impl From<f64> for Probability {
fn from(prob: f64) -> Self {
Probability::new(prob)
}
}
impl Probability {
pub fn new(prob: f64) -> Self {
assert!(prob >= 0.0 && prob <= 1.0);
Probability(prob)
}
pub fn with<X>(self, and: X) -> product_type![Self, X] {
product_pack![self, and]
}
pub fn lift<X: Default>(self) -> product_type![Self, X] {
self.with(Default::default())
}
}
#[cfg(feature = "frunk")]
use frunk_core::generic::Generic;
#[cfg(feature = "frunk")]
impl Generic for Probability {
type Repr = f64;
fn into(self) -> Self::Repr {
self.0
}
fn from(r: Self::Repr) -> Self {
r.into()
}
}
impl From<Probability> for f64 {
fn from(p: Probability) -> Self {
p.0
}
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct Probability(f64);
mapfn! {
[] fn WrapSome[<T : fmt::Debug>](t: T) -> Option<T> {
Some(t)
}
}
#[must_use = "strategies do nothing unless used"]
struct NoneStrategy<T>(PhantomData<T>);
impl<T> Clone for NoneStrategy<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for NoneStrategy<T> {}
impl<T> fmt::Debug for NoneStrategy<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "NoneStrategy")
}
}
impl<T: fmt::Debug> Strategy for NoneStrategy<T> {
type Tree = Self;
type Value = Option<T>;
fn new_tree(&self, _: &mut TestRunner) -> NewTree<Self> {
Ok(*self)
}
}
impl<T: fmt::Debug> ValueTree for NoneStrategy<T> {
type Value = Option<T>;
fn current(&self) -> Option<T> {
None
}
fn simplify(&mut self) -> bool {
false
}
fn complicate(&mut self) -> bool {
false
}
}
opaque_strategy_wrapper! {
#[derive(Clone)]
pub struct OptionStrategy[<T>][where T : Strategy]
(TupleUnion<(WA<NoneStrategy<T::Value>>,
WA<statics::Map<T, WrapSome>>)>)
-> OptionValueTree<T>;
pub struct OptionValueTree[<T>][where T : Strategy]
(TupleUnionValueTree<(
LazyValueTree<NoneStrategy<T::Value>>,
Option<LazyValueTree<statics::Map<T, WrapSome>>>,
)>)
-> Option<T::Value>;
}
impl<T: Strategy + fmt::Debug> fmt::Debug for OptionStrategy<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "OptionStrategy({:?})", self.0)
}
}
impl<T: Strategy> Clone for OptionValueTree<T>
where
T::Tree: Clone,
{
fn clone(&self) -> Self {
OptionValueTree(self.0.clone())
}
}
impl<T: Strategy> fmt::Debug for OptionValueTree<T>
where
T::Tree: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "OptionValueTree({:?})", self.0)
}
}
pub fn of<T: Strategy>(t: T) -> OptionStrategy<T> {
weighted(Probability::default(), t)
}
pub fn weighted<T: Strategy>(
probability_of_some: impl Into<Probability>,
t: T,
) -> OptionStrategy<T> {
let prob = probability_of_some.into().into();
let (weight_some, weight_none) = float_to_weight(prob);
OptionStrategy(TupleUnion::new((
(weight_none, Arc::new(NoneStrategy(PhantomData))),
(weight_some, Arc::new(statics::Map::new(t, WrapSome))),
)))
}
#[cfg(test)]
mod test {
use super::*;
fn count_some_of_1000(s: OptionStrategy<Just<i32>>) -> u32 {
let mut runner = TestRunner::deterministic();
let mut count = 0;
for _ in 0..1000 {
count +=
s.new_tree(&mut runner).unwrap().current().is_some() as u32;
}
count
}
#[test]
fn probability_defaults_to_0p5() {
let count = count_some_of_1000(of(Just(42i32)));
assert!(count > 450 && count < 550);
}
#[test]
fn probability_handled_correctly() {
let count = count_some_of_1000(weighted(0.9, Just(42i32)));
assert!(count > 800 && count < 950);
let count = count_some_of_1000(weighted(0.1, Just(42i32)));
assert!(count > 50 && count < 150);
}
#[test]
fn test_sanity() {
check_strategy_sanity(of(0i32..1000i32), None);
}
}