Click here to Skip to main content
15,896,063 members
Articles / Programming Languages / C#

EventBroker: a notification component for synchronous and asynchronous, loosly coupled event handling

Rate me:
Please Sign up or sign in to vote.
4.89/5 (25 votes)
26 Oct 2008Apache9 min read 198.6K   2K   123  
EventBroker is a notification component for (a)synchronous loosly coupled event handling.
//-------------------------------------------------------------------------------
// <copyright file="EventBrokerTest.cs" company="bbv Software Services AG">
//   Copyright (c) 2008 bbv Software Services AG
//
//   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.
// </copyright>
//-------------------------------------------------------------------------------

namespace bbv.Common.EventBroker
{
    using System;
    using System.ComponentModel;
    using System.Threading;
    using bbv.Common.EventBroker.Exceptions;
    using NUnit.Framework;

    /// <summary>
    /// Test for EventBroker.
    /// </summary>
    /// <remarks>
    /// Note that no tests that require a UI thread can be done. --> no tests on <see cref="ThreadOption.UserInterface"/> and <see cref="ThreadOption.UserInterfaceBackground"/>
    /// </remarks>
    [TestFixture]
    public class EventBrokerTest
    {
        EventBroker eb;
        Publisher p;
        Subscriber s;

        [SetUp]
        public void SetUp()
        {
            Subscriber.Count = 0;

            eb = new EventBroker();
            p = new Publisher();
            s = new Subscriber();

            eb.Register(p);
            eb.Register(s);
        }

        [TearDown]
        public void TearDown()
        {
            if (p != null) eb.Unregister(p);
            if (s != null) eb.Unregister(s);

            eb.Dispose();
        }

        #region SimpleEvent

        [Test]
        public void SimpleEvent()
        {
            p.CallSimpleEvent();

            Assert.IsTrue(s.SimpleEventCalled);
        }
                
        [Test]
        public void SimpleEvent2Subscribers()
        {
            Subscriber s2 = new Subscriber();
            eb.Register(s2);

            p.CallSimpleEvent();

            Assert.IsTrue(s.SimpleEventCalled);
            Assert.IsTrue(s2.SimpleEventCalled);
        }

        #endregion

        #region CustomEventArgs

        [Test]
        public void CustomEventArgs()
        {
            const string value = "Test";
            string v = value;

            p.CallCustomEventArgs(v);

            Assert.IsNotNull(s.ReceivedCustomEventArguments);
            Assert.AreEqual(value, s.ReceivedCustomEventArguments.String);
        }

        #endregion

        #region BackgroundThread

        [Test]
        public void BackgroundThread()
        {
            p.CallBackgroundThread();

            Assert.IsTrue(s.Signal.WaitOne(1000, false), "signal not set.");

            Assert.AreNotEqual(-1, s.ThreadId);
            Assert.AreNotEqual(Thread.CurrentThread.ManagedThreadId, s.ThreadId);
        }

        #endregion

        #region MultiplePublicationTokens

        [Test]
        public void MultiplePublicationTokens()
        {
            p.CallMultiplePublicationTokens();

            Assert.IsTrue(s.MultiplePublicationToken1Called);
            Assert.IsTrue(s.MultiplePublicationToken2Called);
            Assert.IsTrue(s.MultiplePublicationToken3Called);
        }

        #endregion

        #region MultipleSubscriptionTokens

        [Test]
        public void MultipleSubscriptionTokens()
        {
            p.CallMultipleSubscriptionTokens();

            Assert.AreEqual(3, s.MultipleSubscriptionTokensCount);
        }

        #endregion

        #region CancelEventArgs

        [Test]
        public void CancelEventArgs()
        {
            bool cancel = p.CallCancelEvent();

            Assert.IsTrue(cancel, "Result from event call should be true. Return value could not be given back from handler.");
        }

        #endregion
    }

    [TestFixture]
    public class EventBrokerCountTest
    {
        [Test]
        public void PublisherWithoutSubscriber()
        {
            EventBroker eb = new EventBroker();

            Publisher p = new Publisher();
            Subscriber.Count = 0;

            eb.Register(p);

            p.CallCount();

            eb.Unregister(p);

            Assert.AreEqual(0, Subscriber.Count);            
        }

        [Test]
        public void MultiplePublisherMultipleSubscriber()
        {
            EventBroker eb = new EventBroker();
            Subscriber.Count = 0;


            Publisher p1 = new Publisher();
            Publisher p2 = new Publisher();

            Subscriber s1 = new Subscriber();
            Subscriber s2 = new Subscriber();
            Subscriber s3 = new Subscriber();

            eb.Register(p1);
            eb.Register(p2);
            eb.Register(s1);
            eb.Register(s2);
            eb.Register(s3);

            p1.CallCount();
            p2.CallCount();

            eb.Unregister(p1);
            eb.Unregister(p2);
            eb.Unregister(s1);
            eb.Unregister(s2);
            eb.Unregister(s3); 

            Assert.AreEqual(6, Subscriber.Count);
        }
    }

