diff --git a/SafeExamBrowser.Client/CompositionRoot.cs b/SafeExamBrowser.Client/CompositionRoot.cs index c40522de..7b8a9741 100644 --- a/SafeExamBrowser.Client/CompositionRoot.cs +++ b/SafeExamBrowser.Client/CompositionRoot.cs @@ -72,7 +72,7 @@ namespace SafeExamBrowser.Client text = new Text(logger); messageBox = new MessageBox(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 processMonitor = new ProcessMonitor(new ModuleLogger(logger, typeof(ProcessMonitor)), nativeMethods); diff --git a/SafeExamBrowser.Contracts/Communication/ICommunicationProxy.cs b/SafeExamBrowser.Contracts/Communication/ICommunicationProxy.cs index 2fd4d08c..152ae784 100644 --- a/SafeExamBrowser.Contracts/Communication/ICommunicationProxy.cs +++ b/SafeExamBrowser.Contracts/Communication/ICommunicationProxy.cs @@ -17,20 +17,21 @@ namespace SafeExamBrowser.Contracts.Communication public interface ICommunicationProxy { /// - /// 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. /// event CommunicationEventHandler ConnectionLost; /// /// Tries to establish a connection. Returns true if the connection has been accepted, otherwise false. 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. /// /// If the communication failed. - bool Connect(Guid? token = null); + bool Connect(Guid? token = null, bool autoPing = true); /// /// Terminates an open connection. Returns true if the disconnection has been acknowledged, otherwise false. /// + /// If no connection has been established. /// If the communication failed. bool Disconnect(); } diff --git a/SafeExamBrowser.Contracts/Communication/IProxyObjectFactory.cs b/SafeExamBrowser.Contracts/Communication/IProxyObjectFactory.cs new file mode 100644 index 00000000..a2565a2b --- /dev/null +++ b/SafeExamBrowser.Contracts/Communication/IProxyObjectFactory.cs @@ -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 +{ + /// + /// A factory to create communication objects for proxies. + /// + public interface IProxyObjectFactory + { + /// + /// Creates a communication object (see ) for the specified endpoint address. + /// + ICommunication CreateObject(string address); + } +} diff --git a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj index 29ab4299..4383dd9c 100644 --- a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj +++ b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj @@ -62,6 +62,7 @@ + diff --git a/SafeExamBrowser.Core.UnitTests/Communication/BaseProxyImpl.cs b/SafeExamBrowser.Core.UnitTests/Communication/BaseProxyImpl.cs new file mode 100644 index 00000000..cf0d2b7d --- /dev/null +++ b/SafeExamBrowser.Core.UnitTests/Communication/BaseProxyImpl.cs @@ -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); + } + } +} diff --git a/SafeExamBrowser.Core.UnitTests/Communication/BaseProxyTests.cs b/SafeExamBrowser.Core.UnitTests/Communication/BaseProxyTests.cs new file mode 100644 index 00000000..72e120df --- /dev/null +++ b/SafeExamBrowser.Core.UnitTests/Communication/BaseProxyTests.cs @@ -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 proxyObjectFactory; + private Mock logger; + private BaseProxyImpl sut; + + [TestInitialize] + public void Initialize() + { + proxyObjectFactory = new Mock(); + logger = new Mock(); + + sut = new BaseProxyImpl("net.pipe://some/address/here", proxyObjectFactory.Object, logger.Object); + } + + [TestMethod] + public void MustConnectCorrectly() + { + var proxy = new Mock(); + var response = new ConnectionResponse + { + CommunicationToken = Guid.NewGuid(), + ConnectionEstablished = true + }; + + proxy.Setup(p => p.Connect(It.IsAny())).Returns(response); + proxyObjectFactory.Setup(f => f.CreateObject(It.IsAny())).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()), Times.Once); + + Assert.IsTrue(connected); + } + + [TestMethod] + public void MustDisconnectCorrectly() + { + var proxy = new Mock(); + var connectionResponse = new ConnectionResponse + { + CommunicationToken = Guid.NewGuid(), + ConnectionEstablished = true + }; + var disconnectionResponse = new DisconnectionResponse + { + ConnectionTerminated = true + }; + + proxy.Setup(p => p.Connect(It.IsAny())).Returns(connectionResponse); + proxy.Setup(p => p.Disconnect(It.IsAny())).Returns(disconnectionResponse); + proxy.As().Setup(o => o.State).Returns(CommunicationState.Opened); + proxyObjectFactory.Setup(f => f.CreateObject(It.IsAny())).Returns(proxy.Object); + + var token = Guid.NewGuid(); + var connected = sut.Connect(token); + var disconnected = sut.Disconnect(); + + proxy.Verify(p => p.Disconnect(It.Is(m => m.CommunicationToken == connectionResponse.CommunicationToken)), Times.Once); + + Assert.IsTrue(connected); + Assert.IsTrue(disconnected); + } + + [TestMethod] + public void MustHandleConnectionRefusalCorrectly() + { + var proxy = new Mock(); + var response = new ConnectionResponse + { + CommunicationToken = Guid.NewGuid(), + ConnectionEstablished = false + }; + + proxy.Setup(p => p.Connect(It.IsAny())).Returns(response); + proxyObjectFactory.Setup(f => f.CreateObject(It.IsAny())).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()), 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(); + var response = new ConnectionResponse + { + CommunicationToken = Guid.NewGuid(), + ConnectionEstablished = true + }; + + proxy.Setup(p => p.Connect(It.IsAny())).Returns(response); + proxy.As().Setup(o => o.State).Returns(CommunicationState.Faulted); + proxyObjectFactory.Setup(f => f.CreateObject(It.IsAny())).Returns(proxy.Object); + + var token = Guid.NewGuid(); + + sut.Connect(token); + sut.Disconnect(); + } + + [TestMethod] + [ExpectedException(typeof(InvalidOperationException))] + public void MustFailToSendIfNotConnected() + { + sut.Send(new Mock().Object); + } + + [TestMethod] + [ExpectedException(typeof(CommunicationException))] + public void MustFailToSendIfChannelNotOpen() + { + var proxy = new Mock(); + var response = new ConnectionResponse + { + CommunicationToken = Guid.NewGuid(), + ConnectionEstablished = true + }; + + proxy.Setup(p => p.Connect(It.IsAny())).Returns(response); + proxy.As().Setup(o => o.State).Returns(CommunicationState.Faulted); + proxyObjectFactory.Setup(f => f.CreateObject(It.IsAny())).Returns(proxy.Object); + + var token = Guid.NewGuid(); + + sut.Connect(token); + sut.Send(new Mock().Object); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentNullException))] + public void MustNotAllowSendingNull() + { + sut.Send(null); + } + + [TestMethod] + public void MustSendCorrectly() + { + var proxy = new Mock(); + var connectionResponse = new ConnectionResponse + { + CommunicationToken = Guid.NewGuid(), + ConnectionEstablished = true + }; + var message = new SimpleMessage(SimpleMessagePurport.Authenticate); + var response = new Mock(); + + proxy.Setup(p => p.Connect(It.IsAny())).Returns(connectionResponse); + proxy.Setup(p => p.Send(message)).Returns(response.Object); + proxy.As().Setup(o => o.State).Returns(CommunicationState.Opened); + proxyObjectFactory.Setup(f => f.CreateObject(It.IsAny())).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); + } + } +} diff --git a/SafeExamBrowser.Core.UnitTests/SafeExamBrowser.Core.UnitTests.csproj b/SafeExamBrowser.Core.UnitTests/SafeExamBrowser.Core.UnitTests.csproj index a6444549..85b3e7de 100644 --- a/SafeExamBrowser.Core.UnitTests/SafeExamBrowser.Core.UnitTests.csproj +++ b/SafeExamBrowser.Core.UnitTests/SafeExamBrowser.Core.UnitTests.csproj @@ -69,6 +69,7 @@ ..\packages\Moq.4.8.1\lib\net45\Moq.dll + ..\packages\System.Threading.Tasks.Extensions.4.4.0\lib\portable-net45+win8+wp8+wpa81\System.Threading.Tasks.Extensions.dll @@ -83,6 +84,8 @@ + + diff --git a/SafeExamBrowser.Core/Communication/BaseProxy.cs b/SafeExamBrowser.Core/Communication/BaseProxy.cs index f29a03ca..1a6088d0 100644 --- a/SafeExamBrowser.Core/Communication/BaseProxy.cs +++ b/SafeExamBrowser.Core/Communication/BaseProxy.cs @@ -17,8 +17,7 @@ using SafeExamBrowser.Contracts.Logging; namespace SafeExamBrowser.Core.Communication { /// - /// Base implementation of an . Automatically starts a ping mechanism with a timeout of - /// once a connection was established. + /// Base implementation of an . /// public abstract class BaseProxy : ICommunicationProxy { @@ -26,7 +25,8 @@ namespace SafeExamBrowser.Core.Communication private static readonly object @lock = new object(); private string address; - private ICommunication channel; + private ICommunication proxy; + private IProxyObjectFactory factory; private Guid? communicationToken; private Timer timer; @@ -34,31 +34,34 @@ namespace SafeExamBrowser.Core.Communication public event CommunicationEventHandler ConnectionLost; - public BaseProxy(string address, ILogger logger) + public BaseProxy(string address, IProxyObjectFactory factory, ILogger logger) { this.address = address; + this.factory = factory; 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.CreateChannel(new NetNamedPipeBinding(NetNamedPipeSecurityMode.Transport), endpoint); - (channel as ICommunicationObject).Closed += BaseProxy_Closed; - (channel as ICommunicationObject).Closing += BaseProxy_Closing; - (channel as ICommunicationObject).Faulted += BaseProxy_Faulted; - (channel as ICommunicationObject).Opened += BaseProxy_Opened; - (channel as ICommunicationObject).Opening += BaseProxy_Opening; + if (proxy is ICommunicationObject communicationObject) + { + communicationObject.Closed += BaseProxy_Closed; + communicationObject.Closing += BaseProxy_Closing; + communicationObject.Faulted += BaseProxy_Faulted; + communicationObject.Opened += BaseProxy_Opened; + communicationObject.Opening += BaseProxy_Opening; + } 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; Logger.Debug($"Connection was {(response.ConnectionEstablished ? "established" : "refused")}."); - if (response.ConnectionEstablished) + if (response.ConnectionEstablished && autoPing) { StartAutoPing(); } @@ -72,41 +75,64 @@ namespace SafeExamBrowser.Core.Communication StopAutoPing(); 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}."); return response.ConnectionTerminated; } - protected Response Send(Message message) + /// + /// Sends the given message, optionally returning a response. If no response is expected, null will be returned. + /// + /// If the given message is null. + /// If no connection has been established yet. + /// If the communication failed. + protected virtual Response Send(Message message) { + if (message is null) + { + throw new ArgumentNullException(nameof(message)); + } + FailIfNotConnected(nameof(Send)); message.CommunicationToken = communicationToken.Value; - var response = channel.Send(message); + var response = proxy.Send(message); Logger.Debug($"Sent message '{ToString(message)}', got response '{ToString(response)}'."); return response; } + /// + /// Sends the given purport as . + /// protected Response Send(SimpleMessagePurport purport) { return Send(new SimpleMessage(purport)); } + /// + /// Determines whether the given response is a with purport . + /// protected bool IsAcknowledged(Response response) { return response is SimpleResponse simpleResponse && simpleResponse.Purport == SimpleResponsePurport.Acknowledged; } + /// + /// Retrieves the string representation of the given , or indicates that a message is null. + /// protected string ToString(Message message) { return message != null ? message.ToString() : ""; } + /// + /// etrieves the string representation of the given , or indicates that a response is null. + /// protected string ToString(Response response) { return response != null ? response.ToString() : ""; @@ -144,7 +170,7 @@ namespace SafeExamBrowser.Core.Communication 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()}!"); } @@ -152,7 +178,7 @@ namespace SafeExamBrowser.Core.Communication 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() diff --git a/SafeExamBrowser.Core/Communication/ClientProxy.cs b/SafeExamBrowser.Core/Communication/ClientProxy.cs index 996e4e8c..5c9bf375 100644 --- a/SafeExamBrowser.Core/Communication/ClientProxy.cs +++ b/SafeExamBrowser.Core/Communication/ClientProxy.cs @@ -19,7 +19,7 @@ namespace SafeExamBrowser.Core.Communication /// 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) { } diff --git a/SafeExamBrowser.Core/Communication/ProxyObjectFactory.cs b/SafeExamBrowser.Core/Communication/ProxyObjectFactory.cs new file mode 100644 index 00000000..d601709c --- /dev/null +++ b/SafeExamBrowser.Core/Communication/ProxyObjectFactory.cs @@ -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 +{ + /// + /// Default implementation of the utilizing WCF (). + /// + public class ProxyObjectFactory : IProxyObjectFactory + { + public ICommunication CreateObject(string address) + { + var endpoint = new EndpointAddress(address); + var channel = ChannelFactory.CreateChannel(new NetNamedPipeBinding(NetNamedPipeSecurityMode.Transport), endpoint); + + return channel; + } + } +} diff --git a/SafeExamBrowser.Core/Communication/RuntimeProxy.cs b/SafeExamBrowser.Core/Communication/RuntimeProxy.cs index 0aff2402..343ede46 100644 --- a/SafeExamBrowser.Core/Communication/RuntimeProxy.cs +++ b/SafeExamBrowser.Core/Communication/RuntimeProxy.cs @@ -20,7 +20,7 @@ namespace SafeExamBrowser.Core.Communication /// 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) { } diff --git a/SafeExamBrowser.Core/Communication/ServiceProxy.cs b/SafeExamBrowser.Core/Communication/ServiceProxy.cs index f44d7a1f..f84d327e 100644 --- a/SafeExamBrowser.Core/Communication/ServiceProxy.cs +++ b/SafeExamBrowser.Core/Communication/ServiceProxy.cs @@ -20,18 +20,18 @@ namespace SafeExamBrowser.Core.Communication { 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))) { return false; } - return base.Connect(); + return base.Connect(autoPing: autoPing); } public override bool Disconnect() diff --git a/SafeExamBrowser.Core/SafeExamBrowser.Core.csproj b/SafeExamBrowser.Core/SafeExamBrowser.Core.csproj index ec65ac98..77384578 100644 --- a/SafeExamBrowser.Core/SafeExamBrowser.Core.csproj +++ b/SafeExamBrowser.Core/SafeExamBrowser.Core.csproj @@ -63,6 +63,7 @@ + diff --git a/SafeExamBrowser.Runtime.UnitTests/Behaviour/Operations/ServiceConnectionOperationTests.cs b/SafeExamBrowser.Runtime.UnitTests/Behaviour/Operations/ServiceConnectionOperationTests.cs index e9995376..80a3d282 100644 --- a/SafeExamBrowser.Runtime.UnitTests/Behaviour/Operations/ServiceConnectionOperationTests.cs +++ b/SafeExamBrowser.Runtime.UnitTests/Behaviour/Operations/ServiceConnectionOperationTests.cs @@ -45,38 +45,38 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations [TestMethod] 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 }); 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 }); sut.Perform(); - service.Verify(s => s.Connect(null), Times.Exactly(2)); + service.Verify(s => s.Connect(null, true), Times.Exactly(2)); } [TestMethod] 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 }); 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 }); sut.Perform(); - service.Setup(s => s.Connect(null)).Throws(); + service.Setup(s => s.Connect(null, true)).Throws(); configuration.SetupGet(s => s.CurrentSettings).Returns(new Settings { ServicePolicy = ServicePolicy.Mandatory }); sut.Perform(); - service.Setup(s => s.Connect(null)).Throws(); + service.Setup(s => s.Connect(null, true)).Throws(); configuration.SetupGet(s => s.CurrentSettings).Returns(new Settings { ServicePolicy = ServicePolicy.Optional }); sut.Perform(); @@ -85,7 +85,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations [TestMethod] 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 }); var result = sut.Perform(); @@ -96,7 +96,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations [TestMethod] 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 }); var result = sut.Perform(); @@ -109,13 +109,13 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations [TestMethod] 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 }); sut.Perform(); 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 }); sut.Perform(); @@ -127,7 +127,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations [TestMethod] 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(); configuration.SetupGet(s => s.CurrentSettings).Returns(new Settings { ServicePolicy = ServicePolicy.Optional }); @@ -140,25 +140,25 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations [TestMethod] 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 }); sut.Perform(); 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 }); sut.Perform(); sut.Revert(); - service.Setup(s => s.Connect(null)).Throws(); + service.Setup(s => s.Connect(null, true)).Throws(); configuration.SetupGet(s => s.CurrentSettings).Returns(new Settings { ServicePolicy = ServicePolicy.Mandatory }); sut.Perform(); sut.Revert(); - service.Setup(s => s.Connect(null)).Throws(); + service.Setup(s => s.Connect(null, true)).Throws(); configuration.SetupGet(s => s.CurrentSettings).Returns(new Settings { ServicePolicy = ServicePolicy.Optional }); sut.Perform(); diff --git a/SafeExamBrowser.Runtime/Communication/ProxyFactory.cs b/SafeExamBrowser.Runtime/Communication/ProxyFactory.cs index 12ae7cc8..6b38b4ce 100644 --- a/SafeExamBrowser.Runtime/Communication/ProxyFactory.cs +++ b/SafeExamBrowser.Runtime/Communication/ProxyFactory.cs @@ -15,16 +15,18 @@ namespace SafeExamBrowser.Runtime.Communication { internal class ProxyFactory : IProxyFactory { + private IProxyObjectFactory factory; private ILogger logger; - public ProxyFactory(ILogger logger) + public ProxyFactory(IProxyObjectFactory factory, ILogger logger) { + this.factory = factory; this.logger = logger; } public IClientProxy CreateClientProxy(string address) { - return new ClientProxy(address, new ModuleLogger(logger, typeof(ClientProxy))); + return new ClientProxy(address, factory, new ModuleLogger(logger, typeof(ClientProxy))); } } } diff --git a/SafeExamBrowser.Runtime/CompositionRoot.cs b/SafeExamBrowser.Runtime/CompositionRoot.cs index a9b9cff8..e62741a9 100644 --- a/SafeExamBrowser.Runtime/CompositionRoot.cs +++ b/SafeExamBrowser.Runtime/CompositionRoot.cs @@ -50,9 +50,9 @@ namespace SafeExamBrowser.Runtime var uiFactory = new UserInterfaceFactory(text); var desktop = new Desktop(new ModuleLogger(logger, typeof(Desktop))); 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 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 bootstrapOperations = new Queue();