#![cfg(test)]
use super::*;
#[test]
fn parse_config_hz_standard() {
let config = "# comment\nCONFIG_HZ_1000=y\nCONFIG_HZ=1000\n";
assert_eq!(parse_config_hz(config), Some(1000));
}
#[test]
fn parse_config_hz_250() {
let config = "CONFIG_HZ=250\n";
assert_eq!(parse_config_hz(config), Some(250));
}
#[test]
fn parse_config_hz_100() {
let config = "CONFIG_HZ=100\n";
assert_eq!(parse_config_hz(config), Some(100));
}
#[test]
fn parse_config_hz_missing() {
let config = "CONFIG_PREEMPT=y\nCONFIG_HZ_1000=y\n";
assert_eq!(parse_config_hz(config), None);
}
#[test]
fn parse_config_hz_garbage_value() {
let config = "CONFIG_HZ=abc\n";
assert_eq!(parse_config_hz(config), None);
}
#[test]
fn parse_config_hz_whitespace() {
let config = " CONFIG_HZ=1000 \n";
assert_eq!(parse_config_hz(config), Some(1000));
}
#[test]
fn parse_config_hz_commented_out() {
let config = "# CONFIG_HZ=1000\nCONFIG_HZ_1000=y\n";
assert_eq!(parse_config_hz(config), None);
}
#[test]
fn vcpu_threshold_reasonable_range() {
let t = vcpu_preemption_threshold_ns(None);
assert!(
(10_000_000..=100_000_000).contains(&t),
"threshold {t} ns outside expected range 10ms-100ms"
);
}
#[test]
fn vcpu_threshold_default_hz_fallback() {
let t = vcpu_preemption_threshold_ns(Some(std::path::Path::new("/nonexistent/bzImage")));
assert!(
(10_000_000..=100_000_000).contains(&t),
"fallback threshold {t} ns outside expected range"
);
}
#[test]
fn guest_kernel_hz_gated_on_kernel_path() {
let bogus = std::path::Path::new("/nonexistent/ktstr-kernel/bzImage");
let hz = guest_kernel_hz(Some(bogus));
assert_eq!(
hz, DEFAULT_HZ,
"kernel_path=Some with no IKCONFIG/.config must fall back \
to DEFAULT_HZ, not host /boot/config; got {hz}"
);
}
#[test]
fn guest_kernel_hz_none_consults_host_config() {
let hz = guest_kernel_hz(None);
assert!(
matches!(hz, 100 | 250 | 300 | 1000),
"guest_kernel_hz(None) = {hz} outside plausible HZ set"
);
}
fn make_ikconfig_blob(config_text: &str) -> Vec<u8> {
use flate2::Compression;
use flate2::write::GzEncoder;
use std::io::Write;
let mut blob = vec![0u8; 64]; blob.extend_from_slice(IKCONFIG_MAGIC);
let mut encoder = GzEncoder::new(Vec::new(), Compression::default());
encoder.write_all(config_text.as_bytes()).unwrap();
blob.extend(encoder.finish().unwrap());
blob.extend_from_slice(b"IKCFG_ED");
blob
}
#[test]
fn ikconfig_extracts_hz_1000() {
let blob = make_ikconfig_blob("CONFIG_HZ=1000\nCONFIG_PREEMPT=y\n");
let dir = std::env::temp_dir().join("ktstr-ikconfig-test-1000");
std::fs::create_dir_all(&dir).unwrap();
let path = dir.join("vmlinux");
std::fs::write(&path, &blob).unwrap();
assert_eq!(read_hz_from_ikconfig(&path), Some(1000));
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn ikconfig_extracts_hz_250() {
let blob = make_ikconfig_blob("CONFIG_HZ=250\n");
let dir = std::env::temp_dir().join("ktstr-ikconfig-test-250");
std::fs::create_dir_all(&dir).unwrap();
let path = dir.join("vmlinux");
std::fs::write(&path, &blob).unwrap();
assert_eq!(read_hz_from_ikconfig(&path), Some(250));
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn ikconfig_no_marker_returns_none() {
let dir = std::env::temp_dir().join("ktstr-ikconfig-test-none");
std::fs::create_dir_all(&dir).unwrap();
let path = dir.join("vmlinux");
std::fs::write(&path, b"no marker here").unwrap();
assert_eq!(read_hz_from_ikconfig(&path), None);
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn ikconfig_missing_config_hz_returns_none() {
let blob = make_ikconfig_blob("CONFIG_PREEMPT=y\n");
let dir = std::env::temp_dir().join("ktstr-ikconfig-test-nohz");
std::fs::create_dir_all(&dir).unwrap();
let path = dir.join("vmlinux");
std::fs::write(&path, &blob).unwrap();
assert_eq!(read_hz_from_ikconfig(&path), None);
let _ = std::fs::remove_dir_all(&dir);
}