    [TestFixture]
    public class EventBrokerCleanupTest
    {
        EventBroker eb;

        [SetUp]
        public void SetUp()
        {
            eb = new EventBroker();
            Subscriber.Count = 0;
        }

        [Test]
        public void FreedSubscriber()
        {
            Publisher p = new Publisher();
            Subscriber s1 = new Subscriber();
            Subscriber s2 = new Subscriber();

            eb.Register(p);
            eb.Register(s1);
            eb.Register(s2);

            s1 = null;
            GC.Collect();  // breaks up the weak reference to the subscriber

            p.CallCount();

            Assert.AreEqual(1, Subscriber.Count);

            Assert.Greater(s2.ThreadId, -10); // just some stupid code to prevent s2 from being collected
        }
    }

    [TestFixture]
    public class EventBrokerExceptionTest
    {
        EventBroker eb;

        [SetUp]
        public void SetUp()
        {
            eb = new EventBroker();
        }

        #region Multiple Publications

        [Test]
        [ExpectedException(typeof(Exceptions.RepeatedPublicationException))]
        public void MultiplePublication()
        {
            InvalidPublisherRepeatedEventPublication p = new InvalidPublisherRepeatedEventPublication();

            eb.Register(p);
        }

        #endregion

        #region Wrong Signature 

        [Test]
        [ExpectedException(typeof(Exceptions.InvalidPublicationSignatureException))]
        public void WrongEventSignature()
        {
            InvalidPublisherWrongEventSignature p = new InvalidPublisherWrongEventSignature();

            eb.Register(p);
        }

        [Test]
        [ExpectedException(typeof(Exceptions.InvalidSubscriptionSignatureException))]
        public void WrongSubscriptionSignature()
        {
            InvalidSubscriberWrongSignature s = new InvalidSubscriberWrongSignature();

            eb.Register(s);
        }

        #endregion

        #region Exception Handling

        [Test]
        public void ExceptionHandling()
        {
            try
            {
                Publisher p = new Publisher();
                SubscriberThrowingException s = new SubscriberThrowingException();

                eb.Register(p);
                eb.Register(s);

                p.CallSimpleEvent();

                Assert.Fail("must not be reached.");
            }
            catch (EventTopicException e)
            {
                Assert.IsTrue(e.InnerException is SubscriberThrowingException.TestException);
            }
        }

        [Test]
        [ExpectedException(typeof(EventTopicException))]
        public void ExceptionHandlingMultipleException()
        {
            Publisher p = new Publisher();
            SubscriberThrowingException s1 = new SubscriberThrowingException();
            SubscriberThrowingException s2 = new SubscriberThrowingException();

            eb.Register(p);
            eb.Register(s1);
            eb.Register(s2);

            p.CallSimpleEvent();
        }

        #endregion

        #region Static Event / Handler

        [Test]
        [ExpectedException(typeof(Exceptions.StaticPublisherEventException))]
        public void StaticEvent()
        {
            InvalidPublisherStaticEvent p = new InvalidPublisherStaticEvent();

            eb.Register(p);
        }

        [Test]
        [ExpectedException(typeof(Exceptions.StaticSubscriberHandlerException))]
        public void StaticHandler()
        {
            InvalidSubscriberStaticHandler s = new InvalidSubscriberStaticHandler();

            eb.Register(s);
        }

        #endregion

        #region Not User Interface Thread

        [Test]
        [ExpectedException(typeof(NotUserInterfaceThreadException))]
        public void NotInterfaceThread()
        {
            eb.Register(new UserInterfaceSubscriber());
        }

        [Test]
        public void CheckOnUserInterfaceThreadCanBeSwitchedOff()
        {
            eb = new EventBroker(new UnitTestFactory());
            eb.Register(new UserInterfaceSubscriber());
        }

        #endregion
    }

    [TestFixture]
    public class FireEventsDirectlyOnEventBrokerTest
    {
        EventBroker eb;

        [SetUp]
        public void SetUp()
        {
            eb = new EventBroker();
        }

        [Test]
        public void FireEvent()
        {
            Subscriber s = new Subscriber();
            eb.Register(s);

            eb.Fire(EventTopics.SimpleEvent, this, EventArgs.Empty);

            Assert.IsTrue(s.SimpleEventCalled, "event was not received.");
        }
    }

