Restore SEBPatch

This commit is contained in:
2025-06-01 11:44:20 +02:00
commit 8c656e3137
1297 changed files with 142172 additions and 0 deletions

View File

@@ -0,0 +1,385 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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.Threading;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Communication.Contracts;
using SafeExamBrowser.Communication.Contracts.Data;
using SafeExamBrowser.Communication.Contracts.Events;
using SafeExamBrowser.Communication.Contracts.Hosts;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Runtime.Communication;
using SafeExamBrowser.Settings;
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
namespace SafeExamBrowser.Runtime.UnitTests.Communication
{
[TestClass]
public class RuntimeHostTests
{
private Mock<IHostObject> hostObject;
private Mock<IHostObjectFactory> hostObjectFactory;
private Mock<ILogger> logger;
private RuntimeHost sut;
[TestInitialize]
public void Initialize()
{
hostObject = new Mock<IHostObject>();
hostObjectFactory = new Mock<IHostObjectFactory>();
logger = new Mock<ILogger>();
hostObjectFactory.Setup(f => f.CreateObject(It.IsAny<string>(), It.IsAny<ICommunication>())).Returns(hostObject.Object);
sut = new RuntimeHost("net:pipe://some/address", hostObjectFactory.Object, logger.Object, 0);
}
[TestMethod]
public void MustAllowConnectionIfTokenIsValid()
{
var token = Guid.NewGuid();
sut.AllowConnection = true;
sut.AuthenticationToken = token;
var response = sut.Connect(token);
Assert.IsNotNull(response);
Assert.IsTrue(response.ConnectionEstablished);
}
[TestMethod]
public void MustRejectConnectionIfTokenInvalid()
{
var token = Guid.NewGuid();
sut.AllowConnection = true;
sut.AuthenticationToken = token;
var response = sut.Connect(Guid.NewGuid());
Assert.IsNotNull(response);
Assert.IsFalse(response.ConnectionEstablished);
}
[TestMethod]
public void MustRejectConnectionIfNoAuthenticationTokenSet()
{
var token = Guid.Empty;
sut.AllowConnection = true;
var response = sut.Connect(token);
Assert.IsNotNull(response);
Assert.IsFalse(response.ConnectionEstablished);
}
[TestMethod]
public void MustOnlyAllowOneConcurrentConnection()
{
var token = Guid.NewGuid();
sut.AllowConnection = true;
sut.AuthenticationToken = token;
var response1 = sut.Connect(token);
var response2 = sut.Connect(token);
var response3 = sut.Connect(token);
Assert.IsNotNull(response1);
Assert.IsNotNull(response2);
Assert.IsNotNull(response3);
Assert.IsNotNull(response1.CommunicationToken);
Assert.IsNull(response2.CommunicationToken);
Assert.IsNull(response3.CommunicationToken);
Assert.IsTrue(response1.ConnectionEstablished);
Assert.IsFalse(response2.ConnectionEstablished);
Assert.IsFalse(response3.ConnectionEstablished);
}
[TestMethod]
public void MustCorrectlyDisconnectClient()
{
var disconnected = false;
var token = Guid.NewGuid();
sut.AllowConnection = true;
sut.AuthenticationToken = token;
sut.ClientDisconnected += () => disconnected = true;
var connectionResponse = sut.Connect(token);
var message = new DisconnectionMessage
{
CommunicationToken = connectionResponse.CommunicationToken.Value,
Interlocutor = Interlocutor.Client
};
var response = sut.Disconnect(message);
Assert.IsNotNull(response);
Assert.IsTrue(disconnected);
Assert.IsTrue(response.ConnectionTerminated);
}
[TestMethod]
public void MustAllowReconnectionAfterDisconnection()
{
var token = Guid.NewGuid();
sut.AllowConnection = true;
sut.AuthenticationToken = token;
var response = sut.Connect(token);
sut.Disconnect(new DisconnectionMessage { CommunicationToken = response.CommunicationToken.Value });
sut.AllowConnection = true;
sut.AuthenticationToken = token = Guid.NewGuid();
response = sut.Connect(token);
Assert.IsTrue(response.ConnectionEstablished);
}
[TestMethod]
public void MustHandleClientReadyCorrectly()
{
var clientReady = false;
sut.AllowConnection = true;
sut.ClientReady += () => clientReady = true;
sut.AuthenticationToken = Guid.Empty;
var token = sut.Connect(Guid.Empty).CommunicationToken.Value;
var message = new SimpleMessage(SimpleMessagePurport.ClientIsReady) { CommunicationToken = token };
var response = sut.Send(message);
Assert.IsTrue(clientReady);
Assert.IsNotNull(response);
Assert.IsInstanceOfType(response, typeof(SimpleResponse));
Assert.AreEqual(SimpleResponsePurport.Acknowledged, (response as SimpleResponse)?.Purport);
}
[TestMethod]
public void MustHandleConfigurationRequestCorrectly()
{
var args = default(ClientConfigurationEventArgs);
var configuration = new ClientConfiguration { Settings = new AppSettings() };
configuration.Settings.Security.AdminPasswordHash = "12345";
sut.AllowConnection = true;
sut.ClientConfigurationNeeded += (a) => { args = a; args.ClientConfiguration = configuration; };
sut.AuthenticationToken = Guid.Empty;
var token = sut.Connect(Guid.Empty).CommunicationToken.Value;
var message = new SimpleMessage(SimpleMessagePurport.ConfigurationNeeded) { CommunicationToken = token };
var response = sut.Send(message);
Assert.IsNotNull(args);
Assert.IsNotNull(response);
Assert.IsInstanceOfType(response, typeof(ConfigurationResponse));
Assert.AreEqual(configuration.Settings.Security.AdminPasswordHash, (response as ConfigurationResponse)?.Configuration.Settings.Security.AdminPasswordHash);
}
[TestMethod]
public void MustHandleExamSelectionCorrectly()
{
var args = default(ExamSelectionReplyEventArgs);
var examId = "abc123";
var requestId = Guid.NewGuid();
var sync = new AutoResetEvent(false);
sut.AllowConnection = true;
sut.AuthenticationToken = Guid.Empty;
sut.ExamSelectionReceived += (a) => { args = a; sync.Set(); };
var token = sut.Connect(Guid.Empty).CommunicationToken.Value;
var message = new ExamSelectionReplyMessage(requestId, true, examId) { CommunicationToken = token };
var response = sut.Send(message);
sync.WaitOne();
Assert.IsNotNull(args);
Assert.IsNotNull(response);
Assert.IsInstanceOfType(response, typeof(SimpleResponse));
Assert.AreEqual(SimpleResponsePurport.Acknowledged, (response as SimpleResponse)?.Purport);
Assert.IsTrue(args.Success);
Assert.AreEqual(examId, args.SelectedExamId);
Assert.AreEqual(requestId, args.RequestId);
}
[TestMethod]
public void MustHandleServerFailureActionCorrectly()
{
var args = default(ServerFailureActionReplyEventArgs);
var requestId = Guid.NewGuid();
var sync = new AutoResetEvent(false);
sut.AllowConnection = true;
sut.AuthenticationToken = Guid.Empty;
sut.ServerFailureActionReceived += (a) => { args = a; sync.Set(); };
var token = sut.Connect(Guid.Empty).CommunicationToken.Value;
var message = new ServerFailureActionReplyMessage(true, false, true, requestId) { CommunicationToken = token };
var response = sut.Send(message);
sync.WaitOne();
Assert.IsNotNull(args);
Assert.IsNotNull(response);
Assert.IsInstanceOfType(response, typeof(SimpleResponse));
Assert.AreEqual(SimpleResponsePurport.Acknowledged, (response as SimpleResponse)?.Purport);
Assert.IsFalse(args.Fallback);
Assert.IsTrue(args.Abort);
Assert.IsTrue(args.Retry);
Assert.AreEqual(requestId, args.RequestId);
}
[TestMethod]
public void MustHandleShutdownRequestCorrectly()
{
var shutdownRequested = false;
sut.AllowConnection = true;
sut.ShutdownRequested += () => shutdownRequested = true;
sut.AuthenticationToken = Guid.Empty;
var token = sut.Connect(Guid.Empty).CommunicationToken.Value;
var message = new SimpleMessage(SimpleMessagePurport.RequestShutdown) { CommunicationToken = token };
var response = sut.Send(message);
Assert.IsTrue(shutdownRequested);
Assert.IsNotNull(response);
Assert.IsInstanceOfType(response, typeof(SimpleResponse));
Assert.AreEqual(SimpleResponsePurport.Acknowledged, (response as SimpleResponse)?.Purport);
}
[TestMethod]
public void MustHandleMessageBoxReplyCorrectly()
{
var args = default(MessageBoxReplyEventArgs);
var requestId = Guid.NewGuid();
var result = (int) MessageBoxResult.Ok;
var sync = new AutoResetEvent(false);
sut.AllowConnection = true;
sut.MessageBoxReplyReceived += (a) => { args = a; sync.Set(); };
sut.AuthenticationToken = Guid.Empty;
var token = sut.Connect(Guid.Empty).CommunicationToken.Value;
var message = new MessageBoxReplyMessage(requestId, result) { CommunicationToken = token };
var response = sut.Send(message);
sync.WaitOne();
Assert.IsNotNull(args);
Assert.IsNotNull(response);
Assert.IsInstanceOfType(response, typeof(SimpleResponse));
Assert.AreEqual(SimpleResponsePurport.Acknowledged, (response as SimpleResponse)?.Purport);
Assert.AreEqual(requestId, args.RequestId);
Assert.AreEqual(result, args.Result);
}
[TestMethod]
public void MustHandlePasswordReplyCorrectly()
{
var args = default(PasswordReplyEventArgs);
var password = "test1234";
var requestId = Guid.NewGuid();
var success = true;
var sync = new AutoResetEvent(false);
sut.AllowConnection = true;
sut.PasswordReceived += (a) => { args = a; sync.Set(); };
sut.AuthenticationToken = Guid.Empty;
var token = sut.Connect(Guid.Empty).CommunicationToken.Value;
var message = new PasswordReplyMessage(requestId, success, password) { CommunicationToken = token };
var response = sut.Send(message);
sync.WaitOne();
Assert.IsNotNull(args);
Assert.IsNotNull(response);
Assert.IsInstanceOfType(response, typeof(SimpleResponse));
Assert.AreEqual(SimpleResponsePurport.Acknowledged, (response as SimpleResponse)?.Purport);
Assert.AreEqual(password, args.Password);
Assert.AreEqual(requestId, args.RequestId);
Assert.AreEqual(success, args.Success);
}
[TestMethod]
public void MustHandleReconfigurationRequestCorrectly()
{
var args = default(ReconfigurationEventArgs);
var path = "C:\\Temp\\Some\\File.seb";
var url = @"https://www.host.abc/someresource.seb";
var sync = new AutoResetEvent(false);
sut.AllowConnection = true;
sut.ReconfigurationRequested += (a) => { args = a; sync.Set(); };
sut.AuthenticationToken = Guid.Empty;
var token = sut.Connect(Guid.Empty).CommunicationToken.Value;
var message = new ReconfigurationMessage(path, url) { CommunicationToken = token };
var response = sut.Send(message);
sync.WaitOne();
Assert.IsNotNull(args);
Assert.IsNotNull(response);
Assert.IsInstanceOfType(response, typeof(SimpleResponse));
Assert.AreEqual(SimpleResponsePurport.Acknowledged, (response as SimpleResponse)?.Purport);
Assert.AreEqual(path, args.ConfigurationPath);
Assert.AreEqual(url, args.ResourceUrl);
}
[TestMethod]
public void MustReturnUnknownMessageAsDefault()
{
sut.AllowConnection = true;
sut.AuthenticationToken = Guid.Empty;
var token = sut.Connect(Guid.Empty).CommunicationToken.Value;
var message = new TestMessage { CommunicationToken = token } as Message;
var response = sut.Send(message);
Assert.IsNotNull(response);
Assert.IsInstanceOfType(response, typeof(SimpleResponse));
Assert.AreEqual(SimpleResponsePurport.UnknownMessage, (response as SimpleResponse)?.Purport);
message = new SimpleMessage(default(SimpleMessagePurport)) { CommunicationToken = token };
response = sut.Send(message);
Assert.IsNotNull(response);
Assert.IsInstanceOfType(response, typeof(SimpleResponse));
Assert.AreEqual(SimpleResponsePurport.UnknownMessage, (response as SimpleResponse)?.Purport);
}
[TestMethod]
public void MustNotFailIfNoEventHandlersSubscribed()
{
sut.AllowConnection = true;
sut.AuthenticationToken = Guid.Empty;
var token = sut.Connect(Guid.Empty).CommunicationToken.Value;
sut.Send(new SimpleMessage(SimpleMessagePurport.ClientIsReady) { CommunicationToken = token });
sut.Send(new SimpleMessage(SimpleMessagePurport.ConfigurationNeeded) { CommunicationToken = token });
sut.Send(new SimpleMessage(SimpleMessagePurport.RequestShutdown) { CommunicationToken = token });
sut.Send(new MessageBoxReplyMessage(Guid.Empty, (int) MessageBoxResult.Cancel) { CommunicationToken = token });
sut.Send(new PasswordReplyMessage(Guid.Empty, false, "") { CommunicationToken = token });
sut.Send(new ReconfigurationMessage("", "") { CommunicationToken = token });
sut.Disconnect(new DisconnectionMessage { CommunicationToken = token });
}
private class TestMessage : Message { };
}
}

View File

