Skip to content

Commit

Permalink
Add parameter for timeout on a per test level
Browse files Browse the repository at this point in the history
Signed-off-by: Heinz N. Gies <heinz@licenser.net>
  • Loading branch information
Licenser committed Jun 29, 2022
1 parent 479746c commit 7d0a595
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 97 deletions.
31 changes: 4 additions & 27 deletions serial_test/src/code_lock.rs
@@ -1,7 +1,7 @@
use crate::rwlock::{Locks, MutexGuardWrapper};
use lazy_static::lazy_static;
#[cfg(feature = "logging")]
use log::{debug, warn};
use log::debug;
use parking_lot::RwLock;
use std::{
collections::HashMap,
Expand Down Expand Up @@ -48,11 +48,6 @@ lazy_static! {
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 @@ -62,25 +57,7 @@ impl Default for UniqueReentrantMutex {
}
}

/// Sets the maximum amount of time the serial locks will wait to unlock.
/// By default, this is set to 60 seconds, which is almost always much longer than is needed.
/// This is deliberately set high to try and avoid situations where we accidentally hit the limits
/// but is set at all so we can timeout rather than hanging forever.
///
/// 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.write() = max_wait;
}

pub(crate) fn wait_duration() -> Duration {
*MAX_WAIT.read()
}

