summaryrefslogtreecommitdiffstats
path: root/vendor/proptest/src/strategy/filter_map.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/proptest/src/strategy/filter_map.rs')
-rw-r--r--vendor/proptest/src/strategy/filter_map.rs209
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()
+ }),
+ );
+ }
+}