1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-09-16 19:56:17 +00:00

Merge branch 'main' into mkdir-fix

This commit is contained in:
Sylvestre Ledru 2022-03-04 20:38:06 +01:00 committed by GitHub
commit 25f0423399
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
144 changed files with 3429 additions and 1875 deletions

View file

@ -1462,6 +1462,66 @@ fn test_cp_archive_on_nonexistent_file() {
"cp: cannot stat 'nonexistent_file.txt': No such file or directory (os error 2)",
);
}
#[test]
#[cfg(unix)]
fn test_cp_fifo() {
let (at, mut ucmd) = at_and_ucmd!();
at.mkfifo("fifo");
ucmd.arg("-r")
.arg("fifo")
.arg("fifo2")
.succeeds()
.no_stderr()
.no_stdout();
assert!(at.is_fifo("fifo2"));
}
#[test]
fn test_dir_recursive_copy() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.mkdir("parent1");
at.mkdir("parent2");
at.mkdir("parent1/child");
at.mkdir("parent2/child1");
at.mkdir("parent2/child1/child2");
at.mkdir("parent2/child1/child2/child3");
// case-1: copy parent1 -> parent1: should fail
scene
.ucmd()
.arg("-R")
.arg("parent1")
.arg("parent1")
.fails()
.stderr_contains("cannot copy a directory");
// case-2: copy parent1 -> parent1/child should fail
scene
.ucmd()
.arg("-R")
.arg("parent1")
.arg("parent1/child")
.fails()
.stderr_contains("cannot copy a directory");
// case-3: copy parent1/child -> parent2 should pass
scene
.ucmd()
.arg("-R")
.arg("parent1/child")
.arg("parent2")
.succeeds();
// case-4: copy parent2/child1/ -> parent2/child1/child2/child3
scene
.ucmd()
.arg("-R")
.arg("parent2/child1/")
.arg("parent2/child1/child2/child3")
.fails()
.stderr_contains("cannot copy a directory");
}
#[test]
fn test_cp_dir_vs_file() {
new_ucmd!()
@ -1471,3 +1531,28 @@ fn test_cp_dir_vs_file() {
.fails()
.stderr_only("cp: cannot overwrite non-directory with directory");
}
#[test]
fn test_cp_overriding_arguments() {
let s = TestScenario::new(util_name!());
s.fixtures.touch("file1");
for (arg1, arg2) in &[
#[cfg(not(windows))]
("--remove-destination", "--force"),
#[cfg(not(windows))]
("--force", "--remove-destination"),
("--interactive", "--no-clobber"),
("--link", "--symbolic-link"),
("--symbolic-link", "--link"),
("--dereference", "--no-dereference"),
("--no-dereference", "--dereference"),
] {
s.ucmd()
.arg(arg1)
.arg(arg2)
.arg("file1")
.arg("file2")
.succeeds();
s.fixtures.remove("file2");
}
}

View file

@ -1,4 +1,4 @@
// spell-checker:ignore fname, tname, fpath, specfile, testfile, unspec, ifile, ofile, outfile, fullblock, urand, fileio, atoe, atoibm, availible, behaviour, bmax, bremain, btotal, cflags, creat, ctable, ctty, datastructures, doesnt, etoa, fileout, fname, gnudd, iconvflags, nocache, noctty, noerror, nofollow, nolinks, nonblock, oconvflags, outfile, parseargs, rlen, rmax, rposition, rremain, rsofar, rstat, sigusr, sigval, wlen, wstat abcdefghijklm abcdefghi
// spell-checker:ignore fname, tname, fpath, specfile, testfile, unspec, ifile, ofile, outfile, fullblock, urand, fileio, atoe, atoibm, availible, behaviour, bmax, bremain, btotal, cflags, creat, ctable, ctty, datastructures, doesnt, etoa, fileout, fname, gnudd, iconvflags, nocache, noctty, noerror, nofollow, nolinks, nonblock, oconvflags, outfile, parseargs, rlen, rmax, rposition, rremain, rsofar, rstat, sigusr, sigval, wlen, wstat abcdefghijklm abcdefghi nabcde nabcdefg abcdefg
use crate::common::util::*;
@ -201,6 +201,13 @@ fn test_x_multiplier() {
#[test]
fn test_zero_multiplier_warning() {
for arg in ["count", "seek", "skip"] {
new_ucmd!()
.args(&[format!("{}=0", arg).as_str(), "status=none"])
.pipe_in("")
.succeeds()
.no_stdout()
.no_stderr();
new_ucmd!()
.args(&[format!("{}=00x1", arg).as_str(), "status=none"])
.pipe_in("")
@ -453,6 +460,20 @@ fn test_zeros_to_stdout() {
.success();
}
#[cfg(target_pointer_width = "32")]
#[test]
fn test_oversized_bs_32_bit() {
for bs_param in &["bs", "ibs", "obs", "cbs"] {
new_ucmd!()
.args(&[format!("{}=5GB", bs_param)])
.run()
.no_stdout()
.failure()
.status_code(1)
.stderr_is(format!("dd: {}=N cannot fit into memory\n", bs_param));
}
}
#[test]
fn test_to_stdout_with_ibs_obs() {
let output: Vec<_> = String::from("y\n").bytes().cycle().take(1024).collect();
@ -1063,3 +1084,58 @@ fn test_all_valid_ascii_ebcdic_ascii_roundtrip_conv_test() {
.succeeds()
.stdout_is_fixture_bytes("all-valid-ascii-chars-37eff01866ba3f538421b30b7cbefcac.test");
}
#[test]
fn test_skip_zero() {
new_ucmd!()
.args(&["skip=0", "status=noxfer"])
.succeeds()
.no_stdout()
.stderr_is("0+0 records in\n0+0 records out\n");
}
#[test]
fn test_truncated_record() {
new_ucmd!()
.args(&["cbs=1", "conv=block", "status=noxfer"])
.pipe_in("ab")
.succeeds()
.stdout_is("a")
.stderr_is("0+1 records in\n0+1 records out\n1 truncated record\n");
new_ucmd!()
.args(&["cbs=1", "conv=block", "status=noxfer"])
.pipe_in("ab\ncd\n")
.succeeds()
.stdout_is("ac")
.stderr_is("0+1 records in\n0+1 records out\n2 truncated records\n");
}
/// Test that the output file can be `/dev/null`.
#[cfg(unix)]
#[test]
fn test_outfile_dev_null() {
new_ucmd!().arg("of=/dev/null").succeeds().no_stdout();
}
#[test]
fn test_block_sync() {
new_ucmd!()
.args(&["ibs=5", "cbs=5", "conv=block,sync", "status=noxfer"])
.pipe_in("012\nabcde\n")
.succeeds()
// blocks: 1 2
.stdout_is("012 abcde")
.stderr_is("2+0 records in\n0+1 records out\n");
// It seems that a partial record in is represented as an
// all-spaces block at the end of the output. The "1 truncated
// record" line is present in the status report due to the line
// "abcdefg\n" being truncated to "abcde".
new_ucmd!()
.args(&["ibs=5", "cbs=5", "conv=block,sync", "status=noxfer"])
.pipe_in("012\nabcdefg\n")
.succeeds()
// blocks: 1 2 3
.stdout_is("012 abcde ")
.stderr_is("2+1 records in\n0+1 records out\n1 truncated record\n");
}

View file

@ -58,4 +58,23 @@ fn test_order_same() {
assert_eq!(output1, output2);
}
#[test]
fn test_output_conflict_options() {
for option in ["-i", "-T", "-P"] {
new_ucmd!().arg("--output=source").arg(option).fails();
}
}
#[test]
fn test_output_option() {
new_ucmd!().arg("--output").succeeds();
new_ucmd!().arg("--output=source,target").succeeds();
new_ucmd!().arg("--output=invalid_option").fails();
}
#[test]
fn test_type_option() {
new_ucmd!().args(&["-t", "ext4", "-t", "ext3"]).succeeds();
}
// ToDO: more tests...

View file

@ -301,15 +301,18 @@ fn test_head_invalid_num() {
#[cfg(target_pointer_width = "32")]
{
let sizes = ["1000G", "10T"];
for size in &sizes {
new_ucmd!().args(&["-c", size]).succeeds();
}
}
#[cfg(target_pointer_width = "32")]
{
let sizes = ["-1000G", "-10T"];
for size in &sizes {
new_ucmd!()
.args(&["-c", size])
.fails()
.code_is(1)
.stderr_only(format!(
"head: invalid number of bytes: '{}': Value too large for defined data type",
size
));
.stderr_is("head: out of range integral type conversion attempted: number of bytes is too large");
}
}
new_ucmd!()

View file

@ -1100,3 +1100,27 @@ fn test_install_backup_off() {
assert!(at.file_exists(file_b));
assert!(!at.file_exists(&format!("{}~", file_b)));
}
#[test]
fn test_install_missing_arguments() {
let scene = TestScenario::new(util_name!());
scene
.ucmd()
.fails()
.stderr_contains("install: missing file operand");
}
#[test]
fn test_install_missing_destination() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let file_1 = "source_file1";
at.touch(file_1);
scene.ucmd().arg(file_1).fails().stderr_contains(format!(
"install: missing destination file operand after '{}'",
file_1
));
}

View file

@ -1644,11 +1644,7 @@ fn test_ls_indicator_style() {
// Same test as above, but with the alternate flags.
let options = vec!["--classify", "--file-type", "-p"];
for opt in options {
scene
.ucmd()
.arg(opt.to_string())
.succeeds()
.stdout_contains(&"/");
scene.ucmd().arg(opt).succeeds().stdout_contains(&"/");
}
// Classify and File-Type all contain indicators for pipes and links.

View file

@ -149,8 +149,8 @@ fn test_realpath_dangling() {
let (at, mut ucmd) = at_and_ucmd!();
at.symlink_file("nonexistent-file", "link");
ucmd.arg("link")
.succeeds()
.stdout_only(at.plus_as_string("nonexistent-file\n"));
.fails()
.stderr_contains("realpath: link: No such file or directory");
}
#[test]
@ -202,3 +202,34 @@ fn test_realpath_missing() {
.succeeds()
.stdout_only(expect);
}
#[test]
fn test_realpath_when_symlink_is_absolute_and_enoent() {
let (at, mut ucmd) = at_and_ucmd!();
at.mkdir("dir2");
at.touch("dir2/bar");
at.mkdir("dir1");
at.symlink_file("dir2/bar", "dir1/foo1");
at.symlink_file("/dir2/bar", "dir1/foo2");
at.relative_symlink_file("dir2/baz", at.plus("dir1/foo3").to_str().unwrap());
#[cfg(unix)]
ucmd.arg("dir1/foo1")
.arg("dir1/foo2")
.arg("dir1/foo3")
.run()
.stdout_contains("/dir2/bar\n")
.stdout_contains("/dir2/baz\n")
.stderr_is("realpath: dir1/foo2: No such file or directory");
#[cfg(windows)]
ucmd.arg("dir1/foo1")
.arg("dir1/foo2")
.arg("dir1/foo3")
.run()
.stdout_contains("\\dir2\\bar\n")
.stdout_contains("\\dir2\\baz\n")
.stderr_is("realpath: dir1/foo2: No such file or directory");
}

View file

@ -64,7 +64,7 @@ const TESTS: [TestCase; 10] = [
#[allow(clippy::needless_lifetimes)]
fn convert_path<'a>(path: &'a str) -> Cow<'a, str> {
#[cfg(windows)]
return path.replace("/", "\\").into();
return path.replace('/', "\\").into();
#[cfg(not(windows))]
return path.into();
}

View file

@ -20,14 +20,14 @@ fn test_hex_rejects_sign_after_identifier() {
.args(&["-0x-123ABC"])
.fails()
.no_stdout()
.stderr_contains("invalid floating point argument: '-0x-123ABC'")
.stderr_contains("for more information.");
.stderr_contains("which wasn't expected, or isn't valid in this context")
.stderr_contains("For more information try --help");
new_ucmd!()
.args(&["-0x+123ABC"])
.fails()
.no_stdout()
.stderr_contains("invalid floating point argument: '-0x+123ABC'")
.stderr_contains("for more information.");
.stderr_contains("which wasn't expected, or isn't valid in this context")
.stderr_contains("For more information try --help");
}
#[test]
@ -81,6 +81,33 @@ fn test_rejects_non_floats() {
.usage_error("invalid floating point argument: 'foo'");
}
#[test]
fn test_accepts_option_argument_directly() {
new_ucmd!()
.arg("-s,")
.arg("2")
.succeeds()
.stdout_is("1,2\n");
}
#[test]
fn test_option_with_detected_negative_argument() {
new_ucmd!()
.arg("-s,")
.args(&["-1", "2"])
.succeeds()
.stdout_is("-1,0,1,2\n");
}
#[test]
fn test_negative_number_as_separator() {
new_ucmd!()
.arg("-s")
.args(&["-1", "2"])
.succeeds()
.stdout_is("1-12\n");
}
#[test]
fn test_invalid_float() {
new_ucmd!()

View file

@ -2,7 +2,7 @@
// *
// * For the full copyright and license information, please view the LICENSE
// * file that was distributed with this source code.
// spell-checker:ignore xzaaa sixhundredfiftyonebytes ninetyonebytes asciilowercase fghij klmno pqrst uvwxyz fivelines twohundredfortyonebytes
// spell-checker:ignore xzaaa sixhundredfiftyonebytes ninetyonebytes threebytes asciilowercase fghij klmno pqrst uvwxyz fivelines twohundredfortyonebytes
extern crate rand;
extern crate regex;
@ -337,18 +337,36 @@ fn test_split_invalid_bytes_size() {
{
let sizes = ["1000G", "10T"];
for size in &sizes {
new_ucmd!()
.args(&["-b", size])
.fails()
.code_is(1)
.stderr_only(format!(
"split: invalid number of bytes: '{}': Value too large for defined data type",
size
));
new_ucmd!().args(&["-b", size]).succeeds();
}
}
}
#[test]
fn test_split_chunks_num_chunks_oversized_32() {
#[cfg(target_pointer_width = "32")]
{
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch("file");
scene
.ucmd()
.args(&["--number", "5000000000", "file"])
.fails()
.code_is(1)
.stderr_only("split: Number of chunks too big");
}
}
#[test]
fn test_split_stdin_num_chunks() {
new_ucmd!()
.args(&["--number=1"])
.fails()
.code_is(1)
.stderr_only("split: -: cannot determine file size");
}
fn file_read(at: &AtPath, filename: &str) -> String {
let mut s = String::new();
at.open(filename).read_to_string(&mut s).unwrap();
@ -526,3 +544,46 @@ fn test_include_newlines() {
at.open("xac").read_to_string(&mut s).unwrap();
assert_eq!(s, "5\n");
}
#[test]
fn test_allow_empty_files() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.args(&["-n", "4", "threebytes.txt"])
.succeeds()
.no_stdout()
.no_stderr();
assert_eq!(at.read("xaa"), "a");
assert_eq!(at.read("xab"), "b");
assert_eq!(at.read("xac"), "c");
assert_eq!(at.read("xad"), "");
}
#[test]
fn test_elide_empty_files() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.args(&["-e", "-n", "4", "threebytes.txt"])
.succeeds()
.no_stdout()
.no_stderr();
assert_eq!(at.read("xaa"), "a");
assert_eq!(at.read("xab"), "b");
assert_eq!(at.read("xac"), "c");
assert!(!at.plus("xad").exists());
}
#[test]
fn test_lines() {
let (at, mut ucmd) = at_and_ucmd!();
let file_read = |f| {
let mut s = String::new();
at.open(f).read_to_string(&mut s).unwrap();
s
};
// Split into two files without splitting up lines.
ucmd.args(&["-n", "l/2", "fivelines.txt"]).succeeds();
assert_eq!(file_read("xaa"), "1\n2\n3\n");
assert_eq!(file_read("xab"), "4\n5\n");
}

View file

@ -75,5 +75,15 @@ fn test_stdbuf_invalid_mode_fails() {
.fails()
.code_is(125)
.stderr_contains("stdbuf: invalid mode '1Y': Value too large for defined data type");
#[cfg(target_pointer_width = "32")]
{
new_ucmd!()
.args(&[*option, "5GB", "head"])
.fails()
.code_is(125)
.stderr_contains(
"stdbuf: invalid mode '5GB': Value too large for defined data type",
);
}
}
}

View file

@ -485,10 +485,7 @@ fn test_tail_invalid_num() {
.args(&["-c", size])
.fails()
.code_is(1)
.stderr_only(format!(
"tail: invalid number of bytes: '{}': Value too large for defined data type",
size
));
.stderr_only("tail: Insufficient addressable memory");
}
}
new_ucmd!()

View file

@ -440,7 +440,27 @@ fn test_file_is_not_writable() {
#[test]
fn test_file_is_not_executable() {
new_ucmd!().args(&["!", "-x", "regular_file"]).succeeds();
#[cfg(unix)]
let (at, mut ucmd) = at_and_ucmd!();
#[cfg(not(unix))]
let (_, mut ucmd) = at_and_ucmd!();
// WSL creates executable files by default, so if we are on unix, make sure
// to set make it non-executable.
// Files on other targets are non-executable by default.
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let metadata = std::fs::metadata(at.plus("regular_file")).unwrap();
let mut permissions = metadata.permissions();
// The conversion is useless on some platforms and casts from u16 to
// u32 on others
#[allow(clippy::useless_conversion)]
permissions.set_mode(permissions.mode() & !u32::from(libc::S_IXUSR));
std::fs::set_permissions(at.plus("regular_file"), permissions).unwrap();
}
ucmd.args(&["!", "-x", "regular_file"]).succeeds();
}
#[test]

View file

@ -1,4 +1,4 @@
// spell-checker:ignore aabbaa aabbcc aabc abbb abcc abcdefabcdef abcdefghijk abcdefghijklmn abcdefghijklmnop ABCDEFGHIJKLMNOPQRS abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ ABCDEFZZ abcxyz ABCXYZ abcxyzabcxyz ABCXYZABCXYZ acbdef alnum amzamz AMZXAMZ bbbd cclass cefgm cntrl compl dabcdef dncase Gzabcdefg PQRST upcase wxyzz xdigit xycde xyyye xyyz xyzzzzxyzzzz ZABCDEF Zamz
// spell-checker:ignore aabbaa aabbcc aabc abbb abcc abcdefabcdef abcdefghijk abcdefghijklmn abcdefghijklmnop ABCDEFGHIJKLMNOPQRS abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ ABCDEFZZ abcxyz ABCXYZ abcxyzabcxyz ABCXYZABCXYZ acbdef alnum amzamz AMZXAMZ bbbd cclass cefgm cntrl compl dabcdef dncase Gzabcdefg PQRST upcase wxyzz xdigit xycde xyyye xyyz xyzzzzxyzzzz ZABCDEF Zamz Cdefghijkl Cdefghijklmn
use crate::common::util::*;
#[test]
@ -869,6 +869,26 @@ fn check_against_gnu_tr_tests_o_rep_2() {
.stdout_is("BCx");
}
#[test]
fn octal_repeat_count_test() {
//below will result in 8'x' and 4'y' as octal 010 = decimal 8
new_ucmd!()
.args(&["ABCdefghijkl", "[x*010]Y"])
.pipe_in("ABCdefghijklmn12")
.succeeds()
.stdout_is("xxxxxxxxYYYYmn12");
}
#[test]
fn non_octal_repeat_count_test() {
//below will result in 10'x' and 2'y' as the 10 does not have 0 prefix
new_ucmd!()
.args(&["ABCdefghijkl", "[x*10]Y"])
.pipe_in("ABCdefghijklmn12")
.succeeds()
.stdout_is("xxxxxxxxxxYYmn12");
}
#[test]
fn check_against_gnu_tr_tests_esc() {
// ['esc', qw('a\-z' A-Z), {IN=>'abc-z'}, {OUT=>'AbcBC'}],

View file

@ -319,20 +319,6 @@ fn test_truncate_bytes_size() {
.fails()
.code_is(1)
.stderr_only("truncate: Invalid number: '1Y': Value too large for defined data type");
#[cfg(target_pointer_width = "32")]
{
let sizes = ["1000G", "10T"];
for size in &sizes {
new_ucmd!()
.args(&["--size", size, "file"])
.fails()
.code_is(1)
.stderr_only(format!(
"truncate: Invalid number: '{}': Value too large for defined data type",
size
));
}
}
}
/// Test that truncating a non-existent file creates that file.

View file

@ -701,6 +701,11 @@ impl AtPath {
symlink_file(&self.plus(original), &self.plus(link)).unwrap();
}
pub fn relative_symlink_file(&self, original: &str, link: &str) {
log_info("symlink", &format!("{},{}", original, link));
symlink_file(original, link).unwrap();
}
pub fn symlink_dir(&self, original: &str, link: &str) {
log_info(
"symlink",

1
tests/fixtures/split/threebytes.txt vendored Normal file
View file

@ -0,0 +1 @@
abc