// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; using SixLabors.ImageSharp.PixelFormats; using Xunit; using Xunit.Abstractions; using System.Collections.Concurrent; using System.IO; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Tests { using SixLabors.Memory; public class TestImageProviderTests { public TestImageProviderTests(ITestOutputHelper output) { this.Output = output; } private ITestOutputHelper Output { get; } [Theory] [WithBlankImages(1, 1, PixelTypes.Rgba32)] public void NoOutputSubfolderIsPresentByDefault(TestImageProvider provider) where TPixel : struct, IPixel { Assert.Empty(provider.Utility.OutputSubfolderName); } [Theory] [WithBlankImages(42, 666, PixelTypes.Rgba32 | PixelTypes.Argb32 | PixelTypes.HalfSingle, "hello")] public void Use_WithEmptyImageAttribute(TestImageProvider provider, string message) where TPixel : struct, IPixel { Image img = provider.GetImage(); Assert.Equal(42, img.Width); Assert.Equal(666, img.Height); Assert.Equal("hello", message); } [Theory] [WithBlankImages(42, 666, PixelTypes.All, "hello")] public void Use_WithBlankImagesAttribute_WithAllPixelTypes( TestImageProvider provider, string message) where TPixel : struct, IPixel { Image img = provider.GetImage(); Assert.Equal(42, img.Width); Assert.Equal(666, img.Height); Assert.Equal("hello", message); } [Theory] [WithBlankImages(1, 1, PixelTypes.Rgba32, PixelTypes.Rgba32)] [WithBlankImages(1, 1, PixelTypes.Alpha8, PixelTypes.Alpha8)] [WithBlankImages(1, 1, PixelTypes.Argb32, PixelTypes.Argb32)] public void PixelType_PropertyValueIsCorrect(TestImageProvider provider, PixelTypes expected) where TPixel : struct, IPixel { Assert.Equal(expected, provider.PixelType); } [Theory] [WithFile(TestImages.Bmp.Car, PixelTypes.All, 88)] [WithFile(TestImages.Bmp.F, PixelTypes.All, 88)] public void Use_WithFileAttribute(TestImageProvider provider, int yo) where TPixel : struct, IPixel { Assert.NotNull(provider.Utility.SourceFileOrDescription); Image img = provider.GetImage(); Assert.True(img.Width * img.Height > 0); Assert.Equal(88, yo); string fn = provider.Utility.GetTestOutputFileName("jpg"); this.Output.WriteLine(fn); } private class TestDecoder : IImageDecoder { public Image Decode(Configuration configuration, Stream stream) where TPixel : struct, IPixel { invocationCounts[this.callerName]++; return new Image(42, 42); } // Couldn't make xUnit happy without this hackery: private static ConcurrentDictionary invocationCounts = new ConcurrentDictionary(); private string callerName = null; internal void InitCaller(string name) { this.callerName = name; invocationCounts[name] = 0; } internal static int GetInvocationCount(string callerName) => invocationCounts[callerName]; private static readonly object Monitor = new object(); public static void DoTestThreadSafe(Action action) { lock (Monitor) { action(); } } } [Theory] [WithFile(TestImages.Bmp.F, PixelTypes.Rgba32)] public void GetImage_WithCustomParameterlessDecoder_ShouldUtilizeCache(TestImageProvider provider) where TPixel : struct, IPixel { if (!TestEnvironment.Is64BitProcess) { // We don't cache with the 32 bit build. return; } Assert.NotNull(provider.Utility.SourceFileOrDescription); TestDecoder.DoTestThreadSafe(() => { string testName = nameof(this.GetImage_WithCustomParameterlessDecoder_ShouldUtilizeCache); var decoder = new TestDecoder(); decoder.InitCaller(testName); provider.GetImage(decoder); Assert.Equal(1, TestDecoder.GetInvocationCount(testName)); provider.GetImage(decoder); Assert.Equal(1, TestDecoder.GetInvocationCount(testName)); }); } private class TestDecoderWithParameters : IImageDecoder { public string Param1 { get; set; } public int Param2 { get; set; } public Image Decode(Configuration configuration, Stream stream) where TPixel : struct, IPixel { invocationCounts[this.callerName]++; return new Image(42, 42); } private static ConcurrentDictionary invocationCounts = new ConcurrentDictionary(); private string callerName = null; internal void InitCaller(string name) { this.callerName = name; invocationCounts[name] = 0; } internal static int GetInvocationCount(string callerName) => invocationCounts[callerName]; private static readonly object Monitor = new object(); public static void DoTestThreadSafe(Action action) { lock (Monitor) { action(); } } } [Theory] [WithFile(TestImages.Bmp.F, PixelTypes.Rgba32)] public void GetImage_WithCustomParametricDecoder_ShouldUtilizeCache_WhenParametersAreEqual(TestImageProvider provider) where TPixel : struct, IPixel { if (!TestEnvironment.Is64BitProcess) { // We don't cache with the 32 bit build. return; } Assert.NotNull(provider.Utility.SourceFileOrDescription); TestDecoderWithParameters.DoTestThreadSafe(() => { string testName = nameof(this.GetImage_WithCustomParametricDecoder_ShouldUtilizeCache_WhenParametersAreEqual); var decoder1 = new TestDecoderWithParameters() { Param1 = "Lol", Param2 = 666 }; decoder1.InitCaller(testName); var decoder2 = new TestDecoderWithParameters() { Param1 = "Lol", Param2 = 666 }; decoder2.InitCaller(testName); provider.GetImage(decoder1); Assert.Equal(1, TestDecoderWithParameters.GetInvocationCount(testName)); provider.GetImage(decoder2); Assert.Equal(1, TestDecoderWithParameters.GetInvocationCount(testName)); }); } [Theory] [WithFile(TestImages.Bmp.F, PixelTypes.Rgba32)] public void GetImage_WithCustomParametricDecoder_ShouldNotUtilizeCache_WhenParametersAreNotEqual(TestImageProvider provider) where TPixel : struct, IPixel { Assert.NotNull(provider.Utility.SourceFileOrDescription); TestDecoderWithParameters.DoTestThreadSafe(() => { string testName = nameof(this.GetImage_WithCustomParametricDecoder_ShouldNotUtilizeCache_WhenParametersAreNotEqual); var decoder1 = new TestDecoderWithParameters() { Param1 = "Lol", Param2 = 42 }; decoder1.InitCaller(testName); var decoder2 = new TestDecoderWithParameters() { Param1 = "LoL", Param2 = 42 }; decoder2.InitCaller(testName); provider.GetImage(decoder1); Assert.Equal(1, TestDecoderWithParameters.GetInvocationCount(testName)); provider.GetImage(decoder2); Assert.Equal(2, TestDecoderWithParameters.GetInvocationCount(testName)); }); } public static string[] AllBmpFiles = { TestImages.Bmp.F, TestImages.Bmp.Bit8 }; [Theory] [WithFileCollection(nameof(AllBmpFiles), PixelTypes.Rgba32 | PixelTypes.Argb32)] public void Use_WithFileCollection(TestImageProvider provider) where TPixel : struct, IPixel { Assert.NotNull(provider.Utility.SourceFileOrDescription); using (Image image = provider.GetImage()) { provider.Utility.SaveTestOutputFile(image, "png"); } } [Theory] [WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32)] public void SaveTestOutputFileMultiFrame(TestImageProvider provider) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) { string[] files = provider.Utility.SaveTestOutputFileMultiFrame(image); Assert.True(files.Length > 2); foreach (string path in files) { this.Output.WriteLine(path); Assert.True(File.Exists(path)); } } } [Theory] [WithSolidFilledImages(10, 20, 255, 100, 50, 200, PixelTypes.Rgba32 | PixelTypes.Argb32)] public void Use_WithSolidFilledImagesAttribute(TestImageProvider provider) where TPixel : struct, IPixel { Image img = provider.GetImage(); Assert.Equal(10, img.Width); Assert.Equal(20, img.Height); var rgba = default(Rgba32); Buffer2D pixels = img.GetRootFramePixelBuffer(); for (int y = 0; y < pixels.Height; y++) { for (int x = 0; x < pixels.Width; x++) { pixels[x, y].ToRgba32(ref rgba); Assert.Equal(255, rgba.R); Assert.Equal(100, rgba.G); Assert.Equal(50, rgba.B); Assert.Equal(200, rgba.A); } } } /// /// Need to us to create instance of when pixelType is StandardImageClass /// /// /// /// public static Image CreateTestImage() where TPixel : struct, IPixel { return new Image(3, 3); } [Theory] [WithMemberFactory(nameof(CreateTestImage), PixelTypes.All)] public void Use_WithMemberFactoryAttribute(TestImageProvider provider) where TPixel : struct, IPixel { Image img = provider.GetImage(); Assert.Equal(3, img.Width); if (provider.PixelType == PixelTypes.Rgba32) { Assert.IsType>(img); } } public static readonly TheoryData BasicData = new TheoryData() { TestImageProvider.Blank(10, 20), TestImageProvider.Blank( 10, 20), }; [Theory] [MemberData(nameof(BasicData))] public void Blank_MemberData(TestImageProvider provider) where TPixel : struct, IPixel { Image img = provider.GetImage(); Assert.True(img.Width * img.Height > 0); } public static readonly TheoryData FileData = new TheoryData() { TestImageProvider.File(TestImages.Bmp.Car), TestImageProvider.File(TestImages.Bmp.F) }; [Theory] [MemberData(nameof(FileData))] public void File_MemberData(TestImageProvider provider) where TPixel : struct, IPixel { this.Output.WriteLine("SRC: " + provider.Utility.SourceFileOrDescription); this.Output.WriteLine("OUT: " + provider.Utility.GetTestOutputFileName()); Image img = provider.GetImage(); Assert.True(img.Width * img.Height > 0); } } }