impemented mock testing

This commit is contained in:
2026-02-26 17:11:42 +01:00
parent f76acd6256
commit 667d94af7a
21 changed files with 480 additions and 110 deletions

View File

@@ -1,16 +1,16 @@
use anyhow::{Result, anyhow};
use std::path::Path;
use std::path::{Path};
use std::fs;
use std::time::{Duration, Instant};
use std::process::Command;
use tracing::{debug, warn};
use std::sync::Mutex;
use tracing::{debug};
use crate::sal::traits::{SensorBus, ActuatorBus, EnvironmentGuard, HardwareWatchdog, PreflightAuditor, AuditStep, AuditError, SafetyStatus};
use crate::sal::traits::{SensorBus, ActuatorBus, EnvironmentGuard, HardwareWatchdog, PreflightAuditor, AuditStep, AuditError, SafetyStatus, EnvironmentCtx};
use crate::sal::heuristic::discovery::SystemFactSheet;
use crate::sal::heuristic::schema::HardwareDb;
pub struct GenericLinuxSal {
ctx: EnvironmentCtx,
fact_sheet: SystemFactSheet,
db: HardwareDb,
suppressed_services: Mutex<Vec<String>>,
@@ -20,14 +20,21 @@ pub struct GenericLinuxSal {
}
impl GenericLinuxSal {
pub fn new(facts: SystemFactSheet, db: HardwareDb) -> Self {
pub fn new(ctx: EnvironmentCtx, facts: SystemFactSheet, db: HardwareDb) -> Self {
let initial_energy = if let Some(pwr_base) = facts.rapl_paths.first() {
fs::read_to_string(pwr_base.join("energy_uj")).unwrap_or_default().trim().parse().unwrap_or(0)
} else {
0
};
Self {
db,
suppressed_services: Mutex::new(Vec::new()),
last_valid_temp: Mutex::new((0.0, Instant::now())),
current_pl1: Mutex::new(15.0),
last_energy: Mutex::new((0, Instant::now())),
last_energy: Mutex::new((initial_energy, Instant::now())),
fact_sheet: facts,
ctx,
}
}
@@ -35,8 +42,6 @@ impl GenericLinuxSal {
self.fact_sheet.vendor.to_lowercase().contains("dell")
}
/// Read sysfs safely. We removed the thread-per-read timeout logic
/// as it was inefficient. sysfs reads are generally fast enough.
fn read_sysfs(&self, path: &Path) -> Result<String> {
fs::read_to_string(path).map(|s| s.trim().to_string()).map_err(|e| anyhow!(e))
}
@@ -46,11 +51,11 @@ impl PreflightAuditor for GenericLinuxSal {
fn audit(&self) -> Box<dyn Iterator<Item = AuditStep> + '_> {
let mut steps = Vec::new();
for check in &self.db.preflight_checks {
let status = Command::new("sh").arg("-c").arg(&check.check_cmd).status();
let status = self.ctx.runner.run("sh", &["-c", &check.check_cmd]);
steps.push(AuditStep {
description: check.name.clone(),
outcome: match status {
Ok(s) if s.success() => Ok(()),
Ok(_) => Ok(()),
_ => Err(AuditError::KernelIncompatible(check.fail_help.clone())),
}
});
@@ -106,11 +111,12 @@ impl SensorBus for GenericLinuxSal {
}
fn get_freq_mhz(&self) -> Result<f32> {
let path = Path::new("/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq");
let path = self.ctx.sysfs_base.join("sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq");
if path.exists() {
Ok(self.read_sysfs(path)?.parse::<f32>()? / 1000.0)
Ok(self.read_sysfs(&path)?.parse::<f32>()? / 1000.0)
} else {
let cpuinfo = fs::read_to_string("/proc/cpuinfo")?;
let cpuinfo_path = self.ctx.sysfs_base.join("proc/cpuinfo");
let cpuinfo = fs::read_to_string(cpuinfo_path)?;
for line in cpuinfo.lines() {
if line.starts_with("cpu MHz") {
if let Some((_, mhz)) = line.split_once(':') {
@@ -133,7 +139,7 @@ impl ActuatorBus for GenericLinuxSal {
};
if let Some(cmd_str) = cmd {
let parts: Vec<&str> = cmd_str.split_whitespace().collect();
Command::new(parts[0]).args(&parts[1..]).status()?;
self.ctx.runner.run(parts[0], &parts[1..])?;
Ok(())
} else { Err(anyhow!("Dell fan command missing")) }
} else { Ok(()) }
@@ -159,7 +165,8 @@ impl EnvironmentGuard for GenericLinuxSal {
for conflict_id in &self.fact_sheet.active_conflicts {
if let Some(conflict) = self.db.conflicts.iter().find(|c| &c.id == conflict_id) {
for service in &conflict.services {
if Command::new("systemctl").arg("stop").arg(service).status()?.success() {
if self.ctx.runner.run("systemctl", &["is-active", "--quiet", service]).is_ok() {
self.ctx.runner.run("systemctl", &["stop", service])?;
suppressed.push(service.clone());
}
}
@@ -171,7 +178,7 @@ impl EnvironmentGuard for GenericLinuxSal {
fn restore(&self) -> Result<()> {
let mut suppressed = self.suppressed_services.lock().unwrap();
for service in suppressed.drain(..) {
let _ = Command::new("systemctl").arg("start").arg(service).status();
let _ = self.ctx.runner.run("systemctl", &["start", &service]);
}
if self.is_dell() { let _ = self.set_fan_mode("auto"); }
Ok(())