diff --git a/src/libraries/System.ObjectModel/src/System/Collections/ObjectModel/ObservableCollection.cs b/src/libraries/System.ObjectModel/src/System/Collections/ObjectModel/ObservableCollection.cs index 1de8f8294be540..0ed630becc29a2 100644 --- a/src/libraries/System.ObjectModel/src/System/Collections/ObjectModel/ObservableCollection.cs +++ b/src/libraries/System.ObjectModel/src/System/Collections/ObjectModel/ObservableCollection.cs @@ -26,6 +26,9 @@ public class ObservableCollection : Collection, INotifyCollectionChanged, [NonSerialized] private int _blockReentrancyCount; + [NonSerialized] + private bool _skipRaisingEvents; + /// /// Initializes a new instance of ObservableCollection that is empty and has default initial capacity. /// @@ -110,9 +113,115 @@ protected override void RemoveItem(int index) base.RemoveItem(index); - OnCountPropertyChanged(); - OnIndexerPropertyChanged(); - OnCollectionChanged(NotifyCollectionChangedAction.Remove, removedItem, index); + if (!_skipRaisingEvents) + { + OnCountPropertyChanged(); + OnIndexerPropertyChanged(); + OnCollectionChanged(NotifyCollectionChangedAction.Remove, removedItem, index); + } + } + + /// + /// Called by base class Collection<T> when a count of items is removed from the list; + /// raises a CollectionChanged event to any listeners. + /// + protected override void RemoveItemsRange(int index, int count) + { + CheckReentrancy(); + + T[] removedItems = Array.Empty(); + + bool ignore = _skipRaisingEvents; + if (!ignore) + { + if (Items.IsReadOnly) + { + throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); + } + + if ((uint)index > (uint)Items.Count) + { + throw new ArgumentOutOfRangeException(SR.ArgumentOutOfRange_InvalidThreshold); + } + + if (count < 0) + { + throw new ArgumentOutOfRangeException(SR.ArgumentOutOfRange_NeedNonNegNum); + } + + if (index > Items.Count - count) + { + throw new ArgumentException(SR.Argument_ItemNotExist); + } + + _skipRaisingEvents = true; + + if (count > 0) + { + removedItems = new T[count]; + for (int i = 0; i < count; i++) + { + removedItems[i] = this[index + i]; + } + } + } + + try + { + base.RemoveItemsRange(index, count); + } + finally + { + if (!ignore) + { + _skipRaisingEvents = false; + } + } + + if (count > 0 && !_skipRaisingEvents) + { + OnCountPropertyChanged(); + OnIndexerPropertyChanged(); + OnCollectionChanged(NotifyCollectionChangedAction.Remove, removedItems, index); + } + } + + /// + /// Called by base class Collection<T> when a collection of items is added to list; + /// raises a CollectionChanged event to any listeners. + /// + protected override void ReplaceItemsRange(int index, int count, IEnumerable collection) + { + CheckReentrancy(); + + _skipRaisingEvents = true; + + T[] itemsToReplace = new T[count - index]; + for (int i = index; i < count; i++) + { + itemsToReplace[i] = this[i]; + } + + try + { + base.ReplaceItemsRange(index, count, collection); + } + finally + { + _skipRaisingEvents = false; + } + + if (!_skipRaisingEvents) + { + IList newItems = collection is IList list ? list : new List(collection); + + if (newItems.Count > 0) + { + OnCountPropertyChanged(); + OnIndexerPropertyChanged(); + OnCollectionChanged(NotifyCollectionChangedAction.Replace, itemsToReplace, newItems, index); + } + } } /// @@ -124,9 +233,51 @@ protected override void InsertItem(int index, T item) CheckReentrancy(); base.InsertItem(index, item); - OnCountPropertyChanged(); - OnIndexerPropertyChanged(); - OnCollectionChanged(NotifyCollectionChangedAction.Add, item, index); + if (!_skipRaisingEvents) + { + OnCountPropertyChanged(); + OnIndexerPropertyChanged(); + OnCollectionChanged(NotifyCollectionChangedAction.Add, item, index); + } + } + + /// + /// Called by base class Collection<T> when a collection of items is added to list; + /// raises a CollectionChanged event to any listeners. + /// + protected override void InsertItemsRange(int index, IEnumerable collection) + { + CheckReentrancy(); + + bool ignore = _skipRaisingEvents; + if (!ignore) + { + _skipRaisingEvents = true; + } + + try + { + base.InsertItemsRange(index, collection); + } + finally + { + if (!ignore) + { + _skipRaisingEvents = false; + } + } + + if (!_skipRaisingEvents) + { + IList newItems = collection is IList list ? list : new List(collection); + + if (newItems.Count > 0) + { + OnCountPropertyChanged(); + OnIndexerPropertyChanged(); + OnCollectionChanged(NotifyCollectionChangedAction.Add, newItems, index); + } + } } /// @@ -262,6 +413,14 @@ private void OnCollectionChanged(NotifyCollectionChangedAction action, object? i OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, item, index, oldIndex)); } + /// + /// Helper to raise CollectionChanged event to any listeners + /// + private void OnCollectionChanged(NotifyCollectionChangedAction action, IList? items, int index) + { + OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, items, index)); + } + /// /// Helper to raise CollectionChanged event to any listeners /// diff --git a/src/libraries/System.ObjectModel/tests/ObservableCollection/ObservableCollection_RangeTest.cs b/src/libraries/System.ObjectModel/tests/ObservableCollection/ObservableCollection_RangeTest.cs new file mode 100644 index 00000000000000..0e5fcdf85f1a6a --- /dev/null +++ b/src/libraries/System.ObjectModel/tests/ObservableCollection/ObservableCollection_RangeTest.cs @@ -0,0 +1,273 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Linq; +using Xunit; + +namespace System.Collections.ObjectModel.Tests +{ + /// + /// Tests the public methods in ObservableCollection as well as verifies + /// that the CollectionChanged events and eventargs are fired and populated + /// properly. + /// + public static class RangeTests + { + [Fact] + public static void InsertRange_NotifyCollectionChanged_Beginning_Test() + { + int[] dataToInsert = new int[] { 1, 2, 3, 4, 5 }; + int[] initialData = new int[] { 10, 11, 12, 13 }; + int eventCounter = 0; + ObservableCollection collection = new ObservableCollection(initialData); + collection.CollectionChanged += (o, e) => eventCounter++; + + collection.InsertRange(0, dataToInsert); + + Assert.Equal(dataToInsert.Length + initialData.Length, collection.Count); + Assert.Equal(1, eventCounter); + + int[] collectionAssertion = collection.ToArray(); + Assert.Equal(dataToInsert, collectionAssertion.AsSpan(0, 5).ToArray()); + Assert.Equal(initialData, collectionAssertion.AsSpan(5).ToArray()); + } + + [Fact] + public static void InsertRange_NotifyCollectionChanged_Middle_Test() + { + int[] dataToInsert = new int[] { 1, 2, 3, 4, 5 }; + int[] initialData = new int[] { 10, 11, 12, 13 }; + int eventCounter = 0; + ObservableCollection collection = new ObservableCollection(initialData); + collection.CollectionChanged += (o, e) => eventCounter++; + + collection.InsertRange(2, dataToInsert); + + Assert.Equal(dataToInsert.Length + initialData.Length, collection.Count); + Assert.Equal(1, eventCounter); + + int[] collectionAssertion = collection.ToArray(); + Assert.Equal(initialData.AsSpan(0, 2).ToArray(), collectionAssertion.AsSpan(0, 2).ToArray()); + Assert.Equal(dataToInsert, collectionAssertion.AsSpan(2, 5).ToArray()); + Assert.Equal(initialData.AsSpan(2, 2).ToArray(), collectionAssertion.AsSpan(7, 2).ToArray()); + } + + [Fact] + public static void InsertRange_NotifyCollectionChanged_End_Test() + { + int[] dataToInsert = new int[] { 1, 2, 3, 4, 5 }; + int[] initialData = new int[] { 10, 11, 12, 13 }; + int eventCounter = 0; + ObservableCollection collection = new ObservableCollection(initialData); + collection.CollectionChanged += (o, e) => eventCounter++; + + collection.InsertRange(4, dataToInsert); + + Assert.Equal(dataToInsert.Length + initialData.Length, collection.Count); + Assert.Equal(1, eventCounter); + + int[] collectionAssertion = collection.ToArray(); + Assert.Equal(initialData, collectionAssertion.AsSpan(0, 4).ToArray()); + Assert.Equal(dataToInsert, collectionAssertion.AsSpan(4).ToArray()); + } + + [Fact] + public static void AddRange_NotifyCollectionChanged_Test() + { + int[] dataToInsert = new int[] { 1, 2, 3, 4, 5 }; + int[] initialData = new int[] { 10, 11, 12, 13 }; + int eventCounter = 0; + ObservableCollection collection = new ObservableCollection(initialData); + collection.CollectionChanged += (o, e) => eventCounter++; + + collection.AddRange(dataToInsert); + + Assert.Equal(dataToInsert.Length + initialData.Length, collection.Count); + Assert.Equal(1, eventCounter); + + int[] collectionAssertion = collection.ToArray(); + Assert.Equal(initialData, collectionAssertion.AsSpan(0, 4).ToArray()); + Assert.Equal(dataToInsert, collectionAssertion.AsSpan(4).ToArray()); + } + + [Fact] + public static void AddRange_NotifyCollectionChanged_EventArgs_Test() + { + int[] dataToAdd = new int[] { 1, 2, 3, 4, 5, 6, 7, 8 }; + int[] actualDataAdded = new int[0]; + ObservableCollection collection = new ObservableCollection(); + collection.CollectionChanged += (o, e) => actualDataAdded = e.NewItems.Cast().ToArray(); + + collection.AddRange(dataToAdd); + + Assert.Equal(dataToAdd, actualDataAdded); + } + + [Fact] + public static void InsertRange_NotifyCollectionChanged_EventArgs_Test() + { + int[] dataToAdd = new int[] { 1, 2, 3, 4, 5, 6, 7, 8 }; + int[] actualDataAdded = new int[0]; + ObservableCollection collection = new ObservableCollection(); + collection.CollectionChanged += (o, e) => actualDataAdded = e.NewItems.Cast().ToArray(); + + collection.InsertRange(0, dataToAdd); + + Assert.Equal(dataToAdd, actualDataAdded); + } + + [Fact] + public static void InsertRange_NotifyCollectionChanged_EventArgs_Middle_Test() + { + int[] dataToAdd = new int[] { 1, 2, 3, 4, 5, 6, 7, 8 }; + int[] actualDataAdded = new int[0]; + ObservableCollection collection = new ObservableCollection(); + for (int i = 0; i < 4; i++) + { + collection.Add(i); + } + + collection.CollectionChanged += (o, e) => actualDataAdded = e.NewItems.Cast().ToArray(); + collection.InsertRange(2, dataToAdd); + + Assert.Equal(dataToAdd, actualDataAdded); + } + + [Fact] + public static void InsertRange_NotifyCollectionChanged_EventArgs_End_Test() + { + int[] dataToAdd = new int[] { 1, 2, 3, 4, 5, 6, 7, 8 }; + int[] actualDataAdded = new int[0]; + ObservableCollection collection = new ObservableCollection(); + for (int i = 0; i < 4; i++) + { + collection.Add(i); + } + + collection.CollectionChanged += (o, e) => actualDataAdded = e.NewItems.Cast().ToArray(); + collection.InsertRange(4, dataToAdd); + + Assert.Equal(dataToAdd, actualDataAdded); + } + + [Fact] + public static void RemoveRange_NotifyCollectionChanged_FirstTwo_Test() + { + int[] initialData = new int[] { 10, 11, 12, 13 }; + int itemsToRemove = 2; + int eventCounter = 0; + ObservableCollection collection = new ObservableCollection(initialData); + collection.CollectionChanged += (o, e) => eventCounter++; + + collection.RemoveRange(0, itemsToRemove); + + Assert.Equal(initialData.Length - itemsToRemove, collection.Count); + Assert.Equal(1, eventCounter); + Assert.Equal(initialData.AsSpan(2).ToArray(), collection.ToArray()); + } + + [Fact] + public static void RemoveRange_NotifyCollectionChanged_MiddleTwo_Test() + { + int[] initialData = new int[] { 10, 11, 12, 13 }; + int itemsToRemove = 2; + int eventCounter = 0; + ObservableCollection collection = new ObservableCollection(initialData); + collection.CollectionChanged += (o, e) => eventCounter++; + + collection.RemoveRange(1, itemsToRemove); + + Assert.Equal(initialData.Length - itemsToRemove, collection.Count); + Assert.Equal(1, eventCounter); + Assert.Equal(initialData[0], collection[0]); + Assert.Equal(initialData[3], collection[1]); + } + + [Fact] + public static void RemoveRange_NotifyCollectionChanged_LastTwo_Test() + { + int[] initialData = new int[] { 10, 11, 12, 13 }; + int itemsToRemove = 2; + int eventCounter = 0; + ObservableCollection collection = new ObservableCollection(initialData); + collection.CollectionChanged += (o, e) => eventCounter++; + + collection.RemoveRange(2, itemsToRemove); + + Assert.Equal(initialData.Length - itemsToRemove, collection.Count); + Assert.Equal(1, eventCounter); + Assert.Equal(initialData.AsSpan(0, 2).ToArray(), collection.ToArray()); + } + + [Fact] + public static void RemoveRange_NotifyCollectionChanged_IntMaxValueOverflow_Test() + { + int count = 500; + ObservableCollection collection = new ObservableCollection(); + for (int i = 0; i < count; i++) + { + collection.Add(i); + } + + Assert.Throws(() => collection.RemoveRange(collection.Count - 2, int.MaxValue)); + } + + [Fact] + public static void RemoveRange_NotifyCollectionChanged_EventArgs_IndexOfZero_Test() + { + int[] initialData = new int[] { 1, 2, 3, 4, 5, 6, 7, 8 }; + int[] actualDataRemoved = new int[0]; + int numberOfItemsToRemove = 4; + ObservableCollection collection = new ObservableCollection(); + foreach (int item in initialData) + { + collection.Add(item); + } + + collection.CollectionChanged += (o, e) => actualDataRemoved = e.OldItems.Cast().ToArray(); + collection.RemoveRange(0, numberOfItemsToRemove); + + Assert.Equal(initialData.Length - numberOfItemsToRemove, collection.Count); + Assert.Equal(initialData.AsSpan(0, numberOfItemsToRemove).ToArray(), actualDataRemoved); + } + + [Fact] + public static void RemoveRange_NotifyCollectionChanged_EventArgs_IndexMiddle_Test() + { + int[] initialData = new int[] { 1, 2, 3, 4, 5, 6, 7, 8 }; + int[] actualDataRemoved = new int[0]; + int numberOfItemsToRemove = 4; + int startIndex = 3; + ObservableCollection collection = new ObservableCollection(); + foreach (int item in initialData) + { + collection.Add(item); + } + + collection.CollectionChanged += (o, e) => actualDataRemoved = e.OldItems.Cast().ToArray(); + collection.RemoveRange(startIndex, numberOfItemsToRemove); + + Assert.Equal(initialData.Length - numberOfItemsToRemove, collection.Count); + Assert.Equal(initialData.AsSpan(startIndex, numberOfItemsToRemove).ToArray(), actualDataRemoved); + } + + [Fact] + public static void ReplaceRange_NotifyCollectionChanged_Test() + { + int[] initialData = new int[] { 10, 11, 12, 13 }; + int[] dataToReplace = new int[] { 3, 8 }; + int eventCounter = 0; + ObservableCollection collection = new ObservableCollection(initialData); + collection.CollectionChanged += (o, e) => eventCounter++; + + collection.ReplaceRange(0, 2, dataToReplace); + + Assert.Equal(initialData.Length, collection.Count); + Assert.Equal(1, eventCounter); + + int[] collectionAssertion = collection.ToArray(); + Assert.Equal(dataToReplace, collectionAssertion.AsSpan(0, 2).ToArray()); + Assert.Equal(initialData.AsSpan(2, 2).ToArray(), collectionAssertion.AsSpan(2, 2).ToArray()); + } + } +} diff --git a/src/libraries/System.ObjectModel/tests/System.ObjectModel.Tests.csproj b/src/libraries/System.ObjectModel/tests/System.ObjectModel.Tests.csproj index 0adc822c3d1e8c..2a32c33d1d9c9c 100644 --- a/src/libraries/System.ObjectModel/tests/System.ObjectModel.Tests.csproj +++ b/src/libraries/System.ObjectModel/tests/System.ObjectModel.Tests.csproj @@ -1,10 +1,10 @@ - + $(NetCoreAppCurrent) + Link="Common\System\CollectionsIEnumerableTest.cs" /> + diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/Collection.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/Collection.cs index fcafafed633f6d..76243c18c37433 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/Collection.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/Collection.cs @@ -62,6 +62,8 @@ public void Add(T item) InsertItem(index, item); } + public void AddRange(IEnumerable collection) => InsertItemsRange(items.Count, collection); + public void Clear() { if (items.IsReadOnly) @@ -107,6 +109,8 @@ public void Insert(int index, T item) InsertItem(index, item); } + public void InsertRange(int index, IEnumerable collection) => InsertItemsRange(index, collection); + public bool Remove(T item) { if (items.IsReadOnly) @@ -120,6 +124,10 @@ public bool Remove(T item) return true; } + public void RemoveRange(int index, int count) => RemoveItemsRange(index, count); + + public void ReplaceRange(int index, int count, IEnumerable collection) => ReplaceItemsRange(index, count, collection); + public void RemoveAt(int index) { if (items.IsReadOnly) @@ -135,6 +143,77 @@ public void RemoveAt(int index) RemoveItem(index); } + protected virtual void InsertItemsRange(int index, IEnumerable collection) + { + if (items.IsReadOnly) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + } + + if (collection == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.list); + } + + if ((uint)index > (uint)items.Count) + { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_ListInsert); + } + + if (GetType() == typeof(Collection) && items is List list) + { + list.InsertRange(index, collection); + } + else + { + foreach (T item in collection) + { + InsertItem(index++, item); + } + } + } + + protected virtual void RemoveItemsRange(int index, int count) + { + if (items.IsReadOnly) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + } + + if ((uint)index > (uint)items.Count) + { + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); + } + + if (count < 0) + { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); + } + + if (index > items.Count - count) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen); + } + + if (GetType() == typeof(Collection) && items is List list) + { + list.RemoveRange(index, count); + } + else + { + for (int i = 0; i < count; i++) + { + RemoveItem(index); + } + } + } + + protected virtual void ReplaceItemsRange(int index, int count, IEnumerable collection) + { + RemoveItemsRange(index, count); + InsertItemsRange(index, collection); + } + protected virtual void ClearItems() { items.Clear(); diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 4bd98a48d68db6..9f8d3924e62060 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -8610,6 +8610,7 @@ public Collection(System.Collections.Generic.IList list) { } bool System.Collections.IList.IsReadOnly { get { throw null; } } object? System.Collections.IList.this[int index] { get { throw null; } set { } } public void Add(T item) { } + public void AddRange(System.Collections.Generic.IEnumerable collection) { } public void Clear() { } protected virtual void ClearItems() { } public bool Contains(T item) { throw null; } @@ -8618,9 +8619,15 @@ public void CopyTo(T[] array, int index) { } public int IndexOf(T item) { throw null; } public void Insert(int index, T item) { } protected virtual void InsertItem(int index, T item) { } + protected virtual void InsertItemsRange(int index, System.Collections.Generic.IEnumerable collection) { } + public void InsertRange(int index, System.Collections.Generic.IEnumerable collection) { } public bool Remove(T item) { throw null; } public void RemoveAt(int index) { } protected virtual void RemoveItem(int index) { } + protected virtual void RemoveItemsRange(int index, int count) { } + public void RemoveRange(int index, int count) { } + protected virtual void ReplaceItemsRange(int index, int count, System.Collections.Generic.IEnumerable collection) { } + public void ReplaceRange(int index, int count, System.Collections.Generic.IEnumerable collection) { } protected virtual void SetItem(int index, T item) { } void System.Collections.ICollection.CopyTo(System.Array array, int index) { } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj index 051fe609d634b0..86e44b964ca52b 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj @@ -56,6 +56,7 @@ + diff --git a/src/libraries/System.Runtime/tests/System/Collections/ObjectModel/RangeCollectionTests.cs b/src/libraries/System.Runtime/tests/System/Collections/ObjectModel/RangeCollectionTests.cs new file mode 100644 index 00000000000000..e35d8917cd1fc2 --- /dev/null +++ b/src/libraries/System.Runtime/tests/System/Collections/ObjectModel/RangeCollectionTests.cs @@ -0,0 +1,422 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace System.Collections.ObjectModel.Tests +{ + /// + /// Since is just a wrapper base class around an , + /// we just verify that the underlying list is what we expect, validate that the calls which + /// we expect are forwarded to the underlying list, and verify that the exceptions we expect + /// are thrown. + /// + public class RangeCollectionTests : CollectionTestBase + { + [Fact] + public void Collection_AddRange_ToEmpty_Test() + { + int[] expected = new[] { 1, 2, 3, 4 }; + Collection collection = new Collection(); + + collection.AddRange(expected); + + Assert.NotNull(collection); + Assert.Equal(expected.Length, collection.Count); + Assert.Equal(expected, collection.ToArray()); + } + + [Fact] + public void Collection_AddRange_ToExisting_Test() + { + int[] initial = new int[] { 1, 2, 3, 4 }; + int[] dataToInsert = new int[] { 5, 6, 7, 8 }; + Collection collection = new Collection(); + for (int i = 0; i < initial.Length; i++) + collection.Add(initial[i]); + + collection.AddRange(dataToInsert); + + Assert.NotNull(collection); + Assert.Equal(initial.Length + dataToInsert.Length, collection.Count); + + int[] collectionAssertion = collection.ToArray(); + Assert.Equal(initial, collectionAssertion.AsSpan(0, 4).ToArray()); + Assert.Equal(dataToInsert, collectionAssertion.AsSpan(4, 4).ToArray()); + } + + [Fact] + public void Collection_AddRange_Empty_Test() + { + int[] expected = new int[0]; + Collection collection = new Collection(); + + collection.AddRange(expected); + + Assert.NotNull(collection); + Assert.Equal(expected.Length, collection.Count); + } + + [Fact] + public void Collection_AddRange_Null_Test() + { + Collection collection = new Collection(); + Exception ex = Assert.Throws(() => collection.AddRange(null)); + } + + [Fact] + public void Collection_AddRange_ReadOnly_Test() + { + int[] expected = new int[] { 1, 2, 3, 4 }; + int[] baseCollection = new int[] { 5, 6, 7, 8 }; + Collection collection = new Collection(baseCollection); + + Exception ex = Assert.Throws(() => collection.AddRange(expected)); + } + + [Fact] + public void Collection_InsertRange_Beginning_Test() + { + int[] expected = new int[] { 1, 2, 3, 4, 5 }; + int[] originalCollection = new int[] { 10, 11, 12, 13 }; + List baseCollection = new List(originalCollection); + + int expectedLength = expected.Length + originalCollection.Length; + Collection collection = new Collection(baseCollection); + + collection.InsertRange(0, expected); + + Assert.NotNull(collection); + Assert.Equal(expectedLength, collection.Count); + + int[] collectionAssertion = collection.ToArray(); + Assert.Equal(expected, collectionAssertion.AsSpan(0, 5).ToArray()); + Assert.Equal(originalCollection, collectionAssertion.AsSpan(5, 4).ToArray()); + } + + [Fact] + public void Collection_InsertRange_End_Test() + { + int[] expected = new int[] { 1, 2, 3, 4, 5 }; + int[] originalCollection = new int[] { 10, 11, 12, 13 }; + List baseCollection = new List(originalCollection); + + int expectedLength = expected.Length + originalCollection.Length; + Collection collection = new Collection(baseCollection); + + collection.InsertRange(expected.Length - 1, expected); + + Assert.NotNull(collection); + Assert.Equal(expectedLength, collection.Count); + + int[] collectionAssertion = collection.ToArray(); + Assert.Equal(originalCollection, collectionAssertion.AsSpan(0, 4).ToArray()); + Assert.Equal(expected, collectionAssertion.AsSpan(4, 5).ToArray()); + } + + [Fact] + public void Collection_InsertRange_Middle_Test() + { + int[] expected = new int[] { 1, 2, 3, 4, 5 }; + int[] originalCollection = new int[] { 10, 11, 12, 13 }; + List baseCollection = new List(originalCollection); + + int expectedLength = expected.Length + originalCollection.Length; + Collection collection = new Collection(baseCollection); + + collection.InsertRange(2, expected); + + Assert.NotNull(collection); + Assert.Equal(expectedLength, collection.Count); + + int[] collectionAssertion = collection.ToArray(); + Assert.Equal(originalCollection.AsSpan(0, 2).ToArray(), collectionAssertion.AsSpan(0, 2).ToArray()); + Assert.Equal(expected, collectionAssertion.AsSpan(2, 5).ToArray()); + Assert.Equal(originalCollection.AsSpan(2, 2).ToArray(), collectionAssertion.AsSpan(7, 2).ToArray()); + } + + [Fact] + public void Collection_InsertRange_Empty_Test() + { + List baseCollection = new List(new[] { 10, 11, 12, 13 }); + Collection collection = new Collection(baseCollection); + + collection.InsertRange(0, new int[0]); + + Assert.NotNull(collection); + Assert.Equal(baseCollection.Count, collection.Count); + Assert.Equal(baseCollection, collection.ToArray()); + } + + [Fact] + public void Collection_InsertRange_Null_Test() + { + Collection collection = new Collection(); + Exception ex = Assert.Throws(() => collection.InsertRange(0, null)); + } + + [Fact] + public void Collection_InsertRange_ReadOnly_Test() + { + int[] expected = new int[] { 1, 2, 3, 4 }; + int[] baseCollection = new int[] { 5, 6, 7, 8 }; + Collection collection = new Collection(baseCollection); + + Exception ex = Assert.Throws(() => collection.InsertRange(0, expected)); + } + + [Fact] + public void Collection_InsertRange_IndexLessThan0_Test() + { + int[] expected = new int[] { 1, 2, 3, 4 }; + Collection collection = new Collection(); + Exception ex = Assert.Throws(() => collection.InsertRange(-1, expected)); + } + + [Fact] + public void Collection_InsertRange_IndexGreaterThanCount_Test() + { + int[] expected = new int[] { 1, 2, 3, 4 }; + Collection collection = new Collection(); + Exception ex = Assert.Throws(() => collection.InsertRange(10, expected)); + } + + [Fact] + public void Collection_RemoveRange_Overflow_Test() + { + Collection collection = new Collection(); + collection.Add(1); + collection.Add(2); + collection.Add(3); + + Assert.Throws(() => collection.RemoveRange(0, 4)); + } + + [Fact] + public void Collection_RemoveRange_IntMaxValueOverflow_Test() + { + var count = 500; + Collection collection = new Collection(); + for (int i = 0; i < count; i++) + { + collection.Add(i); + } + + Assert.Throws(() => collection.RemoveRange(collection.Count - 2, int.MaxValue)); + } + + [Fact] + public void Collection_RemoveRange_CountIsZero_Test() + { + int[] expected = new int[] { 1, 2, 3, 4, 5 }; + Collection collection = new Collection(); + for (int i = 0; i < expected.Length; i++) + { + collection.Add(expected[i]); + } + + collection.RemoveRange(0, 0); + + Assert.NotNull(collection); + Assert.Equal(expected.Length, collection.Count); + Assert.Equal(expected, collection.ToArray()); + } + + [Fact] + public void Collection_RemoveRange_CountIsLessThanZero_Test() + { + Collection collection = new Collection(); + collection.Add(1); + collection.Add(2); + + Assert.Throws(() => collection.RemoveRange(0, -1)); + } + + [Fact] + public void Collection_RemoveRange_All_Test() + { + Collection collection = new Collection(); + collection.Add(1); + collection.Add(2); + collection.Add(3); + + collection.RemoveRange(0, 3); + + Assert.NotNull(collection); + Assert.Equal(0, collection.Count); + } + + [Fact] + public void Collection_RemoveRange_FirstTwoItems_Test() + { + Collection collection = new Collection(); + collection.Add(1); + collection.Add(2); + collection.Add(3); + + collection.RemoveRange(0, 2); + + Assert.NotNull(collection); + Assert.Equal(1, collection.Count); + Assert.Equal(3, collection[0]); + } + + [Fact] + public void Collection_RemoveRange_LastTwoItems_Test() + { + Collection collection = new Collection(); + collection.Add(1); + collection.Add(2); + collection.Add(3); + + collection.RemoveRange(1, 2); + + Assert.NotNull(collection); + Assert.Equal(1, collection.Count); + Assert.Equal(1, collection[0]); + } + + [Fact] + public void Collection_RemoveRange_ZeroItems_Test() + { + Collection collection = new Collection(); + collection.Add(1); + collection.Add(2); + collection.Add(3); + + collection.RemoveRange(0, 0); + + Assert.NotNull(collection); + Assert.Equal(3, collection.Count); + Assert.Equal(1, collection[0]); + Assert.Equal(2, collection[1]); + Assert.Equal(3, collection[2]); + } + + [Fact] + public void Collection_RemoveRange_IndexLessThanZero_Test() + { + Collection collection = new Collection(); + collection.Add(1); + collection.Add(2); + collection.Add(3); + + AssertExtensions.Throws("index", () => collection.RemoveRange(-1, 3)); + } + + [Fact] + public void Collection_RemoveRange_IndexGreaterThanCollection_Test() + { + Collection collection = new Collection(); + collection.Add(1); + collection.Add(2); + collection.Add(3); + + AssertExtensions.Throws("index", () => collection.RemoveRange(4, 3)); + } + + [Fact] + public void Collection_RemoveRange_ReadOnly_Test() + { + Collection collection = new Collection(new int[] { 1, 2, 3 }); + + Assert.Throws(() => collection.RemoveRange(0, 2)); + } + + [Fact] + public void Collection_ReplaceRange_FirstTwo_Test() + { + int[] initial = new int[] { 1, 2, 3, 4 }; + int[] replace = new int[] { 5, 6, 7, 8 }; + Collection collection = new Collection(); + foreach (var item in initial) + collection.Add(item); + + collection.ReplaceRange(0, 2, replace); + + Assert.NotNull(collection); + Assert.Equal(initial.Length + 2, collection.Count); + + int[] collectionAssertion = collection.ToArray(); + Assert.Equal(replace, collectionAssertion.AsSpan(0, 4).ToArray()); + Assert.Equal(initial.AsSpan(2, 2).ToArray(), collectionAssertion.AsSpan(4, 2).ToArray()); + } + + [Fact] + public void Collection_ReplaceRange_LastTwo_Test() + { + int[] initial = new int[] { 1, 2, 3, 4 }; + int[] replace = new int[] { 5, 6, 7, 8 }; + Collection collection = new Collection(); + foreach (var item in initial) + collection.Add(item); + + collection.ReplaceRange(2, 2, replace); + + Assert.NotNull(collection); + Assert.Equal(initial.Length + 2, collection.Count); + + int[] collectionAssertion = collection.ToArray(); + Assert.Equal(initial.AsSpan(0, 2).ToArray(), collectionAssertion.AsSpan(0, 2).ToArray()); + Assert.Equal(replace.AsSpan(0, 4).ToArray(), collectionAssertion.AsSpan(2, 4).ToArray()); + } + + [Fact] + public void Collection_ReplaceRange_MiddleTwo_Test() + { + int[] initial = new int[] { 1, 2, 3, 4 }; + int[] replace = new int[] { 5, 6, 7, 8 }; + Collection collection = new Collection(); + foreach (var item in initial) + collection.Add(item); + + collection.ReplaceRange(1, 2, replace); + + Assert.NotNull(collection); + Assert.Equal(initial.Length + 2, collection.Count); + + Assert.Equal(initial[0], collection[0]); + Assert.Equal(replace, collection.ToArray().AsSpan(1, 4).ToArray()); + Assert.Equal(initial[3], collection[5]); + } + + [Fact] + public void Collection_ReplaceRange_NullCollection_Test() + { + Collection collection = new Collection(); + collection.Add(1); + collection.Add(2); + + Assert.Throws(() => collection.ReplaceRange(0, 2, null)); + } + + [Fact] + public void Collection_ReplaceRange_ReadOnly_Test() + { + Collection collection = new Collection(new int[] { 1, 2, 3 }); + Assert.Throws(() => collection.ReplaceRange(0, 2, new int[] { 4, 5 })); + } + + [Fact] + public void Collection_ReplaceRange_IndexLessThanZero_Test() + { + Collection collection = new Collection(); + collection.Add(1); + collection.Add(2); + + AssertExtensions.Throws("index", () => collection.ReplaceRange(-2, 2, new int[] { 1, 2 })); + } + + [Fact] + public void Collection_ReplaceRange_IndexGreaterThanCount_Test() + { + Collection collection = new Collection(); + collection.Add(1); + collection.Add(2); + + AssertExtensions.Throws("index", () => collection.ReplaceRange(4, 2, new int[] { 1, 2 })); + } + } +}