diff options
author | Andrew Houts <16907671+ahouts@users.noreply.github.com> | 2021-06-29 18:46:41 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-06-29 19:46:41 -0400 |
commit | 53a30046d1d3504964f8f6cff8b8201dbeae09c0 (patch) | |
tree | 8e875d9c2381e776dc7e3ccded7c693233db6208 /src | |
parent | 72e5a544fc92eb5313a1fa895bd5b9042a8cc601 (diff) |
test(battery): add battery tests (#2795)
Add some tests to the battery module, make it testable by mocking out the code that fetches battery info.
Diffstat (limited to 'src')
-rw-r--r-- | src/context.rs | 5 | ||||
-rw-r--r-- | src/modules/battery.rs | 350 | ||||
-rw-r--r-- | src/modules/mod.rs | 3 | ||||
-rw-r--r-- | src/test/mod.rs | 9 |
4 files changed, 326 insertions, 41 deletions
diff --git a/src/context.rs b/src/context.rs index 451ce53c0..9c3a88dff 100644 --- a/src/context.rs +++ b/src/context.rs @@ -50,6 +50,9 @@ pub struct Context<'a> { #[cfg(test)] pub cmd: HashMap<&'a str, Option<CommandOutput>>, + #[cfg(feature = "battery")] + pub battery_info_provider: &'a (dyn crate::modules::BatteryInfoProvider + Send + Sync), + /// Timeout for the execution of commands cmd_timeout: Duration, } @@ -122,6 +125,8 @@ impl<'a> Context<'a> { env: HashMap::new(), #[cfg(test)] cmd: HashMap::new(), + #[cfg(feature = "battery")] + battery_info_provider: &crate::modules::BatteryStatusProviderImpl, cmd_timeout, } } diff --git a/src/modules/battery.rs b/src/modules/battery.rs index b47677294..630d5faa7 100644 --- a/src/modules/battery.rs +++ b/src/modules/battery.rs @@ -1,5 +1,7 @@ use super::{Context, Module, RootModuleConfig, Shell}; use crate::configs::battery::BatteryConfig; +#[cfg(test)] +use mockall::automock; use crate::formatter::StringFormatter; @@ -12,7 +14,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> { _ => "%", }; - let battery_status = get_battery_status()?; + let battery_status = get_battery_status(context)?; let BatteryStatus { state, percentage } = battery_status; let mut module = context.new_module("battery"); @@ -75,46 +77,12 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> { } } -fn get_battery_status() -> Option<BatteryStatus> { - let battery_manager = battery::Manager::new().ok()?; - let batteries = battery_manager.batteries().ok()?; - let battery_contructor = batteries - .filter_map(|battery| match battery { - Ok(battery) => { - log::debug!("Battery found: {:?}", battery); - Some(BatteryInfo { - energy: battery.energy().value, - energy_full: battery.energy_full().value, - state: battery.state(), - }) - } - Err(e) => { - let level = if cfg!(target_os = "linux") { - log::Level::Info - } else { - log::Level::Warn - }; - log::log!(level, "Unable to access battery information:\n{}", &e); - None - } - }) - .fold( - BatteryInfo { - energy: 0.0, - energy_full: 0.0, - state: battery::State::Unknown, - }, - |mut acc, x| { - acc.energy += x.energy; - acc.energy_full += x.energy_full; - acc.state = merge_battery_states(acc.state, x.state); - acc - }, - ); - if battery_contructor.energy_full != 0.0 { +fn get_battery_status(context: &Context) -> Option<BatteryStatus> { + let battery_info = context.battery_info_provider.get_battery_info()?; + if battery_info.energy_full != 0.0 { let battery = BatteryStatus { - percentage: battery_contructor.energy / battery_contructor.energy_full * 100.0, - state: battery_contructor.state, + percentage: battery_info.energy / battery_info.energy_full * 100.0, + state: battery_info.state, }; log::debug!("Battery status: {:?}", battery); Some(battery) @@ -145,7 +113,7 @@ fn merge_battery_states(state1: battery::State, state2: battery::State) -> batte } } -struct BatteryInfo { +pub struct BatteryInfo { energy: f32, energy_full: f32, state: battery::State, @@ -156,3 +124,303 @@ struct BatteryStatus { percentage: f32, state: battery::State, } + +#[cfg_attr(test, automock)] +pub trait BatteryInfoProvider { + fn get_battery_info(&self) -> Option<BatteryInfo>; +} + +pub struct BatteryStatusProviderImpl; + +impl BatteryInfoProvider for BatteryStatusProviderImpl { + fn get_battery_info(&self) -> Option<BatteryInfo> { + let battery_manager = battery::Manager::new().ok()?; + let batteries = battery_manager.batteries().ok()?; + Some( + batteries + .filter_map(|battery| match battery { + Ok(battery) => { + log::debug!("Battery found: {:?}", battery); + Some(BatteryInfo { + energy: battery.energy().value, + energy_full: battery.energy_full().value, + state: battery.state(), + }) + } + Err(e) => { + let level = if cfg!(target_os = "linux") { + log::Level::Info + } else { + log::Level::Warn + }; + log::log!(level, "Unable to access battery information:\n{}", &e); + None + } + }) + .fold( + BatteryInfo { + energy: 0.0, + energy_full: 0.0, + state: battery::State::Unknown, + }, + |mut acc, x| { + acc.energy += x.energy; + acc.energy_full += x.energy_full; + acc.state = merge_battery_states(acc.state, x.state); + acc + }, + ), + ) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::test::ModuleRenderer; + use ansi_term::Color; + + #[test] + fn no_battery_status() { + let mut mock = MockBatteryInfoProvider::new(); + + mock.expect_get_battery_info().times(1).returning(|| None); + + let actual = ModuleRenderer::new("battery") + .config(toml::toml! { + [[battery.display]] + threshold = 100 + style = "" + }) + .battery_info_provider(&mock) + .collect(); + let expected = None; + + assert_eq!(expected, actual); + } + + #[test] + fn ignores_zero_capacity_battery() { + let mut mock = MockBatteryInfoProvider::new(); + + mock.expect_get_battery_info().times(1).returning(|| { + Some(BatteryInfo { + energy: 0.0, + energy_full: 0.0, + state: battery::State::Full, + }) + }); + + let actual = ModuleRenderer::new("battery") + .config(toml::toml! { + [[battery.display]] + threshold = 100 + style = "" + }) + .battery_info_provider(&mock) + .collect(); + let expected = None; + + assert_eq!(expected, actual); + } + + #[test] + fn battery_full() { + let mut mock = MockBatteryInfoProvider::new(); + + mock.expect_get_battery_info().times(1).returning(|| { + Some(BatteryInfo { + energy: 1000.0, + energy_full: 1000.0, + state: battery::State::Full, + }) + }); + + let actual = ModuleRenderer::new("battery") + .config(toml::toml! { + [[battery.display]] + threshold = 100 + style = "" + }) + .battery_info_provider(&mock) + .collect(); + let expected = Some(String::from(" 100% ")); + + assert_eq!(expected, actual); + } + + #[test] + fn battery_charging() { + let mut mock = MockBatteryInfoProvider::new(); + + mock.expect_get_battery_info().times(1).returning(|| { + Some(BatteryInfo { + energy: 800.0, + energy_full: 1000.0, + state: battery::State::Charging, + }) + }); + + let actual = ModuleRenderer::new("battery") + .config(toml::toml! { + [[battery.display]] + threshold = 90 + style = "" + }) + .battery_info_provider(&mock) + .collect(); + let expected = Some(String::from(" 80% ")); + + assert_eq!(expected, actual); + } + + #[test] + fn battery_discharging() { + let mut mock = MockBatteryInfoProvider::new(); + + mock.expect_get_battery_info().times(1).returning(|| { + Some(BatteryInfo { + energy: 800.0, + energy_full: 1000.0, + state: battery::State::Discharging, + }) + }); + + let actual = ModuleRenderer::new("battery") + .config(toml::toml! { + [[battery.display]] + threshold = 100 + style = "" + }) + .battery_info_provider(&mock) + .collect(); + let expected = Some(String::from(" 80% ")); + + assert_eq!(expected, actual); + } + + #[test] + fn battery_unknown() { + let mut mock = MockBatteryInfoProvider::new(); + + mock.expect_get_battery_info().times(1).returning(|| { + Some(BatteryInfo { + energy: 0.0, + energy_full: 1.0, + state: battery::State::Unknown, + }) + }); + + let actual = ModuleRenderer::new("battery") + .config(toml::toml! { + [[battery.display]] + threshold = 100 + style = "" + }) + .battery_info_provider(&mock) + .collect(); + let expected = Some(String::from(" 0% ")); + + assert_eq!(expected, actual); + } + + #[test] + fn battery_empty() { + let mut mock = MockBatteryInfoProvider::new(); + + mock.expect_get_battery_info().times(1).returning(|| { + Some(BatteryInfo { + energy: 0.0, + energy_full: 1000.0, + state: battery::State::Empty, + }) + }); + + let actual = ModuleRenderer::new("battery") + .config(toml::toml! { + [[battery.display]] + threshold = 100 + style = "" + }) + .battery_info_provider(&mock) + .collect(); + let expected = Some(String::from(" 0% ")); + + assert_eq!(expected, actual); + } + + #[test] + fn battery_hidden_when_percentage_above_threshold() { + let mut mock = MockBatteryInfoProvider::new(); + + mock.expect_get_battery_info().times(1).returning(|| { + Some(BatteryInfo { + energy: 600.0, + energy_full: 1000.0, + state: battery::State::Full, + }) + }); + + let actual = ModuleRenderer::new("battery") + .config(toml::toml! { + [[battery.display]] + threshold = 50 + style = "" + }) + .battery_info_provider(&mock) + .collect(); + let expected = None; + + assert_eq!(expected, actual); + } + + #[test] + fn battery_uses_style() { + let mut mock = MockBatteryInfoProvider::new(); + + mock.expect_get_battery_info().times(1).returning(|| { + Some(BatteryInfo { + energy: 400.0, + energy_full: 1000.0, + state: battery::State::Discharging, + }) + }); + + let actual = ModuleRenderer::new("battery") + .config(toml::toml! { + [[battery.display]] + threshold = 50 + style = "bold red" + }) + .battery_info_provider(&mock) + .collect(); + let expected = Some(format!("{} ", Color::Red.bold().paint(" 40%"))); + + assert_eq!(expected, actual); + } + + #[test] + fn battery_displayed_precision() { + let mut mock = MockBatteryInfoProvider::new(); + + mock.expect_get_battery_info().times(1).returning(|| { + Some(BatteryInfo { + energy: 129.87654, + energy_full: 1000.0, + state: battery::State::Discharging, + }) + }); + + let actual = ModuleRenderer::new("battery") + .config(toml::toml! { + [[battery.display]] + threshold = 100 + style = "" + }) + .battery_info_provider(&mock) + .collect(); + let expected = Some(String::from(" 13% ")); + + assert_eq!(expected, actual); + } +} diff --git a/src/modules/mod.rs b/src/modules/mod.rs index 674a0fc5b..781bc732a 100644 --- a/src/modules/mod.rs +++ b/src/modules/mod.rs @@ -64,6 +64,9 @@ mod zig; #[cfg(feature = "battery")] mod battery; +#[cfg(feature = "battery")] +pub use self::battery::{BatteryInfoProvider, BatteryStatusProviderImpl}; + use crate::config::RootModuleConfig; use crate::context::{Context, Shell}; use crate::module::Module; diff --git a/src/test/mod.rs b/src/test/mod.rs index c81d5958b..299b6f309 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -121,6 +121,15 @@ impl<'a> ModuleRenderer<'a> { self } + #[cfg(feature = "battery")] + pub fn battery_info_provider( + mut self, + battery_info_provider: &'a (dyn crate::modules::BatteryInfoProvider + Send + Sync), + ) -> Self { + self.context.battery_info_provider = battery_info_provider; + self + } + /// Renders the module returning its output pub fn collect(self) -> Option<String> { let ret = crate::print::get_module(self.name, self.context); |