#![allow(unused_imports)]
#![allow(clippy::field_reassign_with_default)]
use std::collections::BTreeMap;
use std::path::Path;
use super::aggregate::{format_cpu_range, merge_aggregated_into};
use super::cgroup_merge::{
merge_cgroup_cpu, merge_cgroup_memory, merge_cgroup_pids, merge_kv_counters, merge_max_option,
merge_memory_stat, merge_min_option, merge_psi,
};
use super::columns::{compare_columns_for, format_cgroup_only_section_warning};
use super::compare::sort_diff_rows_by_keys;
use super::groups::build_row;
use super::pattern::{
Segment, apply_systemd_template, cgroup_normalize_skeleton, cgroup_skeleton_tokens,
classify_token, is_token_separator, pattern_counts_union, pattern_key, split_into_segments,
tighten_group,
};
use super::render::psi_pair_has_data;
use super::scale::{auto_scale, format_delta_cell};
use super::tests_fixtures::*;
use super::*;
use crate::ctprof::{CgroupStats, CtprofSnapshot, Psi, ThreadState};
use crate::metric_types::{
Bytes, CategoricalString, CpuSet, MonotonicCount, MonotonicNs, OrdinalI32, PeakNs,
};
use regex::Regex;
#[test]
fn format_derived_value_cell_ratio_three_decimals() {
let v = DerivedValue::Scalar(0.873_5);
let cell = format_derived_value_cell(v, ScaleLadder::None, true);
assert_eq!(cell, "0.874");
}
#[test]
fn format_derived_value_cell_ns_auto_scales() {
let v = DerivedValue::Scalar(2_500_000.0);
let cell = format_derived_value_cell(v, ScaleLadder::Ns, false);
assert_eq!(cell, "2.500ms");
}
#[test]
fn format_derived_value_cell_ns_preserves_fractional_precision() {
let v = DerivedValue::Scalar(123.4);
let cell = format_derived_value_cell(v, ScaleLadder::Ns, false);
assert_eq!(cell, "123.40ns");
}
#[test]
fn format_derived_value_cell_negative_bytes_signed() {
let two_kib_neg = -(2.0 * 1024.0);
let v = DerivedValue::Scalar(two_kib_neg);
let cell = format_derived_value_cell(v, ScaleLadder::Bytes, false);
assert_eq!(cell, "-2.000KiB");
}
#[test]
fn format_derived_delta_cell_ratio_carries_sign() {
let cell = format_derived_delta_cell(0.1, ScaleLadder::None, true);
assert_eq!(cell, "+0.100");
}
#[test]
fn format_derived_value_cell_negative_bytes_at_mib_step() {
let v = DerivedValue::Scalar(-2_000_000.0);
let cell = format_derived_value_cell(v, ScaleLadder::Bytes, false);
assert_eq!(cell, "-1.907MiB");
}
#[test]
fn format_derived_value_cell_ratio_above_one_renders_verbatim() {
let v = DerivedValue::Scalar(1.5);
let cell = format_derived_value_cell(v, ScaleLadder::None, true);
assert_eq!(cell, "1.500");
}
#[test]
fn auto_scale_ns_boundary_stays_at_base_below_threshold() {
assert_eq!(auto_scale(0.0, ScaleLadder::Ns), (0.0, "ns"));
assert_eq!(auto_scale(999.0, ScaleLadder::Ns), (999.0, "ns"));
assert_eq!(auto_scale(1000.0, ScaleLadder::Ns), (1.0, "µs"));
}
#[test]
fn auto_scale_ns_ladder_steps_up_at_powers_of_ten() {
let (v, u) = auto_scale(1_500.0, ScaleLadder::Ns);
assert_eq!(u, "µs");
assert!((v - 1.5).abs() < 1e-9);
let (v, u) = auto_scale(1_500_000.0, ScaleLadder::Ns);
assert_eq!(u, "ms");
assert!((v - 1.5).abs() < 1e-9);
let (v, u) = auto_scale(1_500_000_000.0, ScaleLadder::Ns);
assert_eq!(u, "s");
assert!((v - 1.5).abs() < 1e-9);
}
#[test]
fn auto_scale_byte_iec_ladder_uses_1024() {
assert_eq!(auto_scale(1023.0, ScaleLadder::Bytes), (1023.0, "B"));
let (v, u) = auto_scale(1024.0, ScaleLadder::Bytes);
assert_eq!(u, "KiB");
assert!((v - 1.0).abs() < 1e-9);
let (v, u) = auto_scale(1024.0 * 1024.0, ScaleLadder::Bytes);
assert_eq!(u, "MiB");
assert!((v - 1.0).abs() < 1e-9);
let (v, u) = auto_scale(1024.0 * 1024.0 * 1024.0, ScaleLadder::Bytes);
assert_eq!(u, "GiB");
assert!((v - 1.0).abs() < 1e-9);
}
#[test]
fn auto_scale_ticks_ladder_uses_decimal_prefixes() {
assert_eq!(auto_scale(999.0, ScaleLadder::Ticks), (999.0, "ticks"));
let (v, u) = auto_scale(1_500.0, ScaleLadder::Ticks);
assert_eq!(u, "Kticks");
assert!((v - 1.5).abs() < 1e-9);
let (v, u) = auto_scale(2_000_000.0, ScaleLadder::Ticks);
assert_eq!(u, "Mticks");
assert!((v - 2.0).abs() < 1e-9);
}
#[test]
fn auto_scale_unitless_ladder_uses_si_prefixes() {
assert_eq!(auto_scale(999.0, ScaleLadder::Unitless), (999.0, ""));
let (v, u) = auto_scale(1_500.0, ScaleLadder::Unitless);
assert_eq!(u, "K");
assert!((v - 1.5).abs() < 1e-9);
let (v, u) = auto_scale(2_500_000.0, ScaleLadder::Unitless);
assert_eq!(u, "M");
assert!((v - 2.5).abs() < 1e-9);
let (v, u) = auto_scale(3_000_000_000.0, ScaleLadder::Unitless);
assert_eq!(u, "G");
assert!((v - 3.0).abs() < 1e-9);
}
#[test]
fn auto_scale_preserves_sign_on_negative_input() {
let (v, u) = auto_scale(-2_000_000.0, ScaleLadder::Ns);
assert_eq!(u, "ms");
assert!((v - (-2.0)).abs() < 1e-9);
let (v, u) = auto_scale(-5_000.0, ScaleLadder::Bytes);
assert_eq!(u, "KiB");
assert!((v - (-5000.0 / 1024.0)).abs() < 1e-9);
}
#[test]
fn format_value_cell_renders_sum_at_appropriate_scale() {
assert_eq!(
format_value_cell(&Aggregated::Sum(50), ScaleLadder::Ns),
"50ns"
);
assert_eq!(
format_value_cell(&Aggregated::Sum(999), ScaleLadder::Ns),
"999ns"
);
assert_eq!(
format_value_cell(&Aggregated::Sum(1_500), ScaleLadder::Ns),
"1.500µs",
);
assert_eq!(
format_value_cell(&Aggregated::Sum(2_000_000), ScaleLadder::Ns),
"2.000ms",
);
}
#[test]
fn format_value_cell_renders_max_at_appropriate_scale() {
assert_eq!(
format_value_cell(&Aggregated::Max(100), ScaleLadder::Ns),
"100ns"
);
assert_eq!(
format_value_cell(&Aggregated::Max(7_500_000), ScaleLadder::Ns),
"7.500ms",
);
}
#[test]
fn format_value_cell_passes_non_numeric_aggregates_through() {
let m = Aggregated::mode_single("SCHED_OTHER".into(), 4, 4);
assert_eq!(format_value_cell(&m, ScaleLadder::None), "SCHED_OTHER");
let r = Aggregated::OrdinalRange { min: -5, max: 10 };
assert_eq!(format_value_cell(&r, ScaleLadder::None), "-5..10");
}
#[test]
fn format_delta_cell_renders_signed_scaled_value() {
assert_eq!(format_delta_cell(-50.0, ScaleLadder::Ns), "-50ns");
assert_eq!(format_delta_cell(50.0, ScaleLadder::Ns), "+50ns");
assert_eq!(format_delta_cell(0.0, ScaleLadder::Ns), "+0ns");
assert_eq!(format_delta_cell(50.5, ScaleLadder::Ns), "+50.500ns");
assert_eq!(format_delta_cell(2_000_000.0, ScaleLadder::Ns), "+2.000ms");
assert_eq!(format_delta_cell(-2_000_000.0, ScaleLadder::Ns), "-2.000ms");
}
#[test]
fn auto_scale_does_not_affect_sort_order() {
let mut a_small = make_thread("small", "w");
a_small.run_time_ns = MonotonicNs(100);
let mut a_big = make_thread("big", "w");
a_big.run_time_ns = MonotonicNs(1_000_000);
let mut b_small = make_thread("small", "w");
b_small.run_time_ns = MonotonicNs(110);
let mut b_big = make_thread("big", "w");
b_big.run_time_ns = MonotonicNs(2_000_000);
let diff = compare(
&snap_with(vec![a_small, a_big]),
&snap_with(vec![b_small, b_big]),
&CompareOptions::default(),
);
let run_rows: Vec<&DiffRow> = diff
.rows
.iter()
.filter(|r| r.metric_name == "run_time_ns")
.collect();
assert_eq!(run_rows[0].group_key, "big");
assert_eq!(run_rows[1].group_key, "small");
}
#[test]
fn write_diff_renders_auto_scaled_cells_for_ns_metric() {
let mut ta = make_thread("p", "w");
ta.run_time_ns = MonotonicNs(5_000_000); let mut tb = make_thread("p", "w");
tb.run_time_ns = MonotonicNs(8_000_000); let diff = compare(
&snap_with(vec![ta]),
&snap_with(vec![tb]),
&CompareOptions::default(),
);
let mut out = String::new();
write_diff(
&mut out,
&diff,
Path::new("a"),
Path::new("b"),
GroupBy::Pcomm,
&DisplayOptions::default(),
)
.unwrap();
assert!(out.contains("5.000ms"), "missing baseline ms:\n{out}");
assert!(out.contains("8.000ms"), "missing candidate ms:\n{out}");
assert!(out.contains("+3.000ms"), "missing delta ms:\n{out}");
}
#[test]
fn registry_utime_stime_carry_ticks_unit() {
let utime = CTPROF_METRICS
.iter()
.find(|m| m.name == "utime_clock_ticks")
.expect("utime_clock_ticks in registry");
let stime = CTPROF_METRICS
.iter()
.find(|m| m.name == "stime_clock_ticks")
.expect("stime_clock_ticks in registry");
assert_eq!(utime.rule.ladder(), ScaleLadder::Ticks);
assert_eq!(stime.rule.ladder(), ScaleLadder::Ticks);
}
#[test]
fn format_scaled_u64_zero_renders_at_base_unit_for_all_families() {
assert_eq!(format_scaled_u64(0, ScaleLadder::Ns), "0ns");
assert_eq!(format_scaled_u64(0, ScaleLadder::Us), "0µs");
assert_eq!(format_scaled_u64(0, ScaleLadder::Bytes), "0B");
assert_eq!(format_scaled_u64(0, ScaleLadder::Ticks), "0ticks");
assert_eq!(format_scaled_u64(0, ScaleLadder::Unitless), "0");
}
#[test]
fn format_delta_cell_negative_microseconds_scales_to_seconds() {
let cell = format_delta_cell(-1_500_000.0, ScaleLadder::Us);
assert_eq!(cell, "-1.500s");
}
#[test]
fn format_delta_cell_negative_bytes_scales_to_gib() {
let two_gib_neg = -(2.0 * 1024.0 * 1024.0 * 1024.0);
let cell = format_delta_cell(two_gib_neg, ScaleLadder::Bytes);
assert_eq!(cell, "-2.000GiB");
}