use std::time::{Duration, Instant};
use ratatui::Terminal;
use ratatui::backend::TestBackend;
use ratatui::buffer::Buffer;
use ratatui::layout::Rect;
use crate::lookout::app::{App, Msg};
use crate::lookout::frames::scene::Scene;
use crate::lookout::theme::Palette;
use crate::lookout::view::{body_rows, draw};
use super::{armed, flock, link, prepare};
#[must_use]
pub fn scene_with(which: Scene, age: Duration, palette: Palette) -> Buffer {
let t0 = Instant::now();
let mut app = App::new(palette, which.control(), "/home/ada/.shep".to_string(), t0);
let rows = flock::build_flock(&mut app, which, t0);
flock::initialize_flock(&mut app, which, rows, t0);
prepare::prepare_scene(&mut app, which);
link::apply_live_updates(&mut app, which);
link::apply_connection_state(&mut app, which, age);
app.update(Msg::Tick { now: t0 + age });
armed::apply_actions(&mut app, which, t0);
armed::apply_post_tick_scene(&mut app, which);
render_scene(&mut app, which)
}
fn render_scene(app: &mut App, which: Scene) -> Buffer {
let (width, height) = which.size();
app.note_body_rows(body_rows(Rect::new(0, 0, width, height)));
app.note_body_width(width);
let mut terminal = Terminal::new(TestBackend::new(width, height)).unwrap();
terminal.draw(|frame| draw(app, frame)).unwrap();
terminal.backend().buffer().clone()
}
#[cfg(test)]
mod tests {
use crate::lookout::frames::render::render_text;
use crate::lookout::frames::scene;
use super::*;
#[test]
#[cfg(unix)] fn every_size_scene_drops_the_panes_its_geometry_cannot_hold() {
let empty = render_text(&scene(Scene::Empty).1);
assert!(
empty.contains("the flock is empty"),
"the table's own sentence"
);
assert!(
empty.contains("no sheep selected: the flock is empty"),
"the detail pane's"
);
assert!(empty.contains("BLEATS no sheep is selected"), "the feed's");
assert!(
!empty.contains("errored"),
"the summary is past a 100-column cut once host memory precedes it"
);
let narrow = render_text(&scene(Scene::Narrow).1);
assert!(narrow.contains("CPU") && narrow.contains("UPTIME"));
for gone in ["FOLD", "EXIT", "RESTARTS", "PID", "MEM"] {
assert!(!narrow.contains(gone), "the narrow tier dropped {gone}");
}
assert!(narrow.contains("host load"), "the strip is up at 14 rows");
assert!(!narrow.contains("BLEATS"), "the feed is not");
assert!(
!narrow.contains("SHEEP 0 "),
"and neither is the detail pane"
);
let too_narrow = render_text(&scene(Scene::TooNarrow).1);
let mut lines = too_narrow.lines();
assert_eq!(lines.next().unwrap().trim_end(), "too small");
assert_eq!(lines.next().unwrap().trim_end(), "need 33x6");
let no_detail = render_text(&scene(Scene::NoDetail).1);
assert!(
no_detail.contains("BLEATS api"),
"the feed stayed, on the selection"
);
assert!(no_detail.contains("host load"), "and so did the strip");
assert!(
!no_detail.contains("out /home/ada/.shep/logs/"),
"the detail pane went"
);
let table_only = render_text(&scene(Scene::TableOnly).1);
assert!(!table_only.contains("host load"));
assert!(!table_only.contains("BLEATS"));
assert!(table_only.contains("STATUS"), "the table is still there");
let cramped = render_text(&scene(Scene::Cramped).1);
assert!(cramped.contains('…'), "something truncated, visibly");
for marker in ["host ", "BLEATS"] {
assert_eq!(
cramped.lines().filter(|line| line.contains(marker)).count(),
1,
"{marker:?} appears once at 33 columns"
);
}
assert_eq!(
cramped
.lines()
.filter(|line| line.starts_with("out ") && line.contains('\u{2502}'))
.count(),
1,
"the detail pane's merged log row appears once at 33 columns"
);
assert!(
cramped.lines().last().unwrap().contains("control enabled"),
"and the status bar is still the last row"
);
}
#[test]
fn the_frozen_scene_draws_every_column() {
use crate::lookout::view::flock::{GUTTER, columns_for};
let (width, _) = Scene::Frozen.size();
assert_eq!(
columns_for(width - GUTTER).len(),
columns_for(u16::MAX).len(),
"the frozen scene is {width} columns, which drops a column from the widest set"
);
}
}