Browse Source

Merge remote-tracking branch 'remotes/pullrequest/test-utils-fixed'

af/merge-core
Dirk Lemstra 9 years ago
parent
commit
f478b962a4
  1. 11
      src/ImageSharp/Image/PixelArea{TColor}.cs
  2. 5
      tests/ImageSharp.Tests/FileTestBase.cs
  3. 71
      tests/ImageSharp.Tests/Formats/Jpg/JpegTests.cs
  4. 109
      tests/ImageSharp.Tests/Image/PixelAccessorTests.cs
  5. 3
      tests/ImageSharp.Tests/TestFile.cs
  6. 242
      tests/ImageSharp.Tests/TestUtilities/FlagsHelper.cs
  7. 34
      tests/ImageSharp.Tests/TestUtilities/GenericFactory.cs
  8. 75
      tests/ImageSharp.Tests/TestUtilities/ImageDataAttributeBase.cs
  9. 115
      tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs
  10. 62
      tests/ImageSharp.Tests/TestUtilities/PixelTypes.cs
  11. 192
      tests/ImageSharp.Tests/TestUtilities/TestImageProvider.cs
  12. 133
      tests/ImageSharp.Tests/TestUtilities/TestUtilityExtensions.cs
  13. 193
      tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs
  14. 147
      tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs
  15. 38
      tests/ImageSharp.Tests/TestUtilities/WithBlankImageAttribute.cs
  16. 36
      tests/ImageSharp.Tests/TestUtilities/WithFileAttribute.cs
  17. 86
      tests/ImageSharp.Tests/TestUtilities/WithFileCollectionAttribute.cs
  18. 54
      tests/ImageSharp.Tests/TestUtilities/WithMemberFactoryAttribute.cs
  19. 93
      tests/ImageSharp.Tests/TestUtilities/WithSolidFilledImagesAttribute.cs

11
src/ImageSharp/Image/PixelArea{TColor}.cs

@ -79,6 +79,17 @@ namespace ImageSharp
this.PixelBase = (byte*)this.dataPointer.ToPointer();
}
/// <summary>
/// Initializes a new instance of the <see cref="PixelArea{TColor}"/> class.
/// </summary>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <param name="componentOrder">The component order.</param>
public PixelArea(int width, int height, ComponentOrder componentOrder)
: this(width, height, componentOrder, 0)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="PixelArea{TColor}"/> class.
/// </summary>

5
tests/ImageSharp.Tests/FileTestBase.cs

@ -46,6 +46,9 @@ namespace ImageSharp.Tests
// TestFile.Create(TestImages.Gif.Giphy) // Perf: Enable for local testing only
};
// TODO: Find a better place for this
internal const string TestOutputRoot = "TestOutput/";
protected string CreateOutputDirectory(string path, params string[] pathParts)
{
var postFix = "";
@ -54,7 +57,7 @@ namespace ImageSharp.Tests
postFix = "/" + string.Join("/", pathParts);
}
path = "TestOutput/" + path + postFix;
path = TestOutputRoot + path + postFix;
if (!Directory.Exists(path))
{

71
tests/ImageSharp.Tests/Formats/Jpg/JpegTests.cs

@ -6,7 +6,7 @@ using ImageSharp.Formats;
using Xunit;
using Xunit.Abstractions;
namespace ImageSharp.Tests.Formats.Jpg
namespace ImageSharp.Tests
{
public class JpegTests
{
@ -17,62 +17,45 @@ namespace ImageSharp.Tests.Formats.Jpg
public JpegTests(ITestOutputHelper output)
{
Output = output;
this.Output = output;
}
public static IEnumerable<string> AllJpegFiles => TestImages.Jpeg.All;
protected string CreateTestOutputFile(string fileName)
{
if (!Directory.Exists(TestOutputDirectory))
{
Directory.CreateDirectory(TestOutputDirectory);
}
//string id = Guid.NewGuid().ToString().Substring(0, 4);
string ext = Path.GetExtension(fileName);
fileName = Path.GetFileNameWithoutExtension(fileName);
return $"{TestOutputDirectory}/{fileName}{ext}";
}
protected Stream CreateOutputStream(string fileName)
{
fileName = CreateTestOutputFile(fileName);
Output?.WriteLine("Opened for write: "+fileName);
return File.OpenWrite(fileName);
}
public static IEnumerable<object[]> AllJpegFiles
=> TestImages.Jpeg.All.Select(file => new object[] {TestFile.Create(file)});
// TODO: Turned off PixelTypes.All to be CI-friendly, what should be the practice?
[Theory]
[MemberData(nameof(AllJpegFiles))]
public void OpenJpeg_SaveBmp(TestFile file)
//[WithFileCollection(nameof(AllJpegFiles), PixelTypes.All)]
[WithFileCollection(nameof(AllJpegFiles), PixelTypes.Color | PixelTypes.StandardImageClass | PixelTypes.Argb)]
public void OpenJpeg_SaveBmp<TColor>(TestImageProvider<TColor> provider)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
string bmpFileName = file.FileNameWithoutExtension + ".bmp";
var image = provider.GetImage();
var image = file.CreateImage();
using (var outputStream = CreateOutputStream(bmpFileName))
{
image.Save(outputStream, new BmpFormat());
}
provider.Utility.SaveTestOutputFile(image, "bmp");
}
public static IEnumerable<object[]> AllBmpFiles
=> TestImages.Bmp.All.Select(file => new object[] { TestFile.Create(file) });
public static IEnumerable<string> AllBmpFiles => TestImages.Bmp.All;
[Theory]
[MemberData(nameof(AllBmpFiles))]
public void OpenBmp_SaveJpeg(TestFile file)
[WithFileCollection(nameof(AllBmpFiles), PixelTypes.Color | PixelTypes.StandardImageClass | PixelTypes.Argb, JpegSubsample.Ratio420, 75)]
[WithFileCollection(nameof(AllBmpFiles), PixelTypes.Color | PixelTypes.StandardImageClass | PixelTypes.Argb, JpegSubsample.Ratio444, 75)]
public void OpenBmp_SaveJpeg<TColor>(TestImageProvider<TColor> provider, JpegSubsample subSample, int quality)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
string jpegPath = file.FileNameWithoutExtension + ".jpeg";
var image = provider.GetImage();
var image = file.CreateImage();
var utility = provider.Utility;
utility.TestName += "_" + subSample + "_Q" + quality;
using (var outputStream = CreateOutputStream(jpegPath))
using (var outputStream = File.OpenWrite(utility.GetTestOutputFileName("jpg")))
{
image.Save(outputStream, new JpegFormat());
var encoder = new JpegEncoder()
{
Subsample = subSample,
Quality = quality
};
image.Save(outputStream, encoder);
}
}
}

109
tests/ImageSharp.Tests/Image/PixelAccessorTests.cs

