Files
ddidderr be6d706ba5 test(core): add headless end-to-end tests and fix bugs they found
Adds crates/ghostty-core/tests/e2e_*.rs: 81 tests that drive the real
core Surface, termio, pty and child processes through a fake runtime
(recording apprt actions, in-memory clipboard) with a headless renderer
(ghostty-renderer/src/headless.rs). They cover keys (legacy, DECCKM,
kitty, modifyOtherKeys), keybinding actions and sequences, mouse
selection/reporting/scrolling/links, clipboard incl. OSC 52 and paste
safety, resize, focus, titles/pwd/bell/notifications/progress, child
exit, and IME preedit.

Fixes for issues found by the tests and by the Codex audit
(docs/rewrite/notes/review-core-codex.md, all 9 items):
- replacing a selection with copy-on-select panicked (stale tracked
  handles); global new_window no longer inherits a parent; the child
  environment uses the runtime's env wholesale so GTK's removals stick;
  Flatpak host resources path; locale recovery (ensureLocale) in
  apprt-gtk before GTK init and for CLI actions; failed screen-file
  opens propagate errors; OSC 52 base64 rules, CSI/ESC binding length
  limits and GHOSTTY_LOG parsing match Zig.
- Also includes V2's termio cleanups (tmux viewer wiring in the stream
  handler, Pty slave semantics docs, shared cgroup wait loop).

apprt-gtk gains a debug input replay hook (GHOSTTY_DEBUG_INPUT_SCRIPT)
exercising the real GTK key path. With it, the real app was verified on
Wayland: typing and output, new tab, splits, select-all/copy/paste and
font size all work (docs/rewrite/notes/verification-V1.md). Not yet
exercised at GTK level: mouse, real IM-context IME, clipboard dialogs.

Test Plan:
- cargo test --workspace (2983 passed)
- cargo test -p ghostty-core --tests (81 e2e tests)
2026-10-12 03:58:19 +02:00

4.4 KiB

Verification V1 / V1b (core end-to-end + GTK runtime)

E2E tests (crates/ghostty-core/tests/, 81 tests, all headless; real core Surface + termio + pty + headless EGL renderer, fake apprt)

  • common/mod.rs: harness (FakeRt recording actions/clipboard, recorder child cat-like process, pump/wait/settle helpers).
  • e2e_bindings.rs (23): default and configured binding actions, select-all/copy/paste, csi/esc/text, screen-file open, child env.
  • e2e_ime.rs (4): preedit state/rendering, commit, composing key events.
  • e2e_keys.rs (15): key events -> encoder -> pty bytes (legacy, kitty, modifiers, sequences).
  • e2e_mouse.rs (19): selection, copy-on-select, multi-click, mouse reporting (x10/normal/sgr), wheel, alternate scroll.
  • e2e_term.rs (20): resize/TIOCSWINSZ, focus, title, pwd, bell, OSC 52 read/write, notifications, progress, child exit.
  • Gate: workspace build, cargo test --workspace (~480 tests), cargo clippy --workspace --all-targets -D warnings, cargo fmt --all all pass.

Bugs found and fixed

  • core surface/mod.rs: set_selection compared against the previous selection after replacing it (tracked-pin panic, e.g. select-all twice with copy-on-select).
  • core surface/mod.rs: child env overlaid runtime env on the process env; now the runtime env wholesale (fallback only if empty).
  • core surface/keyboard.rs: global/all new_window targeted the surface (parent inheritance); now Target::App.
  • core global.rs + apprt-gtk flatpak.rs: runtime-specific resources-dir hook (host_path), wired from src/main.rs.
  • core surface/binding.rs: failed screen-file open returned success; now an error (performable bindings fall through). CSI/ESC > 128 bytes rejected.
  • core surface/clipboard.rs: OSC 52 base64 acceptance matches Zig (size check, ignore trailing-bit errors).
  • core global.rs: GHOSTTY_LOG boolean aliases/empty value/invalid-field behaviour.
  • apprt-gtk class/application.rs + src/main.rs: init_locale (ensureLocale) before GTK init and for CLI actions.
  • renderer headless.rs, termio, apprt runtime_api.rs: fixes needed by the headless harness; apprt-gtk debug_input.rs: new input replay hook.
  • tests: wheel e2e test raced under load (one discrete tick = 3 reports); fixed to wait for all 6.
  • dist/ (desktop file, metainfo, dbus/systemd units, icons) is legitimate install data; kept.

Codex audit (review-core-codex.md), 9 items

  1. selection-replacement panic: FIXED, test select_all_twice_with_copy_on_select_does_not_panic.
  2. child env filtering: FIXED, test child_environment_comes_from_the_runtime.
  3. new_window parent: FIXED for global/all (keyboard.rs). Surface-local new_window binding (binding.rs) still passes Target::Surface deliberately: Zig Surface.zig:5122 calls app.newWindow with the surface as parent there.
  4. flatpak host path: FIXED (hook, no unit test: needs /.flatpak-info).
  5. screen-file open failure: FIXED, test write_screen_file_open_failure_is_an_error.
  6. ensureLocale: FIXED (unsafe setlocale/setenv in apprt-gtk, SAFETY comments); not unit-testable (process-global locale).
  7. OSC 52 base64: FIXED, test osc52_write_base64_acceptance_matches_zig.
  8. CSI/ESC length limit: FIXED, test csi_binding_longer_than_the_zig_buffer_is_rejected.
  9. GHOSTTY_LOG parsing: FIXED, unit test logging_parse.

GTK runtime check (real app, Wayland, GHOSTTY_DEBUG_INPUT_SCRIPT + screenshots)

  • echo hi + Enter: output hi shown. PASS.
  • ctrl+shift+t: new tab, typing works in it. PASS.
  • ctrl+shift+o / ctrl+shift+e: right and down splits, each pane renders and quits cleanly. PASS.
  • ctrl+shift+a, ctrl+shift+c, ctrl+shift+v: select all highlighted, copied text pasted back. PASS.
  • ctrl+= x4: font size 12 -> 16, glyphs visibly larger. PASS.
  • The "panic after the first screenshot" did not reproduce; the likely cause was the selection panic (item 1), now fixed. Observed instead: a screenshot taken <~1.6 s after opening a new tab/split can fail with "no frame has been presented yet" (new surface's first frame is slow in a debug build); use longer waits.
  • A quit with a child shell lacking shell integration (sh) shows the close-confirmation dialog, so the process stays alive (expected behaviour).

Still open

  • Not exercised in the GTK check: mouse, IME via real IM, clipboard confirmation dialogs.
  • Runtime warnings unchanged: unimplemented action=CellSize, cgroup scope creation failing in this sandbox.