blob: 62788612442b225aca6d0e0e4d977f9da26d83e7 [file] [log] [blame]
//! Tests for the cleanup module.
//!
//! The tests work like this:
//!
//! * The register an alarm, to fail if anything takes too long (which is very much possible here).
//! * A fork is done, with the child registering a signal with a NOP and cleanup operation (one or
//! the other).
//! * The child puts some kind of infinite loop or sleep inside itself, so it never actually
//! terminates on the first, but would terminate after the signal.
#![cfg(not(windows))] // Forks don't work on Windows, but windows has the same implementation.
extern crate libc;
extern crate signal_hook;
use std::io::Error;
use std::ptr;
use std::thread;
use std::time::Duration;
fn do_test<C: FnOnce()>(child: C) {
unsafe {
libc::alarm(10); // Time out the test after 10 seconds and get it killed.
match libc::fork() {
-1 => panic!("Fork failed: {}", Error::last_os_error()),
0 => {
child();
loop {
thread::sleep(Duration::from_secs(1));
}
}
pid => {
// Give the child some time to register signals and stuff
// We could actually signal that the child is ready by it eg. closing STDOUT, but
// this is just a test so we don't really bother.
thread::sleep(Duration::from_millis(250));
libc::kill(pid, libc::SIGTERM);
// Wait a small bit to make sure the signal got delivered.
thread::sleep(Duration::from_millis(50));
// The child is still running, because the first signal got "handled" by being
// ignored.
let terminated = libc::waitpid(pid, ptr::null_mut(), libc::WNOHANG);
assert_eq!(0, terminated, "Process {} terminated prematurely", pid);
// But it terminates on the second attempt (we do block on wait here).
libc::kill(pid, libc::SIGTERM);
let terminated = libc::waitpid(pid, ptr::null_mut(), 0);
assert_eq!(pid, terminated);
}
}
}
}
/// Use automatic cleanup inside the signal handler to get rid of old signals, the aggressive way.
#[test]
fn cleanup_inside_signal() {
fn hook() {
unsafe { signal_hook::register(libc::SIGTERM, || ()).unwrap() };
signal_hook::cleanup::register(libc::SIGTERM, vec![libc::SIGTERM]).unwrap();
}
do_test(hook);
}
/// Manually remove the signal handler just after receiving the signal but before going into an
/// infinite loop.
#[test]
fn cleanup_after_signal() {
fn hook() {
let signals = signal_hook::iterator::Signals::new(&[libc::SIGTERM]).unwrap();
assert_eq!(Some(libc::SIGTERM), signals.into_iter().next());
signal_hook::cleanup::cleanup_signal(libc::SIGTERM).unwrap();
}
do_test(hook);
}