SEBWIN-219: Decoupled proxy object creation from BaseProxy and implemented unit tests for the latter.
This commit is contained in:
parent
b206b0d5be
commit
17c068de6f
16 changed files with 366 additions and 48 deletions
|
@ -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);
|
||||
|
|
|
@ -17,20 +17,21 @@ namespace SafeExamBrowser.Contracts.Communication
|
|||
public interface ICommunicationProxy
|
||||
{
|
||||
/// <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>
|
||||
event CommunicationEventHandler ConnectionLost;
|
||||
|
||||
/// <summary>
|
||||
/// 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>
|
||||
/// <exception cref="System.ServiceModel.*">If the communication failed.</exception>
|
||||
bool Connect(Guid? token = null);
|
||||
bool Connect(Guid? token = null, bool autoPing = true);
|
||||
|
||||
/// <summary>
|
||||
/// Terminates an open connection. Returns <c>true</c> if the disconnection has been acknowledged, otherwise <c>false</c>.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">If no connection has been established.</exception>
|
||||
/// <exception cref="System.ServiceModel.*">If the communication failed.</exception>
|
||||
bool Disconnect();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -62,6 +62,7 @@
|
|||
<Compile Include="Communication\IClientProxy.cs" />
|
||||
<Compile Include="Communication\ICommunicationHost.cs" />
|
||||
<Compile Include="Communication\ICommunicationProxy.cs" />
|
||||
<Compile Include="Communication\IProxyObjectFactory.cs" />
|
||||
<Compile Include="Communication\IProxyFactory.cs" />
|
||||
<Compile Include="Communication\IRuntimeHost.cs" />
|
||||
<Compile Include="Communication\IRuntimeProxy.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);
|
||||
}
|
||||
}
|
||||
}
|
197
SafeExamBrowser.Core.UnitTests/Communication/BaseProxyTests.cs
Normal file
197
SafeExamBrowser.Core.UnitTests/Communication/BaseProxyTests.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -69,6 +69,7 @@
|
|||
<HintPath>..\packages\Moq.4.8.1\lib\net45\Moq.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.ServiceModel" />
|
||||
<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>
|
||||
</Reference>
|
||||
|
@ -83,6 +84,8 @@
|
|||
<Compile Include="Behaviour\OperationModel\I18nOperationTests.cs" />
|
||||
<Compile Include="Behaviour\OperationModel\DelegateOperationTests.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\XmlTextResourceTests.cs" />
|
||||
<Compile Include="Logging\DefaultLogFormatterTests.cs" />
|
||||
|
|
|
@ -17,8 +17,7 @@ using SafeExamBrowser.Contracts.Logging;
|
|||
namespace SafeExamBrowser.Core.Communication
|
||||
{
|
||||
/// <summary>
|
||||
/// Base implementation of an <see cref="ICommunicationProxy"/>. Automatically starts a ping mechanism with a timeout of
|
||||
/// <see cref="ONE_MINUTE"/> once a connection was established.
|
||||
/// Base implementation of an <see cref="ICommunicationProxy"/>.
|
||||
/// </summary>
|
||||
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<ICommunication>.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)
|
||||
/// <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));
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends the given purport as <see cref="SimpleMessage"/>.
|
||||
/// </summary>
|
||||
protected Response Send(SimpleMessagePurport 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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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!");
|
||||
}
|
||||
|
||||
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()
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace SafeExamBrowser.Core.Communication
|
|||
/// </summary>
|
||||
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)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
27
SafeExamBrowser.Core/Communication/ProxyObjectFactory.cs
Normal file
27
SafeExamBrowser.Core/Communication/ProxyObjectFactory.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,7 +20,7 @@ namespace SafeExamBrowser.Core.Communication
|
|||
/// </summary>
|
||||
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)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -63,6 +63,7 @@
|
|||
<Compile Include="Communication\BaseProxy.cs" />
|
||||
<Compile Include="Communication\BaseHost.cs" />
|
||||
<Compile Include="Communication\ClientProxy.cs" />
|
||||
<Compile Include="Communication\ProxyObjectFactory.cs" />
|
||||
<Compile Include="Communication\RuntimeProxy.cs" />
|
||||
<Compile Include="Communication\ServiceProxy.cs" />
|
||||
<Compile Include="Logging\DefaultLogFormatter.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<Exception>();
|
||||
service.Setup(s => s.Connect(null, true)).Throws<Exception>();
|
||||
configuration.SetupGet(s => s.CurrentSettings).Returns(new Settings { ServicePolicy = ServicePolicy.Mandatory });
|
||||
|
||||
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 });
|
||||
|
||||
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<Exception>();
|
||||
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<Exception>();
|
||||
service.Setup(s => s.Connect(null, true)).Throws<Exception>();
|
||||
configuration.SetupGet(s => s.CurrentSettings).Returns(new Settings { ServicePolicy = ServicePolicy.Mandatory });
|
||||
|
||||
sut.Perform();
|
||||
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 });
|
||||
|
||||
sut.Perform();
|
||||
|
|
|
@ -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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<IOperation>();
|
||||
|
|
Loading…
Reference in a new issue