#![cfg(test)]
use super::*;
#[cfg(target_os = "linux")]
#[test]
fn collect_host_context_returns_populated_struct_on_linux() {
let ctx = collect_host_context();
assert_eq!(ctx.kernel_name.as_deref(), Some("Linux"));
assert!(ctx.kernel_release.is_some(), "uname release present");
assert!(ctx.arch.is_some(), "uname machine present");
}
#[cfg(target_os = "linux")]
#[test]
fn collect_host_context_captures_cmdline_on_linux() {
let ctx = collect_host_context();
let cmdline = ctx
.kernel_cmdline
.as_deref()
.expect("/proc/cmdline is always readable on a running Linux system");
assert!(
!cmdline.is_empty(),
"populated kernel_cmdline must not be empty"
);
assert_eq!(cmdline, cmdline.trim());
}
#[cfg(target_os = "linux")]
#[test]
fn collect_host_context_static_subset_is_stable_across_calls() {
let a = collect_host_context();
let b = collect_host_context();
assert_eq!(a.kernel_name, b.kernel_name);
assert_eq!(a.kernel_release, b.kernel_release);
assert_eq!(a.arch, b.arch);
assert_eq!(a.cpu_model, b.cpu_model);
assert_eq!(a.cpu_vendor, b.cpu_vendor);
assert_eq!(a.total_memory_kib, b.total_memory_kib);
assert_eq!(a.hugepages_size_kib, b.hugepages_size_kib);
assert_eq!(a.online_cpus, b.online_cpus);
assert_eq!(a.numa_nodes, b.numa_nodes);
assert_eq!(a.cpufreq_governor, b.cpufreq_governor);
}
#[cfg(target_os = "linux")]
#[test]
fn collect_host_context_dynamic_subset_is_stable_across_calls() {
let a = collect_host_context();
let b = collect_host_context();
assert_eq!(a.kernel_cmdline, b.kernel_cmdline);
assert_eq!(a.hugepages_total.is_some(), b.hugepages_total.is_some());
assert_eq!(a.hugepages_free.is_some(), b.hugepages_free.is_some());
assert_eq!(a.thp_enabled.is_some(), b.thp_enabled.is_some());
assert_eq!(a.thp_defrag.is_some(), b.thp_defrag.is_some());
assert_eq!(a.sched_tunables.is_some(), b.sched_tunables.is_some());
}
#[cfg(target_os = "linux")]
#[test]
fn static_host_info_is_cached_after_first_call() {
let _ = collect_host_context();
let first = STATIC_HOST_INFO
.get()
.expect("STATIC_HOST_INFO must be populated after collect_host_context");
let first_ptr = first as *const StaticHostInfo;
let _ = collect_host_context();
let second = STATIC_HOST_INFO
.get()
.expect("STATIC_HOST_INFO must still be populated on second call");
let second_ptr = second as *const StaticHostInfo;
assert_eq!(
first_ptr, second_ptr,
"OnceLock must return the same allocation across calls — \
a pointer mismatch means the cache re-initialized, \
defeating the get_or_init contract",
);
assert_eq!(first.cpu_model, second.cpu_model);
assert_eq!(first.kernel_release, second.kernel_release);
assert_eq!(first.total_memory_kib, second.total_memory_kib);
}
#[test]
fn host_context_empty_round_trips_via_json() {
let empty = HostContext::default();
let json = serde_json::to_string(&empty).expect("serialize empty");
assert_eq!(
json, "{}",
"default host context must serialize to empty object"
);
let decoded: HostContext = serde_json::from_str(&json).expect("deserialize empty");
assert!(decoded.cpu_model.is_none());
assert!(decoded.kernel_name.is_none());
assert!(decoded.kernel_cmdline.is_none());
}
#[test]
fn host_context_populated_round_trips_via_json() {
let mut tunables = BTreeMap::new();
tunables.insert("sched_migration_cost_ns".to_string(), "500000".to_string());
let ctx = HostContext {
cpu_model: Some("Example CPU".to_string()),
cpu_vendor: Some("GenuineExample".to_string()),
total_memory_kib: Some(16_384_000),
hugepages_total: Some(0),
hugepages_free: Some(0),
hugepages_size_kib: Some(2048),
thp_enabled: Some("always [madvise] never".to_string()),
thp_defrag: Some("[always] defer defer+madvise madvise never".to_string()),
sched_tunables: Some(tunables),
online_cpus: Some(16),
numa_nodes: Some(2),
cpufreq_governor: BTreeMap::new(),
kernel_name: Some("Linux".to_string()),
kernel_release: Some("6.11.0".to_string()),
arch: Some("x86_64".to_string()),
kernel_cmdline: Some("preempt=lazy transparent_hugepage=madvise".to_string()),
heap_state: Some(crate::host_heap::HostHeapState::test_fixture()),
};
let json = serde_json::to_string(&ctx).expect("serialize");
let decoded: HostContext = serde_json::from_str(&json).expect("deserialize");
assert_eq!(decoded, ctx);
}
#[test]
fn host_context_partial_none_round_trips_via_json() {
let ctx = HostContext {
kernel_name: Some("Linux".to_string()),
kernel_release: None,
arch: Some("x86_64".to_string()),
sched_tunables: Some(BTreeMap::new()),
cpu_model: None,
cpu_vendor: None,
total_memory_kib: None,
hugepages_total: None,
hugepages_free: None,
hugepages_size_kib: None,
thp_enabled: None,
thp_defrag: None,
online_cpus: None,
numa_nodes: None,
cpufreq_governor: BTreeMap::new(),
kernel_cmdline: None,
heap_state: None,
};
let json = serde_json::to_string(&ctx).expect("serialize");
let decoded: HostContext = serde_json::from_str(&json).expect("deserialize");
assert_eq!(decoded, ctx);
}
#[test]
fn parse_cpuinfo_identity_happy_path() {
let text = "\
processor\t: 0
vendor_id\t: GenuineIntel
cpu family\t: 6
model\t\t: 85
model name\t: Intel(R) Xeon(R) Gold 6138 CPU @ 2.00GHz
stepping\t: 4
";
let (model, vendor) = parse_cpuinfo_identity(text);
assert_eq!(
model.as_deref(),
Some("Intel(R) Xeon(R) Gold 6138 CPU @ 2.00GHz"),
);
assert_eq!(vendor.as_deref(), Some("GenuineIntel"));
}
#[test]
fn parse_cpuinfo_identity_empty_input() {
let (model, vendor) = parse_cpuinfo_identity("");
assert!(model.is_none());
assert!(vendor.is_none());
}
#[test]
fn parse_cpuinfo_identity_arm64_no_model_or_vendor() {
let text = "\
processor\t: 0
BogoMIPS\t: 50.00
Features\t: fp asimd evtstrm aes pmull sha1 sha2 crc32
CPU implementer\t: 0x41
CPU architecture: 8
CPU variant\t: 0x3
CPU part\t: 0xd0c
CPU revision\t: 1
";
let (model, vendor) = parse_cpuinfo_identity(text);
assert!(model.is_none(), "no 'model name' line on ARM64");
assert!(vendor.is_none(), "no 'vendor_id' line on ARM64");
}
#[test]
fn parse_cpuinfo_identity_malformed_lines_are_skipped() {
let text = "\
nonsense line with no colon
vendor_id\t:
model name\t: Actual Model Name
vendor_id\t: ActualVendor
";
let (model, vendor) = parse_cpuinfo_identity(text);
assert_eq!(model.as_deref(), Some("Actual Model Name"));
assert_eq!(
vendor.as_deref(),
Some("ActualVendor"),
"empty vendor line must not poison — next real value wins",
);
}
#[test]
fn parse_cpuinfo_identity_crlf_line_endings() {
let text = "vendor_id\t: GenuineIntel\r\nmodel name\t: Some CPU\r\n";
let (model, vendor) = parse_cpuinfo_identity(text);
assert_eq!(model.as_deref(), Some("Some CPU"));
assert_eq!(vendor.as_deref(), Some("GenuineIntel"));
}
#[test]
fn parse_cpuinfo_identity_first_processor_only() {
let text = "\
processor\t: 0
vendor_id\t: GenuineIntel
model name\t: First CPU
processor\t: 1
vendor_id\t: DifferentVendor
model name\t: Second CPU
";
let (model, vendor) = parse_cpuinfo_identity(text);
assert_eq!(model.as_deref(), Some("First CPU"));
assert_eq!(vendor.as_deref(), Some("GenuineIntel"));
}
#[test]
fn parse_meminfo_happy_path() {
let text = "\
MemTotal: 16384000 kB
MemFree: 8000000 kB
HugePages_Total: 42
HugePages_Free: 40
Hugepagesize: 2048 kB
";
let out = parse_meminfo(text);
assert_eq!(out.mem_total_kib, Some(16_384_000));
assert_eq!(out.hugepages_total, Some(42));
assert_eq!(out.hugepages_free, Some(40));
assert_eq!(out.hugepages_size_kib, Some(2048));
}
#[test]
fn parse_meminfo_empty_input() {
let out = parse_meminfo("");
assert!(out.mem_total_kib.is_none());
assert!(out.hugepages_total.is_none());
assert!(out.hugepages_free.is_none());
assert!(out.hugepages_size_kib.is_none());
}
#[test]
fn parse_meminfo_missing_fields_stay_none() {
let text = "MemTotal: 1024 kB\nMemFree: 512 kB\n";
let out = parse_meminfo(text);
assert_eq!(out.mem_total_kib, Some(1024));
assert!(out.hugepages_total.is_none());
assert!(out.hugepages_free.is_none());
assert!(out.hugepages_size_kib.is_none());
}
#[test]
fn parse_meminfo_non_numeric_value_skipped() {
let text = "\
MemTotal: 2048 kB
SomeFlags: abc def ghi
Hugepagesize: 2048 kB
";
let out = parse_meminfo(text);
assert_eq!(out.mem_total_kib, Some(2048));
assert_eq!(out.hugepages_size_kib, Some(2048));
}
#[test]
fn parse_meminfo_unknown_fields_tolerated() {
let text = "\
MemTotal: 100 kB
Unknown_Field: 999
HugePages_Total: 3
Another_Unknown: 77 kB
";
let out = parse_meminfo(text);
assert_eq!(out.mem_total_kib, Some(100));
assert_eq!(out.hugepages_total, Some(3));
assert!(out.hugepages_free.is_none());
}
#[test]
fn parse_meminfo_crlf_line_endings() {
let text = "MemTotal: 512 kB\r\nHugePages_Total: 2\r\nHugepagesize: 2048 kB\r\n";
let out = parse_meminfo(text);
assert_eq!(out.mem_total_kib, Some(512));
assert_eq!(out.hugepages_total, Some(2));
assert_eq!(out.hugepages_size_kib, Some(2048));
}
#[test]
fn parse_cpuinfo_identity_duplicate_key_first_wins() {
let text = "\
vendor_id\t: FirstVendor
model name\t: First Model
vendor_id\t: SecondVendor
model name\t: Second Model
";
let (model, vendor) = parse_cpuinfo_identity(text);
assert_eq!(model.as_deref(), Some("First Model"));
assert_eq!(vendor.as_deref(), Some("FirstVendor"));
}
#[test]
fn parse_cpuinfo_identity_value_with_internal_colon() {
let text = "model name\t: Intel(R): Xeon(R) CPU @ 2.00GHz\n";
let (model, _vendor) = parse_cpuinfo_identity(text);
assert_eq!(
model.as_deref(),
Some("Intel(R): Xeon(R) CPU @ 2.00GHz"),
"internal ':' must be preserved in the value",
);
}
#[test]
fn parse_cpuinfo_identity_leading_blank_line() {
let text = "\nvendor_id\t: GenuineIntel\nmodel name\t: Some CPU\n";
let (model, vendor) = parse_cpuinfo_identity(text);
assert!(model.is_none(), "leading blank line must short-circuit");
assert!(vendor.is_none(), "leading blank line must short-circuit");
}
#[test]
fn parse_meminfo_duplicate_key_last_wins() {
let text = "MemTotal: 100 kB\nMemTotal: 200 kB\n";
let out = parse_meminfo(text);
assert_eq!(out.mem_total_kib, Some(200));
}
#[test]
fn parse_meminfo_line_without_colon() {
let text = "\
garbage line without any colon
MemTotal: 100 kB
another garbage line
HugePages_Total: 3
";
let out = parse_meminfo(text);
assert_eq!(out.mem_total_kib, Some(100));
assert_eq!(out.hugepages_total, Some(3));
}
#[test]
fn parse_meminfo_empty_value_after_colon() {
let text = "MemTotal:\nHugePages_Total: 5\n";
let out = parse_meminfo(text);
assert!(
out.mem_total_kib.is_none(),
"empty value after ':' must leave the field None",
);
assert_eq!(
out.hugepages_total,
Some(5),
"subsequent valid lines must still parse",
);
}
#[test]
fn parse_meminfo_negative_and_overflow_value_skipped() {
let text = "\
MemTotal: -1 kB
HugePages_Total: 99999999999999999999999
Hugepagesize: 2048 kB
";
let out = parse_meminfo(text);
assert!(
out.mem_total_kib.is_none(),
"negative value must fail u64 parse and skip",
);
assert!(
out.hugepages_total.is_none(),
"overflow value must fail u64 parse and skip",
);
assert_eq!(
out.hugepages_size_kib,
Some(2048),
"later valid line must still parse",
);
}
#[test]
fn parse_trimmed_empty_is_none() {
assert!(parse_trimmed("").is_none());
}
#[test]
fn parse_trimmed_whitespace_only_is_none() {
assert!(parse_trimmed(" \n\t \r\n").is_none());
}
#[test]
fn parse_trimmed_strips_trailing_newline() {
assert_eq!(parse_trimmed("content\n").as_deref(), Some("content"));
}
#[test]
fn parse_trimmed_preserves_bracketed_thp() {
assert_eq!(
parse_trimmed("always [madvise] never\n").as_deref(),
Some("always [madvise] never"),
);
}
const HOST_CONTEXT_FIELDS: &[&str] = &[
"kernel_name",
"kernel_release",
"arch",
"cpu_model",
"cpu_vendor",
"total_memory_kib",
"hugepages_total",
"hugepages_free",
"hugepages_size_kib",
"online_cpus",
"numa_nodes",
"thp_enabled",
"thp_defrag",
"kernel_cmdline",
"cpufreq_governor",
"sched_tunables",
"heap_state",
];
fn drop_to_unit<T>(_: T) {}
#[allow(dead_code)]
fn struct_field_array(ctx: HostContext) -> [(); HOST_CONTEXT_FIELDS.len()] {
let HostContext {
cpu_model,
cpu_vendor,
total_memory_kib,
hugepages_total,
hugepages_free,
hugepages_size_kib,
thp_enabled,
thp_defrag,
sched_tunables,
online_cpus,
numa_nodes,
cpufreq_governor,
kernel_name,
kernel_release,
arch,
kernel_cmdline,
heap_state,
} = ctx;
[
drop_to_unit(cpu_model),
drop_to_unit(cpu_vendor),
drop_to_unit(total_memory_kib),
drop_to_unit(hugepages_total),
drop_to_unit(hugepages_free),
drop_to_unit(hugepages_size_kib),
drop_to_unit(thp_enabled),
drop_to_unit(thp_defrag),
drop_to_unit(sched_tunables),
drop_to_unit(online_cpus),
drop_to_unit(numa_nodes),
drop_to_unit(cpufreq_governor),
drop_to_unit(kernel_name),
drop_to_unit(kernel_release),
drop_to_unit(arch),
drop_to_unit(kernel_cmdline),
drop_to_unit(heap_state),
]
}
#[allow(dead_code)]
const _HOST_CONTEXT_FIELD_COUNT_PIN: () = {
assert!(
HOST_CONTEXT_FIELDS.len() == 17,
"HOST_CONTEXT_FIELDS cardinality drifted from the \
HostContext struct — if a field was added, extend \
HOST_CONTEXT_FIELDS, struct_field_array's destructure, \
and struct_field_array's initializer together; then \
bump this literal from 17 to the new field count",
);
};
#[test]
fn format_human_renders_every_documented_field() {
let out = HostContext::default().format_human();
for key in HOST_CONTEXT_FIELDS {
assert!(
out.contains(&format!("{key}:")),
"field '{key}' is declared in HOST_CONTEXT_FIELDS but does \
not appear in format_human output — either the `row()` \
call was forgotten or the field name drifted:\n{out}",
);
}
}
#[test]
fn diff_renders_every_documented_field() {
let a = HostContext::default();
let heap = crate::host_heap::HostHeapState {
active_bytes: Some(1),
allocated_bytes: Some(2),
resident_bytes: Some(3),
mapped_bytes: Some(4),
narenas: Some(1),
};
let mut tunables = BTreeMap::new();
tunables.insert("sched_migration_cost_ns".to_string(), "500000".to_string());
let mut b = HostContext {
kernel_name: Some("Linux".to_string()),
kernel_release: Some("6.11.0".to_string()),
arch: Some("x86_64".to_string()),
cpu_model: Some("Example CPU".to_string()),
cpu_vendor: Some("GenuineIntel".to_string()),
total_memory_kib: Some(16_384_000),
hugepages_total: Some(0),
hugepages_free: Some(0),
hugepages_size_kib: Some(2048),
online_cpus: Some(8),
numa_nodes: Some(1),
thp_enabled: Some("always [madvise] never".to_string()),
thp_defrag: Some("always [madvise] never".to_string()),
kernel_cmdline: Some("preempt=lazy".to_string()),
sched_tunables: Some(tunables),
heap_state: Some(heap),
..Default::default()
};
b.cpufreq_governor.insert(0, "performance".to_string());
let out = a.diff(&b);
for key in HOST_CONTEXT_FIELDS {
let direct = format!("{key}:");
let dotted = format!("{key}.");
assert!(
out.contains(&direct) || out.contains(&dotted),
"field '{key}' is declared in HOST_CONTEXT_FIELDS but does \
not appear (as '{direct}' or '{dotted}') in diff output \
against a fully-populated partner — either the per-field \
row was forgotten or the field name drifted:\n{out}",
);
}
}
#[test]
fn format_human_field_order_is_stable() {
let out = HostContext::default().format_human();
let labels: Vec<&str> = out
.lines()
.filter_map(|l| l.split(':').next())
.filter(|s| !s.starts_with(' '))
.collect();
assert_eq!(
labels,
vec![
"kernel_name",
"kernel_release",
"arch",
"cpu_model",
"cpu_vendor",
"total_memory_kib",
"hugepages_total",
"hugepages_free",
"hugepages_size_kib",
"online_cpus",
"numa_nodes",
"thp_enabled",
"thp_defrag",
"kernel_cmdline",
"cpufreq_governor",
"sched_tunables",
"heap_state",
],
"format_human field order drifted — if intentional, update \
the expected vector and audit downstream diff/scan consumers",
);
}
#[test]
fn format_human_default_renders_unknown_everywhere() {
let out = HostContext::default().format_human();
for key in [
"kernel_name",
"kernel_release",
"arch",
"cpu_model",
"cpu_vendor",
"total_memory_kib",
"hugepages_total",
"hugepages_free",
"hugepages_size_kib",
"online_cpus",
"numa_nodes",
"thp_enabled",
"thp_defrag",
"kernel_cmdline",
"sched_tunables",
"heap_state",
] {
assert!(
out.contains(&format!("{key}: (unknown)")),
"key '{key}' must render as (unknown) on a default context, got:\n{out}",
);
}
assert!(
out.contains("cpufreq_governor: (empty)"),
"cpufreq_governor must render as (empty) on default context, got:\n{out}",
);
assert!(
out.ends_with('\n'),
"format_human must end with a newline for direct print!() use",
);
}
#[test]
fn format_human_populated_shows_values_and_tunables() {
let mut tunables = BTreeMap::new();
tunables.insert("sched_migration_cost_ns".to_string(), "500000".to_string());
tunables.insert("sched_min_granularity_ns".to_string(), "750000".to_string());
let ctx = HostContext {
kernel_name: Some("Linux".to_string()),
kernel_release: Some("6.11.0".to_string()),
arch: Some("x86_64".to_string()),
cpu_model: Some("Example CPU".to_string()),
total_memory_kib: Some(16_384_000),
sched_tunables: Some(tunables),
kernel_cmdline: Some("preempt=lazy".to_string()),
..HostContext::default()
};
let out = ctx.format_human();
assert!(out.contains("kernel_name: Linux"), "{out}");
assert!(out.contains("kernel_release: 6.11.0"), "{out}");
assert!(out.contains("cpu_model: Example CPU"), "{out}");
assert!(out.contains("total_memory_kib: 16384000"), "{out}");
assert!(out.contains("kernel_cmdline: preempt=lazy"), "{out}");
assert!(out.contains("sched_tunables:\n"), "{out}");
assert!(out.contains(" sched_migration_cost_ns = 500000"), "{out}");
assert!(out.contains(" sched_min_granularity_ns = 750000"), "{out}");
assert!(out.contains("cpu_vendor: (unknown)"), "{out}");
assert!(
out.ends_with('\n'),
"format_human output must terminate with a newline so the \
next line the operator sees sits on its own row: {out:?}",
);
}
#[test]
fn format_human_sched_tunables_empty_vs_none() {
let mut ctx = HostContext {
sched_tunables: Some(BTreeMap::new()),
..Default::default()
};
let out_empty = ctx.format_human();
assert!(
out_empty.contains("sched_tunables: (empty)"),
"empty map must render distinctly from None: {out_empty}",
);
assert!(
out_empty.ends_with('\n'),
"format_human with empty tunables must still end with a \
newline: {out_empty:?}",
);
ctx.sched_tunables = None;
let out_none = ctx.format_human();
assert!(
out_none.contains("sched_tunables: (unknown)"),
"None map must render as (unknown): {out_none}",
);
assert!(
out_none.ends_with('\n'),
"format_human with no tunables must still end with a \
newline: {out_none:?}",
);
}
#[test]
fn diff_identical_is_empty() {
let ctx = HostContext {
kernel_name: Some("Linux".to_string()),
cpu_model: Some("Example CPU".to_string()),
..HostContext::default()
};
assert_eq!(ctx.diff(&ctx), "");
}
#[test]
fn diff_single_field_surfaces_only_that_field() {
let a = HostContext {
kernel_cmdline: Some("preempt=lazy".to_string()),
kernel_release: Some("6.11.0".to_string()),
..HostContext::default()
};
let b = HostContext {
kernel_cmdline: Some("preempt=full".to_string()),
kernel_release: Some("6.11.0".to_string()),
..HostContext::default()
};
let out = a.diff(&b);
assert!(
out.contains("kernel_cmdline: preempt=lazy → preempt=full"),
"kernel_cmdline change must appear: {out}",
);
assert!(
!out.contains("kernel_release"),
"unchanged kernel_release must not appear: {out}",
);
}
#[test]
fn diff_cpufreq_governor_both_empty_produces_no_lines() {
let a = HostContext::default();
let b = HostContext::default();
let out = a.diff(&b);
assert!(
!out.contains("cpufreq_governor"),
"two empty cpufreq_governor maps must not emit any \
diff lines: {out}",
);
}
#[test]
fn diff_cpufreq_governor_cpu_only_in_a_shows_absent() {
let mut a_gov = BTreeMap::new();
a_gov.insert(0, "performance".to_string());
let a = HostContext {
cpufreq_governor: a_gov,
..HostContext::default()
};
let b = HostContext::default();
let out = a.diff(&b);
assert!(
out.contains("cpufreq_governor.cpu0: performance → (absent)"),
"cpu0 removed must render as <value> → (absent): {out}",
);
}
#[test]
fn diff_cpufreq_governor_value_change_shows_old_arrow_new() {
let mut a_gov = BTreeMap::new();
a_gov.insert(0, "performance".to_string());
a_gov.insert(1, "powersave".to_string());
let mut b_gov = BTreeMap::new();
b_gov.insert(0, "schedutil".to_string());
b_gov.insert(1, "powersave".to_string());
let a = HostContext {
cpufreq_governor: a_gov,
..HostContext::default()
};
let b = HostContext {
cpufreq_governor: b_gov,
..HostContext::default()
};
let out = a.diff(&b);
assert!(
out.contains("cpufreq_governor.cpu0: performance → schedutil"),
"cpu0 change must appear as old → new: {out}",
);
assert!(
!out.contains("cpufreq_governor.cpu1"),
"unchanged cpu1 (both powersave) must not appear: {out}",
);
}
#[cfg(target_os = "linux")]
#[test]
fn read_cpufreq_governors_returns_populated_map_when_sysfs_exposes_it() {
use std::path::Path;
let cpu0_gov = Path::new("/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor");
if !cpu0_gov.exists() {
eprintln!(
"skipping: /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor \
absent (kernel without CONFIG_CPU_FREQ or VM without passthrough)"
);
return;
}
let m = read_cpufreq_governors();
assert!(
!m.is_empty(),
"cpu0 scaling_governor is present on-disk — map must be \
non-empty; got {m:?}"
);
for (cpu, gov) in &m {
assert!(
!gov.is_empty(),
"cpu{cpu} governor string is empty after trim; sysfs \
usually writes non-empty content",
);
}
}
#[test]
fn diff_none_to_some_shows_unknown_arrow() {
let a = HostContext::default();
let b = HostContext {
kernel_name: Some("Linux".to_string()),
..HostContext::default()
};
let out = a.diff(&b);
assert!(
out.contains("kernel_name: (unknown) → Linux"),
"(unknown) → Linux must appear: {out}",
);
}
#[test]
fn diff_sched_tunables_per_key() {
let mut am = BTreeMap::new();
am.insert("sched_a".to_string(), "1".to_string());
am.insert("sched_b".to_string(), "old".to_string());
let mut bm = BTreeMap::new();
bm.insert("sched_a".to_string(), "1".to_string());
bm.insert("sched_b".to_string(), "new".to_string());
bm.insert("sched_c".to_string(), "3".to_string());
let a = HostContext {
sched_tunables: Some(am),
..HostContext::default()
};
let b = HostContext {
sched_tunables: Some(bm),
..HostContext::default()
};
let out = a.diff(&b);
assert!(
!out.contains("sched_tunables.sched_a"),
"unchanged sched_a must not appear: {out}",
);
assert!(
out.contains("sched_tunables.sched_b: old → new"),
"changed sched_b must appear: {out}",
);
assert!(
out.contains("sched_tunables.sched_c: (absent) → 3"),
"new key sched_c must appear as (absent) → 3: {out}",
);
}
#[test]
fn diff_sched_tunables_none_vs_some() {
let mut m = BTreeMap::new();
m.insert("sched_x".to_string(), "1".to_string());
let a = HostContext::default();
let b = HostContext {
sched_tunables: Some(m),
..HostContext::default()
};
let out = a.diff(&b);
assert!(
out.contains("sched_tunables: (unknown) → (1 entry)"),
"None → Some(1 entry) must surface cardinality: {out}",
);
}
#[test]
fn diff_some_to_none_shows_arrow_unknown() {
let a = HostContext {
kernel_release: Some("6.11.0".to_string()),
..HostContext::default()
};
let b = HostContext::default();
let out = a.diff(&b);
assert!(
out.contains("kernel_release: 6.11.0 → (unknown)"),
"Some → None must surface as <value> → (unknown): {out}",
);
}
#[test]
fn diff_sched_tunables_key_removed() {
let mut am = BTreeMap::new();
am.insert("sched_a".to_string(), "1".to_string());
am.insert("sched_b".to_string(), "2".to_string());
let mut bm = BTreeMap::new();
bm.insert("sched_a".to_string(), "1".to_string());
let a = HostContext {
sched_tunables: Some(am),
..HostContext::default()
};
let b = HostContext {
sched_tunables: Some(bm),
..HostContext::default()
};
let out = a.diff(&b);
assert!(
!out.contains("sched_tunables.sched_a"),
"unchanged sched_a must not appear: {out}",
);
assert!(
out.contains("sched_tunables.sched_b: 2 → (absent)"),
"removed sched_b must surface as <value> → (absent): {out}",
);
}
#[test]
fn read_trimmed_sysfs_missing_file_returns_none() {
let scratch = tempfile::TempDir::new().expect("create scratch temp dir");
let missing = scratch.path().join("nonexistent-target");
assert!(read_trimmed_sysfs(&missing).is_none());
}
#[test]
fn read_trimmed_sysfs_whitespace_only_returns_none() {
let mut f = tempfile::NamedTempFile::new().expect("create tempfile");
std::io::Write::write_all(&mut f, b" \n\t \r\n ").expect("write whitespace");
assert!(read_trimmed_sysfs(f.path()).is_none());
}
#[test]
fn read_trimmed_sysfs_populated_file_returns_trimmed_content() {
let mut f = tempfile::NamedTempFile::new().expect("create tempfile");
std::io::Write::write_all(&mut f, b"madvise\n").expect("write content");
assert_eq!(read_trimmed_sysfs(f.path()).as_deref(), Some("madvise"));
}
#[test]
fn read_trimmed_sysfs_preserves_thp_bracket_selection() {
let mut f = tempfile::NamedTempFile::new().expect("create tempfile");
std::io::Write::write_all(&mut f, b"always [madvise] never\n").expect("write");
assert_eq!(
read_trimmed_sysfs(f.path()).as_deref(),
Some("always [madvise] never"),
);
}
#[test]
fn read_sched_tunables_from_filters_and_trims() {
let tmp = tempfile::TempDir::new().expect("create tempdir");
let dir = tmp.path();
std::fs::write(dir.join("sched_foo"), b"42\n").expect("write sched_foo");
std::fs::write(dir.join("sched_bar"), b"1\n").expect("write sched_bar");
std::fs::write(dir.join("not_sched_baz"), b"99\n").expect("write not_sched_baz");
std::fs::create_dir(dir.join("sched_subdir")).expect("create sched_subdir");
let out = read_sched_tunables_from(dir).expect("walk must succeed on readable dir");
assert_eq!(out.len(), 2, "expected only two sched_* files, got {out:?}");
assert_eq!(out.get("sched_foo").map(String::as_str), Some("42"));
assert_eq!(out.get("sched_bar").map(String::as_str), Some("1"));
assert!(
!out.contains_key("not_sched_baz"),
"non-sched_ prefix must be filtered out"
);
assert!(
!out.contains_key("sched_subdir"),
"subdirectories must be filtered by is_file"
);
}
#[test]
fn count_numa_nodes_in_topology_empty_returns_one() {
let topo = crate::vmm::host_topology::HostTopology {
llc_groups: Vec::new(),
online_cpus: Vec::new(),
cpu_to_node: std::collections::HashMap::new(),
host_node_llcs: std::collections::BTreeMap::new(),
};
assert_eq!(count_numa_nodes_in_topology(&topo), 1);
}
#[test]
fn count_numa_nodes_in_topology_single_node() {
let mut cpu_to_node = std::collections::HashMap::new();
for cpu in 0..8 {
cpu_to_node.insert(cpu, 0);
}
let topo = crate::vmm::host_topology::HostTopology {
llc_groups: Vec::new(),
online_cpus: (0..8).collect(),
cpu_to_node,
host_node_llcs: std::collections::BTreeMap::new(),
};
assert_eq!(count_numa_nodes_in_topology(&topo), 1);
}
#[test]
fn count_numa_nodes_in_topology_two_nodes() {
let mut cpu_to_node = std::collections::HashMap::new();
for cpu in 0..4 {
cpu_to_node.insert(cpu, 0);
}
for cpu in 4..8 {
cpu_to_node.insert(cpu, 1);
}
let topo = crate::vmm::host_topology::HostTopology {
llc_groups: Vec::new(),
online_cpus: (0..8).collect(),
cpu_to_node,
host_node_llcs: std::collections::BTreeMap::new(),
};
assert_eq!(count_numa_nodes_in_topology(&topo), 2);
}
#[test]
fn count_numa_nodes_in_topology_sparse_ids() {
let mut cpu_to_node = std::collections::HashMap::new();
cpu_to_node.insert(0, 0);
cpu_to_node.insert(1, 2);
cpu_to_node.insert(2, 5);
cpu_to_node.insert(3, 0); let topo = crate::vmm::host_topology::HostTopology {
llc_groups: Vec::new(),
online_cpus: vec![0, 1, 2, 3],
cpu_to_node,
host_node_llcs: std::collections::BTreeMap::new(),
};
assert_eq!(
count_numa_nodes_in_topology(&topo),
3,
"sparse IDs {{0, 2, 5}} must count as 3, not max_id+1",
);
}
#[cfg(target_os = "linux")]
#[test]
fn collect_host_context_call_counts_match_caching_invariants() {
use std::sync::atomic::Ordering;
const N: usize = 5;
let static_was_populated_pre = STATIC_HOST_INFO.get().is_some();
let cpufreq_was_populated_pre = CPUFREQ_GOVERNORS.get().is_some();
let init_before = STATIC_INIT_CALLS.load(Ordering::Relaxed);
let meminfo_before = MEMINFO_READ_CALLS.load(Ordering::Relaxed);
let cpufreq_before = CPUFREQ_GOVERNORS_READ_CALLS.load(Ordering::Relaxed);
for _ in 0..N {
let _ = collect_host_context();
}
let init_delta = STATIC_INIT_CALLS.load(Ordering::Relaxed) - init_before;
let meminfo_delta = MEMINFO_READ_CALLS.load(Ordering::Relaxed) - meminfo_before;
let cpufreq_delta = CPUFREQ_GOVERNORS_READ_CALLS.load(Ordering::Relaxed) - cpufreq_before;
assert!(
init_delta <= 1,
"compute_static_host_info must run at most once across {N} collect_host_context calls, ran {init_delta}",
);
assert_eq!(
meminfo_delta, N,
"read_meminfo must run exactly {N} times across {N} collect_host_context calls, ran {meminfo_delta} — the dedup would regress if this trips",
);
assert!(
cpufreq_delta <= 1,
"read_cpufreq_governors must run at most once across {N} collect_host_context calls, ran {cpufreq_delta} — a regression that bypassed the CPUFREQ_GOVERNORS cache would trip this",
);
if !static_was_populated_pre {
assert_eq!(
init_delta, 1,
"cold-init anchor: compute_static_host_info must run exactly once on the populate path, not {init_delta}",
);
}
if !cpufreq_was_populated_pre {
assert_eq!(
cpufreq_delta, 1,
"cold-init anchor: read_cpufreq_governors must run exactly once on the populate path, not {cpufreq_delta}",
);
}
assert!(
STATIC_HOST_INFO.get().is_some(),
"STATIC_HOST_INFO must be populated after at least one collect_host_context call",
);
assert!(
CPUFREQ_GOVERNORS.get().is_some(),
"CPUFREQ_GOVERNORS must be populated after at least one collect_host_context call",
);
}
#[test]
fn count_numa_nodes_in_topology_excludes_memory_only_nodes() {
let mut cpu_to_node = std::collections::HashMap::new();
cpu_to_node.insert(0, 0);
cpu_to_node.insert(1, 0);
cpu_to_node.insert(2, 1);
cpu_to_node.insert(3, 1);
let topo = crate::vmm::host_topology::HostTopology {
llc_groups: Vec::new(),
online_cpus: vec![0, 1, 2, 3],
cpu_to_node,
host_node_llcs: std::collections::BTreeMap::new(),
};
assert_eq!(
count_numa_nodes_in_topology(&topo),
2,
"memory-only nodes must not inflate the CPU-bearing node count",
);
}
#[test]
fn parse_bracketed_active_policy_middle_selection() {
assert_eq!(
parse_bracketed_active_policy("always [madvise] never"),
Some("madvise"),
);
}
#[test]
fn parse_bracketed_active_policy_leading_selection() {
assert_eq!(
parse_bracketed_active_policy("[always] madvise never"),
Some("always"),
);
}
#[test]
fn parse_bracketed_active_policy_trailing_selection() {
assert_eq!(
parse_bracketed_active_policy("always madvise [never]"),
Some("never"),
);
}
#[test]
fn parse_bracketed_active_policy_thp_defrag_hyphenated() {
assert_eq!(
parse_bracketed_active_policy("always defer [defer+madvise] madvise never",),
Some("defer+madvise"),
);
}
#[test]
fn parse_bracketed_active_policy_no_brackets_is_none() {
assert_eq!(parse_bracketed_active_policy("always madvise never"), None);
}
#[test]
fn parse_bracketed_active_policy_empty_is_none() {
assert_eq!(parse_bracketed_active_policy(""), None);
}
#[test]
fn parse_bracketed_active_policy_unclosed_bracket_is_none() {
assert_eq!(parse_bracketed_active_policy("always [madvise never"), None);
}
#[test]
fn parse_bracketed_active_policy_unopened_bracket_is_none() {
assert_eq!(parse_bracketed_active_policy("always madvise] never"), None);
}
#[test]
fn parse_bracketed_active_policy_multiple_pairs_first_wins() {
assert_eq!(
parse_bracketed_active_policy("[always] [never]"),
Some("always"),
);
}
#[test]
fn parse_bracketed_active_policy_nested_brackets_truncate_at_inner_close() {
assert_eq!(
parse_bracketed_active_policy("[a[b]c]"),
Some("a[b"),
"nested-bracket fixture must truncate at the first inner `]`",
);
assert_eq!(
parse_bracketed_active_policy("[a[b] c"),
Some("a[b"),
"unpaired nest must still close at the first inner `]`",
);
assert_eq!(
parse_bracketed_active_policy("prefix [lit] then [active] tail"),
Some("lit"),
"first-bracket-wins overrides any later 'real' active token",
);
}
#[test]
fn host_context_thp_active_methods_extract_bracketed_choice() {
let mut ctx = HostContext::test_fixture();
assert_eq!(ctx.thp_enabled_active(), Some("madvise"));
assert_eq!(ctx.thp_defrag_active(), Some("madvise"));
ctx.thp_enabled = None;
assert_eq!(ctx.thp_enabled_active(), None);
ctx.thp_defrag = Some("no brackets here".to_string());
assert_eq!(ctx.thp_defrag_active(), None);
}