fix(spec): shell completion mocks across bash, zsh, and fish#6
Merged
Conversation
The mock never had a real terminal to fall back to, so two latent bash-completion quirks surfaced as test failures: - Interactive bash expanded `!` in `_filedir`'s xspec, breaking the extension filter for `apply --file ...`. - An unset `COLUMNS` collapsed `__cli1_format_comp_descriptions` into bare names, so `-e dev ""` lost its description columns. Disable history expansion, default `COLUMNS` to a wide value, and patch `_filedir` to handle the empty-`cur` case the way real bash does via `complete -o default`.
`vared -c tmp` inside `zpty {,}myfn` aborts with `free(): invalid
pointer` on glibc-based zsh 5.9 builds (Ubuntu 24.04, recent Homebrew),
so every zsh case in `mock_spec.cr` returned the abort string instead of
completions. Spawning a fresh `zsh -i -c 'source <file>'` inside the
zpty gives ZLE its own interactive session and avoids the heap
corruption. The completion setup is materialized to a tempfile so the
inner shell can pick it up.
Also drop the outer EXIT/signal trap: `zpty` propagates inherited traps
to its child, and after the first iteration that child runs the trap on
exit and deletes the source file the next iteration still needs.
Cleanup now runs once after the loop instead.
Tighten up the delimiter parser too — using `==` substring matches and
keeping the post-D_OPN remainder in `PTY_LINES` makes the unambiguous
single-match path work without a leading blank line.
Two macOS-specific regressions in the new zpty-based mock: - `mktemp -p /var/tmp …` is GNU-only. BSD `mktemp` (macOS) treats `-p` as the prefix flag, so the inner script either pointed at a stale path or was empty. Build the path explicitly under TMPDIR (or /tmp) and `:>` it instead, which both shells honor. - `zpty mock 'zsh -fi -c …'` returns immediately, but ZLE inside the inner shell isn't ready until later. macOS pty buffering let the TAB land before `bindkey` had taken effect, so `vared` sat there forever. Have the inner shell print a `\C-E` sentinel right before `vared`, and gate `zpty -w` on receiving it. The marker uses a trailing newline because the inner shell's stdout is line-buffered. Also pass `-f` alongside `-i` so the inner shell skips user rcfiles and starts up faster.
`Exception.new stderr.gets_to_end` produced a bare "Error:" message when the simulator died without writing to stderr. Reraise with the shell name and the original IO::Error so the failure has actionable detail instead of an empty string.
fish 3.x's `read` builtin closes stdin after the first line when run from a `-c` script with a piped stdin (fish-shell/fish-shell#5714). The previous `read -l LINE < /dev/stdin` worked around it on Linux but on macOS BSD `/dev/stdin` opens via /dev/fd/0, which EOFs on the second iteration and silently killed the loop — every fish case after the first failed with a broken pipe (and an empty exception message). Replace the `read` loop with `head -n 1` per iteration. The simulator is strictly synchronous (one line in, one response out), so `head`'s read-ahead never eats data the next iteration would need.
Drop the `branches: [main]` filter from `pull_request:` so test runs also fire for PRs targeting feature branches like `fix-relay`. Tests should run on every PR; the previous filter silently skipped CI when PRs were stacked on intermediate branches.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes the failing shell-completion tests on PR #5 across all three supported shells. Targets
fix-relayso PR #5 picks the changes up automatically.What was broken
apply --file,-e dev "") — bash-completion's_filedir <ext>returns nothing for emptycurbecause_quote_readline_by_ref ""becomes the literal''; real bash papers over this withcomplete -o default. Interactive bash also expanded the!in_filedir's xspec, and an unsetCOLUMNSmade__cli1_format_comp_descriptionsdrop descriptions.vared -c tmpinsidezpty {,}fnaborts withfree(): invalid pointeron glibc-based zsh 5.9 builds. The pty's stdout was the abort string, not completions.zpty -wagainst ZLE setup in the inner shell, and usedmktemp -p(GNU-only). On macOS BSD-pmeans prefix, so the source file path was wrong.Error:) — fish 3.xreadcloses stdin after one line when run from-cwith a piped stdin (fish-shell#5714). The historical< /dev/stdinworkaround opens/dev/fd/0on macOS and EOFs after one read. The simulator's rescue clause also swallowed the underlyingIO::Errorwhen stderr was empty, so the failure surfaced asError:with no message.Fixes (in order)
c39e93fset +H, defaultCOLUMNS=200, wrap_filedirto handle emptycur89cf657zsh -i -c 'source <file>'insidezptyso ZLE has its own session; drop the outer EXIT trap (zpty propagates it to the child); tighten the delimiter parser45bb4a9\C-E\nready handshake sozpty -wwaits forbindkeyto take effect5b51c6eIO::Errorinstead of an empty exception messagec698056read -l LINE < /dev/stdinwithhead -n 1per iteration; works on both Linux and macOSTest plan
crystal specpasses locally on Linux (Crystal 1.15.0, bash 5.2, zsh 5.9, fish 3.7) — 145/145crystal spec spec/mock_spec.cr --tag libraryall green (no flake)Generated by Claude Code