summaryrefslogtreecommitdiffstats
path: root/vendor/proptest/src/option.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/proptest/src/option.rs')
-rw-r--r--vendor/proptest/src/option.rs271
1 files changed, 271 insertions, 0 deletions
diff --git a/vendor/proptest/src/option.rs b/vendor/proptest/src/option.rs
new file mode 100644
index 000000000..466ada687
--- /dev/null
+++ b/vendor/proptest/src/option.rs
@@ -0,0 +1,271 @@
+//-
+// 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::std_facade::Arc;
+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 implementations 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
+ /// implementations 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<(WA<NoneStrategy<T::Value>>,
+ WA<statics::Map<T, WrapSome>>)>)
+ -> OptionValueTree<T>;
+ /// `ValueTree` type corresponding to `OptionStrategy`.
+ pub struct OptionValueTree[<T>][where T : Strategy]
+ (TupleUnionValueTree<(
+ LazyValueTree<NoneStrategy<T::Value>>,
+ Option<LazyValueTree<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)
+ }
+}
+
+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)
+ }
+}
+
+/// 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, 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);
+ }
+}