mirror of
https://github.com/starship/starship.git
synced 2026-06-23 02:05:51 +07:00
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:
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user