Przeglądaj źródła

Run tests on Avalonia.Headless

Max Katz 2 lat temu
rodzic
commit
5e4509deb1

+ 7 - 0
Avalonia.sln

@@ -250,6 +250,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Headless", "Headless", "{FF
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Headless.XUnit", "src\Headless\Avalonia.Headless.XUnit\Avalonia.Headless.XUnit.csproj", "{F47F8316-4D4B-4026-8EF3-16B2CFDA8119}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Headless.UnitTests", "tests\Avalonia.Headless.UnitTests\Avalonia.Headless.UnitTests.csproj", "{3B2405E8-9E7A-46D1-8E2D-EF9ED124C9F2}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -585,6 +587,10 @@ Global
 		{F47F8316-4D4B-4026-8EF3-16B2CFDA8119}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{F47F8316-4D4B-4026-8EF3-16B2CFDA8119}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{F47F8316-4D4B-4026-8EF3-16B2CFDA8119}.Release|Any CPU.Build.0 = Release|Any CPU
+		{3B2405E8-9E7A-46D1-8E2D-EF9ED124C9F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{3B2405E8-9E7A-46D1-8E2D-EF9ED124C9F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{3B2405E8-9E7A-46D1-8E2D-EF9ED124C9F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{3B2405E8-9E7A-46D1-8E2D-EF9ED124C9F2}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -655,6 +661,7 @@ Global
 		{8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC} = {FF237916-7150-496B-89ED-6CA3292896E7}
 		{B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E} = {FF237916-7150-496B-89ED-6CA3292896E7}
 		{F47F8316-4D4B-4026-8EF3-16B2CFDA8119} = {FF237916-7150-496B-89ED-6CA3292896E7}
+		{3B2405E8-9E7A-46D1-8E2D-EF9ED124C9F2} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A}

+ 1 - 0
nukebuild/Build.cs

@@ -212,6 +212,7 @@ partial class Build : NukeBuild
             RunCoreTest("Avalonia.Markup.Xaml.UnitTests");
             RunCoreTest("Avalonia.Skia.UnitTests");
             RunCoreTest("Avalonia.ReactiveUI.UnitTests");
+            RunCoreTest("Avalonia.Headless.UnitTests");
         });
 
     Target RunRenderTests => _ => _

+ 2 - 1
src/Headless/Avalonia.Headless/HeadlessPlatformRenderInterface.cs

@@ -19,7 +19,8 @@ namespace Avalonia.Headless
         public static void Initialize()
         {
             AvaloniaLocator.CurrentMutable
-                .Bind<IPlatformRenderInterface>().ToConstant(new HeadlessPlatformRenderInterface());
+                .Bind<IPlatformRenderInterface>().ToConstant(new HeadlessPlatformRenderInterface())
+                .Bind<IFontManagerImpl>().ToConstant(new HeadlessFontManagerStub());
         }
 
         public IEnumerable<string> InstalledFontNames { get; } = new[] { "Tahoma" };

+ 11 - 2
src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs

@@ -215,10 +215,19 @@ namespace Avalonia.Headless
             });
         }
 
-        public IRef<IWriteableBitmapImpl>? GetLastRenderedFrame()
+        public Bitmap? GetLastRenderedFrame()
         {
             lock (_sync)
-                return _lastRenderedFrame?.PlatformImpl?.CloneAs<IWriteableBitmapImpl>();
+            {
+                if (_lastRenderedFrame is null)
+                {
+                    return null;
+                }
+
+                using var lockedFramebuffer = _lastRenderedFrame.Lock();
+                return new Bitmap(lockedFramebuffer.Format, AlphaFormat.Opaque, lockedFramebuffer.Address,
+                    lockedFramebuffer.Size, lockedFramebuffer.Dpi, lockedFramebuffer.RowBytes);
+            }
         }
 
         private ulong Timestamp => (ulong)_st.ElapsedMilliseconds;

+ 1 - 1
src/Headless/Avalonia.Headless/IHeadlessWindow.cs

