diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 4cb40d36a0..b4b966a02d 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -82,7 +82,7 @@ jobs: shell: pwsh run: ./ci-test.ps1 "${{matrix.options.os}}" "${{matrix.options.framework}}" "${{matrix.options.runtime}}" "${{matrix.options.codecov}}" env: - CI : True + CI: True XUNIT_PATH: .\tests\ImageSharp.Tests # Required for xunit - name: Update Codecov @@ -90,16 +90,56 @@ jobs: if: matrix.options.codecov == true with: token: ${{secrets.CODECOV_TOKEN}} - file: "coverage.${{matrix.options.framework}}.xml" flags: unittests - - name: Pack # We can use this filter as we know it happens only once and takes the most time to complete. - if: (github.event_name == 'push') && (matrix.options.codecov == true) + Publish: + needs: [Build] + + runs-on: windows-latest + + if: (github.event_name == 'push') + + steps: + - uses: actions/checkout@v2 + + - name: Install NuGet + uses: NuGet/setup-nuget@v1 + + - name: Setup Git + shell: bash + run: | + git config --global core.autocrlf false + git config --global core.longpaths true + git fetch --prune --unshallow + git submodule -q update --init --recursive + + - name: Fetch Tags for GitVersion + run: | + git fetch --tags + + - name: Fetch master for GitVersion + if: github.ref != 'refs/heads/master' + run: git branch --create-reflog master origin/master + + - name: Install GitVersion + uses: gittools/actions/setup-gitversion@v0.3 + with: + versionSpec: "5.1.x" + + - name: Use GitVersion + id: gitversion # step id used as reference for output values + uses: gittools/actions/execute-gitversion@v0.3 + + - name: Setup DotNet SDK + uses: actions/setup-dotnet@v1 + with: + dotnet-version: "3.1.101" + + - name: Pack shell: pwsh - run: ./ci-build.ps1 "${{steps.gitversion.outputs.nuGetVersion}}" + run: ./ci-pack.ps1 "${{steps.gitversion.outputs.nuGetVersion}}" - name: Publish to MyGet - if: (github.event_name == 'push') && (matrix.options.codecov == true) shell: pwsh run: nuget.exe push .\artifacts\*.nupkg ${{secrets.MYGET_TOKEN}} -Source https://www.myget.org/F/sixlabors/api/v2/package # TODO: If github.ref starts with 'refs/tags' then it was tag push and we can optionally push out package to nuget.org diff --git a/.gitignore b/.gitignore index 8fcb5ef405..a89cfcf104 100644 --- a/.gitignore +++ b/.gitignore @@ -216,7 +216,7 @@ artifacts/ *.csproj.bak #CodeCoverage -/ImageSharp.Coverage.xml +*.lcov # Tests **/Images/ActualOutput diff --git a/Directory.Build.props b/Directory.Build.props index 346da14be8..604153f976 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -91,8 +91,9 @@ git https://www.myget.org/F/sixlabors/api/v3/index.json; - https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json; https://api.nuget.org/v3/index.json; + + https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json; 002400000c8000009400000006020000002400005253413100040000010001000147e6fe6766715eec6cfed61f1e7dcdbf69748a3e355c67e9d8dfd953acab1d5e012ba34b23308166fdc61ee1d0390d5f36d814a6091dd4b5ed9eda5a26afced924c683b4bfb4b3d64b0586a57eff9f02b1f84e3cb0ddd518bd1697f2c84dcbb97eb8bb5c7801be12112ed0ec86db934b0e9a5171e6bb1384b6d2f7d54dfa97 true diff --git a/ImageSharp.sln b/ImageSharp.sln index 875ede1b2d..40878c5751 100644 --- a/ImageSharp.sln +++ b/ImageSharp.sln @@ -10,6 +10,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .gitignore = .gitignore .gitmodules = .gitmodules ci-build.ps1 = ci-build.ps1 + ci-pack.ps1 = ci-pack.ps1 ci-test.ps1 = ci-test.ps1 Directory.Build.props = Directory.Build.props Directory.Build.targets = Directory.Build.targets @@ -322,8 +323,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Tests", "tests\I EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Benchmarks", "tests\ImageSharp.Benchmarks\ImageSharp.Benchmarks.csproj", "{2BF743D8-2A06-412D-96D7-F448F00C5EA5}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Sandbox46", "tests\ImageSharp.Sandbox46\ImageSharp.Sandbox46.csproj", "{561B880A-D9EE-44EF-90F5-817C54A9D9AB}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{C0D7754B-5277-438E-ABEB-2BA34401B5A7}" ProjectSection(SolutionItems) = preProject .github\workflows\build-and-test.yml = .github\workflows\build-and-test.yml @@ -331,6 +330,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{ EndProject Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "SharedInfrastructure", "shared-infrastructure\src\SharedInfrastructure\SharedInfrastructure.shproj", "{68A8CC40-6AED-4E96-B524-31B1158FDEEA}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Tests.ProfilingSandbox", "tests\ImageSharp.Tests.ProfilingSandbox\ImageSharp.Tests.ProfilingSandbox.csproj", "{FC527290-2F22-432C-B77B-6E815726B02C}" +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution shared-infrastructure\src\SharedInfrastructure\SharedInfrastructure.projitems*{68a8cc40-6aed-4e96-b524-31b1158fdeea}*SharedItemsImports = 13 @@ -380,18 +381,18 @@ Global {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release|x64.Build.0 = Release|Any CPU {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release|x86.ActiveCfg = Release|Any CPU {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release|x86.Build.0 = Release|Any CPU - {561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Debug|x64.ActiveCfg = Debug|Any CPU - {561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Debug|x64.Build.0 = Debug|Any CPU - {561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Debug|x86.ActiveCfg = Debug|Any CPU - {561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Debug|x86.Build.0 = Debug|Any CPU - {561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Release|Any CPU.Build.0 = Release|Any CPU - {561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Release|x64.ActiveCfg = Release|Any CPU - {561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Release|x64.Build.0 = Release|Any CPU - {561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Release|x86.ActiveCfg = Release|Any CPU - {561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Release|x86.Build.0 = Release|Any CPU + {FC527290-2F22-432C-B77B-6E815726B02C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FC527290-2F22-432C-B77B-6E815726B02C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FC527290-2F22-432C-B77B-6E815726B02C}.Debug|x64.ActiveCfg = Debug|Any CPU + {FC527290-2F22-432C-B77B-6E815726B02C}.Debug|x64.Build.0 = Debug|Any CPU + {FC527290-2F22-432C-B77B-6E815726B02C}.Debug|x86.ActiveCfg = Debug|Any CPU + {FC527290-2F22-432C-B77B-6E815726B02C}.Debug|x86.Build.0 = Debug|Any CPU + {FC527290-2F22-432C-B77B-6E815726B02C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FC527290-2F22-432C-B77B-6E815726B02C}.Release|Any CPU.Build.0 = Release|Any CPU + {FC527290-2F22-432C-B77B-6E815726B02C}.Release|x64.ActiveCfg = Release|Any CPU + {FC527290-2F22-432C-B77B-6E815726B02C}.Release|x64.Build.0 = Release|Any CPU + {FC527290-2F22-432C-B77B-6E815726B02C}.Release|x86.ActiveCfg = Release|Any CPU + {FC527290-2F22-432C-B77B-6E815726B02C}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -415,9 +416,9 @@ Global {E1C42A6F-913B-4A7B-B1A8-2BB62843B254} = {9DA226A1-8656-49A8-A58A-A8B5C081AD66} {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} {2BF743D8-2A06-412D-96D7-F448F00C5EA5} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} - {561B880A-D9EE-44EF-90F5-817C54A9D9AB} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} {C0D7754B-5277-438E-ABEB-2BA34401B5A7} = {1799C43E-5C54-4A8F-8D64-B1475241DB0D} {68A8CC40-6AED-4E96-B524-31B1158FDEEA} = {815C0625-CD3D-440F-9F80-2D83856AB7AE} + {FC527290-2F22-432C-B77B-6E815726B02C} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {5F8B9D1F-CD8B-4CC5-8216-D531E25BD795} diff --git a/README.md b/README.md index ceb1e51d22..af8d4f73ae 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ SixLabors.ImageSharp
+[![Build Status](https://img.shields.io/github/workflow/status/SixLabors/ImageSharp/Build/master)](https://github.com/SixLabors/ImageSharp/actions) [![GitHub license](https://img.shields.io/badge/license-Apache%202-blue.svg)](https://raw.githubusercontent.com/SixLabors/ImageSharp/master/LICENSE) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ImageSharp/General?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Twitter](https://img.shields.io/twitter/url/http/shields.io.svg?style=flat&logo=twitter)](https://twitter.com/intent/tweet?hashtags=imagesharp,dotnet,oss&text=ImageSharp.+A+new+cross-platform+2D+graphics+API+in+C%23&url=https%3a%2f%2fgithub.com%2fSixLabors%2fImageSharp&via=sixlabors) @@ -45,12 +46,13 @@ The **ImageSharp** library is made up of multiple packages: - Transform methods like Resize, Crop, Skew, Rotate - anything that alters the dimensions of the image - Non-transform methods like Gaussian Blur, Pixelate, Edge Detection - anything that maintains the original image dimensions + ### Questions? - Do you have questions? We are happy to help! Please [join our gitter channel](https://gitter.im/ImageSharp/General), or ask them on [stackoverflow](https://stackoverflow.com) using the `ImageSharp` tag. **Do not** open issues for questions! diff --git a/ci-build.ps1 b/ci-build.ps1 index ad757dc9e2..17c6e6603b 100644 --- a/ci-build.ps1 +++ b/ci-build.ps1 @@ -1,20 +1,13 @@ param( [Parameter(Mandatory, Position = 0)] [string]$version, - [Parameter(Mandatory = $false, Position = 1)] - [string]$targetFramework = 'ALL' + [Parameter(Mandatory = $true, Position = 1)] + [string]$targetFramework ) dotnet clean -c Release $repositoryUrl = "https://github.com/$env:GITHUB_REPOSITORY" -if ($targetFramework -ne 'ALL') { - # Building for a specific framework. - dotnet build -c Release -f $targetFramework /p:packageversion=$version /p:RepositoryUrl=$repositoryUrl -} -else { - - # Building for packing and publishing. - dotnet pack -c Release --output "$PSScriptRoot/artifacts" /p:packageversion=$version /p:RepositoryUrl=$repositoryUrl -} +# Building for a specific framework. +dotnet build -c Release -f $targetFramework /p:packageversion=$version /p:RepositoryUrl=$repositoryUrl diff --git a/ci-pack.ps1 b/ci-pack.ps1 new file mode 100644 index 0000000000..a4e846db95 --- /dev/null +++ b/ci-pack.ps1 @@ -0,0 +1,11 @@ +param( + [Parameter(Mandatory, Position = 0)] + [string]$version +) + +dotnet clean -c Release + +$repositoryUrl = "https://github.com/$env:GITHUB_REPOSITORY" + +# Building for packing and publishing. +dotnet pack -c Release --output "$PSScriptRoot/artifacts" /p:packageversion=$version /p:RepositoryUrl=$repositoryUrl diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props index 3d8286971e..64f79e3248 100644 --- a/tests/Directory.Build.props +++ b/tests/Directory.Build.props @@ -24,7 +24,7 @@ - + diff --git a/tests/Directory.Build.targets b/tests/Directory.Build.targets index 9ee9c226d3..137a7a0305 100644 --- a/tests/Directory.Build.targets +++ b/tests/Directory.Build.targets @@ -17,7 +17,7 @@ - + @@ -28,10 +28,12 @@ true true - opencover + [SixLabors.*]* + lcov - $(MSBuildThisFileDirectory)..\coverage.xml - + $(MSBuildThisFileDirectory)..\ + $(CoverletOutputPath)$(AssemblyName).$(TargetFramework).lcov + true @@ -43,13 +45,13 @@ + - - - + + diff --git a/tests/ImageSharp.Sandbox46/README.md b/tests/ImageSharp.Sandbox46/README.md deleted file mode 100644 index b05afb8538..0000000000 --- a/tests/ImageSharp.Sandbox46/README.md +++ /dev/null @@ -1,24 +0,0 @@ -## Purpose -This project aims to workaround certain .NET Core tooling issues in Visual Studio based developer workflow at the time of it's creation (January 2017): -- .NET Core Performance profiling is not possible neither with Visual Studio nor with JetBrains profilers -- ~~JetBrains Unit Test explorer does not work with .NET Core projects~~ - -## How does it work? -- By referencing .NET 4.5 dll-s created by net45 target's of ImageSharp projects. NOTE: These are not project references! -- By including test classes (and utility classes) of the `ImageSharp.Tests` project using MSBUILD `` -- Compiling `ImageSharp.Sandbox46` should trigger the compilation of ImageSharp subprojects using a manually defined solution dependencies - -## How to profile unit tests - -#### 1. With Visual Studio 2015 Test Runner -- **Do not** build `ImageSharp.Tests` -- Build `ImageSharp.Sandbox46` -- Use the [context menu in Test Explorer](https://adamprescott.net/2012/12/12/performance-profiling-for-unit-tests/) - -NOTE: -There was no *Profile test* option in my VS Professional. Maybe things were messed by VS2017 RC installation. [This post suggests](http://stackoverflow.com/questions/32034375/profiling-tests-in-visual-studio-community-2015) it's necessary to own Premium or Ultimate edition of Visual Studio to profile tests. - -#### 2. With JetBrains ReSharper Ultimate -- The `Sandbox46` project is no longer needed here. The classic `ImageSharp.Tests` project can be discovered by Unit Test Explorer. -- You can use [context menus](https://www.jetbrains.com/resharper/features/unit_testing.html) from your test class, or from unit Test Exporer/Unit Test Sessions windows. -![Context Menu](https://www.jetbrains.com/resharper/features/screenshots/100/unit_testing_profiling.png) \ No newline at end of file diff --git a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj b/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj similarity index 70% rename from tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj rename to tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj index e89b28dc11..99269e339a 100644 --- a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj +++ b/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj @@ -2,26 +2,26 @@ - SixLabors.ImageSharp.Sandbox46 + ImageSharp.Tests.ProfilingSandbox A cross-platform library for processing of image files written in C# Exe false - SixLabors.ImageSharp.Sandbox46 + SixLabors.ImageSharp.Tests.ProfilingSandbox win7-x64 netcoreapp3.1;netcoreapp2.1;net472 - SixLabors.ImageSharp.Sandbox46.Program + SixLabors.ImageSharp.Tests.ProfilingSandbox.Program false - - - - + + + + diff --git a/tests/ImageSharp.Sandbox46/Program.cs b/tests/ImageSharp.Tests.ProfilingSandbox/Program.cs similarity index 88% rename from tests/ImageSharp.Sandbox46/Program.cs rename to tests/ImageSharp.Tests.ProfilingSandbox/Program.cs index 93fe74076e..f041e16eb5 100644 --- a/tests/ImageSharp.Sandbox46/Program.cs +++ b/tests/ImageSharp.Tests.ProfilingSandbox/Program.cs @@ -1,18 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// +using System; +using SixLabors.ImageSharp.Tests.Formats.Jpg; using SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations; using SixLabors.ImageSharp.Tests.ProfilingBenchmarks; +using Xunit.Abstractions; -namespace SixLabors.ImageSharp.Sandbox46 +namespace SixLabors.ImageSharp.Tests.ProfilingSandbox { - using System; - using SixLabors.ImageSharp.Tests.Formats.Jpg; - - using Xunit.Abstractions; - public class Program { private class ConsoleOutput : ITestOutputHelper diff --git a/tests/ImageSharp.Tests.ProfilingSandbox/README.md b/tests/ImageSharp.Tests.ProfilingSandbox/README.md new file mode 100644 index 0000000000..43fdab9ef6 --- /dev/null +++ b/tests/ImageSharp.Tests.ProfilingSandbox/README.md @@ -0,0 +1,2 @@ +## ImageSharp.Tests.ProfilingSandbox +Helper project to run and profile unit tests or other "sandbox" code from a single .exe entry point. diff --git a/tests/ImageSharp.Sandbox46/app.config b/tests/ImageSharp.Tests.ProfilingSandbox/app.config similarity index 100% rename from tests/ImageSharp.Sandbox46/app.config rename to tests/ImageSharp.Tests.ProfilingSandbox/app.config diff --git a/tests/ImageSharp.Tests/AssemblyInfo.cs b/tests/ImageSharp.Tests/AssemblyInfo.cs new file mode 100644 index 0000000000..944fbe101e --- /dev/null +++ b/tests/ImageSharp.Tests/AssemblyInfo.cs @@ -0,0 +1,6 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Runtime.CompilerServices; + +[assembly:InternalsVisibleTo("ImageSharp.Tests.ProfilingSandbox")] diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs index 2485561f1e..59b6963eb9 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs @@ -2,7 +2,9 @@ // Licensed under the Apache License, Version 2.0. +using Microsoft.DotNet.RemoteExecutor; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities; using Xunit; // ReSharper disable InconsistentNaming @@ -15,22 +17,23 @@ public partial class JpegDecoderTests public void DecodeBaselineJpeg(TestImageProvider provider) where TPixel : struct, IPixel { - if (SkipTest(provider)) + static void RunTest(string providerDump) { - // skipping to avoid OutOfMemoryException on CI - return; - } + TestImageProvider provider = + BasicSerializer.Deserialize>(providerDump); - using (Image image = provider.GetImage(JpegDecoder)) - { + using Image image = provider.GetImage(JpegDecoder); image.DebugSave(provider); provider.Utility.TestName = DecodeBaselineJpegOutputName; image.CompareToReferenceOutput( - this.GetImageComparer(provider), + GetImageComparer(provider), provider, appendPixelTypeToFileName: false); } + + string providerDump = BasicSerializer.Serialize(provider); + RemoteExecutor.Invoke(RunTest, providerDump).Dispose(); } [Theory] diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Progressive.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Progressive.cs index 77bc9f5404..4f155e9c3a 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Progressive.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Progressive.cs @@ -1,7 +1,9 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using Microsoft.DotNet.RemoteExecutor; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities; using Xunit; // ReSharper disable InconsistentNaming @@ -16,22 +18,23 @@ public partial class JpegDecoderTests public void DecodeProgressiveJpeg(TestImageProvider provider) where TPixel : struct, IPixel { - if (SkipTest(provider)) + static void RunTest(string providerDump) { - // skipping to avoid OutOfMemoryException on CI - return; - } + TestImageProvider provider = + BasicSerializer.Deserialize>(providerDump); - using (Image image = provider.GetImage(JpegDecoder)) - { + using Image image = provider.GetImage(JpegDecoder); image.DebugSave(provider); provider.Utility.TestName = DecodeProgressiveJpegOutputName; image.CompareToReferenceOutput( - this.GetImageComparer(provider), + GetImageComparer(provider), provider, appendPixelTypeToFileName: false); } + + string dump = BasicSerializer.Serialize(provider); + RemoteExecutor.Invoke(RunTest, dump).Dispose(); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index 009f86483e..90caea387d 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -4,11 +4,12 @@ using System; using System.IO; using System.Linq; - +using Microsoft.DotNet.RemoteExecutor; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; +using SixLabors.ImageSharp.Tests.TestUtilities; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using Xunit; @@ -23,9 +24,15 @@ public partial class JpegDecoderTests public const PixelTypes CommonNonDefaultPixelTypes = PixelTypes.Rgba32 | PixelTypes.Argb32 | PixelTypes.RgbaVector; private const float BaselineTolerance = 0.001F / 100; + private const float ProgressiveTolerance = 0.2F / 100; - private ImageComparer GetImageComparer(TestImageProvider provider) + static JpegDecoderTests() + { + TestEnvironment.PrepareRemoteExecutor(); + } + + private static ImageComparer GetImageComparer(TestImageProvider provider) where TPixel : struct, IPixel { string file = provider.SourceFileOrDescription; @@ -88,23 +95,19 @@ public void ParseStream_BasicPropertiesAreCorrect() public void JpegDecoder_IsNotBoundToSinglePixelType(TestImageProvider provider) where TPixel : struct, IPixel { - if (SkipTest(provider)) - { - return; - } - - // For 32 bit test environments: - provider.Configuration.MemoryAllocator = ArrayPoolMemoryAllocator.CreateWithModeratePooling(); - - using (Image image = provider.GetImage(JpegDecoder)) + static void RunTest(string providerDump) { + TestImageProvider provider = + BasicSerializer.Deserialize>(providerDump); + using Image image = provider.GetImage(JpegDecoder); image.DebugSave(provider); provider.Utility.TestName = DecodeBaselineJpegOutputName; image.CompareToReferenceOutput(ImageComparer.Tolerant(BaselineTolerance), provider, appendPixelTypeToFileName: false); } - provider.Configuration.MemoryAllocator.ReleaseRetainedResources(); + string dump = BasicSerializer.Serialize(provider); + RemoteExecutor.Invoke(RunTest, dump).Dispose(); } // DEBUG ONLY! diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index 4aabc2f4e2..34cdca49a1 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -18,6 +18,7 @@ + diff --git a/tests/ImageSharp.Tests/Memory/Alocators/ArrayPoolMemoryAllocatorTests.cs b/tests/ImageSharp.Tests/Memory/Alocators/ArrayPoolMemoryAllocatorTests.cs index d38b5b9ddb..227d627784 100644 --- a/tests/ImageSharp.Tests/Memory/Alocators/ArrayPoolMemoryAllocatorTests.cs +++ b/tests/ImageSharp.Tests/Memory/Alocators/ArrayPoolMemoryAllocatorTests.cs @@ -4,39 +4,36 @@ // ReSharper disable InconsistentNaming using System; using System.Buffers; +using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using Microsoft.DotNet.RemoteExecutor; +using Microsoft.Win32; using SixLabors.ImageSharp.Tests; using Xunit; namespace SixLabors.ImageSharp.Memory.Tests { - // TODO: Re-enable memory-intensive tests with arcade RemoteExecutor: - // https://github.com/dotnet/runtime/blob/master/docs/project/writing-tests.md#remoteexecutor public class ArrayPoolMemoryAllocatorTests { private const int MaxPooledBufferSizeInBytes = 2048; private const int PoolSelectorThresholdInBytes = MaxPooledBufferSizeInBytes / 2; - private MemoryAllocator MemoryAllocator { get; set; } = - new ArrayPoolMemoryAllocator(MaxPooledBufferSizeInBytes, PoolSelectorThresholdInBytes); - /// - /// Rent a buffer -> return it -> re-rent -> verify if it's span points to the previous location. + /// Contains SUT for in-process tests. /// - private bool CheckIsRentingPooledBuffer(int length) - where T : struct - { - IMemoryOwner buffer = this.MemoryAllocator.Allocate(length); - ref T ptrToPrevPosition0 = ref buffer.GetReference(); - buffer.Dispose(); + private MemoryAllocatorFixture LocalFixture { get; } = new MemoryAllocatorFixture(); - buffer = this.MemoryAllocator.Allocate(length); - bool sameBuffers = Unsafe.AreSame(ref ptrToPrevPosition0, ref buffer.GetReference()); - buffer.Dispose(); + /// + /// Contains SUT for tests executed by , + /// recreated in each external process. + /// + private static MemoryAllocatorFixture StaticFixture { get; } = new MemoryAllocatorFixture(); - return sameBuffers; + static ArrayPoolMemoryAllocatorTests() + { + TestEnvironment.PrepareRemoteExecutor(); } public class BufferTests : BufferTestSuite @@ -78,21 +75,21 @@ public void When_PoolSelectorThresholdInBytes_IsGreaterThan_MaxPooledBufferSizeI [InlineData(MaxPooledBufferSizeInBytes - 1)] public void SmallBuffersArePooled_OfByte(int size) { - Assert.True(this.CheckIsRentingPooledBuffer(size)); + Assert.True(this.LocalFixture.CheckIsRentingPooledBuffer(size)); } - [Theory(Skip = "Should be executed from a separate process.")] + [Theory] [InlineData(128 * 1024 * 1024)] [InlineData(MaxPooledBufferSizeInBytes + 1)] public void LargeBuffersAreNotPooled_OfByte(int size) { - if (!TestEnvironment.Is64BitProcess) + static void RunTest(string sizeStr) { - // can lead to OutOfMemoryException - return; + int size = int.Parse(sizeStr); + StaticFixture.CheckIsRentingPooledBuffer(size); } - Assert.False(this.CheckIsRentingPooledBuffer(size)); + RemoteExecutor.Invoke(RunTest, size.ToString()).Dispose(); } [Fact] @@ -100,21 +97,15 @@ public unsafe void SmallBuffersArePooled_OfBigValueType() { int count = (MaxPooledBufferSizeInBytes / sizeof(LargeStruct)) - 1; - Assert.True(this.CheckIsRentingPooledBuffer(count)); + Assert.True(this.LocalFixture.CheckIsRentingPooledBuffer(count)); } - [Fact(Skip = "Should be executed from a separate process.")] + [Fact] public unsafe void LaregeBuffersAreNotPooled_OfBigValueType() { - if (!TestEnvironment.Is64BitProcess) - { - // can lead to OutOfMemoryException - return; - } - int count = (MaxPooledBufferSizeInBytes / sizeof(LargeStruct)) + 1; - Assert.False(this.CheckIsRentingPooledBuffer(count)); + Assert.False(this.LocalFixture.CheckIsRentingPooledBuffer(count)); } [Theory] @@ -122,12 +113,13 @@ public unsafe void LaregeBuffersAreNotPooled_OfBigValueType() [InlineData(AllocationOptions.Clean)] public void CleaningRequests_AreControlledByAllocationParameter_Clean(AllocationOptions options) { - using (IMemoryOwner firstAlloc = this.MemoryAllocator.Allocate(42)) + MemoryAllocator memoryAllocator = this.LocalFixture.MemoryAllocator; + using (IMemoryOwner firstAlloc = memoryAllocator.Allocate(42)) { firstAlloc.GetSpan().Fill(666); } - using (IMemoryOwner secondAlloc = this.MemoryAllocator.Allocate(42, options)) + using (IMemoryOwner secondAlloc = memoryAllocator.Allocate(42, options)) { int expected = options == AllocationOptions.Clean ? 0 : 666; Assert.Equal(expected, secondAlloc.GetSpan()[0]); @@ -139,7 +131,8 @@ public void CleaningRequests_AreControlledByAllocationParameter_Clean(Allocation [InlineData(true)] public void ReleaseRetainedResources_ReplacesInnerArrayPool(bool keepBufferAlive) { - IMemoryOwner buffer = this.MemoryAllocator.Allocate(32); + MemoryAllocator memoryAllocator = this.LocalFixture.MemoryAllocator; + IMemoryOwner buffer = memoryAllocator.Allocate(32); ref int ptrToPrev0 = ref MemoryMarshal.GetReference(buffer.GetSpan()); if (!keepBufferAlive) @@ -147,9 +140,9 @@ public void ReleaseRetainedResources_ReplacesInnerArrayPool(bool keepBufferAlive buffer.Dispose(); } - this.MemoryAllocator.ReleaseRetainedResources(); + memoryAllocator.ReleaseRetainedResources(); - buffer = this.MemoryAllocator.Allocate(32); + buffer = memoryAllocator.Allocate(32); Assert.False(Unsafe.AreSame(ref ptrToPrev0, ref buffer.GetReference())); } @@ -157,87 +150,69 @@ public void ReleaseRetainedResources_ReplacesInnerArrayPool(bool keepBufferAlive [Fact] public void ReleaseRetainedResources_DisposingPreviouslyAllocatedBuffer_IsAllowed() { - IMemoryOwner buffer = this.MemoryAllocator.Allocate(32); - this.MemoryAllocator.ReleaseRetainedResources(); + MemoryAllocator memoryAllocator = this.LocalFixture.MemoryAllocator; + IMemoryOwner buffer = memoryAllocator.Allocate(32); + memoryAllocator.ReleaseRetainedResources(); buffer.Dispose(); } - [Fact(Skip = "Should be executed from a separate process.")] + [Fact] public void AllocationOverLargeArrayThreshold_UsesDifferentPool() { - if (!TestEnvironment.Is64BitProcess) + static void RunTest() { - // can lead to OutOfMemoryException - return; - } + const int ArrayLengthThreshold = PoolSelectorThresholdInBytes / sizeof(int); - const int ArrayLengthThreshold = PoolSelectorThresholdInBytes / sizeof(int); + IMemoryOwner small = StaticFixture.MemoryAllocator.Allocate(ArrayLengthThreshold - 1); + ref int ptr2Small = ref small.GetReference(); + small.Dispose(); - IMemoryOwner small = this.MemoryAllocator.Allocate(ArrayLengthThreshold - 1); - ref int ptr2Small = ref small.GetReference(); - small.Dispose(); + IMemoryOwner large = StaticFixture.MemoryAllocator.Allocate(ArrayLengthThreshold + 1); - IMemoryOwner large = this.MemoryAllocator.Allocate(ArrayLengthThreshold + 1); + Assert.False(Unsafe.AreSame(ref ptr2Small, ref large.GetReference())); + } - Assert.False(Unsafe.AreSame(ref ptr2Small, ref large.GetReference())); + RemoteExecutor.Invoke(RunTest).Dispose(); } - [Fact(Skip = "Should be executed from a separate process.")] + [Fact] public void CreateWithAggressivePooling() { - if (!TestEnvironment.Is64BitProcess) + static void RunTest() { - // can lead to OutOfMemoryException - return; + StaticFixture.MemoryAllocator = ArrayPoolMemoryAllocator.CreateWithAggressivePooling(); + Assert.True(StaticFixture.CheckIsRentingPooledBuffer(4096 * 4096)); } - this.MemoryAllocator = ArrayPoolMemoryAllocator.CreateWithAggressivePooling(); - - Assert.True(this.CheckIsRentingPooledBuffer(4096 * 4096)); + RemoteExecutor.Invoke(RunTest).Dispose(); } - [Fact(Skip = "Should be executed from a separate process.")] + [Fact] public void CreateDefault() { - if (!TestEnvironment.Is64BitProcess) + static void RunTest() { - // can lead to OutOfMemoryException - return; - } + StaticFixture.MemoryAllocator = ArrayPoolMemoryAllocator.CreateDefault(); - this.MemoryAllocator = ArrayPoolMemoryAllocator.CreateDefault(); + Assert.False(StaticFixture.CheckIsRentingPooledBuffer(2 * 4096 * 4096)); + Assert.True(StaticFixture.CheckIsRentingPooledBuffer(2048 * 2048)); + } - Assert.False(this.CheckIsRentingPooledBuffer(2 * 4096 * 4096)); - Assert.True(this.CheckIsRentingPooledBuffer(2048 * 2048)); + RemoteExecutor.Invoke(RunTest).Dispose(); } [Fact] public void CreateWithModeratePooling() { - if (!TestEnvironment.Is64BitProcess) + static void RunTest() { - // can lead to OutOfMemoryException - return; + StaticFixture.MemoryAllocator = ArrayPoolMemoryAllocator.CreateWithModeratePooling(); + Assert.False(StaticFixture.CheckIsRentingPooledBuffer(2048 * 2048)); + Assert.True(StaticFixture.CheckIsRentingPooledBuffer(1024 * 16)); } - this.MemoryAllocator = ArrayPoolMemoryAllocator.CreateWithModeratePooling(); - - Assert.False(this.CheckIsRentingPooledBuffer(2048 * 2048)); - Assert.True(this.CheckIsRentingPooledBuffer(1024 * 16)); - } - - [StructLayout(LayoutKind.Sequential)] - private struct Rgba32 - { - private readonly uint dummy; - } - - private const int SizeOfLargeStruct = MaxPooledBufferSizeInBytes / 5; - - [StructLayout(LayoutKind.Explicit, Size = SizeOfLargeStruct)] - private struct LargeStruct - { + RemoteExecutor.Invoke(RunTest).Dispose(); } [Theory] @@ -245,7 +220,8 @@ private struct LargeStruct [InlineData((int.MaxValue / SizeOfLargeStruct) + 1)] public void AllocateIncorrectAmount_ThrowsCorrect_ArgumentOutOfRangeException(int length) { - ArgumentOutOfRangeException ex = Assert.Throws(() => this.MemoryAllocator.Allocate(length)); + ArgumentOutOfRangeException ex = Assert.Throws(() => + this.LocalFixture.MemoryAllocator.Allocate(length)); Assert.Equal("length", ex.ParamName); } @@ -253,8 +229,45 @@ public void AllocateIncorrectAmount_ThrowsCorrect_ArgumentOutOfRangeException(in [InlineData(-1)] public void AllocateManagedByteBuffer_IncorrectAmount_ThrowsCorrect_ArgumentOutOfRangeException(int length) { - ArgumentOutOfRangeException ex = Assert.Throws(() => this.MemoryAllocator.AllocateManagedByteBuffer(length)); + ArgumentOutOfRangeException ex = Assert.Throws(() => + this.LocalFixture.MemoryAllocator.AllocateManagedByteBuffer(length)); Assert.Equal("length", ex.ParamName); } + + private class MemoryAllocatorFixture + { + public MemoryAllocator MemoryAllocator { get; set; } = + new ArrayPoolMemoryAllocator(MaxPooledBufferSizeInBytes, PoolSelectorThresholdInBytes); + + /// + /// Rent a buffer -> return it -> re-rent -> verify if it's span points to the previous location. + /// + public bool CheckIsRentingPooledBuffer(int length) + where T : struct + { + IMemoryOwner buffer = MemoryAllocator.Allocate(length); + ref T ptrToPrevPosition0 = ref buffer.GetReference(); + buffer.Dispose(); + + buffer = MemoryAllocator.Allocate(length); + bool sameBuffers = Unsafe.AreSame(ref ptrToPrevPosition0, ref buffer.GetReference()); + buffer.Dispose(); + + return sameBuffers; + } + } + + [StructLayout(LayoutKind.Sequential)] + private struct SmallStruct + { + private readonly uint dummy; + } + + private const int SizeOfLargeStruct = MaxPooledBufferSizeInBytes / 5; + + [StructLayout(LayoutKind.Explicit, Size = SizeOfLargeStruct)] + private struct LargeStruct + { + } } } diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs index 859be6b205..6706e4077a 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs @@ -29,8 +29,8 @@ private static Span AsSpan(T value) public void NormalBlendFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = PorterDuffFunctions.NormalSrcOver((TPixel)back, source, amount); - VectorAssert.Equal(expected, actual, 2); + TPixel actual = PorterDuffFunctions.NormalSrcOver(back.AsPixel(), source.AsPixel(), amount); + VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] @@ -38,8 +38,8 @@ public void NormalBlendFunction(TestPixel back, TestPixel(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = new DefaultPixelBlenders.NormalSrcOver().Blend(back, source, amount); - VectorAssert.Equal(expected, actual, 2); + TPixel actual = new DefaultPixelBlenders.NormalSrcOver().Blend(back.AsPixel(), source.AsPixel(), amount); + VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] @@ -49,7 +49,7 @@ public void NormalBlendFunctionBlenderBulk(TestPixel back, TestP { var dest = new Span(new TPixel[1]); new DefaultPixelBlenders.NormalSrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); - VectorAssert.Equal(expected, dest[0], 2); + VectorAssert.Equal(expected.AsPixel(), dest[0], 2); } public static TheoryData MultiplyFunctionData = new TheoryData { @@ -68,8 +68,8 @@ public void NormalBlendFunctionBlenderBulk(TestPixel back, TestP public void MultiplyFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = PorterDuffFunctions.MultiplySrcOver((TPixel)back, source, amount); - VectorAssert.Equal(expected, actual, 2); + TPixel actual = PorterDuffFunctions.MultiplySrcOver(back.AsPixel(), source.AsPixel(), amount); + VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] @@ -77,8 +77,8 @@ public void MultiplyFunction(TestPixel back, TestPixel s public void MultiplyFunctionBlender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = new DefaultPixelBlenders.MultiplySrcOver().Blend(back, source, amount); - VectorAssert.Equal(expected, actual, 2); + TPixel actual = new DefaultPixelBlenders.MultiplySrcOver().Blend(back.AsPixel(), source.AsPixel(), amount); + VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] @@ -88,7 +88,7 @@ public void MultiplyFunctionBlenderBulk(TestPixel back, TestPixe { var dest = new Span(new TPixel[1]); new DefaultPixelBlenders.MultiplySrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); - VectorAssert.Equal(expected, dest[0], 2); + VectorAssert.Equal(expected.AsPixel(), dest[0], 2); } public static TheoryData AddFunctionData = new TheoryData { @@ -107,8 +107,8 @@ public void MultiplyFunctionBlenderBulk(TestPixel back, TestPixe public void AddFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = PorterDuffFunctions.AddSrcOver((TPixel)back, source, amount); - VectorAssert.Equal(expected, actual, 2); + TPixel actual = PorterDuffFunctions.AddSrcOver(back.AsPixel(), source.AsPixel(), amount); + VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] @@ -116,8 +116,8 @@ public void AddFunction(TestPixel back, TestPixel source public void AddFunctionBlender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = new DefaultPixelBlenders.AddSrcOver().Blend(back, source, amount); - VectorAssert.Equal(expected, actual, 2); + TPixel actual = new DefaultPixelBlenders.AddSrcOver().Blend(back.AsPixel(), source.AsPixel(), amount); + VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] @@ -127,7 +127,7 @@ public void AddFunctionBlenderBulk(TestPixel back, TestPixel(new TPixel[1]); new DefaultPixelBlenders.AddSrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); - VectorAssert.Equal(expected, dest[0], 2); + VectorAssert.Equal(expected.AsPixel(), dest[0], 2); } public static TheoryData SubtractFunctionData = new TheoryData { @@ -146,8 +146,8 @@ public void AddFunctionBlenderBulk(TestPixel back, TestPixel(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = PorterDuffFunctions.SubtractSrcOver((TPixel)back, source, amount); - VectorAssert.Equal(expected, actual, 2); + TPixel actual = PorterDuffFunctions.SubtractSrcOver(back.AsPixel(), source.AsPixel(), amount); + VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] @@ -155,8 +155,8 @@ public void SubtractFunction(TestPixel back, TestPixel s public void SubtractFunctionBlender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = new DefaultPixelBlenders.SubtractSrcOver().Blend(back, source, amount); - VectorAssert.Equal(expected, actual, 2); + TPixel actual = new DefaultPixelBlenders.SubtractSrcOver().Blend(back.AsPixel(), source.AsPixel(), amount); + VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] @@ -166,7 +166,7 @@ public void SubtractFunctionBlenderBulk(TestPixel back, TestPixe { var dest = new Span(new TPixel[1]); new DefaultPixelBlenders.SubtractSrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); - VectorAssert.Equal(expected, dest[0], 2); + VectorAssert.Equal(expected.AsPixel(), dest[0], 2); } public static TheoryData ScreenFunctionData = new TheoryData { @@ -185,8 +185,8 @@ public void SubtractFunctionBlenderBulk(TestPixel back, TestPixe public void ScreenFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = PorterDuffFunctions.ScreenSrcOver((TPixel)back, source, amount); - VectorAssert.Equal(expected, actual, 2); + TPixel actual = PorterDuffFunctions.ScreenSrcOver(back.AsPixel(), source.AsPixel(), amount); + VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] @@ -194,8 +194,8 @@ public void ScreenFunction(TestPixel back, TestPixel sou public void ScreenFunctionBlender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = new DefaultPixelBlenders.ScreenSrcOver().Blend(back, source, amount); - VectorAssert.Equal(expected, actual, 2); + TPixel actual = new DefaultPixelBlenders.ScreenSrcOver().Blend(back.AsPixel(), source.AsPixel(), amount); + VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] @@ -205,7 +205,7 @@ public void ScreenFunctionBlenderBulk(TestPixel back, TestPixel< { var dest = new Span(new TPixel[1]); new DefaultPixelBlenders.ScreenSrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); - VectorAssert.Equal(expected, dest[0], 2); + VectorAssert.Equal(expected.AsPixel(), dest[0], 2); } public static TheoryData DarkenFunctionData = new TheoryData { @@ -224,8 +224,8 @@ public void ScreenFunctionBlenderBulk(TestPixel back, TestPixel< public void DarkenFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = PorterDuffFunctions.DarkenSrcOver((TPixel)back, source, amount); - VectorAssert.Equal(expected, actual, 2); + TPixel actual = PorterDuffFunctions.DarkenSrcOver(back.AsPixel(), source.AsPixel(), amount); + VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] @@ -233,8 +233,8 @@ public void DarkenFunction(TestPixel back, TestPixel sou public void DarkenFunctionBlender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = new DefaultPixelBlenders.DarkenSrcOver().Blend(back, source, amount); - VectorAssert.Equal(expected, actual, 2); + TPixel actual = new DefaultPixelBlenders.DarkenSrcOver().Blend(back.AsPixel(), source.AsPixel(), amount); + VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] @@ -244,7 +244,7 @@ public void DarkenFunctionBlenderBulk(TestPixel back, TestPixel< { var dest = new Span(new TPixel[1]); new DefaultPixelBlenders.DarkenSrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); - VectorAssert.Equal(expected, dest[0], 2); + VectorAssert.Equal(expected.AsPixel(), dest[0], 2); } public static TheoryData LightenFunctionData = new TheoryData { @@ -263,8 +263,8 @@ public void DarkenFunctionBlenderBulk(TestPixel back, TestPixel< public void LightenFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = PorterDuffFunctions.LightenSrcOver((TPixel)back, source, amount); - VectorAssert.Equal(expected, actual, 2); + TPixel actual = PorterDuffFunctions.LightenSrcOver(back.AsPixel(), source.AsPixel(), amount); + VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] @@ -272,8 +272,8 @@ public void LightenFunction(TestPixel back, TestPixel so public void LightenFunctionBlender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = new DefaultPixelBlenders.LightenSrcOver().Blend(back, source, amount); - VectorAssert.Equal(expected, actual, 2); + TPixel actual = new DefaultPixelBlenders.LightenSrcOver().Blend(back.AsPixel(), source.AsPixel(), amount); + VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] @@ -283,7 +283,7 @@ public void LightenFunctionBlenderBulk(TestPixel back, TestPixel { var dest = new Span(new TPixel[1]); new DefaultPixelBlenders.LightenSrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); - VectorAssert.Equal(expected, dest[0], 2); + VectorAssert.Equal(expected.AsPixel(), dest[0], 2); } public static TheoryData OverlayFunctionData = new TheoryData { @@ -302,8 +302,8 @@ public void LightenFunctionBlenderBulk(TestPixel back, TestPixel public void OverlayFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = PorterDuffFunctions.OverlaySrcOver((TPixel)back, source, amount); - VectorAssert.Equal(expected, actual, 2); + TPixel actual = PorterDuffFunctions.OverlaySrcOver(back.AsPixel(), source.AsPixel(), amount); + VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] @@ -311,8 +311,8 @@ public void OverlayFunction(TestPixel back, TestPixel so public void OverlayFunctionBlender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = new DefaultPixelBlenders.OverlaySrcOver().Blend(back, source, amount); - VectorAssert.Equal(expected, actual, 2); + TPixel actual = new DefaultPixelBlenders.OverlaySrcOver().Blend(back.AsPixel(), source.AsPixel(), amount); + VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] @@ -322,7 +322,7 @@ public void OverlayFunctionBlenderBulk(TestPixel back, TestPixel { var dest = new Span(new TPixel[1]); new DefaultPixelBlenders.OverlaySrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); - VectorAssert.Equal(expected, dest[0], 2); + VectorAssert.Equal(expected.AsPixel(), dest[0], 2); } public static TheoryData HardLightFunctionData = new TheoryData { @@ -341,8 +341,8 @@ public void OverlayFunctionBlenderBulk(TestPixel back, TestPixel public void HardLightFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = PorterDuffFunctions.HardLightSrcOver((TPixel)back, source, amount); - VectorAssert.Equal(expected, actual, 2); + TPixel actual = PorterDuffFunctions.HardLightSrcOver(back.AsPixel(), source.AsPixel(), amount); + VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] @@ -350,8 +350,8 @@ public void HardLightFunction(TestPixel back, TestPixel public void HardLightFunctionBlender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = new DefaultPixelBlenders.HardLightSrcOver().Blend(back, source, amount); - VectorAssert.Equal(expected, actual, 2); + TPixel actual = new DefaultPixelBlenders.HardLightSrcOver().Blend(back.AsPixel(), source.AsPixel(), amount); + VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] @@ -361,7 +361,7 @@ public void HardLightFunctionBlenderBulk(TestPixel back, TestPix { var dest = new Span(new TPixel[1]); new DefaultPixelBlenders.HardLightSrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); - VectorAssert.Equal(expected, dest[0], 2); + VectorAssert.Equal(expected.AsPixel(), dest[0], 2); } } } diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs index ef2531060e..9b6814f9a0 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs @@ -10,7 +10,7 @@ using SixLabors.ImageSharp.ColorSpaces.Companding; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; - +using SixLabors.ImageSharp.Tests.TestUtilities; using Xunit; using Xunit.Abstractions; @@ -279,20 +279,20 @@ public void ToVector4(int count) } - public static readonly TheoryData Generic_To_Data = new TheoryData + public static readonly TheoryData Generic_To_Data = new TheoryData { - default(Rgba32), - default(Bgra32), - default(Rgb24), - default(L8), - default(L16), - default(Rgb48), - default(Rgba64) + new TestPixel(), + new TestPixel(), + new TestPixel(), + new TestPixel(), + new TestPixel(), + new TestPixel(), + new TestPixel() }; [Theory] [MemberData(nameof(Generic_To_Data))] - public void Generic_To(TDestPixel dummy) + public void Generic_To(TestPixel dummy) where TDestPixel : struct, IPixel { const int Count = 2134; diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs index cd3f74f8e2..34b0165139 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs @@ -6,11 +6,12 @@ using System.Globalization; using System.Linq; using System.Text.RegularExpressions; +using Microsoft.DotNet.RemoteExecutor; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Convolution; - +using SixLabors.ImageSharp.Tests.TestUtilities; using Xunit; using Xunit.Abstractions; @@ -18,6 +19,11 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution { public class BokehBlurTest { + static BokehBlurTest() + { + TestEnvironment.PrepareRemoteExecutor(); + } + private static readonly string Components10x2 = @" [[ 0.00451261+0.0165137j 0.02161237-0.00299122j 0.00387479-0.02682816j -0.02752798-0.01788438j -0.03553877+0.0154543j -0.01428268+0.04224722j @@ -124,10 +130,22 @@ public void Serialize(IXunitSerializationInfo info) public void BokehBlurFilterProcessor(TestImageProvider provider, BokehBlurInfo value) where TPixel : struct, IPixel { - provider.RunValidatingProcessorTest( - x => x.BokehBlur(value.Radius, value.Components, value.Gamma), - testOutputDetails: value.ToString(), - appendPixelTypeToFileName: false); + static void RunTest(string providerDump, string infoDump) + { + TestImageProvider provider = + BasicSerializer.Deserialize>(providerDump); + BokehBlurInfo value = BasicSerializer.Deserialize(infoDump); + + provider.RunValidatingProcessorTest( + x => x.BokehBlur(value.Radius, value.Components, value.Gamma), + testOutputDetails: value.ToString(), + appendPixelTypeToFileName: false); + + } + + RemoteExecutor + .Invoke(RunTest, BasicSerializer.Serialize(provider), BasicSerializer.Serialize(value)) + .Dispose(); } [Theory] @@ -137,9 +155,18 @@ public void BokehBlurFilterProcessor(TestImageProvider provider, public void BokehBlurFilterProcessor_WorksWithAllPixelTypes(TestImageProvider provider) where TPixel : struct, IPixel { - provider.RunValidatingProcessorTest( - x => x.BokehBlur(8, 2, 3), - appendSourceFileOrDescription: false); + static void RunTest(string providerDump) + { + TestImageProvider provider = + BasicSerializer.Deserialize>(providerDump); + provider.RunValidatingProcessorTest( + x => x.BokehBlur(8, 2, 3), + appendSourceFileOrDescription: false); + } + + RemoteExecutor + .Invoke(RunTest, BasicSerializer.Serialize(provider)) + .Dispose(); } @@ -148,15 +175,26 @@ public void BokehBlurFilterProcessor_WorksWithAllPixelTypes(TestImagePro public void BokehBlurFilterProcessor_Bounded(TestImageProvider provider, BokehBlurInfo value) where TPixel : struct, IPixel { - provider.RunValidatingProcessorTest( - x => + static void RunTest(string providerDump, string infoDump) + { + TestImageProvider provider = + BasicSerializer.Deserialize>(providerDump); + BokehBlurInfo value = BasicSerializer.Deserialize(infoDump); + + provider.RunValidatingProcessorTest( + x => { Size size = x.GetCurrentSize(); var bounds = new Rectangle(10, 10, size.Width / 2, size.Height / 2); x.BokehBlur(value.Radius, value.Components, value.Gamma, bounds); }, - testOutputDetails: value.ToString(), - appendPixelTypeToFileName: false); + testOutputDetails: value.ToString(), + appendPixelTypeToFileName: false); + } + + RemoteExecutor + .Invoke(RunTest, BasicSerializer.Serialize(provider), BasicSerializer.Serialize(value)) + .Dispose(); } } } diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithMemberFactoryAttribute.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithMemberFactoryAttribute.cs index cdf5fcb089..5aed3b3647 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithMemberFactoryAttribute.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithMemberFactoryAttribute.cs @@ -31,21 +31,9 @@ public WithMemberFactoryAttribute(string memberMethodName, PixelTypes pixelTypes protected override object[] GetFactoryMethodArgs(MethodInfo testMethod, Type factoryType) { - MethodInfo m = testMethod.DeclaringType.GetMethod(this.memberMethodName); - - Type[] args = factoryType.GetGenericArguments(); - Type colorType = args.Single(); - - Type imgType = typeof(Image<>).MakeGenericType(colorType); - - Type funcType = typeof(Func<>).MakeGenericType(imgType); - - MethodInfo genericMethod = m.MakeGenericMethod(args); - - Delegate d = genericMethod.CreateDelegate(funcType); - return new object[] { d }; + return new object[] { testMethod.DeclaringType.FullName, this.memberMethodName}; } protected override string GetFactoryMethodName(MethodInfo testMethod) => "Lambda"; } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/TestUtilities/BasicSerializer.cs b/tests/ImageSharp.Tests/TestUtilities/BasicSerializer.cs new file mode 100644 index 0000000000..48469db431 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/BasicSerializer.cs @@ -0,0 +1,97 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Globalization; +using System.IO; +using Xunit.Abstractions; + +namespace SixLabors.ImageSharp.Tests.TestUtilities +{ + /// + /// RemoteExecutor can only execute static methods, which can only consume string arguments, + /// because data is being passed on command line interface. This utility allows serialization + /// of types to strings. + /// + internal class BasicSerializer : IXunitSerializationInfo + { + private readonly Dictionary map = new Dictionary(); + + public const char Separator = ':'; + + private string DumpToString(Type type) + { + using var ms = new MemoryStream(); + using var writer = new StreamWriter(ms); + writer.WriteLine(type.FullName); + foreach (KeyValuePair kv in this.map) + { + writer.WriteLine($"{kv.Key}{Separator}{kv.Value}"); + } + writer.Flush(); + byte[] data = ms.ToArray(); + return System.Convert.ToBase64String(data); + } + + private Type LoadDump(string dump) + { + byte[] data = System.Convert.FromBase64String(dump); + + using var ms = new MemoryStream(data); + using var reader = new StreamReader(ms); + var type = Type.GetType(reader.ReadLine()); + for (string s = reader.ReadLine(); s != null ; s = reader.ReadLine()) + { + string[] kv = s.Split(Separator); + this.map[kv[0]] = kv[1]; + } + + return type; + } + + public static string Serialize(IXunitSerializable serializable) + { + var serializer = new BasicSerializer(); + serializable.Serialize(serializer); + return serializer.DumpToString(serializable.GetType()); + } + + public static T Deserialize(string dump) where T : IXunitSerializable + { + var serializer = new BasicSerializer(); + Type type = serializer.LoadDump(dump); + + var result = (T) Activator.CreateInstance(type); + result.Deserialize(serializer); + return result; + } + + public void AddValue(string key, object value, Type type = null) + { + Guard.NotNull(key, nameof(key)); + if (value == null) + { + return; + } + type ??= value.GetType(); + + this.map[key] = TypeDescriptor.GetConverter(type).ConvertToInvariantString(value); + } + + public object GetValue(string key, Type type) + { + Guard.NotNull(key, nameof(key)); + + if (!this.map.TryGetValue(key, out string str)) + { + return type.IsValueType ? Activator.CreateInstance(type) : null; + } + + return TypeDescriptor.GetConverter(type).ConvertFromInvariantString(str); + } + + public T GetValue(string key) => (T)this.GetValue(key, typeof(T)); + } +} diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/LambdaProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/LambdaProvider.cs deleted file mode 100644 index 5bd53a4c0c..0000000000 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/LambdaProvider.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; - -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp.Tests -{ - /// - /// Provides instances for parametric unit tests. - /// - /// The pixel format of the image - public abstract partial class TestImageProvider - where TPixel : struct, IPixel - { - private class LambdaProvider : TestImageProvider - { - private readonly Func> factoryFunc; - - public LambdaProvider(Func> factoryFunc) - { - this.factoryFunc = factoryFunc; - } - - public override Image GetImage() => this.factoryFunc(); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/MemberMethodProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/MemberMethodProvider.cs new file mode 100644 index 0000000000..077dc622f8 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/MemberMethodProvider.cs @@ -0,0 +1,69 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Linq; +using System.Reflection; +using SixLabors.ImageSharp.PixelFormats; +using Xunit.Abstractions; + +namespace SixLabors.ImageSharp.Tests +{ + /// + /// Provides instances for parametric unit tests. + /// + /// The pixel format of the image + public abstract partial class TestImageProvider + where TPixel : struct, IPixel + { + private class MemberMethodProvider : TestImageProvider + { + private string declaringTypeName; + private string methodName; + private Func> factoryFunc; + + public MemberMethodProvider() + { + } + + public MemberMethodProvider(string declaringTypeName, string methodName) + { + this.declaringTypeName = declaringTypeName; + this.methodName = methodName; + } + + public override Image GetImage() + { + this.factoryFunc ??= this.GetFactory(); + return this.factoryFunc(); + } + + public override void Serialize(IXunitSerializationInfo info) + { + base.Serialize(info); + + info.AddValue(nameof(this.declaringTypeName), this.declaringTypeName); + info.AddValue(nameof(this.methodName), this.methodName); + } + + public override void Deserialize(IXunitSerializationInfo info) + { + base.Deserialize(info); + + this.methodName = info.GetValue(nameof(this.methodName)); + this.declaringTypeName = info.GetValue(nameof(this.declaringTypeName)); + } + + private Func> GetFactory() + { + var declaringType = Type.GetType(this.declaringTypeName); + MethodInfo m = declaringType.GetMethod(this.methodName); + Type pixelType = typeof(TPixel); + Type imgType = typeof(Image<>).MakeGenericType(pixelType); + Type funcType = typeof(Func<>).MakeGenericType(imgType); + MethodInfo genericMethod = m.MakeGenericMethod(pixelType); + return (Func>) genericMethod.CreateDelegate(funcType); + } + } + } +} diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs index 63de4c96f4..8b165a8beb 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs @@ -26,7 +26,7 @@ public interface ITestImageProvider /// Provides instances for parametric unit tests. /// /// The pixel format of the image - public abstract partial class TestImageProvider : ITestImageProvider + public abstract partial class TestImageProvider : ITestImageProvider, IXunitSerializable where TPixel : struct, IPixel { public PixelTypes PixelType { get; private set; } = typeof(TPixel).GetPixelType(); @@ -73,10 +73,11 @@ public static TestImageProvider File( } public static TestImageProvider Lambda( - Func> factoryFunc, + string declaringTypeName, + string methodName, MethodInfo testMethod = null, PixelTypes pixelTypeOverride = PixelTypes.Undefined) - => new LambdaProvider(factoryFunc).Init(testMethod, pixelTypeOverride); + => new MemberMethodProvider(declaringTypeName, methodName).Init(testMethod, pixelTypeOverride); public static TestImageProvider Solid( int width, diff --git a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs index a5a3e332c7..1bc4f47c72 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs @@ -2,10 +2,12 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Tests { @@ -45,13 +47,12 @@ public static partial class TestEnvironment internal static string SolutionDirectoryFullPath => SolutionDirectoryFullPathLazy.Value; + private static readonly FileInfo TestAssemblyFile = + new FileInfo(typeof(TestEnvironment).GetTypeInfo().Assembly.Location); + private static string GetSolutionDirectoryFullPathImpl() { - string assemblyLocation = typeof(TestEnvironment).GetTypeInfo().Assembly.Location; - - var assemblyFile = new FileInfo(assemblyLocation); - - DirectoryInfo directory = assemblyFile.Directory; + DirectoryInfo directory = TestAssemblyFile.Directory; while (!directory.EnumerateFiles(ImageSharpSolutionFileName).Any()) { @@ -62,20 +63,20 @@ private static string GetSolutionDirectoryFullPathImpl() catch (Exception ex) { throw new Exception( - $"Unable to find ImageSharp solution directory from {assemblyLocation} because of {ex.GetType().Name}!", + $"Unable to find ImageSharp solution directory from {TestAssemblyFile} because of {ex.GetType().Name}!", ex); } if (directory == null) { - throw new Exception($"Unable to find ImageSharp solution directory from {assemblyLocation}!"); + throw new Exception($"Unable to find ImageSharp solution directory from {TestAssemblyFile}!"); } } return directory.FullName; } - private static string GetFullPath(string relativePath) => + private static string GetFullPath(string relativePath) => Path.Combine(SolutionDirectoryFullPath, relativePath) .Replace('\\', Path.DirectorySeparatorChar); @@ -83,7 +84,7 @@ private static string GetFullPath(string relativePath) => /// Gets the correct full path to the Input Images directory. /// internal static string InputImagesDirectoryFullPath => GetFullPath(InputImagesRelativePath); - + /// /// Gets the correct full path to the Actual Output directory. (To be written to by the test cases.) /// @@ -100,13 +101,15 @@ internal static string GetReferenceOutputFileName(string actualOutputFileName) = actualOutputFileName.Replace("ActualOutput", @"External\ReferenceOutput").Replace('\\', Path.DirectorySeparatorChar); internal static bool IsLinux => RuntimeInformation.IsOSPlatform(OSPlatform.Linux); - + internal static bool IsMono => Type.GetType("Mono.Runtime") != null; // https://stackoverflow.com/a/721194 internal static bool IsWindows => RuntimeInformation.IsOSPlatform(OSPlatform.Windows); internal static bool Is64BitProcess => IntPtr.Size == 8; + internal static bool IsFramework => string.IsNullOrEmpty(NetCoreVersion); + /// /// Creates the image output directory. /// @@ -132,6 +135,95 @@ internal static string CreateOutputDirectory(string path, params string[] pathPa return path; } + /// + /// Creates Microsoft.DotNet.RemoteExecutor.exe.config for .NET framework, + /// When running in 32 bits, enforces 32 bit execution of Microsoft.DotNet.RemoteExecutor.exe + /// with the help of CorFlags.exe found in Windows SDK. + /// + internal static void PrepareRemoteExecutor() + { + if (!IsFramework) + { + return; + } + + string remoteExecutorConfigPath = + Path.Combine(TestAssemblyFile.DirectoryName, "Microsoft.DotNet.RemoteExecutor.exe.config"); + + if (File.Exists(remoteExecutorConfigPath)) + { + // already prepared + return; + } + + string testProjectConfigPath = TestAssemblyFile.FullName + ".config"; + + File.Copy(testProjectConfigPath, remoteExecutorConfigPath); + + if (Is64BitProcess) + { + return; + } + + EnsureRemoteExecutorIs32Bit(); + } + + /// + /// Locate and run CorFlags.exe /32Bit+ + /// https://docs.microsoft.com/en-us/dotnet/framework/tools/corflags-exe-corflags-conversion-tool + /// + private static void EnsureRemoteExecutorIs32Bit() + { + string windowsSdksDir = Path.Combine(Environment.GetEnvironmentVariable("PROGRAMFILES(x86)"), + "Microsoft SDKs", "Windows"); + + FileInfo corFlagsFile = Find(new DirectoryInfo(windowsSdksDir), "CorFlags.exe"); + + string remoteExecutorPath = Path.Combine(TestAssemblyFile.DirectoryName, "Microsoft.DotNet.RemoteExecutor.exe"); + + string args = $"{remoteExecutorPath} /32Bit+ /Force"; + + var si = new ProcessStartInfo() + { + FileName = corFlagsFile.FullName, + Arguments = args, + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true + }; + + using var proc = Process.Start(si); + proc.WaitForExit(); + string standardOutput = proc.StandardOutput.ReadToEnd(); + string standardError = proc.StandardError.ReadToEnd(); + + if (proc.ExitCode != 0) + { + throw new Exception( + $@"Failed to run {si.FileName} {si.Arguments}:\n STDOUT: {standardOutput}\n STDERR: {standardError}"); + } + + static FileInfo Find(DirectoryInfo root, string name) + { + FileInfo fi = root.EnumerateFiles(name).FirstOrDefault(); + if (fi != null) + { + return fi; + } + + foreach (DirectoryInfo dir in root.EnumerateDirectories()) + { + fi = Find(dir, name); + if (fi != null) + { + return fi; + } + } + + return null; + } + } + /// /// Solution borrowed from: /// https://github.com/dotnet/BenchmarkDotNet/issues/448#issuecomment-308424100 @@ -146,4 +238,4 @@ private static string GetNetCoreVersion() return ""; } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/TestUtilities/TestPixel.cs b/tests/ImageSharp.Tests/TestUtilities/TestPixel.cs index 1e1a45f074..f3be50e9af 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestPixel.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestPixel.cs @@ -29,11 +29,6 @@ public TestPixel(float red, float green, float blue, float alpha) public float Blue { get; set; } public float Alpha { get; set; } - public static implicit operator TPixel(TestPixel d) - { - return d?.AsPixel() ?? default(TPixel); - } - public TPixel AsPixel() { TPixel pix = default(TPixel); diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/BasicSerializerTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/BasicSerializerTests.cs new file mode 100644 index 0000000000..71ab5b2626 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/BasicSerializerTests.cs @@ -0,0 +1,62 @@ +using SixLabors.ImageSharp.Tests.TestUtilities; +using Xunit; +using Xunit.Abstractions; + +namespace SixLabors.ImageSharp.Tests +{ + public class BasicSerializerTests + { + class BaseObj : IXunitSerializable + { + public double Length { get; set; } + public string Name { get; set; } + public int Lives { get; set; } + + public virtual void Deserialize(IXunitSerializationInfo info) + { + info.AddValue(nameof(Length), Length); + info.AddValue(nameof(Name), Name); + info.AddValue(nameof(this.Lives), Lives); + } + + public virtual void Serialize(IXunitSerializationInfo info) + { + this.Length = info.GetValue(nameof(Length)); + this.Name = info.GetValue(nameof(Name)); + this.Lives = info.GetValue(nameof(Lives)); + } + } + + class DerivedObj : BaseObj + { + public double Strength { get; set; } + + public override void Deserialize(IXunitSerializationInfo info) + { + this.Strength = info.GetValue(nameof(Strength)); + base.Deserialize(info); + } + + public override void Serialize(IXunitSerializationInfo info) + { + base.Serialize(info); + info.AddValue(nameof(Strength), Strength); + } + } + + [Fact] + public void SerializeDeserialize_ShouldPreserveValues() + { + var obj = new DerivedObj() {Length = 123.1, Name = "Lol123!", Lives = 7, Strength = 4.8}; + + string str = BasicSerializer.Serialize(obj); + BaseObj mirrorBase = BasicSerializer.Deserialize(str); + + DerivedObj mirror = Assert.IsType(mirrorBase); + Assert.Equal(obj.Length, mirror.Length); + Assert.Equal(obj.Name, mirror.Name); + Assert.Equal(obj.Lives, mirror.Lives); + Assert.Equal(obj.Strength, mirror.Strength); + } + } +}