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:
parent
72e5a544fc
commit
53a30046d1
|
@ -368,6 +368,12 @@ dependencies = [
|
|||
"syn 1.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "difference"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.8.1"
|
||||
|
@ -418,6 +424,12 @@ dependencies = [
|
|||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "downcast"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bb454f0228b18c7f4c3b0ebbee346ed9c52e7443b0999cd543ff3571205701d"
|
||||
|
||||
[[package]]
|
||||
name = "dtoa"
|
||||
version = "0.4.8"
|
||||
|
@ -466,6 +478,15 @@ dependencies = [
|
|||
"instant",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "float-cmp"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1267f4ac4f343772758f7b1bdcbe767c218bbab93bb432acbf5162bbf85a6c4"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
|
@ -497,6 +518,12 @@ dependencies = [
|
|||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fragile"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69a039c3498dc930fe810151a34ba0c1c70b02b8625035592e74432f678591f2"
|
||||
|
||||
[[package]]
|
||||
name = "funty"
|
||||
version = "1.1.0"
|
||||
|
@ -877,6 +904,33 @@ dependencies = [
|
|||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mockall"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18d614ad23f9bb59119b8b5670a85c7ba92c5e9adf4385c81ea00c51c8be33d5"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"downcast",
|
||||
"fragile",
|
||||
"lazy_static",
|
||||
"mockall_derive",
|
||||
"predicates",
|
||||
"predicates-tree",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mockall_derive"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5dd4234635bca06fc96c7368d038061e0aae1b00a764dc817e900dc974e3deea"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "native-tls"
|
||||
version = "0.2.7"
|
||||
|
@ -956,6 +1010,12 @@ dependencies = [
|
|||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "normalize-line-endings"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
|
||||
|
||||
[[package]]
|
||||
name = "notify-rust"
|
||||
version = "4.5.2"
|
||||
|
@ -1212,6 +1272,35 @@ version = "0.2.10"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
|
||||
|
||||
[[package]]
|
||||
name = "predicates"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f49cfaf7fdaa3bfacc6fa3e7054e65148878354a5cfddcf661df4c851f8021df"
|
||||
dependencies = [
|
||||
"difference",
|
||||
"float-cmp",
|
||||
"normalize-line-endings",
|
||||
"predicates-core",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "predicates-core"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57e35a3326b75e49aa85f5dc6ec15b41108cf5aee58eabb1f274dd18b73c2451"
|
||||
|
||||
[[package]]
|
||||
name = "predicates-tree"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15f553275e5721409451eb85e15fd9a860a6e5ab4496eb215987502b5f5391f2"
|
||||
dependencies = [
|
||||
"predicates-core",
|
||||
"treeline",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-crate"
|
||||
version = "0.1.5"
|
||||
|
@ -1602,6 +1691,7 @@ dependencies = [
|
|||
"git2",
|
||||
"indexmap",
|
||||
"log",
|
||||
"mockall",
|
||||
"native-tls",
|
||||
"nix 0.21.0",
|
||||
"notify-rust",
|
||||
|
@ -1796,6 +1886,12 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "treeline"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.13.0"
|
||||
|
|
|
@ -94,6 +94,7 @@ shadow-rs = "0.6.2"
|
|||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.2.0"
|
||||
mockall = "0.9"
|
||||
|
||||
[profile.release]
|
||||
codegen-units = 1
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,10 +77,67 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_battery_status() -> Option<BatteryStatus> {
|
||||
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_info.energy / battery_info.energy_full * 100.0,
|
||||
state: battery_info.state,
|
||||
};
|
||||
log::debug!("Battery status: {:?}", battery);
|
||||
Some(battery)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// the merge returns Charging if at least one is charging
|
||||
/// Discharging if at least one is Discharging
|
||||
/// Full if both are Full or one is Full and the other Unknow
|
||||
/// Empty if both are Empty or one is Empty and the other Unknow
|
||||
/// Unknown otherwise
|
||||
fn merge_battery_states(state1: battery::State, state2: battery::State) -> battery::State {
|
||||
use battery::State::{Charging, Discharging, Unknown};
|
||||
if state1 == Charging || state2 == Charging {
|
||||
Charging
|
||||
} else if state1 == Discharging || state2 == Discharging {
|
||||
Discharging
|
||||
} else if state1 == state2 {
|
||||
state1
|
||||
} else if state1 == Unknown {
|
||||
state2
|
||||
} else if state2 == Unknown {
|
||||
state1
|
||||
} else {
|
||||
Unknown
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BatteryInfo {
|
||||
energy: f32,
|
||||
energy_full: f32,
|
||||
state: battery::State,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
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()?;
|
||||
let battery_contructor = batteries
|
||||
Some(
|
||||
batteries
|
||||
.filter_map(|battery| match battery {
|
||||
Ok(battery) => {
|
||||
log::debug!("Battery found: {:?}", battery);
|
||||
|
@ -110,49 +169,258 @@ fn get_battery_status() -> Option<BatteryStatus> {
|
|||
acc.state = merge_battery_states(acc.state, x.state);
|
||||
acc
|
||||
},
|
||||
);
|
||||
if battery_contructor.energy_full != 0.0 {
|
||||
let battery = BatteryStatus {
|
||||
percentage: battery_contructor.energy / battery_contructor.energy_full * 100.0,
|
||||
state: battery_contructor.state,
|
||||
};
|
||||
log::debug!("Battery status: {:?}", battery);
|
||||
Some(battery)
|
||||
} else {
|
||||
None
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// the merge returns Charging if at least one is charging
|
||||
/// Discharging if at least one is Discharging
|
||||
/// Full if both are Full or one is Full and the other Unknow
|
||||
/// Empty if both are Empty or one is Empty and the other Unknow
|
||||
/// Unknown otherwise
|
||||
fn merge_battery_states(state1: battery::State, state2: battery::State) -> battery::State {
|
||||
use battery::State::{Charging, Discharging, Unknown};
|
||||
if state1 == Charging || state2 == Charging {
|
||||
Charging
|
||||
} else if state1 == Discharging || state2 == Discharging {
|
||||
Discharging
|
||||
} else if state1 == state2 {
|
||||
state1
|
||||
} else if state1 == Unknown {
|
||||
state2
|
||||
} else if state2 == Unknown {
|
||||
state1
|
||||
} else {
|
||||
Unknown
|
||||
#[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);
|
||||
}
|
||||
}
|
||||
|
||||
struct BatteryInfo {
|
||||
energy: f32,
|
||||
energy_full: f32,
|
||||
state: battery::State,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct BatteryStatus {
|
||||
percentage: f32,
|
||||
state: battery::State,
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue