diff --git a/SafeExamBrowser.Contracts/Behaviour/IShutdownController.cs b/SafeExamBrowser.Contracts/Behaviour/IShutdownController.cs
index a10dc4a4..caa1d1df 100644
--- a/SafeExamBrowser.Contracts/Behaviour/IShutdownController.cs
+++ b/SafeExamBrowser.Contracts/Behaviour/IShutdownController.cs
@@ -15,6 +15,6 @@ namespace SafeExamBrowser.Contracts.Behaviour
///
/// Reverts any changes performed during the startup or runtime and releases all used resources.
///
- void FinalizeApplication(Stack operations);
+ void FinalizeApplication(Queue operations);
}
}
diff --git a/SafeExamBrowser.Contracts/Behaviour/IStartupController.cs b/SafeExamBrowser.Contracts/Behaviour/IStartupController.cs
index 5218c28b..9f0223b9 100644
--- a/SafeExamBrowser.Contracts/Behaviour/IStartupController.cs
+++ b/SafeExamBrowser.Contracts/Behaviour/IStartupController.cs
@@ -13,10 +13,9 @@ namespace SafeExamBrowser.Contracts.Behaviour
public interface IStartupController
{
///
- /// Tries to initialize the application. Returns true if the initialization was successful,
- /// false otherwise. All operations performed during the startup procedure will be registered
- /// to the given out parameter.
+ /// Tries to initialize the application according to the given queue of operations.
+ /// Returns true if the initialization was successful, false otherwise.
///
- bool TryInitializeApplication(out Stack operations);
+ bool TryInitializeApplication(Queue operations);
}
}
diff --git a/SafeExamBrowser.Core.UnitTests/Behaviour/ShutdownControllerTests.cs b/SafeExamBrowser.Core.UnitTests/Behaviour/ShutdownControllerTests.cs
new file mode 100644
index 00000000..f2ad03f9
--- /dev/null
+++ b/SafeExamBrowser.Core.UnitTests/Behaviour/ShutdownControllerTests.cs
@@ -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 browserControllerMock;
+ private Mock browserInfoMock;
+ private Mock loggerMock;
+ private Mock messageBoxMock;
+ private Mock aboutInfoMock;
+ private Mock processMonitorMock;
+ private Mock settingsMock;
+ private Mock taskbarMock;
+ private Mock textMock;
+ private Mock uiFactoryMock;
+ private Mock workingAreaMock;
+
+ private IShutdownController sut;
+
+ [TestInitialize]
+ public void Initialize()
+ {
+ browserControllerMock = new Mock();
+ browserInfoMock = new Mock();
+ loggerMock = new Mock();
+ messageBoxMock = new Mock();
+ aboutInfoMock = new Mock();
+ processMonitorMock = new Mock();
+ settingsMock = new Mock();
+ taskbarMock = new Mock();
+ textMock = new Mock();
+ uiFactoryMock = new Mock();
+ workingAreaMock = new Mock();
+
+ uiFactoryMock.Setup(f => f.CreateSplashScreen(settingsMock.Object, textMock.Object)).Returns(new Mock().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();
+ var operationB = new Mock();
+ var operationC = new Mock();
+ var operations = new Queue();
+
+ 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();
+ var operationB = new Mock();
+ var operationC = new Mock();
+ var operations = new Queue();
+
+ 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());
+ }
+ }
+}
diff --git a/SafeExamBrowser.Core.UnitTests/Behaviour/StartupControllerTests.cs b/SafeExamBrowser.Core.UnitTests/Behaviour/StartupControllerTests.cs
new file mode 100644
index 00000000..281a1996
--- /dev/null
+++ b/SafeExamBrowser.Core.UnitTests/Behaviour/StartupControllerTests.cs
@@ -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 browserControllerMock;
+ private Mock browserInfoMock;
+ private Mock loggerMock;
+ private Mock messageBoxMock;
+ private Mock aboutInfoMock;
+ private Mock processMonitorMock;
+ private Mock settingsMock;
+ private Mock taskbarMock;
+ private Mock textMock;
+ private Mock uiFactoryMock;
+ private Mock workingAreaMock;
+
+ private IStartupController sut;
+
+ [TestInitialize]
+ public void Initialize()
+ {
+ browserControllerMock = new Mock();
+ browserInfoMock = new Mock();
+ loggerMock = new Mock();
+ messageBoxMock = new Mock();
+ aboutInfoMock = new Mock();
+ processMonitorMock = new Mock();
+ settingsMock = new Mock();
+ taskbarMock = new Mock();
+ textMock = new Mock();
+ uiFactoryMock = new Mock();
+ workingAreaMock = new Mock();
+
+ uiFactoryMock.Setup(f => f.CreateSplashScreen(settingsMock.Object, textMock.Object)).Returns(new Mock().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();
+ var operationB = new Mock();
+ var operationC = new Mock();
+ var operations = new Queue();
+
+ 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();
+ var operationB = new Mock();
+ var operationC = new Mock();
+ var operations = new Queue();
+
+ 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();
+ var operationB = new Mock();
+ var operationC = new Mock();
+ var operationD = new Mock();
+ var operations = new Queue();
+
+ operationC.Setup(o => o.Perform()).Throws();
+
+ 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();
+ var operationB = new Mock();
+ var operationC = new Mock();
+ var operationD = new Mock();
+ var operations = new Queue();
+
+ 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();
+ 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());
+
+ Assert.IsTrue(result);
+ }
+ }
+}
diff --git a/SafeExamBrowser.Core.UnitTests/SafeExamBrowser.Core.UnitTests.csproj b/SafeExamBrowser.Core.UnitTests/SafeExamBrowser.Core.UnitTests.csproj
index 7233ac84..6d6a473c 100644
--- a/SafeExamBrowser.Core.UnitTests/SafeExamBrowser.Core.UnitTests.csproj
+++ b/SafeExamBrowser.Core.UnitTests/SafeExamBrowser.Core.UnitTests.csproj
@@ -54,6 +54,8 @@
+
+
diff --git a/SafeExamBrowser.Core/Behaviour/Operations/BrowserInitializationOperation.cs b/SafeExamBrowser.Core/Behaviour/Operations/BrowserInitializationOperation.cs
index c474329c..c489ca08 100644
--- a/SafeExamBrowser.Core/Behaviour/Operations/BrowserInitializationOperation.cs
+++ b/SafeExamBrowser.Core/Behaviour/Operations/BrowserInitializationOperation.cs
@@ -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;
diff --git a/SafeExamBrowser.Core/Behaviour/Operations/ProcessMonitoringOperation.cs b/SafeExamBrowser.Core/Behaviour/Operations/ProcessMonitoringOperation.cs
index cba3de03..16c17e63 100644
--- a/SafeExamBrowser.Core/Behaviour/Operations/ProcessMonitoringOperation.cs
+++ b/SafeExamBrowser.Core/Behaviour/Operations/ProcessMonitoringOperation.cs
@@ -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;
diff --git a/SafeExamBrowser.Core/Behaviour/Operations/TaskbarInitializationOperation.cs b/SafeExamBrowser.Core/Behaviour/Operations/TaskbarInitializationOperation.cs
index 1639a2f3..63552b82 100644
--- a/SafeExamBrowser.Core/Behaviour/Operations/TaskbarInitializationOperation.cs
+++ b/SafeExamBrowser.Core/Behaviour/Operations/TaskbarInitializationOperation.cs
@@ -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;
diff --git a/SafeExamBrowser.Core/Behaviour/Operations/WorkingAreaOperation.cs b/SafeExamBrowser.Core/Behaviour/Operations/WorkingAreaOperation.cs
index fcda9b01..1f995241 100644
--- a/SafeExamBrowser.Core/Behaviour/Operations/WorkingAreaOperation.cs
+++ b/SafeExamBrowser.Core/Behaviour/Operations/WorkingAreaOperation.cs
@@ -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;
diff --git a/SafeExamBrowser.Core/Behaviour/ShutdownController.cs b/SafeExamBrowser.Core/Behaviour/ShutdownController.cs
index 7b41aa68..e238cfa0 100644
--- a/SafeExamBrowser.Core/Behaviour/ShutdownController.cs
+++ b/SafeExamBrowser.Core/Behaviour/ShutdownController.cs
@@ -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 operations)
+ public void FinalizeApplication(Queue operations)
{
try
{
@@ -63,17 +62,13 @@ namespace SafeExamBrowser.Core.Behaviour
}
}
- private void RevertOperations(Stack operations)
+ private void RevertOperations(Queue 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);
}
diff --git a/SafeExamBrowser.Core/Behaviour/StartupController.cs b/SafeExamBrowser.Core/Behaviour/StartupController.cs
index 6bb2bec9..be13b0c0 100644
--- a/SafeExamBrowser.Core/Behaviour/StartupController.cs
+++ b/SafeExamBrowser.Core/Behaviour/StartupController.cs
@@ -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 startupOperations;
+ private Stack stack = new Stack();
public StartupController(
IApplicationController browserController,
@@ -63,18 +62,14 @@ namespace SafeExamBrowser.Core.Behaviour
this.workingArea = workingArea;
}
- public bool TryInitializeApplication(out Stack operations)
+ public bool TryInitializeApplication(Queue operations)
{
- operations = new Stack();
-
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 PerformOperations()
+ private void PerformOperations(Queue operations)
{
- var operations = new Stack();
-
- 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 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();
}
diff --git a/SafeExamBrowser/App.cs b/SafeExamBrowser/App.cs
index 6a295fb5..9cb14d80 100644
--- a/SafeExamBrowser/App.cs
+++ b/SafeExamBrowser/App.cs
@@ -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 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 operations)
+ private void ShutdownApplication()
{
+ var operations = new Queue(instances.StartupOperations.Reverse());
+
MainWindow.Hide();
instances.ShutdownController.FinalizeApplication(operations);
}
diff --git a/SafeExamBrowser/CompositionRoot.cs b/SafeExamBrowser/CompositionRoot.cs
index 6b0cea9a..761bd7b7 100644
--- a/SafeExamBrowser/CompositionRoot.cs
+++ b/SafeExamBrowser/CompositionRoot.cs
@@ -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 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();
+ 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));
}
}
}