use revue::layout::Rect;
use revue::render::Buffer;
use revue::style::Color;
use revue::widget::traits::{RenderContext, StyledView};
use revue::widget::{Timer, TimerFormat, TimerState, View};
use std::thread;
use std::time::{Duration, Instant};
fn poll_until<F>(mut condition: F, timeout_ms: u64) -> bool
where
F: FnMut() -> bool,
{
let start = Instant::now();
let timeout = Duration::from_millis(timeout_ms);
let poll_interval = Duration::from_millis(5);
while start.elapsed() < timeout {
if condition() {
return true;
}
thread::sleep(poll_interval);
}
false
}
#[test]
fn test_timer_countdown_new() {
let timer = Timer::countdown(60);
assert_eq!(timer.remaining_seconds(), 60);
assert_eq!(timer.state(), TimerState::Stopped);
assert!(!timer.is_running());
assert!(!timer.is_completed());
}
#[test]
fn test_timer_countdown_zero() {
let timer = Timer::countdown(0);
assert_eq!(timer.remaining_seconds(), 0);
assert_eq!(timer.state(), TimerState::Stopped);
}
#[test]
fn test_timer_countdown_large() {
let timer = Timer::countdown(3600); assert_eq!(timer.remaining_seconds(), 3600);
assert_eq!(timer.state(), TimerState::Stopped);
}
#[test]
fn test_timer_pomodoro() {
let timer = Timer::pomodoro();
assert_eq!(timer.remaining_seconds(), 25 * 60);
}
#[test]
fn test_timer_short_break() {
let timer = Timer::short_break();
assert_eq!(timer.remaining_seconds(), 5 * 60);
}
#[test]
fn test_timer_long_break() {
let timer = Timer::long_break();
assert_eq!(timer.remaining_seconds(), 15 * 60);
}
#[test]
fn test_timer_format_full() {
let timer = Timer::countdown(3661).format(TimerFormat::Full);
assert_eq!(timer.format_remaining(), "01:01:01");
}
#[test]
fn test_timer_format_short() {
let timer = Timer::countdown(65).format(TimerFormat::Short);
assert_eq!(timer.format_remaining(), "01:05");
}
#[test]
fn test_timer_format_short_with_hours() {
let timer = Timer::countdown(3661).format(TimerFormat::Short);
assert_eq!(timer.format_remaining(), "01:01:01");
}
#[test]
fn test_timer_format_precise() {
let timer = Timer::countdown(0).format(TimerFormat::Precise);
let formatted = timer.format_remaining();
assert!(formatted.contains('.'));
assert!(formatted.len() >= 4); }
#[test]
fn test_timer_format_compact_hours() {
let timer = Timer::countdown(3661).format(TimerFormat::Compact);
assert_eq!(timer.format_remaining(), "1h 1m");
}
#[test]
fn test_timer_format_compact_minutes() {
let timer = Timer::countdown(125).format(TimerFormat::Compact);
assert_eq!(timer.format_remaining(), "2m 5s");
}
#[test]
fn test_timer_format_compact_seconds() {
let timer = Timer::countdown(30).format(TimerFormat::Compact);
assert_eq!(timer.format_remaining(), "30s");
}
#[test]
fn test_timer_show_progress() {
let _timer = Timer::countdown(60).show_progress(false);
let _timer2 = Timer::countdown(60).show_progress(true);
}
#[test]
fn test_timer_progress_width() {
let _timer = Timer::countdown(60).progress_width(50);
}
#[test]
fn test_timer_fg() {
let _timer = Timer::countdown(60).fg(Color::BLUE);
}
#[test]
fn test_timer_warning_threshold() {
let _timer = Timer::countdown(60).warning_threshold(30);
}
#[test]
fn test_timer_danger_threshold() {
let _timer = Timer::countdown(60).danger_threshold(15);
}
#[test]
fn test_timer_title() {
let _timer = Timer::countdown(60).title("Test Timer");
}
#[test]
fn test_timer_large_digits() {
let _timer = Timer::countdown(60).large_digits(true);
let _timer2 = Timer::countdown(60).large_digits(false);
}
#[test]
fn test_timer_auto_restart_builder() {
let _timer = Timer::countdown(60).auto_restart(true);
let _timer2 = Timer::countdown(60).auto_restart(false);
}
#[test]
fn test_timer_element_id() {
let timer = Timer::countdown(60).element_id("my-timer");
assert_eq!(View::id(&timer), Some("my-timer"));
}
#[test]
fn test_timer_class() {
let timer = Timer::countdown(60).class("countdown").class("urgent");
assert!(timer.has_class("countdown"));
assert!(timer.has_class("urgent"));
}
#[test]
fn test_timer_classes() {
let timer = Timer::countdown(60).classes(vec!["class1", "class2", "class3"]);
assert!(timer.has_class("class1"));
assert!(timer.has_class("class2"));
assert!(timer.has_class("class3"));
}
#[test]
fn test_timer_start() {
let mut timer = Timer::countdown(60);
assert_eq!(timer.state(), TimerState::Stopped);
timer.start();
assert_eq!(timer.state(), TimerState::Running);
assert!(timer.is_running());
}
#[test]
fn test_timer_pause() {
let mut timer = Timer::countdown(60);
timer.start();
assert_eq!(timer.state(), TimerState::Running);
timer.pause();
assert_eq!(timer.state(), TimerState::Paused);
assert!(!timer.is_running());
}
#[test]
fn test_timer_pause_from_non_running() {
let mut timer = Timer::countdown(60);
timer.pause();
assert_eq!(timer.state(), TimerState::Stopped);
}
#[test]
fn test_timer_stop() {
let mut timer = Timer::countdown(60);
timer.start();
timer.pause();
timer.stop();
assert_eq!(timer.state(), TimerState::Stopped);
assert_eq!(timer.remaining_seconds(), 60);
}
#[test]
fn test_timer_reset() {
let mut timer = Timer::countdown(60);
timer.start();
poll_until(
|| {
timer.update();
timer.remaining_seconds() < 60
},
500,
);
let remaining = timer.remaining_seconds();
timer.reset();
assert_eq!(timer.remaining_seconds(), 60);
assert!(timer.remaining_seconds() > remaining || remaining == 60);
}
#[test]
fn test_timer_toggle_from_stopped() {
let mut timer = Timer::countdown(60);
assert_eq!(timer.state(), TimerState::Stopped);
timer.toggle();
assert_eq!(timer.state(), TimerState::Running);
}
#[test]
fn test_timer_toggle_from_running() {
let mut timer = Timer::countdown(60);
timer.start();
assert_eq!(timer.state(), TimerState::Running);
timer.toggle();
assert_eq!(timer.state(), TimerState::Paused);
}
#[test]
fn test_timer_toggle_from_paused() {
let mut timer = Timer::countdown(60);
timer.start();
timer.pause();
assert_eq!(timer.state(), TimerState::Paused);
timer.toggle();
assert_eq!(timer.state(), TimerState::Running);
}
#[test]
fn test_timer_toggle_from_completed() {
let mut timer = Timer::countdown(0); timer.toggle();
assert_eq!(timer.state(), TimerState::Running);
}
#[test]
fn test_timer_resume_from_pause() {
let mut timer = Timer::countdown(60);
timer.start();
poll_until(
|| {
timer.update();
timer.remaining_seconds() < 60
},
500,
);
timer.pause();
let paused_remaining = timer.remaining_seconds();
timer.start();
assert_eq!(timer.state(), TimerState::Running);
assert!(timer.remaining_seconds() <= paused_remaining);
}
#[test]
fn test_timer_progress_initial() {
let timer = Timer::countdown(60);
assert_eq!(timer.progress(), 0.0);
}
#[test]
fn test_timer_progress_half() {
let mut timer = Timer::countdown(60);
let initial = timer.progress();
timer.start();
poll_until(
|| {
timer.update();
timer.progress() > initial
},
500,
);
assert!(timer.progress() > initial);
}
#[test]
fn test_timer_progress_completed() {
let mut timer = Timer::countdown(0);
timer.start();
timer.update();
assert_eq!(timer.progress(), 1.0);
}
#[test]
fn test_timer_progress_zero_duration() {
let timer = Timer::countdown(0);
assert_eq!(timer.progress(), 1.0);
}
#[test]
fn test_timer_progress_increases() {
let mut timer = Timer::countdown(60);
let initial_progress = timer.progress();
timer.start();
poll_until(
|| {
timer.update();
timer.progress() > initial_progress
},
500,
);
let later_progress = timer.progress();
assert!(later_progress > initial_progress);
}
#[test]
fn test_timer_update_when_stopped() {
let mut timer = Timer::countdown(60);
timer.update();
assert_eq!(timer.state(), TimerState::Stopped);
assert_eq!(timer.remaining_seconds(), 60);
}
#[test]
fn test_timer_update_when_paused() {
let mut timer = Timer::countdown(60);
timer.start();
timer.pause();
timer.update();
assert_eq!(timer.state(), TimerState::Paused);
}
#[test]
fn test_timer_update_decrements_time() {
let mut timer = Timer::countdown(60);
timer.start();
poll_until(
|| {
timer.update();
timer.remaining_seconds() < 60
},
500,
);
assert!(timer.remaining_seconds() < 60);
assert!(timer.remaining_seconds() > 0);
}
#[test]
fn test_timer_update_to_completion() {
let mut timer = Timer::countdown(0);
timer.start();
timer.update();
assert_eq!(timer.state(), TimerState::Completed);
assert!(timer.is_completed());
assert_eq!(timer.remaining_seconds(), 0);
}
#[test]
fn test_timer_auto_restart() {
let mut timer = Timer::countdown(0).auto_restart(true);
timer.start();
timer.update();
assert_eq!(timer.state(), TimerState::Running);
assert!(!timer.is_completed());
}
#[test]
fn test_timer_color_normal() {
let timer = Timer::countdown(120); assert_eq!(timer.remaining_seconds(), 120);
}
#[test]
fn test_timer_color_warning() {
let timer = Timer::countdown(45).warning_threshold(30);
assert!(timer.remaining_seconds() > 30); }
#[test]
fn test_timer_color_danger() {
let timer = Timer::countdown(10).danger_threshold(15);
assert!(timer.remaining_seconds() <= 15); }
#[test]
fn test_timer_color_custom() {
let _timer = Timer::countdown(60).fg(Color::CYAN);
}
#[test]
fn test_timer_render_basic() {
let mut buffer = Buffer::new(20, 5);
let area = Rect::new(0, 0, 20, 5);
let mut ctx = RenderContext::new(&mut buffer, area);
let timer = Timer::countdown(60);
timer.render(&mut ctx);
let mut found_time = false;
for y in 0..5 {
for x in 0..20 {
if let Some(cell) = buffer.get(x, y) {
if cell.symbol.is_ascii_digit() || cell.symbol == ':' {
found_time = true;
break;
}
}
}
}
assert!(found_time);
}
#[test]
fn test_timer_render_with_title() {
let mut buffer = Buffer::new(20, 5);
let area = Rect::new(0, 0, 20, 5);
let mut ctx = RenderContext::new(&mut buffer, area);
let timer = Timer::countdown(60).title("Test");
timer.render(&mut ctx);
assert_eq!(buffer.get(0, 0).unwrap().symbol, 'T');
assert_eq!(buffer.get(1, 0).unwrap().symbol, 'e');
assert_eq!(buffer.get(2, 0).unwrap().symbol, 's');
assert_eq!(buffer.get(3, 0).unwrap().symbol, 't');
}
#[test]
fn test_timer_render_stopped_state() {
let mut buffer = Buffer::new(20, 5);
let area = Rect::new(0, 0, 20, 5);
let mut ctx = RenderContext::new(&mut buffer, area);
let timer = Timer::countdown(60);
timer.render(&mut ctx);
let mut found_stopped = false;
for y in 0..5 {
for x in 0..20 {
if let Some(cell) = buffer.get(x, y) {
if cell.symbol == 'S' {
found_stopped = true;
break;
}
}
}
}
assert!(found_stopped);
}
#[test]
fn test_timer_render_running_state() {
let mut buffer = Buffer::new(20, 5);
let area = Rect::new(0, 0, 20, 5);
let mut ctx = RenderContext::new(&mut buffer, area);
let mut timer = Timer::countdown(60);
timer.start();
timer.render(&mut ctx);
let mut found_running = false;
for y in 0..5 {
for x in 0..20 {
if let Some(cell) = buffer.get(x, y) {
if cell.symbol == 'R' {
found_running = true;
break;
}
}
}
}
assert!(found_running);
}
#[test]
fn test_timer_render_paused_state() {
let mut buffer = Buffer::new(20, 5);
let area = Rect::new(0, 0, 20, 5);
let mut ctx = RenderContext::new(&mut buffer, area);
let mut timer = Timer::countdown(60);
timer.start();
timer.pause();
timer.render(&mut ctx);
let mut found_paused = false;
for y in 0..5 {
for x in 0..20 {
if let Some(cell) = buffer.get(x, y) {
if cell.symbol == 'P' {
found_paused = true;
break;
}
}
}
}
assert!(found_paused);
}
#[test]
fn test_timer_render_completed_state() {
let mut buffer = Buffer::new(20, 5);
let area = Rect::new(0, 0, 20, 5);
let mut ctx = RenderContext::new(&mut buffer, area);
let mut timer = Timer::countdown(0);
timer.start();
timer.update();
timer.render(&mut ctx);
let mut found_completed = false;
for y in 0..5 {
for x in 0..20 {
if let Some(cell) = buffer.get(x, y) {
if cell.symbol == 'C' {
found_completed = true;
break;
}
}
}
}
assert!(found_completed);
}
#[test]
fn test_timer_render_without_progress() {
let mut buffer = Buffer::new(20, 5);
let area = Rect::new(0, 0, 20, 5);
let mut ctx = RenderContext::new(&mut buffer, area);
let timer = Timer::countdown(60).show_progress(false);
timer.render(&mut ctx);
}
#[test]
fn test_timer_render_with_progress() {
let mut buffer = Buffer::new(30, 5);
let area = Rect::new(0, 0, 30, 5);
let mut ctx = RenderContext::new(&mut buffer, area);
let timer = Timer::countdown(60).show_progress(true).progress_width(20);
timer.render(&mut ctx);
}
#[test]
fn test_timer_render_small_buffer() {
let mut buffer = Buffer::new(5, 3);
let area = Rect::new(0, 0, 5, 3);
let mut ctx = RenderContext::new(&mut buffer, area);
let timer = Timer::countdown(60);
timer.render(&mut ctx);
}
#[test]
fn test_timer_render_with_offset() {
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(10, 3, 20, 5);
let mut ctx = RenderContext::new(&mut buffer, area);
let timer = Timer::countdown(60);
timer.render(&mut ctx);
}
#[test]
fn test_timer_helper_function() {
let t = Timer::countdown(120);
assert_eq!(t.remaining_seconds(), 120);
assert_eq!(t.state(), TimerState::Stopped);
}
#[test]
fn test_timer_zero_duration() {
let timer = Timer::countdown(0);
assert_eq!(timer.remaining_seconds(), 0);
assert_eq!(timer.format_remaining(), "00:00:00");
}
#[test]
fn test_timer_very_short_duration() {
let timer = Timer::countdown(1);
assert_eq!(timer.remaining_seconds(), 1);
}
#[test]
fn test_timer_very_long_duration() {
let timer = Timer::countdown(86400); assert_eq!(timer.remaining_seconds(), 86400);
assert_eq!(timer.format_remaining(), "24:00:00");
}
#[test]
fn test_timer_multiple_starts() {
let mut timer = Timer::countdown(60);
timer.start();
timer.start();
timer.start();
assert_eq!(timer.state(), TimerState::Running);
}
#[test]
fn test_timer_multiple_pauses() {
let mut timer = Timer::countdown(60);
timer.start();
timer.pause();
timer.pause();
assert_eq!(timer.state(), TimerState::Paused);
}
#[test]
fn test_timer_format_zero_remaining() {
let timer = Timer::countdown(0);
assert_eq!(timer.format_remaining(), "00:00:00");
}
#[test]
fn test_timer_format_precise_zero() {
let timer = Timer::countdown(0).format(TimerFormat::Precise);
let formatted = timer.format_remaining();
assert!(formatted.starts_with("00"));
}
#[test]
fn test_timer_threshold_edge_case() {
let timer = Timer::countdown(10)
.warning_threshold(10)
.danger_threshold(5);
assert_eq!(timer.remaining_seconds(), 10);
}
#[test]
fn test_timer_danger_threshold_edge_case() {
let timer = Timer::countdown(5).danger_threshold(5);
assert_eq!(timer.remaining_seconds(), 5);
}
#[test]
fn test_timer_format_compact_zero() {
let timer = Timer::countdown(0).format(TimerFormat::Compact);
assert_eq!(timer.format_remaining(), "0s");
}
#[test]
fn test_timer_progress_clamp() {
let timer = Timer::countdown(60);
let progress = timer.progress();
assert!(progress >= 0.0);
assert!(progress <= 1.0);
}
#[test]
fn test_timer_styled_view_set_id() {
let mut timer = Timer::countdown(60);
StyledView::set_id(&mut timer, "test-timer-id");
assert_eq!(View::id(&timer), Some("test-timer-id"));
}
#[test]
fn test_timer_styled_view_add_class() {
let mut timer = Timer::countdown(60);
StyledView::add_class(&mut timer, "first");
StyledView::add_class(&mut timer, "second");
assert!(StyledView::has_class(&timer, "first"));
assert!(StyledView::has_class(&timer, "second"));
assert_eq!(View::classes(&timer).len(), 2);
}
#[test]
fn test_timer_styled_view_add_class_no_duplicates() {
let mut timer = Timer::countdown(60);
StyledView::add_class(&mut timer, "test");
StyledView::add_class(&mut timer, "test");
let classes = View::classes(&timer);
assert_eq!(classes.len(), 1);
assert!(classes.contains(&"test".to_string()));
}
#[test]
fn test_timer_styled_view_remove_class() {
let mut timer = Timer::countdown(60).class("a").class("b").class("c");
StyledView::remove_class(&mut timer, "b");
assert!(StyledView::has_class(&timer, "a"));
assert!(!StyledView::has_class(&timer, "b"));
assert!(StyledView::has_class(&timer, "c"));
}
#[test]
fn test_timer_styled_view_remove_nonexistent_class() {
let mut timer = Timer::countdown(60).class("test");
StyledView::remove_class(&mut timer, "nonexistent");
assert!(StyledView::has_class(&timer, "test"));
}
#[test]
fn test_timer_styled_view_toggle_class_add() {
let mut timer = Timer::countdown(60);
StyledView::toggle_class(&mut timer, "test");
assert!(StyledView::has_class(&timer, "test"));
}
#[test]
fn test_timer_styled_view_toggle_class_remove() {
let mut timer = Timer::countdown(60).class("test");
StyledView::toggle_class(&mut timer, "test");
assert!(!StyledView::has_class(&timer, "test"));
}
#[test]
fn test_timer_styled_view_has_class() {
let timer = Timer::countdown(60).class("present");
assert!(StyledView::has_class(&timer, "present"));
assert!(!StyledView::has_class(&timer, "absent"));
}
#[test]
fn test_timer_view_widget_type() {
let timer = Timer::countdown(60);
assert_eq!(timer.widget_type(), "Timer");
}
#[test]
fn test_timer_view_id_none() {
let timer = Timer::countdown(60);
assert!(View::id(&timer).is_none());
}
#[test]
fn test_timer_view_id_some() {
let timer = Timer::countdown(60).element_id("my-timer");
assert_eq!(View::id(&timer), Some("my-timer"));
}
#[test]
fn test_timer_view_classes_empty() {
let timer = Timer::countdown(60);
assert!(View::classes(&timer).is_empty());
}
#[test]
fn test_timer_view_classes_with_values() {
let timer = Timer::countdown(60).class("first").class("second");
let classes = View::classes(&timer);
assert_eq!(classes.len(), 2);
assert!(classes.contains(&"first".to_string()));
assert!(classes.contains(&"second".to_string()));
}
#[test]
fn test_timer_view_meta() {
let timer = Timer::countdown(60)
.element_id("test-id")
.class("test-class");
let meta = timer.meta();
assert_eq!(meta.widget_type, "Timer");
assert_eq!(meta.id, Some("test-id".to_string()));
assert!(meta.classes.contains("test-class"));
}
#[test]
fn test_timer_view_children_default() {
let timer = Timer::countdown(60);
assert!(View::children(&timer).is_empty());
}
#[test]
fn test_timer_format_eq() {
assert_eq!(TimerFormat::Full, TimerFormat::Full);
assert_eq!(TimerFormat::Short, TimerFormat::Short);
assert_eq!(TimerFormat::Precise, TimerFormat::Precise);
assert_eq!(TimerFormat::Compact, TimerFormat::Compact);
}
#[test]
fn test_timer_format_ne() {
assert_ne!(TimerFormat::Full, TimerFormat::Short);
assert_ne!(TimerFormat::Precise, TimerFormat::Compact);
assert_ne!(TimerFormat::Full, TimerFormat::Precise);
}
#[test]
fn test_timer_state_eq() {
assert_eq!(TimerState::Stopped, TimerState::Stopped);
assert_eq!(TimerState::Running, TimerState::Running);
assert_eq!(TimerState::Paused, TimerState::Paused);
assert_eq!(TimerState::Completed, TimerState::Completed);
}
#[test]
fn test_timer_state_ne() {
assert_ne!(TimerState::Stopped, TimerState::Running);
assert_ne!(TimerState::Running, TimerState::Paused);
assert_ne!(TimerState::Paused, TimerState::Completed);
}
#[test]
fn test_timer_clone() {
let timer = Timer::countdown(60)
.title("Test")
.fg(Color::RED)
.element_id("test-id")
.class("test-class");
let cloned = timer.clone();
assert_eq!(cloned.remaining_seconds(), 60);
assert_eq!(View::id(&cloned), Some("test-id"));
assert!(cloned.has_class("test-class"));
}
#[test]
fn test_timer_default_format() {
let timer = Timer::countdown(60);
assert_eq!(timer.format_remaining(), "00:01:00");
}
#[test]
fn test_timer_default_show_progress() {
let _timer = Timer::countdown(60);
}
#[test]
fn test_timer_default_progress_width() {
let _timer = Timer::countdown(60);
}
#[test]
fn test_timer_default_warning_threshold() {
let _timer = Timer::countdown(60);
}
#[test]
fn test_timer_default_danger_threshold() {
let _timer = Timer::countdown(60);
}
#[test]
fn test_timer_default_large_digits() {
let _timer = Timer::countdown(60);
}
#[test]
fn test_timer_default_auto_restart() {
let _timer = Timer::countdown(60);
}
#[test]
fn test_timer_tick_integration() {
let mut timer = Timer::countdown(1); timer.start();
poll_until(
|| {
timer.update();
timer.remaining_seconds() < 1
},
500,
);
assert!(timer.remaining_seconds() < 1);
assert!(!timer.is_completed());
}
#[test]
fn test_timer_completion_flow() {
let mut timer = Timer::countdown(0);
assert!(!timer.is_completed());
timer.start();
timer.update();
assert!(timer.is_completed());
assert_eq!(timer.state(), TimerState::Completed);
}
#[test]
fn test_timer_restart_after_completion() {
let mut timer = Timer::countdown(0);
timer.start();
timer.update();
assert!(timer.is_completed());
timer.stop();
assert!(!timer.is_completed());
assert_eq!(timer.state(), TimerState::Stopped);
assert_eq!(timer.remaining_seconds(), 0);
}
#[test]
fn test_timer_chained_builder_methods() {
let timer = Timer::countdown(120)
.format(TimerFormat::Short)
.show_progress(true)
.progress_width(25)
.fg(Color::GREEN)
.warning_threshold(30)
.danger_threshold(10)
.title("Chain Test")
.large_digits(true)
.auto_restart(false)
.element_id("chained")
.class("timer")
.class("countdown");
assert_eq!(timer.remaining_seconds(), 120);
assert_eq!(timer.format_remaining(), "02:00"); assert_eq!(View::id(&timer), Some("chained"));
assert!(timer.has_class("timer"));
assert!(timer.has_class("countdown"));
}