proptest/strategy/
fuse.rs

1//-
2// Copyright 2017 Jason Lingle
3//
4// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7// option. This file may not be copied, modified, or distributed
8// except according to those terms.
9
10use crate::strategy::*;
11use crate::test_runner::*;
12
13/// Adaptor for `Strategy` and `ValueTree` which guards `simplify()` and
14/// `complicate()` to avoid contract violations.
15///
16/// This can be used as an intermediate when the caller would otherwise need
17/// its own separate state tracking, or as a workaround for a broken
18/// `ValueTree` implementation.
19///
20/// This wrapper specifically has the following effects:
21///
22/// - Calling `complicate()` before `simplify()` was ever called does nothing
23///   and returns `false`.
24///
25/// - Calling `simplify()` after it has returned `false` and no calls to
26///   `complicate()` returned `true` does nothing and returns `false`.
27///
28/// - Calling `complicate()` after it has returned `false` and no calls to
29///   `simplify()` returned `true` does nothing and returns `false`.
30///
31/// There is also limited functionality to alter the internal state to assist
32/// in its usage as a state tracker.
33///
34/// Wrapping a `Strategy` in `Fuse` simply causes its `ValueTree` to also be
35/// wrapped in `Fuse`.
36///
37/// While this is similar to `std::iter::Fuse`, it is not exposed as a method
38/// on `Strategy` since the vast majority of proptest should never need this
39/// functionality; it mainly concerns implementors of strategies.
40#[derive(Debug, Clone, Copy)]
41#[must_use = "strategies do nothing unless used"]
42pub struct Fuse<T> {
43    inner: T,
44    may_simplify: bool,
45    may_complicate: bool,
46}
47
48impl<T> Fuse<T> {
49    /// Wrap the given `T` in `Fuse`.
50    pub fn new(inner: T) -> Self {
51        Fuse {
52            inner,
53            may_simplify: true,
54            may_complicate: false,
55        }
56    }
57}
58
59impl<T: Strategy> Strategy for Fuse<T> {
60    type Tree = Fuse<T::Tree>;
61    type Value = T::Value;
62
63    fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
64        self.inner.new_tree(runner).map(Fuse::new)
65    }
66}
67
68impl<T: ValueTree> Fuse<T> {
69    /// Return whether a call to `simplify()` may be productive.
70    ///
71    /// Formally, this is true if one of the following holds:
72    ///
73    /// - `simplify()` has never been called.
74    /// - The most recent call to `simplify()` returned `true`.
75    /// - `complicate()` has been called more recently than `simplify()` and
76    ///   the last call returned `true`.
77    pub fn may_simplify(&self) -> bool {
78        self.may_simplify
79    }
80
81    /// Disallow any further calls to `simplify()` until a call to
82    /// `complicate()` returns `true`.
83    pub fn disallow_simplify(&mut self) {
84        self.may_simplify = false;
85    }
86
87    /// Return whether a call to `complicate()` may be productive.
88    ///
89    /// Formally, this is true if one of the following holds:
90    ///
91    /// - The most recent call to `complicate()` returned `true`.
92    /// - `simplify()` has been called more recently than `complicate()` and
93    ///   the last call returned `true`.
94    pub fn may_complicate(&self) -> bool {
95        self.may_complicate
96    }
97
98    /// Disallow any further calls to `complicate()` until a call to
99    /// `simplify()` returns `true`.
100    pub fn disallow_complicate(&mut self) {
101        self.may_complicate = false;
102    }
103
104    /// Prevent any further shrinking operations from occurring.
105    pub fn freeze(&mut self) {
106        self.disallow_simplify();
107        self.disallow_complicate();
108    }
109}
110
111impl<T: ValueTree> ValueTree for Fuse<T> {
112    type Value = T::Value;
113
114    fn current(&self) -> T::Value {
115        self.inner.current()
116    }
117
118    fn simplify(&mut self) -> bool {
119        if self.may_simplify {
120            if self.inner.simplify() {
121                self.may_complicate = true;
122                true
123            } else {
124                self.may_simplify = false;
125                false
126            }
127        } else {
128            false
129        }
130    }
131
132    fn complicate(&mut self) -> bool {
133        if self.may_complicate {
134            if self.inner.complicate() {
135                self.may_simplify = true;
136                true
137            } else {
138                self.may_complicate = false;
139                false
140            }
141        } else {
142            false
143        }
144    }
145}
146
147#[cfg(test)]
148mod test {
149    use super::*;
150
151    struct StrictValueTree {
152        min: u32,
153        curr: u32,
154        max: u32,
155        ready: bool,
156    }
157
158    impl StrictValueTree {
159        fn new(start: u32) -> Self {
160            StrictValueTree {
161                min: 0,
162                curr: start,
163                max: start,
164                ready: false,
165            }
166        }
167    }
168
169    impl ValueTree for StrictValueTree {
170        type Value = u32;
171
172        fn current(&self) -> u32 {
173            self.curr
174        }
175
176        fn simplify(&mut self) -> bool {
177            assert!(self.min <= self.curr);
178            if self.curr > self.min {
179                self.max = self.curr;
180                self.curr -= 1;
181                self.ready = true;
182                true
183            } else {
184                self.min += 1;
185                false
186            }
187        }
188
189        fn complicate(&mut self) -> bool {
190            assert!(self.max >= self.curr);
191            assert!(self.ready);
192            if self.curr < self.max {
193                self.curr += 1;
194                true
195            } else {
196                self.max -= 1;
197                false
198            }
199        }
200    }
201
202    #[test]
203    fn test_sanity() {
204        check_strategy_sanity(Fuse::new(0i32..100i32), None);
205    }
206
207    #[test]
208    fn guards_bad_transitions() {
209        let mut vt = Fuse::new(StrictValueTree::new(5));
210        assert!(!vt.complicate());
211        assert_eq!(5, vt.current());
212
213        assert!(vt.simplify()); // 0, 4, 5
214        assert!(vt.simplify()); // 0, 3, 4
215        assert!(vt.simplify()); // 0, 2, 3
216        assert!(vt.simplify()); // 0, 1, 2
217        assert!(vt.simplify()); // 0, 0, 1
218        assert_eq!(0, vt.current());
219        assert!(!vt.simplify()); // 1, 0, 1
220        assert!(!vt.simplify()); // 1, 0, 1
221        assert_eq!(0, vt.current());
222        assert!(vt.complicate()); // 1, 1, 1
223        assert_eq!(1, vt.current());
224        assert!(!vt.complicate()); // 1, 1, 0
225        assert!(!vt.complicate()); // 1, 1, 0
226        assert_eq!(1, vt.current());
227    }
228}