#region CPL License /* Nuclex Framework Copyright (C) 2002-2012 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #endregion #if UNITTEST using System; using System.Collections; using System.Collections.Generic; using NUnit.Framework; namespace Nuclex.Support.Collections { /// Unit Test for the double ended queue [TestFixture] internal class DequeTest { /// Verifies that the AddLast() method of the deque is working [Test] public void TestAddLast() { Deque intDeque = new Deque(16); for(int item = 0; item < 48; ++item) { intDeque.AddLast(item); } for(int item = 0; item < 48; ++item) { Assert.AreEqual(item, intDeque[item]); } } /// Verifies that the AddFirst() method of the deque is working [Test] public void TestAddFirst() { Deque intDeque = new Deque(16); for(int item = 0; item < 48; ++item) { intDeque.AddFirst(item); } for(int item = 0; item < 48; ++item) { Assert.AreEqual(47 - item, intDeque[item]); } } /// /// Verifies that the RemoveFirst() method of the deque is working /// [Test] public void TestRemoveFirst() { Deque intDeque = new Deque(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(); } } /// /// Verifies that the RemoveLast() method of the deque is working /// [Test] public void TestRemoveLast() { Deque intDeque = new Deque(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(); } } /// Verifies that the Insert() method works in all cases /// /// 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 /// [Test] public void TestInsert() { for(int testedIndex = 0; testedIndex <= 96; ++testedIndex) { Deque 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]); } } } /// /// Verifies that the Insert() method works in all cases when the deque doesn't /// start at a block boundary /// [Test] public void TestInsertNonNormalized() { for(int testedIndex = 0; testedIndex <= 96; ++testedIndex) { Deque 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]); } } } /// Verifies the the RemoveAt() method works in all cases [Test] public void TestRemoveAt() { for(int testedIndex = 0; testedIndex < 96; ++testedIndex) { Deque intDeque = new Deque(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]); } } } /// /// Verifies the the RemoveAt() method works in all cases when the deque doesn't /// start at a block boundary /// [Test] public void TestRemoveAtNonNormalized() { for(int testedIndex = 0; testedIndex < 96; ++testedIndex) { Deque intDeque = new Deque(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]); } } } /// /// 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 /// [Test] public void TestRemoveAtEmptiesLeftBlock() { Deque intDeque = new Deque(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]); } } /// /// 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 /// [Test] public void TestRemoveAtEmptiesRightBlock() { Deque intDeque = new Deque(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]); } } /// /// Validates that an exception is thrown if the 'First' property is accessed /// in an empty deque /// [Test] public void TestThrowOnAccessFirstInEmptyDeque() { Deque intDeque = new Deque(); Assert.Throws( delegate() { Console.WriteLine(intDeque.First); } ); } /// /// Validates that an exception is thrown if the 'Last' property is accessed /// in an empty deque /// [Test] public void TestThrowOnAccessLastInEmptyDeque() { Deque intDeque = new Deque(); Assert.Throws( delegate() { Console.WriteLine(intDeque.Last); } ); } /// /// Validates that an exception is thrown if the first item is attempted to be /// removed from an empty deque /// [Test] public void TestThrowOnRemoveFirstFromEmptyDeque() { Deque intDeque = new Deque(); Assert.Throws( delegate() { intDeque.RemoveFirst(); } ); } /// /// Validates that an exception is thrown if the last item is attempted to be /// removed from an empty deque /// [Test] public void TestThrowOnRemoveLastFromEmptyDeque() { Deque intDeque = new Deque(); Assert.Throws( delegate() { intDeque.RemoveLast(); } ); } /// /// Verifies that items can be assigned by their index /// [Test] public void TestIndexAssignment() { Deque 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); } /// /// Verifies that an exception is thrown if an invalid index is accessed /// [Test] public void TestThrowOnInvalidIndex() { Deque intDeque = new Deque(16); for(int item = 0; item < 32; ++item) { intDeque.AddLast(item); } Assert.Throws( delegate() { Console.WriteLine(intDeque[32]); } ); } /// Tests the IndexOf() method [Test, TestCase(0), TestCase(16), TestCase(32), TestCase(48)] public void TestIndexOf(int count) { Deque intDeque = new Deque(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)); } /// /// Tests the IndexOf() method with the deque not starting at a block boundary /// [Test, TestCase(0), TestCase(16), TestCase(32), TestCase(48)] public void TestIndexOfNonNormalized(int count) { Deque intDeque = createNonNormalizedDeque(count); for(int item = 0; item < count; ++item) { Assert.AreEqual(item, intDeque.IndexOf(item)); } Assert.AreEqual(-1, intDeque.IndexOf(count)); } /// Verifies that the deque's enumerator works [Test] public void TestEnumerator() { Deque intDeque = createNonNormalizedDeque(40); for(int testRun = 0; testRun < 2; ++testRun) { using(IEnumerator 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(); } } } /// Verifies that the deque's enumerator works [Test] public void TestObjectEnumerator() { Deque 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(); } } /// /// Verifies that an exception is thrown if the enumerator is accessed in /// an invalid position /// [Test] public void TestThrowOnInvalidEnumeratorPosition() { Deque intDeque = createNonNormalizedDeque(40); using(IEnumerator enumerator = intDeque.GetEnumerator()) { Assert.Throws( delegate() { Console.WriteLine(enumerator.Current); } ); while(enumerator.MoveNext()) { } Assert.Throws( delegate() { Console.WriteLine(enumerator.Current); } ); } } /// Tests whether a small deque can be cleared [Test] public void TestClearSmallDeque() { Deque intDeque = createDeque(12); intDeque.Clear(); Assert.AreEqual(0, intDeque.Count); } /// Tests whether a large deque can be cleared [Test] public void TestClearLargeDeque() { Deque intDeque = createDeque(40); intDeque.Clear(); Assert.AreEqual(0, intDeque.Count); } /// Verifies that the non-typesafe Add() method is working [Test] public void TestAddObject() { Deque intDeque = new Deque(); Assert.AreEqual(0, ((IList)intDeque).Add(123)); Assert.AreEqual(1, intDeque.Count); } /// /// Tests whether an exception is thrown if the non-typesafe Add() method is /// used to add an incompatible object into the deque /// [Test] public void TestThrowOnAddIncompatibleObject() { Deque intDeque = new Deque(); Assert.Throws( delegate() { ((IList)intDeque).Add("Hello World"); } ); } /// Verifies that the Add() method is working [Test] public void TestAdd() { Deque intDeque = new Deque(); ((IList)intDeque).Add(123); Assert.AreEqual(1, intDeque.Count); } /// Tests whether the Contains() method is working [Test] public void TestContains() { Deque intDeque = createDeque(16); Assert.IsTrue(intDeque.Contains(14)); Assert.IsFalse(intDeque.Contains(16)); } /// Tests the non-typesafe Contains() method [Test] public void TestContainsObject() { Deque intDeque = createDeque(16); Assert.IsTrue(((IList)intDeque).Contains(14)); Assert.IsFalse(((IList)intDeque).Contains(16)); Assert.IsFalse(((IList)intDeque).Contains("Hello World")); } /// Tests the non-typesafe Contains() method [Test] public void TestIndexOfObject() { Deque 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")); } /// Tests wether the non-typesafe Insert() method is working [Test] public void TestInsertObject() { for(int testedIndex = 0; testedIndex <= 96; ++testedIndex) { Deque 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]); } } } /// /// Verifies that an exception is thrown if an incompatible object is inserted /// into the deque /// [Test] public void TestThrowOnInsertIncompatibleObject() { Deque intDeque = createDeque(12); Assert.Throws( delegate() { ((IList)intDeque).Insert(8, "Hello World"); } ); } /// Validates that the IsFixedObject property is set to false [Test] public void TestIsFixedObject() { Deque intDeque = new Deque(); Assert.IsFalse(((IList)intDeque).IsFixedSize); } /// Validates that the IsSynchronized property is set to false [Test] public void TestIsSynchronized() { Deque intDeque = new Deque(); Assert.IsFalse(((IList)intDeque).IsSynchronized); } /// /// Verifies that items can be assigned by their index using the non-typesafe /// IList interface /// [Test] public void TestObjectIndexAssignment() { Deque intDeque = createDeque(32); ((IList)intDeque)[16] = 12345; ((IList)intDeque)[17] = 54321; Assert.AreEqual(12345, ((IList)intDeque)[16]); Assert.AreEqual(54321, ((IList)intDeque)[17]); } /// /// Tests whether an exception is thrown if an incompatible object is assigned /// to the deque /// [Test] public void TestIncompatibleObjectIndexAssignment() { Deque intDeque = createDeque(2); Assert.Throws( delegate() { ((IList)intDeque)[0] = "Hello World"; } ); } /// Verifies that the Remove() method is working correctly [Test] public void TestRemove() { Deque intDeque = createDeque(16); Assert.AreEqual(16, intDeque.Count); Assert.IsTrue(intDeque.Remove(13)); Assert.IsFalse(intDeque.Remove(13)); Assert.AreEqual(15, intDeque.Count); } /// Tests the non-typesafe remove method [Test] public void TestRemoveObject() { Deque 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); } /// /// Tests the non-typesafe remove method used to remove an incompatible object /// [Test] public void TestRemoveIncompatibleObject() { Deque intDeque = createDeque(10); ((IList)intDeque).Remove("Hello World"); // should simply do nothing Assert.AreEqual(10, intDeque.Count); } /// /// Verifies that the IsSynchronized property and the SyncRoot property are working /// [Test] public void TestSynchronization() { Deque intDeque = new Deque(); if(!(intDeque as ICollection).IsSynchronized) { lock((intDeque as ICollection).SyncRoot) { Assert.AreEqual(0, intDeque.Count); } } } /// /// Validates that the IsReadOnly property of the deque returns false /// [Test] public void TestIsReadOnly() { Deque intDeque = new Deque(); Assert.IsFalse(((IList)intDeque).IsReadOnly); Assert.IsFalse(((ICollection)intDeque).IsReadOnly); } /// Tests the non-typesafe CopyTo() method [Test] public void TestCopyToObjectArray() { Deque intDeque = createNonNormalizedDeque(40); int[] array = new int[40]; ((ICollection)intDeque).CopyTo(array, 0); Assert.AreEqual(intDeque, array); } /// Tests the CopyTo() method [Test] public void TestCopyToArray() { Deque 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 ); } /// /// Verifies that the non-typesafe CopyTo() method throws an exception if /// the array is of an incompatible type /// [Test] public void TestThrowOnCopyToIncompatibleObjectArray() { Deque intDeque = createDeque(4); short[] array = new short[4]; Assert.Throws( delegate() { ((ICollection)intDeque).CopyTo(array, 4); } ); } /// /// Verifies that the CopyTo() method throws an exception if the target array /// is too small /// [Test] public void TestThrowOnCopyToTooSmallArray() { Deque intDeque = createDeque(8); Assert.Throws( delegate() { intDeque.CopyTo(new int[7], 0); } ); } #if DEBUG /// /// Tests whether the deque enumerator detects when it runs out of sync /// [Test] public void TestInvalidatedEnumeratorDetection() { Deque intDeque = createDeque(8); using(IEnumerator enumerator = intDeque.GetEnumerator()) { Assert.IsTrue(enumerator.MoveNext()); intDeque.AddFirst(12345); Assert.Throws( delegate() { enumerator.MoveNext(); } ); } } #endif /// /// Creates a deque whose first element does not coincide with a block boundary /// /// Number of items the deque will be filled with /// The newly created deque private static Deque createNonNormalizedDeque(int count) { Deque intDeque = new Deque(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; } /// Creates a deque filled with the specified number of items /// /// Number of items the deque will be filled with /// The newly created deque private static Deque createDeque(int count) { Deque intDeque = new Deque(16); for(int item = 0; item < count; ++item) { intDeque.AddLast(item); } return intDeque; } } } // namespace Nuclex.Support.Collections #endif // UNITTEST