SEBWIN-219: Changed and simplified implementation of operation mechanism to allow reconfiguration (i.e. repeating operations).

This commit is contained in:
dbuechel 2018-02-01 08:37:12 +01:00
parent 18b8f66300
commit 196836b7eb
32 changed files with 461 additions and 462 deletions

View file

@ -12,7 +12,7 @@ using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Windows; using System.Windows;
using SafeExamBrowser.Contracts.Behaviour; using SafeExamBrowser.Contracts.Behaviour.Operations;
namespace SafeExamBrowser.Client namespace SafeExamBrowser.Client
{ {
@ -61,18 +61,18 @@ namespace SafeExamBrowser.Client
instances.BuildObjectGraph(); instances.BuildObjectGraph();
var success = instances.StartupController.TryInitializeApplication(instances.StartupOperations); //var success = instances.StartupController.TryInitializeApplication(instances.StartupOperations);
if (success) //if (success)
{ //{
MainWindow = instances.Taskbar; // MainWindow = instances.Taskbar;
MainWindow.Closing += MainWindow_Closing; // MainWindow.Closing += MainWindow_Closing;
MainWindow.Show(); // MainWindow.Show();
} //}
else //else
{ //{
Shutdown(); // Shutdown();
} //}
} }
private void MainWindow_Closing(object sender, CancelEventArgs e) private void MainWindow_Closing(object sender, CancelEventArgs e)
@ -80,7 +80,7 @@ namespace SafeExamBrowser.Client
var operations = new Queue<IOperation>(instances.StartupOperations.Reverse()); var operations = new Queue<IOperation>(instances.StartupOperations.Reverse());
MainWindow.Hide(); MainWindow.Hide();
instances.ShutdownController.FinalizeApplication(operations); //instances.ShutdownController.FinalizeApplication(operations);
} }
} }
} }

View file

