From 640e6dd6bf7f25519a1ec78fb5415fdbcb4a783d Mon Sep 17 00:00:00 2001 From: Alexander Bantyev Date: Thu, 17 Sep 2020 00:19:02 +0300 Subject: [PATCH] Add tests and an example --- battery/Cargo.toml | 2 +- battery/src/main.rs | 130 +++++++++++++++++++++++++++++++++++--- common/examples/simple.rs | 54 ++++++++++++++++ 3 files changed, 177 insertions(+), 9 deletions(-) create mode 100644 common/examples/simple.rs diff --git a/battery/Cargo.toml b/battery/Cargo.toml index 4eab083..7199c26 100644 --- a/battery/Cargo.toml +++ b/battery/Cargo.toml @@ -8,4 +8,4 @@ edition = "2018" [dependencies] battery = "0.7.6" -simple-osd-common = { version = "0.1", path = "../common" } \ No newline at end of file +simple-osd-common = { version = "0.1", path = "../common" } diff --git a/battery/src/main.rs b/battery/src/main.rs index d17e18f..551ec17 100644 --- a/battery/src/main.rs +++ b/battery/src/main.rs @@ -11,7 +11,7 @@ use std::time::Duration; use osd::notify::{OSD, Urgency}; use osd::config::Config; -#[derive(Debug)] +#[derive(Debug, Eq, PartialEq)] enum Threshold { Percentage(i32), Minutes(i32) @@ -25,18 +25,133 @@ enum State { Normal } +fn threshold_sane(thresh: Threshold) -> Option { + match thresh { + Threshold::Percentage(p) => { + if p < 0 || p > 100 { return None; } + Some(thresh) + }, + Threshold::Minutes(m) => { + if m < 0 { return None; } + Some(thresh) + } + } +} + fn parse_threshold(thresh: String) -> Option { let mut s = thresh.clone(); let last = s.pop(); + let parsed = s.parse(); + match last { - Some('%') => s.parse().map(Threshold::Percentage).ok(), - Some('m') => s.parse().map(Threshold::Minutes).ok(), + Some('%') => parsed.map(Threshold::Percentage).ok().and_then(threshold_sane), + Some('m') => parsed.map(Threshold::Minutes).ok().and_then(threshold_sane), _ => None } } +#[cfg(test)] +mod parse_threshold_tests { + use super::parse_threshold; + use super::Threshold; + #[test] + fn parses_percentage() { + assert_eq!(parse_threshold("15%".to_string()), Some(Threshold::Percentage(15))); + } + #[test] + fn parses_minutes() { + assert_eq!(parse_threshold("10m".to_string()), Some(Threshold::Minutes(10))); + } + #[test] + fn fails_on_incorrect_percentage() { + assert_eq!(parse_threshold("foo%".to_string()), None); + } + #[test] + fn fails_on_incorrect_minutes() { + assert_eq!(parse_threshold("foom".to_string()), None); + } + #[test] + fn fails_on_high_percentage() { + assert_eq!(parse_threshold("110%".to_string()), None); + } + #[test] + fn fails_on_negative_percentage() { + assert_eq!(parse_threshold("-10%".to_string()), None); + } + #[test] + fn fails_on_negative_minutes() { + assert_eq!(parse_threshold("-10m".to_string()), None); + } +} + +fn format_duration(duration: f32) -> String { + let mut d = duration as i32; + if d == 0 { + return "0s".to_string(); + } + let mut s = String::new(); + if d < 0 { + s.push_str("-"); + d = -d; + } + let hours = d / 3600; + let minutes = (d % 3600) / 60; + let seconds = d % 60; + + + if hours > 0 { + s.push_str(&format!("{}h", hours)); + } + if minutes > 0 { + if hours > 0 { s.push_str(" "); } + s.push_str(&format!("{}m", minutes)); + } + if seconds > 0 { + if hours > 0 || minutes > 0 { s.push_str(" "); } + s.push_str(&format!("{}s", seconds)); + } + s +} + +#[cfg(test)] +mod format_duration_tests { + use super::format_duration; + #[test] + fn no_time() { + assert_eq!(&format_duration(0.), "0s"); + } + #[test] + fn seconds() { + assert_eq!(&format_duration(12.), "12s"); + } + #[test] + fn minutes_seconds() { + assert_eq!(&format_duration(123.), "2m 3s"); + } + #[test] + fn minutes() { + assert_eq!(&format_duration(120.), "2m"); + } + #[test] + fn hours_minutes_seconds() { + assert_eq!(&format_duration(12345.), "3h 25m 45s"); + } + #[test] + fn hours_minutes() { + assert_eq!(&format_duration(9000.), "2h 30m") + } + #[test] + fn hours() { + assert_eq!(&format_duration(3600.), "1h") + } + #[test] + fn negative() { + assert_eq!(&format_duration(-12345.), "-3h 25m 45s"); + } +} + fn main() -> battery::Result<()> { let mut config = Config::new("battery"); @@ -48,8 +163,6 @@ fn main() -> battery::Result<()> { let refresh_interval = config.get_default("default", "refresh interval", 30); - println!("{:?}, {:?}", low_threshold, critical_threshold); - let mut osd = OSD::new(); osd.icon = Some(String::from("battery")); @@ -69,6 +182,7 @@ fn main() -> battery::Result<()> { let mut state: State; let mut last_state: State = State::Normal; + loop { state = match battery.state() { battery::State::Charging => State::Charging, @@ -92,14 +206,14 @@ fn main() -> battery::Result<()> { match state { State::Charging => { battery.time_to_full().map(|ttf| { - osd.title = Some(format!("Charging, {:?} until full", ttf)); + osd.title = Some(format!("Charging, {} until full", format_duration(ttf.value))); osd.urgency = Urgency::Low; osd.update(); }); } State::Low => { battery.time_to_empty().map(|tte| { - osd.title = Some(format!("Low battery, {:?} remaining", tte)); + osd.title = Some(format!("Low battery, {} remaining", format_duration(tte.value))); osd.urgency = Urgency::Normal; osd.update(); }); @@ -110,7 +224,7 @@ fn main() -> battery::Result<()> { if state == State::Critical { battery.time_to_empty().map(|tte| { - osd.title = Some(format!("Critically low battery, {:?} remaining", tte)); + osd.title = Some(format!("Critically low battery, {} remaining", format_duration(tte.value))); osd.urgency = Urgency::Critical; osd.update(); }); diff --git a/common/examples/simple.rs b/common/examples/simple.rs new file mode 100644 index 0000000..9b1b336 --- /dev/null +++ b/common/examples/simple.rs @@ -0,0 +1,54 @@ +extern crate simple_osd_common as osd; + +use osd::notify::{OSD, OSDContents, OSDProgressText, Urgency}; +use osd::config::Config; +use std::time::Duration; +use std::thread::sleep; + +fn main() { + let mut config = Config::new("simple-example"); + + let foo = config.get_default("example section", "foo", "bar baz".to_string()); + + println!("Value of foo is {}", foo); + + let example_no_default = config.get::("example section", "example variable with no default"); + + println!("Value of example variable with no default is {:?}", example_no_default); + + let refresh_interval = config.get_default("default", "refresh interval", 1); + + let mut osd_simple = OSD::new(); + osd_simple.title = Some("Simple (but urgent) notification".to_string()); + osd_simple.contents = OSDContents::Simple(Some("Just simple contents".to_string())); + osd_simple.urgency = Urgency::Critical; + + let mut percentage = 0.; + + let mut osd_progress_bar_percentage = OSD::new(); + osd_progress_bar_percentage.title = Some("A progress bar showing important percentage!".to_string()); + + let eta = 15.; + let mut elapsed = 0.; + + let mut osd_progress_bar_text = OSD::new(); + osd_progress_bar_text.title = Some("Nuclear warhead launch in progress, time left:".to_string()); + osd_progress_bar_text.urgency = Urgency::Low; + + loop { + percentage = (percentage + 0.123) % 1.; + + elapsed = (elapsed + refresh_interval as f32) % eta; + + osd_progress_bar_percentage.contents = OSDContents::Progress(percentage, OSDProgressText::Percentage); + + osd_progress_bar_text.contents = OSDContents::Progress(elapsed / eta, OSDProgressText::Text(Some(format!("{}s / {}s", elapsed, eta)))); + + osd_simple.update(); + osd_progress_bar_percentage.update(); + osd_progress_bar_text.update(); + + sleep(Duration::from_secs(refresh_interval)); + } + +}