Let the suite run on Linux, and fix the three things that stopped it
ci / build and test (push) Failing after 1m47s
ci / api image (push) Skipped
ci / android head (push) Failing after 5s

The pipeline finally reached the tests and found four failures. None was the pipeline's,
and only one of the four was a test being fussy about a platform rather than telling the
truth about one.

The local pane's roots bar was the real bug. LocalDirectory.Roots built it from
DriveInfo.GetDrives on every platform, and its own summary — "the drives on Windows, and
the root elsewhere" — had been describing an intention rather than the code for as long as
nobody ran it off Windows. On Unix that call answers with every mount the kernel holds:
/proc, /sys/fs/bpf, one per installed snap, /run/user/1000/doc, some forty on an ordinary
laptop. The transfers screen draws a button per root, so the bar ran to about five thousand
pixels inside an eight-hundred pixel window. Anybody running the Linux build has been
looking at that.

Filtering GetDrives is not the fix and the comment now says why at length, because it is
the obvious thing to try: DriveType answers Fixed for / and /home and equally for every
squashfs snap, for efivarfs and for tracefs, while /boot/efi comes back Removable, and
DriveFormat would need a hand-kept list of every virtual filesystem Linux might grow. So
Unix now names what somebody would want instead of subtracting what they would not — the
root, their home, and whatever is mounted under /run/media/<user>, /media, /mnt or
/Volumes. Anything else is still reachable by navigating from /, which is what the pane is
for. Windows is untouched.

ClientPathsTests looked for "odoSSH" in the profile directory. ClientPaths spells it
DodoSSH on Windows and dodossh on Unix deliberately, one per platform convention, and that
substring was clever enough to survive either spelling of the leading D while still only
ever matching one of them. Now OrdinalIgnoreCase.

WhyTheWindowItselfIsNeverShown asserted a COMException with HResult RPC_E_CHANGED_MODE,
which is WebView2 refusing an MTA thread — a Win32 component raising a COM error. On Linux
the adapter is a different implementation with no apartment to disagree about, so showing
the window works and Should.Throw catches nothing. Skipped there rather than loosened to
accept both outcomes: the assertion is the documentation in that test, and one that passed
everywhere would have stopped recording the constraint it exists to record.

The fourth was CI's alone, and the diagnosis is the useful part. All 69 layout tests failed
on the runner while 6 failed here, which looked like missing fonts and was not: Avalonia's
headless renderer is Skia, libSkiaSharp.so links against libfontconfig, and without it the
suite dies in HeadlessUnitTestSession with a TypeInitializationException on SKImageInfo
naming none of its actual subjects. The job installs the one library now. Verified in a
container where fc-list returns zero and the suite passes regardless, because the
application carries Inter itself — fonts were never the problem, only the thing that would
have looked for them.

The whole solution now passes on Linux: 19 suites, 1295 tests, 0 failures, 4 skipped, the
end-to-end Testcontainers suite included. README and platform-flags.md said testing was
Windows-only, which CI now contradicts on every push, so both say what is true instead and
the two findings are written down where the next person will look for them. macOS is still
untested and now says so on its own rather than hiding inside "not Windows".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-01 14:28:06 +02:00
co-authored by Claude Opus 5
parent 71c0bd8882
commit 43d76d0f2d
6 changed files with 154 additions and 13 deletions
+23 -2
View File
@@ -1,8 +1,10 @@
# Platform flags
Things known or suspected to behave differently outside Windows, plus deployment gotchas that
have already cost time once. **Development and testing are currently Windows-only**, so anything
here marked *unverified* has not run on the platform in question and must not be assumed to work.
have already cost time once. Development is Windows-first, but **the full test suite now runs on
Linux in CI on every change**, so a Linux claim here is usually a measurement now rather than a
suspicion. **macOS is still untested**, and anything marked *unverified* has not run on the platform
in question and must not be assumed to work.
Each entry says what the risk is, why it matters, and what to do about it. Delete an entry when it
has been verified or made moot — not when it merely stops being convenient.
@@ -28,6 +30,17 @@ or notarization fails with an error that does not name the offending file.
## Desktop client
**The local pane's roots bar is built differently per platform, and has to be.** On Windows it is the
ready drives, from `DriveInfo.GetDrives`. On Unix that same call answers with every mount the kernel
holds — around forty on an ordinary laptop, counting `/proc`, `/sys/fs/bpf`, one per installed snap and
`/run/user/1000/doc` — and the transfers screen draws a button per root, so the bar ran to roughly five
thousand pixels inside an eight-hundred pixel window. *Fixed* in `LocalDirectory.Roots`, which on Unix
returns the root, the user's home, and whatever is mounted under `/run/media/<user>`, `/media`, `/mnt`
or `/Volumes`. Do not try to filter `GetDrives` instead: `DriveType` reports `Fixed` for `/` and `/home`
but also for every squashfs snap, for `efivarfs` and for `tracefs`, while `/boot/efi` comes back
`Removable`, and `DriveFormat` would need a hand-kept list of every virtual filesystem Linux may grow.
Found by the layout suite on its first Linux run, which is the argument for that suite existing.
**The WebView runs on Windows.** `Avalonia.Controls.WebView` 12.0.1 (MIT, no licence key) hosts the
terminal page: WebView2 launches, navigates to the loopback page, runs its JavaScript and completes the
WebSocket handshake. Verified by observing an established TCP connection from `msedgewebview2` to the data
@@ -411,6 +424,14 @@ be honest about: the cache offers no protection against another process running
## Build and CI
**The layout suite needs `libfontconfig` installed, and nothing else.** Avalonia's headless renderer is
still Skia, and `libSkiaSharp.so` — which the test project copies into its own output — links against
it. On an image without it, every one of the 69 tests fails inside `HeadlessUnitTestSession` before
measuring anything, reported as a `TypeInitializationException` on `SkiaSharp.SKImageInfo` that names
none of the tests' actual subjects. The CI job installs it. Worth being precise that this is the
library and not fonts: verified in a container where `fc-list` returns zero and the suite passes anyway,
because the application carries Inter itself.
**Integration tests need a Docker daemon** (Testcontainers). They run on `ubuntu-latest` in CI.
macOS runners have no Docker daemon, and the Windows CI job is deliberately build-only. So
anything proved by an integration test is proved on Linux only — which is the right place for