442 строки
13 KiB
C#
442 строки
13 KiB
C#
using System;
|
|
using NUnit.Framework;
|
|
|
|
namespace Xamarin.Forms.Core.UnitTests
|
|
{
|
|
[TestFixture]
|
|
public class MessagingCenterTests : BaseTestFixture
|
|
{
|
|
TestSubcriber _subscriber;
|
|
|
|
[Test]
|
|
public void SingleSubscriber ()
|
|
{
|
|
string sentMessage = null;
|
|
MessagingCenter.Subscribe<MessagingCenterTests, string> (this, "SimpleTest", (sender, args) => sentMessage = args);
|
|
|
|
MessagingCenter.Send (this, "SimpleTest", "My Message");
|
|
|
|
Assert.That (sentMessage, Is.EqualTo ("My Message"));
|
|
|
|
MessagingCenter.Unsubscribe<MessagingCenterTests, string> (this, "SimpleTest");
|
|
}
|
|
|
|
[Test]
|
|
public void Filter ()
|
|
{
|
|
string sentMessage = null;
|
|
MessagingCenter.Subscribe<MessagingCenterTests, string> (this, "SimpleTest", (sender, args) => sentMessage = args, this);
|
|
|
|
MessagingCenter.Send (new MessagingCenterTests (), "SimpleTest", "My Message");
|
|
|
|
Assert.That (sentMessage, Is.Null);
|
|
|
|
MessagingCenter.Send (this, "SimpleTest", "My Message");
|
|
|
|
Assert.That (sentMessage, Is.EqualTo ("My Message"));
|
|
|
|
MessagingCenter.Unsubscribe<MessagingCenterTests, string> (this, "SimpleTest");
|
|
}
|
|
|
|
[Test]
|
|
public void MultiSubscriber ()
|
|
{
|
|
var sub1 = new object ();
|
|
var sub2 = new object ();
|
|
string sentMessage1 = null;
|
|
string sentMessage2 = null;
|
|
MessagingCenter.Subscribe<MessagingCenterTests, string> (sub1, "SimpleTest", (sender, args) => sentMessage1 = args);
|
|
MessagingCenter.Subscribe<MessagingCenterTests, string> (sub2, "SimpleTest", (sender, args) => sentMessage2 = args);
|
|
|
|
MessagingCenter.Send (this, "SimpleTest", "My Message");
|
|
|
|
Assert.That (sentMessage1, Is.EqualTo ("My Message"));
|
|
Assert.That (sentMessage2, Is.EqualTo ("My Message"));
|
|
|
|
MessagingCenter.Unsubscribe<MessagingCenterTests, string> (sub1, "SimpleTest");
|
|
MessagingCenter.Unsubscribe<MessagingCenterTests, string> (sub2, "SimpleTest");
|
|
}
|
|
|
|
[Test]
|
|
public void Unsubscribe ()
|
|
{
|
|
string sentMessage = null;
|
|
MessagingCenter.Subscribe<MessagingCenterTests, string> (this, "SimpleTest", (sender, args) => sentMessage = args);
|
|
MessagingCenter.Unsubscribe<MessagingCenterTests, string> (this, "SimpleTest");
|
|
|
|
MessagingCenter.Send (this, "SimpleTest", "My Message");
|
|
|
|
Assert.That (sentMessage, Is.EqualTo (null));
|
|
}
|
|
|
|
[Test]
|
|
public void SendWithoutSubscribers ()
|
|
{
|
|
Assert.DoesNotThrow (() => MessagingCenter.Send (this, "SimpleTest", "My Message"));
|
|
}
|
|
|
|
[Test]
|
|
public void NoArgSingleSubscriber ()
|
|
{
|
|
bool sentMessage = false;
|
|
MessagingCenter.Subscribe<MessagingCenterTests> (this, "SimpleTest", sender => sentMessage = true);
|
|
|
|
MessagingCenter.Send (this, "SimpleTest");
|
|
|
|
Assert.That (sentMessage, Is.True);
|
|
|
|
MessagingCenter.Unsubscribe<MessagingCenterTests> (this, "SimpleTest");
|
|
}
|
|
|
|
[Test]
|
|
public void NoArgFilter ()
|
|
{
|
|
bool sentMessage = false;
|
|
MessagingCenter.Subscribe (this, "SimpleTest", (sender) => sentMessage = true, this);
|
|
|
|
MessagingCenter.Send (new MessagingCenterTests (), "SimpleTest");
|
|
|
|
Assert.That (sentMessage, Is.False);
|
|
|
|
MessagingCenter.Send (this, "SimpleTest");
|
|
|
|
Assert.That (sentMessage, Is.True);
|
|
|
|
MessagingCenter.Unsubscribe<MessagingCenterTests> (this, "SimpleTest");
|
|
}
|
|
|
|
[Test]
|
|
public void NoArgMultiSubscriber ()
|
|
{
|
|
var sub1 = new object ();
|
|
var sub2 = new object ();
|
|
bool sentMessage1 = false;
|
|
bool sentMessage2 = false;
|
|
MessagingCenter.Subscribe<MessagingCenterTests> (sub1, "SimpleTest", (sender) => sentMessage1 = true);
|
|
MessagingCenter.Subscribe<MessagingCenterTests> (sub2, "SimpleTest", (sender) => sentMessage2 = true);
|
|
|
|
MessagingCenter.Send (this, "SimpleTest");
|
|
|
|
Assert.That (sentMessage1, Is.True);
|
|
Assert.That (sentMessage2, Is.True);
|
|
|
|
MessagingCenter.Unsubscribe<MessagingCenterTests> (sub1, "SimpleTest");
|
|
MessagingCenter.Unsubscribe<MessagingCenterTests> (sub2, "SimpleTest");
|
|
}
|
|
|
|
[Test]
|
|
public void NoArgUnsubscribe ()
|
|
{
|
|
bool sentMessage = false;
|
|
MessagingCenter.Subscribe<MessagingCenterTests> (this, "SimpleTest", (sender) => sentMessage = true);
|
|
MessagingCenter.Unsubscribe<MessagingCenterTests> (this, "SimpleTest");
|
|
|
|
MessagingCenter.Send (this, "SimpleTest", "My Message");
|
|
|
|
Assert.That (sentMessage, Is.False);
|
|
}
|
|
|
|
[Test]
|
|
public void NoArgSendWithoutSubscribers ()
|
|
{
|
|
Assert.DoesNotThrow (() => MessagingCenter.Send (this, "SimpleTest"));
|
|
}
|
|
|
|
[Test]
|
|
public void ThrowOnNullArgs ()
|
|
{
|
|
Assert.Throws<ArgumentNullException> (() => MessagingCenter.Subscribe<MessagingCenterTests, string> (null, "Foo", (sender, args) => { }));
|
|
Assert.Throws<ArgumentNullException> (() => MessagingCenter.Subscribe<MessagingCenterTests, string> (this, null, (sender, args) => { }));
|
|
Assert.Throws<ArgumentNullException> (() => MessagingCenter.Subscribe<MessagingCenterTests, string> (this, "Foo", null));
|
|
|
|
Assert.Throws<ArgumentNullException> (() => MessagingCenter.Subscribe<MessagingCenterTests> (null, "Foo", (sender) => { }));
|
|
Assert.Throws<ArgumentNullException> (() => MessagingCenter.Subscribe<MessagingCenterTests> (this, null, (sender) => { }));
|
|
Assert.Throws<ArgumentNullException> (() => MessagingCenter.Subscribe<MessagingCenterTests> (this, "Foo", null));
|
|
|
|
Assert.Throws<ArgumentNullException> (() => MessagingCenter.Send<MessagingCenterTests, string> (null, "Foo", "Bar"));
|
|
Assert.Throws<ArgumentNullException> (() => MessagingCenter.Send<MessagingCenterTests, string> (this, null, "Bar"));
|
|
|
|
Assert.Throws<ArgumentNullException> (() => MessagingCenter.Send<MessagingCenterTests> (null, "Foo"));
|
|
Assert.Throws<ArgumentNullException> (() => MessagingCenter.Send<MessagingCenterTests> (this, null));
|
|
|
|
Assert.Throws<ArgumentNullException> (() => MessagingCenter.Unsubscribe<MessagingCenterTests> (null, "Foo"));
|
|
Assert.Throws<ArgumentNullException> (() => MessagingCenter.Unsubscribe<MessagingCenterTests> (this, null));
|
|
|
|
Assert.Throws<ArgumentNullException> (() => MessagingCenter.Unsubscribe<MessagingCenterTests, string> (null, "Foo"));
|
|
Assert.Throws<ArgumentNullException> (() => MessagingCenter.Unsubscribe<MessagingCenterTests, string> (this, null));
|
|
}
|
|
|
|
[Test]
|
|
public void UnsubscribeInCallback ()
|
|
{
|
|
int messageCount = 0;
|
|
|
|
var subscriber1 = new object ();
|
|
var subscriber2 = new object ();
|
|
|
|
MessagingCenter.Subscribe<MessagingCenterTests> (subscriber1, "SimpleTest", (sender) => {
|
|
messageCount++;
|
|
MessagingCenter.Unsubscribe<MessagingCenterTests> (subscriber2, "SimpleTest");
|
|
});
|
|
|
|
MessagingCenter.Subscribe<MessagingCenterTests> (subscriber2, "SimpleTest", (sender) => {
|
|
messageCount++;
|
|
MessagingCenter.Unsubscribe<MessagingCenterTests> (subscriber1, "SimpleTest");
|
|
});
|
|
|
|
MessagingCenter.Send (this, "SimpleTest");
|
|
|
|
Assert.AreEqual (1, messageCount);
|
|
}
|
|
|
|
[Test]
|
|
public void SubscriberShouldBeCollected()
|
|
{
|
|
new Action(() =>
|
|
{
|
|
var subscriber = new TestSubcriber();
|
|
MessagingCenter.Subscribe<TestPublisher>(subscriber, "test", p => Assert.Fail());
|
|
})();
|
|
|
|
GC.Collect();
|
|
GC.WaitForPendingFinalizers();
|
|
|
|
var pub = new TestPublisher();
|
|
pub.Test(); // Assert.Fail() shouldn't be called, because the TestSubcriber object should have ben GCed
|
|
}
|
|
|
|
[Test]
|
|
public void ShouldBeCollectedIfCallbackTargetIsSubscriber()
|
|
{
|
|
WeakReference wr = null;
|
|
|
|
new Action(() =>
|
|
{
|
|
var subscriber = new TestSubcriber();
|
|
|
|
wr = new WeakReference(subscriber);
|
|
|
|
subscriber.SubscribeToTestMessages();
|
|
})();
|
|
|
|
GC.Collect();
|
|
GC.WaitForPendingFinalizers();
|
|
|
|
var pub = new TestPublisher();
|
|
pub.Test();
|
|
|
|
Assert.IsFalse(wr.IsAlive); // The Action target and subscriber were the same object, so both could be collected
|
|
}
|
|
|
|
[Test]
|
|
public void NotCollectedIfSubscriberIsNotTheCallbackTarget()
|
|
{
|
|
WeakReference wr = null;
|
|
|
|
new Action(() =>
|
|
{
|
|
var subscriber = new TestSubcriber();
|
|
|
|
wr = new WeakReference(subscriber);
|
|
|
|
// This creates a closure, so the callback target is not 'subscriber', but an instancce of a compiler generated class
|
|
// So MC has to keep a strong reference to it, and 'subscriber' won't be collectable
|
|
MessagingCenter.Subscribe<TestPublisher>(subscriber, "test", p => subscriber.SetSuccess());
|
|
})();
|
|
|
|
GC.Collect();
|
|
GC.WaitForPendingFinalizers();
|
|
|
|
Assert.IsTrue(wr.IsAlive); // The closure in Subscribe should be keeping the subscriber alive
|
|
Assert.IsNotNull(wr.Target as TestSubcriber);
|
|
|
|
Assert.IsFalse(((TestSubcriber)wr.Target).Successful);
|
|
|
|
var pub = new TestPublisher();
|
|
pub.Test();
|
|
|
|
Assert.IsTrue(((TestSubcriber)wr.Target).Successful); // Since it's still alive, the subscriber should still have received the message and updated the property
|
|
}
|
|
|
|
[Test]
|
|
public void SubscriberCollectableAfterUnsubscribeEvenIfHeldByClosure()
|
|
{
|
|
WeakReference wr = null;
|
|
|
|
new Action(() =>
|
|
{
|
|
var subscriber = new TestSubcriber();
|
|
|
|
wr = new WeakReference(subscriber);
|
|
|
|
MessagingCenter.Subscribe<TestPublisher>(subscriber, "test", p => subscriber.SetSuccess());
|
|
})();
|
|
|
|
Assert.IsNotNull(wr.Target as TestSubcriber);
|
|
|
|
MessagingCenter.Unsubscribe<TestPublisher>(wr.Target, "test");
|
|
|
|
GC.Collect();
|
|
GC.WaitForPendingFinalizers();
|
|
|
|
Assert.IsFalse(wr.IsAlive); // The Action target and subscriber were the same object, so both could be collected
|
|
}
|
|
|
|
[Test]
|
|
public void StaticCallback()
|
|
{
|
|
int i = 4;
|
|
|
|
_subscriber = new TestSubcriber(); // Using a class member so it doesn't get optimized away in Release build
|
|
|
|
MessagingCenter.Subscribe<TestPublisher>(_subscriber, "test", p => MessagingCenterTestsCallbackSource.Increment(ref i));
|
|
|
|
GC.Collect();
|
|
GC.WaitForPendingFinalizers();
|
|
|
|
var pub = new TestPublisher();
|
|
pub.Test();
|
|
|
|
Assert.IsTrue(i == 5, "The static method should have incremented 'i'");
|
|
}
|
|
|
|
[Test]
|
|
public void NothingShouldBeCollected()
|
|
{
|
|
var success = false;
|
|
|
|
_subscriber = new TestSubcriber(); // Using a class member so it doesn't get optimized away in Release build
|
|
|
|
var source = new MessagingCenterTestsCallbackSource();
|
|
MessagingCenter.Subscribe<TestPublisher>(_subscriber, "test", p => source.SuccessCallback(ref success));
|
|
|
|
GC.Collect();
|
|
GC.WaitForPendingFinalizers();
|
|
|
|
var pub = new TestPublisher();
|
|
pub.Test();
|
|
|
|
Assert.True(success); // TestCallbackSource.SuccessCallback() should be invoked to make success == true
|
|
}
|
|
|
|
[Test]
|
|
public void MultipleSubscribersOfTheSameClass()
|
|
{
|
|
var sub1 = new object();
|
|
var sub2 = new object();
|
|
|
|
string args2 = null;
|
|
|
|
const string message = "message";
|
|
|
|
MessagingCenter.Subscribe<MessagingCenterTests, string>(sub1, message, (sender, args) => { });
|
|
MessagingCenter.Subscribe<MessagingCenterTests, string>(sub2, message, (sender, args) => args2 = args);
|
|
MessagingCenter.Unsubscribe<MessagingCenterTests, string>(sub1, message);
|
|
|
|
MessagingCenter.Send(this, message, "Testing");
|
|
Assert.That(args2, Is.EqualTo("Testing"), "unsubscribing sub1 should not unsubscribe sub2");
|
|
}
|
|
|
|
class TestSubcriber
|
|
{
|
|
public void SetSuccess()
|
|
{
|
|
Successful = true;
|
|
}
|
|
|
|
public bool Successful { get; private set; }
|
|
|
|
public void SubscribeToTestMessages()
|
|
{
|
|
MessagingCenter.Subscribe<TestPublisher>(this, "test", p => SetSuccess());
|
|
}
|
|
}
|
|
|
|
class TestPublisher
|
|
{
|
|
public void Test()
|
|
{
|
|
MessagingCenter.Send(this, "test");
|
|
}
|
|
}
|
|
|
|
public class MessagingCenterTestsCallbackSource
|
|
{
|
|
public void SuccessCallback(ref bool success)
|
|
{
|
|
success = true;
|
|
}
|
|
|
|
public static void Increment(ref int i)
|
|
{
|
|
i = i + 1;
|
|
}
|
|
}
|
|
|
|
[Test(Description = "This is a demonstration of what a test with a fake/mock/substitute IMessagingCenter might look like")]
|
|
public void TestMessagingCenterSubstitute()
|
|
{
|
|
var mc = new FakeMessagingCenter();
|
|
|
|
// In the real world, you'd construct this with `new ComponentWithMessagingDependency(MessagingCenter.Instance);`
|
|
var component = new ComponentWithMessagingDependency(mc);
|
|
component.DoAThing();
|
|
|
|
Assert.IsTrue(mc.WasSubscribeCalled, "ComponentWithMessagingDependency should have subscribed in its constructor");
|
|
Assert.IsTrue(mc.WasSendCalled, "The DoAThing method should send a message");
|
|
}
|
|
|
|
class ComponentWithMessagingDependency
|
|
{
|
|
readonly IMessagingCenter _messagingCenter;
|
|
|
|
public ComponentWithMessagingDependency(IMessagingCenter messagingCenter)
|
|
{
|
|
_messagingCenter = messagingCenter;
|
|
_messagingCenter.Subscribe<ComponentWithMessagingDependency>(this, "test", dependency => Console.WriteLine("test"));
|
|
}
|
|
|
|
public void DoAThing()
|
|
{
|
|
_messagingCenter.Send(this, "test");
|
|
}
|
|
}
|
|
|
|
internal class FakeMessagingCenter : IMessagingCenter
|
|
{
|
|
public bool WasSubscribeCalled { get; private set; }
|
|
public bool WasSendCalled { get; private set; }
|
|
|
|
public void Send<TSender, TArgs>(TSender sender, string message, TArgs args) where TSender : class
|
|
{
|
|
WasSendCalled = true;
|
|
}
|
|
|
|
public void Send<TSender>(TSender sender, string message) where TSender : class
|
|
{
|
|
WasSendCalled = true;
|
|
}
|
|
|
|
public void Subscribe<TSender, TArgs>(object subscriber, string message, Action<TSender, TArgs> callback, TSender source = default(TSender)) where TSender : class
|
|
{
|
|
WasSubscribeCalled = true;
|
|
}
|
|
|
|
public void Subscribe<TSender>(object subscriber, string message, Action<TSender> callback, TSender source = default(TSender)) where TSender : class
|
|
{
|
|
WasSubscribeCalled = true;
|
|
}
|
|
|
|
public void Unsubscribe<TSender, TArgs>(object subscriber, string message) where TSender : class
|
|
{
|
|
|
|
}
|
|
|
|
public void Unsubscribe<TSender>(object subscriber, string message) where TSender : class
|
|
{
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|