From d233538f2cf3368007fee4cf3e9ea45c40d3ef45 Mon Sep 17 00:00:00 2001 From: Erik Date: Wed, 19 Aug 2026 21:53:19 +0200 Subject: [PATCH] fix(tests): the launcher/headless command-line contract needs executable stubs on Linux MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Run 173's Linux job failed on the contract test added one commit earlier — my test, not the product. LauncherExecutableSet refuses a host that exists but has no execute bit on Linux (HasUnixExecutePermission), which is a real and useful check: an update whose extraction lost its permissions would otherwise fail deep inside process start instead of at the launch gate. The stubs were written with File.WriteAllText, which is 0644, so on Linux every one of the four tests died at that gate before reaching the command-line contract they exist to pin. Windows never sees this — the predicate short-circuits to true off Linux — so the test passed locally and could only fail on the runner. Stubs are now created through a helper that chmods them executable on non- Windows. Verified on Windows (4 passed); the Linux half is what run 174 checks. Co-Authored-By: Claude Opus 5 --- ...auncherHeadlessCommandLineContractTests.cs | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/tests/AcDream.Headless.Tests/LauncherHeadlessCommandLineContractTests.cs b/tests/AcDream.Headless.Tests/LauncherHeadlessCommandLineContractTests.cs index 6ed44d9c..4772af4d 100644 --- a/tests/AcDream.Headless.Tests/LauncherHeadlessCommandLineContractTests.cs +++ b/tests/AcDream.Headless.Tests/LauncherHeadlessCommandLineContractTests.cs @@ -32,8 +32,28 @@ public sealed class LauncherHeadlessCommandLineContractTests : IDisposable // the contract can only be exercised against files that exist. Directory.CreateDirectory(AppDirectory); string suffix = OperatingSystem.IsWindows() ? ".exe" : string.Empty; - File.WriteAllText(Path.Combine(AppDirectory, "AcDream.App" + suffix), "stub"); - File.WriteAllText(Path.Combine(AppDirectory, "acdream-headless" + suffix), "stub"); + CreateStubExecutable(Path.Combine(AppDirectory, "AcDream.App" + suffix)); + CreateStubExecutable(Path.Combine(AppDirectory, "acdream-headless" + suffix)); + } + + /// + /// On Linux the spec builders also refuse a host that exists but has no + /// execute bit — a real check, since an extracted update that lost its + /// permissions would otherwise fail deep inside process start. A stub + /// written with WriteAllText is 0644, so it has to be made executable for + /// the command-line contract underneath to be reachable at all. + /// + private static void CreateStubExecutable(string path) + { + File.WriteAllText(path, "stub"); + if (!OperatingSystem.IsWindows()) + { + File.SetUnixFileMode( + path, + UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute + | UnixFileMode.GroupRead | UnixFileMode.GroupExecute + | UnixFileMode.OtherRead | UnixFileMode.OtherExecute); + } } public void Dispose()