Unit tests extended. Various fixes. README.md updated.
This commit is contained in:
1 parent
98347f0727
commit
e941fdf8db
8 files changed
+779
-202
No files matched your search
@@ -8,15 +8,32 @@ namespace Recyclable.CollectionsTests
|
||||
{
|
||||
public class RecyclableListTests
|
||||
{
|
||||
[Fact]
|
||||
public void AddRangeShouldAcceptNulls()
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableList<long?>();
|
||||
|
||||
// Act
|
||||
list.AddRange(new long?[] { null, default });
|
||||
|
||||
// Validate
|
||||
_ = list.Should().HaveCount(2).And.AllSatisfy(x => x.Should().BeNull());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void AddRangeShouldAddItemsInCorrectOrder(string testCase, IEnumerable<long> testData, long itemsCount)
|
||||
public void AddRangeShouldAddItemsInCorrectOrder(string testCase, IEnumerable<long> testData, int itemsCount)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableList<long>();
|
||||
|
||||
// Act
|
||||
if (testData is long[] testDataArray)
|
||||
if (testCase.Contains("ReadOnlySpan[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.AddRange(new ReadOnlySpan<long>((long[])testData));
|
||||
}
|
||||
else if (testData is long[] testDataArray)
|
||||
{
|
||||
list.AddRange(testDataArray);
|
||||
}
|
||||
@@ -46,20 +63,24 @@ namespace Recyclable.CollectionsTests
|
||||
}
|
||||
|
||||
// Validate
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo((int)itemsCount, "when capacity == 0, then we should allocate as much memory as needed, only");
|
||||
_ = list.Count.Should().Be((int)itemsCount);
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo(itemsCount);
|
||||
_ = list.Count.Should().Be(itemsCount);
|
||||
_ = list.Should().Equal(testData);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.EmptySourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void AddRangeShouldDoNothingWhenSourceIsEmpty(string testCase, IEnumerable<long> testData, long itemsCount)
|
||||
public void AddRangeShouldDoNothingWhenSourceIsEmpty(string testCase, IEnumerable<long> testData, int itemsCount)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableList<long>();
|
||||
|
||||
// Act
|
||||
if (testData is long[] testDataArray)
|
||||
if (testCase.Contains("ReadOnlySpan[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.AddRange(new ReadOnlySpan<long>((long[])testData));
|
||||
}
|
||||
else if (testData is long[] testDataArray)
|
||||
{
|
||||
list.AddRange(testDataArray);
|
||||
}
|
||||
@@ -90,19 +111,25 @@ namespace Recyclable.CollectionsTests
|
||||
|
||||
// Validate
|
||||
_ = list.Capacity.Should().Be(RecyclableDefaults.InitialCapacity);
|
||||
_ = list.Count.Should().Be(0);
|
||||
_ = list.Count.Should().Be(itemsCount);
|
||||
_ = list.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void AddRangeShouldNotOverrideItems(string testCase, IEnumerable<long> testData, long itemsCount)
|
||||
public void AddRangeShouldNotOverrideItems(string testCase, IEnumerable<long> testData, int itemsCount)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableList<long>();
|
||||
|
||||
// Act
|
||||
if (testData is long[] testDataArray)
|
||||
if (testCase.Contains("ReadOnlySpan[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var readOnlySpan = new ReadOnlySpan<long>((long[])testData);
|
||||
list.AddRange(readOnlySpan);
|
||||
list.AddRange(readOnlySpan);
|
||||
}
|
||||
else if (testData is long[] testDataArray)
|
||||
{
|
||||
list.AddRange(testDataArray);
|
||||
list.AddRange(testDataArray);
|
||||
@@ -138,23 +165,18 @@ namespace Recyclable.CollectionsTests
|
||||
}
|
||||
|
||||
// Validate
|
||||
var expectedData = testData.Concat(testData).ToArray();
|
||||
|
||||
if (itemsCount > 0)
|
||||
{
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo((int)itemsCount * 2);
|
||||
}
|
||||
|
||||
_ = list.Count.Should().Be(expectedData.Length);
|
||||
var expectedData = testData.Concat(testData);
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo(itemsCount * 2);
|
||||
_ = list.Count.Should().Be(itemsCount * 2);
|
||||
_ = list.Should().Equal(expectedData);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void AddShouldAcceptDuplicates(string testCase, IEnumerable<long> testData, long itemsCount)
|
||||
public void AddShouldAcceptDuplicates(string testCase, IEnumerable<long> testData, int itemsCount)
|
||||
{
|
||||
// Prepare
|
||||
testData = testData.Concat(testData).ToArray();
|
||||
testData = testData.Concat(testData);
|
||||
|
||||
// Act
|
||||
using var list = new RecyclableList<long>();
|
||||
@@ -164,13 +186,31 @@ namespace Recyclable.CollectionsTests
|
||||
}
|
||||
|
||||
// Validate
|
||||
_ = list.Count.Should().Be((int)(itemsCount * 2));
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo(itemsCount * 2);
|
||||
_ = list.Count.Should().Be(itemsCount * 2);
|
||||
_ = list.Should().Equal(testData);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddShouldAcceptNulls()
|
||||
{
|
||||
// Prepare
|
||||
#pragma warning disable IDE0028
|
||||
using var list = new RecyclableList<long?>();
|
||||
|
||||
// Act
|
||||
list.Add(null);
|
||||
list.Add(default);
|
||||
#pragma warning restore IDE0028
|
||||
|
||||
// Validate
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo(RecyclableDefaults.InitialCapacity);
|
||||
_ = list.Should().AllSatisfy(x => x.Should().BeNull());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void AddShouldAddItems(string testCase, IEnumerable<long> testData, long itemsCount)
|
||||
public void AddShouldAddItems(string testCase, IEnumerable<long> testData, int itemsCount)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableList<long>();
|
||||
@@ -183,14 +223,14 @@ namespace Recyclable.CollectionsTests
|
||||
}
|
||||
|
||||
// Validate
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo((int)itemsCount);
|
||||
_ = list.Count.Should().Be((int)itemsCount);
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo(itemsCount);
|
||||
_ = list.Count.Should().Be(itemsCount);
|
||||
_ = list.Should().Equal(testData);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void AddShouldAddItemWhenAfterAddRange(string testCase, IEnumerable<long> testData, long itemsCount)
|
||||
public void AddShouldAddItemWhenAfterAddRange(string testCase, IEnumerable<long> testData, int itemsCount)
|
||||
{
|
||||
// Act
|
||||
using var list = new RecyclableList<long>();
|
||||
@@ -201,34 +241,56 @@ namespace Recyclable.CollectionsTests
|
||||
}
|
||||
|
||||
// Validate
|
||||
_ = list.Count.Should().Be((int)(itemsCount * 2));
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo(itemsCount * 2);
|
||||
_ = list.Count.Should().Be(itemsCount * 2);
|
||||
_ = list.Should().Equal(testData.Concat(testData));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceRefDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void ClearShouldClearItemsWhenReferenceType(string testCase, IEnumerable<object> testData, int itemsCount)
|
||||
{
|
||||
if (itemsCount == 0)
|
||||
{
|
||||
/// Clearing empty list is tested in <see cref="ClearShouldSucceed"/>.
|
||||
return;
|
||||
}
|
||||
|
||||
// Arrange
|
||||
using var list = new RecyclableList<object>(testData, itemsCount);
|
||||
_ = list.AsArray[..itemsCount].Should().AllSatisfy(x => x.Should().NotBeNull());
|
||||
|
||||
// Act
|
||||
list.Clear();
|
||||
|
||||
// Assert
|
||||
_ = list.AsArray[..itemsCount].Should().AllSatisfy(x => x.Should().BeNull());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void ClearShouldRemoveAllItems(string testCase, IEnumerable<long> testData, long itemsCount)
|
||||
public void ClearShouldRemoveAllItems(string testCase, IEnumerable<long> testData, int itemsCount)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableList<long>(testData, (int)itemsCount);
|
||||
_ = list.Count.Should().Be((int)itemsCount);
|
||||
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||
_ = list.Count.Should().Be(itemsCount);
|
||||
|
||||
// Act
|
||||
list.Clear();
|
||||
list.Clear();
|
||||
|
||||
// Validate
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo((int)itemsCount);
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo(itemsCount);
|
||||
_ = list.Count.Should().Be(0);
|
||||
_ = list.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void ConsecutiveDisposeShouldSucceed(string testCase, IEnumerable<long> testData, long itemsCount)
|
||||
public void ConsecutiveDisposeShouldSucceed(string testCase, IEnumerable<long> testData, int itemsCount)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableList<long>(testData, (int)itemsCount);
|
||||
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||
list.Dispose();
|
||||
|
||||
// Act
|
||||
@@ -237,41 +299,45 @@ namespace Recyclable.CollectionsTests
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void ConstructorShouldAcceptDuplicates(string testCase, IEnumerable<long> testData, long itemsCount)
|
||||
public void ConstructorShouldAcceptDuplicates(string testCase, IEnumerable<long> testData, int itemsCount)
|
||||
{
|
||||
// Prepare
|
||||
testData = testData.Concat(testData);
|
||||
|
||||
// Act
|
||||
using var list = new RecyclableList<long>(testData.Concat(testData), (int)itemsCount * 2);
|
||||
using var list = new RecyclableList<long>(testData, itemsCount * 2);
|
||||
|
||||
// Validate
|
||||
_ = list.Count.Should().Be((int)itemsCount * 2);
|
||||
_ = list.Should().Equal(testData.Concat(testData));
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo(itemsCount * 2);
|
||||
_ = list.Count.Should().Be(itemsCount * 2);
|
||||
_ = list.Should().Equal(testData);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void ConstructorSourceShouldInitializeList(string testCase, IEnumerable<long> testData, long itemsCount)
|
||||
public void ConstructorSourceShouldInitializeList(string testCase, IEnumerable<long> testData, int itemsCount)
|
||||
{
|
||||
// Act
|
||||
using var list = new RecyclableList<long>(testData, (int)itemsCount);
|
||||
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||
|
||||
// Validate
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo((int)itemsCount, "when we succeed in taking count without enumerating, then we allocate as much memory as needed, only");
|
||||
_ = list.Count.Should().Be((int)itemsCount);
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo(itemsCount);
|
||||
_ = list.Count.Should().Be(itemsCount);
|
||||
_ = list.Should().Equal(testData);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void ContainsShouldFindAllItems(string testCase, IEnumerable<long> testData, long itemsCount, in long[] itemIndexes)
|
||||
public void ContainsShouldFindAllItems(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableList<long>(testData, (int)itemsCount);
|
||||
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||
_ = testData.Any().Should().BeTrue("we need items on the list that we can look for");
|
||||
|
||||
// Act & Validate
|
||||
foreach (var itemIndex in itemIndexes)
|
||||
{
|
||||
_ = list.Contains(list[(int)itemIndex]).Should().BeTrue();
|
||||
_ = list.Contains(list[(int)itemIndex]).Should().BeTrue($"we searched for {list[(int)itemIndex]}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,10 +354,10 @@ namespace Recyclable.CollectionsTests
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void ContainsShouldNotFindNonExistingItems(string testCase, IEnumerable<long> testData, long itemsCount, in long[] itemIndexes)
|
||||
public void ContainsShouldNotFindNonExistingItems(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableList<long>(testData, (int)itemsCount);
|
||||
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||
_ = testData.Any().Should().BeTrue("we need items on the list that we can look for");
|
||||
|
||||
// Act & Validate
|
||||
@@ -303,10 +369,10 @@ namespace Recyclable.CollectionsTests
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void CopyToShouldCopyAllItemsInTheCorrectOrder(string testCase, IEnumerable<long> testData, long itemsCount)
|
||||
public void CopyToShouldCopyAllItemsInTheCorrectOrder(string testCase, IEnumerable<long> testData, int itemsCount)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableList<long>(testData, (int)itemsCount);
|
||||
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||
long[] copiedItems = new long[itemsCount];
|
||||
|
||||
// Act
|
||||
@@ -317,14 +383,72 @@ namespace Recyclable.CollectionsTests
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void EnumerateShouldYieldAllItemsInCorrectOrder(string testCase, IEnumerable<long> testData, long itemsCount)
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceRefDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void DisposeShouldClearItemsWhenReferenceType(string testCase, IEnumerable<object> testData, int itemsCount)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableList<long>(testData, (int)itemsCount);
|
||||
if (itemsCount == 0)
|
||||
{
|
||||
/// Disposing empty list is tested in <see cref="DisposeShouldSucceed"/>.
|
||||
return;
|
||||
}
|
||||
|
||||
// Arrange
|
||||
using var list = new RecyclableList<object>(testData, itemsCount);
|
||||
var listArray = list.AsArray;
|
||||
_ = list.AsArray[..itemsCount].Should().NotBeNull();
|
||||
_ = listArray[..itemsCount].Should().AllSatisfy(x => x.Should().NotBeNull());
|
||||
|
||||
// Act
|
||||
var yieldedItems = new List<long>((int)itemsCount);
|
||||
list.Dispose();
|
||||
|
||||
// Assert
|
||||
_ = listArray[..itemsCount].Should().AllSatisfy(x => x.Should().BeNull());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DisposeShouldReturnPooledArrayOnlyOnce()
|
||||
{
|
||||
// Arrange
|
||||
var testData = RecyclableLongListTestData.CreateTestData(RecyclableDefaults.MinPooledArrayLength);
|
||||
RecyclableList<long> firstInstance = new(testData);
|
||||
var firstArray = firstInstance.AsArray;
|
||||
|
||||
// Act
|
||||
firstInstance.Dispose();
|
||||
|
||||
// Assert
|
||||
using RecyclableList<long> sameInstance = new(testData);
|
||||
_ = sameInstance.AsArray.Should().BeSameAs(firstArray);
|
||||
|
||||
using RecyclableList<long> anotherInstance = new(testData);
|
||||
_ = anotherInstance.AsArray.Should().NotBeSameAs(firstArray);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void DisposeShouldSucceed(string testCase, IEnumerable<long> testData, int itemsCount)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||
|
||||
// Act
|
||||
list.Dispose();
|
||||
|
||||
// Validate
|
||||
_ = list.Capacity.Should().Be(0);
|
||||
_ = list.Count.Should().Be(0);
|
||||
_ = list.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void EnumerateShouldYieldAllItemsInCorrectOrder(string testCase, IEnumerable<long> testData, int itemsCount)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||
|
||||
// Act
|
||||
var yieldedItems = new List<long>(itemsCount);
|
||||
using var enumerator = list.GetEnumerator();
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
@@ -332,10 +456,23 @@ namespace Recyclable.CollectionsTests
|
||||
}
|
||||
|
||||
// Validate
|
||||
_ = yieldedItems.Count.Should().Be((int)itemsCount);
|
||||
_ = yieldedItems.Should().Equal(testData);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void GetByIndexShouldReturnCorrectItem(string testCase, IEnumerable<long> testData, int itemsCount)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableLongList<long>(testData, itemsCount);
|
||||
|
||||
// Act & Validate
|
||||
foreach (var item in testData)
|
||||
{
|
||||
_ = list[item - 1].Should().Be(item);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IndexOfShouldNotFindAnythingWhenListEmpty()
|
||||
{
|
||||
@@ -349,10 +486,10 @@ namespace Recyclable.CollectionsTests
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void IndexOfShouldNotFindNonExistingItems(string testCase, IEnumerable<long> testData, long itemsCount, in long[] itemIndexes)
|
||||
public void IndexOfShouldNotFindNonExistingItems(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableList<long>(testData, (int)itemsCount);
|
||||
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||
_ = testData.Any().Should().BeTrue("we need items on the list that we can look for");
|
||||
|
||||
// Act & Validate
|
||||
@@ -364,10 +501,10 @@ namespace Recyclable.CollectionsTests
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void IndexOfShouldReturnCorrectIndexes(string testCase, IEnumerable<long> testData, long itemsCount, in long[] itemIndexes)
|
||||
public void IndexOfShouldReturnCorrectIndexes(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableList<long>(testData, (int)itemsCount);
|
||||
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||
|
||||
// Act & Validate
|
||||
foreach (var itemIndex in itemIndexes)
|
||||
@@ -378,17 +515,21 @@ namespace Recyclable.CollectionsTests
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void InitializeSortClearAddRangeRemoveShouldSucceed(string testCase, IEnumerable<long> testData, long itemsCount, in long[] itemIndexes)
|
||||
public void InitializeSortClearAddRangeRemoveShouldSucceed(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
|
||||
{
|
||||
foreach (var itemIndex in itemIndexes)
|
||||
{
|
||||
// Prepare
|
||||
var testDataList = testData.ToList();
|
||||
var item = testDataList[(int)itemIndex];
|
||||
|
||||
// Act
|
||||
using RecyclableList<long> list = new(testData, (int)itemsCount);
|
||||
using RecyclableList<long> list = new(testData);
|
||||
QuickSortExtensions<long>.QuickSort(list);
|
||||
|
||||
// Validate
|
||||
_ = list.Count.Should().Be((int)itemsCount);
|
||||
_ = list.Should().Equal(testData);
|
||||
_ = list.Count.Should().Be(itemsCount);
|
||||
_ = list.Should().Equal(testDataList);
|
||||
|
||||
// Act
|
||||
list.Clear();
|
||||
@@ -401,22 +542,29 @@ namespace Recyclable.CollectionsTests
|
||||
list.AddRange(testData);
|
||||
|
||||
// Validate
|
||||
_ = list.Count.Should().Be((int)itemsCount);
|
||||
_ = list.Should().Equal(testData);
|
||||
_ = list.Count.Should().Be(itemsCount);
|
||||
_ = list.Should().Equal(testDataList);
|
||||
|
||||
// Prepare
|
||||
testDataList.RemoveAt((int)itemIndex);
|
||||
|
||||
// Act
|
||||
_ = list.Remove(list[(int)itemIndex]).Should().BeTrue();
|
||||
_ = list.Remove(item).Should().BeTrue();
|
||||
_ = list.IndexOf(item).Should().Be(RecyclableDefaults.ItemNotFoundIndex);
|
||||
_ = list.Contains(item).Should().BeFalse();
|
||||
_ = list.Count.Should().Be(itemsCount - 1);
|
||||
_ = list.Should().Equal(testDataList);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void InsertShouldMoveItems(string testCase, IEnumerable<long> testData, long itemsCount, in long[] itemIndexes)
|
||||
public void InsertShouldMoveItems(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
|
||||
{
|
||||
foreach (var itemIndex in itemIndexes)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableList<long>(testData, (int)itemsCount);
|
||||
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||
var item = list[(int)itemIndex];
|
||||
var expectedItems = list.ToList();
|
||||
expectedItems.Insert((int)itemIndex, item);
|
||||
@@ -425,22 +573,40 @@ namespace Recyclable.CollectionsTests
|
||||
list.Insert((int)itemIndex, item);
|
||||
|
||||
// Validate
|
||||
_ = list.Count.Should().Be((int)itemsCount + 1);
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo((int)itemsCount);
|
||||
_ = list[(int)itemIndex].Should().Be(item);
|
||||
_ = list[(int)itemIndex + 1].Should().Be(item);
|
||||
_ = list.Count.Should().Be(itemsCount + 1);
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo(itemsCount);
|
||||
_ = list.Should().Equal(expectedItems);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceRefDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void RemoveAtShouldClearItemWhenReferenceType(string testCase, IEnumerable<object> testData, int itemsCount, in long[] indexesToRemove)
|
||||
{
|
||||
// Arrange
|
||||
foreach (int indexToRemove in indexesToRemove)
|
||||
{
|
||||
using var list = new RecyclableList<object>(testData, itemsCount);
|
||||
_ = list[^1].Should().NotBeNull();
|
||||
|
||||
// Act
|
||||
list.RemoveAt(indexToRemove);
|
||||
|
||||
// Assert
|
||||
_ = list[list.Count].Should().BeNull();
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void RemoveAtShouldSucceed(string testCase, IEnumerable<long> testData, long itemsCount, in long[] itemIndexes)
|
||||
public void RemoveAtShouldRemoveTheCorrectItem(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
|
||||
{
|
||||
foreach (var itemIndex in itemIndexes)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableList<long>(testData, (int)itemsCount);
|
||||
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||
var expectedItems = testData.ToList();
|
||||
expectedItems.RemoveAt((int)itemIndex);
|
||||
|
||||
@@ -448,42 +614,103 @@ namespace Recyclable.CollectionsTests
|
||||
list.RemoveAt((int)itemIndex);
|
||||
|
||||
// Validate
|
||||
_ = list.Count.Should().Be((int)(itemsCount - 1));
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo(itemsCount);
|
||||
_ = list.Count.Should().Be(itemsCount - 1);
|
||||
_ = list.Should().Equal(expectedItems);
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo((int)itemsCount);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceRefDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void RemoveShouldClearItemWhenReferenceType(string testCase, IEnumerable<object> testData, int itemsCount, in long[] indexesToRemove)
|
||||
{
|
||||
var expectedItems = testData.ToArray();
|
||||
|
||||
foreach (int indexToRemove in indexesToRemove)
|
||||
{
|
||||
// Arrange
|
||||
var itemToRemove = expectedItems[indexToRemove];
|
||||
using var list = new RecyclableList<object>(testData, itemsCount);
|
||||
_ = list[^1].Should().NotBeNull();
|
||||
|
||||
// Act
|
||||
list.Remove(itemToRemove);
|
||||
|
||||
// Assert
|
||||
_ = list[list.Count].Should().BeNull();
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void RemoveShouldRemoveTheCorrectItem(string testCase, IEnumerable<long> testData, long itemsCount, in long[] itemIndexes)
|
||||
public void RemoveShouldRemoveTheCorrectItem(string testCase, IEnumerable<long> testData, int itemsCount, in long[] itemIndexes)
|
||||
{
|
||||
foreach (var itemIndex in itemIndexes)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableList<long>(testData, (int)itemsCount);
|
||||
var item = list[(int)itemIndex];
|
||||
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||
var expectedItems = testData.ToList();
|
||||
var item = list[(int)itemIndex];
|
||||
expectedItems.RemoveAt((int)itemIndex);
|
||||
|
||||
// Act
|
||||
_ = list.Remove(item).Should().BeTrue();
|
||||
|
||||
// Validate
|
||||
_ = list.Count.Should().Be((int)(itemsCount - 1));
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo(itemsCount);
|
||||
_ = list.Count.Should().Be(itemsCount - 1);
|
||||
_ = list.Should().Equal(expectedItems);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void VersionedEnumerateShouldYieldAllItemsInCorrectOrder(string testCase, IEnumerable<long> testData, long itemsCount)
|
||||
public void SetByIndexShouldReturnCorrectItem(string testCase, IEnumerable<long> testData, int itemsCount)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableList<long>(testData, (int)itemsCount);
|
||||
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||
var expectedData = testData.ToList();
|
||||
|
||||
// Act & Validate
|
||||
foreach (long item in testData)
|
||||
{
|
||||
// Act
|
||||
list[(int)item - 1] = item + 1;
|
||||
|
||||
// Validate
|
||||
expectedData[(int)item - 1] = item + 1;
|
||||
list[(int)item - 1].Should().Be(item + 1);
|
||||
list.Should().Equal(expectedData);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void VersionedEnumerateShouldRaiseExceptionWhenCollectionIsModified(string testCase, IEnumerable<long> testData, int itemsCount)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||
|
||||
// Act
|
||||
var yieldedItems = new List<long>((int)itemsCount);
|
||||
Assert.Throws<InvalidOperationException>(() =>
|
||||
{
|
||||
list.Add(-1);
|
||||
foreach (var item in (IRecyclableVersionedList<long>)list)
|
||||
{
|
||||
list.Add(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void VersionedEnumerateShouldYieldAllItemsInCorrectOrder(string testCase, IEnumerable<long> testData, int itemsCount)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableList<long>(testData, itemsCount);
|
||||
|
||||
// Act
|
||||
var yieldedItems = new List<long>(itemsCount);
|
||||
foreach (var item in (IRecyclableVersionedList<long>)list)
|
||||
{
|
||||
yieldedItems.Add(item);
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace Recyclable.CollectionsTests
|
||||
public static readonly IEnumerable<long> ItemsCountVariants = new long[]
|
||||
{
|
||||
//RecyclableDefaults.MinPooledArrayLength - 5, RecyclableDefaults.MinPooledArrayLength - 1, RecyclableDefaults.MinPooledArrayLength, RecyclableDefaults.MinPooledArrayLength + 1, RecyclableDefaults.MinPooledArrayLength + 5, 127, 128, 129, RecyclableDefaults.MinItemsCountForParallelization
|
||||
0, 1, 2, 3, 7, 10, 16, RecyclableDefaults.MinPooledArrayLength - 5, RecyclableDefaults.MinPooledArrayLength - 1, RecyclableDefaults.MinPooledArrayLength, RecyclableDefaults.MinPooledArrayLength + 1, RecyclableDefaults.MinPooledArrayLength + 5, RecyclableDefaults.MinPooledArrayLength * 100, RecyclableDefaults.MinItemsCountForParallelization
|
||||
0, 1, 2, 3, 7, 10, 16, RecyclableDefaults.MinPooledArrayLength - 5, RecyclableDefaults.MinPooledArrayLength - 1, RecyclableDefaults.MinPooledArrayLength, RecyclableDefaults.MinPooledArrayLength + 1, RecyclableDefaults.MinPooledArrayLength + 5, RecyclableDefaults.MinPooledArrayLength * 100
|
||||
};
|
||||
|
||||
public static readonly IEnumerable<int> BlockSizeVariants = new int[]
|
||||
@@ -83,9 +83,10 @@ namespace Recyclable.CollectionsTests
|
||||
switch (refType)
|
||||
{
|
||||
case false:
|
||||
IEnumerable<long> testData = CreateTestData(itemsCount).ToArray();
|
||||
long[] testData = CreateTestData(itemsCount).ToArray();
|
||||
|
||||
yield return new object[] { $"int[{itemsCount}]", testData, itemsCount };
|
||||
yield return new object[] { $"ReadOnlySpan[{itemsCount}]", testData, itemsCount };
|
||||
//GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true);
|
||||
|
||||
yield return new object[] { $"List<int>(itemsCount: {itemsCount}", testData.ToList(), itemsCount };
|
||||
|
||||
@@ -12,12 +12,7 @@ namespace Recyclable.CollectionsTests
|
||||
private static long CalculateExpectedCapacity(string testCase, long itemsCount, int targetBlockSize)
|
||||
{
|
||||
var expectedCapacity = (long)BitOperations.RoundUpToPowerOf2((uint)itemsCount + 1);
|
||||
if (testCase.Contains("non-enumerated"))
|
||||
{
|
||||
expectedCapacity = (long)BitOperations.RoundUpToPowerOf2((ulong)itemsCount + RecyclableDefaults.BlockSize);
|
||||
expectedCapacity = expectedCapacity >= RecyclableDefaults.InitialCapacity ? expectedCapacity : RecyclableDefaults.InitialCapacity;
|
||||
}
|
||||
else if (targetBlockSize > itemsCount)
|
||||
if (targetBlockSize > itemsCount)
|
||||
{
|
||||
expectedCapacity = ((itemsCount / targetBlockSize) + (itemsCount % targetBlockSize > 0 ? 1 : 0)) * targetBlockSize;
|
||||
}
|
||||
@@ -25,6 +20,19 @@ namespace Recyclable.CollectionsTests
|
||||
return expectedCapacity;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddRangeShouldAcceptNulls()
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableLongList<long?>();
|
||||
|
||||
// Act
|
||||
list.AddRange(new long?[] { null, default });
|
||||
|
||||
// Validate
|
||||
_ = list.Should().HaveCount(2).And.AllSatisfy(x => x.Should().BeNull());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void AddRangeShouldAddItemsInCorrectOrder(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize)
|
||||
@@ -33,7 +41,11 @@ namespace Recyclable.CollectionsTests
|
||||
using var list = new RecyclableLongList<long>(minBlockSize: targetBlockSize);
|
||||
|
||||
// Act
|
||||
if (testData is long[] testDataArray)
|
||||
if (testCase.Contains("ReadOnlySpan[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.AddRange(new ReadOnlySpan<long>((long[])testData));
|
||||
}
|
||||
else if (testData is long[] testDataArray)
|
||||
{
|
||||
list.AddRange(testDataArray);
|
||||
}
|
||||
@@ -63,9 +75,9 @@ namespace Recyclable.CollectionsTests
|
||||
}
|
||||
|
||||
// Validate
|
||||
int expectedItemsCount = testData.Count();
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo(expectedItemsCount, "when capacity == 0, then we should allocate as much memory as needed, only");
|
||||
_ = list.LastBlockWithData.Should().Be((int)(itemsCount / list.BlockSize) - (itemsCount % list.BlockSize != 0 ? 0 : 1));
|
||||
long expectedItemsCount = itemsCount;
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo(expectedItemsCount);
|
||||
_ = list.LastBlockWithData.Should().Be((int)(itemsCount / targetBlockSize) - (itemsCount % targetBlockSize != 0 ? 0 : 1));
|
||||
_ = list.LongCount.Should().Be(expectedItemsCount);
|
||||
_ = list.Should().Equal(testData);
|
||||
}
|
||||
@@ -78,7 +90,11 @@ namespace Recyclable.CollectionsTests
|
||||
using var list = new RecyclableLongList<long>();
|
||||
|
||||
// Act
|
||||
if (testData is long[] testDataArray)
|
||||
if (testCase.Contains("ReadOnlySpan[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
list.AddRange(new ReadOnlySpan<long>((long[])testData));
|
||||
}
|
||||
else if (testData is long[] testDataArray)
|
||||
{
|
||||
list.AddRange(testDataArray);
|
||||
}
|
||||
@@ -110,7 +126,7 @@ namespace Recyclable.CollectionsTests
|
||||
// Validate
|
||||
_ = list.Capacity.Should().Be(0);
|
||||
_ = list.LastBlockWithData.Should().Be(-1);
|
||||
_ = list.LongCount.Should().Be(0);
|
||||
_ = list.LongCount.Should().Be(itemsCount);
|
||||
_ = list.Should().BeEmpty();
|
||||
}
|
||||
|
||||
@@ -122,7 +138,13 @@ namespace Recyclable.CollectionsTests
|
||||
using var list = new RecyclableLongList<long>(minBlockSize: targetBlockSize);
|
||||
|
||||
// Act
|
||||
if (testData is long[] testDataArray)
|
||||
if (testCase.Contains("ReadOnlySpan[", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var readOnlySpan = new ReadOnlySpan<long>((long[])testData);
|
||||
list.AddRange(readOnlySpan);
|
||||
list.AddRange(readOnlySpan);
|
||||
}
|
||||
else if (testData is long[] testDataArray)
|
||||
{
|
||||
list.AddRange(testDataArray);
|
||||
list.AddRange(testDataArray);
|
||||
@@ -158,14 +180,9 @@ namespace Recyclable.CollectionsTests
|
||||
}
|
||||
|
||||
// Validate
|
||||
var expectedData = testData.Concat(testData).ToArray();
|
||||
|
||||
if (itemsCount > 0)
|
||||
{
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo(targetBlockSize);
|
||||
}
|
||||
|
||||
_ = list.LongCount.Should().Be(expectedData.Length);
|
||||
var expectedData = testData.Concat(testData);
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo(itemsCount * 2);
|
||||
_ = list.LongCount.Should().Be(itemsCount * 2);
|
||||
_ = list.LastBlockWithData.Should().Be((int)(itemsCount * 2 / list.BlockSize) - (itemsCount * 2 % list.BlockSize != 0 ? 0 : 1));
|
||||
_ = list.Should().Equal(expectedData);
|
||||
}
|
||||
@@ -175,7 +192,7 @@ namespace Recyclable.CollectionsTests
|
||||
public void AddShouldAcceptDuplicates(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize)
|
||||
{
|
||||
// Prepare
|
||||
testData = testData.Concat(testData).ToArray();
|
||||
testData = testData.Concat(testData);
|
||||
|
||||
// Act
|
||||
using var list = new RecyclableLongList<long>(minBlockSize: targetBlockSize);
|
||||
@@ -185,11 +202,29 @@ namespace Recyclable.CollectionsTests
|
||||
}
|
||||
|
||||
// Validate
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo(itemsCount * 2);
|
||||
_ = list.LongCount.Should().Be(itemsCount * 2);
|
||||
_ = list.LastBlockWithData.Should().Be((int)(itemsCount * 2 / list.BlockSize) - (itemsCount * 2 % list.BlockSize != 0 ? 0 : 1));
|
||||
_ = list.Should().Equal(testData);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddShouldAcceptNulls()
|
||||
{
|
||||
// Prepare
|
||||
#pragma warning disable IDE0028
|
||||
using var list = new RecyclableLongList<long?>();
|
||||
|
||||
// Act
|
||||
list.Add(null);
|
||||
list.Add(default);
|
||||
#pragma warning restore IDE0028
|
||||
|
||||
// Validate
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo(RecyclableDefaults.BlockSize);
|
||||
_ = list.Should().AllSatisfy(x => x.Should().BeNull());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void AddShouldAddItems(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize)
|
||||
@@ -201,9 +236,11 @@ namespace Recyclable.CollectionsTests
|
||||
foreach (var index in testData)
|
||||
{
|
||||
list.Add(index);
|
||||
_ = list.LongCount.Should().Be(index);
|
||||
}
|
||||
|
||||
// Validate
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo(itemsCount);
|
||||
_ = list.LongCount.Should().Be(itemsCount, "we added so many items");
|
||||
_ = list.LastBlockWithData.Should().Be((int)(itemsCount / list.BlockSize) - (itemsCount % list.BlockSize == 0 ? 1 : 0));
|
||||
_ = list.Should().Equal(testData);
|
||||
@@ -223,11 +260,38 @@ namespace Recyclable.CollectionsTests
|
||||
}
|
||||
|
||||
// Validate
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo(itemsCount * 2);
|
||||
_ = list.LongCount.Should().Be(itemsCount * 2);
|
||||
_ = list.LastBlockWithData.Should().Be((int)(itemsCount * 2 / list.BlockSize) - (itemsCount * 2 % list.BlockSize != 0 ? 0 : 1));
|
||||
_ = list.Should().Equal(testData.Concat(testData));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceRefDataWithBlockSizeVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void ClearShouldClearItemsWhenReferenceType(string testCase, IEnumerable<object> testData, long itemsCount, int targetBlockSize)
|
||||
{
|
||||
if (itemsCount == 0)
|
||||
{
|
||||
/// Clearing empty list is tested in <see cref="ClearShouldSucceed"/>.
|
||||
return;
|
||||
}
|
||||
|
||||
// Arrange
|
||||
using var list = new RecyclableLongList<object>(testData, minBlockSize: targetBlockSize, itemsCount);
|
||||
var blocks = list.AsArray.ToArray();
|
||||
_ = blocks.Should().AllBeOfType<object[]>();
|
||||
var beforeItems = blocks.SelectMany(x => x.Take(targetBlockSize)).Take((int)itemsCount).ToArray();
|
||||
_ = beforeItems.Should().AllSatisfy(x => x.Should().NotBeNull());
|
||||
|
||||
// Act
|
||||
list.Clear();
|
||||
|
||||
// Assert
|
||||
var afterItems = blocks.SelectMany(x => x.Take(targetBlockSize)).Take((int)itemsCount).ToArray();
|
||||
_ = afterItems.Should().AllSatisfy(x => x.Should().BeNull());
|
||||
_ = list.AsArray.Should().AllBeOfType<object[]>().And.AllSatisfy(x => x.Should().BeEmpty());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void ClearShouldRemoveAllItems(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize)
|
||||
@@ -241,10 +305,31 @@ namespace Recyclable.CollectionsTests
|
||||
list.Clear();
|
||||
|
||||
// Validate
|
||||
_ = list.Capacity.Should().Be(0);
|
||||
_ = list.LastBlockWithData.Should().Be(-1);
|
||||
_ = list.LongCount.Should().Be(0);
|
||||
_ = list.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ClearShouldReturnPooledArraysOnlyOnce()
|
||||
{
|
||||
// Arrange
|
||||
var testData = RecyclableLongListTestData.CreateTestData(RecyclableDefaults.MinPooledArrayLength * 10);
|
||||
using RecyclableLongList<long> firstInstances = new(testData, RecyclableDefaults.MinPooledArrayLength);
|
||||
var firstArrays = firstInstances.AsArray.ToArray();
|
||||
|
||||
// Act
|
||||
firstInstances.Clear();
|
||||
|
||||
// Assert
|
||||
using RecyclableLongList<long> sameInstances = new(testData, RecyclableDefaults.MinPooledArrayLength);
|
||||
_ = sameInstances.AsArray.Should().Equal(firstArrays.Reverse());
|
||||
|
||||
using RecyclableLongList<long> otherInstances = new(testData, RecyclableDefaults.MinPooledArrayLength);
|
||||
_ = otherInstances.AsArray.Should().NotIntersectWith(firstArrays);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void ConsecutiveDisposeShouldSucceed(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize)
|
||||
@@ -262,12 +347,14 @@ namespace Recyclable.CollectionsTests
|
||||
public void ConstructorShouldAcceptDuplicates(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize)
|
||||
{
|
||||
// Prepare
|
||||
testData = testData.Concat(testData).ToArray();
|
||||
testData = testData.Concat(testData);
|
||||
|
||||
// Act
|
||||
using var list = new RecyclableLongList<long>(testData, minBlockSize: targetBlockSize, itemsCount);
|
||||
|
||||
// Validate
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo(itemsCount * 2);
|
||||
_ = list.LastBlockWithData.Should().Be((int)(itemsCount * 2 / targetBlockSize) - (itemsCount * 2 % targetBlockSize > 0 ? 0 : 1));
|
||||
_ = list.LongCount.Should().Be(itemsCount * 2);
|
||||
_ = list.Should().Equal(testData);
|
||||
}
|
||||
@@ -280,7 +367,8 @@ namespace Recyclable.CollectionsTests
|
||||
using var list = new RecyclableLongList<long>(testData, minBlockSize: targetBlockSize);
|
||||
|
||||
// Validate
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo((int)itemsCount, "when we succeed in taking count without enumerating, then we allocate as much memory as needed, only");
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo(itemsCount);
|
||||
_ = list.LastBlockWithData.Should().Be((int)(itemsCount / targetBlockSize) - (itemsCount % targetBlockSize > 0 ? 0 : 1));
|
||||
_ = list.LongCount.Should().Be(itemsCount);
|
||||
_ = list.Should().Equal(testData);
|
||||
}
|
||||
@@ -334,11 +422,57 @@ namespace Recyclable.CollectionsTests
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableLongList<long>(testData, minBlockSize: targetBlockSize, itemsCount);
|
||||
long[] copiedItems = new long[itemsCount];
|
||||
|
||||
// Act
|
||||
var actual = new long[itemsCount];
|
||||
list.CopyTo(actual, 0);
|
||||
_ = actual.Should().Equal(testData);
|
||||
list.CopyTo(copiedItems, 0);
|
||||
|
||||
// Validate
|
||||
_ = copiedItems.Should().Equal(testData);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceRefDataWithBlockSizeVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void DisposeShouldClearItemsWhenReferenceType(string testCase, IEnumerable<object> testData, long itemsCount, int targetBlockSize)
|
||||
{
|
||||
if (itemsCount == 0)
|
||||
{
|
||||
/// Disposing empty list is tested in <see cref="DisposeShouldSucceed"/>.
|
||||
return;
|
||||
}
|
||||
|
||||
// Arrange
|
||||
using var list = new RecyclableLongList<object>(testData, targetBlockSize, itemsCount);
|
||||
var blocks = list.AsArray.ToArray();
|
||||
_ = blocks.Should().AllBeOfType<object[]>();
|
||||
|
||||
_ = blocks.SelectMany(x => x.Take(targetBlockSize)).Take((int)itemsCount).Should().AllSatisfy(x => x.Should().NotBeNull());
|
||||
|
||||
// Act
|
||||
list.Dispose();
|
||||
|
||||
// Assert
|
||||
_ = blocks.SelectMany(x => x.Take(targetBlockSize)).Take((int)itemsCount).Should().AllSatisfy(x => x.Should().BeNull());
|
||||
_ = list.AsArray.Should().AllBeOfType<object[]>().And.AllSatisfy(x => x.Should().BeEmpty());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DisposeShouldReturnPooledArrayOnlyOnce()
|
||||
{
|
||||
// Arrange
|
||||
var testData = RecyclableLongListTestData.CreateTestData(RecyclableDefaults.MinPooledArrayLength * 10);
|
||||
using RecyclableLongList<long> firstInstances = new(testData, RecyclableDefaults.MinPooledArrayLength);
|
||||
var firstArrays = firstInstances.AsArray.ToArray();
|
||||
|
||||
// Act
|
||||
firstInstances.Dispose();
|
||||
|
||||
// Assert
|
||||
using RecyclableLongList<long> sameInstances = new(testData, RecyclableDefaults.MinPooledArrayLength);
|
||||
_ = sameInstances.AsArray.Should().BeEquivalentTo(firstArrays);
|
||||
|
||||
using RecyclableLongList<long> otherInstances = new(testData, RecyclableDefaults.MinPooledArrayLength);
|
||||
_ = otherInstances.AsArray.Should().NotIntersectWith(firstArrays);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@@ -352,27 +486,29 @@ namespace Recyclable.CollectionsTests
|
||||
list.Dispose();
|
||||
|
||||
// Validate
|
||||
_ = list.LongCount.Should().Be(0L);
|
||||
_ = list.Capacity.Should().Be(0);
|
||||
_ = list.LastBlockWithData.Should().Be(-1);
|
||||
_ = list.LongCount.Should().Be(0);
|
||||
_ = list.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void EnumerateShouldYieldAllItemsInCorrectOrder(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize)
|
||||
public void EnumerateShouldYieldAllItemsInCorrectOrder(string testCase, IEnumerable<long> testData, int itemsCount, int targetBlockSize)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableLongList<long>(testData, minBlockSize: targetBlockSize, itemsCount);
|
||||
|
||||
// Act
|
||||
var actual = new List<long>((int)itemsCount);
|
||||
var yieldedItems = new List<long>(itemsCount);
|
||||
using var enumerator = list.GetEnumerator();
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
actual.Add(enumerator.Current);
|
||||
yieldedItems.Add(enumerator.Current);
|
||||
}
|
||||
|
||||
// Validate
|
||||
_ = actual.Should().Equal(testData);
|
||||
_ = yieldedItems.Should().Equal(testData);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@@ -383,7 +519,21 @@ namespace Recyclable.CollectionsTests
|
||||
using var list = new RecyclableLongList<long>(testData, minBlockSize: targetBlockSize, itemsCount);
|
||||
|
||||
// Act & Validate
|
||||
foreach (var item in testData)
|
||||
foreach (long item in testData)
|
||||
{
|
||||
_ = list[(int)item - 1].Should().Be(item);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void GetByIndexShouldReturnCorrectItemWhenLong(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableLongList<long>(testData, minBlockSize: targetBlockSize, itemsCount);
|
||||
|
||||
// Act & Validate
|
||||
foreach (long item in testData)
|
||||
{
|
||||
_ = list[item - 1].Should().Be(item);
|
||||
}
|
||||
@@ -436,7 +586,7 @@ namespace Recyclable.CollectionsTests
|
||||
foreach (var itemIndex in itemIndexes)
|
||||
{
|
||||
// Prepare
|
||||
using var testDataList = testData.ToRecyclableList();
|
||||
var testDataList = testData.ToList();
|
||||
var item = testDataList[(int)itemIndex];
|
||||
|
||||
// Act
|
||||
@@ -477,23 +627,52 @@ namespace Recyclable.CollectionsTests
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void InsertShouldMoveItems(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize, in long[] itemIndexes)
|
||||
{
|
||||
|
||||
foreach (var itemIndex in itemIndexes)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableLongList<long>(testData, targetBlockSize, itemsCount);
|
||||
long expectedCapacity = CalculateExpectedCapacity(testCase, itemsCount, targetBlockSize);
|
||||
var expectedItems = testData.ToList();
|
||||
|
||||
var item = list[itemIndex];
|
||||
var expectedItems = list.ToList();
|
||||
expectedItems.Insert((int)itemIndex, item);
|
||||
var existingItem = expectedItems[(int)itemIndex];
|
||||
var newItem = existingItem + 1;
|
||||
expectedItems.Insert((int)itemIndex, newItem);
|
||||
|
||||
// Act
|
||||
list.Insert(itemIndex, item);
|
||||
list.Insert((int)itemIndex, newItem);
|
||||
|
||||
// Validate
|
||||
_ = list[itemIndex].Should().Be(newItem);
|
||||
_ = list[itemIndex + 1].Should().Be(existingItem);
|
||||
_ = list.Capacity.Should().Be(expectedCapacity);
|
||||
_ = list.Count.Should().Be((int)itemsCount + 1);
|
||||
_ = list.Should().Equal(expectedItems);
|
||||
}
|
||||
}
|
||||
|
||||
_ = list[itemIndex].Should().Be(item);
|
||||
_ = list[itemIndex + 1].Should().Be(item);
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void InsertShouldMoveItemsWhenLong(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize, in long[] itemIndexes)
|
||||
{
|
||||
|
||||
foreach (var itemIndex in itemIndexes)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableLongList<long>(testData, targetBlockSize, itemsCount);
|
||||
long expectedCapacity = CalculateExpectedCapacity(testCase, itemsCount, targetBlockSize);
|
||||
var expectedItems = testData.ToList();
|
||||
|
||||
var existingItem = expectedItems[(int)itemIndex];
|
||||
var newItem = existingItem + 1;
|
||||
expectedItems.Insert((int)itemIndex, newItem);
|
||||
|
||||
// Act
|
||||
list.Insert(itemIndex, newItem);
|
||||
|
||||
// Validate
|
||||
_ = list[itemIndex].Should().Be(newItem);
|
||||
_ = list[itemIndex + 1].Should().Be(existingItem);
|
||||
_ = list.Capacity.Should().Be(expectedCapacity);
|
||||
_ = list.Count.Should().Be((int)itemsCount + 1);
|
||||
_ = list.Should().Equal(expectedItems);
|
||||
@@ -540,9 +719,49 @@ namespace Recyclable.CollectionsTests
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceRefDataWithBlockSizeWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void RemoveAtShouldClearItemWhenReferenceType(string testCase, IEnumerable<object> testData, long itemsCount, int targetBlockSize, in long[] indexesToRemove)
|
||||
{
|
||||
foreach (var indexToRemove in indexesToRemove)
|
||||
{
|
||||
// Arrange
|
||||
using var list = new RecyclableLongList<object>(testData, targetBlockSize, itemsCount);
|
||||
_ = list[^1].Should().NotBeNull();
|
||||
|
||||
// Act
|
||||
list.RemoveAt(indexToRemove);
|
||||
|
||||
// Assert
|
||||
_ = list[list.LongCount].Should().BeNull();
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void RemoveAtShouldSucceed(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize, in long[] itemIndexes)
|
||||
public void RemoveAtShouldRemoveTheCorrectItem(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize, in long[] itemIndexes)
|
||||
{
|
||||
foreach (var itemIndex in itemIndexes)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableLongList<long>(testData, minBlockSize: targetBlockSize);
|
||||
var expectedItems = list.ToList();
|
||||
expectedItems.RemoveAt((int)itemIndex);
|
||||
|
||||
// Act
|
||||
list.RemoveAt((int)itemIndex);
|
||||
|
||||
// Validate
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo(itemsCount);
|
||||
_ = list.LastBlockWithData.Should().Be((int)(list.LongCount >> list.BlockSizePow2BitShift) - ((list.LongCount & list.BlockSizeMinus1) != 0 ? 0 : 1));
|
||||
_ = list.LongCount.Should().Be(itemsCount - 1);
|
||||
_ = list.Should().Equal(expectedItems);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void RemoveAtShouldRemoveTheCorrectItemWhenLong(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize, in long[] itemIndexes)
|
||||
{
|
||||
foreach (var itemIndex in itemIndexes)
|
||||
{
|
||||
@@ -555,13 +774,34 @@ namespace Recyclable.CollectionsTests
|
||||
list.RemoveAt(itemIndex);
|
||||
|
||||
// Validate
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo((int)itemsCount);
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo(itemsCount);
|
||||
_ = list.LastBlockWithData.Should().Be((int)(list.LongCount >> list.BlockSizePow2BitShift) - ((list.LongCount & list.BlockSizeMinus1) != 0 ? 0 : 1));
|
||||
_ = list.LongCount.Should().Be(itemsCount - 1);
|
||||
_ = list.Should().Equal(expectedItems);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceRefDataWithBlockSizeWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void RemoveShouldClearItemWhenReferenceType(string testCase, IEnumerable<object> testData, long itemsCount, int targetBlockSize, in long[] indexesToRemove)
|
||||
{
|
||||
var expectedItems = testData.ToArray();
|
||||
|
||||
foreach (var indexToRemove in indexesToRemove)
|
||||
{
|
||||
// Arrange
|
||||
var itemToRemove = expectedItems[(int)indexToRemove];
|
||||
using var list = new RecyclableLongList<object>(testData, targetBlockSize, itemsCount);
|
||||
_ = list[^1].Should().NotBeNull();
|
||||
|
||||
// Act
|
||||
list.Remove(itemToRemove);
|
||||
|
||||
// Assert
|
||||
_ = list[list.LongCount].Should().BeNull();
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void RemoveShouldRemoveTheCorrectItem(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize, in long[] itemIndexes)
|
||||
@@ -578,29 +818,91 @@ namespace Recyclable.CollectionsTests
|
||||
_ = list.Remove(item).Should().BeTrue();
|
||||
|
||||
// Validate
|
||||
_ = list.LongCount.Should().Be(itemsCount - 1);
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo(itemsCount);
|
||||
_ = list.LastBlockWithData.Should().Be((int)(list.LongCount >> list.BlockSizePow2BitShift) - ((list.LongCount & list.BlockSizeMinus1) != 0 ? 0 : 1));
|
||||
_ = list.LongCount.Should().Be(itemsCount - 1);
|
||||
_ = list.Should().Equal(expectedItems);
|
||||
_ = list.Capacity.Should().BeGreaterThanOrEqualTo((int)itemsCount);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void VersionedEnumerateShouldYieldAllItemsInCorrectOrder(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize)
|
||||
public void SetByIndexShouldReturnCorrectItem(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableLongList<long>(testData, minBlockSize: targetBlockSize, itemsCount);
|
||||
var expectedData = testData.ToList();
|
||||
|
||||
// Act & Validate
|
||||
foreach (long item in testData)
|
||||
{
|
||||
// Act
|
||||
list[(int)item - 1] = item + 1;
|
||||
|
||||
// Validate
|
||||
expectedData[(int)item - 1] = item + 1;
|
||||
list[item - 1].Should().Be(item + 1);
|
||||
list.Should().Equal(expectedData);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void SetByIndexShouldReturnCorrectItemWhenLong(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableLongList<long>(testData, minBlockSize: targetBlockSize, itemsCount);
|
||||
var expectedData = testData.ToList();
|
||||
|
||||
// Act & Validate
|
||||
foreach (long item in testData)
|
||||
{
|
||||
// Act
|
||||
list[item - 1] = item + 1;
|
||||
|
||||
// Validate
|
||||
expectedData[(int)item - 1] = item + 1;
|
||||
list[item - 1].Should().Be(item + 1);
|
||||
list.Should().Equal(expectedData);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void VersionedEnumerateShouldRaiseExceptionWhenCollectionIsModified(string testCase, IEnumerable<long> testData, long itemsCount, int targetBlockSize)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableLongList<long>(testData, targetBlockSize, itemsCount)
|
||||
{
|
||||
-1
|
||||
};
|
||||
|
||||
// Act
|
||||
_ = Assert.Throws<InvalidOperationException>(() =>
|
||||
{
|
||||
foreach (var item in (IRecyclableVersionedLongList<long>)list)
|
||||
{
|
||||
list.Add(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeVariants), MemberType = typeof(RecyclableLongListTestData))]
|
||||
public void VersionedEnumerateShouldYieldAllItemsInCorrectOrder(string testCase, IEnumerable<long> testData, int itemsCount, int targetBlockSize)
|
||||
{
|
||||
// Prepare
|
||||
using var list = new RecyclableLongList<long>(testData, minBlockSize: targetBlockSize, itemsCount);
|
||||
|
||||
// Act
|
||||
using var actual = new RecyclableList<long>((int)itemsCount);
|
||||
var yieldedItems = new List<long>(itemsCount);
|
||||
foreach (var item in (IRecyclableVersionedLongList<long>)list)
|
||||
{
|
||||
actual.Add(item);
|
||||
yieldedItems.Add(item);
|
||||
}
|
||||
|
||||
// Validate
|
||||
_ = actual.Should().Equal(testData);
|
||||
_ = yieldedItems.Should().Equal(testData);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user