use fsys::builder;
use std::path::PathBuf;
use std::sync::atomic::{AtomicU64, Ordering};
static C: AtomicU64 = AtomicU64::new(0);
fn tmp_path(tag: &str) -> PathBuf {
let n = C.fetch_add(1, Ordering::Relaxed);
std::env::temp_dir().join(format!(
"fsys_write_copy_{}_{}_{}",
std::process::id(),
n,
tag
))
}
struct Cleanup(PathBuf);
impl Drop for Cleanup {
fn drop(&mut self) {
let _ = std::fs::remove_file(&self.0);
}
}
#[test]
fn write_copy_creates_new_file_when_target_missing() {
let path = tmp_path("create");
let _g = Cleanup(path.clone());
let fs = builder().build().expect("handle");
fs.write_copy(&path, b"hello").expect("write_copy");
let read = std::fs::read(&path).expect("read");
assert_eq!(read, b"hello");
}
#[test]
fn write_copy_replaces_existing_file_atomically() {
let path = tmp_path("replace");
let _g = Cleanup(path.clone());
std::fs::write(&path, b"old payload").unwrap();
let fs = builder().build().expect("handle");
fs.write_copy(&path, b"new payload").expect("write_copy");
let read = std::fs::read(&path).expect("read");
assert_eq!(read, b"new payload");
}
#[test]
fn write_copy_preserves_unix_mode() {
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let path = tmp_path("mode");
let _g = Cleanup(path.clone());
std::fs::write(&path, b"original").unwrap();
std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o644)).unwrap();
let fs = builder().build().expect("handle");
fs.write_copy(&path, b"replacement").expect("write_copy");
let mode = std::fs::metadata(&path).unwrap().permissions().mode() & 0o777;
assert_eq!(
mode, 0o644,
"mode bits must survive write_copy (got {mode:o})"
);
}
}
#[test]
fn write_copy_preserves_unix_mode_with_unusual_bits() {
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let path = tmp_path("mode_750");
let _g = Cleanup(path.clone());
std::fs::write(&path, b"original").unwrap();
std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o750)).unwrap();
let fs = builder().build().expect("handle");
fs.write_copy(&path, b"replacement").expect("write_copy");
let mode = std::fs::metadata(&path).unwrap().permissions().mode() & 0o777;
assert_eq!(mode, 0o750);
}
}
#[test]
fn write_copy_zero_byte_payload() {
let path = tmp_path("zero");
let _g = Cleanup(path.clone());
std::fs::write(&path, b"non-empty").unwrap();
let fs = builder().build().expect("handle");
fs.write_copy(&path, b"").expect("write_copy zero bytes");
let read = std::fs::read(&path).expect("read");
assert!(read.is_empty());
}
#[test]
fn write_copy_preserves_mtime_within_tolerance() {
let path = tmp_path("mtime");
let _g = Cleanup(path.clone());
std::fs::write(&path, b"original").unwrap();
let pre_mtime = std::fs::metadata(&path).unwrap().modified().unwrap();
std::thread::sleep(std::time::Duration::from_millis(50));
let fs = builder().build().expect("handle");
fs.write_copy(&path, b"replacement").expect("write_copy");
let post_mtime = std::fs::metadata(&path).unwrap().modified().unwrap();
let drift = post_mtime
.duration_since(pre_mtime)
.unwrap_or_else(|e| e.duration());
assert!(
drift.as_secs() < 2,
"mtime drifted {drift:?} — preservation broken or fs has unusual granularity"
);
}