1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-29 12:07:46 +00:00

Merge branch 'master' into iss1769

This commit is contained in:
Jan Scheer 2021-03-25 23:23:08 +01:00 committed by GitHub
commit aac79d13b3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
81 changed files with 3802 additions and 1438 deletions

View file

@ -10,16 +10,16 @@ fn test_output_multi_files_print_all_chars() {
.succeeds()
.stdout_only(
" 1\tabcde$\n 2\tfghij$\n 3\tklmno$\n 4\tpqrst$\n \
5\tuvwxyz$\n 6\t^@^A^B^C^D^E^F^G^H^I$\n \
7\t^K^L^M^N^O^P^Q^R^S^T^U^V^W^X^Y^Z^[^\\^]^^^_ \
!\"#$%&\'()*+,-./0123456789:;\
<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~^?M-^@M-^AM-^\
BM-^CM-^DM-^EM-^FM-^GM-^HM-^IM-^JM-^KM-^LM-^MM-^NM-^OM-^PM-^QM-^RM-^SM-^TM-^UM-^V\
M-^WM-^XM-^YM-^ZM-^[M-^\\M-^]M-^^M-^_M- \
M-!M-\"M-#M-$M-%M-&M-\'M-(M-)M-*M-+M-,M--M-.M-/M-0M-1M-2M-3M-4M-5M-6M-7M-8M-9M-:\
M-;M-<M-=M->M-?M-@M-AM-BM-CM-DM-EM-FM-GM-HM-IM-JM-KM-LM-MM-NM-OM-PM-QM-RM-SM-TM-U\
M-VM-WM-XM-YM-ZM-[M-\\M-]M-^M-_M-`M-aM-bM-cM-dM-eM-fM-gM-hM-iM-jM-kM-lM-mM-nM-oM-\
pM-qM-rM-sM-tM-uM-vM-wM-xM-yM-zM-{M-|M-}M-~M-^?",
5\tuvwxyz$\n 6\t^@^A^B^C^D^E^F^G^H^I$\n \
7\t^K^L^M^N^O^P^Q^R^S^T^U^V^W^X^Y^Z^[^\\^]^^^_ \
!\"#$%&\'()*+,-./0123456789:;\
<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~^?M-^@M-^AM-^\
BM-^CM-^DM-^EM-^FM-^GM-^HM-^IM-^JM-^KM-^LM-^MM-^NM-^OM-^PM-^QM-^RM-^SM-^TM-^UM-^V\
M-^WM-^XM-^YM-^ZM-^[M-^\\M-^]M-^^M-^_M- \
M-!M-\"M-#M-$M-%M-&M-\'M-(M-)M-*M-+M-,M--M-.M-/M-0M-1M-2M-3M-4M-5M-6M-7M-8M-9M-:\
M-;M-<M-=M->M-?M-@M-AM-BM-CM-DM-EM-FM-GM-HM-IM-JM-KM-LM-MM-NM-OM-PM-QM-RM-SM-TM-U\
M-VM-WM-XM-YM-ZM-[M-\\M-]M-^M-_M-`M-aM-bM-cM-dM-eM-fM-gM-hM-iM-jM-kM-lM-mM-nM-oM-\
pM-qM-rM-sM-tM-uM-vM-wM-xM-yM-zM-{M-|M-}M-~M-^?",
);
}
@ -30,7 +30,7 @@ fn test_numbered_lines_no_trailing_newline() {
.succeeds()
.stdout_only(
" 1\ttext without a trailing newlineabcde\n 2\tfghij\n \
3\tklmno\n 4\tpqrst\n 5\tuvwxyz\n",
3\tklmno\n 4\tpqrst\n 5\tuvwxyz\n",
);
}

View file

