Files
ghostty-rs/crates/ghostty-vt-capi/tests/c_examples.rs
T
ddidderr 75252a7651 feat(vt-capi): port the libghostty-vt C API
Ports ZIG/src/lib_vt.zig and terminal/c/* as crates/ghostty-vt-capi
(cdylib + staticlib + rlib, soname libghostty-vt.so) implementing the
unchanged include/ghostty/vt.h + vt/*.h ABI: OSC parser, SGR parser,
key events and key encoding, paste safety, colors, result codes - all
38 native symbols, plus the ghostty_wasm_* helpers on wasm32.

Verification:
- tests/abi.rs generates a C probe from the headers, compiled with cc,
  that checks sizeof/alignof/offsetof of every struct/union, every
  enumerator and #define against Rust, and links every declared symbol;
  nm -D exports equal the declared functions.
- Ghostty's example/c-vt* programs compile unchanged against the Rust
  library (shared and static) and produce the expected output.
- The wasm32 build runs Ghostty's wasm-key-encode/wasm-sgr example
  scripts unmodified under node (upstream's wasm-sgr tag table is stale
  vs sgr.h; a regenerated copy checks full output).

Test Plan:
- cargo test -p ghostty-vt-capi (22 unit + 2 ABI + 5 C + 3 wasm tests)
2026-10-12 05:16:41 +02:00

95 lines
2.7 KiB
Rust

//! Builds Ghostty's unchanged C examples (examples/c/*.c, from
//! ZIG/example/c-vt*) against the Rust `libghostty-vt` and checks their output.
#![cfg(unix)]
#![allow(clippy::unwrap_used)]
mod common;
use std::process::Command;
fn run_example(name: &str) -> String {
let lib = common::install_native_lib();
let scratch = common::scratch_dir("c_examples");
let src = common::manifest_dir()
.join("examples/c")
.join(format!("{name}.c"));
let exe = scratch.join(name);
common::compile_c(&lib, &[&src], &exe, &["-Wall"]);
let out = Command::new(&exe).output().expect("run example");
assert!(out.status.success(), "{name} exited with {:?}", out.status);
String::from_utf8(out.stdout).unwrap()
}
#[test]
fn c_vt_osc() {
assert_eq!(
run_example("c-vt"),
"Command type: 1\nExtracted title: hello\n"
);
}
#[test]
fn c_vt_key_encode() {
assert_eq!(
run_example("c-vt-key-encode"),
"Encoding event: left ctrl release with all Kitty flags enabled\n\
Required buffer size: 12 bytes\n\
Encoded 12 bytes\n\
Hex: 1b 5b 35 37 34 34 32 3b 35 3a 33 75 \n\
String: \\x1b[57442;5:3u\n"
);
}
#[test]
fn c_vt_paste() {
assert_eq!(
run_example("c-vt-paste"),
"'hello world' is safe to paste\n\
'rm -rf /\n' is UNSAFE - contains newline\n\
Data with escape sequence is UNSAFE\n\
Empty data is safe\n"
);
}
#[test]
fn c_vt_sgr() {
assert_eq!(
run_example("c-vt-sgr"),
"Parsing Kakoune SGR sequence:\n\
ESC[4:3;38;2;51;51;51;48;2;170;170;170;58;2;255;97;136m\n\
\n\
Attribute 1: Underline style = curly\n\
Attribute 2: Foreground RGB = (51, 51, 51)\n\
Attribute 3: Background RGB = (170, 170, 170)\n\
Attribute 4: Underline color RGB = (255, 97, 136)\n\
\n\
Total attributes parsed: 4\n"
);
}
/// The static archive links too (what embedders of `libghostty-vt.a` use).
#[test]
fn c_vt_links_statically() {
let lib = common::install_native_lib();
let scratch = common::scratch_dir("c_examples_static");
let src = common::manifest_dir().join("examples/c/c-vt-paste.c");
let exe = scratch.join("paste-static");
let out = Command::new("cc")
.arg("-I")
.arg(common::include_dir())
.arg(&src)
.arg(lib.join("libghostty-vt.a"))
.args(["-lpthread", "-ldl", "-lm"])
.arg("-o")
.arg(&exe)
.output()
.unwrap();
assert!(
out.status.success(),
"{}",
String::from_utf8_lossy(&out.stderr)
);
let run = Command::new(&exe).output().unwrap();
assert!(String::from_utf8_lossy(&run.stdout).contains("Empty data is safe"));
}