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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
//! Allows testing code to enable and disable failspots
//!
//! When testing a crate, failspots can be enabled through a [Client] object. This can be retrieved
//! by using the `fn testing_client() -> Client<'static, Self>` method that exists as part of
//! every enum that was declared using the [`failspot_name!()`][crate::failspot_name] macro.
//!
//! The [`Client::set_enabled()`][Client::set_enabled] method can be used to set or unset a
//! failspot. [`Client::reset()`][Client::reset] will unset all failspots.
//!
//! Example usage:
//!
//! ```
//! # failspot::failspot_name! { pub enum FailSpotName { Name1 } }
//! # fn run_tests() {}
//! let mut client = FailSpotName::testing_client();
//! client.set_enabled(FailSpotName::Name1, true);
//! // When the `Client` object drops, all the failspots will be reset to disabled
//! // Must ensure it stays alive while tests are running.
//! run_tests();
//! ```
//!
//! # Concurrency -- Important!!!
//!
//! **TL;DR -- Put all your integration tests that use failspot in a separate source file!**
//!
//! ## The problem
//!
//! In Rust, **tests are run concurrently by default**. Since the configuration for the failspots
//! is a global variable that will be shared by all threads, that would create a problem -- Tests
//! that don't use failspots will suddenly start failing because another concurrent test enabled
//! them, and tests that do use failspots would clobber each other's configuration.
//!
//! To prevent this, the [Client] returned by `testing_client()` is **protected by a mutex** --
//! Only one test at a time can configure the failspots through the `Client` methods. When the
//! client is dropped, all the failspots are reset to disabled state and the mutex is released so
//! the next test can start with a fresh state.
//!
//! This means **every test that may run concurrently with a failspot test must hold the [Client]
//! object the entire time the test is running**, even if that test doesn't actually use failspots.
//! If there are multiple enums declared with [`failspot_name!()`][crate::failspot_name] then a
//! [Client] object for each enum must be held by every test that may run concurrently.
//!
//! For tests that use failspots, this is intuitive -- Most tests that use failspots will create a
//! [Client] as part of their setup.
//!
//! ## Stopping regular tests from breaking
//!
//! For tests that don't use failspots, there are 2 choices:
//!
//! 1. **Put failspot tests in their own source file (recommended)** Integration tests in
//! different source files are run in different processes, so separating failspot and non
//! failspot tests eliminates the concurrency issue.
//!
//! 2. **Force tests to run serially** By setting `RUST_TEST_THREADS=1` in the enviroment, the
//! tests will run one-at-a-time and there will be no interference.
//!
//! Obviously, the first one should be preferred unless there is a good reason not to.
use ;
/// Config object for an enum declared with [`failspot_name!()`][crate::failspot_name]
///
/// Every failspot enum has one of these attached. It tracks which failspots are currently
/// enabled for that enum, and contains the mutex that ensures that only one [Client] at a time
/// is running. It is not normally used directly by user code, but is instead used by the
/// [`failspot!()`][crate::failspot] macro for testing failpoints, and by the `testing_client()`
/// method to obtain a [Client] for testing code.
/// Client for testing code
///
/// See [module-level docs][self], especially the part about concurrency.