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()