using System.Linq; using NUnit.Framework; namespace System.Collections.Generic { /// /// A reusable test fixture base class for testing ICollection{T} types. This /// helper class also implements the EnumerationTest{T} class to test /// the IEnumeration{T} portion of the ICollection{T} being tested." /// /// The type held by the collection. public abstract class CollectionTests : EnumerationTests { /// /// Initializes a new instance of the /// class. /// /// True if the order of iteration by /// the should be tested, false if otherwise. /// If you specify true, be sure to implement the /// property. protected CollectionTests(bool testIterationOrder) : this(testIterationOrder, null) { } /// /// Initializes a new instance of the /// class. /// /// True if the order of iteration by /// the should be tested, false if otherwise. /// If you specify true, be sure to implement the /// property. /// The that /// is used to compare values in the for /// uniqueness or null if the default /// specified by is to be used. protected CollectionTests(bool testIterationOrder, IEqualityComparer comparer) : base(testIterationOrder, comparer) { } [Test] [Description("Tests read-only classes. Checks if an NotSupportedException " + "is thrown if an object is added. If the ICollection is not read " + "only, then the test always passes.")] public void NotSupportedExceptionWhenAddingToReadOnlyCollection() { ICollection collection = CreateReadOnlyCollection(); if (collection == null) Assert.Ignore("The collection is NOT read only, so no testing " + "needs to be done."); CheckCollectionCount(collection); var item = CreateItem(); Assert.Throws(() => collection.Add(item)); } [Test] [Description("Tests read-only classes. Checks if an NotSupportedException " + "is thrown when Clear() is called on a collection. If the ICollection " + "is not read only, then the test always passes.")] public void NotSupportedExceptionWhenClearingAReadOnlyCollection() { ICollection collection = CreateReadOnlyCollection(); if (collection == null) Assert.Ignore("The collection is NOT read only, so no testing " + "needs to be done."); CheckCollectionCount(collection); Assert.Throws(() => collection.Clear()); } [Test] [Description("Tests read-only classes. Checks if an NotSupportedException " + "is thrown when Remove() is called on a collection. If the ICollection " + "is not read only, then the test always passes.")] public void NotSupportedExceptionWhenRemovingFromAReadOnlyCollection() { ICollection collection = CreateReadOnlyCollection(); if (collection == null) Assert.Ignore("The collection is NOT read only, so no testing " + "needs to be done."); CheckCollectionCount(collection); var item = collection.First(); Assert.Throws(() => collection.Remove(item)); } [Test] [Description("Enumerates through the ICollection and checks if the " + "Contains(T) method properly returns true for each item in the list.")] public void ContainsWorksProperly() { ICollection collection = CreateCollection(); CheckCollectionCount(collection); foreach (T item in collection) { Assert.True(collection.Contains(item), "The item \"" + item + "\" is not in the collection even though that item was " + "pulled from the collection."); } } [Test] [Description("Calls CopyTo(T[], int) on the ICollection with a null " + "array parameter to ensure the CopyTo(T[], int) method throws an " + "ArgumentNullException.")] public void ArgumentNullExceptionWhenCopyToArrayIsNull() { ICollection collection = CreateCollection(); CheckCollectionCount(collection); Assert.Throws(() => collection.CopyTo(null, 0)); } [Test] [Description("Calls CopyTo(T[], int) on the ICollection with a -1 " + "arrayIndex parameter to ensure the CopyTo(T[], int) method throws an " + "ArgumentOutOfRangeException.")] public void ArgumentOutOfRangeExceptionWhenCopyToArrayIndexIsNegative() { ICollection collection = CreateCollection(); CheckCollectionCount(collection); T[] array = new T[RequiredCollectionCount]; Assert.Throws(() => collection.CopyTo(array, -1)); } [Test] [Description("Calls CopyTo(T[], int) on the ICollection with an array " + "index parameter that is equal to the length of the array passed in " + "to ensure an ArgumentException is thrown.")] public void ArgumentExceptionWhenCopyToArrayIndexIsEqualToTheLengthOfTheArray() { ICollection collection = CreateCollection(); CheckCollectionCount(collection); T[] array = new T[RequiredCollectionCount]; Assert.Throws(() => collection.CopyTo(array, RequiredCollectionCount)); } [Test] [Description("Calls CopyTo(T[], int) on the ICollection with an array " + "index parameter that is greater than the length of the array passed in " + "to ensure an ArgumentException is thrown.")] public void ArgumentExceptionWhenCopyToArrayIndexIsGreaterThanTheLengthOfTheArray() { ICollection collection = CreateCollection(); CheckCollectionCount(collection); T[] array = new T[RequiredCollectionCount]; Assert.Throws(Is.AssignableTo(), () => collection.CopyTo(array, RequiredCollectionCount + 1)); } [Test] [Description("Calls CopyTo(T[], int) on the ICollection with an array " + "that is smaller than the number of elements from the array index " + "passed in to the end of the ICollection to ensure an " + "ArgumentException is thrown.")] public void ArgumentExceptionWhenCopyToArrayIsSmallerThanTheNumberOfElementsFromArrayIndexToTheEndOfTheCollection() { ICollection collection = CreateCollection(); CheckCollectionCount(collection); T[] array = new T[1]; Assert.Throws(() => collection.CopyTo(array, 0)); } [Test] [Description("Calls CopyTo(T[], int) on the ICollection and then checks " + "the returned array to ensure it contains items that are in " + "the ICollection.")] public void CopyToContainsProperItems() { ICollection collection = CreateCollection(); CheckCollectionCount(collection); T[] array = new T[RequiredCollectionCount]; collection.CopyTo(array, 0); foreach (T item in array) { Assert.True(collection.Contains(item), "The item \"" + item + "\" " + "copied from the array is not contained within the collection it " + "was copied from."); Assert.AreEqual(1, array.Count(otherItem => item.Equals(otherItem)), "The item \"" + item + "\" is contained within the copied array " + "more than once. Each item in the collection must be unique."); } } [Test] [Description("Removes() all items from the ICollection using Remove() " + "method to ensure the Remove() method works properly")] public void RemovesProperly() { ICollection collection = CreateCollection(); CheckCollectionCount(collection); T[] array = new T[RequiredCollectionCount]; collection.CopyTo(array, 0); foreach (T item in array) { Assert.True(collection.Remove(item), "Failed to remove one of " + "the items from the list, even though it was contained " + "within the list."); } Assert.AreEqual(0, collection.Count, "The collection is not empty even " + "though all items were supposedly removed. "); } [Test] [Description("Removes() an item from the list twice to ensure the " + "Remove() method properly returns true the first time and false the " + "second time.")] public void RemoveMethodDoesNotRemoveTwice() { ICollection collection = CreateCollection(); CheckCollectionCount(collection); // grab an item from the center of the list, just for kicks T item = collection.Skip((int)(RequiredCollectionCount - (RequiredCollectionCount * 0.5f))).First(); Assert.True(collection.Remove(item), "Failed to remove an item that " + "was supposed to be in the collection."); Assert.False(collection.Remove(item), "The Remove() method returned " + "true (i.e. it had properly removed an item from the collection) " + "even though it had already been removed previously."); } [Test] [Description("Counts the items in the ICollection using iteration and " + "then checks that number against the Count property to ensure both " + "are correct.")] public void CountIsCorrect() { ICollection collection = CreateCollection(); Assert.AreEqual(collection.Count(), collection.Count, "The Count() " + "LINQ method returned a different value from the ICollection's " + "Count property."); } /// /// Checks the collection to see if it contains the required number of /// items as specified by . /// /// The collection to check. void CheckCollectionCount(ICollection collection) { Assert.AreEqual(RequiredCollectionCount, collection.Count, "The collection " + "does not contain the required number of items. It must contain " + RequiredCollectionCount + " items."); } /// /// The number of items each collection is required to contain when it /// was created by . The value of this /// field is 7. /// public const int RequiredCollectionCount = 7; /// /// Creates an to test. Derived classes /// should always create a new collection and never reuse the same /// instance unless it makes sense for the class in question. The created /// collection should contain the number of items specified by /// . Each item in the list should /// be unique so that a comparison of them does not yield duplicates. /// This method should also create a collection that follows the rules /// specified by the method /// /// (see the comments on that method for details). /// protected abstract ICollection CreateCollection(); /// /// Creates a read-only to test. This method /// should behave exactly like the method /// and follow the exact same rules (see the comments on /// for details). If the collection can /// never be read-only, then it should return the same exact thing as the /// method. /// protected abstract ICollection CreateReadOnlyCollection(); /// /// Creates an item that can be added to the class. /// protected abstract T CreateItem(); /// /// This method should return a new that can /// be iterated over exactly once. This method should create a new /// every time it is invoked and not reuse /// instances. /// /// /// The containing a single item. /// protected override IEnumerable CreateSingleItemEnumerable() { ICollection collection = CreateCollection(); // skip all of the items except one T[] array = new T[collection.Count]; collection.CopyTo(array, 0); IEnumerable itemsToRemove = array.Take(collection.Count - 1); foreach (T item in itemsToRemove) collection.Remove(item); return collection; } /// /// This method should return a new that can /// be iterated over five or more times. The items will be compared to /// the items in but only /// if the property /// is set to true. This method should create a new /// every time it is invoked and not reuse /// instances. All instances returned through iteration of the /// should be unique. /// /// /// The containing many items. /// protected override IEnumerable CreateManyItemEnumerable() { return CreateCollection(); } } /// /// A test fixture that is used to test the /// and to provide an example of /// how to create a proper /// test fixture. /// [TestFixture] public class ListCollectionTestsFixture : CollectionTests { /// /// Initializes a new instance of the /// class. /// public ListCollectionTestsFixture() : base(true) { } /// /// Creates an to test. Derived classes /// should always create a new collection and never reuse the same /// instance unless it makes sense for the class in question. The created /// collection should contain the number of items specified by /// . Each item in the list should /// be unique so that a comparison of them does not yield duplicates. /// /// The protected override ICollection CreateCollection() { return new List(new[] { 0, 1, 2, 3, 4, 5, 6 }); } /// /// Creates a read-only to test. This method /// should behave exactly like the method /// and follow the exact same rules (see the comments on /// for details). If the collection can /// never be read-only, then it should return the same exact thing as the /// method. /// /// protected override ICollection CreateReadOnlyCollection() { List list = new List(new[] { 0, 1, 2, 3, 4, 5, 6 }); return list.AsReadOnly(); } /// /// Creates an item that can be added to the class. /// /// The item that can be added to the list. protected override int CreateItem() { return 1; } /// /// Returns the items created by /// /// are expected to be in. /// protected override IEnumerable OrderedItems { get { return _orderedItems; } } /// /// The backing field to . /// readonly int[] _orderedItems = {0, 1, 2, 3, 4, 5, 6}; } }