@ -115,7 +115,7 @@ fn test_reference() {
}
#[test]
#[cfg(target_os = "macos")]
#[cfg(target_vendor = "apple")]
fn test_reference() {
new_ucmd!()
.arg("-v")

View file

@ -1 +1,98 @@
// ToDO: add tests
use crate::common::util::*;
#[test]
fn test_missing_operand() {
let result = new_ucmd!().run();
assert_eq!(
true,
result
.stderr
.starts_with("error: The following required arguments were not provided")
);
assert_eq!(true, result.stderr.contains("<newroot>"));
}
#[test]
fn test_enter_chroot_fails() {
let (at, mut ucmd) = at_and_ucmd!();
at.mkdir("jail");
let result = ucmd.arg("jail").run();
assert_eq!(
true,
result.stderr.starts_with(
"chroot: error: cannot chroot to jail: Operation not permitted (os error 1)"
)
)
}
#[test]
fn test_no_such_directory() {
let (at, mut ucmd) = at_and_ucmd!();
at.touch(&at.plus_as_string("a"));
ucmd.arg("a")
.fails()
.stderr_is("chroot: error: cannot change root directory to `a`: no such directory");
}
#[test]
fn test_invalid_user_spec() {
let (at, mut ucmd) = at_and_ucmd!();
at.mkdir("a");
let result = ucmd.arg("a").arg("--userspec=ARABA:").run();
assert_eq!(
true,
result.stderr.starts_with("chroot: error: invalid userspec")
);
}
#[test]
fn test_preference_of_userspec() {
let scene = TestScenario::new(util_name!());
let result = scene.cmd("whoami").run();
if is_ci() && result.stderr.contains("No such user/group") {
// In the CI, some server are failing to return whoami.
// As seems to be a configuration issue, ignoring it
return;
}
println!("result.stdout {}", result.stdout);
println!("result.stderr = {}", result.stderr);
let username = result.stdout.trim_end();
let ts = TestScenario::new("id");
let result = ts.cmd("id").arg("-g").arg("-n").run();
println!("result.stdout {}", result.stdout);
println!("result.stderr = {}", result.stderr);
if is_ci() && result.stderr.contains("cannot find name for user ID") {
// In the CI, some server are failing to return id.
// As seems to be a configuration issue, ignoring it
return;
}
let group_name = result.stdout.trim_end();
let (at, mut ucmd) = at_and_ucmd!();
at.mkdir("a");
let result = ucmd
.arg("a")
.arg("--user")
.arg("fake")
.arg("-G")
.arg("ABC,DEF")
.arg(format!("--userspec={}:{}", username, group_name))
.run();
println!("result.stdout {}", result.stdout);
println!("result.stderr = {}", result.stderr);
}

View file

@ -31,6 +31,12 @@ static TEST_COPY_FROM_FOLDER: &str = "hello_dir_with_file/";
static TEST_COPY_FROM_FOLDER_FILE: &str = "hello_dir_with_file/hello_world.txt";
static TEST_COPY_TO_FOLDER_NEW: &str = "hello_dir_new";
static TEST_COPY_TO_FOLDER_NEW_FILE: &str = "hello_dir_new/hello_world.txt";
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
static TEST_MOUNT_COPY_FROM_FOLDER: &str = "dir_with_mount";
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
static TEST_MOUNT_MOUNTPOINT: &str = "mount";
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
static TEST_MOUNT_OTHER_FILESYSTEM_FILE: &str = "mount/DO_NOT_copy_me.txt";
#[test]
fn test_cp_cp() {
@ -1001,3 +1007,70 @@ fn test_cp_target_file_dev_null() {
assert!(at.file_exists(file2));
}
#[test]
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
fn test_cp_one_file_system() {
use crate::common::util::AtPath;
use walkdir::WalkDir;
let scene = TestScenario::new(util_name!());
// Test must be run as root (or with `sudo -E`)
if scene.cmd("whoami").run().stdout != "root\n" {
return;
}
let at = scene.fixtures.clone();
let at_src = AtPath::new(&at.plus(TEST_MOUNT_COPY_FROM_FOLDER));
let at_dst = AtPath::new(&at.plus(TEST_COPY_TO_FOLDER_NEW));
// Prepare the mount
at_src.mkdir(TEST_MOUNT_MOUNTPOINT);
let mountpoint_path = &at_src.plus_as_string(TEST_MOUNT_MOUNTPOINT);
let _r = scene
.cmd("mount")
.arg("-t")
.arg("tmpfs")
.arg("-o")
.arg("size=640k") // ought to be enough
.arg("tmpfs")
.arg(mountpoint_path)
.run();
assert!(_r.code == Some(0), _r.stderr);
at_src.touch(TEST_MOUNT_OTHER_FILESYSTEM_FILE);
// Begin testing -x flag
let result = scene
.ucmd()
.arg("-rx")
.arg(TEST_MOUNT_COPY_FROM_FOLDER)
.arg(TEST_COPY_TO_FOLDER_NEW)
.run();
// Ditch the mount before the asserts
let _r = scene.cmd("umount").arg(mountpoint_path).run();
assert!(_r.code == Some(0), _r.stderr);
assert!(result.success);
assert!(!at_dst.file_exists(TEST_MOUNT_OTHER_FILESYSTEM_FILE));
// Check if the other files were copied from the source folder hirerarchy
for entry in WalkDir::new(at_src.as_string()) {
let entry = entry.unwrap();
let relative_src = entry
.path()
.strip_prefix(at_src.as_string())
.unwrap()
.to_str()
.unwrap();
let ft = entry.file_type();
match (ft.is_dir(), ft.is_file(), ft.is_symlink()) {
(true, _, _) => assert!(at_dst.dir_exists(relative_src)),
(_, true, _) => assert!(at_dst.file_exists(relative_src)),
(_, _, _) => panic!(),
}
}
}

View file

@ -2,6 +2,8 @@ extern crate regex;
use self::regex::Regex;
use crate::common::util::*;
#[cfg(all(unix, not(target_os = "macos")))]
use rust_users::*;
#[test]
fn test_date_email() {
@ -131,3 +133,92 @@ fn test_date_format_full_day() {
let re = Regex::new(r"\S+ \d{4}-\d{2}-\d{2}").unwrap();
assert!(re.is_match(&result.stdout.trim()));
}
#[test]
#[cfg(all(unix, not(target_os = "macos")))]
fn test_date_set_valid() {
if get_effective_uid() == 0 {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd
.arg("--set")
.arg("2020-03-12 13:30:00+08:00")
.succeeds();
result.no_stdout().no_stderr();
}
}
#[test]
#[cfg(any(windows, all(unix, not(target_os = "macos"))))]
fn test_date_set_invalid() {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.arg("--set").arg("123abcd").fails();
let result = result.no_stdout();
assert!(result.stderr.starts_with("date: invalid date "));
}
#[test]
#[cfg(all(unix, not(target_os = "macos")))]
fn test_date_set_permissions_error() {
if !(get_effective_uid() == 0 || is_wsl()) {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.arg("--set").arg("2020-03-11 21:45:00+08:00").fails();
let result = result.no_stdout();
assert!(result.stderr.starts_with("date: cannot set date: "));
}
}
#[test]
#[cfg(target_os = "macos")]
fn test_date_set_mac_unavailable() {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.arg("--set").arg("2020-03-11 21:45:00+08:00").fails();
let result = result.no_stdout();
assert!(result
.stderr
.starts_with("date: setting the date is not supported by macOS"));
}
#[test]
#[cfg(all(unix, not(target_os = "macos")))]
/// TODO: expected to fail currently; change to succeeds() when required.
fn test_date_set_valid_2() {
if get_effective_uid() == 0 {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd
.arg("--set")
.arg("Sat 20 Mar 2021 14:53:01 AWST")
.fails();
let result = result.no_stdout();
assert!(result.stderr.starts_with("date: invalid date "));
}
}
#[test]
#[cfg(all(unix, not(target_os = "macos")))]
/// TODO: expected to fail currently; change to succeeds() when required.
fn test_date_set_valid_3() {
if get_effective_uid() == 0 {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd
.arg("--set")
.arg("Sat 20 Mar 2021 14:53:01") // Local timezone
.fails();
let result = result.no_stdout();
assert!(result.stderr.starts_with("date: invalid date "));
}
}
#[test]
#[cfg(all(unix, not(target_os = "macos")))]
/// TODO: expected to fail currently; change to succeeds() when required.
fn test_date_set_valid_4() {
if get_effective_uid() == 0 {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd
.arg("--set")
.arg("2020-03-11 21:45:00") // Local timezone
.fails();
let result = result.no_stdout();
assert!(result.stderr.starts_with("date: invalid date "));
}
}

View file

@ -12,7 +12,7 @@ fn test_du_basics() {
assert!(result.success);
assert_eq!(result.stderr, "");
}
#[cfg(target_os = "macos")]
#[cfg(target_vendor = "apple")]
fn _du_basics(s: String) {
let answer = "32\t./subdir
8\t./subdir/deeper
@ -21,7 +21,7 @@ fn _du_basics(s: String) {
";
assert_eq!(s, answer);
}
#[cfg(not(target_os = "macos"))]
#[cfg(not(target_vendor = "apple"))]
fn _du_basics(s: String) {
let answer = "28\t./subdir
8\t./subdir/deeper
@ -41,11 +41,11 @@ fn test_du_basics_subdir() {
_du_basics_subdir(result.stdout);
}
#[cfg(target_os = "macos")]
#[cfg(target_vendor = "apple")]
fn _du_basics_subdir(s: String) {
assert_eq!(s, "4\tsubdir/deeper\n");
}
#[cfg(not(target_os = "macos"))]
#[cfg(not(target_vendor = "apple"))]
fn _du_basics_subdir(s: String) {
// MS-WSL linux has altered expected output
if !is_wsl() {
@ -80,12 +80,12 @@ fn test_du_soft_link() {
_du_soft_link(result.stdout);
}
#[cfg(target_os = "macos")]
#[cfg(target_vendor = "apple")]
fn _du_soft_link(s: String) {
// 'macos' host variants may have `du` output variation for soft links
assert!((s == "12\tsubdir/links\n") || (s == "16\tsubdir/links\n"));
}
#[cfg(not(target_os = "macos"))]
#[cfg(not(target_vendor = "apple"))]
fn _du_soft_link(s: String) {
// MS-WSL linux has altered expected output
if !is_wsl() {
@ -109,11 +109,11 @@ fn test_du_hard_link() {
_du_hard_link(result.stdout);
}
#[cfg(target_os = "macos")]
#[cfg(target_vendor = "apple")]
fn _du_hard_link(s: String) {
assert_eq!(s, "12\tsubdir/links\n")
}
#[cfg(not(target_os = "macos"))]
#[cfg(not(target_vendor = "apple"))]
fn _du_hard_link(s: String) {
// MS-WSL linux has altered expected output
if !is_wsl() {
@ -133,11 +133,11 @@ fn test_du_d_flag() {
_du_d_flag(result.stdout);
}
#[cfg(target_os = "macos")]
#[cfg(target_vendor = "apple")]
fn _du_d_flag(s: String) {
assert_eq!(s, "16\t./subdir\n20\t./\n");
}
#[cfg(not(target_os = "macos"))]
#[cfg(not(target_vendor = "apple"))]
fn _du_d_flag(s: String) {
// MS-WSL linux has altered expected output
if !is_wsl() {

View file

@ -173,3 +173,58 @@ fn test_disable_escapes() {
.succeeds()
.stdout_only(format!("{}\n", input_str));
}
#[test]
fn test_hyphen_value() {
new_ucmd!().arg("-abc").succeeds().stdout_is("-abc\n");
}
#[test]
fn test_multiple_hyphen_values() {
new_ucmd!()
.args(&["-abc", "-def", "-edf"])
.succeeds()
.stdout_is("-abc -def -edf\n");
}
#[test]
fn test_hyphen_values_inside_string() {
new_ucmd!()
.arg("'\"\n'CXXFLAGS=-g -O2'\n\"'")
.succeeds()
.stdout
.contains("CXXFLAGS");
}
#[test]
fn test_hyphen_values_at_start() {
let result = new_ucmd!()
.arg("-E")
.arg("-test")
.arg("araba")
.arg("-merci")
.run();
assert!(result.success);
assert_eq!(false, result.stdout.contains("-E"));
assert_eq!(result.stdout, "-test araba -merci\n");
}
#[test]
fn test_hyphen_values_between() {
let result = new_ucmd!().arg("test").arg("-E").arg("araba").run();
assert!(result.success);
assert_eq!(result.stdout, "test -E araba\n");
let result = new_ucmd!()
.arg("dumdum ")
.arg("dum dum dum")
.arg("-e")
.arg("dum")
.run();
assert!(result.success);
assert_eq!(result.stdout, "dumdum dum dum dum -e dum\n");
assert_eq!(true, result.stdout.contains("-e"));
}

View file

@ -86,6 +86,14 @@ fn test_verbose() {
.stdout_is_fixture("lorem_ipsum_verbose.expected");
}
#[test]
fn test_zero_terminated() {
new_ucmd!()
.args(&["-z", "zero_terminated.txt"])
.run()
.stdout_is_fixture("zero_terminated.expected");
}
#[test]
#[ignore]
fn test_spams_newline() {
@ -159,3 +167,15 @@ fn test_bug_in_negative_zero_lines() {
//GNU Head returns "a\nb\n"
.stdout_is("");
}
#[test]
fn test_no_such_file_or_directory() {
let result = new_ucmd!().arg("no_such_file.toml").run();
assert_eq!(
true,
result
.stderr
.contains("cannot open 'no_such_file.toml' for reading: No such file or directory")
)
}

View file

@ -11,7 +11,7 @@ fn test_hostname() {
}
// FixME: fails for "MacOS"
#[cfg(not(target_os = "macos"))]
#[cfg(not(target_vendor = "apple"))]
#[test]
fn test_hostname_ip() {
let result = new_ucmd!().arg("-i").run();

View file

@ -17,9 +17,9 @@ fn test_install_help() {
#[test]
fn test_install_basic() {
let (at, mut ucmd) = at_and_ucmd!();
let dir = "test_install_target_dir_dir_a";
let file1 = "test_install_target_dir_file_a1";
let file2 = "test_install_target_dir_file_a2";
let dir = "target_dir";
let file1 = "source_file1";
let file2 = "source_file2";
at.touch(file1);
at.touch(file2);
@ -34,7 +34,7 @@ fn test_install_basic() {
#[test]
fn test_install_twice_dir() {
let dir = "test_install_target_dir_dir_a";
let dir = "dir";
let scene = TestScenario::new(util_name!());
scene.ucmd().arg("-d").arg(dir).succeeds();
@ -47,9 +47,9 @@ fn test_install_twice_dir() {
#[test]
fn test_install_failing_not_dir() {
let (at, mut ucmd) = at_and_ucmd!();
let file1 = "test_install_target_dir_file_a1";
let file2 = "test_install_target_dir_file_a2";
let file3 = "test_install_target_dir_file_a3";
let file1 = "file1";
let file2 = "file2";
let file3 = "file3";
at.touch(file1);
at.touch(file2);
@ -66,8 +66,8 @@ fn test_install_failing_not_dir() {
#[test]
fn test_install_unimplemented_arg() {
let (at, mut ucmd) = at_and_ucmd!();
let dir = "test_install_target_dir_dir_b";
let file = "test_install_target_dir_file_b";
let dir = "target_dir";
let file = "source_file";
let context_arg = "--context";
at.touch(file);
@ -86,9 +86,9 @@ fn test_install_unimplemented_arg() {
#[test]
fn test_install_component_directories() {
let (at, mut ucmd) = at_and_ucmd!();
let component1 = "test_install_target_dir_component_c1";
let component2 = "test_install_target_dir_component_c2";
let component3 = "test_install_target_dir_component_c3";
let component1 = "component1";
let component2 = "component2";
let component3 = "component3";
let directories_arg = "-d";
ucmd.args(&[directories_arg, component1, component2, component3])
@ -104,10 +104,10 @@ fn test_install_component_directories() {
fn test_install_mode_numeric() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let dir = "test_install_target_dir_dir_e";
let dir2 = "test_install_target_dir_dir_e2";
let dir = "dir1";
let dir2 = "dir2";
let file = "test_install_target_dir_file_e";
let file = "file";
let mode_arg = "--mode=333";
at.touch(file);
@ -145,8 +145,8 @@ fn test_install_mode_numeric() {
#[test]
fn test_install_mode_symbolic() {
let (at, mut ucmd) = at_and_ucmd!();
let dir = "test_install_target_dir_dir_f";
let file = "test_install_target_dir_file_f";
let dir = "target_dir";
let file = "source_file";
let mode_arg = "--mode=o+wx";
at.touch(file);
@ -163,8 +163,8 @@ fn test_install_mode_symbolic() {
#[test]
fn test_install_mode_failing() {
let (at, mut ucmd) = at_and_ucmd!();
let dir = "test_install_target_dir_dir_g";
let file = "test_install_target_dir_file_g";
let dir = "target_dir";
let file = "source_file";
let mode_arg = "--mode=999";
at.touch(file);
@ -185,7 +185,7 @@ fn test_install_mode_failing() {
#[test]
fn test_install_mode_directories() {
let (at, mut ucmd) = at_and_ucmd!();
let component = "test_install_target_dir_component_h";
let component = "component";
let directories_arg = "-d";
let mode_arg = "--mode=333";
@ -203,8 +203,8 @@ fn test_install_mode_directories() {
#[test]
fn test_install_target_file() {
let (at, mut ucmd) = at_and_ucmd!();
let file1 = "test_install_target_file_file_i1";
let file2 = "test_install_target_file_file_i2";
let file1 = "source_file";
let file2 = "target_file";
at.touch(file1);
at.touch(file2);
@ -217,8 +217,8 @@ fn test_install_target_file() {
#[test]
fn test_install_target_new_file() {
let (at, mut ucmd) = at_and_ucmd!();
let file = "test_install_target_new_filer_file_j";
let dir = "test_install_target_new_file_dir_j";
let file = "file";
let dir = "target_dir";
at.touch(file);
at.mkdir(dir);
@ -234,8 +234,8 @@ fn test_install_target_new_file() {
#[test]
fn test_install_target_new_file_with_group() {
let (at, mut ucmd) = at_and_ucmd!();
let file = "test_install_target_new_filer_file_j";
let dir = "test_install_target_new_file_dir_j";
let file = "file";
let dir = "target_dir";
let gid = get_effective_gid();
at.touch(file);
@ -264,8 +264,8 @@ fn test_install_target_new_file_with_group() {
#[test]
fn test_install_target_new_file_with_owner() {
let (at, mut ucmd) = at_and_ucmd!();
let file = "test_install_target_new_filer_file_j";
let dir = "test_install_target_new_file_dir_j";
let file = "file";
let dir = "target_dir";
let uid = get_effective_uid();
at.touch(file);
@ -294,9 +294,9 @@ fn test_install_target_new_file_with_owner() {
#[test]
fn test_install_target_new_file_failing_nonexistent_parent() {
let (at, mut ucmd) = at_and_ucmd!();
let file1 = "test_install_target_new_file_failing_file_k1";
let file2 = "test_install_target_new_file_failing_file_k2";
let dir = "test_install_target_new_file_failing_dir_k";
let file1 = "source_file";
let file2 = "target_file";
let dir = "target_dir";
at.touch(file1);
@ -312,8 +312,8 @@ fn test_install_target_new_file_failing_nonexistent_parent() {
#[test]
fn test_install_preserve_timestamps() {
let (at, mut ucmd) = at_and_ucmd!();
let file1 = "test_install_target_dir_file_a1";
let file2 = "test_install_target_dir_file_a2";
let file1 = "source_file";
let file2 = "target_file";
at.touch(file1);
ucmd.arg(file1).arg(file2).arg("-p").succeeds().no_stderr();
@ -338,8 +338,8 @@ fn test_install_preserve_timestamps() {
#[test]
fn test_install_copy_file() {
let (at, mut ucmd) = at_and_ucmd!();
let file1 = "test_install_target_dir_file_a1";
let file2 = "test_install_target_dir_file_a2";
let file1 = "source_file";
let file2 = "target_file";
at.touch(file1);
ucmd.arg(file1).arg(file2).succeeds().no_stderr();
@ -353,8 +353,57 @@ fn test_install_copy_file() {
fn test_install_target_file_dev_null() {
let (at, mut ucmd) = at_and_ucmd!();
let file1 = "/dev/null";
let file2 = "test_install_target_file_file_i2";
let file2 = "target_file";
ucmd.arg(file1).arg(file2).succeeds().no_stderr();
assert!(at.file_exists(file2));
}
#[test]
fn test_install_nested_paths_copy_file() {
let (at, mut ucmd) = at_and_ucmd!();
let file1 = "source_file";
let dir1 = "source_dir";
let dir2 = "target_dir";
at.mkdir(dir1);
at.mkdir(dir2);
at.touch(&format!("{}/{}", dir1, file1));
ucmd.arg(format!("{}/{}", dir1, file1))
.arg(dir2)
.succeeds()
.no_stderr();
assert!(at.file_exists(&format!("{}/{}", dir2, file1)));
}
#[test]
fn test_install_failing_omitting_directory() {
let (at, mut ucmd) = at_and_ucmd!();
let file1 = "source_file";
let dir1 = "source_dir";
let dir2 = "target_dir";
at.mkdir(dir1);
at.mkdir(dir2);
at.touch(file1);
let r = ucmd.arg(dir1).arg(file1).arg(dir2).run();
assert!(r.code == Some(1));
assert!(r.stderr.contains("omitting directory"));
}
#[test]
fn test_install_failing_no_such_file() {
let (at, mut ucmd) = at_and_ucmd!();
let file1 = "source_file";
let file2 = "inexistent_file";
let dir1 = "target_dir";
at.mkdir(dir1);
at.touch(file1);
let r = ucmd.arg(file1).arg(file2).arg(dir1).run();
assert!(r.code == Some(1));
assert!(r.stderr.contains("No such file or directory"));
}

View file

@ -57,6 +57,200 @@ fn test_ls_a() {
assert!(!result.stdout.contains(".."));
}
#[test]
fn test_ls_width() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch(&at.plus_as_string("test-width-1"));
at.touch(&at.plus_as_string("test-width-2"));
at.touch(&at.plus_as_string("test-width-3"));
at.touch(&at.plus_as_string("test-width-4"));
for option in &["-w 100", "-w=100", "--width=100", "--width 100"] {
let result = scene
.ucmd()
.args(&option.split(" ").collect::<Vec<_>>())
.run();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
assert_eq!(
result.stdout,
"test-width-1 test-width-2 test-width-3 test-width-4\n",
)
}
for option in &["-w 50", "-w=50", "--width=50", "--width 50"] {
let result = scene
.ucmd()
.args(&option.split(" ").collect::<Vec<_>>())
.run();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
assert_eq!(
result.stdout,
"test-width-1 test-width-3\ntest-width-2 test-width-4\n",
)
}
for option in &[
"-w 25",
"-w=25",
"--width=25",
"--width 25",
"-w 0",
"-w=0",
"--width=0",
"--width 0",
] {
let result = scene
.ucmd()
.args(&option.split(" ").collect::<Vec<_>>())
.run();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
assert_eq!(
result.stdout,
"test-width-1\ntest-width-2\ntest-width-3\ntest-width-4\n",
)
}
}
#[test]
fn test_ls_columns() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch(&at.plus_as_string("test-columns-1"));
at.touch(&at.plus_as_string("test-columns-2"));
at.touch(&at.plus_as_string("test-columns-3"));
at.touch(&at.plus_as_string("test-columns-4"));
// Columns is the default
let result = scene.ucmd().run();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
assert!(result.success);
#[cfg(not(windows))]
assert_eq!(
result.stdout,
"test-columns-1\ntest-columns-2\ntest-columns-3\ntest-columns-4\n"
);
#[cfg(windows)]
assert_eq!(
result.stdout,
"test-columns-1 test-columns-2 test-columns-3 test-columns-4\n"
);
for option in &["-C", "--format=columns"] {
let result = scene.ucmd().arg(option).run();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
assert!(result.success);
#[cfg(not(windows))]
assert_eq!(
result.stdout,
"test-columns-1\ntest-columns-2\ntest-columns-3\ntest-columns-4\n"
);
#[cfg(windows)]
assert_eq!(
result.stdout,
"test-columns-1 test-columns-2 test-columns-3 test-columns-4\n"
);
}
for option in &["-C", "--format=columns"] {
let result = scene.ucmd().arg("-w=40").arg(option).run();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
assert!(result.success);
assert_eq!(
result.stdout,
"test-columns-1 test-columns-3\ntest-columns-2 test-columns-4\n"
);
}
}
#[test]
fn test_ls_across() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch(&at.plus_as_string("test-across-1"));
at.touch(&at.plus_as_string("test-across-2"));
at.touch(&at.plus_as_string("test-across-3"));
at.touch(&at.plus_as_string("test-across-4"));
for option in &["-x", "--format=across"] {
let result = scene.ucmd().arg(option).succeeds();
// Because the test terminal has width 0, this is the same output as
// the columns option.
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
if cfg!(unix) {
assert_eq!(
result.stdout,
"test-across-1\ntest-across-2\ntest-across-3\ntest-across-4\n"
);
} else {
assert_eq!(
result.stdout,
"test-across-1 test-across-2 test-across-3 test-across-4\n"
);
}
}
for option in &["-x", "--format=across"] {
let result = scene.ucmd().arg("-w=30").arg(option).run();
// Because the test terminal has width 0, this is the same output as
// the columns option.
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
assert_eq!(
result.stdout,
"test-across-1 test-across-2\ntest-across-3 test-across-4\n"
);
}
}
#[test]
fn test_ls_commas() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch(&at.plus_as_string("test-commas-1"));
at.touch(&at.plus_as_string("test-commas-2"));
at.touch(&at.plus_as_string("test-commas-3"));
at.touch(&at.plus_as_string("test-commas-4"));
for option in &["-m", "--format=commas"] {
let result = scene.ucmd().arg(option).succeeds();
if cfg!(unix) {
assert_eq!(
result.stdout,
"test-commas-1,\ntest-commas-2,\ntest-commas-3,\ntest-commas-4\n"
);
} else {
assert_eq!(
result.stdout,
"test-commas-1, test-commas-2, test-commas-3, test-commas-4\n"
);
}
}
for option in &["-m", "--format=commas"] {
let result = scene.ucmd().arg("-w=30").arg(option).succeeds();
assert_eq!(
result.stdout,
"test-commas-1, test-commas-2,\ntest-commas-3, test-commas-4\n"
);
}
for option in &["-m", "--format=commas"] {
let result = scene.ucmd().arg("-w=45").arg(option).succeeds();
assert_eq!(
result.stdout,
"test-commas-1, test-commas-2, test-commas-3,\ntest-commas-4\n"
);
}
}
#[test]
fn test_ls_long() {
#[cfg(not(windows))]
@ -71,16 +265,20 @@ fn test_ls_long() {
}
}
let (at, mut ucmd) = at_and_ucmd!();
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch(&at.plus_as_string("test-long"));
let result = ucmd.arg("-l").arg("test-long").succeeds();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
#[cfg(not(windows))]
assert!(result.stdout.contains("-rw-rw-r--"));
#[cfg(windows)]
assert!(result.stdout.contains("---------- 1 somebody somegroup"));
for arg in &["-l", "--long", "--format=long", "--format=verbose"] {
let result = scene.ucmd().arg(arg).arg("test-long").succeeds();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
#[cfg(not(windows))]
assert!(result.stdout.contains("-rw-rw-r--"));
#[cfg(windows)]
assert!(result.stdout.contains("---------- 1 somebody somegroup"));
}
#[cfg(not(windows))]
{
@ -90,6 +288,126 @@ fn test_ls_long() {
}
}
#[test]
fn test_ls_long_formats() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch(&at.plus_as_string("test-long-formats"));
// 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();
// Regex for two names, either:
// - 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();
// Regex for one name: author, group or owner
let re_one = Regex::new(r"[xrw-]{9} \d [-0-9_a-z]+ 0").unwrap();
// Regex for no names
let re_zero = Regex::new(r"[xrw-]{9} \d 0").unwrap();
let result = scene
.ucmd()
.arg("-l")
.arg("--author")
.arg("test-long-formats")
.run();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
assert!(re_three.is_match(&result.stdout));
let result = scene
.ucmd()
.arg("-l1")
.arg("--author")
.arg("test-long-formats")
.run();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
assert!(re_three.is_match(&result.stdout));
for arg in &[
"-l", // only group and owner
"-g --author", // only author and group
"-o --author", // only author and owner
"-lG --author", // only author and owner
"-l --no-group --author", // only author and owner
] {
let result = scene
.ucmd()
.args(&arg.split(" ").collect::<Vec<_>>())
.arg("test-long-formats")
.succeeds();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
assert!(re_two.is_match(&result.stdout));
}
for arg in &[
"-g", // only group
"-gl", // only group
"-o", // only owner
"-ol", // only owner
"-oG", // only owner
"-lG", // only owner
"-l --no-group", // only owner
"-gG --author", // only author
] {
let result = scene
.ucmd()
.args(&arg.split(" ").collect::<Vec<_>>())
.arg("test-long-formats")
.succeeds();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
assert!(re_one.is_match(&result.stdout));
}
for arg in &[
"-og",
"-ogl",
"-lgo",
"-gG",
"-g --no-group",
"-og --no-group",
"-og --format=long",
"-ogCl",
"-og --format=vertical -l",
"-og1",
"-og1l",
] {
let result = scene
.ucmd()
.args(&arg.split(" ").collect::<Vec<_>>())
.arg("test-long-formats")
.succeeds();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
assert!(re_zero.is_match(&result.stdout));
}
}
#[test]
fn test_ls_oneline() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch(&at.plus_as_string("test-oneline-1"));
at.touch(&at.plus_as_string("test-oneline-2"));
// Bit of a weird situation: in the tests oneline and columns have the same output,
// except on Windows.
for option in &["-1", "--format=single-column"] {
let result = scene.ucmd().arg(option).run();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
assert!(result.success);
assert_eq!(result.stdout, "test-oneline-1\ntest-oneline-2\n");
}
}
#[test]
fn test_ls_deref() {
let scene = TestScenario::new(util_name!());
@ -166,27 +484,55 @@ fn test_ls_order_size() {
}
#[test]
fn test_ls_order_creation() {
fn test_ls_long_ctime() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch("test-long-ctime-1");
let result = scene.ucmd().arg("-lc").succeeds();
// Should show the time on Unix, but question marks on windows.
#[cfg(unix)]
assert!(result.stdout.contains(":"));
#[cfg(not(unix))]
assert!(result.stdout.contains("???"));
}
#[test]
fn test_ls_order_time() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch("test-1");
at.append("test-1", "1");
sleep(Duration::from_millis(500));
sleep(Duration::from_millis(100));
at.touch("test-2");
at.append("test-2", "22");
sleep(Duration::from_millis(500));
sleep(Duration::from_millis(100));
at.touch("test-3");
at.append("test-3", "333");
sleep(Duration::from_millis(500));
sleep(Duration::from_millis(100));
at.touch("test-4");
at.append("test-4", "4444");
sleep(Duration::from_millis(100));
// Read test-3, only changing access time
at.read("test-3");
// Set permissions of test-2, only changing ctime
std::fs::set_permissions(
at.plus_as_string("test-2"),
at.metadata("test-2").permissions(),
)
.unwrap();
let result = scene.ucmd().arg("-al").run();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
assert!(result.success);
// ctime was changed at write, so the order is 4 3 2 1
let result = scene.ucmd().arg("-t").run();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
@ -196,7 +542,7 @@ fn test_ls_order_creation() {
#[cfg(windows)]
assert_eq!(result.stdout, "test-4 test-3 test-2 test-1\n");
let result = scene.ucmd().arg("-t").arg("-r").run();
let result = scene.ucmd().arg("-tr").run();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
assert!(result.success);
@ -204,6 +550,41 @@ fn test_ls_order_creation() {
assert_eq!(result.stdout, "test-1\ntest-2\ntest-3\ntest-4\n");
#[cfg(windows)]
assert_eq!(result.stdout, "test-1 test-2 test-3 test-4\n");
// 3 was accessed last in the read
// So the order should be 2 3 4 1
let result = scene.ucmd().arg("-tu").run();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
assert!(result.success);
let file3_access = at.open("test-3").metadata().unwrap().accessed().unwrap();
let file4_access = at.open("test-4").metadata().unwrap().accessed().unwrap();
if file3_access > file4_access {
if cfg!(not(windows)) {
assert_eq!(result.stdout, "test-3\ntest-4\ntest-2\ntest-1\n");
} else {
assert_eq!(result.stdout, "test-3 test-4 test-2 test-1\n");
}
} else {
// Access time does not seem to be set on Windows and some other
// systems so the order is 4 3 2 1
if cfg!(not(windows)) {
assert_eq!(result.stdout, "test-4\ntest-3\ntest-2\ntest-1\n");
} else {
assert_eq!(result.stdout, "test-4 test-3 test-2 test-1\n");
}
}
// test-2 had the last ctime change when the permissions were set
// So the order should be 2 4 3 1
#[cfg(unix)]
{
let result = scene.ucmd().arg("-tc").run();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
assert!(result.success);
assert_eq!(result.stdout, "test-2\ntest-4\ntest-3\ntest-1\n");
}
}
#[test]
@ -270,45 +651,197 @@ fn test_ls_recursive() {
assert!(result.stdout.contains("a\\b:\nb"));
}
#[cfg(unix)]
#[test]
fn test_ls_ls_color() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.mkdir("a");
at.mkdir("a/nested_dir");
at.mkdir("z");
at.touch(&at.plus_as_string("a/a"));
scene.ucmd().arg("--color").succeeds();
scene.ucmd().arg("--color=always").succeeds();
scene.ucmd().arg("--color=never").succeeds();
scene.ucmd().arg("--color").arg("a").succeeds();
scene.ucmd().arg("--color=always").arg("a/a").succeeds();
scene.ucmd().arg("--color=never").arg("z").succeeds();
at.touch(&at.plus_as_string("a/nested_file"));
at.touch("test-color");
let a_with_colors = "\x1b[01;34ma\x1b[0m";
let z_with_colors = "\x1b[01;34mz\x1b[0m";
let nested_dir_with_colors = "\x1b[01;34mnested_dir\x1b[0m";
// Color is disabled by default
let result = scene.ucmd().succeeds();
assert!(!result.stdout.contains(a_with_colors));
assert!(!result.stdout.contains(z_with_colors));
// Color should be enabled
let result = scene.ucmd().arg("--color").succeeds();
assert!(result.stdout.contains(a_with_colors));
assert!(result.stdout.contains(z_with_colors));
// Color should be enabled
let result = scene.ucmd().arg("--color=always").succeeds();
assert!(result.stdout.contains(a_with_colors));
assert!(result.stdout.contains(z_with_colors));
// Color should be disabled
let result = scene.ucmd().arg("--color=never").succeeds();
assert!(!result.stdout.contains(a_with_colors));
assert!(!result.stdout.contains(z_with_colors));
// Nested dir should be shown and colored
let result = scene.ucmd().arg("--color").arg("a").succeeds();
assert!(result.stdout.contains(nested_dir_with_colors));
// Color has no effect
let result = scene
.ucmd()
.arg("--color=always")
.arg("a/nested_file")
.succeeds();
assert!(result.stdout.contains("a/nested_file\n"));
// No output
let result = scene.ucmd().arg("--color=never").arg("z").succeeds();
assert_eq!(result.stdout, "");
}
#[cfg(not(any(target_os = "macos", target_os = "windows")))] // Truncate not available on mac or win
#[cfg(unix)]
#[test]
fn test_ls_human() {
fn test_ls_inode() {
let scene = TestScenario::new(util_name!());
let file = "test_human";
let result = scene.cmd("truncate").arg("-s").arg("+1000").arg(file).run();
let at = &scene.fixtures;
let file = "test_inode";
at.touch(file);
let re_short = Regex::new(r" *(\d+) test_inode").unwrap();
let re_long = Regex::new(r" *(\d+) [xrw-]{10} \d .+ test_inode").unwrap();
let result = scene.ucmd().arg("test_inode").arg("-i").run();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
let result = scene.ucmd().arg("-hl").arg(file).run();
assert!(re_short.is_match(&result.stdout));
let inode_short = re_short
.captures(&result.stdout)
.unwrap()
.get(1)
.unwrap()
.as_str();
let result = scene.ucmd().arg("test_inode").run();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
assert!(!re_short.is_match(&result.stdout));
assert!(!result.stdout.contains(inode_short));
let result = scene.ucmd().arg("-li").arg("test_inode").run();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
assert!(re_long.is_match(&result.stdout));
let inode_long = re_long
.captures(&result.stdout)
.unwrap()
.get(1)
.unwrap()
.as_str();
let result = scene.ucmd().arg("-l").arg("test_inode").run();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
assert!(!re_long.is_match(&result.stdout));
assert!(!result.stdout.contains(inode_long));
assert_eq!(inode_short, inode_long)
}
#[cfg(not(any(target_vendor = "apple", target_os = "windows")))] // Truncate not available on mac or win
#[test]
fn test_ls_human_si() {
let scene = TestScenario::new(util_name!());
let file1 = "test_human-1";
let result = scene
.cmd("truncate")
.arg("-s")
.arg("+1000")
.arg(file1)
.run();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
let result = scene.ucmd().arg("-hl").arg(file1).run();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
assert!(result.success);
assert!(result.stdout.contains("1.00K"));
assert!(result.stdout.contains(" 1000 "));
let result = scene.ucmd().arg("-l").arg("--si").arg(file1).run();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
assert!(result.success);
assert!(result.stdout.contains(" 1.0k "));
scene
.cmd("truncate")
.arg("-s")
.arg("+1000k")
.arg(file)
.arg(file1)
.run();
let result = scene.ucmd().arg("-hl").arg(file).run();
let result = scene.ucmd().arg("-hl").arg(file1).run();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
assert!(result.success);
assert!(result.stdout.contains("1.02M"));
assert!(result.stdout.contains(" 1001K "));
let result = scene.ucmd().arg("-l").arg("--si").arg(file1).run();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
assert!(result.success);
assert!(result.stdout.contains(" 1.1M "));
let file2 = "test-human-2";
let result = scene
.cmd("truncate")
.arg("-s")
.arg("+12300k")
.arg(file2)
.run();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
assert!(result.success);
let result = scene.ucmd().arg("-hl").arg(file2).run();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
assert!(result.success);
// GNU rounds up, so we must too.
assert!(result.stdout.contains(" 13M "));
let result = scene.ucmd().arg("-l").arg("--si").arg(file2).run();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
// GNU rounds up, so we must too.
assert!(result.stdout.contains(" 13M "));
let file3 = "test-human-3";
let result = scene
.cmd("truncate")
.arg("-s")
.arg("+9999")
.arg(file3)
.run();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
assert!(result.success);
let result = scene.ucmd().arg("-hl").arg(file3).run();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
assert!(result.success);
assert!(result.stdout.contains(" 9.8K "));
let result = scene.ucmd().arg("-l").arg("--si").arg(file3).run();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
assert!(result.success);
assert!(result.stdout.contains(" 10k "));
}
#[cfg(windows)]
@ -336,3 +869,81 @@ fn test_ls_hidden_windows() {
assert!(result.success);
assert!(result.stdout.contains(file));
}
#[test]
fn test_ls_version_sort() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
for filename in &[
"a2",
"b1",
"b20",
"a1.4",
"a1.40",
"b3",
"b11",
"b20b",
"b20a",
"a100",
"a1.13",
"aa",
"a1",
"aaa",
"a1.00000040",
"abab",
"ab",
"a01.40",
"a001.001",
"a01.0000001",
"a01.001",
"a001.01",
] {
at.touch(filename);
}
let mut expected = vec![
"a1",
"a001.001",
"a001.01",
"a01.0000001",
"a01.001",
"a1.4",
"a1.13",
"a01.40",
"a1.00000040",
"a1.40",
"a2",
"a100",
"aa",
"aaa",
"ab",
"abab",
"b1",
"b3",
"b11",
"b20",
"b20a",
"b20b",
"", // because of '\n' at the end of the output
];
let result = scene.ucmd().arg("-1v").run();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
assert_eq!(result.stdout.split('\n').collect::<Vec<_>>(), expected);
let result = scene.ucmd().arg("-1").arg("--sort=version").run();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
assert_eq!(result.stdout.split('\n').collect::<Vec<_>>(), expected);
let result = scene.ucmd().arg("-a1v").run();
println!("stderr = {:?}", result.stderr);
println!("stdout = {:?}", result.stdout);
expected.insert(0, "..");
expected.insert(0, ".");
assert_eq!(result.stdout.split('\n').collect::<Vec<_>>(), expected,)
}

View file

@ -15,7 +15,9 @@ fn test_negative_adjustment() {
// correctly.
let res = new_ucmd!().args(&["-n", "-1", "true"]).run();
assert!(res.stderr.starts_with("nice: warning: setpriority: Permission denied"));
assert!(res
.stderr
.starts_with("nice: warning: setpriority: Permission denied"));
}
#[test]

View file

@ -23,8 +23,8 @@ fn test_padding_without_overflow() {
.run()
.stdout_is(
"000001xL1\n001001xL2\n002001xL3\n003001xL4\n004001xL5\n005001xL6\n006001xL7\n0070\
01xL8\n008001xL9\n009001xL10\n010001xL11\n011001xL12\n012001xL13\n013001xL14\n014\
001xL15\n",
01xL8\n008001xL9\n009001xL10\n010001xL11\n011001xL12\n012001xL13\n013001xL14\n014\
001xL15\n",
);
}
@ -35,7 +35,7 @@ fn test_padding_with_overflow() {
.run()
.stdout_is(
"0001xL1\n1001xL2\n2001xL3\n3001xL4\n4001xL5\n5001xL6\n6001xL7\n7001xL8\n8001xL9\n\
9001xL10\n10001xL11\n11001xL12\n12001xL13\n13001xL14\n14001xL15\n",
9001xL10\n10001xL11\n11001xL12\n12001xL13\n13001xL14\n14001xL15\n",
);
}
@ -45,15 +45,15 @@ fn test_sections_and_styles() {
(
"section.txt",
"\nHEADER1\nHEADER2\n\n1 |BODY1\n2 \
|BODY2\n\nFOOTER1\nFOOTER2\n\nNEXTHEADER1\nNEXTHEADER2\n\n1 \
|NEXTBODY1\n2 |NEXTBODY2\n\nNEXTFOOTER1\nNEXTFOOTER2\n",
|BODY2\n\nFOOTER1\nFOOTER2\n\nNEXTHEADER1\nNEXTHEADER2\n\n1 \
|NEXTBODY1\n2 |NEXTBODY2\n\nNEXTFOOTER1\nNEXTFOOTER2\n",
),
(
"joinblanklines.txt",
"1 |Nonempty\n2 |Nonempty\n3 |Followed by 10x empty\n\n\n\n\n4 \
|\n\n\n\n\n5 |\n6 |Followed by 5x empty\n\n\n\n\n7 |\n8 \
|Followed by 4x empty\n\n\n\n\n9 |Nonempty\n10 |Nonempty\n11 \
|Nonempty.\n",
|\n\n\n\n\n5 |\n6 |Followed by 5x empty\n\n\n\n\n7 |\n8 \
|Followed by 4x empty\n\n\n\n\n9 |Nonempty\n10 |Nonempty\n11 \
|Nonempty.\n",
),
] {
new_ucmd!()

View file

@ -1 +1,19 @@
// ToDO: add tests
use crate::common::util::*;
use std::thread::sleep;
// General observation: nohup.out will not be created in tests run by cargo test
// because stdin/stdout is not attached to a TTY.
// All that can be tested is the side-effects.
#[test]
#[cfg(any(target_os = "linux", target_os = "freebsd", target_vendor = "apple"))]
fn test_nohup_multiple_args_and_flags() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.args(&["touch", "-t", "1006161200", "file1", "file2"])
.succeeds();
sleep(std::time::Duration::from_millis(10));
assert!(at.file_exists("file1"));
assert!(at.file_exists("file2"));
}

View file

@ -9,7 +9,7 @@ fn test_default_mode() {
// fail on long inputs
new_ucmd!()
.args(&[repeat_str("test", 20000)])
.args(&["test".repeat(20000)])
.fails()
.no_stdout();
}

View file

@ -177,7 +177,7 @@ fn test_rm_directory_without_flag() {
let dir = "test_rm_directory_without_flag_dir";
at.mkdir(dir);
let result = ucmd.arg(dir).fails();
println!("{}", result.stderr);
assert!(result

View file

@ -40,7 +40,7 @@ fn test_rmdir_nonempty_directory_no_parents() {
ucmd.arg(dir).fails().stderr_is(
"rmdir: error: failed to remove 'test_rmdir_nonempty_no_parents': Directory not \
empty\n",
empty\n",
);
assert!(at.dir_exists(dir));
@ -60,9 +60,9 @@ fn test_rmdir_nonempty_directory_with_parents() {
ucmd.arg("-p").arg(dir).fails().stderr_is(
"rmdir: error: failed to remove 'test_rmdir_nonempty/with/parents': Directory not \
empty\nrmdir: error: failed to remove 'test_rmdir_nonempty/with': Directory not \
empty\nrmdir: error: failed to remove 'test_rmdir_nonempty': Directory not \
empty\n",
empty\nrmdir: error: failed to remove 'test_rmdir_nonempty/with': Directory not \
empty\nrmdir: error: failed to remove 'test_rmdir_nonempty': Directory not \
empty\n",
);
assert!(at.dir_exists(dir));

View file

@ -329,3 +329,17 @@ fn test_multiple_input_quiet_flag_overrides_verbose_flag_for_suppressing_headers
.run()
.stdout_is_fixture("foobar_multiple_quiet.expected");
}
#[test]
fn test_negative_indexing() {
let positive_lines_index = new_ucmd!().arg("-n").arg("5").arg(FOOBAR_TXT).run();
let negative_lines_index = new_ucmd!().arg("-n").arg("-5").arg(FOOBAR_TXT).run();
let positive_bytes_index = new_ucmd!().arg("-c").arg("20").arg(FOOBAR_TXT).run();
let negative_bytes_index = new_ucmd!().arg("-c").arg("-20").arg(FOOBAR_TXT).run();
assert_eq!(positive_lines_index.stdout, negative_lines_index.stdout);
assert_eq!(positive_bytes_index.stdout, negative_bytes_index.stdout);
}

View file

@ -15,3 +15,36 @@ fn test_sort_self_loop() {
.succeeds()
.stdout_only("first\nsecond\n");
}
#[test]
fn test_no_such_file() {
let result = new_ucmd!().arg("invalid_file_txt").run();
assert_eq!(true, result.stderr.contains("No such file or directory"));
}
#[test]
fn test_version_flag() {
let version_short = new_ucmd!().arg("-V").run();
let version_long = new_ucmd!().arg("--version").run();
assert_eq!(version_short.stdout, version_long.stdout);
}
#[test]
fn test_help_flag() {
let help_short = new_ucmd!().arg("-h").run();
let help_long = new_ucmd!().arg("--help").run();
assert_eq!(help_short.stdout, help_long.stdout);
}
#[test]
fn test_multiple_arguments() {
let result = new_ucmd!()
.arg("call_graph.txt")
.arg("invalid_file.txt")
.run();
assert_eq!(true, result.stderr.contains("error: Found argument 'invalid_file.txt' which wasn't expected, or isn't valid in this context"))
}

View file

@ -136,3 +136,22 @@ fn unexpand_spaces_after_fields() {
.run()
.stdout_is("\t\tA B C D\t\t A\t\n");
}
#[test]
fn unexpand_read_from_file() {
new_ucmd!()
.arg("with_spaces.txt")
.arg("-t4")
.run()
.success();
}
#[test]
fn unexpand_read_from_two_file() {
new_ucmd!()
.arg("with_spaces.txt")
.arg("with_spaces.txt")
.arg("-t4")
.run()
.success();
}

View file

@ -23,7 +23,7 @@ fn test_unlink_multiple_files() {
ucmd.arg(file_a).arg(file_b).fails().stderr_is(
"unlink: error: extra operand: 'test_unlink_multiple_file_b'\nTry 'unlink --help' \
for more information.\n",
for more information.\n",
);
}
@ -36,7 +36,7 @@ fn test_unlink_directory() {
ucmd.arg(dir).fails().stderr_is(
"unlink: error: cannot unlink 'test_unlink_empty_directory': Not a regular file \
or symlink\n",
or symlink\n",
);
}
@ -46,6 +46,6 @@ fn test_unlink_nonexistent() {
new_ucmd!().arg(file).fails().stderr_is(
"unlink: error: Cannot stat 'test_unlink_nonexistent': No such file or directory \
(os error 2)\n",
(os error 2)\n",
);
}

View file

@ -81,6 +81,6 @@ fn test_multiple_default() {
.run()
.stdout_is(
" 13 109 772 lorem_ipsum.txt\n 18 204 1115 moby_dick.txt\n 5 57 302 \
alice_in_wonderland.txt\n 36 370 2189 total\n",
alice_in_wonderland.txt\n 36 370 2189 total\n",
);
}

View file

@ -1,3 +1,6 @@
/// Assertion helper macro for [`CmdResult`] types
///
/// [`CmdResult`]: crate::tests::common::util::CmdResult
#[macro_export]
macro_rules! assert_empty_stderr(
($cond:expr) => (
@ -7,6 +10,9 @@ macro_rules! assert_empty_stderr(
);
);
/// Assertion helper macro for [`CmdResult`] types
///
/// [`CmdResult`]: crate::tests::common::util::CmdResult
#[macro_export]
macro_rules! assert_empty_stdout(
($cond:expr) => (
@ -16,6 +22,9 @@ macro_rules! assert_empty_stdout(
);
);
/// Assertion helper macro for [`CmdResult`] types
///
/// [`CmdResult`]: crate::tests::common::util::CmdResult
#[macro_export]
macro_rules! assert_no_error(
($cond:expr) => (
@ -26,6 +35,7 @@ macro_rules! assert_no_error(
);
);
/// Platform-independent helper for constructing a PathBuf from individual elements
#[macro_export]
macro_rules! path_concat {
($e:expr, ..$n:expr) => {{
@ -47,6 +57,9 @@ macro_rules! path_concat {
}};
}
/// Deduce the name of the test binary from the test filename.
///
/// e.g.: `tests/by-util/test_cat.rs` -> `cat`
#[macro_export]
macro_rules! util_name {
() => {
@ -54,6 +67,16 @@ macro_rules! util_name {
};
}
/// Convenience macro for acquiring a [`UCommand`] builder.
///
/// Returns the following:
/// - a [`UCommand`] builder for invoking the binary to be tested
///
/// This macro is intended for quick, single-call tests. For more complex tests
/// that require multiple invocations of the tested binary, see [`TestScenario`]
///
/// [`UCommand`]: crate::tests::common::util::UCommand
/// [`TestScenario]: crate::tests::common::util::TestScenario
#[macro_export]
macro_rules! new_ucmd {
() => {
@ -61,6 +84,18 @@ macro_rules! new_ucmd {
};
}
/// Convenience macro for acquiring a [`UCommand`] builder and a test path.
///
/// Returns a tuple containing the following:
/// - an [`AsPath`] that points to a unique temporary test directory
/// - a [`UCommand`] builder for invoking the binary to be tested
///
/// This macro is intended for quick, single-call tests. For more complex tests
/// that require multiple invocations of the tested binary, see [`TestScenario`]
///
/// [`UCommand`]: crate::tests::common::util::UCommand
/// [`AsPath`]: crate::tests::common::util::AsPath
/// [`TestScenario]: crate::tests::common::util::TestScenario
#[macro_export]
macro_rules! at_and_ucmd {
() => {{

View file

@ -1,3 +1,5 @@
#![allow(dead_code)]
use std::env;
use std::ffi::OsStr;
use std::fs::{self, File, OpenOptions};
@ -27,7 +29,7 @@ static ALREADY_RUN: &str = " you have already run this UCommand, if you want to
testing();";
static MULTIPLE_STDIN_MEANINGLESS: &str = "Ucommand is designed around a typical use case of: provide args and input stream -> spawn process -> block until completion -> return output streams. For verifying that a particular section of the input stream is what causes a particular behavior, use the Command type directly.";
/// Test if the program are running under CI
/// Test if the program is running under CI
pub fn is_ci() -> bool {
std::env::var("CI")
.unwrap_or(String::from("false"))
@ -55,14 +57,6 @@ fn read_scenario_fixture<S: AsRef<OsStr>>(tmpd: &Option<Rc<TempDir>>, file_rel_p
AtPath::new(tmpdir_path).read(file_rel_path.as_ref().to_str().unwrap())
}
pub fn repeat_str(s: &str, n: u32) -> String {
let mut repeated = String::new();
for _ in 0..n {
repeated.push_str(s);
}
repeated
}
/// A command result is the outputs of a command (streams and status code)
/// within a struct which has convenience assertion functions about those outputs
#[derive(Debug)]
@ -384,8 +378,10 @@ impl AtPath {
/// An environment for running a single uutils test case, serves three functions:
/// 1. centralizes logic for locating the uutils binary and calling the utility
/// 2. provides a temporary directory for the test case
/// 2. provides a unique temporary directory for the test case
/// 3. copies over fixtures for the utility to the temporary directory
///
/// Fixtures can be found under `tests/fixtures/$util_name/`
pub struct TestScenario {
bin_path: PathBuf,
util_name: String,
@ -420,12 +416,16 @@ impl TestScenario {
ts
}
/// Returns builder for invoking the target uutils binary. Paths given are
/// treated relative to the environment's unique temporary test directory.
pub fn ucmd(&self) -> UCommand {
let mut cmd = self.cmd(&self.bin_path);
cmd.arg(&self.util_name);
cmd
}
/// Returns builder for invoking any system command. Paths given are treated
/// relative to the environment's unique temporary test directory.
pub fn cmd<S: AsRef<OsStr>>(&self, bin: S) -> UCommand {
UCommand::new_from_tmp(bin, self.tmpd.clone(), true)
}
@ -495,6 +495,8 @@ impl UCommand {
ucmd
}
/// Add a parameter to the invocation. Path arguments are treated relative
/// to the test environment directory.
pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> Box<&mut UCommand> {
if self.has_run {
panic!(ALREADY_RUN);
@ -505,6 +507,8 @@ impl UCommand {
Box::new(self)
}
/// Add multiple parameters to the invocation. Path arguments are treated relative
/// to the test environment directory.
pub fn args<S: AsRef<OsStr>>(&mut self, args: &[S]) -> Box<&mut UCommand> {
if self.has_run {
panic!(MULTIPLE_STDIN_MEANINGLESS);

View file

View file

Binary file not shown.

BIN
tests/fixtures/head/zero_terminated.txt vendored Normal file

Binary file not shown.

View file

@ -0,0 +1,2 @@
abc d e f g \t\t A