#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 {
/// Unit Test for the observable dictionary wrapper
[TestFixture]
internal class ObservableDictionaryTest {
#region interface IObservableDictionarySubscriber
/// Interface used to test the observable dictionary
public interface IObservableDictionarySubscriber {
/// Called when the dictionary is about to clear its contents
/// Dictionary that is clearing its contents
/// Not used
void Clearing(object sender, EventArgs arguments);
/// Called when the dictionary has been clear of its contents
/// Dictionary that was cleared of its contents
/// Not used
void Cleared(object sender, EventArgs arguments);
/// Called when an item is added to the dictionary
/// Dictionary to which an item is being added
/// Contains the item that is being added
void ItemAdded(object sender, ItemEventArgs> arguments);
/// Called when an item is removed from the dictionary
/// Dictionary from which an item is being removed
/// Contains the item that is being removed
void ItemRemoved(object sender, ItemEventArgs> arguments);
/// Called when an item is replaced in the dictionary
/// Dictionary in which an item is being replaced
/// Contains the replaced item and its replacement
void ItemReplaced(
object sender, ItemReplaceEventArgs> arguments
);
}
#endregion // interface IObservableDictionarySubscriber
/// Initialization routine executed before each test is run
[SetUp]
public void Setup() {
this.mockery = new MockFactory();
this.mockedSubscriber = this.mockery.CreateMock();
this.observedDictionary = new ObservableDictionary();
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>>(
this.mockedSubscriber.MockObject.ItemAdded
);
this.observedDictionary.ItemRemoved +=
new EventHandler>>(
this.mockedSubscriber.MockObject.ItemRemoved
);
this.observedDictionary.ItemReplaced +=
new EventHandler>>(
this.mockedSubscriber.MockObject.ItemReplaced
);
}
///
/// Verifies that the default constructor of the observable dictionary works
///
[Test]
public void TestDefaultConstructor() {
ObservableDictionary testDictionary =
new ObservableDictionary();
Assert.AreEqual(0, testDictionary.Count);
}
///
/// Verifies that the copy constructor of the observable dictionary works
///
[Test]
public void TestCopyConstructor() {
Dictionary numbers = createTestDictionary();
ObservableDictionary testDictionary = makeObservable(numbers);
CollectionAssert.AreEqual(numbers, testDictionary);
}
/// Verifies that the IsReadOnly property is working
[Test]
public void TestIsReadOnly() {
Dictionary numbers = createTestDictionary();
ObservableDictionary testDictionary = makeObservable(numbers);
Assert.IsFalse(testDictionary.IsReadOnly);
}
///
/// Checks whether the Contains() method of the observable dictionary is able to
/// determine if the dictionary contains an item
///
[Test]
public void TestContains() {
Dictionary numbers = createTestDictionary();
ObservableDictionary testDictionary = makeObservable(numbers);
Assert.IsTrue(
testDictionary.Contains(new KeyValuePair(42, "forty-two"))
);
Assert.IsFalse(
testDictionary.Contains(new KeyValuePair(24, "twenty-four"))
);
}
///
/// Checks whether the Contains() method of the observable dictionary is able to
/// determine if the dictionary contains a key
///
[Test]
public void TestContainsKey() {
Dictionary numbers = createTestDictionary();
ObservableDictionary testDictionary = makeObservable(numbers);
Assert.IsTrue(testDictionary.ContainsKey(42));
Assert.IsFalse(testDictionary.ContainsKey(24));
}
///
/// Verifies that the CopyTo() of the observable dictionary works
///
[Test]
public void TestCopyToArray() {
Dictionary numbers = createTestDictionary();
ObservableDictionary testDictionary = makeObservable(numbers);
KeyValuePair[] items = new KeyValuePair[numbers.Count];
testDictionary.CopyTo(items, 0);
CollectionAssert.AreEqual(numbers, items);
}
///
/// Tests whether the typesafe enumerator of the observable dictionary is working
///
[Test]
public void TestTypesafeEnumerator() {
Dictionary numbers = createTestDictionary();
ObservableDictionary testDictionary = makeObservable(numbers);
List> outputItems = new List>();
foreach(KeyValuePair item in testDictionary) {
outputItems.Add(item);
}
CollectionAssert.AreEqual(numbers, outputItems);
}
///
/// Tests whether the keys collection of the observable dictionary can be queried
///
[Test]
public void TestGetKeysCollection() {
Dictionary numbers = createTestDictionary();
ObservableDictionary testDictionary = makeObservable(numbers);
ICollection inputKeys = numbers.Keys;
ICollection keys = testDictionary.Keys;
CollectionAssert.AreEquivalent(inputKeys, keys);
}
///
/// Tests whether the values collection of the observable dictionary can be queried
///
[Test]
public void TestGetValuesCollection() {
Dictionary numbers = createTestDictionary();
ObservableDictionary testDictionary = makeObservable(numbers);
ICollection inputValues = numbers.Values;
ICollection values = testDictionary.Values;
CollectionAssert.AreEquivalent(inputValues, values);
}
///
/// Tests whether the TryGetValue() method of the observable dictionary is working
///
[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);
}
///
/// Tests whether the retrieval of values using the indexer of the observable
/// dictionary is working
///
[Test]
public void TestRetrieveValueByIndexer() {
Assert.AreEqual("forty-two", this.observedDictionary[42]);
}
///
/// Tests whether an exception is thrown if the indexer of the observable dictionary
/// is used to attempt to retrieve a non-existing value
///
[Test]
public void TestRetrieveNonExistingValueByIndexer() {
Assert.Throws(
delegate() { Console.WriteLine(this.observedDictionary[24]); }
);
}
///
/// Checks whether the Add() methods works via the generic
/// IDictionary<> interface
///
[Test]
public void TestAddViaGenericIDictionary() {
this.mockedSubscriber.Expects.One.Method(m => m.ItemAdded(null, null)).WithAnyArguments();
(this.observedDictionary as IDictionary).Add(10, "ten");
this.mockery.VerifyAllExpectationsHaveBeenMet();
CollectionAssert.Contains(
this.observedDictionary, new KeyValuePair(10, "ten")
);
}
///
/// Checks whether the Remove() method works via the generic
/// IDictionary<> interface
///
[Test]
public void TestRemoveViaGenericIDictionary() {
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);
}
///
/// Tests whether the TryGetValue() method of the observable dictionary is working
///
[Test]
public void TestRetrieveValueByIndexerViaGenericIDictionary() {
Assert.AreEqual(
"forty-two", (this.observedDictionary as IDictionary)[42]
);
}
///
/// Verifies that the indexer can be used to insert an item via the generic
/// IDictionar<> interface
///
[Test]
public void TestReplaceByIndexerViaGenericIDictionary() {
this.mockedSubscriber.Expects.One.Method(
m => m.ItemReplaced(null, null)
).WithAnyArguments();
(this.observedDictionary as IDictionary)[42] = "two and fourty";
this.mockery.VerifyAllExpectationsHaveBeenMet();
Assert.AreEqual("two and fourty", this.observedDictionary[42]);
}
///
/// Checks whether the Clear() method of observable dictionary is working
///
[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);
}
///
/// Checks whether the Add() method works via the IDictionary interface
///
[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(24, "twenty-four")
);
}
///
/// Checks whether the Contains() method of the observable dictionary is able to
/// determine if the dictionary contains an item via the IDictionary interface
///
[Test]
public void TestContainsViaIDictionary() {
Assert.IsTrue((this.observedDictionary as IDictionary).Contains(42));
Assert.IsFalse((this.observedDictionary as IDictionary).Contains(24));
}
///
/// Checks whether the GetEnumerator() method of the observable dictionary
/// returns a working enumerator if accessed via the IDictionary interface
///
[Test]
public void TestEnumeratorViaIDictionary() {
Dictionary outputNumbers = new Dictionary();
foreach(DictionaryEntry entry in (this.observedDictionary as IDictionary)) {
(outputNumbers as IDictionary).Add(entry.Key, entry.Value);
}
CollectionAssert.AreEquivalent(this.observedDictionary, outputNumbers);
}
///
/// Checks whether the IsFixedSize property of the observable dictionary returns
/// the expected result for a read only dictionary based on a dynamic dictionary
///
[Test]
public void TestIsFixedSizeViaIList() {
Assert.IsFalse((this.observedDictionary as IDictionary).IsFixedSize);
}
///
/// Tests whether the keys collection of the observable dictionary can be queried
/// via the IDictionary interface
///
[Test]
public void TestGetKeysCollectionViaIDictionary() {
ICollection keys = (this.observedDictionary as IDictionary).Keys;
Assert.AreEqual(this.observedDictionary.Count, keys.Count);
}
///
/// Tests whether the values collection of the observable dictionary can be queried
/// via the IDictionary interface
///
[Test]
public void TestGetValuesCollectionViaIDictionary() {
ICollection values = (this.observedDictionary as IDictionary).Values;
Assert.AreEqual(this.observedDictionary.Count, values.Count);
}
///
/// Checks whether Remove() method works via the IDictionary interface
///
[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);
}
///
/// Tests whether the retrieval of values using the indexer of the observable
/// dictionary is working via the IDictionary interface
///
[Test]
public void TestRetrieveValueByIndexerViaIDictionary() {
Assert.AreEqual("forty-two", (this.observedDictionary as IDictionary)[42]);
}
///
/// Verifies the indexer can be used to insert an item via the IDictionary interface
///
[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]);
}
///
/// Checks whether Add() method is working via the generic
/// ICollection<> interface
///
[Test]
public void TestAddViaGenericICollection() {
this.mockedSubscriber.Expects.One.Method(
m => m.ItemAdded(null, null)
).WithAnyArguments();
(this.observedDictionary as ICollection>).Add(
new KeyValuePair(24, "twenty-four")
);
this.mockery.VerifyAllExpectationsHaveBeenMet();
CollectionAssert.Contains(
this.observedDictionary, new KeyValuePair(24, "twenty-four")
);
}
///
/// Checks whether the Clear() method is working via the generic
/// ICollection<> interface
///
[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>).Clear();
this.mockery.VerifyAllExpectationsHaveBeenMet();
Assert.AreEqual(0, this.observedDictionary.Count);
}
///
/// Checks whether the Remove() method is working via the
/// generic ICollection<> interface
///
[Test]
public void TestRemoveViaGenericICollection() {
IEnumerator> enumerator =
(this.observedDictionary as ICollection>).GetEnumerator();
enumerator.MoveNext();
this.mockedSubscriber.Expects.One.Method(
m => m.ItemRemoved(null, null)
).WithAnyArguments();
(this.observedDictionary as ICollection>).Remove(
enumerator.Current
);
this.mockery.VerifyAllExpectationsHaveBeenMet();
CollectionAssert.DoesNotContain(this.observedDictionary, enumerator.Current);
}
///
/// Verifies that the CopyTo() of the observable dictionary works when called
/// via the the ICollection interface
///
[Test]
public void TestCopyToArrayViaICollection() {
Dictionary numbers = createTestDictionary();
ObservableDictionary testDictionary = makeObservable(numbers);
DictionaryEntry[] entries = new DictionaryEntry[numbers.Count];
(testDictionary as ICollection).CopyTo(entries, 0);
KeyValuePair[] items = new KeyValuePair[numbers.Count];
for(int index = 0; index < entries.Length; ++index) {
items[index] = new KeyValuePair(
(int)entries[index].Key, (string)entries[index].Value
);
}
CollectionAssert.AreEquivalent(numbers, items);
}
///
/// Verifies that the IsSynchronized property and the SyncRoot property are working
///
[Test]
public void TestSynchronization() {
Dictionary numbers = createTestDictionary();
ObservableDictionary testDictionary = makeObservable(numbers);
if(!(testDictionary as ICollection).IsSynchronized) {
lock((testDictionary as ICollection).SyncRoot) {
Assert.AreEqual(numbers.Count, testDictionary.Count);
}
}
}
///
/// Test whether the observable dictionary can be serialized
///
[Test]
public void TestSerialization() {
BinaryFormatter formatter = new BinaryFormatter();
using(MemoryStream memory = new MemoryStream()) {
Dictionary numbers = createTestDictionary();
ObservableDictionary testDictionary1 = makeObservable(numbers);
formatter.Serialize(memory, testDictionary1);
memory.Position = 0;
object testDictionary2 = formatter.Deserialize(memory);
CollectionAssert.AreEquivalent(testDictionary1, (IEnumerable)testDictionary2);
}
}
///
/// Creates a new observable dictionary filled with some values for testing
///
/// The newly created observable dictionary
private static Dictionary createTestDictionary() {
Dictionary numbers = new Dictionary();
numbers.Add(1, "one");
numbers.Add(2, "two");
numbers.Add(3, "three");
numbers.Add(42, "forty-two");
return new Dictionary(numbers);
}
///
/// Creates a new observable dictionary filled with some values for testing
///
/// The newly created observable dictionary
private static ObservableDictionary makeObservable(
IDictionary dictionary
) {
return new ObservableDictionary(dictionary);
}
/// Mock object factory
private MockFactory mockery;
/// The mocked observable collection subscriber
private Mock mockedSubscriber;
/// An observable dictionary to which a mock will be subscribed
private ObservableDictionary observedDictionary;
}
} // namespace Nuclex.Support.Collections
#endif // UNITTEST
#endif // !NO_NMOCK