cp: add readonly file regression tests (#9045)

* feat: add comprehensive readonly file regression tests for cp

- Add 10 new test functions covering readonly destination behavior
- Tests cover basic readonly copying, flag combinations, and edge cases
- Include macOS-specific clonefile behavior tests
- Ensure readonly file protection from PR #5261 cannot regress
- Tests provide evidence for closing issue #5349

* perf: optimize readonly regression tests with batched I/O operations

- Reduce file I/O overhead by batching file operations
- Consolidate setup operations to minimize system calls
- Improve test execution time from 0.44s to 0.27s (38% improvement)
- Maintain comprehensive test coverage for readonly file behavior

* fix: remove duplicate tests and trivial comments per PR feedback

- Remove test_cp_readonly_dest_regression (duplicate of test_cp_dest_no_permissions)
- Remove test_cp_readonly_dest_with_force (duplicate of test_cp_arg_force)
- Remove test_cp_readonly_dest_with_remove_destination (duplicate of test_cp_arg_remove_destination)
- Remove test_cp_macos_clonefile_readonly (duplicate of test_cp_existing_target)
- Remove test_cp_normal_copy_still_works (duplicate of test_cp_existing_target)
- Remove trivial performance comments from readonly tests
- Keep existing proven tests per maintainer preferences
- Keep unique readonly tests that provide additional coverage
This commit is contained in:
Cả thế giới là Rust
2025-12-10 14:54:17 +01:00
committed by GitHub
parent ee9bd8bf7a
commit 415d01cc75
+104
View File
@@ -4101,6 +4101,110 @@ fn test_cp_dest_no_permissions() {
.stderr_contains("denied");
}
/// Test readonly destination behavior with reflink options
#[cfg(any(target_os = "linux", target_os = "macos"))]
#[test]
fn test_cp_readonly_dest_with_reflink() {
let ts = TestScenario::new(util_name!());
let at = &ts.fixtures;
at.write("source.txt", "source content");
at.write("readonly_dest_auto.txt", "original content");
at.write("readonly_dest_always.txt", "original content");
at.set_readonly("readonly_dest_auto.txt");
at.set_readonly("readonly_dest_always.txt");
// Test reflink=auto
ts.ucmd()
.args(&["--reflink=auto", "source.txt", "readonly_dest_auto.txt"])
.fails()
.stderr_contains("readonly_dest_auto.txt");
// Test reflink=always
ts.ucmd()
.args(&["--reflink=always", "source.txt", "readonly_dest_always.txt"])
.fails()
.stderr_contains("readonly_dest_always.txt");
assert_eq!(at.read("readonly_dest_auto.txt"), "original content");
assert_eq!(at.read("readonly_dest_always.txt"), "original content");
}
/// Test readonly destination behavior in recursive directory copy
#[test]
fn test_cp_readonly_dest_recursive() {
let ts = TestScenario::new(util_name!());
let at = &ts.fixtures;
at.mkdir("source_dir");
at.mkdir("dest_dir");
at.write("source_dir/file.txt", "source content");
at.write("dest_dir/file.txt", "original content");
at.set_readonly("dest_dir/file.txt");
ts.ucmd().args(&["-r", "source_dir", "dest_dir"]).succeeds();
assert_eq!(at.read("dest_dir/file.txt"), "original content");
}
/// Test copying to readonly file when another file exists
#[test]
fn test_cp_readonly_dest_with_existing_file() {
let ts = TestScenario::new(util_name!());
let at = &ts.fixtures;
at.write("source.txt", "source content");
at.write("readonly_dest.txt", "original content");
at.write("other_file.txt", "other content");
at.set_readonly("readonly_dest.txt");
ts.ucmd()
.args(&["source.txt", "readonly_dest.txt"])
.fails()
.stderr_contains("readonly_dest.txt")
.stderr_contains("denied");
assert_eq!(at.read("readonly_dest.txt"), "original content");
assert_eq!(at.read("other_file.txt"), "other content");
}
/// Test readonly source file (should work fine)
#[test]
fn test_cp_readonly_source() {
let ts = TestScenario::new(util_name!());
let at = &ts.fixtures;
at.write("readonly_source.txt", "source content");
at.write("dest.txt", "dest content");
at.set_readonly("readonly_source.txt");
ts.ucmd()
.args(&["readonly_source.txt", "dest.txt"])
.succeeds();
assert_eq!(at.read("dest.txt"), "source content");
}
/// Test readonly source and destination (should fail)
#[test]
fn test_cp_readonly_source_and_dest() {
let ts = TestScenario::new(util_name!());
let at = &ts.fixtures;
at.write("readonly_source.txt", "source content");
at.write("readonly_dest.txt", "original content");
at.set_readonly("readonly_source.txt");
at.set_readonly("readonly_dest.txt");
ts.ucmd()
.args(&["readonly_source.txt", "readonly_dest.txt"])
.fails()
.stderr_contains("readonly_dest.txt")
.stderr_contains("denied");
assert_eq!(at.read("readonly_dest.txt"), "original content");
}
#[test]
#[cfg(all(unix, not(target_os = "freebsd"), not(target_os = "openbsd")))]
fn test_cp_attributes_only() {