SEBWIN-221: Fixed issues with the operation model by separating repeatable from non-repeatable operations and solved conundrum with session operation sequence.
This commit is contained in:
parent
4ca2fac50e
commit
f4631a1a3d
60 changed files with 719 additions and 786 deletions
|
@ -6,7 +6,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using SafeExamBrowser.Client.Operations;
|
||||
|
@ -64,12 +63,5 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
|
|||
|
||||
controllerMock.Verify(c => c.Terminate(), Times.Once);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(InvalidOperationException))]
|
||||
public void MustNotAllowRepeating()
|
||||
{
|
||||
sut.Repeat();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -96,12 +96,5 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
|
|||
|
||||
Assert.AreEqual(OperationResult.Success, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(InvalidOperationException))]
|
||||
public void MustNotAllowRepeating()
|
||||
{
|
||||
sut.Repeat();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using SafeExamBrowser.Client.Operations;
|
||||
|
@ -47,12 +46,5 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
|
|||
|
||||
nativeMethodsMock.Verify(n => n.EmptyClipboard(), Times.Once);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(InvalidOperationException))]
|
||||
public void MustNotAllowRepeating()
|
||||
{
|
||||
sut.Repeat();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using SafeExamBrowser.Client.Operations;
|
||||
|
@ -39,12 +38,5 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
|
|||
{
|
||||
Assert.Fail();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(InvalidOperationException))]
|
||||
public void MustNotAllowRepeating()
|
||||
{
|
||||
sut.Repeat();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using SafeExamBrowser.Client.Operations;
|
||||
|
@ -64,12 +63,5 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
|
|||
displayMonitorMock.Verify(d => d.StopMonitoringDisplayChanges(), Times.Once);
|
||||
displayMonitorMock.Verify(d => d.ResetPrimaryDisplay(), Times.Once);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(InvalidOperationException))]
|
||||
public void MustNotAllowRepeating()
|
||||
{
|
||||
sut.Repeat();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using SafeExamBrowser.Client.Operations;
|
||||
|
@ -50,12 +49,5 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
|
|||
|
||||
nativeMethodsMock.Verify(n => n.DeregisterKeyboardHook(It.IsAny<IKeyboardInterceptor>()), Times.Once);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(InvalidOperationException))]
|
||||
public void MustNotAllowRepeating()
|
||||
{
|
||||
sut.Repeat();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using SafeExamBrowser.Client.Operations;
|
||||
|
@ -50,12 +49,5 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
|
|||
|
||||
nativeMethodsMock.Verify(n => n.DeregisterMouseHook(It.IsAny<IMouseInterceptor>()), Times.Once);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(InvalidOperationException))]
|
||||
public void MustNotAllowRepeating()
|
||||
{
|
||||
sut.Repeat();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using SafeExamBrowser.Client.Operations;
|
||||
|
@ -71,12 +70,5 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
|
|||
processMonitor.Verify(p => p.StartMonitoringExplorer(), Times.Never);
|
||||
processMonitor.Verify(p => p.StopMonitoringExplorer(), Times.Never);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(InvalidOperationException))]
|
||||
public void MustNotAllowRepeating()
|
||||
{
|
||||
sut.Repeat();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,10 +6,8 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using SafeExamBrowser.Client.Operations;
|
||||
using SafeExamBrowser.Contracts.Communication.Proxies;
|
||||
using SafeExamBrowser.Contracts.Logging;
|
||||
|
||||
|
@ -33,14 +31,5 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
|
|||
{
|
||||
Assert.Fail();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(InvalidOperationException))]
|
||||
public void MustNotAllowRepeating()
|
||||
{
|
||||
var sut = new RuntimeConnectionOperation(logger.Object, runtime.Object, Guid.Empty);
|
||||
|
||||
sut.Repeat();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using SafeExamBrowser.Client.Operations;
|
||||
|
@ -94,12 +93,5 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
|
|||
powerSupplyMock.Verify(p => p.Terminate(), Times.Once);
|
||||
wirelessNetworkMock.Verify(w => w.Terminate(), Times.Once);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(InvalidOperationException))]
|
||||
public void MustNotAllowRepeating()
|
||||
{
|
||||
sut.Repeat();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using SafeExamBrowser.Client.Operations;
|
||||
|
@ -106,14 +105,5 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
|
|||
|
||||
windowMonitorMock.VerifyNoOtherCalls();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(InvalidOperationException))]
|
||||
public void MustNotAllowRepeating()
|
||||
{
|
||||
var sut = new WindowMonitorOperation(KioskMode.None, loggerMock.Object, windowMonitorMock.Object);
|
||||
|
||||
sut.Repeat();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -140,7 +140,7 @@ namespace SafeExamBrowser.Client
|
|||
|
||||
DeregisterEvents();
|
||||
|
||||
var success = operations.TryRevert();
|
||||
var success = operations.TryRevert() == OperationResult.Success;
|
||||
|
||||
if (success)
|
||||
{
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using SafeExamBrowser.Contracts.Configuration;
|
||||
using SafeExamBrowser.Contracts.Core;
|
||||
using SafeExamBrowser.Contracts.Core.OperationModel;
|
||||
|
@ -58,17 +57,14 @@ namespace SafeExamBrowser.Client.Operations
|
|||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
public OperationResult Repeat()
|
||||
{
|
||||
throw new InvalidOperationException($"The '{nameof(BrowserOperation)}' is not meant to be repeated!");
|
||||
}
|
||||
|
||||
public void Revert()
|
||||
public OperationResult Revert()
|
||||
{
|
||||
logger.Info("Terminating browser...");
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_TerminateBrowser);
|
||||
|
||||
browserController.Terminate();
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using SafeExamBrowser.Contracts.Communication.Events;
|
||||
using SafeExamBrowser.Contracts.Communication.Hosts;
|
||||
|
@ -42,29 +41,32 @@ namespace SafeExamBrowser.Client.Operations
|
|||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
public OperationResult Repeat()
|
||||
public OperationResult Revert()
|
||||
{
|
||||
throw new InvalidOperationException($"The '{nameof(ClientHostDisconnectionOperation)}' is not meant to be repeated!");
|
||||
}
|
||||
|
||||
public void Revert()
|
||||
{
|
||||
var disconnected = false;
|
||||
var disconnectedEvent = new AutoResetEvent(false);
|
||||
var disconnectedEventHandler = new CommunicationEventHandler(() => disconnectedEvent.Set());
|
||||
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_WaitRuntimeDisconnection);
|
||||
|
||||
clientHost.RuntimeDisconnected += disconnectedEventHandler;
|
||||
|
||||
if (clientHost.IsConnected)
|
||||
{
|
||||
var disconnected = false;
|
||||
var disconnectedEvent = new AutoResetEvent(false);
|
||||
var disconnectedEventHandler = new CommunicationEventHandler(() => disconnectedEvent.Set());
|
||||
|
||||
clientHost.RuntimeDisconnected += disconnectedEventHandler;
|
||||
|
||||
logger.Info("Waiting for runtime to disconnect from client communication host...");
|
||||
disconnected = disconnectedEvent.WaitOne(timeout_ms);
|
||||
|
||||
if (!disconnected)
|
||||
clientHost.RuntimeDisconnected -= disconnectedEventHandler;
|
||||
|
||||
if (disconnected)
|
||||
{
|
||||
logger.Error($"Runtime failed to disconnect within {timeout_ms / 1000} seconds!");
|
||||
logger.Info("The runtime has successfully disconnected from the client communication host.");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Error($"The runtime failed to disconnect within {timeout_ms / 1000} seconds!");
|
||||
|
||||
return OperationResult.Failed;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -72,7 +74,7 @@ namespace SafeExamBrowser.Client.Operations
|
|||
logger.Info("The runtime has already disconnected from the client communication host.");
|
||||
}
|
||||
|
||||
clientHost.RuntimeDisconnected -= disconnectedEventHandler;
|
||||
return OperationResult.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using SafeExamBrowser.Contracts.Core.OperationModel;
|
||||
using SafeExamBrowser.Contracts.Core.OperationModel.Events;
|
||||
using SafeExamBrowser.Contracts.I18n;
|
||||
|
@ -36,14 +35,11 @@ namespace SafeExamBrowser.Client.Operations
|
|||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
public OperationResult Repeat()
|
||||
{
|
||||
throw new InvalidOperationException($"The '{nameof(ClipboardOperation)}' is not meant to be repeated!");
|
||||
}
|
||||
|
||||
public void Revert()
|
||||
public OperationResult Revert()
|
||||
{
|
||||
EmptyClipboard();
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
private void EmptyClipboard()
|
||||
|
|
|
@ -61,14 +61,9 @@ namespace SafeExamBrowser.Client.Operations
|
|||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
public OperationResult Repeat()
|
||||
public OperationResult Revert()
|
||||
{
|
||||
throw new InvalidOperationException($"The '{nameof(ConfigurationOperation)}' is not meant to be repeated!");
|
||||
}
|
||||
|
||||
public void Revert()
|
||||
{
|
||||
// Nothing to do here...
|
||||
return OperationResult.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using SafeExamBrowser.Contracts.Core.OperationModel;
|
||||
using SafeExamBrowser.Contracts.Core.OperationModel.Events;
|
||||
using SafeExamBrowser.Contracts.I18n;
|
||||
|
@ -44,18 +43,15 @@ namespace SafeExamBrowser.Client.Operations
|
|||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
public OperationResult Repeat()
|
||||
{
|
||||
throw new InvalidOperationException($"The '{nameof(DisplayMonitorOperation)}' is not meant to be repeated!");
|
||||
}
|
||||
|
||||
public void Revert()
|
||||
public OperationResult Revert()
|
||||
{
|
||||
logger.Info("Restoring working area...");
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_RestoreWorkingArea);
|
||||
|
||||
displayMonitor.StopMonitoringDisplayChanges();
|
||||
displayMonitor.ResetPrimaryDisplay();
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using SafeExamBrowser.Contracts.Core.OperationModel;
|
||||
using SafeExamBrowser.Contracts.Core.OperationModel.Events;
|
||||
using SafeExamBrowser.Contracts.I18n;
|
||||
|
@ -45,17 +44,14 @@ namespace SafeExamBrowser.Client.Operations
|
|||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
public OperationResult Repeat()
|
||||
{
|
||||
throw new InvalidOperationException($"The '{nameof(KeyboardInterceptorOperation)}' is not meant to be repeated!");
|
||||
}
|
||||
|
||||
public void Revert()
|
||||
public OperationResult Revert()
|
||||
{
|
||||
logger.Info("Stopping keyboard interception...");
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_StopKeyboardInterception);
|
||||
|
||||
nativeMethods.DeregisterKeyboardHook(keyboardInterceptor);
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using SafeExamBrowser.Contracts.Core.OperationModel;
|
||||
using SafeExamBrowser.Contracts.Core.OperationModel.Events;
|
||||
using SafeExamBrowser.Contracts.I18n;
|
||||
|
@ -45,17 +44,14 @@ namespace SafeExamBrowser.Client.Operations
|
|||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
public OperationResult Repeat()
|
||||
{
|
||||
throw new InvalidOperationException($"The '{nameof(MouseInterceptorOperation)}' is not meant to be repeated!");
|
||||
}
|
||||
|
||||
public void Revert()
|
||||
public OperationResult Revert()
|
||||
{
|
||||
logger.Info("Stopping mouse interception...");
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_StopMouseInterception);
|
||||
|
||||
nativeMethods.DeregisterMouseHook(mouseInterceptor);
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using SafeExamBrowser.Contracts.Configuration.Settings;
|
||||
using SafeExamBrowser.Contracts.Core.OperationModel;
|
||||
using SafeExamBrowser.Contracts.Core.OperationModel.Events;
|
||||
|
@ -45,12 +44,7 @@ namespace SafeExamBrowser.Client.Operations
|
|||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
public OperationResult Repeat()
|
||||
{
|
||||
throw new InvalidOperationException($"The '{nameof(ProcessMonitorOperation)}' is not meant to be repeated!");
|
||||
}
|
||||
|
||||
public void Revert()
|
||||
public OperationResult Revert()
|
||||
{
|
||||
logger.Info("Stopping process monitoring...");
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_StopProcessMonitoring);
|
||||
|
@ -59,6 +53,8 @@ namespace SafeExamBrowser.Client.Operations
|
|||
{
|
||||
processMonitor.StopMonitoringExplorer();
|
||||
}
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,12 +51,7 @@ namespace SafeExamBrowser.Client.Operations
|
|||
return connected ? OperationResult.Success : OperationResult.Failed;
|
||||
}
|
||||
|
||||
public OperationResult Repeat()
|
||||
{
|
||||
throw new InvalidOperationException($"The '{nameof(RuntimeConnectionOperation)}' is not meant to be repeated!");
|
||||
}
|
||||
|
||||
public void Revert()
|
||||
public OperationResult Revert()
|
||||
{
|
||||
logger.Info("Closing runtime connection...");
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_CloseRuntimeConnection);
|
||||
|
@ -73,7 +68,11 @@ namespace SafeExamBrowser.Client.Operations
|
|||
{
|
||||
logger.Error("Failed to disconnect from the runtime!");
|
||||
}
|
||||
|
||||
return success ? OperationResult.Success : OperationResult.Failed;
|
||||
}
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using SafeExamBrowser.Contracts.Configuration;
|
||||
using SafeExamBrowser.Contracts.Configuration.Settings;
|
||||
using SafeExamBrowser.Contracts.Core;
|
||||
|
@ -91,12 +90,7 @@ namespace SafeExamBrowser.Client.Operations
|
|||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
public OperationResult Repeat()
|
||||
{
|
||||
throw new InvalidOperationException($"The '{nameof(TaskbarOperation)}' is not meant to be repeated!");
|
||||
}
|
||||
|
||||
public void Revert()
|
||||
public OperationResult Revert()
|
||||
{
|
||||
logger.Info("Terminating taskbar...");
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_TerminateTaskbar);
|
||||
|
@ -120,6 +114,8 @@ namespace SafeExamBrowser.Client.Operations
|
|||
{
|
||||
wirelessNetwork.Terminate();
|
||||
}
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
private void AddKeyboardLayoutControl()
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using SafeExamBrowser.Contracts.Configuration.Settings;
|
||||
using SafeExamBrowser.Contracts.Core.OperationModel;
|
||||
using SafeExamBrowser.Contracts.Core.OperationModel.Events;
|
||||
|
@ -50,12 +49,7 @@ namespace SafeExamBrowser.Client.Operations
|
|||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
public OperationResult Repeat()
|
||||
{
|
||||
throw new InvalidOperationException($"The '{nameof(WindowMonitorOperation)}' is not meant to be repeated!");
|
||||
}
|
||||
|
||||
public void Revert()
|
||||
public OperationResult Revert()
|
||||
{
|
||||
logger.Info("Stopping window monitoring...");
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_StopWindowMonitoring);
|
||||
|
@ -69,6 +63,8 @@ namespace SafeExamBrowser.Client.Operations
|
|||
{
|
||||
windowMonitor.RestoreHiddenWindows();
|
||||
}
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,9 +64,9 @@ namespace SafeExamBrowser.Configuration
|
|||
{
|
||||
CurrentSession = new SessionData
|
||||
{
|
||||
ClientProcess = CurrentSession?.ClientProcess,
|
||||
ClientProxy = CurrentSession?.ClientProxy,
|
||||
Id = Guid.NewGuid(),
|
||||
NewDesktop = CurrentSession?.NewDesktop,
|
||||
OriginalDesktop = CurrentSession?.OriginalDesktop,
|
||||
StartupToken = Guid.NewGuid()
|
||||
};
|
||||
|
||||
|
|
|
@ -18,6 +18,8 @@ namespace SafeExamBrowser.Configuration
|
|||
public IClientProxy ClientProxy { get; set; }
|
||||
public IProcess ClientProcess { get; set; }
|
||||
public Guid Id { get; set; }
|
||||
public IDesktop NewDesktop { get; set; }
|
||||
public IDesktop OriginalDesktop { get; set; }
|
||||
public Guid StartupToken { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ using SafeExamBrowser.Contracts.WindowsApi;
|
|||
namespace SafeExamBrowser.Contracts.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines all session-related (configuration) data.
|
||||
/// Holds all session-related configuration and runtime data.
|
||||
/// </summary>
|
||||
public interface ISessionData
|
||||
{
|
||||
|
@ -32,6 +32,16 @@ namespace SafeExamBrowser.Contracts.Configuration
|
|||
/// </summary>
|
||||
Guid Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The new desktop, if <see cref="Settings.KioskMode.CreateNewDesktop"/> is active for this session.
|
||||
/// </summary>
|
||||
IDesktop NewDesktop { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The original desktop, if <see cref="Settings.KioskMode.CreateNewDesktop"/> is active for this session.
|
||||
/// </summary>
|
||||
IDesktop OriginalDesktop { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The startup token used by the client and runtime components for initial authentication.
|
||||
/// </summary>
|
||||
|
|
|
@ -31,13 +31,8 @@ namespace SafeExamBrowser.Contracts.Core.OperationModel
|
|||
OperationResult Perform();
|
||||
|
||||
/// <summary>
|
||||
/// Repeats the operation.
|
||||
/// Reverts all changes made when executing the operation.
|
||||
/// </summary>
|
||||
OperationResult Repeat();
|
||||
|
||||
/// <summary>
|
||||
/// Reverts all changes which were made when executing the operation.
|
||||
/// </summary>
|
||||
void Revert();
|
||||
OperationResult Revert();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,14 +11,13 @@ using SafeExamBrowser.Contracts.Core.OperationModel.Events;
|
|||
namespace SafeExamBrowser.Contracts.Core.OperationModel
|
||||
{
|
||||
/// <summary>
|
||||
/// A sequence of <see cref="IOperation"/>s which can be used for sequential procedures, e.g. the initialization & finalization of
|
||||
/// A sequence of <see cref="IOperation"/> which can be used for sequential procedures, e.g. the initialization & finalization of
|
||||
/// an application component. Each operation will be executed failsafe, i.e. the return value will indicate whether a procedure
|
||||
/// completed successfully or not.
|
||||
///
|
||||
/// Exemplary execution order for a sequence initialized with operations A, B, C, D:
|
||||
///
|
||||
/// <see cref="TryPerform"/>: A -> B -> C -> D.
|
||||
/// <see cref="TryRepeat"/>: A -> B -> C -> D.
|
||||
/// <see cref="TryRevert"/>: D -> C -> B -> A.
|
||||
/// </summary>
|
||||
public interface IOperationSequence
|
||||
|
@ -45,15 +44,9 @@ namespace SafeExamBrowser.Contracts.Core.OperationModel
|
|||
OperationResult TryPerform();
|
||||
|
||||
/// <summary>
|
||||
/// Tries to repeat the operations of this sequence according to their initialized order. If any operation fails, the already
|
||||
/// performed operations will not be reverted.
|
||||
/// Tries to revert the operations of this sequence in reversion of their initialized order. The reversion of all operations will
|
||||
/// continue, even if one or multiple operations fail to revert successfully.
|
||||
/// </summary>
|
||||
OperationResult TryRepeat();
|
||||
|
||||
/// <summary>
|
||||
/// Tries to revert the operations of this sequence. Returns <c>true</c> if all operations were reverted without errors,
|
||||
/// otherwise <c>false</c>. The reversion of all operations will continue, even if one or multiple operations fail.
|
||||
/// </summary>
|
||||
bool TryRevert();
|
||||
OperationResult TryRevert();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
namespace SafeExamBrowser.Contracts.Core.OperationModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines an operation which can be executed multiple times as part of an <see cref="IRepeatableOperationSequence"/>.
|
||||
/// </summary>
|
||||
public interface IRepeatableOperation : IOperation
|
||||
{
|
||||
/// <summary>
|
||||
/// Repeats the operation.
|
||||
/// </summary>
|
||||
OperationResult Repeat();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
namespace SafeExamBrowser.Contracts.Core.OperationModel
|
||||
{
|
||||
/// <summary>
|
||||
/// A sequence of <see cref="IRepeatableOperation"/> which can be used for repeatable sequential procedures.
|
||||
///
|
||||
/// Exemplary execution order for a sequence initialized with operations A, B, C, D:
|
||||
///
|
||||
/// <see cref="TryRepeat()"/>: A -> B -> C -> D.
|
||||
/// </summary>
|
||||
public interface IRepeatableOperationSequence : IOperationSequence
|
||||
{
|
||||
/// <summary>
|
||||
/// Tries to repeat the operations of this sequence according to their initialized order. If any operation fails, the already
|
||||
/// repeated operations will not be reverted.
|
||||
/// </summary>
|
||||
OperationResult TryRepeat();
|
||||
}
|
||||
}
|
|
@ -9,7 +9,7 @@
|
|||
namespace SafeExamBrowser.Contracts.Core.OperationModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the result of the sequential execution of <see cref="IOperation"/>s (as part of an <see cref="IOperationSequence"/>).
|
||||
/// Defines the operation result of the execution of an <see cref="IOperation"/> resp. <see cref="IOperationSequence"/>.
|
||||
/// </summary>
|
||||
public enum OperationResult
|
||||
{
|
||||
|
|
|
@ -63,6 +63,8 @@
|
|||
<Compile Include="Core\OperationModel\Events\ProgressChangedEventHandler.cs" />
|
||||
<Compile Include="Core\OperationModel\Events\StatusChangedEventHandler.cs" />
|
||||
<Compile Include="Core\OperationModel\IOperationSequence.cs" />
|
||||
<Compile Include="Core\OperationModel\IRepeatableOperation.cs" />
|
||||
<Compile Include="Core\OperationModel\IRepeatableOperationSequence.cs" />
|
||||
<Compile Include="Core\OperationModel\OperationResult.cs" />
|
||||
<Compile Include="Browser\DownloadEventArgs.cs" />
|
||||
<Compile Include="Browser\DownloadFinishedCallback.cs" />
|
||||
|
|
|
@ -303,159 +303,6 @@ namespace SafeExamBrowser.Core.UnitTests.OperationModel
|
|||
|
||||
#endregion
|
||||
|
||||
#region Repeat Tests
|
||||
|
||||
[TestMethod]
|
||||
public void MustCorrectlyAbortRepeat()
|
||||
{
|
||||
var operationA = new Mock<IOperation>();
|
||||
var operationB = new Mock<IOperation>();
|
||||
var operationC = new Mock<IOperation>();
|
||||
var operations = new Queue<IOperation>();
|
||||
|
||||
operationA.Setup(o => o.Repeat()).Returns(OperationResult.Success);
|
||||
operationB.Setup(o => o.Repeat()).Returns(OperationResult.Aborted);
|
||||
|
||||
operations.Enqueue(operationA.Object);
|
||||
operations.Enqueue(operationB.Object);
|
||||
operations.Enqueue(operationC.Object);
|
||||
|
||||
var sut = new OperationSequence(loggerMock.Object, operations);
|
||||
var result = sut.TryRepeat();
|
||||
|
||||
operationA.Verify(o => o.Repeat(), Times.Once);
|
||||
operationA.Verify(o => o.Revert(), Times.Never);
|
||||
operationB.Verify(o => o.Repeat(), Times.Once);
|
||||
operationB.Verify(o => o.Revert(), Times.Never);
|
||||
operationC.Verify(o => o.Repeat(), Times.Never);
|
||||
operationC.Verify(o => o.Revert(), Times.Never);
|
||||
|
||||
Assert.AreEqual(OperationResult.Aborted, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustRepeatOperations()
|
||||
{
|
||||
var operationA = new Mock<IOperation>();
|
||||
var operationB = new Mock<IOperation>();
|
||||
var operationC = new Mock<IOperation>();
|
||||
var operations = new Queue<IOperation>();
|
||||
|
||||
operationA.Setup(o => o.Repeat()).Returns(OperationResult.Success);
|
||||
operationB.Setup(o => o.Repeat()).Returns(OperationResult.Success);
|
||||
operationC.Setup(o => o.Repeat()).Returns(OperationResult.Success);
|
||||
|
||||
operations.Enqueue(operationA.Object);
|
||||
operations.Enqueue(operationB.Object);
|
||||
operations.Enqueue(operationC.Object);
|
||||
|
||||
var sut = new OperationSequence(loggerMock.Object, operations);
|
||||
var result = sut.TryRepeat();
|
||||
|
||||
operationA.Verify(o => o.Perform(), Times.Never);
|
||||
operationA.Verify(o => o.Repeat(), Times.Once);
|
||||
operationA.Verify(o => o.Revert(), Times.Never);
|
||||
operationB.Verify(o => o.Perform(), Times.Never);
|
||||
operationB.Verify(o => o.Repeat(), Times.Once);
|
||||
operationB.Verify(o => o.Revert(), Times.Never);
|
||||
operationC.Verify(o => o.Perform(), Times.Never);
|
||||
operationC.Verify(o => o.Repeat(), Times.Once);
|
||||
operationC.Verify(o => o.Revert(), Times.Never);
|
||||
|
||||
Assert.AreEqual(OperationResult.Success, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustRepeatOperationsInSequence()
|
||||
{
|
||||
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.Repeat()).Returns(OperationResult.Success).Callback(() => a = ++current);
|
||||
operationB.Setup(o => o.Repeat()).Returns(OperationResult.Success).Callback(() => b = ++current);
|
||||
operationC.Setup(o => o.Repeat()).Returns(OperationResult.Success).Callback(() => c = ++current);
|
||||
|
||||
operations.Enqueue(operationA.Object);
|
||||
operations.Enqueue(operationB.Object);
|
||||
operations.Enqueue(operationC.Object);
|
||||
|
||||
var sut = new OperationSequence(loggerMock.Object, operations);
|
||||
var result = sut.TryRepeat();
|
||||
|
||||
Assert.AreEqual(OperationResult.Success, result);
|
||||
Assert.IsTrue(a == 1);
|
||||
Assert.IsTrue(b == 2);
|
||||
Assert.IsTrue(c == 3);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustNotRevertOperationsInCaseOfError()
|
||||
{
|
||||
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.Repeat()).Returns(OperationResult.Success);
|
||||
operationB.Setup(o => o.Repeat()).Returns(OperationResult.Success);
|
||||
operationC.Setup(o => o.Repeat()).Throws<Exception>();
|
||||
|
||||
operations.Enqueue(operationA.Object);
|
||||
operations.Enqueue(operationB.Object);
|
||||
operations.Enqueue(operationC.Object);
|
||||
operations.Enqueue(operationD.Object);
|
||||
|
||||
var sut = new OperationSequence(loggerMock.Object, operations);
|
||||
var result = sut.TryRepeat();
|
||||
|
||||
operationA.Verify(o => o.Repeat(), Times.Once);
|
||||
operationA.Verify(o => o.Revert(), Times.Never);
|
||||
operationB.Verify(o => o.Repeat(), Times.Once);
|
||||
operationB.Verify(o => o.Revert(), Times.Never);
|
||||
operationC.Verify(o => o.Repeat(), Times.Once);
|
||||
operationC.Verify(o => o.Revert(), Times.Never);
|
||||
operationD.Verify(o => o.Repeat(), Times.Never);
|
||||
operationD.Verify(o => o.Revert(), Times.Never);
|
||||
|
||||
Assert.AreEqual(OperationResult.Failed, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustSucceedRepeatingWithEmptyQueue()
|
||||
{
|
||||
var sut = new OperationSequence(loggerMock.Object, new Queue<IOperation>());
|
||||
var result = sut.TryRepeat();
|
||||
|
||||
Assert.AreEqual(OperationResult.Success, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustSucceedRepeatingWithoutCallingPerform()
|
||||
{
|
||||
var sut = new OperationSequence(loggerMock.Object, new Queue<IOperation>());
|
||||
var result = sut.TryRepeat();
|
||||
|
||||
Assert.AreEqual(OperationResult.Success, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustNotFailInCaseOfUnexpectedErrorWhenRepeating()
|
||||
{
|
||||
var sut = new OperationSequence(loggerMock.Object, new Queue<IOperation>());
|
||||
|
||||
sut.ProgressChanged += (args) => throw new Exception();
|
||||
|
||||
var result = sut.TryRepeat();
|
||||
|
||||
Assert.AreEqual(OperationResult.Failed, result);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Revert Tests
|
||||
|
||||
[TestMethod]
|
||||
|
@ -467,11 +314,11 @@ namespace SafeExamBrowser.Core.UnitTests.OperationModel
|
|||
var operations = new Queue<IOperation>();
|
||||
|
||||
operationA.Setup(o => o.Perform()).Returns(OperationResult.Success);
|
||||
operationA.Setup(o => o.Repeat()).Returns(OperationResult.Success);
|
||||
operationA.Setup(o => o.Revert()).Returns(OperationResult.Success);
|
||||
operationB.Setup(o => o.Perform()).Returns(OperationResult.Success);
|
||||
operationB.Setup(o => o.Repeat()).Returns(OperationResult.Success);
|
||||
operationB.Setup(o => o.Revert()).Returns(OperationResult.Success);
|
||||
operationC.Setup(o => o.Perform()).Returns(OperationResult.Success);
|
||||
operationC.Setup(o => o.Repeat()).Returns(OperationResult.Success);
|
||||
operationC.Setup(o => o.Revert()).Returns(OperationResult.Success);
|
||||
|
||||
operations.Enqueue(operationA.Object);
|
||||
operations.Enqueue(operationB.Object);
|
||||
|
@ -481,13 +328,13 @@ namespace SafeExamBrowser.Core.UnitTests.OperationModel
|
|||
|
||||
sut.TryPerform();
|
||||
|
||||
var success = sut.TryRevert();
|
||||
var result = 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);
|
||||
Assert.AreEqual(OperationResult.Success, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
@ -515,9 +362,9 @@ namespace SafeExamBrowser.Core.UnitTests.OperationModel
|
|||
|
||||
sut.TryPerform();
|
||||
|
||||
var success = sut.TryRevert();
|
||||
var result = sut.TryRevert();
|
||||
|
||||
Assert.IsTrue(success);
|
||||
Assert.AreEqual(OperationResult.Success, result);
|
||||
Assert.IsTrue(c == 1);
|
||||
Assert.IsTrue(b == 2);
|
||||
Assert.IsTrue(a == 3);
|
||||
|
@ -547,13 +394,13 @@ namespace SafeExamBrowser.Core.UnitTests.OperationModel
|
|||
|
||||
sut.TryPerform();
|
||||
|
||||
var success = sut.TryRevert();
|
||||
var result = 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);
|
||||
Assert.AreEqual(OperationResult.Failed, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
@ -565,9 +412,9 @@ namespace SafeExamBrowser.Core.UnitTests.OperationModel
|
|||
var operations = new Queue<IOperation>();
|
||||
|
||||
operationA.Setup(o => o.Perform()).Returns(OperationResult.Success);
|
||||
operationA.Setup(o => o.Repeat()).Returns(OperationResult.Success);
|
||||
operationA.Setup(o => o.Revert()).Returns(OperationResult.Success);
|
||||
operationB.Setup(o => o.Perform()).Returns(OperationResult.Aborted);
|
||||
operationB.Setup(o => o.Repeat()).Returns(OperationResult.Success);
|
||||
operationB.Setup(o => o.Revert()).Returns(OperationResult.Success);
|
||||
|
||||
operations.Enqueue(operationA.Object);
|
||||
operations.Enqueue(operationB.Object);
|
||||
|
@ -577,13 +424,13 @@ namespace SafeExamBrowser.Core.UnitTests.OperationModel
|
|||
|
||||
sut.TryPerform();
|
||||
|
||||
var success = sut.TryRevert();
|
||||
var result = 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);
|
||||
Assert.AreEqual(OperationResult.Success, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
@ -592,16 +439,19 @@ namespace SafeExamBrowser.Core.UnitTests.OperationModel
|
|||
var sut = new OperationSequence(loggerMock.Object, new Queue<IOperation>());
|
||||
|
||||
sut.TryPerform();
|
||||
sut.TryRevert();
|
||||
|
||||
var result = sut.TryRevert();
|
||||
|
||||
Assert.AreEqual(OperationResult.Success, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustSucceedRevertingWithoutCallingPerform()
|
||||
{
|
||||
var sut = new OperationSequence(loggerMock.Object, new Queue<IOperation>());
|
||||
var success = sut.TryRevert();
|
||||
var result = sut.TryRevert();
|
||||
|
||||
Assert.IsTrue(success);
|
||||
Assert.AreEqual(OperationResult.Success, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
@ -611,9 +461,9 @@ namespace SafeExamBrowser.Core.UnitTests.OperationModel
|
|||
|
||||
sut.ProgressChanged += (args) => throw new Exception();
|
||||
|
||||
var success = sut.TryRevert();
|
||||
var result = sut.TryRevert();
|
||||
|
||||
Assert.IsFalse(success);
|
||||
Assert.AreEqual(OperationResult.Failed, result);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
|
@ -0,0 +1,177 @@
|
|||
/*
|
||||
* 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 Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace SafeExamBrowser.Core.UnitTests.OperationModel
|
||||
{
|
||||
[TestClass]
|
||||
public class RepeatableOperationSequenceTests
|
||||
{
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TODO()
|
||||
{
|
||||
Assert.Fail();
|
||||
}
|
||||
|
||||
//[TestMethod]
|
||||
//public void MustCorrectlyAbortRepeat()
|
||||
//{
|
||||
// var operationA = new Mock<IOperation>();
|
||||
// var operationB = new Mock<IOperation>();
|
||||
// var operationC = new Mock<IOperation>();
|
||||
// var operations = new Queue<IOperation>();
|
||||
|
||||
// operationA.Setup(o => o.Repeat()).Returns(OperationResult.Success);
|
||||
// operationB.Setup(o => o.Repeat()).Returns(OperationResult.Aborted);
|
||||
|
||||
// operations.Enqueue(operationA.Object);
|
||||
// operations.Enqueue(operationB.Object);
|
||||
// operations.Enqueue(operationC.Object);
|
||||
|
||||
// var sut = new OperationSequence(loggerMock.Object, operations);
|
||||
// var result = sut.TryRepeat();
|
||||
|
||||
// operationA.Verify(o => o.Repeat(), Times.Once);
|
||||
// operationA.Verify(o => o.Revert(), Times.Never);
|
||||
// operationB.Verify(o => o.Repeat(), Times.Once);
|
||||
// operationB.Verify(o => o.Revert(), Times.Never);
|
||||
// operationC.Verify(o => o.Repeat(), Times.Never);
|
||||
// operationC.Verify(o => o.Revert(), Times.Never);
|
||||
|
||||
// Assert.AreEqual(OperationResult.Aborted, result);
|
||||
//}
|
||||
|
||||
//[TestMethod]
|
||||
//public void MustRepeatOperations()
|
||||
//{
|
||||
// var operationA = new Mock<IOperation>();
|
||||
// var operationB = new Mock<IOperation>();
|
||||
// var operationC = new Mock<IOperation>();
|
||||
// var operations = new Queue<IOperation>();
|
||||
|
||||
// operationA.Setup(o => o.Repeat()).Returns(OperationResult.Success);
|
||||
// operationB.Setup(o => o.Repeat()).Returns(OperationResult.Success);
|
||||
// operationC.Setup(o => o.Repeat()).Returns(OperationResult.Success);
|
||||
|
||||
// operations.Enqueue(operationA.Object);
|
||||
// operations.Enqueue(operationB.Object);
|
||||
// operations.Enqueue(operationC.Object);
|
||||
|
||||
// var sut = new OperationSequence(loggerMock.Object, operations);
|
||||
// var result = sut.TryRepeat();
|
||||
|
||||
// operationA.Verify(o => o.Perform(), Times.Never);
|
||||
// operationA.Verify(o => o.Repeat(), Times.Once);
|
||||
// operationA.Verify(o => o.Revert(), Times.Never);
|
||||
// operationB.Verify(o => o.Perform(), Times.Never);
|
||||
// operationB.Verify(o => o.Repeat(), Times.Once);
|
||||
// operationB.Verify(o => o.Revert(), Times.Never);
|
||||
// operationC.Verify(o => o.Perform(), Times.Never);
|
||||
// operationC.Verify(o => o.Repeat(), Times.Once);
|
||||
// operationC.Verify(o => o.Revert(), Times.Never);
|
||||
|
||||
// Assert.AreEqual(OperationResult.Success, result);
|
||||
//}
|
||||
|
||||
//[TestMethod]
|
||||
//public void MustRepeatOperationsInSequence()
|
||||
//{
|
||||
// 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.Repeat()).Returns(OperationResult.Success).Callback(() => a = ++current);
|
||||
// operationB.Setup(o => o.Repeat()).Returns(OperationResult.Success).Callback(() => b = ++current);
|
||||
// operationC.Setup(o => o.Repeat()).Returns(OperationResult.Success).Callback(() => c = ++current);
|
||||
|
||||
// operations.Enqueue(operationA.Object);
|
||||
// operations.Enqueue(operationB.Object);
|
||||
// operations.Enqueue(operationC.Object);
|
||||
|
||||
// var sut = new OperationSequence(loggerMock.Object, operations);
|
||||
// var result = sut.TryRepeat();
|
||||
|
||||
// Assert.AreEqual(OperationResult.Success, result);
|
||||
// Assert.IsTrue(a == 1);
|
||||
// Assert.IsTrue(b == 2);
|
||||
// Assert.IsTrue(c == 3);
|
||||
//}
|
||||
|
||||
//[TestMethod]
|
||||
//public void MustNotRevertOperationsInCaseOfError()
|
||||
//{
|
||||
// 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.Repeat()).Returns(OperationResult.Success);
|
||||
// operationB.Setup(o => o.Repeat()).Returns(OperationResult.Success);
|
||||
// operationC.Setup(o => o.Repeat()).Throws<Exception>();
|
||||
|
||||
// operations.Enqueue(operationA.Object);
|
||||
// operations.Enqueue(operationB.Object);
|
||||
// operations.Enqueue(operationC.Object);
|
||||
// operations.Enqueue(operationD.Object);
|
||||
|
||||
// var sut = new OperationSequence(loggerMock.Object, operations);
|
||||
// var result = sut.TryRepeat();
|
||||
|
||||
// operationA.Verify(o => o.Repeat(), Times.Once);
|
||||
// operationA.Verify(o => o.Revert(), Times.Never);
|
||||
// operationB.Verify(o => o.Repeat(), Times.Once);
|
||||
// operationB.Verify(o => o.Revert(), Times.Never);
|
||||
// operationC.Verify(o => o.Repeat(), Times.Once);
|
||||
// operationC.Verify(o => o.Revert(), Times.Never);
|
||||
// operationD.Verify(o => o.Repeat(), Times.Never);
|
||||
// operationD.Verify(o => o.Revert(), Times.Never);
|
||||
|
||||
// Assert.AreEqual(OperationResult.Failed, result);
|
||||
//}
|
||||
|
||||
//[TestMethod]
|
||||
//public void MustSucceedRepeatingWithEmptyQueue()
|
||||
//{
|
||||
// var sut = new OperationSequence(loggerMock.Object, new Queue<IOperation>());
|
||||
// var result = sut.TryRepeat();
|
||||
|
||||
// Assert.AreEqual(OperationResult.Success, result);
|
||||
//}
|
||||
|
||||
//[TestMethod]
|
||||
//public void MustSucceedRepeatingWithoutCallingPerform()
|
||||
//{
|
||||
// var sut = new OperationSequence(loggerMock.Object, new Queue<IOperation>());
|
||||
// var result = sut.TryRepeat();
|
||||
|
||||
// Assert.AreEqual(OperationResult.Success, result);
|
||||
//}
|
||||
|
||||
//[TestMethod]
|
||||
//public void MustNotFailInCaseOfUnexpectedErrorWhenRepeating()
|
||||
//{
|
||||
// var sut = new OperationSequence(loggerMock.Object, new Queue<IOperation>());
|
||||
|
||||
// sut.ProgressChanged += (args) => throw new Exception();
|
||||
|
||||
// var result = sut.TryRepeat();
|
||||
|
||||
// Assert.AreEqual(OperationResult.Failed, result);
|
||||
//}
|
||||
}
|
||||
}
|
|
@ -58,11 +58,11 @@ namespace SafeExamBrowser.Core.UnitTests.Operations
|
|||
|
||||
var perform = sut.Perform();
|
||||
var repeat = sut.Repeat();
|
||||
|
||||
sut.Revert();
|
||||
var revert = sut.Revert();
|
||||
|
||||
Assert.AreEqual(OperationResult.Success, perform);
|
||||
Assert.AreEqual(OperationResult.Success, repeat);
|
||||
Assert.AreEqual(OperationResult.Success, revert);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using SafeExamBrowser.Contracts.Core.OperationModel;
|
||||
|
@ -52,12 +51,5 @@ namespace SafeExamBrowser.Core.UnitTests.Operations
|
|||
|
||||
text.VerifyNoOtherCalls();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(InvalidOperationException))]
|
||||
public void MustNotAllowRepeating()
|
||||
{
|
||||
sut.Repeat();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,20 +45,6 @@ namespace SafeExamBrowser.Core.UnitTests.Operations
|
|||
Assert.IsTrue(initialized);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(NullReferenceException))]
|
||||
public void MustNotInstantiateOperationOnRepeat()
|
||||
{
|
||||
IOperation initialize()
|
||||
{
|
||||
return operationMock.Object;
|
||||
};
|
||||
|
||||
var sut = new LazyInitializationOperation(initialize);
|
||||
|
||||
sut.Repeat();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(NullReferenceException))]
|
||||
public void MustNotInstantiateOperationOnRevert()
|
||||
|
@ -82,16 +68,14 @@ namespace SafeExamBrowser.Core.UnitTests.Operations
|
|||
};
|
||||
|
||||
operationMock.Setup(o => o.Perform()).Returns(OperationResult.Success);
|
||||
operationMock.Setup(o => o.Repeat()).Returns(OperationResult.Failed);
|
||||
operationMock.Setup(o => o.Revert()).Returns(OperationResult.Failed);
|
||||
|
||||
var sut = new LazyInitializationOperation(initialize);
|
||||
var perform = sut.Perform();
|
||||
var repeat = sut.Repeat();
|
||||
|
||||
sut.Revert();
|
||||
var revert = sut.Revert();
|
||||
|
||||
Assert.AreEqual(OperationResult.Success, perform);
|
||||
Assert.AreEqual(OperationResult.Failed, repeat);
|
||||
Assert.AreEqual(OperationResult.Failed, revert);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
@ -147,6 +131,8 @@ namespace SafeExamBrowser.Core.UnitTests.Operations
|
|||
{
|
||||
if (first)
|
||||
{
|
||||
first = false;
|
||||
|
||||
return operation;
|
||||
}
|
||||
|
||||
|
@ -156,11 +142,9 @@ namespace SafeExamBrowser.Core.UnitTests.Operations
|
|||
var sut = new LazyInitializationOperation(initialize);
|
||||
|
||||
sut.Perform();
|
||||
sut.Repeat();
|
||||
sut.Revert();
|
||||
|
||||
operationMock.Verify(o => o.Perform(), Times.Once);
|
||||
operationMock.Verify(o => o.Repeat(), Times.Once);
|
||||
operationMock.Verify(o => o.Revert(), Times.Once);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,6 +79,7 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="OperationModel\QueueExtensionTests.cs" />
|
||||
<Compile Include="OperationModel\RepeatableOperationSequenceTests.cs" />
|
||||
<Compile Include="Operations\CommunicationHostOperationTests.cs" />
|
||||
<Compile Include="Operations\LazyInitializationOperationTests.cs" />
|
||||
<Compile Include="Operations\I18nOperationTests.cs" />
|
||||
|
|
|
@ -20,9 +20,9 @@ namespace SafeExamBrowser.Core.OperationModel
|
|||
/// </summary>
|
||||
public class OperationSequence : IOperationSequence
|
||||
{
|
||||
private ILogger logger;
|
||||
private Queue<IOperation> operations = new Queue<IOperation>();
|
||||
private Stack<IOperation> stack = new Stack<IOperation>();
|
||||
protected ILogger logger;
|
||||
protected Queue<IOperation> operations = new Queue<IOperation>();
|
||||
protected Stack<IOperation> stack = new Stack<IOperation>();
|
||||
|
||||
public event ActionRequiredEventHandler ActionRequired
|
||||
{
|
||||
|
@ -44,7 +44,7 @@ namespace SafeExamBrowser.Core.OperationModel
|
|||
this.operations = new Queue<IOperation>(operations);
|
||||
}
|
||||
|
||||
public OperationResult TryPerform()
|
||||
public virtual OperationResult TryPerform()
|
||||
{
|
||||
var result = OperationResult.Failed;
|
||||
|
||||
|
@ -66,53 +66,36 @@ namespace SafeExamBrowser.Core.OperationModel
|
|||
return result;
|
||||
}
|
||||
|
||||
public OperationResult TryRepeat()
|
||||
public virtual OperationResult TryRevert()
|
||||
{
|
||||
var result = OperationResult.Failed;
|
||||
|
||||
try
|
||||
{
|
||||
Initialize();
|
||||
result = Repeat();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.Error("Failed to repeat operations!", e);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public bool TryRevert()
|
||||
{
|
||||
var success = false;
|
||||
|
||||
try
|
||||
{
|
||||
Initialize(true);
|
||||
success = Revert();
|
||||
result = Revert();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.Error("Failed to revert operations!", e);
|
||||
}
|
||||
|
||||
return success;
|
||||
return result;
|
||||
}
|
||||
|
||||
private void Initialize(bool indeterminate = false)
|
||||
protected virtual void Initialize(bool indeterminate = false)
|
||||
{
|
||||
if (indeterminate)
|
||||
{
|
||||
ProgressChanged?.Invoke(new ProgressChangedEventArgs { IsIndeterminate = true });
|
||||
UpdateProgress(new ProgressChangedEventArgs { IsIndeterminate = true });
|
||||
}
|
||||
else
|
||||
{
|
||||
ProgressChanged?.Invoke(new ProgressChangedEventArgs { CurrentValue = 0, MaxValue = operations.Count });
|
||||
UpdateProgress(new ProgressChangedEventArgs { CurrentValue = 0, MaxValue = operations.Count });
|
||||
}
|
||||
}
|
||||
|
||||
private OperationResult Perform()
|
||||
protected virtual OperationResult Perform()
|
||||
{
|
||||
foreach (var operation in operations)
|
||||
{
|
||||
|
@ -134,39 +117,13 @@ namespace SafeExamBrowser.Core.OperationModel
|
|||
return result;
|
||||
}
|
||||
|
||||
ProgressChanged?.Invoke(new ProgressChangedEventArgs { Progress = true });
|
||||
UpdateProgress(new ProgressChangedEventArgs { Progress = true });
|
||||
}
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
private OperationResult Repeat()
|
||||
{
|
||||
foreach (var operation in operations)
|
||||
{
|
||||
var result = OperationResult.Failed;
|
||||
|
||||
try
|
||||
{
|
||||
result = operation.Repeat();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.Error($"Caught unexpected exception while repeating operation '{operation.GetType().Name}'!", e);
|
||||
}
|
||||
|
||||
if (result != OperationResult.Success)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
ProgressChanged?.Invoke(new ProgressChangedEventArgs { Progress = true });
|
||||
}
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
private bool Revert(bool regress = false)
|
||||
protected virtual OperationResult Revert(bool regress = false)
|
||||
{
|
||||
var success = true;
|
||||
|
||||
|
@ -176,21 +133,31 @@ namespace SafeExamBrowser.Core.OperationModel
|
|||
|
||||
try
|
||||
{
|
||||
operation.Revert();
|
||||
var result = operation.Revert();
|
||||
|
||||
if (result != OperationResult.Success)
|
||||
{
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.Error($"Failed to revert operation '{operation.GetType().Name}'!", e);
|
||||
logger.Error($"Caught unexpected exception while reverting operation '{operation.GetType().Name}'!", e);
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (regress)
|
||||
{
|
||||
ProgressChanged?.Invoke(new ProgressChangedEventArgs { Regress = true });
|
||||
UpdateProgress(new ProgressChangedEventArgs { Regress = true });
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
return success ? OperationResult.Success : OperationResult.Failed;
|
||||
}
|
||||
|
||||
protected void UpdateProgress(ProgressChangedEventArgs args)
|
||||
{
|
||||
ProgressChanged?.Invoke(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* 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.Core.OperationModel;
|
||||
using SafeExamBrowser.Contracts.Core.OperationModel.Events;
|
||||
using SafeExamBrowser.Contracts.Logging;
|
||||
|
||||
namespace SafeExamBrowser.Core.OperationModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Default implementation of the <see cref="IRepeatableOperationSequence"/>.
|
||||
/// </summary>
|
||||
public class RepeatableOperationSequence : OperationSequence, IRepeatableOperationSequence
|
||||
{
|
||||
private new Queue<IRepeatableOperation> operations;
|
||||
|
||||
public RepeatableOperationSequence(ILogger logger, Queue<IRepeatableOperation> operations) : base(logger, new Queue<IOperation>(operations))
|
||||
{
|
||||
this.operations = new Queue<IRepeatableOperation>(operations);
|
||||
}
|
||||
|
||||
public OperationResult TryRepeat()
|
||||
{
|
||||
var result = OperationResult.Failed;
|
||||
|
||||
try
|
||||
{
|
||||
Initialize();
|
||||
result = Repeat();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.Error("Failed to repeat operations!", e);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private OperationResult Repeat()
|
||||
{
|
||||
foreach (var operation in operations)
|
||||
{
|
||||
var result = OperationResult.Failed;
|
||||
|
||||
try
|
||||
{
|
||||
result = operation.Repeat();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.Error($"Caught unexpected exception while repeating operation '{operation.GetType().Name}'!", e);
|
||||
}
|
||||
|
||||
if (result != OperationResult.Success)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
UpdateProgress(new ProgressChangedEventArgs { Progress = true });
|
||||
}
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,7 +18,7 @@ namespace SafeExamBrowser.Core.Operations
|
|||
/// An operation to handle the lifetime of an <see cref="ICommunicationHost"/>. The host is started during <see cref="Perform"/>,
|
||||
/// stopped and restarted during <see cref="Repeat"/> (if not running) and stopped during <see cref="Revert"/>.
|
||||
/// </summary>
|
||||
public class CommunicationHostOperation : IOperation
|
||||
public class CommunicationHostOperation : IRepeatableOperation
|
||||
{
|
||||
private ICommunicationHost host;
|
||||
private ILogger logger;
|
||||
|
@ -56,12 +56,14 @@ namespace SafeExamBrowser.Core.Operations
|
|||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
public void Revert()
|
||||
public OperationResult Revert()
|
||||
{
|
||||
logger.Info("Stopping communication host...");
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_StopCommunicationHost);
|
||||
|
||||
host.Stop();
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ namespace SafeExamBrowser.Core.Operations
|
|||
/// A generic operation to allow for the (inline) definition of an operation via delegates. Useful if implementing a complete
|
||||
/// <see cref="IOperation"/> would be an unnecessary overhead.
|
||||
/// </summary>
|
||||
public class DelegateOperation : IOperation
|
||||
public class DelegateOperation : IRepeatableOperation
|
||||
{
|
||||
private Action perform;
|
||||
private Action repeat;
|
||||
|
@ -46,9 +46,11 @@ namespace SafeExamBrowser.Core.Operations
|
|||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
public void Revert()
|
||||
public OperationResult Revert()
|
||||
{
|
||||
revert?.Invoke();
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using SafeExamBrowser.Contracts.Core.OperationModel;
|
||||
using SafeExamBrowser.Contracts.Core.OperationModel.Events;
|
||||
|
@ -43,14 +42,9 @@ namespace SafeExamBrowser.Core.Operations
|
|||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
public OperationResult Repeat()
|
||||
public OperationResult Revert()
|
||||
{
|
||||
throw new InvalidOperationException($"The '{nameof(I18nOperation)}' is not meant to be repeated!");
|
||||
}
|
||||
|
||||
public void Revert()
|
||||
{
|
||||
// Nothing to do here...
|
||||
return OperationResult.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,14 +83,9 @@ namespace SafeExamBrowser.Core.Operations
|
|||
return operation.Perform();
|
||||
}
|
||||
|
||||
public OperationResult Repeat()
|
||||
public OperationResult Revert()
|
||||
{
|
||||
return operation.Repeat();
|
||||
}
|
||||
|
||||
public void Revert()
|
||||
{
|
||||
operation.Revert();
|
||||
return operation.Revert();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,6 +55,7 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="OperationModel\QueueExtensions.cs" />
|
||||
<Compile Include="OperationModel\RepeatableOperationSequence.cs" />
|
||||
<Compile Include="Operations\CommunicationHostOperation.cs" />
|
||||
<Compile Include="Operations\LazyInitializationOperation.cs" />
|
||||
<Compile Include="Operations\I18nOperation.cs" />
|
||||
|
|
|
@ -117,7 +117,7 @@ namespace SafeExamBrowser.Monitoring.Windows
|
|||
|
||||
private void OnWindowChanged(IntPtr window)
|
||||
{
|
||||
if (activeWindow != window)
|
||||
if (window != IntPtr.Zero && activeWindow != window)
|
||||
{
|
||||
logger.Debug($"Window has changed from {activeWindow} to {window}.");
|
||||
activeWindow = window;
|
||||
|
|
|
@ -84,26 +84,6 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
|||
Assert.AreEqual(OperationResult.Success, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustStartClientWhenRepeating()
|
||||
{
|
||||
var result = default(OperationResult);
|
||||
var response = new AuthenticationResponse { ProcessId = 1234 };
|
||||
var communication = new CommunicationResult<AuthenticationResponse>(true, response);
|
||||
|
||||
process.SetupGet(p => p.Id).Returns(response.ProcessId);
|
||||
processFactory.Setup(f => f.StartNew(It.IsAny<string>(), It.IsAny<string[]>())).Returns(process.Object).Callback(clientReady);
|
||||
proxy.Setup(p => p.RequestAuthentication()).Returns(communication);
|
||||
proxy.Setup(p => p.Connect(It.IsAny<Guid>(), true)).Returns(true);
|
||||
|
||||
result = sut.Repeat();
|
||||
|
||||
session.VerifySet(s => s.ClientProcess = process.Object, Times.Once);
|
||||
session.VerifySet(s => s.ClientProxy = proxy.Object, Times.Once);
|
||||
|
||||
Assert.AreEqual(OperationResult.Success, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustFailStartupIfClientNotStartedWithinTimeout()
|
||||
{
|
||||
|
@ -153,6 +133,87 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
|||
Assert.AreEqual(OperationResult.Failed, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustStartClientWhenRepeating()
|
||||
{
|
||||
var result = default(OperationResult);
|
||||
var response = new AuthenticationResponse { ProcessId = 1234 };
|
||||
var communication = new CommunicationResult<AuthenticationResponse>(true, response);
|
||||
|
||||
process.SetupGet(p => p.Id).Returns(response.ProcessId);
|
||||
processFactory.Setup(f => f.StartNew(It.IsAny<string>(), It.IsAny<string[]>())).Returns(process.Object).Callback(clientReady);
|
||||
proxy.Setup(p => p.RequestAuthentication()).Returns(communication);
|
||||
proxy.Setup(p => p.Connect(It.IsAny<Guid>(), true)).Returns(true);
|
||||
|
||||
result = sut.Repeat();
|
||||
|
||||
session.VerifySet(s => s.ClientProcess = process.Object, Times.Once);
|
||||
session.VerifySet(s => s.ClientProxy = proxy.Object, Times.Once);
|
||||
|
||||
Assert.AreEqual(OperationResult.Success, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustTerminateClientOnRepeat()
|
||||
{
|
||||
var terminated = new Action(() =>
|
||||
{
|
||||
runtimeHost.Raise(h => h.ClientDisconnected += null);
|
||||
process.Raise(p => p.Terminated += null, 0);
|
||||
});
|
||||
|
||||
proxy.Setup(p => p.Disconnect()).Callback(terminated);
|
||||
session.SetupGet(s => s.ClientProcess).Returns(process.Object);
|
||||
|
||||
var result = sut.Repeat();
|
||||
|
||||
proxy.Verify(p => p.InitiateShutdown(), Times.Once);
|
||||
proxy.Verify(p => p.Disconnect(), Times.Once);
|
||||
process.Verify(p => p.Kill(), Times.Never);
|
||||
session.VerifySet(s => s.ClientProcess = null, Times.Once);
|
||||
session.VerifySet(s => s.ClientProxy = null, Times.Once);
|
||||
|
||||
Assert.AreEqual(OperationResult.Success, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustDoNothingIfNoClientCreated()
|
||||
{
|
||||
session.SetupGet(s => s.ClientProcess).Returns(null as IProcess);
|
||||
|
||||
var result = sut.Repeat();
|
||||
|
||||
session.VerifyGet(s => s.ClientProcess, Times.Once);
|
||||
|
||||
process.VerifyNoOtherCalls();
|
||||
processFactory.VerifyNoOtherCalls();
|
||||
proxy.VerifyNoOtherCalls();
|
||||
proxyFactory.VerifyNoOtherCalls();
|
||||
runtimeHost.VerifyNoOtherCalls();
|
||||
|
||||
Assert.AreEqual(OperationResult.Success, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustDoNothingIfNoClientRunning()
|
||||
{
|
||||
process.SetupGet(p => p.HasTerminated).Returns(true);
|
||||
session.SetupGet(s => s.ClientProcess).Returns(process.Object);
|
||||
|
||||
var result = sut.Repeat();
|
||||
|
||||
process.VerifyGet(p => p.HasTerminated, Times.Once);
|
||||
session.VerifyGet(s => s.ClientProcess, Times.Exactly(2));
|
||||
|
||||
process.VerifyNoOtherCalls();
|
||||
processFactory.VerifyNoOtherCalls();
|
||||
proxy.VerifyNoOtherCalls();
|
||||
proxyFactory.VerifyNoOtherCalls();
|
||||
runtimeHost.VerifyNoOtherCalls();
|
||||
|
||||
Assert.AreEqual(OperationResult.Success, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustStopClientWhenReverting()
|
||||
{
|
||||
|
|
|
@ -1,144 +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 Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using SafeExamBrowser.Contracts.Communication.Hosts;
|
||||
using SafeExamBrowser.Contracts.Communication.Proxies;
|
||||
using SafeExamBrowser.Contracts.Configuration;
|
||||
using SafeExamBrowser.Contracts.Core.OperationModel;
|
||||
using SafeExamBrowser.Contracts.Logging;
|
||||
using SafeExamBrowser.Contracts.WindowsApi;
|
||||
using SafeExamBrowser.Runtime.Operations;
|
||||
|
||||
namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||
{
|
||||
[TestClass]
|
||||
public class ClientTerminationOperationTests
|
||||
{
|
||||
private Action clientReady;
|
||||
private AppConfig appConfig;
|
||||
private Mock<IConfigurationRepository> configuration;
|
||||
private Mock<IClientProxy> proxy;
|
||||
private Mock<ILogger> logger;
|
||||
private Mock<IProcess> process;
|
||||
private Mock<IProcessFactory> processFactory;
|
||||
private Mock<IProxyFactory> proxyFactory;
|
||||
private Mock<IRuntimeHost> runtimeHost;
|
||||
private Mock<ISessionData> session;
|
||||
private ClientTerminationOperation sut;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
appConfig = new AppConfig();
|
||||
configuration = new Mock<IConfigurationRepository>();
|
||||
clientReady = new Action(() => runtimeHost.Raise(h => h.ClientReady += null));
|
||||
logger = new Mock<ILogger>();
|
||||
process = new Mock<IProcess>();
|
||||
processFactory = new Mock<IProcessFactory>();
|
||||
proxy = new Mock<IClientProxy>();
|
||||
proxyFactory = new Mock<IProxyFactory>();
|
||||
runtimeHost = new Mock<IRuntimeHost>();
|
||||
session = new Mock<ISessionData>();
|
||||
|
||||
configuration.SetupGet(c => c.CurrentSession).Returns(session.Object);
|
||||
configuration.SetupGet(c => c.AppConfig).Returns(appConfig);
|
||||
proxyFactory.Setup(f => f.CreateClientProxy(It.IsAny<string>())).Returns(proxy.Object);
|
||||
session.SetupGet(s => s.ClientProxy).Returns(proxy.Object);
|
||||
|
||||
sut = new ClientTerminationOperation(configuration.Object, logger.Object, processFactory.Object, proxyFactory.Object, runtimeHost.Object, 0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustDoNothingOnPerform()
|
||||
{
|
||||
sut.Perform();
|
||||
|
||||
process.VerifyNoOtherCalls();
|
||||
processFactory.VerifyNoOtherCalls();
|
||||
proxy.VerifyNoOtherCalls();
|
||||
proxyFactory.VerifyNoOtherCalls();
|
||||
runtimeHost.VerifyNoOtherCalls();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustDoNothingOnRevert()
|
||||
{
|
||||
sut.Revert();
|
||||
|
||||
process.VerifyNoOtherCalls();
|
||||
processFactory.VerifyNoOtherCalls();
|
||||
proxy.VerifyNoOtherCalls();
|
||||
proxyFactory.VerifyNoOtherCalls();
|
||||
runtimeHost.VerifyNoOtherCalls();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustTerminateClientOnRepeat()
|
||||
{
|
||||
var terminated = new Action(() =>
|
||||
{
|
||||
runtimeHost.Raise(h => h.ClientDisconnected += null);
|
||||
process.Raise(p => p.Terminated += null, 0);
|
||||
});
|
||||
|
||||
proxy.Setup(p => p.Disconnect()).Callback(terminated);
|
||||
session.SetupGet(s => s.ClientProcess).Returns(process.Object);
|
||||
|
||||
var result = sut.Repeat();
|
||||
|
||||
proxy.Verify(p => p.InitiateShutdown(), Times.Once);
|
||||
proxy.Verify(p => p.Disconnect(), Times.Once);
|
||||
process.Verify(p => p.Kill(), Times.Never);
|
||||
session.VerifySet(s => s.ClientProcess = null, Times.Once);
|
||||
session.VerifySet(s => s.ClientProxy = null, Times.Once);
|
||||
|
||||
Assert.AreEqual(OperationResult.Success, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustDoNothingIfNoClientCreated()
|
||||
{
|
||||
session.SetupGet(s => s.ClientProcess).Returns(null as IProcess);
|
||||
|
||||
var result = sut.Repeat();
|
||||
|
||||
session.VerifyGet(s => s.ClientProcess, Times.Once);
|
||||
|
||||
process.VerifyNoOtherCalls();
|
||||
processFactory.VerifyNoOtherCalls();
|
||||
proxy.VerifyNoOtherCalls();
|
||||
proxyFactory.VerifyNoOtherCalls();
|
||||
runtimeHost.VerifyNoOtherCalls();
|
||||
|
||||
Assert.AreEqual(OperationResult.Success, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustDoNothingIfNoClientRunning()
|
||||
{
|
||||
process.SetupGet(p => p.HasTerminated).Returns(true);
|
||||
session.SetupGet(s => s.ClientProcess).Returns(process.Object);
|
||||
|
||||
var result = sut.Repeat();
|
||||
|
||||
process.VerifyGet(p => p.HasTerminated, Times.Once);
|
||||
session.VerifyGet(s => s.ClientProcess, Times.Exactly(2));
|
||||
|
||||
process.VerifyNoOtherCalls();
|
||||
processFactory.VerifyNoOtherCalls();
|
||||
proxy.VerifyNoOtherCalls();
|
||||
proxyFactory.VerifyNoOtherCalls();
|
||||
runtimeHost.VerifyNoOtherCalls();
|
||||
|
||||
Assert.AreEqual(OperationResult.Success, result);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -90,8 +90,8 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
|||
[TestMethod]
|
||||
public void MustCorrectlyRevertCreateNewDesktop()
|
||||
{
|
||||
var originalDesktop = new Mock<IDesktop>();
|
||||
var newDesktop = new Mock<IDesktop>();
|
||||
var originalDesktop = new Mock<IDesktop>();
|
||||
var order = 0;
|
||||
var activate = 0;
|
||||
var setStartup = 0;
|
||||
|
@ -135,8 +135,8 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
|||
[TestMethod]
|
||||
public void MustCorrectlySwitchToOtherKioskModeWhenRepeating()
|
||||
{
|
||||
var originalDesktop = new Mock<IDesktop>();
|
||||
var newDesktop = new Mock<IDesktop>();
|
||||
var originalDesktop = new Mock<IDesktop>();
|
||||
|
||||
desktopFactory.Setup(f => f.GetCurrent()).Returns(originalDesktop.Object);
|
||||
desktopFactory.Setup(f => f.CreateNew(It.IsAny<string>())).Returns(newDesktop.Object);
|
||||
|
@ -192,8 +192,8 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
|||
[TestMethod]
|
||||
public void MustNotReinitializeCreateNewDesktopWhenRepeating()
|
||||
{
|
||||
var originalDesktop = new Mock<IDesktop>();
|
||||
var newDesktop = new Mock<IDesktop>();
|
||||
var originalDesktop = new Mock<IDesktop>();
|
||||
|
||||
settings.KioskMode = KioskMode.CreateNewDesktop;
|
||||
|
||||
|
|
|
@ -83,7 +83,6 @@
|
|||
<Compile Include="Operations\KioskModeOperationTests.cs" />
|
||||
<Compile Include="Operations\ServiceOperationTests.cs" />
|
||||
<Compile Include="Operations\ClientOperationTests.cs" />
|
||||
<Compile Include="Operations\ClientTerminationOperationTests.cs" />
|
||||
<Compile Include="Operations\SessionInitializationOperationTests.cs" />
|
||||
<Compile Include="RuntimeControllerTests.cs" />
|
||||
<Compile Include="Communication\RuntimeHostTests.cs" />
|
||||
|
|
|
@ -66,20 +66,21 @@ namespace SafeExamBrowser.Runtime
|
|||
var uiFactory = new UserInterfaceFactory(text);
|
||||
|
||||
var bootstrapOperations = new Queue<IOperation>();
|
||||
var sessionOperations = new Queue<IOperation>();
|
||||
var sessionOperations = new Queue<IRepeatableOperation>();
|
||||
|
||||
bootstrapOperations.Enqueue(new I18nOperation(logger, text, textResource));
|
||||
bootstrapOperations.Enqueue(new CommunicationHostOperation(runtimeHost, logger));
|
||||
|
||||
sessionOperations.Enqueue(new ConfigurationOperation(appConfig, configuration, logger, resourceLoader, args));
|
||||
sessionOperations.Enqueue(new ClientTerminationOperation(configuration, logger, processFactory, proxyFactory, runtimeHost, FIFTEEN_SECONDS));
|
||||
sessionOperations.Enqueue(new KioskModeTerminationOperation(configuration, desktopFactory, explorerShell, logger, processFactory));
|
||||
sessionOperations.Enqueue(new SessionInitializationOperation(configuration, logger, runtimeHost));
|
||||
sessionOperations.Enqueue(new ServiceOperation(configuration, logger, serviceProxy));
|
||||
sessionOperations.Enqueue(new ClientTerminationOperation(configuration, logger, processFactory, proxyFactory, runtimeHost, FIFTEEN_SECONDS));
|
||||
sessionOperations.Enqueue(new KioskModeOperation(configuration, desktopFactory, explorerShell, logger, processFactory));
|
||||
sessionOperations.Enqueue(new ClientOperation(configuration, logger, processFactory, proxyFactory, runtimeHost, FIFTEEN_SECONDS));
|
||||
|
||||
var bootstrapSequence = new OperationSequence(logger, bootstrapOperations);
|
||||
var sessionSequence = new OperationSequence(logger, sessionOperations);
|
||||
var sessionSequence = new RepeatableOperationSequence(logger, sessionOperations);
|
||||
|
||||
RuntimeController = new RuntimeController(appConfig, configuration, logger, messageBox, bootstrapSequence, sessionSequence, runtimeHost, serviceProxy, shutdown, text, uiFactory);
|
||||
}
|
||||
|
|
|
@ -20,26 +20,26 @@ using SafeExamBrowser.Contracts.WindowsApi.Events;
|
|||
|
||||
namespace SafeExamBrowser.Runtime.Operations
|
||||
{
|
||||
internal class ClientOperation : IOperation
|
||||
internal class ClientOperation : IRepeatableOperation
|
||||
{
|
||||
private readonly int timeout_ms;
|
||||
|
||||
protected IConfigurationRepository configuration;
|
||||
protected ILogger logger;
|
||||
protected IProcessFactory processFactory;
|
||||
protected IProxyFactory proxyFactory;
|
||||
protected IRuntimeHost runtimeHost;
|
||||
private IConfigurationRepository configuration;
|
||||
private ILogger logger;
|
||||
private IProcessFactory processFactory;
|
||||
private IProxyFactory proxyFactory;
|
||||
private IRuntimeHost runtimeHost;
|
||||
|
||||
public event ActionRequiredEventHandler ActionRequired { add { } remove { } }
|
||||
public event StatusChangedEventHandler StatusChanged;
|
||||
|
||||
protected IProcess ClientProcess
|
||||
private IProcess ClientProcess
|
||||
{
|
||||
get { return configuration.CurrentSession.ClientProcess; }
|
||||
set { configuration.CurrentSession.ClientProcess = value; }
|
||||
}
|
||||
|
||||
protected IClientProxy ClientProxy
|
||||
private IClientProxy ClientProxy
|
||||
{
|
||||
get { return configuration.CurrentSession.ClientProxy; }
|
||||
set { configuration.CurrentSession.ClientProxy = value; }
|
||||
|
@ -84,16 +84,20 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
return Perform();
|
||||
}
|
||||
|
||||
public virtual void Revert()
|
||||
public virtual OperationResult Revert()
|
||||
{
|
||||
var success = true;
|
||||
|
||||
if (ClientProcess != null && !ClientProcess.HasTerminated)
|
||||
{
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_StopClient);
|
||||
TryStopClient();
|
||||
success = TryStopClient();
|
||||
}
|
||||
|
||||
return success ? OperationResult.Success : OperationResult.Failed;
|
||||
}
|
||||
|
||||
protected bool TryStartClient()
|
||||
private bool TryStartClient()
|
||||
{
|
||||
var clientReady = false;
|
||||
var clientReadyEvent = new AutoResetEvent(false);
|
||||
|
@ -146,7 +150,7 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
return true;
|
||||
}
|
||||
|
||||
protected bool TryStopClient()
|
||||
private bool TryStopClient()
|
||||
{
|
||||
var success = false;
|
||||
|
||||
|
@ -206,7 +210,7 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
return success;
|
||||
}
|
||||
|
||||
protected bool TryKillClient(int attempt = 0)
|
||||
private bool TryKillClient(int attempt = 0)
|
||||
{
|
||||
const int MAX_ATTEMPTS = 5;
|
||||
|
||||
|
|
|
@ -10,18 +10,13 @@ using SafeExamBrowser.Contracts.Communication.Hosts;
|
|||
using SafeExamBrowser.Contracts.Communication.Proxies;
|
||||
using SafeExamBrowser.Contracts.Configuration;
|
||||
using SafeExamBrowser.Contracts.Core.OperationModel;
|
||||
using SafeExamBrowser.Contracts.Core.OperationModel.Events;
|
||||
using SafeExamBrowser.Contracts.I18n;
|
||||
using SafeExamBrowser.Contracts.Logging;
|
||||
using SafeExamBrowser.Contracts.WindowsApi;
|
||||
|
||||
namespace SafeExamBrowser.Runtime.Operations
|
||||
{
|
||||
internal class ClientTerminationOperation : ClientOperation
|
||||
internal class ClientTerminationOperation : ClientOperation, IRepeatableOperation
|
||||
{
|
||||
public new event ActionRequiredEventHandler ActionRequired { add { } remove { } }
|
||||
public new event StatusChangedEventHandler StatusChanged;
|
||||
|
||||
public ClientTerminationOperation(
|
||||
IConfigurationRepository configuration,
|
||||
ILogger logger,
|
||||
|
@ -39,20 +34,12 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
|
||||
public override OperationResult Repeat()
|
||||
{
|
||||
var success = true;
|
||||
|
||||
if (ClientProcess != null && !ClientProcess.HasTerminated)
|
||||
{
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_StopClient);
|
||||
success = TryStopClient();
|
||||
}
|
||||
|
||||
return success ? OperationResult.Success : OperationResult.Failed;
|
||||
return base.Revert();
|
||||
}
|
||||
|
||||
public override void Revert()
|
||||
public override OperationResult Revert()
|
||||
{
|
||||
// Nothing to do here...
|
||||
return OperationResult.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ using SafeExamBrowser.Runtime.Operations.Events;
|
|||
|
||||
namespace SafeExamBrowser.Runtime.Operations
|
||||
{
|
||||
internal class ConfigurationOperation : IOperation
|
||||
internal class ConfigurationOperation : IRepeatableOperation
|
||||
{
|
||||
private IConfigurationRepository configuration;
|
||||
private ILogger logger;
|
||||
|
@ -92,9 +92,9 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
return OperationResult.Failed;
|
||||
}
|
||||
|
||||
public void Revert()
|
||||
public OperationResult Revert()
|
||||
{
|
||||
// Nothing to do here...
|
||||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
private OperationResult LoadSettings(Uri uri)
|
||||
|
|
|
@ -16,7 +16,7 @@ using SafeExamBrowser.Contracts.WindowsApi;
|
|||
|
||||
namespace SafeExamBrowser.Runtime.Operations
|
||||
{
|
||||
internal class KioskModeOperation : IOperation
|
||||
internal class KioskModeOperation : IRepeatableOperation
|
||||
{
|
||||
private IConfigurationRepository configuration;
|
||||
private IDesktopFactory desktopFactory;
|
||||
|
@ -24,12 +24,22 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
private KioskMode kioskMode;
|
||||
private ILogger logger;
|
||||
private IProcessFactory processFactory;
|
||||
private IDesktop newDesktop;
|
||||
private IDesktop originalDesktop;
|
||||
|
||||
public event ActionRequiredEventHandler ActionRequired { add { } remove { } }
|
||||
public event StatusChangedEventHandler StatusChanged;
|
||||
|
||||
private IDesktop NewDesktop
|
||||
{
|
||||
get { return configuration.CurrentSession.NewDesktop; }
|
||||
set { configuration.CurrentSession.NewDesktop = value; }
|
||||
}
|
||||
|
||||
private IDesktop OriginalDesktop
|
||||
{
|
||||
get { return configuration.CurrentSession.OriginalDesktop; }
|
||||
set { configuration.CurrentSession.OriginalDesktop = value; }
|
||||
}
|
||||
|
||||
public KioskModeOperation(
|
||||
IConfigurationRepository configuration,
|
||||
IDesktopFactory desktopFactory,
|
||||
|
@ -44,7 +54,7 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
this.processFactory = processFactory;
|
||||
}
|
||||
|
||||
public OperationResult Perform()
|
||||
public virtual OperationResult Perform()
|
||||
{
|
||||
kioskMode = configuration.CurrentSettings.KioskMode;
|
||||
|
||||
|
@ -64,7 +74,7 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
public OperationResult Repeat()
|
||||
public virtual OperationResult Repeat()
|
||||
{
|
||||
var oldMode = kioskMode;
|
||||
var newMode = configuration.CurrentSettings.KioskMode;
|
||||
|
@ -72,19 +82,14 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
if (newMode == oldMode)
|
||||
{
|
||||
logger.Info($"New kiosk mode '{newMode}' is equal to the currently active '{oldMode}', skipping re-initialization...");
|
||||
}
|
||||
else
|
||||
{
|
||||
Revert();
|
||||
Perform();
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
kioskMode = newMode;
|
||||
|
||||
return OperationResult.Success;
|
||||
return Perform();
|
||||
}
|
||||
|
||||
public void Revert()
|
||||
public virtual OperationResult Revert()
|
||||
{
|
||||
logger.Info($"Reverting kiosk mode '{kioskMode}'...");
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_RevertKioskMode);
|
||||
|
@ -98,18 +103,20 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
RestartExplorerShell();
|
||||
break;
|
||||
}
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
private void CreateNewDesktop()
|
||||
{
|
||||
originalDesktop = desktopFactory.GetCurrent();
|
||||
logger.Info($"Current desktop is {originalDesktop}.");
|
||||
OriginalDesktop = desktopFactory.GetCurrent();
|
||||
logger.Info($"Current desktop is {OriginalDesktop}.");
|
||||
|
||||
newDesktop = desktopFactory.CreateNew(nameof(SafeExamBrowser));
|
||||
logger.Info($"Created new desktop {newDesktop}.");
|
||||
NewDesktop = desktopFactory.CreateNew(nameof(SafeExamBrowser));
|
||||
logger.Info($"Created new desktop {NewDesktop}.");
|
||||
|
||||
newDesktop.Activate();
|
||||
processFactory.StartupDesktop = newDesktop;
|
||||
NewDesktop.Activate();
|
||||
processFactory.StartupDesktop = NewDesktop;
|
||||
logger.Info("Successfully activated new desktop.");
|
||||
|
||||
explorerShell.Suspend();
|
||||
|
@ -117,21 +124,21 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
|
||||
private void CloseNewDesktop()
|
||||
{
|
||||
if (originalDesktop != null)
|
||||
if (OriginalDesktop != null)
|
||||
{
|
||||
originalDesktop.Activate();
|
||||
processFactory.StartupDesktop = originalDesktop;
|
||||
logger.Info($"Switched back to original desktop {originalDesktop}.");
|
||||
OriginalDesktop.Activate();
|
||||
processFactory.StartupDesktop = OriginalDesktop;
|
||||
logger.Info($"Switched back to original desktop {OriginalDesktop}.");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Warn($"No original desktop found when attempting to close new desktop!");
|
||||
}
|
||||
|
||||
if (newDesktop != null)
|
||||
if (NewDesktop != null)
|
||||
{
|
||||
newDesktop.Close();
|
||||
logger.Info($"Closed new desktop {newDesktop}.");
|
||||
NewDesktop.Close();
|
||||
logger.Info($"Closed new desktop {NewDesktop}.");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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 SafeExamBrowser.Contracts.Configuration;
|
||||
using SafeExamBrowser.Contracts.Configuration.Settings;
|
||||
using SafeExamBrowser.Contracts.Core.OperationModel;
|
||||
using SafeExamBrowser.Contracts.Logging;
|
||||
using SafeExamBrowser.Contracts.WindowsApi;
|
||||
|
||||
namespace SafeExamBrowser.Runtime.Operations
|
||||
{
|
||||
internal class KioskModeTerminationOperation : KioskModeOperation, IRepeatableOperation
|
||||
{
|
||||
private IConfigurationRepository configuration;
|
||||
private KioskMode kioskMode;
|
||||
private ILogger logger;
|
||||
|
||||
public KioskModeTerminationOperation(
|
||||
IConfigurationRepository configuration,
|
||||
IDesktopFactory desktopFactory,
|
||||
IExplorerShell explorerShell,
|
||||
ILogger logger,
|
||||
IProcessFactory processFactory) : base(configuration, desktopFactory, explorerShell, logger, processFactory)
|
||||
{
|
||||
this.configuration = configuration;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
public override OperationResult Perform()
|
||||
{
|
||||
kioskMode = configuration.CurrentSettings.KioskMode;
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
public override OperationResult Repeat()
|
||||
{
|
||||
var oldMode = kioskMode;
|
||||
var newMode = configuration.CurrentSettings.KioskMode;
|
||||
|
||||
if (newMode == oldMode)
|
||||
{
|
||||
logger.Info($"New kiosk mode '{newMode}' is equal to the currently active '{oldMode}', skipping termination...");
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
return base.Revert();
|
||||
}
|
||||
|
||||
public override OperationResult Revert()
|
||||
{
|
||||
return OperationResult.Success;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,7 +16,7 @@ using SafeExamBrowser.Contracts.Logging;
|
|||
|
||||
namespace SafeExamBrowser.Runtime.Operations
|
||||
{
|
||||
internal class ServiceOperation : IOperation
|
||||
internal class ServiceOperation : IRepeatableOperation
|
||||
{
|
||||
private bool connected, mandatory;
|
||||
private IConfigurationRepository configuration;
|
||||
|
@ -43,7 +43,7 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
|
||||
if (mandatory && !connected)
|
||||
{
|
||||
logger.Error("Aborting startup because the service is mandatory but not available!");
|
||||
logger.Error("Failed to initialize a service session since the service is mandatory but not available!");
|
||||
|
||||
return OperationResult.Failed;
|
||||
}
|
||||
|
@ -61,17 +61,17 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
|
||||
public OperationResult Repeat()
|
||||
{
|
||||
// TODO: Re-check if mandatory, if so, try to connect (if not connected) - otherwise, no action required (except maybe logging of status?)
|
||||
if (connected)
|
||||
var result = Revert();
|
||||
|
||||
if (result != OperationResult.Success)
|
||||
{
|
||||
StopServiceSession();
|
||||
StartServiceSession();
|
||||
return result;
|
||||
}
|
||||
|
||||
return OperationResult.Success;
|
||||
return Perform();
|
||||
}
|
||||
|
||||
public void Revert()
|
||||
public OperationResult Revert()
|
||||
{
|
||||
logger.Info("Finalizing service session...");
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_FinalizeServiceSession);
|
||||
|
@ -91,6 +91,8 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
logger.Error("Failed to disconnect from the service!");
|
||||
}
|
||||
}
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
private void StartServiceSession()
|
||||
|
|
|
@ -15,7 +15,7 @@ using SafeExamBrowser.Contracts.Logging;
|
|||
|
||||
namespace SafeExamBrowser.Runtime.Operations
|
||||
{
|
||||
internal class SessionInitializationOperation : IOperation
|
||||
internal class SessionInitializationOperation : IRepeatableOperation
|
||||
{
|
||||
private IConfigurationRepository configuration;
|
||||
private ILogger logger;
|
||||
|
@ -40,14 +40,12 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
|
||||
public OperationResult Repeat()
|
||||
{
|
||||
InitializeSessionConfiguration();
|
||||
|
||||
return OperationResult.Success;
|
||||
return Perform();
|
||||
}
|
||||
|
||||
public void Revert()
|
||||
public OperationResult Revert()
|
||||
{
|
||||
// Nothing to do here...
|
||||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
private void InitializeSessionConfiguration()
|
||||
|
|
|
@ -35,7 +35,7 @@ namespace SafeExamBrowser.Runtime
|
|||
private ILogger logger;
|
||||
private IMessageBox messageBox;
|
||||
private IOperationSequence bootstrapSequence;
|
||||
private IOperationSequence sessionSequence;
|
||||
private IRepeatableOperationSequence sessionSequence;
|
||||
private IRuntimeHost runtimeHost;
|
||||
private IRuntimeWindow runtimeWindow;
|
||||
private IServiceProxy service;
|
||||
|
@ -50,7 +50,7 @@ namespace SafeExamBrowser.Runtime
|
|||
ILogger logger,
|
||||
IMessageBox messageBox,
|
||||
IOperationSequence bootstrapSequence,
|
||||
IOperationSequence sessionSequence,
|
||||
IRepeatableOperationSequence sessionSequence,
|
||||
IRuntimeHost runtimeHost,
|
||||
IServiceProxy service,
|
||||
Action shutdown,
|
||||
|
@ -127,7 +127,7 @@ namespace SafeExamBrowser.Runtime
|
|||
logger.Log(string.Empty);
|
||||
logger.Info("Initiating shutdown procedure...");
|
||||
|
||||
var success = bootstrapSequence.TryRevert();
|
||||
var success = bootstrapSequence.TryRevert() == OperationResult.Success;
|
||||
|
||||
if (success)
|
||||
{
|
||||
|
@ -181,8 +181,8 @@ namespace SafeExamBrowser.Runtime
|
|||
|
||||
if (result == OperationResult.Failed)
|
||||
{
|
||||
// TODO: Check if message box is rendered on new desktop as well! -> E.g. if settings for reconfiguration are invalid
|
||||
messageBox.Show(TextKey.MessageBox_SessionStartError, TextKey.MessageBox_SessionStartErrorTitle, icon: MessageBoxIcon.Error, parent: runtimeWindow);
|
||||
// TODO: Find solution for this; maybe manually switch back to original desktop?
|
||||
// messageBox.Show(TextKey.MessageBox_SessionStartError, TextKey.MessageBox_SessionStartErrorTitle, icon: MessageBoxIcon.Error, parent: runtimeWindow);
|
||||
|
||||
if (!initial)
|
||||
{
|
||||
|
@ -202,7 +202,7 @@ namespace SafeExamBrowser.Runtime
|
|||
|
||||
DeregisterSessionEvents();
|
||||
|
||||
var success = sessionSequence.TryRevert();
|
||||
var success = sessionSequence.TryRevert() == OperationResult.Success;
|
||||
|
||||
if (success)
|
||||
{
|
||||
|
@ -236,37 +236,20 @@ namespace SafeExamBrowser.Runtime
|
|||
|
||||
private void DeregisterSessionEvents()
|
||||
{
|
||||
configuration.CurrentSession.ClientProcess.Terminated -= ClientProcess_Terminated;
|
||||
configuration.CurrentSession.ClientProxy.ConnectionLost -= Client_ConnectionLost;
|
||||
if (configuration.CurrentSession.ClientProcess != null)
|
||||
{
|
||||
configuration.CurrentSession.ClientProcess.Terminated -= ClientProcess_Terminated;
|
||||
}
|
||||
|
||||
if (configuration.CurrentSession.ClientProxy != null)
|
||||
{
|
||||
configuration.CurrentSession.ClientProxy.ConnectionLost -= Client_ConnectionLost;
|
||||
}
|
||||
}
|
||||
|
||||
private void BootstrapSequence_ProgressChanged(ProgressChangedEventArgs args)
|
||||
{
|
||||
// TODO: Duplicated code (for splashScreen as well as runtimeWindow)!
|
||||
if (args.CurrentValue.HasValue)
|
||||
{
|
||||
splashScreen?.SetValue(args.CurrentValue.Value);
|
||||
}
|
||||
|
||||
if (args.IsIndeterminate == true)
|
||||
{
|
||||
splashScreen?.SetIndeterminate();
|
||||
}
|
||||
|
||||
if (args.MaxValue.HasValue)
|
||||
{
|
||||
splashScreen?.SetMaxValue(args.MaxValue.Value);
|
||||
}
|
||||
|
||||
if (args.Progress == true)
|
||||
{
|
||||
splashScreen?.Progress();
|
||||
}
|
||||
|
||||
if (args.Regress == true)
|
||||
{
|
||||
splashScreen?.Regress();
|
||||
}
|
||||
MapProgress(splashScreen, args);
|
||||
}
|
||||
|
||||
private void BootstrapSequence_StatusChanged(TextKey status)
|
||||
|
@ -277,7 +260,12 @@ namespace SafeExamBrowser.Runtime
|
|||
private void ClientProcess_Terminated(int exitCode)
|
||||
{
|
||||
logger.Error($"Client application has unexpectedly terminated with exit code {exitCode}!");
|
||||
// TODO: Check if message box is rendered on new desktop as well -> otherwise shutdown is blocked! Check if parent needed!
|
||||
|
||||
if (sessionRunning)
|
||||
{
|
||||
StopSession();
|
||||
}
|
||||
|
||||
messageBox.Show(TextKey.MessageBox_ApplicationError, TextKey.MessageBox_ApplicationErrorTitle, icon: MessageBoxIcon.Error);
|
||||
|
||||
shutdown.Invoke();
|
||||
|
@ -286,7 +274,12 @@ namespace SafeExamBrowser.Runtime
|
|||
private void Client_ConnectionLost()
|
||||
{
|
||||
logger.Error("Lost connection to the client application!");
|
||||
// TODO: Check if message box is rendered on new desktop as well -> otherwise shutdown is blocked! Check if parent needed!
|
||||
|
||||
if (sessionRunning)
|
||||
{
|
||||
StopSession();
|
||||
}
|
||||
|
||||
messageBox.Show(TextKey.MessageBox_ApplicationError, TextKey.MessageBox_ApplicationErrorTitle, icon: MessageBoxIcon.Error);
|
||||
|
||||
shutdown.Invoke();
|
||||
|
@ -400,35 +393,40 @@ namespace SafeExamBrowser.Runtime
|
|||
|
||||
private void SessionSequence_ProgressChanged(ProgressChangedEventArgs args)
|
||||
{
|
||||
if (args.CurrentValue.HasValue)
|
||||
{
|
||||
runtimeWindow?.SetValue(args.CurrentValue.Value);
|
||||
}
|
||||
|
||||
if (args.IsIndeterminate == true)
|
||||
{
|
||||
runtimeWindow?.SetIndeterminate();
|
||||
}
|
||||
|
||||
if (args.MaxValue.HasValue)
|
||||
{
|
||||
runtimeWindow?.SetMaxValue(args.MaxValue.Value);
|
||||
}
|
||||
|
||||
if (args.Progress == true)
|
||||
{
|
||||
runtimeWindow?.Progress();
|
||||
}
|
||||
|
||||
if (args.Regress == true)
|
||||
{
|
||||
runtimeWindow?.Regress();
|
||||
}
|
||||
MapProgress(runtimeWindow, args);
|
||||
}
|
||||
|
||||
private void SessionSequence_StatusChanged(TextKey status)
|
||||
{
|
||||
runtimeWindow?.UpdateStatus(status, true);
|
||||
}
|
||||
|
||||
private void MapProgress(IProgressIndicator progressIndicator, ProgressChangedEventArgs args)
|
||||
{
|
||||
if (args.CurrentValue.HasValue)
|
||||
{
|
||||
progressIndicator?.SetValue(args.CurrentValue.Value);
|
||||
}
|
||||
|
||||
if (args.IsIndeterminate == true)
|
||||
{
|
||||
progressIndicator?.SetIndeterminate();
|
||||
}
|
||||
|
||||
if (args.MaxValue.HasValue)
|
||||
{
|
||||
progressIndicator?.SetMaxValue(args.MaxValue.Value);
|
||||
}
|
||||
|
||||
if (args.Progress == true)
|
||||
{
|
||||
progressIndicator?.Progress();
|
||||
}
|
||||
|
||||
if (args.Regress == true)
|
||||
{
|
||||
progressIndicator?.Regress();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -92,6 +92,7 @@
|
|||
<Compile Include="Operations\Events\ConfigurationCompletedEventArgs.cs" />
|
||||
<Compile Include="Operations\Events\PasswordRequiredEventArgs.cs" />
|
||||
<Compile Include="Operations\KioskModeOperation.cs" />
|
||||
<Compile Include="Operations\KioskModeTerminationOperation.cs" />
|
||||
<Compile Include="Operations\ServiceOperation.cs" />
|
||||
<Compile Include="Operations\SessionInitializationOperation.cs" />
|
||||
<Compile Include="Communication\ProxyFactory.cs" />
|
||||
|
|
Loading…
Reference in a new issue