2022-09-07 16:55:32 +00:00
|
|
|
// spell-checker:ignore (flags) reflink (fs) tmpfs (linux) rlimit Rlim NOFILE clob btrfs ROOTDIR USERDIR procfs
|
2021-05-30 05:10:54 +00:00
|
|
|
|
2020-05-25 17:05:26 +00:00
|
|
|
use crate::common::util::*;
|
2018-09-23 00:11:13 +00:00
|
|
|
#[cfg(not(windows))]
|
2016-12-28 01:08:43 +00:00
|
|
|
use std::fs::set_permissions;
|
2015-11-16 05:25:01 +00:00
|
|
|
|
2020-05-26 19:35:30 +00:00
|
|
|
#[cfg(not(windows))]
|
|
|
|
use std::os::unix::fs;
|
|
|
|
|
2022-08-12 21:51:28 +00:00
|
|
|
#[cfg(all(unix, not(target_os = "freebsd")))]
|
2022-07-03 17:01:23 +00:00
|
|
|
use std::os::unix::fs::MetadataExt;
|
2021-09-12 21:14:34 +00:00
|
|
|
#[cfg(all(unix, not(target_os = "freebsd")))]
|
2021-06-01 21:06:38 +00:00
|
|
|
use std::os::unix::fs::PermissionsExt;
|
2020-05-26 19:35:30 +00:00
|
|
|
#[cfg(windows)]
|
|
|
|
use std::os::windows::fs::symlink_file;
|
|
|
|
|
2022-02-09 18:08:28 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
2020-08-04 21:15:33 +00:00
|
|
|
use filetime::FileTime;
|
2022-02-09 18:08:28 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
2021-06-02 16:08:09 +00:00
|
|
|
use rlimit::Resource;
|
2022-02-09 18:08:28 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
2020-08-04 21:15:33 +00:00
|
|
|
use std::fs as std_fs;
|
2022-08-12 21:51:28 +00:00
|
|
|
#[cfg(not(target_os = "freebsd"))]
|
2020-08-04 21:15:33 +00:00
|
|
|
use std::thread::sleep;
|
2022-08-12 21:51:28 +00:00
|
|
|
#[cfg(not(target_os = "freebsd"))]
|
2020-08-04 21:15:33 +00:00
|
|
|
use std::time::Duration;
|
2022-07-17 20:03:29 +00:00
|
|
|
use uucore::display::Quotable;
|
2020-05-28 19:54:03 +00:00
|
|
|
|
2020-04-13 18:36:03 +00:00
|
|
|
static TEST_EXISTING_FILE: &str = "existing_file.txt";
|
|
|
|
static TEST_HELLO_WORLD_SOURCE: &str = "hello_world.txt";
|
2020-05-26 19:35:30 +00:00
|
|
|
static TEST_HELLO_WORLD_SOURCE_SYMLINK: &str = "hello_world.txt.link";
|
2020-04-13 18:36:03 +00:00
|
|
|
static TEST_HELLO_WORLD_DEST: &str = "copy_of_hello_world.txt";
|
2022-02-09 01:37:55 +00:00
|
|
|
static TEST_HELLO_WORLD_DEST_SYMLINK: &str = "copy_of_hello_world.txt.link";
|
2020-04-13 18:36:03 +00:00
|
|
|
static TEST_HOW_ARE_YOU_SOURCE: &str = "how_are_you.txt";
|
|
|
|
static TEST_HOW_ARE_YOU_DEST: &str = "hello_dir/how_are_you.txt";
|
|
|
|
static TEST_COPY_TO_FOLDER: &str = "hello_dir/";
|
|
|
|
static TEST_COPY_TO_FOLDER_FILE: &str = "hello_dir/hello_world.txt";
|
|
|
|
static TEST_COPY_FROM_FOLDER: &str = "hello_dir_with_file/";
|
|
|
|
static TEST_COPY_FROM_FOLDER_FILE: &str = "hello_dir_with_file/hello_world.txt";
|
2020-05-28 19:54:03 +00:00
|
|
|
static TEST_COPY_TO_FOLDER_NEW: &str = "hello_dir_new";
|
2017-08-10 23:04:25 +00:00
|
|
|
static TEST_COPY_TO_FOLDER_NEW_FILE: &str = "hello_dir_new/hello_world.txt";
|
2022-02-09 18:08:28 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))]
|
2021-03-19 20:15:35 +00:00
|
|
|
static TEST_MOUNT_COPY_FROM_FOLDER: &str = "dir_with_mount";
|
2022-02-09 18:08:28 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))]
|
2021-03-19 20:15:35 +00:00
|
|
|
static TEST_MOUNT_MOUNTPOINT: &str = "mount";
|
2022-02-09 18:08:28 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))]
|
2021-03-19 20:15:35 +00:00
|
|
|
static TEST_MOUNT_OTHER_FILESYSTEM_FILE: &str = "mount/DO_NOT_copy_me.txt";
|
2022-01-30 08:33:06 +00:00
|
|
|
#[cfg(unix)]
|
|
|
|
static TEST_NONEXISTENT_FILE: &str = "nonexistent_file.txt";
|
2015-11-16 05:25:01 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_cp() {
|
2016-08-23 11:52:43 +00:00
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
2015-11-16 05:25:01 +00:00
|
|
|
// Invoke our binary to make the copy.
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
|
2020-04-13 18:36:03 +00:00
|
|
|
.arg(TEST_HELLO_WORLD_DEST)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2015-11-16 05:25:01 +00:00
|
|
|
|
|
|
|
// Check the content of the destination file that was copied.
|
|
|
|
assert_eq!(at.read(TEST_HELLO_WORLD_DEST), "Hello, World!\n");
|
|
|
|
}
|
2016-07-15 17:17:30 +00:00
|
|
|
|
2018-09-23 00:10:14 +00:00
|
|
|
#[test]
|
|
|
|
fn test_cp_existing_target() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
|
2018-09-23 00:10:14 +00:00
|
|
|
.arg(TEST_EXISTING_FILE)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2018-09-23 00:10:14 +00:00
|
|
|
|
|
|
|
// Check the content of the destination file
|
|
|
|
assert_eq!(at.read(TEST_EXISTING_FILE), "Hello, World!\n");
|
|
|
|
|
|
|
|
// No backup should have been created
|
2022-08-22 02:48:09 +00:00
|
|
|
assert!(!at.file_exists(&format!("{}~", TEST_EXISTING_FILE)));
|
2018-09-23 00:10:14 +00:00
|
|
|
}
|
|
|
|
|
2016-12-28 01:08:43 +00:00
|
|
|
#[test]
|
|
|
|
fn test_cp_duplicate_files() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
|
2016-12-28 01:08:43 +00:00
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_COPY_TO_FOLDER)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds()
|
|
|
|
.stderr_contains("specified more than once");
|
2016-12-28 01:08:43 +00:00
|
|
|
assert_eq!(at.read(TEST_COPY_TO_FOLDER_FILE), "Hello, World!\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_multiple_files_target_is_file() {
|
2021-04-22 20:37:44 +00:00
|
|
|
new_ucmd!()
|
2020-04-13 18:36:03 +00:00
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
2016-12-28 01:08:43 +00:00
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_EXISTING_FILE)
|
2021-04-22 20:37:44 +00:00
|
|
|
.fails()
|
|
|
|
.stderr_contains("not a directory");
|
2016-12-28 01:08:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_directory_not_recursive() {
|
2021-04-22 20:37:44 +00:00
|
|
|
new_ucmd!()
|
2020-04-13 18:36:03 +00:00
|
|
|
.arg(TEST_COPY_TO_FOLDER)
|
2016-12-28 01:08:43 +00:00
|
|
|
.arg(TEST_HELLO_WORLD_DEST)
|
2021-04-22 20:37:44 +00:00
|
|
|
.fails()
|
|
|
|
.stderr_contains("omitting directory");
|
2016-12-28 01:08:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_multiple_files() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
|
2016-12-28 01:08:43 +00:00
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
|
|
|
.arg(TEST_COPY_TO_FOLDER)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2016-12-28 01:08:43 +00:00
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_COPY_TO_FOLDER_FILE), "Hello, World!\n");
|
|
|
|
assert_eq!(at.read(TEST_HOW_ARE_YOU_DEST), "How are you?\n");
|
|
|
|
}
|
|
|
|
|
2017-06-02 11:37:31 +00:00
|
|
|
#[test]
|
2020-10-14 20:34:28 +00:00
|
|
|
// FixME: for MacOS, this has intermittent failures; track repair progress at GH:uutils/coreutils/issues/1590
|
2021-05-29 12:32:35 +00:00
|
|
|
#[cfg(not(target_os = "macos"))]
|
2017-06-02 11:37:31 +00:00
|
|
|
fn test_cp_recurse() {
|
2017-08-10 23:04:25 +00:00
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg("-r")
|
2017-08-10 23:04:25 +00:00
|
|
|
.arg(TEST_COPY_FROM_FOLDER)
|
|
|
|
.arg(TEST_COPY_TO_FOLDER_NEW)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2017-06-02 11:37:31 +00:00
|
|
|
|
|
|
|
// Check the content of the destination file that was copied.
|
2017-08-10 23:04:25 +00:00
|
|
|
assert_eq!(at.read(TEST_COPY_TO_FOLDER_NEW_FILE), "Hello, World!\n");
|
2017-06-02 11:37:31 +00:00
|
|
|
}
|
|
|
|
|
2016-07-15 17:17:30 +00:00
|
|
|
#[test]
|
|
|
|
fn test_cp_with_dirs_t() {
|
2016-08-23 11:52:43 +00:00
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg("-t")
|
2016-07-15 17:17:30 +00:00
|
|
|
.arg(TEST_COPY_TO_FOLDER)
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2016-07-15 17:17:30 +00:00
|
|
|
assert_eq!(at.read(TEST_COPY_TO_FOLDER_FILE), "Hello, World!\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2020-10-14 20:34:28 +00:00
|
|
|
// FixME: for MacOS, this has intermittent failures; track repair progress at GH:uutils/coreutils/issues/1590
|
2021-05-29 12:32:35 +00:00
|
|
|
#[cfg(not(target_os = "macos"))]
|
2016-07-15 17:17:30 +00:00
|
|
|
fn test_cp_with_dirs() {
|
2016-08-23 11:52:43 +00:00
|
|
|
let scene = TestScenario::new(util_name!());
|
2016-07-29 21:26:32 +00:00
|
|
|
let at = &scene.fixtures;
|
2016-07-15 17:17:30 +00:00
|
|
|
|
2021-04-22 20:37:44 +00:00
|
|
|
scene
|
2020-04-13 18:36:03 +00:00
|
|
|
.ucmd()
|
2016-07-15 17:17:30 +00:00
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_COPY_TO_FOLDER)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2016-07-15 17:17:30 +00:00
|
|
|
assert_eq!(at.read(TEST_COPY_TO_FOLDER_FILE), "Hello, World!\n");
|
|
|
|
|
2021-04-22 20:37:44 +00:00
|
|
|
scene
|
2020-04-13 18:36:03 +00:00
|
|
|
.ucmd()
|
2016-07-15 17:17:30 +00:00
|
|
|
.arg(TEST_COPY_FROM_FOLDER_FILE)
|
|
|
|
.arg(TEST_HELLO_WORLD_DEST)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2016-07-15 17:17:30 +00:00
|
|
|
assert_eq!(at.read(TEST_HELLO_WORLD_DEST), "Hello, World!\n");
|
2016-07-29 21:26:32 +00:00
|
|
|
}
|
2016-12-28 01:08:43 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_arg_target_directory() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
|
2016-12-28 01:08:43 +00:00
|
|
|
.arg("-t")
|
|
|
|
.arg(TEST_COPY_TO_FOLDER)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2016-12-28 01:08:43 +00:00
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_COPY_TO_FOLDER_FILE), "Hello, World!\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_arg_no_target_directory() {
|
2021-04-22 20:37:44 +00:00
|
|
|
new_ucmd!()
|
2020-04-13 18:36:03 +00:00
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
2016-12-28 01:08:43 +00:00
|
|
|
.arg("-v")
|
|
|
|
.arg("-T")
|
|
|
|
.arg(TEST_COPY_TO_FOLDER)
|
2021-04-22 20:37:44 +00:00
|
|
|
.fails()
|
|
|
|
.stderr_contains("cannot overwrite directory");
|
2016-12-28 01:08:43 +00:00
|
|
|
}
|
|
|
|
|
2022-02-02 03:26:33 +00:00
|
|
|
#[test]
|
|
|
|
fn test_cp_target_directory_is_file() {
|
|
|
|
new_ucmd!()
|
|
|
|
.arg("-t")
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.fails()
|
2022-03-01 06:30:29 +00:00
|
|
|
.stderr_contains(format!("'{}' is not a directory", TEST_HOW_ARE_YOU_SOURCE));
|
2022-02-02 03:26:33 +00:00
|
|
|
}
|
|
|
|
|
2016-12-28 01:08:43 +00:00
|
|
|
#[test]
|
|
|
|
fn test_cp_arg_interactive() {
|
2021-04-22 20:37:44 +00:00
|
|
|
new_ucmd!()
|
2020-04-13 18:36:03 +00:00
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
2016-12-28 01:08:43 +00:00
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
|
|
|
.arg("-i")
|
|
|
|
.pipe_in("N\n")
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds()
|
2022-09-17 16:15:52 +00:00
|
|
|
.no_stdout()
|
|
|
|
.stderr_contains(format!("overwrite '{}'?", TEST_HOW_ARE_YOU_SOURCE))
|
2021-04-22 20:37:44 +00:00
|
|
|
.stderr_contains("Not overwriting");
|
2016-12-28 01:08:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2020-08-04 21:15:33 +00:00
|
|
|
#[cfg(target_os = "linux")]
|
2016-12-28 01:08:43 +00:00
|
|
|
fn test_cp_arg_link() {
|
|
|
|
use std::os::linux::fs::MetadataExt;
|
|
|
|
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
|
2016-12-28 01:08:43 +00:00
|
|
|
.arg("--link")
|
|
|
|
.arg(TEST_HELLO_WORLD_DEST)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2016-12-28 01:08:43 +00:00
|
|
|
|
|
|
|
assert_eq!(at.metadata(TEST_HELLO_WORLD_SOURCE).st_nlink(), 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_arg_symlink() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
|
2016-12-28 01:08:43 +00:00
|
|
|
.arg("--symbolic-link")
|
|
|
|
.arg(TEST_HELLO_WORLD_DEST)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2016-12-28 01:08:43 +00:00
|
|
|
|
|
|
|
assert!(at.is_symlink(TEST_HELLO_WORLD_DEST));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_arg_no_clobber() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
|
2016-12-28 01:08:43 +00:00
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
2021-05-25 20:41:07 +00:00
|
|
|
.arg("--no-clobber")
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2016-12-28 01:08:43 +00:00
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "How are you?\n");
|
2020-12-16 22:52:42 +00:00
|
|
|
}
|
|
|
|
|
2022-01-29 00:03:28 +00:00
|
|
|
#[test]
|
|
|
|
fn test_cp_arg_no_clobber_inferred_arg() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
|
|
|
.arg("--no-clob")
|
|
|
|
.succeeds();
|
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "How are you?\n");
|
|
|
|
}
|
|
|
|
|
2020-12-16 22:52:42 +00:00
|
|
|
#[test]
|
|
|
|
fn test_cp_arg_no_clobber_twice() {
|
|
|
|
let scene = TestScenario::new(util_name!());
|
|
|
|
let at = &scene.fixtures;
|
2021-04-22 20:37:44 +00:00
|
|
|
|
2020-12-16 22:52:42 +00:00
|
|
|
at.touch("source.txt");
|
2021-04-22 20:37:44 +00:00
|
|
|
scene
|
2020-12-16 22:52:42 +00:00
|
|
|
.ucmd()
|
|
|
|
.arg("--no-clobber")
|
|
|
|
.arg("source.txt")
|
|
|
|
.arg("dest.txt")
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds()
|
|
|
|
.no_stderr();
|
2020-12-16 22:52:42 +00:00
|
|
|
|
|
|
|
assert_eq!(at.read("source.txt"), "");
|
|
|
|
|
|
|
|
at.append("source.txt", "some-content");
|
2021-04-22 20:37:44 +00:00
|
|
|
scene
|
2020-12-16 22:52:42 +00:00
|
|
|
.ucmd()
|
|
|
|
.arg("--no-clobber")
|
|
|
|
.arg("source.txt")
|
|
|
|
.arg("dest.txt")
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds()
|
|
|
|
.stdout_does_not_contain("Not overwriting");
|
2020-12-16 22:52:42 +00:00
|
|
|
|
|
|
|
assert_eq!(at.read("source.txt"), "some-content");
|
|
|
|
// Should be empty as the "no-clobber" should keep
|
|
|
|
// the previous version
|
|
|
|
assert_eq!(at.read("dest.txt"), "");
|
2016-12-28 01:08:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
fn test_cp_arg_force() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
// create dest without write permissions
|
2020-04-13 18:36:03 +00:00
|
|
|
let mut permissions = at
|
|
|
|
.make_file(TEST_HELLO_WORLD_DEST)
|
|
|
|
.metadata()
|
|
|
|
.unwrap()
|
|
|
|
.permissions();
|
2016-12-28 01:08:43 +00:00
|
|
|
permissions.set_readonly(true);
|
|
|
|
set_permissions(at.plus(TEST_HELLO_WORLD_DEST), permissions).unwrap();
|
|
|
|
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
|
2016-12-28 01:08:43 +00:00
|
|
|
.arg("--force")
|
|
|
|
.arg(TEST_HELLO_WORLD_DEST)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2016-12-28 01:08:43 +00:00
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_HELLO_WORLD_DEST), "Hello, World!\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
/// TODO: write a better test that differentiates --remove-destination
|
|
|
|
/// from --force. Also this test currently doesn't work on
|
|
|
|
/// Windows. This test originally checked file timestamps, which
|
|
|
|
/// proved to be unreliable per target / CI platform
|
|
|
|
#[test]
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
fn test_cp_arg_remove_destination() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
// create dest without write permissions
|
2020-04-13 18:36:03 +00:00
|
|
|
let mut permissions = at
|
|
|
|
.make_file(TEST_HELLO_WORLD_DEST)
|
|
|
|
.metadata()
|
|
|
|
.unwrap()
|
|
|
|
.permissions();
|
2016-12-28 01:08:43 +00:00
|
|
|
permissions.set_readonly(true);
|
|
|
|
set_permissions(at.plus(TEST_HELLO_WORLD_DEST), permissions).unwrap();
|
|
|
|
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
|
2016-12-28 01:08:43 +00:00
|
|
|
.arg("--remove-destination")
|
|
|
|
.arg(TEST_HELLO_WORLD_DEST)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2016-12-28 01:08:43 +00:00
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_HELLO_WORLD_DEST), "Hello, World!\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_arg_backup() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
|
2021-05-25 20:41:07 +00:00
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
|
|
|
.arg("-b")
|
|
|
|
.succeeds();
|
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
|
|
|
|
assert_eq!(
|
2022-08-22 02:48:09 +00:00
|
|
|
at.read(&format!("{}~", TEST_HOW_ARE_YOU_SOURCE)),
|
2021-05-25 20:41:07 +00:00
|
|
|
"How are you?\n"
|
2021-05-26 22:28:17 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_arg_backup_with_other_args() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
|
|
|
.arg("-vbL")
|
|
|
|
.succeeds();
|
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
|
|
|
|
assert_eq!(
|
2022-08-22 02:48:09 +00:00
|
|
|
at.read(&format!("{}~", TEST_HOW_ARE_YOU_SOURCE)),
|
2021-05-26 22:28:17 +00:00
|
|
|
"How are you?\n"
|
2021-05-25 20:41:07 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_arg_backup_arg_first() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
ucmd.arg("--backup")
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
2016-12-28 01:08:43 +00:00
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2016-12-28 01:08:43 +00:00
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
|
2020-04-13 18:36:03 +00:00
|
|
|
assert_eq!(
|
2022-08-22 02:48:09 +00:00
|
|
|
at.read(&format!("{}~", TEST_HOW_ARE_YOU_SOURCE)),
|
2020-04-13 18:36:03 +00:00
|
|
|
"How are you?\n"
|
|
|
|
);
|
2016-12-28 01:08:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_arg_suffix() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
|
2021-05-25 20:41:07 +00:00
|
|
|
.arg("-b")
|
2016-12-28 01:08:43 +00:00
|
|
|
.arg("--suffix")
|
|
|
|
.arg(".bak")
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2016-12-28 01:08:43 +00:00
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
|
2020-04-13 18:36:03 +00:00
|
|
|
assert_eq!(
|
2022-08-22 02:48:09 +00:00
|
|
|
at.read(&format!("{}.bak", TEST_HOW_ARE_YOU_SOURCE)),
|
2020-04-13 18:36:03 +00:00
|
|
|
"How are you?\n"
|
|
|
|
);
|
2016-12-28 01:08:43 +00:00
|
|
|
}
|
2020-05-26 19:35:30 +00:00
|
|
|
|
2022-01-31 11:10:57 +00:00
|
|
|
#[test]
|
|
|
|
fn test_cp_arg_suffix_hyphen_value() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg("-b")
|
|
|
|
.arg("--suffix")
|
|
|
|
.arg("-v")
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
|
|
|
.succeeds();
|
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
|
|
|
|
assert_eq!(
|
2022-08-22 02:48:09 +00:00
|
|
|
at.read(&format!("{}-v", TEST_HOW_ARE_YOU_SOURCE)),
|
2022-01-31 11:10:57 +00:00
|
|
|
"How are you?\n"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-05-25 20:41:07 +00:00
|
|
|
#[test]
|
|
|
|
fn test_cp_custom_backup_suffix_via_env() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
let suffix = "super-suffix-of-the-century";
|
|
|
|
|
|
|
|
ucmd.arg("-b")
|
|
|
|
.env("SIMPLE_BACKUP_SUFFIX", suffix)
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
|
|
|
.succeeds()
|
|
|
|
.no_stderr();
|
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
|
|
|
|
assert_eq!(
|
2022-08-22 02:48:09 +00:00
|
|
|
at.read(&format!("{}{}", TEST_HOW_ARE_YOU_SOURCE, suffix)),
|
2021-05-25 20:41:07 +00:00
|
|
|
"How are you?\n"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_backup_numbered_with_t() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
ucmd.arg("--backup=t")
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
|
|
|
.succeeds()
|
|
|
|
.no_stderr();
|
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
|
|
|
|
assert_eq!(
|
2022-08-22 02:48:09 +00:00
|
|
|
at.read(&format!("{}.~1~", TEST_HOW_ARE_YOU_SOURCE)),
|
2021-05-25 20:41:07 +00:00
|
|
|
"How are you?\n"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_backup_numbered() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
ucmd.arg("--backup=numbered")
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
|
|
|
.succeeds()
|
|
|
|
.no_stderr();
|
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
|
|
|
|
assert_eq!(
|
2022-08-22 02:48:09 +00:00
|
|
|
at.read(&format!("{}.~1~", TEST_HOW_ARE_YOU_SOURCE)),
|
2021-05-25 20:41:07 +00:00
|
|
|
"How are you?\n"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_backup_existing() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
ucmd.arg("--backup=existing")
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
|
|
|
.succeeds()
|
|
|
|
.no_stderr();
|
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
|
|
|
|
assert_eq!(
|
2022-08-22 02:48:09 +00:00
|
|
|
at.read(&format!("{}~", TEST_HOW_ARE_YOU_SOURCE)),
|
2021-05-25 20:41:07 +00:00
|
|
|
"How are you?\n"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_backup_nil() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
ucmd.arg("--backup=nil")
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
|
|
|
.succeeds()
|
|
|
|
.no_stderr();
|
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
|
|
|
|
assert_eq!(
|
2022-08-22 02:48:09 +00:00
|
|
|
at.read(&format!("{}~", TEST_HOW_ARE_YOU_SOURCE)),
|
2021-05-25 20:41:07 +00:00
|
|
|
"How are you?\n"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_numbered_if_existing_backup_existing() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
2022-08-22 02:48:09 +00:00
|
|
|
let existing_backup = &format!("{}.~1~", TEST_HOW_ARE_YOU_SOURCE);
|
2021-05-25 20:41:07 +00:00
|
|
|
at.touch(existing_backup);
|
|
|
|
|
|
|
|
ucmd.arg("--backup=existing")
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
|
|
|
.succeeds()
|
|
|
|
.no_stderr();
|
|
|
|
|
|
|
|
assert!(at.file_exists(TEST_HOW_ARE_YOU_SOURCE));
|
|
|
|
assert!(at.file_exists(existing_backup));
|
|
|
|
assert_eq!(
|
2022-08-22 02:48:09 +00:00
|
|
|
at.read(&format!("{}.~2~", TEST_HOW_ARE_YOU_SOURCE)),
|
2021-05-25 20:41:07 +00:00
|
|
|
"How are you?\n"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_numbered_if_existing_backup_nil() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
2022-08-22 02:48:09 +00:00
|
|
|
let existing_backup = &format!("{}.~1~", TEST_HOW_ARE_YOU_SOURCE);
|
2021-05-25 20:41:07 +00:00
|
|
|
|
|
|
|
at.touch(existing_backup);
|
|
|
|
ucmd.arg("--backup=nil")
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
|
|
|
.succeeds()
|
|
|
|
.no_stderr();
|
|
|
|
|
|
|
|
assert!(at.file_exists(TEST_HOW_ARE_YOU_SOURCE));
|
|
|
|
assert!(at.file_exists(existing_backup));
|
|
|
|
assert_eq!(
|
2022-08-22 02:48:09 +00:00
|
|
|
at.read(&format!("{}.~2~", TEST_HOW_ARE_YOU_SOURCE)),
|
2021-05-25 20:41:07 +00:00
|
|
|
"How are you?\n"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_backup_simple() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
ucmd.arg("--backup=simple")
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
|
|
|
.succeeds()
|
|
|
|
.no_stderr();
|
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
|
|
|
|
assert_eq!(
|
2022-08-22 02:48:09 +00:00
|
|
|
at.read(&format!("{}~", TEST_HOW_ARE_YOU_SOURCE)),
|
2021-05-25 20:41:07 +00:00
|
|
|
"How are you?\n"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-06-18 10:53:18 +00:00
|
|
|
fn test_cp_backup_simple_protect_source() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
2022-06-18 16:51:39 +00:00
|
|
|
let source = format!("{}~", TEST_HELLO_WORLD_SOURCE);
|
|
|
|
at.touch(&source);
|
2022-06-18 10:53:18 +00:00
|
|
|
ucmd.arg("--backup=simple")
|
2022-06-18 16:51:39 +00:00
|
|
|
.arg(&source)
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
2022-06-18 10:53:18 +00:00
|
|
|
.fails()
|
|
|
|
.stderr_only(format!(
|
|
|
|
"cp: backing up '{}' might destroy source; '{}' not copied",
|
2022-06-18 16:51:39 +00:00
|
|
|
TEST_HELLO_WORLD_SOURCE, source,
|
2022-06-18 10:53:18 +00:00
|
|
|
));
|
|
|
|
|
2022-06-18 16:51:39 +00:00
|
|
|
assert_eq!(at.read(TEST_HELLO_WORLD_SOURCE), "Hello, World!\n");
|
|
|
|
assert_eq!(at.read(&source), "");
|
2022-06-18 10:53:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-05-25 20:41:07 +00:00
|
|
|
fn test_cp_backup_never() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
ucmd.arg("--backup=never")
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
|
|
|
.succeeds()
|
|
|
|
.no_stderr();
|
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
|
|
|
|
assert_eq!(
|
2022-08-22 02:48:09 +00:00
|
|
|
at.read(&format!("{}~", TEST_HOW_ARE_YOU_SOURCE)),
|
2021-05-25 20:41:07 +00:00
|
|
|
"How are you?\n"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_backup_none() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
ucmd.arg("--backup=none")
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
|
|
|
.succeeds()
|
|
|
|
.no_stderr();
|
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
|
|
|
|
assert!(!at.file_exists(&format!("{}~", TEST_HOW_ARE_YOU_SOURCE)));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_backup_off() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
ucmd.arg("--backup=off")
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
|
|
|
.succeeds()
|
|
|
|
.no_stderr();
|
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
|
|
|
|
assert!(!at.file_exists(&format!("{}~", TEST_HOW_ARE_YOU_SOURCE)));
|
|
|
|
}
|
|
|
|
|
2021-05-26 14:50:41 +00:00
|
|
|
#[test]
|
|
|
|
fn test_cp_backup_no_clobber_conflicting_options() {
|
2021-11-09 20:23:41 +00:00
|
|
|
new_ucmd!()
|
2021-07-27 05:21:12 +00:00
|
|
|
.arg("--backup")
|
2021-05-26 14:50:41 +00:00
|
|
|
.arg("--no-clobber")
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
2021-11-09 20:23:41 +00:00
|
|
|
.fails()
|
|
|
|
.usage_error("options --backup and --no-clobber are mutually exclusive");
|
2021-05-26 14:50:41 +00:00
|
|
|
}
|
|
|
|
|
2020-11-11 20:41:02 +00:00
|
|
|
#[test]
|
|
|
|
fn test_cp_deref_conflicting_options() {
|
2021-04-22 20:37:44 +00:00
|
|
|
new_ucmd!()
|
|
|
|
.arg("-LP")
|
2020-11-11 20:41:02 +00:00
|
|
|
.arg(TEST_COPY_TO_FOLDER)
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.fails();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_deref() {
|
2021-04-22 20:37:44 +00:00
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
2020-11-11 20:41:02 +00:00
|
|
|
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
let _r = fs::symlink(
|
|
|
|
TEST_HELLO_WORLD_SOURCE,
|
|
|
|
at.subdir.join(TEST_HELLO_WORLD_SOURCE_SYMLINK),
|
|
|
|
);
|
|
|
|
#[cfg(windows)]
|
|
|
|
let _r = symlink_file(
|
|
|
|
TEST_HELLO_WORLD_SOURCE,
|
|
|
|
at.subdir.join(TEST_HELLO_WORLD_SOURCE_SYMLINK),
|
|
|
|
);
|
|
|
|
//using -L option
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg("-L")
|
2020-11-11 20:41:02 +00:00
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE_SYMLINK)
|
|
|
|
.arg(TEST_COPY_TO_FOLDER)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2020-11-11 20:41:02 +00:00
|
|
|
|
|
|
|
let path_to_new_symlink = at
|
|
|
|
.subdir
|
|
|
|
.join(TEST_COPY_TO_FOLDER)
|
|
|
|
.join(TEST_HELLO_WORLD_SOURCE_SYMLINK);
|
|
|
|
// unlike -P/--no-deref, we expect a file, not a link
|
|
|
|
assert!(at.file_exists(
|
|
|
|
&path_to_new_symlink
|
|
|
|
.clone()
|
|
|
|
.into_os_string()
|
|
|
|
.into_string()
|
|
|
|
.unwrap()
|
|
|
|
));
|
|
|
|
// Check the content of the destination file that was copied.
|
|
|
|
assert_eq!(at.read(TEST_COPY_TO_FOLDER_FILE), "Hello, World!\n");
|
|
|
|
let path_to_check = path_to_new_symlink.to_str().unwrap();
|
2021-06-06 19:13:54 +00:00
|
|
|
assert_eq!(at.read(path_to_check), "Hello, World!\n");
|
2020-11-11 20:41:02 +00:00
|
|
|
}
|
2020-05-26 19:35:30 +00:00
|
|
|
#[test]
|
|
|
|
fn test_cp_no_deref() {
|
2021-04-22 20:37:44 +00:00
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
2020-05-26 19:35:30 +00:00
|
|
|
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
let _r = fs::symlink(
|
|
|
|
TEST_HELLO_WORLD_SOURCE,
|
|
|
|
at.subdir.join(TEST_HELLO_WORLD_SOURCE_SYMLINK),
|
|
|
|
);
|
|
|
|
#[cfg(windows)]
|
|
|
|
let _r = symlink_file(
|
|
|
|
TEST_HELLO_WORLD_SOURCE,
|
|
|
|
at.subdir.join(TEST_HELLO_WORLD_SOURCE_SYMLINK),
|
|
|
|
);
|
2020-05-28 19:54:03 +00:00
|
|
|
//using -P option
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg("-P")
|
2020-05-26 19:35:30 +00:00
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE_SYMLINK)
|
|
|
|
.arg(TEST_COPY_TO_FOLDER)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2020-05-26 19:35:30 +00:00
|
|
|
|
|
|
|
let path_to_new_symlink = at
|
|
|
|
.subdir
|
|
|
|
.join(TEST_COPY_TO_FOLDER)
|
|
|
|
.join(TEST_HELLO_WORLD_SOURCE_SYMLINK);
|
|
|
|
assert!(at.is_symlink(
|
|
|
|
&path_to_new_symlink
|
|
|
|
.clone()
|
|
|
|
.into_os_string()
|
|
|
|
.into_string()
|
|
|
|
.unwrap()
|
|
|
|
));
|
|
|
|
// Check the content of the destination file that was copied.
|
|
|
|
assert_eq!(at.read(TEST_COPY_TO_FOLDER_FILE), "Hello, World!\n");
|
|
|
|
let path_to_check = path_to_new_symlink.to_str().unwrap();
|
2021-06-06 19:13:54 +00:00
|
|
|
assert_eq!(at.read(path_to_check), "Hello, World!\n");
|
2020-05-26 19:35:30 +00:00
|
|
|
}
|
2020-05-28 19:54:03 +00:00
|
|
|
|
2022-02-09 01:37:55 +00:00
|
|
|
#[test]
|
|
|
|
fn test_cp_no_deref_link_onto_link() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
at.copy(TEST_HELLO_WORLD_SOURCE, TEST_HELLO_WORLD_DEST);
|
|
|
|
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
let _r = fs::symlink(
|
|
|
|
TEST_HELLO_WORLD_SOURCE,
|
|
|
|
at.subdir.join(TEST_HELLO_WORLD_SOURCE_SYMLINK),
|
|
|
|
);
|
|
|
|
#[cfg(windows)]
|
|
|
|
let _r = symlink_file(
|
|
|
|
TEST_HELLO_WORLD_SOURCE,
|
|
|
|
at.subdir.join(TEST_HELLO_WORLD_SOURCE_SYMLINK),
|
|
|
|
);
|
|
|
|
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
let _r = fs::symlink(
|
|
|
|
TEST_HELLO_WORLD_DEST,
|
|
|
|
at.subdir.join(TEST_HELLO_WORLD_DEST_SYMLINK),
|
|
|
|
);
|
|
|
|
#[cfg(windows)]
|
|
|
|
let _r = symlink_file(
|
|
|
|
TEST_HELLO_WORLD_DEST,
|
|
|
|
at.subdir.join(TEST_HELLO_WORLD_DEST_SYMLINK),
|
|
|
|
);
|
|
|
|
|
|
|
|
ucmd.arg("-P")
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE_SYMLINK)
|
|
|
|
.arg(TEST_HELLO_WORLD_DEST_SYMLINK)
|
|
|
|
.succeeds();
|
|
|
|
|
|
|
|
// Ensure that the target of the destination was not modified.
|
|
|
|
assert!(!at
|
|
|
|
.symlink_metadata(TEST_HELLO_WORLD_DEST)
|
|
|
|
.file_type()
|
|
|
|
.is_symlink());
|
|
|
|
assert!(at
|
|
|
|
.symlink_metadata(TEST_HELLO_WORLD_DEST_SYMLINK)
|
|
|
|
.file_type()
|
|
|
|
.is_symlink());
|
|
|
|
assert_eq!(at.read(TEST_HELLO_WORLD_DEST_SYMLINK), "Hello, World!\n");
|
|
|
|
}
|
|
|
|
|
2021-03-09 10:59:26 +00:00
|
|
|
#[test]
|
|
|
|
fn test_cp_strip_trailing_slashes() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
//using --strip-trailing-slashes option
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg("--strip-trailing-slashes")
|
2021-03-09 10:59:26 +00:00
|
|
|
.arg(format!("{}/", TEST_HELLO_WORLD_SOURCE))
|
|
|
|
.arg(TEST_HELLO_WORLD_DEST)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2021-03-09 10:59:26 +00:00
|
|
|
|
|
|
|
// Check the content of the destination file that was copied.
|
|
|
|
assert_eq!(at.read(TEST_HELLO_WORLD_DEST), "Hello, World!\n");
|
|
|
|
}
|
|
|
|
|
2021-03-12 12:25:15 +00:00
|
|
|
#[test]
|
|
|
|
fn test_cp_parents() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg("--parents")
|
2021-03-12 12:25:15 +00:00
|
|
|
.arg(TEST_COPY_FROM_FOLDER_FILE)
|
|
|
|
.arg(TEST_COPY_TO_FOLDER)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2021-03-12 12:25:15 +00:00
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
at.read(&format!(
|
|
|
|
"{}/{}",
|
|
|
|
TEST_COPY_TO_FOLDER, TEST_COPY_FROM_FOLDER_FILE
|
|
|
|
)),
|
|
|
|
"Hello, World!\n"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_parents_multiple_files() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg("--parents")
|
2021-03-12 12:25:15 +00:00
|
|
|
.arg(TEST_COPY_FROM_FOLDER_FILE)
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
|
|
|
.arg(TEST_COPY_TO_FOLDER)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2021-03-12 12:25:15 +00:00
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
at.read(&format!(
|
|
|
|
"{}/{}",
|
|
|
|
TEST_COPY_TO_FOLDER, TEST_COPY_FROM_FOLDER_FILE
|
|
|
|
)),
|
|
|
|
"Hello, World!\n"
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
at.read(&format!(
|
|
|
|
"{}/{}",
|
|
|
|
TEST_COPY_TO_FOLDER, TEST_HOW_ARE_YOU_SOURCE
|
|
|
|
)),
|
|
|
|
"How are you?\n"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_parents_dest_not_directory() {
|
2021-04-22 20:37:44 +00:00
|
|
|
new_ucmd!()
|
2021-03-12 12:25:15 +00:00
|
|
|
.arg("--parents")
|
|
|
|
.arg(TEST_COPY_FROM_FOLDER_FILE)
|
|
|
|
.arg(TEST_HELLO_WORLD_DEST)
|
2021-04-22 20:37:44 +00:00
|
|
|
.fails()
|
|
|
|
.stderr_contains("with --parents, the destination must be a directory");
|
2021-03-12 12:25:15 +00:00
|
|
|
}
|
|
|
|
|
2021-12-15 21:18:02 +00:00
|
|
|
#[test]
|
|
|
|
#[cfg(unix)]
|
|
|
|
fn test_cp_writable_special_file_permissions() {
|
|
|
|
new_ucmd!().arg("/dev/null").arg("/dev/zero").succeeds();
|
|
|
|
}
|
|
|
|
|
2021-12-19 17:19:48 +00:00
|
|
|
#[test]
|
|
|
|
#[cfg(unix)]
|
|
|
|
fn test_cp_issue_1665() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
ucmd.arg("/dev/null").arg("foo").succeeds();
|
|
|
|
assert!(at.file_exists("foo"));
|
|
|
|
assert_eq!(at.read("foo"), "");
|
|
|
|
}
|
|
|
|
|
2021-06-16 03:32:15 +00:00
|
|
|
#[test]
|
|
|
|
fn test_cp_preserve_no_args() {
|
|
|
|
new_ucmd!()
|
|
|
|
.arg(TEST_COPY_FROM_FOLDER_FILE)
|
|
|
|
.arg(TEST_HELLO_WORLD_DEST)
|
|
|
|
.arg("--preserve")
|
|
|
|
.succeeds();
|
|
|
|
}
|
|
|
|
|
2020-11-11 20:41:02 +00:00
|
|
|
#[test]
|
|
|
|
// For now, disable the test on Windows. Symlinks aren't well support on Windows.
|
|
|
|
// It works on Unix for now and it works locally when run from a powershell
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
fn test_cp_deref_folder_to_folder() {
|
|
|
|
let scene = TestScenario::new(util_name!());
|
|
|
|
let at = &scene.fixtures;
|
|
|
|
|
2021-08-12 20:42:40 +00:00
|
|
|
let path_to_new_symlink = at.plus(TEST_COPY_FROM_FOLDER);
|
2020-11-11 20:41:02 +00:00
|
|
|
|
2021-08-12 20:42:40 +00:00
|
|
|
at.symlink_file(
|
|
|
|
&path_to_new_symlink
|
|
|
|
.join(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.to_string_lossy(),
|
|
|
|
&path_to_new_symlink
|
|
|
|
.join(TEST_HELLO_WORLD_SOURCE_SYMLINK)
|
|
|
|
.to_string_lossy(),
|
|
|
|
);
|
2020-11-11 20:41:02 +00:00
|
|
|
|
|
|
|
//using -P -R option
|
2021-04-22 20:37:44 +00:00
|
|
|
scene
|
2020-11-11 20:41:02 +00:00
|
|
|
.ucmd()
|
|
|
|
.arg("-L")
|
|
|
|
.arg("-R")
|
|
|
|
.arg("-v")
|
|
|
|
.arg(TEST_COPY_FROM_FOLDER)
|
|
|
|
.arg(TEST_COPY_TO_FOLDER_NEW)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2020-11-11 20:41:02 +00:00
|
|
|
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
{
|
|
|
|
let scene2 = TestScenario::new("ls");
|
|
|
|
let result = scene2.cmd("ls").arg("-al").arg(path_to_new_symlink).run();
|
2021-04-05 20:03:43 +00:00
|
|
|
println!("ls source {}", result.stdout_str());
|
2020-11-11 20:41:02 +00:00
|
|
|
|
|
|
|
let path_to_new_symlink = at.subdir.join(TEST_COPY_TO_FOLDER_NEW);
|
|
|
|
|
|
|
|
let result = scene2.cmd("ls").arg("-al").arg(path_to_new_symlink).run();
|
2021-04-05 20:03:43 +00:00
|
|
|
println!("ls dest {}", result.stdout_str());
|
2020-11-11 20:41:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
{
|
|
|
|
// No action as this test is disabled but kept in case we want to
|
|
|
|
// try to make it work in the future.
|
|
|
|
let a = Command::new("cmd").args(&["/C", "dir"]).output();
|
|
|
|
println!("output {:#?}", a);
|
|
|
|
|
|
|
|
let a = Command::new("cmd")
|
|
|
|
.args(&["/C", "dir", &at.as_string()])
|
|
|
|
.output();
|
|
|
|
println!("output {:#?}", a);
|
|
|
|
|
|
|
|
let a = Command::new("cmd")
|
|
|
|
.args(&["/C", "dir", path_to_new_symlink.to_str().unwrap()])
|
|
|
|
.output();
|
|
|
|
println!("output {:#?}", a);
|
|
|
|
|
|
|
|
let path_to_new_symlink = at.subdir.join(TEST_COPY_FROM_FOLDER);
|
|
|
|
|
|
|
|
let a = Command::new("cmd")
|
|
|
|
.args(&["/C", "dir", path_to_new_symlink.to_str().unwrap()])
|
|
|
|
.output();
|
|
|
|
println!("output {:#?}", a);
|
|
|
|
|
|
|
|
let path_to_new_symlink = at.subdir.join(TEST_COPY_TO_FOLDER_NEW);
|
|
|
|
|
|
|
|
let a = Command::new("cmd")
|
|
|
|
.args(&["/C", "dir", path_to_new_symlink.to_str().unwrap()])
|
|
|
|
.output();
|
|
|
|
println!("output {:#?}", a);
|
|
|
|
}
|
|
|
|
|
|
|
|
let path_to_new_symlink = at
|
|
|
|
.subdir
|
|
|
|
.join(TEST_COPY_TO_FOLDER_NEW)
|
|
|
|
.join(TEST_HELLO_WORLD_SOURCE_SYMLINK);
|
|
|
|
assert!(at.file_exists(
|
|
|
|
&path_to_new_symlink
|
|
|
|
.clone()
|
|
|
|
.into_os_string()
|
|
|
|
.into_string()
|
|
|
|
.unwrap()
|
|
|
|
));
|
|
|
|
|
|
|
|
let path_to_new = at.subdir.join(TEST_COPY_TO_FOLDER_NEW_FILE);
|
|
|
|
|
|
|
|
// Check the content of the destination file that was copied.
|
|
|
|
let path_to_check = path_to_new.to_str().unwrap();
|
|
|
|
assert_eq!(at.read(path_to_check), "Hello, World!\n");
|
|
|
|
|
|
|
|
// Check the content of the symlink
|
|
|
|
let path_to_check = path_to_new_symlink.to_str().unwrap();
|
2021-06-06 19:13:54 +00:00
|
|
|
assert_eq!(at.read(path_to_check), "Hello, World!\n");
|
2020-11-11 20:41:02 +00:00
|
|
|
}
|
|
|
|
|
2020-05-28 19:54:03 +00:00
|
|
|
#[test]
|
|
|
|
// For now, disable the test on Windows. Symlinks aren't well support on Windows.
|
|
|
|
// It works on Unix for now and it works locally when run from a powershell
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
fn test_cp_no_deref_folder_to_folder() {
|
|
|
|
let scene = TestScenario::new(util_name!());
|
|
|
|
let at = &scene.fixtures;
|
|
|
|
|
2021-08-12 20:42:40 +00:00
|
|
|
let path_to_new_symlink = at.plus(TEST_COPY_FROM_FOLDER);
|
2020-05-28 19:54:03 +00:00
|
|
|
|
2021-08-12 20:42:40 +00:00
|
|
|
at.symlink_file(
|
|
|
|
&path_to_new_symlink
|
|
|
|
.join(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.to_string_lossy(),
|
|
|
|
&path_to_new_symlink
|
|
|
|
.join(TEST_HELLO_WORLD_SOURCE_SYMLINK)
|
|
|
|
.to_string_lossy(),
|
|
|
|
);
|
2020-05-28 19:54:03 +00:00
|
|
|
|
|
|
|
//using -P -R option
|
2021-04-22 20:37:44 +00:00
|
|
|
scene
|
2020-05-28 19:54:03 +00:00
|
|
|
.ucmd()
|
|
|
|
.arg("-P")
|
|
|
|
.arg("-R")
|
|
|
|
.arg("-v")
|
|
|
|
.arg(TEST_COPY_FROM_FOLDER)
|
|
|
|
.arg(TEST_COPY_TO_FOLDER_NEW)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2020-05-28 19:54:03 +00:00
|
|
|
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
{
|
|
|
|
let scene2 = TestScenario::new("ls");
|
|
|
|
let result = scene2.cmd("ls").arg("-al").arg(path_to_new_symlink).run();
|
2021-04-05 20:03:43 +00:00
|
|
|
println!("ls source {}", result.stdout_str());
|
2020-05-28 19:54:03 +00:00
|
|
|
|
|
|
|
let path_to_new_symlink = at.subdir.join(TEST_COPY_TO_FOLDER_NEW);
|
|
|
|
|
|
|
|
let result = scene2.cmd("ls").arg("-al").arg(path_to_new_symlink).run();
|
2021-04-05 20:03:43 +00:00
|
|
|
println!("ls dest {}", result.stdout_str());
|
2020-05-28 19:54:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
{
|
|
|
|
// No action as this test is disabled but kept in case we want to
|
|
|
|
// try to make it work in the future.
|
|
|
|
let a = Command::new("cmd").args(&["/C", "dir"]).output();
|
|
|
|
println!("output {:#?}", a);
|
|
|
|
|
|
|
|
let a = Command::new("cmd")
|
|
|
|
.args(&["/C", "dir", &at.as_string()])
|
|
|
|
.output();
|
|
|
|
println!("output {:#?}", a);
|
|
|
|
|
|
|
|
let a = Command::new("cmd")
|
|
|
|
.args(&["/C", "dir", path_to_new_symlink.to_str().unwrap()])
|
|
|
|
.output();
|
|
|
|
println!("output {:#?}", a);
|
|
|
|
|
|
|
|
let path_to_new_symlink = at.subdir.join(TEST_COPY_FROM_FOLDER);
|
|
|
|
|
|
|
|
let a = Command::new("cmd")
|
|
|
|
.args(&["/C", "dir", path_to_new_symlink.to_str().unwrap()])
|
|
|
|
.output();
|
|
|
|
println!("output {:#?}", a);
|
|
|
|
|
|
|
|
let path_to_new_symlink = at.subdir.join(TEST_COPY_TO_FOLDER_NEW);
|
|
|
|
|
|
|
|
let a = Command::new("cmd")
|
|
|
|
.args(&["/C", "dir", path_to_new_symlink.to_str().unwrap()])
|
|
|
|
.output();
|
|
|
|
println!("output {:#?}", a);
|
|
|
|
}
|
|
|
|
|
|
|
|
let path_to_new_symlink = at
|
|
|
|
.subdir
|
|
|
|
.join(TEST_COPY_TO_FOLDER_NEW)
|
|
|
|
.join(TEST_HELLO_WORLD_SOURCE_SYMLINK);
|
|
|
|
assert!(at.is_symlink(
|
|
|
|
&path_to_new_symlink
|
|
|
|
.clone()
|
|
|
|
.into_os_string()
|
|
|
|
.into_string()
|
|
|
|
.unwrap()
|
|
|
|
));
|
|
|
|
|
|
|
|
let path_to_new = at.subdir.join(TEST_COPY_TO_FOLDER_NEW_FILE);
|
|
|
|
|
|
|
|
// Check the content of the destination file that was copied.
|
|
|
|
let path_to_check = path_to_new.to_str().unwrap();
|
|
|
|
assert_eq!(at.read(path_to_check), "Hello, World!\n");
|
|
|
|
|
|
|
|
// Check the content of the symlink
|
|
|
|
let path_to_check = path_to_new_symlink.to_str().unwrap();
|
2021-06-06 19:13:54 +00:00
|
|
|
assert_eq!(at.read(path_to_check), "Hello, World!\n");
|
2020-05-28 19:54:03 +00:00
|
|
|
}
|
2020-08-04 21:15:33 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
fn test_cp_archive() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
2022-04-23 19:32:35 +00:00
|
|
|
let ts = time::OffsetDateTime::now_local().unwrap();
|
|
|
|
let previous = FileTime::from_unix_time(ts.unix_timestamp() - 3600, ts.nanosecond() as u32);
|
2021-05-30 05:10:54 +00:00
|
|
|
// set the file creation/modification an hour ago
|
2020-08-04 21:15:33 +00:00
|
|
|
filetime::set_file_times(
|
|
|
|
at.plus_as_string(TEST_HELLO_WORLD_SOURCE),
|
|
|
|
previous,
|
|
|
|
previous,
|
|
|
|
)
|
|
|
|
.unwrap();
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
|
2020-08-04 21:15:33 +00:00
|
|
|
.arg("--archive")
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2020-08-04 21:15:33 +00:00
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
|
|
|
|
|
|
|
|
let metadata = std_fs::metadata(at.subdir.join(TEST_HELLO_WORLD_SOURCE)).unwrap();
|
|
|
|
let creation = metadata.modified().unwrap();
|
|
|
|
|
|
|
|
let metadata2 = std_fs::metadata(at.subdir.join(TEST_HOW_ARE_YOU_SOURCE)).unwrap();
|
|
|
|
let creation2 = metadata2.modified().unwrap();
|
|
|
|
|
|
|
|
let scene2 = TestScenario::new("ls");
|
2021-04-22 20:37:44 +00:00
|
|
|
let result = scene2.cmd("ls").arg("-al").arg(at.subdir).succeeds();
|
2020-08-04 21:15:33 +00:00
|
|
|
|
2021-04-05 20:03:43 +00:00
|
|
|
println!("ls dest {}", result.stdout_str());
|
2020-08-04 21:15:33 +00:00
|
|
|
assert_eq!(creation, creation2);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-02-09 18:08:28 +00:00
|
|
|
#[cfg(all(unix, not(target_os = "android")))]
|
2020-08-04 21:15:33 +00:00
|
|
|
fn test_cp_archive_recursive() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
// creates
|
|
|
|
// dir/1
|
|
|
|
// dir/1.link => dir/1
|
|
|
|
// dir/2
|
|
|
|
// dir/2.link => dir/2
|
|
|
|
|
|
|
|
let file_1 = at.subdir.join(TEST_COPY_TO_FOLDER).join("1");
|
|
|
|
let file_1_link = at.subdir.join(TEST_COPY_TO_FOLDER).join("1.link");
|
|
|
|
let file_2 = at.subdir.join(TEST_COPY_TO_FOLDER).join("2");
|
|
|
|
let file_2_link = at.subdir.join(TEST_COPY_TO_FOLDER).join("2.link");
|
|
|
|
|
|
|
|
at.touch(&file_1.to_string_lossy());
|
|
|
|
at.touch(&file_2.to_string_lossy());
|
|
|
|
|
2021-08-12 20:42:40 +00:00
|
|
|
at.symlink_file("1", &file_1_link.to_string_lossy());
|
|
|
|
at.symlink_file("2", &file_2_link.to_string_lossy());
|
2020-08-04 21:15:33 +00:00
|
|
|
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg("--archive")
|
2020-08-04 21:15:33 +00:00
|
|
|
.arg(TEST_COPY_TO_FOLDER)
|
|
|
|
.arg(TEST_COPY_TO_FOLDER_NEW)
|
2021-08-12 20:42:40 +00:00
|
|
|
.succeeds();
|
2020-08-04 21:15:33 +00:00
|
|
|
|
|
|
|
let scene2 = TestScenario::new("ls");
|
|
|
|
let result = scene2
|
|
|
|
.cmd("ls")
|
|
|
|
.arg("-al")
|
|
|
|
.arg(&at.subdir.join(TEST_COPY_TO_FOLDER))
|
|
|
|
.run();
|
|
|
|
|
2021-04-05 20:03:43 +00:00
|
|
|
println!("ls dest {}", result.stdout_str());
|
2020-08-04 21:15:33 +00:00
|
|
|
|
|
|
|
let result = scene2
|
|
|
|
.cmd("ls")
|
|
|
|
.arg("-al")
|
|
|
|
.arg(&at.subdir.join(TEST_COPY_TO_FOLDER_NEW))
|
|
|
|
.run();
|
|
|
|
|
2021-04-05 20:03:43 +00:00
|
|
|
println!("ls dest {}", result.stdout_str());
|
2020-08-04 21:15:33 +00:00
|
|
|
assert!(at.file_exists(
|
|
|
|
&at.subdir
|
|
|
|
.join(TEST_COPY_TO_FOLDER_NEW)
|
|
|
|
.join("1")
|
|
|
|
.to_string_lossy()
|
|
|
|
));
|
|
|
|
assert!(at.file_exists(
|
|
|
|
&at.subdir
|
|
|
|
.join(TEST_COPY_TO_FOLDER_NEW)
|
|
|
|
.join("2")
|
|
|
|
.to_string_lossy()
|
|
|
|
));
|
|
|
|
|
|
|
|
assert!(at.is_symlink(
|
|
|
|
&at.subdir
|
|
|
|
.join(TEST_COPY_TO_FOLDER_NEW)
|
|
|
|
.join("1.link")
|
|
|
|
.to_string_lossy()
|
|
|
|
));
|
|
|
|
assert!(at.is_symlink(
|
|
|
|
&at.subdir
|
|
|
|
.join(TEST_COPY_TO_FOLDER_NEW)
|
|
|
|
.join("2.link")
|
|
|
|
.to_string_lossy()
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-02-09 18:08:28 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
2020-08-04 21:15:33 +00:00
|
|
|
fn test_cp_preserve_timestamps() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
2022-04-23 19:32:35 +00:00
|
|
|
let ts = time::OffsetDateTime::now_local().unwrap();
|
|
|
|
let previous = FileTime::from_unix_time(ts.unix_timestamp() - 3600, ts.nanosecond());
|
2021-05-30 05:10:54 +00:00
|
|
|
// set the file creation/modification an hour ago
|
2020-08-04 21:15:33 +00:00
|
|
|
filetime::set_file_times(
|
|
|
|
at.plus_as_string(TEST_HELLO_WORLD_SOURCE),
|
|
|
|
previous,
|
|
|
|
previous,
|
|
|
|
)
|
|
|
|
.unwrap();
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
|
2020-08-04 21:15:33 +00:00
|
|
|
.arg("--preserve=timestamps")
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2020-08-04 21:15:33 +00:00
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
|
|
|
|
|
|
|
|
let metadata = std_fs::metadata(at.subdir.join(TEST_HELLO_WORLD_SOURCE)).unwrap();
|
|
|
|
let creation = metadata.modified().unwrap();
|
|
|
|
|
|
|
|
let metadata2 = std_fs::metadata(at.subdir.join(TEST_HOW_ARE_YOU_SOURCE)).unwrap();
|
|
|
|
let creation2 = metadata2.modified().unwrap();
|
|
|
|
|
|
|
|
let scene2 = TestScenario::new("ls");
|
|
|
|
let result = scene2.cmd("ls").arg("-al").arg(at.subdir).run();
|
|
|
|
|
2021-04-05 20:03:43 +00:00
|
|
|
println!("ls dest {}", result.stdout_str());
|
2020-08-04 21:15:33 +00:00
|
|
|
assert_eq!(creation, creation2);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-02-09 18:08:28 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
2021-05-30 05:10:54 +00:00
|
|
|
fn test_cp_no_preserve_timestamps() {
|
2020-08-04 21:15:33 +00:00
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
2022-04-23 19:32:35 +00:00
|
|
|
let ts = time::OffsetDateTime::now_local().unwrap();
|
|
|
|
let previous = FileTime::from_unix_time(ts.unix_timestamp() - 3600, ts.nanosecond());
|
2021-05-30 05:10:54 +00:00
|
|
|
// set the file creation/modification an hour ago
|
2020-08-04 21:15:33 +00:00
|
|
|
filetime::set_file_times(
|
|
|
|
at.plus_as_string(TEST_HELLO_WORLD_SOURCE),
|
|
|
|
previous,
|
|
|
|
previous,
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
sleep(Duration::from_secs(3));
|
|
|
|
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
|
2020-08-04 21:15:33 +00:00
|
|
|
.arg("--no-preserve=timestamps")
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2020-08-04 21:15:33 +00:00
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
|
|
|
|
|
|
|
|
let metadata = std_fs::metadata(at.subdir.join(TEST_HELLO_WORLD_SOURCE)).unwrap();
|
|
|
|
let creation = metadata.modified().unwrap();
|
|
|
|
|
|
|
|
let metadata2 = std_fs::metadata(at.subdir.join(TEST_HOW_ARE_YOU_SOURCE)).unwrap();
|
|
|
|
let creation2 = metadata2.modified().unwrap();
|
|
|
|
|
|
|
|
let scene2 = TestScenario::new("ls");
|
|
|
|
let result = scene2.cmd("ls").arg("-al").arg(at.subdir).run();
|
|
|
|
|
2021-04-05 20:03:43 +00:00
|
|
|
println!("ls dest {}", result.stdout_str());
|
2020-08-04 21:15:33 +00:00
|
|
|
println!("creation {:?} / {:?}", creation, creation2);
|
|
|
|
|
|
|
|
assert_ne!(creation, creation2);
|
|
|
|
let res = creation.elapsed().unwrap() - creation2.elapsed().unwrap();
|
|
|
|
// Some margins with time check
|
|
|
|
assert!(res.as_secs() > 3595);
|
|
|
|
assert!(res.as_secs() < 3605);
|
|
|
|
}
|
2021-01-01 17:36:30 +00:00
|
|
|
|
|
|
|
#[test]
|
2022-02-09 18:08:28 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
2021-01-01 17:36:30 +00:00
|
|
|
fn test_cp_target_file_dev_null() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
let file1 = "/dev/null";
|
|
|
|
let file2 = "test_cp_target_file_file_i2";
|
|
|
|
|
|
|
|
at.touch(file2);
|
|
|
|
ucmd.arg(file1).arg(file2).succeeds().no_stderr();
|
|
|
|
|
|
|
|
assert!(at.file_exists(file2));
|
|
|
|
}
|
2021-03-19 20:15:35 +00:00
|
|
|
|
|
|
|
#[test]
|
2022-02-09 18:08:28 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))]
|
2021-03-19 20:15:35 +00:00
|
|
|
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`)
|
2021-04-22 20:37:44 +00:00
|
|
|
if scene.cmd("whoami").run().stdout_str() != "root\n" {
|
2021-03-19 20:15:35 +00:00
|
|
|
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);
|
|
|
|
|
2021-04-17 13:48:23 +00:00
|
|
|
scene
|
2021-03-19 20:15:35 +00:00
|
|
|
.cmd("mount")
|
|
|
|
.arg("-t")
|
|
|
|
.arg("tmpfs")
|
|
|
|
.arg("-o")
|
|
|
|
.arg("size=640k") // ought to be enough
|
|
|
|
.arg("tmpfs")
|
|
|
|
.arg(mountpoint_path)
|
2021-04-17 13:48:23 +00:00
|
|
|
.succeeds();
|
2021-03-19 20:15:35 +00:00
|
|
|
|
|
|
|
at_src.touch(TEST_MOUNT_OTHER_FILESYSTEM_FILE);
|
|
|
|
|
|
|
|
// Begin testing -x flag
|
2021-04-22 20:37:44 +00:00
|
|
|
scene
|
2021-03-19 20:15:35 +00:00
|
|
|
.ucmd()
|
|
|
|
.arg("-rx")
|
|
|
|
.arg(TEST_MOUNT_COPY_FROM_FOLDER)
|
|
|
|
.arg(TEST_COPY_TO_FOLDER_NEW)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2021-03-19 20:15:35 +00:00
|
|
|
|
|
|
|
// Ditch the mount before the asserts
|
2021-04-17 13:48:23 +00:00
|
|
|
scene.cmd("umount").arg(mountpoint_path).succeeds();
|
2021-03-19 20:15:35 +00:00
|
|
|
|
|
|
|
assert!(!at_dst.file_exists(TEST_MOUNT_OTHER_FILESYSTEM_FILE));
|
2021-05-30 05:10:54 +00:00
|
|
|
// Check if the other files were copied from the source folder hierarchy
|
2021-03-19 20:15:35 +00:00
|
|
|
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!(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-04-18 12:17:55 +00:00
|
|
|
|
|
|
|
#[test]
|
2022-02-09 18:08:28 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "android", target_os = "macos"))]
|
2021-04-18 12:17:55 +00:00
|
|
|
fn test_cp_reflink_always() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
let result = ucmd
|
|
|
|
.arg("--reflink=always")
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_EXISTING_FILE)
|
|
|
|
.run();
|
|
|
|
|
2021-04-24 10:45:55 +00:00
|
|
|
if result.succeeded() {
|
2021-04-18 12:17:55 +00:00
|
|
|
// Check the content of the destination file
|
|
|
|
assert_eq!(at.read(TEST_EXISTING_FILE), "Hello, World!\n");
|
|
|
|
} else {
|
|
|
|
// Older Linux versions do not support cloning.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-02-09 18:08:28 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "android", target_os = "macos"))]
|
2021-04-18 12:17:55 +00:00
|
|
|
fn test_cp_reflink_auto() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
2021-04-23 05:24:47 +00:00
|
|
|
ucmd.arg("--reflink=auto")
|
2021-04-18 12:17:55 +00:00
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_EXISTING_FILE)
|
2021-04-23 05:24:47 +00:00
|
|
|
.succeeds();
|
2021-04-18 12:17:55 +00:00
|
|
|
|
|
|
|
// Check the content of the destination file
|
|
|
|
assert_eq!(at.read(TEST_EXISTING_FILE), "Hello, World!\n");
|
|
|
|
}
|
|
|
|
|
2022-10-03 18:20:55 +00:00
|
|
|
#[test]
|
|
|
|
#[cfg(any(target_os = "linux", target_os = "android", target_os = "macos"))]
|
|
|
|
fn test_cp_reflink_none() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
let result = ucmd
|
|
|
|
.arg("--reflink")
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_EXISTING_FILE)
|
|
|
|
.run();
|
|
|
|
|
|
|
|
if result.succeeded() {
|
|
|
|
// Check the content of the destination file
|
|
|
|
assert_eq!(at.read(TEST_EXISTING_FILE), "Hello, World!\n");
|
|
|
|
} else {
|
|
|
|
// Older Linux versions do not support cloning.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-18 12:17:55 +00:00
|
|
|
#[test]
|
2022-02-09 18:08:28 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "android", target_os = "macos"))]
|
2021-04-18 12:17:55 +00:00
|
|
|
fn test_cp_reflink_never() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
2021-04-23 05:24:47 +00:00
|
|
|
ucmd.arg("--reflink=never")
|
2021-04-18 12:17:55 +00:00
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_EXISTING_FILE)
|
2021-04-23 05:24:47 +00:00
|
|
|
.succeeds();
|
2021-04-18 12:17:55 +00:00
|
|
|
|
|
|
|
// Check the content of the destination file
|
|
|
|
assert_eq!(at.read(TEST_EXISTING_FILE), "Hello, World!\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-02-09 18:08:28 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "android", target_os = "macos"))]
|
2021-04-18 12:17:55 +00:00
|
|
|
fn test_cp_reflink_bad() {
|
|
|
|
let (_, mut ucmd) = at_and_ucmd!();
|
2021-04-24 10:45:55 +00:00
|
|
|
let _result = ucmd
|
2021-04-18 12:17:55 +00:00
|
|
|
.arg("--reflink=bad")
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_EXISTING_FILE)
|
2021-04-23 05:24:47 +00:00
|
|
|
.fails()
|
2022-10-03 18:20:55 +00:00
|
|
|
.stderr_contains("error: \"bad\" isn't a valid value for '--reflink[=<WHEN>...]'");
|
2021-04-18 12:17:55 +00:00
|
|
|
}
|
2021-06-01 21:06:38 +00:00
|
|
|
|
|
|
|
#[test]
|
2022-02-09 18:08:28 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
2021-06-01 21:06:38 +00:00
|
|
|
fn test_cp_reflink_insufficient_permission() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
at.make_file("unreadable")
|
|
|
|
.set_permissions(PermissionsExt::from_mode(0o000))
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
ucmd.arg("-r")
|
|
|
|
.arg("--reflink=auto")
|
|
|
|
.arg("unreadable")
|
|
|
|
.arg(TEST_EXISTING_FILE)
|
|
|
|
.fails()
|
|
|
|
.stderr_only("cp: 'unreadable' -> 'existing_file.txt': Permission denied (os error 13)");
|
|
|
|
}
|
2021-06-02 16:08:09 +00:00
|
|
|
|
2022-02-09 18:08:28 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
2021-06-02 16:08:09 +00:00
|
|
|
#[test]
|
|
|
|
fn test_closes_file_descriptors() {
|
2022-09-07 16:55:32 +00:00
|
|
|
use procfs::process::Process;
|
|
|
|
let me = Process::myself().unwrap();
|
|
|
|
|
|
|
|
// The test suite runs in parallel, we have pipe, sockets
|
|
|
|
// opened by other tests.
|
|
|
|
// So, we take in account the various fd to increase the limit
|
|
|
|
let number_file_already_opened: u64 = me.fd_count().unwrap().try_into().unwrap();
|
|
|
|
let limit_fd: u64 = number_file_already_opened + 9;
|
|
|
|
|
|
|
|
// For debugging purposes:
|
2022-09-09 13:10:42 +00:00
|
|
|
#[cfg(not(target_os = "android"))]
|
2022-09-07 16:55:32 +00:00
|
|
|
for f in me.fd().unwrap() {
|
|
|
|
let fd = f.unwrap();
|
|
|
|
println!("{:?} {:?}", fd, fd.mode());
|
|
|
|
}
|
|
|
|
|
2021-06-02 16:08:09 +00:00
|
|
|
new_ucmd!()
|
|
|
|
.arg("-r")
|
|
|
|
.arg("--reflink=auto")
|
|
|
|
.arg("dir_with_10_files/")
|
|
|
|
.arg("dir_with_10_files_new/")
|
2022-09-07 16:55:32 +00:00
|
|
|
.with_limit(Resource::NOFILE, limit_fd, limit_fd)
|
2021-06-02 16:08:09 +00:00
|
|
|
.succeeds();
|
|
|
|
}
|
2021-06-17 20:26:13 +00:00
|
|
|
|
cp: Implement `--sparse` flag (#3766)
* cp: Refactor `reflink`/`sparse` handling to enable `--sparse` flag
`--sparse` and `--reflink` options have a lot of similarities:
- They have similar options (`always`, `never`, `auto`)
- Both need OS specific handling
- They can be mutually exclusive
Prior to this change, `sparse` was defined as `CopyMode`, but `reflink`
wasn't. Given the similarities, it makes sense to handle them similarly.
The idea behind this change is to move all OS specific file copy
handling in the `copy_on_write_*` functions. Those function then
dispatch to the correct logic depending on the arguments (at the moment,
the tuple `(reflink, sparse)`).
Also, move the handling of `--reflink=never` from `copy_file` to the
`copy_on_write_*` functions, at the cost of a bit of code duplication,
to allow `copy_on_write_*` to handle all cases (and later handle
`--reflink=never` with `--sparse`).
* cp: Implement `--sparse` flag
This begins to address #3362
At the moment, only the `--sparse=always` logic matches the requirement
form GNU cp info page, i.e. always make holes in destination when
possible.
Sparse copy is done by copying the source to the destination block by
block (blocks being of the destination's fs block size). If the block
only holds NUL bytes, we don't write to the destination.
About `--sparse=auto`: according to GNU cp info page, the destination
file will be made sparse if the source file is sparse as well. The next
step are likely to use `lseek` with `SEEK_HOLE` detect if the source
file has holes. Currently, this has the same behaviour as
`--sparse=never`. This `SEEK_HOLE` logic can also be applied to
`--sparse=always` to improve performance when copying sparse files.
About `--sparse=never`: from my understanding, it is not guaranteed that
Rust's `fs::copy` will always produce a file with no holes, as
["platform-specific behavior may change in the
future"](https://doc.rust-lang.org/std/fs/fn.copy.html#platform-specific-behavior)
About other platforms:
- `macos`: The solution may be to use `fcntl` command `F_PUNCHHOLE`.
- `windows`: I only see `FSCTL_SET_SPARSE`.
This should pass the following GNU tests:
- `tests/cp/sparse.sh`
- `tests/cp/sparse-2.sh`
- `tests/cp/sparse-extents.sh`
- `tests/cp/sparse-extents-2.sh`
`sparse-perf.sh` needs `--sparse=auto`, and in particular a way to skip
holes in the source file.
Co-authored-by: Sylvestre Ledru <sylvestre@debian.org>
2022-08-04 11:22:59 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
|
|
|
#[test]
|
|
|
|
fn test_cp_sparse_never_empty() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
const BUFFER_SIZE: usize = 4096 * 4;
|
|
|
|
let buf: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
|
|
|
|
|
|
|
|
at.make_file("src_file1");
|
|
|
|
at.write_bytes("src_file1", &buf);
|
|
|
|
|
|
|
|
ucmd.args(&["--sparse=never", "src_file1", "dst_file_non_sparse"])
|
|
|
|
.succeeds();
|
|
|
|
assert_eq!(at.read_bytes("dst_file_non_sparse"), buf);
|
|
|
|
assert_eq!(
|
|
|
|
at.metadata("dst_file_non_sparse").blocks() * 512,
|
|
|
|
buf.len() as u64
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
|
|
|
#[test]
|
|
|
|
fn test_cp_sparse_always_empty() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
const BUFFER_SIZE: usize = 4096 * 4;
|
|
|
|
let buf: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
|
|
|
|
|
|
|
|
at.make_file("src_file1");
|
|
|
|
at.write_bytes("src_file1", &buf);
|
|
|
|
|
|
|
|
ucmd.args(&["--sparse=always", "src_file1", "dst_file_sparse"])
|
|
|
|
.succeeds();
|
|
|
|
|
|
|
|
assert_eq!(at.read_bytes("dst_file_sparse"), buf);
|
|
|
|
assert_eq!(at.metadata("dst_file_sparse").blocks(), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
|
|
|
#[test]
|
|
|
|
fn test_cp_sparse_always_non_empty() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
const BUFFER_SIZE: usize = 4096 * 16 + 3;
|
|
|
|
let mut buf: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
|
|
|
|
let blocks_to_touch = [buf.len() / 3, 2 * (buf.len() / 3)];
|
|
|
|
|
|
|
|
for i in blocks_to_touch {
|
|
|
|
buf[i] = b'x';
|
|
|
|
}
|
|
|
|
|
|
|
|
at.make_file("src_file1");
|
|
|
|
at.write_bytes("src_file1", &buf);
|
|
|
|
|
|
|
|
ucmd.args(&["--sparse=always", "src_file1", "dst_file_sparse"])
|
|
|
|
.succeeds();
|
|
|
|
|
|
|
|
let touched_block_count =
|
|
|
|
blocks_to_touch.len() as u64 * at.metadata("dst_file_sparse").blksize() / 512;
|
|
|
|
|
|
|
|
assert_eq!(at.read_bytes("dst_file_sparse"), buf);
|
|
|
|
assert_eq!(at.metadata("dst_file_sparse").blocks(), touched_block_count);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
|
|
|
#[test]
|
|
|
|
fn test_cp_sparse_invalid_option() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
at.make_file("src_file1");
|
|
|
|
|
|
|
|
ucmd.args(&["--sparse=invalid", "src_file1", "dst_file"])
|
|
|
|
.fails();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
|
|
|
#[test]
|
|
|
|
fn test_cp_sparse_always_reflink_always() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
at.make_file("src_file1");
|
|
|
|
|
|
|
|
ucmd.args(&[
|
|
|
|
"--sparse=always",
|
|
|
|
"--reflink=always",
|
|
|
|
"src_file1",
|
|
|
|
"dst_file",
|
|
|
|
])
|
|
|
|
.fails();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
|
|
|
#[test]
|
|
|
|
fn test_cp_sparse_never_reflink_always() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
at.make_file("src_file1");
|
|
|
|
|
|
|
|
ucmd.args(&[
|
|
|
|
"--sparse=never",
|
|
|
|
"--reflink=always",
|
|
|
|
"src_file1",
|
|
|
|
"dst_file",
|
|
|
|
])
|
|
|
|
.fails();
|
|
|
|
}
|
|
|
|
|
2022-08-04 06:50:19 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
|
|
|
#[test]
|
|
|
|
fn test_cp_reflink_always_override() {
|
|
|
|
let scene = TestScenario::new(util_name!());
|
|
|
|
|
|
|
|
const DISK: &str = "disk.img";
|
|
|
|
const ROOTDIR: &str = "disk_root/";
|
|
|
|
const USERDIR: &str = "dir/";
|
|
|
|
const MOUNTPOINT: &str = "mountpoint/";
|
|
|
|
|
|
|
|
let src1_path: &str = &vec![MOUNTPOINT, USERDIR, "src1"].concat();
|
|
|
|
let src2_path: &str = &vec![MOUNTPOINT, USERDIR, "src2"].concat();
|
|
|
|
let dst_path: &str = &vec![MOUNTPOINT, USERDIR, "dst"].concat();
|
|
|
|
|
|
|
|
scene.fixtures.mkdir(ROOTDIR);
|
|
|
|
scene.fixtures.mkdir(&vec![ROOTDIR, USERDIR].concat());
|
|
|
|
|
|
|
|
// Setup:
|
|
|
|
// Because neither `mkfs.btrfs` not btrfs `mount` options allow us to have a mountpoint owned
|
|
|
|
// by a non-root user, we want the following directory structure:
|
|
|
|
//
|
|
|
|
// uid | path
|
|
|
|
// ---------------------------
|
|
|
|
// user | .
|
|
|
|
// root | └── mountpoint
|
|
|
|
// user | └── dir
|
|
|
|
// user | ├── src1
|
|
|
|
// user | └── src2
|
|
|
|
|
|
|
|
scene
|
|
|
|
.ccmd("truncate")
|
|
|
|
.args(&["-s", "128M", DISK])
|
|
|
|
.succeeds();
|
|
|
|
|
|
|
|
if !scene
|
|
|
|
.cmd_keepenv("env")
|
|
|
|
.args(&["mkfs.btrfs", "--rootdir", ROOTDIR, DISK])
|
|
|
|
.run()
|
|
|
|
.succeeded()
|
|
|
|
{
|
|
|
|
print!("Test skipped; couldn't make btrfs disk image");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
scene.fixtures.mkdir(MOUNTPOINT);
|
|
|
|
|
|
|
|
let mount = scene
|
|
|
|
.cmd_keepenv("sudo")
|
|
|
|
.args(&["-E", "--non-interactive", "mount", DISK, MOUNTPOINT])
|
|
|
|
.run();
|
|
|
|
|
|
|
|
if !mount.succeeded() {
|
|
|
|
print!("Test skipped; requires root user");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
scene.fixtures.make_file(src1_path);
|
|
|
|
scene.fixtures.write_bytes(src1_path, &[0x64; 8192]);
|
|
|
|
|
|
|
|
scene.fixtures.make_file(src2_path);
|
|
|
|
scene.fixtures.write(src2_path, "other data");
|
|
|
|
|
|
|
|
scene
|
|
|
|
.ucmd()
|
|
|
|
.args(&["--reflink=always", src1_path, dst_path])
|
|
|
|
.succeeds();
|
|
|
|
|
|
|
|
scene
|
|
|
|
.ucmd()
|
|
|
|
.args(&["--reflink=always", src2_path, dst_path])
|
|
|
|
.succeeds();
|
|
|
|
|
|
|
|
scene
|
|
|
|
.cmd_keepenv("sudo")
|
|
|
|
.args(&["-E", "--non-interactive", "umount", MOUNTPOINT])
|
|
|
|
.succeeds();
|
|
|
|
}
|
|
|
|
|
2021-06-17 20:26:13 +00:00
|
|
|
#[test]
|
|
|
|
fn test_copy_dir_symlink() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.mkdir("dir");
|
|
|
|
at.symlink_dir("dir", "dir-link");
|
|
|
|
ucmd.args(&["-r", "dir-link", "copy"]).succeeds();
|
|
|
|
assert_eq!(at.resolve_link("copy"), "dir");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-08-12 21:51:28 +00:00
|
|
|
#[cfg(not(target_os = "freebsd"))] // FIXME: fix this test for FreeBSD
|
2021-06-17 20:26:13 +00:00
|
|
|
fn test_copy_dir_with_symlinks() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.mkdir("dir");
|
|
|
|
at.make_file("dir/file");
|
|
|
|
|
|
|
|
TestScenario::new("ln")
|
|
|
|
.ucmd()
|
|
|
|
.arg("-sr")
|
|
|
|
.arg(at.subdir.join("dir/file"))
|
|
|
|
.arg(at.subdir.join("dir/file-link"))
|
|
|
|
.succeeds();
|
|
|
|
|
|
|
|
ucmd.args(&["-r", "dir", "copy"]).succeeds();
|
|
|
|
assert_eq!(at.resolve_link("copy/file-link"), "file");
|
|
|
|
}
|
2021-06-19 15:49:04 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
fn test_copy_symlink_force() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.touch("file");
|
|
|
|
at.symlink_file("file", "file-link");
|
|
|
|
at.touch("copy");
|
|
|
|
|
|
|
|
ucmd.args(&["file-link", "copy", "-f", "--no-dereference"])
|
|
|
|
.succeeds();
|
|
|
|
assert_eq!(at.resolve_link("copy"), "file");
|
|
|
|
}
|
2021-08-29 23:27:47 +00:00
|
|
|
|
|
|
|
#[test]
|
2021-09-05 16:51:47 +00:00
|
|
|
#[cfg(all(unix, not(target_os = "freebsd")))]
|
2021-08-29 23:27:47 +00:00
|
|
|
fn test_no_preserve_mode() {
|
|
|
|
use std::os::unix::prelude::MetadataExt;
|
|
|
|
|
|
|
|
use uucore::mode::get_umask;
|
|
|
|
|
|
|
|
const PERMS_ALL: u32 = 0o7777;
|
|
|
|
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.touch("file");
|
|
|
|
set_permissions(at.plus("file"), PermissionsExt::from_mode(PERMS_ALL)).unwrap();
|
|
|
|
ucmd.arg("file")
|
|
|
|
.arg("dest")
|
|
|
|
.succeeds()
|
|
|
|
.no_stderr()
|
|
|
|
.no_stdout();
|
|
|
|
let umask = get_umask();
|
|
|
|
// remove sticky bit, setuid and setgid bit; apply umask
|
|
|
|
let expected_perms = PERMS_ALL & !0o7000 & !umask;
|
|
|
|
assert_eq!(
|
|
|
|
at.plus("dest").metadata().unwrap().mode() & 0o7777,
|
|
|
|
expected_perms
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-09-05 16:51:47 +00:00
|
|
|
#[cfg(all(unix, not(target_os = "freebsd")))]
|
2021-08-29 23:27:47 +00:00
|
|
|
fn test_preserve_mode() {
|
|
|
|
use std::os::unix::prelude::MetadataExt;
|
|
|
|
|
|
|
|
const PERMS_ALL: u32 = 0o7777;
|
|
|
|
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.touch("file");
|
|
|
|
set_permissions(at.plus("file"), PermissionsExt::from_mode(PERMS_ALL)).unwrap();
|
|
|
|
ucmd.arg("file")
|
|
|
|
.arg("dest")
|
|
|
|
.arg("-p")
|
|
|
|
.succeeds()
|
|
|
|
.no_stderr()
|
|
|
|
.no_stdout();
|
|
|
|
assert_eq!(
|
|
|
|
at.plus("dest").metadata().unwrap().mode() & 0o7777,
|
|
|
|
PERMS_ALL
|
|
|
|
);
|
|
|
|
}
|
2021-09-03 12:56:35 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_canonicalize_symlink() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.mkdir("dir");
|
|
|
|
at.touch("dir/file");
|
2022-09-23 17:31:24 +00:00
|
|
|
at.relative_symlink_file("../dir/file", "dir/file-ln");
|
2021-09-03 12:56:35 +00:00
|
|
|
ucmd.arg("dir/file-ln")
|
|
|
|
.arg(".")
|
|
|
|
.succeeds()
|
|
|
|
.no_stderr()
|
|
|
|
.no_stdout();
|
|
|
|
}
|
2021-08-27 16:40:42 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_copy_through_just_created_symlink() {
|
2022-04-02 08:47:37 +00:00
|
|
|
for create_t in [true, false] {
|
2021-08-27 16:40:42 +00:00
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.mkdir("a");
|
|
|
|
at.mkdir("b");
|
|
|
|
at.mkdir("c");
|
2022-07-10 14:49:25 +00:00
|
|
|
at.relative_symlink_file("../t", "a/1");
|
2021-08-27 16:40:42 +00:00
|
|
|
at.touch("b/1");
|
2022-07-10 14:49:25 +00:00
|
|
|
at.write("b/1", "hello");
|
2021-08-27 16:40:42 +00:00
|
|
|
if create_t {
|
|
|
|
at.touch("t");
|
2022-07-10 14:49:25 +00:00
|
|
|
at.write("t", "world");
|
2021-08-27 16:40:42 +00:00
|
|
|
}
|
|
|
|
ucmd.arg("--no-dereference")
|
|
|
|
.arg("a/1")
|
|
|
|
.arg("b/1")
|
|
|
|
.arg("c")
|
|
|
|
.fails()
|
|
|
|
.stderr_only(if cfg!(not(target_os = "windows")) {
|
|
|
|
"cp: will not copy 'b/1' through just-created symlink 'c/1'"
|
|
|
|
} else {
|
|
|
|
"cp: will not copy 'b/1' through just-created symlink 'c\\1'"
|
|
|
|
});
|
2022-07-10 14:49:25 +00:00
|
|
|
if create_t {
|
|
|
|
assert_eq!(at.read("a/1"), "world");
|
|
|
|
}
|
2021-08-27 16:40:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_copy_through_dangling_symlink() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.touch("file");
|
|
|
|
at.symlink_file("nonexistent", "target");
|
|
|
|
ucmd.arg("file")
|
|
|
|
.arg("target")
|
|
|
|
.fails()
|
|
|
|
.stderr_only("cp: not writing through dangling symlink 'target'");
|
|
|
|
}
|
2022-01-30 06:59:53 +00:00
|
|
|
|
2022-05-02 06:20:46 +00:00
|
|
|
#[test]
|
|
|
|
fn test_copy_through_dangling_symlink_no_dereference() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.symlink_file("no-such-file", "dangle");
|
|
|
|
ucmd.arg("-P")
|
|
|
|
.arg("dangle")
|
|
|
|
.arg("d2")
|
|
|
|
.succeeds()
|
|
|
|
.no_stderr()
|
|
|
|
.no_stdout();
|
|
|
|
}
|
|
|
|
|
2022-07-03 17:01:23 +00:00
|
|
|
/// Test for copying a dangling symbolic link and its permissions.
|
2022-08-12 21:51:28 +00:00
|
|
|
#[cfg(not(target_os = "freebsd"))] // FIXME: fix this test for FreeBSD
|
2022-07-03 17:01:23 +00:00
|
|
|
#[test]
|
|
|
|
fn test_copy_through_dangling_symlink_no_dereference_permissions() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
// target name link name
|
|
|
|
at.symlink_file("no-such-file", "dangle");
|
2022-08-08 09:11:17 +00:00
|
|
|
// to check if access time and modification time didn't change
|
|
|
|
sleep(Duration::from_millis(5000));
|
2022-07-03 17:01:23 +00:00
|
|
|
// don't dereference the link
|
|
|
|
// | copy permissions, too
|
|
|
|
// | | from the link
|
|
|
|
// | | | to new file d2
|
|
|
|
// | | | |
|
|
|
|
// V V V V
|
|
|
|
ucmd.args(&["-P", "-p", "dangle", "d2"])
|
|
|
|
.succeeds()
|
|
|
|
.no_stderr()
|
|
|
|
.no_stdout();
|
2022-08-08 09:11:17 +00:00
|
|
|
assert!(at.symlink_exists("d2"), "symlink wasn't created");
|
2022-07-03 17:01:23 +00:00
|
|
|
|
|
|
|
// `-p` means `--preserve=mode,ownership,timestamps`
|
|
|
|
#[cfg(unix)]
|
|
|
|
{
|
|
|
|
let metadata1 = at.symlink_metadata("dangle");
|
|
|
|
let metadata2 = at.symlink_metadata("d2");
|
2022-08-08 09:11:17 +00:00
|
|
|
assert_eq!(metadata1.mode(), metadata2.mode(), "mode is different");
|
|
|
|
assert_eq!(metadata1.uid(), metadata2.uid(), "uid is different");
|
|
|
|
assert_eq!(metadata1.atime(), metadata2.atime(), "atime is different");
|
|
|
|
assert_eq!(
|
|
|
|
metadata1.atime_nsec(),
|
|
|
|
metadata2.atime_nsec(),
|
|
|
|
"atime_nsec is different"
|
|
|
|
);
|
|
|
|
assert_eq!(metadata1.mtime(), metadata2.mtime(), "mtime is different");
|
|
|
|
assert_eq!(
|
|
|
|
metadata1.mtime_nsec(),
|
|
|
|
metadata2.mtime_nsec(),
|
|
|
|
"mtime_nsec is different"
|
|
|
|
);
|
2022-07-03 17:01:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-10 14:49:25 +00:00
|
|
|
#[test]
|
|
|
|
fn test_copy_through_dangling_symlink_no_dereference_2() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.touch("file");
|
|
|
|
at.symlink_file("nonexistent", "target");
|
|
|
|
ucmd.args(&["-P", "file", "target"])
|
|
|
|
.fails()
|
|
|
|
.stderr_only("cp: not writing through dangling symlink 'target'");
|
|
|
|
}
|
|
|
|
|
2022-01-30 06:59:53 +00:00
|
|
|
#[test]
|
|
|
|
#[cfg(unix)]
|
|
|
|
fn test_cp_archive_on_nonexistent_file() {
|
|
|
|
new_ucmd!()
|
|
|
|
.arg("-a")
|
|
|
|
.arg(TEST_NONEXISTENT_FILE)
|
|
|
|
.arg(TEST_EXISTING_FILE)
|
|
|
|
.fails()
|
2022-01-30 08:33:06 +00:00
|
|
|
.stderr_only(
|
|
|
|
"cp: cannot stat 'nonexistent_file.txt': No such file or directory (os error 2)",
|
|
|
|
);
|
2022-01-30 06:59:53 +00:00
|
|
|
}
|
2022-02-02 06:47:30 +00:00
|
|
|
|
|
|
|
#[test]
|
2022-02-09 18:08:28 +00:00
|
|
|
#[cfg(not(target_os = "android"))]
|
2022-02-02 06:47:30 +00:00
|
|
|
fn test_cp_link_backup() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.touch("file2");
|
|
|
|
ucmd.arg("-l")
|
|
|
|
.arg("-b")
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg("file2")
|
|
|
|
.succeeds();
|
|
|
|
|
|
|
|
assert!(at.file_exists("file2~"));
|
|
|
|
assert_eq!(at.read("file2"), "Hello, World!\n");
|
|
|
|
}
|
2022-02-20 09:31:13 +00:00
|
|
|
|
2022-03-03 21:58:27 +00:00
|
|
|
#[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"));
|
|
|
|
}
|
|
|
|
|
2022-02-01 12:07:04 +00:00
|
|
|
#[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");
|
|
|
|
}
|
2022-02-07 17:56:06 +00:00
|
|
|
|
2022-02-03 07:40:26 +00:00
|
|
|
#[test]
|
|
|
|
fn test_cp_dir_vs_file() {
|
|
|
|
new_ucmd!()
|
|
|
|
.arg("-R")
|
|
|
|
.arg(TEST_COPY_FROM_FOLDER)
|
|
|
|
.arg(TEST_EXISTING_FILE)
|
|
|
|
.fails()
|
|
|
|
.stderr_only("cp: cannot overwrite non-directory with directory");
|
|
|
|
}
|
2022-01-31 11:54:26 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_overriding_arguments() {
|
|
|
|
let s = TestScenario::new(util_name!());
|
|
|
|
s.fixtures.touch("file1");
|
2022-04-02 08:47:37 +00:00
|
|
|
for (arg1, arg2) in [
|
2022-01-31 11:54:26 +00:00
|
|
|
#[cfg(not(windows))]
|
|
|
|
("--remove-destination", "--force"),
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
("--force", "--remove-destination"),
|
|
|
|
("--interactive", "--no-clobber"),
|
|
|
|
("--link", "--symbolic-link"),
|
2022-02-09 18:08:28 +00:00
|
|
|
#[cfg(not(target_os = "android"))]
|
2022-01-31 11:54:26 +00:00
|
|
|
("--symbolic-link", "--link"),
|
|
|
|
("--dereference", "--no-dereference"),
|
|
|
|
("--no-dereference", "--dereference"),
|
|
|
|
] {
|
|
|
|
s.ucmd()
|
|
|
|
.arg(arg1)
|
|
|
|
.arg(arg2)
|
|
|
|
.arg("file1")
|
|
|
|
.arg("file2")
|
|
|
|
.succeeds();
|
|
|
|
s.fixtures.remove("file2");
|
|
|
|
}
|
|
|
|
}
|
2022-07-10 14:49:25 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_copy_no_dereference_1() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.mkdir("a");
|
|
|
|
at.mkdir("b");
|
|
|
|
at.touch("a/foo");
|
|
|
|
at.write("a/foo", "bar");
|
|
|
|
at.relative_symlink_file("../a/foo", "b/foo");
|
|
|
|
ucmd.args(&["-P", "a/foo", "b"]).fails();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_abuse_existing() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.mkdir("a");
|
|
|
|
at.mkdir("b");
|
|
|
|
at.mkdir("c");
|
|
|
|
at.relative_symlink_file("../t", "a/1");
|
|
|
|
at.touch("b/1");
|
|
|
|
at.write("b/1", "hello");
|
|
|
|
at.relative_symlink_file("../t", "c/1");
|
|
|
|
at.touch("t");
|
|
|
|
at.write("t", "i");
|
|
|
|
ucmd.args(&["-dR", "a/1", "b/1", "c"])
|
|
|
|
.fails()
|
|
|
|
.stderr_contains(format!(
|
|
|
|
"will not copy 'b/1' through just-created symlink 'c{}1'",
|
|
|
|
if cfg!(windows) { "\\" } else { "/" }
|
|
|
|
));
|
|
|
|
assert_eq!(at.read("t"), "i");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_copy_same_symlink_no_dereference() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.relative_symlink_file("t", "a");
|
|
|
|
at.relative_symlink_file("t", "b");
|
|
|
|
at.touch("t");
|
|
|
|
ucmd.args(&["-d", "a", "b"]).succeeds();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_copy_same_symlink_no_dereference_dangling() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.relative_symlink_file("t", "a");
|
|
|
|
at.relative_symlink_file("t", "b");
|
|
|
|
ucmd.args(&["-d", "a", "b"]).succeeds();
|
|
|
|
}
|
2022-07-17 20:03:29 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[ignore = "issue #3332"]
|
|
|
|
fn test_cp_parents_2() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.mkdir_all("a/b");
|
|
|
|
at.touch("a/b/c");
|
|
|
|
at.mkdir("d");
|
|
|
|
ucmd.args(&["--verbose", "-a", "--parents", "a/b/c", "d"])
|
|
|
|
.succeeds()
|
|
|
|
.stdout_is(format!(
|
|
|
|
"{} -> {}\n{} -> {}\n{} -> {}\n",
|
|
|
|
"a",
|
|
|
|
path_concat!("d", "a"),
|
|
|
|
path_concat!("a", "b"),
|
|
|
|
path_concat!("d", "a", "b"),
|
|
|
|
path_concat!("a", "b", "c").quote(),
|
|
|
|
path_concat!("d", "a", "b", "c").quote()
|
|
|
|
));
|
|
|
|
assert!(at.file_exists("d/a/b/c"));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[ignore = "issue #3332"]
|
|
|
|
fn test_cp_parents_2_link() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.mkdir_all("a/b");
|
|
|
|
at.touch("a/b/c");
|
|
|
|
at.mkdir("d");
|
|
|
|
at.relative_symlink_file("b", "a/link");
|
|
|
|
ucmd.args(&["--verbose", "-a", "--parents", "a/link/c", "d"])
|
|
|
|
.succeeds()
|
|
|
|
.stdout_is(format!(
|
|
|
|
"{} -> {}\n{} -> {}\n{} -> {}\n",
|
|
|
|
"a",
|
|
|
|
path_concat!("d", "a"),
|
|
|
|
path_concat!("a", "link"),
|
|
|
|
path_concat!("d", "a", "link"),
|
|
|
|
path_concat!("a", "link", "c").quote(),
|
|
|
|
path_concat!("d", "a", "link", "c").quote()
|
|
|
|
));
|
|
|
|
assert!(at.dir_exists("d/a/link") && !at.symlink_exists("d/a/link"));
|
|
|
|
assert!(at.file_exists("d/a/link/c"));
|
|
|
|
}
|
2022-09-23 17:31:24 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_copy_symlink_contents_recursive() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.mkdir("src-dir");
|
|
|
|
at.mkdir("dest-dir");
|
|
|
|
at.touch("f");
|
|
|
|
at.write("f", "f");
|
|
|
|
at.relative_symlink_file("f", "slink");
|
|
|
|
at.relative_symlink_file("no-file", &path_concat!("src-dir", "slink"));
|
|
|
|
ucmd.args(&["-H", "-R", "slink", "src-dir", "dest-dir"])
|
|
|
|
.succeeds();
|
|
|
|
assert!(at.dir_exists("src-dir"));
|
|
|
|
assert!(at.dir_exists("dest-dir"));
|
|
|
|
assert!(at.dir_exists(&path_concat!("dest-dir", "src-dir")));
|
|
|
|
let regular_file = path_concat!("dest-dir", "slink");
|
|
|
|
assert!(!at.symlink_exists(®ular_file) && at.file_exists(®ular_file));
|
|
|
|
assert_eq!(at.read(®ular_file), "f");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_mode_symlink() {
|
|
|
|
for from in ["file", "slink", "slink2"] {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.touch("file");
|
|
|
|
at.write("file", "f");
|
|
|
|
at.relative_symlink_file("file", "slink");
|
|
|
|
at.relative_symlink_file("slink", "slink2");
|
|
|
|
ucmd.args(&["-s", "-L", from, "z"]).succeeds();
|
|
|
|
assert!(at.symlink_exists("z"));
|
|
|
|
assert_eq!(at.read_symlink("z"), from);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Android doesn't allow creating hard links
|
|
|
|
#[cfg(not(target_os = "android"))]
|
|
|
|
#[test]
|
|
|
|
fn test_cp_mode_hardlink() {
|
|
|
|
for from in ["file", "slink", "slink2"] {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.touch("file");
|
|
|
|
at.write("file", "f");
|
|
|
|
at.relative_symlink_file("file", "slink");
|
|
|
|
at.relative_symlink_file("slink", "slink2");
|
|
|
|
ucmd.args(&["--link", "-L", from, "z"]).succeeds();
|
|
|
|
assert!(at.file_exists("z") && !at.symlink_exists("z"));
|
|
|
|
assert_eq!(at.read("z"), "f");
|
|
|
|
// checking that it's the same hard link
|
|
|
|
at.append("z", "g");
|
|
|
|
assert_eq!(at.read("file"), "fg");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Android doesn't allow creating hard links
|
|
|
|
#[cfg(not(target_os = "android"))]
|
|
|
|
#[test]
|
|
|
|
fn test_cp_mode_hardlink_no_dereference() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.touch("file");
|
|
|
|
at.write("file", "f");
|
|
|
|
at.relative_symlink_file("file", "slink");
|
|
|
|
at.relative_symlink_file("slink", "slink2");
|
|
|
|
ucmd.args(&["--link", "-P", "slink2", "z"]).succeeds();
|
|
|
|
assert!(at.symlink_exists("z"));
|
|
|
|
assert_eq!(at.read_symlink("z"), "slink");
|
|
|
|
}
|
2022-09-23 19:56:44 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_remove_destination_symbolic_link_loop() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.symlink_file("loop", "loop");
|
|
|
|
at.plus("loop");
|
|
|
|
at.touch("f");
|
|
|
|
ucmd.args(&["--remove-destination", "f", "loop"])
|
|
|
|
.succeeds()
|
|
|
|
.no_stdout()
|
|
|
|
.no_stderr();
|
|
|
|
assert!(at.file_exists("loop"));
|
|
|
|
}
|
2022-10-06 07:53:02 +00:00
|
|
|
|
2022-09-25 21:09:01 +00:00
|
|
|
/// Test that copying a directory to itself is disallowed.
|
|
|
|
#[test]
|
|
|
|
fn test_copy_directory_to_itself_disallowed() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.mkdir("d");
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
let expected = "cp: cannot copy a directory, 'd', into itself, 'd/d'";
|
|
|
|
#[cfg(windows)]
|
|
|
|
let expected = "cp: cannot copy a directory, 'd', into itself, 'd\\d'";
|
|
|
|
ucmd.args(&["-R", "d", "d"]).fails().stderr_only(expected);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Test that copying a nested directory to itself is disallowed.
|
|
|
|
#[test]
|
|
|
|
fn test_copy_nested_directory_to_itself_disallowed() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.mkdir("a");
|
|
|
|
at.mkdir("a/b");
|
|
|
|
at.mkdir("a/b/c");
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
let expected = "cp: cannot copy a directory, 'a/b', into itself, 'a/b/c/b'";
|
|
|
|
#[cfg(windows)]
|
|
|
|
let expected = "cp: cannot copy a directory, 'a/b', into itself, 'a/b/c\\b'";
|
|
|
|
ucmd.args(&["-R", "a/b", "a/b/c"])
|
|
|
|
.fails()
|
|
|
|
.stderr_only(expected);
|
|
|
|
}
|