use anyhow::Context;
use ktstr::assert::AssertResult;
use ktstr::ktstr_test;
use ktstr::scenario::Ctx;
#[ktstr_test(host_only)]
fn host_mode_sees_real_host_cpus(ctx: &Ctx) -> Result<AssertResult, anyhow::Error> {
let topo_cpus: std::collections::BTreeSet<usize> =
ctx.topo.all_cpus().iter().copied().collect();
let sysfs_online = std::fs::read_to_string("/sys/devices/system/cpu/online")
.context("read /sys/devices/system/cpu/online")?;
let sysfs_cpus =
parse_cpu_list(sysfs_online.trim()).context("parse /sys/devices/system/cpu/online")?;
if sysfs_cpus.len() < 4 {
return Ok(AssertResult::skip(format!(
"sysfs reports {} CPUs (< 4) in this test env (sandboxed \
cargo-ktstr context with narrowed /sys); the from_system \
routing regression check cannot reliably trigger. Run on a \
real-host CI with >= 4 visible sysfs CPUs to gate.",
sysfs_cpus.len(),
)));
}
if topo_cpus == std::collections::BTreeSet::from([0, 1]) && sysfs_cpus.len() > 2 {
return Ok(AssertResult::fail_msg(format!(
"host_only routed through `from_vm_topology` instead of \
`from_system`: ctx.topo.all_cpus() = {{0, 1}} matches \
KtstrTestEntry::DEFAULT.topology (1 LLC × 2 cores × 1 \
thread) exactly while the real host has {} online CPUs.",
sysfs_cpus.len(),
)));
}
let sysfs_set: std::collections::BTreeSet<usize> = sysfs_cpus.iter().copied().collect();
let synth_ids: Vec<usize> = topo_cpus.difference(&sysfs_set).copied().collect();
if !synth_ids.is_empty() {
return Ok(AssertResult::fail_msg(format!(
"ctx.topo.all_cpus() contains IDs {synth_ids:?} not present \
in /sys/devices/system/cpu/online (sysfs_count = {}); \
host_only must derive its topology from real sysfs CPUs \
— synthesized IDs indicate a from_vm_topology routing.",
sysfs_cpus.len(),
)));
}
Ok(AssertResult::pass())
}
fn parse_cpu_list(s: &str) -> anyhow::Result<Vec<usize>> {
use anyhow::anyhow;
let mut cpus = Vec::new();
for range in s.split(',') {
let range = range.trim();
if range.is_empty() {
continue;
}
match range.split_once('-') {
Some((lo, hi)) => {
let lo: usize = lo
.trim()
.parse()
.map_err(|e| anyhow!("cpu-list range lo {lo:?}: {e}"))?;
let hi: usize = hi
.trim()
.parse()
.map_err(|e| anyhow!("cpu-list range hi {hi:?}: {e}"))?;
if hi < lo {
return Err(anyhow!(
"cpu-list range {lo}-{hi}: hi < lo (kernel emits monotonic ranges)"
));
}
for c in lo..=hi {
cpus.push(c);
}
}
None => {
let c: usize = range
.parse()
.map_err(|e| anyhow!("cpu-list singleton {range:?}: {e}"))?;
cpus.push(c);
}
}
}
Ok(cpus)
}
#[ktstr_test(host_only)]
fn host_mode_workers_pct_scales_to_host_cpu_count(
ctx: &Ctx,
) -> Result<AssertResult, anyhow::Error> {
let all_cpus = ctx.topo.all_cpus().len();
let usable = ctx.topo.usable_cpuset().len();
if usable != all_cpus {
return Ok(AssertResult::fail_msg(format!(
"ctx.topo.usable_cpuset().len()={usable} != all_cpus().len()={all_cpus}: \
the operator restricted CPUs via KTSTR_NO_PERF_MODE / KTSTR_CPU_CAP \
or a sched_setaffinity policy, so this test cannot prove that \
workers_pct(1.0) scales to ALL host CPUs. Unset CPU restrictions \
or skip this test."
)));
}
if all_cpus < 4 {
return Ok(AssertResult::skip(format!(
"ctx.topo reports {all_cpus} CPUs (< 4) in this test env \
(sandboxed cargo-ktstr context with narrowed \
sched_getaffinity); the workers_pct scaling check cannot \
reliably trigger. Run on a real-host CI with >= 4 visible \
CPUs to gate."
)));
}
Ok(AssertResult::pass())
}