proptest/
array.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
10//! Support for strategies producing fixed-length arrays.
11//!
12//! An array of strategies (but only length 1 to 32 for now) is itself a
13//! strategy which generates arrays of that size drawing elements from the
14//! corresponding input strategies.
15//!
16//! See also [`UniformArrayStrategy`](struct.UniformArrayStrategy.html) for
17//! easily making a strategy for an array drawn from one strategy.
18//!
19//! General implementations are available for sizes 1 through 32.
20
21use core::marker::PhantomData;
22
23use crate::strategy::*;
24use crate::test_runner::*;
25
26/// A `Strategy` which generates fixed-size arrays containing values drawn from
27/// an inner strategy.
28///
29/// `T` must be an array type of length 1 to 32 whose values are produced by
30/// strategy `S`. Instances of this type are normally created by the various
31/// `uniformXX` functions in this module.
32///
33/// This is mainly useful when the inner strategy is not `Copy`, precluding
34/// expressing the strategy as `[myStrategy; 32]`, for example.
35///
36/// ## Example
37///
38/// ```
39/// use proptest::prelude::*;
40///
41/// proptest! {
42///   #[test]
43///   fn test_something(a in prop::array::uniform32(1u32..)) {
44///     let unexpected = [0u32;32];
45///     // `a` is also a [u32;32], so we can compare them directly
46///     assert_ne!(unexpected, a);
47///   }
48/// }
49/// # fn main() { }
50/// ```
51#[must_use = "strategies do nothing unless used"]
52#[derive(Clone, Copy, Debug)]
53pub struct UniformArrayStrategy<S, T> {
54    strategy: S,
55    _marker: PhantomData<T>,
56}
57
58impl<S, T> UniformArrayStrategy<S, T> {
59    /// Directly create a `UniformArrayStrategy`.
60    ///
61    /// This is only intended for advanced use, since the only way to specify
62    /// the array size is with the turbofish operator and explicitly naming the
63    /// type of the values in the array and the strategy itself.
64    ///
65    /// Prefer the `uniformXX` functions at module-level unless something
66    /// precludes their use.
67    pub fn new(strategy: S) -> Self {
68        UniformArrayStrategy {
69            strategy,
70            _marker: PhantomData,
71        }
72    }
73}
74
75/// A `ValueTree` operating over a fixed-size array.
76#[derive(Clone, Copy, Debug)]
77pub struct ArrayValueTree<T> {
78    tree: T,
79    shrinker: usize,
80    last_shrinker: Option<usize>,
81}
82
83macro_rules! small_array {
84    ($n:tt $uni:ident : $($ix:expr),*) => {
85        /// Create a strategy to generate fixed-length arrays.
86        ///
87        /// All values within the new strategy are generated using the given
88        /// strategy. The length of the array corresponds to the suffix of the
89        /// name of this function.
90        ///
91        /// See [`UniformArrayStrategy`](struct.UniformArrayStrategy.html) for
92        /// example usage.
93        pub fn $uni<S : Strategy>
94            (strategy: S) -> UniformArrayStrategy<S, [S::Value; $n]>
95        {
96            UniformArrayStrategy {
97                strategy,
98                _marker: PhantomData
99            }
100        }
101
102        impl<S : Strategy> Strategy for [S; $n] {
103            type Tree = ArrayValueTree<[S::Tree; $n]>;
104            type Value = [S::Value; $n];
105
106            fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
107                Ok(ArrayValueTree {
108                    tree: [$(self[$ix].new_tree(runner)?,)*],
109                    shrinker: 0,
110                    last_shrinker: None,
111                })
112            }
113        }
114
115        impl<S : Strategy> Strategy
116        for UniformArrayStrategy<S, [S::Value; $n]> {
117            type Tree = ArrayValueTree<[S::Tree; $n]>;
118            type Value = [S::Value; $n];
119
120            fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
121                Ok(ArrayValueTree {
122                    tree: [$({
123                        let _ = $ix;
124                        self.strategy.new_tree(runner)?
125                    },)*],
126                    shrinker: 0,
127                    last_shrinker: None,
128                })
129            }
130        }
131
132        impl<T : ValueTree> ValueTree for ArrayValueTree<[T;$n]> {
133            type Value = [T::Value;$n];
134
135            fn current(&self) -> [T::Value;$n] {
136                [$(self.tree[$ix].current(),)*]
137            }
138
139            fn simplify(&mut self) -> bool {
140                while self.shrinker < $n {
141                    if self.tree[self.shrinker].simplify() {
142                        self.last_shrinker = Some(self.shrinker);
143                        return true;
144                    } else {
145                        self.shrinker += 1;
146                    }
147                }
148
149                false
150            }
151
152            fn complicate(&mut self) -> bool {
153                if let Some(shrinker) = self.last_shrinker {
154                    self.shrinker = shrinker;
155                    if self.tree[shrinker].complicate() {
156                        true
157                    } else {
158                        self.last_shrinker = None;
159                        false
160                    }
161                } else {
162                    false
163                }
164            }
165        }
166    }
167}
168
169small_array!(1 uniform1:
170             0);
171small_array!(2 uniform2:
172             0, 1);
173small_array!(3 uniform3:
174             0, 1, 2);
175small_array!(4 uniform4:
176             0, 1, 2, 3);
177small_array!(5 uniform5:
178             0, 1, 2, 3, 4);
179small_array!(6 uniform6:
180             0, 1, 2, 3, 4, 5);
181small_array!(7 uniform7:
182             0, 1, 2, 3, 4, 5, 6);
183small_array!(8 uniform8:
184             0, 1, 2, 3, 4, 5, 6, 7);
185small_array!(9 uniform9:
186             0, 1, 2, 3, 4, 5, 6, 7, 8);
187small_array!(10 uniform10:
188             0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
189small_array!(11 uniform11:
190             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
191small_array!(12 uniform12:
192             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
193small_array!(13 uniform13:
194             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12);
195small_array!(14 uniform14:
196             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13);
197small_array!(15 uniform15:
198             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14);
199small_array!(16 uniform16:
200             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
201small_array!(17 uniform17:
202             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
203small_array!(18 uniform18:
204             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17);
205small_array!(19 uniform19:
206             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
207             18);
208small_array!(20 uniform20:
209             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
210             18, 19);
211small_array!(21 uniform21:
212             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
213             18, 19, 20);
214small_array!(22 uniform22:
215             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
216             18, 19, 20, 21);
217small_array!(23 uniform23:
218             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
219             18, 19, 20, 21, 22);
220small_array!(24 uniform24:
221             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
222             18, 19, 20, 21, 22, 23);
223small_array!(25 uniform25:
224             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
225             18, 19, 20, 21, 22, 23, 24);
226small_array!(26 uniform26:
227             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
228             18, 19, 20, 21, 22, 23, 24, 25);
229small_array!(27 uniform27:
230             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
231             18, 19, 20, 21, 22, 23, 24, 25, 26);
232small_array!(28 uniform28:
233             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
234             18, 19, 20, 21, 22, 23, 24, 25, 26, 27);
235small_array!(29 uniform29:
236             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
237             18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28);
238small_array!(30 uniform30:
239             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
240             18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29);
241small_array!(31 uniform31:
242             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
243             18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30);
244small_array!(32 uniform32:
245             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
246             18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31);
247
248#[cfg(test)]
249mod test {
250    use super::*;
251
252    #[test]
253    fn shrinks_fully_ltr() {
254        fn pass(a: [i32; 2]) -> bool {
255            a[0] * a[1] <= 9
256        }
257
258        let input = [0..32, 0..32];
259        let mut runner = TestRunner::deterministic();
260
261        let mut cases_tested = 0;
262        for _ in 0..256 {
263            // Find a failing test case
264            let mut case = input.new_tree(&mut runner).unwrap();
265            if pass(case.current()) {
266                continue;
267            }
268
269            loop {
270                if pass(case.current()) {
271                    if !case.complicate() {
272                        break;
273                    }
274                } else {
275                    if !case.simplify() {
276                        break;
277                    }
278                }
279            }
280
281            let last = case.current();
282            assert!(!pass(last));
283            // Maximally shrunken
284            assert!(pass([last[0] - 1, last[1]]));
285            assert!(pass([last[0], last[1] - 1]));
286
287            cases_tested += 1;
288        }
289
290        assert!(cases_tested > 32, "Didn't find enough test cases");
291    }
292
293    #[test]
294    fn test_sanity() {
295        check_strategy_sanity([(0i32..1000), (1i32..1000)], None);
296    }
297}