summaryrefslogtreecommitdiffstats
path: root/vendor/proptest/src/strategy/fuse.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/proptest/src/strategy/fuse.rs')
-rw-r--r--vendor/proptest/src/strategy/fuse.rs228
1 files changed, 228 insertions, 0 deletions
diff --git a/vendor/proptest/src/strategy/fuse.rs b/vendor/proptest/src/strategy/fuse.rs
new file mode 100644
index 000000000..5c0f9a374
--- /dev/null
+++ b/vendor/proptest/src/strategy/fuse.rs
@@ -0,0 +1,228 @@
+//-
+// 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::strategy::*;
+use crate::test_runner::*;
+
+/// Adaptor for `Strategy` and `ValueTree` which guards `simplify()` and
+/// `complicate()` to avoid contract violations.
+///
+/// This can be used as an intermediate when the caller would otherwise need
+/// its own separate state tracking, or as a workaround for a broken
+/// `ValueTree` implementation.
+///
+/// This wrapper specifically has the following effects:
+///
+/// - Calling `complicate()` before `simplify()` was ever called does nothing
+/// and returns `false`.
+///
+/// - Calling `simplify()` after it has returned `false` and no calls to
+/// `complicate()` returned `true` does nothing and returns `false`.
+///
+/// - Calling `complicate()` after it has returned `false` and no calls to
+/// `simplify()` returned `true` does nothing and returns `false`.
+///
+/// There is also limited functionality to alter the internal state to assist
+/// in its usage as a state tracker.
+///
+/// Wrapping a `Strategy` in `Fuse` simply causes its `ValueTree` to also be
+/// wrapped in `Fuse`.
+///
+/// While this is similar to `std::iter::Fuse`, it is not exposed as a method
+/// on `Strategy` since the vast majority of proptest should never need this
+/// functionality; it mainly concerns implementors of strategies.
+#[derive(Debug, Clone, Copy)]
+#[must_use = "strategies do nothing unless used"]
+pub struct Fuse<T> {
+ inner: T,
+ may_simplify: bool,
+ may_complicate: bool,
+}
+
+impl<T> Fuse<T> {
+ /// Wrap the given `T` in `Fuse`.
+ pub fn new(inner: T) -> Self {
+ Fuse {
+ inner,
+ may_simplify: true,
+ may_complicate: false,
+ }
+ }
+}
+
+impl<T: Strategy> Strategy for Fuse<T> {
+ type Tree = Fuse<T::Tree>;
+ type Value = T::Value;
+
+ fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
+ self.inner.new_tree(runner).map(Fuse::new)
+ }
+}
+
+impl<T: ValueTree> Fuse<T> {
+ /// Return whether a call to `simplify()` may be productive.
+ ///
+ /// Formally, this is true if one of the following holds:
+ ///
+ /// - `simplify()` has never been called.
+ /// - The most recent call to `simplify()` returned `true`.
+ /// - `complicate()` has been called more recently than `simplify()` and
+ /// the last call returned `true`.
+ pub fn may_simplify(&self) -> bool {
+ self.may_simplify
+ }
+
+ /// Disallow any further calls to `simplify()` until a call to
+ /// `complicate()` returns `true`.
+ pub fn disallow_simplify(&mut self) {
+ self.may_simplify = false;
+ }
+
+ /// Return whether a call to `complicate()` may be productive.
+ ///
+ /// Formally, this is true if one of the following holds:
+ ///
+ /// - The most recent call to `complicate()` returned `true`.
+ /// - `simplify()` has been called more recently than `complicate()` and
+ /// the last call returned `true`.
+ pub fn may_complicate(&self) -> bool {
+ self.may_complicate
+ }
+
+ /// Disallow any further calls to `complicate()` until a call to
+ /// `simplify()` returns `true`.
+ pub fn disallow_complicate(&mut self) {
+ self.may_complicate = false;
+ }
+
+ /// Prevent any further shrinking operations from occurring.
+ pub fn freeze(&mut self) {
+ self.disallow_simplify();
+ self.disallow_complicate();
+ }
+}
+
+impl<T: ValueTree> ValueTree for Fuse<T> {
+ type Value = T::Value;
+
+ fn current(&self) -> T::Value {
+ self.inner.current()
+ }
+
+ fn simplify(&mut self) -> bool {
+ if self.may_simplify {
+ if self.inner.simplify() {
+ self.may_complicate = true;
+ true
+ } else {
+ self.may_simplify = false;
+ false
+ }
+ } else {
+ false
+ }
+ }
+
+ fn complicate(&mut self) -> bool {
+ if self.may_complicate {
+ if self.inner.complicate() {
+ self.may_simplify = true;
+ true
+ } else {
+ self.may_complicate = false;
+ false
+ }
+ } else {
+ false
+ }
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ struct StrictValueTree {
+ min: u32,
+ curr: u32,
+ max: u32,
+ ready: bool,
+ }
+
+ impl StrictValueTree {
+ fn new(start: u32) -> Self {
+ StrictValueTree {
+ min: 0,
+ curr: start,
+ max: start,
+ ready: false,
+ }
+ }
+ }
+
+ impl ValueTree for StrictValueTree {
+ type Value = u32;
+
+ fn current(&self) -> u32 {
+ self.curr
+ }
+
+ fn simplify(&mut self) -> bool {
+ assert!(self.min <= self.curr);
+ if self.curr > self.min {
+ self.max = self.curr;
+ self.curr -= 1;
+ self.ready = true;
+ true
+ } else {
+ self.min += 1;
+ false
+ }
+ }
+
+ fn complicate(&mut self) -> bool {
+ assert!(self.max >= self.curr);
+ assert!(self.ready);
+ if self.curr < self.max {
+ self.curr += 1;
+ true
+ } else {
+ self.max -= 1;
+ false
+ }
+ }
+ }
+
+ #[test]
+ fn test_sanity() {
+ check_strategy_sanity(Fuse::new(0i32..100i32), None);
+ }
+
+ #[test]
+ fn guards_bad_transitions() {
+ let mut vt = Fuse::new(StrictValueTree::new(5));
+ assert!(!vt.complicate());
+ assert_eq!(5, vt.current());
+
+ assert!(vt.simplify()); // 0, 4, 5
+ assert!(vt.simplify()); // 0, 3, 4
+ assert!(vt.simplify()); // 0, 2, 3
+ assert!(vt.simplify()); // 0, 1, 2
+ assert!(vt.simplify()); // 0, 0, 1
+ assert_eq!(0, vt.current());
+ assert!(!vt.simplify()); // 1, 0, 1
+ assert!(!vt.simplify()); // 1, 0, 1
+ assert_eq!(0, vt.current());
+ assert!(vt.complicate()); // 1, 1, 1
+ assert_eq!(1, vt.current());
+ assert!(!vt.complicate()); // 1, 1, 0
+ assert!(!vt.complicate()); // 1, 1, 0
+ assert_eq!(1, vt.current());
+ }
+}