From a1815ac40c557ef5d70f63507d429878a65406aa Mon Sep 17 00:00:00 2001 From: Denis Benato Date: Thu, 6 Nov 2025 15:34:30 +0100 Subject: [PATCH] Chore: improve tests --- asusd/src/asus_armoury.rs | 137 +++++++++++++++++---- asusd/src/daemon.rs | 5 +- asusd/tests/sysfs_full_service.rs | 173 +++++++++++++++++++++++++++ asusd/tests/sysfs_nv_and_ppt_acdc.rs | 144 ++++++++++++++++++++++ rog-platform/src/asus_armoury.rs | 1 + rog-platform/src/power.rs | 10 ++ 6 files changed, 447 insertions(+), 23 deletions(-) create mode 100644 asusd/tests/sysfs_full_service.rs create mode 100644 asusd/tests/sysfs_nv_and_ppt_acdc.rs diff --git a/asusd/src/asus_armoury.rs b/asusd/src/asus_armoury.rs index f47286e3..1ec8b032 100644 --- a/asusd/src/asus_armoury.rs +++ b/asusd/src/asus_armoury.rs @@ -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 = 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>, + enable_zbus: bool, + profile_override: Option, + power_plugged_override: Option, ) -> Result { 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); diff --git a/asusd/src/daemon.rs b/asusd/src/daemon.rs index 20aebe8a..657bec97 100644 --- a/asusd/src/daemon.rs +++ b/asusd/src/daemon.rs @@ -77,11 +77,14 @@ async fn start_daemon() -> Result<(), Box> { 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 { diff --git a/asusd/tests/sysfs_full_service.rs b/asusd/tests/sysfs_full_service.rs new file mode 100644 index 00000000..e6471454 --- /dev/null +++ b/asusd/tests/sysfs_full_service.rs @@ -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 = + 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"); + }); +} diff --git a/asusd/tests/sysfs_nv_and_ppt_acdc.rs b/asusd/tests/sysfs_nv_and_ppt_acdc.rs new file mode 100644 index 00000000..9559a26f --- /dev/null +++ b/asusd/tests/sysfs_nv_and_ppt_acdc.rs @@ -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" + ); +} diff --git a/rog-platform/src/asus_armoury.rs b/rog-platform/src/asus_armoury.rs index c109468a..6e38d5e1 100644 --- a/rog-platform/src/asus_armoury.rs +++ b/rog-platform/src/asus_armoury.rs @@ -394,6 +394,7 @@ impl FirmwareAttribute { self, FirmwareAttribute::NvDynamicBoost | FirmwareAttribute::NvTempTarget + | FirmwareAttribute::DgpuBaseTgp | FirmwareAttribute::DgpuTgp ) } diff --git a/rog-platform/src/power.rs b/rog-platform/src/power.rs index adf75e2c..eeb4e14c 100644 --- a/rog-platform/src/power.rs +++ b/rog-platform/src/power.rs @@ -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, + } + } +}