Skip to content

test: unify exec framework - #209

Open
ThomasWaldmann wants to merge 2 commits into
tqdm:mainfrom
ThomasWaldmann:fix-bash-test
Open

test: unify exec framework#209
ThomasWaldmann wants to merge 2 commits into
tqdm:mainfrom
ThomasWaldmann:fix-bash-test

Conversation

@ThomasWaldmann

@ThomasWaldmann ThomasWaldmann commented Nov 19, 2025

Copy link
Copy Markdown
Contributor

Repurposed as requested to fix #243.

Every shell now gets the same black-box helper: give it a generated completion and a
command line, get back the candidates that shell actually offers.
candidates(shell, completion, cmdline) dispatches, so the per-shell if/elif
branches collapse into a single assertion and most skip("WiP")s are gone.

before after
bash Bash.compgen, poking the generated script's internal arrays bash_candidates: sets up COMP_WORDS/COMP_CWORD the way readline does (COMP_WORDBREAKS splitting included) and calls the registered completion function
zsh zsh_specs (the _arguments spec array) zsh_candidates: zsh has no complete -C, so drive an interactive zsh through a pty with a widget that only ever lists, so even a unique match shows up
tcsh pty + autolist, script written into cwd shares the pty driver; lists via ^D (list-choices) so unique matches show up, and the script is written outside cwd so it is not a candidate itself
fish complete -C same mechanism, shared (completion, cmdlines, cwd) signature

zsh_specs is kept rather than renamed: it answers a different question (which spec was
generated
), which is what the #224 quoting tests need.

The first commit is the original content of this PR (Bash.test captured neither stdout
nor stderr, so assert not stdout was vacuous) — capturing the output is what makes
reading back bash's candidates possible. Bash itself has no users left and is removed.

Test counts: 71 passed / 33 skipped → 105 passed / 9 skipped / 5 xfailed (runtime 2.6s → 24s,
the pty-driven shells).

Running the same expectations on every shell surfaces five real differences

Marked xfail (so they show up under the existing -rxs) rather than skipped — happy to
split any of these off into their own issues:

The last two are exactly what test_subparser_colons/test_subparser_slashes were written
to guard against; they were invisible while only bash was exercised.

@codecov

codecov Bot commented Nov 19, 2025

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 89.18%. Comparing base (fe5f177) to head (201bf5c).

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #209   +/-   ##
=======================================
  Coverage   89.18%   89.18%           
=======================================
  Files           3        3           
  Lines         370      370           
=======================================
  Hits          330      330           
  Misses         40       40           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@ThomasWaldmann

Copy link
Copy Markdown
Contributor Author

Brought to you by google antigravity. :-)

@casperdcl
casperdcl force-pushed the main branch 4 times, most recently from 8fd5dfa to ca739c5 Compare August 2, 2026 20:32
@casperdcl casperdcl mentioned this pull request Aug 4, 2026
2 tasks

@casperdcl casperdcl left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would prefer if you could repurpose this PR to fix #243 :)

@casperdcl casperdcl linked an issue Aug 4, 2026 that may be closed by this pull request
2 tasks
@casperdcl casperdcl added testing Unit tests & debugging technical-debt Refactoring, linting & tidying labels Aug 4, 2026
Previously, the `Bash.test` helper did not capture stdout or stderr,
causing them to be None. This meant that the assertion `not stdout`
was always true (vacuously), and if a test failed, the error message
contained no output.

Changed the assertion and also the assertion failure message to not
hide the "None/None" issue.

This commit adds output capturing to `subprocess.Popen`, ensuring that
test failures provide useful debugging information -- and it is the
prerequisite for reading back what bash actually completes.
@ThomasWaldmann ThomasWaldmann changed the title test: capture stdout/stderr in Bash.test helper test: unify exec framework Aug 7, 2026
@ThomasWaldmann

Copy link
Copy Markdown
Contributor Author

Would prefer if you could repurpose this PR to fix #243 :)

I asked Claude to do it, this is the result. Review carefully! :)

