bolero_engine/
shrink.rs

1use crate::{panic, panic::PanicError, Failure, Seed, Test};
2use bolero_generator::driver;
3use std::time::Instant;
4
5#[cfg(test)]
6mod tests;
7
8#[allow(clippy::len_without_is_empty)]
9pub trait Input: AsRef<Vec<u8>> + AsMut<Vec<u8>> {
10    type Driver<'a>: crate::Driver + core::panic::RefUnwindSafe
11    where
12        Self: 'a;
13
14    #[inline(always)]
15    fn len(&self) -> usize {
16        self.as_ref().len()
17    }
18
19    fn driver(&self, len: usize, options: &driver::Options) -> Self::Driver<'_>;
20}
21
22impl Input for Vec<u8> {
23    type Driver<'a> = driver::ByteSliceDriver<'a>;
24
25    #[inline]
26    fn driver(&self, len: usize, options: &driver::Options) -> Self::Driver<'_> {
27        driver::ByteSliceDriver::new(&self[..len], options)
28    }
29}
30
31/// Shrink the input to a simpler form
32pub fn shrink<T: Test, I: Input>(
33    test: &mut T,
34    input: I,
35    seed: Option<Seed>,
36    options: &driver::Options,
37) -> Option<Failure<T::Value>> {
38    Shrinker::new(test, input, seed, options).shrink()
39}
40
41macro_rules! predicate {
42    ($expr:expr) => {
43        if !($expr) {
44            return Err(());
45        }
46    };
47}
48
49macro_rules! shrink_integer {
50    ($current:expr, $check:expr) => {{
51        let mut check = $check;
52
53        (0..($current)).into_iter().find(|value| check(*value))
54    }};
55}
56
57#[derive(Debug)]
58struct Shrinker<'a, T, I> {
59    test: &'a mut T,
60    input: I,
61    temp_input: Vec<u8>,
62    end: usize,
63    seed: Option<Seed>,
64    options: &'a driver::Options,
65    #[cfg(test)]
66    snapshot_input: Vec<u8>,
67}
68
69impl<'a, T: Test, I: Input> Shrinker<'a, T, I> {
70    fn new(test: &'a mut T, input: I, seed: Option<Seed>, options: &'a driver::Options) -> Self {
71        let len = input.as_ref().len();
72        Self {
73            temp_input: input.as_ref().to_vec(),
74            input,
75            end: len,
76            test,
77            seed,
78            options,
79            #[cfg(test)]
80            snapshot_input: vec![],
81        }
82    }
83
84    fn shrink(mut self) -> Option<Failure<T::Value>> {
85        if self.options.shrink_time_or_default().is_zero() {
86            return None;
87        }
88
89        panic::set_hook();
90        let forward_panic = panic::forward_panic(false);
91        let capture_backtrace = panic::capture_backtrace(false);
92
93        if cfg!(test) {
94            panic::forward_panic(forward_panic);
95            assert!(
96                self.execute().is_err(),
97                "shrinking should only be performed on a failing test"
98            );
99            panic::forward_panic(false);
100        }
101
102        let mut was_changed;
103        let start_time = Instant::now();
104        let shrink_time = self.options.shrink_time_or_default();
105
106        loop {
107            was_changed = self.apply_truncation();
108
109            // empty input means we're done
110            if self.end == 0 {
111                break;
112            }
113
114            for index in 0..self.end {
115                if index >= self.end {
116                    // the length changed so start a new loop
117                    break;
118                }
119
120                self.apply_transforms(index, &mut was_changed);
121
122                // put a time limit on the number of shrink attempts
123                if start_time.elapsed() > shrink_time {
124                    break;
125                }
126            }
127
128            // we made it through all of the transforms without shrinking
129            // which means it's as small as it's going to get
130            if !was_changed {
131                break;
132            }
133
134            // put a time limit on the number of shrink iterations
135            if start_time.elapsed() > shrink_time {
136                break;
137            }
138        }
139
140        panic::capture_backtrace(capture_backtrace);
141        let error = self.execute().err()?;
142        panic::capture_backtrace(false);
143
144        let input = self.generate_value();
145
146        // restore settings
147        panic::forward_panic(forward_panic);
148        panic::capture_backtrace(capture_backtrace);
149
150        Some(Failure {
151            seed: self.seed,
152            error,
153            input,
154        })
155    }
156
157    fn apply_truncation(&mut self) -> bool {
158        self.apply("truncation end", |this| this.apply_truncation_end())
159    }
160
161    fn apply_truncation_end(&mut self) -> Result<(), ()> {
162        let prev_value = self.end;
163        let result = shrink_integer!(prev_value, |end| {
164            self.end = end;
165            self.execute().is_err()
166        });
167
168        if let Some(end) = result {
169            self.end = end;
170            Ok(())
171        } else {
172            // revert
173            self.end = prev_value;
174            Err(())
175        }
176    }
177
178    fn apply_transforms(&mut self, index: usize, was_changed: &mut bool) {
179        *was_changed |= self.apply("remove chunk", |this| this.apply_remove_chunk(index));
180
181        // try the more aggressive transforms before moving to the single-byte transforms
182        if *was_changed {
183            return;
184        }
185
186        *was_changed |= self.apply("sort", |this| this.apply_sort(index));
187
188        *was_changed |= self.apply("byte shrink", |this| this.apply_byte_shrink(index));
189    }
190
191    fn apply_byte_shrink(&mut self, index: usize) -> Result<(), ()> {
192        let prev_value = self.input.as_ref()[index];
193        let result = shrink_integer!(prev_value, |value| {
194            self.input.as_mut()[index] = value;
195            self.execute().is_err()
196        });
197
198        let input = self.input.as_mut();
199        if let Some(value) = result {
200            input[index] = value;
201            Ok(())
202        } else {
203            // revert
204            input[index] = prev_value;
205            Err(())
206        }
207    }
208
209    fn apply_remove_chunk(&mut self, index: usize) -> Result<(), ()> {
210        // since most generators are going to use at least the first byte we don't remove it
211        predicate!(index != 0);
212
213        let slice = &self.input.as_ref()[index..self.end];
214
215        // we need at least one byte to remove and one to shift up in its place
216        predicate!(slice.len() >= 2);
217
218        // store the previous input
219        let mut temp_input = core::mem::take(&mut self.temp_input);
220        temp_input.clear();
221        temp_input.extend_from_slice(slice);
222
223        // we need at least one byte to shift up
224        let max_len = temp_input.len() - 1;
225
226        let result = shrink_integer!(max_len, |diff| {
227            self.input.as_mut().truncate(index);
228            let offset = max_len - diff;
229            let slice = &temp_input[offset..];
230
231            // ensure the slicing logic makes sense
232            if cfg!(test) {
233                // the slice should be at least 1 and equal to diff + 1
234                assert_eq!(slice.len(), diff + 1);
235            }
236
237            self.input.as_mut().extend_from_slice(slice);
238            self.end = self.input.len();
239            self.execute().is_err()
240        });
241
242        self.input.as_mut().truncate(index);
243
244        let res = if let Some(diff) = result {
245            let offset = max_len - diff;
246            self.input.as_mut().extend_from_slice(&temp_input[offset..]);
247            Ok(())
248        } else {
249            self.input.as_mut().extend_from_slice(&temp_input);
250            Err(())
251        };
252
253        self.temp_input = temp_input;
254        self.end = self.input.len();
255
256        res
257    }
258
259    fn apply_sort(&mut self, index: usize) -> Result<(), ()> {
260        // make sure we have at least 1 byte to swap with
261        predicate!(index + 1 < self.end);
262        predicate!(self.input.as_ref()[index] > self.input.as_ref()[index + 1]);
263
264        self.input.as_mut().swap(index, index + 1);
265
266        if self.execute().is_err() {
267            return Ok(());
268        }
269
270        // revert
271        self.input.as_mut().swap(index, index + 1);
272
273        Err(())
274    }
275
276    #[inline(always)]
277    fn apply<F: FnOnce(&mut Self) -> Result<(), ()>>(&mut self, transform: &str, f: F) -> bool {
278        // store a snapshot of the previous input
279        #[cfg(test)]
280        {
281            self.snapshot_input.truncate(0);
282            self.snapshot_input
283                .extend_from_slice(&self.input.as_ref()[..self.end]);
284        }
285
286        let result = f(self).is_ok();
287
288        // ensures that the test is failing under the current input
289        //
290        // if not, this would indicate a invalid transform or non-determinsitic test
291        #[cfg(test)]
292        {
293            if self.execute().is_ok() {
294                eprintln!(
295                    "transform created non-failing test: {}\nBEFORE: {:?}\nAFTER: {:?}",
296                    transform,
297                    &self.snapshot_input,
298                    &self.input.as_ref()[..self.end],
299                );
300                panic!();
301            }
302        }
303
304        let _ = transform;
305
306        result
307    }
308
309    fn execute(&mut self) -> Result<bool, PanicError> {
310        self.test.test(&mut ShrinkInput {
311            input: &self.input,
312            len: self.end,
313            options: self.options,
314        })
315    }
316
317    fn generate_value(&mut self) -> T::Value {
318        self.test.generate_value(&mut ShrinkInput {
319            input: &self.input,
320            len: self.end,
321            options: self.options,
322        })
323    }
324}
325
326struct ShrinkInput<'a, I: Input> {
327    input: &'a I,
328    options: &'a driver::Options,
329    len: usize,
330}
331
332impl<'a, I: Input, Output> crate::Input<Output> for ShrinkInput<'a, I> {
333    type Driver = I::Driver<'a>;
334
335    fn with_slice<F: FnMut(&[u8]) -> Output>(&mut self, f: &mut F) -> Output {
336        f(self.input.as_ref())
337    }
338
339    fn with_driver<F: FnMut(&mut Self::Driver) -> Output>(&mut self, f: &mut F) -> Output {
340        let mut driver = self.input.driver(self.len, self.options);
341        f(&mut driver)
342    }
343}