From 03df1dc791059b3effebb2b35b7c88df480b33f6 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 15 Aug 2017 19:37:42 +0200 Subject: [PATCH] changed ImageComparer design again --- .../CieLuvAndCieLchuvConversionTests.cs | 2 - .../Formats/Png/PngSmokeTests.cs | 6 +- tests/ImageSharp.Tests/ImageComparer.cs | 10 - .../ImagesSimilarityException.cs | 59 ------ .../Binarization/BinaryThresholdTest.cs | 4 +- .../Processors/Binarization/DitherTest.cs | 6 +- .../Processors/ColorMatrix/BlackWhiteTest.cs | 4 +- .../ColorMatrix/ColorBlindnessTest.cs | 4 +- .../Processors/ColorMatrix/GrayscaleTest.cs | 4 +- .../Processors/ColorMatrix/HueTest.cs | 4 +- .../Processors/ColorMatrix/KodachromeTest.cs | 4 +- .../Processors/ColorMatrix/LomographTest.cs | 4 +- .../Processors/ColorMatrix/PolaroidTest.cs | 4 +- .../Processors/ColorMatrix/SaturationTest.cs | 4 +- .../Processors/ColorMatrix/SepiaTest.cs | 4 +- .../Processors/Convolution/BoxBlurTest.cs | 4 +- .../Processors/Convolution/DetectEdgesTest.cs | 4 +- .../Convolution/GaussianBlurTest.cs | 4 +- .../Convolution/GaussianSharpenTest.cs | 4 +- .../Processors/Effects/AlphaTest.cs | 4 +- .../Processors/Effects/BackgroundColorTest.cs | 4 +- .../Processors/Effects/BrightnessTest.cs | 4 +- .../Processors/Effects/ContrastTest.cs | 4 +- .../Processors/Effects/InvertTest.cs | 4 +- .../Processors/Effects/OilPaintTest.cs | 4 +- .../Processors/Effects/PixelateTest.cs | 4 +- .../Processors/Overlays/GlowTest.cs | 4 +- .../Processors/Overlays/VignetteTest.cs | 4 +- .../ImageComparison}/ExactImageComparer.cs | 21 ++- .../ImageComparison/ImageComparer.cs | 80 ++++++++ .../ImageDimensionsMismatchException.cs | 17 ++ .../ImagePixelsAreDifferentException.cs | 18 ++ .../ImageComparison/ImageSimilarityReport.cs | 50 +++++ .../ImagesSimilarityException.cs | 12 ++ .../PercentageImageComparer_Old.cs} | 29 ++- .../ImageComparison/PixelDifference.cs | 40 ++++ .../ImageComparison/TolerantImageComparer.cs | 40 ++++ .../TestUtilities/TestImageExtensions.cs | 22 +-- .../TestUtilities/Tests/ImageComparerTests.cs | 173 +++++++++++------- 39 files changed, 481 insertions(+), 196 deletions(-) delete mode 100644 tests/ImageSharp.Tests/ImageComparer.cs delete mode 100644 tests/ImageSharp.Tests/ImagesSimilarityException.cs rename tests/ImageSharp.Tests/{ => TestUtilities/ImageComparison}/ExactImageComparer.cs (61%) create mode 100644 tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparer.cs create mode 100644 tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageDimensionsMismatchException.cs create mode 100644 tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImagePixelsAreDifferentException.cs create mode 100644 tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageSimilarityReport.cs create mode 100644 tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImagesSimilarityException.cs rename tests/ImageSharp.Tests/{PercentageImageComparer.cs => TestUtilities/ImageComparison/PercentageImageComparer_Old.cs} (91%) create mode 100644 tests/ImageSharp.Tests/TestUtilities/ImageComparison/PixelDifference.cs create mode 100644 tests/ImageSharp.Tests/TestUtilities/ImageComparison/TolerantImageComparer.cs diff --git a/tests/ImageSharp.Tests/Colorspaces/CieLuvAndCieLchuvConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/CieLuvAndCieLchuvConversionTests.cs index d263bbe18..e7fa2f04f 100644 --- a/tests/ImageSharp.Tests/Colorspaces/CieLuvAndCieLchuvConversionTests.cs +++ b/tests/ImageSharp.Tests/Colorspaces/CieLuvAndCieLchuvConversionTests.cs @@ -28,7 +28,6 @@ [InlineData(100, 0, 0, 100, 0, 0)] [InlineData(100, 50, 180, 100, -50, 0)] [InlineData(10, 36.0555, 56.3099, 10, 20, 30)] - [InlineData(10, 36.0555, 56.3099, 10, 20, 30)] [InlineData(10, 36.0555, 123.6901, 10, -20, 30)] [InlineData(10, 36.0555, 303.6901, 10, 20, -30)] [InlineData(10, 36.0555, 236.3099, 10, -20, -30)] @@ -55,7 +54,6 @@ [InlineData(100, 0, 0, 100, 0, 0)] [InlineData(100, -50, 0, 100, 50, 180)] [InlineData(10, 20, 30, 10, 36.0555, 56.3099)] - [InlineData(10, 20, 30, 10, 36.0555, 56.3099)] [InlineData(10, -20, 30, 10, 36.0555, 123.6901)] [InlineData(10, 20, -30, 10, 36.0555, 303.6901)] [InlineData(10, -20, -30, 10, 36.0555, 236.3099)] diff --git a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs index 6baaf1a54..c60712dd3 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs @@ -16,6 +16,7 @@ namespace ImageSharp.Tests.Formats.Png using System.Numerics; using ImageSharp.PixelFormats; + using ImageSharp.Tests.TestUtilities.ImageComparison; public class PngSmokeTests { @@ -34,8 +35,9 @@ namespace ImageSharp.Tests.Formats.Png ms.Position = 0; using (Image img2 = Image.Load(ms, new PngDecoder())) { + ImageComparer.Tolerant().VerifySimilarity(image, img2); // img2.Save(provider.Utility.GetTestOutputFileName("bmp", "_loaded"), new BmpEncoder()); - PercentageImageComparer.VerifySimilarity(image, img2); + PercentageImageComparer_Old.VerifySimilarity(image, img2); } } } @@ -121,7 +123,7 @@ namespace ImageSharp.Tests.Formats.Png ms.Position = 0; using (Image img2 = Image.Load(ms, new PngDecoder())) { - PercentageImageComparer.VerifySimilarity(image, img2); + PercentageImageComparer_Old.VerifySimilarity(image, img2); } } } diff --git a/tests/ImageSharp.Tests/ImageComparer.cs b/tests/ImageSharp.Tests/ImageComparer.cs deleted file mode 100644 index 2469c4611..000000000 --- a/tests/ImageSharp.Tests/ImageComparer.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace ImageSharp.Tests -{ - using ImageSharp.PixelFormats; - - public abstract class ImageComparer - { - public abstract void Verify(Image expected, Image actual) - where TPixelA : struct, IPixel where TPixelB : struct, IPixel; - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/ImagesSimilarityException.cs b/tests/ImageSharp.Tests/ImagesSimilarityException.cs deleted file mode 100644 index 92e5f518d..000000000 --- a/tests/ImageSharp.Tests/ImagesSimilarityException.cs +++ /dev/null @@ -1,59 +0,0 @@ -namespace ImageSharp.Tests -{ - using System; - using System.Text; - - using SixLabors.Primitives; - - public class ImagesSimilarityException : Exception - { - public ImagesSimilarityException(string message) - : base(message) - { - } - } - - public class ImageDimensionsMismatchException : ImagesSimilarityException - { - public ImageDimensionsMismatchException(Size expectedSize, Size actualSize) - : base($"The image dimensions {actualSize} do not match the expected {expectedSize}!") - { - this.ExpectedSize = expectedSize; - this.ActualSize = actualSize; - } - - public Size ExpectedSize { get; } - public Size ActualSize { get; } - } - - public class ImagesAreNotEqualException : ImagesSimilarityException - { - public ImagesAreNotEqualException(Point[] differences) - : base("Images are not equal! Differences: " + StringifyDifferences(differences)) - { - this.Differences = differences; - } - - public Point[] Differences { get; } - - private static string StringifyDifferences(Point[] differences) - { - var sb = new StringBuilder(); - int max = Math.Min(5, differences.Length); - - for (int i = 0; i < max; i++) - { - sb.Append(differences[i]); - if (i < max - 1) - { - sb.Append(';'); - } - } - if (differences.Length >= 5) - { - sb.Append("..."); - } - return sb.ToString(); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryThresholdTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryThresholdTest.cs index 6825a0cda..353dbb917 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryThresholdTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryThresholdTest.cs @@ -6,6 +6,8 @@ namespace ImageSharp.Tests.Processing.Processors.Binarization { using ImageSharp.PixelFormats; + using ImageSharp.Tests.TestUtilities.ImageComparison; + using SixLabors.Primitives; using Xunit; @@ -44,7 +46,7 @@ namespace ImageSharp.Tests.Processing.Processors.Binarization image.Mutate(x => x.BinaryThreshold(value, bounds)); image.DebugSave(provider, value); - PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + PercentageImageComparer_Old.EnsureProcessorChangesAreConstrained(source, image, bounds); } } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Binarization/DitherTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Binarization/DitherTest.cs index 149655ea6..8cfd1195f 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Binarization/DitherTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Binarization/DitherTest.cs @@ -8,6 +8,8 @@ namespace ImageSharp.Tests.Processing.Processors.Binarization using ImageSharp.Dithering; using ImageSharp.Dithering.Ordered; using ImageSharp.PixelFormats; + using ImageSharp.Tests.TestUtilities.ImageComparison; + using SixLabors.Primitives; using Xunit; @@ -56,7 +58,7 @@ namespace ImageSharp.Tests.Processing.Processors.Binarization image.Mutate(x => x.Dither(ditherer, bounds)); image.DebugSave(provider, name); - PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + PercentageImageComparer_Old.EnsureProcessorChangesAreConstrained(source, image, bounds); } } @@ -85,7 +87,7 @@ namespace ImageSharp.Tests.Processing.Processors.Binarization image.Mutate(x => x.Dither(diffuser, .5F, bounds)); image.DebugSave(provider, name); - PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + PercentageImageComparer_Old.EnsureProcessorChangesAreConstrained(source, image, bounds); } } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/BlackWhiteTest.cs b/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/BlackWhiteTest.cs index 10c6c2690..e91c9b98a 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/BlackWhiteTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/BlackWhiteTest.cs @@ -6,6 +6,8 @@ namespace ImageSharp.Tests.Processing.Processors.ColorMatrix { using ImageSharp.PixelFormats; + using ImageSharp.Tests.TestUtilities.ImageComparison; + using SixLabors.Primitives; using Xunit; @@ -36,7 +38,7 @@ namespace ImageSharp.Tests.Processing.Processors.ColorMatrix image.Mutate(x => x.BlackWhite(bounds)); image.DebugSave(provider); - PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + PercentageImageComparer_Old.EnsureProcessorChangesAreConstrained(source, image, bounds); } } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/ColorBlindnessTest.cs b/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/ColorBlindnessTest.cs index bf923c373..0d295f683 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/ColorBlindnessTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/ColorBlindnessTest.cs @@ -7,6 +7,8 @@ namespace ImageSharp.Tests.Processing.Processors.ColorMatrix { using ImageSharp.PixelFormats; using ImageSharp.Processing; + using ImageSharp.Tests.TestUtilities.ImageComparison; + using SixLabors.Primitives; using Xunit; @@ -50,7 +52,7 @@ namespace ImageSharp.Tests.Processing.Processors.ColorMatrix image.Mutate(x => x.ColorBlindness(colorBlindness, bounds)); image.DebugSave(provider, colorBlindness.ToString()); - PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + PercentageImageComparer_Old.EnsureProcessorChangesAreConstrained(source, image, bounds); } } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/GrayscaleTest.cs b/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/GrayscaleTest.cs index 2567c41af..f32012653 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/GrayscaleTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/GrayscaleTest.cs @@ -7,6 +7,8 @@ namespace ImageSharp.Tests.Processing.Processors.ColorMatrix { using ImageSharp.PixelFormats; using ImageSharp.Processing; + using ImageSharp.Tests.TestUtilities.ImageComparison; + using SixLabors.Primitives; using Xunit; @@ -54,7 +56,7 @@ namespace ImageSharp.Tests.Processing.Processors.ColorMatrix image.Mutate(x => x.Grayscale(value, bounds)); image.DebugSave(provider, value.ToString()); - PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + PercentageImageComparer_Old.EnsureProcessorChangesAreConstrained(source, image, bounds); } } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/HueTest.cs b/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/HueTest.cs index d96c908e2..8aaf4fd5c 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/HueTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/HueTest.cs @@ -6,6 +6,8 @@ namespace ImageSharp.Tests.Processing.Processors.ColorMatrix { using ImageSharp.PixelFormats; + using ImageSharp.Tests.TestUtilities.ImageComparison; + using SixLabors.Primitives; using Xunit; @@ -43,7 +45,7 @@ namespace ImageSharp.Tests.Processing.Processors.ColorMatrix image.Mutate(x => x.Hue(value, bounds)); image.DebugSave(provider, value); - PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + PercentageImageComparer_Old.EnsureProcessorChangesAreConstrained(source, image, bounds); } } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/KodachromeTest.cs b/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/KodachromeTest.cs index 17d969a68..77b23902f 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/KodachromeTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/KodachromeTest.cs @@ -6,6 +6,8 @@ namespace ImageSharp.Tests.Processing.Processors.ColorMatrix { using ImageSharp.PixelFormats; + using ImageSharp.Tests.TestUtilities.ImageComparison; + using SixLabors.Primitives; using Xunit; @@ -36,7 +38,7 @@ namespace ImageSharp.Tests.Processing.Processors.ColorMatrix image.Mutate(x => x.Kodachrome(bounds)); image.DebugSave(provider); - PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + PercentageImageComparer_Old.EnsureProcessorChangesAreConstrained(source, image, bounds); } } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/LomographTest.cs b/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/LomographTest.cs index fe5017392..a27d761cc 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/LomographTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/LomographTest.cs @@ -8,6 +8,8 @@ namespace ImageSharp.Tests.Processing.Processors.ColorMatrix using System.IO; using ImageSharp.PixelFormats; + using ImageSharp.Tests.TestUtilities.ImageComparison; + using SixLabors.Primitives; using Xunit; @@ -38,7 +40,7 @@ namespace ImageSharp.Tests.Processing.Processors.ColorMatrix image.Mutate(x => x.Lomograph(bounds)); image.DebugSave(provider); - PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + PercentageImageComparer_Old.EnsureProcessorChangesAreConstrained(source, image, bounds); } } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/PolaroidTest.cs b/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/PolaroidTest.cs index b09288fd3..06d27c0c2 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/PolaroidTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/PolaroidTest.cs @@ -6,6 +6,8 @@ namespace ImageSharp.Tests.Processing.Processors.ColorMatrix { using ImageSharp.PixelFormats; + using ImageSharp.Tests.TestUtilities.ImageComparison; + using SixLabors.Primitives; using Xunit; @@ -36,7 +38,7 @@ namespace ImageSharp.Tests.Processing.Processors.ColorMatrix image.Mutate(x => x.Polaroid(bounds)); image.DebugSave(provider); - PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + PercentageImageComparer_Old.EnsureProcessorChangesAreConstrained(source, image, bounds); } } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/SaturationTest.cs b/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/SaturationTest.cs index 20d0b8d9f..7ab55662a 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/SaturationTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/SaturationTest.cs @@ -6,6 +6,8 @@ namespace ImageSharp.Tests.Processing.Processors.ColorMatrix { using ImageSharp.PixelFormats; + using ImageSharp.Tests.TestUtilities.ImageComparison; + using SixLabors.Primitives; using Xunit; @@ -43,7 +45,7 @@ namespace ImageSharp.Tests.Processing.Processors.ColorMatrix image.Mutate(x => x.Saturation(value, bounds)); image.DebugSave(provider, value); - PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + PercentageImageComparer_Old.EnsureProcessorChangesAreConstrained(source, image, bounds); } } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/SepiaTest.cs b/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/SepiaTest.cs index 00a59f9c6..c3e9fe6dd 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/SepiaTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/SepiaTest.cs @@ -6,6 +6,8 @@ namespace ImageSharp.Tests.Processing.Processors.ColorMatrix { using ImageSharp.PixelFormats; + using ImageSharp.Tests.TestUtilities.ImageComparison; + using SixLabors.Primitives; using Xunit; @@ -36,7 +38,7 @@ namespace ImageSharp.Tests.Processing.Processors.ColorMatrix image.Mutate(x => x.Sepia(bounds)); image.DebugSave(provider); - PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + PercentageImageComparer_Old.EnsureProcessorChangesAreConstrained(source, image, bounds); } } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BoxBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BoxBlurTest.cs index 69c65e7c7..7847a6fc0 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BoxBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BoxBlurTest.cs @@ -6,6 +6,8 @@ namespace ImageSharp.Tests.Processing.Processors.Convolution { using ImageSharp.PixelFormats; + using ImageSharp.Tests.TestUtilities.ImageComparison; + using SixLabors.Primitives; using Xunit; @@ -43,7 +45,7 @@ namespace ImageSharp.Tests.Processing.Processors.Convolution image.Mutate(x => x.BoxBlur(value, bounds)); image.DebugSave(provider, value); - PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + PercentageImageComparer_Old.EnsureProcessorChangesAreConstrained(source, image, bounds); } } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs index 3d6467716..1ccecd33c 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs @@ -7,6 +7,8 @@ namespace ImageSharp.Tests.Processing.Processors.Convolution { using ImageSharp.PixelFormats; using ImageSharp.Processing; + using ImageSharp.Tests.TestUtilities.ImageComparison; + using SixLabors.Primitives; using Xunit; @@ -56,7 +58,7 @@ namespace ImageSharp.Tests.Processing.Processors.Convolution image.DebugSave(provider, grayscale: true); // TODO: We don't need this any longer after switching to ReferenceImages - PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + PercentageImageComparer_Old.EnsureProcessorChangesAreConstrained(source, image, bounds); } } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs index 93b57e7dc..3a8882ee4 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs @@ -6,6 +6,8 @@ namespace ImageSharp.Tests.Processing.Processors.Convolution { using ImageSharp.PixelFormats; + using ImageSharp.Tests.TestUtilities.ImageComparison; + using SixLabors.Primitives; using Xunit; @@ -43,7 +45,7 @@ namespace ImageSharp.Tests.Processing.Processors.Convolution image.Mutate(x => x.GaussianBlur(value, bounds)); image.DebugSave(provider, value); - PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + PercentageImageComparer_Old.EnsureProcessorChangesAreConstrained(source, image, bounds); } } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianSharpenTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianSharpenTest.cs index 4cfbd022e..61df672ce 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianSharpenTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianSharpenTest.cs @@ -6,6 +6,8 @@ namespace ImageSharp.Tests.Processing.Processors.Convolution { using ImageSharp.PixelFormats; + using ImageSharp.Tests.TestUtilities.ImageComparison; + using SixLabors.Primitives; using Xunit; @@ -43,7 +45,7 @@ namespace ImageSharp.Tests.Processing.Processors.Convolution image.Mutate(x => x.GaussianSharpen(value, bounds)); image.DebugSave(provider, value); - PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + PercentageImageComparer_Old.EnsureProcessorChangesAreConstrained(source, image, bounds); } } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Effects/AlphaTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Effects/AlphaTest.cs index 616e5f7ab..e67a6dd67 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Effects/AlphaTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Effects/AlphaTest.cs @@ -6,6 +6,8 @@ namespace ImageSharp.Tests.Processing.Processors.Effects { using ImageSharp.PixelFormats; + using ImageSharp.Tests.TestUtilities.ImageComparison; + using SixLabors.Primitives; using Xunit; @@ -43,7 +45,7 @@ namespace ImageSharp.Tests.Processing.Processors.Effects image.Mutate(x => x.Alpha(value, bounds)); image.DebugSave(provider, value); - PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + PercentageImageComparer_Old.EnsureProcessorChangesAreConstrained(source, image, bounds); } } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Effects/BackgroundColorTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Effects/BackgroundColorTest.cs index b87069b7b..29da7b625 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Effects/BackgroundColorTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Effects/BackgroundColorTest.cs @@ -6,6 +6,8 @@ namespace ImageSharp.Tests.Processing.Processors.Effects { using ImageSharp.PixelFormats; + using ImageSharp.Tests.TestUtilities.ImageComparison; + using SixLabors.Primitives; using Xunit; @@ -36,7 +38,7 @@ namespace ImageSharp.Tests.Processing.Processors.Effects image.Mutate(x => x.BackgroundColor(NamedColors.HotPink, bounds)); image.DebugSave(provider); - PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + PercentageImageComparer_Old.EnsureProcessorChangesAreConstrained(source, image, bounds); } } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Effects/BrightnessTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Effects/BrightnessTest.cs index aa7d0bd9f..7c7160f9a 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Effects/BrightnessTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Effects/BrightnessTest.cs @@ -6,6 +6,8 @@ namespace ImageSharp.Tests.Processing.Processors.Effects { using ImageSharp.PixelFormats; + using ImageSharp.Tests.TestUtilities.ImageComparison; + using SixLabors.Primitives; using Xunit; @@ -43,7 +45,7 @@ namespace ImageSharp.Tests.Processing.Processors.Effects image.Mutate(x => x.Brightness(value, bounds)); image.DebugSave(provider, value); - PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); ; + PercentageImageComparer_Old.EnsureProcessorChangesAreConstrained(source, image, bounds); ; } } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Effects/ContrastTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Effects/ContrastTest.cs index bc8fc7f79..f6f8fa5f8 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Effects/ContrastTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Effects/ContrastTest.cs @@ -6,6 +6,8 @@ namespace ImageSharp.Tests.Processing.Processors.Effects { using ImageSharp.PixelFormats; + using ImageSharp.Tests.TestUtilities.ImageComparison; + using SixLabors.Primitives; using Xunit; @@ -43,7 +45,7 @@ namespace ImageSharp.Tests.Processing.Processors.Effects image.Mutate(x => x.Contrast(value, bounds)); image.DebugSave(provider, value); - PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + PercentageImageComparer_Old.EnsureProcessorChangesAreConstrained(source, image, bounds); } } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Effects/InvertTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Effects/InvertTest.cs index aa1bcb638..6a7bd0a36 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Effects/InvertTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Effects/InvertTest.cs @@ -6,6 +6,8 @@ namespace ImageSharp.Tests.Processing.Processors.Effects { using ImageSharp.PixelFormats; + using ImageSharp.Tests.TestUtilities.ImageComparison; + using SixLabors.Primitives; using Xunit; @@ -36,7 +38,7 @@ namespace ImageSharp.Tests.Processing.Processors.Effects image.Mutate(x => x.Invert(bounds)); image.DebugSave(provider); - PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + PercentageImageComparer_Old.EnsureProcessorChangesAreConstrained(source, image, bounds); } } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Effects/OilPaintTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Effects/OilPaintTest.cs index 7f6459573..b1528e8ac 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Effects/OilPaintTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Effects/OilPaintTest.cs @@ -6,6 +6,8 @@ namespace ImageSharp.Tests.Processing.Processors.Effects { using ImageSharp.PixelFormats; + using ImageSharp.Tests.TestUtilities.ImageComparison; + using SixLabors.Primitives; using Xunit; @@ -43,7 +45,7 @@ namespace ImageSharp.Tests.Processing.Processors.Effects image.Mutate(x => x.OilPaint(levels, brushSize, bounds)); image.DebugSave(provider, string.Join("-", levels, brushSize)); - PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds, 0.001F); + PercentageImageComparer_Old.EnsureProcessorChangesAreConstrained(source, image, bounds, 0.001F); } } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Effects/PixelateTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Effects/PixelateTest.cs index 7d6931747..cbeb53823 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Effects/PixelateTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Effects/PixelateTest.cs @@ -6,6 +6,8 @@ namespace ImageSharp.Tests.Processing.Processors.Effects { using ImageSharp.PixelFormats; + using ImageSharp.Tests.TestUtilities.ImageComparison; + using SixLabors.Primitives; using Xunit; @@ -77,7 +79,7 @@ namespace ImageSharp.Tests.Processing.Processors.Effects } } - PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + PercentageImageComparer_Old.EnsureProcessorChangesAreConstrained(source, image, bounds); } } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Overlays/GlowTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Overlays/GlowTest.cs index cc9fa1095..05a287a0e 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Overlays/GlowTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Overlays/GlowTest.cs @@ -6,6 +6,8 @@ namespace ImageSharp.Tests.Processing.Processors.Overlays { using ImageSharp.PixelFormats; + using ImageSharp.Tests.TestUtilities.ImageComparison; + using SixLabors.Primitives; using Xunit; @@ -60,7 +62,7 @@ namespace ImageSharp.Tests.Processing.Processors.Overlays image.Mutate(x => x.Glow(bounds)); image.DebugSave(provider); - PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + PercentageImageComparer_Old.EnsureProcessorChangesAreConstrained(source, image, bounds); } } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Overlays/VignetteTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Overlays/VignetteTest.cs index bd8e72e43..656bf9554 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Overlays/VignetteTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Overlays/VignetteTest.cs @@ -6,6 +6,8 @@ namespace ImageSharp.Tests.Processing.Processors.Overlays { using ImageSharp.PixelFormats; + using ImageSharp.Tests.TestUtilities.ImageComparison; + using SixLabors.Primitives; using Xunit; @@ -60,7 +62,7 @@ namespace ImageSharp.Tests.Processing.Processors.Overlays image.Mutate(x => x.Vignette(bounds)); image.DebugSave(provider); - PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + PercentageImageComparer_Old.EnsureProcessorChangesAreConstrained(source, image, bounds); } } } diff --git a/tests/ImageSharp.Tests/ExactImageComparer.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ExactImageComparer.cs similarity index 61% rename from tests/ImageSharp.Tests/ExactImageComparer.cs rename to tests/ImageSharp.Tests/TestUtilities/ImageComparison/ExactImageComparer.cs index f1202e27b..d7cd39729 100644 --- a/tests/ImageSharp.Tests/ExactImageComparer.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ExactImageComparer.cs @@ -1,4 +1,4 @@ -namespace ImageSharp.Tests +namespace ImageSharp.Tests.TestUtilities.ImageComparison { using System; using System.Collections.Generic; @@ -11,11 +11,11 @@ namespace ImageSharp.Tests { public static ExactImageComparer Instance { get; } = new ExactImageComparer(); - public override void Verify(Image expected, Image actual) + public override ImageSimilarityReport CompareImagesOrFrames(ImageBase expected, ImageBase actual) { if (expected.Size() != actual.Size()) { - throw new ImageDimensionsMismatchException(expected.Size(), actual.Size()); + throw new InvalidOperationException("Calling ImageComparer is invalid when dimensions mismatch!"); } int width = actual.Width; @@ -25,7 +25,7 @@ namespace ImageSharp.Tests Rgba32[] aBuffer = new Rgba32[width]; Rgba32[] bBuffer = new Rgba32[width]; - var differences = new List(); + var differences = new List(); for (int y = 0; y < actual.Height; y++) { @@ -37,17 +37,18 @@ namespace ImageSharp.Tests for (int x = 0; x < width; x++) { - if (aBuffer[x] != bBuffer[x]) + Rgba32 aPixel = aBuffer[x]; + Rgba32 bPixel = bBuffer[x]; + + if (aPixel != bPixel) { - differences.Add(new Point(x, y)); + var diff = new PixelDifference(new Point(x, y), aPixel, bPixel); + differences.Add(diff); } } } - if (differences.Count > 0) - { - throw new ImagesAreNotEqualException(differences.ToArray()); - } + return new ImageSimilarityReport(expected, actual, differences); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparer.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparer.cs new file mode 100644 index 000000000..d88bc66ee --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparer.cs @@ -0,0 +1,80 @@ +namespace ImageSharp.Tests.TestUtilities.ImageComparison +{ + using System; + using System.Collections.Generic; + using System.Linq; + + using ImageSharp.PixelFormats; + + public abstract class ImageComparer + { + public static ImageComparer Exact { get; } = ExactImageComparer.Instance; + + public static ImageComparer Tolerant( + float imageThresholdInPercents = 0.01f, + int pixelThresholdInPixelByteSum = 0) + { + return new TolerantImageComparer(imageThresholdInPercents, pixelThresholdInPixelByteSum); + } + + public abstract ImageSimilarityReport CompareImagesOrFrames( + ImageBase expected, + ImageBase actual) + where TPixelA : struct, IPixel where TPixelB : struct, IPixel; + } + + public static class ImageComparerExtensions + { + public static IEnumerable CompareImages( + this ImageComparer comparer, + Image expected, + Image actual) + where TPixelA : struct, IPixel where TPixelB : struct, IPixel + { + var result = new List(); + ImageSimilarityReport report = comparer.CompareImagesOrFrames(expected, actual); + + if (!report.IsEmpty) + { + result.Add(report); + } + + if (expected.Frames.Count != actual.Frames.Count) + { + throw new Exception("Frame count does not match!"); + } + for (int i = 0; i < expected.Frames.Count; i++) + { + report = comparer.CompareImagesOrFrames(expected.Frames[i], actual.Frames[i]); + if (!report.IsEmpty) + { + result.Add(report); + } + } + return result; + } + + public static void VerifySimilarity( + this ImageComparer comparer, + Image expected, + Image actual) + where TPixelA : struct, IPixel where TPixelB : struct, IPixel + { + if (expected.Size() != actual.Size()) + { + throw new ImageDimensionsMismatchException(expected.Size(), actual.Size()); + } + + if (expected.Frames.Count != actual.Frames.Count) + { + throw new ImagesSimilarityException("Image frame count does not match!"); + } + + IEnumerable reports = comparer.CompareImages(expected, actual); + if (reports.Any()) + { + throw new ImagePixelsAreDifferentException(reports); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageDimensionsMismatchException.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageDimensionsMismatchException.cs new file mode 100644 index 000000000..c16e732d3 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageDimensionsMismatchException.cs @@ -0,0 +1,17 @@ +namespace ImageSharp.Tests.TestUtilities.ImageComparison +{ + using SixLabors.Primitives; + + public class ImageDimensionsMismatchException : ImagesSimilarityException + { + public ImageDimensionsMismatchException(Size expectedSize, Size actualSize) + : base((string)$"The image dimensions {actualSize} do not match the expected {expectedSize}!") + { + this.ExpectedSize = expectedSize; + this.ActualSize = actualSize; + } + + public Size ExpectedSize { get; } + public Size ActualSize { get; } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImagePixelsAreDifferentException.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImagePixelsAreDifferentException.cs new file mode 100644 index 000000000..ec74cb7c3 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImagePixelsAreDifferentException.cs @@ -0,0 +1,18 @@ +namespace ImageSharp.Tests.TestUtilities.ImageComparison +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + + public class ImagePixelsAreDifferentException : ImagesSimilarityException + { + public ImageSimilarityReport[] Reports { get; } + + public ImagePixelsAreDifferentException(IEnumerable reports) + : base("Images are not similar enough! See 'Reports' for more details! ") + { + this.Reports = reports.ToArray(); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageSimilarityReport.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageSimilarityReport.cs new file mode 100644 index 000000000..7c63b0e55 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageSimilarityReport.cs @@ -0,0 +1,50 @@ +namespace ImageSharp.Tests.TestUtilities.ImageComparison +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + + public class ImageSimilarityReport + { + public ImageSimilarityReport(IImageBase expectedImage, IImageBase actualImage, IEnumerable differences) + { + this.ExpectedImage = expectedImage; + this.ActualImage = actualImage; + this.Differences = differences.ToArray(); + } + + public IImageBase ExpectedImage { get; } + + public IImageBase ActualImage { get; } + + public PixelDifference[] Differences { get; } + + public bool IsEmpty => this.Differences.Length == 0; + + public override string ToString() + { + return this.IsEmpty ? "[SimilarImages]" : StringifyDifferences(this.Differences); + } + + private static string StringifyDifferences(PixelDifference[] differences) + { + var sb = new StringBuilder(); + int max = Math.Min(5, differences.Length); + + for (int i = 0; i < max; i++) + { + sb.Append(differences[i]); + if (i < max - 1) + { + sb.Append("; "); + } + } + if (differences.Length >= 5) + { + sb.Append("..."); + } + return sb.ToString(); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImagesSimilarityException.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImagesSimilarityException.cs new file mode 100644 index 000000000..ce57bcc35 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImagesSimilarityException.cs @@ -0,0 +1,12 @@ +namespace ImageSharp.Tests.TestUtilities.ImageComparison +{ + using System; + + public class ImagesSimilarityException : Exception + { + public ImagesSimilarityException(string message) + : base(message) + { + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/PercentageImageComparer.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/PercentageImageComparer_Old.cs similarity index 91% rename from tests/ImageSharp.Tests/PercentageImageComparer.cs rename to tests/ImageSharp.Tests/TestUtilities/ImageComparison/PercentageImageComparer_Old.cs index e94ce8d68..85a78f804 100644 --- a/tests/ImageSharp.Tests/PercentageImageComparer.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/PercentageImageComparer_Old.cs @@ -3,19 +3,19 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Tests +namespace ImageSharp.Tests.TestUtilities.ImageComparison { using System; - using ImageSharp; + using ImageSharp.Memory; using ImageSharp.PixelFormats; + using SixLabors.Primitives; - using Xunit; /// /// Class to perform simple image comparisons. /// - public class PercentageImageComparer : ImageComparer + public class PercentageImageComparer_Old : ImageComparer { public float ImageThreshold { get; } @@ -24,7 +24,7 @@ namespace ImageSharp.Tests public int ScaleIntoSize { get; } - public PercentageImageComparer( + public PercentageImageComparer_Old( float imageThreshold = DefaultImageThreshold, byte segmentThreshold = DefaultSegmentThreshold, int scaleIntoSize = DefaultScaleIntoSize) @@ -114,8 +114,8 @@ namespace ImageSharp.Tests int scaleIntoSize = DefaultScaleIntoSize) where TPixelA : struct, IPixel where TPixelB : struct, IPixel { - var comparer = new PercentageImageComparer(imageTheshold, segmentThreshold, scaleIntoSize); - comparer.Verify(expected, actual); + var comparer = new PercentageImageComparer_Old(imageTheshold, segmentThreshold, scaleIntoSize); + comparer.CompareImages(expected, actual); } /// @@ -134,7 +134,11 @@ namespace ImageSharp.Tests /// The default undefined value is /// /// Returns a number from 0 - 1 which represents the difference focter between the images. - public static float PercentageDifference(Image source, Image target, byte segmentThreshold = DefaultSegmentThreshold, int scalingFactor = DefaultScaleIntoSize) + public static float PercentageDifference( + Image source, + Image target, + byte segmentThreshold = DefaultSegmentThreshold, + int scalingFactor = DefaultScaleIntoSize) where TPixelA : struct, IPixel where TPixelB : struct, IPixel { @@ -194,7 +198,14 @@ namespace ImageSharp.Tests } } - public override void Verify(Image expected, Image actual) + public override ImageSimilarityReport CompareImagesOrFrames(ImageBase expected, ImageBase actual) + { + throw new NotImplementedException(); + } + + public void CompareImages(Image expected, Image actual) + where TPixelA : struct, IPixel + where TPixelB : struct, IPixel { if (expected.Size() != actual.Size()) { diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/PixelDifference.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/PixelDifference.cs new file mode 100644 index 000000000..bb2a02192 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/PixelDifference.cs @@ -0,0 +1,40 @@ +namespace ImageSharp.Tests.TestUtilities.ImageComparison +{ + using SixLabors.Primitives; + + public struct PixelDifference + { + public PixelDifference( + Point position, + int redDifference, + int greenDifference, + int blueDifference, + int alphaDifference) + { + this.Position = position; + this.RedDifference = redDifference; + this.GreenDifference = greenDifference; + this.BlueDifference = blueDifference; + this.AlphaDifference = alphaDifference; + } + + public PixelDifference(Point position, Rgba32 expected, Rgba32 actual) + : this(position, + (int)actual.R - (int)expected.R, + (int)actual.G - (int)expected.G, + (int)actual.B - (int)expected.B, + (int)actual.A - (int)expected.A) + { + } + + public Point Position { get; } + + public int RedDifference { get; } + public int GreenDifference { get; } + public int BlueDifference { get; } + public int AlphaDifference { get; } + + public override string ToString() => + $"[Δ({this.RedDifference},{this.GreenDifference},{this.BlueDifference},{this.AlphaDifference}) @ ({this.Position.X},{this.Position.Y})]"; + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/TolerantImageComparer.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/TolerantImageComparer.cs new file mode 100644 index 000000000..64ec92d97 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/TolerantImageComparer.cs @@ -0,0 +1,40 @@ +namespace ImageSharp.Tests.TestUtilities.ImageComparison +{ + using System; + + public class TolerantImageComparer : ImageComparer + { + public const float DefaultImageThreshold = 1.0f / (100 * 100 * 255); + + public TolerantImageComparer(float imageThreshold, int pixelThresholdInPixelByteSum = 0) + { + this.ImageThreshold = imageThreshold; + this.PixelThresholdInPixelByteSum = pixelThresholdInPixelByteSum; + } + + /// + /// The maximal tolerated difference represented by a value between 0.0 and 1.0. + /// Examples of percentage differences on a single pixel: + /// 1. PixelA = (255,255,255,0) PixelB =(0,0,0,255) leads to 100% difference on a single pixel + /// 2. PixelA = (255,255,255,0) PixelB =(255,255,255,255) leads to 25% difference on a single pixel + /// 3. PixelA = (255,255,255,0) PixelB =(128,128,128,128) leads to 50% difference on a single pixel + /// + /// The total differences is the sum of all pixel differences normalized by image dimensions! + /// + /// ImageThresholdInPercents = 1.0/255 means that we allow one byte difference per channel on a 1x1 image + /// ImageThresholdInPercents = 1.0/(100*100*255) means that we allow only one byte difference per channel on a 100x100 image + /// + public float ImageThreshold { get; } + + /// + /// The threshold of the individual pixels before they acumulate towards the overall difference. + /// For an individual pixel the value it's calculated as: pixel.R + pixel.G + pixel.B + pixel.A + /// + public int PixelThresholdInPixelByteSum { get; } + + public override ImageSimilarityReport CompareImagesOrFrames(ImageBase expected, ImageBase actual) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index bd7d7f0c6..fe70e6cc1 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -12,6 +12,7 @@ namespace ImageSharp.Tests using System.Reflection; using ImageSharp.PixelFormats; + using ImageSharp.Tests.TestUtilities.ImageComparison; using ImageSharp.Tests.TestUtilities.ReferenceCodecs; public static class TestImageExtensions @@ -57,28 +58,14 @@ namespace ImageSharp.Tests /// Details to be concatenated to the test output file, describing the parameters of the test. /// The extension /// A boolean indicating whether we should debug save + compare against a grayscale image, smaller in size. - /// - /// The threshold for the percentage difference where the images are asumed to be the same. - /// The default/undefined value is - /// - /// - /// The threshold of the individual segments before it acumulates towards the overall difference. - /// The default undefined value is - /// - /// - /// This is a sampling factor we sample a grid of average pixels width by high - /// The default undefined value is - /// + /// A custom if exact equlity is not the expected behaviour /// public static Image CompareToReferenceOutput( this Image image, ITestImageProvider provider, object testOutputDetails = null, string extension = "png", - bool grayscale = false, - float imageTheshold = PercentageImageComparer.DefaultImageThreshold, - byte segmentThreshold = PercentageImageComparer.DefaultSegmentThreshold, - int scalingFactor = PercentageImageComparer.DefaultScaleIntoSize) + bool grayscale = false) where TPixel : struct, IPixel { string referenceOutputFile = provider.Utility.GetReferenceOutputFileName(extension, testOutputDetails); @@ -99,7 +86,8 @@ namespace ImageSharp.Tests using (Image referenceImage = Image.Load(referenceOutputFile, ReferenceDecoder.Instance)) { - PercentageImageComparer.VerifySimilarity(referenceImage, image, imageTheshold, segmentThreshold, scalingFactor); + ImageComparer comparer = ImageComparer.Exact; + comparer.CompareImages(referenceImage, image); } return image; diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/ImageComparerTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/ImageComparerTests.cs index db58e9ba0..e61ef822a 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/ImageComparerTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/ImageComparerTests.cs @@ -1,7 +1,13 @@ // ReSharper disable InconsistentNaming namespace ImageSharp.Tests { + using System.Collections.Generic; + using System.Linq; + using ImageSharp.PixelFormats; + using ImageSharp.Tests.TestUtilities.ImageComparison; + + using Moq; using SixLabors.Primitives; @@ -18,49 +24,91 @@ namespace ImageSharp.Tests private ITestOutputHelper Output { get; } [Theory] - [WithTestPatternImages( - 100, - 100, - PixelTypes.Rgba32, - PercentageImageComparer.DefaultImageThreshold, - PercentageImageComparer.DefaultSegmentThreshold, - PercentageImageComparer.DefaultScaleIntoSize)] - [WithTestPatternImages(100, 100, PixelTypes.Rgba32, 0, 0, 100)] - public void PercentageComparer_ApprovesPerfectSimilarity( + [WithTestPatternImages(100,100,PixelTypes.Rgba32, 0.0001f, 1)] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32, 0, 0)] + public void TolerantImageComparer_ApprovesPerfectSimilarity( TestImageProvider provider, float imageTheshold, - byte segmentThreshold, - int scaleIntoSize) + int pixelThreshold) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) { using (Image clone = image.Clone()) { - PercentageImageComparer.VerifySimilarity( - image, - clone, - imageTheshold, - segmentThreshold, - scaleIntoSize); + var comparer = ImageComparer.Tolerant(imageTheshold, pixelThreshold); + comparer.VerifySimilarity(image, clone); } } } - private static void ModifyPixel(Image img, int x, int y, byte value) + private static void ModifyPixel(ImageBase img, int x, int y, byte perChannelChange) where TPixel : struct, IPixel { TPixel pixel = img[x, y]; var rgbaPixel = default(Rgba32); pixel.ToRgba32(ref rgbaPixel); - rgbaPixel.R += value; + + if (rgbaPixel.R + perChannelChange <= 255) + { + rgbaPixel.R += perChannelChange; + } + else + { + rgbaPixel.R -= perChannelChange; + } + + if (rgbaPixel.G + perChannelChange <= 255) + { + rgbaPixel.G += perChannelChange; + } + else + { + rgbaPixel.G -= perChannelChange; + } + + if (rgbaPixel.B + perChannelChange <= 255) + { + rgbaPixel.B += perChannelChange; + } + else + { + rgbaPixel.B -= perChannelChange; + } + + if (rgbaPixel.A + perChannelChange <= 255) + { + rgbaPixel.A += perChannelChange; + } + else + { + rgbaPixel.A -= perChannelChange; + } + pixel.PackFromRgba32(rgbaPixel); img[x, y] = pixel; } [Theory] [WithTestPatternImages(100, 100, PixelTypes.Rgba32)] - public void PercentageComparer_ApprovesImperfectSimilarity(TestImageProvider provider) + public void TolerantImageComparer_ApprovesSimilarityBelowTolerance(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + using (Image clone = image.Clone()) + { + ModifyPixel(clone, 0, 0, 1); + + var comparer = ImageComparer.Tolerant(); + comparer.VerifySimilarity(image, clone); + } + } + } + + [Theory] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32)] + public void TolerantImageComparer_DoesNotApproveSimilarityAboveTolerance(TestImageProvider provider) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) @@ -69,7 +117,27 @@ namespace ImageSharp.Tests { ModifyPixel(clone, 0, 0, 2); - PercentageImageComparer.VerifySimilarity(image, clone, scaleIntoSize: 100); + var comparer = ImageComparer.Tolerant(); + comparer.VerifySimilarity(image, clone); + } + } + } + + [Theory] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32)] + public void TolerantImageComparer_TestPerPixelThreshold(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + using (Image clone = image.Clone()) + { + ModifyPixel(clone, 0, 0, 10); + ModifyPixel(clone, 1, 0, 10); + ModifyPixel(clone, 2, 0, 10); + + var comparer = ImageComparer.Tolerant(pixelThresholdInPixelByteSum: 42); + comparer.VerifySimilarity(image, clone); } } } @@ -77,7 +145,7 @@ namespace ImageSharp.Tests [Theory] [WithTestPatternImages(100, 100, PixelTypes.Rgba32, 99, 100)] [WithTestPatternImages(100, 100, PixelTypes.Rgba32, 100, 99)] - public void PercentageComparer_ThrowsOnSizeMismatch(TestImageProvider provider, int w, int h) + public void VerifySimilarity_ThrowsOnSizeMismatch(TestImageProvider provider, int w, int h) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) @@ -87,32 +155,35 @@ namespace ImageSharp.Tests ImageDimensionsMismatchException ex = Assert.ThrowsAny( () => { - PercentageImageComparer.VerifySimilarity(image, clone); + ImageComparer comparer = Mock.Of(); + comparer.VerifySimilarity(image, clone); }); this.Output.WriteLine(ex.Message); } } } + [Theory] - [WithTestPatternImages(100, 100, PixelTypes.Rgba32)] - public void PercentageComparer_WhenDifferenceIsTooLarge_Throws(TestImageProvider provider) + [WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32)] + public void VerifySimilarity_WhenAnImageFrameIsDifferent_Reports(TestImageProvider provider) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) { using (Image clone = image.Clone()) { - ModifyPixel(clone, 0, 0, 42); - ModifyPixel(clone, 1, 0, 42); - ModifyPixel(clone, 2, 0, 42); + ModifyPixel(clone.Frames[0], 42, 43, 1); + + IEnumerable reports = ImageComparer.Exact.CompareImages(image, clone); - Assert.ThrowsAny( - () => { PercentageImageComparer.VerifySimilarity(image, clone, scaleIntoSize: 100); }); + PixelDifference difference = reports.Single().Differences.Single(); + Assert.Equal(new Point(42, 43), difference.Position); } } } + [Theory] [WithTestPatternImages(100, 100, PixelTypes.Rgba32)] public void ExactComparer_ApprovesExactEquality(TestImageProvider provider) @@ -122,34 +193,14 @@ namespace ImageSharp.Tests { using (Image clone = image.Clone()) { - ExactImageComparer.Instance.Verify(image, clone); + ExactImageComparer.Instance.CompareImages(image, clone); } } } - - [Theory] - [WithTestPatternImages(100, 100, PixelTypes.Rgba32, 99, 100)] - [WithTestPatternImages(100, 100, PixelTypes.Rgba32, 100, 99)] - public void ExactComparer_ThrowsOnSizeMismatch(TestImageProvider provider, int w, int h) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - using (Image clone = image.Clone(ctx => ctx.Resize(w, h))) - { - ImageDimensionsMismatchException ex = Assert.ThrowsAny( - () => - { - ExactImageComparer.Instance.Verify(image, clone); - }); - this.Output.WriteLine(ex.Message); - } - } - } - + [Theory] [WithTestPatternImages(100, 100, PixelTypes.Rgba32)] - public void ExactComparer_ThrowsOnSmallestPixelDifference(TestImageProvider provider) + public void ExactComparer_DoesNotTolerateAnyPixelDifference(TestImageProvider provider) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) @@ -159,15 +210,13 @@ namespace ImageSharp.Tests ModifyPixel(clone, 42, 24, 1); ModifyPixel(clone, 7, 93, 1); - ImagesAreNotEqualException ex = Assert.ThrowsAny( - () => - { - ExactImageComparer.Instance.Verify(image, clone); - }); - this.Output.WriteLine(ex.Message); - Assert.Equal(2, ex.Differences.Length); - Assert.Contains(new Point(42, 24), ex.Differences); - Assert.Contains(new Point(7, 93), ex.Differences); + IEnumerable reports = ExactImageComparer.Instance.CompareImages(image, clone); + + this.Output.WriteLine(reports.Single().ToString()); + PixelDifference[] differences = reports.Single().Differences; + Assert.Equal(2, differences.Length); + Assert.Contains(differences, d => d.Position == new Point(42, 24)); + Assert.Contains(differences, d => d.Position == new Point(7, 93)); } } }