@@ -0,0 +1,236 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Communication.Contracts;
using SafeExamBrowser.Communication.Contracts.Data;
using SafeExamBrowser.Communication.Contracts.Hosts;
using SafeExamBrowser.Communication.Contracts.Proxies;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Core.Contracts.OperationModel;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Runtime.Operations;
using SafeExamBrowser.Settings;
using SafeExamBrowser.WindowsApi.Contracts;
namespace SafeExamBrowser.Runtime.UnitTests.Operations
{
[TestClass]
public class ClientOperationTests
{
private Action clientReady;
private Action terminated;
private AppConfig appConfig;
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 SessionConfiguration session;
private SessionContext sessionContext;
private AppSettings settings;
private ClientOperation sut;
[TestInitialize]
public void Initialize()
{
appConfig = new AppConfig();
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 SessionConfiguration();
sessionContext = new SessionContext();
settings = new AppSettings();
terminated = new Action(() =>
{
runtimeHost.Raise(h => h.ClientDisconnected += null);
process.Raise(p => p.Terminated += null, 0);
});
appConfig.ClientLogFilePath = "";
session.AppConfig = appConfig;
session.Settings = settings;
sessionContext.Current = session;
sessionContext.Next = session;
proxyFactory.Setup(f => f.CreateClientProxy(It.IsAny<string>(), It.IsAny<Interlocutor>())).Returns(proxy.Object);
sut = new ClientOperation(logger.Object, processFactory.Object, proxyFactory.Object, runtimeHost.Object, sessionContext, 0);
}
[TestMethod]
public void Perform_MustStartClient()
{
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.Perform();
Assert.AreEqual(process.Object, sessionContext.ClientProcess);
Assert.AreEqual(proxy.Object, sessionContext.ClientProxy);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Perform_MustFailStartupImmediatelyIfClientTerminates()
{
const int ONE_SECOND = 1000;
var after = default(DateTime);
var before = default(DateTime);
var result = default(OperationResult);
var terminateClient = new Action(() => Task.Delay(100).ContinueWith(_ => process.Raise(p => p.Terminated += null, 0)));
processFactory.Setup(f => f.StartNew(It.IsAny<string>(), It.IsAny<string[]>())).Returns(process.Object).Callback(terminateClient);
sut = new ClientOperation(logger.Object, processFactory.Object, proxyFactory.Object, runtimeHost.Object, sessionContext, ONE_SECOND);
before = DateTime.Now;
result = sut.Perform();
after = DateTime.Now;
Assert.IsTrue(after - before < new TimeSpan(0, 0, ONE_SECOND));
Assert.AreEqual(OperationResult.Failed, result);
}
[TestMethod]
public void Perform_MustFailStartupIfConnectionToClientNotEstablished()
{
var result = default(OperationResult);
processFactory.Setup(f => f.StartNew(It.IsAny<string>(), It.IsAny<string[]>())).Returns(process.Object).Callback(clientReady);
proxy.Setup(p => p.Connect(It.IsAny<Guid>(), true)).Returns(false);
result = sut.Perform();
Assert.IsNotNull(sessionContext.ClientProcess);
Assert.IsNotNull(sessionContext.ClientProxy);
Assert.AreEqual(OperationResult.Failed, result);
}
[TestMethod]
public void Perform_MustFailStartupIfAuthenticationNotSuccessful()
{
var result = default(OperationResult);
var response = new AuthenticationResponse { ProcessId = -1 };
var communication = new CommunicationResult<AuthenticationResponse>(true, response);
process.SetupGet(p => p.Id).Returns(1234);
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.Perform();
Assert.IsNotNull(sessionContext.ClientProcess);
Assert.IsNotNull(sessionContext.ClientProxy);
Assert.AreEqual(OperationResult.Failed, result);
}
[TestMethod]
public void Repeat_MustStartClient()
{
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();
Assert.AreEqual(process.Object, sessionContext.ClientProcess);
Assert.AreEqual(proxy.Object, sessionContext.ClientProxy);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Revert_MustStopClient()
{
proxy.Setup(p => p.Disconnect()).Callback(terminated);
PerformNormally();
sut.Revert();
proxy.Verify(p => p.InitiateShutdown(), Times.Once);
proxy.Verify(p => p.Disconnect(), Times.Once);
process.Verify(p => p.TryKill(It.IsAny<int>()), Times.Never);
Assert.IsNull(sessionContext.ClientProcess);
Assert.IsNull(sessionContext.ClientProxy);
}
[TestMethod]
public void Revert_MustKillClientIfStoppingFailed()
{
process.Setup(p => p.TryKill(It.IsAny<int>())).Callback(() => process.SetupGet(p => p.HasTerminated).Returns(true));
PerformNormally();
sut.Revert();
process.Verify(p => p.TryKill(It.IsAny<int>()), Times.AtLeastOnce);
Assert.IsNull(sessionContext.ClientProcess);
Assert.IsNull(sessionContext.ClientProxy);
}
[TestMethod]
public void Revert_MustAttemptToKillFiveTimesThenAbort()
{
PerformNormally();
sut.Revert();
process.Verify(p => p.TryKill(It.IsAny<int>()), Times.Exactly(5));
Assert.IsNotNull(sessionContext.ClientProcess);
Assert.IsNotNull(sessionContext.ClientProxy);
}
[TestMethod]
public void Revert_MustNotStopClientIfAlreadyTerminated()
{
process.SetupGet(p => p.HasTerminated).Returns(true);
sut.Revert();
proxy.Verify(p => p.InitiateShutdown(), Times.Never);
proxy.Verify(p => p.Disconnect(), Times.Never);
process.Verify(p => p.TryKill(It.IsAny<int>()), Times.Never);
Assert.IsNull(sessionContext.ClientProcess);
Assert.IsNull(sessionContext.ClientProxy);
}
private void PerformNormally()
{
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);
sut.Perform();
}
}
}

View File

@@ -0,0 +1,138 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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.Communication.Contracts;
using SafeExamBrowser.Communication.Contracts.Hosts;
using SafeExamBrowser.Communication.Contracts.Proxies;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Core.Contracts.OperationModel;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.WindowsApi.Contracts;
using SafeExamBrowser.Runtime.Operations;
namespace SafeExamBrowser.Runtime.UnitTests.Operations
{
[TestClass]
public class ClientTerminationOperationTests
{
private Action clientReady;
private Action terminated;
private AppConfig appConfig;
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 SessionConfiguration session;
private SessionContext sessionContext;
private ClientTerminationOperation sut;
[TestInitialize]
public void Initialize()
{
appConfig = new AppConfig();
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 SessionConfiguration();
sessionContext = new SessionContext();
terminated = new Action(() =>
{
runtimeHost.Raise(h => h.ClientDisconnected += null);
process.Raise(p => p.Terminated += null, 0);
});
session.AppConfig = appConfig;
sessionContext.ClientProcess = process.Object;
sessionContext.ClientProxy = proxy.Object;
sessionContext.Current = session;
sessionContext.Next = session;
proxyFactory.Setup(f => f.CreateClientProxy(It.IsAny<string>(), It.IsAny<Interlocutor>())).Returns(proxy.Object);
sut = new ClientTerminationOperation(logger.Object, processFactory.Object, proxyFactory.Object, runtimeHost.Object, sessionContext, 0);
}
[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);
var result = sut.Repeat();
proxy.Verify(p => p.InitiateShutdown(), Times.Once);
proxy.Verify(p => p.Disconnect(), Times.Once);
process.Verify(p => p.TryKill(default(int)), Times.Never);
Assert.IsNull(sessionContext.ClientProcess);
Assert.IsNull(sessionContext.ClientProxy);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void MustDoNothingOnRepeatIfNoClientRunning()
{
process.SetupGet(p => p.HasTerminated).Returns(true);
sessionContext.ClientProcess = process.Object;
var result = sut.Repeat();
process.VerifyGet(p => p.HasTerminated, Times.Once);
process.VerifyNoOtherCalls();
processFactory.VerifyNoOtherCalls();
proxy.VerifyNoOtherCalls();
proxyFactory.VerifyNoOtherCalls();
runtimeHost.VerifyNoOtherCalls();
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void MustDoNothingOnPerform()
{
var result = sut.Perform();
process.VerifyNoOtherCalls();
processFactory.VerifyNoOtherCalls();
proxy.VerifyNoOtherCalls();
proxyFactory.VerifyNoOtherCalls();
runtimeHost.VerifyNoOtherCalls();
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void MustDoNothingOnRevert()
{
var result = sut.Revert();
process.VerifyNoOtherCalls();
processFactory.VerifyNoOtherCalls();
proxy.VerifyNoOtherCalls();
proxyFactory.VerifyNoOtherCalls();
runtimeHost.VerifyNoOtherCalls();
Assert.AreEqual(OperationResult.Success, result);
}
}
}

View File

@@ -0,0 +1,647 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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.IO;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Communication.Contracts.Data;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Configuration.Contracts.Cryptography;
using SafeExamBrowser.Core.Contracts.OperationModel;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Runtime.Operations;
using SafeExamBrowser.Runtime.Operations.Events;
using SafeExamBrowser.Settings;
using SafeExamBrowser.SystemComponents.Contracts;
namespace SafeExamBrowser.Runtime.UnitTests.Operations
{
[TestClass]
public class ConfigurationOperationTests
{
private const string FILE_NAME = "SebClientSettings.seb";
private AppConfig appConfig;
private Mock<IFileSystem> fileSystem;
private Mock<IHashAlgorithm> hashAlgorithm;
private Mock<ILogger> logger;
private Mock<IConfigurationRepository> repository;
private SessionConfiguration currentSession;
private SessionConfiguration nextSession;
private SessionContext sessionContext;
[TestInitialize]
public void Initialize()
{
appConfig = new AppConfig();
fileSystem = new Mock<IFileSystem>();
hashAlgorithm = new Mock<IHashAlgorithm>();
logger = new Mock<ILogger>();
repository = new Mock<IConfigurationRepository>();
currentSession = new SessionConfiguration();
nextSession = new SessionConfiguration();
sessionContext = new SessionContext();
appConfig.AppDataFilePath = $@"C:\Not\Really\AppData\File.xml";
appConfig.ProgramDataFilePath = $@"C:\Not\Really\ProgramData\File.xml";
currentSession.AppConfig = appConfig;
nextSession.AppConfig = appConfig;
sessionContext.Current = currentSession;
sessionContext.Next = nextSession;
}
[TestMethod]
public void Perform_MustUseCommandLineArgumentAs1stPrio()
{
var settings = new AppSettings { ConfigurationMode = ConfigurationMode.Exam };
var url = @"http://www.safeexambrowser.org/whatever.seb";
var location = Path.Combine(Path.GetDirectoryName(GetType().Assembly.Location), nameof(Operations), "Testdata", FILE_NAME);
appConfig.AppDataFilePath = location;
appConfig.ProgramDataFilePath = location;
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, It.IsAny<PasswordParameters>())).Returns(LoadStatus.Success);
var sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext);
var result = sut.Perform();
var resource = new Uri(url);
repository.Verify(r => r.TryLoadSettings(It.Is<Uri>(u => u.Equals(resource)), out settings, It.IsAny<PasswordParameters>()), Times.Once);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Perform_MustUseProgramDataAs2ndPrio()
{
var location = Path.Combine(Path.GetDirectoryName(GetType().Assembly.Location), nameof(Operations), "Testdata", FILE_NAME);
var settings = default(AppSettings);
appConfig.ProgramDataFilePath = location;
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, It.IsAny<PasswordParameters>())).Returns(LoadStatus.Success);
var sut = new ConfigurationOperation(null, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext);
var result = sut.Perform();
repository.Verify(r => r.TryLoadSettings(It.Is<Uri>(u => u.Equals(location)), out settings, It.IsAny<PasswordParameters>()), Times.Once);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Perform_MustUseAppDataAs3rdPrio()
{
var location = Path.Combine(Path.GetDirectoryName(GetType().Assembly.Location), nameof(Operations), "Testdata", FILE_NAME);
var settings = default(AppSettings);
appConfig.AppDataFilePath = location;
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, It.IsAny<PasswordParameters>())).Returns(LoadStatus.Success);
var sut = new ConfigurationOperation(null, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext);
var result = sut.Perform();
repository.Verify(r => r.TryLoadSettings(It.Is<Uri>(u => u.Equals(location)), out settings, It.IsAny<PasswordParameters>()), Times.Once);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Perform_MustCorrectlyHandleBrowserResource()
{
var settings = new AppSettings { ConfigurationMode = ConfigurationMode.Exam };
var url = @"http://www.safeexambrowser.org/whatever.seb";
nextSession.Settings = settings;
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, It.IsAny<PasswordParameters>())).Returns(LoadStatus.LoadWithBrowser);
var sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext);
var result = sut.Perform();
Assert.IsFalse(settings.Browser.DeleteCacheOnShutdown);
Assert.IsFalse(settings.Browser.DeleteCookiesOnShutdown);
Assert.IsTrue(settings.Security.AllowReconfiguration);
Assert.AreEqual(url, settings.Browser.StartUrl);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Perform_MustFallbackToDefaultsAsLastPrio()
{
var defaultSettings = new AppSettings();
repository.Setup(r => r.LoadDefaultSettings()).Returns(defaultSettings);
var sut = new ConfigurationOperation(null, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext);
var result = sut.Perform();
repository.Verify(r => r.LoadDefaultSettings(), Times.Once);
Assert.AreEqual(OperationResult.Success, result);
Assert.AreSame(defaultSettings, nextSession.Settings);
}
[TestMethod]
public void Perform_MustAbortIfWishedByUser()
{
var settings = new AppSettings();
var url = @"http://www.safeexambrowser.org/whatever.seb";
sessionContext.Current = null;
settings.ConfigurationMode = ConfigurationMode.ConfigureClient;
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, It.IsAny<PasswordParameters>())).Returns(LoadStatus.Success);
repository.Setup(r => r.ConfigureClientWith(It.IsAny<Uri>(), It.IsAny<PasswordParameters>())).Returns(SaveStatus.Success);
var sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext);
sut.ActionRequired += args =>
{
if (args is ConfigurationCompletedEventArgs c)
{
c.AbortStartup = true;
}
};
var result = sut.Perform();
Assert.AreEqual(OperationResult.Aborted, result);
}
[TestMethod]
public void Perform_MustNotAbortIfNotWishedByUser()
{
var settings = new AppSettings();
var url = @"http://www.safeexambrowser.org/whatever.seb";
settings.ConfigurationMode = ConfigurationMode.ConfigureClient;
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, It.IsAny<PasswordParameters>())).Returns(LoadStatus.Success);
repository.Setup(r => r.ConfigureClientWith(It.IsAny<Uri>(), It.IsAny<PasswordParameters>())).Returns(SaveStatus.Success);
var sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext);
sut.ActionRequired += args =>
{
if (args is ConfigurationCompletedEventArgs c)
{
c.AbortStartup = false;
}
};
var result = sut.Perform();
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Perform_MustInformAboutClientConfigurationError()
{
var informed = false;
var settings = new AppSettings();
var url = @"http://www.safeexambrowser.org/whatever.seb";
settings.ConfigurationMode = ConfigurationMode.ConfigureClient;
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, It.IsAny<PasswordParameters>())).Returns(LoadStatus.Success);
repository.Setup(r => r.ConfigureClientWith(It.IsAny<Uri>(), It.IsAny<PasswordParameters>())).Returns(SaveStatus.UnexpectedError);
var sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext);
sut.ActionRequired += args =>
{
if (args is ClientConfigurationErrorMessageArgs)
{
informed = true;
}
};
var result = sut.Perform();
Assert.AreEqual(OperationResult.Failed, result);
Assert.IsTrue(informed);
}
[TestMethod]
public void Perform_MustNotAllowToAbortIfNotInConfigureClientMode()
{
var settings = new AppSettings();
settings.ConfigurationMode = ConfigurationMode.Exam;
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, It.IsAny<PasswordParameters>())).Returns(LoadStatus.Success);
var sut = new ConfigurationOperation(null, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext);
sut.ActionRequired += args =>
{
if (args is ConfigurationCompletedEventArgs c)
{
Assert.Fail();
}
};
var result = sut.Perform();
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Perform_MustNotFailWithoutCommandLineArgs()
{
var defaultSettings = new AppSettings();
var result = OperationResult.Failed;
repository.Setup(r => r.LoadDefaultSettings()).Returns(defaultSettings);
var sut = new ConfigurationOperation(null, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext);
result = sut.Perform();
repository.Verify(r => r.LoadDefaultSettings(), Times.Once);
Assert.AreEqual(OperationResult.Success, result);
Assert.AreSame(defaultSettings, nextSession.Settings);
sut = new ConfigurationOperation(new string[] { }, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext);
result = sut.Perform();
repository.Verify(r => r.LoadDefaultSettings(), Times.Exactly(2));
Assert.AreEqual(OperationResult.Success, result);
Assert.AreSame(defaultSettings, nextSession.Settings);
}
[TestMethod]
public void Perform_MustNotFailWithInvalidUri()
{
var uri = @"an/invalid\uri.'*%yolo/()你好";
var sut = new ConfigurationOperation(new[] { "blubb.exe", uri }, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext);
var result = sut.Perform();
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Perform_MustOnlyAllowToEnterAdminPasswordFiveTimes()
{
var count = 0;
var localSettings = new AppSettings();
var settings = new AppSettings { ConfigurationMode = ConfigurationMode.ConfigureClient };
var url = @"http://www.safeexambrowser.org/whatever.seb";
appConfig.AppDataFilePath = Path.Combine(Path.GetDirectoryName(GetType().Assembly.Location), nameof(Operations), "Testdata", FILE_NAME);
localSettings.Security.AdminPasswordHash = "1234";
settings.Security.AdminPasswordHash = "9876";
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, It.IsAny<PasswordParameters>())).Returns(LoadStatus.Success);
repository.Setup(r => r.TryLoadSettings(It.Is<Uri>(u => u.LocalPath.Contains(FILE_NAME)), out localSettings, It.IsAny<PasswordParameters>())).Returns(LoadStatus.Success);
nextSession.Settings = settings;
var sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext);
sut.ActionRequired += args =>
{
if (args is PasswordRequiredEventArgs p && p.Purpose == PasswordRequestPurpose.LocalAdministrator)
{
count++;
p.Success = true;
}
};
var result = sut.Perform();
Assert.AreEqual(5, count);
Assert.AreEqual(OperationResult.Failed, result);
}
[TestMethod]
public void Perform_MustOnlyAllowToEnterSettingsPasswordFiveTimes()
{
var count = 0;
var settings = default(AppSettings);
var url = @"http://www.safeexambrowser.org/whatever.seb";
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, It.IsAny<PasswordParameters>())).Returns(LoadStatus.PasswordNeeded);
var sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext);
sut.ActionRequired += args =>
{
if (args is PasswordRequiredEventArgs p && p.Purpose == PasswordRequestPurpose.Settings)
{
count++;
p.Success = true;
}
};
var result = sut.Perform();
repository.Verify(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, It.IsAny<PasswordParameters>()), Times.Exactly(6));
Assert.AreEqual(5, count);
Assert.AreEqual(OperationResult.Failed, result);
}
[TestMethod]
public void Perform_MustSucceedIfAdminPasswordCorrect()
{
var password = "test";
var currentSettings = new AppSettings { ConfigurationMode = ConfigurationMode.ConfigureClient };
var nextSettings = new AppSettings { ConfigurationMode = ConfigurationMode.ConfigureClient };
var url = @"http://www.safeexambrowser.org/whatever.seb";
currentSettings.Security.AdminPasswordHash = "1234";
nextSession.Settings = nextSettings;
nextSettings.Security.AdminPasswordHash = "9876";
hashAlgorithm.Setup(h => h.GenerateHashFor(It.Is<string>(p => p == password))).Returns(currentSettings.Security.AdminPasswordHash);
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out currentSettings, It.IsAny<PasswordParameters>())).Returns(LoadStatus.Success);
repository.Setup(r => r.TryLoadSettings(It.Is<Uri>(u => u.AbsoluteUri == url), out nextSettings, It.IsAny<PasswordParameters>())).Returns(LoadStatus.Success);
repository.Setup(r => r.ConfigureClientWith(It.IsAny<Uri>(), It.IsAny<PasswordParameters>())).Returns(SaveStatus.Success);
var sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext);
sut.ActionRequired += args =>
{
if (args is PasswordRequiredEventArgs p && p.Purpose == PasswordRequestPurpose.LocalAdministrator)
{
p.Password = password;
p.Success = true;
}
};
var result = sut.Perform();
repository.Verify(r => r.ConfigureClientWith(It.IsAny<Uri>(), It.IsAny<PasswordParameters>()), Times.Once);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Perform_MustNotAuthenticateIfSameAdminPassword()
{
var currentSettings = new AppSettings { ConfigurationMode = ConfigurationMode.ConfigureClient };
var nextSettings = new AppSettings { ConfigurationMode = ConfigurationMode.ConfigureClient };
var url = @"http://www.safeexambrowser.org/whatever.seb";
currentSettings.Security.AdminPasswordHash = "1234";
nextSession.Settings = nextSettings;
nextSettings.Security.AdminPasswordHash = "1234";
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out currentSettings, It.IsAny<PasswordParameters>())).Returns(LoadStatus.Success);
repository.Setup(r => r.TryLoadSettings(It.Is<Uri>(u => u.AbsoluteUri == url), out nextSettings, It.IsAny<PasswordParameters>())).Returns(LoadStatus.Success);
repository.Setup(r => r.ConfigureClientWith(It.IsAny<Uri>(), It.IsAny<PasswordParameters>())).Returns(SaveStatus.Success);
var sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext);
sut.ActionRequired += args =>
{
if (args is PasswordRequiredEventArgs)
{
Assert.Fail();
}
};
var result = sut.Perform();
hashAlgorithm.Verify(h => h.GenerateHashFor(It.IsAny<string>()), Times.Never);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Perform_MustSucceedIfSettingsPasswordCorrect()
{
var password = "test";
var settings = new AppSettings { ConfigurationMode = ConfigurationMode.Exam };
var url = @"http://www.safeexambrowser.org/whatever.seb";
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, It.IsAny<PasswordParameters>())).Returns(LoadStatus.PasswordNeeded);
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, It.Is<PasswordParameters>(p => p.Password == password))).Returns(LoadStatus.Success);
var sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext);
sut.ActionRequired += args =>
{
if (args is PasswordRequiredEventArgs p)
{
p.Password = password;
p.Success = true;
}
};
var result = sut.Perform();
repository.Verify(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, It.Is<PasswordParameters>(p => p.Password == password)), Times.AtLeastOnce);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Perform_MustUseCurrentPasswordIfAvailable()
{
var url = @"http://www.safeexambrowser.org/whatever.seb";
var location = Path.Combine(Path.GetDirectoryName(GetType().Assembly.Location), nameof(Operations), "Testdata", FILE_NAME);
var settings = new AppSettings { ConfigurationMode = ConfigurationMode.Exam };
appConfig.AppDataFilePath = location;
settings.Security.AdminPasswordHash = "1234";
repository
.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, It.IsAny<PasswordParameters>()))
.Returns(LoadStatus.PasswordNeeded);
repository
.Setup(r => r.TryLoadSettings(It.Is<Uri>(u => u.Equals(new Uri(location))), out settings, It.IsAny<PasswordParameters>()))
.Returns(LoadStatus.Success);
repository
.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, It.Is<PasswordParameters>(p => p.IsHash == true && p.Password == settings.Security.AdminPasswordHash)))
.Returns(LoadStatus.Success);
var sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext);
var result = sut.Perform();
repository.Verify(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, It.Is<PasswordParameters>(p => p.Password == settings.Security.AdminPasswordHash)), Times.AtLeastOnce);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Perform_MustAbortAskingForAdminPasswordIfDecidedByUser()
{
var password = "test";
var currentSettings = new AppSettings { ConfigurationMode = ConfigurationMode.ConfigureClient };
var nextSettings = new AppSettings { ConfigurationMode = ConfigurationMode.ConfigureClient };
var url = @"http://www.safeexambrowser.org/whatever.seb";
appConfig.AppDataFilePath = Path.Combine(Path.GetDirectoryName(GetType().Assembly.Location), nameof(Operations), "Testdata", FILE_NAME);
currentSettings.Security.AdminPasswordHash = "1234";
nextSession.Settings = nextSettings;
nextSettings.Security.AdminPasswordHash = "9876";
hashAlgorithm.Setup(h => h.GenerateHashFor(It.Is<string>(p => p == password))).Returns(currentSettings.Security.AdminPasswordHash);
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out currentSettings, It.IsAny<PasswordParameters>())).Returns(LoadStatus.Success);
repository.Setup(r => r.TryLoadSettings(It.Is<Uri>(u => u.AbsoluteUri == url), out nextSettings, It.IsAny<PasswordParameters>())).Returns(LoadStatus.Success);
var sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext);
sut.ActionRequired += args =>
{
if (args is PasswordRequiredEventArgs p && p.Purpose == PasswordRequestPurpose.LocalAdministrator)
{
p.Success = false;
}
};
var result = sut.Perform();
repository.Verify(r => r.ConfigureClientWith(It.IsAny<Uri>(), It.IsAny<PasswordParameters>()), Times.Never);
Assert.AreEqual(OperationResult.Aborted, result);
}
[TestMethod]
public void Perform_MustAbortAskingForSettingsPasswordIfDecidedByUser()
{
var settings = default(AppSettings);
var url = @"http://www.safeexambrowser.org/whatever.seb";
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, It.IsAny<PasswordParameters>())).Returns(LoadStatus.PasswordNeeded);
var sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext);
sut.ActionRequired += args =>
{
if (args is PasswordRequiredEventArgs p)
{
p.Success = false;
}
};
var result = sut.Perform();
Assert.AreEqual(OperationResult.Aborted, result);
}
[TestMethod]
public void Repeat_MustPerformForExamWithCorrectUri()
{
var currentSettings = new AppSettings();
var location = Path.GetDirectoryName(GetType().Assembly.Location);
var resource = new Uri(Path.Combine(location, nameof(Operations), "Testdata", FILE_NAME));
var settings = new AppSettings { ConfigurationMode = ConfigurationMode.Exam };
currentSession.Settings = currentSettings;
sessionContext.ReconfigurationFilePath = resource.LocalPath;
repository.Setup(r => r.TryLoadSettings(It.Is<Uri>(u => u.Equals(resource)), out settings, It.IsAny<PasswordParameters>())).Returns(LoadStatus.Success);
var sut = new ConfigurationOperation(null, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext);
var result = sut.Repeat();
fileSystem.Verify(f => f.Delete(It.Is<string>(s => s == resource.LocalPath)), Times.Once);
repository.Verify(r => r.TryLoadSettings(It.Is<Uri>(u => u.Equals(resource)), out settings, It.IsAny<PasswordParameters>()), Times.AtLeastOnce);
repository.Verify(r => r.ConfigureClientWith(It.Is<Uri>(u => u.Equals(resource)), It.IsAny<PasswordParameters>()), Times.Never);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Repeat_MustPerformForClientConfigurationWithCorrectUri()
{
var currentSettings = new AppSettings();
var location = Path.GetDirectoryName(GetType().Assembly.Location);
var resource = new Uri(Path.Combine(location, nameof(Operations), "Testdata", FILE_NAME));
var settings = new AppSettings { ConfigurationMode = ConfigurationMode.ConfigureClient };
currentSession.Settings = currentSettings;
sessionContext.ReconfigurationFilePath = resource.LocalPath;
repository.Setup(r => r.TryLoadSettings(It.Is<Uri>(u => u.Equals(resource)), out settings, It.IsAny<PasswordParameters>())).Returns(LoadStatus.Success);
repository.Setup(r => r.ConfigureClientWith(It.Is<Uri>(u => u.Equals(resource)), It.IsAny<PasswordParameters>())).Returns(SaveStatus.Success);
var sut = new ConfigurationOperation(null, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext);
var result = sut.Repeat();
fileSystem.Verify(f => f.Delete(It.Is<string>(s => s == resource.LocalPath)), Times.Once);
repository.Verify(r => r.TryLoadSettings(It.Is<Uri>(u => u.Equals(resource)), out settings, It.IsAny<PasswordParameters>()), Times.AtLeastOnce);
repository.Verify(r => r.ConfigureClientWith(It.Is<Uri>(u => u.Equals(resource)), It.IsAny<PasswordParameters>()), Times.Once);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Repeat_MustDeleteTemporaryFileAfterClientConfiguration()
{
var currentSettings = new AppSettings();
var location = Path.GetDirectoryName(GetType().Assembly.Location);
var resource = new Uri(Path.Combine(location, nameof(Operations), "Testdata", FILE_NAME));
var settings = new AppSettings { ConfigurationMode = ConfigurationMode.ConfigureClient };
var delete = 0;
var configure = 0;
var order = 0;
currentSession.Settings = currentSettings;
sessionContext.ReconfigurationFilePath = resource.LocalPath;
fileSystem.Setup(f => f.Delete(It.IsAny<string>())).Callback(() => delete = ++order);
repository.Setup(r => r.TryLoadSettings(It.Is<Uri>(u => u.Equals(resource)), out settings, It.IsAny<PasswordParameters>())).Returns(LoadStatus.Success);
repository.Setup(r => r.ConfigureClientWith(It.Is<Uri>(u => u.Equals(resource)), It.IsAny<PasswordParameters>())).Returns(SaveStatus.Success).Callback(() => configure = ++order);
var sut = new ConfigurationOperation(null, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext);
var result = sut.Repeat();
fileSystem.Verify(f => f.Delete(It.Is<string>(s => s == resource.LocalPath)), Times.Once);
repository.Verify(r => r.TryLoadSettings(It.Is<Uri>(u => u.Equals(resource)), out settings, It.IsAny<PasswordParameters>()), Times.AtLeastOnce);
repository.Verify(r => r.ConfigureClientWith(It.Is<Uri>(u => u.Equals(resource)), It.IsAny<PasswordParameters>()), Times.Once);
Assert.AreEqual(OperationResult.Success, result);
Assert.AreEqual(1, configure);
Assert.AreEqual(2, delete);
}
[TestMethod]
public void Repeat_MustFailWithInvalidUri()
{
var resource = new Uri("file:///C:/does/not/exist.txt");
var settings = default(AppSettings);
sessionContext.ReconfigurationFilePath = null;
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, It.IsAny<PasswordParameters>())).Returns(LoadStatus.Success);
var sut = new ConfigurationOperation(null, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext);
var result = sut.Repeat();
fileSystem.Verify(f => f.Delete(It.Is<string>(s => s == resource.LocalPath)), Times.Never);
repository.Verify(r => r.TryLoadSettings(It.Is<Uri>(u => u.Equals(resource)), out settings, It.IsAny<PasswordParameters>()), Times.Never);
Assert.AreEqual(OperationResult.Failed, result);
sessionContext.ReconfigurationFilePath = resource.LocalPath;
result = sut.Repeat();
fileSystem.Verify(f => f.Delete(It.Is<string>(s => s == resource.LocalPath)), Times.Never);
repository.Verify(r => r.TryLoadSettings(It.Is<Uri>(u => u.Equals(resource)), out settings, It.IsAny<PasswordParameters>()), Times.Never);
Assert.AreEqual(OperationResult.Failed, result);
}
[TestMethod]
public void Repeat_MustAbortForSettingsPasswordIfWishedByUser()
{
var currentSettings = new AppSettings();
var location = Path.GetDirectoryName(GetType().Assembly.Location);
var resource = new Uri(Path.Combine(location, nameof(Operations), "Testdata", FILE_NAME));
var settings = new AppSettings { ConfigurationMode = ConfigurationMode.ConfigureClient };
currentSession.Settings = currentSettings;
sessionContext.ReconfigurationFilePath = resource.LocalPath;
repository.Setup(r => r.TryLoadSettings(It.Is<Uri>(u => u.Equals(resource)), out settings, It.IsAny<PasswordParameters>())).Returns(LoadStatus.PasswordNeeded);
var sut = new ConfigurationOperation(null, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext);
sut.ActionRequired += args =>
{
if (args is PasswordRequiredEventArgs p)
{
p.Success = false;
}
};
var result = sut.Repeat();
fileSystem.Verify(f => f.Delete(It.Is<string>(s => s == resource.LocalPath)), Times.Once);
Assert.AreEqual(OperationResult.Aborted, result);
}
[TestMethod]
public void Revert_MustDoNothing()
{
var sut = new ConfigurationOperation(null, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext);
var result = sut.Revert();
fileSystem.VerifyNoOtherCalls();
hashAlgorithm.VerifyNoOtherCalls();
repository.VerifyNoOtherCalls();
Assert.AreEqual(OperationResult.Success, result);
}
}
}

