Skip to content

Run the WSL tests wherever a distribution answers - #40

Merged
Korijn merged 1 commit into
masterfrom
test-wsl-wherever-it-answers
Aug 5, 2026
Merged

Run the WSL tests wherever a distribution answers#40
Korijn merged 1 commit into
masterfrom
test-wsl-wherever-it-answers

Conversation

@Korijn

@Korijn Korijn commented Aug 5, 2026

Copy link
Copy Markdown
Owner

Calling the Windows install of keycmd from a shell inside WSL was opt in through KEYCMD_TEST_WSL, on the grounds that installing WSL takes a CI job of its own. That holds for CI and nowhere else: a developer machine with WSL2 on it can run those tests today, and the only thing in the way was a variable nobody remembers to set.

The suite works it out for itself now. find_wsl in tests/conftest.py asks whether this is a Windows machine, whether a distribution answers — a different question from whether one is registered, since wsl.exe is on PATH on any Windows install and Docker's distributions run no shell — and whether there is a keycmd on PATH for it to reach. The wsl fixture then hands the tests the distribution, or skips them with the reason there is none, the way os_keyring does for the keyring. KEYCMD_REQUIRE_WSL=1 turns that skip into a failure, and the one CI job that provisions a distro sets it. The answer goes in the pytest header next to the shells, since a run that silently skipped these looks exactly like a run on a machine without WSL:

keyring backend: keyring.backends.Windows.WinVaultKeyring (priority: 5)
shells exercised: cmd, powershell, pwsh
WSL distribution: Ubuntu

What the tests needed of wsl.exe moves to the fixture with it, so a test reads wsl.sh(script) and wsl.path(p) rather than reaching for helpers of its own, and decode sniffs the utf-16 that wsl.exe writes its own errors in — a machine with no distribution registered says so in utf-16, and that sentence is now a skip reason.

A bug that would have made most of this moot

Subclassing a keyring backend registers it, for the session and with no way to take it back out, so FakeChainer in test_backend.py was a candidate in every search that ran after that module was imported. A chainer of two backends outranks the credential manager, so the keyring probe settled on a chainer of null and fail and concluded the machine has no keyring — and every credential test skipped itself, which is exactly what a machine without a keyring is supposed to look like.

It passed in CI only because -v prints the report header, which initializes keyring before collection. -q does not, so a local run quietly covered twenty tests fewer. The double sets viable = False and stays out of the search.

Verified

On Windows 11 with WSL2 (Ubuntu), KEYCMD_REQUIRE_OS_KEYRING=1 KEYCMD_REQUIRE_WSL=1:

  • 196 passed, 15 skipped, 99.13% coverage — where master gives 175 passed and 21 errors under the same flags
  • the three end to end tests pass against the real credential manager, from a real distribution
  • ruff check, ruff format --check, ty check --error-on-warning and mkdocs build --strict are clean

CI is the other half of the verification: the WSL job proves the require flag still fails loudly on a provisioned distro, and the three Windows/macOS/Linux jobs prove the new tests skip quietly where there is no WSL.

🤖 Generated with Claude Code

Calling the windows install of keycmd from a shell inside WSL is the half
of the WSL setup that needs both sides of the boundary to be real, and it
was opt in through KEYCMD_TEST_WSL, on the grounds that installing WSL
takes a CI job of its own. That reasoning holds for CI and nowhere else: a
developer machine with WSL2 on it can run those tests today, and the only
thing standing in the way was a variable nobody remembers to set.

The suite works it out for itself now. find_wsl in tests/conftest.py asks
whether this is a windows machine, whether a distribution answers -- which
is a different question from whether one is registered, since wsl.exe is
on PATH on any windows install and docker's distributions run no shell --
and whether there is a keycmd on PATH for it to reach. The `wsl` fixture
hands the tests the distribution or skips them with the reason there is
none, the way `os_keyring` does for the keyring, and KEYCMD_REQUIRE_WSL
turns that skip into a failure for the one CI job that provisions a distro.
The answer goes in the pytest header next to the shells, since a run that
silently skipped these looks exactly like a run on a machine without WSL.

What the tests needed of wsl.exe moves to the fixture with it, so a test
reads wsl.sh(script) and wsl.path(p) rather than reaching for helpers of
its own, and decode now sniffs the utf-16 that wsl.exe writes its own
errors in. A machine with no distribution registered says so in utf-16,
and that sentence is the skip reason, which is not the place to hand
someone W\x00i\x00n\x00d\x00o\x00w\x00s.

Which turned up a bug that would have made most of this moot. Subclassing
a keyring backend registers it, for the session and with no way to take it
back out, so FakeChainer in test_backend.py was a candidate in every
search that ran after that module was imported -- and a chainer of two
backends outranks the credential manager, so the keyring probe settled on
a chainer of null and fail and reported that this machine has no keyring.
Every credential test then skipped itself, which is what a machine without
a keyring is supposed to look like. It passed in CI only because -v prints
the report header, which initializes keyring before collection; -q does
not, so a local run covered twenty tests fewer without saying anything.
The double sets viable = False and stays out of the search.

Locally that is 196 passed where it was 175 passed and 21 skipped.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Korijn
Korijn merged commit 9304a40 into master Aug 5, 2026
11 checks passed
@Korijn
Korijn deleted the test-wsl-wherever-it-answers branch August 5, 2026 19:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant