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
//! Declared invariants — properties that must hold after every transition.
use crate;
use PhantomData;
/// The assertion an invariant runs: `(before, event, after) -> holds?`, where
/// `after` is `Some` if the transition committed and `None` if it was rejected.
type Check<S, E> = ;
/// A named property checked after each transition during `test!`.
///
/// The check receives the state before the event, the event, and the state
/// after — `Some` if the transition committed, `None` if it was rejected.
/// A half-built [`Invariant`] awaiting its assertion closure.
/// Implemented by a machine that declares domain invariants.
///
/// Optional: a machine without an `Invariants` impl is still verified for its
/// structural properties by `test!`. Implement this to add domain-specific
/// checks on top.
///
/// Each invariant is a description paired with a closure over the step
/// `(before, event, after)`, where `after` is `Some(state)` when the transition
/// committed and `None` when it was rejected. Return `true` while the property
/// holds; `test!` reports the description and the shrunk event sequence when it
/// doesn't.
///
/// # Examples
///
/// An invariant is most useful for a property the structure can't express on its
/// own — a condition on the *data* a state carries. Here `Charging` holds a
/// charge level, and the property is a bound on that number rather than on which
/// variant we're in:
///
/// ```ignore
/// // enum Battery { Empty, Charging { percent: u8 }, Full }
///
/// impl Invariants for Battery {
/// fn invariants() -> Vec<Invariant<Self, Self::Event>> {
/// vec![
/// Invariant::custom("charge never exceeds 100%").assert(
/// |_before, _event, after| match after {
/// Some(Battery::Charging { percent }) => *percent <= 100,
/// _ => true,
/// },
/// ),
/// ]
/// }
/// }
/// ```