procutils-skill 0.2.0

Send a signal to processes selected by user, tty, pid or command (obsolete; prefer pkill)
Documentation
//! Compatibility tests implementing the same scenarios as procps-ng's
//! `testsuite/skill.test/skill.exp`.

use regex::Regex;
use std::process::Command;

const BIN: &str = env!("CARGO_BIN_EXE_skill");

#[track_caller]
fn run(args: &[&str]) -> std::process::Output {
    Command::new(BIN).args(args).output().expect("spawn skill")
}

#[track_caller]
fn assert_matches(haystack: &str, pattern: &str) {
    let re = Regex::new(pattern).expect("valid regex");
    assert!(
        re.is_match(haystack),
        "pattern did not match\n--- pattern ---\n{pattern}\n--- output ---\n{haystack}"
    );
}

// ===========================================================================
// "skill with no arguments"
// procps regex: Usage:\s+(lt-)?skill \[signal\] \[options\] <expression>
// ===========================================================================

#[test]
fn no_arguments() {
    let out = run(&[]);
    assert!(!out.status.success());
    // The Usage line goes to stderr in our implementation, stdout in
    // procps's. Either way the pattern is the same; check both.
    let combined = format!(
        "{}{}",
        String::from_utf8_lossy(&out.stdout),
        String::from_utf8_lossy(&out.stderr),
    );
    assert_matches(
        &combined,
        r"Usage:\s+skill \[signal\] \[options\] <expression>",
    );
}

// ===========================================================================
// "skill list signal names"
// procps regex: ^([A-Z12+-]\s*)+$
//
// Char class widened to [A-Z0-9+-] for RT signal names with two-digit
// suffixes like RTMIN+10 (procps's `[A-Z12+-]` shape pre-dated RT
// enumeration in the listing).
// ===========================================================================

#[test]
fn list_signal_names() {
    let out = run(&["-l"]);
    let stdout = String::from_utf8_lossy(&out.stdout);
    assert_matches(stdout.trim(), r"^([A-Z0-9+-]+\s*)+$");
}

// ===========================================================================
// "skill list signal names in table"
// procps regex: ^(\s+\d+ [A-Z12+-]+)+\s*$
//
// Same widening as above.
// ===========================================================================

#[test]
fn list_signal_names_in_table() {
    let out = run(&["-L"]);
    let stdout = String::from_utf8_lossy(&out.stdout);
    assert_matches(&stdout, r"^(\s+\d+ [A-Z0-9+-]+)+\s*$");
}

// ===========================================================================
// "skill find one process"
//
// procps spawns a test process and runs `skill -n -p $pid`. -n is
// no-action mode (print PIDs, don't signal). We use the test runner's
// own PID since we just need a real PID for -p to filter against;
// -n means nothing actually gets signalled.
// ===========================================================================

#[test]
fn find_one_process() {
    let pid = std::process::id();
    let out = run(&["-n", "-p", &pid.to_string()]);
    assert!(out.status.success());
    let stdout = String::from_utf8_lossy(&out.stdout);
    assert_matches(stdout.trim(), &format!(r"^{pid}\s*$"));
}