Public Access
Let the suite run on Linux, and fix the three things that stopped it
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:
@@ -98,26 +98,89 @@ public static class LocalDirectory
|
||||
/// Where the local pane can start from: the drives on Windows, and the root elsewhere.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Ready drives only. An empty optical drive or a disconnected network mapping is listed by
|
||||
/// <para>
|
||||
/// Ready drives only, on Windows. An empty optical drive or a disconnected network mapping is listed by
|
||||
/// <see cref="DriveInfo.GetDrives"/> and throws on the first attempt to read it, which would put a row on
|
||||
/// screen whose only behaviour is an error.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Unix does not go through <see cref="DriveInfo.GetDrives"/> at all, and the summary above was true of
|
||||
/// the intent long before it was true of the code. That call returns every mount the kernel holds, which
|
||||
/// on an ordinary laptop is upwards of forty: <c>/proc</c>, <c>/sys/fs/bpf</c>, one per installed snap,
|
||||
/// <c>/run/user/1000/doc</c>. The transfers screen draws a button per root, so the bar ran to some five
|
||||
/// thousand pixels inside an eight-hundred pixel window — found by the layout suite the first time it
|
||||
/// ran on Linux, which is the whole reason that suite measures rather than eyeballs.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Filtering that list is not workable, and it is worth writing down why so nobody tries it again.
|
||||
/// Neither property that looks like it would separate a real volume from the kernel's bookkeeping does:
|
||||
/// <see cref="DriveInfo.DriveType"/> answers <c>Fixed</c> for <c>/</c> and <c>/home</c>, but equally for
|
||||
/// every squashfs snap, for <c>efivarfs</c> and for <c>tracefs</c>, while <c>/boot/efi</c> comes back
|
||||
/// <c>Removable</c>. <see cref="DriveInfo.DriveFormat"/> would need a hand-maintained list of every
|
||||
/// virtual filesystem Linux might grow, which is a list that is wrong the moment it is written.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// So this names what somebody would actually want rather than subtracting what they would not: the
|
||||
/// root, their home, and whatever is mounted where removable media conventionally mounts. A volume
|
||||
/// anywhere else is still reachable by navigating from <c>/</c>, which is what the pane is for.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public static IReadOnlyList<string> Roots()
|
||||
{
|
||||
var roots = new List<string>();
|
||||
|
||||
foreach (var drive in DriveInfo.GetDrives())
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
foreach (var drive in DriveInfo.GetDrives())
|
||||
{
|
||||
try
|
||||
{
|
||||
if (drive.IsReady)
|
||||
{
|
||||
roots.Add(drive.RootDirectory.FullName);
|
||||
}
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
// A drive that fails even to answer whether it is ready. Nothing to show.
|
||||
}
|
||||
}
|
||||
|
||||
return roots;
|
||||
}
|
||||
|
||||
roots.Add("/");
|
||||
|
||||
var home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
|
||||
if (!string.IsNullOrEmpty(home) && Directory.Exists(home))
|
||||
{
|
||||
roots.Add(home);
|
||||
}
|
||||
|
||||
// Where a desktop mounts a stick. /run/media/<user> is udisks2's, and so the one that matters on a
|
||||
// current distribution; /media and /mnt stay because a hand-written fstab still uses them, and
|
||||
// /Volumes is macOS, which reaches this branch too.
|
||||
string[] mountParents =
|
||||
[
|
||||
"/run/media/" + Environment.UserName,
|
||||
"/media",
|
||||
"/mnt",
|
||||
"/Volumes",
|
||||
];
|
||||
|
||||
foreach (var parent in mountParents)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (drive.IsReady)
|
||||
if (Directory.Exists(parent))
|
||||
{
|
||||
roots.Add(drive.RootDirectory.FullName);
|
||||
roots.AddRange(Directory.EnumerateDirectories(parent));
|
||||
}
|
||||
}
|
||||
catch (IOException)
|
||||
catch (Exception e) when (e is IOException or UnauthorizedAccessException)
|
||||
{
|
||||
// A drive that fails even to answer whether it is ready. Nothing to show.
|
||||
// A media directory that will not be listed contributes nothing. Failing to offer a
|
||||
// shortcut to a stick is not a reason to have no roots bar at all.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user