SEBWIN-219: Decoupled proxy object creation from BaseProxy and implemented unit tests for the latter.

This commit is contained in:
dbuechel 2018-03-15 09:55:04 +01:00
parent b206b0d5be
commit 17c068de6f
16 changed files with 366 additions and 48 deletions

View file

@ -72,7 +72,7 @@ namespace SafeExamBrowser.Client
text = new Text(logger); text = new Text(logger);
messageBox = new MessageBox(text); messageBox = new MessageBox(text);
uiFactory = new UserInterfaceFactory(text); uiFactory = new UserInterfaceFactory(text);
runtimeProxy = new RuntimeProxy(runtimeHostUri, new ModuleLogger(logger, typeof(RuntimeProxy))); runtimeProxy = new RuntimeProxy(runtimeHostUri, new ProxyObjectFactory(), new ModuleLogger(logger, typeof(RuntimeProxy)));
var displayMonitor = new DisplayMonitor(new ModuleLogger(logger, typeof(DisplayMonitor)), nativeMethods); var displayMonitor = new DisplayMonitor(new ModuleLogger(logger, typeof(DisplayMonitor)), nativeMethods);
var processMonitor = new ProcessMonitor(new ModuleLogger(logger, typeof(ProcessMonitor)), nativeMethods); var processMonitor = new ProcessMonitor(new ModuleLogger(logger, typeof(ProcessMonitor)), nativeMethods);

View file

@ -17,20 +17,21 @@ namespace SafeExamBrowser.Contracts.Communication
public interface ICommunicationProxy public interface ICommunicationProxy
{ {
/// <summary> /// <summary>
/// Fired when the connection to the proxy was lost, e.g. if a ping request failed or a communication fault occurred. /// Fired when the connection to the host was lost, e.g. if a ping request failed or a communication fault occurred.
/// </summary> /// </summary>
event CommunicationEventHandler ConnectionLost; event CommunicationEventHandler ConnectionLost;
/// <summary> /// <summary>
/// Tries to establish a connection. Returns <c>true</c> if the connection has been accepted, otherwise <c>false</c>. If a /// Tries to establish a connection. Returns <c>true</c> if the connection has been accepted, otherwise <c>false</c>. If a
/// connection was successfully established, a ping mechanism will be activated to periodically check the connection status. /// connection was successfully established and the auto-ping flag is set, the connection status will be periodically checked.
/// </summary> /// </summary>
/// <exception cref="System.ServiceModel.*">If the communication failed.</exception> /// <exception cref="System.ServiceModel.*">If the communication failed.</exception>
bool Connect(Guid? token = null); bool Connect(Guid? token = null, bool autoPing = true);
/// <summary> /// <summary>
/// Terminates an open connection. Returns <c>true</c> if the disconnection has been acknowledged, otherwise <c>false</c>. /// Terminates an open connection. Returns <c>true</c> if the disconnection has been acknowledged, otherwise <c>false</c>.
/// </summary> /// </summary>
/// <exception cref="InvalidOperationException">If no connection has been established.</exception>
/// <exception cref="System.ServiceModel.*">If the communication failed.</exception> /// <exception cref="System.ServiceModel.*">If the communication failed.</exception>
bool Disconnect(); bool Disconnect();
} }

View file

@ -0,0 +1,21 @@
/*
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
namespace SafeExamBrowser.Contracts.Communication
{
/// <summary>
/// A factory to create communication objects for proxies.
/// </summary>
public interface IProxyObjectFactory
{
/// <summary>
/// Creates a communication object (see <see cref="ICommunication"/>) for the specified endpoint address.
/// </summary>
ICommunication CreateObject(string address);
}
}

View file

@ -62,6 +62,7 @@
<Compile Include="Communication\IClientProxy.cs" /> <Compile Include="Communication\IClientProxy.cs" />
<Compile Include="Communication\ICommunicationHost.cs" /> <Compile Include="Communication\ICommunicationHost.cs" />
<Compile Include="Communication\ICommunicationProxy.cs" /> <Compile Include="Communication\ICommunicationProxy.cs" />
<Compile Include="Communication\IProxyObjectFactory.cs" />
<Compile Include="Communication\IProxyFactory.cs" /> <Compile Include="Communication\IProxyFactory.cs" />
<Compile Include="Communication\IRuntimeHost.cs" /> <Compile Include="Communication\IRuntimeHost.cs" />
<Compile Include="Communication\IRuntimeProxy.cs" /> <Compile Include="Communication\IRuntimeProxy.cs" />

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
using SafeExamBrowser.Contracts.Communication;
using SafeExamBrowser.Contracts.Communication.Messages;
using SafeExamBrowser.Contracts.Communication.Responses;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Core.Communication;
namespace SafeExamBrowser.Core.UnitTests.Communication
{
internal class BaseProxyImpl : BaseProxy
{
public BaseProxyImpl(string address, IProxyObjectFactory factory, ILogger logger) : base(address, factory, logger)
{
}
public override bool Connect(Guid? token = null, bool autoPing = false)
{
return base.Connect(token, autoPing);
}
public override bool Disconnect()
{
return base.Disconnect();
}
public new Response Send(Message message)
{
return base.Send(message);
}
}
}

View file

@ -0,0 +1,197 @@
/*
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
using System.ServiceModel;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Contracts.Communication;
using SafeExamBrowser.Contracts.Communication.Messages;
using SafeExamBrowser.Contracts.Communication.Responses;
using SafeExamBrowser.Contracts.Logging;
namespace SafeExamBrowser.Core.UnitTests.Communication
{
[TestClass]
public class BaseProxyTests
{
private Mock<IProxyObjectFactory> proxyObjectFactory;
private Mock<ILogger> logger;
private BaseProxyImpl sut;
[TestInitialize]
public void Initialize()
{
proxyObjectFactory = new Mock<IProxyObjectFactory>();
logger = new Mock<ILogger>();
sut = new BaseProxyImpl("net.pipe://some/address/here", proxyObjectFactory.Object, logger.Object);
}
[TestMethod]
public void MustConnectCorrectly()
{
var proxy = new Mock<ICommunication>();
var response = new ConnectionResponse
{
CommunicationToken = Guid.NewGuid(),
ConnectionEstablished = true
};
proxy.Setup(p => p.Connect(It.IsAny<Guid>())).Returns(response);
proxyObjectFactory.Setup(f => f.CreateObject(It.IsAny<string>())).Returns(proxy.Object);
var token = Guid.NewGuid();
var connected = sut.Connect(token);
proxy.Verify(p => p.Connect(token), Times.Once);
proxyObjectFactory.Verify(f => f.CreateObject(It.IsAny<string>()), Times.Once);
Assert.IsTrue(connected);
}
[TestMethod]
public void MustDisconnectCorrectly()
{
var proxy = new Mock<ICommunication>();
var connectionResponse = new ConnectionResponse
{
CommunicationToken = Guid.NewGuid(),
ConnectionEstablished = true
};
var disconnectionResponse = new DisconnectionResponse
{
ConnectionTerminated = true
};
proxy.Setup(p => p.Connect(It.IsAny<Guid>())).Returns(connectionResponse);
proxy.Setup(p => p.Disconnect(It.IsAny<DisconnectionMessage>())).Returns(disconnectionResponse);
proxy.As<ICommunicationObject>().Setup(o => o.State).Returns(CommunicationState.Opened);
proxyObjectFactory.Setup(f => f.CreateObject(It.IsAny<string>())).Returns(proxy.Object);
var token = Guid.NewGuid();
var connected = sut.Connect(token);
var disconnected = sut.Disconnect();
proxy.Verify(p => p.Disconnect(It.Is<DisconnectionMessage>(m => m.CommunicationToken == connectionResponse.CommunicationToken)), Times.Once);
Assert.IsTrue(connected);
Assert.IsTrue(disconnected);
}
[TestMethod]
public void MustHandleConnectionRefusalCorrectly()
{
var proxy = new Mock<ICommunication>();
var response = new ConnectionResponse
{
CommunicationToken = Guid.NewGuid(),
ConnectionEstablished = false
};
proxy.Setup(p => p.Connect(It.IsAny<Guid>())).Returns(response);
proxyObjectFactory.Setup(f => f.CreateObject(It.IsAny<string>())).Returns(proxy.Object);
var token = Guid.NewGuid();
var connected = sut.Connect(token);
proxy.Verify(p => p.Connect(token), Times.Once);
proxyObjectFactory.Verify(f => f.CreateObject(It.IsAny<string>()), Times.Once);
Assert.IsFalse(connected);
}
[TestMethod]
[ExpectedException(typeof(InvalidOperationException))]
public void MustFailToDisconnectIfNotConnected()
{
sut.Disconnect();
}
[TestMethod]
[ExpectedException(typeof(CommunicationException))]
public void MustFailToDisconnectIfChannelNotOpen()
{
var proxy = new Mock<ICommunication>();
var response = new ConnectionResponse
{
CommunicationToken = Guid.NewGuid(),
ConnectionEstablished = true
};
proxy.Setup(p => p.Connect(It.IsAny<Guid>())).Returns(response);
proxy.As<ICommunicationObject>().Setup(o => o.State).Returns(CommunicationState.Faulted);
proxyObjectFactory.Setup(f => f.CreateObject(It.IsAny<string>())).Returns(proxy.Object);
var token = Guid.NewGuid();
sut.Connect(token);
sut.Disconnect();
}
[TestMethod]
[ExpectedException(typeof(InvalidOperationException))]
public void MustFailToSendIfNotConnected()
{
sut.Send(new Mock<Message>().Object);
}
[TestMethod]
[ExpectedException(typeof(CommunicationException))]
public void MustFailToSendIfChannelNotOpen()
{
var proxy = new Mock<ICommunication>();
var response = new ConnectionResponse
{
CommunicationToken = Guid.NewGuid(),
ConnectionEstablished = true
};
proxy.Setup(p => p.Connect(It.IsAny<Guid>())).Returns(response);
proxy.As<ICommunicationObject>().Setup(o => o.State).Returns(CommunicationState.Faulted);
proxyObjectFactory.Setup(f => f.CreateObject(It.IsAny<string>())).Returns(proxy.Object);
var token = Guid.NewGuid();
sut.Connect(token);
sut.Send(new Mock<Message>().Object);
}
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void MustNotAllowSendingNull()
{
sut.Send(null);
}
[TestMethod]
public void MustSendCorrectly()
{
var proxy = new Mock<ICommunication>();
var connectionResponse = new ConnectionResponse
{
CommunicationToken = Guid.NewGuid(),
ConnectionEstablished = true
};
var message = new SimpleMessage(SimpleMessagePurport.Authenticate);
var response = new Mock<Response>();
proxy.Setup(p => p.Connect(It.IsAny<Guid>())).Returns(connectionResponse);
proxy.Setup(p => p.Send(message)).Returns(response.Object);
proxy.As<ICommunicationObject>().Setup(o => o.State).Returns(CommunicationState.Opened);
proxyObjectFactory.Setup(f => f.CreateObject(It.IsAny<string>())).Returns(proxy.Object);
var token = Guid.NewGuid();
var connected = sut.Connect(token);
var received = sut.Send(message);
Assert.AreEqual(response.Object, received);
Assert.AreEqual(connectionResponse.CommunicationToken, message.CommunicationToken);
}
}
}

View file

@ -69,6 +69,7 @@
<HintPath>..\packages\Moq.4.8.1\lib\net45\Moq.dll</HintPath> <HintPath>..\packages\Moq.4.8.1\lib\net45\Moq.dll</HintPath>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.ServiceModel" />
<Reference Include="System.Threading.Tasks.Extensions, Version=4.1.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> <Reference Include="System.Threading.Tasks.Extensions, Version=4.1.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Threading.Tasks.Extensions.4.4.0\lib\portable-net45+win8+wp8+wpa81\System.Threading.Tasks.Extensions.dll</HintPath> <HintPath>..\packages\System.Threading.Tasks.Extensions.4.4.0\lib\portable-net45+win8+wp8+wpa81\System.Threading.Tasks.Extensions.dll</HintPath>
</Reference> </Reference>
@ -83,6 +84,8 @@
<Compile Include="Behaviour\OperationModel\I18nOperationTests.cs" /> <Compile Include="Behaviour\OperationModel\I18nOperationTests.cs" />
<Compile Include="Behaviour\OperationModel\DelegateOperationTests.cs" /> <Compile Include="Behaviour\OperationModel\DelegateOperationTests.cs" />
<Compile Include="Behaviour\OperationModel\OperationSequenceTests.cs" /> <Compile Include="Behaviour\OperationModel\OperationSequenceTests.cs" />
<Compile Include="Communication\BaseProxyImpl.cs" />
<Compile Include="Communication\BaseProxyTests.cs" />
<Compile Include="I18n\TextTests.cs" /> <Compile Include="I18n\TextTests.cs" />
<Compile Include="I18n\XmlTextResourceTests.cs" /> <Compile Include="I18n\XmlTextResourceTests.cs" />
<Compile Include="Logging\DefaultLogFormatterTests.cs" /> <Compile Include="Logging\DefaultLogFormatterTests.cs" />

View file

@ -17,8 +17,7 @@ using SafeExamBrowser.Contracts.Logging;
namespace SafeExamBrowser.Core.Communication namespace SafeExamBrowser.Core.Communication
{ {
/// <summary> /// <summary>
/// Base implementation of an <see cref="ICommunicationProxy"/>. Automatically starts a ping mechanism with a timeout of /// Base implementation of an <see cref="ICommunicationProxy"/>.
/// <see cref="ONE_MINUTE"/> once a connection was established.
/// </summary> /// </summary>
public abstract class BaseProxy : ICommunicationProxy public abstract class BaseProxy : ICommunicationProxy
{ {
@ -26,7 +25,8 @@ namespace SafeExamBrowser.Core.Communication
private static readonly object @lock = new object(); private static readonly object @lock = new object();
private string address; private string address;
private ICommunication channel; private ICommunication proxy;
private IProxyObjectFactory factory;
private Guid? communicationToken; private Guid? communicationToken;
private Timer timer; private Timer timer;
@ -34,31 +34,34 @@ namespace SafeExamBrowser.Core.Communication
public event CommunicationEventHandler ConnectionLost; public event CommunicationEventHandler ConnectionLost;
public BaseProxy(string address, ILogger logger) public BaseProxy(string address, IProxyObjectFactory factory, ILogger logger)
{ {
this.address = address; this.address = address;
this.factory = factory;
this.Logger = logger; this.Logger = logger;
} }
public virtual bool Connect(Guid? token = null) public virtual bool Connect(Guid? token = null, bool autoPing = true)
{ {
var endpoint = new EndpointAddress(address); proxy = factory.CreateObject(address);
channel = ChannelFactory<ICommunication>.CreateChannel(new NetNamedPipeBinding(NetNamedPipeSecurityMode.Transport), endpoint); if (proxy is ICommunicationObject communicationObject)
(channel as ICommunicationObject).Closed += BaseProxy_Closed; {
(channel as ICommunicationObject).Closing += BaseProxy_Closing; communicationObject.Closed += BaseProxy_Closed;
(channel as ICommunicationObject).Faulted += BaseProxy_Faulted; communicationObject.Closing += BaseProxy_Closing;
(channel as ICommunicationObject).Opened += BaseProxy_Opened; communicationObject.Faulted += BaseProxy_Faulted;
(channel as ICommunicationObject).Opening += BaseProxy_Opening; communicationObject.Opened += BaseProxy_Opened;
communicationObject.Opening += BaseProxy_Opening;
}
Logger.Debug($"Trying to connect to endpoint '{address}'{(token.HasValue ? $" with authentication token '{token}'" : string.Empty)}..."); Logger.Debug($"Trying to connect to endpoint '{address}'{(token.HasValue ? $" with authentication token '{token}'" : string.Empty)}...");
var response = channel.Connect(token); var response = proxy.Connect(token);
communicationToken = response.CommunicationToken; communicationToken = response.CommunicationToken;
Logger.Debug($"Connection was {(response.ConnectionEstablished ? "established" : "refused")}."); Logger.Debug($"Connection was {(response.ConnectionEstablished ? "established" : "refused")}.");
if (response.ConnectionEstablished) if (response.ConnectionEstablished && autoPing)
{ {
StartAutoPing(); StartAutoPing();
} }
@ -72,41 +75,64 @@ namespace SafeExamBrowser.Core.Communication
StopAutoPing(); StopAutoPing();
var message = new DisconnectionMessage { CommunicationToken = communicationToken.Value }; var message = new DisconnectionMessage { CommunicationToken = communicationToken.Value };
var response = channel.Disconnect(message); var response = proxy.Disconnect(message);
Logger.Debug($"{(response.ConnectionTerminated ? "Disconnected" : "Failed to disconnect")} from {address}."); Logger.Debug($"{(response.ConnectionTerminated ? "Disconnected" : "Failed to disconnect")} from {address}.");
return response.ConnectionTerminated; return response.ConnectionTerminated;
} }
protected Response Send(Message message) /// <summary>
/// Sends the given message, optionally returning a response. If no response is expected, <c>null</c> will be returned.
/// </summary>
/// <exception cref="ArgumentNullException">If the given message is <c>null</c>.</exception>
/// <exception cref="InvalidOperationException">If no connection has been established yet.</exception>
/// <exception cref="System.ServiceModel.*">If the communication failed.</exception>
protected virtual Response Send(Message message)
{ {
if (message is null)
{
throw new ArgumentNullException(nameof(message));
}
FailIfNotConnected(nameof(Send)); FailIfNotConnected(nameof(Send));
message.CommunicationToken = communicationToken.Value; message.CommunicationToken = communicationToken.Value;
var response = channel.Send(message); var response = proxy.Send(message);
Logger.Debug($"Sent message '{ToString(message)}', got response '{ToString(response)}'."); Logger.Debug($"Sent message '{ToString(message)}', got response '{ToString(response)}'.");
return response; return response;
} }
/// <summary>
/// Sends the given purport as <see cref="SimpleMessage"/>.
/// </summary>
protected Response Send(SimpleMessagePurport purport) protected Response Send(SimpleMessagePurport purport)
{ {
return Send(new SimpleMessage(purport)); return Send(new SimpleMessage(purport));
} }
/// <summary>
/// Determines whether the given response is a <see cref="SimpleResponse"/> with purport <see cref="SimpleResponsePurport.Acknowledged"/>.
/// </summary>
protected bool IsAcknowledged(Response response) protected bool IsAcknowledged(Response response)
{ {
return response is SimpleResponse simpleResponse && simpleResponse.Purport == SimpleResponsePurport.Acknowledged; return response is SimpleResponse simpleResponse && simpleResponse.Purport == SimpleResponsePurport.Acknowledged;
} }
/// <summary>
/// Retrieves the string representation of the given <see cref="Message"/>, or indicates that a message is <c>null</c>.
/// </summary>
protected string ToString(Message message) protected string ToString(Message message)
{ {
return message != null ? message.ToString() : "<null>"; return message != null ? message.ToString() : "<null>";
} }
/// <summary>
/// etrieves the string representation of the given <see cref="Response"/>, or indicates that a response is <c>null</c>.
/// </summary>
protected string ToString(Response response) protected string ToString(Response response)
{ {
return response != null ? response.ToString() : "<null>"; return response != null ? response.ToString() : "<null>";
@ -144,7 +170,7 @@ namespace SafeExamBrowser.Core.Communication
throw new InvalidOperationException($"Cannot perform '{operationName}' before being connected to endpoint!"); throw new InvalidOperationException($"Cannot perform '{operationName}' before being connected to endpoint!");
} }
if (channel == null || (channel as ICommunicationObject).State != CommunicationState.Opened) if (proxy == null || (proxy as ICommunicationObject)?.State != CommunicationState.Opened)
{ {
throw new CommunicationException($"Tried to perform {operationName}, but channel was {GetChannelState()}!"); throw new CommunicationException($"Tried to perform {operationName}, but channel was {GetChannelState()}!");
} }
@ -152,7 +178,7 @@ namespace SafeExamBrowser.Core.Communication
private string GetChannelState() private string GetChannelState()
{ {
return channel == null ? "null" : $"in state '{(channel as ICommunicationObject).State}'"; return proxy == null ? "null" : $"in state '{(proxy as ICommunicationObject)?.State}'";
} }
private void StartAutoPing() private void StartAutoPing()

View file

@ -19,7 +19,7 @@ namespace SafeExamBrowser.Core.Communication
/// </summary> /// </summary>
public class ClientProxy : BaseProxy, IClientProxy public class ClientProxy : BaseProxy, IClientProxy
{ {
public ClientProxy(string address, ILogger logger) : base(address, logger) public ClientProxy(string address, IProxyObjectFactory factory, ILogger logger) : base(address, factory, logger)
{ {
} }

View file

@ -0,0 +1,27 @@
/*
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System.ServiceModel;
using SafeExamBrowser.Contracts.Communication;
namespace SafeExamBrowser.Core.Communication
{
/// <summary>
/// Default implementation of the <see cref="IProxyObjectFactory"/> utilizing WCF (<see cref="ChannelFactory"/>).
/// </summary>
public class ProxyObjectFactory : IProxyObjectFactory
{
public ICommunication CreateObject(string address)
{
var endpoint = new EndpointAddress(address);
var channel = ChannelFactory<ICommunication>.CreateChannel(new NetNamedPipeBinding(NetNamedPipeSecurityMode.Transport), endpoint);
return channel;
}
}
}

View file

@ -20,7 +20,7 @@ namespace SafeExamBrowser.Core.Communication
/// </summary> /// </summary>
public class RuntimeProxy : BaseProxy, IRuntimeProxy public class RuntimeProxy : BaseProxy, IRuntimeProxy
{ {
public RuntimeProxy(string address, ILogger logger) : base(address, logger) public RuntimeProxy(string address, IProxyObjectFactory factory, ILogger logger) : base(address, factory, logger)
{ {
} }

View file

@ -20,18 +20,18 @@ namespace SafeExamBrowser.Core.Communication
{ {
public bool Ignore { private get; set; } public bool Ignore { private get; set; }
public ServiceProxy(string address, ILogger logger) : base(address, logger) public ServiceProxy(string address, IProxyObjectFactory factory, ILogger logger) : base(address, factory, logger)
{ {
} }
public override bool Connect(Guid? token = null) public override bool Connect(Guid? token = null, bool autoPing = true)
{ {
if (IgnoreOperation(nameof(Connect))) if (IgnoreOperation(nameof(Connect)))
{ {
return false; return false;
} }
return base.Connect(); return base.Connect(autoPing: autoPing);
} }
public override bool Disconnect() public override bool Disconnect()

View file

@ -63,6 +63,7 @@
<Compile Include="Communication\BaseProxy.cs" /> <Compile Include="Communication\BaseProxy.cs" />
<Compile Include="Communication\BaseHost.cs" /> <Compile Include="Communication\BaseHost.cs" />
<Compile Include="Communication\ClientProxy.cs" /> <Compile Include="Communication\ClientProxy.cs" />
<Compile Include="Communication\ProxyObjectFactory.cs" />
<Compile Include="Communication\RuntimeProxy.cs" /> <Compile Include="Communication\RuntimeProxy.cs" />
<Compile Include="Communication\ServiceProxy.cs" /> <Compile Include="Communication\ServiceProxy.cs" />
<Compile Include="Logging\DefaultLogFormatter.cs" /> <Compile Include="Logging\DefaultLogFormatter.cs" />

View file

@ -45,38 +45,38 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
[TestMethod] [TestMethod]
public void MustConnectToService() public void MustConnectToService()
{ {
service.Setup(s => s.Connect(null)).Returns(true); service.Setup(s => s.Connect(null, true)).Returns(true);
configuration.SetupGet(s => s.CurrentSettings).Returns(new Settings { ServicePolicy = ServicePolicy.Mandatory }); configuration.SetupGet(s => s.CurrentSettings).Returns(new Settings { ServicePolicy = ServicePolicy.Mandatory });
sut.Perform(); sut.Perform();
service.Setup(s => s.Connect(null)).Returns(true); service.Setup(s => s.Connect(null, true)).Returns(true);
configuration.SetupGet(s => s.CurrentSettings).Returns(new Settings { ServicePolicy = ServicePolicy.Optional }); configuration.SetupGet(s => s.CurrentSettings).Returns(new Settings { ServicePolicy = ServicePolicy.Optional });
sut.Perform(); sut.Perform();
service.Verify(s => s.Connect(null), Times.Exactly(2)); service.Verify(s => s.Connect(null, true), Times.Exactly(2));
} }
[TestMethod] [TestMethod]
public void MustNotFailIfServiceNotAvailable() public void MustNotFailIfServiceNotAvailable()
{ {
service.Setup(s => s.Connect(null)).Returns(false); service.Setup(s => s.Connect(null, true)).Returns(false);
configuration.SetupGet(s => s.CurrentSettings).Returns(new Settings { ServicePolicy = ServicePolicy.Mandatory }); configuration.SetupGet(s => s.CurrentSettings).Returns(new Settings { ServicePolicy = ServicePolicy.Mandatory });
sut.Perform(); sut.Perform();
service.Setup(s => s.Connect(null)).Returns(false); service.Setup(s => s.Connect(null, true)).Returns(false);
configuration.SetupGet(s => s.CurrentSettings).Returns(new Settings { ServicePolicy = ServicePolicy.Optional }); configuration.SetupGet(s => s.CurrentSettings).Returns(new Settings { ServicePolicy = ServicePolicy.Optional });
sut.Perform(); sut.Perform();
service.Setup(s => s.Connect(null)).Throws<Exception>(); service.Setup(s => s.Connect(null, true)).Throws<Exception>();
configuration.SetupGet(s => s.CurrentSettings).Returns(new Settings { ServicePolicy = ServicePolicy.Mandatory }); configuration.SetupGet(s => s.CurrentSettings).Returns(new Settings { ServicePolicy = ServicePolicy.Mandatory });
sut.Perform(); sut.Perform();
service.Setup(s => s.Connect(null)).Throws<Exception>(); service.Setup(s => s.Connect(null, true)).Throws<Exception>();
configuration.SetupGet(s => s.CurrentSettings).Returns(new Settings { ServicePolicy = ServicePolicy.Optional }); configuration.SetupGet(s => s.CurrentSettings).Returns(new Settings { ServicePolicy = ServicePolicy.Optional });
sut.Perform(); sut.Perform();
@ -85,7 +85,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
[TestMethod] [TestMethod]
public void MustFailIfServiceMandatoryAndNotAvailable() public void MustFailIfServiceMandatoryAndNotAvailable()
{ {
service.Setup(s => s.Connect(null)).Returns(false); service.Setup(s => s.Connect(null, true)).Returns(false);
configuration.SetupGet(s => s.CurrentSettings).Returns(new Settings { ServicePolicy = ServicePolicy.Mandatory }); configuration.SetupGet(s => s.CurrentSettings).Returns(new Settings { ServicePolicy = ServicePolicy.Mandatory });
var result = sut.Perform(); var result = sut.Perform();
@ -96,7 +96,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
[TestMethod] [TestMethod]
public void MustNotFailIfServiceOptionalAndNotAvailable() public void MustNotFailIfServiceOptionalAndNotAvailable()
{ {
service.Setup(s => s.Connect(null)).Returns(false); service.Setup(s => s.Connect(null, true)).Returns(false);
configuration.SetupGet(s => s.CurrentSettings).Returns(new Settings { ServicePolicy = ServicePolicy.Optional }); configuration.SetupGet(s => s.CurrentSettings).Returns(new Settings { ServicePolicy = ServicePolicy.Optional });
var result = sut.Perform(); var result = sut.Perform();
@ -109,13 +109,13 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
[TestMethod] [TestMethod]
public void MustDisconnectWhenReverting() public void MustDisconnectWhenReverting()
{ {
service.Setup(s => s.Connect(null)).Returns(true); service.Setup(s => s.Connect(null, true)).Returns(true);
configuration.SetupGet(s => s.CurrentSettings).Returns(new Settings { ServicePolicy = ServicePolicy.Mandatory }); configuration.SetupGet(s => s.CurrentSettings).Returns(new Settings { ServicePolicy = ServicePolicy.Mandatory });
sut.Perform(); sut.Perform();
sut.Revert(); sut.Revert();
service.Setup(s => s.Connect(null)).Returns(true); service.Setup(s => s.Connect(null, true)).Returns(true);
configuration.SetupGet(s => s.CurrentSettings).Returns(new Settings { ServicePolicy = ServicePolicy.Optional }); configuration.SetupGet(s => s.CurrentSettings).Returns(new Settings { ServicePolicy = ServicePolicy.Optional });
sut.Perform(); sut.Perform();
@ -127,7 +127,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
[TestMethod] [TestMethod]
public void MustNotFailWhenDisconnecting() public void MustNotFailWhenDisconnecting()
{ {
service.Setup(s => s.Connect(null)).Returns(true); service.Setup(s => s.Connect(null, true)).Returns(true);
service.Setup(s => s.Disconnect()).Throws<Exception>(); service.Setup(s => s.Disconnect()).Throws<Exception>();
configuration.SetupGet(s => s.CurrentSettings).Returns(new Settings { ServicePolicy = ServicePolicy.Optional }); configuration.SetupGet(s => s.CurrentSettings).Returns(new Settings { ServicePolicy = ServicePolicy.Optional });
@ -140,25 +140,25 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
[TestMethod] [TestMethod]
public void MustNotDisconnnectIfNotAvailable() public void MustNotDisconnnectIfNotAvailable()
{ {
service.Setup(s => s.Connect(null)).Returns(false); service.Setup(s => s.Connect(null, true)).Returns(false);
configuration.SetupGet(s => s.CurrentSettings).Returns(new Settings { ServicePolicy = ServicePolicy.Mandatory }); configuration.SetupGet(s => s.CurrentSettings).Returns(new Settings { ServicePolicy = ServicePolicy.Mandatory });
sut.Perform(); sut.Perform();
sut.Revert(); sut.Revert();
service.Setup(s => s.Connect(null)).Returns(false); service.Setup(s => s.Connect(null, true)).Returns(false);
configuration.SetupGet(s => s.CurrentSettings).Returns(new Settings { ServicePolicy = ServicePolicy.Optional }); configuration.SetupGet(s => s.CurrentSettings).Returns(new Settings { ServicePolicy = ServicePolicy.Optional });
sut.Perform(); sut.Perform();
sut.Revert(); sut.Revert();
service.Setup(s => s.Connect(null)).Throws<Exception>(); service.Setup(s => s.Connect(null, true)).Throws<Exception>();
configuration.SetupGet(s => s.CurrentSettings).Returns(new Settings { ServicePolicy = ServicePolicy.Mandatory }); configuration.SetupGet(s => s.CurrentSettings).Returns(new Settings { ServicePolicy = ServicePolicy.Mandatory });
sut.Perform(); sut.Perform();
sut.Revert(); sut.Revert();
service.Setup(s => s.Connect(null)).Throws<Exception>(); service.Setup(s => s.Connect(null, true)).Throws<Exception>();
configuration.SetupGet(s => s.CurrentSettings).Returns(new Settings { ServicePolicy = ServicePolicy.Optional }); configuration.SetupGet(s => s.CurrentSettings).Returns(new Settings { ServicePolicy = ServicePolicy.Optional });
sut.Perform(); sut.Perform();

View file

@ -15,16 +15,18 @@ namespace SafeExamBrowser.Runtime.Communication
{ {
internal class ProxyFactory : IProxyFactory internal class ProxyFactory : IProxyFactory
{ {
private IProxyObjectFactory factory;
private ILogger logger; private ILogger logger;
public ProxyFactory(ILogger logger) public ProxyFactory(IProxyObjectFactory factory, ILogger logger)
{ {
this.factory = factory;
this.logger = logger; this.logger = logger;
} }
public IClientProxy CreateClientProxy(string address) public IClientProxy CreateClientProxy(string address)
{ {
return new ClientProxy(address, new ModuleLogger(logger, typeof(ClientProxy))); return new ClientProxy(address, factory, new ModuleLogger(logger, typeof(ClientProxy)));
} }
} }
} }

View file

@ -50,9 +50,9 @@ namespace SafeExamBrowser.Runtime
var uiFactory = new UserInterfaceFactory(text); var uiFactory = new UserInterfaceFactory(text);
var desktop = new Desktop(new ModuleLogger(logger, typeof(Desktop))); var desktop = new Desktop(new ModuleLogger(logger, typeof(Desktop)));
var processFactory = new ProcessFactory(desktop, new ModuleLogger(logger, typeof(ProcessFactory))); var processFactory = new ProcessFactory(desktop, new ModuleLogger(logger, typeof(ProcessFactory)));
var proxyFactory = new ProxyFactory(logger); var proxyFactory = new ProxyFactory(new ProxyObjectFactory(), logger);
var runtimeHost = new RuntimeHost(runtimeInfo.RuntimeAddress, configuration, new ModuleLogger(logger, typeof(RuntimeHost))); var runtimeHost = new RuntimeHost(runtimeInfo.RuntimeAddress, configuration, new ModuleLogger(logger, typeof(RuntimeHost)));
var serviceProxy = new ServiceProxy(runtimeInfo.ServiceAddress, new ModuleLogger(logger, typeof(ServiceProxy))); var serviceProxy = new ServiceProxy(runtimeInfo.ServiceAddress, new ProxyObjectFactory(), new ModuleLogger(logger, typeof(ServiceProxy)));
var sessionController = new SessionController(configuration, logger, processFactory, proxyFactory, runtimeHost, serviceProxy); var sessionController = new SessionController(configuration, logger, processFactory, proxyFactory, runtimeHost, serviceProxy);
var bootstrapOperations = new Queue<IOperation>(); var bootstrapOperations = new Queue<IOperation>();