proptest/
tuple.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 combining strategies into tuples.
11//!
12//! There is no explicit "tuple strategy"; simply make a tuple containing the
13//! strategy and that tuple is itself a strategy.
14
15use crate::strategy::*;
16use crate::test_runner::*;
17
18/// Common `ValueTree` implementation for all tuple strategies.
19#[derive(Clone, Copy, Debug)]
20pub struct TupleValueTree<T> {
21    tree: T,
22    shrinker: u32,
23    prev_shrinker: Option<u32>,
24}
25
26impl<T> TupleValueTree<T> {
27    /// Create a new `TupleValueTree` wrapping `inner`.
28    ///
29    /// It only makes sense for `inner` to be a tuple of an arity for which the
30    /// type implements `ValueTree`.
31    pub fn new(inner: T) -> Self {
32        TupleValueTree {
33            tree: inner,
34            shrinker: 0,
35            prev_shrinker: None,
36        }
37    }
38}
39
40macro_rules! tuple {
41    ($($fld:tt : $typ:ident),*) => {
42        impl<$($typ : Strategy),*> Strategy for ($($typ,)*) {
43            type Tree = TupleValueTree<($($typ::Tree,)*)>;
44            type Value = ($($typ::Value,)*);
45
46            fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
47                let values = ($(self.$fld.new_tree(runner)?,)*);
48                Ok(TupleValueTree::new(values))
49            }
50        }
51
52        impl<$($typ : ValueTree),*> ValueTree
53        for TupleValueTree<($($typ,)*)> {
54            type Value = ($($typ::Value,)*);
55
56            fn current(&self) -> Self::Value {
57                ($(self.tree.$fld.current(),)*)
58            }
59
60            fn simplify(&mut self) -> bool {
61                $(
62                    if $fld == self.shrinker {
63                        if self.tree.$fld.simplify() {
64                            self.prev_shrinker = Some(self.shrinker);
65                            return true;
66                        } else {
67                            self.shrinker += 1;
68                        }
69                    }
70                )*
71                false
72            }
73
74            fn complicate(&mut self) -> bool {
75                if let Some(shrinker) = self.prev_shrinker {$(
76                    if $fld == shrinker {
77                        if self.tree.$fld.complicate() {
78                            self.shrinker = shrinker;
79                            return true;
80                        } else {
81                            self.prev_shrinker = None;
82                            return false;
83                        }
84                    }
85                )*}
86                false
87            }
88        }
89    }
90}
91
92tuple!(0: A);
93tuple!(0: A, 1: B);
94tuple!(0: A, 1: B, 2: C);
95tuple!(0: A, 1: B, 2: C, 3: D);
96tuple!(0: A, 1: B, 2: C, 3: D, 4: E);
97tuple!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F);
98tuple!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G);
99tuple!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H);
100tuple!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, 8: I);
101tuple!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, 8: I, 9: J);
102tuple!(
103    0: A,
104    1: B,
105    2: C,
106    3: D,
107    4: E,
108    5: F,
109    6: G,
110    7: H,
111    8: I,
112    9: J,
113    10: K
114);
115tuple!(
116    0: A,
117    1: B,
118    2: C,
119    3: D,
120    4: E,
121    5: F,
122    6: G,
123    7: H,
124    8: I,
125    9: J,
126    10: K,
127    11: L
128);
129
130#[cfg(test)]
131mod test {
132    use crate::strategy::*;
133
134    use super::*;
135
136    #[test]
137    fn shrinks_fully_ltr() {
138        fn pass(a: (i32, i32)) -> bool {
139            a.0 * a.1 <= 9
140        }
141
142        let input = (0..32, 0..32);
143        let mut runner = TestRunner::default();
144
145        let mut cases_tested = 0;
146        for _ in 0..256 {
147            // Find a failing test case
148            let mut case = input.new_tree(&mut runner).unwrap();
149            if pass(case.current()) {
150                continue;
151            }
152
153            loop {
154                if pass(case.current()) {
155                    if !case.complicate() {
156                        break;
157                    }
158                } else {
159                    if !case.simplify() {
160                        break;
161                    }
162                }
163            }
164
165            let last = case.current();
166            assert!(!pass(last));
167            // Maximally shrunken
168            assert!(pass((last.0 - 1, last.1)));
169            assert!(pass((last.0, last.1 - 1)));
170
171            cases_tested += 1;
172        }
173
174        assert!(cases_tested > 32, "Didn't find enough test cases");
175    }
176
177    #[test]
178    fn test_sanity() {
179        check_strategy_sanity((0i32..100, 0i32..1000, 0i32..10000), None);
180    }
181}