mod test_utils;
use std::{
env,
fs::{create_dir_all, remove_file, OpenOptions},
io::Write,
path::{Path, PathBuf},
process::{Command, Stdio},
};
const CTRL_INDEX: &str = "CTRL_INDEX";
const CRASHFILE: &str = "CRASHFILE";
const RUNS: usize = 3;
const MILLIS: u64 = 50;
#[test]
fn main() {
match env::var(CTRL_INDEX).as_ref() {
Err(_) => {
controller();
}
Ok(s) if s == "parent" => {
parent(false);
}
Ok(s) if s == "parent_panic" => {
parent(true);
}
Ok(s) if s == "child" => {
child(false);
}
Ok(s) if s == "child_panic" => {
child(true);
}
Ok(s) => panic!("Unexpected value {s}"),
}
}
fn controller() {
let progpath = env::args().next().unwrap();
create_dir_all(crashdump_file().parent().unwrap()).unwrap();
remove_file(crashdump_file()).ok();
let mut child = Command::new(progpath.clone())
.env(CTRL_INDEX, "parent")
.stdout(Stdio::null())
.stderr(Stdio::piped())
.spawn()
.unwrap();
assert!(child.wait().expect("failed to wait on child").success());
std::thread::sleep(std::time::Duration::from_millis(200));
assert!(!Path::new(&crashdump_file()).try_exists().unwrap());
let mut child = Command::new(progpath)
.env(CTRL_INDEX, "parent_panic")
.stdout(Stdio::null())
.stderr(Stdio::piped())
.spawn()
.unwrap();
assert!(child.wait().expect("failed to wait on child").success());
std::thread::sleep(std::time::Duration::from_millis(200));
assert!(Path::new(&crashdump_file()).try_exists().unwrap());
}
fn parent(panic: bool) {
let progpath = std::env::args().next().unwrap();
#[allow(clippy::zombie_processes)]
Command::new(progpath)
.env(CTRL_INDEX, if panic { "child_panic" } else { "child" })
.stdout(Stdio::null())
.stderr(Stdio::piped())
.spawn()
.unwrap();
}
fn child(panic: bool) {
let original_hook = std::panic::take_hook();
std::panic::set_hook(Box::new(move |panic| {
let backtrace = std::backtrace::Backtrace::capture();
let mut file = OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(crashdump_file())
.unwrap();
file.write_all(format!("Panic occured:\n{}\n{}\n", panic, backtrace).as_bytes())
.unwrap();
file.flush().unwrap();
original_hook(panic);
}));
let _logger = flexi_logger::Logger::try_with_str("info")
.unwrap()
.log_to_stderr()
.panic_if_error_channel_is_broken(panic)
.start()
.unwrap();
for i in 0..RUNS {
log::info!("log test ({i})"); std::thread::sleep(std::time::Duration::from_millis(MILLIS));
}
}
fn crashdump_file() -> PathBuf {
match std::env::var(CRASHFILE) {
Ok(s) => Path::new(&s).to_path_buf(),
Err(_) => {
let progname = PathBuf::from(std::env::args().next().unwrap())
.file_name()
.unwrap()
.to_string_lossy()
.to_string();
let path = test_utils::file(&format!("./{progname}.log"));
std::env::set_var(CRASHFILE, &path);
path
}
}
}