File: LinuxFolderApplicationTests.cs
Web Access
Project: src\tests\Aspire.Tray.Tests\Aspire.Tray.Tests.csproj (Aspire.Tray.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Diagnostics;
using System.Runtime.Versioning;
using Aspire.Tray.Tests.Helpers;
 
namespace Aspire.Tray.Tests;
 
public class LinuxFolderApplicationTests
{
    public static bool SupportsLinux => OperatingSystem.IsLinux();
 
    [Theory]
    [InlineData("code", "Visual Studio Code", "", "--")]
    [InlineData("code-insiders", "Visual Studio Code - Insiders", "", "--")]
    [InlineData("codium", "VSCodium", "", "--")]
    [InlineData("rider", "Rider", "", "")]
    [InlineData("subl", "Sublime Text", "", "--")]
    [InlineData("gnome-terminal", "GNOME Terminal", "", "--working-directory")]
    [InlineData("kgx", "GNOME Console", "", "--working-directory")]
    [InlineData("ptyxis", "Ptyxis", "--new-window", "--working-directory")]
    [InlineData("konsole", "Konsole", "", "--workdir")]
    [InlineData("xfce4-terminal", "Xfce Terminal", "", "--working-directory")]
    [InlineData("kitty", "Kitty", "", "--directory")]
    [InlineData("alacritty", "Alacritty", "", "--working-directory")]
    [InlineData("foot", "Foot", "", "--working-directory")]
    [InlineData("wezterm", "WezTerm", "start", "--cwd")]
    [InlineData("ghostty", "Ghostty", "", "--working-directory=")]
    public void DiscoveredApplicationsPassTheFolderAsData(string command, string title, string prefix, string option)
    {
        using var directory = new TestTrayStateDirectory();
        var executable = directory.CreateAppHost($"bin/{command}");
        if (OperatingSystem.IsLinux())
        {
            File.SetUnixFileMode(executable, UnixFileMode.UserRead | UnixFileMode.UserExecute);
        }
        var folder = Path.GetDirectoryName(directory.CreateAppHost("project 'quotes' & $var; %20 #/apphost.cs"))!;
 
        var application = Assert.Single(LinuxFolderApplication.Discover(Path.GetDirectoryName(executable)!));
        var start = application.CreateStartInfo(folder);
 
        Assert.Equal(title, application.Title);
        Assert.Equal(executable, start.FileName);
        Assert.Equal(folder, start.WorkingDirectory);
        Assert.False(start.UseShellExecute);
        List<string> expected = [];
        if (prefix.Length > 0)
        {
            expected.Add(prefix);
        }
        if (option.EndsWith('='))
        {
            expected.Add(option + folder);
        }
        else
        {
            if (option.Length > 0)
            {
                expected.Add(option);
            }
            expected.Add(folder);
        }
        Assert.Equal(expected, start.ArgumentList);
        Assert.Throws<DirectoryNotFoundException>(() => application.CreateStartInfo(Path.Combine(folder, "missing")));
    }
 
    [Fact(Skip = "Uses Linux executable permissions.", SkipUnless = nameof(SupportsLinux))]
    [SupportedOSPlatform("linux")]
    public void DiscoveryIgnoresRelativeAndNonExecutableEntriesAndKeepsPathPriority()
    {
        using var directory = new TestTrayStateDirectory();
        var first = directory.CreateAppHost("first/code");
        var ignored = directory.CreateAppHost("first/konsole");
        var second = directory.CreateAppHost("second/code");
        var terminal = directory.CreateAppHost("second/konsole");
        foreach (var executable in new[] { first, second, terminal })
        {
            File.SetUnixFileMode(executable, UnixFileMode.UserRead | UnixFileMode.UserExecute);
        }
        File.SetUnixFileMode(ignored, UnixFileMode.UserRead);
        var path = string.Join(Path.PathSeparator, "", ".", "relative", Path.GetDirectoryName(first), Path.GetDirectoryName(second));
 
        var applications = LinuxFolderApplication.Discover(path);
 
        Assert.Equal([first, terminal], applications.Select(application => application.Executable));
        Assert.Empty(LinuxFolderApplication.Discover(""));
    }
 
    [Fact(Skip = "Runs an isolated Unix executable.", SkipUnless = nameof(SupportsLinux))]
    [SupportedOSPlatform("linux")]
    public async Task TerminalLaunchPreservesSpecialCharactersWithoutShellExpansion()
    {
        using var directory = new TestTrayStateDirectory();
        var executable = directory.CreateAppHost("bin/gnome-terminal");
        File.WriteAllText(executable, "#!/bin/sh\nprintf '%s\\n' \"$PWD\" \"$@\" > arguments.txt\n");
        File.SetUnixFileMode(executable, UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute);
        var folder = Path.GetDirectoryName(directory.CreateAppHost("project $(echo expanded); 'quotes' & %20 #/apphost.cs"))!;
        var application = Assert.Single(LinuxFolderApplication.Discover(Path.GetDirectoryName(executable)!));
 
        using var process = Process.Start(application.CreateStartInfo(folder))!;
        using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(10));
        await process.WaitForExitAsync(timeout.Token);
 
        Assert.Equal(0, process.ExitCode);
        Assert.Equal([folder, "--working-directory", folder], File.ReadAllLines(Path.Combine(folder, "arguments.txt")));
    }
}