    public static class EventTopics
    {
        public const string SimpleEvent = "topic://EventBrokerTest/SimpleEvent";
        public const string CustomEventArgs = "topic://EventBrokerTest/CustomEventArgs";
        public const string BackgroundThread = "topic://EventBrokerTest/BackgroundThread";
        public const string CreatorThread = "topic://EventBrokerTest/CreatorThread";
        public const string Count = "topic://EventBrokerTest/Count";
        public const string MultiplePublicationTokens = "topic://EventBroker/MultiplePublicationTokens";
        public const string MultipleSubscriptionTokens = "topic://EventBroker/MultipleSubscriptionTokens";
        public const string Private = "topic://EventBrokerTest/Private";
        public const string CancelEventArgs = "topic://EventBrokerTest/CancelEventArgs";
    }

    public class Publisher
    {
        #region SimpleEvent

        [EventPublication(EventTopics.SimpleEvent)]
        public event EventHandler SimpleEvent;

        public void CallSimpleEvent()
        {
            SimpleEvent(this, EventArgs.Empty);
        }

        #endregion

        #region CustomEventArgs

        [EventPublication(EventTopics.CustomEventArgs)]
        public event EventHandler<CustomEventArguments> CustomEventArgs;

        public void CallCustomEventArgs(string value)
        {
            CustomEventArgs(this, new CustomEventArguments(value));
        }

        #endregion

        #region BackgroundThread

        [EventPublication(EventTopics.BackgroundThread)]
        public event EventHandler BackgroundThread;

        public void CallBackgroundThread()
        {
            BackgroundThread(this, EventArgs.Empty);
        }

        #endregion

        #region Count

        [EventPublication(EventTopics.Count)]
        public event EventHandler Count;

        public void CallCount()
        {
            Count(this, EventArgs.Empty);
        }

        #endregion

        #region MultiplePublicationTokens

        [EventPublication(EventTopics.MultiplePublicationTokens + "1")]
        [EventPublication(EventTopics.MultiplePublicationTokens + "2")]
        [EventPublication(EventTopics.MultiplePublicationTokens + "3")]
        public event EventHandler MultiplePublicationTokens;

        public void CallMultiplePublicationTokens()
        {
            MultiplePublicationTokens(this, EventArgs.Empty);
        }

        #endregion

        #region MultipleSubscriptionTokens

        [EventPublication(EventTopics.MultipleSubscriptionTokens + "1")]
        public event EventHandler MultipleSubscriptionTokens1;

        [EventPublication(EventTopics.MultipleSubscriptionTokens + "2")]
        public event EventHandler MultipleSubscriptionTokens2;

        [EventPublication(EventTopics.MultipleSubscriptionTokens + "3")]
        public event EventHandler MultipleSubscriptionTokens3;

        public void CallMultipleSubscriptionTokens()
        {
            MultipleSubscriptionTokens1(this, EventArgs.Empty);
            MultipleSubscriptionTokens2(this, EventArgs.Empty);
            MultipleSubscriptionTokens3(this, EventArgs.Empty);
        }

        #endregion

        #region CancelEventArgs

        [EventPublication(EventTopics.CancelEventArgs)]
        public event EventHandler<CancelEventArgs> CancelEvent;

        public bool CallCancelEvent()
        {
            CancelEventArgs e = new CancelEventArgs(false);
            CancelEvent(this, e);

            return e.Cancel;
        }

        #endregion
    }

    public class Subscriber
    {
        #region SimpleEvent

        public bool SimpleEventCalled = false;

        [EventSubscription(EventTopics.SimpleEvent, typeof(Handlers.Publisher))]
        public void SimpleEvent(object sender, EventArgs e)
        {
            SimpleEventCalled = true;
        }

        #endregion

        #region CustomEventArgs

        public CustomEventArguments ReceivedCustomEventArguments = null;

        [EventSubscription(EventTopics.CustomEventArgs, typeof(Handlers.Publisher))]
        public void CustomEventArgs(object sender, CustomEventArguments e)
        {
            ReceivedCustomEventArguments = e;
        }

        #endregion

        #region BackgroundThread

        public int ThreadId = -1;

        public AutoResetEvent Signal = new AutoResetEvent(false);

        [EventSubscription(EventTopics.BackgroundThread, typeof(Handlers.Background))]
        public void BackgroundThread(object sender, EventArgs e)
        {
            ThreadId = Thread.CurrentThread.ManagedThreadId;
            Signal.Set();
        }

        #endregion

        #region Count

        public static int Count = 0;

        [EventSubscription(EventTopics.Count, typeof(Handlers.Publisher))]
        public void CountHandler(object sender, EventArgs e)
        {
            Count++;
        }

        #endregion

        #region MultiplePublicationTokens

