RecyclableLongList.Compatibility.List.CopyTo() implemented. Tests extended.

This commit is contained in:
mlemanczyk committed 2023-12-10 19:32:02 +01:00
1 parent 89626511d6
commit f77d5024c3
4 files changed
+120 -28

No files matched your search

@@ -13,7 +13,7 @@
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
<PackageReference Include="Recyclable.Collections" Version="0.0.6.1" />
<PackageReference Include="Recyclable.Collections.TestData" Version="0.0.3.1" />
<PackageReference Include="Recyclable.Collections.TestData" Version="0.0.3.2" />
<PackageReference Include="xunit" Version="2.5.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
@@ -35,8 +35,8 @@ namespace Recyclable.CollectionsTests
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
public void BinarySearchShouldFindAllItemsWhenConstrainedRange(string testCase, IEnumerable<long> testData, int itemsCount, int minBlockSize, in long[] itemIndexes)
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeWithItemIndexWithRangeVariants), MemberType = typeof(RecyclableLongListTestData))]
public void BinarySearchShouldFindAllItemsWhenConstrainedRange(string testCase, IEnumerable<long> testData, int itemsCount, int minBlockSize, in (long ItemIndex, long RangedItemsCount)[] itemRanges)
{
_ = testData.Any().Should().BeTrue("we need items on the list that we can look for");
@@ -45,15 +45,13 @@ namespace Recyclable.CollectionsTests
var comparer = Comparer<long>.Default;
var expectedData = testData.ToList();
foreach (var itemIndex in itemIndexes)
foreach (var (itemIndex, rangedItemsCount) in itemRanges)
{
var rangeSize = (int)Math.Min(itemsCount - itemIndex, 4);
var expectedItem = expectedData[(int)itemIndex];
var startItemIndex = (int)(rangeSize > 2 ? Math.Max(0, itemIndex - 2) : itemIndex);
var expected = expectedData.BinarySearch(startItemIndex, rangeSize, expectedItem, comparer);
var expected = expectedData.BinarySearch((int)itemIndex, (int)rangedItemsCount, expectedItem, comparer);
// Act
var actual = list.BinarySearch(startItemIndex, rangeSize, expectedItem, comparer);
var actual = list.BinarySearch((int)itemIndex, (int)rangedItemsCount, expectedItem, comparer);
// Validate
_ = actual.Should().Be(expected);
@@ -111,8 +109,8 @@ namespace Recyclable.CollectionsTests
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
public void BinarySearchShouldNotFindNonExistingItemsWhenConstrainedRange(string testCase, IEnumerable<long> testData, int itemsCount, int minBlockSize, in long[] itemIndexes)
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeWithItemIndexWithRangeVariants), MemberType = typeof(RecyclableLongListTestData))]
public void BinarySearchShouldNotFindNonExistingItemsWhenConstrainedRange(string testCase, IEnumerable<long> testData, int itemsCount, int minBlockSize, in (long ItemIndex, long RangedItemsCount)[] itemRanges)
{
_ = testData.Any().Should().BeTrue("we need items on the list that we can look for");
@@ -120,20 +118,19 @@ namespace Recyclable.CollectionsTests
var comparer = Comparer<long>.Default;
var expectedData = testData.ToList();
foreach (var itemIndex in itemIndexes)
foreach (var (itemIndex, rangedItemsCount) in itemRanges)
{
using var list = new RecyclableLongList<long>(testData, minBlockSize, initialCapacity: itemsCount);
list.RemoveAt(itemIndex);
var expectedItem = expectedData[(int)itemIndex];
var expectedRangedData = testData.ToList();
expectedRangedData.RemoveAt((int)itemIndex);
var expectedRangeSize = itemIndex + rangedItemsCount < expectedRangedData.Count ? rangedItemsCount : expectedRangedData.Count - itemIndex;
// Now we have 1 element less on the lists
var rangeSize = (int)Math.Max(Math.Min(itemsCount - itemIndex - 2, 4), 0);
int startItemIndex = (int)(rangeSize > 2 ? Math.Max(0, itemIndex - 2 - 1) : Math.Max(0, itemIndex - 1));
var expected = expectedRangedData.BinarySearch(startItemIndex, rangeSize, expectedItem, comparer);
var expected = expectedRangedData.BinarySearch((int)itemIndex, (int)expectedRangeSize, expectedItem, comparer);
// Act
var actual = list.BinarySearch(startItemIndex, rangeSize, expectedItem, comparer);
var actual = list.BinarySearch((int)itemIndex, (int)expectedRangeSize, expectedItem, comparer);
// Validate
_ = actual.Should().Be(expected);
@@ -212,6 +209,9 @@ namespace Recyclable.CollectionsTests
Assert.Throws<ArgumentException>(() => _ = expectedData.BinarySearch((int)itemIndex + 1, itemsCount, expectedItem, comparer));
Assert.Throws<ArgumentException>(() => _ = list.BinarySearch((int)itemIndex + 1, itemsCount, expectedItem, comparer));
Assert.Throws<ArgumentOutOfRangeException>(() => _ = expectedData.BinarySearch((int)itemIndex + 1, -1, expectedItem, comparer));
Assert.Throws<ArgumentOutOfRangeException>(() => _ = list.BinarySearch((int)itemIndex + 1, -1, expectedItem, comparer));
}
}
@@ -232,32 +232,41 @@ namespace Recyclable.CollectionsTests
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
public void CopyToShouldCopyAllItemsInTheCorrectOrderWhenConstrainedRange(string testCase, IEnumerable<long> testData, int itemsCount, int minBlockSize, in long[] itemIndexes)
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeWithItemIndexWithRangeVariants), MemberType = typeof(RecyclableLongListTestData))]
public void CopyToShouldCopyAllItemsInTheCorrectOrderWhenConstrainedIndex(string testCase, IEnumerable<long> testData, int itemsCount, int minBlockSize, in (long ItemIndex, long RangedItemsCount)[] itemIndexRanges)
{
// Prepare
var expectedData = testData.ToList();
foreach (var itemIndex in itemIndexes)
foreach (var (itemIndex, _) in itemIndexRanges)
{
var rangedItemsCount = (int)Math.Max(itemIndex, 1);
TestCopyTo(0, rangedItemsCount);
using var list = new RecyclableLongList<long>(testData, minBlockSize, initialCapacity: itemsCount);
long[] expectedItems = new long[expectedData.Count];
expectedData.CopyTo((int)itemIndex, expectedItems, (int)itemIndex, (int)(expectedData.Count - itemIndex));
long[] actualItems = new long[expectedData.Count];
rangedItemsCount = (int)(itemsCount - itemIndex);
TestCopyTo((int)itemIndex, rangedItemsCount);
// Act
list.CopyTo((int)itemIndex, actualItems, (int)itemIndex);
rangedItemsCount = 1;
TestCopyTo((int)itemIndex, rangedItemsCount);
// Validate
_ = actualItems.Should().Equal(expectedItems);
}
}
void TestCopyTo(int itemIndex, int rangedItemsCount)
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeWithItemIndexWithRangeVariants), MemberType = typeof(RecyclableLongListTestData))]
public void CopyToShouldCopyAllItemsInTheCorrectOrderWhenConstrainedRange(string testCase, IEnumerable<long> testData, int itemsCount, int minBlockSize, in (long ItemIndex, long RangedItemsCount)[] itemIndexRanges)
{
// Prepare
var expectedData = testData.ToList();
foreach (var (itemIndex, rangedItemsCount) in itemIndexRanges)
{
using var list = new RecyclableLongList<long>(testData, minBlockSize, initialCapacity: itemsCount);
long[] expectedItems = new long[rangedItemsCount + itemIndex];
expectedData.CopyTo(itemIndex, expectedItems, itemIndex, rangedItemsCount);
expectedData.CopyTo((int)itemIndex, expectedItems, (int)itemIndex, (int)rangedItemsCount);
long[] actualItems = new long[rangedItemsCount + itemIndex];
// Act
list.CopyTo(itemIndex, actualItems, itemIndex, rangedItemsCount);
list.CopyTo((int)itemIndex, actualItems, (int)itemIndex, (int)rangedItemsCount);
// Validate
_ = actualItems.Should().Equal(expectedItems);
@@ -282,6 +291,36 @@ namespace Recyclable.CollectionsTests
_ = copiedItems.Should().Equal(testData);
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
public void CopyToShouldThrowArgumentOutOfRangeWhenOutsideRange(string testCase, IEnumerable<long> testData, int itemsCount, int minBlockSize, in long[] itemIndexes)
{
_ = testData.Any().Should().BeTrue("we need items on the list that we can look for");
// Prepare
using var list = new RecyclableLongList<long>(testData, minBlockSize, initialCapacity: itemsCount);
var comparer = Comparer<long>.Default;
var expectedData = testData.ToList();
var actual = new long[itemsCount];
foreach (var itemIndex in itemIndexes)
{
// Act && Validate
Assert.Throws<ArgumentOutOfRangeException>(() => expectedData.CopyTo(-1, actual, 0, (int)itemIndex));
Assert.Throws<ArgumentOutOfRangeException>(() => list.CopyTo(-1, actual, 0, (int)itemIndex));
Assert.Throws<ArgumentOutOfRangeException>(() => expectedData.CopyTo(0, actual, -1, (int)itemIndex));
Assert.Throws<ArgumentOutOfRangeException>(() => list.CopyTo(0, actual, -1, (int)itemIndex));
Assert.Throws<ArgumentException>(() => expectedData.CopyTo(0, actual, (int)itemIndex + 1, itemsCount));
Assert.Throws<ArgumentException>(() => list.CopyTo(0, actual, (int)itemIndex + 1, itemsCount));
Assert.Throws<ArgumentOutOfRangeException>(() => expectedData.CopyTo(0, actual, (int)itemIndex + 1, -1));
Assert.Throws<ArgumentOutOfRangeException>(() => list.CopyTo(0, actual, (int)itemIndex + 1, -1));
}
}
[Theory]
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
public void ExistsShouldFindAllItems(string testCase, IEnumerable<long> testData, int itemsCount, int minBlockSize, in long[] itemIndexes)
@@ -302,8 +302,36 @@ namespace Recyclable.Collections
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
//public static void CopyTo<T>(this RecyclableLongList<T> list, int index, T[] array, int arrayIndex, int count) => RecyclableLongList<T>.Helpers.CopyTo(list._memoryBlocks, index, list._blockSize, count, array, arrayIndex);
public static void CopyTo<T>(this RecyclableLongList<T> list, int index, T[] array, int arrayIndex, int count) => DoCopyTo(list._memoryBlocks, index, list._blockSize, count, array, arrayIndex);
public static void CopyTo<T>(this RecyclableLongList<T> list, int index, T[] array, int arrayIndex) => DoCopyTo(list._memoryBlocks, index, list._blockSize, list._longCount - index, array, arrayIndex);
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static void CopyTo<T>(this RecyclableLongList<T> list, int index, T[] array, int arrayIndex, int count)
{
if (index < 0)
{
ThrowHelper.ThrowArgumentOutOfRangeException_Index();
}
if (count < 0)
{
ThrowHelper.ThrowArgumentOutOfRangeException_Count();
}
if (arrayIndex < 0)
{
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(arrayIndex), "Parameter `arrayIndex` has invalid value. It must be within the no. of elements in the collection");
}
if (index + count > list._longCount || arrayIndex + count > list._longCount)
{
Compatibility.List.ThrowHelper.ThrowArgumentException_Count();
}
DoCopyTo(list._memoryBlocks, index, list._blockSize, count, array, arrayIndex);
}
public static void ForEach<T>(this RecyclableLongList<T> list, Action<T> action)
{
//int sourceItemsCount = list._count;
@@ -0,0 +1,25 @@
#pragma warning disable IDE0022, CA2208
namespace Recyclable.Collections.Compatibility.List
{
[Obsolete("This class is temporary until new Recyclable.Collections is released")]
internal static class ThrowHelper
{
public static void ThrowArgumentException(string argumentName) => throw new ArgumentException("Parameter has invalid value. ", argumentName);
public static void ThrowArgumentException(string argumentName, Exception? innerException) => throw new ArgumentException("Parameter has invalid value. ", argumentName, innerException);
public static void ThrowArgumentException(string argumentName, string message) => throw new ArgumentException(message, argumentName);
public static void ThrowArgumentException(string argumentName, string message, Exception? innerException) => throw new ArgumentException(message, argumentName, innerException);
public static void ThrowArgumentException_Index() => throw new ArgumentException("Parameter has invalid value", "index");
public static void ThrowArgumentException_Index(Exception? innerException) => throw new ArgumentException("Parameter has invalid value", "index", innerException);
public static void ThrowArgumentException_Count() => throw new ArgumentException("Parameter has invalid value", "count");
public static void ThrowArgumentException_Count(Exception? innerException) => throw new ArgumentException("Parameter has invalid value", "count", innerException);
public static void ThrowArgumentOutOfRangeException(string argumentName) => throw new ArgumentOutOfRangeException(argumentName);
public static void ThrowArgumentOutOfRangeException(string argumentName, Exception? innerException) => throw new ArgumentOutOfRangeException($"Argument {argumentName} is out of range", innerException);
public static void ThrowArgumentOutOfRangeException(string argumentName, string message) => throw new ArgumentOutOfRangeException(argumentName, message);
public static void ThrowWrongReturnedArraySizeException(int returnedLength, int blockSize) => throw new InvalidOperationException($"The array size {returnedLength} doesn't match the size {blockSize} of the pool buffer and cannot be returned to this pool.");
public static void ThrowArgumentOutOfRangeException_Index() => throw new ArgumentOutOfRangeException("index", "Parameter `index` has invalid value. It must be within the no. of elements in the collection");
public static void ThrowArgumentOutOfRangeException_Index(Exception? innerException) => throw new ArgumentOutOfRangeException("Parameter `index` has invalid value. It must be within the no. of elements in the collection", innerException);
public static void ThrowArgumentOutOfRangeException_Count() => throw new ArgumentOutOfRangeException("count", "Parameter `count` has invalid value. Combined with index, it must be within the no. of elements in the collection");
public static void ThrowArgumentOutOfRangeException_Count(Exception? innerException) => throw new ArgumentOutOfRangeException("Parameter `count` has invalid value. Combined with index, it must be within the no. of elements in the collection", innerException);
}
}