From dd66ae774ca9a93a2c1bd471e3b1ba2075044f15 Mon Sep 17 00:00:00 2001 From: Clement Tsang <34804052+ClementTsang@users.noreply.github.com> Date: Mon, 15 Jan 2024 04:19:18 -0500 Subject: refactor: simplify some config -> constraints code (#1383) * refactor: simplify some config -> constraints code * iteratively progress... * update bcr; this might need testing since I removed some old proc code * widget side * fix battery * fix widget tests with bandaid for now The issue was that the calculations assume a certain ratio for CPU legends. * add some tests * bump up * fix proc drawing issues So with the proc widget in certain places, there would be a panic during constraint determination. Looks like back when I wrote this I made some gross assumptions about certain things. In particular, the problem here was that the search added an additional "one" height, so that needs to be accounted for after we removed the "doubling" code. * tests * fix tests * reorganize tests * clippy * fix cross tests not working * fix builds for android --- .github/workflows/ci.yml | 2 +- Cargo.lock | 150 ++++++++- Cargo.toml | 3 + src/app/layout_manager.rs | 273 ++++++++-------- src/canvas.rs | 91 +++--- src/options.rs | 16 +- src/options/config/layout.rs | 84 ++--- tests/arg_tests.rs | 187 ----------- tests/empty_config.toml | 1 - tests/integration/arg_tests.rs | 163 +++++++++ tests/integration/invalid_config_tests.rs | 133 ++++++++ tests/integration/layout_management_tests.rs | 472 +++++++++++++++++++++++++++ tests/integration/layout_movement_tests.rs | 9 + tests/integration/main.rs | 11 + tests/integration/util.rs | 144 ++++++++ tests/integration/valid_config_tests.rs | 69 ++++ tests/invalid_config_tests.rs | 153 --------- tests/layout_management_tests.rs | 472 --------------------------- tests/layout_movement_tests.rs | 9 - tests/util.rs | 76 ----- tests/valid_configs/all_proc.toml | 22 ++ tests/valid_configs/cpu_doughnut.toml | 29 ++ tests/valid_configs/empty_config.toml | 1 + tests/valid_configs/many_proc.toml | 22 ++ 24 files changed, 1452 insertions(+), 1140 deletions(-) delete mode 100644 tests/arg_tests.rs delete mode 100644 tests/empty_config.toml create mode 100644 tests/integration/arg_tests.rs create mode 100644 tests/integration/invalid_config_tests.rs create mode 100644 tests/integration/layout_management_tests.rs create mode 100644 tests/integration/layout_movement_tests.rs create mode 100644 tests/integration/main.rs create mode 100644 tests/integration/util.rs create mode 100644 tests/integration/valid_config_tests.rs delete mode 100644 tests/invalid_config_tests.rs delete mode 100644 tests/layout_management_tests.rs delete mode 100644 tests/layout_movement_tests.rs delete mode 100644 tests/util.rs create mode 100644 tests/valid_configs/all_proc.toml create mode 100644 tests/valid_configs/cpu_doughnut.toml create mode 100644 tests/valid_configs/empty_config.toml create mode 100644 tests/valid_configs/many_proc.toml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bff02a44..07d78d56 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -236,7 +236,7 @@ jobs: target: "aarch64-linux-android", cross: true, rust: stable, - cross-version: "git:d6511b7b166c18640f81b8f6a74d9eef380f7ded", + cross-version: "git:cabfc3b02d1edec03869fabdabf6a7f8b0519160", no-default-features: true, } diff --git a/Cargo.lock b/Cargo.lock index 30e79e1a..f7f171f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -176,6 +176,7 @@ dependencies = [ "log", "mach2", "nvml-wrapper", + "portable-pty", "predicates", "ratatui", "regex", @@ -352,7 +353,7 @@ dependencies = [ "autocfg", "cfg-if", "crossbeam-utils", - "memoffset", + "memoffset 0.9.0", ] [[package]] @@ -476,6 +477,12 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" +[[package]] +name = "downcast-rs" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" + [[package]] name = "either" version = "1.9.0" @@ -606,6 +613,15 @@ version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8" +[[package]] +name = "ioctl-rs" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7970510895cee30b3e9128319f2cefd4bde883a39f38baa279567ba3a7eb97d" +dependencies = [ + "libc", +] + [[package]] name = "itertools" version = "0.11.0" @@ -639,6 +655,12 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "lazycell" version = "1.3.0" @@ -718,6 +740,15 @@ version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + [[package]] name = "memoffset" version = "0.9.0" @@ -748,6 +779,20 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "nix" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" +dependencies = [ + "autocfg", + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset 0.6.5", + "pin-utils", +] + [[package]] name = "nix" version = "0.26.4" @@ -876,6 +921,33 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "portable-pty" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "806ee80c2a03dbe1a9fb9534f8d19e4c0546b790cde8fd1fea9d6390644cb0be" +dependencies = [ + "anyhow", + "bitflags 1.3.2", + "downcast-rs", + "filedescriptor", + "lazy_static", + "libc", + "log", + "nix 0.25.1", + "serial", + "shared_library", + "shell-words", + "winapi", + "winreg", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -1111,6 +1183,64 @@ dependencies = [ "serde", ] +[[package]] +name = "serial" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1237a96570fc377c13baa1b88c7589ab66edced652e43ffb17088f003db3e86" +dependencies = [ + "serial-core", + "serial-unix", + "serial-windows", +] + +[[package]] +name = "serial-core" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f46209b345401737ae2125fe5b19a77acce90cd53e1658cda928e4fe9a64581" +dependencies = [ + "libc", +] + +[[package]] +name = "serial-unix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f03fbca4c9d866e24a459cbca71283f545a37f8e3e002ad8c70593871453cab7" +dependencies = [ + "ioctl-rs", + "libc", + "serial-core", + "termios", +] + +[[package]] +name = "serial-windows" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15c6d3b776267a75d31bbdfd5d36c0ca051251caafc285827052bc53bcdc8162" +dependencies = [ + "libc", + "serial-core", +] + +[[package]] +name = "shared_library" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11" +dependencies = [ + "lazy_static", + "libc", +] + +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + [[package]] name = "signal-hook" version = "0.3.17" @@ -1275,6 +1405,15 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "termios" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5d9cf598a6d7ce700a4e6a9199da127e6819a61e64b68609683cc9a01b5683a" +dependencies = [ + "libc", +] + [[package]] name = "termtree" version = "0.4.1" @@ -1616,6 +1755,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + [[package]] name = "wrapcenum-derive" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index 39a0cfce..45805ddc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -135,6 +135,9 @@ cargo-husky = { version = "1.5.0", default-features = false, features = [ ] } predicates = "3.0.3" +[target.'cfg(all(target_arch = "x86_64", target_os = "linux"))'.dev-dependencies] +portable-pty = "0.8.1" + [build-dependencies] clap = { version = "4.4.14", features = ["default", "cargo", "wrap_help"] } clap_complete = "4.4.6" diff --git a/src/app/layout_manager.rs b/src/app/layout_manager.rs index ca353f52..e7904c9a 100644 --- a/src/app/layout_manager.rs +++ b/src/app/layout_manager.rs @@ -66,20 +66,20 @@ impl BottomLayout { col_row_mapping.insert( ( widget_width * 100 / col_row.total_widget_ratio, - (widget_width + widget.width_ratio) * 100 + (widget_width + widget.constraint.ratio()) * 100 / col_row.total_widget_ratio, ), widget.widget_id, ); } } - widget_width += widget.width_ratio; + widget_width += widget.constraint.ratio(); } if is_valid_col_row { col_mapping.insert( ( col_row_height * 100 / col.total_col_row_ratio, - (col_row_height + col_row.col_row_height_ratio) * 100 + (col_row_height + col_row.constraint.ratio()) * 100 / col.total_col_row_ratio, ), (col.total_col_row_ratio, col_row_mapping), @@ -87,31 +87,31 @@ impl BottomLayout { is_valid_col = true; } - col_row_height += col_row.col_row_height_ratio; + col_row_height += col_row.constraint.ratio(); } if is_valid_col { row_mapping.insert( ( row_width * 100 / row.total_col_ratio, - (row_width + col.col_width_ratio) * 100 / row.total_col_ratio, + (row_width + col.constraint.ratio()) * 100 / row.total_col_ratio, ), (row.total_col_ratio, col_mapping), ); is_valid_row = true; } - row_width += col.col_width_ratio; + row_width += col.constraint.ratio(); } if is_valid_row { layout_mapping.insert( ( total_height * 100 / self.total_row_height_ratio, - (total_height + row.row_height_ratio) * 100 / self.total_row_height_ratio, + (total_height + row.constraint.ratio()) * 100 / self.total_row_height_ratio, ), (self.total_row_height_ratio, row_mapping), ); } - total_height += row.row_height_ratio; + total_height += row.constraint.ratio(); } // Now pass through a second time; this time we want to build up @@ -121,20 +121,20 @@ impl BottomLayout { let mut col_cursor = 0; let row_height_percentage_start = height_cursor * 100 / self.total_row_height_ratio; let row_height_percentage_end = - (height_cursor + row.row_height_ratio) * 100 / self.total_row_height_ratio; + (height_cursor + row.constraint.ratio()) * 100 / self.total_row_height_ratio; for col in &mut row.children { let mut col_row_cursor = 0; let col_width_percentage_start = col_cursor * 100 / row.total_col_ratio; let col_width_percentage_end = - (col_cursor + col.col_width_ratio) * 100 / row.total_col_ratio; + (col_cursor + col.constraint.ratio()) * 100 / row.total_col_ratio; for col_row in &mut col.children { let mut widget_cursor = 0; let col_row_height_percentage_start = col_row_cursor * 100 / col.total_col_row_ratio; let col_row_height_percentage_end = - (col_row_cursor + col_row.col_row_height_ratio) * 100 + (col_row_cursor + col_row.constraint.ratio()) * 100 / col.total_col_row_ratio; let col_row_children_len = col_row.children.len(); @@ -147,7 +147,8 @@ impl BottomLayout { let widget_width_percentage_start = widget_cursor * 100 / col_row.total_widget_ratio; let widget_width_percentage_end = - (widget_cursor + widget.width_ratio) * 100 / col_row.total_widget_ratio; + (widget_cursor + widget.constraint.ratio()) * 100 + / col_row.total_widget_ratio; if let Some(current_row) = layout_mapping .get(&(row_height_percentage_start, row_height_percentage_end)) @@ -523,91 +524,85 @@ impl BottomLayout { } } } - widget_cursor += widget.width_ratio; + widget_cursor += widget.constraint.ratio(); } - col_row_cursor += col_row.col_row_height_ratio; + col_row_cursor += col_row.constraint.ratio(); } - col_cursor += col.col_width_ratio; + col_cursor += col.constraint.ratio(); } - height_cursor += row.row_height_ratio; + height_cursor += row.constraint.ratio(); } } pub fn init_basic_default(use_battery: bool) -> Self { let table_widgets = if use_battery { let disk_widget = BottomWidget::new(BottomWidgetType::Disk, 4) - .canvas_handle_width(true) + .canvas_handled() .up_neighbour(Some(100)) .left_neighbour(Some(8)) .right_neighbour(Some(DEFAULT_WIDGET_ID + 2)); let proc_sort = BottomWidget::new(BottomWidgetType::ProcSort, DEFAULT_WIDGET_ID + 2) - .canvas_handle_width(true) + .canvas_handled() .up_neighbour(Some(100)) .down_neighbour(Some(DEFAULT_WIDGET_ID + 1)) .left_neighbour(Some(4)) .right_neighbour(Some(DEFAULT_WIDGET_ID)) - .width_ratio(1) + .ratio(1) .parent_reflector(Some((WidgetDirection::Right, 2))); let proc = BottomWidget::new(BottomWidgetType::Proc, DEFAULT_WIDGET_ID) - .canvas_handle_width(true) + .canvas_handled() .up_neighbour(Some(100)) .down_neighbour(Some(DEFAULT_WIDGET_ID + 1)) .left_neighbour(Some(DEFAULT_WIDGET_ID + 2)) .right_neighbour(Some(7)) - .width_ratio(2); + .ratio(2); let proc_search = BottomWidget::new(BottomWidgetType::ProcSearch, DEFAULT_WIDGET_ID + 1) - .canvas_handle_width(true) + .canvas_handled() .up_neighbour(Some(DEFAULT_WIDGET_ID)) .left_neighbour(Some(4)) .right_neighbour(Some(7)) .parent_reflector(Some((WidgetDirection::Up, 1))); let temp = BottomWidget::new(BottomWidgetType::Temp, 7) - .canvas_handle_width(true) + .canvas_handled() .up_neighbour(Some(100)) .left_neighbour(Some(DEFAULT_WIDGET_ID)) .right_neighbour(Some(8)); let battery = BottomWidget::new(BottomWidgetType::Battery, 8) - .canvas_handle_width(true) + .canvas_handled() .up_neighbour(Some(100)) .left_neighbour(Some(7)) .right_neighbour(Some(4)); vec![ - BottomCol::new(vec![ - BottomColRow::new(vec![disk_widget]).canvas_handle_height(true) - ]) - .canvas_handle_width(true), + BottomCol::new(vec![BottomColRow::new(vec![disk_widget]).canvas_handled()]) + .canvas_handled(), BottomCol::new(vec![ BottomColRow::new(vec![proc_sort, proc]) - .canvas_handle_height(true) + .canvas_handled() .total_widget_ratio(3), - BottomColRow::new(vec![proc_search]).canvas_handle_height(true), - ]) - .canvas_handle_width(true), - BottomCol::new(vec![ - BottomColRow::new(vec![temp]).canvas_handle_height(true) + BottomColRow::new(vec![proc_search]).canvas_handled(), ]) - .canvas_handle_width(true), - BottomCol::new(vec![ - BottomColRow::new(vec![battery]).canvas_handle_height(true) - ]) - .canvas_handle_width(true), + .canvas_handled(), + BottomCol::new(vec![BottomColRow::new(vec![temp]).canvas_handled()]) + .canvas_handled(), + BottomCol::new(vec![BottomColRow::new(vec![battery]).canvas_handled()]) + .canvas_handled(), ] } else { let disk = BottomWidget::new(BottomWidgetType::Disk, 4) - .canvas_handle_width(true) + .canvas_handled() .up_neighbour(Some(100)) .left_neighbour(Some(7)) .right_neighbour(Some(DEFAULT_WIDGET_ID + 2)); let proc_sort = BottomWidget::new(BottomWidgetType::ProcSort, DEFAULT_WIDGET_ID + 2) - .canvas_handle_width(true) + .canvas_handled() .up_neighbour(Some(100)) .down_neighbour(Some(DEFAULT_WIDGET_ID + 1)) .left_neighbour(Some(4)) @@ -615,7 +610,7 @@ impl BottomLayout { .parent_reflector(Some((WidgetDirection::Right, 2))); let proc = BottomWidget::new(BottomWidgetType::Proc, DEFAULT_WIDGET_ID) - .canvas_handle_width(true) + .canvas_handled() .up_neighbour(Some(100)) .down_neighbour(Some(DEFAULT_WIDGET_ID + 1)) .left_neighbour(Some(DEFAULT_WIDGET_ID + 2)) @@ -623,110 +618,111 @@ impl BottomLayout { let proc_search = BottomWidget::new(BottomWidgetType::ProcSearch, DEFAULT_WIDGET_ID + 1) - .canvas_handle_width(true) + .canvas_handled() .up_neighbour(Some(DEFAULT_WIDGET_ID)) .left_neighbour(Some(4)) .right_neighbour(Some(7)) .parent_reflector(Some((WidgetDirection::Up, 1))); let temp = BottomWidget::new(BottomWidgetType::Temp, 7) - .canvas_handle_width(true) + .canvas_handled() .up_neighbour(Some(100)) .left_neighbour(Some(DEFAULT_WIDGET_ID)) .right_neighbour(Some(4)); vec![ + BottomCol::new(vec![BottomColRow::new(vec![disk]).canvas_handled()]) + .canvas_handled(), BottomCol::new(vec![ - BottomColRow::new(vec![disk]).canvas_handle_height(true) - ]) - .canvas_handle_width(true), - BottomCol::new(vec![ - BottomColRow::new(vec![proc_sort, proc]).canvas_handle_height(true), - BottomColRow::new(vec![proc_search]).canvas_handle_height(true), + BottomColRow::new(vec![proc_sort, proc]).canvas_handled(), + BottomColRow::new(vec![proc_search]).canvas_handled(), ]) - .canvas_handle_width(true), - BottomCol::new(vec![ - BottomColRow::new(vec![temp]).canvas_handle_height(true) - ]) - .canvas_handle_width(true), + .canvas_handled(), + BottomCol::new(vec![BottomColRow::new(vec![temp]).canvas_handled()]) + .canvas_handled(), ] }; let cpu = BottomWidget::new(BottomWidgetType::BasicCpu, 1) - .canvas_handle_width(true) + .canvas_handled() .down_neighbour(Some(2)); let mem = BottomWidget::new(BottomWidgetType::BasicMem, 2) - .canvas_handle_width(true) + .canvas_handled() .up_neighbour(Some(1)) .down_neighbour(Some(100)) .right_neighbour(Some(3)); let net = BottomWidget::new(BottomWidgetType::BasicNet, 3) - .canvas_handle_width(true) + .canvas_handled() .up_neighbour(Some(1)) .down_neighbour(Some(100)) .left_neighbour(Some(2)); let table = BottomWidget::new(BottomWidgetType::BasicTables, 100) - .canvas_handle_width(true) + .canvas_handled() .up_neighbour(Some(2)); BottomLayout { total_row_height_ratio: 3, rows: vec![ BottomRow::new(vec![BottomCol::new(vec![ - BottomColRow::new(vec![cpu]).canvas_handle_height(true) + BottomColRow::new(vec![cpu]).canvas_handled() ]) - .canvas_handle_width(true)]) - .canvas_handle_height(true), + .canvas_handled()]) + .canvas_handled(), BottomRow::new(vec![BottomCol::new(vec![BottomColRow::new(vec![ mem, net, ]) - .canvas_handle_height(true)]) - .canvas_handle_width(true)]) - .canvas_handle_height(true), + .canvas_handled()]) + .canvas_handled()]) + .canvas_handled(), BottomRow::new(vec![BottomCol::new(vec![ - BottomColRow::new(vec![table]).canvas_handle_height(true) + BottomColRow::new(vec![table]).canvas_handled() ]) - .canvas_handle_width(true)]) - .canvas_handle_height(true), - BottomRow::new(table_widgets).canvas_handle_height(true), + .canvas_handled()]) + .canvas_handled(), + BottomRow::new(table_widgets).canvas_handled(), ], } } } -// pub enum BottomLayoutNode { -// Container(BottomContainer), -// Widget(BottomWidget), -// } - -// pub struct BottomContainer { -// children: Vec, -// root_ratio: u32, -// growth_type: BottomLayoutNodeSizing, -// } +#[derive(Clone, Debug)] +pub enum IntermediaryConstraint { + PartialRatio(u32), + CanvasHandled { ratio: Option }, + Grow { minimum: Option }, +} -// pub enum BottomContainerType { -// Row, -// Col, -// } +impl Default for IntermediaryConstraint { + fn default() -> Self { + IntermediaryConstraint::PartialRatio(1) + } +} -// pub enum BottomLayoutNodeSizing { -// Ratio(u32), -// CanvasHandles, -// FlexGrow, -// } +impl IntermediaryConstraint { + pub fn ratio(&self) -> u32 { + match self { + IntermediaryConstraint::PartialRatio(val) => *val, + IntermediaryConstraint::Grow { minimum } => match minimum { + Some(val) => *val, + None => 1, + }, + IntermediaryConstraint::CanvasHandled { ratio } => match ratio { + Some(val) => *val, + None => 1, + }, + } + } +} /// Represents a single row in the layout. #[derive(Clone, Debug)] pub struct BottomRow { pub children: Vec, pub total_col_ratio: u32, - pub row_height_ratio: u32, - pub canvas_handle_height: bool, - pub flex_grow: bool, + pub constraint: IntermediaryConstraint, } impl BottomRow { @@ -734,9 +730,7 @@ impl BottomRow { Self { children, total_col_ratio: 1, - row_height_ratio: 1, - canvas_handle_height: false, - flex_grow: false, + constraint: IntermediaryConstraint::default(), } } @@ -745,18 +739,18 @@ impl BottomRow { self } - pub fn row_height_ratio(mut self, row_height_ratio: u32) -> Self { - self.row_height_ratio = row_height_ratio; + pub fn ratio(mut self, row_height_ratio: u32) -> Self { + self.constraint = IntermediaryConstraint::PartialRatio(row_height_ratio); self } - pub fn canvas_handle_height(mut self, canvas_handle_height: bool) -> Self { - self.canvas_handle_height = canvas_handle_height; + pub fn canvas_handled(mut self) -> Self { + self.constraint = IntermediaryConstraint::CanvasHandled { ratio: None }; self } - pub fn flex_grow(mut self, flex_grow: bool) -> Self { - self.flex_grow = flex_grow; + pub fn grow(mut self, minimum: Option) -> Self { + self.constraint = IntermediaryConstraint::Grow { minimum }; self } } @@ -768,9 +762,7 @@ impl BottomRow { pub struct BottomCol { pub children: Vec, pub total_col_row_ratio: u32, - pub col_width_ratio: u32, - pub canvas_handle_width: bool, - pub flex_grow: bool, + pub constraint: IntermediaryConstraint, } impl BottomCol { @@ -778,9 +770,7 @@ impl BottomCol { Self { children, total_col_row_ratio: 1, - col_width_ratio: 1, - canvas_handle_width: false, - flex_grow: false, + constraint: IntermediaryConstraint::default(), } } @@ -789,18 +779,18 @@ impl BottomCol { self } - pub fn col_width_ratio(mut self, col_width_ratio: u32) -> Self { - self.col_width_ratio = col_width_ratio; + pub fn ratio(mut self, col_width_ratio: u32) -> Self { + self.constraint = IntermediaryConstraint::PartialRatio(col_width_ratio); self } - pub fn canvas_handle_width(mut self, canvas_handle_width: bool) -> Self { - self.canvas_handle_width = canvas_handle_width; + pub fn canvas_handled(mut self) -> Self { + self.constraint = IntermediaryConstraint::CanvasHandled { ratio: None }; self } - pub fn flex_grow(mut self, flex_grow: bool) -> Self { - self.flex_grow = flex_grow; + pub fn grow(mut self, minimum: Option) -> Self { + self.constraint = IntermediaryConstraint::Grow { minimum }; self } } @@ -809,9 +799,7 @@ impl BottomCol { pub struct BottomColRow { pub children: Vec, pub total_widget_ratio: u32, - pub col_row_height_ratio: u32, - pub canvas_handle_height: bool, - pub flex_grow: bool, + pub constraint: IntermediaryConstraint, } impl BottomColRow { @@ -819,9 +807,7 @@ impl BottomColRow { Self { children, total_widget_ratio: 1, - col_row_height_ratio: 1, - canvas_handle_height: false, - flex_grow: false, + constraint: IntermediaryConstraint::default(), } } @@ -830,18 +816,18 @@ impl BottomColRow { self } - pub(crate) fn col_row_height_ratio(mut self, col_row_height_ratio: u32) -> Self { - self.col_row_height_ratio = col_row_height_ratio; + pub fn ratio(mut self, col_row_height_ratio: u32) -> Self { + self.constraint = IntermediaryConstraint::PartialRatio(col_row_height_ratio); self } - pub(crate) fn canvas_handle_height(mut self, canvas_handle_height: bool) -> Self { - self.canvas_handle_height = canvas_handle_height; + pub fn canvas_handled(mut self) -> Self { + self.constraint = IntermediaryConstraint::CanvasHandled { ratio: None }; self } - pub(crate) fn flex_grow(mut self, flex_grow: bool) -> Self { - self.flex_grow = flex_grow; + pub fn grow(mut self, minimum: Option) -> Self { + self.constraint = IntermediaryConstraint::Grow { minimum }; self } } @@ -872,18 +858,12 @@ impl WidgetDirection { pub struct BottomWidget { pub widget_type: BottomWidgetType, pub widget_id: u64, - pub width_ratio: u32, + pub constraint: IntermediaryConstraint, pub left_neighbour: Option, pub right_neighbour: Option, pub up_neighbour: Option, pub down_neighbour: Option, - /// If set to true, the canvas will override any ratios. - pub canvas_handle_width: bool, - - /// Whether we want this widget to take up all available room (and ignore any ratios). - pub flex_grow: bool, - /// The value is the direction to bounce, as well as the parent offset. pub parent_reflector: Option<(WidgetDirection, u64)>, @@ -899,24 +879,17 @@ impl BottomWidget { Self { widget_type, widget_id, - width_ratio: 1, + constraint: IntermediaryConstraint::default(), left_neighbour: None, right_neighbour: None, up_neighbour: None, down_neighbour: None, - canvas_handle_width: false, - flex_grow: false, parent_reflector: None, top_left_corner: None, bottom_right_corner: None, } } - pub(crate) fn width_ratio(mut self, width_ratio: u32) -> Self { - self.width_ratio = width_ratio; - self - } - pub(crate) fn left_neighbour(mut self, left_neighbour: Option) -> Self { self.left_neighbour = left_neighbour; self @@ -937,13 +910,23 @@ impl BottomWidget { self } - pub(crate) fn canvas_handle_width(mut self, canvas_handle_width: bool) -> Self { - self.canvas_handle_width = canvas_handle_width; + pub(crate) fn ratio(mut self, width_ratio: u32) -> Self { + self.constraint = IntermediaryConstraint::PartialRatio(width_ratio); self } - pub(crate) fn flex_grow(mut self, flex_grow: bool) -> Self { - self.flex_grow = flex_grow; + pub fn canvas_handled(mut self) -> Self { + self.constraint = IntermediaryConstraint::CanvasHandled { ratio: None }; + self + } + + pub fn canvas_with_ratio(mut self, ratio: u32) -> Self { + self.constraint = IntermediaryConstraint::CanvasHandled { ratio: Some(ratio) }; + self + } + + pub fn grow(mut self, minimum: Option) -> Self { + self.constraint = IntermediaryConstraint::Grow { minimum }; self } @@ -1037,6 +1020,8 @@ Supported widget names: | disk | +--------------------------+ | batt, battery | ++--------------------------+ +| empty | +--------------------------+ ", ))) @@ -1059,6 +1044,8 @@ Supported widget names: | temp, temperature | +--------------------------+ | disk | ++--------------------------+ +| empty | +--------------------------+ ", ))) diff --git a/src/canvas.rs b/src/canvas.rs index 3a02803b..0f3e7202 100644 --- a/src/canvas.rs +++ b/src/canvas.rs @@ -18,7 +18,7 @@ use tui::{ use crate::{ app::{ - layout_manager::{BottomColRow, BottomLayout, BottomWidgetType}, + layout_manager::{BottomColRow, BottomLayout, BottomWidgetType, IntermediaryConstraint}, App, }, constants::*, @@ -80,7 +80,7 @@ pub enum LayoutConstraint { } impl Painter { - pub fn init(widget_layout: BottomLayout, styling: CanvasStyling) -> anyhow::Result { + pub fn init(layout: BottomLayout, styling: CanvasStyling) -> anyhow::Result { // Now for modularity; we have to also initialize the base layouts! // We want to do this ONCE and reuse; after this we can just construct // based on the console size. @@ -90,56 +90,69 @@ impl Painter { let mut col_row_constraints = Vec::new(); let mut layout_constraints = Vec::new(); - widget_layout.rows.iter().for_each(|row| { - if row.canvas_handle_height { - row_constraints.push(LayoutConstraint::CanvasHandled); - } else { - row_constraints.push(LayoutConstraint::Ratio( - row.row_height_ratio, - widget_layout.total_row_height_ratio, - )); + layout.rows.iter().for_each(|row| { + match row.constraint { + IntermediaryConstraint::PartialRatio(val) => { + row_constraints + .push(LayoutConstraint::Ratio(val, layout.total_row_height_ratio)); + } + IntermediaryConstraint::CanvasHandled { .. } => { + row_constraints.push(LayoutConstraint::CanvasHandled); + } + IntermediaryConstraint::Grow { .. } => { + row_constraints.push(LayoutConstraint::Grow); + } } let mut new_col_constraints = Vec::new(); let mut new_widget_constraints = Vec::new(); let mut new_col_row_constraints = Vec::new(); row.children.iter().for_each(|col| { - if col.canvas_handle_width { - new_col_constraints.push(LayoutConstraint::CanvasHandled); - } else { - new_col_constraints.push(LayoutConstraint::Ratio( - col.col_width_ratio, - row.total_col_ratio, - )); + match col.constraint { + IntermediaryConstraint::PartialRatio(val) => { + new_col_constraints.push(LayoutConstraint::Ratio(val, row.total_col_ratio)); + } + IntermediaryConstraint::CanvasHandled { .. } => { + new_col_constraints.push(LayoutConstraint::CanvasHandled); + } + IntermediaryConstraint::Grow { .. } => { + new_col_constraints.push(LayoutConstraint::Grow); + } } let mut new_new_col_row_constraints = Vec::new(); let mut new_new_widget_constraints = Vec::new(); col.children.iter().for_each(|col_row| { - if col_row.canvas_handle_height { - new_new_col_row_constraints.push(LayoutConstraint::CanvasHandled); - } else if col_row.flex_grow { - new_new_col_row_constraints.push(LayoutConstraint::Grow); - } else { - new_new_col_row_constraints.push(LayoutConstraint::Ratio( - col_row.col_row_height_ratio, - col.total_col_row_ratio, - )); + match col_row.constraint { + IntermediaryConstraint::PartialRatio(val) => { + new_new_col_row_constraints + .push(LayoutConstraint::Ratio(val, col.total_col_row_ratio)); + } + IntermediaryConstraint::CanvasHandled { .. } => { + new_new_col_row_constraints.push(LayoutConstraint::CanvasHandled); + } + IntermediaryConstraint::Grow { .. } => { + new_new_col_row_constraints.push(LayoutConstraint::Grow); + } } let mut new_new_new_widget_constraints = Vec::new(); - col_row.children.iter().for_each(|widget| { - if widget.canvas_handle_width { - new_new_new_widget_constraints.push(LayoutConstraint::CanvasHandled); - } else if widget.flex_grow { - new_new_new_widget_constraints.push(LayoutConstraint::Grow); - } else { - new_new_new_widget_constraints.push(LayoutConstraint::Ratio( - widget.width_ratio, - col_row.total_widget_ratio, - )); - } - }); + col_row + .children + .iter() + .for_each(|widget| match widget.constraint { + IntermediaryConstraint::PartialRatio(val) => { + new_new_new_widget_constraints + .push(LayoutConstraint::Ratio(val, col_row.total_widget_ratio)); + } + IntermediaryConstraint::CanvasHandled { .. } => { + new_new_new_widget_constraints + .push(LayoutConstraint::CanvasHandled); + } + IntermediaryConstraint::Grow { .. } => { + new_new_new_widget_constraints.push(LayoutConstraint::Grow); + } + }); new_new_widget_constraints.push(new_new_new_widget_constraints); }); new_col_row_constraints.push(new_new_col_row_constraints); @@ -158,7 +171,7 @@ impl Painter { col_constraints, col_row_constraints, layout_constraints, - widget_layout, + widget_layout: layout, derived_widget_draw_locs: Vec::default(), }; diff --git a/src/options.rs b/src/options.rs index 86d44e75..95c780f6 100644 --- a/src/options.rs +++ b/src/options.rs @@ -747,14 +747,6 @@ fn get_default_widget_and_count( fn get_use_battery(matches: &ArgMatches, config: &Config) -> bool { #[cfg(feature = "battery")] { - if matches.get_flag("battery") { - return true; - } else if let Some(flags) = &config.flags { - if let Some(battery) = flags.battery { - return battery; - } - } - if let Ok(battery_manager) = Manager::new() { if let Ok(batteries) = battery_manager.batteries() { if batteries.count() == 0 { @@ -762,6 +754,14 @@ fn get_use_battery(matches: &ArgMatches, config: &Config) -> bool { } } } + + if matches.get_flag("battery") { + return true; + } else if let Some(flags) = &config.flags { + if let Some(battery) = flags.battery { + return battery; + } + } } false diff --git a/src/options/config/layout.rs b/src/options/config/layout.rs index 4b3e587a..45c2a0ab 100644 --- a/src/options/config/layout.rs +++ b/src/options/config/layout.rs @@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize}; use crate::{app::layout_manager::*, error::Result}; -/// Represents a row. This has a length of some sort (optional) and a vector +/// Represents a row. This has a length of some sort (optional) and a vector /// of children. #[derive(Clone, Deserialize, Debug, Serialize)] #[serde(rename = "row")] @@ -19,21 +19,15 @@ fn new_cpu(left_legend: bool, iter_id: &mut u64) -> BottomColRow { if left_legend { BottomColRow::new(vec![ BottomWidget::new(BottomWidgetType::CpuLegend, legend_id) - .width_ratio(3) - .canvas_handle_width(true) + .canvas_with_ratio(3) .parent_reflector(Some((WidgetDirection::Right, 1))), - BottomWidget::new(BottomWidgetType::Cpu, cpu_id) - .width_ratio(17) - .flex_grow(true), + BottomWidget::new(BottomWidgetType::Cpu, cpu_id).grow(Some(17)), ]) } else { BottomColRow::new(vec![ - BottomWidget::new(BottomWidgetType::Cpu, cpu_id) - .width_ratio(17) - .flex_grow(true), + BottomWidget::new(BottomWidgetType::Cpu, cpu_id).grow(Some(17)), BottomWidget::new(BottomWidgetType::CpuLegend, legend_id) - .width_ratio(3) - .canvas_handle_width(true) + .canvas_with_ratio(3) .parent_reflector(Some((WidgetDirection::Left, 1))), ]) } @@ -42,13 +36,12 @@ fn new_cpu(left_legend: bool, iter_id: &mut u64) -> BottomColRow { fn new_proc_sort(sort_id: u64) -> BottomWidget { BottomWidget::new(BottomWidgetType::ProcSort, sort_id) - .canvas_handle_width(true) + .canvas_handled() .parent_reflector(Some((WidgetDirection::Right, 2))) - .width_ratio(1) } fn new_proc(proc_id: u64) -> BottomWidget { - BottomWidget::new(BottomWidgetType::Proc, proc_id).width_ratio(2) + BottomWidget::new(BottomWidgetType::Proc, proc_id).ratio(2) } fn new_proc_search(search_id: u64) -> BottomWidget { @@ -99,7 +92,7 @@ impl Row { children.push(match widget_type { BottomWidgetType::Cpu => { BottomCol::new(vec![new_cpu(left_legend, iter_id)]) - .col_width_ratio(width_ratio) + .ratio(width_ratio) } BottomWidgetType::Proc => { let proc_id = *iter_id; @@ -110,34 +103,31 @@ impl Row { new_proc_sort(*iter_id), new_proc(proc_id), ]) - .total_widget_ratio(3) - .flex_grow(true), + .grow(None) + .total_widget_ratio(3), BottomColRow::new(vec![new_proc_search(proc_search_id)]) - .canvas_handle_height(true), + .canvas_handled(), ]) .total_col_row_ratio(2) - .col_width_ratio(width_ratio) + .ratio(width_ratio) } _ => BottomCol::new(vec![BottomColRow::new(vec![BottomWidget::new( widget_type, *iter_id, )])]) - .col_width_ratio(width_ratio), + .ratio(width_ratio), }); } RowChildren::Col { ratio, child } => { let col_width_ratio = ratio.unwrap_or(1); total_col_ratio += col_width_ratio; let mut total_col_row_ratio = 0; - let mut contains_proc = false; let mut col_row_children: Vec = Vec::new(); for widget in child { let widget_type = widget.widget_type.parse::()?; *iter_id += 1; - let col_row_height_ratio = widget.ratio.unwrap_or(1); - total_col_row_ratio += col_row_height_ratio; if let Some(default_widget_type_val) = default_widget_type { if *default_widget_type_val == widget_type @@ -159,13 +149,17 @@ impl Row { match widget_type { BottomWidgetType::Cpu => { + let col_row_height_ratio = widget.ratio.unwrap_or(1); + total_col_row_ratio += col_row_height_ratio; + col_row_children.push( - new_cpu(left_legend, iter_id) - .col_row_height_ratio(col_row_height_ratio), + new_cpu(left_legend, iter_id).ratio(col_row_height_ratio), ); } BottomWidgetType::Proc => { - contains_proc = true; + let col_row_height_ratio = widget.ratio.unwrap_or(1) + 1; + total_col_row_ratio += col_row_height_ratio; + let proc_id = *iter_id; let proc_search_id = *iter_id + 1; *iter_id += 2; @@ -174,35 +168,25 @@ impl Row { new_proc_sort(*iter_id), new_proc(proc_id), ]) - .col_row_height_ratio(col_row_height_ratio) + .ratio(col_row_height_ratio) .total_widget_ratio(3), ); col_row_children.push( BottomColRow::new(vec![new_proc_search(proc_search_id)]) - .canvas_handle_height(true) - .col_row_height_ratio(col_row_height_ratio), + .canvas_handled(), ); } - _ => col_row_children.push( - BottomColRow::new(vec![BottomWidget::new( - widget_type, - *iter_id, - )]) - .col_row_height_ratio(col_row_height_ratio), - ), - } - } + _ => { + let col_row_height_ratio = widget.ratio.unwrap_or(1); + total_col_row_ratio += col_row_height_ratio; - if contains_proc { - // Must adjust ratios to work with proc - total_col_row_ratio *= 2; - for child in &mut col_row_children { - // Multiply all non-proc or proc-search ratios by 2 - if !child.children.is_empty() { - match child.children[0].widget_type { - BottomWidgetType::ProcSearch => {} - _ => child.col_row_height_ratio *= 2, - } + col_row_children.push( + BottomColRow::new(vec![BottomWidget::new( + widget_type, + *iter_id, + )]) + .ratio(col_row_height_ratio), + ) } } } @@ -210,7 +194,7 @@ impl Row { children.push( BottomCol::new(col_row_children) .total_col_row_ratio(total_col_row_ratio) - .col_width_ratio(col_width_ratio), + .ratio(col_width_ratio), ); } } @@ -219,7 +203,7 @@ impl Row { Ok(BottomRow::new(children) .total_col_ratio(total_col_ratio) - .row_height_ratio(row_ratio)) + .ratio(row_ratio)) } } diff --git a/tests/arg_tests.rs b/tests/arg_tests.rs deleted file mode 100644 index af4df7b8..00000000 --- a/tests/arg_tests.rs +++ /dev/null @@ -1,187 +0,0 @@ -//! These tests are mostly here just to ensure that invalid results will be caught when passing arguments. -mod util; - -use assert_cmd::prelude::*; -use predicates::prelude::*; -use util::*; - -#[test] -fn test_small_rate() { - btm_command() - .arg("-C") - .arg("./tests/empty_config.toml") - .arg("-r") - .arg("249") - .assert() - .failure() - .stderr(predicate::str::contains( - "set your update rate to be at least 250 ms.", - )); -} - -#[test] -fn test_large_default_time() { - btm_command() - .arg("-C") - .arg("./tests/empty_config.toml") - .arg("-t") - .arg("18446744073709551616") - .assert() - .failure() - .stderr(predicate::str::contains("could not parse")); -} - -#[test] -fn test_small_default_time() { - btm_command() - .arg("-C") - .arg("./tests/empty_config.toml") - .arg("-t") - .arg("900") - .assert() - .failure() - .stderr(predicate::str::contains( - "set your default value to be at least", - )); -} - -#[test] -fn test_large_delta_time() { - btm_command() - .arg("-C") - .arg("./tests/empty_config.toml") - .arg("-d") - .arg("18446744073709551616") - .assert() - .failure() - .stderr(predicate::str::contains("could not parse")); -} - -#[test] -fn test_small_delta_time() { - btm_command() - .arg("-C") - .arg("./tests/empty_config.toml") - .arg("-d") - .arg("900") - .assert() - .failure() - .stderr(predicate::str::contains( - "set your time delta to be at least", - )); -} - -#[test] -fn test_large_rate() { - btm_command() - .arg("-C") - .arg("./tests/empty_config.toml") - .arg("-r") - .arg("18446744073709551616") - .assert() - .failure() - .stderr(predicate::str::contains("could not parse")); -} - -#[test] -fn test_negative_rate() { - // This test should auto fail due to how clap works - btm_command() - .arg("-C") - .arg("./tests/empty_config.toml") - .arg("-r") - .arg("-1000") - .assert() - .failure() - .stderr(predicate::str::contains("unexpected argument")); -} - -#[test] -fn test_invalid_rate() { - btm_command() - .arg("-C") - .arg("./tests/empty_config.toml") - .arg("-r") - .arg("100-1000") - .assert() - .failure() - .stderr(predicate::str::contains("could not parse")); -} - -#[test] -fn test_conflicting_temps() { - btm_command() - .arg("-C") - .arg("./tests/empty_config.toml") - .arg("-c") - .arg("-f") - .assert() - .failure() - .stderr(predicate::str::contains("cannot be used with")); -} - -#[test] -fn test_invalid_default_widget_1() { - btm_command() - .arg("-C") - .arg("./tests/empty_config.toml") - .arg("--default_widget_type") - .arg("fake_widget") - .assert() - .failure() - .stderr(predicate::str::contains("invalid widget name")); -} - -#[test] -fn test_invalid_default_widget_2() { - btm_command() - .arg("-C") - .arg("./tests/empty_config.toml") - .arg("--default_widget_type") - .arg("cpu") - .arg("--default_widget_count") - .arg("18446744073709551616") - .assert() - .failure() - .stderr(predicate::str::contains( - "set your widget count to be at most unsigned INT_MAX", - )); -} - -#[test] -fn test_missing_default_widget_type() { - btm_command() - .arg("-C") - .arg("./tests/empty_config.toml") - .arg("--default_widget_count") - .arg("3") - .assert() - .failure() - .stderr(predicate::str::contains( - "the following required arguments were not provided", - )); -} - -#[test] -#[cfg_attr(feature = "battery", ignore)] -fn test_battery_flag() { - btm_command() - .arg("--battery") - .assert() - .failure() - .stderr(predicate::str::contains( - "unexpected argument '--battery' found", - )); -} - -#[test] -#[cfg_attr(feature = "gpu", ignore)] -fn test_gpu_flag() { - btm_command() - .arg("--enable_gpu") - .assert() - .failure() - .stderr(predicate::str::contains( - "unexpected argument '--enable_gpu' found", - )); -} diff --git a/tests/empty_config.toml b/tests/empty_config.toml deleted file mode 100644 index b2c0f98b..00000000 --- a/tests/empty_config.toml +++ /dev/null @@ -1 +0,0 @@ -# This config file is "empty" so running tests does not modify a user's home directory. diff --git a/tests/integration/arg_tests.rs b/tests/integration/arg_tests.rs new file mode 100644 index 00000000..13ebc5f0 --- /dev/null +++ b/tests/integration/arg_tests.rs @@ -0,0 +1,163 @@ +//! These tests are mostly here just to ensure that invalid results will be caught when passing arguments. + +use assert_cmd::prelude::*; +use predicates::prelude::*; + +use crate::util::{btm_command, no_cfg_btm_command}; + +#[test] +fn test_small_rate() { + btm_command(&["-C", "./tests/valid_configs/empty_config.toml"]) + .arg("-r") + .arg("249") + .assert() + .failure() + .stderr(predicate::str::contains( + "set your update rate to be at least 250 ms.", + )); +} + +#[test] +fn test_large_default_time() { + no_cfg_btm_command() + .arg("-t") + .arg("18446744073709551616") + .assert() + .failure() + .stderr(predicate::str::contains("could not parse")); +} + +#[test] +fn test_small_default_time() { + no_cfg_btm_command() + .arg("-t") + .arg("900") + .assert() + .failure() + .stderr(predicate::str::contains( + "set your default value to be at least", + )); +} + +#[test] +fn test_large_delta_time() { + no_cfg_btm_command() + .arg("-d") + .arg("18446744073709551616") + .assert() + .failure() + .stderr(predicate::str::contains("could not parse")); +} + +#[test] +fn test_small_delta_time() { + no_cfg_btm_command() + .arg("-d") + .arg("900") + .assert() + .failure() + .stderr(predicate::str::contains( + "set your time delta to be at least", + )); +} + +#[test] +fn test_large_rate() { + no_cfg_btm_command() + .arg("-r") + .arg("18446744073709551616") + .assert() + .failure() + .stderr(predicate::str::contains("could not parse")); +} + +#[test] +fn test_negative_rate() { + // This test should auto fail due to how clap works + no_cfg_btm_command() + .arg("-r") + .arg("-1000") + .assert() + .failure() + .stderr(predicate::str::contains("unexpected argument")); +} + +#[test] +fn test_invalid_rate() { + no_cfg_btm_command() + .arg("-r") + .arg("100-1000") + .assert() + .failure() + .stderr(predicate::str::contains("could not parse")); +} + +#[test] +fn test_conflicting_temps() { + no_cfg_btm_command() + .arg("-c") + .arg("-f") + .assert() + .failure() + .stderr(predicate::str::contains("cannot be used with")); +} + +#[test] +fn test_invalid_default_widget_1() { + no_cfg_btm_command() + .arg("--default_widget_type") + .arg("fake_widget") + .assert() + .failure() + .stderr(predicate::str::contains("invalid widget name")); +} + +#[test] +fn test_invalid_default_widget_2() { + no_cfg_btm_command() + .arg("--default_widget_type") + .arg("cpu") + .arg("--default_widget_count") + .arg("18446744073709551616") + .assert() + .failure() + .stderr(predicate::str::contains( + "set your widget count to be at most unsigned INT_MAX", + )); +} + +#[test] +fn test_missing_default_widget_type() { + no_cfg_btm_command() + .arg("--default_widget_count") + .arg("3") + .assert() + .failure() + .stderr(predicate::str::contains( + "the following required arguments were not provided", + )); +} + +#[test] +#[cfg_attr(feature = "battery", ignore)] +fn test_battery_flag() { + no_cfg_btm_command() + .arg("--battery") + .assert() + .failure() + .stderr(predicate::str::contains( + "unexpected argument '--battery' found", + )); +} + +#[test] +#[cfg_attr(feature = "gpu", ignore)] +fn test_gpu_flag() { + no_cfg_btm_command() + .arg("--enable_gpu") + .assert() + .failure() + .stderr(predicate::str::contains( + "unexpected argument '--enable_gpu' found", + )); +} diff --git a/tests/integration/invalid_config_tests.rs b/tests/integration/invalid_config_tests.rs new file mode 100644 index 00000000..92a67eac --- /dev/null +++ b/tests/integration/invalid_config_tests.rs @@ -0,0 +1,133 @@ +//! These tests are for testing some invalid config-file-specific options. + +use assert_cmd::prelude::*; +use predicates::prelude::*; + +use crate::util::btm_command; + +#[test] +fn test_toml_mismatch_type() { + btm_command(&["-C", "./tests/invalid_configs/toml_mismatch_type.toml"]) + .assert() + .failure() + .stderr(predicate::str::contains("invalid type")); +} + +#[test] +fn test_empty_layout() { + btm_command(&["-C", "./tests/invalid_configs/empty_layout.toml"]) + .assert() + .failure() + .stderr(predicate::str::contains("at least one widget")); +} + +#[test] +fn test_invalid_layout_widget_type() { + btm_command(&[ + "-C", + "./tests/invalid_configs/invalid_layout_widget_type.toml", + ]) + .assert() + .failure() + .stderr(predicate::str::contains("invalid widget name")); +} + +/// This test isn't really needed as this is technically covered by TOML spec. +/// However, I feel like it's worth checking anyways - not like it takes long. +#[test] +fn test_duplicate_temp_type() { + btm_command(&["-C", "./tests/invalid_configs/duplicate_temp_type.toml"]) + .assert() + .failure() + .stderr(predicate::str::contains("duplicate key")); +} + +/// Checks for if a hex is valid +#[test] +fn test_invalid_colour_hex() { + btm_command(&["-C", "./tests/invalid_configs/invalid_colour_hex.toml"]) + .assert() + .failure() + .stderr(predicate::str::contains("invalid hex color")); +} + +/// Checks for if a hex is too long +#[test] +fn test_invalid_colour_hex_2() { + btm_command(&["-C", "./tests/invalid_configs/invalid_colour_hex_2.toml"]) + .assert() + .failure() + .stderr(predicate::str::contains("invalid hex color")); +} + +/// Checks unicode hex because the way we originally did it could cause char +/// boundary errors! +#[test] +fn test_invalid_colour_hex_3() { + btm_command(&["-C", "./tests/invalid_configs/invalid_colour_hex_3.toml"]) + .assert() + .failure() + .stderr(predicate::str::contains("invalid hex color")); +} + +#[test] +fn test_invalid_colour_name() { + btm_command(&["-C", "./tests/invalid_configs/invalid_colour_name.toml"]) + .assert() + .failure() + .stderr(predicate::str::contains("invalid named color")); +} + +#[test] +fn test_invalid_colour_rgb() { + btm_command(&["-C", "./tests/invalid_configs/invalid_colour_rgb.toml"]) + .assert() + .failure() + .stderr(predicate::str::contains("invalid RGB")); +} + +#[test] +fn test_invalid_colour_rgb_2() { + btm_command(&["-C", "./tests/invalid_configs/invalid_colour_rgb_2.toml"]) + .assert() + .failure() + .stderr(predicate::str::contains("invalid RGB")); +} + +#[test] +fn test_invalid_colour_string() { + btm_command(&["-C", "./tests/invalid_configs/invalid_colour_string.toml"]) + .assert() + .failure() + .stderr(predicate::str::contains("invalid named color")); +} + +#[test] +fn test_lone_default_widget_count() { + btm_command(&[ + "-C", + "./tests/invalid_configs/lone_default_widget_count.toml", + ]) + .assert() + .failure() + .stderr(predicate::str::contains("it must be used with")); +} + +#[test] +fn test_invalid_default_widget_count() { + btm_command(&[ + "-C", + "./tests/invalid_configs/invalid_default_widget_count.toml", + ]) + .assert() + .failure() + .stderr(predicate::str::contains("number too large")); +} + +#[test] +fn test_invalid_process_column() { + btm_command(&["-C", "./tests/invalid_configs/invalid_process_column.toml"]) + .assert() + .failure() + .stderr(predicate::str::contains("doesn't match")); +} diff --git a/tests/integration/layout_management_tests.rs b/tests/integration/layout_management_tests.rs new file mode 100644 index 00000000..711e7f02 --- /dev/null +++ b/tests/integration/layout_management_tests.rs @@ -0,0 +1,472 @@ +//! Mocks layout management, so we can check if we broke anything. + +#[cfg(feature = "battery")] +use bottom::constants::DEFAULT_BATTERY_LAYOUT; +use bottom::{ + app::layout_manager::{BottomLayout, BottomWidgetType}, + constants::{DEFAULT_LAYOUT, DEFAULT_WIDGET_ID}, + options::{config::layout::Row, Config}, + utils::error, +}; +use toml_edit::de::from_str; + +// TODO: Could move these into the library files rather than external tbh. + +const PROC_LAYOUT: &str = r#" +[[row]] + [[row.child]] + type="proc" +[[row]] + [[row.child]] + type="proc" + [[row.child]] + type="proc" +[[row]] + [[row.child]] + type="proc" + [[row.child]] + type="proc" +"#; + +fn test_create_layout( + rows: &[Row], default_widget_id: u64, default_widget_type: Option, + default_