From e750f870c0d58ac95312ae62d81fcf6108a710d8 Mon Sep 17 00:00:00 2001 From: dbuechel Date: Tue, 20 Mar 2018 09:21:10 +0100 Subject: [PATCH] SEBWIN-219: Completed provisional implementation of unit test for communication hosts. --- .../Communication/ClientHostTests.cs | 163 +++++++++++++++++ .../SafeExamBrowser.Client.UnitTests.csproj | 5 + .../Communication/Hosts/BaseHostTests.cs | 61 ++++++- .../Communication/RuntimeHostTests.cs | 168 ++++++++++++++++++ .../SafeExamBrowser.Runtime.UnitTests.csproj | 5 + .../Communication/RuntimeHost.cs | 2 + 6 files changed, 398 insertions(+), 6 deletions(-) create mode 100644 SafeExamBrowser.Client.UnitTests/Communication/ClientHostTests.cs create mode 100644 SafeExamBrowser.Runtime.UnitTests/Communication/RuntimeHostTests.cs diff --git a/SafeExamBrowser.Client.UnitTests/Communication/ClientHostTests.cs b/SafeExamBrowser.Client.UnitTests/Communication/ClientHostTests.cs new file mode 100644 index 00000000..88ea4e35 --- /dev/null +++ b/SafeExamBrowser.Client.UnitTests/Communication/ClientHostTests.cs @@ -0,0 +1,163 @@ +/* + * 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 Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using SafeExamBrowser.Client.Communication; +using SafeExamBrowser.Contracts.Communication; +using SafeExamBrowser.Contracts.Communication.Data; +using SafeExamBrowser.Contracts.Communication.Hosts; +using SafeExamBrowser.Contracts.Configuration; +using SafeExamBrowser.Contracts.Logging; + +namespace SafeExamBrowser.Client.UnitTests.Communication +{ + [TestClass] + public class ClientHostTests + { + private const int PROCESS_ID = 1234; + + private Mock configuration; + private Mock hostObject; + private Mock hostObjectFactory; + private Mock logger; + private ClientHost sut; + + [TestInitialize] + public void Initialize() + { + configuration = new Mock(); + hostObject = new Mock(); + hostObjectFactory = new Mock(); + logger = new Mock(); + + hostObjectFactory.Setup(f => f.CreateObject(It.IsAny(), It.IsAny())).Returns(hostObject.Object); + + sut = new ClientHost("net:pipe://some/address", hostObjectFactory.Object, logger.Object, PROCESS_ID); + } + + [TestMethod] + public void MustOnlyAllowConnectionIfTokenIsValid() + { + var token = Guid.NewGuid(); + + sut.StartupToken = token; + + var response = sut.Connect(token); + + Assert.IsNotNull(response); + Assert.IsTrue(response.ConnectionEstablished); + } + + [TestMethod] + public void MustOnlyAllowOneConcurrentConnection() + { + var token = Guid.NewGuid(); + + sut.StartupToken = token; + + var response1 = sut.Connect(token); + var response2 = sut.Connect(token); + var response3 = sut.Connect(token); + + Assert.IsNotNull(response1); + Assert.IsNotNull(response2); + Assert.IsNotNull(response3); + Assert.IsNotNull(response1.CommunicationToken); + Assert.IsNull(response2.CommunicationToken); + Assert.IsNull(response3.CommunicationToken); + Assert.IsTrue(response1.ConnectionEstablished); + Assert.IsFalse(response2.ConnectionEstablished); + Assert.IsFalse(response3.ConnectionEstablished); + } + + [TestMethod] + public void MustCorrectlyDisconnect() + { + var token = Guid.NewGuid(); + + sut.StartupToken = token; + + var connectionResponse = sut.Connect(token); + var response = sut.Disconnect(new DisconnectionMessage { CommunicationToken = connectionResponse.CommunicationToken.Value }); + + Assert.IsNotNull(response); + Assert.IsTrue(response.ConnectionTerminated); + } + + [TestMethod] + public void MustNotAllowReconnectionAfterDisconnection() + { + var token = sut.StartupToken = Guid.NewGuid(); + var response = sut.Connect(token); + + sut.Disconnect(new DisconnectionMessage { CommunicationToken = response.CommunicationToken.Value }); + sut.StartupToken = token = Guid.NewGuid(); + + response = sut.Connect(token); + + Assert.IsFalse(response.ConnectionEstablished); + } + + [TestMethod] + public void MustHandleAuthenticationRequestCorrectly() + { + sut.StartupToken = Guid.Empty; + + var token = sut.Connect(Guid.Empty).CommunicationToken.Value; + var message = new SimpleMessage(SimpleMessagePurport.Authenticate) { CommunicationToken = token }; + var response = sut.Send(message); + + Assert.IsNotNull(response); + Assert.IsInstanceOfType(response, typeof(AuthenticationResponse)); + Assert.AreEqual(PROCESS_ID, (response as AuthenticationResponse)?.ProcessId); + } + + [TestMethod] + public void MustHandleShutdownRequestCorrectly() + { + var shutdownRequested = false; + + sut.Shutdown += () => shutdownRequested = true; + sut.StartupToken = Guid.Empty; + + var token = sut.Connect(Guid.Empty).CommunicationToken.Value; + var message = new SimpleMessage(SimpleMessagePurport.Shutdown) { CommunicationToken = token }; + var response = sut.Send(message); + + Assert.IsTrue(shutdownRequested); + Assert.IsNotNull(response); + Assert.IsInstanceOfType(response, typeof(SimpleResponse)); + Assert.AreEqual(SimpleResponsePurport.Acknowledged, (response as SimpleResponse)?.Purport); + } + + [TestMethod] + public void MustReturnUnknownMessageAsDefault() + { + sut.StartupToken = Guid.Empty; + + var token = sut.Connect(Guid.Empty).CommunicationToken.Value; + var message = new TestMessage { CommunicationToken = token } as Message; + var response = sut.Send(message); + + Assert.IsNotNull(response); + Assert.IsInstanceOfType(response, typeof(SimpleResponse)); + Assert.AreEqual(SimpleResponsePurport.UnknownMessage, (response as SimpleResponse)?.Purport); + + message = new SimpleMessage(default(SimpleMessagePurport)) { CommunicationToken = token }; + response = sut.Send(message); + + Assert.IsNotNull(response); + Assert.IsInstanceOfType(response, typeof(SimpleResponse)); + Assert.AreEqual(SimpleResponsePurport.UnknownMessage, (response as SimpleResponse)?.Purport); + } + + private class TestMessage : Message { }; + } +} diff --git a/SafeExamBrowser.Client.UnitTests/SafeExamBrowser.Client.UnitTests.csproj b/SafeExamBrowser.Client.UnitTests/SafeExamBrowser.Client.UnitTests.csproj index 1d8d954f..2c7a4b34 100644 --- a/SafeExamBrowser.Client.UnitTests/SafeExamBrowser.Client.UnitTests.csproj +++ b/SafeExamBrowser.Client.UnitTests/SafeExamBrowser.Client.UnitTests.csproj @@ -86,6 +86,7 @@ + @@ -105,6 +106,10 @@ {47DA5933-BEF8-4729-94E6-ABDE2DB12262} SafeExamBrowser.Contracts + + {3D6FDBB6-A4AF-4626-BB2B-BF329D44F9CC} + SafeExamBrowser.Core + diff --git a/SafeExamBrowser.Core.UnitTests/Communication/Hosts/BaseHostTests.cs b/SafeExamBrowser.Core.UnitTests/Communication/Hosts/BaseHostTests.cs index 80560a30..cfb12703 100644 --- a/SafeExamBrowser.Core.UnitTests/Communication/Hosts/BaseHostTests.cs +++ b/SafeExamBrowser.Core.UnitTests/Communication/Hosts/BaseHostTests.cs @@ -206,22 +206,71 @@ namespace SafeExamBrowser.Core.UnitTests.Communication.Hosts [TestMethod] public void MustCorrectlyHandlePingMessage() { - // TODO - Assert.Fail(); + var received = false; + var simpleReceived = false; + var message = new SimpleMessage(SimpleMessagePurport.Ping); + + sut.OnReceiveStub = (m) => { received = true; return null; }; + sut.OnReceiveSimpleMessageStub = (m) => { simpleReceived = true; return null; }; + sut.OnConnectStub = (t) => { return true; }; + sut.Connect(); + + message.CommunicationToken = sut.GetCommunicationToken().Value; + + var response = sut.Send(message); + + Assert.IsFalse(received); + Assert.IsFalse(simpleReceived); + Assert.IsInstanceOfType(response, typeof(SimpleResponse)); + Assert.AreEqual(SimpleResponsePurport.Acknowledged, (response as SimpleResponse)?.Purport); } [TestMethod] public void MustCorrectlyReceiveSimpleMessage() { - // TODO - Assert.Fail(); + var received = false; + var simpleReceived = false; + var purport = default(SimpleMessagePurport); + var message = new SimpleMessage(SimpleMessagePurport.ConfigurationNeeded); + var simpleResponse = new SimpleResponse(SimpleResponsePurport.UnknownMessage); + + sut.OnReceiveStub = (m) => { received = true; return null; }; + sut.OnReceiveSimpleMessageStub = (m) => { simpleReceived = true; purport = m; return simpleResponse; }; + sut.OnConnectStub = (t) => { return true; }; + sut.Connect(); + + message.CommunicationToken = sut.GetCommunicationToken().Value; + + var response = sut.Send(message); + + Assert.IsFalse(received); + Assert.IsTrue(simpleReceived); + Assert.IsInstanceOfType(response, typeof(SimpleResponse)); + Assert.AreEqual(SimpleMessagePurport.ConfigurationNeeded, purport); + Assert.AreSame(simpleResponse, response); } [TestMethod] public void MustCorrectlyReceiveMessage() { - // TODO - Assert.Fail(); + var received = false; + var simpleReceived = false; + var message = new ReconfigurationMessage(null); + var reconfigurationResponse = new ReconfigurationResponse(); + + sut.OnReceiveStub = (m) => { received = true; return reconfigurationResponse; }; + sut.OnReceiveSimpleMessageStub = (m) => { simpleReceived = true; return null; }; + sut.OnConnectStub = (t) => { return true; }; + sut.Connect(); + + message.CommunicationToken = sut.GetCommunicationToken().Value; + + var response = sut.Send(message); + + Assert.IsTrue(received); + Assert.IsFalse(simpleReceived); + Assert.IsInstanceOfType(response, typeof(ReconfigurationResponse)); + Assert.AreSame(reconfigurationResponse, response); } } } diff --git a/SafeExamBrowser.Runtime.UnitTests/Communication/RuntimeHostTests.cs b/SafeExamBrowser.Runtime.UnitTests/Communication/RuntimeHostTests.cs new file mode 100644 index 00000000..ce3a2ea7 --- /dev/null +++ b/SafeExamBrowser.Runtime.UnitTests/Communication/RuntimeHostTests.cs @@ -0,0 +1,168 @@ +/* + * 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 Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using SafeExamBrowser.Contracts.Communication; +using SafeExamBrowser.Contracts.Communication.Data; +using SafeExamBrowser.Contracts.Communication.Hosts; +using SafeExamBrowser.Contracts.Configuration; +using SafeExamBrowser.Contracts.Logging; +using SafeExamBrowser.Runtime.Communication; + +namespace SafeExamBrowser.Runtime.UnitTests.Communication +{ + [TestClass] + public class RuntimeHostTests + { + private Mock configuration; + private Mock hostObject; + private Mock hostObjectFactory; + private Mock logger; + private RuntimeHost sut; + + [TestInitialize] + public void Initialize() + { + configuration = new Mock(); + hostObject = new Mock(); + hostObjectFactory = new Mock(); + logger = new Mock(); + + hostObjectFactory.Setup(f => f.CreateObject(It.IsAny(), It.IsAny())).Returns(hostObject.Object); + + sut = new RuntimeHost("net:pipe://some/address", configuration.Object, hostObjectFactory.Object, logger.Object); + } + + [TestMethod] + public void MustOnlyAllowConnectionIfTokenIsValid() + { + var token = Guid.NewGuid(); + + sut.StartupToken = token; + + var response = sut.Connect(token); + + Assert.IsNotNull(response); + Assert.IsTrue(response.ConnectionEstablished); + } + + [TestMethod] + public void MustOnlyAllowOneConcurrentConnection() + { + var token = Guid.NewGuid(); + + sut.StartupToken = token; + + var response1 = sut.Connect(token); + var response2 = sut.Connect(token); + var response3 = sut.Connect(token); + + Assert.IsNotNull(response1); + Assert.IsNotNull(response2); + Assert.IsNotNull(response3); + Assert.IsNotNull(response1.CommunicationToken); + Assert.IsNull(response2.CommunicationToken); + Assert.IsNull(response3.CommunicationToken); + Assert.IsTrue(response1.ConnectionEstablished); + Assert.IsFalse(response2.ConnectionEstablished); + Assert.IsFalse(response3.ConnectionEstablished); + } + + [TestMethod] + public void MustCorrectlyDisconnect() + { + var disconnected = false; + var token = Guid.NewGuid(); + + sut.StartupToken = token; + sut.ClientDisconnected += () => disconnected = true; + + var connectionResponse = sut.Connect(token); + var response = sut.Disconnect(new DisconnectionMessage { CommunicationToken = connectionResponse.CommunicationToken.Value }); + + Assert.IsNotNull(response); + Assert.IsTrue(disconnected); + Assert.IsTrue(response.ConnectionTerminated); + } + + [TestMethod] + public void MustAllowReconnectionAfterDisconnection() + { + var token = sut.StartupToken = Guid.NewGuid(); + var response = sut.Connect(token); + + sut.Disconnect(new DisconnectionMessage { CommunicationToken = response.CommunicationToken.Value }); + sut.StartupToken = token = Guid.NewGuid(); + + response = sut.Connect(token); + + Assert.IsTrue(response.ConnectionEstablished); + } + + [TestMethod] + public void MustHandleClientReadyCorrectly() + { + var clientReady = false; + + sut.ClientReady += () => clientReady = true; + sut.StartupToken = Guid.Empty; + + var token = sut.Connect(Guid.Empty).CommunicationToken.Value; + var message = new SimpleMessage(SimpleMessagePurport.ClientIsReady) { CommunicationToken = token }; + var response = sut.Send(message); + + Assert.IsTrue(clientReady); + Assert.IsNotNull(response); + Assert.IsInstanceOfType(response, typeof(SimpleResponse)); + Assert.AreEqual(SimpleResponsePurport.Acknowledged, (response as SimpleResponse)?.Purport); + } + + [TestMethod] + public void MustHandleShutdownRequestCorrectly() + { + var shutdownRequested = false; + + sut.ShutdownRequested += () => shutdownRequested = true; + sut.StartupToken = Guid.Empty; + + var token = sut.Connect(Guid.Empty).CommunicationToken.Value; + var message = new SimpleMessage(SimpleMessagePurport.RequestShutdown) { CommunicationToken = token }; + var response = sut.Send(message); + + Assert.IsTrue(shutdownRequested); + Assert.IsNotNull(response); + Assert.IsInstanceOfType(response, typeof(SimpleResponse)); + Assert.AreEqual(SimpleResponsePurport.Acknowledged, (response as SimpleResponse)?.Purport); + } + + [TestMethod] + public void MustReturnUnknownMessageAsDefault() + { + sut.StartupToken = Guid.Empty; + + var token = sut.Connect(Guid.Empty).CommunicationToken.Value; + var message = new TestMessage { CommunicationToken = token } as Message; + var response = sut.Send(message); + + Assert.IsNotNull(response); + Assert.IsInstanceOfType(response, typeof(SimpleResponse)); + Assert.AreEqual(SimpleResponsePurport.UnknownMessage, (response as SimpleResponse)?.Purport); + + message = new SimpleMessage(default(SimpleMessagePurport)) { CommunicationToken = token }; + response = sut.Send(message); + + Assert.IsNotNull(response); + Assert.IsInstanceOfType(response, typeof(SimpleResponse)); + Assert.AreEqual(SimpleResponsePurport.UnknownMessage, (response as SimpleResponse)?.Purport); + } + + private class TestMessage : Message { }; + } +} diff --git a/SafeExamBrowser.Runtime.UnitTests/SafeExamBrowser.Runtime.UnitTests.csproj b/SafeExamBrowser.Runtime.UnitTests/SafeExamBrowser.Runtime.UnitTests.csproj index aa293215..12e75e06 100644 --- a/SafeExamBrowser.Runtime.UnitTests/SafeExamBrowser.Runtime.UnitTests.csproj +++ b/SafeExamBrowser.Runtime.UnitTests/SafeExamBrowser.Runtime.UnitTests.csproj @@ -85,6 +85,7 @@ + @@ -96,6 +97,10 @@ {47DA5933-BEF8-4729-94E6-ABDE2DB12262} SafeExamBrowser.Contracts + + {3D6FDBB6-A4AF-4626-BB2B-BF329D44F9CC} + SafeExamBrowser.Core + {e3aed2f8-b5df-45d1-ac19-48066923d6d8} SafeExamBrowser.Runtime diff --git a/SafeExamBrowser.Runtime/Communication/RuntimeHost.cs b/SafeExamBrowser.Runtime/Communication/RuntimeHost.cs index 419c9f05..d4520d10 100644 --- a/SafeExamBrowser.Runtime/Communication/RuntimeHost.cs +++ b/SafeExamBrowser.Runtime/Communication/RuntimeHost.cs @@ -65,6 +65,7 @@ namespace SafeExamBrowser.Runtime.Communication switch (message) { case ReconfigurationMessage reconfigurationMessage: + // TODO: Not the job of the host, fire event or alike! return Handle(reconfigurationMessage); } @@ -79,6 +80,7 @@ namespace SafeExamBrowser.Runtime.Communication ClientReady?.Invoke(); return new SimpleResponse(SimpleResponsePurport.Acknowledged); case SimpleMessagePurport.ConfigurationNeeded: + // TODO: Not the job of the host, fire event or alike! return new ConfigurationResponse { Configuration = configuration.BuildClientConfiguration() }; case SimpleMessagePurport.RequestShutdown: ShutdownRequested?.Invoke();