use super::*;
#[test]
#[ignore = "offline typing-under-load measurement; run with --release --ignored --nocapture --test-threads=1"]
fn profile_typing_under_streaming_load() {
const WARMUP: usize = 10;
const SAMPLES: usize = 100;
const DRAFT: &str = "benchmark draft ";
for (scenario, history_turns, queued_events) in [
("typing_idle", 0, 0),
("typing_streaming", 0, 1024),
("typing_streaming_history", 120, 1024),
] {
let temp = tempfile::TempDir::new().unwrap();
let (sender, receiver) = bounded::<TuiEvent>(1024);
let mut app = profiling_app(&temp, sender.clone());
let mut ui_state = state::MissionControlState {
focus_pane: state::TuiFocusPane::Prompt,
..Default::default()
};
for turn in 0..history_turns {
ui_state.apply_output_event(&OutputEvent::UserPrompt {
text: format!("Inspect synthetic turn {turn}"),
});
ui_state.apply_output_event(&OutputEvent::AssistantComplete {
text: include_str!(
"../../../../../scripts/fixtures/cpu_profiling/rendering_transcript.md"
)
.to_string(),
});
}
let area = ratatui::layout::Rect::new(0, 0, 120, 36);
let mut terminal =
ratatui::Terminal::new(ratatui::backend::TestBackend::new(area.width, area.height))
.unwrap();
let mut last_area = area;
let mut redraw = RedrawIntent::None;
let mut budget = DrainLoopBudgetState::default();
let mut candidates = Vec::new();
let mut clipboard = ProfilingClipboard;
let mut samples = Vec::with_capacity(SAMPLES);
for sample in 0..WARMUP + SAMPLES {
ui_state.set_prompt_text(DRAFT, DRAFT.len());
while receiver.len() < queued_events {
sender
.try_send(TuiEvent::Output(OutputEvent::AssistantDelta {
text: "streaming output\n".to_string(),
}))
.unwrap();
}
let typed = if sample % 2 == 0 { "x" } else { "y" };
let event = TerminalInputEvent::KeyBurst(typed.to_string());
let revision = ui_state.prompt_content_revision();
let started = Instant::now();
let outcome = drain_tui_events_budgeted(&receiver, &mut ui_state, UI_DRAIN_BUDGET);
let drained_at = Instant::now();
budget.apply_outcome(&outcome);
let command = classify_prompt_key_burst_event(event, &ui_state, area, &candidates)
.expect("typed character should edit prompt");
let (flow, _) = app.reduce_input_command(
command,
&mut ui_state,
&mut actions::ActionContext {
receiver: &receiver,
autocomplete_candidates: &mut candidates,
clipboard: &mut clipboard,
last_area: area,
},
revision,
);
budget.request_input_paint(&mut redraw, false, drained_at);
draw_frame(
&mut terminal,
&mut ui_state,
&mut last_area,
&receiver,
&mut redraw,
&mut budget,
)
.unwrap();
let drawn_at = Instant::now();
assert!(matches!(flow, actions::AppControlFlow::Continue));
let expected = format!("{DRAFT}{typed}");
assert_eq!(ui_state.prompt_plain_text(), expected);
let prompt_area = render::layout_for(area, &ui_state).prompt;
let buffer = terminal.backend().buffer();
assert!(
(prompt_area.y..prompt_area.bottom()).any(|y| {
(prompt_area.x..prompt_area.right())
.map(|x| buffer[(x, y)].symbol())
.collect::<String>()
.contains(&expected)
}),
"typed draft must reach drawn prompt while output remains queued"
);
assert_eq!(receiver.len(), queued_events - outcome.processed);
if queued_events > 0 {
assert!(outcome.processed > 0 && !receiver.is_empty());
assert!(ui_state.assistant_streaming);
}
if sample >= WARMUP {
samples.push([
drained_at.duration_since(started).as_secs_f64() * 1000.0,
drawn_at.duration_since(drained_at).as_secs_f64() * 1000.0,
drawn_at.duration_since(started).as_secs_f64() * 1000.0,
outcome.processed as f64,
receiver.len() as f64,
]);
}
}
println!(
"typing_under_load {}",
serde_json::json!({
"schema": 1,
"scenario": scenario,
"backend": "test",
"area": [area.width, area.height],
"history_turns": history_turns,
"queued_events_per_sample": queued_events,
"warmup_samples": WARMUP,
"sample_columns": ["drain_ms", "edit_to_draw_ms", "drain_then_edit_to_draw_ms", "events_drained", "backlog_after_draw"],
"samples": samples,
})
);
app.cleanup_after_run().unwrap();
}
}