// Copyright (C) Stichting Deltares 2016. All rights reserved.
//
// This file is part of Ringtoets.
//
// Ringtoets is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see .
//
// All names, logos, and references to "Deltares" are registered trademarks of
// Stichting Deltares and remain full property of Stichting Deltares at all times.
// All rights reserved.
using System;
using System.Collections.Specialized;
using System.Configuration;
using System.Linq;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;
using System.Windows.Threading;
using Core.Common.Base.Data;
using Core.Common.Base.Storage;
using Core.Common.Controls.TreeView;
using Core.Common.Gui.Commands;
using Core.Common.Gui.ContextMenu;
using Core.Common.Gui.Forms.MainWindow;
using Core.Common.Gui.Forms.MessageWindow;
using Core.Common.Gui.Forms.PropertyGridView;
using Core.Common.Gui.Forms.ViewHost;
using Core.Common.Gui.Plugin;
using Core.Common.Gui.Settings;
using Core.Common.Gui.Test.Forms.ViewHost;
using Core.Common.TestUtil;
using log4net;
using log4net.Appender;
using log4net.Repository.Hierarchy;
using NUnit.Framework;
using Rhino.Mocks;
namespace Core.Common.Gui.Test
{
[TestFixture]
public class GuiCoreTests
{
private MessageWindowLogAppender originalMessageWindowLogAppender;
private IViewCommands originalViewPropertyEditor;
[SetUp]
public void SetUp()
{
originalMessageWindowLogAppender = MessageWindowLogAppender.Instance;
MessageWindowLogAppender.Instance = new MessageWindowLogAppender();
originalViewPropertyEditor = ViewPropertyEditor.ViewCommands;
}
[TearDown]
public void TearDown()
{
MessageWindowLogAppender.Instance = originalMessageWindowLogAppender;
ViewPropertyEditor.ViewCommands = originalViewPropertyEditor;
Dispatcher.CurrentDispatcher.InvokeShutdown();
}
[Test]
[STAThread]
public void ParameteredConstructor_ValidArguments_ExpectedValues()
{
// Setup
var mocks = new MockRepository();
var projectStore = mocks.Stub();
mocks.ReplayAll();
var guiCoreSettings = new GuiCoreSettings();
// Call
using (var mainWindow = new MainWindow())
{
using (var gui = new GuiCore(mainWindow, projectStore, guiCoreSettings))
{
// Assert
Assert.AreEqual(null, gui.PropertyResolver);
Assert.AreEqual(null, gui.ProjectFilePath);
Assert.AreEqual(null, gui.Project);
Assert.AreEqual(null, gui.Selection);
Assert.IsInstanceOf(gui.ProjectCommands);
Assert.IsInstanceOf(gui.StorageCommands);
Assert.IsInstanceOf(gui.ViewCommands);
Assert.AreEqual(null, gui.ApplicationCommands);
Assert.AreEqual(null, gui.ViewHost);
Assert.AreEqual(null, gui.DocumentViewController);
AssertDefaultUserSettings(gui.UserSettings);
Assert.AreSame(guiCoreSettings, gui.FixedSettings);
CollectionAssert.IsEmpty(gui.Plugins);
Assert.AreEqual(mainWindow, gui.MainWindow);
Assert.AreSame(ViewPropertyEditor.ViewCommands, gui.ViewCommands);
// Check for OS settings that allow visual styles to be rendered in the first place:
if (VisualStyleInformation.IsSupportedByOS && VisualStyleInformation.IsEnabledByUser &&
(Application.VisualStyleState == VisualStyleState.ClientAreaEnabled ||
Application.VisualStyleState == VisualStyleState.ClientAndNonClientAreasEnabled))
{
Assert.IsTrue(Application.RenderWithVisualStyles,
"OS configured to support visual styles, therefore GUI should enable this rendering style.");
}
else
{
//
Assert.IsFalse(Application.RenderWithVisualStyles,
"OS not supporting visual styles, therefore application shouldn't be render with visual styles.");
}
}
}
mocks.VerifyAll();
}
[Test]
[STAThread]
[TestCase(0)]
[TestCase(1)]
[TestCase(2)]
public void ParameteredConstructor_SomeArgumentIsNull_ThrowsArgumentNullException(int nullArgumentIndex)
{
// Setup
var mocks = new MockRepository();
IStoreProject projectStore = nullArgumentIndex == 1 ? null : mocks.Stub();
mocks.ReplayAll();
GuiCoreSettings guiCoreSettings = nullArgumentIndex == 2 ? null : new GuiCoreSettings();
// Call
using (var mainWindow = new MainWindow())
{
// Call
TestDelegate call = () => new GuiCore(nullArgumentIndex == 0 ? null : mainWindow, projectStore, guiCoreSettings);
// Assert
TestHelper.AssertThrowsArgumentExceptionAndTestMessage(call, "Value cannot be null.");
}
mocks.VerifyAll();
}
[Test]
[STAThread]
[ExpectedException(typeof(InvalidOperationException))]
public void Constructor_ConstuctedAfterAnotherInstanceHasBeenCreated_ThrowsInvalidOperationException()
{
// Setup
var mocks = new MockRepository();
var projectStore = mocks.Stub();
mocks.ReplayAll();
var guiCoreSettings = new GuiCoreSettings();
// Call
using (var mainWindow = new MainWindow())
{
using (var gui = new GuiCore(mainWindow, projectStore, guiCoreSettings))
{
// Call
using (var gui2 = new GuiCore(mainWindow, projectStore, guiCoreSettings))
{
// Assert
Assert.Fail("Expected an InvalidOperationException to be thrown.");
}
}
}
mocks.VerifyAll();
}
[Test]
[STAThread]
public void Dispose_PluginsAdded_PluginsDisabledAndRemovedAndDisposed()
{
// Setup
var mocks = new MockRepository();
var projectStore = mocks.Stub();
var pluginMock = mocks.Stub();
pluginMock.Expect(p => p.Deactivate());
pluginMock.Expect(p => p.Dispose());
mocks.ReplayAll();
var gui = new GuiCore(new MainWindow(), projectStore, new GuiCoreSettings());
gui.Plugins.Add(pluginMock);
// Call
gui.Dispose();
// Assert
Assert.IsNull(gui.Plugins);
mocks.VerifyAll();
}
[Test]
[STAThread]
public void Dispose_PluginAddedButThrowsExceptionDuringDeactivation_LogErrorAndStillDisposeAndRemove()
{
// Setup
var mocks = new MockRepository();
var projectStore = mocks.Stub();
var pluginMock = mocks.Stub();
pluginMock.Expect(p => p.Deactivate()).Throw(new Exception("Bad stuff happening!"));
pluginMock.Expect(p => p.Dispose());
mocks.ReplayAll();
var gui = new GuiCore(new MainWindow(), projectStore, new GuiCoreSettings());
gui.Plugins.Add(pluginMock);
// Call
Action call = () => gui.Dispose();
// Assert
TestHelper.AssertLogMessageIsGenerated(call, "Kritieke fout opgetreden tijdens deactivering van de grafische interface plugin.", 1);
Assert.IsNull(gui.Plugins);
mocks.VerifyAll();
}
[Test]
[STAThread]
public void Dispose_HasSelection_ClearSelection()
{
// Setup
var mocks = new MockRepository();
var projectStore = mocks.Stub();
mocks.ReplayAll();
var gui = new GuiCore(new MainWindow(), projectStore, new GuiCoreSettings())
{
Selection = new object()
};
// Call
gui.Dispose();
// Assert
Assert.IsNull(gui.Selection);
mocks.VerifyAll();
}
[Test]
[STAThread]
public void Dispose_HasProject_ClearProject()
{
// Setup
var mocks = new MockRepository();
var projectStore = mocks.Stub();
mocks.ReplayAll();
var gui = new GuiCore(new MainWindow(), projectStore, new GuiCoreSettings())
{
Project = new Project()
};
// Call
gui.Dispose();
// Assert
Assert.IsNull(gui.Project);
mocks.VerifyAll();
}
[Test]
[STAThread]
public void Dispose_HasMainWindow_DiposeOfMainWindow()
{
// Setup
var mocks = new MockRepository();
var projectStore = mocks.Stub();
mocks.ReplayAll();
using (var mainWindow = new MainWindow())
{
var gui = new GuiCore(mainWindow, projectStore, new GuiCoreSettings());
// Call
gui.Dispose();
// Assert
Assert.IsTrue(mainWindow.IsWindowDisposed);
Assert.IsNull(gui.MainWindow);
}
mocks.VerifyAll();
}
[Test]
[STAThread]
public void Dispose_HasInitializedMessageWindowForLogAppender_ClearMessageWindow()
{
// Setup
var mocks = new MockRepository();
var projectStore = mocks.Stub();
mocks.ReplayAll();
var messageWindowLogAppender = new MessageWindowLogAppender();
var rootLogger = ((Hierarchy) LogManager.GetRepository()).Root;
rootLogger.AddAppender(messageWindowLogAppender);
try
{
using (var gui = new GuiCore(new MainWindow(), projectStore, new GuiCoreSettings()))
{
gui.Run();
// Precondition:
Assert.IsNotNull(MessageWindowLogAppender.Instance.MessageWindow);
Assert.IsNotNull(messageWindowLogAppender.MessageWindow);
// Call
gui.Dispose();
// Assert
Assert.IsNull(MessageWindowLogAppender.Instance.MessageWindow);
Assert.IsNull(messageWindowLogAppender.MessageWindow);
CollectionAssert.DoesNotContain(rootLogger.Appenders, messageWindowLogAppender);
}
}
finally
{
rootLogger.RemoveAppender(messageWindowLogAppender);
}
mocks.VerifyAll();
}
[Test]
[STAThread]
public void Dispose_HasOpenedToolView_ToolViewsClearedAndViewsDisposed()
{
// Setup
var mocks = new MockRepository();
var projectStore = mocks.Stub();
mocks.ReplayAll();
using (var toolView = new TestView())
{
using (var gui = new GuiCore(new MainWindow(), projectStore, new GuiCoreSettings()))
{
gui.Run();
gui.ViewHost.AddToolView(toolView, ToolViewLocation.Left);
// Call
gui.Dispose();
// Assert
Assert.IsEmpty(gui.ViewHost.ToolViews);
Assert.IsTrue(toolView.IsDisposed);
}
}
mocks.VerifyAll();
}
[Test]
[STAThread]
public void Dispose_HasOpenedDocumentView_DocumentViewsClearedAndViewsDisposed()
{
// Setup
var mocks = new MockRepository();
var projectStore = mocks.Stub();
mocks.ReplayAll();
using (var documentView = new TestView())
{
using (var gui = new GuiCore(new MainWindow(), projectStore, new GuiCoreSettings()))
{
gui.Run();
gui.ViewHost.AddDocumentView(documentView);
// Call
gui.Dispose();
// Assert
Assert.IsEmpty(gui.ViewHost.DocumentViews);
Assert.IsNull(gui.DocumentViewController);
Assert.IsTrue(documentView.IsDisposed);
}
}
mocks.VerifyAll();
}
[Test]
[STAThread]
public void Run_NoMessageWindowLogAppender_AddNewLogAppender()
{
// Setup
var mocks = new MockRepository();
var projectStore = mocks.Stub();
mocks.ReplayAll();
Logger rootLogger = ((Hierarchy) LogManager.GetRepository()).Root;
IAppender[] originalAppenders = rootLogger.Appenders.ToArray();
rootLogger.RemoveAllAppenders();
try
{
using (var gui = new GuiCore(new MainWindow(), projectStore, new GuiCoreSettings()))
{
// Call
gui.Run();
// Assert
Assert.AreEqual(1, rootLogger.Appenders.Count);
IAppender appender = rootLogger.Appenders[0];
Assert.IsInstanceOf(appender);
Assert.AreSame(appender, MessageWindowLogAppender.Instance);
Assert.IsTrue(rootLogger.Repository.Configured);
Assert.IsTrue(MessageWindowLogAppender.Instance.Enabled);
}
}
finally
{
rootLogger.RemoveAllAppenders();
foreach (var appender in originalAppenders)
{
rootLogger.AddAppender(appender);
}
}
mocks.VerifyAll();
}
[Test]
[STAThread]
public void Run_AlreadyHasMessageWindowLogAppender_NoChangesToLogAppenders()
{
// Setup
var mocks = new MockRepository();
var projectStore = mocks.Stub();
mocks.ReplayAll();
var appender = new MessageWindowLogAppender();
Logger rootLogger = ((Hierarchy) LogManager.GetRepository()).Root;
IAppender[] originalAppenders = rootLogger.Appenders.ToArray();
rootLogger.RemoveAllAppenders();
rootLogger.AddAppender(appender);
var rootloggerConfigured = rootLogger.Repository.Configured;
try
{
using (var gui = new GuiCore(new MainWindow(), projectStore, new GuiCoreSettings()))
{
// Call
gui.Run();
// Assert
Assert.AreEqual(1, rootLogger.Appenders.Count);
Assert.AreSame(appender, rootLogger.Appenders[0]);
Assert.AreSame(appender, MessageWindowLogAppender.Instance);
Assert.AreEqual(rootloggerConfigured, rootLogger.Repository.Configured);
Assert.IsTrue(MessageWindowLogAppender.Instance.Enabled);
}
}
finally
{
rootLogger.RemoveAllAppenders();
foreach (var originalAppender in originalAppenders)
{
rootLogger.AddAppender(originalAppender);
}
}
mocks.VerifyAll();
}
[Test]
[STAThread]
public void Run_WithFile_LoadProjectFromFile()
{
// Setup
const string fileName = "SomeFile";
string testFile = string.Format("{0}.rtd", fileName);
var deserializedProject = new Project();
var mocks = new MockRepository();
var projectStore = mocks.Stub();
projectStore.Expect(ps => ps.LoadProject(testFile)).Return(deserializedProject);
mocks.ReplayAll();
var fixedSettings = new GuiCoreSettings
{
MainWindowTitle = ""
};
using (var mainWindow = new MainWindow())
{
using (var gui = new GuiCore(mainWindow, projectStore, fixedSettings))
{
// Call
Action call = () => gui.Run(testFile);
// Assert
var expectedMessages = new[]
{
"Openen van bestaand Ringtoetsproject.",
"Bestaand Ringtoetsproject succesvol geopend."
};
TestHelper.AssertLogMessagesAreGenerated(call, expectedMessages);
Assert.AreEqual(testFile, gui.ProjectFilePath);
Assert.AreSame(deserializedProject, gui.Project);
Assert.AreEqual(fileName, gui.Project.Name,
"Project name should be updated to the name of the file.");
var expectedTitle = string.Format("{0} - {1} {2}",
fileName, fixedSettings.MainWindowTitle, SettingsHelper.ApplicationVersion);
Assert.AreEqual(expectedTitle, mainWindow.Title);
}
}
mocks.VerifyAll();
}
[Test]
[STAThread]
public void Run_LoadingFromFileThrowsStorageException_LogErrorAndLoadDefaultProjectInstead()
{
// Setup
const string fileName = "SomeFile";
string testFile = string.Format("{0}.rtd", fileName);
const string storageExceptionText = "";
var mocks = new MockRepository();
var projectStore = mocks.Stub();
projectStore.Expect(ps => ps.LoadProject(testFile)).Throw(new StorageException(storageExceptionText));
mocks.ReplayAll();
var fixedSettings = new GuiCoreSettings
{
MainWindowTitle = ""
};
using (var mainWindow = new MainWindow())
{
using (var gui = new GuiCore(mainWindow, projectStore, fixedSettings))
{
// Call
Action call = () => gui.Run(testFile);
// Assert
var expectedMessages = new[]
{
"Openen van bestaand Ringtoetsproject.",
storageExceptionText,
"Het is niet gelukt om het Ringtoetsproject te laden.",
"Nieuw project aanmaken..."
};
TestHelper.AssertLogMessagesAreGenerated(call, expectedMessages);
Assert.IsNull(gui.ProjectFilePath);
const string expectedProjectName = "Project";
Assert.AreEqual(expectedProjectName, gui.Project.Name);
Assert.AreEqual(string.Empty, gui.Project.Description);
CollectionAssert.IsEmpty(gui.Project.Items);
Assert.AreEqual(0, gui.Project.StorageId);
var expectedTitle = string.Format("{0} - {1} {2}",
expectedProjectName, fixedSettings.MainWindowTitle, SettingsHelper.ApplicationVersion);
Assert.AreEqual(expectedTitle, mainWindow.Title);
}
}
mocks.VerifyAll();
}
[Test]
[TestCase("")]
[TestCase(" ")]
[TestCase(null)]
[STAThread]
public void Run_WithoutFile_CreateNewDefaultProject(string path)
{
// Setup
var mocks = new MockRepository();
var projectStore = mocks.StrictMock();
mocks.ReplayAll();
var fixedSettings = new GuiCoreSettings
{
MainWindowTitle = ""
};
using (var mainWindow = new MainWindow())
{
using (var gui = new GuiCore(mainWindow, projectStore, fixedSettings))
{
// Call
Action call = () => gui.Run(path);
// Assert
TestHelper.AssertLogMessageIsGenerated(call, "Nieuw project aanmaken...");
Assert.IsNull(gui.ProjectFilePath);
const string expectedProjectName = "Project";
Assert.AreEqual(expectedProjectName, gui.Project.Name);
Assert.AreEqual(string.Empty, gui.Project.Description);
CollectionAssert.IsEmpty(gui.Project.Items);
Assert.AreEqual(0, gui.Project.StorageId);
var expectedTitle = string.Format("{0} - {1} {2}",
expectedProjectName, fixedSettings.MainWindowTitle, SettingsHelper.ApplicationVersion);
Assert.AreEqual(expectedTitle, mainWindow.Title);
}
}
mocks.VerifyAll();
}
[Test]
[STAThread]
public void Run_WithPlugins_SetGuiAndActivatePlugins()
{
var mocks = new MockRepository();
var projectStore = mocks.Stub();
var plugin = mocks.Stub();
plugin.Stub(p => p.Deactivate());
plugin.Stub(p => p.Dispose());
plugin.Expect(p => p.Activate());
plugin.Expect(p => p.GetViewInfos()).Return(Enumerable.Empty());
plugin.Expect(p => p.GetPropertyInfos()).Return(Enumerable.Empty());
mocks.ReplayAll();
// Setup
using (var gui = new GuiCore(new MainWindow(), projectStore, new GuiCoreSettings()))
{
gui.Plugins.Add(plugin);
// Call
gui.Run();
// Assert
Assert.AreSame(gui, plugin.Gui);
}
mocks.VerifyAll();
}
[Test]
[STAThread]
public void Run_WithPluginThatThrowsExceptionWhenActivated_DeactivateAndDisposePlugin()
{
var mocks = new MockRepository();
var projectStore = mocks.Stub();
var plugin = mocks.Stub();
plugin.Stub(p => p.GetViewInfos()).Return(Enumerable.Empty());
plugin.Stub(p => p.GetPropertyInfos()).Return(Enumerable.Empty());
plugin.Stub(p => p.Activate()).Throw(new Exception("ERROR!"));
plugin.Expect(p => p.Deactivate());
plugin.Expect(p => p.Dispose());
mocks.ReplayAll();
// Setup
using (var gui = new GuiCore(new MainWindow(), projectStore, new GuiCoreSettings()))
{
gui.Plugins.Add(plugin);
// Call
gui.Run();
}
// Assert
mocks.VerifyAll(); // Expect calls on plugin
}
[Test]
[STAThread]
public void Run_WithPluginThatThrowsExceptionWhenActivatedAndDeactivated_LogErrorForDeactivatingThenDispose()
{
var mocks = new MockRepository();
var projectStore = mocks.Stub();
var plugin = mocks.Stub();
plugin.Stub(p => p.GetViewInfos()).Return(Enumerable.Empty());
plugin.Stub(p => p.GetPropertyInfos()).Return(Enumerable.Empty());
plugin.Stub(p => p.Activate()).Throw(new Exception("ERROR!"));
plugin.Stub(p => p.Deactivate()).Throw(new Exception("MORE ERROR!"));
plugin.Expect(p => p.Dispose());
mocks.ReplayAll();
// Setup
using (var gui = new GuiCore(new MainWindow(), projectStore, new GuiCoreSettings()))
{
gui.Plugins.Add(plugin);
// Call
Action call = () => gui.Run();
// Assert
var expectedMessage = "Kritieke fout opgetreden tijdens deactivering van de grafische interface plugin.";
TestHelper.AssertLogMessageIsGenerated(call, expectedMessage);
}
mocks.VerifyAll(); // Expect Dispose call on plugin
}
[Test]
[STAThread]
public void Run_InitializesViewController()
{
// Setup
var mocks = new MockRepository();
var projectStore = mocks.Stub();
mocks.ReplayAll();
using (var gui = new GuiCore(new MainWindow(), projectStore, new GuiCoreSettings()))
{
// Call
gui.Run();
// Assert
CollectionAssert.IsEmpty(gui.ViewHost.DocumentViews);
Assert.IsNull(gui.ViewHost.ActiveDocumentView);
Assert.AreEqual(2, gui.ViewHost.ToolViews.Count());
Assert.AreEqual(1, gui.ViewHost.ToolViews.Count(v => v is PropertyGridView));
Assert.AreEqual(1, gui.ViewHost.ToolViews.Count(v => v is MessageWindow));
Assert.IsNotNull(gui.DocumentViewController);
CollectionAssert.IsEmpty(gui.DocumentViewController.DefaultViewTypes);
}
mocks.VerifyAll();
}
[Test]
[STAThread]
public void GetAllDataWithViewDefinitionsRecursively_DataHasNoViewDefinitions_ReturnEmpty()
{
// Setup
var mocks = new MockRepository();
var projectStore = mocks.Stub();
mocks.ReplayAll();
using (var gui = new GuiCore(new MainWindow(), projectStore, new GuiCoreSettings()))
{
var rootData = new object();
// Call
var dataInstancesWithViewDefinitions = gui.GetAllDataWithViewDefinitionsRecursively(rootData);
// Assert
CollectionAssert.IsEmpty(dataInstancesWithViewDefinitions);
}
mocks.VerifyAll();
}
[Test]
[STAThread]
public void GetAllDataWithViewDefinitionsRecursively_MultiplePluginsHaveViewDefinitionsForRoot_ReturnRootObject()
{
// Setup
var rootData = new object();
var mocks = new MockRepository();
var projectStore = mocks.Stub();
var plugin1 = mocks.StrictMock();
plugin1.Expect(p => p.GetChildDataWithViewDefinitions(rootData)).Return(new[]
{
rootData
});
plugin1.Stub(p => p.Dispose());
plugin1.Stub(p => p.Deactivate());
var plugin2 = mocks.StrictMock();
plugin2.Expect(p => p.GetChildDataWithViewDefinitions(rootData)).Return(new[]
{
rootData
});
plugin2.Stub(p => p.Dispose());
plugin2.Stub(p => p.Deactivate());
mocks.ReplayAll();
using (var gui = new GuiCore(new MainWindow(), projectStore, new GuiCoreSettings()))
{
gui.Plugins.Add(plugin1);
gui.Plugins.Add(plugin2);
// Call
var dataInstancesWithViewDefinitions = gui.GetAllDataWithViewDefinitionsRecursively(rootData).OfType