|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Windows;
namespace System.Collections.Specialized.Tests;
public class CollectionChangedEventManagerTests
{
public static IEnumerable<object?[]> AddHandler_TestData()
{
yield return new object?[] { null };
yield return new object?[] { new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset) };
}
[Theory]
[MemberData(nameof(AddHandler_TestData))]
public void AddHandler_InvokeWithHandler_CallsCollectionChanged(NotifyCollectionChangedEventArgs e)
{
var source = new CustomNotifyCollectionChanged();
int callCount1 = 0;
var listener1 = new CustomWeakEventListener();
listener1.HandlerAction += (actualSender, actualE) =>
{
Assert.Same(source, actualSender);
Assert.Same(e, actualE);
callCount1++;
};
EventHandler<NotifyCollectionChangedEventArgs> handler1 = (EventHandler<NotifyCollectionChangedEventArgs>)Delegate.CreateDelegate(typeof(EventHandler<NotifyCollectionChangedEventArgs>), listener1, nameof(CustomWeakEventListener.Handler));
int callCount2 = 0;
var listener2 = new CustomWeakEventListener();
listener2.HandlerAction += (actualSender, actualE) =>
{
Assert.Same(source, actualSender);
Assert.Same(e, actualE);
callCount2++;
};
EventHandler<NotifyCollectionChangedEventArgs> handler2 = (EventHandler<NotifyCollectionChangedEventArgs>)Delegate.CreateDelegate(typeof(EventHandler<NotifyCollectionChangedEventArgs>), listener2, nameof(CustomWeakEventListener.Handler));
// Add.
CollectionChangedEventManager.AddHandler(source, handler1);
// Call.
source.OnCollectionChanged(source, e);
Assert.Equal(1, callCount1);
Assert.Equal(0, callCount2);
// Call invalid source.
source.OnCollectionChanged(listener1, e);
Assert.Equal(1, callCount1);
Assert.Equal(0, callCount2);
source.OnCollectionChanged(new object(), e);
Assert.Equal(1, callCount1);
Assert.Equal(0, callCount2);
source.OnCollectionChanged(null!, e);
Assert.Equal(1, callCount1);
Assert.Equal(0, callCount2);
// Add again.
CollectionChangedEventManager.AddHandler(source, handler1);
source.OnCollectionChanged(source, e);
Assert.Equal(3, callCount1);
Assert.Equal(0, callCount2);
// Add another.
CollectionChangedEventManager.AddHandler(source, handler2);
source.OnCollectionChanged(source, e);
Assert.Equal(5, callCount1);
Assert.Equal(1, callCount2);
// Remove second.
CollectionChangedEventManager.RemoveHandler(source, handler2);
source.OnCollectionChanged(source, e);
Assert.Equal(7, callCount1);
Assert.Equal(1, callCount2);
// Remove first.
CollectionChangedEventManager.RemoveHandler(source, handler1);
source.OnCollectionChanged(source, e);
Assert.Equal(8, callCount1);
Assert.Equal(1, callCount2);
// Remove again.
CollectionChangedEventManager.RemoveHandler(source, handler1);
source.OnCollectionChanged(source, e);
Assert.Equal(8, callCount1);
Assert.Equal(1, callCount2);
}
[Fact]
public void AddHandler_InvokeMultipleTimes_Success()
{
var source1 = new CustomNotifyCollectionChanged();
var source2 = new CustomNotifyCollectionChanged();
var target1 = new CustomWeakEventListener();
int callCount1 = 0;
target1.HandlerAction += (actualSender, actualE) => callCount1++;
EventHandler<NotifyCollectionChangedEventArgs> handler1 = (EventHandler<NotifyCollectionChangedEventArgs>)Delegate.CreateDelegate(typeof(EventHandler<NotifyCollectionChangedEventArgs>), target1, nameof(CustomWeakEventListener.Handler));
var target2 = new CustomWeakEventListener();
int callCount2 = 0;
target2.HandlerAction += (actualSender, actualE) => callCount2++;
EventHandler<NotifyCollectionChangedEventArgs> handler2 = (EventHandler<NotifyCollectionChangedEventArgs>)Delegate.CreateDelegate(typeof(EventHandler<NotifyCollectionChangedEventArgs>), target2, nameof(CustomWeakEventListener.Handler));
var target3 = new CustomWeakEventListener();
int callCount3 = 0;
target3.HandlerAction += (actualSender, actualE) => callCount3++;
EventHandler<NotifyCollectionChangedEventArgs> handler3 = (EventHandler<NotifyCollectionChangedEventArgs>)Delegate.CreateDelegate(typeof(EventHandler<NotifyCollectionChangedEventArgs>), target3, nameof(CustomWeakEventListener.Handler));
// Add.
CollectionChangedEventManager.AddHandler(source1, handler1);
source1.OnCollectionChanged(source1, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
Assert.Equal(1, callCount1);
Assert.Equal(0, callCount2);
Assert.Equal(0, callCount3);
source2.OnCollectionChanged(source2, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
Assert.Equal(1, callCount1);
Assert.Equal(0, callCount2);
Assert.Equal(0, callCount3);
// Add again.
CollectionChangedEventManager.AddHandler(source1, handler1);
source1.OnCollectionChanged(source1, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
Assert.Equal(3, callCount1);
Assert.Equal(0, callCount2);
Assert.Equal(0, callCount3);
source2.OnCollectionChanged(source2, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
Assert.Equal(3, callCount1);
Assert.Equal(0, callCount2);
Assert.Equal(0, callCount3);
// Add another handler.
CollectionChangedEventManager.AddHandler(source1, handler2);
source1.OnCollectionChanged(source1, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
Assert.Equal(5, callCount1);
Assert.Equal(1, callCount2);
Assert.Equal(0, callCount3);
source2.OnCollectionChanged(source2, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
Assert.Equal(5, callCount1);
Assert.Equal(1, callCount2);
Assert.Equal(0, callCount3);
// Add another source.
CollectionChangedEventManager.AddHandler(source2, handler3);
source1.OnCollectionChanged(source1, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
Assert.Equal(7, callCount1);
Assert.Equal(2, callCount2);
Assert.Equal(0, callCount3);
source2.OnCollectionChanged(source2, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
Assert.Equal(7, callCount1);
Assert.Equal(2, callCount2);
Assert.Equal(1, callCount3);
}
// [Fact]
// public void AddHandler_InvokeNoSource_Success()
// {
// var target1 = new CustomWeakEventListener();
// EventHandler<NotifyCollectionChangedEventArgs> handler1 = (EventHandler<NotifyCollectionChangedEventArgs>)Delegate.CreateDelegate(typeof(EventHandler<NotifyCollectionChangedEventArgs>), target1, nameof(CustomWeakEventListener.Handler));
// var target2 = new CustomWeakEventListener();
// EventHandler<NotifyCollectionChangedEventArgs> handler2 = (EventHandler<NotifyCollectionChangedEventArgs>)Delegate.CreateDelegate(typeof(EventHandler<NotifyCollectionChangedEventArgs>), target2, nameof(CustomWeakEventListener.Handler));
// // Add.
// Assert.Throws<NullReferenceException>(() => CollectionChangedEventManager.AddHandler(null, handler1));
// // Add again.
// CollectionChangedEventManager.AddHandler(null, handler1);
// // Add another.
// CollectionChangedEventManager.AddHandler(null, handler2);
// }
[Fact]
public void AddHandler_NullHandler_ThrowsArgumentNullException()
{
var source = new CustomNotifyCollectionChanged();
Assert.Throws<ArgumentNullException>("handler", () => CollectionChangedEventManager.AddHandler(null, null));
Assert.Throws<ArgumentNullException>("handler", () => CollectionChangedEventManager.AddHandler(source, null));
}
public static IEnumerable<object?[]> AddListener_TestData()
{
yield return new object?[] { null };
yield return new object?[] { new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset) };
}
[Theory]
[MemberData(nameof(AddListener_TestData))]
public void AddListener_InvokeWithHandler_CallsCollectionChanged(NotifyCollectionChangedEventArgs e)
{
var source = new CustomNotifyCollectionChanged();
int callCount1 = 0;
var listener1 = new CustomWeakEventListener();
listener1.ReceiveWeakEventAction += (actualManagerType, actualSender, actualE) =>
{
Assert.Equal(typeof(CollectionChangedEventManager), actualManagerType);
Assert.Same(source, actualSender);
Assert.Same(e, actualE);
callCount1++;
return true;
};
int callCount2 = 0;
var listener2 = new CustomWeakEventListener();
listener2.ReceiveWeakEventAction += (actualManagerType, actualSender, actualE) =>
{
Assert.Equal(typeof(CollectionChangedEventManager), actualManagerType);
Assert.Same(source, actualSender);
Assert.Same(e, actualE);
callCount2++;
return true;
};
// Add.
CollectionChangedEventManager.AddListener(source, listener1);
// Call.
source.OnCollectionChanged(source, e);
Assert.Equal(1, callCount1);
Assert.Equal(0, callCount2);
// Call invalid source.
source.OnCollectionChanged(listener1, e);
Assert.Equal(1, callCount1);
Assert.Equal(0, callCount2);
source.OnCollectionChanged(new object(), e);
Assert.Equal(1, callCount1);
Assert.Equal(0, callCount2);
source.OnCollectionChanged(null!, e);
Assert.Equal(1, callCount1);
Assert.Equal(0, callCount2);
// Add again.
CollectionChangedEventManager.AddListener(source, listener1);
source.OnCollectionChanged(source, e);
Assert.Equal(3, callCount1);
Assert.Equal(0, callCount2);
// Add another.
CollectionChangedEventManager.AddListener(source, listener2);
source.OnCollectionChanged(source, e);
Assert.Equal(5, callCount1);
Assert.Equal(1, callCount2);
// Remove second.
CollectionChangedEventManager.RemoveListener(source, listener2);
source.OnCollectionChanged(source, e);
Assert.Equal(7, callCount1);
Assert.Equal(1, callCount2);
// Remove first.
CollectionChangedEventManager.RemoveListener(source, listener1);
source.OnCollectionChanged(source, e);
Assert.Equal(8, callCount1);
Assert.Equal(1, callCount2);
// Remove again.
CollectionChangedEventManager.RemoveListener(source, listener1);
source.OnCollectionChanged(source, e);
Assert.Equal(8, callCount1);
Assert.Equal(1, callCount2);
}
[Fact]
public void AddListener_InvokeMultipleTimes_Success()
{
var source1 = new CustomNotifyCollectionChanged();
var source2 = new CustomNotifyCollectionChanged();
var listener1 = new CustomWeakEventListener();
int callCount1 = 0;
listener1.ReceiveWeakEventAction += (t, s, e) =>
{
callCount1++;
return true;
};
var listener2 = new CustomWeakEventListener();
int callCount2 = 0;
listener2.ReceiveWeakEventAction += (t, s, e) =>
{
callCount2++;
return true;
};
var listener3 = new CustomWeakEventListener();
int callCount3 = 0;
listener3.ReceiveWeakEventAction += (t, s, e) =>
{
callCount3++;
return true;
};
// Add.
CollectionChangedEventManager.AddListener(source1, listener1);
source1.OnCollectionChanged(source1, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
Assert.Equal(1, callCount1);
Assert.Equal(0, callCount2);
Assert.Equal(0, callCount3);
source2.OnCollectionChanged(source2, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
Assert.Equal(1, callCount1);
Assert.Equal(0, callCount2);
Assert.Equal(0, callCount3);
// Add again.
CollectionChangedEventManager.AddListener(source1, listener1);
source1.OnCollectionChanged(source1, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
Assert.Equal(3, callCount1);
Assert.Equal(0, callCount2);
Assert.Equal(0, callCount3);
source2.OnCollectionChanged(source2, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
Assert.Equal(3, callCount1);
Assert.Equal(0, callCount2);
Assert.Equal(0, callCount3);
// Add another listener.
CollectionChangedEventManager.AddListener(source1, listener2);
source1.OnCollectionChanged(source1, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
Assert.Equal(5, callCount1);
Assert.Equal(1, callCount2);
Assert.Equal(0, callCount3);
source2.OnCollectionChanged(source2, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
Assert.Equal(5, callCount1);
Assert.Equal(1, callCount2);
Assert.Equal(0, callCount3);
// Add another source.
CollectionChangedEventManager.AddListener(source2, listener3);
source1.OnCollectionChanged(source1, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
Assert.Equal(7, callCount1);
Assert.Equal(2, callCount2);
Assert.Equal(0, callCount3);
source2.OnCollectionChanged(source2, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
Assert.Equal(7, callCount1);
Assert.Equal(2, callCount2);
Assert.Equal(1, callCount3);
}
[Fact]
public void AddListener_NullSource_ThrowsArgumentNullException()
{
var listener = new CustomWeakEventListener();
Assert.Throws<ArgumentNullException>("source", () => CollectionChangedEventManager.AddListener(null, listener));
}
[Fact]
public void AddListener_NullListener_ThrowsArgumentNullException()
{
var source = new CustomNotifyCollectionChanged();
Assert.Throws<ArgumentNullException>("listener", () => CollectionChangedEventManager.AddListener(source, null));
}
[Fact]
public void RemoveHandler_Invoke_Success()
{
var source = new CustomNotifyCollectionChanged();
var target = new CustomWeakEventListener();
int callCount = 0;
target.HandlerAction += (actualSender, actualE) => callCount++;
EventHandler<NotifyCollectionChangedEventArgs> handler = (EventHandler<NotifyCollectionChangedEventArgs>)Delegate.CreateDelegate(typeof(EventHandler<NotifyCollectionChangedEventArgs>), target, nameof(CustomWeakEventListener.Handler));
CollectionChangedEventManager.AddHandler(source, handler);
// Remove.
CollectionChangedEventManager.RemoveHandler(source, handler);
source.OnCollectionChanged(source, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
Assert.Equal(0, callCount);
// Remove again.
CollectionChangedEventManager.RemoveHandler(source, handler);
source.OnCollectionChanged(source, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
Assert.Equal(0, callCount);
}
// [Fact]
// public void RemoveHandler_InvokeNoSource_Success()
// {
// var target = new CustomWeakEventListener();
// EventHandler<NotifyCollectionChangedEventArgs> handler = (EventHandler<NotifyCollectionChangedEventArgs>)Delegate.CreateDelegate(typeof(EventHandler<NotifyCollectionChangedEventArgs>), target, nameof(CustomWeakEventListener.Handler));
// CollectionChangedEventManager.AddHandler(null, handler);
// // Remove.
// CollectionChangedEventManager.RemoveHandler(null, handler);
// // Remove again.
// CollectionChangedEventManager.RemoveHandler(null, handler);
// }
[Fact]
public void RemoveHandler_InvokeNoSuchSource_Nop()
{
var source1 = new CustomNotifyCollectionChanged();
var source2 = new CustomNotifyCollectionChanged();
var target = new CustomWeakEventListener();
int callCount = 0;
target.HandlerAction += (actualSender, actualE) => callCount++;
EventHandler<NotifyCollectionChangedEventArgs> handler = (EventHandler<NotifyCollectionChangedEventArgs>)Delegate.CreateDelegate(typeof(EventHandler<NotifyCollectionChangedEventArgs>), target, nameof(CustomWeakEventListener.Handler));
CollectionChangedEventManager.AddHandler(source1, handler);
// Remove.
CollectionChangedEventManager.RemoveHandler(source2, handler);
source1.OnCollectionChanged(source1, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
Assert.Equal(1, callCount);
source2.OnCollectionChanged(source2, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
Assert.Equal(1, callCount);
// Remove again.
CollectionChangedEventManager.RemoveHandler(source2, handler);
source1.OnCollectionChanged(source1, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
Assert.Equal(2, callCount);
source2.OnCollectionChanged(source2, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
Assert.Equal(2, callCount);
}
[Fact]
public void RemoveHandler_InvokeNoSuchHandler_Nop()
{
var source = new CustomNotifyCollectionChanged();
var target1 = new CustomWeakEventListener();
int callCount1 = 0;
target1.HandlerAction += (actualSender, actualE) => callCount1++;
EventHandler<NotifyCollectionChangedEventArgs> handler1 = (EventHandler<NotifyCollectionChangedEventArgs>)Delegate.CreateDelegate(typeof(EventHandler<NotifyCollectionChangedEventArgs>), target1, nameof(CustomWeakEventListener.Handler));
var target2 = new CustomWeakEventListener();
int callCount2 = 0;
target2.HandlerAction += (actualSender, actualE) => callCount2++;
EventHandler<NotifyCollectionChangedEventArgs> handler2 = (EventHandler<NotifyCollectionChangedEventArgs>)Delegate.CreateDelegate(typeof(EventHandler<NotifyCollectionChangedEventArgs>), target2, nameof(CustomWeakEventListener.Handler));
CollectionChangedEventManager.AddHandler(source, handler1);
// Remove.
CollectionChangedEventManager.RemoveHandler(source, handler2);
source.OnCollectionChanged(source, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
Assert.Equal(1, callCount1);
Assert.Equal(0, callCount2);
// Remove again.
CollectionChangedEventManager.RemoveHandler(source, handler2);
source.OnCollectionChanged(source, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
Assert.Equal(2, callCount1);
Assert.Equal(0, callCount2);
}
[Fact]
public void RemoveHandler_NullHandler_ThrowsArgumentNullException()
{
var source = new CustomNotifyCollectionChanged();
Assert.Throws<ArgumentNullException>("handler", () => CollectionChangedEventManager.RemoveHandler(null, null));
Assert.Throws<ArgumentNullException>("handler", () => CollectionChangedEventManager.RemoveHandler(source, null));
}
[Fact]
public void RemoveListener_Invoke_Success()
{
var source = new CustomNotifyCollectionChanged();
var listener = new CustomWeakEventListener();
int callCount = 0;
listener.ReceiveWeakEventAction += (t, s, e) =>
{
callCount++;
return true;
};
CollectionChangedEventManager.AddListener(source, listener);
// Remove.
CollectionChangedEventManager.RemoveListener(source, listener);
source.OnCollectionChanged(source, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
Assert.Equal(0, callCount);
// Remove again.
CollectionChangedEventManager.RemoveListener(source, listener);
source.OnCollectionChanged(source, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
Assert.Equal(0, callCount);
}
[Fact]
public void RemoveListener_InvokeNoSuchSource_Nop()
{
var source1 = new CustomNotifyCollectionChanged();
var source2 = new CustomNotifyCollectionChanged();
var listener = new CustomWeakEventListener();
int callCount = 0;
listener.ReceiveWeakEventAction += (t, s, e) =>
{
callCount++;
return true;
};
CollectionChangedEventManager.AddListener(source1, listener);
// Remove.
CollectionChangedEventManager.RemoveListener(source2, listener);
source1.OnCollectionChanged(source1, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
Assert.Equal(1, callCount);
source2.OnCollectionChanged(source2, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
Assert.Equal(1, callCount);
// Remove again.
CollectionChangedEventManager.RemoveListener(source2, listener);
source1.OnCollectionChanged(source1, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
Assert.Equal(2, callCount);
source2.OnCollectionChanged(source2, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
Assert.Equal(2, callCount);
}
[Fact]
public void RemoveListener_InvokeNoSuchListener_Nop()
{
var source = new CustomNotifyCollectionChanged();
var listener1 = new CustomWeakEventListener();
int callCount1 = 0;
listener1.ReceiveWeakEventAction += (t, s, e) =>
{
callCount1++;
return true;
};
var listener2 = new CustomWeakEventListener();
int callCount2 = 0;
listener2.ReceiveWeakEventAction += (t, s, e) =>
{
callCount2++;
return true;
};
CollectionChangedEventManager.AddListener(source, listener1);
// Remove.
CollectionChangedEventManager.RemoveListener(source, listener2);
source.OnCollectionChanged(source, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
Assert.Equal(1, callCount1);
Assert.Equal(0, callCount2);
// Remove again.
CollectionChangedEventManager.RemoveListener(source, listener2);
source.OnCollectionChanged(source, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
Assert.Equal(2, callCount1);
Assert.Equal(0, callCount2);
}
[Fact]
public void RemoveListener_NullListener_ThrowsArgumentNullException()
{
var source = new CustomNotifyCollectionChanged();
Assert.Throws<ArgumentNullException>("listener", () => CollectionChangedEventManager.RemoveListener(source, null));
}
private class CustomNotifyCollectionChanged : INotifyCollectionChanged
{
#pragma warning disable CS0067
public event NotifyCollectionChangedEventHandler? CollectionChanged;
#pragma warning restore CS0067
public void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
CollectionChanged?.Invoke(sender, e);
}
}
private class CustomWeakEventListener : IWeakEventListener
{
public Func<Type, object, EventArgs, bool>? ReceiveWeakEventAction { get; set; }
public bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
{
if (ReceiveWeakEventAction is null)
{
return true;
}
return ReceiveWeakEventAction(managerType, sender, e);
}
public Action<object, NotifyCollectionChangedEventArgs>? HandlerAction { get; set; }
public void Handler(object sender, NotifyCollectionChangedEventArgs e)
{
HandlerAction?.Invoke(sender, e);
}
}
}
|