mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-09-16 19:56:17 +00:00
Merge branch 'main' into cp-lb
This commit is contained in:
commit
63cf0d20b4
130 changed files with 2194 additions and 1531 deletions
|
@ -1477,6 +1477,65 @@ fn test_cp_link_backup() {
|
|||
assert_eq!(at.read("file2"), "Hello, World!\n");
|
||||
}
|
||||
|
||||
#[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!()
|
||||
|
@ -1486,3 +1545,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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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::*;
|
||||
|
||||
|
@ -460,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();
|
||||
|
@ -1095,3 +1109,33 @@ fn test_truncated_record() {
|
|||
.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");
|
||||
}
|
||||
|
|
|
@ -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...
|
||||
|
|
|
@ -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!()
|
||||
|
|
|
@ -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
|
||||
));
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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!()
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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!()
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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'}],
|
||||
|
|
|
@ -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.
|
||||
|
|
1
tests/fixtures/split/threebytes.txt
vendored
Normal file
1
tests/fixtures/split/threebytes.txt
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
abc
|
Loading…
Add table
Add a link
Reference in a new issue