ito_test_support/pty/
mod.rs1use std::io::{Read, Write};
4use std::path::Path;
5
6use portable_pty::{CommandBuilder, PtySize, native_pty_system};
7
8use crate::CmdOutput;
9
10pub fn run_pty(program: &Path, args: &[&str], cwd: &Path, home: &Path, input: &str) -> CmdOutput {
17 run_pty_with_interactive_env(program, args, cwd, home, input, false, &[])
18}
19
20pub fn run_pty_interactive(
25 program: &Path,
26 args: &[&str],
27 cwd: &Path,
28 home: &Path,
29 input: &str,
30) -> CmdOutput {
31 run_pty_with_interactive_env(program, args, cwd, home, input, true, &[])
32}
33
34pub fn run_pty_interactive_with_env(
36 program: &Path,
37 args: &[&str],
38 cwd: &Path,
39 home: &Path,
40 input: &str,
41 env: &[(&str, &str)],
42) -> CmdOutput {
43 run_pty_with_interactive_env(program, args, cwd, home, input, true, env)
44}
45
46fn run_pty_with_interactive_env(
47 program: &Path,
48 args: &[&str],
49 cwd: &Path,
50 home: &Path,
51 input: &str,
52 interactive: bool,
53 env: &[(&str, &str)],
54) -> CmdOutput {
55 let pty_system = native_pty_system();
56 let pair = pty_system
57 .openpty(PtySize {
58 rows: 24,
59 cols: 120,
60 pixel_width: 0,
61 pixel_height: 0,
62 })
63 .expect("openpty");
64
65 let mut cmd = CommandBuilder::new(program);
66 cmd.args(args);
67 cmd.cwd(cwd);
68 cmd.env("CI", "1");
69 cmd.env("NO_COLOR", "1");
70 let interactive_value = match interactive {
71 true => "1",
72 false => "0",
73 };
74 cmd.env("ITO_INTERACTIVE", interactive_value);
75 cmd.env("TERM", "dumb");
76 cmd.env("HOME", home);
77 cmd.env("XDG_CONFIG_HOME", home.join(".config"));
78 cmd.env("XDG_DATA_HOME", home);
79
80 for (k, v) in env {
81 cmd.env(k, v);
82 }
83
84 let mut child = pair.slave.spawn_command(cmd).expect("spawn_command");
85 drop(pair.slave);
86
87 if !input.is_empty() {
88 let mut writer = pair.master.take_writer().expect("take_writer");
89 writer.write_all(input.as_bytes()).expect("write input");
90 writer.flush().ok();
91 }
92
93 let mut reader = pair.master.try_clone_reader().expect("clone_reader");
95 let mut out = String::new();
96 reader.read_to_string(&mut out).ok();
97
98 let status = child.wait().expect("wait");
99 let code = status.exit_code() as i32;
100
101 CmdOutput {
102 code,
103 stdout: out,
104 stderr: String::new(),
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[test]
114 #[cfg(unix)]
115 fn pty_can_echo_input_via_cat() {
116 let home = tempfile::tempdir().expect("home");
118 let cwd = tempfile::tempdir().expect("cwd");
119
120 let out = run_pty(Path::new("cat"), &[], cwd.path(), home.path(), "hello\n");
121 assert_eq!(out.code, 0);
122 assert!(out.stdout.contains("hello"));
123 }
124}