diff --git a/SafeExamBrowser.Configuration/Settings.cs b/SafeExamBrowser.Configuration/Settings.cs
index b5cfedc6..bfed33a7 100644
--- a/SafeExamBrowser.Configuration/Settings.cs
+++ b/SafeExamBrowser.Configuration/Settings.cs
@@ -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
{
diff --git a/SafeExamBrowser.Contracts/I18n/IText.cs b/SafeExamBrowser.Contracts/I18n/IText.cs
index 47549c0c..9059e79f 100644
--- a/SafeExamBrowser.Contracts/I18n/IText.cs
+++ b/SafeExamBrowser.Contracts/I18n/IText.cs
@@ -10,9 +10,14 @@ namespace SafeExamBrowser.Contracts.I18n
{
public interface IText
{
+ ///
+ /// Initializes the text module, e.g. loads text data from the specified text resource.
+ ///
+ void Initialize(ITextResource resource);
+
///
/// 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.
///
string Get(TextKey key);
}
diff --git a/SafeExamBrowser.Contracts/I18n/ITextResource.cs b/SafeExamBrowser.Contracts/I18n/ITextResource.cs
index e8f9e098..b316fb34 100644
--- a/SafeExamBrowser.Contracts/I18n/ITextResource.cs
+++ b/SafeExamBrowser.Contracts/I18n/ITextResource.cs
@@ -13,7 +13,7 @@ namespace SafeExamBrowser.Contracts.I18n
public interface ITextResource
{
///
- /// 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.
///
IDictionary LoadText();
}
diff --git a/SafeExamBrowser.Core.UnitTests/I18n/TextTests.cs b/SafeExamBrowser.Core.UnitTests/I18n/TextTests.cs
index 8958036d..564f4afa 100644
--- a/SafeExamBrowser.Core.UnitTests/I18n/TextTests.cs
+++ b/SafeExamBrowser.Core.UnitTests/I18n/TextTests.cs
@@ -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 loggerMock;
+
+ [TestInitialize]
+ public void Initialize()
+ {
+ loggerMock = new Mock();
+ }
+
[TestMethod]
public void MustNeverReturnNull()
{
- var resource = new Mock();
- var sut = new Text(resource.Object);
-
- resource.Setup(r => r.LoadText()).Returns>(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();
+ var sut = new Text(loggerMock.Object);
+
+ resource.Setup(r => r.LoadText()).Returns>(null);
+ sut.Initialize(resource.Object);
+
+ var text = sut.Get((TextKey)(-1));
+
+ Assert.IsNotNull(text);
+ }
+
+ [TestMethod]
+ public void MustNotFailWhenResourceThrowsException()
+ {
+ var resource = new Mock();
+ var sut = new Text(loggerMock.Object);
+
+ resource.Setup(r => r.LoadText()).Throws();
+ sut.Initialize(resource.Object);
+
+ var text = sut.Get((TextKey)(-1));
+
+ loggerMock.Verify(l => l.Error(It.IsAny(), It.IsAny()), Times.AtLeastOnce);
+
+ Assert.IsNotNull(text);
}
}
}
diff --git a/SafeExamBrowser.Core.UnitTests/I18n/Text_Incompatible.xml b/SafeExamBrowser.Core.UnitTests/I18n/Text_Incompatible.xml
new file mode 100644
index 00000000..74e6710f
--- /dev/null
+++ b/SafeExamBrowser.Core.UnitTests/I18n/Text_Incompatible.xml
@@ -0,0 +1,6 @@
+
+
+ Some random text here...
+ Bappa-dee boopa-dee
+
+
\ No newline at end of file
diff --git a/SafeExamBrowser.Core.UnitTests/I18n/Text_Invalid.txt b/SafeExamBrowser.Core.UnitTests/I18n/Text_Invalid.txt
new file mode 100644
index 00000000..f83886a0
--- /dev/null
+++ b/SafeExamBrowser.Core.UnitTests/I18n/Text_Invalid.txt
@@ -0,0 +1,13 @@
+
+
+ Some random text here
+ >
+
+ Data
+
+
+
+
+ /Bappa-dee-boopa-dee>
+
diff --git a/SafeExamBrowser.Core.UnitTests/I18n/Text_Valid.xml b/SafeExamBrowser.Core.UnitTests/I18n/Text_Valid.xml
new file mode 100644
index 00000000..def6f21a
--- /dev/null
+++ b/SafeExamBrowser.Core.UnitTests/I18n/Text_Valid.xml
@@ -0,0 +1,5 @@
+
+
+ Application Log
+ Version
+
\ No newline at end of file
diff --git a/SafeExamBrowser.Core.UnitTests/I18n/XmlTextResourceTests.cs b/SafeExamBrowser.Core.UnitTests/I18n/XmlTextResourceTests.cs
new file mode 100644
index 00000000..ec24608c
--- /dev/null
+++ b/SafeExamBrowser.Core.UnitTests/I18n/XmlTextResourceTests.cs
@@ -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);
+ }
+ }
+}
diff --git a/SafeExamBrowser.Core.UnitTests/SafeExamBrowser.Core.UnitTests.csproj b/SafeExamBrowser.Core.UnitTests/SafeExamBrowser.Core.UnitTests.csproj
index 4c3726c3..f0ab8304 100644
--- a/SafeExamBrowser.Core.UnitTests/SafeExamBrowser.Core.UnitTests.csproj
+++ b/SafeExamBrowser.Core.UnitTests/SafeExamBrowser.Core.UnitTests.csproj
@@ -69,12 +69,14 @@
..\packages\Moq.4.7.63\lib\net45\Moq.dll
+
+
@@ -91,6 +93,17 @@
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
diff --git a/SafeExamBrowser.Core/Behaviour/Operations/I18nOperation.cs b/SafeExamBrowser.Core/Behaviour/Operations/I18nOperation.cs
new file mode 100644
index 00000000..510c1101
--- /dev/null
+++ b/SafeExamBrowser.Core/Behaviour/Operations/I18nOperation.cs
@@ -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...
+ }
+ }
+}
diff --git a/SafeExamBrowser.Core/Behaviour/RuntimeController.cs b/SafeExamBrowser.Core/Behaviour/RuntimeController.cs
index fc4667ba..59913a1d 100644
--- a/SafeExamBrowser.Core/Behaviour/RuntimeController.cs
+++ b/SafeExamBrowser.Core/Behaviour/RuntimeController.cs
@@ -45,6 +45,7 @@ namespace SafeExamBrowser.Core.Behaviour
public void Stop()
{
+ displayMonitor.DisplayChanged -= DisplayMonitor_DisplaySettingsChanged;
processMonitor.ExplorerStarted -= ProcessMonitor_ExplorerStarted;
windowMonitor.WindowChanged -= WindowMonitor_WindowChanged;
}
diff --git a/SafeExamBrowser.Core/I18n/Text.cs b/SafeExamBrowser.Core/I18n/Text.cs
index aa88869d..9f46ae83 100644
--- a/SafeExamBrowser.Core/I18n/Text.cs
+++ b/SafeExamBrowser.Core/I18n/Text.cs
@@ -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 cache;
+ private IDictionary cache = new Dictionary();
+ private ILogger logger;
- public Text(ITextResource resource)
+ public Text(ILogger logger)
{
- if (resource == null)
- {
- throw new ArgumentNullException(nameof(resource));
- }
-
- cache = resource.LoadText() ?? new Dictionary();
+ 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();
+ }
+ catch (Exception e)
+ {
+ logger.Error("Failed to load text data from provided resource!", e);
+ }
+ }
}
}
diff --git a/SafeExamBrowser.Core/I18n/XmlTextResource.cs b/SafeExamBrowser.Core/I18n/XmlTextResource.cs
index 236aa54b..75b8397f 100644
--- a/SafeExamBrowser.Core/I18n/XmlTextResource.cs
+++ b/SafeExamBrowser.Core/I18n/XmlTextResource.cs
@@ -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;
+
+ ///
+ /// Initializes a new text resource for an XML file located at the specified path.
+ ///
+ /// If the specifed file does not exist.
+ /// If the given path is null.
+ 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 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();
diff --git a/SafeExamBrowser.Core/SafeExamBrowser.Core.csproj b/SafeExamBrowser.Core/SafeExamBrowser.Core.csproj
index 8f5b78ec..47345186 100644
--- a/SafeExamBrowser.Core/SafeExamBrowser.Core.csproj
+++ b/SafeExamBrowser.Core/SafeExamBrowser.Core.csproj
@@ -55,6 +55,7 @@
+
diff --git a/SafeExamBrowser.UserInterface.Classic/SplashScreen.xaml.cs b/SafeExamBrowser.UserInterface.Classic/SplashScreen.xaml.cs
index 4f87cfad..a9b3d59f 100644
--- a/SafeExamBrowser.UserInterface.Classic/SplashScreen.xaml.cs
+++ b/SafeExamBrowser.UserInterface.Classic/SplashScreen.xaml.cs
@@ -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 });
diff --git a/SafeExamBrowser.UserInterface.Windows10/SplashScreen.xaml.cs b/SafeExamBrowser.UserInterface.Windows10/SplashScreen.xaml.cs
index 87300276..591a5240 100644
--- a/SafeExamBrowser.UserInterface.Windows10/SplashScreen.xaml.cs
+++ b/SafeExamBrowser.UserInterface.Windows10/SplashScreen.xaml.cs
@@ -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 });
diff --git a/SafeExamBrowser/CompositionRoot.cs b/SafeExamBrowser/CompositionRoot.cs
index 80d4cafd..8c4c4c72 100644
--- a/SafeExamBrowser/CompositionRoot.cs
+++ b/SafeExamBrowser/CompositionRoot.cs
@@ -51,7 +51,6 @@ namespace SafeExamBrowser
private ISystemComponent 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();
+ 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));