mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 11:37:44 +00:00
tests/util: Restructure UCommand to run in shell per default. Introduce constant TESTS_BINARY.
Summary of changes in tests/util: * Introduce global constant TESTS_BINARY holding the path to `coreutils` * Implement running an arbitrary command in a sh or cmd shell per default. * Implement std::fmt::Display for UCommand. * Change usages of UCommand::util_name from &Option<OsStr> to Option<OsStr> * `UCommand::new_from_tmp`: Use OsStr directly instead of TempDir -> String -> OsStr * Collect arguments in `UCommand::args` field * Build environment variables in `UCommand` itself instead of `std::process::Command` * Move building of std::process:Command from fields in UCommand to own method `build` * Remove assertions of UCommand::has_run in arg, args and env. Summary of changes in tests/by-util: * Remove usages of UCommand::raw. Fix tests to use UCommand::to_string. * test_test: Adjust test_invalid_utf8_integer_compare to use `UCommand::args` * test_chmod: run_single_test * test_pwd: symlinked_env * test_cp: Fix the usage of &Option<OsStr> in `test_src_base_dot` Refactor test_src_base_dot to not use UCommand::new but ts.ucmd() instead
This commit is contained in:
parent
2b5b0c82c1
commit
fdf0f96a01
5 changed files with 178 additions and 111 deletions
|
@ -48,15 +48,12 @@ fn run_single_test(test: &TestCase, at: &AtPath, mut ucmd: UCommand) {
|
||||||
let r = ucmd.run();
|
let r = ucmd.run();
|
||||||
if !r.succeeded() {
|
if !r.succeeded() {
|
||||||
println!("{}", r.stderr_str());
|
println!("{}", r.stderr_str());
|
||||||
panic!("{:?}: failed", ucmd.raw);
|
panic!("{}: failed", ucmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
let perms = at.metadata(TEST_FILE).permissions().mode();
|
let perms = at.metadata(TEST_FILE).permissions().mode();
|
||||||
if perms != test.after {
|
if perms != test.after {
|
||||||
panic!(
|
panic!("{}: expected: {:o} got: {:o}", ucmd, test.after, perms);
|
||||||
"{:?}: expected: {:o} got: {:o}",
|
|
||||||
ucmd.raw, test.after, perms
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2524,9 +2524,9 @@ fn test_src_base_dot() {
|
||||||
let at = ts.fixtures.clone();
|
let at = ts.fixtures.clone();
|
||||||
at.mkdir("x");
|
at.mkdir("x");
|
||||||
at.mkdir("y");
|
at.mkdir("y");
|
||||||
let mut ucmd = UCommand::new(ts.bin_path, &Some(ts.util_name), at.plus("y"), true);
|
ts.ucmd()
|
||||||
|
.current_dir(at.plus("y"))
|
||||||
ucmd.args(&["--verbose", "-r", "../x/.", "."])
|
.args(&["--verbose", "-r", "../x/.", "."])
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.no_stderr()
|
.no_stderr()
|
||||||
.no_stdout();
|
.no_stdout();
|
||||||
|
|
|
@ -60,7 +60,7 @@ fn symlinked_env() -> Env {
|
||||||
// Note: on Windows this requires admin permissions
|
// Note: on Windows this requires admin permissions
|
||||||
at.symlink_dir("subdir", "symdir");
|
at.symlink_dir("subdir", "symdir");
|
||||||
let root = PathBuf::from(at.root_dir_resolved());
|
let root = PathBuf::from(at.root_dir_resolved());
|
||||||
ucmd.raw.current_dir(root.join("symdir"));
|
ucmd.current_dir(root.join("symdir"));
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
ucmd.env("PWD", root.join("symdir"));
|
ucmd.env("PWD", root.join("symdir"));
|
||||||
Env {
|
Env {
|
||||||
|
|
|
@ -300,19 +300,15 @@ fn test_invalid_utf8_integer_compare() {
|
||||||
let source = [0x66, 0x6f, 0x80, 0x6f];
|
let source = [0x66, 0x6f, 0x80, 0x6f];
|
||||||
let arg = OsStr::from_bytes(&source[..]);
|
let arg = OsStr::from_bytes(&source[..]);
|
||||||
|
|
||||||
let mut cmd = new_ucmd!();
|
new_ucmd!()
|
||||||
cmd.arg("123").arg("-ne");
|
.args(&[OsStr::new("123"), OsStr::new("-ne"), arg])
|
||||||
cmd.raw.arg(arg);
|
.run()
|
||||||
|
|
||||||
cmd.run()
|
|
||||||
.code_is(2)
|
.code_is(2)
|
||||||
.stderr_is("test: invalid integer $'fo\\x80o'\n");
|
.stderr_is("test: invalid integer $'fo\\x80o'\n");
|
||||||
|
|
||||||
let mut cmd = new_ucmd!();
|
new_ucmd!()
|
||||||
cmd.raw.arg(arg);
|
.args(&[arg, OsStr::new("-eq"), OsStr::new("456")])
|
||||||
cmd.arg("-eq").arg("456");
|
.run()
|
||||||
|
|
||||||
cmd.run()
|
|
||||||
.code_is(2)
|
.code_is(2)
|
||||||
.stderr_is("test: invalid integer $'fo\\x80o'\n");
|
.stderr_is("test: invalid integer $'fo\\x80o'\n");
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
// * For the full copyright and license information, please view the LICENSE
|
// * For the full copyright and license information, please view the LICENSE
|
||||||
// * file that was distributed with this source code.
|
// * file that was distributed with this source code.
|
||||||
|
|
||||||
//spell-checker: ignore (linux) rlimit prlimit coreutil ggroups uchild uncaptured scmd
|
//spell-checker: ignore (linux) rlimit prlimit coreutil ggroups uchild uncaptured scmd SHLVL
|
||||||
|
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ use rstest::rstest;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::ffi::OsStr;
|
use std::ffi::{OsStr, OsString};
|
||||||
use std::fs::{self, hard_link, remove_file, File, OpenOptions};
|
use std::fs::{self, hard_link, remove_file, File, OpenOptions};
|
||||||
use std::io::{self, BufWriter, Read, Result, Write};
|
use std::io::{self, BufWriter, Read, Result, Write};
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
|
@ -34,7 +34,6 @@ use std::thread::{sleep, JoinHandle};
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
use std::{env, hint, thread};
|
use std::{env, hint, thread};
|
||||||
use tempfile::{Builder, TempDir};
|
use tempfile::{Builder, TempDir};
|
||||||
use uucore::Args;
|
|
||||||
|
|
||||||
static TESTS_DIR: &str = "tests";
|
static TESTS_DIR: &str = "tests";
|
||||||
static FIXTURES_DIR: &str = "fixtures";
|
static FIXTURES_DIR: &str = "fixtures";
|
||||||
|
@ -46,6 +45,8 @@ static MULTIPLE_STDIN_MEANINGLESS: &str = "Ucommand is designed around a typical
|
||||||
|
|
||||||
static NO_STDIN_MEANINGLESS: &str = "Setting this flag has no effect if there is no stdin";
|
static NO_STDIN_MEANINGLESS: &str = "Setting this flag has no effect if there is no stdin";
|
||||||
|
|
||||||
|
pub const TESTS_BINARY: &str = env!("CARGO_BIN_EXE_coreutils");
|
||||||
|
|
||||||
/// Test if the program is running under CI
|
/// Test if the program is running under CI
|
||||||
pub fn is_ci() -> bool {
|
pub fn is_ci() -> bool {
|
||||||
std::env::var("CI")
|
std::env::var("CI")
|
||||||
|
@ -1096,7 +1097,7 @@ impl TestScenario {
|
||||||
pub fn new(util_name: &str) -> Self {
|
pub fn new(util_name: &str) -> Self {
|
||||||
let tmpd = Rc::new(TempDir::new().unwrap());
|
let tmpd = Rc::new(TempDir::new().unwrap());
|
||||||
let ts = Self {
|
let ts = Self {
|
||||||
bin_path: PathBuf::from(env!("CARGO_BIN_EXE_coreutils")),
|
bin_path: PathBuf::from(TESTS_BINARY),
|
||||||
util_name: String::from(util_name),
|
util_name: String::from(util_name),
|
||||||
fixtures: AtPath::new(tmpd.as_ref().path()),
|
fixtures: AtPath::new(tmpd.as_ref().path()),
|
||||||
tmpd,
|
tmpd,
|
||||||
|
@ -1127,13 +1128,13 @@ impl TestScenario {
|
||||||
util_name: T,
|
util_name: T,
|
||||||
env_clear: bool,
|
env_clear: bool,
|
||||||
) -> UCommand {
|
) -> UCommand {
|
||||||
UCommand::new_from_tmp(bin, &Some(util_name), self.tmpd.clone(), env_clear)
|
UCommand::new_from_tmp(bin, Some(util_name), self.tmpd.clone(), env_clear)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns builder for invoking any system command. Paths given are treated
|
/// Returns builder for invoking any system command. Paths given are treated
|
||||||
/// relative to the environment's unique temporary test directory.
|
/// relative to the environment's unique temporary test directory.
|
||||||
pub fn cmd<S: AsRef<OsStr>>(&self, bin: S) -> UCommand {
|
pub fn cmd<S: AsRef<OsStr>>(&self, bin: S) -> UCommand {
|
||||||
UCommand::new_from_tmp::<S, S>(bin, &None, self.tmpd.clone(), true)
|
UCommand::new_from_tmp::<S, S>(bin, None, self.tmpd.clone(), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns builder for invoking any uutils command. Paths given are treated
|
/// Returns builder for invoking any uutils command. Paths given are treated
|
||||||
|
@ -1153,7 +1154,7 @@ impl TestScenario {
|
||||||
/// Differs from the builder returned by `cmd` in that `cmd_keepenv` does not call
|
/// Differs from the builder returned by `cmd` in that `cmd_keepenv` does not call
|
||||||
/// `Command::env_clear` (Clears the entire environment map for the child process.)
|
/// `Command::env_clear` (Clears the entire environment map for the child process.)
|
||||||
pub fn cmd_keepenv<S: AsRef<OsStr>>(&self, bin: S) -> UCommand {
|
pub fn cmd_keepenv<S: AsRef<OsStr>>(&self, bin: S) -> UCommand {
|
||||||
UCommand::new_from_tmp::<S, S>(bin, &None, self.tmpd.clone(), false)
|
UCommand::new_from_tmp::<S, S>(bin, None, self.tmpd.clone(), false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1165,16 +1166,19 @@ impl TestScenario {
|
||||||
/// 3. it provides convenience construction arguments to set the Command working directory and/or clear its environment.
|
/// 3. it provides convenience construction arguments to set the Command working directory and/or clear its environment.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct UCommand {
|
pub struct UCommand {
|
||||||
pub raw: Command,
|
args: Vec<OsString>,
|
||||||
comm_string: String,
|
env_vars: Vec<(OsString, OsString)>,
|
||||||
bin_path: String,
|
current_dir: Option<PathBuf>,
|
||||||
util_name: Option<String>,
|
env_clear: bool,
|
||||||
|
bin_path: Option<PathBuf>,
|
||||||
|
util_name: Option<OsString>,
|
||||||
has_run: bool,
|
has_run: bool,
|
||||||
ignore_stdin_write_error: bool,
|
ignore_stdin_write_error: bool,
|
||||||
stdin: Option<Stdio>,
|
stdin: Option<Stdio>,
|
||||||
stdout: Option<Stdio>,
|
stdout: Option<Stdio>,
|
||||||
stderr: Option<Stdio>,
|
stderr: Option<Stdio>,
|
||||||
bytes_into_stdin: Option<Vec<u8>>,
|
bytes_into_stdin: Option<Vec<u8>>,
|
||||||
|
// TODO: Why android?
|
||||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
limits: Vec<(rlimit::Resource, u64, u64)>,
|
limits: Vec<(rlimit::Resource, u64, u64)>,
|
||||||
stderr_to_stdout: bool,
|
stderr_to_stdout: bool,
|
||||||
|
@ -1183,74 +1187,51 @@ pub struct UCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UCommand {
|
impl UCommand {
|
||||||
pub fn new<T: AsRef<OsStr>, S: AsRef<OsStr>, U: AsRef<OsStr>>(
|
pub fn new() -> Self {
|
||||||
bin_path: T,
|
Self {
|
||||||
util_name: &Option<S>,
|
|
||||||
curdir: U,
|
|
||||||
env_clear: bool,
|
|
||||||
) -> Self {
|
|
||||||
let bin_path = bin_path.as_ref();
|
|
||||||
let util_name = util_name.as_ref().map(std::convert::AsRef::as_ref);
|
|
||||||
|
|
||||||
let mut ucmd = Self {
|
|
||||||
tmpd: None,
|
tmpd: None,
|
||||||
has_run: false,
|
has_run: false,
|
||||||
raw: {
|
bin_path: None,
|
||||||
let mut cmd = Command::new(bin_path);
|
current_dir: None,
|
||||||
cmd.current_dir(curdir.as_ref());
|
args: vec![],
|
||||||
if env_clear {
|
env_clear: true,
|
||||||
cmd.env_clear();
|
env_vars: vec![],
|
||||||
if cfg!(windows) {
|
util_name: None,
|
||||||
// spell-checker:ignore (dll) rsaenh
|
|
||||||
// %SYSTEMROOT% is required on Windows to initialize crypto provider
|
|
||||||
// ... and crypto provider is required for std::rand
|
|
||||||
// From `procmon`: RegQueryValue HKLM\SOFTWARE\Microsoft\Cryptography\Defaults\Provider\Microsoft Strong Cryptographic Provider\Image Path
|
|
||||||
// SUCCESS Type: REG_SZ, Length: 66, Data: %SystemRoot%\system32\rsaenh.dll"
|
|
||||||
if let Some(systemroot) = env::var_os("SYSTEMROOT") {
|
|
||||||
cmd.env("SYSTEMROOT", systemroot);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// if someone is setting LD_PRELOAD, there's probably a good reason for it
|
|
||||||
if let Some(ld_preload) = env::var_os("LD_PRELOAD") {
|
|
||||||
cmd.env("LD_PRELOAD", ld_preload);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cmd
|
|
||||||
},
|
|
||||||
comm_string: String::from(bin_path.to_str().unwrap()),
|
|
||||||
bin_path: bin_path.to_str().unwrap().to_string(),
|
|
||||||
util_name: util_name.map(|un| un.to_str().unwrap().to_string()),
|
|
||||||
ignore_stdin_write_error: false,
|
ignore_stdin_write_error: false,
|
||||||
bytes_into_stdin: None,
|
bytes_into_stdin: None,
|
||||||
stdin: None,
|
stdin: None,
|
||||||
stdout: None,
|
stdout: None,
|
||||||
stderr: None,
|
stderr: None,
|
||||||
|
// TODO: Why android?
|
||||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
limits: vec![],
|
limits: vec![],
|
||||||
stderr_to_stdout: false,
|
stderr_to_stdout: false,
|
||||||
timeout: Some(Duration::from_secs(30)),
|
timeout: Some(Duration::from_secs(30)),
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(un) = util_name {
|
|
||||||
ucmd.arg(un);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ucmd
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_from_tmp<T: AsRef<OsStr>, S: AsRef<OsStr>>(
|
pub fn new_from_tmp<T: AsRef<OsStr>, S: AsRef<OsStr>>(
|
||||||
bin_path: T,
|
bin_path: T,
|
||||||
util_name: &Option<S>,
|
util_name: Option<S>,
|
||||||
tmpd: Rc<TempDir>,
|
tmpd: Rc<TempDir>,
|
||||||
env_clear: bool,
|
env_clear: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let tmpd_path_buf = String::from(tmpd.as_ref().path().to_str().unwrap());
|
let mut ucmd: Self = Self::new();
|
||||||
let mut ucmd: Self = Self::new(bin_path, util_name, tmpd_path_buf, env_clear);
|
ucmd.bin_path = Some(PathBuf::from(bin_path.as_ref()));
|
||||||
|
ucmd.util_name = util_name.map(|s| s.as_ref().to_os_string());
|
||||||
ucmd.tmpd = Some(tmpd);
|
ucmd.tmpd = Some(tmpd);
|
||||||
|
ucmd.env_clear = env_clear;
|
||||||
ucmd
|
ucmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn current_dir<T>(&mut self, current_dir: T) -> &mut Self
|
||||||
|
where
|
||||||
|
T: AsRef<Path>,
|
||||||
|
{
|
||||||
|
self.current_dir = Some(current_dir.as_ref().into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_stdin<T: Into<Stdio>>(&mut self, stdin: T) -> &mut Self {
|
pub fn set_stdin<T: Into<Stdio>>(&mut self, stdin: T) -> &mut Self {
|
||||||
self.stdin = Some(stdin.into());
|
self.stdin = Some(stdin.into());
|
||||||
self
|
self
|
||||||
|
@ -1274,29 +1255,14 @@ impl UCommand {
|
||||||
/// Add a parameter to the invocation. Path arguments are treated relative
|
/// Add a parameter to the invocation. Path arguments are treated relative
|
||||||
/// to the test environment directory.
|
/// to the test environment directory.
|
||||||
pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Self {
|
pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Self {
|
||||||
assert!(!self.has_run, "{}", ALREADY_RUN);
|
self.args.push(arg.as_ref().into());
|
||||||
self.comm_string.push(' ');
|
|
||||||
self.comm_string
|
|
||||||
.push_str(arg.as_ref().to_str().unwrap_or_default());
|
|
||||||
self.raw.arg(arg.as_ref());
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add multiple parameters to the invocation. Path arguments are treated relative
|
/// Add multiple parameters to the invocation. Path arguments are treated relative
|
||||||
/// to the test environment directory.
|
/// to the test environment directory.
|
||||||
pub fn args<S: AsRef<OsStr>>(&mut self, args: &[S]) -> &mut Self {
|
pub fn args<S: AsRef<OsStr>>(&mut self, args: &[S]) -> &mut Self {
|
||||||
assert!(!self.has_run, "{}", MULTIPLE_STDIN_MEANINGLESS);
|
self.args.extend(args.iter().map(|s| s.as_ref().into()));
|
||||||
let strings = args
|
|
||||||
.iter()
|
|
||||||
.map(|s| s.as_ref().to_os_string())
|
|
||||||
.collect_ignore();
|
|
||||||
|
|
||||||
for s in strings {
|
|
||||||
self.comm_string.push(' ');
|
|
||||||
self.comm_string.push_str(&s);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.raw.args(args.as_ref());
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1331,11 +1297,12 @@ impl UCommand {
|
||||||
K: AsRef<OsStr>,
|
K: AsRef<OsStr>,
|
||||||
V: AsRef<OsStr>,
|
V: AsRef<OsStr>,
|
||||||
{
|
{
|
||||||
assert!(!self.has_run, "{}", ALREADY_RUN);
|
self.env_vars
|
||||||
self.raw.env(key, val);
|
.push((key.as_ref().into(), val.as_ref().into()));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Why android?
|
||||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
pub fn with_limit(
|
pub fn with_limit(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -1359,26 +1326,85 @@ impl UCommand {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Spawns the command, feeds the stdin if any, and returns the
|
// TODO: make public?
|
||||||
/// child process immediately.
|
fn build(&mut self) -> (Command, Option<CapturedOutput>, Option<CapturedOutput>) {
|
||||||
pub fn run_no_wait(&mut self) -> UChild {
|
if self.bin_path.is_some() {
|
||||||
assert!(!self.has_run, "{}", ALREADY_RUN);
|
if let Some(util_name) = &self.util_name {
|
||||||
self.has_run = true;
|
self.args.insert(0, OsString::from(util_name));
|
||||||
log_info("run", &self.comm_string);
|
}
|
||||||
|
} else if let Some(util_name) = &self.util_name {
|
||||||
|
self.bin_path = Some(PathBuf::from(TESTS_BINARY));
|
||||||
|
self.args.insert(0, OsString::from(util_name));
|
||||||
|
} else if cfg!(unix) {
|
||||||
|
let bin_path = if cfg!(target_os = "android") {
|
||||||
|
PathBuf::from("/system/bin/sh")
|
||||||
|
} else {
|
||||||
|
PathBuf::from("/bin/sh")
|
||||||
|
};
|
||||||
|
self.bin_path = Some(bin_path);
|
||||||
|
let c_arg = OsString::from("-c");
|
||||||
|
if !self.args.contains(&c_arg) {
|
||||||
|
self.args.insert(0, c_arg);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.bin_path = Some(PathBuf::from("cmd"));
|
||||||
|
let c_arg = OsString::from("/C");
|
||||||
|
let k_arg = OsString::from("/K");
|
||||||
|
if !self
|
||||||
|
.args
|
||||||
|
.iter()
|
||||||
|
.any(|s| s.eq_ignore_ascii_case(&c_arg) || s.eq_ignore_ascii_case(&k_arg))
|
||||||
|
{
|
||||||
|
self.args.insert(0, c_arg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut command = Command::new(self.bin_path.as_ref().unwrap());
|
||||||
|
command.args(&self.args);
|
||||||
|
|
||||||
|
if self.tmpd.is_none() {
|
||||||
|
self.tmpd = Some(Rc::new(tempfile::tempdir().unwrap()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(current_dir) = &self.current_dir {
|
||||||
|
command.current_dir(current_dir);
|
||||||
|
} else {
|
||||||
|
command.current_dir(self.tmpd.as_ref().unwrap().path());
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.env_clear {
|
||||||
|
command.env_clear();
|
||||||
|
if cfg!(windows) {
|
||||||
|
// spell-checker:ignore (dll) rsaenh
|
||||||
|
// %SYSTEMROOT% is required on Windows to initialize crypto provider
|
||||||
|
// ... and crypto provider is required for std::rand
|
||||||
|
// From `procmon`: RegQueryValue HKLM\SOFTWARE\Microsoft\Cryptography\Defaults\Provider\Microsoft Strong Cryptographic Provider\Image Path
|
||||||
|
// SUCCESS Type: REG_SZ, Length: 66, Data: %SystemRoot%\system32\rsaenh.dll"
|
||||||
|
if let Some(systemroot) = env::var_os("SYSTEMROOT") {
|
||||||
|
command.env("SYSTEMROOT", systemroot);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// if someone is setting LD_PRELOAD, there's probably a good reason for it
|
||||||
|
if let Some(ld_preload) = env::var_os("LD_PRELOAD") {
|
||||||
|
command.env("LD_PRELOAD", ld_preload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (key, value) in &self.env_vars {
|
||||||
|
command.env(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
let mut captured_stdout = None;
|
let mut captured_stdout = None;
|
||||||
let mut captured_stderr = None;
|
let mut captured_stderr = None;
|
||||||
let command = if self.stderr_to_stdout {
|
if self.stderr_to_stdout {
|
||||||
let mut output = CapturedOutput::default();
|
let mut output = CapturedOutput::default();
|
||||||
|
|
||||||
let command = self
|
command
|
||||||
.raw
|
|
||||||
.stdin(self.stdin.take().unwrap_or_else(Stdio::null))
|
.stdin(self.stdin.take().unwrap_or_else(Stdio::null))
|
||||||
.stdout(Stdio::from(output.try_clone().unwrap()))
|
.stdout(Stdio::from(output.try_clone().unwrap()))
|
||||||
.stderr(Stdio::from(output.try_clone().unwrap()));
|
.stderr(Stdio::from(output.try_clone().unwrap()));
|
||||||
captured_stdout = Some(output);
|
captured_stdout = Some(output);
|
||||||
|
|
||||||
command
|
|
||||||
} else {
|
} else {
|
||||||
let stdout = if self.stdout.is_some() {
|
let stdout = if self.stdout.is_some() {
|
||||||
self.stdout.take().unwrap()
|
self.stdout.take().unwrap()
|
||||||
|
@ -1398,12 +1424,25 @@ impl UCommand {
|
||||||
stdio
|
stdio
|
||||||
};
|
};
|
||||||
|
|
||||||
self.raw
|
command
|
||||||
.stdin(self.stdin.take().unwrap_or_else(Stdio::null))
|
.stdin(self.stdin.take().unwrap_or_else(Stdio::null))
|
||||||
.stdout(stdout)
|
.stdout(stdout)
|
||||||
.stderr(stderr)
|
.stderr(stderr);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
(command, captured_stdout, captured_stderr)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Spawns the command, feeds the stdin if any, and returns the
|
||||||
|
/// child process immediately.
|
||||||
|
pub fn run_no_wait(&mut self) -> UChild {
|
||||||
|
// TODO: remove?
|
||||||
|
assert!(!self.has_run, "{}", ALREADY_RUN);
|
||||||
|
self.has_run = true;
|
||||||
|
|
||||||
|
let (mut command, captured_stdout, captured_stderr) = self.build();
|
||||||
|
log_info("run", self.to_string());
|
||||||
|
|
||||||
let child = command.spawn().unwrap();
|
let child = command.spawn().unwrap();
|
||||||
|
|
||||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
|
@ -1465,6 +1504,17 @@ impl UCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for UCommand {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let mut comm_string: Vec<String> = vec![self
|
||||||
|
.bin_path
|
||||||
|
.as_ref()
|
||||||
|
.map_or("".to_string(), |p| p.display().to_string())];
|
||||||
|
comm_string.extend(self.args.iter().map(|s| s.to_string_lossy().to_string()));
|
||||||
|
f.write_str(&comm_string.join(" "))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Stored the captured output in a temporary file. The file is deleted as soon as
|
/// Stored the captured output in a temporary file. The file is deleted as soon as
|
||||||
/// [`CapturedOutput`] is dropped.
|
/// [`CapturedOutput`] is dropped.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -1704,8 +1754,11 @@ impl UChild {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
raw: child,
|
raw: child,
|
||||||
bin_path: ucommand.bin_path.clone(),
|
bin_path: ucommand.bin_path.as_ref().unwrap().display().to_string(),
|
||||||
util_name: ucommand.util_name.clone(),
|
util_name: ucommand
|
||||||
|
.util_name
|
||||||
|
.clone()
|
||||||
|
.map(|s| s.to_string_lossy().to_string()),
|
||||||
captured_stdout,
|
captured_stdout,
|
||||||
captured_stderr,
|
captured_stderr,
|
||||||
ignore_stdin_write_error: ucommand.ignore_stdin_write_error,
|
ignore_stdin_write_error: ucommand.ignore_stdin_write_error,
|
||||||
|
@ -2443,7 +2496,7 @@ mod tests {
|
||||||
pub fn run_cmd<T: AsRef<OsStr>>(cmd: T) -> CmdResult {
|
pub fn run_cmd<T: AsRef<OsStr>>(cmd: T) -> CmdResult {
|
||||||
let mut ucmd = UCommand::new_from_tmp::<&str, String>(
|
let mut ucmd = UCommand::new_from_tmp::<&str, String>(
|
||||||
"sh",
|
"sh",
|
||||||
&None,
|
None,
|
||||||
Rc::new(tempfile::tempdir().unwrap()),
|
Rc::new(tempfile::tempdir().unwrap()),
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
@ -2456,7 +2509,7 @@ mod tests {
|
||||||
pub fn run_cmd<T: AsRef<OsStr>>(cmd: T) -> CmdResult {
|
pub fn run_cmd<T: AsRef<OsStr>>(cmd: T) -> CmdResult {
|
||||||
let mut ucmd = UCommand::new_from_tmp::<&str, String>(
|
let mut ucmd = UCommand::new_from_tmp::<&str, String>(
|
||||||
"cmd",
|
"cmd",
|
||||||
&None,
|
None,
|
||||||
Rc::new(tempfile::tempdir().unwrap()),
|
Rc::new(tempfile::tempdir().unwrap()),
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
@ -3200,4 +3253,25 @@ mod tests {
|
||||||
let ts = TestScenario::new("sleep");
|
let ts = TestScenario::new("sleep");
|
||||||
ts.ucmd().timeout(Duration::from_secs(60)).arg("1.0").run();
|
ts.ucmd().timeout(Duration::from_secs(60)).arg("1.0").run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "echo")]
|
||||||
|
#[test]
|
||||||
|
fn test_ucommand_when_default() {
|
||||||
|
let shell_cmd = format!("{} echo -n hello", TESTS_BINARY);
|
||||||
|
|
||||||
|
let mut command = UCommand::new();
|
||||||
|
command.arg(&shell_cmd).succeeds().stdout_is("hello");
|
||||||
|
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
let (expected_bin, expected_arg) = (PathBuf::from("/system/bin/sh"), OsString::from("-c"));
|
||||||
|
#[cfg(all(unix, not(target_os = "android")))]
|
||||||
|
let (expected_bin, expected_arg) = (PathBuf::from("/bin/sh"), OsString::from("-c"));
|
||||||
|
#[cfg(windows)]
|
||||||
|
let (expected_bin, expected_arg) = (PathBuf::from("cmd"), OsString::from("/C"));
|
||||||
|
|
||||||
|
std::assert_eq!(&expected_bin, command.bin_path.as_ref().unwrap());
|
||||||
|
assert!(command.util_name.is_none());
|
||||||
|
std::assert_eq!(command.args, &[expected_arg, OsString::from(&shell_cmd)]);
|
||||||
|
assert!(command.tmpd.is_some());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue