mirror of https://github.com/SixLabors/ImageSharp
6 changed files with 351 additions and 36 deletions
@ -0,0 +1,285 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using SixLabors.ImageSharp.Memory; |
||||
|
using Xunit; |
||||
|
// ReSharper disable InconsistentNaming
|
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Tests.Memory |
||||
|
{ |
||||
|
|
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Inherit this class to test an <see cref="IBuffer{T}"/> implementation (provided by <see cref="MemoryManager"/>).
|
||||
|
/// </summary>
|
||||
|
public abstract class BufferTestSuite |
||||
|
{ |
||||
|
protected BufferTestSuite(MemoryManager memoryManager) |
||||
|
{ |
||||
|
this.MemoryManager = memoryManager; |
||||
|
} |
||||
|
|
||||
|
protected MemoryManager MemoryManager { get; } |
||||
|
|
||||
|
public struct CustomStruct : IEquatable<CustomStruct> |
||||
|
{ |
||||
|
public long A; |
||||
|
|
||||
|
public byte B; |
||||
|
|
||||
|
public float C; |
||||
|
|
||||
|
public CustomStruct(long a, byte b, float c) |
||||
|
{ |
||||
|
this.A = a; |
||||
|
this.B = b; |
||||
|
this.C = c; |
||||
|
} |
||||
|
|
||||
|
public bool Equals(CustomStruct other) |
||||
|
{ |
||||
|
return this.A == other.A && this.B == other.B && this.C.Equals(other.C); |
||||
|
} |
||||
|
|
||||
|
public override bool Equals(object obj) |
||||
|
{ |
||||
|
if (ReferenceEquals(null, obj)) return false; |
||||
|
return obj is CustomStruct && this.Equals((CustomStruct)obj); |
||||
|
} |
||||
|
|
||||
|
public override int GetHashCode() |
||||
|
{ |
||||
|
unchecked |
||||
|
{ |
||||
|
int hashCode = this.A.GetHashCode(); |
||||
|
hashCode = (hashCode * 397) ^ this.B.GetHashCode(); |
||||
|
hashCode = (hashCode * 397) ^ this.C.GetHashCode(); |
||||
|
return hashCode; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static readonly TheoryData<int> LenthValues = new TheoryData<int> { 0, 1, 7, 1023, 1024 }; |
||||
|
|
||||
|
[Theory] |
||||
|
[MemberData(nameof(LenthValues))] |
||||
|
public void HasCorrectLength_byte(int desiredLength) |
||||
|
{ |
||||
|
this.TestHasCorrectLength<byte>(desiredLength); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[MemberData(nameof(LenthValues))] |
||||
|
public void HasCorrectLength_float(int desiredLength) |
||||
|
{ |
||||
|
this.TestHasCorrectLength<float>(desiredLength); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[MemberData(nameof(LenthValues))] |
||||
|
public void HasCorrectLength_CustomStruct(int desiredLength) |
||||
|
{ |
||||
|
this.TestHasCorrectLength<CustomStruct>(desiredLength); |
||||
|
} |
||||
|
|
||||
|
private void TestHasCorrectLength<T>(int desiredLength) |
||||
|
where T : struct |
||||
|
{ |
||||
|
using (IBuffer<T> buffer = this.MemoryManager.Allocate<T>(desiredLength)) |
||||
|
{ |
||||
|
Assert.Equal(desiredLength, buffer.Span.Length); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[MemberData(nameof(LenthValues))] |
||||
|
public void CanAllocateCleanBuffer_byte(int desiredLength) |
||||
|
{ |
||||
|
this.TestCanAllocateCleanBuffer<byte>(desiredLength, false); |
||||
|
this.TestCanAllocateCleanBuffer<byte>(desiredLength, true); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[MemberData(nameof(LenthValues))] |
||||
|
public void CanAllocateCleanBuffer_double(int desiredLength) |
||||
|
{ |
||||
|
this.TestCanAllocateCleanBuffer<double>(desiredLength); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[MemberData(nameof(LenthValues))] |
||||
|
public void CanAllocateCleanBuffer_CustomStruct(int desiredLength) |
||||
|
{ |
||||
|
this.TestCanAllocateCleanBuffer<CustomStruct>(desiredLength); |
||||
|
} |
||||
|
|
||||
|
private IBuffer<T> Allocate<T>(int desiredLength, bool clean, bool managedByteBuffer) |
||||
|
where T : struct |
||||
|
{ |
||||
|
if (managedByteBuffer) |
||||
|
{ |
||||
|
if (!(this.MemoryManager.AllocateManagedByteBuffer(desiredLength, clean) is IBuffer<T> buffer)) |
||||
|
{ |
||||
|
throw new InvalidOperationException("typeof(T) != typeof(byte)"); |
||||
|
} |
||||
|
|
||||
|
return buffer; |
||||
|
} |
||||
|
|
||||
|
return this.MemoryManager.Allocate<T>(desiredLength, clean); |
||||
|
} |
||||
|
|
||||
|
private void TestCanAllocateCleanBuffer<T>(int desiredLength, bool testManagedByteBuffer = false) |
||||
|
where T : struct, IEquatable<T> |
||||
|
{ |
||||
|
ReadOnlySpan<T> expected = new T[desiredLength]; |
||||
|
|
||||
|
for (int i = 0; i < 10; i++) |
||||
|
{ |
||||
|
using (IBuffer<T> buffer = this.Allocate<T>(desiredLength, true, testManagedByteBuffer)) |
||||
|
{ |
||||
|
Assert.True(buffer.Span.SequenceEqual(expected)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[MemberData(nameof(LenthValues))] |
||||
|
public void SpanPropertyIsAlwaysTheSame_int(int desiredLength) |
||||
|
{ |
||||
|
this.TestSpanPropertyIsAlwaysTheSame<int>(desiredLength); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[MemberData(nameof(LenthValues))] |
||||
|
public void SpanPropertyIsAlwaysTheSame_byte(int desiredLength) |
||||
|
{ |
||||
|
this.TestSpanPropertyIsAlwaysTheSame<byte>(desiredLength, false); |
||||
|
this.TestSpanPropertyIsAlwaysTheSame<byte>(desiredLength, true); |
||||
|
} |
||||
|
|
||||
|
private void TestSpanPropertyIsAlwaysTheSame<T>(int desiredLength, bool testManagedByteBuffer = false) |
||||
|
where T : struct |
||||
|
{ |
||||
|
using (IBuffer<T> buffer = this.Allocate<T>(desiredLength, false, testManagedByteBuffer)) |
||||
|
{ |
||||
|
ref T a = ref buffer.Span.DangerousGetPinnableReference(); |
||||
|
ref T b = ref buffer.Span.DangerousGetPinnableReference(); |
||||
|
ref T c = ref buffer.Span.DangerousGetPinnableReference(); |
||||
|
|
||||
|
Assert.True(Unsafe.AreSame(ref a, ref b)); |
||||
|
Assert.True(Unsafe.AreSame(ref b, ref c)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[MemberData(nameof(LenthValues))] |
||||
|
public void WriteAndReadElements_float(int desiredLength) |
||||
|
{ |
||||
|
this.TestWriteAndReadElements<float>(desiredLength, x => x * 1.2f); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[MemberData(nameof(LenthValues))] |
||||
|
public void WriteAndReadElements_byte(int desiredLength) |
||||
|
{ |
||||
|
this.TestWriteAndReadElements<byte>(desiredLength, x => (byte)(x+1), false); |
||||
|
this.TestWriteAndReadElements<byte>(desiredLength, x => (byte)(x + 1), true); |
||||
|
} |
||||
|
|
||||
|
private void TestWriteAndReadElements<T>(int desiredLength, Func<int, T> getExpectedValue, bool testManagedByteBuffer = false) |
||||
|
where T : struct |
||||
|
{ |
||||
|
using (IBuffer<T> buffer = this.Allocate<T>(desiredLength, false, testManagedByteBuffer)) |
||||
|
{ |
||||
|
T[] expectedVals = new T[buffer.Length()]; |
||||
|
|
||||
|
for (int i = 0; i < buffer.Length(); i++) |
||||
|
{ |
||||
|
Span<T> span = buffer.Span; |
||||
|
expectedVals[i] = getExpectedValue(i); |
||||
|
span[i] = expectedVals[i]; |
||||
|
} |
||||
|
|
||||
|
for (int i = 0; i < buffer.Length(); i++) |
||||
|
{ |
||||
|
Span<T> span = buffer.Span; |
||||
|
Assert.Equal(expectedVals[i], span[i]); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[MemberData(nameof(LenthValues))] |
||||
|
public void IndexingSpan_WhenOutOfRange_Throws_byte(int desiredLength) |
||||
|
{ |
||||
|
this.TestIndexOutOfRangeShouldThrow<byte>(desiredLength, false); |
||||
|
this.TestIndexOutOfRangeShouldThrow<byte>(desiredLength, true); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[MemberData(nameof(LenthValues))] |
||||
|
public void IndexingSpan_WhenOutOfRange_Throws_long(int desiredLength) |
||||
|
{ |
||||
|
this.TestIndexOutOfRangeShouldThrow<long>(desiredLength); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[MemberData(nameof(LenthValues))] |
||||
|
public void IndexingSpan_WhenOutOfRange_Throws_CustomStruct(int desiredLength) |
||||
|
{ |
||||
|
this.TestIndexOutOfRangeShouldThrow<CustomStruct>(desiredLength); |
||||
|
} |
||||
|
|
||||
|
private T TestIndexOutOfRangeShouldThrow<T>(int desiredLength, bool testManagedByteBuffer = false) |
||||
|
where T : struct, IEquatable<T> |
||||
|
{ |
||||
|
var dummy = default(T); |
||||
|
|
||||
|
using (IBuffer<T> buffer = this.Allocate<T>(desiredLength, false, testManagedByteBuffer)) |
||||
|
{ |
||||
|
Assert.ThrowsAny<Exception>( |
||||
|
() => |
||||
|
{ |
||||
|
Span<T> span = buffer.Span; |
||||
|
dummy = span[desiredLength]; |
||||
|
}); |
||||
|
|
||||
|
Assert.ThrowsAny<Exception>( |
||||
|
() => |
||||
|
{ |
||||
|
Span<T> span = buffer.Span; |
||||
|
dummy = span[desiredLength + 1]; |
||||
|
}); |
||||
|
|
||||
|
Assert.ThrowsAny<Exception>( |
||||
|
() => |
||||
|
{ |
||||
|
Span<T> span = buffer.Span; |
||||
|
dummy = span[desiredLength + 42]; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
return dummy; |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData(1)] |
||||
|
[InlineData(7)] |
||||
|
[InlineData(1024)] |
||||
|
[InlineData(6666)] |
||||
|
public void ManagedByteBuffer_ArrayIsCorrect(int desiredLength) |
||||
|
{ |
||||
|
using (IManagedByteBuffer buffer = this.MemoryManager.AllocateManagedByteBuffer(desiredLength)) |
||||
|
{ |
||||
|
ref byte array0 = ref buffer.Array[0]; |
||||
|
ref byte span0 = ref buffer.DangerousGetPinnableReference(); |
||||
|
|
||||
|
Assert.True(Unsafe.AreSame(ref span0, ref array0)); |
||||
|
Assert.True(buffer.Array.Length >= buffer.Span.Length); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
namespace SixLabors.ImageSharp.Tests.Memory |
||||
|
{ |
||||
|
using SixLabors.ImageSharp.Memory; |
||||
|
|
||||
|
public class SimpleManagedMemoryManagerTests |
||||
|
{ |
||||
|
public class BufferTests : BufferTestSuite |
||||
|
{ |
||||
|
public BufferTests() |
||||
|
: base(new SimpleManagedMemoryManager()) |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue