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>
/// Reverts any changes performed during the startup or runtime and releases all used resources.
/// </summary>
void FinalizeApplication(Stack<IOperation> operations);
void FinalizeApplication(Queue<IOperation> operations);
}
}

View file

@ -13,10 +13,9 @@ namespace SafeExamBrowser.Contracts.Behaviour
public interface IStartupController
{
/// <summary>
/// Tries to initialize the application. Returns <c>true</c> if the initialization was successful,
/// <c>false</c> otherwise. All operations performed during the startup procedure will be registered
/// to the given <c>out</c> parameter.
/// Tries to initialize the application according to the given queue of operations.
/// Returns <c>true</c> if the initialization was successful, <c>false</c> otherwise.
/// </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" />
</ItemGroup>
<ItemGroup>
<Compile Include="Behaviour\StartupControllerTests.cs" />
<Compile Include="Behaviour\ShutdownControllerTests.cs" />
<Compile Include="I18n\TextTests.cs" />
<Compile Include="Logging\LoggerTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />

View file

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

View file

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

View file

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

View file

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

View file

@ -8,7 +8,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using SafeExamBrowser.Contracts.Behaviour;
using SafeExamBrowser.Contracts.Configuration;
@ -48,7 +47,7 @@ namespace SafeExamBrowser.Core.Behaviour
this.workingArea = workingArea;
}
public void FinalizeApplication(Stack<IOperation> operations)
public void FinalizeApplication(Queue<IOperation> operations)
{
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.Revert();
splashScreen.Progress();
// TODO: Remove!
Thread.Sleep(250);
}

View file

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

View file

@ -8,6 +8,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Windows;
using SafeExamBrowser.Contracts.Behaviour;
@ -59,12 +60,12 @@ namespace SafeExamBrowser
instances.BuildObjectGraph();
var success = instances.StartupController.TryInitializeApplication(out Stack<IOperation> operations);
var success = instances.StartupController.TryInitializeApplication(instances.StartupOperations);
if (success)
{
MainWindow = instances.Taskbar;
MainWindow.Closing += (o, args) => ShutdownApplication(operations);
MainWindow.Closing += (o, args) => ShutdownApplication();
MainWindow.Show();
}
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();
instances.ShutdownController.FinalizeApplication(operations);
}

View file

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