Ver código fonte

Render Tests wip

Krzysztof Krysiński 4 meses atrás
pai
commit
bf3df3a79e

+ 12 - 0
src/PixiEditor.Platform/IPlatform.cs

@@ -19,3 +19,15 @@ public interface IPlatform
         Current = platform;
     }
 }
+
+public class NullAdditionalContentProvider : IAdditionalContentProvider
+{
+    public bool IsContentInstalled(AdditionalContentProduct product) => false;
+    public bool PlatformHasContent(AdditionalContentProduct product)
+    {
+        return false;
+    }
+
+    public void InstallContent(AdditionalContentProduct product) { }
+    public void UninstallContent(AdditionalContentProduct product) { }
+}

+ 0 - 2
src/PixiEditor/Helpers/Extensions/ApplicationExtensions.cs

@@ -14,8 +14,6 @@ public static class ApplicationExtensions
             case IClassicDesktopStyleApplicationLifetime desktop:
                 action(desktop.MainWindow);
                 break;
-            default:
-                throw new NotSupportedException("ApplicationLifetime is not supported");
         }
     }
 

+ 2 - 0
src/PixiEditor/Properties/AssemblyInfo.cs

@@ -1,4 +1,5 @@
 using System.Reflection;
+using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 
 // General Information about an assembly is controlled through the following
@@ -12,6 +13,7 @@ using System.Runtime.InteropServices;
 [assembly: AssemblyCopyright("Copyright PixiEditor © 2017 - 2025")]
 [assembly: AssemblyTrademark("")]
 [assembly: AssemblyCulture("")]
+[assembly: InternalsVisibleTo("PixiEditor.Tests")]
 
 // Setting ComVisible to false makes the types in this assembly not visible
 // to COM components.  If you need to access a type in this assembly from

+ 1 - 0
src/PixiEditor/ViewModels/Document/DocumentViewModel.cs

@@ -4,6 +4,7 @@ using System.Collections.Immutable;
 using System.Collections.ObjectModel;
 using System.IO;
 using System.Linq;
+using System.Runtime.CompilerServices;
 using System.Text.Json;
 using Avalonia;
 using Avalonia.Media.Imaging;

+ 7 - 0
src/PixiEditor/ViewModels/Menu/MenuBarViewModel.cs

@@ -4,6 +4,7 @@ using System.Linq;
 using System.Windows.Input;
 using Avalonia;
 using Avalonia.Controls;
+using Avalonia.Controls.ApplicationLifetimes;
 using Avalonia.Data;
 using Avalonia.Threading;
 using Microsoft.Extensions.DependencyInjection;
@@ -14,6 +15,7 @@ using PixiEditor.Models.Commands;
 using PixiEditor.OperatingSystem;
 using PixiEditor.ViewModels.SubViewModels;
 using PixiEditor.ViewModels.SubViewModels.AdditionalContent;
+using PixiEditor.Views;
 using Command = PixiEditor.Models.Commands.Commands.Command;
 using Commands_Command = PixiEditor.Models.Commands.Commands.Command;
 using NativeMenu = Avalonia.Controls.NativeMenu;
