Skip to content

Commit a803ce1

Browse files
committed
simulate terminal utility (squash)
1 parent 6998c1a commit a803ce1

File tree

7 files changed

+350
-30
lines changed

7 files changed

+350
-30
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@ Cargo.lock
1414
lib*.a
1515
/docs/_build
1616
*.iml
17+
worktree*
1718
### macOS ###
1819
.DS_Store

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,7 @@ procfs = { version = "0.16", default-features = false }
498498
rlimit = "0.10.1"
499499

500500
[target.'cfg(unix)'.dev-dependencies]
501-
nix = { workspace = true, features = ["process", "signal", "user"] }
501+
nix = { workspace = true, features = ["process", "signal", "user", "term"] }
502502
rand_pcg = "0.3"
503503
xattr = { workspace = true }
504504

tests/by-util/test_env.rs

+93
Original file line numberDiff line numberDiff line change
@@ -245,3 +245,96 @@ fn test_fail_change_directory() {
245245
.stderr_move_str();
246246
assert!(out.contains("env: cannot change directory to "));
247247
}
248+
249+
#[cfg(unix)]
250+
#[test]
251+
fn test_simulation_of_terminal_false() {
252+
let scene = TestScenario::new(util_name!());
253+
254+
let out = scene.ucmd().arg("sh").arg("is_atty.sh").succeeds();
255+
assert_eq!(
256+
String::from_utf8_lossy(out.stdout()),
257+
"stdin is not atty\nstdout is not atty\nstderr is not atty\n"
258+
);
259+
assert_eq!(
260+
String::from_utf8_lossy(out.stderr()),
261+
"This is an error message.\n"
262+
);
263+
}
264+
265+
#[cfg(unix)]
266+
#[test]
267+
fn test_simulation_of_terminal_true() {
268+
let scene = TestScenario::new(util_name!());
269+
270+
let out = scene
271+
.ucmd()
272+
.arg("sh")
273+
.arg("is_atty.sh")
274+
.terminal_simulation(true)
275+
.succeeds();
276+
assert_eq!(
277+
String::from_utf8_lossy(out.stdout()),
278+
"stdin is atty\r\nstdout is atty\r\nstderr is atty\r\n"
279+
);
280+
assert_eq!(
281+
String::from_utf8_lossy(out.stderr()),
282+
"This is an error message.\r\n"
283+
);
284+
}
285+
286+
#[cfg(unix)]
287+
#[test]
288+
fn test_simulation_of_terminal_pty_sends_eot_automatically() {
289+
let scene = TestScenario::new(util_name!());
290+
291+
let mut cmd = scene.ucmd();
292+
cmd.timeout(std::time::Duration::from_secs(10));
293+
cmd.args(&["cat", "-"]);
294+
cmd.terminal_simulation(true);
295+
let child = cmd.run_no_wait();
296+
let out = child.wait().unwrap(); // cat would block if there is no eot
297+
298+
assert_eq!(String::from_utf8_lossy(out.stdout()), "\r\n");
299+
assert_eq!(String::from_utf8_lossy(out.stderr()), "");
300+
}
301+
302+
#[cfg(unix)]
303+
#[test]
304+
fn test_simulation_of_terminal_pty_pipes_into_data_and_sends_eot_automatically() {
305+
let scene = TestScenario::new(util_name!());
306+
307+
let message = "Hello stdin forwarding!";
308+
309+
let mut cmd = scene.ucmd();
310+
cmd.args(&["cat", "-"]);
311+
cmd.terminal_simulation(true);
312+
cmd.pipe_in(message);
313+
let child = cmd.run_no_wait();
314+
let out = child.wait().unwrap();
315+
316+
assert_eq!(
317+
String::from_utf8_lossy(out.stdout()),
318+
format!("{}\r\n", message)
319+
);
320+
assert_eq!(String::from_utf8_lossy(out.stderr()), "");
321+
}
322+
323+
#[cfg(unix)]
324+
#[test]
325+
fn test_simulation_of_terminal_pty_write_in_data_and_sends_eot_automatically() {
326+
let scene = TestScenario::new(util_name!());
327+
328+
let mut cmd = scene.ucmd();
329+
cmd.args(&["cat", "-"]);
330+
cmd.terminal_simulation(true);
331+
let mut child = cmd.run_no_wait();
332+
child.write_in("Hello stdin forwarding via write_in!");
333+
let out = child.wait().unwrap();
334+
335+
assert_eq!(
336+
String::from_utf8_lossy(out.stdout()),
337+
"Hello stdin forwarding via write_in!\r\n"
338+
);
339+
assert_eq!(String::from_utf8_lossy(out.stderr()), "");
340+
}

tests/by-util/test_nohup.rs

+30
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
//
33
// For the full copyright and license information, please view the LICENSE
44
// file that was distributed with this source code.
5+
// spell-checker:ignore winsize Openpty openpty xpixel ypixel ptyprocess
56
use crate::common::util::TestScenario;
67
use std::thread::sleep;
78

@@ -31,3 +32,32 @@ fn test_nohup_multiple_args_and_flags() {
3132
assert!(at.file_exists("file1"));
3233
assert!(at.file_exists("file2"));
3334
}
35+
36+
#[test]
37+
#[cfg(any(
38+
target_os = "linux",
39+
target_os = "android",
40+
target_os = "freebsd",
41+
target_vendor = "apple"
42+
))]
43+
fn test_nohup_with_pseudo_terminal_emulation_on_stdin_stdout_stderr_get_replaced() {
44+
let ts = TestScenario::new(util_name!());
45+
let result = ts
46+
.ucmd()
47+
.terminal_simulation(true)
48+
.args(&["sh", "is_atty.sh"])
49+
.succeeds();
50+
51+
assert_eq!(
52+
String::from_utf8_lossy(result.stderr()).trim(),
53+
"nohup: ignoring input and appending output to 'nohup.out'"
54+
);
55+
56+
sleep(std::time::Duration::from_millis(10));
57+
58+
// this proves that nohup was exchanging the stdio file descriptors
59+
assert_eq!(
60+
std::fs::read_to_string(ts.fixtures.plus_as_string("nohup.out")).unwrap(),
61+
"stdin is not atty\nstdout is not atty\nstderr is not atty\n"
62+
);
63+
}

0 commit comments

Comments
 (0)