#![recursion_limit = "256"] use parallel_disk_usage::{ bytes_format::BytesFormat::*, data_tree::DataTree, size::{self, Blocks, Bytes}, visualizer::{BarAlignment, ColumnWidthDistribution, Direction, Visualizer}, }; use pretty_assertions::assert_eq; use std::{cmp::Ordering, num::NonZeroUsize}; use text_block_macros::text_block_fnl; use zero_copy_pads::Width; fn order_tree( left: &DataTree, right: &DataTree, ) -> Ordering { left.size().cmp(&right.size()).reverse() } macro_rules! test_case { ( $(#[$attributes:meta])* $name:ident where tree = $tree:expr, bytes_format = $bytes_format:expr, max_depth = $max_depth:expr, column_width_distribution = $column_width_function:ident $($column_width_arguments:literal)+, direction = $direction:ident, bar_alignment = $bar_alignment:ident, expected = $expected:expr, ) => { $(#[$attributes])* #[test] fn $name() { let tree = $tree; let column_width_distribution = ColumnWidthDistribution::$column_width_function($($column_width_arguments),+); let max_depth = NonZeroUsize::new($max_depth).expect("non-zero max_depth"); let actual = Visualizer { max_depth, column_width_distribution, data_tree: &tree, bytes_format: $bytes_format, direction: Direction::$direction, bar_alignment: BarAlignment::$bar_alignment, } .to_string(); let expected = $expected; eprintln!("\nACTUAL:\n{}\n", &actual); let actual_lines: Vec<_> = actual.lines().collect(); let expected_lines: Vec<_> = expected.lines().collect(); assert_eq!(actual_lines, expected_lines); if let ColumnWidthDistribution::Total { width } = column_width_distribution { let actual_line_widths: Vec<_> = actual .lines() .map(|line| line.width()) .collect(); let expected_line_widths: Vec<_> = expected .lines() .map(|_| width) .collect(); assert_eq!(actual_line_widths, expected_line_widths); } } }; } fn typical_tree(size_per_dir: Size, file_size_factor: u64) -> DataTree<&'static str, Size> where Size: size::Size + Ord + From + Send, { let dir = DataTree::<&'static str, Size>::fixed_size_dir_constructor(size_per_dir); let file = |name: &'static str, size: u64| DataTree::file(name, Size::from(size * file_size_factor)); dir( "root", vec![ file("foo", 2530), file("bar", 52), dir( "hello", vec![dir("world", vec![file("hello", 45), file("world", 54)])], ), dir("empty dir", vec![]), dir( "directory with a really long name", vec![dir( "subdirectory with a really long name", vec![file("file with a really long name", 475)], )], ), ], ) .into_par_sorted(order_tree) } test_case! { typical_bottom_up_binary where tree = typical_tree::(4096.into(), 1), bytes_format = BinaryUnits, max_depth = 10, column_width_distribution = total 90, direction = BottomUp, bar_alignment = Right, expected = text_block_fnl! { " 52 ┌──bar │ │ 0%" " 2.5K ├──foo │ ███│ 9%" " 4.0K ├──empty dir │ █████│ 15%" " 45 │ ┌──hello │ ░░░░░▒▒▒▒▒│ 0%" " 54 │ ├──world │ ░░░░░▒▒▒▒▒│ 0%" " 4.1K │ ┌─┴world │ ░░░░░█████│ 15%" " 8.1K ├─┴hello │ ██████████│ 30%" "475 │ ┌──file with a really long name │ ░░░░░▒▒▒▒▒█│ 2%" " 4.5K │ ┌─┴subdirectory with a really long name│ ░░░░░██████│ 16%" " 8.5K ├─┴directory with a really long name │ ███████████│ 31%" " 27.1K ┌─┴root │██████████████████████████████████│100%" }, } test_case! { typical_bottom_up_metric where tree = typical_tree::(4096.into(), 1), bytes_format = MetricUnits, max_depth = 10, column_width_distribution = total 90, direction = BottomUp, bar_alignment = Right, expected = text_block_fnl! { " 52 ┌──bar │ │ 0%" " 2.5K ├──foo │ ███│ 9%" " 4.1K ├──empty dir │ █████│ 15%" " 45 │ ┌──hello │ ░░░░░▒▒▒▒▒│ 0%" " 54 │ ├──world │ ░░░░░▒▒▒▒▒│ 0%" " 4.2K │ ┌─┴world │ ░░░░░█████│ 15%" " 8.3K ├─┴hello │ ██████████│ 30%" "475 │ ┌──file with a really long name │ ░░░░░▒▒▒▒▒█│ 2%" " 4.6K │ ┌─┴subdirectory with a really long name│ ░░░░░██████│ 16%" " 8.7K ├─┴directory with a really long name │ ███████████│ 31%" " 27.7K ┌─┴root │██████████████████████████████████│100%" }, } test_case! { typical_top_down_binary where tree = typical_tree::(4096.into(), 1), bytes_format = BinaryUnits, max_depth = 10, column_width_distribution = total 90, direction = TopDown, bar_alignment = Right, expected = text_block_fnl! { " 27.1K └─┬root │██████████████████████████████████│100%" " 8.5K ├─┬directory with a really long name │ ███████████│ 31%" " 4.5K │ └─┬subdirectory with a really long name│ ░░░░░██████│ 16%" "475 │ └──file with a really long name │ ░░░░░▒▒▒▒▒█│ 2%" " 8.1K ├─┬hello │ ██████████│ 30%" " 4.1K │ └─┬world │ ░░░░░█████│ 15%" " 54 │ ├──world │ ░░░░░▒▒▒▒▒│ 0%" " 45 │ └──hello │ ░░░░░▒▒▒▒▒│ 0%" " 4.0K ├──empty dir │ █████│ 15%" " 2.5K ├──foo │ ███│ 9%" " 52 └──bar │ │ 0%" }, } test_case! { typical_bottom_up_binary_align_left where tree = typical_tree::(4096.into(), 1), bytes_format = BinaryUnits, max_depth = 10, column_width_distribution = total 90, direction = BottomUp, bar_alignment = Left, expected = text_block_fnl! { " 52 ┌──bar │ │ 0%" " 2.5K ├──foo │███ │ 9%" " 4.0K ├──empty dir │█████ │ 15%" " 45 │ ┌──hello │▒▒▒▒▒░░░░░ │ 0%" " 54 │ ├──world │▒▒▒▒▒░░░░░ │ 0%" " 4.1K │ ┌─┴world │█████░░░░░ │ 15%" " 8.1K ├─┴hello │██████████ │ 30%" "475 │ ┌──file with a really long name │█▒▒▒▒▒░░░░░ │ 2%" " 4.5K │ ┌─┴subdirectory with a really long name│██████░░░░░ │ 16%" " 8.5K ├─┴directory with a really long name │███████████ │ 31%" " 27.1K ┌─┴root │██████████████████████████████████│100%" }, } test_case! { typical_narrow_tree_column where tree = typical_tree::(4096.into(), 1), bytes_format = BinaryUnits, max_depth = 10, column_width_distribution = components 24 49, direction = BottomUp, bar_alignment = Right, expected = text_block_fnl! { " 52 ┌──bar │ │ 0%" " 2.5K ├──foo │ ████│ 9%" " 4.0K ├──empty dir │ ███████│ 15%" " 45 │ ┌──hello │ ░░░░░░░░▒▒▒▒▒▒▒│ 0%" " 54 │ ├──world │ ░░░░░░░░▒▒▒▒▒▒▒│ 0%" " 4.1K │ ┌─┴world │ ░░░░░░░░███████│ 15%" " 8.1K ├─┴hello │ ███████████████│ 30%" "475 │ ┌──file with a ...│ ░░░░░░░▒▒▒▒▒▒▒█│ 2%" " 4.5K │ ┌─┴subdirectory w...│ ░░░░░░░████████│ 16%" " 8.5K ├─┴directory with a...│ ███████████████│ 31%" " 27.1K ┌─┴root │█████████████████████████████████████████████████│100%" }, } test_case! { typical_even_shorter_max_width where tree = typical_tree::(4096.into(), 1), bytes_format = BinaryUnits, max_depth = 10, column_width_distribution = components 10 23, direction = BottomUp, bar_alignment = Right, expected = text_block_fnl! { " 52 ┌──bar │ │ 0%" " 2.5K ├──foo │ ██│ 9%" " 4.0K ├──em...│ ███│ 15%" " 8.1K ├──hello│ ███████│ 30%" " 8.5K ├──di...│ ███████│ 31%" " 27.1K ┌─┴root │███████████████████████│100%" }, } test_case! { typical_sufficient_depth where tree = typical_tree::(4096.into(), 1), bytes_format = BinaryUnits, max_depth = 4, column_width_distribution = total 90, direction = BottomUp, bar_alignment = Right, expected = text_block_fnl! { " 52 ┌──bar │ │ 0%" " 2.5K ├──foo │ ███│ 9%" " 4.0K ├──empty dir │ █████│ 15%" " 45 │ ┌──hello │ ░░░░░▒▒▒▒▒│ 0%" " 54 │ ├──world │ ░░░░░▒▒▒▒▒│ 0%" " 4.1K │ ┌─┴world │ ░░░░░█████│ 15%" " 8.1K ├─┴hello │ ██████████│ 30%" "475 │ ┌──file with a really long name │ ░░░░░▒▒▒▒▒█│ 2%" " 4.5K │ ┌─┴subdirectory with a really long name│ ░░░░░██████│ 16%" " 8.5K ├─┴directory with a really long name │ ███████████│ 31%" " 27.1K ┌─┴root │██████████████████████████████████│100%" }, } test_case! { typical_shallow where tree = typical_tree::(4096.into(), 1), bytes_format = BinaryUnits, max_depth = 3, column_width_distribution = total 90, direction = BottomUp, bar_alignment = Right, expected = text_block_fnl! { "52 ┌──bar │ │ 0%" " 2.5K ├──foo │ ███│ 9%" " 4.0K ├──empty dir │ █████│ 15%" " 4.1K │ ┌──world │ ░░░░░█████│ 15%" " 8.1K ├─┴hello │ ██████████│ 30%" " 4.5K │ ┌──subdirectory with a really long name│ ░░░░░██████│ 16%" " 8.5K ├─┴directory with a really long name │ ███████████│ 31%" "27.1K ┌─┴root │███████████████████████████████████│100%" }, } test_case! { typical_flat where tree = typical_tree::(4096.into(), 1), bytes_format = BinaryUnits, max_depth = 2, column_width_distribution = total 90, direction = BottomUp, bar_alignment = Right, expected = text_block_fnl! { "52 ┌──bar │ │ 0%" " 2.5K ├──foo │ ████│ 9%" " 4.0K ├──empty dir │ ██████│ 15%" " 8.1K ├──hello │ ████████████│ 30%" " 8.5K ├──directory with a really long name│ █████████████│ 31%" "27.1K ┌─┴root │████████████████████████████████████████│100%" }, } test_case! { typical_root_only where tree = typical_tree::(4096.into(), 1), bytes_format = BinaryUnits, max_depth = 1, column_width_distribution = total 90, direction = BottomUp, bar_alignment = Right, expected = text_block_fnl! { "27.1K ┌──root│███████████████████████████████████████████████████████████████████████│100%" }, } test_case! { typical_binary_tebi_scale where tree = typical_tree::(4096.into(), 1 << 40), bytes_format = BinaryUnits, max_depth = 10, column_width_distribution = total 90, direction = BottomUp, bar_alignment = Right, expected = text_block_fnl! { " 4.0K ┌──empty dir │ │ 0%" " 52.0T ├──bar │ █│ 2%" " 45.0T │ ┌──hello │ ▒│ 1%" " 54.0T │ ├──world │ █│ 2%" " 99.0T │ ┌─┴world │ █│ 3%" " 99.0T ├─┴hello │ █│ 3%" "475.0T │ ┌──file with a really long name │ █████│ 15%" "475.0T │ ┌─┴subdirectory with a really long name│ █████│ 15%" "475.0T ├─┴directory with a really long name │ █████│ 15%" " 2.5P ├──foo │ ███████████████████████████│ 80%" " 3.1P ┌─┴root │██████████████████████████████████│100%" }, } test_case! { typical_metric_tebi_scale where tree = typical_tree::(4096.into(), 1 << 40), bytes_format = MetricUnits, max_depth = 10, column_width_distribution = total 90, direction = BottomUp, bar_alignment = Right, expected = text_block_fnl! { " 4.1K ┌──empty dir │ │ 0%" " 57.2T ├──bar │ █│ 2%" " 49.5T │ ┌──hello │ ▒│ 1%" " 59.4T │ ├──world │ █│ 2%" "108.9T │ ┌─┴world │ █│ 3%" "108.9T ├─┴hello │ █│ 3%" "522.3T │ ┌──file with a really long name │ █████│ 15%" "522.3T │ ┌─┴subdirectory with a really long name│ █████│ 15%" "522.3T ├─┴directory with a really long name │ █████│ 15%" " 2.8P ├──foo │ ███████████████████████████│ 80%" " 3.5P ┌─┴root │██████████████████████████████████│100%" }, } test_case! { typical_empty_files where tree = typical_tree::(4096.into(), 0), bytes_format = BinaryUnits, max_depth = 10, column_width_distribution = total 90, direction = BottomUp, bar_alignment = Right, expected = text_block_fnl! { " 0 ┌──bar │ │ 0%" " 0 ├──foo │ │ 0%" " 4.0K ├──empty dir │ ██████│ 17%" " 0 │ ┌──file with a really long name │ ░░░░░░▒▒▒▒▒▒│ 0%" " 4.0K │ ┌─┴subdirectory with a really long name│ ░░░░░░██████│ 17%" " 8.0K ├─┴directory with a really long name │ ████████████│ 33%" " 0 │ ┌──world │ ░░░░░░▒▒▒▒▒▒│ 0%" " 0 │ ├──hello │ ░░░░░░▒▒▒▒▒▒│ 0%" " 4.0K │ ┌─┴world │ ░░░░░░██████│ 17%" " 8.0K ├─┴hello │ ████████████│ 33%" "24.0K ┌─┴root │███████████████████████████████████│100%" }, } test_case! { typical_empty_files_zero_sized_inodes where tree = typical_tree::(0.into(), 0), bytes_format = BinaryUnits, max_depth = 10, column_width_distribution = total 90, direction = BottomUp, bar_alignment = Right, expected = text_block_fnl! { "0 ┌──file with a really long name │ │ 0%" "0 ┌─┴subdirectory with a really long name│ │ 0%" "0 ┌─┴directory with a really long name │ │ 0%" "0 ├──empty dir │ │ 0%" "0 │ ┌──world │ │ 0%" "0 │ ├──hello │ │ 0%" "0 │ ┌─┴world │ │ 0%" "0 ├─┴hello │ │ 0%" "0 ├──bar │ │ 0%" "0 ├──foo │ │ 0%" "0 ┌─┴root │ │ 0%" }, } fn nested_tree( dir_names: &[&'static str], size_per_dir: Size, file_name: &'static str, file_size: Size, ) -> DataTree<&'static str, Size> { if let Some((head, tail)) = dir_names.split_first() { let child = nested_tree(tail, size_per_dir, file_name, file_size); DataTree::dir(*head, size_per_dir, vec![child]) } else { DataTree::file(file_name, file_size) } } test_case! { nested_bottom_up_binary where tree = nested_tree::( &["a", "b", "c", "d", "e", "f"], 4096.into(), "z", 1024.into(), ), bytes_format = BinaryUnits, max_depth = 10, column_width_distribution = total 90, direction = BottomUp, bar_alignment = Right, expected = text_block_fnl! { " 1.0K ┌──z│ ░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓██│ 4%" " 5.0K ┌─┴f │ ░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓████████████│ 20%" " 9.0K ┌─┴e │ ░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓██████████████████████│ 36%" "13.0K ┌─┴d │ ░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒████████████████████████████████│ 52%" "17.0K ┌─┴c │ ░░░░░░░░░░██████████████████████████████████████████│ 68%" "21.0K ┌─┴b │ ████████████████████████████████████████████████████│ 84%" "25.0K ┌─┴a │██████████████████████████████████████████████████████████████│100%" }, } test_case! { nested_bottom_up_metric where tree = nested_tree::( &["a", "b", "c", "d", "e", "f"], 4096.into(), "z", 1024.into(), ), bytes_format = MetricUnits, max_depth = 10, column_width_distribution = total 90, direction = BottomUp, bar_alignment = Right, expected = text_block_fnl! { " 1.0K ┌──z│ ░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓██│ 4%" " 5.1K ┌─┴f │ ░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓████████████│ 20%" " 9.2K ┌─┴e │ ░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓██████████████████████│ 36%" "13.3K ┌─┴d │ ░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒████████████████████████████████│ 52%" "17.4K ┌─┴c │ ░░░░░░░░░░██████████████████████████████████████████│ 68%" "21.5K ┌─┴b │ ████████████████████████████████████████████████████│ 84%" "25.6K ┌─┴a │██████████████████████████████████████████████████████████████│100%" }, } test_case! { nested_top_down_binary where tree = nested_tree::( &["a", "b", "c", "d", "e", "f"], 4096.into(), "z", 1024.into(), ), bytes_format = BinaryUnits, max_depth = 10, column_width_distribution = total 90, direction = TopDown, bar_alignment = Right, expected = text_block_fnl! { "25.0K └─┬a │██████████████████████████████████████████████████████████████│100%" "21.0K └─┬b │ ████████████████████████████████████████████████████│ 84%" "17.0K └─┬c │ ░░░░░░░░░░██████████████████████████████████████████│ 68%" "13.0K └─┬d │ ░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒████████████████████████████████│ 52%" " 9.0K └─┬e │ ░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓██████████████████████│ 36%" " 5.0K └─┬f │ ░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓████████████│ 20%" " 1.0K └──z│ ░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓██│ 4%" }, } test_case! { nested_bottom_up_blocks where tree = nested_tree::( &["a", "b", "c", "d", "e", "f"], 8.into(), "z", 2.into(), ), bytes_format = (), max_depth = 10, column_width_distribution = total 90, direction = BottomUp, bar_alignment = Right, expected = text_block_fnl! { " 2 ┌──z│ ░░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓███│ 4%" "10 ┌─┴f │ ░░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█████████████│ 20%" "18 ┌─┴e │ ░░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓███████████████████████│ 36%" "26 ┌─┴d │ ░░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒██████████████████████████████████│ 52%" "34 ┌─┴c │ ░░░░░░░░░░░████████████████████████████████████████████│ 68%" "42 ┌─┴b │ ███████████████████████████████████████████████████████│ 84%" "50 ┌─┴a │█████████████████████████████████████████████████████████████████│100%" }, } test_case! { nested_bottom_up_binary_align_left where tree = nested_tree::( &["a", "b", "c", "d", "e", "f"], 4096.into(), "z", 1024.into(), ), bytes_format = BinaryUnits, max_depth = 10, column_width_distribution = total 90, direction = BottomUp, bar_alignment = Left, expected = text_block_fnl! { " 1.0K ┌──z│██▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░ │ 4%" " 5.0K ┌─┴f │████████████▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░ │ 20%" " 9.0K ┌─┴e │██████████████████████▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░ │ 36%" "13.0K ┌─┴d │████████████████████████████████▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░ │ 52%" "17.0K ┌─┴c │██████████████████████████████████████████░░░░░░░░░░ │ 68%" "21.0K ┌─┴b │████████████████████████████████████████████████████ │ 84%" "25.0K ┌─┴a │██████████████████████████████████████████████████████████████│100%" }, } test_case! { nested_top_down_binary_align_left where tree = nested_tree::( &["a", "b", "c", "d", "e", "f"], 4096.into(), "z", 1024.into(), ), bytes_format = BinaryUnits, max_depth = 10, column_width_distribution = total 90, direction = TopDown, bar_alignment = Left, expected = text_block_fnl! { "25.0K └─┬a │██████████████████████████████████████████████████████████████│100%" "21.0K └─┬b │████████████████████████████████████████████████████ │ 84%" "17.0K └─┬c │██████████████████████████████████████████░░░░░░░░░░ │ 68%" "13.0K └─┬d │████████████████████████████████▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░ │ 52%" " 9.0K └─┬e │██████████████████████▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░ │ 36%" " 5.0K └─┬f │████████████▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░ │ 20%" " 1.0K └──z│██▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░ │ 4%" }, } test_case! { nested_bottom_up_binary_long_names_narrow_tree_column where tree = nested_tree::( &[ "directory with a long name", "child directory with a long name", "grandchild directory with a long name", "great-grandchild directory with a long name", "great-great-grandchild directory with a long name", ], 4096.into(), "file with a long name", 1024.into(), ), bytes_format = BinaryUnits, max_depth = 10, column_width_distribution = components 27 57, direction = BottomUp, bar_alignment = Right, expected = text_block_fnl! { " 1.0K ┌──file with a...│ ░░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓███│ 5%" " 5.0K ┌─┴great-great-g...│ ░░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓██████████████│ 24%" " 9.0K ┌─┴great-grandchil...│ ░░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▒████████████████████████│ 43%" "13.0K ┌─┴grandchild direct...│ ░░░░░░░░░░░███████████████████████████████████│ 62%" "17.0K ┌─┴child directory wit...│ ██████████████████████████████████████████████│ 81%" "21.0K ┌─┴directory with a long...│█████████████████████████████████████████████████████████│100%" }, } test_case! { nested_bottom_up_binary_many_names_narrow_tree_column where tree = nested_tree::( &[ "a", "ab", "abc", "abcd", "abcde", "abcdef", "abcdefg", "abcdefgh", "abcdefghi", "abcdefghij", "abcdefghijk", "abcdefghijkl", ], 4096.into(), "xyz", 1024.into(), ), bytes_format = BinaryUnits, max_depth = 10, column_width_distribution = components 24 50, direction = BottomUp, bar_alignment = Right, expected = text_block_fnl! { "17.0K ┌──ab...│ ░░░░▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█████████████████│ 35%" "21.0K ┌─┴abcd...│ ░░░░▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█████████████████████│ 43%" "25.0K ┌─┴abcdefg │ ░░░░▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓██████████████████████████│ 51%" "29.0K ┌─┴abcdef │ ░░░░▒▒▒▒▓▓▓▓▓▓▓▓██████████████████████████████│ 59%" "33.0K ┌─┴abcde │ ░░░░▒▒▒▒▓▓▓▓██████████████████████████████████│ 67%" "37.0K ┌─┴abcd │ ░░░░▒▒▒▒██████████████████████████████████████│ 76%" "41.0K ┌─┴abc │ ░░░░██████████████████████████████████████████│ 84%" "45.0K ┌─┴ab │ ██████████████████████████████████████████████│ 92%" "49.0K ┌─┴a │██████████████████████████████████████████████████│100%" }, } test_case! { nested_bottom_up_binary_tebi_scale where tree = nested_tree::( &["a", "b", "c", "d", "e", "f"], (4 << 40).into(), "z", (1 << 40).into(), ), bytes_format = BinaryUnits, max_depth = 10, column_width_distribution = total 90, direction = BottomUp, bar_alignment = Right, expected = text_block_fnl! { " 1.0T ┌──z│ ░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓██│ 4%" " 5.0T ┌─┴f │ ░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓████████████│ 20%" " 9.0T ┌─┴e │ ░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓██████████████████████│ 36%" "13.0T ┌─┴d │ ░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒████████████████████████████████│ 52%" "17.0T ┌─┴c │ ░░░░░░░░░░██████████████████████████████████████████│ 68%" "21.0T ┌─┴b │ ████████████████████████████████████████████████████│ 84%" "25.0T ┌─┴a │██████████████████████████████████████████████████████████████│100%" }, } fn empty_dir(inode_size: Size) -> DataTree<&'static str, Size> where Size: size::Size + Ord + From + Send, { DataTree::dir("empty directory", inode_size, Vec::new()).into_par_sorted(order_tree) } test_case! { empty_dir_non_zero_inode where tree = empty_dir::(4069.into()), bytes_format = MetricUnits, max_depth = 10, column_width_distribution = total 90, direction = BottomUp, bar_alignment = Right, expected = text_block_fnl! { "4.1K ┌──empty directory│█████████████████████████████████████████████████████████████│100%" }, } test_case! { empty_dir_zero_sized_inode where tree = empty_dir::(0.into()), bytes_format = MetricUnits, max_depth = 10, column_width_distribution = total 90, direction = BottomUp, bar_alignment = Right, expected = text_block_fnl! { "0 ┌──empty directory│ │ 0%" }, } test_case! { empty_file where tree = DataTree::file("empty file", Bytes::from(0)), bytes_format = MetricUnits, max_depth = 10, column_width_distribution = total 90, direction = BottomUp, bar_alignment = Right, expected = text_block_fnl! { "0 ┌──empty file│ │ 0%" }, } fn long_and_short_names() -> DataTree<&'static str, Size> where Size: size::Size + Ord + From + Send, { let dir = DataTree::<&'static str, Size>::fixed_size_dir_constructor(1.into()); let file = |name: &'static str, size: u64| DataTree::file(name, Size::from(size)); dir( "root", vec![ file("a", 1), file("file with a long name 1", 2), file("b", 3), file("file with a long name 2", 4), dir( "c", vec![ file("a", 1), file("file with a long name 1", 2), file("b", 3), file("file with a long name 2", 4), file("weight", 5), ], ), dir( "directory with a long name 1", vec![ file("a", 1), file("file with a long name 1", 2), file("b", 3), file("file with a long name 2", 4), file("weight", 6), ], ), dir( "d", vec![ file("a", 1), file("file with a long name 1", 2), file("b", 3), file("file with a long name 2", 4), file("weight", 7), ], ), dir( "directory with a long name 2", vec![ file("a", 1), file("file with a long name 1", 2), file("b", 3), file("file with a long name 2", 4), file("weight", 8), ], ), ], ) .into_par_sorted(order_tree) } test_case! { long_and_short_names_fit_tree_column where tree = long_and_short_names::(), bytes_format = (), max_depth = 10, column_width_distribution = components 33 25, direction = BottomUp, bar_alignment = Right, expected = text_block_fnl! { " 1 ┌──a │ │ 1%" " 2 ├──file with a long name 1 │ █│ 2%" " 3 ├──b │ █│ 4%" " 4 ├──file with a long name 2 │ █│ 5%" " 1 │ ┌──a │ ░░░░░│ 1%" " 2 │ ├──file with a long name 1 │ ░░░░█│ 2%" " 3 │ ├──b │ ░░░░█│ 4%" " 4 │ ├──file with a long name 2 │ ░░░░█│ 5%" " 5 │ ├──weight │ ░░░██│ 6%" "16 ├─┴c │ █████│ 20%" " 1 │ ┌──a │ ░░░░░│ 1%" " 2 │ ├──file with a long name 1 │ ░░░░█│ 2%" " 3 │ ├──b │ ░░░░█│ 4%" " 4 │ ├──file with a long name 2 │ ░░░░█│ 5%" " 6 │ ├──weight │ ░░░██│ 7%" "17 ├─┴directory with a long name 1│ █████│ 21%" " 1 │ ┌──a │ ░░░░░░│ 1%" " 2 │ ├──file with a long name 1 │ ░░░░░█│ 2%" " 3 │ ├──b │ ░░░░░█│ 4%" " 4 │ ├──file with a long name 2 │ ░░░░░█│ 5%" " 7 │ ├──weight │ ░░░░██│ 9%" "18 ├─┴d │ ██████│ 22%" " 1 │ ┌──a │ ░░░░░░│ 1%" " 2 │ ├──file with a long name 1 │ ░░░░░█│ 2%" " 3 │ ├──b │ ░░░░░█│ 4%" " 4 │ ├──file with a long name 2 │ ░░░░░█│ 5%" " 8 │ ├──weight │ ░░░░██│ 10%" "19 ├─┴directory with a long name 2│ ██████│ 23%" "81 ┌─┴root │█████████████████████████│100%" }, } test_case! { remaining_siblings_properly_connect_when_some_amongst_them_disappear where tree = long_and_short_names::(), bytes_format = (), max_depth = 10, column_width_distribution = components 10 25, direction = BottomUp, bar_alignment = Right, expected = text_block_fnl! { " 1 ┌──a │ │ 1%" " 2 ├──fi...│ █│ 2%" " 3 ├──b │ █│ 4%" " 4 ├──fi...│ █│ 5%" " 1 │ ┌──a │ ░░░░░│ 1%" " 3 │ ├──b │ ░░░░█│ 4%" "16 ├─┴c │ █████│ 20%" " 1 │ ┌──a │ ░░░░░│ 1%" " 3 │ ├──b │ ░░░░█│ 4%" "17 ├─┴di...│ █████│ 21%" " 1 │ ┌──a │ ░░░░░░│ 1%" " 3 │ ├──b │ ░░░░░█│ 4%" "18 ├─┴d │ ██████│ 22%" " 1 │ ┌──a │ ░░░░░░│ 1%" " 3 │ ├──b │ ░░░░░█│ 4%" "19 ├─┴di...│ ██████│ 23%" "81 ┌─┴root │█████████████████████████│100%" }, } test_case! { children_of_disappeared_nodes_also_disappear where tree = long_and_short_names::(), bytes_format = (), max_depth = 10, column_width_distribution = components 8 25, direction = BottomUp, bar_alignment = Right, expected = text_block_fnl! { " 1 ┌──a │ │ 1%" " 3 ├──b │ █│ 4%" " 1 │ ┌──a│ ░░░░░│ 1%" " 3 │ ├──b│ ░░░░█│ 4%" "16 ├─┴c │ █████│ 20%" " 1 │ ┌──a│ ░░░░░░│ 1%" " 3 │ ├──b│ ░░░░░█│ 4%" "18 ├─┴d │ ██████│ 22%" "81 ┌─┴root │█████████████████████████│100%" }, } test_case! { remaining_siblings_properly_connect_when_some_amongst_them_disappear_top_down where tree = long_and_short_names::(), bytes_format = (), max_depth = 10, column_width_distribution = components 10 25, direction = TopDown, bar_alignment = Right, expected = text_block_fnl! { "81 └─┬root │█████████████████████████│100%" "19 ├─┬di...│ ██████│ 23%" " 3 │ ├──b │ ░░░░░█│ 4%" " 1 │ └──a │ ░░░░░░│ 1%" "18 ├─┬d │ ██████│ 22%" " 3 │ ├──b │ ░░░░░█│ 4%" " 1 │ └──a │ ░░░░░░│ 1%" "17 ├─┬di...│ █████│ 21%" " 3 │ ├──b │ ░░░░█│ 4%" " 1 │ └──a │ ░░░░░│ 1%" "16 ├─┬c │ █████│ 20%" " 3 │ ├──b │ ░░░░█│ 4%" " 1 │ └──a │ ░░░░░│ 1%" " 4 ├──fi...│ █│ 5%" " 3 ├──b │ █│ 4%" " 2 ├──fi...│ █│ 2%" " 1 └──a │ │ 1%" }, } test_case! { children_of_disappeared_nodes_also_disappear_top_down where tree = long_and_short_names::(), bytes_format = (), max_depth = 10, column_width_distribution = components 8 25, direction = TopDown, bar_alignment = Right, expected = text_block_fnl! { "81 └─┬root │█████████████████████████│100%" "18 ├─┬d │ ██████│ 22%" " 3 │ ├──b│ ░░░░░█│ 4%" " 1 │ └──a│ ░░░░░░│ 1%" "16 ├─┬c │ █████│ 20%" " 3 │ ├──b│ ░░░░█│ 4%" " 1 │ └──a│ ░░░░░│ 1%" " 3 ├──b │ █│ 4%" " 1 └──a │ │ 1%" }, } fn tree_with_a_file_of_extremely_long_name() -> DataTree<&'static str, Size> where Size: size::Size + Ord + From + Send, { let dir = DataTree::<&'static str, Size>::fixed_size_dir_constructor(4069.into()); let file = |name: &'static str, size: u64| DataTree::file(name, Size::from(size)); dir( "root", vec![file( "file with a very super extraordinary extremely long name", 4069, )], ) .into_par_sorted(order_tree) } test_case! { width_of_tree_column_is_prioritized_before_bar_column where tree = tree_with_a_file_of_extremely_long_name::(), bytes_format = MetricUnits, max_depth = 10, column_width_distribution = total 85, direction = BottomUp, bar_alignment = Right, expected = text_block_fnl! { "4.1K ┌──file with a very super extraordinary extremely long name│ ███████│ 50%" "8.1K ┌─┴root │█████████████│100%" }, } test_case! { bar_column_has_a_minimum_width where tree = tree_with_a_file_of_extremely_long_name::(), bytes_format = BinaryUnits, max_depth = 10, column_width_distribution = total 50, direction = BottomUp, bar_alignment = Right, expected = text_block_fnl! { "4.0K ┌──file with a very super ex...│ ███│ 50%" "7.9K ┌─┴root │██████│100%" }, } fn big_tree_with_long_names() -> DataTree<&'static str, Size> where Size: size::Size + Ord + From + Send, { let dir = DataTree::<&'static str, Size>::fixed_size_dir_constructor(4069.into()); let file = |name: &'static str, size: u64| DataTree::file(name, Size::from(size)); let mut short_file_names = [ "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", ] .iter(); let mut short_file = || { let name = short_file_names.next().expect("access short file name"); file(name, 750) }; dir( "root", vec![ dir( "sub 1", vec![ file("first file with a long name", 999), dir( "sub 1.1", vec![ file("second file with a long name", 7766), short_file(), short_file(), dir( "first sub directory with a long name", vec![file("abc", 123), file("def", 456), short_file()], ), dir( "second sub directory with a long name", vec![ file("abcdefghi", 1234), file("ihgfedbca", 4321), file("abc abc abc", 1212), file("third file with a long name", 4545), short_file(), short_file(), short_file(), ], ), ], ), dir( "sub 1.2", vec![ short_file(), dir( "third sub directory with a long name", vec![dir( "forth sub directory with a long name", vec![file("forth file with a long name", 3647), short_file()], )], ), dir( "fifth sub directory with a long name", vec![ short_file(), short_file(), dir( "sixth sub directory with a long name", vec![ file("fifth file with a long name", 364), short_file(), short_file(), short_file(), ], ), ], ), dir( "sixth sub directory with a long name", vec![ short_file(), dir( "seventh sub directory with a long name", vec![ file("sixth file with a long name", 6565), file("seventh file with a long name", 555), short_file(), ], ), dir( "eighth sub directory with a long name", vec![ file("eighth file with a long name", 444), short_file(), short_file(), short_file(), ], ), file("ninth file with a long name", 777), ], ), ], ), ], ), dir( "sub 2", vec![ dir( "sub 2.1", vec![ short_file(), dir( "ninth sub directory with a long name", vec![ file("tenth file with a long name", 88888), short_file(), short_file(), dir( "tenth sub directory with a long name", vec![ file("eleventh file with a long name", 44444), short_file(), short_file(), short_file(), ], ), ], ), ], ), dir( "sub 2.2", vec![ short_file(), dir( "eleventh sub directory with a long name", vec![ file("twelfth file with a long name", 453), file("thirteenth file with a long name", 352), short_file(), dir( "twelfth sub directory with a long name", vec![ file("fourteenth file with a long name", 128), short_file(), ], ), dir( "thirteenth sub directory with a long name", vec![ file("fifteenth file with a long name", 128), file("sixteenth file with a long name", 256), file("seventeenth file with a long name", 512), short_file(), ], ), ], ), dir( "fourteenth sub directory with a long name", vec![ file("eighteenth file with a long name", 542), file("eighty-first file with a long name", 357), short_file(), short_file(), short_file(), dir( "twelfth sub directory with a long name", vec![ file("eighty-second file with a long name", 222), short_file(), short_file(), ], ), dir( "fifteenth sub directory with a long name", vec![ file("eighty-third file with a long name", 333), file("eighty-fourth file with a long name", 344), file("eighty-seventh file with a long name", 444), short_file(), ], ), ], ), ], ), ], ), ], ) .into_par_sorted(order_tree) } test_case! { big_tree_with_long_names_narrow_tree_column where tree = big_tree_with_long_names::(), bytes_format = BinaryUnits, max_depth = 100, column_width_distribution = components 16 34, direction = BottomUp, bar_alignment = Right, expected = text_block_fnl! { "999 ┌──first ...│ ░░░░░░░░░░░│ 0%" "750 │ ┌──b │ ░░░░░░░▒▒▒▒│ 0%" "750 │ ├──a │ ░░░░░░░▒▒▒▒│ 0%" "123 │ │ ┌──abc │ ░░░░░░░▒▒▒▓│ 0%" "456 │ │ ├──def │ ░░░░░░░▒▒▒▓│ 0%" "750 │ │ ├──c │ ░░░░░░░▒▒▒▓│ 0%" " 5.3K │ ├─┴firs...│ ░░░░░░░▒▒▒█│ 2%" " 7.6K │ ├──seco...│ ░░░░░░░▒▒▒█│ 3%" "750 │ │ ┌──f │ ░░░░░░░▒▒▓▓│ 0%" "750 │ │ ├──e │ ░░░░░░░▒▒▓▓│ 0%" "750 │ │ ├──d │ ░░░░░░░▒▒▓▓│ 0%" " 1.2K │ │ ├──ab...│ ░░░░░░░▒▒▓▓│ 0%" " 1.2K │ │ ├──ab...│ ░░░░░░░▒▒▓▓│ 0%" " 4.2K │ │ ├──ih...│ ░░░░░░░▒▒▓█│ 1%" " 4.4K │ │ ├──th...│ ░░░░░░░▒▒▓█│ 2%" " 17.2K │ ├─┴seco...│ ░░░░░░░▒▒██│ 6%" " 35.5K ├─┴sub 1.1 │ ░░░░░░░████│ 12%" "750 │ ┌──g │ ░░░░░▒▒▒▒▒▒│ 0%" "750 │ │ ┌──j │ ░░░░░▒▒▒▒▒▓│ 0%" "750 │ │ ├──i │ ░░░░░▒▒▒▒▒▓│ 0%" "750 │ │ │ ┌──m │ ░░░░░▒▒▒▒▒▓│ 0%" "750 │ │ │ ├──l │ ░░░░░▒▒▒▒▒▓│ 0%" "750 │ │ │ ├──k │ ░░░░░▒▒▒▒▒▓│ 0%" " 6.5K │ │ ├─┴si...│ ░░░░░▒▒▒▒▒█│ 2%" " 12.0K │ ├─┴fift...│ ░░░░░▒▒▒▒▒█│ 4%" "750 │ │ ┌──h │ ░░░░░▒▒▒▒▒▓│ 0%" " 8.3K │ │ ┌─┴fo...│ ░░░░░▒▒▒▒▒█│ 3%" " 12.2K │ ├─┴thir...│ ░░░░░▒▒▒▒▒█│ 4%" "750 │ │ ┌──n │ ░░░░░▒▒▒▓▓▓│ 0%" "777 │ │ ├──ni...│ ░░░░░▒▒▒▓▓▓│ 0%" "750 │ │ │ ┌──r │ ░░░░░▒▒▒▓▓▓│ 0%" "750 │ │ │ ├──q │ ░░░░░▒▒▒▓▓▓│ 0%" "750 │ │ │ ├──p │ ░░░░░▒▒▒▓▓▓│ 0%" " 6.6K │ │ ├─┴ei...│ ░░░░░▒▒▒▓▓█│ 2%" "750 │ │ │ ┌──o │ ░░░░░▒▒▒▓▓▓│ 0%" " 11.7K │ │ ├─┴se...│ ░░░░░▒▒▒▓▓█│ 4%" " 23.7K │ ├─┴sixt...│ ░░░░░▒▒▒███│ 8%" " 52.6K ├─┴sub 1.2 │ ░░░░░██████│ 18%" " 93.1K ┌─┴sub 1 │ ███████████│ 32%" "750 │ ┌──y │ ░░░░░░░░░░░░░░░░░▒▒▒▒▒│ 0%" "352 │ │ ┌──th...│ ░░░░░░░░░░░░░░░░░▒▒▒▓▓│ 0%" "453 │ │ ├──tw...│ ░░░░░░░░░░░░░░░░░▒▒▒▓▓│ 0%" "750 │ │ ├──z │ ░░░░░░░░░░░░░░░░░▒▒▒▓▓│ 0%" "750 │ │ │ ┌──A │ ░░░░░░░░░░░░░░░░░▒▒▒▓▓│ 0%" " 4.8K │ │ ├─┴tw...│ ░░░░░░░░░░░░░░░░░▒▒▒▓█│ 2%" "750 │ │ │ ┌──B │ ░░░░░░░░░░░░░░░░░▒▒▒▓▓│ 0%" " 5.6K │ │ ├─┴th...│ ░░░░░░░░░░░░░░░░░▒▒▒▓█│ 2%" " 15.9K │ ├─┴elev...│ ░░░░░░░░░░░░░░░░░▒▒▒██│ 6%" "357 │ │ ┌──ei...│ ░░░░░░░░░░░░░░░░░▒▒▒▓▓│ 0%" "542 │ │ ├──ei...│ ░░░░░░░░░░░░░░░░░▒▒▒▓▓│ 0%" "750 │ │ ├──E │ ░░░░░░░░░░░░░░░░░▒▒▒▓▓│ 0%" "750 │ │ ├──D │ ░░░░░░░░░░░░░░░░░▒▒▒▓▓│ 0%" "750 │ │ ├──C │ ░░░░░░░░░░░░░░░░░▒▒▒▓▓│ 0%" "750 │ │ │ ┌──G │ ░░░░░░░░░░░░░░░░░▒▒▒▓▓│ 0%" "750 │ │ │ ├──F │ ░░░░░░░░░░░░░░░░░▒▒▒▓▓│ 0%" " 5.7K │ │ ├─┴tw...│ ░░░░░░░░░░░░░░░░░▒▒▒▓█│ 2%" "750 │ │ │ ┌──H │ ░░░░░░░░░░░░░░░░░▒▒▒▓▓│ 0%" " 5.8K │ │ ├─┴fi...│ ░░░░░░░░░░░░░░░░░▒▒▒▓█│ 2%" " 18.5K │ ├─┴four...│ ░░░░░░░░░░░░░░░░░▒▒▒██│ 6%" " 39.1K │ ┌─┴sub 2.2 │ ░░░░░░░░░░░░░░░░░█████│ 14%" "750 │ │ ┌──s │ ░░░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒│ 0%" "750 │ │ │ ┌──u │ ░░░░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓│ 0%" "750 │ │ │ ├──t │ ░░░░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓│ 0%" "750 │ │ │ │ ┌──x │ ░░░░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓│ 0%" "750 │ │ │ │ ├──w │ ░░░░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓│ 0%" "750 │ │ │ │ ├──v │ ░░░░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓│ 0%" " 49.6K │ │ │ ├─┴te...│ ░░░░░▓▓▓▓▓▓▓▓▓▓▓██████│ 17%" " 86.8K │ │ │ ├──te...│ ░░░░░▓▓▓▓▓▓▓██████████│ 30%" "141.8K │ │ ├─┴nint...│ ░░░░░█████████████████│ 49%" "146.5K │ ├─┴sub 2.1 │ ░░░░░█████████████████│ 51%" "189.6K ├─┴sub 2 │ ██████████████████████│ 66%" "286.7K ┌─┴root │██████████████████████████████████│100%" }, } test_case! { big_tree_with_long_names_shallow where tree = big_tree_with_long_names::(), bytes_format = BinaryUnits, max_depth = 3, column_width_distribution = total 90, direction = BottomUp, bar_alignment = Right, expected = text_block_fnl! { "999 ┌──first file with a long name│ ░░░░░░░░░░░░░░│ 0%" " 35.5K ├──sub 1.1 │ ░░░░░░░░░█████│ 12%" " 52.6K ├──sub 1.2 │ ░░░░░░████████│ 18%" " 93.1K ┌─┴sub 1 │ ██████████████│ 32%" " 39.1K │ ┌──sub 2.2 │ ░░░░░░░░░░░░░░░░░░░░░░██████│ 14%" "146.5K │ ├──sub 2.1 │ ░░░░░░██████████████████████│ 51%" "189.6K ├─┴sub 2 │ ████████████████████████████│ 66%" "286.7K ┌─┴root │███████████████████████████████████████████│100%" }, }