@ -6,6 +6,7 @@
namespace ImageSharp.Tests
{
using System;
using System.Numerics;
using Xunit;
@ -14,6 +15,114 @@ namespace ImageSharp.Tests
/// </summary>
public class PixelAccessorTests
{
public static Image<TColor> CreateTestImage<TColor>(GenericFactory<TColor> factory)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
Image<TColor> image = factory.CreateImage(10, 10);
using (var pixels = image.Lock())
{
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
Vector4 v = new Vector4(i, j, 0, 1);
v /= 10;
TColor color = default(TColor);
color.PackFromVector4(v);
pixels[i, j] = color;
}
}
}
return image;
}
[Theory]
[WithMemberFactory(nameof(CreateTestImage), PixelTypes.All, ComponentOrder.XYZ)]
[WithMemberFactory(nameof(CreateTestImage), PixelTypes.All, ComponentOrder.ZYX)]
[WithMemberFactory(nameof(CreateTestImage), PixelTypes.All, ComponentOrder.XYZW)]
[WithMemberFactory(nameof(CreateTestImage), PixelTypes.All, ComponentOrder.ZYXW)]
public void CopyTo_Then_CopyFrom_OnFullImageRect<TColor>(TestImageProvider<TColor> provider, ComponentOrder order)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
var src = provider.GetImage();
var dest = new Image<TColor>(src.Width, src.Height);
using (PixelArea<TColor> area = new PixelArea<TColor>(src.Width, src.Height, order))
{
using (var srcPixels = src.Lock())
{
srcPixels.CopyTo(area, 0, 0);
}
using (var destPixels = dest.Lock())
{
destPixels.CopyFrom(area, 0, 0);
}
}
Assert.True(src.IsEquivalentTo(dest, false));
}
// TODO: Need a processor in the library with this signature
private static void Fill<TColor>(Image<TColor> image, Rectangle region, TColor color)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
using (var pixels = image.Lock())
{
for (int y = region.Top; y < region.Bottom; y++)
{
for (int x = region.Left; x < region.Right; x++)
{
pixels[x, y] = color;
}
}
}
}
[Theory]
[WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.XYZ)]
[WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.ZYX)]
[WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.XYZW)]
[WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.ZYXW)]
public void CopyTo_Then_CopyFrom_WithOffset<TColor>(TestImageProvider<TColor> provider, ComponentOrder order)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
var srcImage = provider.GetImage();
var color = default(TColor);
color.PackFromBytes(255, 0, 0, 255);
Fill(srcImage, new Rectangle(4, 4, 8, 8), color);
var destImage = new Image<TColor>(8, 8);
using (var srcPixels = srcImage.Lock())
{
using (var area = new PixelArea<TColor>(8, 8, order))
{
srcPixels.CopyTo(area, 4, 4);
using (var destPixels = destImage.Lock())
{
destPixels.CopyFrom(area, 0, 0);
}
}
}
provider.Utility.SourceFileOrDescription = order.ToString();
provider.Utility.SaveTestOutputFile(destImage, "bmp");
var expectedImage = new Image<TColor>(8, 8).Fill(color);
Assert.True(destImage.IsEquivalentTo(expectedImage));
}
[Fact]
public void CopyFromZYX()
{

3
tests/ImageSharp.Tests/TestFile.cs

@ -4,6 +4,7 @@
// </copyright>
namespace ImageSharp.Tests
{
using System;
using System.Collections.Concurrent;
using System.IO;
@ -33,7 +34,7 @@ namespace ImageSharp.Tests
this.Bytes = File.ReadAllBytes(file);
this.image = new Image(this.Bytes);
}
public static TestFile Create(string file)
{
return cache.GetOrAdd(file, (string fileName) =>

242
tests/ImageSharp.Tests/TestUtilities/FlagsHelper.cs

@ -0,0 +1,242 @@
// <copyright file="FlagsHelper.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Tests
{
using System;
using System.Collections.Generic;
using System.Text;
/// <summary>
/// Helper class for flag manipulation, based on
/// <see>
/// <cref>http://www.codeproject.com/KB/dotnet/enum.aspx</cref>
/// </see>
/// </summary>
/// <typeparam name="T">Must be enum type (declared using <c>enum</c> keyword)</typeparam>
public class FlagsHelper<T>
where T : struct, IConvertible
{
private static readonly EnumConverter Converter;
static FlagsHelper()
{
Type type = typeof(T);
string[] names = Enum.GetNames(type);
var values = (T[])Enum.GetValues(type);
Converter = new FlagsEnumConverter(names, values);
}
public static T[] GetSortedValues()
{
T[] vals = (T[])Enum.GetValues(typeof(T));
Array.Sort(vals);
return vals;
}
public static T Parse(string value, bool ignoreCase = false, bool parseNumeric = true)
{
return (T)Enum.ToObject(typeof(T), Converter.ParseInternal(value, ignoreCase, parseNumeric));
}
/// <summary>
/// Converts enum value to string
/// </summary>
/// <param name="value">Enum value converted to int</param>
/// <returns>If <paramref name="value"/> is defined, the enum member name; otherwise the string representation of the <paramref name="value"/>.
/// If <see cref="FlagsAttribute"/> is applied, can return comma-separated list of values</returns>
public static string ToString(int value)
{
return Converter.ToStringInternal(value);
}
/// <summary>
/// Converts enum value to string
/// </summary>
/// <param name="value">Enum value</param>
/// <returns>If <paramref name="value"/> is defined, the enum member name; otherwise the string representation of the <paramref name="value"/>.
/// If <see cref="FlagsAttribute"/> is applied, can return comma-separated list of values</returns>
public static string ToString(T value)
{
return Converter.ToStringInternal(value.ToInt32(null));
}
public static bool TryParse(string value, bool ignoreCase, bool parseNumeric, out T result)
{
int ir;
bool b = Converter.TryParseInternal(value, ignoreCase, parseNumeric, out ir);
result = (T)Enum.ToObject(typeof(T), ir);
return b;
}
public static bool TryParse(string value, bool ignoreCase, out T result)
{
int ir;
bool b = Converter.TryParseInternal(value, ignoreCase, true, out ir);
result = (T)Enum.ToObject(typeof(T), ir);
return b;
}
public static bool TryParse(string value, out T result)
{
int ir;
bool b = Converter.TryParseInternal(value, false, true, out ir);
result = (T)Enum.ToObject(typeof(T), ir);
return b;
}
class DictionaryEnumConverter : EnumConverter
{
protected readonly Dictionary<int, string> Dic;
protected DictionaryEnumConverter(string[] names, T[] values)
{
this.Dic = new Dictionary<int, string>(names.Length);
for (int j = 0; j < names.Length; j++) this.Dic.Add(Convert.ToInt32(values[j], null), names[j]);
}
public override int ParseInternal(string value, bool ignoreCase, bool parseNumber)
{
if (value == null) throw new ArgumentNullException(nameof(value));
if (value.Length == 0) throw new ArgumentException("Value is empty", nameof(value));
char f = value[0];
if (parseNumber && (char.IsDigit(f) || f == '+' || f == '-')) return int.Parse(value);
StringComparison stringComparison = ignoreCase
? StringComparison.OrdinalIgnoreCase
: StringComparison.Ordinal;
foreach (KeyValuePair<int, string> pair in this.Dic)
{
if (pair.Value.Equals(value, stringComparison)) return pair.Key;
}
throw new ArgumentException("Enum value wasn't found", nameof(value));
}
public override string ToStringInternal(int value)
{
string n;
return this.Dic.TryGetValue(value, out n) ? n : value.ToString();
}
public override bool TryParseInternal(string value, bool ignoreCase, bool parseNumber, out int result)
{
result = 0;
if (string.IsNullOrEmpty(value)) return false;
char f = value[0];
if (parseNumber && (char.IsDigit(f) || f == '+' || f == '-'))
{
int i;
if (int.TryParse(value, out i))
{
result = i;
return true;
}
return false;
}
StringComparison stringComparison = ignoreCase
? StringComparison.OrdinalIgnoreCase
: StringComparison.Ordinal;
foreach (KeyValuePair<int, string> pair in this.Dic)
{
if (pair.Value.Equals(value, stringComparison))
{
result = pair.Key;
return true;
}
}
return false;
}
}
abstract class EnumConverter
{
public abstract int ParseInternal(string value, bool ignoreCase, bool parseNumber);
public abstract string ToStringInternal(int value);
public abstract bool TryParseInternal(string value, bool ignoreCase, bool parseNumber, out int result);
}
class FlagsEnumConverter : DictionaryEnumConverter
{
private static readonly string[] Seps = new[] { "," };
private readonly uint[] values;
public FlagsEnumConverter(string[] names, T[] values)
: base(names, values)
{
this.values = new uint[values.Length];
for (int i = 0; i < values.Length; i++) this.values[i] = values[i].ToUInt32(null);
}
public override int ParseInternal(string value, bool ignoreCase, bool parseNumber)
{
string[] parts = value.Split(Seps, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length == 1) return base.ParseInternal(value, ignoreCase, parseNumber);
int val = 0;
for (int i = 0; i < parts.Length; i++)
{
string part = parts[i];
int t = base.ParseInternal(part.Trim(), ignoreCase, parseNumber);
val |= t;
}
return val;
}
public override string ToStringInternal(int value)
{
string n;
if (this.Dic.TryGetValue(value, out n)) return n;
var sb = new StringBuilder();
const string sep = ", ";
uint uval;
unchecked
{
uval = (uint)value;
for (int i = this.values.Length - 1; i >= 0; i--)
{
uint v = this.values[i];
if (v == 0) continue;
if ((v & uval) == v)
{
uval &= ~v;
sb.Insert(0, sep).Insert(0, this.Dic[(int)v]);
}
}
}
return uval == 0 && sb.Length > sep.Length ? sb.ToString(0, sb.Length - sep.Length) : value.ToString();
}
public override bool TryParseInternal(string value, bool ignoreCase, bool parseNumber, out int result)
{
string[] parts = value.Split(Seps, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length == 1) return base.TryParseInternal(value, ignoreCase, parseNumber, out result);
int val = 0;
for (int i = 0; i < parts.Length; i++)
{
string part = parts[i];
int t;
if (!base.TryParseInternal(part.Trim(), ignoreCase, parseNumber, out t))
{
result = 0;
return false;
}
val |= t;
}
result = val;
return true;
}
}
}
}

34
tests/ImageSharp.Tests/TestUtilities/GenericFactory.cs

@ -0,0 +1,34 @@
// <copyright file="GenericFactory.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Tests
{
using System;
/// <summary>
/// Utility class to create specialized subclasses of generic classes (eg. <see cref="Image"/>)
/// Used as parameter for <see cref="WithMemberFactoryAttribute"/> -based factory methods
/// </summary>
public class GenericFactory<TColor>
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
public virtual Image<TColor> CreateImage(int width, int height)
{
return new Image<TColor>(width, height);
}
public virtual Image<TColor> CreateImage(byte[] bytes)
{
return new Image<TColor>(bytes);
}
}
public class DefaultImageClassSpecificFactory : GenericFactory<Color>
{
public override Image<Color> CreateImage(byte[] bytes) => new Image(bytes);
public override Image<Color> CreateImage(int width, int height) => new Image(width, height);
}
}

75
tests/ImageSharp.Tests/TestUtilities/ImageDataAttributeBase.cs

@ -0,0 +1,75 @@
// <copyright file="ImageDataAttributeBase.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Tests
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using Xunit.Sdk;
/// <summary>
/// Base class for Theory Data attributes which pass an instance of <see cref="TestImageProvider{TColor}"/> to the test case.
/// </summary>
public abstract class ImageDataAttributeBase : DataAttribute
{
protected readonly object[] AdditionalParameters;
protected readonly PixelTypes PixelTypes;
protected ImageDataAttributeBase(PixelTypes pixelTypes, object[] additionalParameters)
{
this.PixelTypes = pixelTypes;
this.AdditionalParameters = additionalParameters;
}
public override IEnumerable<object[]> GetData(MethodInfo testMethod)
{
var type = testMethod.GetParameters().First().ParameterType;
if (!typeof(ITestImageFactory).IsAssignableFrom(type))
{
yield return this.AdditionalParameters;
}
else
{
foreach (var kv in this.PixelTypes.ExpandAllTypes())
{
var factoryType = typeof(TestImageProvider<>).MakeGenericType(kv.Value);
foreach (object[] originalFacoryMethodArgs in this.GetAllFactoryMethodArgs(testMethod, factoryType))
{
var actualFactoryMethodArgs = new object[originalFacoryMethodArgs.Length + 2];
Array.Copy(originalFacoryMethodArgs, actualFactoryMethodArgs, originalFacoryMethodArgs.Length);
actualFactoryMethodArgs[actualFactoryMethodArgs.Length - 2] = testMethod;
actualFactoryMethodArgs[actualFactoryMethodArgs.Length - 1] = kv.Key;
var factory = factoryType.GetMethod(this.GetFactoryMethodName(testMethod))
.Invoke(null, actualFactoryMethodArgs);
object[] result = new object[this.AdditionalParameters.Length + 1];
result[0] = factory;
Array.Copy(this.AdditionalParameters, 0, result, 1, this.AdditionalParameters.Length);
yield return result;
}
}
}
}
protected virtual IEnumerable<object[]> GetAllFactoryMethodArgs(MethodInfo testMethod, Type factoryType)
{
var args = this.GetFactoryMethodArgs(testMethod, factoryType);
return Enumerable.Repeat(args, 1);
}
protected virtual object[] GetFactoryMethodArgs(MethodInfo testMethod, Type factoryType)
{
throw new InvalidOperationException("Semi-abstract method");
}
protected abstract string GetFactoryMethodName(MethodInfo testMethod);
}
}

115
tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs

@ -0,0 +1,115 @@
// <copyright file="ImagingTestCaseUtility.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Tests
{
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using ImageSharp.Formats;
/// <summary>
/// Utility class to provide information about the test image & the test case for the test code,
/// and help managing IO.
/// </summary>
public class ImagingTestCaseUtility
{
/// <summary>
/// Name of the TColor in the owner <see cref="TestImageProvider{TColor}"/>
/// </summary>
public string PixelTypeName { get; set; } = string.Empty;
/// <summary>
/// The name of the file which is provided by <see cref="TestImageProvider{TColor}"/>
/// Or a short string describing the image in the case of a non-file based image provider.
/// </summary>
public string SourceFileOrDescription { get; set; } = string.Empty;
/// <summary>
/// By default this is the name of the test class, but it's possible to change it
/// </summary>
public string TestGroupName { get; set; } = string.Empty;
/// <summary>
/// The name of the test case (by default)
/// </summary>
public string TestName { get; set; } = string.Empty;
/// <summary>
/// Root directory for output images
/// </summary>
public string TestOutputRoot { get; set; } = FileTestBase.TestOutputRoot;
public string GetTestOutputDir()
{
string testGroupName = Path.GetFileNameWithoutExtension(this.TestGroupName);
string dir = $@"{this.TestOutputRoot}{testGroupName}";
Directory.CreateDirectory(dir);
return dir;
}
/// <summary>
/// Gets the recommended file name for the output of the test
/// </summary>
/// <param name="extension"></param>
/// <returns>The required extension</returns>
public string GetTestOutputFileName(string extension = null)
{
string fn = string.Empty;
fn = Path.GetFileNameWithoutExtension(this.SourceFileOrDescription);
extension = extension ?? Path.GetExtension(this.SourceFileOrDescription);
extension = extension ?? ".bmp";
if (extension[0] != '.')
{
extension = '.' + extension;
}
if (fn != string.Empty) fn = '_' + fn;
string pixName = this.PixelTypeName;
if (pixName != string.Empty)
{
pixName = '_' + pixName;
}
return $"{this.GetTestOutputDir()}/{this.TestName}{pixName}{fn}{extension}";
}
private static IImageFormat GetImageFormatByExtension(string extension)
{
extension = extension.ToLower();
return Bootstrapper.ImageFormats.First(f => f.SupportedExtensions.Contains(extension));
}
/// <summary>
/// Encodes image by the format matching the required extension, than saves it to the recommended output file.
/// </summary>
/// <typeparam name="TColor">The pixel format of the image</typeparam>
/// <param name="image">The image instance</param>
/// <param name="extension">The requested extension</param>
public void SaveTestOutputFile<TColor>(Image<TColor> image, string extension = null)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
string path = this.GetTestOutputFileName(extension);
var format = GetImageFormatByExtension(extension);
using (var stream = File.OpenWrite(path))
{
image.Save(stream, format);
}
}
internal void Init(MethodInfo method)
{
this.TestGroupName = method.DeclaringType.Name;
this.TestName = method.Name;
}
}
}

