Files

249 lines
11 KiB
C#

using System.Runtime.Versioning;
using System.Xml.Linq;
using Avalonia.Controls;
using Shouldly;
using Xunit;
namespace AtomUI.Desktop.Controls.Tests.Window;
public class WindowingPlatformDetectionTests
{
[Theory]
[InlineData(AtomUIWindowingPlatform.Wayland, null, null, AtomUIWindowingPlatform.Wayland)]
[InlineData(AtomUIWindowingPlatform.X11, "wayland", "wayland-0", AtomUIWindowingPlatform.X11)]
[InlineData(AtomUIWindowingPlatform.Auto, "wayland", null, AtomUIWindowingPlatform.Wayland)]
[InlineData(AtomUIWindowingPlatform.Auto, " X11 ", "wayland-0", AtomUIWindowingPlatform.X11)]
[InlineData(AtomUIWindowingPlatform.Auto, null, "wayland-0", AtomUIWindowingPlatform.Wayland)]
[InlineData(AtomUIWindowingPlatform.Auto, null, null, AtomUIWindowingPlatform.X11)]
[InlineData(AtomUIWindowingPlatform.Auto, "", "", AtomUIWindowingPlatform.X11)]
public void Linux_Windowing_Platform_Resolution_Uses_Approved_Precedence(
AtomUIWindowingPlatform requested,
string? environmentOverride,
string? waylandDisplay,
AtomUIWindowingPlatform expected)
{
DesktopAppBuilderExtensions.ResolveLinuxWindowingPlatform(
requested,
environmentOverride,
waylandDisplay)
.ShouldBe(expected);
}
[Fact]
public void Invalid_Environment_Override_Fails_Clearly()
{
var exception = Should.Throw<InvalidOperationException>(() =>
DesktopAppBuilderExtensions.ResolveLinuxWindowingPlatform(
AtomUIWindowingPlatform.Auto,
"mir",
"wayland-0"));
exception.Message.ShouldContain(DesktopAppBuilderExtensions.WindowingPlatformEnvironmentVariable);
exception.Message.ShouldContain("mir");
}
[Fact]
public void Desktop_Platform_Detection_Does_Not_Expand_AtomUI_Core_Backend_Dependencies()
{
var coreProject = File.ReadAllText(GetRepoFile("src/AtomUI.Core/AtomUI.Core.csproj"));
var desktopProject = File.ReadAllText(GetRepoFile(
"src/AtomUI.Desktop.Controls/AtomUI.Desktop.Controls.csproj"));
coreProject.ShouldNotContain("Avalonia.Wayland");
coreProject.ShouldNotContain("Avalonia.Desktop");
desktopProject.ShouldContain("Avalonia.Desktop");
desktopProject.ShouldContain("Avalonia.Wayland");
}
[Fact]
public void Wayland_Chrome_Does_Not_Call_X11_Handle_Or_Absolute_Geometry_Hacks()
{
var commonSource = File.ReadAllText(GetRepoFile(
"src/AtomUI.Desktop.Controls/Window/Chrome/AbstractLinuxWindowChromeManager.cs"));
var waylandSource = File.ReadAllText(GetRepoFile(
"src/AtomUI.Desktop.Controls/Window/Chrome/WaylandWindowChromeManager.cs"));
var x11Source = File.ReadAllText(GetRepoFile(
"src/AtomUI.Desktop.Controls/Window/Chrome/X11WindowChromeManager.cs"));
commonSource.ShouldContain("new X11WindowChromeManager(window)");
commonSource.ShouldContain("new WaylandWindowChromeManager(window)");
commonSource.ShouldContain("new GenericLinuxWindowChromeManager(window)");
commonSource.ShouldContain("HandleDescriptor, \"XID\"");
waylandSource.ShouldNotContain("ConfigureLinuxInitialWindowGeometry");
waylandSource.ShouldNotContain("SetLinuxX11CsdFrameExtents");
waylandSource.ShouldNotContain("AttachClickThroughShadow");
waylandSource.ShouldNotContain("Position");
waylandSource.ShouldNotContain("PlatformImpl?.Handle");
x11Source.ShouldContain("ConfigureLinuxInitialWindowGeometry");
x11Source.ShouldContain("SetLinuxX11CsdFrameExtents");
x11Source.ShouldContain("AttachClickThroughShadow");
}
[Fact]
public void Window_Consumes_Custom_Resizer_As_A_Chrome_Manager_Capability()
{
var windowSource = File.ReadAllText(GetRepoFile(
"src/AtomUI.Desktop.Controls/Window/Window.cs"));
var contractSource = File.ReadAllText(GetRepoFile(
"src/AtomUI.Desktop.Controls/Window/Chrome/WindowChromeManager.cs"));
var linuxSource = File.ReadAllText(GetRepoFile(
"src/AtomUI.Desktop.Controls/Window/Chrome/AbstractLinuxWindowChromeManager.cs"));
var waylandSource = File.ReadAllText(GetRepoFile(
"src/AtomUI.Desktop.Controls/Window/Chrome/WaylandWindowChromeManager.cs"));
contractSource.ShouldContain("bool UsesCustomResizer { get; }");
linuxSource.ShouldContain("public virtual bool UsesCustomResizer => !Window.IsCsdEnabled;");
waylandSource.ShouldContain("public override bool UsesCustomResizer => true;");
windowSource.ShouldContain("_platformChromeManager?.UsesCustomResizer == true");
windowSource.ShouldNotContain("_platformChromeManager is not WaylandWindowChromeManager");
}
[Theory]
[SupportedOSPlatform("linux")]
[InlineData(AtomUIWindowingPlatform.X11, null, null, 1)]
[InlineData(AtomUIWindowingPlatform.Wayland, "XID", "Avalonia.X11", 2)]
[InlineData(null, "XID", "Avalonia.X11", 1)]
[InlineData(null, null, "Avalonia.Wayland", 2)]
[InlineData(null, null, "Avalonia.Headless", 0)]
[InlineData(null, "FB", "Avalonia.LinuxFramebuffer", 0)]
public void Linux_Chrome_Backend_Requires_Positive_Platform_Evidence(
AtomUIWindowingPlatform? configuredPlatform,
string? handleDescriptor,
string? platformAssemblyName,
int expected)
{
AbstractLinuxWindowChromeManager.ResolveBackend(
configuredPlatform,
handleDescriptor,
platformAssemblyName)
.ShouldBe((LinuxWindowingBackend)expected);
}
[Theory]
[SupportedOSPlatform("linux")]
[InlineData(0, true)]
[InlineData(1, true)]
[InlineData(2, false)]
public void Caption_Pin_Support_Follows_Linux_Backend_Capability(
int backend,
bool expected)
{
AbstractLinuxWindowChromeManager.IsPinCaptionButtonSupported((LinuxWindowingBackend)backend)
.ShouldBe(expected);
}
[Theory]
[SupportedOSPlatform("windows")]
[InlineData(true, true, WindowState.Normal, true)]
[InlineData(false, true, WindowState.Normal, false)]
[InlineData(true, false, WindowState.Normal, false)]
[InlineData(true, true, WindowState.Maximized, false)]
[InlineData(true, true, WindowState.FullScreen, false)]
public void Windows_Visible_Frame_Border_Is_Only_Used_For_Extended_Normal_Csd_Windows(
bool isCsdEnabled,
bool isExtendedIntoWindowDecorations,
WindowState windowState,
bool expected)
{
WindowsWindowChromeManager.ShouldUseVisibleFrameBorder(
isCsdEnabled,
isExtendedIntoWindowDecorations,
windowState)
.ShouldBe(expected);
}
[Fact]
public void Wayland_Disables_Caption_Pin_Through_Effective_State()
{
var captionSource = File.ReadAllText(GetRepoFile(
"src/AtomUI.Desktop.Controls/WindowTitleBar/CaptionButtonGroup.cs"));
var linuxChromeSource = File.ReadAllText(GetRepoFile(
"src/AtomUI.Desktop.Controls/Window/Chrome/AbstractLinuxWindowChromeManager.cs"));
var captionDocument = XDocument.Load(GetRepoFile(
"src/AtomUI.Desktop.Controls/WindowTitleBar/Themes/CaptionButtonGroupTheme.axaml"));
var pinButtons = captionDocument.Descendants()
.Where(element =>
element.Name.LocalName is "CaptionButton" or "WindowsCaptionButton" &&
(string?)element.Attribute("Name") == "PART_PinButton")
.ToList();
captionSource.ShouldContain("IsPinButtonEffectivelyVisibleProperty");
captionSource.ShouldContain("IsPinCaptionButtonSupportedProperty");
captionSource.ShouldContain("IsPinCaptionButtonVisible && IsPinCaptionButtonSupported");
linuxChromeSource.ShouldContain("IsPinCaptionButtonSupported");
linuxChromeSource.ShouldContain("LinuxWindowingBackend.Wayland");
pinButtons.Count.ShouldBe(3);
pinButtons.ShouldAllBe(button =>
(string?)button.Attribute("IsVisible") == "{TemplateBinding IsPinButtonEffectivelyVisible}");
pinButtons.ShouldAllBe(button =>
(string?)button.Attribute("IsEnabled") == "{TemplateBinding IsPinButtonEffectivelyVisible}");
}
[Fact]
public void Linux_Csd_Tracks_Platform_Decoration_Requests_And_Uses_AtomUI_Theme()
{
var commonSource = File.ReadAllText(GetRepoFile(
"src/AtomUI.Desktop.Controls/Window/Chrome/AbstractLinuxWindowChromeManager.cs"));
var windowSource = File.ReadAllText(GetRepoFile(
"src/AtomUI.Desktop.Controls/Window/Window.cs"));
var document = XDocument.Load(GetRepoFile(
"src/AtomUI.Desktop.Controls/Window/Themes/WindowTheme.axaml"));
XNamespace av = "https://github.com/avaloniaui";
commonSource.ShouldContain("platformImpl.DrawnDecorationsRequestChanged +=");
commonSource.ShouldContain("_window.RefreshPlatformCsdStatus();");
windowSource.ShouldContain("PlatformImpl?.NeedsManagedDecorations == true");
windowSource.ShouldContain("change.Property == IsExtendedIntoWindowDecorationsProperty");
document.Descendants(av + "Setter").ShouldContain(setter =>
(string?)setter.Attribute("Property") == "WindowDecorationsTheme" &&
(string?)setter.Attribute("Value") ==
"{DynamicResource {x:Type WindowDrawnDecorations}}");
}
[Fact]
public void Platform_Backend_Helpers_Configure_Renderer_And_Text_Shaping_Without_Reflection()
{
var source = File.ReadAllText(GetRepoFile(
"src/AtomUI.Desktop.Controls/DesktopAppBuilderExtensions.cs"));
source.ShouldContain("MethodImplOptions.NoInlining");
source.ShouldContain("builder.UseWayland()");
source.ShouldContain("builder.UseX11()");
source.ShouldContain(".UseSkia()");
source.ShouldContain(".UseHarfBuzz()");
source.ShouldNotContain("System.Reflection");
source.ShouldNotContain("Assembly.Load");
source.ShouldNotContain("Activator.CreateInstance");
}
[Fact]
public void Wayland_Backend_Disables_Server_Decoration_Negotiation_Before_Creating_Windows()
{
var source = File.ReadAllText(GetRepoFile(
"src/AtomUI.Desktop.Controls/DesktopAppBuilderExtensions.cs"));
source.ShouldContain("builder.With(new WaylandPlatformOptions");
source.ShouldContain("ForceDrawnDecorations = true");
}
private static string GetRepoFile(string relativePath)
{
var directory = new DirectoryInfo(AppContext.BaseDirectory);
while (directory is not null)
{
var candidate = Path.Combine(directory.FullName, relativePath);
if (File.Exists(candidate))
{
return candidate;
}
directory = directory.Parent;
}
throw new FileNotFoundException($"Could not find repository file: {relativePath}");
}
}