Restore SEBPatch

This commit is contained in:
2025-06-01 11:44:20 +02:00
commit 8c656e3137
1297 changed files with 142172 additions and 0 deletions

View File

@@ -0,0 +1,101 @@
/*
* 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.Collections.Generic;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Binary;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Configuration.ConfigurationData;
using SafeExamBrowser.Settings;
using SafeExamBrowser.Settings.Applications;
namespace SafeExamBrowser.Configuration.UnitTests.ConfigurationData
{
[TestClass]
public class DataProcessorTests
{
private DataProcessor sut;
[TestInitialize]
public void Initialize()
{
sut = new DataProcessor();
}
[TestMethod]
public void MustAllowBrowserToolbarForReloading()
{
var raw = new Dictionary<string, object>();
var settings = new AppSettings();
raw.Add(Keys.Browser.ShowReloadButton, true);
settings.Browser.AdditionalWindow.AllowReloading = false;
settings.Browser.AdditionalWindow.ShowReloadButton = false;
settings.Browser.MainWindow.AllowReloading = false;
settings.Browser.MainWindow.ShowReloadButton = false;
sut.Process(raw, settings);
Assert.IsFalse(settings.Browser.AdditionalWindow.ShowToolbar);
Assert.IsFalse(settings.Browser.MainWindow.ShowToolbar);
settings.Browser.AdditionalWindow.AllowReloading = true;
settings.Browser.AdditionalWindow.ShowReloadButton = true;
settings.Browser.MainWindow.AllowReloading = true;
settings.Browser.MainWindow.ShowReloadButton = true;
sut.Process(raw, settings);
Assert.IsTrue(settings.Browser.AdditionalWindow.ShowToolbar);
Assert.IsTrue(settings.Browser.MainWindow.ShowToolbar);
}
[TestMethod]
public void MustCalculateCorrectConfigurationKey()
{
var formatter = new BinaryFormatter();
var path1 = $"{nameof(SafeExamBrowser)}.{nameof(Configuration)}.{nameof(UnitTests)}.{nameof(ConfigurationData)}.TestDictionary1.bin";
var path2 = $"{nameof(SafeExamBrowser)}.{nameof(Configuration)}.{nameof(UnitTests)}.{nameof(ConfigurationData)}.TestDictionary2.bin";
var path3 = $"{nameof(SafeExamBrowser)}.{nameof(Configuration)}.{nameof(UnitTests)}.{nameof(ConfigurationData)}.TestDictionary3.bin";
var stream1 = Assembly.GetAssembly(GetType()).GetManifestResourceStream(path1);
var stream2 = Assembly.GetAssembly(GetType()).GetManifestResourceStream(path2);
var stream3 = Assembly.GetAssembly(GetType()).GetManifestResourceStream(path3);
var data1 = formatter.Deserialize(stream1) as IDictionary<string, object>;
var data2 = formatter.Deserialize(stream2) as IDictionary<string, object>;
var data3 = formatter.Deserialize(stream3) as IDictionary<string, object>;
var settings1 = new AppSettings();
var settings2 = new AppSettings();
var settings3 = new AppSettings();
sut.Process(data1, settings1);
sut.Process(data2, settings2);
sut.Process(data3, settings3);
Assert.AreEqual("6063c3351ed1ac878c05072598d5079e30ca763c957d8e04bd45131c08f88d1a", settings1.Browser.ConfigurationKey);
Assert.AreEqual("4fc002d2ae4faf994a14bede54d95ac58a1a2cb9b59bc5b4277ff29559b46e3d", settings2.Browser.ConfigurationKey);
Assert.AreEqual("ab426e25b795c917f1fb40f7ef8e5757ef97d7c7ad6792e655c4421d47329d7a", settings3.Browser.ConfigurationKey);
}
[TestMethod]
public void MustRemoveLegacyBrowserEngines()
{
var settings = new AppSettings();
var firefox = new WhitelistApplication { ExecutablePath = @"C:\Program Files (x86)\Mozilla Firefox", ExecutableName = "firefox.exe" };
settings.Applications.Whitelist.Add(new WhitelistApplication { ExecutablePath = @"C:\some\path\xulrunner\etc", ExecutableName = "firefox.exe" });
settings.Applications.Whitelist.Add(new WhitelistApplication { ExecutablePath = @"C:\some\path\xulrunner\etc", ExecutableName = "xulrunner.exe" });
settings.Applications.Whitelist.Add(firefox);
sut.Process(Mock.Of<IDictionary<string, object>>(), settings);
Assert.AreEqual(1, settings.Applications.Whitelist.Count);
Assert.AreSame(firefox, settings.Applications.Whitelist[0]);
}
}
}

View File

@@ -0,0 +1,348 @@
/*
* 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.Collections.Generic;
using System.IO;
using System.Reflection;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Configuration.ConfigurationData;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Configuration.Contracts.Cryptography;
using SafeExamBrowser.Configuration.Contracts.DataFormats;
using SafeExamBrowser.Configuration.Contracts.DataResources;
using SafeExamBrowser.Logging.Contracts;
namespace SafeExamBrowser.Configuration.UnitTests
{
[TestClass]
public class ConfigurationRepositoryTests
{
private ConfigurationRepository sut;
private Mock<IDataParser> binaryParser;
private Mock<IDataSerializer> binarySerializer;
private Mock<ICertificateStore> certificateStore;
private Mock<IResourceLoader> fileLoader;
private Mock<IResourceSaver> fileSaver;
private Mock<IModuleLogger> logger;
private Mock<IResourceLoader> networkLoader;
private Mock<IDataParser> xmlParser;
private Mock<IDataSerializer> xmlSerializer;
[TestInitialize]
public void Initialize()
{
binaryParser = new Mock<IDataParser>();
binarySerializer = new Mock<IDataSerializer>();
certificateStore = new Mock<ICertificateStore>();
fileLoader = new Mock<IResourceLoader>();
fileSaver = new Mock<IResourceSaver>();
logger = new Mock<IModuleLogger>();
networkLoader = new Mock<IResourceLoader>();
xmlParser = new Mock<IDataParser>();
xmlSerializer = new Mock<IDataSerializer>();
fileLoader.Setup(f => f.CanLoad(It.IsAny<Uri>())).Returns<Uri>(u => u.IsFile);
fileSaver.Setup(f => f.CanSave(It.IsAny<Uri>())).Returns<Uri>(u => u.IsFile);
networkLoader.Setup(n => n.CanLoad(It.IsAny<Uri>())).Returns<Uri>(u => u.Scheme.Equals("http") || u.Scheme.Equals("seb"));
SetEntryAssembly();
sut = new ConfigurationRepository(certificateStore.Object, logger.Object);
sut.InitializeAppConfig();
}
[TestMethod]
public void ConfigureClient_MustWorkAsExpected()
{
var stream = new MemoryStream() as Stream;
var password = new PasswordParameters { Password = "test123" };
var parseResult = new ParseResult
{
Format = FormatType.Binary,
RawData = new Dictionary<string, object>(),
Status = LoadStatus.Success
};
var serializeResult = new SerializeResult
{
Data = new MemoryStream(),
Status = SaveStatus.Success
};
RegisterModules();
fileLoader.Setup(n => n.TryLoad(It.IsAny<Uri>(), out stream)).Returns(LoadStatus.Success);
binaryParser.Setup(b => b.CanParse(It.IsAny<Stream>())).Returns(true);
binaryParser.Setup(b => b.TryParse(It.IsAny<Stream>(), It.IsAny<PasswordParameters>())).Returns(parseResult);
binarySerializer.Setup(b => b.CanSerialize(FormatType.Binary)).Returns(true);
binarySerializer.Setup(b => b.TrySerialize(It.IsAny<Dictionary<string, object>>(), It.IsAny<PasswordParameters>())).Returns(serializeResult);
fileSaver.Setup(f => f.TrySave(It.IsAny<Uri>(), It.IsAny<Stream>())).Returns(SaveStatus.Success);
var status = sut.ConfigureClientWith(new Uri("C:\\TEMP\\Some\\file.seb"), password);
fileLoader.Verify(n => n.TryLoad(It.IsAny<Uri>(), out stream), Times.Once);
binaryParser.Verify(b => b.TryParse(It.IsAny<Stream>(), It.IsAny<PasswordParameters>()), Times.Once);
certificateStore.Verify(c => c.ExtractAndImportIdentities(It.IsAny<Dictionary<string, object>>()), Times.Once);
binarySerializer.Verify(b => b.TrySerialize(
It.IsAny<Dictionary<string, object>>(),
It.Is<PasswordParameters>(p => p.IsHash == true && p.Password == string.Empty)), Times.Once);
fileSaver.Verify(f => f.TrySave(It.IsAny<Uri>(), It.IsAny<Stream>()), Times.Once);
Assert.AreEqual(SaveStatus.Success, status);
}
[TestMethod]
public void ConfigureClient_MustKeepSameEncryptionAccordingToConfiguration()
{
var stream = new MemoryStream() as Stream;
var password = new PasswordParameters { Password = "test123" };
var parseResult = new ParseResult
{
Encryption = new PublicKeyParameters
{
InnerEncryption = password,
SymmetricEncryption = true
},
Format = FormatType.Binary,
RawData = new Dictionary<string, object> { { Keys.ConfigurationFile.KeepClientConfigEncryption, true } },
Status = LoadStatus.Success
};
var serializeResult = new SerializeResult
{
Data = new MemoryStream(),
Status = SaveStatus.Success
};
RegisterModules();
fileLoader.Setup(n => n.TryLoad(It.IsAny<Uri>(), out stream)).Returns(LoadStatus.Success);
binaryParser.Setup(b => b.CanParse(It.IsAny<Stream>())).Returns(true);
binaryParser.Setup(b => b.TryParse(It.IsAny<Stream>(), It.IsAny<PasswordParameters>())).Returns(parseResult);
binarySerializer.Setup(b => b.CanSerialize(FormatType.Binary)).Returns(true);
binarySerializer.Setup(b => b.TrySerialize(It.IsAny<Dictionary<string, object>>(), It.IsAny<EncryptionParameters>())).Returns(serializeResult);
fileSaver.Setup(f => f.TrySave(It.IsAny<Uri>(), It.IsAny<Stream>())).Returns(SaveStatus.Success);
var status = sut.ConfigureClientWith(new Uri("C:\\TEMP\\Some\\file.seb"), password);
binarySerializer.Verify(b => b.TrySerialize(
It.IsAny<Dictionary<string, object>>(),
It.Is<PublicKeyParameters>(p => p.InnerEncryption == password && p.SymmetricEncryption)), Times.Once);
Assert.AreEqual(SaveStatus.Success, status);
}
[TestMethod]
public void ConfigureClient_MustAbortProcessOnError()
{
var stream = new MemoryStream() as Stream;
var password = new PasswordParameters { Password = "test123" };
var parseResult = new ParseResult
{
Format = FormatType.Binary,
RawData = new Dictionary<string, object>(),
Status = LoadStatus.Success
};
var serializeResult = new SerializeResult
{
Data = new MemoryStream(),
Status = SaveStatus.Success
};
RegisterModules();
fileLoader.Setup(n => n.TryLoad(It.IsAny<Uri>(), out stream)).Returns(LoadStatus.Success);
binaryParser.Setup(b => b.CanParse(It.IsAny<Stream>())).Throws<Exception>();
var status = sut.ConfigureClientWith(new Uri("C:\\TEMP\\Some\\file.seb"), password);
fileLoader.Verify(n => n.TryLoad(It.IsAny<Uri>(), out stream), Times.Once);
binaryParser.Verify(b => b.TryParse(It.IsAny<Stream>(), It.IsAny<PasswordParameters>()), Times.Never);
certificateStore.Verify(c => c.ExtractAndImportIdentities(It.IsAny<Dictionary<string, object>>()), Times.Never);
binarySerializer.Verify(b => b.TrySerialize(It.IsAny<Dictionary<string, object>>(), It.IsAny<EncryptionParameters>()), Times.Never);
fileSaver.Verify(f => f.TrySave(It.IsAny<Uri>(), It.IsAny<Stream>()), Times.Never);
Assert.AreEqual(SaveStatus.UnexpectedError, status);
}
[TestMethod]
public void TryLoad_MustWorkAsExpected()
{
var stream = new MemoryStream() as Stream;
var parseResult = new ParseResult { RawData = new Dictionary<string, object>(), Status = LoadStatus.Success };
RegisterModules();
networkLoader.Setup(n => n.TryLoad(It.IsAny<Uri>(), out stream)).Returns(LoadStatus.Success);
binaryParser.Setup(b => b.CanParse(It.IsAny<Stream>())).Returns(true);
binaryParser.Setup(b => b.TryParse(It.IsAny<Stream>(), It.IsAny<PasswordParameters>())).Returns(parseResult);
var result = sut.TryLoadSettings(new Uri("http://www.blubb.org"), out _);
fileLoader.Verify(f => f.CanLoad(It.IsAny<Uri>()), Times.Once);
fileLoader.Verify(f => f.TryLoad(It.IsAny<Uri>(), out stream), Times.Never);
networkLoader.Verify(n => n.CanLoad(It.IsAny<Uri>()), Times.Once);
networkLoader.Verify(n => n.TryLoad(It.IsAny<Uri>(), out stream), Times.Once);
binaryParser.Verify(b => b.CanParse(It.IsAny<Stream>()), Times.Once);
binaryParser.Verify(b => b.TryParse(It.IsAny<Stream>(), It.IsAny<PasswordParameters>()), Times.Once);
xmlParser.Verify(x => x.CanParse(It.IsAny<Stream>()), Times.AtMostOnce);
xmlParser.Verify(x => x.TryParse(It.IsAny<Stream>(), It.IsAny<PasswordParameters>()), Times.Never);
Assert.AreEqual(LoadStatus.Success, result);
}
[TestMethod]
public void TryLoad_MustReportPasswordNeed()
{
var stream = new MemoryStream() as Stream;
var parseResult = new ParseResult { Status = LoadStatus.PasswordNeeded };
RegisterModules();
networkLoader.Setup(n => n.TryLoad(It.IsAny<Uri>(), out stream)).Returns(LoadStatus.Success);
binaryParser.Setup(b => b.CanParse(It.IsAny<Stream>())).Returns(true);
binaryParser.Setup(b => b.TryParse(It.IsAny<Stream>(), It.IsAny<PasswordParameters>())).Returns(parseResult);
var result = sut.TryLoadSettings(new Uri("http://www.blubb.org"), out _);
Assert.AreEqual(LoadStatus.PasswordNeeded, result);
}
[TestMethod]
public void TryLoad_MustNotFailToIfNoLoaderRegistered()
{
var result = sut.TryLoadSettings(new Uri("http://www.blubb.org"), out _);
Assert.AreEqual(LoadStatus.NotSupported, result);
sut.Register(fileLoader.Object);
sut.Register(networkLoader.Object);
result = sut.TryLoadSettings(new Uri("ftp://www.blubb.org"), out _);
fileLoader.Verify(f => f.CanLoad(It.IsAny<Uri>()), Times.Once);
networkLoader.Verify(n => n.CanLoad(It.IsAny<Uri>()), Times.Once);
Assert.AreEqual(LoadStatus.NotSupported, result);
}
[TestMethod]
public void TryLoad_MustNotFailIfNoParserRegistered()
{
var data = default(Stream);
networkLoader.Setup(l => l.TryLoad(It.IsAny<Uri>(), out data)).Returns(LoadStatus.Success);
sut.Register(networkLoader.Object);
var result = sut.TryLoadSettings(new Uri("http://www.blubb.org"), out _);
networkLoader.Verify(n => n.TryLoad(It.IsAny<Uri>(), out data), Times.Once);
Assert.AreEqual(LoadStatus.NotSupported, result);
}
[TestMethod]
public void TryLoad_MustNotFailInCaseOfUnexpectedError()
{
var data = default(Stream);
networkLoader.Setup(l => l.TryLoad(It.IsAny<Uri>(), out data)).Throws<Exception>();
sut.Register(networkLoader.Object);
var result = sut.TryLoadSettings(new Uri("http://www.blubb.org"), out _);
Assert.AreEqual(LoadStatus.UnexpectedError, result);
binaryParser.Setup(b => b.CanParse(It.IsAny<Stream>())).Throws<Exception>();
networkLoader.Setup(l => l.TryLoad(It.IsAny<Uri>(), out data)).Returns(LoadStatus.Success);
sut.Register(binaryParser.Object);
result = sut.TryLoadSettings(new Uri("http://www.blubb.org"), out _);
Assert.AreEqual(LoadStatus.UnexpectedError, result);
}
[TestMethod]
public void MustInitializeSessionConfiguration()
{
var appConfig = sut.InitializeAppConfig();
var configuration = sut.InitializeSessionConfiguration();
Assert.IsNull(configuration.Settings);
Assert.IsInstanceOfType(configuration.AppConfig, typeof(AppConfig));
Assert.IsInstanceOfType(configuration.ClientAuthenticationToken, typeof(Guid));
Assert.IsInstanceOfType(configuration.SessionId, typeof(Guid));
}
[TestMethod]
public void MustUpdateAppConfig()
{
var appConfig = sut.InitializeAppConfig();
var clientAddress = appConfig.ClientAddress;
var clientId = appConfig.ClientId;
var clientLogFilePath = appConfig.ClientLogFilePath;
var runtimeAddress = appConfig.RuntimeAddress;
var runtimeId = appConfig.RuntimeId;
var runtimeLogFilePath = appConfig.RuntimeLogFilePath;
var serviceEventName = appConfig.ServiceEventName;
var configuration = sut.InitializeSessionConfiguration();
Assert.AreEqual(configuration.AppConfig.ClientLogFilePath, clientLogFilePath);
Assert.AreEqual(configuration.AppConfig.RuntimeAddress, runtimeAddress);
Assert.AreEqual(configuration.AppConfig.RuntimeId, runtimeId);
Assert.AreEqual(configuration.AppConfig.RuntimeLogFilePath, runtimeLogFilePath);
Assert.AreNotEqual(configuration.AppConfig.ClientAddress, clientAddress);
Assert.AreNotEqual(configuration.AppConfig.ClientId, clientId);
Assert.AreNotEqual(configuration.AppConfig.ServiceEventName, serviceEventName);
}
[TestMethod]
public void MustUpdateSessionConfiguration()
{
var appConfig = sut.InitializeAppConfig();
var firstSession = sut.InitializeSessionConfiguration();
var secondSession = sut.InitializeSessionConfiguration();
var thirdSession = sut.InitializeSessionConfiguration();
Assert.AreNotEqual(firstSession.SessionId, secondSession.SessionId);
Assert.AreNotEqual(firstSession.ClientAuthenticationToken, secondSession.ClientAuthenticationToken);
Assert.AreNotEqual(secondSession.SessionId, thirdSession.SessionId);
Assert.AreNotEqual(secondSession.ClientAuthenticationToken, thirdSession.ClientAuthenticationToken);
}
private void RegisterModules()
{
sut.Register(binaryParser.Object);
sut.Register(binarySerializer.Object);
sut.Register(fileLoader.Object);
sut.Register(fileSaver.Object);
sut.Register(networkLoader.Object);
sut.Register(xmlParser.Object);
sut.Register(xmlSerializer.Object);
}
/// <summary>
/// Required for unit tests to be able to retrieve the <see cref="Assembly.GetEntryAssembly"/> while executing.
/// </summary>
public void SetEntryAssembly()
{
var assembly = Assembly.GetCallingAssembly();
var manager = new AppDomainManager();
var entryAssemblyfield = manager.GetType().GetField("m_entryAssembly", BindingFlags.Instance | BindingFlags.NonPublic);
entryAssemblyfield.SetValue(manager, assembly);
var domain = AppDomain.CurrentDomain;
var domainManagerField = domain.GetType().GetField("_domainManager", BindingFlags.Instance | BindingFlags.NonPublic);
domainManagerField.SetValue(domain, manager);
}
}
}

View File

@@ -0,0 +1,34 @@
/*
* 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 Microsoft.VisualStudio.TestTools.UnitTesting;
using SafeExamBrowser.Configuration.Cryptography;
namespace SafeExamBrowser.Configuration.UnitTests.Cryptography
{
[TestClass]
public class HashAlgorithmTests
{
private HashAlgorithm sut;
[TestInitialize]
public void Initialize()
{
sut = new HashAlgorithm();
}
[TestMethod]
public void MustGeneratePasswordHashCorrectly()
{
var hash = "4adfa806cb610693a6200e4cdbdafeaf352876a35f964a781d691457df9cd378";
var generated = sut.GenerateHashFor("blabbedyblubbedy");
Assert.AreEqual(hash, generated);
}
}
}

View File

@@ -0,0 +1,62 @@
/*
* 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.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Configuration.Contracts.Integrity;
using SafeExamBrowser.Configuration.Cryptography;
using SafeExamBrowser.Logging.Contracts;
namespace SafeExamBrowser.Configuration.UnitTests.Cryptography
{
[TestClass]
public class KeyGeneratorTests
{
private AppConfig appConfig;
private Mock<IIntegrityModule> integrityModule;
private Mock<ILogger> logger;
private KeyGenerator sut;
[TestInitialize]
public void Initialize()
{
appConfig = new AppConfig();
integrityModule = new Mock<IIntegrityModule>();
logger = new Mock<ILogger>();
sut = new KeyGenerator(appConfig, integrityModule.Object, logger.Object);
}
[TestMethod]
[ExpectedException(typeof(Exception), AllowDerivedTypes = true)]
public void CalculateBrowserExamKeyHash_MustFailWithoutUrl()
{
sut.CalculateBrowserExamKeyHash(default, default, default);
}
[TestMethod]
[ExpectedException(typeof(Exception), AllowDerivedTypes = true)]
public void CalculateConfigurationKeyHash_MustFailWithoutUrl()
{
sut.CalculateConfigurationKeyHash(default, default);
}
[TestMethod]
public void MustAllowForConcurrentKeyHashCalculation()
{
Parallel.For(0, 1000, (_) =>
{
sut.CalculateBrowserExamKeyHash(default, default, "https://www.safeexambrowser.org");
sut.CalculateConfigurationKeyHash(default, "https://www.safeexambrowser.org");
});
}
}
}

View File

@@ -0,0 +1,72 @@
/*
* 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.IO;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Configuration.Cryptography;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Logging.Contracts;
namespace SafeExamBrowser.Configuration.UnitTests.Cryptography
{
[TestClass]
public class PasswordEncryptionTests
{
private Mock<ILogger> logger;
private PasswordEncryption sut;
[TestInitialize]
public void Initialize()
{
logger = new Mock<ILogger>();
sut = new PasswordEncryption(logger.Object);
}
[TestMethod]
public void MustPerformCorrectly()
{
var password = "test1234";
var message = Encoding.UTF8.GetBytes("A super secret message!");
var saveStatus = sut.Encrypt(new MemoryStream(message), password, out var encrypted);
var loadStatus = sut.Decrypt(encrypted, password, out var decrypted);
var original = new MemoryStream(message);
decrypted.Seek(0, SeekOrigin.Begin);
original.Seek(0, SeekOrigin.Begin);
while (original.Position < original.Length)
{
Assert.AreEqual(original.ReadByte(), decrypted.ReadByte());
}
Assert.AreEqual(SaveStatus.Success, saveStatus);
Assert.AreEqual(LoadStatus.Success, loadStatus);
}
[TestMethod]
public void MustRequestPasswordForDecryption()
{
var status = sut.Decrypt(new MemoryStream(), null, out _);
Assert.AreEqual(LoadStatus.PasswordNeeded, status);
}
[TestMethod]
public void MustRequestPasswordIfInvalid()
{
var password = "test1234";
var saveStatus = sut.Encrypt(new MemoryStream(Encoding.UTF8.GetBytes("A super secret message!")), password, out var encrypted);
var loadStatus = sut.Decrypt(encrypted, "not the correct password", out _);
Assert.AreEqual(SaveStatus.Success, saveStatus);
Assert.AreEqual(LoadStatus.PasswordNeeded, loadStatus);
}
}
}

View File

@@ -0,0 +1,94 @@
/*
* 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.IO;
using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Configuration.Cryptography;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Configuration.Contracts.Cryptography;
using SafeExamBrowser.Logging.Contracts;
namespace SafeExamBrowser.Configuration.UnitTests.Cryptography
{
[TestClass]
public class PublicKeyEncryptionTests
{
private Mock<ILogger> logger;
private Mock<ICertificateStore> store;
private X509Certificate2 certificate;
private PublicKeyEncryption sut;
[TestInitialize]
public void Initialize()
{
logger = new Mock<ILogger>();
store = new Mock<ICertificateStore>();
LoadCertificate();
store.Setup(s => s.TryGetCertificateWith(It.IsAny<byte[]>(), out certificate)).Returns(true);
sut = new PublicKeyEncryption(store.Object, logger.Object);
}
[TestMethod]
public void MustPerformCorrectly()
{
var message = Encoding.UTF8.GetBytes("A super secret message!");
var saveStatus = sut.Encrypt(new MemoryStream(message), certificate, out var encrypted);
var loadStatus = sut.Decrypt(encrypted, out var decrypted, out _);
var original = new MemoryStream(message);
decrypted.Seek(0, SeekOrigin.Begin);
original.Seek(0, SeekOrigin.Begin);
while (original.Position < original.Length)
{
Assert.AreEqual(original.ReadByte(), decrypted.ReadByte());
}
Assert.AreEqual(SaveStatus.Success, saveStatus);
Assert.AreEqual(LoadStatus.Success, loadStatus);
}
[TestMethod]
public void MustFailIfCertificateNotFound()
{
store.Setup(s => s.TryGetCertificateWith(It.IsAny<byte[]>(), out certificate)).Returns(false);
var buffer = new byte[20];
new Random().NextBytes(buffer);
var data = new MemoryStream(buffer);
var status = sut.Decrypt(data, out _, out _);
Assert.AreEqual(LoadStatus.InvalidData, status);
}
/// <summary>
/// makecert -sv UnitTestCert.pvk -n "CN=Unit Test Certificate" UnitTestCert.cer -r -pe -sky eXchange
/// pvk2pfx -pvk UnitTestCert.pvk -spc UnitTestCert.cer -pfx UnitTestCert.pfx -f
/// </summary>
private void LoadCertificate()
{
var path = $"{nameof(SafeExamBrowser)}.{nameof(Configuration)}.{nameof(UnitTests)}.UnitTestCert.pfx";
using (var stream = Assembly.GetAssembly(GetType()).GetManifestResourceStream(path))
{
var data = new byte[stream.Length];
stream.Read(data, 0, (int)stream.Length);
certificate = new X509Certificate2(data);
}
}
}
}

View File

@@ -0,0 +1,96 @@
/*
* 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.IO;
using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Configuration.Cryptography;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Configuration.Contracts.Cryptography;
using SafeExamBrowser.Logging.Contracts;
namespace SafeExamBrowser.Configuration.UnitTests.Cryptography
{
[TestClass]
public class PublicKeySymmetricEncryptionTests
{
private Mock<ILogger> logger;
private PasswordEncryption passwordEncryption;
private Mock<ICertificateStore> store;
private PublicKeySymmetricEncryption sut;
private X509Certificate2 certificate;
[TestInitialize]
public void Initialize()
{
logger = new Mock<ILogger>();
passwordEncryption = new PasswordEncryption(logger.Object);
store = new Mock<ICertificateStore>();
LoadCertificate();
store.Setup(s => s.TryGetCertificateWith(It.IsAny<byte[]>(), out certificate)).Returns(true);
sut = new PublicKeySymmetricEncryption(store.Object, logger.Object, passwordEncryption);
}
[TestMethod]
public void MustPerformCorrectly()
{
var message = Encoding.UTF8.GetBytes("A super secret message!");
var saveStatus = sut.Encrypt(new MemoryStream(message), certificate, out var encrypted);
var loadStatus = sut.Decrypt(encrypted, out var decrypted, out _);
var original = new MemoryStream(message);
decrypted.Seek(0, SeekOrigin.Begin);
original.Seek(0, SeekOrigin.Begin);
while (original.Position < original.Length)
{
Assert.AreEqual(original.ReadByte(), decrypted.ReadByte());
}
Assert.AreEqual(SaveStatus.Success, saveStatus);
Assert.AreEqual(LoadStatus.Success, loadStatus);
}
[TestMethod]
public void MustFailIfCertificateNotFound()
{
store.Setup(s => s.TryGetCertificateWith(It.IsAny<byte[]>(), out certificate)).Returns(false);
var buffer = new byte[20];
new Random().NextBytes(buffer);
var data = new MemoryStream(buffer);
var status = sut.Decrypt(data, out _, out _);
Assert.AreEqual(LoadStatus.InvalidData, status);
}
/// <summary>
/// makecert -sv UnitTestCert.pvk -n "CN=Unit Test Certificate" UnitTestCert.cer -r -pe -sky eXchange
/// pvk2pfx -pvk UnitTestCert.pvk -spc UnitTestCert.cer -pfx UnitTestCert.pfx -f
/// </summary>
private void LoadCertificate()
{
var path = $"{nameof(SafeExamBrowser)}.{nameof(Configuration)}.{nameof(UnitTests)}.UnitTestCert.pfx";
using (var stream = Assembly.GetAssembly(GetType()).GetManifestResourceStream(path))
{
var data = new byte[stream.Length];
stream.Read(data, 0, (int)stream.Length);
certificate = new X509Certificate2(data);
}
}
}
}

View File

@@ -0,0 +1,100 @@
/*
* 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.IO;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Configuration.DataCompression;
using SafeExamBrowser.Logging.Contracts;
namespace SafeExamBrowser.Configuration.UnitTests.DataCompression
{
[TestClass]
public class GZipCompressorTests
{
private const int ID1 = 0x1F;
private const int ID2 = 0x8B;
private const int CM = 8;
private const int FOOTER_LENGTH = 8;
private const int HEADER_LENGTH = 10;
private Mock<ILogger> logger;
private GZipCompressor sut;
[TestInitialize]
public void Initialize()
{
logger = new Mock<ILogger>();
sut = new GZipCompressor(logger.Object);
}
[TestMethod]
public void MustCorrectlyDetectGZipStream()
{
var randomBytes = new byte[123];
new Random().NextBytes(randomBytes);
Assert.IsFalse(sut.IsCompressed(null));
Assert.IsFalse(sut.IsCompressed(new MemoryStream()));
Assert.IsFalse(sut.IsCompressed(new MemoryStream(randomBytes)));
Assert.IsTrue(sut.IsCompressed(new MemoryStream(new byte[] { ID1, ID2, CM }.Concat(randomBytes).ToArray())));
}
[TestMethod]
public void MustPerformCorrectly()
{
var data = Encoding.UTF8.GetBytes(String.Join(" ", Enumerable.Repeat("A comparatively easy text to compress.", 100)));
var compressed = sut.Compress(new MemoryStream(data));
compressed.Seek(0, SeekOrigin.Begin);
Assert.AreEqual(ID1, compressed.ReadByte());
Assert.AreEqual(ID2, compressed.ReadByte());
Assert.AreEqual(CM, compressed.ReadByte());
Assert.IsTrue(compressed.Length < data.Length);
var decompressed = sut.Decompress(compressed);
decompressed.Seek(0, SeekOrigin.Begin);
foreach (var item in data)
{
Assert.AreEqual(item, decompressed.ReadByte());
}
Assert.IsTrue(decompressed.Length == data.Length);
}
[TestMethod]
public void MustPreviewDataCorrectly()
{
var data = Encoding.UTF8.GetBytes("A comparatively easy text to compress.");
var compressed = sut.Compress(new MemoryStream(data));
var preview = sut.Peek(compressed, 5);
try
{
var position = compressed.Position;
var length = compressed.Length;
}
catch (ObjectDisposedException)
{
Assert.Fail("Source stream was disposed after previewing data!");
}
Assert.AreEqual(5, preview.Length);
Assert.IsTrue(Encoding.UTF8.GetBytes("A com").SequenceEqual(preview));
}
}
}

View File

@@ -0,0 +1,168 @@
/*
* 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.IO;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Configuration.DataFormats;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Configuration.Contracts.Cryptography;
using SafeExamBrowser.Configuration.Contracts.DataCompression;
using SafeExamBrowser.Configuration.Contracts.DataFormats;
using SafeExamBrowser.Logging.Contracts;
namespace SafeExamBrowser.Configuration.UnitTests.DataFormats
{
[TestClass]
public class BinaryParserTests
{
private Mock<IDataCompressor> compressor;
private Mock<IHashAlgorithm> hashAlgorithm;
private Mock<ILogger> logger;
private Mock<IPasswordEncryption> passwordEncryption;
private Mock<IPublicKeyEncryption> publicKeyEncryption;
private Mock<IPublicKeyEncryption> symmetricEncryption;
private Mock<IDataParser> xmlParser;
private BinaryParser sut;
[TestInitialize]
public void Initialize()
{
compressor = new Mock<IDataCompressor>();
hashAlgorithm = new Mock<IHashAlgorithm>();
logger = new Mock<ILogger>();
passwordEncryption = new Mock<IPasswordEncryption>();
publicKeyEncryption = new Mock<IPublicKeyEncryption>();
symmetricEncryption = new Mock<IPublicKeyEncryption>();
xmlParser = new Mock<IDataParser>();
xmlParser.Setup(p => p.TryParse(It.IsAny<Stream>(), It.IsAny<PasswordParameters>())).Returns(new ParseResult { Status = LoadStatus.Success });
sut = new BinaryParser(compressor.Object, hashAlgorithm.Object, logger.Object, passwordEncryption.Object, publicKeyEncryption.Object, symmetricEncryption.Object, xmlParser.Object);
}
[TestMethod]
public void MustCorrectlyDetectValidPrefixes()
{
var data = new byte[123];
var pswd = new MemoryStream(Encoding.UTF8.GetBytes(BinaryBlock.Password).Concat(data).ToArray());
var pwcc = new MemoryStream(Encoding.UTF8.GetBytes(BinaryBlock.PasswordConfigureClient).Concat(data).ToArray());
var plnd = new MemoryStream(Encoding.UTF8.GetBytes(BinaryBlock.PlainData).Concat(data).ToArray());
var pkhs = new MemoryStream(Encoding.UTF8.GetBytes(BinaryBlock.PublicKey).Concat(data).ToArray());
var phsk = new MemoryStream(Encoding.UTF8.GetBytes(BinaryBlock.PublicKeySymmetric).Concat(data).ToArray());
Assert.IsFalse(sut.CanParse(null));
Assert.IsFalse(sut.CanParse(new MemoryStream()));
Assert.IsFalse(sut.CanParse(new MemoryStream(data)));
Assert.IsTrue(sut.CanParse(pswd));
Assert.IsTrue(sut.CanParse(pwcc));
Assert.IsTrue(sut.CanParse(plnd));
Assert.IsTrue(sut.CanParse(pkhs));
Assert.IsTrue(sut.CanParse(phsk));
}
[TestMethod]
public void MustCorrectlyParsePasswordBlock()
{
var data = new byte[123];
var decrypted = default(Stream);
var pswd = new MemoryStream(Encoding.UTF8.GetBytes(BinaryBlock.Password).Concat(data).ToArray()) as Stream;
passwordEncryption.Setup(p => p.Decrypt(It.IsAny<Stream>(), It.Is<string>(s => s == "wrong"), out decrypted)).Returns(LoadStatus.PasswordNeeded);
passwordEncryption.Setup(p => p.Decrypt(It.IsAny<Stream>(), It.Is<string>(s => s == "correct"), out decrypted)).Returns(LoadStatus.Success);
var result = sut.TryParse(pswd);
Assert.AreEqual(LoadStatus.PasswordNeeded, result.Status);
result = sut.TryParse(pswd, new PasswordParameters { Password = "wrong" });
Assert.AreEqual(LoadStatus.PasswordNeeded, result.Status);
result = sut.TryParse(pswd, new PasswordParameters { Password = "correct" });
passwordEncryption.Verify(p => p.Decrypt(It.IsAny<Stream>(), It.IsAny<string>(), out decrypted), Times.AtLeastOnce);
xmlParser.Verify(p => p.TryParse(It.Is<Stream>(s => s == decrypted), It.IsAny<PasswordParameters>()), Times.Once);
publicKeyEncryption.VerifyNoOtherCalls();
symmetricEncryption.VerifyNoOtherCalls();
}
[TestMethod]
public void MustCorrectlyParsePlainDataBlock()
{
var data = new byte[123];
var plnd = new MemoryStream(Encoding.UTF8.GetBytes(BinaryBlock.PlainData).Concat(data).ToArray());
compressor.Setup(c => c.Decompress(It.IsAny<Stream>())).Returns(plnd);
compressor.Setup(c => c.Peek(It.IsAny<Stream>(), It.IsAny<int>())).Returns(Encoding.UTF8.GetBytes(BinaryBlock.PlainData));
compressor.Setup(c => c.IsCompressed(It.IsAny<Stream>())).Returns(true);
var result = sut.TryParse(plnd);
compressor.Verify(c => c.IsCompressed(It.IsAny<Stream>()), Times.AtLeastOnce);
compressor.Verify(c => c.Decompress(It.IsAny<Stream>()), Times.AtLeastOnce);
xmlParser.Verify(x => x.TryParse(It.IsAny<Stream>(), It.IsAny<PasswordParameters>()), Times.Once);
passwordEncryption.VerifyNoOtherCalls();
publicKeyEncryption.VerifyNoOtherCalls();
symmetricEncryption.VerifyNoOtherCalls();
Assert.AreEqual(LoadStatus.Success, result.Status);
}
[TestMethod]
public void MustCorrectlyParsePublicKeyBlock()
{
var data = new byte[123];
var certificate = default(X509Certificate2);
var decrypted = default(Stream);
var pswd = new MemoryStream(Encoding.UTF8.GetBytes(BinaryBlock.Password).Concat(data).ToArray()) as Stream;
var pkhs = new MemoryStream(Encoding.UTF8.GetBytes(BinaryBlock.PublicKey).Concat(data).ToArray());
passwordEncryption.Setup(p => p.Decrypt(It.IsAny<Stream>(), It.IsAny<string>(), out decrypted)).Returns(LoadStatus.Success);
publicKeyEncryption.Setup(p => p.Decrypt(It.IsAny<Stream>(), out pswd, out certificate)).Returns(LoadStatus.Success);
var result = sut.TryParse(pkhs, new PasswordParameters { Password = "blubb" });
publicKeyEncryption.Verify(p => p.Decrypt(It.IsAny<Stream>(), out decrypted, out certificate), Times.Once);
passwordEncryption.Verify(p => p.Decrypt(It.IsAny<Stream>(), It.Is<string>(s => s == "blubb"), out decrypted), Times.Once);
symmetricEncryption.VerifyNoOtherCalls();
Assert.AreEqual(LoadStatus.Success, result.Status);
}
[TestMethod]
public void MustOnlyParseIfFormatSupported()
{
var data = new byte[123];
var certificate = default(X509Certificate2);
var decrypted = default(Stream);
var pswd = new MemoryStream(Encoding.UTF8.GetBytes(BinaryBlock.Password).Concat(data).ToArray()) as Stream;
var pwcc = new MemoryStream(Encoding.UTF8.GetBytes(BinaryBlock.PasswordConfigureClient).Concat(data).ToArray());
var plnd = new MemoryStream(Encoding.UTF8.GetBytes(BinaryBlock.PlainData).Concat(data).ToArray());
var pkhs = new MemoryStream(Encoding.UTF8.GetBytes(BinaryBlock.PublicKey).Concat(data).ToArray());
var phsk = new MemoryStream(Encoding.UTF8.GetBytes(BinaryBlock.PublicKeySymmetric).Concat(data).ToArray());
passwordEncryption.Setup(p => p.Decrypt(It.IsAny<Stream>(), It.IsAny<string>(), out decrypted)).Returns(LoadStatus.Success);
publicKeyEncryption.Setup(p => p.Decrypt(It.IsAny<Stream>(), out pswd, out certificate)).Returns(LoadStatus.Success);
symmetricEncryption.Setup(p => p.Decrypt(It.IsAny<Stream>(), out pswd, out certificate)).Returns(LoadStatus.Success);
Assert.AreEqual(LoadStatus.InvalidData, sut.TryParse(new MemoryStream(data)).Status);
Assert.AreEqual(LoadStatus.Success, sut.TryParse(pswd, new PasswordParameters { Password = "blubb" })?.Status);
Assert.AreEqual(LoadStatus.Success, sut.TryParse(pwcc, new PasswordParameters { Password = "blubb" })?.Status);
Assert.AreEqual(LoadStatus.Success, sut.TryParse(plnd).Status);
Assert.AreEqual(LoadStatus.Success, sut.TryParse(pkhs, new PasswordParameters { Password = "blubb" }).Status);
Assert.AreEqual(LoadStatus.Success, sut.TryParse(phsk, new PasswordParameters { Password = "blubb" }).Status);
}
}
}

View File

@@ -0,0 +1,153 @@
/*
* 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.Collections.Generic;
using System.IO;
using System.Security.Cryptography.X509Certificates;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Configuration.DataFormats;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Configuration.Contracts.Cryptography;
using SafeExamBrowser.Configuration.Contracts.DataCompression;
using SafeExamBrowser.Configuration.Contracts.DataFormats;
using SafeExamBrowser.Logging.Contracts;
namespace SafeExamBrowser.Configuration.UnitTests.DataFormats
{
[TestClass]
public class BinarySerializerTests
{
private Mock<IDataCompressor> compressor;
private Mock<ILogger> logger;
private Mock<IPasswordEncryption> passwordEncryption;
private Mock<IPublicKeyEncryption> publicKeyEncryption;
private Mock<IPublicKeyEncryption> symmetricEncryption;
private Mock<IDataSerializer> xmlSerializer;
private SerializeResult xmlResult;
private BinarySerializer sut;
[TestInitialize]
public void Initialize()
{
compressor = new Mock<IDataCompressor>();
logger = new Mock<ILogger>();
passwordEncryption = new Mock<IPasswordEncryption>();
publicKeyEncryption = new Mock<IPublicKeyEncryption>();
symmetricEncryption = new Mock<IPublicKeyEncryption>();
xmlResult = new SerializeResult { Data = new MemoryStream(), Status = SaveStatus.Success };
xmlSerializer = new Mock<IDataSerializer>();
compressor.Setup(c => c.Compress(It.IsAny<Stream>())).Returns(new MemoryStream());
xmlSerializer.Setup(x => x.TrySerialize(It.IsAny<IDictionary<string, object>>(), It.IsAny<EncryptionParameters>())).Returns(xmlResult);
sut = new BinarySerializer(compressor.Object, logger.Object, passwordEncryption.Object, publicKeyEncryption.Object, symmetricEncryption.Object, xmlSerializer.Object);
}
[TestMethod]
public void MustOnlySupportBinaryFormat()
{
var values = Enum.GetValues(typeof(FormatType));
foreach (var value in values)
{
if (value is FormatType format && format != FormatType.Binary)
{
Assert.IsFalse(sut.CanSerialize(format));
}
}
Assert.IsTrue(sut.CanSerialize(FormatType.Binary));
}
[TestMethod]
public void MustCorrectlySerializePlainDataBlock()
{
var data = new Dictionary<string, object>();
var result = sut.TrySerialize(data);
compressor.Verify(c => c.Compress(It.IsAny<Stream>()), Times.Exactly(2));
xmlSerializer.Verify(x => x.TrySerialize(It.Is<IDictionary<string, object>>(d => d == data), It.Is<EncryptionParameters>(e => e == null)), Times.Once);
passwordEncryption.VerifyNoOtherCalls();
publicKeyEncryption.VerifyNoOtherCalls();
symmetricEncryption.VerifyNoOtherCalls();
Assert.AreEqual(SaveStatus.Success, result.Status);
}
[TestMethod]
public void MustCorrectlySerializePasswordBlock()
{
var encrypted = new MemoryStream() as Stream;
var data = new Dictionary<string, object>();
var encryption = new PasswordParameters { Password = "blubb" };
passwordEncryption.Setup(p => p.Encrypt(It.IsAny<Stream>(), It.IsAny<string>(), out encrypted)).Returns(SaveStatus.Success);
var result = sut.TrySerialize(data, encryption);
compressor.Verify(c => c.Compress(It.IsAny<Stream>()), Times.Exactly(2));
passwordEncryption.Verify(p => p.Encrypt(It.IsAny<Stream>(), It.Is<string>(s => s == encryption.Password), out encrypted), Times.Once);
xmlSerializer.Verify(x => x.TrySerialize(It.Is<IDictionary<string, object>>(d => d == data), It.Is<EncryptionParameters>(e => e == null)), Times.Once);
publicKeyEncryption.VerifyNoOtherCalls();
symmetricEncryption.VerifyNoOtherCalls();
Assert.AreEqual(SaveStatus.Success, result.Status);
}
[TestMethod]
public void MustCorrectlySerializePublicKeyBlock()
{
var encrypted = new MemoryStream() as Stream;
var data = new Dictionary<string, object>();
var encryption = new PublicKeyParameters
{
InnerEncryption = new PasswordParameters { Password = "test" },
SymmetricEncryption = false
};
passwordEncryption.Setup(p => p.Encrypt(It.IsAny<Stream>(), It.IsAny<string>(), out encrypted)).Returns(SaveStatus.Success);
publicKeyEncryption.Setup(p => p.Encrypt(It.IsAny<Stream>(), It.IsAny<X509Certificate2>(), out encrypted)).Returns(SaveStatus.Success);
var result = sut.TrySerialize(data, encryption);
compressor.Verify(c => c.Compress(It.IsAny<Stream>()), Times.Exactly(2));
passwordEncryption.Verify(p => p.Encrypt(It.IsAny<Stream>(), It.Is<string>(s => s == encryption.InnerEncryption.Password), out encrypted), Times.Once);
publicKeyEncryption.Verify(p => p.Encrypt(It.IsAny<Stream>(), It.IsAny<X509Certificate2>(), out encrypted), Times.Once);
xmlSerializer.Verify(x => x.TrySerialize(It.Is<IDictionary<string, object>>(d => d == data), It.Is<EncryptionParameters>(e => e == null)), Times.Once);
symmetricEncryption.VerifyNoOtherCalls();
Assert.AreEqual(SaveStatus.Success, result.Status);
}
[TestMethod]
public void MustCorrectlySerializePublicKeySymmetricBlock()
{
var encrypted = new MemoryStream() as Stream;
var data = new Dictionary<string, object>();
var encryption = new PublicKeyParameters
{
SymmetricEncryption = true
};
symmetricEncryption.Setup(p => p.Encrypt(It.IsAny<Stream>(), It.IsAny<X509Certificate2>(), out encrypted)).Returns(SaveStatus.Success);
var result = sut.TrySerialize(data, encryption);
compressor.Verify(c => c.Compress(It.IsAny<Stream>()), Times.Exactly(2));
symmetricEncryption.Verify(p => p.Encrypt(It.IsAny<Stream>(), It.IsAny<X509Certificate2>(), out encrypted), Times.Once);
xmlSerializer.Verify(x => x.TrySerialize(It.Is<IDictionary<string, object>>(d => d == data), It.Is<EncryptionParameters>(e => e == null)), Times.Once);
passwordEncryption.VerifyNoOtherCalls();
publicKeyEncryption.VerifyNoOtherCalls();
Assert.AreEqual(SaveStatus.Success, result.Status);
}
}
}

View File

@@ -0,0 +1,239 @@
/*
* 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.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Configuration.ConfigurationData;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Configuration.Contracts.DataCompression;
using SafeExamBrowser.Configuration.Contracts.DataFormats;
using SafeExamBrowser.Configuration.DataFormats;
using SafeExamBrowser.Logging.Contracts;
namespace SafeExamBrowser.Configuration.UnitTests.DataFormats
{
[TestClass]
public class XmlParserTests
{
private Mock<IDataCompressor> compressor;
private Mock<ILogger> logger;
private XmlParser sut;
[TestInitialize]
public void Initialize()
{
compressor = new Mock<IDataCompressor>();
logger = new Mock<ILogger>();
sut = new XmlParser(compressor.Object, logger.Object);
}
[TestMethod]
public void MustOnlyParseXmlData()
{
Assert.IsFalse(sut.CanParse(null));
Assert.IsFalse(sut.CanParse(new MemoryStream(Encoding.UTF8.GetBytes("<key>someKey</key><value>1</value>"))));
Assert.IsFalse(sut.CanParse(new MemoryStream(Encoding.UTF8.GetBytes("<html></html>"))));
Assert.IsFalse(sut.CanParse(new MemoryStream(Encoding.UTF8.GetBytes("<?x"))));
Assert.IsTrue(sut.CanParse(new MemoryStream(Encoding.UTF8.GetBytes("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"))));
}
[TestMethod]
public void MustCorrectlyParseXml()
{
var data = LoadTestData();
var result = sut.TryParse(data);
Assert.AreEqual(LoadStatus.Success, result.Status);
Assert.AreEqual(FormatType.Xml, result.Format);
Assert.IsNull(result.Encryption);
Assert.IsNotNull(result.RawData);
Assert.AreEqual("test123", result.RawData["someString"]);
Assert.AreEqual(9876, result.RawData["someInteger"]);
Assert.IsTrue(Convert.FromBase64String("/5vlF76sb+5vgkhjiNTOn7l1SN3Ho2UAMJD3TtLo49M=").SequenceEqual(result.RawData["someData"] as IEnumerable<byte>));
Assert.AreEqual(true, result.RawData["someBoolean"]);
Assert.AreEqual(false, result.RawData["anotherBoolean"]);
Assert.AreEqual(12.34, result.RawData["someReal"]);
Assert.AreEqual(new DateTime(2019, 02, 20, 12, 30, 00, 123), result.RawData["someDate"]);
var array = result.RawData["anArray"] as List<object>;
var dictOne = array[0] as Dictionary<string, object>;
var dictTwo = array[1] as Dictionary<string, object>;
var dictThree = array[2] as Dictionary<string, object>;
Assert.AreEqual(3, dictOne["dictOneKeyOne"]);
Assert.AreEqual(4, dictOne["dictOneKeyTwo"]);
Assert.AreEqual(5, dictTwo["dictTwoKeyOne"]);
Assert.AreEqual(6, dictTwo["dictTwoKeyTwo"]);
Assert.AreEqual(7, dictThree["dictThreeKeyOne"]);
Assert.AreEqual(1, (dictThree["dictThreeKeyTwo"] as List<object>)[0]);
Assert.AreEqual(2, (dictThree["dictThreeKeyTwo"] as List<object>)[1]);
Assert.AreEqual(3, (dictThree["dictThreeKeyTwo"] as List<object>)[2]);
Assert.AreEqual(4, (dictThree["dictThreeKeyTwo"] as List<object>)[3]);
}
[TestMethod]
public void MustCorrectlyParseTestSettings()
{
var data = LoadTestSettings();
var result = sut.TryParse(data);
Assert.AreEqual(LoadStatus.Success, result.Status);
Assert.AreEqual(FormatType.Xml, result.Format);
Assert.IsNull(result.Encryption);
Assert.IsNotNull(result.RawData);
Assert.AreEqual(true, result.RawData[Keys.Browser.AllowConfigurationDownloads]);
Assert.AreEqual(0, result.RawData[Keys.ConfigurationFile.ConfigurationPurpose]);
Assert.AreEqual("https://safeexambrowser.org/start", result.RawData[Keys.Browser.StartUrl]);
Assert.AreEqual(true, result.RawData[Keys.Keyboard.EnableF5]);
Assert.IsInstanceOfType(result.RawData[Keys.Network.Certificates.EmbeddedCertificates], typeof(List<object>));
}
[TestMethod]
public void MustCheckForRootDictionary()
{
var xml = "<?xml version=\"1.0\"?><plist></plist>";
var xml2 = "<?xml version=\"1.0\"?><plist><key>someKey</key><integer>5</integer></plist>";
var xml3 = "<?xml version=\"1.0\"?><plist><dictionary></dictionary></plist>";
var result = sut.TryParse(new MemoryStream(Encoding.UTF8.GetBytes(xml)));
Assert.AreEqual(LoadStatus.InvalidData, result.Status);
result = sut.TryParse(new MemoryStream(Encoding.UTF8.GetBytes(xml2)));
Assert.AreEqual(LoadStatus.InvalidData, result.Status);
result = sut.TryParse(new MemoryStream(Encoding.UTF8.GetBytes(xml3)));
Assert.AreEqual(LoadStatus.InvalidData, result.Status);
}
[TestMethod]
public void MustDecompressDataIfCompressed()
{
var bytes = Encoding.UTF8.GetBytes("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
var data = LoadTestData();
var stream = new MemoryStream(Encoding.UTF8.GetBytes("some incorrect data here"));
compressor.Setup(c => c.Peek(It.IsAny<Stream>(), It.IsAny<int>())).Returns<Stream, int>((d, c) => bytes.Take(c).ToArray());
compressor.Setup(c => c.IsCompressed(It.IsAny<Stream>())).Returns(true);
Assert.IsTrue(sut.CanParse(stream));
compressor.Verify(c => c.IsCompressed(It.IsAny<Stream>()), Times.Once);
compressor.Verify(c => c.Peek(It.IsAny<Stream>(), It.IsAny<int>()), Times.Once);
compressor.Setup(c => c.Decompress(It.IsAny<Stream>())).Returns(data);
var result = sut.TryParse(stream);
compressor.Verify(c => c.IsCompressed(It.IsAny<Stream>()), Times.AtLeast(2));
compressor.Verify(c => c.Decompress(It.IsAny<Stream>()), Times.Once);
Assert.AreEqual(LoadStatus.Success, result.Status);
}
[TestMethod]
public void MustParseEmptyXml()
{
var xml = "<?xml version=\"1.0\"?><plist><dict></dict></plist>";
var result = sut.TryParse(new MemoryStream(Encoding.UTF8.GetBytes(xml)));
Assert.AreEqual(LoadStatus.Success, result.Status);
Assert.AreEqual(0, result.RawData.Count);
}
[TestMethod]
public void MustDetectInvalidKey()
{
var xml = "<?xml version=\"1.0\"?><plist><dict><nokey>blubb</nokey><true /></dict></plist>";
var result = sut.TryParse(new MemoryStream(Encoding.UTF8.GetBytes(xml)));
Assert.AreEqual(LoadStatus.InvalidData, result.Status);
}
[TestMethod]
public void MustDetectInvalidValueType()
{
var xml = "<?xml version=\"1.0\"?><plist><dict><key>blubb</key><globb>1234</globb></dict></plist>";
var result = sut.TryParse(new MemoryStream(Encoding.UTF8.GetBytes(xml)));
Assert.AreEqual(LoadStatus.InvalidData, result.Status);
}
[TestMethod]
public void MustAllowEmptyArray()
{
var xml = "<?xml version=\"1.0\"?><plist><dict><key>value</key><array /></dict></plist>";
var result = sut.TryParse(new MemoryStream(Encoding.UTF8.GetBytes(xml)));
Assert.AreEqual(LoadStatus.Success, result.Status);
Assert.IsInstanceOfType(result.RawData["value"], typeof(IList<object>));
}
[TestMethod]
public void MustAbortParsingArrayOnError()
{
var xml = "<?xml version=\"1.0\"?><plist><dict><key>value</key><array><blobb /></array></dict></plist>";
var result = sut.TryParse(new MemoryStream(Encoding.UTF8.GetBytes(xml)));
Assert.AreEqual(LoadStatus.InvalidData, result.Status);
Assert.AreEqual(0, result.RawData.Count);
}
[TestMethod]
public void MustAllowEmptyDictionary()
{
var xml = "<?xml version=\"1.0\"?><plist><dict><key>value</key><dict /></dict></plist>";
var result = sut.TryParse(new MemoryStream(Encoding.UTF8.GetBytes(xml)));
Assert.AreEqual(LoadStatus.Success, result.Status);
Assert.IsInstanceOfType(result.RawData["value"], typeof(IDictionary<string, object>));
}
[TestMethod]
public void MustMapNullForEmptyStringElement()
{
var xml = "<?xml version=\"1.0\"?><plist><dict><key>value</key><string /></dict></plist>";
var result = sut.TryParse(new MemoryStream(Encoding.UTF8.GetBytes(xml)));
Assert.AreEqual(LoadStatus.Success, result.Status);
Assert.IsNull(result.RawData["value"]);
}
[TestMethod]
public void MustNotFailWithWrongData()
{
var result = sut.TryParse(new MemoryStream(Encoding.UTF8.GetBytes("some random text")));
Assert.AreEqual(LoadStatus.InvalidData, result.Status);
}
private Stream LoadTestData()
{
var path = $"{nameof(SafeExamBrowser)}.{nameof(Configuration)}.{nameof(UnitTests)}.{nameof(DataFormats)}.XmlTestData.xml";
var data = Assembly.GetAssembly(GetType()).GetManifestResourceStream(path);
return data;
}
private Stream LoadTestSettings()
{
var path = $"{nameof(SafeExamBrowser)}.{nameof(Configuration)}.{nameof(UnitTests)}.{nameof(DataFormats)}.XmlTestSettings.xml";
var data = Assembly.GetAssembly(GetType()).GetManifestResourceStream(path);
return data;
}
}
}

View File

@@ -0,0 +1,287 @@
/*
* 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.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Configuration.DataFormats;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Configuration.Contracts.DataFormats;
using SafeExamBrowser.Logging.Contracts;
namespace SafeExamBrowser.Configuration.UnitTests.DataFormats
{
[TestClass]
public class XmlSerializerTests
{
private Mock<ILogger> logger;
private XmlSerializer sut;
[TestInitialize]
public void Initialize()
{
logger = new Mock<ILogger>();
sut = new XmlSerializer(logger.Object);
}
[TestMethod]
public void MustOnlySupportXmlFormat()
{
var values = Enum.GetValues(typeof(FormatType));
foreach (var value in values)
{
if (value is FormatType format && format != FormatType.Xml)
{
Assert.IsFalse(sut.CanSerialize(format));
}
}
Assert.IsTrue(sut.CanSerialize(FormatType.Xml));
}
[TestMethod]
public void MustCorrectlySerializeEmptyDictionary()
{
var result = sut.TrySerialize(new Dictionary<string, object>());
Assert.AreEqual(SaveStatus.Success, result.Status);
Assert.IsNotNull(result.Data);
Assert.AreNotEqual(0, result.Data.Length);
result.Data.Seek(0, SeekOrigin.Begin);
var xml = XDocument.Load(result.Data);
Assert.AreEqual(XmlElement.Root, xml.Root.Name);
Assert.AreEqual(1, xml.Root.Nodes().Count());
Assert.AreEqual(XmlElement.Dictionary, (xml.Root.Nodes().First() as XElement).Name);
Assert.IsTrue((xml.Root.Nodes().First() as XElement).IsEmpty);
}
[TestMethod]
public void MustCorrectlySerializeDictionary()
{
var data = new Dictionary<string, object> { { "abc", 1 }, { "def", 2 }, { "ghi", 3 } };
var result = sut.TrySerialize(data);
Assert.AreEqual(SaveStatus.Success, result.Status);
result.Data.Seek(0, SeekOrigin.Begin);
var xml = XDocument.Load(result.Data);
Assert.AreEqual(XmlElement.Root, xml.Root.Name);
Assert.AreEqual(1, xml.Root.Nodes().Count());
var dictionary = xml.Root.Nodes().First() as XElement;
Assert.AreEqual(XmlElement.Key, (dictionary.Nodes().ElementAt(0) as XElement).Name);
Assert.AreEqual("abc", (dictionary.Nodes().ElementAt(0) as XElement).Value);
Assert.AreEqual(XmlElement.Integer, (dictionary.Nodes().ElementAt(1) as XElement).Name);
Assert.AreEqual(1, Convert.ToInt32((dictionary.Nodes().ElementAt(1) as XElement).Value));
Assert.AreEqual(XmlElement.Key, (dictionary.Nodes().ElementAt(2) as XElement).Name);
Assert.AreEqual("def", (dictionary.Nodes().ElementAt(2) as XElement).Value);
Assert.AreEqual(XmlElement.Integer, (dictionary.Nodes().ElementAt(3) as XElement).Name);
Assert.AreEqual(2, Convert.ToInt32((dictionary.Nodes().ElementAt(3) as XElement).Value));
Assert.AreEqual(XmlElement.Key, (dictionary.Nodes().ElementAt(4) as XElement).Name);
Assert.AreEqual("ghi", (dictionary.Nodes().ElementAt(4) as XElement).Value);
Assert.AreEqual(XmlElement.Integer, (dictionary.Nodes().ElementAt(5) as XElement).Name);
Assert.AreEqual(3, Convert.ToInt32((dictionary.Nodes().ElementAt(5) as XElement).Value));
}
[TestMethod]
public void MustCorrectlySerializeArray()
{
var data = new Dictionary<string, object> { { "test", new List<object> { 12.3, 23.4, 34.5, 45.6 } } };
var result = sut.TrySerialize(data);
Assert.AreEqual(SaveStatus.Success, result.Status);
result.Data.Seek(0, SeekOrigin.Begin);
var xml = XDocument.Load(result.Data);
Assert.AreEqual(XmlElement.Root, xml.Root.Name);
Assert.AreEqual(1, xml.Root.Nodes().Count());
var dictionary = xml.Root.Nodes().First() as XElement;
var array = dictionary.Nodes().ElementAt(1) as XElement;
var values = array.Nodes().Cast<XElement>();
Assert.AreEqual(XmlElement.Array, array.Name);
Assert.AreEqual(4, values.Count());
Assert.AreEqual(XmlElement.Real, values.ElementAt(0).Name);
Assert.AreEqual(12.3, Convert.ToDouble(values.ElementAt(0).Value));
Assert.AreEqual(XmlElement.Real, values.ElementAt(1).Name);
Assert.AreEqual(23.4, Convert.ToDouble(values.ElementAt(1).Value));
Assert.AreEqual(XmlElement.Real, values.ElementAt(2).Name);
Assert.AreEqual(34.5, Convert.ToDouble(values.ElementAt(2).Value));
Assert.AreEqual(XmlElement.Real, values.ElementAt(3).Name);
Assert.AreEqual(45.6, Convert.ToDouble(values.ElementAt(3).Value));
}
[TestMethod]
public void MustCorrectlySerializeNestedCollections()
{
var data = new Dictionary<string, object>
{
{ "ArrayOfDictionaries",
new List<object>
{
new Dictionary<string, object>
{
{ "Double", 123.5 },
{ "Text", "Hello World!" }
},
new Dictionary<string, object>
{
{ "InnerArray", new List<object> { 6, 7, 8 } }
}
}
},
{ "SomeDictionary", new Dictionary<string, object> { { "blubb", 123 } } },
{ "SomeArray", new List<object> { 1, 2, 3, 4 } }
};
var result = sut.TrySerialize(data);
Assert.AreEqual(SaveStatus.Success, result.Status);
result.Data.Seek(0, SeekOrigin.Begin);
var xml = XDocument.Load(result.Data);
Assert.AreEqual(XmlElement.Root, xml.Root.Name);
Assert.AreEqual(1, xml.Root.Nodes().Count());
var dictionary = xml.Root.Nodes().First() as XElement;
var arrayOfDictionaries = dictionary.Nodes().ElementAt(1) as XElement;
var innerDictionaryOne = arrayOfDictionaries.Nodes().ElementAt(0) as XElement;
var innerDictionaryTwo = arrayOfDictionaries.Nodes().ElementAt(1) as XElement;
var innerDictionaryTwoArray = innerDictionaryTwo.Nodes().ElementAt(1) as XElement;
var someArray = dictionary.Nodes().ElementAt(3) as XElement;
var someDictionary = dictionary.Nodes().ElementAt(5) as XElement;
Assert.AreEqual(6, dictionary.Nodes().Count());
Assert.AreEqual(2, arrayOfDictionaries.Nodes().Count());
Assert.AreEqual(4, someArray.Nodes().Count());
Assert.AreEqual(2, someDictionary.Nodes().Count());
Assert.AreEqual(XmlElement.Array, arrayOfDictionaries.Name);
Assert.AreEqual(XmlElement.Array, someArray.Name);
Assert.AreEqual(XmlElement.Dictionary, someDictionary.Name);
Assert.AreEqual(123.5, Convert.ToDouble((innerDictionaryOne.Nodes().ElementAt(1) as XElement).Value));
Assert.AreEqual("Hello World!", (innerDictionaryOne.Nodes().ElementAt(3) as XElement).Value);
Assert.AreEqual(6, Convert.ToInt32((innerDictionaryTwoArray.Nodes().ElementAt(0) as XElement).Value));
Assert.AreEqual(7, Convert.ToInt32((innerDictionaryTwoArray.Nodes().ElementAt(1) as XElement).Value));
Assert.AreEqual(8, Convert.ToInt32((innerDictionaryTwoArray.Nodes().ElementAt(2) as XElement).Value));
Assert.AreEqual(1, Convert.ToInt32((someArray.Nodes().ElementAt(0) as XElement).Value));
Assert.AreEqual(2, Convert.ToInt32((someArray.Nodes().ElementAt(1) as XElement).Value));
Assert.AreEqual(3, Convert.ToInt32((someArray.Nodes().ElementAt(2) as XElement).Value));
Assert.AreEqual(4, Convert.ToInt32((someArray.Nodes().ElementAt(3) as XElement).Value));
Assert.AreEqual(123, Convert.ToInt32((someDictionary.Nodes().ElementAt(1) as XElement).Value));
}
[TestMethod]
public void MustCorectlySerializeSimpleTypes()
{
var data = new Dictionary<string, object>
{
{ "SomeData", Convert.FromBase64String("/5vlF76sb+5vgkhjiNTOn7l1SN3Ho2UAMJD3TtLo49M=") },
{ "SomeDate", new DateTime(2019, 02, 20, 14, 30, 00, 123, DateTimeKind.Utc) },
{ "SomeBoolean", true },
{ "AnotherBoolean", false },
{ "SomeInteger", 4567 },
{ "SomeDouble", 4567.8912 },
{ "SomeText", "Here goes some text" }
};
var result = sut.TrySerialize(data);
Assert.AreEqual(SaveStatus.Success, result.Status);
result.Data.Seek(0, SeekOrigin.Begin);
var xml = XDocument.Load(result.Data);
Assert.AreEqual(XmlElement.Root, xml.Root.Name);
Assert.AreEqual(1, xml.Root.Nodes().Count());
var dictionary = xml.Root.Nodes().First() as XElement;
Assert.AreEqual(XmlElement.Key, (dictionary.Nodes().ElementAt(0) as XElement).Name);
Assert.AreEqual("AnotherBoolean", (dictionary.Nodes().ElementAt(0) as XElement).Value);
Assert.AreEqual(false, Convert.ToBoolean((dictionary.Nodes().ElementAt(1) as XElement).Name.LocalName));
Assert.AreEqual(XmlElement.Key, (dictionary.Nodes().ElementAt(2) as XElement).Name);
Assert.AreEqual("SomeBoolean", (dictionary.Nodes().ElementAt(2) as XElement).Value);
Assert.AreEqual(true, Convert.ToBoolean((dictionary.Nodes().ElementAt(3) as XElement).Name.LocalName));
Assert.AreEqual(XmlElement.Key, (dictionary.Nodes().ElementAt(4) as XElement).Name);
Assert.AreEqual("SomeData", (dictionary.Nodes().ElementAt(4) as XElement).Value);
Assert.AreEqual(XmlElement.Data, (dictionary.Nodes().ElementAt(5) as XElement).Name);
Assert.AreEqual("/5vlF76sb+5vgkhjiNTOn7l1SN3Ho2UAMJD3TtLo49M=", (dictionary.Nodes().ElementAt(5) as XElement).Value);
Assert.AreEqual(XmlElement.Key, (dictionary.Nodes().ElementAt(6) as XElement).Name);
Assert.AreEqual("SomeDate", (dictionary.Nodes().ElementAt(6) as XElement).Value);
Assert.AreEqual(XmlElement.Date, (dictionary.Nodes().ElementAt(7) as XElement).Name);
Assert.AreEqual("2019-02-20T14:30:00.123Z", (dictionary.Nodes().ElementAt(7) as XElement).Value);
Assert.AreEqual(XmlElement.Key, (dictionary.Nodes().ElementAt(8) as XElement).Name);
Assert.AreEqual("SomeDouble", (dictionary.Nodes().ElementAt(8) as XElement).Value);
Assert.AreEqual(XmlElement.Real, (dictionary.Nodes().ElementAt(9) as XElement).Name);
Assert.AreEqual(4567.8912, Convert.ToDouble((dictionary.Nodes().ElementAt(9) as XElement).Value));
Assert.AreEqual(XmlElement.Key, (dictionary.Nodes().ElementAt(10) as XElement).Name);
Assert.AreEqual("SomeInteger", (dictionary.Nodes().ElementAt(10) as XElement).Value);
Assert.AreEqual(XmlElement.Integer, (dictionary.Nodes().ElementAt(11) as XElement).Name);
Assert.AreEqual(4567, Convert.ToInt32((dictionary.Nodes().ElementAt(11) as XElement).Value));
Assert.AreEqual(XmlElement.Key, (dictionary.Nodes().ElementAt(12) as XElement).Name);
Assert.AreEqual("SomeText", (dictionary.Nodes().ElementAt(12) as XElement).Value);
Assert.AreEqual(XmlElement.String, (dictionary.Nodes().ElementAt(13) as XElement).Name);
Assert.AreEqual("Here goes some text", (dictionary.Nodes().ElementAt(13) as XElement).Value);
}
[TestMethod]
public void MustSerializeNullAsString()
{
var data = new Dictionary<string, object> { { "test", null } };
var result = sut.TrySerialize(data);
Assert.AreEqual(SaveStatus.Success, result.Status);
result.Data.Seek(0, SeekOrigin.Begin);
var xml = XDocument.Load(result.Data);
Assert.AreEqual(XmlElement.Root, xml.Root.Name);
Assert.AreEqual(1, xml.Root.Nodes().Count());
var dictionary = xml.Root.Nodes().First() as XElement;
Assert.IsTrue((dictionary.Nodes().ElementAt(1) as XElement).IsEmpty);
Assert.AreEqual(XmlElement.String, (dictionary.Nodes().ElementAt(1) as XElement).Name);
}
[TestMethod]
public void MustFailForUnknownSimpleType()
{
var data = new Dictionary<string, object> { { "test", new Tuple<int>(123) } };
var result = sut.TrySerialize(data);
Assert.AreEqual(SaveStatus.InvalidData, result.Status);
}
}
}

View File

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<plist>
<dict>
<key>someString</key>
<string>test123</string>
<key>someInteger</key>
<integer>9876</integer>
<key>someData</key>
<data>/5vlF76sb+5vgkhjiNTOn7l1SN3Ho2UAMJD3TtLo49M=</data>
<key>anArray</key>
<array>
<dict>
<key>dictOneKeyOne</key>
<integer>3</integer>
<key>dictOneKeyTwo</key>
<integer>4</integer>
</dict>
<dict>
<key>dictTwoKeyOne</key>
<integer>5</integer>
<key>dictTwoKeyTwo</key>
<integer>6</integer>
</dict>
<dict>
<key>dictThreeKeyOne</key>
<integer>7</integer>
<key>dictThreeKeyTwo</key>
<array>
<integer>1</integer>
<integer>2</integer>
<integer>3</integer>
<integer>4</integer>
</array>
</dict>
</array>
<key>someBoolean</key>
<true />
<key>anotherBoolean</key>
<false />
<key>someReal</key>
<real>12.34</real>
<key>someDate</key>
<date>2019-02-20T14:30:00.1230000+02:00</date>
</dict>
</plist>

View File

@@ -0,0 +1,795 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>originatorVersion</key>
<string>SEB_Win_2.1.1</string>
<key>startURL</key>
<string>https://safeexambrowser.org/start</string>
<key>startResource</key>
<string />
<key>sebServerURL</key>
<string />
<key>hashedAdminPassword</key>
<string />
<key>allowQuit</key>
<true />
<key>ignoreExitKeys</key>
<true />
<key>hashedQuitPassword</key>
<string />
<key>exitKey1</key>
<integer>2</integer>
<key>exitKey2</key>
<integer>10</integer>
<key>exitKey3</key>
<integer>5</integer>
<key>sebMode</key>
<integer>0</integer>
<key>browserMessagingSocket</key>
<string>ws://localhost:8706</string>
<key>browserMessagingPingTime</key>
<integer>120000</integer>
<key>sebConfigPurpose</key>
<integer>0</integer>
<key>allowPreferencesWindow</key>
<true />
<key>useAsymmetricOnlyEncryption</key>
<false />
<key>browserViewMode</key>
<integer>0</integer>
<key>mainBrowserWindowWidth</key>
<string>100%</string>
<key>mainBrowserWindowHeight</key>
<string>100%</string>
<key>mainBrowserWindowPositioning</key>
<integer>1</integer>
<key>enableBrowserWindowToolbar</key>
<false />
<key>hideBrowserWindowToolbar</key>
<false />
<key>showMenuBar</key>
<false />
<key>showTaskBar</key>
<true />
<key>taskBarHeight</key>
<integer>40</integer>
<key>touchOptimized</key>
<false />
<key>enableZoomText</key>
<true />
<key>enableZoomPage</key>
<true />
<key>zoomMode</key>
<integer>0</integer>
<key>allowSpellCheck</key>
<false />
<key>allowDictionaryLookup</key>
<false />
<key>allowSpellCheckDictionary</key>
<array></array>
<key>additionalDictionaries</key>
<array></array>
<key>showReloadButton</key>
<true />
<key>showTime</key>
<true />
<key>showInputLanguage</key>
<true />
<key>enableTouchExit</key>
<false />
<key>oskBehavior</key>
<integer>2</integer>
<key>audioControlEnabled</key>
<true />
<key>audioMute</key>
<false />
<key>audioVolumeLevel</key>
<integer>25</integer>
<key>audioSetVolumeLevel</key>
<false />
<key>browserScreenKeyboard</key>
<false />
<key>newBrowserWindowByLinkPolicy</key>
<integer>2</integer>
<key>newBrowserWindowByScriptPolicy</key>
<integer>2</integer>
<key>newBrowserWindowByLinkBlockForeign</key>
<false />
<key>newBrowserWindowByScriptBlockForeign</key>
<false />
<key>newBrowserWindowByLinkWidth</key>
<string>1000</string>
<key>newBrowserWindowByLinkHeight</key>
<string>100%</string>
<key>newBrowserWindowByLinkPositioning</key>
<integer>2</integer>
<key>enablePlugIns</key>
<true />
<key>enableJava</key>
<false />
<key>enableJavaScript</key>
<true />
<key>blockPopUpWindows</key>
<false />
<key>allowVideoCapture</key>
<false />
<key>allowAudioCapture</key>
<false />
<key>allowBrowsingBackForward</key>
<false />
<key>newBrowserWindowNavigation</key>
<true />
<key>removeBrowserProfile</key>
<false />
<key>removeLocalStorage</key>
<false />
<key>enableSebBrowser</key>
<true />
<key>browserWindowAllowReload</key>
<true />
<key>newBrowserWindowAllowReload</key>
<true />
<key>showReloadWarning</key>
<true />
<key>newBrowserWindowShowReloadWarning</key>
<false />
<key>browserUserAgentWinDesktopMode</key>
<integer>0</integer>
<key>browserUserAgentWinDesktopModeCustom</key>
<string />
<key>browserUserAgentWinTouchMode</key>
<integer>0</integer>
<key>browserUserAgentWinTouchModeIPad</key>
<string>Mozilla/5.0 (iPad; CPU OS 11_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.3 Mobile/15E216 Safari/605.1.15</string>
<key>browserUserAgentWinTouchModeCustom</key>
<string />
<key>browserUserAgent</key>
<string />
<key>browserUserAgentMac</key>
<integer>0</integer>
<key>browserUserAgentMacCustom</key>
<string />
<key>browserWindowTitleSuffix</key>
<string />
<key>allowDownUploads</key>
<true />
<key>downloadDirectoryOSX</key>
<string>~/Downloads</string>
<key>downloadDirectoryWin</key>
<string />
<key>openDownloads</key>
<false />
<key>chooseFileToUploadPolicy</key>
<integer>0</integer>
<key>downloadPDFFiles</key>
<false />
<key>allowPDFPlugIn</key>
<false />
<key>downloadAndOpenSebConfig</key>
<true />
<key>examKeySalt</key>
<data>/5vlF76sb+5vgkhjiNTOn7l1SN3Ho2UAMJD3TtLo49M=</data>
<key>browserExamKey</key>
<string />
<key>browserURLSalt</key>
<true />
<key>sendBrowserExamKey</key>
<false />
<key>quitURL</key>
<string />
<key>quitURLConfirm</key>
<true />
<key>restartExamURL</key>
<string />
<key>restartExamUseStartURL</key>
<false />
<key>restartExamText</key>
<string />
<key>restartExamPasswordProtected</key>
<true />
<key>additionalResources</key>
<array></array>
<key>monitorProcesses</key>
<true />
<key>allowSwitchToApplications</key>
<false />
<key>allowFlashFullscreen</key>
<false />
<key>permittedProcesses</key>
<array>
<dict>
<key>active</key>
<true />
<key>autostart</key>
<true />
<key>iconInTaskbar</key>
<true />
<key>runInBackground</key>
<false />
<key>allowUserToChooseApp</key>
<false />
<key>strongKill</key>
<false />
<key>os</key>
<integer>1</integer>
<key>title</key>
<string>SEB</string>
<key>description</key>
<string />
<key>executable</key>
<string>firefox.exe</string>
<key>originalName</key>
<string>firefox.exe</string>
<key>path</key>
<string>../xulrunner/</string>
<key>identifier</key>
<string>Firefox</string>
<key>windowHandlingProcess</key>
<string />
<key>arguments</key>
<array></array>
</dict>
</array>
<key>prohibitedProcesses</key>
<array>
<dict>
<key>active</key>
<true />
<key>currentUser</key>
<true />
<key>strongKill</key>
<false />
<key>os</key>
<integer>1</integer>
<key>executable</key>
<string>join.me</string>
<key>originalName</key>
<string>join.me</string>
<key>description</key>
<string />
<key>identifier</key>
<string />
<key>windowHandlingProcess</key>
<string />
<key>user</key>
<string />
</dict>
<dict>
<key>active</key>
<true />
<key>currentUser</key>
<true />
<key>strongKill</key>
<false />
<key>os</key>
<integer>1</integer>
<key>executable</key>
<string>RPCSuite</string>
<key>originalName</key>
<string>RPCSuite</string>
<key>description</key>
<string />
<key>identifier</key>
<string />
<key>windowHandlingProcess</key>
<string />
<key>user</key>
<string />
</dict>
<dict>
<key>active</key>
<true />
<key>currentUser</key>
<true />
<key>strongKill</key>
<false />
<key>os</key>
<integer>1</integer>
<key>executable</key>
<string>RPCService</string>
<key>originalName</key>
<string>RPCService</string>
<key>description</key>
<string />
<key>identifier</key>
<string />
<key>windowHandlingProcess</key>
<string />
<key>user</key>
<string />
</dict>
<dict>
<key>active</key>
<true />
<key>currentUser</key>
<true />
<key>strongKill</key>
<false />
<key>os</key>
<integer>1</integer>
<key>executable</key>
<string>RemotePCDesktop</string>
<key>originalName</key>
<string>RemotePCDesktop</string>
<key>description</key>
<string />
<key>identifier</key>
<string />
<key>windowHandlingProcess</key>
<string />
<key>user</key>
<string />
</dict>
<dict>
<key>active</key>
<true />
<key>currentUser</key>
<true />
<key>strongKill</key>
<false />
<key>os</key>
<integer>1</integer>
<key>executable</key>
<string>beamyourscreen-host</string>
<key>originalName</key>
<string>beamyourscreen-host</string>
<key>description</key>
<string />
<key>identifier</key>
<string />
<key>windowHandlingProcess</key>
<string />
<key>user</key>
<string />
</dict>
<dict>
<key>active</key>
<true />
<key>currentUser</key>
<true />
<key>strongKill</key>
<false />
<key>os</key>
<integer>1</integer>
<key>executable</key>
<string>AeroAdmin</string>
<key>originalName</key>
<string>AeroAdmin</string>
<key>description</key>
<string />
<key>identifier</key>
<string />
<key>windowHandlingProcess</key>
<string />
<key>user</key>
<string />
</dict>
<dict>
<key>active</key>
<true />
<key>currentUser</key>
<true />
<key>strongKill</key>
<false />
<key>os</key>
<integer>1</integer>
<key>executable</key>
<string>Mikogo-host</string>
<key>originalName</key>
<string>Mikogo-host</string>
<key>description</key>
<string />
<key>identifier</key>
<string />
<key>windowHandlingProcess</key>
<string />
<key>user</key>
<string />
</dict>
<dict>
<key>active</key>
<true />
<key>currentUser</key>
<true />
<key>strongKill</key>
<false />
<key>os</key>
<integer>1</integer>
<key>executable</key>
<string>chromoting</string>
<key>originalName</key>
<string>chromoting</string>
<key>description</key>
<string />
<key>identifier</key>
<string />
<key>windowHandlingProcess</key>
<string />
<key>user</key>
<string />
</dict>
<dict>
<key>active</key>
<true />
<key>currentUser</key>
<true />
<key>strongKill</key>
<false />
<key>os</key>
<integer>1</integer>
<key>executable</key>
<string>vncserverui</string>
<key>originalName</key>
<string>vncserverui</string>
<key>description</key>
<string />
<key>identifier</key>
<string />
<key>windowHandlingProcess</key>
<string />
<key>user</key>
<string />
</dict>
<dict>
<key>active</key>
<true />
<key>currentUser</key>
<true />
<key>strongKill</key>
<false />
<key>os</key>
<integer>1</integer>
<key>executable</key>
<string>vncviewer</string>
<key>originalName</key>
<string>vncviewer</string>
<key>description</key>
<string />
<key>identifier</key>
<string />
<key>windowHandlingProcess</key>
<string />
<key>user</key>
<string />
</dict>
<dict>
<key>active</key>
<true />
<key>currentUser</key>
<true />
<key>strongKill</key>
<false />
<key>os</key>
<integer>1</integer>
<key>executable</key>
<string>vncserver</string>
<key>originalName</key>
<string>vncserver</string>
<key>description</key>
<string />
<key>identifier</key>
<string />
<key>windowHandlingProcess</key>
<string />
<key>user</key>
<string />
</dict>
<dict>
<key>active</key>
<true />
<key>currentUser</key>
<true />
<key>strongKill</key>
<false />
<key>os</key>
<integer>1</integer>
<key>executable</key>
<string>TeamViewer</string>
<key>originalName</key>
<string>TeamViewer</string>
<key>description</key>
<string />
<key>identifier</key>
<string />
<key>windowHandlingProcess</key>
<string />
<key>user</key>
<string />
</dict>
<dict>
<key>active</key>
<true />
<key>currentUser</key>
<true />
<key>strongKill</key>
<false />
<key>os</key>
<integer>1</integer>
<key>executable</key>
<string>GotoMeetingWinStore</string>
<key>originalName</key>
<string>GotoMeetingWinStore</string>
<key>description</key>
<string />
<key>identifier</key>
<string />
<key>windowHandlingProcess</key>
<string />
<key>user</key>
<string />
</dict>
<dict>
<key>active</key>
<true />
<key>currentUser</key>
<true />
<key>strongKill</key>
<false />
<key>os</key>
<integer>1</integer>
<key>executable</key>
<string>g2mcomm.exe</string>
<key>originalName</key>
<string>g2mcomm.exe</string>
<key>description</key>
<string />
<key>identifier</key>
<string />
<key>windowHandlingProcess</key>
<string />
<key>user</key>
<string />
</dict>
<dict>
<key>active</key>
<true />
<key>currentUser</key>
<true />
<key>strongKill</key>
<false />
<key>os</key>
<integer>1</integer>
<key>executable</key>
<string>SkypeHost</string>
<key>originalName</key>
<string>SkypeHost</string>
<key>description</key>
<string />
<key>identifier</key>
<string />
<key>windowHandlingProcess</key>
<string />
<key>user</key>
<string />
</dict>
<dict>
<key>active</key>
<true />
<key>currentUser</key>
<true />
<key>strongKill</key>
<false />
<key>os</key>
<integer>1</integer>
<key>executable</key>
<string>Skype</string>
<key>originalName</key>
<string>Skype</string>
<key>description</key>
<string />
<key>identifier</key>
<string />
<key>windowHandlingProcess</key>
<string />
<key>user</key>
<string />
</dict>
</array>
<key>enableURLFilter</key>
<false />
<key>enableURLContentFilter</key>
<false />
<key>URLFilterRules</key>
<array></array>
<key>URLFilterEnable</key>
<false />
<key>URLFilterEnableContentFilter</key>
<false />
<key>blacklistURLFilter</key>
<string />
<key>whitelistURLFilter</key>
<string />
<key>urlFilterTrustedContent</key>
<true />
<key>urlFilterRegex</key>
<true />
<key>embeddedCertificates</key>
<array></array>
<key>pinEmbeddedCertificates</key>
<false />
<key>proxySettingsPolicy</key>
<integer>0</integer>
<key>proxies</key>
<dict>
<key>ExceptionsList</key>
<array></array>
<key>ExcludeSimpleHostnames</key>
<false />
<key>AutoDiscoveryEnabled</key>
<false />
<key>AutoConfigurationEnabled</key>
<false />
<key>AutoConfigurationJavaScript</key>
<string />
<key>AutoConfigurationURL</key>
<string />
<key>FTPPassive</key>
<true />
<key>HTTPEnable</key>
<false />
<key>HTTPPort</key>
<integer>80</integer>
<key>HTTPProxy</key>
<string />
<key>HTTPRequiresPassword</key>
<false />
<key>HTTPUsername</key>
<string />
<key>HTTPPassword</key>
<string />
<key>HTTPSEnable</key>
<false />
<key>HTTPSPort</key>
<integer>443</integer>
<key>HTTPSProxy</key>
<string />
<key>HTTPSRequiresPassword</key>
<false />
<key>HTTPSUsername</key>
<string />
<key>HTTPSPassword</key>
<string />
<key>FTPEnable</key>
<false />
<key>FTPPort</key>
<integer>21</integer>
<key>FTPProxy</key>
<string />
<key>FTPRequiresPassword</key>
<false />
<key>FTPUsername</key>
<string />
<key>FTPPassword</key>
<string />
<key>SOCKSEnable</key>
<false />
<key>SOCKSPort</key>
<integer>1080</integer>
<key>SOCKSProxy</key>
<string />
<key>SOCKSRequiresPassword</key>
<false />
<key>SOCKSUsername</key>
<string />
<key>SOCKSPassword</key>
<string />
<key>RTSPEnable</key>
<false />
<key>RTSPPort</key>
<integer>554</integer>
<key>RTSPProxy</key>
<string />
<key>RTSPRequiresPassword</key>
<false />
<key>RTSPUsername</key>
<string />
<key>RTSPPassword</key>
<string />
</dict>
<key>sebServicePolicy</key>
<integer>1</integer>
<key>allowVirtualMachine</key>
<false />
<key>allowScreenSharing</key>
<false />
<key>enablePrivateClipboard</key>
<true />
<key>createNewDesktop</key>
<true />
<key>killExplorerShell</key>
<false />
<key>enableLogging</key>
<true />
<key>logDirectoryOSX</key>
<string>~/Documents</string>
<key>logDirectoryWin</key>
<string />
<key>allowWlan</key>
<false />
<key>lockOnMessageSocketClose</key>
<true />
<key>minMacOSVersion</key>
<integer>4</integer>
<key>enableAppSwitcherCheck</key>
<true />
<key>forceAppFolderInstall</key>
<true />
<key>allowUserAppFolderInstall</key>
<false />
<key>allowSiri</key>
<false />
<key>allowDictation</key>
<false />
<key>detectStoppedProcess</key>
<true />
<key>allowDisplayMirroring</key>
<false />
<key>allowedDisplaysMaxNumber</key>
<integer>1</integer>
<key>allowedDisplayBuiltin</key>
<true />
<key>insideSebEnableSwitchUser</key>
<false />
<key>insideSebEnableLockThisComputer</key>
<false />
<key>insideSebEnableChangeAPassword</key>
<false />
<key>insideSebEnableStartTaskManager</key>
<false />
<key>insideSebEnableLogOff</key>
<false />
<key>insideSebEnableShutDown</key>
<false />
<key>insideSebEnableEaseOfAccess</key>
<false />
<key>insideSebEnableVmWareClientShade</key>
<false />
<key>insideSebEnableEnableNetworkConnectionSelector</key>
<false />
<key>hookKeys</key>
<true />
<key>enableEsc</key>
<true />
<key>enableCtrlEsc</key>
<false />
<key>enableAltEsc</key>
<false />
<key>enableAltTab</key>
<true />
<key>enableAltF4</key>
<false />
<key>enableStartMenu</key>
<false />
<key>enableRightMouse</key>
<true />
<key>enablePrintScreen</key>
<false />
<key>enableAltMouseWheel</key>
<false />
<key>enableF1</key>
<true />
<key>enableF2</key>
<true />
<key>enableF3</key>
<true />
<key>enableF4</key>
<true />
<key>enableF5</key>
<true />
<key>enableF6</key>
<true />
<key>enableF7</key>
<true />
<key>enableF8</key>
<true />
<key>enableF9</key>
<true />
<key>enableF10</key>
<true />
<key>enableF11</key>
<true />
<key>enableF12</key>
<true />
</dict>
</plist>

View File

@@ -0,0 +1,17 @@
using System.Reflection;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("SafeExamBrowser.Configuration.UnitTests")]
[assembly: AssemblyDescription("Safe Exam Browser")]
[assembly: AssemblyCompany("ETH Zürich")]
[assembly: AssemblyProduct("SafeExamBrowser.Configuration.UnitTests")]
[assembly: AssemblyCopyright("Copyright © 2024 ETH Zürich, IT Services")]
[assembly: ComVisible(false)]
[assembly: Guid("9cdd03e7-ed65-409f-8c07-bd6f633a0170")]
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0.0")]

View File

@@ -0,0 +1,208 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\MSTest.TestAdapter.3.2.2\build\net462\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.3.2.2\build\net462\MSTest.TestAdapter.props')" />
<Import Project="..\packages\Microsoft.Testing.Extensions.Telemetry.1.0.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props" Condition="Exists('..\packages\Microsoft.Testing.Extensions.Telemetry.1.0.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props')" />
<Import Project="..\packages\Microsoft.Testing.Platform.MSBuild.1.0.2\build\netstandard2.0\Microsoft.Testing.Platform.MSBuild.props" Condition="Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.0.2\build\netstandard2.0\Microsoft.Testing.Platform.MSBuild.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{9CDD03E7-ED65-409F-8C07-BD6F633A0170}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>SafeExamBrowser.Configuration.UnitTests</RootNamespace>
<AssemblyName>SafeExamBrowser.Configuration.UnitTests</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">15.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
<IsCodedUITest>False</IsCodedUITest>
<TestProjectType>UnitTest</TestProjectType>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="Castle.Core, Version=5.0.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
<HintPath>..\packages\Castle.Core.5.1.1\lib\net462\Castle.Core.dll</HintPath>
</Reference>
<Reference Include="Microsoft.ApplicationInsights, Version=2.22.0.997, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.ApplicationInsights.2.22.0\lib\net46\Microsoft.ApplicationInsights.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.Testing.Extensions.Telemetry, Version=1.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Testing.Extensions.Telemetry.1.0.2\lib\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Testing.Extensions.TrxReport.Abstractions, Version=1.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Testing.Extensions.TrxReport.Abstractions.1.0.2\lib\netstandard2.0\Microsoft.Testing.Extensions.TrxReport.Abstractions.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Testing.Extensions.VSTestBridge, Version=1.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Testing.Extensions.VSTestBridge.1.0.2\lib\netstandard2.0\Microsoft.Testing.Extensions.VSTestBridge.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Testing.Platform, Version=1.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Testing.Platform.MSBuild.1.0.2\lib\netstandard2.0\Microsoft.Testing.Platform.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Testing.Platform.MSBuild, Version=1.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Testing.Platform.MSBuild.1.0.2\lib\netstandard2.0\Microsoft.Testing.Platform.MSBuild.dll</HintPath>
</Reference>
<Reference Include="Microsoft.TestPlatform.CoreUtilities, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.9.0\lib\net462\Microsoft.TestPlatform.CoreUtilities.dll</HintPath>
</Reference>
<Reference Include="Microsoft.TestPlatform.PlatformAbstractions, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.9.0\lib\net462\Microsoft.TestPlatform.PlatformAbstractions.dll</HintPath>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestPlatform.ObjectModel, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.9.0\lib\net462\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll</HintPath>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\MSTest.TestFramework.3.2.2\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\MSTest.TestFramework.3.2.2\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath>
</Reference>
<Reference Include="Moq, Version=4.20.70.0, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
<HintPath>..\packages\Moq.4.20.70\lib\net462\Moq.dll</HintPath>
</Reference>
<Reference Include="NuGet.Frameworks, Version=6.9.1.3, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\NuGet.Frameworks.6.9.1\lib\net472\NuGet.Frameworks.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Buffers, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll</HintPath>
</Reference>
<Reference Include="System.Collections.Immutable, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Collections.Immutable.8.0.0\lib\net462\System.Collections.Immutable.dll</HintPath>
</Reference>
<Reference Include="System.Configuration" />
<Reference Include="System.Core" />
<Reference Include="System.Diagnostics.DiagnosticSource, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Diagnostics.DiagnosticSource.8.0.0\lib\net462\System.Diagnostics.DiagnosticSource.dll</HintPath>
</Reference>
<Reference Include="System.Memory, Version=4.0.1.2, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Memory.4.5.5\lib\net461\System.Memory.dll</HintPath>
</Reference>
<Reference Include="System.Net.Http" />
<Reference Include="System.Numerics" />
<Reference Include="System.Numerics.Vectors, Version=4.1.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
</Reference>
<Reference Include="System.Reflection.Metadata, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Reflection.Metadata.8.0.0\lib\net462\System.Reflection.Metadata.dll</HintPath>
</Reference>
<Reference Include="System.Runtime" />
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Threading.Tasks.Extensions, Version=4.2.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll</HintPath>
</Reference>
<Reference Include="System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll</HintPath>
</Reference>
<Reference Include="System.XML" />
<Reference Include="System.Xml.Linq" />
</ItemGroup>
<ItemGroup>
<Compile Include="ConfigurationData\DataProcessorTests.cs" />
<Compile Include="ConfigurationRepositoryTests.cs" />
<Compile Include="Cryptography\HashAlgorithmTests.cs" />
<Compile Include="Cryptography\PasswordEncryptionTests.cs" />
<Compile Include="Cryptography\PublicKeyEncryptionTests.cs" />
<Compile Include="Cryptography\PublicKeySymmetricEncryptionTests.cs" />
<Compile Include="Cryptography\KeyGeneratorTests.cs" />
<Compile Include="DataCompression\GZipCompressorTests.cs" />
<Compile Include="DataFormats\BinaryParserTests.cs" />
<Compile Include="DataFormats\BinarySerializerTests.cs" />
<Compile Include="DataFormats\XmlParserTests.cs" />
<Compile Include="DataFormats\XmlSerializerTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SubStreamTests.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config">
<SubType>Designer</SubType>
</None>
<EmbeddedResource Include="DataFormats\XmlTestSettings.xml" />
<EmbeddedResource Include="UnitTestCert.pfx" />
<EmbeddedResource Include="ConfigurationData\TestDictionary1.bin" />
<EmbeddedResource Include="ConfigurationData\TestDictionary2.bin" />
<EmbeddedResource Include="ConfigurationData\TestDictionary3.bin" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SafeExamBrowser.Configuration.Contracts\SafeExamBrowser.Configuration.Contracts.csproj">
<Project>{7d74555e-63e1-4c46-bd0a-8580552368c8}</Project>
<Name>SafeExamBrowser.Configuration.Contracts</Name>
</ProjectReference>
<ProjectReference Include="..\SafeExamBrowser.Configuration\SafeExamBrowser.Configuration.csproj">
<Project>{c388c4dd-a159-457d-af92-89f7ad185109}</Project>
<Name>SafeExamBrowser.Configuration</Name>
</ProjectReference>
<ProjectReference Include="..\SafeExamBrowser.Logging.Contracts\SafeExamBrowser.Logging.Contracts.csproj">
<Project>{64ea30fb-11d4-436a-9c2b-88566285363e}</Project>
<Name>SafeExamBrowser.Logging.Contracts</Name>
</ProjectReference>
<ProjectReference Include="..\SafeExamBrowser.Settings\SafeExamBrowser.Settings.csproj">
<Project>{30b2d907-5861-4f39-abad-c4abf1b3470e}</Project>
<Name>SafeExamBrowser.Settings</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="DataFormats\XmlTestData.xml">
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.0.2\build\netstandard2.0\Microsoft.Testing.Platform.MSBuild.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.MSBuild.1.0.2\build\netstandard2.0\Microsoft.Testing.Platform.MSBuild.props'))" />
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.0.2\build\netstandard2.0\Microsoft.Testing.Platform.MSBuild.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.MSBuild.1.0.2\build\netstandard2.0\Microsoft.Testing.Platform.MSBuild.targets'))" />
<Error Condition="!Exists('..\packages\Microsoft.Testing.Extensions.Telemetry.1.0.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Extensions.Telemetry.1.0.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props'))" />
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.3.2.2\build\net462\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.3.2.2\build\net462\MSTest.TestAdapter.props'))" />
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.3.2.2\build\net462\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.3.2.2\build\net462\MSTest.TestAdapter.targets'))" />
</Target>
<Import Project="..\packages\Microsoft.Testing.Platform.MSBuild.1.0.2\build\netstandard2.0\Microsoft.Testing.Platform.MSBuild.targets" Condition="Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.0.2\build\netstandard2.0\Microsoft.Testing.Platform.MSBuild.targets')" />
<Import Project="..\packages\MSTest.TestAdapter.3.2.2\build\net462\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.3.2.2\build\net462\MSTest.TestAdapter.targets')" />
</Project>

View File

@@ -0,0 +1,187 @@
/*
* 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.IO;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
namespace SafeExamBrowser.Configuration.UnitTests
{
[TestClass]
public class SubStreamTests
{
private Mock<Stream> stream;
[TestInitialize]
public void Initialize()
{
stream = new Mock<Stream>();
stream.SetupGet(s => s.CanRead).Returns(true);
stream.SetupGet(s => s.CanSeek).Returns(true);
stream.SetupGet(s => s.Length).Returns(1000);
}
[TestMethod]
public void MustSetPropertiesCorrectly()
{
var sut = new SubStream(stream.Object, 100, 200);
Assert.IsTrue(sut.CanRead);
Assert.IsTrue(sut.CanSeek);
Assert.IsFalse(sut.CanWrite);
Assert.AreEqual(200, sut.Length);
Assert.AreEqual(0, sut.Position);
}
[TestMethod]
public void MustReadCorrectly()
{
var position = 750L;
var sut = new SubStream(stream.Object, 100, 200);
stream.SetupGet(s => s.Position).Returns(position);
stream.SetupSet(s => s.Position = It.IsAny<long>()).Callback<long>(p => position = p);
stream.Setup(s => s.Read(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<int>())).Returns<byte[], int, int>((a, o, c) => c);
sut.Position = 50;
var bytesRead = sut.Read(new byte[25], 0, 25);
stream.Verify(s => s.Read(It.IsAny<byte[]>(), 0, 25), Times.Once);
Assert.AreEqual(25, bytesRead);
Assert.AreEqual(75, sut.Position);
Assert.AreEqual(750, position);
sut.Position = 150;
bytesRead = sut.Read(new byte[75], 0, 75);
stream.Verify(s => s.Read(It.IsAny<byte[]>(), 0, 50), Times.Once);
Assert.AreEqual(50, bytesRead);
Assert.AreEqual(200, sut.Position);
Assert.AreEqual(750, position);
}
[TestMethod]
public void MustNotReadOutsideOfBounds()
{
var sut = new SubStream(stream.Object, 100, 200);
sut.Position = -1;
var bytesRead = sut.Read(new byte[0], 0, 0);
Assert.AreEqual(0, bytesRead);
stream.Verify(s => s.ReadByte(), Times.Never);
stream.Verify(s => s.Read(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<int>()), Times.Never);
sut.Position = 500;
bytesRead = sut.Read(new byte[0], 0, 0);
Assert.AreEqual(0, bytesRead);
stream.Verify(s => s.ReadByte(), Times.Never);
stream.Verify(s => s.Read(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<int>()), Times.Never);
}
[TestMethod]
public void MustReadByteCorrectly()
{
var sut = new SubStream(stream.Object, 100, 200);
sut.Position = -100;
Assert.AreEqual(-1, sut.ReadByte());
sut.Position = 200;
Assert.AreEqual(-1, sut.ReadByte());
sut.Position = 25;
sut.ReadByte();
stream.Verify(s => s.Read(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<int>()), Times.Once);
}
[TestMethod]
public void MustSeekCorrectly()
{
var sut = new SubStream(stream.Object, 100, 200);
sut.Seek(10, SeekOrigin.Begin);
Assert.AreEqual(10, sut.Position);
sut.Seek(15, SeekOrigin.Current);
Assert.AreEqual(25, sut.Position);
sut.Seek(-5, SeekOrigin.Current);
Assert.AreEqual(20, sut.Position);
sut.Seek(-50, SeekOrigin.End);
Assert.AreEqual(150, sut.Position);
sut.Seek(10, SeekOrigin.End);
Assert.AreEqual(210, sut.Position);
sut.Seek(-10, SeekOrigin.Begin);
Assert.AreEqual(-10, sut.Position);
}
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void MustNotAllowNonReadableStream()
{
stream.SetupGet(s => s.CanRead).Returns(false);
new SubStream(stream.Object, 0, 0);
}
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void MustNotAllowNonSeekableStream()
{
stream.SetupGet(s => s.CanSeek).Returns(false);
new SubStream(stream.Object, 0, 0);
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void MustNotAllowOffsetSmallerThanZero()
{
new SubStream(stream.Object, -1, 100);
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void MustNotAllowLengthSmallerThanOne()
{
new SubStream(stream.Object, 100, 0);
}
[TestMethod]
[ExpectedException(typeof(NotSupportedException))]
public void MustNotSupportFlushing()
{
new SubStream(stream.Object, 100, 100).Flush();
}
[TestMethod]
[ExpectedException(typeof(NotSupportedException))]
public void MustNotSupportChangingLength()
{
new SubStream(stream.Object, 100, 100).SetLength(100);
}
[TestMethod]
[ExpectedException(typeof(NotSupportedException))]
public void MustNotSupportWriting()
{
new SubStream(stream.Object, 100, 100).Write(new byte[0], 0, 0);
}
}
}

View File

@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Threading.Tasks.Extensions" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.0.1" newVersion="4.2.0.1" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="NuGet.Frameworks" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.11.3.1" newVersion="5.11.3.1" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.ApplicationInsights" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.22.0.997" newVersion="2.22.0.997" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Diagnostics.DiagnosticSource" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.2" newVersion="4.0.1.2" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Reflection.Metadata" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" /></startup></configuration>

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Castle.Core" version="5.1.1" targetFramework="net48" />
<package id="Microsoft.ApplicationInsights" version="2.22.0" targetFramework="net48" />
<package id="Microsoft.Testing.Extensions.Telemetry" version="1.0.2" targetFramework="net48" />
<package id="Microsoft.Testing.Extensions.TrxReport.Abstractions" version="1.0.2" targetFramework="net48" />
<package id="Microsoft.Testing.Extensions.VSTestBridge" version="1.0.2" targetFramework="net48" />
<package id="Microsoft.Testing.Platform" version="1.0.2" targetFramework="net48" />
<package id="Microsoft.Testing.Platform.MSBuild" version="1.0.2" targetFramework="net48" />
<package id="Microsoft.TestPlatform.ObjectModel" version="17.9.0" targetFramework="net48" />
<package id="Moq" version="4.20.70" targetFramework="net48" />
<package id="MSTest.TestAdapter" version="3.2.2" targetFramework="net48" />
<package id="MSTest.TestFramework" version="3.2.2" targetFramework="net48" />
<package id="NuGet.Frameworks" version="6.9.1" targetFramework="net48" />
<package id="System.Buffers" version="4.5.1" targetFramework="net48" />
<package id="System.Collections.Immutable" version="8.0.0" targetFramework="net48" />
<package id="System.Diagnostics.DiagnosticSource" version="8.0.0" targetFramework="net48" />
<package id="System.Memory" version="4.5.5" targetFramework="net48" />
<package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net48" />
<package id="System.Reflection.Metadata" version="8.0.0" targetFramework="net48" />
<package id="System.Runtime.CompilerServices.Unsafe" version="6.0.0" targetFramework="net48" />
<package id="System.Threading.Tasks.Extensions" version="4.5.4" targetFramework="net48" />
<package id="System.ValueTuple" version="4.5.0" targetFramework="net48" />
</packages>