62
tests/ImageSharp.Tests/TestUtilities/PixelTypes.cs

@ -0,0 +1,62 @@
// <copyright file="PixelTypes.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Tests
{
using System;
/// <summary>
/// Flags that are mapped to PackedPixel types.
/// They trigger the desired parametrization for <see cref="TestImageProvider{TColor}"/>.
/// </summary>
[Flags]
public enum PixelTypes : uint
{
Undefined = 0,
Alpha8 = 1 << 0,
Argb = 1 << 1,
Bgr565 = 1 << 2,
Bgra4444 = 1 << 3,
Byte4 = 1 << 4,
Color = 1 << 5,
HalfSingle = 1 << 6,
HalfVector2 = 1 << 7,
HalfVector4 = 1 << 8,
NormalizedByte2 = 1 << 9,
NormalizedByte4 = 1 << 10,
NormalizedShort4 = 1 << 11,
Rg32 = 1 << 12,
Rgba1010102 = 1 << 13,
Rgba64 = 1 << 14,
Short2 = 1 << 15,
Short4 = 1 << 16,
/// <summary>
/// Triggers instantiating the <see cref="Image"/> subclass of <see cref="Image{TColor}"/>
/// </summary>
StandardImageClass = 1 << 29,
// TODO: Add multi-flag entries by rules defined in PackedPixelConverterHelper
// "All" is handled as a separate, individual case instead of using bitwise OR
All = 30
}
}

