use std::time::{Duration, Instant};
use crate::unix::{JobExt, PipelineExt};
use crate::{Exec, ExecExt, ExitStatus, Redirection};
#[test]
fn err_terminate() {
let job = Exec::cmd("sleep").arg("5").start().unwrap();
assert!(job.poll().is_none());
job.terminate().unwrap();
assert!(job.wait().unwrap().is_killed_by(libc::SIGTERM));
}
#[test]
fn waitpid_echild() {
let job = Exec::cmd("true").start().unwrap();
let pid = job.pid() as i32;
let mut status = 0 as libc::c_int;
let wpid = unsafe { libc::waitpid(pid, &mut status, 0) };
assert_eq!(wpid, pid);
assert_eq!(status, 0);
let exit = job.wait().unwrap();
assert!(exit.code().is_none() && exit.signal().is_none());
}
#[test]
fn send_signal() {
let job = Exec::cmd("sleep").arg("5").start().unwrap();
job.send_signal(libc::SIGUSR1).unwrap();
assert_eq!(job.wait().unwrap().signal(), Some(libc::SIGUSR1));
}
#[test]
fn env_set_all_1() {
let out = Exec::cmd("env")
.env_clear()
.stdout(Redirection::Pipe)
.capture()
.unwrap()
.stdout_str();
assert_eq!(out, "");
}
#[test]
fn env_set_all_2() {
let out = Exec::cmd("env")
.env_clear()
.env("FOO", "bar")
.stdout(Redirection::Pipe)
.capture()
.unwrap()
.stdout_str();
assert_eq!(out.trim_end(), "FOO=bar");
}
#[test]
fn exec_setpgid() {
let job = Exec::cmd("sh")
.args(&["-c", "sleep 10 & wait"])
.setpgid()
.start()
.unwrap();
job.send_signal_group(libc::SIGTERM).unwrap();
assert!(job.wait().unwrap().is_killed_by(libc::SIGTERM));
}
#[test]
fn send_signal_group() {
let job = Exec::cmd("sh")
.args(&["-c", "sleep 10 & wait"])
.setpgid()
.start()
.unwrap();
job.send_signal_group(libc::SIGTERM).unwrap();
assert!(job.wait().unwrap().is_killed_by(libc::SIGTERM));
}
#[test]
fn send_signal_group_after_finish() {
let job = Exec::cmd("true").setpgid().start().unwrap();
job.wait().unwrap();
job.send_signal_group(libc::SIGTERM).unwrap();
}
#[test]
fn kill_process() {
let job = Exec::cmd("sleep").arg("10").start().unwrap();
job.kill().unwrap();
assert!(job.wait().unwrap().is_killed_by(libc::SIGKILL));
}
#[test]
fn kill_vs_terminate() {
let j1 = Exec::cmd("sleep").arg("10").start().unwrap();
j1.terminate().unwrap();
let status1 = j1.wait().unwrap();
let j2 = Exec::cmd("sleep").arg("10").start().unwrap();
j2.kill().unwrap();
let status2 = j2.wait().unwrap();
assert!(status1.is_killed_by(libc::SIGTERM));
assert!(status2.is_killed_by(libc::SIGKILL));
assert_ne!(status1, status2);
}
#[test]
fn exit_status_code() {
assert_eq!(ExitStatus::from_raw(0 << 8).code(), Some(0));
assert_eq!(ExitStatus::from_raw(1 << 8).code(), Some(1));
assert_eq!(ExitStatus::from_raw(42 << 8).code(), Some(42));
assert_eq!(ExitStatus::from_raw(9).code(), None); }
#[test]
fn exit_status_signal() {
assert_eq!(ExitStatus::from_raw(9).signal(), Some(9)); assert_eq!(
ExitStatus::from_raw(libc::SIGTERM).signal(),
Some(libc::SIGTERM)
);
assert_eq!(ExitStatus::from_raw(0 << 8).signal(), None);
assert_eq!(ExitStatus::from_raw(1 << 8).signal(), None);
}
#[test]
fn exit_status_display() {
assert_eq!(ExitStatus::from_raw(0 << 8).to_string(), "exit code 0");
assert_eq!(ExitStatus::from_raw(1 << 8).to_string(), "exit code 1");
assert_eq!(ExitStatus::from_raw(9).to_string(), "signal 9");
}
#[test]
fn started_send_signal() {
let job = Exec::cmd("sleep").arg("100").start().unwrap();
job.send_signal(libc::SIGTERM).unwrap();
let status = job.wait().unwrap();
assert!(status.is_killed_by(libc::SIGTERM));
}
#[test]
fn started_send_signal_group() {
let job = Exec::cmd("sh")
.args(&["-c", "sleep 10 & wait"])
.setpgid()
.start()
.unwrap();
job.send_signal_group(libc::SIGKILL).unwrap();
let status = job.wait().unwrap();
assert!(status.is_killed_by(libc::SIGKILL) || status.is_killed_by(libc::SIGTERM));
}
#[test]
fn pipeline_setpgid() {
let handle = (Exec::cmd("sleep").arg("100") | Exec::cmd("sleep").arg("100"))
.setpgid()
.start()
.unwrap();
assert_eq!(handle.processes.len(), 2);
handle.send_signal_group(libc::SIGTERM).unwrap();
for p in &handle.processes {
let status = p.wait().unwrap();
assert!(status.is_killed_by(libc::SIGTERM));
}
}
#[test]
fn pipeline_setpgid_rejects_exec_setpgid() {
let result = (Exec::cmd("true").setpgid() | Exec::cmd("true")).start();
assert!(result.is_err());
let err = result.unwrap_err();
assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput);
assert!(err.to_string().contains("setpgid"));
}
#[test]
fn null_redirect_does_not_leak_fd() {
let start = Instant::now();
let status = Exec::cmd("sh")
.args(&["-c", "sleep 10 &"])
.stdout(Redirection::Null)
.stderr(Redirection::Null)
.join()
.unwrap();
assert!(status.success());
assert!(
start.elapsed() < Duration::from_secs(5),
"join() took too long, /dev/null fds may have leaked"
);
}