1 use crate::io::prelude::*;
3 use super::{Command, Output, Stdio};
4 use crate::io::ErrorKind;
7 fn known_command() -> Command {
8 if cfg!(windows) { Command::new("help") } else { Command::new("echo") }
11 #[cfg(target_os = "android")]
12 fn shell_cmd() -> Command {
13 Command::new("/system/bin/sh")
16 #[cfg(not(target_os = "android"))]
17 fn shell_cmd() -> Command {
18 Command::new("/bin/sh")
22 #[cfg_attr(any(target_os = "vxworks"), ignore)]
24 let p = if cfg!(target_os = "windows") {
25 Command::new("cmd").args(&["/C", "exit 0"]).spawn()
27 shell_cmd().arg("-c").arg("true").spawn()
30 let mut p = p.unwrap();
31 assert!(p.wait().unwrap().success());
35 #[cfg_attr(target_os = "android", ignore)]
37 match Command::new("if-this-is-a-binary-then-the-world-has-ended").spawn() {
44 #[cfg_attr(any(target_os = "vxworks"), ignore)]
45 fn exit_reported_right() {
46 let p = if cfg!(target_os = "windows") {
47 Command::new("cmd").args(&["/C", "exit 1"]).spawn()
49 shell_cmd().arg("-c").arg("false").spawn()
52 let mut p = p.unwrap();
53 assert!(p.wait().unwrap().code() == Some(1));
59 #[cfg_attr(any(target_os = "vxworks"), ignore)]
60 fn signal_reported_right() {
61 use crate::os::unix::process::ExitStatusExt;
63 let mut p = shell_cmd().arg("-c").arg("read a").stdin(Stdio::piped()).spawn().unwrap();
65 match p.wait().unwrap().signal() {
67 result => panic!("not terminated by signal 9 (instead, {result:?})"),
71 pub fn run_output(mut cmd: Command) -> String {
74 let mut p = p.unwrap();
75 assert!(p.stdout.is_some());
76 let mut ret = String::new();
77 p.stdout.as_mut().unwrap().read_to_string(&mut ret).unwrap();
78 assert!(p.wait().unwrap().success());
83 #[cfg_attr(any(target_os = "vxworks"), ignore)]
85 if cfg!(target_os = "windows") {
86 let mut cmd = Command::new("cmd");
87 cmd.args(&["/C", "echo foobar"]).stdout(Stdio::piped());
88 assert_eq!(run_output(cmd), "foobar\r\n");
90 let mut cmd = shell_cmd();
91 cmd.arg("-c").arg("echo foobar").stdout(Stdio::piped());
92 assert_eq!(run_output(cmd), "foobar\n");
97 #[cfg_attr(any(windows, target_os = "vxworks"), ignore)]
98 fn set_current_dir_works() {
99 let mut cmd = shell_cmd();
100 cmd.arg("-c").arg("pwd").current_dir("/").stdout(Stdio::piped());
101 assert_eq!(run_output(cmd), "/\n");
105 #[cfg_attr(any(windows, target_os = "vxworks"), ignore)]
107 let mut p = shell_cmd()
109 .arg("read line; echo $line")
110 .stdin(Stdio::piped())
111 .stdout(Stdio::piped())
114 p.stdin.as_mut().unwrap().write("foobar".as_bytes()).unwrap();
115 drop(p.stdin.take());
116 let mut out = String::new();
117 p.stdout.as_mut().unwrap().read_to_string(&mut out).unwrap();
118 assert!(p.wait().unwrap().success());
119 assert_eq!(out, "foobar\n");
123 #[cfg_attr(any(target_os = "vxworks"), ignore)]
124 fn test_process_status() {
125 let mut status = if cfg!(target_os = "windows") {
126 Command::new("cmd").args(&["/C", "exit 1"]).status().unwrap()
128 shell_cmd().arg("-c").arg("false").status().unwrap()
130 assert!(status.code() == Some(1));
132 status = if cfg!(target_os = "windows") {
133 Command::new("cmd").args(&["/C", "exit 0"]).status().unwrap()
135 shell_cmd().arg("-c").arg("true").status().unwrap()
137 assert!(status.success());
141 fn test_process_output_fail_to_start() {
142 match Command::new("/no-binary-by-this-name-should-exist").output() {
143 Err(e) => assert_eq!(e.kind(), ErrorKind::NotFound),
149 #[cfg_attr(any(target_os = "vxworks"), ignore)]
150 fn test_process_output_output() {
151 let Output { status, stdout, stderr } = if cfg!(target_os = "windows") {
152 Command::new("cmd").args(&["/C", "echo hello"]).output().unwrap()
154 shell_cmd().arg("-c").arg("echo hello").output().unwrap()
156 let output_str = str::from_utf8(&stdout).unwrap();
158 assert!(status.success());
159 assert_eq!(output_str.trim().to_string(), "hello");
160 assert_eq!(stderr, Vec::new());
164 #[cfg_attr(any(target_os = "vxworks"), ignore)]
165 fn test_process_output_error() {
166 let Output { status, stdout, stderr } = if cfg!(target_os = "windows") {
167 Command::new("cmd").args(&["/C", "mkdir ."]).output().unwrap()
169 Command::new("mkdir").arg("./").output().unwrap()
172 assert!(status.code().is_some());
173 assert!(status.code() != Some(0));
174 assert_eq!(stdout, Vec::new());
175 assert!(!stderr.is_empty());
179 #[cfg_attr(any(target_os = "vxworks"), ignore)]
180 fn test_finish_once() {
181 let mut prog = if cfg!(target_os = "windows") {
182 Command::new("cmd").args(&["/C", "exit 1"]).spawn().unwrap()
184 shell_cmd().arg("-c").arg("false").spawn().unwrap()
186 assert!(prog.wait().unwrap().code() == Some(1));
190 #[cfg_attr(any(target_os = "vxworks"), ignore)]
191 fn test_finish_twice() {
192 let mut prog = if cfg!(target_os = "windows") {
193 Command::new("cmd").args(&["/C", "exit 1"]).spawn().unwrap()
195 shell_cmd().arg("-c").arg("false").spawn().unwrap()
197 assert!(prog.wait().unwrap().code() == Some(1));
198 assert!(prog.wait().unwrap().code() == Some(1));
202 #[cfg_attr(any(target_os = "vxworks"), ignore)]
203 fn test_wait_with_output_once() {
204 let prog = if cfg!(target_os = "windows") {
205 Command::new("cmd").args(&["/C", "echo hello"]).stdout(Stdio::piped()).spawn().unwrap()
207 shell_cmd().arg("-c").arg("echo hello").stdout(Stdio::piped()).spawn().unwrap()
210 let Output { status, stdout, stderr } = prog.wait_with_output().unwrap();
211 let output_str = str::from_utf8(&stdout).unwrap();
213 assert!(status.success());
214 assert_eq!(output_str.trim().to_string(), "hello");
215 assert_eq!(stderr, Vec::new());
218 #[cfg(all(unix, not(target_os = "android")))]
219 pub fn env_cmd() -> Command {
222 #[cfg(target_os = "android")]
223 pub fn env_cmd() -> Command {
224 let mut cmd = Command::new("/system/bin/sh");
225 cmd.arg("-c").arg("set");
230 pub fn env_cmd() -> Command {
231 let mut cmd = Command::new("cmd");
232 cmd.arg("/c").arg("set");
237 #[cfg_attr(target_os = "vxworks", ignore)]
238 fn test_override_env() {
241 // In some build environments (such as chrooted Nix builds), `env` can
242 // only be found in the explicitly-provided PATH env variable, not in
243 // default places such as /bin or /usr/bin. So we need to pass through
244 // PATH to our sub-process.
245 let mut cmd = env_cmd();
246 cmd.env_clear().env("RUN_TEST_NEW_ENV", "123");
247 if let Some(p) = env::var_os("PATH") {
250 let result = cmd.output().unwrap();
251 let output = String::from_utf8_lossy(&result.stdout).to_string();
254 output.contains("RUN_TEST_NEW_ENV=123"),
255 "didn't find RUN_TEST_NEW_ENV inside of:\n\n{output}",
260 #[cfg_attr(target_os = "vxworks", ignore)]
261 fn test_add_to_env() {
262 let result = env_cmd().env("RUN_TEST_NEW_ENV", "123").output().unwrap();
263 let output = String::from_utf8_lossy(&result.stdout).to_string();
266 output.contains("RUN_TEST_NEW_ENV=123"),
267 "didn't find RUN_TEST_NEW_ENV inside of:\n\n{output}"
272 #[cfg_attr(target_os = "vxworks", ignore)]
273 fn test_capture_env_at_spawn() {
276 let mut cmd = env_cmd();
277 cmd.env("RUN_TEST_NEW_ENV1", "123");
279 // This variable will not be present if the environment has already
280 // been captured above.
281 env::set_var("RUN_TEST_NEW_ENV2", "456");
282 let result = cmd.output().unwrap();
283 env::remove_var("RUN_TEST_NEW_ENV2");
285 let output = String::from_utf8_lossy(&result.stdout).to_string();
288 output.contains("RUN_TEST_NEW_ENV1=123"),
289 "didn't find RUN_TEST_NEW_ENV1 inside of:\n\n{output}"
292 output.contains("RUN_TEST_NEW_ENV2=456"),
293 "didn't find RUN_TEST_NEW_ENV2 inside of:\n\n{output}"
297 // Regression tests for #30858.
299 fn test_interior_nul_in_progname_is_error() {
300 match Command::new("has-some-\0\0s-inside").spawn() {
301 Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput),
307 fn test_interior_nul_in_arg_is_error() {
308 match known_command().arg("has-some-\0\0s-inside").spawn() {
309 Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput),
315 fn test_interior_nul_in_args_is_error() {
316 match known_command().args(&["has-some-\0\0s-inside"]).spawn() {
317 Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput),
323 fn test_interior_nul_in_current_dir_is_error() {
324 match known_command().current_dir("has-some-\0\0s-inside").spawn() {
325 Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput),
330 // Regression tests for #30862.
332 #[cfg_attr(target_os = "vxworks", ignore)]
333 fn test_interior_nul_in_env_key_is_error() {
334 match env_cmd().env("has-some-\0\0s-inside", "value").spawn() {
335 Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput),
341 #[cfg_attr(target_os = "vxworks", ignore)]
342 fn test_interior_nul_in_env_value_is_error() {
343 match env_cmd().env("key", "has-some-\0\0s-inside").spawn() {
344 Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput),
349 /// Tests that process creation flags work by debugging a process.
350 /// Other creation flags make it hard or impossible to detect
351 /// behavioral changes in the process.
354 fn test_creation_flags() {
355 use crate::os::windows::process::CommandExt;
356 use crate::sys::c::{BOOL, DWORD, INFINITE};
359 pub event_code: DWORD,
360 pub process_id: DWORD,
361 pub thread_id: DWORD,
362 // This is a union in the real struct, but we don't
363 // need this data for the purposes of this test.
364 pub _junk: [u8; 164],
368 fn WaitForDebugEvent(lpDebugEvent: *mut DEBUG_EVENT, dwMilliseconds: DWORD) -> BOOL;
369 fn ContinueDebugEvent(
372 dwContinueStatus: DWORD,
376 const DEBUG_PROCESS: DWORD = 1;
377 const EXIT_PROCESS_DEBUG_EVENT: DWORD = 5;
378 const DBG_EXCEPTION_NOT_HANDLED: DWORD = 0x80010001;
381 Command::new("cmd").creation_flags(DEBUG_PROCESS).stdin(Stdio::piped()).spawn().unwrap();
382 child.stdin.take().unwrap().write_all(b"exit\r\n").unwrap();
384 let mut event = DEBUG_EVENT { event_code: 0, process_id: 0, thread_id: 0, _junk: [0; 164] };
386 if unsafe { WaitForDebugEvent(&mut event as *mut DEBUG_EVENT, INFINITE) } == 0 {
387 panic!("WaitForDebugEvent failed!");
391 if event.event_code == EXIT_PROCESS_DEBUG_EVENT {
396 ContinueDebugEvent(event.process_id, event.thread_id, DBG_EXCEPTION_NOT_HANDLED)
399 panic!("ContinueDebugEvent failed!");
406 fn test_command_implements_send_sync() {
407 fn take_send_sync_type<T: Send + Sync>(_: T) {}
408 take_send_sync_type(Command::new(""))
411 // Ensure that starting a process with no environment variables works on Windows.
412 // This will fail if the environment block is ill-formed.
416 let p = Command::new("cmd").args(&["/C", "exit 0"]).env_clear().spawn();
422 #[cfg_attr(any(target_os = "emscripten", target_env = "sgx"), ignore)]
424 const PIDFD: &'static str =
425 if cfg!(target_os = "linux") { " create_pidfd: false,\n" } else { "" };
427 let mut command = Command::new("some-boring-name");
429 assert_eq!(format!("{command:?}"), format!(r#""some-boring-name""#));
432 format!("{command:#?}"),
435 program: "some-boring-name",
443 command.args(&["1", "2", "3"]);
445 assert_eq!(format!("{command:?}"), format!(r#""some-boring-name" "1" "2" "3""#));
448 format!("{command:#?}"),
451 program: "some-boring-name",
462 crate::os::unix::process::CommandExt::arg0(&mut command, "exciting-name");
465 format!("{command:?}"),
466 format!(r#"["some-boring-name"] "exciting-name" "1" "2" "3""#)
470 format!("{command:#?}"),
473 program: "some-boring-name",
484 let mut command_with_env_and_cwd = Command::new("boring-name");
485 command_with_env_and_cwd.current_dir("/some/path").env("FOO", "bar");
487 format!("{command_with_env_and_cwd:?}"),
488 r#"cd "/some/path" && FOO="bar" "boring-name""#
491 format!("{command_with_env_and_cwd:#?}"),
494 program: "boring-name",
517 fn run_bat_script() {
518 let tempdir = crate::sys_common::io::test::tmpdir();
519 let script_path = tempdir.join("hello.cmd");
521 crate::fs::write(&script_path, "@echo Hello, %~1!").unwrap();
522 let output = Command::new(&script_path)
523 .arg("fellow Rustaceans")
524 .stdout(crate::process::Stdio::piped())
529 assert!(output.status.success());
530 assert_eq!(String::from_utf8_lossy(&output.stdout).trim(), "Hello, fellow Rustaceans!");
536 fn run_canonical_bat_script() {
537 let tempdir = crate::sys_common::io::test::tmpdir();
538 let script_path = tempdir.join("hello.cmd");
540 crate::fs::write(&script_path, "@echo Hello, %~1!").unwrap();
542 // Try using a canonical path
543 let output = Command::new(&script_path.canonicalize().unwrap())
544 .arg("fellow Rustaceans")
545 .stdout(crate::process::Stdio::piped())
550 assert!(output.status.success());
551 assert_eq!(String::from_utf8_lossy(&output.stdout).trim(), "Hello, fellow Rustaceans!");