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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
use crate::BoxShrink;
use crate::ConfAndGen;
use crate::Seed;
use std::fmt::Debug;
/// Result summary from evaluation of a property tested.
#[derive(Debug, PartialEq)]
pub enum MonkeyResult<E> {
/// A successful monkey test result.
MonkeyOk(),
/// A failed monkey test result.
MonkeyErr {
/// The minimum example found that disproves the property. In case a
/// shinker is provided, this is the shrunken failure example, possibly
/// separate from original failure. In other cases the same as original
/// failure.
minimum_failure: E,
/// The original (first found) example that disproves the property.
original_failure: E,
/// Other examples that also disproves the property. In case a shinker
/// is provided, this vector is populated with non-minimum values found
/// as part of the shrinking process of the original failure example.
/// Some found failures may be exluded from list if many failure
/// examples are found
some_other_failures: Vec<E>,
/// Successful example count tried before finding a failure.
success_count: u64,
/// Number of examples used when trying to shrink original failure
/// example.
shrink_count: u64,
/// The seed used for generating the examples. Can be useful for
/// reproducing the failed test run.
seed: Seed,
/// Optional title of the failed property.
title: Option<String>,
/// Reason for the failed propert. This reason is from the
/// [minimum failure](self::MonkeyResult#variant.MonkeyErr.field.minimum_failure)
/// example. Other failures can have other reasons not shown here.
reason: String,
},
}
impl<E> MonkeyResult<E> {
/// Verify that the result is a failure and that the minimum failure equals
/// given argument `expected_minimum_failure`.
///
/// # Panics
///
/// The function panics if there is not a failure result matching
/// `expected_minimum_failue`.
pub fn assert_minimum_failure(&self, expected_minimum_failure: E) -> &Self
where
E: Debug + PartialEq,
{
match self {
MonkeyResult::MonkeyOk() => panic!(
"Expecting property to fail for some example, but it never failed."
),
MonkeyResult::MonkeyErr {
minimum_failure, ..
} => assert!(
expected_minimum_failure == *minimum_failure,
"Expecting property to fail, which it did, but also \
expecting minimum failure to be equal to {:?}, but got {:?}",
expected_minimum_failure,
minimum_failure,
),
};
self
}
}
pub fn evaluate_property<E, P>(cg: &ConfAndGen<E>, prop: P) -> MonkeyResult<E>
where
E: Clone + 'static,
P: Fn(E) -> Result<(), String>,
{
let mut it = cg.generator.examples(cg.conf.seed, cg.conf.size.clone());
for i in 0..cg.conf.example_count {
let example = it.next();
let (first_example, maybe_first_reason) = match example {
Some(e) => (e.clone(), prop(e.clone())),
None => panic!("To few examples. Only got {i}"),
};
if let Err(first_reason) = maybe_first_reason {
let shrinked_values =
do_shrink(prop, first_example.clone(), cg.generator.shrinker());
// All but last shrinked value, up to a max limit
let other_count = shrinked_values.len().clamp(1, 100) as u64 - 1;
let some_other_failures = shrinked_values
.clone()
.into_iter()
.map(|(example, _)| example)
.take(other_count as usize)
.collect::<Vec<_>>();
let (minimum_failure, minimum_reason) = shrinked_values
.last()
.cloned()
.unwrap_or((first_example.clone(), first_reason));
return MonkeyResult::<E>::MonkeyErr {
minimum_failure,
original_failure: first_example,
some_other_failures,
success_count: i as u64,
shrink_count: shrinked_values.len() as u64,
seed: cg.conf.seed,
title: cg.title.clone(),
reason: minimum_reason,
};
}
}
MonkeyResult::<E>::MonkeyOk()
}
fn do_shrink<E, P>(
prop: P,
original_failure: E,
shrinker: BoxShrink<E>,
) -> Vec<(E, String)>
where
E: Clone,
P: Fn(E) -> Result<(), String>,
{
let shrink_effort = 10_000;
let mut shrinked_examples = vec![];
let mut candidates = shrinker.candidates(original_failure);
for _ in 0..shrink_effort {
match candidates.next() {
Some(candidate) => {
let maybe_failure_reason = prop(candidate.clone());
if let Err(reason) = maybe_failure_reason {
shrinked_examples.push((candidate.clone(), reason));
candidates = shrinker.candidates(candidate);
}
}
None => return shrinked_examples,
}
}
shrinked_examples
}