View File

@@ -0,0 +1,190 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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;
using Moq;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Core.Contracts.OperationModel;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Runtime.Operations;
using SafeExamBrowser.Runtime.Operations.Events;
using SafeExamBrowser.Settings;
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
namespace SafeExamBrowser.Runtime.UnitTests.Operations
{
[TestClass]
public class DisclaimerOperationTests
{
private Mock<ILogger> logger;
private AppSettings settings;
private SessionContext context;
private DisclaimerOperation sut;
[TestInitialize]
public void Initialize()
{
context = new SessionContext();
logger = new Mock<ILogger>();
settings = new AppSettings();
context.Next = new SessionConfiguration();
context.Next.Settings = settings;
sut = new DisclaimerOperation(logger.Object, context);
}
[TestMethod]
public void Perform_MustShowDisclaimerWhenProctoringEnabled()
{
var count = 0;
settings.Proctoring.ScreenProctoring.Enabled = true;
sut.ActionRequired += (args) =>
{
if (args is MessageEventArgs m)
{
count++;
m.Result = MessageBoxResult.Ok;
}
};
var result = sut.Perform();
Assert.AreEqual(1, count);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Perform_MustAbortIfDisclaimerNotConfirmed()
{
var disclaimerShown = false;
settings.Proctoring.ScreenProctoring.Enabled = true;
sut.ActionRequired += (args) =>
{
if (args is MessageEventArgs m)
{
disclaimerShown = true;
m.Result = MessageBoxResult.Cancel;
}
};
var result = sut.Repeat();
Assert.IsTrue(disclaimerShown);
Assert.AreEqual(OperationResult.Aborted, result);
}
[TestMethod]
public void Perform_MustDoNothingIfProctoringNotEnabled()
{
var disclaimerShown = false;
sut.ActionRequired += (args) =>
{
if (args is MessageEventArgs m)
{
disclaimerShown = true;
m.Result = MessageBoxResult.Cancel;
}
};
var result = sut.Perform();
Assert.IsFalse(disclaimerShown);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Repeat_MustShowDisclaimerWhenProctoringEnabled()
{
var count = 0;
settings.Proctoring.ScreenProctoring.Enabled = true;
sut.ActionRequired += (args) =>
{
if (args is MessageEventArgs m)
{
count++;
m.Result = MessageBoxResult.Ok;
}
};
var result = sut.Perform();
Assert.AreEqual(1, count);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Repeat_MustAbortIfDisclaimerNotConfirmed()
{
var disclaimerShown = false;
settings.Proctoring.ScreenProctoring.Enabled = true;
sut.ActionRequired += (args) =>
{
if (args is MessageEventArgs m)
{
disclaimerShown = true;
m.Result = MessageBoxResult.Cancel;
}
};
var result = sut.Repeat();
Assert.IsTrue(disclaimerShown);
Assert.AreEqual(OperationResult.Aborted, result);
}
[TestMethod]
public void Repeat_MustDoNothingIfProctoringNotEnabled()
{
var disclaimerShown = false;
sut.ActionRequired += (args) =>
{
if (args is MessageEventArgs m)
{
disclaimerShown = true;
m.Result = MessageBoxResult.Cancel;
}
};
var result = sut.Repeat();
Assert.IsFalse(disclaimerShown);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Revert_MustDoNothing()
{
var disclaimerShown = false;
sut.ActionRequired += (args) =>
{
if (args is MessageEventArgs m)
{
disclaimerShown = true;
m.Result = MessageBoxResult.Cancel;
}
};
var result = sut.Revert();
Assert.IsFalse(disclaimerShown);
Assert.AreEqual(OperationResult.Success, result);
}
}
}

View File

@@ -0,0 +1,124 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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;
using Moq;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Core.Contracts.OperationModel;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Monitoring.Contracts.Display;
using SafeExamBrowser.Runtime.Operations;
using SafeExamBrowser.Runtime.Operations.Events;
using SafeExamBrowser.Settings;
using SafeExamBrowser.Settings.Monitoring;
namespace SafeExamBrowser.Runtime.UnitTests.Operations
{
[TestClass]
public class DisplayMonitorOperationTests
{
private SessionContext context;
private Mock<IDisplayMonitor> displayMonitor;
private Mock<ILogger> logger;
private AppSettings settings;
private Mock<IText> text;
private DisplayMonitorOperation sut;
[TestInitialize]
public void Initialize()
{
context = new SessionContext();
displayMonitor = new Mock<IDisplayMonitor>();
logger = new Mock<ILogger>();
settings = new AppSettings();
text = new Mock<IText>();
context.Next = new SessionConfiguration();
context.Next.Settings = settings;
sut = new DisplayMonitorOperation(displayMonitor.Object, logger.Object, context, text.Object);
}
[TestMethod]
public void Perform_MustSucceedIfDisplayConfigurationAllowed()
{
displayMonitor.Setup(m => m.ValidateConfiguration(It.IsAny<DisplaySettings>())).Returns(new ValidationResult { IsAllowed = true });
var result = sut.Perform();
displayMonitor.Verify(m => m.ValidateConfiguration(It.IsAny<DisplaySettings>()), Times.Once);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Perform_MustFailIfDisplayConfigurationNotAllowed()
{
var messageShown = false;
displayMonitor.Setup(m => m.ValidateConfiguration(It.IsAny<DisplaySettings>())).Returns(new ValidationResult { IsAllowed = false });
sut.ActionRequired += (args) =>
{
if (args is MessageEventArgs)
{
messageShown = true;
}
};
var result = sut.Perform();
displayMonitor.Verify(m => m.ValidateConfiguration(It.IsAny<DisplaySettings>()), Times.Once);
Assert.IsTrue(messageShown);
Assert.AreEqual(OperationResult.Failed, result);
}
[TestMethod]
public void Repeat_MustSucceedIfDisplayConfigurationAllowed()
{
displayMonitor.Setup(m => m.ValidateConfiguration(It.IsAny<DisplaySettings>())).Returns(new ValidationResult { IsAllowed = true });
var result = sut.Repeat();
displayMonitor.Verify(m => m.ValidateConfiguration(It.IsAny<DisplaySettings>()), Times.Once);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Repeat_MustFailIfDisplayConfigurationNotAllowed()
{
var messageShown = false;
displayMonitor.Setup(m => m.ValidateConfiguration(It.IsAny<DisplaySettings>())).Returns(new ValidationResult { IsAllowed = false });
sut.ActionRequired += (args) =>
{
if (args is MessageEventArgs)
{
messageShown = true;
}
};
var result = sut.Repeat();
displayMonitor.Verify(m => m.ValidateConfiguration(It.IsAny<DisplaySettings>()), Times.Once);
Assert.IsTrue(messageShown);
Assert.AreEqual(OperationResult.Failed, result);
}
[TestMethod]
public void Revert_MustDoNothing()
{
var result = sut.Revert();
displayMonitor.VerifyNoOtherCalls();
Assert.AreEqual(OperationResult.Success, result);
}
}
}

View File

@@ -0,0 +1,487 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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;
using Moq;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Core.Contracts.OperationModel;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Runtime.Operations;
using SafeExamBrowser.Settings;
using SafeExamBrowser.Settings.Security;
using SafeExamBrowser.WindowsApi.Contracts;
namespace SafeExamBrowser.Runtime.UnitTests.Operations
{
[TestClass]
public class KioskModeOperationTests
{
private SessionConfiguration currentSession;
private AppSettings currentSettings;
private Mock<IDesktopFactory> desktopFactory;
private Mock<IDesktopMonitor> desktopMonitor;
private Mock<IExplorerShell> explorerShell;
private Mock<ILogger> logger;
private SessionConfiguration nextSession;
private AppSettings nextSettings;
private Mock<IProcessFactory> processFactory;
private SessionContext sessionContext;
private KioskModeOperation sut;
[TestInitialize]
public void Initialize()
{
currentSession = new SessionConfiguration();
currentSettings = new AppSettings();
desktopFactory = new Mock<IDesktopFactory>();
desktopMonitor = new Mock<IDesktopMonitor>();
explorerShell = new Mock<IExplorerShell>();
logger = new Mock<ILogger>();
nextSession = new SessionConfiguration();
nextSettings = new AppSettings();
processFactory = new Mock<IProcessFactory>();
sessionContext = new SessionContext();
currentSession.Settings = currentSettings;
nextSession.Settings = nextSettings;
sessionContext.Current = currentSession;
sessionContext.Next = nextSession;
sut = new KioskModeOperation(desktopFactory.Object, desktopMonitor.Object, explorerShell.Object, logger.Object, processFactory.Object, sessionContext);
}
[TestMethod]
public void Perform_MustCorrectlyInitializeCreateNewDesktop()
{
var originalDesktop = new Mock<IDesktop>();
var randomDesktop = new Mock<IDesktop>();
var order = 0;
var getCurrrent = 0;
var createNew = 0;
var activate = 0;
var setStartup = 0;
var startMonitor = 0;
nextSettings.Security.KioskMode = KioskMode.CreateNewDesktop;
desktopFactory.Setup(f => f.GetCurrent()).Callback(() => getCurrrent = ++order).Returns(originalDesktop.Object);
desktopFactory.Setup(f => f.CreateRandom()).Callback(() => createNew = ++order).Returns(randomDesktop.Object);
randomDesktop.Setup(d => d.Activate()).Callback(() => activate = ++order);
processFactory.SetupSet(f => f.StartupDesktop = It.IsAny<IDesktop>()).Callback(() => setStartup = ++order);
desktopMonitor.Setup(m => m.Start(It.IsAny<IDesktop>())).Callback(() => startMonitor = ++order);
var result = sut.Perform();
desktopFactory.Verify(f => f.GetCurrent(), Times.Once);
desktopFactory.Verify(f => f.CreateRandom(), Times.Once);
explorerShell.VerifyNoOtherCalls();
randomDesktop.Verify(d => d.Activate(), Times.Once);
processFactory.VerifySet(f => f.StartupDesktop = randomDesktop.Object, Times.Once);
Assert.AreEqual(OperationResult.Success, result);
Assert.AreEqual(1, getCurrrent);
Assert.AreEqual(2, createNew);
Assert.AreEqual(3, activate);
Assert.AreEqual(4, setStartup);
Assert.AreEqual(5, startMonitor);
}
[TestMethod]
public void Perform_MustCorrectlyInitializeDisableExplorerShell()
{
var order = 0;
nextSettings.Security.KioskMode = KioskMode.DisableExplorerShell;
explorerShell.Setup(s => s.HideAllWindows()).Callback(() => Assert.AreEqual(1, ++order));
explorerShell.Setup(s => s.Terminate()).Callback(() => Assert.AreEqual(2, ++order));
var result = sut.Perform();
explorerShell.Verify(s => s.HideAllWindows(), Times.Once);
explorerShell.Verify(s => s.Terminate(), Times.Once);
explorerShell.VerifyNoOtherCalls();
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Repeat_MustCorrectlySwitchFromCreateNewDesktopToDisableExplorerShell()
{
var originalDesktop = new Mock<IDesktop>();
var randomDesktop = new Mock<IDesktop>();
var order = 0;
var activate = 0;
var close = 0;
var hide = 0;
var startupDesktop = 0;
var terminate = 0;
desktopFactory.Setup(f => f.GetCurrent()).Returns(originalDesktop.Object);
desktopFactory.Setup(f => f.CreateRandom()).Returns(randomDesktop.Object);
nextSettings.Security.KioskMode = KioskMode.CreateNewDesktop;
sut.Perform();
desktopFactory.Reset();
explorerShell.Reset();
explorerShell.Setup(s => s.HideAllWindows()).Callback(() => hide = ++order);
explorerShell.Setup(s => s.Terminate()).Callback(() => terminate = ++order);
randomDesktop.Reset();
randomDesktop.Setup(d => d.Close()).Callback(() => close = ++order);
originalDesktop.Reset();
originalDesktop.Setup(d => d.Activate()).Callback(() => activate = ++order);
processFactory.Reset();
processFactory.SetupSet(f => f.StartupDesktop = It.Is<IDesktop>(d => d == originalDesktop.Object)).Callback(() => startupDesktop = ++order);
nextSettings.Security.KioskMode = KioskMode.DisableExplorerShell;
var result = sut.Repeat();
desktopFactory.VerifyNoOtherCalls();
explorerShell.Verify(s => s.HideAllWindows(), Times.Once);
explorerShell.Verify(s => s.Terminate(), Times.Once);
explorerShell.VerifyNoOtherCalls();
randomDesktop.Verify(d => d.Close(), Times.Once);
originalDesktop.Verify(d => d.Activate(), Times.Once);
processFactory.VerifySet(f => f.StartupDesktop = It.Is<IDesktop>(d => d == originalDesktop.Object), Times.Once);
Assert.AreEqual(OperationResult.Success, result);
Assert.AreEqual(1, activate);
Assert.AreEqual(2, startupDesktop);
Assert.AreEqual(3, close);
Assert.AreEqual(4, hide);
Assert.AreEqual(5, terminate);
}
[TestMethod]
public void Repeat_MustCorrectlySwitchFromCreateNewDesktopToNone()
{
var originalDesktop = new Mock<IDesktop>();
var randomDesktop = new Mock<IDesktop>();
var order = 0;
var activate = 0;
var close = 0;
var startupDesktop = 0;
desktopFactory.Setup(f => f.GetCurrent()).Returns(originalDesktop.Object);
desktopFactory.Setup(f => f.CreateRandom()).Returns(randomDesktop.Object);
nextSettings.Security.KioskMode = KioskMode.CreateNewDesktop;
sut.Perform();
desktopFactory.Reset();
explorerShell.Reset();
randomDesktop.Reset();
randomDesktop.Setup(d => d.Close()).Callback(() => close = ++order);
originalDesktop.Reset();
originalDesktop.Setup(d => d.Activate()).Callback(() => activate = ++order);
processFactory.Reset();
processFactory.SetupSet(f => f.StartupDesktop = It.Is<IDesktop>(d => d == originalDesktop.Object)).Callback(() => startupDesktop = ++order);
nextSettings.Security.KioskMode = KioskMode.None;
var result = sut.Repeat();
desktopFactory.VerifyNoOtherCalls();
explorerShell.VerifyNoOtherCalls();
randomDesktop.Verify(d => d.Close(), Times.Once);
originalDesktop.Verify(d => d.Activate(), Times.Once);
processFactory.VerifySet(f => f.StartupDesktop = It.Is<IDesktop>(d => d == originalDesktop.Object), Times.Once);
Assert.AreEqual(OperationResult.Success, result);
Assert.AreEqual(1, activate);
Assert.AreEqual(2, startupDesktop);
Assert.AreEqual(3, close);
}
[TestMethod]
public void Repeat_MustCorrectlySwitchFromDisableExplorerShellToCreateNewDesktop()
{
var originalDesktop = new Mock<IDesktop>();
var randomDesktop = new Mock<IDesktop>();
var order = 0;
var activate = 0;
var current = 0;
var restore = 0;
var start = 0;
var startupDesktop = 0;
nextSettings.Security.KioskMode = KioskMode.DisableExplorerShell;
sut.Perform();
desktopFactory.Reset();
desktopFactory.Setup(f => f.GetCurrent()).Returns(originalDesktop.Object).Callback(() => current = ++order);
desktopFactory.Setup(f => f.CreateRandom()).Returns(randomDesktop.Object);
explorerShell.Reset();
explorerShell.Setup(s => s.RestoreAllWindows()).Callback(() => restore = ++order);
explorerShell.Setup(s => s.Start()).Callback(() => start = ++order);
randomDesktop.Reset();
randomDesktop.Setup(d => d.Activate()).Callback(() => activate = ++order);
originalDesktop.Reset();
processFactory.Reset();
processFactory.SetupSet(f => f.StartupDesktop = It.Is<IDesktop>(d => d == randomDesktop.Object)).Callback(() => startupDesktop = ++order);
nextSettings.Security.KioskMode = KioskMode.CreateNewDesktop;
var result = sut.Repeat();
desktopFactory.Verify(f => f.GetCurrent(), Times.Once);
desktopFactory.Verify(f => f.CreateRandom(), Times.Once);
explorerShell.Verify(s => s.RestoreAllWindows(), Times.Once);
explorerShell.Verify(s => s.Start(), Times.Once);
explorerShell.VerifyNoOtherCalls();
randomDesktop.Verify(d => d.Activate(), Times.Once);
originalDesktop.VerifyNoOtherCalls();
processFactory.VerifySet(f => f.StartupDesktop = It.Is<IDesktop>(d => d == randomDesktop.Object), Times.Once);
Assert.AreEqual(OperationResult.Success, result);
Assert.AreEqual(1, start);
Assert.AreEqual(2, restore);
Assert.AreEqual(3, current);
Assert.AreEqual(4, activate);
Assert.AreEqual(5, startupDesktop);
}
[TestMethod]
public void Repeat_MustCorrectlySwitchFromDisableExplorerShellToNone()
{
var order = 0;
var restore = 0;
var start = 0;
nextSettings.Security.KioskMode = KioskMode.DisableExplorerShell;
sut.Perform();
explorerShell.Reset();
explorerShell.Setup(s => s.RestoreAllWindows()).Callback(() => restore = ++order);
explorerShell.Setup(s => s.Start()).Callback(() => start = ++order);
processFactory.Reset();
nextSettings.Security.KioskMode = KioskMode.None;
var result = sut.Repeat();
desktopFactory.VerifyNoOtherCalls();
explorerShell.Verify(s => s.RestoreAllWindows(), Times.Once);
explorerShell.Verify(s => s.Start(), Times.Once);
explorerShell.VerifyNoOtherCalls();
processFactory.VerifySet(f => f.StartupDesktop = It.IsAny<IDesktop>(), Times.Never);
Assert.AreEqual(OperationResult.Success, result);
Assert.AreEqual(1, start);
Assert.AreEqual(2, restore);
}
[TestMethod]
public void Repeat_MustCorrectlySwitchFromNoneToCreateNewDesktop()
{
var originalDesktop = new Mock<IDesktop>();
var randomDesktop = new Mock<IDesktop>();
var order = 0;
var activate = 0;
var current = 0;
var startup = 0;
nextSettings.Security.KioskMode = KioskMode.None;
sut.Perform();
desktopFactory.Reset();
desktopFactory.Setup(f => f.GetCurrent()).Returns(originalDesktop.Object).Callback(() => current = ++order);
desktopFactory.Setup(f => f.CreateRandom()).Returns(randomDesktop.Object);
explorerShell.Reset();
randomDesktop.Reset();
randomDesktop.Setup(d => d.Activate()).Callback(() => activate = ++order);
originalDesktop.Reset();
processFactory.Reset();
processFactory.SetupSet(f => f.StartupDesktop = It.Is<IDesktop>(d => d == randomDesktop.Object)).Callback(() => startup = ++order);
nextSettings.Security.KioskMode = KioskMode.CreateNewDesktop;
var result = sut.Repeat();
desktopFactory.Verify(f => f.GetCurrent(), Times.Once);
desktopFactory.Verify(f => f.CreateRandom(), Times.Once);
explorerShell.VerifyNoOtherCalls();
randomDesktop.Verify(d => d.Activate(), Times.Once);
originalDesktop.VerifyNoOtherCalls();
processFactory.VerifySet(f => f.StartupDesktop = It.Is<IDesktop>(d => d == randomDesktop.Object), Times.Once);
Assert.AreEqual(OperationResult.Success, result);
Assert.AreEqual(1, current);
Assert.AreEqual(2, activate);
Assert.AreEqual(3, startup);
}
[TestMethod]
public void Repeat_MustCorrectlySwitchFromNoneToDisableExplorerShell()
{
var order = 0;
var hide = 0;
var terminate = 0;
nextSettings.Security.KioskMode = KioskMode.None;
sut.Perform();
desktopFactory.Reset();
explorerShell.Reset();
explorerShell.Setup(s => s.HideAllWindows()).Callback(() => hide = ++order);
explorerShell.Setup(s => s.Terminate()).Callback(() => terminate = ++order);
processFactory.Reset();
nextSettings.Security.KioskMode = KioskMode.DisableExplorerShell;
var result = sut.Repeat();
desktopFactory.VerifyNoOtherCalls();
explorerShell.Verify(s => s.HideAllWindows(), Times.Once);
explorerShell.Verify(s => s.Terminate(), Times.Once);
processFactory.VerifySet(f => f.StartupDesktop = It.IsAny<IDesktop>(), Times.Never);
Assert.AreEqual(OperationResult.Success, result);
Assert.AreEqual(1, hide);
Assert.AreEqual(2, terminate);
}
[TestMethod]
public void Repeat_MustNotReinitializeCreateNewDesktopIfAlreadyActive()
{
var originalDesktop = new Mock<IDesktop>();
var randomDesktop = new Mock<IDesktop>();
var success = true;
currentSettings.Security.KioskMode = KioskMode.CreateNewDesktop;
nextSettings.Security.KioskMode = KioskMode.CreateNewDesktop;
desktopFactory.Setup(f => f.GetCurrent()).Returns(originalDesktop.Object);
desktopFactory.Setup(f => f.CreateRandom()).Returns(randomDesktop.Object);
success &= sut.Perform() == OperationResult.Success;
success &= sut.Repeat() == OperationResult.Success;
success &= sut.Repeat() == OperationResult.Success;
success &= sut.Repeat() == OperationResult.Success;
success &= sut.Repeat() == OperationResult.Success;
success &= sut.Repeat() == OperationResult.Success;
Assert.IsTrue(success);
desktopFactory.Verify(f => f.GetCurrent(), Times.Once);
desktopFactory.Verify(f => f.CreateRandom(), Times.Once);
desktopMonitor.Verify(m => m.Start(It.IsAny<IDesktop>()), Times.Once);
desktopMonitor.Verify(m => m.Stop(), Times.Never);
explorerShell.VerifyNoOtherCalls();
randomDesktop.Verify(d => d.Activate(), Times.Once);
randomDesktop.Verify(d => d.Close(), Times.Never);
processFactory.VerifySet(f => f.StartupDesktop = randomDesktop.Object, Times.Once);
}
[TestMethod]
public void Repeat_MustNotReinitializeDisableExplorerShellIfAlreadyActive()
{
var success = true;
currentSettings.Security.KioskMode = KioskMode.DisableExplorerShell;
nextSettings.Security.KioskMode = KioskMode.DisableExplorerShell;
success &= sut.Perform() == OperationResult.Success;
success &= sut.Repeat() == OperationResult.Success;
success &= sut.Repeat() == OperationResult.Success;
success &= sut.Repeat() == OperationResult.Success;
success &= sut.Repeat() == OperationResult.Success;
success &= sut.Repeat() == OperationResult.Success;
Assert.IsTrue(success);
explorerShell.Verify(s => s.Start(), Times.Never);
explorerShell.Verify(s => s.Terminate(), Times.Once);
explorerShell.Verify(s => s.HideAllWindows(), Times.Once);
explorerShell.Verify(s => s.RestoreAllWindows(), Times.Never);
}
[TestMethod]
public void Revert_MustCorrectlyRevertCreateNewDesktop()
{
var originalDesktop = new Mock<IDesktop>();
var randomDesktop = new Mock<IDesktop>();
var order = 0;
var activate = 0;
var setStartup = 0;
var stopMonitor = 0;
var close = 0;
currentSettings.Security.KioskMode = KioskMode.CreateNewDesktop;
nextSettings.Security.KioskMode = KioskMode.CreateNewDesktop;
desktopFactory.Setup(f => f.GetCurrent()).Returns(originalDesktop.Object);
desktopFactory.Setup(f => f.CreateRandom()).Returns(randomDesktop.Object);
var performResult = sut.Perform();
Assert.AreEqual(OperationResult.Success, performResult);
desktopFactory.Reset();
desktopMonitor.Setup(m => m.Stop()).Callback(() => stopMonitor = ++order);
explorerShell.Reset();
originalDesktop.Reset();
originalDesktop.Setup(d => d.Activate()).Callback(() => activate = ++order);
processFactory.SetupSet(f => f.StartupDesktop = It.Is<IDesktop>(d => d == originalDesktop.Object)).Callback(() => setStartup = ++order);
randomDesktop.Reset();
randomDesktop.Setup(d => d.Close()).Callback(() => close = ++order);
var revertResult = sut.Revert();
desktopFactory.VerifyNoOtherCalls();
explorerShell.VerifyNoOtherCalls();
originalDesktop.Verify(d => d.Activate(), Times.Once);
processFactory.VerifySet(f => f.StartupDesktop = originalDesktop.Object, Times.Once);
randomDesktop.Verify(d => d.Close(), Times.Once);
Assert.AreEqual(OperationResult.Success, performResult);
Assert.AreEqual(OperationResult.Success, revertResult);
Assert.AreEqual(1, stopMonitor);
Assert.AreEqual(2, activate);
Assert.AreEqual(3, setStartup);
Assert.AreEqual(4, close);
}
[TestMethod]
public void Revert_MustCorrectlyRevertDisableExplorerShell()
{
var order = 0;
currentSettings.Security.KioskMode = KioskMode.DisableExplorerShell;
nextSettings.Security.KioskMode = KioskMode.DisableExplorerShell;
explorerShell.Setup(s => s.Start()).Callback(() => Assert.AreEqual(1, ++order));
explorerShell.Setup(s => s.RestoreAllWindows()).Callback(() => Assert.AreEqual(2, ++order));
var performResult = sut.Perform();
var revertResult = sut.Revert();
explorerShell.Verify(s => s.Start(), Times.Once);
explorerShell.Verify(s => s.RestoreAllWindows(), Times.Once);
Assert.AreEqual(OperationResult.Success, performResult);
Assert.AreEqual(OperationResult.Success, revertResult);
}
[TestMethod]
public void MustDoNothingWithoutKioskMode()
{
nextSettings.Security.KioskMode = KioskMode.None;
Assert.AreEqual(OperationResult.Success, sut.Perform());
Assert.AreEqual(OperationResult.Success, sut.Repeat());
Assert.AreEqual(OperationResult.Success, sut.Repeat());
Assert.AreEqual(OperationResult.Success, sut.Repeat());
Assert.AreEqual(OperationResult.Success, sut.Repeat());
Assert.AreEqual(OperationResult.Success, sut.Revert());
desktopFactory.VerifyNoOtherCalls();
explorerShell.VerifyNoOtherCalls();
processFactory.VerifyNoOtherCalls();
}
}
}

View File

@@ -0,0 +1,205 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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;
using Moq;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Core.Contracts.OperationModel;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Monitoring.Contracts;
using SafeExamBrowser.Runtime.Operations;
using SafeExamBrowser.Runtime.Operations.Events;
using SafeExamBrowser.Settings;
namespace SafeExamBrowser.Runtime.UnitTests.Operations
{
[TestClass]
public class RemoteSessionOperationTests
{
private SessionContext context;
private Mock<IRemoteSessionDetector> detector;
private Mock<ILogger> logger;
private AppSettings settings;
private RemoteSessionOperation sut;
[TestInitialize]
public void Initialize()
{
context = new SessionContext();
detector = new Mock<IRemoteSessionDetector>();
logger = new Mock<ILogger>();
settings = new AppSettings();
context.Next = new SessionConfiguration();
context.Next.Settings = settings;
sut = new RemoteSessionOperation(detector.Object, logger.Object, context);
}
[TestMethod]
public void Perform_MustAbortIfRemoteSessionNotAllowed()
{
var messageShown = false;
detector.Setup(d => d.IsRemoteSession()).Returns(true);
settings.Service.DisableRemoteConnections = true;
sut.ActionRequired += (args) =>
{
if (args is MessageEventArgs)
{
messageShown = true;
}
};
var result = sut.Perform();
detector.Verify(d => d.IsRemoteSession(), Times.Once);
Assert.IsTrue(messageShown);
Assert.AreEqual(OperationResult.Aborted, result);
}
[TestMethod]
public void Perform_MustSucceedIfRemoteSessionAllowed()
{
var messageShown = false;
detector.Setup(d => d.IsRemoteSession()).Returns(true);
settings.Service.DisableRemoteConnections = false;
sut.ActionRequired += (args) =>
{
if (args is MessageEventArgs)
{
messageShown = true;
}
};
var result = sut.Perform();
detector.Verify(d => d.IsRemoteSession(), Times.AtMostOnce);
Assert.IsFalse(messageShown);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Perform_MustSucceedIfNoRemoteSession()
{
var messageShown = false;
detector.Setup(d => d.IsRemoteSession()).Returns(false);
settings.Service.DisableRemoteConnections = true;
sut.ActionRequired += (args) =>
{
if (args is MessageEventArgs)
{
messageShown = true;
}
};
var result = sut.Perform();
detector.Verify(d => d.IsRemoteSession(), Times.Once);
Assert.IsFalse(messageShown);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Repeat_MustAbortIfRemoteSessionNotAllowed()
{
var messageShown = false;
detector.Setup(d => d.IsRemoteSession()).Returns(true);
settings.Service.DisableRemoteConnections = true;
sut.ActionRequired += (args) =>
{
if (args is MessageEventArgs)
{
messageShown = true;
}
};
var result = sut.Repeat();
detector.Verify(d => d.IsRemoteSession(), Times.Once);
Assert.IsTrue(messageShown);
Assert.AreEqual(OperationResult.Aborted, result);
}
[TestMethod]
public void Repeat_MustSucceedIfRemoteSessionAllowed()
{
var messageShown = false;
detector.Setup(d => d.IsRemoteSession()).Returns(true);
settings.Service.DisableRemoteConnections = false;
sut.ActionRequired += (args) =>
{
if (args is MessageEventArgs)
{
messageShown = true;
}
};
var result = sut.Repeat();
detector.Verify(d => d.IsRemoteSession(), Times.AtMostOnce);
Assert.IsFalse(messageShown);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Repeat_MustSucceedIfNoRemoteSession()
{
var messageShown = false;
detector.Setup(d => d.IsRemoteSession()).Returns(false);
settings.Service.DisableRemoteConnections = true;
sut.ActionRequired += (args) =>
{
if (args is MessageEventArgs)
{
messageShown = true;
}
};
var result = sut.Repeat();
detector.Verify(d => d.IsRemoteSession(), Times.Once);
Assert.IsFalse(messageShown);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Revert_MustDoNoting()
{
var messageShown = false;
detector.Setup(d => d.IsRemoteSession()).Returns(true);
settings.Service.DisableRemoteConnections = false;
sut.ActionRequired += (args) =>
{
if (args is MessageEventArgs)
{
messageShown = true;
}
};
var result = sut.Revert();
detector.VerifyNoOtherCalls();
Assert.IsFalse(messageShown);
Assert.AreEqual(OperationResult.Success, result);
}
}
}

View File

@@ -0,0 +1,660 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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.Configuration.Contracts;
using SafeExamBrowser.Configuration.Contracts.Cryptography;
using SafeExamBrowser.Core.Contracts.OperationModel;
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Runtime.Operations;
using SafeExamBrowser.Runtime.Operations.Events;
using SafeExamBrowser.Server.Contracts;
using SafeExamBrowser.Server.Contracts.Data;
using SafeExamBrowser.Settings;
using SafeExamBrowser.Settings.Server;
using SafeExamBrowser.SystemComponents.Contracts;
namespace SafeExamBrowser.Runtime.UnitTests.Operations
{
[TestClass]
public class ServerOperationTests
{
private SessionContext context;
private Mock<IFileSystem> fileSystem;
private Mock<ILogger> logger;
private Mock<IServerProxy> server;
private Mock<IConfigurationRepository> configuration;
private ServerOperation sut;
[TestInitialize]
public void Initialize()
{
configuration = new Mock<IConfigurationRepository>();
context = new SessionContext();
fileSystem = new Mock<IFileSystem>();
logger = new Mock<ILogger>();
server = new Mock<IServerProxy>();
context.Current = new SessionConfiguration();
context.Current.AppConfig = new AppConfig();
context.Current.Settings = new AppSettings();
context.Next = new SessionConfiguration();
context.Next.AppConfig = new AppConfig();
context.Next.Settings = new AppSettings();
sut = new ServerOperation(new string[0], configuration.Object, fileSystem.Object, logger.Object, context, server.Object);
}
[TestMethod]
public void Perform_MustCorrectlyInitializeServerSession()
{
var connect = 0;
var connection = new ConnectionInfo { Api = "some API", ConnectionToken = "some token", Oauth2Token = "some OAuth2 token" };
var counter = 0;
var delete = 0;
var exam = new Exam { Id = "some id", LmsName = "some LMS", Name = "some name", Url = "some URL" };
var examSelection = 0;
var examSettings = new AppSettings();
var getConfiguration = 0;
var getConnection = 0;
var getExams = 0;
var initialize = 0;
var initialSettings = context.Next.Settings;
var serverSettings = context.Next.Settings.Server;
configuration
.Setup(c => c.TryLoadSettings(It.IsAny<Uri>(), out examSettings, It.IsAny<PasswordParameters>()))
.Returns(LoadStatus.Success);
context.Next.Settings.SessionMode = SessionMode.Server;
fileSystem.Setup(f => f.Delete(It.IsAny<string>())).Callback(() => delete = ++counter);
server.Setup(s => s.Connect()).Returns(new ServerResponse(true)).Callback(() => connect = ++counter);
server.Setup(s => s.Initialize(It.IsAny<ServerSettings>())).Callback(() => initialize = ++counter);
server.Setup(s => s.GetConnectionInfo()).Returns(connection).Callback(() => getConnection = ++counter);
server.Setup(s => s.SendSelectedExam(It.IsAny<Exam>())).Returns(new ServerResponse<string>(true, default));
server
.Setup(s => s.GetAvailableExams(It.IsAny<string>()))
.Returns(new ServerResponse<IEnumerable<Exam>>(true, default(IEnumerable<Exam>)))
.Callback(() => getExams = ++counter);
server
.Setup(s => s.GetConfigurationFor(It.IsAny<Exam>()))
.Returns(new ServerResponse<Uri>(true, new Uri("file:///configuration.seb")))
.Callback(() => getConfiguration = ++counter);
sut.ActionRequired += (args) =>
{
if (args is ExamSelectionEventArgs e)
{
e.Success = true;
e.SelectedExam = exam;
examSelection = ++counter;
}
};
var result = sut.Perform();
fileSystem.Verify(f => f.Delete(It.IsAny<string>()), Times.Once);
server.Verify(s => s.Connect(), Times.Once);
server.Verify(s => s.Initialize(It.IsAny<ServerSettings>()), Times.Once);
server.Verify(s => s.GetAvailableExams(It.IsAny<string>()), Times.Once);
server.Verify(s => s.GetConfigurationFor(It.Is<Exam>(e => e == exam)), Times.Once);
server.Verify(s => s.GetConnectionInfo(), Times.Once);
server.Verify(s => s.SendSelectedExam(It.Is<Exam>(e => e == exam)), Times.Once);
Assert.AreEqual(1, initialize);
Assert.AreEqual(2, connect);
Assert.AreEqual(3, getExams);
Assert.AreEqual(4, examSelection);
Assert.AreEqual(5, getConfiguration);
Assert.AreEqual(6, getConnection);
Assert.AreEqual(7, delete);
Assert.AreEqual(connection.Api, context.Next.AppConfig.ServerApi);
Assert.AreEqual(connection.ConnectionToken, context.Next.AppConfig.ServerConnectionToken);
Assert.AreEqual(connection.Oauth2Token, context.Next.AppConfig.ServerOauth2Token);
Assert.AreEqual(exam.Id, context.Next.AppConfig.ServerExamId);
Assert.AreEqual(exam.Url, context.Next.Settings.Browser.StartUrl);
Assert.AreSame(examSettings, context.Next.Settings);
Assert.AreSame(serverSettings, context.Next.Settings.Server);
Assert.AreNotSame(initialSettings, context.Next.Settings);
Assert.AreEqual(OperationResult.Success, result);
Assert.AreEqual(SessionMode.Server, context.Next.Settings.SessionMode);
}
[TestMethod]
public void Perform_MustFailIfSettingsCouldNotBeLoaded()
{
var connection = new ConnectionInfo { Api = "some API", ConnectionToken = "some token", Oauth2Token = "some OAuth2 token" };
var exam = new Exam { Id = "some id", LmsName = "some LMS", Name = "some name", Url = "some URL" };
var examSettings = new AppSettings();
var initialSettings = context.Next.Settings;
configuration.Setup(c => c.TryLoadSettings(It.IsAny<Uri>(), out examSettings, It.IsAny<PasswordParameters>())).Returns(LoadStatus.UnexpectedError);
context.Next.Settings.SessionMode = SessionMode.Server;
server.Setup(s => s.Connect()).Returns(new ServerResponse(true));
server.Setup(s => s.Initialize(It.IsAny<ServerSettings>()));
server.Setup(s => s.GetConnectionInfo()).Returns(connection);
server.Setup(s => s.GetAvailableExams(It.IsAny<string>())).Returns(new ServerResponse<IEnumerable<Exam>>(true, default(IEnumerable<Exam>)));
server.Setup(s => s.GetConfigurationFor(It.IsAny<Exam>())).Returns(new ServerResponse<Uri>(true, new Uri("file:///configuration.seb")));
sut.ActionRequired += (args) =>
{
if (args is ExamSelectionEventArgs e)
{
e.Success = true;
e.SelectedExam = exam;
}
};
var result = sut.Perform();
fileSystem.Verify(f => f.Delete(It.IsAny<string>()), Times.Once);
server.Verify(s => s.Connect(), Times.Once);
server.Verify(s => s.Initialize(It.IsAny<ServerSettings>()), Times.Once);
server.Verify(s => s.GetAvailableExams(It.IsAny<string>()), Times.Once);
server.Verify(s => s.GetConfigurationFor(It.Is<Exam>(e => e == exam)), Times.Once);
server.Verify(s => s.GetConnectionInfo(), Times.Once);
Assert.AreNotEqual(connection.Api, context.Next.AppConfig.ServerApi);
Assert.AreNotEqual(connection.ConnectionToken, context.Next.AppConfig.ServerConnectionToken);
Assert.AreNotEqual(connection.Oauth2Token, context.Next.AppConfig.ServerOauth2Token);
Assert.AreNotEqual(exam.Id, context.Next.AppConfig.ServerExamId);
Assert.AreNotEqual(exam.Url, context.Next.Settings.Browser.StartUrl);
Assert.AreNotSame(examSettings, context.Next.Settings);
Assert.AreSame(initialSettings, context.Next.Settings);
Assert.AreEqual(OperationResult.Failed, result);
Assert.AreEqual(SessionMode.Server, context.Next.Settings.SessionMode);
}
[TestMethod]
public void Perform_MustCorrectlyAbort()
{
var connection = new ConnectionInfo { Api = "some API", ConnectionToken = "some token", Oauth2Token = "some OAuth2 token" };
var exam = new Exam { Id = "some id", LmsName = "some LMS", Name = "some name", Url = "some URL" };
var examSettings = new AppSettings();
var messageShown = false;
configuration.Setup(c => c.TryLoadSettings(It.IsAny<Uri>(), out examSettings, It.IsAny<PasswordParameters>())).Returns(LoadStatus.Success);
context.Next.Settings.SessionMode = SessionMode.Server;
server.Setup(s => s.Connect()).Returns(new ServerResponse(true));
server.Setup(s => s.Initialize(It.IsAny<ServerSettings>()));
server.Setup(s => s.GetConnectionInfo()).Returns(connection);
server.Setup(s => s.GetAvailableExams(It.IsAny<string>())).Returns(new ServerResponse<IEnumerable<Exam>>(true, default(IEnumerable<Exam>)));
server.Setup(s => s.GetConfigurationFor(It.IsAny<Exam>())).Returns(new ServerResponse<Uri>(false, default(Uri)));
sut.ActionRequired += (args) =>
{
if (args is ExamSelectionEventArgs e)
{
e.Success = true;
e.SelectedExam = exam;
}
if (args is ServerFailureEventArgs s)
{
s.Abort = true;
messageShown = true;
}
};
var result = sut.Perform();
fileSystem.Verify(f => f.Delete(It.IsAny<string>()), Times.Never);
server.Verify(s => s.Connect(), Times.Once);
server.Verify(s => s.Initialize(It.IsAny<ServerSettings>()), Times.Once);
server.Verify(s => s.GetAvailableExams(It.IsAny<string>()), Times.Once);
server.Verify(s => s.GetConfigurationFor(It.Is<Exam>(e => e == exam)), Times.Once);
server.Verify(s => s.GetConnectionInfo(), Times.Never);
Assert.IsTrue(messageShown);
Assert.AreEqual(OperationResult.Aborted, result);
}
[TestMethod]
public void Perform_MustCorrectlyFallback()
{
var connection = new ConnectionInfo { Api = "some API", ConnectionToken = "some token", Oauth2Token = "some OAuth2 token" };
var exam = new Exam { Id = "some id", LmsName = "some LMS", Name = "some name", Url = "some URL" };
var examSettings = new AppSettings();
var messageShown = false;
configuration.Setup(c => c.TryLoadSettings(It.IsAny<Uri>(), out examSettings, It.IsAny<PasswordParameters>())).Returns(LoadStatus.Success);
context.Next.Settings.SessionMode = SessionMode.Server;
server.Setup(s => s.Connect()).Returns(new ServerResponse(true));
server.Setup(s => s.Initialize(It.IsAny<ServerSettings>()));
server.Setup(s => s.GetConnectionInfo()).Returns(connection);
server.Setup(s => s.GetAvailableExams(It.IsAny<string>())).Returns(new ServerResponse<IEnumerable<Exam>>(true, default(IEnumerable<Exam>)));
server.Setup(s => s.GetConfigurationFor(It.IsAny<Exam>())).Returns(new ServerResponse<Uri>(false, default(Uri)));
sut.ActionRequired += (args) =>
{
if (args is ExamSelectionEventArgs e)
{
e.Success = true;
e.SelectedExam = exam;
}
if (args is ServerFailureEventArgs s)
{
s.Fallback = true;
messageShown = true;
}
};
var result = sut.Perform();
fileSystem.Verify(f => f.Delete(It.IsAny<string>()), Times.Never);
server.Verify(s => s.Connect(), Times.Once);
server.Verify(s => s.Initialize(It.IsAny<ServerSettings>()), Times.Once);
server.Verify(s => s.GetAvailableExams(It.IsAny<string>()), Times.Once);
server.Verify(s => s.GetConfigurationFor(It.Is<Exam>(e => e == exam)), Times.Once);
server.Verify(s => s.GetConnectionInfo(), Times.Never);
Assert.IsTrue(messageShown);
Assert.AreEqual(SessionMode.Normal, context.Next.Settings.SessionMode);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Perform_MustAutomaticallySelectExam()
{
var connection = new ConnectionInfo { Api = "some API", ConnectionToken = "some token", Oauth2Token = "some OAuth2 token" };
var exam = new Exam { Id = "some id", LmsName = "some LMS", Name = "some name", Url = "some URL" };
var examSettings = new AppSettings();
var serverSettings = context.Next.Settings.Server;
configuration.Setup(c => c.TryLoadSettings(It.IsAny<Uri>(), out examSettings, It.IsAny<PasswordParameters>())).Returns(LoadStatus.Success);
context.Next.Settings.SessionMode = SessionMode.Server;
context.Next.Settings.Server.ExamId = "some id";
fileSystem.Setup(f => f.Delete(It.IsAny<string>()));
server.Setup(s => s.Connect()).Returns(new ServerResponse(true));
server.Setup(s => s.Initialize(It.IsAny<ServerSettings>()));
server.Setup(s => s.GetConnectionInfo()).Returns(connection);
server.Setup(s => s.GetAvailableExams(It.IsAny<string>())).Returns(new ServerResponse<IEnumerable<Exam>>(true, new[] { exam }));
server.Setup(s => s.GetConfigurationFor(It.IsAny<Exam>())).Returns(new ServerResponse<Uri>(true, new Uri("file:///configuration.seb")));
server.Setup(s => s.SendSelectedExam(It.IsAny<Exam>())).Returns(new ServerResponse<string>(true, default));
sut.ActionRequired += (args) => Assert.Fail();
var result = sut.Perform();
fileSystem.Verify(f => f.Delete(It.IsAny<string>()), Times.Once);
server.Verify(s => s.Connect(), Times.Once);
server.Verify(s => s.Initialize(It.IsAny<ServerSettings>()), Times.Once);
server.Verify(s => s.GetAvailableExams(It.IsAny<string>()), Times.Once);
server.Verify(s => s.GetConfigurationFor(It.Is<Exam>(e => e == exam)), Times.Once);
server.Verify(s => s.GetConnectionInfo(), Times.Once);
server.Verify(s => s.SendSelectedExam(It.Is<Exam>(e => e == exam)), Times.Once);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Perform_MustDoNothingIfNormalSession()
{
var initialSettings = context.Next.Settings;
context.Next.Settings.SessionMode = SessionMode.Normal;
sut.ActionRequired += (_) => Assert.Fail();
var result = sut.Perform();
configuration.VerifyNoOtherCalls();
fileSystem.VerifyNoOtherCalls();
server.VerifyNoOtherCalls();
Assert.AreSame(initialSettings, context.Next.Settings);
Assert.AreEqual(SessionMode.Normal, context.Next.Settings.SessionMode);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Repeat_MustCorrectlyInitializeServerSession()
{
var connect = 0;
var connection = new ConnectionInfo { Api = "some API", ConnectionToken = "some token", Oauth2Token = "some OAuth2 token" };
var counter = 0;
var delete = 0;
var exam = new Exam { Id = "some id", LmsName = "some LMS", Name = "some name", Url = "some URL" };
var examSelection = 0;
var examSettings = new AppSettings();
var getConfiguration = 0;
var getConnection = 0;
var getExams = 0;
var initialize = 0;
var initialSettings = context.Next.Settings;
var serverSettings = context.Next.Settings.Server;
configuration
.Setup(c => c.TryLoadSettings(It.IsAny<Uri>(), out examSettings, It.IsAny<PasswordParameters>()))
.Returns(LoadStatus.Success);
context.Next.Settings.SessionMode = SessionMode.Server;
fileSystem.Setup(f => f.Delete(It.IsAny<string>())).Callback(() => delete = ++counter);
server.Setup(s => s.Connect()).Returns(new ServerResponse(true)).Callback(() => connect = ++counter);
server.Setup(s => s.Initialize(It.IsAny<ServerSettings>())).Callback(() => initialize = ++counter);
server.Setup(s => s.GetConnectionInfo()).Returns(connection).Callback(() => getConnection = ++counter);
server.Setup(s => s.SendSelectedExam(It.IsAny<Exam>())).Returns(new ServerResponse<string>(true, default));
server
.Setup(s => s.GetAvailableExams(It.IsAny<string>()))
.Returns(new ServerResponse<IEnumerable<Exam>>(true, default(IEnumerable<Exam>)))
.Callback(() => getExams = ++counter);
server
.Setup(s => s.GetConfigurationFor(It.IsAny<Exam>()))
.Returns(new ServerResponse<Uri>(true, new Uri("file:///configuration.seb")))
.Callback(() => getConfiguration = ++counter);
sut.ActionRequired += (args) =>
{
if (args is ExamSelectionEventArgs e)
{
e.Success = true;
e.SelectedExam = exam;
examSelection = ++counter;
}
};
var result = sut.Repeat();
fileSystem.Verify(f => f.Delete(It.IsAny<string>()), Times.Once);
server.Verify(s => s.Connect(), Times.Once);
server.Verify(s => s.Initialize(It.IsAny<ServerSettings>()), Times.Once);
server.Verify(s => s.GetAvailableExams(It.IsAny<string>()), Times.Once);
server.Verify(s => s.GetConfigurationFor(It.Is<Exam>(e => e == exam)), Times.Once);
server.Verify(s => s.GetConnectionInfo(), Times.Once);
server.Verify(s => s.SendSelectedExam(It.Is<Exam>(e => e == exam)), Times.Once);
Assert.AreEqual(1, initialize);
Assert.AreEqual(2, connect);
Assert.AreEqual(3, getExams);
Assert.AreEqual(4, examSelection);
Assert.AreEqual(5, getConfiguration);
Assert.AreEqual(6, getConnection);
Assert.AreEqual(7, delete);
Assert.AreEqual(connection.Api, context.Next.AppConfig.ServerApi);
Assert.AreEqual(connection.ConnectionToken, context.Next.AppConfig.ServerConnectionToken);
Assert.AreEqual(connection.Oauth2Token, context.Next.AppConfig.ServerOauth2Token);
Assert.AreEqual(exam.Id, context.Next.AppConfig.ServerExamId);
Assert.AreEqual(exam.Url, context.Next.Settings.Browser.StartUrl);
Assert.AreSame(examSettings, context.Next.Settings);
Assert.AreSame(serverSettings, context.Next.Settings.Server);
Assert.AreNotSame(initialSettings, context.Next.Settings);
Assert.AreEqual(OperationResult.Success, result);
Assert.AreEqual(SessionMode.Server, context.Next.Settings.SessionMode);
}
[TestMethod]
public void Repeat_MustFailIfSettingsCouldNotBeLoaded()
{
var connection = new ConnectionInfo { Api = "some API", ConnectionToken = "some token", Oauth2Token = "some OAuth2 token" };
var exam = new Exam { Id = "some id", LmsName = "some LMS", Name = "some name", Url = "some URL" };
var examSettings = new AppSettings();
var initialSettings = context.Next.Settings;
configuration.Setup(c => c.TryLoadSettings(It.IsAny<Uri>(), out examSettings, It.IsAny<PasswordParameters>())).Returns(LoadStatus.UnexpectedError);
context.Next.Settings.SessionMode = SessionMode.Server;
server.Setup(s => s.Connect()).Returns(new ServerResponse(true));
server.Setup(s => s.Initialize(It.IsAny<ServerSettings>()));
server.Setup(s => s.GetConnectionInfo()).Returns(connection);
server.Setup(s => s.GetAvailableExams(It.IsAny<string>())).Returns(new ServerResponse<IEnumerable<Exam>>(true, default(IEnumerable<Exam>)));
server.Setup(s => s.GetConfigurationFor(It.IsAny<Exam>())).Returns(new ServerResponse<Uri>(true, new Uri("file:///configuration.seb")));
sut.ActionRequired += (args) =>
{
if (args is ExamSelectionEventArgs e)
{
e.Success = true;
e.SelectedExam = exam;
}
};
var result = sut.Repeat();
fileSystem.Verify(f => f.Delete(It.IsAny<string>()), Times.Once);
server.Verify(s => s.Connect(), Times.Once);
server.Verify(s => s.Initialize(It.IsAny<ServerSettings>()), Times.Once);
server.Verify(s => s.GetAvailableExams(It.IsAny<string>()), Times.Once);
server.Verify(s => s.GetConfigurationFor(It.Is<Exam>(e => e == exam)), Times.Once);
server.Verify(s => s.GetConnectionInfo(), Times.Once);
Assert.AreNotEqual(connection.Api, context.Next.AppConfig.ServerApi);
Assert.AreNotEqual(connection.ConnectionToken, context.Next.AppConfig.ServerConnectionToken);
Assert.AreNotEqual(connection.Oauth2Token, context.Next.AppConfig.ServerOauth2Token);
Assert.AreNotEqual(exam.Id, context.Next.AppConfig.ServerExamId);
Assert.AreNotEqual(exam.Url, context.Next.Settings.Browser.StartUrl);
Assert.AreNotSame(examSettings, context.Next.Settings);
Assert.AreSame(initialSettings, context.Next.Settings);
Assert.AreEqual(OperationResult.Failed, result);
Assert.AreEqual(SessionMode.Server, context.Next.Settings.SessionMode);
}
[TestMethod]
public void Repeat_MustCorrectlyAbort()
{
var connection = new ConnectionInfo { Api = "some API", ConnectionToken = "some token", Oauth2Token = "some OAuth2 token" };
var exam = new Exam { Id = "some id", LmsName = "some LMS", Name = "some name", Url = "some URL" };
var examSettings = new AppSettings();
var messageShown = false;
configuration.Setup(c => c.TryLoadSettings(It.IsAny<Uri>(), out examSettings, It.IsAny<PasswordParameters>())).Returns(LoadStatus.Success);
context.Next.Settings.SessionMode = SessionMode.Server;
server.Setup(s => s.Connect()).Returns(new ServerResponse(true));
server.Setup(s => s.Initialize(It.IsAny<ServerSettings>()));
server.Setup(s => s.GetConnectionInfo()).Returns(connection);
server.Setup(s => s.GetAvailableExams(It.IsAny<string>())).Returns(new ServerResponse<IEnumerable<Exam>>(true, default(IEnumerable<Exam>)));
server.Setup(s => s.GetConfigurationFor(It.IsAny<Exam>())).Returns(new ServerResponse<Uri>(false, default(Uri)));
sut.ActionRequired += (args) =>
{
if (args is ExamSelectionEventArgs e)
{
e.Success = true;
e.SelectedExam = exam;
}
if (args is ServerFailureEventArgs s)
{
s.Abort = true;
messageShown = true;
}
};
var result = sut.Repeat();
fileSystem.Verify(f => f.Delete(It.IsAny<string>()), Times.Never);
server.Verify(s => s.Connect(), Times.Once);
server.Verify(s => s.Initialize(It.IsAny<ServerSettings>()), Times.Once);
server.Verify(s => s.GetAvailableExams(It.IsAny<string>()), Times.Once);
server.Verify(s => s.GetConfigurationFor(It.Is<Exam>(e => e == exam)), Times.Once);
server.Verify(s => s.GetConnectionInfo(), Times.Never);
Assert.IsTrue(messageShown);
Assert.AreEqual(OperationResult.Aborted, result);
}
[TestMethod]
public void Repeat_MustCorrectlyFallback()
{
var connection = new ConnectionInfo { Api = "some API", ConnectionToken = "some token", Oauth2Token = "some OAuth2 token" };
var exam = new Exam { Id = "some id", LmsName = "some LMS", Name = "some name", Url = "some URL" };
var examSettings = new AppSettings();
var messageShown = false;
configuration.Setup(c => c.TryLoadSettings(It.IsAny<Uri>(), out examSettings, It.IsAny<PasswordParameters>())).Returns(LoadStatus.Success);
context.Next.Settings.SessionMode = SessionMode.Server;
server.Setup(s => s.Connect()).Returns(new ServerResponse(true));
server.Setup(s => s.Initialize(It.IsAny<ServerSettings>()));
server.Setup(s => s.GetConnectionInfo()).Returns(connection);
server.Setup(s => s.GetAvailableExams(It.IsAny<string>())).Returns(new ServerResponse<IEnumerable<Exam>>(true, default(IEnumerable<Exam>)));
server.Setup(s => s.GetConfigurationFor(It.IsAny<Exam>())).Returns(new ServerResponse<Uri>(false, default(Uri)));
sut.ActionRequired += (args) =>
{
if (args is ExamSelectionEventArgs e)
{
e.Success = true;
e.SelectedExam = exam;
}
if (args is ServerFailureEventArgs s)
{
s.Fallback = true;
messageShown = true;
}
};
var result = sut.Repeat();
fileSystem.Verify(f => f.Delete(It.IsAny<string>()), Times.Never);
server.Verify(s => s.Connect(), Times.Once);
server.Verify(s => s.Initialize(It.IsAny<ServerSettings>()), Times.Once);
server.Verify(s => s.GetAvailableExams(It.IsAny<string>()), Times.Once);
server.Verify(s => s.GetConfigurationFor(It.Is<Exam>(e => e == exam)), Times.Once);
server.Verify(s => s.GetConnectionInfo(), Times.Never);
Assert.IsTrue(messageShown);
Assert.AreEqual(SessionMode.Normal, context.Next.Settings.SessionMode);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Repeat_MustAutomaticallySelectExam()
{
var connection = new ConnectionInfo { Api = "some API", ConnectionToken = "some token", Oauth2Token = "some OAuth2 token" };
var exam = new Exam { Id = "some id", LmsName = "some LMS", Name = "some name", Url = "some URL" };
var examSettings = new AppSettings();
var serverSettings = context.Next.Settings.Server;
configuration.Setup(c => c.TryLoadSettings(It.IsAny<Uri>(), out examSettings, It.IsAny<PasswordParameters>())).Returns(LoadStatus.Success);
context.Next.Settings.SessionMode = SessionMode.Server;
context.Next.Settings.Server.ExamId = "some id";
fileSystem.Setup(f => f.Delete(It.IsAny<string>()));
server.Setup(s => s.Connect()).Returns(new ServerResponse(true));
server.Setup(s => s.Initialize(It.IsAny<ServerSettings>()));
server.Setup(s => s.GetConnectionInfo()).Returns(connection);
server.Setup(s => s.GetAvailableExams(It.IsAny<string>())).Returns(new ServerResponse<IEnumerable<Exam>>(true, new[] { exam }));
server.Setup(s => s.GetConfigurationFor(It.IsAny<Exam>())).Returns(new ServerResponse<Uri>(true, new Uri("file:///configuration.seb")));
server.Setup(s => s.SendSelectedExam(It.IsAny<Exam>())).Returns(new ServerResponse<string>(true, default));
sut.ActionRequired += (args) => Assert.Fail();
var result = sut.Repeat();
fileSystem.Verify(f => f.Delete(It.IsAny<string>()), Times.Once);
server.Verify(s => s.Connect(), Times.Once);
server.Verify(s => s.Initialize(It.IsAny<ServerSettings>()), Times.Once);
server.Verify(s => s.GetAvailableExams(It.IsAny<string>()), Times.Once);
server.Verify(s => s.GetConfigurationFor(It.Is<Exam>(e => e == exam)), Times.Once);
server.Verify(s => s.GetConnectionInfo(), Times.Once);
server.Verify(s => s.SendSelectedExam(It.Is<Exam>(e => e == exam)), Times.Once);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Repeat_MustNotAllowToReconfigureServerSession()
{
var args = default(ActionRequiredEventArgs);
context.Current.AppConfig.ServerApi = "api";
context.Current.AppConfig.ServerConnectionToken = "token";
context.Current.AppConfig.ServerExamId = "id";
context.Current.AppConfig.ServerOauth2Token = "oauth2";
context.Current.Settings.SessionMode = SessionMode.Server;
context.Next.Settings.SessionMode = SessionMode.Server;
sut.ActionRequired += (a) =>
{
args = a;
};
var result = sut.Repeat();
configuration.VerifyNoOtherCalls();
fileSystem.VerifyNoOtherCalls();
server.VerifyNoOtherCalls();
Assert.IsNull(context.Next.AppConfig.ServerApi);
Assert.IsNull(context.Next.AppConfig.ServerConnectionToken);
Assert.IsNull(context.Next.AppConfig.ServerOauth2Token);
Assert.IsInstanceOfType(args, typeof(MessageEventArgs));
Assert.AreEqual(OperationResult.Aborted, result);
}
[TestMethod]
public void Repeat_MustDoNothingIfNormalSession()
{
var initialSettings = context.Next.Settings;
context.Current.Settings.SessionMode = SessionMode.Normal;
context.Next.Settings.SessionMode = SessionMode.Normal;
sut.ActionRequired += (_) => Assert.Fail();
var result = sut.Repeat();
configuration.VerifyNoOtherCalls();
fileSystem.VerifyNoOtherCalls();
server.VerifyNoOtherCalls();
Assert.AreSame(initialSettings, context.Next.Settings);
Assert.AreEqual(SessionMode.Normal, context.Next.Settings.SessionMode);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Revert_MustDisconnectFromServerWhenSessionRunning()
{
context.Current.Settings.SessionMode = SessionMode.Server;
context.Next = default;
server.Setup(s => s.Disconnect()).Returns(new ServerResponse(true));
var result = sut.Revert();
fileSystem.VerifyNoOtherCalls();
server.Verify(s => s.Disconnect(), Times.Once);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Revert_MustDisconnectFromServerWhenSessionStartFailed()
{
context.Current = default;
context.Next.Settings.SessionMode = SessionMode.Server;
server.Setup(s => s.Disconnect()).Returns(new ServerResponse(true));
var result = sut.Revert();
fileSystem.VerifyNoOtherCalls();
server.Verify(s => s.Disconnect(), Times.Once);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Revert_MustFailWhenDisconnectionUnsuccesful()
{
context.Current.Settings.SessionMode = SessionMode.Server;
server.Setup(s => s.Disconnect()).Returns(new ServerResponse(false));
var result = sut.Revert();
fileSystem.VerifyNoOtherCalls();
server.Verify(s => s.Disconnect(), Times.Once);
Assert.AreEqual(OperationResult.Failed, result);
}
[TestMethod]
public void Revert_MustDoNothingIfNormalSession()
{
context.Current.Settings.SessionMode = SessionMode.Normal;
sut.ActionRequired += (_) => Assert.Fail();
var result = sut.Revert();
configuration.VerifyNoOtherCalls();
fileSystem.VerifyNoOtherCalls();
server.VerifyNoOtherCalls();
Assert.AreEqual(OperationResult.Success, result);
}
}
}

View File

@@ -0,0 +1,564 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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.Threading;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Communication.Contracts.Hosts;
using SafeExamBrowser.Communication.Contracts.Proxies;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Core.Contracts.OperationModel;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Runtime.Operations;
using SafeExamBrowser.Runtime.Operations.Events;
using SafeExamBrowser.Settings;
using SafeExamBrowser.Settings.Service;
using SafeExamBrowser.SystemComponents.Contracts;
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
namespace SafeExamBrowser.Runtime.UnitTests.Operations
{
[TestClass]
public class ServiceOperationTests
{
private SessionContext context;
private Mock<ILogger> logger;
private Mock<IRuntimeHost> runtimeHost;
private Mock<IServiceProxy> service;
private EventWaitHandle serviceEvent;
private Mock<IUserInfo> userInfo;
private ServiceOperation sut;
[TestInitialize]
public void Initialize()
{
var serviceEventName = $"{nameof(SafeExamBrowser)}-{nameof(ServiceOperationTests)}";
logger = new Mock<ILogger>();
runtimeHost = new Mock<IRuntimeHost>();
service = new Mock<IServiceProxy>();
serviceEvent = new EventWaitHandle(false, EventResetMode.AutoReset, serviceEventName);
context = new SessionContext();
userInfo = new Mock<IUserInfo>();
context.Current = new SessionConfiguration();
context.Current.AppConfig = new AppConfig();
context.Current.AppConfig.ServiceEventName = serviceEventName;
context.Current.Settings = new AppSettings();
context.Next = new SessionConfiguration();
context.Next.AppConfig = new AppConfig();
context.Next.AppConfig.ServiceEventName = serviceEventName;
context.Next.Settings = new AppSettings();
sut = new ServiceOperation(logger.Object, runtimeHost.Object, service.Object, context, 0, userInfo.Object);
}
[TestMethod]
public void Perform_MustConnectToService()
{
service.Setup(s => s.Connect(null, true)).Returns(true);
context.Next.Settings.Service.Policy = ServicePolicy.Mandatory;
sut.Perform();
service.Setup(s => s.Connect(null, true)).Returns(true);
context.Next.Settings.Service.Policy = ServicePolicy.Optional;
sut.Perform();
service.Verify(s => s.Connect(null, true), Times.Exactly(2));
}
[TestMethod]
public void Perform_MustNotConnectToServiceWithIgnoreSet()
{
service.Setup(s => s.Connect(null, true)).Returns(true);
context.Next.Settings.Service.IgnoreService = true;
context.Next.Settings.Service.Policy = ServicePolicy.Mandatory;
sut.Perform();
service.Setup(s => s.Connect(null, true)).Returns(true);
context.Next.Settings.Service.Policy = ServicePolicy.Optional;
sut.Perform();
service.Verify(s => s.Connect(null, true), Times.Never);
}
[TestMethod]
public void Perform_MustStartSessionIfConnected()
{
context.Next.SessionId = Guid.NewGuid();
context.Next.Settings.Service.Policy = ServicePolicy.Optional;
service.SetupGet(s => s.IsConnected).Returns(true);
service.Setup(s => s.Connect(null, true)).Returns(true);
service.Setup(s => s.StartSession(It.IsAny<ServiceConfiguration>())).Returns(new CommunicationResult(true)).Callback(() => serviceEvent.Set());
var result = sut.Perform();
service.Verify(s => s.StartSession(It.Is<ServiceConfiguration>(c => c.SessionId == context.Next.SessionId)), Times.Once);
service.Verify(s => s.RunSystemConfigurationUpdate(), Times.Never);
userInfo.Verify(u => u.GetUserName(), Times.Once);
userInfo.Verify(u => u.GetUserSid(), Times.Once);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Perform_MustFailIfSessionStartUnsuccessful()
{
service.SetupGet(s => s.IsConnected).Returns(true);
service.Setup(s => s.Connect(null, true)).Returns(true);
service.Setup(s => s.StartSession(It.IsAny<ServiceConfiguration>())).Returns(new CommunicationResult(true));
var result = sut.Perform();
service.Verify(s => s.StartSession(It.IsAny<ServiceConfiguration>()), Times.Once);
Assert.AreEqual(OperationResult.Failed, result);
}
[TestMethod]
public void Perform_MustFailIfSessionNotStartedWithinTimeout()
{
const int TIMEOUT = 50;
var after = default(DateTime);
var before = default(DateTime);
service.SetupGet(s => s.IsConnected).Returns(true);
service.Setup(s => s.Connect(null, true)).Returns(true);
service.Setup(s => s.StartSession(It.IsAny<ServiceConfiguration>())).Returns(new CommunicationResult(true));
sut = new ServiceOperation(logger.Object, runtimeHost.Object, service.Object, context, TIMEOUT, userInfo.Object);
before = DateTime.Now;
var result = sut.Perform();
after = DateTime.Now;
service.Verify(s => s.StartSession(It.IsAny<ServiceConfiguration>()), Times.Once);
Assert.AreEqual(OperationResult.Failed, result);
Assert.IsTrue(after - before >= new TimeSpan(0, 0, 0, 0, TIMEOUT));
}
[TestMethod]
public void Perform_MustNotStartSessionIfNotConnected()
{
context.Next.Settings.Service.Policy = ServicePolicy.Mandatory;
service.SetupGet(s => s.IsConnected).Returns(false);
service.Setup(s => s.Connect(null, true)).Returns(false);
var result = sut.Perform();
service.Verify(s => s.StartSession(It.IsAny<ServiceConfiguration>()), Times.Never);
Assert.AreEqual(OperationResult.Failed, result);
}
[TestMethod]
public void Perform_MustHandleCommunicationFailureWhenStartingSession()
{
service.SetupGet(s => s.IsConnected).Returns(true);
service.Setup(s => s.Connect(null, true)).Returns(true);
service.Setup(s => s.StartSession(It.IsAny<ServiceConfiguration>())).Returns(new CommunicationResult(false));
var result = sut.Perform();
service.Verify(s => s.StartSession(It.IsAny<ServiceConfiguration>()), Times.Once);
Assert.AreEqual(OperationResult.Failed, result);
}
[TestMethod]
public void Perform_MustFailIfServiceMandatoryAndNotAvailable()
{
var errorShown = false;
context.Next.Settings.Service.Policy = ServicePolicy.Mandatory;
service.SetupGet(s => s.IsConnected).Returns(false);
service.Setup(s => s.Connect(null, true)).Returns(false);
sut.ActionRequired += (args) => errorShown = args is MessageEventArgs m && m.Icon == MessageBoxIcon.Error;
var result = sut.Perform();
Assert.AreEqual(OperationResult.Failed, result);
Assert.IsTrue(errorShown);
}
[TestMethod]
public void Perform_MustNotFailIfServiceOptionalAndNotAvailable()
{
context.Next.Settings.Service.Policy = ServicePolicy.Optional;
service.SetupGet(s => s.IsConnected).Returns(false);
service.Setup(s => s.Connect(null, true)).Returns(false);
var result = sut.Perform();
service.Verify(s => s.StartSession(It.IsAny<ServiceConfiguration>()), Times.Never);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Perform_MustShowWarningIfServiceNotAvailableAndPolicyWarn()
{
var warningShown = false;
context.Next.Settings.Service.Policy = ServicePolicy.Warn;
service.SetupGet(s => s.IsConnected).Returns(false);
service.Setup(s => s.Connect(null, true)).Returns(false);
sut.ActionRequired += (args) => warningShown = args is MessageEventArgs m && m.Icon == MessageBoxIcon.Warning;
var result = sut.Perform();
Assert.AreEqual(OperationResult.Success, result);
Assert.IsTrue(warningShown);
}
[TestMethod]
public void Repeat_MustStopCurrentAndStartNewSession()
{
var order = 0;
var start1 = 0;
var stop1 = 0;
var start2 = 0;
var session1Id = Guid.NewGuid();
var session2Id = Guid.NewGuid();
context.Next.SessionId = session1Id;
service.SetupGet(s => s.IsConnected).Returns(true);
service.Setup(s => s.Connect(null, true)).Returns(true);
service
.Setup(s => s.StartSession(It.Is<ServiceConfiguration>(c => c.SessionId == session1Id)))
.Returns(new CommunicationResult(true))
.Callback(() => { start1 = ++order; serviceEvent.Set(); });
service
.Setup(s => s.StartSession(It.Is<ServiceConfiguration>(c => c.SessionId == session2Id)))
.Returns(new CommunicationResult(true))
.Callback(() => { start2 = ++order; serviceEvent.Set(); });
service
.Setup(s => s.StopSession(It.IsAny<Guid>()))
.Returns(new CommunicationResult(true))
.Callback(() => { stop1 = ++order; serviceEvent.Set(); });
sut.Perform();
context.Current.SessionId = session1Id;
context.Next.SessionId = session2Id;
var result = sut.Repeat();
service.Verify(s => s.Connect(It.IsAny<Guid?>(), It.IsAny<bool>()), Times.Once);
service.Verify(s => s.StopSession(It.Is<Guid>(id => id == session1Id)), Times.Once);
service.Verify(s => s.StartSession(It.Is<ServiceConfiguration>(c => c.SessionId == session1Id)), Times.Once);
service.Verify(s => s.StartSession(It.Is<ServiceConfiguration>(c => c.SessionId == session2Id)), Times.Once);
service.Verify(s => s.Disconnect(), Times.Never);
service.Verify(s => s.RunSystemConfigurationUpdate(), Times.Never);
Assert.AreEqual(OperationResult.Success, result);
Assert.IsTrue(start1 == 1);
Assert.IsTrue(stop1 == 2);
Assert.IsTrue(start2 == 3);
}
[TestMethod]
public void Repeat_MustEstablishConnectionIfNotConnected()
{
PerformNormally();
service.Reset();
service.SetupGet(s => s.IsConnected).Returns(false);
service.Setup(s => s.Connect(null, true)).Returns(true).Callback(() => service.SetupGet(s => s.IsConnected).Returns(true));
service.Setup(s => s.StartSession(It.IsAny<ServiceConfiguration>())).Returns(new CommunicationResult(true)).Callback(() => serviceEvent.Set());
var result = sut.Repeat();
service.Verify(s => s.Connect(It.IsAny<Guid?>(), It.IsAny<bool>()), Times.Once);
service.Verify(s => s.StopSession(It.IsAny<Guid>()), Times.Never);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Repeat_MustNotEstablishConnectionWithIgnoreSet()
{
PerformNormally();
context.Next.Settings.Service.IgnoreService = true;
context.Next.Settings.Service.Policy = ServicePolicy.Mandatory;
service.Reset();
service.SetupGet(s => s.IsConnected).Returns(false);
service.Setup(s => s.Connect(null, true)).Returns(true).Callback(() => service.SetupGet(s => s.IsConnected).Returns(true));
service.Setup(s => s.StartSession(It.IsAny<ServiceConfiguration>())).Returns(new CommunicationResult(true)).Callback(() => serviceEvent.Set());
var result = sut.Repeat();
service.Verify(s => s.Connect(It.IsAny<Guid?>(), It.IsAny<bool>()), Times.Never);
service.Verify(s => s.StartSession(It.IsAny<ServiceConfiguration>()), Times.Never);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Repeat_MustStopSessionAndCloseConnectionWithIgnoreSet()
{
var connected = true;
PerformNormally();
context.Next.Settings.Service.IgnoreService = true;
context.Next.Settings.Service.Policy = ServicePolicy.Mandatory;
service.Reset();
service.SetupGet(s => s.IsConnected).Returns(() => connected);
service.Setup(s => s.StopSession(It.IsAny<Guid>())).Returns(new CommunicationResult(true)).Callback(() => serviceEvent.Set());
service.Setup(s => s.Disconnect()).Returns(true).Callback(() => connected = false);
var result = sut.Repeat();
service.Verify(s => s.Connect(It.IsAny<Guid?>(), It.IsAny<bool>()), Times.Never);
service.Verify(s => s.StartSession(It.IsAny<ServiceConfiguration>()), Times.Never);
service.Verify(s => s.StopSession(It.IsAny<Guid>()), Times.Once);
service.Verify(s => s.Disconnect(), Times.Once);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Repeat_MustFailIfCurrentSessionWasNotStoppedSuccessfully()
{
service.Setup(s => s.StopSession(It.IsAny<Guid>())).Returns(new CommunicationResult(false));
PerformNormally();
var result = sut.Repeat();
service.Verify(s => s.StopSession(It.IsAny<Guid>()), Times.Once);
service.Verify(s => s.StartSession(It.IsAny<ServiceConfiguration>()), Times.Once);
service.Verify(s => s.Disconnect(), Times.Never);
Assert.AreEqual(OperationResult.Failed, result);
}
[TestMethod]
public void Repeat_MustFailIfSessionNotStoppedWithinTimeout()
{
const int TIMEOUT = 50;
var after = default(DateTime);
var before = default(DateTime);
service.Setup(s => s.StopSession(It.IsAny<Guid>())).Returns(new CommunicationResult(true));
sut = new ServiceOperation(logger.Object, runtimeHost.Object, service.Object, context, TIMEOUT, userInfo.Object);
PerformNormally();
before = DateTime.Now;
var result = sut.Repeat();
after = DateTime.Now;
service.Verify(s => s.StopSession(It.IsAny<Guid>()), Times.Once);
service.Verify(s => s.Disconnect(), Times.Never);
Assert.AreEqual(OperationResult.Failed, result);
Assert.IsTrue(after - before >= new TimeSpan(0, 0, 0, 0, TIMEOUT));
}
[TestMethod]
public void Repeat_MustNotStopSessionIfNoSessionRunning()
{
service.SetupGet(s => s.IsConnected).Returns(true);
service.Setup(s => s.StartSession(It.IsAny<ServiceConfiguration>())).Returns(new CommunicationResult(true)).Callback(() => serviceEvent.Set());
var result = sut.Repeat();
service.Verify(s => s.StopSession(It.IsAny<Guid>()), Times.Never);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Revert_MustDisconnect()
{
service.Setup(s => s.Disconnect()).Returns(true);
service.Setup(s => s.StopSession(It.IsAny<Guid>())).Returns(new CommunicationResult(true)).Callback(() => serviceEvent.Set());
service.Setup(s => s.RunSystemConfigurationUpdate()).Returns(new CommunicationResult(true));
PerformNormally();
var result = sut.Revert();
service.Verify(s => s.Disconnect(), Times.Once);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Revert_MustHandleCommunicationFailureWhenStoppingSession()
{
service.Setup(s => s.StopSession(It.IsAny<Guid>())).Returns(new CommunicationResult(false));
PerformNormally();
var result = sut.Revert();
service.Verify(s => s.StopSession(It.IsAny<Guid>()), Times.Once);
service.Verify(s => s.Disconnect(), Times.Once);
service.Verify(s => s.RunSystemConfigurationUpdate(), Times.Never);
Assert.AreEqual(OperationResult.Failed, result);
}
[TestMethod]
public void Revert_MustHandleCommunicationFailureWhenInitiatingSystemConfigurationUpdate()
{
service.Setup(s => s.StopSession(It.IsAny<Guid>())).Returns(new CommunicationResult(true)).Callback(() => serviceEvent.Set());
service.Setup(s => s.RunSystemConfigurationUpdate()).Returns(new CommunicationResult(false));
PerformNormally();
var result = sut.Revert();
service.Verify(s => s.StopSession(It.IsAny<Guid>()), Times.Once);
service.Verify(s => s.Disconnect(), Times.Once);
service.Verify(s => s.RunSystemConfigurationUpdate(), Times.Once);
Assert.AreEqual(OperationResult.Failed, result);
}
[TestMethod]
public void Revert_MustFailIfSessionStopUnsuccessful()
{
service.Setup(s => s.StopSession(It.IsAny<Guid>())).Returns(new CommunicationResult(true));
PerformNormally();
var result = sut.Revert();
service.Verify(s => s.RunSystemConfigurationUpdate(), Times.Never);
Assert.AreEqual(OperationResult.Failed, result);
}
[TestMethod]
public void Revert_MustFailIfSessionNotStoppedWithinTimeout()
{
const int TIMEOUT = 50;
var after = default(DateTime);
var before = default(DateTime);
service.Setup(s => s.StopSession(It.IsAny<Guid>())).Returns(new CommunicationResult(true));
sut = new ServiceOperation(logger.Object, runtimeHost.Object, service.Object, context, TIMEOUT, userInfo.Object);
PerformNormally();
before = DateTime.Now;
var result = sut.Revert();
after = DateTime.Now;
service.Verify(s => s.StopSession(It.IsAny<Guid>()), Times.Once);
service.Verify(s => s.Disconnect(), Times.Once);
service.Verify(s => s.RunSystemConfigurationUpdate(), Times.Never);
Assert.AreEqual(OperationResult.Failed, result);
Assert.IsTrue(after - before >= new TimeSpan(0, 0, 0, 0, TIMEOUT));
}
[TestMethod]
public void Revert_MustNotStopSessionWhenNotConnected()
{
var result = sut.Revert();
service.Verify(s => s.StopSession(It.IsAny<Guid>()), Times.Never);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Revert_MustNotStopSessionIfNoSessionRunning()
{
context.Current = null;
context.Next = null;
service.SetupGet(s => s.IsConnected).Returns(true);
service.Setup(s => s.Disconnect()).Returns(true);
var result = sut.Revert();
service.Verify(s => s.StopSession(It.IsAny<Guid>()), Times.Never);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Revert_MustNotDisconnnectIfNotConnected()
{
var result = sut.Revert();
service.Verify(s => s.Disconnect(), Times.Never);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Revert_MustStopSessionIfConnected()
{
context.Next.SessionId = Guid.NewGuid();
service.Setup(s => s.Disconnect()).Returns(true);
service.Setup(s => s.StopSession(It.IsAny<Guid>())).Returns(new CommunicationResult(true)).Callback(() => serviceEvent.Set());
service.Setup(s => s.RunSystemConfigurationUpdate()).Returns(new CommunicationResult(true));
PerformNormally();
var result = sut.Revert();
service.Verify(s => s.StopSession(It.Is<Guid>(id => id == context.Next.SessionId)), Times.Once);
service.Verify(s => s.Disconnect(), Times.Once);
service.Verify(s => s.RunSystemConfigurationUpdate(), Times.Once);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Revert_MustStopSessionIfNewSessionNotCompletelyStarted()
{
var sessionId = Guid.NewGuid();
context.Current.SessionId = Guid.NewGuid();
context.Next.SessionId = sessionId;
PerformNormally();
context.Current.SessionId = default(Guid);
context.Next.SessionId = default(Guid);
service.Setup(s => s.StopSession(It.Is<Guid>(id => id == sessionId))).Returns(new CommunicationResult(true)).Callback(() => serviceEvent.Set());
service.Setup(s => s.RunSystemConfigurationUpdate()).Returns(new CommunicationResult(true));
service.Setup(s => s.Disconnect()).Returns(true);
var result = sut.Revert();
service.Verify(s => s.StopSession(It.Is<Guid>(id => id == sessionId)), Times.Once);
service.Verify(s => s.StopSession(It.Is<Guid>(id => id == default(Guid))), Times.Never);
service.Verify(s => s.Disconnect(), Times.Once);
Assert.AreEqual(OperationResult.Success, result);
}
private void PerformNormally()
{
service.SetupGet(s => s.IsConnected).Returns(true);
service.Setup(s => s.Connect(null, true)).Returns(true);
service.Setup(s => s.StartSession(It.IsAny<ServiceConfiguration>())).Returns(new CommunicationResult(true)).Callback(() => serviceEvent.Set());
sut.Perform();
}
}
}

View File

@@ -0,0 +1,97 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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;
using Moq;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Core.Contracts.OperationModel;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Runtime.Operations;
using SafeExamBrowser.Settings;
using SafeExamBrowser.Settings.Logging;
namespace SafeExamBrowser.Runtime.UnitTests.Operations
{
[TestClass]
public class SessionActivationOperationTests
{
private SessionConfiguration currentSession;
private Mock<ILogger> logger;
private SessionConfiguration nextSession;
private AppSettings nextSettings;
private SessionContext sessionContext;
private SessionActivationOperation sut;
[TestInitialize]
public void Initialize()
{
currentSession = new SessionConfiguration();
logger = new Mock<ILogger>();
nextSession = new SessionConfiguration();
nextSettings = new AppSettings();
sessionContext = new SessionContext();
nextSession.Settings = nextSettings;
sessionContext.Current = currentSession;
sessionContext.Next = nextSession;
sut = new SessionActivationOperation(logger.Object, sessionContext);
}
[TestMethod]
public void Perform_MustCorrectlyActivateFirstSession()
{
sessionContext.Current = null;
var result = sut.Perform();
Assert.AreEqual(OperationResult.Success, result);
Assert.AreSame(sessionContext.Current, nextSession);
Assert.IsNull(sessionContext.Next);
}
[TestMethod]
public void Perform_MustCorrectlySwitchLogSeverity()
{
nextSettings.LogLevel = LogLevel.Info;
sut.Perform();
logger.VerifySet(l => l.LogLevel = It.Is<LogLevel>(ll => ll == nextSettings.LogLevel));
}
[TestMethod]
public void Repeat_MustCorrectlySwitchSession()
{
var result = sut.Repeat();
Assert.AreEqual(OperationResult.Success, result);
Assert.AreSame(sessionContext.Current, nextSession);
Assert.IsNull(sessionContext.Next);
}
[TestMethod]
public void Repeat_MustCorrectlySwitchLogSeverity()
{
nextSettings.LogLevel = LogLevel.Warning;
sut.Perform();
logger.VerifySet(l => l.LogLevel = It.Is<LogLevel>(ll => ll == nextSettings.LogLevel));
}
[TestMethod]
public void Revert_MustAlwaysCompleteSuccessfully()
{
var result = sut.Revert();
Assert.AreEqual(OperationResult.Success, result);
}
}
}

View File

@@ -0,0 +1,101 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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.Communication.Contracts.Hosts;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Core.Contracts.OperationModel;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Runtime.Operations;
using SafeExamBrowser.SystemComponents.Contracts;
namespace SafeExamBrowser.Runtime.UnitTests.Operations
{
[TestClass]
public class SessionInitializationOperationTests
{
private AppConfig appConfig;
private Mock<IConfigurationRepository> configuration;
private Mock<IFileSystem> fileSystem;
private Mock<ILogger> logger;
private Mock<IRuntimeHost> runtimeHost;
private SessionConfiguration session;
private SessionContext sessionContext;
private SessionInitializationOperation sut;
[TestInitialize]
public void Initialize()
{
appConfig = new AppConfig();
configuration = new Mock<IConfigurationRepository>();
fileSystem = new Mock<IFileSystem>();
logger = new Mock<ILogger>();
runtimeHost = new Mock<IRuntimeHost>();
session = new SessionConfiguration();
sessionContext = new SessionContext();
configuration.Setup(c => c.InitializeSessionConfiguration()).Returns(session);
session.AppConfig = appConfig;
sessionContext.Next = session;
sut = new SessionInitializationOperation(configuration.Object, fileSystem.Object, logger.Object, runtimeHost.Object, sessionContext);
}
[TestMethod]
public void Perform_MustInitializeConfiguration()
{
var token = Guid.NewGuid();
appConfig.TemporaryDirectory = @"C:\Some\Random\Path";
session.ClientAuthenticationToken = token;
var result = sut.Perform();
configuration.Verify(c => c.InitializeSessionConfiguration(), Times.Once);
fileSystem.Verify(f => f.CreateDirectory(It.Is<string>(s => s == appConfig.TemporaryDirectory)), Times.Once);
Assert.AreEqual(OperationResult.Success, result);
Assert.IsNull(sessionContext.Current);
}
[TestMethod]
public void Repeat_MustInitializeConfiguration()
{
var currentSession = new SessionConfiguration();
var token = Guid.NewGuid();
appConfig.TemporaryDirectory = @"C:\Some\Random\Path";
session.ClientAuthenticationToken = token;
sessionContext.Current = currentSession;
var result = sut.Repeat();
configuration.Verify(c => c.InitializeSessionConfiguration(), Times.Once);
fileSystem.Verify(f => f.CreateDirectory(It.Is<string>(s => s == appConfig.TemporaryDirectory)), Times.Once);
Assert.AreEqual(OperationResult.Success, result);
Assert.AreSame(currentSession,sessionContext.Current);
}
[TestMethod]
public void Revert_MustDoNothing()
{
var result = sut.Revert();
configuration.VerifyNoOtherCalls();
fileSystem.VerifyNoOtherCalls();
logger.VerifyNoOtherCalls();
runtimeHost.VerifyNoOtherCalls();
Assert.AreEqual(OperationResult.Success, result);
}
}
}

View File

@@ -0,0 +1,141 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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;
using Moq;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Core.Contracts.OperationModel;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Monitoring.Contracts;
using SafeExamBrowser.Runtime.Operations;
using SafeExamBrowser.Settings;
using SafeExamBrowser.Settings.Security;
namespace SafeExamBrowser.Runtime.UnitTests.Operations
{
[TestClass]
public class VirtualMachineOperationTests
{
private Mock<IVirtualMachineDetector> detector;
private Mock<ILogger> logger;
private SessionContext context;
private VirtualMachineOperation sut;
[TestInitialize]
public void Initialize()
{
detector = new Mock<IVirtualMachineDetector>();
logger = new Mock<ILogger>();
context = new SessionContext();
context.Next = new SessionConfiguration();
context.Next.Settings = new AppSettings();
sut = new VirtualMachineOperation(detector.Object, logger.Object, context);
}
[TestMethod]
public void Perform_MustAbortIfVirtualMachineNotAllowed()
{
context.Next.Settings.Security.VirtualMachinePolicy = VirtualMachinePolicy.Deny;
detector.Setup(d => d.IsVirtualMachine()).Returns(true);
var result = sut.Perform();
detector.Verify(d => d.IsVirtualMachine(), Times.Once);
Assert.AreEqual(OperationResult.Aborted, result);
}
[TestMethod]
public void Perform_MustSucceedIfVirtualMachineAllowed()
{
context.Next.Settings.Security.VirtualMachinePolicy = VirtualMachinePolicy.Allow;
detector.Setup(d => d.IsVirtualMachine()).Returns(true);
var result = sut.Perform();
detector.Verify(d => d.IsVirtualMachine(), Times.AtMostOnce);
Assert.AreEqual(OperationResult.Success, result);
context.Next.Settings.Security.VirtualMachinePolicy = VirtualMachinePolicy.Allow;
detector.Setup(d => d.IsVirtualMachine()).Returns(false);
result = sut.Perform();
detector.Verify(d => d.IsVirtualMachine(), Times.AtMost(2));
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Perform_MustSucceedIfNotAVirtualMachine()
{
context.Next.Settings.Security.VirtualMachinePolicy = VirtualMachinePolicy.Deny;
detector.Setup(d => d.IsVirtualMachine()).Returns(false);
var result = sut.Perform();
detector.Verify(d => d.IsVirtualMachine(), Times.Once);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Repeat_MustAbortIfVirtualMachineNotAllowed()
{
context.Next.Settings.Security.VirtualMachinePolicy = VirtualMachinePolicy.Deny;
detector.Setup(d => d.IsVirtualMachine()).Returns(true);
var result = sut.Repeat();
detector.Verify(d => d.IsVirtualMachine(), Times.Once);
Assert.AreEqual(OperationResult.Aborted, result);
}
[TestMethod]
public void Repeat_MustSucceedIfVirtualMachineAllowed()
{
context.Next.Settings.Security.VirtualMachinePolicy = VirtualMachinePolicy.Allow;
detector.Setup(d => d.IsVirtualMachine()).Returns(true);
var result = sut.Repeat();
detector.Verify(d => d.IsVirtualMachine(), Times.AtMostOnce);
Assert.AreEqual(OperationResult.Success, result);
context.Next.Settings.Security.VirtualMachinePolicy = VirtualMachinePolicy.Allow;
detector.Setup(d => d.IsVirtualMachine()).Returns(false);
result = sut.Repeat();
detector.Verify(d => d.IsVirtualMachine(), Times.AtMost(2));
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Repeat_MustSucceedIfNotAVirtualMachine()
{
context.Next.Settings.Security.VirtualMachinePolicy = VirtualMachinePolicy.Deny;
detector.Setup(d => d.IsVirtualMachine()).Returns(false);
var result = sut.Repeat();
detector.Verify(d => d.IsVirtualMachine(), Times.Once);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Revert_MustDoNothing()
{
var result = sut.Revert();
detector.VerifyNoOtherCalls();
logger.VerifyNoOtherCalls();
Assert.AreEqual(OperationResult.Success, result);
}
}
}

View File

@@ -0,0 +1,17 @@
using System.Reflection;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("SafeExamBrowser.Runtime.UnitTests")]
[assembly: AssemblyDescription("Safe Exam Browser")]
[assembly: AssemblyCompany("ETH Zürich")]
[assembly: AssemblyProduct("SafeExamBrowser.Runtime.UnitTests")]
[assembly: AssemblyCopyright("Copyright © 2024 ETH Zürich, IT Services")]
[assembly: ComVisible(false)]
[assembly: Guid("b716a8b2-df72-4143-9941-25e033089f5f")]
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0.0")]

View File

@@ -0,0 +1,773 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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 System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Communication.Contracts.Data;
using SafeExamBrowser.Communication.Contracts.Events;
using SafeExamBrowser.Communication.Contracts.Hosts;
using SafeExamBrowser.Communication.Contracts.Proxies;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Core.Contracts.OperationModel;
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Runtime.Operations.Events;
using SafeExamBrowser.Server.Contracts.Data;
using SafeExamBrowser.Settings;
using SafeExamBrowser.Settings.Security;
using SafeExamBrowser.Settings.Service;
using SafeExamBrowser.UserInterface.Contracts;
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
using SafeExamBrowser.UserInterface.Contracts.Windows;
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
using SafeExamBrowser.WindowsApi.Contracts;
namespace SafeExamBrowser.Runtime.UnitTests
{
[TestClass]
public class RuntimeControllerTests
{
private AppConfig appConfig;
private Mock<IOperationSequence> bootstrapSequence;
private Mock<IProcess> clientProcess;
private Mock<IClientProxy> clientProxy;
private SessionConfiguration currentSession;
private AppSettings currentSettings;
private Mock<ILogger> logger;
private Mock<IMessageBox> messageBox;
private SessionConfiguration nextSession;
private AppSettings nextSettings;
private Mock<IRuntimeHost> runtimeHost;
private Mock<IRuntimeWindow> runtimeWindow;
private Mock<IServiceProxy> service;
private SessionContext sessionContext;
private Mock<IRepeatableOperationSequence> sessionSequence;
private Mock<Action> shutdown;
private Mock<ISplashScreen> splashScreen;
private Mock<IText> text;
private Mock<IUserInterfaceFactory> uiFactory;
private RuntimeController sut;
[TestInitialize]
public void Initialize()
{
appConfig = new AppConfig();
bootstrapSequence = new Mock<IOperationSequence>();
clientProcess = new Mock<IProcess>();
clientProxy = new Mock<IClientProxy>();
currentSession = new SessionConfiguration();
currentSettings = new AppSettings();
logger = new Mock<ILogger>();
messageBox = new Mock<IMessageBox>();
nextSession = new SessionConfiguration();
nextSettings = new AppSettings();
runtimeHost = new Mock<IRuntimeHost>();
runtimeWindow = new Mock<IRuntimeWindow>();
service = new Mock<IServiceProxy>();
sessionContext = new SessionContext();
sessionSequence = new Mock<IRepeatableOperationSequence>();
shutdown = new Mock<Action>();
splashScreen = new Mock<ISplashScreen>();
text = new Mock<IText>();
uiFactory = new Mock<IUserInterfaceFactory>();
currentSession.Settings = currentSettings;
nextSession.Settings = nextSettings;
sessionContext.ClientProcess = clientProcess.Object;
sessionContext.ClientProxy = clientProxy.Object;
sessionContext.Current = currentSession;
sessionContext.Next = nextSession;
uiFactory.Setup(u => u.CreateRuntimeWindow(It.IsAny<AppConfig>())).Returns(new Mock<IRuntimeWindow>().Object);
uiFactory.Setup(u => u.CreateSplashScreen(It.IsAny<AppConfig>())).Returns(new Mock<ISplashScreen>().Object);
sut = new RuntimeController(
appConfig,
logger.Object,
messageBox.Object,
bootstrapSequence.Object,
sessionSequence.Object,
runtimeHost.Object,
runtimeWindow.Object,
service.Object,
sessionContext,
shutdown.Object,
splashScreen.Object,
text.Object,
uiFactory.Object);
}
[TestMethod]
public void ClientProcess_MustShutdownWhenClientTerminated()
{
StartSession();
clientProcess.Raise(c => c.Terminated += null, -1);
messageBox.Verify(m => m.Show(
It.IsAny<TextKey>(),
It.IsAny<TextKey>(),
It.IsAny<MessageBoxAction>(),
It.Is<MessageBoxIcon>(i => i == MessageBoxIcon.Error),
It.IsAny<IWindow>()), Times.Once);
sessionSequence.Verify(s => s.TryRevert(), Times.Once);
shutdown.Verify(s => s(), Times.Once);
}
[TestMethod]
public void ClientProxy_MustShutdownWhenConnectionLost()
{
StartSession();
clientProxy.Raise(c => c.ConnectionLost += null);
messageBox.Verify(m => m.Show(
It.IsAny<TextKey>(),
It.IsAny<TextKey>(),
It.IsAny<MessageBoxAction>(),
It.Is<MessageBoxIcon>(i => i == MessageBoxIcon.Error),
It.IsAny<IWindow>()), Times.Once);
sessionSequence.Verify(s => s.TryRevert(), Times.Once);
shutdown.Verify(s => s(), Times.Once);
}
[TestMethod]
public void Communication_MustProvideClientConfigurationUponRequest()
{
var args = new ClientConfigurationEventArgs();
var nextAppConfig = new AppConfig();
var nextSessionId = Guid.NewGuid();
var nextSettings = new AppSettings();
nextSession.AppConfig = nextAppConfig;
nextSession.SessionId = nextSessionId;
nextSession.Settings = nextSettings;
StartSession();
runtimeHost.Raise(r => r.ClientConfigurationNeeded += null, args);
Assert.AreSame(nextAppConfig, args.ClientConfiguration.AppConfig);
Assert.AreEqual(nextSessionId, args.ClientConfiguration.SessionId);
Assert.AreSame(nextSettings, args.ClientConfiguration.Settings);
}
[TestMethod]
public void Communication_MustStartNewSessionUponRequest()
{
var args = new ReconfigurationEventArgs { ConfigurationPath = "C:\\Some\\File\\Path.seb" };
StartSession();
bootstrapSequence.Reset();
sessionSequence.Reset();
sessionSequence.Setup(s => s.TryRepeat()).Returns(OperationResult.Success);
runtimeHost.Raise(r => r.ReconfigurationRequested += null, args);
bootstrapSequence.VerifyNoOtherCalls();
sessionSequence.Verify(s => s.TryPerform(), Times.Never);
sessionSequence.Verify(s => s.TryRepeat(), Times.Once);
sessionSequence.Verify(s => s.TryRevert(), Times.Never);
Assert.AreEqual(sessionContext.ReconfigurationFilePath, args.ConfigurationPath);
}
[TestMethod]
public void Communication_MustInformClientAboutAbortedReconfiguration()
{
StartSession();
sessionSequence.Reset();
sessionSequence.Setup(s => s.TryRepeat()).Returns(OperationResult.Aborted);
runtimeHost.Raise(r => r.ReconfigurationRequested += null, new ReconfigurationEventArgs());
clientProxy.Verify(c => c.InformReconfigurationAborted(), Times.Once);
}
[TestMethod]
public void Communication_MustShutdownUponRequest()
{
StartSession();
bootstrapSequence.Reset();
sessionSequence.Reset();
runtimeHost.Raise(r => r.ShutdownRequested += null);
bootstrapSequence.VerifyNoOtherCalls();
sessionSequence.VerifyNoOtherCalls();
shutdown.Verify(s => s(), Times.Once);
}
[TestMethod]
public void Operations_MustAllowToAbortStartupForClientConfiguration()
{
var args = new ConfigurationCompletedEventArgs();
messageBox.Setup(m => m.Show(It.IsAny<TextKey>(), It.IsAny<TextKey>(), It.IsAny<MessageBoxAction>(), It.IsAny<MessageBoxIcon>(), It.IsAny<IWindow>())).Returns(MessageBoxResult.Yes);
sut.TryStart();
sessionSequence.Raise(s => s.ActionRequired += null, args);
Assert.IsTrue(args.AbortStartup);
}
[TestMethod]
public void Operations_MustRequestServerExamSelectionViaDialogOnDefaultDesktop()
{
var args = new ExamSelectionEventArgs(Enumerable.Empty<Exam>());
var examSelectionDialog = new Mock<IExamSelectionDialog>();
var result = new ExamSelectionDialogResult { SelectedExam = new Exam(), Success = true };
currentSettings.Security.KioskMode = KioskMode.DisableExplorerShell;
examSelectionDialog.Setup(p => p.Show(It.IsAny<IWindow>())).Returns(result);
uiFactory.Setup(u => u.CreateExamSelectionDialog(It.IsAny<IEnumerable<Exam>>())).Returns(examSelectionDialog.Object);
sut.TryStart();
sessionSequence.Raise(s => s.ActionRequired += null, args);
clientProxy.VerifyNoOtherCalls();
examSelectionDialog.Verify(p => p.Show(It.IsAny<IWindow>()), Times.Once);
uiFactory.Verify(u => u.CreateExamSelectionDialog(It.IsAny<IEnumerable<Exam>>()), Times.Once);
Assert.AreEqual(true, args.Success);
Assert.AreEqual(result.SelectedExam, args.SelectedExam);
}
[TestMethod]
public void Operations_MustRequestServerExamSelectionViaClientOnNewDesktop()
{
var args = new ExamSelectionEventArgs(new[] { new Exam { Id = "abc1234" } });
var examSelectionReceived = new Action<IEnumerable<(string, string, string, string)>, Guid>((e, id) =>
{
runtimeHost.Raise(r => r.ExamSelectionReceived += null, new ExamSelectionReplyEventArgs
{
RequestId = id,
SelectedExamId = "abc1234",
Success = true
});
});
currentSettings.Security.KioskMode = KioskMode.CreateNewDesktop;
clientProxy
.Setup(c => c.RequestExamSelection(It.IsAny<IEnumerable<(string, string, string, string)>>(), It.IsAny<Guid>()))
.Returns(new CommunicationResult(true))
.Callback(examSelectionReceived);
sut.TryStart();
sessionSequence.Raise(s => s.ActionRequired += null, args);
clientProxy.Verify(c => c.RequestExamSelection(It.IsAny<IEnumerable<(string, string, string, string)>>(), It.IsAny<Guid>()), Times.Once);
uiFactory.Verify(u => u.CreateExamSelectionDialog(It.IsAny<IEnumerable<Exam>>()), Times.Never);
Assert.AreEqual("abc1234", args.SelectedExam.Id);
}
[TestMethod]
public void Operations_MustRequestPasswordViaDialogOnDefaultDesktop()
{
var args = new PasswordRequiredEventArgs();
var passwordDialog = new Mock<IPasswordDialog>();
var result = new PasswordDialogResult { Password = "test1234", Success = true };
currentSettings.Security.KioskMode = KioskMode.DisableExplorerShell;
passwordDialog.Setup(p => p.Show(It.IsAny<IWindow>())).Returns(result);
uiFactory.Setup(u => u.CreatePasswordDialog(It.IsAny<string>(), It.IsAny<string>())).Returns(passwordDialog.Object);
sut.TryStart();
sessionSequence.Raise(s => s.ActionRequired += null, args);
clientProxy.VerifyNoOtherCalls();
passwordDialog.Verify(p => p.Show(It.IsAny<IWindow>()), Times.Once);
uiFactory.Verify(u => u.CreatePasswordDialog(It.IsAny<string>(), It.IsAny<string>()), Times.Once);
Assert.AreEqual(true, args.Success);
Assert.AreEqual(result.Password, args.Password);
}
[TestMethod]
public void Operations_MustRequestPasswordViaClientOnNewDesktop()
{
var args = new PasswordRequiredEventArgs();
var passwordReceived = new Action<PasswordRequestPurpose, Guid>((p, id) =>
{
runtimeHost.Raise(r => r.PasswordReceived += null, new PasswordReplyEventArgs { Password = "test", RequestId = id, Success = true });
});
currentSettings.Security.KioskMode = KioskMode.CreateNewDesktop;
clientProxy.Setup(c => c.RequestPassword(It.IsAny<PasswordRequestPurpose>(), It.IsAny<Guid>())).Returns(new CommunicationResult(true)).Callback(passwordReceived);
sut.TryStart();
sessionSequence.Raise(s => s.ActionRequired += null, args);
clientProxy.Verify(c => c.RequestPassword(It.IsAny<PasswordRequestPurpose>(), It.IsAny<Guid>()), Times.Once);
uiFactory.Verify(u => u.CreatePasswordDialog(It.IsAny<string>(), It.IsAny<string>()), Times.Never);
Assert.AreEqual("test", args.Password);
}
[TestMethod]
public void Operations_MustRequestServerFailureActionViaDialogOnDefaultDesktop()
{
var args = new ServerFailureEventArgs(default(string), default(bool));
var failureDialog = new Mock<IServerFailureDialog>();
var result = new ServerFailureDialogResult { Fallback = true, Success = true };
currentSettings.Security.KioskMode = KioskMode.DisableExplorerShell;
failureDialog.Setup(p => p.Show(It.IsAny<IWindow>())).Returns(result);
uiFactory.Setup(u => u.CreateServerFailureDialog(It.IsAny<string>(), It.IsAny<bool>())).Returns(failureDialog.Object);
sut.TryStart();
sessionSequence.Raise(s => s.ActionRequired += null, args);
clientProxy.VerifyNoOtherCalls();
failureDialog.Verify(p => p.Show(It.IsAny<IWindow>()), Times.Once);
uiFactory.Verify(u => u.CreateServerFailureDialog(It.IsAny<string>(), It.IsAny<bool>()), Times.Once);
Assert.AreEqual(result.Abort, args.Abort);
Assert.AreEqual(result.Fallback, args.Fallback);
Assert.AreEqual(result.Retry, args.Retry);
}
[TestMethod]
public void Operations_MustRequestServerFailureActionViaClientOnNewDesktop()
{
var args = new ServerFailureEventArgs(default(string), default(bool));
var failureActionReceived = new Action<string, bool, Guid>((m, f, id) =>
{
runtimeHost.Raise(r => r.ServerFailureActionReceived += null, new ServerFailureActionReplyEventArgs
{
RequestId = id,
Fallback = true
});
});
currentSettings.Security.KioskMode = KioskMode.CreateNewDesktop;
clientProxy
.Setup(c => c.RequestServerFailureAction(It.IsAny<string>(), It.IsAny<bool>(), It.IsAny<Guid>()))
.Returns(new CommunicationResult(true))
.Callback(failureActionReceived);
sut.TryStart();
sessionSequence.Raise(s => s.ActionRequired += null, args);
clientProxy.Verify(c => c.RequestServerFailureAction(It.IsAny<string>(), It.IsAny<bool>(), It.IsAny<Guid>()), Times.Once);
uiFactory.Verify(u => u.CreateServerFailureDialog(It.IsAny<string>(), It.IsAny<bool>()), Times.Never);
Assert.IsFalse(args.Abort);
Assert.IsTrue(args.Fallback);
Assert.IsFalse(args.Retry);
}
[TestMethod]
public void Operations_MustAbortAskingForPasswordViaClientIfDecidedByUser()
{
var args = new PasswordRequiredEventArgs();
var passwordReceived = new Action<PasswordRequestPurpose, Guid>((p, id) =>
{
runtimeHost.Raise(r => r.PasswordReceived += null, new PasswordReplyEventArgs { RequestId = id, Success = false });
});
currentSettings.Security.KioskMode = KioskMode.CreateNewDesktop;
clientProxy.Setup(c => c.RequestPassword(It.IsAny<PasswordRequestPurpose>(), It.IsAny<Guid>())).Returns(new CommunicationResult(true)).Callback(passwordReceived);
sut.TryStart();
sessionSequence.Raise(s => s.ActionRequired += null, args);
clientProxy.Verify(c => c.RequestPassword(It.IsAny<PasswordRequestPurpose>(), It.IsAny<Guid>()), Times.Once);
uiFactory.Verify(u => u.CreatePasswordDialog(It.IsAny<string>(), It.IsAny<string>()), Times.Never);
}
[TestMethod]
public void Operations_MustNotWaitForPasswordViaClientIfCommunicationHasFailed()
{
var args = new PasswordRequiredEventArgs();
currentSettings.Security.KioskMode = KioskMode.CreateNewDesktop;
clientProxy.Setup(c => c.RequestPassword(It.IsAny<PasswordRequestPurpose>(), It.IsAny<Guid>())).Returns(new CommunicationResult(false));
sut.TryStart();
sessionSequence.Raise(s => s.ActionRequired += null, args);
}
[TestMethod]
public void Operations_MustShowNormalMessageBoxOnDefaultDesktop()
{
var args = new MessageEventArgs
{
Icon = MessageBoxIcon.Question,
Message = TextKey.MessageBox_ClientConfigurationQuestion,
Title = TextKey.MessageBox_ClientConfigurationQuestionTitle
};
StartSession();
currentSettings.Security.KioskMode = KioskMode.DisableExplorerShell;
sessionSequence.Raise(s => s.ActionRequired += null, args);
messageBox.Verify(m => m.Show(
It.IsAny<string>(),
It.IsAny<string>(),
It.Is<MessageBoxAction>(a => a == MessageBoxAction.Ok),
It.Is<MessageBoxIcon>(i => i == args.Icon),
It.IsAny<IWindow>()), Times.Once);
clientProxy.VerifyAdd(p => p.ConnectionLost += It.IsAny<CommunicationEventHandler>());
clientProxy.VerifyNoOtherCalls();
}
[TestMethod]
public void Operations_MustShowMessageBoxViaClientOnNewDesktop()
{
var args = new MessageEventArgs
{
Icon = MessageBoxIcon.Question,
Message = TextKey.MessageBox_ClientConfigurationQuestion,
Title = TextKey.MessageBox_ClientConfigurationQuestionTitle
};
var reply = new MessageBoxReplyEventArgs();
StartSession();
currentSettings.Security.KioskMode = KioskMode.CreateNewDesktop;
clientProxy.Setup(c => c.ShowMessage(
It.IsAny<string>(),
It.IsAny<string>(),
It.Is<int>(a => a == (int) MessageBoxAction.Ok),
It.IsAny<int>(),
It.IsAny<Guid>()))
.Callback<string, string, int, int, Guid>((m, t, a, i, id) =>
{
runtimeHost.Raise(r => r.MessageBoxReplyReceived += null, new MessageBoxReplyEventArgs { RequestId = id });
})
.Returns(new CommunicationResult(true));
sessionSequence.Raise(s => s.ActionRequired += null, args);
messageBox.Verify(m => m.Show(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<MessageBoxAction>(), It.IsAny<MessageBoxIcon>(), It.IsAny<IWindow>()), Times.Never);
clientProxy.Verify(c => c.ShowMessage(
It.IsAny<string>(),
It.IsAny<string>(),
It.Is<int>(a => a == (int) MessageBoxAction.Ok),
It.Is<int>(i => i == (int) args.Icon),
It.IsAny<Guid>()), Times.Once);
}
[TestMethod]
public void Operations_MustUpdateProgress()
{
var args = new ProgressChangedEventArgs
{
CurrentValue = 23,
IsIndeterminate = true,
MaxValue = 150,
Progress = true,
Regress = true
};
sut.TryStart();
sessionSequence.Raise(o => o.ProgressChanged += null, args);
runtimeWindow.Verify(s => s.SetValue(It.Is<int>(i => i == args.CurrentValue)), Times.Once);
runtimeWindow.Verify(s => s.SetIndeterminate(), Times.Exactly(2));
runtimeWindow.Verify(s => s.SetMaxValue(It.Is<int>(i => i == args.MaxValue)), Times.Once);
runtimeWindow.Verify(s => s.Progress(), Times.Once);
runtimeWindow.Verify(s => s.Regress(), Times.Once);
}
[TestMethod]
public void Operations_MustUpdateStatus()
{
var key = TextKey.OperationStatus_InitializeClipboard;
sut.TryStart();
sessionSequence.Raise(o => o.StatusChanged += null, key);
runtimeWindow.Verify(s => s.UpdateStatus(It.Is<TextKey>(k => k == key), It.IsAny<bool>()), Times.Once);
}
[TestMethod]
public void Session_MustHideRuntimeWindowWhenUsingDisableExplorerShell()
{
currentSettings.Security.KioskMode = KioskMode.DisableExplorerShell;
StartSession();
runtimeWindow.Verify(w => w.Hide(), Times.AtLeastOnce);
runtimeWindow.Reset();
sessionSequence.Reset();
sessionSequence.Setup(b => b.TryRepeat()).Returns(OperationResult.Aborted);
runtimeHost.Raise(h => h.ReconfigurationRequested += null, new ReconfigurationEventArgs());
runtimeWindow.Verify(w => w.Hide(), Times.AtLeastOnce);
}
[TestMethod]
public void Session_MustShowMessageBoxOnFailure()
{
bootstrapSequence.Setup(b => b.TryPerform()).Returns(OperationResult.Success);
sessionSequence.Setup(b => b.TryPerform()).Returns(OperationResult.Failed);
sessionContext.Current = null;
sut.TryStart();
messageBox.Verify(m => m.Show(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<MessageBoxAction>(), It.IsAny<MessageBoxIcon>(), It.IsAny<IWindow>()), Times.AtLeastOnce);
StartSession();
messageBox.Reset();
sessionSequence.Reset();
sessionSequence.Setup(b => b.TryRepeat()).Returns(OperationResult.Failed);
runtimeHost.Raise(h => h.ReconfigurationRequested += null, new ReconfigurationEventArgs());
messageBox.Verify(m => m.Show(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<MessageBoxAction>(), It.IsAny<MessageBoxIcon>(), It.IsAny<IWindow>()), Times.AtLeastOnce);
}
[TestMethod]
public void ServiceProxy_MustShutdownWhenConnectionLostAndMandatory()
{
currentSettings.Service.Policy = ServicePolicy.Mandatory;
StartSession();
service.Raise(c => c.ConnectionLost += null);
messageBox.Verify(m => m.Show(
It.IsAny<TextKey>(),
It.IsAny<TextKey>(),
It.IsAny<MessageBoxAction>(),
It.Is<MessageBoxIcon>(i => i == MessageBoxIcon.Error),
It.IsAny<IWindow>()), Times.Once);
sessionSequence.Verify(s => s.TryRevert(), Times.Once);
shutdown.Verify(s => s(), Times.Once);
}
[TestMethod]
public void ServiceProxy_MustNotShutdownWhenConnectionLostAndNotMandatory()
{
currentSettings.Service.Policy = ServicePolicy.Optional;
StartSession();
service.Raise(c => c.ConnectionLost += null);
messageBox.Verify(m => m.Show(
It.IsAny<TextKey>(),
It.IsAny<TextKey>(),
It.IsAny<MessageBoxAction>(),
It.Is<MessageBoxIcon>(i => i == MessageBoxIcon.Error),
It.IsAny<IWindow>()), Times.Never);
sessionSequence.Verify(s => s.TryRevert(), Times.Never);
shutdown.Verify(s => s(), Times.Never);
}
[TestMethod]
public void Shutdown_MustRevertSessionThenBootstrapSequence()
{
var order = 0;
var bootstrap = 0;
var session = 0;
sut.TryStart();
bootstrapSequence.Reset();
sessionSequence.Reset();
bootstrapSequence.Setup(b => b.TryRevert()).Returns(OperationResult.Success).Callback(() => bootstrap = ++order);
sessionSequence.Setup(b => b.TryRevert()).Returns(OperationResult.Success).Callback(() => session = ++order);
sut.Terminate();
bootstrapSequence.Verify(b => b.TryPerform(), Times.Never);
bootstrapSequence.Verify(b => b.TryRevert(), Times.Once);
sessionSequence.Verify(b => b.TryPerform(), Times.Never);
sessionSequence.Verify(b => b.TryRepeat(), Times.Never);
sessionSequence.Verify(b => b.TryRevert(), Times.Once);
Assert.AreEqual(1, session);
Assert.AreEqual(2, bootstrap);
}
[TestMethod]
public void Shutdown_MustOnlyRevertBootstrapSequenceIfNoSessionRunning()
{
var order = 0;
var bootstrap = 0;
var session = 0;
sut.TryStart();
bootstrapSequence.Reset();
sessionSequence.Reset();
bootstrapSequence.Setup(b => b.TryRevert()).Returns(OperationResult.Success).Callback(() => bootstrap = ++order);
sessionSequence.Setup(b => b.TryRevert()).Returns(OperationResult.Success).Callback(() => session = ++order);
sessionContext.Current = null;
sut.Terminate();
bootstrapSequence.Verify(b => b.TryPerform(), Times.Never);
bootstrapSequence.Verify(b => b.TryRevert(), Times.Once);
sessionSequence.Verify(b => b.TryPerform(), Times.Never);
sessionSequence.Verify(b => b.TryRepeat(), Times.Never);
sessionSequence.Verify(b => b.TryRevert(), Times.Never);
Assert.AreEqual(0, session);
Assert.AreEqual(1, bootstrap);
}
[TestMethod]
public void Shutdown_MustIndicateFailureToUser()
{
var order = 0;
var bootstrap = 0;
var session = 0;
sut.TryStart();
bootstrapSequence.Reset();
sessionSequence.Reset();
bootstrapSequence.Setup(b => b.TryRevert()).Returns(OperationResult.Failed).Callback(() => bootstrap = ++order);
sessionSequence.Setup(b => b.TryRevert()).Returns(OperationResult.Success).Callback(() => session = ++order);
sut.Terminate();
messageBox.Verify(m => m.Show(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<MessageBoxAction>(), It.IsAny<MessageBoxIcon>(), It.IsAny<IWindow>()), Times.AtLeastOnce);
}
[TestMethod]
public void Startup_MustPerformBootstrapThenSessionSequence()
{
var order = 0;
var bootstrap = 0;
var session = 0;
bootstrapSequence.Setup(b => b.TryPerform()).Returns(OperationResult.Success).Callback(() => bootstrap = ++order);
sessionSequence.Setup(b => b.TryPerform()).Returns(OperationResult.Success).Callback(() => { session = ++order; sessionContext.Current = currentSession; });
sessionContext.Current = null;
var success = sut.TryStart();
bootstrapSequence.Verify(b => b.TryPerform(), Times.Once);
bootstrapSequence.Verify(b => b.TryRevert(), Times.Never);
sessionSequence.Verify(b => b.TryPerform(), Times.Once);
sessionSequence.Verify(b => b.TryRepeat(), Times.Never);
sessionSequence.Verify(b => b.TryRevert(), Times.Never);
Assert.IsTrue(success);
Assert.AreEqual(1, bootstrap);
Assert.AreEqual(2, session);
}
[TestMethod]
public void Startup_MustNotPerformSessionSequenceIfBootstrapFails()
{
var order = 0;
var bootstrap = 0;
var session = 0;
bootstrapSequence.Setup(b => b.TryPerform()).Returns(OperationResult.Failed).Callback(() => bootstrap = ++order);
sessionSequence.Setup(b => b.TryPerform()).Returns(OperationResult.Success).Callback(() => { session = ++order; sessionContext.Current = currentSession; });
sessionContext.Current = null;
var success = sut.TryStart();
bootstrapSequence.Verify(b => b.TryPerform(), Times.Once);
bootstrapSequence.Verify(b => b.TryRevert(), Times.Never);
sessionSequence.Verify(b => b.TryPerform(), Times.Never);
sessionSequence.Verify(b => b.TryRepeat(), Times.Never);
sessionSequence.Verify(b => b.TryRevert(), Times.Never);
Assert.IsFalse(success);
Assert.AreEqual(1, bootstrap);
Assert.AreEqual(0, session);
}
[TestMethod]
public void Startup_MustTerminateOnSessionStartFailure()
{
bootstrapSequence.Setup(b => b.TryPerform()).Returns(OperationResult.Success);
sessionSequence.Setup(b => b.TryPerform()).Returns(OperationResult.Failed).Callback(() => sessionContext.Current = currentSession);
sessionContext.Current = null;
var success = sut.TryStart();
bootstrapSequence.Verify(b => b.TryPerform(), Times.Once);
bootstrapSequence.Verify(b => b.TryRevert(), Times.Never);
sessionSequence.Verify(b => b.TryPerform(), Times.Once);
sessionSequence.Verify(b => b.TryRepeat(), Times.Never);
sessionSequence.Verify(b => b.TryRevert(), Times.Once);
shutdown.Verify(s => s(), Times.Once);
}
[TestMethod]
public void Startup_MustNotTerminateOnSessionStartAbortion()
{
bootstrapSequence.Setup(b => b.TryPerform()).Returns(OperationResult.Success);
sessionSequence.Setup(b => b.TryPerform()).Returns(OperationResult.Aborted).Callback(() => sessionContext.Current = currentSession);
sessionContext.Current = null;
var success = sut.TryStart();
bootstrapSequence.Verify(b => b.TryPerform(), Times.Once);
bootstrapSequence.Verify(b => b.TryRevert(), Times.Never);
sessionSequence.Verify(b => b.TryPerform(), Times.Once);
sessionSequence.Verify(b => b.TryRepeat(), Times.Never);
sessionSequence.Verify(b => b.TryRevert(), Times.Never);
shutdown.Verify(s => s(), Times.Never);
}
[TestMethod]
public void Startup_MustUpdateProgressForBootstrapSequence()
{
var args = new ProgressChangedEventArgs
{
CurrentValue = 12,
IsIndeterminate = true,
MaxValue = 100,
Progress = true,
Regress = true
};
bootstrapSequence
.Setup(b => b.TryPerform())
.Returns(OperationResult.Success)
.Callback(() => { bootstrapSequence.Raise(s => s.ProgressChanged += null, args); });
var success = sut.TryStart();
splashScreen.Verify(s => s.SetValue(It.Is<int>(i => i == args.CurrentValue)), Times.Once);
splashScreen.Verify(s => s.SetIndeterminate(), Times.Once);
splashScreen.Verify(s => s.SetMaxValue(It.Is<int>(i => i == args.MaxValue)), Times.Once);
splashScreen.Verify(s => s.Progress(), Times.Once);
splashScreen.Verify(s => s.Regress(), Times.Once);
}
[TestMethod]
public void Startup_MustUpdateStatusForBootstrapSequence()
{
var key = TextKey.OperationStatus_InitializeBrowser;
bootstrapSequence
.Setup(b => b.TryPerform())
.Returns(OperationResult.Success)
.Callback(() => { bootstrapSequence.Raise(s => s.StatusChanged += null, key); });
var success = sut.TryStart();
splashScreen.Verify(s => s.UpdateStatus(It.Is<TextKey>(k => k == key), true), Times.Once);
}
private void StartSession()
{
bootstrapSequence.Setup(b => b.TryPerform()).Returns(OperationResult.Success);
sessionSequence.Setup(b => b.TryPerform()).Returns(OperationResult.Success).Callback(() => sessionContext.Current = currentSession);
sessionContext.Current = null;
sut.TryStart();
}
}
}

View File

@@ -0,0 +1,242 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\MSTest.TestAdapter.3.2.2\build\net462\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.3.2.2\build\net462\MSTest.TestAdapter.props')" />
<Import Project="..\packages\Microsoft.Testing.Extensions.Telemetry.1.0.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props" Condition="Exists('..\packages\Microsoft.Testing.Extensions.Telemetry.1.0.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props')" />
<Import Project="..\packages\Microsoft.Testing.Platform.MSBuild.1.0.2\build\netstandard2.0\Microsoft.Testing.Platform.MSBuild.props" Condition="Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.0.2\build\netstandard2.0\Microsoft.Testing.Platform.MSBuild.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{B716A8B2-DF72-4143-9941-25E033089F5F}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>SafeExamBrowser.Runtime.UnitTests</RootNamespace>
<AssemblyName>SafeExamBrowser.Runtime.UnitTests</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">15.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
<IsCodedUITest>False</IsCodedUITest>
<TestProjectType>UnitTest</TestProjectType>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="Castle.Core, Version=5.0.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
<HintPath>..\packages\Castle.Core.5.1.1\lib\net462\Castle.Core.dll</HintPath>
</Reference>
<Reference Include="Microsoft.ApplicationInsights, Version=2.22.0.997, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.ApplicationInsights.2.22.0\lib\net46\Microsoft.ApplicationInsights.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.Testing.Extensions.Telemetry, Version=1.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Testing.Extensions.Telemetry.1.0.2\lib\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Testing.Extensions.TrxReport.Abstractions, Version=1.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Testing.Extensions.TrxReport.Abstractions.1.0.2\lib\netstandard2.0\Microsoft.Testing.Extensions.TrxReport.Abstractions.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Testing.Extensions.VSTestBridge, Version=1.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Testing.Extensions.VSTestBridge.1.0.2\lib\netstandard2.0\Microsoft.Testing.Extensions.VSTestBridge.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Testing.Platform, Version=1.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Testing.Platform.MSBuild.1.0.2\lib\netstandard2.0\Microsoft.Testing.Platform.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Testing.Platform.MSBuild, Version=1.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Testing.Platform.MSBuild.1.0.2\lib\netstandard2.0\Microsoft.Testing.Platform.MSBuild.dll</HintPath>
</Reference>
<Reference Include="Microsoft.TestPlatform.CoreUtilities, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.9.0\lib\net462\Microsoft.TestPlatform.CoreUtilities.dll</HintPath>
</Reference>
<Reference Include="Microsoft.TestPlatform.PlatformAbstractions, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.9.0\lib\net462\Microsoft.TestPlatform.PlatformAbstractions.dll</HintPath>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestPlatform.ObjectModel, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.9.0\lib\net462\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll</HintPath>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\MSTest.TestFramework.3.2.2\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\MSTest.TestFramework.3.2.2\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath>
</Reference>
<Reference Include="Moq, Version=4.20.70.0, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
<HintPath>..\packages\Moq.4.20.70\lib\net462\Moq.dll</HintPath>
</Reference>
<Reference Include="NuGet.Frameworks, Version=6.9.1.3, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\NuGet.Frameworks.6.9.1\lib\net472\NuGet.Frameworks.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Buffers, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll</HintPath>
</Reference>
<Reference Include="System.Collections.Immutable, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Collections.Immutable.8.0.0\lib\net462\System.Collections.Immutable.dll</HintPath>
</Reference>
<Reference Include="System.Configuration" />
<Reference Include="System.Core" />
<Reference Include="System.Diagnostics.DiagnosticSource, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Diagnostics.DiagnosticSource.8.0.0\lib\net462\System.Diagnostics.DiagnosticSource.dll</HintPath>
</Reference>
<Reference Include="System.Memory, Version=4.0.1.2, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Memory.4.5.5\lib\net461\System.Memory.dll</HintPath>
</Reference>
<Reference Include="System.Net.Http" />
<Reference Include="System.Numerics" />
<Reference Include="System.Numerics.Vectors, Version=4.1.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
</Reference>
<Reference Include="System.Reflection.Metadata, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Reflection.Metadata.8.0.0\lib\net462\System.Reflection.Metadata.dll</HintPath>
</Reference>
<Reference Include="System.Runtime" />
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.ServiceModel" />
<Reference Include="System.Threading.Tasks.Extensions, Version=4.2.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll</HintPath>
</Reference>
<Reference Include="System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll</HintPath>
</Reference>
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Operations\ClientTerminationOperationTests.cs" />
<Compile Include="Operations\ConfigurationOperationTests.cs" />
<Compile Include="Operations\DisclaimerOperationTests.cs" />
<Compile Include="Operations\DisplayMonitorOperationTests.cs" />
<Compile Include="Operations\KioskModeOperationTests.cs" />
<Compile Include="Operations\RemoteSessionOperationTests.cs" />
<Compile Include="Operations\ServerOperationTests.cs" />
<Compile Include="Operations\ServiceOperationTests.cs" />
<Compile Include="Operations\ClientOperationTests.cs" />
<Compile Include="Operations\SessionActivationOperationTests.cs" />
<Compile Include="Operations\SessionInitializationOperationTests.cs" />
<Compile Include="Operations\VirtualMachineOperationTests.cs" />
<Compile Include="RuntimeControllerTests.cs" />
<Compile Include="Communication\RuntimeHostTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config">
<SubType>Designer</SubType>
</None>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SafeExamBrowser.Communication.Contracts\SafeExamBrowser.Communication.Contracts.csproj">
<Project>{0cd2c5fe-711a-4c32-afe0-bb804fe8b220}</Project>
<Name>SafeExamBrowser.Communication.Contracts</Name>
</ProjectReference>
<ProjectReference Include="..\SafeExamBrowser.Communication\SafeExamBrowser.Communication.csproj">
<Project>{c9416a62-0623-4d38-96aa-92516b32f02f}</Project>
<Name>SafeExamBrowser.Communication</Name>
</ProjectReference>
<ProjectReference Include="..\SafeExamBrowser.Configuration.Contracts\SafeExamBrowser.Configuration.Contracts.csproj">
<Project>{7d74555e-63e1-4c46-bd0a-8580552368c8}</Project>
<Name>SafeExamBrowser.Configuration.Contracts</Name>
</ProjectReference>
<ProjectReference Include="..\SafeExamBrowser.Core.Contracts\SafeExamBrowser.Core.Contracts.csproj">
<Project>{fe0e1224-b447-4b14-81e7-ed7d84822aa0}</Project>
<Name>SafeExamBrowser.Core.Contracts</Name>
</ProjectReference>
<ProjectReference Include="..\SafeExamBrowser.Core\SafeExamBrowser.Core.csproj">
<Project>{3D6FDBB6-A4AF-4626-BB2B-BF329D44F9CC}</Project>
<Name>SafeExamBrowser.Core</Name>
</ProjectReference>
<ProjectReference Include="..\SafeExamBrowser.I18n.Contracts\SafeExamBrowser.I18n.Contracts.csproj">
<Project>{1858ddf3-bc2a-4bff-b663-4ce2ffeb8b7d}</Project>
<Name>SafeExamBrowser.I18n.Contracts</Name>
</ProjectReference>
<ProjectReference Include="..\SafeExamBrowser.Logging.Contracts\SafeExamBrowser.Logging.Contracts.csproj">
<Project>{64ea30fb-11d4-436a-9c2b-88566285363e}</Project>
<Name>SafeExamBrowser.Logging.Contracts</Name>
</ProjectReference>
<ProjectReference Include="..\SafeExamBrowser.Monitoring.Contracts\SafeExamBrowser.Monitoring.Contracts.csproj">
<Project>{6d563a30-366d-4c35-815b-2c9e6872278b}</Project>
<Name>SafeExamBrowser.Monitoring.Contracts</Name>
</ProjectReference>
<ProjectReference Include="..\SafeExamBrowser.Runtime\SafeExamBrowser.Runtime.csproj">
<Project>{e3aed2f8-b5df-45d1-ac19-48066923d6d8}</Project>
<Name>SafeExamBrowser.Runtime</Name>
</ProjectReference>
<ProjectReference Include="..\SafeExamBrowser.Server.Contracts\SafeExamBrowser.Server.Contracts.csproj">
<Project>{db701e6f-bddc-4cec-b662-335a9dc11809}</Project>
<Name>SafeExamBrowser.Server.Contracts</Name>
</ProjectReference>
<ProjectReference Include="..\SafeExamBrowser.Settings\SafeExamBrowser.Settings.csproj">
<Project>{30b2d907-5861-4f39-abad-c4abf1b3470e}</Project>
<Name>SafeExamBrowser.Settings</Name>
</ProjectReference>
<ProjectReference Include="..\SafeExamBrowser.SystemComponents.Contracts\SafeExamBrowser.SystemComponents.Contracts.csproj">
<Project>{903129c6-e236-493b-9ad6-c6a57f647a3a}</Project>
<Name>SafeExamBrowser.SystemComponents.Contracts</Name>
</ProjectReference>
<ProjectReference Include="..\SafeExamBrowser.UserInterface.Contracts\SafeExamBrowser.UserInterface.Contracts.csproj">
<Project>{c7889e97-6ff6-4a58-b7cb-521ed276b316}</Project>
<Name>SafeExamBrowser.UserInterface.Contracts</Name>
</ProjectReference>
<ProjectReference Include="..\SafeExamBrowser.WindowsApi.Contracts\SafeExamBrowser.WindowsApi.Contracts.csproj">
<Project>{7016f080-9aa5-41b2-a225-385ad877c171}</Project>
<Name>SafeExamBrowser.WindowsApi.Contracts</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="Operations\Testdata\SebClientSettings.seb">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.0.2\build\netstandard2.0\Microsoft.Testing.Platform.MSBuild.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.MSBuild.1.0.2\build\netstandard2.0\Microsoft.Testing.Platform.MSBuild.props'))" />
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.0.2\build\netstandard2.0\Microsoft.Testing.Platform.MSBuild.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.MSBuild.1.0.2\build\netstandard2.0\Microsoft.Testing.Platform.MSBuild.targets'))" />
<Error Condition="!Exists('..\packages\Microsoft.Testing.Extensions.Telemetry.1.0.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Extensions.Telemetry.1.0.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props'))" />
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.3.2.2\build\net462\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.3.2.2\build\net462\MSTest.TestAdapter.props'))" />
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.3.2.2\build\net462\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.3.2.2\build\net462\MSTest.TestAdapter.targets'))" />
</Target>
<Import Project="..\packages\Microsoft.Testing.Platform.MSBuild.1.0.2\build\netstandard2.0\Microsoft.Testing.Platform.MSBuild.targets" Condition="Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.0.2\build\netstandard2.0\Microsoft.Testing.Platform.MSBuild.targets')" />
<Import Project="..\packages\MSTest.TestAdapter.3.2.2\build\net462\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.3.2.2\build\net462\MSTest.TestAdapter.targets')" />
</Project>

View File

@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Threading.Tasks.Extensions" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.0.1" newVersion="4.2.0.1" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Win32.Registry" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Security.Principal.Windows" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="NuGet.Frameworks" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.11.3.1" newVersion="5.11.3.1" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.ApplicationInsights" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.22.0.997" newVersion="2.22.0.997" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Diagnostics.DiagnosticSource" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.2" newVersion="4.0.1.2" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Reflection.Metadata" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" /></startup></configuration>

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Castle.Core" version="5.1.1" targetFramework="net48" />
<package id="Microsoft.ApplicationInsights" version="2.22.0" targetFramework="net48" />
<package id="Microsoft.Testing.Extensions.Telemetry" version="1.0.2" targetFramework="net48" />
<package id="Microsoft.Testing.Extensions.TrxReport.Abstractions" version="1.0.2" targetFramework="net48" />
<package id="Microsoft.Testing.Extensions.VSTestBridge" version="1.0.2" targetFramework="net48" />
<package id="Microsoft.Testing.Platform" version="1.0.2" targetFramework="net48" />
<package id="Microsoft.Testing.Platform.MSBuild" version="1.0.2" targetFramework="net48" />
<package id="Microsoft.TestPlatform.ObjectModel" version="17.9.0" targetFramework="net48" />
<package id="Moq" version="4.20.70" targetFramework="net48" />
<package id="MSTest.TestAdapter" version="3.2.2" targetFramework="net48" />
<package id="MSTest.TestFramework" version="3.2.2" targetFramework="net48" />
<package id="NuGet.Frameworks" version="6.9.1" targetFramework="net48" />
<package id="System.Buffers" version="4.5.1" targetFramework="net48" />
<package id="System.Collections.Immutable" version="8.0.0" targetFramework="net48" />
<package id="System.Diagnostics.DiagnosticSource" version="8.0.0" targetFramework="net48" />
<package id="System.Memory" version="4.5.5" targetFramework="net48" />
<package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net48" />
<package id="System.Reflection.Metadata" version="8.0.0" targetFramework="net48" />
<package id="System.Runtime.CompilerServices.Unsafe" version="6.0.0" targetFramework="net48" />
<package id="System.Threading.Tasks.Extensions" version="4.5.4" targetFramework="net48" />
<package id="System.ValueTuple" version="4.5.0" targetFramework="net48" />
</packages>