rs_quickcheck_util/
shrink_field.rs

1/// Shrinks one field of an object.
2///
3/// For any object `t` of type `T` and `a` is one of its field,
4/// `shrink_a_field!(t, a)` will result in an iterator of type `T`,
5/// whose `a` fields are shrinked and the other fields are kept untouched.
6/// For example,
7/// ```rust
8/// use rs_quickcheck_util::shrink_a_field;
9///
10/// #[derive(Debug, Clone)]
11/// struct T {
12///     a: i64,
13///     b: i64,
14/// }
15/// let t = T {
16///     a: 100,
17///     b: 42,
18/// };
19/// for x in shrink_a_field!(t, a) {
20///     assert!(x.a < t.a);
21///     assert_eq!(x.b, t.b);
22/// }
23/// ```
24///
25/// Sometimes, a field must be shrinked with a different behaviour,
26/// e.g., for a Vec field, shrinking the vector but not shrinking their elements.
27/// This can be achieved by "wrapping" the field.
28///
29/// ```rust
30/// use rs_quickcheck_util::{shrink_a_field, Unshrinkable};
31///
32/// #[derive(Debug, Clone)]
33/// struct T {
34///     a: Vec<i64>,
35/// }
36/// let t = T {
37///     a: [10, 10].into(),
38/// };
39/// let it = shrink_a_field!(
40///     t,
41///     a,
42///     |xs: &Vec<i64>| {
43///         xs.iter().map(|x| Unshrinkable::new(*x)).collect::<Vec<_>>()
44///     },
45///     |xs: Vec<Unshrinkable::<i64>>| {
46///         xs.into_iter()
47///             .map(|x| x.take())
48///             .collect::<Vec<_>>()
49///     }
50/// );
51/// for x in it {
52///     assert!(x.a.iter().all(|y| *y == 10));
53/// }
54/// ```
55///
56#[macro_export]
57macro_rules! shrink_a_field {
58    ($obj:expr, $field:ident) => {
59        {
60            use quickcheck::Arbitrary;
61            let me = $obj.clone();
62            me.clone().$field.shrink()
63                .map(move |x| {
64                    let mut res = me.clone();
65                    res.$field = x;
66                    res
67                })
68        }
69    };
70    ($obj:expr, $field:ident, $wrap_fn:expr, $unwrap_fn:expr) => {
71        {
72            use quickcheck::Arbitrary;
73            let wrap_fn = $wrap_fn;
74            let unwrap_fn = $unwrap_fn;
75            let xs = wrap_fn(&($obj.$field));
76            let me = $obj.clone();
77            xs.shrink()
78                .map(move |x| {
79                    let mut res = me.clone();
80                    res.$field = unwrap_fn(x);
81                    res
82                })
83        }
84    }
85}
86
87/// Shrinks a mapping field whose key is determined by the value.
88///
89/// For a mapping field, sometimes the key is determined by the value.
90/// To keep this constraint in shrinking, one can use `shrink_a_map_field`.
91/// For example,
92///
93/// ```rust
94/// use rs_quickcheck_util::shrink_a_map_field;
95/// use std::collections::BTreeMap;
96///
97/// #[derive(Debug, Clone)]
98/// struct T {
99///     m: BTreeMap<String, usize>,
100/// }
101/// let t = T {
102///     m: [
103///         ("1".to_string(), 1),
104///         ("2".to_string(), 2),
105///         ("3".to_string(), 3),
106///         ("4".to_string(), 4),
107///         ("5".to_string(), 5),
108///         ("6".to_string(), 6),
109///     ].into(),
110/// };
111/// for x in shrink_a_map_field!(t, m, |x: &usize| -> String {format!("{}", x)}) {
112///     for (k, v) in x.m.iter() {
113///         assert_eq!(k.to_string(), format!("{}", v));
114///     }
115/// }
116/// ```
117/// Then, both size and values of `t.m` will be shrinked, but the relation between
118/// keys and values are kept.
119#[macro_export]
120macro_rules! shrink_a_map_field {
121    ($obj:expr, $field:ident, $key_fn:expr) => {
122        {
123            use quickcheck::Arbitrary;
124            let key_fn = $key_fn;
125            let me = $obj.clone();
126            let xs: Vec<_> = me.$field.values().cloned().collect();
127            xs.shrink()
128                .map(move |xs| {
129                    let mut res = me.clone();
130                    res.$field = xs.into_iter()
131                        .map(|x| (key_fn(&x), x))
132                        .collect();
133                    res
134                })
135        }
136    };
137}
138
139#[cfg(test)]
140mod tests {
141    use quickcheck_macros::*;
142    use std::collections::BTreeMap;
143
144    #[quickcheck]
145    fn shrink_simple_field(trial: A) {
146        for x in shrink_a_field!(trial, a) {
147            assert_eq!(x.b, trial.b);
148            assert!(x.a < trial.a);
149        }
150    }
151
152    #[derive(Debug, Clone)]
153    struct A{a: usize, b: usize}
154
155    impl quickcheck::Arbitrary for A {
156        fn arbitrary(g: &mut quickcheck::Gen) -> Self {
157            Self{
158                a: usize::arbitrary(g),
159                b: usize::arbitrary(g),
160            }
161        }
162    }
163
164
165    #[quickcheck]
166    fn shrink_map(trial: B) {
167        let bs: Vec<_> = shrink_a_map_field!(trial, b, |v: &usize| -> usize {*v}).collect();
168        for x in bs.iter() {
169            let keys: Vec<_> = x.b.keys().copied().collect();
170            let values: Vec<_> = x.b.values().copied().collect();
171            assert_eq!(keys, values);
172            assert!(x.b.len() <= trial.b.len());
173        }
174        if !trial.b.is_empty() {
175            assert!(bs.iter().any(|x| x.b.is_empty()));
176        }
177    }
178
179    #[derive(Debug, Clone)]
180    struct B{
181        b: BTreeMap<usize, usize>,
182    }
183
184    impl quickcheck::Arbitrary for B {
185        fn arbitrary(g: &mut quickcheck::Gen) -> Self {
186            let b = crate::gen_bytes(g, b"abc.", b'.', 0..).iter()
187                .map(|_| {
188                    let v = usize::arbitrary(g);
189                    (v, v)
190                })
191                .collect();
192            Self{b}
193        }
194    }
195
196    #[test]
197    fn unshrinkable() {
198        let z = A {
199            a: 100,
200            b: 100,
201        };
202        let it = shrink_a_field!(
203            z,
204            b,
205            |x: &usize| {crate::Unshrinkable::new(*x)},
206            |x: crate::Unshrinkable::<usize>| {x.take()}
207        );
208        for x in it {
209            assert_eq!(x.b, z.b);
210        }
211    }
212
213}