1//-
2// Copyright 2017 Jason Lingle
3//
4// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7// option. This file may not be copied, modified, or distributed
8// except according to those terms.
910//! Strategies for generating `std::Option` values.
1112#![cfg_attr(clippy, allow(expl_impl_clone_on_copy))]
1314use core::fmt;
15use core::marker::PhantomData;
1617use crate::std_facade::Arc;
18use crate::strategy::*;
19use crate::test_runner::*;
2021//==============================================================================
22// Probability
23//==============================================================================
2425/// Creates a `Probability` from some value that is convertible into it.
26///
27/// # Panics
28///
29/// Panics if the converted to probability would lie
30/// outside interval `[0.0, 1.0]`. Consult the `Into` (or `From`)
31/// implementations for more details.
32pub fn prob(from: impl Into<Probability>) -> Probability {
33 from.into()
34}
3536impl Default for Probability {
37/// The default probability is 0.5, or 50% chance.
38fn default() -> Self {
39 prob(0.5)
40 }
41}
4243impl Probability {
44/// Creates a `Probability` from a `f64`.
45 ///
46 /// # Panics
47 ///
48 /// Panics if the probability is outside interval `[0.0, 1.0]`.
49pub fn new(prob: f64) -> Self {
50assert!(prob >= 0.0 && prob <= 1.0);
51 Probability(prob)
52 }
5354// Don't rely on these existing internally:
5556/// Merges self together with some other argument producing a product
57 /// type expected by some implementations of `A: Arbitrary` in
58 /// `A::Parameters`. This can be more ergonomic to work with and may
59 /// help type inference.
60pub fn with<X>(self, and: X) -> product_type![Self, X] {
61product_pack![self, and]
62 }
6364/// Merges self together with some other argument generated with a
65 /// default value producing a product type expected by some
66 /// implementations of `A: Arbitrary` in `A::Parameters`.
67 /// This can be more ergonomic to work with and may help type inference.
68pub fn lift<X: Default>(self) -> product_type![Self, X] {
69self.with(Default::default())
70 }
71}
7273impl From<f64> for Probability {
74/// Creates a `Probability` from a `f64`.
75 ///
76 /// # Panics
77 ///
78 /// Panics if the probability is outside interval `[0.0, 1.0]`.
79fn from(prob: f64) -> Self {
80 Probability::new(prob)
81 }
82}
8384impl From<Probability> for f64 {
85fn from(p: Probability) -> Self {
86 p.0
87}
88}
8990/// A probability in the range `[0.0, 1.0]` with a default of `0.5`.
91#[derive(Clone, Copy, PartialEq, Debug)]
92pub struct Probability(f64);
9394//==============================================================================
95// Strategies for Option
96//==============================================================================
9798mapfn! {
99 [] fn WrapSome[<T : fmt::Debug>](t: T) -> Option<T> {
100Some(t)
101 }
102}
103104#[must_use = "strategies do nothing unless used"]
105struct NoneStrategy<T>(PhantomData<T>);
106impl<T> Clone for NoneStrategy<T> {
107fn clone(&self) -> Self {
108*self
109}
110}
111impl<T> Copy for NoneStrategy<T> {}
112impl<T> fmt::Debug for NoneStrategy<T> {
113fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
114write!(f, "NoneStrategy")
115 }
116}
117impl<T: fmt::Debug> Strategy for NoneStrategy<T> {
118type Tree = Self;
119type Value = Option<T>;
120121fn new_tree(&self, _: &mut TestRunner) -> NewTree<Self> {
122Ok(*self)
123 }
124}
125impl<T: fmt::Debug> ValueTree for NoneStrategy<T> {
126type Value = Option<T>;
127128fn current(&self) -> Option<T> {
129None
130}
131fn simplify(&mut self) -> bool {
132false
133}
134fn complicate(&mut self) -> bool {
135false
136}
137}
138139opaque_strategy_wrapper! {
140/// Strategy which generates `Option` values whose inner `Some` values are
141 /// generated by another strategy.
142 ///
143 /// Constructed by other functions in this module.
144#[derive(Clone)]
145pub struct OptionStrategy[<T>][where T : Strategy]
146 (TupleUnion<(WA<NoneStrategy<T::Value>>,
147 WA<statics::Map<T, WrapSome>>)>)
148 -> OptionValueTree<T>;
149/// `ValueTree` type corresponding to `OptionStrategy`.
150pub struct OptionValueTree[<T>][where T : Strategy]
151 (TupleUnionValueTree<(
152 LazyValueTree<NoneStrategy<T::Value>>,
153Option<LazyValueTree<statics::Map<T, WrapSome>>>,
154 )>)
155 -> Option<T::Value>;
156}
157158// XXX Unclear why this is necessary; #[derive(Debug)] *should* generate
159// exactly this, but for some reason it adds a `T::Value : Debug` constraint as
160// well.
161impl<T: Strategy + fmt::Debug> fmt::Debug for OptionStrategy<T> {
162fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
163write!(f, "OptionStrategy({:?})", self.0)
164 }
165}
166167impl<T: Strategy> Clone for OptionValueTree<T>
168where
169T::Tree: Clone,
170{
171fn clone(&self) -> Self {
172 OptionValueTree(self.0.clone())
173 }
174}
175176impl<T: Strategy> fmt::Debug for OptionValueTree<T>
177where
178T::Tree: fmt::Debug,
179{
180fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
181write!(f, "OptionValueTree({:?})", self.0)
182 }
183}
184185/// Return a strategy producing `Optional` values wrapping values from the
186/// given delegate strategy.
187///
188/// `Some` values shrink to `None`.
189///
190/// `Some` and `None` are each chosen with 50% probability.
191pub fn of<T: Strategy>(t: T) -> OptionStrategy<T> {
192 weighted(Probability::default(), t)
193}
194195/// Return a strategy producing `Optional` values wrapping values from the
196/// given delegate strategy.
197///
198/// `Some` values shrink to `None`.
199///
200/// `Some` is chosen with a probability given by `probability_of_some`, which
201/// must be between 0.0 and 1.0, both exclusive.
202pub fn weighted<T: Strategy>(
203 probability_of_some: impl Into<Probability>,
204 t: T,
205) -> OptionStrategy<T> {
206let prob = probability_of_some.into().into();
207let (weight_some, weight_none) = float_to_weight(prob);
208209 OptionStrategy(TupleUnion::new((
210 (weight_none, Arc::new(NoneStrategy(PhantomData))),
211 (weight_some, Arc::new(statics::Map::new(t, WrapSome))),
212 )))
213}
214215#[cfg(test)]
216mod test {
217use super::*;
218219fn count_some_of_1000(s: OptionStrategy<Just<i32>>) -> u32 {
220let mut runner = TestRunner::deterministic();
221let mut count = 0;
222for _ in 0..1000 {
223 count +=
224 s.new_tree(&mut runner).unwrap().current().is_some() as u32;
225 }
226227 count
228 }
229230#[test]
231fn probability_defaults_to_0p5() {
232let count = count_some_of_1000(of(Just(42i32)));
233assert!(count > 450 && count < 550);
234 }
235236#[test]
237fn probability_handled_correctly() {
238let count = count_some_of_1000(weighted(0.9, Just(42i32)));
239assert!(count > 800 && count < 950);
240241let count = count_some_of_1000(weighted(0.1, Just(42i32)));
242assert!(count > 50 && count < 150);
243 }
244245#[test]
246fn test_sanity() {
247 check_strategy_sanity(of(0i32..1000i32), None);
248 }
249}