Files

496 lines
12 KiB
Rust
Raw Permalink Normal View History

2023-08-21 10:49:27 +02:00
// This file is part of the uutils coreutils package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
// spell-checker:ignore IAMNOTASIGNAL RTMAX RTMIN SIGRTMAX
2021-04-26 22:14:59 +02:00
use regex::Regex;
use std::os::unix::process::ExitStatusExt;
use std::process::{Child, Command};
2025-03-28 09:51:51 +01:00
use uutests::new_ucmd;
2021-04-26 22:14:59 +02:00
// A child process the tests will try to kill.
struct Target {
child: Child,
killed: bool,
}
impl Target {
// Creates a target that will naturally die after some time if not killed
// fast enough.
// This timeout avoids hanging failing tests.
fn new() -> Self {
Self {
2021-04-26 22:14:59 +02:00
child: Command::new("sleep")
.arg("30")
.spawn()
.expect("cannot spawn target"),
killed: false,
}
}
// Waits for the target to complete and returns the signal it received if any.
fn wait_for_signal(&mut self) -> Option<i32> {
let sig = self.child.wait().expect("cannot wait on target").signal();
self.killed = true;
sig
}
fn pid(&self) -> u32 {
self.child.id()
}
}
impl Drop for Target {
// Terminates this target to avoid littering test boxes with zombi processes
// when a test fails after creating a target but before killing it.
fn drop(&mut self) {
if !self.killed {
self.child.kill().expect("cannot kill target");
}
}
}
2022-09-10 18:38:14 +02:00
#[test]
fn test_invalid_arg() {
2025-03-01 15:29:11 +01:00
new_ucmd!().arg("--definitely-invalid").fails_with_code(1);
2022-09-10 18:38:14 +02:00
}
2021-04-26 22:14:59 +02:00
#[test]
fn test_kill_list_all_signals() {
// Check for a few signals. Do not try to be comprehensive.
new_ucmd!()
.arg("-l")
.succeeds()
.stdout_contains("KILL")
.stdout_contains("TERM")
2024-04-13 16:40:30 +02:00
.stdout_contains("HUP")
2025-01-22 08:20:47 +01:00
.stdout_contains("EXIT");
2021-04-26 22:14:59 +02:00
}
#[cfg(any(target_os = "linux", target_os = "android"))]
#[test]
fn test_kill_list_contains_realtime_signals() {
new_ucmd!()
.arg("-l")
.succeeds()
.stdout_contains("RTMIN")
.stdout_contains("RTMAX");
}
2021-09-13 00:15:53 +08:00
#[test]
fn test_kill_list_final_new_line() {
2021-09-14 23:56:08 +08:00
let re = Regex::new("\\n$").unwrap();
assert!(re.is_match(new_ucmd!().arg("-l").succeeds().stdout_str()));
2021-09-13 00:15:53 +08:00
}
2021-04-26 22:14:59 +02:00
#[test]
fn test_kill_list_all_signals_as_table() {
// Check for a few signals. Do not try to be comprehensive.
new_ucmd!()
.arg("-t")
.succeeds()
.stdout_contains("KILL")
.stdout_contains("TERM")
2025-01-22 08:20:47 +01:00
.stdout_contains("HUP")
.stdout_contains("EXIT");
2021-04-26 22:14:59 +02:00
}
#[cfg(any(target_os = "linux", target_os = "android"))]
#[test]
fn test_kill_table_contains_realtime_signals() {
new_ucmd!()
.arg("-t")
.succeeds()
.stdout_contains("RTMIN")
.stdout_contains("RTMAX");
}
2024-04-14 18:08:51 +05:00
#[test]
2025-01-22 08:20:47 +01:00
fn test_kill_table_starts_at_0() {
2024-04-14 18:08:51 +05:00
new_ucmd!()
.arg("-t")
.succeeds()
2025-01-22 08:20:47 +01:00
.stdout_matches(&Regex::new("^\\s?0\\sEXIT").unwrap());
2024-04-14 18:08:51 +05:00
}
#[test]
fn test_kill_table_lists_all_vertically() {
// Check for a few signals. Do not try to be comprehensive.
let command = new_ucmd!().arg("-t").succeeds();
let signals = command
.stdout_str()
.split('\n')
2024-06-30 16:27:08 +02:00
.filter_map(|line| line.trim().split(' ').nth(1))
2024-04-14 18:08:51 +05:00
.collect::<Vec<&str>>();
assert!(signals.contains(&"KILL"));
assert!(signals.contains(&"TERM"));
assert!(signals.contains(&"HUP"));
2025-01-22 08:20:47 +01:00
assert!(signals.contains(&"EXIT"));
2024-04-14 18:08:51 +05:00
}
#[test]
fn test_kill_list_one_signal_from_number() {
new_ucmd!()
.arg("-l")
.arg("9")
.succeeds()
.stdout_only("KILL\n");
}
#[cfg(any(target_os = "linux", target_os = "android"))]
#[test]
fn test_kill_list_rtmax_from_name() {
new_ucmd!()
.arg("-l")
.arg("RTMAX")
.succeeds()
.stdout_only(format!("{}\n", libc::SIGRTMAX()));
}
#[test]
fn test_kill_list_one_signal_from_invalid_number() {
new_ucmd!()
.arg("-l")
.arg("99")
.fails()
.stderr_contains("'99': invalid signal");
}
2021-04-26 22:14:59 +02:00
#[test]
fn test_kill_list_one_signal_from_name() {
// Use SIGKILL because it is 9 on all unixes.
new_ucmd!()
.arg("-l")
.arg("KILL")
.succeeds()
.stdout_matches(&Regex::new("\\b9\\b").unwrap());
}
#[test]
fn test_kill_list_one_signal_ignore_case() {
// Use SIGKILL because it is 9 on all unixes.
new_ucmd!()
.arg("-l")
.arg("KiLl")
.succeeds()
.stdout_matches(&Regex::new("\\b9\\b").unwrap());
}
#[test]
fn test_kill_list_unknown_must_match_input_case() {
new_ucmd!()
.arg("-l")
.arg("IaMnOtAsIgNaL")
.fails()
.stderr_contains("IaMnOtAsIgNaL");
2021-04-26 22:14:59 +02:00
}
2024-04-11 08:48:15 +05:00
#[test]
fn test_kill_list_all_vertically() {
// Check for a few signals. Do not try to be comprehensive.
let command = new_ucmd!().arg("-l").succeeds();
let signals = command.stdout_str().split('\n').collect::<Vec<&str>>();
assert!(signals.contains(&"KILL"));
assert!(signals.contains(&"TERM"));
assert!(signals.contains(&"HUP"));
2025-01-22 08:20:47 +01:00
assert!(signals.contains(&"EXIT"));
2024-04-11 08:48:15 +05:00
}
2024-04-11 08:45:53 +05:00
#[test]
fn test_kill_list_two_signal_from_name() {
new_ucmd!()
.arg("-l")
.arg("INT")
.arg("KILL")
.succeeds()
.stdout_matches(&Regex::new("\\d\n\\d").unwrap());
}
#[test]
fn test_kill_list_three_signal_first_unknown() {
new_ucmd!()
.arg("-l")
.arg("IAMNOTASIGNAL")
2024-04-11 08:45:53 +05:00
.arg("INT")
.arg("KILL")
.fails()
.stderr_contains("'IAMNOTASIGNAL': invalid signal")
2024-04-11 08:45:53 +05:00
.stdout_matches(&Regex::new("\\d\n\\d").unwrap());
}
2021-04-26 22:14:59 +02:00
#[test]
fn test_kill_set_bad_signal_name() {
new_ucmd!()
.arg("-s")
.arg("IAMNOTASIGNAL")
2021-04-26 22:14:59 +02:00
.fails()
.stderr_contains("'IAMNOTASIGNAL': invalid signal");
2021-04-26 22:14:59 +02:00
}
#[test]
fn test_kill_with_default_signal() {
let mut target = Target::new();
new_ucmd!().arg(format!("{}", target.pid())).succeeds();
assert_eq!(target.wait_for_signal(), Some(libc::SIGTERM));
}
#[test]
fn test_kill_with_signal_number_old_form() {
let mut target = Target::new();
new_ucmd!()
.arg("-9")
.arg(format!("{}", target.pid()))
.succeeds();
assert_eq!(target.wait_for_signal(), Some(9));
}
#[test]
fn test_kill_with_signal_name_old_form() {
for arg in ["-Kill", "-KILL"] {
let mut target = Target::new();
new_ucmd!()
.arg(arg)
.arg(format!("{}", target.pid()))
.succeeds();
assert_eq!(target.wait_for_signal(), Some(libc::SIGKILL));
}
}
#[test]
fn test_kill_with_lower_case_signal_name_old_form() {
let target = Target::new();
new_ucmd!()
.arg("-kill")
.arg(format!("{}", target.pid()))
.fails()
.stderr_contains("unexpected argument");
}
#[test]
fn test_kill_with_signal_prefixed_name_old_form() {
let mut target = Target::new();
new_ucmd!()
.arg("-SIGKILL")
.arg(format!("{}", target.pid()))
.succeeds();
assert_eq!(target.wait_for_signal(), Some(libc::SIGKILL));
}
2021-04-26 22:14:59 +02:00
#[test]
fn test_kill_with_signal_number_new_form() {
let mut target = Target::new();
new_ucmd!()
.arg("-s")
.arg("9")
.arg(format!("{}", target.pid()))
.succeeds();
assert_eq!(target.wait_for_signal(), Some(9));
}
#[test]
fn test_kill_with_signal_name_new_form() {
let mut target = Target::new();
new_ucmd!()
.arg("-s")
.arg("KILL")
.arg(format!("{}", target.pid()))
.succeeds();
assert_eq!(target.wait_for_signal(), Some(libc::SIGKILL));
}
2024-04-14 23:13:17 +05:00
#[test]
fn test_kill_with_signal_name_new_form_ignore_case() {
let mut target = Target::new();
new_ucmd!()
.arg("-s")
.arg("KiLl")
.arg(format!("{}", target.pid()))
.succeeds();
assert_eq!(target.wait_for_signal(), Some(libc::SIGKILL));
}
#[test]
fn test_kill_with_signal_prefixed_name_new_form() {
let mut target = Target::new();
new_ucmd!()
.arg("-s")
.arg("SIGKILL")
.arg(format!("{}", target.pid()))
.succeeds();
assert_eq!(target.wait_for_signal(), Some(libc::SIGKILL));
}
2024-04-14 23:13:17 +05:00
#[test]
fn test_kill_with_signal_prefixed_name_new_form_ignore_case() {
let mut target = Target::new();
new_ucmd!()
.arg("-s")
.arg("SiGKiLl")
.arg(format!("{}", target.pid()))
.succeeds();
assert_eq!(target.wait_for_signal(), Some(libc::SIGKILL));
}
#[test]
fn test_kill_with_signal_name_new_form_unknown_must_match_input_case() {
let target = Target::new();
new_ucmd!()
.arg("-s")
.arg("IaMnOtAsIgNaL")
2024-04-14 23:13:17 +05:00
.arg(format!("{}", target.pid()))
.fails()
.stderr_contains("'IaMnOtAsIgNaL': invalid signal");
2024-04-14 23:13:17 +05:00
}
#[test]
fn test_kill_no_pid_provided() {
new_ucmd!()
.fails()
.stderr_contains("no process ID specified");
}
#[test]
fn test_kill_with_signal_exit_new_form() {
let target = Target::new();
new_ucmd!()
.arg("-s")
.arg("EXIT")
.arg(format!("{}", target.pid()))
.succeeds();
}
#[test]
fn test_kill_with_signal_number_hidden_compatibility_option() {
let mut target = Target::new();
new_ucmd!()
.arg("-n")
.arg("9")
.arg(format!("{}", target.pid()))
.succeeds();
assert_eq!(target.wait_for_signal(), Some(9));
}
2025-01-04 11:40:21 +01:00
#[test]
fn test_kill_with_signal_and_list() {
let target = Target::new();
new_ucmd!()
.arg("-s")
.arg("EXIT")
.arg(format!("{}", target.pid()))
.arg("-l")
.fails();
}
#[test]
fn test_kill_with_list_lower_bits() {
new_ucmd!()
.arg("-l")
.arg("128")
.succeeds()
.stdout_contains("EXIT");
new_ucmd!()
.arg("-l")
.arg("143")
.succeeds()
.stdout_contains("TERM");
new_ucmd!()
.arg("-l")
.arg("256")
.succeeds()
.stdout_contains("EXIT");
new_ucmd!()
.arg("-l")
.arg("2304")
.succeeds()
.stdout_contains("EXIT");
}
#[test]
fn test_kill_with_list_lower_bits_unrecognized() {
new_ucmd!().arg("-l").arg("111").fails();
new_ucmd!().arg("-l").arg("384").fails();
}
#[cfg(any(target_os = "linux", target_os = "android"))]
#[test]
fn test_kill_with_list_unnamed_signal_numbers() {
new_ucmd!()
.arg("-l")
.arg("32")
.succeeds()
.stdout_only("32\n");
new_ucmd!()
.arg("-l")
.arg("33")
.succeeds()
.stdout_only("33\n");
}
#[cfg(any(target_os = "linux", target_os = "android"))]
#[test]
fn test_kill_with_list_all_signal_numbers_up_to_last_named_signal() {
let last_signal_name = new_ucmd!()
.arg("-l")
.succeeds()
.stdout_str()
.lines()
.last()
.unwrap()
.to_string();
let last_signal_number: usize = new_ucmd!()
.arg("-l")
.arg("--")
.arg(&last_signal_name)
.succeeds()
.stdout_str()
.trim()
.parse()
.unwrap();
let args = std::iter::once(String::from("--"))
.chain((0..=last_signal_number).map(|signal| signal.to_string()))
.collect::<Vec<_>>();
new_ucmd!().arg("-l").args(&args).succeeds();
}
2025-01-04 11:40:21 +01:00
#[test]
fn test_kill_with_signal_and_table() {
let target = Target::new();
new_ucmd!()
.arg("-s")
.arg("EXIT")
.arg(format!("{}", target.pid()))
.arg("-t")
.fails();
}
2025-12-18 21:46:52 +01:00
/// Test that `kill -1` (signal without PID) reports "no process ID" error
/// instead of being misinterpreted as pid=-1 which would kill all processes.
/// This matches GNU kill behavior.
#[test]
fn test_kill_signal_only_no_pid() {
// Test with -1 (SIGHUP)
new_ucmd!()
.arg("-1")
.fails()
.stderr_contains("no process ID specified");
// Test with -9 (SIGKILL)
new_ucmd!()
.arg("-9")
.fails()
.stderr_contains("no process ID specified");
// Test with -TERM
new_ucmd!()
.arg("-TERM")
.fails()
.stderr_contains("no process ID specified");
}