1use std::time::{Duration, Instant};
2
3use crossterm::event::KeyEvent;
4use ratatui::{Terminal, backend::TestBackend, buffer::Buffer};
5
6use crate::pace::FRAME;
7use crate::shell::{Flow, Tui};
8
9#[derive(Debug, Clone, PartialEq, Eq)]
10#[non_exhaustive]
11pub struct Bench {
12 pub screen: String,
13 pub frames: usize,
14 pub average: Duration,
15 pub worst: Duration,
16}
17
18fn terminal(width: u16, height: u16) -> Terminal<TestBackend> {
19 match Terminal::new(TestBackend::new(width, height)) {
20 Ok(terminal) => terminal,
21 Err(never) => match never {},
22 }
23}
24
25impl<E: Send + 'static> Tui<E> {
26 pub fn frame(&mut self, width: u16, height: u16) -> Buffer {
27 let mut terminal = terminal(width, height);
28 if let Err(never) = terminal.draw(|frame| self.draw(frame)) {
29 match never {}
30 }
31 terminal.backend().buffer().clone()
32 }
33
34 pub fn shot(&mut self, width: u16, height: u16, keys: &[KeyEvent], settle: bool) -> Buffer {
35 self.start();
36 self.ready(settle);
37 for key in keys {
38 self.frame(width, height);
39 if self.key(*key) == Flow::Quit {
40 break;
41 }
42 self.ready(settle);
43 }
44 self.frame(width, height)
45 }
46
47 fn ready(&mut self, settle: bool) {
48 if settle {
49 self.settle();
50 } else {
51 drop(self.reads());
52 }
53 }
54
55 pub fn bench(&mut self, width: u16, height: u16, frames: usize) -> Vec<Bench> {
56 let names: Vec<String> = self.names().into_iter().map(str::to_string).collect();
57 let mut terminal = terminal(width, height);
58 let start = self.moment;
59 let mut out = Vec::with_capacity(names.len());
60 for (index, screen) in names.into_iter().enumerate() {
61 if !self.go(index) {
62 continue;
63 }
64 self.settle();
65 let mut total = Duration::ZERO;
66 let mut worst = Duration::ZERO;
67 for frame in 0..frames {
68 self.moment = start + FRAME * u32::try_from(frame).unwrap_or(u32::MAX);
69 let began = Instant::now();
70 if let Err(never) = terminal.draw(|frame| self.draw(frame)) {
71 match never {}
72 }
73 let spent = began.elapsed();
74 total += spent;
75 worst = worst.max(spent);
76 }
77 self.moment = start;
78 out.push(Bench {
79 screen,
80 frames,
81 average: total / u32::try_from(frames.max(1)).unwrap_or(u32::MAX),
82 worst,
83 });
84 }
85 self.open = None;
86 out
87 }
88}