MultiDictionary wasn't firing 'removed' events when an entire set of values is replaced - fixed, but not unit-tested yet; added more unit tests to the ObservableSet class; ObservableSet was not firing the 'added' event - fixed

git-svn-id: file:///srv/devel/repo-conversion/nusu@260 d2e56fa2-650e-0410-a79f-9358c0239efd
This commit is contained in:
Markus Ewald 2012-03-02 21:05:05 +00:00
parent 0195b34289
commit b37c4a757c
4 changed files with 124 additions and 9 deletions

View File

@ -41,6 +41,20 @@ namespace Nuclex.Support.Collections {
Assert.IsNotNull(dictionary); // nonsense, prevents compiler warning 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> /// <summary>
/// Verifies that a new multi dictionary based on a read-only dictionary is /// Verifies that a new multi dictionary based on a read-only dictionary is
/// also read-only /// also read-only
@ -116,6 +130,15 @@ namespace Nuclex.Support.Collections {
Assert.AreEqual(1, dictionary.CountValues(30)); 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> /// <summary>
/// Ensures that its possible to remove values individually without affecting /// Ensures that its possible to remove values individually without affecting
/// other values stored under the same key /// other values stored under the same key
@ -150,10 +173,39 @@ namespace Nuclex.Support.Collections {
dictionary.Add(10, "zehn"); dictionary.Add(10, "zehn");
Assert.AreEqual(2, dictionary.Count); Assert.AreEqual(2, dictionary.Count);
var collectionOfCollections = (ICollection<KeyValuePair<int, ICollection<string>>>)dictionary; var collectionOfCollections =
(ICollection<KeyValuePair<int, ICollection<string>>>)dictionary;
Assert.AreEqual(1, collectionOfCollections.Count); 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));
}
} }
} // namespace Nuclex.Support.Collections } // namespace Nuclex.Support.Collections

View File

@ -333,15 +333,39 @@ namespace Nuclex.Support.Collections {
ICollection<TValue> currentValues; ICollection<TValue> currentValues;
if(this.typedDictionary.TryGetValue(key, out currentValues)) { if(this.typedDictionary.TryGetValue(key, out currentValues)) {
currentValues.Clear(); ValueList currentValueList = (ValueList)currentValues;
int index = 0;
foreach(TValue addedValue in value) {
if(index < currentValueList.Count) {
TValue original = currentValueList[index];
currentValueList[index] = addedValue;
OnReplaced(
new KeyValuePair<TKey, TValue>(key, original),
new KeyValuePair<TKey, TValue>(key, addedValue)
);
} else {
currentValueList.Add(addedValue);
OnAdded(new KeyValuePair<TKey, TValue>(key, addedValue));
}
++index;
}
int count = currentValueList.Count;
while(count > index) {
--count;
TValue removedValue = currentValueList[count];
currentValueList.RemoveAt(count);
OnRemoved(new KeyValuePair<TKey, TValue>(key, removedValue));
}
} else { } else {
currentValues = new ValueList(this); currentValues = new ValueList(this);
this.typedDictionary.Add(key, currentValues); this.typedDictionary.Add(key, currentValues);
}
foreach(TValue addedValue in value) { foreach(TValue addedValue in value) {
currentValues.Add(addedValue); currentValues.Add(addedValue);
OnAdded(new KeyValuePair<TKey, TValue>(key, addedValue)); OnAdded(new KeyValuePair<TKey, TValue>(key, addedValue));
}
} }
} }
} }
@ -420,6 +444,13 @@ namespace Nuclex.Support.Collections {
/// <param name="item">Item that has been removed from the collection</param> /// <param name="item">Item that has been removed from the collection</param>
protected virtual void OnRemoved(KeyValuePair<TKey, TValue> item) { } protected virtual void OnRemoved(KeyValuePair<TKey, TValue> item) { }
/// <summary>Fires the 'ItemReplaced' event</summary>
/// <param name="oldItem">Item that was replaced in the collection</param>
/// <param name="newItem">Item that the original was replaced by</param>
protected virtual void OnReplaced(
KeyValuePair<TKey, TValue> oldItem, KeyValuePair<TKey, TValue> newItem
) { }
/// <summary>Fires the 'Clearing' event</summary> /// <summary>Fires the 'Clearing' event</summary>
protected virtual void OnClearing() { } protected virtual void OnClearing() { }

View File

@ -31,12 +31,40 @@ using NMock;
namespace Nuclex.Support.Collections { namespace Nuclex.Support.Collections {
#if false
/// <summary>Unit Test for the observable set wrapper</summary> /// <summary>Unit Test for the observable set wrapper</summary>
[TestFixture] [TestFixture]
internal class ObservableSetTest { internal class ObservableSetTest {
#region interface IObservableCollectionSubscriber<TItem>
public interface IObservableCollectionSubscriber<TItem> {
/// <summary>Raised when an item has been added to the collection</summary>
event EventHandler<ItemEventArgs<TItem>> ItemAdded;
/// <summary>Raised when an item is removed from the collection</summary>
event EventHandler<ItemEventArgs<TItem>> ItemRemoved;
/// <summary>Raised when an item is replaced in the collection</summary>
event EventHandler<ItemReplaceEventArgs<TItem>> ItemReplaced;
/// <summary>Raised when the collection is about to be cleared</summary>
event EventHandler Clearing;
/// <summary>Raised when the collection has been cleared</summary>
event EventHandler Cleared;
}
#endregion // interface IObservableCollectionSubscriber<TItem>
/// <summary>
/// Verifies that the observable set has a default constructor
/// </summary>
[Test]
public void HasDefaultConstructor() {
Assert.IsNotNull(new ObservableSet<int>());
}
} }
#endif
} // namespace Nuclex.Support.Collections } // namespace Nuclex.Support.Collections

View File

@ -82,7 +82,11 @@ namespace Nuclex.Support.Collections {
/// True if the element was added, false if it was already contained in the set /// True if the element was added, false if it was already contained in the set
/// </returns> /// </returns>
public bool Add(TItem item) { public bool Add(TItem item) {
return this.set.Add(item); bool wasAdded = this.set.Add(item);
if(wasAdded) {
OnAdded(item);
}
return wasAdded;
} }
/// <summary>Removes all elements that are contained in the collection</summary> /// <summary>Removes all elements that are contained in the collection</summary>