Every shell now gets the same black-box helper: give it a generated
completion and a command line, get back the candidates that shell
actually offers.

- `bash_candidates` replaces `Bash.compgen`: instead of poking the
  generated script's internal arrays, set up `COMP_WORDS`/`COMP_CWORD`
  the way readline does (`COMP_WORDBREAKS` splitting included) and call
  the registered completion function.
- `zsh_candidates` is new: zsh has no `complete -C`, so drive an
  interactive zsh through a pty (like tcsh) with a completion widget
  that only ever lists, so even a unique match shows up.
- `fish_candidates`/`tcsh_candidates` keep their mechanism but share the
  pty driver and the `(completion, cmdlines, cwd)` signature; tcsh now
  lists via `^D` rather than `autolist` (again: unique matches) and the
  script is written outside `cwd` so it is not a completion candidate
  itself.
- `zsh_specs` stays: it answers a different question (which `_arguments`
  spec was generated), which is what the quoting tests need.

`candidates(shell, completion, cmdline)` then dispatches, so the
per-shell `if`/`elif` branches collapse into one assertion and most
`skip("WiP")`s are gone. `Bash` had no users left and is removed.

Running the same expectations on every shell turns up five genuine
differences, marked `xfail` rather than skipped:

- tcsh completes files, not subcommands, after a global option's value
- tcsh offers the options of sibling subcommands
- zsh re-offers earlier positionals' choices at a later slot
- zsh's `_describe` eats a `:` in a subcommand name
- tcsh treats a `/` in a word list as a pathname separator
@codecov

codecov Bot commented Aug 7, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 85.20%. Comparing base (f16f4b4) to head (32480fe).

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #209   +/-   ##
=======================================
  Coverage   85.20%   85.20%           
=======================================
  Files           3        3           
  Lines         473      473           
  Branches       93       93           
=======================================
  Hits          403      403           
  Misses         39       39           
  Partials       31       31           
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@yangfan-yf-yf yangfan-yf-yf left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found three paths where the new execution harness can report expected test outcomes without having exercised a successful completion. Details inline. The hosted checks are green, but they do not cover these failure paths.

Comment thread tests/test_shtab.py
printf '%s\\n' "${{COMPREPLY[@]}}\""""
proc = subprocess.run(['bash', '-o', 'pipefail', '-uc', completion + driver], cwd=cwd,
capture_output=True, text=True)
assert not proc.stderr, proc.stderr

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This checks only stderr and parses stdout even when Bash exits with a non-zero status. For example, bad() { exit 7; }; complete -F bad myprog is reported as an empty candidate list, so a broken completion is indistinguishable from a valid no-match result. The trailing printf also masks a completion function that returns non-zero without exiting. Please propagate the function status, check proc.returncode, and add regression cases for both exit and return.

Comment thread tests/test_shtab.py
nonlocal output
strip = r"\x1b\[[0-9;?]*[A-Za-z]|\x1b[=>]|[\a\r\b]"
deadline = time.time() + timeout
while output.count(PROMPT) < prompts and time.time() < deadline:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the deadline expires, read() returns partial output without verifying that the requested prompt was observed. The caller then parses that output as candidates, so an empty result can satisfy negative assertions even though zsh or tcsh never completed setup or completion. Please raise on a missing prompt and apply a bounded deadline to the drain phase as well.

Comment thread tests/test_shtab.py
assert {"create", "delete", "list"} <= set(candidates)
completion = complete(test_parser, shell)
if shell == 'tcsh':
pytest.xfail("tcsh completes files instead of subcommands after a global option's value")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This calls pytest.xfail() before the affected candidate query and assertion. The branch therefore never checks the documented tcsh behavior and can never XPASS after it is fixed. The same pattern occurs in the other four shell-specific xfails on this head. Please mark the affected parameter with pytest.mark.xfail(..., strict=True) so that the query and assertion still run.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

technical-debt Refactoring, linting & tidying testing Unit tests & debugging

Projects

None yet

Development

Successfully merging this pull request may close these issues.

tests: unify exec framework

3 participants