SEBWIN-353: Fixed bug with service session not being terminated when startup is aborted. Removed ignore flag from service proxy, as it did not serve any real purpose.

This commit is contained in:
dbuechel 2019-12-10 14:22:18 +01:00
parent 69c09dcb35
commit cf4e229fef
5 changed files with 144 additions and 169 deletions

View file

@ -16,12 +16,6 @@ namespace SafeExamBrowser.Communication.Contracts.Proxies
/// </summary>
public interface IServiceProxy : ICommunicationProxy
{
/// <summary>
/// Instructs the proxy to ignore all operations or simulate a connection, where applicable. To be set e.g. when the service
/// policy is optional and the service is not available.
/// </summary>
bool Ignore { set; }
/// <summary>
/// Instructs the service to start a system configuration update.
/// </summary>

View file

@ -47,24 +47,6 @@ namespace SafeExamBrowser.Communication.UnitTests.Proxies
sut.Connect();
}
[TestMethod]
public void MustIgnoreConnectIfUnavailable()
{
sut.Ignore = true;
sut.Connect();
proxy.Verify(p => p.Connect(It.IsAny<Guid>()), Times.Never);
}
[TestMethod]
public void MustIgnoreDisconnectIfUnavailable()
{
sut.Ignore = true;
sut.Disconnect();
proxy.Verify(p => p.Disconnect(It.IsAny<DisconnectionMessage>()), Times.Never);
}
[TestMethod]
public void MustCorrectlySendSystemConfigurationUpdate()
{
@ -87,15 +69,6 @@ namespace SafeExamBrowser.Communication.UnitTests.Proxies
Assert.IsFalse(communication.Success);
}
[TestMethod]
public void MustIgnoreSystemConfigurationUpdateIfUnavailable()
{
sut.Ignore = true;
sut.RunSystemConfigurationUpdate();
proxy.Verify(p => p.Send(It.IsAny<Message>()), Times.Never);
}
[TestMethod]
public void MustCorrectlyStartSession()
{
@ -120,17 +93,6 @@ namespace SafeExamBrowser.Communication.UnitTests.Proxies
Assert.IsFalse(communication.Success);
}
[TestMethod]
public void MustIgnoreStartSessionIfUnavaiable()
{
sut.Ignore = true;
var communication = sut.StartSession(null);
Assert.IsTrue(communication.Success);
proxy.Verify(p => p.Send(It.IsAny<Message>()), Times.Never);
}
[TestMethod]
public void MustCorrectlyStopSession()
{
@ -154,16 +116,5 @@ namespace SafeExamBrowser.Communication.UnitTests.Proxies
Assert.IsFalse(communication.Success);
}
[TestMethod]
public void MustIgnoreStopSessionIfUnavaiable()
{
sut.Ignore = true;
var communication = sut.StopSession(Guid.Empty);
Assert.IsTrue(communication.Success);
proxy.Verify(p => p.Send(It.IsAny<Message>()), Times.Never);
}
}
}

View file

