|  | //- | 
|  | // Copyright 2017 Jason Lingle | 
|  | // | 
|  | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | 
|  | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | 
|  | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | 
|  | // option. This file may not be copied, modified, or distributed | 
|  | // except according to those terms. | 
|  |  | 
|  | //! Strategies for generating `std::Option` values. | 
|  |  | 
|  | #![cfg_attr(feature="cargo-clippy", allow(expl_impl_clone_on_copy))] | 
|  |  | 
|  | use core::fmt; | 
|  | use core::marker::PhantomData; | 
|  |  | 
|  | use crate::strategy::*; | 
|  | use crate::test_runner::*; | 
|  |  | 
|  | //============================================================================== | 
|  | // Probability | 
|  | //============================================================================== | 
|  |  | 
|  | /// Creates a `Probability` from some value that is convertible into it. | 
|  | /// | 
|  | /// # Panics | 
|  | /// | 
|  | /// Panics if the converted to probability would lie | 
|  | /// outside interval `[0.0, 1.0]`. Consult the `Into` (or `From`) | 
|  | /// implementations for more details. | 
|  | pub fn prob(from: impl Into<Probability>) -> Probability { | 
|  | from.into() | 
|  | } | 
|  |  | 
|  | impl Default for Probability { | 
|  | /// The default probability is 0.5, or 50% chance. | 
|  | fn default() -> Self { prob(0.5) } | 
|  | } | 
|  |  | 
|  | impl From<f64> for Probability { | 
|  | /// Creates a `Probability` from a `f64`. | 
|  | /// | 
|  | /// # Panics | 
|  | /// | 
|  | /// Panics if the probability is outside interval `[0.0, 1.0]`. | 
|  | fn from(prob: f64) -> Self { | 
|  | Probability::new(prob) | 
|  | } | 
|  | } | 
|  |  | 
|  | impl Probability { | 
|  | /// Creates a `Probability` from a `f64`. | 
|  | /// | 
|  | /// # Panics | 
|  | /// | 
|  | /// Panics if the probability is outside interval `[0.0, 1.0]`. | 
|  | pub fn new(prob: f64) -> Self { | 
|  | assert!(prob >= 0.0 && prob <= 1.0); | 
|  | Probability(prob) | 
|  | } | 
|  |  | 
|  | // Don't rely on these existing internally: | 
|  |  | 
|  | /// Merges self together with some other argument producing a product | 
|  | /// type expected by some impelementations of `A: Arbitrary` in | 
|  | /// `A::Parameters`. This can be more ergonomic to work with and may | 
|  | /// help type inference. | 
|  | pub fn with<X>(self, and: X) -> product_type![Self, X] { | 
|  | product_pack![self, and] | 
|  | } | 
|  |  | 
|  | /// Merges self together with some other argument generated with a | 
|  | /// default value producing a product type expected by some | 
|  | /// impelementations of `A: Arbitrary` in `A::Parameters`. | 
|  | /// This can be more ergonomic to work with and may help type inference. | 
|  | 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; | 
|  |  | 
|  | /// Converts the `Probability` into an `f64`. | 
|  | fn into(self) -> Self::Repr { self.0 } | 
|  |  | 
|  | /// Creates a `Probability` from a `f64`. | 
|  | /// | 
|  | /// # Panics | 
|  | /// | 
|  | /// Panics if the probability is outside interval `[0.0, 1.0]`. | 
|  | fn from(r: Self::Repr) -> Self { r.into() } | 
|  | } | 
|  |  | 
|  | impl From<Probability> for f64 { | 
|  | fn from(p: Probability) -> Self { p.0 } | 
|  | } | 
|  |  | 
|  | /// A probability in the range `[0.0, 1.0]` with a default of `0.5`. | 
|  | #[derive(Clone, Copy, PartialEq, Debug)] | 
|  | pub struct Probability(f64); | 
|  |  | 
|  | //============================================================================== | 
|  | // Strategies for Option | 
|  | //============================================================================== | 
|  |  | 
|  | 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! { | 
|  | /// Strategy which generates `Option` values whose inner `Some` values are | 
|  | /// generated by another strategy. | 
|  | /// | 
|  | /// Constructed by other functions in this module. | 
|  | #[derive(Clone)] | 
|  | pub struct OptionStrategy[<T>][where T : Strategy] | 
|  | (TupleUnion<(W<NoneStrategy<T::Value>>, | 
|  | W<statics::Map<T, WrapSome>>)>) | 
|  | -> OptionValueTree<T::Tree>; | 
|  | /// `ValueTree` type corresponding to `OptionStrategy`. | 
|  | #[derive(Clone, Debug)] | 
|  | pub struct OptionValueTree[<T>][where T : ValueTree] | 
|  | (TupleUnionValueTree<(NoneStrategy<T::Value>, | 
|  | Option<statics::Map<T, WrapSome>>)>) | 
|  | -> Option<T::Value>; | 
|  | } | 
|  |  | 
|  | // XXX Unclear why this is necessary; #[derive(Debug)] *should* generate | 
|  | // exactly this, but for some reason it adds a `T::Value : Debug` constraint as | 
|  | // well. | 
|  | impl<T : Strategy + fmt::Debug> fmt::Debug for OptionStrategy<T> { | 
|  | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | 
|  | write!(f, "OptionStrategy({:?})", self.0) | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Return a strategy producing `Optional` values wrapping values from the | 
|  | /// given delegate strategy. | 
|  | /// | 
|  | /// `Some` values shrink to `None`. | 
|  | /// | 
|  | /// `Some` and `None` are each chosen with 50% probability. | 
|  | pub fn of<T : Strategy>(t: T) -> OptionStrategy<T> { | 
|  | weighted(Probability::default(), t) | 
|  | } | 
|  |  | 
|  | /// Return a strategy producing `Optional` values wrapping values from the | 
|  | /// given delegate strategy. | 
|  | /// | 
|  | /// `Some` values shrink to `None`. | 
|  | /// | 
|  | /// `Some` is chosen with a probability given by `probability_of_some`, which | 
|  | /// must be between 0.0 and 1.0, both exclusive. | 
|  | 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, NoneStrategy(PhantomData)), | 
|  | (weight_some, 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); | 
|  | } | 
|  | } |