From 3338710949d56f2502c58d284a9b755aa020f21c Mon Sep 17 00:00:00 2001 From: dbuechel Date: Thu, 27 Jun 2019 10:47:41 +0200 Subject: [PATCH] SEBWIN-301: Extended unit tests for service and related functionality. --- .../AutoRestoreMechanismTests.cs | 22 ++ .../Communication/ServiceHostTests.cs | 10 + .../Operations/LockdownOperationTests.cs | 208 ++++++++++++++++++ .../Operations/RestoreOperationTests.cs | 58 +++++ .../SafeExamBrowser.Service.UnitTests.csproj | 2 + .../ServiceControllerTests.cs | 1 + 6 files changed, 301 insertions(+) create mode 100644 SafeExamBrowser.Service.UnitTests/Operations/LockdownOperationTests.cs create mode 100644 SafeExamBrowser.Service.UnitTests/Operations/RestoreOperationTests.cs diff --git a/SafeExamBrowser.Lockdown.UnitTests/AutoRestoreMechanismTests.cs b/SafeExamBrowser.Lockdown.UnitTests/AutoRestoreMechanismTests.cs index 2ca3be4d..7b9d15db 100644 --- a/SafeExamBrowser.Lockdown.UnitTests/AutoRestoreMechanismTests.cs +++ b/SafeExamBrowser.Lockdown.UnitTests/AutoRestoreMechanismTests.cs @@ -47,6 +47,28 @@ namespace SafeExamBrowser.Lockdown.UnitTests Assert.AreNotEqual(Thread.CurrentThread.ManagedThreadId, threadId); } + [TestMethod] + public void MustNotStartMultipleTimes() + { + var counter = 0; + var list = new List(); + var sync = new AutoResetEvent(false); + + backup.Setup(b => b.GetAllConfigurations()).Returns(() => { counter++; Thread.Sleep(50); sync.Set(); return list; }); + + sut.Start(); + sut.Start(); + sut.Start(); + sut.Start(); + sut.Start(); + sut.Start(); + sut.Start(); + sync.WaitOne(); + sut.Stop(); + + Assert.AreEqual(1, counter); + } + [TestMethod] public void MustNotTerminateUntilAllChangesReverted() { diff --git a/SafeExamBrowser.Service.UnitTests/Communication/ServiceHostTests.cs b/SafeExamBrowser.Service.UnitTests/Communication/ServiceHostTests.cs index ff5b9719..9991d754 100644 --- a/SafeExamBrowser.Service.UnitTests/Communication/ServiceHostTests.cs +++ b/SafeExamBrowser.Service.UnitTests/Communication/ServiceHostTests.cs @@ -135,6 +135,16 @@ namespace SafeExamBrowser.Service.UnitTests.Communication Assert.AreEqual(SimpleResponsePurport.UnknownMessage, (response as SimpleResponse)?.Purport); } + [TestMethod] + public void MustNotFailIfNoEventHandlersSubscribed() + { + var token = sut.Connect(Guid.Empty).CommunicationToken.Value; + + sut.Send(new SessionStartMessage(null) { CommunicationToken = token }); + sut.Send(new SessionStopMessage(Guid.Empty) { CommunicationToken = token }); + sut.Disconnect(new DisconnectionMessage { CommunicationToken = token, Interlocutor = Interlocutor.Runtime }); + } + private class TestMessage : Message { }; } } diff --git a/SafeExamBrowser.Service.UnitTests/Operations/LockdownOperationTests.cs b/SafeExamBrowser.Service.UnitTests/Operations/LockdownOperationTests.cs new file mode 100644 index 00000000..5fffda28 --- /dev/null +++ b/SafeExamBrowser.Service.UnitTests/Operations/LockdownOperationTests.cs @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2019 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.Collections.Generic; +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using SafeExamBrowser.Contracts.Configuration; +using SafeExamBrowser.Contracts.Configuration.Settings; +using SafeExamBrowser.Contracts.Core.OperationModel; +using SafeExamBrowser.Contracts.Lockdown; +using SafeExamBrowser.Contracts.Logging; +using SafeExamBrowser.Service.Operations; + +namespace SafeExamBrowser.Service.UnitTests.Operations +{ + [TestClass] + public class LockdownOperationTests + { + private Mock backup; + private Mock factory; + private Mock logger; + private Settings settings; + private SessionContext sessionContext; + private LockdownOperation sut; + + [TestInitialize] + public void Initialize() + { + backup = new Mock(); + factory = new Mock(); + logger = new Mock(); + settings = new Settings(); + sessionContext = new SessionContext { Configuration = new ServiceConfiguration { Settings = settings } }; + + sut = new LockdownOperation(backup.Object, factory.Object, logger.Object, sessionContext); + } + + [TestMethod] + public void Perform_MustSetConfigurationsCorrectly() + { + var configuration = new Mock(); + var count = typeof(IFeatureConfigurationFactory).GetMethods().Where(m => m.Name.StartsWith("Create")).Count(); + + configuration.SetReturnsDefault(true); + factory.SetReturnsDefault(configuration.Object); + settings.Service.DisableChromeNotifications = true; + settings.Service.DisablePowerOptions = true; + settings.Service.DisableSignout = true; + + var result = sut.Perform(); + + backup.Verify(b => b.Save(It.Is(c => c == configuration.Object)), Times.Exactly(count)); + configuration.Verify(c => c.DisableFeature(), Times.Exactly(3)); + configuration.Verify(c => c.EnableFeature(), Times.Exactly(count - 3)); + configuration.Verify(c => c.Monitor(), Times.Exactly(count)); + + Assert.AreEqual(OperationResult.Success, result); + } + + [TestMethod] + public void Perform_MustUseSameGroupForAllConfigurations() + { + var configuration = new Mock(); + var groupId = default(Guid); + + configuration.SetReturnsDefault(true); + factory.Setup(f => f.CreateChromeNotificationConfiguration(It.IsAny())).Returns(configuration.Object).Callback(id => groupId = id); + factory.SetReturnsDefault(configuration.Object); + + sut.Perform(); + + factory.Verify(f => f.CreateChromeNotificationConfiguration(It.Is(id => id == groupId)), Times.Once); + factory.Verify(f => f.CreateEaseOfAccessConfiguration(It.Is(id => id == groupId)), Times.Once); + factory.Verify(f => f.CreateNetworkOptionsConfiguration(It.Is(id => id == groupId)), Times.Once); + factory.Verify(f => f.CreatePasswordChangeConfiguration(It.Is(id => id == groupId)), Times.Once); + factory.Verify(f => f.CreatePowerOptionsConfiguration(It.Is(id => id == groupId)), Times.Once); + factory.Verify(f => f.CreateRemoteConnectionConfiguration(It.Is(id => id == groupId)), Times.Once); + factory.Verify(f => f.CreateSignoutConfiguration(It.Is(id => id == groupId)), Times.Once); + factory.Verify(f => f.CreateTaskManagerConfiguration(It.Is(id => id == groupId)), Times.Once); + factory.Verify(f => f.CreateUserLockConfiguration(It.Is(id => id == groupId)), Times.Once); + factory.Verify(f => f.CreateUserSwitchConfiguration(It.Is(id => id == groupId)), Times.Once); + factory.Verify(f => f.CreateVmwareOverlayConfiguration(It.Is(id => id == groupId)), Times.Once); + factory.Verify(f => f.CreateWindowsUpdateConfiguration(It.Is(id => id == groupId)), Times.Once); + } + + [TestMethod] + public void Perform_MustImmediatelyAbortOnFailure() + { + var configuration = new Mock(); + var count = typeof(IFeatureConfigurationFactory).GetMethods().Where(m => m.Name.StartsWith("Create")).Count(); + var counter = 0; + var offset = 3; + + configuration.Setup(c => c.EnableFeature()).Returns(() => ++counter < count - offset); + factory.SetReturnsDefault(configuration.Object); + + var result = sut.Perform(); + + backup.Verify(b => b.Save(It.Is(c => c == configuration.Object)), Times.Exactly(count - offset)); + configuration.Verify(c => c.DisableFeature(), Times.Never); + configuration.Verify(c => c.EnableFeature(), Times.Exactly(count - offset)); + configuration.Verify(c => c.Monitor(), Times.Exactly(count - offset - 1)); + + Assert.AreEqual(OperationResult.Failed, result); + } + + [TestMethod] + public void Revert_MustRestoreConfigurationsCorrectly() + { + var configuration1 = new Mock(); + var configuration2 = new Mock(); + var configuration3 = new Mock(); + var configuration4 = new Mock(); + var configurations = new List + { + configuration1.Object, + configuration2.Object, + configuration3.Object, + configuration4.Object + }; + + backup.Setup(b => b.GetBy(It.IsAny())).Returns(configurations); + configuration1.Setup(c => c.Restore()).Returns(true); + configuration2.Setup(c => c.Restore()).Returns(true); + configuration3.Setup(c => c.Restore()).Returns(true); + configuration4.Setup(c => c.Restore()).Returns(true); + + var result = sut.Revert(); + + backup.Verify(b => b.Delete(It.Is(c => c == configuration1.Object)), Times.Once); + backup.Verify(b => b.Delete(It.Is(c => c == configuration2.Object)), Times.Once); + backup.Verify(b => b.Delete(It.Is(c => c == configuration3.Object)), Times.Once); + backup.Verify(b => b.Delete(It.Is(c => c == configuration4.Object)), Times.Once); + + configuration1.Verify(c => c.DisableFeature(), Times.Never); + configuration1.Verify(c => c.EnableFeature(), Times.Never); + configuration1.Verify(c => c.Restore(), Times.Once); + + configuration2.Verify(c => c.DisableFeature(), Times.Never); + configuration2.Verify(c => c.EnableFeature(), Times.Never); + configuration2.Verify(c => c.Restore(), Times.Once); + + configuration3.Verify(c => c.DisableFeature(), Times.Never); + configuration3.Verify(c => c.EnableFeature(), Times.Never); + configuration3.Verify(c => c.Restore(), Times.Once); + + configuration4.Verify(c => c.DisableFeature(), Times.Never); + configuration4.Verify(c => c.EnableFeature(), Times.Never); + configuration4.Verify(c => c.Restore(), Times.Once); + + Assert.AreEqual(OperationResult.Success, result); + } + + [TestMethod] + public void Revert_MustContinueToRevertOnFailure() + { + var configuration1 = new Mock(); + var configuration2 = new Mock(); + var configuration3 = new Mock(); + var configuration4 = new Mock(); + var configurations = new List + { + configuration1.Object, + configuration2.Object, + configuration3.Object, + configuration4.Object + }; + + backup.Setup(b => b.GetBy(It.IsAny())).Returns(configurations); + configuration1.Setup(c => c.Restore()).Returns(true); + configuration2.Setup(c => c.Restore()).Returns(false); + configuration3.Setup(c => c.Restore()).Returns(false); + configuration4.Setup(c => c.Restore()).Returns(true); + + var result = sut.Revert(); + + backup.Verify(b => b.Delete(It.Is(c => c == configuration1.Object)), Times.Once); + backup.Verify(b => b.Delete(It.Is(c => c == configuration2.Object)), Times.Never); + backup.Verify(b => b.Delete(It.Is(c => c == configuration3.Object)), Times.Never); + backup.Verify(b => b.Delete(It.Is(c => c == configuration4.Object)), Times.Once); + + configuration1.Verify(c => c.DisableFeature(), Times.Never); + configuration1.Verify(c => c.EnableFeature(), Times.Never); + configuration1.Verify(c => c.Restore(), Times.Once); + + configuration2.Verify(c => c.DisableFeature(), Times.Never); + configuration2.Verify(c => c.EnableFeature(), Times.Never); + configuration2.Verify(c => c.Restore(), Times.Once); + + configuration3.Verify(c => c.DisableFeature(), Times.Never); + configuration3.Verify(c => c.EnableFeature(), Times.Never); + configuration3.Verify(c => c.Restore(), Times.Once); + + configuration4.Verify(c => c.DisableFeature(), Times.Never); + configuration4.Verify(c => c.EnableFeature(), Times.Never); + configuration4.Verify(c => c.Restore(), Times.Once); + + Assert.AreEqual(OperationResult.Failed, result); + } + } +} diff --git a/SafeExamBrowser.Service.UnitTests/Operations/RestoreOperationTests.cs b/SafeExamBrowser.Service.UnitTests/Operations/RestoreOperationTests.cs new file mode 100644 index 00000000..1787d6aa --- /dev/null +++ b/SafeExamBrowser.Service.UnitTests/Operations/RestoreOperationTests.cs @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019 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 Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using SafeExamBrowser.Contracts.Core.OperationModel; +using SafeExamBrowser.Contracts.Lockdown; +using SafeExamBrowser.Contracts.Logging; +using SafeExamBrowser.Service.Operations; + +namespace SafeExamBrowser.Service.UnitTests.Operations +{ + [TestClass] + public class RestoreOperationTests + { + private SessionContext sessionContext; + private Mock autoRestoreMechanism; + private Mock backup; + private Mock logger; + private RestoreOperation sut; + + [TestInitialize] + public void Initialize() + { + autoRestoreMechanism = new Mock(); + backup = new Mock(); + logger = new Mock(); + sessionContext = new SessionContext { AutoRestoreMechanism = autoRestoreMechanism.Object }; + + sut = new RestoreOperation(backup.Object, logger.Object, sessionContext); + } + + [TestMethod] + public void Perform_MustStartAutoRestore() + { + var result = sut.Perform(); + + autoRestoreMechanism.Verify(m => m.Start(), Times.Once); + autoRestoreMechanism.Verify(m => m.Stop(), Times.Never); + Assert.AreEqual(OperationResult.Success, result); + } + + [TestMethod] + public void Revert_MustStopAutoRestore() + { + var result = sut.Revert(); + + autoRestoreMechanism.Verify(m => m.Start(), Times.Never); + autoRestoreMechanism.Verify(m => m.Stop(), Times.Once); + Assert.AreEqual(OperationResult.Success, result); + } + } +} diff --git a/SafeExamBrowser.Service.UnitTests/SafeExamBrowser.Service.UnitTests.csproj b/SafeExamBrowser.Service.UnitTests/SafeExamBrowser.Service.UnitTests.csproj index d6ac720e..52a851c6 100644 --- a/SafeExamBrowser.Service.UnitTests/SafeExamBrowser.Service.UnitTests.csproj +++ b/SafeExamBrowser.Service.UnitTests/SafeExamBrowser.Service.UnitTests.csproj @@ -82,6 +82,8 @@ + + diff --git a/SafeExamBrowser.Service.UnitTests/ServiceControllerTests.cs b/SafeExamBrowser.Service.UnitTests/ServiceControllerTests.cs index 7816ab7b..c5560ed0 100644 --- a/SafeExamBrowser.Service.UnitTests/ServiceControllerTests.cs +++ b/SafeExamBrowser.Service.UnitTests/ServiceControllerTests.cs @@ -95,6 +95,7 @@ namespace SafeExamBrowser.Service.UnitTests bootstrapSequence.Setup(b => b.TryPerform()).Returns(OperationResult.Success); sessionContext.Configuration = new ServiceConfiguration { SessionId = Guid.NewGuid() }; + sessionContext.IsRunning = true; sut.TryStart(); serviceHost.Raise(h => h.SessionStopRequested += null, args);