From 66972e5bc6c6bc2612ec0ff0d7feb54e1244442d Mon Sep 17 00:00:00 2001 From: "kay.one" Date: Sat, 23 Feb 2013 12:09:44 -0800 Subject: [PATCH] simplified EventAggregator --- .../EventingTests/EventAggregatorTests.cs | 58 ++++++ .../NzbDrone.Common.Test.csproj | 1 + NzbDrone.Common/Eventing/EventAggregator.cs | 169 ++---------------- NzbDrone.Common/Eventing/IEventAggregator.cs | 38 +--- .../DatabaseTargetFixture.cs | 110 ++++++++++++ NzbDrone.Test.Common/LoggingTest.cs | 3 + 6 files changed, 186 insertions(+), 193 deletions(-) create mode 100644 NzbDrone.Common.Test/EventingTests/EventAggregatorTests.cs create mode 100644 NzbDrone.Core.Test/InstrumentationTests/DatabaseTargetFixture.cs diff --git a/NzbDrone.Common.Test/EventingTests/EventAggregatorTests.cs b/NzbDrone.Common.Test/EventingTests/EventAggregatorTests.cs new file mode 100644 index 000000000..1c9ea2788 --- /dev/null +++ b/NzbDrone.Common.Test/EventingTests/EventAggregatorTests.cs @@ -0,0 +1,58 @@ +using System.Collections.Generic; +using System.Linq; +using Moq; +using NUnit.Framework; +using NzbDrone.Common.Eventing; +using NzbDrone.Test.Common; + +namespace NzbDrone.Common.Test.EventingTests +{ + [TestFixture] + public class ServiceNameFixture : TestBase + { + private EventAggregator _aggregator; + + [SetUp] + public void Setup() + { + _aggregator = new EventAggregator(TestLogger, null); + } + + [Test] + public void should_publish_event_to_handlers() + { + var intHandler = new Mock>(); + _aggregator = new EventAggregator(TestLogger, new List { intHandler.Object }); + _aggregator.Publish(12); + + intHandler.Verify(c => c.Handle(12), Times.Once()); + } + + [Test] + public void should_publish_to_more_than_one_handler() + { + var intHandler1 =new Mock>(); + var intHandler2 = new Mock>(); + _aggregator = new EventAggregator(TestLogger, new List { intHandler1.Object, intHandler2.Object }); + _aggregator.Publish(12); + + intHandler1.Verify(c => c.Handle(12), Times.Once()); + intHandler2.Verify(c => c.Handle(12), Times.Once()); + } + + [Test] + public void should_not_publish_to_incompatible_handlers() + { + var intHandler = new Mock>(); + var stringHandler = new Mock>(); + _aggregator = new EventAggregator(TestLogger, new List { intHandler.Object, stringHandler.Object }); + + _aggregator.Publish(12); + + intHandler.Verify(c => c.Handle(12), Times.Once()); + stringHandler.Verify(c => c.Handle(It.IsAny()), Times.Never()); + } + + } + +} \ No newline at end of file diff --git a/NzbDrone.Common.Test/NzbDrone.Common.Test.csproj b/NzbDrone.Common.Test/NzbDrone.Common.Test.csproj index c2cad225c..ca81e25e4 100644 --- a/NzbDrone.Common.Test/NzbDrone.Common.Test.csproj +++ b/NzbDrone.Common.Test/NzbDrone.Common.Test.csproj @@ -86,6 +86,7 @@ + diff --git a/NzbDrone.Common/Eventing/EventAggregator.cs b/NzbDrone.Common/Eventing/EventAggregator.cs index bb76f8479..44648525b 100644 --- a/NzbDrone.Common/Eventing/EventAggregator.cs +++ b/NzbDrone.Common/Eventing/EventAggregator.cs @@ -1,173 +1,28 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; -using System.Reflection; - -//From http://caliburnmicro.codeplex.com/ +using NLog; namespace NzbDrone.Common.Eventing { - /// - /// Enables loosely-coupled publication of and subscription to events. - /// public class EventAggregator : IEventAggregator { - readonly List handlers = new List(); - - /// - /// The default thread marshaller used for publication; - /// - public static Action DefaultPublicationThreadMarshaller = action => action(); - - /// - /// Processing of handler results on publication thread. - /// - public static Action HandlerResultProcessing = (target, result) => { }; - - /// - /// Initializes a new instance of the class. - /// - public EventAggregator() - { - PublicationThreadMarshaller = DefaultPublicationThreadMarshaller; - } - - /// - /// Gets or sets the default publication thread marshaller. - /// - /// - /// The default publication thread marshaller. - /// - public Action PublicationThreadMarshaller { get; set; } - - /// - /// Subscribes an instance to all events declared through implementations of - /// - /// The instance to subscribe for event publication. - public virtual void Subscribe(object instance) - { - lock (handlers) - { - if (handlers.Any(x => x.Matches(instance))) - { - return; - } + private readonly Logger _logger; + private readonly IEnumerable _handlers; - handlers.Add(new Handler(instance)); - } - } - - /// - /// Unsubscribes the instance from all events. - /// - /// The instance to unsubscribe. - public virtual void Unsubscribe(object instance) + public EventAggregator(Logger logger, IEnumerable handlers) { - lock (handlers) - { - var found = handlers.FirstOrDefault(x => x.Matches(instance)); - - if (found != null) - { - handlers.Remove(found); - } - } + _logger = logger; + _handlers = handlers; } - /// - /// Publishes a message. - /// - /// The message instance. - /// - /// Does not marshall the the publication to any special thread by default. - /// - public virtual void Publish(object message) + public void Publish(TEvent message) { - Publish(message, PublicationThreadMarshaller); - } + _logger.Trace("Publishing {0}", message.GetType().Name); - /// - /// Publishes a message. - /// - /// The message instance. - /// Allows the publisher to provide a custom thread marshaller for the message publication. - public virtual void Publish(object message, Action marshal) - { - Handler[] toNotify; - lock (handlers) + foreach (var handler in _handlers.OfType>()) { - toNotify = handlers.ToArray(); - } - - marshal(() => - { - var messageType = message.GetType(); - - var dead = toNotify - .Where(handler => !handler.Handle(messageType, message)) - .ToList(); - - if (dead.Any()) - { - lock (handlers) - { - foreach (var item in dead) - { - handlers.Remove(item); - } - } - } - }); - } - - - class Handler - { - readonly WeakReference reference; - readonly Dictionary supportedHandlers = new Dictionary(); - - public Handler(object handler) - { - reference = new WeakReference(handler); - - var interfaces = handler.GetType().GetInterfaces() - .Where(x => typeof(IHandle).IsAssignableFrom(x) && x.IsGenericType); - - foreach (var @interface in interfaces) - { - var type = @interface.GetGenericArguments()[0]; - var method = @interface.GetMethod("Handle"); - supportedHandlers[type] = method; - } - } - - public bool Matches(object instance) - { - return reference.Target == instance; - } - - public bool Handle(Type messageType, object message) - { - var target = reference.Target; - if (target == null) - { - return false; - } - - foreach (var pair in supportedHandlers) - { - if (pair.Key.IsAssignableFrom(messageType)) - { - var result = pair.Value.Invoke(target, new[] { message }); - if (result != null) - { - HandlerResultProcessing(target, result); - } - return true; - } - } - - return true; + _logger.Trace("{0} => {1}", message.GetType().Name, handler.GetType().Name); + handler.Handle(message); } } } diff --git a/NzbDrone.Common/Eventing/IEventAggregator.cs b/NzbDrone.Common/Eventing/IEventAggregator.cs index 369b8a8f1..6ed3b8270 100644 --- a/NzbDrone.Common/Eventing/IEventAggregator.cs +++ b/NzbDrone.Common/Eventing/IEventAggregator.cs @@ -4,44 +4,10 @@ using System.Linq; namespace NzbDrone.Common.Eventing { /// - /// Enables loosely-coupled publication of and subscription to events. + /// Enables loosely-coupled publication of events. /// public interface IEventAggregator { - /// - /// Gets or sets the default publication thread marshaller. - /// - /// - /// The default publication thread marshaller. - /// - Action PublicationThreadMarshaller { get; set; } - - /// - /// Subscribes an instance to all events declared through implementations of - /// - /// The instance to subscribe for event publication. - void Subscribe(object instance); - - /// - /// Unsubscribes the instance from all events. - /// - /// The instance to unsubscribe. - void Unsubscribe(object instance); - - /// - /// Publishes a message. - /// - /// The message instance. - /// - /// Uses the default thread marshaller during publication. - /// - void Publish(object message); - - /// - /// Publishes a message. - /// - /// The message instance. - /// Allows the publisher to provide a custom thread marshaller for the message publication. - void Publish(object message, Action marshal); + void Publish(TEvent message); } } \ No newline at end of file diff --git a/NzbDrone.Core.Test/InstrumentationTests/DatabaseTargetFixture.cs b/NzbDrone.Core.Test/InstrumentationTests/DatabaseTargetFixture.cs new file mode 100644 index 000000000..e8d786c47 --- /dev/null +++ b/NzbDrone.Core.Test/InstrumentationTests/DatabaseTargetFixture.cs @@ -0,0 +1,110 @@ +using System; +using System.Diagnostics; +using System.Linq; +using FluentAssertions; +using NLog; +using NUnit.Framework; +using NzbDrone.Core.Tv; +using NzbDrone.Core.Instrumentation; +using NzbDrone.Core.Test.Framework; +using NzbDrone.Test.Common; + +namespace NzbDrone.Core.Test.InstrumentationTests +{ + [TestFixture] + public class DatabaseTargetFixture : ObjectDbTest + { + string _loggerName; + + private static string _uniqueMessage; + Logger _logger; + + [SetUp] + public void Setup() + { + Mocker.Resolve(); + Mocker.Resolve().Register(); + + _logger = LogManager.GetCurrentClassLogger(); + _loggerName = _logger.Name.Replace("NzbDrone.",""); + + _uniqueMessage = "Unique message: " + Guid.NewGuid().ToString(); + } + + [Test] + public void write_log() + { + _logger.Info(_uniqueMessage); + + StoredModel.Message.Should().Be(_uniqueMessage); + VerifyLog(StoredModel, LogLevel.Info); + } + + [Test] + public void write_long_log() + { + var message = String.Empty; + for (int i = 0; i < 100; i++) + { + message += Guid.NewGuid(); + } + + _logger.Info(message); + + StoredModel.Message.Should().HaveLength(message.Length); + StoredModel.Message.Should().Be(message); + VerifyLog(StoredModel, LogLevel.Info); + } + + [Test] + public void write_log_exception() + { + var ex = new InvalidOperationException("Fake Exception"); + + _logger.ErrorException(_uniqueMessage, ex); + + + VerifyLog(StoredModel, LogLevel.Error); + StoredModel.Message.Should().Be(_uniqueMessage + ": " + ex.Message); + StoredModel.ExceptionType.Should().Be(ex.GetType().ToString()); + StoredModel.Exception.Should().Be(ex.ToString()); + + ExceptionVerification.ExpectedErrors(1); + } + + [Test] + public void exception_log_with_no_message_should_use_exceptions_message() + { + + var ex = new InvalidOperationException("Fake Exception"); + _uniqueMessage = String.Empty; + + + _logger.ErrorException(_uniqueMessage, ex); + + StoredModel.Message.Should().Be(ex.Message); + + VerifyLog(StoredModel, LogLevel.Error); + + ExceptionVerification.ExpectedErrors(1); + } + + [Test] + public void null_string_as_arg_should_not_fail() + { + var epFile = new EpisodeFile(); + _logger.Trace("File {0} no longer exists on disk. removing from database.", epFile.Path); + + epFile.Path.Should().BeNull(); + } + + private void VerifyLog(Log logItem, LogLevel level) + { + logItem.Time.Should().BeWithin(TimeSpan.FromSeconds(2)); + logItem.Logger.Should().Be(_loggerName); + logItem.Level.Should().Be(level.Name); + logItem.Method.Should().Be(new StackTrace().GetFrame(1).GetMethod().Name); + _logger.Name.Should().EndWith(logItem.Logger); + } + } +} diff --git a/NzbDrone.Test.Common/LoggingTest.cs b/NzbDrone.Test.Common/LoggingTest.cs index 83acc938a..78788d3ff 100644 --- a/NzbDrone.Test.Common/LoggingTest.cs +++ b/NzbDrone.Test.Common/LoggingTest.cs @@ -7,6 +7,9 @@ namespace NzbDrone.Test.Common { public abstract class LoggingTest { + + protected Logger TestLogger = LogManager.GetLogger("TestLogger"); + protected static void InitLogging() { if (LogManager.Configuration == null || LogManager.Configuration is XmlLoggingConfiguration)