test: faster test execution in codex-core (#2633)

this dramatically improves time to run `cargo test -p codex-core` (~25x
speedup).

before:
```
cargo test -p codex-core  35.96s user 68.63s system 19% cpu 8:49.80 total
```

after:
```
cargo test -p codex-core  5.51s user 8.16s system 63% cpu 21.407 total
```

both tests measured "hot", i.e. on a 2nd run with no filesystem changes,
to exclude compile times.

approach inspired by [Delete Cargo Integration
Tests](https://matklad.github.io/2021/02/27/delete-cargo-integration-tests.html),
we move all test cases in tests/ into a single suite in order to have a
single binary, as there is significant overhead for each test binary
executed, and because test execution is only parallelized with a single
binary.
This commit is contained in:
Jeremy Rose
2025-08-24 11:10:53 -07:00
committed by GitHub
parent c6a52d611c
commit 32bbbbad61
56 changed files with 78 additions and 3 deletions

View File

@@ -0,0 +1,5 @@
// Aggregates all former standalone integration tests as modules.
mod status_indicator;
mod vt100_history;
mod vt100_live_commit;
mod vt100_streaming_no_dup;

View File

@@ -0,0 +1,24 @@
//! Regression test: ensure that `StatusIndicatorWidget` sanitises ANSI escape
//! sequences so that no raw `\x1b` bytes are written into the backing
//! buffer. Rendering logic is tricky to unittest endtoend, therefore we
//! verify the *public* contract of `ansi_escape_line()` which the widget now
//! relies on.
use codex_ansi_escape::ansi_escape_line;
#[test]
fn ansi_escape_line_strips_escape_sequences() {
let text_in_ansi_red = "\x1b[31mRED\x1b[0m";
// The returned line must contain three printable glyphs and **no** raw
// escape bytes.
let line = ansi_escape_line(text_in_ansi_red);
let combined: String = line
.spans
.iter()
.map(|span| span.content.to_string())
.collect();
assert_eq!(combined, "RED");
}

View File

@@ -0,0 +1,245 @@
#![cfg(feature = "vt100-tests")]
#![expect(clippy::expect_used)]
use ratatui::backend::TestBackend;
use ratatui::layout::Rect;
use ratatui::style::Color;
use ratatui::style::Style;
use ratatui::text::Line;
use ratatui::text::Span;
// Small helper macro to assert a collection contains an item with a clearer
// failure message.
macro_rules! assert_contains {
($collection:expr, $item:expr $(,)?) => {
assert!(
$collection.contains(&$item),
"Expected {:?} to contain {:?}",
$collection,
$item
);
};
($collection:expr, $item:expr, $($arg:tt)+) => {
assert!($collection.contains(&$item), $($arg)+);
};
}
struct TestScenario {
width: u16,
height: u16,
term: codex_tui::custom_terminal::Terminal<TestBackend>,
}
impl TestScenario {
fn new(width: u16, height: u16, viewport: Rect) -> Self {
let backend = TestBackend::new(width, height);
let mut term = codex_tui::custom_terminal::Terminal::with_options(backend)
.expect("failed to construct terminal");
term.set_viewport_area(viewport);
Self {
width,
height,
term,
}
}
fn run_insert(&mut self, lines: Vec<Line<'static>>) -> Vec<u8> {
let mut buf: Vec<u8> = Vec::new();
codex_tui::insert_history::insert_history_lines_to_writer(&mut self.term, &mut buf, lines);
buf
}
fn screen_rows_from_bytes(&self, bytes: &[u8]) -> Vec<String> {
let mut parser = vt100::Parser::new(self.height, self.width, 0);
parser.process(bytes);
let screen = parser.screen();
let mut rows: Vec<String> = Vec::with_capacity(self.height as usize);
for row in 0..self.height {
let mut s = String::with_capacity(self.width as usize);
for col in 0..self.width {
if let Some(cell) = screen.cell(row, col) {
if let Some(ch) = cell.contents().chars().next() {
s.push(ch);
} else {
s.push(' ');
}
} else {
s.push(' ');
}
}
rows.push(s.trim_end().to_string());
}
rows
}
}
#[test]
fn basic_insertion_no_wrap() {
// Screen of 20x6; viewport is the last row (height=1 at y=5)
let area = Rect::new(0, 5, 20, 1);
let mut scenario = TestScenario::new(20, 6, area);
let lines = vec![Line::from("first"), Line::from("second")];
let buf = scenario.run_insert(lines);
let rows = scenario.screen_rows_from_bytes(&buf);
assert_contains!(rows, String::from("first"));
assert_contains!(rows, String::from("second"));
let first_idx = rows
.iter()
.position(|r| r == "first")
.expect("expected 'first' row to be present");
let second_idx = rows
.iter()
.position(|r| r == "second")
.expect("expected 'second' row to be present");
assert_eq!(second_idx, first_idx + 1, "rows should be adjacent");
}
#[test]
fn long_token_wraps() {
let area = Rect::new(0, 5, 20, 1);
let mut scenario = TestScenario::new(20, 6, area);
let long = "A".repeat(45); // > 2 lines at width 20
let lines = vec![Line::from(long.clone())];
let buf = scenario.run_insert(lines);
let mut parser = vt100::Parser::new(6, 20, 0);
parser.process(&buf);
let screen = parser.screen();
// Count total A's on the screen
let mut count_a = 0usize;
for row in 0..6 {
for col in 0..20 {
if let Some(cell) = screen.cell(row, col)
&& let Some(ch) = cell.contents().chars().next()
&& ch == 'A'
{
count_a += 1;
}
}
}
assert_eq!(
count_a,
long.len(),
"wrapped content did not preserve all characters"
);
}
#[test]
fn emoji_and_cjk() {
let area = Rect::new(0, 5, 20, 1);
let mut scenario = TestScenario::new(20, 6, area);
let text = String::from("😀😀😀😀😀 你好世界");
let lines = vec![Line::from(text.clone())];
let buf = scenario.run_insert(lines);
let rows = scenario.screen_rows_from_bytes(&buf);
let reconstructed: String = rows.join("").chars().filter(|c| *c != ' ').collect();
for ch in text.chars().filter(|c| !c.is_whitespace()) {
assert!(
reconstructed.contains(ch),
"missing character {ch:?} in reconstructed screen"
);
}
}
#[test]
fn mixed_ansi_spans() {
let area = Rect::new(0, 5, 20, 1);
let mut scenario = TestScenario::new(20, 6, area);
let line = Line::from(vec![
Span::styled("red", Style::default().fg(Color::Red)),
Span::raw("+plain"),
]);
let buf = scenario.run_insert(vec![line]);
let rows = scenario.screen_rows_from_bytes(&buf);
assert_contains!(rows, String::from("red+plain"));
}
#[test]
fn cursor_restoration() {
let area = Rect::new(0, 5, 20, 1);
let mut scenario = TestScenario::new(20, 6, area);
let lines = vec![Line::from("x")];
let buf = scenario.run_insert(lines);
let s = String::from_utf8_lossy(&buf);
// CUP to 1;1 (ANSI: ESC[1;1H)
assert!(
s.contains("\u{1b}[1;1H"),
"expected final CUP to 1;1 in output, got: {s:?}"
);
// Reset scroll region
assert!(
s.contains("\u{1b}[r"),
"expected reset scroll region in output, got: {s:?}"
);
}
#[test]
fn word_wrap_no_mid_word_split() {
// Screen of 40x10; viewport is the last row
let area = Rect::new(0, 9, 40, 1);
let mut scenario = TestScenario::new(40, 10, area);
let sample = "Years passed, and Willowmere thrived in peace and friendship. Miras herb garden flourished with both ordinary and enchanted plants, and travelers spoke of the kindness of the woman who tended them.";
let buf = scenario.run_insert(vec![Line::from(sample)]);
let rows = scenario.screen_rows_from_bytes(&buf);
let joined = rows.join("\n");
assert!(
!joined.contains("bo\nth"),
"word 'both' should not be split across lines:\n{joined}"
);
}
#[test]
fn em_dash_and_space_word_wrap() {
// Repro from report: ensure we break before "inside", not mid-word.
let area = Rect::new(0, 9, 40, 1);
let mut scenario = TestScenario::new(40, 10, area);
let sample = "Mara found an old key on the shore. Curious, she opened a tarnished box half-buried in sand—and inside lay a single, glowing seed.";
let buf = scenario.run_insert(vec![Line::from(sample)]);
let rows = scenario.screen_rows_from_bytes(&buf);
let joined = rows.join("\n");
assert!(
!joined.contains("insi\nde"),
"word 'inside' should not be split across lines:\n{joined}"
);
}
#[test]
fn pre_scroll_region_down() {
// Viewport not at bottom: y=3 (0-based), height=1
let area = Rect::new(0, 3, 20, 1);
let mut scenario = TestScenario::new(20, 6, area);
let lines = vec![Line::from("first"), Line::from("second")];
let buf = scenario.run_insert(lines);
let s = String::from_utf8_lossy(&buf);
// Expect we limited scroll region to [top+1 .. screen_height] => [4 .. 6] (1-based)
assert!(
s.contains("\u{1b}[4;6r"),
"expected pre-scroll SetScrollRegion 4..6, got: {s:?}"
);
// Expect we moved cursor to top of that region: row 3 (0-based) => CUP 4;1H
assert!(
s.contains("\u{1b}[4;1H"),
"expected cursor at top of pre-scroll region, got: {s:?}"
);
// Expect at least two Reverse Index commands (ESC M) for two inserted lines
let ri_count = s.matches("\u{1b}M").count();
assert!(
ri_count >= 1,
"expected at least one RI (ESC M), got: {s:?}"
);
// After pre-scroll, we set insertion scroll region to [1 .. new_top] => [1 .. 5]
assert!(
s.contains("\u{1b}[1;5r"),
"expected insertion SetScrollRegion 1..5, got: {s:?}"
);
}

View File

@@ -0,0 +1,101 @@
#![cfg(feature = "vt100-tests")]
use ratatui::backend::TestBackend;
use ratatui::layout::Rect;
use ratatui::text::Line;
#[test]
fn live_001_commit_on_overflow() {
let backend = TestBackend::new(20, 6);
let mut term = match codex_tui::custom_terminal::Terminal::with_options(backend) {
Ok(t) => t,
Err(e) => panic!("failed to construct terminal: {e}"),
};
let area = Rect::new(0, 5, 20, 1);
term.set_viewport_area(area);
// Build 5 explicit rows at width 20.
let mut rb = codex_tui::live_wrap::RowBuilder::new(20);
rb.push_fragment("one\n");
rb.push_fragment("two\n");
rb.push_fragment("three\n");
rb.push_fragment("four\n");
rb.push_fragment("five\n");
// Keep the last 3 in the live ring; commit the first 2.
let commit_rows = rb.drain_commit_ready(3);
let lines: Vec<Line<'static>> = commit_rows
.into_iter()
.map(|r| Line::from(r.text))
.collect();
let mut buf: Vec<u8> = Vec::new();
codex_tui::insert_history::insert_history_lines_to_writer(&mut term, &mut buf, lines);
let mut parser = vt100::Parser::new(6, 20, 0);
parser.process(&buf);
let screen = parser.screen();
// The words "one" and "two" should appear above the viewport.
let mut joined = String::new();
for row in 0..6 {
for col in 0..20 {
if let Some(cell) = screen.cell(row, col) {
if let Some(ch) = cell.contents().chars().next() {
joined.push(ch);
} else {
joined.push(' ');
}
}
}
joined.push('\n');
}
assert!(
joined.contains("one"),
"expected committed 'one' to be visible\n{joined}"
);
assert!(
joined.contains("two"),
"expected committed 'two' to be visible\n{joined}"
);
// The last three (three,four,five) remain in the live ring, not committed here.
}
#[test]
fn live_002_pre_scroll_and_commit() {
let backend = TestBackend::new(20, 6);
let mut term = match codex_tui::custom_terminal::Terminal::with_options(backend) {
Ok(t) => t,
Err(e) => panic!("failed to construct terminal: {e}"),
};
// Viewport not at bottom: y=3
let area = Rect::new(0, 3, 20, 1);
term.set_viewport_area(area);
let mut rb = codex_tui::live_wrap::RowBuilder::new(20);
rb.push_fragment("alpha\n");
rb.push_fragment("beta\n");
rb.push_fragment("gamma\n");
rb.push_fragment("delta\n");
// Keep 3, commit 1.
let commit_rows = rb.drain_commit_ready(3);
let lines: Vec<Line<'static>> = commit_rows
.into_iter()
.map(|r| Line::from(r.text))
.collect();
let mut buf: Vec<u8> = Vec::new();
codex_tui::insert_history::insert_history_lines_to_writer(&mut term, &mut buf, lines);
let s = String::from_utf8_lossy(&buf);
// Expect a SetScrollRegion to [area.top()+1 .. screen_height] and a cursor move to top of that region.
assert!(
s.contains("\u{1b}[4;6r"),
"expected pre-scroll region 4..6, got: {s:?}"
);
assert!(
s.contains("\u{1b}[4;1H"),
"expected cursor CUP 4;1H, got: {s:?}"
);
}

View File

@@ -0,0 +1,77 @@
#![cfg(feature = "vt100-tests")]
use ratatui::backend::TestBackend;
use ratatui::layout::Rect;
use ratatui::text::Line;
fn term(viewport: Rect) -> codex_tui::custom_terminal::Terminal<TestBackend> {
let backend = TestBackend::new(20, 6);
let mut term = codex_tui::custom_terminal::Terminal::with_options(backend)
.unwrap_or_else(|e| panic!("failed to construct terminal: {e}"));
term.set_viewport_area(viewport);
term
}
#[test]
fn stream_commit_trickle_no_duplication() {
// Viewport is the last row (height=1 at y=5)
let area = Rect::new(0, 5, 20, 1);
let mut t = term(area);
// Step 1: commit first row
let mut out1 = Vec::new();
codex_tui::insert_history::insert_history_lines_to_writer(
&mut t,
&mut out1,
vec![Line::from("one")],
);
// Step 2: later commit next row
let mut out2 = Vec::new();
codex_tui::insert_history::insert_history_lines_to_writer(
&mut t,
&mut out2,
vec![Line::from("two")],
);
let combined = [out1, out2].concat();
let s = String::from_utf8_lossy(&combined);
assert_eq!(
s.matches("one").count(),
1,
"history line duplicated: {s:?}"
);
assert_eq!(
s.matches("two").count(),
1,
"history line duplicated: {s:?}"
);
assert!(
!s.contains("three"),
"live-only content leaked into history: {s:?}"
);
}
#[test]
fn live_ring_rows_not_inserted_into_history() {
let area = Rect::new(0, 5, 20, 1);
let mut t = term(area);
// Commit two rows to history.
let mut buf = Vec::new();
codex_tui::insert_history::insert_history_lines_to_writer(
&mut t,
&mut buf,
vec![Line::from("one"), Line::from("two")],
);
// The live ring might display tail+head rows like ["two", "three"],
// but only committed rows should be present in the history ANSI stream.
let s = String::from_utf8_lossy(&buf);
assert!(s.contains("one"));
assert!(s.contains("two"));
assert!(
!s.contains("three"),
"uncommitted live-ring content should not be inserted into history: {s:?}"
);
}