summaryrefslogtreecommitdiffstats
path: root/vendor/proptest/src/result.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/proptest/src/result.rs')
-rw-r--r--vendor/proptest/src/result.rs326
1 files changed, 326 insertions, 0 deletions
diff --git a/vendor/proptest/src/result.rs b/vendor/proptest/src/result.rs
new file mode 100644
index 000000000..bdb163b35
--- /dev/null
+++ b/vendor/proptest/src/result.rs
@@ -0,0 +1,326 @@
+//-
+// 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 combining delegate strategies into `std::Result`s.
+//!
+//! That is, the strategies here are for producing `Ok` _and_ `Err` cases. To
+//! simply adapt a strategy producing `T` into `Result<T, something>` which is
+//! always `Ok`, you can do something like `base_strategy.prop_map(Ok)` to
+//! simply wrap the generated values.
+//!
+//! Note that there are two nearly identical APIs for doing this, termed "maybe
+//! ok" and "maybe err". The difference between the two is in how they shrink;
+//! "maybe ok" treats `Ok` as the special case and shrinks to `Err`;
+//! conversely, "maybe err" treats `Err` as the special case and shrinks to
+//! `Ok`. Which to use largely depends on the code being tested; if the code
+//! typically handles errors by immediately bailing out and doing nothing else,
+//! "maybe ok" is likely more suitable, as shrinking will cause the code to
+//! take simpler paths. On the other hand, functions that need to make a
+//! complicated or fragile "back out" process on error are better tested with
+//! "maybe err" since the success case results in an easier to understand code
+//! path.
+
+#![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::*;
+
+// Re-export the type for easier usage.
+pub use crate::option::{prob, Probability};
+
+struct WrapOk<T, E>(PhantomData<T>, PhantomData<E>);
+impl<T, E> Clone for WrapOk<T, E> {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+impl<T, E> Copy for WrapOk<T, E> {}
+impl<T, E> fmt::Debug for WrapOk<T, E> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "WrapOk")
+ }
+}
+impl<T: fmt::Debug, E: fmt::Debug> statics::MapFn<T> for WrapOk<T, E> {
+ type Output = Result<T, E>;
+ fn apply(&self, t: T) -> Result<T, E> {
+ Ok(t)
+ }
+}
+struct WrapErr<T, E>(PhantomData<T>, PhantomData<E>);
+impl<T, E> Clone for WrapErr<T, E> {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+impl<T, E> Copy for WrapErr<T, E> {}
+impl<T, E> fmt::Debug for WrapErr<T, E> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "WrapErr")
+ }
+}
+impl<T: fmt::Debug, E: fmt::Debug> statics::MapFn<E> for WrapErr<T, E> {
+ type Output = Result<T, E>;
+ fn apply(&self, e: E) -> Result<T, E> {
+ Err(e)
+ }
+}
+
+type MapErr<T, E> =
+ statics::Map<E, WrapErr<<T as Strategy>::Value, <E as Strategy>::Value>>;
+type MapOk<T, E> =
+ statics::Map<T, WrapOk<<T as Strategy>::Value, <E as Strategy>::Value>>;
+
+opaque_strategy_wrapper! {
+ /// Strategy which generates `Result`s using `Ok` and `Err` values from two
+ /// delegate strategies.
+ ///
+ /// Shrinks to `Err`.
+ #[derive(Clone)]
+ pub struct MaybeOk[<T, E>][where T : Strategy, E : Strategy]
+ (TupleUnion<(WA<MapErr<T, E>>, WA<MapOk<T, E>>)>)
+ -> MaybeOkValueTree<T, E>;
+ /// `ValueTree` type corresponding to `MaybeOk`.
+ pub struct MaybeOkValueTree[<T, E>][where T : Strategy, E : Strategy]
+ (TupleUnionValueTree<(
+ LazyValueTree<statics::Map<E, WrapErr<T::Value, E::Value>>>,
+ Option<LazyValueTree<statics::Map<T, WrapOk<T::Value, E::Value>>>>,
+ )>)
+ -> Result<T::Value, E::Value>;
+}
+
+opaque_strategy_wrapper! {
+ /// Strategy which generates `Result`s using `Ok` and `Err` values from two
+ /// delegate strategies.
+ ///
+ /// Shrinks to `Ok`.
+ #[derive(Clone)]
+ pub struct MaybeErr[<T, E>][where T : Strategy, E : Strategy]
+ (TupleUnion<(WA<MapOk<T, E>>, WA<MapErr<T, E>>)>)
+ -> MaybeErrValueTree<T, E>;
+ /// `ValueTree` type corresponding to `MaybeErr`.
+ pub struct MaybeErrValueTree[<T, E>][where T : Strategy, E : Strategy]
+ (TupleUnionValueTree<(
+ LazyValueTree<statics::Map<T, WrapOk<T::Value, E::Value>>>,
+ Option<LazyValueTree<statics::Map<E, WrapErr<T::Value, E::Value>>>>,
+ )>)
+ -> Result<T::Value, E::Value>;
+}
+
+// These need to exist for the same reason as the one on `OptionStrategy`
+impl<T: Strategy + fmt::Debug, E: Strategy + fmt::Debug> fmt::Debug
+ for MaybeOk<T, E>
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "MaybeOk({:?})", self.0)
+ }
+}
+impl<T: Strategy + fmt::Debug, E: Strategy + fmt::Debug> fmt::Debug
+ for MaybeErr<T, E>
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "MaybeErr({:?})", self.0)
+ }
+}
+
+impl<T: Strategy, E: Strategy> Clone for MaybeOkValueTree<T, E>
+where
+ T::Tree: Clone,
+ E::Tree: Clone,
+{
+ fn clone(&self) -> Self {
+ MaybeOkValueTree(self.0.clone())
+ }
+}
+
+impl<T: Strategy, E: Strategy> fmt::Debug for MaybeOkValueTree<T, E>
+where
+ T::Tree: fmt::Debug,
+ E::Tree: fmt::Debug,
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "MaybeOkValueTree({:?})", self.0)
+ }
+}
+
+impl<T: Strategy, E: Strategy> Clone for MaybeErrValueTree<T, E>
+where
+ T::Tree: Clone,
+ E::Tree: Clone,
+{
+ fn clone(&self) -> Self {
+ MaybeErrValueTree(self.0.clone())
+ }
+}
+
+impl<T: Strategy, E: Strategy> fmt::Debug for MaybeErrValueTree<T, E>
+where
+ T::Tree: fmt::Debug,
+ E::Tree: fmt::Debug,
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "MaybeErrValueTree({:?})", self.0)
+ }
+}
+
+/// Create a strategy for `Result`s where `Ok` values are taken from `t` and
+/// `Err` values are taken from `e`.
+///
+/// `Ok` and `Err` are chosen with equal probability.
+///
+/// Generated values shrink to `Err`.
+pub fn maybe_ok<T: Strategy, E: Strategy>(t: T, e: E) -> MaybeOk<T, E> {
+ maybe_ok_weighted(0.5, t, e)
+}
+
+/// Create a strategy for `Result`s where `Ok` values are taken from `t` and
+/// `Err` values are taken from `e`.
+///
+/// `probability_of_ok` is the probability (between 0.0 and 1.0, exclusive)
+/// that `Ok` is initially chosen.
+///
+/// Generated values shrink to `Err`.
+pub fn maybe_ok_weighted<T: Strategy, E: Strategy>(
+ probability_of_ok: impl Into<Probability>,
+ t: T,
+ e: E,
+) -> MaybeOk<T, E> {
+ let prob = probability_of_ok.into().into();
+ let (ok_weight, err_weight) = float_to_weight(prob);
+
+ MaybeOk(TupleUnion::new((
+ (
+ err_weight,
+ Arc::new(statics::Map::new(e, WrapErr(PhantomData, PhantomData))),
+ ),
+ (
+ ok_weight,
+ Arc::new(statics::Map::new(t, WrapOk(PhantomData, PhantomData))),
+ ),
+ )))
+}
+
+/// Create a strategy for `Result`s where `Ok` values are taken from `t` and
+/// `Err` values are taken from `e`.
+///
+/// `Ok` and `Err` are chosen with equal probability.
+///
+/// Generated values shrink to `Ok`.
+pub fn maybe_err<T: Strategy, E: Strategy>(t: T, e: E) -> MaybeErr<T, E> {
+ maybe_err_weighted(0.5, t, e)
+}
+
+/// Create a strategy for `Result`s where `Ok` values are taken from `t` and
+/// `Err` values are taken from `e`.
+///
+/// `probability_of_ok` is the probability (between 0.0 and 1.0, exclusive)
+/// that `Err` is initially chosen.
+///
+/// Generated values shrink to `Ok`.
+pub fn maybe_err_weighted<T: Strategy, E: Strategy>(
+ probability_of_err: impl Into<Probability>,
+ t: T,
+ e: E,
+) -> MaybeErr<T, E> {
+ let prob = probability_of_err.into().into();
+ let (err_weight, ok_weight) = float_to_weight(prob);
+
+ MaybeErr(TupleUnion::new((
+ (
+ ok_weight,
+ Arc::new(statics::Map::new(t, WrapOk(PhantomData, PhantomData))),
+ ),
+ (
+ err_weight,
+ Arc::new(statics::Map::new(e, WrapErr(PhantomData, PhantomData))),
+ ),
+ )))
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ fn count_ok_of_1000(s: impl Strategy<Value = Result<(), ()>>) -> u32 {
+ let mut runner = TestRunner::deterministic();
+ let mut count = 0;
+ for _ in 0..1000 {
+ count += s.new_tree(&mut runner).unwrap().current().is_ok() as u32;
+ }
+
+ count
+ }
+
+ #[test]
+ fn probability_defaults_to_0p5() {
+ let count = count_ok_of_1000(maybe_err(Just(()), Just(())));
+ assert!(count > 400 && count < 600);
+ let count = count_ok_of_1000(maybe_ok(Just(()), Just(())));
+ assert!(count > 400 && count < 600);
+ }
+
+ #[test]
+ fn probability_handled_correctly() {
+ let count =
+ count_ok_of_1000(maybe_err_weighted(0.1, Just(()), Just(())));
+ assert!(count > 800 && count < 950);
+
+ let count =
+ count_ok_of_1000(maybe_err_weighted(0.9, Just(()), Just(())));
+ assert!(count > 50 && count < 150);
+
+ let count =
+ count_ok_of_1000(maybe_ok_weighted(0.9, Just(()), Just(())));
+ assert!(count > 800 && count < 950);
+
+ let count =
+ count_ok_of_1000(maybe_ok_weighted(0.1, Just(()), Just(())));
+ assert!(count > 50 && count < 150);
+ }
+
+ #[test]
+ fn shrink_to_correct_case() {
+ let mut runner = TestRunner::default();
+ {
+ let input = maybe_err(Just(()), Just(()));
+ for _ in 0..64 {
+ let mut val = input.new_tree(&mut runner).unwrap();
+ if val.current().is_ok() {
+ assert!(!val.simplify());
+ assert!(val.current().is_ok());
+ } else {
+ assert!(val.simplify());
+ assert!(val.current().is_ok());
+ }
+ }
+ }
+ {
+ let input = maybe_ok(Just(()), Just(()));
+ for _ in 0..64 {
+ let mut val = input.new_tree(&mut runner).unwrap();
+ if val.current().is_err() {
+ assert!(!val.simplify());
+ assert!(val.current().is_err());
+ } else {
+ assert!(val.simplify());
+ assert!(val.current().is_err());
+ }
+ }
+ }
+ }
+
+ #[test]
+ fn test_sanity() {
+ check_strategy_sanity(maybe_ok(0i32..100i32, 0i32..100i32), None);
+ check_strategy_sanity(maybe_err(0i32..100i32, 0i32..100i32), None);
+ }
+}