#![allow(dead_code)]
use anyhow::{Context, Result};
use async_recursion::async_recursion;
use std::os::unix::fs::MetadataExt;
pub async fn create_temp_dir() -> Result<std::path::PathBuf> {
let mut idx = 0;
loop {
let tmp_dir = std::env::temp_dir().join(format!("rcp_test{}", &idx));
if let Err(error) = tokio::fs::create_dir(&tmp_dir).await {
match error.kind() {
std::io::ErrorKind::AlreadyExists => {
idx += 1;
}
_ => return Err(error.into()),
}
} else {
return Ok(tmp_dir);
}
}
}
pub async fn setup_test_dir() -> Result<std::path::PathBuf> {
let tmp_dir = create_temp_dir().await?;
let foo_path = tmp_dir.join("foo");
tokio::fs::create_dir(&foo_path).await.unwrap();
tokio::fs::write(foo_path.join("0.txt"), "0").await.unwrap();
let bar_path = foo_path.join("bar");
tokio::fs::create_dir(&bar_path).await.unwrap();
tokio::fs::write(bar_path.join("1.txt"), "1").await.unwrap();
tokio::fs::write(bar_path.join("2.txt"), "2").await.unwrap();
tokio::fs::write(bar_path.join("3.txt"), "3").await.unwrap();
let baz_path = foo_path.join("baz");
tokio::fs::create_dir(&baz_path).await.unwrap();
tokio::fs::write(baz_path.join("4.txt"), "4").await.unwrap();
tokio::fs::symlink("../bar/2.txt", baz_path.join("5.txt"))
.await
.unwrap();
tokio::fs::symlink(bar_path.join("3.txt"), baz_path.join("6.txt"))
.await
.unwrap();
tokio::time::sleep(std::time::Duration::from_millis(1000)).await;
Ok(tmp_dir)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FileEqualityCheck {
Basic,
Timestamp,
HardLink,
}
#[async_recursion]
pub async fn check_dirs_identical(
src: &std::path::Path,
dst: &std::path::Path,
file_equality_check: FileEqualityCheck,
) -> Result<()> {
let mut src_entries = tokio::fs::read_dir(src).await?;
while let Some(src_entry) = src_entries.next_entry().await? {
let src_entry_path = src_entry.path();
let src_entry_name = src_entry_path.file_name().unwrap();
let dst_entry_path = dst.join(src_entry_name);
let src_md = tokio::fs::symlink_metadata(&src_entry_path)
.await
.context(format!("Source file {:?} is missing!", &src_entry_path))?;
let dst_md = tokio::fs::symlink_metadata(&dst_entry_path)
.await
.context(format!(
"Destination file {:?} is missing!",
&dst_entry_path
))?;
assert_eq!(src_md.file_type(), dst_md.file_type());
if src_md.is_file() {
if file_equality_check == FileEqualityCheck::HardLink {
assert_eq!(src_md.ino(), dst_md.ino());
} else {
let src_contents = tokio::fs::read_to_string(&src_entry_path).await?;
let dst_contents = tokio::fs::read_to_string(&dst_entry_path).await?;
assert_eq!(src_contents, dst_contents);
}
} else if src_md.file_type().is_symlink() {
let src_link = tokio::fs::read_link(&src_entry_path).await?;
let dst_link = tokio::fs::read_link(&dst_entry_path).await?;
assert_eq!(src_link, dst_link);
} else {
check_dirs_identical(&src_entry_path, &dst_entry_path, file_equality_check).await?;
}
assert_eq!(src_md.permissions(), dst_md.permissions());
if file_equality_check != FileEqualityCheck::Timestamp {
continue;
}
assert_eq!(
src_md.mtime_nsec(),
dst_md.mtime_nsec(),
"mtime doesn't match for {src_entry_path:?} {dst_entry_path:?}"
);
}
Ok(())
}