use std::path::PathBuf;
use tempfile::TempDir;
use blazehash::folder_diff::{diff_folders, CompareBy, FolderDiffEntry};
fn make_file(dir: &TempDir, rel: &str, content: &[u8]) -> PathBuf {
let path = dir.path().join(rel);
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent).unwrap();
}
std::fs::write(&path, content).unwrap();
path
}
#[test]
fn test_folder_diff_identical_folders() {
let left = tempfile::tempdir().unwrap();
let right = tempfile::tempdir().unwrap();
make_file(&left, "a.txt", b"hello");
make_file(&left, "b.bin", b"world");
make_file(&right, "a.txt", b"hello");
make_file(&right, "b.bin", b"world");
let result = diff_folders(left.path(), right.path(), false, CompareBy::Content).unwrap();
assert!(!result.has_diff(), "identical folders should not have diff");
let s = result.summary();
assert_eq!(s.identical, 2);
assert_eq!(s.modified + s.added + s.removed + s.moved, 0);
}
#[test]
fn test_folder_diff_added_file() {
let left = tempfile::tempdir().unwrap();
let right = tempfile::tempdir().unwrap();
make_file(&left, "a.txt", b"same");
make_file(&right, "a.txt", b"same");
make_file(&right, "new.bin", b"newcontent");
let result = diff_folders(left.path(), right.path(), false, CompareBy::Content).unwrap();
assert!(result.has_diff());
let added: Vec<_> = result
.entries
.iter()
.filter_map(|e| {
if let FolderDiffEntry::Added { path, .. } = e {
Some(path.clone())
} else {
None
}
})
.collect();
assert_eq!(added.len(), 1);
assert_eq!(added[0], PathBuf::from("new.bin"));
}
#[test]
fn test_folder_diff_removed_file() {
let left = tempfile::tempdir().unwrap();
let right = tempfile::tempdir().unwrap();
make_file(&left, "a.txt", b"same");
make_file(&left, "old.bin", b"gone");
make_file(&right, "a.txt", b"same");
let result = diff_folders(left.path(), right.path(), false, CompareBy::Content).unwrap();
let removed: Vec<_> = result
.entries
.iter()
.filter_map(|e| {
if let FolderDiffEntry::Removed { path, .. } = e {
Some(path.clone())
} else {
None
}
})
.collect();
assert_eq!(removed.len(), 1);
assert_eq!(removed[0], PathBuf::from("old.bin"));
}
#[test]
fn test_folder_diff_modified_file() {
let left = tempfile::tempdir().unwrap();
let right = tempfile::tempdir().unwrap();
make_file(&left, "config.json", b"{\"v\":1}");
make_file(&right, "config.json", b"{\"v\":2}");
let result = diff_folders(left.path(), right.path(), false, CompareBy::Content).unwrap();
let mods: Vec<_> = result
.entries
.iter()
.filter_map(|e| {
if let FolderDiffEntry::Modified { path, .. } = e {
Some(path.clone())
} else {
None
}
})
.collect();
assert_eq!(mods.len(), 1);
assert_eq!(mods[0], PathBuf::from("config.json"));
}
#[test]
fn test_folder_diff_moved_file() {
let left = tempfile::tempdir().unwrap();
let right = tempfile::tempdir().unwrap();
make_file(&left, "old_name.txt", b"unique content xyz");
make_file(&right, "new_name.txt", b"unique content xyz");
let result = diff_folders(left.path(), right.path(), false, CompareBy::Content).unwrap();
let moves: Vec<_> = result
.entries
.iter()
.filter_map(|e| {
if let FolderDiffEntry::Moved { from, to, .. } = e {
Some((from.clone(), to.clone()))
} else {
None
}
})
.collect();
assert_eq!(
moves.len(),
1,
"expected 1 move, got {:?}",
result
.entries
.iter()
.map(|e| format!("{e:?}"))
.collect::<Vec<_>>()
);
assert_eq!(moves[0].0, PathBuf::from("old_name.txt"));
assert_eq!(moves[0].1, PathBuf::from("new_name.txt"));
}
#[test]
fn test_folder_diff_recursive() {
let left = tempfile::tempdir().unwrap();
let right = tempfile::tempdir().unwrap();
make_file(&left, "sub/a.txt", b"same");
make_file(&left, "sub/b.txt", b"left only");
make_file(&right, "sub/a.txt", b"same");
make_file(&right, "sub/c.txt", b"right only");
let result = diff_folders(left.path(), right.path(), true, CompareBy::Content).unwrap();
let s = result.summary();
assert_eq!(s.identical, 1, "sub/a.txt should be identical");
assert_eq!(s.removed, 1, "sub/b.txt should be removed");
assert_eq!(s.added, 1, "sub/c.txt should be added");
}
#[test]
fn test_folder_diff_non_recursive_skips_subdirs() {
let left = tempfile::tempdir().unwrap();
let right = tempfile::tempdir().unwrap();
make_file(&left, "top.txt", b"same");
make_file(&left, "sub/deep.txt", b"should be ignored");
make_file(&right, "top.txt", b"same");
let result = diff_folders(left.path(), right.path(), false, CompareBy::Content).unwrap();
assert!(
!result.has_diff(),
"non-recursive diff should ignore subdirs"
);
}
#[test]
fn test_folder_diff_size_time_same_size_is_identical() {
let left = tempfile::tempdir().unwrap();
let right = tempfile::tempdir().unwrap();
make_file(&left, "a.txt", b"hello");
make_file(&right, "a.txt", b"hello");
let result = diff_folders(left.path(), right.path(), false, CompareBy::SizeTime).unwrap();
let _ = result.summary();
}
#[test]
fn test_folder_diff_size_time_different_size_is_modified() {
let left = tempfile::tempdir().unwrap();
let right = tempfile::tempdir().unwrap();
make_file(&left, "a.txt", b"short");
make_file(&right, "a.txt", b"much longer content here");
let result = diff_folders(left.path(), right.path(), false, CompareBy::SizeTime).unwrap();
let s = result.summary();
assert_eq!(s.modified, 1, "different sizes → modified");
}
#[test]
fn test_folder_diff_name_mode_same_names_is_identical() {
let left = tempfile::tempdir().unwrap();
let right = tempfile::tempdir().unwrap();
make_file(&left, "a.txt", b"completely different content left");
make_file(&right, "a.txt", b"completely different content right");
let result = diff_folders(left.path(), right.path(), false, CompareBy::Name).unwrap();
let s = result.summary();
assert_eq!(s.identical, 1);
assert_eq!(s.modified, 0);
}
#[test]
fn test_folder_diff_summary_byte_counts() {
let left = tempfile::tempdir().unwrap();
let right = tempfile::tempdir().unwrap();
make_file(&left, "a.txt", b"12345"); make_file(&right, "a.txt", b"1234567890");
let result = diff_folders(left.path(), right.path(), false, CompareBy::Content).unwrap();
let s = result.summary();
assert_eq!(s.left_bytes, 5);
assert_eq!(s.right_bytes, 10);
}
#[test]
fn test_folder_diff_empty_left() {
let left = tempfile::tempdir().unwrap();
let right = tempfile::tempdir().unwrap();
make_file(&right, "newfile.bin", b"new");
let result = diff_folders(left.path(), right.path(), false, CompareBy::Content).unwrap();
let s = result.summary();
assert_eq!(s.added, 1);
assert_eq!(s.removed + s.modified + s.identical, 0);
}
#[test]
fn test_folder_diff_empty_right() {
let left = tempfile::tempdir().unwrap();
let right = tempfile::tempdir().unwrap();
make_file(&left, "gone.bin", b"old");
let result = diff_folders(left.path(), right.path(), false, CompareBy::Content).unwrap();
let s = result.summary();
assert_eq!(s.removed, 1);
assert_eq!(s.added + s.modified + s.identical, 0);
}
#[test]
fn test_folder_diff_has_diff_false_when_identical() {
let left = tempfile::tempdir().unwrap();
let right = tempfile::tempdir().unwrap();
make_file(&left, "x.bin", b"data");
make_file(&right, "x.bin", b"data");
let result = diff_folders(left.path(), right.path(), false, CompareBy::Content).unwrap();
assert!(!result.has_diff());
}
#[test]
fn test_folder_diff_has_diff_true_when_different() {
let left = tempfile::tempdir().unwrap();
let right = tempfile::tempdir().unwrap();
make_file(&left, "x.bin", b"old");
make_file(&right, "x.bin", b"new");
let result = diff_folders(left.path(), right.path(), false, CompareBy::Content).unwrap();
assert!(result.has_diff());
}
#[test]
fn test_folder_diff_paranoid_identical() {
let left = tempfile::tempdir().unwrap();
let right = tempfile::tempdir().unwrap();
make_file(&left, "a.bin", b"identical content");
make_file(&right, "a.bin", b"identical content");
let result = diff_folders(left.path(), right.path(), false, CompareBy::Paranoid).unwrap();
assert!(!result.has_diff());
assert_eq!(result.summary().identical, 1);
}
#[test]
fn test_folder_diff_paranoid_detects_modification() {
let left = tempfile::tempdir().unwrap();
let right = tempfile::tempdir().unwrap();
make_file(&left, "a.bin", b"version one");
make_file(&right, "a.bin", b"version two");
let result = diff_folders(left.path(), right.path(), false, CompareBy::Paranoid).unwrap();
assert!(result.has_diff());
assert_eq!(result.summary().modified, 1);
}
#[test]
fn test_folder_diff_paranoid_move_detection() {
let left = tempfile::tempdir().unwrap();
let right = tempfile::tempdir().unwrap();
make_file(&left, "old.bin", b"unique paranoid content abc");
make_file(&right, "new.bin", b"unique paranoid content abc");
let result = diff_folders(left.path(), right.path(), false, CompareBy::Paranoid).unwrap();
assert_eq!(result.summary().moved, 1);
}
#[test]
fn test_folder_diff_paranoid_agrees_with_content_on_identical() {
let left = tempfile::tempdir().unwrap();
let right = tempfile::tempdir().unwrap();
make_file(&left, "f.bin", b"some bytes");
make_file(&right, "f.bin", b"some bytes");
let r_content = diff_folders(left.path(), right.path(), false, CompareBy::Content).unwrap();
let r_paranoid = diff_folders(left.path(), right.path(), false, CompareBy::Paranoid).unwrap();
assert_eq!(r_content.has_diff(), r_paranoid.has_diff());
assert_eq!(
r_content.summary().identical,
r_paranoid.summary().identical
);
}
#[test]
fn test_folder_diff_paranoid_agrees_with_content_on_modified() {
let left = tempfile::tempdir().unwrap();
let right = tempfile::tempdir().unwrap();
make_file(&left, "f.bin", b"before");
make_file(&right, "f.bin", b"after");
let r_content = diff_folders(left.path(), right.path(), false, CompareBy::Content).unwrap();
let r_paranoid = diff_folders(left.path(), right.path(), false, CompareBy::Paranoid).unwrap();
assert_eq!(r_content.summary().modified, r_paranoid.summary().modified);
}