use super::profiling_support::visible_scrollback_visual_lines;
use super::*;
use crate::output::OutputEvent;
use crate::rendering::{RenderInputKind, render};
#[test]
#[ignore = "manual projection workload; reports wall time, not physical terminal latency; run with --release --ignored --nocapture"]
fn streaming_viewport_large_response_workload() {
let viewport_sample = |state: &MissionControlState, scrollback| {
let start = std::time::Instant::now();
let visible = visible_scrollback_visual_lines(state, 80, 24, scrollback);
assert!(visible.lines.len() <= 24);
drop(std::hint::black_box(visible));
start.elapsed().as_micros()
};
let reference_sample = |state: &MissionControlState| {
let start = std::time::Instant::now();
let mut lines = Vec::new();
for span in project_card_spans(state) {
let mut card = (*span.card).clone();
if streaming::is_windowed(&card) {
card.body_lines = render(&card.body, RenderInputKind::Plain);
}
lines.extend(card_visual_lines_uncached(
state,
&card,
span.first_entry_index,
state.is_transcript_focused(),
80,
span.first_entry_index > 0,
None,
));
}
lines.push(transcript_bottom_padding_visual_line(80, state.theme));
annotate_copy_byte_ranges(&mut lines);
let rows: usize = lines.iter().map(|line| visual_line_rows(line, 80)).sum();
drop(std::hint::black_box((lines, rows)));
start.elapsed().as_micros()
};
for (label, body) in [
("small_ascii", "streaming response\n".repeat(8)),
("small_unicode", "streaming 界 response\n".repeat(8)),
(
"large_ascii",
"streaming response with completed rows\n".repeat(10000),
),
(
"large_unicode",
"streaming 界 response with completed rows\n".repeat(10000),
),
(
"unbroken_unicode",
"界 cafe\u{301} 👩\u{200d}💻 ".repeat(12000),
),
] {
let source_bytes = body.len();
let mut viewport_state = MissionControlState::default();
let mut reference_state = MissionControlState::default();
let mut rebuild_state = MissionControlState::default();
viewport_state.apply_output_event(&OutputEvent::AssistantDelta { text: body.clone() });
rebuild_state.apply_output_event(&OutputEvent::AssistantDelta { text: body.clone() });
reference_state.apply_output_event(&OutputEvent::AssistantDelta { text: body });
let viewport_cold = viewport_sample(&viewport_state, 0);
let reference_cold = reference_sample(&reference_state);
let rebuild_cold = viewport_sample(&rebuild_state, 0);
let mut viewport_times = Vec::new();
let mut reference_times = Vec::new();
let mut rebuild_times = Vec::new();
for index in 0..30 {
let text = if label == "unbroken_unicode" {
format!(" chunk {index} 界")
} else if label.ends_with("ascii") {
format!("\nchunk {index}")
} else {
format!("\nchunk {index} 界")
};
viewport_state.apply_output_event(&OutputEvent::AssistantDelta { text: text.clone() });
rebuild_state.apply_output_event(&OutputEvent::AssistantDelta { text: text.clone() });
rebuild_state.transcript_cache.borrow_mut().streaming_reuse = None;
reference_state.apply_output_event(&OutputEvent::AssistantDelta { text });
let scrollback = if index % 2 == 0 { 4000 } else { 0 };
let (viewport, rebuild, reference) = match index % 3 {
0 => {
let viewport = viewport_sample(&viewport_state, scrollback);
let rebuild = viewport_sample(&rebuild_state, scrollback);
(viewport, rebuild, reference_sample(&reference_state))
}
1 => {
let rebuild = viewport_sample(&rebuild_state, scrollback);
let reference = reference_sample(&reference_state);
(
viewport_sample(&viewport_state, scrollback),
rebuild,
reference,
)
}
_ => {
let reference = reference_sample(&reference_state);
let viewport = viewport_sample(&viewport_state, scrollback);
(
viewport,
viewport_sample(&rebuild_state, scrollback),
reference,
)
}
};
viewport_times.push(viewport);
reference_times.push(reference);
rebuild_times.push(rebuild);
}
viewport_times.sort_unstable();
reference_times.sort_unstable();
rebuild_times.sort_unstable();
eprintln!(
"streaming_viewport_large_response case={label} bytes={source_bytes} frames=30 viewport_cold_us={viewport_cold} rebuild_cold_us={rebuild_cold} full_reference_cold_us={reference_cold} viewport_median_us={} rebuild_median_us={} full_reference_median_us={} width=80 height=24 physical_terminal=false",
viewport_times[15], rebuild_times[15], reference_times[15],
);
}
}
#[test]
#[ignore = "manual first-display/resize workload; projection only; run with --release --ignored --nocapture"]
fn profile_large_response_first_display_and_resize() {
let fixtures = [
(
"large_ascii",
"streaming response with completed rows\n".repeat(10000),
),
(
"large_unicode",
"streaming 界 response with completed rows\n".repeat(10000),
),
(
"unbroken_unicode",
"界 cafe\u{301} 👩\u{200d}💻 ".repeat(12000),
),
];
for sample in 0..10 {
for offset in 0..fixtures.len() {
let (label, body) = &fixtures[(sample + offset) % fixtures.len()];
let mut state = MissionControlState::default();
state.apply_output_event(&OutputEvent::AssistantDelta { text: body.clone() });
let mut timings = Vec::new();
let mut original_text = None;
for width in [80, 80, 120, 120, 40, 80] {
let start = std::time::Instant::now();
let visible = visible_scrollback_visual_lines(&state, width, 24, 0);
let elapsed = start.elapsed().as_secs_f64() * 1000.0;
assert!(visible.lines.len() <= 24 && visible.has_overflow_above);
let text = visible
.lines
.iter()
.map(|line| line.copy_text.as_str())
.collect::<String>();
let expected_body = if *label == "unbroken_unicode" {
"cafe\u{301}"
} else {
"streaming"
};
assert!(
text.contains(expected_body),
"latest response body must remain visible after resize"
);
if width == 80 {
if let Some(original) = &original_text {
assert_eq!(
&text, original,
"returning to a retained width must preserve visible text"
);
} else {
original_text = Some(text);
}
}
timings.push(elapsed);
drop(std::hint::black_box(visible));
}
println!(
"first_display_resize {}",
serde_json::json!({
"schema": 1, "sample": sample, "fixture": label, "source_bytes": body.len(),
"height": 24, "widths": [80, 80, 120, 120, 40, 80],
"phases": ["first_80", "cached_80", "first_120", "cached_120", "first_40", "retained_80"],
"projection_ms": timings, "physical_terminal": false,
})
);
}
}
}