2020-08-27 13:45:01 +00:00
|
|
|
use crate::io::prelude::*;
|
|
|
|
|
|
|
|
|
|
use super::{Command, Output, Stdio};
|
2023-02-21 00:00:00 +00:00
|
|
|
use crate::io::{BorrowedBuf, ErrorKind};
|
|
|
|
|
use crate::mem::MaybeUninit;
|
2020-08-27 13:45:01 +00:00
|
|
|
use crate::str;
|
|
|
|
|
|
2021-12-22 18:31:36 +00:00
|
|
|
fn known_command() -> Command {
|
|
|
|
|
if cfg!(windows) { Command::new("help") } else { Command::new("echo") }
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-23 13:40:19 +08:00
|
|
|
#[cfg(target_os = "android")]
|
|
|
|
|
fn shell_cmd() -> Command {
|
|
|
|
|
Command::new("/system/bin/sh")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(not(target_os = "android"))]
|
|
|
|
|
fn shell_cmd() -> Command {
|
|
|
|
|
Command::new("/bin/sh")
|
|
|
|
|
}
|
2020-08-27 13:45:01 +00:00
|
|
|
|
|
|
|
|
#[test]
|
2021-11-23 13:40:19 +08:00
|
|
|
#[cfg_attr(any(target_os = "vxworks"), ignore)]
|
2020-08-27 13:45:01 +00:00
|
|
|
fn smoke() {
|
|
|
|
|
let p = if cfg!(target_os = "windows") {
|
|
|
|
|
Command::new("cmd").args(&["/C", "exit 0"]).spawn()
|
|
|
|
|
} else {
|
2021-11-23 13:40:19 +08:00
|
|
|
shell_cmd().arg("-c").arg("true").spawn()
|
2020-08-27 13:45:01 +00:00
|
|
|
};
|
|
|
|
|
assert!(p.is_ok());
|
|
|
|
|
let mut p = p.unwrap();
|
|
|
|
|
assert!(p.wait().unwrap().success());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
#[cfg_attr(target_os = "android", ignore)]
|
|
|
|
|
fn smoke_failure() {
|
|
|
|
|
match Command::new("if-this-is-a-binary-then-the-world-has-ended").spawn() {
|
|
|
|
|
Ok(..) => panic!(),
|
|
|
|
|
Err(..) => {}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
2021-11-23 13:40:19 +08:00
|
|
|
#[cfg_attr(any(target_os = "vxworks"), ignore)]
|
2020-08-27 13:45:01 +00:00
|
|
|
fn exit_reported_right() {
|
|
|
|
|
let p = if cfg!(target_os = "windows") {
|
|
|
|
|
Command::new("cmd").args(&["/C", "exit 1"]).spawn()
|
|
|
|
|
} else {
|
2021-11-23 13:40:19 +08:00
|
|
|
shell_cmd().arg("-c").arg("false").spawn()
|
2020-08-27 13:45:01 +00:00
|
|
|
};
|
|
|
|
|
assert!(p.is_ok());
|
|
|
|
|
let mut p = p.unwrap();
|
|
|
|
|
assert!(p.wait().unwrap().code() == Some(1));
|
|
|
|
|
drop(p.wait());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
#[cfg(unix)]
|
2021-11-23 13:40:19 +08:00
|
|
|
#[cfg_attr(any(target_os = "vxworks"), ignore)]
|
2020-08-27 13:45:01 +00:00
|
|
|
fn signal_reported_right() {
|
|
|
|
|
use crate::os::unix::process::ExitStatusExt;
|
|
|
|
|
|
2021-11-23 13:40:19 +08:00
|
|
|
let mut p = shell_cmd().arg("-c").arg("read a").stdin(Stdio::piped()).spawn().unwrap();
|
2020-08-27 13:45:01 +00:00
|
|
|
p.kill().unwrap();
|
|
|
|
|
match p.wait().unwrap().signal() {
|
|
|
|
|
Some(9) => {}
|
2022-02-12 23:16:17 +04:00
|
|
|
result => panic!("not terminated by signal 9 (instead, {result:?})"),
|
2020-08-27 13:45:01 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn run_output(mut cmd: Command) -> String {
|
|
|
|
|
let p = cmd.spawn();
|
|
|
|
|
assert!(p.is_ok());
|
|
|
|
|
let mut p = p.unwrap();
|
|
|
|
|
assert!(p.stdout.is_some());
|
|
|
|
|
let mut ret = String::new();
|
|
|
|
|
p.stdout.as_mut().unwrap().read_to_string(&mut ret).unwrap();
|
|
|
|
|
assert!(p.wait().unwrap().success());
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
2021-11-23 13:40:19 +08:00
|
|
|
#[cfg_attr(any(target_os = "vxworks"), ignore)]
|
2020-08-27 13:45:01 +00:00
|
|
|
fn stdout_works() {
|
|
|
|
|
if cfg!(target_os = "windows") {
|
|
|
|
|
let mut cmd = Command::new("cmd");
|
|
|
|
|
cmd.args(&["/C", "echo foobar"]).stdout(Stdio::piped());
|
|
|
|
|
assert_eq!(run_output(cmd), "foobar\r\n");
|
|
|
|
|
} else {
|
2021-11-23 13:40:19 +08:00
|
|
|
let mut cmd = shell_cmd();
|
|
|
|
|
cmd.arg("-c").arg("echo foobar").stdout(Stdio::piped());
|
2020-08-27 13:45:01 +00:00
|
|
|
assert_eq!(run_output(cmd), "foobar\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
2021-11-23 13:40:19 +08:00
|
|
|
#[cfg_attr(any(windows, target_os = "vxworks"), ignore)]
|
2020-08-27 13:45:01 +00:00
|
|
|
fn set_current_dir_works() {
|
2021-11-23 13:40:19 +08:00
|
|
|
let mut cmd = shell_cmd();
|
2020-08-27 13:45:01 +00:00
|
|
|
cmd.arg("-c").arg("pwd").current_dir("/").stdout(Stdio::piped());
|
|
|
|
|
assert_eq!(run_output(cmd), "/\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
2021-11-23 13:40:19 +08:00
|
|
|
#[cfg_attr(any(windows, target_os = "vxworks"), ignore)]
|
2020-08-27 13:45:01 +00:00
|
|
|
fn stdin_works() {
|
2021-11-23 13:40:19 +08:00
|
|
|
let mut p = shell_cmd()
|
2020-08-27 13:45:01 +00:00
|
|
|
.arg("-c")
|
|
|
|
|
.arg("read line; echo $line")
|
|
|
|
|
.stdin(Stdio::piped())
|
|
|
|
|
.stdout(Stdio::piped())
|
|
|
|
|
.spawn()
|
|
|
|
|
.unwrap();
|
|
|
|
|
p.stdin.as_mut().unwrap().write("foobar".as_bytes()).unwrap();
|
|
|
|
|
drop(p.stdin.take());
|
|
|
|
|
let mut out = String::new();
|
|
|
|
|
p.stdout.as_mut().unwrap().read_to_string(&mut out).unwrap();
|
|
|
|
|
assert!(p.wait().unwrap().success());
|
|
|
|
|
assert_eq!(out, "foobar\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
2021-11-23 13:40:19 +08:00
|
|
|
#[cfg_attr(any(target_os = "vxworks"), ignore)]
|
2023-02-21 00:00:00 +00:00
|
|
|
fn child_stdout_read_buf() {
|
|
|
|
|
let mut cmd = if cfg!(target_os = "windows") {
|
|
|
|
|
let mut cmd = Command::new("cmd");
|
|
|
|
|
cmd.arg("/C").arg("echo abc");
|
|
|
|
|
cmd
|
|
|
|
|
} else {
|
|
|
|
|
let mut cmd = shell_cmd();
|
|
|
|
|
cmd.arg("-c").arg("echo abc");
|
|
|
|
|
cmd
|
|
|
|
|
};
|
|
|
|
|
cmd.stdin(Stdio::null());
|
|
|
|
|
cmd.stdout(Stdio::piped());
|
|
|
|
|
let child = cmd.spawn().unwrap();
|
|
|
|
|
|
|
|
|
|
let mut stdout = child.stdout.unwrap();
|
|
|
|
|
let mut buf: [MaybeUninit<u8>; 128] = MaybeUninit::uninit_array();
|
|
|
|
|
let mut buf = BorrowedBuf::from(buf.as_mut_slice());
|
|
|
|
|
stdout.read_buf(buf.unfilled()).unwrap();
|
|
|
|
|
|
|
|
|
|
// ChildStdout::read_buf should omit buffer initialization.
|
|
|
|
|
if cfg!(target_os = "windows") {
|
|
|
|
|
assert_eq!(buf.filled(), b"abc\r\n");
|
|
|
|
|
assert_eq!(buf.init_len(), 5);
|
|
|
|
|
} else {
|
|
|
|
|
assert_eq!(buf.filled(), b"abc\n");
|
|
|
|
|
assert_eq!(buf.init_len(), 4);
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
#[cfg_attr(any(target_os = "vxworks"), ignore)]
|
2020-08-27 13:45:01 +00:00
|
|
|
fn test_process_status() {
|
|
|
|
|
let mut status = if cfg!(target_os = "windows") {
|
|
|
|
|
Command::new("cmd").args(&["/C", "exit 1"]).status().unwrap()
|
|
|
|
|
} else {
|
2021-11-23 13:40:19 +08:00
|
|
|
shell_cmd().arg("-c").arg("false").status().unwrap()
|
2020-08-27 13:45:01 +00:00
|
|
|
};
|
|
|
|
|
assert!(status.code() == Some(1));
|
|
|
|
|
|
|
|
|
|
status = if cfg!(target_os = "windows") {
|
|
|
|
|
Command::new("cmd").args(&["/C", "exit 0"]).status().unwrap()
|
|
|
|
|
} else {
|
2021-11-23 13:40:19 +08:00
|
|
|
shell_cmd().arg("-c").arg("true").status().unwrap()
|
2020-08-27 13:45:01 +00:00
|
|
|
};
|
|
|
|
|
assert!(status.success());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_process_output_fail_to_start() {
|
|
|
|
|
match Command::new("/no-binary-by-this-name-should-exist").output() {
|
|
|
|
|
Err(e) => assert_eq!(e.kind(), ErrorKind::NotFound),
|
|
|
|
|
Ok(..) => panic!(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
2021-11-23 13:40:19 +08:00
|
|
|
#[cfg_attr(any(target_os = "vxworks"), ignore)]
|
2020-08-27 13:45:01 +00:00
|
|
|
fn test_process_output_output() {
|
|
|
|
|
let Output { status, stdout, stderr } = if cfg!(target_os = "windows") {
|
|
|
|
|
Command::new("cmd").args(&["/C", "echo hello"]).output().unwrap()
|
|
|
|
|
} else {
|
2021-11-23 13:40:19 +08:00
|
|
|
shell_cmd().arg("-c").arg("echo hello").output().unwrap()
|
2020-08-27 13:45:01 +00:00
|
|
|
};
|
|
|
|
|
let output_str = str::from_utf8(&stdout).unwrap();
|
|
|
|
|
|
|
|
|
|
assert!(status.success());
|
|
|
|
|
assert_eq!(output_str.trim().to_string(), "hello");
|
|
|
|
|
assert_eq!(stderr, Vec::new());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
2021-11-23 13:40:19 +08:00
|
|
|
#[cfg_attr(any(target_os = "vxworks"), ignore)]
|
2020-08-27 13:45:01 +00:00
|
|
|
fn test_process_output_error() {
|
|
|
|
|
let Output { status, stdout, stderr } = if cfg!(target_os = "windows") {
|
|
|
|
|
Command::new("cmd").args(&["/C", "mkdir ."]).output().unwrap()
|
|
|
|
|
} else {
|
|
|
|
|
Command::new("mkdir").arg("./").output().unwrap()
|
|
|
|
|
};
|
|
|
|
|
|
2021-11-23 13:40:19 +08:00
|
|
|
assert!(status.code().is_some());
|
|
|
|
|
assert!(status.code() != Some(0));
|
2020-08-27 13:45:01 +00:00
|
|
|
assert_eq!(stdout, Vec::new());
|
|
|
|
|
assert!(!stderr.is_empty());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
2021-11-23 13:40:19 +08:00
|
|
|
#[cfg_attr(any(target_os = "vxworks"), ignore)]
|
2020-08-27 13:45:01 +00:00
|
|
|
fn test_finish_once() {
|
|
|
|
|
let mut prog = if cfg!(target_os = "windows") {
|
|
|
|
|
Command::new("cmd").args(&["/C", "exit 1"]).spawn().unwrap()
|
|
|
|
|
} else {
|
2021-11-23 13:40:19 +08:00
|
|
|
shell_cmd().arg("-c").arg("false").spawn().unwrap()
|
2020-08-27 13:45:01 +00:00
|
|
|
};
|
|
|
|
|
assert!(prog.wait().unwrap().code() == Some(1));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
2021-11-23 13:40:19 +08:00
|
|
|
#[cfg_attr(any(target_os = "vxworks"), ignore)]
|
2020-08-27 13:45:01 +00:00
|
|
|
fn test_finish_twice() {
|
|
|
|
|
let mut prog = if cfg!(target_os = "windows") {
|
|
|
|
|
Command::new("cmd").args(&["/C", "exit 1"]).spawn().unwrap()
|
|
|
|
|
} else {
|
2021-11-23 13:40:19 +08:00
|
|
|
shell_cmd().arg("-c").arg("false").spawn().unwrap()
|
2020-08-27 13:45:01 +00:00
|
|
|
};
|
|
|
|
|
assert!(prog.wait().unwrap().code() == Some(1));
|
|
|
|
|
assert!(prog.wait().unwrap().code() == Some(1));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
2021-11-23 13:40:19 +08:00
|
|
|
#[cfg_attr(any(target_os = "vxworks"), ignore)]
|
2020-08-27 13:45:01 +00:00
|
|
|
fn test_wait_with_output_once() {
|
|
|
|
|
let prog = if cfg!(target_os = "windows") {
|
|
|
|
|
Command::new("cmd").args(&["/C", "echo hello"]).stdout(Stdio::piped()).spawn().unwrap()
|
|
|
|
|
} else {
|
2021-11-23 13:40:19 +08:00
|
|
|
shell_cmd().arg("-c").arg("echo hello").stdout(Stdio::piped()).spawn().unwrap()
|
2020-08-27 13:45:01 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let Output { status, stdout, stderr } = prog.wait_with_output().unwrap();
|
|
|
|
|
let output_str = str::from_utf8(&stdout).unwrap();
|
|
|
|
|
|
|
|
|
|
assert!(status.success());
|
|
|
|
|
assert_eq!(output_str.trim().to_string(), "hello");
|
|
|
|
|
assert_eq!(stderr, Vec::new());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(all(unix, not(target_os = "android")))]
|
|
|
|
|
pub fn env_cmd() -> Command {
|
|
|
|
|
Command::new("env")
|
|
|
|
|
}
|
|
|
|
|
#[cfg(target_os = "android")]
|
|
|
|
|
pub fn env_cmd() -> Command {
|
|
|
|
|
let mut cmd = Command::new("/system/bin/sh");
|
|
|
|
|
cmd.arg("-c").arg("set");
|
|
|
|
|
cmd
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
|
pub fn env_cmd() -> Command {
|
|
|
|
|
let mut cmd = Command::new("cmd");
|
|
|
|
|
cmd.arg("/c").arg("set");
|
|
|
|
|
cmd
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
#[cfg_attr(target_os = "vxworks", ignore)]
|
|
|
|
|
fn test_override_env() {
|
|
|
|
|
use crate::env;
|
|
|
|
|
|
|
|
|
|
// In some build environments (such as chrooted Nix builds), `env` can
|
|
|
|
|
// only be found in the explicitly-provided PATH env variable, not in
|
|
|
|
|
// default places such as /bin or /usr/bin. So we need to pass through
|
|
|
|
|
// PATH to our sub-process.
|
|
|
|
|
let mut cmd = env_cmd();
|
|
|
|
|
cmd.env_clear().env("RUN_TEST_NEW_ENV", "123");
|
|
|
|
|
if let Some(p) = env::var_os("PATH") {
|
|
|
|
|
cmd.env("PATH", &p);
|
|
|
|
|
}
|
|
|
|
|
let result = cmd.output().unwrap();
|
|
|
|
|
let output = String::from_utf8_lossy(&result.stdout).to_string();
|
|
|
|
|
|
|
|
|
|
assert!(
|
|
|
|
|
output.contains("RUN_TEST_NEW_ENV=123"),
|
2022-02-12 23:16:17 +04:00
|
|
|
"didn't find RUN_TEST_NEW_ENV inside of:\n\n{output}",
|
2020-08-27 13:45:01 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
#[cfg_attr(target_os = "vxworks", ignore)]
|
|
|
|
|
fn test_add_to_env() {
|
|
|
|
|
let result = env_cmd().env("RUN_TEST_NEW_ENV", "123").output().unwrap();
|
|
|
|
|
let output = String::from_utf8_lossy(&result.stdout).to_string();
|
|
|
|
|
|
|
|
|
|
assert!(
|
|
|
|
|
output.contains("RUN_TEST_NEW_ENV=123"),
|
2022-02-12 23:16:17 +04:00
|
|
|
"didn't find RUN_TEST_NEW_ENV inside of:\n\n{output}"
|
2020-08-27 13:45:01 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
#[cfg_attr(target_os = "vxworks", ignore)]
|
|
|
|
|
fn test_capture_env_at_spawn() {
|
|
|
|
|
use crate::env;
|
|
|
|
|
|
|
|
|
|
let mut cmd = env_cmd();
|
|
|
|
|
cmd.env("RUN_TEST_NEW_ENV1", "123");
|
|
|
|
|
|
|
|
|
|
// This variable will not be present if the environment has already
|
|
|
|
|
// been captured above.
|
|
|
|
|
env::set_var("RUN_TEST_NEW_ENV2", "456");
|
|
|
|
|
let result = cmd.output().unwrap();
|
|
|
|
|
env::remove_var("RUN_TEST_NEW_ENV2");
|
|
|
|
|
|
|
|
|
|
let output = String::from_utf8_lossy(&result.stdout).to_string();
|
|
|
|
|
|
|
|
|
|
assert!(
|
|
|
|
|
output.contains("RUN_TEST_NEW_ENV1=123"),
|
2022-02-12 23:16:17 +04:00
|
|
|
"didn't find RUN_TEST_NEW_ENV1 inside of:\n\n{output}"
|
2020-08-27 13:45:01 +00:00
|
|
|
);
|
|
|
|
|
assert!(
|
|
|
|
|
output.contains("RUN_TEST_NEW_ENV2=456"),
|
2022-02-12 23:16:17 +04:00
|
|
|
"didn't find RUN_TEST_NEW_ENV2 inside of:\n\n{output}"
|
2020-08-27 13:45:01 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Regression tests for #30858.
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_interior_nul_in_progname_is_error() {
|
|
|
|
|
match Command::new("has-some-\0\0s-inside").spawn() {
|
|
|
|
|
Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput),
|
|
|
|
|
Ok(_) => panic!(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_interior_nul_in_arg_is_error() {
|
2021-12-22 18:31:36 +00:00
|
|
|
match known_command().arg("has-some-\0\0s-inside").spawn() {
|
2020-08-27 13:45:01 +00:00
|
|
|
Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput),
|
|
|
|
|
Ok(_) => panic!(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_interior_nul_in_args_is_error() {
|
2021-12-22 18:31:36 +00:00
|
|
|
match known_command().args(&["has-some-\0\0s-inside"]).spawn() {
|
2020-08-27 13:45:01 +00:00
|
|
|
Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput),
|
|
|
|
|
Ok(_) => panic!(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_interior_nul_in_current_dir_is_error() {
|
2021-12-22 18:31:36 +00:00
|
|
|
match known_command().current_dir("has-some-\0\0s-inside").spawn() {
|
2020-08-27 13:45:01 +00:00
|
|
|
Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput),
|
|
|
|
|
Ok(_) => panic!(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Regression tests for #30862.
|
|
|
|
|
#[test]
|
|
|
|
|
#[cfg_attr(target_os = "vxworks", ignore)]
|
|
|
|
|
fn test_interior_nul_in_env_key_is_error() {
|
|
|
|
|
match env_cmd().env("has-some-\0\0s-inside", "value").spawn() {
|
|
|
|
|
Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput),
|
|
|
|
|
Ok(_) => panic!(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
#[cfg_attr(target_os = "vxworks", ignore)]
|
|
|
|
|
fn test_interior_nul_in_env_value_is_error() {
|
|
|
|
|
match env_cmd().env("key", "has-some-\0\0s-inside").spawn() {
|
|
|
|
|
Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput),
|
|
|
|
|
Ok(_) => panic!(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Tests that process creation flags work by debugging a process.
|
|
|
|
|
/// Other creation flags make it hard or impossible to detect
|
|
|
|
|
/// behavioral changes in the process.
|
|
|
|
|
#[test]
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
|
fn test_creation_flags() {
|
|
|
|
|
use crate::os::windows::process::CommandExt;
|
|
|
|
|
use crate::sys::c::{BOOL, DWORD, INFINITE};
|
|
|
|
|
#[repr(C, packed)]
|
|
|
|
|
struct DEBUG_EVENT {
|
|
|
|
|
pub event_code: DWORD,
|
|
|
|
|
pub process_id: DWORD,
|
|
|
|
|
pub thread_id: DWORD,
|
|
|
|
|
// This is a union in the real struct, but we don't
|
|
|
|
|
// need this data for the purposes of this test.
|
|
|
|
|
pub _junk: [u8; 164],
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
extern "system" {
|
|
|
|
|
fn WaitForDebugEvent(lpDebugEvent: *mut DEBUG_EVENT, dwMilliseconds: DWORD) -> BOOL;
|
|
|
|
|
fn ContinueDebugEvent(
|
|
|
|
|
dwProcessId: DWORD,
|
|
|
|
|
dwThreadId: DWORD,
|
|
|
|
|
dwContinueStatus: DWORD,
|
|
|
|
|
) -> BOOL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const DEBUG_PROCESS: DWORD = 1;
|
|
|
|
|
const EXIT_PROCESS_DEBUG_EVENT: DWORD = 5;
|
|
|
|
|
const DBG_EXCEPTION_NOT_HANDLED: DWORD = 0x80010001;
|
|
|
|
|
|
|
|
|
|
let mut child =
|
|
|
|
|
Command::new("cmd").creation_flags(DEBUG_PROCESS).stdin(Stdio::piped()).spawn().unwrap();
|
|
|
|
|
child.stdin.take().unwrap().write_all(b"exit\r\n").unwrap();
|
|
|
|
|
let mut events = 0;
|
|
|
|
|
let mut event = DEBUG_EVENT { event_code: 0, process_id: 0, thread_id: 0, _junk: [0; 164] };
|
|
|
|
|
loop {
|
|
|
|
|
if unsafe { WaitForDebugEvent(&mut event as *mut DEBUG_EVENT, INFINITE) } == 0 {
|
|
|
|
|
panic!("WaitForDebugEvent failed!");
|
|
|
|
|
}
|
|
|
|
|
events += 1;
|
|
|
|
|
|
|
|
|
|
if event.event_code == EXIT_PROCESS_DEBUG_EVENT {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if unsafe {
|
|
|
|
|
ContinueDebugEvent(event.process_id, event.thread_id, DBG_EXCEPTION_NOT_HANDLED)
|
|
|
|
|
} == 0
|
|
|
|
|
{
|
|
|
|
|
panic!("ContinueDebugEvent failed!");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
assert!(events > 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_command_implements_send_sync() {
|
|
|
|
|
fn take_send_sync_type<T: Send + Sync>(_: T) {}
|
|
|
|
|
take_send_sync_type(Command::new(""))
|
|
|
|
|
}
|
2021-06-24 09:32:24 +01:00
|
|
|
|
|
|
|
|
// Ensure that starting a process with no environment variables works on Windows.
|
|
|
|
|
// This will fail if the environment block is ill-formed.
|
|
|
|
|
#[test]
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
|
fn env_empty() {
|
|
|
|
|
let p = Command::new("cmd").args(&["/C", "exit 0"]).env_clear().spawn();
|
|
|
|
|
assert!(p.is_ok());
|
|
|
|
|
}
|
2021-12-16 17:21:34 +00:00
|
|
|
|
2022-05-19 15:50:27 +02:00
|
|
|
#[test]
|
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
|
#[cfg_attr(any(target_os = "emscripten", target_env = "sgx"), ignore)]
|
2023-08-02 21:20:51 +02:00
|
|
|
fn debug_print() {
|
2022-05-19 15:50:27 +02:00
|
|
|
const PIDFD: &'static str =
|
|
|
|
|
if cfg!(target_os = "linux") { " create_pidfd: false,\n" } else { "" };
|
|
|
|
|
|
|
|
|
|
let mut command = Command::new("some-boring-name");
|
|
|
|
|
|
|
|
|
|
assert_eq!(format!("{command:?}"), format!(r#""some-boring-name""#));
|
|
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
|
format!("{command:#?}"),
|
|
|
|
|
format!(
|
|
|
|
|
r#"Command {{
|
|
|
|
|
program: "some-boring-name",
|
|
|
|
|
args: [
|
|
|
|
|
"some-boring-name",
|
|
|
|
|
],
|
|
|
|
|
{PIDFD}}}"#
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
command.args(&["1", "2", "3"]);
|
|
|
|
|
|
|
|
|
|
assert_eq!(format!("{command:?}"), format!(r#""some-boring-name" "1" "2" "3""#));
|
|
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
|
format!("{command:#?}"),
|
|
|
|
|
format!(
|
|
|
|
|
r#"Command {{
|
|
|
|
|
program: "some-boring-name",
|
|
|
|
|
args: [
|
|
|
|
|
"some-boring-name",
|
|
|
|
|
"1",
|
|
|
|
|
"2",
|
|
|
|
|
"3",
|
|
|
|
|
],
|
|
|
|
|
{PIDFD}}}"#
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
crate::os::unix::process::CommandExt::arg0(&mut command, "exciting-name");
|
|
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
|
format!("{command:?}"),
|
|
|
|
|
format!(r#"["some-boring-name"] "exciting-name" "1" "2" "3""#)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
|
format!("{command:#?}"),
|
|
|
|
|
format!(
|
|
|
|
|
r#"Command {{
|
|
|
|
|
program: "some-boring-name",
|
|
|
|
|
args: [
|
|
|
|
|
"exciting-name",
|
|
|
|
|
"1",
|
|
|
|
|
"2",
|
|
|
|
|
"3",
|
|
|
|
|
],
|
|
|
|
|
{PIDFD}}}"#
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let mut command_with_env_and_cwd = Command::new("boring-name");
|
|
|
|
|
command_with_env_and_cwd.current_dir("/some/path").env("FOO", "bar");
|
|
|
|
|
assert_eq!(
|
|
|
|
|
format!("{command_with_env_and_cwd:?}"),
|
|
|
|
|
r#"cd "/some/path" && FOO="bar" "boring-name""#
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
format!("{command_with_env_and_cwd:#?}"),
|
|
|
|
|
format!(
|
|
|
|
|
r#"Command {{
|
|
|
|
|
program: "boring-name",
|
|
|
|
|
args: [
|
|
|
|
|
"boring-name",
|
|
|
|
|
],
|
|
|
|
|
env: CommandEnv {{
|
|
|
|
|
clear: false,
|
|
|
|
|
vars: {{
|
|
|
|
|
"FOO": Some(
|
|
|
|
|
"bar",
|
|
|
|
|
),
|
|
|
|
|
}},
|
|
|
|
|
}},
|
|
|
|
|
cwd: Some(
|
|
|
|
|
"/some/path",
|
|
|
|
|
),
|
2023-08-02 21:20:51 +02:00
|
|
|
{PIDFD}}}"#
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let mut command_with_removed_env = Command::new("boring-name");
|
2023-08-03 09:22:54 +02:00
|
|
|
command_with_removed_env.env_remove("FOO").env_remove("BAR");
|
|
|
|
|
assert_eq!(format!("{command_with_removed_env:?}"), r#"unset BAR FOO && "boring-name""#);
|
2023-08-02 21:20:51 +02:00
|
|
|
assert_eq!(
|
|
|
|
|
format!("{command_with_removed_env:#?}"),
|
|
|
|
|
format!(
|
|
|
|
|
r#"Command {{
|
|
|
|
|
program: "boring-name",
|
|
|
|
|
args: [
|
|
|
|
|
"boring-name",
|
|
|
|
|
],
|
|
|
|
|
env: CommandEnv {{
|
|
|
|
|
clear: false,
|
|
|
|
|
vars: {{
|
|
|
|
|
"BAR": None,
|
2023-08-03 09:22:54 +02:00
|
|
|
"FOO": None,
|
2023-08-02 21:20:51 +02:00
|
|
|
}},
|
|
|
|
|
}},
|
2022-05-19 15:50:27 +02:00
|
|
|
{PIDFD}}}"#
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-16 17:21:34 +00:00
|
|
|
// See issue #91991
|
|
|
|
|
#[test]
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
|
fn run_bat_script() {
|
|
|
|
|
let tempdir = crate::sys_common::io::test::tmpdir();
|
|
|
|
|
let script_path = tempdir.join("hello.cmd");
|
|
|
|
|
|
|
|
|
|
crate::fs::write(&script_path, "@echo Hello, %~1!").unwrap();
|
|
|
|
|
let output = Command::new(&script_path)
|
|
|
|
|
.arg("fellow Rustaceans")
|
|
|
|
|
.stdout(crate::process::Stdio::piped())
|
|
|
|
|
.spawn()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.wait_with_output()
|
|
|
|
|
.unwrap();
|
|
|
|
|
assert!(output.status.success());
|
|
|
|
|
assert_eq!(String::from_utf8_lossy(&output.stdout).trim(), "Hello, fellow Rustaceans!");
|
|
|
|
|
}
|
2022-03-23 04:51:26 +00:00
|
|
|
|
|
|
|
|
// See issue #95178
|
|
|
|
|
#[test]
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
|
fn run_canonical_bat_script() {
|
|
|
|
|
let tempdir = crate::sys_common::io::test::tmpdir();
|
|
|
|
|
let script_path = tempdir.join("hello.cmd");
|
|
|
|
|
|
|
|
|
|
crate::fs::write(&script_path, "@echo Hello, %~1!").unwrap();
|
|
|
|
|
|
|
|
|
|
// Try using a canonical path
|
|
|
|
|
let output = Command::new(&script_path.canonicalize().unwrap())
|
|
|
|
|
.arg("fellow Rustaceans")
|
|
|
|
|
.stdout(crate::process::Stdio::piped())
|
|
|
|
|
.spawn()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.wait_with_output()
|
|
|
|
|
.unwrap();
|
|
|
|
|
assert!(output.status.success());
|
|
|
|
|
assert_eq!(String::from_utf8_lossy(&output.stdout).trim(), "Hello, fellow Rustaceans!");
|
|
|
|
|
}
|
2023-07-01 01:34:06 +01:00
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn terminate_exited_process() {
|
2023-07-05 09:54:16 +01:00
|
|
|
let mut cmd = if cfg!(target_os = "android") {
|
|
|
|
|
let mut p = shell_cmd();
|
|
|
|
|
p.args(&["-c", "true"]);
|
|
|
|
|
p
|
|
|
|
|
} else {
|
|
|
|
|
known_command()
|
|
|
|
|
};
|
|
|
|
|
let mut p = cmd.stdout(Stdio::null()).spawn().unwrap();
|
2023-07-01 01:34:06 +01:00
|
|
|
p.wait().unwrap();
|
|
|
|
|
assert!(p.kill().is_ok());
|
|
|
|
|
assert!(p.kill().is_ok());
|
|
|
|
|
}
|