#![allow(non_snake_case)]
#![allow(non_upper_case_globals)] #![allow(dead_code)]
use std::io::Write;
use crate::ported::meter::{
Meter, MeterClass, MeterModeId, Meter_class, Meter_new, Meter_setCaption, Meter_setMode,
BAR_METERMODE, METERMODE_DEFAULT_SUPPORTED,
};
use crate::ported::object::ObjectClass;
pub fn AllCPUsMeter_getRange(this: &Meter) -> (i32, i32) {
let cpus: u32 = unsafe { (*this.host).existingCPUs };
let start: i32;
let count: i32;
match this.name.as_bytes().first().copied() {
Some(b'L') => {
start = 0;
count = ((cpus + 1) / 2) as i32;
}
Some(b'R') => {
start = ((cpus + 1) / 2) as i32;
count = (cpus - start as u32) as i32;
}
_ => {
start = 0;
count = cpus as i32;
}
}
(start, count)
}
pub fn CPUMeter_init(this: &mut Meter) {
let cpu = this.param;
if cpu == 0 {
Meter_setCaption(this, "Avg");
return;
}
let (active_cpus, show_smt, count_from_one) = unsafe {
let host = &*this.host;
let s = host
.settings
.as_ref()
.expect("CPUMeter_init: host->settings");
(host.activeCPUs, s.showCPUSMTLabels, s.countCPUsFromOne)
};
if active_cpus <= 1 {
return;
}
let caption = if show_smt {
#[cfg(target_os = "macos")]
let (core_id, thread_index) = unsafe {
use crate::ported::darwin::darwinmachine::{
Machine_getCPUPhysicalCoreID, Machine_getCPUThreadIndex,
};
let host = &*this.host;
(
Machine_getCPUPhysicalCoreID(host, cpu - 1),
Machine_getCPUThreadIndex(host, cpu - 1),
)
};
#[cfg(not(target_os = "macos"))]
let (core_id, thread_index) = unsafe {
use crate::ported::linux::linuxmachine::{
LinuxMachine, Machine_getCPUPhysicalCoreID, Machine_getCPUThreadIndex,
};
let lm = &*(this.host as *const LinuxMachine);
(
Machine_getCPUPhysicalCoreID(lm, cpu - 1),
Machine_getCPUThreadIndex(lm, cpu - 1),
)
};
let mut thread_letter = b'a' + (thread_index % 26) as u8;
if (thread_index % 52) > 26 {
thread_letter -= b'a' - b'A';
}
let core_disp = if count_from_one { core_id + 1 } else { core_id };
format!("{:2}{}", core_disp, thread_letter as char)
} else {
let id = cpu - 1;
let disp = if count_from_one { id + 1 } else { id };
format!("{:3}", disp)
};
Meter_setCaption(this, &caption);
}
pub fn CPUMeter_getUiName(this: &Meter) -> String {
if this.param > 0 {
let cpu: u32 = this.param - 1;
let id = if unsafe {
(*this.host)
.settings
.as_ref()
.expect("CPUMeter_getUiName: host->settings")
.countCPUsFromOne
} {
cpu + 1
} else {
cpu
};
format!("{} {}", this.uiName, id)
} else {
this.uiName.to_string()
}
}
const CPU_METER_NICE: usize = 0;
const CPU_METER_NORMAL: usize = 1;
const CPU_METER_KERNEL: usize = 2;
const CPU_METER_IRQ: usize = 3;
const CPU_METER_SOFTIRQ: usize = 4;
const CPU_METER_STEAL: usize = 5;
const CPU_METER_GUEST: usize = 6;
const CPU_METER_IOWAIT: usize = 7;
const CPU_METER_FREQUENCY: usize = 8;
const CPU_METER_ITEMCOUNT: usize = 10;
static CPUMETER_ATTRIBUTES: [i32; 8] = [
crate::ported::crt::ColorElements::CPU_NICE as i32,
crate::ported::crt::ColorElements::CPU_NORMAL as i32,
crate::ported::crt::ColorElements::CPU_SYSTEM as i32,
crate::ported::crt::ColorElements::CPU_IRQ as i32,
crate::ported::crt::ColorElements::CPU_SOFTIRQ as i32,
crate::ported::crt::ColorElements::CPU_STEAL as i32,
crate::ported::crt::ColorElements::CPU_GUEST as i32,
crate::ported::crt::ColorElements::CPU_IOWAIT as i32,
];
static CPUMETER_ATTRIBUTES_SUMMARY: [i32; 4] = [
crate::ported::crt::ColorElements::CPU_NICE as i32,
crate::ported::crt::ColorElements::CPU_NORMAL as i32,
crate::ported::crt::ColorElements::CPU_SYSTEM as i32,
crate::ported::crt::ColorElements::CPU_GUEST as i32,
];
pub static CPUMeter_class: MeterClass = MeterClass {
super_: ObjectClass {
extends: Some(&Meter_class.super_),
},
display: Some(CPUMeter_display),
init: Some(CPUMeter_init),
done: None,
updateMode: None,
updateValues: Some(CPUMeter_updateValues),
draw: None,
getCaption: None,
getUiName: Some(CPUMeter_getUiName),
defaultMode: BAR_METERMODE,
supportedModes: METERMODE_DEFAULT_SUPPORTED,
total: 100.0,
attributes: &CPUMETER_ATTRIBUTES,
name: "CPU",
uiName: "CPU",
caption: "CPU",
description: None,
maxItems: CPU_METER_ITEMCOUNT as u8,
isMultiColumn: false,
isPercentChart: true,
};
pub fn CPUMeter_updateValues(this: &mut crate::ported::meter::Meter) {
for i in 0..CPU_METER_ITEMCOUNT {
this.values[i] = 0.0;
}
let (show_cpu_usage, show_cpu_frequency, detailed, existing_cpus) = unsafe {
let host = &*this.host;
let s = host
.settings
.as_ref()
.expect("CPUMeter_updateValues: host->settings");
(
s.showCPUUsage,
s.showCPUFrequency,
s.detailedCPUTime,
host.existingCPUs,
)
};
this.curAttributes = Some(if detailed {
&CPUMETER_ATTRIBUTES[..]
} else {
&CPUMETER_ATTRIBUTES_SUMMARY[..]
});
let cpu = this.param;
if cpu > existing_cpus {
this.txtBuffer = "absent".to_string();
return;
}
#[cfg(target_os = "macos")]
let percent = crate::ported::darwin::platform::Platform_setCPUValues(this, cpu);
#[cfg(not(target_os = "macos"))]
let percent = crate::ported::linux::platform::Platform_setCPUValues(this, cpu);
if !(percent >= 0.0) {
this.txtBuffer = "offline".to_string();
return;
}
let mut cpu_usage = String::new();
let mut cpu_frequency = String::new();
if show_cpu_usage {
cpu_usage = format!("{percent:.1}%");
}
if show_cpu_frequency {
let f = this.values[CPU_METER_FREQUENCY];
cpu_frequency = if f >= 0.0 {
format!("{:>4}MHz", f as u32)
} else {
"N/A".to_string()
};
}
let sep = if !cpu_usage.is_empty() && !cpu_frequency.is_empty() {
" "
} else {
""
};
this.txtBuffer = format!("{cpu_usage}{sep}{cpu_frequency}");
}
pub fn CPUMeter_display(
this: &crate::ported::meter::Meter,
out: &mut crate::ported::richstring::RichString,
) {
use crate::ported::crt::{ColorElements as CE, ColorScheme};
use crate::ported::richstring::{
RichString_appendAscii, RichString_appendnAscii, RichString_appendnWide,
};
let scheme = ColorScheme::active();
let (detailed, show_frequency, existing_cpus) = unsafe {
let host = &*this.host;
let s = host
.settings
.as_ref()
.expect("CPUMeter_display: host->settings");
(s.detailedCPUTime, s.showCPUFrequency, host.existingCPUs)
};
let pct = |v: f64| -> String { format!("{v:5.1}% ") };
if this.param > existing_cpus {
RichString_appendAscii(out, CE::METER_SHADOW.packed(scheme), b" absent");
return;
}
if this.curItems == 0 {
RichString_appendAscii(out, CE::METER_SHADOW.packed(scheme), b" offline");
return;
}
let v = &this.values;
let text = CE::METER_TEXT.packed(scheme);
let buffer = pct(v[CPU_METER_NORMAL]);
RichString_appendAscii(out, text, b":");
RichString_appendnAscii(
out,
CE::CPU_NORMAL.packed(scheme),
buffer.as_bytes(),
buffer.len(),
);
if detailed {
for (label, idx, color, gated) in [
("sy:", CPU_METER_KERNEL, CE::CPU_SYSTEM, false),
("ni:", CPU_METER_NICE, CE::CPU_NICE_TEXT, false),
("hi:", CPU_METER_IRQ, CE::CPU_IRQ, false),
("si:", CPU_METER_SOFTIRQ, CE::CPU_SOFTIRQ, false),
("st:", CPU_METER_STEAL, CE::CPU_STEAL, true),
("gu:", CPU_METER_GUEST, CE::CPU_GUEST, true),
("wa:", CPU_METER_IOWAIT, CE::CPU_IOWAIT, false),
] {
if gated && !(v[idx] >= 0.0) {
continue; }
let buffer = pct(v[idx]);
RichString_appendAscii(out, text, label.as_bytes());
RichString_appendnAscii(out, color.packed(scheme), buffer.as_bytes(), buffer.len());
}
} else {
let buffer = pct(v[CPU_METER_KERNEL]);
RichString_appendAscii(out, text, b"sys:");
RichString_appendnAscii(
out,
CE::CPU_SYSTEM.packed(scheme),
buffer.as_bytes(),
buffer.len(),
);
let buffer = pct(v[CPU_METER_NICE]);
RichString_appendAscii(out, text, b"low:");
RichString_appendnAscii(
out,
CE::CPU_NICE_TEXT.packed(scheme),
buffer.as_bytes(),
buffer.len(),
);
if v[CPU_METER_IRQ] >= 0.0 {
let buffer = pct(v[CPU_METER_IRQ]);
RichString_appendAscii(out, text, b"vir:");
RichString_appendnAscii(
out,
CE::CPU_GUEST.packed(scheme),
buffer.as_bytes(),
buffer.len(),
);
}
}
if show_frequency {
let f = v[CPU_METER_FREQUENCY];
let buffer = if f >= 0.0 {
format!("{:>4}MHz ", f as u32)
} else {
"N/A ".to_string()
};
RichString_appendAscii(out, text, b"freq: ");
RichString_appendnWide(
out,
CE::METER_VALUE.packed(scheme),
buffer.as_bytes(),
buffer.len(),
);
}
}
struct CPUMeterData {
cpus: u32,
meters: Vec<Meter>,
}
impl CPUMeterData {
fn of(this: &mut Meter) -> &mut CPUMeterData {
this.meterData
.as_mut()
.and_then(|d| d.downcast_mut::<CPUMeterData>())
.expect("CPU meter: meterData is not an initialized CPUMeterData")
}
}
pub fn AllCPUsMeter_updateValues(this: &mut Meter) {
let (_start, count) = AllCPUsMeter_getRange(this);
let data = CPUMeterData::of(this);
for i in 0..count as usize {
let m = &mut data.meters[i];
let uv = m
.updateValues
.expect("AllCPUsMeter_updateValues: sub-meter updateValues");
uv(m);
}
}
pub fn CPUMeterCommonInit(this: &mut Meter) {
let (start, count) = AllCPUsMeter_getRange(this);
let host = this.host;
if this.meterData.is_none() {
let cpus = unsafe { (*host).existingCPUs };
this.meterData = Some(Box::new(CPUMeterData {
cpus,
meters: Vec::new(),
}));
}
let data = CPUMeterData::of(this);
for i in 0..count as usize {
if i < data.meters.len() {
CPUMeter_init(&mut data.meters[i]);
} else {
let param = (start + i as i32 + 1) as u32;
data.meters.push(Meter_new(host, param, &CPUMeter_class));
}
}
}
pub fn CPUMeterCommonUpdateMode(this: &mut Meter, mode: MeterModeId, ncol: i32) {
this.mode = mode;
let (_start, count) = AllCPUsMeter_getRange(this);
if count == 0 {
this.h = 1;
return;
}
let data = CPUMeterData::of(this);
for i in 0..count as usize {
Meter_setMode(&mut data.meters[i], mode);
}
let h = data.meters[0].h;
debug_assert!(h > 0);
this.h = h * ((count + ncol - 1) / ncol);
}
pub fn AllCPUsMeter_done(this: &mut Meter) {
this.meterData = None;
}
pub fn SingleColCPUsMeter_updateMode(this: &mut Meter, mode: MeterModeId) {
CPUMeterCommonUpdateMode(this, mode, 1);
}
pub fn DualColCPUsMeter_updateMode(this: &mut Meter, mode: MeterModeId) {
CPUMeterCommonUpdateMode(this, mode, 2);
}
pub fn QuadColCPUsMeter_updateMode(this: &mut Meter, mode: MeterModeId) {
CPUMeterCommonUpdateMode(this, mode, 4);
}
pub fn OctoColCPUsMeter_updateMode(this: &mut Meter, mode: MeterModeId) {
CPUMeterCommonUpdateMode(this, mode, 8);
}
pub fn CPUMeterCommonDraw(
out: &mut dyn Write,
this: &mut Meter,
x: i32,
y: i32,
w: i32,
ncol: i32,
) {
let (_start, count) = AllCPUsMeter_getRange(this);
let colwidth = w / ncol;
let diff = w % ncol;
let nrows = (count + ncol - 1) / ncol;
let data = CPUMeterData::of(this);
let h0 = if count > 0 { data.meters[0].h } else { 0 };
for i in 0..count {
let col = i / nrows;
let d = if col > diff { diff } else { col }; let xpos = x + col * colwidth + d;
let ypos = y + (i % nrows) * h0;
let m = &mut data.meters[i as usize];
let draw = m.draw.expect("CPUMeterCommonDraw: sub-meter draw");
draw(&mut *out, m, xpos, ypos, colwidth);
}
}
pub fn DualColCPUsMeter_draw(out: &mut dyn Write, this: &mut Meter, x: i32, y: i32, w: i32) {
CPUMeterCommonDraw(out, this, x, y, w, 2);
}
pub fn QuadColCPUsMeter_draw(out: &mut dyn Write, this: &mut Meter, x: i32, y: i32, w: i32) {
CPUMeterCommonDraw(out, this, x, y, w, 4);
}
pub fn OctoColCPUsMeter_draw(out: &mut dyn Write, this: &mut Meter, x: i32, y: i32, w: i32) {
CPUMeterCommonDraw(out, this, x, y, w, 8);
}
pub fn SingleColCPUsMeter_draw(out: &mut dyn Write, this: &mut Meter, x: i32, mut y: i32, w: i32) {
let (_start, count) = AllCPUsMeter_getRange(this);
let data = CPUMeterData::of(this);
for i in 0..count as usize {
let m = &mut data.meters[i];
let draw = m.draw.expect("SingleColCPUsMeter_draw: sub-meter draw");
draw(&mut *out, m, x, y, w);
y += m.h;
}
}
macro_rules! all_cpus_meter_class {
($id:ident, $draw:path, $mode:path, $multicol:expr, $name:literal, $ui:literal, $desc:literal) => {
pub static $id: MeterClass = MeterClass {
super_: ObjectClass {
extends: Some(&Meter_class.super_),
},
display: Some(CPUMeter_display),
init: Some(CPUMeterCommonInit),
done: Some(AllCPUsMeter_done),
updateMode: Some($mode),
updateValues: Some(AllCPUsMeter_updateValues),
draw: Some($draw),
getCaption: None,
getUiName: None,
defaultMode: BAR_METERMODE,
supportedModes: METERMODE_DEFAULT_SUPPORTED,
total: 100.0,
attributes: &CPUMETER_ATTRIBUTES,
name: $name,
uiName: $ui,
caption: "CPU",
description: Some($desc),
maxItems: 0,
isMultiColumn: $multicol,
isPercentChart: false,
};
};
}
all_cpus_meter_class!(
AllCPUsMeter_class,
SingleColCPUsMeter_draw,
SingleColCPUsMeter_updateMode,
false,
"AllCPUs",
"CPUs (1/1)",
"CPUs (1/1): all CPUs"
);
all_cpus_meter_class!(
AllCPUs2Meter_class,
DualColCPUsMeter_draw,
DualColCPUsMeter_updateMode,
true,
"AllCPUs2",
"CPUs (1&2/2)",
"CPUs (1&2/2): all CPUs in 2 shorter columns"
);
all_cpus_meter_class!(
LeftCPUsMeter_class,
SingleColCPUsMeter_draw,
SingleColCPUsMeter_updateMode,
true,
"LeftCPUs",
"CPUs (1/2)",
"CPUs (1/2): first half of list"
);
all_cpus_meter_class!(
RightCPUsMeter_class,
SingleColCPUsMeter_draw,
SingleColCPUsMeter_updateMode,
true,
"RightCPUs",
"CPUs (2/2)",
"CPUs (2/2): second half of list"
);
all_cpus_meter_class!(
LeftCPUs2Meter_class,
DualColCPUsMeter_draw,
DualColCPUsMeter_updateMode,
true,
"LeftCPUs2",
"CPUs (1&2/4)",
"CPUs (1&2/4): first half in 2 shorter columns"
);
all_cpus_meter_class!(
RightCPUs2Meter_class,
DualColCPUsMeter_draw,
DualColCPUsMeter_updateMode,
true,
"RightCPUs2",
"CPUs (3&4/4)",
"CPUs (3&4/4): second half in 2 shorter columns"
);
all_cpus_meter_class!(
AllCPUs4Meter_class,
QuadColCPUsMeter_draw,
QuadColCPUsMeter_updateMode,
true,
"AllCPUs4",
"CPUs (1&2&3&4/4)",
"CPUs (1&2&3&4/4): all CPUs in 4 shorter columns"
);
all_cpus_meter_class!(
LeftCPUs4Meter_class,
QuadColCPUsMeter_draw,
QuadColCPUsMeter_updateMode,
true,
"LeftCPUs4",
"CPUs (1-4/8)",
"CPUs (1-4/8): first half in 4 shorter columns"
);
all_cpus_meter_class!(
RightCPUs4Meter_class,
QuadColCPUsMeter_draw,
QuadColCPUsMeter_updateMode,
true,
"RightCPUs4",
"CPUs (5-8/8)",
"CPUs (5-8/8): second half in 4 shorter columns"
);
all_cpus_meter_class!(
AllCPUs8Meter_class,
OctoColCPUsMeter_draw,
OctoColCPUsMeter_updateMode,
true,
"AllCPUs8",
"CPUs (1-8/8)",
"CPUs (1-8/8): all CPUs in 8 shorter columns"
);
all_cpus_meter_class!(
LeftCPUs8Meter_class,
OctoColCPUsMeter_draw,
OctoColCPUsMeter_updateMode,
true,
"LeftCPUs8",
"CPUs (1-8/16)",
"CPUs (1-8/16): first half in 8 shorter columns"
);
all_cpus_meter_class!(
RightCPUs8Meter_class,
OctoColCPUsMeter_draw,
OctoColCPUsMeter_updateMode,
true,
"RightCPUs8",
"CPUs (9-16/16)",
"CPUs (9-16/16): second half in 8 shorter columns"
);
#[cfg(test)]
mod tests {
use super::*;
use crate::ported::machine::Machine as CanonMachine;
use crate::ported::settings::Settings as CanonSettings;
fn host(existingCPUs: u32, countCPUsFromOne: bool) -> *const CanonMachine {
let m: &'static CanonMachine = Box::leak(Box::new(CanonMachine {
existingCPUs,
settings: Some(CanonSettings {
countCPUsFromOne,
..Default::default()
}),
..Default::default()
}));
m as *const CanonMachine
}
fn meter(name: &'static str, existingCPUs: u32) -> Meter {
Meter {
name,
uiName: "CPU",
param: 0,
host: host(existingCPUs, false),
..Meter::empty()
}
}
fn ui_meter(uiName: &'static str, param: u32, countCPUsFromOne: bool) -> Meter {
Meter {
name: "CPU",
uiName,
param,
host: host(0, countCPUsFromOne),
..Meter::empty()
}
}
fn init_host(activeCPUs: u32, countCPUsFromOne: bool, smt: bool) -> *const CanonMachine {
let m: &'static CanonMachine = Box::leak(Box::new(CanonMachine {
activeCPUs,
existingCPUs: activeCPUs,
settings: Some(CanonSettings {
countCPUsFromOne,
showCPUSMTLabels: smt,
..Default::default()
}),
..Default::default()
}));
m as *const CanonMachine
}
fn init_meter(param: u32, host: *const CanonMachine) -> Meter {
Meter {
name: "CPU",
uiName: "CPU",
param,
host,
..Meter::empty()
}
}
#[test]
fn init_average_meter_is_avg() {
let mut m = init_meter(0, init_host(4, false, false));
CPUMeter_init(&mut m);
assert_eq!(m.caption, "Avg");
}
#[test]
fn init_per_cpu_caption_is_zero_based_id() {
let mut m = init_meter(1, init_host(4, false, false));
CPUMeter_init(&mut m);
assert_eq!(m.caption, " 0");
}
#[test]
fn init_per_cpu_caption_counts_from_one() {
let mut m = init_meter(2, init_host(4, true, false));
CPUMeter_init(&mut m);
assert_eq!(m.caption, " 2");
}
#[test]
fn init_single_cpu_host_leaves_caption_default() {
let mut m = init_meter(1, init_host(1, false, false));
CPUMeter_init(&mut m);
assert_eq!(m.caption, "");
}
fn cpu_meter_data(m: &Meter) -> &CPUMeterData {
m.meterData
.as_ref()
.unwrap()
.downcast_ref::<CPUMeterData>()
.unwrap()
}
#[test]
fn allcpus_new_builds_one_submeter_per_cpu() {
let m = Meter_new(init_host(4, false, false), 0, &AllCPUsMeter_class);
let data = cpu_meter_data(&m);
assert_eq!(data.cpus, 4);
assert_eq!(data.meters.len(), 4);
assert_eq!(
data.meters.iter().map(|s| s.param).collect::<Vec<_>>(),
vec![1, 2, 3, 4]
);
assert_eq!(m.mode, BAR_METERMODE);
}
#[test]
fn left_right_split_range_across_submeters() {
let left = Meter_new(init_host(4, false, false), 0, &LeftCPUsMeter_class);
let right = Meter_new(init_host(4, false, false), 0, &RightCPUsMeter_class);
assert_eq!(
cpu_meter_data(&left)
.meters
.iter()
.map(|s| s.param)
.collect::<Vec<_>>(),
vec![1, 2]
);
assert_eq!(
cpu_meter_data(&right)
.meters
.iter()
.map(|s| s.param)
.collect::<Vec<_>>(),
vec![3, 4]
);
}
#[test]
fn dualcol_updatemode_stacks_height_over_two_columns() {
let mut m = Meter_new(init_host(4, false, false), 0, &AllCPUs2Meter_class);
let sub_h = cpu_meter_data(&m).meters[0].h;
DualColCPUsMeter_updateMode(&mut m, BAR_METERMODE);
assert!(sub_h > 0);
assert_eq!(m.h, sub_h * 2);
}
#[test]
fn done_releases_submeters() {
let mut m = Meter_new(init_host(4, false, false), 0, &AllCPUsMeter_class);
assert!(m.meterData.is_some());
AllCPUsMeter_done(&mut m);
assert!(m.meterData.is_none());
}
#[test]
fn all_covers_whole_range() {
assert_eq!(AllCPUsMeter_getRange(&meter("AllCPUs", 8)), (0, 8));
assert_eq!(AllCPUsMeter_getRange(&meter("AllCPUs4", 12)), (0, 12));
}
#[test]
fn default_first_char_falls_through_to_all() {
assert_eq!(AllCPUsMeter_getRange(&meter("CPU", 6)), (0, 6));
assert_eq!(AllCPUsMeter_getRange(&meter("Xyz", 6)), (0, 6));
assert_eq!(AllCPUsMeter_getRange(&meter("z", 6)), (0, 6));
}
#[test]
fn left_is_ceiling_half() {
assert_eq!(AllCPUsMeter_getRange(&meter("LeftCPUs", 8)), (0, 4));
assert_eq!(AllCPUsMeter_getRange(&meter("LeftCPUs2", 5)), (0, 3));
assert_eq!(AllCPUsMeter_getRange(&meter("LeftCPUs4", 7)), (0, 4));
}
#[test]
fn right_is_remainder_after_left() {
assert_eq!(AllCPUsMeter_getRange(&meter("RightCPUs", 8)), (4, 4));
assert_eq!(AllCPUsMeter_getRange(&meter("RightCPUs2", 5)), (3, 2));
assert_eq!(AllCPUsMeter_getRange(&meter("RightCPUs8", 7)), (4, 3));
}
#[test]
fn left_and_right_partition_all_cpus() {
for cpus in 0u32..=64 {
let (l_start, l_count) = AllCPUsMeter_getRange(&meter("LeftCPUs", cpus));
let (r_start, r_count) = AllCPUsMeter_getRange(&meter("RightCPUs", cpus));
assert_eq!(l_start, 0, "left always starts at 0 (cpus={cpus})");
assert_eq!(
r_start, l_count,
"right starts where left ends (cpus={cpus})"
);
assert_eq!(
l_count + r_count,
cpus as i32,
"halves sum to cpus (cpus={cpus})"
);
assert!(l_count >= r_count, "left >= right (cpus={cpus})");
}
}
#[test]
fn ui_name_average_meter_is_bare_ui_name() {
assert_eq!(CPUMeter_getUiName(&ui_meter("CPU", 0, false)), "CPU");
assert_eq!(CPUMeter_getUiName(&ui_meter("CPU", 0, true)), "CPU");
}
#[test]
fn ui_name_per_cpu_zero_based() {
assert_eq!(CPUMeter_getUiName(&ui_meter("CPU", 1, false)), "CPU 0");
assert_eq!(CPUMeter_getUiName(&ui_meter("CPU", 8, false)), "CPU 7");
}
#[test]
fn ui_name_per_cpu_one_based() {
assert_eq!(CPUMeter_getUiName(&ui_meter("CPU", 1, true)), "CPU 1");
assert_eq!(CPUMeter_getUiName(&ui_meter("CPU", 8, true)), "CPU 8");
}
#[test]
fn zero_and_one_cpu_edges() {
assert_eq!(AllCPUsMeter_getRange(&meter("AllCPUs", 0)), (0, 0));
assert_eq!(AllCPUsMeter_getRange(&meter("LeftCPUs", 0)), (0, 0));
assert_eq!(AllCPUsMeter_getRange(&meter("RightCPUs", 0)), (0, 0));
assert_eq!(AllCPUsMeter_getRange(&meter("AllCPUs", 1)), (0, 1));
assert_eq!(AllCPUsMeter_getRange(&meter("LeftCPUs", 1)), (0, 1));
assert_eq!(AllCPUsMeter_getRange(&meter("RightCPUs", 1)), (1, 0));
}
}
#[cfg(test)]
#[cfg(not(target_os = "macos"))]
mod cpu_data_tests {
use crate::ported::linux::linuxmachine::{CPUData, LinuxMachine};
use crate::ported::machine::{Machine, Settings};
use crate::ported::meter::Meter;
fn hosted(cpu: CPUData, settings: Settings, existing: u32) -> Meter {
let host = Box::leak(Box::new(LinuxMachine {
super_: Machine {
existingCPUs: existing,
settings: Some(settings),
..Default::default()
},
cpuData: vec![cpu],
..Default::default()
}));
Meter {
values: vec![0.0; 10],
param: 0,
host: &host.super_ as *const crate::ported::machine::Machine,
..Meter::empty()
}
}
#[test]
fn summary_mode_usage_percent() {
let cpu = CPUData {
online: true,
totalPeriod: 100,
userPeriod: 50,
nicePeriod: 10,
systemAllPeriod: 20,
..Default::default()
};
let settings = Settings {
showCPUUsage: true,
detailedCPUTime: false,
..Default::default()
};
let mut m = hosted(cpu, settings, 8);
super::CPUMeter_updateValues(&mut m);
assert_eq!(m.txtBuffer, "80.0%");
assert_eq!(m.curItems, 4);
assert_eq!(m.values[0], 10.0); assert_eq!(m.values[1], 50.0); }
#[test]
fn offline_cpu_renders_offline() {
let cpu = CPUData {
online: false,
..Default::default()
};
let mut m = hosted(
cpu,
Settings {
showCPUUsage: true,
..Default::default()
},
8,
);
super::CPUMeter_updateValues(&mut m);
assert_eq!(m.txtBuffer, "offline");
assert_eq!(m.curItems, 0);
}
#[test]
fn absent_cpu_renders_absent() {
let cpu = CPUData {
online: true,
totalPeriod: 100,
..Default::default()
};
let mut m = hosted(cpu, Settings::default(), 0);
m.param = 5; super::CPUMeter_updateValues(&mut m);
assert_eq!(m.txtBuffer, "absent");
}
fn text(r: &crate::ported::richstring::RichString) -> String {
(0..r.chlen as usize).map(|i| r.chptr[i].chars).collect()
}
#[test]
fn display_summary_line() {
let cpu = CPUData {
online: true,
totalPeriod: 100,
userPeriod: 50, nicePeriod: 10, systemAllPeriod: 20, ..Default::default()
};
let settings = Settings {
detailedCPUTime: false,
..Default::default()
};
let mut m = hosted(cpu, settings, 8);
super::CPUMeter_updateValues(&mut m);
let mut out = crate::ported::richstring::RichString::new();
super::CPUMeter_display(&m, &mut out);
assert_eq!(text(&out), ": 50.0% sys: 20.0% low: 10.0% vir: 0.0% ");
}
}
#[cfg(test)]
#[cfg(target_os = "macos")]
mod cpu_data_darwin_tests {
use crate::ported::darwin::darwinmachine::{DarwinMachine_freeCPULoadInfo, Machine_new};
use crate::ported::machine::{ScreenSettings, Settings};
use crate::ported::meter::Meter;
#[test]
fn update_values_reads_live_cpu_load() {
let mut dm = Machine_new(None, 0);
dm.super_.settings = Some(Settings {
showCPUUsage: true,
detailedCPUTime: false,
screens: vec![ScreenSettings::default()],
..Default::default()
});
let mut m = Meter {
values: vec![0.0; 10],
param: 1, host: &dm.super_ as *const crate::ported::machine::Machine,
..Meter::empty()
};
super::CPUMeter_updateValues(&mut m);
assert!(m.txtBuffer.ends_with('%'), "got {:?}", m.txtBuffer);
assert_eq!(m.curItems, 3);
DarwinMachine_freeCPULoadInfo(&mut dm.prev_load);
DarwinMachine_freeCPULoadInfo(&mut dm.curr_load);
}
}