@ -7,6 +7,7 @@
*/ */
using SafeExamBrowser.Contracts.Behaviour; using SafeExamBrowser.Contracts.Behaviour;
using SafeExamBrowser.Contracts.Behaviour.Operations;
using SafeExamBrowser.Contracts.Configuration; using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.Logging;
@ -23,7 +24,7 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
private ITaskbar taskbar; private ITaskbar taskbar;
private IUserInterfaceFactory uiFactory; private IUserInterfaceFactory uiFactory;
public bool AbortStartup { get; private set; } public bool Abort { get; private set; }
public ISplashScreen SplashScreen { private get; set; } public ISplashScreen SplashScreen { private get; set; }
public BrowserOperation( public BrowserOperation(
@ -53,6 +54,11 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
taskbar.AddApplication(browserButton); taskbar.AddApplication(browserButton);
} }
public void Repeat()
{
// Nothing to do here...
}
public void Revert() public void Revert()
{ {
logger.Info("Terminating browser..."); logger.Info("Terminating browser...");

View file

@ -7,6 +7,7 @@
*/ */
using SafeExamBrowser.Contracts.Behaviour; using SafeExamBrowser.Contracts.Behaviour;
using SafeExamBrowser.Contracts.Behaviour.Operations;
using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface; using SafeExamBrowser.Contracts.UserInterface;
@ -18,7 +19,7 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
private ILogger logger; private ILogger logger;
private IClientController controller; private IClientController controller;
public bool AbortStartup { get; private set; } public bool Abort { get; private set; }
public ISplashScreen SplashScreen { private get; set; } public ISplashScreen SplashScreen { private get; set; }
public ClientControllerOperation(IClientController controller, ILogger logger) public ClientControllerOperation(IClientController controller, ILogger logger)
@ -35,6 +36,11 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
controller.Start(); controller.Start();
} }
public void Repeat()
{
// Nothing to do here...
}
public void Revert() public void Revert()
{ {
logger.Info("Stopping event handling..."); logger.Info("Stopping event handling...");

View file

@ -6,7 +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 SafeExamBrowser.Contracts.Behaviour; using SafeExamBrowser.Contracts.Behaviour.Operations;
using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface; using SafeExamBrowser.Contracts.UserInterface;
@ -19,7 +19,7 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
private ILogger logger; private ILogger logger;
private INativeMethods nativeMethods; private INativeMethods nativeMethods;
public bool AbortStartup { get; private set; } public bool Abort { get; private set; }
public ISplashScreen SplashScreen { private get; set; } public ISplashScreen SplashScreen { private get; set; }
public ClipboardOperation(ILogger logger, INativeMethods nativeMethods) public ClipboardOperation(ILogger logger, INativeMethods nativeMethods)
@ -33,6 +33,11 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
EmptyClipboard(); EmptyClipboard();
} }
public void Repeat()
{
// Nothing to do here...
}
public void Revert() public void Revert()
{ {
EmptyClipboard(); EmptyClipboard();

View file

@ -6,7 +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 SafeExamBrowser.Contracts.Behaviour; using SafeExamBrowser.Contracts.Behaviour.Operations;
using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.Monitoring; using SafeExamBrowser.Contracts.Monitoring;
@ -21,7 +21,7 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
private ILogger logger; private ILogger logger;
private ITaskbar taskbar; private ITaskbar taskbar;
public bool AbortStartup { get; private set; } public bool Abort { get; private set; }
public ISplashScreen SplashScreen { private get; set; } public ISplashScreen SplashScreen { private get; set; }
public DisplayMonitorOperation(IDisplayMonitor displayMonitor, ILogger logger, ITaskbar taskbar) public DisplayMonitorOperation(IDisplayMonitor displayMonitor, ILogger logger, ITaskbar taskbar)
@ -41,6 +41,11 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
displayMonitor.StartMonitoringDisplayChanges(); displayMonitor.StartMonitoringDisplayChanges();
} }
public void Repeat()
{
// Nothing to do here...
}
public void Revert() public void Revert()
{ {
logger.Info("Restoring working area..."); logger.Info("Restoring working area...");

View file

@ -6,7 +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 SafeExamBrowser.Contracts.Behaviour; using SafeExamBrowser.Contracts.Behaviour.Operations;
using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.Monitoring; using SafeExamBrowser.Contracts.Monitoring;
@ -21,7 +21,7 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
private ILogger logger; private ILogger logger;
private INativeMethods nativeMethods; private INativeMethods nativeMethods;
public bool AbortStartup { get; private set; } public bool Abort { get; private set; }
public ISplashScreen SplashScreen { private get; set; } public ISplashScreen SplashScreen { private get; set; }
public KeyboardInterceptorOperation( public KeyboardInterceptorOperation(
@ -40,7 +40,11 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
SplashScreen.UpdateText(TextKey.SplashScreen_StartKeyboardInterception); SplashScreen.UpdateText(TextKey.SplashScreen_StartKeyboardInterception);
nativeMethods.RegisterKeyboardHook(keyboardInterceptor); nativeMethods.RegisterKeyboardHook(keyboardInterceptor);
}
public void Repeat()
{
// Nothing to do here...
} }
public void Revert() public void Revert()

View file

@ -6,7 +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 SafeExamBrowser.Contracts.Behaviour; using SafeExamBrowser.Contracts.Behaviour.Operations;
using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.Monitoring; using SafeExamBrowser.Contracts.Monitoring;
@ -21,7 +21,7 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
private IMouseInterceptor mouseInterceptor; private IMouseInterceptor mouseInterceptor;
private INativeMethods nativeMethods; private INativeMethods nativeMethods;
public bool AbortStartup { get; private set; } public bool Abort { get; private set; }
public ISplashScreen SplashScreen { private get; set; } public ISplashScreen SplashScreen { private get; set; }
public MouseInterceptorOperation( public MouseInterceptorOperation(
@ -42,6 +42,11 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
nativeMethods.RegisterMouseHook(mouseInterceptor); nativeMethods.RegisterMouseHook(mouseInterceptor);
} }
public void Repeat()
{
// Nothing to do here...
}
public void Revert() public void Revert()
{ {
logger.Info("Stopping mouse interception..."); logger.Info("Stopping mouse interception...");

View file

@ -6,7 +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 SafeExamBrowser.Contracts.Behaviour; using SafeExamBrowser.Contracts.Behaviour.Operations;
using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.Monitoring; using SafeExamBrowser.Contracts.Monitoring;
@ -19,7 +19,7 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
private ILogger logger; private ILogger logger;
private IProcessMonitor processMonitor; private IProcessMonitor processMonitor;
public bool AbortStartup { get; private set; } public bool Abort { get; private set; }
public ISplashScreen SplashScreen { private get; set; } public ISplashScreen SplashScreen { private get; set; }
public ProcessMonitorOperation(ILogger logger, IProcessMonitor processMonitor) public ProcessMonitorOperation(ILogger logger, IProcessMonitor processMonitor)
@ -41,6 +41,11 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
// TODO // TODO
} }
public void Repeat()
{
// Nothing to do here...
}
public void Revert() public void Revert()
{ {
logger.Info("Stopping process monitoring..."); logger.Info("Stopping process monitoring...");

View file

@ -8,6 +8,7 @@
using SafeExamBrowser.Client.Notifications; using SafeExamBrowser.Client.Notifications;
using SafeExamBrowser.Contracts.Behaviour; using SafeExamBrowser.Contracts.Behaviour;
using SafeExamBrowser.Contracts.Behaviour.Operations;
using SafeExamBrowser.Contracts.Configuration; using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Configuration.Settings; using SafeExamBrowser.Contracts.Configuration.Settings;
using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Contracts.I18n;
@ -31,7 +32,7 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
private IUserInterfaceFactory uiFactory; private IUserInterfaceFactory uiFactory;
private IText text; private IText text;
public bool AbortStartup { get; private set; } public bool Abort { get; private set; }
public ISplashScreen SplashScreen { private get; set; } public ISplashScreen SplashScreen { private get; set; }
public TaskbarOperation( public TaskbarOperation(
@ -82,6 +83,11 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
} }
} }
public void Repeat()
{
// Nothing to do here...
}
public void Revert() public void Revert()
{ {
logger.Info("Terminating taskbar..."); logger.Info("Terminating taskbar...");

View file

@ -6,7 +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 SafeExamBrowser.Contracts.Behaviour; using SafeExamBrowser.Contracts.Behaviour.Operations;
using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.Monitoring; using SafeExamBrowser.Contracts.Monitoring;
@ -19,7 +19,7 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
private ILogger logger; private ILogger logger;
private IWindowMonitor windowMonitor; private IWindowMonitor windowMonitor;
public bool AbortStartup { get; private set; } public bool Abort { get; private set; }
public ISplashScreen SplashScreen { private get; set; } public ISplashScreen SplashScreen { private get; set; }
public WindowMonitorOperation(ILogger logger, IWindowMonitor windowMonitor) public WindowMonitorOperation(ILogger logger, IWindowMonitor windowMonitor)
@ -37,6 +37,11 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
windowMonitor.StartMonitoringWindows(); windowMonitor.StartMonitoringWindows();
} }
public void Repeat()
{
// Nothing to do here...
}
public void Revert() public void Revert()
{ {
logger.Info("Stopping window monitoring..."); logger.Info("Stopping window monitoring...");

View file

@ -11,6 +11,7 @@ using SafeExamBrowser.Browser;
using SafeExamBrowser.Configuration; using SafeExamBrowser.Configuration;
using SafeExamBrowser.Configuration.Settings; using SafeExamBrowser.Configuration.Settings;
using SafeExamBrowser.Contracts.Behaviour; using SafeExamBrowser.Contracts.Behaviour;
using SafeExamBrowser.Contracts.Behaviour.Operations;
using SafeExamBrowser.Contracts.Configuration; using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Configuration.Settings; using SafeExamBrowser.Contracts.Configuration.Settings;
using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Contracts.I18n;
@ -47,8 +48,8 @@ namespace SafeExamBrowser.Client
private IUserInterfaceFactory uiFactory; private IUserInterfaceFactory uiFactory;
private IWindowMonitor windowMonitor; private IWindowMonitor windowMonitor;
internal IShutdownController ShutdownController { get; private set; } //internal IShutdownController ShutdownController { get; private set; }
internal IStartupController StartupController { get; private set; } //internal IStartupController StartupController { get; private set; }
internal Queue<IOperation> StartupOperations { get; private set; } internal Queue<IOperation> StartupOperations { get; private set; }
internal Taskbar Taskbar { get; private set; } internal Taskbar Taskbar { get; private set; }

View file

@ -6,18 +6,21 @@
* 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.Contracts.Behaviour.Operations;
namespace SafeExamBrowser.Contracts.Behaviour namespace SafeExamBrowser.Contracts.Behaviour
{ {
public interface IRuntimeController : IStartupController public interface IRuntimeController
{ {
/// <summary> /// <summary>
/// Reverts any changes performed during the startup or runtime and releases all used resources. /// Reverts any changes, releases all used resources and terminates the runtime.
/// </summary> /// </summary>
void FinalizeApplication(); void Terminate();
/// <summary> /// <summary>
/// Initializes a new session and starts performing the runtime logic / event handling. /// Tries to start the runtime. Returns <c>true</c> if successful, otherwise <c>false</c>.
/// </summary> /// </summary>
void StartSession(); bool TryStart(Queue<IOperation> operations);
} }
} }

View file

@ -1,20 +0,0 @@
/*
* Copyright (c) 2018 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;
namespace SafeExamBrowser.Contracts.Behaviour
{
public interface IShutdownController
{
/// <summary>
/// Reverts any changes performed during the startup or runtime and releases all used resources.
/// </summary>
void FinalizeApplication(Queue<IOperation> operations);
}
}

View file

@ -1,21 +0,0 @@
/*
* Copyright (c) 2018 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;
namespace SafeExamBrowser.Contracts.Behaviour
{
public interface IStartupController
{
/// <summary>
/// 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(Queue<IOperation> operations);
}
}

View file

@ -8,14 +8,14 @@
using SafeExamBrowser.Contracts.UserInterface; using SafeExamBrowser.Contracts.UserInterface;
namespace SafeExamBrowser.Contracts.Behaviour namespace SafeExamBrowser.Contracts.Behaviour.Operations
{ {
public interface IOperation public interface IOperation
{ {
/// <summary> /// <summary>
/// Determines whether the startup procedure to which this operation belongs should be aborted. /// Determines whether the procedure to which this operation belongs should be aborted.
/// </summary> /// </summary>
bool AbortStartup { get; } bool Abort { get; }
/// <summary> /// <summary>
/// The splash screen to be used to show status information to the user. /// The splash screen to be used to show status information to the user.
@ -27,6 +27,11 @@ namespace SafeExamBrowser.Contracts.Behaviour
/// </summary> /// </summary>
void Perform(); void Perform();
/// <summary>
/// Repeats the operation.
/// </summary>
void Repeat();
/// <summary> /// <summary>
/// Reverts all changes which were made when performing the operation. /// Reverts all changes which were made when performing the operation.
/// </summary> /// </summary>

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2018 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;
namespace SafeExamBrowser.Contracts.Behaviour.Operations
{
public interface IOperationSequence
{
/// <summary>
/// Tries to perform the given sequence of operations. Returns <c>true</c> if the procedure was successful, <c>false</c> otherwise.
/// </summary>
bool TryPerform(Queue<IOperation> operations);
/// <summary>
/// Tries to repeat all operations of this sequence. Returns <c>true</c> if the procedure was successful, <c>false</c> otherwise.
/// </summary>
bool TryRepeat();
/// <summary>
/// Tries to revert all operations of this sequence. Returns <c>true</c> if the procedure was successful, <c>false</c> otherwise.
/// </summary>
bool TryRevert();
}
}

View file

@ -55,6 +55,7 @@
<ItemGroup> <ItemGroup>
<Compile Include="Behaviour\IApplicationController.cs" /> <Compile Include="Behaviour\IApplicationController.cs" />
<Compile Include="Behaviour\IRuntimeController.cs" /> <Compile Include="Behaviour\IRuntimeController.cs" />
<Compile Include="Behaviour\Operations\IOperationSequence.cs" />
<Compile Include="Communication\ICommunication.cs" /> <Compile Include="Communication\ICommunication.cs" />
<Compile Include="Communication\IClientProxy.cs" /> <Compile Include="Communication\IClientProxy.cs" />
<Compile Include="Communication\IRuntimeProxy.cs" /> <Compile Include="Communication\IRuntimeProxy.cs" />
@ -65,7 +66,7 @@
<Compile Include="Configuration\IRuntimeInfo.cs" /> <Compile Include="Configuration\IRuntimeInfo.cs" />
<Compile Include="Configuration\Settings\ConfigurationMode.cs" /> <Compile Include="Configuration\Settings\ConfigurationMode.cs" />
<Compile Include="Behaviour\INotificationController.cs" /> <Compile Include="Behaviour\INotificationController.cs" />
<Compile Include="Behaviour\IOperation.cs" /> <Compile Include="Behaviour\Operations\IOperation.cs" />
<Compile Include="Behaviour\IClientController.cs" /> <Compile Include="Behaviour\IClientController.cs" />
<Compile Include="Configuration\IIconResource.cs" /> <Compile Include="Configuration\IIconResource.cs" />
<Compile Include="Configuration\IApplicationInfo.cs" /> <Compile Include="Configuration\IApplicationInfo.cs" />
@ -77,8 +78,6 @@
<Compile Include="Configuration\Settings\IKeyboardSettings.cs" /> <Compile Include="Configuration\Settings\IKeyboardSettings.cs" />
<Compile Include="Configuration\Settings\IMouseSettings.cs" /> <Compile Include="Configuration\Settings\IMouseSettings.cs" />
<Compile Include="Configuration\Settings\ISettings.cs" /> <Compile Include="Configuration\Settings\ISettings.cs" />
<Compile Include="Behaviour\IShutdownController.cs" />
<Compile Include="Behaviour\IStartupController.cs" />
<Compile Include="Configuration\Settings\ISettingsRepository.cs" /> <Compile Include="Configuration\Settings\ISettingsRepository.cs" />
<Compile Include="Configuration\Settings\ITaskbarSettings.cs" /> <Compile Include="Configuration\Settings\ITaskbarSettings.cs" />
<Compile Include="Configuration\Settings\KioskMode.cs" /> <Compile Include="Configuration\Settings\KioskMode.cs" />

View file

@ -10,40 +10,39 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq; using Moq;
using SafeExamBrowser.Contracts.Behaviour; using SafeExamBrowser.Contracts.Behaviour.Operations;
using SafeExamBrowser.Contracts.Configuration; using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface; using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Core.Behaviour; using SafeExamBrowser.Core.Behaviour.Operations;
namespace SafeExamBrowser.Core.UnitTests.Behaviour namespace SafeExamBrowser.Core.UnitTests.Behaviour.Operations
{ {
[TestClass] [TestClass]
public class StartupControllerTests public class OperationSequenceTests
{ {
private Mock<ILogger> loggerMock; private Mock<ILogger> loggerMock;
private Mock<IRuntimeInfo> runtimeInfoMock; private Mock<IRuntimeInfo> runtimeInfoMock;
private Mock<ISystemInfo> systemInfoMock;
private Mock<IText> textMock; private Mock<IText> textMock;
private Mock<IUserInterfaceFactory> uiFactoryMock; private Mock<IUserInterfaceFactory> uiFactoryMock;
private IStartupController sut; private IOperationSequence sut;
[TestInitialize] [TestInitialize]
public void Initialize() public void Initialize()
{ {
loggerMock = new Mock<ILogger>(); loggerMock = new Mock<ILogger>();
runtimeInfoMock = new Mock<IRuntimeInfo>(); runtimeInfoMock = new Mock<IRuntimeInfo>();
systemInfoMock = new Mock<ISystemInfo>();
textMock = new Mock<IText>(); textMock = new Mock<IText>();
uiFactoryMock = new Mock<IUserInterfaceFactory>(); uiFactoryMock = new Mock<IUserInterfaceFactory>();
uiFactoryMock.Setup(f => f.CreateSplashScreen(runtimeInfoMock.Object, textMock.Object)).Returns(new Mock<ISplashScreen>().Object); uiFactoryMock.Setup(f => f.CreateSplashScreen(runtimeInfoMock.Object, textMock.Object)).Returns(new Mock<ISplashScreen>().Object);
sut = new StartupController(loggerMock.Object, runtimeInfoMock.Object, systemInfoMock.Object, textMock.Object, uiFactoryMock.Object); sut = new OperationSequence(loggerMock.Object, runtimeInfoMock.Object, textMock.Object, uiFactoryMock.Object);
} }
#region Perform Tests
[TestMethod] [TestMethod]
public void MustCorrectlyAbortProcess() public void MustCorrectlyAbortProcess()
{ {
@ -52,13 +51,13 @@ namespace SafeExamBrowser.Core.UnitTests.Behaviour
var operationC = new Mock<IOperation>(); var operationC = new Mock<IOperation>();
var operations = new Queue<IOperation>(); var operations = new Queue<IOperation>();
operationB.SetupGet(o => o.AbortStartup).Returns(true); operationB.SetupGet(o => o.Abort).Returns(true);
operations.Enqueue(operationA.Object); operations.Enqueue(operationA.Object);
operations.Enqueue(operationB.Object); operations.Enqueue(operationB.Object);
operations.Enqueue(operationC.Object); operations.Enqueue(operationC.Object);
var result = sut.TryInitializeApplication(operations); var success = sut.TryPerform(operations);
operationA.Verify(o => o.Perform(), Times.Once); operationA.Verify(o => o.Perform(), Times.Once);
operationA.Verify(o => o.Revert(), Times.Once); operationA.Verify(o => o.Revert(), Times.Once);
@ -67,7 +66,7 @@ namespace SafeExamBrowser.Core.UnitTests.Behaviour
operationC.Verify(o => o.Perform(), Times.Never); operationC.Verify(o => o.Perform(), Times.Never);
operationC.Verify(o => o.Revert(), Times.Never); operationC.Verify(o => o.Revert(), Times.Never);
Assert.IsFalse(result); Assert.IsFalse(success);
} }
[TestMethod] [TestMethod]
@ -82,7 +81,7 @@ namespace SafeExamBrowser.Core.UnitTests.Behaviour
operations.Enqueue(operationB.Object); operations.Enqueue(operationB.Object);
operations.Enqueue(operationC.Object); operations.Enqueue(operationC.Object);
var result = sut.TryInitializeApplication(operations); var success = sut.TryPerform(operations);
operationA.Verify(o => o.Perform(), Times.Once); operationA.Verify(o => o.Perform(), Times.Once);
operationA.Verify(o => o.Revert(), Times.Never); operationA.Verify(o => o.Revert(), Times.Never);
@ -91,7 +90,7 @@ namespace SafeExamBrowser.Core.UnitTests.Behaviour
operationC.Verify(o => o.Perform(), Times.Once); operationC.Verify(o => o.Perform(), Times.Once);
operationC.Verify(o => o.Revert(), Times.Never); operationC.Verify(o => o.Revert(), Times.Never);
Assert.IsTrue(result); Assert.IsTrue(success);
} }
[TestMethod] [TestMethod]
@ -111,8 +110,9 @@ namespace SafeExamBrowser.Core.UnitTests.Behaviour
operations.Enqueue(operationB.Object); operations.Enqueue(operationB.Object);
operations.Enqueue(operationC.Object); operations.Enqueue(operationC.Object);
sut.TryInitializeApplication(operations); var success = sut.TryPerform(operations);
Assert.IsTrue(success);
Assert.IsTrue(a == 1); Assert.IsTrue(a == 1);
Assert.IsTrue(b == 2); Assert.IsTrue(b == 2);
Assert.IsTrue(c == 3); Assert.IsTrue(c == 3);
@ -134,7 +134,7 @@ namespace SafeExamBrowser.Core.UnitTests.Behaviour
operations.Enqueue(operationC.Object); operations.Enqueue(operationC.Object);
operations.Enqueue(operationD.Object); operations.Enqueue(operationD.Object);
var result = sut.TryInitializeApplication(operations); var success = sut.TryPerform(operations);
operationA.Verify(o => o.Perform(), Times.Once); operationA.Verify(o => o.Perform(), Times.Once);
operationA.Verify(o => o.Revert(), Times.Once); operationA.Verify(o => o.Revert(), Times.Once);
@ -145,11 +145,11 @@ namespace SafeExamBrowser.Core.UnitTests.Behaviour
operationD.Verify(o => o.Perform(), Times.Never); operationD.Verify(o => o.Perform(), Times.Never);
operationD.Verify(o => o.Revert(), Times.Never); operationD.Verify(o => o.Revert(), Times.Never);
Assert.IsFalse(result); Assert.IsFalse(success);
} }
[TestMethod] [TestMethod]
public void MustRevertOperationsInSequence() public void MustRevertOperationsInSequenceAfterPerformError()
{ {
int current = 0, a = 0, b = 0, c = 0, d = 0; int current = 0, a = 0, b = 0, c = 0, d = 0;
var operationA = new Mock<IOperation>(); var operationA = new Mock<IOperation>();
@ -169,8 +169,9 @@ namespace SafeExamBrowser.Core.UnitTests.Behaviour
operations.Enqueue(operationC.Object); operations.Enqueue(operationC.Object);
operations.Enqueue(operationD.Object); operations.Enqueue(operationD.Object);
sut.TryInitializeApplication(operations); var success = sut.TryPerform(operations);
Assert.IsFalse(success);
Assert.IsTrue(d == 0); Assert.IsTrue(d == 0);
Assert.IsTrue(c == 1); Assert.IsTrue(c == 1);
Assert.IsTrue(b == 2); Assert.IsTrue(b == 2);
@ -178,7 +179,7 @@ namespace SafeExamBrowser.Core.UnitTests.Behaviour
} }
[TestMethod] [TestMethod]
public void MustContinueToRevertOperationsInCaseOfError() public void MustContinueToRevertOperationsAfterPerformError()
{ {
var operationA = new Mock<IOperation>(); var operationA = new Mock<IOperation>();
var operationB = new Mock<IOperation>(); var operationB = new Mock<IOperation>();
@ -194,7 +195,7 @@ namespace SafeExamBrowser.Core.UnitTests.Behaviour
operations.Enqueue(operationB.Object); operations.Enqueue(operationB.Object);
operations.Enqueue(operationC.Object); operations.Enqueue(operationC.Object);
var result = sut.TryInitializeApplication(operations); var result = sut.TryPerform(operations);
operationA.Verify(o => o.Perform(), Times.Once); operationA.Verify(o => o.Perform(), Times.Once);
operationA.Verify(o => o.Revert(), Times.Once); operationA.Verify(o => o.Revert(), Times.Once);
@ -207,7 +208,7 @@ namespace SafeExamBrowser.Core.UnitTests.Behaviour
[TestMethod] [TestMethod]
public void MustSucceedWithEmptyQueue() public void MustSucceedWithEmptyQueue()
{ {
var result = sut.TryInitializeApplication(new Queue<IOperation>()); var result = sut.TryPerform(new Queue<IOperation>());
Assert.IsTrue(result); Assert.IsTrue(result);
} }
@ -217,7 +218,151 @@ namespace SafeExamBrowser.Core.UnitTests.Behaviour
public void MustNotFailInCaseOfUnexpectedError() public void MustNotFailInCaseOfUnexpectedError()
{ {
uiFactoryMock.Setup(l => l.CreateSplashScreen(It.IsAny<IRuntimeInfo>(), It.IsAny<IText>())).Throws(new Exception()); uiFactoryMock.Setup(l => l.CreateSplashScreen(It.IsAny<IRuntimeInfo>(), It.IsAny<IText>())).Throws(new Exception());
sut.TryInitializeApplication(new Queue<IOperation>());
var success = sut.TryPerform(new Queue<IOperation>());
Assert.IsFalse(success);
} }
#endregion
#region Repeat Tests
[TestMethod]
public void Fail()
{
// TODO
Assert.Fail();
}
#endregion
#region Revert Tests
[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.TryPerform(operations);
var success = sut.TryRevert();
operationA.Verify(o => o.Revert(), Times.Once);
operationB.Verify(o => o.Revert(), Times.Once);
operationC.Verify(o => o.Revert(), Times.Once);
Assert.IsTrue(success);
}
[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.TryPerform(operations);
var success = sut.TryRevert();
Assert.IsTrue(success);
Assert.IsTrue(c == 1);
Assert.IsTrue(b == 2);
Assert.IsTrue(a == 3);
}
[TestMethod]
public void MustContinueToRevertOperationsInCaseOfError()
{
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()).Throws<Exception>();
operationB.Setup(o => o.Revert()).Throws<Exception>();
operationC.Setup(o => o.Revert()).Throws<Exception>();
operations.Enqueue(operationA.Object);
operations.Enqueue(operationB.Object);
operations.Enqueue(operationC.Object);
sut.TryPerform(operations);
var success = sut.TryRevert();
operationA.Verify(o => o.Revert(), Times.Once);
operationB.Verify(o => o.Revert(), Times.Once);
operationC.Verify(o => o.Revert(), Times.Once);
Assert.IsFalse(success);
}
[TestMethod]
public void MustOnlyRevertPerformedOperations()
{
var operationA = new Mock<IOperation>();
var operationB = new Mock<IOperation>();
var operationC = new Mock<IOperation>();
var operations = new Queue<IOperation>();
operationB.SetupGet(o => o.Abort).Returns(true);
operations.Enqueue(operationA.Object);
operations.Enqueue(operationB.Object);
operations.Enqueue(operationC.Object);
sut.TryPerform(operations);
var success = sut.TryRevert();
operationA.Verify(o => o.Revert(), Times.Once);
operationB.Verify(o => o.Revert(), Times.Once);
operationC.Verify(o => o.Revert(), Times.Never);
Assert.IsTrue(success);
}
[TestMethod]
public void MustNotFailWithEmptyQueueWhenReverting()
{
sut.TryPerform(new Queue<IOperation>());
sut.TryRevert();
}
[TestMethod]
public void MustNotFailWithoutPerformWhenReverting()
{
var success = sut.TryRevert();
Assert.IsTrue(success);
}
[TestMethod]
public void MustNotFailInCaseOfUnexpectedErrorWhenReverting()
{
uiFactoryMock.Setup(l => l.CreateSplashScreen(It.IsAny<IRuntimeInfo>(), It.IsAny<IText>())).Throws(new Exception());
sut.TryRevert();
}
#endregion
} }
} }

View file

@ -1,148 +0,0 @@
/*
* Copyright (c) 2018 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.UserInterface;
using SafeExamBrowser.Core.Behaviour;
namespace SafeExamBrowser.Core.UnitTests.Behaviour
{
[TestClass]
public class ShutdownControllerTests
{
private Mock<ILogger> loggerMock;
private Mock<IRuntimeInfo> runtimeInfoMock;
private Mock<IText> textMock;
private Mock<IUserInterfaceFactory> uiFactoryMock;
private IShutdownController sut;
[TestInitialize]
public void Initialize()
{
loggerMock = new Mock<ILogger>();
runtimeInfoMock = new Mock<IRuntimeInfo>();
textMock = new Mock<IText>();
uiFactoryMock = new Mock<IUserInterfaceFactory>();
uiFactoryMock.Setup(f => f.CreateSplashScreen(runtimeInfoMock.Object, textMock.Object)).Returns(new Mock<ISplashScreen>().Object);
sut = new ShutdownController(loggerMock.Object, runtimeInfoMock.Object, textMock.Object, uiFactoryMock.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);
operationA.Verify(o => o.Perform(), Times.Never);
operationB.Verify(o => o.Revert(), Times.Once);
operationB.Verify(o => o.Perform(), Times.Never);
operationC.Verify(o => o.Revert(), Times.Once);
operationC.Verify(o => o.Perform(), Times.Never);
}
[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 MustContinueToRevertOperationsInCaseOfError()
{
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()).Throws<Exception>();
operationB.Setup(o => o.Revert()).Throws<Exception>();
operationC.Setup(o => o.Revert()).Throws<Exception>();
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 MustNotEvaluateAbortFlag()
{
var operationA = new Mock<IOperation>();
var operationB = new Mock<IOperation>();
var operationC = new Mock<IOperation>();
var operations = new Queue<IOperation>();
operationB.SetupGet(o => o.AbortStartup).Returns(true);
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 MustNotFailWithEmptyQueue()
{
sut.FinalizeApplication(new Queue<IOperation>());
}
[TestMethod]
public void MustNotFailInCaseOfUnexpectedError()
{
uiFactoryMock.Setup(l => l.CreateSplashScreen(It.IsAny<IRuntimeInfo>(), It.IsAny<IText>())).Throws(new Exception());
sut.FinalizeApplication(new Queue<IOperation>());
}
}
}

View file

@ -79,8 +79,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Behaviour\Operations\I18nOperationTests.cs" /> <Compile Include="Behaviour\Operations\I18nOperationTests.cs" />
<Compile Include="Behaviour\StartupControllerTests.cs" /> <Compile Include="Behaviour\Operations\OperationSequenceTests.cs" />
<Compile Include="Behaviour\ShutdownControllerTests.cs" />
<Compile Include="I18n\TextTests.cs" /> <Compile Include="I18n\TextTests.cs" />
<Compile Include="I18n\XmlTextResourceTests.cs" /> <Compile Include="I18n\XmlTextResourceTests.cs" />
<Compile Include="Logging\DefaultLogFormatterTests.cs" /> <Compile Include="Logging\DefaultLogFormatterTests.cs" />

View file

@ -9,7 +9,7 @@
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
using SafeExamBrowser.Contracts.Behaviour; using SafeExamBrowser.Contracts.Behaviour.Operations;
using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface; using SafeExamBrowser.Contracts.UserInterface;
@ -22,7 +22,7 @@ namespace SafeExamBrowser.Core.Behaviour.Operations
private ILogger logger; private ILogger logger;
private IText text; private IText text;
public bool AbortStartup { get; private set; } public bool Abort { get; private set; }
public ISplashScreen SplashScreen { private get; set; } public ISplashScreen SplashScreen { private get; set; }
public I18nOperation(ILogger logger, IText text) public I18nOperation(ILogger logger, IText text)
@ -42,6 +42,11 @@ namespace SafeExamBrowser.Core.Behaviour.Operations
text.Initialize(textResource); text.Initialize(textResource);
} }
public void Repeat()
{
// Nothing to do here...
}
public void Revert() public void Revert()
{ {
// Nothing to do here... // Nothing to do here...

View file

@ -9,35 +9,33 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using SafeExamBrowser.Contracts.Behaviour; using SafeExamBrowser.Contracts.Behaviour.Operations;
using SafeExamBrowser.Contracts.Configuration; using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface; using SafeExamBrowser.Contracts.UserInterface;
namespace SafeExamBrowser.Core.Behaviour namespace SafeExamBrowser.Core.Behaviour.Operations
{ {
public class StartupController : IStartupController public class OperationSequence : IOperationSequence
{ {
private ILogger logger; private ILogger logger;
private IRuntimeInfo runtimeInfo; private IRuntimeInfo runtimeInfo;
private ISplashScreen splashScreen; private ISplashScreen splashScreen;
private ISystemInfo systemInfo;
private IText text; private IText text;
private IUserInterfaceFactory uiFactory; private IUserInterfaceFactory uiFactory;
private Stack<IOperation> stack = new Stack<IOperation>(); private Stack<IOperation> stack = new Stack<IOperation>();
public StartupController(ILogger logger, IRuntimeInfo runtimeInfo, ISystemInfo systemInfo, IText text, IUserInterfaceFactory uiFactory) public OperationSequence(ILogger logger, IRuntimeInfo runtimeInfo, IText text, IUserInterfaceFactory uiFactory)
{ {
this.logger = logger; this.logger = logger;
this.runtimeInfo = runtimeInfo; this.runtimeInfo = runtimeInfo;
this.systemInfo = systemInfo;
this.text = text; this.text = text;
this.uiFactory = uiFactory; this.uiFactory = uiFactory;
} }
public bool TryInitializeApplication(Queue<IOperation> operations) public bool TryPerform(Queue<IOperation> operations)
{ {
var success = false; var success = false;
@ -50,18 +48,62 @@ namespace SafeExamBrowser.Core.Behaviour
{ {
RevertOperations(); RevertOperations();
} }
Finish(success);
} }
catch (Exception e) catch (Exception e)
{ {
LogAndShowException(e); logger.Error($"Failed to perform operations!", e);
Finish(false); }
finally
{
Finish();
} }
return success; return success;
} }
public bool TryRepeat()
{
throw new NotImplementedException();
}
public bool TryRevert()
{
var success = false;
try
{
Initialize();
success = RevertOperations(false);
}
catch (Exception e)
{
logger.Error($"Failed to revert operations!", e);
}
finally
{
Finish();
}
return success;
}
private void Initialize(int? operationCount = null)
{
splashScreen = uiFactory.CreateSplashScreen(runtimeInfo, text);
if (operationCount.HasValue)
{
splashScreen.SetMaxProgress(operationCount.Value);
}
else
{
splashScreen.SetIndeterminate();
}
splashScreen.UpdateText(TextKey.SplashScreen_StartupProcedure);
splashScreen.Show();
}
private bool Perform(Queue<IOperation> operations) private bool Perform(Queue<IOperation> operations)
{ {
foreach (var operation in operations) foreach (var operation in operations)
@ -75,12 +117,12 @@ namespace SafeExamBrowser.Core.Behaviour
} }
catch (Exception e) catch (Exception e)
{ {
LogAndShowException(e); logger.Error($"Failed to perform operation '{operation.GetType().Name}'!", e);
return false; return false;
} }
if (operation.AbortStartup) if (operation.Abort)
{ {
return false; return false;
} }
@ -91,8 +133,10 @@ namespace SafeExamBrowser.Core.Behaviour
return true; return true;
} }
private void RevertOperations() private bool RevertOperations(bool regress = true)
{ {
var success = true;
while (stack.Any()) while (stack.Any())
{ {
var operation = stack.Pop(); var operation = stack.Pop();
@ -104,41 +148,20 @@ namespace SafeExamBrowser.Core.Behaviour
catch (Exception e) catch (Exception e)
{ {
logger.Error($"Failed to revert operation '{operation.GetType().Name}'!", e); logger.Error($"Failed to revert operation '{operation.GetType().Name}'!", e);
success = false;
} }
splashScreen.Regress(); if (regress)
} {
} splashScreen.Regress();
}
private void Initialize(int operationCount)
{
logger.Info("--- Initiating startup procedure ---");
splashScreen = uiFactory.CreateSplashScreen(runtimeInfo, text);
splashScreen.SetMaxProgress(operationCount);
splashScreen.UpdateText(TextKey.SplashScreen_StartupProcedure);
splashScreen.Show();
}
private void LogAndShowException(Exception e)
{
logger.Error($"Failed to initialize application!", e);
uiFactory.Show(text.Get(TextKey.MessageBox_StartupError), text.Get(TextKey.MessageBox_StartupErrorTitle), icon: MessageBoxIcon.Error);
logger.Info("Reverting operations...");
}
private void Finish(bool success = true)
{
if (success)
{
logger.Info("--- Application successfully initialized! ---");
logger.Log(string.Empty);
}
else
{
logger.Info("--- Startup procedure aborted! ---");
} }
return success;
}
private void Finish()
{
splashScreen?.Close(); splashScreen?.Close();
} }
} }

View file

@ -1,98 +0,0 @@
/*
* Copyright (c) 2018 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 SafeExamBrowser.Contracts.Behaviour;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface;
namespace SafeExamBrowser.Core.Behaviour
{
public class ShutdownController : IShutdownController
{
private ILogger logger;
private IRuntimeInfo runtimeInfo;
private ISplashScreen splashScreen;
private IText text;
private IUserInterfaceFactory uiFactory;
public ShutdownController(ILogger logger, IRuntimeInfo runtimeInfo, IText text, IUserInterfaceFactory uiFactory)
{
this.logger = logger;
this.runtimeInfo = runtimeInfo;
this.text = text;
this.uiFactory = uiFactory;
}
public void FinalizeApplication(Queue<IOperation> operations)
{
try
{
Initialize();
Revert(operations);
Finish();
}
catch (Exception e)
{
LogAndShowException(e);
Finish(false);
}
}
private void Revert(Queue<IOperation> operations)
{
foreach (var operation in operations)
{
operation.SplashScreen = splashScreen;
try
{
operation.Revert();
}
catch (Exception e)
{
logger.Error($"Failed to revert operation '{operation.GetType().Name}'!", e);
}
}
}
private void Initialize()
{
logger.Log(string.Empty);
logger.Info("--- Initiating shutdown procedure ---");
splashScreen = uiFactory.CreateSplashScreen(runtimeInfo, text);
splashScreen.SetIndeterminate();
splashScreen.UpdateText(TextKey.SplashScreen_ShutdownProcedure);
splashScreen.Show();
}
private void LogAndShowException(Exception e)
{
logger.Error($"Failed to finalize application!", e);
uiFactory.Show(text.Get(TextKey.MessageBox_ShutdownError), text.Get(TextKey.MessageBox_ShutdownErrorTitle), icon: MessageBoxIcon.Error);
}
private void Finish(bool success = true)
{
if (success)
{
logger.Info("--- Application successfully finalized! ---");
}
else
{
logger.Info("--- Shutdown procedure failed! ---");
}
splashScreen?.Close();
}
}
}

View file

@ -56,8 +56,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Behaviour\Operations\I18nOperation.cs" /> <Compile Include="Behaviour\Operations\I18nOperation.cs" />
<Compile Include="Behaviour\ShutdownController.cs" /> <Compile Include="Behaviour\Operations\OperationSequence.cs" />
<Compile Include="Behaviour\StartupController.cs" />
<Compile Include="Communication\BaseProxy.cs" /> <Compile Include="Communication\BaseProxy.cs" />
<Compile Include="Communication\Messages\Message.cs" /> <Compile Include="Communication\Messages\Message.cs" />
<Compile Include="Communication\ServiceProxy.cs" /> <Compile Include="Communication\ServiceProxy.cs" />

View file

@ -166,7 +166,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
sut.Perform(); sut.Perform();
Assert.IsTrue(sut.AbortStartup); Assert.IsTrue(sut.Abort);
} }
[TestMethod] [TestMethod]
@ -181,7 +181,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
sut.Perform(); sut.Perform();
Assert.IsFalse(sut.AbortStartup); Assert.IsFalse(sut.Abort);
} }
} }
} }

View file

@ -91,7 +91,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
sut.Perform(); sut.Perform();
Assert.IsTrue(sut.AbortStartup); Assert.IsTrue(sut.Abort);
} }
[TestMethod] [TestMethod]
@ -103,7 +103,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
sut.Perform(); sut.Perform();
service.VerifySet(s => s.Ignore = true); service.VerifySet(s => s.Ignore = true);
Assert.IsFalse(sut.AbortStartup); Assert.IsFalse(sut.Abort);
} }
[TestMethod] [TestMethod]

View file

@ -60,13 +60,9 @@ namespace SafeExamBrowser.Runtime
instances.BuildObjectGraph(); instances.BuildObjectGraph();
instances.LogStartupInformation(); instances.LogStartupInformation();
var success = instances.RuntimeController.TryInitializeApplication(instances.StartupOperations); var success = instances.RuntimeController.TryStart(instances.StartupOperations);
if (success) if (!success)
{
instances.RuntimeController.StartSession();
}
else
{ {
Shutdown(); Shutdown();
} }
@ -74,7 +70,7 @@ namespace SafeExamBrowser.Runtime
protected override void OnExit(ExitEventArgs e) protected override void OnExit(ExitEventArgs e)
{ {
instances.RuntimeController.FinalizeApplication(); instances.RuntimeController.Terminate();
instances.LogShutdownInformation(); instances.LogShutdownInformation();
base.OnExit(e); base.OnExit(e);

View file

@ -8,7 +8,7 @@
using System; using System;
using System.IO; using System.IO;
using SafeExamBrowser.Contracts.Behaviour; using SafeExamBrowser.Contracts.Behaviour.Operations;
using SafeExamBrowser.Contracts.Configuration; using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Configuration.Settings; using SafeExamBrowser.Contracts.Configuration.Settings;
using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Contracts.I18n;
@ -26,7 +26,7 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
private IUserInterfaceFactory uiFactory; private IUserInterfaceFactory uiFactory;
private string[] commandLineArgs; private string[] commandLineArgs;
public bool AbortStartup { get; private set; } public bool Abort { get; private set; }
public ISplashScreen SplashScreen { private get; set; } public ISplashScreen SplashScreen { private get; set; }
public ConfigurationOperation( public ConfigurationOperation(
@ -60,8 +60,8 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
if (settings.ConfigurationMode == ConfigurationMode.ConfigureClient && UserWantsToAbortStartup()) if (settings.ConfigurationMode == ConfigurationMode.ConfigureClient && UserWantsToAbortStartup())
{ {
AbortStartup = true; Abort = true;
logger.Info($"The user chose to {(AbortStartup ? "abort" : "continue")} the application startup after successful client configuration."); logger.Info($"The user chose to {(Abort ? "abort" : "continue")} the application startup after successful client configuration.");
} }
} }
else else
@ -71,6 +71,11 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
} }
} }
public void Repeat()
{
// TODO
}
public void Revert() public void Revert()
{ {
// Nothing to do here... // Nothing to do here...

View file

@ -6,7 +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 SafeExamBrowser.Contracts.Behaviour; using SafeExamBrowser.Contracts.Behaviour.Operations;
using SafeExamBrowser.Contracts.Configuration.Settings; using SafeExamBrowser.Contracts.Configuration.Settings;
using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.Logging;
@ -20,7 +20,7 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
private ISettingsRepository settingsRepository; private ISettingsRepository settingsRepository;
private KioskMode kioskMode; private KioskMode kioskMode;
public bool AbortStartup { get; private set; } public bool Abort { get; private set; }
public ISplashScreen SplashScreen { private get; set; } public ISplashScreen SplashScreen { private get; set; }
public KioskModeOperation(ILogger logger, ISettingsRepository settingsRepository) public KioskModeOperation(ILogger logger, ISettingsRepository settingsRepository)
@ -46,6 +46,11 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
} }
} }
public void Repeat()
{
// TODO
}
public void Revert() public void Revert()
{ {
logger.Info($"Reverting kiosk mode '{kioskMode}'..."); logger.Info($"Reverting kiosk mode '{kioskMode}'...");
@ -63,22 +68,22 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
private void CreateNewDesktop() private void CreateNewDesktop()
{ {
// TODO
} }
private void CloseNewDesktop() private void CloseNewDesktop()
{ {
// TODO
} }
private void DisableExplorerShell() private void DisableExplorerShell()
{ {
// TODO
} }
private void RestartExplorerShell() private void RestartExplorerShell()
{ {
// TODO
} }
} }
} }

View file

@ -7,7 +7,7 @@
*/ */
using System; using System;
using SafeExamBrowser.Contracts.Behaviour; using SafeExamBrowser.Contracts.Behaviour.Operations;
using SafeExamBrowser.Contracts.Communication; using SafeExamBrowser.Contracts.Communication;
using SafeExamBrowser.Contracts.Configuration.Settings; using SafeExamBrowser.Contracts.Configuration.Settings;
using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Contracts.I18n;
@ -25,7 +25,7 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
private ISettingsRepository settingsRepository; private ISettingsRepository settingsRepository;
private IText text; private IText text;
public bool AbortStartup { get; private set; } public bool Abort { get; private set; }
public ISplashScreen SplashScreen { private get; set; } public ISplashScreen SplashScreen { private get; set; }
public ServiceOperation(ILogger logger, IServiceProxy service, ISettingsRepository settingsRepository, IText text) public ServiceOperation(ILogger logger, IServiceProxy service, ISettingsRepository settingsRepository, IText text)
@ -48,34 +48,26 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
} }
catch (Exception e) catch (Exception e)
{ {
var message = "Failed to connect to the service component!"; LogException(e);
if (serviceMandatory)
{
logger.Error(message, e);
}
else
{
logger.Info($"{message} Reason: {e.Message}");
}
} }
if (serviceMandatory && !serviceAvailable) if (serviceMandatory && !serviceAvailable)
{ {
AbortStartup = true; Abort = true;
logger.Info("Aborting startup because the service is mandatory but not available!"); logger.Info("Aborting startup because the service is mandatory but not available!");
} }
else if (!serviceAvailable)
{
service.Ignore = true;
logger.Info("All service-related operations will be ignored, since the service is optional and not available.");
}
else else
{ {
logger.Info($"The service is {(serviceMandatory ? "mandatory" : "optional")} and available."); service.Ignore = !serviceAvailable;
logger.Info($"The service is {(serviceMandatory ? "mandatory" : "optional")} and {(serviceAvailable ? "available." : "not available. All service-related operations will be ignored!")}");
} }
} }
public void Repeat()
{
// TODO
}
public void Revert() public void Revert()
{ {
logger.Info("Closing service connection..."); logger.Info("Closing service connection...");
@ -93,5 +85,19 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
} }
} }
} }
private void LogException(Exception e)
{
var message = "Failed to connect to the service component!";
if (serviceMandatory)
{
logger.Error(message, e);
}
else
{
logger.Info($"{message} Reason: {e.Message}");
}
}
} }
} }

