Index: Core/Common/src/Core.Common.Gui/Forms/MessageWindow/IMessageWindow.cs =================================================================== diff -u -rfbb37872d09f1ded75ce2209e8e48a6b64d8a78f -r07138211a0efc377b1c9ef29a8e86178e8d90ab3 --- Core/Common/src/Core.Common.Gui/Forms/MessageWindow/IMessageWindow.cs (.../IMessageWindow.cs) (revision fbb37872d09f1ded75ce2209e8e48a6b64d8a78f) +++ Core/Common/src/Core.Common.Gui/Forms/MessageWindow/IMessageWindow.cs (.../IMessageWindow.cs) (revision 07138211a0efc377b1c9ef29a8e86178e8d90ab3) @@ -25,27 +25,29 @@ namespace Core.Common.Gui.Forms.MessageWindow { + /// + /// Interface declaring the members for a view that can show log messages. + /// public interface IMessageWindow : IView { /// - /// Adds logging event as a log4net event to the window. - /// Only some columns are added. + /// Adds a logging message to the view. /// - /// - /// - /// + /// Type of logging message. + /// Time when the message was logged. + /// The message text. void AddMessage(Level level, DateTime time, string message); /// - /// Clears all messages in the window. + /// Clears all messages in the view. /// void Clear(); /// - /// Returns true if message level is enabled in the window (can be shown). + /// Indicates if a given logging-level is enabled or not. /// - /// - /// + /// The type of logging message to check. + /// true is the particular logging-level is enabled; false otherwise. bool IsMessageLevelEnabled(Level level); } } \ No newline at end of file Index: Core/Common/src/Core.Common.Gui/Forms/MessageWindow/MessageWindow.cs =================================================================== diff -u -rfbb37872d09f1ded75ce2209e8e48a6b64d8a78f -r07138211a0efc377b1c9ef29a8e86178e8d90ab3 --- Core/Common/src/Core.Common.Gui/Forms/MessageWindow/MessageWindow.cs (.../MessageWindow.cs) (revision fbb37872d09f1ded75ce2209e8e48a6b64d8a78f) +++ Core/Common/src/Core.Common.Gui/Forms/MessageWindow/MessageWindow.cs (.../MessageWindow.cs) (revision 07138211a0efc377b1c9ef29a8e86178e8d90ab3) @@ -29,42 +29,58 @@ namespace Core.Common.Gui.Forms.MessageWindow { + /// + /// Class that receives messages from to be displayed + /// in a thread-safe way. This view supports filtering particular logging levels. + /// public partial class MessageWindow : UserControl, IMessageWindow { - public event EventHandler OnError; - private readonly IWin32Window owner; + #region Constants referring to the item-names of the ImageList + + private const string errorLevelImageName = "exclamation.png"; + private const string warningLevelImageName = "error.png"; + private const string informationLevelImageName = "information.png"; + private const string debugLevelImageName = "debug.png"; + + #endregion + + private readonly IWin32Window dialogParent; private readonly Dictionary levelImageName; private readonly ConcurrentQueue newMessages = new ConcurrentQueue(); private bool filtering; - public MessageWindow(IWin32Window owner) + /// + /// Initializes a new instance of the class. + /// + /// The dialog parent for which dialogs should be shown on top. + public MessageWindow(IWin32Window dialogParent) { - this.owner = owner; + this.dialogParent = dialogParent; Text = Resources.MessageWindow_MessageWindow_Messages; - MessageWindowLogAppender.MessageWindow = this; + MessageWindowLogAppender.Instance.MessageWindow = this; InitializeComponent(); levelImageName = new Dictionary(); // order is the same as in log4j Level (check sources of log4net) - levelImageName[Level.Off.ToString()] = "exclamation.png"; - levelImageName[Level.Emergency.ToString()] = "exclamation.png"; - levelImageName[Level.Fatal.ToString()] = "exclamation.png"; - levelImageName[Level.Alert.ToString()] = "exclamation.png"; - levelImageName[Level.Critical.ToString()] = "exclamation.png"; - levelImageName[Level.Severe.ToString()] = "exclamation.png"; - levelImageName[Level.Error.ToString()] = "exclamation.png"; - levelImageName[Level.Warn.ToString()] = "error.png"; - levelImageName[Level.Notice.ToString()] = "error.png"; - levelImageName[Level.Info.ToString()] = "information.png"; - levelImageName[Level.Debug.ToString()] = "debug.png"; - levelImageName[Level.Fine.ToString()] = "debug.png"; - levelImageName[Level.Trace.ToString()] = "debug.png"; - levelImageName[Level.Finer.ToString()] = "debug.png"; - levelImageName[Level.Verbose.ToString()] = "debug.png"; - levelImageName[Level.Finest.ToString()] = "debug.png"; - levelImageName[Level.All.ToString()] = "debug.png"; + levelImageName[Level.Off.ToString()] = errorLevelImageName; + levelImageName[Level.Emergency.ToString()] = errorLevelImageName; + levelImageName[Level.Fatal.ToString()] = errorLevelImageName; + levelImageName[Level.Alert.ToString()] = errorLevelImageName; + levelImageName[Level.Critical.ToString()] = errorLevelImageName; + levelImageName[Level.Severe.ToString()] = errorLevelImageName; + levelImageName[Level.Error.ToString()] = errorLevelImageName; + levelImageName[Level.Warn.ToString()] = warningLevelImageName; + levelImageName[Level.Notice.ToString()] = warningLevelImageName; + levelImageName[Level.Info.ToString()] = informationLevelImageName; + levelImageName[Level.Debug.ToString()] = debugLevelImageName; + levelImageName[Level.Fine.ToString()] = debugLevelImageName; + levelImageName[Level.Trace.ToString()] = debugLevelImageName; + levelImageName[Level.Finer.ToString()] = debugLevelImageName; + levelImageName[Level.Verbose.ToString()] = debugLevelImageName; + levelImageName[Level.Finest.ToString()] = debugLevelImageName; + levelImageName[Level.All.ToString()] = debugLevelImageName; messagesDataGridView.ClipboardCopyMode = DataGridViewClipboardCopyMode.EnableWithoutHeaderText; messagesDataGridView.MouseUp += MessagesDataGridViewMouseUp; @@ -76,7 +92,7 @@ // fixes DPI problem messagesDataGridView.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize; - messagesDataGridView.RowsAdded += messagesDataGridView_RowsAdded; + messagesDataGridView.RowsAdded += MessagesDataGridViewRowsAdded; } #region IView Members @@ -92,10 +108,6 @@ #endregion - /// - /// Raises the event. - /// - /// A that contains the event data. protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); @@ -109,34 +121,27 @@ return; } - var hasError = false; messageWindowData.Messages.BeginLoadData(); try { MessageData msg; while (newMessages.TryDequeue(out msg)) { messageWindowData.Messages.AddMessagesRow(msg.ImageName, msg.Time, msg.Message); - hasError = hasError || (msg.ImageName == "ERROR" && OnError != null); } } finally { messageWindowData.Messages.EndLoadData(); } - if (hasError) - { - OnError(this, null); - } - if (messagesDataGridView.Rows.Count > 0) { messagesDataGridView.CurrentCell = messagesDataGridView.Rows[0].Cells[0]; } } - private void messagesDataGridView_RowsAdded(object sender, DataGridViewRowsAddedEventArgs e) + private void MessagesDataGridViewRowsAdded(object sender, DataGridViewRowsAddedEventArgs e) { if (filtering) { @@ -180,36 +185,40 @@ Clear(); } - /// - /// Copies selected range to the clipboard - /// - /// - /// private void ButtonCopyClick(object sender, EventArgs e) { Clipboard.SetDataObject(messagesDataGridView.GetClipboardContent()); } - /// - /// since the dataset stores the image name and not the actual image, we have to put - /// the corresponding image in the datagridviewcell of the first column. - /// - /// - /// private void MessagesDataGridViewCellFormatting(object sender, DataGridViewCellFormattingEventArgs e) { if (e.ColumnIndex != 0 || e.Value == null) { return; } + // Dataset stores image-name instead of actual image, therefore we map to + // actual image during formatting. var level = (string) e.Value; e.Value = levelImages.Images[levelImageName[level]]; } private void ApplyFilter() { filtering = true; + + messagesBindingSource.Filter = CreateLoggingLevelDataGridViewFilter(); + + filtering = false; + + foreach (DataGridViewRow row in messagesDataGridView.Rows) + { + AutoSizeRow(row); + } + } + + private string CreateLoggingLevelDataGridViewFilter() + { var filterlines = new List(); if (buttonShowInfo.Checked) { @@ -224,27 +233,9 @@ filterlines.Add(string.Format("Image = '{0}'", Level.Error)); filterlines.Add(string.Format("Image = '{0}'", Level.Fatal)); } - - if (filterlines.Count == 0) - { - messagesBindingSource.Filter = "Image = 'NOTHING SHOWN'"; - } - else - { - string filter = filterlines[0]; - for (var i = 1; i < filterlines.Count; i++) - { - filter += " OR " + filterlines[i]; - } - messagesBindingSource.Filter = filter; - } - - filtering = false; - - foreach (DataGridViewRow row in messagesDataGridView.Rows) - { - AutoSizeRow(row); - } + return filterlines.Count == 0 ? + "Image = 'NOTHING SHOWN'" : + string.Join(" OR ", filterlines); } private void ButtonShowInfoClick(object sender, EventArgs e) @@ -272,7 +263,7 @@ return; } - var messageWindowDialog = new MessageWindowDialog(owner, (string) messagesDataGridView.CurrentRow.Cells[messageDataGridViewTextBoxColumn.Index].Value); + var messageWindowDialog = new MessageWindowDialog(dialogParent, (string) messagesDataGridView.CurrentRow.Cells[messageDataGridViewTextBoxColumn.Index].Value); messageWindowDialog.ShowDialog(); } @@ -286,12 +277,6 @@ #region IMessageWindow Members - /// - /// Appends message to the Top of the messagewindow - /// - /// - /// - /// public void AddMessage(Level level, DateTime time, string message) { newMessages.Enqueue(new MessageData @@ -301,9 +286,6 @@ Invalidate(); } - /// - /// Clears all messages from the view - /// public void Clear() { messageWindowData.Clear(); Index: Core/Common/src/Core.Common.Gui/Forms/MessageWindow/MessageWindowLogAppender.cs =================================================================== diff -u -r98de4c5be2622a4476aa47e6d5084e88d57b80bd -r07138211a0efc377b1c9ef29a8e86178e8d90ab3 --- Core/Common/src/Core.Common.Gui/Forms/MessageWindow/MessageWindowLogAppender.cs (.../MessageWindowLogAppender.cs) (revision 98de4c5be2622a4476aa47e6d5084e88d57b80bd) +++ Core/Common/src/Core.Common.Gui/Forms/MessageWindow/MessageWindowLogAppender.cs (.../MessageWindowLogAppender.cs) (revision 07138211a0efc377b1c9ef29a8e86178e8d90ab3) @@ -22,38 +22,68 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Reflection; -using System.Resources; + using Core.Common.Gui.Properties; +using Core.Common.Utils.Reflection; + using log4net.Appender; using log4net.Core; +using log4net.Util; namespace Core.Common.Gui.Forms.MessageWindow { + /// + /// A log-appender for Log4Net that is able to forward received messages to a + /// instance. + /// public class MessageWindowLogAppender : AppenderSkeleton { /// - /// This list contains any messages that could not yet be delivered to the MessageWindow (typically because it doesn't exist - /// yet at startup). They are kept in the backlog and send to the MessageWindow upon the first message arriving while there - /// is a MessageWindow. + /// This list contains any messages that could not yet be delivered to the + /// (typically because it doesn't exist yet at startup). They are kept in the backlog + /// and send to upon the first message arriving while there is a MessageWindow + /// has been set. /// - protected static IList messageBackLog = new List(); + private readonly IList messageBackLog = new List(); - private static bool enabled; + private bool enabled; + private IMessageWindow messageWindow; - public static IMessageWindow MessageWindow { get; set; } + /// + /// Initializes a new instance of the class and + /// the singleton instance. + /// + public MessageWindowLogAppender() + { + Instance = this; + } /// - /// Resource manager for looking up culture/language depended messages + /// Gets the singleton instance. /// - public static ResourceManager ResourceManager { get; set; } + public static MessageWindowLogAppender Instance { get; set; } /// - /// Resource writer makes a catalogue for not found messages at the resources + /// Gets or sets the message window to which log-messages should be forwarded. /// - public static ResourceWriter ResourceWriter { get; set; } + public IMessageWindow MessageWindow + { + get + { + return messageWindow; + } + set + { + messageWindow = value; + FlushMessagesToMessageWindow(); + } + } - public static bool Enabled + /// + /// Indicating whether this appender should forward it's messages to + /// (set to true) or should cache them when it's enabled at a later time (set to false). + /// + public bool Enabled { get { @@ -68,7 +98,7 @@ protected override void Append(LoggingEvent loggingEvent) { - if (MessageWindow == null || !Enabled) + if (MessageWindow == null || !enabled) { messageBackLog.Add(loggingEvent); } @@ -79,39 +109,21 @@ } } - protected static void AppendToMessageWindow(LoggingEvent loggingEvent) + private void AppendToMessageWindow(LoggingEvent loggingEvent) { - if (MessageWindow == null) - { - return; - } - string message = null; - if (loggingEvent.MessageObject != null) + var stringFormat = loggingEvent.MessageObject as SystemStringFormat; + if (stringFormat != null) { - Type t = loggingEvent.MessageObject.GetType(); - - if (t.FullName == "log4net.Util.SystemStringFormat") - { - string format = - (string) - t.InvokeMember("m_format", - BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.Instance, null, - loggingEvent.MessageObject, null); - object[] args = - (object[]) - t.InvokeMember("m_args", - BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.Instance, null, - loggingEvent.MessageObject, null); - - message = GetLocalizedMessage(format, args); - } + string format = TypeUtils.GetField(stringFormat, "m_format"); + object[] args = TypeUtils.GetField(stringFormat, "m_args"); + message = GetLocalizedMessage(format, args); } if (message == null) { - message = GetLocalizedMessage(loggingEvent.RenderedMessage); + message = loggingEvent.RenderedMessage; } if (loggingEvent.ExceptionObject != null) @@ -122,9 +134,9 @@ MessageWindow.AddMessage(loggingEvent.Level, loggingEvent.TimeStamp, message); } - private static void FlushMessagesToMessageWindow() + private void FlushMessagesToMessageWindow() { - if (MessageWindow == null) + if (messageWindow == null || !enabled) { return; } @@ -143,43 +155,16 @@ { try { - return string.Format(GetLocalizedMessage(format), args); + return string.Format(format, args); } - catch + catch (ArgumentNullException) { return format; } - } - - private static string GetLocalizedMessage(string message) - { - string localizedMessage = ""; - if (ResourceManager != null) + catch (FormatException) { - localizedMessage = ResourceManager.GetString(message); + return format; } - if (string.IsNullOrEmpty(localizedMessage)) - { - WriteMessageToResourceFile(message); - return message; // return non-localized message - } - return localizedMessage; } - - private static void WriteMessageToResourceFile(string message) - { - if (ResourceWriter != null) - { - try - { - // bug: this will fail - // resourceWriter.AddResource(message, message); - } - catch (ArgumentException) - { - //name (or a name that varies only by capitalization) has already been added to this ResourceWriter. - } - } - } } } \ No newline at end of file Index: Core/Common/src/Core.Common.Gui/RingtoetsGui.cs =================================================================== diff -u -rbc173e2a73c395bf64d1d9b4ad8d81129a1cdd7c -r07138211a0efc377b1c9ef29a8e86178e8d90ab3 --- Core/Common/src/Core.Common.Gui/RingtoetsGui.cs (.../RingtoetsGui.cs) (revision bc173e2a73c395bf64d1d9b4ad8d81129a1cdd7c) +++ Core/Common/src/Core.Common.Gui/RingtoetsGui.cs (.../RingtoetsGui.cs) (revision 07138211a0efc377b1c9ef29a8e86178e8d90ab3) @@ -165,7 +165,7 @@ HideSplashScreen(); - MessageWindowLogAppender.Enabled = true; + MessageWindowLogAppender.Instance.Enabled = true; } public void Exit() @@ -273,7 +273,7 @@ splashScreen = null; - MessageWindowLogAppender.MessageWindow = null; + MessageWindowLogAppender.Instance.MessageWindow = null; if (ApplicationCore != null) { Index: Core/Common/test/Core.Common.Gui.Test/Core.Common.Gui.Test.csproj =================================================================== diff -u -rf1e2ad82328ef2ef9a8079b79dc405c0a166188a -r07138211a0efc377b1c9ef29a8e86178e8d90ab3 --- Core/Common/test/Core.Common.Gui.Test/Core.Common.Gui.Test.csproj (.../Core.Common.Gui.Test.csproj) (revision f1e2ad82328ef2ef9a8079b79dc405c0a166188a) +++ Core/Common/test/Core.Common.Gui.Test/Core.Common.Gui.Test.csproj (.../Core.Common.Gui.Test.csproj) (revision 07138211a0efc377b1c9ef29a8e86178e8d90ab3) @@ -58,13 +58,15 @@ ..\..\..\..\packages\RhinoMocks.3.6.1\lib\net\Rhino.Mocks.dll + ..\..\..\..\packages\Fluent.Ribbon.3.4.0\lib\net40\System.Windows.Interactivity.dll True + ..\..\..\..\packages\AvalonDock.2.0.2000\lib\net40\Xceed.Wpf.AvalonDock.dll @@ -111,6 +113,8 @@ + + True True Index: Core/Common/test/Core.Common.Gui.Test/Forms/MainWindow/MainWindowTest.cs =================================================================== diff -u -rf1e2ad82328ef2ef9a8079b79dc405c0a166188a -r07138211a0efc377b1c9ef29a8e86178e8d90ab3 --- Core/Common/test/Core.Common.Gui.Test/Forms/MainWindow/MainWindowTest.cs (.../MainWindowTest.cs) (revision f1e2ad82328ef2ef9a8079b79dc405c0a166188a) +++ Core/Common/test/Core.Common.Gui.Test/Forms/MainWindow/MainWindowTest.cs (.../MainWindowTest.cs) (revision 07138211a0efc377b1c9ef29a8e86178e8d90ab3) @@ -31,6 +31,21 @@ [TestFixture] public class MainWindowTest { + private MessageWindowLogAppender originalValue; + + [SetUp] + public void SetUp() + { + originalValue = MessageWindowLogAppender.Instance; + MessageWindowLogAppender.Instance = new MessageWindowLogAppender(); + } + + [TearDown] + public void TearDown() + { + MessageWindowLogAppender.Instance = originalValue; + } + [Test] [STAThread] public void DefaultConstructor_ExpectedValues() @@ -514,11 +529,11 @@ grid.Data == selectedObjectProperties), Arg.Is.Equal(ViewLocation.Right | ViewLocation.Bottom))); - toolWindowList.Stub(l => l.Contains(Arg.Matches(messages => messages.Text == "Berichten"))) + toolWindowList.Stub(l => l.Contains(Arg.Matches(messages => messages.Text == "Berichten"))) .Return(messageWindowAddedToViewList); if (!messageWindowAddedToViewList) { - toolWindowList.Expect(l => l.Add(Arg.Matches(messages => messages.Text == "Berichten"), + toolWindowList.Expect(l => l.Add(Arg.Matches(messages => messages.Text == "Berichten"), Arg.Is.Equal(ViewLocation.Bottom))); } @@ -546,7 +561,7 @@ Assert.AreEqual("Eigenschappen", mainWindow.PropertyGrid.Text); Assert.AreEqual(selectedObjectProperties, mainWindow.PropertyGrid.Data); - Assert.IsInstanceOf(mainWindow.MessageWindow); + Assert.IsInstanceOf(mainWindow.MessageWindow); Assert.AreEqual("Berichten", mainWindow.MessageWindow.Text); Assert.AreSame(mainWindow.PropertyGrid, toolWindowList.ActiveView, Index: Core/Common/test/Core.Common.Gui.Test/Forms/MessageWindow/MessageWindowLogAppenderTest.cs =================================================================== diff -u --- Core/Common/test/Core.Common.Gui.Test/Forms/MessageWindow/MessageWindowLogAppenderTest.cs (revision 0) +++ Core/Common/test/Core.Common.Gui.Test/Forms/MessageWindow/MessageWindowLogAppenderTest.cs (revision 07138211a0efc377b1c9ef29a8e86178e8d90ab3) @@ -0,0 +1,377 @@ +using System; +using System.Globalization; + +using Core.Common.Gui.Forms.MessageWindow; + +using log4net.Core; +using log4net.Util; + +using NUnit.Framework; + +using Rhino.Mocks; + +namespace Core.Common.Gui.Test.Forms.MessageWindow +{ + [TestFixture] + public class MessageWindowLogAppenderTest + { + private MessageWindowLogAppender originalInstance; + + [SetUp] + public void SetUp() + { + originalInstance = MessageWindowLogAppender.Instance; + } + + [TearDown] + public void TearDown() + { + MessageWindowLogAppender.Instance = originalInstance; + } + + [Test] + public void DefaultConstructor_ExpectedValues() + { + // Call + var appender = new MessageWindowLogAppender(); + + // Assert + Assert.IsFalse(appender.Enabled); + Assert.IsNull(appender.MessageWindow); + Assert.IsInstanceOf(appender.ErrorHandler); + Assert.AreEqual(null, appender.FilterHead); + Assert.AreEqual(null, appender.Layout); + Assert.AreEqual(null, appender.Name); + Assert.AreEqual(null, appender.Threshold); + Assert.AreSame(appender, MessageWindowLogAppender.Instance); + } + + [Test] + [TestCase(LogLevel.Off)] + [TestCase(LogLevel.Fatal)] + [TestCase(LogLevel.Error)] + [TestCase(LogLevel.Warn)] + [TestCase(LogLevel.Info)] + [TestCase(LogLevel.Debug)] + public void DoAppend_AppenderEnabled_ForwardAllMessagesToMessageWindow(LogLevel logLevel) + { + // Setup + Level level = CreateLog4NetLevel(logLevel); + var dataTime = DateTime.Now; + const string message = ""; + + var mocks = new MockRepository(); + var messageWindow = mocks.Stub(); + messageWindow.Expect(w => w.AddMessage(level, dataTime, message)); + mocks.ReplayAll(); + + var appender = new MessageWindowLogAppender + { + Enabled = true, + MessageWindow = messageWindow + }; + + var logData = new LoggingEventData + { + Level = level, + TimeStamp = dataTime, + Message = message + }; + var logEvent = new LoggingEvent(logData); + + // Call + appender.DoAppend(logEvent); + + // Assert + mocks.VerifyAll(); + } + + [Test] + [SetCulture("nl-NL")] + public void DoAppend_LogMessageHasFormattedText_ReturnLocalCulturedText() + { + // Setup + const string messageText = "Gestart in {0:f2} seconden."; + const double formatArgument = 1.2; + + const string expectedText = "Gestart in 1,20 seconden."; + + Level level = CreateLog4NetLevel(LogLevel.Warn); + var dataTime = DateTime.Now; + var logEvent = new LoggingEvent(null, null, "", Level.Warn, + new SystemStringFormat(CultureInfo.InvariantCulture, messageText, formatArgument), + null); + + var mocks = new MockRepository(); + var messageWindow = mocks.Stub(); + messageWindow.Expect(w => w.AddMessage(Arg.Is.Equal(level), + Arg.Matches(time => (time - dataTime) <= new TimeSpan(0, 0, 0, 0, 1)), + Arg.Is.Equal(expectedText))); + mocks.ReplayAll(); + + var appender = new MessageWindowLogAppender + { + Enabled = true, + MessageWindow = messageWindow + }; + + // Call + appender.DoAppend(logEvent); + + // Assert + mocks.VerifyAll(); + } + + [Test] + [SetCulture("nl-NL")] + public void DoAppend_LogMessageHasFormattedTextWithBug_ForwardUnformattedText() + { + // Setup + const string messageText = "Gestart in {5:f2} seconden."; + const double formatArgument = 1.2; + + Level level = CreateLog4NetLevel(LogLevel.Warn); + var dataTime = DateTime.Now; + var logEvent = new LoggingEvent(null, null, "", Level.Warn, + new SystemStringFormat(CultureInfo.InvariantCulture, messageText, formatArgument), + null); + + var mocks = new MockRepository(); + var messageWindow = mocks.Stub(); + messageWindow.Expect(w => w.AddMessage(Arg.Is.Equal(level), + Arg.Matches(time => (time - dataTime) <= new TimeSpan(0, 0, 0, 0, 1)), + Arg.Is.Equal(messageText))); + mocks.ReplayAll(); + + var appender = new MessageWindowLogAppender + { + Enabled = true, + MessageWindow = messageWindow + }; + + // Call + appender.DoAppend(logEvent); + + // Assert + mocks.VerifyAll(); + } + + [Test] + [SetCulture("nl-NL")] + public void DoAppend_LogMessageHasFormattedTextWithoutFormatArgument_ForwardUnformattedText() + { + // Setup + const string messageText = "Gestart in {5:f2} seconden."; + + Level level = CreateLog4NetLevel(LogLevel.Warn); + var dataTime = DateTime.Now; + var logEvent = new LoggingEvent(null, null, "", Level.Warn, + new SystemStringFormat(CultureInfo.InvariantCulture, messageText, null), + null); + + var mocks = new MockRepository(); + var messageWindow = mocks.Stub(); + messageWindow.Expect(w => w.AddMessage(Arg.Is.Equal(level), + Arg.Matches(time => (time - dataTime) <= new TimeSpan(0, 0, 0, 0, 1)), + Arg.Is.Equal(messageText))); + mocks.ReplayAll(); + + var appender = new MessageWindowLogAppender + { + Enabled = true, + MessageWindow = messageWindow + }; + + // Call + appender.DoAppend(logEvent); + + // Assert + mocks.VerifyAll(); + } + + [Test] + public void DoAppend_LogMessagetHasException_AppendTextForReferToLogfileToMessage() + { + // Setup + const string messageText = ""; + string expectedText = string.Format("{0} {1}{2}", + messageText, Environment.NewLine, "Controleer logbestand voor meer informatie (\"Bestand\"->\"Help\"->\"Log tonen\")."); + var exception = new Exception(); + + Level level = CreateLog4NetLevel(LogLevel.Error); + var dataTime = DateTime.Now; + var logEvent = new LoggingEvent(null, null, "", Level.Error, + messageText, exception); + + var mocks = new MockRepository(); + var messageWindow = mocks.Stub(); + messageWindow.Expect(w => w.AddMessage(Arg.Is.Equal(level), + Arg.Matches(time => (time - dataTime) <= new TimeSpan(0, 0, 0, 0, 1)), + Arg.Is.Equal(expectedText))); + mocks.ReplayAll(); + + var appender = new MessageWindowLogAppender + { + Enabled = true, + MessageWindow = messageWindow + }; + + // Call + appender.DoAppend(logEvent); + + // Assert + mocks.VerifyAll(); + } + + [Test] + [TestCase(LogLevel.Off)] + [TestCase(LogLevel.Fatal)] + [TestCase(LogLevel.Error)] + [TestCase(LogLevel.Warn)] + [TestCase(LogLevel.Info)] + [TestCase(LogLevel.Debug)] + public void DoAppend_AppenderDisabled_MessagesNotForwardedToMessageWindow(LogLevel logLevel) + { + // Setup + Level level = CreateLog4NetLevel(logLevel); + var dataTime = DateTime.Now; + const string message = ""; + + var mocks = new MockRepository(); + var messageWindow = mocks.StrictMock(); + mocks.ReplayAll(); + + var appender = new MessageWindowLogAppender + { + Enabled = false, + MessageWindow = messageWindow + }; + + var logData = new LoggingEventData + { + Level = level, + TimeStamp = dataTime, + Message = message + }; + var logEvent = new LoggingEvent(logData); + + // Call + appender.DoAppend(logEvent); + + // Assert + mocks.VerifyAll(); + } + + [Test] + [TestCase(LogLevel.Off)] + [TestCase(LogLevel.Fatal)] + [TestCase(LogLevel.Error)] + [TestCase(LogLevel.Warn)] + [TestCase(LogLevel.Info)] + [TestCase(LogLevel.Debug)] + public void Enabled_FromFalseToTrue_ForwardAllCachedMessagesToMessageWindow(LogLevel logLevel) + { + // Setup + Level level = CreateLog4NetLevel(logLevel); + var dataTime = DateTime.Today; + const string message = ""; + + var mocks = new MockRepository(); + var messageWindow = mocks.Stub(); + messageWindow.Expect(w => w.AddMessage(level, dataTime, message)); + mocks.ReplayAll(); + + var appender = new MessageWindowLogAppender + { + Enabled = false, + MessageWindow = messageWindow + }; + + var logData = new LoggingEventData + { + Level = level, + TimeStamp = dataTime, + Message = message + }; + var logEvent = new LoggingEvent(logData); + appender.DoAppend(logEvent); + + // Call + appender.Enabled = true; + + // Assert + mocks.VerifyAll(); + } + + [Test] + [TestCase(LogLevel.Off)] + [TestCase(LogLevel.Fatal)] + [TestCase(LogLevel.Error)] + [TestCase(LogLevel.Warn)] + [TestCase(LogLevel.Info)] + [TestCase(LogLevel.Debug)] + public void MessageWindow_FromNullToProperInstance_ForwardAllCachedMessagesToMessageWindow(LogLevel logLevel) + { + // Setup + Level level = CreateLog4NetLevel(logLevel); + var dataTime = DateTime.Today; + const string message = ""; + + var mocks = new MockRepository(); + var messageWindow = mocks.Stub(); + messageWindow.Expect(w => w.AddMessage(level, dataTime, message)); + mocks.ReplayAll(); + + var appender = new MessageWindowLogAppender + { + Enabled = true + }; + + var logData = new LoggingEventData + { + Level = level, + TimeStamp = dataTime, + Message = message + }; + var logEvent = new LoggingEvent(logData); + appender.DoAppend(logEvent); + + // Call + appender.MessageWindow = messageWindow; + + // Assert + mocks.VerifyAll(); + } + + private Level CreateLog4NetLevel(LogLevel level) + { + switch (level) + { + case LogLevel.Off: + return Level.Off; + case LogLevel.Fatal: + return Level.Fatal; + case LogLevel.Error: + return Level.Error; + case LogLevel.Warn: + return Level.Warn; + case LogLevel.Info: + return Level.Info; + case LogLevel.Debug: + return Level.Debug; + default: + throw new NotImplementedException(); + } + } + + public enum LogLevel + { + Off, + Fatal, + Error, + Warn, + Info, + Debug + } + } +} \ No newline at end of file Index: Core/Common/test/Core.Common.Gui.Test/Forms/MessageWindow/MessageWindowTest.cs =================================================================== diff -u --- Core/Common/test/Core.Common.Gui.Test/Forms/MessageWindow/MessageWindowTest.cs (revision 0) +++ Core/Common/test/Core.Common.Gui.Test/Forms/MessageWindow/MessageWindowTest.cs (revision 07138211a0efc377b1c9ef29a8e86178e8d90ab3) @@ -0,0 +1,55 @@ +using System.Windows.Forms; + +using Core.Common.Gui.Forms.MessageWindow; + +using NUnit.Framework; + +using Rhino.Mocks; + +namespace Core.Common.Gui.Test.Forms.MessageWindow +{ + [TestFixture] + public class MessageWindowTest + { + private MessageWindowLogAppender originalValue; + + [SetUp] + public void SetUp() + { + originalValue = MessageWindowLogAppender.Instance; + } + + [TearDown] + public void TearDown() + { + MessageWindowLogAppender.Instance = originalValue; + } + + [Test] + public void ParameteredConstructor_ExpectedValues() + { + // Setup + var logAppender = new MessageWindowLogAppender(); + + // Precondition + Assert.AreSame(logAppender, MessageWindowLogAppender.Instance); + + var mocks = new MockRepository(); + var dialogParent = mocks.Stub(); + mocks.ReplayAll(); + + // Call + using (var messageswindow = new Gui.Forms.MessageWindow.MessageWindow(dialogParent)) + { + // Assert + Assert.IsInstanceOf(messageswindow); + Assert.IsInstanceOf(messageswindow); + Assert.AreEqual("Berichten", messageswindow.Text); + Assert.IsInstanceOf(messageswindow.Data); + Assert.AreSame(messageswindow, MessageWindowLogAppender.Instance.MessageWindow); + + } + mocks.VerifyAll(); + } + } +} \ No newline at end of file