diff --git a/SafeExamBrowser.Configuration/Settings/SettingsRepository.cs b/SafeExamBrowser.Configuration/Settings/SettingsRepository.cs
index f613eccc..596a24e0 100644
--- a/SafeExamBrowser.Configuration/Settings/SettingsRepository.cs
+++ b/SafeExamBrowser.Configuration/Settings/SettingsRepository.cs
@@ -13,19 +13,22 @@ namespace SafeExamBrowser.Configuration.Settings
{
public class SettingsRepository : ISettingsRepository
{
+ public ISettings Current { get; private set; }
+
public ISettings Load(Uri path)
{
// TODO
+
return LoadDefaults();
}
public ISettings LoadDefaults()
{
- var settings = new Settings();
+ Current = new Settings();
// TODO
- return settings;
+ return Current;
}
}
}
diff --git a/SafeExamBrowser.Contracts/Communication/ICommunicationHost.cs b/SafeExamBrowser.Contracts/Communication/ICommunicationHost.cs
new file mode 100644
index 00000000..386af226
--- /dev/null
+++ b/SafeExamBrowser.Contracts/Communication/ICommunicationHost.cs
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2018 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.ServiceModel;
+using SafeExamBrowser.Contracts.Communication.Messages;
+using SafeExamBrowser.Contracts.Communication.Responses;
+
+namespace SafeExamBrowser.Contracts.Communication
+{
+ [ServiceContract(SessionMode = SessionMode.Required)]
+ public interface ICommunicationHost
+ {
+ ///
+ /// Initiates a connection to the host and must thus be called before any other opertion. To authenticate itself to the host, the
+ /// client can specify a security token. If the connection request was successful, a new session will be created by the host and
+ /// the client will subsequently be allowed to start communicating with the host.
+ ///
+ [OperationContract(IsInitiating = true)]
+ IConnectResponse Connect(Guid? token = null);
+
+ ///
+ /// Closes the connection to the host and instructs it to terminate the communication session.
+ ///
+ [OperationContract(IsInitiating = false, IsTerminating = true)]
+ void Disconnect(IMessage message);
+
+ ///
+ /// Sends a message to the host, optionally returning a response. If no response is expected, null will be returned.
+ ///
+ [OperationContract(IsInitiating = false)]
+ IResponse Send(IMessage message);
+ }
+}
diff --git a/SafeExamBrowser.Contracts/Communication/Messages/IMessage.cs b/SafeExamBrowser.Contracts/Communication/Messages/IMessage.cs
new file mode 100644
index 00000000..52d1a51e
--- /dev/null
+++ b/SafeExamBrowser.Contracts/Communication/Messages/IMessage.cs
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2018 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.Runtime.Serialization;
+
+namespace SafeExamBrowser.Contracts.Communication.Messages
+{
+ public interface IMessage : ISerializable
+ {
+ ///
+ /// The communication token needed for authentication with the host.
+ ///
+ Guid CommunicationToken { get; }
+ }
+}
diff --git a/SafeExamBrowser.Contracts/Communication/Responses/IConnectResponse.cs b/SafeExamBrowser.Contracts/Communication/Responses/IConnectResponse.cs
new file mode 100644
index 00000000..664973a7
--- /dev/null
+++ b/SafeExamBrowser.Contracts/Communication/Responses/IConnectResponse.cs
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2018 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;
+
+namespace SafeExamBrowser.Contracts.Communication.Responses
+{
+ public interface IConnectResponse : IResponse
+ {
+ ///
+ /// The communication token needed for authentication with the host. Is null if a connection was refused.
+ ///
+ Guid? CommunicationToken { get; }
+
+ ///
+ /// Indicates whether the host has accepted the connection request.
+ ///
+ bool ConnectionEstablished { get; }
+ }
+}
diff --git a/SafeExamBrowser.Contracts/Communication/Responses/IResponse.cs b/SafeExamBrowser.Contracts/Communication/Responses/IResponse.cs
new file mode 100644
index 00000000..51562212
--- /dev/null
+++ b/SafeExamBrowser.Contracts/Communication/Responses/IResponse.cs
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2018 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.Runtime.Serialization;
+
+namespace SafeExamBrowser.Contracts.Communication.Responses
+{
+ public interface IResponse : ISerializable
+ {
+ }
+}
diff --git a/SafeExamBrowser.Contracts/Configuration/Settings/ISettingsRepository.cs b/SafeExamBrowser.Contracts/Configuration/Settings/ISettingsRepository.cs
index 15fb434a..8c419131 100644
--- a/SafeExamBrowser.Contracts/Configuration/Settings/ISettingsRepository.cs
+++ b/SafeExamBrowser.Contracts/Configuration/Settings/ISettingsRepository.cs
@@ -12,6 +12,12 @@ namespace SafeExamBrowser.Contracts.Configuration.Settings
{
public interface ISettingsRepository
{
+ ///
+ /// Retrieves the current settings, i.e. the last ones which were loaded. If no settings have been loaded yet, this property will
+ /// be null.
+ ///
+ ISettings Current { get; }
+
///
/// Attempts to load settings from the specified path.
///
diff --git a/SafeExamBrowser.Contracts/Logging/ILogger.cs b/SafeExamBrowser.Contracts/Logging/ILogger.cs
index 04cd8acc..c0351bae 100644
--- a/SafeExamBrowser.Contracts/Logging/ILogger.cs
+++ b/SafeExamBrowser.Contracts/Logging/ILogger.cs
@@ -13,6 +13,12 @@ namespace SafeExamBrowser.Contracts.Logging
{
public interface ILogger
{
+ ///
+ /// Logs the given message with severity DEBUG.
+ ///
+ ///
+ void Debug(string message);
+
///
/// Logs the given message with severity INFO.
///
diff --git a/SafeExamBrowser.Contracts/Logging/LogLevel.cs b/SafeExamBrowser.Contracts/Logging/LogLevel.cs
index 81a845cd..37836a99 100644
--- a/SafeExamBrowser.Contracts/Logging/LogLevel.cs
+++ b/SafeExamBrowser.Contracts/Logging/LogLevel.cs
@@ -13,8 +13,9 @@ namespace SafeExamBrowser.Contracts.Logging
///
public enum LogLevel
{
- Info = 1,
- Warning = 2,
- Error = 3
+ Debug = 1,
+ Info = 2,
+ Warning = 3,
+ Error = 4
}
}
diff --git a/SafeExamBrowser.Contracts/Runtime/IRuntimeController.cs b/SafeExamBrowser.Contracts/Runtime/IRuntimeController.cs
index 770fe573..c6452974 100644
--- a/SafeExamBrowser.Contracts/Runtime/IRuntimeController.cs
+++ b/SafeExamBrowser.Contracts/Runtime/IRuntimeController.cs
@@ -6,17 +6,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
-using SafeExamBrowser.Contracts.Configuration.Settings;
-
namespace SafeExamBrowser.Contracts.Runtime
{
public interface IRuntimeController
{
- ///
- /// Allows to specify the application settings to be used during runtime.
- ///
- ISettings Settings { set; }
-
///
/// Wires up and starts the application event handling.
///
diff --git a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj
index bfcd66da..3b391f27 100644
--- a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj
+++ b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj
@@ -50,9 +50,14 @@
+
+
+
+
+
diff --git a/SafeExamBrowser.Core.UnitTests/Logging/LoggerTests.cs b/SafeExamBrowser.Core.UnitTests/Logging/LoggerTests.cs
index 36f90238..e0bc98d2 100644
--- a/SafeExamBrowser.Core.UnitTests/Logging/LoggerTests.cs
+++ b/SafeExamBrowser.Core.UnitTests/Logging/LoggerTests.cs
@@ -22,6 +22,7 @@ namespace SafeExamBrowser.Core.UnitTests.Logging
public void MustAddMessagesToLog()
{
var sut = new Logger();
+ var debug = "I'm a debug message";
var info = "I'm an info message";
var warn = "I'm a warning!";
var error = "I AM AN ERROR!!";
@@ -30,6 +31,7 @@ namespace SafeExamBrowser.Core.UnitTests.Logging
var message = "I'm a simple text message";
var content = new LogText("I'm some raw log text...");
+ sut.Debug(debug);
sut.Info(info);
sut.Warn(warn);
sut.Error(error);
@@ -39,34 +41,39 @@ namespace SafeExamBrowser.Core.UnitTests.Logging
var log = sut.GetLog();
- Assert.IsTrue(log.Count == 7);
+ Assert.IsTrue(log.Count == 8);
- Assert.IsTrue(info.Equals((log[0] as ILogMessage).Message));
- Assert.IsTrue((log[0] as ILogMessage).Severity == LogLevel.Info);
+ Assert.IsTrue(debug.Equals((log[0] as ILogMessage).Message));
+ Assert.IsTrue((log[0] as ILogMessage).Severity == LogLevel.Debug);
- Assert.IsTrue(warn.Equals((log[1] as ILogMessage).Message));
- Assert.IsTrue((log[1] as ILogMessage).Severity == LogLevel.Warning);
+ Assert.IsTrue(info.Equals((log[1] as ILogMessage).Message));
+ Assert.IsTrue((log[1] as ILogMessage).Severity == LogLevel.Info);
- Assert.IsTrue(error.Equals((log[2] as ILogMessage).Message));
- Assert.IsTrue((log[2] as ILogMessage).Severity == LogLevel.Error);
+ Assert.IsTrue(warn.Equals((log[2] as ILogMessage).Message));
+ Assert.IsTrue((log[2] as ILogMessage).Severity == LogLevel.Warning);
Assert.IsTrue(error.Equals((log[3] as ILogMessage).Message));
Assert.IsTrue((log[3] as ILogMessage).Severity == LogLevel.Error);
- Assert.IsTrue((log[4] as ILogText).Text.Contains(exceptionMessage));
- Assert.IsTrue(message.Equals((log[5] as ILogText).Text));
+ Assert.IsTrue(error.Equals((log[4] as ILogMessage).Message));
+ Assert.IsTrue((log[4] as ILogMessage).Severity == LogLevel.Error);
+ Assert.IsTrue((log[5] as ILogText).Text.Contains(exceptionMessage));
- Assert.IsTrue(content.Text.Equals((log[6] as ILogText).Text));
+ Assert.IsTrue(message.Equals((log[6] as ILogText).Text));
+
+ Assert.IsTrue(content.Text.Equals((log[7] as ILogText).Text));
}
[TestMethod]
public void MustReturnCopyOfLog()
{
var sut = new Logger();
+ var debug = "I'm a debug message";
var info = "I'm an info message";
var warn = "I'm a warning!";
var error = "I AM AN ERROR!!";
+ sut.Debug(debug);
sut.Info(info);
sut.Warn(warn);
sut.Error(error);
@@ -87,6 +94,7 @@ namespace SafeExamBrowser.Core.UnitTests.Logging
{
var sut = new Logger();
+ Assert.ThrowsException(() => sut.Debug(null));
Assert.ThrowsException(() => sut.Info(null));
Assert.ThrowsException(() => sut.Warn(null));
Assert.ThrowsException(() => sut.Error(null));
diff --git a/SafeExamBrowser.Core/Communication/CommunicationHostProxy.cs b/SafeExamBrowser.Core/Communication/CommunicationHostProxy.cs
new file mode 100644
index 00000000..c2739109
--- /dev/null
+++ b/SafeExamBrowser.Core/Communication/CommunicationHostProxy.cs
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2018 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.ServiceModel;
+using SafeExamBrowser.Contracts.Communication;
+using SafeExamBrowser.Contracts.Communication.Messages;
+using SafeExamBrowser.Contracts.Communication.Responses;
+using SafeExamBrowser.Contracts.Logging;
+
+namespace SafeExamBrowser.Core.Communication
+{
+ public class CommunicationHostProxy : ICommunicationHost
+ {
+ private string address;
+ private ILogger logger;
+ private ICommunicationHost channel;
+
+ public CommunicationHostProxy(ILogger logger, string address)
+ {
+ this.address = address;
+ this.logger = logger;
+ }
+
+ public IConnectResponse Connect(Guid? token = null)
+ {
+ var endpoint = new EndpointAddress(address);
+
+ channel = ChannelFactory.CreateChannel(new NetNamedPipeBinding(NetNamedPipeSecurityMode.Transport), endpoint);
+ (channel as ICommunicationObject).Closed += CommunicationHostProxy_Closed;
+ (channel as ICommunicationObject).Closing += CommunicationHostProxy_Closing;
+ (channel as ICommunicationObject).Faulted += CommunicationHostProxy_Faulted;
+
+ var response = channel.Connect(token);
+
+ logger.Debug($"Tried to connect to {address}, connection was {(response.ConnectionEstablished ? "established" : "refused")}.");
+
+ return response;
+ }
+
+ public void Disconnect(IMessage message)
+ {
+ if (ChannelIsReady())
+ {
+ channel.Disconnect(message);
+ logger.Debug($"Disconnected from {address}, transmitting {ToString(message)}.");
+ }
+
+ throw new CommunicationException($"Tried to disconnect from host, but channel was {GetChannelState()}!");
+ }
+
+ public IResponse Send(IMessage message)
+ {
+ if (ChannelIsReady())
+ {
+ var response = channel.Send(message);
+
+ logger.Debug($"Sent {ToString(message)}, got {ToString(response)}.");
+
+ return response;
+ }
+
+ throw new CommunicationException($"Tried to send {ToString(message)}, but channel was {GetChannelState()}!");
+ }
+
+ private bool ChannelIsReady()
+ {
+ return channel != null && (channel as ICommunicationObject).State == CommunicationState.Opened;
+ }
+
+ private void CommunicationHostProxy_Closed(object sender, EventArgs e)
+ {
+ logger.Debug("Communication channel has been closed.");
+ }
+
+ private void CommunicationHostProxy_Closing(object sender, EventArgs e)
+ {
+ logger.Debug("Communication channel is closing.");
+ }
+
+ private void CommunicationHostProxy_Faulted(object sender, EventArgs e)
+ {
+ logger.Error("Communication channel has faulted!");
+ }
+
+ private string GetChannelState()
+ {
+ return channel == null ? "null" : $"in state '{(channel as ICommunicationObject).State}'";
+ }
+
+ private string ToString(IMessage message)
+ {
+ return message != null ? $"message of type '{message.GetType()}'" : "no message";
+ }
+
+ private string ToString(IResponse response)
+ {
+ return response != null ? $"response of type '{response.GetType()}'" : "no response";
+ }
+ }
+}
diff --git a/SafeExamBrowser.Core/Logging/Logger.cs b/SafeExamBrowser.Core/Logging/Logger.cs
index e2a327f0..c5c4aaa6 100644
--- a/SafeExamBrowser.Core/Logging/Logger.cs
+++ b/SafeExamBrowser.Core/Logging/Logger.cs
@@ -21,6 +21,16 @@ namespace SafeExamBrowser.Core.Logging
private readonly IList log = new List();
private readonly IList observers = new List();
+ public void Debug(string message)
+ {
+ if (message == null)
+ {
+ throw new ArgumentNullException(nameof(message));
+ }
+
+ Add(LogLevel.Debug, message);
+ }
+
public void Info(string message)
{
if (message == null)
diff --git a/SafeExamBrowser.Core/Logging/ModuleLogger.cs b/SafeExamBrowser.Core/Logging/ModuleLogger.cs
index 040c9809..7370cf7c 100644
--- a/SafeExamBrowser.Core/Logging/ModuleLogger.cs
+++ b/SafeExamBrowser.Core/Logging/ModuleLogger.cs
@@ -27,6 +27,11 @@ namespace SafeExamBrowser.Core.Logging
this.module = module;
}
+ public void Debug(string message)
+ {
+ logger.Debug(AppendModuleInfo(message));
+ }
+
public void Error(string message)
{
logger.Error(AppendModuleInfo(message));
diff --git a/SafeExamBrowser.Core/SafeExamBrowser.Core.csproj b/SafeExamBrowser.Core/SafeExamBrowser.Core.csproj
index f0b0fcc2..b5f686b1 100644
--- a/SafeExamBrowser.Core/SafeExamBrowser.Core.csproj
+++ b/SafeExamBrowser.Core/SafeExamBrowser.Core.csproj
@@ -50,6 +50,7 @@
+
@@ -57,6 +58,7 @@
+
diff --git a/SafeExamBrowser.Runtime.UnitTests/Behaviour/Operations/ConfigurationOperationTests.cs b/SafeExamBrowser.Runtime.UnitTests/Behaviour/Operations/ConfigurationOperationTests.cs
index bdeab170..eb4b89ea 100644
--- a/SafeExamBrowser.Runtime.UnitTests/Behaviour/Operations/ConfigurationOperationTests.cs
+++ b/SafeExamBrowser.Runtime.UnitTests/Behaviour/Operations/ConfigurationOperationTests.cs
@@ -14,7 +14,6 @@ using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Configuration.Settings;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
-using SafeExamBrowser.Contracts.Runtime;
using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Runtime.Behaviour.Operations;
@@ -24,7 +23,6 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
public class ConfigurationOperationTests
{
private Mock logger;
- private Mock controller;
private Mock info;
private Mock repository;
private Mock settings;
@@ -37,7 +35,6 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
public void Initialize()
{
logger = new Mock();
- controller = new Mock();
info = new Mock();
repository = new Mock();
settings = new Mock();
@@ -55,23 +52,23 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
[TestMethod]
public void MustNotFailWithoutCommandLineArgs()
{
- controller.SetupSet(c => c.Settings = It.IsAny());
+ repository.Setup(r => r.LoadDefaults());
- sut = new ConfigurationOperation(logger.Object, controller.Object, info.Object, repository.Object, text.Object, uiFactory.Object, null)
+ sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, null)
{
SplashScreen = splashScreen.Object
};
sut.Perform();
- sut = new ConfigurationOperation(logger.Object, controller.Object, info.Object, repository.Object, text.Object, uiFactory.Object, new string[] { })
+ sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, new string[] { })
{
SplashScreen = splashScreen.Object
};
sut.Perform();
- controller.VerifySet(c => c.Settings = It.IsAny(), Times.Exactly(2));
+ repository.Verify(r => r.LoadDefaults(), Times.Exactly(2));
}
[TestMethod]
@@ -79,7 +76,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
{
var path = @"an/invalid\path.'*%yolo/()";
- sut = new ConfigurationOperation(logger.Object, controller.Object, info.Object, repository.Object, text.Object, uiFactory.Object, new [] { "blubb.exe", path })
+ sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, new [] { "blubb.exe", path })
{
SplashScreen = splashScreen.Object
};
@@ -96,14 +93,13 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
info.SetupGet(r => r.ProgramDataFolder).Returns(location);
info.SetupGet(r => r.AppDataFolder).Returns(location);
- sut = new ConfigurationOperation(logger.Object, controller.Object, info.Object, repository.Object, text.Object, uiFactory.Object, new[] { "blubb.exe", path })
+ sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, new[] { "blubb.exe", path })
{
SplashScreen = splashScreen.Object
};
sut.Perform();
- controller.VerifySet(c => c.Settings = It.IsAny(), Times.Once);
repository.Verify(r => r.Load(It.Is(u => u.Equals(new Uri(path)))), Times.Once);
}
@@ -115,14 +111,13 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
info.SetupGet(r => r.ProgramDataFolder).Returns(location);
info.SetupGet(r => r.AppDataFolder).Returns($@"{location}\WRONG");
- sut = new ConfigurationOperation(logger.Object, controller.Object, info.Object, repository.Object, text.Object, uiFactory.Object, null)
+ sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, null)
{
SplashScreen = splashScreen.Object
};
sut.Perform();
- controller.VerifySet(c => c.Settings = It.IsAny(), Times.Once);
repository.Verify(r => r.Load(It.Is(u => u.Equals(new Uri(Path.Combine(location, "SettingsDummy.txt"))))), Times.Once);
}
@@ -133,28 +128,26 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
info.SetupGet(r => r.AppDataFolder).Returns(location);
- sut = new ConfigurationOperation(logger.Object, controller.Object, info.Object, repository.Object, text.Object, uiFactory.Object, null)
+ sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, null)
{
SplashScreen = splashScreen.Object
};
sut.Perform();
- controller.VerifySet(c => c.Settings = It.IsAny(), Times.Once);
repository.Verify(r => r.Load(It.Is(u => u.Equals(new Uri(Path.Combine(location, "SettingsDummy.txt"))))), Times.Once);
}
[TestMethod]
public void MustFallbackToDefaultsAsLastPrio()
{
- sut = new ConfigurationOperation(logger.Object, controller.Object, info.Object, repository.Object, text.Object, uiFactory.Object, null)
+ sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, null)
{
SplashScreen = splashScreen.Object
};
sut.Perform();
- controller.VerifySet(c => c.Settings = It.IsAny(), Times.Once);
repository.Verify(r => r.LoadDefaults(), Times.Once);
}
@@ -166,7 +159,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
info.SetupGet(r => r.ProgramDataFolder).Returns(location);
uiFactory.Setup(u => u.Show(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).Returns(MessageBoxResult.Yes);
- sut = new ConfigurationOperation(logger.Object, controller.Object, info.Object, repository.Object, text.Object, uiFactory.Object, null)
+ sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, null)
{
SplashScreen = splashScreen.Object
};
@@ -181,7 +174,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
{
uiFactory.Setup(u => u.Show(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).Returns(MessageBoxResult.No);
- sut = new ConfigurationOperation(logger.Object, controller.Object, info.Object, repository.Object, text.Object, uiFactory.Object, null)
+ sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, null)
{
SplashScreen = splashScreen.Object
};
diff --git a/SafeExamBrowser.Runtime.UnitTests/Behaviour/Operations/ServiceOperationTests.cs b/SafeExamBrowser.Runtime.UnitTests/Behaviour/Operations/ServiceOperationTests.cs
new file mode 100644
index 00000000..10d4df4b
--- /dev/null
+++ b/SafeExamBrowser.Runtime.UnitTests/Behaviour/Operations/ServiceOperationTests.cs
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2018 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 Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
+{
+ [TestClass]
+ public class ServiceOperationTests
+ {
+ [TestMethod]
+ public void Test()
+ {
+ // TODO
+ }
+ }
+}
diff --git a/SafeExamBrowser.Runtime.UnitTests/SafeExamBrowser.Runtime.UnitTests.csproj b/SafeExamBrowser.Runtime.UnitTests/SafeExamBrowser.Runtime.UnitTests.csproj
index 881e3ab3..e3b1a600 100644
--- a/SafeExamBrowser.Runtime.UnitTests/SafeExamBrowser.Runtime.UnitTests.csproj
+++ b/SafeExamBrowser.Runtime.UnitTests/SafeExamBrowser.Runtime.UnitTests.csproj
@@ -82,6 +82,7 @@
+
diff --git a/SafeExamBrowser.Runtime/App.cs b/SafeExamBrowser.Runtime/App.cs
index e1ff1797..f7118da0 100644
--- a/SafeExamBrowser.Runtime/App.cs
+++ b/SafeExamBrowser.Runtime/App.cs
@@ -60,7 +60,7 @@ namespace SafeExamBrowser.Runtime
base.OnStartup(e);
instances.BuildObjectGraph();
- LogStartupInformation();
+ instances.LogStartupInformation();
var success = instances.StartupController.TryInitializeApplication(instances.StartupOperations);
@@ -79,27 +79,11 @@ namespace SafeExamBrowser.Runtime
protected override void OnExit(ExitEventArgs e)
{
- instances.Logger?.Log($"{Environment.NewLine}# Application terminated at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
+ instances.LogShutdownInformation();
base.OnExit(e);
}
- private void LogStartupInformation()
- {
- var runtimeInfo = instances.RuntimeInfo;
- var logger = instances.Logger;
- var titleLine = $"/* {runtimeInfo.ProgramTitle}, Version {runtimeInfo.ProgramVersion}{Environment.NewLine}";
- var copyrightLine = $"/* {runtimeInfo.ProgramCopyright}{Environment.NewLine}";
- var emptyLine = $"/* {Environment.NewLine}";
- var githubLine = $"/* Please visit https://github.com/SafeExamBrowser for more information.";
-
- logger.Log($"{titleLine}{copyrightLine}{emptyLine}{githubLine}");
- logger.Log(string.Empty);
- logger.Log($"# Application started at {runtimeInfo.ApplicationStartTime.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
- logger.Log($"# Running on {instances.SystemInfo.OperatingSystemInfo}");
- logger.Log(string.Empty);
- }
-
private void MainWindow_Closing(object sender, CancelEventArgs e)
{
var operations = new Queue(instances.StartupOperations.Reverse());
diff --git a/SafeExamBrowser.Runtime/Behaviour/Operations/ConfigurationOperation.cs b/SafeExamBrowser.Runtime/Behaviour/Operations/ConfigurationOperation.cs
index 85a8401b..90c92195 100644
--- a/SafeExamBrowser.Runtime/Behaviour/Operations/ConfigurationOperation.cs
+++ b/SafeExamBrowser.Runtime/Behaviour/Operations/ConfigurationOperation.cs
@@ -13,7 +13,6 @@ using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Configuration.Settings;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
-using SafeExamBrowser.Contracts.Runtime;
using SafeExamBrowser.Contracts.UserInterface;
namespace SafeExamBrowser.Runtime.Behaviour.Operations
@@ -21,7 +20,6 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
internal class ConfigurationOperation : IOperation
{
private ILogger logger;
- private IRuntimeController controller;
private IRuntimeInfo runtimeInfo;
private ISettingsRepository repository;
private IText text;
@@ -33,7 +31,6 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
public ConfigurationOperation(
ILogger logger,
- IRuntimeController controller,
IRuntimeInfo runtimeInfo,
ISettingsRepository repository,
IText text,
@@ -41,7 +38,6 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
string[] commandLineArgs)
{
this.logger = logger;
- this.controller = controller;
this.commandLineArgs = commandLineArgs;
this.repository = repository;
this.runtimeInfo = runtimeInfo;
@@ -62,11 +58,10 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
logger.Info($"Loading configuration from '{uri.AbsolutePath}'...");
settings = repository.Load(uri);
- if (settings.ConfigurationMode == ConfigurationMode.ConfigureClient && Abort())
+ if (settings.ConfigurationMode == ConfigurationMode.ConfigureClient && UserWantsToAbortStartup())
{
AbortStartup = true;
-
- return;
+ logger.Info($"The user chose to {(AbortStartup ? "abort" : "continue")} the application startup after successful client configuration.");
}
}
else
@@ -74,8 +69,6 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
logger.Info("No valid settings file specified nor found in PROGRAMDATA or APPDATA - loading default settings...");
settings = repository.LoadDefaults();
}
-
- controller.Settings = settings;
}
public void Revert()
@@ -116,23 +109,13 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
return isValidUri;
}
- private bool Abort()
+ private bool UserWantsToAbortStartup()
{
var message = text.Get(TextKey.MessageBox_ConfigureClientSuccess);
var title = text.Get(TextKey.MessageBox_ConfigureClientSuccessTitle);
- var quitDialogResult = uiFactory.Show(message, title, MessageBoxAction.YesNo, MessageBoxIcon.Question);
- var abort = quitDialogResult == MessageBoxResult.Yes;
+ var abort = uiFactory.Show(message, title, MessageBoxAction.YesNo, MessageBoxIcon.Question);
- if (abort)
- {
- logger.Info("The user chose to terminate the application after successful client configuration.");
- }
- else
- {
- logger.Info("The user chose to continue starting up the application after successful client configuration.");
- }
-
- return abort;
+ return abort == MessageBoxResult.Yes;
}
}
}
diff --git a/SafeExamBrowser.Runtime/Behaviour/Operations/ServiceOperation.cs b/SafeExamBrowser.Runtime/Behaviour/Operations/ServiceOperation.cs
new file mode 100644
index 00000000..531cc21f
--- /dev/null
+++ b/SafeExamBrowser.Runtime/Behaviour/Operations/ServiceOperation.cs
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2018 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 SafeExamBrowser.Contracts.Behaviour;
+using SafeExamBrowser.Contracts.Communication;
+using SafeExamBrowser.Contracts.Configuration.Settings;
+using SafeExamBrowser.Contracts.Logging;
+using SafeExamBrowser.Contracts.UserInterface;
+
+namespace SafeExamBrowser.Runtime.Behaviour.Operations
+{
+ internal class ServiceOperation : IOperation
+ {
+ private ICommunicationHost serviceHost;
+ private ILogger logger;
+ private ISettingsRepository settingsRepository;
+
+ public bool AbortStartup { get; private set; }
+ public ISplashScreen SplashScreen { private get; set; }
+
+ public ServiceOperation(ICommunicationHost serviceHost, ILogger logger, ISettingsRepository settingsRepository)
+ {
+ this.serviceHost = serviceHost;
+ this.logger = logger;
+ this.settingsRepository = settingsRepository;
+ }
+
+ public void Perform()
+ {
+ logger.Info("Initializing service connection...");
+ // SplashScreen.UpdateText(...)
+
+ // TODO
+ }
+
+ public void Revert()
+ {
+ // TODO
+ }
+ }
+}
diff --git a/SafeExamBrowser.Runtime/CompositionRoot.cs b/SafeExamBrowser.Runtime/CompositionRoot.cs
index 2535dab5..02ed3b77 100644
--- a/SafeExamBrowser.Runtime/CompositionRoot.cs
+++ b/SafeExamBrowser.Runtime/CompositionRoot.cs
@@ -17,6 +17,7 @@ using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Core.Behaviour;
using SafeExamBrowser.Core.Behaviour.Operations;
+using SafeExamBrowser.Core.Communication;
using SafeExamBrowser.Core.I18n;
using SafeExamBrowser.Core.Logging;
using SafeExamBrowser.Runtime.Behaviour;
@@ -28,11 +29,12 @@ namespace SafeExamBrowser.Runtime
{
internal class CompositionRoot
{
- internal ILogger Logger { get; private set; }
- internal RuntimeInfo RuntimeInfo { get; private set; }
+ private ILogger logger;
+ private RuntimeInfo runtimeInfo;
+ private ISystemInfo systemInfo;
+
internal IShutdownController ShutdownController { get; private set; }
internal IStartupController StartupController { get; private set; }
- internal ISystemInfo SystemInfo { get; private set; }
internal Queue StartupOperations { get; private set; }
internal void BuildObjectGraph()
@@ -42,24 +44,45 @@ namespace SafeExamBrowser.Runtime
var settingsRepository = new SettingsRepository();
var uiFactory = new UserInterfaceFactory();
- Logger = new Logger();
- RuntimeInfo = new RuntimeInfo();
- SystemInfo = new SystemInfo();
+ logger = new Logger();
+ runtimeInfo = new RuntimeInfo();
+ systemInfo = new SystemInfo();
InitializeRuntimeInfo();
InitializeLogging();
- var text = new Text(Logger);
- var runtimeController = new RuntimeController(new ModuleLogger(Logger, typeof(RuntimeController)));
+ var text = new Text(logger);
+ var runtimeController = new RuntimeController(new ModuleLogger(logger, typeof(RuntimeController)));
+ var serviceProxy = new CommunicationHostProxy(new ModuleLogger(logger, typeof(CommunicationHostProxy)), "net.pipe://localhost/safeexambrowser/service");
- ShutdownController = new ShutdownController(Logger, RuntimeInfo, text, uiFactory);
- StartupController = new StartupController(Logger, RuntimeInfo, SystemInfo, text, uiFactory);
+ ShutdownController = new ShutdownController(logger, runtimeInfo, text, uiFactory);
+ StartupController = new StartupController(logger, runtimeInfo, systemInfo, text, uiFactory);
StartupOperations = new Queue();
- StartupOperations.Enqueue(new I18nOperation(Logger, text));
- StartupOperations.Enqueue(new ConfigurationOperation(Logger, runtimeController, RuntimeInfo, settingsRepository, text, uiFactory, args));
+ StartupOperations.Enqueue(new I18nOperation(logger, text));
+ StartupOperations.Enqueue(new ConfigurationOperation(logger, runtimeInfo, settingsRepository, text, uiFactory, args));
+ StartupOperations.Enqueue(new ServiceOperation(serviceProxy, logger, settingsRepository));
//StartupOperations.Enqueue(new KioskModeOperation());
- StartupOperations.Enqueue(new RuntimeControllerOperation(runtimeController, Logger));
+ StartupOperations.Enqueue(new RuntimeControllerOperation(runtimeController, logger));
+ }
+
+ internal void LogStartupInformation()
+ {
+ var titleLine = $"/* {runtimeInfo.ProgramTitle}, Version {runtimeInfo.ProgramVersion}{Environment.NewLine}";
+ var copyrightLine = $"/* {runtimeInfo.ProgramCopyright}{Environment.NewLine}";
+ var emptyLine = $"/* {Environment.NewLine}";
+ var githubLine = $"/* Please visit https://www.github.com/SafeExamBrowser for more information.";
+
+ logger.Log($"{titleLine}{copyrightLine}{emptyLine}{githubLine}");
+ logger.Log(string.Empty);
+ logger.Log($"# Application started at {runtimeInfo.ApplicationStartTime.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
+ logger.Log($"# Running on {systemInfo.OperatingSystemInfo}");
+ logger.Log(string.Empty);
+ }
+
+ internal void LogShutdownInformation()
+ {
+ logger?.Log($"{Environment.NewLine}# Application terminated at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
}
private void InitializeRuntimeInfo()
@@ -70,25 +93,25 @@ namespace SafeExamBrowser.Runtime
var logFolder = Path.Combine(appDataFolder, "Logs");
var logFilePrefix = startTime.ToString("yyyy-MM-dd\\_HH\\hmm\\mss\\s");
- RuntimeInfo.ApplicationStartTime = startTime;
- RuntimeInfo.AppDataFolder = appDataFolder;
- RuntimeInfo.BrowserCachePath = Path.Combine(appDataFolder, "Cache");
- RuntimeInfo.BrowserLogFile = Path.Combine(logFolder, $"{logFilePrefix}_Browser.txt");
- RuntimeInfo.ClientLogFile = Path.Combine(logFolder, $"{logFilePrefix}_Client.txt");
- RuntimeInfo.DefaultSettingsFileName = "SebClientSettings.seb";
- RuntimeInfo.ProgramCopyright = executable.GetCustomAttribute().Copyright;
- RuntimeInfo.ProgramDataFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), nameof(SafeExamBrowser));
- RuntimeInfo.ProgramTitle = executable.GetCustomAttribute().Title;
- RuntimeInfo.ProgramVersion = executable.GetCustomAttribute().InformationalVersion;
- RuntimeInfo.RuntimeLogFile = Path.Combine(logFolder, $"{logFilePrefix}_Runtime.txt");
+ runtimeInfo.ApplicationStartTime = startTime;
+ runtimeInfo.AppDataFolder = appDataFolder;
+ runtimeInfo.BrowserCachePath = Path.Combine(appDataFolder, "Cache");
+ runtimeInfo.BrowserLogFile = Path.Combine(logFolder, $"{logFilePrefix}_Browser.txt");
+ runtimeInfo.ClientLogFile = Path.Combine(logFolder, $"{logFilePrefix}_Client.txt");
+ runtimeInfo.DefaultSettingsFileName = "SebClientSettings.seb";
+ runtimeInfo.ProgramCopyright = executable.GetCustomAttribute().Copyright;
+ runtimeInfo.ProgramDataFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), nameof(SafeExamBrowser));
+ runtimeInfo.ProgramTitle = executable.GetCustomAttribute().Title;
+ runtimeInfo.ProgramVersion = executable.GetCustomAttribute().InformationalVersion;
+ runtimeInfo.RuntimeLogFile = Path.Combine(logFolder, $"{logFilePrefix}_Runtime.txt");
}
private void InitializeLogging()
{
- var logFileWriter = new LogFileWriter(new DefaultLogFormatter(), RuntimeInfo.RuntimeLogFile);
+ var logFileWriter = new LogFileWriter(new DefaultLogFormatter(), runtimeInfo.RuntimeLogFile);
logFileWriter.Initialize();
- Logger.Subscribe(logFileWriter);
+ logger.Subscribe(logFileWriter);
}
}
}
diff --git a/SafeExamBrowser.Runtime/SafeExamBrowser.Runtime.csproj b/SafeExamBrowser.Runtime/SafeExamBrowser.Runtime.csproj
index 2f587880..2b04d1f4 100644
--- a/SafeExamBrowser.Runtime/SafeExamBrowser.Runtime.csproj
+++ b/SafeExamBrowser.Runtime/SafeExamBrowser.Runtime.csproj
@@ -89,6 +89,7 @@
+
Code