Add tests and an example

This commit is contained in:
Alexander Bantyev 2020-09-17 00:19:02 +03:00
parent 625e2355b9
commit 640e6dd6bf
Signed by: balsoft
GPG Key ID: E081FF12ADCB4AD5
3 changed files with 177 additions and 9 deletions

View File

@ -8,4 +8,4 @@ edition = "2018"
[dependencies] [dependencies]
battery = "0.7.6" battery = "0.7.6"
simple-osd-common = { version = "0.1", path = "../common" } simple-osd-common = { version = "0.1", path = "../common" }

View File

@ -11,7 +11,7 @@ use std::time::Duration;
use osd::notify::{OSD, Urgency}; use osd::notify::{OSD, Urgency};
use osd::config::Config; use osd::config::Config;
#[derive(Debug)] #[derive(Debug, Eq, PartialEq)]
enum Threshold { enum Threshold {
Percentage(i32), Percentage(i32),
Minutes(i32) Minutes(i32)
@ -25,18 +25,133 @@ enum State {
Normal Normal
} }
fn threshold_sane(thresh: Threshold) -> Option<Threshold> {
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<Threshold> { fn parse_threshold(thresh: String) -> Option<Threshold> {
let mut s = thresh.clone(); let mut s = thresh.clone();
let last = s.pop(); let last = s.pop();
let parsed = s.parse();
match last { match last {
Some('%') => s.parse().map(Threshold::Percentage).ok(), Some('%') => parsed.map(Threshold::Percentage).ok().and_then(threshold_sane),
Some('m') => s.parse().map(Threshold::Minutes).ok(), Some('m') => parsed.map(Threshold::Minutes).ok().and_then(threshold_sane),
_ => None _ => 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<()> { fn main() -> battery::Result<()> {
let mut config = Config::new("battery"); let mut config = Config::new("battery");
@ -48,8 +163,6 @@ fn main() -> battery::Result<()> {
let refresh_interval = config.get_default("default", "refresh interval", 30); let refresh_interval = config.get_default("default", "refresh interval", 30);
println!("{:?}, {:?}", low_threshold, critical_threshold);
let mut osd = OSD::new(); let mut osd = OSD::new();
osd.icon = Some(String::from("battery")); osd.icon = Some(String::from("battery"));
@ -69,6 +182,7 @@ fn main() -> battery::Result<()> {
let mut state: State; let mut state: State;
let mut last_state: State = State::Normal; let mut last_state: State = State::Normal;
loop { loop {
state = match battery.state() { state = match battery.state() {
battery::State::Charging => State::Charging, battery::State::Charging => State::Charging,
@ -92,14 +206,14 @@ fn main() -> battery::Result<()> {
match state { match state {
State::Charging => { State::Charging => {
battery.time_to_full().map(|ttf| { 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.urgency = Urgency::Low;
osd.update(); osd.update();
}); });
} }
State::Low => { State::Low => {
battery.time_to_empty().map(|tte| { 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.urgency = Urgency::Normal;
osd.update(); osd.update();
}); });
@ -110,7 +224,7 @@ fn main() -> battery::Result<()> {
if state == State::Critical { if state == State::Critical {
battery.time_to_empty().map(|tte| { 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.urgency = Urgency::Critical;
osd.update(); osd.update();
}); });

54
common/examples/simple.rs Normal file
View File

@ -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::<i32>("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));
}
}