Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Intoduce timeout as a feature #70

Merged
merged 3 commits into from Jul 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 12 additions & 6 deletions serial_test/Cargo.toml
Expand Up @@ -14,28 +14,34 @@ keywords = ["sequential"]
lazy_static = "1.2"
parking_lot = "^0.12"
serial_test_derive = { version = "~0.8.0", path = "../serial_test_derive" }
fslock = {version = "0.2", optional = true}
document-features = {version = "0.2", optional=true}
log = {version = "0.4", optional = true}
futures = {version = "^0.3", default_features = false, features = ["executor"] }
fslock = { version = "0.2", optional = true }
document-features = { version = "0.2", optional = true }
log = { version = "0.4", optional = true }
futures = { version = "^0.3", default_features = false, features = [
"executor",
] }

[dev-dependencies]
itertools = "0.10"
tokio = { version = "^1.17", features = ["macros", "rt"] }

[features]
default = ["logging"]
default = ["logging", "timeout"]

## Switches on debug logging (and requires the `log` package)
logging = ["log"]

## The file_locks feature unlocks the `file_serial`/`file_parallel` macros
file_locks = ["fslock"]

## The `timeout` feature lets tests time out after a certain amount of time
## if not enabled tests will wait indefinitely to be started
timeout = []

docsrs = ["document-features"]

# docs.rs-specific configuration
[package.metadata.docs.rs]
all-features = true
# defines the configuration attribute `docsrs`
rustdoc-args = ["--cfg", "docsrs"]
rustdoc-args = ["--cfg", "docsrs"]
36 changes: 24 additions & 12 deletions serial_test/src/code_lock.rs
Expand Up @@ -2,13 +2,14 @@ use crate::rwlock::{Locks, MutexGuardWrapper};
use lazy_static::lazy_static;
#[cfg(feature = "logging")]
use log::debug;
use parking_lot::{Mutex, RwLock};
use parking_lot::RwLock;
#[cfg(feature = "timeout")]
use std::time::Instant;
use std::{
cell::RefCell,
collections::HashMap,
ops::{Deref, DerefMut},
sync::{atomic::AtomicU32, Arc},
time::{Duration, Instant},
time::Duration,
};

pub(crate) struct UniqueReentrantMutex {
Expand Down Expand Up @@ -46,11 +47,14 @@ impl UniqueReentrantMutex {
lazy_static! {
pub(crate) static ref LOCKS: Arc<RwLock<HashMap<String, UniqueReentrantMutex>>> =
Arc::new(RwLock::new(HashMap::new()));
static ref MAX_WAIT: Arc<Mutex<RefCell<Duration>>> =
Arc::new(Mutex::new(RefCell::new(Duration::from_secs(60))));
static ref MUTEX_ID: Arc<AtomicU32> = Arc::new(AtomicU32::new(1));
}

#[cfg(feature = "timeout")]
lazy_static! {
static ref MAX_WAIT: Arc<RwLock<Duration>> = Arc::new(RwLock::new(Duration::from_secs(60)));
}

impl Default for UniqueReentrantMutex {
fn default() -> Self {
Self {
Expand All @@ -67,20 +71,25 @@ impl Default for UniqueReentrantMutex {
///
/// However, sometimes if you've got a *lot* of serial tests it might theoretically not be enough,
/// hence this method.
///
/// This function is only available when the `timeout` feature is enabled.
#[cfg(feature = "timeout")]
pub fn set_max_wait(max_wait: Duration) {
MAX_WAIT.lock().replace(max_wait);
*MAX_WAIT.write() = max_wait;
}

#[cfg(feature = "timeout")]
pub(crate) fn wait_duration() -> Duration {
*MAX_WAIT.lock().borrow()
*MAX_WAIT.read()
}

pub(crate) fn check_new_key(name: &str) {
#[cfg(feature = "timeout")]
let start = Instant::now();
loop {
#[cfg(feature = "logging")]
#[cfg(all(feature = "logging", feature = "timeout"))]
{
let duration = Instant::now() - start;
let duration = start.elapsed();
debug!("Waiting for '{}' {:?}", name, duration);
}
// Check if a new key is needed. Just need a read lock, which can be done in sync with everyone else
Expand All @@ -105,9 +114,12 @@ pub(crate) fn check_new_key(name: &str) {
// If the try_lock fails, then go around the loop again
// Odds are another test was also locking on the write and has now written the key

let duration = Instant::now() - start;
if duration >= wait_duration() {
panic!("check_new_key timed out!");
#[cfg(feature = "timeout")]
{
let duration = start.elapsed();
if duration > wait_duration() {
panic!("Timeout waiting for '{}' {:?}", name, duration);
}
}
}
}
1 change: 1 addition & 0 deletions serial_test/src/lib.rs
Expand Up @@ -63,6 +63,7 @@ mod parallel_file_lock;
#[cfg(feature = "file_locks")]
mod serial_file_lock;

#[cfg(feature = "timeout")]
pub use code_lock::set_max_wait;
pub use parallel_code_lock::{
local_async_parallel_core, local_async_parallel_core_with_return, local_parallel_core,
Expand Down
9 changes: 5 additions & 4 deletions serial_test/src/serial_code_lock.rs
Expand Up @@ -53,13 +53,14 @@ pub async fn local_async_serial_core(name: &str, fut: impl std::future::Future<O
#[allow(clippy::print_stdout)]
mod tests {
use super::local_serial_core;
use crate::code_lock::{check_new_key, wait_duration, LOCKS};
use crate::code_lock::{check_new_key, LOCKS};
use itertools::Itertools;
use parking_lot::RwLock;
use std::{
ops::Deref,
sync::{Arc, Barrier},
thread,
time::Duration,
};

#[test]
Expand All @@ -79,12 +80,12 @@ mod tests {
check_new_key("foo");
{
let unlock = local_locks
.try_read_recursive_for(wait_duration())
.try_read_recursive_for(Duration::from_secs(1))
.expect("read lock didn't work");
let mutex = unlock.deref().get("foo").unwrap();

let mut ptr_guard = local_ptrs
.try_write_for(wait_duration())
.try_write_for(Duration::from_secs(1))
.expect("write lock didn't work");
ptr_guard.push(mutex.id);
}
Expand All @@ -96,7 +97,7 @@ mod tests {
thread.join().expect("thread join worked");
}
let ptrs_read_lock = ptrs
.try_read_recursive_for(wait_duration())
.try_read_recursive_for(Duration::from_secs(1))
.expect("ptrs read work");
assert_eq!(ptrs_read_lock.len(), count);
println!("{:?}", ptrs_read_lock);
Expand Down