2023-08-21 08:49:27 +00:00
|
|
|
|
// This file is part of the uutils coreutils package.
|
|
|
|
|
//
|
|
|
|
|
// For the full copyright and license information, please view the LICENSE
|
|
|
|
|
// file that was distributed with this source code.
|
2024-03-14 18:38:28 +00:00
|
|
|
|
// spell-checker:ignore (words) bamf chdir rlimit prlimit COMSPEC cout cerr FFFD
|
2024-06-30 14:27:08 +00:00
|
|
|
|
#![allow(clippy::missing_errors_doc)]
|
2020-08-03 15:32:31 +00:00
|
|
|
|
|
2023-03-20 13:51:19 +00:00
|
|
|
|
use crate::common::util::TestScenario;
|
2024-05-23 20:01:39 +00:00
|
|
|
|
#[cfg(unix)]
|
|
|
|
|
use crate::common::util::UChild;
|
|
|
|
|
#[cfg(unix)]
|
|
|
|
|
use nix::sys::signal::Signal;
|
2024-03-30 20:19:35 +00:00
|
|
|
|
use regex::Regex;
|
2020-08-03 15:32:31 +00:00
|
|
|
|
use std::env;
|
2021-01-19 07:15:53 +00:00
|
|
|
|
use std::path::Path;
|
2024-05-23 20:01:39 +00:00
|
|
|
|
#[cfg(unix)]
|
|
|
|
|
use std::process::Command;
|
2021-01-19 07:15:53 +00:00
|
|
|
|
use tempfile::tempdir;
|
2015-11-16 05:25:01 +00:00
|
|
|
|
|
2024-05-23 20:01:39 +00:00
|
|
|
|
#[cfg(unix)]
|
|
|
|
|
struct Target {
|
|
|
|
|
child: UChild,
|
|
|
|
|
}
|
|
|
|
|
#[cfg(unix)]
|
|
|
|
|
impl Target {
|
|
|
|
|
fn new(signals: &[&str]) -> Self {
|
|
|
|
|
let mut child = new_ucmd!()
|
|
|
|
|
.args(&[
|
|
|
|
|
format!("--ignore-signal={}", signals.join(",")).as_str(),
|
|
|
|
|
"sleep",
|
|
|
|
|
"1000",
|
|
|
|
|
])
|
|
|
|
|
.run_no_wait();
|
|
|
|
|
child.delay(500);
|
|
|
|
|
Self { child }
|
|
|
|
|
}
|
|
|
|
|
fn send_signal(&mut self, signal: Signal) {
|
|
|
|
|
Command::new("kill")
|
|
|
|
|
.args(&[
|
|
|
|
|
format!("-{}", signal as i32),
|
|
|
|
|
format!("{}", self.child.id()),
|
|
|
|
|
])
|
|
|
|
|
.spawn()
|
|
|
|
|
.expect("failed to send signal");
|
|
|
|
|
self.child.delay(100);
|
|
|
|
|
}
|
|
|
|
|
fn is_alive(&mut self) -> bool {
|
|
|
|
|
self.child.is_alive()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#[cfg(unix)]
|
|
|
|
|
impl Drop for Target {
|
|
|
|
|
fn drop(&mut self) {
|
|
|
|
|
self.child.kill();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-10 16:38:14 +00:00
|
|
|
|
#[test]
|
|
|
|
|
fn test_invalid_arg() {
|
|
|
|
|
new_ucmd!().arg("--definitely-invalid").fails().code_is(125);
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-28 14:32:50 +00:00
|
|
|
|
#[test]
|
|
|
|
|
fn test_env_help() {
|
2021-04-05 20:03:43 +00:00
|
|
|
|
new_ucmd!()
|
2020-04-13 18:36:03 +00:00
|
|
|
|
.arg("--help")
|
|
|
|
|
.succeeds()
|
|
|
|
|
.no_stderr()
|
2022-09-29 15:48:27 +00:00
|
|
|
|
.stdout_contains("Options:");
|
2017-10-28 14:32:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_env_version() {
|
2021-04-05 20:03:43 +00:00
|
|
|
|
new_ucmd!()
|
2020-04-13 18:36:03 +00:00
|
|
|
|
.arg("--version")
|
|
|
|
|
.succeeds()
|
|
|
|
|
.no_stderr()
|
2021-04-05 20:03:43 +00:00
|
|
|
|
.stdout_contains(util_name!());
|
2017-10-28 14:32:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-19 08:22:13 +00:00
|
|
|
|
#[test]
|
|
|
|
|
fn test_echo() {
|
2024-03-14 18:38:28 +00:00
|
|
|
|
#[cfg(target_os = "windows")]
|
|
|
|
|
let args = ["cmd", "/d/c", "echo"];
|
|
|
|
|
#[cfg(not(target_os = "windows"))]
|
|
|
|
|
let args = ["echo"];
|
|
|
|
|
|
|
|
|
|
let result = new_ucmd!().args(&args).arg("FOO-bar").succeeds();
|
2018-11-19 08:22:13 +00:00
|
|
|
|
|
2021-04-05 20:03:43 +00:00
|
|
|
|
assert_eq!(result.stdout_str().trim(), "FOO-bar");
|
2018-11-19 08:22:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-03-14 18:38:28 +00:00
|
|
|
|
#[cfg(target_os = "windows")]
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_if_windows_batch_files_can_be_executed() {
|
|
|
|
|
let result = new_ucmd!().arg("./runBat.bat").succeeds();
|
|
|
|
|
|
|
|
|
|
assert!(result.stdout_str().contains("Hello Windows World!"));
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-30 20:19:35 +00:00
|
|
|
|
#[test]
|
|
|
|
|
fn test_debug_1() {
|
|
|
|
|
let ts = TestScenario::new(util_name!());
|
|
|
|
|
let result = ts
|
|
|
|
|
.ucmd()
|
|
|
|
|
.arg("-v")
|
|
|
|
|
.arg(&ts.bin_path)
|
|
|
|
|
.args(&["echo", "hello"])
|
|
|
|
|
.succeeds();
|
|
|
|
|
result.stderr_matches(
|
|
|
|
|
&Regex::new(concat!(
|
|
|
|
|
r"executing: [^\n]+(\/|\\)coreutils(\.exe)?\n",
|
|
|
|
|
r" arg\[0\]= '[^\n]+(\/|\\)coreutils(\.exe)?'\n",
|
|
|
|
|
r" arg\[1\]= 'echo'\n",
|
|
|
|
|
r" arg\[2\]= 'hello'"
|
|
|
|
|
))
|
|
|
|
|
.unwrap(),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_debug_2() {
|
|
|
|
|
let ts = TestScenario::new(util_name!());
|
|
|
|
|
let result = ts
|
|
|
|
|
.ucmd()
|
|
|
|
|
.arg("-vv")
|
|
|
|
|
.arg(ts.bin_path)
|
|
|
|
|
.args(&["echo", "hello2"])
|
|
|
|
|
.succeeds();
|
|
|
|
|
result.stderr_matches(
|
|
|
|
|
&Regex::new(concat!(
|
|
|
|
|
r"input args:\n",
|
|
|
|
|
r"arg\[0\]: 'env'\n",
|
|
|
|
|
r"arg\[1\]: '-vv'\n",
|
|
|
|
|
r"arg\[2\]: '[^\n]+(\/|\\)coreutils(.exe)?'\n",
|
|
|
|
|
r"arg\[3\]: 'echo'\n",
|
|
|
|
|
r"arg\[4\]: 'hello2'\n",
|
|
|
|
|
r"executing: [^\n]+(\/|\\)coreutils(.exe)?\n",
|
|
|
|
|
r" arg\[0\]= '[^\n]+(\/|\\)coreutils(.exe)?'\n",
|
|
|
|
|
r" arg\[1\]= 'echo'\n",
|
|
|
|
|
r" arg\[2\]= 'hello2'"
|
|
|
|
|
))
|
|
|
|
|
.unwrap(),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_debug1_part_of_string_arg() {
|
|
|
|
|
let ts = TestScenario::new(util_name!());
|
|
|
|
|
|
|
|
|
|
let result = ts
|
|
|
|
|
.ucmd()
|
|
|
|
|
.arg("-vS FOO=BAR")
|
|
|
|
|
.arg(ts.bin_path)
|
|
|
|
|
.args(&["echo", "hello1"])
|
|
|
|
|
.succeeds();
|
|
|
|
|
result.stderr_matches(
|
|
|
|
|
&Regex::new(concat!(
|
|
|
|
|
r"executing: [^\n]+(\/|\\)coreutils(\.exe)?\n",
|
|
|
|
|
r" arg\[0\]= '[^\n]+(\/|\\)coreutils(\.exe)?'\n",
|
|
|
|
|
r" arg\[1\]= 'echo'\n",
|
|
|
|
|
r" arg\[2\]= 'hello1'"
|
|
|
|
|
))
|
|
|
|
|
.unwrap(),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_debug2_part_of_string_arg() {
|
|
|
|
|
let ts = TestScenario::new(util_name!());
|
|
|
|
|
let result = ts
|
|
|
|
|
.ucmd()
|
|
|
|
|
.arg("-vvS FOO=BAR")
|
|
|
|
|
.arg(ts.bin_path)
|
|
|
|
|
.args(&["echo", "hello2"])
|
|
|
|
|
.succeeds();
|
|
|
|
|
result.stderr_matches(
|
|
|
|
|
&Regex::new(concat!(
|
|
|
|
|
r"input args:\n",
|
|
|
|
|
r"arg\[0\]: 'env'\n",
|
|
|
|
|
r"arg\[1\]: '-vvS FOO=BAR'\n",
|
|
|
|
|
r"arg\[2\]: '[^\n]+(\/|\\)coreutils(.exe)?'\n",
|
|
|
|
|
r"arg\[3\]: 'echo'\n",
|
|
|
|
|
r"arg\[4\]: 'hello2'\n",
|
|
|
|
|
r"executing: [^\n]+(\/|\\)coreutils(.exe)?\n",
|
|
|
|
|
r" arg\[0\]= '[^\n]+(\/|\\)coreutils(.exe)?'\n",
|
|
|
|
|
r" arg\[1\]= 'echo'\n",
|
|
|
|
|
r" arg\[2\]= 'hello2'"
|
|
|
|
|
))
|
|
|
|
|
.unwrap(),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-28 15:19:14 +00:00
|
|
|
|
#[test]
|
|
|
|
|
fn test_file_option() {
|
2021-04-17 09:22:49 +00:00
|
|
|
|
let out = new_ucmd!()
|
|
|
|
|
.arg("-f")
|
|
|
|
|
.arg("vars.conf.txt")
|
|
|
|
|
.run()
|
|
|
|
|
.stdout_move_str();
|
2020-04-13 18:36:03 +00:00
|
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
|
out.lines()
|
|
|
|
|
.filter(|&line| line == "FOO=bar" || line == "BAR=bamf this")
|
|
|
|
|
.count(),
|
|
|
|
|
2
|
|
|
|
|
);
|
2019-04-28 15:19:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_combined_file_set() {
|
|
|
|
|
let out = new_ucmd!()
|
2020-04-13 18:36:03 +00:00
|
|
|
|
.arg("-f")
|
|
|
|
|
.arg("vars.conf.txt")
|
2019-04-28 15:19:14 +00:00
|
|
|
|
.arg("FOO=bar.alt")
|
2020-04-13 18:36:03 +00:00
|
|
|
|
.run()
|
2021-04-05 20:03:43 +00:00
|
|
|
|
.stdout_move_str();
|
2019-04-28 15:19:14 +00:00
|
|
|
|
|
|
|
|
|
assert_eq!(out.lines().filter(|&line| line == "FOO=bar.alt").count(), 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_combined_file_set_unset() {
|
|
|
|
|
let out = new_ucmd!()
|
2020-04-13 18:36:03 +00:00
|
|
|
|
.arg("-u")
|
|
|
|
|
.arg("BAR")
|
|
|
|
|
.arg("-f")
|
|
|
|
|
.arg("vars.conf.txt")
|
2019-04-28 15:19:14 +00:00
|
|
|
|
.arg("FOO=bar.alt")
|
2021-04-05 20:03:43 +00:00
|
|
|
|
.succeeds()
|
|
|
|
|
.stdout_move_str();
|
2020-04-13 18:36:03 +00:00
|
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
|
out.lines()
|
|
|
|
|
.filter(|&line| line == "FOO=bar.alt" || line.starts_with("BAR="))
|
|
|
|
|
.count(),
|
|
|
|
|
1
|
|
|
|
|
);
|
2019-04-28 15:19:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-02 22:32:41 +00:00
|
|
|
|
#[test]
|
|
|
|
|
fn test_unset_invalid_variables() {
|
|
|
|
|
use uucore::display::Quotable;
|
|
|
|
|
|
|
|
|
|
// Cannot test input with \0 in it, since output will also contain \0. rlimit::prlimit fails
|
|
|
|
|
// with this error: Error { kind: InvalidInput, message: "nul byte found in provided data" }
|
2022-04-02 08:47:37 +00:00
|
|
|
|
for var in ["", "a=b"] {
|
2021-11-02 22:32:41 +00:00
|
|
|
|
new_ucmd!().arg("-u").arg(var).run().stderr_only(format!(
|
2023-01-05 20:09:15 +00:00
|
|
|
|
"env: cannot unset {}: Invalid argument\n",
|
2021-11-02 22:32:41 +00:00
|
|
|
|
var.quote()
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-16 05:25:01 +00:00
|
|
|
|
#[test]
|
|
|
|
|
fn test_single_name_value_pair() {
|
2021-04-05 20:03:43 +00:00
|
|
|
|
let out = new_ucmd!().arg("FOO=bar").run();
|
2015-11-16 05:25:01 +00:00
|
|
|
|
|
2021-04-05 20:03:43 +00:00
|
|
|
|
assert!(out.stdout_str().lines().any(|line| line == "FOO=bar"));
|
2015-11-16 05:25:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_multiple_name_value_pairs() {
|
2021-04-05 20:03:43 +00:00
|
|
|
|
let out = new_ucmd!().arg("FOO=bar").arg("ABC=xyz").run();
|
2020-04-13 18:36:03 +00:00
|
|
|
|
|
|
|
|
|
assert_eq!(
|
2021-04-17 09:22:49 +00:00
|
|
|
|
out.stdout_str()
|
|
|
|
|
.lines()
|
2020-04-13 18:36:03 +00:00
|
|
|
|
.filter(|&line| line == "FOO=bar" || line == "ABC=xyz")
|
|
|
|
|
.count(),
|
|
|
|
|
2
|
|
|
|
|
);
|
2015-11-16 05:25:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_ignore_environment() {
|
2016-08-23 11:52:43 +00:00
|
|
|
|
let scene = TestScenario::new(util_name!());
|
2015-11-16 05:25:01 +00:00
|
|
|
|
|
2022-10-13 17:59:10 +00:00
|
|
|
|
scene.ucmd().arg("-i").succeeds().no_stdout();
|
|
|
|
|
scene.ucmd().arg("-").succeeds().no_stdout();
|
2015-11-16 05:25:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-10-25 17:45:29 +00:00
|
|
|
|
#[test]
|
|
|
|
|
fn test_empty_name() {
|
|
|
|
|
new_ucmd!()
|
|
|
|
|
.arg("-i")
|
|
|
|
|
.arg("=xyz")
|
|
|
|
|
.run()
|
2023-01-05 20:09:15 +00:00
|
|
|
|
.stderr_only("env: warning: no name specified for value 'xyz'\n");
|
2021-10-25 17:45:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-11-16 05:25:01 +00:00
|
|
|
|
#[test]
|
|
|
|
|
fn test_null_delimiter() {
|
2016-08-23 11:52:43 +00:00
|
|
|
|
let out = new_ucmd!()
|
2020-04-13 18:36:03 +00:00
|
|
|
|
.arg("-i")
|
|
|
|
|
.arg("--null")
|
|
|
|
|
.arg("FOO=bar")
|
|
|
|
|
.arg("ABC=xyz")
|
2021-04-05 20:03:43 +00:00
|
|
|
|
.succeeds()
|
|
|
|
|
.stdout_move_str();
|
2020-04-13 18:36:03 +00:00
|
|
|
|
|
|
|
|
|
let mut vars: Vec<_> = out.split('\0').collect();
|
2015-12-22 11:44:05 +00:00
|
|
|
|
assert_eq!(vars.len(), 3);
|
2021-05-29 12:32:35 +00:00
|
|
|
|
vars.sort_unstable();
|
2015-12-22 11:44:05 +00:00
|
|
|
|
assert_eq!(vars[0], "");
|
|
|
|
|
assert_eq!(vars[1], "ABC=xyz");
|
|
|
|
|
assert_eq!(vars[2], "FOO=bar");
|
2015-11-16 05:25:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_unset_variable() {
|
2016-08-23 11:52:43 +00:00
|
|
|
|
let out = TestScenario::new(util_name!())
|
2023-01-25 02:40:39 +00:00
|
|
|
|
.ucmd()
|
2023-03-24 02:52:02 +00:00
|
|
|
|
.env("HOME", "FOO")
|
2020-04-13 18:36:03 +00:00
|
|
|
|
.arg("-u")
|
|
|
|
|
.arg("HOME")
|
2021-04-05 20:03:43 +00:00
|
|
|
|
.succeeds()
|
|
|
|
|
.stdout_move_str();
|
2015-11-16 05:25:01 +00:00
|
|
|
|
|
2021-05-29 12:32:35 +00:00
|
|
|
|
assert!(!out.lines().any(|line| line.starts_with("HOME=")));
|
2015-11-16 05:25:01 +00:00
|
|
|
|
}
|
2019-04-28 09:12:37 +00:00
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_fail_null_with_program() {
|
2021-04-22 20:37:44 +00:00
|
|
|
|
new_ucmd!()
|
|
|
|
|
.arg("--null")
|
|
|
|
|
.arg("cd")
|
|
|
|
|
.fails()
|
|
|
|
|
.stderr_contains("cannot specify --null (-0) with command");
|
2019-04-28 09:12:37 +00:00
|
|
|
|
}
|
2020-08-03 15:32:31 +00:00
|
|
|
|
|
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_change_directory() {
|
|
|
|
|
let scene = TestScenario::new(util_name!());
|
|
|
|
|
let temporary_directory = tempdir().unwrap();
|
2021-11-02 23:06:59 +00:00
|
|
|
|
let temporary_path = std::fs::canonicalize(temporary_directory.path()).unwrap();
|
2020-08-03 15:32:31 +00:00
|
|
|
|
assert_ne!(env::current_dir().unwrap(), temporary_path);
|
|
|
|
|
|
|
|
|
|
// command to print out current working directory
|
|
|
|
|
let pwd = "pwd";
|
|
|
|
|
|
|
|
|
|
let out = scene
|
|
|
|
|
.ucmd()
|
|
|
|
|
.arg("--chdir")
|
|
|
|
|
.arg(&temporary_path)
|
|
|
|
|
.arg(pwd)
|
2021-04-05 20:03:43 +00:00
|
|
|
|
.succeeds()
|
|
|
|
|
.stdout_move_str();
|
2022-01-30 12:55:03 +00:00
|
|
|
|
assert_eq!(out.trim(), temporary_path.as_os_str());
|
2020-08-03 15:32:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_change_directory() {
|
|
|
|
|
let scene = TestScenario::new(util_name!());
|
|
|
|
|
let temporary_directory = tempdir().unwrap();
|
2021-11-02 23:06:59 +00:00
|
|
|
|
|
2020-08-03 15:32:31 +00:00
|
|
|
|
let temporary_path = temporary_directory.path();
|
2021-11-02 23:06:59 +00:00
|
|
|
|
let temporary_path = temporary_path
|
|
|
|
|
.strip_prefix(r"\\?\")
|
|
|
|
|
.unwrap_or(temporary_path);
|
2020-08-03 15:32:31 +00:00
|
|
|
|
|
2021-11-02 23:06:59 +00:00
|
|
|
|
let env_cd = env::current_dir().unwrap();
|
|
|
|
|
let env_cd = env_cd.strip_prefix(r"\\?\").unwrap_or(&env_cd);
|
|
|
|
|
|
|
|
|
|
assert_ne!(env_cd, temporary_path);
|
|
|
|
|
|
|
|
|
|
// COMSPEC is a variable that contains the full path to cmd.exe
|
|
|
|
|
let cmd_path = env::var("COMSPEC").unwrap();
|
|
|
|
|
|
|
|
|
|
// command to print out current working directory
|
|
|
|
|
let pwd = [&*cmd_path, "/C", "cd"];
|
2020-08-03 15:32:31 +00:00
|
|
|
|
|
|
|
|
|
let out = scene
|
|
|
|
|
.ucmd()
|
|
|
|
|
.arg("--chdir")
|
2022-11-04 10:10:35 +00:00
|
|
|
|
.arg(temporary_path)
|
2021-11-02 23:06:59 +00:00
|
|
|
|
.args(&pwd)
|
2021-04-05 20:03:43 +00:00
|
|
|
|
.succeeds()
|
|
|
|
|
.stdout_move_str();
|
2022-01-30 20:25:09 +00:00
|
|
|
|
assert_eq!(out.trim(), temporary_path.as_os_str());
|
2020-08-03 15:32:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_fail_change_directory() {
|
|
|
|
|
let scene = TestScenario::new(util_name!());
|
|
|
|
|
let some_non_existing_path = "some_nonexistent_path";
|
2021-05-29 12:32:35 +00:00
|
|
|
|
assert!(!Path::new(some_non_existing_path).is_dir());
|
2020-08-03 15:32:31 +00:00
|
|
|
|
|
|
|
|
|
let out = scene
|
|
|
|
|
.ucmd()
|
|
|
|
|
.arg("--chdir")
|
|
|
|
|
.arg(some_non_existing_path)
|
|
|
|
|
.arg("pwd")
|
|
|
|
|
.fails()
|
2021-04-05 20:03:43 +00:00
|
|
|
|
.stderr_move_str();
|
2020-08-03 15:32:31 +00:00
|
|
|
|
assert!(out.contains("env: cannot change directory to "));
|
|
|
|
|
}
|
2024-03-14 18:38:28 +00:00
|
|
|
|
|
|
|
|
|
#[cfg(not(target_os = "windows"))] // windows has no executable "echo", its only supported as part of a batch-file
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_split_string_into_args_one_argument_no_quotes() {
|
|
|
|
|
let scene = TestScenario::new(util_name!());
|
|
|
|
|
|
|
|
|
|
let out = scene
|
|
|
|
|
.ucmd()
|
|
|
|
|
.arg("-S echo hello world")
|
|
|
|
|
.succeeds()
|
|
|
|
|
.stdout_move_str();
|
|
|
|
|
assert_eq!(out, "hello world\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(not(target_os = "windows"))] // windows has no executable "echo", its only supported as part of a batch-file
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_split_string_into_args_one_argument() {
|
|
|
|
|
let scene = TestScenario::new(util_name!());
|
|
|
|
|
|
|
|
|
|
let out = scene
|
|
|
|
|
.ucmd()
|
|
|
|
|
.arg("-S echo \"hello world\"")
|
|
|
|
|
.succeeds()
|
|
|
|
|
.stdout_move_str();
|
|
|
|
|
assert_eq!(out, "hello world\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(not(target_os = "windows"))] // windows has no executable "echo", its only supported as part of a batch-file
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_split_string_into_args_s_escaping_challenge() {
|
|
|
|
|
let scene = TestScenario::new(util_name!());
|
|
|
|
|
|
|
|
|
|
let out = scene
|
|
|
|
|
.ucmd()
|
|
|
|
|
.args(&[r#"-S echo "hello \"great\" world""#])
|
|
|
|
|
.succeeds()
|
|
|
|
|
.stdout_move_str();
|
|
|
|
|
assert_eq!(out, "hello \"great\" world\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_split_string_into_args_s_escaped_c_not_allowed() {
|
|
|
|
|
let scene = TestScenario::new(util_name!());
|
|
|
|
|
|
|
|
|
|
let out = scene.ucmd().args(&[r#"-S"\c""#]).fails().stderr_move_str();
|
|
|
|
|
assert_eq!(
|
|
|
|
|
out,
|
|
|
|
|
"env: '\\c' must not appear in double-quoted -S string\n"
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(not(target_os = "windows"))] // no printf available
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_split_string_into_args_s_whitespace_handling() {
|
|
|
|
|
let scene = TestScenario::new(util_name!());
|
|
|
|
|
|
|
|
|
|
let out = scene
|
|
|
|
|
.ucmd()
|
|
|
|
|
.args(&["-Sprintf x%sx\\n A \t B \x0B\x0C\r\n"])
|
|
|
|
|
.succeeds()
|
|
|
|
|
.stdout_move_str();
|
|
|
|
|
assert_eq!(out, "xAx\nxBx\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(not(target_os = "windows"))] // no printf available
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_split_string_into_args_long_option_whitespace_handling() {
|
|
|
|
|
let scene = TestScenario::new(util_name!());
|
|
|
|
|
|
|
|
|
|
let out = scene
|
|
|
|
|
.ucmd()
|
|
|
|
|
.args(&["--split-string printf x%sx\\n A \t B \x0B\x0C\r\n"])
|
|
|
|
|
.succeeds()
|
|
|
|
|
.stdout_move_str();
|
|
|
|
|
assert_eq!(out, "xAx\nxBx\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(not(target_os = "windows"))] // no printf available
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_split_string_into_args_debug_output_whitespace_handling() {
|
|
|
|
|
let scene = TestScenario::new(util_name!());
|
|
|
|
|
|
|
|
|
|
let out = scene
|
|
|
|
|
.ucmd()
|
2024-03-30 20:19:35 +00:00
|
|
|
|
.args(&["-vvS printf x%sx\\n A \t B \x0B\x0C\r\n"])
|
2024-03-14 18:38:28 +00:00
|
|
|
|
.succeeds();
|
|
|
|
|
assert_eq!(out.stdout_str(), "xAx\nxBx\n");
|
2024-03-30 20:19:35 +00:00
|
|
|
|
assert_eq!(
|
|
|
|
|
out.stderr_str(),
|
|
|
|
|
"input args:\narg[0]: 'env'\narg[1]: $\
|
|
|
|
|
'-vvS printf x%sx\\\\n A \\t B \\x0B\\x0C\\r\\n'\nexecuting: printf\
|
|
|
|
|
\n arg[0]= 'printf'\n arg[1]= $'x%sx\\n'\n arg[2]= 'A'\n arg[3]= 'B'\n"
|
|
|
|
|
);
|
2024-03-14 18:38:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// FixMe: This test fails on MACOS:
|
|
|
|
|
// thread 'test_env::test_gnu_e20' panicked at 'assertion failed: `(left == right)`
|
|
|
|
|
// left: `"A=B C=D\n__CF_USER_TEXT_ENCODING=0x1F5:0x0:0x0\n"`,
|
|
|
|
|
// right: `"A=B C=D\n"`', tests/by-util/test_env.rs:369:5
|
|
|
|
|
#[cfg(not(target_os = "macos"))]
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_gnu_e20() {
|
|
|
|
|
let scene = TestScenario::new(util_name!());
|
|
|
|
|
|
|
|
|
|
let env_bin = String::from(crate::common::util::TESTS_BINARY) + " " + util_name!();
|
|
|
|
|
|
|
|
|
|
let (input, output) = (
|
|
|
|
|
[
|
|
|
|
|
String::from("-i"),
|
|
|
|
|
String::from(r#"-SA="B\_C=D" "#) + env_bin.escape_default().to_string().as_str() + "",
|
|
|
|
|
],
|
|
|
|
|
"A=B C=D\n",
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let out = scene.ucmd().args(&input).succeeds();
|
|
|
|
|
assert_eq!(out.stdout_str(), output);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
2024-03-23 08:49:35 +00:00
|
|
|
|
#[allow(clippy::cognitive_complexity)] // Ignore clippy lint of too long function sign
|
2024-04-22 12:55:14 +00:00
|
|
|
|
fn test_env_parsing_errors() {
|
2024-03-14 18:38:28 +00:00
|
|
|
|
let ts = TestScenario::new(util_name!());
|
|
|
|
|
|
2024-04-22 12:55:14 +00:00
|
|
|
|
ts.ucmd()
|
|
|
|
|
.arg("-S\\|echo hallo") // no quotes, invalid escape sequence |
|
|
|
|
|
.fails()
|
2024-03-14 18:38:28 +00:00
|
|
|
|
.code_is(125)
|
|
|
|
|
.no_stdout()
|
|
|
|
|
.stderr_is("env: invalid sequence '\\|' in -S\n");
|
|
|
|
|
|
2024-04-22 12:55:14 +00:00
|
|
|
|
ts.ucmd()
|
|
|
|
|
.arg("-S\\a") // no quotes, invalid escape sequence a
|
|
|
|
|
.fails()
|
2024-03-14 18:38:28 +00:00
|
|
|
|
.code_is(125)
|
|
|
|
|
.no_stdout()
|
|
|
|
|
.stderr_is("env: invalid sequence '\\a' in -S\n");
|
|
|
|
|
|
2024-04-22 12:55:14 +00:00
|
|
|
|
ts.ucmd()
|
|
|
|
|
.arg("-S\"\\a\"") // double quotes, invalid escape sequence a
|
|
|
|
|
.fails()
|
2024-03-14 18:38:28 +00:00
|
|
|
|
.code_is(125)
|
|
|
|
|
.no_stdout()
|
|
|
|
|
.stderr_is("env: invalid sequence '\\a' in -S\n");
|
|
|
|
|
|
2024-04-22 12:55:14 +00:00
|
|
|
|
ts.ucmd()
|
|
|
|
|
.arg(r#"-S"\a""#) // same as before, just using r#""#
|
|
|
|
|
.fails()
|
2024-03-14 18:38:28 +00:00
|
|
|
|
.code_is(125)
|
|
|
|
|
.no_stdout()
|
|
|
|
|
.stderr_is("env: invalid sequence '\\a' in -S\n");
|
|
|
|
|
|
2024-04-22 12:55:14 +00:00
|
|
|
|
ts.ucmd()
|
|
|
|
|
.arg("-S'\\a'") // single quotes, invalid escape sequence a
|
|
|
|
|
.fails()
|
2024-03-14 18:38:28 +00:00
|
|
|
|
.code_is(125)
|
|
|
|
|
.no_stdout()
|
|
|
|
|
.stderr_is("env: invalid sequence '\\a' in -S\n");
|
|
|
|
|
|
2024-04-22 12:55:14 +00:00
|
|
|
|
ts.ucmd()
|
2024-06-30 14:27:08 +00:00
|
|
|
|
.arg(r"-S\|\&\;") // no quotes, invalid escape sequence |
|
2024-04-22 12:55:14 +00:00
|
|
|
|
.fails()
|
2024-03-14 18:38:28 +00:00
|
|
|
|
.code_is(125)
|
|
|
|
|
.no_stdout()
|
|
|
|
|
.stderr_is("env: invalid sequence '\\|' in -S\n");
|
|
|
|
|
|
2024-04-22 12:55:14 +00:00
|
|
|
|
ts.ucmd()
|
2024-06-30 14:27:08 +00:00
|
|
|
|
.arg(r"-S\<\&\;") // no quotes, invalid escape sequence <
|
2024-04-22 12:55:14 +00:00
|
|
|
|
.fails()
|
2024-03-14 18:38:28 +00:00
|
|
|
|
.code_is(125)
|
|
|
|
|
.no_stdout()
|
|
|
|
|
.stderr_is("env: invalid sequence '\\<' in -S\n");
|
|
|
|
|
|
2024-04-22 12:55:14 +00:00
|
|
|
|
ts.ucmd()
|
2024-06-30 14:27:08 +00:00
|
|
|
|
.arg(r"-S\>\&\;") // no quotes, invalid escape sequence >
|
2024-04-22 12:55:14 +00:00
|
|
|
|
.fails()
|
2024-03-14 18:38:28 +00:00
|
|
|
|
.code_is(125)
|
|
|
|
|
.no_stdout()
|
|
|
|
|
.stderr_is("env: invalid sequence '\\>' in -S\n");
|
|
|
|
|
|
2024-04-22 12:55:14 +00:00
|
|
|
|
ts.ucmd()
|
2024-06-30 14:27:08 +00:00
|
|
|
|
.arg(r"-S\`\&\;") // no quotes, invalid escape sequence `
|
2024-04-22 12:55:14 +00:00
|
|
|
|
.fails()
|
2024-03-14 18:38:28 +00:00
|
|
|
|
.code_is(125)
|
|
|
|
|
.no_stdout()
|
|
|
|
|
.stderr_is("env: invalid sequence '\\`' in -S\n");
|
|
|
|
|
|
2024-04-22 12:55:14 +00:00
|
|
|
|
ts.ucmd()
|
|
|
|
|
.arg(r#"-S"\`\&\;""#) // double quotes, invalid escape sequence `
|
|
|
|
|
.fails()
|
2024-03-14 18:38:28 +00:00
|
|
|
|
.code_is(125)
|
|
|
|
|
.no_stdout()
|
|
|
|
|
.stderr_is("env: invalid sequence '\\`' in -S\n");
|
|
|
|
|
|
2024-04-22 12:55:14 +00:00
|
|
|
|
ts.ucmd()
|
2024-06-30 14:27:08 +00:00
|
|
|
|
.arg(r"-S'\`\&\;'") // single quotes, invalid escape sequence `
|
2024-04-22 12:55:14 +00:00
|
|
|
|
.fails()
|
2024-03-14 18:38:28 +00:00
|
|
|
|
.code_is(125)
|
|
|
|
|
.no_stdout()
|
|
|
|
|
.stderr_is("env: invalid sequence '\\`' in -S\n");
|
|
|
|
|
|
2024-04-22 12:55:14 +00:00
|
|
|
|
ts.ucmd()
|
2024-06-30 14:27:08 +00:00
|
|
|
|
.arg(r"-S\`") // ` escaped without quotes
|
2024-04-22 12:55:14 +00:00
|
|
|
|
.fails()
|
2024-03-14 18:38:28 +00:00
|
|
|
|
.code_is(125)
|
|
|
|
|
.no_stdout()
|
|
|
|
|
.stderr_is("env: invalid sequence '\\`' in -S\n");
|
|
|
|
|
|
2024-04-22 12:55:14 +00:00
|
|
|
|
ts.ucmd()
|
|
|
|
|
.arg(r#"-S"\`""#) // ` escaped in double quotes
|
|
|
|
|
.fails()
|
2024-03-14 18:38:28 +00:00
|
|
|
|
.code_is(125)
|
|
|
|
|
.no_stdout()
|
|
|
|
|
.stderr_is("env: invalid sequence '\\`' in -S\n");
|
|
|
|
|
|
2024-04-22 12:55:14 +00:00
|
|
|
|
ts.ucmd()
|
2024-06-30 14:27:08 +00:00
|
|
|
|
.arg(r"-S'\`'") // ` escaped in single quotes
|
2024-04-22 12:55:14 +00:00
|
|
|
|
.fails()
|
2024-03-14 18:38:28 +00:00
|
|
|
|
.code_is(125)
|
|
|
|
|
.no_stdout()
|
|
|
|
|
.stderr_is("env: invalid sequence '\\`' in -S\n");
|
|
|
|
|
|
|
|
|
|
ts.ucmd()
|
2024-06-30 14:27:08 +00:00
|
|
|
|
.args(&[r"-S\🦉"]) // ` escaped in single quotes
|
2024-03-14 18:38:28 +00:00
|
|
|
|
.fails()
|
|
|
|
|
.code_is(125)
|
|
|
|
|
.no_stdout()
|
|
|
|
|
.stderr_is("env: invalid sequence '\\\u{FFFD}' in -S\n"); // gnu doesn't show the owl. Instead a invalid unicode ?
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
2024-04-22 12:55:14 +00:00
|
|
|
|
fn test_env_with_empty_executable_single_quotes() {
|
2024-03-14 18:38:28 +00:00
|
|
|
|
let ts = TestScenario::new(util_name!());
|
|
|
|
|
|
|
|
|
|
ts.ucmd()
|
|
|
|
|
.args(&["-S''"]) // empty single quotes, considered as program name
|
|
|
|
|
.fails()
|
|
|
|
|
.code_is(127)
|
|
|
|
|
.no_stdout()
|
|
|
|
|
.stderr_is("env: '': No such file or directory\n"); // gnu version again adds escaping here
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
2024-04-22 12:55:14 +00:00
|
|
|
|
fn test_env_with_empty_executable_double_quotes() {
|
2024-03-14 18:38:28 +00:00
|
|
|
|
let ts = TestScenario::new(util_name!());
|
|
|
|
|
|
2024-04-22 12:55:14 +00:00
|
|
|
|
ts.ucmd()
|
|
|
|
|
.args(&["-S\"\""]) // empty double quotes, considered as program name
|
|
|
|
|
.fails()
|
2024-03-14 18:38:28 +00:00
|
|
|
|
.code_is(127)
|
|
|
|
|
.no_stdout()
|
|
|
|
|
.stderr_is("env: '': No such file or directory\n");
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-30 20:19:35 +00:00
|
|
|
|
#[test]
|
|
|
|
|
#[cfg(unix)]
|
|
|
|
|
fn test_env_overwrite_arg0() {
|
|
|
|
|
let ts = TestScenario::new(util_name!());
|
|
|
|
|
|
|
|
|
|
let bin = ts.bin_path.clone();
|
|
|
|
|
|
|
|
|
|
ts.ucmd()
|
|
|
|
|
.args(&["--argv0", "echo"])
|
|
|
|
|
.arg(&bin)
|
|
|
|
|
.args(&["-n", "hello", "world!"])
|
|
|
|
|
.succeeds()
|
|
|
|
|
.stdout_is("hello world!")
|
|
|
|
|
.stderr_is("");
|
|
|
|
|
|
|
|
|
|
ts.ucmd()
|
|
|
|
|
.args(&["-a", "dirname"])
|
|
|
|
|
.arg(bin)
|
|
|
|
|
.args(&["aa/bb/cc"])
|
|
|
|
|
.succeeds()
|
|
|
|
|
.stdout_is("aa/bb\n")
|
|
|
|
|
.stderr_is("");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
#[cfg(unix)]
|
|
|
|
|
fn test_env_arg_argv0_overwrite() {
|
|
|
|
|
let ts = TestScenario::new(util_name!());
|
|
|
|
|
|
2024-04-17 14:57:38 +00:00
|
|
|
|
let bin = &ts.bin_path;
|
2024-03-30 20:19:35 +00:00
|
|
|
|
|
|
|
|
|
// overwrite --argv0 by --argv0
|
|
|
|
|
ts.ucmd()
|
|
|
|
|
.args(&["--argv0", "dirname"])
|
|
|
|
|
.args(&["--argv0", "echo"])
|
2024-04-17 14:57:38 +00:00
|
|
|
|
.arg(bin)
|
2024-03-30 20:19:35 +00:00
|
|
|
|
.args(&["aa/bb/cc"])
|
|
|
|
|
.succeeds()
|
|
|
|
|
.stdout_is("aa/bb/cc\n")
|
|
|
|
|
.stderr_is("");
|
|
|
|
|
|
|
|
|
|
// overwrite -a by -a
|
|
|
|
|
ts.ucmd()
|
|
|
|
|
.args(&["-a", "dirname"])
|
|
|
|
|
.args(&["-a", "echo"])
|
2024-04-17 14:57:38 +00:00
|
|
|
|
.arg(bin)
|
2024-03-30 20:19:35 +00:00
|
|
|
|
.args(&["aa/bb/cc"])
|
|
|
|
|
.succeeds()
|
|
|
|
|
.stdout_is("aa/bb/cc\n")
|
|
|
|
|
.stderr_is("");
|
|
|
|
|
|
|
|
|
|
// overwrite --argv0 by -a
|
|
|
|
|
ts.ucmd()
|
|
|
|
|
.args(&["--argv0", "dirname"])
|
|
|
|
|
.args(&["-a", "echo"])
|
2024-04-17 14:57:38 +00:00
|
|
|
|
.arg(bin)
|
2024-03-30 20:19:35 +00:00
|
|
|
|
.args(&["aa/bb/cc"])
|
|
|
|
|
.succeeds()
|
|
|
|
|
.stdout_is("aa/bb/cc\n")
|
|
|
|
|
.stderr_is("");
|
|
|
|
|
|
|
|
|
|
// overwrite -a by --argv0
|
|
|
|
|
ts.ucmd()
|
|
|
|
|
.args(&["-a", "dirname"])
|
|
|
|
|
.args(&["--argv0", "echo"])
|
2024-04-17 14:57:38 +00:00
|
|
|
|
.arg(bin)
|
2024-03-30 20:19:35 +00:00
|
|
|
|
.args(&["aa/bb/cc"])
|
|
|
|
|
.succeeds()
|
|
|
|
|
.stdout_is("aa/bb/cc\n")
|
|
|
|
|
.stderr_is("");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
#[cfg(unix)]
|
|
|
|
|
fn test_env_arg_argv0_overwrite_mixed_with_string_args() {
|
|
|
|
|
let ts = TestScenario::new(util_name!());
|
|
|
|
|
|
2024-04-17 14:57:38 +00:00
|
|
|
|
let bin = &ts.bin_path;
|
2024-03-30 20:19:35 +00:00
|
|
|
|
|
|
|
|
|
// string arg following normal
|
|
|
|
|
ts.ucmd()
|
|
|
|
|
.args(&["-S--argv0 dirname"])
|
|
|
|
|
.args(&["--argv0", "echo"])
|
2024-04-17 14:57:38 +00:00
|
|
|
|
.arg(bin)
|
2024-03-30 20:19:35 +00:00
|
|
|
|
.args(&["aa/bb/cc"])
|
|
|
|
|
.succeeds()
|
|
|
|
|
.stdout_is("aa/bb/cc\n")
|
|
|
|
|
.stderr_is("");
|
|
|
|
|
|
|
|
|
|
// normal following string arg
|
|
|
|
|
ts.ucmd()
|
|
|
|
|
.args(&["-a", "dirname"])
|
|
|
|
|
.args(&["-S-a echo"])
|
2024-04-17 14:57:38 +00:00
|
|
|
|
.arg(bin)
|
2024-03-30 20:19:35 +00:00
|
|
|
|
.args(&["aa/bb/cc"])
|
|
|
|
|
.succeeds()
|
|
|
|
|
.stdout_is("aa/bb/cc\n")
|
|
|
|
|
.stderr_is("");
|
|
|
|
|
|
|
|
|
|
// one large string arg
|
|
|
|
|
ts.ucmd()
|
|
|
|
|
.args(&["-S--argv0 dirname -a echo"])
|
2024-04-17 14:57:38 +00:00
|
|
|
|
.arg(bin)
|
2024-03-30 20:19:35 +00:00
|
|
|
|
.args(&["aa/bb/cc"])
|
|
|
|
|
.succeeds()
|
|
|
|
|
.stdout_is("aa/bb/cc\n")
|
|
|
|
|
.stderr_is("");
|
|
|
|
|
|
|
|
|
|
// two string args
|
|
|
|
|
ts.ucmd()
|
|
|
|
|
.args(&["-S-a dirname"])
|
|
|
|
|
.args(&["-S--argv0 echo"])
|
2024-04-17 14:57:38 +00:00
|
|
|
|
.arg(bin)
|
2024-03-30 20:19:35 +00:00
|
|
|
|
.args(&["aa/bb/cc"])
|
|
|
|
|
.succeeds()
|
|
|
|
|
.stdout_is("aa/bb/cc\n")
|
|
|
|
|
.stderr_is("");
|
|
|
|
|
|
|
|
|
|
// three args: normal, string, normal
|
|
|
|
|
ts.ucmd()
|
|
|
|
|
.args(&["-a", "sleep"])
|
|
|
|
|
.args(&["-S-a dirname"])
|
|
|
|
|
.args(&["-a", "echo"])
|
2024-04-17 14:57:38 +00:00
|
|
|
|
.arg(bin)
|
2024-03-30 20:19:35 +00:00
|
|
|
|
.args(&["aa/bb/cc"])
|
|
|
|
|
.succeeds()
|
|
|
|
|
.stdout_is("aa/bb/cc\n")
|
|
|
|
|
.stderr_is("");
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-23 20:01:39 +00:00
|
|
|
|
#[test]
|
|
|
|
|
#[cfg(unix)]
|
|
|
|
|
fn test_env_arg_ignore_signal_invalid_signals() {
|
|
|
|
|
let ts = TestScenario::new(util_name!());
|
|
|
|
|
ts.ucmd()
|
|
|
|
|
.args(&["--ignore-signal=banana"])
|
|
|
|
|
.fails()
|
|
|
|
|
.code_is(125)
|
|
|
|
|
.stderr_contains("env: 'banana': invalid signal");
|
|
|
|
|
ts.ucmd()
|
|
|
|
|
.args(&["--ignore-signal=SIGbanana"])
|
|
|
|
|
.fails()
|
|
|
|
|
.code_is(125)
|
|
|
|
|
.stderr_contains("env: 'SIGbanana': invalid signal");
|
|
|
|
|
ts.ucmd()
|
|
|
|
|
.args(&["--ignore-signal=exit"])
|
|
|
|
|
.fails()
|
|
|
|
|
.code_is(125)
|
|
|
|
|
.stderr_contains("env: 'exit': invalid signal");
|
|
|
|
|
ts.ucmd()
|
|
|
|
|
.args(&["--ignore-signal=SIGexit"])
|
|
|
|
|
.fails()
|
|
|
|
|
.code_is(125)
|
|
|
|
|
.stderr_contains("env: 'SIGexit': invalid signal");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
#[cfg(unix)]
|
|
|
|
|
fn test_env_arg_ignore_signal_special_signals() {
|
|
|
|
|
let ts = TestScenario::new(util_name!());
|
|
|
|
|
let signal_stop = nix::sys::signal::SIGSTOP;
|
|
|
|
|
let signal_kill = nix::sys::signal::SIGKILL;
|
|
|
|
|
ts.ucmd()
|
|
|
|
|
.args(&["--ignore-signal=stop", "echo", "hello"])
|
|
|
|
|
.fails()
|
|
|
|
|
.code_is(125)
|
|
|
|
|
.stderr_contains(format!(
|
|
|
|
|
"env: failed to set signal action for signal {}: Invalid argument",
|
|
|
|
|
signal_stop as i32
|
|
|
|
|
));
|
|
|
|
|
ts.ucmd()
|
|
|
|
|
.args(&["--ignore-signal=kill", "echo", "hello"])
|
|
|
|
|
.fails()
|
|
|
|
|
.code_is(125)
|
|
|
|
|
.stderr_contains(format!(
|
|
|
|
|
"env: failed to set signal action for signal {}: Invalid argument",
|
|
|
|
|
signal_kill as i32
|
|
|
|
|
));
|
|
|
|
|
ts.ucmd()
|
|
|
|
|
.args(&["--ignore-signal=SToP", "echo", "hello"])
|
|
|
|
|
.fails()
|
|
|
|
|
.code_is(125)
|
|
|
|
|
.stderr_contains(format!(
|
|
|
|
|
"env: failed to set signal action for signal {}: Invalid argument",
|
|
|
|
|
signal_stop as i32
|
|
|
|
|
));
|
|
|
|
|
ts.ucmd()
|
|
|
|
|
.args(&["--ignore-signal=SIGKILL", "echo", "hello"])
|
|
|
|
|
.fails()
|
|
|
|
|
.code_is(125)
|
|
|
|
|
.stderr_contains(format!(
|
|
|
|
|
"env: failed to set signal action for signal {}: Invalid argument",
|
|
|
|
|
signal_kill as i32
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
#[cfg(unix)]
|
|
|
|
|
fn test_env_arg_ignore_signal_valid_signals() {
|
|
|
|
|
{
|
|
|
|
|
let mut target = Target::new(&["int"]);
|
|
|
|
|
target.send_signal(Signal::SIGINT);
|
|
|
|
|
assert!(target.is_alive());
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
let mut target = Target::new(&["usr2"]);
|
|
|
|
|
target.send_signal(Signal::SIGUSR2);
|
|
|
|
|
assert!(target.is_alive());
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
let mut target = Target::new(&["int", "usr2"]);
|
|
|
|
|
target.send_signal(Signal::SIGUSR1);
|
|
|
|
|
assert!(!target.is_alive());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
#[cfg(unix)]
|
|
|
|
|
fn test_env_arg_ignore_signal_empty() {
|
|
|
|
|
let ts = TestScenario::new(util_name!());
|
|
|
|
|
ts.ucmd()
|
|
|
|
|
.args(&["--ignore-signal=", "echo", "hello"])
|
|
|
|
|
.succeeds()
|
|
|
|
|
.no_stderr()
|
|
|
|
|
.stdout_contains("hello");
|
|
|
|
|
}
|
2024-03-14 18:38:28 +00:00
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests_split_iterator {
|
|
|
|
|
|
|
|
|
|
enum EscapeStyle {
|
|
|
|
|
/// No escaping.
|
|
|
|
|
None,
|
|
|
|
|
/// Wrap in single quotes.
|
|
|
|
|
SingleQuoted,
|
|
|
|
|
/// Single quotes combined with backslash.
|
|
|
|
|
Mixed,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Determines escaping style to use.
|
|
|
|
|
fn escape_style(s: &str) -> EscapeStyle {
|
|
|
|
|
if s.is_empty() {
|
|
|
|
|
return EscapeStyle::SingleQuoted;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut special = false;
|
|
|
|
|
let mut newline = false;
|
|
|
|
|
let mut single_quote = false;
|
|
|
|
|
|
|
|
|
|
for c in s.chars() {
|
|
|
|
|
match c {
|
|
|
|
|
'\n' => {
|
|
|
|
|
newline = true;
|
|
|
|
|
special = true;
|
|
|
|
|
}
|
|
|
|
|
'\'' => {
|
|
|
|
|
single_quote = true;
|
|
|
|
|
special = true;
|
|
|
|
|
}
|
|
|
|
|
'|' | '&' | ';' | '<' | '>' | '(' | ')' | '$' | '`' | '\\' | '"' | ' ' | '\t'
|
|
|
|
|
| '*' | '?' | '[' | '#' | '˜' | '=' | '%' => {
|
|
|
|
|
special = true;
|
|
|
|
|
}
|
|
|
|
|
_ => continue,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !special {
|
|
|
|
|
EscapeStyle::None
|
|
|
|
|
} else if newline && !single_quote {
|
|
|
|
|
EscapeStyle::SingleQuoted
|
|
|
|
|
} else {
|
|
|
|
|
EscapeStyle::Mixed
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Escapes special characters in a string, so that it will retain its literal
|
|
|
|
|
/// meaning when used as a part of command in Unix shell.
|
|
|
|
|
///
|
|
|
|
|
/// It tries to avoid introducing any unnecessary quotes or escape characters,
|
|
|
|
|
/// but specifics regarding quoting style are left unspecified.
|
|
|
|
|
pub fn quote(s: &str) -> std::borrow::Cow<str> {
|
|
|
|
|
// We are going somewhat out of the way to provide
|
|
|
|
|
// minimal amount of quoting in typical cases.
|
|
|
|
|
match escape_style(s) {
|
|
|
|
|
EscapeStyle::None => s.into(),
|
2024-09-19 21:56:27 +00:00
|
|
|
|
EscapeStyle::SingleQuoted => format!("'{s}'").into(),
|
2024-03-14 18:38:28 +00:00
|
|
|
|
EscapeStyle::Mixed => {
|
|
|
|
|
let mut quoted = String::new();
|
|
|
|
|
quoted.push('\'');
|
|
|
|
|
for c in s.chars() {
|
|
|
|
|
if c == '\'' {
|
|
|
|
|
quoted.push_str("'\\''");
|
|
|
|
|
} else {
|
|
|
|
|
quoted.push(c);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
quoted.push('\'');
|
|
|
|
|
quoted.into()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Joins arguments into a single command line suitable for execution in Unix
|
|
|
|
|
/// shell.
|
|
|
|
|
///
|
|
|
|
|
/// Each argument is quoted using [`quote`] to preserve its literal meaning when
|
|
|
|
|
/// parsed by Unix shell.
|
|
|
|
|
///
|
|
|
|
|
/// Note: This function is essentially an inverse of [`split`].
|
|
|
|
|
///
|
|
|
|
|
/// # Examples
|
|
|
|
|
///
|
|
|
|
|
/// Logging executed commands in format that can be easily copied and pasted
|
|
|
|
|
/// into an actual shell:
|
|
|
|
|
///
|
|
|
|
|
/// ```rust,no_run
|
|
|
|
|
/// fn execute(args: &[&str]) {
|
|
|
|
|
/// use std::process::Command;
|
|
|
|
|
/// println!("Executing: {}", shell_words::join(args));
|
|
|
|
|
/// Command::new(&args[0])
|
|
|
|
|
/// .args(&args[1..])
|
|
|
|
|
/// .spawn()
|
|
|
|
|
/// .expect("failed to start subprocess")
|
|
|
|
|
/// .wait()
|
|
|
|
|
/// .expect("failed to wait for subprocess");
|
|
|
|
|
/// }
|
|
|
|
|
///
|
|
|
|
|
/// execute(&["python", "-c", "print('Hello world!')"]);
|
|
|
|
|
/// ```
|
|
|
|
|
///
|
|
|
|
|
/// [`quote`]: fn.quote.html
|
|
|
|
|
/// [`split`]: fn.split.html
|
|
|
|
|
pub fn join<I, S>(words: I) -> String
|
|
|
|
|
where
|
|
|
|
|
I: IntoIterator<Item = S>,
|
|
|
|
|
S: AsRef<str>,
|
|
|
|
|
{
|
|
|
|
|
let mut line = words.into_iter().fold(String::new(), |mut line, word| {
|
|
|
|
|
let quoted = quote(word.as_ref());
|
|
|
|
|
line.push_str(quoted.as_ref());
|
|
|
|
|
line.push(' ');
|
|
|
|
|
line
|
|
|
|
|
});
|
|
|
|
|
line.pop();
|
|
|
|
|
line
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
use std::ffi::OsString;
|
|
|
|
|
|
|
|
|
|
use env::native_int_str::{from_native_int_representation_owned, Convert, NCvt};
|
2024-04-17 14:57:38 +00:00
|
|
|
|
use env::parse_error::ParseError;
|
2024-03-14 18:38:28 +00:00
|
|
|
|
|
|
|
|
|
fn split(input: &str) -> Result<Vec<OsString>, ParseError> {
|
|
|
|
|
::env::split_iterator::split(&NCvt::convert(input)).map(|vec| {
|
|
|
|
|
vec.into_iter()
|
|
|
|
|
.map(from_native_int_representation_owned)
|
|
|
|
|
.collect()
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn split_ok(cases: &[(&str, &[&str])]) {
|
|
|
|
|
for (i, &(input, expected)) in cases.iter().enumerate() {
|
|
|
|
|
match split(input) {
|
|
|
|
|
Err(actual) => {
|
|
|
|
|
panic!(
|
2024-09-19 21:56:27 +00:00
|
|
|
|
"[{i}] calling split({input:?}):\nexpected: Ok({expected:?})\n actual: Err({actual:?})\n"
|
2024-03-14 18:38:28 +00:00
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
Ok(actual) => {
|
|
|
|
|
assert!(
|
|
|
|
|
expected == actual.as_slice(),
|
2024-09-19 21:56:27 +00:00
|
|
|
|
"[{i}] After split({input:?}).unwrap()\nexpected: {expected:?}\n actual: {actual:?}\n"
|
2024-03-14 18:38:28 +00:00
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn split_empty() {
|
|
|
|
|
split_ok(&[("", &[])]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn split_initial_whitespace_is_removed() {
|
|
|
|
|
split_ok(&[
|
|
|
|
|
(" a", &["a"]),
|
|
|
|
|
("\t\t\t\tbar", &["bar"]),
|
|
|
|
|
("\t \nc", &["c"]),
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn split_trailing_whitespace_is_removed() {
|
|
|
|
|
split_ok(&[
|
|
|
|
|
("a ", &["a"]),
|
|
|
|
|
("b\t", &["b"]),
|
|
|
|
|
("c\t \n \n \n", &["c"]),
|
|
|
|
|
("d\n\n", &["d"]),
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn split_carriage_return() {
|
|
|
|
|
split_ok(&[("c\ra\r'\r'\r", &["c", "a", "\r"])]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn split_() {
|
|
|
|
|
split_ok(&[("\\'\\'", &["''"])]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn split_single_quotes() {
|
|
|
|
|
split_ok(&[
|
2024-06-30 14:27:08 +00:00
|
|
|
|
(r"''", &[r""]),
|
|
|
|
|
(r"'a'", &[r"a"]),
|
|
|
|
|
(r"'\\'", &[r"\"]),
|
|
|
|
|
(r"' \\ '", &[r" \ "]),
|
|
|
|
|
(r"'#'", &[r"#"]),
|
2024-03-14 18:38:28 +00:00
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn split_double_quotes() {
|
|
|
|
|
split_ok(&[
|
|
|
|
|
(r#""""#, &[""]),
|
|
|
|
|
(r#""""""#, &[""]),
|
|
|
|
|
(r#""a b c' d""#, &["a b c' d"]),
|
|
|
|
|
(r#""\$""#, &["$"]),
|
|
|
|
|
(r#""`""#, &["`"]),
|
|
|
|
|
(r#""\"""#, &["\""]),
|
|
|
|
|
(r#""\\""#, &["\\"]),
|
|
|
|
|
("\"\n\"", &["\n"]),
|
|
|
|
|
("\"\\\n\"", &[""]),
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn split_unquoted() {
|
|
|
|
|
split_ok(&[
|
2024-06-30 14:27:08 +00:00
|
|
|
|
(r"\\|\\&\\;", &[r"\|\&\;"]),
|
|
|
|
|
(r"\\<\\>", &[r"\<\>"]),
|
|
|
|
|
(r"\\(\\)", &[r"\(\)"]),
|
|
|
|
|
(r"\$", &[r"$"]),
|
2024-03-14 18:38:28 +00:00
|
|
|
|
(r#"\""#, &[r#"""#]),
|
2024-06-30 14:27:08 +00:00
|
|
|
|
(r"\'", &[r"'"]),
|
2024-03-14 18:38:28 +00:00
|
|
|
|
("\\\n", &[]),
|
|
|
|
|
(" \\\n \n", &[]),
|
|
|
|
|
("a\nb\nc", &["a", "b", "c"]),
|
|
|
|
|
("a\\\nb\\\nc", &["abc"]),
|
|
|
|
|
("foo bar baz", &["foo", "bar", "baz"]),
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn split_trailing_backslash() {
|
|
|
|
|
assert_eq!(
|
|
|
|
|
split("\\"),
|
|
|
|
|
Err(ParseError::InvalidBackslashAtEndOfStringInMinusS {
|
|
|
|
|
pos: 1,
|
|
|
|
|
quoting: "Delimiter".into()
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
split(" \\"),
|
|
|
|
|
Err(ParseError::InvalidBackslashAtEndOfStringInMinusS {
|
|
|
|
|
pos: 2,
|
|
|
|
|
quoting: "Delimiter".into()
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
split("a\\"),
|
|
|
|
|
Err(ParseError::InvalidBackslashAtEndOfStringInMinusS {
|
|
|
|
|
pos: 2,
|
|
|
|
|
quoting: "Unquoted".into()
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn split_errors() {
|
|
|
|
|
assert_eq!(
|
|
|
|
|
split("'abc"),
|
|
|
|
|
Err(ParseError::MissingClosingQuote { pos: 4, c: '\'' })
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
split("\""),
|
|
|
|
|
Err(ParseError::MissingClosingQuote { pos: 1, c: '"' })
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
split("'\\"),
|
|
|
|
|
Err(ParseError::MissingClosingQuote { pos: 2, c: '\'' })
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
split("'\\"),
|
|
|
|
|
Err(ParseError::MissingClosingQuote { pos: 2, c: '\'' })
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
split(r#""$""#),
|
|
|
|
|
Err(ParseError::ParsingOfVariableNameFailed {
|
|
|
|
|
pos: 2,
|
|
|
|
|
msg: "Missing variable name".into()
|
|
|
|
|
}),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn split_error_fail_with_unknown_escape_sequences() {
|
|
|
|
|
assert_eq!(
|
|
|
|
|
split("\\a"),
|
|
|
|
|
Err(ParseError::InvalidSequenceBackslashXInMinusS { pos: 1, c: 'a' })
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
split("\"\\a\""),
|
|
|
|
|
Err(ParseError::InvalidSequenceBackslashXInMinusS { pos: 2, c: 'a' })
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
split("'\\a'"),
|
|
|
|
|
Err(ParseError::InvalidSequenceBackslashXInMinusS { pos: 2, c: 'a' })
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
split(r#""\a""#),
|
|
|
|
|
Err(ParseError::InvalidSequenceBackslashXInMinusS { pos: 2, c: 'a' })
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
2024-06-30 14:27:08 +00:00
|
|
|
|
split(r"\🦉"),
|
2024-03-14 18:38:28 +00:00
|
|
|
|
Err(ParseError::InvalidSequenceBackslashXInMinusS {
|
|
|
|
|
pos: 1,
|
|
|
|
|
c: '\u{FFFD}'
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn split_comments() {
|
|
|
|
|
split_ok(&[
|
2024-06-30 14:27:08 +00:00
|
|
|
|
(r" x # comment ", &["x"]),
|
|
|
|
|
(r" w1#w2 ", &["w1#w2"]),
|
|
|
|
|
(r"'not really a # comment'", &["not really a # comment"]),
|
2024-03-14 18:38:28 +00:00
|
|
|
|
(" a # very long comment \n b # another comment", &["a", "b"]),
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_quote() {
|
|
|
|
|
assert_eq!(quote(""), "''");
|
|
|
|
|
assert_eq!(quote("'"), "''\\'''");
|
|
|
|
|
assert_eq!(quote("abc"), "abc");
|
|
|
|
|
assert_eq!(quote("a \n b"), "'a \n b'");
|
|
|
|
|
assert_eq!(quote("X'\nY"), "'X'\\''\nY'");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_join() {
|
|
|
|
|
assert_eq!(join(["a", "b", "c"]), "a b c");
|
|
|
|
|
assert_eq!(join([" ", "$", "\n"]), "' ' '$' '\n'");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn join_followed_by_split_is_identity() {
|
|
|
|
|
let cases: Vec<&[&str]> = vec![
|
|
|
|
|
&["a"],
|
|
|
|
|
&["python", "-c", "print('Hello world!')"],
|
|
|
|
|
&["echo", " arg with spaces ", "arg \' with \" quotes"],
|
|
|
|
|
&["even newlines are quoted correctly\n", "\n", "\n\n\t "],
|
|
|
|
|
&["$", "`test`"],
|
|
|
|
|
&["cat", "~user/log*"],
|
|
|
|
|
&["test", "'a \"b", "\"X'"],
|
|
|
|
|
&["empty", "", "", ""],
|
|
|
|
|
];
|
|
|
|
|
for argv in cases {
|
|
|
|
|
let args = join(argv);
|
|
|
|
|
assert_eq!(split(&args).unwrap(), argv);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mod test_raw_string_parser {
|
|
|
|
|
use std::{
|
|
|
|
|
borrow::Cow,
|
|
|
|
|
ffi::{OsStr, OsString},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
use env::{
|
|
|
|
|
native_int_str::{
|
|
|
|
|
from_native_int_representation, from_native_int_representation_owned,
|
|
|
|
|
to_native_int_representation, NativeStr,
|
|
|
|
|
},
|
|
|
|
|
string_expander::StringExpander,
|
|
|
|
|
string_parser,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const LEN_OWL: usize = if cfg!(target_os = "windows") { 2 } else { 4 };
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_ascii_only_take_one_look_at_correct_data_and_end_behavior() {
|
|
|
|
|
let input = "hello";
|
|
|
|
|
let cow = to_native_int_representation(OsStr::new(input));
|
|
|
|
|
let mut uut = StringExpander::new(&cow);
|
|
|
|
|
for c in input.chars() {
|
|
|
|
|
assert_eq!(c, uut.get_parser().peek().unwrap());
|
|
|
|
|
uut.take_one().unwrap();
|
|
|
|
|
}
|
|
|
|
|
assert_eq!(
|
|
|
|
|
uut.get_parser().peek(),
|
|
|
|
|
Err(string_parser::Error {
|
|
|
|
|
peek_position: 5,
|
|
|
|
|
err_type: string_parser::ErrorType::EndOfInput
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
uut.take_one().unwrap_err();
|
|
|
|
|
uut.take_one().unwrap_err();
|
|
|
|
|
assert_eq!(
|
|
|
|
|
from_native_int_representation_owned(uut.take_collected_output()),
|
|
|
|
|
input
|
|
|
|
|
);
|
|
|
|
|
uut.take_one().unwrap_err();
|
|
|
|
|
assert_eq!(
|
|
|
|
|
from_native_int_representation_owned(uut.take_collected_output()),
|
|
|
|
|
""
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_multi_byte_codes_take_one_look_at_correct_data_and_end_behavior() {
|
|
|
|
|
let input = OsString::from("🦉🦉🦉x🦉🦉x🦉x🦉🦉🦉🦉");
|
|
|
|
|
let cow = to_native_int_representation(input.as_os_str());
|
|
|
|
|
let mut uut = StringExpander::new(&cow);
|
|
|
|
|
for _i in 0..3 {
|
|
|
|
|
assert_eq!(uut.get_parser().peek().unwrap(), '\u{FFFD}');
|
|
|
|
|
uut.take_one().unwrap();
|
|
|
|
|
assert_eq!(uut.get_parser().peek().unwrap(), 'x');
|
|
|
|
|
uut.take_one().unwrap();
|
|
|
|
|
}
|
|
|
|
|
assert_eq!(uut.get_parser().peek().unwrap(), '\u{FFFD}');
|
|
|
|
|
uut.take_one().unwrap();
|
|
|
|
|
assert_eq!(
|
|
|
|
|
uut.get_parser().peek(),
|
|
|
|
|
Err(string_parser::Error {
|
|
|
|
|
peek_position: 10 * LEN_OWL + 3,
|
|
|
|
|
err_type: string_parser::ErrorType::EndOfInput
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
uut.take_one().unwrap_err();
|
|
|
|
|
uut.take_one().unwrap_err();
|
|
|
|
|
assert_eq!(
|
|
|
|
|
from_native_int_representation_owned(uut.take_collected_output()),
|
|
|
|
|
input
|
|
|
|
|
);
|
|
|
|
|
uut.take_one().unwrap_err();
|
|
|
|
|
assert_eq!(
|
|
|
|
|
from_native_int_representation_owned(uut.take_collected_output()),
|
|
|
|
|
""
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_multi_byte_codes_put_one_ascii_start_middle_end_try_invalid_ascii() {
|
|
|
|
|
let input = OsString::from("🦉🦉🦉x🦉🦉x🦉x🦉🦉🦉🦉");
|
|
|
|
|
let cow = to_native_int_representation(input.as_os_str());
|
|
|
|
|
let owl: char = '🦉';
|
|
|
|
|
let mut uut = StringExpander::new(&cow);
|
|
|
|
|
uut.put_one_char('a');
|
|
|
|
|
for _i in 0..3 {
|
|
|
|
|
assert_eq!(uut.get_parser().peek().unwrap(), '\u{FFFD}');
|
|
|
|
|
uut.take_one().unwrap();
|
|
|
|
|
uut.put_one_char('a');
|
|
|
|
|
assert_eq!(uut.get_parser().peek().unwrap(), 'x');
|
|
|
|
|
uut.take_one().unwrap();
|
|
|
|
|
uut.put_one_char('a');
|
|
|
|
|
}
|
|
|
|
|
assert_eq!(uut.get_parser().peek().unwrap(), '\u{FFFD}');
|
|
|
|
|
uut.take_one().unwrap();
|
|
|
|
|
uut.put_one_char(owl);
|
|
|
|
|
uut.put_one_char('a');
|
|
|
|
|
assert_eq!(
|
|
|
|
|
uut.get_parser().peek(),
|
|
|
|
|
Err(string_parser::Error {
|
|
|
|
|
peek_position: LEN_OWL * 10 + 3,
|
|
|
|
|
err_type: string_parser::ErrorType::EndOfInput
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
uut.take_one().unwrap_err();
|
|
|
|
|
uut.put_one_char('a');
|
|
|
|
|
uut.take_one().unwrap_err();
|
|
|
|
|
assert_eq!(
|
|
|
|
|
from_native_int_representation_owned(uut.take_collected_output()),
|
|
|
|
|
"a🦉🦉🦉axa🦉🦉axa🦉axa🦉🦉🦉🦉🦉aa"
|
|
|
|
|
);
|
|
|
|
|
uut.take_one().unwrap_err();
|
|
|
|
|
assert_eq!(
|
|
|
|
|
from_native_int_representation_owned(uut.take_collected_output()),
|
|
|
|
|
""
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_multi_byte_codes_skip_one_take_one_skip_until_ascii_char_or_end() {
|
|
|
|
|
let input = OsString::from("🦉🦉🦉x🦉🦉x🦉x🦉🦉🦉🦉");
|
|
|
|
|
let cow = to_native_int_representation(input.as_os_str());
|
|
|
|
|
let mut uut = StringExpander::new(&cow);
|
|
|
|
|
|
|
|
|
|
uut.skip_one().unwrap(); // skip 🦉🦉🦉
|
|
|
|
|
let p = LEN_OWL * 3;
|
|
|
|
|
assert_eq!(uut.get_peek_position(), p);
|
|
|
|
|
|
|
|
|
|
uut.skip_one().unwrap(); // skip x
|
|
|
|
|
assert_eq!(uut.get_peek_position(), p + 1);
|
|
|
|
|
uut.take_one().unwrap(); // take 🦉🦉
|
|
|
|
|
let p = p + 1 + LEN_OWL * 2;
|
|
|
|
|
assert_eq!(uut.get_peek_position(), p);
|
|
|
|
|
|
|
|
|
|
uut.skip_one().unwrap(); // skip x
|
|
|
|
|
assert_eq!(uut.get_peek_position(), p + 1);
|
|
|
|
|
uut.get_parser_mut().skip_until_char_or_end('x'); // skip 🦉
|
|
|
|
|
let p = p + 1 + LEN_OWL;
|
|
|
|
|
assert_eq!(uut.get_peek_position(), p);
|
|
|
|
|
uut.take_one().unwrap(); // take x
|
|
|
|
|
uut.get_parser_mut().skip_until_char_or_end('x'); // skip 🦉🦉🦉🦉 till end
|
|
|
|
|
let p = p + 1 + LEN_OWL * 4;
|
|
|
|
|
assert_eq!(uut.get_peek_position(), p);
|
|
|
|
|
|
|
|
|
|
uut.take_one().unwrap_err();
|
|
|
|
|
assert_eq!(
|
|
|
|
|
from_native_int_representation_owned(uut.take_collected_output()),
|
|
|
|
|
"🦉🦉x"
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_multi_byte_codes_skip_multiple_ascii_bounded_good_and_bad() {
|
|
|
|
|
let input = OsString::from("🦉🦉🦉x🦉🦉x🦉x🦉🦉🦉🦉");
|
|
|
|
|
let cow = to_native_int_representation(input.as_os_str());
|
|
|
|
|
let mut uut = StringExpander::new(&cow);
|
|
|
|
|
|
|
|
|
|
uut.get_parser_mut().skip_multiple(0);
|
|
|
|
|
assert_eq!(uut.get_peek_position(), 0);
|
|
|
|
|
let p = LEN_OWL * 3;
|
|
|
|
|
uut.get_parser_mut().skip_multiple(p); // skips 🦉🦉🦉
|
|
|
|
|
assert_eq!(uut.get_peek_position(), p);
|
|
|
|
|
|
|
|
|
|
uut.take_one().unwrap(); // take x
|
|
|
|
|
assert_eq!(uut.get_peek_position(), p + 1);
|
|
|
|
|
let step = LEN_OWL * 3 + 1;
|
|
|
|
|
uut.get_parser_mut().skip_multiple(step); // skips 🦉🦉x🦉
|
|
|
|
|
let p = p + 1 + step;
|
|
|
|
|
assert_eq!(uut.get_peek_position(), p);
|
|
|
|
|
uut.take_one().unwrap(); // take x
|
|
|
|
|
|
|
|
|
|
assert_eq!(uut.get_peek_position(), p + 1);
|
|
|
|
|
let step = 4 * LEN_OWL;
|
|
|
|
|
uut.get_parser_mut().skip_multiple(step); // skips 🦉🦉🦉🦉
|
|
|
|
|
let p = p + 1 + step;
|
|
|
|
|
assert_eq!(uut.get_peek_position(), p);
|
|
|
|
|
|
|
|
|
|
uut.take_one().unwrap_err();
|
|
|
|
|
assert_eq!(
|
|
|
|
|
from_native_int_representation_owned(uut.take_collected_output()),
|
|
|
|
|
"xx"
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_multi_byte_codes_put_string_utf8_start_middle_end() {
|
|
|
|
|
let input = OsString::from("🦉🦉🦉x🦉🦉x🦉x🦉🦉🦉🦉");
|
|
|
|
|
let cow = to_native_int_representation(input.as_os_str());
|
|
|
|
|
let mut uut = StringExpander::new(&cow);
|
|
|
|
|
|
|
|
|
|
uut.put_string("🦔oo");
|
|
|
|
|
uut.take_one().unwrap(); // takes 🦉🦉🦉
|
|
|
|
|
uut.put_string("oo🦔");
|
|
|
|
|
uut.take_one().unwrap(); // take x
|
|
|
|
|
uut.get_parser_mut().skip_until_char_or_end('\n'); // skips till end
|
|
|
|
|
uut.put_string("o🦔o");
|
|
|
|
|
|
|
|
|
|
uut.take_one().unwrap_err();
|
|
|
|
|
assert_eq!(
|
|
|
|
|
from_native_int_representation_owned(uut.take_collected_output()),
|
|
|
|
|
"🦔oo🦉🦉🦉oo🦔xo🦔o"
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_multi_byte_codes_look_at_remaining_start_middle_end() {
|
|
|
|
|
let input = "🦉🦉🦉x🦉🦉x🦉x🦉🦉🦉🦉";
|
|
|
|
|
let cow = to_native_int_representation(OsStr::new(input));
|
|
|
|
|
let mut uut = StringExpander::new(&cow);
|
|
|
|
|
|
|
|
|
|
assert_eq!(uut.get_parser().peek_remaining(), OsStr::new(input));
|
|
|
|
|
uut.take_one().unwrap(); // takes 🦉🦉🦉
|
|
|
|
|
assert_eq!(uut.get_parser().peek_remaining(), OsStr::new(&input[12..]));
|
|
|
|
|
uut.get_parser_mut().skip_until_char_or_end('\n'); // skips till end
|
|
|
|
|
assert_eq!(uut.get_parser().peek_remaining(), OsStr::new(""));
|
|
|
|
|
|
|
|
|
|
uut.take_one().unwrap_err();
|
|
|
|
|
assert_eq!(
|
|
|
|
|
from_native_int_representation_owned(uut.take_collected_output()),
|
|
|
|
|
"🦉🦉🦉"
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_deal_with_invalid_encoding() {
|
|
|
|
|
let owl_invalid_part;
|
|
|
|
|
let (brace_1, brace_2);
|
|
|
|
|
#[cfg(target_os = "windows")]
|
|
|
|
|
{
|
|
|
|
|
let mut buffer = [0u16; 2];
|
|
|
|
|
let owl = '🦉'.encode_utf16(&mut buffer);
|
|
|
|
|
owl_invalid_part = owl[0];
|
|
|
|
|
brace_1 = '<'.encode_utf16(&mut buffer).to_vec();
|
|
|
|
|
brace_2 = '>'.encode_utf16(&mut buffer).to_vec();
|
|
|
|
|
}
|
|
|
|
|
#[cfg(not(target_os = "windows"))]
|
|
|
|
|
{
|
|
|
|
|
let mut buffer = [0u8; 4];
|
|
|
|
|
let owl = '🦉'.encode_utf8(&mut buffer);
|
|
|
|
|
owl_invalid_part = owl.bytes().next().unwrap();
|
|
|
|
|
brace_1 = [b'<'].to_vec();
|
|
|
|
|
brace_2 = [b'>'].to_vec();
|
|
|
|
|
}
|
|
|
|
|
let mut input_ux = brace_1;
|
|
|
|
|
input_ux.push(owl_invalid_part);
|
|
|
|
|
input_ux.extend(brace_2);
|
|
|
|
|
let input_str = from_native_int_representation(Cow::Borrowed(&input_ux));
|
|
|
|
|
let mut uut = StringExpander::new(&input_ux);
|
|
|
|
|
|
|
|
|
|
assert_eq!(uut.get_parser().peek_remaining(), input_str);
|
|
|
|
|
assert_eq!(uut.get_parser().peek().unwrap(), '<');
|
|
|
|
|
uut.take_one().unwrap(); // takes "<"
|
|
|
|
|
assert_eq!(
|
|
|
|
|
uut.get_parser().peek_remaining(),
|
|
|
|
|
NativeStr::new(&input_str).split_at(1).1
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(uut.get_parser().peek().unwrap(), '\u{FFFD}');
|
|
|
|
|
uut.take_one().unwrap(); // takes owl_b
|
|
|
|
|
assert_eq!(
|
|
|
|
|
uut.get_parser().peek_remaining(),
|
|
|
|
|
NativeStr::new(&input_str).split_at(2).1
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(uut.get_parser().peek().unwrap(), '>');
|
|
|
|
|
uut.get_parser_mut().skip_until_char_or_end('\n');
|
|
|
|
|
assert_eq!(uut.get_parser().peek_remaining(), OsStr::new(""));
|
|
|
|
|
|
|
|
|
|
uut.take_one().unwrap_err();
|
|
|
|
|
assert_eq!(
|
|
|
|
|
from_native_int_representation_owned(uut.take_collected_output()),
|
|
|
|
|
NativeStr::new(&input_str).split_at(2).0
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|