SEBWIN-183: Refactored I18n components and extended their unit tests.

This commit is contained in:
dbuechel 2017-10-10 10:17:28 +02:00
parent 93e84b1120
commit 8be4fdda29
17 changed files with 262 additions and 25 deletions

View file

@ -29,7 +29,7 @@ namespace SafeExamBrowser.Configuration
public bool AllowApplicationLog => true;
public bool AllowKeyboardLayout => true;
public string AppDataFolderName => "SafeExamBrowser";
public string AppDataFolderName => nameof(SafeExamBrowser);
public string ApplicationLogFile
{

View file

@ -10,9 +10,14 @@ namespace SafeExamBrowser.Contracts.I18n
{
public interface IText
{
/// <summary>
/// Initializes the text module, e.g. loads text data from the specified text resource.
/// </summary>
void Initialize(ITextResource resource);
/// <summary>
/// Gets the text associated with the specified key. If the key was not found, a default text indicating
/// that the given key is not configured shall be returned.
/// that the given key is not configured will be returned.
/// </summary>
string Get(TextKey key);
}

View file

@ -13,7 +13,7 @@ namespace SafeExamBrowser.Contracts.I18n
public interface ITextResource
{
/// <summary>
/// Loads all text data from a resource.
/// Loads all text data from a resource. Throws an exception if the data could not be loaded, e.g. due to a data format error.
/// </summary>
IDictionary<TextKey, string> LoadText();
}

View file

@ -11,6 +11,7 @@ using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Core.I18n;
namespace SafeExamBrowser.Core.UnitTests.I18n
@ -18,14 +19,18 @@ namespace SafeExamBrowser.Core.UnitTests.I18n
[TestClass]
public class TextTests
{
private Mock<ILogger> loggerMock;
[TestInitialize]
public void Initialize()
{
loggerMock = new Mock<ILogger>();
}
[TestMethod]
public void MustNeverReturnNull()
{
var resource = new Mock<ITextResource>();
var sut = new Text(resource.Object);
resource.Setup(r => r.LoadText()).Returns<IDictionary<TextKey, string>>(null);
var sut = new Text(loggerMock.Object);
var text = sut.Get((TextKey)(-1));
Assert.IsNotNull(text);
@ -35,7 +40,39 @@ namespace SafeExamBrowser.Core.UnitTests.I18n
[ExpectedException(typeof(ArgumentNullException))]
public void MustNotAllowNullResource()
{
new Text(null);
var sut = new Text(loggerMock.Object);
sut.Initialize(null);
}
[TestMethod]
public void MustNotFailWhenGettingNullFromResource()
{
var resource = new Mock<ITextResource>();
var sut = new Text(loggerMock.Object);
resource.Setup(r => r.LoadText()).Returns<IDictionary<TextKey, string>>(null);
sut.Initialize(resource.Object);
var text = sut.Get((TextKey)(-1));
Assert.IsNotNull(text);
}
[TestMethod]
public void MustNotFailWhenResourceThrowsException()
{
var resource = new Mock<ITextResource>();
var sut = new Text(loggerMock.Object);
resource.Setup(r => r.LoadText()).Throws<Exception>();
sut.Initialize(resource.Object);
var text = sut.Get((TextKey)(-1));
loggerMock.Verify(l => l.Error(It.IsAny<string>(), It.IsAny<Exception>()), Times.AtLeastOnce);
Assert.IsNotNull(text);
}
}
}

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<Blubb>
<ThisTagIsNotSupported>Some random text here...</ThisTagIsNotSupported>
<NeitherIsThisOne>Bappa-dee boopa-dee</NeitherIsThisOne>
<AnEmptyTag />
</Blubb>

View file

@ -0,0 +1,13 @@
<?xyzml version="1.0" encoding="utf-8" ?>
<Blubb>
Some random text here
<LogWindow_Title\>>
<Version *+()=?!
<xml>
Data
</xml>
</Blubb>
<Blobb>
<Blabb>
<Bappa-dee-boopa-dee>/Bappa-dee-boopa-dee>
</Blabb>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8" ?>
<Text>
<LogWindow_Title>Application Log</LogWindow_Title>
<Version>Version</Version>
</Text>

View file

@ -0,0 +1,75 @@
/*
* Copyright (c) 2017 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
using System.IO;
using System.Reflection;
using System.Xml;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Core.I18n;
namespace SafeExamBrowser.Core.UnitTests.I18n
{
[TestClass]
public class XmlTextResourceTests
{
[TestMethod]
public void MustCorrectlyLoadData()
{
var location = Assembly.GetAssembly(typeof(XmlTextResourceTests)).Location;
var path = Path.GetDirectoryName(location) + $@"\{nameof(I18n)}\Text_Valid.xml";
var sut = new XmlTextResource(path);
var text = sut.LoadText();
Assert.IsNotNull(text);
Assert.IsTrue(text.Count == 2);
Assert.AreEqual("Application Log", text[TextKey.LogWindow_Title]);
Assert.AreEqual("Version", text[TextKey.Version]);
}
[TestMethod]
[ExpectedException(typeof(XmlException))]
public void MustFailWithInvalidData()
{
var location = Assembly.GetAssembly(typeof(XmlTextResourceTests)).Location;
var path = Path.GetDirectoryName(location) + $@"\{nameof(I18n)}\Text_Invalid.txt";
var sut = new XmlTextResource(path);
sut.LoadText();
}
[TestMethod]
public void MustNeverReturnNull()
{
var location = Assembly.GetAssembly(typeof(XmlTextResourceTests)).Location;
var path = Path.GetDirectoryName(location) + $@"\{nameof(I18n)}\Text_Incompatible.xml";
var sut = new XmlTextResource(path);
var text = sut.LoadText();
Assert.IsNotNull(text);
Assert.IsTrue(text.Count == 0);
}
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void MustNotAcceptInvalidPath()
{
new XmlTextResource("This is not a valid path");
}
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void MustNotAcceptNullAsPath()
{
new XmlTextResource(null);
}
}
}

View file

@ -69,12 +69,14 @@
<HintPath>..\packages\Moq.4.7.63\lib\net45\Moq.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Behaviour\RuntimeControllerTests.cs" />
<Compile Include="Behaviour\StartupControllerTests.cs" />
<Compile Include="Behaviour\ShutdownControllerTests.cs" />
<Compile Include="I18n\TextTests.cs" />
<Compile Include="I18n\XmlTextResourceTests.cs" />
<Compile Include="Logging\LoggerTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
@ -91,6 +93,17 @@
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Content Include="I18n\Text_Invalid.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="I18n\Text_Incompatible.xml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="I18n\Text_Valid.xml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</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">

View file

@ -0,0 +1,49 @@
/*
* Copyright (c) 2017 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System.Globalization;
using System.IO;
using System.Reflection;
using SafeExamBrowser.Contracts.Behaviour;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Core.I18n;
namespace SafeExamBrowser.Core.Behaviour.Operations
{
public class I18nOperation : IOperation
{
private ILogger logger;
private IText text;
public ISplashScreen SplashScreen { private get; set; }
public I18nOperation(ILogger logger, IText text)
{
this.logger = logger;
this.text = text;
}
public void Perform()
{
logger.Info($"Loading default text data (the currently active culture is '{CultureInfo.CurrentCulture.Name}')...");
var location = Assembly.GetAssembly(typeof(XmlTextResource)).Location;
var path = Path.GetDirectoryName(location) + $@"\{nameof(I18n)}\Text.xml";
var textResource = new XmlTextResource(path);
text.Initialize(textResource);
}
public void Revert()
{
// Nothing to do here...
}
}
}

View file

@ -45,6 +45,7 @@ namespace SafeExamBrowser.Core.Behaviour
public void Stop()
{
displayMonitor.DisplayChanged -= DisplayMonitor_DisplaySettingsChanged;
processMonitor.ExplorerStarted -= ProcessMonitor_ExplorerStarted;
windowMonitor.WindowChanged -= WindowMonitor_WindowChanged;
}

View file

@ -9,26 +9,40 @@
using System;
using System.Collections.Generic;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
namespace SafeExamBrowser.Core.I18n
{
public class Text : IText
{
private readonly IDictionary<TextKey, string> cache;
private IDictionary<TextKey, string> cache = new Dictionary<TextKey, string>();
private ILogger logger;
public Text(ITextResource resource)
public Text(ILogger logger)
{
if (resource == null)
{
throw new ArgumentNullException(nameof(resource));
}
cache = resource.LoadText() ?? new Dictionary<TextKey, string>();
this.logger = logger;
}
public string Get(TextKey key)
{
return cache.ContainsKey(key) ? cache[key] : $"Could not find string for key '{key}'!";
}
public void Initialize(ITextResource resource)
{
if (resource == null)
{
throw new ArgumentNullException(nameof(resource));
}
try
{
cache = resource.LoadText() ?? new Dictionary<TextKey, string>();
}
catch (Exception e)
{
logger.Error("Failed to load text data from provided resource!", e);
}
}
}
}

View file

@ -9,7 +9,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Xml.Linq;
using SafeExamBrowser.Contracts.I18n;
@ -17,10 +16,30 @@ namespace SafeExamBrowser.Core.I18n
{
public class XmlTextResource : ITextResource
{
private string path;
/// <summary>
/// Initializes a new text resource for an XML file located at the specified path.
/// </summary>
/// <exception cref="System.ArgumentException">If the specifed file does not exist.</exception>
/// <exception cref="System.ArgumentNullException">If the given path is null.</exception>
public XmlTextResource(string path)
{
if (path == null)
{
throw new ArgumentNullException(nameof(path));
}
if (!File.Exists(path))
{
throw new ArgumentException("The specified file does not exist!");
}
this.path = path;
}
public IDictionary<TextKey, string> LoadText()
{
var assembly = Assembly.GetAssembly(typeof(XmlTextResource)).Location;
var path = Path.GetDirectoryName(assembly) + $@"\{nameof(I18n)}\Text.xml";
var xml = XDocument.Load(path);
var text = new Dictionary<TextKey, string>();

View file

@ -55,6 +55,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Behaviour\Operations\ClipboardOperation.cs" />
<Compile Include="Behaviour\Operations\I18nOperation.cs" />
<Compile Include="Behaviour\Operations\KeyboardInterceptorOperation.cs" />
<Compile Include="Behaviour\Operations\MouseInterceptorOperation.cs" />
<Compile Include="Behaviour\RuntimeController.cs" />

View file

@ -73,7 +73,7 @@ namespace SafeExamBrowser.UserInterface.Classic
private void InitializeSplashScreen()
{
InfoTextBlock.Inlines.Add(new Run($"{text.Get(TextKey.Version)} {settings.ProgramVersion}") { FontStyle = FontStyles.Italic });
InfoTextBlock.Inlines.Add(new Run($"Version {settings.ProgramVersion}") { FontStyle = FontStyles.Italic });
InfoTextBlock.Inlines.Add(new LineBreak());
InfoTextBlock.Inlines.Add(new LineBreak());
InfoTextBlock.Inlines.Add(new Run(settings.ProgramCopyright) { FontSize = 10 });

View file

@ -73,7 +73,7 @@ namespace SafeExamBrowser.UserInterface.Windows10
private void InitializeSplashScreen()
{
InfoTextBlock.Inlines.Add(new Run($"{text.Get(TextKey.Version)} {settings.ProgramVersion}") { FontStyle = FontStyles.Italic });
InfoTextBlock.Inlines.Add(new Run($"Version {settings.ProgramVersion}") { FontStyle = FontStyles.Italic });
InfoTextBlock.Inlines.Add(new LineBreak());
InfoTextBlock.Inlines.Add(new LineBreak());
InfoTextBlock.Inlines.Add(new Run(settings.ProgramCopyright) { FontSize = 10 });

View file

@ -51,7 +51,6 @@ namespace SafeExamBrowser
private ISystemComponent<ISystemPowerSupplyControl> powerSupply;
private ISystemInfo systemInfo;
private IText text;
private ITextResource textResource;
private IUserInterfaceFactory uiFactory;
private IWindowMonitor windowMonitor;
@ -68,12 +67,11 @@ namespace SafeExamBrowser
nativeMethods = new NativeMethods();
settings = new Settings();
systemInfo = new SystemInfo();
textResource = new XmlTextResource();
uiFactory = new UserInterfaceFactory();
logger.Subscribe(new LogFileWriter(logFormatter, settings));
text = new Text(textResource);
text = new Text(logger);
Taskbar = new Taskbar(new ModuleLogger(logger, typeof(Taskbar)));
browserController = new BrowserApplicationController(settings, text, uiFactory);
displayMonitor = new DisplayMonitor(new ModuleLogger(logger, typeof(DisplayMonitor)), nativeMethods);
@ -89,6 +87,7 @@ namespace SafeExamBrowser
StartupController = new StartupController(logger, settings, systemInfo, text, uiFactory);
StartupOperations = new Queue<IOperation>();
StartupOperations.Enqueue(new I18nOperation(logger, text));
StartupOperations.Enqueue(new KeyboardInterceptorOperation(keyboardInterceptor, logger, nativeMethods));
StartupOperations.Enqueue(new WindowMonitorOperation(logger, windowMonitor));
StartupOperations.Enqueue(new ProcessMonitorOperation(logger, processMonitor));