#![allow(non_snake_case)]
#![allow(dead_code)]
#![allow(clippy::needless_range_loop)]
use std::io::Write;
use crate::ported::crt::{ColorElements, ColorScheme};
use crate::ported::functionbar::Ncurses;
use crate::ported::machine::Machine;
use crate::ported::meter::{
BlankMeter_class, Meter, MeterClass, Meter_new, Meter_setMode, TEXT_METERMODE,
};
use crate::ported::settings::{
HeaderLayout, HeaderLayout_getColumns, HeaderLayout_layouts, MeterModeId, Settings,
Settings_setHeaderLayout,
};
#[cfg(target_os = "macos")]
use crate::ported::darwin::platform::Platform_meterTypes;
#[cfg(not(target_os = "macos"))]
use crate::ported::linux::platform::Platform_meterTypes;
pub struct Header {
pub host: *const Machine,
pub columns: Vec<Vec<Meter>>,
pub headerLayout: HeaderLayout,
pub pad: i32,
pub height: i32,
pub headerMargin: bool,
pub screenTabs: bool,
}
pub fn Header_new(host: *const Machine, hLayout: HeaderLayout) -> Header {
let ncol = HeaderLayout_getColumns(hLayout);
let mut columns = Vec::with_capacity(ncol);
for _ in 0..ncol {
columns.push(Vec::new());
}
Header {
host,
columns,
headerLayout: hLayout,
pad: 0,
height: 0,
headerMargin: false,
screenTabs: false,
}
}
pub fn Header_delete(this: Header) {
let _ = this;
}
pub fn Header_setLayout(this: &mut Header, hLayout: HeaderLayout) {
let oldColumns = HeaderLayout_getColumns(this.headerLayout);
let newColumns = HeaderLayout_getColumns(hLayout);
this.headerLayout = hLayout;
if newColumns == oldColumns {
return;
}
if newColumns > oldColumns {
for _ in oldColumns..newColumns {
this.columns.push(Vec::new());
}
} else {
for i in newColumns..oldColumns {
let mut removed = std::mem::take(&mut this.columns[i]);
while let Some(meter) = removed.pop() {
this.columns[newColumns - 1].push(meter);
}
}
this.columns.truncate(newColumns);
}
Header_calculateHeight(this);
}
pub fn Header_addMeterByName(this: &mut Header, name: &str, mode: MeterModeId, column: usize) {
debug_assert!(column < HeaderLayout_getColumns(this.headerLayout));
let mut param: u32 = 0;
let nameLen = if let Some(parenPos) = name.find('(') {
let digits: String = name[parenPos + 1..]
.chars()
.take_while(|c| c.is_ascii_digit())
.take(10)
.collect();
if digits.is_empty() {
return; }
param = digits.parse().unwrap_or(0);
parenPos
} else {
name.len()
};
let baseName = &name[..nameLen];
for &type_ in Platform_meterTypes.iter() {
if type_.name == baseName {
let mut meter = Meter_new(this.host, param, type_);
if mode != 0 {
Meter_setMode(&mut meter, mode);
}
this.columns[column].push(meter);
break;
}
}
}
pub fn Header_populateFromSettings(this: &mut Header, settings: &Settings) {
Header_setLayout(this, settings.hLayout);
this.headerMargin = settings.headerMargin;
this.screenTabs = settings.screenTabs;
let numColumns = HeaderLayout_getColumns(this.headerLayout);
for col in 0..numColumns {
this.columns[col].clear();
let colSettings = &settings.hColumns[col];
for i in 0..colSettings.len {
let name = colSettings.names.as_ref().unwrap()[i].clone();
let mode = colSettings.modes.as_ref().unwrap()[i];
Header_addMeterByName(this, &name, mode, col);
}
}
Header_calculateHeight(this);
}
pub fn Header_writeBackToSettings(this: &Header, settings: &mut Settings) {
Settings_setHeaderLayout(settings, this.headerLayout);
let numColumns = HeaderLayout_getColumns(this.headerLayout);
for col in 0..numColumns {
let vec = &this.columns[col];
let len = vec.len();
let colSettings = &mut settings.hColumns[col];
if len != 0 {
colSettings.names = Some(
vec.iter()
.map(|meter| {
if meter.param != 0 && meter.name == "CPU" {
format!("{}({})", meter.name, meter.param)
} else {
meter.name.to_string()
}
})
.collect(),
);
colSettings.modes = Some(vec.iter().map(|m| m.mode).collect());
} else {
colSettings.names = None;
colSettings.modes = None;
}
colSettings.len = len;
}
}
pub fn Header_addMeterByClass<'a>(
this: &'a mut Header,
type_: &'static MeterClass,
param: u32,
column: usize,
) -> &'a mut Meter {
debug_assert!(column < HeaderLayout_getColumns(this.headerLayout));
let meter = Meter_new(this.host, param, type_);
this.columns[column].push(meter);
this.columns[column].last_mut().unwrap()
}
pub fn Header_reinit(this: &mut Header) {
let numColumns = HeaderLayout_getColumns(this.headerLayout);
for col in 0..numColumns {
let items = this.columns[col].len();
for i in 0..items {
if let Some(init) = this.columns[col][i].init {
init(&mut this.columns[col][i]);
}
}
}
}
pub fn Header_draw(this: &mut Header, mut out: &mut dyn Write) {
let scheme = ColorScheme::active();
let height = this.height;
let pad = this.pad;
let cols = Ncurses::cols();
Ncurses::attrset(&mut out, ColorElements::RESET_COLOR.packed(scheme));
for y in 0..height {
Ncurses::mvhline(&mut out, y, 0, ' ', cols);
}
let numCols = HeaderLayout_getColumns(this.headerLayout);
let width = cols - 2 * pad - (numCols as i32 - 1);
let widths = HeaderLayout_layouts[this.headerLayout as usize].widths;
let mut x = pad;
let mut roundingLoss = 0.0f32;
for col in 0..numCols {
let mut colWidth = width as f32 * widths[col] as f32 / 100.0;
roundingLoss += colWidth - colWidth.floor();
if roundingLoss >= 1.0 {
colWidth += 1.0;
roundingLoss -= 1.0;
}
let mut y = pad / 2;
let colLen = this.columns[col].len();
for i in 0..colLen {
let (mode, columnWidthCount, isMultiColumn, h, draw) = {
let meter = &this.columns[col][i];
(
meter.mode,
meter.columnWidthCount,
meter.isMultiColumn,
meter.h,
meter.draw,
)
};
let mut actualWidth = colWidth;
if mode == TEXT_METERMODE && !isMultiColumn {
for j in 1..columnWidthCount {
actualWidth += 1.0; actualWidth += width as f32 * widths[col + j as usize] as f32 / 100.0;
}
}
let draw = draw.expect("Header_draw: meter->draw is NULL");
draw(
&mut *out,
&mut this.columns[col][i],
x,
y,
actualWidth.floor() as i32,
);
y += h;
}
x += colWidth.floor() as i32;
x += 1; }
}
pub fn Header_updateData(this: &mut Header) {
let numColumns = HeaderLayout_getColumns(this.headerLayout);
for col in 0..numColumns {
let items = this.columns[col].len();
for i in 0..items {
let updateValues = this.columns[col][i]
.updateValues
.expect("Meter_updateValues: updateValues slot is NULL");
updateValues(&mut this.columns[col][i]);
}
}
}
fn calcColumnWidthCount(
this: &Header,
curMeter: &Meter,
pad: i32,
curColumn: usize,
curHeight: i32,
) -> i32 {
let numColumns = HeaderLayout_getColumns(this.headerLayout);
for i in (curColumn + 1)..numColumns {
let meters = &this.columns[i];
let mut height = pad;
for j in 0..meters.len() {
let meter = &meters[j];
if height >= curHeight + curMeter.h {
break;
}
height += meter.h;
if height <= curHeight {
continue;
}
if meter.name != BlankMeter_class.name {
return (i - curColumn) as i32;
}
}
}
(numColumns - curColumn) as i32
}
pub fn Header_calculateHeight(this: &mut Header) -> i32 {
let pad = if this.headerMargin { 2 } else { 0 };
let mut maxHeight = pad;
let numColumns = HeaderLayout_getColumns(this.headerLayout);
for col in 0..numColumns {
let mut height = pad;
let colLen = this.columns[col].len();
for i in 0..colLen {
let columnWidthCount = {
let meter = &this.columns[col][i];
calcColumnWidthCount(this, meter, pad, col, height)
};
this.columns[col][i].columnWidthCount = columnWidthCount;
height += this.columns[col][i].h;
}
maxHeight = maxHeight.max(height);
}
if maxHeight == pad {
maxHeight = 0;
this.pad = 0;
} else {
this.pad = pad;
}
if this.screenTabs {
maxHeight += 1;
}
this.height = maxHeight;
maxHeight
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ported::settings::{MeterColumnSetting, MeterModeId};
fn meter(name: &'static str, h: i32) -> Meter {
Meter {
name,
mode: 1,
h,
..Meter::empty()
}
}
fn blank(h: i32) -> Meter {
Meter {
name: BlankMeter_class.name,
mode: 1,
h,
..Meter::empty()
}
}
fn names(col: &[Meter]) -> Vec<&str> {
col.iter().map(|m| m.name).collect()
}
fn record_draw(out: &mut dyn Write, m: &mut Meter, x: i32, y: i32, w: i32) {
let _ = write!(out, "\nMARK name={} x={} y={} w={}\n", m.name, x, y, w);
}
fn draw_meter(name: &'static str, h: i32) -> Meter {
Meter {
name,
mode: 1,
h,
draw: Some(record_draw),
..Meter::empty()
}
}
fn marks(buf: &[u8]) -> Vec<String> {
String::from_utf8_lossy(buf)
.lines()
.filter(|l| l.starts_with("MARK"))
.map(|l| l.to_string())
.collect()
}
#[test]
fn new_allocates_one_empty_column_per_layout_column() {
let host = 0xF00D as *const Machine;
for hLayout in [
HeaderLayout::HF_ONE_100,
HeaderLayout::HF_TWO_50_50,
HeaderLayout::HF_THREE_33_34_33,
] {
let h = Header_new(host, hLayout);
assert_eq!(h.columns.len(), HeaderLayout_getColumns(hLayout));
assert!(h.columns.iter().all(|c| c.is_empty()));
assert_eq!(h.host, host);
assert_eq!(h.headerLayout, hLayout);
assert_eq!((h.pad, h.height), (0, 0));
assert!(!h.headerMargin && !h.screenTabs);
}
}
fn header(hLayout: HeaderLayout, columns: Vec<Vec<Meter>>) -> Header {
assert_eq!(HeaderLayout_getColumns(hLayout), columns.len());
Header {
host: core::ptr::null(),
columns,
headerLayout: hLayout,
pad: 0,
height: 0,
headerMargin: false,
screenTabs: false,
}
}
#[test]
fn calculate_height_one_column() {
let mut h = header(
HeaderLayout::HF_ONE_100,
vec![vec![meter("A", 2), meter("B", 2)]],
);
assert_eq!(Header_calculateHeight(&mut h), 4);
assert_eq!(h.height, 4);
assert_eq!(h.pad, 0);
}
#[test]
fn calculate_height_two_columns_takes_max() {
let mut h = header(
HeaderLayout::HF_TWO_50_50,
vec![vec![meter("A", 2), meter("B", 2)], vec![meter("C", 2)]],
);
assert_eq!(Header_calculateHeight(&mut h), 4);
assert_eq!(h.height, 4);
}
#[test]
fn calculate_height_margin_adds_pad_to_each_column() {
let mut h = header(
HeaderLayout::HF_TWO_50_50,
vec![vec![meter("A", 2), meter("B", 2)], vec![meter("C", 2)]],
);
h.headerMargin = true;
assert_eq!(Header_calculateHeight(&mut h), 6);
assert_eq!(h.pad, 2);
}
#[test]
fn calculate_height_three_columns_varying_counts() {
let mut h = header(
HeaderLayout::HF_THREE_33_34_33,
vec![
vec![meter("A", 2)],
vec![meter("B", 2), meter("C", 2), meter("D", 2)],
vec![meter("E", 2), meter("F", 2)],
],
);
assert_eq!(Header_calculateHeight(&mut h), 6);
}
#[test]
fn calculate_height_empty_collapses_to_zero() {
let mut h = header(HeaderLayout::HF_TWO_50_50, vec![vec![], vec![]]);
assert_eq!(Header_calculateHeight(&mut h), 0);
assert_eq!(h.pad, 0);
h.headerMargin = true;
assert_eq!(Header_calculateHeight(&mut h), 0);
assert_eq!(h.pad, 0);
}
#[test]
fn calculate_height_screen_tabs_adds_one() {
let mut h = header(
HeaderLayout::HF_ONE_100,
vec![vec![meter("A", 2), meter("B", 2)]],
);
h.screenTabs = true;
assert_eq!(Header_calculateHeight(&mut h), 5);
let mut e = header(HeaderLayout::HF_ONE_100, vec![vec![]]);
e.screenTabs = true;
assert_eq!(Header_calculateHeight(&mut e), 1);
}
#[test]
fn calculate_height_writes_column_width_count() {
let mut h = header(
HeaderLayout::HF_TWO_50_50,
vec![vec![meter("A", 2)], vec![meter("B", 2)]],
);
Header_calculateHeight(&mut h);
assert_eq!(h.columns[0][0].columnWidthCount, 1);
assert_eq!(h.columns[1][0].columnWidthCount, 1);
}
#[test]
fn width_count_occupied_neighbor_is_one() {
let h = header(
HeaderLayout::HF_TWO_50_50,
vec![vec![meter("A", 2)], vec![meter("B", 2)]],
);
let cur = &h.columns[0][0];
assert_eq!(calcColumnWidthCount(&h, cur, 0, 0, 0), 1);
}
#[test]
fn width_count_blank_then_occupied_spans_two() {
let h = header(
HeaderLayout::HF_THREE_33_34_33,
vec![vec![meter("A", 2)], vec![blank(1)], vec![meter("C", 1)]],
);
let cur = &h.columns[0][0];
assert_eq!(calcColumnWidthCount(&h, cur, 0, 0, 0), 2);
}
#[test]
fn width_count_all_empty_right_is_full_span() {
let h = header(
HeaderLayout::HF_THREE_33_34_33,
vec![vec![meter("A", 2)], vec![], vec![]],
);
let cur = &h.columns[0][0];
assert_eq!(calcColumnWidthCount(&h, cur, 0, 0, 0), 3);
}
#[test]
fn width_count_break_when_neighbor_below_band_with_pad() {
let h = header(
HeaderLayout::HF_TWO_50_50,
vec![vec![meter("A", 1)], vec![meter("B", 5)]],
);
let cur = &h.columns[0][0];
assert_eq!(calcColumnWidthCount(&h, cur, 2, 0, 0), 2);
}
#[test]
fn width_count_skips_meter_above_current_height() {
let h = header(
HeaderLayout::HF_TWO_50_50,
vec![vec![meter("A", 4)], vec![blank(2), meter("C", 2)]],
);
let cur = &h.columns[0][0];
assert_eq!(calcColumnWidthCount(&h, cur, 0, 0, 3), 1);
}
#[test]
fn draw_dispatches_each_meter_at_stacked_y() {
let mut h = header(
HeaderLayout::HF_TWO_50_50,
vec![
vec![draw_meter("A", 2), draw_meter("B", 2)],
vec![draw_meter("C", 1)],
],
);
Header_calculateHeight(&mut h);
let mut buf: Vec<u8> = Vec::new();
Header_draw(&mut h, &mut buf);
let m = marks(&buf);
assert_eq!(m.len(), 3, "one dispatch per meter");
assert!(m[0].contains("name=A") && m[0].contains("y=0"));
assert!(m[1].contains("name=B") && m[1].contains("y=2"));
assert!(m[2].contains("name=C") && m[2].contains("y=0"));
}
#[test]
fn draw_margin_starts_y_at_half_pad() {
let mut h = header(
HeaderLayout::HF_ONE_100,
vec![vec![draw_meter("A", 2), draw_meter("B", 2)]],
);
h.headerMargin = true;
Header_calculateHeight(&mut h);
let mut buf: Vec<u8> = Vec::new();
Header_draw(&mut h, &mut buf);
let m = marks(&buf);
assert_eq!(m.len(), 2);
assert!(m[0].contains("name=A") && m[0].contains("y=1"));
assert!(m[1].contains("name=B") && m[1].contains("y=3"));
}
#[test]
#[should_panic(expected = "meter->draw")]
fn draw_panics_on_missing_draw_slot() {
let mut h = header(HeaderLayout::HF_ONE_100, vec![vec![meter("A", 1)]]);
Header_calculateHeight(&mut h);
let mut buf: Vec<u8> = Vec::new();
Header_draw(&mut h, &mut buf);
}
fn mark_init(m: &mut Meter) {
m.txtBuffer = "inited".to_string();
}
#[test]
fn reinit_runs_init_slot_when_present_and_skips_when_absent() {
let mut h = header(
HeaderLayout::HF_TWO_50_50,
vec![
vec![Meter {
name: "A",
init: Some(mark_init),
..Meter::empty()
}],
vec![Meter {
name: "B",
..Meter::empty()
}],
],
);
Header_reinit(&mut h);
assert_eq!(h.columns[0][0].txtBuffer, "inited");
assert!(h.columns[1][0].txtBuffer.is_empty());
}
#[test]
fn add_meter_by_class_appends_to_column_and_returns_it() {
let mut h = header(HeaderLayout::HF_TWO_50_50, vec![vec![], vec![]]);
let m = Header_addMeterByClass(&mut h, &BlankMeter_class, 7, 1);
assert_eq!(m.name, "Blank");
assert_eq!(m.param, 7);
assert_eq!(h.columns[0].len(), 0);
assert_eq!(h.columns[1].len(), 1);
assert_eq!(h.columns[1][0].name, "Blank");
}
#[test]
fn update_data_dispatches_update_slot_per_meter() {
let mut h = header(HeaderLayout::HF_TWO_50_50, vec![vec![], vec![]]);
Header_addMeterByClass(&mut h, &BlankMeter_class, 0, 0);
Header_addMeterByClass(&mut h, &BlankMeter_class, 0, 1);
h.columns[0][0].txtBuffer = "dirty".to_string();
h.columns[1][0].txtBuffer = "dirty".to_string();
Header_updateData(&mut h);
assert!(h.columns[0][0].txtBuffer.is_empty());
assert!(h.columns[1][0].txtBuffer.is_empty());
}
#[test]
fn add_meter_by_name_looks_up_class_and_appends() {
let mut h = header(HeaderLayout::HF_TWO_50_50, vec![vec![], vec![]]);
Header_addMeterByName(&mut h, "Blank", 0, 1);
assert_eq!(h.columns[0].len(), 0);
assert_eq!(h.columns[1].len(), 1);
assert_eq!(h.columns[1][0].name, "Blank");
assert_eq!(h.columns[1][0].param, 0);
}
#[test]
fn add_meter_by_name_parses_numeric_param_suffix() {
let mut h = header(HeaderLayout::HF_ONE_100, vec![vec![]]);
Header_addMeterByName(&mut h, "Blank(7)", 0, 0);
assert_eq!(h.columns[0].len(), 1);
assert_eq!(h.columns[0][0].name, "Blank");
assert_eq!(h.columns[0][0].param, 7);
}
#[test]
fn add_meter_by_name_unknown_name_adds_nothing() {
let mut h = header(HeaderLayout::HF_ONE_100, vec![vec![]]);
Header_addMeterByName(&mut h, "NoSuchMeter", 0, 0);
assert!(h.columns[0].is_empty());
}
#[test]
fn add_meter_by_name_non_numeric_suffix_is_dynamic_and_adds_nothing() {
let mut h = header(HeaderLayout::HF_ONE_100, vec![vec![]]);
Header_addMeterByName(&mut h, "Blank(cpu)", 0, 0);
assert!(h.columns[0].is_empty());
}
#[test]
fn add_meter_by_name_applies_mode() {
let mut h = header(HeaderLayout::HF_ONE_100, vec![vec![]]);
Header_addMeterByName(&mut h, "Blank", TEXT_METERMODE, 0);
assert_eq!(h.columns[0][0].mode, TEXT_METERMODE);
}
#[test]
fn populate_from_settings_relayouts_and_builds_columns() {
let mut h = header(HeaderLayout::HF_ONE_100, vec![vec![]]);
let settings = Settings {
hLayout: HeaderLayout::HF_TWO_50_50,
hColumns: vec![
MeterColumnSetting {
len: 2,
names: Some(vec!["Blank".to_string(), "Blank".to_string()]),
modes: Some(vec![TEXT_METERMODE, TEXT_METERMODE]),
},
MeterColumnSetting {
len: 1,
names: Some(vec!["Blank".to_string()]),
modes: Some(vec![TEXT_METERMODE]),
},
],
..Default::default()
};
Header_populateFromSettings(&mut h, &settings);
assert_eq!(h.headerLayout, HeaderLayout::HF_TWO_50_50);
assert_eq!(h.columns.len(), 2);
assert_eq!(names(&h.columns[0]), ["Blank", "Blank"]);
assert_eq!(names(&h.columns[1]), ["Blank"]);
assert!(h.height >= 1);
}
#[test]
fn populate_from_settings_resolves_real_meter_classes() {
let mut h = header(HeaderLayout::HF_TWO_50_50, vec![vec![], vec![]]);
let settings = Settings {
hLayout: HeaderLayout::HF_TWO_50_50,
hColumns: vec![
MeterColumnSetting {
len: 2,
names: Some(vec!["Memory".to_string(), "Swap".to_string()]),
modes: Some(vec![TEXT_METERMODE, TEXT_METERMODE]),
},
MeterColumnSetting {
len: 1,
names: Some(vec!["Tasks".to_string()]),
modes: Some(vec![TEXT_METERMODE]),
},
],
..Default::default()
};
Header_populateFromSettings(&mut h, &settings);
assert_eq!(names(&h.columns[0]), ["Memory", "Swap"]);
assert_eq!(names(&h.columns[1]), ["Tasks"]);
assert_eq!(h.columns[0][0].values.len(), 6); assert_eq!(h.columns[0][1].values.len(), 3); assert_eq!(h.columns[1][0].values.len(), 4); }
#[test]
fn populate_from_settings_resolves_load_uptime_clock_hostname() {
let mut h = header(HeaderLayout::HF_ONE_100, vec![vec![]]);
let settings = Settings {
hLayout: HeaderLayout::HF_ONE_100,
hColumns: vec![MeterColumnSetting {
len: 6,
names: Some(vec![
"LoadAverage".to_string(),
"Uptime".to_string(),
"Clock".to_string(),
"Hostname".to_string(),
"Battery".to_string(),
"System".to_string(),
]),
modes: Some(vec![TEXT_METERMODE; 6]),
}],
..Default::default()
};
Header_populateFromSettings(&mut h, &settings);
assert_eq!(
names(&h.columns[0]),
[
"LoadAverage",
"Uptime",
"Clock",
"Hostname",
"Battery",
"System"
]
);
assert_eq!(h.columns[0][0].values.len(), 3); assert_eq!(h.columns[0][1].values.len(), 0); assert_eq!(h.columns[0][4].values.len(), 1); }
#[test]
fn populate_from_settings_prunes_existing_meters_first() {
let mut h = header(HeaderLayout::HF_ONE_100, vec![vec![draw_meter("stale", 2)]]);
let settings = Settings {
hLayout: HeaderLayout::HF_ONE_100,
hColumns: vec![MeterColumnSetting {
len: 1,
names: Some(vec!["Blank".to_string()]),
modes: Some(vec![0]),
}],
..Default::default()
};
Header_populateFromSettings(&mut h, &settings);
assert_eq!(names(&h.columns[0]), ["Blank"]); }
#[test]
fn set_layout_grow_appends_empty_columns() {
let mut h = header(
HeaderLayout::HF_TWO_50_50,
vec![vec![meter("A", 2)], vec![meter("B", 2)]],
);
Header_setLayout(&mut h, HeaderLayout::HF_FOUR_25_25_25_25);
assert_eq!(h.columns.len(), 4);
assert_eq!(h.headerLayout, HeaderLayout::HF_FOUR_25_25_25_25);
assert_eq!(names(&h.columns[0]), ["A"]);
assert_eq!(names(&h.columns[1]), ["B"]);
assert!(h.columns[2].is_empty());
assert!(h.columns[3].is_empty());
}
#[test]
fn set_layout_shrink_migrates_meters_in_reverse() {
let mut h = header(
HeaderLayout::HF_THREE_33_34_33,
vec![
vec![meter("A", 2)],
vec![meter("B", 2), meter("C", 2)],
vec![meter("D", 2)],
],
);
Header_setLayout(&mut h, HeaderLayout::HF_ONE_100);
assert_eq!(h.columns.len(), 1);
assert_eq!(names(&h.columns[0]), ["A", "C", "B", "D"]);
}
#[test]
fn set_layout_same_count_is_noop_on_columns() {
let mut h = header(
HeaderLayout::HF_TWO_50_50,
vec![vec![meter("A", 2)], vec![meter("B", 2)]],
);
Header_setLayout(&mut h, HeaderLayout::HF_TWO_33_67);
assert_eq!(h.headerLayout, HeaderLayout::HF_TWO_33_67);
assert_eq!(h.columns.len(), 2);
assert_eq!(names(&h.columns[0]), ["A"]);
assert_eq!(names(&h.columns[1]), ["B"]);
assert_eq!(h.columns[0][0].h, 2);
assert_eq!(h.columns[1][0].h, 2);
}
#[test]
fn write_back_copies_names_modes_and_sets_layout() {
let hm = |n: &'static str, mode: MeterModeId| Meter {
name: n,
mode,
h: 2,
..Meter::empty()
};
let h = header(
HeaderLayout::HF_TWO_50_50,
vec![
vec![hm("AllCPUs", 1), hm("Memory", 2)],
vec![hm("Tasks", 1)],
],
);
let mut settings = Settings {
hLayout: HeaderLayout::HF_ONE_100,
hColumns: vec![MeterColumnSetting::default()],
screens: Vec::new(),
ssIndex: 0,
changed: false,
lastUpdate: 0,
..Default::default()
};
Header_writeBackToSettings(&h, &mut settings);
assert_eq!(settings.hLayout, HeaderLayout::HF_TWO_50_50);
assert_eq!(settings.hColumns.len(), 2);
assert_eq!(
settings.hColumns[0].names.as_deref().unwrap(),
["AllCPUs", "Memory"]
);
assert_eq!(settings.hColumns[0].modes.as_deref().unwrap(), [1u32, 2]);
assert_eq!(settings.hColumns[0].len, 2);
assert_eq!(settings.hColumns[1].names.as_deref().unwrap(), ["Tasks"]);
assert_eq!(settings.hColumns[1].len, 1);
}
#[test]
fn write_back_empty_column_is_none() {
let h = header(
HeaderLayout::HF_TWO_50_50,
vec![vec![meter("AllCPUs", 2)], vec![]],
);
let mut settings = Settings {
hLayout: HeaderLayout::HF_TWO_50_50,
hColumns: vec![MeterColumnSetting::default(), MeterColumnSetting::default()],
screens: Vec::new(),
ssIndex: 0,
changed: false,
lastUpdate: 0,
..Default::default()
};
Header_writeBackToSettings(&h, &mut settings);
assert_eq!(settings.hColumns[0].len, 1);
assert!(settings.hColumns[1].names.is_none());
assert!(settings.hColumns[1].modes.is_none());
assert_eq!(settings.hColumns[1].len, 0);
}
}