192
tests/ImageSharp.Tests/TestUtilities/TestImageProvider.cs

@ -0,0 +1,192 @@
// <copyright file="TestImageProvider.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Tests
{
using System;
using System.Collections.Concurrent;
using System.Reflection;
/// <summary>
/// Provides <see cref="Image{TColor}" /> instances for parametric unit tests.
/// </summary>
/// <typeparam name="TColor">The pixel format of the image</typeparam>
public abstract class TestImageProvider<TColor> : ITestImageFactory
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
public PixelTypes PixelType { get; private set; } = typeof(TColor).GetPixelType();
public virtual string SourceFileOrDescription => "";
/// <summary>
/// Utility instance to provide informations about the test image & manage input/output
/// </summary>
public ImagingTestCaseUtility Utility { get; private set; }
public GenericFactory<TColor> Factory { get; private set; } = new GenericFactory<TColor>();
public static TestImageProvider<TColor> Blank(
int width,
int height,
MethodInfo testMethod = null,
PixelTypes pixelTypeOverride = PixelTypes.Undefined)
=> new BlankProvider(width, height).Init(testMethod, pixelTypeOverride);
public static TestImageProvider<TColor> File(
string filePath,
MethodInfo testMethod = null,
PixelTypes pixelTypeOverride = PixelTypes.Undefined)
{
return new FileProvider(filePath).Init(testMethod, pixelTypeOverride);
}
public static TestImageProvider<TColor> Lambda(
Func<GenericFactory<TColor>, Image<TColor>> func,
MethodInfo testMethod = null,
PixelTypes pixelTypeOverride = PixelTypes.Undefined)
=> new LambdaProvider(func).Init(testMethod, pixelTypeOverride);
public static TestImageProvider<TColor> Solid(
int width,
int height,
byte r,
byte g,
byte b,
byte a = 255,
MethodInfo testMethod = null,
PixelTypes pixelTypeOverride = PixelTypes.Undefined)
{
return new SolidProvider(width, height, r, g, b, a).Init(testMethod, pixelTypeOverride);
}
/// <summary>
/// Returns an <see cref="Image{TColor}"/> instance to the test case with the necessary traits.
/// </summary>
public abstract Image<TColor> GetImage();
protected TestImageProvider<TColor> Init(MethodInfo testMethod, PixelTypes pixelTypeOverride)
{
if (pixelTypeOverride != PixelTypes.Undefined)
{
this.PixelType = pixelTypeOverride;
}
if (pixelTypeOverride == PixelTypes.StandardImageClass)
{
this.Factory = new DefaultImageClassSpecificFactory() as GenericFactory<TColor>;
}
this.Utility = new ImagingTestCaseUtility()
{
SourceFileOrDescription = this.SourceFileOrDescription,
PixelTypeName = typeof(TColor).Name
};
if (testMethod != null)
{
this.Utility.Init(testMethod);
}
return this;
}
private class BlankProvider : TestImageProvider<TColor>
{
public BlankProvider(int width, int height)
{
this.Width = width;
this.Height = height;
}
public override string SourceFileOrDescription => $"Blank{this.Width}x{this.Height}";
protected int Height { get; }
protected int Width { get; }
public override Image<TColor> GetImage() => this.Factory.CreateImage(this.Width, this.Height);
}
private class FileProvider : TestImageProvider<TColor>
{
private static ConcurrentDictionary<string, Image<TColor>> cache =
new ConcurrentDictionary<string, Image<TColor>>();
private string filePath;
public FileProvider(string filePath)
{
this.filePath = filePath;
}
public override string SourceFileOrDescription => this.filePath;
public override Image<TColor> GetImage()
{
var cachedImage = cache.GetOrAdd(
this.filePath,
fn =>
{
var testFile = TestFile.Create(this.filePath);
return this.Factory.CreateImage(testFile.Bytes);
});
return new Image<TColor>(cachedImage);
}
}
private class LambdaProvider : TestImageProvider<TColor>
{
private readonly Func<GenericFactory<TColor>, Image<TColor>> creator;
public LambdaProvider(Func<GenericFactory<TColor>, Image<TColor>> creator)
{
this.creator = creator;
}
public override Image<TColor> GetImage() => this.creator(this.Factory);
}
private class SolidProvider : BlankProvider
{
private readonly byte a;
private readonly byte b;
private readonly byte g;
private readonly byte r;
public SolidProvider(int width, int height, byte r, byte g, byte b, byte a)
: base(width, height)
{
this.r = r;
this.g = g;
this.b = b;
this.a = a;
}
public override string SourceFileOrDescription
=> $"Solid{this.Width}x{this.Height}_({this.r},{this.g},{this.b},{this.a})";
public override Image<TColor> GetImage()
{
var image = base.GetImage();
TColor color = default(TColor);
color.PackFromBytes(this.r, this.g, this.b, this.a);
return image.Fill(color);
}
}
}
/// <summary>
/// Marker
/// </summary>
public interface ITestImageFactory
{
}
}

133
tests/ImageSharp.Tests/TestUtilities/TestUtilityExtensions.cs

@ -0,0 +1,133 @@
// <copyright file="TestUtilityExtensions.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Tests
{
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
/// <summary>
/// Extension methods for TestUtilities
/// </summary>
public static class TestUtilityExtensions
{
private static readonly Dictionary<Type, PixelTypes> ClrTypes2PixelTypes = new Dictionary<Type, PixelTypes>();
private static readonly Assembly ImageSharpAssembly = typeof(Color).GetTypeInfo().Assembly;
private static readonly Dictionary<PixelTypes, Type> PixelTypes2ClrTypes = new Dictionary<PixelTypes, Type>();
private static readonly PixelTypes[] AllConcretePixelTypes = FlagsHelper<PixelTypes>
.GetSortedValues()
.Except(new [] {PixelTypes.Undefined, PixelTypes.All })
.ToArray();
static TestUtilityExtensions()
{
string nameSpace = typeof(Color).FullName;
nameSpace = nameSpace.Substring(0, nameSpace.Length - typeof(Color).Name.Length - 1);
foreach (PixelTypes pt in AllConcretePixelTypes.Where(pt => pt != PixelTypes.StandardImageClass))
{
string typeName = $"{nameSpace}.{FlagsHelper<PixelTypes>.ToString(pt)}";
var t = ImageSharpAssembly.GetType(typeName);
if (t != null)
{
PixelTypes2ClrTypes[pt] = t;
ClrTypes2PixelTypes[t] = pt;
}
}
PixelTypes2ClrTypes[PixelTypes.StandardImageClass] = typeof(Color);
}
public static Type GetPackedType(Type pixelType)
{
var intrfcType =
pixelType.GetInterfaces()
.Single(i => i.IsConstructedGenericType && i.GetGenericTypeDefinition() == typeof(IPackedPixel<>));
return intrfcType.GetGenericArguments().Single();
}
public static bool HasFlag(this PixelTypes pixelTypes, PixelTypes flag) => (pixelTypes & flag) == flag;
public static bool IsEquivalentTo<TColor>(this Image<TColor> a, Image<TColor> b, bool compareAlpha = true)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
if (a.Width != b.Width || a.Height != b.Height)
{
return false;
}
byte[] bytesA = new byte[3];
byte[] bytesB = new byte[3];
using (var pixA = a.Lock())
{
using (var pixB = b.Lock())
{
for (int y = 0; y < a.Height; y++)
{
for (int x = 0; x < a.Width; x++)
{
var ca = pixA[x, y];
var cb = pixB[x, y];
if (compareAlpha)
{
if (!ca.Equals(cb))
{
return false;
}
}
else
{
ca.ToBytes(bytesA, 0, ComponentOrder.XYZ);
cb.ToBytes(bytesB, 0, ComponentOrder.XYZ);
if (bytesA[0] != bytesB[0] ||
bytesA[1] != bytesB[1] ||
bytesA[2] != bytesB[2])
{
return false;
}
}
}
}
}
}
return true;
}
public static string ToCsv<T>(this IEnumerable<T> items, string separator = ",")
{
return string.Join(separator, items.Select(o => string.Format(CultureInfo.InvariantCulture, "{0}", o)));
}
public static Type ToType(this PixelTypes pixelType) => PixelTypes2ClrTypes[pixelType];
public static PixelTypes GetPixelType(this Type colorStructClrType) => ClrTypes2PixelTypes[colorStructClrType];
public static IEnumerable<KeyValuePair<PixelTypes, Type>> ExpandAllTypes(this PixelTypes pixelTypes)
{
if (pixelTypes == PixelTypes.Undefined)
{
return Enumerable.Empty<KeyValuePair<PixelTypes, Type>>();
}
else if (pixelTypes == PixelTypes.All)
{
// TODO: Need to return unknown types here without forcing CLR to load all types in ImageSharp assembly
return PixelTypes2ClrTypes;
}
return AllConcretePixelTypes
.Where(pt => pixelTypes.HasFlag(pt))
.Select(pt => new KeyValuePair<PixelTypes, Type>(pt, pt.ToType()));
}
}
}

193
tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs

@ -0,0 +1,193 @@
// <copyright file="TestImageFactoryTests.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// ReSharper disable InconsistentNaming
namespace ImageSharp.Tests
{
using System;
using Xunit;
using Xunit.Abstractions;
public class TestImageProviderTests
{
public TestImageProviderTests(ITestOutputHelper output)
{
this.Output = output;
}
private ITestOutputHelper Output { get; }
[Theory]
[WithBlankImages(42, 666, PixelTypes.Color | PixelTypes.Argb | PixelTypes.HalfSingle, "hello")]
public void Use_WithEmptyImageAttribute<TColor>(TestImageProvider<TColor> provider, string message)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
var 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<TColor>(
TestImageProvider<TColor> provider,
string message)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
var img = provider.GetImage();
Assert.Equal(42, img.Width);
Assert.Equal(666, img.Height);
Assert.Equal("hello", message);
}
[Theory]
[WithBlankImages(1, 1, PixelTypes.Color, PixelTypes.Color)]
[WithBlankImages(1, 1, PixelTypes.Alpha8, PixelTypes.Alpha8)]
[WithBlankImages(1, 1, PixelTypes.StandardImageClass, PixelTypes.StandardImageClass)]
public void PixelType_PropertyValueIsCorrect<TColor>(TestImageProvider<TColor> provider, PixelTypes expected)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
Assert.Equal(expected, provider.PixelType);
}
[Theory]
[WithBlankImages(1, 1, PixelTypes.StandardImageClass)]
public void PixelTypes_ColorWithDefaultImageClass_TriggersCreatingTheNonGenericDerivedImageClass<TColor>(
TestImageProvider<TColor> provider)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
var img = provider.GetImage();
Assert.IsType<Image>(img);
}
// TODO: @dlemstra this works only with constant strings!
[Theory]
[WithFile(TestImages.Bmp.Car, PixelTypes.All, 88)]
[WithFile(TestImages.Bmp.F, PixelTypes.All, 88)]
public void Use_WithFileAttribute<TColor>(TestImageProvider<TColor> provider, int yo)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
Assert.NotNull(provider.Utility.SourceFileOrDescription);
var img = provider.GetImage();
Assert.True(img.Width * img.Height > 0);
Assert.Equal(88, yo);
string fn = provider.Utility.GetTestOutputFileName("jpg");
this.Output.WriteLine(fn);
}
public static string[] AllBmpFiles => TestImages.Bmp.All;
[Theory]
[WithFileCollection(nameof(AllBmpFiles), PixelTypes.Color | PixelTypes.Argb)]
public void Use_WithFileCollection<TColor>(TestImageProvider<TColor> provider)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
Assert.NotNull(provider.Utility.SourceFileOrDescription);
var image = provider.GetImage();
provider.Utility.SaveTestOutputFile(image, "png");
}
[Theory]
[WithSolidFilledImages(10, 20, 255, 100, 50, 200, PixelTypes.Color | PixelTypes.Argb)]
public void Use_WithSolidFilledImagesAttribute<TColor>(TestImageProvider<TColor> provider)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
var img = provider.GetImage();
Assert.Equal(img.Width, 10);
Assert.Equal(img.Height, 20);
byte[] colors = new byte[4];
using (var pixels = img.Lock())
{
for (int y = 0; y < pixels.Height; y++)
{
for (int x = 0; x < pixels.Width; x++)
{
pixels[x, y].ToBytes(colors, 0, ComponentOrder.XYZW);
Assert.Equal(colors[0], 255);
Assert.Equal(colors[1], 100);
Assert.Equal(colors[2], 50);
Assert.Equal(colors[3], 200);
}
}
}
}
/// <summary>
/// Need to us <see cref="GenericFactory{TColor}"/> to create instance of <see cref="Image"/> when pixelType is StandardImageClass
/// </summary>
/// <typeparam name="TColor"></typeparam>
/// <param name="factory"></param>
/// <returns></returns>
public static Image<TColor> CreateTestImage<TColor>(GenericFactory<TColor> factory)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
return factory.CreateImage(3, 3);
}
[Theory]
[WithMemberFactory(nameof(CreateTestImage), PixelTypes.All)]
public void Use_WithMemberFactoryAttribute<TColor>(TestImageProvider<TColor> provider)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
var img = provider.GetImage();
Assert.Equal(img.Width, 3);
if (provider.PixelType == PixelTypes.StandardImageClass)
{
Assert.IsType<Image>(img);
}
}
public static readonly TheoryData<ITestImageFactory> BasicData = new TheoryData<ITestImageFactory>()
{
TestImageProvider<Color>.Blank(10, 20),
TestImageProvider<HalfVector4>.Blank(
10,
20)
};
[Theory]
[MemberData(nameof(BasicData))]
public void Blank_MemberData<TColor>(TestImageProvider<TColor> provider)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
var img = provider.GetImage();
Assert.True(img.Width * img.Height > 0);
}
public static readonly TheoryData<ITestImageFactory> FileData = new TheoryData<ITestImageFactory>()
{
TestImageProvider<Color>.File(
TestImages.Bmp.Car),
TestImageProvider<HalfVector4>.File(
TestImages.Bmp.F)
};
[Theory]
[MemberData(nameof(FileData))]
public void File_MemberData<TColor>(TestImageProvider<TColor> provider)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
this.Output.WriteLine("SRC: " + provider.Utility.SourceFileOrDescription);
this.Output.WriteLine("OUT: " + provider.Utility.GetTestOutputFileName());
var img = provider.GetImage();
Assert.True(img.Width * img.Height > 0);
}
}
}

147
tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs

@ -0,0 +1,147 @@
// <copyright file="FlagsHelper.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// ReSharper disable InconsistentNaming
namespace ImageSharp.Tests
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Reflection;
using Xunit;
using Xunit.Abstractions;
public class TestUtilityExtensionsTests
{
public TestUtilityExtensionsTests(ITestOutputHelper output)
{
this.Output = output;
}
private ITestOutputHelper Output { get; }
public static Image<TColor> CreateTestImage<TColor>(GenericFactory<TColor> factory)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
Image<TColor> image = factory.CreateImage(10, 10);
using (var pixels = image.Lock())
{
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
Vector4 v = new Vector4(i, j, 0, 1);
v /= 10;
TColor color = default(TColor);
color.PackFromVector4(v);
pixels[i, j] = color;
}
}
}
return image;
}
[Fact]
public void Baz()
{
var type = typeof(Color).GetTypeInfo().Assembly.GetType("ImageSharp.Color");
this.Output.WriteLine(type.ToString());
var fake = typeof(Color).GetTypeInfo().Assembly.GetType("ImageSharp.dsaada_DASqewrr");
Assert.Null(fake);
}
[Fact]
public void GetPackedType()
{
Type shouldBeUIint32 = TestUtilityExtensions.GetPackedType(typeof(Color));
Assert.Equal(shouldBeUIint32, typeof(uint));
}
[Theory]
[WithFile(TestImages.Bmp.Car, PixelTypes.Color, true)]
[WithFile(TestImages.Bmp.Car, PixelTypes.Color, false)]
public void IsEquivalentTo_WhenFalse<TColor>(TestImageProvider<TColor> provider, bool compareAlpha)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
var a = provider.GetImage();
var b = provider.GetImage();
b = b.OilPaint(3, 2);
Assert.False(a.IsEquivalentTo(b, compareAlpha));
}
[Theory]
[WithMemberFactory(nameof(CreateTestImage), PixelTypes.Color | PixelTypes.Bgr565, true)]
[WithMemberFactory(nameof(CreateTestImage), PixelTypes.Color | PixelTypes.Bgr565, false)]
public void IsEquivalentTo_WhenTrue<TColor>(TestImageProvider<TColor> provider, bool compareAlpha)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
var a = provider.GetImage();
var b = provider.GetImage();
Assert.True(a.IsEquivalentTo(b, compareAlpha));
}
[Theory]
[InlineData(PixelTypes.Color, typeof(Color))]
[InlineData(PixelTypes.Argb, typeof(Argb))]
[InlineData(PixelTypes.HalfVector4, typeof(HalfVector4))]
[InlineData(PixelTypes.StandardImageClass, typeof(Color))]
public void ToType(PixelTypes pt, Type expectedType)
{
Assert.Equal(pt.ToType(), expectedType);
}
[Theory]
[InlineData(typeof(Color), PixelTypes.Color)]
[InlineData(typeof(Argb), PixelTypes.Argb)]
public void GetPixelType(Type clrType, PixelTypes expectedPixelType)
{
Assert.Equal(expectedPixelType, clrType.GetPixelType());
}
private static void AssertContainsPixelType<T>(
PixelTypes pt,
IEnumerable<KeyValuePair<PixelTypes, Type>> pixelTypesExp)
{
Assert.Contains(new KeyValuePair<PixelTypes, Type>(pt, typeof(T)), pixelTypesExp);
}
[Fact]
public void ToTypes()
{
PixelTypes pixelTypes = PixelTypes.Alpha8 | PixelTypes.Bgr565 | PixelTypes.Color | PixelTypes.HalfVector2 | PixelTypes.StandardImageClass;
var expanded = pixelTypes.ExpandAllTypes();
Assert.Equal(expanded.Count(), 5);
AssertContainsPixelType<Alpha8>(PixelTypes.Alpha8, expanded);
AssertContainsPixelType<Bgr565>(PixelTypes.Bgr565, expanded);
AssertContainsPixelType<Color>(PixelTypes.Color, expanded);
AssertContainsPixelType<HalfVector2>(PixelTypes.HalfVector2, expanded);
AssertContainsPixelType<Color>(PixelTypes.StandardImageClass, expanded);
}
[Fact]
public void ToTypes_All()
{
var expanded = PixelTypes.All.ExpandAllTypes().ToArray();
Assert.True(expanded.Length >= FlagsHelper<PixelTypes>.GetSortedValues().Length - 2);
AssertContainsPixelType<Color>(PixelTypes.Color, expanded);
AssertContainsPixelType<Color>(PixelTypes.StandardImageClass, expanded);
}
}
}

38
tests/ImageSharp.Tests/TestUtilities/WithBlankImageAttribute.cs

@ -0,0 +1,38 @@
// <copyright file="WithBlankImagesAttribute.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Tests
{
using System;
using System.Reflection;
/// <summary>
/// Triggers passing <see cref="TestImageProvider{TColor}"/> instances which produce a blank image of size width * height.
/// One <see cref="TestImageProvider{TColor}"/> instance will be passed for each the pixel format defined by the pixelTypes parameter
/// </summary>
public class WithBlankImagesAttribute : ImageDataAttributeBase
{
/// <summary>
/// Triggers passing an <see cref="TestImageProvider{TColor}"/> that produces a blank image of size width * height
/// </summary>
/// <param name="width">The required width</param>
/// <param name="height">The required height</param>
/// <param name="pixelTypes">The requested parameter</param>
/// <param name="additionalParameters">Additional theory parameter values</param>
public WithBlankImagesAttribute(int width, int height, PixelTypes pixelTypes, params object[] additionalParameters)
: base(pixelTypes, additionalParameters)
{
this.Width = width;
this.Height = height;
}
public int Width { get; }
public int Height { get; }
protected override string GetFactoryMethodName(MethodInfo testMethod) => "Blank";
protected override object[] GetFactoryMethodArgs(MethodInfo testMethod, Type factoryType) => new object[] { this.Width, this.Height };
}
}

36
tests/ImageSharp.Tests/TestUtilities/WithFileAttribute.cs

@ -0,0 +1,36 @@
// <copyright file="WithFileAttribute.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Tests
{
using System;
using System.Reflection;
/// <summary>
/// Triggers passing <see cref="TestImageProvider{TColor}"/> instances which read an image from the given file
/// One <see cref="TestImageProvider{TColor}"/> instance will be passed for each the pixel format defined by the pixelTypes parameter
/// </summary>
public class WithFileAttribute : ImageDataAttributeBase
{
private readonly string fileName;
/// <summary>
/// Triggers passing <see cref="TestImageProvider{TColor}"/> instances which read an image from the given file
/// One <see cref="TestImageProvider{TColor}"/> instance will be passed for each the pixel format defined by the pixelTypes parameter
/// </summary>
/// <param name="fileName">The name of the file</param>
/// <param name="pixelTypes">The requested pixel types</param>
/// <param name="additionalParameters">Additional theory parameter values</param>
public WithFileAttribute(string fileName, PixelTypes pixelTypes, params object[] additionalParameters)
: base(pixelTypes, additionalParameters)
{
this.fileName = fileName;
}
protected override object[] GetFactoryMethodArgs(MethodInfo testMethod, Type factoryType) => new object[] { this.fileName };
protected override string GetFactoryMethodName(MethodInfo testMethod) => "File";
}
}

86
tests/ImageSharp.Tests/TestUtilities/WithFileCollectionAttribute.cs

