//- // Copyright 2017 Jason Lingle // // Licensed under the Apache License, Version 2.0 or the MIT license // , 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 { inner: T, may_simplify: bool, may_complicate: bool, } impl Fuse { /// Wrap the given `T` in `Fuse`. pub fn new(inner: T) -> Self { Fuse { inner, may_simplify: true, may_complicate: false, } } } impl Strategy for Fuse { type Tree = Fuse; type Value = T::Value; fn new_tree(&self, runner: &mut TestRunner) -> NewTree { self.inner.new_tree(runner).map(Fuse::new) } } impl Fuse { /// 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 ValueTree for Fuse { 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()); } }