SEBWIN-219: Extracted creation of proxy and host objects from base implementations and started implementing unit tests for hosts.
This commit is contained in:
parent
e4940383fb
commit
3a4f189916
21 changed files with 448 additions and 66 deletions
|
@ -24,7 +24,7 @@ namespace SafeExamBrowser.Client.Communication
|
|||
|
||||
public event CommunicationEventHandler Shutdown;
|
||||
|
||||
public ClientHost(string address, ILogger logger, int processId) : base(address, logger)
|
||||
public ClientHost(string address, IHostObjectFactory factory, ILogger logger, int processId) : base(address, factory, logger)
|
||||
{
|
||||
this.processId = processId;
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ using SafeExamBrowser.Contracts.UserInterface;
|
|||
using SafeExamBrowser.Contracts.UserInterface.MessageBox;
|
||||
using SafeExamBrowser.Contracts.WindowsApi;
|
||||
using SafeExamBrowser.Core.Behaviour.OperationModel;
|
||||
using SafeExamBrowser.Core.Communication.Hosts;
|
||||
using SafeExamBrowser.Core.Communication.Proxies;
|
||||
using SafeExamBrowser.Core.I18n;
|
||||
using SafeExamBrowser.Core.Logging;
|
||||
|
@ -159,7 +160,8 @@ namespace SafeExamBrowser.Client
|
|||
private IOperation BuildCommunicationHostOperation()
|
||||
{
|
||||
var processId = Process.GetCurrentProcess().Id;
|
||||
var host = new ClientHost(configuration.RuntimeInfo.ClientAddress, new ModuleLogger(logger, typeof(ClientHost)), processId);
|
||||
var factory = new HostObjectFactory();
|
||||
var host = new ClientHost(configuration.RuntimeInfo.ClientAddress, factory, new ModuleLogger(logger, typeof(ClientHost)), processId);
|
||||
var operation = new CommunicationOperation(host, logger);
|
||||
|
||||
clientHost = host;
|
||||
|
|
19
SafeExamBrowser.Contracts/Communication/Hosts/IHostObject.cs
Normal file
19
SafeExamBrowser.Contracts/Communication/Hosts/IHostObject.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
namespace SafeExamBrowser.Contracts.Communication.Hosts
|
||||
{
|
||||
/// <summary>
|
||||
/// The host object to be used in communication hosts.
|
||||
/// </summary>
|
||||
public interface IHostObject : ICommunicationObject
|
||||
{
|
||||
}
|
||||
}
|
|
@ -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.Hosts
|
||||
{
|
||||
/// <summary>
|
||||
/// A factory to create host objects for communication hosts.
|
||||
/// </summary>
|
||||
public interface IHostObjectFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Utilizes the given communication object to create a host object (see <see cref="IHostObject"/>) for the specified endpoint address.
|
||||
/// </summary>
|
||||
IHostObject CreateObject(string address, ICommunication communicationObject);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
namespace SafeExamBrowser.Contracts.Communication.Proxies
|
||||
{
|
||||
/// <summary>
|
||||
/// The communication object to be used in an <see cref="ICommunicationProxy"/>.
|
||||
/// </summary>
|
||||
public interface IProxyObject : ICommunication, ICommunicationObject
|
||||
{
|
||||
}
|
||||
}
|
|
@ -9,13 +9,13 @@
|
|||
namespace SafeExamBrowser.Contracts.Communication.Proxies
|
||||
{
|
||||
/// <summary>
|
||||
/// A factory to create communication objects for proxies.
|
||||
/// A factory to create proxy objects for communication proxies.
|
||||
/// </summary>
|
||||
public interface IProxyObjectFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a communication object (see <see cref="ICommunication"/>) for the specified endpoint address.
|
||||
/// Creates a proxy object (see <see cref="IProxyObject"/>) for the specified endpoint address.
|
||||
/// </summary>
|
||||
ICommunication CreateObject(string address);
|
||||
IProxyObject CreateObject(string address);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,10 +58,13 @@
|
|||
<Compile Include="Behaviour\OperationModel\IOperationSequence.cs" />
|
||||
<Compile Include="Behaviour\OperationModel\OperationResult.cs" />
|
||||
<Compile Include="Communication\Hosts\IClientHost.cs" />
|
||||
<Compile Include="Communication\Hosts\IHostObject.cs" />
|
||||
<Compile Include="Communication\Hosts\IHostObjectFactory.cs" />
|
||||
<Compile Include="Communication\ICommunication.cs" />
|
||||
<Compile Include="Communication\Proxies\IClientProxy.cs" />
|
||||
<Compile Include="Communication\ICommunicationHost.cs" />
|
||||
<Compile Include="Communication\ICommunicationProxy.cs" />
|
||||
<Compile Include="Communication\Proxies\IProxyObject.cs" />
|
||||
<Compile Include="Communication\Proxies\IProxyObjectFactory.cs" />
|
||||
<Compile Include="Communication\Proxies\IProxyFactory.cs" />
|
||||
<Compile Include="Communication\Hosts\IRuntimeHost.cs" />
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* 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.Data;
|
||||
using SafeExamBrowser.Contracts.Communication.Hosts;
|
||||
using SafeExamBrowser.Contracts.Logging;
|
||||
using SafeExamBrowser.Core.Communication.Hosts;
|
||||
|
||||
namespace SafeExamBrowser.Core.UnitTests.Communication.Hosts
|
||||
{
|
||||
internal class BaseHostImpl : BaseHost
|
||||
{
|
||||
public Func<Guid?, bool> OnConnectStub { get; set; }
|
||||
public Action OnDisconnectStub { get; set; }
|
||||
public Func<Message, Response> OnReceiveStub { get; set; }
|
||||
public Func<SimpleMessagePurport, Response> OnReceiveSimpleMessageStub { get; set; }
|
||||
|
||||
public BaseHostImpl(string address, IHostObjectFactory factory, ILogger logger) : base(address, factory, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public Guid? GetCommunicationToken()
|
||||
{
|
||||
return CommunicationToken;
|
||||
}
|
||||
|
||||
protected override bool OnConnect(Guid? token)
|
||||
{
|
||||
return OnConnectStub?.Invoke(token) == true;
|
||||
}
|
||||
|
||||
protected override void OnDisconnect()
|
||||
{
|
||||
OnDisconnectStub?.Invoke();
|
||||
}
|
||||
|
||||
protected override Response OnReceive(Message message)
|
||||
{
|
||||
return OnReceiveStub?.Invoke(message);
|
||||
}
|
||||
|
||||
protected override Response OnReceive(SimpleMessagePurport message)
|
||||
{
|
||||
return OnReceiveSimpleMessageStub?.Invoke(message);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,227 @@
|
|||
/*
|
||||
* 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 System.Threading;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using SafeExamBrowser.Contracts.Communication;
|
||||
using SafeExamBrowser.Contracts.Communication.Data;
|
||||
using SafeExamBrowser.Contracts.Communication.Hosts;
|
||||
using SafeExamBrowser.Contracts.Logging;
|
||||
|
||||
namespace SafeExamBrowser.Core.UnitTests.Communication.Hosts
|
||||
{
|
||||
[TestClass]
|
||||
public class BaseHostTests
|
||||
{
|
||||
private Mock<IHostObject> hostObject;
|
||||
private Mock<IHostObjectFactory> hostObjectFactory;
|
||||
private Mock<ILogger> logger;
|
||||
private BaseHostImpl sut;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
hostObject = new Mock<IHostObject>();
|
||||
hostObjectFactory = new Mock<IHostObjectFactory>();
|
||||
logger = new Mock<ILogger>();
|
||||
|
||||
hostObjectFactory.Setup(f => f.CreateObject(It.IsAny<string>(), It.IsAny<ICommunication>())).Returns(hostObject.Object);
|
||||
|
||||
sut = new BaseHostImpl("net.pipe://some/address/here", hostObjectFactory.Object, logger.Object);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustCorrectlyStartHost()
|
||||
{
|
||||
var threadId = Thread.CurrentThread.ManagedThreadId;
|
||||
|
||||
hostObject.Setup(h => h.Open()).Callback(() => threadId = Thread.CurrentThread.ManagedThreadId);
|
||||
|
||||
sut.Start();
|
||||
|
||||
hostObjectFactory.Verify(f => f.CreateObject(It.IsAny<string>(), sut), Times.Once);
|
||||
|
||||
Assert.AreNotEqual(Thread.CurrentThread.ManagedThreadId, threadId);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(CommunicationException))]
|
||||
public void MustCorrectlyHandleStartupException()
|
||||
{
|
||||
hostObject.Setup(h => h.Open()).Throws<Exception>();
|
||||
|
||||
sut.Start();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustCorrectlyStopHost()
|
||||
{
|
||||
sut.Start();
|
||||
sut.Stop();
|
||||
|
||||
hostObject.Verify(h => h.Close(), Times.Once);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(CommunicationException))]
|
||||
public void MustCorrectlyHandleShutdownException()
|
||||
{
|
||||
hostObject.Setup(h => h.Close()).Throws<Exception>();
|
||||
|
||||
sut.Start();
|
||||
sut.Stop();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustNotFailToStopIfNotRunning()
|
||||
{
|
||||
sut.Stop();
|
||||
sut.Stop();
|
||||
sut.Stop();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustNotFailToEvaluateIsRunningIfNotRunning()
|
||||
{
|
||||
var running = sut.IsRunning;
|
||||
|
||||
Assert.IsFalse(running);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustCorrectlyIndicateWhetherHostIsRunning()
|
||||
{
|
||||
hostObject.SetupGet(h => h.State).Returns(CommunicationState.Faulted);
|
||||
|
||||
sut.Start();
|
||||
|
||||
Assert.IsFalse(sut.IsRunning);
|
||||
|
||||
hostObject.SetupGet(h => h.State).Returns(CommunicationState.Opened);
|
||||
|
||||
Assert.IsTrue(sut.IsRunning);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustCorrectlyHandleConnectionRequest()
|
||||
{
|
||||
var token = Guid.NewGuid();
|
||||
var receivedToken = default(Guid?);
|
||||
|
||||
sut.OnConnectStub = (t) =>
|
||||
{
|
||||
receivedToken = t;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
var response = sut.Connect(token);
|
||||
|
||||
Assert.IsTrue(response.ConnectionEstablished);
|
||||
Assert.AreEqual(token, receivedToken);
|
||||
Assert.AreEqual(sut.GetCommunicationToken(), response.CommunicationToken);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustCorrectlyHandleDeniedConnectionRequest()
|
||||
{
|
||||
var token = Guid.NewGuid();
|
||||
var receivedToken = default(Guid?);
|
||||
|
||||
sut.OnConnectStub = (t) =>
|
||||
{
|
||||
receivedToken = t;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
var response = sut.Connect(token);
|
||||
|
||||
Assert.IsFalse(response.ConnectionEstablished);
|
||||
Assert.AreEqual(token, receivedToken);
|
||||
Assert.IsNull(sut.GetCommunicationToken());
|
||||
Assert.IsNull(response.CommunicationToken);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustCorrectlyHandleDisconnectionRequest()
|
||||
{
|
||||
var message = new DisconnectionMessage();
|
||||
var disconnected = false;
|
||||
|
||||
sut.OnConnectStub = (t) => { return true; };
|
||||
sut.OnDisconnectStub = () => disconnected = true;
|
||||
sut.Connect();
|
||||
|
||||
message.CommunicationToken = sut.GetCommunicationToken().Value;
|
||||
|
||||
var response = sut.Disconnect(message);
|
||||
|
||||
Assert.IsTrue(disconnected);
|
||||
Assert.IsTrue(response.ConnectionTerminated);
|
||||
Assert.IsNull(sut.GetCommunicationToken());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustCorrectlyHandleUnauthorizedDisconnectionRequest()
|
||||
{
|
||||
var disconnected = false;
|
||||
|
||||
sut.OnConnectStub = (t) => { return true; };
|
||||
sut.OnDisconnectStub = () => disconnected = true;
|
||||
sut.Connect();
|
||||
|
||||
var response = sut.Disconnect(new DisconnectionMessage());
|
||||
|
||||
Assert.IsFalse(disconnected);
|
||||
Assert.IsFalse(response.ConnectionTerminated);
|
||||
Assert.IsNotNull(sut.GetCommunicationToken());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustCorrectlyHandleUnauthorizedTransmission()
|
||||
{
|
||||
var received = false;
|
||||
var simpleReceived = false;
|
||||
|
||||
sut.OnReceiveStub = (m) => { received = true; return null; };
|
||||
sut.OnReceiveSimpleMessageStub = (m) => { simpleReceived = true; return null; };
|
||||
|
||||
var response = sut.Send(new DisconnectionMessage());
|
||||
|
||||
Assert.IsFalse(received);
|
||||
Assert.IsFalse(simpleReceived);
|
||||
Assert.IsInstanceOfType(response, typeof(SimpleResponse));
|
||||
Assert.AreEqual(SimpleResponsePurport.Unauthorized, (response as SimpleResponse)?.Purport);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustCorrectlyHandlePingMessage()
|
||||
{
|
||||
// TODO
|
||||
Assert.Fail();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustCorrectlyReceiveSimpleMessage()
|
||||
{
|
||||
// TODO
|
||||
Assert.Fail();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustCorrectlyReceiveMessage()
|
||||
{
|
||||
// TODO
|
||||
Assert.Fail();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,7 +10,6 @@ using System;
|
|||
using System.ServiceModel;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using SafeExamBrowser.Contracts.Communication;
|
||||
using SafeExamBrowser.Contracts.Communication.Data;
|
||||
using SafeExamBrowser.Contracts.Communication.Proxies;
|
||||
using SafeExamBrowser.Contracts.Logging;
|
||||
|
@ -36,7 +35,7 @@ namespace SafeExamBrowser.Core.UnitTests.Communication.Proxies
|
|||
[TestMethod]
|
||||
public void MustConnectCorrectly()
|
||||
{
|
||||
var proxy = new Mock<ICommunication>();
|
||||
var proxy = new Mock<IProxyObject>();
|
||||
var response = new ConnectionResponse
|
||||
{
|
||||
CommunicationToken = Guid.NewGuid(),
|
||||
|
@ -58,7 +57,7 @@ namespace SafeExamBrowser.Core.UnitTests.Communication.Proxies
|
|||
[TestMethod]
|
||||
public void MustDisconnectCorrectly()
|
||||
{
|
||||
var proxy = new Mock<ICommunication>();
|
||||
var proxy = new Mock<IProxyObject>();
|
||||
var connectionResponse = new ConnectionResponse
|
||||
{
|
||||
CommunicationToken = Guid.NewGuid(),
|
||||
|
@ -71,7 +70,7 @@ namespace SafeExamBrowser.Core.UnitTests.Communication.Proxies
|
|||
|
||||
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);
|
||||
proxy.Setup(o => o.State).Returns(CommunicationState.Opened);
|
||||
proxyObjectFactory.Setup(f => f.CreateObject(It.IsAny<string>())).Returns(proxy.Object);
|
||||
|
||||
var token = Guid.NewGuid();
|
||||
|
@ -87,7 +86,7 @@ namespace SafeExamBrowser.Core.UnitTests.Communication.Proxies
|
|||
[TestMethod]
|
||||
public void MustHandleConnectionRefusalCorrectly()
|
||||
{
|
||||
var proxy = new Mock<ICommunication>();
|
||||
var proxy = new Mock<IProxyObject>();
|
||||
var response = new ConnectionResponse
|
||||
{
|
||||
CommunicationToken = Guid.NewGuid(),
|
||||
|
@ -117,7 +116,7 @@ namespace SafeExamBrowser.Core.UnitTests.Communication.Proxies
|
|||
[ExpectedException(typeof(CommunicationException))]
|
||||
public void MustFailToDisconnectIfChannelNotOpen()
|
||||
{
|
||||
var proxy = new Mock<ICommunication>();
|
||||
var proxy = new Mock<IProxyObject>();
|
||||
var response = new ConnectionResponse
|
||||
{
|
||||
CommunicationToken = Guid.NewGuid(),
|
||||
|
@ -125,7 +124,7 @@ namespace SafeExamBrowser.Core.UnitTests.Communication.Proxies
|
|||
};
|
||||
|
||||
proxy.Setup(p => p.Connect(It.IsAny<Guid>())).Returns(response);
|
||||
proxy.As<ICommunicationObject>().Setup(o => o.State).Returns(CommunicationState.Faulted);
|
||||
proxy.Setup(o => o.State).Returns(CommunicationState.Faulted);
|
||||
proxyObjectFactory.Setup(f => f.CreateObject(It.IsAny<string>())).Returns(proxy.Object);
|
||||
|
||||
var token = Guid.NewGuid();
|
||||
|
@ -145,7 +144,7 @@ namespace SafeExamBrowser.Core.UnitTests.Communication.Proxies
|
|||
[ExpectedException(typeof(CommunicationException))]
|
||||
public void MustFailToSendIfChannelNotOpen()
|
||||
{
|
||||
var proxy = new Mock<ICommunication>();
|
||||
var proxy = new Mock<IProxyObject>();
|
||||
var response = new ConnectionResponse
|
||||
{
|
||||
CommunicationToken = Guid.NewGuid(),
|
||||
|
@ -153,7 +152,7 @@ namespace SafeExamBrowser.Core.UnitTests.Communication.Proxies
|
|||
};
|
||||
|
||||
proxy.Setup(p => p.Connect(It.IsAny<Guid>())).Returns(response);
|
||||
proxy.As<ICommunicationObject>().Setup(o => o.State).Returns(CommunicationState.Faulted);
|
||||
proxy.Setup(o => o.State).Returns(CommunicationState.Faulted);
|
||||
proxyObjectFactory.Setup(f => f.CreateObject(It.IsAny<string>())).Returns(proxy.Object);
|
||||
|
||||
var token = Guid.NewGuid();
|
||||
|
@ -172,7 +171,7 @@ namespace SafeExamBrowser.Core.UnitTests.Communication.Proxies
|
|||
[TestMethod]
|
||||
public void MustSendCorrectly()
|
||||
{
|
||||
var proxy = new Mock<ICommunication>();
|
||||
var proxy = new Mock<IProxyObject>();
|
||||
var connectionResponse = new ConnectionResponse
|
||||
{
|
||||
CommunicationToken = Guid.NewGuid(),
|
||||
|
@ -183,7 +182,7 @@ namespace SafeExamBrowser.Core.UnitTests.Communication.Proxies
|
|||
|
||||
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);
|
||||
proxy.Setup(o => o.State).Returns(CommunicationState.Opened);
|
||||
proxyObjectFactory.Setup(f => f.CreateObject(It.IsAny<string>())).Returns(proxy.Object);
|
||||
|
||||
var token = Guid.NewGuid();
|
||||
|
@ -197,7 +196,7 @@ namespace SafeExamBrowser.Core.UnitTests.Communication.Proxies
|
|||
[TestMethod]
|
||||
public void MustSendSimpleMessageCorrectly()
|
||||
{
|
||||
var proxy = new Mock<ICommunication>();
|
||||
var proxy = new Mock<IProxyObject>();
|
||||
var connectionResponse = new ConnectionResponse
|
||||
{
|
||||
CommunicationToken = Guid.NewGuid(),
|
||||
|
@ -208,7 +207,7 @@ namespace SafeExamBrowser.Core.UnitTests.Communication.Proxies
|
|||
|
||||
proxy.Setup(p => p.Connect(It.IsAny<Guid>())).Returns(connectionResponse);
|
||||
proxy.Setup(p => p.Send(It.IsAny<Message>())).Returns(response.Object);
|
||||
proxy.As<ICommunicationObject>().Setup(o => o.State).Returns(CommunicationState.Opened);
|
||||
proxy.Setup(o => o.State).Returns(CommunicationState.Opened);
|
||||
proxyObjectFactory.Setup(f => f.CreateObject(It.IsAny<string>())).Returns(proxy.Object);
|
||||
|
||||
var token = Guid.NewGuid();
|
||||
|
@ -255,7 +254,7 @@ namespace SafeExamBrowser.Core.UnitTests.Communication.Proxies
|
|||
[TestMethod]
|
||||
public void TestConnectionMustPingHost()
|
||||
{
|
||||
var proxy = new Mock<ICommunication>();
|
||||
var proxy = new Mock<IProxyObject>();
|
||||
var connectionResponse = new ConnectionResponse
|
||||
{
|
||||
CommunicationToken = Guid.NewGuid(),
|
||||
|
@ -264,7 +263,7 @@ namespace SafeExamBrowser.Core.UnitTests.Communication.Proxies
|
|||
|
||||
proxy.Setup(p => p.Connect(It.IsAny<Guid>())).Returns(connectionResponse);
|
||||
proxy.Setup(p => p.Send(It.Is<SimpleMessage>(m => m.Purport == SimpleMessagePurport.Ping))).Returns(new SimpleResponse(SimpleResponsePurport.Acknowledged));
|
||||
proxy.As<ICommunicationObject>().Setup(o => o.State).Returns(CommunicationState.Opened);
|
||||
proxy.Setup(o => o.State).Returns(CommunicationState.Opened);
|
||||
proxyObjectFactory.Setup(f => f.CreateObject(It.IsAny<string>())).Returns(proxy.Object);
|
||||
|
||||
var token = Guid.NewGuid();
|
||||
|
@ -279,7 +278,7 @@ namespace SafeExamBrowser.Core.UnitTests.Communication.Proxies
|
|||
public void TestConnectionMustInvokeConnectionLostEvent()
|
||||
{
|
||||
var lost = false;
|
||||
var proxy = new Mock<ICommunication>();
|
||||
var proxy = new Mock<IProxyObject>();
|
||||
var connectionResponse = new ConnectionResponse
|
||||
{
|
||||
CommunicationToken = Guid.NewGuid(),
|
||||
|
@ -290,7 +289,7 @@ namespace SafeExamBrowser.Core.UnitTests.Communication.Proxies
|
|||
|
||||
proxy.Setup(p => p.Connect(It.IsAny<Guid>())).Returns(connectionResponse);
|
||||
proxy.Setup(p => p.Send(It.Is<SimpleMessage>(m => m.Purport == SimpleMessagePurport.Ping))).Returns(new SimpleResponse(SimpleResponsePurport.UnknownMessage));
|
||||
proxy.As<ICommunicationObject>().Setup(o => o.State).Returns(CommunicationState.Opened);
|
||||
proxy.Setup(o => o.State).Returns(CommunicationState.Opened);
|
||||
proxyObjectFactory.Setup(f => f.CreateObject(It.IsAny<string>())).Returns(proxy.Object);
|
||||
|
||||
var token = Guid.NewGuid();
|
||||
|
@ -305,7 +304,7 @@ namespace SafeExamBrowser.Core.UnitTests.Communication.Proxies
|
|||
public void TestConnectionMustNotFail()
|
||||
{
|
||||
var lost = false;
|
||||
var proxy = new Mock<ICommunication>();
|
||||
var proxy = new Mock<IProxyObject>();
|
||||
var connectionResponse = new ConnectionResponse
|
||||
{
|
||||
CommunicationToken = Guid.NewGuid(),
|
||||
|
@ -316,7 +315,7 @@ namespace SafeExamBrowser.Core.UnitTests.Communication.Proxies
|
|||
|
||||
proxy.Setup(p => p.Connect(It.IsAny<Guid>())).Returns(connectionResponse);
|
||||
proxy.Setup(p => p.Send(It.IsAny<Message>())).Throws<Exception>();
|
||||
proxy.As<ICommunicationObject>().Setup(o => o.State).Returns(CommunicationState.Opened);
|
||||
proxy.Setup(o => o.State).Returns(CommunicationState.Opened);
|
||||
proxyObjectFactory.Setup(f => f.CreateObject(It.IsAny<string>())).Returns(proxy.Object);
|
||||
|
||||
var token = Guid.NewGuid();
|
||||
|
|
|
@ -23,7 +23,7 @@ namespace SafeExamBrowser.Core.UnitTests.Communication.Proxies
|
|||
{
|
||||
private Mock<ILogger> logger;
|
||||
private Mock<IProxyObjectFactory> proxyObjectFactory;
|
||||
private Mock<ICommunication> proxy;
|
||||
private Mock<IProxyObject> proxy;
|
||||
private ClientProxy sut;
|
||||
|
||||
[TestInitialize]
|
||||
|
@ -37,10 +37,10 @@ namespace SafeExamBrowser.Core.UnitTests.Communication.Proxies
|
|||
|
||||
logger = new Mock<ILogger>();
|
||||
proxyObjectFactory = new Mock<IProxyObjectFactory>();
|
||||
proxy = new Mock<ICommunication>();
|
||||
proxy = new Mock<IProxyObject>();
|
||||
|
||||
proxy.Setup(p => p.Connect(It.IsAny<Guid>())).Returns(response);
|
||||
proxy.As<ICommunicationObject>().Setup(o => o.State).Returns(CommunicationState.Opened);
|
||||
proxy.Setup(o => o.State).Returns(CommunicationState.Opened);
|
||||
proxyObjectFactory.Setup(f => f.CreateObject(It.IsAny<string>())).Returns(proxy.Object);
|
||||
|
||||
sut = new ClientProxy("net.pipe://random/address/here", proxyObjectFactory.Object, logger.Object);
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace SafeExamBrowser.Core.UnitTests.Communication.Proxies
|
|||
{
|
||||
private Mock<ILogger> logger;
|
||||
private Mock<IProxyObjectFactory> proxyObjectFactory;
|
||||
private Mock<ICommunication> proxy;
|
||||
private Mock<IProxyObject> proxy;
|
||||
private RuntimeProxy sut;
|
||||
|
||||
[TestInitialize]
|
||||
|
@ -38,10 +38,10 @@ namespace SafeExamBrowser.Core.UnitTests.Communication.Proxies
|
|||
|
||||
logger = new Mock<ILogger>();
|
||||
proxyObjectFactory = new Mock<IProxyObjectFactory>();
|
||||
proxy = new Mock<ICommunication>();
|
||||
proxy = new Mock<IProxyObject>();
|
||||
|
||||
proxy.Setup(p => p.Connect(It.IsAny<Guid>())).Returns(response);
|
||||
proxy.As<ICommunicationObject>().Setup(o => o.State).Returns(CommunicationState.Opened);
|
||||
proxy.Setup(o => o.State).Returns(CommunicationState.Opened);
|
||||
proxyObjectFactory.Setup(f => f.CreateObject(It.IsAny<string>())).Returns(proxy.Object);
|
||||
|
||||
sut = new RuntimeProxy("net.pipe://random/address/here", proxyObjectFactory.Object, logger.Object);
|
||||
|
|
|
@ -23,7 +23,7 @@ namespace SafeExamBrowser.Core.UnitTests.Communication.Proxies
|
|||
{
|
||||
private Mock<ILogger> logger;
|
||||
private Mock<IProxyObjectFactory> proxyObjectFactory;
|
||||
private Mock<ICommunication> proxy;
|
||||
private Mock<IProxyObject> proxy;
|
||||
private ServiceProxy sut;
|
||||
|
||||
[TestInitialize]
|
||||
|
@ -37,10 +37,10 @@ namespace SafeExamBrowser.Core.UnitTests.Communication.Proxies
|
|||
|
||||
logger = new Mock<ILogger>();
|
||||
proxyObjectFactory = new Mock<IProxyObjectFactory>();
|
||||
proxy = new Mock<ICommunication>();
|
||||
proxy = new Mock<IProxyObject>();
|
||||
|
||||
proxy.Setup(p => p.Connect(It.IsAny<Guid>())).Returns(response);
|
||||
proxy.As<ICommunicationObject>().Setup(o => o.State).Returns(CommunicationState.Opened);
|
||||
proxy.Setup(o => o.State).Returns(CommunicationState.Opened);
|
||||
proxyObjectFactory.Setup(f => f.CreateObject(It.IsAny<string>())).Returns(proxy.Object);
|
||||
|
||||
sut = new ServiceProxy("net.pipe://random/address/here", proxyObjectFactory.Object, logger.Object);
|
||||
|
|
|
@ -84,6 +84,8 @@
|
|||
<Compile Include="Behaviour\OperationModel\I18nOperationTests.cs" />
|
||||
<Compile Include="Behaviour\OperationModel\DelegateOperationTests.cs" />
|
||||
<Compile Include="Behaviour\OperationModel\OperationSequenceTests.cs" />
|
||||
<Compile Include="Communication\Hosts\BaseHostImpl.cs" />
|
||||
<Compile Include="Communication\Hosts\BaseHostTests.cs" />
|
||||
<Compile Include="Communication\Proxies\BaseProxyImpl.cs" />
|
||||
<Compile Include="Communication\Proxies\BaseProxyTests.cs" />
|
||||
<Compile Include="Communication\Proxies\ClientProxyTests.cs" />
|
||||
|
|
|
@ -11,6 +11,7 @@ using System.ServiceModel;
|
|||
using System.Threading;
|
||||
using SafeExamBrowser.Contracts.Communication;
|
||||
using SafeExamBrowser.Contracts.Communication.Data;
|
||||
using SafeExamBrowser.Contracts.Communication.Hosts;
|
||||
using SafeExamBrowser.Contracts.Logging;
|
||||
|
||||
namespace SafeExamBrowser.Core.Communication.Hosts
|
||||
|
@ -25,7 +26,8 @@ namespace SafeExamBrowser.Core.Communication.Hosts
|
|||
private readonly object @lock = new object();
|
||||
|
||||
private string address;
|
||||
private ServiceHost host;
|
||||
private IHostObject host;
|
||||
private IHostObjectFactory factory;
|
||||
private Thread hostThread;
|
||||
|
||||
protected Guid? CommunicationToken { get; private set; }
|
||||
|
@ -42,9 +44,10 @@ namespace SafeExamBrowser.Core.Communication.Hosts
|
|||
}
|
||||
}
|
||||
|
||||
public BaseHost(string address, ILogger logger)
|
||||
public BaseHost(string address, IHostObjectFactory factory, ILogger logger)
|
||||
{
|
||||
this.address = address;
|
||||
this.factory = factory;
|
||||
this.Logger = logger;
|
||||
}
|
||||
|
||||
|
@ -153,7 +156,7 @@ namespace SafeExamBrowser.Core.Communication.Hosts
|
|||
{
|
||||
Logger.Debug($"Terminated communication host for endpoint '{address}'.");
|
||||
}
|
||||
else
|
||||
else if (exception != null)
|
||||
{
|
||||
throw new CommunicationException($"Failed to terminate communication host for endpoint '{address}'!", exception);
|
||||
}
|
||||
|
@ -171,16 +174,15 @@ namespace SafeExamBrowser.Core.Communication.Hosts
|
|||
|
||||
try
|
||||
{
|
||||
host = new ServiceHost(this);
|
||||
host.AddServiceEndpoint(typeof(ICommunication), new NetNamedPipeBinding(NetNamedPipeSecurityMode.Transport), address);
|
||||
host = factory.CreateObject(address, this);
|
||||
|
||||
host.Closed += Host_Closed;
|
||||
host.Closing += Host_Closing;
|
||||
host.Faulted += Host_Faulted;
|
||||
host.Opened += Host_Opened;
|
||||
host.Opening += Host_Opening;
|
||||
host.UnknownMessageReceived += Host_UnknownMessageReceived;
|
||||
host.Open();
|
||||
|
||||
host.Open();
|
||||
Logger.Debug($"Successfully started communication host for endpoint '{address}'.");
|
||||
|
||||
startedEvent.Set();
|
||||
|
@ -200,7 +202,7 @@ namespace SafeExamBrowser.Core.Communication.Hosts
|
|||
try
|
||||
{
|
||||
host?.Close();
|
||||
success = hostThread.Join(TWO_SECONDS);
|
||||
success = hostThread?.Join(TWO_SECONDS) == true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -236,11 +238,6 @@ namespace SafeExamBrowser.Core.Communication.Hosts
|
|||
Logger.Debug("Communication host is opening...");
|
||||
}
|
||||
|
||||
private void Host_UnknownMessageReceived(object sender, UnknownMessageReceivedEventArgs e)
|
||||
{
|
||||
Logger.Warn($"Communication host has received an unknown message: {e?.Message}.");
|
||||
}
|
||||
|
||||
private string ToString(Message message)
|
||||
{
|
||||
return message != null ? message.ToString() : "<null>";
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* 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 SafeExamBrowser.Contracts.Communication;
|
||||
using SafeExamBrowser.Contracts.Communication.Hosts;
|
||||
|
||||
namespace SafeExamBrowser.Core.Communication.Hosts
|
||||
{
|
||||
/// <summary>
|
||||
/// Default implementation of the <see cref="IHostObjectFactory"/> utilizing WCF (<see cref="ServiceHost"/>).
|
||||
/// </summary>
|
||||
public class HostObjectFactory : IHostObjectFactory
|
||||
{
|
||||
public IHostObject CreateObject(string address, ICommunication communicationObject)
|
||||
{
|
||||
var host = new Host(communicationObject);
|
||||
|
||||
host.AddServiceEndpoint(typeof(ICommunication), new NetNamedPipeBinding(NetNamedPipeSecurityMode.Transport), address);
|
||||
|
||||
return host;
|
||||
}
|
||||
|
||||
private class Host : ServiceHost, IHostObject
|
||||
{
|
||||
internal Host(object singletonInstance, params Uri[] baseAddresses) : base(singletonInstance, baseAddresses)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,7 +25,7 @@ namespace SafeExamBrowser.Core.Communication.Proxies
|
|||
private static readonly object @lock = new object();
|
||||
|
||||
private string address;
|
||||
private ICommunication proxy;
|
||||
private IProxyObject proxy;
|
||||
private IProxyObjectFactory factory;
|
||||
private Guid? communicationToken;
|
||||
private Timer timer;
|
||||
|
@ -43,19 +43,10 @@ namespace SafeExamBrowser.Core.Communication.Proxies
|
|||
|
||||
public virtual bool Connect(Guid? token = null, bool autoPing = true)
|
||||
{
|
||||
proxy = factory.CreateObject(address);
|
||||
|
||||
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)}...");
|
||||
|
||||
InitializeProxyObject();
|
||||
|
||||
var response = proxy.Connect(token);
|
||||
|
||||
communicationToken = response.CommunicationToken;
|
||||
|
@ -160,13 +151,24 @@ namespace SafeExamBrowser.Core.Communication.Proxies
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// etrieves the string representation of the given <see cref="Response"/>, or indicates that a response is <c>null</c>.
|
||||
/// Retrieves 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>";
|
||||
}
|
||||
|
||||
private void InitializeProxyObject()
|
||||
{
|
||||
proxy = factory.CreateObject(address);
|
||||
|
||||
proxy.Closed += BaseProxy_Closed;
|
||||
proxy.Closing += BaseProxy_Closing;
|
||||
proxy.Faulted += BaseProxy_Faulted;
|
||||
proxy.Opened += BaseProxy_Opened;
|
||||
proxy.Opening += BaseProxy_Opening;
|
||||
}
|
||||
|
||||
private void BaseProxy_Closed(object sender, EventArgs e)
|
||||
{
|
||||
Logger.Debug("Communication channel has been closed.");
|
||||
|
@ -199,7 +201,7 @@ namespace SafeExamBrowser.Core.Communication.Proxies
|
|||
throw new InvalidOperationException($"Cannot perform '{operationName}' before being connected to endpoint!");
|
||||
}
|
||||
|
||||
if (proxy == null || (proxy as ICommunicationObject)?.State != CommunicationState.Opened)
|
||||
if (proxy == null || proxy.State != CommunicationState.Opened)
|
||||
{
|
||||
throw new CommunicationException($"Tried to perform {operationName}, but channel was {GetChannelState()}!");
|
||||
}
|
||||
|
@ -207,7 +209,7 @@ namespace SafeExamBrowser.Core.Communication.Proxies
|
|||
|
||||
private string GetChannelState()
|
||||
{
|
||||
return proxy == null ? "null" : $"in state '{(proxy as ICommunicationObject)?.State}'";
|
||||
return proxy == null ? "null" : $"in state '{proxy.State}'";
|
||||
}
|
||||
|
||||
private void StartAutoPing()
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
*/
|
||||
|
||||
using System.ServiceModel;
|
||||
using SafeExamBrowser.Contracts.Communication;
|
||||
using SafeExamBrowser.Contracts.Communication.Proxies;
|
||||
|
||||
namespace SafeExamBrowser.Core.Communication.Proxies
|
||||
|
@ -17,10 +16,10 @@ namespace SafeExamBrowser.Core.Communication.Proxies
|
|||
/// </summary>
|
||||
public class ProxyObjectFactory : IProxyObjectFactory
|
||||
{
|
||||
public ICommunication CreateObject(string address)
|
||||
public IProxyObject CreateObject(string address)
|
||||
{
|
||||
var endpoint = new EndpointAddress(address);
|
||||
var channel = ChannelFactory<ICommunication>.CreateChannel(new NetNamedPipeBinding(NetNamedPipeSecurityMode.Transport), endpoint);
|
||||
var channel = ChannelFactory<IProxyObject>.CreateChannel(new NetNamedPipeBinding(NetNamedPipeSecurityMode.Transport), endpoint);
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
|
|
@ -60,6 +60,7 @@
|
|||
<Compile Include="Behaviour\OperationModel\I18nOperation.cs" />
|
||||
<Compile Include="Behaviour\OperationModel\DelegateOperation.cs" />
|
||||
<Compile Include="Behaviour\OperationModel\OperationSequence.cs" />
|
||||
<Compile Include="Communication\Hosts\HostObjectFactory.cs" />
|
||||
<Compile Include="Communication\Proxies\BaseProxy.cs" />
|
||||
<Compile Include="Communication\Hosts\BaseHost.cs" />
|
||||
<Compile Include="Communication\Proxies\ClientProxy.cs" />
|
||||
|
|
|
@ -30,7 +30,7 @@ namespace SafeExamBrowser.Runtime.Communication
|
|||
public event CommunicationEventHandler ReconfigurationRequested;
|
||||
public event CommunicationEventHandler ShutdownRequested;
|
||||
|
||||
public RuntimeHost(string address, IConfigurationRepository configuration, ILogger logger) : base(address, logger)
|
||||
public RuntimeHost(string address, IConfigurationRepository configuration, IHostObjectFactory factory, ILogger logger) : base(address, factory, logger)
|
||||
{
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ using SafeExamBrowser.Contracts.Behaviour.OperationModel;
|
|||
using SafeExamBrowser.Contracts.Configuration;
|
||||
using SafeExamBrowser.Contracts.Logging;
|
||||
using SafeExamBrowser.Core.Behaviour.OperationModel;
|
||||
using SafeExamBrowser.Core.Communication.Hosts;
|
||||
using SafeExamBrowser.Core.Communication.Proxies;
|
||||
using SafeExamBrowser.Core.I18n;
|
||||
using SafeExamBrowser.Core.Logging;
|
||||
|
@ -51,7 +52,7 @@ namespace SafeExamBrowser.Runtime
|
|||
var desktop = new Desktop(new ModuleLogger(logger, typeof(Desktop)));
|
||||
var processFactory = new ProcessFactory(desktop, new ModuleLogger(logger, typeof(ProcessFactory)));
|
||||
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 HostObjectFactory(), new ModuleLogger(logger, typeof(RuntimeHost)));
|
||||
var serviceProxy = new ServiceProxy(runtimeInfo.ServiceAddress, new ProxyObjectFactory(), new ModuleLogger(logger, typeof(ServiceProxy)));
|
||||
var sessionController = new SessionController(configuration, logger, processFactory, proxyFactory, runtimeHost, serviceProxy);
|
||||
|
||||
|
|
Loading…
Reference in a new issue