mirror of
https://github.com/JanDeDobbeleer/oh-my-posh.git
synced 2026-08-24 10:14:12 -05:00
Add a separate e2e Go module that validates the shell integrations against real shells instead of only asserting generated script text: - layer 1: generate the init script per shell and feature-config overlay (base, transient, rprompt, tooltips, full) and validate it with the shell's own parser - layer 2: boot each shell interactively in a pty (ConPTY on Windows, creack/pty elsewhere) with a vt10x screen emulator, assert the prompt renders and the session exits cleanly - layer 3: feature scenarios asserting exit-code propagation, transient prompt replacement, and rprompt right-alignment Covers bash, zsh, fish, pwsh, and nu; tests skip cleanly when a shell binary is absent or the platform cannot drive it faithfully. The harness answers PSReadLine's DSR cursor queries on Unix ptys and isolates sessions from the host's cache and nu vendor autoloads. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Entire-Checkpoint: 8a75c4edb40b
4.4 KiB
4.4 KiB
Testing shells end-to-end
Patterns for functionally driving omp's shell integrations, mostly in WSL (verified on aarch64, zsh 5.9, fish 4.1.2).
The e2e module (e2e/)
A separate Go module with a cross-platform pty harness (go-pty + vt10x) that runs three layers
(syntax check, interactive smoke, feature scenarios) for bash/zsh/fish/pwsh/nu. See e2e/README.md
for usage. Gotchas baked into it, relevant to any future pty work:
- PSReadLine on a raw Unix pty floods
CSI 6n(DSR cursor-position) queries and wedges without a reply; ConPTY answers them internally on Windows. The harness's reader goroutine answers with the vt10x cursor position (harness/session.go). - nu autoloads every
.nuunder$nu.vendor-autoload-dirsAFTER--config, so a dev machine's real oh-my-posh nu integration clobbers the test prompt. Isolate withXDG_DATA_HOMEpointed at an empty dir (works on Windows too). - go-pty's Windows
Cmdresolves bare executable names relative toCmd.DirwhenDiris set. Always pass an absolute binary path. - Windows PATH resolves
bashto System32's WSL launcher, not Git Bash; it fails on Windows-style paths.harness.LookupShellBinaryderives Git Bash fromgit.exe's location. - bash transient and rprompt are ble.sh-only (
bashBLEsession, gated onBLE_SESSION_IDinsrc/shell/bash.go). Plain interactive bash gets no code for either feature.
WSL basics
- WSL
/tmpis wiped between separatewsl.exeinvocations (instance auto-shutdown). Either make a test fully self-contained in ONEwsl -ecall, or stage everything under$HOME(e.g.~/omp-test). - From Git Bash, prefix
MSYS_NO_PATHCONV=1so/mnt/c/...arguments reach wsl.exe unmangled. - Build for WSL either inside WSL (
/usr/local/go/bin/go) or cross-compile from Windows (GOOS=linux GOARCH=arm64). Shell scripts are embedded at build time - rebuild after every script edit, then regenerate the sourced init withoh-my-posh init <shell> --print. - Differential testing: build the control binary from
git show HEAD:<file>or viagit stash push -- <file>; the fixed binary from the working tree. pkill -f <pattern>matches its own caller's command line when inlined - pick patterns that cannot appear in the runner script.
Getting a pty
zsh -iunder plainwsl -ehangs before the first prompt (no foreground pty). Options:wsl -e script -qec 'zsh -i' /dev/null- real pty, MONITOR enabled, job-control behavior testable. Run backgrounded with output to a file and poll a done-marker; foreground runs can wedge under the wsl relay.zmodload zsh/zptyin a driver zsh - best for keystroke-level scenarios:zpty omp zsh -i, write raw keys withzpty -w -n omp $'\x03'(Ctrl+C delivers a real SIGINT through the pty), assert via state files written by in-session commands, drain output withzpty -rafter exit. Notescript(1)does NOT work inside zpty (typescript never appears).
- Per-shell
script(1)quirks: bash prompt bytes are not relayed to stdout (probe${PS1@P}in-session); fish discards piped typeahead (in-session probes never run); zsh relays fine. - Do not test the zsh serve path from a non-interactive
zsh script.zsh- it hangs inread -u fd -d $'\0' -t N(the-tis ignored with-d, see zsh).
Driving vi-mode / keystroke scenarios (zpty pattern, verified 2026-07-14)
- Harness pattern (built for #5992): stage a binary, config, zdot dirs, and zpty driver scripts
under
$HOMEin WSL; one driver runs mode-aware ESC/Enter/Ctrl+C scenarios into a state log, another asserts the transient prompt renders. Always compare a fixed run against a control zdot (same setup, feature under test disabled). - Keep probes mode-aware: with zsh-vi-mode, a line after a normal-mode accept starts in normal
mode - prefix typed commands with
iwhere needed, and remember a stray self-insertediturns the probe into a failing command (useful as a broken-state detector:last_status=127).
Keeping a render alive
- There is no shell-command segment type. To hold
omp stream/a render open for a controllable window, point anhttpsegment at a silent local TCP listener (python3 -c 'socket...accept...sleep') with a largehttp_timeout; kill the listener to trigger the async update record.
Reading protocol streams
- One
bufio.Scannerper pipe, ever - a second scanner on the same pipe loses buffered data (use a shared reader helper in Go tests).