mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-09-15 19:36:16 +00:00
Merge branch 'main' into dd-seconds-precision-3
This commit is contained in:
commit
59d34ce667
47 changed files with 1068 additions and 649 deletions
|
@ -4,9 +4,8 @@ use std::fs::{metadata, set_permissions, OpenOptions, Permissions};
|
|||
use std::os::unix::fs::{OpenOptionsExt, PermissionsExt};
|
||||
use std::sync::Mutex;
|
||||
|
||||
extern crate libc;
|
||||
use uucore::mode::strip_minus_from_mode;
|
||||
extern crate chmod;
|
||||
extern crate libc;
|
||||
use self::libc::umask;
|
||||
|
||||
static TEST_FILE: &str = "file";
|
||||
|
@ -503,35 +502,6 @@ fn test_chmod_symlink_non_existing_file_recursive() {
|
|||
.no_stderr();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_chmod_strip_minus_from_mode() {
|
||||
let tests = vec![
|
||||
// ( before, after )
|
||||
("chmod -v -xw -R FILE", "chmod -v xw -R FILE"),
|
||||
("chmod g=rwx FILE -c", "chmod g=rwx FILE -c"),
|
||||
(
|
||||
"chmod -c -R -w,o+w FILE --preserve-root",
|
||||
"chmod -c -R w,o+w FILE --preserve-root",
|
||||
),
|
||||
("chmod -c -R +w FILE ", "chmod -c -R +w FILE "),
|
||||
("chmod a=r,=xX FILE", "chmod a=r,=xX FILE"),
|
||||
(
|
||||
"chmod -v --reference REF_FILE -R FILE",
|
||||
"chmod -v --reference REF_FILE -R FILE",
|
||||
),
|
||||
("chmod -Rvc -w-x FILE", "chmod -Rvc w-x FILE"),
|
||||
("chmod 755 -v FILE", "chmod 755 -v FILE"),
|
||||
("chmod -v +0004 FILE -R", "chmod -v +0004 FILE -R"),
|
||||
("chmod -v -0007 FILE -R", "chmod -v 0007 FILE -R"),
|
||||
];
|
||||
|
||||
for test in tests {
|
||||
let mut args: Vec<String> = test.0.split(' ').map(|v| v.to_string()).collect();
|
||||
let _mode_had_minus_prefix = strip_minus_from_mode(&mut args);
|
||||
assert_eq!(test.1, args.join(" "));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_chmod_keep_setgid() {
|
||||
for (from, arg, to) in [
|
||||
|
@ -671,3 +641,68 @@ fn test_quiet_n_verbose_used_multiple_times() {
|
|||
.arg("file")
|
||||
.succeeds();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gnu_invalid_mode() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
at.touch("file");
|
||||
scene.ucmd().arg("u+gr").arg("file").fails();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gnu_options() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
at.touch("file");
|
||||
scene.ucmd().arg("-w").arg("file").succeeds();
|
||||
scene.ucmd().arg("file").arg("-w").succeeds();
|
||||
scene.ucmd().arg("-w").arg("--").arg("file").succeeds();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gnu_repeating_options() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
at.touch("file");
|
||||
scene.ucmd().arg("-w").arg("-w").arg("file").succeeds();
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("-w")
|
||||
.arg("-w")
|
||||
.arg("-w")
|
||||
.arg("file")
|
||||
.succeeds();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gnu_special_filenames() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
let perms_before = Permissions::from_mode(0o100640);
|
||||
let perms_after = Permissions::from_mode(0o100440);
|
||||
|
||||
make_file(&at.plus_as_string("--"), perms_before.mode());
|
||||
scene.ucmd().arg("-w").arg("--").arg("--").succeeds();
|
||||
assert_eq!(at.metadata("--").permissions(), perms_after);
|
||||
set_permissions(at.plus("--"), perms_before.clone()).unwrap();
|
||||
scene.ucmd().arg("--").arg("-w").arg("--").succeeds();
|
||||
assert_eq!(at.metadata("--").permissions(), perms_after);
|
||||
at.remove("--");
|
||||
|
||||
make_file(&at.plus_as_string("-w"), perms_before.mode());
|
||||
scene.ucmd().arg("-w").arg("--").arg("-w").succeeds();
|
||||
assert_eq!(at.metadata("-w").permissions(), perms_after);
|
||||
set_permissions(at.plus("-w"), perms_before).unwrap();
|
||||
scene.ucmd().arg("--").arg("-w").arg("-w").succeeds();
|
||||
assert_eq!(at.metadata("-w").permissions(), perms_after);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gnu_special_options() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
at.touch("file");
|
||||
scene.ucmd().arg("--").arg("--").arg("file").succeeds();
|
||||
scene.ucmd().arg("--").arg("--").fails();
|
||||
}
|
||||
|
|
|
@ -44,16 +44,91 @@ fn test_date_rfc_3339() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_rfc_8601() {
|
||||
fn test_date_rfc_3339_invalid_arg() {
|
||||
for param in ["--iso-3339", "--rfc-3"] {
|
||||
new_ucmd!().arg(format!("{param}=foo")).fails();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_rfc_8601_default() {
|
||||
let re = Regex::new(r"^\d{4}-\d{2}-\d{2}\n$").unwrap();
|
||||
for param in ["--iso-8601", "--i"] {
|
||||
new_ucmd!().arg(format!("{param}=ns")).succeeds();
|
||||
new_ucmd!().arg(param).succeeds().stdout_matches(&re);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_rfc_8601() {
|
||||
let re = Regex::new(r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2},\d{9}[+-]\d{2}:\d{2}\n$").unwrap();
|
||||
for param in ["--iso-8601", "--i"] {
|
||||
new_ucmd!()
|
||||
.arg(format!("{param}=ns"))
|
||||
.succeeds()
|
||||
.stdout_matches(&re);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_rfc_8601_invalid_arg() {
|
||||
for param in ["--iso-8601", "--i"] {
|
||||
new_ucmd!().arg(format!("{param}=@")).fails();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_rfc_8601_second() {
|
||||
let re = Regex::new(r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+-]\d{2}:\d{2}\n$").unwrap();
|
||||
for param in ["--iso-8601", "--i"] {
|
||||
new_ucmd!().arg(format!("{param}=second")).succeeds();
|
||||
new_ucmd!()
|
||||
.arg(format!("{param}=second"))
|
||||
.succeeds()
|
||||
.stdout_matches(&re);
|
||||
new_ucmd!()
|
||||
.arg(format!("{param}=seconds"))
|
||||
.succeeds()
|
||||
.stdout_matches(&re);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_rfc_8601_minute() {
|
||||
let re = Regex::new(r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}[+-]\d{2}:\d{2}\n$").unwrap();
|
||||
for param in ["--iso-8601", "--i"] {
|
||||
new_ucmd!()
|
||||
.arg(format!("{param}=minute"))
|
||||
.succeeds()
|
||||
.stdout_matches(&re);
|
||||
new_ucmd!()
|
||||
.arg(format!("{param}=minutes"))
|
||||
.succeeds()
|
||||
.stdout_matches(&re);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_rfc_8601_hour() {
|
||||
let re = Regex::new(r"^\d{4}-\d{2}-\d{2}T\d{2}[+-]\d{2}:\d{2}\n$").unwrap();
|
||||
for param in ["--iso-8601", "--i"] {
|
||||
new_ucmd!()
|
||||
.arg(format!("{param}=hour"))
|
||||
.succeeds()
|
||||
.stdout_matches(&re);
|
||||
new_ucmd!()
|
||||
.arg(format!("{param}=hours"))
|
||||
.succeeds()
|
||||
.stdout_matches(&re);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_rfc_8601_date() {
|
||||
let re = Regex::new(r"^\d{4}-\d{2}-\d{2}\n$").unwrap();
|
||||
for param in ["--iso-8601", "--i"] {
|
||||
new_ucmd!()
|
||||
.arg(format!("{param}=date"))
|
||||
.succeeds()
|
||||
.stdout_matches(&re);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -198,6 +273,24 @@ fn test_date_set_valid_2() {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_for_invalid_file() {
|
||||
let result = new_ucmd!().arg("--file").arg("invalid_file").fails();
|
||||
result.no_stdout();
|
||||
assert_eq!(
|
||||
result.stderr_str().trim(),
|
||||
"date: invalid_file: No such file or directory",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_for_file() {
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
let file = "test_date_for_file";
|
||||
at.touch(file);
|
||||
ucmd.arg("--file").arg(file).succeeds();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(all(unix, not(target_os = "macos")))]
|
||||
/// TODO: expected to fail currently; change to succeeds() when required.
|
||||
|
@ -232,3 +325,13 @@ fn test_invalid_format_string() {
|
|||
result.no_stdout();
|
||||
assert!(result.stderr_str().starts_with("date: invalid format "));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_date_string() {
|
||||
new_ucmd!()
|
||||
.arg("-d")
|
||||
.arg("foo")
|
||||
.fails()
|
||||
.no_stdout()
|
||||
.stderr_contains("invalid date");
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ use regex::Regex;
|
|||
use std::fs::{File, OpenOptions};
|
||||
use std::io::{BufReader, Read, Write};
|
||||
use std::path::PathBuf;
|
||||
#[cfg(all(not(windows), not(target_os = "macos")))]
|
||||
#[cfg(all(unix, not(target_os = "macos"), not(target_os = "freebsd")))]
|
||||
use std::process::{Command, Stdio};
|
||||
#[cfg(not(windows))]
|
||||
use std::thread::sleep;
|
||||
|
@ -1520,3 +1520,17 @@ fn test_skip_input_fifo() {
|
|||
assert!(output.stdout.is_empty());
|
||||
assert_eq!(&output.stderr, b"1+0 records in\n1+0 records out\n");
|
||||
}
|
||||
|
||||
/// Test for reading part of stdin from each of two child processes.
|
||||
#[cfg(all(not(windows), feature = "printf"))]
|
||||
#[test]
|
||||
fn test_multiple_processes_reading_stdin() {
|
||||
// TODO Investigate if this is possible on Windows.
|
||||
let printf = format!("{TESTS_BINARY} printf 'abcdef\n'");
|
||||
let dd_skip = format!("{TESTS_BINARY} dd bs=1 skip=3 count=0");
|
||||
let dd = format!("{TESTS_BINARY} dd");
|
||||
UCommand::new()
|
||||
.arg(format!("{printf} | ( {dd_skip} && {dd} ) 2> /dev/null"))
|
||||
.succeeds()
|
||||
.stdout_only("def\n");
|
||||
}
|
||||
|
|
|
@ -82,10 +82,10 @@ fn _du_basics_subdir(s: &str) {
|
|||
))]
|
||||
fn _du_basics_subdir(s: &str) {
|
||||
// MS-WSL linux has altered expected output
|
||||
if !uucore::os::is_wsl_1() {
|
||||
assert_eq!(s, "8\tsubdir/deeper\n");
|
||||
} else {
|
||||
if uucore::os::is_wsl_1() {
|
||||
assert_eq!(s, "0\tsubdir/deeper\n");
|
||||
} else {
|
||||
assert_eq!(s, "8\tsubdir/deeper\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -164,10 +164,10 @@ fn _du_soft_link(s: &str) {
|
|||
))]
|
||||
fn _du_soft_link(s: &str) {
|
||||
// MS-WSL linux has altered expected output
|
||||
if !uucore::os::is_wsl_1() {
|
||||
assert_eq!(s, "16\tsubdir/links\n");
|
||||
} else {
|
||||
if uucore::os::is_wsl_1() {
|
||||
assert_eq!(s, "8\tsubdir/links\n");
|
||||
} else {
|
||||
assert_eq!(s, "16\tsubdir/links\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -212,10 +212,10 @@ fn _du_hard_link(s: &str) {
|
|||
))]
|
||||
fn _du_hard_link(s: &str) {
|
||||
// MS-WSL linux has altered expected output
|
||||
if !uucore::os::is_wsl_1() {
|
||||
assert_eq!(s, "16\tsubdir/links\n");
|
||||
} else {
|
||||
if uucore::os::is_wsl_1() {
|
||||
assert_eq!(s, "8\tsubdir/links\n");
|
||||
} else {
|
||||
assert_eq!(s, "16\tsubdir/links\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -255,10 +255,10 @@ fn _du_d_flag(s: &str) {
|
|||
))]
|
||||
fn _du_d_flag(s: &str) {
|
||||
// MS-WSL linux has altered expected output
|
||||
if !uucore::os::is_wsl_1() {
|
||||
assert_eq!(s, "28\t./subdir\n36\t.\n");
|
||||
} else {
|
||||
if uucore::os::is_wsl_1() {
|
||||
assert_eq!(s, "8\t./subdir\n8\t.\n");
|
||||
} else {
|
||||
assert_eq!(s, "28\t./subdir\n36\t.\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -303,10 +303,10 @@ fn _du_dereference(s: &str) {
|
|||
))]
|
||||
fn _du_dereference(s: &str) {
|
||||
// MS-WSL linux has altered expected output
|
||||
if !uucore::os::is_wsl_1() {
|
||||
assert_eq!(s, "8\tsubdir/links/deeper_dir\n24\tsubdir/links\n");
|
||||
} else {
|
||||
if uucore::os::is_wsl_1() {
|
||||
assert_eq!(s, "0\tsubdir/links/deeper_dir\n8\tsubdir/links\n");
|
||||
} else {
|
||||
assert_eq!(s, "8\tsubdir/links/deeper_dir\n24\tsubdir/links\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1280,7 +1280,7 @@ fn test_ls_long_formats() {
|
|||
// Zero or one "." for indicating a file with security context
|
||||
|
||||
// Regex for three names, so all of author, group and owner
|
||||
let re_three = Regex::new(r"[xrw-]{9}\.? \d ([-0-9_a-z]+ ){3}0").unwrap();
|
||||
let re_three = Regex::new(r"[xrw-]{9}\.? \d ([-0-9_a-z_A-Z]+ ){3}0").unwrap();
|
||||
|
||||
#[cfg(unix)]
|
||||
let re_three_num = Regex::new(r"[xrw-]{9}\.? \d (\d+ ){3}0").unwrap();
|
||||
|
@ -1289,13 +1289,13 @@ fn test_ls_long_formats() {
|
|||
// - group and owner
|
||||
// - author and owner
|
||||
// - author and group
|
||||
let re_two = Regex::new(r"[xrw-]{9}\.? \d ([-0-9_a-z]+ ){2}0").unwrap();
|
||||
let re_two = Regex::new(r"[xrw-]{9}\.? \d ([-0-9_a-z_A-Z]+ ){2}0").unwrap();
|
||||
|
||||
#[cfg(unix)]
|
||||
let re_two_num = Regex::new(r"[xrw-]{9}\.? \d (\d+ ){2}0").unwrap();
|
||||
|
||||
// Regex for one name: author, group or owner
|
||||
let re_one = Regex::new(r"[xrw-]{9}\.? \d [-0-9_a-z]+ 0").unwrap();
|
||||
let re_one = Regex::new(r"[xrw-]{9}\.? \d [-0-9_a-z_A-Z]+ 0").unwrap();
|
||||
|
||||
#[cfg(unix)]
|
||||
let re_one_num = Regex::new(r"[xrw-]{9}\.? \d \d+ 0").unwrap();
|
||||
|
|
|
@ -25,7 +25,6 @@ fn test_shred_remove() {
|
|||
assert!(at.file_exists(file_b));
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "freebsd"))]
|
||||
#[test]
|
||||
fn test_shred_force() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
|
|
|
@ -10,7 +10,7 @@ fn test_output_is_random_permutation() {
|
|||
let input_seq = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||
let input = input_seq
|
||||
.iter()
|
||||
.map(|x| x.to_string())
|
||||
.map(ToString::to_string)
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n");
|
||||
|
||||
|
@ -52,7 +52,7 @@ fn test_echo() {
|
|||
.args(
|
||||
&input_seq
|
||||
.iter()
|
||||
.map(|x| x.to_string())
|
||||
.map(ToString::to_string)
|
||||
.collect::<Vec<String>>(),
|
||||
)
|
||||
.succeeds();
|
||||
|
@ -74,7 +74,7 @@ fn test_head_count() {
|
|||
let input_seq = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||
let input = input_seq
|
||||
.iter()
|
||||
.map(|x| x.to_string())
|
||||
.map(ToString::to_string)
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n");
|
||||
|
||||
|
@ -105,7 +105,7 @@ fn test_repeat() {
|
|||
let input_seq = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||
let input = input_seq
|
||||
.iter()
|
||||
.map(|x| x.to_string())
|
||||
.map(ToString::to_string)
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n");
|
||||
|
||||
|
|
|
@ -10,11 +10,11 @@ fn test_invalid_time_interval() {
|
|||
new_ucmd!()
|
||||
.arg("xyz")
|
||||
.fails()
|
||||
.usage_error("invalid time interval 'xyz'");
|
||||
.usage_error("invalid time interval 'xyz': Invalid character: 'x' at position 1");
|
||||
new_ucmd!()
|
||||
.args(&["--", "-1"])
|
||||
.fails()
|
||||
.usage_error("invalid time interval '-1'");
|
||||
.usage_error("invalid time interval '-1': Number was negative");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -204,14 +204,16 @@ fn test_sleep_when_input_has_only_whitespace_then_error(#[case] input: &str) {
|
|||
.arg(input)
|
||||
.timeout(Duration::from_secs(10))
|
||||
.fails()
|
||||
.usage_error(format!("invalid time interval '{input}'"));
|
||||
.usage_error(format!(
|
||||
"invalid time interval '{input}': Found only whitespace in input"
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sleep_when_multiple_input_some_with_error_then_shows_all_errors() {
|
||||
let expected = "invalid time interval 'abc'\n\
|
||||
sleep: invalid time interval '1years'\n\
|
||||
sleep: invalid time interval ' '";
|
||||
let expected = "invalid time interval 'abc': Invalid character: 'a' at position 1\n\
|
||||
sleep: invalid time interval '1years': Invalid time unit: 'years' at position 2\n\
|
||||
sleep: invalid time interval ' ': Found only whitespace in input";
|
||||
|
||||
// Even if one of the arguments is valid, but the rest isn't, we should still fail and exit early.
|
||||
// So, the timeout of 10 seconds ensures we haven't executed `thread::sleep` with the only valid
|
||||
|
@ -228,5 +230,5 @@ fn test_negative_interval() {
|
|||
new_ucmd!()
|
||||
.args(&["--", "-1"])
|
||||
.fails()
|
||||
.usage_error("invalid time interval '-1'");
|
||||
.usage_error("invalid time interval '-1': Number was negative");
|
||||
}
|
||||
|
|
|
@ -4459,7 +4459,7 @@ fn test_follow_when_files_are_pointing_to_same_relative_file_and_file_stays_same
|
|||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::exponent_exceed_float_max("1.0e2048")]
|
||||
#[case::exponent_exceed_float_max("1.0e100000")]
|
||||
#[case::underscore_delimiter("1_000")]
|
||||
#[case::only_point(".")]
|
||||
#[case::space_in_primes("' '")]
|
||||
|
|
|
@ -138,3 +138,19 @@ fn test_kill_after_long() {
|
|||
.no_stdout()
|
||||
.no_stderr();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_kill_subprocess() {
|
||||
new_ucmd!()
|
||||
.args(&[
|
||||
// Make sure the CI can spawn the subprocess.
|
||||
"10",
|
||||
"sh",
|
||||
"-c",
|
||||
"sh -c \"trap 'echo xyz' TERM; sleep 30\"",
|
||||
])
|
||||
.fails()
|
||||
.code_is(124)
|
||||
.stdout_contains("xyz")
|
||||
.stderr_contains("Terminated");
|
||||
}
|
||||
|
|
|
@ -2822,7 +2822,7 @@ mod tests {
|
|||
#[should_panic]
|
||||
fn test_cmd_result_stdout_check_when_false_then_panics() {
|
||||
let result = TestScenario::new("echo").ucmd().arg("Hello world").run();
|
||||
result.stdout_check(|s| s.is_empty());
|
||||
result.stdout_check(<[u8]>::is_empty);
|
||||
}
|
||||
|
||||
#[cfg(feature = "echo")]
|
||||
|
@ -3065,9 +3065,10 @@ mod tests {
|
|||
// check `child.is_alive()` and `child.delay()` is working
|
||||
let mut trials = 10;
|
||||
while child.is_alive() {
|
||||
if trials <= 0 {
|
||||
panic!("Assertion failed: child process is still alive.")
|
||||
}
|
||||
assert!(
|
||||
trials > 0,
|
||||
"Assertion failed: child process is still alive."
|
||||
);
|
||||
|
||||
child.delay(500);
|
||||
trials -= 1;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue