diff options
Diffstat (limited to 'vendor/proptest/src/strategy/fuse.rs')
-rw-r--r-- | vendor/proptest/src/strategy/fuse.rs | 228 |
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()); + } +} |