diff --git a/SafeExamBrowser.Client/Communication/ClientHost.cs b/SafeExamBrowser.Client/Communication/ClientHost.cs
index 47f5395c..4f62cf73 100644
--- a/SafeExamBrowser.Client/Communication/ClientHost.cs
+++ b/SafeExamBrowser.Client/Communication/ClientHost.cs
@@ -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;
}
diff --git a/SafeExamBrowser.Client/CompositionRoot.cs b/SafeExamBrowser.Client/CompositionRoot.cs
index 9279a868..062224e9 100644
--- a/SafeExamBrowser.Client/CompositionRoot.cs
+++ b/SafeExamBrowser.Client/CompositionRoot.cs
@@ -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;
diff --git a/SafeExamBrowser.Contracts/Communication/Hosts/IHostObject.cs b/SafeExamBrowser.Contracts/Communication/Hosts/IHostObject.cs
new file mode 100644
index 00000000..d1fd739d
--- /dev/null
+++ b/SafeExamBrowser.Contracts/Communication/Hosts/IHostObject.cs
@@ -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
+{
+ ///
+ /// The host object to be used in communication hosts.
+ ///
+ public interface IHostObject : ICommunicationObject
+ {
+ }
+}
diff --git a/SafeExamBrowser.Contracts/Communication/Hosts/IHostObjectFactory.cs b/SafeExamBrowser.Contracts/Communication/Hosts/IHostObjectFactory.cs
new file mode 100644
index 00000000..aeb61921
--- /dev/null
+++ b/SafeExamBrowser.Contracts/Communication/Hosts/IHostObjectFactory.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.Hosts
+{
+ ///
+ /// A factory to create host objects for communication hosts.
+ ///
+ public interface IHostObjectFactory
+ {
+ ///
+ /// Utilizes the given communication object to create a host object (see ) for the specified endpoint address.
+ ///
+ IHostObject CreateObject(string address, ICommunication communicationObject);
+ }
+}
diff --git a/SafeExamBrowser.Contracts/Communication/Proxies/IProxyObject.cs b/SafeExamBrowser.Contracts/Communication/Proxies/IProxyObject.cs
new file mode 100644
index 00000000..359d42b7
--- /dev/null
+++ b/SafeExamBrowser.Contracts/Communication/Proxies/IProxyObject.cs
@@ -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
+{
+ ///
+ /// The communication object to be used in an .
+ ///
+ public interface IProxyObject : ICommunication, ICommunicationObject
+ {
+ }
+}
diff --git a/SafeExamBrowser.Contracts/Communication/Proxies/IProxyObjectFactory.cs b/SafeExamBrowser.Contracts/Communication/Proxies/IProxyObjectFactory.cs
index d10d85d3..31c459d4 100644
--- a/SafeExamBrowser.Contracts/Communication/Proxies/IProxyObjectFactory.cs
+++ b/SafeExamBrowser.Contracts/Communication/Proxies/IProxyObjectFactory.cs
@@ -9,13 +9,13 @@
namespace SafeExamBrowser.Contracts.Communication.Proxies
{
///
- /// A factory to create communication objects for proxies.
+ /// A factory to create proxy objects for communication proxies.
///
public interface IProxyObjectFactory
{
///
- /// Creates a communication object (see ) for the specified endpoint address.
+ /// Creates a proxy object (see ) for the specified endpoint address.
///
- ICommunication CreateObject(string address);
+ IProxyObject CreateObject(string address);
}
}
diff --git a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj
index c7226e24..f4b5c8e8 100644
--- a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj
+++ b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj
@@ -58,10 +58,13 @@
+
+
+
diff --git a/SafeExamBrowser.Core.UnitTests/Communication/Hosts/BaseHostImpl.cs b/SafeExamBrowser.Core.UnitTests/Communication/Hosts/BaseHostImpl.cs
new file mode 100644
index 00000000..b0fb5a11
--- /dev/null
+++ b/SafeExamBrowser.Core.UnitTests/Communication/Hosts/BaseHostImpl.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 OnConnectStub { get; set; }
+ public Action OnDisconnectStub { get; set; }
+ public Func OnReceiveStub { get; set; }
+ public Func 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);
+ }
+ }
+}
diff --git a/SafeExamBrowser.Core.UnitTests/Communication/Hosts/BaseHostTests.cs b/SafeExamBrowser.Core.UnitTests/Communication/Hosts/BaseHostTests.cs
new file mode 100644
index 00000000..80560a30
--- /dev/null
+++ b/SafeExamBrowser.Core.UnitTests/Communication/Hosts/BaseHostTests.cs
@@ -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 hostObject;
+ private Mock hostObjectFactory;
+ private Mock logger;
+ private BaseHostImpl sut;
+
+ [TestInitialize]
+ public void Initialize()
+ {
+ hostObject = new Mock();
+ hostObjectFactory = new Mock();
+ logger = new Mock();
+
+ hostObjectFactory.Setup(f => f.CreateObject(It.IsAny(), It.IsAny())).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(), sut), Times.Once);
+
+ Assert.AreNotEqual(Thread.CurrentThread.ManagedThreadId, threadId);
+ }
+
+ [TestMethod]
+ [ExpectedException(typeof(CommunicationException))]
+ public void MustCorrectlyHandleStartupException()
+ {
+ hostObject.Setup(h => h.Open()).Throws();
+
+ 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();
+
+ 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();
+ }
+ }
+}
diff --git a/SafeExamBrowser.Core.UnitTests/Communication/Proxies/BaseProxyTests.cs b/SafeExamBrowser.Core.UnitTests/Communication/Proxies/BaseProxyTests.cs
index 96c5b8e7..24d9cca9 100644
--- a/SafeExamBrowser.Core.UnitTests/Communication/Proxies/BaseProxyTests.cs
+++ b/SafeExamBrowser.Core.UnitTests/Communication/Proxies/BaseProxyTests.cs
@@ -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();
+ var proxy = new Mock();
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();
+ var proxy = new Mock();
var connectionResponse = new ConnectionResponse
{
CommunicationToken = Guid.NewGuid(),
@@ -71,7 +70,7 @@ namespace SafeExamBrowser.Core.UnitTests.Communication.Proxies
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);
+ proxy.Setup(o => o.State).Returns(CommunicationState.Opened);
proxyObjectFactory.Setup(f => f.CreateObject(It.IsAny())).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();
+ var proxy = new Mock();
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();
+ var proxy = new Mock();
var response = new ConnectionResponse
{
CommunicationToken = Guid.NewGuid(),
@@ -125,7 +124,7 @@ namespace SafeExamBrowser.Core.UnitTests.Communication.Proxies
};
proxy.Setup(p => p.Connect(It.IsAny())).Returns(response);
- proxy.As().Setup(o => o.State).Returns(CommunicationState.Faulted);
+ proxy.Setup(o => o.State).Returns(CommunicationState.Faulted);
proxyObjectFactory.Setup(f => f.CreateObject(It.IsAny())).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();
+ var proxy = new Mock();
var response = new ConnectionResponse
{
CommunicationToken = Guid.NewGuid(),
@@ -153,7 +152,7 @@ namespace SafeExamBrowser.Core.UnitTests.Communication.Proxies
};
proxy.Setup(p => p.Connect(It.IsAny())).Returns(response);
- proxy.As().Setup(o => o.State).Returns(CommunicationState.Faulted);
+ proxy.Setup(o => o.State).Returns(CommunicationState.Faulted);
proxyObjectFactory.Setup(f => f.CreateObject(It.IsAny())).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();
+ var proxy = new Mock();
var connectionResponse = new ConnectionResponse
{
CommunicationToken = Guid.NewGuid(),
@@ -183,7 +182,7 @@ namespace SafeExamBrowser.Core.UnitTests.Communication.Proxies
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);
+ proxy.Setup(o => o.State).Returns(CommunicationState.Opened);
proxyObjectFactory.Setup(f => f.CreateObject(It.IsAny())).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();
+ var proxy = new Mock();
var connectionResponse = new ConnectionResponse
{
CommunicationToken = Guid.NewGuid(),
@@ -208,7 +207,7 @@ namespace SafeExamBrowser.Core.UnitTests.Communication.Proxies
proxy.Setup(p => p.Connect(It.IsAny())).Returns(connectionResponse);
proxy.Setup(p => p.Send(It.IsAny())).Returns(response.Object);
- proxy.As().Setup(o => o.State).Returns(CommunicationState.Opened);
+ proxy.Setup(o => o.State).Returns(CommunicationState.Opened);
proxyObjectFactory.Setup(f => f.CreateObject(It.IsAny())).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();
+ var proxy = new Mock();
var connectionResponse = new ConnectionResponse
{
CommunicationToken = Guid.NewGuid(),
@@ -264,7 +263,7 @@ namespace SafeExamBrowser.Core.UnitTests.Communication.Proxies
proxy.Setup(p => p.Connect(It.IsAny())).Returns(connectionResponse);
proxy.Setup(p => p.Send(It.Is(m => m.Purport == SimpleMessagePurport.Ping))).Returns(new SimpleResponse(SimpleResponsePurport.Acknowledged));
- proxy.As().Setup(o => o.State).Returns(CommunicationState.Opened);
+ proxy.Setup(o => o.State).Returns(CommunicationState.Opened);
proxyObjectFactory.Setup(f => f.CreateObject(It.IsAny())).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();
+ var proxy = new Mock();
var connectionResponse = new ConnectionResponse
{
CommunicationToken = Guid.NewGuid(),
@@ -290,7 +289,7 @@ namespace SafeExamBrowser.Core.UnitTests.Communication.Proxies
proxy.Setup(p => p.Connect(It.IsAny())).Returns(connectionResponse);
proxy.Setup(p => p.Send(It.Is(m => m.Purport == SimpleMessagePurport.Ping))).Returns(new SimpleResponse(SimpleResponsePurport.UnknownMessage));
- proxy.As().Setup(o => o.State).Returns(CommunicationState.Opened);
+ proxy.Setup(o => o.State).Returns(CommunicationState.Opened);
proxyObjectFactory.Setup(f => f.CreateObject(It.IsAny())).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();
+ var proxy = new Mock();
var connectionResponse = new ConnectionResponse
{
CommunicationToken = Guid.NewGuid(),
@@ -316,7 +315,7 @@ namespace SafeExamBrowser.Core.UnitTests.Communication.Proxies
proxy.Setup(p => p.Connect(It.IsAny())).Returns(connectionResponse);
proxy.Setup(p => p.Send(It.IsAny())).Throws();
- proxy.As().Setup(o => o.State).Returns(CommunicationState.Opened);
+ proxy.Setup(o => o.State).Returns(CommunicationState.Opened);
proxyObjectFactory.Setup(f => f.CreateObject(It.IsAny())).Returns(proxy.Object);
var token = Guid.NewGuid();
diff --git a/SafeExamBrowser.Core.UnitTests/Communication/Proxies/ClientProxyTests.cs b/SafeExamBrowser.Core.UnitTests/Communication/Proxies/ClientProxyTests.cs
index 97205118..7424271b 100644
--- a/SafeExamBrowser.Core.UnitTests/Communication/Proxies/ClientProxyTests.cs
+++ b/SafeExamBrowser.Core.UnitTests/Communication/Proxies/ClientProxyTests.cs
@@ -23,7 +23,7 @@ namespace SafeExamBrowser.Core.UnitTests.Communication.Proxies
{
private Mock logger;
private Mock proxyObjectFactory;
- private Mock proxy;
+ private Mock proxy;
private ClientProxy sut;
[TestInitialize]
@@ -37,10 +37,10 @@ namespace SafeExamBrowser.Core.UnitTests.Communication.Proxies
logger = new Mock();
proxyObjectFactory = new Mock();
- proxy = new Mock();
+ proxy = new Mock();
proxy.Setup(p => p.Connect(It.IsAny())).Returns(response);
- proxy.As().Setup(o => o.State).Returns(CommunicationState.Opened);
+ proxy.Setup(o => o.State).Returns(CommunicationState.Opened);
proxyObjectFactory.Setup(f => f.CreateObject(It.IsAny())).Returns(proxy.Object);
sut = new ClientProxy("net.pipe://random/address/here", proxyObjectFactory.Object, logger.Object);
diff --git a/SafeExamBrowser.Core.UnitTests/Communication/Proxies/RuntimeProxyTests.cs b/SafeExamBrowser.Core.UnitTests/Communication/Proxies/RuntimeProxyTests.cs
index 7a22d067..7c6a7f9c 100644
--- a/SafeExamBrowser.Core.UnitTests/Communication/Proxies/RuntimeProxyTests.cs
+++ b/SafeExamBrowser.Core.UnitTests/Communication/Proxies/RuntimeProxyTests.cs
@@ -24,7 +24,7 @@ namespace SafeExamBrowser.Core.UnitTests.Communication.Proxies
{
private Mock logger;
private Mock proxyObjectFactory;
- private Mock proxy;
+ private Mock proxy;
private RuntimeProxy sut;
[TestInitialize]
@@ -38,10 +38,10 @@ namespace SafeExamBrowser.Core.UnitTests.Communication.Proxies
logger = new Mock();
proxyObjectFactory = new Mock();
- proxy = new Mock();
+ proxy = new Mock();
proxy.Setup(p => p.Connect(It.IsAny())).Returns(response);
- proxy.As().Setup(o => o.State).Returns(CommunicationState.Opened);
+ proxy.Setup(o => o.State).Returns(CommunicationState.Opened);
proxyObjectFactory.Setup(f => f.CreateObject(It.IsAny())).Returns(proxy.Object);
sut = new RuntimeProxy("net.pipe://random/address/here", proxyObjectFactory.Object, logger.Object);
diff --git a/SafeExamBrowser.Core.UnitTests/Communication/Proxies/ServiceProxyTests.cs b/SafeExamBrowser.Core.UnitTests/Communication/Proxies/ServiceProxyTests.cs
index b610838f..3385aa36 100644
--- a/SafeExamBrowser.Core.UnitTests/Communication/Proxies/ServiceProxyTests.cs
+++ b/SafeExamBrowser.Core.UnitTests/Communication/Proxies/ServiceProxyTests.cs
@@ -23,7 +23,7 @@ namespace SafeExamBrowser.Core.UnitTests.Communication.Proxies
{
private Mock logger;
private Mock proxyObjectFactory;
- private Mock proxy;
+ private Mock proxy;
private ServiceProxy sut;
[TestInitialize]
@@ -37,10 +37,10 @@ namespace SafeExamBrowser.Core.UnitTests.Communication.Proxies
logger = new Mock();
proxyObjectFactory = new Mock();
- proxy = new Mock();
+ proxy = new Mock();
proxy.Setup(p => p.Connect(It.IsAny())).Returns(response);
- proxy.As().Setup(o => o.State).Returns(CommunicationState.Opened);
+ proxy.Setup(o => o.State).Returns(CommunicationState.Opened);
proxyObjectFactory.Setup(f => f.CreateObject(It.IsAny())).Returns(proxy.Object);
sut = new ServiceProxy("net.pipe://random/address/here", proxyObjectFactory.Object, logger.Object);
diff --git a/SafeExamBrowser.Core.UnitTests/SafeExamBrowser.Core.UnitTests.csproj b/SafeExamBrowser.Core.UnitTests/SafeExamBrowser.Core.UnitTests.csproj
index dc7fb843..630f0e30 100644
--- a/SafeExamBrowser.Core.UnitTests/SafeExamBrowser.Core.UnitTests.csproj
+++ b/SafeExamBrowser.Core.UnitTests/SafeExamBrowser.Core.UnitTests.csproj
@@ -84,6 +84,8 @@
+
+
diff --git a/SafeExamBrowser.Core/Communication/Hosts/BaseHost.cs b/SafeExamBrowser.Core/Communication/Hosts/BaseHost.cs
index 93540c09..85df3f6d 100644
--- a/SafeExamBrowser.Core/Communication/Hosts/BaseHost.cs
+++ b/SafeExamBrowser.Core/Communication/Hosts/BaseHost.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() : "";
diff --git a/SafeExamBrowser.Core/Communication/Hosts/HostObjectFactory.cs b/SafeExamBrowser.Core/Communication/Hosts/HostObjectFactory.cs
new file mode 100644
index 00000000..063bb24f
--- /dev/null
+++ b/SafeExamBrowser.Core/Communication/Hosts/HostObjectFactory.cs
@@ -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
+{
+ ///
+ /// Default implementation of the utilizing WCF ().
+ ///
+ 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)
+ {
+ }
+ }
+ }
+}
diff --git a/SafeExamBrowser.Core/Communication/Proxies/BaseProxy.cs b/SafeExamBrowser.Core/Communication/Proxies/BaseProxy.cs
index cca1ad89..862d2a20 100644
--- a/SafeExamBrowser.Core/Communication/Proxies/BaseProxy.cs
+++ b/SafeExamBrowser.Core/Communication/Proxies/BaseProxy.cs
@@ -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
}
///
- /// etrieves the string representation of the given , or indicates that a response is null.
+ /// Retrieves the string representation of the given , or indicates that a response is null.
///
protected string ToString(Response response)
{
return response != null ? response.ToString() : "";
}
+ 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()
diff --git a/SafeExamBrowser.Core/Communication/Proxies/ProxyObjectFactory.cs b/SafeExamBrowser.Core/Communication/Proxies/ProxyObjectFactory.cs
index b816f8d2..0dd9462c 100644
--- a/SafeExamBrowser.Core/Communication/Proxies/ProxyObjectFactory.cs
+++ b/SafeExamBrowser.Core/Communication/Proxies/ProxyObjectFactory.cs
@@ -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
///
public class ProxyObjectFactory : IProxyObjectFactory
{
- public ICommunication CreateObject(string address)
+ public IProxyObject CreateObject(string address)
{
var endpoint = new EndpointAddress(address);
- var channel = ChannelFactory.CreateChannel(new NetNamedPipeBinding(NetNamedPipeSecurityMode.Transport), endpoint);
+ var channel = ChannelFactory.CreateChannel(new NetNamedPipeBinding(NetNamedPipeSecurityMode.Transport), endpoint);
return channel;
}
diff --git a/SafeExamBrowser.Core/SafeExamBrowser.Core.csproj b/SafeExamBrowser.Core/SafeExamBrowser.Core.csproj
index 7f22750b..553c2361 100644
--- a/SafeExamBrowser.Core/SafeExamBrowser.Core.csproj
+++ b/SafeExamBrowser.Core/SafeExamBrowser.Core.csproj
@@ -60,6 +60,7 @@
+
diff --git a/SafeExamBrowser.Runtime/Communication/RuntimeHost.cs b/SafeExamBrowser.Runtime/Communication/RuntimeHost.cs
index c2876f3b..419c9f05 100644
--- a/SafeExamBrowser.Runtime/Communication/RuntimeHost.cs
+++ b/SafeExamBrowser.Runtime/Communication/RuntimeHost.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;
}
diff --git a/SafeExamBrowser.Runtime/CompositionRoot.cs b/SafeExamBrowser.Runtime/CompositionRoot.cs
index 1f3de615..bce653e3 100644
--- a/SafeExamBrowser.Runtime/CompositionRoot.cs
+++ b/SafeExamBrowser.Runtime/CompositionRoot.cs
@@ -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);