@ -0,0 +1,86 @@
// <copyright file="WithFileCollectionAttribute.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Tests
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
/// <summary>
/// Triggers passing <see cref="TestImageProvider{TColor}"/> instances which read an image for each file being enumerated by the (static) test class field/property defined by enumeratorMemberName
/// <see cref="TestImageProvider{TColor}"/> instances will be passed for each the pixel format defined by the pixelTypes parameter
/// </summary>
public class WithFileCollectionAttribute : ImageDataAttributeBase
{
private readonly string enumeratorMemberName;
/// <summary>
/// Triggers passing <see cref="TestImageProvider{TColor}"/> instances which read an image for each file being enumerated by the (static) test class field/property defined by enumeratorMemberName
/// <see cref="TestImageProvider{TColor}"/> instances will be passed for each the pixel format defined by the pixelTypes parameter
/// </summary>
/// <param name="enumeratorMemberName">The name of the static test class field/property enumerating the files</param>
/// <param name="pixelTypes">The requested pixel types</param>
/// <param name="additionalParameters">Additional theory parameter values</param>
public WithFileCollectionAttribute(
string enumeratorMemberName,
PixelTypes pixelTypes,
params object[] additionalParameters)
: base(pixelTypes, additionalParameters)
{
this.enumeratorMemberName = enumeratorMemberName;
}
protected override IEnumerable<object[]> GetAllFactoryMethodArgs(MethodInfo testMethod, Type factoryType)
{
var accessor = this.GetPropertyAccessor(testMethod.DeclaringType);
accessor = accessor ?? this.GetFieldAccessor(testMethod.DeclaringType);
IEnumerable<string> files = (IEnumerable<string>)accessor();
return files.Select(f => new object[] { f });
}
protected override string GetFactoryMethodName(MethodInfo testMethod) => "File";
/// <summary>
/// Based on MemberData implementation
/// </summary>
private Func<object> GetFieldAccessor(Type type)
{
FieldInfo fieldInfo = null;
for (var reflectionType = type;
reflectionType != null;
reflectionType = reflectionType.GetTypeInfo().BaseType)
{
fieldInfo = reflectionType.GetRuntimeField(this.enumeratorMemberName);
if (fieldInfo != null) break;
}
if (fieldInfo == null || !fieldInfo.IsStatic) return null;
return () => fieldInfo.GetValue(null);
}
/// <summary>
/// Based on MemberData implementation
/// </summary>
private Func<object> GetPropertyAccessor(Type type)
{
PropertyInfo propInfo = null;
for (var reflectionType = type;
reflectionType != null;
reflectionType = reflectionType.GetTypeInfo().BaseType)
{
propInfo = reflectionType.GetRuntimeProperty(this.enumeratorMemberName);
if (propInfo != null) break;
}
if (propInfo == null || propInfo.GetMethod == null || !propInfo.GetMethod.IsStatic) return null;
return () => propInfo.GetValue(null, null);
}
}
}

54
tests/ImageSharp.Tests/TestUtilities/WithMemberFactoryAttribute.cs

@ -0,0 +1,54 @@
// <copyright file="WithMemberFactoryAttribute.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Tests
{
using System;
using System.Linq;
using System.Reflection;
/// <summary>
/// Triggers passing <see cref="TestImageProvider{TColor}"/> instances which return the image produced by the given test class member method
/// <see cref="TestImageProvider{TColor}"/> instances will be passed for each the pixel format defined by the pixelTypes parameter
/// The parameter of the factory method must be a <see cref="GenericFactory{TColor}"/> instance
/// </summary>
public class WithMemberFactoryAttribute : ImageDataAttributeBase
{
private readonly string memberMethodName;
/// <summary>
/// Triggers passing <see cref="TestImageProvider{TColor}"/> instances which return the image produced by the given test class member method
/// <see cref="TestImageProvider{TColor}"/> instances will be passed for each the pixel format defined by the pixelTypes parameter
/// </summary>
/// <param name="memberMethodName">The name of the static test class which returns the image</param>
/// <param name="pixelTypes">The requested pixel types</param>
/// <param name="additionalParameters">Additional theory parameter values</param>
public WithMemberFactoryAttribute(string memberMethodName, PixelTypes pixelTypes, params object[] additionalParameters)
: base(pixelTypes, additionalParameters)
{
this.memberMethodName = memberMethodName;
}
protected override object[] GetFactoryMethodArgs(MethodInfo testMethod, Type factoryType)
{
var m = testMethod.DeclaringType.GetMethod(this.memberMethodName);
var args = factoryType.GetGenericArguments();
var colorType = args.Single();
var imgType = typeof(Image<>).MakeGenericType(colorType);
var genericFactoryType = (typeof(GenericFactory<>)).MakeGenericType(colorType);
var funcType = typeof(Func<,>).MakeGenericType(genericFactoryType, imgType);
var genericMethod = m.MakeGenericMethod(args);
var d = genericMethod.CreateDelegate(funcType);
return new object[] { d };
}
protected override string GetFactoryMethodName(MethodInfo testMethod) => "Lambda";
}
}

93
tests/ImageSharp.Tests/TestUtilities/WithSolidFilledImagesAttribute.cs

@ -0,0 +1,93 @@
// <copyright file="WithSolidFilledImagesAttribute.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Tests
{
using System;
using System.Reflection;
/// <summary>
/// Triggers passing <see cref="TestImageProvider{TColor}"/> instances which produce an image of size width * height filled with the requested color.
/// One <see cref="TestImageProvider{TColor}"/> instance will be passed for each the pixel format defined by the pixelTypes parameter
/// </summary>
public class WithSolidFilledImagesAttribute : WithBlankImagesAttribute
{
/// <summary>
/// Triggers passing <see cref="TestImageProvider{TColor}"/> instances which produce an image of size width * height filled with the requested color.
/// One <see cref="TestImageProvider{TColor}"/> instance will be passed for each the pixel format defined by the pixelTypes parameter
/// </summary>
/// <param name="width">The width of the requested image</param>
/// <param name="height">The height of the requested image</param>
/// <param name="r">Red</param>
/// <param name="g">Green</param>
/// <param name="b">Blue</param>
/// <param name="pixelTypes">The requested pixel types</param>
/// <param name="additionalParameters">Additional theory parameter values</param>
public WithSolidFilledImagesAttribute(
int width,
int height,
byte r,
byte g,
byte b,
PixelTypes pixelTypes,
params object[] additionalParameters)
: this(width, height, r, g, b, 255, pixelTypes, additionalParameters)
{
}
/// <summary>
/// Triggers passing <see cref="TestImageProvider{TColor}"/> instances which produce an image of size width * height filled with the requested color.
/// One <see cref="TestImageProvider{TColor}"/> instance will be passed for each the pixel format defined by the pixelTypes parameter
/// </summary>
/// <param name="width">The width of the requested image</param>
/// <param name="height">The height of the requested image</param>
/// <param name="r">Red</param>
/// <param name="g">Green</param>
/// <param name="b">Blue</param>
/// /// <param name="a">Alpha</param>
/// <param name="pixelTypes">The requested pixel types</param>
/// <param name="additionalParameters">Additional theory parameter values</param>
public WithSolidFilledImagesAttribute(
int width,
int height,
byte r,
byte g,
byte b,
byte a,
PixelTypes pixelTypes,
params object[] additionalParameters)
: base(width, height, pixelTypes, additionalParameters)
{
this.R = r;
this.G = g;
this.B = b;
this.A = a;
}
/// <summary>
/// Red
/// </summary>
public byte R { get; }
/// <summary>
/// Green
/// </summary>
public byte G { get; }
/// <summary>
/// Blue
/// </summary>
public byte B { get; }
/// <summary>
/// Alpha
/// </summary>
public byte A { get; }
protected override object[] GetFactoryMethodArgs(MethodInfo testMethod, Type factoryType)
=> new object[] { this.Width, this.Height, this.R, this.G, this.B, this.A };
protected override string GetFactoryMethodName(MethodInfo testMethod) => "Solid";
}
}
Loading…
Cancel
Save