Chore: improve tests

This commit is contained in:
Denis Benato
2025-11-06 15:34:30 +01:00
parent ad051bd7b8
commit a1815ac40c
6 changed files with 447 additions and 23 deletions

View File

@@ -146,6 +146,14 @@ impl ArmouryAttributeRegistry {
self.attrs.push(attr);
}
pub fn is_empty(&self) -> bool {
self.attrs.is_empty()
}
pub fn iter(&self) -> std::slice::Iter<'_, AsusArmouryAttribute> {
self.attrs.iter()
}
pub async fn emit_limits(&self, connection: &Connection) -> Result<(), RogError> {
let mut last_err: Option<RogError> = None;
for attr in &self.attrs {
@@ -517,11 +525,14 @@ impl AsusArmouryAttribute {
}
pub async fn start_attributes_zbus(
conn: &Connection,
conn: Option<&Connection>,
platform: RogPlatform,
power: AsusPower,
attributes: FirmwareAttributes,
config: Arc<Mutex<Config>>,
enable_zbus: bool,
profile_override: Option<rog_platform::platform::PlatformProfile>,
power_plugged_override: Option<bool>,
) -> Result<ArmouryAttributeRegistry, RogError> {
let mut registry = ArmouryAttributeRegistry::default();
for attr in attributes.attributes() {
@@ -534,35 +545,117 @@ pub async fn start_attributes_zbus(
let registry_attr = attr.clone();
if let Err(e) = attr.reload().await {
error!(
"Skipping attribute '{}' due to reload error: {e:?}",
attr.attr.name()
);
// continue with others
continue;
// Only perform the full reload (which may query the platform/power sysfs)
// when zbus is enabled. Tests using the no-zbus mode skip the reload and
// emulate the reload/apply behavior to avoid depending on udev/sysfs.
if enable_zbus {
if let Err(e) = attr.reload().await {
error!(
"Skipping attribute '{}' due to reload error: {e:?}",
attr.attr.name()
);
// continue with others
continue;
}
}
let attr_name = attr.attribute_name();
let path = dbus_path_for_attr(attr_name.as_str());
match zbus::object_server::SignalEmitter::new(conn, path) {
Ok(sig) => {
if let Err(e) = attr.watch_and_notify(sig).await {
error!("Failed to start watcher for '{}': {e:?}", attr.attr.name());
// If zbus is enabled and a connection is provided, create the SignalEmitter,
// start watchers and register the object on zbus. Tests can call this function
// with enable_zbus=false and conn=None to skip DBus registration and watchers.
if !enable_zbus {
// Emulate reload logic but prefer overrides when provided to avoid dependency on udev/sysfs in tests
let name: rog_platform::asus_armoury::FirmwareAttribute = attr.attr.name().into();
if name.is_ppt() || name.is_dgpu() {
// determine profile
let profile = if let Some(p) = profile_override {
p
} else {
match attr.platform.get_platform_profile() {
Ok(p) => p.into(),
Err(_) => rog_platform::platform::PlatformProfile::Balanced,
}
};
// determine power plugged
let power_plugged = if let Some(v) = power_plugged_override {
v
} else {
match attr.power.get_online() {
Ok(v) => v == 1,
Err(_) => false,
}
};
let apply_value = {
let config = attr.config.lock().await;
config
.select_tunings_ref(power_plugged, profile)
.and_then(|tuning| {
if tuning.enabled {
tuning.group.get(&attr.name()).copied()
} else {
None
}
})
};
if let Some(tune) = apply_value {
attr.attr
.set_current_value(&AttrValue::Integer(tune))
.map_err(|e| {
error!("Could not set {} value: {e:?}", attr.attr.name());
e
})?;
}
} else {
if let Some(saved_value) = attr.config.lock().await.armoury_settings.get(&name) {
attr.attr
.set_current_value(&AttrValue::Integer(*saved_value))
.map_err(|e| {
error!("Could not set {} value: {e:?}", attr.attr.name());
e
})?;
info!(
"Restored armoury setting {} to {:?}",
attr.attr.name(),
saved_value
);
}
}
Err(e) => {
error!(
"Failed to create SignalEmitter for '{}': {e:?}",
attr.attr.name()
);
}
registry.push(registry_attr);
continue;
}
if let Err(e) = attr.move_to_zbus(conn).await {
error!("Failed to register attribute '{attr_name}' on zbus: {e:?}");
continue;
// If zbus is enabled and a connection is provided, create the SignalEmitter,
// start watchers and register the object on zbus. Tests can call this function
// with enable_zbus=false and conn=None to skip DBus registration and watchers.
if enable_zbus {
if let Some(connection) = conn {
let path = dbus_path_for_attr(attr_name.as_str());
match zbus::object_server::SignalEmitter::new(connection, path) {
Ok(sig) => {
if let Err(e) = attr.watch_and_notify(sig).await {
error!("Failed to start watcher for '{}': {e:?}", attr.attr.name());
}
}
Err(e) => {
error!(
"Failed to create SignalEmitter for '{}': {e:?}",
attr.attr.name()
);
}
}
if let Err(e) = attr.move_to_zbus(connection).await {
error!("Failed to register attribute '{attr_name}' on zbus: {e:?}");
continue;
}
} else {
error!("zbus enabled but no Connection provided for attribute registration");
}
}
registry.push(registry_attr);

View File

@@ -77,11 +77,14 @@ async fn start_daemon() -> Result<(), Box<dyn Error>> {
let power = AsusPower::new()?; // TODO: maybe needs async mutex?
let attributes = FirmwareAttributes::new();
let armoury_registry = match start_attributes_zbus(
&server,
Some(&server),
platform.clone(),
power.clone(),
attributes.clone(),
config.clone(),
true,
None,
None,
)
.await
{

View File

@@ -0,0 +1,173 @@
use std::fs::{create_dir_all, File};
use std::io::Write;
use std::path::PathBuf;
use std::sync::Arc;
use tempfile::tempdir;
use asusd::asus_armoury::start_attributes_zbus;
use asusd::config::Config;
use rog_platform::asus_armoury::FirmwareAttributes;
use rog_platform::platform::PlatformProfile;
use rog_platform::platform::RogPlatform;
use rog_platform::power::AsusPower;
use tokio::runtime::Runtime;
use tokio::sync::Mutex as TokioMutex;
fn write_attr_dir(base: &PathBuf, name: &str, default: &str, display: &str) {
let attr_dir = base.join(name);
create_dir_all(&attr_dir).unwrap();
let mut f = File::create(attr_dir.join("default_value")).unwrap();
write!(f, "{}", default).unwrap();
let mut f = File::create(attr_dir.join("display_name")).unwrap();
write!(f, "{}", display).unwrap();
// create current_value file so set_current_value can open for write
let mut f = File::create(attr_dir.join("current_value")).unwrap();
write!(f, "{}", default).unwrap();
}
#[test]
fn full_service_handles_boot_sound_and_nv_tgp() {
let td = tempdir().unwrap();
let base = td.path().join("attributes");
create_dir_all(&base).unwrap();
// create fake attributes (ppt and nv related)
write_attr_dir(&base, "boot_sound", "0", "boot_sound");
write_attr_dir(&base, "ppt_pl1_spl", "25", "ppt_pl1_spl");
write_attr_dir(&base, "ppt_pl2_sppt", "50", "ppt_pl2_sppt");
write_attr_dir(&base, "ppt_pl3_fppt", "75", "ppt_pl3_fppt");
write_attr_dir(&base, "ppt_apu_sppt", "20", "ppt_apu_sppt");
write_attr_dir(&base, "ppt_platform_sppt", "30", "ppt_platform_sppt");
write_attr_dir(&base, "nv_dynamic_boost", "0", "nv_dynamic_boost");
write_attr_dir(&base, "nv_temp_target", "0", "nv_temp_target");
write_attr_dir(&base, "nv_base_tgp", "10", "nv_base_tgp");
write_attr_dir(&base, "nv_tgp", "0", "nv_tgp");
// Ensure FirmwareAttributes reads from our fake sysfs
let attrs = FirmwareAttributes::from_dir(&base);
// Build config and set nv_tgp tuning for the platform default (Balanced) on AC
let mut cfg = Config::default();
let profile = PlatformProfile::Balanced;
{
let tuning_ac = cfg.select_tunings(true, profile);
tuning_ac.enabled = true;
tuning_ac
.group
.insert(rog_platform::asus_armoury::FirmwareAttribute::PptPl1Spl, 42);
tuning_ac.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::PptPl2Sppt,
43,
);
tuning_ac.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::PptPl3Fppt,
44,
);
tuning_ac.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::PptApuSppt,
45,
);
tuning_ac.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::PptPlatformSppt,
46,
);
tuning_ac.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::NvDynamicBoost,
11,
);
tuning_ac.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::NvTempTarget,
66,
);
tuning_ac.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::DgpuBaseTgp,
12,
);
tuning_ac
.group
.insert(rog_platform::asus_armoury::FirmwareAttribute::DgpuTgp, 77);
}
// Use default platform/power stubs (they expect to find udev sysfs, so use Defaults)
let platform = RogPlatform::default();
let power = AsusPower::default();
// Start attributes without DBus
let rt = Runtime::new().unwrap();
let cfg_arc = Arc::new(TokioMutex::new(cfg));
let attrs_clone = attrs.clone();
rt.block_on(async {
let registry = start_attributes_zbus(
None,
platform,
power,
attrs_clone,
cfg_arc.clone(),
false,
Some(PlatformProfile::Balanced),
Some(true),
)
.await
.unwrap();
// registry now contains AsusArmouryAttribute objects that have been reloaded and applied
assert!(!registry.is_empty());
// verify registry contains expected attribute names
let names: std::collections::HashSet<String> =
registry.iter().map(|a| a.attribute_name()).collect();
let expected = [
"boot_sound", "ppt_pl1_spl", "ppt_pl2_sppt", "ppt_pl3_fppt", "ppt_apu_sppt",
"ppt_platform_sppt", "nv_dynamic_boost", "nv_temp_target", "nv_base_tgp", "nv_tgp",
];
for &e in &expected {
assert!(names.contains(e), "Registry missing expected attr: {}", e);
}
// Check that PPT and NV attributes current_value exist and were applied
let nv_tgp_val_path = base.join("nv_tgp").join("current_value");
let boot_val_path = base.join("boot_sound").join("current_value");
let ppt1_val_path = base.join("ppt_pl1_spl").join("current_value");
let ppt2_val_path = base.join("ppt_pl2_sppt").join("current_value");
let ppt3_val_path = base.join("ppt_pl3_fppt").join("current_value");
let apu_val_path = base.join("ppt_apu_sppt").join("current_value");
let plat_val_path = base.join("ppt_platform_sppt").join("current_value");
let nv_dyn_path = base.join("nv_dynamic_boost").join("current_value");
let nv_temp_path = base.join("nv_temp_target").join("current_value");
let nv_base_path = base.join("nv_base_tgp").join("current_value");
let nv = std::fs::read_to_string(&nv_tgp_val_path).unwrap();
assert_eq!(nv.trim(), "77");
// PPTs
assert_eq!(
std::fs::read_to_string(&ppt1_val_path).unwrap().trim(),
"42"
);
assert_eq!(
std::fs::read_to_string(&ppt2_val_path).unwrap().trim(),
"43"
);
assert_eq!(
std::fs::read_to_string(&ppt3_val_path).unwrap().trim(),
"44"
);
assert_eq!(std::fs::read_to_string(&apu_val_path).unwrap().trim(), "45");
assert_eq!(
std::fs::read_to_string(&plat_val_path).unwrap().trim(),
"46"
);
// NVs
assert_eq!(std::fs::read_to_string(&nv_dyn_path).unwrap().trim(), "11");
assert_eq!(std::fs::read_to_string(&nv_temp_path).unwrap().trim(), "66");
assert_eq!(std::fs::read_to_string(&nv_base_path).unwrap().trim(), "12");
// boot_sound default was 0, it should remain 0 unless config.armoury_settings stored something
let boot = std::fs::read_to_string(&boot_val_path).unwrap();
assert_eq!(boot.trim(), "0");
});
}

View File

@@ -0,0 +1,144 @@
use std::fs::{create_dir_all, File};
use std::io::Write;
use std::path::PathBuf;
use tempfile::tempdir;
use asusd::asus_armoury::set_config_or_default;
use asusd::config::Config;
use rog_platform::asus_armoury::FirmwareAttributes;
use rog_platform::platform::PlatformProfile;
fn write_attr_dir(base: &PathBuf, name: &str, default: &str, display: &str) {
let attr_dir = base.join(name);
create_dir_all(&attr_dir).unwrap();
let mut f = File::create(attr_dir.join("default_value")).unwrap();
write!(f, "{}", default).unwrap();
let mut f = File::create(attr_dir.join("display_name")).unwrap();
write!(f, "{}", display).unwrap();
// create current_value file so set_current_value can open for write
let mut f = File::create(attr_dir.join("current_value")).unwrap();
write!(f, "{}", default).unwrap();
}
#[test]
fn nv_dynamic_boost_and_ppt_acdc() {
let td = tempdir().unwrap();
let base = td.path().join("attributes");
create_dir_all(&base).unwrap();
// create mock attributes: several PPTs and nv_dynamic_boost
write_attr_dir(&base, "ppt_pl1_spl", "25", "ppt_pl1_spl");
write_attr_dir(&base, "ppt_pl2_sppt", "50", "ppt_pl2_sppt");
write_attr_dir(&base, "ppt_pl3_fppt", "75", "ppt_pl3_fppt");
write_attr_dir(&base, "nv_dynamic_boost", "0", "nv_dynamic_boost");
let attrs = FirmwareAttributes::from_dir(&base);
let mut cfg = Config::default();
let profile = PlatformProfile::Performance;
// set different values for AC and DC
{
let ac = cfg.select_tunings(true, profile);
ac.enabled = true;
ac.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::PptPl1Spl,
100,
);
ac.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::PptPl2Sppt,
101,
);
ac.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::PptPl3Fppt,
102,
);
ac.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::NvDynamicBoost,
9,
);
}
{
let dc = cfg.select_tunings(false, profile);
dc.enabled = true;
dc.group
.insert(rog_platform::asus_armoury::FirmwareAttribute::PptPl1Spl, 10);
dc.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::PptPl2Sppt,
11,
);
dc.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::PptPl3Fppt,
12,
);
dc.group.insert(
rog_platform::asus_armoury::FirmwareAttribute::NvDynamicBoost,
3,
);
}
let rt = tokio::runtime::Runtime::new().unwrap();
// apply AC
rt.block_on(async {
set_config_or_default(&attrs, &mut cfg, true, profile).await;
});
assert_eq!(
std::fs::read_to_string(base.join("ppt_pl1_spl").join("current_value"))
.unwrap()
.trim(),
"100"
);
assert_eq!(
std::fs::read_to_string(base.join("ppt_pl2_sppt").join("current_value"))
.unwrap()
.trim(),
"101"
);
assert_eq!(
std::fs::read_to_string(base.join("ppt_pl3_fppt").join("current_value"))
.unwrap()
.trim(),
"102"
);
assert_eq!(
std::fs::read_to_string(base.join("nv_dynamic_boost").join("current_value"))
.unwrap()
.trim(),
"9"
);
// apply DC
rt.block_on(async {
set_config_or_default(&attrs, &mut cfg, false, profile).await;
});
assert_eq!(
std::fs::read_to_string(base.join("ppt_pl1_spl").join("current_value"))
.unwrap()
.trim(),
"10"
);
assert_eq!(
std::fs::read_to_string(base.join("ppt_pl2_sppt").join("current_value"))
.unwrap()
.trim(),
"11"
);
assert_eq!(
std::fs::read_to_string(base.join("ppt_pl3_fppt").join("current_value"))
.unwrap()
.trim(),
"12"
);
assert_eq!(
std::fs::read_to_string(base.join("nv_dynamic_boost").join("current_value"))
.unwrap()
.trim(),
"3"
);
}

View File

@@ -394,6 +394,7 @@ impl FirmwareAttribute {
self,
FirmwareAttribute::NvDynamicBoost
| FirmwareAttribute::NvTempTarget
| FirmwareAttribute::DgpuBaseTgp
| FirmwareAttribute::DgpuTgp
)
}

View File

@@ -106,3 +106,13 @@ impl AsusPower {
))
}
}
impl Default for AsusPower {
fn default() -> Self {
Self {
mains: PathBuf::from("/this_shouldNeVErr_exisid"),
battery: PathBuf::from("/this_shouldNeVErr_exisid"),
usb: None,
}
}
}