serial_test/
lib.rs

1//! # serial_test
2//! `serial_test` allows for the creation of serialised Rust tests using the [serial](attr.serial.html) attribute
3//! e.g.
4//! ````
5//! #[test]
6//! #[serial]
7//! fn test_serial_one() {
8//!   // Do things
9//! }
10//!
11//! #[test]
12//! #[serial]
13//! fn test_serial_another() {
14//!   // Do things
15//! }
16//! ````
17//! Multiple tests with the [serial](attr.serial.html) attribute are guaranteed to be executed in serial. Ordering
18//! of the tests is not guaranteed however.
19
20use lazy_static::lazy_static;
21use parking_lot::ReentrantMutex;
22use std::collections::HashMap;
23use std::ops::{Deref, DerefMut};
24use std::sync::{Arc, RwLock};
25
26lazy_static! {
27    static ref LOCKS: Arc<RwLock<HashMap<String, ReentrantMutex<()>>>> =
28        Arc::new(RwLock::new(HashMap::new()));
29}
30
31fn check_new_key(name: &str) {
32    // Check if a new key is needed. Just need a read lock, which can be done in sync with everyone else
33    let new_key = {
34        let unlock = LOCKS.read().unwrap();
35        !unlock.deref().contains_key(name)
36    };
37    if new_key {
38        // This is the rare path, which avoids the multi-writer situation mostly
39        LOCKS
40            .write()
41            .unwrap()
42            .deref_mut()
43            .insert(name.to_string(), ReentrantMutex::new(()));
44    }
45}
46
47#[doc(hidden)]
48pub fn serial_core_with_return<E>(name: &str, function: fn() -> Result<(), E>) -> Result<(), E> {
49    check_new_key(name);
50
51    let unlock = LOCKS.read().unwrap();
52    // _guard needs to be named to avoid being instant dropped
53    let _guard = unlock.deref()[name].lock();
54    function()
55}
56
57#[doc(hidden)]
58pub fn serial_core(name: &str, function: fn()) {
59    check_new_key(name);
60
61    let unlock = LOCKS.read().unwrap();
62    // _guard needs to be named to avoid being instant dropped
63    let _guard = unlock.deref()[name].lock();
64    function();
65}
66
67#[doc(hidden)]
68pub async fn async_serial_core_with_return<E>(
69    name: &str,
70    fut: impl std::future::Future<Output = Result<(), E>>,
71) -> Result<(), E> {
72    check_new_key(name);
73
74    let unlock = LOCKS.read().unwrap();
75    // _guard needs to be named to avoid being instant dropped
76    let _guard = unlock.deref()[name].lock();
77    fut.await
78}
79
80#[doc(hidden)]
81pub async fn async_serial_core(name: &str, fut: impl std::future::Future<Output = ()>) {
82    check_new_key(name);
83
84    let unlock = LOCKS.read().unwrap();
85    // _guard needs to be named to avoid being instant dropped
86    let _guard = unlock.deref()[name].lock();
87    fut.await
88}
89
90// Re-export #[serial].
91#[allow(unused_imports)]
92pub use serial_test_derive::serial;