use super::ms;
pub struct Timer {
listen_period: ms,
last_speak_time: ms,
}
impl Timer {
pub fn new(listen_period: ms) -> Self {
Self {
listen_period,
last_speak_time: 0,
}
}
pub fn is_time_to_speak(&self, current_time: ms) -> bool {
current_time.wrapping_sub(self.last_speak_time) >= self.listen_period
}
pub fn record_speak_time(&mut self, current_time: ms) {
self.last_speak_time = current_time;
}
}
#[cfg(test)]
mod tests {
use super::*;
const PERIOD: ms = 100;
#[test]
fn does_not_speak_before_period_elapses() {
let mut timer = Timer::new(PERIOD);
timer.record_speak_time(1000);
assert!(!timer.is_time_to_speak(1099));
}
#[test]
fn speaks_when_period_elapses() {
let mut timer = Timer::new(PERIOD);
timer.record_speak_time(1000);
assert!(timer.is_time_to_speak(1100));
}
#[test]
fn does_not_speak_before_period_elapses_across_u32_wraparound() {
let mut timer = Timer::new(PERIOD);
let last_speak: ms = u32::MAX - 50;
timer.record_speak_time(last_speak);
let current = last_speak.wrapping_add(PERIOD - 1);
assert!(
!timer.is_time_to_speak(current),
"fired after only {}ms; old code: ({} + {}) overflows to {}, then {} > {} is true",
PERIOD - 1,
last_speak,
PERIOD,
last_speak.wrapping_add(PERIOD),
current,
last_speak.wrapping_add(PERIOD),
);
}
#[test]
fn speaks_after_period_elapses_across_u32_wraparound() {
let mut timer = Timer::new(PERIOD);
let last_speak: ms = u32::MAX - 50;
timer.record_speak_time(last_speak);
let current = last_speak.wrapping_add(PERIOD);
assert!(timer.is_time_to_speak(current));
}
}