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}