1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/*!
# Shrinking Test Cases
You can configure a [`Session`][crate::Session] to only perform mutations that
"shrink" their given values. Compared to the original input, a shrunken value is
simpler and less complex, has fewer members inside its inner `Vec`s and other
container types, serializes to fewer bytes, and etc...
When paired with an oracle, property, or predicate function, shrinking makes
test-case reduction easy! You can automate finding the smallest and
easiest-to-understand input that still triggers a bug in your code.
## Example
```rust
# fn foo() -> mutatis::Result<()> {
use mutatis::{mutators as m, Mutate, Session};
// Configure mutation to only shrink the input.
let mut session = Session::new().shrink(true);
let mut value = u32::MAX;
for _ in 0..10 {
session.mutate(&mut value)?;
println!("shrunken value is {value}");
}
// Example output:
//
// shrunken value is 1682887620
// shrunken value is 363687628
// shrunken value is 259482126
// shrunken value is 49968845
// shrunken value is 12933345
// shrunken value is 9334495
// shrunken value is 124077
// shrunken value is 12325
// shrunken value is 9732
// shrunken value is 3837
# Ok(())
# }
# foo().unwrap()
```
## Shrinking and Manual `Mutate` Implementations
When implementing [`Mutate`][crate::Mutate] by hand, rather than relying on the
[`mutatis::mutators`][crate::mutators] module's combinators or the
[`#[derive(Mutate)]`][crate::_guide::derive_macro] macro, be sure to check
whether you're inside a shrinking session and adjust your candidate mutations
appropriately. In general, checking for shrinking is often something that only
collections and "leaf" `Mutate` implementations need to worry about; if you are
simply delegating to sub-mutators then they can likely take responsibility for
shrinking.
Here is an example mutator that produces powers of two. When we are shrinking,
it only produces powers of two that are smaller than the original value:
```rust
# fn foo() -> mutatis::Result<()> {
use mutatis::{
mutators as m, Error, Generate, Mutate, Session, Candidates,
Result,
};
/// A mutator that mutates `u32`s into powers of two.
pub struct PowersOfTwo;
impl Mutate<u32> for PowersOfTwo {
fn mutate(
&mut self,
c: &mut Candidates,
value: &mut u32,
) -> Result<()> {
// If we should only shrink the value, then only generate powers of two
// less than the input. Otherwise, generate any power of two `u32`.
let max_log2 = if c.shrink() {
match value.ilog2().checked_sub(value.is_power_of_two() as u32) {
Some(x) => x,
// There are no more powers of two to shrink to, so early return
// and do not register any candidate mutations.
None => return Ok(()),
}
} else {
31
};
c.mutation(|context| {
// Choose a random `log2(value)` between 0 and `max_log2`, inclusive.
let log2 = m::mrange(0..=max_log2).generate(context)?;
// value = 2^log2(value) = 1 << log2(value)
*value = 1 << log2;
Ok(())
})?;
Ok(())
}
}
let mut value = u32::MAX;
// Configure mutation to only shrink the input.
let mut session = Session::new().shrink(true);
for _ in 0..4 {
session.mutate_with(&mut PowersOfTwo, &mut value)?;
println!("shrunken value is {value}");
}
// Example output:
//
// shrunken value is 8388608
// shrunken value is 8192
// shrunken value is 128
// shrunken value is 16
# Ok(())
# }
# mutatis::error::ResultExt::ignore_exhausted(foo()).unwrap()
```
## See Also
* [`Session::shrink`][crate::Session::shrink]: Configure whether a mutation
session should only shrink or not.
* [`Candidates::shrink`][crate::Candidates::shrink]: While enumerating candidate
mutations inside a `Mutate::mutate` implementation, determine whether you
should only register candidate mutations that shrink the original value.
* [`Context::shrink`][crate::Context::shrink]: While applying a mutation, check
whether the current mutation should only shrink the original value.
* [`Check::shrink_iters`][crate::check::Check::shrink_iters]: Configure the
number of attempts to shrink a failing input before reporting the failure.
*/