66 lines
2.0 KiB
Rust
66 lines
2.0 KiB
Rust
//! Harte Frist auch für blockierende Bibliotheksaufrufe im Testprozess.
|
|
use std::sync::mpsc::{self, Sender};
|
|
use std::time::Duration;
|
|
|
|
pub struct Prozessfrist(Sender<()>);
|
|
|
|
impl Prozessfrist {
|
|
pub fn neu(name: &str, dauer: Duration) -> Self {
|
|
let (tx, rx) = mpsc::channel();
|
|
let name = name.to_string();
|
|
std::thread::spawn(move || {
|
|
if matches!(rx.recv_timeout(dauer), Err(mpsc::RecvTimeoutError::Timeout)) {
|
|
eprintln!("Prozessfrist überschritten: {name}");
|
|
// Auch ein in redb blockierter Test lässt sich so sicher beenden.
|
|
std::process::exit(124);
|
|
}
|
|
});
|
|
Self(tx)
|
|
}
|
|
}
|
|
|
|
impl Drop for Prozessfrist {
|
|
fn drop(&mut self) {
|
|
let _ = self.0.send(());
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn frist_beendet_blockierten_testprozess() {
|
|
use std::process::{Command, Stdio};
|
|
use std::time::Instant;
|
|
|
|
const MARKER: &str = "TB_TEST_FRIST_KIND";
|
|
if std::env::var_os(MARKER).is_some() {
|
|
let _frist = Prozessfrist::neu("absichtlich blockiert", Duration::from_millis(100));
|
|
loop {
|
|
std::thread::park();
|
|
}
|
|
}
|
|
let mut child = Command::new(std::env::current_exe().unwrap())
|
|
.arg("frist_beendet_blockierten_testprozess")
|
|
.arg("--nocapture")
|
|
.current_dir(std::env::temp_dir())
|
|
.env(MARKER, "1")
|
|
.stdin(Stdio::null())
|
|
.stdout(Stdio::piped())
|
|
.stderr(Stdio::piped())
|
|
.spawn()
|
|
.unwrap();
|
|
let start = Instant::now();
|
|
loop {
|
|
if child.try_wait().unwrap().is_some() {
|
|
break;
|
|
}
|
|
if start.elapsed() > Duration::from_secs(5) {
|
|
let _ = child.kill();
|
|
let _ = child.wait();
|
|
panic!("Prozessfrist hat den blockierten Test nicht beendet");
|
|
}
|
|
std::thread::sleep(Duration::from_millis(10));
|
|
}
|
|
let out = child.wait_with_output().unwrap();
|
|
assert_eq!(out.status.code(), Some(124), "{out:?}");
|
|
assert!(String::from_utf8_lossy(&out.stderr).contains("absichtlich blockiert"));
|
|
}
|