|
1 | | -use std::time::Duration; |
| 1 | +use std::time::{Duration, Instant}; |
| 2 | +use uutests::util::TestScenario; |
2 | 3 |
|
3 | 4 | // This file is part of the uutils coreutils package. |
4 | 5 | // |
@@ -52,11 +53,13 @@ fn test_command_with_args() { |
52 | 53 | fn test_verbose() { |
53 | 54 | for verbose_flag in ["-v", "--verbose"] { |
54 | 55 | new_ucmd!() |
55 | | - .args(&[verbose_flag, ".1", "sleep", "1"]) |
| 56 | + .args(&[verbose_flag, ".1", "sleep", "10"]) |
| 57 | + .timeout(Duration::from_secs(1)) |
56 | 58 | .fails() |
57 | 59 | .stderr_only("timeout: sending signal TERM to command 'sleep'\n"); |
58 | 60 | new_ucmd!() |
59 | | - .args(&[verbose_flag, "-s0", "-k.1", ".1", "sleep", "1"]) |
| 61 | + .args(&[verbose_flag, "-s0", "-k.1", ".1", "sleep", "10"]) |
| 62 | + .timeout(Duration::from_secs(1)) |
60 | 63 | .fails() |
61 | 64 | .stderr_only("timeout: sending signal EXIT to command 'sleep'\ntimeout: sending signal KILL to command 'sleep'\n"); |
62 | 65 | } |
@@ -107,22 +110,26 @@ fn test_preserve_status() { |
107 | 110 | fn test_preserve_status_even_when_send_signal() { |
108 | 111 | // When sending CONT signal, process doesn't get killed or stopped. |
109 | 112 | // So, expected result is success and code 0. |
| 113 | + let time = Instant::now(); |
110 | 114 | for cont_spelling in ["CONT", "cOnT", "SIGcont"] { |
111 | 115 | new_ucmd!() |
112 | | - .args(&["-s", cont_spelling, "--preserve-status", ".1", "sleep", "1"]) |
| 116 | + .args(&["-s", cont_spelling, "--preserve-status", ".1", "sleep", "2"]) |
113 | 117 | .succeeds() |
114 | 118 | .no_output(); |
115 | 119 | } |
| 120 | + assert!(time.elapsed().as_secs() >= 6) // Assert they run for one second each. |
116 | 121 | } |
117 | 122 |
|
118 | 123 | #[test] |
119 | 124 | fn test_dont_overflow() { |
120 | 125 | new_ucmd!() |
121 | 126 | .args(&["9223372036854775808d", "sleep", "0"]) |
| 127 | + .timeout(Duration::from_secs(2)) |
122 | 128 | .succeeds() |
123 | 129 | .no_output(); |
124 | 130 | new_ucmd!() |
125 | 131 | .args(&["-k", "9223372036854775808d", "10", "sleep", "0"]) |
| 132 | + .timeout(Duration::from_secs(2)) |
126 | 133 | .succeeds() |
127 | 134 | .no_output(); |
128 | 135 | } |
@@ -183,11 +190,12 @@ fn test_kill_subprocess() { |
183 | 190 | new_ucmd!() |
184 | 191 | .args(&[ |
185 | 192 | // Make sure the CI can spawn the subprocess. |
186 | | - "1", |
| 193 | + "5", |
187 | 194 | "sh", |
188 | 195 | "-c", |
189 | | - "trap 'echo inside_trap' TERM; sleep 5", |
| 196 | + "trap 'echo inside_trap' TERM; sleep 30", |
190 | 197 | ]) |
| 198 | + .timeout(Duration::from_secs(6)) // assert it exits when it times out. |
191 | 199 | .fails_with_code(124) |
192 | 200 | .stdout_contains("inside_trap"); |
193 | 201 | } |
@@ -243,3 +251,42 @@ fn test_command_cannot_invoke() { |
243 | 251 | // Try to execute a directory (should give permission denied or similar) |
244 | 252 | new_ucmd!().args(&["1", "/"]).fails_with_code(126); |
245 | 253 | } |
| 254 | + |
| 255 | +fn test_cascaded_timeout_with_bash_trap() { |
| 256 | + // Use bash if available, otherwise skip |
| 257 | + if std::process::Command::new("bash") |
| 258 | + .arg("--version") |
| 259 | + .output() |
| 260 | + .is_err() |
| 261 | + { |
| 262 | + // Skip test if bash is not available |
| 263 | + return; |
| 264 | + } |
| 265 | + |
| 266 | + // Test with bash explicitly to ensure SIGINT handlers work |
| 267 | + let script = r" |
| 268 | + trap 'echo bash_trap_fired; exit 0' INT |
| 269 | + sleep 10 |
| 270 | + "; |
| 271 | + |
| 272 | + let ts = TestScenario::new("timeout"); |
| 273 | + let timeout_bin = ts.bin_path.to_str().unwrap(); |
| 274 | + |
| 275 | + ts.ucmd() |
| 276 | + .args(&[ |
| 277 | + "-s", |
| 278 | + "ALRM", |
| 279 | + "0.3", |
| 280 | + timeout_bin, |
| 281 | + "timeout", |
| 282 | + "-s", |
| 283 | + "INT", |
| 284 | + "5", |
| 285 | + "bash", |
| 286 | + "-c", |
| 287 | + script, |
| 288 | + ]) |
| 289 | + .timeout(Duration::from_secs(6)) |
| 290 | + .fails_with_code(124) |
| 291 | + .stdout_contains("bash_trap_fired"); |
| 292 | +} |
0 commit comments