View file

@ -8,8 +8,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using SafeExamBrowser.Contracts.Behaviour; using SafeExamBrowser.Contracts.Behaviour;
using SafeExamBrowser.Contracts.Behaviour.Operations;
using SafeExamBrowser.Contracts.Communication; using SafeExamBrowser.Contracts.Communication;
using SafeExamBrowser.Contracts.Configuration; using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Configuration.Settings; using SafeExamBrowser.Contracts.Configuration.Settings;
@ -21,52 +21,56 @@ namespace SafeExamBrowser.Runtime.Behaviour
{ {
internal class RuntimeController : IRuntimeController internal class RuntimeController : IRuntimeController
{ {
private Queue<IOperation> operations;
private ILogger logger; private ILogger logger;
private IRuntimeInfo runtimeInfo; private IRuntimeInfo runtimeInfo;
private IRuntimeWindow runtimeWindow; private IRuntimeWindow runtimeWindow;
private IServiceProxy serviceProxy; private IServiceProxy serviceProxy;
private ISettingsRepository settingsRepository; private ISettingsRepository settingsRepository;
private IShutdownController shutdownController; private IOperationSequence operationSequence;
private IStartupController startupController;
private Action terminationCallback; private Action terminationCallback;
private IText text; private IText text;
private IUserInterfaceFactory uiFactory; private IUserInterfaceFactory uiFactory;
public RuntimeController( public RuntimeController(
ILogger logger, ILogger logger,
IOperationSequence operationSequence,
IRuntimeInfo runtimeInfo, IRuntimeInfo runtimeInfo,
IServiceProxy serviceProxy, IServiceProxy serviceProxy,
ISettingsRepository settingsRepository, ISettingsRepository settingsRepository,
IShutdownController shutdownController,
IStartupController startupController,
Action terminationCallback, Action terminationCallback,
IText text, IText text,
IUserInterfaceFactory uiFactory) IUserInterfaceFactory uiFactory)
{ {
this.logger = logger; this.logger = logger;
this.operationSequence = operationSequence;
this.runtimeInfo = runtimeInfo; this.runtimeInfo = runtimeInfo;
this.serviceProxy = serviceProxy; this.serviceProxy = serviceProxy;
this.settingsRepository = settingsRepository; this.settingsRepository = settingsRepository;
this.shutdownController = shutdownController;
this.startupController = startupController;
this.terminationCallback = terminationCallback; this.terminationCallback = terminationCallback;
this.text = text; this.text = text;
this.uiFactory = uiFactory; this.uiFactory = uiFactory;
operations = new Queue<IOperation>();
} }
public bool TryInitializeApplication(Queue<IOperation> operations) public bool TryStart(Queue<IOperation> operations)
{ {
var success = startupController.TryInitializeApplication(operations); logger.Info("--- Initiating startup procedure ---");
var success = operationSequence.TryPerform(operations);
runtimeWindow = uiFactory.CreateRuntimeWindow(runtimeInfo, text); runtimeWindow = uiFactory.CreateRuntimeWindow(runtimeInfo, text);
if (success) if (success)
{ {
this.operations = new Queue<IOperation>(operations); logger.Info("--- Application successfully initialized! ---");
logger.Log(string.Empty);
logger.Subscribe(runtimeWindow); logger.Subscribe(runtimeWindow);
StartSession();
}
else
{
logger.Info("--- Application startup aborted! ---");
logger.Log(string.Empty);
} }
return success; return success;
@ -95,9 +99,11 @@ namespace SafeExamBrowser.Runtime.Behaviour
{ {
runtimeWindow.Hide(); runtimeWindow.Hide();
} }
terminationCallback.Invoke();
} }
public void FinalizeApplication() public void Terminate()
{ {
StopSession(); StopSession();
@ -108,7 +114,20 @@ namespace SafeExamBrowser.Runtime.Behaviour
logger.Unsubscribe(runtimeWindow); logger.Unsubscribe(runtimeWindow);
runtimeWindow.Close(); runtimeWindow.Close();
shutdownController.FinalizeApplication(new Queue<IOperation>(operations.Reverse()));
logger.Log(string.Empty);
logger.Info("--- Initiating shutdown procedure ---");
var success = operationSequence.TryRevert();
if (success)
{
logger.Info("--- Application successfully finalized! ---");
}
else
{
logger.Info("--- Shutdown procedure failed! ---");
}
} }
private void StopSession() private void StopSession()

View file

@ -14,9 +14,9 @@ using System.Windows;
using SafeExamBrowser.Configuration; using SafeExamBrowser.Configuration;
using SafeExamBrowser.Configuration.Settings; using SafeExamBrowser.Configuration.Settings;
using SafeExamBrowser.Contracts.Behaviour; using SafeExamBrowser.Contracts.Behaviour;
using SafeExamBrowser.Contracts.Behaviour.Operations;
using SafeExamBrowser.Contracts.Configuration; using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Core.Behaviour;
using SafeExamBrowser.Core.Behaviour.Operations; using SafeExamBrowser.Core.Behaviour.Operations;
using SafeExamBrowser.Core.Communication; using SafeExamBrowser.Core.Communication;
using SafeExamBrowser.Core.I18n; using SafeExamBrowser.Core.I18n;
@ -52,11 +52,10 @@ namespace SafeExamBrowser.Runtime
InitializeLogging(); InitializeLogging();
var text = new Text(logger); var text = new Text(logger);
var operationSequence = new OperationSequence(logger, runtimeInfo, text, uiFactory);
var serviceProxy = new ServiceProxy(new ModuleLogger(logger, typeof(ServiceProxy)), "net.pipe://localhost/safeexambrowser/service"); var serviceProxy = new ServiceProxy(new ModuleLogger(logger, typeof(ServiceProxy)), "net.pipe://localhost/safeexambrowser/service");
var shutdownController = new ShutdownController(logger, runtimeInfo, text, uiFactory);
var startupController = new StartupController(logger, runtimeInfo, systemInfo, text, uiFactory);
RuntimeController = new RuntimeController(new ModuleLogger(logger, typeof(RuntimeController)), runtimeInfo, serviceProxy, settingsRepository, shutdownController, startupController, Application.Current.Shutdown, text, uiFactory); RuntimeController = new RuntimeController(logger, operationSequence, runtimeInfo, serviceProxy, settingsRepository, Application.Current.Shutdown, text, uiFactory);
StartupOperations = new Queue<IOperation>(); StartupOperations = new Queue<IOperation>();
StartupOperations.Enqueue(new I18nOperation(logger, text)); StartupOperations.Enqueue(new I18nOperation(logger, text));