Implemented unit tests for startup- and shutdown-controller.

This commit is contained in:
Damian Büchel 2017-07-21 12:05:31 +02:00
parent 1153fea091
commit e7b8404254
13 changed files with 339 additions and 53 deletions

View file

@ -15,6 +15,6 @@ namespace SafeExamBrowser.Contracts.Behaviour
/// <summary> /// <summary>
/// Reverts any changes performed during the startup or runtime and releases all used resources. /// Reverts any changes performed during the startup or runtime and releases all used resources.
/// </summary> /// </summary>
void FinalizeApplication(Stack<IOperation> operations); void FinalizeApplication(Queue<IOperation> operations);
} }
} }

View file

@ -13,10 +13,9 @@ namespace SafeExamBrowser.Contracts.Behaviour
public interface IStartupController public interface IStartupController
{ {
/// <summary> /// <summary>
/// Tries to initialize the application. Returns <c>true</c> if the initialization was successful, /// Tries to initialize the application according to the given queue of operations.
/// <c>false</c> otherwise. All operations performed during the startup procedure will be registered /// Returns <c>true</c> if the initialization was successful, <c>false</c> otherwise.
/// to the given <c>out</c> parameter.
/// </summary> /// </summary>
bool TryInitializeApplication(out Stack<IOperation> operations); bool TryInitializeApplication(Queue<IOperation> operations);
} }
} }

View file

