mirror of
https://github.com/uutils/coreutils.git
synced 2025-07-07 21:45:01 +00:00
452 lines
12 KiB
Rust
452 lines
12 KiB
Rust
// 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.
|
|
use rstest::rstest;
|
|
|
|
use uucore::display::Quotable;
|
|
// spell-checker:ignore dont SIGBUS SIGSEGV sigsegv sigbus infd
|
|
use uutests::new_ucmd;
|
|
|
|
#[cfg(unix)]
|
|
use nix::sys::signal::Signal::{SIGBUS, SIGSEGV};
|
|
use std::io::ErrorKind;
|
|
use std::time::{Duration, Instant};
|
|
|
|
#[test]
|
|
fn test_invalid_time_interval() {
|
|
new_ucmd!()
|
|
.arg("xyz")
|
|
.fails()
|
|
.usage_error("invalid time interval 'xyz'");
|
|
new_ucmd!()
|
|
.args(&["--", "-1"])
|
|
.fails()
|
|
.usage_error("invalid time interval '-1'");
|
|
}
|
|
|
|
#[test]
|
|
fn test_sleep_no_suffix() {
|
|
let millis_100 = Duration::from_millis(100);
|
|
let before_test = Instant::now();
|
|
|
|
new_ucmd!().args(&["0.1"]).succeeds().stdout_only("");
|
|
|
|
let duration = before_test.elapsed();
|
|
assert!(duration >= millis_100);
|
|
}
|
|
|
|
#[test]
|
|
fn test_sleep_s_suffix() {
|
|
let millis_100 = Duration::from_millis(100);
|
|
let before_test = Instant::now();
|
|
|
|
new_ucmd!().args(&["0.1s"]).succeeds().stdout_only("");
|
|
|
|
let duration = before_test.elapsed();
|
|
assert!(duration >= millis_100);
|
|
}
|
|
|
|
#[test]
|
|
fn test_sleep_m_suffix() {
|
|
let millis_600 = Duration::from_millis(600);
|
|
let before_test = Instant::now();
|
|
|
|
new_ucmd!().args(&["0.01m"]).succeeds().stdout_only("");
|
|
|
|
let duration = before_test.elapsed();
|
|
assert!(duration >= millis_600);
|
|
}
|
|
|
|
#[test]
|
|
fn test_sleep_h_suffix() {
|
|
let millis_360 = Duration::from_millis(360);
|
|
let before_test = Instant::now();
|
|
|
|
new_ucmd!().args(&["0.0001h"]).succeeds().stdout_only("");
|
|
|
|
let duration = before_test.elapsed();
|
|
assert!(duration >= millis_360);
|
|
}
|
|
|
|
#[test]
|
|
fn test_sleep_negative_duration() {
|
|
new_ucmd!().args(&["-1"]).fails();
|
|
new_ucmd!().args(&["-1s"]).fails();
|
|
new_ucmd!().args(&["-1m"]).fails();
|
|
new_ucmd!().args(&["-1h"]).fails();
|
|
new_ucmd!().args(&["-1d"]).fails();
|
|
}
|
|
|
|
#[test]
|
|
fn test_sleep_zero_duration() {
|
|
new_ucmd!().args(&["0"]).succeeds().stdout_only("");
|
|
new_ucmd!().args(&["0s"]).succeeds().stdout_only("");
|
|
new_ucmd!().args(&["0m"]).succeeds().stdout_only("");
|
|
new_ucmd!().args(&["0h"]).succeeds().stdout_only("");
|
|
new_ucmd!().args(&["0d"]).succeeds().stdout_only("");
|
|
}
|
|
|
|
#[test]
|
|
fn test_sleep_no_argument() {
|
|
new_ucmd!().fails().usage_error("missing operand");
|
|
}
|
|
|
|
#[test]
|
|
fn test_sleep_sum_duration_same_suffix() {
|
|
let millis_200 = Duration::from_millis(100 + 100);
|
|
let before_test = Instant::now();
|
|
|
|
new_ucmd!()
|
|
.args(&["0.1s", "0.1s"])
|
|
.succeeds()
|
|
.stdout_only("");
|
|
|
|
let duration = before_test.elapsed();
|
|
assert!(duration >= millis_200);
|
|
}
|
|
|
|
#[test]
|
|
fn test_sleep_sum_duration_different_suffix() {
|
|
let millis_700 = Duration::from_millis(100 + 600);
|
|
let before_test = Instant::now();
|
|
|
|
new_ucmd!()
|
|
.args(&["0.1s", "0.01m"])
|
|
.succeeds()
|
|
.stdout_only("");
|
|
|
|
let duration = before_test.elapsed();
|
|
assert!(duration >= millis_700);
|
|
}
|
|
|
|
#[test]
|
|
fn test_sleep_sum_duration_many() {
|
|
let millis_900 = Duration::from_millis(100 + 100 + 300 + 400);
|
|
let before_test = Instant::now();
|
|
|
|
new_ucmd!()
|
|
.args(&["0.1s", "0.1s", "0.3s", "0.4s"])
|
|
.succeeds()
|
|
.stdout_only("");
|
|
|
|
let duration = before_test.elapsed();
|
|
assert!(duration >= millis_900);
|
|
}
|
|
|
|
#[test]
|
|
fn test_sleep_wrong_time() {
|
|
new_ucmd!().args(&["0.1s", "abc"]).fails();
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(unix)]
|
|
fn test_sleep_stops_after_sigsegv() {
|
|
let mut child = new_ucmd!()
|
|
.arg("100")
|
|
.timeout(Duration::from_secs(10))
|
|
.run_no_wait();
|
|
|
|
child
|
|
.delay(100)
|
|
.kill_with_custom_signal(SIGSEGV)
|
|
.make_assertion()
|
|
.with_current_output()
|
|
.signal_is(SIGSEGV as i32) // make sure it was us who terminated the process
|
|
.no_output();
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(unix)]
|
|
fn test_sleep_stops_after_sigbus() {
|
|
let mut child = new_ucmd!()
|
|
.arg("100")
|
|
.timeout(Duration::from_secs(10))
|
|
.run_no_wait();
|
|
|
|
child
|
|
.delay(100)
|
|
.kill_with_custom_signal(SIGBUS)
|
|
.make_assertion()
|
|
.with_current_output()
|
|
.signal_is(SIGBUS as i32) // make sure it was us who terminated the process
|
|
.no_output();
|
|
}
|
|
|
|
#[test]
|
|
fn test_sleep_when_single_input_exceeds_max_duration_then_no_error() {
|
|
let mut child = new_ucmd!()
|
|
.arg(format!("{}", u128::from(u64::MAX) + 1))
|
|
.timeout(Duration::from_secs(10))
|
|
.run_no_wait();
|
|
|
|
#[cfg(unix)]
|
|
child
|
|
.delay(100)
|
|
.kill()
|
|
.make_assertion()
|
|
.with_current_output()
|
|
.signal_is(9) // make sure it was us who terminated the process
|
|
.no_output();
|
|
#[cfg(windows)]
|
|
child
|
|
.delay(100)
|
|
.kill()
|
|
.make_assertion()
|
|
.with_current_output()
|
|
.failure()
|
|
.no_output();
|
|
}
|
|
|
|
#[test]
|
|
fn test_sleep_when_multiple_inputs_exceed_max_duration_then_no_error() {
|
|
let mut child = new_ucmd!()
|
|
.arg(format!("{}", u64::MAX))
|
|
.arg("1")
|
|
.timeout(Duration::from_secs(10))
|
|
.run_no_wait();
|
|
|
|
#[cfg(unix)]
|
|
child
|
|
.delay(100)
|
|
.kill()
|
|
.make_assertion()
|
|
.with_current_output()
|
|
.signal_is(9) // make sure it was us who terminated the process
|
|
.no_output();
|
|
#[cfg(windows)]
|
|
child
|
|
.delay(100)
|
|
.kill()
|
|
.make_assertion()
|
|
.with_current_output()
|
|
.failure()
|
|
.no_output();
|
|
}
|
|
|
|
#[rstest]
|
|
#[case::whitespace_prefix(" 0.1s")]
|
|
#[case::multiple_whitespace_prefix(" 0.1s")]
|
|
fn test_sleep_when_input_has_leading_whitespace_then_no_error(#[case] input: &str) {
|
|
new_ucmd!()
|
|
.arg(input)
|
|
.timeout(Duration::from_secs(10))
|
|
.succeeds()
|
|
.no_output();
|
|
}
|
|
|
|
#[rstest]
|
|
#[case::whitespace_suffix("0.1s ")]
|
|
#[case::mixed_newlines_spaces_tabs("\n\t0.1s \n ")]
|
|
fn test_sleep_when_input_has_trailing_whitespace_then_error(#[case] input: &str) {
|
|
new_ucmd!()
|
|
.arg(input)
|
|
.timeout(Duration::from_secs(10))
|
|
.fails()
|
|
.usage_error(format!("invalid time interval {}", input.quote()));
|
|
}
|
|
|
|
#[rstest]
|
|
#[case::only_space(" ")]
|
|
#[case::only_tab("\t")]
|
|
#[case::only_newline("\n")]
|
|
fn test_sleep_when_input_has_only_whitespace_then_error(#[case] input: &str) {
|
|
new_ucmd!()
|
|
.arg(input)
|
|
.timeout(Duration::from_secs(10))
|
|
.fails()
|
|
.usage_error(format!("invalid time interval {}", input.quote()));
|
|
}
|
|
|
|
#[test]
|
|
fn test_sleep_when_multiple_input_some_with_error_then_shows_all_errors() {
|
|
let expected = "invalid time interval 'abc'\n\
|
|
sleep: invalid time interval '1years'\n\
|
|
sleep: invalid time interval ' '";
|
|
|
|
// Even if one of the arguments is valid, but the rest isn't, we should still fail and exit early.
|
|
// So, the timeout of 10 seconds ensures we haven't executed `thread::sleep` with the only valid
|
|
// interval of `100000.0` seconds.
|
|
new_ucmd!()
|
|
.args(&["abc", "100000.0", "1years", " "])
|
|
.timeout(Duration::from_secs(10))
|
|
.fails()
|
|
.usage_error(expected);
|
|
}
|
|
|
|
#[test]
|
|
fn test_negative_interval() {
|
|
new_ucmd!()
|
|
.args(&["--", "-1"])
|
|
.fails()
|
|
.usage_error("invalid time interval '-1'");
|
|
}
|
|
|
|
#[rstest]
|
|
#[case::int("0x0")]
|
|
#[case::negative_zero("-0x0")]
|
|
#[case::int_suffix("0x0s")]
|
|
#[case::int_suffix("0x0h")]
|
|
#[case::frac("0x0.1")]
|
|
#[case::frac_suffix("0x0.1s")]
|
|
#[case::frac_suffix("0x0.001h")]
|
|
#[case::scientific("0x1.0p-3")]
|
|
#[case::scientific_suffix("0x1.0p-4s")]
|
|
fn test_valid_hex_duration(#[case] input: &str) {
|
|
new_ucmd!().args(&["--", input]).succeeds().no_output();
|
|
}
|
|
|
|
#[rstest]
|
|
#[case::negative("-0x1")]
|
|
#[case::negative_suffix("-0x1s")]
|
|
#[case::negative_frac_suffix("-0x0.1s")]
|
|
#[case::wrong_capitalization("infD")]
|
|
#[case::wrong_capitalization("INFD")]
|
|
#[case::wrong_capitalization("iNfD")]
|
|
#[case::single_quote("'1")]
|
|
fn test_invalid_duration(#[case] input: &str) {
|
|
new_ucmd!()
|
|
.args(&["--", input])
|
|
.fails()
|
|
.usage_error(format!("invalid time interval {}", input.quote()));
|
|
}
|
|
|
|
#[cfg(unix)]
|
|
#[test]
|
|
#[should_panic = "Program must be run first or has not finished"]
|
|
fn test_cmd_result_signal_when_still_running_then_panic() {
|
|
let mut child = new_ucmd!().arg("60").run_no_wait();
|
|
|
|
child
|
|
.make_assertion()
|
|
.is_alive()
|
|
.with_current_output()
|
|
.signal();
|
|
}
|
|
|
|
#[cfg(unix)]
|
|
#[test]
|
|
fn test_cmd_result_signal_when_kill_then_signal() {
|
|
let mut child = new_ucmd!().arg("60").run_no_wait();
|
|
|
|
child.kill();
|
|
child
|
|
.make_assertion()
|
|
.is_not_alive()
|
|
.with_current_output()
|
|
.signal_is(9)
|
|
.signal_name_is("SIGKILL")
|
|
.signal_name_is("KILL")
|
|
.signal_name_is("9")
|
|
.signal()
|
|
.expect("Signal was none");
|
|
|
|
child
|
|
.wait()
|
|
.unwrap()
|
|
.signal_is(9)
|
|
.signal_name_is("SIGKILL")
|
|
.signal_name_is("KILL")
|
|
.signal_name_is("9")
|
|
.signal()
|
|
.expect("Signal was none");
|
|
}
|
|
|
|
#[cfg(unix)]
|
|
#[rstest]
|
|
#[case::signal_only_part_of_name("IGKILL")] // spell-checker: disable-line
|
|
#[case::signal_just_sig("SIG")]
|
|
#[case::signal_value_too_high("100")]
|
|
#[case::signal_value_negative("-1")]
|
|
#[should_panic = "Invalid signal name or value"]
|
|
fn test_cmd_result_signal_when_invalid_signal_name_then_panic(#[case] signal_name: &str) {
|
|
let mut child = new_ucmd!().arg("60").run_no_wait();
|
|
|
|
child.kill();
|
|
child.wait().unwrap().signal_name_is(signal_name);
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(unix)]
|
|
fn test_cmd_result_signal_name_is_accepts_lowercase() {
|
|
let mut child = new_ucmd!().arg("60").run_no_wait();
|
|
child.kill();
|
|
let result = child.wait().unwrap();
|
|
result.signal_name_is("sigkill");
|
|
result.signal_name_is("kill");
|
|
}
|
|
|
|
#[test]
|
|
fn test_uchild_when_wait_and_timeout_is_reached_then_timeout_error() {
|
|
let child = new_ucmd!()
|
|
.timeout(Duration::from_secs(1))
|
|
.arg("10.0")
|
|
.run_no_wait();
|
|
|
|
match child.wait() {
|
|
Err(error) if error.kind() == ErrorKind::Other => {
|
|
std::assert_eq!(error.to_string(), "wait: Timeout of '1s' reached");
|
|
}
|
|
Err(error) => panic!("Assertion failed: Expected error with timeout but was: {error}"),
|
|
Ok(_) => panic!("Assertion failed: Expected timeout of `wait`."),
|
|
}
|
|
}
|
|
|
|
#[rstest]
|
|
#[timeout(Duration::from_secs(5))]
|
|
fn test_uchild_when_kill_and_timeout_higher_than_kill_time_then_no_panic() {
|
|
let mut child = new_ucmd!()
|
|
.timeout(Duration::from_secs(60))
|
|
.arg("20.0")
|
|
.run_no_wait();
|
|
|
|
child.kill().make_assertion().is_not_alive();
|
|
}
|
|
|
|
#[test]
|
|
fn test_uchild_when_try_kill_and_timeout_is_reached_then_error() {
|
|
let mut child = new_ucmd!()
|
|
.timeout(Duration::ZERO)
|
|
.arg("10.0")
|
|
.run_no_wait();
|
|
|
|
match child.try_kill() {
|
|
Err(error) if error.kind() == ErrorKind::Other => {
|
|
std::assert_eq!(error.to_string(), "kill: Timeout of '0s' reached");
|
|
}
|
|
Err(error) => panic!("Assertion failed: Expected error with timeout but was: {error}"),
|
|
Ok(()) => panic!("Assertion failed: Expected timeout of `try_kill`."),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic = "kill: Timeout of '0s' reached"]
|
|
fn test_uchild_when_kill_with_timeout_and_timeout_is_reached_then_panic() {
|
|
let mut child = new_ucmd!()
|
|
.timeout(Duration::ZERO)
|
|
.arg("10.0")
|
|
.run_no_wait();
|
|
|
|
child.kill();
|
|
panic!("Assertion failed: Expected timeout of `kill`.");
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic(expected = "wait: Timeout of '1.1s' reached")]
|
|
fn test_ucommand_when_run_with_timeout_and_timeout_is_reached_then_panic() {
|
|
new_ucmd!()
|
|
.timeout(Duration::from_millis(1100))
|
|
.arg("10.0")
|
|
.run();
|
|
|
|
panic!("Assertion failed: Expected timeout of `run`.")
|
|
}
|
|
|
|
#[rstest]
|
|
#[timeout(Duration::from_secs(10))]
|
|
fn test_ucommand_when_run_with_timeout_higher_then_execution_time_then_no_panic() {
|
|
new_ucmd!()
|
|
.timeout(Duration::from_secs(60))
|
|
.arg("1.0")
|
|
.run();
|
|
}
|