@@ -123,6 +123,117 @@ use std::sync::Arc;
123
123
/// # }
124
124
/// ```
125
125
///
126
+ /// ## Prevent tests from running in parallel
127
+ ///
128
+ /// By default, Rust runs tests in the same file in parallel. However, in some cases, running two tests in parallel may lead to problems.
129
+ /// For example, this can happen when tests use the same database.
130
+ ///
131
+ /// Consider the following scenario:
132
+ /// 1. `test_insert`: Inserts a key-value pair into the database, then retrieves the value using the same key to verify the insertion.
133
+ /// 2. `test_update`: Inserts a key, then updates the key to a new value and verifies that the value has been accurately updated.
134
+ /// 3. `test_others`: A third test that doesn't modify the database state. It can run in parallel with the other tests.
135
+ ///
136
+ /// In this example, `test_insert` and `test_update` need to run in sequence to work, but it doesn't matter which test runs first.
137
+ /// We can leverage a semaphore with a single permit to address this challenge.
138
+ ///
139
+ /// ```
140
+ /// use tokio::sync::Semaphore;
141
+ /// # use tokio::sync::Mutex;
142
+ /// # use std::collections::BTreeMap;
143
+ /// # struct Database {
144
+ /// # map: Mutex<BTreeMap<String, i32>>,
145
+ /// # }
146
+ /// # impl Database {
147
+ /// # pub const fn setup() -> Database {
148
+ /// # Database {
149
+ /// # map: Mutex::const_new(BTreeMap::new()),
150
+ /// # }
151
+ /// # }
152
+ /// # pub async fn insert(&self, key: &str, value: i32) {
153
+ /// # self.map.lock().await.insert(key.to_string(), value);
154
+ /// # }
155
+ /// # pub async fn update(&self, key: &str, value: i32) {
156
+ /// # self.map.lock().await
157
+ /// # .entry(key.to_string())
158
+ /// # .and_modify(|origin| *origin = value);
159
+ /// # }
160
+ /// # pub async fn delete(&self, key: &str) {
161
+ /// # self.map.lock().await.remove(key);
162
+ /// # }
163
+ /// # pub async fn get(&self, key: &str) -> i32 {
164
+ /// # *self.map.lock().await.get(key).unwrap()
165
+ /// # }
166
+ /// # }
167
+ ///
168
+ /// // Initialize a static semaphore with only one permit, which is used to
169
+ /// // prevent test_insert and test_update from running in parallel.
170
+ /// static PERMIT: Semaphore = Semaphore::const_new(1);
171
+ ///
172
+ /// // Initialize the database that will be used by the subsequent tests.
173
+ /// static DB: Database = Database::setup();
174
+ ///
175
+ /// #[tokio::test]
176
+ /// # async fn fake_test() {}
177
+ /// async fn test_insert() {
178
+ /// // Acquire permit before proceeding. Since the semaphore has only one permit,
179
+ /// // the test will wait if the permit is already acquired by other tests.
180
+ /// let permit = PERMIT.acquire().await.unwrap();
181
+ ///
182
+ /// // Do the actual test stuff with database
183
+ ///
184
+ /// // Insert a key-value pair to database
185
+ /// let (key, value) = ("name", 0);
186
+ /// DB.insert(key, value).await;
187
+ ///
188
+ /// // Verify that the value has been inserted correctly.
189
+ /// assert_eq!(DB.get(key).await, value);
190
+ ///
191
+ /// // Undo the insertion, so the database is empty at the end of the test.
192
+ /// DB.delete(key).await;
193
+ ///
194
+ /// // Drop permit. This allows the other test to start running.
195
+ /// drop(permit);
196
+ /// }
197
+ ///
198
+ /// #[tokio::test]
199
+ /// # async fn fake_test() {}
200
+ /// async fn test_update() {
201
+ /// // Acquire permit before proceeding. Since the semaphore has only one permit,
202
+ /// // the test will wait if the permit is already acquired by other tests.
203
+ /// let permit = PERMIT.acquire().await.unwrap();
204
+ ///
205
+ /// // Do the same insert.
206
+ /// let (key, value) = ("name", 0);
207
+ /// DB.insert(key, value).await;
208
+ ///
209
+ /// // Update the existing value with a new one.
210
+ /// let new_value = 1;
211
+ /// DB.update(key, new_value).await;
212
+ ///
213
+ /// // Verify that the value has been updated correctly.
214
+ /// assert_eq!(DB.get(key).await, new_value);
215
+ ///
216
+ /// // Undo any modificattion.
217
+ /// DB.delete(key).await;
218
+ ///
219
+ /// // Drop permit. This allows the other test to start running.
220
+ /// drop(permit);
221
+ /// }
222
+ ///
223
+ /// #[tokio::test]
224
+ /// # async fn fake_test() {}
225
+ /// async fn test_others() {
226
+ /// // This test can run in parallel with test_insert and test_update,
227
+ /// // so it does not use PERMIT.
228
+ /// }
229
+ /// # #[tokio::main]
230
+ /// # async fn main() {
231
+ /// # test_insert().await;
232
+ /// # test_update().await;
233
+ /// # test_others().await;
234
+ /// # }
235
+ /// ```
236
+ ///
126
237
/// ## Rate limiting using a token bucket
127
238
///
128
239
/// Many applications and systems have constraints on the rate at which certain
0 commit comments