A pragmatic, portable plan to guarantee correctness, parity, and crash‑safety across all ratatui_ffi language bindings. Copy this into every bindings repo. Agents and humans alike should follow it.
- One set of tests, many languages: the same rendering scenarios validate Rust (oracle) and every binding.
- 100% functional parity and zero‑crash interop, verified automatically.
- Fast, local developer workflow; CI optional.
- Single source of truth: the Rust oracle renders ground‑truth outputs using first‑class
ratatuiAPIs. Bindings must match those outputs when driving throughratatui_ffi. - Introspection as a safety net: coverage checker ensures every FFI export has a binding entry.
- Scenario JSON: portable test cases, independent of any language runtime.
- Normalized outputs: text, extended styles, and structured cells are compared deterministically.
-
Scenario Schema (JSON)
- Describes a frame (width, height) and a sequence of draw commands.
- Commands reference widget kinds and their parameters in a plain, FFI‑friendly shape (strings, numbers, arrays).
- Optional expected outputs (text/styles/cells) for goldens, versioned.
-
Oracle Renderer (Rust)
- A small binary in
ratatui-ffi/toolsthat:- Reads a Scenario JSON
- Constructs ratatui widgets directly (no FFI)
- Produces outputs: text, styles_ex, cells
- Writes
ResultJSONwith normalized outputs
- A small binary in
-
Binding Runner (per language)
- Reads the same Scenario JSON
- Builds the same widgets via the binding APIs (through FFI)
- Emits the same
ResultJSON - Diffs against the oracle result and fails on mismatch
-
Coverage + Crash Safety
- Coverage: measure how many FFI exports are exercised by the scenario set
- Crash safety: run scenarios under panic/exception monitors; add fuzz input later
{
"version": 1,
"width": 60,
"height": 20,
"cmds": [
{ "kind": "Paragraph", "rect": {"x":0,"y":0,"width":60,"height":3},
"title": {"text":"Hello","showBorder": true},
"lines": [
{"spans": [{"text":"hi","style": {"fg":"rgb(255,255,0)","mods":["BOLD"]}}]},
{"spans": [{"text":"bye","style": {"fg":"idx(5)"}}]}
],
"align": "Left",
"wrapTrim": true
},
{ "kind": "Gauge", "rect": {"x":0,"y":3,"width":60,"height":1},
"ratio": 0.42, "label": "42%" }
]
}Notes
- Keep only primitives (numbers, strings, arrays). Encode colors as
rgb(r,g,b)oridx(n)strings, styles as{fg,bg,mods[]}. - Keep widget params flat. Multi‑span lines become arrays of
{text,style}. - The schema is intentionally simple; binding runners translate JSON → native structs.
{
"version": 1,
"text": "...\n...",
"styles_ex": "<compact rows/cols + attrs>",
"cells": [ {"ch": 32, "fg": 0, "bg": 0, "mods": 0 } ]
}text: headless frame textstyles_ex: extended style dump (string)cells: structured per‑cell dump using{ch,fg,bg,mods}
- Normalize newlines to
\n - Ensure fixed frame size (width×height)
- Canonicalize colors to the FFI bit format (bindings can call color helpers)
- Sort object keys in JSON for stability
-
Define/collect scenarios
- Phase 1: curate a representative set manually (widgets, layout, styles)
- Phase 2: auto‑emit scenarios from ratatui’s own tests via a feature flag or macro (recommended)
-
Generate oracle outputs
cargo run --bin scenario_runner -- --in scenario.json --out oracle.json
-
Run binding runner
node bindings-*/test/run_scenario.js --in scenario.json --out binding.json
-
Diff
- Compare
oracle.jsonvsbinding.json(text, styles_ex, cells) - Fail on any difference (optionally allow waivers for platform quirks)
- Compare
-
Coverage signal
- Track which FFI exports were exercised during runs; raise a warning when a family is untested
When ratatui/FFI updates:
- Rebuild FFI; regenerate
ffi_introspect --json; keep 100% binding coverage - Re‑run scenarios; review diffs
- If intended visual changes: update oracle goldens and re‑generate bindings results
- Add/expand scenarios for new widgets/features
ratatui-ffi/
tools/
scenario_runner.rs # oracle renderer (Rust)
scripts/
ffi_introspect.sh
bindings-*/
src/
test/
scenarios/
schema.md # schema doc
*.json # test inputs
run_scenario.* # runner via bindings
diff.* # oracle vs binding diff
scripts/
check-introspection.js
features-map.json
- Ensure 100% export coverage using the introspection checker.
- Add a minimal scenario set; implement the Rust oracle runner.
- Implement the binding runner for the current language.
- Produce and diff outputs; enforce failure on mismatch.
- Expand scenarios to cover optional features (feature bits), batching, and edge cases.
- All FFI exports bound and validated by checker
- Scenario runner implemented; passes against oracle for the scenario suite
- No crashes or leaks on scenario execution
- A README/DEV‑GUIDE describing how to run, extend, and regenerate oracles
- Auto‑extract scenarios from ratatui tests with a proc‑macro
- Randomized fuzz scenarios (size, colors, spans) with clamps/guards
- Performance baselines (time per scenario, allocs)
- Visual HTML captures for artifacts (optional)
- Shared JSON inputs remove language noise
- Rust oracle provides authoritative behavior
- Bindings are thin; the diff surface is tight and objective
- The system scales to new widgets and languages without redesign
Make it strict, make it portable, and keep it local and fast.