summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Houts <16907671+ahouts@users.noreply.github.com>2021-06-29 18:46:41 -0500
committerGitHub <noreply@github.com>2021-06-29 19:46:41 -0400
commit53a30046d1d3504964f8f6cff8b8201dbeae09c0 (patch)
tree8e875d9c2381e776dc7e3ccded7c693233db6208
parent72e5a544fc92eb5313a1fa895bd5b9042a8cc601 (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.
-rw-r--r--Cargo.lock96
-rw-r--r--Cargo.toml1
-rw-r--r--src/context.rs5
-rw-r--r--src/modules/battery.rs350
-rw-r--r--src/modules/mod.rs3
-rw-r--r--src/test/mod.rs9
6 files changed, 423 insertions, 41 deletions
diff --git a/Cargo.lock b/Cargo.lock
index c415492f2..f290d7a46 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -369,6 +369,12 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -419,6 +425,12 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -467,6 +479,15 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -498,6 +519,12 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -878,6 +905,33 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -957,6 +1011,12 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1213,6 +1273,35 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1602,6 +1691,7 @@ dependencies = [
"git2",
"indexmap",
"log",
+ "mockall",
"native-tls",
"nix 0.21.0",
"notify-rust",
@@ -1797,6 +1887,12 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index b745fcb58..c625b76a1 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -94,6 +94,7 @@ shadow-rs = "0.6.2"
[dev-dependencies]
tempfile = "3.2.0"
+mockall = "0.9"
[profile.release]
codegen-units = 1
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);