From ee5c97f3e5947dc6f6862c0dce208ffcab0f0a16 Mon Sep 17 00:00:00 2001 From: Kotauskas Date: Tue, 2 Apr 2024 22:29:28 +0300 Subject: [PATCH] Add watchdog timer for tests --- tests/local_socket.rs | 26 +++++---- tests/named_pipe.rs | 13 +++-- tests/os/unix/local_socket_mode.rs | 4 +- .../local_socket_security_descriptor.rs | 8 ++- tests/tokio_local_socket.rs | 55 ++++++++++--------- tests/tokio_named_pipe.rs | 21 ++++--- tests/util/mod.rs | 11 ++-- tests/util/tokio.rs | 12 +++- tests/util/wdt.rs | 21 +++++++ 9 files changed, 108 insertions(+), 63 deletions(-) create mode 100644 tests/util/wdt.rs diff --git a/tests/local_socket.rs b/tests/local_socket.rs index 86e4bd6..7e4f0fb 100644 --- a/tests/local_socket.rs +++ b/tests/local_socket.rs @@ -7,26 +7,28 @@ use crate::{local_socket::NameTypeSupport, tests::util::*}; fn test_stream(id: &'static str, path: bool) -> TestResult { use stream::*; - testinit(); - let scl = |s, n| server(id, handle_client, s, n, path); - drive_server_and_multiple_clients(scl, client)?; - Ok(()) + test_wrapper(move || { + let scl = |s, n| server(id, handle_client, s, n, path); + drive_server_and_multiple_clients(scl, client)?; + Ok(()) + }) } fn test_no_server(id: &'static str, path: bool) -> TestResult { - testinit(); - no_server::run_and_verify_error(id, path) + test_wrapper(move || no_server::run_and_verify_error(id, path)) } macro_rules! tests { (@querymethod true $e:expr) => { NameTypeSupport::fs_supported($e) }; (@querymethod false $e:expr) => { NameTypeSupport::ns_supported($e) }; - (@body $fn:ident $path:ident) => {{ - if tests!(@querymethod $path NameTypeSupport::query()) { - $fn(make_id!(), $path)?; - } - Ok(()) - }}; + (@body $fn:ident $path:ident) => { + test_wrapper(|| { + if tests!(@querymethod $path NameTypeSupport::query()) { + $fn(make_id!(), $path)?; + } + Ok(()) + }) + }; ($fn:ident $nm:ident $path:ident) => { #[test] fn $nm() -> TestResult { tests!(@body $fn $path) } diff --git a/tests/named_pipe.rs b/tests/named_pipe.rs index 37339e7..4ebfb5e 100644 --- a/tests/named_pipe.rs +++ b/tests/named_pipe.rs @@ -17,12 +17,13 @@ macro_rules! matrix { #[test] fn $nm() -> TestResult { use $mod::*; - testinit(); - let server = matrix!(@dir_s $ty); - drive_server_and_multiple_clients( - |ns, nc| server(make_id!(), ns, nc), - matrix!(@dir_c $ty), - ) + test_wrapper(|| { + let server = matrix!(@dir_s $ty); + drive_server_and_multiple_clients( + |ns, nc| server(make_id!(), ns, nc), + matrix!(@dir_c $ty), + ) + }) } )+}; } diff --git a/tests/os/unix/local_socket_mode.rs b/tests/os/unix/local_socket_mode.rs index 2c8994c..84721c1 100644 --- a/tests/os/unix/local_socket_mode.rs +++ b/tests/os/unix/local_socket_mode.rs @@ -32,11 +32,11 @@ fn test_inner(path: bool) -> TestResult { #[test] fn local_socket_file_mode() -> TestResult { - test_inner(true) + test_wrapper(|| test_inner(true)) } #[cfg(any(target_os = "linux", target_os = "android"))] #[test] fn local_socket_namespaced_mode() -> TestResult { - test_inner(false) + test_wrapper(|| test_inner(false)) } diff --git a/tests/os/windows/local_socket_security_descriptor.rs b/tests/os/windows/local_socket_security_descriptor.rs index 946768e..a5abbc8 100644 --- a/tests/os/windows/local_socket_security_descriptor.rs +++ b/tests/os/windows/local_socket_security_descriptor.rs @@ -94,8 +94,7 @@ fn get_self_exe(obuf: &mut [MaybeUninit]) -> io::Result<&U16CStr> { }) } -#[test] -fn local_socket_security_descriptor() -> TestResult { +fn test_main() -> TestResult { let sd = { let mut pathbuf = [MaybeUninit::uninit(); MAX_PATH as _]; let path: OsString = get_self_exe(&mut pathbuf) @@ -137,3 +136,8 @@ fn local_socket_security_descriptor() -> TestResult { Ok(()) } + +#[test] +fn local_socket_security_descriptor() -> TestResult { + test_wrapper(test_main) +} diff --git a/tests/tokio_local_socket.rs b/tests/tokio_local_socket.rs index 67afe3a..c525a2e 100644 --- a/tests/tokio_local_socket.rs +++ b/tests/tokio_local_socket.rs @@ -6,14 +6,13 @@ mod stream; use crate::{ local_socket::{tokio::Stream, Name, NameTypeSupport}, - tests::util::{self, testinit, TestResult}, + tests::util::{self, tokio::test_wrapper, TestResult}, }; use std::{future::Future, pin::Pin, sync::Arc}; #[allow(clippy::type_complexity)] async fn test_stream(id: &'static str, split: bool, path: bool) -> TestResult { use stream::*; - testinit(); type Fut = Pin + Send + 'static>>; type F = Box Fut + Send + Sync>; let hcl: F = if split { @@ -33,20 +32,22 @@ async fn test_stream(id: &'static str, split: bool, path: bool) -> TestResult { macro_rules! matrix { (@querymethod true $e:expr) => { NameTypeSupport::fs_supported($e) }; (@querymethod false $e:expr) => { NameTypeSupport::ns_supported($e) }; - (@body $split:ident $path:ident) => {{ - if matrix!(@querymethod $path NameTypeSupport::query()) { - test_stream(make_id!(), $split, $path).await?; - } - Ok(()) - }}; + (@body $split:ident $path:ident) => { + test_wrapper(async { + if matrix!(@querymethod $path NameTypeSupport::query()) { + test_stream(make_id!(), $split, $path).await?; + } + Ok(()) + }) + }; ($nm:ident false $path:ident) => { - #[tokio::test] - async fn $nm() -> TestResult { matrix!(@body false $path) } + #[test] + fn $nm() -> TestResult { matrix!(@body false $path) } }; ($nm:ident true $path:ident) => { - #[tokio::test] + #[test] #[cfg(not(windows))] - async fn $nm() -> TestResult { matrix!(@body true $path) } + fn $nm() -> TestResult { matrix!(@body true $path) } }; ($($nm:ident $split:ident $path:ident)+) => { $(matrix!($nm $split $path);)+ }; } @@ -58,19 +59,21 @@ matrix! { stream_namespaced_split true false } -#[tokio::test] -async fn no_server_file() -> TestResult { - testinit(); - if NameTypeSupport::query().fs_supported() { - no_server::run_and_verify_error(true).await?; - } - Ok(()) +#[test] +fn no_server_file() -> TestResult { + test_wrapper(async { + if NameTypeSupport::query().fs_supported() { + no_server::run_and_verify_error(true).await?; + } + Ok(()) + }) } -#[tokio::test] -async fn no_server_namespaced() -> TestResult { - testinit(); - if NameTypeSupport::query().ns_supported() { - no_server::run_and_verify_error(false).await?; - } - Ok(()) +#[test] +fn no_server_namespaced() -> TestResult { + test_wrapper(async { + if NameTypeSupport::query().ns_supported() { + no_server::run_and_verify_error(false).await?; + } + Ok(()) + }) } diff --git a/tests/tokio_named_pipe.rs b/tests/tokio_named_pipe.rs index ea4a2f5..a16fa63 100644 --- a/tests/tokio_named_pipe.rs +++ b/tests/tokio_named_pipe.rs @@ -5,7 +5,9 @@ mod bytes; use crate::{ os::windows::named_pipe::PipeListenerOptions, tests::util::{ - listen_and_pick_name, testinit, tokio::drive_server_and_multiple_clients, TestResult, + listen_and_pick_name, + tokio::{drive_server_and_multiple_clients, test_wrapper}, + TestResult, }, }; use color_eyre::eyre::WrapErr; @@ -18,15 +20,16 @@ macro_rules! matrix { (@dir_s duplex) => {server_duplex}; (@dir_s stc) => {server_stc}; (@dir_s cts) => {server_cts}; (@dir_c duplex) => {client_duplex}; (@dir_c stc) => {client_stc}; (@dir_c cts) => {client_cts}; ($($mod:ident $ty:ident $nm:ident)+) => {$( - #[tokio::test] - async fn $nm() -> TestResult { + #[test] + fn $nm() -> TestResult { use $mod::*; - testinit(); - let server = matrix!(@dir_s $ty); - drive_server_and_multiple_clients( - move |ns, nc| server(make_id!(), ns, nc), - matrix!(@dir_c $ty), - ).await + test_wrapper(async { + let server = matrix!(@dir_s $ty); + drive_server_and_multiple_clients( + move |ns, nc| server(make_id!(), ns, nc), + matrix!(@dir_c $ty), + ).await + }) } )+}; } diff --git a/tests/util/mod.rs b/tests/util/mod.rs index fc7db15..1aa5cbd 100644 --- a/tests/util/mod.rs +++ b/tests/util/mod.rs @@ -2,14 +2,14 @@ //! it. #![allow(dead_code, unused_macros)] -mod choke; - -mod drive; #[macro_use] mod eyre; -mod xorshift; #[macro_use] mod namegen; +mod choke; +mod drive; +mod wdt; +mod xorshift; #[allow(unused_imports)] pub use {drive::*, eyre::*, namegen::*, xorshift::*}; @@ -23,8 +23,9 @@ const NUM_CONCURRENT_CLIENTS: u32 = 6; use color_eyre::eyre::WrapErr; use std::{fmt::Arguments, io, sync::Arc}; -pub fn testinit() { +pub fn test_wrapper(f: impl (FnOnce() -> TestResult) + Send + 'static) -> TestResult { eyre::install(); + self::wdt::run_under_wachdog(f) } pub fn message(msg: Option>, server: bool, terminator: Option) -> Box { diff --git a/tests/util/tokio.rs b/tests/util/tokio.rs index b64fb4a..fb48b52 100644 --- a/tests/util/tokio.rs +++ b/tests/util/tokio.rs @@ -1,4 +1,4 @@ -use super::{TestResult, NUM_CLIENTS, NUM_CONCURRENT_CLIENTS}; +use super::{TestResult, WrapErrExt, NUM_CLIENTS, NUM_CONCURRENT_CLIENTS}; use color_eyre::eyre::{bail, Context}; use std::{future::Future, sync::Arc}; use tokio::{ @@ -9,6 +9,16 @@ use tokio::{ task, try_join, }; +pub fn test_wrapper(f: impl Future + Send + 'static) -> TestResult { + super::test_wrapper(|| { + let rt = tokio::runtime::Builder::new_current_thread() + .enable_io() + .build() + .opname("Tokio runtime spawn")?; + rt.block_on(f) + }) +} + /// Waits for the leader closure to reach a point where it sends a message for the follower closure, /// then runs the follower. Captures Eyre errors on both sides and panics if any occur, reporting /// which side produced the error. diff --git a/tests/util/wdt.rs b/tests/util/wdt.rs new file mode 100644 index 0000000..8d5865e --- /dev/null +++ b/tests/util/wdt.rs @@ -0,0 +1,21 @@ +use super::{TestResult, WrapErrExt}; +use color_eyre::eyre::bail; +use std::{sync::mpsc, thread, time::Duration}; + +const TIMEOUT: Duration = Duration::from_secs(2); + +pub fn run_under_wachdog(f: impl (FnOnce() -> TestResult) + Send + 'static) -> TestResult { + let (killswitch, timeout_joiner) = mpsc::channel(); + let joiner = thread::Builder::new() + .name("test main (under watchdog)".to_owned()) + .spawn(move || { + let ret = f(); + let _ = killswitch.send(()); + ret + }) + .opname("watchdog scrutinee thread spawn")?; + if let Err(mpsc::RecvTimeoutError::Timeout) = timeout_joiner.recv_timeout(TIMEOUT) { + bail!("watchdog timer has run out"); + } + joiner.join().unwrap() +}