Files
ghostty-rs/crates/ghostty-terminal/src/terminal/tests/p7.rs
T
ddidderr 0e8878cf93 feat(terminal): port Terminal, read-only stream handler and StringMap
Ports ZIG/src/terminal/Terminal.zig - printing with grapheme clustering
and Unicode widths, wraparound, insert mode, charsets, cursor movement,
scroll regions and left/right margins, erase/insert/delete, tabs, modes,
alternate screens, save/restore cursor, DECALN, full reset, palette,
mouse/focus/paste flags, kitty keyboard, titles/pwd, semantic prompts,
hyperlinks, protected mode, synchronized output and resize.

stream_readonly.rs implements ghostty_vt::stream::Handler, applying all
state-changing actions to a Terminal and ignoring queries; it is the
base for termio's side-effecting handler. StringMap uses the onig crate
for Oniguruma-compatible regexes (needed by Ghostty's URL regex).

Not yet wired: kitty graphics (hooks and dirty flags are in place where
Zig calls into it, storage is being ported separately) and the tmux
control-mode viewer.

Test Plan:
- cargo test -p ghostty-terminal (849 passed, 5 ignored; all 372
  Terminal and 33 stream_readonly tests ported; ignored ones are blocked
  on the PageList parity pass)
2026-10-11 22:39:29 +02:00

673 lines
19 KiB
Rust

use super::*;
use crate::page::{ContentTag, SemanticContent, SemanticPrompt as RowPrompt};
use ghostty_vt::{
ansi::ProtectedMode,
color::Rgb,
csi::{EraseDisplay, EraseLine, TabClear},
osc::semantic_prompt::{Action as PA, Command},
sgr::{Attribute as A, Underline},
};
fn pr(t: &mut Terminal, s: &str) {
for c in s.chars() {
t.print(u32::from(c)).unwrap();
}
}
fn red() -> Rgb {
Rgb {
r: 0xFF,
g: 0,
b: 0,
}
}
fn assert_bg_red(t: &Terminal, xs: std::ops::Range<u16>, y: u32) {
for x in xs {
let c = cell_at(t, pt_active(x, y));
assert_eq!(c.content_tag(), ContentTag::BgColorRgb);
assert_eq!(c.color_rgb(), red());
}
}
fn abc_def_ghi(t: &mut Terminal) {
pr(t, "ABC");
t.carriage_return();
t.linefeed().unwrap();
pr(t, "DEF");
t.carriage_return();
t.linefeed().unwrap();
pr(t, "GHI");
}
#[test]
fn terminal_erase_line_resets_pending_wrap() {
let mut t = init(5, 5);
pr(&mut t, "ABCDE");
assert!(t.screen().cursor.pending_wrap);
t.erase_line(EraseLine::Right, false);
assert!(!t.screen().cursor.pending_wrap);
t.print(u32::from('B')).unwrap();
assert_eq!("ABCDB", plain(&t));
}
#[test]
fn terminal_erase_line_resets_wrap() {
let mut t = init(5, 5);
pr(&mut t, "ABCDE123");
assert!(row_at(&t, pt_active(0, 0)).wrap());
t.set_cursor_pos(1, 1);
t.erase_line(EraseLine::Right, false);
assert!(!row_at(&t, pt_active(0, 0)).wrap());
t.print(u32::from('X')).unwrap();
assert_eq!("X\n123", plain(&t));
}
#[test]
fn terminal_erase_line_right_preserves_background_sgr() {
let mut t = init(5, 5);
pr(&mut t, "ABCDE");
t.set_cursor_pos(1, 2);
t.set_attribute(A::DirectColorBg(red())).unwrap();
t.erase_line(EraseLine::Right, false);
assert_eq!("A", plain(&t));
assert_bg_red(&t, 1..5, 0);
}
#[test]
fn terminal_erase_line_right_wide_character() {
let mut t = init(10, 5);
pr(&mut t, "AB橋DE");
t.set_cursor_pos(1, 4);
t.clear_dirty();
t.erase_line(EraseLine::Right, false);
assert!(t.is_dirty(pt_active(0, 0)));
assert_eq!("AB", plain(&t));
}
#[test]
fn terminal_erase_line_right_protected_attributes_respected_with_iso() {
let mut t = init(5, 5);
t.set_protected_mode(ProtectedMode::Iso);
pr(&mut t, "ABC");
t.set_cursor_pos(1, 1);
t.clear_dirty();
t.erase_line(EraseLine::Right, false);
assert!(t.is_dirty(pt_active(0, 0)));
assert_eq!("ABC", plain(&t));
}
#[test]
fn terminal_erase_line_right_protected_attributes_ignored_with_dec_most_recent() {
let mut t = init(5, 5);
t.set_protected_mode(ProtectedMode::Iso);
pr(&mut t, "ABC");
t.set_protected_mode(ProtectedMode::Dec);
t.set_protected_mode(ProtectedMode::Off);
t.set_cursor_pos(1, 2);
t.clear_dirty();
t.erase_line(EraseLine::Right, false);
assert!(t.is_dirty(pt_active(0, 0)));
assert_eq!("A", plain(&t));
}
#[test]
fn terminal_erase_line_right_protected_attributes_ignored_with_dec_set() {
let mut t = init(5, 5);
t.set_protected_mode(ProtectedMode::Dec);
pr(&mut t, "ABC");
t.set_cursor_pos(1, 2);
t.clear_dirty();
t.erase_line(EraseLine::Right, false);
assert!(t.is_dirty(pt_active(0, 0)));
assert_eq!("A", plain(&t));
}
#[test]
fn terminal_erase_line_right_protected_requested() {
let mut t = init(10, 5);
pr(&mut t, "12345678");
let y = usize::from(t.screen().cursor.y);
t.set_cursor_pos(y + 1, 6);
t.set_protected_mode(ProtectedMode::Dec);
t.print(u32::from('X')).unwrap();
let y = usize::from(t.screen().cursor.y);
t.set_cursor_pos(y + 1, 4);
t.clear_dirty();
t.erase_line(EraseLine::Right, true);
assert!(t.is_dirty(pt_active(0, 0)));
assert_eq!("123 X", plain(&t));
}
#[test]
fn terminal_erase_line_simple_erase_left() {
let mut t = init(5, 5);
pr(&mut t, "ABCDE");
t.set_cursor_pos(1, 3);
t.clear_dirty();
t.erase_line(EraseLine::Left, false);
assert!(t.is_dirty(pt_active(0, 0)));
assert_eq!(" DE", plain(&t));
}
#[test]
fn terminal_erase_line_left_resets_wrap() {
let mut t = init(5, 5);
pr(&mut t, "ABCDE");
assert!(t.screen().cursor.pending_wrap);
t.clear_dirty();
t.erase_line(EraseLine::Left, false);
assert!(t.is_dirty(pt_active(0, 0)));
assert!(!t.screen().cursor.pending_wrap);
t.print(u32::from('B')).unwrap();
assert_eq!(" B", plain(&t));
}
#[test]
fn terminal_erase_line_left_preserves_background_sgr() {
let mut t = init(5, 5);
pr(&mut t, "ABCDE");
t.set_cursor_pos(1, 2);
t.set_attribute(A::DirectColorBg(red())).unwrap();
t.erase_line(EraseLine::Left, false);
assert_eq!(" CDE", plain(&t));
assert_bg_red(&t, 0..2, 0);
}
#[test]
fn terminal_erase_line_left_wide_character() {
let mut t = init(10, 5);
pr(&mut t, "AB橋DE");
t.set_cursor_pos(1, 3);
t.clear_dirty();
t.erase_line(EraseLine::Left, false);
assert!(t.is_dirty(pt_active(0, 0)));
assert_eq!(" DE", plain(&t));
}
#[test]
fn terminal_erase_line_left_protected_attributes_respected_with_iso() {
let mut t = init(5, 5);
t.set_protected_mode(ProtectedMode::Iso);
pr(&mut t, "ABC");
t.set_cursor_pos(1, 1);
t.clear_dirty();
t.erase_line(EraseLine::Left, false);
assert!(t.is_dirty(pt_active(0, 0)));
assert_eq!("ABC", plain(&t));
}
#[test]
fn terminal_erase_line_left_protected_attributes_ignored_with_dec_most_recent() {
let mut t = init(5, 5);
t.set_protected_mode(ProtectedMode::Iso);
pr(&mut t, "ABC");
t.set_protected_mode(ProtectedMode::Dec);
t.set_protected_mode(ProtectedMode::Off);
t.set_cursor_pos(1, 2);
t.clear_dirty();
t.erase_line(EraseLine::Left, false);
assert!(t.is_dirty(pt_active(0, 0)));
assert_eq!(" C", plain(&t));
}
#[test]
fn terminal_erase_line_left_protected_attributes_ignored_with_dec_set() {
let mut t = init(5, 5);
t.set_protected_mode(ProtectedMode::Dec);
pr(&mut t, "ABC");
t.set_cursor_pos(1, 2);
t.clear_dirty();
t.erase_line(EraseLine::Left, false);
assert!(t.is_dirty(pt_active(0, 0)));
assert_eq!(" C", plain(&t));
}
#[test]
fn terminal_erase_line_left_protected_requested() {
let mut t = init(10, 5);
pr(&mut t, "123456789");
let y = usize::from(t.screen().cursor.y);
t.set_cursor_pos(y + 1, 6);
t.set_protected_mode(ProtectedMode::Dec);
t.print(u32::from('X')).unwrap();
let y = usize::from(t.screen().cursor.y);
t.set_cursor_pos(y + 1, 8);
t.clear_dirty();
t.erase_line(EraseLine::Left, true);
assert!(t.is_dirty(pt_active(0, 0)));
assert_eq!(" X 9", plain(&t));
}
#[test]
fn terminal_erase_line_complete_preserves_background_sgr() {
let mut t = init(5, 5);
pr(&mut t, "ABCDE");
t.set_cursor_pos(1, 2);
t.set_attribute(A::DirectColorBg(red())).unwrap();
t.erase_line(EraseLine::Complete, false);
assert_eq!("", plain(&t));
assert_bg_red(&t, 0..5, 0);
}
#[test]
fn terminal_erase_line_complete_protected_attributes_respected_with_iso() {
let mut t = init(5, 5);
t.set_protected_mode(ProtectedMode::Iso);
pr(&mut t, "ABC");
t.set_cursor_pos(1, 1);
t.clear_dirty();
t.erase_line(EraseLine::Complete, false);
assert!(t.is_dirty(pt_active(0, 0)));
assert_eq!("ABC", plain(&t));
}
#[test]
fn terminal_erase_line_complete_protected_attributes_ignored_with_dec_most_recent() {
let mut t = init(5, 5);
t.set_protected_mode(ProtectedMode::Iso);
pr(&mut t, "ABC");
t.set_protected_mode(ProtectedMode::Dec);
t.set_protected_mode(ProtectedMode::Off);
t.set_cursor_pos(1, 2);
t.clear_dirty();
t.erase_line(EraseLine::Complete, false);
assert!(t.is_dirty(pt_active(0, 0)));
assert_eq!("", plain(&t));
}
#[test]
fn terminal_erase_line_complete_protected_attributes_ignored_with_dec_set() {
let mut t = init(5, 5);
t.set_protected_mode(ProtectedMode::Dec);
pr(&mut t, "ABC");
t.set_cursor_pos(1, 2);
t.clear_dirty();
t.erase_line(EraseLine::Complete, false);
assert!(t.is_dirty(pt_active(0, 0)));
assert_eq!("", plain(&t));
}
#[test]
fn terminal_erase_line_complete_protected_requested() {
let mut t = init(10, 5);
pr(&mut t, "123456789");
let y = usize::from(t.screen().cursor.y);
t.set_cursor_pos(y + 1, 6);
t.set_protected_mode(ProtectedMode::Dec);
t.print(u32::from('X')).unwrap();
let y = usize::from(t.screen().cursor.y);
t.set_cursor_pos(y + 1, 8);
t.clear_dirty();
t.erase_line(EraseLine::Complete, true);
assert!(t.is_dirty(pt_active(0, 0)));
assert_eq!(" X", plain(&t));
}
#[test]
fn terminal_tab_clear_single() {
let mut t = init(30, 5);
t.horizontal_tab();
t.tab_clear(TabClear::Current);
assert!(!t.is_dirty(pt_active(0, 0)));
t.set_cursor_pos(1, 1);
t.horizontal_tab();
assert_eq!(16, t.screen().cursor.x);
}
#[test]
fn terminal_tab_clear_all() {
let mut t = init(30, 5);
t.tab_clear(TabClear::All);
assert!(!t.is_dirty(pt_active(0, 0)));
t.set_cursor_pos(1, 1);
t.horizontal_tab();
assert_eq!(29, t.screen().cursor.x);
}
#[test]
fn terminal_print_repeat_simple() {
let mut t = init(5, 5);
t.print_string("A").unwrap();
t.print_repeat(1).unwrap();
assert!(t.is_dirty(pt_active(0, 0)));
assert_eq!("AA", plain(&t));
}
#[test]
fn terminal_print_repeat_wrap() {
let mut t = init(5, 5);
t.print_string(" A").unwrap();
t.print_repeat(1).unwrap();
assert!(t.is_dirty(pt_active(0, 0)));
assert_eq!(" A\nA", plain(&t));
}
#[test]
fn terminal_print_repeat_no_previous_character() {
let mut t = init(5, 5);
t.print_repeat(1).unwrap();
assert!(!t.is_dirty(pt_active(0, 0)));
assert_eq!("", plain(&t));
}
#[test]
fn terminal_print_attributes() {
let mut t = init(5, 5);
let rgb = |r, g, b| Rgb { r, g, b };
{
t.set_attribute(A::DirectColorFg(rgb(1, 2, 3))).unwrap();
assert_eq!("0;38:2::1:2:3", t.print_attributes());
t.set_attribute(A::Unset).unwrap();
}
{
t.set_attribute(A::Bold).unwrap();
t.set_attribute(A::DirectColorBg(rgb(1, 2, 3))).unwrap();
assert_eq!("0;1;48:2::1:2:3", t.print_attributes());
t.set_attribute(A::Unset).unwrap();
}
{
t.set_attribute(A::Bold).unwrap();
t.set_attribute(A::Faint).unwrap();
t.set_attribute(A::Italic).unwrap();
t.set_attribute(A::Underline(Underline::Single)).unwrap();
t.set_attribute(A::Blink).unwrap();
t.set_attribute(A::Inverse).unwrap();
t.set_attribute(A::Invisible).unwrap();
t.set_attribute(A::Strikethrough).unwrap();
t.set_attribute(A::DirectColorFg(rgb(100, 200, 255)))
.unwrap();
t.set_attribute(A::DirectColorBg(rgb(101, 102, 103)))
.unwrap();
assert_eq!(
"0;1;2;3;4;5;7;8;9;38:2::100:200:255;48:2::101:102:103",
t.print_attributes()
);
t.set_attribute(A::Unset).unwrap();
}
{
t.set_attribute(A::Underline(Underline::Single)).unwrap();
assert_eq!("0;4", t.print_attributes());
t.set_attribute(A::Unset).unwrap();
}
assert_eq!("0", t.print_attributes());
}
#[test]
fn terminal_erase_display_simple_erase_below() {
let mut t = init(5, 5);
abc_def_ghi(&mut t);
t.set_cursor_pos(2, 2);
t.clear_dirty();
t.erase_display(EraseDisplay::Below, false);
assert!(!t.is_dirty(pt_active(0, 0)));
assert!(t.is_dirty(pt_active(0, 1)));
assert!(t.is_dirty(pt_active(0, 2)));
assert_eq!("ABC\nD", plain(&t));
}
#[test]
fn terminal_erase_display_erase_below_preserves_sgr_bg() {
let mut t = init(5, 5);
abc_def_ghi(&mut t);
t.set_cursor_pos(2, 2);
t.set_attribute(A::DirectColorBg(red())).unwrap();
t.erase_display(EraseDisplay::Below, false);
assert_eq!("ABC\nD", plain(&t));
assert_bg_red(&t, 1..5, 1);
}
#[test]
fn terminal_erase_display_below_split_multi_cell() {
let mut t = init(5, 5);
t.print_string("AB橋C").unwrap();
t.carriage_return();
t.linefeed().unwrap();
t.print_string("DE橋F").unwrap();
t.carriage_return();
t.linefeed().unwrap();
t.print_string("GH橋I").unwrap();
t.set_cursor_pos(2, 4);
t.erase_display(EraseDisplay::Below, false);
assert_eq!("AB橋C\nDE", plain(&t));
}
#[test]
fn terminal_erase_display_below_protected_attributes_respected_with_iso() {
let mut t = init(5, 5);
t.set_protected_mode(ProtectedMode::Iso);
abc_def_ghi(&mut t);
t.set_cursor_pos(2, 2);
t.erase_display(EraseDisplay::Below, false);
assert_eq!("ABC\nDEF\nGHI", plain(&t));
}
#[test]
fn terminal_erase_display_below_protected_attributes_ignored_with_dec_most_recent() {
let mut t = init(5, 5);
t.set_protected_mode(ProtectedMode::Iso);
abc_def_ghi(&mut t);
t.set_protected_mode(ProtectedMode::Dec);
t.set_protected_mode(ProtectedMode::Off);
t.set_cursor_pos(2, 2);
t.erase_display(EraseDisplay::Below, false);
assert_eq!("ABC\nD", plain(&t));
}
#[test]
fn terminal_erase_display_below_protected_attributes_ignored_with_dec_set() {
let mut t = init(5, 5);
t.set_protected_mode(ProtectedMode::Dec);
abc_def_ghi(&mut t);
t.set_cursor_pos(2, 2);
t.erase_display(EraseDisplay::Below, false);
assert_eq!("ABC\nD", plain(&t));
}
#[test]
fn terminal_erase_display_below_protected_attributes_respected_with_force() {
let mut t = init(5, 5);
t.set_protected_mode(ProtectedMode::Dec);
abc_def_ghi(&mut t);
t.set_cursor_pos(2, 2);
t.erase_display(EraseDisplay::Below, true);
assert_eq!("ABC\nDEF\nGHI", plain(&t));
}
#[test]
fn terminal_erase_display_simple_erase_above() {
let mut t = init(5, 5);
abc_def_ghi(&mut t);
t.set_cursor_pos(2, 2);
t.clear_dirty();
t.erase_display(EraseDisplay::Above, false);
assert!(t.is_dirty(pt_active(0, 0)));
assert!(t.is_dirty(pt_active(0, 1)));
assert!(!t.is_dirty(pt_active(0, 2)));
assert_eq!("\n F\nGHI", plain(&t));
}
#[test]
fn terminal_erase_display_erase_above_preserves_sgr_bg() {
let mut t = init(5, 5);
abc_def_ghi(&mut t);
t.set_cursor_pos(2, 2);
t.set_attribute(A::DirectColorBg(red())).unwrap();
t.erase_display(EraseDisplay::Above, false);
assert_eq!("\n F\nGHI", plain(&t));
assert_bg_red(&t, 0..2, 1);
}
#[test]
fn terminal_erase_display_above_split_multi_cell() {
let mut t = init(5, 5);
t.print_string("AB橋C").unwrap();
t.carriage_return();
t.linefeed().unwrap();
t.print_string("DE橋F").unwrap();
t.carriage_return();
t.linefeed().unwrap();
t.print_string("GH橋I").unwrap();
t.set_cursor_pos(2, 3);
t.erase_display(EraseDisplay::Above, false);
assert_eq!("\n F\nGH橋I", plain(&t));
}
#[test]
fn terminal_erase_display_above_protected_attributes_respected_with_iso() {
let mut t = init(5, 5);
t.set_protected_mode(ProtectedMode::Iso);
abc_def_ghi(&mut t);
t.set_cursor_pos(2, 2);
t.erase_display(EraseDisplay::Above, false);
assert_eq!("ABC\nDEF\nGHI", plain(&t));
}
#[test]
fn terminal_erase_display_above_protected_attributes_ignored_with_dec_most_recent() {
let mut t = init(5, 5);
t.set_protected_mode(ProtectedMode::Iso);
abc_def_ghi(&mut t);
t.set_protected_mode(ProtectedMode::Dec);
t.set_protected_mode(ProtectedMode::Off);
t.set_cursor_pos(2, 2);
t.erase_display(EraseDisplay::Above, false);
assert_eq!("\n F\nGHI", plain(&t));
}
#[test]
fn terminal_erase_display_above_protected_attributes_ignored_with_dec_set() {
let mut t = init(5, 5);
t.set_protected_mode(ProtectedMode::Dec);
abc_def_ghi(&mut t);
t.set_cursor_pos(2, 2);
t.erase_display(EraseDisplay::Above, false);
assert_eq!("\n F\nGHI", plain(&t));
}
#[test]
fn terminal_erase_display_above_protected_attributes_respected_with_force() {
let mut t = init(5, 5);
t.set_protected_mode(ProtectedMode::Dec);
abc_def_ghi(&mut t);
t.set_cursor_pos(2, 2);
t.erase_display(EraseDisplay::Above, true);
assert_eq!("ABC\nDEF\nGHI", plain(&t));
}
#[test]
fn terminal_erase_display_protected_complete() {
let mut t = init(10, 5);
t.print(u32::from('A')).unwrap();
t.carriage_return();
t.linefeed().unwrap();
pr(&mut t, "123456789");
let y = usize::from(t.screen().cursor.y);
t.set_cursor_pos(y + 1, 6);
t.set_protected_mode(ProtectedMode::Dec);
t.print(u32::from('X')).unwrap();
let y = usize::from(t.screen().cursor.y);
t.set_cursor_pos(y + 1, 4);
t.clear_dirty();
t.erase_display(EraseDisplay::Complete, true);
for y in 0..u32::from(t.rows) {
assert!(t.is_dirty(pt_active(0, y)));
}
assert_eq!("\n X", plain(&t));
}
#[test]
fn terminal_erase_display_protected_below() {
let mut t = init(10, 5);
t.print(u32::from('A')).unwrap();
t.carriage_return();
t.linefeed().unwrap();
pr(&mut t, "123456789");
let y = usize::from(t.screen().cursor.y);
t.set_cursor_pos(y + 1, 6);
t.set_protected_mode(ProtectedMode::Dec);
t.print(u32::from('X')).unwrap();
let y = usize::from(t.screen().cursor.y);
t.set_cursor_pos(y + 1, 4);
t.erase_display(EraseDisplay::Below, true);
assert_eq!("A\n123 X", plain(&t));
}
#[test]
fn terminal_erase_display_scroll_complete() {
let mut t = init(10, 5);
t.print(u32::from('A')).unwrap();
t.carriage_return();
t.linefeed().unwrap();
t.erase_display(EraseDisplay::ScrollComplete, false);
assert_eq!("", plain(&t));
}
#[test]
fn terminal_erase_display_protected_above() {
let mut t = init(10, 3);
t.print(u32::from('A')).unwrap();
t.carriage_return();
t.linefeed().unwrap();
pr(&mut t, "123456789");
let y = usize::from(t.screen().cursor.y);
t.set_cursor_pos(y + 1, 6);
t.set_protected_mode(ProtectedMode::Dec);
t.print(u32::from('X')).unwrap();
let y = usize::from(t.screen().cursor.y);
t.set_cursor_pos(y + 1, 8);
t.erase_display(EraseDisplay::Above, true);
assert_eq!("\n X 9", plain(&t));
}
#[test]
fn terminal_erase_display_complete_preserves_cursor() {
let mut t = init(5, 5);
// Set our cursor
t.set_attribute(A::Bold).unwrap();
t.print_string("AAAA").unwrap();
assert_ne!(style::DEFAULT_ID, t.screen().cursor.style_id);
// Erasing the display may detect that our style is no longer in use
// and prune our style, which we don't want because its still our
// active cursor.
t.erase_display(EraseDisplay::Complete, false);
assert_ne!(style::DEFAULT_ID, t.screen().cursor.style_id);
}
#[test]
fn terminal_semantic_prompt() {
let mut t = init(10, 5);
// Prompt
t.semantic_prompt(&Command::init(PA::FreshLineNewPrompt))
.unwrap();
pr(&mut t, "hello");
assert_eq!(0, t.screen().cursor.y);
assert_eq!(5, t.screen().cursor.x);
{
let pt = pt_active(t.screen().cursor.x - 1, u32::from(t.screen().cursor.y));
assert_eq!(SemanticContent::Prompt, cell_at(&t, pt).semantic_content());
assert_eq!(RowPrompt::Prompt, row_at(&t, pt).semantic_prompt());
}
// Start input but end it on EOL
t.semantic_prompt(&Command::init(PA::EndPromptStartInputTerminateEol))
.unwrap();
t.carriage_return();
t.linefeed().unwrap();
// Write some output
assert_eq!(1, t.screen().cursor.y);
assert_eq!(0, t.screen().cursor.x);
pr(&mut t, "world");
{
let pt = pt_active(t.screen().cursor.x - 1, u32::from(t.screen().cursor.y));
assert_eq!(SemanticContent::Output, cell_at(&t, pt).semantic_content());
assert_eq!(RowPrompt::None, row_at(&t, pt).semantic_prompt());
}
}