diff options
Diffstat (limited to 'vendor/proptest/src/strategy/filter_map.rs')
-rw-r--r-- | vendor/proptest/src/strategy/filter_map.rs | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/vendor/proptest/src/strategy/filter_map.rs b/vendor/proptest/src/strategy/filter_map.rs new file mode 100644 index 000000000..52f901276 --- /dev/null +++ b/vendor/proptest/src/strategy/filter_map.rs @@ -0,0 +1,209 @@ +//- +// 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. + +use crate::std_facade::{fmt, Arc, Cell}; + +use crate::strategy::traits::*; +use crate::test_runner::*; + +/// `Strategy` and `ValueTree` filter_map adaptor. +/// +/// See `Strategy::prop_filter_map()`. +#[must_use = "strategies do nothing unless used"] +pub struct FilterMap<S, F> { + pub(super) source: S, + pub(super) whence: Reason, + pub(super) fun: Arc<F>, +} + +impl<S, F> FilterMap<S, F> { + pub(super) fn new(source: S, whence: Reason, fun: F) -> Self { + Self { + source, + whence, + fun: Arc::new(fun), + } + } +} + +impl<S: fmt::Debug, F> fmt::Debug for FilterMap<S, F> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("FilterMap") + .field("source", &self.source) + .field("whence", &self.whence) + .field("fun", &"<function>") + .finish() + } +} + +impl<S: Clone, F> Clone for FilterMap<S, F> { + fn clone(&self) -> Self { + Self { + source: self.source.clone(), + whence: self.whence.clone(), + fun: Arc::clone(&self.fun), + } + } +} + +impl<S: Strategy, F: Fn(S::Value) -> Option<O>, O: fmt::Debug> Strategy + for FilterMap<S, F> +{ + type Tree = FilterMapValueTree<S::Tree, F, O>; + type Value = O; + + fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> { + loop { + let val = self.source.new_tree(runner)?; + if let Some(current) = (self.fun)(val.current()) { + return Ok(FilterMapValueTree::new(val, &self.fun, current)); + } else { + runner.reject_local(self.whence.clone())?; + } + } + } +} + +/// `ValueTree` corresponding to `FilterMap`. +pub struct FilterMapValueTree<V, F, O> { + source: V, + current: Cell<Option<O>>, + fun: Arc<F>, +} + +impl<V: Clone + ValueTree, F: Fn(V::Value) -> Option<O>, O> Clone + for FilterMapValueTree<V, F, O> +{ + fn clone(&self) -> Self { + Self::new(self.source.clone(), &self.fun, self.fresh_current()) + } +} + +impl<V: fmt::Debug, F, O> fmt::Debug for FilterMapValueTree<V, F, O> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("FilterMapValueTree") + .field("source", &self.source) + .field("current", &"<current>") + .field("fun", &"<function>") + .finish() + } +} + +impl<V: ValueTree, F: Fn(V::Value) -> Option<O>, O> + FilterMapValueTree<V, F, O> +{ + fn new(source: V, fun: &Arc<F>, current: O) -> Self { + Self { + source, + current: Cell::new(Some(current)), + fun: Arc::clone(fun), + } + } + + fn fresh_current(&self) -> O { + (self.fun)(self.source.current()) + .expect("internal logic error; this is a bug!") + } + + fn ensure_acceptable(&mut self) { + loop { + if let Some(current) = (self.fun)(self.source.current()) { + // Found an acceptable element! + self.current = Cell::new(Some(current)); + break; + } else if !self.source.complicate() { + panic!( + "Unable to complicate filtered strategy \ + back into acceptable value" + ); + } + } + } +} + +impl<V: ValueTree, F: Fn(V::Value) -> Option<O>, O: fmt::Debug> ValueTree + for FilterMapValueTree<V, F, O> +{ + type Value = O; + + fn current(&self) -> O { + // Optimization: we avoid the else branch in most success cases + // thereby avoiding to call the closure and the source tree. + if let Some(current) = self.current.replace(None) { + current + } else { + self.fresh_current() + } + } + + fn simplify(&mut self) -> bool { + if self.source.simplify() { + self.ensure_acceptable(); + true + } else { + false + } + } + + fn complicate(&mut self) -> bool { + if self.source.complicate() { + self.ensure_acceptable(); + true + } else { + false + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_filter_map() { + let input = (0..256).prop_filter_map("%3 + 1", |v| { + if 0 == v % 3 { + Some(v + 1) + } else { + None + } + }); + + for _ in 0..256 { + let mut runner = TestRunner::default(); + let mut case = input.new_tree(&mut runner).unwrap(); + + assert_eq!(0, (case.current() - 1) % 3); + + while case.simplify() { + assert_eq!(0, (case.current() - 1) % 3); + } + assert_eq!(0, (case.current() - 1) % 3); + } + } + + #[test] + fn test_filter_map_sanity() { + check_strategy_sanity( + (0..256).prop_filter_map("!%5 * 2", |v| { + if 0 != v % 5 { + Some(v * 2) + } else { + None + } + }), + Some(CheckStrategySanityOptions { + // Due to internal rejection sampling, `simplify()` can + // converge back to what `complicate()` would do. + strict_complicate_after_simplify: false, + ..CheckStrategySanityOptions::default() + }), + ); + } +} |