@ -20,39 +20,12 @@ namespace SafeExamBrowser.Communication.Proxies
/// </summary>
public class ServiceProxy : BaseProxy, IServiceProxy
{
public bool Ignore { private get; set; }
public ServiceProxy(string address, IProxyObjectFactory factory, ILogger logger, Interlocutor owner) : base(address, factory, logger, owner)
{
}
public override bool Connect(Guid? token = null, bool autoPing = true)
{
if (IgnoreOperation(nameof(Connect)))
{
return false;
}
return base.Connect(autoPing: autoPing);
}
public override bool Disconnect()
{
if (IgnoreOperation(nameof(Disconnect)))
{
return false;
}
return base.Disconnect();
}
public CommunicationResult RunSystemConfigurationUpdate()
{
if (IgnoreOperation(nameof(RunSystemConfigurationUpdate)))
{
return new CommunicationResult(true);
}
try
{
var response = Send(SimpleMessagePurport.UpdateSystemConfiguration);
@ -79,11 +52,6 @@ namespace SafeExamBrowser.Communication.Proxies
public CommunicationResult StartSession(ServiceConfiguration configuration)
{
if (IgnoreOperation(nameof(StartSession)))
{
return new CommunicationResult(true);
}
try
{
var response = Send(new SessionStartMessage(configuration));
@ -110,11 +78,6 @@ namespace SafeExamBrowser.Communication.Proxies
public CommunicationResult StopSession(Guid sessionId)
{
if (IgnoreOperation(nameof(StopSession)))
{
return new CommunicationResult(true);
}
try
{
var response = Send(new SessionStopMessage(sessionId));
@ -138,15 +101,5 @@ namespace SafeExamBrowser.Communication.Proxies
return new CommunicationResult(false);
}
}
private bool IgnoreOperation(string operationName)
{
if (Ignore)
{
Logger.Debug($"Skipping '{operationName}' operation because the ignore flag is set.");
}
return Ignore;
}
}
}

View file

@ -13,12 +13,12 @@ using Moq;
using SafeExamBrowser.Communication.Contracts.Hosts;
using SafeExamBrowser.Communication.Contracts.Proxies;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Settings;
using SafeExamBrowser.Settings.Service;
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;
@ -27,14 +27,11 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
[TestClass]
public class ServiceOperationTests
{
private AppConfig appConfig;
private SessionContext context;
private Mock<ILogger> logger;
private Mock<IRuntimeHost> runtimeHost;
private Mock<IServiceProxy> service;
private EventWaitHandle serviceEvent;
private SessionConfiguration session;
private SessionContext sessionContext;
private AppSettings settings;
private Mock<IUserInfo> userInfo;
private ServiceOperation sut;
@ -43,37 +40,35 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
{
var serviceEventName = $"{nameof(SafeExamBrowser)}-{nameof(ServiceOperationTests)}";
appConfig = new AppConfig();
logger = new Mock<ILogger>();
runtimeHost = new Mock<IRuntimeHost>();
service = new Mock<IServiceProxy>();
serviceEvent = new EventWaitHandle(false, EventResetMode.AutoReset, serviceEventName);
session = new SessionConfiguration();
sessionContext = new SessionContext();
settings = new AppSettings();
context = new SessionContext();
userInfo = new Mock<IUserInfo>();
appConfig.ServiceEventName = serviceEventName;
sessionContext.Current = session;
sessionContext.Current.AppConfig = appConfig;
sessionContext.Next = session;
sessionContext.Next.AppConfig = appConfig;
session.Settings = settings;
settings.Service.Policy = ServicePolicy.Mandatory;
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, sessionContext, 0, userInfo.Object);
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);
settings.Service.Policy = ServicePolicy.Mandatory;
context.Next.Settings.Service.Policy = ServicePolicy.Mandatory;
sut.Perform();
service.Setup(s => s.Connect(null, true)).Returns(true);
settings.Service.Policy = ServicePolicy.Optional;
context.Next.Settings.Service.Policy = ServicePolicy.Optional;
sut.Perform();
@ -83,13 +78,15 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
[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.IsAny<ServiceConfiguration>()), Times.Once);
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);
@ -123,7 +120,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
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, sessionContext, TIMEOUT, userInfo.Object);
sut = new ServiceOperation(logger.Object, runtimeHost.Object, service.Object, context, TIMEOUT, userInfo.Object);
before = DateTime.Now;
var result = sut.Perform();
@ -138,12 +135,15 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
[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);
sut.Perform();
var result = sut.Perform();
service.Verify(s => s.StartSession(It.IsAny<ServiceConfiguration>()), Times.Never);
Assert.AreEqual(OperationResult.Failed, result);
}
[TestMethod]
@ -165,9 +165,9 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
{
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);
settings.Service.Policy = ServicePolicy.Mandatory;
sut.ActionRequired += (args) => errorShown = args is MessageEventArgs m && m.Icon == MessageBoxIcon.Error;
var result = sut.Perform();
@ -179,13 +179,14 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
[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);
settings.Service.Policy = ServicePolicy.Optional;
var result = sut.Perform();
service.VerifySet(s => s.Ignore = true);
service.Verify(s => s.StartSession(It.IsAny<ServiceConfiguration>()), Times.Never);
Assert.AreEqual(OperationResult.Success, result);
}
@ -194,9 +195,9 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
{
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);
settings.Service.Policy = ServicePolicy.Warn;
sut.ActionRequired += (args) => warningShown = args is MessageEventArgs m && m.Icon == MessageBoxIcon.Warning;
var result = sut.Perform();
@ -208,18 +209,47 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
[TestMethod]
public void Repeat_MustStopCurrentAndStartNewSession()
{
service.Setup(s => s.StopSession(It.IsAny<Guid>())).Returns(new CommunicationResult(true)).Callback(() => serviceEvent.Set());
PerformNormally();
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.IsAny<Guid>()), Times.Once);
service.Verify(s => s.StartSession(It.IsAny<ServiceConfiguration>()), Times.Exactly(2));
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]
@ -229,12 +259,13 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
service.Reset();
service.SetupGet(s => s.IsConnected).Returns(false);
service.Setup(s => s.Connect(null, true)).Returns(true);
service.Setup(s => s.StopSession(It.IsAny<Guid>())).Returns(new CommunicationResult(true));
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);
}
@ -264,7 +295,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
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, sessionContext, TIMEOUT, userInfo.Object);
sut = new ServiceOperation(logger.Object, runtimeHost.Object, service.Object, context, TIMEOUT, userInfo.Object);
PerformNormally();
@ -279,6 +310,19 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
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()
{
@ -295,24 +339,6 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Revert_MustStopSessionIfConnected()
{
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.IsAny<Guid>()), 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_MustHandleCommunicationFailureWhenStoppingSession()
{
@ -369,7 +395,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
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, sessionContext, TIMEOUT, userInfo.Object);
sut = new ServiceOperation(logger.Object, runtimeHost.Object, service.Object, context, TIMEOUT, userInfo.Object);
PerformNormally();
@ -391,13 +417,15 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
var result = sut.Revert();
service.Verify(s => s.StopSession(It.IsAny<Guid>()), Times.Never);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Revert_MustNotStopSessionIfNoSessionRunning()
{
sessionContext.Current = null;
context.Current = null;
context.Next = null;
service.SetupGet(s => s.IsConnected).Returns(true);
service.Setup(s => s.Disconnect()).Returns(true);
@ -417,6 +445,50 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
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);

View file

@ -12,12 +12,12 @@ using System.Threading;
using SafeExamBrowser.Communication.Contracts.Hosts;
using SafeExamBrowser.Communication.Contracts.Proxies;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Settings.Service;
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.Settings.Service;
using SafeExamBrowser.SystemComponents.Contracts;
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
@ -28,6 +28,8 @@ namespace SafeExamBrowser.Runtime.Operations
private ILogger logger;
private IRuntimeHost runtimeHost;
private IServiceProxy service;
private string serviceEventName;
private Guid? sessionId;
private int timeout_ms;
private IUserInfo userInfo;
@ -54,9 +56,9 @@ namespace SafeExamBrowser.Runtime.Operations
logger.Info($"Initializing service...");
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeServiceSession);
var success = TryEstablishConnection();
var success = TryInitializeConnection();
if (service.IsConnected)
if (success && service.IsConnected)
{
success = TryStartSession();
}
@ -69,15 +71,15 @@ namespace SafeExamBrowser.Runtime.Operations
logger.Info($"Initializing service...");
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeServiceSession);
var success = false;
var success = true;
if (service.IsConnected)
if (service.IsConnected && sessionId.HasValue)
{
success = TryStopSession();
}
else
else if (!service.IsConnected)
{
success = TryEstablishConnection();
success = TryInitializeConnection();
}
if (success && service.IsConnected)
@ -97,7 +99,7 @@ namespace SafeExamBrowser.Runtime.Operations
if (service.IsConnected)
{
if (Context.Current != null)
if (sessionId.HasValue)
{
success &= TryStopSession(true);
}
@ -108,7 +110,7 @@ namespace SafeExamBrowser.Runtime.Operations
return success ? OperationResult.Success : OperationResult.Failed;
}
private bool TryEstablishConnection()
private bool TryInitializeConnection()
{
var mandatory = Context.Next.Settings.Service.Policy == ServicePolicy.Mandatory;
var warn = Context.Next.Settings.Service.Policy == ServicePolicy.Warn;
@ -117,8 +119,7 @@ namespace SafeExamBrowser.Runtime.Operations
if (success)
{
service.Ignore = !connected;
logger.Info($"The service is {(mandatory ? "mandatory" : "optional")} and {(connected ? "connected." : "not connected. All service-related operations will be ignored!")}");
logger.Info($"The service is {(mandatory ? "mandatory" : "optional")} and {(connected ? "connected." : "not connected.")}");
if (!connected && warn)
{
@ -182,6 +183,8 @@ namespace SafeExamBrowser.Runtime.Operations
if (started)
{
sessionId = Context.Next.SessionId;
serviceEventName = Context.Next.AppConfig.ServiceEventName;
logger.Info("Successfully started new service session.");
}
else
@ -203,14 +206,16 @@ namespace SafeExamBrowser.Runtime.Operations
logger.Info("Stopping current service session...");
var communication = service.StopSession(Context.Current.SessionId);
var communication = service.StopSession(sessionId.Value);
if (communication.Success)
{
success = TryWaitForServiceEvent(Context.Current.AppConfig.ServiceEventName);
success = TryWaitForServiceEvent(serviceEventName);
if (success)
{
sessionId = default(Guid?);
serviceEventName = default(string);
logger.Info("Successfully stopped service session.");
}
else