pub(crate) fn check_new_key(name: &str) {
pub(crate) fn check_new_key(name: &str, max_wait: Option<Duration>) {
let start = Instant::now();
loop {
#[cfg(all(feature = "logging", feature = "timeout"))]
Expand Down Expand Up @@ -111,9 +88,9 @@ pub(crate) fn check_new_key(name: &str) {
// Odds are another test was also locking on the write and has now written the key

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

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,
local_parallel_core_with_return,
Expand Down
22 changes: 14 additions & 8 deletions serial_test/src/parallel_code_lock.rs
Expand Up @@ -2,14 +2,15 @@

use crate::code_lock::{check_new_key, LOCKS};
use futures::FutureExt;
use std::{ops::Deref, panic};
use std::{ops::Deref, panic, time::Duration};

#[doc(hidden)]
pub fn local_parallel_core_with_return<E>(
name: &str,
max_wait: Option<Duration>,
function: fn() -> Result<(), E>,
) -> Result<(), E> {
check_new_key(name);
check_new_key(name, max_wait);

let unlock = LOCKS.read_recursive();
unlock.deref()[name].start_parallel();
Expand All @@ -24,8 +25,8 @@ pub fn local_parallel_core_with_return<E>(
}

#[doc(hidden)]
pub fn local_parallel_core(name: &str, function: fn()) {
check_new_key(name);
pub fn local_parallel_core(name: &str, max_wait: Option<Duration>, function: fn()) {
check_new_key(name, max_wait);

let unlock = LOCKS.read_recursive();
unlock.deref()[name].start_parallel();
Expand All @@ -41,9 +42,10 @@ pub fn local_parallel_core(name: &str, function: fn()) {
#[doc(hidden)]
pub async fn local_async_parallel_core_with_return<E>(
name: &str,
max_wait: Option<Duration>,
fut: impl std::future::Future<Output = Result<(), E>> + panic::UnwindSafe,
) -> Result<(), E> {
check_new_key(name);
check_new_key(name, max_wait);

let unlock = LOCKS.read_recursive();
unlock.deref()[name].start_parallel();
Expand All @@ -60,9 +62,10 @@ pub async fn local_async_parallel_core_with_return<E>(
#[doc(hidden)]
pub async fn local_async_parallel_core(
name: &str,
max_wait: Option<Duration>,
fut: impl std::future::Future<Output = ()> + panic::UnwindSafe,
) {
check_new_key(name);
check_new_key(name, max_wait);

let unlock = LOCKS.read_recursive();
unlock.deref()[name].start_parallel();
Expand All @@ -84,7 +87,7 @@ mod tests {
#[test]
fn unlock_on_assert_sync_without_return() {
let _ = panic::catch_unwind(|| {
local_parallel_core("unlock_on_assert_sync_without_return", || {
local_parallel_core("unlock_on_assert_sync_without_return", None, || {
assert!(false);
})
});
Expand All @@ -100,6 +103,7 @@ mod tests {
let _ = panic::catch_unwind(|| {
local_parallel_core_with_return(
"unlock_on_assert_sync_with_return",
None,
|| -> Result<(), Error> {
assert!(false);
Ok(())
Expand All @@ -119,7 +123,8 @@ mod tests {
assert!(false);
}
async fn call_serial_test_fn() {
local_async_parallel_core("unlock_on_assert_async_without_return", demo_assert()).await
local_async_parallel_core("unlock_on_assert_async_without_return", None, demo_assert())
.await
}
// as per https://stackoverflow.com/a/66529014/320546
let _ = panic::catch_unwind(|| {
Expand All @@ -145,6 +150,7 @@ mod tests {
async fn call_serial_test_fn() {
local_async_parallel_core_with_return(
"unlock_on_assert_async_with_return",
None,
demo_assert(),
)
.await;
Expand Down
33 changes: 20 additions & 13 deletions serial_test/src/serial_code_lock.rs
@@ -1,14 +1,15 @@
#![allow(clippy::await_holding_lock)]

use crate::code_lock::{check_new_key, LOCKS};
use std::ops::Deref;
use std::{ops::Deref, time::Duration};

#[doc(hidden)]
pub fn local_serial_core_with_return<E>(
name: &str,
max_wait: Option<Duration>,
function: fn() -> Result<(), E>,
) -> Result<(), E> {
check_new_key(name);
check_new_key(name, max_wait);

let unlock = LOCKS.read_recursive();
// _guard needs to be named to avoid being instant dropped
Expand All @@ -17,8 +18,8 @@ pub fn local_serial_core_with_return<E>(
}

#[doc(hidden)]
pub fn local_serial_core(name: &str, function: fn()) {
check_new_key(name);
pub fn local_serial_core(name: &str, max_wait: Option<Duration>, function: fn()) {
check_new_key(name, max_wait);

let unlock = LOCKS.read_recursive();
// _guard needs to be named to avoid being instant dropped
Expand All @@ -29,9 +30,10 @@ pub fn local_serial_core(name: &str, function: fn()) {
#[doc(hidden)]
pub async fn local_async_serial_core_with_return<E>(
name: &str,
max_wait: Option<Duration>,
fut: impl std::future::Future<Output = Result<(), E>>,
) -> Result<(), E> {
check_new_key(name);
check_new_key(name, max_wait);

let unlock = LOCKS.read_recursive();
// _guard needs to be named to avoid being instant dropped
Expand All @@ -40,8 +42,12 @@ pub async fn local_async_serial_core_with_return<E>(
}

#[doc(hidden)]
pub async fn local_async_serial_core(name: &str, fut: impl std::future::Future<Output = ()>) {
check_new_key(name);
pub async fn local_async_serial_core(
name: &str,
max_wait: Option<Duration>,
fut: impl std::future::Future<Output = ()>,
) {
check_new_key(name, max_wait);

let unlock = LOCKS.read_recursive();
// _guard needs to be named to avoid being instant dropped
Expand All @@ -53,13 +59,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 @@ -76,15 +83,15 @@ mod tests {
let c = barrier.clone();
threads.push(thread::spawn(move || {
c.wait();
check_new_key("foo");
check_new_key("foo", None);
{
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 +103,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 All @@ -106,7 +113,7 @@ mod tests {
#[test]
fn unlock_on_assert() {
let _ = std::panic::catch_unwind(|| {
local_serial_core("assert", || {
local_serial_core("assert", None, || {
assert!(false);
})
});
Expand Down
2 changes: 1 addition & 1 deletion serial_test/tests/tests.rs
Expand Up @@ -2,7 +2,7 @@ use serial_test::local_serial_core;

#[test]
fn test_empty_serial_call() {
local_serial_core("beta", || {
local_serial_core("beta", None, || {
println!("Bar");
});
}

0 comments on commit 7d0a595

Please sign in to comment.