diff --git a/ImageSharp.sln b/ImageSharp.sln
index 875ede1b2..a5ab1b297 100644
--- a/ImageSharp.sln
+++ b/ImageSharp.sln
@@ -44,6 +44,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{56801022
ProjectSection(SolutionItems) = preProject
tests\Directory.Build.props = tests\Directory.Build.props
tests\Directory.Build.targets = tests\Directory.Build.targets
+ tests\NuGet.config = tests\NuGet.config
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Images", "Images", "{FA55F5DE-11A6-487D-ABA4-BC93A02717DD}"
diff --git a/tests/ImageSharp.Tests/Memory/Alocators/ArrayPoolMemoryAllocatorTests.cs b/tests/ImageSharp.Tests/Memory/Alocators/ArrayPoolMemoryAllocatorTests.cs
index d38b5b9dd..8b850b91f 100644
--- a/tests/ImageSharp.Tests/Memory/Alocators/ArrayPoolMemoryAllocatorTests.cs
+++ b/tests/ImageSharp.Tests/Memory/Alocators/ArrayPoolMemoryAllocatorTests.cs
@@ -6,38 +6,28 @@ using System;
using System.Buffers;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+using Microsoft.DotNet.RemoteExecutor;
using SixLabors.ImageSharp.Tests;
using Xunit;
namespace SixLabors.ImageSharp.Memory.Tests
{
- // TODO: Re-enable memory-intensive tests with arcade RemoteExecutor:
- // https://github.com/dotnet/runtime/blob/master/docs/project/writing-tests.md#remoteexecutor
public class ArrayPoolMemoryAllocatorTests
{
private const int MaxPooledBufferSizeInBytes = 2048;
private const int PoolSelectorThresholdInBytes = MaxPooledBufferSizeInBytes / 2;
- private MemoryAllocator MemoryAllocator { get; set; } =
- new ArrayPoolMemoryAllocator(MaxPooledBufferSizeInBytes, PoolSelectorThresholdInBytes);
-
///
- /// Rent a buffer -> return it -> re-rent -> verify if it's span points to the previous location.
+ /// Contains SUT for in-process tests.
///
- private bool CheckIsRentingPooledBuffer(int length)
- where T : struct
- {
- IMemoryOwner buffer = this.MemoryAllocator.Allocate(length);
- ref T ptrToPrevPosition0 = ref buffer.GetReference();
- buffer.Dispose();
+ private MemoryAllocatorFixture LocalFixture { get; } = new MemoryAllocatorFixture();
- buffer = this.MemoryAllocator.Allocate(length);
- bool sameBuffers = Unsafe.AreSame(ref ptrToPrevPosition0, ref buffer.GetReference());
- buffer.Dispose();
-
- return sameBuffers;
- }
+ ///
+ /// Contains SUT for tests executed by ,
+ /// recreated in each external process.
+ ///
+ private static MemoryAllocatorFixture StaticFixture { get; } = new MemoryAllocatorFixture();
public class BufferTests : BufferTestSuite
{
@@ -78,21 +68,21 @@ namespace SixLabors.ImageSharp.Memory.Tests
[InlineData(MaxPooledBufferSizeInBytes - 1)]
public void SmallBuffersArePooled_OfByte(int size)
{
- Assert.True(this.CheckIsRentingPooledBuffer(size));
+ Assert.True(this.LocalFixture.CheckIsRentingPooledBuffer(size));
}
- [Theory(Skip = "Should be executed from a separate process.")]
+ [Theory]
[InlineData(128 * 1024 * 1024)]
[InlineData(MaxPooledBufferSizeInBytes + 1)]
public void LargeBuffersAreNotPooled_OfByte(int size)
{
- if (!TestEnvironment.Is64BitProcess)
+ static void RunTest(string sizeStr)
{
- // can lead to OutOfMemoryException
- return;
+ int size = int.Parse(sizeStr);
+ StaticFixture.CheckIsRentingPooledBuffer(size);
}
- Assert.False(this.CheckIsRentingPooledBuffer(size));
+ RemoteExecutor.Invoke(RunTest, size.ToString()).Dispose();
}
[Fact]
@@ -100,21 +90,15 @@ namespace SixLabors.ImageSharp.Memory.Tests
{
int count = (MaxPooledBufferSizeInBytes / sizeof(LargeStruct)) - 1;
- Assert.True(this.CheckIsRentingPooledBuffer(count));
+ Assert.True(this.LocalFixture.CheckIsRentingPooledBuffer(count));
}
- [Fact(Skip = "Should be executed from a separate process.")]
+ [Fact]
public unsafe void LaregeBuffersAreNotPooled_OfBigValueType()
{
- if (!TestEnvironment.Is64BitProcess)
- {
- // can lead to OutOfMemoryException
- return;
- }
-
int count = (MaxPooledBufferSizeInBytes / sizeof(LargeStruct)) + 1;
- Assert.False(this.CheckIsRentingPooledBuffer(count));
+ Assert.False(this.LocalFixture.CheckIsRentingPooledBuffer(count));
}
[Theory]
@@ -122,12 +106,13 @@ namespace SixLabors.ImageSharp.Memory.Tests
[InlineData(AllocationOptions.Clean)]
public void CleaningRequests_AreControlledByAllocationParameter_Clean(AllocationOptions options)
{
- using (IMemoryOwner firstAlloc = this.MemoryAllocator.Allocate(42))
+ MemoryAllocator memoryAllocator = this.LocalFixture.MemoryAllocator;
+ using (IMemoryOwner firstAlloc = memoryAllocator.Allocate(42))
{
firstAlloc.GetSpan().Fill(666);
}
- using (IMemoryOwner secondAlloc = this.MemoryAllocator.Allocate(42, options))
+ using (IMemoryOwner secondAlloc = memoryAllocator.Allocate(42, options))
{
int expected = options == AllocationOptions.Clean ? 0 : 666;
Assert.Equal(expected, secondAlloc.GetSpan()[0]);
@@ -139,7 +124,8 @@ namespace SixLabors.ImageSharp.Memory.Tests
[InlineData(true)]
public void ReleaseRetainedResources_ReplacesInnerArrayPool(bool keepBufferAlive)
{
- IMemoryOwner buffer = this.MemoryAllocator.Allocate(32);
+ MemoryAllocator memoryAllocator = this.LocalFixture.MemoryAllocator;
+ IMemoryOwner buffer = memoryAllocator.Allocate(32);
ref int ptrToPrev0 = ref MemoryMarshal.GetReference(buffer.GetSpan());
if (!keepBufferAlive)
@@ -147,9 +133,9 @@ namespace SixLabors.ImageSharp.Memory.Tests
buffer.Dispose();
}
- this.MemoryAllocator.ReleaseRetainedResources();
+ memoryAllocator.ReleaseRetainedResources();
- buffer = this.MemoryAllocator.Allocate(32);
+ buffer = memoryAllocator.Allocate(32);
Assert.False(Unsafe.AreSame(ref ptrToPrev0, ref buffer.GetReference()));
}
@@ -157,87 +143,69 @@ namespace SixLabors.ImageSharp.Memory.Tests
[Fact]
public void ReleaseRetainedResources_DisposingPreviouslyAllocatedBuffer_IsAllowed()
{
- IMemoryOwner buffer = this.MemoryAllocator.Allocate(32);
- this.MemoryAllocator.ReleaseRetainedResources();
+ MemoryAllocator memoryAllocator = this.LocalFixture.MemoryAllocator;
+ IMemoryOwner buffer = memoryAllocator.Allocate(32);
+ memoryAllocator.ReleaseRetainedResources();
buffer.Dispose();
}
- [Fact(Skip = "Should be executed from a separate process.")]
+ [Fact]
public void AllocationOverLargeArrayThreshold_UsesDifferentPool()
{
- if (!TestEnvironment.Is64BitProcess)
+ static void RunTest()
{
- // can lead to OutOfMemoryException
- return;
- }
+ const int ArrayLengthThreshold = PoolSelectorThresholdInBytes / sizeof(int);
- const int ArrayLengthThreshold = PoolSelectorThresholdInBytes / sizeof(int);
+ IMemoryOwner small = StaticFixture.MemoryAllocator.Allocate(ArrayLengthThreshold - 1);
+ ref int ptr2Small = ref small.GetReference();
+ small.Dispose();
- IMemoryOwner small = this.MemoryAllocator.Allocate(ArrayLengthThreshold - 1);
- ref int ptr2Small = ref small.GetReference();
- small.Dispose();
+ IMemoryOwner large = StaticFixture.MemoryAllocator.Allocate(ArrayLengthThreshold + 1);
- IMemoryOwner large = this.MemoryAllocator.Allocate(ArrayLengthThreshold + 1);
+ Assert.False(Unsafe.AreSame(ref ptr2Small, ref large.GetReference()));
+ }
- Assert.False(Unsafe.AreSame(ref ptr2Small, ref large.GetReference()));
+ RemoteExecutor.Invoke(RunTest).Dispose();
}
- [Fact(Skip = "Should be executed from a separate process.")]
+ [Fact]
public void CreateWithAggressivePooling()
{
- if (!TestEnvironment.Is64BitProcess)
+ static void RunTest()
{
- // can lead to OutOfMemoryException
- return;
+ StaticFixture.MemoryAllocator = ArrayPoolMemoryAllocator.CreateWithAggressivePooling();
+ Assert.True(StaticFixture.CheckIsRentingPooledBuffer(4096 * 4096));
}
- this.MemoryAllocator = ArrayPoolMemoryAllocator.CreateWithAggressivePooling();
-
- Assert.True(this.CheckIsRentingPooledBuffer(4096 * 4096));
+ RemoteExecutor.Invoke(RunTest).Dispose();
}
- [Fact(Skip = "Should be executed from a separate process.")]
+ [Fact]
public void CreateDefault()
{
- if (!TestEnvironment.Is64BitProcess)
+ static void RunTest()
{
- // can lead to OutOfMemoryException
- return;
- }
+ StaticFixture.MemoryAllocator = ArrayPoolMemoryAllocator.CreateDefault();
- this.MemoryAllocator = ArrayPoolMemoryAllocator.CreateDefault();
+ Assert.False(StaticFixture.CheckIsRentingPooledBuffer(2 * 4096 * 4096));
+ Assert.True(StaticFixture.CheckIsRentingPooledBuffer(2048 * 2048));
+ }
- Assert.False(this.CheckIsRentingPooledBuffer(2 * 4096 * 4096));
- Assert.True(this.CheckIsRentingPooledBuffer(2048 * 2048));
+ RemoteExecutor.Invoke(RunTest).Dispose();
}
[Fact]
public void CreateWithModeratePooling()
{
- if (!TestEnvironment.Is64BitProcess)
+ static void RunTest()
{
- // can lead to OutOfMemoryException
- return;
+ StaticFixture.MemoryAllocator = ArrayPoolMemoryAllocator.CreateWithModeratePooling();
+ Assert.False(StaticFixture.CheckIsRentingPooledBuffer(2048 * 2048));
+ Assert.True(StaticFixture.CheckIsRentingPooledBuffer(1024 * 16));
}
- this.MemoryAllocator = ArrayPoolMemoryAllocator.CreateWithModeratePooling();
-
- Assert.False(this.CheckIsRentingPooledBuffer(2048 * 2048));
- Assert.True(this.CheckIsRentingPooledBuffer(1024 * 16));
- }
-
- [StructLayout(LayoutKind.Sequential)]
- private struct Rgba32
- {
- private readonly uint dummy;
- }
-
- private const int SizeOfLargeStruct = MaxPooledBufferSizeInBytes / 5;
-
- [StructLayout(LayoutKind.Explicit, Size = SizeOfLargeStruct)]
- private struct LargeStruct
- {
+ RemoteExecutor.Invoke(RunTest).Dispose();
}
[Theory]
@@ -245,7 +213,8 @@ namespace SixLabors.ImageSharp.Memory.Tests
[InlineData((int.MaxValue / SizeOfLargeStruct) + 1)]
public void AllocateIncorrectAmount_ThrowsCorrect_ArgumentOutOfRangeException(int length)
{
- ArgumentOutOfRangeException ex = Assert.Throws(() => this.MemoryAllocator.Allocate(length));
+ ArgumentOutOfRangeException ex = Assert.Throws(() =>
+ this.LocalFixture.MemoryAllocator.Allocate(length));
Assert.Equal("length", ex.ParamName);
}
@@ -253,8 +222,45 @@ namespace SixLabors.ImageSharp.Memory.Tests
[InlineData(-1)]
public void AllocateManagedByteBuffer_IncorrectAmount_ThrowsCorrect_ArgumentOutOfRangeException(int length)
{
- ArgumentOutOfRangeException ex = Assert.Throws(() => this.MemoryAllocator.AllocateManagedByteBuffer(length));
+ ArgumentOutOfRangeException ex = Assert.Throws(() =>
+ this.LocalFixture.MemoryAllocator.AllocateManagedByteBuffer(length));
Assert.Equal("length", ex.ParamName);
}
+
+ private class MemoryAllocatorFixture
+ {
+ public MemoryAllocator MemoryAllocator { get; set; } =
+ new ArrayPoolMemoryAllocator(MaxPooledBufferSizeInBytes, PoolSelectorThresholdInBytes);
+
+ ///
+ /// Rent a buffer -> return it -> re-rent -> verify if it's span points to the previous location.
+ ///
+ public bool CheckIsRentingPooledBuffer(int length)
+ where T : struct
+ {
+ IMemoryOwner buffer = MemoryAllocator.Allocate(length);
+ ref T ptrToPrevPosition0 = ref buffer.GetReference();
+ buffer.Dispose();
+
+ buffer = MemoryAllocator.Allocate(length);
+ bool sameBuffers = Unsafe.AreSame(ref ptrToPrevPosition0, ref buffer.GetReference());
+ buffer.Dispose();
+
+ return sameBuffers;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ private struct SmallStruct
+ {
+ private readonly uint dummy;
+ }
+
+ private const int SizeOfLargeStruct = MaxPooledBufferSizeInBytes / 5;
+
+ [StructLayout(LayoutKind.Explicit, Size = SizeOfLargeStruct)]
+ private struct LargeStruct
+ {
+ }
}
}
diff --git a/tests/ImageSharp.Tests/TestUtilities/BasicSerializer.cs b/tests/ImageSharp.Tests/TestUtilities/BasicSerializer.cs
new file mode 100644
index 000000000..971e6c54e
--- /dev/null
+++ b/tests/ImageSharp.Tests/TestUtilities/BasicSerializer.cs
@@ -0,0 +1,87 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Globalization;
+using System.IO;
+using Xunit.Abstractions;
+
+namespace SixLabors.ImageSharp.Tests.TestUtilities
+{
+ ///
+ /// -compatible serialization for cross-process use-cases.
+ ///
+ internal class BasicSerializer : IXunitSerializationInfo
+ {
+ private readonly Dictionary map = new Dictionary();
+
+ public const char Separator = ':';
+
+ private string DumpToString()
+ {
+ using var ms = new MemoryStream();
+ using var writer = new StreamWriter(ms);
+ foreach (KeyValuePair kv in this.map)
+ {
+ writer.WriteLine($"{kv.Key}{Separator}{kv.Value}");
+ }
+ writer.Flush();
+ byte[] data = ms.ToArray();
+ return System.Convert.ToBase64String(data);
+ }
+
+ private void LoadDump(string dump)
+ {
+ byte[] data = System.Convert.FromBase64String(dump);
+
+ using var ms = new MemoryStream(data);
+ using var reader = new StreamReader(ms);
+ for (string s = reader.ReadLine(); s != null ; s = reader.ReadLine())
+ {
+ string[] kv = s.Split(Separator);
+ this.map[kv[0]] = kv[1];
+ }
+ }
+
+ public static string Serialize(IXunitSerializable serializable)
+ {
+ var serializer = new BasicSerializer();
+ serializable.Serialize(serializer);
+ return serializer.DumpToString();
+ }
+
+ public static T Deserialize(string dump) where T : IXunitSerializable
+ {
+ T result = Activator.CreateInstance();
+ var serializer = new BasicSerializer();
+ serializer.LoadDump(dump);
+ result.Deserialize(serializer);
+ return result;
+ }
+
+ public void AddValue(string key, object value, Type type = null)
+ {
+ Guard.NotNull(key, nameof(key));
+ if (value == null)
+ {
+ return;
+ }
+ type ??= value.GetType();
+
+ this.map[key] = TypeDescriptor.GetConverter(type).ConvertToInvariantString(value);
+ }
+
+ public object GetValue(string key, Type type)
+ {
+ Guard.NotNull(key, nameof(key));
+
+ if (!this.map.TryGetValue(key, out string str))
+ {
+ return type.IsValueType ? Activator.CreateInstance(type) : null;
+ }
+
+ return TypeDescriptor.GetConverter(type).ConvertFromInvariantString(str);
+ }
+
+ public T GetValue(string key) => (T)this.GetValue(key, typeof(T));
+ }
+}
diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/BasicSerializerTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/BasicSerializerTests.cs
new file mode 100644
index 000000000..ba5bdb219
--- /dev/null
+++ b/tests/ImageSharp.Tests/TestUtilities/Tests/BasicSerializerTests.cs
@@ -0,0 +1,43 @@
+using SixLabors.ImageSharp.Tests.TestUtilities;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace SixLabors.ImageSharp.Tests
+{
+ public class BasicSerializerTests
+ {
+ class TestObj : IXunitSerializable
+ {
+ public double Length { get; set; }
+ public string Name { get; set; }
+ public int Lives { get; set; }
+
+ public void Deserialize(IXunitSerializationInfo info)
+ {
+ info.AddValue(nameof(Length), Length);
+ info.AddValue(nameof(Name), Name);
+ info.AddValue(nameof(this.Lives), Lives);
+ }
+
+ public void Serialize(IXunitSerializationInfo info)
+ {
+ this.Length = info.GetValue(nameof(Length));
+ this.Name = info.GetValue(nameof(Name));
+ this.Lives = info.GetValue(nameof(Lives));
+ }
+ }
+
+ [Fact]
+ public void SerializeDeserialize_ShouldPreserveValues()
+ {
+ var obj = new TestObj() {Length = 123, Name = "Lol123!", Lives = 7};
+
+ string str = BasicSerializer.Serialize(obj);
+ TestObj mirror = BasicSerializer.Deserialize(str);
+
+ Assert.Equal(obj.Length, mirror.Length);
+ Assert.Equal(obj.Name, mirror.Name);
+ Assert.Equal(obj.Lives, mirror.Lives);
+ }
+ }
+}
diff --git a/tests/NuGet.config b/tests/NuGet.config
new file mode 100644
index 000000000..8f9bf7e10
--- /dev/null
+++ b/tests/NuGet.config
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+