From 302c06b3a63747d89cfbbedb402feb545b6831dd Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 2 Feb 2020 23:04:19 +0100 Subject: [PATCH] add some tests for CopyTo --- .../DiscontiguousBuffers/IMemoryGroup{T}.cs | 20 ++- .../InvalidMemoryOperationException.cs | 3 + .../MemoryGroupExtensions.cs | 16 ++ .../MemoryGroupView{T}.cs | 24 +-- .../MemoryGroup{T}.Consumed.cs | 7 +- .../MemoryGroup{T}.Owned.cs | 7 +- .../DiscontiguousBuffers/MemoryGroup{T}.cs | 39 +++-- .../DiscontiguousBuffers/MemoryGroupIndex.cs | 120 ++++++++++++++ .../MemoryGroupIndexTests.cs | 64 ++++++++ .../MemoryGroupTests.Allocate.cs | 119 ++++++++++++++ .../MemoryGroupTests.CopyTo.cs | 45 ++++++ .../DiscontiguousBuffers/MemoryGroupTests.cs | 148 ++++-------------- 12 files changed, 467 insertions(+), 145 deletions(-) create mode 100644 src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupExtensions.cs create mode 100644 tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupIndex.cs create mode 100644 tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupIndexTests.cs create mode 100644 tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.Allocate.cs create mode 100644 tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.CopyTo.cs diff --git a/src/ImageSharp/Memory/DiscontiguousBuffers/IMemoryGroup{T}.cs b/src/ImageSharp/Memory/DiscontiguousBuffers/IMemoryGroup{T}.cs index a2eafb160..2649b7fb1 100644 --- a/src/ImageSharp/Memory/DiscontiguousBuffers/IMemoryGroup{T}.cs +++ b/src/ImageSharp/Memory/DiscontiguousBuffers/IMemoryGroup{T}.cs @@ -1,3 +1,6 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + using System; using System.Collections.Generic; @@ -12,10 +15,23 @@ namespace SixLabors.ImageSharp.Memory where T : struct { /// - /// Gets the number of elements per contiguous sub-block. + /// Gets the number of elements per contiguous sub-buffer preceding the last buffer. + /// The last buffer is allowed to be smaller. + /// + public int BufferLength { get; } + + /// + /// Gets the aggregate number of elements in the group. /// - public int BufferSize { get; } + public long TotalLength { get; } + /// + /// Gets a value indicating whether the group has been invalidated. + /// + /// + /// Invalidation usually occurs when an image processor capable to alter the image dimensions replaces + /// the image buffers internally. + /// bool IsValid { get; } } } diff --git a/src/ImageSharp/Memory/DiscontiguousBuffers/InvalidMemoryOperationException.cs b/src/ImageSharp/Memory/DiscontiguousBuffers/InvalidMemoryOperationException.cs index df30c2ee2..51ed7e861 100644 --- a/src/ImageSharp/Memory/DiscontiguousBuffers/InvalidMemoryOperationException.cs +++ b/src/ImageSharp/Memory/DiscontiguousBuffers/InvalidMemoryOperationException.cs @@ -1,3 +1,6 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + using System; namespace SixLabors.ImageSharp.Memory diff --git a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupExtensions.cs b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupExtensions.cs new file mode 100644 index 000000000..3e0df15ea --- /dev/null +++ b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupExtensions.cs @@ -0,0 +1,16 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +namespace SixLabors.ImageSharp.Memory +{ + internal static class MemoryGroupExtensions + { + public static void CopyTo(this IMemoryGroup source, IMemoryGroup target) + where T : struct + { + throw new NotImplementedException(); + } + } +} diff --git a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupView{T}.cs b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupView{T}.cs index b1077e254..ec801015a 100644 --- a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupView{T}.cs +++ b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupView{T}.cs @@ -1,3 +1,6 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + using System; using System.Buffers; using System.Collections; @@ -15,7 +18,8 @@ namespace SixLabors.ImageSharp.Memory /// instance becomes invalid, throwing an exception on all operations. /// /// The element type. - internal class MemoryGroupView : IMemoryGroup where T : struct + internal class MemoryGroupView : IMemoryGroup + where T : struct { private readonly Memory.MemoryGroup owner; private readonly MemoryOwnerWrapper[] memoryWrappers; @@ -32,23 +36,25 @@ namespace SixLabors.ImageSharp.Memory } } - public IEnumerator> GetEnumerator() => throw new NotImplementedException(); + public int Count => this.owner.Count; - IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); + public int BufferLength => this.owner.BufferLength; + + public long TotalLength => this.owner.TotalLength; - public int Count { get; } + public bool IsValid { get; internal set; } public Memory this[int index] => throw new NotImplementedException(); - public int BufferSize => this.owner.BufferSize; + public IEnumerator> GetEnumerator() => throw new NotImplementedException(); - public bool IsValid { get; internal set; } + IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); - class MemoryOwnerWrapper : MemoryManager + private class MemoryOwnerWrapper : MemoryManager { - private MemoryGroupView view; + private readonly MemoryGroupView view; - private int index; + private readonly int index; public MemoryOwnerWrapper(MemoryGroupView view, int index) { diff --git a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Consumed.cs b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Consumed.cs index 20afb2d57..4b7f8acae 100644 --- a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Consumed.cs +++ b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Consumed.cs @@ -1,3 +1,6 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + using System; using System.Collections.Generic; @@ -10,8 +13,8 @@ namespace SixLabors.ImageSharp.Memory { private readonly ReadOnlyMemory> source; - public Consumed(ReadOnlyMemory> source, int bufferSize) - : base(bufferSize) + public Consumed(ReadOnlyMemory> source, int bufferLength, long totalLength) + : base(bufferLength, totalLength) { this.source = source; } diff --git a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs index c90a24376..e0dfc6396 100644 --- a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs +++ b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs @@ -1,3 +1,6 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + using System; using System.Buffers; using System.Collections.Generic; @@ -12,8 +15,8 @@ namespace SixLabors.ImageSharp.Memory { private IMemoryOwner[] memoryOwners; - public Owned(IMemoryOwner[] memoryOwners, int bufferSize) - : base(bufferSize) + public Owned(IMemoryOwner[] memoryOwners, int bufferLength, long totalLength) + : base(bufferLength, totalLength) { this.memoryOwners = memoryOwners; } diff --git a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs index 9d36cc8dc..ac43d6847 100644 --- a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs +++ b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs @@ -1,3 +1,6 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + using System; using System.Buffers; using System.Collections; @@ -17,11 +20,17 @@ namespace SixLabors.ImageSharp.Memory { private static readonly int ElementSize = Unsafe.SizeOf(); - private MemoryGroup(int bufferSize) => this.BufferSize = bufferSize; + private MemoryGroup(int bufferLength, long totalLength) + { + this.BufferLength = bufferLength; + this.TotalLength = totalLength; + } public abstract int Count { get; } - public int BufferSize { get; } + public int BufferLength { get; } + + public long TotalLength { get; } public bool IsValid { get; private set; } = true; @@ -51,18 +60,18 @@ namespace SixLabors.ImageSharp.Memory } int numberOfAlignedSegments = blockCapacityInElements / blockAlignment; - int bufferSize = numberOfAlignedSegments * blockAlignment; - if (totalLength > 0 && totalLength < bufferSize) + int bufferLength = numberOfAlignedSegments * blockAlignment; + if (totalLength > 0 && totalLength < bufferLength) { - bufferSize = (int)totalLength; + bufferLength = (int)totalLength; } - int sizeOfLastBuffer = (int)(totalLength % bufferSize); - long bufferCount = totalLength / bufferSize; + int sizeOfLastBuffer = (int)(totalLength % bufferLength); + long bufferCount = totalLength / bufferLength; if (sizeOfLastBuffer == 0) { - sizeOfLastBuffer = bufferSize; + sizeOfLastBuffer = bufferLength; } else { @@ -72,7 +81,7 @@ namespace SixLabors.ImageSharp.Memory var buffers = new IMemoryOwner[bufferCount]; for (int i = 0; i < buffers.Length - 1; i++) { - buffers[i] = allocator.Allocate(bufferSize, allocationOptions); + buffers[i] = allocator.Allocate(bufferLength, allocationOptions); } if (bufferCount > 0) @@ -80,28 +89,30 @@ namespace SixLabors.ImageSharp.Memory buffers[^1] = allocator.Allocate(sizeOfLastBuffer, allocationOptions); } - return new Owned(buffers, bufferSize); + return new Owned(buffers, bufferLength, totalLength); } public static MemoryGroup Wrap(params Memory[] source) => Wrap(source.AsMemory()); public static MemoryGroup Wrap(ReadOnlyMemory> source) { - int bufferSize = source.Length > 0 ? source.Span[0].Length : 0; + int bufferLength = source.Length > 0 ? source.Span[0].Length : 0; for (int i = 1; i < source.Length - 1; i++) { - if (source.Span[i].Length != bufferSize) + if (source.Span[i].Length != bufferLength) { throw new InvalidMemoryOperationException("Wrap: buffers should be uniformly sized!"); } } - if (source.Length > 0 && source.Span[^1].Length > bufferSize) + if (source.Length > 0 && source.Span[^1].Length > bufferLength) { throw new InvalidMemoryOperationException("Wrap: the last buffer is too large!"); } - return new Consumed(source, bufferSize); + long totalLength = bufferLength > 0 ? ((long)bufferLength * (source.Length - 1)) + source.Span[^1].Length : 0; + + return new Consumed(source, bufferLength, totalLength); } // Analogous to current MemorySource.SwapOrCopyContent() diff --git a/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupIndex.cs b/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupIndex.cs new file mode 100644 index 000000000..710f90216 --- /dev/null +++ b/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupIndex.cs @@ -0,0 +1,120 @@ +using System; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers +{ + public struct MemoryGroupIndex : IEquatable + { + public override bool Equals(object obj) => obj is MemoryGroupIndex other && this.Equals(other); + + public override int GetHashCode() => HashCode.Combine(this.BufferLength, this.BufferIndex, this.ElementIndex); + + public int BufferLength { get; } + + public int BufferIndex { get; } + + public int ElementIndex { get; } + + public MemoryGroupIndex(int bufferLength, int bufferIndex, int elementIndex) + { + this.BufferLength = bufferLength; + this.BufferIndex = bufferIndex; + this.ElementIndex = elementIndex; + } + + public static MemoryGroupIndex operator +(MemoryGroupIndex idx, int val) + { + int nextElementIndex = idx.ElementIndex + val; + return new MemoryGroupIndex( + idx.BufferLength, + idx.BufferIndex + (nextElementIndex / idx.BufferLength), + nextElementIndex % idx.BufferLength); + } + + public bool Equals(MemoryGroupIndex other) + { + if (this.BufferLength != other.BufferLength) + { + throw new InvalidOperationException(); + } + + return this.BufferIndex == other.BufferIndex && this.ElementIndex == other.ElementIndex; + } + + public static bool operator ==(MemoryGroupIndex a, MemoryGroupIndex b) => a.Equals(b); + + public static bool operator !=(MemoryGroupIndex a, MemoryGroupIndex b) => !a.Equals(b); + + public static bool operator <(MemoryGroupIndex a, MemoryGroupIndex b) + { + if (a.BufferLength != b.BufferLength) + { + throw new InvalidOperationException(); + } + + if (a.BufferIndex < b.BufferIndex) + { + return true; + } + + if (a.BufferIndex == b.BufferIndex) + { + return a.ElementIndex < b.ElementIndex; + } + + return false; + } + + public static bool operator >(MemoryGroupIndex a, MemoryGroupIndex b) + { + if (a.BufferLength != b.BufferLength) + { + throw new InvalidOperationException(); + } + + if (a.BufferIndex > b.BufferIndex) + { + return true; + } + + if (a.BufferIndex == b.BufferIndex) + { + return a.ElementIndex > b.ElementIndex; + } + + return false; + } + } + + internal static class MemoryGroupIndexExtensions + { + public static T GetElementAt(this MemoryGroup group, MemoryGroupIndex idx) + where T : struct + { + return group[idx.BufferIndex].Span[idx.ElementIndex]; + } + + public static void SetElementAt(this MemoryGroup group, MemoryGroupIndex idx, T value) + where T : struct + { + group[idx.BufferIndex].Span[idx.ElementIndex] = value; + } + + public static MemoryGroupIndex MinIndex(this MemoryGroup group) + where T : struct + { + return new MemoryGroupIndex(group.BufferLength, 0, 0); + } + + public static MemoryGroupIndex MaxIndex(this MemoryGroup group) + where T : struct + { + if (group.Count == 0) + { + return new MemoryGroupIndex(group.BufferLength, 0, 0); + } + + return new MemoryGroupIndex(group.BufferLength, group.Count - 1, group[^1].Length - 1); + } + } +} diff --git a/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupIndexTests.cs b/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupIndexTests.cs new file mode 100644 index 000000000..6a9d322f6 --- /dev/null +++ b/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupIndexTests.cs @@ -0,0 +1,64 @@ +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers +{ + public class MemoryGroupIndexTests + { + [Fact] + public void Equal() + { + var a = new MemoryGroupIndex(10, 1, 3); + var b = new MemoryGroupIndex(10, 1, 3); + + Assert.True(a.Equals(b)); + Assert.True(a == b); + Assert.False(a != b); + Assert.False(a < b); + Assert.False(a > b); + } + + [Fact] + public void SmallerBufferIndex() + { + var a = new MemoryGroupIndex(10, 3, 3); + var b = new MemoryGroupIndex(10, 5, 3); + + Assert.False(a == b); + Assert.True(a != b); + Assert.True(a < b); + Assert.False(a > b); + } + + [Fact] + public void SmallerElementIndex() + { + var a = new MemoryGroupIndex(10, 3, 3); + var b = new MemoryGroupIndex(10, 3, 9); + + Assert.False(a == b); + Assert.True(a != b); + Assert.True(a < b); + Assert.False(a > b); + } + + [Fact] + public void Increment() + { + var a = new MemoryGroupIndex(10, 3, 3); + a += 1; + Assert.Equal(new MemoryGroupIndex(10, 3, 4), a); + } + + [Fact] + public void Increment_OverflowBuffer() + { + var a = new MemoryGroupIndex(10, 5, 3); + var b = new MemoryGroupIndex(10, 5, 9); + a += 8; + b += 1; + + Assert.Equal(new MemoryGroupIndex(10, 6, 1), a); + Assert.Equal(new MemoryGroupIndex(10, 6, 0), b); + } + } +} diff --git a/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.Allocate.cs b/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.Allocate.cs new file mode 100644 index 000000000..1a617d396 --- /dev/null +++ b/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.Allocate.cs @@ -0,0 +1,119 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Collections.Generic; +using System.Linq; +using SixLabors.ImageSharp.Memory; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers +{ + public partial class MemoryGroupTests + { + public class Allocate : MemoryGroupTestsBase + { +#pragma warning disable SA1509 + public static TheoryData AllocateData = + new TheoryData() + { + { default(S5), 22, 4, 4, 1, 4, 4 }, + { default(S5), 22, 4, 7, 2, 4, 3 }, + { default(S5), 22, 4, 8, 2, 4, 4 }, + { default(S5), 22, 4, 21, 6, 4, 1 }, + { default(S5), 22, 4, 0, 0, 4, -1 }, + + { default(S4), 50, 12, 12, 1, 12, 12 }, + { default(S4), 50, 7, 12, 2, 7, 5 }, + { default(S4), 50, 6, 12, 1, 12, 12 }, + { default(S4), 50, 5, 12, 2, 10, 2 }, + { default(S4), 50, 4, 12, 1, 12, 12 }, + { default(S4), 50, 3, 12, 1, 12, 12 }, + { default(S4), 50, 2, 12, 1, 12, 12 }, + { default(S4), 50, 1, 12, 1, 12, 12 }, + + { default(S4), 50, 12, 13, 2, 12, 1 }, + { default(S4), 50, 7, 21, 3, 7, 7 }, + { default(S4), 50, 7, 23, 4, 7, 2 }, + { default(S4), 50, 6, 13, 2, 12, 1 }, + + { default(short), 200, 50, 49, 1, 49, 49 }, + { default(short), 200, 50, 1, 1, 1, 1 }, + { default(byte), 1000, 512, 2047, 4, 512, 511 } + }; + + [Theory] + [MemberData(nameof(AllocateData))] + public void BufferSizesAreCorrect( + T dummy, + int bufferCapacity, + int bufferAlignment, + long totalLength, + int expectedNumberOfBuffers, + int expectedBufferSize, + int expectedSizeOfLastBuffer) + where T : struct + { + this.MemoryAllocator.BufferCapacity = bufferCapacity; + + // Act: + using var g = MemoryGroup.Allocate(this.MemoryAllocator, totalLength, bufferAlignment); + + // Assert: + Assert.Equal(expectedNumberOfBuffers, g.Count); + Assert.Equal(expectedBufferSize, g.BufferLength); + if (g.Count == 0) + { + return; + } + + for (int i = 0; i < g.Count - 1; i++) + { + Assert.Equal(g[i].Length, expectedBufferSize); + } + + Assert.Equal(g.Last().Length, expectedSizeOfLastBuffer); + } + + [Fact] + public void WhenBlockAlignmentIsOverCapacity_Throws_InvalidMemoryOperationException() + { + this.MemoryAllocator.BufferCapacity = 84; // 42 * Int16 + + Assert.Throws(() => + { + MemoryGroup.Allocate(this.MemoryAllocator, 50, 43); + }); + } + + [Theory] + [InlineData(AllocationOptions.None)] + [InlineData(AllocationOptions.Clean)] + public void MemoryAllocatorIsUtilizedCorrectly(AllocationOptions allocationOptions) + { + this.MemoryAllocator.BufferCapacity = 200; + + HashSet bufferHashes; + + int expectedBlockCount = 5; + using (var g = MemoryGroup.Allocate(this.MemoryAllocator, 500, 100, allocationOptions)) + { + IReadOnlyList allocationLog = this.MemoryAllocator.AllocationLog; + Assert.Equal(expectedBlockCount, allocationLog.Count); + bufferHashes = allocationLog.Select(l => l.HashCodeOfBuffer).ToHashSet(); + Assert.Equal(expectedBlockCount, bufferHashes.Count); + Assert.Equal(0, this.MemoryAllocator.ReturnLog.Count); + + for (int i = 0; i < expectedBlockCount; i++) + { + Assert.Equal(allocationOptions, allocationLog[i].AllocationOptions); + Assert.Equal(100, allocationLog[i].Length); + Assert.Equal(200, allocationLog[i].LengthInBytes); + } + } + + Assert.Equal(expectedBlockCount, this.MemoryAllocator.ReturnLog.Count); + Assert.True(bufferHashes.SetEquals(this.MemoryAllocator.ReturnLog.Select(l => l.HashCodeOfBuffer))); + } + } + } +} diff --git a/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.CopyTo.cs b/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.CopyTo.cs new file mode 100644 index 000000000..206d6499b --- /dev/null +++ b/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.CopyTo.cs @@ -0,0 +1,45 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Memory; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers +{ + public partial class MemoryGroupTests + { + public class CopyTo : MemoryGroupTestsBase + { + public static readonly TheoryData WhenSourceBufferIsShorterOrEqual_Data = + new TheoryData() + { + { 20, 5, 20, 4 }, + { 20, 4, 20, 5 }, + { 18, 6, 20, 5 }, + { 19, 10, 20, 10 }, + { 21, 10, 22, 2 }, + { 1, 5, 5, 4 }, + }; + + [Theory] + [MemberData(nameof(WhenSourceBufferIsShorterOrEqual_Data))] + public void WhenSourceBufferIsShorterOrEqual(int srcTotal, int srcBufLen, int trgTotal, int trgBufLen) + { + using MemoryGroup src = this.CreateTestGroup(srcTotal, srcBufLen, true); + using MemoryGroup trg = this.CreateTestGroup(trgTotal, trgBufLen, false); + + src.CopyTo(trg); + + MemoryGroupIndex i = src.MinIndex(); + MemoryGroupIndex j = trg.MinIndex(); + for (; i < src.MaxIndex(); i += 1, j += 1) + { + int a = src.GetElementAt(i); + int b = src.GetElementAt(j); + + Assert.Equal(a, b); + } + } + } + } +} diff --git a/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.cs b/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.cs index c12c09b7a..b9fea3497 100644 --- a/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.cs +++ b/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.cs @@ -1,128 +1,18 @@ -using System.Collections.Generic; -using System.Linq; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; using Xunit; -using Xunit.Sdk; namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers { - public class MemoryGroupTests + public partial class MemoryGroupTests : MemoryGroupTestsBase { - private readonly TestMemoryAllocator memoryAllocator = new TestMemoryAllocator(); - - public class Allocate - { - private readonly TestMemoryAllocator memoryAllocator = new TestMemoryAllocator(); - -#pragma warning disable SA1509 - public static TheoryData AllocateData = - new TheoryData() - { - { default(S5), 22, 4, 4, 1, 4, 4 }, - { default(S5), 22, 4, 7, 2, 4, 3 }, - { default(S5), 22, 4, 8, 2, 4, 4 }, - { default(S5), 22, 4, 21, 6, 4, 1 }, - { default(S5), 22, 4, 0, 0, 4, -1 }, - - { default(S4), 50, 12, 12, 1, 12, 12 }, - { default(S4), 50, 7, 12, 2, 7, 5 }, - { default(S4), 50, 6, 12, 1, 12, 12 }, - { default(S4), 50, 5, 12, 2, 10, 2 }, - { default(S4), 50, 4, 12, 1, 12, 12 }, - { default(S4), 50, 3, 12, 1, 12, 12 }, - { default(S4), 50, 2, 12, 1, 12, 12 }, - { default(S4), 50, 1, 12, 1, 12, 12 }, - - { default(S4), 50, 12, 13, 2, 12, 1 }, - { default(S4), 50, 7, 21, 3, 7, 7 }, - { default(S4), 50, 7, 23, 4, 7, 2 }, - { default(S4), 50, 6, 13, 2, 12, 1 }, - - { default(short), 200, 50, 49, 1, 49, 49 }, - { default(short), 200, 50, 1, 1, 1, 1 }, - { default(byte), 1000, 512, 2047, 4, 512, 511 } - }; - - [Theory] - [MemberData(nameof(AllocateData))] - public void BufferSizesAreCorrect( - T dummy, - int bufferCapacity, - int bufferAlignment, - long totalLength, - int expectedNumberOfBuffers, - int expectedBufferSize, - int expectedSizeOfLastBuffer) - where T : struct - { - this.memoryAllocator.BufferCapacity = bufferCapacity; - - // Act: - using var g = MemoryGroup.Allocate(this.memoryAllocator, totalLength, bufferAlignment); - - // Assert: - Assert.Equal(expectedNumberOfBuffers, g.Count); - Assert.Equal(expectedBufferSize, g.BufferSize); - if (g.Count == 0) - { - return; - } - - for (int i = 0; i < g.Count - 1; i++) - { - Assert.Equal(g[i].Length, expectedBufferSize); - } - - Assert.Equal(g.Last().Length, expectedSizeOfLastBuffer); - } - - [Fact] - public void WhenBlockAlignmentIsOverCapacity_Throws_InvalidMemoryOperationException() - { - this.memoryAllocator.BufferCapacity = 84; // 42 * Int16 - - Assert.Throws(() => - { - MemoryGroup.Allocate(this.memoryAllocator, 50, 43); - }); - } - - [Theory] - [InlineData(AllocationOptions.None)] - [InlineData(AllocationOptions.Clean)] - public void MemoryAllocatorIsUtilizedCorrectly(AllocationOptions allocationOptions) - { - this.memoryAllocator.BufferCapacity = 200; - - HashSet bufferHashes; - - int expectedBlockCount = 5; - using (var g = MemoryGroup.Allocate(this.memoryAllocator, 500, 100, allocationOptions)) - { - IReadOnlyList allocationLog = this.memoryAllocator.AllocationLog; - Assert.Equal(expectedBlockCount, allocationLog.Count); - bufferHashes = allocationLog.Select(l => l.HashCodeOfBuffer).ToHashSet(); - Assert.Equal(expectedBlockCount, bufferHashes.Count); - Assert.Equal(0, this.memoryAllocator.ReturnLog.Count); - - for (int i = 0; i < expectedBlockCount; i++) - { - Assert.Equal(allocationOptions, allocationLog[i].AllocationOptions); - Assert.Equal(100, allocationLog[i].Length); - Assert.Equal(200, allocationLog[i].LengthInBytes); - } - } - - Assert.Equal(expectedBlockCount, this.memoryAllocator.ReturnLog.Count); - Assert.True(bufferHashes.SetEquals(this.memoryAllocator.ReturnLog.Select(l => l.HashCodeOfBuffer))); - } - } - [Fact] public void IsValid_TrueAfterCreation() { - using var g = MemoryGroup.Allocate(this.memoryAllocator, 10, 100); + using var g = MemoryGroup.Allocate(this.MemoryAllocator, 10, 100); Assert.True(g.IsValid); } @@ -130,12 +20,13 @@ namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers [Fact] public void IsValid_FalseAfterDisposal() { - using var g = MemoryGroup.Allocate(this.memoryAllocator, 10, 100); + using var g = MemoryGroup.Allocate(this.MemoryAllocator, 10, 100); g.Dispose(); Assert.False(g.IsValid); } + [StructLayout(LayoutKind.Sequential, Size = 5)] private struct S5 { @@ -148,4 +39,29 @@ namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers public override string ToString() => "S4"; } } + + public abstract class MemoryGroupTestsBase + { + internal readonly TestMemoryAllocator MemoryAllocator = new TestMemoryAllocator(); + + internal MemoryGroup CreateTestGroup(long totalLength, int bufferLength, bool fillSequence = false) + { + this.MemoryAllocator.BufferCapacity = bufferLength; + var g = MemoryGroup.Allocate(this.MemoryAllocator, totalLength, bufferLength); + + if (!fillSequence) + { + return g; + } + + int j = 1; + for (MemoryGroupIndex i = g.MinIndex(); i < g.MaxIndex(); i += 1) + { + g.SetElementAt(i, j); + j++; + } + + return g; + } + } }