diff options
Diffstat (limited to 'vendor/proptest/src/strategy/filter.rs')
-rw-r--r-- | vendor/proptest/src/strategy/filter.rs | 147 |
1 files changed, 147 insertions, 0 deletions
diff --git a/vendor/proptest/src/strategy/filter.rs b/vendor/proptest/src/strategy/filter.rs new file mode 100644 index 000000000..029dbcebc --- /dev/null +++ b/vendor/proptest/src/strategy/filter.rs @@ -0,0 +1,147 @@ +//- +// 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}; + +use crate::strategy::traits::*; +use crate::test_runner::*; + +/// `Strategy` and `ValueTree` filter adaptor. +/// +/// See `Strategy::prop_filter()`. +#[must_use = "strategies do nothing unless used"] +pub struct Filter<S, F> { + pub(super) source: S, + pub(super) whence: Reason, + pub(super) fun: Arc<F>, +} + +impl<S, F> Filter<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 Filter<S, F> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Filter") + .field("source", &self.source) + .field("whence", &self.whence) + .field("fun", &"<function>") + .finish() + } +} + +impl<S: Clone, F> Clone for Filter<S, F> { + fn clone(&self) -> Self { + Filter { + source: self.source.clone(), + whence: "unused".into(), + fun: Arc::clone(&self.fun), + } + } +} + +impl<S: Strategy, F: Fn(&S::Value) -> bool> Strategy for Filter<S, F> { + type Tree = Filter<S::Tree, F>; + type Value = S::Value; + + fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> { + loop { + let val = self.source.new_tree(runner)?; + if !(self.fun)(&val.current()) { + runner.reject_local(self.whence.clone())?; + } else { + return Ok(Filter { + source: val, + whence: self.whence.clone(), + fun: Arc::clone(&self.fun), + }); + } + } + } +} + +impl<S: ValueTree, F: Fn(&S::Value) -> bool> Filter<S, F> { + fn ensure_acceptable(&mut self) { + while !(self.fun)(&self.source.current()) { + if !self.source.complicate() { + panic!( + "Unable to complicate filtered strategy \ + back into acceptable value" + ); + } + } + } +} + +impl<S: ValueTree, F: Fn(&S::Value) -> bool> ValueTree for Filter<S, F> { + type Value = S::Value; + + fn current(&self) -> S::Value { + self.source.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() { + let input = (0..256).prop_filter("%3", |&v| 0 == v % 3); + + for _ in 0..256 { + let mut runner = TestRunner::default(); + let mut case = input.new_tree(&mut runner).unwrap(); + + assert!(0 == case.current() % 3); + + while case.simplify() { + assert!(0 == case.current() % 3); + } + assert!(0 == case.current() % 3); + } + } + + #[test] + fn test_filter_sanity() { + check_strategy_sanity( + (0..256).prop_filter("!%5", |&v| 0 != v % 5), + Some(CheckStrategySanityOptions { + // Due to internal rejection sampling, `simplify()` can + // converge back to what `complicate()` would do. + strict_complicate_after_simplify: false, + ..CheckStrategySanityOptions::default() + }), + ); + } +} |