#![allow(clippy::unwrap_used)]
#![allow(clippy::expect_used)]
#![allow(non_snake_case)]
use super::node::*;
#[test]
fn split_path____unix_absolute____strips_leading_slash() {
let result = split_path("/home/user/file.txt");
assert_eq!(result, vec!["home", "user", "file.txt"]);
}
#[test]
fn split_path____windows_path____splits_on_backslash() {
let result = split_path("C:\\Users\\test\\file.txt");
assert_eq!(result, vec!["C:", "Users", "test", "file.txt"]);
}
#[test]
fn split_path____empty_string____returns_empty() {
let result = split_path("");
assert!(result.is_empty());
}
#[test]
fn split_path____mixed_separators____splits_correctly() {
let result = split_path("/home/user\\docs/file.txt");
assert_eq!(result, vec!["home", "user", "docs", "file.txt"]);
}
#[test]
fn build_tree____single_file____correct_structure() {
let files = vec![("/home/user/doc.txt", 1000u64)];
let tree = build_tree(&files);
assert_eq!(tree.total_size(), 1000);
assert!(tree.children.contains_key("home"));
let home = &tree.children["home"];
let user = &home.children["user"];
let doc = &user.children["doc.txt"];
assert_eq!(doc.file_size, 1000);
assert!(doc.children.is_empty());
}
#[test]
fn build_tree____multiple_files_same_dir____aggregates_size() {
let files = vec![
("/data/a.bin", 500u64),
("/data/b.bin", 300),
("/data/c.bin", 200),
];
let tree = build_tree(&files);
assert_eq!(tree.total_size(), 1000);
let data = &tree.children["data"];
assert_eq!(data.total_size(), 1000);
assert_eq!(data.children.len(), 3);
}
#[test]
fn build_tree____nested_paths____correct_hierarchy() {
let files = vec![
("/a/b/c/file1.txt", 100u64),
("/a/b/file2.txt", 200),
("/a/file3.txt", 300),
];
let tree = build_tree(&files);
assert_eq!(tree.total_size(), 600);
let a = &tree.children["a"];
assert_eq!(a.total_size(), 600);
let b = &a.children["b"];
assert_eq!(b.total_size(), 300);
assert_eq!(b.children["file2.txt"].file_size, 200);
let c = &b.children["c"];
assert_eq!(c.total_size(), 100);
}
#[test]
fn build_tree____empty_file_list____empty_tree() {
let files: Vec<(&str, u64)> = Vec::new();
let tree = build_tree(&files);
assert_eq!(tree.total_size(), 0);
assert!(tree.children.is_empty());
}
#[test]
fn total_file_count____nested_tree____counts_all_files() {
let files = vec![
("/a/b/f1.txt", 100u64),
("/a/b/f2.txt", 200),
("/a/f3.txt", 300),
("/c/f4.txt", 400),
];
let tree = build_tree(&files);
assert_eq!(tree.total_file_count(), 4);
}
#[test]
fn to_echarts____leaf_node____has_value_no_children() {
let mut node = DirNode::new();
node.file_size = 42;
let echarts = node.to_echarts("test.txt");
assert_eq!(echarts.name, "test.txt");
assert_eq!(echarts.value, Some(42));
assert!(echarts.children.is_empty());
}
#[test]
fn to_echarts____single_child_chain____collapses_path() {
let files = vec![("/a/b/c/file.txt", 100u64)];
let tree = build_tree(&files);
let echarts_children: Vec<EChartsNode> = tree
.children
.iter()
.map(|(name, node)| node.to_echarts(name))
.collect();
assert_eq!(echarts_children.len(), 1);
let collapsed = &echarts_children[0];
assert!(
collapsed.name.contains("a"),
"Expected collapsed name containing 'a', got: {}",
collapsed.name
);
}
#[test]
fn to_echarts____children_sorted_by_size_descending____largest_first() {
let files = vec![
("/dir/small.txt", 10u64),
("/dir/large.bin", 1000),
("/dir/medium.dat", 500),
];
let tree = build_tree(&files);
let dir = &tree.children["dir"];
let echarts = dir.to_echarts("dir");
assert_eq!(echarts.children.len(), 3);
let sizes: Vec<u64> = echarts
.children
.iter()
.map(|c| c.value.unwrap_or(0))
.collect();
assert_eq!(sizes, vec![1000, 500, 10]);
}
#[test]
fn to_echarts____serializes_to_valid_json____no_error() {
let files = vec![
("/home/user/docs/report.pdf", 5_000_000u64),
("/home/user/docs/notes.txt", 1_000),
("/home/user/photos/vacation.jpg", 3_000_000),
];
let tree = build_tree(&files);
let echarts_children: Vec<EChartsNode> = tree
.children
.iter()
.map(|(name, node)| node.to_echarts(name))
.collect();
let json = serde_json::to_string(&echarts_children).unwrap();
assert!(!json.is_empty());
let _: serde_json::Value = serde_json::from_str(&json).unwrap();
}
#[test]
fn to_echarts____directory_subtrees_sorted_by_total_size____largest_first() {
let files = vec![
("/small_dir/a.txt", 10u64),
("/big_dir/b.txt", 1000),
("/big_dir/c.txt", 500),
("/med_dir/d.txt", 200),
];
let tree = build_tree(&files);
let mut echarts_children: Vec<EChartsNode> = tree
.children
.iter()
.map(|(name, node)| node.to_echarts(name))
.collect();
echarts_children.sort_by(|a, b| {
let sa = a.value.unwrap_or(0);
let sb = b.value.unwrap_or(0);
sb.cmp(&sa)
});
assert_eq!(echarts_children.len(), 3);
}
use proptest::prelude::*;
fn arb_file_list() -> impl Strategy<Value = Vec<(String, u64)>> {
let dir_names = prop::collection::vec("[a-z]{1,4}", 1..=4);
let file_name = "[a-z]{1,6}\\.[a-z]{1,3}";
let size = 0u64..10_000_000;
prop::collection::vec((dir_names, file_name, size), 0..50).prop_map(|entries| {
entries
.into_iter()
.map(|(dirs, name, sz)| {
let path = format!("/{}/{}", dirs.join("/"), name);
(path, sz)
})
.collect()
})
}
fn echarts_total_size(node: &EChartsNode) -> u64 {
if let Some(val) = node.value {
val
} else {
node.children.iter().map(echarts_total_size).sum()
}
}
proptest! {
#[test]
fn prop____total_size_equals_sum_of_file_sizes(files in arb_file_list()) {
let refs: Vec<(&str, u64)> = files.iter().map(|(p, s)| (p.as_str(), *s)).collect();
let tree = build_tree(&refs);
let expected: u64 = files.iter().map(|(_, s)| *s).sum();
prop_assert_eq!(tree.total_size(), expected);
}
#[test]
fn prop____total_file_count_equals_input_count(files in arb_file_list()) {
let refs: Vec<(&str, u64)> = files.iter().map(|(p, s)| (p.as_str(), *s)).collect();
let tree = build_tree(&refs);
prop_assert!(tree.total_file_count() <= files.len());
}
#[test]
fn prop____echarts_preserves_total_size(files in arb_file_list()) {
let refs: Vec<(&str, u64)> = files.iter().map(|(p, s)| (p.as_str(), *s)).collect();
let tree = build_tree(&refs);
let expected = tree.total_size();
let echarts_children: Vec<EChartsNode> = tree
.children
.iter()
.map(|(name, node)| node.to_echarts(name))
.collect();
let echarts_size: u64 = echarts_children.iter().map(echarts_total_size).sum();
prop_assert_eq!(echarts_size, expected);
}
#[test]
fn prop____echarts_serializes_to_valid_json(files in arb_file_list()) {
let refs: Vec<(&str, u64)> = files.iter().map(|(p, s)| (p.as_str(), *s)).collect();
let tree = build_tree(&refs);
let echarts_children: Vec<EChartsNode> = tree
.children
.iter()
.map(|(name, node)| node.to_echarts(name))
.collect();
let json = serde_json::to_string(&echarts_children).unwrap();
let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
prop_assert!(parsed.is_array());
}
#[test]
fn prop____every_dir_total_size_gte_children_sum(files in arb_file_list()) {
let refs: Vec<(&str, u64)> = files.iter().map(|(p, s)| (p.as_str(), *s)).collect();
let tree = build_tree(&refs);
fn check_invariant(node: &DirNode) -> bool {
let children_sum: u64 = node.children.values().map(|c| c.total_size()).sum();
if node.total_size() < children_sum {
return false;
}
node.children.values().all(check_invariant)
}
prop_assert!(check_invariant(&tree));
}
}