mirror of
https://github.com/AtomUI/AtomUI.git
synced 2026-10-18 22:46:38 +08:00
162 lines
6.9 KiB
C#
162 lines
6.9 KiB
C#
using System.Reflection;
|
|
using Avalonia.Controls;
|
|
using Shouldly;
|
|
using Xunit;
|
|
|
|
namespace AtomUI.Desktop.Controls.Tests.Window;
|
|
|
|
public class LinuxWindowFixAotTests
|
|
{
|
|
[Fact]
|
|
public void Linux_Window_And_TitleBar_Popup_Fixes_Do_Not_Add_Aot_Unsafe_Code()
|
|
{
|
|
var source = string.Join(
|
|
Environment.NewLine,
|
|
File.ReadAllText(GetRepoFile("src/AtomUI.Desktop.Controls/Window/Window.cs")),
|
|
File.ReadAllText(GetRepoFile("src/AtomUI.Desktop.Controls/Window/Chrome/WindowChromeManager.cs")),
|
|
File.ReadAllText(GetRepoFile("src/AtomUI.Desktop.Controls/Window/Chrome/AbstractLinuxWindowChromeManager.cs")),
|
|
File.ReadAllText(GetRepoFile("src/AtomUI.Desktop.Controls/Window/Chrome/X11WindowChromeManager.cs")),
|
|
File.ReadAllText(GetRepoFile("src/AtomUI.Desktop.Controls/Window/Chrome/WaylandWindowChromeManager.cs")),
|
|
File.ReadAllText(GetRepoFile("src/AtomUI.Desktop.Controls/DesktopAppBuilderExtensions.cs")),
|
|
File.ReadAllText(GetRepoFile("src/AtomUI.Desktop.Controls/Popup/DetachedTitleBarPopupSupport.cs")),
|
|
File.ReadAllText(GetRepoFile("src/AtomUI.Desktop.Controls/Menu/Menu.cs")),
|
|
File.ReadAllText(GetRepoFile("src/AtomUI.Desktop.Controls/Menu/MenuItem.cs")));
|
|
|
|
foreach (var unsafePattern in s_aotUnsafePatterns)
|
|
{
|
|
source.ShouldNotContain(unsafePattern);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Managed_Resize_Grip_Reflection_Boundary_Is_Trimming_Safe()
|
|
{
|
|
var source = File.ReadAllText(GetRepoFile(
|
|
"src/AtomUI.Desktop.Controls/Window/Utils/WindowDrawnDecorationsReflectionExtensions.cs"));
|
|
|
|
source.ShouldContain("DynamicDependency");
|
|
source.ShouldContain("DynamicallyAccessedMemberTypes.NonPublicFields, typeof(TopLevel)");
|
|
source.ShouldContain("\"Avalonia.Controls.TopLevelHost\"");
|
|
source.ShouldContain("\"Avalonia.Controls.Chrome.ResizeGripLayer\"");
|
|
source.ShouldContain("DynamicallyAccessedMemberTypes.NonPublicProperties");
|
|
source.ShouldContain("\"GripThickness\"");
|
|
source.ShouldNotContain("Assembly.GetTypes");
|
|
source.ShouldNotContain("Activator.CreateInstance");
|
|
source.ShouldNotContain("RequiresUnreferencedCode");
|
|
source.ShouldNotContain("RequiresDynamicCode");
|
|
source.ShouldNotContain("UnconditionalSuppressMessage");
|
|
}
|
|
|
|
[Fact]
|
|
public void Avalonia_Managed_Resize_Grip_Internal_Contract_Is_Available()
|
|
{
|
|
var topLevelHostField = typeof(TopLevel).GetField(
|
|
"_topLevelHost",
|
|
BindingFlags.Instance | BindingFlags.NonPublic);
|
|
topLevelHostField.ShouldNotBeNull();
|
|
|
|
var decorationsField = topLevelHostField.FieldType.GetField(
|
|
"_decorations",
|
|
BindingFlags.Instance | BindingFlags.NonPublic);
|
|
decorationsField.ShouldNotBeNull();
|
|
decorationsField.FieldType.FullName.ShouldBe("Avalonia.Controls.Chrome.WindowDrawnDecorations");
|
|
|
|
var resizeGripsField = topLevelHostField.FieldType.GetField(
|
|
"_resizeGrips",
|
|
BindingFlags.Instance | BindingFlags.NonPublic);
|
|
resizeGripsField.ShouldNotBeNull();
|
|
resizeGripsField.FieldType.GetProperty(
|
|
"GripThickness",
|
|
BindingFlags.Instance | BindingFlags.NonPublic).ShouldNotBeNull();
|
|
}
|
|
|
|
[Fact]
|
|
public void Wayland_Input_Region_Reflection_Boundary_Is_Trimming_Safe()
|
|
{
|
|
var source = File.ReadAllText(GetRepoFile(
|
|
"src/AtomUI.Native/Linux/WaylandWindowReflectionExtensions.cs"));
|
|
|
|
source.ShouldContain("DynamicDependency");
|
|
source.ShouldContain("\"Avalonia.Wayland.WindowImpl\"");
|
|
source.ShouldContain("\"_surfaceProxy\"");
|
|
source.ShouldContain("\"Avalonia.Wayland.Server.Persistent.WXdgTopLevelProxy\"");
|
|
source.ShouldContain("ProxyTargetFieldInfo");
|
|
source.ShouldContain("\"_target\"");
|
|
source.ShouldContain("\"Avalonia.Wayland.Server.Persistent.WSurface\"");
|
|
source.ShouldContain("\"WlSurface\"");
|
|
source.ShouldContain("\"Globals\"");
|
|
source.ShouldContain("\"Avalonia.Wayland.Server.Transient.WaylandGlobals\"");
|
|
source.ShouldContain("\"WlCompositor\"");
|
|
source.ShouldNotContain("Assembly.GetTypes");
|
|
source.ShouldNotContain("Activator.CreateInstance");
|
|
source.ShouldNotContain("RequiresUnreferencedCode");
|
|
source.ShouldNotContain("RequiresDynamicCode");
|
|
source.ShouldNotContain("UnconditionalSuppressMessage");
|
|
}
|
|
|
|
[Fact]
|
|
public void Avalonia_Wayland_Input_Region_Internal_Contract_Is_Available()
|
|
{
|
|
var waylandAssembly = Assembly.Load("Avalonia.Wayland");
|
|
var windowImplType = waylandAssembly.GetType("Avalonia.Wayland.WindowImpl");
|
|
var proxyType = waylandAssembly.GetType(
|
|
"Avalonia.Wayland.Server.Persistent.WXdgTopLevelProxy");
|
|
var surfaceType = waylandAssembly.GetType(
|
|
"Avalonia.Wayland.Server.Persistent.WSurface");
|
|
var globalsType = waylandAssembly.GetType(
|
|
"Avalonia.Wayland.Server.Transient.WaylandGlobals");
|
|
|
|
windowImplType.ShouldNotBeNull();
|
|
windowImplType.GetField("_surfaceProxy", BindingFlags.Instance | BindingFlags.NonPublic)
|
|
.ShouldNotBeNull();
|
|
proxyType.ShouldNotBeNull();
|
|
proxyType.GetField("_target", BindingFlags.Instance | BindingFlags.NonPublic)
|
|
.ShouldNotBeNull();
|
|
surfaceType.ShouldNotBeNull();
|
|
surfaceType.GetProperty("WlSurface", BindingFlags.Instance | BindingFlags.Public)
|
|
.ShouldNotBeNull();
|
|
surfaceType.GetProperty("Globals", BindingFlags.Instance | BindingFlags.Public)
|
|
.ShouldNotBeNull();
|
|
globalsType.ShouldNotBeNull();
|
|
globalsType.GetProperty("WlCompositor", BindingFlags.Instance | BindingFlags.Public)
|
|
.ShouldNotBeNull();
|
|
}
|
|
|
|
private static readonly string[] s_aotUnsafePatterns =
|
|
[
|
|
"using System.Reflection",
|
|
"Assembly.GetTypes",
|
|
"GetCustomAttribute",
|
|
"Activator.CreateInstance",
|
|
"Expression.Compile",
|
|
"MakeGenericType",
|
|
"new Binding(",
|
|
"ReflectionBinding",
|
|
"WhenAnyValue",
|
|
"ToProperty",
|
|
"OneWayBind",
|
|
"BindCommand",
|
|
"ReactiveUserControl",
|
|
"RequiresUnreferencedCode",
|
|
"RequiresDynamicCode",
|
|
"UnconditionalSuppressMessage"
|
|
];
|
|
|
|
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}");
|
|
}
|
|
}
|