#![cfg(unix)]
use std::path::Path;
use gander_core::linux::system::Sampler;
use gander_core::PlatformSampler;
#[cfg(target_os = "linux")]
use gander_core::row::Field;
#[cfg(target_os = "linux")]
use gander_core::table::{self, Dir, Sort};
fn fixture_root() -> &'static Path {
Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/fixtures/proc"))
}
#[test]
fn pressure_stall_information_is_read() {
let mut sampler = Sampler::new(fixture_root());
let (snapshot, _rows) = sampler.sample();
let psi = snapshot.psi.expect("the fixture tree has /proc/pressure");
assert!(psi.any_present());
assert_eq!(psi.io.some.avg10, 12.5);
assert_eq!(psi.io.full.avg10, 40.5);
assert!(psi.io.has_full);
assert_eq!(psi.memory.some.avg10, 0.1);
assert!(psi.cpu.has_full);
assert!(!psi.irq.present);
}
#[test]
fn missing_pressure_directory_reports_none() {
let dir = tempfile_dir();
std::fs::create_dir_all(&dir).unwrap();
let mut sampler = Sampler::new(&dir);
let (snapshot, _rows) = sampler.sample();
assert!(snapshot.psi.is_none(), "got {:?}", snapshot.psi);
std::fs::remove_dir_all(&dir).ok();
}
#[cfg(target_os = "linux")]
#[test]
fn smaps_is_only_read_when_requested() {
let mut sampler = Sampler::new(fixture_root());
let (_snap, rows) = sampler.sample();
let row = rows.iter().find(|r| r.pid == 42).expect("pid 42");
assert_eq!(row.pss, None, "the cheap scan must not read smaps_rollup");
PlatformSampler::set_scan_options(
&mut sampler,
gander_core::ScanOptions::none().with_smaps(true),
);
let (_snap, rows) = sampler.sample();
let row = rows.iter().find(|r| r.pid == 42).expect("pid 42");
assert_eq!(row.pss, None, "no smaps_rollup in the fixture tree");
}
#[cfg(target_os = "linux")]
#[test]
fn gpu_usage_is_read_from_fdinfo() {
let mut sampler = Sampler::new(fixture_root());
PlatformSampler::set_scan_options(
&mut sampler,
gander_core::ScanOptions::none().with_gpu(true),
);
let (_snap, rows) = sampler.sample();
let row = rows.iter().find(|r| r.pid == 42).expect("pid 42");
assert_eq!(row.gpu_ns, Some(6_000_000_000));
let pct = row.gpu_percent.expect("busy time is present");
assert!((pct - 24.0).abs() < 0.01, "got {pct}");
let other = rows.iter().find(|r| r.pid == 1).expect("pid 1");
assert_eq!(other.gpu_ns, None);
assert_eq!(other.gpu_percent, None);
}
#[cfg(target_os = "linux")]
#[test]
fn the_m3_fields_are_selectable() {
let names: Vec<&str> = Field::all().map(|f| f.name()).collect();
for want in ["M_PSS", "M_EPSS", "M_PRIVATE", "M_SWAP", "GPU%", "GPU_TIME"] {
assert!(names.contains(&want), "{want} missing from {names:?}");
assert_eq!(
Field::from_name(want).map(Field::name),
Some(want),
"{want} does not round-trip through its name"
);
}
}
#[cfg(target_os = "linux")]
#[test]
fn sorting_by_pss_sorts_by_pss() {
let mut rows = Vec::new();
for (pid, pss) in [(1u32, 300u64), (2, 100), (3, 200)] {
let mut r = gander_core::row::Row::default_for_test(pid);
r.pss = Some(pss);
rows.push(r);
}
let mut order: Vec<usize> = (0..rows.len()).collect();
table::sort_order(
&rows,
&mut order,
Sort {
field: Field::Pss,
dir: Dir::Asc,
},
);
let pids: Vec<u32> = order.iter().map(|&i| rows[i].pid).collect();
assert_eq!(pids, vec![2, 3, 1], "ascending PSS");
}
#[cfg(target_os = "linux")]
#[test]
fn unread_pss_sorts_as_unknown() {
let mut a = gander_core::row::Row::default_for_test(1);
a.pss = None;
let mut b = gander_core::row::Row::default_for_test(2);
b.pss = Some(0);
let rows = vec![a, b];
let mut order: Vec<usize> = (0..rows.len()).collect();
table::sort_order(
&rows,
&mut order,
Sort {
field: Field::Pss,
dir: Dir::Desc,
},
);
let pids: Vec<u32> = order.iter().map(|&i| rows[i].pid).collect();
assert_eq!(pids, vec![1, 2], "equal values fall back to pid, ascending");
}
fn tempfile_dir() -> std::path::PathBuf {
let mut p = std::env::temp_dir();
p.push(format!(
"gander-m3-{}-{:?}",
std::process::id(),
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_nanos())
.unwrap_or(0)
));
p
}