use super::super::helpers::format_uptime;
#[must_use]
pub fn build_cpu_title(
cpu_pct: f64,
core_count: usize,
freq_ghz: f64,
is_boosting: bool,
uptime_secs: u64,
load_one: f64,
deterministic: bool,
) -> String {
let boost_icon = if is_boosting { "⚡" } else { "" };
if deterministic {
format!(
"CPU {cpu_pct:.0}% │ {core_count} cores │ {freq_ghz:.1}GHz │ up {} │",
format_uptime(uptime_secs)
)
} else {
format!(
"CPU {cpu_pct:.0}% │ {core_count} cores │ {freq_ghz:.1}GHz{boost_icon} │ up {} │ LAV {load_one:.1}",
format_uptime(uptime_secs)
)
}
}
#[must_use]
pub fn build_cpu_title_compact(
cpu_pct: f64,
core_count: usize,
freq_ghz: f64,
is_boosting: bool,
) -> String {
let boost_icon = if is_boosting { "⚡" } else { "" };
format!("CPU {cpu_pct:.0}% │ {core_count}c │ {freq_ghz:.1}GHz{boost_icon}")
}
#[derive(Debug, Clone, PartialEq)]
pub struct CpuMeterLayout {
pub bar_len: usize,
pub meter_bar_width: f32,
pub cores_per_col: usize,
pub num_meter_cols: usize,
}
impl CpuMeterLayout {
#[must_use]
pub fn calculate(core_count: usize, core_area_height: f32, is_exploded: bool) -> Self {
let bar_len: usize = if is_exploded { 8 } else { 6 };
let meter_bar_width = (bar_len + 9) as f32;
let max_cores_per_col = if is_exploded {
(core_area_height as usize).min(12)
} else {
core_area_height as usize
};
let cores_per_col = max_cores_per_col.max(1);
let num_meter_cols = core_count.div_ceil(cores_per_col);
Self {
bar_len,
meter_bar_width,
cores_per_col,
num_meter_cols,
}
}
}
use presentar_core::Color;
#[must_use]
pub fn load_color(load_normalized: f64) -> Color {
if load_normalized > 1.0 {
Color::new(1.0, 0.3, 0.3, 1.0) } else if load_normalized > 0.7 {
Color::new(1.0, 0.8, 0.2, 1.0) } else {
Color::new(0.3, 0.9, 0.3, 1.0) }
}
#[must_use]
pub fn load_trend_arrow(current: f64, previous: f64) -> &'static str {
let delta = current - previous;
if delta >= 0.5 {
"↑"
} else if delta <= -0.5 {
"↓"
} else {
""
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_build_cpu_title_deterministic() {
let title = build_cpu_title(45.0, 8, 3.6, false, 3600, 2.1, true);
assert!(title.contains("CPU 45%"));
assert!(title.contains("8 cores"));
assert!(title.contains("3.6GHz"));
assert!(title.contains("up 1h"));
assert!(!title.contains("LAV"), "Deterministic should not show LAV");
}
#[test]
fn test_build_cpu_title_normal() {
let title = build_cpu_title(75.0, 16, 4.8, true, 86400, 5.5, false);
assert!(title.contains("CPU 75%"));
assert!(title.contains("16 cores"));
assert!(title.contains("4.8GHz"));
assert!(title.contains("⚡"), "Boosting should show lightning");
assert!(title.contains("LAV 5.5"));
}
#[test]
fn test_build_cpu_title_no_boost() {
let title = build_cpu_title(50.0, 4, 2.4, false, 0, 1.0, false);
assert!(
!title.contains("⚡"),
"Not boosting should not show lightning"
);
}
#[test]
fn test_build_cpu_title_zero_values() {
let title = build_cpu_title(0.0, 1, 0.0, false, 0, 0.0, true);
assert!(title.contains("CPU 0%"));
assert!(title.contains("1 cores"));
assert!(title.contains("0.0GHz"));
}
#[test]
fn test_build_cpu_title_high_cpu() {
let title = build_cpu_title(100.0, 64, 5.5, true, 604800, 32.0, false);
assert!(title.contains("CPU 100%"));
assert!(title.contains("64 cores"));
assert!(title.contains("7d")); }
#[test]
fn test_build_cpu_title_compact_basic() {
let title = build_cpu_title_compact(45.0, 8, 3.6, false);
assert!(title.contains("CPU 45%"));
assert!(title.contains("8c"));
assert!(title.contains("3.6GHz"));
}
#[test]
fn test_build_cpu_title_compact_boosting() {
let title = build_cpu_title_compact(99.0, 48, 4.8, true);
assert!(title.contains("⚡"));
assert!(title.contains("48c"));
}
#[test]
fn test_build_cpu_title_compact_length() {
let title = build_cpu_title_compact(45.0, 8, 3.6, false);
assert!(title.chars().count() < 30, "Compact title should be short");
}
#[test]
fn test_build_cpu_title_compact_no_uptime() {
let title = build_cpu_title_compact(45.0, 8, 3.6, false);
assert!(!title.contains("up"), "Compact should not have uptime");
assert!(!title.contains("LAV"), "Compact should not have LAV");
}
#[test]
fn test_cpu_meter_layout_normal() {
let layout = CpuMeterLayout::calculate(8, 10.0, false);
assert_eq!(layout.bar_len, 6, "Normal mode bar length");
assert_eq!(layout.meter_bar_width, 15.0); }
#[test]
fn test_cpu_meter_layout_exploded() {
let layout = CpuMeterLayout::calculate(8, 10.0, true);
assert_eq!(layout.bar_len, 8, "Exploded mode bar length");
assert_eq!(layout.meter_bar_width, 17.0); }
#[test]
fn test_cpu_meter_layout_single_column() {
let layout = CpuMeterLayout::calculate(4, 10.0, false);
assert_eq!(layout.cores_per_col, 10);
assert_eq!(layout.num_meter_cols, 1, "4 cores in 10 rows = 1 column");
}
#[test]
fn test_cpu_meter_layout_multi_column() {
let layout = CpuMeterLayout::calculate(16, 4.0, false);
assert_eq!(layout.cores_per_col, 4);
assert_eq!(layout.num_meter_cols, 4, "16 cores / 4 per col = 4 columns");
}
#[test]
fn test_cpu_meter_layout_exploded_max_12_per_col() {
let layout = CpuMeterLayout::calculate(48, 20.0, true);
assert_eq!(layout.cores_per_col, 12, "Exploded caps at 12 per column");
assert_eq!(layout.num_meter_cols, 4);
}
#[test]
fn test_cpu_meter_layout_tiny_height() {
let layout = CpuMeterLayout::calculate(8, 0.5, false);
assert_eq!(layout.cores_per_col, 1, "Minimum 1 core per column");
assert_eq!(layout.num_meter_cols, 8);
}
#[test]
fn test_cpu_meter_layout_single_core() {
let layout = CpuMeterLayout::calculate(1, 10.0, false);
assert_eq!(layout.num_meter_cols, 1);
assert_eq!(layout.cores_per_col, 10);
}
#[test]
fn test_cpu_meter_layout_many_cores() {
let layout = CpuMeterLayout::calculate(128, 8.0, false);
assert_eq!(layout.cores_per_col, 8);
assert_eq!(layout.num_meter_cols, 16);
}
#[test]
fn test_cpu_meter_layout_derive_debug() {
let layout = CpuMeterLayout::calculate(8, 10.0, false);
let debug = format!("{:?}", layout);
assert!(debug.contains("CpuMeterLayout"));
}
#[test]
fn test_cpu_meter_layout_derive_clone() {
let layout = CpuMeterLayout::calculate(8, 10.0, false);
let cloned = layout.clone();
assert_eq!(layout, cloned);
}
#[test]
fn test_load_color_low() {
let color = load_color(0.3);
assert!(color.g > 0.8, "Low load should be green");
assert!(color.r < 0.5);
}
#[test]
fn test_load_color_medium() {
let color = load_color(0.8);
assert!(color.r > 0.9, "Medium load should be yellow");
assert!(color.g > 0.7);
}
#[test]
fn test_load_color_high() {
let color = load_color(1.5);
assert!(color.r > 0.9, "High load should be red");
assert!(color.g < 0.5);
}
#[test]
fn test_load_color_boundary_07() {
let below = load_color(0.69);
let above = load_color(0.71);
assert!(
below.g > above.g,
"0.7 is boundary between green and yellow"
);
}
#[test]
fn test_load_color_boundary_10() {
let below = load_color(0.99);
let above = load_color(1.01);
assert!(below.g > above.g, "1.0 is boundary between yellow and red");
}
#[test]
fn test_load_trend_arrow_increasing() {
assert_eq!(load_trend_arrow(2.0, 1.0), "↑");
assert_eq!(load_trend_arrow(5.0, 4.0), "↑");
}
#[test]
fn test_load_trend_arrow_decreasing() {
assert_eq!(load_trend_arrow(1.0, 2.0), "↓");
assert_eq!(load_trend_arrow(3.0, 4.0), "↓");
}
#[test]
fn test_load_trend_arrow_stable() {
assert_eq!(load_trend_arrow(1.0, 1.0), "");
assert_eq!(load_trend_arrow(1.2, 1.0), "");
assert_eq!(load_trend_arrow(0.8, 1.0), "");
}
#[test]
fn test_load_trend_arrow_threshold() {
assert_eq!(load_trend_arrow(1.5, 1.0), "↑"); assert_eq!(load_trend_arrow(1.0, 1.5), "↓"); }
#[test]
fn test_load_trend_arrow_just_below_threshold() {
assert_eq!(load_trend_arrow(1.49, 1.0), ""); assert_eq!(load_trend_arrow(1.0, 1.49), ""); }
}