use std::time::Duration; // 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 dont use rstest::rstest; use uucore::display::Quotable; use uutests::new_ucmd; #[test] fn test_invalid_arg() { new_ucmd!().arg("--definitely-invalid").fails_with_code(125); } // FIXME: this depends on the system having true and false in PATH // the best solution is probably to generate some test binaries that we can call for any // utility that requires executing another program (kill, for instance) #[test] fn test_subcommand_return_code() { new_ucmd!().arg("1").arg("true").succeeds(); new_ucmd!().arg("1").arg("false").fails_with_code(1); } #[rstest] #[case::alphabetic("xyz")] #[case::single_quote("'1")] fn test_invalid_time_interval(#[case] input: &str) { new_ucmd!() .args(&[input, "sleep", "0"]) .fails_with_code(125) .usage_error(format!("invalid time interval {}", input.quote())); } #[test] fn test_invalid_kill_after() { new_ucmd!() .args(&["-k", "xyz", "1", "sleep", "0"]) .fails_with_code(125) .usage_error("invalid time interval 'xyz'"); } #[test] fn test_command_with_args() { new_ucmd!() .args(&["1700", "echo", "-n", "abcd"]) .succeeds() .stdout_only("abcd"); } #[test] fn test_verbose() { for verbose_flag in ["-v", "--verbose"] { new_ucmd!() .args(&[verbose_flag, ".1", "sleep", "10"]) .fails() .stderr_only("timeout: sending signal TERM to command 'sleep'\n"); new_ucmd!() .args(&[verbose_flag, "-s0", "-k.1", ".1", "sleep", "10"]) .fails() .stderr_only("timeout: sending signal EXIT to command 'sleep'\ntimeout: sending signal KILL to command 'sleep'\n"); } } #[test] fn test_zero_timeout() { new_ucmd!() .args(&["-v", "0", "sleep", ".1"]) .succeeds() .no_output(); new_ucmd!() .args(&["-v", "0", "-s0", "-k0", "sleep", ".1"]) .succeeds() .no_output(); } #[test] fn test_command_empty_args() { new_ucmd!() .args(&["", ""]) .fails() .stderr_contains("timeout: invalid time interval ''"); } #[test] fn test_foreground() { for arg in ["-f", "--foreground"] { new_ucmd!() .args(&[arg, ".1", "sleep", "10"]) .fails_with_code(124) .no_output(); } } #[test] fn test_preserve_status() { for arg in ["-p", "--preserve-status"] { new_ucmd!() .args(&[arg, ".1", "sleep", "10"]) // 128 + SIGTERM = 128 + 15 .fails_with_code(128 + 15) .no_output(); } } #[test] fn test_preserve_status_even_when_send_signal() { // When sending CONT signal, process doesn't get killed or stopped. // So, expected result is success and code 0. for cont_spelling in ["CONT", "cOnT", "SIGcont"] { new_ucmd!() .args(&["-s", cont_spelling, "--preserve-status", ".1", "sleep", "2"]) .succeeds() .no_output(); } } #[test] fn test_dont_overflow() { new_ucmd!() .args(&["9223372036854775808d", "sleep", "0"]) .succeeds() .no_output(); new_ucmd!() .args(&["-k", "9223372036854775808d", "10", "sleep", "0"]) .succeeds() .no_output(); } #[test] fn test_dont_underflow() { new_ucmd!() .args(&[".0000000001", "sleep", "1"]) .fails_with_code(124) .no_output(); new_ucmd!() .args(&["1e-100", "sleep", "1"]) .fails_with_code(124) .no_output(); // Unlike GNU coreutils, we underflow to 1ns for very short timeouts. // https://debbugs.gnu.org/cgi/bugreport.cgi?bug=77535 new_ucmd!() .args(&["1e-18172487393827593258", "sleep", "1"]) .fails_with_code(124) .no_output(); } #[test] fn test_negative_interval() { new_ucmd!() .args(&["--", "-1", "sleep", "0"]) .fails() .usage_error("invalid time interval '-1'"); } #[test] fn test_invalid_signal() { new_ucmd!() .args(&["-s", "invalid", "1", "sleep", "0"]) .fails() .usage_error("'invalid': invalid signal"); } #[test] fn test_invalid_multi_byte_characters() { new_ucmd!() .args(&["10€", "sleep", "0"]) .fails() .usage_error("invalid time interval '10€'"); } /// Test that the long form of the `--kill-after` argument is recognized. #[test] fn test_kill_after_long() { new_ucmd!() .args(&["--kill-after=1", "1", "sleep", "0"]) .succeeds() .no_output(); } #[test] fn test_kill_subprocess() { new_ucmd!() .args(&[ // Make sure the CI can spawn the subprocess. "10", "sh", "-c", "trap 'echo inside_trap' TERM; sleep 30", ]) .fails_with_code(124) .stdout_contains("inside_trap") .stderr_contains("Terminated"); } #[test] fn test_hex_timeout_ending_with_d() { new_ucmd!() .args(&["0x0.1d", "sleep", "10"]) .timeout(Duration::from_secs(1)) .fails_with_code(124) .no_output(); } #[test] fn test_terminate_child_on_receiving_terminate() { let mut timeout_cmd = new_ucmd!() .args(&[ "10", "sh", "-c", "trap 'echo child received TERM' TERM; sleep 5", ]) .run_no_wait(); timeout_cmd.delay(100); timeout_cmd.kill_with_custom_signal(nix::sys::signal::Signal::SIGTERM); timeout_cmd .make_assertion() .is_not_alive() .with_current_output() .code_is(143) .stdout_contains("child received TERM"); }