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}