/* * Copyright (c) 2024 ETH Zürich, IT Services * * 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.Threading; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using SafeExamBrowser.Lockdown.Contracts; using SafeExamBrowser.Logging.Contracts; namespace SafeExamBrowser.Lockdown.UnitTests { [TestClass] public class FeatureConfigurationMonitorTests { private Mock logger; private FeatureConfigurationMonitor sut; [TestInitialize] public void Initialize() { logger = new Mock(); sut = new FeatureConfigurationMonitor(logger.Object, 0); } [TestMethod] public void MustEnforceConfigurations() { var configuration1 = new Mock(); var configuration2 = new Mock(); var configuration3 = new Mock(); var counter = 0; var limit = new Random().Next(5, 50); var sync = new AutoResetEvent(false); configuration1.Setup(c => c.GetStatus()).Returns(FeatureConfigurationStatus.Disabled); configuration2.Setup(c => c.GetStatus()).Returns(FeatureConfigurationStatus.Enabled); configuration3.Setup(c => c.GetStatus()).Returns(FeatureConfigurationStatus.Disabled).Callback(() => { if (++counter >= limit) { sync.Set(); } }); sut = new FeatureConfigurationMonitor(logger.Object, 2); sut.Observe(configuration1.Object, FeatureConfigurationStatus.Enabled); sut.Observe(configuration2.Object, FeatureConfigurationStatus.Disabled); sut.Observe(configuration3.Object, FeatureConfigurationStatus.Undefined); sut.Start(); sync.WaitOne(); sut.Reset(); configuration1.Verify(c => c.EnableFeature(), Times.Exactly(limit)); configuration2.Verify(c => c.DisableFeature(), Times.Exactly(limit)); configuration3.Verify(c => c.DisableFeature(), Times.Never); configuration3.Verify(c => c.EnableFeature(), Times.Never); } [TestMethod] public void MustExecuteAsynchronously() { var configuration = new Mock(); var sync = new AutoResetEvent(false); var threadId = Thread.CurrentThread.ManagedThreadId; configuration.Setup(c => c.GetStatus()).Callback(() => { threadId = Thread.CurrentThread.ManagedThreadId; sync.Set(); }); sut.Observe(configuration.Object, FeatureConfigurationStatus.Disabled); sut.Start(); sync.WaitOne(); sut.Reset(); Assert.AreNotEqual(Thread.CurrentThread.ManagedThreadId, threadId); } [TestMethod] public void MustNotStartMultipleTimes() { var configuration = new Mock(); var counter = 0; var sync = new AutoResetEvent(false); configuration.Setup(c => c.GetStatus()).Returns(() => { counter++; Thread.Sleep(50); sync.Set(); sut.Reset(); return FeatureConfigurationStatus.Disabled; }); sut.Observe(configuration.Object, FeatureConfigurationStatus.Enabled); sut.Start(); sut.Start(); sut.Start(); sut.Start(); sut.Start(); sut.Start(); sut.Start(); sync.WaitOne(); sut.Reset(); Assert.AreEqual(1, counter); } [TestMethod] public void MustRespectTimeout() { const int TIMEOUT = 50; var after = default(DateTime); var before = default(DateTime); var configuration = new Mock(); var counter = 0; var sync = new AutoResetEvent(false); sut = new FeatureConfigurationMonitor(logger.Object, TIMEOUT); configuration.Setup(c => c.GetStatus()).Returns(FeatureConfigurationStatus.Undefined).Callback(() => { switch (++counter) { case 1: before = DateTime.Now; break; case 2: after = DateTime.Now; sync.Set(); break; } }); sut.Observe(configuration.Object, FeatureConfigurationStatus.Disabled); sut.Start(); sync.WaitOne(); sut.Reset(); Assert.IsTrue(after - before >= new TimeSpan(0, 0, 0, 0, TIMEOUT)); } [TestMethod] public void MustStopWhenReset() { var configuration = new Mock(); var counter = 0; configuration.Setup(c => c.GetStatus()).Returns(FeatureConfigurationStatus.Disabled).Callback(() => counter++); sut.Observe(configuration.Object, FeatureConfigurationStatus.Enabled); sut.Start(); Thread.Sleep(10); sut.Reset(); Thread.Sleep(10); configuration.Verify(c => c.GetStatus(), Times.Exactly(counter)); } [TestMethod] public void MustRemoveConfigurationsWhenReset() { var configuration = new Mock(); var counter = 0; configuration.Setup(c => c.GetStatus()).Returns(FeatureConfigurationStatus.Disabled).Callback(() => counter++); sut.Observe(configuration.Object, FeatureConfigurationStatus.Enabled); sut.Start(); Thread.Sleep(10); sut.Reset(); configuration.Verify(c => c.GetStatus(), Times.Exactly(counter)); configuration.Reset(); sut.Start(); Thread.Sleep(10); sut.Reset(); configuration.Verify(c => c.GetStatus(), Times.Never); } [TestMethod] [ExpectedException(typeof(ArgumentException))] public void MustValidateTimeout() { new FeatureConfigurationMonitor(logger.Object, new Random().Next(int.MinValue, -1)); } } }