        public bool MultiplePublicationToken1Called = false;
        public bool MultiplePublicationToken2Called = false;
        public bool MultiplePublicationToken3Called = false;

        [EventSubscription(EventTopics.MultiplePublicationTokens + "1", typeof(Handlers.Publisher))]
        public void MultiplePublicationTokens1(object sender, EventArgs e)
        {
            MultiplePublicationToken1Called = true;
        }

        [EventSubscription(EventTopics.MultiplePublicationTokens + "2", typeof(Handlers.Publisher))]
        public void MultiplePublicationTokens2(object sender, EventArgs e)
        {
            MultiplePublicationToken2Called = true;
        }

        [EventSubscription(EventTopics.MultiplePublicationTokens + "3", typeof(Handlers.Publisher))]
        public void MultiplePublicationTokens3(object sender, EventArgs e)
        {
            MultiplePublicationToken3Called = true;
        }

        #endregion

        #region MultipleSubscriptionTokens

        public int MultipleSubscriptionTokensCount = 0;

        [EventSubscription(EventTopics.MultipleSubscriptionTokens + "1", typeof(Handlers.Publisher))]
        [EventSubscription(EventTopics.MultipleSubscriptionTokens + "2", typeof(Handlers.Publisher))]
        [EventSubscription(EventTopics.MultipleSubscriptionTokens + "3", typeof(Handlers.Publisher))]
        public void MultipleSubscriptionTokens(object sender, EventArgs e)
        {
            MultipleSubscriptionTokensCount++;
        }

        #endregion

        #region CancelEventArgs

        [EventSubscription(EventTopics.CancelEventArgs, typeof(Handlers.Publisher))]
        public void CancelEventArgs(object sender, CancelEventArgs e)
        {
            e.Cancel = true;
        }

        #endregion

    }

    public class InvalidPublisherRepeatedEventPublication
    {
        [EventPublication(EventTopics.SimpleEvent)]
        [EventPublication(EventTopics.SimpleEvent)]
        public event EventHandler SimpleEvent1;

        public void CallSimpleEvent1()
        {
            SimpleEvent1(this, EventArgs.Empty);
        }
    }

    public class InvalidPublisherWrongEventSignature
    {
        public delegate int MyEventHandler(string name);

        [EventPublication(EventTopics.SimpleEvent)]
        public event MyEventHandler SimpleEvent;        
    }

    public class InvalidPublisherPrivateEvent
    {
        [EventPublication(EventTopics.SimpleEvent)]
        private event EventHandler SimpleEvent;
    }

    public class InvalidPublisherStaticEvent
    {
        [EventPublication(EventTopics.SimpleEvent)]
        static public event EventHandler SimpleEvent;
    }

    public class InvalidSubscriberWrongSignature
    {
        [EventSubscription(EventTopics.SimpleEvent, typeof(Handlers.Publisher))]
        public void SimpleEvent(EventArgs e)
        {
        }
    }

    public class InvalidSubscriberWrongSignatureWrongEventArgs
    {
        [EventSubscription(EventTopics.SimpleEvent, typeof(Handlers.Publisher))]
        public void SimpleEvent(object sender, CustomEventArguments e)
        {
        }
    }

    public class SubscriberThrowingException
    {
        public class TestException : Exception
        {
        }

        [EventSubscription(EventTopics.SimpleEvent, typeof(Handlers.Publisher))]
        public void SimpleEvent(object sender, EventArgs e)
        {
            throw new TestException();
        }
    }

    public class SubscriberThrowingExceptionBackgroundWorker
    {
        public class TestException : Exception
        {
        }

        [EventSubscription(EventTopics.SimpleEvent, typeof(Handlers.Background))]
        public void SimpleEvent(object sender, EventArgs e)
        {
            throw new TestException();
        }
    }

    public class InvalidSubscriberStaticHandler
    {
        [EventSubscription(EventTopics.SimpleEvent, typeof(Handlers.Publisher))]
        public static void SimpleEvent(object sender, EventArgs e)
        {
        }
    }

    public class UserInterfaceSubscriber
    {
        [EventSubscription(EventTopics.SimpleEvent, typeof(Handlers.UserInterface))]
        public void SimpleEvent(object sender, EventArgs e)
        {
        }
    }

    #region CustomEventArgs

    public class CustomEventArguments : EventArgs
    {
        public CustomEventArguments(string s)
        {
            String = s;
        }

        public string String;
    }

    #endregion
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Apache License, Version 2.0


Written By
Architect bbv Software Services AG
Switzerland Switzerland
Urs Enzler is working for bbv Software Services in Switzerland as a Software Architect.

Blogger at planetgeek.ch

Comments and Discussions