@@ -92,6 +94,11 @@ internal class MenuBarViewModel : PixiObservableObject
 
     private void BuildMenu(CommandController controller, MenuItemBuilder[] builders)
     {
+        if (Application.Current?.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime)
+        {
+            return;
+        }
+
         if (IOperatingSystem.Current.IsMacOs)
         {
             BuildBasicNativeMenuItems(controller, menuItems);

+ 1 - 0
tests/ChunkyImageLibTest/ChunkyImageLibTest.csproj

@@ -22,6 +22,7 @@
   <ItemGroup>
     <ProjectReference Include="..\..\src\ChunkyImageLib\ChunkyImageLib.csproj" />
     <ProjectReference Include="..\..\src\Drawie\src\Drawie.Backend.Skia\Drawie.Backend.Skia.csproj" />
+    <ProjectReference Include="..\PixiEditor.Tests\PixiEditor.Tests.csproj" />
   </ItemGroup>
 
 </Project>

+ 2 - 12
tests/ChunkyImageLibTest/ChunkyImageTests.cs

@@ -6,23 +6,13 @@ using Drawie.Backend.Core.Surfaces;
 using Drawie.Backend.Core.Surfaces.ImageData;
 using Drawie.Numerics;
 using Drawie.Skia;
+using PixiEditor.Tests;
 using Xunit;
 
 namespace ChunkyImageLibTest;
 
-public class ChunkyImageTests
+public class ChunkyImageTests : PixiEditorTest
 {
-    public ChunkyImageTests()
-    {
-        try
-        {
-            DrawingBackendApi.SetupBackend(new SkiaDrawingBackend(), null);
-        }
-        catch
-        {
-        }
-    }
-
     [Fact]
     public void Dispose_ComplexImage_ReturnsAllChunks()
     {

+ 2 - 9
tests/ChunkyImageLibTest/ImageOperationTests.cs

@@ -5,19 +5,12 @@ using Drawie.Backend.Core;
 using Drawie.Backend.Core.Bridge;
 using Drawie.Numerics;
 using Drawie.Skia;
+using PixiEditor.Tests;
 using Xunit;
 
 namespace ChunkyImageLibTest;
-public class ImageOperationTests
+public class ImageOperationTests : PixiEditorTest
 {
-    public ImageOperationTests()
-    {
-        try
-        {
-            DrawingBackendApi.SetupBackend(new SkiaDrawingBackend(), null);
-        }
-        catch { }
-    }
 
     [Fact]
     public void FindAffectedChunks_SingleChunk_ReturnsSingleChunk()

+ 3 - 11
tests/ChunkyImageLibTest/RectangleOperationTests.cs

@@ -5,23 +5,15 @@ using Drawie.Backend.Core.Bridge;
 using Drawie.Backend.Core.ColorsImpl;
 using Drawie.Numerics;
 using Drawie.Skia;
+using PixiEditor.Tests;
 using Xunit;
 
 namespace ChunkyImageLibTest;
 
-public class RectangleOperationTests
+public class RectangleOperationTests : PixiEditorTest
 {
     const int chunkSize = ChunkPool.FullChunkSize;
-    public RectangleOperationTests()
-    {
-        try
-        {
-            DrawingBackendApi.SetupBackend(new SkiaDrawingBackend(), null);
-        }
-        catch
-        {
-        }
-    }
+
 // to keep expected rectangles aligned
 #pragma warning disable format
     [Fact]

+ 11 - 0
tests/ChunkyImageLibTest/TestRunnerSetup.cs

@@ -0,0 +1,11 @@
+using Xunit;
+
+[assembly:
+    CollectionBehavior(CollectionBehavior.CollectionPerAssembly, DisableTestParallelization = false,
+        MaxParallelThreads = 1)]
+
+namespace ChunkyImageLibTest;
+
+public class TestRunnerSetup
+{
+}

+ 2 - 3
tests/PixiEditor.Backend.Tests/NodeSystemTests.cs

@@ -12,11 +12,12 @@ using PixiEditor.ChangeableDocument.Changes.NodeGraph;
 using PixiEditor.Models.Serialization;
 using PixiEditor.Models.Serialization.Factories;
 using PixiEditor.Parser.Skia.Encoders;
+using PixiEditor.Tests;
 using Xunit.Abstractions;
 
 namespace PixiEditor.Backend.Tests;
 
-public class NodeSystemTests
+public class NodeSystemTests : PixiEditorTest
 {
     private readonly ITestOutputHelper output;
 
@@ -29,8 +30,6 @@ public class NodeSystemTests
     public NodeSystemTests(ITestOutputHelper output)
     {
         this.output = output;
-        if (!DrawingBackendApi.HasBackend)
-            DrawingBackendApi.SetupBackend(new SkiaDrawingBackend(), new AvaloniaRenderingDispatcher());
     }
 
     [Fact]

+ 1 - 0
tests/PixiEditor.Backend.Tests/PixiEditor.Backend.Tests.csproj

@@ -29,6 +29,7 @@
     <ItemGroup>
       <ProjectReference Include="..\..\src\PixiEditor.ChangeableDocument\PixiEditor.ChangeableDocument.csproj" />
       <ProjectReference Include="..\..\src\PixiEditor\PixiEditor.csproj" />
+      <ProjectReference Include="..\PixiEditor.Tests\PixiEditor.Tests.csproj" />
     </ItemGroup>
 
 </Project>

+ 6 - 74
tests/PixiEditor.Tests/AvaloniaTestRunner.cs

@@ -1,83 +1,15 @@
-using System.Reflection;
+using Avalonia;
 using Avalonia.Headless;
-using Avalonia.Platform;
-using Avalonia.Threading;
-using Drawie.Backend.Core.Bridge;
-using Drawie.Skia;
-using DrawiEngine;
-using PixiEditor.Desktop;
-using Xunit.Abstractions;
-using Xunit.Sdk;
+using PixiEditor.Tests;
 
 [assembly:TestFramework("PixiEditor.Tests.AvaloniaTestRunner", "PixiEditor.Tests")]
 [assembly:CollectionBehavior(CollectionBehavior.CollectionPerAssembly, DisableTestParallelization = false, MaxParallelThreads = 1)]
+[assembly: AvaloniaTestApplication(typeof(AvaloniaTestRunner))]
 namespace PixiEditor.Tests
 {
-    public class AvaloniaTestRunner : XunitTestFramework
+    public class AvaloniaTestRunner
     {
-        public AvaloniaTestRunner(IMessageSink messageSink) : base(messageSink)
-        {
-        }
-
-        protected override ITestFrameworkExecutor CreateExecutor(AssemblyName assemblyName)
-            => new Executor(assemblyName, SourceInformationProvider, DiagnosticMessageSink);
-
-
-        class Executor : XunitTestFrameworkExecutor
-        {
-            public Executor(AssemblyName assemblyName, ISourceInformationProvider sourceInformationProvider,
-                IMessageSink diagnosticMessageSink) : base(assemblyName, sourceInformationProvider,
-                diagnosticMessageSink)
-            {
-            }
-
-            protected override async void RunTestCases(IEnumerable<IXunitTestCase> testCases,
-                IMessageSink executionMessageSink,
-                ITestFrameworkExecutionOptions executionOptions)
-            {
-                executionOptions.SetValue("xunit.execution.DisableParallelization", false);
-                using (var assemblyRunner = new Runner(
-                    TestAssembly, testCases, DiagnosticMessageSink, executionMessageSink,
-                    executionOptions)) await assemblyRunner.RunAsync();
-            }
-        }
-
-        class Runner : XunitTestAssemblyRunner
-        {
-            public Runner(ITestAssembly testAssembly, IEnumerable<IXunitTestCase> testCases,
-                IMessageSink diagnosticMessageSink, IMessageSink executionMessageSink,
-                ITestFrameworkExecutionOptions executionOptions) : base(testAssembly, testCases, diagnosticMessageSink,
-                executionMessageSink, executionOptions)
-            {
-            }
-
-
-            protected override void SetupSyncContext(int maxParallelThreads)
-            {
-                var tcs = new TaskCompletionSource<SynchronizationContext>();
-                new Thread(() =>
-                {
-                    try
-                    {
-                        Program.BuildAvaloniaApp()
-                            .UseHeadless(new AvaloniaHeadlessPlatformOptions { FrameBufferFormat = PixelFormat.Bgra8888, UseHeadlessDrawing = false })
-                            .SetupWithoutStarting();
-                        tcs.SetResult(SynchronizationContext.Current);
-                    }
-                    catch (Exception e)
-                    {
-                        tcs.SetException(e);
-                    }
-                    Dispatcher.UIThread.MainLoop(CancellationToken.None);
-                })
-                {
-                    IsBackground = true
-                }.Start();
-
-                SynchronizationContext.SetSynchronizationContext(tcs.Task.Result);
-            }
-
-
-        }
+        public static AppBuilder BuildAvaloniaApp() => AppBuilder.Configure<App>()
+            .UseHeadless(new AvaloniaHeadlessPlatformOptions());
     }
 }

+ 7 - 0
tests/PixiEditor.Tests/PixiEditor.Tests.csproj

@@ -12,6 +12,7 @@
 
     <ItemGroup>
         <PackageReference Include="Avalonia" Version="$(AvaloniaVersion)" />
+        <PackageReference Include="Avalonia.Headless.XUnit" Version="$(AvaloniaVersion)" />
         <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
         <PackageReference Include="xunit" Version="2.9.2"/>
         <PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
@@ -27,6 +28,12 @@
 
     <ItemGroup>
       <ProjectReference Include="..\..\src\PixiEditor.Desktop\PixiEditor.Desktop.csproj" />
+      <ProjectReference Include="..\..\src\PixiEditor.Linux\PixiEditor.Linux.csproj" />
+      <ProjectReference Include="..\..\src\PixiEditor.MacOs\PixiEditor.MacOs.csproj" />
+    </ItemGroup>
+    
+    <ItemGroup>
+        <Content Include="TestFiles\**" CopyToOutputDirectory="PreserveNewest" />
     </ItemGroup>
 
 </Project>

+ 65 - 0
tests/PixiEditor.Tests/PixiEditorTest.cs

@@ -1,6 +1,15 @@
 using Drawie.Backend.Core.Bridge;
 using Drawie.Skia;
 using DrawiEngine;
+using Microsoft.Extensions.DependencyInjection;
+using PixiEditor.Extensions.Runtime;
+using PixiEditor.Helpers;
+using PixiEditor.Linux;
+using PixiEditor.MacOs;
+using PixiEditor.OperatingSystem;
+using PixiEditor.Platform;
+using PixiEditor.ViewModels;
+using PixiEditor.Windows;
 
 namespace PixiEditor.Tests;
 
@@ -16,4 +25,60 @@ public class PixiEditorTest
         SkiaDrawingBackend skiaDrawingBackend = new SkiaDrawingBackend();
         DrawingBackendApi.SetupBackend(skiaDrawingBackend, new DrawieRenderingDispatcher());
     }
+}
+
+public class FullPixiEditorTest : PixiEditorTest
+{
+    public FullPixiEditorTest()
+    {
+        ExtensionLoader loader = new ExtensionLoader("TestExtensions", "TestExtensions/Unpacked");
+
+        IOperatingSystem os;
+        if (System.OperatingSystem.IsWindows())
+        {
+            os = new WindowsOperatingSystem();
+        }
+        else if (System.OperatingSystem.IsLinux())
+        {
+            os = new LinuxOperatingSystem();
+        }
+        else if (System.OperatingSystem.IsMacOS())
+        {
+            os = new MacOperatingSystem();
+        }
+        else
+        {
+            throw new NotSupportedException("Unsupported operating system");
+        }
+
+        IOperatingSystem.RegisterOS(os);
+        IPlatform.RegisterPlatform(new TestPlatform());
+
+        var services = new ServiceCollection()
+            .AddPlatform()
+            .AddPixiEditor(loader)
+            .AddExtensionServices(loader)
+            .BuildServiceProvider();
+
+
+        var vm = services.GetRequiredService<ViewModelMain>();
+        vm.Setup(services);
+    }
+
+    class TestPlatform : IPlatform
+    {
+        public string Id { get; } = "TestPlatform";
+        public string Name { get; } = "Tests";
+
+        public bool PerformHandshake()
+        {
+            return true;
+        }
+
+        public void Update()
+        {
+        }
+
+        public IAdditionalContentProvider? AdditionalContentProvider { get; } = new NullAdditionalContentProvider();
+    }
 }

+ 74 - 0
tests/PixiEditor.Tests/RenderTests.cs

@@ -0,0 +1,74 @@
+using Avalonia.Headless.XUnit;
+using Drawie.Backend.Core;
+using Drawie.Backend.Core.ColorsImpl;
+using Drawie.Backend.Core.Surfaces;
+using PixiEditor.ChangeableDocument.Changeables.Animations;
+using PixiEditor.Models.IO;
+using PixiEditor.ViewModels.Document;
+
+namespace PixiEditor.Tests;
+
+public class RenderTests : FullPixiEditorTest
+{
+    [AvaloniaFact]
+    public void TestThatPixiFilesRenderTheSameResultAsSavedPng()
+    {
+        string[] files = Directory.GetFiles("TestFiles/RenderTests", "*.pixi");
+        string[] results = Directory.GetFiles("TestFiles/RenderTests", "*.png");
+
+        Assert.Equal(files.Length, results.Length);
+
+        for (int i = 0; i < files.Length; i++)
+        {
+            string pixiFile = files[i];
+            var document = Importer.ImportDocument(pixiFile);
+            var pngFile = results.FirstOrDefault(x => x.EndsWith(Path.GetFileNameWithoutExtension(pixiFile) + ".png"));
+
+            Assert.NotNull(pngFile);
+
+            var result = document.TryRenderWholeImage(0);
+
+            Assert.True(result is { IsT1: true, AsT1: not null }); // Check if rendering was successful
+
+            using var image = result.AsT1;
+
+            using var toCompareTo = Importer.ImportImage(results[i], document.SizeBindable);
+
+            Assert.NotNull(toCompareTo);
+
+            Assert.True(PixelCompare(image, toCompareTo));
+        }
+    }
+
+    private static bool PixelCompare(Surface image, Surface compareTo)
+    {
+        if (image.Size != compareTo.Size)
+        {
+            return false;
+        }
+
+        Pixmap imagePixmap = image.PeekPixels();
+        Pixmap compareToPixmap = compareTo.PeekPixels();
+
+        if (imagePixmap == null || compareToPixmap == null)
+        {
+            return false;
+        }
+
+        for (int y = 0; y < image.Size.Y; y++)
+        {
+            for (int x = 0; x < image.Size.X; x++)
+            {
+                Color color1 = imagePixmap.GetPixelColor(x, y);
+                Color color2 = compareToPixmap.GetPixelColor(x, y);
+
+                if (color1 != color2)
+                {
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+}

BIN
tests/PixiEditor.Tests/TestFiles/RenderTests/Pond.pixi


BIN
tests/PixiEditor.Tests/TestFiles/RenderTests/Pond.png


+ 14 - 0
tests/PixiEditorTests.sln

@@ -117,6 +117,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ColorPicker.AvaloniaUI", ".
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ColorPicker.Models", "..\src\ColorPicker\src\ColorPicker.Models\ColorPicker.Models.csproj", "{B4A2F5BC-A07D-4C99-B022-B959B54CC4A0}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixiEditor.MacOs", "..\src\PixiEditor.MacOs\PixiEditor.MacOs.csproj", "{554F618D-DDD1-484B-AE89-540852FF7D4F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixiEditor.Linux", "..\src\PixiEditor.Linux\PixiEditor.Linux.csproj", "{C7D2CAEE-2DF4-4920-AE9F-E3323E80AAF4}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -334,6 +338,14 @@ Global
 		{B4A2F5BC-A07D-4C99-B022-B959B54CC4A0}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{B4A2F5BC-A07D-4C99-B022-B959B54CC4A0}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{B4A2F5BC-A07D-4C99-B022-B959B54CC4A0}.Release|Any CPU.Build.0 = Release|Any CPU
+		{554F618D-DDD1-484B-AE89-540852FF7D4F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{554F618D-DDD1-484B-AE89-540852FF7D4F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{554F618D-DDD1-484B-AE89-540852FF7D4F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{554F618D-DDD1-484B-AE89-540852FF7D4F}.Release|Any CPU.Build.0 = Release|Any CPU
+		{C7D2CAEE-2DF4-4920-AE9F-E3323E80AAF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{C7D2CAEE-2DF4-4920-AE9F-E3323E80AAF4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{C7D2CAEE-2DF4-4920-AE9F-E3323E80AAF4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{C7D2CAEE-2DF4-4920-AE9F-E3323E80AAF4}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(NestedProjects) = preSolution
 		{0EF3CAB9-7361-472C-8789-D17D4EA2DEBB} = {D914C08C-5F1A-4E13-AAA6-F25E8C9748E2}
@@ -390,5 +402,7 @@ Global
 		{924CA5E4-F579-435F-B39A-11802F9B1390} = {E118E6FE-67E7-4472-A8D7-E7F470E66131}
 		{1B9B2155-9D9C-4259-8015-4F06F944812C} = {E118E6FE-67E7-4472-A8D7-E7F470E66131}
 		{B4A2F5BC-A07D-4C99-B022-B959B54CC4A0} = {E118E6FE-67E7-4472-A8D7-E7F470E66131}
+		{554F618D-DDD1-484B-AE89-540852FF7D4F} = {E118E6FE-67E7-4472-A8D7-E7F470E66131}
+		{C7D2CAEE-2DF4-4920-AE9F-E3323E80AAF4} = {E118E6FE-67E7-4472-A8D7-E7F470E66131}
 	EndGlobalSection
 EndGlobal