Moved all unit test files into a separate directory in preparation for splitting the project

This commit is contained in:
Markus Ewald 2024-07-24 13:27:29 +02:00 committed by cygon
parent 28b96fd557
commit ba5234f701
58 changed files with 0 additions and 853 deletions

View file

@ -0,0 +1,52 @@
#region Apache License 2.0
/*
Nuclex .NET Framework
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#endregion // Apache License 2.0
#if UNITTEST
using System;
using System.Collections.Specialized;
using NUnit.Framework;
namespace Nuclex.Support.Collections {
/// <summary>Unit Test for the collection constants</summary>
[TestFixture]
internal class ConstantsTest {
#if !NO_SPECIALIZED_COLLECTIONS
/// <summary>
/// Verifies that the collection reset event arguments have 'reset' specified as
/// their action
/// </summary>
[Test]
public void CollectionResetEventArgsHaveResetActionSet() {
Assert.AreEqual(
NotifyCollectionChangedAction.Reset, Constants.NotifyCollectionResetEventArgs.Action
);
}
#endif // !NO_SPECIALIZED_COLLECTIONS
}
} // namespace Nuclex.Support.Collections
#endif // UNITTEST

View file

@ -0,0 +1,710 @@
#region Apache License 2.0
/*
Nuclex .NET Framework
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#endregion // Apache License 2.0
#if UNITTEST
using System;
using System.Collections;
using System.Collections.Generic;
using NUnit.Framework;
namespace Nuclex.Support.Collections {
/// <summary>Unit Test for the double ended queue</summary>
[TestFixture]
internal class DequeTest {
/// <summary>Verifies that the AddLast() method of the deque is working</summary>
[Test]
public void TestAddLast() {
Deque<int> intDeque = new Deque<int>(16);
for(int item = 0; item < 48; ++item) {
intDeque.AddLast(item);
}
for(int item = 0; item < 48; ++item) {
Assert.AreEqual(item, intDeque[item]);
}
}
/// <summary>Verifies that the AddFirst() method of the deque is working</summary>
[Test]
public void TestAddFirst() {
Deque<int> intDeque = new Deque<int>(16);
for(int item = 0; item < 48; ++item) {
intDeque.AddFirst(item);
}
for(int item = 0; item < 48; ++item) {
Assert.AreEqual(47 - item, intDeque[item]);
}
}
/// <summary>
/// Verifies that the RemoveFirst() method of the deque is working
/// </summary>
[Test]
public void TestRemoveFirst() {
Deque<int> intDeque = new Deque<int>(16);
for(int item = 0; item < 48; ++item) {
intDeque.AddLast(item);
}
for(int item = 0; item < 48; ++item) {
Assert.AreEqual(item, intDeque.First);
Assert.AreEqual(48 - item, intDeque.Count);
intDeque.RemoveFirst();
}
}
/// <summary>
/// Verifies that the RemoveLast() method of the deque is working
/// </summary>
[Test]
public void TestRemoveLast() {
Deque<int> intDeque = new Deque<int>(16);
for(int item = 0; item < 48; ++item) {
intDeque.AddLast(item);
}
for(int item = 0; item < 48; ++item) {
Assert.AreEqual(47 - item, intDeque.Last);
Assert.AreEqual(48 - item, intDeque.Count);
intDeque.RemoveLast();
}
}
/// <summary>Verifies that the Insert() method works in all cases</summary>
/// <remarks>
/// We have several different cases here that will be tested. The deque can
/// shift items to the left or right (depending on which end is closer to
/// the insertion point) and the insertion point may fall in an only partially
/// occupied block, requiring elaborate index calculations
/// </remarks>
[Test]
public void TestInsert() {
for(int testedIndex = 0; testedIndex <= 96; ++testedIndex) {
Deque<int> intDeque = createDeque(96);
intDeque.Insert(testedIndex, 12345);
Assert.AreEqual(97, intDeque.Count);
for(int index = 0; index < testedIndex; ++index) {
Assert.AreEqual(index, intDeque[index]);
}
Assert.AreEqual(12345, intDeque[testedIndex]);
for(int index = testedIndex + 1; index < 97; ++index) {
Assert.AreEqual(index - 1, intDeque[index]);
}
}
}
/// <summary>
/// Verifies that the Insert() method works in all cases when the deque doesn't
/// start at a block boundary
/// </summary>
[Test]
public void TestInsertNonNormalized() {
for(int testedIndex = 0; testedIndex <= 96; ++testedIndex) {
Deque<int> intDeque = createNonNormalizedDeque(96);
intDeque.Insert(testedIndex, 12345);
Assert.AreEqual(97, intDeque.Count);
for(int index = 0; index < testedIndex; ++index) {
Assert.AreEqual(index, intDeque[index]);
}
Assert.AreEqual(12345, intDeque[testedIndex]);
for(int index = testedIndex + 1; index < 97; ++index) {
Assert.AreEqual(index - 1, intDeque[index]);
}
}
}
/// <summary>Verifies the the RemoveAt() method works in all cases</summary>
[Test]
public void TestRemoveAt() {
for(int testedIndex = 0; testedIndex < 96; ++testedIndex) {
Deque<int> intDeque = new Deque<int>(16);
for(int item = 0; item < 96; ++item) {
intDeque.AddLast(item);
}
intDeque.RemoveAt(testedIndex);
Assert.AreEqual(95, intDeque.Count);
for(int index = 0; index < testedIndex; ++index) {
Assert.AreEqual(index, intDeque[index]);
}
for(int index = testedIndex; index < 95; ++index) {
Assert.AreEqual(index + 1, intDeque[index]);
}
}
}
/// <summary>
/// Verifies the the RemoveAt() method works in all cases when the deque doesn't
/// start at a block boundary
/// </summary>
[Test]
public void TestRemoveAtNonNormalized() {
for(int testedIndex = 0; testedIndex < 96; ++testedIndex) {
Deque<int> intDeque = new Deque<int>(16);
for(int item = 4; item < 96; ++item) {
intDeque.AddLast(item);
}
intDeque.AddFirst(3);
intDeque.AddFirst(2);
intDeque.AddFirst(1);
intDeque.AddFirst(0);
intDeque.RemoveAt(testedIndex);
Assert.AreEqual(95, intDeque.Count);
for(int index = 0; index < testedIndex; ++index) {
Assert.AreEqual(index, intDeque[index]);
}
for(int index = testedIndex; index < 95; ++index) {
Assert.AreEqual(index + 1, intDeque[index]);
}
}
}
/// <summary>
/// Tests whether the RemoveAt() method keeps the state of the deque intact when
/// it has to remove a block from the left end of the deque
/// </summary>
[Test]
public void TestRemoveAtEmptiesLeftBlock() {
Deque<int> intDeque = new Deque<int>(16);
for(int item = 1; item <= 16; ++item) {
intDeque.AddLast(item);
}
intDeque.AddFirst(0);
intDeque.RemoveAt(3);
Assert.AreEqual(16, intDeque.Count);
for(int index = 0; index < 3; ++index) {
Assert.AreEqual(index, intDeque[index]);
}
for(int index = 3; index < 16; ++index) {
Assert.AreEqual(index + 1, intDeque[index]);
}
}
/// <summary>
/// Tests whether the RemoveAt() method keeps the state of the deque intact when
/// it has to remove a block from the right end of the deque
/// </summary>
[Test]
public void TestRemoveAtEmptiesRightBlock() {
Deque<int> intDeque = new Deque<int>(16);
for(int item = 0; item <= 16; ++item) {
intDeque.AddLast(item);
}
intDeque.RemoveAt(13);
Assert.AreEqual(16, intDeque.Count);
for(int index = 0; index < 13; ++index) {
Assert.AreEqual(index, intDeque[index]);
}
for(int index = 13; index < 16; ++index) {
Assert.AreEqual(index + 1, intDeque[index]);
}
}
/// <summary>
/// Validates that an exception is thrown if the 'First' property is accessed
/// in an empty deque
/// </summary>
[Test]
public void TestThrowOnAccessFirstInEmptyDeque() {
Deque<int> intDeque = new Deque<int>();
Assert.Throws<InvalidOperationException>(
delegate() { Console.WriteLine(intDeque.First); }
);
}
/// <summary>
/// Validates that an exception is thrown if the 'Last' property is accessed
/// in an empty deque
/// </summary>
[Test]
public void TestThrowOnAccessLastInEmptyDeque() {
Deque<int> intDeque = new Deque<int>();
Assert.Throws<InvalidOperationException>(
delegate() { Console.WriteLine(intDeque.Last); }
);
}
/// <summary>
/// Validates that an exception is thrown if the first item is attempted to be
/// removed from an empty deque
/// </summary>
[Test]
public void TestThrowOnRemoveFirstFromEmptyDeque() {
Deque<int> intDeque = new Deque<int>();
Assert.Throws<InvalidOperationException>(
delegate() { intDeque.RemoveFirst(); }
);
}
/// <summary>
/// Validates that an exception is thrown if the last item is attempted to be
/// removed from an empty deque
/// </summary>
[Test]
public void TestThrowOnRemoveLastFromEmptyDeque() {
Deque<int> intDeque = new Deque<int>();
Assert.Throws<InvalidOperationException>(
delegate() { intDeque.RemoveLast(); }
);
}
/// <summary>
/// Verifies that items can be assigned by their index
/// </summary>
[Test]
public void TestIndexAssignment() {
Deque<int> intDeque = createDeque(32);
intDeque[16] = 12345;
intDeque[17] = 54321;
for(int index = 0; index < 16; ++index) {
intDeque.RemoveFirst();
}
Assert.AreEqual(12345, intDeque.First);
intDeque.RemoveFirst();
Assert.AreEqual(54321, intDeque.First);
}
/// <summary>
/// Verifies that an exception is thrown if an invalid index is accessed
/// </summary>
[Test]
public void TestThrowOnInvalidIndex() {
Deque<int> intDeque = new Deque<int>(16);
for(int item = 0; item < 32; ++item) {
intDeque.AddLast(item);
}
Assert.Throws<ArgumentOutOfRangeException>(
delegate() { Console.WriteLine(intDeque[32]); }
);
}
/// <summary>Tests the IndexOf() method</summary>
[Test, TestCase(0), TestCase(16), TestCase(32), TestCase(48)]
public void TestIndexOf(int count) {
Deque<int> intDeque = new Deque<int>(16);
for(int item = 0; item < count; ++item) {
intDeque.AddLast(item);
}
for(int item = 0; item < count; ++item) {
Assert.AreEqual(item, intDeque.IndexOf(item));
}
Assert.AreEqual(-1, intDeque.IndexOf(count));
}
/// <summary>
/// Tests the IndexOf() method with the deque not starting at a block boundary
/// </summary>
[Test, TestCase(0), TestCase(16), TestCase(32), TestCase(48)]
public void TestIndexOfNonNormalized(int count) {
Deque<int> intDeque = createNonNormalizedDeque(count);
for(int item = 0; item < count; ++item) {
Assert.AreEqual(item, intDeque.IndexOf(item));
}
Assert.AreEqual(-1, intDeque.IndexOf(count));
}
/// <summary>Verifies that the deque's enumerator works</summary>
[Test]
public void TestEnumerator() {
Deque<int> intDeque = createNonNormalizedDeque(40);
for(int testRun = 0; testRun < 2; ++testRun) {
using(IEnumerator<int> enumerator = intDeque.GetEnumerator()) {
for(int index = 0; index < intDeque.Count; ++index) {
Assert.IsTrue(enumerator.MoveNext());
Assert.AreEqual(index, enumerator.Current);
}
Assert.IsFalse(enumerator.MoveNext());
enumerator.Reset();
}
}
}
/// <summary>Verifies that the deque's enumerator works</summary>
[Test]
public void TestObjectEnumerator() {
Deque<int> intDeque = createNonNormalizedDeque(40);
for(int testRun = 0; testRun < 2; ++testRun) {
IEnumerator enumerator = ((IEnumerable)intDeque).GetEnumerator();
for(int index = 0; index < intDeque.Count; ++index) {
Assert.IsTrue(enumerator.MoveNext());
Assert.AreEqual(index, enumerator.Current);
}
Assert.IsFalse(enumerator.MoveNext());
enumerator.Reset();
}
}
/// <summary>
/// Verifies that an exception is thrown if the enumerator is accessed in
/// an invalid position
/// </summary>
[Test]
public void TestThrowOnInvalidEnumeratorPosition() {
Deque<int> intDeque = createNonNormalizedDeque(40);
using(IEnumerator<int> enumerator = intDeque.GetEnumerator()) {
Assert.Throws<InvalidOperationException>(
delegate() { Console.WriteLine(enumerator.Current); }
);
while(enumerator.MoveNext()) { }
Assert.Throws<InvalidOperationException>(
delegate() { Console.WriteLine(enumerator.Current); }
);
}
}
/// <summary>Tests whether a small deque can be cleared</summary>
[Test]
public void TestClearSmallDeque() {
Deque<int> intDeque = createDeque(12);
intDeque.Clear();
Assert.AreEqual(0, intDeque.Count);
}
/// <summary>Tests whether a large deque can be cleared</summary>
[Test]
public void TestClearLargeDeque() {
Deque<int> intDeque = createDeque(40);
intDeque.Clear();
Assert.AreEqual(0, intDeque.Count);
}
/// <summary>Verifies that the non-typesafe Add() method is working</summary>
[Test]
public void TestAddObject() {
Deque<int> intDeque = new Deque<int>();
Assert.AreEqual(0, ((IList)intDeque).Add(123));
Assert.AreEqual(1, intDeque.Count);
}
/// <summary>
/// Tests whether an exception is thrown if the non-typesafe Add() method is
/// used to add an incompatible object into the deque
/// </summary>
[Test]
public void TestThrowOnAddIncompatibleObject() {
Deque<int> intDeque = new Deque<int>();
Assert.Throws<ArgumentException>(
delegate() { ((IList)intDeque).Add("Hello World"); }
);
}
/// <summary>Verifies that the Add() method is working</summary>
[Test]
public void TestAdd() {
Deque<int> intDeque = new Deque<int>();
((IList<int>)intDeque).Add(123);
Assert.AreEqual(1, intDeque.Count);
}
/// <summary>Tests whether the Contains() method is working</summary>
[Test]
public void TestContains() {
Deque<int> intDeque = createDeque(16);
Assert.IsTrue(intDeque.Contains(14));
Assert.IsFalse(intDeque.Contains(16));
}
/// <summary>Tests the non-typesafe Contains() method</summary>
[Test]
public void TestContainsObject() {
Deque<int> intDeque = createDeque(16);
Assert.IsTrue(((IList)intDeque).Contains(14));
Assert.IsFalse(((IList)intDeque).Contains(16));
Assert.IsFalse(((IList)intDeque).Contains("Hello World"));
}
/// <summary>Tests the non-typesafe Contains() method</summary>
[Test]
public void TestIndexOfObject() {
Deque<int> intDeque = createDeque(16);
Assert.AreEqual(14, ((IList)intDeque).IndexOf(14));
Assert.AreEqual(-1, ((IList)intDeque).IndexOf(16));
Assert.AreEqual(-1, ((IList)intDeque).IndexOf("Hello World"));
}
/// <summary>Tests wether the non-typesafe Insert() method is working</summary>
[Test]
public void TestInsertObject() {
for(int testedIndex = 0; testedIndex <= 96; ++testedIndex) {
Deque<int> intDeque = createDeque(96);
((IList)intDeque).Insert(testedIndex, 12345);
Assert.AreEqual(97, intDeque.Count);
for(int index = 0; index < testedIndex; ++index) {
Assert.AreEqual(index, intDeque[index]);
}
Assert.AreEqual(12345, intDeque[testedIndex]);
for(int index = testedIndex + 1; index < 97; ++index) {
Assert.AreEqual(index - 1, intDeque[index]);
}
}
}
/// <summary>
/// Verifies that an exception is thrown if an incompatible object is inserted
/// into the deque
/// </summary>
[Test]
public void TestThrowOnInsertIncompatibleObject() {
Deque<int> intDeque = createDeque(12);
Assert.Throws<ArgumentException>(
delegate() { ((IList)intDeque).Insert(8, "Hello World"); }
);
}
/// <summary>Validates that the IsFixedObject property is set to false</summary>
[Test]
public void TestIsFixedObject() {
Deque<int> intDeque = new Deque<int>();
Assert.IsFalse(((IList)intDeque).IsFixedSize);
}
/// <summary>Validates that the IsSynchronized property is set to false</summary>
[Test]
public void TestIsSynchronized() {
Deque<int> intDeque = new Deque<int>();
Assert.IsFalse(((IList)intDeque).IsSynchronized);
}
/// <summary>
/// Verifies that items can be assigned by their index using the non-typesafe
/// IList interface
/// </summary>
[Test]
public void TestObjectIndexAssignment() {
Deque<int> intDeque = createDeque(32);
((IList)intDeque)[16] = 12345;
((IList)intDeque)[17] = 54321;
Assert.AreEqual(12345, ((IList)intDeque)[16]);
Assert.AreEqual(54321, ((IList)intDeque)[17]);
}
/// <summary>
/// Tests whether an exception is thrown if an incompatible object is assigned
/// to the deque
/// </summary>
[Test]
public void TestIncompatibleObjectIndexAssignment() {
Deque<int> intDeque = createDeque(2);
Assert.Throws<ArgumentException>(
delegate() { ((IList)intDeque)[0] = "Hello World"; }
);
}
/// <summary>Verifies that the Remove() method is working correctly</summary>
[Test]
public void TestRemove() {
Deque<int> intDeque = createDeque(16);
Assert.AreEqual(16, intDeque.Count);
Assert.IsTrue(intDeque.Remove(13));
Assert.IsFalse(intDeque.Remove(13));
Assert.AreEqual(15, intDeque.Count);
}
/// <summary>Tests the non-typesafe remove method</summary>
[Test]
public void TestRemoveObject() {
Deque<int> intDeque = createDeque(10);
Assert.IsTrue(intDeque.Contains(8));
Assert.AreEqual(10, intDeque.Count);
((IList)intDeque).Remove(8);
Assert.IsFalse(intDeque.Contains(8));
Assert.AreEqual(9, intDeque.Count);
}
/// <summary>
/// Tests the non-typesafe remove method used to remove an incompatible object
/// </summary>
[Test]
public void TestRemoveIncompatibleObject() {
Deque<int> intDeque = createDeque(10);
((IList)intDeque).Remove("Hello World"); // should simply do nothing
Assert.AreEqual(10, intDeque.Count);
}
/// <summary>
/// Verifies that the IsSynchronized property and the SyncRoot property are working
/// </summary>
[Test]
public void TestSynchronization() {
Deque<int> intDeque = new Deque<int>();
if(!(intDeque as ICollection).IsSynchronized) {
lock((intDeque as ICollection).SyncRoot) {
Assert.AreEqual(0, intDeque.Count);
}
}
}
/// <summary>
/// Validates that the IsReadOnly property of the deque returns false
/// </summary>
[Test]
public void TestIsReadOnly() {
Deque<int> intDeque = new Deque<int>();
Assert.IsFalse(((IList)intDeque).IsReadOnly);
Assert.IsFalse(((ICollection<int>)intDeque).IsReadOnly);
}
/// <summary>Tests the non-typesafe CopyTo() method</summary>
[Test]
public void TestCopyToObjectArray() {
Deque<int> intDeque = createNonNormalizedDeque(40);
int[] array = new int[40];
((ICollection)intDeque).CopyTo(array, 0);
Assert.AreEqual(intDeque, array);
}
/// <summary>Tests the CopyTo() method</summary>
[Test]
public void TestCopyToArray() {
Deque<int> intDeque = createDeque(12);
intDeque.RemoveFirst();
intDeque.RemoveFirst();
int[] array = new int[14];
intDeque.CopyTo(array, 4);
Assert.AreEqual(
new int[] { 0, 0, 0, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 },
array
);
}
/// <summary>
/// Verifies that the non-typesafe CopyTo() method throws an exception if
/// the array is of an incompatible type
/// </summary>
[Test]
public void TestThrowOnCopyToIncompatibleObjectArray() {
Deque<int> intDeque = createDeque(4);
short[] array = new short[4];
Assert.Throws<ArgumentException>(
delegate() { ((ICollection)intDeque).CopyTo(array, 4); }
);
}
/// <summary>
/// Verifies that the CopyTo() method throws an exception if the target array
/// is too small
/// </summary>
[Test]
public void TestThrowOnCopyToTooSmallArray() {
Deque<int> intDeque = createDeque(8);
Assert.Throws<ArgumentException>(
delegate() { intDeque.CopyTo(new int[7], 0); }
);
}
#if DEBUG
/// <summary>
/// Tests whether the deque enumerator detects when it runs out of sync
/// </summary>
[Test]
public void TestInvalidatedEnumeratorDetection() {
Deque<int> intDeque = createDeque(8);
using(IEnumerator<int> enumerator = intDeque.GetEnumerator()) {
Assert.IsTrue(enumerator.MoveNext());
intDeque.AddFirst(12345);
Assert.Throws<InvalidOperationException>(
delegate() { enumerator.MoveNext(); }
);
}
}
#endif
/// <summary>
/// Creates a deque whose first element does not coincide with a block boundary
/// </summary>
/// <param name="count">Number of items the deque will be filled with</param>
/// <returns>The newly created deque</returns>
private static Deque<int> createNonNormalizedDeque(int count) {
Deque<int> intDeque = new Deque<int>(16);
for(int item = 4; item < count; ++item) {
intDeque.AddLast(item);
}
if(count > 3) { intDeque.AddFirst(3); }
if(count > 2) { intDeque.AddFirst(2); }
if(count > 1) { intDeque.AddFirst(1); }
if(count > 0) { intDeque.AddFirst(0); }
return intDeque;
}
/// <summary>Creates a deque filled with the specified number of items
/// </summary>
/// <param name="count">Number of items the deque will be filled with</param>
/// <returns>The newly created deque</returns>
private static Deque<int> createDeque(int count) {
Deque<int> intDeque = new Deque<int>(16);
for(int item = 0; item < count; ++item) {
intDeque.AddLast(item);
}
return intDeque;
}
}
} // namespace Nuclex.Support.Collections
#endif // UNITTEST

View file

@ -0,0 +1,136 @@
#region Apache License 2.0
/*
Nuclex .NET Framework
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#endregion // Apache License 2.0
using System;
using System.Collections.Generic;
using System.IO;
#if UNITTEST
using NUnit.Framework;
namespace Nuclex.Support.Collections {
/// <summary>Unit Test for the IList extension methods</summary>
[TestFixture]
internal class IListExtensionsTest {
/// <summary>Tests whether the insertion sort algorithm sorts a list correctly</summary>
[Test]
public void InsertionSortCanSortWholeList() {
var testList = new List<int>(capacity: 5) { 1, 5, 2, 4, 3 };
var testListAsIList = (IList<int>)testList;
testListAsIList.InsertionSort();
CollectionAssert.AreEqual(
new List<int>(capacity: 5) { 1, 2, 3, 4, 5 },
testList
);
}
/// <summary>Tests whether the insertion sort algorithm works on big lists</summary>
[Test]
public void InsertionSortCanSortBigList() {
const int ListSize = 16384;
var testList = new List<int>(capacity: ListSize);
{
var random = new Random();
for(int index = 0; index < ListSize; ++index) {
testList.Add(random.Next());
}
}
var testListAsIList = (IList<int>)testList;
testListAsIList.InsertionSort();
for(int index = 1; index < ListSize; ++index) {
Assert.LessOrEqual(testListAsIList[index - 1], testListAsIList[index]);
}
}
/// <summary>Tests whether the insertion sort algorithm respects custom boundaries</summary>
[Test]
public void InsertionSortCanSortListSegment() {
var testList = new List<int>(capacity: 7) { 9, 1, 5, 2, 4, 3, 0 };
var testListAsIList = (IList<int>)testList;
testListAsIList.InsertionSort(1, 5, Comparer<int>.Default);
CollectionAssert.AreEqual(
new List<int>(capacity: 7) { 9, 1, 2, 3, 4, 5, 0 },
testList
);
}
/// <summary>Tests whether the quicksort algorithm sorts a list correctly</summary>
[Test]
public void QuickSortCanSortWholeList() {
var testList = new List<int>(capacity: 5) { 1, 5, 2, 4, 3 };
var testListAsIList = (IList<int>)testList;
testListAsIList.QuickSort();
CollectionAssert.AreEqual(
new List<int>(capacity: 5) { 1, 2, 3, 4, 5 },
testList
);
}
/// <summary>Tests whether the quicksort algorithm works on big lists</summary>
[Test]
public void QuickSortCanSortBigList() {
const int ListSize = 16384;
var testList = new List<int>(capacity: ListSize);
{
var random = new Random();
for(int index = 0; index < ListSize; ++index) {
testList.Add(random.Next());
}
}
var testListAsIList = (IList<int>)testList;
testListAsIList.QuickSort();
for(int index = 1; index < ListSize; ++index) {
Assert.LessOrEqual(testListAsIList[index - 1], testListAsIList[index]);
}
}
/// <summary>Tests whether the quicksort algorithm respects custom boundaries</summary>
[Test]
public void QuickSortCanSortListSegment() {
var testList = new List<int>(capacity: 7) { 9, 1, 5, 2, 4, 3, 0 };
var testListAsIList = (IList<int>)testList;
testListAsIList.QuickSort(1, 5, Comparer<int>.Default);
CollectionAssert.AreEqual(
new List<int>(capacity: 7) { 9, 1, 2, 3, 4, 5, 0 },
testList
);
}
}
} // namespace Nuclex.Support.Collections
#endif // UNITTEST

View file

@ -0,0 +1,55 @@
#region Apache License 2.0
/*
Nuclex .NET Framework
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#endregion // Apache License 2.0
#if UNITTEST
using System;
using System.Collections.Generic;
using NUnit.Framework;
namespace Nuclex.Support.Collections {
/// <summary>Unit Test for the item event argument container</summary>
[TestFixture]
internal class ItemEventArgsTest {
/// <summary>
/// Tests whether an integer argument can be stored in the argument container
/// </summary>
[Test]
public void IntegersCanBeCarried() {
var test = new ItemEventArgs<int>(12345);
Assert.AreEqual(12345, test.Item);
}
/// <summary>
/// Tests whether a string argument can be stored in the argument container
/// </summary>
[Test]
public void StringsCanBeCarried() {
var test = new ItemEventArgs<string>("hello world");
Assert.AreEqual("hello world", test.Item);
}
}
} // namespace Nuclex.Support.Collections
#endif // UNITTEST

View file

@ -0,0 +1,57 @@
#region Apache License 2.0
/*
Nuclex .NET Framework
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#endregion // Apache License 2.0
#if UNITTEST
using System;
using System.Collections.Generic;
using NUnit.Framework;
namespace Nuclex.Support.Collections {
/// <summary>Unit Test for the item event argument container</summary>
[TestFixture]
internal class ItemReplaceEventArgsTest {
/// <summary>
/// Tests whether an integer argument can be stored in the argument container
/// </summary>
[Test]
public void IntegersCanBeCarried() {
var test = new ItemReplaceEventArgs<int>(12345, 54321);
Assert.AreEqual(12345, test.OldItem);
Assert.AreEqual(54321, test.NewItem);
}
/// <summary>
/// Tests whether a string argument can be stored in the argument container
/// </summary>
[Test]
public void StringsCanBeCarried() {
var test = new ItemReplaceEventArgs<string>("hello", "world");
Assert.AreEqual("hello", test.OldItem);
Assert.AreEqual("world", test.NewItem);
}
}
} // namespace Nuclex.Support.Collections
#endif // UNITTEST

View file

@ -0,0 +1,256 @@
#region Apache License 2.0
/*
Nuclex .NET Framework
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#endregion // Apache License 2.0
using System;
using System.Collections.Generic;
using System.IO;
#if UNITTEST
using NUnit.Framework;
namespace Nuclex.Support.Collections {
/// <summary>Unit Test for the list segment class</summary>
[TestFixture]
internal class ListSegmentTest {
/// <summary>
/// Tests whether the default constructor of the ListSegment class throws the
/// right exception when being passed 'null' instead of a list
/// </summary>
[Test]
public void SimpleConstructorThrowsWhenListIsNull() {
Assert.Throws<ArgumentNullException>(
delegate() { new ListSegment<int>(null); }
);
}
/// <summary>
/// Tests whether the simple constructor of the ListSegment class accepts
/// an empty list
/// </summary>
[Test]
public void SimpleConstructorAcceptsEmptyList() {
new ListSegment<int>(new List<int>());
}
/// <summary>
/// Tests whether the full constructor of the ListSegment class throws the
/// right exception when being passed 'null' instead of a string
/// </summary>
[Test]
public void ConstructorThrowsWhenListIsNull() {
Assert.Throws<ArgumentNullException>(
delegate() { new ListSegment<int>(null, 0, 0); }
);
}
/// <summary>
/// Tests whether the full constructor of the ListSegment class accepts
/// an empty string
/// </summary>
[Test]
public void ConstructorAcceptsEmptyList() {
new ListSegment<int>(new List<int>(), 0, 0);
}
/// <summary>
/// Tests whether the full constructor of the ListSegment class throws the
/// right exception when being passed an invalid start offset
/// </summary>
[Test]
public void ConstructorThrowsOnInvalidOffset() {
Assert.Throws<ArgumentOutOfRangeException>(
delegate() { new ListSegment<int>(new List<int>(), -1, 0); }
);
}
/// <summary>
/// Tests whether the full constructor of the ListSegment class throws the
/// right exception when being passed an invalid element count
/// </summary>
[Test]
public void ConstructorThrowsOnInvalidCount() {
Assert.Throws<ArgumentOutOfRangeException>(
delegate() { new ListSegment<int>(new List<int>(), 0, -1); }
);
}
/// <summary>
/// Tests whether the full constructor of the ListSegment class throws the
/// right exception when being passed a string length that's too large
/// </summary>
[Test]
public void ConstructorThrowsOnListOverrun() {
var testList = new List<int>(capacity: 5) { 1, 2, 3, 4, 5 };
Assert.Throws<ArgumentException>(
delegate() { new ListSegment<int>(testList, 3, 3); }
);
}
/// <summary>Tests whether the 'Text' property works as expected</summary>
[Test]
public void ListPropertyStoresOriginalList() {
var testList = new List<int>(capacity: 5) { 1, 2, 3, 4, 5 };
ListSegment<int> testSegment = new ListSegment<int>(testList, 1, 3);
Assert.AreSame(testList, testSegment.List);
}
/// <summary>Tests whether the 'Offset' property works as expected</summary>
[Test]
public void OffsetPropertyIsStored() {
var testList = new List<int>(capacity: 5) { 1, 2, 3, 4, 5 };
ListSegment<int> testSegment = new ListSegment<int>(testList, 1, 3);
Assert.AreEqual(1, testSegment.Offset);
}
/// <summary>Tests whether the 'Count' property works as expected</summary>
[Test]
public void CountPropertyIsStored() {
var testList = new List<int>(capacity: 5) { 1, 2, 3, 4, 5 };
ListSegment<int> testSegment = new ListSegment<int>(testList, 1, 3);
Assert.AreEqual(3, testSegment.Count);
}
/// <summary>
/// Tests whether two differing instances produce different hash codes
/// </summary>
[Test]
public void DifferentInstancesHaveDifferentHashCodes_Usually() {
var forwardCountSegment = new ListSegment<int>(
new List<int>(capacity: 9) { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 2, 7
);
var reverseCountSegment = new ListSegment<int>(
new List<int>(capacity: 9) { 9, 8, 7, 6, 5, 4, 3, 2, 1 }, 1, 8
);
Assert.AreNotEqual(
forwardCountSegment.GetHashCode(), reverseCountSegment.GetHashCode()
);
}
/// <summary>
/// Tests whether two equivalent instances produce an identical hash code
/// </summary>
[Test]
public void EquivalentInstancesHaveSameHashcode() {
var testSegment = new ListSegment<int>(
new List<int>(capacity: 9) { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 2, 7
);
var identicalSegment = new ListSegment<int>(
new List<int>(capacity: 9) { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 2, 7
);
Assert.AreEqual(
testSegment.GetHashCode(), identicalSegment.GetHashCode()
);
}
/// <summary>Tests the equals method performing a comparison against null</summary>
[Test]
public void EqualsAgainstNullIsAlwaysFalse() {
var testList = new List<int>(capacity: 5) { 1, 2, 3, 4, 5 };
ListSegment<int> testSegment = new ListSegment<int>(testList, 1, 3);
Assert.IsFalse(
testSegment.Equals(null)
);
}
/// <summary>Tests the equality operator with differing instances</summary>
[Test]
public void DifferingInstancesAreNotEqual() {
var forwardCountSegment = new ListSegment<int>(
new List<int>(capacity: 9) { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 2, 7
);
var reverseCountSegment = new ListSegment<int>(
new List<int>(capacity: 9) { 9, 8, 7, 6, 5, 4, 3, 2, 1 }, 1, 8
);
Assert.IsFalse(forwardCountSegment == reverseCountSegment);
}
/// <summary>Tests the equality operator with equivalent instances</summary>
[Test]
public void EquivalentInstancesAreEqual() {
var testSegment = new ListSegment<int>(
new List<int>(capacity: 9) { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 2, 7
);
var identicalSegment = new ListSegment<int>(
new List<int>(capacity: 9) { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 2, 7
);
Assert.IsTrue(testSegment == identicalSegment);
}
/// <summary>Tests the inequality operator with differing instances</summary>
[Test]
public void DifferingInstancesAreUnequal() {
var forwardCountSegment = new ListSegment<int>(
new List<int>(capacity: 9) { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 2, 7
);
var reverseCountSegment = new ListSegment<int>(
new List<int>(capacity: 9) { 9, 8, 7, 6, 5, 4, 3, 2, 1 }, 1, 8
);
Assert.IsTrue(forwardCountSegment != reverseCountSegment);
}
/// <summary>Tests the inequality operator with equivalent instances</summary>
[Test]
public void EquivalentInstancesAreNotUnequal() {
var testSegment = new ListSegment<int>(
new List<int>(capacity: 9) { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 2, 7
);
var identicalSegment = new ListSegment<int>(
new List<int>(capacity: 9) { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 2, 7
);
Assert.IsFalse(testSegment != identicalSegment);
}
/// <summary>Tests the ToString() method of the string segment</summary>
[Test]
public void TestToString() {
var testList = new List<int>(capacity: 6) { 1, 2, 3, 4, 5, 6 };
ListSegment<int> testSegment = new ListSegment<int>(testList, 2, 2);
string stringRepresentation = testSegment.ToString();
StringAssert.Contains("3, 4", stringRepresentation);
StringAssert.DoesNotContain("2", stringRepresentation);
StringAssert.DoesNotContain("5", stringRepresentation);
}
/// <summary>Tests whether the 'Text' property works as expected</summary>
[Test]
public void ToListReturnsSubset() {
var testList = new List<int>(capacity: 5) { 1, 2, 3, 4, 5 };
ListSegment<int> testSegment = new ListSegment<int>(testList, 1, 3);
CollectionAssert.AreEqual(
new List<int>(capacity: 3) { 2, 3, 4 },
testSegment.ToList()
);
}
}
} // namespace Nuclex.Support.Collections
#endif // UNITTEST

View file

@ -0,0 +1,390 @@
#region Apache License 2.0
/*
Nuclex .NET Framework
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#endregion // Apache License 2.0
#if UNITTEST
using System;
using System.Collections.Generic;
using NUnit.Framework;
using NMock;
namespace Nuclex.Support.Collections {
/// <summary>Unit tests for the multi dictionary</summary>
[TestFixture]
internal class MultiDictionaryTest {
/// <summary>
/// Verifies that new instances of the multi dictionary can be created
/// </summary>
[Test]
public void CanConstructNewDictionary() {
var dictionary = new MultiDictionary<int, string>();
Assert.IsNotNull(dictionary); // nonsense, prevents compiler warning
}
/// <summary>
/// Verifies that the count is initialized correctly when building
/// a multi dictionary from a dictionary of value collections.
/// </summary>
[Test]
public void CountIsCalculatedIfInitializedFromDictionary() {
var contents = new Dictionary<int, ICollection<string>>();
contents.Add(1, new List<string>(new string[] { "one", "eins" }));
contents.Add(2, new List<string>(new string[] { "two", "zwei" }));
var multiDictionary = new MultiDictionary<int, string>(contents);
Assert.AreEqual(4, multiDictionary.Count);
}
/// <summary>
/// Verifies that a new multi dictionary based on a read-only dictionary is
/// also read-only
/// </summary>
[Test]
public void IsReadOnlyWhenBasedOnReadOnlyContainer() {
var readOnly = new ReadOnlyDictionary<int, ICollection<string>>(
new Dictionary<int, ICollection<string>>()
);
var dictionary = new MultiDictionary<int, string>(readOnly);
Assert.IsTrue(dictionary.IsReadOnly);
}
/// <summary>
/// Ensures that the multi dictionary can contain the same key multiple times
/// (or in other words, multiple values on the same key)
/// </summary>
[Test]
public void CanContainKeyMultipleTimes() {
var dictionary = new MultiDictionary<int, string>();
dictionary.Add(123, "one two three");
dictionary.Add(123, "eins zwei drei");
Assert.AreEqual(2, dictionary.Count);
CollectionAssert.AreEquivalent(
new KeyValuePair<int, string>[] {
new KeyValuePair<int, string>(123, "one two three"),
new KeyValuePair<int, string>(123, "eins zwei drei")
},
dictionary
);
}
/// <summary>
/// Verifies that adding values through the indexer still updates the item count
/// </summary>
[Test]
public void AddingValuesFromIndexerUpdatesCount() {
var dictionary = new MultiDictionary<int, string>();
dictionary.Add(42, "the answer to everything");
dictionary[42].Add("21x2");
Assert.AreEqual(2, dictionary.Count);
CollectionAssert.AreEquivalent(
new KeyValuePair<int, string>[] {
new KeyValuePair<int, string>(42, "the answer to everything"),
new KeyValuePair<int, string>(42, "21x2")
},
dictionary
);
}
/// <summary>
/// Tests whether the collection can count the number of values stored
/// under a key
/// </summary>
[Test]
public void ValuesWithSameKeyCanBeCounted() {
var dictionary = new MultiDictionary<int, string>();
dictionary.Add(10, "ten");
dictionary.Add(20, "twenty");
dictionary.Add(30, "thirty");
dictionary.Add(10, "zehn");
dictionary.Add(20, "zwanzig");
dictionary.Add(10, "dix");
Assert.AreEqual(6, dictionary.Count);
Assert.AreEqual(3, dictionary.CountValues(10));
Assert.AreEqual(2, dictionary.CountValues(20));
Assert.AreEqual(1, dictionary.CountValues(30));
}
/// <summary>
/// Verifies that counting the values of a non-existing key returns 0
/// </summary>
[Test]
public void CountingValuesOfNonExistentKeyReturnsNull() {
var dictionary = new MultiDictionary<int, string>();
Assert.AreEqual(0, dictionary.CountValues(1));
}
/// <summary>
/// Ensures that its possible to remove values individually without affecting
/// other values stored under the same key
/// </summary>
[Test]
public void ValuesCanBeRemovedIndividually() {
var dictionary = new MultiDictionary<int, string>();
dictionary.Add(10, "ten");
dictionary.Add(10, "zehn");
dictionary.Add(10, "dix");
dictionary.Remove(10, "zehn");
Assert.AreEqual(2, dictionary.Count);
CollectionAssert.AreEquivalent(
new KeyValuePair<int, string>[] {
new KeyValuePair<int, string>(10, "ten"),
new KeyValuePair<int, string>(10, "dix")
},
dictionary
);
}
/// <summary>
/// Verifies that the Count property returns the number of unique keys if it is called
/// on the collection-of-collections interface implemented by the multi dictionary
/// </summary>
[Test]
public void CollectionOfCollectionCountIsUniqueKeyCount() {
var dictionary = new MultiDictionary<int, string>();
dictionary.Add(10, "ten");
dictionary.Add(10, "zehn");
Assert.AreEqual(2, dictionary.Count);
var collectionOfCollections =
(ICollection<KeyValuePair<int, ICollection<string>>>)dictionary;
Assert.AreEqual(1, collectionOfCollections.Count);
}
/// <summary>
/// Verifies that the multi dictionary can be tested for containment of a specific value
/// </summary>
[Test]
public void ContainmentCanBeTested() {
var dictionary = new MultiDictionary<int, string>();
dictionary.Add(10, "ten");
dictionary.Add(10, "zehn");
Assert.IsTrue(dictionary.Contains(new KeyValuePair<int, string>(10, "ten")));
Assert.IsTrue(dictionary.Contains(new KeyValuePair<int, string>(10, "zehn")));
Assert.IsFalse(dictionary.Contains(new KeyValuePair<int, string>(10, "dix")));
Assert.IsFalse(dictionary.Contains(new KeyValuePair<int, string>(20, "ten")));
}
/// <summary>
/// Verifies that the multi dictionary can be tested for containment of a specific key
/// </summary>
[Test]
public void KeyContainmentCanBeTested() {
var dictionary = new MultiDictionary<int, string>();
dictionary.Add(10, "ten");
dictionary.Add(10, "zehn");
Assert.IsTrue(dictionary.ContainsKey(10));
Assert.IsFalse(dictionary.ContainsKey(20));
}
/// <summary>
/// Verifies that the key collection can be retrieved from the dictionary
/// </summary>
[Test]
public void KeyCollectionCanBeRetrieved() {
var dictionary = new MultiDictionary<int, string>();
dictionary.Add(10, "ten");
dictionary.Add(10, "zehn");
ICollection<int> keys = dictionary.Keys;
Assert.IsNotNull(keys);
Assert.AreEqual(1, keys.Count);
}
/// <summary>
/// Verifies that the key collection can be retrieved from the dictionary
/// </summary>
[Test]
public void ValueCollectionCanBeRetrieved() {
var dictionary = new MultiDictionary<int, string>();
dictionary.Add(10, "ten");
dictionary.Add(10, "zehn");
dictionary.Add(20, "twenty");
ICollection<string> values = dictionary.Values;
Assert.IsNotNull(values);
Assert.AreEqual(3, values.Count);
}
/// <summary>
/// Verifies that TryGetValue() returns false and doesn't throw if a key
/// is not found in the collection
/// </summary>
[Test]
public void TryGetValueReturnsFalseOnMissingKey() {
var dictionary = new MultiDictionary<int, string>();
ICollection<string> values;
Assert.IsFalse(dictionary.TryGetValue(123, out values));
}
/// <summary>Verifies that keys can be looked up via TryGetValue()</summary>
[Test]
public void TryGetValueCanLookUpValues() {
var dictionary = new MultiDictionary<int, string>();
dictionary.Add(10, "ten");
dictionary.Add(10, "zehn");
ICollection<string> values;
Assert.IsTrue(dictionary.TryGetValue(10, out values));
Assert.AreEqual(2, values.Count);
}
/// <summary>
/// Verifies that assigning null to a key deletes all the values stored
/// under it
/// </summary>
[Test]
public void AssigningNullToKeyRemovesAllValues() {
var dictionary = new MultiDictionary<int, string>();
dictionary.Add(10, "ten");
dictionary.Add(10, "zehn");
dictionary.Add(20, "twenty");
Assert.AreEqual(3, dictionary.Count);
dictionary[10] = null;
Assert.AreEqual(1, dictionary.Count);
Assert.IsFalse(dictionary.ContainsKey(10));
}
/// <summary>
/// Verifies that assigning null to a key deletes all the values stored
/// under it
/// </summary>
[Test]
public void ValueListCanBeAssignedToNewKey() {
var dictionary = new MultiDictionary<int, string>();
dictionary[3] = new List<string>() { "three", "drei" };
Assert.AreEqual(2, dictionary.Count);
Assert.IsTrue(dictionary.Contains(new KeyValuePair<int, string>(3, "three")));
}
/// <summary>
/// Verifies that assigning null to a key deletes all the values stored
/// under it
/// </summary>
[Test]
public void ValueListCanOverwriteExistingKey() {
var dictionary = new MultiDictionary<int, string>();
dictionary.Add(10, "dix");
Assert.AreEqual(1, dictionary.Count);
dictionary[10] = new List<string>() { "ten", "zehn" };
Assert.AreEqual(2, dictionary.Count);
Assert.IsFalse(dictionary.Contains(new KeyValuePair<int, string>(10, "dix")));
Assert.IsTrue(dictionary.Contains(new KeyValuePair<int, string>(10, "ten")));
}
/// <summary>
/// Verifies that nothing bad happens when a key is removed from the dictionary
/// that it doesn't contain
/// </summary>
[Test]
public void NonExistingKeyCanBeRemoved() {
var dictionary = new MultiDictionary<int, string>();
Assert.AreEqual(0, dictionary.RemoveKey(123));
}
/// <summary>
/// Verifies that the remove method returns the number of values that have
/// been removed from the dictionary
/// </summary>
[Test]
public void RemoveReturnsNumberOfValuesRemoved() {
var dictionary = new MultiDictionary<int, string>();
dictionary.Add(10, "ten");
dictionary.Add(10, "zehn");
Assert.AreEqual(2, dictionary.RemoveKey(10));
}
/// <summary>
/// Verifies that the dictionary becomes empty after clearing it
/// </summary>
[Test]
public void DictionaryIsEmptyAfterClear() {
var dictionary = new MultiDictionary<int, string>();
dictionary.Add(10, "ten");
dictionary.Add(10, "zehn");
dictionary.Add(20, "twenty");
Assert.AreEqual(3, dictionary.Count);
dictionary.Clear();
Assert.AreEqual(0, dictionary.Count);
}
/// <summary>
/// Verifies that non-existing values can be removed from the dictionary
/// </summary>
[Test]
public void NonExistingValueCanBeRemoved() {
var dictionary = new MultiDictionary<int, string>();
Assert.IsFalse(dictionary.Remove(123, "test"));
}
/// <summary>
/// Verifies that nothing bad happens when the last value under a key is removed
/// </summary>
[Test]
public void LastValueOfKeyCanBeRemoved() {
var dictionary = new MultiDictionary<int, string>();
dictionary.Add(123, "test");
dictionary.Remove(123, "test");
Assert.AreEqual(0, dictionary.CountValues(123));
}
/// <summary>
/// Verifies that the dictionary can be copied into an array
/// </summary>
[Test]
public void DictionaryCanBeCopiedIntoArray() {
var expected = new List<KeyValuePair<int, string>>() {
new KeyValuePair<int, string>(1, "one"),
new KeyValuePair<int, string>(1, "eins"),
new KeyValuePair<int, string>(2, "two"),
new KeyValuePair<int, string>(2, "zwei")
};
var dictionary = new MultiDictionary<int, string>();
foreach(KeyValuePair<int, string> entry in expected) {
dictionary.Add(entry.Key, entry.Value);
}
var actual = new KeyValuePair<int, string>[4];
dictionary.CopyTo(actual, 0);
CollectionAssert.AreEquivalent(expected, actual);
}
}
} // namespace Nuclex.Support.Collections
#endif // UNITTEST

View file

@ -0,0 +1,144 @@
#region Apache License 2.0
/*
Nuclex .NET Framework
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#endregion // Apache License 2.0
#if !NO_NMOCK
using System;
using System.Collections.Generic;
#if UNITTEST
using NUnit.Framework;
using NMock;
namespace Nuclex.Support.Collections {
/// <summary>Unit Test for the observable collection class</summary>
[TestFixture]
internal class ObservableCollectionTest {
#region interface IObservableCollectionSubscriber
/// <summary>Interface used to test the observable collection</summary>
public interface IObservableCollectionSubscriber {
/// <summary>Called when the collection is about to clear its contents</summary>
/// <param name="sender">Collection that is clearing its contents</param>
/// <param name="arguments">Not used</param>
void Clearing(object sender, EventArgs arguments);
/// <summary>Called when the collection has been cleared of its contents</summary>
/// <param name="sender">Collection that was cleared of its contents</param>
/// <param name="arguments">Not used</param>
void Cleared(object sender, EventArgs arguments);
/// <summary>Called when an item is added to the collection</summary>
/// <param name="sender">Collection to which an item is being added</param>
/// <param name="arguments">Contains the item that is being added</param>
void ItemAdded(object sender, ItemEventArgs<int> arguments);
/// <summary>Called when an item is removed from the collection</summary>
/// <param name="sender">Collection from which an item is being removed</param>
/// <param name="arguments">Contains the item that is being removed</param>
void ItemRemoved(object sender, ItemEventArgs<int> arguments);
}
#endregion // interface IObservableCollectionSubscriber
/// <summary>Initialization routine executed before each test is run</summary>
[SetUp]
public void Setup() {
this.mockery = new MockFactory();
this.mockedSubscriber = this.mockery.CreateMock<IObservableCollectionSubscriber>();
this.observedCollection = new ObservableCollection<int>();
this.observedCollection.Clearing += new EventHandler(
this.mockedSubscriber.MockObject.Clearing
);
this.observedCollection.Cleared += new EventHandler(
this.mockedSubscriber.MockObject.Cleared
);
this.observedCollection.ItemAdded += new EventHandler<ItemEventArgs<int>>(
this.mockedSubscriber.MockObject.ItemAdded
);
this.observedCollection.ItemRemoved += new EventHandler<ItemEventArgs<int>>(
this.mockedSubscriber.MockObject.ItemRemoved
);
}
/// <summary>Tests whether the Clearing event is fired</summary>
[Test]
public void TestClearingEvent() {
this.mockedSubscriber.Expects.One.Method(m => m.Clearing(null, null)).WithAnyArguments();
this.mockedSubscriber.Expects.One.Method(m => m.Cleared(null, null)).WithAnyArguments();
this.observedCollection.Clear();
this.mockery.VerifyAllExpectationsHaveBeenMet();
}
/// <summary>Tests whether the ItemAdded event is fired</summary>
[Test]
public void TestItemAddedEvent() {
this.mockedSubscriber.Expects.One.Method(m => m.ItemAdded(null, null)).WithAnyArguments();
this.observedCollection.Add(123);
this.mockery.VerifyAllExpectationsHaveBeenMet();
}
/// <summary>Tests whether the ItemRemoved event is fired</summary>
[Test]
public void TestItemRemovedEvent() {
this.mockedSubscriber.Expects.One.Method(m => m.ItemAdded(null, null)).WithAnyArguments();
this.observedCollection.Add(123);
this.mockedSubscriber.Expects.One.Method(m => m.ItemRemoved(null, null)).WithAnyArguments();
this.observedCollection.Remove(123);
this.mockery.VerifyAllExpectationsHaveBeenMet();
}
/// <summary>Tests whether a the list constructor is working</summary>
[Test]
public void TestListConstructor() {
int[] integers = new int[] { 12, 34, 56, 78 };
var testCollection = new ObservableCollection<int>(integers);
CollectionAssert.AreEqual(integers, testCollection);
}
/// <summary>Mock object factory</summary>
private MockFactory mockery;
/// <summary>The mocked observable collection subscriber</summary>
private Mock<IObservableCollectionSubscriber> mockedSubscriber;
/// <summary>An observable collection to which a mock will be subscribed</summary>
private ObservableCollection<int> observedCollection;
}
} // namespace Nuclex.Support.Collections
#endif // UNITTEST
#endif // !NO_NMOCK

View file

@ -0,0 +1,589 @@
#region Apache License 2.0
/*
Nuclex .NET Framework
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#endregion // Apache License 2.0
#if !NO_NMOCK
#if UNITTEST
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using NUnit.Framework;
using NMock;
namespace Nuclex.Support.Collections {
/// <summary>Unit Test for the observable dictionary wrapper</summary>
[TestFixture]
internal class ObservableDictionaryTest {
#region interface IObservableDictionarySubscriber
/// <summary>Interface used to test the observable dictionary</summary>
public interface IObservableDictionarySubscriber {
/// <summary>Called when the dictionary is about to clear its contents</summary>
/// <param name="sender">Dictionary that is clearing its contents</param>
/// <param name="arguments">Not used</param>
void Clearing(object sender, EventArgs arguments);
/// <summary>Called when the dictionary has been clear of its contents</summary>
/// <param name="sender">Dictionary that was cleared of its contents</param>
/// <param name="arguments">Not used</param>
void Cleared(object sender, EventArgs arguments);
/// <summary>Called when an item is added to the dictionary</summary>
/// <param name="sender">Dictionary to which an item is being added</param>
/// <param name="arguments">Contains the item that is being added</param>
void ItemAdded(object sender, ItemEventArgs<KeyValuePair<int, string>> arguments);
/// <summary>Called when an item is removed from the dictionary</summary>
/// <param name="sender">Dictionary from which an item is being removed</param>
/// <param name="arguments">Contains the item that is being removed</param>
void ItemRemoved(object sender, ItemEventArgs<KeyValuePair<int, string>> arguments);
/// <summary>Called when an item is replaced in the dictionary</summary>
/// <param name="sender">Dictionary in which an item is being replaced</param>
/// <param name="arguments">Contains the replaced item and its replacement</param>
void ItemReplaced(
object sender, ItemReplaceEventArgs<KeyValuePair<int, string>> arguments
);
}
#endregion // interface IObservableDictionarySubscriber
/// <summary>Initialization routine executed before each test is run</summary>
[SetUp]
public void Setup() {
this.mockery = new MockFactory();
this.mockedSubscriber = this.mockery.CreateMock<IObservableDictionarySubscriber>();
this.observedDictionary = new ObservableDictionary<int, string>();
this.observedDictionary.Add(1, "one");
this.observedDictionary.Add(2, "two");
this.observedDictionary.Add(3, "three");
this.observedDictionary.Add(42, "forty-two");
this.observedDictionary.Clearing +=
new EventHandler(this.mockedSubscriber.MockObject.Clearing);
this.observedDictionary.Cleared +=
new EventHandler(this.mockedSubscriber.MockObject.Cleared);
this.observedDictionary.ItemAdded +=
new EventHandler<ItemEventArgs<KeyValuePair<int, string>>>(
this.mockedSubscriber.MockObject.ItemAdded
);
this.observedDictionary.ItemRemoved +=
new EventHandler<ItemEventArgs<KeyValuePair<int, string>>>(
this.mockedSubscriber.MockObject.ItemRemoved
);
this.observedDictionary.ItemReplaced +=
new EventHandler<ItemReplaceEventArgs<KeyValuePair<int, string>>>(
this.mockedSubscriber.MockObject.ItemReplaced
);
}
/// <summary>
/// Verifies that the default constructor of the observable dictionary works
/// </summary>
[Test]
public void TestDefaultConstructor() {
ObservableDictionary<int, string> testDictionary =
new ObservableDictionary<int, string>();
Assert.AreEqual(0, testDictionary.Count);
}
/// <summary>
/// Verifies that the copy constructor of the observable dictionary works
/// </summary>
[Test]
public void TestCopyConstructor() {
Dictionary<int, string> numbers = createTestDictionary();
ObservableDictionary<int, string> testDictionary = makeObservable(numbers);
CollectionAssert.AreEqual(numbers, testDictionary);
}
/// <summary>Verifies that the IsReadOnly property is working</summary>
[Test]
public void TestIsReadOnly() {
Dictionary<int, string> numbers = createTestDictionary();
ObservableDictionary<int, string> testDictionary = makeObservable(numbers);
Assert.IsFalse(testDictionary.IsReadOnly);
}
/// <summary>
/// Checks whether the Contains() method of the observable dictionary is able to
/// determine if the dictionary contains an item
/// </summary>
[Test]
public void TestContains() {
Dictionary<int, string> numbers = createTestDictionary();
ObservableDictionary<int, string> testDictionary = makeObservable(numbers);
Assert.IsTrue(
testDictionary.Contains(new KeyValuePair<int, string>(42, "forty-two"))
);
Assert.IsFalse(
testDictionary.Contains(new KeyValuePair<int, string>(24, "twenty-four"))
);
}
/// <summary>
/// Checks whether the Contains() method of the observable dictionary is able to
/// determine if the dictionary contains a key
/// </summary>
[Test]
public void TestContainsKey() {
Dictionary<int, string> numbers = createTestDictionary();
ObservableDictionary<int, string> testDictionary = makeObservable(numbers);
Assert.IsTrue(testDictionary.ContainsKey(42));
Assert.IsFalse(testDictionary.ContainsKey(24));
}
/// <summary>
/// Verifies that the CopyTo() of the observable dictionary works
/// </summary>
[Test]
public void TestCopyToArray() {
Dictionary<int, string> numbers = createTestDictionary();
ObservableDictionary<int, string> testDictionary = makeObservable(numbers);
KeyValuePair<int, string>[] items = new KeyValuePair<int, string>[numbers.Count];
testDictionary.CopyTo(items, 0);
CollectionAssert.AreEqual(numbers, items);
}
/// <summary>
/// Tests whether the typesafe enumerator of the observable dictionary is working
/// </summary>
[Test]
public void TestTypesafeEnumerator() {
Dictionary<int, string> numbers = createTestDictionary();
ObservableDictionary<int, string> testDictionary = makeObservable(numbers);
List<KeyValuePair<int, string>> outputItems = new List<KeyValuePair<int, string>>();
foreach(KeyValuePair<int, string> item in testDictionary) {
outputItems.Add(item);
}
CollectionAssert.AreEqual(numbers, outputItems);
}
/// <summary>
/// Tests whether the keys collection of the observable dictionary can be queried
/// </summary>
[Test]
public void TestGetKeysCollection() {
Dictionary<int, string> numbers = createTestDictionary();
ObservableDictionary<int, string> testDictionary = makeObservable(numbers);
ICollection<int> inputKeys = numbers.Keys;
ICollection<int> keys = testDictionary.Keys;
CollectionAssert.AreEquivalent(inputKeys, keys);
}
/// <summary>
/// Tests whether the values collection of the observable dictionary can be queried
/// </summary>
[Test]
public void TestGetValuesCollection() {
Dictionary<int, string> numbers = createTestDictionary();
ObservableDictionary<int, string> testDictionary = makeObservable(numbers);
ICollection<string> inputValues = numbers.Values;
ICollection<string> values = testDictionary.Values;
CollectionAssert.AreEquivalent(inputValues, values);
}
/// <summary>
/// Tests whether the TryGetValue() method of the observable dictionary is working
/// </summary>
[Test]
public void TestTryGetValue() {
string value;
Assert.IsTrue(this.observedDictionary.TryGetValue(42, out value));
Assert.AreEqual("forty-two", value);
Assert.IsFalse(this.observedDictionary.TryGetValue(24, out value));
Assert.AreEqual(null, value);
}
/// <summary>
/// Tests whether the retrieval of values using the indexer of the observable
/// dictionary is working
/// </summary>
[Test]
public void TestRetrieveValueByIndexer() {
Assert.AreEqual("forty-two", this.observedDictionary[42]);
}
/// <summary>
/// Tests whether an exception is thrown if the indexer of the observable dictionary
/// is used to attempt to retrieve a non-existing value
/// </summary>
[Test]
public void TestRetrieveNonExistingValueByIndexer() {
Assert.Throws<KeyNotFoundException>(
delegate() { Console.WriteLine(this.observedDictionary[24]); }
);
}
/// <summary>
/// Checks whether the Add() methods works via the generic
/// IDictionary&lt;&gt; interface
/// </summary>
[Test]
public void TestAddViaGenericIDictionary() {
this.mockedSubscriber.Expects.One.Method(m => m.ItemAdded(null, null)).WithAnyArguments();
(this.observedDictionary as IDictionary<int, string>).Add(10, "ten");
this.mockery.VerifyAllExpectationsHaveBeenMet();
CollectionAssert.Contains(
this.observedDictionary, new KeyValuePair<int, string>(10, "ten")
);
}
/// <summary>
/// Checks whether the Remove() method works via the generic
/// IDictionary&lt;&gt; interface
/// </summary>
[Test]
public void TestRemoveViaGenericIDictionary() {
this.mockedSubscriber.Expects.One.Method(m => m.ItemRemoved(null, null)).WithAnyArguments();
(this.observedDictionary as IDictionary<int, string>).Remove(3);
this.mockery.VerifyAllExpectationsHaveBeenMet();
CollectionAssert.DoesNotContain(this.observedDictionary.Keys, 3);
}
/// <summary>
/// Tests whether the TryGetValue() method of the observable dictionary is working
/// </summary>
[Test]
public void TestRetrieveValueByIndexerViaGenericIDictionary() {
Assert.AreEqual(
"forty-two", (this.observedDictionary as IDictionary<int, string>)[42]
);
}
/// <summary>
/// Verifies that the indexer can be used to insert an item via the generic
/// IDictionar&lt;&gt; interface
/// </summary>
[Test]
public void TestReplaceByIndexerViaGenericIDictionary() {
this.mockedSubscriber.Expects.One.Method(
m => m.ItemReplaced(null, null)
).WithAnyArguments();
(this.observedDictionary as IDictionary<int, string>)[42] = "two and fourty";
this.mockery.VerifyAllExpectationsHaveBeenMet();
Assert.AreEqual("two and fourty", this.observedDictionary[42]);
}
/// <summary>
/// Checks whether the Clear() method of observable dictionary is working
/// </summary>
[Test]
public void TestClearViaIDictionary() {
this.mockedSubscriber.Expects.One.Method(
m => m.Clearing(null, null)
).WithAnyArguments();
this.mockedSubscriber.Expects.One.Method(
m => m.Cleared(null, null)
).WithAnyArguments();
(this.observedDictionary as IDictionary).Clear();
this.mockery.VerifyAllExpectationsHaveBeenMet();
Assert.AreEqual(0, this.observedDictionary.Count);
}
/// <summary>
/// Checks whether the Add() method works via the IDictionary interface
/// </summary>
[Test]
public void TestAddViaIDictionary() {
this.mockedSubscriber.Expects.One.Method(
m => m.ItemAdded(null, null)
).WithAnyArguments();
(this.observedDictionary as IDictionary).Add(24, "twenty-four");
this.mockery.VerifyAllExpectationsHaveBeenMet();
CollectionAssert.Contains(
this.observedDictionary, new KeyValuePair<int, string>(24, "twenty-four")
);
}
/// <summary>
/// Checks whether the Contains() method of the observable dictionary is able to
/// determine if the dictionary contains an item via the IDictionary interface
/// </summary>
[Test]
public void TestContainsViaIDictionary() {
Assert.IsTrue((this.observedDictionary as IDictionary).Contains(42));
Assert.IsFalse((this.observedDictionary as IDictionary).Contains(24));
}
/// <summary>
/// Checks whether the GetEnumerator() method of the observable dictionary
/// returns a working enumerator if accessed via the IDictionary interface
/// </summary>
[Test]
public void TestEnumeratorViaIDictionary() {
Dictionary<int, string> outputNumbers = new Dictionary<int, string>();
foreach(DictionaryEntry entry in (this.observedDictionary as IDictionary)) {
(outputNumbers as IDictionary).Add(entry.Key, entry.Value);
}
CollectionAssert.AreEquivalent(this.observedDictionary, outputNumbers);
}
/// <summary>
/// Checks whether the IsFixedSize property of the observable dictionary returns
/// the expected result for a read only dictionary based on a dynamic dictionary
/// </summary>
[Test]
public void TestIsFixedSizeViaIList() {
Assert.IsFalse((this.observedDictionary as IDictionary).IsFixedSize);
}
/// <summary>
/// Tests whether the keys collection of the observable dictionary can be queried
/// via the IDictionary interface
/// </summary>
[Test]
public void TestGetKeysCollectionViaIDictionary() {
ICollection keys = (this.observedDictionary as IDictionary).Keys;
Assert.AreEqual(this.observedDictionary.Count, keys.Count);
}
/// <summary>
/// Tests whether the values collection of the observable dictionary can be queried
/// via the IDictionary interface
/// </summary>
[Test]
public void TestGetValuesCollectionViaIDictionary() {
ICollection values = (this.observedDictionary as IDictionary).Values;
Assert.AreEqual(this.observedDictionary.Count, values.Count);
}
/// <summary>
/// Checks whether Remove() method works via the IDictionary interface
/// </summary>
[Test]
public void TestRemoveViaIDictionary() {
this.mockedSubscriber.Expects.One.Method(m => m.ItemRemoved(null, null)).WithAnyArguments();
(this.observedDictionary as IDictionary).Remove(3);
this.mockery.VerifyAllExpectationsHaveBeenMet();
CollectionAssert.DoesNotContain(this.observedDictionary.Keys, 3);
}
/// <summary>
/// Tests whether the retrieval of values using the indexer of the observable
/// dictionary is working via the IDictionary interface
/// </summary>
[Test]
public void TestRetrieveValueByIndexerViaIDictionary() {
Assert.AreEqual("forty-two", (this.observedDictionary as IDictionary)[42]);
}
/// <summary>
/// Verifies the indexer can be used to insert an item via the IDictionary interface
/// </summary>
[Test]
public void TestReplaceByIndexerViaIDictionary() {
this.mockedSubscriber.Expects.One.Method(
m => m.ItemRemoved(null, null)
).WithAnyArguments();
this.mockedSubscriber.Expects.One.Method(
m => m.ItemAdded(null, null)
).WithAnyArguments();
(this.observedDictionary as IDictionary)[42] = "two and fourty";
this.mockery.VerifyAllExpectationsHaveBeenMet();
Assert.AreEqual("two and fourty", this.observedDictionary[42]);
}
/// <summary>
/// Checks whether Add() method is working via the generic
/// ICollection&lt;&gt; interface
/// </summary>
[Test]
public void TestAddViaGenericICollection() {
this.mockedSubscriber.Expects.One.Method(
m => m.ItemAdded(null, null)
).WithAnyArguments();
(this.observedDictionary as ICollection<KeyValuePair<int, string>>).Add(
new KeyValuePair<int, string>(24, "twenty-four")
);
this.mockery.VerifyAllExpectationsHaveBeenMet();
CollectionAssert.Contains(
this.observedDictionary, new KeyValuePair<int, string>(24, "twenty-four")
);
}
/// <summary>
/// Checks whether the Clear() method is working via the generic
/// ICollection&lt;&gt; interface
/// </summary>
[Test]
public void TestClearViaGenericICollection() {
this.mockedSubscriber.Expects.One.Method(
m => m.Clearing(null, null)
).WithAnyArguments();
this.mockedSubscriber.Expects.One.Method(
m => m.Cleared(null, null)
).WithAnyArguments();
(this.observedDictionary as ICollection<KeyValuePair<int, string>>).Clear();
this.mockery.VerifyAllExpectationsHaveBeenMet();
Assert.AreEqual(0, this.observedDictionary.Count);
}
/// <summary>
/// Checks whether the Remove() method is working via the
/// generic ICollection&lt;&gt; interface
/// </summary>
[Test]
public void TestRemoveViaGenericICollection() {
IEnumerator<KeyValuePair<int, string>> enumerator =
(this.observedDictionary as ICollection<KeyValuePair<int, string>>).GetEnumerator();
enumerator.MoveNext();
this.mockedSubscriber.Expects.One.Method(
m => m.ItemRemoved(null, null)
).WithAnyArguments();
(this.observedDictionary as ICollection<KeyValuePair<int, string>>).Remove(
enumerator.Current
);
this.mockery.VerifyAllExpectationsHaveBeenMet();
CollectionAssert.DoesNotContain(this.observedDictionary, enumerator.Current);
}
/// <summary>
/// Verifies that the CopyTo() of the observable dictionary works when called
/// via the the ICollection interface
/// </summary>
[Test]
public void TestCopyToArrayViaICollection() {
Dictionary<int, string> numbers = createTestDictionary();
ObservableDictionary<int, string> testDictionary = makeObservable(numbers);
DictionaryEntry[] entries = new DictionaryEntry[numbers.Count];
(testDictionary as ICollection).CopyTo(entries, 0);
KeyValuePair<int, string>[] items = new KeyValuePair<int, string>[numbers.Count];
for(int index = 0; index < entries.Length; ++index) {
items[index] = new KeyValuePair<int, string>(
(int)entries[index].Key, (string)entries[index].Value
);
}
CollectionAssert.AreEquivalent(numbers, items);
}
/// <summary>
/// Verifies that the IsSynchronized property and the SyncRoot property are working
/// </summary>
[Test]
public void TestSynchronization() {
Dictionary<int, string> numbers = createTestDictionary();
ObservableDictionary<int, string> testDictionary = makeObservable(numbers);
if(!(testDictionary as ICollection).IsSynchronized) {
lock((testDictionary as ICollection).SyncRoot) {
Assert.AreEqual(numbers.Count, testDictionary.Count);
}
}
}
/// <summary>
/// Test whether the observable dictionary can be serialized
/// </summary>
[Test]
public void TestSerialization() {
BinaryFormatter formatter = new BinaryFormatter();
using(MemoryStream memory = new MemoryStream()) {
Dictionary<int, string> numbers = createTestDictionary();
ObservableDictionary<int, string> testDictionary1 = makeObservable(numbers);
formatter.Serialize(memory, testDictionary1);
memory.Position = 0;
object testDictionary2 = formatter.Deserialize(memory);
CollectionAssert.AreEquivalent(testDictionary1, (IEnumerable)testDictionary2);
}
}
/// <summary>
/// Creates a new observable dictionary filled with some values for testing
/// </summary>
/// <returns>The newly created observable dictionary</returns>
private static Dictionary<int, string> createTestDictionary() {
Dictionary<int, string> numbers = new Dictionary<int, string>();
numbers.Add(1, "one");
numbers.Add(2, "two");
numbers.Add(3, "three");
numbers.Add(42, "forty-two");
return new Dictionary<int, string>(numbers);
}
/// <summary>
/// Creates a new observable dictionary filled with some values for testing
/// </summary>
/// <returns>The newly created observable dictionary</returns>
private static ObservableDictionary<int, string> makeObservable(
IDictionary<int, string> dictionary
) {
return new ObservableDictionary<int, string>(dictionary);
}
/// <summary>Mock object factory</summary>
private MockFactory mockery;
/// <summary>The mocked observable collection subscriber</summary>
private Mock<IObservableDictionarySubscriber> mockedSubscriber;
/// <summary>An observable dictionary to which a mock will be subscribed</summary>
private ObservableDictionary<int, string> observedDictionary;
}
} // namespace Nuclex.Support.Collections
#endif // UNITTEST
#endif // !NO_NMOCK

View file

@ -0,0 +1,175 @@
#region Apache License 2.0
/*
Nuclex .NET Framework
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#endregion // Apache License 2.0
#if !NO_NMOCK
using System;
using System.Collections.Generic;
#if UNITTEST
using NUnit.Framework;
using NMock;
namespace Nuclex.Support.Collections {
/// <summary>Unit Test for the observable list class</summary>
[TestFixture]
internal class ObservableListTest {
#region interface IObservableCollectionSubscriber
/// <summary>Interface used to test the observable collection</summary>
public interface IObservableCollectionSubscriber {
/// <summary>Called when the collection is about to clear its contents</summary>
/// <param name="sender">Collection that is clearing its contents</param>
/// <param name="arguments">Not used</param>
void Clearing(object sender, EventArgs arguments);
/// <summary>Called when the collection has been cleared of its contents</summary>
/// <param name="sender">Collection that was cleared of its contents</param>
/// <param name="arguments">Not used</param>
void Cleared(object sender, EventArgs arguments);
/// <summary>Called when an item is added to the collection</summary>
/// <param name="sender">Collection to which an item is being added</param>
/// <param name="arguments">Contains the item that is being added</param>
void ItemAdded(object sender, ItemEventArgs<int> arguments);
/// <summary>Called when an item is removed from the collection</summary>
/// <param name="sender">Collection from which an item is being removed</param>
/// <param name="arguments">Contains the item that is being removed</param>
void ItemRemoved(object sender, ItemEventArgs<int> arguments);
/// <summary>Called when an item is replaced in the dictionary</summary>
/// <param name="sender">Dictionary in which an item is being replaced</param>
/// <param name="arguments">Contains the replaced item and its replacement</param>
void ItemReplaced(object sender, ItemReplaceEventArgs<int> arguments);
}
#endregion // interface IObservableCollectionSubscriber
/// <summary>Initialization routine executed before each test is run</summary>
[SetUp]
public void Setup() {
this.mockery = new MockFactory();
this.mockedSubscriber = this.mockery.CreateMock<IObservableCollectionSubscriber>();
this.observedList = new ObservableList<int>();
this.observedList.Clearing += new EventHandler(
this.mockedSubscriber.MockObject.Clearing
);
this.observedList.Cleared += new EventHandler(
this.mockedSubscriber.MockObject.Cleared
);
this.observedList.ItemAdded += new EventHandler<ItemEventArgs<int>>(
this.mockedSubscriber.MockObject.ItemAdded
);
this.observedList.ItemRemoved += new EventHandler<ItemEventArgs<int>>(
this.mockedSubscriber.MockObject.ItemRemoved
);
this.observedList.ItemReplaced += new EventHandler<ItemReplaceEventArgs<int>>(
this.mockedSubscriber.MockObject.ItemReplaced
);
}
/// <summary>Tests whether the Clearing event is fired</summary>
[Test]
public void TestClearingEvent() {
this.mockedSubscriber.Expects.One.Method(m => m.Clearing(null, null)).WithAnyArguments();
this.mockedSubscriber.Expects.One.Method(m => m.Cleared(null, null)).WithAnyArguments();
this.observedList.Clear();
this.mockery.VerifyAllExpectationsHaveBeenMet();
}
/// <summary>Tests whether the ItemAdded event is fired</summary>
[Test]
public void TestItemAddedEvent() {
this.mockedSubscriber.Expects.One.Method(m => m.ItemAdded(null, null)).WithAnyArguments();
this.observedList.Add(123);
this.mockery.VerifyAllExpectationsHaveBeenMet();
}
/// <summary>Tests whether the ItemRemoved event is fired</summary>
[Test]
public void TestItemRemovedEvent() {
this.mockedSubscriber.Expects.One.Method(m => m.ItemAdded(null, null)).WithAnyArguments();
this.observedList.Add(123);
this.mockedSubscriber.Expects.One.Method(m => m.ItemRemoved(null, null)).WithAnyArguments();
this.observedList.Remove(123);
this.mockery.VerifyAllExpectationsHaveBeenMet();
}
/// <summary>Tests whether items in the collection can be replaced</summary>
[Test]
public void TestItemReplacement() {
this.mockedSubscriber.Expects.Exactly(3).Method(
m => m.ItemAdded(null, null)
).WithAnyArguments();
this.observedList.Add(1);
this.observedList.Add(2);
this.observedList.Add(3);
this.mockedSubscriber.Expects.One.Method(m => m.ItemReplaced(null, null)).WithAnyArguments();
// Replace the middle item with something else
this.observedList[1] = 4;
Assert.AreEqual(
1, this.observedList.IndexOf(4)
);
this.mockery.VerifyAllExpectationsHaveBeenMet();
}
/// <summary>Tests whether a the list constructor is working</summary>
[Test]
public void TestListConstructor() {
int[] integers = new int[] { 12, 34, 56, 78 };
var testList = new ObservableList<int>(integers);
CollectionAssert.AreEqual(integers, testList);
}
/// <summary>Mock object factory</summary>
private MockFactory mockery;
/// <summary>The mocked observable collection subscriber</summary>
private Mock<IObservableCollectionSubscriber> mockedSubscriber;
/// <summary>An observable collection to which a mock will be subscribed</summary>
private ObservableList<int> observedList;
}
} // namespace Nuclex.Support.Collections
#endif // UNITTEST
#endif // !NO_NMOCK

View file

@ -0,0 +1,300 @@
#region Apache License 2.0
/*
Nuclex .NET Framework
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#endregion // Apache License 2.0
#if !NO_SETS
#if UNITTEST
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using NUnit.Framework;
using NMock;
namespace Nuclex.Support.Collections {
/// <summary>Unit Test for the observable set wrapper</summary>
[TestFixture]
internal class ObservableSetTest {
#region interface IObservableCollectionSubscriber<TItem>
public interface IObservableCollectionSubscriber<TItem> {
/// <summary>Called when an item has been added to the collection</summary>
void ItemAdded(object sender, ItemEventArgs<TItem> arguments);
/// <summary>Called when an item is removed from the collection</summary>
void ItemRemoved(object sender, ItemEventArgs<TItem> arguments);
/// <summary>Called when an item is replaced in the collection</summary>
void ItemReplaced(object sender, ItemReplaceEventArgs<TItem> arguments);
/// <summary>Called when the collection is about to be cleared</summary>
void Clearing(object sender, EventArgs arguments);
/// <summary>Called when the collection has been cleared</summary>
void Cleared(object sender, EventArgs arguments);
}
#endregion // interface IObservableCollectionSubscriber<TItem>
/// <summary>Called before each test is run</summary>
[SetUp]
public void Setup() {
this.mockFactory = new MockFactory();
this.observableSet = new ObservableSet<int>();
this.subscriber = this.mockFactory.CreateMock<IObservableCollectionSubscriber<int>>();
this.observableSet.ItemAdded += this.subscriber.MockObject.ItemAdded;
this.observableSet.ItemRemoved += this.subscriber.MockObject.ItemRemoved;
this.observableSet.ItemReplaced += this.subscriber.MockObject.ItemReplaced;
this.observableSet.Clearing += this.subscriber.MockObject.Clearing;
this.observableSet.Cleared += this.subscriber.MockObject.Cleared;
}
/// <summary>Called after each test has run</summary>
[TearDown]
public void Teardown() {
if(this.mockFactory != null) {
this.mockFactory.VerifyAllExpectationsHaveBeenMet();
this.subscriber = null;
this.mockFactory.Dispose();
this.mockFactory = null;
}
}
/// <summary>
/// Verifies that the observable set has a default constructor
/// </summary>
[Test]
public void HasDefaultConstructor() {
Assert.IsNotNull(new ObservableSet<int>());
}
/// <summary>
/// Verifies that adding items to the set triggers the 'ItemAdded' event
/// </summary>
[Test]
public void AddingItemsTriggersEvent() {
this.subscriber.Expects.One.Method((s) => s.ItemAdded(null, null)).WithAnyArguments();
this.observableSet.Add(123);
}
/// <summary>
/// Verifies that removing items from the set triggers the 'ItemRemoved' event
/// </summary>
[Test]
public void RemovingItemsTriggersEvent() {
this.subscriber.Expects.One.Method((s) => s.ItemAdded(null, null)).WithAnyArguments();
this.observableSet.Add(123);
this.subscriber.Expects.One.Method((s) => s.ItemRemoved(null, null)).WithAnyArguments();
this.observableSet.Remove(123);
}
/// <summary>
/// Verifies that adding items to the set triggers the 'ItemAdded' event
/// </summary>
[Test]
public void AddingAlreadyContainedItemDoesNotTriggerEvent() {
this.subscriber.Expects.One.Method((s) => s.ItemAdded(null, null)).WithAnyArguments();
this.observableSet.Add(123);
this.subscriber.Expects.No.Method((s) => s.ItemAdded(null, null)).WithAnyArguments();
this.observableSet.Add(123);
}
/// <summary>
/// Verifies that excepting the set with itself empties the set
/// </summary>
[Test]
public void ExceptWithSelfEmptiesSet() {
this.subscriber.Expects.Exactly(3).Method(
(s) => s.ItemAdded(null, null)
).WithAnyArguments();
this.observableSet.Add(1);
this.observableSet.Add(2);
this.observableSet.Add(3);
Assert.AreEqual(3, this.observableSet.Count);
this.subscriber.Expects.One.Method((s) => s.Clearing(null, null)).WithAnyArguments();
this.subscriber.Expects.One.Method((s) => s.Cleared(null, null)).WithAnyArguments();
this.observableSet.ExceptWith(this.observableSet);
Assert.AreEqual(0, this.observableSet.Count);
}
/// <summary>
/// Verifies that a set can be excepted with a collection
/// </summary>
[Test]
public void SetCanBeExceptedWithCollection() {
this.subscriber.Expects.Exactly(2).Method(
(s) => s.ItemAdded(null, null)
).WithAnyArguments();
this.observableSet.Add(1);
this.observableSet.Add(2);
var collection = new List<int>() { 1 };
this.subscriber.Expects.One.Method((s) => s.ItemRemoved(null, null)).WithAnyArguments();
this.observableSet.ExceptWith(collection);
Assert.AreEqual(1, this.observableSet.Count);
Assert.IsTrue(this.observableSet.Contains(2));
}
/// <summary>
/// Verifies that a set can be intersected with a collection
/// </summary>
[Test]
public void SetCanBeIntersectedWithCollection() {
this.subscriber.Expects.Exactly(2).Method(
(s) => s.ItemAdded(null, null)
).WithAnyArguments();
this.observableSet.Add(1);
this.observableSet.Add(2);
var collection = new List<int>() { 1 };
this.subscriber.Expects.One.Method((s) => s.ItemRemoved(null, null)).WithAnyArguments();
this.observableSet.IntersectWith(collection);
Assert.AreEqual(1, this.observableSet.Count);
Assert.IsTrue(this.observableSet.Contains(1));
}
/// <summary>
/// Verifies that it's possible to determine whether a set is a proper subset
/// or superset of another set
/// </summary>
[Test]
public void CanDetermineProperSubsetAndSuperset() {
var set1 = new ObservableSet<int>() { 1, 2, 3 };
var set2 = new HashSet<int>() { 1, 3 };
Assert.IsTrue(set1.IsProperSupersetOf(set2));
Assert.IsTrue(set2.IsProperSubsetOf(set1));
set2.Add(2);
Assert.IsFalse(set1.IsProperSupersetOf(set2));
Assert.IsFalse(set2.IsProperSubsetOf(set1));
}
/// <summary>
/// Verifies that it's possible to determine whether a set is a subset
/// or a superset of another set
/// </summary>
[Test]
public void CanDetermineSubsetAndSuperset() {
var set1 = new ObservableSet<int>() { 1, 2, 3 };
var set2 = new HashSet<int>() { 1, 2, 3 };
Assert.IsTrue(set1.IsSupersetOf(set2));
Assert.IsTrue(set2.IsSubsetOf(set1));
set2.Add(4);
Assert.IsFalse(set1.IsSupersetOf(set2));
Assert.IsFalse(set2.IsSubsetOf(set1));
}
/// <summary>
/// Verifies that a set can determine if another set overlaps with it
/// </summary>
[Test]
public void CanDetermineOverlap() {
var set1 = new ObservableSet<int>() { 1, 3, 5 };
var set2 = new HashSet<int>() { 3 };
Assert.IsTrue(set1.Overlaps(set2));
Assert.IsTrue(set2.Overlaps(set1));
}
/// <summary>
/// Verifies that a set can determine if another set contains the same elements
/// </summary>
[Test]
public void CanDetermineSetEquality() {
var set1 = new ObservableSet<int>() { 1, 3, 5 };
var set2 = new HashSet<int>() { 3, 1, 5 };
Assert.IsTrue(set1.SetEquals(set2));
Assert.IsTrue(set2.SetEquals(set1));
set1.Add(7);
Assert.IsFalse(set1.SetEquals(set2));
Assert.IsFalse(set2.SetEquals(set1));
}
/// <summary>
/// Verifies that a set can be symmetrically excepted with another set
/// </summary>
[Test]
public void CanBeSymmetricallyExcepted() {
var set1 = new ObservableSet<int>() { 1, 2, 3 };
var set2 = new HashSet<int>() { 3, 4, 5 };
set1.SymmetricExceptWith(set2);
Assert.AreEqual(4, set1.Count);
}
/// <summary>
/// Verifies that a union of two sets can be built
/// </summary>
[Test]
public void CanBeUnioned() {
this.subscriber.Expects.Exactly(3).Method(
(s) => s.ItemAdded(null, null)
).WithAnyArguments();
this.observableSet.Add(1);
this.observableSet.Add(2);
this.observableSet.Add(3);
var set2 = new ObservableSet<int>() { 3, 4, 5 };
this.subscriber.Expects.Exactly(2).Method(
(s) => s.ItemAdded(null, null)
).WithAnyArguments();
this.observableSet.UnionWith(set2);
Assert.AreEqual(5, this.observableSet.Count);
}
/// <summary>Creates mock object for the test</summary>
private MockFactory mockFactory;
/// <summary>Observable set being tested</summary>
private ObservableSet<int> observableSet;
/// <summary>Subscriber for the observable set's events</summary>
private Mock<IObservableCollectionSubscriber<int>> subscriber;
}
} // namespace Nuclex.Support.Collections
#endif // UNITTEST
#endif // !NO_SETS

View file

@ -0,0 +1,150 @@
#region Apache License 2.0
/*
Nuclex .NET Framework
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#endregion // Apache License 2.0
using System;
using System.Collections.Generic;
#if UNITTEST
using NUnit.Framework;
namespace Nuclex.Support.Collections {
/// <summary>Unit Test for the priority queue class</summary>
[TestFixture]
internal class PairPriorityQueueTest {
/// <summary>Tests to ensure the count property is properly updated</summary>
[Test]
public void TestCount() {
PairPriorityQueue<float, string> testQueue = new PairPriorityQueue<float, string>();
Assert.AreEqual(0, testQueue.Count);
testQueue.Enqueue(12.34f, "a");
Assert.AreEqual(1, testQueue.Count);
testQueue.Enqueue(56.78f, "b");
Assert.AreEqual(2, testQueue.Count);
testQueue.Dequeue();
Assert.AreEqual(1, testQueue.Count);
testQueue.Enqueue(9.0f, "c");
Assert.AreEqual(2, testQueue.Count);
testQueue.Clear();
Assert.AreEqual(0, testQueue.Count);
}
/// <summary>Tests to ensure that the priority collection actually sorts items</summary>
[Test]
public void TestOrdering() {
PairPriorityQueue<float, string> testQueue = new PairPriorityQueue<float, string>();
testQueue.Enqueue(1.0f, "a");
testQueue.Enqueue(9.0f, "i");
testQueue.Enqueue(2.0f, "b");
testQueue.Enqueue(8.0f, "h");
testQueue.Enqueue(3.0f, "c");
testQueue.Enqueue(7.0f, "g");
testQueue.Enqueue(4.0f, "d");
testQueue.Enqueue(6.0f, "f");
testQueue.Enqueue(5.0f, "e");
Assert.AreEqual("i", testQueue.Dequeue().Item);
Assert.AreEqual("h", testQueue.Dequeue().Item);
Assert.AreEqual("g", testQueue.Dequeue().Item);
Assert.AreEqual("f", testQueue.Dequeue().Item);
Assert.AreEqual("e", testQueue.Dequeue().Item);
Assert.AreEqual("d", testQueue.Dequeue().Item);
Assert.AreEqual("c", testQueue.Dequeue().Item);
Assert.AreEqual("b", testQueue.Dequeue().Item);
Assert.AreEqual("a", testQueue.Dequeue().Item);
}
/// <summary>Tests to ensure that the priority collection's Peek() method works</summary>
[Test]
public void TestPeek() {
PairPriorityQueue<float, string> testQueue = new PairPriorityQueue<float, string>();
testQueue.Enqueue(1.0f, "a");
testQueue.Enqueue(2.0f, "b");
testQueue.Enqueue(0.0f, "c");
Assert.AreEqual("b", testQueue.Peek().Item);
}
/// <summary>Tests whether the priority collection can copy itself into an array</summary>
[Test]
public void TestCopyTo() {
PairPriorityQueue<float, string> testQueue = new PairPriorityQueue<float, string>();
testQueue.Enqueue(1.0f, "a");
testQueue.Enqueue(9.0f, "i");
testQueue.Enqueue(2.0f, "b");
testQueue.Enqueue(8.0f, "h");
testQueue.Enqueue(3.0f, "c");
testQueue.Enqueue(7.0f, "g");
testQueue.Enqueue(4.0f, "d");
testQueue.Enqueue(6.0f, "f");
testQueue.Enqueue(5.0f, "e");
PriorityItemPair<float, string>[] itemArray = new PriorityItemPair<float, string>[9];
testQueue.CopyTo(itemArray, 0);
CollectionAssert.AreEquivalent(testQueue, itemArray);
}
/// <summary>
/// Tests whether the priority collection provides a synchronization root
/// </summary>
[Test]
public void TestSyncRoot() {
PairPriorityQueue<int, int> testQueue = new PairPriorityQueue<int, int>();
// If IsSynchronized returns true, SyncRoot is allowed to be null
if(!testQueue.IsSynchronized) {
lock(testQueue.SyncRoot) {
testQueue.Clear();
}
}
}
/// <summary>
/// Tests whether the priority collection provides a working type-safe enumerator
/// </summary>
[Test]
public void TestEnumerator() {
PairPriorityQueue<float, string> testQueue = new PairPriorityQueue<float, string>();
testQueue.Enqueue(1.0f, "a");
testQueue.Enqueue(2.0f, "b");
testQueue.Enqueue(0.0f, "c");
List<PriorityItemPair<float, string>> testList =
new List<PriorityItemPair<float,string>>();
foreach(PriorityItemPair<float, string> entry in testQueue) {
testList.Add(entry);
}
CollectionAssert.AreEquivalent(testQueue, testList);
}
}
} // namespace Nuclex.Support.Collections
#endif // UNITTEST

View file

@ -0,0 +1,100 @@
#region Apache License 2.0
/*
Nuclex .NET Framework
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#endregion // Apache License 2.0
using System;
using System.Collections.Generic;
#if UNITTEST
using NUnit.Framework;
namespace Nuclex.Support.Collections {
/// <summary>Unit Test for the Parentable class</summary>
[TestFixture]
internal class ParentableTest {
#region class TestParentable
/// <summary>Parentable object that can be the child of an int</summary>
private class TestParentable : Parentable<int> {
/// <summary>Initializes a new instance of the parentable test class</summary>
public TestParentable() { }
/// <summary>The parent object that owns this instance</summary>
public int GetParent() {
return base.Parent;
}
/// <summary>Invoked whenever the instance's owner changes</summary>
/// <remarks>
/// When items are parented for the first time, the oldParent argument will
/// be null. Also, if the element is removed from the collection, the
/// current parent will be null.
/// </remarks>
/// <param name="oldParent">Previous owner of the instance</param>
protected override void OnParentChanged(int oldParent) {
this.parentChangedCalled = true;
base.OnParentChanged(oldParent); // to satisfy NCover :-/
}
/// <summary>Whether the OnParentChanged method has been called</summary>
public bool ParentChangedCalled {
get { return this.parentChangedCalled; }
}
/// <summary>Whether the OnParentChanged method has been called</summary>
private bool parentChangedCalled;
}
#endregion // class TestParentable
/// <summary>
/// Tests whether a parent can be assigned and then retrieved from
/// the parentable object
/// </summary>
[Test]
public void TestParentAssignment() {
TestParentable testParentable = new TestParentable();
testParentable.SetParent(12345);
Assert.AreEqual(12345, testParentable.GetParent());
}
/// <summary>
/// Tests whether a parent can be assigned and then retrieved from
/// the parentable object
/// </summary>
[Test]
public void TestParentChangedNotification() {
TestParentable testParentable = new TestParentable();
testParentable.SetParent(12345);
Assert.IsTrue(testParentable.ParentChangedCalled);
}
}
} // namespace Nuclex.Support.Collections
#endif // UNITTEST

View file

@ -0,0 +1,189 @@
#region Apache License 2.0
/*
Nuclex .NET Framework
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#endregion // Apache License 2.0
using System;
using System.Collections.Generic;
#if UNITTEST
using NUnit.Framework;
namespace Nuclex.Support.Collections {
/// <summary>Unit Test for the Parenting Collection class</summary>
[TestFixture]
internal class ParentingCollectionTest {
#region class TestParentable
/// <summary>Parentable object that can be the child of an int</summary>
private class TestParentable : Parentable<int>, IDisposable {
/// <summary>Initializes a new instance of the parentable test class</summary>
public TestParentable() { }
/// <summary>The parent object that owns this instance</summary>
public int GetParent() {
return base.Parent;
}
/// <summary>Immediately releases all resources owned by the item</summary>
public void Dispose() {
this.disposeCalled = true;
}
/// <summary>Whether Dispose() has been called on this item</summary>
public bool DisposeCalled {
get { return this.disposeCalled; }
}
/// <summary>Whether Dispose() has been called on this item</summary>
private bool disposeCalled;
}
#endregion // class TestParentable
#region class TestParentingCollection
/// <summary>Parentable object that can be the child of an int</summary>
private class TestParentingCollection : ParentingCollection<int, TestParentable> {
/// <summary>Changes the parent of the collection</summary>
/// <param name="parent">New parent to assign to the collection</param>
public void SetParent(int parent) {
base.Reparent(parent);
}
/// <summary>Disposes all items contained in the collection</summary>
public new void DisposeItems() {
base.DisposeItems();
}
}
#endregion // class TestParentingCollection
/// <summary>
/// Tests whether the parenting collection propagates its parent to an item that
/// is added to the collection after the collection's aprent is already assigned
/// </summary>
[Test]
public void TestPropagatePreassignedParent() {
TestParentingCollection testCollection = new TestParentingCollection();
TestParentable testParentable = new TestParentable();
testCollection.SetParent(54321);
testCollection.Add(testParentable);
Assert.AreEqual(54321, testParentable.GetParent());
}
/// <summary>
/// Tests whether the parenting collection propagates a new parent to all items
/// contained in it when its parent is changed
/// </summary>
[Test]
public void TestPropagateParentChange() {
TestParentingCollection testCollection = new TestParentingCollection();
TestParentable testParentable = new TestParentable();
testCollection.Add(testParentable);
testCollection.SetParent(54321);
Assert.AreEqual(54321, testParentable.GetParent());
}
/// <summary>
/// Tests whether the parenting collection propagates its parent to an item that
/// is added to the collection after the collection's aprent is already assigned
/// </summary>
[Test]
public void TestPropagateParentOnReplace() {
TestParentingCollection testCollection = new TestParentingCollection();
TestParentable testParentable1 = new TestParentable();
TestParentable testParentable2 = new TestParentable();
testCollection.SetParent(54321);
testCollection.Add(testParentable1);
testCollection[0] = testParentable2;
Assert.AreEqual(0, testParentable1.GetParent());
Assert.AreEqual(54321, testParentable2.GetParent());
}
/// <summary>
/// Tests whether the parenting collection unsets the parent when an item is removed
/// from the collection
/// </summary>
[Test]
public void TestUnsetParentOnRemoveItem() {
TestParentingCollection testCollection = new TestParentingCollection();
TestParentable testParentable = new TestParentable();
testCollection.Add(testParentable);
testCollection.SetParent(54321);
Assert.AreEqual(54321, testParentable.GetParent());
testCollection.RemoveAt(0);
Assert.AreEqual(0, testParentable.GetParent());
}
/// <summary>
/// Tests whether the parenting collection unsets the parent when all item are
/// removed from the collection by clearing it
/// </summary>
[Test]
public void TestUnsetParentOnClear() {
TestParentingCollection testCollection = new TestParentingCollection();
TestParentable testParentable = new TestParentable();
testCollection.Add(testParentable);
testCollection.SetParent(54321);
Assert.AreEqual(54321, testParentable.GetParent());
testCollection.Clear();
Assert.AreEqual(0, testParentable.GetParent());
}
/// <summary>
/// Tests whether the parenting collection calls Dispose() on all contained items
/// that implement IDisposable when its DisposeItems() method is called
/// </summary>
[Test]
public void TestDisposeItems() {
TestParentingCollection testCollection = new TestParentingCollection();
TestParentable testParentable = new TestParentable();
testCollection.Add(testParentable);
testCollection.DisposeItems();
Assert.IsTrue(testParentable.DisposeCalled);
}
}
} // namespace Nuclex.Support.Collections
#endif // UNITTEST

View file

@ -0,0 +1,117 @@
#region Apache License 2.0
/*
Nuclex .NET Framework
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#endregion // Apache License 2.0
#if UNITTEST
using System;
using System.Collections.Generic;
using NUnit.Framework;
namespace Nuclex.Support.Collections {
/// <summary>Unit tests for the Pool class</summary>
[TestFixture]
internal class PoolTest {
#region class TestClass
/// <summary>Used to test the pool</summary>
private class TestClass : IRecyclable {
/// <summary>Returns the object to its initial state</summary>
public void Recycle() {
this.Recycled = true;
}
/// <summary>Whether the instance has been recycled</summary>
public bool Recycled;
}
#endregion // class TestClass
#region class NoDefaultConstructor
/// <summary>Used to test the pool</summary>
private class NoDefaultConstructor {
/// <summary>Private constructor so no instances can be created</summary>
private NoDefaultConstructor() { }
}
#endregion // class NoDefaultConstructor
/// <summary>
/// Verifies that the pool can return newly constructed objects
/// </summary>
[Test]
public void NewInstancesCanBeObtained() {
Pool<TestClass> pool = new Pool<TestClass>();
Assert.IsNotNull(pool.Get());
}
/// <summary>
/// Verifies that an exception is thrown if the pool's default instance creator is used
/// on a type that doesn't have a default constructor
/// </summary>
[Test]
public void UsingDefaultInstanceCreatorRequiresDefaultConstructor() {
Assert.Throws<ArgumentException>(
delegate() { new Pool<NoDefaultConstructor>(); }
);
}
/// <summary>
/// Tests whether the pool can redeem objects that are no longer used
/// </summary>
[Test]
public void InstancesCanBeRedeemed() {
Pool<TestClass> pool = new Pool<TestClass>();
pool.Redeem(new TestClass());
}
/// <summary>
/// Tests whether the Recycle() method is called at the appropriate time
/// </summary>
[Test]
public void RedeemedItemsWillBeRecycled() {
Pool<TestClass> pool = new Pool<TestClass>();
TestClass x = new TestClass();
Assert.IsFalse(x.Recycled);
pool.Redeem(x);
Assert.IsTrue(x.Recycled);
}
/// <summary>Verifies that the pool's Capacity is applied correctly</summary>
[Test]
public void PoolCapacityCanBeAdjusted() {
Pool<TestClass> pool = new Pool<TestClass>(123);
Assert.AreEqual(123, pool.Capacity);
pool.Capacity = 321;
Assert.AreEqual(321, pool.Capacity);
}
}
} // namespace Nuclex.Support.Collections
#endif // UNITTEST

View file

@ -0,0 +1,99 @@
#region Apache License 2.0
/*
Nuclex .NET Framework
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#endregion // Apache License 2.0
#if UNITTEST
using System;
using System.Collections.Generic;
using NUnit.Framework;
namespace Nuclex.Support.Collections {
/// <summary>Unit Test for the Priority/Item pair class</summary>
[TestFixture]
internal class PriorityItemPairTest {
#region class ToStringNullReturner
/// <summary>Test class in which ToString() can return null</summary>
private class ToStringNullReturner {
/// <summary>
/// Returns a System.String that represents the current System.Object
/// </summary>
/// <returns>A System.String that represents the current System.Object</returns>
public override string ToString() { return null; }
}
#endregion // class ToStringNullReturner
/// <summary>Tests whether the pair's default constructor works</summary>
[Test]
public void TestDefaultConstructor() {
new PriorityItemPair<int, string>();
}
/// <summary>Tests whether the priority can be retrieved from the pair</summary>
[Test]
public void TestPriorityRetrieval() {
PriorityItemPair<int, string> testPair = new PriorityItemPair<int, string>(
12345, "hello world"
);
Assert.AreEqual(12345, testPair.Priority);
}
/// <summary>Tests whether the item can be retrieved from the pair</summary>
[Test]
public void TestItemRetrieval() {
PriorityItemPair<int, string> testPair = new PriorityItemPair<int, string>(
12345, "hello world"
);
Assert.AreEqual("hello world", testPair.Item);
}
/// <summary>Tests whether the ToString() methods works with valid strings</summary>
[Test]
public void TestToStringWithValidStrings() {
PriorityItemPair<string, string> testPair = new PriorityItemPair<string, string>(
"hello", "world"
);
Assert.AreEqual("[hello, world]", testPair.ToString());
}
/// <summary>Tests whether the ToString() methods works with null strings</summary>
[Test]
public void TestToStringWithNullStrings() {
PriorityItemPair<ToStringNullReturner, ToStringNullReturner> testPair =
new PriorityItemPair<ToStringNullReturner, ToStringNullReturner>(
new ToStringNullReturner(), new ToStringNullReturner()
);
Assert.AreEqual("[, ]", testPair.ToString());
}
}
} // namespace Nuclex.Support.Collections
#endif // UNITTEST

View file

@ -0,0 +1,157 @@
#region Apache License 2.0
/*
Nuclex .NET Framework
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#endregion // Apache License 2.0
using System;
using System.Collections.Generic;
#if UNITTEST
using NUnit.Framework;
namespace Nuclex.Support.Collections {
/// <summary>Unit Test for the priority queue class</summary>
[TestFixture]
internal class PriorityQueueTest {
#region class FloatComparer
/// <summary>Comparer for two floating point values</summary>
private class FloatComparer : IComparer<float> {
/// <summary>The default instance of this comparer</summary>
public static readonly FloatComparer Default = new FloatComparer();
/// <summary>Compares two floating points against each other</summary>
/// <param name="left">First float to compare</param>
/// <param name="right">Second float to compare</param>
/// <returns>The relationship of the two floats to each other</returns>
public int Compare(float left, float right) {
return Math.Sign(left - right);
}
}
#endregion // class FloatComparer
/// <summary>Tests to ensure the count property is properly updated</summary>
[Test]
public void TestCount() {
PriorityQueue<float> testQueue = new PriorityQueue<float>(FloatComparer.Default);
Assert.AreEqual(0, testQueue.Count);
testQueue.Enqueue(12.34f);
Assert.AreEqual(1, testQueue.Count);
testQueue.Enqueue(56.78f);
Assert.AreEqual(2, testQueue.Count);
testQueue.Dequeue();
Assert.AreEqual(1, testQueue.Count);
testQueue.Enqueue(9.0f);
Assert.AreEqual(2, testQueue.Count);
testQueue.Clear();
Assert.AreEqual(0, testQueue.Count);
}
/// <summary>Tests to ensure that the priority collection actually sorts items</summary>
[Test]
public void TestOrdering() {
PriorityQueue<float> testQueue = new PriorityQueue<float>(FloatComparer.Default);
testQueue.Enqueue(1.0f);
testQueue.Enqueue(9.0f);
testQueue.Enqueue(2.0f);
testQueue.Enqueue(8.0f);
testQueue.Enqueue(3.0f);
testQueue.Enqueue(7.0f);
testQueue.Enqueue(4.0f);
testQueue.Enqueue(6.0f);
testQueue.Enqueue(5.0f);
Assert.AreEqual(9.0f, testQueue.Dequeue());
Assert.AreEqual(8.0f, testQueue.Dequeue());
Assert.AreEqual(7.0f, testQueue.Dequeue());
Assert.AreEqual(6.0f, testQueue.Dequeue());
Assert.AreEqual(5.0f, testQueue.Dequeue());
Assert.AreEqual(4.0f, testQueue.Dequeue());
Assert.AreEqual(3.0f, testQueue.Dequeue());
Assert.AreEqual(2.0f, testQueue.Dequeue());
Assert.AreEqual(1.0f, testQueue.Dequeue());
}
#if DEBUG
/// <summary>
/// Tests whether the priority queue's enumerators are invalidated when the queue's
/// contents are modified
/// </summary>
[Test]
public void TestEnumeratorInvalidationOnModify() {
PriorityQueue<int> testQueue = new PriorityQueue<int>();
IEnumerator<int> testQueueEnumerator = testQueue.GetEnumerator();
testQueue.Enqueue(123);
Assert.Throws<InvalidOperationException>(
delegate() { testQueueEnumerator.MoveNext(); }
);
}
#endif
/// <summary>
/// Verifies that an exception is thrown when Peek() is called on an empty queue
/// </summary>
[Test]
public void TestPeekEmptyQueue() {
PriorityQueue<int> testQueue = new PriorityQueue<int>();
Assert.Throws<InvalidOperationException>(
delegate() { testQueue.Peek(); }
);
}
/// <summary>
/// Verifies that an exception is thrown when Dequeue() is called on an empty queue
/// </summary>
[Test]
public void TestDequeueEmptyQueue() {
PriorityQueue<int> testQueue = new PriorityQueue<int>();
Assert.Throws<InvalidOperationException>(
delegate() { testQueue.Dequeue(); }
);
}
/// <summary>
/// Verifies that the priority queue can handle large amounts of data
/// </summary>
[Test]
public void TestLargeQueue() {
PriorityQueue<int> testQueue = new PriorityQueue<int>();
List<int> testList = new List<int>();
for(int index = 0; index < 1000; ++index) {
testQueue.Enqueue(index * 2);
testList.Add(index * 2);
}
CollectionAssert.AreEquivalent(testList, testQueue);
}
}
} // namespace Nuclex.Support.Collections
#endif // UNITTEST

View file

@ -0,0 +1,162 @@
#region Apache License 2.0
/*
Nuclex .NET Framework
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#endregion // Apache License 2.0
using System;
using System.Collections;
using System.Collections.Generic;
#if UNITTEST
using NUnit.Framework;
namespace Nuclex.Support.Collections {
/// <summary>Unit Test for the read only collection wrapper</summary>
[TestFixture]
internal class ReadOnlyCollectionTest {
/// <summary>
/// Verifies that the copy constructor of the read only collection works
/// </summary>
[Test]
public void TestCopyConstructor() {
int[] integers = new int[] { 12, 34, 56, 78 };
ReadOnlyCollection<int> testCollection = new ReadOnlyCollection<int>(integers);
CollectionAssert.AreEqual(integers, testCollection);
}
/// <summary>Verifies that the IsReadOnly property returns true</summary>
[Test]
public void TestIsReadOnly() {
ReadOnlyCollection<int> testCollection = new ReadOnlyCollection<int>(new int[0]);
Assert.IsTrue(testCollection.IsReadOnly);
}
/// <summary>
/// Verifies that the CopyTo() of the read only collection works
/// </summary>
[Test]
public void TestCopyToArray() {
int[] inputIntegers = new int[] { 12, 34, 56, 78 };
ReadOnlyCollection<int> testCollection = new ReadOnlyCollection<int>(inputIntegers);
int[] outputIntegers = new int[testCollection.Count];
testCollection.CopyTo(outputIntegers, 0);
CollectionAssert.AreEqual(inputIntegers, outputIntegers);
}
/// <summary>
/// Checks whether the Contains() method of the read only collection is able to
/// determine if the collection contains an item
/// </summary>
[Test]
public void TestContains() {
int[] integers = new int[] { 1234, 6789 };
ReadOnlyCollection<int> testCollection = new ReadOnlyCollection<int>(integers);
Assert.IsTrue(testCollection.Contains(1234));
Assert.IsFalse(testCollection.Contains(4321));
}
/// <summary>
/// Ensures that the Add() method of the read only collection throws an exception
/// </summary>
[Test]
public void TestThrowOnAdd() {
ReadOnlyCollection<int> testCollection = new ReadOnlyCollection<int>(new int[0]);
Assert.Throws<NotSupportedException>(
delegate() { (testCollection as ICollection<int>).Add(123); }
);
}
/// <summary>
/// Ensures that the Remove() method of the read only collection throws an exception
/// </summary>
[Test]
public void TestThrowOnRemove() {
ReadOnlyCollection<int> testCollection = new ReadOnlyCollection<int>(new int[0]);
Assert.Throws<NotSupportedException>(
delegate() { (testCollection as ICollection<int>).Remove(123); }
);
}
/// <summary>
/// Ensures that the Clear() method of the read only collection throws an exception
/// </summary>
[Test]
public void TestThrowOnClear() {
ReadOnlyCollection<int> testCollection = new ReadOnlyCollection<int>(new int[0]);
Assert.Throws<NotSupportedException>(
delegate() { (testCollection as ICollection<int>).Clear(); }
);
}
/// <summary>
/// Tests whether the typesafe enumerator of the read only collection is working
/// </summary>
[Test]
public void TestTypesafeEnumerator() {
int[] inputIntegers = new int[] { 12, 34, 56, 78 };
ReadOnlyCollection<int> testCollection = new ReadOnlyCollection<int>(inputIntegers);
List<int> outputIntegers = new List<int>();
foreach(int value in testCollection) {
outputIntegers.Add(value);
}
CollectionAssert.AreEqual(inputIntegers, outputIntegers);
}
/// <summary>
/// Verifies that the CopyTo() of the read only collection works if invoked via
/// the ICollection interface
/// </summary>
[Test]
public void TestCopyToArrayViaICollection() {
int[] inputIntegers = new int[] { 12, 34, 56, 78 };
ReadOnlyCollection<int> testCollection = new ReadOnlyCollection<int>(inputIntegers);
int[] outputIntegers = new int[testCollection.Count];
(testCollection as ICollection).CopyTo(outputIntegers, 0);
CollectionAssert.AreEqual(inputIntegers, outputIntegers);
}
/// <summary>
/// Verifies that the IsSynchronized property and the SyncRoot property are working
/// </summary>
[Test]
public void TestSynchronization() {
ReadOnlyCollection<int> testCollection = new ReadOnlyCollection<int>(new int[0]);
if(!(testCollection as ICollection).IsSynchronized) {
lock((testCollection as ICollection).SyncRoot) {
Assert.AreEqual(0, testCollection.Count);
}
}
}
}
} // namespace Nuclex.Support.Collections
#endif // UNITTEST

View file

@ -0,0 +1,509 @@
#region Apache License 2.0
/*
Nuclex .NET Framework
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#endregion // Apache License 2.0
#if UNITTEST
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using NUnit.Framework;
namespace Nuclex.Support.Collections {
/// <summary>Unit Test for the read only dictionary wrapper</summary>
[TestFixture]
internal class ReadOnlyDictionaryTest {
/// <summary>
/// Verifies that the copy constructor of the read only dictionary works
/// </summary>
[Test]
public void TestCopyConstructor() {
Dictionary<int, string> numbers = createTestDictionary();
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
CollectionAssert.AreEqual(numbers, testDictionary);
}
/// <summary>Verifies that the IsReadOnly property returns true</summary>
[Test]
public void TestIsReadOnly() {
Dictionary<int, string> numbers = createTestDictionary();
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
Assert.IsTrue(testDictionary.IsReadOnly);
}
/// <summary>
/// Checks whether the Contains() method of the read only dictionary is able to
/// determine if the dictionary contains an item
/// </summary>
[Test]
public void TestContains() {
Dictionary<int, string> numbers = createTestDictionary();
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
Assert.IsTrue(
testDictionary.Contains(new KeyValuePair<int, string>(42, "forty-two"))
);
Assert.IsFalse(
testDictionary.Contains(new KeyValuePair<int, string>(24, "twenty-four"))
);
}
/// <summary>
/// Checks whether the Contains() method of the read only dictionary is able to
/// determine if the dictionary contains a key
/// </summary>
[Test]
public void TestContainsKey() {
Dictionary<int, string> numbers = createTestDictionary();
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
Assert.IsTrue(testDictionary.ContainsKey(42));
Assert.IsFalse(testDictionary.ContainsKey(24));
}
/// <summary>
/// Verifies that the CopyTo() of the read only dictionary works
/// </summary>
[Test]
public void TestCopyToArray() {
Dictionary<int, string> numbers = createTestDictionary();
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
KeyValuePair<int, string>[] items = new KeyValuePair<int, string>[numbers.Count];
testDictionary.CopyTo(items, 0);
CollectionAssert.AreEqual(numbers, items);
}
/// <summary>
/// Tests whether the typesafe enumerator of the read only dictionary is working
/// </summary>
[Test]
public void TestTypesafeEnumerator() {
Dictionary<int, string> numbers = createTestDictionary();
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
List<KeyValuePair<int, string>> outputItems = new List<KeyValuePair<int, string>>();
foreach(KeyValuePair<int, string> item in testDictionary) {
outputItems.Add(item);
}
CollectionAssert.AreEqual(numbers, outputItems);
}
/// <summary>
/// Tests whether the keys collection of the read only dictionary can be queried
/// </summary>
[Test]
public void TestGetKeysCollection() {
Dictionary<int, string> numbers = createTestDictionary();
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
ICollection<int> inputKeys = numbers.Keys;
ICollection<int> keys = testDictionary.Keys;
CollectionAssert.AreEqual(inputKeys, keys);
}
/// <summary>
/// Tests whether the values collection of the read only dictionary can be queried
/// </summary>
[Test]
public void TestGetValuesCollection() {
Dictionary<int, string> numbers = createTestDictionary();
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
ICollection<string> inputValues = numbers.Values;
ICollection<string> values = testDictionary.Values;
CollectionAssert.AreEqual(inputValues, values);
}
/// <summary>
/// Tests whether the TryGetValue() method of the read only dictionary is working
/// </summary>
[Test]
public void TestTryGetValue() {
Dictionary<int, string> numbers = createTestDictionary();
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
string value;
Assert.IsTrue(testDictionary.TryGetValue(42, out value));
Assert.AreEqual("forty-two", value);
Assert.IsFalse(testDictionary.TryGetValue(24, out value));
Assert.AreEqual(null, value);
}
/// <summary>
/// Tests whether the retrieval of values using the indexer of the read only
/// dictionary is working
/// </summary>
[Test]
public void TestRetrieveValueByIndexer() {
Dictionary<int, string> numbers = createTestDictionary();
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
Assert.AreEqual("forty-two", testDictionary[42]);
}
/// <summary>
/// Tests whether an exception is thrown if the indexer of the read only dictionary
/// is used to attempt to retrieve a non-existing value
/// </summary>
[Test]
public void TestThrowOnRetrieveNonExistingValueByIndexer() {
Dictionary<int, string> numbers = createTestDictionary();
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
Assert.Throws<KeyNotFoundException>(
delegate() { Console.WriteLine(testDictionary[24]); }
);
}
/// <summary>
/// Checks whether the read only dictionary will throw an exception if its
/// Add() method is called via the generic IDictionary&lt;&gt; interface
/// </summary>
[Test]
public void TestThrowOnAddViaGenericIDictionary() {
Dictionary<int, string> numbers = createTestDictionary();
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
Assert.Throws<NotSupportedException>(
delegate() { (testDictionary as IDictionary<int, string>).Add(10, "ten"); }
);
}
/// <summary>
/// Checks whether the read only dictionary will throw an exception if its
/// Remove() method is called via the generic IDictionary&lt;&gt; interface
/// </summary>
[Test]
public void TestThrowOnRemoveViaGenericIDictionary() {
Dictionary<int, string> numbers = createTestDictionary();
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
Assert.Throws<NotSupportedException>(
delegate() { (testDictionary as IDictionary<int, string>).Remove(3); }
);
}
/// <summary>
/// Tests whether the TryGetValue() method of the read only dictionary is working
/// </summary>
[Test]
public void TestRetrieveValueByIndexerViaGenericIDictionary() {
Dictionary<int, string> numbers = createTestDictionary();
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
Assert.AreEqual("forty-two", (testDictionary as IDictionary<int, string>)[42]);
}
/// <summary>
/// Checks whether the read only dictionary will throw an exception if its
/// indexer is used to insert an item via the generic IDictionar&lt;&gt; interface
/// </summary>
[Test]
public void TestThrowOnReplaceByIndexerViaGenericIDictionary() {
Dictionary<int, string> numbers = createTestDictionary();
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
Assert.Throws<NotSupportedException>(
delegate() { (testDictionary as IDictionary<int, string>)[24] = "twenty-four"; }
);
}
/// <summary>
/// Checks whether the read only dictionary will throw an exception if its
/// Clear() method is called via the IDictionary interface
/// </summary>
[Test]
public void TestThrowOnClearViaIDictionary() {
Dictionary<int, string> numbers = createTestDictionary();
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
Assert.Throws<NotSupportedException>(
delegate() { (testDictionary as IDictionary).Clear(); }
);
}
/// <summary>
/// Checks whether the read only dictionary will throw an exception if its
/// Add() method is called via the IDictionary interface
/// </summary>
[Test]
public void TestThrowOnAddViaIDictionary() {
Dictionary<int, string> numbers = createTestDictionary();
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
Assert.Throws<NotSupportedException>(
delegate() { (testDictionary as IDictionary).Add(24, "twenty-four"); }
);
}
/// <summary>
/// Checks whether the Contains() method of the read only dictionary is able to
/// determine if the dictionary contains an item via the IDictionary interface
/// </summary>
[Test]
public void TestContainsViaIDictionary() {
Dictionary<int, string> numbers = createTestDictionary();
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
Assert.IsTrue((testDictionary as IDictionary).Contains(42));
Assert.IsFalse((testDictionary as IDictionary).Contains(24));
}
/// <summary>
/// Checks whether the GetEnumerator() method of the read only dictionary returns
/// a working enumerator if accessed via the IDictionary interface
/// </summary>
[Test]
public void TestEnumeratorViaIDictionary() {
Dictionary<int, string> numbers = createTestDictionary();
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
Dictionary<int, string> outputNumbers = new Dictionary<int, string>();
foreach(DictionaryEntry entry in (testDictionary as IDictionary)) {
(outputNumbers as IDictionary).Add(entry.Key, entry.Value);
}
CollectionAssert.AreEquivalent(numbers, outputNumbers);
}
/// <summary>
/// Checks whether the IsFixedSize property of the read only dictionary returns
/// the expected result for a read only dictionary based on a dynamic dictionary
/// </summary>
[Test]
public void TestIsFixedSizeViaIList() {
Dictionary<int, string> numbers = createTestDictionary();
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
Assert.IsFalse((testDictionary as IDictionary).IsFixedSize);
}
/// <summary>
/// Tests whether the keys collection of the read only dictionary can be queried
/// via the IDictionary interface
/// </summary>
[Test]
public void TestGetKeysCollectionViaIDictionary() {
Dictionary<int, string> numbers = createTestDictionary();
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
ICollection inputKeys = (numbers as IDictionary).Keys;
ICollection keys = (testDictionary as IDictionary).Keys;
CollectionAssert.AreEqual(inputKeys, keys);
}
/// <summary>
/// Tests whether the values collection of the read only dictionary can be queried
/// via the IDictionary interface
/// </summary>
[Test]
public void TestGetValuesCollectionViaIDictionary() {
Dictionary<int, string> numbers = createTestDictionary();
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
ICollection inputValues = (numbers as IDictionary).Values;
ICollection values = (testDictionary as IDictionary).Values;
CollectionAssert.AreEqual(inputValues, values);
}
/// <summary>
/// Checks whether the read only dictionary will throw an exception if its
/// Remove() method is called via the IDictionary interface
/// </summary>
[Test]
public void TestThrowOnRemoveViaIDictionary() {
Dictionary<int, string> numbers = createTestDictionary();
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
Assert.Throws<NotSupportedException>(
delegate() { (testDictionary as IDictionary).Remove(3); }
);
}
/// <summary>
/// Tests whether the retrieval of values using the indexer of the read only
/// dictionary is working via the IDictionary interface
/// </summary>
[Test]
public void TestRetrieveValueByIndexerViaIDictionary() {
Dictionary<int, string> numbers = createTestDictionary();
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
Assert.AreEqual("forty-two", (testDictionary as IDictionary)[42]);
}
/// <summary>
/// Checks whether the read only dictionary will throw an exception if its
/// indexer is used to insert an item via the IDictionary interface
/// </summary>
[Test]
public void TestThrowOnReplaceByIndexerViaIDictionary() {
Dictionary<int, string> numbers = createTestDictionary();
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
Assert.Throws<NotSupportedException>(
delegate() { (testDictionary as IDictionary)[24] = "twenty-four"; }
);
}
/// <summary>
/// Checks whether the read only dictionary will throw an exception if its
/// Add() method is used via the generic ICollection&lt;&gt; interface
/// </summary>
[Test]
public void TestThrowOnAddViaGenericICollection() {
Dictionary<int, string> numbers = createTestDictionary();
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
Assert.Throws<NotSupportedException>(
delegate() {
(testDictionary as ICollection<KeyValuePair<int, string>>).Add(
new KeyValuePair<int, string>(24, "twenty-four")
);
}
);
}
/// <summary>
/// Checks whether the read only dictionary will throw an exception if its
/// Clear() method is used via the generic ICollection&lt;&gt; interface
/// </summary>
[Test]
public void TestThrowOnClearViaGenericICollection() {
Dictionary<int, string> numbers = createTestDictionary();
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
Assert.Throws<NotSupportedException>(
delegate() { (testDictionary as ICollection<KeyValuePair<int, string>>).Clear(); }
);
}
/// <summary>
/// Checks whether the read only dictionary will throw an exception if its
/// Remove() method is used via the generic ICollection&lt;&gt; interface
/// </summary>
[Test]
public void TestThrowOnRemoveViaGenericICollection() {
Dictionary<int, string> numbers = createTestDictionary();
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
Assert.Throws<NotSupportedException>(
delegate() {
(testDictionary as ICollection<KeyValuePair<int, string>>).Remove(
new KeyValuePair<int, string>(42, "fourty-two")
);
}
);
}
/// <summary>
/// Verifies that the CopyTo() of the read only dictionary works when called
/// via the the ICollection interface
/// </summary>
[Test]
public void TestCopyToArrayViaICollection() {
Dictionary<int, string> numbers = createTestDictionary();
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
DictionaryEntry[] entries = new DictionaryEntry[numbers.Count];
(testDictionary as ICollection).CopyTo(entries, 0);
KeyValuePair<int, string>[] items = new KeyValuePair<int, string>[numbers.Count];
for(int index = 0; index < entries.Length; ++index) {
items[index] = new KeyValuePair<int, string>(
(int)entries[index].Key, (string)entries[index].Value
);
}
CollectionAssert.AreEquivalent(numbers, items);
}
/// <summary>
/// Verifies that the IsSynchronized property and the SyncRoot property are working
/// </summary>
[Test]
public void TestSynchronization() {
Dictionary<int, string> numbers = createTestDictionary();
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
if(!(testDictionary as ICollection).IsSynchronized) {
lock((testDictionary as ICollection).SyncRoot) {
Assert.AreEqual(numbers.Count, testDictionary.Count);
}
}
}
/// <summary>
/// Test whether the read only dictionary can be serialized
/// </summary>
[Test]
public void TestSerialization() {
BinaryFormatter formatter = new BinaryFormatter();
using(MemoryStream memory = new MemoryStream()) {
Dictionary<int, string> numbers = createTestDictionary();
ReadOnlyDictionary<int, string> testDictionary1 = makeReadOnly(numbers);
formatter.Serialize(memory, testDictionary1);
memory.Position = 0;
object testDictionary2 = formatter.Deserialize(memory);
CollectionAssert.AreEquivalent(testDictionary1, (IEnumerable)testDictionary2);
}
}
/// <summary>
/// Creates a new read-only dictionary filled with some values for testing
/// </summary>
/// <returns>The newly created read-only dictionary</returns>
private static Dictionary<int, string> createTestDictionary() {
Dictionary<int, string> numbers = new Dictionary<int, string>();
numbers.Add(1, "one");
numbers.Add(2, "two");
numbers.Add(3, "three");
numbers.Add(42, "forty-two");
return new Dictionary<int, string>(numbers);
}
/// <summary>
/// Creates a new read-only dictionary filled with some values for testing
/// </summary>
/// <returns>The newly created read-only dictionary</returns>
private static ReadOnlyDictionary<int, string> makeReadOnly(
IDictionary<int, string> dictionary
) {
return new ReadOnlyDictionary<int, string>(dictionary);
}
}
} // namespace Nuclex.Support.Collections
#endif // UNITTEST

View file

@ -0,0 +1,380 @@
#region Apache License 2.0
/*
Nuclex .NET Framework
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#endregion // Apache License 2.0
using System;
using System.Collections;
using System.Collections.Generic;
#if UNITTEST
using NUnit.Framework;
namespace Nuclex.Support.Collections {
/// <summary>Unit Test for the read only list wrapper</summary>
[TestFixture]
internal class ReadOnlyListTest {
/// <summary>
/// Verifies that the copy constructor of the read only list works
/// </summary>
[Test]
public void TestCopyConstructor() {
int[] integers = new int[] { 12, 34, 56, 78 };
ReadOnlyList<int> testList = new ReadOnlyList<int>(integers);
CollectionAssert.AreEqual(integers, testList);
}
/// <summary>Verifies that the IsReadOnly property returns true</summary>
[Test]
public void TestIsReadOnly() {
ReadOnlyList<int> testList = new ReadOnlyList<int>(new int[0]);
Assert.IsTrue(testList.IsReadOnly);
}
/// <summary>
/// Verifies that the CopyTo() of the read only list works
/// </summary>
[Test]
public void TestCopyToArray() {
int[] inputIntegers = new int[] { 12, 34, 56, 78 };
ReadOnlyList<int> testList = new ReadOnlyList<int>(inputIntegers);
int[] outputIntegers = new int[testList.Count];
testList.CopyTo(outputIntegers, 0);
CollectionAssert.AreEqual(inputIntegers, outputIntegers);
}
/// <summary>
/// Checks whether the Contains() method of the read only list is able to
/// determine if the list contains an item
/// </summary>
[Test]
public void TestContains() {
int[] integers = new int[] { 1234, 6789 };
ReadOnlyList<int> testList = new ReadOnlyList<int>(integers);
Assert.IsTrue(testList.Contains(1234));
Assert.IsFalse(testList.Contains(4321));
}
/// <summary>
/// Checks whether the IndexOf() method of the read only list is able to
/// determine if the index of an item in the list
/// </summary>
[Test]
public void TestIndexOf() {
int[] integers = new int[] { 12, 34, 67, 89 };
ReadOnlyList<int> testList = new ReadOnlyList<int>(integers);
Assert.AreEqual(0, testList.IndexOf(12));
Assert.AreEqual(1, testList.IndexOf(34));
Assert.AreEqual(2, testList.IndexOf(67));
Assert.AreEqual(3, testList.IndexOf(89));
}
/// <summary>
/// Checks whether the indexer method of the read only list is able to
/// retrieve items from the list
/// </summary>
[Test]
public void TestRetrieveByIndexer() {
int[] integers = new int[] { 12, 34, 67, 89 };
ReadOnlyList<int> testList = new ReadOnlyList<int>(integers);
Assert.AreEqual(12, testList[0]);
Assert.AreEqual(34, testList[1]);
Assert.AreEqual(67, testList[2]);
Assert.AreEqual(89, testList[3]);
}
/// <summary>
/// Checks whether the read only list will throw an exception if its Insert() method
/// is called via the generic IList&lt;&gt; interface
/// </summary>
[Test]
public void TestThrowOnInsertViaGenericIList() {
ReadOnlyList<int> testList = new ReadOnlyList<int>(new int[0]);
Assert.Throws<NotSupportedException>(
delegate() { (testList as IList<int>).Insert(0, 12345); }
);
}
/// <summary>
/// Checks whether the read only list will throw an exception if its RemoveAt() method
/// is called via the generic IList&lt;&gt; interface
/// </summary>
[Test]
public void TestThrowOnRemoveViaGenericIList() {
ReadOnlyList<int> testList = new ReadOnlyList<int>(new int[1]);
Assert.Throws<NotSupportedException>(
delegate() { (testList as IList<int>).RemoveAt(0); }
);
}
/// <summary>
/// Checks whether the indexer method of the read only list will throw an exception
/// if it is attempted to be used for replacing an item
/// </summary>
[Test]
public void TestRetrieveByIndexerViaGenericIList() {
int[] integers = new int[] { 12, 34, 67, 89 };
ReadOnlyList<int> testList = new ReadOnlyList<int>(integers);
Assert.AreEqual(12, (testList as IList<int>)[0]);
Assert.AreEqual(34, (testList as IList<int>)[1]);
Assert.AreEqual(67, (testList as IList<int>)[2]);
Assert.AreEqual(89, (testList as IList<int>)[3]);
}
/// <summary>
/// Checks whether the indexer method of the read only list will throw an exception
/// if it is attempted to be used for replacing an item
/// </summary>
[Test]
public void TestThrowOnReplaceByIndexerViaGenericIList() {
ReadOnlyList<int> testList = new ReadOnlyList<int>(new int[1]);
Assert.Throws<NotSupportedException>(
delegate() { (testList as IList<int>)[0] = 12345; }
);
}
/// <summary>
/// Checks whether the read only list will throw an exception if its Add() method
/// is called via the generic ICollection&lt;&gt; interface
/// </summary>
[Test]
public void TestThrowOnAddViaGenericICollection() {
ReadOnlyList<int> testList = new ReadOnlyList<int>(new int[0]);
Assert.Throws<NotSupportedException>(
delegate() { (testList as ICollection<int>).Add(12345); }
);
}
/// <summary>
/// Checks whether the read only list will throw an exception if its Clear() method
/// is called via the generic ICollection&lt;&gt; interface
/// </summary>
[Test]
public void TestThrowOnClearViaGenericICollection() {
ReadOnlyList<int> testList = new ReadOnlyList<int>(new int[1]);
Assert.Throws<NotSupportedException>(
delegate() { (testList as ICollection<int>).Clear(); }
);
}
/// <summary>
/// Checks whether the read only list will throw an exception if its Remove() method
/// is called via the generic ICollection&lt;&gt; interface
/// </summary>
[Test]
public void TestThrowOnRemoveViaGenericICollection() {
int[] integers = new int[] { 12, 34, 67, 89 };
ReadOnlyList<int> testList = new ReadOnlyList<int>(integers);
Assert.Throws<NotSupportedException>(
delegate() { (testList as ICollection<int>).Remove(89); }
);
}
/// <summary>
/// Tests whether the typesafe enumerator of the read only list is working
/// </summary>
[Test]
public void TestTypesafeEnumerator() {
int[] inputIntegers = new int[] { 12, 34, 56, 78 };
ReadOnlyList<int> testList = new ReadOnlyList<int>(inputIntegers);
List<int> outputIntegers = new List<int>();
foreach(int value in testList) {
outputIntegers.Add(value);
}
CollectionAssert.AreEqual(inputIntegers, outputIntegers);
}
/// <summary>
/// Checks whether the read only list will throw an exception if its Clear() method
/// is called via the IList interface
/// </summary>
[Test]
public void TestThrowOnClearViaIList() {
ReadOnlyList<int> testList = new ReadOnlyList<int>(new int[1]);
Assert.Throws<NotSupportedException>(
delegate() { (testList as IList).Clear(); }
);
}
/// <summary>
/// Checks whether the read only list will throw an exception if its Add() method
/// is called via the IList interface
/// </summary>
[Test]
public void TestThrowOnAddViaIList() {
ReadOnlyList<int> testList = new ReadOnlyList<int>(new int[0]);
Assert.Throws<NotSupportedException>(
delegate() { (testList as IList).Add(12345); }
);
}
/// <summary>
/// Checks whether the Contains() method of the read only list is able to
/// determine if the list contains an item
/// </summary>
[Test]
public void TestContainsViaIList() {
int[] integers = new int[] { 1234, 6789 };
ReadOnlyList<int> testList = new ReadOnlyList<int>(integers);
Assert.IsTrue((testList as IList).Contains(1234));
Assert.IsFalse((testList as IList).Contains(4321));
}
/// <summary>
/// Checks whether the IndexOf() method of the read only list is able to
/// determine if the index of an item in the list
/// </summary>
[Test]
public void TestIndexOfViaIList() {
int[] integers = new int[] { 12, 34, 67, 89 };
ReadOnlyList<int> testList = new ReadOnlyList<int>(integers);
Assert.AreEqual(0, (testList as IList).IndexOf(12));
Assert.AreEqual(1, (testList as IList).IndexOf(34));
Assert.AreEqual(2, (testList as IList).IndexOf(67));
Assert.AreEqual(3, (testList as IList).IndexOf(89));
}
/// <summary>
/// Checks whether the read only list will throw an exception if its Insert() method
/// is called via the IList interface
/// </summary>
[Test]
public void TestThrowOnInsertViaIList() {
ReadOnlyList<int> testList = new ReadOnlyList<int>(new int[0]);
Assert.Throws<NotSupportedException>(
delegate() { (testList as IList).Insert(0, 12345); }
);
}
/// <summary>
/// Checks whether the IsFixedSize property of the read only list returns the
/// expected result for a read only list based on a fixed array
/// </summary>
[Test]
public void TestIsFixedSizeViaIList() {
int[] integers = new int[] { 12, 34, 67, 89 };
ReadOnlyList<int> testList = new ReadOnlyList<int>(integers);
Assert.IsTrue((testList as IList).IsFixedSize);
}
/// <summary>
/// Checks whether the read only list will throw an exception if its Remove() method
/// is called via the IList interface
/// </summary>
[Test]
public void TestThrowOnRemoveViaIList() {
int[] integers = new int[] { 1234, 6789 };
ReadOnlyList<int> testList = new ReadOnlyList<int>(integers);
Assert.Throws<NotSupportedException>(
delegate() { (testList as IList).Remove(6789); }
);
}
/// <summary>
/// Checks whether the read only list will throw an exception if its Remove() method
/// is called via the IList interface
/// </summary>
[Test]
public void TestThrowOnRemoveAtViaIList() {
ReadOnlyList<int> testList = new ReadOnlyList<int>(new int[1]);
Assert.Throws<NotSupportedException>(
delegate() { (testList as IList).RemoveAt(0); }
);
}
/// <summary>
/// Checks whether the indexer method of the read only list will throw an exception
/// if it is attempted to be used for replacing an item
/// </summary>
[Test]
public void TestRetrieveByIndexerViaIList() {
int[] integers = new int[] { 12, 34, 67, 89 };
ReadOnlyList<int> testList = new ReadOnlyList<int>(integers);
Assert.AreEqual(12, (testList as IList)[0]);
Assert.AreEqual(34, (testList as IList)[1]);
Assert.AreEqual(67, (testList as IList)[2]);
Assert.AreEqual(89, (testList as IList)[3]);
}
/// <summary>
/// Checks whether the indexer method of the read only list will throw an exception
/// if it is attempted to be used for replacing an item
/// </summary>
[Test]
public void TestThrowOnReplaceByIndexerViaIList() {
ReadOnlyList<int> testList = new ReadOnlyList<int>(new int[1]);
Assert.Throws<NotSupportedException>(
delegate() { (testList as IList)[0] = 12345; }
);
}
/// <summary>
/// Verifies that the CopyTo() of the read only list works if invoked via
/// the ICollection interface
/// </summary>
[Test]
public void TestCopyToArrayViaICollection() {
int[] inputIntegers = new int[] { 12, 34, 56, 78 };
ReadOnlyList<int> testList = new ReadOnlyList<int>(inputIntegers);
int[] outputIntegers = new int[testList.Count];
(testList as ICollection).CopyTo(outputIntegers, 0);
CollectionAssert.AreEqual(inputIntegers, outputIntegers);
}
/// <summary>
/// Verifies that the IsSynchronized property and the SyncRoot property are working
/// </summary>
[Test]
public void TestSynchronization() {
ReadOnlyList<int> testList = new ReadOnlyList<int>(new int[0]);
if(!(testList as ICollection).IsSynchronized) {
lock((testList as ICollection).SyncRoot) {
Assert.AreEqual(0, testList.Count);
}
}
}
}
} // namespace Nuclex.Support.Collections
#endif // UNITTEST

View file

@ -0,0 +1,207 @@
#region Apache License 2.0
/*
Nuclex .NET Framework
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#endregion // Apache License 2.0
#if !NO_SETS
#if UNITTEST
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using NUnit.Framework;
using NMock;
namespace Nuclex.Support.Collections {
/// <summary>Unit Test for the observable set wrapper</summary>
[TestFixture]
internal class ReadOnlySetTest {
/// <summary>Called before each test is run</summary>
[SetUp]
public void Setup() {
this.set = new HashSet<int>();
this.readOnlySet = new ReadOnlySet<int>(this.set);
}
/// <summary>
/// Verifies that the observable set has a default constructor
/// </summary>
[Test]
public void HasDefaultConstructor() {
Assert.IsNotNull(new ReadOnlySet<int>(new HashSet<int>()));
}
/// <summary>
/// Verifies that an exception is thrown upon any attempt to add items
/// to a read-only set
/// </summary>
[Test]
public void AddingThrowsException() {
Assert.Throws<NotSupportedException>(
delegate() { ((ISet<int>)this.readOnlySet).Add(123); }
);
}
/// <summary>
/// Verifies that an exception is thrown upon any attempt to remove items
/// from a read-only set
/// </summary>
[Test]
public void RemovingThrowsException() {
Assert.Throws<NotSupportedException>(
delegate() { ((ISet<int>)this.readOnlySet).Remove(123); }
);
}
/// <summary>
/// Verifies that an exception is thrown upon any attempt to except
/// the set with another set
/// </summary>
[Test]
public void ExceptingThrowsException() {
Assert.Throws<NotSupportedException>(
delegate() { ((ISet<int>)this.readOnlySet).ExceptWith(null); }
);
}
/// <summary>
/// Verifies that an exception is thrown upon any attempt to intersect
/// the set with another set
/// </summary>
[Test]
public void InsersectThrowsException() {
Assert.Throws<NotSupportedException>(
delegate() { ((ISet<int>)this.readOnlySet).IntersectWith(null); }
);
}
/// <summary>
/// Verifies that it's possible to determine whether a set is a proper subset
/// or superset of another set
/// </summary>
[Test]
public void CanDetermineProperSubsetAndSuperset() {
this.set.Add(1);
this.set.Add(2);
this.set.Add(3);
var set2 = new HashSet<int>() { 1, 3 };
Assert.IsTrue(this.readOnlySet.IsProperSupersetOf(set2));
Assert.IsTrue(set2.IsProperSubsetOf(this.readOnlySet));
set2.Add(2);
Assert.IsFalse(this.readOnlySet.IsProperSupersetOf(set2));
Assert.IsFalse(set2.IsProperSubsetOf(this.readOnlySet));
}
/// <summary>
/// Verifies that it's possible to determine whether a set is a subset
/// or a superset of another set
/// </summary>
[Test]
public void CanDetermineSubsetAndSuperset() {
this.set.Add(1);
this.set.Add(2);
this.set.Add(3);
var set2 = new HashSet<int>() { 1, 2, 3 };
Assert.IsTrue(this.readOnlySet.IsSupersetOf(set2));
Assert.IsTrue(set2.IsSubsetOf(this.readOnlySet));
set2.Add(4);
Assert.IsFalse(this.readOnlySet.IsSupersetOf(set2));
Assert.IsFalse(set2.IsSubsetOf(this.readOnlySet));
}
/// <summary>
/// Verifies that a set can determine if another set overlaps with it
/// </summary>
[Test]
public void CanDetermineOverlap() {
this.set.Add(1);
this.set.Add(3);
this.set.Add(5);
var set2 = new HashSet<int>() { 3 };
Assert.IsTrue(this.readOnlySet.Overlaps(set2));
Assert.IsTrue(set2.Overlaps(this.readOnlySet));
}
/// <summary>
/// Verifies that a set can determine if another set contains the same elements
/// </summary>
[Test]
public void CanDetermineSetEquality() {
this.set.Add(1);
this.set.Add(3);
this.set.Add(5);
var set2 = new HashSet<int>() { 3, 1, 5 };
Assert.IsTrue(this.readOnlySet.SetEquals(set2));
Assert.IsTrue(set2.SetEquals(this.readOnlySet));
this.set.Add(7);
Assert.IsFalse(this.readOnlySet.SetEquals(set2));
Assert.IsFalse(set2.SetEquals(this.readOnlySet));
}
/// <summary>
/// Verifies that any attempt to symmetrically except a read-only set
/// causes an exception
/// </summary>
[Test]
public void SymmetricallyExceptingThrowsException() {
Assert.Throws<NotSupportedException>(
delegate() { ((ISet<int>)this.readOnlySet).SymmetricExceptWith(null); }
);
}
/// <summary>
/// Verifies that any attempt to union a read-only set causes an exception
/// </summary>
[Test]
public void UnioningThrowsException() {
Assert.Throws<NotSupportedException>(
delegate() { ((ISet<int>)this.readOnlySet).UnionWith(null); }
);
}
/// <summary>Set being wrapped in a read-only set</summary>
private ISet<int> set;
/// <summary>Read-only wrapper around the set</summary>
private ReadOnlySet<int> readOnlySet;
}
} // namespace Nuclex.Support.Collections
#endif // UNITTEST
#endif // !NO_SETS

View file

@ -0,0 +1,116 @@
#region Apache License 2.0
/*
Nuclex .NET Framework
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#endregion // Apache License 2.0
using System;
using System.Collections.Generic;
#if UNITTEST
using NUnit.Framework;
namespace Nuclex.Support.Collections {
/// <summary>Unit Test for the ReverseComparer helper class</summary>
[TestFixture]
internal class ReverseComparerTest {
#region class FortyTwoComparer
/// <summary>Special comparer in which 42 is larger than everything</summary>
private class FortyTwoComparer : IComparer<int> {
/// <summary>Compares the left value to the right value</summary>
/// <param name="left">Value on the left side</param>
/// <param name="right">Value on the right side</param>
/// <returns>The relationship of the two values</returns>
public int Compare(int left, int right) {
// Is there a 42 in the arguments?
if(left == 42) {
if(right == 42) {
return 0; // both are equal
} else {
return +1; // left is larger
}
} else if(right == 42) {
return -1; // right is larger
}
// No 42 encountered, proceed as normal
return Math.Sign(left - right);
}
}
#endregion // class FortyTwoComparer
/// <summary>
/// Tests whether the default constructor of the reverse comparer works
/// </summary>
[Test]
public void TestDefaultConstructor() {
new ReverseComparer<int>();
}
/// <summary>
/// Tests whether the full constructor of the reverse comparer works
/// </summary>
[Test]
public void TestFullConstructor() {
new ReverseComparer<int>(new FortyTwoComparer());
}
/// <summary>
/// Tests whether the full constructor of the reverse comparer works
/// </summary>
[Test]
public void TestReversedDefaultComparer() {
Comparer<int> comparer = Comparer<int>.Default;
ReverseComparer<int> reverseComparer = new ReverseComparer<int>(comparer);
Assert.Greater(0, comparer.Compare(10, 20));
Assert.Less(0, comparer.Compare(20, 10));
Assert.Less(0, reverseComparer.Compare(10, 20));
Assert.Greater(0, reverseComparer.Compare(20, 10));
}
/// <summary>
/// Tests whether the full constructor of the reverse comparer works
/// </summary>
[Test]
public void TestReversedCustomComparer() {
FortyTwoComparer fortyTwoComparer = new FortyTwoComparer();
ReverseComparer<int> reverseFortyTwoComparer = new ReverseComparer<int>(
fortyTwoComparer
);
Assert.Less(0, fortyTwoComparer.Compare(42, 84));
Assert.Greater(0, fortyTwoComparer.Compare(84, 42));
Assert.Greater(0, reverseFortyTwoComparer.Compare(42, 84));
Assert.Less(0, reverseFortyTwoComparer.Compare(84, 42));
}
}
} // namespace Nuclex.Support.Collections
#endif // UNITTEST

View file

@ -0,0 +1,119 @@
#region Apache License 2.0
/*
Nuclex .NET Framework
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#endregion // Apache License 2.0
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
#if UNITTEST
using NUnit.Framework;
namespace Nuclex.Support.Collections {
/// <summary>Unit Test for the SortableBindingList class</summary>
[TestFixture]
internal class SortableBindingListTest {
#region class TestRecord
/// <summary>Dummy record used to test the sortable binding list</summary>
private class TestRecord {
/// <summary>A property of type integer</summary>
public int IntegerValue { get; set; }
/// <summary>A property of type string</summary>
public string StringValue { get; set; }
/// <summary>A property of type float</summary>
public float FloatValue { get; set; }
}
#endregion // class TestRecord
/// <summary>Verifies that the sortable binding list is default constructible</summary>
[Test]
public void HasDefaultConstructor() {
Assert.DoesNotThrow(
delegate () { new SortableBindingList<TestRecord>(); }
);
}
/// <summary>
/// Tests whether the sortable binding list can copy an existing list
/// when being constructed
/// </summary>
[Test]
public void HasEnumerableConstructor() {
var items = new List<TestRecord>() {
new TestRecord() { IntegerValue = 123 },
new TestRecord() { IntegerValue = 456 }
};
var testList = new SortableBindingList<TestRecord>(items);
Assert.AreEqual(2, testList.Count);
Assert.AreSame(items[0], testList[0]);
Assert.AreSame(items[1], testList[1]);
}
/// <summary>Verifies that the sortable binding list supports sorting</summary>
[Test]
public void SupportsSorting() {
var testList = new SortableBindingList<TestRecord>();
IBindingList testListAsBindingList = testList;
Assert.IsTrue(testListAsBindingList.SupportsSorting);
}
/// <summary>
/// Tests whether the sortable binding list can sort its elements by different properties
/// </summary>
[Test]
public void CanSortItems() {
var items = new List<TestRecord>() {
new TestRecord() { IntegerValue = 456 },
new TestRecord() { IntegerValue = 789 },
new TestRecord() { IntegerValue = 123 }
};
var testList = new SortableBindingList<TestRecord>(items);
IBindingList testListAsBindingList = testList;
PropertyDescriptor integerValuePropertyDescriptor = (
TypeDescriptor.GetProperties(typeof(TestRecord))[nameof(TestRecord.IntegerValue)]
);
testListAsBindingList.ApplySort(
integerValuePropertyDescriptor, ListSortDirection.Ascending
);
Assert.AreEqual(3, testList.Count);
Assert.AreEqual(123, testList[0].IntegerValue);
Assert.AreEqual(456, testList[1].IntegerValue);
Assert.AreEqual(789, testList[2].IntegerValue);
}
}
} // namespace Nuclex.Support.Collections
#endif // UNITTEST

View file

@ -0,0 +1,500 @@
#region Apache License 2.0
/*
Nuclex .NET Framework
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#endregion // Apache License 2.0
#if !NO_NMOCK
using System;
using System.Collections;
using System.Collections.Generic;
#if UNITTEST
using NUnit.Framework;
using NMock;
namespace Nuclex.Support.Collections {
/// <summary>Unit Test for the transforming read only collection wrapper</summary>
[TestFixture]
internal class TransformingReadOnlyCollectionTest {
#region class StringTransformer
/// <summary>Test implementation of a transforming collection</summary>
private class StringTransformer : TransformingReadOnlyCollection<int, string> {
/// <summary>Initializes a new int-to-string transforming collection</summary>
/// <param name="items">Items the transforming collection will contain</param>
public StringTransformer(IList<int> items) : base(items) { }
/// <summary>Transforms an item into the exposed type</summary>
/// <param name="item">Item to be transformed</param>
/// <returns>The transformed item</returns>
/// <remarks>
/// This method is used to transform an item in the wrapped collection into
/// the exposed item type whenever the user accesses an item. Expect it to
/// be called frequently, because the TransformingReadOnlyCollection does
/// not cache or otherwise store the transformed items.
/// </remarks>
protected override string Transform(int item) {
if(item == 42) {
return null;
}
return item.ToString();
}
}
#endregion // class StringTransformer
/// <summary>
/// Verifies that the copy constructor of the transforming read only collection works
/// </summary>
[Test]
public void TestCopyConstructor() {
int[] integers = new int[] { 12, 34, 56, 78 };
StringTransformer testCollection = new StringTransformer(integers);
string[] strings = new string[] { "12", "34", "56", "78" };
CollectionAssert.AreEqual(strings, testCollection);
}
/// <summary>Verifies that the IsReadOnly property returns true</summary>
[Test]
public void TestIsReadOnly() {
StringTransformer testCollection = new StringTransformer(new int[0]);
Assert.IsTrue(testCollection.IsReadOnly);
}
/// <summary>
/// Verifies that the CopyTo() method of the transforming read only collection works
/// </summary>
[Test]
public void TestCopyToArray() {
int[] inputIntegers = new int[] { 12, 34, 56, 78 };
StringTransformer testCollection = new StringTransformer(inputIntegers);
string[] inputStrings = new string[] { "12", "34", "56", "78" };
string[] outputStrings = new string[testCollection.Count];
testCollection.CopyTo(outputStrings, 0);
CollectionAssert.AreEqual(inputStrings, outputStrings);
}
/// <summary>
/// Verifies that the CopyTo() method of the transforming read only collection throws
/// an exception if the target array is too small to hold the collection's contents
/// </summary>
[Test]
public void TestThrowOnCopyToTooSmallArray() {
int[] inputIntegers = new int[] { 12, 34, 56, 78 };
StringTransformer testCollection = new StringTransformer(inputIntegers);
string[] outputStrings = new string[testCollection.Count - 1];
Assert.Throws<ArgumentException>(
delegate() { testCollection.CopyTo(outputStrings, 0); }
);
}
/// <summary>
/// Checks whether the Contains() method of the transforming read only collection
/// is able to determine if the collection contains an item
/// </summary>
[Test]
public void TestContains() {
int[] integers = new int[] { 1234, 6789 };
StringTransformer testCollection = new StringTransformer(integers);
Assert.IsTrue(testCollection.Contains("1234"));
Assert.IsFalse(testCollection.Contains("4321"));
}
/// <summary>
/// Checks whether the IndexOf() method of the transforming read only collection
/// is able to determine if the index of an item in the collection
/// </summary>
[Test]
public void TestIndexOf() {
int[] integers = new int[] { 12, 34, 67, 89 };
StringTransformer testCollection = new StringTransformer(integers);
Assert.AreEqual(0, testCollection.IndexOf("12"));
Assert.AreEqual(1, testCollection.IndexOf("34"));
Assert.AreEqual(2, testCollection.IndexOf("67"));
Assert.AreEqual(3, testCollection.IndexOf("89"));
}
/// <summary>
/// Checks whether the IndexOf() method of the transforming read only collection
/// can cope with queries for 'null' when no 'null' item is contained on it
/// </summary>
[Test]
public void TestIndexOfWithNullItemNotContained() {
int[] integers = new int[] { 12, 34, 67, 89 };
StringTransformer testCollection = new StringTransformer(integers);
Assert.AreEqual(-1, testCollection.IndexOf(null));
}
/// <summary>
/// Checks whether the IndexOf() method of the transforming read only collection
/// can cope with queries for 'null' when a 'null' item is contained on it
/// </summary>
[Test]
public void TestIndexOfWithNullItemContained() {
int[] integers = new int[] { 12, 34, 67, 89, 42 };
StringTransformer testCollection = new StringTransformer(integers);
Assert.AreEqual(4, testCollection.IndexOf(null));
}
/// <summary>
/// Verifies that the Enumerator of the transforming read only collection correctly
/// implements the Reset() method
/// </summary>
[Test]
public void TestEnumeratorReset() {
int[] integers = new int[] { 1234, 6789 };
StringTransformer testCollection = new StringTransformer(integers);
IEnumerator<string> stringEnumerator = testCollection.GetEnumerator();
Assert.IsTrue(stringEnumerator.MoveNext());
Assert.IsTrue(stringEnumerator.MoveNext());
Assert.IsFalse(stringEnumerator.MoveNext());
stringEnumerator.Reset();
Assert.IsTrue(stringEnumerator.MoveNext());
Assert.IsTrue(stringEnumerator.MoveNext());
Assert.IsFalse(stringEnumerator.MoveNext());
}
/// <summary>
/// Checks whether the indexer method of the transforming read only collection
/// is able to retrieve items from the collection
/// </summary>
[Test]
public void TestRetrieveByIndexer() {
int[] integers = new int[] { 12, 34, 67, 89 };
StringTransformer testCollection = new StringTransformer(integers);
Assert.AreEqual("12", testCollection[0]);
Assert.AreEqual("34", testCollection[1]);
Assert.AreEqual("67", testCollection[2]);
Assert.AreEqual("89", testCollection[3]);
}
/// <summary>
/// Checks whether the transforming read only collection will throw an exception
/// if its Insert() method is called via the generic IList&lt;&gt; interface
/// </summary>
[Test]
public void TestThrowOnInsertViaGenericIList() {
StringTransformer testCollection = new StringTransformer(new int[0]);
Assert.Throws<NotSupportedException>(
delegate() { (testCollection as IList<string>).Insert(0, "12345"); }
);
}
/// <summary>
/// Checks whether the transforming read only collection will throw an exception
/// if its RemoveAt() method is called via the generic IList&lt;&gt; interface
/// </summary>
[Test]
public void TestThrowOnRemoveViaGenericIList() {
StringTransformer testCollection = new StringTransformer(new int[1]);
Assert.Throws<NotSupportedException>(
delegate() { (testCollection as IList<string>).RemoveAt(0); }
);
}
/// <summary>
/// Checks whether the indexer method of the transforming read only collection will
/// throw an exception if it is attempted to be used for replacing an item
/// </summary>
[Test]
public void TestRetrieveByIndexerViaGenericIList() {
int[] integers = new int[] { 12, 34, 67, 89 };
StringTransformer testCollection = new StringTransformer(integers);
Assert.AreEqual("12", (testCollection as IList<string>)[0]);
Assert.AreEqual("34", (testCollection as IList<string>)[1]);
Assert.AreEqual("67", (testCollection as IList<string>)[2]);
Assert.AreEqual("89", (testCollection as IList<string>)[3]);
}
/// <summary>
/// Checks whether the indexer method of the transforming read only collection
/// will throw an exception if it is attempted to be used for replacing an item
/// </summary>
[Test]
public void TestThrowOnReplaceByIndexerViaGenericIList() {
StringTransformer testCollection = new StringTransformer(new int[1]);
Assert.Throws<NotSupportedException>(
delegate() { (testCollection as IList<string>)[0] = "12345"; }
);
}
/// <summary>
/// Checks whether the transforming read only collection will throw an exception
/// if its Add() method is called via the generic ICollection&lt;&gt; interface
/// </summary>
[Test]
public void TestThrowOnAddViaGenericICollection() {
StringTransformer testCollection = new StringTransformer(new int[0]);
Assert.Throws<NotSupportedException>(
delegate() { (testCollection as ICollection<string>).Add("12345"); }
);
}
/// <summary>
/// Checks whether the transforming read only collection will throw an exception
/// if its Clear() method is called via the generic ICollection&lt;&gt; interface
/// </summary>
[Test]
public void TestThrowOnClearViaGenericICollection() {
StringTransformer testCollection = new StringTransformer(new int[1]);
Assert.Throws<NotSupportedException>(
delegate() { (testCollection as ICollection<string>).Clear(); }
);
}
/// <summary>
/// Checks whether the transforming read only collection will throw an exception
/// if its Remove() method is called via the generic ICollection&lt;&gt; interface
/// </summary>
[Test]
public void TestThrowOnRemoveViaGenericICollection() {
int[] integers = new int[] { 12, 34, 67, 89 };
StringTransformer testCollection = new StringTransformer(integers);
Assert.Throws<NotSupportedException>(
delegate() { (testCollection as ICollection<string>).Remove("89"); }
);
}
/// <summary>
/// Tests whether the typesafe enumerator of the read only collection is working
/// </summary>
[Test]
public void TestTypesafeEnumerator() {
int[] inputIntegers = new int[] { 12, 34, 56, 78 };
StringTransformer testCollection = new StringTransformer(inputIntegers);
List<string> outputStrings = new List<string>();
foreach(string value in testCollection) {
outputStrings.Add(value);
}
string[] inputStrings = new string[] { "12", "34", "56", "78" };
CollectionAssert.AreEqual(inputStrings, outputStrings);
}
/// <summary>
/// Checks whether the transforming read only collection will throw an exception
/// if its Clear() method is called via the IList interface
/// </summary>
[Test]
public void TestThrowOnClearViaIList() {
StringTransformer testCollection = new StringTransformer(new int[1]);
Assert.Throws<NotSupportedException>(
delegate() { (testCollection as IList).Clear(); }
);
}
/// <summary>
/// Checks whether the transforming read only collection will throw an exception
/// if its Add() method is called via the IList interface
/// </summary>
[Test]
public void TestThrowOnAddViaIList() {
StringTransformer testCollection = new StringTransformer(new int[0]);
Assert.Throws<NotSupportedException>(
delegate() { (testCollection as IList).Add("12345"); }
);
}
/// <summary>
/// Checks whether the Contains() method of the transforming read only collection
/// is able to determine if the collection contains an item
/// </summary>
[Test]
public void TestContainsViaIList() {
int[] integers = new int[] { 1234, 6789 };
StringTransformer testCollection = new StringTransformer(integers);
Assert.IsTrue((testCollection as IList).Contains("1234"));
Assert.IsFalse((testCollection as IList).Contains("4321"));
}
/// <summary>
/// Checks whether the IndexOf() method of the transforming read only collection
/// is able to determine if the index of an item in the collection
/// </summary>
[Test]
public void TestIndexOfViaIList() {
int[] integers = new int[] { 12, 34, 67, 89 };
StringTransformer testCollection = new StringTransformer(integers);
Assert.AreEqual(0, (testCollection as IList).IndexOf("12"));
Assert.AreEqual(1, (testCollection as IList).IndexOf("34"));
Assert.AreEqual(2, (testCollection as IList).IndexOf("67"));
Assert.AreEqual(3, (testCollection as IList).IndexOf("89"));
}
/// <summary>
/// Checks whether the transforming read only collection will throw an exception
/// if its Insert() method is called via the IList interface
/// </summary>
[Test]
public void TestThrowOnInsertViaIList() {
StringTransformer testCollection = new StringTransformer(new int[0]);
Assert.Throws<NotSupportedException>(
delegate() { (testCollection as IList).Insert(0, "12345"); }
);
}
/// <summary>
/// Checks whether the IsFixedSize property of the transforming read only collection
/// returns the expected result for a transforming read only collection based on
/// a fixed array
/// </summary>
[Test]
public void TestIsFixedSizeViaIList() {
int[] integers = new int[] { 12, 34, 67, 89 };
StringTransformer testCollection = new StringTransformer(integers);
Assert.IsTrue((testCollection as IList).IsFixedSize);
}
/// <summary>
/// Checks whether the transforming read only collection will throw an exception
/// if its Remove() method is called via the IList interface
/// </summary>
[Test]
public void TestThrowOnRemoveViaIList() {
int[] integers = new int[] { 1234, 6789 };
StringTransformer testCollection = new StringTransformer(integers);
Assert.Throws<NotSupportedException>(
delegate() { (testCollection as IList).Remove("6789"); }
);
}
/// <summary>
/// Checks whether the transforming read only collection will throw an exception
/// if its Remove() method is called via the IList interface
/// </summary>
[Test]
public void TestThrowOnRemoveAtViaIList() {
StringTransformer testCollection = new StringTransformer(new int[1]);
Assert.Throws<NotSupportedException>(
delegate() { (testCollection as IList).RemoveAt(0); }
);
}
/// <summary>
/// Checks whether the indexer method of the transforming read only collection
/// will throw an exception if it is attempted to be used for replacing an item
/// </summary>
[Test]
public void TestRetrieveByIndexerViaIList() {
int[] integers = new int[] { 12, 34, 67, 89 };
StringTransformer testCollection = new StringTransformer(integers);
Assert.AreEqual("12", (testCollection as IList)[0]);
Assert.AreEqual("34", (testCollection as IList)[1]);
Assert.AreEqual("67", (testCollection as IList)[2]);
Assert.AreEqual("89", (testCollection as IList)[3]);
}
/// <summary>
/// Checks whether the indexer method of the transforming read only collection
/// will throw an exception if it is attempted to be used for replacing an item
/// </summary>
[Test]
public void TestThrowOnReplaceByIndexerViaIList() {
StringTransformer testCollection = new StringTransformer(new int[1]);
Assert.Throws<NotSupportedException>(
delegate() { (testCollection as IList)[0] = "12345"; }
);
}
/// <summary>
/// Verifies that the CopyTo() method of the transforming read only collection
/// works if invoked via the ICollection interface
/// </summary>
[Test]
public void TestCopyToArrayViaICollection() {
int[] inputIntegers = new int[] { 12, 34, 56, 78 };
StringTransformer testCollection = new StringTransformer(inputIntegers);
string[] outputStrings = new string[testCollection.Count];
(testCollection as ICollection).CopyTo(outputStrings, 0);
string[] inputStrings = new string[] { "12", "34", "56", "78" };
CollectionAssert.AreEqual(inputStrings, outputStrings);
}
/// <summary>
/// Verifies that the IsSynchronized property and the SyncRoot property are working
/// </summary>
[Test]
public void TestSynchronization() {
StringTransformer testCollection = new StringTransformer(new int[0]);
if(!(testCollection as ICollection).IsSynchronized) {
lock((testCollection as ICollection).SyncRoot) {
Assert.AreEqual(0, testCollection.Count);
}
}
}
/// <summary>
/// Verifies that the IsSynchronized property and the SyncRoot property are working
/// on transforming read only collections based on IList&lt;&gt;s that do not
/// implement the ICollection interface
/// </summary>
[Test]
public void TestSynchronizationOfIListWithoutICollection() {
MockFactory mockery = new MockFactory();
Mock<IList<int>> mockedIList = mockery.CreateMock<IList<int>>();
StringTransformer testCollection = new StringTransformer(mockedIList.MockObject);
if(!(testCollection as ICollection).IsSynchronized) {
lock((testCollection as ICollection).SyncRoot) {
mockedIList.Expects.One.GetProperty(p => p.Count).WillReturn(12345);
int count = testCollection.Count;
Assert.AreEqual(12345, count); // ;-)
}
}
}
}
} // namespace Nuclex.Support.Collections
#endif // UNITTEST
#endif // !NO_NMOCK

View file

@ -0,0 +1,87 @@
#region Apache License 2.0
/*
Nuclex .NET Framework
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#endregion // Apache License 2.0
#if !NO_SETS
using System;
using System.Collections.Generic;
#if UNITTEST
using NUnit.Framework;
namespace Nuclex.Support.Collections {
/// <summary>Unit Test for the Variegator multi dictionary</summary>
[TestFixture]
internal class VariegatorTest {
/// <summary>
/// Tests whether the default constructor of the reverse comparer works
/// </summary>
[Test]
public void InstancesCanBeCreated() {
new Variegator<int, string>();
}
/// <summary>
/// Verifies that querying for a missing value leads to an exception being thrown
/// </summary>
[Test]
public void QueryingMissingValueThrowsException() {
var variegator = new Variegator<int, string>();
Assert.Throws<KeyNotFoundException>(
() => {
variegator.Get(123);
}
);
}
/// <summary>
/// Verifies that the variegator resolves ambiguous matches according to its design
/// </summary>
[Test]
public void AmbiguityResolvesToLeastRecentValue() {
var variegator = new Variegator<int, string>();
variegator.Add(1, "one");
variegator.Add(1, "eins");
string first = variegator.Get(1);
string second = variegator.Get(1);
// The variegator should have selected the first value by random and then
// returned the other value on the second query
Assert.AreNotEqual(first, second);
// Now the variegator should return the first value again because it is
// the least recently used value
Assert.AreEqual(first, variegator.Get(1));
// Repeating the query, the second should be returned again because now
// it has become the least recently used value
Assert.AreEqual(second, variegator.Get(1));
}
}
} // namespace Nuclex.Support.Collections
#endif // UNITTEST
#endif // !NO_SETS

View file

@ -0,0 +1,668 @@
#region Apache License 2.0
/*
Nuclex .NET Framework
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#endregion // Apache License 2.0
using System;
using System.Collections;
using System.Collections.Generic;
#if UNITTEST
using NUnit.Framework;
namespace Nuclex.Support.Collections {
/// <summary>Unit Test for the weak collection wrapper</summary>
[TestFixture]
internal class WeakCollectionTest {
#region class Dummy
/// <summary>Dummy class used to test the weakly referencing collection</summary>
private class Dummy {
/// <summary>Initializes a new dummy</summary>
/// <param name="value">Value that will be stored by the dummy</param>
public Dummy(int value) {
this.Value = value;
}
/// <summary>
/// Determines whether the specified System.Object is equal to
/// the current Dummy object.
/// </summary>
/// <param name="otherAsObject">
/// The System.Object to compare with the current Dummy object
/// </param>
/// <returns>
/// True if the specified System.Object is equal to the current Dummy object;
/// otherwise, false.
/// </returns>
public override bool Equals(object otherAsObject) {
Dummy other = otherAsObject as Dummy;
if(other == null) {
return false;
}
return this.Value.Equals(other.Value);
}
/// <summary>Serves as a hash function for a particular type.</summary>
/// <returns>A hash code for the current System.Object.</returns>
public override int GetHashCode() {
return this.Value.GetHashCode();
}
/// <summary>Some value that can be used for testing</summary>
public int Value;
}
#endregion // class Dummy
#region class ListWithoutICollection
private class ListWithoutICollection : IList<WeakReference<Dummy>> {
public int IndexOf(WeakReference<Dummy> item) { throw new NotImplementedException(); }
public void Insert(int index, WeakReference<Dummy> item) {
throw new NotImplementedException();
}
public void RemoveAt(int index) { throw new NotImplementedException(); }
public WeakReference<Dummy> this[int index] {
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }
}
public void Add(WeakReference<Dummy> item) { throw new NotImplementedException(); }
public void Clear() { throw new NotImplementedException(); }
public bool Contains(WeakReference<Dummy> item) { throw new NotImplementedException(); }
public void CopyTo(WeakReference<Dummy>[] array, int arrayIndex) {
throw new NotImplementedException();
}
public int Count { get { return 12345; } }
public bool IsReadOnly { get { throw new NotImplementedException(); } }
public bool Remove(WeakReference<Dummy> item) { throw new NotImplementedException(); }
public IEnumerator<WeakReference<Dummy>> GetEnumerator() {
throw new NotImplementedException();
}
IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); }
}
#endregion // class ListWithoutICollection
/// <summary>Verifies that the constructor of the weak collection is working</summary>
[Test]
public void TestConstructor() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Assert.IsNotNull(dummies);
}
/// <summary>
/// Test whether the non-typesafe Add() method of the weak collection works
/// </summary>
[Test]
public void TestAddAsObject() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Dummy oneTwoThreeDummy = new Dummy(12345);
(dummies as IList).Add((object)oneTwoThreeDummy);
CollectionAssert.Contains(dummies, oneTwoThreeDummy);
}
/// <summary>
/// Test whether the non-typesafe Add() method throws an exception if an object is
/// added that is not compatible to the collection's item type
/// </summary>
[Test]
public void TestThrowOnAddIncompatibleObject() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Assert.Throws<ArgumentException>(
delegate() { (dummies as IList).Add(new object()); }
);
}
/// <summary>
/// Test whether the generic Add() method of the weak collection works
/// </summary>
[Test]
public void TestAdd() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Dummy oneTwoThreeDummy = new Dummy(12345);
dummies.Add(oneTwoThreeDummy);
CollectionAssert.Contains(dummies, oneTwoThreeDummy);
}
/// <summary>Tests whether the Clear() method works</summary>
[Test]
public void TestClear() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Dummy oneTwoThreeDummy = new Dummy(12345);
dummies.Add(oneTwoThreeDummy);
Dummy threeTwoOneDummy = new Dummy(54321);
dummies.Add(threeTwoOneDummy);
Assert.AreEqual(2, dummies.Count);
dummies.Clear();
Assert.AreEqual(0, dummies.Count);
}
/// <summary>Tests whether the Contains() method works</summary>
[Test]
public void TestContains() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Dummy oneTwoThreeDummy = new Dummy(12345);
dummies.Add(oneTwoThreeDummy);
Dummy threeTwoOneDummy = new Dummy(54321);
Assert.IsTrue(dummies.Contains(oneTwoThreeDummy));
Assert.IsFalse(dummies.Contains(threeTwoOneDummy));
}
/// <summary>Tests whether the non-typesafe Contains() method works</summary>
[Test]
public void TestContainsWithObject() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Dummy oneTwoThreeDummy = new Dummy(12345);
dummies.Add(oneTwoThreeDummy);
Dummy threeTwoOneDummy = new Dummy(54321);
Assert.IsTrue((dummies as IList).Contains((object)oneTwoThreeDummy));
Assert.IsFalse((dummies as IList).Contains((object)threeTwoOneDummy));
}
/// <summary>
/// Verifies that the Enumerator of the dummy collection correctly
/// implements the Reset() method
/// </summary>
[Test]
public void TestEnumeratorReset() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Dummy oneTwoThreeDummy = new Dummy(123);
dummies.Add(oneTwoThreeDummy);
Dummy fourFiveSixDummy = new Dummy(456);
dummies.Add(fourFiveSixDummy);
IEnumerator<Dummy> dummyEnumerator = dummies.GetEnumerator();
Assert.IsTrue(dummyEnumerator.MoveNext());
Assert.IsTrue(dummyEnumerator.MoveNext());
Assert.IsFalse(dummyEnumerator.MoveNext());
dummyEnumerator.Reset();
Assert.IsTrue(dummyEnumerator.MoveNext());
Assert.IsTrue(dummyEnumerator.MoveNext());
Assert.IsFalse(dummyEnumerator.MoveNext());
}
/// <summary>Verifies that the IndexOf() method is working as intended</summary>
[Test]
public void TestIndexOf() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Dummy oneTwoThreeDummy = new Dummy(123);
dummies.Add(oneTwoThreeDummy);
Dummy fourFiveSixDummy = new Dummy(456);
dummies.Add(fourFiveSixDummy);
Dummy sevenEightNineDummy = new Dummy(789);
Assert.AreEqual(0, dummies.IndexOf(oneTwoThreeDummy));
Assert.AreEqual(1, dummies.IndexOf(fourFiveSixDummy));
Assert.AreEqual(-1, dummies.IndexOf(sevenEightNineDummy));
}
/// <summary>
/// Verifies that the non-typesafe IndexOf() method is working as intended
/// </summary>
[Test]
public void TestIndexOfWithObject() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Dummy oneTwoThreeDummy = new Dummy(123);
dummies.Add(oneTwoThreeDummy);
Dummy fourFiveSixDummy = new Dummy(456);
dummies.Add(fourFiveSixDummy);
Dummy sevenEightNineDummy = new Dummy(789);
Assert.AreEqual(0, (dummies as IList).IndexOf((object)oneTwoThreeDummy));
Assert.AreEqual(1, (dummies as IList).IndexOf((object)fourFiveSixDummy));
Assert.AreEqual(-1, (dummies as IList).IndexOf((object)sevenEightNineDummy));
}
/// <summary>
/// Verifies that an exception is thrown if an incompatible object is passed to
/// the non-typesafe variant of the IndexOf() method
/// </summary>
[Test]
public void TestThrowOnIndexOfWithIncompatibleObject() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Assert.Throws<ArgumentException>(
delegate() { Assert.IsNull((dummies as IList).IndexOf(new object())); }
);
}
/// <summary>Test whether the IndexOf() method can cope with null references</summary>
[Test]
public void TestIndexOfNull() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Assert.AreEqual(-1, dummies.IndexOf(null));
dummies.Add(null);
Assert.AreEqual(0, dummies.IndexOf(null));
}
/// <summary>
/// Verifies that the CopyTo() method of the weak collection works
/// </summary>
[Test]
public void TestCopyToArray() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Dummy oneTwoThreeDummy = new Dummy(123);
dummies.Add(oneTwoThreeDummy);
Dummy fourFiveSixDummy = new Dummy(456);
dummies.Add(fourFiveSixDummy);
Dummy[] inputDummies = new Dummy[] { oneTwoThreeDummy, fourFiveSixDummy };
Dummy[] outputDummies = new Dummy[dummies.Count];
dummies.CopyTo(outputDummies, 0);
CollectionAssert.AreEqual(inputDummies, outputDummies);
}
/// <summary>
/// Verifies that the CopyTo() method of the weak collection throws an exception
/// if the target array is too small to hold the collection's contents
/// </summary>
[Test]
public void TestThrowOnCopyToTooSmallArray() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Dummy oneTwoThreeDummy = new Dummy(123);
dummies.Add(oneTwoThreeDummy);
Dummy fourFiveSixDummy = new Dummy(456);
dummies.Add(fourFiveSixDummy);
Dummy[] outputStrings = new Dummy[dummies.Count - 1];
Assert.Throws<ArgumentException>(
delegate() { dummies.CopyTo(outputStrings, 0); }
);
}
/// <summary>
/// Verifies that the CopyTo() method of the transforming read only collection
/// works if invoked via the ICollection interface
/// </summary>
[Test]
public void TestCopyToArrayViaICollection() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Dummy oneTwoThreeDummy = new Dummy(123);
dummies.Add(oneTwoThreeDummy);
Dummy fourFiveSixDummy = new Dummy(456);
dummies.Add(fourFiveSixDummy);
Dummy[] inputDummies = new Dummy[] { oneTwoThreeDummy, fourFiveSixDummy };
Dummy[] outputDummies = new Dummy[dummies.Count];
(dummies as ICollection).CopyTo(outputDummies, 0);
CollectionAssert.AreEqual(inputDummies, outputDummies);
}
/// <summary>
/// Verifies that the Insert() method correctly shifts items in the collection
/// </summary>
[Test]
public void TestInsert() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Dummy oneTwoThreeDummy = new Dummy(123);
dummies.Add(oneTwoThreeDummy);
Dummy fourFiveSixDummy = new Dummy(456);
dummies.Insert(0, fourFiveSixDummy);
Assert.AreEqual(2, dummies.Count);
Assert.AreSame(fourFiveSixDummy, dummies[0]);
Assert.AreSame(oneTwoThreeDummy, dummies[1]);
}
/// <summary>
/// Verifies that the non-typesafe Insert() method correctly shifts items in
/// the collection
/// </summary>
[Test]
public void TestInsertObject() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Dummy oneTwoThreeDummy = new Dummy(123);
dummies.Add(oneTwoThreeDummy);
Dummy fourFiveSixDummy = new Dummy(456);
(dummies as IList).Insert(0, (object)fourFiveSixDummy);
Assert.AreEqual(2, dummies.Count);
Assert.AreSame(fourFiveSixDummy, dummies[0]);
Assert.AreSame(oneTwoThreeDummy, dummies[1]);
}
/// <summary>
/// Verifies that the non-typesafe Insert() method correctly shifts items in
/// the collection
/// </summary>
[Test]
public void TestThrowOnInsertIncompatibleObject() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Dummy oneTwoThreeDummy = new Dummy(123);
dummies.Add(oneTwoThreeDummy);
Assert.Throws<ArgumentException>(
delegate() { (dummies as IList).Insert(0, new object()); }
);
}
/// <summary>
/// Checks whether the IsFixedSize property of the weak collection returns
/// the expected result for a weak collection based on a fixed array
/// </summary>
[Test]
public void TestIsFixedSizeViaIList() {
Dummy oneTwoThreeDummy = new Dummy(123);
Dummy fourFiveSixDummy = new Dummy(456);
WeakReference<Dummy>[] dummyReferences = new WeakReference<Dummy>[] {
new WeakReference<Dummy>(oneTwoThreeDummy),
new WeakReference<Dummy>(fourFiveSixDummy)
};
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(dummyReferences);
Assert.IsTrue((dummies as IList).IsFixedSize);
}
/// <summary>
/// Tests whether the IsReadOnly property of the weak collection works
/// </summary>
[Test]
public void TestIsReadOnly() {
Dummy oneTwoThreeDummy = new Dummy(123);
Dummy fourFiveSixDummy = new Dummy(456);
List<WeakReference<Dummy>> dummyReferences = new List<WeakReference<Dummy>>();
dummyReferences.Add(new WeakReference<Dummy>(oneTwoThreeDummy));
dummyReferences.Add(new WeakReference<Dummy>(fourFiveSixDummy));
ReadOnlyList<WeakReference<Dummy>> readOnlyDummyReferences =
new ReadOnlyList<WeakReference<Dummy>>(dummyReferences);
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(dummyReferences);
WeakCollection<Dummy> readOnlydummies = new WeakCollection<Dummy>(
readOnlyDummyReferences
);
Assert.IsFalse(dummies.IsReadOnly);
Assert.IsTrue(readOnlydummies.IsReadOnly);
}
/// <summary>
/// Tests whether the IsSynchronized property of the weak collection works
/// </summary>
[Test]
public void TestIsSynchronized() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Assert.IsFalse((dummies as IList).IsSynchronized);
}
/// <summary>Tests the indexer of the weak collection</summary>
[Test]
public void TestIndexer() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Dummy oneTwoThreeDummy = new Dummy(123);
dummies.Add(oneTwoThreeDummy);
Dummy fourFiveSixDummy = new Dummy(456);
dummies.Add(fourFiveSixDummy);
Assert.AreSame(oneTwoThreeDummy, dummies[0]);
Assert.AreSame(fourFiveSixDummy, dummies[1]);
dummies[0] = fourFiveSixDummy;
Assert.AreSame(fourFiveSixDummy, dummies[0]);
}
/// <summary>Tests the non-typesafe indexer of the weak collection</summary>
[Test]
public void TestIndexerWithObject() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Dummy oneTwoThreeDummy = new Dummy(123);
dummies.Add(oneTwoThreeDummy);
Dummy fourFiveSixDummy = new Dummy(456);
dummies.Add(fourFiveSixDummy);
Assert.AreSame((object)oneTwoThreeDummy, (dummies as IList)[0]);
Assert.AreSame((object)fourFiveSixDummy, (dummies as IList)[1]);
(dummies as IList)[0] = (object)fourFiveSixDummy;
Assert.AreSame((object)fourFiveSixDummy, (dummies as IList)[0]);
}
/// <summary>
/// Tests whether the non-typesafe indexer of the weak collection throws
/// the correct exception if an incompatible object is assigned
/// </summary>
[Test]
public void TestThrowOnIndexerWithIncompatibleObject() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Dummy oneTwoThreeDummy = new Dummy(123);
dummies.Add(oneTwoThreeDummy);
Assert.Throws<ArgumentException>(
delegate() { (dummies as IList)[0] = new object(); }
);
}
/// <summary>Tests the Remove() method of the weak collection</summary>
[Test]
public void TestRemove() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Dummy oneTwoThreeDummy = new Dummy(123);
dummies.Add(oneTwoThreeDummy);
Dummy fourFiveSixDummy = new Dummy(456);
dummies.Add(fourFiveSixDummy);
Assert.AreEqual(2, dummies.Count);
Assert.IsTrue(dummies.Remove(oneTwoThreeDummy));
Assert.AreEqual(1, dummies.Count);
Assert.IsFalse(dummies.Remove(oneTwoThreeDummy));
}
/// <summary>Tests the non-typesafe Remove() method of the weak collection</summary>
[Test]
public void TestRemoveObject() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Dummy oneTwoThreeDummy = new Dummy(123);
dummies.Add(oneTwoThreeDummy);
Dummy fourFiveSixDummy = new Dummy(456);
dummies.Add(fourFiveSixDummy);
Assert.AreEqual(2, dummies.Count);
(dummies as IList).Remove((object)oneTwoThreeDummy);
Assert.AreEqual(1, dummies.Count);
}
/// <summary>
/// Tests whether a null object can be managed by and removed from the weak collection
/// </summary>
[Test]
public void TestRemoveNull() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
dummies.Add(null);
Assert.AreEqual(1, dummies.Count);
Assert.IsTrue(dummies.Remove(null));
Assert.AreEqual(0, dummies.Count);
}
/// <summary>
/// Tests whether the non-typesafe Remove() method of the weak collection throws
/// an exception if an object is tried to be removed that is incompatible with
/// the collection's item type
/// </summary>
[Test]
public void TestThrowOnRemoveIncompatibleObject() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Assert.Throws<ArgumentException>(
delegate() { (dummies as IList).Remove(new object()); }
);
}
/// <summary>Tests the RemoveAt() method of the weak collection</summary>
[Test]
public void TestRemoveAt() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
Dummy oneTwoThreeDummy = new Dummy(123);
dummies.Add(oneTwoThreeDummy);
Dummy fourFiveSixDummy = new Dummy(456);
dummies.Add(fourFiveSixDummy);
Assert.AreSame(oneTwoThreeDummy, dummies[0]);
dummies.RemoveAt(0);
Assert.AreSame(fourFiveSixDummy, dummies[0]);
}
/// <summary>
/// Verifies that the IsSynchronized property and the SyncRoot property are working
/// </summary>
[Test]
public void TestSynchronization() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new List<WeakReference<Dummy>>()
);
if(!(dummies as ICollection).IsSynchronized) {
lock((dummies as ICollection).SyncRoot) {
Assert.AreEqual(0, dummies.Count);
}
}
}
/// <summary>
/// Verifies that the IsSynchronized property and the SyncRoot property are working
/// on transforming read only collections based on IList&lt;&gt;s that do not
/// implement the ICollection interface
/// </summary>
[Test]
public void TestSynchronizationOfIListWithoutICollection() {
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
new ListWithoutICollection()
);
if(!(dummies as ICollection).IsSynchronized) {
lock((dummies as ICollection).SyncRoot) {
int count = dummies.Count;
Assert.AreEqual(12345, count); // ;-)
}
}
}
/// <summary>Tests the RemoveDeadItems() method</summary>
[Test]
public void TestRemoveDeadItems() {
List<WeakReference<Dummy>> dummyReferences = new List<WeakReference<Dummy>>();
Dummy oneTwoThreeDummy = new Dummy(123);
dummyReferences.Add(new WeakReference<Dummy>(oneTwoThreeDummy));
dummyReferences.Add(new WeakReference<Dummy>(null));
Dummy fourFiveSixDummy = new Dummy(456);
dummyReferences.Add(new WeakReference<Dummy>(fourFiveSixDummy));
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(dummyReferences);
Assert.AreEqual(3, dummies.Count);
dummies.RemoveDeadItems();
Assert.AreEqual(2, dummies.Count);
Assert.AreSame(oneTwoThreeDummy, dummies[0]);
Assert.AreSame(fourFiveSixDummy, dummies[1]);
}
}
} // namespace Nuclex.Support.Collections
#endif // UNITTEST