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.
This commit is contained in:
Andrew Houts
2021-06-29 18:46:41 -05:00
committed by GitHub
parent 72e5a544fc
commit 53a30046d1
6 changed files with 423 additions and 41 deletions
+5
View File
@@ -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,
}
}
+309 -41
View File
@@ -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);
}
}
+3
View File
@@ -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;
+9
View File
@@ -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);