From 9835e2ae736235810b4ea1c162ca5e65c547e770 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 18 May 2024 04:49:50 +0200 Subject: Merging upstream version 1.71.1+dfsg1. Signed-off-by: Daniel Baumann --- vendor/proptest/src/option.rs | 271 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 271 insertions(+) create mode 100644 vendor/proptest/src/option.rs (limited to 'vendor/proptest/src/option.rs') 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 or the MIT license +// , 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 { + from.into() +} + +impl Default for Probability { + /// The default probability is 0.5, or 50% chance. + fn default() -> Self { + prob(0.5) + } +} + +impl From 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(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(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 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: T) -> Option { + Some(t) + } +} + +#[must_use = "strategies do nothing unless used"] +struct NoneStrategy(PhantomData); +impl Clone for NoneStrategy { + fn clone(&self) -> Self { + *self + } +} +impl Copy for NoneStrategy {} +impl fmt::Debug for NoneStrategy { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "NoneStrategy") + } +} +impl Strategy for NoneStrategy { + type Tree = Self; + type Value = Option; + + fn new_tree(&self, _: &mut TestRunner) -> NewTree { + Ok(*self) + } +} +impl ValueTree for NoneStrategy { + type Value = Option; + + fn current(&self) -> Option { + 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[][where T : Strategy] + (TupleUnion<(WA>, + WA>)>) + -> OptionValueTree; + /// `ValueTree` type corresponding to `OptionStrategy`. + pub struct OptionValueTree[][where T : Strategy] + (TupleUnionValueTree<( + LazyValueTree>, + Option>>, + )>) + -> Option; +} + +// 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 fmt::Debug for OptionStrategy { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "OptionStrategy({:?})", self.0) + } +} + +impl Clone for OptionValueTree +where + T::Tree: Clone, +{ + fn clone(&self) -> Self { + OptionValueTree(self.0.clone()) + } +} + +impl fmt::Debug for OptionValueTree +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: T) -> OptionStrategy { + 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( + probability_of_some: impl Into, + t: T, +) -> OptionStrategy { + 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>) -> 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); + } +} -- cgit v1.2.3