@@ -8,7 +8,7 @@ namespace Avalonia.Headless
 {
     public interface IHeadlessWindow
     {
-        IRef<IWriteableBitmapImpl>? GetLastRenderedFrame();
+        Bitmap? GetLastRenderedFrame();
         void KeyPress(Key key, RawInputModifiers modifiers);
         void KeyRelease(Key key, RawInputModifiers modifiers);
         void MouseDown(Point point, int button, RawInputModifiers modifiers = RawInputModifiers.None);

+ 19 - 0
tests/Avalonia.Headless.UnitTests/Avalonia.Headless.UnitTests.csproj

@@ -0,0 +1,19 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>net6.0</TargetFramework>
+    <IsTestProject>true</IsTestProject>
+  </PropertyGroup>
+  <Import Project="..\..\build\UnitTests.NetCore.targets" />
+  <Import Project="..\..\build\UnitTests.NetFX.props" />
+  <Import Project="..\..\build\Moq.props" />
+  <Import Project="..\..\build\XUnit.props" />
+  <Import Project="..\..\build\Rx.props" />
+  <Import Project="..\..\build\SharedVersion.props" />
+
+  <ItemGroup>
+    <ProjectReference Include="..\..\src\Avalonia.Themes.Simple\Avalonia.Themes.Simple.csproj" />
+    <ProjectReference Include="..\..\src\Headless\Avalonia.Headless.XUnit\Avalonia.Headless.XUnit.csproj" />
+    <ProjectReference Include="..\..\src\Skia\Avalonia.Skia\Avalonia.Skia.csproj" />
+  </ItemGroup>
+</Project>

+ 37 - 0
tests/Avalonia.Headless.UnitTests/InputTests.cs

@@ -0,0 +1,37 @@
+using Avalonia.Controls;
+using Avalonia.Layout;
+using Avalonia.Threading;
+using Xunit;
+
+namespace Avalonia.Headless.XUnit.Tests;
+
+public class InputTests
+{
+    [Fact]
+    public void Should_Click_Button_On_Window()
+    {
+        var buttonClicked = false;
+        var button = new Button
+        {
+            HorizontalAlignment = HorizontalAlignment.Stretch,
+            VerticalAlignment = VerticalAlignment.Stretch
+        };
+
+        button.Click += (_, _) => buttonClicked = true;
+
+        var window = new Window
+        {
+            Width = 100,
+            Height = 100,
+            Content = button
+        };
+        window.Show();
+
+        Dispatcher.UIThread.RunJobs();
+
+        ((IHeadlessWindow)window.PlatformImpl!).MouseDown(new Point(50, 50), 0);
+        ((IHeadlessWindow)window.PlatformImpl!).MouseUp(new Point(50, 50), 0);
+        
+        Assert.True(buttonClicked);
+    }
+}

+ 39 - 0
tests/Avalonia.Headless.UnitTests/RenderingTests.cs

@@ -0,0 +1,39 @@
+using System.IO;
+using System.Linq;
+using Avalonia.Controls;
+using Avalonia.Layout;
+using Avalonia.Media;
+using Avalonia.Media.Imaging;
+using Avalonia.Threading;
+using Xunit;
+
+namespace Avalonia.Headless.XUnit.Tests;
+
+public class RenderingTests
+{
+    [Fact]
+    public void Should_Render_Last_Frame_To_Bitmap()
+    {
+        var window = new Window
+        {
+            Content = new ContentControl
+            {
+                HorizontalAlignment = HorizontalAlignment.Stretch,
+                VerticalAlignment = VerticalAlignment.Stretch,
+                Padding = new Thickness(4),
+                Content = new PathIcon
+                {
+                    Data = StreamGeometry.Parse("M0,9 L10,0 20,9 19,10 10,2 1,10 z")
+                }
+            },
+            SizeToContent = SizeToContent.WidthAndHeight
+        };
+        window.Show();
+
+        Dispatcher.UIThread.RunJobs();
+        AvaloniaHeadlessPlatform.ForceRenderTimerTick();
+
+        var frame = ((IHeadlessWindow)window.PlatformImpl!).GetLastRenderedFrame();
+        Assert.NotNull(frame);
+    }
+}

+ 24 - 0
tests/Avalonia.Headless.UnitTests/TestApplication.cs

@@ -0,0 +1,24 @@
+using Avalonia.Headless.XUnit;
+using Avalonia.Headless.XUnit.Tests;
+using Avalonia.Themes.Simple;
+using Xunit;
+
+[assembly: AvaloniaTestFramework(typeof(TestApplication))]
+[assembly: CollectionBehavior(DisableTestParallelization = true)]
+
+namespace Avalonia.Headless.XUnit.Tests;
+
+public class TestApplication : Application
+{
+    public TestApplication()
+    {
+        Styles.Add(new SimpleTheme());
+    }
+
+    public static AppBuilder BuildAvaloniaApp() => AppBuilder.Configure<TestApplication>()
+        .UseSkia()
+        .UseHeadless(new AvaloniaHeadlessPlatformOptions
+        {
+            UseHeadlessDrawing = false
+        });
+}

+ 32 - 0
tests/Avalonia.Headless.UnitTests/ThreadingTests.cs

@@ -0,0 +1,32 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Avalonia.Threading;
+using Xunit;
+
+namespace Avalonia.Headless.XUnit.Tests;
+
+public class ThreadingTests
+{
+    [Fact]
+    public void Should_Be_On_Dispatcher_Thread()
+    {
+        Dispatcher.UIThread.VerifyAccess();
+    }
+    
+    [Fact]
+    public async Task DispatcherTimer_Works_On_The_Same_Thread()
+    {
+        var currentThread = Thread.CurrentThread;
+        var tcs = new TaskCompletionSource();
+
+        DispatcherTimer.RunOnce(() =>
+        {
+            Assert.Equal(currentThread, Thread.CurrentThread);
+
+            tcs.SetResult();
+        }, TimeSpan.FromTicks(1));
+
+        await tcs.Task;
+    }
+}