use lv_tui::prelude::*;
use lv_tui::Component;
#[derive(Component)]
struct OneShot {
#[reactive(paint, copy)]
count: i32,
}
impl OneShot {
fn new() -> Self { Self { count: 0 } }
}
impl Component for OneShot {
fn render(&self, cx: &mut RenderCx) {
cx.line(&format!("count: {}", self.get_count()));
}
fn mount(&mut self, cx: &mut EventCx) {
cx.set_timer(5); }
fn event(&mut self, event: &Event, cx: &mut EventCx) {
if let Event::Timer(_) = event {
self.set_count(self.get_count() + 1, cx);
}
if event.is_key(Key::Char('q')) {
cx.quit();
}
}
}
#[test]
fn set_timer_fires_once() {
let mut pilot = Pilot::new(OneShot::new(), 20, 3);
let fired = pilot.run_until(50, |buf| {
buf.cells.iter().any(|c| c.symbol == "1")
});
assert!(fired.unwrap(), "timer should have fired");
for _ in 0..20 {
let _ = pilot.send_event(Event::Tick);
}
assert!(pilot.frame().cells.iter().any(|c| c.symbol == "1"),
"one-shot timer should not fire again");
assert!(!pilot.frame().cells.iter().any(|c| c.symbol == "2"),
"one-shot timer should fire exactly once");
}
#[derive(Component)]
struct Periodic {
#[reactive(paint, copy)]
ticks: i32,
timer_id: Option<u64>,
}
impl Periodic {
fn new() -> Self { Self { ticks: 0, timer_id: None } }
}
impl Component for Periodic {
fn render(&self, cx: &mut RenderCx) {
cx.line(&format!("ticks: {}", self.get_ticks()));
}
fn mount(&mut self, cx: &mut EventCx) {
self.timer_id = Some(cx.set_interval(5));
}
fn event(&mut self, event: &Event, cx: &mut EventCx) {
if let Event::Timer(_) = event {
self.set_ticks(self.get_ticks() + 1, cx);
}
if event.is_key(Key::Char('q')) {
cx.quit();
}
}
}
#[test]
fn set_interval_fires_multiple_times() {
let mut pilot = Pilot::new(Periodic::new(), 20, 3);
let fired = pilot.run_until(100, |buf| {
buf.cells.iter().any(|c| c.symbol == "3")
});
assert!(fired.unwrap(), "interval timer should fire at least 3 times");
}
#[derive(Component)]
struct CancelTimer {
#[reactive(paint, copy)]
count: i32,
timer_id: Option<u64>,
}
impl CancelTimer {
fn new() -> Self { Self { count: 0, timer_id: None } }
}
impl Component for CancelTimer {
fn render(&self, cx: &mut RenderCx) {
cx.line(&format!("count: {}", self.get_count()));
}
fn mount(&mut self, cx: &mut EventCx) {
self.timer_id = Some(cx.set_interval(5));
}
fn event(&mut self, event: &Event, cx: &mut EventCx) {
match event {
Event::Timer(_) => {
self.set_count(self.get_count() + 1, cx);
if self.get_count() == 1 {
if let Some(id) = self.timer_id.take() {
cx.cancel_timer(id);
}
}
}
_ => {}
}
if event.is_key(Key::Char('q')) {
cx.quit();
}
}
}
#[test]
fn cancel_timer_stops_firing() {
let mut pilot = Pilot::new(CancelTimer::new(), 20, 3);
pilot.run_until(50, |buf| {
buf.cells.iter().any(|c| c.symbol == "1")
}).unwrap();
std::thread::sleep(std::time::Duration::from_millis(30));
for _ in 0..10 {
let _ = pilot.send_event(Event::Tick);
}
assert!(!pilot.frame().cells.iter().any(|c| c.symbol == "2"),
"cancelled timer should not fire again");
}
#[derive(Component)]
struct MultiTimer {
#[reactive(paint, copy)]
a: i32,
#[reactive(paint, copy)]
b: i32,
}
impl MultiTimer {
fn new() -> Self { Self { a: 0, b: 0 } }
}
impl Component for MultiTimer {
fn render(&self, cx: &mut RenderCx) {
cx.line(&format!("a:{} b:{}", self.get_a(), self.get_b()));
}
fn mount(&mut self, cx: &mut EventCx) {
cx.set_interval(5); cx.set_interval(15); }
fn event(&mut self, event: &Event, cx: &mut EventCx) {
if let Event::Timer(_) = event {
self.set_a(self.get_a() + 1, cx);
if self.get_a() >= 3 {
cx.quit();
}
}
}
}
#[test]
fn multiple_timers_on_same_component() {
let mut pilot = Pilot::new(MultiTimer::new(), 20, 3);
let quit = pilot.run_until_quit(100).unwrap();
assert!(quit, "timers should fire and app should quit");
assert!(pilot.frame().cells.iter().any(|c| c.symbol != " "),
"should have rendered timer results");
}
#[derive(Component)]
struct UnmountCleanup {
#[reactive(paint, copy)]
count: i32,
}
impl UnmountCleanup {
fn new() -> Self { Self { count: 0 } }
}
impl Component for UnmountCleanup {
fn render(&self, cx: &mut RenderCx) {
cx.line(&format!("count: {}", self.get_count()));
}
fn mount(&mut self, cx: &mut EventCx) {
cx.set_interval(5);
}
fn event(&mut self, event: &Event, cx: &mut EventCx) {
if let Event::Timer(_) = event {
self.set_count(self.get_count() + 1, cx);
}
if event.is_key(Key::Char('q')) {
cx.quit();
}
}
}
#[test]
fn timer_unmount_cleanup() {
let mut pilot = Pilot::new(UnmountCleanup::new(), 20, 3);
pilot.run_until(50, |buf| {
buf.cells.iter().any(|c| c.symbol == "1")
}).unwrap();
pilot.press(Key::Char('q')).unwrap();
assert!(pilot.has_quit());
}