use std::sync::{Mutex, MutexGuard};
use std::time::Instant;
use ratatui::buffer::Buffer;
use ratatui::layout::Rect;
use typ_core::{Action, Motion, Panel, RenderContext, ThemeColors};
use typ_panel_editor::EditorPanel;
fn big_editor() -> EditorPanel {
let line = " let editor = Editor::new(); // a representative line of code\n";
let text: String = std::iter::repeat_n(line, 50_000).collect();
EditorPanel::from_str(&text)
}
const BUDGET_US: u128 = 16_000;
static EXCLUSIVE: Mutex<()> = Mutex::new(());
fn exclusive() -> MutexGuard<'static, ()> {
EXCLUSIVE
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner())
}
#[test]
#[ignore = "wall-clock budget; run with --release --ignored"]
fn typing_a_character_into_a_large_file_fits_in_a_frame() {
let _guard = exclusive();
let mut editor = big_editor();
editor.perform(Action::InsertChar('x'));
let n = 20;
let start = Instant::now();
for _ in 0..n {
editor.perform(Action::InsertChar('x'));
}
let per_key = start.elapsed() / n;
println!("InsertChar: {per_key:?} per keystroke");
assert!(
per_key.as_micros() < BUDGET_US,
"one keystroke cost {per_key:?}, over the 16ms budget"
);
}
#[test]
#[ignore = "wall-clock budget; run with --release --ignored"]
fn undo_and_redo_on_a_large_file_fit_in_a_frame() {
let _guard = exclusive();
let mut editor = big_editor();
for _ in 0..20 {
editor.perform(Action::InsertChar('x'));
editor.perform(Action::Move {
motion: Motion::Down,
extend: false,
});
}
let n = 20;
let start = Instant::now();
for _ in 0..n {
editor.perform(Action::Undo);
editor.perform(Action::Redo);
}
let per_pair = start.elapsed() / n;
println!("Undo+Redo: {per_pair:?} per pair");
assert!(
per_pair.as_micros() < BUDGET_US * 2,
"an undo/redo pair cost {per_pair:?}, over two frame budgets"
);
}
fn draw_frame(editor: &mut EditorPanel, area: Rect) {
let theme = ThemeColors::default();
let ctx = RenderContext {
theme: &theme,
is_focused: true,
panel_index: 0,
terminal_width: area.width,
terminal_height: area.height,
};
let mut buf = Buffer::empty(area);
editor.render(area, &mut buf, &ctx);
}
#[test]
#[ignore = "wall-clock budget; run with --release --ignored"]
fn drawing_a_frame_deep_in_a_large_file_fits_in_a_frame() {
let _guard = exclusive();
let mut editor = big_editor();
let area = Rect::new(0, 0, 120, 40);
editor.perform(Action::Move {
motion: Motion::DocumentEnd,
extend: false,
});
draw_frame(&mut editor, area);
let n = 50;
let start = Instant::now();
for _ in 0..n {
draw_frame(&mut editor, area);
}
let per_frame = start.elapsed() / n;
println!("render, deep in a 50k-line file: {per_frame:?} per frame");
assert!(
per_frame.as_micros() < BUDGET_US,
"one frame cost {per_frame:?}, over the 16ms budget"
);
}
#[test]
#[ignore = "wall-clock budget; run with --release --ignored"]
fn an_unmatched_bracket_does_not_walk_the_file() {
let _guard = exclusive();
let mut editor = EditorPanel::from_str(&format!("(\n{}", "filler\n".repeat(50_000)));
let area = Rect::new(0, 0, 120, 40);
draw_frame(&mut editor, area);
let n = 50;
let start = Instant::now();
for _ in 0..n {
draw_frame(&mut editor, area);
}
let per_frame = start.elapsed() / n;
println!("render with an unmatched bracket at the cursor: {per_frame:?} per frame");
assert!(
per_frame.as_micros() < BUDGET_US,
"one frame cost {per_frame:?}, over the 16ms budget"
);
}