From f9723d2c9e45304ebec1d3ab1c09bf58c406739a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damian=20B=C3=BCchel?= Date: Thu, 6 Jul 2017 18:18:39 +0200 Subject: [PATCH] Continued scaffolding: - Implemented basic logging - Added basic settings - Moved contracts to separate assembly --- .../Configuration/ISettings.cs | 15 ++ SafeExamBrowser.Contracts/I18n/IText.cs | 15 ++ .../I18n}/ITextResource.cs | 3 +- .../I18n/Key.cs | 3 +- .../Logging/ILogMessage.cs | 14 +- .../Logging/ILogObserver.cs | 15 ++ SafeExamBrowser.Contracts/Logging/ILogger.cs | 22 +++ SafeExamBrowser.Contracts/Logging/LogLevel.cs | 17 ++ .../Properties/AssemblyInfo.cs | 36 +++++ .../SafeExamBrowser.Contracts.csproj | 55 +++++++ .../UserInterface}/ITaskbar.cs | 2 +- .../I18n/TextTests.cs | 25 ++- .../Logging/LoggerTests.cs | 152 ++++++++++++++++++ .../SafeExamBrowser.Core.UnitTests.csproj | 17 +- .../packages.config | 2 + .../Configuration/Settings.cs | 25 +++ SafeExamBrowser.Core/I18n/Text.cs | 29 +--- SafeExamBrowser.Core/I18n/Text.xml | 1 - SafeExamBrowser.Core/I18n/XmlTextResource.cs | 2 +- SafeExamBrowser.Core/Logging/LogFileWriter.cs | 50 ++++++ SafeExamBrowser.Core/Logging/LogMessage.cs | 32 ++++ SafeExamBrowser.Core/Logging/Logger.cs | 85 ++++++++++ .../SafeExamBrowser.Core.csproj | 16 +- .../SafeExamBrowser.UserInterface.csproj | 6 +- SafeExamBrowser.UserInterface/Taskbar.xaml.cs | 2 +- SafeExamBrowser.sln | 6 + SafeExamBrowser/App.cs | 17 +- SafeExamBrowser/CompositionRoot.cs | 13 +- SafeExamBrowser/SafeExamBrowser.csproj | 31 ++++ 29 files changed, 635 insertions(+), 73 deletions(-) create mode 100644 SafeExamBrowser.Contracts/Configuration/ISettings.cs create mode 100644 SafeExamBrowser.Contracts/I18n/IText.cs rename {SafeExamBrowser.Core/Contracts => SafeExamBrowser.Contracts/I18n}/ITextResource.cs (84%) rename {SafeExamBrowser.Core => SafeExamBrowser.Contracts}/I18n/Key.cs (85%) rename SafeExamBrowser.Core/I18n/NullTextResource.cs => SafeExamBrowser.Contracts/Logging/ILogMessage.cs (56%) create mode 100644 SafeExamBrowser.Contracts/Logging/ILogObserver.cs create mode 100644 SafeExamBrowser.Contracts/Logging/ILogger.cs create mode 100644 SafeExamBrowser.Contracts/Logging/LogLevel.cs create mode 100644 SafeExamBrowser.Contracts/Properties/AssemblyInfo.cs create mode 100644 SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj rename {SafeExamBrowser.Core/Contracts => SafeExamBrowser.Contracts/UserInterface}/ITaskbar.cs (89%) create mode 100644 SafeExamBrowser.Core.UnitTests/Logging/LoggerTests.cs create mode 100644 SafeExamBrowser.Core/Configuration/Settings.cs create mode 100644 SafeExamBrowser.Core/Logging/LogFileWriter.cs create mode 100644 SafeExamBrowser.Core/Logging/LogMessage.cs create mode 100644 SafeExamBrowser.Core/Logging/Logger.cs diff --git a/SafeExamBrowser.Contracts/Configuration/ISettings.cs b/SafeExamBrowser.Contracts/Configuration/ISettings.cs new file mode 100644 index 00000000..a961f0de --- /dev/null +++ b/SafeExamBrowser.Contracts/Configuration/ISettings.cs @@ -0,0 +1,15 @@ +/* + * 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/. + */ + +namespace SafeExamBrowser.Contracts.Configuration +{ + public interface ISettings + { + string LogFolderPath { get; } + } +} diff --git a/SafeExamBrowser.Contracts/I18n/IText.cs b/SafeExamBrowser.Contracts/I18n/IText.cs new file mode 100644 index 00000000..eff2d16e --- /dev/null +++ b/SafeExamBrowser.Contracts/I18n/IText.cs @@ -0,0 +1,15 @@ +/* + * 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/. + */ + +namespace SafeExamBrowser.Contracts.I18n +{ + public interface IText + { + string Get(Key key); + } +} diff --git a/SafeExamBrowser.Core/Contracts/ITextResource.cs b/SafeExamBrowser.Contracts/I18n/ITextResource.cs similarity index 84% rename from SafeExamBrowser.Core/Contracts/ITextResource.cs rename to SafeExamBrowser.Contracts/I18n/ITextResource.cs index d136df42..64fdb881 100644 --- a/SafeExamBrowser.Core/Contracts/ITextResource.cs +++ b/SafeExamBrowser.Contracts/I18n/ITextResource.cs @@ -7,9 +7,8 @@ */ using System.Collections.Generic; -using SafeExamBrowser.Core.I18n; -namespace SafeExamBrowser.Core.Contracts +namespace SafeExamBrowser.Contracts.I18n { public interface ITextResource { diff --git a/SafeExamBrowser.Core/I18n/Key.cs b/SafeExamBrowser.Contracts/I18n/Key.cs similarity index 85% rename from SafeExamBrowser.Core/I18n/Key.cs rename to SafeExamBrowser.Contracts/I18n/Key.cs index 2a21e2bc..a4e92d94 100644 --- a/SafeExamBrowser.Core/I18n/Key.cs +++ b/SafeExamBrowser.Contracts/I18n/Key.cs @@ -6,11 +6,10 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -namespace SafeExamBrowser.Core.I18n +namespace SafeExamBrowser.Contracts.I18n { public enum Key { - MessageBox_FatalErrorTitle, MessageBox_SingleInstance, MessageBox_SingleInstanceTitle } diff --git a/SafeExamBrowser.Core/I18n/NullTextResource.cs b/SafeExamBrowser.Contracts/Logging/ILogMessage.cs similarity index 56% rename from SafeExamBrowser.Core/I18n/NullTextResource.cs rename to SafeExamBrowser.Contracts/Logging/ILogMessage.cs index 29d64b3f..a242c890 100644 --- a/SafeExamBrowser.Core/I18n/NullTextResource.cs +++ b/SafeExamBrowser.Contracts/Logging/ILogMessage.cs @@ -6,16 +6,14 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -using System.Collections.Generic; -using SafeExamBrowser.Core.Contracts; +using System; -namespace SafeExamBrowser.Core.I18n +namespace SafeExamBrowser.Contracts.Logging { - class NullTextResource : ITextResource + public interface ILogMessage : ICloneable { - public IDictionary LoadText() - { - return new Dictionary(); - } + DateTime DateTime { get; } + LogLevel Severity { get; } + string Message { get; } } } diff --git a/SafeExamBrowser.Contracts/Logging/ILogObserver.cs b/SafeExamBrowser.Contracts/Logging/ILogObserver.cs new file mode 100644 index 00000000..ac33892b --- /dev/null +++ b/SafeExamBrowser.Contracts/Logging/ILogObserver.cs @@ -0,0 +1,15 @@ +/* + * 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/. + */ + +namespace SafeExamBrowser.Contracts.Logging +{ + public interface ILogObserver + { + void Notify(ILogMessage message); + } +} diff --git a/SafeExamBrowser.Contracts/Logging/ILogger.cs b/SafeExamBrowser.Contracts/Logging/ILogger.cs new file mode 100644 index 00000000..be1e5e29 --- /dev/null +++ b/SafeExamBrowser.Contracts/Logging/ILogger.cs @@ -0,0 +1,22 @@ +/* + * 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.Collections.Generic; + +namespace SafeExamBrowser.Contracts.Logging +{ + public interface ILogger + { + void Info(string message); + void Warn(string message); + void Error(string message); + void Subscribe(ILogObserver observer); + void Unsubscribe(ILogObserver observer); + IList GetLog(); + } +} diff --git a/SafeExamBrowser.Contracts/Logging/LogLevel.cs b/SafeExamBrowser.Contracts/Logging/LogLevel.cs new file mode 100644 index 00000000..53428f9a --- /dev/null +++ b/SafeExamBrowser.Contracts/Logging/LogLevel.cs @@ -0,0 +1,17 @@ +/* + * 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/. + */ + +namespace SafeExamBrowser.Contracts.Logging +{ + public enum LogLevel + { + Info = 1, + Warn = 2, + Error = 3 + } +} diff --git a/SafeExamBrowser.Contracts/Properties/AssemblyInfo.cs b/SafeExamBrowser.Contracts/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..1f346fb9 --- /dev/null +++ b/SafeExamBrowser.Contracts/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("SafeExamBrowser.Contracts")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("SafeExamBrowser.Contracts")] +[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("47da5933-bef8-4729-94e6-abde2db12262")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj new file mode 100644 index 00000000..bbadca59 --- /dev/null +++ b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj @@ -0,0 +1,55 @@ + + + + + Debug + AnyCPU + {47DA5933-BEF8-4729-94E6-ABDE2DB12262} + Library + Properties + SafeExamBrowser.Contracts + SafeExamBrowser.Contracts + v4.5.2 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SafeExamBrowser.Core/Contracts/ITaskbar.cs b/SafeExamBrowser.Contracts/UserInterface/ITaskbar.cs similarity index 89% rename from SafeExamBrowser.Core/Contracts/ITaskbar.cs rename to SafeExamBrowser.Contracts/UserInterface/ITaskbar.cs index 4b500da5..9458bf7a 100644 --- a/SafeExamBrowser.Core/Contracts/ITaskbar.cs +++ b/SafeExamBrowser.Contracts/UserInterface/ITaskbar.cs @@ -6,7 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -namespace SafeExamBrowser.Core.Contracts +namespace SafeExamBrowser.Contracts.UserInterface { public interface ITaskbar { diff --git a/SafeExamBrowser.Core.UnitTests/I18n/TextTests.cs b/SafeExamBrowser.Core.UnitTests/I18n/TextTests.cs index 1d7a41ca..34572749 100644 --- a/SafeExamBrowser.Core.UnitTests/I18n/TextTests.cs +++ b/SafeExamBrowser.Core.UnitTests/I18n/TextTests.cs @@ -7,35 +7,30 @@ */ using System; +using System.Collections.Generic; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Core.I18n; -namespace SafeExamBrowser.Core.UnitTests +namespace SafeExamBrowser.Core.UnitTests.I18n { [TestClass] public class TextTests { - [TestMethod] - public void MustNeverBeNull() - { - Assert.IsNotNull(Text.Instance); - } - [TestMethod] public void MustNeverReturnNull() { - var text = Text.Instance.Get((Key) (-1)); + var resource = new Mock(); + var sut = new Text(resource.Object); + + resource.Setup(r => r.LoadText()).Returns>(null); + + var text = sut.Get((Key)(-1)); Assert.IsNotNull(text); } - [TestMethod] - [ExpectedException(typeof(ArgumentNullException))] - public void MustNotAllowBeingNull() - { - Text.Instance = null; - } - [TestMethod] [ExpectedException(typeof(ArgumentNullException))] public void MustNotAllowNullResource() diff --git a/SafeExamBrowser.Core.UnitTests/Logging/LoggerTests.cs b/SafeExamBrowser.Core.UnitTests/Logging/LoggerTests.cs new file mode 100644 index 00000000..ae8d279a --- /dev/null +++ b/SafeExamBrowser.Core.UnitTests/Logging/LoggerTests.cs @@ -0,0 +1,152 @@ +/* + * 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.Collections.Generic; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using SafeExamBrowser.Contracts.Logging; +using SafeExamBrowser.Core.Logging; + +namespace SafeExamBrowser.Core.UnitTests.Logging +{ + [TestClass] + public class LoggerTests + { + [TestMethod] + public void MustAddMessagesToLog() + { + var sut = new Logger(); + var info = "I'm an info message"; + var warn = "I'm a warning!"; + var error = "I AM AN ERROR!!"; + + sut.Info(info); + sut.Warn(warn); + sut.Error(error); + + var log = sut.GetLog(); + + Assert.IsTrue(log.Count == 3); + + Assert.IsTrue(info.Equals(log[0].Message)); + Assert.IsTrue(log[0].Severity == LogLevel.Info); + + Assert.IsTrue(warn.Equals(log[1].Message)); + Assert.IsTrue(log[1].Severity == LogLevel.Warn); + + Assert.IsTrue(error.Equals(log[2].Message)); + Assert.IsTrue(log[2].Severity == LogLevel.Error); + } + + [TestMethod] + public void MustReturnCopyOfLog() + { + var sut = new Logger(); + var info = "I'm an info message"; + var warn = "I'm a warning!"; + var error = "I AM AN ERROR!!"; + + sut.Info(info); + sut.Warn(warn); + sut.Error(error); + + var log1 = sut.GetLog(); + var log2 = sut.GetLog(); + + Assert.AreNotSame(log1, log2); + + foreach (var message in log1) + { + Assert.AreNotSame(message, log2[log1.IndexOf(message)]); + } + } + + [TestMethod] + [ExpectedException(typeof(ArgumentNullException))] + public void MustNotAllowNullObserver() + { + var sut = new Logger(); + + sut.Subscribe(null); + } + + [TestMethod] + public void MustNotSubscribeSameObserverMultipleTimes() + { + var sut = new Logger(); + var observer = new Mock(); + + observer.Setup(o => o.Notify(It.IsAny())); + + sut.Subscribe(observer.Object); + sut.Subscribe(observer.Object); + sut.Subscribe(observer.Object); + + sut.Info("Blubb"); + + observer.Verify(o => o.Notify(It.IsAny()), Times.Once()); + } + + [TestMethod] + public void MustNotFailWhenRemovingNullObserver() + { + var sut = new Logger(); + + sut.Unsubscribe(null); + } + + [TestMethod] + public void MustSubscribeObserver() + { + var sut = new Logger(); + var observer = new Mock(); + var message = "Blubb"; + var messages = new List(); + + observer.Setup(o => o.Notify(It.IsAny())).Callback(m => messages.Add(m)); + + sut.Subscribe(observer.Object); + sut.Info(message); + sut.Warn(message); + + observer.Verify(o => o.Notify(It.IsAny()), Times.Exactly(2)); + + Assert.IsTrue(messages.Count == 2); + + Assert.IsTrue(messages[0].Severity == LogLevel.Info); + Assert.IsTrue(message.Equals(messages[0].Message)); + + Assert.IsTrue(messages[1].Severity == LogLevel.Warn); + Assert.IsTrue(message.Equals(messages[1].Message)); + } + + [TestMethod] + public void MustUnsubscribeObserver() + { + var sut = new Logger(); + var observer = new Mock(); + var message = "Blubb"; + var messages = new List(); + + observer.Setup(o => o.Notify(It.IsAny())).Callback(m => messages.Add(m)); + + sut.Subscribe(observer.Object); + sut.Info(message); + sut.Unsubscribe(observer.Object); + sut.Warn(message); + + observer.Verify(o => o.Notify(It.IsAny()), Times.Once()); + + Assert.IsTrue(messages.Count == 1); + + Assert.IsTrue(messages[0].Severity == LogLevel.Info); + Assert.IsTrue(message.Equals(messages[0].Message)); + } + } +} diff --git a/SafeExamBrowser.Core.UnitTests/SafeExamBrowser.Core.UnitTests.csproj b/SafeExamBrowser.Core.UnitTests/SafeExamBrowser.Core.UnitTests.csproj index be1618ea..7233ac84 100644 --- a/SafeExamBrowser.Core.UnitTests/SafeExamBrowser.Core.UnitTests.csproj +++ b/SafeExamBrowser.Core.UnitTests/SafeExamBrowser.Core.UnitTests.csproj @@ -38,28 +38,39 @@ 4 + + ..\packages\Castle.Core.4.1.0\lib\net45\Castle.Core.dll + ..\packages\MSTest.TestFramework.1.1.11\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll ..\packages\MSTest.TestFramework.1.1.11\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll + + ..\packages\Moq.4.7.63\lib\net45\Moq.dll + + - - - + + {47DA5933-BEF8-4729-94E6-ABDE2DB12262} + SafeExamBrowser.Contracts + {3d6fdbb6-a4af-4626-bb2b-bf329d44f9cc} SafeExamBrowser.Core + + + diff --git a/SafeExamBrowser.Core.UnitTests/packages.config b/SafeExamBrowser.Core.UnitTests/packages.config index 1ab72187..24b8c592 100644 --- a/SafeExamBrowser.Core.UnitTests/packages.config +++ b/SafeExamBrowser.Core.UnitTests/packages.config @@ -1,5 +1,7 @@  + + \ No newline at end of file diff --git a/SafeExamBrowser.Core/Configuration/Settings.cs b/SafeExamBrowser.Core/Configuration/Settings.cs new file mode 100644 index 00000000..c76c5095 --- /dev/null +++ b/SafeExamBrowser.Core/Configuration/Settings.cs @@ -0,0 +1,25 @@ +/* + * 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 SafeExamBrowser.Contracts.Configuration; + +namespace SafeExamBrowser.Core.Configuration +{ + public class Settings : ISettings + { + public string LogFolderPath + { + get + { + return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "SafeExamBrowser", "Logs"); + } + } + } +} diff --git a/SafeExamBrowser.Core/I18n/Text.cs b/SafeExamBrowser.Core/I18n/Text.cs index bec437c4..236fd3ad 100644 --- a/SafeExamBrowser.Core/I18n/Text.cs +++ b/SafeExamBrowser.Core/I18n/Text.cs @@ -8,16 +8,13 @@ using System; using System.Collections.Generic; -using SafeExamBrowser.Core.Contracts; +using SafeExamBrowser.Contracts.I18n; namespace SafeExamBrowser.Core.I18n { - public class Text + public class Text : IText { - private static Text instance; - private static readonly object @lock = new object(); - - private IDictionary cache = new Dictionary(); + private readonly IDictionary cache; public Text(ITextResource resource) { @@ -26,25 +23,7 @@ namespace SafeExamBrowser.Core.I18n throw new ArgumentNullException(nameof(resource)); } - cache = resource.LoadText(); - } - - public static Text Instance - { - get - { - lock (@lock) - { - return instance ?? new Text(new NullTextResource()); - } - } - set - { - lock (@lock) - { - instance = value ?? throw new ArgumentNullException(nameof(value)); - } - } + cache = resource.LoadText() ?? new Dictionary(); } public string Get(Key key) diff --git a/SafeExamBrowser.Core/I18n/Text.xml b/SafeExamBrowser.Core/I18n/Text.xml index 10eec81e..5c279b7a 100644 --- a/SafeExamBrowser.Core/I18n/Text.xml +++ b/SafeExamBrowser.Core/I18n/Text.xml @@ -1,6 +1,5 @@  - Fatal Error You can only run one instance of SEB at a time. Startup Not Allowed \ No newline at end of file diff --git a/SafeExamBrowser.Core/I18n/XmlTextResource.cs b/SafeExamBrowser.Core/I18n/XmlTextResource.cs index 72145520..9f21b672 100644 --- a/SafeExamBrowser.Core/I18n/XmlTextResource.cs +++ b/SafeExamBrowser.Core/I18n/XmlTextResource.cs @@ -11,7 +11,7 @@ using System.Collections.Generic; using System.IO; using System.Reflection; using System.Xml.Linq; -using SafeExamBrowser.Core.Contracts; +using SafeExamBrowser.Contracts.I18n; namespace SafeExamBrowser.Core.I18n { diff --git a/SafeExamBrowser.Core/Logging/LogFileWriter.cs b/SafeExamBrowser.Core/Logging/LogFileWriter.cs new file mode 100644 index 00000000..419bdc7f --- /dev/null +++ b/SafeExamBrowser.Core/Logging/LogFileWriter.cs @@ -0,0 +1,50 @@ +/* + * 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.Text; +using System.Threading; +using SafeExamBrowser.Contracts.Configuration; +using SafeExamBrowser.Contracts.Logging; + +namespace SafeExamBrowser.Core.Logging +{ + public class LogFileWriter : ILogObserver + { + private static readonly object @lock = new object(); + private readonly string filePath; + + public LogFileWriter(ISettings settings) + { + var fileName = $"{DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss")}.txt"; + + if (!Directory.Exists(settings.LogFolderPath)) + { + Directory.CreateDirectory(settings.LogFolderPath); + } + + filePath = Path.Combine(settings.LogFolderPath, fileName); + } + + public void Notify(ILogMessage message) + { + lock (@lock) + { + using (var stream = new StreamWriter(filePath, true, Encoding.UTF8)) + { + var date = message.DateTime.ToString("yyyy-MM-dd HH:mm:ss"); + var threadId = Thread.CurrentThread.ManagedThreadId; + var severity = message.Severity.ToString().ToUpper(); + + stream.WriteLine($"{date} [{threadId}] - {severity}: {message.Message}"); + } + } + } + } +} diff --git a/SafeExamBrowser.Core/Logging/LogMessage.cs b/SafeExamBrowser.Core/Logging/LogMessage.cs new file mode 100644 index 00000000..12005733 --- /dev/null +++ b/SafeExamBrowser.Core/Logging/LogMessage.cs @@ -0,0 +1,32 @@ +/* + * 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 SafeExamBrowser.Contracts.Logging; + +namespace SafeExamBrowser.Core.Entities +{ + public class LogMessage : ILogMessage + { + public DateTime DateTime { get; private set; } + public LogLevel Severity { get; private set; } + public string Message { get; private set; } + + public LogMessage(DateTime dateTime, LogLevel severity, string message) + { + DateTime = dateTime; + Severity = severity; + Message = message; + } + + public object Clone() + { + return new LogMessage(DateTime, Severity, Message); + } + } +} diff --git a/SafeExamBrowser.Core/Logging/Logger.cs b/SafeExamBrowser.Core/Logging/Logger.cs new file mode 100644 index 00000000..2474e200 --- /dev/null +++ b/SafeExamBrowser.Core/Logging/Logger.cs @@ -0,0 +1,85 @@ +/* + * 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.Collections.Generic; +using System.Linq; +using SafeExamBrowser.Contracts.Logging; +using SafeExamBrowser.Core.Entities; + +namespace SafeExamBrowser.Core.Logging +{ + public class Logger : ILogger + { + private static readonly object @lock = new object(); + private readonly IList log = new List(); + private readonly IList observers = new List(); + + public void Error(string message) + { + Log(LogLevel.Error, message); + } + + public void Info(string message) + { + Log(LogLevel.Info, message); + } + + public void Warn(string message) + { + Log(LogLevel.Warn, message); + } + + public IList GetLog() + { + lock (@lock) + { + return log.Select(m => m.Clone() as ILogMessage).ToList(); + } + } + + public void Subscribe(ILogObserver observer) + { + if (observer == null) + { + throw new ArgumentNullException(nameof(observer)); + } + + lock (@lock) + { + if (!observers.Contains(observer)) + { + observers.Add(observer); + } + } + } + + public void Unsubscribe(ILogObserver observer) + { + lock (@lock) + { + observers.Remove(observer); + } + } + + private void Log(LogLevel severity, string message) + { + lock (@lock) + { + var entry = new LogMessage(DateTime.Now, severity, message); + + log.Add(entry); + + foreach (var observer in observers) + { + observer.Notify(entry); + } + } + } + } +} diff --git a/SafeExamBrowser.Core/SafeExamBrowser.Core.csproj b/SafeExamBrowser.Core/SafeExamBrowser.Core.csproj index cade94a3..844d596b 100644 --- a/SafeExamBrowser.Core/SafeExamBrowser.Core.csproj +++ b/SafeExamBrowser.Core/SafeExamBrowser.Core.csproj @@ -40,18 +40,26 @@ - - - - + + + + Always + Designer + + + {47DA5933-BEF8-4729-94E6-ABDE2DB12262} + SafeExamBrowser.Contracts + + + \ No newline at end of file diff --git a/SafeExamBrowser.UserInterface/SafeExamBrowser.UserInterface.csproj b/SafeExamBrowser.UserInterface/SafeExamBrowser.UserInterface.csproj index 8c0b6a1c..9a82b20f 100644 --- a/SafeExamBrowser.UserInterface/SafeExamBrowser.UserInterface.csproj +++ b/SafeExamBrowser.UserInterface/SafeExamBrowser.UserInterface.csproj @@ -79,9 +79,9 @@ - - {3d6fdbb6-a4af-4626-bb2b-bf329d44f9cc} - SafeExamBrowser.Core + + {47DA5933-BEF8-4729-94E6-ABDE2DB12262} + SafeExamBrowser.Contracts diff --git a/SafeExamBrowser.UserInterface/Taskbar.xaml.cs b/SafeExamBrowser.UserInterface/Taskbar.xaml.cs index 9cf036d9..4ed67410 100644 --- a/SafeExamBrowser.UserInterface/Taskbar.xaml.cs +++ b/SafeExamBrowser.UserInterface/Taskbar.xaml.cs @@ -7,7 +7,7 @@ */ using System.Windows; -using SafeExamBrowser.Core.Contracts; +using SafeExamBrowser.Contracts.UserInterface; namespace SafeExamBrowser.UserInterface { diff --git a/SafeExamBrowser.sln b/SafeExamBrowser.sln index 71711402..d8ea5884 100644 --- a/SafeExamBrowser.sln +++ b/SafeExamBrowser.sln @@ -17,6 +17,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SafeExamBrowser.UserInterfa EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SafeExamBrowser.Core.UnitTests", "SafeExamBrowser.Core.UnitTests\SafeExamBrowser.Core.UnitTests.csproj", "{48B9F2A1-B87D-40F0-BEC9-399E8909860F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SafeExamBrowser.Contracts", "SafeExamBrowser.Contracts\SafeExamBrowser.Contracts.csproj", "{47DA5933-BEF8-4729-94E6-ABDE2DB12262}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -39,6 +41,10 @@ Global {48B9F2A1-B87D-40F0-BEC9-399E8909860F}.Debug|Any CPU.Build.0 = Debug|Any CPU {48B9F2A1-B87D-40F0-BEC9-399E8909860F}.Release|Any CPU.ActiveCfg = Release|Any CPU {48B9F2A1-B87D-40F0-BEC9-399E8909860F}.Release|Any CPU.Build.0 = Release|Any CPU + {47DA5933-BEF8-4729-94E6-ABDE2DB12262}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {47DA5933-BEF8-4729-94E6-ABDE2DB12262}.Debug|Any CPU.Build.0 = Debug|Any CPU + {47DA5933-BEF8-4729-94E6-ABDE2DB12262}.Release|Any CPU.ActiveCfg = Release|Any CPU + {47DA5933-BEF8-4729-94E6-ABDE2DB12262}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/SafeExamBrowser/App.cs b/SafeExamBrowser/App.cs index 647238df..8b5386b6 100644 --- a/SafeExamBrowser/App.cs +++ b/SafeExamBrowser/App.cs @@ -9,7 +9,7 @@ using System; using System.Threading; using System.Windows; -using SafeExamBrowser.Core.I18n; +using SafeExamBrowser.Contracts.I18n; namespace SafeExamBrowser { @@ -27,23 +27,28 @@ namespace SafeExamBrowser compositionRoot.InitializeGlobalModules(); compositionRoot.BuildObjectGraph(); - StartApplication(compositionRoot.Taskbar); + StartApplication(compositionRoot); } catch (Exception e) { - MessageBox.Show(e.Message + "\n\n" + e.StackTrace, Text.Instance.Get(Key.MessageBox_FatalErrorTitle)); + MessageBox.Show(e.Message + "\n\n" + e.StackTrace, "Fatal Error"); } } - private static void StartApplication(Window taskbar) + private static void StartApplication(CompositionRoot compositionRoot) { + compositionRoot.Logger.Info("Testing the log..."); + if (NoInstanceRunning()) { - new App().Run(taskbar); + new App().Run(compositionRoot.Taskbar); } else { - MessageBox.Show(Text.Instance.Get(Key.MessageBox_SingleInstance), Text.Instance.Get(Key.MessageBox_SingleInstanceTitle)); + var message = compositionRoot.Text.Get(Key.MessageBox_SingleInstance); + var title = compositionRoot.Text.Get(Key.MessageBox_SingleInstanceTitle); + + MessageBox.Show(message, title); } } diff --git a/SafeExamBrowser/CompositionRoot.cs b/SafeExamBrowser/CompositionRoot.cs index 97a3b6ff..6b8451a2 100644 --- a/SafeExamBrowser/CompositionRoot.cs +++ b/SafeExamBrowser/CompositionRoot.cs @@ -7,18 +7,29 @@ */ using System.Windows; +using SafeExamBrowser.Contracts.Configuration; +using SafeExamBrowser.Contracts.I18n; +using SafeExamBrowser.Contracts.Logging; +using SafeExamBrowser.Core.Configuration; using SafeExamBrowser.Core.I18n; +using SafeExamBrowser.Core.Logging; using SafeExamBrowser.UserInterface; namespace SafeExamBrowser { class CompositionRoot { + public ILogger Logger { get; private set; } + public ISettings Settings { get; private set; } + public IText Text { get; private set; } public Window Taskbar { get; private set; } public void InitializeGlobalModules() { - Text.Instance = new Text(new XmlTextResource()); + Settings = new Settings(); + Logger = new Logger(); + Logger.Subscribe(new LogFileWriter(Settings)); + Text = new Text(new XmlTextResource()); } public void BuildObjectGraph() diff --git a/SafeExamBrowser/SafeExamBrowser.csproj b/SafeExamBrowser/SafeExamBrowser.csproj index 13d7278c..1bf280d0 100644 --- a/SafeExamBrowser/SafeExamBrowser.csproj +++ b/SafeExamBrowser/SafeExamBrowser.csproj @@ -13,6 +13,21 @@ {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 4 true + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 3.0.0.%2a + false + false + true AnyCPU @@ -78,6 +93,10 @@ + + {47DA5933-BEF8-4729-94E6-ABDE2DB12262} + SafeExamBrowser.Contracts + {3d6fdbb6-a4af-4626-bb2b-bf329d44f9cc} SafeExamBrowser.Core @@ -87,5 +106,17 @@ SafeExamBrowser.UserInterface + + + False + Microsoft .NET Framework 4.5.2 %28x86 and x64%29 + true + + + False + .NET Framework 3.5 SP1 + false + + \ No newline at end of file