From 98347f0727ce3079fdba3b7452afbe94c1fcfdee Mon Sep 17 00:00:00 2001 From: mlemanczyk Date: Sun, 3 Sep 2023 17:55:43 +0200 Subject: [PATCH] Tests reworked for better coverage & more targeted scenarios. --- .../Recyclable.CollectionsTests.csproj | 1 + .../RecyclableListTests.cs | 272 ++++------- .../RecyclableLongListTestData.cs | 304 ++++++++++-- .../RecyclableLongListTests.cs | 452 +++++++----------- 4 files changed, 537 insertions(+), 492 deletions(-) diff --git a/Recyclable.CollectionsTests/Recyclable.CollectionsTests.csproj b/Recyclable.CollectionsTests/Recyclable.CollectionsTests.csproj index 3e6de67..2be9788 100644 --- a/Recyclable.CollectionsTests/Recyclable.CollectionsTests.csproj +++ b/Recyclable.CollectionsTests/Recyclable.CollectionsTests.csproj @@ -16,6 +16,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + all diff --git a/Recyclable.CollectionsTests/RecyclableListTests.cs b/Recyclable.CollectionsTests/RecyclableListTests.cs index 35f450b..9104730 100644 --- a/Recyclable.CollectionsTests/RecyclableListTests.cs +++ b/Recyclable.CollectionsTests/RecyclableListTests.cs @@ -53,7 +53,7 @@ namespace Recyclable.CollectionsTests [Theory] [MemberData(nameof(RecyclableLongListTestData.EmptySourceDataVariants), MemberType = typeof(RecyclableLongListTestData))] - public void AddRangeShouldDoNothingWhenSourceIsEmpty(string testCase, IEnumerable testData) + public void AddRangeShouldDoNothingWhenSourceIsEmpty(string testCase, IEnumerable testData, long itemsCount) { // Prepare using var list = new RecyclableList(); @@ -145,7 +145,7 @@ namespace Recyclable.CollectionsTests _ = list.Capacity.Should().BeGreaterThanOrEqualTo((int)itemsCount * 2); } - _ = list.Count.Should().Be(expectedData.Count); + _ = list.Count.Should().Be(expectedData.Length); _ = list.Should().Equal(expectedData); } @@ -217,7 +217,8 @@ namespace Recyclable.CollectionsTests list.Clear(); list.Clear(); - // Validate + // Validate + _ = list.Capacity.Should().BeGreaterThanOrEqualTo((int)itemsCount); _ = list.Count.Should().Be(0); _ = list.Should().BeEmpty(); } @@ -260,23 +261,17 @@ namespace Recyclable.CollectionsTests } [Theory] - [MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))] - public void ContainsShouldFindAllItems(string testCase, IEnumerable testData, long itemsCount) + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void ContainsShouldFindAllItems(string testCase, IEnumerable testData, long itemsCount, in long[] itemIndexes) { - if (itemsCount == 0) - { - return; - } - // Prepare using var list = new RecyclableList(testData, (int)itemsCount); _ = testData.Any().Should().BeTrue("we need items on the list that we can look for"); - // Act - foreach (var item in testData) + // Act & Validate + foreach (var itemIndex in itemIndexes) { - // Validate - _ = list.Contains(item).Should().BeTrue(); + _ = list.Contains(list[(int)itemIndex]).Should().BeTrue(); } } @@ -292,27 +287,18 @@ namespace Recyclable.CollectionsTests } [Theory] - [MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))] - public void ContainsShouldNotFindNonExistingItems(string testCase, IEnumerable testData, long itemsCount) + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void ContainsShouldNotFindNonExistingItems(string testCase, IEnumerable testData, long itemsCount, in long[] itemIndexes) { - if (itemsCount == 0) - { - return; - } - // Prepare using var list = new RecyclableList(testData, (int)itemsCount); _ = testData.Any().Should().BeTrue("we need items on the list that we can look for"); - // Act - foreach (var item in testData) + // Act & Validate + foreach (var itemIndex in itemIndexes) { - // Validate - _ = list.Contains(-item).Should().BeFalse(); + _ = list.Contains(-list[(int)itemIndex]).Should().BeFalse(); } - - // Validate - _ = list.Contains(0).Should().BeFalse(); } [Theory] @@ -362,198 +348,130 @@ namespace Recyclable.CollectionsTests } [Theory] - [MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))] - public void IndexOfShouldNotFindNonExistingItems(string testCase, IEnumerable testData, long itemsCount) + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void IndexOfShouldNotFindNonExistingItems(string testCase, IEnumerable testData, long itemsCount, in long[] itemIndexes) { - // Prepare - if (itemsCount == 0) - { - // There is nothing to search for when our lists are empty - return; - } - // Prepare using var list = new RecyclableList(testData, (int)itemsCount); _ = testData.Any().Should().BeTrue("we need items on the list that we can look for"); - // Act - foreach (var item in testData) + // Act & Validate + foreach (var itemIndex in itemIndexes) { - // Validate - _ = list.IndexOf(-item).Should().Be(-1); + _ = list.IndexOf(-list[(int)itemIndex]).Should().Be(-1); } - - // Validate - _ = list.IndexOf(0).Should().Be(-1); } [Theory] - [MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))] - public void IndexOfShouldReturnCorrectIndexes(string testCase, IEnumerable testData, long itemsCount) + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void IndexOfShouldReturnCorrectIndexes(string testCase, IEnumerable testData, long itemsCount, in long[] itemIndexes) { // Prepare using var list = new RecyclableList(testData, (int)itemsCount); // Act & Validate - foreach (var index in testData) + foreach (var itemIndex in itemIndexes) { - var actual = list.IndexOf(index); - _ = actual.Should().Be((int)(index - 1)); + _ = list.IndexOf(list[(int)itemIndex]).Should().Be((int)itemIndex); } } [Theory] - [MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))] - public void InitializeSortClearAddRangeRemoveShouldSucceed(string testCase, IEnumerable testData, long itemsCount) + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void InitializeSortClearAddRangeRemoveShouldSucceed(string testCase, IEnumerable testData, long itemsCount, in long[] itemIndexes) { - if (itemsCount == 0) - { - return; - } - - // Act - using RecyclableList list = new(testData, (int)itemsCount); - QuickSortExtensions.QuickSort(list); - - // Validate - _ = list.Count.Should().Be((int)itemsCount); - _ = list.Should().Equal(testData); - - // Act - list.Clear(); - - // Validate - _ = list.Count.Should().Be(0); - _ = list.Should().BeEmpty(); - - // Act - list.AddRange(testData); - - // Validate - _ = list.Count.Should().Be((int)itemsCount); - _ = list.Should().Equal(testData); - - // Act - foreach (var item in testData.Reverse()) - { - _ = list.Remove(item).Should().BeTrue(); - } - } - - [Theory] - [MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))] - public void InsertAtTheBeginningShouldMoveItems(string testCase, IEnumerable testData, long itemsCount) - { - // Prepare - using var list = new RecyclableList((int)itemsCount); - - // Act - foreach (var item in testData) - { - list.Insert(0, item); - _ = list.Count.Should().Be((int)item); - } - - // Validate - _ = list.Capacity.Should().BeGreaterThanOrEqualTo((int)itemsCount); - _ = list.Count.Should().Be((int)itemsCount); - _ = list.Should().Equal(testData.Reverse()); - } - - [Theory] - [MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))] - public void InsertAtTheEndShouldAppend(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize) - { - // Prepare - using var list = new RecyclableList(testData); - - // Act - list.Insert((int)itemsCount, -1); - - // Assert - _ = list.Count.Should().Be((int)itemsCount + 1); - _ = list[^1].Should().Be(-1); - } - - [Theory] - [MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))] - public void RemoveAtFromTheBeginningShouldSucceed(string testCase, IEnumerable testData, long itemsCount) - { - // Prepare - using var list = new RecyclableList(testData, (int)itemsCount); - - // Act & Validate - for (var deleted = 1; deleted <= itemsCount; deleted++) + foreach (var itemIndex in itemIndexes) { // Act - list.RemoveAt(0); + using RecyclableList list = new(testData, (int)itemsCount); + QuickSortExtensions.QuickSort(list); // Validate - _ = list.Count.Should().Be((int)(itemsCount - deleted)); - _ = list.Should().Equal(testData.Skip(deleted)); - } + _ = list.Count.Should().Be((int)itemsCount); + _ = list.Should().Equal(testData); - _ = list.Should().BeEmpty(); - _ = list.Capacity.Should().BeGreaterThanOrEqualTo((int)itemsCount); - } - - [Theory] - [MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))] - public void RemoveAtFromTheEndShouldSucceed(string testCase, IEnumerable testData, long itemsCount) - { - // Prepare - using var list = new RecyclableList(testData, (int)itemsCount); - - // Act & Validate - for (var deleted = 1; deleted <= itemsCount; deleted++) - { // Act - list.RemoveAt(list.Count - 1); + list.Clear(); // Validate - _ = list.Count.Should().Be((int)(itemsCount - deleted)); - _ = list.Should().Equal(testData.Take((int)(itemsCount - deleted))); - } + _ = list.Count.Should().Be(0); + _ = list.Should().BeEmpty(); - _ = list.Should().BeEmpty(); - _ = list.Capacity.Should().BeGreaterThanOrEqualTo((int)itemsCount); - } - - [Theory] - [MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))] - public void RemoveFromTheBeginningShouldRemoveTheCorrectItem(string testCase, IEnumerable testData, long itemsCount) - { - // Prepare - using var list = new RecyclableList(testData, (int)itemsCount); - - // Act & Validate - for (var deleted = 1; deleted <= itemsCount; deleted++) - { // Act - _ = list.Remove(deleted).Should().BeTrue(); + list.AddRange(testData); // Validate - _ = list.Count.Should().Be((int)(itemsCount - deleted)); - _ = list.Should().Equal(testData.Skip(deleted)); + _ = list.Count.Should().Be((int)itemsCount); + _ = list.Should().Equal(testData); + + // Act + _ = list.Remove(list[(int)itemIndex]).Should().BeTrue(); } } [Theory] - [MemberData(nameof(RecyclableLongListTestData.SourceDataVariants), MemberType = typeof(RecyclableLongListTestData))] - public void RemoveFromTheEndShouldRemoveTheCorrectItem(string testCase, IEnumerable testData, long itemsCount) + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void InsertShouldMoveItems(string testCase, IEnumerable testData, long itemsCount, in long[] itemIndexes) { - // Act & Validate - using var list = new RecyclableList(testData, (int)itemsCount); - long deleted = 0; - foreach (var item in testData.Reverse()) + foreach (var itemIndex in itemIndexes) { + // Prepare + using var list = new RecyclableList(testData, (int)itemsCount); + var item = list[(int)itemIndex]; + var expectedItems = list.ToList(); + expectedItems.Insert((int)itemIndex, item); + + // Act + 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.Should().Equal(expectedItems); + } + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void RemoveAtShouldSucceed(string testCase, IEnumerable testData, long itemsCount, in long[] itemIndexes) + { + foreach (var itemIndex in itemIndexes) + { + // Prepare + using var list = new RecyclableList(testData, (int)itemsCount); + var expectedItems = testData.ToList(); + expectedItems.RemoveAt((int)itemIndex); + + // Act + list.RemoveAt((int)itemIndex); + + // Validate + _ = list.Count.Should().Be((int)(itemsCount - 1)); + _ = list.Should().Equal(expectedItems); + _ = list.Capacity.Should().BeGreaterThanOrEqualTo((int)itemsCount); + } + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void RemoveShouldRemoveTheCorrectItem(string testCase, IEnumerable testData, long itemsCount, in long[] itemIndexes) + { + foreach (var itemIndex in itemIndexes) + { + // Prepare + using var list = new RecyclableList(testData, (int)itemsCount); + var item = list[(int)itemIndex]; + var expectedItems = testData.ToList(); + expectedItems.RemoveAt((int)itemIndex); + // Act _ = list.Remove(item).Should().BeTrue(); - deleted++; // Validate - _ = list.Count.Should().Be((int)(itemsCount - deleted)); - _ = list.Should().Equal(testData.Take((int)(itemsCount - deleted))); + _ = list.Count.Should().Be((int)(itemsCount - 1)); + _ = list.Should().Equal(expectedItems); } } diff --git a/Recyclable.CollectionsTests/RecyclableLongListTestData.cs b/Recyclable.CollectionsTests/RecyclableLongListTestData.cs index bf4d8b9..665c589 100644 --- a/Recyclable.CollectionsTests/RecyclableLongListTestData.cs +++ b/Recyclable.CollectionsTests/RecyclableLongListTestData.cs @@ -1,10 +1,17 @@ -using Recyclable.Collections; +using MoreLinq; +using Open.Numeric.Primes; +using Recyclable.Collections; +using System.Numerics; namespace Recyclable.CollectionsTests { public static class RecyclableLongListTestData { - private static IEnumerable DoCreateTestData(long itemsCount) + + private static readonly object[] _sourceRefData = Enumerable.Range(1, 100).Cast().ToArray(); + public static object[] SourceRefData => _sourceRefData; + + public static IEnumerable CreateTestData(long itemsCount) { for (long index = 1; index <= itemsCount; index++) { @@ -12,26 +19,38 @@ namespace Recyclable.CollectionsTests } } - public static IEnumerable CreateTestData(long itemsCount) => DoCreateTestData(itemsCount); + public static IEnumerable CreateRefTestData(long itemsCount) + { + for (long index = 1; index <= itemsCount; index++) + { + yield return index; + } + } public static readonly IEnumerable ItemsCountVariants = new long[] { - 0, 1, 2, 3, 10, 16, 32, 64, 128, 256, 333, 512, 1_024, 8_192, 16_384, - 100_000 + //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 }; public static readonly IEnumerable BlockSizeVariants = new int[] { - 1, 2, 4, 8, 10, 16, 32, 64, 128, 256, 512, 1_024, 2_048, 4_096, - 8_192, 16_384 + 1, 2, 4, 16, RecyclableDefaults.MinPooledArrayLength - 5, (int)BitOperations.RoundUpToPowerOf2(RecyclableDefaults.MinPooledArrayLength) }; + private static bool IsValidBlockSize(long itemsCount, long blockSize) => itemsCount is < 1024 || blockSize is > 8; + private static IEnumerable GetTargetDataVariants() { foreach (var itemsCount in ItemsCountVariants) { foreach (var targetBlockSize in BlockSizeVariants) { + if (!IsValidBlockSize(itemsCount, targetBlockSize)) + { + continue; + } + yield return new object[] { itemsCount, targetBlockSize }; } } @@ -39,55 +58,244 @@ namespace Recyclable.CollectionsTests public static IEnumerable TargetDataVariants => GetTargetDataVariants(); - private static IEnumerable CreateSourceDataVariants() + private static IEnumerable CombineSourceDataWithBlockSize(IEnumerable sourceData) { - foreach (var itemsCount in ItemsCountVariants) - { - yield return new object[] { $"int[{itemsCount}]", CreateTestData(itemsCount).ToArray(), itemsCount }; - yield return new object[] { $"List(itemsCount: {itemsCount}", CreateTestData(itemsCount).ToList(), itemsCount }; - yield return new object[] { $"RecyclableList(itemsCount: {itemsCount})", CreateTestData(itemsCount).ToRecyclableList(), itemsCount }; - foreach (var sourceBlockSize in BlockSizeVariants) - { - yield return new object[] - { - $"RecyclableLongList(itemsCount: {itemsCount}, minBlockSize: {sourceBlockSize})", - CreateTestData(itemsCount).ToRecyclableLongList(sourceBlockSize), - itemsCount - }; - } - - yield return new object[] { $"IList(itemsCount: {itemsCount})", new CustomIList(CreateTestData(itemsCount)), itemsCount }; - yield return new object[] { $"IEnumerable(itemsCount: {itemsCount}) with non-enumerated count", CreateTestData(itemsCount), itemsCount }; - yield return new object[] { $"IEnumerable(itemsCount: {itemsCount}) without non-enumerated count", new EnumerableWithoutCount(CreateTestData(itemsCount)), itemsCount }; - } - } - - public static IEnumerable SourceDataVariants => CreateSourceDataVariants(); - - private static IEnumerable CreateSourceEmptyDataVariants() => new[] - { - new object[] { "int[]", Array.Empty() }, - new object[] { "List", Array.Empty().ToList() }, - new object[] { "RecyclableList", Array.Empty().ToRecyclableList() }, - new object[] { "RecyclableLongList", Array.Empty().ToRecyclableLongList() }, - new object[] { "IList", new CustomIList(Array.Empty()) }, - new object[] { "IEnumerable with non-enumerated count", Array.Empty().AsEnumerable() }, - new object[] { "IEnumerable without non-enumerated count", new EnumerableWithoutCount(Array.Empty()) } - }; - - public static IEnumerable EmptySourceDataVariants => CreateSourceEmptyDataVariants(); - - private static IEnumerable CreateSourceTargetDataVariants() - { - foreach (var testData in SourceDataVariants) + foreach (var testData in sourceData) { foreach (var targetBlockSize in BlockSizeVariants) { + if (!IsValidBlockSize((long)testData[2], targetBlockSize)) + { + continue; + } + yield return new object[] { testData[0], testData[1], testData[2], targetBlockSize }; } } } - public static IEnumerable SourceTargetDataVariants => CreateSourceTargetDataVariants(); + private static IEnumerable CreateSourceDataVariants(long itemsCount, bool refType) + { + GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true); + + IDisposable disposable; + + switch (refType) + { + case false: + IEnumerable testData = CreateTestData(itemsCount).ToArray(); + + yield return new object[] { $"int[{itemsCount}]", testData, itemsCount }; + //GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true); + + yield return new object[] { $"List(itemsCount: {itemsCount}", testData.ToList(), itemsCount }; + //GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true); + + yield return new object[] { $"RecyclableList(itemsCount: {itemsCount})", disposable = testData.ToRecyclableList(), itemsCount }; + //disposable.Dispose(); + //GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true); + + foreach (var sourceBlockSize in BlockSizeVariants) + { + if (!IsValidBlockSize(itemsCount, sourceBlockSize)) + { + continue; + } + + yield return new object[] + { + $"RecyclableLongList(itemsCount: {itemsCount}, minBlockSize: {sourceBlockSize})", + disposable = testData.ToRecyclableLongList(sourceBlockSize), + itemsCount + }; + + //disposable.Dispose(); + //GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true); + } + + yield return new object[] { $"IList(itemsCount: {itemsCount})", new CustomIList(testData), itemsCount }; + //GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true); + + yield return new object[] { $"IEnumerable(itemsCount: {itemsCount}) with non-enumerated count", testData, itemsCount }; + //GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true); + + yield return new object[] { $"IEnumerable(itemsCount: {itemsCount}) without non-enumerated count", new EnumerableWithoutCount(testData), itemsCount }; + //GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true); + + break; + + case true: + IEnumerable testRefData = CreateRefTestData(itemsCount).ToArray(); + + yield return new object[] { $"int[{itemsCount}]", testRefData, itemsCount }; + //GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true); + + yield return new object[] { $"List(itemsCount: {itemsCount}", testRefData.ToList(), itemsCount }; + //GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true); + + yield return new object[] { $"RecyclableList(itemsCount: {itemsCount})", disposable = testRefData.ToRecyclableList(), itemsCount }; + + //disposable.Dispose(); + //GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true); + + foreach (var sourceBlockSize in BlockSizeVariants) + { + if (!IsValidBlockSize(itemsCount, sourceBlockSize)) + { + continue; + } + + yield return new object[] + { + $"RecyclableLongList(itemsCount: {itemsCount}, minBlockSize: {sourceBlockSize})", + disposable = testRefData.ToRecyclableLongList(sourceBlockSize), + itemsCount + }; + + //disposable.Dispose(); + //GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true); + } + + yield return new object[] { $"IList(itemsCount: {itemsCount})", new CustomIList(testRefData), itemsCount }; + //GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true); + + yield return new object[] { $"IEnumerable(itemsCount: {itemsCount}) with non-enumerated count", testRefData, itemsCount }; + //GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true); + + yield return new object[] { $"IEnumerable(itemsCount: {itemsCount}) without non-enumerated count", new EnumerableWithoutCount(testRefData), itemsCount }; + //GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true); + + break; + } + } + + private static IEnumerable CreateSourceDataVariants(bool refType) + { + foreach (var itemsCount in ItemsCountVariants) + { + foreach (var variant in CreateSourceDataVariants(itemsCount, refType)) + { + yield return variant; + } + } + } + + public static IEnumerable EmptySourceDataVariants => CreateSourceDataVariants(0, false); + public static IEnumerable SourceDataVariants => CreateSourceDataVariants(false); + public static IEnumerable SourceDataWithItemIndexVariants + { + get + { + foreach (var testCase in CreateSourceDataVariants(false).Where(testCase => (long)testCase[2] > 0)) + { + var itemIndexes = CreateItemIndexVariants((long)testCase[2], RecyclableDefaults.BlockSize).ToArray(); + yield return new object[] { testCase[0], testCase[1], testCase[2], itemIndexes }; + } + } + } + + public static IEnumerable SourceRefDataWithItemIndexVariants + { + get + { + IEnumerable testCases = CreateSourceDataVariants(true).Where(testCase => (long)testCase[2] > 0); + foreach (var testCase in testCases) + { + var itemIndexes = CreateItemIndexVariants((long)testCase[2], RecyclableDefaults.BlockSize).ToArray(); + + yield return new object[] { testCase[0], testCase[1], testCase[2], itemIndexes }; + } + } + } + + public static IEnumerable SourceDataWithBlockSizeVariants => CombineSourceDataWithBlockSize(SourceDataVariants); + public static IEnumerable SourceRefDataVariants => CreateSourceDataVariants(true); + public static IEnumerable SourceRefDataWithBlockSizeVariants { get; } = CombineSourceDataWithBlockSize(SourceRefDataVariants); + + private static IEnumerable _initialItemIndexVariants => new long[] { 0, 1, 2, 3, 4, 5 }; + private static IEnumerable CreateItemIndexVariants(long itemsCount, int blockSize) + { + IEnumerable primes = _initialItemIndexVariants.Concat( + _initialItemIndexVariants.SelectMany(index => new long[] { blockSize - index, blockSize + index }) + //new long[] { blockSize - 3, blockSize - 2, blockSize - 1, blockSize, blockSize + 1, blockSize + 2, blockSize + 3 } + ).ToArray(); + + IEnumerable second = Prime.Numbers.StartingAt(7) + .TakeUntil(prime => + prime > ((blockSize * 2) - 1) || + prime >= itemsCount + ); + + primes = primes.Concat(second).ToArray(); + + primes = primes.Concat( + primes.Select(index => itemsCount - index - 1) + ) + .Where(index => itemsCount > 0 && index >= 0 && index < itemsCount).ToArray() + .Distinct().ToArray(); + + return primes; + + //IEnumerable CreateItemIndexVariants() + //{ + //if (itemsCount > 0) + //{ + // yield return 0; + //} + + //foreach (long prime in primes) + //{ + // yield return prime; + + // long index = itemsCount - prime; + // if (index >= 0) + // { + // yield return index; + // } + //} + + //for (long index = itemsCount - (blockSize * 2) - 1; index < itemsCount; index++) + //{ + // if (index >= 0) + // { + // yield return index; + // } + //} + + //if ((blockSize / 16 * 16) + 1 < itemsCount) + //{ + // yield return (blockSize / 16 * 16) + 1; + //} + //} + + //return CreateItemIndexVariants(); + } + + public static IEnumerable CombineSourceRefDataWithBlockSizeWithItemIndex(bool refType) + { + IEnumerable sourceData = refType switch + { + false => SourceDataWithBlockSizeVariants, + true => SourceRefDataWithBlockSizeVariants + }; + + foreach (var testCase in sourceData) + { + long itemsCount = (long)testCase[2]; + if (itemsCount == 0) + { + continue; + } + + long[] itemIndexes = CreateItemIndexVariants(itemsCount, (int)testCase[3]) + .Where(_ => IsValidBlockSize((long)testCase[2], (int)testCase[3])) + .ToArray(); + + yield return new object[] { testCase[0], testCase[1], (long)testCase[2], (int)testCase[3], itemIndexes }; + } + } + + public static IEnumerable SourceDataWithBlockSizeWithItemIndexVariants { get; } = CombineSourceRefDataWithBlockSizeWithItemIndex(false); + public static IEnumerable SourceRefDataWithBlockSizeWithItemIndexVariants { get; } = CombineSourceRefDataWithBlockSizeWithItemIndex(true); } } \ No newline at end of file diff --git a/Recyclable.CollectionsTests/RecyclableLongListTests.cs b/Recyclable.CollectionsTests/RecyclableLongListTests.cs index 8b2d7b2..24c796b 100644 --- a/Recyclable.CollectionsTests/RecyclableLongListTests.cs +++ b/Recyclable.CollectionsTests/RecyclableLongListTests.cs @@ -1,6 +1,7 @@ using FluentAssertions; using Recyclable.Collections; using Recyclable.Collections.Linq; +using System.Numerics; #pragma warning disable xUnit1026, RCS1163, IDE0060, RCS1235 @@ -8,8 +9,24 @@ namespace Recyclable.CollectionsTests { public class RecyclableLongListTests { + 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) + { + expectedCapacity = ((itemsCount / targetBlockSize) + (itemsCount % targetBlockSize > 0 ? 1 : 0)) * targetBlockSize; + } + + return expectedCapacity; + } + [Theory] - [MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeVariants), MemberType = typeof(RecyclableLongListTestData))] public void AddRangeShouldAddItemsInCorrectOrder(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize) { // Prepare @@ -55,7 +72,7 @@ namespace Recyclable.CollectionsTests [Theory] [MemberData(nameof(RecyclableLongListTestData.EmptySourceDataVariants), MemberType = typeof(RecyclableLongListTestData))] - public void AddRangeShouldDoNothingWhenSourceIsEmpty(string testCase, IEnumerable testData) + public void AddRangeShouldDoNothingWhenSourceIsEmpty(string testCase, IEnumerable testData, long itemsCount) { // Prepare using var list = new RecyclableLongList(); @@ -98,7 +115,7 @@ namespace Recyclable.CollectionsTests } [Theory] - [MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeVariants), MemberType = typeof(RecyclableLongListTestData))] public void AddRangeShouldNotOverrideItems(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize) { // Prepare @@ -154,7 +171,7 @@ namespace Recyclable.CollectionsTests } [Theory] - [MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeVariants), MemberType = typeof(RecyclableLongListTestData))] public void AddShouldAcceptDuplicates(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize) { // Prepare @@ -174,7 +191,7 @@ namespace Recyclable.CollectionsTests } [Theory] - [MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeVariants), MemberType = typeof(RecyclableLongListTestData))] public void AddShouldAddItems(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize) { // Prepare @@ -193,21 +210,7 @@ namespace Recyclable.CollectionsTests } [Theory] - [MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))] - public void GetByIndexShouldReturnCorrectItem(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize) - { - // Prepare - using var list = new RecyclableLongList(testData, minBlockSize: targetBlockSize, itemsCount); - - // Act & Validate - foreach (var item in testData) - { - _ = list[item - 1].Should().Be(item); - } - } - - [Theory] - [MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeVariants), MemberType = typeof(RecyclableLongListTestData))] //[InlineData("Debugging case", new long[1] { 1 }, 1, 2)] public void AddShouldAddItemWhenAfterAddRange(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize) { @@ -226,7 +229,7 @@ namespace Recyclable.CollectionsTests } [Theory] - [MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeVariants), MemberType = typeof(RecyclableLongListTestData))] public void ClearShouldRemoveAllItems(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize) { // Prepare @@ -243,7 +246,7 @@ namespace Recyclable.CollectionsTests } [Theory] - [MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeVariants), MemberType = typeof(RecyclableLongListTestData))] public void ConsecutiveDisposeShouldSucceed(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize) { // Prepare @@ -255,14 +258,14 @@ namespace Recyclable.CollectionsTests } [Theory] - [MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeVariants), MemberType = typeof(RecyclableLongListTestData))] public void ConstructorShouldAcceptDuplicates(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize) { // Prepare testData = testData.Concat(testData).ToArray(); // Act - using var list = new RecyclableLongList(expectedData, minBlockSize: targetBlockSize, itemsCount); + using var list = new RecyclableLongList(testData, minBlockSize: targetBlockSize, itemsCount); // Validate _ = list.LongCount.Should().Be(itemsCount * 2); @@ -270,7 +273,7 @@ namespace Recyclable.CollectionsTests } [Theory] - [MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeVariants), MemberType = typeof(RecyclableLongListTestData))] public void ConstructorSourceShouldInitializeList(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize) { // Act @@ -283,24 +286,17 @@ namespace Recyclable.CollectionsTests } [Theory] - [MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))] - public void ContainsShouldFindAllItems(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize) + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void ContainsShouldFindAllItems(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize, in long[] itemIndexes) { // Prepare - if (itemsCount == 0) - { - // There is nothing to search for when our lists are empty - return; - } - using var list = new RecyclableLongList(testData, minBlockSize: targetBlockSize, itemsCount); _ = testData.Any().Should().BeTrue("we need items on the list that we can look for"); - // Act - foreach (var item in testData) + // Act & Validate + foreach (var itemIndex in itemIndexes) { - // Validate - _ = list.Contains(item).Should().BeTrue($"we searched for {item}"); + _ = list.Contains(list[itemIndex]).Should().BeTrue($"we searched for {list[itemIndex]}"); } } @@ -317,32 +313,22 @@ namespace Recyclable.CollectionsTests } [Theory] - [MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))] - public void ContainsShouldNotFindNonExistingItems(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize) + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void ContainsShouldNotFindNonExistingItems(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize, in long[] itemIndexes) { // Prepare - if (itemsCount == 0) - { - // There is nothing to search for when our lists are empty - return; - } - using var list = new RecyclableLongList(testData, minBlockSize: targetBlockSize, itemsCount); _ = testData.Any().Should().BeTrue("we need items on the list that we can look for"); - // Act - foreach (var item in testData) + // Act & Validate + foreach (var itemIndex in itemIndexes) { - // Validate - _ = list.Contains(-item).Should().BeFalse(); + _ = list.Contains(-list[itemIndex]).Should().BeFalse(); } - - // Validate - this case is special, because this is the default item value - _ = list.Contains(0).Should().BeFalse(); } [Theory] - [MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeVariants), MemberType = typeof(RecyclableLongListTestData))] //[InlineData("Debugging case", new[] { 1L, 2, }, 2, 2)] public void CopyToShouldCopyAllItemsInTheCorrectOrder(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize) { @@ -356,7 +342,7 @@ namespace Recyclable.CollectionsTests } [Theory] - [MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeVariants), MemberType = typeof(RecyclableLongListTestData))] public void DisposeShouldSucceed(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize) { // Prepare @@ -371,7 +357,7 @@ namespace Recyclable.CollectionsTests } [Theory] - [MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeVariants), MemberType = typeof(RecyclableLongListTestData))] public void EnumerateShouldYieldAllItemsInCorrectOrder(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize) { // Prepare @@ -390,21 +376,17 @@ namespace Recyclable.CollectionsTests } [Theory] - [MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))] - public void VersionedEnumerateShouldYieldAllItemsInCorrectOrder(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize) + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeVariants), MemberType = typeof(RecyclableLongListTestData))] + public void GetByIndexShouldReturnCorrectItem(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize) { // Prepare using var list = new RecyclableLongList(testData, minBlockSize: targetBlockSize, itemsCount); - // Act - var actual = new List((int)itemsCount); - foreach(var item in (IRecyclableVersionedLongList)list) + // Act & Validate + foreach (var item in testData) { - actual.Add(item); + _ = list[item - 1].Should().Be(item); } - - // Validate - _ = actual.Should().Equal(testData); } [Fact] @@ -419,118 +401,103 @@ namespace Recyclable.CollectionsTests } [Theory] - [MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))] - public void IndexOfShouldNotFindNonExistingItems(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize) + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void IndexOfShouldNotFindNonExistingItems(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize, in long[] itemIndexes) { - // Prepare - if (itemsCount == 0) - { - // There is nothing to search for when our lists are empty - return; - } - // Prepare using var list = new RecyclableLongList(testData, minBlockSize: targetBlockSize, itemsCount); _ = testData.Any().Should().BeTrue("we need items on the list that we can look for"); - // Act - foreach (var item in testData) + // Act & Validate + foreach (var itemIndex in itemIndexes) { - // Validate - _ = list.IndexOf(-item).Should().Be(-1); + _ = list.IndexOf(-list[itemIndex]).Should().Be(-1); } - - // Validate - _ = list.IndexOf(0).Should().Be(-1); } [Theory] - [MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))] - public void IndexOfShouldReturnCorrectIndexes(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize) + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void IndexOfShouldReturnCorrectIndexes(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize, in long[] itemIndexes) { // Prepare using var list = new RecyclableLongList(testData, minBlockSize: targetBlockSize, itemsCount); // Act & Validate - foreach (var item in testData) + foreach (var itemIndex in itemIndexes) { - var actual = list.IndexOf(item); - _ = actual.Should().Be((int)item - 1); + _ = list.IndexOf(list[itemIndex]).Should().Be((int)itemIndex); } } [Theory] - [MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))] - public void InitializeSortClearAddRangeRemoveShouldSucceed(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize) + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void InitializeSortClearAddRangeRemoveShouldSucceed(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize, in long[] itemIndexes) { - if (itemsCount == 0) + foreach (var itemIndex in itemIndexes) { - return; - } + // Prepare + using var testDataList = testData.ToRecyclableList(); + var item = testDataList[(int)itemIndex]; - // Act - using RecyclableLongList list = new(testData, minBlockSize: targetBlockSize, itemsCount); - QuickSortExtensions.QuickSort(list); + // Act + using RecyclableLongList list = new(testData, minBlockSize: targetBlockSize); + QuickSortExtensions.QuickSort(list); - // Validate - _ = list.LongCount.Should().Be(itemsCount); - _ = list.Should().Equal(testData); + // Validate + _ = list.LongCount.Should().Be(itemsCount); + _ = list.Should().Equal(testDataList); - // Act - list.Clear(); + // Act + list.Clear(); - // Validate - _ = list.LongCount.Should().Be(0); - _ = list.Should().BeEmpty(); + // Validate + _ = list.LongCount.Should().Be(0); + _ = list.Should().BeEmpty(); - // Act - list.AddRange(testData); + // Act + list.AddRange(testData); - // Validate - _ = list.LongCount.Should().Be(itemsCount); - _ = list.Should().Equal(testData); + // Validate + _ = list.LongCount.Should().Be(itemsCount); + _ = list.Should().Equal(testDataList); - // Act - foreach (var item in testData.Reverse()) - { + // Prepare + testDataList.RemoveAt((int)itemIndex); + + // Act _ = list.Remove(item).Should().BeTrue(); + _ = list.LongIndexOf(item).Should().Be(RecyclableDefaults.ItemNotFoundIndexLong); + _ = list.Contains(item).Should().BeFalse(); + _ = list.LongCount.Should().Be(itemsCount - 1); + _ = list.Should().Equal(testDataList); } } [Theory] - [MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))] - public void InsertAtTheBeginningShouldMoveItems(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize) + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void InsertShouldMoveItems(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize, in long[] itemIndexes) { - // Prepare - using var list = new RecyclableLongList(testData, targetBlockSize, itemsCount); - - // Act - foreach (var item in testData) + foreach (var itemIndex in itemIndexes) { - list.Insert(0, item); - _ = list.Count.Should().Be((int)item); + // Prepare + using var list = new RecyclableLongList(testData, targetBlockSize, itemsCount); + long expectedCapacity = CalculateExpectedCapacity(testCase, itemsCount, targetBlockSize); + + var item = list[itemIndex]; + var expectedItems = list.ToList(); + expectedItems.Insert((int)itemIndex, item); + + // Act + list.Insert(itemIndex, item); + + // Validate + + _ = list[itemIndex].Should().Be(item); + _ = list[itemIndex + 1].Should().Be(item); + _ = list.Capacity.Should().Be(expectedCapacity); + _ = list.Count.Should().Be((int)itemsCount + 1); + _ = list.Should().Equal(expectedItems); } - - // Validate - _ = list.Capacity.Should().BeGreaterThanOrEqualTo((int)itemsCount); - _ = list.Count.Should().Be((int)itemsCount); - _ = list.Should().Equal(testData.Reverse()); - } - - [Theory] - [MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))] - public void InsertAtTheEndShouldAppend(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize) - { - // Prepare - using var list = new RecyclableLongList(testData, minBlockSize: targetBlockSize); - - // Act - list.Insert(itemsCount, -1); - - // Assert - _ = list.LongCount.Should().Be(itemsCount + 1); - _ = list.LongCount.Should().Be(list.Count); - _ = list[list.LongCount - 1].Should().Be(-1); } [Fact] @@ -545,144 +512,95 @@ namespace Recyclable.CollectionsTests } [Theory] - [MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))] - public void LongIndexOfShouldNotFindNonExistingItems(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize) + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void LongIndexOfShouldNotFindNonExistingItems(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize, in long[] itemIndexes) { - if (itemsCount == 0) - { - return; - } - // Prepare using var list = new RecyclableLongList(testData, minBlockSize: targetBlockSize, itemsCount); _ = testData.Any().Should().BeTrue("we need items on the list that we can look for"); - // Act - foreach (var item in testData) + // Act & Validate + foreach (var itemIndex in itemIndexes) { + _ = list.LongIndexOf(-list[itemIndex]).Should().Be(-1); + } + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void LongIndexOfShouldReturnCorrectIndexes(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize, in long[] itemIndexes) + { + // Prepare + using var list = new RecyclableLongList(testData, minBlockSize: targetBlockSize, itemsCount); + + // Act & Validate + foreach (var itemIndex in itemIndexes) + { + _ = list.LongIndexOf(list[itemIndex]).Should().Be(itemIndex); + } + } + + [Theory] + [MemberData(nameof(RecyclableLongListTestData.SourceDataWithBlockSizeWithItemIndexVariants), MemberType = typeof(RecyclableLongListTestData))] + public void RemoveAtShouldSucceed(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize, in long[] itemIndexes) + { + foreach (var itemIndex in itemIndexes) + { + // Prepare + using var list = new RecyclableLongList(testData, minBlockSize: targetBlockSize); + var expectedItems = list.ToList(); + expectedItems.RemoveAt((int)itemIndex); + + // Act + list.RemoveAt(itemIndex); + // Validate - _ = list.LongIndexOf(-item).Should().Be(-1); + _ = list.Capacity.Should().BeGreaterThanOrEqualTo((int)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 RemoveShouldRemoveTheCorrectItem(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize, in long[] itemIndexes) + { + foreach (var itemIndex in itemIndexes) + { + // Prepare + using var list = new RecyclableLongList(testData, minBlockSize: targetBlockSize); + var expectedItems = list.ToList(); + var item = expectedItems[(int)itemIndex]; + expectedItems.RemoveAt((int)itemIndex); + + // Act + _ = list.Remove(item).Should().BeTrue(); + + // Validate + _ = list.LongCount.Should().Be(itemsCount - 1); + _ = list.LastBlockWithData.Should().Be((int)(list.LongCount >> list.BlockSizePow2BitShift) - ((list.LongCount & list.BlockSizeMinus1) != 0 ? 0 : 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 testData, long itemsCount, int targetBlockSize) + { + // Prepare + using var list = new RecyclableLongList(testData, minBlockSize: targetBlockSize, itemsCount); + + // Act + using var actual = new RecyclableList((int)itemsCount); + foreach (var item in (IRecyclableVersionedLongList)list) + { + actual.Add(item); } // Validate - _ = list.LongIndexOf(0).Should().Be(-1); - } - - [Theory] - [MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))] - public void LongIndexOfShouldReturnCorrectIndexes(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize) - { - // Prepare - using var list = new RecyclableLongList(testData, minBlockSize: targetBlockSize, itemsCount); - - // Act & Validate - foreach (var item in testData) - { - // Act - var index = list.LongIndexOf(item); - - // Validate - _ = index.Should().Be(item - 1); - } - } - - [Theory] - [MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))] - public void RemoveAtFromTheBeginningShouldSucceed(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize) - { - // Prepare - using var list = new RecyclableLongList(testData, minBlockSize: targetBlockSize, itemsCount); - var listAsArray = list.ToArray(); - - // Act & Validate - for (var deleted = 1; deleted <= itemsCount; deleted++) - { - // Act - list.RemoveAt(0); - - // Validate - _ = list.Count.Should().Be((int)(itemsCount - deleted)); - _ = list.LastBlockWithData.Should().Be((int)(list.LongCount >> list.BlockSizePow2BitShift) - ((list.LongCount & list.BlockSizeMinus1) != 0 ? 0 : 1)); - _ = list.Should().Equal(listAsArray[deleted..]); - } - - _ = list.Should().BeEmpty(); - _ = list.Capacity.Should().BeGreaterThanOrEqualTo((int)itemsCount); - _ = list.LastBlockWithData.Should().Be(RecyclableDefaults.ItemNotFoundIndex); - } - - [Theory] - [MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))] - public void RemoveAtFromTheEndShouldSucceed(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize) - { - // Prepare - using var list = new RecyclableLongList(testData, minBlockSize: targetBlockSize, itemsCount); - var listAsArray = list.ToArray(); - - // Act & Validate - for (var deleted = 1; deleted <= itemsCount; deleted++) - { - // Act - list.RemoveAt(list.Count - 1); - - // Validate - _ = list.Count.Should().Be((int)(itemsCount - deleted)); - _ = list.LastBlockWithData.Should().Be((int)(list.LongCount >> list.BlockSizePow2BitShift) - ((list.LongCount & list.BlockSizeMinus1) != 0 ? 0 : 1)); - _ = list.Should().Equal(listAsArray[..(int)(itemsCount - deleted)]); - } - - _ = list.Should().BeEmpty(); - _ = list.Capacity.Should().BeGreaterThanOrEqualTo((int)itemsCount); - _ = list.LastBlockWithData.Should().Be(RecyclableDefaults.ItemNotFoundIndex); - } - - [Theory] - [MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))] - public void RemoveFromTheBeginningShouldRemoveTheCorrectItem(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize) - { - // Prepare - using var list = new RecyclableLongList(testData, targetBlockSize, itemsCount) - var listAsArray = list.ToArray(); - - // Act & Validate - for (var deleted = 1; deleted <= itemsCount; deleted++) - { - // Act - _ = list.Remove(deleted).Should().BeTrue(); - - // Validate - _ = list.Count.Should().Be((int)(itemsCount - deleted)); - _ = list.LastBlockWithData.Should().Be((int)(list.LongCount >> list.BlockSizePow2BitShift) - ((list.LongCount & list.BlockSizeMinus1) != 0 ? 0 : 1)); - _ = list.Should().Equal(listAsArray[deleted..]); - } - - _ = list.Should().BeEmpty(); - _ = list.Capacity.Should().BeGreaterThanOrEqualTo((int)itemsCount); - _ = list.LastBlockWithData.Should().Be(RecyclableDefaults.ItemNotFoundIndex); - } - - [Theory] - [MemberData(nameof(RecyclableLongListTestData.SourceTargetDataVariants), MemberType = typeof(RecyclableLongListTestData))] - public void RemoveFromTheEndShouldRemoveTheCorrectItem(string testCase, IEnumerable testData, long itemsCount, int targetBlockSize) - { - // Prepare - using var list = new RecyclableLongList(testData, minBlockSize: targetBlockSize, itemsCount); - var listAsArray = testData.ToArray(); - int removedCount = 0; - foreach (var item in testData.Reverse()) - { - // Act & Validate - _ = list.Remove(item).Should().BeTrue($"we search for {item} which should exist"); - removedCount++; - var expectedList = listAsArray[..((int)itemsCount - removedCount)]; - _ = list.LastBlockWithData.Should().Be((int)(list.LongCount >> list.BlockSizePow2BitShift) - ((list.LongCount & list.BlockSizeMinus1) != 0 ? 0 : 1)); - _ = list.LongCount.Should().Be(expectedList.Length); - _ = list.Should().Equal(expectedList); - } - - _ = list.Should().BeEmpty(); - _ = list.Capacity.Should().BeGreaterThanOrEqualTo((int)itemsCount); - _ = list.LastBlockWithData.Should().Be(RecyclableDefaults.ItemNotFoundIndex); + _ = actual.Should().Equal(testData); } } } \ No newline at end of file