From 124235d7745167fb7af61936631bfc6f699f73a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dorian=20P=C3=A9ron?= Date: Mon, 12 Oct 2026 20:59:41 +0200 Subject: [PATCH 1/3] uutests: Uniformize `AtPath` interfaces to accept `AsRef` --- tests/by-util/test_chmod.rs | 20 ++-- tests/by-util/test_cp.rs | 150 ++++++++++++-------------- tests/by-util/test_du.rs | 20 ++-- tests/by-util/test_install.rs | 24 ++--- tests/by-util/test_ln.rs | 2 +- tests/by-util/test_ls.rs | 41 ++++---- tests/by-util/test_mkfifo.rs | 2 +- tests/by-util/test_mknod.rs | 2 +- tests/by-util/test_more.rs | 2 +- tests/by-util/test_mv.rs | 2 +- tests/by-util/test_realpath.rs | 2 +- tests/by-util/test_rm.rs | 2 +- tests/by-util/test_truncate.rs | 2 +- tests/uutests/src/lib/macros.rs | 18 +++- tests/uutests/src/lib/util.rs | 180 ++++++++++++++------------------ 15 files changed, 221 insertions(+), 248 deletions(-) diff --git a/tests/by-util/test_chmod.rs b/tests/by-util/test_chmod.rs index 9e8aa224809..4fe98a4a6f0 100644 --- a/tests/by-util/test_chmod.rs +++ b/tests/by-util/test_chmod.rs @@ -581,7 +581,7 @@ fn test_chmod_recursive_reports_every_unreadable_directory() { let (at, mut ucmd) = at_and_ucmd!(); let dirs = ["d/a", "d/b", "d/c"]; for dir in dirs { - at.mkdir_all(&format!("{dir}/y")); + at.mkdir_all(format!("{dir}/y")); set_permissions(at.plus_as_string(dir), Permissions::from_mode(0o311)).unwrap(); } @@ -833,7 +833,7 @@ fn test_chmod_symlink_non_existing_file_recursive() { let test_directory = "test_chmod_symlink_non_existing_file_directory"; at.mkdir(test_directory); - at.symlink_file(non_existing, &format!("{test_directory}/{test_symlink}")); + at.symlink_file(non_existing, format!("{test_directory}/{test_symlink}")); // this should succeed scene @@ -1466,7 +1466,7 @@ fn test_chmod_traverse_symlink_combo() { at.mkdir(directory); at.touch(target); - at.symlink_file(target, &format!("{directory}/{symlink}")); + at.symlink_file(target, format!("{directory}/{symlink}")); set_permissions(at.plus(target), Permissions::from_mode(0o664)).unwrap(); @@ -1485,7 +1485,7 @@ fn test_chmod_traverse_symlink_combo() { ); let actual_symlink = at - .symlink_metadata(&format!("{directory}/{symlink}")) + .symlink_metadata(format!("{directory}/{symlink}")) .permissions() .mode(); assert_eq!( @@ -1531,7 +1531,7 @@ fn test_chmod_recursive_symlink_to_directory_command_line() { .no_stderr(); let actual_file_perms = at - .metadata(&format!("{target_dir}/{file_in_target}")) + .metadata(format!("{target_dir}/{file_in_target}")) .permissions() .mode(); @@ -1573,7 +1573,7 @@ fn test_chmod_recursive_symlink_during_traversal() { at.mkdir(directory); at.mkdir(target_dir); at.touch(format!("{target_dir}/{file_in_target}")); - at.symlink_dir(target_dir, &format!("{directory}/{symlink_to_dir}")); + at.symlink_dir(target_dir, format!("{directory}/{symlink_to_dir}")); set_permissions( at.plus(format!("{target_dir}/{file_in_target}")), @@ -1589,7 +1589,7 @@ fn test_chmod_recursive_symlink_during_traversal() { .no_stderr(); let actual_file_perms = at - .metadata(&format!("{target_dir}/{file_in_target}")) + .metadata(format!("{target_dir}/{file_in_target}")) .permissions() .mode(); @@ -1625,8 +1625,8 @@ fn test_chmod_recursive_symlink_combinations() { at.mkdir(target_dir); at.touch(target_file); at.touch(format!("{target_dir}/{file_in_target}")); - at.symlink_dir(target_dir, &format!("{directory}/{symlink_to_dir}")); - at.symlink_file(target_file, &format!("{directory}/{symlink_to_file}")); + at.symlink_dir(target_dir, format!("{directory}/{symlink_to_dir}")); + at.symlink_file(target_file, format!("{directory}/{symlink_to_file}")); set_permissions(at.plus(target_file), Permissions::from_mode(0o644)).unwrap(); set_permissions( @@ -1648,7 +1648,7 @@ fn test_chmod_recursive_symlink_combinations() { // Both target file and file in target directory should have permissions changed assert_eq!(at.metadata(target_file).permissions().mode(), 0o100_600); assert_eq!( - at.metadata(&format!("{target_dir}/{file_in_target}")) + at.metadata(format!("{target_dir}/{file_in_target}")) .permissions() .mode(), 0o100_600 diff --git a/tests/by-util/test_cp.rs b/tests/by-util/test_cp.rs index b30e774a13f..d9e4d14c964 100644 --- a/tests/by-util/test_cp.rs +++ b/tests/by-util/test_cp.rs @@ -11,7 +11,7 @@ use uucore::display::Quotable; #[cfg(all(feature = "selinux", any(target_os = "linux", target_os = "android")))] use uucore::selinux::get_getfattr_output; use uutests::util::TestScenario; -use uutests::{at_and_ucmd, new_ucmd, path_concat, util_name}; +use uutests::{at_and_ucmd, new_ucmd, path, util_name}; use std::fs::set_permissions; @@ -686,7 +686,7 @@ fn test_cp_recursive_continues_after_skipped_file(#[case] arg: &str, #[case] suc .map(|entry| entry.unwrap().file_name().to_str().unwrap().to_owned()) .collect::>(); let (skipped, copied) = (&order[0], &order[1]); - at.write(&format!("destination/source/{skipped}"), "old contents"); + at.write(format!("destination/source/{skipped}"), "old contents"); ucmd.args(&["-R", arg, "source", "destination"]); // Only the prompt reads stdin; writing to a cp that never reads it can fail. @@ -703,12 +703,12 @@ fn test_cp_recursive_continues_after_skipped_file(#[case] arg: &str, #[case] suc } assert_eq!( - at.read(&format!("destination/source/{skipped}")), + at.read(format!("destination/source/{skipped}")), "old contents" ); assert_eq!( - at.read(&format!("destination/source/{copied}")), - at.read(&format!("source/{copied}")) + at.read(format!("destination/source/{copied}")), + at.read(format!("source/{copied}")) ); } @@ -1553,7 +1553,7 @@ fn test_cp_backup_control(#[case] args: &[&str], #[case] backup_suffix: Option<& assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n"); match backup_suffix { Some(suffix) => assert_eq!( - at.read(&format!("{TEST_HOW_ARE_YOU_SOURCE}{suffix}")), + at.read(format!("{TEST_HOW_ARE_YOU_SOURCE}{suffix}")), "How are you?\n" ), None => assert!(!at.file_exists(format!("{TEST_HOW_ARE_YOU_SOURCE}~"))), @@ -1622,7 +1622,7 @@ fn test_cp_custom_backup_suffix_via_env() { assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n"); assert_eq!( - at.read(&format!("{TEST_HOW_ARE_YOU_SOURCE}{suffix}")), + at.read(format!("{TEST_HOW_ARE_YOU_SOURCE}{suffix}")), "How are you?\n" ); } @@ -1631,7 +1631,7 @@ fn test_cp_custom_backup_suffix_via_env() { #[cfg(unix)] fn test_cp_backup_existing_target_is_fifo() { let (at, mut ucmd) = at_and_ucmd!(); - at.mkfifo(&format!("{TEST_HOW_ARE_YOU_SOURCE}~")); + at.mkfifo(format!("{TEST_HOW_ARE_YOU_SOURCE}~")); ucmd.arg("--backup=simple") .arg(TEST_HELLO_WORLD_SOURCE) @@ -1641,9 +1641,9 @@ fn test_cp_backup_existing_target_is_fifo() { .no_stderr(); assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n"); - assert!(!at.is_fifo(&format!("{TEST_HOW_ARE_YOU_SOURCE}~"))); + assert!(!at.is_fifo(format!("{TEST_HOW_ARE_YOU_SOURCE}~"))); assert_eq!( - at.read(&format!("{TEST_HOW_ARE_YOU_SOURCE}~")), + at.read(format!("{TEST_HOW_ARE_YOU_SOURCE}~")), "How are you?\n" ); } @@ -1665,7 +1665,7 @@ fn test_cp_numbered_if_existing_backup(#[case] backup_arg: &str) { assert!(at.file_exists(TEST_HOW_ARE_YOU_SOURCE)); assert!(at.file_exists(existing_backup)); assert_eq!( - at.read(&format!("{TEST_HOW_ARE_YOU_SOURCE}.~2~")), + at.read(format!("{TEST_HOW_ARE_YOU_SOURCE}.~2~")), "How are you?\n" ); } @@ -1695,9 +1695,9 @@ fn test_cp_backup_simple_allows_hardlink_under_another_name() { // `other` shares an inode with the backup path but is not named after it, // so the backup rename cannot clobber it. GNU copies this happily. let (at, mut ucmd) = at_and_ucmd!(); - let backup = format!("{TEST_HELLO_WORLD_SOURCE}~"); - at.write(&backup, "backup content"); - at.hard_link(&backup, "other"); + let backup = &format!("{TEST_HELLO_WORLD_SOURCE}~"); + at.write(backup, "backup content"); + at.hard_link(backup, "other"); ucmd.arg("--backup=simple") .arg("other") @@ -1712,8 +1712,8 @@ fn test_cp_backup_simple_allows_hardlink_under_another_name() { fn test_cp_backup_simple_protect_source_regardless_of_spelling() { // The guard compares files, not the strings naming them. let (at, mut ucmd) = at_and_ucmd!(); - let source = format!("{TEST_HELLO_WORLD_SOURCE}~"); - at.write(&source, "source content"); + let source = &format!("{TEST_HELLO_WORLD_SOURCE}~"); + at.write(source, "source content"); ucmd.arg("--backup=simple") .arg(format!("./{source}")) @@ -1721,22 +1721,22 @@ fn test_cp_backup_simple_protect_source_regardless_of_spelling() { .fails() .stderr_contains("might destroy source"); - assert_eq!(at.read(&source), "source content"); + assert_eq!(at.read(source), "source content"); } #[test] fn test_cp_backup_numbered_allows_source_named_like_backup() { // A numbered backup never reuses an existing name, so nothing is at risk. let (at, mut ucmd) = at_and_ucmd!(); - let source = format!("{TEST_HELLO_WORLD_SOURCE}~"); - at.write(&source, "source content"); + let source = &format!("{TEST_HELLO_WORLD_SOURCE}~"); + at.write(source, "source content"); ucmd.arg("--backup=numbered") - .arg(&source) + .arg(source) .arg(TEST_HELLO_WORLD_SOURCE) .succeeds(); - assert_eq!(at.read(&source), "source content"); + assert_eq!(at.read(source), "source content"); assert_eq!(at.read(TEST_HELLO_WORLD_SOURCE), "source content"); } @@ -1827,7 +1827,7 @@ fn test_cp_no_deref() { .join(TEST_HELLO_WORLD_SOURCE_SYMLINK); assert!( at.is_symlink( - &path_to_new_symlink + path_to_new_symlink .clone() .into_os_string() .into_string() @@ -1911,7 +1911,7 @@ fn test_cp_parents() { .succeeds(); assert_eq!( - at.read(&format!( + at.read(format!( "{TEST_COPY_TO_FOLDER}/{TEST_COPY_FROM_FOLDER_FILE}" )), "Hello, World!\n" @@ -1929,13 +1929,13 @@ fn test_cp_parents_multiple_files() { .succeeds(); assert_eq!( - at.read(&format!( + 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}")), + at.read(format!("{TEST_COPY_TO_FOLDER}/{TEST_HOW_ARE_YOU_SOURCE}")), "How are you?\n" ); } @@ -2123,8 +2123,8 @@ fn test_cp_parents_existing_dirs() { at.set_mode("a", 0o777); at.set_mode("a/b", 0o750); for dest in ["none_file", "none_dir", "new_file", "new_dir", "new_plain"] { - at.mkdir_all(&format!("{dest}/a")); - at.set_mode(&format!("{dest}/a"), 0o700); + at.mkdir_all(format!("{dest}/a")); + at.set_mode(format!("{dest}/a"), 0o700); } for args in [ @@ -2714,12 +2714,8 @@ fn test_cp_deref_folder_to_folder() { let path_to_new_symlink = at.plus(TEST_COPY_FROM_FOLDER); 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(), + path_to_new_symlink.join(TEST_HELLO_WORLD_SOURCE), + path_to_new_symlink.join(TEST_HELLO_WORLD_SOURCE_SYMLINK), ); //using -P -R option @@ -2816,12 +2812,8 @@ fn test_cp_no_deref_folder_to_folder() { let path_to_new_symlink = at.plus(TEST_COPY_FROM_FOLDER); 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(), + path_to_new_symlink.join(TEST_HELLO_WORLD_SOURCE), + path_to_new_symlink.join(TEST_HELLO_WORLD_SOURCE_SYMLINK), ); //using -P -R option @@ -2884,7 +2876,7 @@ fn test_cp_no_deref_folder_to_folder() { .join(TEST_HELLO_WORLD_SOURCE_SYMLINK); assert!( at.is_symlink( - &path_to_new_symlink + path_to_new_symlink .clone() .into_os_string() .into_string() @@ -2966,8 +2958,8 @@ fn test_cp_archive_recursive() { at.touch(file_1); at.touch(file_2); - at.symlink_file("1", &file_1_link.to_string_lossy()); - at.symlink_file("2", &file_2_link.to_string_lossy()); + at.symlink_file("1", &file_1_link); + at.symlink_file("2", &file_2_link); ucmd.arg("--archive") .arg(TEST_COPY_TO_FOLDER) @@ -2993,22 +2985,8 @@ fn test_cp_archive_recursive() { assert!(at.file_exists(at.subdir.join(TEST_COPY_TO_FOLDER_NEW).join("1"))); assert!(at.file_exists(at.subdir.join(TEST_COPY_TO_FOLDER_NEW).join("2"))); - 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() - ) - ); + assert!(at.is_symlink(at.subdir.join(TEST_COPY_TO_FOLDER_NEW).join("1.link"))); + assert!(at.is_symlink(at.subdir.join(TEST_COPY_TO_FOLDER_NEW).join("2.link"))); } #[test] @@ -3927,8 +3905,8 @@ fn test_preserve_mode() { fn test_canonicalize_symlink() { let (at, mut ucmd) = at_and_ucmd!(); at.mkdir("dir"); - at.touch("dir/file"); - at.relative_symlink_file("../dir/file", "dir/file-ln"); + at.touch(path!("dir", "file")); + at.relative_symlink_file(path!("..", "dir", "file"), path!("dir", "file-ln")); ucmd.arg("dir/file-ln").arg(".").succeeds().no_output(); } @@ -3939,8 +3917,12 @@ fn test_copy_through_just_created_symlink() { at.mkdir("a"); at.mkdir("b"); at.mkdir("c"); - at.relative_symlink_file("../t", "a/1"); - at.write("b/1", "hello"); + let parent_t = path!("..", "t"); + let a_one = path!("a", "1"); + let b_one = path!("b", "1"); + + at.relative_symlink_file(parent_t, a_one); + at.write(b_one, "hello"); if create_t { at.write("t", "world"); } @@ -4524,12 +4506,14 @@ fn test_cp_recursive_dest_subdir_is_file() { at.mkdir_all("dst/src"); at.write("dst/src/sub", "kept"); - let dest = path_concat!("dst", "src", "sub"); - let source = path_concat!("src", "sub"); + let dest = path!("dst", "src", "sub"); + let source = path!("src", "sub"); ucmd.args(&["-r", "src", "dst"]) .fails() .stderr_only(format!( - "cp: cannot overwrite non-directory '{dest}' with directory '{source}'\n" + "cp: cannot overwrite non-directory '{}' with directory '{}'\n", + dest.display(), + source.display() )); assert_eq!(at.read("dst/src/sub"), "kept"); assert_eq!(at.read("dst/src/other"), "other"); @@ -4713,13 +4697,13 @@ fn test_cp_copy_symlink_contents_recursive() { at.mkdir("dest-dir"); at.write("f", "f"); at.relative_symlink_file("f", "slink"); - at.relative_symlink_file("no-file", &path_concat!("src-dir", "slink")); + at.relative_symlink_file("no-file", &path!("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.dir_exists(&path!("dest-dir", "src-dir"))); + let regular_file = path!("dest-dir", "slink"); assert!(!at.symlink_exists(®ular_file) && at.file_exists(®ular_file)); assert_eq!(at.read(®ular_file), "f"); } @@ -7452,7 +7436,7 @@ fn test_dir_perm_race_with_preserve_mode_and_ownership() { } sleep(Duration::from_millis(100)); } - let mode = at.metadata(&format!("{DEST_DIR}/{SRC_DIR}")).mode(); + let mode = at.metadata(format!("{DEST_DIR}/{SRC_DIR}")).mode(); #[allow(clippy::unnecessary_cast, clippy::cast_lossless)] let mask = if attr == "mode" { libc::S_IWGRP | libc::S_IWOTH @@ -7523,15 +7507,15 @@ fn test_preserve_attrs_overriding_2() { let scene = TestScenario::new(util_name!()); let at = &scene.fixtures; at.mkdir(FOLDER); - at.make_file(&format!("{FOLDER}/{FILE1}")); - at.set_mode(&format!("{FOLDER}/{FILE1}"), 0o775); - at.hard_link(&format!("{FOLDER}/{FILE1}"), &format!("{FOLDER}/{FILE2}")); + at.make_file(format!("{FOLDER}/{FILE1}")); + at.set_mode(format!("{FOLDER}/{FILE1}"), 0o775); + at.hard_link(format!("{FOLDER}/{FILE1}"), format!("{FOLDER}/{FILE2}")); args.append(&mut vec![FOLDER, DEST]); - let src_file1_metadata = at.metadata(&format!("{FOLDER}/{FILE1}")); + let src_file1_metadata = at.metadata(format!("{FOLDER}/{FILE1}")); scene.ucmd().args(&args).succeeds(); at.dir_exists(DEST); - let dest_file1_metadata = at.metadata(&format!("{DEST}/{FILE1}")); - let dest_file2_metadata = at.metadata(&format!("{DEST}/{FILE2}")); + let dest_file1_metadata = at.metadata(format!("{DEST}/{FILE1}")); + let dest_file2_metadata = at.metadata(format!("{DEST}/{FILE2}")); assert_eq!( src_file1_metadata.modified().unwrap(), dest_file1_metadata.modified().unwrap() @@ -7752,8 +7736,12 @@ fn test_copy_symlink_overwrite() { at.mkdir("c"); at.write("t", "hello"); - at.relative_symlink_file("../t", "a/1"); - at.relative_symlink_file("../t", "b/1"); + + let parent_t = &path!("..", "t"); + let a_one = path!("a", "1"); + let b_one = path!("b", "1"); + at.relative_symlink_file(parent_t, a_one); + at.relative_symlink_file(parent_t, b_one); ucmd.arg("--no-dereference") .arg("a/1") @@ -7936,7 +7924,7 @@ fn test_cp_archive_preserves_directory_permissions() { let path = format!("test-images/{subdir}"); at.mkdir(&path); at.set_mode(&path, mode); - at.write(&format!("{path}/test{}.txt", i + 1), "test content"); + at.write(format!("{path}/test{}.txt", i + 1), "test content"); } ucmd.arg("-a") @@ -8149,7 +8137,7 @@ fn test_cp_selinux() { selinux_perm.contains("unconfined_u"), "Expected 'foo' not found in getfattr output:\n{selinux_perm}" ); - at.remove(&at.plus_as_string(TEST_HELLO_WORLD_DEST)); + at.remove(at.plus_as_string(TEST_HELLO_WORLD_DEST)); } } @@ -8252,7 +8240,7 @@ fn test_cp_preserve_selinux() { assert_metadata_eq!(metadata_src, metadata_dst); } - at.remove(&at.plus_as_string(TEST_HELLO_WORLD_DEST)); + at.remove(at.plus_as_string(TEST_HELLO_WORLD_DEST)); } } @@ -8312,7 +8300,7 @@ fn test_cp_preserve_selinux_admin_context() { assert_eq!(default_context, selinux_perm_dest); - at.remove(&at.plus_as_string(TEST_HELLO_WORLD_DEST)); + at.remove(at.plus_as_string(TEST_HELLO_WORLD_DEST)); } #[test] diff --git a/tests/by-util/test_du.rs b/tests/by-util/test_du.rs index 39cc4626997..2030aa3c78b 100644 --- a/tests/by-util/test_du.rs +++ b/tests/by-util/test_du.rs @@ -165,7 +165,7 @@ fn test_du_with_posixly_correct() { let dir = "a"; at.mkdir(dir); - at.write(&format!("{dir}/file"), "some content"); + at.write(format!("{dir}/file"), "some content"); let expected = ts .ucmd() @@ -206,7 +206,7 @@ fn test_du_zero_env_block_size() { let dir = "a"; at.mkdir(dir); - at.write(&format!("{dir}/file"), "some content"); + at.write(format!("{dir}/file"), "some content"); let expected = ts .ucmd() @@ -232,7 +232,7 @@ fn test_du_zero_env_block_size_hierarchy() { let dir = "a"; at.mkdir(dir); - at.write(&format!("{dir}/file"), "some content"); + at.write(format!("{dir}/file"), "some content"); let expected = ts .ucmd() @@ -269,7 +269,7 @@ fn test_du_env_block_size_hierarchy() { let dir = "a"; at.mkdir(dir); - at.write(&format!("{dir}/file"), "some content"); + at.write(format!("{dir}/file"), "some content"); let expected = ts .ucmd() @@ -396,7 +396,7 @@ fn test_du_invalid_env_block_size_stops_lookup() { let dir = "a"; at.mkdir(dir); - at.write(&format!("{dir}/file"), "some content"); + at.write(format!("{dir}/file"), "some content"); let default_output = ts .ucmd() @@ -436,7 +436,7 @@ fn test_du_posixly_correct_default() { let dir = "a"; at.mkdir(dir); - at.write(&format!("{dir}/file"), "some content"); + at.write(format!("{dir}/file"), "some content"); let expected = ts .ucmd() @@ -2198,7 +2198,7 @@ fn test_du_very_deep_directory() { at.mkdir_all(¤t_path); } - at.write(&format!("{current_path}/file.txt"), "deep file"); + at.write(format!("{current_path}/file.txt"), "deep file"); let result = ts.ucmd().arg("-s").arg("x").succeeds(); assert!(result.stdout_str().contains('x')); @@ -2222,9 +2222,9 @@ fn test_du_safe_traversal_with_symlinks() { at.mkdir_all(&deep_path); } - at.write(&format!("{deep_path}/target.txt"), "target content"); + at.write(format!("{deep_path}/target.txt"), "target content"); - at.symlink_file(&format!("{deep_path}/target.txt"), "shallow_link.txt"); + at.symlink_file(format!("{deep_path}/target.txt"), "shallow_link.txt"); let result = ts.ucmd().arg("-L").arg("shallow_link.txt").succeeds(); assert!(!result.stdout_str().is_empty()); @@ -2357,7 +2357,7 @@ fn test_du_long_path_from_unreadable() { at.mkdir_all(¤t_path); } - at.write(&format!("{current_path}/test.txt"), "test content"); + at.write(format!("{current_path}/test.txt"), "test content"); at.mkdir("inaccessible"); diff --git a/tests/by-util/test_install.rs b/tests/by-util/test_install.rs index 5a29f47fb41..022cea63c42 100644 --- a/tests/by-util/test_install.rs +++ b/tests/by-util/test_install.rs @@ -993,12 +993,9 @@ fn test_install_on_invalid_link_at_destination() { let dst_dir = at.plus("dest"); at.touch("test.sh"); - at.symlink_file( - "/opt/FakeDestination", - &dst_dir.join("test.sh").to_string_lossy(), - ); + at.symlink_file("/opt/FakeDestination", dst_dir.join("test.sh")); scene.ccmd("chmod").arg("+x").arg("test.sh").succeeds(); - at.symlink_file("test.sh", &src_dir.join("test.sh").to_string_lossy()); + at.symlink_file("test.sh", src_dir.join("test.sh")); scene .ucmd() @@ -1021,12 +1018,9 @@ fn test_install_on_invalid_link_at_destination_and_dev_null_at_source() { let dst_dir = at.plus("dest"); at.touch("test.sh"); - at.symlink_file( - "/opt/FakeDestination", - &dst_dir.join("test.sh").to_string_lossy(), - ); + at.symlink_file("/opt/FakeDestination", dst_dir.join("test.sh")); scene.ccmd("chmod").arg("+x").arg("test.sh").succeeds(); - at.symlink_file("test.sh", &src_dir.join("test.sh").to_string_lossy()); + at.symlink_file("test.sh", src_dir.join("test.sh")); scene .ucmd() @@ -1300,7 +1294,7 @@ fn test_install_backup_control(#[case] args: &[&str], #[case] backup_suffix: Opt assert_eq!(at.read(source), "new"); assert_eq!(at.read(dest), "new"); match backup_suffix { - Some(suffix) => assert_eq!(at.read(&format!("{dest}{suffix}")), "old"), + Some(suffix) => assert_eq!(at.read(format!("{dest}{suffix}")), "old"), None => assert!(!at.file_exists(format!("{dest}~"))), } } @@ -2125,7 +2119,7 @@ fn test_install_failing_copy_file_to_target_contain_subdir_with_same_name() { let dir1 = "dir1"; at.touch(file); - at.mkdir_all(&format!("{dir1}/{file}")); + at.mkdir_all(format!("{dir1}/{file}")); ucmd.arg(file) .arg(dir1) .fails() @@ -2399,7 +2393,7 @@ fn test_selinux() { let Ok(context_value) = context_value else { println!("Skipping SELinux test: getfattr not available or failed"); - at.remove(&at.plus_as_string(dest)); + at.remove(at.plus_as_string(dest)); continue; }; @@ -2407,7 +2401,7 @@ fn test_selinux() { context_value.contains("unconfined_u"), "Expected 'unconfined_u' not found in getfattr output:\n{context_value}" ); - at.remove(&at.plus_as_string(dest)); + at.remove(at.plus_as_string(dest)); } } @@ -2439,7 +2433,7 @@ fn test_selinux_invalid_args() { .fails() .stderr_contains("failed to set default file creation"); - at.remove(&at.plus_as_string(dest)); + at.remove(at.plus_as_string(dest)); } } diff --git a/tests/by-util/test_ln.rs b/tests/by-util/test_ln.rs index fb8652410ae..5c7ec10378c 100644 --- a/tests/by-util/test_ln.rs +++ b/tests/by-util/test_ln.rs @@ -454,7 +454,7 @@ fn test_symlink_suffix_without_backup_option() { assert_eq!(at.read("a"), "a\n"); assert_eq!(at.read("b"), "a\n"); // we should have created backup for b file - assert_eq!(at.read(&format!("b{suffix}")), "b2\n"); + assert_eq!(at.read(format!("b{suffix}")), "b2\n"); } #[test] diff --git a/tests/by-util/test_ls.rs b/tests/by-util/test_ls.rs index 91445abd734..65d671a86ad 100644 --- a/tests/by-util/test_ls.rs +++ b/tests/by-util/test_ls.rs @@ -32,7 +32,7 @@ use uutests::unwrap_or_return; use uutests::util::TestScenario; #[cfg(any(unix, feature = "selinux"))] use uutests::util::expected_result; -use uutests::{at_and_ucmd, util_name}; +use uutests::{at_and_ucmd, path, util_name}; const LONG_ARGS: &[&str] = &[ "-l", @@ -3524,17 +3524,10 @@ mod quoting { fn test_ls_color() { let scene = TestScenario::new(util_name!()); let at = &scene.fixtures; - at.mkdir("a"); - let nested_dir = Path::new("a") - .join("nested_dir") - .to_string_lossy() - .to_string(); - at.mkdir(nested_dir); + let nested_dir = Path::new("a").join("nested_dir"); + at.mkdir_all(nested_dir); at.mkdir("z"); - let nested_file = Path::new("a") - .join("nested_file") - .to_string_lossy() - .to_string(); + let nested_file = Path::new("a").join("nested_file"); at.touch(nested_file); at.touch("test-color"); @@ -5550,7 +5543,7 @@ fn test_ls_dereference_looped_symlinks_recursive() { let (at, mut ucmd) = at_and_ucmd!(); at.mkdir("loop"); - at.relative_symlink_dir("../loop", "loop/sub"); + at.relative_symlink_dir(path!("..", "loop"), path!("loop", "sub")); ucmd.args(&["-RL", "loop"]) .fails_with_code(2) @@ -5574,14 +5567,17 @@ fn test_dereference_dangling_color() { #[test] fn test_dereference_symlink_dir_color() { let (at, mut ucmd) = at_and_ucmd!(); + let dir1_link = &path!("dir1", "link"); + let parent_dir2 = path!("..", "dir2"); + at.mkdir("dir1"); - at.mkdir("dir1/link"); + at.mkdir(dir1_link); let out_exp = ucmd.args(&["--color", "dir1"]).succeeds().stdout_move_str(); let (at, mut ucmd) = at_and_ucmd!(); at.mkdir("dir1"); at.mkdir("dir2"); - at.relative_symlink_dir("../dir2", "dir1/link"); + at.relative_symlink_dir(parent_dir2, dir1_link); ucmd.args(&["-L", "--color", "dir1"]) .succeeds() .stdout_is(out_exp); @@ -5590,14 +5586,16 @@ fn test_dereference_symlink_dir_color() { #[test] fn test_dereference_symlink_file_color() { let (at, mut ucmd) = at_and_ucmd!(); + let dir1_link = &path!("dir1", "link"); + at.mkdir("dir1"); - at.touch("dir1/link"); + at.touch(dir1_link); let out_exp = ucmd.args(&["--color", "dir1"]).succeeds().stdout_move_str(); let (at, mut ucmd) = at_and_ucmd!(); at.mkdir("dir1"); at.touch("file"); - at.relative_symlink_file("../file", "dir1/link"); + at.relative_symlink_file(path!("..", "file"), dir1_link); ucmd.args(&["-L", "--color", "dir1"]) .succeeds() .stdout_is(out_exp); @@ -8556,10 +8554,13 @@ fn test_ls_dereference_looped_symlinks_recursive_nested() { fn test_ls_dereference_cross_branch_alias_after_first_branch_completes() { let (at, mut ucmd) = at_and_ucmd!(); - at.mkdir_all("top/a/target"); - at.touch("top/a/target/file"); - at.mkdir_all("top/b"); - at.relative_symlink_dir("../a/target", "top/b/link"); + let top_a_target = &path!("top", "a", "target"); + let top_b = &path!("top", "b"); + + at.mkdir_all(top_a_target); + at.touch(top_a_target.join("file")); + at.mkdir_all(top_b); + at.relative_symlink_dir(path!("..", "a", "target"), top_b.join("link")); let result = ucmd.args(&["-RL", "top"]).succeeds(); result diff --git a/tests/by-util/test_mkfifo.rs b/tests/by-util/test_mkfifo.rs index 58b93d358f1..ef275ee58d8 100644 --- a/tests/by-util/test_mkfifo.rs +++ b/tests/by-util/test_mkfifo.rs @@ -187,7 +187,7 @@ fn test_mkfifo_selinux() { context_value.contains("unconfined_u"), "Expected 'unconfined_u' not found in getfattr output:\n{context_value}" ); - at.remove(&at.plus_as_string(dest)); + at.remove(at.plus_as_string(dest)); } } diff --git a/tests/by-util/test_mknod.rs b/tests/by-util/test_mknod.rs index 9c9bc9730a3..ab6eca8c018 100644 --- a/tests/by-util/test_mknod.rs +++ b/tests/by-util/test_mknod.rs @@ -289,7 +289,7 @@ fn test_mknod_selinux() { context_value.contains("unconfined_u"), "Expected 'unconfined_u' not found in getfattr output:\n{context_value}" ); - at.remove(&at.plus_as_string(dest)); + at.remove(at.plus_as_string(dest)); } } diff --git a/tests/by-util/test_more.rs b/tests/by-util/test_more.rs index 1fc51d068dd..3b76bec06a0 100644 --- a/tests/by-util/test_more.rs +++ b/tests/by-util/test_more.rs @@ -208,7 +208,7 @@ fn test_more_non_utf8_paths() { let file_name = std::ffi::OsStr::from_bytes(b"test_\xFF\xFE.txt"); // Create test file with normal name first at.write( - &file_name.to_string_lossy(), + file_name.to_string_lossy().to_string(), "test content for non-UTF-8 file", ); diff --git a/tests/by-util/test_mv.rs b/tests/by-util/test_mv.rs index 9759e142e07..175388a557c 100644 --- a/tests/by-util/test_mv.rs +++ b/tests/by-util/test_mv.rs @@ -881,7 +881,7 @@ fn test_mv_backup_control(#[case] args: &[&str], #[case] backup_suffix: Option<& assert!(!at.file_exists(source)); assert_eq!(at.read(dest), "new"); match backup_suffix { - Some(suffix) => assert_eq!(at.read(&format!("{dest}{suffix}")), "old"), + Some(suffix) => assert_eq!(at.read(format!("{dest}{suffix}")), "old"), None => assert!(!at.file_exists(format!("{dest}~"))), } } diff --git a/tests/by-util/test_realpath.rs b/tests/by-util/test_realpath.rs index 14d67f57143..0a2b6b17214 100644 --- a/tests/by-util/test_realpath.rs +++ b/tests/by-util/test_realpath.rs @@ -220,7 +220,7 @@ fn test_realpath_symlink_follow_limit() { at.touch("target"); at.relative_symlink_file("target", "link1"); for i in 2..=41 { - at.relative_symlink_file(&format!("link{}", i - 1), &format!("link{i}")); + at.relative_symlink_file(format!("link{}", i - 1), format!("link{i}")); } let expect = path_concat!(at.root_dir_resolved(), "target") + "\n"; scene.ucmd().arg("link40").succeeds().stdout_only(expect); diff --git a/tests/by-util/test_rm.rs b/tests/by-util/test_rm.rs index 242863e7d31..2ffafab53e7 100644 --- a/tests/by-util/test_rm.rs +++ b/tests/by-util/test_rm.rs @@ -1289,7 +1289,7 @@ fn test_rm_recursive_long_path_safe_traversal() { } at.write("rm_deep/test1.txt", "content1"); - at.write(&format!("{deep_path}/test2.txt"), "content2"); + at.write(format!("{deep_path}/test2.txt"), "content2"); ts.ucmd().arg("-rf").arg("rm_deep").succeeds(); diff --git a/tests/by-util/test_truncate.rs b/tests/by-util/test_truncate.rs index 841fd53a9fe..8ee99aea8e4 100644 --- a/tests/by-util/test_truncate.rs +++ b/tests/by-util/test_truncate.rs @@ -513,7 +513,7 @@ fn test_truncate_non_utf8_paths() { let ts = TestScenario::new(util_name!()); let at = &ts.fixtures; let file_name = std::ffi::OsStr::from_bytes(b"test_\xFF\xFE.txt"); - at.write(&file_name.to_string_lossy(), "test content"); + at.write(file_name, "test content"); // Test that truncate can handle non-UTF-8 filenames ts.ucmd().arg("-s").arg("10").arg(file_name).succeeds(); diff --git a/tests/uutests/src/lib/macros.rs b/tests/uutests/src/lib/macros.rs index f94f17aec41..4446ce7471f 100644 --- a/tests/uutests/src/lib/macros.rs +++ b/tests/uutests/src/lib/macros.rs @@ -5,7 +5,7 @@ /// Platform-independent helper for constructing a `PathBuf` from individual elements #[macro_export] -macro_rules! path_concat { +macro_rules! path { ($e:expr, ..$n:expr) => {{ use std::path::PathBuf; let n = $n; @@ -13,7 +13,7 @@ macro_rules! path_concat { for _ in 0..n { pb.push($e); } - pb.to_str().unwrap().to_owned() + pb }}; ($($e:expr),*) => {{ use std::path::PathBuf; @@ -21,7 +21,19 @@ macro_rules! path_concat { $( pb.push($e); )* - pb.to_str().unwrap().to_owned() + pb + }}; +} + +/// Wrapper around [`path`] that casts the +/// [`std::path::PathBuf`] into a [`String`]. +#[macro_export] +macro_rules! path_concat { + ($e:expr, ..$n:expr) => {{ + ::uutests::path!($e, ..$n).to_str().unwrap().to_owned() + }}; + ($($e:expr),*) => {{ + ::uutests::path!($($e),*).to_str().unwrap().to_owned() }}; } diff --git a/tests/uutests/src/lib/util.rs b/tests/uutests/src/lib/util.rs index 598614874fb..7a79c247316 100644 --- a/tests/uutests/src/lib/util.rs +++ b/tests/uutests/src/lib/util.rs @@ -43,8 +43,6 @@ use std::os::unix::process::CommandExt; use std::os::unix::process::ExitStatusExt; #[cfg(windows)] use std::os::windows::fs::{symlink_dir, symlink_file}; -#[cfg(windows)] -use std::path::MAIN_SEPARATOR_STR; use std::path::{Path, PathBuf}; use std::process::{Child, Command, ExitStatus, Output, Stdio}; use std::rc::Rc; @@ -980,8 +978,8 @@ impl CmdResult { } } -pub fn log_info, U: AsRef>(msg: T, par: U) { - println!("{}: {}", msg.as_ref(), par.as_ref()); +pub fn log_info, U: std::fmt::Display>(msg: T, par: U) { + println!("{}: {par}", msg.as_ref()); } pub fn recursive_copy(src: &Path, dest: &Path) -> Result<()> { @@ -1050,17 +1048,16 @@ impl AtPath { } pub fn plus>(&self, name: P) -> PathBuf { - let mut pathbuf = self.subdir.clone(); - pathbuf.push(name); - pathbuf + let pathbuf = self.subdir.clone(); + pathbuf.join(name) } pub fn plus_as_string>(&self, name: P) -> String { self.plus(name).display().to_string() } - fn minus(&self, name: &str) -> PathBuf { - let prefixed = PathBuf::from(name); + fn minus>(&self, name: P) -> PathBuf { + let prefixed = name.as_ref().to_path_buf(); if prefixed.starts_with(&self.subdir) { let mut unprefixed = PathBuf::new(); for component in prefixed.components().skip(self.subdir.components().count()) { @@ -1072,48 +1069,49 @@ impl AtPath { } } - pub fn minus_as_string(&self, name: &str) -> String { + pub fn minus_as_string>(&self, name: P) -> String { String::from(self.minus(name).to_str().unwrap()) } - pub fn set_readonly(&self, name: &str) { - let metadata = fs::metadata(self.plus(name)).unwrap(); + pub fn set_readonly>(&self, name: P) { + let metadata = fs::metadata(self.plus(&name)).unwrap(); let mut permissions = metadata.permissions(); permissions.set_readonly(true); fs::set_permissions(self.plus(name), permissions).unwrap(); } - pub fn open(&self, name: &str) -> File { - log_info("open", self.plus_as_string(name)); + pub fn open>(&self, name: P) -> File { + log_info("open", self.plus_as_string(&name)); File::open(self.plus(name)).unwrap() } - pub fn read(&self, name: &str) -> String { + pub fn read>(&self, name: P) -> String { + let name = name.as_ref(); let mut f = self.open(name); let mut contents = String::new(); f.read_to_string(&mut contents) - .unwrap_or_else(|e| panic!("Couldn't read {name}: {e}")); + .unwrap_or_else(|e| panic!("Couldn't read {}: {e}", name.display())); contents } - pub fn read_bytes(&self, name: &str) -> Vec { + pub fn read_bytes>(&self, name: P) -> Vec { + let name = name.as_ref(); let mut f = self.open(name); let mut contents = Vec::new(); f.read_to_end(&mut contents) - .unwrap_or_else(|e| panic!("Couldn't read {name}: {e}")); + .unwrap_or_else(|e| panic!("Couldn't read {}: {e}", name.display())); contents } - pub fn write(&self, name: &str, contents: &str) { - log_info("write(default)", self.plus_as_string(name)); - fs::write(self.plus(name), contents) - .unwrap_or_else(|e| panic!("Couldn't write {name}: {e}")); + pub fn write>(&self, name: P, contents: &str) { + self.write_bytes(name, contents.as_bytes()); } - pub fn write_bytes(&self, name: &str, contents: &[u8]) { + pub fn write_bytes>(&self, name: P, contents: &[u8]) { + let name = name.as_ref(); log_info("write(default)", self.plus_as_string(name)); fs::write(self.plus(name), contents) - .unwrap_or_else(|e| panic!("Couldn't write {name}: {e}")); + .unwrap_or_else(|e| panic!("Couldn't write {}: {e}", name.display())); } pub fn append(&self, name: impl AsRef, contents: &str) { @@ -1128,7 +1126,8 @@ impl AtPath { .unwrap_or_else(|e| panic!("Couldn't write(append) {}: {e}", name.display())); } - pub fn append_bytes(&self, name: &str, contents: &[u8]) { + pub fn append_bytes>(&self, name: P, contents: &[u8]) { + let name = name.as_ref(); log_info("write(append)", self.plus_as_string(name)); let mut f = OpenOptions::new() .append(true) @@ -1136,10 +1135,11 @@ impl AtPath { .open(self.plus(name)) .unwrap(); f.write_all(contents) - .unwrap_or_else(|e| panic!("Couldn't write(append) to {name}: {e}")); + .unwrap_or_else(|e| panic!("Couldn't write(append) to {}: {e}", name.display())); } - pub fn truncate(&self, name: &str, contents: &str) { + pub fn truncate>(&self, name: P, contents: &str) { + let name = name.as_ref(); log_info("write(truncate)", self.plus_as_string(name)); let mut f = OpenOptions::new() .write(true) @@ -1148,10 +1148,10 @@ impl AtPath { .open(self.plus(name)) .unwrap(); f.write_all(contents.as_bytes()) - .unwrap_or_else(|e| panic!("Couldn't write(truncate) {name}: {e}")); + .unwrap_or_else(|e| panic!("Couldn't write(truncate) {}: {e}", name.display())); } - pub fn rename(&self, source: &str, target: &str) { + pub fn rename, P2: AsRef>(&self, source: P1, target: P2) { let source = self.plus(source); let target = self.plus(target); log_info( @@ -1167,14 +1167,14 @@ impl AtPath { }); } - pub fn remove(&self, source: &str) { + pub fn remove>(&self, source: P) { let source = self.plus(source); log_info("remove", format!("{}", source.display())); remove_file(&source) .unwrap_or_else(|e| panic!("Couldn't remove {}: {e}", source.display())); } - pub fn copy(&self, source: &str, target: &str) { + pub fn copy, P2: AsRef>(&self, source: P1, target: P2) { let source = self.plus(source); let target = self.plus(target); log_info("copy", format!("{} {}", source.display(), target.display())); @@ -1187,7 +1187,8 @@ impl AtPath { }); } - pub fn rmdir(&self, dir: &str) { + pub fn rmdir>(&self, dir: P) { + let dir = dir.as_ref(); log_info("rmdir", self.plus_as_string(dir)); fs::remove_dir(self.plus(dir)).unwrap(); } @@ -1198,26 +1199,28 @@ impl AtPath { fs::create_dir(self.plus(dir)).unwrap(); } - pub fn mkdir_all(&self, dir: &str) { + pub fn mkdir_all>(&self, dir: P) { + let dir = dir.as_ref(); log_info("mkdir_all", self.plus_as_string(dir)); fs::create_dir_all(self.plus(dir)).unwrap(); } - pub fn make_file(&self, name: &str) -> File { + pub fn make_file>(&self, name: P) -> File { + let name = name.as_ref(); match File::create(self.plus(name)) { Ok(f) => f, - Err(e) => panic!("{e}"), + Err(e) => panic!("Couldn't create file {}: {e}", name.display()), } } pub fn touch>(&self, file: P) { let file = file.as_ref(); log_info("touch", self.plus_as_string(file)); - File::create(self.plus(file)).unwrap(); + let _ = self.make_file(file); } #[cfg(not(windows))] - pub fn mkfifo(&self, fifo: &str) { + pub fn mkfifo>(&self, fifo: P) { // rustix::fs::mkfifoat is linux only use nix::sys::stat::Mode; let full_path = self.plus_as_string(fifo); @@ -1228,25 +1231,27 @@ impl AtPath { } #[cfg(unix)] - pub fn mksocket(&self, socket: &str) { + pub fn mksocket>(&self, socket: P) { let full_path = self.plus_as_string(socket); log_info("mksocket", &full_path); UnixListener::bind(full_path).expect("Socket file creation failed."); } #[cfg(not(windows))] - pub fn is_fifo(&self, fifo: &str) -> bool { + pub fn is_fifo>(&self, fifo: P) -> bool { stat::stat(&self.plus(fifo)) .is_ok_and(|s| SFlag::from_bits_truncate(s.st_mode).contains(SFlag::S_IFIFO)) } #[cfg(not(windows))] - pub fn is_char_device(&self, char_dev: &str) -> bool { + pub fn is_char_device>(&self, char_dev: P) -> bool { stat::stat(&self.plus(char_dev)) .is_ok_and(|s| SFlag::from_bits_truncate(s.st_mode).contains(SFlag::S_IFCHR)) } - pub fn hard_link(&self, original: &str, link: &str) { + pub fn hard_link, P2: AsRef>(&self, original: P1, link: P2) { + let original = original.as_ref(); + let link = link.as_ref(); log_info( "hard_link", format!( @@ -1258,59 +1263,46 @@ impl AtPath { hard_link(self.plus(original), self.plus(link)).unwrap(); } - pub fn symlink_file(&self, original: &str, link: &str) { + fn symlink_inner(&self, original: &Path, link: &Path) { log_info( "symlink", - format!( - "{},{}", - self.plus_as_string(original), - self.plus_as_string(link) - ), + format!("{},{}", original.display(), link.display()), ); - symlink_file(self.plus(original), self.plus(link)).unwrap(); + symlink_file(original, link).unwrap(); } - pub fn relative_symlink_file(&self, original: &str, link: &str) { - #[cfg(windows)] - let original = original.replace('/', MAIN_SEPARATOR_STR); - log_info( - "symlink", - format!("{original},{}", self.plus_as_string(link)), - ); - symlink_file(original, self.plus(link)).unwrap(); + pub fn symlink_file, P2: AsRef>(&self, original: P1, link: P2) { + self.symlink_inner(self.plus(original).as_ref(), self.plus(link).as_ref()); } - pub fn symlink_dir(&self, original: &str, link: &str) { - log_info( - "symlink", - format!( - "{},{}", - self.plus_as_string(original), - self.plus_as_string(link) - ), - ); - symlink_dir(self.plus(original), self.plus(link)).unwrap(); + pub fn relative_symlink_file, P2: AsRef>(&self, original: P1, link: P2) { + self.symlink_inner(original.as_ref(), self.plus(link).as_ref()); } - pub fn relative_symlink_dir(&self, original: &str, link: &str) { - #[cfg(windows)] - let original = original.replace('/', MAIN_SEPARATOR_STR); + fn symlink_dir_inner(&self, original: &Path, link: &Path) { log_info( "symlink", - format!("{original},{}", self.plus_as_string(link)), + format!("{},{}", original.display(), link.display()), ); - symlink_dir(original, self.plus(link)).unwrap(); + symlink_dir(original, link).unwrap(); } - pub fn is_symlink(&self, path: &str) -> bool { + pub fn symlink_dir, P2: AsRef>(&self, original: P1, link: P2) { + self.symlink_dir_inner(self.plus(original).as_ref(), self.plus(link).as_ref()); + } + + pub fn relative_symlink_dir, P2: AsRef>(&self, original: P1, link: P2) { + self.symlink_dir_inner(original.as_ref(), self.plus(link).as_ref()); + } + + pub fn is_symlink>(&self, path: P) -> bool { + let path = path.as_ref(); log_info("is_symlink", self.plus_as_string(path)); - match fs::symlink_metadata(self.plus(path)) { - Ok(m) => m.file_type().is_symlink(), - Err(_) => false, - } + fs::symlink_metadata(self.plus(path)).is_ok_and(|m| m.is_symlink()) } - pub fn resolve_link(&self, path: &str) -> String { + pub fn resolve_link>(&self, path: P) -> String { + let path = path.as_ref(); log_info("resolve_link", self.plus_as_string(path)); match fs::read_link(self.plus(path)) { Ok(p) => self.minus_as_string(p.to_str().unwrap()), @@ -1318,7 +1310,8 @@ impl AtPath { } } - pub fn read_symlink(&self, path: &str) -> String { + pub fn read_symlink>(&self, path: P) -> String { + let path = path.as_ref(); log_info("read_symlink", self.plus_as_string(path)); fs::read_link(self.plus(path)) .unwrap() @@ -1327,40 +1320,25 @@ impl AtPath { .to_owned() } - pub fn symlink_metadata(&self, path: &str) -> fs::Metadata { - match fs::symlink_metadata(self.plus(path)) { - Ok(m) => m, - Err(e) => panic!("{e}"), - } + pub fn symlink_metadata>(&self, path: P) -> fs::Metadata { + fs::symlink_metadata(self.plus(path)).unwrap() } - pub fn metadata(&self, path: &str) -> fs::Metadata { - match fs::metadata(self.plus(path)) { - Ok(m) => m, - Err(e) => panic!("{e}"), - } + pub fn metadata>(&self, path: P) -> fs::Metadata { + fs::metadata(self.plus(path)).unwrap() } pub fn file_exists>(&self, path: P) -> bool { - match fs::metadata(self.plus(path)) { - Ok(m) => m.is_file(), - Err(_) => false, - } + fs::metadata(self.plus(path)).is_ok_and(|m| m.is_file()) } /// Decide whether the named symbolic link exists in the test directory. pub fn symlink_exists>(&self, path: P) -> bool { - match fs::symlink_metadata(self.plus(path)) { - Ok(m) => m.file_type().is_symlink(), - Err(_) => false, - } + fs::symlink_metadata(self.plus(path)).is_ok_and(|m| m.is_symlink()) } pub fn dir_exists>(&self, path: P) -> bool { - match fs::metadata(self.plus(path)) { - Ok(m) => m.is_dir(), - Err(_) => false, - } + fs::metadata(self.plus(path)).is_ok_and(|m| m.is_dir()) } pub fn root_dir_resolved(&self) -> String { @@ -1404,7 +1382,7 @@ impl AtPath { /// This function panics if there is an error loading the metadata /// or setting the permissions of the file. #[cfg(not(windows))] - pub fn set_mode(&self, filename: &str, mode: u32) { + pub fn set_mode>(&self, filename: P, mode: u32) { let path = self.plus(filename); let mut perms = fs::metadata(&path).unwrap().permissions(); perms.set_mode(mode); From 6fe4eba8b5bd61731e5d40b519b5d828508b0abd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dorian=20P=C3=A9ron?= Date: Mon, 26 Oct 2026 11:32:57 +0100 Subject: [PATCH 2/3] uutests: Rewrite `AtPath::append` to use the `>` syntax --- tests/uutests/src/lib/util.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/uutests/src/lib/util.rs b/tests/uutests/src/lib/util.rs index 7a79c247316..22aea18e0fb 100644 --- a/tests/uutests/src/lib/util.rs +++ b/tests/uutests/src/lib/util.rs @@ -1114,7 +1114,7 @@ impl AtPath { .unwrap_or_else(|e| panic!("Couldn't write {}: {e}", name.display())); } - pub fn append(&self, name: impl AsRef, contents: &str) { + pub fn append>(&self, name: P, contents: &str) { let name = name.as_ref(); log_info("write(append)", self.plus_as_string(name)); let mut f = OpenOptions::new() From 001242df4b11f85262ae35848064f5859a5a3b28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dorian=20P=C3=A9ron?= Date: Wed, 21 Oct 2026 15:34:41 +0200 Subject: [PATCH 3/3] uutests: Reduce unnecessary string allocations in tests Replace uses of `at.plus_as_string()` with `at.plus()` where possible --- tests/by-util/test_cat.rs | 4 +- tests/by-util/test_chmod.rs | 38 ++++----- tests/by-util/test_chown.rs | 8 +- tests/by-util/test_chroot.rs | 2 +- tests/by-util/test_cp.rs | 37 ++++---- tests/by-util/test_dd.rs | 30 ++++--- tests/by-util/test_install.rs | 18 ++-- tests/by-util/test_ls.rs | 90 ++++++++++---------- tests/by-util/test_mkdir.rs | 4 +- tests/by-util/test_mkfifo.rs | 2 +- tests/by-util/test_mknod.rs | 4 +- tests/by-util/test_mv.rs | 29 +++---- tests/by-util/test_nohup.rs | 2 +- tests/by-util/test_sync.rs | 2 +- tests/by-util/test_touch.rs | 2 +- tests/by-util/test_uptime.rs | 6 +- tests/uutests/src/lib/util.rs | 156 +++++++++++++++------------------- 17 files changed, 203 insertions(+), 231 deletions(-) diff --git a/tests/by-util/test_cat.rs b/tests/by-util/test_cat.rs index 6dff8fe8558..a9dd4454de8 100644 --- a/tests/by-util/test_cat.rs +++ b/tests/by-util/test_cat.rs @@ -762,7 +762,7 @@ fn test_write_fast_read_error() { at.write("foo", "content"); // Remove read permissions to cause a read error - let file_path = at.plus_as_string("foo"); + let file_path = at.plus("foo"); let mut perms = std::fs::metadata(&file_path).unwrap().permissions(); perms.set_mode(0o000); // No permissions std::fs::set_permissions(&file_path, perms).unwrap(); @@ -803,7 +803,7 @@ fn test_appending_same_input_output() { let (at, mut ucmd) = at_and_ucmd!(); at.write("foo", "content"); - let foo_file = at.plus_as_string("foo"); + let foo_file = at.plus("foo"); let file_read = File::open(&foo_file).unwrap(); let file_write = OpenOptions::new().append(true).open(&foo_file).unwrap(); diff --git a/tests/by-util/test_chmod.rs b/tests/by-util/test_chmod.rs index 4fe98a4a6f0..6804ada2a79 100644 --- a/tests/by-util/test_chmod.rs +++ b/tests/by-util/test_chmod.rs @@ -24,20 +24,20 @@ struct TestCase { umask: Option, } -fn make_file(file: &str, mode: u32) { +fn make_file(file: impl AsRef, mode: u32) { OpenOptions::new() .mode(mode) .create(true) .write(true) - .open(file) + .open(&file) .unwrap(); - let mut perms = metadata(file).unwrap().permissions(); + let mut perms = metadata(&file).unwrap().permissions(); perms.set_mode(mode); set_permissions(file, perms).unwrap(); } fn run_single_test(test: &TestCase, at: &AtPath, mut ucmd: UCommand) { - make_file(&at.plus_as_string(TEST_FILE), test.before); + make_file(at.plus(TEST_FILE), test.before); let perms = at.metadata(TEST_FILE).permissions().mode(); assert_eq!( @@ -407,7 +407,7 @@ fn test_chmod_reference_file() { }, ]; let (at, ucmd) = at_and_ucmd!(); - make_file(&at.plus_as_string(REFERENCE_FILE), REFERENCE_PERMS); + make_file(at.plus(REFERENCE_FILE), REFERENCE_PERMS); run_single_test(&tests[0], &at, ucmd); } @@ -441,8 +441,8 @@ fn test_chmod_inaccessible_file_reports_permission_denied() { let at = &scene.fixtures; at.mkdir("locked"); - make_file(&at.plus_as_string("locked/file"), 0o100_644); - set_permissions(at.plus_as_string("locked"), Permissions::from_mode(0o000)).unwrap(); + make_file(at.plus("locked/file"), 0o100_644); + set_permissions(at.plus("locked"), Permissions::from_mode(0o000)).unwrap(); scene .ucmd() @@ -452,7 +452,7 @@ fn test_chmod_inaccessible_file_reports_permission_denied() { .stderr_is("chmod: cannot access 'locked/file': Permission denied\n"); // Restore search permission so the fixture directory can be cleaned up. - set_permissions(at.plus_as_string("locked"), Permissions::from_mode(0o755)).unwrap(); + set_permissions(at.plus("locked"), Permissions::from_mode(0o755)).unwrap(); } #[test] @@ -507,7 +507,7 @@ fn test_chmod_recursive_correct_exit_code() { // remove read permissions for folder a so the chmod command for a/b fails let mut perms = at.metadata("a").permissions(); perms.set_mode(0o000); - set_permissions(at.plus_as_string("a"), perms).unwrap(); + set_permissions(at.plus("a"), perms).unwrap(); // `a` is changed before it is read, so the failure is about reading it. let err_msg = "chmod: cannot read directory 'a': Permission denied\n"; @@ -548,10 +548,10 @@ fn test_chmod_recursive() { let a_perms_expected = (at.metadata("a").permissions().mode() & !0o444) | 0o222; let z_perms_expected = (at.metadata("z").permissions().mode() & !0o444) | 0o222; - make_file(&at.plus_as_string("a/a"), 0o100444); - make_file(&at.plus_as_string("a/b/b"), 0o100444); - make_file(&at.plus_as_string("a/b/c/c"), 0o100444); - make_file(&at.plus_as_string("z/y"), 0o100444); + make_file(at.plus("a/a"), 0o100444); + make_file(at.plus("a/b/b"), 0o100444); + make_file(at.plus("a/b/c/c"), 0o100444); + make_file(at.plus("z/y"), 0o100444); // Both operands become unreadable, and each failure is reported. let err_msg = "chmod: cannot read directory 'a': Permission denied\n\ chmod: cannot read directory 'z': Permission denied\n"; @@ -646,8 +646,8 @@ fn test_chmod_recursive_read_permission() { at.mkdir("a/b"); let mut perms = at.metadata("a/b").permissions(); perms.set_mode(0o311); - set_permissions(at.plus_as_string("a/b"), perms.clone()).unwrap(); - set_permissions(at.plus_as_string("a"), perms).unwrap(); + set_permissions(at.plus("a/b"), perms.clone()).unwrap(); + set_permissions(at.plus("a"), perms).unwrap(); ucmd.arg("-R").arg("u+r").arg("a").succeeds(); @@ -1081,7 +1081,7 @@ fn test_gnu_special_filenames() { let perms_before = Permissions::from_mode(0o100_640); let perms_after = Permissions::from_mode(0o100_440); - make_file(&at.plus_as_string("--"), perms_before.mode()); + make_file(at.plus("--"), perms_before.mode()); scene.ucmd().arg("-w").arg("--").arg("--").succeeds(); assert_eq!(at.metadata("--").permissions(), perms_after); set_permissions(at.plus("--"), perms_before.clone()).unwrap(); @@ -1089,7 +1089,7 @@ fn test_gnu_special_filenames() { assert_eq!(at.metadata("--").permissions(), perms_after); at.remove("--"); - make_file(&at.plus_as_string("-w"), perms_before.mode()); + make_file(at.plus("-w"), perms_before.mode()); scene.ucmd().arg("-w").arg("--").arg("-w").succeeds(); assert_eq!(at.metadata("-w").permissions(), perms_after); set_permissions(at.plus("-w"), perms_before).unwrap(); @@ -1153,7 +1153,7 @@ fn test_gnu_usage_matrix() { // 0o644 has no group or other write bit, so `-w` lands on 0o444 under any umask. This test // is about which operands get picked, not about umask arithmetic. for name in ["f", "--", "-w"] { - make_file(&at.plus_as_string(name), 0o644); + make_file(at.plus(name), 0o644); } let result = scene.ucmd().arg("-v").args(args).run(); @@ -1220,7 +1220,7 @@ fn test_umask_conflict_reported_only_for_option_like_mode() { let at = &scene.fixtures; // 0o666 is required: on 0o644 the umask has nothing left to keep, so the conflict never // arises and every one of these cases would pass vacuously. - make_file(&at.plus_as_string("file"), 0o666); + make_file(at.plus("file"), 0o666); let result = scene.ucmd().umask(0o022).args(args).run(); let context = format!("chmod {}", args.join(" ")); diff --git a/tests/by-util/test_chown.rs b/tests/by-util/test_chown.rs index 3f561370841..f4e05d5515d 100644 --- a/tests/by-util/test_chown.rs +++ b/tests/by-util/test_chown.rs @@ -780,10 +780,10 @@ fn test_chown_recursive() { at.mkdir_all("a/b/c"); at.mkdir("z"); - at.touch(at.plus_as_string("a/a")); - at.touch(at.plus_as_string("a/b/b")); - at.touch(at.plus_as_string("a/b/c/c")); - at.touch(at.plus_as_string("z/y")); + at.touch(at.plus("a/a")); + at.touch(at.plus("a/b/b")); + at.touch(at.plus("a/b/c/c")); + at.touch(at.plus("z/y")); scene .ucmd() diff --git a/tests/by-util/test_chroot.rs b/tests/by-util/test_chroot.rs index a6d5a8bbce5..de00627535d 100644 --- a/tests/by-util/test_chroot.rs +++ b/tests/by-util/test_chroot.rs @@ -70,7 +70,7 @@ fn test_no_such() { #[cfg(target_env = "gnu")] // todo: override by proper error message on other platforms fn test_not_a_directory() { let (at, mut ucmd) = at_and_ucmd!(); - at.touch(at.plus_as_string("a")); + at.touch(at.plus("a")); ucmd.arg("a") .fails_with_code(125) .stderr_only("chroot: cannot chroot to 'a': Not a directory\n"); diff --git a/tests/by-util/test_cp.rs b/tests/by-util/test_cp.rs index d9e4d14c964..eff7534b4c6 100644 --- a/tests/by-util/test_cp.rs +++ b/tests/by-util/test_cp.rs @@ -3167,7 +3167,7 @@ fn test_cp_one_file_system() { // Prepare the mount at_src.mkdir(TEST_MOUNT_MOUNTPOINT); - let mountpoint_path = &at_src.plus_as_string(TEST_MOUNT_MOUNTPOINT); + let mountpoint_path = &at_src.plus(TEST_MOUNT_MOUNTPOINT); scene .mount_temp_fs(mountpoint_path) @@ -5720,7 +5720,7 @@ fn test_acl_preserve() { let path = at.plus_as_string(file); scene.ucmd().args(&["-p", &path, path2]).succeeds(); - assert!(compare_xattrs(&file, &file_target)); + assert!(compare_xattrs(file, file_target)); } /// Layout of the source file copied by `test_cp_debug_sparse_source`. @@ -7863,7 +7863,7 @@ fn test_cp_preserve_xattr_readonly_source() { } let getfattr_output = Command::new("getfattr") - .args([&at.plus_as_string(source_file)]) + .args([&at.plus(source_file)]) .output() .expect("Failed to run `getfattr` on the destination file"); @@ -7900,7 +7900,7 @@ fn test_cp_preserve_xattr_readonly_source() { assert!(scene.fixtures.metadata(dest_file).permissions().readonly()); assert!( - compare_xattrs(&at.plus(source_file), &at.plus(dest_file)), + compare_xattrs(at.plus(source_file), at.plus(dest_file)), "Extended attributes were not preserved" ); } @@ -8137,7 +8137,7 @@ fn test_cp_selinux() { selinux_perm.contains("unconfined_u"), "Expected 'foo' not found in getfattr output:\n{selinux_perm}" ); - at.remove(at.plus_as_string(TEST_HELLO_WORLD_DEST)); + at.remove(at.plus(TEST_HELLO_WORLD_DEST)); } } @@ -8147,7 +8147,6 @@ fn test_cp_selinux_default_context_relative_dest() { // -Z labels the destination with the context the policy has for its path, // and the policy only lists absolute paths: a relative destination used to // match nothing and silently keep the context it already carried. - use std::path::Path; use uucore::selinux::set_selinux_security_context; let ts = TestScenario::new(util_name!()); @@ -8160,7 +8159,7 @@ fn test_cp_selinux_default_context_relative_dest() { let ctx = "root:object_r:etc_t:s0".to_string(); for dest in ["relative", "absolute"] { at.touch(dest); - if set_selinux_security_context(Path::new(&at.plus_as_string(dest)), Some(&ctx)).is_err() { + if set_selinux_security_context(&at.plus(dest), Some(&ctx)).is_err() { return; } } @@ -8256,7 +8255,7 @@ fn test_cp_preserve_selinux_admin_context() { // On Debian/Ubuntu, this program is provided by the selinux-utils package // On Fedora/RHEL, this program is provided by the libselinux-devel package let output = std::process::Command::new("matchpathcon") - .arg(at.plus_as_string(TEST_HELLO_WORLD_DEST)) + .arg(at.plus(TEST_HELLO_WORLD_DEST)) .output() .expect("failed to execute matchpathcon command"); @@ -8300,7 +8299,7 @@ fn test_cp_preserve_selinux_admin_context() { assert_eq!(default_context, selinux_perm_dest); - at.remove(at.plus_as_string(TEST_HELLO_WORLD_DEST)); + at.remove(at.plus(TEST_HELLO_WORLD_DEST)); } #[test] @@ -8498,7 +8497,7 @@ fn test_cp_preserve_context_root() { let chcon_result = std::process::Command::new("chcon") .arg(context) - .arg(at.plus_as_string(source_file)) + .arg(at.plus(source_file)) .status(); if !chcon_result.is_ok_and(|status| status.success()) { @@ -9195,7 +9194,7 @@ fn test_cp_archive_deref_symlinks_inside_dir() { let at = &scene.fixtures; at.mkdir("srcdir"); at.touch("srcdir/real.txt"); - symlink("real.txt", at.plus_as_string("srcdir/link.txt")).unwrap(); + symlink("real.txt", at.plus("srcdir/link.txt")).unwrap(); // -a (no deref): inner symlinks preserved scene.ucmd().args(&["-a", "srcdir", "dest_a"]).succeeds(); @@ -9232,7 +9231,7 @@ fn test_cp_archive_cli_deref_inner_preserved() { let at = &scene.fixtures; at.mkdir("srcdir"); at.touch("srcdir/real.txt"); - symlink("real.txt", at.plus_as_string("srcdir/link.txt")).unwrap(); + symlink("real.txt", at.plus("srcdir/link.txt")).unwrap(); // -aH: CLI symlink dereferenced, inner symlinks preserved scene.ucmd().args(&["-aH", "srcdir", "dest_aH"]).succeeds(); @@ -9262,7 +9261,7 @@ fn test_cp_archive_deref_repeated_flag_last_wins() { let at = &scene.fixtures; at.mkdir("srcdir"); at.touch("srcdir/real.txt"); - symlink("real.txt", at.plus_as_string("srcdir/link.txt")).unwrap(); + symlink("real.txt", at.plus("srcdir/link.txt")).unwrap(); // -aL -a: -a is last, inner symlinks preserved scene @@ -9648,14 +9647,13 @@ fn test_cp_gnu_preserve_mode() { #[cfg(all(feature = "selinux", any(target_os = "linux", target_os = "android")))] fn test_cp_a_z_overrides_context() { // Verifies -aZ succeeds (-Z overrides implicit --preserve=context from -a) - use std::path::Path; use uucore::selinux::set_selinux_security_context; let (at, mut ucmd) = at_and_ucmd!(); at.touch("src"); let ctx = "unconfined_u:object_r:user_tmp_t:s0".to_string(); - if set_selinux_security_context(Path::new(&at.plus_as_string("src")), Some(&ctx)).is_err() { + if set_selinux_security_context(&at.plus("src"), Some(&ctx)).is_err() { return; } @@ -9665,22 +9663,19 @@ fn test_cp_a_z_overrides_context() { #[test] #[cfg(all(feature = "selinux", any(target_os = "linux", target_os = "android")))] fn test_cp_a_preserves_context() { - use std::path::Path; use uucore::selinux::{get_selinux_security_context, set_selinux_security_context}; let (at, mut ucmd) = at_and_ucmd!(); at.touch("src"); let ctx = "unconfined_u:object_r:user_tmp_t:s0".to_string(); - if set_selinux_security_context(Path::new(&at.plus_as_string("src")), Some(&ctx)).is_err() { + if set_selinux_security_context(&at.plus("src"), Some(&ctx)).is_err() { return; } - let src_ctx = - get_selinux_security_context(Path::new(&at.plus_as_string("src")), false).unwrap(); + let src_ctx = get_selinux_security_context(&at.plus("src"), false).unwrap(); ucmd.args(&["-a", "src", "dst"]).succeeds(); - let dst_ctx = - get_selinux_security_context(Path::new(&at.plus_as_string("dst")), false).unwrap(); + let dst_ctx = get_selinux_security_context(&at.plus("dst"), false).unwrap(); assert_eq!(src_ctx, dst_ctx, "-a should preserve SELinux context"); } diff --git a/tests/by-util/test_dd.rs b/tests/by-util/test_dd.rs index e8b9172beb5..116790d8a96 100644 --- a/tests/by-util/test_dd.rs +++ b/tests/by-util/test_dd.rs @@ -1736,13 +1736,14 @@ fn test_reading_partial_blocks_from_fifo() { let ts = TestScenario::new(util_name!()); let at = &ts.fixtures; at.mkfifo("fifo"); - let fifoname = at.plus_as_string("fifo"); + let fifoname = at.plus("fifo"); + let fifo_disp = fifoname.display(); // Start a `dd` process that reads from the fifo (so it will wait // until the writer process starts). let mut reader_command = Command::new(get_tests_binary()); let child = reader_command - .args(["dd", "ibs=3", "obs=3", &format!("if={fifoname}")]) + .args(["dd", "ibs=3", "obs=3", &format!("if={fifo_disp}")]) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .env("LC_ALL", "C") @@ -1757,7 +1758,7 @@ fn test_reading_partial_blocks_from_fifo() { let _ = writer_command .args([ "-c", - &format!("(printf \"ab\"; sleep 0.1; printf \"cd\") > {fifoname}"), + &format!("(printf \"ab\"; sleep 0.1; printf \"cd\") > {fifo_disp}"), ]) .spawn() .unwrap() @@ -1776,7 +1777,8 @@ fn test_reading_partial_blocks_from_fifo_unbuffered() { let ts = TestScenario::new(util_name!()); let at = &ts.fixtures; at.mkfifo("fifo"); - let fifoname = at.plus_as_string("fifo"); + let fifoname = at.plus("fifo"); + let fifo_disp = fifoname.display(); // Start a `dd` process that reads from the fifo (so it will wait // until the writer process starts). @@ -1784,7 +1786,7 @@ fn test_reading_partial_blocks_from_fifo_unbuffered() { // `bs=N` takes precedence over `ibs=N` and `obs=N`. let mut reader_command = Command::new(get_tests_binary()); let child = reader_command - .args(["dd", "bs=3", "ibs=1", "obs=1", &format!("if={fifoname}")]) + .args(["dd", "bs=3", "ibs=1", "obs=1", &format!("if={fifo_disp}")]) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .env("LC_ALL", "C") @@ -1799,7 +1801,7 @@ fn test_reading_partial_blocks_from_fifo_unbuffered() { let _ = writer_command .args([ "-c", - &format!("(printf \"ab\"; sleep 0.1; printf \"cd\") > {fifoname}"), + &format!("(printf \"ab\"; sleep 0.1; printf \"cd\") > {fifo_disp}"), ]) .spawn() .unwrap() @@ -1825,13 +1827,14 @@ fn test_reading_partial_blocks_from_fifo_gathered_into_larger_obs() { let ts = TestScenario::new(util_name!()); let at = &ts.fixtures; at.mkfifo("fifo"); - let fifoname = at.plus_as_string("fifo"); + let fifoname = at.plus("fifo"); + let fifo_disp = fifoname.display(); // Start a `dd` process that reads from the fifo (so it will wait // until the writer process starts). let mut reader_command = Command::new(get_tests_binary()); let child = reader_command - .args(["dd", "ibs=3", "obs=6", &format!("if={fifoname}")]) + .args(["dd", "ibs=3", "obs=6", &format!("if={fifo_disp}")]) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .env("LC_ALL", "C") @@ -1846,7 +1849,7 @@ fn test_reading_partial_blocks_from_fifo_gathered_into_larger_obs() { let _ = writer_command .args([ "-c", - &format!("(printf \"ab\"; sleep 0.1; printf \"cd\") > {fifoname}"), + &format!("(printf \"ab\"; sleep 0.1; printf \"cd\") > {fifo_disp}"), ]) .spawn() .unwrap() @@ -1934,11 +1937,10 @@ fn test_large_bs_does_not_fault_in_copy_buffer() { fn test_iflag_directory_fails_when_file_is_passed_via_std_in() { let ts = TestScenario::new(util_name!()); let at = &ts.fixtures; - at.make_file("input"); - let filename = at.plus_as_string("input"); + let file = at.make_file("input"); new_ucmd!() .args(&["iflag=directory", "count=0"]) - .set_stdin(Stdio::from(File::open(filename).unwrap())) + .set_stdin(Stdio::from(file)) .fails() .stderr_only("dd: setting flags for 'standard input': Not a directory\n"); } @@ -2013,10 +2015,10 @@ fn test_stdin_stdout_not_rewound_even_when_connected_to_seekable_file() { .set_stderr(stderr) .succeeds(); - let err_file_content = std::fs::read_to_string(at.plus_as_string("err")).unwrap(); + let err_file_content = at.read("err"); println!("stderr:\n{err_file_content}"); - let out_file_content = std::fs::read_to_string(at.plus_as_string("out")).unwrap(); + let out_file_content = at.read("out"); println!("stdout:\n{out_file_content}"); assert_eq!(out_file_content, "bde"); } diff --git a/tests/by-util/test_install.rs b/tests/by-util/test_install.rs index 022cea63c42..df369e62d69 100644 --- a/tests/by-util/test_install.rs +++ b/tests/by-util/test_install.rs @@ -2382,8 +2382,8 @@ fn test_selinux() { new_ucmd!() .arg(arg) .arg("-v") - .arg(at.plus_as_string(src)) - .arg(at.plus_as_string(dest)) + .arg(at.plus(src)) + .arg(at.plus(dest)) .succeeds() .stdout_contains("orig' -> '"); @@ -2393,7 +2393,7 @@ fn test_selinux() { let Ok(context_value) = context_value else { println!("Skipping SELinux test: getfattr not available or failed"); - at.remove(at.plus_as_string(dest)); + at.remove(at.plus(dest)); continue; }; @@ -2401,7 +2401,7 @@ fn test_selinux() { context_value.contains("unconfined_u"), "Expected 'unconfined_u' not found in getfattr output:\n{context_value}" ); - at.remove(at.plus_as_string(dest)); + at.remove(at.plus(dest)); } } @@ -2428,12 +2428,12 @@ fn test_selinux_invalid_args() { new_ucmd!() .arg(arg) .arg("-v") - .arg(at.plus_as_string(src)) - .arg(at.plus_as_string(dest)) + .arg(at.plus(src)) + .arg(at.plus(dest)) .fails() .stderr_contains("failed to set default file creation"); - at.remove(at.plus_as_string(dest)); + at.remove(at.plus(dest)); } } @@ -2454,8 +2454,8 @@ fn test_selinux_default_context() { new_ucmd!() .arg("-Z") .arg("-v") - .arg(at.plus_as_string(src)) - .arg(at.plus_as_string(dest)) + .arg(at.plus(src)) + .arg(at.plus(dest)) .succeeds() .stdout_contains("orig' -> '"); assert!(at.file_exists(dest)); diff --git a/tests/by-util/test_ls.rs b/tests/by-util/test_ls.rs index 65d671a86ad..6b02b890d36 100644 --- a/tests/by-util/test_ls.rs +++ b/tests/by-util/test_ls.rs @@ -797,10 +797,10 @@ fn test_ls_a() { fn test_ls_width() { let scene = TestScenario::new(util_name!()); let at = &scene.fixtures; - at.touch(at.plus_as_string("test-width-1")); - at.touch(at.plus_as_string("test-width-2")); - at.touch(at.plus_as_string("test-width-3")); - at.touch(at.plus_as_string("test-width-4")); + at.touch(at.plus("test-width-1")); + at.touch(at.plus("test-width-2")); + at.touch(at.plus("test-width-3")); + at.touch(at.plus("test-width-4")); for option in [ "-w 100", @@ -897,10 +897,10 @@ fn test_ls_width() { fn test_ls_columns() { let scene = TestScenario::new(util_name!()); let at = &scene.fixtures; - at.touch(at.plus_as_string("test-columns-1")); - at.touch(at.plus_as_string("test-columns-2")); - at.touch(at.plus_as_string("test-columns-3")); - at.touch(at.plus_as_string("test-columns-4")); + at.touch(at.plus("test-columns-1")); + at.touch(at.plus("test-columns-2")); + at.touch(at.plus("test-columns-3")); + at.touch(at.plus("test-columns-4")); // Columns is the default let result = scene.ucmd().succeeds(); @@ -958,10 +958,10 @@ fn test_ls_columns() { fn test_ls_across() { let scene = TestScenario::new(util_name!()); let at = &scene.fixtures; - at.touch(at.plus_as_string("test-across-1")); - at.touch(at.plus_as_string("test-across-2")); - at.touch(at.plus_as_string("test-across-3")); - at.touch(at.plus_as_string("test-across-4")); + at.touch(at.plus("test-across-1")); + at.touch(at.plus("test-across-2")); + at.touch(at.plus("test-across-3")); + at.touch(at.plus("test-across-4")); for option in ACROSS_ARGS { let result = scene.ucmd().arg(option).succeeds(); @@ -986,10 +986,10 @@ fn test_ls_across() { fn test_ls_commas() { let scene = TestScenario::new(util_name!()); let at = &scene.fixtures; - at.touch(at.plus_as_string("test-commas-1")); - at.touch(at.plus_as_string("test-commas-2")); - at.touch(at.plus_as_string("test-commas-3")); - at.touch(at.plus_as_string("test-commas-4")); + at.touch(at.plus("test-commas-1")); + at.touch(at.plus("test-commas-2")); + at.touch(at.plus("test-commas-3")); + at.touch(at.plus("test-commas-4")); for option in COMMA_ARGS { let result = scene.ucmd().arg(option).succeeds(); @@ -1050,8 +1050,8 @@ fn test_ls_zero() { let scene = TestScenario::new(util_name!()); let at = &scene.fixtures; at.mkdir("0-test-zero"); - at.touch(at.plus_as_string("2-test-zero")); - at.touch(at.plus_as_string("3-test-zero")); + at.touch(at.plus("2-test-zero")); + at.touch(at.plus("3-test-zero")); let ignored_opts = [ "--quoting-style=c", @@ -1106,7 +1106,7 @@ fn test_ls_zero() { #[cfg(unix)] { - at.touch(at.plus_as_string("1\ntest-zero")); + at.touch(at.plus("1\ntest-zero")); let ignored_opts = [ "--quoting-style=c", @@ -1169,9 +1169,9 @@ fn test_ls_zero() { fn test_ls_commas_trailing() { let scene = TestScenario::new(util_name!()); let at = &scene.fixtures; - at.touch(at.plus_as_string("test-commas-trailing-2")); + at.touch(at.plus("test-commas-trailing-2")); - at.touch(at.plus_as_string("test-commas-trailing-1")); + at.touch(at.plus("test-commas-trailing-1")); at.append( "test-commas-trailing-1", &(0..2000) @@ -1193,7 +1193,7 @@ fn test_ls_commas_trailing() { fn test_ls_long() { let scene = TestScenario::new(util_name!()); let at = &scene.fixtures; - at.touch(at.plus_as_string("test-long")); + at.touch(at.plus("test-long")); #[cfg(not(windows))] let regex = r"[-bcCdDlMnpPsStTx?]([r-][w-][xt-]){3}.*"; @@ -1215,9 +1215,9 @@ fn test_ls_long() { fn test_ls_long_format() { let scene = TestScenario::new(util_name!()); let at = &scene.fixtures; - at.mkdir(at.plus_as_string("test-long-dir")); - at.touch(at.plus_as_string("test-long-dir/test-long-file")); - at.mkdir(at.plus_as_string("test-long-dir/test-long-dir")); + at.mkdir(at.plus("test-long-dir")); + at.touch(at.plus("test-long-dir/test-long-file")); + at.mkdir(at.plus("test-long-dir/test-long-dir")); // Assuming sane username do not have spaces within them. // A line of the output should be: @@ -1771,9 +1771,9 @@ fn test_ls_directory_dangling_symlink_uses_ln_when_or_blank() { fn test_ls_long_total_size() { let scene = TestScenario::new(util_name!()); let at = &scene.fixtures; - at.touch(at.plus_as_string("test-long")); + at.touch(at.plus("test-long")); at.append("test-long", "1"); - at.touch(at.plus_as_string("test-long2")); + at.touch(at.plus("test-long2")); at.append("test-long2", "2"); let expected_prints: HashMap<_, _> = if cfg!(unix) { @@ -1817,7 +1817,7 @@ fn test_ls_long_total_size() { fn test_ls_long_formats() { let scene = TestScenario::new(util_name!()); let at = &scene.fixtures; - at.touch(at.plus_as_string("test-long-formats")); + at.touch(at.plus("test-long-formats")); // Zero or one "." for indicating a file with security context @@ -1964,8 +1964,8 @@ fn test_ls_long_formats() { fn test_ls_oneline() { let scene = TestScenario::new(util_name!()); let at = &scene.fixtures; - at.touch(at.plus_as_string("test-oneline-1")); - at.touch(at.plus_as_string("test-oneline-2")); + at.touch(at.plus("test-oneline-1")); + at.touch(at.plus("test-oneline-2")); // Bit of a weird situation: in the tests oneline and columns have the same output, // except on Windows. @@ -1985,7 +1985,7 @@ fn test_ls_deref() { let path_regexp = r"(.*)test-long.link -> (.*)test-long(.*)"; let re = Regex::new(path_regexp).unwrap(); - at.touch(at.plus_as_string("test-long")); + at.touch(at.plus("test-long")); at.symlink_file("test-long", "test-long.link"); assert!(at.is_symlink("test-long.link")); @@ -2619,7 +2619,7 @@ fn test_ls_order_time_breaks_ties_by_name() { } let same = FileTime::from_unix_time(1_700_000_000, 0); for name in names { - set_file_times(at.plus_as_string(name), same, same).unwrap(); + set_file_times(at.plus(name), same, same).unwrap(); } scene @@ -2676,11 +2676,7 @@ fn test_ls_order_time() { at.read("test-3"); // Set permissions of test-2, only changing ctime - std::fs::set_permissions( - at.plus_as_string("test-2"), - at.metadata("test-2").permissions(), - ) - .unwrap(); + std::fs::set_permissions(at.plus("test-2"), at.metadata("test-2").permissions()).unwrap(); scene.ucmd().arg("-al").succeeds(); @@ -2789,8 +2785,8 @@ fn test_ls_files_dirs() { at.mkdir("a/b"); at.mkdir("a/b/c"); at.mkdir("z"); - at.touch(at.plus_as_string("a/a")); - at.touch(at.plus_as_string("a/b/b")); + at.touch(at.plus("a/a")); + at.touch(at.plus("a/b/b")); scene.ucmd().arg("a").succeeds(); scene.ucmd().arg("a/a").succeeds(); @@ -2821,8 +2817,8 @@ fn test_ls_recursive() { at.mkdir("a/b"); at.mkdir("a/b/c"); at.mkdir("z"); - at.touch(at.plus_as_string("a/a")); - at.touch(at.plus_as_string("a/b/b")); + at.touch(at.plus("a/a")); + at.touch(at.plus("a/b/b")); scene.ucmd().arg("a").succeeds(); scene.ucmd().arg("a/a").succeeds(); @@ -3634,7 +3630,7 @@ fn test_ls_indicator_style() { at.mkdir("directory"); assert!(at.dir_exists("directory")); - at.touch(at.plus_as_string("link-src")); + at.touch(at.plus("link-src")); at.symlink_file("link-src", "link-dest.link"); assert!(at.is_symlink("link-dest.link")); @@ -3837,7 +3833,7 @@ fn test_ls_indicator_style() { at.mkdir("directory"); assert!(at.dir_exists("directory")); - at.touch(at.plus_as_string("link-src")); + at.touch(at.plus("link-src")); at.symlink_file("link-src", "link-dest.link"); assert!(at.is_symlink("link-dest.link")); @@ -4335,8 +4331,8 @@ fn test_ls_quoting_style() { fn test_ls_quoting_style_env_var_default() { let scene = TestScenario::new(util_name!()); let at = &scene.fixtures; - at.touch(at.plus_as_string("foo-1")); - at.touch(at.plus_as_string("bar-2")); + at.touch(at.plus("foo-1")); + at.touch(at.plus("bar-2")); // If no quoting style argument is provided, the QUOTING_STYLE environment variable // shall be used. @@ -4410,8 +4406,8 @@ fn test_ls_invalid_quoting_style_env_var_non_utf8() { fn test_ls_quoting_style_arg_overrides_env_var() { let scene = TestScenario::new(util_name!()); let at = &scene.fixtures; - at.touch(at.plus_as_string("foo-1")); - at.touch(at.plus_as_string("bar-2")); + at.touch(at.plus("foo-1")); + at.touch(at.plus("bar-2")); // The quoting style given by the env variable should be // overridden by any escape style provided by argument. diff --git a/tests/by-util/test_mkdir.rs b/tests/by-util/test_mkdir.rs index 4d2722a858a..64d2610c488 100644 --- a/tests/by-util/test_mkdir.rs +++ b/tests/by-util/test_mkdir.rs @@ -565,7 +565,7 @@ fn test_selinux() { new_ucmd!() .arg(arg) .arg("-v") - .arg(at.plus_as_string(dest)) + .arg(at.plus(dest)) .succeeds() .stdout_contains("created directory"); @@ -586,7 +586,7 @@ fn test_selinux_invalid() { let dest = "test_dir_a"; new_ucmd!() .arg("--context=testtest") - .arg(at.plus_as_string(dest)) + .arg(at.plus(dest)) .fails() .no_stdout() .stderr_contains("failed to set default file creation context to 'testtest':"); diff --git a/tests/by-util/test_mkfifo.rs b/tests/by-util/test_mkfifo.rs index ef275ee58d8..8fd208e02cb 100644 --- a/tests/by-util/test_mkfifo.rs +++ b/tests/by-util/test_mkfifo.rs @@ -187,7 +187,7 @@ fn test_mkfifo_selinux() { context_value.contains("unconfined_u"), "Expected 'unconfined_u' not found in getfattr output:\n{context_value}" ); - at.remove(at.plus_as_string(dest)); + at.remove(at.plus(dest)); } } diff --git a/tests/by-util/test_mknod.rs b/tests/by-util/test_mknod.rs index ab6eca8c018..2d1a3d98b1e 100644 --- a/tests/by-util/test_mknod.rs +++ b/tests/by-util/test_mknod.rs @@ -289,7 +289,7 @@ fn test_mknod_selinux() { context_value.contains("unconfined_u"), "Expected 'unconfined_u' not found in getfattr output:\n{context_value}" ); - at.remove(at.plus_as_string(dest)); + at.remove(at.plus(dest)); } } @@ -339,7 +339,7 @@ fn test_mknod_selinux_invalid_cleanup() { new_ucmd!() .arg("--context=invalid_context_t") - .arg(at.plus_as_string(dest)) + .arg(at.plus(dest)) .arg("p") .fails() .no_stdout(); diff --git a/tests/by-util/test_mv.rs b/tests/by-util/test_mv.rs index 175388a557c..8619da332e2 100644 --- a/tests/by-util/test_mv.rs +++ b/tests/by-util/test_mv.rs @@ -983,13 +983,13 @@ fn test_mv_update_option() { let times_later = FileTimes::new().set_accessed(now).set_modified(later); File::options() .write(true) - .open(at.plus_as_string(file_a)) + .open(at.plus(file_a)) .unwrap() .set_times(times_now) .unwrap(); File::options() .write(true) - .open(at.plus_as_string(file_b)) + .open(at.plus(file_b)) .unwrap() .set_times(times_later) .unwrap(); @@ -1732,13 +1732,13 @@ fn test_acl() { let at = &scene.fixtures; let path1 = "a"; let path2 = "b"; - let file = "a/file"; let file_target = "b/file"; at.mkdir(path1); at.mkdir(path2); - at.touch(file); + let filename = "a/file"; + at.touch(filename); + let path = at.plus(filename); - let path = at.plus_as_string(file); // calling the command directly. xattr requires some dev packages to be installed // and it adds a complex dependency just for a test match Command::new("setfacl") @@ -1759,7 +1759,7 @@ fn test_acl() { scene.ucmd().arg(&path).arg(path2).succeeds(); - assert!(compare_xattrs(&file, &file_target)); + assert!(compare_xattrs(&path, file_target)); } #[test] @@ -2789,10 +2789,7 @@ fn test_mv_selinux_context() { let mut cmd = scene.ucmd(); cmd.arg(arg); - let result = cmd - .arg(at.plus_as_string(src)) - .arg(at.plus_as_string(dest)) - .run(); + let result = cmd.arg(at.plus(src)).arg(at.plus(dest)).run(); // Skip test if SELinux is not enabled if result @@ -2819,8 +2816,8 @@ fn test_mv_selinux_context() { } // Clean up files - let _ = std::fs::remove_file(at.plus_as_string(dest)); - let _ = std::fs::remove_file(at.plus_as_string(src)); + let _ = std::fs::remove_file(at.plus(dest)); + let _ = std::fs::remove_file(at.plus(src)); } } @@ -2987,7 +2984,7 @@ fn test_mv_xattr_enotsup_silent() { { scene .ucmd() - .arg(at.plus_as_string("src")) + .arg(at.plus("src")) .arg("/dev/shm/mv_test") .succeeds() .no_stderr(); @@ -3031,7 +3028,7 @@ fn test_mv_cross_device_dir_xattr_preserved() { scene .ucmd() - .arg(at.plus_as_string("src_dir")) + .arg(at.plus("src_dir")) .arg(dst_path.to_str().unwrap()) .succeeds() .no_stderr(); @@ -3144,7 +3141,7 @@ fn test_mv_cross_device_symlink_onto_existing() { scene .ucmd() - .arg(at.plus_as_string("src_link")) + .arg(at.plus("src_link")) .arg(dst_path.to_str().unwrap()) .succeeds() .no_stderr(); @@ -3187,7 +3184,7 @@ fn test_mv_cross_device_symlink_onto_existing_dir() { scene .ucmd() .arg("-T") - .arg(at.plus_as_string("src_link")) + .arg(at.plus("src_link")) .arg(dst_dir.to_str().unwrap()) .fails(); diff --git a/tests/by-util/test_nohup.rs b/tests/by-util/test_nohup.rs index 778cbb3af15..fd02e3e29e2 100644 --- a/tests/by-util/test_nohup.rs +++ b/tests/by-util/test_nohup.rs @@ -102,7 +102,7 @@ fn test_nohup_replaced_stdin_is_not_readable() { sleep(std::time::Duration::from_millis(10)); // cat's error message goes to stderr, which nohup redirected into nohup.out - let content = std::fs::read_to_string(at.plus_as_string("nohup.out")).unwrap(); + let content = at.read("nohup.out"); assert!( content.contains("Bad file descriptor"), "expected a read error from cat in nohup.out, got: {content:?}" diff --git a/tests/by-util/test_sync.rs b/tests/by-util/test_sync.rs index f74e9374730..64bcc79802a 100644 --- a/tests/by-util/test_sync.rs +++ b/tests/by-util/test_sync.rs @@ -200,7 +200,7 @@ fn test_sync_data_fifo_fails_immediately() { ts.ucmd() .arg("--data") - .arg(at.plus_as_string("test-fifo")) + .arg(at.plus("test-fifo")) .timeout(Duration::from_secs(2)) .fails() .stderr_contains("error syncing") diff --git a/tests/by-util/test_touch.rs b/tests/by-util/test_touch.rs index 257b80f1ce8..5c831e4a1e3 100644 --- a/tests/by-util/test_touch.rs +++ b/tests/by-util/test_touch.rs @@ -34,7 +34,7 @@ fn get_symlink_times(at: &AtPath, path: &str) -> (FileTime, FileTime) { } fn set_file_times(at: &AtPath, path: &str, atime: FileTime, mtime: FileTime) { - filetime::set_file_times(at.plus_as_string(path), atime, mtime).unwrap(); + filetime::set_file_times(at.plus(path), atime, mtime).unwrap(); } fn str_to_filetime(format: &str, s: &str) -> FileTime { diff --git a/tests/by-util/test_uptime.rs b/tests/by-util/test_uptime.rs index 1f356ae4ced..54d18bbfe78 100644 --- a/tests/by-util/test_uptime.rs +++ b/tests/by-util/test_uptime.rs @@ -61,7 +61,7 @@ fn test_uptime_for_file_without_utmpx_records() { let (at, mut ucmd) = at_and_ucmd!(); at.write("file1", "hello"); - ucmd.arg(at.plus_as_string("file1")) + ucmd.arg(at.plus("file1")) .fails() .stderr_contains("uptime: couldn't get boot time") .stdout_contains("up ???? days ??:??") @@ -86,8 +86,8 @@ fn test_uptime_with_fifo() { // Creating a child process to write to the fifo let mut child = ts .ccmd("cp") - .arg(at.plus_as_string("a")) - .arg(at.plus_as_string("fifo1")) + .arg(at.plus("a")) + .arg(at.plus("fifo1")) .run_no_wait(); ts.ucmd() diff --git a/tests/uutests/src/lib/util.rs b/tests/uutests/src/lib/util.rs index 22aea18e0fb..e523b74f9db 100644 --- a/tests/uutests/src/lib/util.rs +++ b/tests/uutests/src/lib/util.rs @@ -1015,18 +1015,17 @@ pub fn get_root_path() -> &'static str { target_os = "android", target_os = "netbsd" ))] -pub fn compare_xattrs>(path1: P, path2: P) -> bool { - let get_sorted_xattrs = |path: P| { - xattr::list(path) - .map(|attrs| { - let mut attrs = attrs.collect::>(); - attrs.sort(); - attrs - }) - .unwrap_or_default() +pub fn compare_xattrs, P2: AsRef>(path1: P1, path2: P2) -> bool { + let get_sorted_xattrs = |list: Result| { + list.map(|attrs| { + let mut attrs = attrs.collect::>(); + attrs.sort(); + attrs + }) + .unwrap_or_default() }; - get_sorted_xattrs(path1) == get_sorted_xattrs(path2) + get_sorted_xattrs(xattr::list(path1)) == get_sorted_xattrs(xattr::list(path2)) } /// Object-oriented path struct that represents and operates on @@ -1081,17 +1080,14 @@ impl AtPath { } pub fn open>(&self, name: P) -> File { - log_info("open", self.plus_as_string(&name)); - File::open(self.plus(name)).unwrap() + let path = self.plus(name); + log_info("open", path.display()); + File::open(path).unwrap() } pub fn read>(&self, name: P) -> String { - let name = name.as_ref(); - let mut f = self.open(name); - let mut contents = String::new(); - f.read_to_string(&mut contents) - .unwrap_or_else(|e| panic!("Couldn't read {}: {e}", name.display())); - contents + String::from_utf8(self.read_bytes(&name)) + .unwrap_or_else(|e| panic!("Couldn't decode {} as UTF-8: {e}", name.as_ref().display())) } pub fn read_bytes>(&self, name: P) -> Vec { @@ -1108,47 +1104,41 @@ impl AtPath { } pub fn write_bytes>(&self, name: P, contents: &[u8]) { - let name = name.as_ref(); - log_info("write(default)", self.plus_as_string(name)); - fs::write(self.plus(name), contents) - .unwrap_or_else(|e| panic!("Couldn't write {}: {e}", name.display())); + let path = self.plus(&name); + log_info("write(default)", path.display()); + fs::write(path, contents) + .unwrap_or_else(|e| panic!("Couldn't write {}: {e}", name.as_ref().display())); } pub fn append>(&self, name: P, contents: &str) { - let name = name.as_ref(); - log_info("write(append)", self.plus_as_string(name)); - let mut f = OpenOptions::new() - .append(true) - .create(true) - .open(self.plus(name)) - .unwrap(); - f.write_all(contents.as_bytes()) - .unwrap_or_else(|e| panic!("Couldn't write(append) {}: {e}", name.display())); + self.append_bytes(name, contents.as_bytes()); } pub fn append_bytes>(&self, name: P, contents: &[u8]) { - let name = name.as_ref(); - log_info("write(append)", self.plus_as_string(name)); + let path = self.plus(&name); + log_info("write(append)", path.display()); let mut f = OpenOptions::new() .append(true) .create(true) - .open(self.plus(name)) + .open(path) .unwrap(); - f.write_all(contents) - .unwrap_or_else(|e| panic!("Couldn't write(append) to {}: {e}", name.display())); + f.write_all(contents).unwrap_or_else(|e| { + panic!("Couldn't write(append) to {}: {e}", name.as_ref().display()) + }); } pub fn truncate>(&self, name: P, contents: &str) { - let name = name.as_ref(); - log_info("write(truncate)", self.plus_as_string(name)); + let path = self.plus(name.as_ref()); + log_info("write(truncate)", path.display()); let mut f = OpenOptions::new() .write(true) .truncate(true) .create(true) - .open(self.plus(name)) + .open(path) .unwrap(); - f.write_all(contents.as_bytes()) - .unwrap_or_else(|e| panic!("Couldn't write(truncate) {}: {e}", name.display())); + f.write_all(contents.as_bytes()).unwrap_or_else(|e| { + panic!("Couldn't write(truncate) {}: {e}", name.as_ref().display()) + }); } pub fn rename, P2: AsRef>(&self, source: P1, target: P2) { @@ -1188,34 +1178,31 @@ impl AtPath { } pub fn rmdir>(&self, dir: P) { - let dir = dir.as_ref(); - log_info("rmdir", self.plus_as_string(dir)); - fs::remove_dir(self.plus(dir)).unwrap(); + let path = self.plus(dir); + log_info("rmdir", path.display()); + fs::remove_dir(path).unwrap(); } pub fn mkdir>(&self, dir: P) { - let dir = dir.as_ref(); - log_info("mkdir", self.plus_as_string(dir)); - fs::create_dir(self.plus(dir)).unwrap(); + let path = self.plus(dir); + log_info("mkdir", path.display()); + fs::create_dir(path).unwrap(); } pub fn mkdir_all>(&self, dir: P) { - let dir = dir.as_ref(); - log_info("mkdir_all", self.plus_as_string(dir)); - fs::create_dir_all(self.plus(dir)).unwrap(); + let path = self.plus(dir); + log_info("mkdir_all", path.display()); + fs::create_dir_all(path).unwrap(); } pub fn make_file>(&self, name: P) -> File { let name = name.as_ref(); - match File::create(self.plus(name)) { - Ok(f) => f, - Err(e) => panic!("Couldn't create file {}: {e}", name.display()), - } + File::create(self.plus(name)) + .unwrap_or_else(|e| panic!("Couldn't create file {}: {e}", name.display())) } pub fn touch>(&self, file: P) { - let file = file.as_ref(); - log_info("touch", self.plus_as_string(file)); + log_info("touch", self.plus(&file).display()); let _ = self.make_file(file); } @@ -1223,18 +1210,18 @@ impl AtPath { pub fn mkfifo>(&self, fifo: P) { // rustix::fs::mkfifoat is linux only use nix::sys::stat::Mode; - let full_path = self.plus_as_string(fifo); - log_info("mkfifo", &full_path); + let path = self.plus(fifo); + log_info("mkfifo", path.display()); let mode = Mode::S_IRUSR | Mode::S_IWUSR; - nix::unistd::mkfifo(Path::new(&full_path), mode).expect("mkfifo failed"); + nix::unistd::mkfifo(&path, mode).expect("mkfifo failed"); } #[cfg(unix)] pub fn mksocket>(&self, socket: P) { - let full_path = self.plus_as_string(socket); - log_info("mksocket", &full_path); - UnixListener::bind(full_path).expect("Socket file creation failed."); + let path = self.plus(socket); + log_info("mksocket", path.display()); + UnixListener::bind(path).expect("Socket file creation failed."); } #[cfg(not(windows))] @@ -1250,17 +1237,13 @@ impl AtPath { } pub fn hard_link, P2: AsRef>(&self, original: P1, link: P2) { - let original = original.as_ref(); - let link = link.as_ref(); + let original = self.plus(original); + let link = self.plus(link); log_info( "hard_link", - format!( - "{},{}", - self.plus_as_string(original), - self.plus_as_string(link) - ), + format!("{},{}", original.display(), link.display()), ); - hard_link(self.plus(original), self.plus(link)).unwrap(); + hard_link(original, link).unwrap(); } fn symlink_inner(&self, original: &Path, link: &Path) { @@ -1296,28 +1279,24 @@ impl AtPath { } pub fn is_symlink>(&self, path: P) -> bool { - let path = path.as_ref(); - log_info("is_symlink", self.plus_as_string(path)); - fs::symlink_metadata(self.plus(path)).is_ok_and(|m| m.is_symlink()) + let path = self.plus(path); + log_info("is_symlink", path.display()); + fs::symlink_metadata(path).is_ok_and(|m| m.is_symlink()) } pub fn resolve_link>(&self, path: P) -> String { - let path = path.as_ref(); - log_info("resolve_link", self.plus_as_string(path)); - match fs::read_link(self.plus(path)) { + let path = self.plus(path); + log_info("resolve_link", path.display()); + match fs::read_link(path) { Ok(p) => self.minus_as_string(p.to_str().unwrap()), Err(_) => String::new(), } } pub fn read_symlink>(&self, path: P) -> String { - let path = path.as_ref(); - log_info("read_symlink", self.plus_as_string(path)); - fs::read_link(self.plus(path)) - .unwrap() - .to_str() - .unwrap() - .to_owned() + let path = self.plus(path); + log_info("read_symlink", path.display()); + fs::read_link(path).unwrap().to_str().unwrap().to_owned() } pub fn symlink_metadata>(&self, path: P) -> fs::Metadata { @@ -1402,7 +1381,7 @@ pub struct TestScenario { pub fixtures: AtPath, tmpd: Rc, #[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))] - tmp_fs_mountpoint: Option, + tmp_fs_mountpoint: Option, } impl TestScenario { @@ -1464,7 +1443,10 @@ impl TestScenario { /// Mounts a temporary filesystem at the specified mount point. #[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))] - pub fn mount_temp_fs(&mut self, mount_point: &str) -> core::result::Result<(), String> { + pub fn mount_temp_fs>( + &mut self, + mount_point: P, + ) -> core::result::Result<(), String> { if self.tmp_fs_mountpoint.is_some() { return Err("already mounted".to_string()); } @@ -1475,12 +1457,12 @@ impl TestScenario { .arg("-o") .arg("size=640k") // ought to be enough .arg("tmpfs") - .arg(mount_point) + .arg(mount_point.as_ref()) .run(); if !cmd_result.succeeded() { return Err(format!("mount failed: {}", cmd_result.stderr_str())); } - self.tmp_fs_mountpoint = Some(mount_point.to_string()); + self.tmp_fs_mountpoint = Some(mount_point.as_ref().as_os_str().to_os_string()); Ok(()) }