@ -0,0 +1,115 @@
/*
* Copyright (c) 2017 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Contracts.Behaviour;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.Monitoring;
using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Core.Behaviour;
namespace SafeExamBrowser.Core.UnitTests.Behaviour
{
[TestClass]
public class ShutdownControllerTests
{
private Mock<IApplicationController> browserControllerMock;
private Mock<IApplicationInfo> browserInfoMock;
private Mock<ILogger> loggerMock;
private Mock<IMessageBox> messageBoxMock;
private Mock<INotificationInfo> aboutInfoMock;
private Mock<IProcessMonitor> processMonitorMock;
private Mock<ISettings> settingsMock;
private Mock<ITaskbar> taskbarMock;
private Mock<IText> textMock;
private Mock<IUiElementFactory> uiFactoryMock;
private Mock<IWorkingArea> workingAreaMock;
private IShutdownController sut;
[TestInitialize]
public void Initialize()
{
browserControllerMock = new Mock<IApplicationController>();
browserInfoMock = new Mock<IApplicationInfo>();
loggerMock = new Mock<ILogger>();
messageBoxMock = new Mock<IMessageBox>();
aboutInfoMock = new Mock<INotificationInfo>();
processMonitorMock = new Mock<IProcessMonitor>();
settingsMock = new Mock<ISettings>();
taskbarMock = new Mock<ITaskbar>();
textMock = new Mock<IText>();
uiFactoryMock = new Mock<IUiElementFactory>();
workingAreaMock = new Mock<IWorkingArea>();
uiFactoryMock.Setup(f => f.CreateSplashScreen(settingsMock.Object, textMock.Object)).Returns(new Mock<ISplashScreen>().Object);
sut = new ShutdownController(
loggerMock.Object,
messageBoxMock.Object,
processMonitorMock.Object,
settingsMock.Object,
textMock.Object,
uiFactoryMock.Object,
workingAreaMock.Object);
}
[TestMethod]
public void MustRevertOperations()
{
var operationA = new Mock<IOperation>();
var operationB = new Mock<IOperation>();
var operationC = new Mock<IOperation>();
var operations = new Queue<IOperation>();
operations.Enqueue(operationA.Object);
operations.Enqueue(operationB.Object);
operations.Enqueue(operationC.Object);
sut.FinalizeApplication(operations);
operationA.Verify(o => o.Revert(), Times.Once);
operationB.Verify(o => o.Revert(), Times.Once);
operationC.Verify(o => o.Revert(), Times.Once);
}
[TestMethod]
public void MustRevertOperationsInSequence()
{
int current = 0, a = 0, b = 0, c = 0;
var operationA = new Mock<IOperation>();
var operationB = new Mock<IOperation>();
var operationC = new Mock<IOperation>();
var operations = new Queue<IOperation>();
operationA.Setup(o => o.Revert()).Callback(() => a = ++current);
operationB.Setup(o => o.Revert()).Callback(() => b = ++current);
operationC.Setup(o => o.Revert()).Callback(() => c = ++current);
operations.Enqueue(operationA.Object);
operations.Enqueue(operationB.Object);
operations.Enqueue(operationC.Object);
sut.FinalizeApplication(operations);
Assert.IsTrue(a == 1);
Assert.IsTrue(b == 2);
Assert.IsTrue(c == 3);
}
[TestMethod]
public void MustNotFailWithEmptyQueue()
{
sut.FinalizeApplication(new Queue<IOperation>());
}
}
}

View file

@ -0,0 +1,183 @@
/*
* Copyright (c) 2017 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Contracts.Behaviour;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.Monitoring;
using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Core.Behaviour;
namespace SafeExamBrowser.Core.UnitTests.Behaviour
{
[TestClass]
public class StartupControllerTests
{
private Mock<IApplicationController> browserControllerMock;
private Mock<IApplicationInfo> browserInfoMock;
private Mock<ILogger> loggerMock;
private Mock<IMessageBox> messageBoxMock;
private Mock<INotificationInfo> aboutInfoMock;
private Mock<IProcessMonitor> processMonitorMock;
private Mock<ISettings> settingsMock;
private Mock<ITaskbar> taskbarMock;
private Mock<IText> textMock;
private Mock<IUiElementFactory> uiFactoryMock;
private Mock<IWorkingArea> workingAreaMock;
private IStartupController sut;
[TestInitialize]
public void Initialize()
{
browserControllerMock = new Mock<IApplicationController>();
browserInfoMock = new Mock<IApplicationInfo>();
loggerMock = new Mock<ILogger>();
messageBoxMock = new Mock<IMessageBox>();
aboutInfoMock = new Mock<INotificationInfo>();
processMonitorMock = new Mock<IProcessMonitor>();
settingsMock = new Mock<ISettings>();
taskbarMock = new Mock<ITaskbar>();
textMock = new Mock<IText>();
uiFactoryMock = new Mock<IUiElementFactory>();
workingAreaMock = new Mock<IWorkingArea>();
uiFactoryMock.Setup(f => f.CreateSplashScreen(settingsMock.Object, textMock.Object)).Returns(new Mock<ISplashScreen>().Object);
sut = new StartupController(
browserControllerMock.Object,
browserInfoMock.Object,
loggerMock.Object,
messageBoxMock.Object,
aboutInfoMock.Object,
processMonitorMock.Object,
settingsMock.Object,
taskbarMock.Object,
textMock.Object,
uiFactoryMock.Object,
workingAreaMock.Object);
}
[TestMethod]
public void MustPerformOperations()
{
var operationA = new Mock<IOperation>();
var operationB = new Mock<IOperation>();
var operationC = new Mock<IOperation>();
var operations = new Queue<IOperation>();
operations.Enqueue(operationA.Object);
operations.Enqueue(operationB.Object);
operations.Enqueue(operationC.Object);
var result = sut.TryInitializeApplication(operations);
operationA.Verify(o => o.Perform(), Times.Once);
operationB.Verify(o => o.Perform(), Times.Once);
operationC.Verify(o => o.Perform(), Times.Once);
Assert.IsTrue(result);
}
[TestMethod]
public void MustPerformOperationsInSequence()
{
int current = 0, a = 0, b = 0, c = 0;
var operationA = new Mock<IOperation>();
var operationB = new Mock<IOperation>();
var operationC = new Mock<IOperation>();
var operations = new Queue<IOperation>();
operationA.Setup(o => o.Perform()).Callback(() => a = ++current);
operationB.Setup(o => o.Perform()).Callback(() => b = ++current);
operationC.Setup(o => o.Perform()).Callback(() => c = ++current);
operations.Enqueue(operationA.Object);
operations.Enqueue(operationB.Object);
operations.Enqueue(operationC.Object);
sut.TryInitializeApplication(operations);
Assert.IsTrue(a == 1);
Assert.IsTrue(b == 2);
Assert.IsTrue(c == 3);
}
[TestMethod]
public void MustRevertOperationsInCaseOfError()
{
var operationA = new Mock<IOperation>();
var operationB = new Mock<IOperation>();
var operationC = new Mock<IOperation>();
var operationD = new Mock<IOperation>();
var operations = new Queue<IOperation>();
operationC.Setup(o => o.Perform()).Throws<Exception>();
operations.Enqueue(operationA.Object);
operations.Enqueue(operationB.Object);
operations.Enqueue(operationC.Object);
operations.Enqueue(operationD.Object);
var result = sut.TryInitializeApplication(operations);
operationA.Verify(o => o.Perform(), Times.Once);
operationA.Verify(o => o.Revert(), Times.Once);
operationB.Verify(o => o.Perform(), Times.Once);
operationB.Verify(o => o.Revert(), Times.Once);
operationC.Verify(o => o.Perform(), Times.Once);
operationC.Verify(o => o.Revert(), Times.Once);
operationD.Verify(o => o.Perform(), Times.Never);
operationD.Verify(o => o.Revert(), Times.Never);
Assert.IsFalse(result);
}
[TestMethod]
public void MustRevertOperationsInSequence()
{
int current = 0, a = 0, b = 0, c = 0, d = 0;
var operationA = new Mock<IOperation>();
var operationB = new Mock<IOperation>();
var operationC = new Mock<IOperation>();
var operationD = new Mock<IOperation>();
var operations = new Queue<IOperation>();
operationA.Setup(o => o.Revert()).Callback(() => a = ++current);
operationB.Setup(o => o.Revert()).Callback(() => b = ++current);
operationC.Setup(o => o.Revert()).Callback(() => c = ++current);
operationC.Setup(o => o.Perform()).Throws<Exception>();
operationD.Setup(o => o.Revert()).Callback(() => d = ++current);
operations.Enqueue(operationA.Object);
operations.Enqueue(operationB.Object);
operations.Enqueue(operationC.Object);
operations.Enqueue(operationD.Object);
sut.TryInitializeApplication(operations);
Assert.IsTrue(d == 0);
Assert.IsTrue(c == 1);
Assert.IsTrue(b == 2);
Assert.IsTrue(a == 3);
}
[TestMethod]
public void MustSucceedWithEmptyQueue()
{
var result = sut.TryInitializeApplication(new Queue<IOperation>());
Assert.IsTrue(result);
}
}
}

View file

@ -54,6 +54,8 @@
<Reference Include="System.Core" /> <Reference Include="System.Core" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Behaviour\StartupControllerTests.cs" />
<Compile Include="Behaviour\ShutdownControllerTests.cs" />
<Compile Include="I18n\TextTests.cs" /> <Compile Include="I18n\TextTests.cs" />
<Compile Include="Logging\LoggerTests.cs" /> <Compile Include="Logging\LoggerTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />

View file

@ -14,7 +14,7 @@ using SafeExamBrowser.Contracts.UserInterface;
namespace SafeExamBrowser.Core.Behaviour.Operations namespace SafeExamBrowser.Core.Behaviour.Operations
{ {
class BrowserInitializationOperation : IOperation public class BrowserInitializationOperation : IOperation
{ {
private IApplicationController browserController; private IApplicationController browserController;
private IApplicationInfo browserInfo; private IApplicationInfo browserInfo;

View file

@ -14,7 +14,7 @@ using SafeExamBrowser.Contracts.UserInterface;
namespace SafeExamBrowser.Core.Behaviour.Operations namespace SafeExamBrowser.Core.Behaviour.Operations
{ {
class ProcessMonitoringOperation : IOperation public class ProcessMonitoringOperation : IOperation
{ {
private ILogger logger; private ILogger logger;
private IProcessMonitor processMonitor; private IProcessMonitor processMonitor;

View file

@ -14,7 +14,7 @@ using SafeExamBrowser.Contracts.UserInterface;
namespace SafeExamBrowser.Core.Behaviour.Operations namespace SafeExamBrowser.Core.Behaviour.Operations
{ {
class TaskbarInitializationOperation : IOperation public class TaskbarInitializationOperation : IOperation
{ {
private ILogger logger; private ILogger logger;
private ITaskbar taskbar; private ITaskbar taskbar;

View file

@ -15,7 +15,7 @@ using SafeExamBrowser.Contracts.UserInterface;
namespace SafeExamBrowser.Core.Behaviour.Operations namespace SafeExamBrowser.Core.Behaviour.Operations
{ {
class WorkingAreaOperation : IOperation public class WorkingAreaOperation : IOperation
{ {
private ILogger logger; private ILogger logger;
private IProcessMonitor processMonitor; private IProcessMonitor processMonitor;

View file

@ -8,7 +8,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading; using System.Threading;
using SafeExamBrowser.Contracts.Behaviour; using SafeExamBrowser.Contracts.Behaviour;
using SafeExamBrowser.Contracts.Configuration; using SafeExamBrowser.Contracts.Configuration;
@ -48,7 +47,7 @@ namespace SafeExamBrowser.Core.Behaviour
this.workingArea = workingArea; this.workingArea = workingArea;
} }
public void FinalizeApplication(Stack<IOperation> operations) public void FinalizeApplication(Queue<IOperation> operations)
{ {
try try
{ {
@ -63,17 +62,13 @@ namespace SafeExamBrowser.Core.Behaviour
} }
} }
private void RevertOperations(Stack<IOperation> operations) private void RevertOperations(Queue<IOperation> operations)
{ {
while (operations.Any()) foreach (var operation in operations)
{ {
var operation = operations.Pop();
operation.SplashScreen = splashScreen; operation.SplashScreen = splashScreen;
operation.Revert(); operation.Revert();
splashScreen.Progress();
// TODO: Remove! // TODO: Remove!
Thread.Sleep(250); Thread.Sleep(250);
} }

View file

@ -16,7 +16,6 @@ using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.Monitoring; using SafeExamBrowser.Contracts.Monitoring;
using SafeExamBrowser.Contracts.UserInterface; using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Core.Behaviour.Operations;
namespace SafeExamBrowser.Core.Behaviour namespace SafeExamBrowser.Core.Behaviour
{ {
@ -35,7 +34,7 @@ namespace SafeExamBrowser.Core.Behaviour
private IUiElementFactory uiFactory; private IUiElementFactory uiFactory;
private IWorkingArea workingArea; private IWorkingArea workingArea;
private IEnumerable<IOperation> startupOperations; private Stack<IOperation> stack = new Stack<IOperation>();
public StartupController( public StartupController(
IApplicationController browserController, IApplicationController browserController,
@ -63,18 +62,14 @@ namespace SafeExamBrowser.Core.Behaviour
this.workingArea = workingArea; this.workingArea = workingArea;
} }
public bool TryInitializeApplication(out Stack<IOperation> operations) public bool TryInitializeApplication(Queue<IOperation> operations)
{ {
operations = new Stack<IOperation>();
try try
{ {
CreateStartupOperations();
InitializeApplicationLog(); InitializeApplicationLog();
InitializeSplashScreen(); InitializeSplashScreen(operations.Count);
operations = PerformOperations(); PerformOperations(operations);
FinishInitialization(); FinishInitialization();
@ -83,20 +78,18 @@ namespace SafeExamBrowser.Core.Behaviour
catch (Exception e) catch (Exception e)
{ {
LogAndShowException(e); LogAndShowException(e);
RevertOperations(operations); RevertOperations();
FinishInitialization(false); FinishInitialization(false);
return false; return false;
} }
} }
private Stack<IOperation> PerformOperations() private void PerformOperations(Queue<IOperation> operations)
{ {
var operations = new Stack<IOperation>(); foreach (var operation in operations)
foreach (var operation in startupOperations)
{ {
operations.Push(operation); stack.Push(operation);
operation.SplashScreen = splashScreen; operation.SplashScreen = splashScreen;
operation.Perform(); operation.Perform();
@ -106,15 +99,13 @@ namespace SafeExamBrowser.Core.Behaviour
// TODO: Remove! // TODO: Remove!
Thread.Sleep(250); Thread.Sleep(250);
} }
return operations;
} }
private void RevertOperations(Stack<IOperation> operations) private void RevertOperations()
{ {
while (operations.Any()) while (stack.Any())
{ {
var operation = operations.Pop(); var operation = stack.Pop();
operation.Revert(); operation.Revert();
splashScreen.Regress(); splashScreen.Regress();
@ -124,17 +115,6 @@ namespace SafeExamBrowser.Core.Behaviour
} }
} }
private void CreateStartupOperations()
{
startupOperations = new IOperation[]
{
new ProcessMonitoringOperation(logger, processMonitor),
new WorkingAreaOperation(logger, processMonitor, taskbar, workingArea),
new TaskbarInitializationOperation(logger, aboutInfo, taskbar, uiFactory),
new BrowserInitializationOperation(browserController, browserInfo, logger, taskbar, uiFactory)
};
}
private void InitializeApplicationLog() private void InitializeApplicationLog()
{ {
var titleLine = $"/* {settings.ProgramTitle}, Version {settings.ProgramVersion}{Environment.NewLine}"; var titleLine = $"/* {settings.ProgramTitle}, Version {settings.ProgramVersion}{Environment.NewLine}";
@ -147,10 +127,10 @@ namespace SafeExamBrowser.Core.Behaviour
logger.Info("--- Initiating startup procedure ---"); logger.Info("--- Initiating startup procedure ---");
} }
private void InitializeSplashScreen() private void InitializeSplashScreen(int operationCount)
{ {
splashScreen = uiFactory.CreateSplashScreen(settings, text); splashScreen = uiFactory.CreateSplashScreen(settings, text);
splashScreen.SetMaxProgress(startupOperations.Count()); splashScreen.SetMaxProgress(operationCount);
splashScreen.UpdateText(Key.SplashScreen_StartupProcedure); splashScreen.UpdateText(Key.SplashScreen_StartupProcedure);
splashScreen.InvokeShow(); splashScreen.InvokeShow();
} }

View file

@ -8,6 +8,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading; using System.Threading;
using System.Windows; using System.Windows;
using SafeExamBrowser.Contracts.Behaviour; using SafeExamBrowser.Contracts.Behaviour;
@ -59,12 +60,12 @@ namespace SafeExamBrowser
instances.BuildObjectGraph(); instances.BuildObjectGraph();
var success = instances.StartupController.TryInitializeApplication(out Stack<IOperation> operations); var success = instances.StartupController.TryInitializeApplication(instances.StartupOperations);
if (success) if (success)
{ {
MainWindow = instances.Taskbar; MainWindow = instances.Taskbar;
MainWindow.Closing += (o, args) => ShutdownApplication(operations); MainWindow.Closing += (o, args) => ShutdownApplication();
MainWindow.Show(); MainWindow.Show();
} }
else else
@ -73,8 +74,10 @@ namespace SafeExamBrowser
} }
} }
private void ShutdownApplication(Stack<IOperation> operations) private void ShutdownApplication()
{ {
var operations = new Queue<IOperation>(instances.StartupOperations.Reverse());
MainWindow.Hide(); MainWindow.Hide();
instances.ShutdownController.FinalizeApplication(operations); instances.ShutdownController.FinalizeApplication(operations);
} }

View file

@ -6,6 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
using System.Collections.Generic;
using SafeExamBrowser.Browser; using SafeExamBrowser.Browser;
using SafeExamBrowser.Configuration; using SafeExamBrowser.Configuration;
using SafeExamBrowser.Contracts.Behaviour; using SafeExamBrowser.Contracts.Behaviour;
@ -15,6 +16,7 @@ using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.Monitoring; using SafeExamBrowser.Contracts.Monitoring;
using SafeExamBrowser.Contracts.UserInterface; using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Core.Behaviour; using SafeExamBrowser.Core.Behaviour;
using SafeExamBrowser.Core.Behaviour.Operations;
using SafeExamBrowser.Core.I18n; using SafeExamBrowser.Core.I18n;
using SafeExamBrowser.Core.Logging; using SafeExamBrowser.Core.Logging;
using SafeExamBrowser.Monitoring.Processes; using SafeExamBrowser.Monitoring.Processes;
@ -38,6 +40,7 @@ namespace SafeExamBrowser
public IShutdownController ShutdownController { get; private set; } public IShutdownController ShutdownController { get; private set; }
public IStartupController StartupController { get; private set; } public IStartupController StartupController { get; private set; }
public Queue<IOperation> StartupOperations { get; private set; }
public Taskbar Taskbar { get; private set; } public Taskbar Taskbar { get; private set; }
public void BuildObjectGraph() public void BuildObjectGraph()
@ -59,6 +62,12 @@ namespace SafeExamBrowser
workingArea = new WorkingArea(new ModuleLogger(logger, typeof(WorkingArea))); workingArea = new WorkingArea(new ModuleLogger(logger, typeof(WorkingArea)));
ShutdownController = new ShutdownController(logger, messageBox, processMonitor, settings, text, uiFactory, workingArea); ShutdownController = new ShutdownController(logger, messageBox, processMonitor, settings, text, uiFactory, workingArea);
StartupController = new StartupController(browserController, browserInfo, logger, messageBox, aboutInfo, processMonitor, settings, Taskbar, text, uiFactory, workingArea); StartupController = new StartupController(browserController, browserInfo, logger, messageBox, aboutInfo, processMonitor, settings, Taskbar, text, uiFactory, workingArea);
StartupOperations = new Queue<IOperation>();
StartupOperations.Enqueue(new ProcessMonitoringOperation(logger, processMonitor));
StartupOperations.Enqueue(new WorkingAreaOperation(logger, processMonitor, Taskbar, workingArea));
StartupOperations.Enqueue(new TaskbarInitializationOperation(logger, aboutInfo, Taskbar, uiFactory));
StartupOperations.Enqueue(new BrowserInitializationOperation(browserController, browserInfo, logger, Taskbar, uiFactory));
} }
} }
} }