use nu_test_support::fs::file_contents; use nu_test_support::fs::{ files_exist_at, AbsoluteFile, Stub::{EmptyFile, FileWithContent, FileWithPermission}, }; use nu_test_support::nu; use nu_test_support::playground::Playground; use rstest::rstest; use std::path::Path; #[cfg(not(target_os = "windows"))] const PATH_SEPARATOR: &str = "/"; #[cfg(target_os = "windows")] const PATH_SEPARATOR: &str = "\\"; fn get_file_hash(file: T) -> String { nu!("open -r {} | to text | hash md5", file).out } #[test] fn copies_a_file() { copies_a_file_impl(false); copies_a_file_impl(true); } fn copies_a_file_impl(progress: bool) { Playground::setup("ucp_test_1", |dirs, _| { let test_file = dirs.formats().join("sample.ini"); let progress_flag = if progress { "-p" } else { "" }; // Get the hash of the file content to check integrity after copy. let first_hash = get_file_hash(test_file.display()); nu!( cwd: dirs.root(), "cp {} `{}` ucp_test_1/sample.ini", progress_flag, test_file.display() ); assert!(dirs.test().join("sample.ini").exists()); // Get the hash of the copied file content to check against first_hash. let after_cp_hash = get_file_hash(dirs.test().join("sample.ini").display()); assert_eq!(first_hash, after_cp_hash); }); } #[test] fn copies_the_file_inside_directory_if_path_to_copy_is_directory() { copies_the_file_inside_directory_if_path_to_copy_is_directory_impl(false); copies_the_file_inside_directory_if_path_to_copy_is_directory_impl(true); } fn copies_the_file_inside_directory_if_path_to_copy_is_directory_impl(progress: bool) { Playground::setup("ucp_test_2", |dirs, _| { let expected_file = AbsoluteFile::new(dirs.test().join("sample.ini")); let progress_flag = if progress { "-p" } else { "" }; // Get the hash of the file content to check integrity after copy. let first_hash = get_file_hash(dirs.formats().join("../formats/sample.ini").display()); nu!( cwd: dirs.formats(), "cp {} ../formats/sample.ini {}", progress_flag, expected_file.dir() ); assert!(dirs.test().join("sample.ini").exists()); // Check the integrity of the file. let after_cp_hash = get_file_hash(expected_file); assert_eq!(first_hash, after_cp_hash); }) } // error msg changes on coreutils #[test] fn error_if_attempting_to_copy_a_directory_to_another_directory() { error_if_attempting_to_copy_a_directory_to_another_directory_impl(false); error_if_attempting_to_copy_a_directory_to_another_directory_impl(true); } fn error_if_attempting_to_copy_a_directory_to_another_directory_impl(progress: bool) { Playground::setup("ucp_test_3", |dirs, _| { let progress_flag = if progress { "-p" } else { "" }; let actual = nu!( cwd: dirs.formats(), "cp {} ../formats {}", progress_flag, dirs.test().display() ); assert!(actual.err.contains("formats")); assert!(actual.err.contains("resolves to a directory (not copied)")); }); } #[test] fn copies_the_directory_inside_directory_if_path_to_copy_is_directory_and_with_recursive_flag() { copies_the_directory_inside_directory_if_path_to_copy_is_directory_and_with_recursive_flag_impl( false, ); copies_the_directory_inside_directory_if_path_to_copy_is_directory_and_with_recursive_flag_impl( true, ); } fn copies_the_directory_inside_directory_if_path_to_copy_is_directory_and_with_recursive_flag_impl( progress: bool, ) { Playground::setup("ucp_test_4", |dirs, sandbox| { sandbox .within("originals") .with_files(&[ EmptyFile("yehuda.txt"), EmptyFile("jttxt"), EmptyFile("andres.txt"), ]) .mkdir("expected"); let expected_dir = dirs.test().join("expected").join("originals"); let progress_flag = if progress { "-p" } else { "" }; nu!( cwd: dirs.test(), "cp {} originals expected -r", progress_flag ); assert!(expected_dir.exists()); assert!(files_exist_at( vec![ Path::new("yehuda.txt"), Path::new("jttxt"), Path::new("andres.txt") ], &expected_dir )); }) } #[test] fn deep_copies_with_recursive_flag() { deep_copies_with_recursive_flag_impl(false); deep_copies_with_recursive_flag_impl(true); } fn deep_copies_with_recursive_flag_impl(progress: bool) { Playground::setup("ucp_test_5", |dirs, sandbox| { sandbox .within("originals") .with_files(&[EmptyFile("manifest.txt")]) .within("originals/contributors") .with_files(&[ EmptyFile("yehuda.txt"), EmptyFile("jttxt"), EmptyFile("andres.txt"), ]) .within("originals/contributors/JT") .with_files(&[EmptyFile("errors.txt"), EmptyFile("multishells.txt")]) .within("originals/contributors/andres") .with_files(&[EmptyFile("coverage.txt"), EmptyFile("commands.txt")]) .within("originals/contributors/yehuda") .with_files(&[EmptyFile("defer-evaluation.txt")]) .mkdir("expected"); let expected_dir = dirs.test().join("expected").join("originals"); let progress_flag = if progress { "-p" } else { "" }; let jts_expected_copied_dir = expected_dir.join("contributors").join("JT"); let andres_expected_copied_dir = expected_dir.join("contributors").join("andres"); let yehudas_expected_copied_dir = expected_dir.join("contributors").join("yehuda"); nu!( cwd: dirs.test(), "cp {} originals expected --recursive", progress_flag ); assert!(expected_dir.exists()); assert!(files_exist_at( vec![Path::new("errors.txt"), Path::new("multishells.txt")], jts_expected_copied_dir )); assert!(files_exist_at( vec![Path::new("coverage.txt"), Path::new("commands.txt")], andres_expected_copied_dir )); assert!(files_exist_at( vec![Path::new("defer-evaluation.txt")], yehudas_expected_copied_dir )); }) } #[test] fn copies_using_path_with_wildcard() { copies_using_path_with_wildcard_impl(false); copies_using_path_with_wildcard_impl(true); } fn copies_using_path_with_wildcard_impl(progress: bool) { Playground::setup("ucp_test_6", |dirs, _| { let progress_flag = if progress { "-p" } else { "" }; // Get the hash of the file content to check integrity after copy. let src_hashes = nu!( cwd: dirs.formats(), "for file in (ls ../formats/*) { open --raw $file.name | to text | hash md5 }" ) .out; nu!( cwd: dirs.formats(), "cp {} -r ../formats/* {}", progress_flag, dirs.test().display() ); assert!(files_exist_at( vec![ Path::new("caco3_plastics.csv"), Path::new("cargo_sample.toml"), Path::new("jt.xml"), Path::new("sample.ini"), Path::new("sgml_description.json"), Path::new("utf16.ini"), ], dirs.test() )); // Check integrity after the copy is done let dst_hashes = nu!( cwd: dirs.formats(), "for file in (ls {}) {{ open --raw $file.name | to text | hash md5 }}", dirs.test().display() ).out; assert_eq!(src_hashes, dst_hashes); }) } #[test] fn copies_using_a_glob() { copies_using_a_glob_impl(false); copies_using_a_glob_impl(true); } fn copies_using_a_glob_impl(progress: bool) { Playground::setup("ucp_test_7", |dirs, _| { let progress_flag = if progress { "-p" } else { "" }; // Get the hash of the file content to check integrity after copy. let src_hashes = nu!( cwd: dirs.formats(), "for file in (ls *) { open --raw $file.name | to text | hash md5 }" ) .out; nu!( cwd: dirs.formats(), "cp {} -r * {}", progress_flag, dirs.test().display() ); assert!(files_exist_at( vec![ Path::new("caco3_plastics.csv"), Path::new("cargo_sample.toml"), Path::new("jt.xml"), Path::new("sample.ini"), Path::new("sgml_description.json"), Path::new("utf16.ini"), ], dirs.test() )); // Check integrity after the copy is done let dst_hashes = nu!( cwd: dirs.formats(), "for file in (ls {}) {{ open --raw $file.name | to text | hash md5 }}", dirs.test().display() ) .out; assert_eq!(src_hashes, dst_hashes); }); } #[test] fn copies_same_file_twice() { copies_same_file_twice_impl(false); copies_same_file_twice_impl(true); } fn copies_same_file_twice_impl(progress: bool) { Playground::setup("ucp_test_8", |dirs, _| { let progress_flag = if progress { "-p" } else { "" }; nu!( cwd: dirs.root(), "cp {} `{}` ucp_test_8/sample.ini", progress_flag, dirs.formats().join("sample.ini").display() ); nu!( cwd: dirs.root(), "cp {} `{}` ucp_test_8/sample.ini", progress_flag, dirs.formats().join("sample.ini").display() ); assert!(dirs.test().join("sample.ini").exists()); }); } #[test] #[ignore = "Behavior not supported by uutils cp"] fn copy_files_using_glob_two_parents_up_using_multiple_dots() { copy_files_using_glob_two_parents_up_using_multiple_dots_imp(false); copy_files_using_glob_two_parents_up_using_multiple_dots_imp(true); } fn copy_files_using_glob_two_parents_up_using_multiple_dots_imp(progress: bool) { Playground::setup("ucp_test_9", |dirs, sandbox| { sandbox.within("foo").within("bar").with_files(&[ EmptyFile("jtjson"), EmptyFile("andres.xml"), EmptyFile("yehuda.yaml"), EmptyFile("kevin.txt"), EmptyFile("many_more.ppl"), ]); let progress_flag = if progress { "-p" } else { "" }; nu!( cwd: dirs.test().join("foo/bar"), " cp {} * ...", progress_flag, ); assert!(files_exist_at( vec![ "yehuda.yaml", "jtjson", "andres.xml", "kevin.txt", "many_more.ppl", ], dirs.test() )); }) } #[test] fn copy_file_and_dir_from_two_parents_up_using_multiple_dots_to_current_dir_recursive() { copy_file_and_dir_from_two_parents_up_using_multiple_dots_to_current_dir_recursive_impl(false); copy_file_and_dir_from_two_parents_up_using_multiple_dots_to_current_dir_recursive_impl(true); } fn copy_file_and_dir_from_two_parents_up_using_multiple_dots_to_current_dir_recursive_impl( progress: bool, ) { Playground::setup("ucp_test_10", |dirs, sandbox| { sandbox.with_files(&[EmptyFile("hello_there")]); sandbox.mkdir("hello_again"); sandbox.within("foo").mkdir("bar"); let progress_flag = if progress { "-p" } else { "" }; nu!( cwd: dirs.test().join("foo/bar"), "cp {} -r .../hello* .", progress_flag ); let expected = dirs.test().join("foo/bar"); assert!(files_exist_at(vec!["hello_there", "hello_again"], expected)); }) } // error msg changes on coreutils #[test] fn copy_to_non_existing_dir() { copy_to_non_existing_dir_impl(false); copy_to_non_existing_dir_impl(true); } fn copy_to_non_existing_dir_impl(progress: bool) { Playground::setup("ucp_test_11", |_dirs, sandbox| { sandbox.with_files(&[EmptyFile("empty_file")]); let progress_flag = if progress { "-p" } else { "" }; let actual = nu!( cwd: sandbox.cwd(), "cp {} empty_file ~/not_a_dir{}", progress_flag, PATH_SEPARATOR, ); assert!(actual.err.contains("is not a directory")); }); } #[test] fn copy_dir_contains_symlink_ignored() { copy_dir_contains_symlink_ignored_impl(false); copy_dir_contains_symlink_ignored_impl(true); } fn copy_dir_contains_symlink_ignored_impl(progress: bool) { Playground::setup("ucp_test_12", |_dirs, sandbox| { sandbox .within("tmp_dir") .with_files(&[EmptyFile("hello_there"), EmptyFile("good_bye")]) .within("tmp_dir") .symlink("good_bye", "dangle_symlink"); let progress_flag = if progress { "-p" } else { "" }; // make symbolic link and copy. nu!( cwd: sandbox.cwd(), "rm {} tmp_dir/good_bye; cp -r tmp_dir tmp_dir_2", progress_flag ); // check hello_there exists inside `tmp_dir_2`, and `dangle_symlink` don't exists inside `tmp_dir_2`. let expected = sandbox.cwd().join("tmp_dir_2"); assert!(files_exist_at(vec!["hello_there"], expected)); // GNU cp will copy the broken symlink, so following their behavior // thus commenting out below // let path = expected.join("dangle_symlink"); // assert!(!path.exists() && !path.is_symlink()); }); } #[test] fn copy_dir_contains_symlink() { copy_dir_contains_symlink_impl(false); copy_dir_contains_symlink_impl(true); } fn copy_dir_contains_symlink_impl(progress: bool) { Playground::setup("ucp_test_13", |_dirs, sandbox| { sandbox .within("tmp_dir") .with_files(&[EmptyFile("hello_there"), EmptyFile("good_bye")]) .within("tmp_dir") .symlink("good_bye", "dangle_symlink"); let progress_flag = if progress { "-p" } else { "" }; // make symbolic link and copy. nu!( cwd: sandbox.cwd(), "rm tmp_dir/good_bye; cp {} -r -n tmp_dir tmp_dir_2", progress_flag ); // check hello_there exists inside `tmp_dir_2`, and `dangle_symlink` also exists inside `tmp_dir_2`. let expected = sandbox.cwd().join("tmp_dir_2"); assert!(files_exist_at(vec!["hello_there"], expected.clone())); let path = expected.join("dangle_symlink"); assert!(path.is_symlink()); }); } #[test] fn copy_dir_symlink_file_body_not_changed() { copy_dir_symlink_file_body_not_changed_impl(false); copy_dir_symlink_file_body_not_changed_impl(true); } fn copy_dir_symlink_file_body_not_changed_impl(progress: bool) { Playground::setup("ucp_test_14", |_dirs, sandbox| { sandbox .within("tmp_dir") .with_files(&[EmptyFile("hello_there"), EmptyFile("good_bye")]) .within("tmp_dir") .symlink("good_bye", "dangle_symlink"); let progress_flag = if progress { "-p" } else { "" }; // make symbolic link and copy. nu!( cwd: sandbox.cwd(), "rm tmp_dir/good_bye; cp {} -r -n tmp_dir tmp_dir_2; rm -r tmp_dir; cp {} -r -n tmp_dir_2 tmp_dir; echo hello_data | save tmp_dir/good_bye", progress_flag, progress_flag, ); // check dangle_symlink in tmp_dir is no longer dangling. let expected_file = sandbox.cwd().join("tmp_dir").join("dangle_symlink"); let actual = file_contents(expected_file); assert!(actual.contains("hello_data")); }); } // error msg changes on coreutils #[test] fn copy_identical_file() { copy_identical_file_impl(false); copy_identical_file_impl(true); } fn copy_identical_file_impl(progress: bool) { Playground::setup("ucp_test_15", |dirs, sandbox| { sandbox.with_files(&[EmptyFile("same.txt")]); let progress_flag = if progress { "-p" } else { "" }; let actual = nu!( cwd: dirs.test(), "cp {} same.txt same.txt", progress_flag, ); // assert!(actual.err.contains("Copy aborted")); let msg = format!( "'{}' and '{}' are the same file", dirs.test().join("same.txt").display(), dirs.test().join("same.txt").display(), ); // debug messages in CI if !actual.err.contains(&msg) { panic!("stderr was: {}", actual.err); } }); } #[test] #[ignore = "File name in progress bar not on uutils impl"] fn copy_ignores_ansi() { copy_ignores_ansi_impl(false); copy_ignores_ansi_impl(true); } fn copy_ignores_ansi_impl(progress: bool) { Playground::setup("ucp_test_16", |_dirs, sandbox| { sandbox.with_files(&[EmptyFile("test.txt")]); let progress_flag = if progress { "-p" } else { "" }; let actual = nu!( cwd: sandbox.cwd(), "ls | find test | get name | cp {} $in.0 success.txt; ls | find success | get name | ansi strip | get 0", progress_flag, ); assert_eq!(actual.out, "success.txt"); }); } //apparently on windows error msg is different, but linux(where i test) is fine. //fix later FIXME #[cfg(unix)] #[test] fn copy_file_not_exists_dst() { copy_file_not_exists_dst_impl(false); copy_file_not_exists_dst_impl(true); } #[cfg(unix)] fn copy_file_not_exists_dst_impl(progress: bool) { Playground::setup("ucp_test_17", |_dirs, sandbox| { sandbox.with_files(&[EmptyFile("valid.txt")]); let progress_flag = if progress { "-p" } else { "" }; let actual = nu!( cwd: sandbox.cwd(), "cp {} valid.txt ~/invalid_dir/invalid_dir1", progress_flag, ); assert!( actual.err.contains("invalid_dir1") && actual.err.contains("No such file or directory") ); }); } //again slightly different error message on windows on tests // compared to linux #[test] #[ignore] //FIXME: This test needs to be re-enabled once uu_cp has fixed the bug fn copy_file_with_read_permission() { copy_file_with_read_permission_impl(false); copy_file_with_read_permission_impl(true); } fn copy_file_with_read_permission_impl(progress: bool) { Playground::setup("ucp_test_18", |_dirs, sandbox| { sandbox.with_files(&[ EmptyFile("valid.txt"), FileWithPermission("invalid_prem.txt", false), ]); let progress_flag = if progress { "-p" } else { "" }; let actual = nu!( cwd: sandbox.cwd(), "cp {} valid.txt invalid_prem.txt", progress_flag, ); assert!(actual.err.contains("invalid_prem.txt") && actual.err.contains("denied")); }); } // uutils/coreutils copy tests static TEST_EXISTING_FILE: &str = "existing_file.txt"; static TEST_HELLO_WORLD_SOURCE: &str = "hello_world.txt"; static TEST_HELLO_WORLD_DEST: &str = "copy_of_hello_world.txt"; 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"; #[cfg(not(target_os = "macos"))] static TEST_COPY_TO_FOLDER_NEW: &str = "hello_dir_new"; #[cfg(not(target_os = "macos"))] static TEST_COPY_TO_FOLDER_NEW_FILE: &str = "hello_dir_new/hello_world.txt"; #[test] fn test_cp_cp() { Playground::setup("ucp_test_19", |dirs, _| { let src = dirs.fixtures.join("cp").join(TEST_HELLO_WORLD_SOURCE); // Get the hash of the file content to check integrity after copy. let src_hash = get_file_hash(src.display()); nu!( cwd: dirs.root(), "cp {} ucp_test_19/{}", src.display(), TEST_HELLO_WORLD_DEST ); assert!(dirs.test().join(TEST_HELLO_WORLD_DEST).exists()); // Get the hash of the copied file content to check against first_hash. let after_cp_hash = get_file_hash(dirs.test().join(TEST_HELLO_WORLD_DEST).display()); assert_eq!(src_hash, after_cp_hash); }); } #[test] fn test_cp_existing_target() { Playground::setup("ucp_test_20", |dirs, _| { let src = dirs.fixtures.join("cp").join(TEST_HELLO_WORLD_SOURCE); let existing = dirs.fixtures.join("cp").join(TEST_EXISTING_FILE); // Get the hash of the file content to check integrity after copy. let src_hash = get_file_hash(src.display()); // Copy existing file to destination, so that it exists for the test nu!( cwd: dirs.root(), "cp {} ucp_test_20/{}", existing.display(), TEST_EXISTING_FILE ); // At this point the src and existing files should be different assert!(dirs.test().join(TEST_EXISTING_FILE).exists()); // Now for the test nu!( cwd: dirs.root(), "cp {} ucp_test_20/{}", src.display(), TEST_EXISTING_FILE ); assert!(dirs.test().join(TEST_EXISTING_FILE).exists()); // Get the hash of the copied file content to check against first_hash. let after_cp_hash = get_file_hash(dirs.test().join(TEST_EXISTING_FILE).display()); assert_eq!(src_hash, after_cp_hash); }); } #[test] fn test_cp_multiple_files() { Playground::setup("ucp_test_21", |dirs, sandbox| { let src1 = dirs.fixtures.join("cp").join(TEST_HELLO_WORLD_SOURCE); let src2 = dirs.fixtures.join("cp").join(TEST_HOW_ARE_YOU_SOURCE); // Get the hash of the file content to check integrity after copy. let src1_hash = get_file_hash(src1.display()); let src2_hash = get_file_hash(src2.display()); //Create target directory sandbox.mkdir(TEST_COPY_TO_FOLDER); // Start test nu!( cwd: dirs.root(), "cp {} {} ucp_test_21/{}", src1.display(), src2.display(), TEST_COPY_TO_FOLDER ); assert!(dirs.test().join(TEST_COPY_TO_FOLDER).exists()); // Get the hash of the copied file content to check against first_hash. let after_cp_1_hash = get_file_hash(dirs.test().join(TEST_COPY_TO_FOLDER_FILE).display()); let after_cp_2_hash = get_file_hash(dirs.test().join(TEST_HOW_ARE_YOU_DEST).display()); assert_eq!(src1_hash, after_cp_1_hash); assert_eq!(src2_hash, after_cp_2_hash); }); } #[test] #[cfg(not(target_os = "macos"))] fn test_cp_recurse() { Playground::setup("ucp_test_22", |dirs, sandbox| { // Create the relevant target directories sandbox.mkdir(TEST_COPY_FROM_FOLDER); sandbox.mkdir(TEST_COPY_TO_FOLDER_NEW); let src = dirs .fixtures .join("cp") .join(TEST_COPY_FROM_FOLDER) .join(TEST_COPY_FROM_FOLDER_FILE); let src_hash = get_file_hash(src.display()); // Start test nu!( cwd: dirs.root(), "cp -r {} ucp_test_22/{}", TEST_COPY_FROM_FOLDER, TEST_COPY_TO_FOLDER_NEW, ); let after_cp_hash = get_file_hash(dirs.test().join(TEST_COPY_TO_FOLDER_NEW_FILE).display()); assert_eq!(src_hash, after_cp_hash); }); } #[test] fn test_cp_with_dirs() { Playground::setup("ucp_test_23", |dirs, sandbox| { let src = dirs.fixtures.join("cp").join(TEST_HELLO_WORLD_SOURCE); let src_hash = get_file_hash(src.display()); //Create target directory sandbox.mkdir(TEST_COPY_TO_FOLDER); // Start test nu!( cwd: dirs.root(), "cp {} ucp_test_23/{}", src.display(), TEST_COPY_TO_FOLDER, ); let after_cp_hash = get_file_hash(dirs.test().join(TEST_COPY_TO_FOLDER_FILE).display()); assert_eq!(src_hash, after_cp_hash); // Other way around sandbox.mkdir(TEST_COPY_FROM_FOLDER); let src2 = dirs.fixtures.join("cp").join(TEST_COPY_FROM_FOLDER_FILE); let src2_hash = get_file_hash(src2.display()); nu!( cwd: dirs.root(), "cp {} ucp_test_23/{}", src2.display(), TEST_HELLO_WORLD_DEST, ); let after_cp_2_hash = get_file_hash(dirs.test().join(TEST_HELLO_WORLD_DEST).display()); assert_eq!(src2_hash, after_cp_2_hash); }); } #[cfg(not(windows))] #[test] fn test_cp_arg_force() { Playground::setup("ucp_test_24", |dirs, sandbox| { let src = dirs.fixtures.join("cp").join(TEST_HELLO_WORLD_SOURCE); let src_hash = get_file_hash(src.display()); sandbox.with_files(&[FileWithPermission("invalid_prem.txt", false)]); nu!( cwd: dirs.root(), "cp {} --force ucp_test_24/{}", src.display(), "invalid_prem.txt" ); let after_cp_hash = get_file_hash(dirs.test().join("invalid_prem.txt").display()); // Check content was copied by the use of --force assert_eq!(src_hash, after_cp_hash); }); } #[test] fn test_cp_directory_to_itself_disallowed() { Playground::setup("ucp_test_25", |dirs, sandbox| { sandbox.mkdir("d"); let actual = nu!( cwd: dirs.root(), "cp -r ucp_test_25/{} ucp_test_25/{}", "d", "d" ); actual .err .contains("cannot copy a directory, 'd', into itself, 'd/d'"); }); } #[test] fn test_cp_nested_directory_to_itself_disallowed() { Playground::setup("ucp_test_26", |dirs, sandbox| { sandbox.mkdir("a"); sandbox.mkdir("a/b"); sandbox.mkdir("a/b/c"); let actual = nu!( cwd: dirs.test(), "cp -r {} {}", "a/b", "a/b/c" ); actual .err .contains("cannot copy a directory, 'a/b', into itself, 'a/b/c/b'"); }); } #[cfg(not(windows))] #[test] fn test_cp_same_file_force() { Playground::setup("ucp_test_27", |dirs, sandbox| { sandbox.with_files(&[EmptyFile("f")]); let actual = nu!( cwd: dirs.test(), "cp --force {} {}", "f", "f" ); actual.err.contains("cp: 'f' and 'f' are the same file"); assert!(!dirs.test().join("f~").exists()); }); } #[test] fn test_cp_arg_no_clobber() { Playground::setup("ucp_test_28", |dirs, _| { let src = dirs.fixtures.join("cp").join(TEST_HELLO_WORLD_SOURCE); let target = dirs.fixtures.join("cp").join(TEST_HOW_ARE_YOU_SOURCE); let target_hash = get_file_hash(target.display()); let actual = nu!( cwd: dirs.root(), "cp {} {} --no-clobber", src.display(), target.display() ); let after_cp_hash = get_file_hash(target.display()); assert!(actual.err.contains("not replacing")); // Check content was not clobbered assert_eq!(after_cp_hash, target_hash); }); } #[test] fn test_cp_arg_no_clobber_twice() { Playground::setup("ucp_test_29", |dirs, sandbox| { sandbox.with_files(&[ EmptyFile("source.txt"), FileWithContent("source_with_body.txt", "some-body"), ]); nu!( cwd: dirs.root(), "cp --no-clobber ucp_test_29/{} ucp_test_29/{}", "source.txt", "dest.txt" ); assert!(dirs.test().join("dest.txt").exists()); nu!( cwd: dirs.root(), "cp --no-clobber ucp_test_29/{} ucp_test_29/{}", "source_with_body.txt", "dest.txt" ); // Should have same contents of original empty file as --no-clobber should not overwrite dest.txt assert_eq!(file_contents(dirs.test().join("dest.txt")), "fake data"); }); } #[test] fn test_cp_debug_default() { Playground::setup("ucp_test_30", |dirs, _| { let src = dirs.fixtures.join("cp").join(TEST_HELLO_WORLD_SOURCE); let actual = nu!( cwd: dirs.root(), "cp --debug {} ucp_test_30/{}", src.display(), TEST_HELLO_WORLD_DEST ); #[cfg(target_os = "macos")] if !actual .out .contains("copy offload: unknown, reflink: unsupported, sparse detection: unsupported") { panic!("{}", format!("Failure: stdout was \n{}", actual.out)); } #[cfg(any(target_os = "linux", target_os = "freebsd"))] if !actual .out .contains("copy offload: unknown, reflink: unsupported, sparse detection: no") { panic!("{}", format!("Failure: stdout was \n{}", actual.out)); } #[cfg(windows)] if !actual.out.contains( "copy offload: unsupported, reflink: unsupported, sparse detection: unsupported", ) { panic!("{}", format!("Failure: stdout was \n{}", actual.out)); } }); } #[test] fn test_cp_verbose_default() { Playground::setup("ucp_test_31", |dirs, _| { let src = dirs.fixtures.join("cp").join(TEST_HELLO_WORLD_SOURCE); let actual = nu!( cwd: dirs.root(), "cp --verbose {} {}", src.display(), TEST_HELLO_WORLD_DEST ); assert!(actual.out.contains( format!( "'{}' -> '{}'", src.display(), dirs.root().join(TEST_HELLO_WORLD_DEST).display() ) .as_str(), )); }); } #[test] fn test_cp_only_source_no_dest() { Playground::setup("ucp_test_32", |dirs, _| { let src = dirs.fixtures.join("cp").join(TEST_HELLO_WORLD_SOURCE); let actual = nu!( cwd: dirs.root(), "cp {}", src.display(), ); assert!(actual .err .contains("Missing destination path operand after")); assert!(actual.err.contains(TEST_HELLO_WORLD_SOURCE)); }); } #[test] fn test_cp_with_vars() { Playground::setup("ucp_test_33", |dirs, sandbox| { sandbox.with_files(&[EmptyFile("input")]); nu!( cwd: dirs.test(), "let src = 'input'; let dst = 'target'; cp $src $dst", ); assert!(dirs.test().join("target").exists()); }); } #[test] fn test_cp_destination_after_cd() { Playground::setup("ucp_test_34", |dirs, sandbox| { sandbox.mkdir("test"); sandbox.with_files(&[EmptyFile("test/file.txt")]); nu!( cwd: dirs.test(), // Defining variable avoid path expansion of cp argument. // If argument was not expanded ucp wrapper should do it "cd test; let file = 'copy.txt'; cp file.txt $file", ); assert!(dirs.test().join("test").join("copy.txt").exists()); }); } #[rstest] #[case("a]c")] #[case("a[c")] #[case("a[bc]d")] #[case("a][c")] fn copies_files_with_glob_metachars(#[case] src_name: &str) { Playground::setup("ucp_test_34", |dirs, sandbox| { sandbox.with_files(&[FileWithContent( src_name, "What is the sound of one hand clapping?", )]); let src = dirs.test().join(src_name); // -- open command doesn't like file name //// Get the hash of the file content to check integrity after copy. //let src_hash = get_file_hash(src.display()); let actual = nu!( cwd: dirs.test(), "cp '{}' {}", src.display(), TEST_HELLO_WORLD_DEST ); assert!(actual.err.is_empty()); assert!(dirs.test().join(TEST_HELLO_WORLD_DEST).exists()); //// Get the hash of the copied file content to check against first_hash. //let after_cp_hash = get_file_hash(dirs.test().join(TEST_HELLO_WORLD_DEST).display()); //assert_eq!(src_hash, after_cp_hash); }); } #[rstest] #[case("a]c")] #[case("a[c")] #[case("a[bc]d")] #[case("a][c")] fn copies_files_with_glob_metachars_when_input_are_variables(#[case] src_name: &str) { Playground::setup("ucp_test_35", |dirs, sandbox| { sandbox.with_files(&[FileWithContent( src_name, "What is the sound of one hand clapping?", )]); let src = dirs.test().join(src_name); // -- open command doesn't like file name //// Get the hash of the file content to check integrity after copy. //let src_hash = get_file_hash(src.display()); let actual = nu!( cwd: dirs.test(), "let f = '{}'; cp $f {}", src.display(), TEST_HELLO_WORLD_DEST ); assert!(actual.err.is_empty()); assert!(dirs.test().join(TEST_HELLO_WORLD_DEST).exists()); //// Get the hash of the copied file content to check against first_hash. //let after_cp_hash = get_file_hash(dirs.test().join(TEST_HELLO_WORLD_DEST).display()); //assert_eq!(src_hash, after_cp_hash); }); } #[cfg(not(windows))] #[rstest] #[case(r#"'a]?c'"#)] #[case(r#"'a*.?c'"#)] // windows doesn't allow filename with `*`. fn copies_files_with_glob_metachars_nw(#[case] src_name: &str) { copies_files_with_glob_metachars(src_name); copies_files_with_glob_metachars_when_input_are_variables(src_name); } #[cfg(not(windows))] #[test] fn test_cp_preserve_timestamps() { // Preserve timestamp and mode Playground::setup("ucp_test_35", |dirs, sandbox| { sandbox.with_files(&[EmptyFile("file.txt")]); let actual = nu!( cwd: dirs.test(), " chmod +x file.txt cp --preserve [ mode timestamps ] file.txt other.txt let old_attrs = ls -l file.txt | get 0 | select mode accessed modified let new_attrs = ls -l other.txt | get 0 | select mode accessed modified $old_attrs == $new_attrs ", ); assert!(actual.err.is_empty()); assert_eq!(actual.out, "true"); }); } #[cfg(not(windows))] #[test] fn test_cp_preserve_only_timestamps() { // Preserve timestamps and discard all other attributes including mode Playground::setup("ucp_test_35", |dirs, sandbox| { sandbox.with_files(&[EmptyFile("file.txt")]); let actual = nu!( cwd: dirs.test(), " chmod +x file.txt cp --preserve [ timestamps ] file.txt other.txt let old_attrs = ls -l file.txt | get 0 | select mode accessed modified let new_attrs = ls -l other.txt | get 0 | select mode accessed modified print (($old_attrs | select mode) != ($new_attrs | select mode)) print (($old_attrs | select accessed modified) == ($new_attrs | select accessed modified)) ", ); assert!(actual.err.is_empty()); assert_eq!(actual.out, "truetrue"); }); } #[cfg(not(windows))] #[test] fn test_cp_preserve_nothing() { // Preserve no attributes Playground::setup("ucp_test_35", |dirs, sandbox| { sandbox.with_files(&[EmptyFile("file.txt")]); let actual = nu!( cwd: dirs.test(), " chmod +x file.txt cp --preserve [] file.txt other.txt let old_attrs = ls -l file.txt | get 0 | select mode accessed modified let new_attrs = ls -l other.txt | get 0 | select mode accessed modified $old_attrs != $new_attrs ", ); assert!(actual.err.is_empty()); assert_eq!(actual.out, "true"); }); } #[test] fn test_cp_inside_glob_metachars_dir() { Playground::setup("open_files_inside_glob_metachars_dir", |dirs, sandbox| { let sub_dir = "test[]"; sandbox .within(sub_dir) .with_files(&[FileWithContent("test_file.txt", "hello")]); let actual = nu!( cwd: dirs.test().join(sub_dir), "cp test_file.txt ../", ); assert!(actual.err.is_empty()); assert!(files_exist_at( vec!["test_file.txt"], dirs.test().join(sub_dir) )); assert!(files_exist_at(vec!["test_file.txt"], dirs.test())); }); } #[cfg(not(windows))] #[test] fn test_cp_to_customized_home_directory() { Playground::setup("cp_to_home", |dirs, sandbox| { std::env::set_var("HOME", dirs.test()); sandbox.with_files(&[EmptyFile("test_file.txt")]); let actual = nu!(cwd: dirs.test(), "mkdir test; cp test_file.txt ~/test/"); assert!(actual.err.is_empty()); assert!(files_exist_at( vec!["test_file.txt"], dirs.test().join("test") )); }) } #[test] fn cp_with_tilde() { Playground::setup("cp_tilde", |dirs, sandbox| { sandbox.within("~tilde").with_files(&[ EmptyFile("f1.txt"), EmptyFile("f2.txt"), EmptyFile("f3.txt"), ]); sandbox.within("~tilde2"); // cp directory let actual = nu!( cwd: dirs.test(), "let f = '~tilde'; cp -r $f '~tilde2'; ls '~tilde2/~tilde' | length" ); assert_eq!(actual.out, "3"); // cp file let actual = nu!(cwd: dirs.test(), "cp '~tilde/f1.txt' ./"); assert!(actual.err.is_empty()); assert!(files_exist_at( vec![Path::new("f1.txt")], dirs.test().join("~tilde") )); assert!(files_exist_at(vec![Path::new("f1.txt")], dirs.test())); // pass variable let actual = nu!(cwd: dirs.test(), "let f = '~tilde/f2.txt'; cp $f ./"); assert!(actual.err.is_empty()); assert!(files_exist_at( vec![Path::new("f2.txt")], dirs.test().join("~tilde") )); assert!(files_exist_at(vec![Path::new("f1.txt")], dirs.test())); }) } #[test] fn copy_file_with_update_flag() { copy_file_with_update_flag_impl(false); copy_file_with_update_flag_impl(true); } fn copy_file_with_update_flag_impl(progress: bool) { Playground::setup("cp_test_36", |_dirs, sandbox| { sandbox.with_files(&[ EmptyFile("valid.txt"), FileWithContent("newer_valid.txt", "body"), ]); let progress_flag = if progress { "-p" } else { "" }; let actual = nu!( cwd: sandbox.cwd(), "cp {} -u valid.txt newer_valid.txt; open newer_valid.txt", progress_flag, ); assert!(actual.out.contains("body")); // create a file after assert to make sure that newest_valid.txt is newest std::thread::sleep(std::time::Duration::from_secs(1)); sandbox.with_files(&[FileWithContent("newest_valid.txt", "newest_body")]); let actual = nu!(cwd: sandbox.cwd(), "cp {} -u newest_valid.txt valid.txt; open valid.txt", progress_flag); assert_eq!(actual.out, "newest_body"); // when destination doesn't exist let actual = nu!(cwd: sandbox.cwd(), "cp {} -u newest_valid.txt des_missing.txt; open des_missing.txt", progress_flag); assert_eq!(actual.out, "newest_body"); }); } #[test] fn cp_with_cd() { Playground::setup("cp_test_37", |_dirs, sandbox| { sandbox .mkdir("tmp_dir") .with_files(&[FileWithContent("tmp_dir/file.txt", "body")]); let actual = nu!( cwd: sandbox.cwd(), r#"do { cd tmp_dir; let f = 'file.txt'; cp $f .. }; open file.txt"#, ); assert!(actual.out.contains("body")); }); }