SEBWIN-219: Implemented draft of communication operation and runtime communication host.

This commit is contained in:
dbuechel 2018-02-06 15:12:11 +01:00
parent c10e141e7f
commit 8cd0659a22
32 changed files with 626 additions and 170 deletions

View file

@ -58,7 +58,7 @@ namespace SafeExamBrowser.Client
browserInfo = new BrowserApplicationInfo(); browserInfo = new BrowserApplicationInfo();
logger = new Logger(); logger = new Logger();
nativeMethods = new NativeMethods(); nativeMethods = new NativeMethods();
settings = new SettingsRepository().LoadDefaults(); settings = new ConfigurationRepository().LoadDefaultSettings();
systemInfo = new SystemInfo(); systemInfo = new SystemInfo();
uiFactory = new UserInterfaceFactory(); uiFactory = new UserInterfaceFactory();

View file

@ -0,0 +1,87 @@
/*
* 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.IO;
using System.Reflection;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Configuration.Settings;
namespace SafeExamBrowser.Configuration
{
public class ConfigurationRepository : IConfigurationRepository
{
private RuntimeInfo runtimeInfo;
public ISettings CurrentSettings { get; private set; }
public IRuntimeInfo RuntimeInfo
{
get
{
if (runtimeInfo == null)
{
InitializeRuntimeInfo();
}
return runtimeInfo;
}
}
public ISettings LoadSettings(Uri path)
{
// TODO
return LoadDefaultSettings();
}
public ISettings LoadDefaultSettings()
{
var settings = new Settings.Settings();
// TODO
settings.ServicePolicy = ServicePolicy.Optional;
CurrentSettings = settings;
return settings;
}
private void InitializeRuntimeInfo()
{
var appDataFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), nameof(SafeExamBrowser));
var baseAddress = "net.pipe://localhost/safeexambrowser";
var clientId = Guid.NewGuid();
var executable = Assembly.GetEntryAssembly();
var runtimeId = Guid.NewGuid();
var startTime = DateTime.Now;
var logFolder = Path.Combine(appDataFolder, "Logs");
var logFilePrefix = startTime.ToString("yyyy-MM-dd\\_HH\\hmm\\mss\\s");
runtimeInfo = new RuntimeInfo
{
ApplicationStartTime = startTime,
AppDataFolder = appDataFolder,
BrowserCachePath = Path.Combine(appDataFolder, "Cache"),
BrowserLogFile = Path.Combine(logFolder, $"{logFilePrefix}_Browser.txt"),
ClientId = Guid.NewGuid(),
ClientAddress = $"{baseAddress}/client/{clientId}",
ClientLogFile = Path.Combine(logFolder, $"{logFilePrefix}_Client.txt"),
DefaultSettingsFileName = "SebClientSettings.seb",
ProgramCopyright = executable.GetCustomAttribute<AssemblyCopyrightAttribute>().Copyright,
ProgramDataFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), nameof(SafeExamBrowser)),
ProgramTitle = executable.GetCustomAttribute<AssemblyTitleAttribute>().Title,
ProgramVersion = executable.GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion,
RuntimeId = Guid.NewGuid(),
RuntimeAddress = $"{baseAddress}/runtime/{runtimeId}",
RuntimeLogFile = Path.Combine(logFolder, $"{logFilePrefix}_Runtime.txt"),
ServiceAddress = $"{baseAddress}/service"
};
}
}
}

View file

@ -18,12 +18,17 @@ namespace SafeExamBrowser.Configuration
public DateTime ApplicationStartTime { get; set; } public DateTime ApplicationStartTime { get; set; }
public string BrowserCachePath { get; set; } public string BrowserCachePath { get; set; }
public string BrowserLogFile { get; set; } public string BrowserLogFile { get; set; }
public string ClientAddress { get; set; }
public Guid ClientId { get; set; }
public string ClientLogFile { get; set; } public string ClientLogFile { get; set; }
public string DefaultSettingsFileName { get; set; } public string DefaultSettingsFileName { get; set; }
public string ProgramCopyright { get; set; } public string ProgramCopyright { get; set; }
public string ProgramDataFolder { get; set; } public string ProgramDataFolder { get; set; }
public string ProgramTitle { get; set; } public string ProgramTitle { get; set; }
public string ProgramVersion { get; set; } public string ProgramVersion { get; set; }
public string RuntimeAddress { get; set; }
public Guid RuntimeId { get; set; }
public string RuntimeLogFile { get; set; } public string RuntimeLogFile { get; set; }
public string ServiceAddress { get; set; }
} }
} }

View file

@ -59,7 +59,7 @@
<Compile Include="Settings\MouseSettings.cs" /> <Compile Include="Settings\MouseSettings.cs" />
<Compile Include="Settings\Settings.cs" /> <Compile Include="Settings\Settings.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Settings\SettingsRepository.cs" /> <Compile Include="ConfigurationRepository.cs" />
<Compile Include="SystemInfo.cs" /> <Compile Include="SystemInfo.cs" />
<Compile Include="Settings\TaskbarSettings.cs" /> <Compile Include="Settings\TaskbarSettings.cs" />
</ItemGroup> </ItemGroup>

View file

@ -1,37 +0,0 @@
/*
* 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 SafeExamBrowser.Contracts.Configuration.Settings;
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();
// TODO
settings.ServicePolicy = ServicePolicy.Optional;
Current = settings;
return settings;
}
}
}

View file

@ -6,6 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
namespace SafeExamBrowser.Contracts.Behaviour namespace SafeExamBrowser.Contracts.Behaviour
{ {
public interface IRuntimeController public interface IRuntimeController

View file

@ -0,0 +1,28 @@
/*
* 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/.
*/
namespace SafeExamBrowser.Contracts.Communication
{
public interface ICommunicationHost
{
/// <summary>
/// Indicates whether the host is running and ready for communication.
/// </summary>
bool IsRunning { get; }
/// <summary>
/// Starts the host and opens it for communication.
/// </summary>
void Start();
/// <summary>
/// Closes and terminates the host.
/// </summary>
void Stop();
}
}

View file

@ -0,0 +1,15 @@
/*
* 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/.
*/
namespace SafeExamBrowser.Contracts.Communication
{
public interface IRuntimeHost : ICommunicationHost
{
}
}

View file

@ -7,26 +7,32 @@
*/ */
using System; using System;
using SafeExamBrowser.Contracts.Configuration.Settings;
namespace SafeExamBrowser.Contracts.Configuration.Settings namespace SafeExamBrowser.Contracts.Configuration
{ {
public interface ISettingsRepository public interface IConfigurationRepository
{ {
/// <summary> /// <summary>
/// Retrieves the current settings, i.e. the last ones which were loaded. If no settings have been loaded yet, this property will /// Retrieves the current settings, i.e. the last ones which were loaded. If no settings have been loaded yet, this property will
/// be <c>null</c>. /// be <c>null</c>!
/// </summary> /// </summary>
ISettings Current { get; } ISettings CurrentSettings { get; }
/// <summary>
/// The runtime information for the currently running application instance.
/// </summary>
IRuntimeInfo RuntimeInfo { get; }
/// <summary> /// <summary>
/// Attempts to load settings from the specified path. /// Attempts to load settings from the specified path.
/// </summary> /// </summary>
/// <exception cref="System.ArgumentException">Thrown if the given path cannot be resolved to a settings file.</exception> /// <exception cref="ArgumentException">Thrown if the given path cannot be resolved to a settings file.</exception>
ISettings Load(Uri path); ISettings LoadSettings(Uri path);
/// <summary> /// <summary>
/// Loads the default settings. /// Loads the default settings.
/// </summary> /// </summary>
ISettings LoadDefaults(); ISettings LoadDefaultSettings();
} }
} }

View file

@ -32,6 +32,22 @@ namespace SafeExamBrowser.Contracts.Configuration
/// </summary> /// </summary>
string BrowserLogFile { get; } string BrowserLogFile { get; }
/// <summary>
/// The communication address of the client component.
///
/// TODO: Will need to be updated for each new client instance!
///
/// </summary>
string ClientAddress { get; }
/// <summary>
/// The unique identifier for the currently running client instance.
///
/// TODO: Will need to be updated for each new client instance!
///
/// </summary>
Guid ClientId { get; }
/// <summary> /// <summary>
/// The file path under which the log of the client component is to be stored. /// The file path under which the log of the client component is to be stored.
/// </summary> /// </summary>
@ -62,9 +78,24 @@ namespace SafeExamBrowser.Contracts.Configuration
/// </summary> /// </summary>
string ProgramVersion { get; } string ProgramVersion { get; }
/// <summary>
/// The communication address of the runtime component.
/// </summary>
string RuntimeAddress { get; }
/// <summary>
/// The unique identifier for the currently running runtime instance.
/// </summary>
Guid RuntimeId { get; }
/// <summary> /// <summary>
/// The file path under which the log of the runtime component is to be stored. /// The file path under which the log of the runtime component is to be stored.
/// </summary> /// </summary>
string RuntimeLogFile { get; } string RuntimeLogFile { get; }
/// <summary>
/// The communication address of the service component.
/// </summary>
string ServiceAddress { get; }
} }
} }

View file

@ -35,12 +35,15 @@ namespace SafeExamBrowser.Contracts.I18n
ProgressIndicator_InitializeTaskbar, ProgressIndicator_InitializeTaskbar,
ProgressIndicator_InitializeWindowMonitoring, ProgressIndicator_InitializeWindowMonitoring,
ProgressIndicator_InitializeWorkingArea, ProgressIndicator_InitializeWorkingArea,
ProgressIndicator_RestartCommunicationHost,
ProgressIndicator_RestoreWorkingArea, ProgressIndicator_RestoreWorkingArea,
ProgressIndicator_RevertKioskMode, ProgressIndicator_RevertKioskMode,
ProgressIndicator_ShutdownProcedure, ProgressIndicator_ShutdownProcedure,
ProgressIndicator_StartCommunicationHost,
ProgressIndicator_StartEventHandling, ProgressIndicator_StartEventHandling,
ProgressIndicator_StartKeyboardInterception, ProgressIndicator_StartKeyboardInterception,
ProgressIndicator_StartMouseInterception, ProgressIndicator_StartMouseInterception,
ProgressIndicator_StopCommunicationHost,
ProgressIndicator_StopEventHandling, ProgressIndicator_StopEventHandling,
ProgressIndicator_StopKeyboardInterception, ProgressIndicator_StopKeyboardInterception,
ProgressIndicator_StopMouseInterception, ProgressIndicator_StopMouseInterception,

View file

@ -58,6 +58,8 @@
<Compile Include="Behaviour\Operations\IOperationSequence.cs" /> <Compile Include="Behaviour\Operations\IOperationSequence.cs" />
<Compile Include="Communication\ICommunication.cs" /> <Compile Include="Communication\ICommunication.cs" />
<Compile Include="Communication\IClientProxy.cs" /> <Compile Include="Communication\IClientProxy.cs" />
<Compile Include="Communication\ICommunicationHost.cs" />
<Compile Include="Communication\IRuntimeHost.cs" />
<Compile Include="Communication\IRuntimeProxy.cs" /> <Compile Include="Communication\IRuntimeProxy.cs" />
<Compile Include="Communication\IServiceProxy.cs" /> <Compile Include="Communication\IServiceProxy.cs" />
<Compile Include="Communication\Messages\IMessage.cs" /> <Compile Include="Communication\Messages\IMessage.cs" />
@ -78,7 +80,7 @@
<Compile Include="Configuration\Settings\IKeyboardSettings.cs" /> <Compile Include="Configuration\Settings\IKeyboardSettings.cs" />
<Compile Include="Configuration\Settings\IMouseSettings.cs" /> <Compile Include="Configuration\Settings\IMouseSettings.cs" />
<Compile Include="Configuration\Settings\ISettings.cs" /> <Compile Include="Configuration\Settings\ISettings.cs" />
<Compile Include="Configuration\Settings\ISettingsRepository.cs" /> <Compile Include="Configuration\IConfigurationRepository.cs" />
<Compile Include="Configuration\Settings\ITaskbarSettings.cs" /> <Compile Include="Configuration\Settings\ITaskbarSettings.cs" />
<Compile Include="Configuration\Settings\KioskMode.cs" /> <Compile Include="Configuration\Settings\KioskMode.cs" />
<Compile Include="Configuration\Settings\ServicePolicy.cs" /> <Compile Include="Configuration\Settings\ServicePolicy.cs" />

View file

@ -0,0 +1,70 @@
/*
* 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;
using Moq;
using SafeExamBrowser.Contracts.Communication;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Core.Behaviour.Operations;
namespace SafeExamBrowser.Core.UnitTests.Behaviour.Operations
{
[TestClass]
public class CommunicationOperationTests
{
private Mock<ICommunicationHost> hostMock;
private Mock<ILogger> loggerMock;
private CommunicationOperation sut;
[TestInitialize]
public void Initialize()
{
hostMock = new Mock<ICommunicationHost>();
loggerMock = new Mock<ILogger>();
sut = new CommunicationOperation(hostMock.Object, loggerMock.Object);
}
[TestMethod]
public void MustRestartHostOnRepeat()
{
var order = 0;
var stop = 0;
var start = 0;
hostMock.Setup(h => h.Stop()).Callback(() => stop = ++order);
hostMock.Setup(h => h.Start()).Callback(() => start = ++order);
sut.Repeat();
hostMock.Verify(h => h.Stop(), Times.Once);
hostMock.Verify(h => h.Start(), Times.Once);
Assert.AreEqual(stop, 1);
Assert.AreEqual(start, 2);
}
[TestMethod]
public void MustStartHostOnPerform()
{
sut.Perform();
hostMock.Verify(h => h.Start(), Times.Once);
hostMock.Verify(h => h.Stop(), Times.Never);
}
[TestMethod]
public void MustStopHostOnRevert()
{
sut.Revert();
hostMock.Verify(h => h.Stop(), Times.Once);
hostMock.Verify(h => h.Start(), Times.Never);
}
}
}

View file

@ -28,6 +28,29 @@ namespace SafeExamBrowser.Core.UnitTests.Behaviour.Operations
loggerMock = new Mock<ILogger>(); loggerMock = new Mock<ILogger>();
} }
[TestMethod]
public void MustCreateCopyOfOperationQueue()
{
var operationA = new Mock<IOperation>();
var operationB = new Mock<IOperation>();
var operationC = new Mock<IOperation>();
var operations = new Queue<IOperation>();
operations.Enqueue(operationA.Object);
operations.Enqueue(operationB.Object);
operations.Enqueue(operationC.Object);
var sut = new OperationSequence(loggerMock.Object, operations);
operations.Clear();
sut.TryPerform();
operationA.Verify(o => o.Perform(), Times.Once);
operationB.Verify(o => o.Perform(), Times.Once);
operationC.Verify(o => o.Perform(), Times.Once);
}
#region Perform Tests #region Perform Tests
[TestMethod] [TestMethod]

View file

@ -78,6 +78,7 @@
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Behaviour\Operations\CommunicationOperationTests.cs" />
<Compile Include="Behaviour\Operations\I18nOperationTests.cs" /> <Compile Include="Behaviour\Operations\I18nOperationTests.cs" />
<Compile Include="Behaviour\Operations\OperationSequenceTests.cs" /> <Compile Include="Behaviour\Operations\OperationSequenceTests.cs" />
<Compile Include="I18n\TextTests.cs" /> <Compile Include="I18n\TextTests.cs" />

View file

@ -0,0 +1,59 @@
/*
* 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.Operations;
using SafeExamBrowser.Contracts.Communication;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface;
namespace SafeExamBrowser.Core.Behaviour.Operations
{
public class CommunicationOperation : IOperation
{
private ICommunicationHost host;
private ILogger logger;
public bool Abort { get; private set; }
public IProgressIndicator ProgressIndicator { private get; set; }
public CommunicationOperation(ICommunicationHost host, ILogger logger)
{
this.host = host;
this.logger = logger;
}
public void Perform()
{
logger.Info("Starting communication host...");
ProgressIndicator?.UpdateText(TextKey.ProgressIndicator_StartCommunicationHost);
host.Start();
}
public void Repeat()
{
if (!host.IsRunning)
{
logger.Info("Restarting communication host...");
ProgressIndicator?.UpdateText(TextKey.ProgressIndicator_RestartCommunicationHost);
host.Stop();
host.Start();
}
}
public void Revert()
{
logger.Info("Stopping communication host...");
ProgressIndicator?.UpdateText(TextKey.ProgressIndicator_StopCommunicationHost);
host.Stop();
}
}
}

View file

@ -40,7 +40,7 @@ namespace SafeExamBrowser.Core.Behaviour.Operations
if (!success) if (!success)
{ {
Revert(); Revert(true);
} }
} }
catch (Exception e) catch (Exception e)
@ -74,8 +74,8 @@ namespace SafeExamBrowser.Core.Behaviour.Operations
try try
{ {
Initialize(); Initialize(true);
success = Revert(false); success = Revert();
} }
catch (Exception e) catch (Exception e)
{ {
@ -154,7 +154,7 @@ namespace SafeExamBrowser.Core.Behaviour.Operations
return true; return true;
} }
private bool Revert(bool regress = true) private bool Revert(bool regress = false)
{ {
var success = true; var success = true;

View file

@ -0,0 +1,91 @@
/*
* 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
{
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Single)]
public abstract class BaseHost : ICommunication, ICommunicationHost
{
private string address;
private ILogger logger;
private ServiceHost host;
public bool IsRunning
{
get { return host?.State == CommunicationState.Opened; }
}
public BaseHost(string address, ILogger logger)
{
this.address = address;
this.logger = logger;
}
public abstract IConnectResponse Connect(Guid? token = null);
public abstract void Disconnect(IMessage message);
public abstract IResponse Send(IMessage message);
public void Start()
{
host = new ServiceHost(this);
host.AddServiceEndpoint(typeof(ICommunication), new NetNamedPipeBinding(NetNamedPipeSecurityMode.Transport), address);
host.Closed += Host_Closed;
host.Closing += Host_Closing;
host.Faulted += Host_Faulted;
host.Opened += Host_Opened;
host.Opening += Host_Opening;
host.UnknownMessageReceived += Host_UnknownMessageReceived;
host.Open();
logger.Debug($"Successfully started communication host for endpoint '{address}'.");
}
public void Stop()
{
host?.Close();
logger.Debug($"Terminated communication host for endpoint '{address}'.");
}
private void Host_Closed(object sender, EventArgs e)
{
logger.Debug("Communication host has been closed.");
}
private void Host_Closing(object sender, EventArgs e)
{
logger.Debug("Communication host is closing...");
}
private void Host_Faulted(object sender, EventArgs e)
{
logger.Debug("Communication host has faulted!");
}
private void Host_Opened(object sender, EventArgs e)
{
logger.Debug("Communication host has been opened.");
}
private void Host_Opening(object sender, EventArgs e)
{
logger.Debug("Communication host is opening...");
}
private void Host_UnknownMessageReceived(object sender, UnknownMessageReceivedEventArgs e)
{
logger.Debug($"Communication host has received an unknown message: {e?.Message}.");
}
}
}

View file

@ -23,7 +23,7 @@ namespace SafeExamBrowser.Core.Communication
protected Guid? CommunicationToken { get; private set; } protected Guid? CommunicationToken { get; private set; }
protected ILogger Logger { get; private set; } protected ILogger Logger { get; private set; }
public BaseProxy(ILogger logger, string address) public BaseProxy(string address, ILogger logger)
{ {
this.address = address; this.address = address;
this.Logger = logger; this.Logger = logger;
@ -34,9 +34,11 @@ namespace SafeExamBrowser.Core.Communication
var endpoint = new EndpointAddress(address); var endpoint = new EndpointAddress(address);
channel = ChannelFactory<ICommunication>.CreateChannel(new NetNamedPipeBinding(NetNamedPipeSecurityMode.Transport), endpoint); channel = ChannelFactory<ICommunication>.CreateChannel(new NetNamedPipeBinding(NetNamedPipeSecurityMode.Transport), endpoint);
(channel as ICommunicationObject).Closed += CommunicationHostProxy_Closed; (channel as ICommunicationObject).Closed += BaseProxy_Closed;
(channel as ICommunicationObject).Closing += CommunicationHostProxy_Closing; (channel as ICommunicationObject).Closing += BaseProxy_Closing;
(channel as ICommunicationObject).Faulted += CommunicationHostProxy_Faulted; (channel as ICommunicationObject).Faulted += BaseProxy_Faulted;
(channel as ICommunicationObject).Opened += BaseProxy_Opened;
(channel as ICommunicationObject).Opening += BaseProxy_Opening;
var response = channel.Connect(token); var response = channel.Connect(token);
@ -84,21 +86,31 @@ namespace SafeExamBrowser.Core.Communication
return channel != null && (channel as ICommunicationObject).State == CommunicationState.Opened; return channel != null && (channel as ICommunicationObject).State == CommunicationState.Opened;
} }
private void CommunicationHostProxy_Closed(object sender, EventArgs e) private void BaseProxy_Closed(object sender, EventArgs e)
{ {
Logger.Debug("Communication channel has been closed."); Logger.Debug("Communication channel has been closed.");
} }
private void CommunicationHostProxy_Closing(object sender, EventArgs e) private void BaseProxy_Closing(object sender, EventArgs e)
{ {
Logger.Debug("Communication channel is closing."); Logger.Debug("Communication channel is closing...");
} }
private void CommunicationHostProxy_Faulted(object sender, EventArgs e) private void BaseProxy_Faulted(object sender, EventArgs e)
{ {
Logger.Debug("Communication channel has faulted!"); Logger.Debug("Communication channel has faulted!");
} }
private void BaseProxy_Opened(object sender, EventArgs e)
{
Logger.Debug("Communication channel has been opened.");
}
private void BaseProxy_Opening(object sender, EventArgs e)
{
Logger.Debug("Communication channel is opening...");
}
private string GetChannelState() private string GetChannelState()
{ {
return channel == null ? "null" : $"in state '{(channel as ICommunicationObject).State}'"; return channel == null ? "null" : $"in state '{(channel as ICommunicationObject).State}'";

View file

@ -16,7 +16,7 @@ namespace SafeExamBrowser.Core.Communication
{ {
public bool Ignore { private get; set; } public bool Ignore { private get; set; }
public ServiceProxy(ILogger logger, string address) : base(logger, address) public ServiceProxy(string address, ILogger logger) : base(address, logger)
{ {
} }

View file

@ -60,6 +60,9 @@
<Entry key="ProgressIndicator_InitializeWorkingArea"> <Entry key="ProgressIndicator_InitializeWorkingArea">
Initializing working area Initializing working area
</Entry> </Entry>
<Entry key="ProgressIndicator_RestartCommunicationHost">
Restarting communication host
</Entry>
<Entry key="ProgressIndicator_RestoreWorkingArea"> <Entry key="ProgressIndicator_RestoreWorkingArea">
Restoring working area Restoring working area
</Entry> </Entry>
@ -69,6 +72,9 @@
<Entry key="ProgressIndicator_ShutdownProcedure"> <Entry key="ProgressIndicator_ShutdownProcedure">
Initiating shutdown procedure Initiating shutdown procedure
</Entry> </Entry>
<Entry key="ProgressIndicator_StartCommunicationHost">
Starting communication host
</Entry>
<Entry key="ProgressIndicator_StartEventHandling"> <Entry key="ProgressIndicator_StartEventHandling">
Starting event handling Starting event handling
</Entry> </Entry>
@ -78,6 +84,9 @@
<Entry key="ProgressIndicator_StartMouseInterception"> <Entry key="ProgressIndicator_StartMouseInterception">
Starting mouse interception Starting mouse interception
</Entry> </Entry>
<Entry key="ProgressIndicator_StopCommunicationHost">
Stopping communication host
</Entry>
<Entry key="ProgressIndicator_StopEventHandling"> <Entry key="ProgressIndicator_StopEventHandling">
Stopping event handling Stopping event handling
</Entry> </Entry>

View file

@ -55,9 +55,11 @@
<Reference Include="System.Xml.Linq" /> <Reference Include="System.Xml.Linq" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Behaviour\Operations\CommunicationOperation.cs" />
<Compile Include="Behaviour\Operations\I18nOperation.cs" /> <Compile Include="Behaviour\Operations\I18nOperation.cs" />
<Compile Include="Behaviour\Operations\OperationSequence.cs" /> <Compile Include="Behaviour\Operations\OperationSequence.cs" />
<Compile Include="Communication\BaseProxy.cs" /> <Compile Include="Communication\BaseProxy.cs" />
<Compile Include="Communication\BaseHost.cs" />
<Compile Include="Communication\Messages\Message.cs" /> <Compile Include="Communication\Messages\Message.cs" />
<Compile Include="Communication\ServiceProxy.cs" /> <Compile Include="Communication\ServiceProxy.cs" />
<Compile Include="Logging\DefaultLogFormatter.cs" /> <Compile Include="Logging\DefaultLogFormatter.cs" />

View file

@ -24,7 +24,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
{ {
private Mock<ILogger> logger; private Mock<ILogger> logger;
private Mock<IRuntimeInfo> info; private Mock<IRuntimeInfo> info;
private Mock<ISettingsRepository> repository; private Mock<IConfigurationRepository> repository;
private Mock<ISettings> settings; private Mock<ISettings> settings;
private Mock<IText> text; private Mock<IText> text;
private Mock<IUserInterfaceFactory> uiFactory; private Mock<IUserInterfaceFactory> uiFactory;
@ -35,7 +35,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
{ {
logger = new Mock<ILogger>(); logger = new Mock<ILogger>();
info = new Mock<IRuntimeInfo>(); info = new Mock<IRuntimeInfo>();
repository = new Mock<ISettingsRepository>(); repository = new Mock<IConfigurationRepository>();
settings = new Mock<ISettings>(); settings = new Mock<ISettings>();
text = new Mock<IText>(); text = new Mock<IText>();
uiFactory = new Mock<IUserInterfaceFactory>(); uiFactory = new Mock<IUserInterfaceFactory>();
@ -43,24 +43,24 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
info.SetupGet(i => i.AppDataFolder).Returns(@"C:\Not\Really\AppData"); info.SetupGet(i => i.AppDataFolder).Returns(@"C:\Not\Really\AppData");
info.SetupGet(i => i.DefaultSettingsFileName).Returns("SettingsDummy.txt"); info.SetupGet(i => i.DefaultSettingsFileName).Returns("SettingsDummy.txt");
info.SetupGet(i => i.ProgramDataFolder).Returns(@"C:\Not\Really\ProgramData"); info.SetupGet(i => i.ProgramDataFolder).Returns(@"C:\Not\Really\ProgramData");
repository.Setup(r => r.Load(It.IsAny<Uri>())).Returns(settings.Object); repository.Setup(r => r.LoadSettings(It.IsAny<Uri>())).Returns(settings.Object);
repository.Setup(r => r.LoadDefaults()).Returns(settings.Object); repository.Setup(r => r.LoadDefaultSettings()).Returns(settings.Object);
} }
[TestMethod] [TestMethod]
public void MustNotFailWithoutCommandLineArgs() public void MustNotFailWithoutCommandLineArgs()
{ {
repository.Setup(r => r.LoadDefaults()); repository.Setup(r => r.LoadDefaultSettings());
sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, null); sut = new ConfigurationOperation(repository.Object, logger.Object, info.Object, text.Object, uiFactory.Object, null);
sut.Perform(); sut.Perform();
sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, new string[] { }); sut = new ConfigurationOperation(repository.Object, logger.Object, info.Object, text.Object, uiFactory.Object, new string[] { });
sut.Perform(); sut.Perform();
repository.Verify(r => r.LoadDefaults(), Times.Exactly(2)); repository.Verify(r => r.LoadDefaultSettings(), Times.Exactly(2));
} }
[TestMethod] [TestMethod]
@ -68,7 +68,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
{ {
var path = @"an/invalid\path.'*%yolo/()"; var path = @"an/invalid\path.'*%yolo/()";
sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, new [] { "blubb.exe", path }); sut = new ConfigurationOperation(repository.Object, logger.Object, info.Object, text.Object, uiFactory.Object, new [] { "blubb.exe", path });
sut.Perform(); sut.Perform();
} }
@ -82,11 +82,11 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
info.SetupGet(r => r.ProgramDataFolder).Returns(location); info.SetupGet(r => r.ProgramDataFolder).Returns(location);
info.SetupGet(r => r.AppDataFolder).Returns(location); info.SetupGet(r => r.AppDataFolder).Returns(location);
sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, new[] { "blubb.exe", path }); sut = new ConfigurationOperation(repository.Object, logger.Object, info.Object, text.Object, uiFactory.Object, new[] { "blubb.exe", path });
sut.Perform(); sut.Perform();
repository.Verify(r => r.Load(It.Is<Uri>(u => u.Equals(new Uri(path)))), Times.Once); repository.Verify(r => r.LoadSettings(It.Is<Uri>(u => u.Equals(new Uri(path)))), Times.Once);
} }
[TestMethod] [TestMethod]
@ -97,11 +97,11 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
info.SetupGet(r => r.ProgramDataFolder).Returns(location); info.SetupGet(r => r.ProgramDataFolder).Returns(location);
info.SetupGet(r => r.AppDataFolder).Returns($@"{location}\WRONG"); info.SetupGet(r => r.AppDataFolder).Returns($@"{location}\WRONG");
sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, null); sut = new ConfigurationOperation(repository.Object, logger.Object, info.Object, text.Object, uiFactory.Object, null);
sut.Perform(); sut.Perform();
repository.Verify(r => r.Load(It.Is<Uri>(u => u.Equals(new Uri(Path.Combine(location, "SettingsDummy.txt"))))), Times.Once); repository.Verify(r => r.LoadSettings(It.Is<Uri>(u => u.Equals(new Uri(Path.Combine(location, "SettingsDummy.txt"))))), Times.Once);
} }
[TestMethod] [TestMethod]
@ -111,21 +111,21 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
info.SetupGet(r => r.AppDataFolder).Returns(location); info.SetupGet(r => r.AppDataFolder).Returns(location);
sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, null); sut = new ConfigurationOperation(repository.Object, logger.Object, info.Object, text.Object, uiFactory.Object, null);
sut.Perform(); sut.Perform();
repository.Verify(r => r.Load(It.Is<Uri>(u => u.Equals(new Uri(Path.Combine(location, "SettingsDummy.txt"))))), Times.Once); repository.Verify(r => r.LoadSettings(It.Is<Uri>(u => u.Equals(new Uri(Path.Combine(location, "SettingsDummy.txt"))))), Times.Once);
} }
[TestMethod] [TestMethod]
public void MustFallbackToDefaultsAsLastPrio() public void MustFallbackToDefaultsAsLastPrio()
{ {
sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, null); sut = new ConfigurationOperation(repository.Object, logger.Object, info.Object, text.Object, uiFactory.Object, null);
sut.Perform(); sut.Perform();
repository.Verify(r => r.LoadDefaults(), Times.Once); repository.Verify(r => r.LoadDefaultSettings(), Times.Once);
} }
[TestMethod] [TestMethod]
@ -136,7 +136,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
info.SetupGet(r => r.ProgramDataFolder).Returns(location); info.SetupGet(r => r.ProgramDataFolder).Returns(location);
uiFactory.Setup(u => u.Show(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<MessageBoxAction>(), It.IsAny<MessageBoxIcon>())).Returns(MessageBoxResult.Yes); uiFactory.Setup(u => u.Show(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<MessageBoxAction>(), It.IsAny<MessageBoxIcon>())).Returns(MessageBoxResult.Yes);
sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, null); sut = new ConfigurationOperation(repository.Object, logger.Object, info.Object, text.Object, uiFactory.Object, null);
sut.Perform(); sut.Perform();
@ -148,7 +148,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
{ {
uiFactory.Setup(u => u.Show(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<MessageBoxAction>(), It.IsAny<MessageBoxIcon>())).Returns(MessageBoxResult.No); uiFactory.Setup(u => u.Show(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<MessageBoxAction>(), It.IsAny<MessageBoxIcon>())).Returns(MessageBoxResult.No);
sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, null); sut = new ConfigurationOperation(repository.Object, logger.Object, info.Object, text.Object, uiFactory.Object, null);
sut.Perform(); sut.Perform();

View file

@ -14,7 +14,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
public class KioskModeOperationTests public class KioskModeOperationTests
{ {
[TestMethod] [TestMethod]
public void Todo() public void TODO()
{ {
Assert.Fail(); Assert.Fail();
} }

View file

@ -10,6 +10,7 @@ using System;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq; using Moq;
using SafeExamBrowser.Contracts.Communication; using SafeExamBrowser.Contracts.Communication;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Configuration.Settings; using SafeExamBrowser.Contracts.Configuration.Settings;
using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.Logging;
@ -23,7 +24,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
{ {
private Mock<ILogger> logger; private Mock<ILogger> logger;
private Mock<IServiceProxy> service; private Mock<IServiceProxy> service;
private Mock<ISettingsRepository> settings; private Mock<IConfigurationRepository> configuration;
private Mock<IProgressIndicator> progressIndicator; private Mock<IProgressIndicator> progressIndicator;
private Mock<IText> text; private Mock<IText> text;
private ServiceOperation sut; private ServiceOperation sut;
@ -33,23 +34,23 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
{ {
logger = new Mock<ILogger>(); logger = new Mock<ILogger>();
service = new Mock<IServiceProxy>(); service = new Mock<IServiceProxy>();
settings = new Mock<ISettingsRepository>(); configuration = new Mock<IConfigurationRepository>();
progressIndicator = new Mock<IProgressIndicator>(); progressIndicator = new Mock<IProgressIndicator>();
text = new Mock<IText>(); text = new Mock<IText>();
sut = new ServiceOperation(logger.Object, service.Object, settings.Object, text.Object); sut = new ServiceOperation(configuration.Object, logger.Object, service.Object, text.Object);
} }
[TestMethod] [TestMethod]
public void MustConnectToService() public void MustConnectToService()
{ {
service.Setup(s => s.Connect()).Returns(true); service.Setup(s => s.Connect()).Returns(true);
settings.SetupGet(s => s.Current.ServicePolicy).Returns(ServicePolicy.Mandatory); configuration.SetupGet(s => s.CurrentSettings.ServicePolicy).Returns(ServicePolicy.Mandatory);
sut.Perform(); sut.Perform();
service.Setup(s => s.Connect()).Returns(true); service.Setup(s => s.Connect()).Returns(true);
settings.SetupGet(s => s.Current.ServicePolicy).Returns(ServicePolicy.Optional); configuration.SetupGet(s => s.CurrentSettings.ServicePolicy).Returns(ServicePolicy.Optional);
sut.Perform(); sut.Perform();
@ -60,22 +61,22 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
public void MustNotFailIfServiceNotAvailable() public void MustNotFailIfServiceNotAvailable()
{ {
service.Setup(s => s.Connect()).Returns(false); service.Setup(s => s.Connect()).Returns(false);
settings.SetupGet(s => s.Current.ServicePolicy).Returns(ServicePolicy.Mandatory); configuration.SetupGet(s => s.CurrentSettings.ServicePolicy).Returns(ServicePolicy.Mandatory);
sut.Perform(); sut.Perform();
service.Setup(s => s.Connect()).Returns(false); service.Setup(s => s.Connect()).Returns(false);
settings.SetupGet(s => s.Current.ServicePolicy).Returns(ServicePolicy.Optional); configuration.SetupGet(s => s.CurrentSettings.ServicePolicy).Returns(ServicePolicy.Optional);
sut.Perform(); sut.Perform();
service.Setup(s => s.Connect()).Throws<Exception>(); service.Setup(s => s.Connect()).Throws<Exception>();
settings.SetupGet(s => s.Current.ServicePolicy).Returns(ServicePolicy.Mandatory); configuration.SetupGet(s => s.CurrentSettings.ServicePolicy).Returns(ServicePolicy.Mandatory);
sut.Perform(); sut.Perform();
service.Setup(s => s.Connect()).Throws<Exception>(); service.Setup(s => s.Connect()).Throws<Exception>();
settings.SetupGet(s => s.Current.ServicePolicy).Returns(ServicePolicy.Optional); configuration.SetupGet(s => s.CurrentSettings.ServicePolicy).Returns(ServicePolicy.Optional);
sut.Perform(); sut.Perform();
} }
@ -84,7 +85,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
public void MustAbortIfServiceMandatoryAndNotAvailable() public void MustAbortIfServiceMandatoryAndNotAvailable()
{ {
service.Setup(s => s.Connect()).Returns(false); service.Setup(s => s.Connect()).Returns(false);
settings.SetupGet(s => s.Current.ServicePolicy).Returns(ServicePolicy.Mandatory); configuration.SetupGet(s => s.CurrentSettings.ServicePolicy).Returns(ServicePolicy.Mandatory);
sut.Perform(); sut.Perform();
@ -95,7 +96,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
public void MustNotAbortIfServiceOptionalAndNotAvailable() public void MustNotAbortIfServiceOptionalAndNotAvailable()
{ {
service.Setup(s => s.Connect()).Returns(false); service.Setup(s => s.Connect()).Returns(false);
settings.SetupGet(s => s.Current.ServicePolicy).Returns(ServicePolicy.Optional); configuration.SetupGet(s => s.CurrentSettings.ServicePolicy).Returns(ServicePolicy.Optional);
sut.Perform(); sut.Perform();
@ -107,13 +108,13 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
public void MustDisconnectWhenReverting() public void MustDisconnectWhenReverting()
{ {
service.Setup(s => s.Connect()).Returns(true); service.Setup(s => s.Connect()).Returns(true);
settings.SetupGet(s => s.Current.ServicePolicy).Returns(ServicePolicy.Mandatory); configuration.SetupGet(s => s.CurrentSettings.ServicePolicy).Returns(ServicePolicy.Mandatory);
sut.Perform(); sut.Perform();
sut.Revert(); sut.Revert();
service.Setup(s => s.Connect()).Returns(true); service.Setup(s => s.Connect()).Returns(true);
settings.SetupGet(s => s.Current.ServicePolicy).Returns(ServicePolicy.Optional); configuration.SetupGet(s => s.CurrentSettings.ServicePolicy).Returns(ServicePolicy.Optional);
sut.Perform(); sut.Perform();
sut.Revert(); sut.Revert();
@ -126,7 +127,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
{ {
service.Setup(s => s.Connect()).Returns(true); service.Setup(s => s.Connect()).Returns(true);
service.Setup(s => s.Disconnect()).Throws<Exception>(); service.Setup(s => s.Disconnect()).Throws<Exception>();
settings.SetupGet(s => s.Current.ServicePolicy).Returns(ServicePolicy.Optional); configuration.SetupGet(s => s.CurrentSettings.ServicePolicy).Returns(ServicePolicy.Optional);
sut.Perform(); sut.Perform();
sut.Revert(); sut.Revert();
@ -138,25 +139,25 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
public void MustNotDisconnnectIfNotAvailable() public void MustNotDisconnnectIfNotAvailable()
{ {
service.Setup(s => s.Connect()).Returns(false); service.Setup(s => s.Connect()).Returns(false);
settings.SetupGet(s => s.Current.ServicePolicy).Returns(ServicePolicy.Mandatory); configuration.SetupGet(s => s.CurrentSettings.ServicePolicy).Returns(ServicePolicy.Mandatory);
sut.Perform(); sut.Perform();
sut.Revert(); sut.Revert();
service.Setup(s => s.Connect()).Returns(false); service.Setup(s => s.Connect()).Returns(false);
settings.SetupGet(s => s.Current.ServicePolicy).Returns(ServicePolicy.Optional); configuration.SetupGet(s => s.CurrentSettings.ServicePolicy).Returns(ServicePolicy.Optional);
sut.Perform(); sut.Perform();
sut.Revert(); sut.Revert();
service.Setup(s => s.Connect()).Throws<Exception>(); service.Setup(s => s.Connect()).Throws<Exception>();
settings.SetupGet(s => s.Current.ServicePolicy).Returns(ServicePolicy.Mandatory); configuration.SetupGet(s => s.CurrentSettings.ServicePolicy).Returns(ServicePolicy.Mandatory);
sut.Perform(); sut.Perform();
sut.Revert(); sut.Revert();
service.Setup(s => s.Connect()).Throws<Exception>(); service.Setup(s => s.Connect()).Throws<Exception>();
settings.SetupGet(s => s.Current.ServicePolicy).Returns(ServicePolicy.Optional); configuration.SetupGet(s => s.CurrentSettings.ServicePolicy).Returns(ServicePolicy.Optional);
sut.Perform(); sut.Perform();
sut.Revert(); sut.Revert();

View file

@ -19,9 +19,9 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
{ {
internal class ConfigurationOperation : IOperation internal class ConfigurationOperation : IOperation
{ {
private IConfigurationRepository repository;
private ILogger logger; private ILogger logger;
private IRuntimeInfo runtimeInfo; private IRuntimeInfo runtimeInfo;
private ISettingsRepository repository;
private IText text; private IText text;
private IUserInterfaceFactory uiFactory; private IUserInterfaceFactory uiFactory;
private string[] commandLineArgs; private string[] commandLineArgs;
@ -30,16 +30,16 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
public IProgressIndicator ProgressIndicator { private get; set; } public IProgressIndicator ProgressIndicator { private get; set; }
public ConfigurationOperation( public ConfigurationOperation(
IConfigurationRepository repository,
ILogger logger, ILogger logger,
IRuntimeInfo runtimeInfo, IRuntimeInfo runtimeInfo,
ISettingsRepository repository,
IText text, IText text,
IUserInterfaceFactory uiFactory, IUserInterfaceFactory uiFactory,
string[] commandLineArgs) string[] commandLineArgs)
{ {
this.repository = repository;
this.logger = logger; this.logger = logger;
this.commandLineArgs = commandLineArgs; this.commandLineArgs = commandLineArgs;
this.repository = repository;
this.runtimeInfo = runtimeInfo; this.runtimeInfo = runtimeInfo;
this.text = text; this.text = text;
this.uiFactory = uiFactory; this.uiFactory = uiFactory;
@ -56,7 +56,7 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
if (isValidUri) if (isValidUri)
{ {
logger.Info($"Loading configuration from '{uri.AbsolutePath}'..."); logger.Info($"Loading configuration from '{uri.AbsolutePath}'...");
settings = repository.Load(uri); settings = repository.LoadSettings(uri);
if (settings.ConfigurationMode == ConfigurationMode.ConfigureClient && UserWantsToAbortStartup()) if (settings.ConfigurationMode == ConfigurationMode.ConfigureClient && UserWantsToAbortStartup())
{ {
@ -67,7 +67,7 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
else else
{ {
logger.Info("No valid settings file specified nor found in PROGRAMDATA or APPDATA - loading default settings..."); logger.Info("No valid settings file specified nor found in PROGRAMDATA or APPDATA - loading default settings...");
settings = repository.LoadDefaults(); settings = repository.LoadDefaultSettings();
} }
} }

View file

@ -7,6 +7,7 @@
*/ */
using SafeExamBrowser.Contracts.Behaviour.Operations; using SafeExamBrowser.Contracts.Behaviour.Operations;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Configuration.Settings; using SafeExamBrowser.Contracts.Configuration.Settings;
using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.Logging;
@ -17,21 +18,21 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
internal class KioskModeOperation : IOperation internal class KioskModeOperation : IOperation
{ {
private ILogger logger; private ILogger logger;
private ISettingsRepository settingsRepository; private IConfigurationRepository configuration;
private KioskMode kioskMode; private KioskMode kioskMode;
public bool Abort { get; private set; } public bool Abort { get; private set; }
public IProgressIndicator ProgressIndicator { private get; set; } public IProgressIndicator ProgressIndicator { private get; set; }
public KioskModeOperation(ILogger logger, ISettingsRepository settingsRepository) public KioskModeOperation(ILogger logger, IConfigurationRepository configuration)
{ {
this.logger = logger; this.logger = logger;
this.settingsRepository = settingsRepository; this.configuration = configuration;
} }
public void Perform() public void Perform()
{ {
kioskMode = settingsRepository.Current.KioskMode; kioskMode = configuration.CurrentSettings.KioskMode;
logger.Info($"Initializing kiosk mode '{kioskMode}'..."); logger.Info($"Initializing kiosk mode '{kioskMode}'...");
ProgressIndicator?.UpdateText(TextKey.ProgressIndicator_InitializeKioskMode); ProgressIndicator?.UpdateText(TextKey.ProgressIndicator_InitializeKioskMode);

View file

@ -9,6 +9,7 @@
using System; using System;
using SafeExamBrowser.Contracts.Behaviour.Operations; using SafeExamBrowser.Contracts.Behaviour.Operations;
using SafeExamBrowser.Contracts.Communication; using SafeExamBrowser.Contracts.Communication;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Configuration.Settings; using SafeExamBrowser.Contracts.Configuration.Settings;
using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.Logging;
@ -20,19 +21,19 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
{ {
private bool serviceAvailable; private bool serviceAvailable;
private bool serviceMandatory; private bool serviceMandatory;
private IConfigurationRepository configuration;
private ILogger logger; private ILogger logger;
private IServiceProxy service; private IServiceProxy service;
private ISettingsRepository settingsRepository;
private IText text; private IText text;
public bool Abort { get; private set; } public bool Abort { get; private set; }
public IProgressIndicator ProgressIndicator { private get; set; } public IProgressIndicator ProgressIndicator { private get; set; }
public ServiceOperation(ILogger logger, IServiceProxy service, ISettingsRepository settingsRepository, IText text) public ServiceOperation(IConfigurationRepository configuration, ILogger logger, IServiceProxy service, IText text)
{ {
this.configuration = configuration;
this.service = service; this.service = service;
this.logger = logger; this.logger = logger;
this.settingsRepository = settingsRepository;
this.text = text; this.text = text;
} }
@ -43,7 +44,7 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
try try
{ {
serviceMandatory = settingsRepository.Current.ServicePolicy == ServicePolicy.Mandatory; serviceMandatory = configuration.CurrentSettings.ServicePolicy == ServicePolicy.Mandatory;
serviceAvailable = service.Connect(); serviceAvailable = service.Connect();
} }
catch (Exception e) catch (Exception e)

View file

@ -20,36 +20,41 @@ namespace SafeExamBrowser.Runtime.Behaviour
{ {
internal class RuntimeController : IRuntimeController internal class RuntimeController : IRuntimeController
{ {
private bool initialized;
private IConfigurationRepository configuration;
private ILogger logger; private ILogger logger;
private IOperationSequence bootstrapSequence; private IOperationSequence bootstrapSequence;
private IOperationSequence sessionSequence; private IOperationSequence sessionSequence;
private IRuntimeHost runtimeHost;
private IRuntimeInfo runtimeInfo; private IRuntimeInfo runtimeInfo;
private IRuntimeWindow runtimeWindow; private IRuntimeWindow runtimeWindow;
private IServiceProxy serviceProxy; private IServiceProxy serviceProxy;
private ISettingsRepository settingsRepository;
private ISplashScreen splashScreen; private ISplashScreen splashScreen;
private Action terminationCallback; private Action shutdown;
private IText text; private IText text;
private IUserInterfaceFactory uiFactory; private IUserInterfaceFactory uiFactory;
public RuntimeController( public RuntimeController(
IConfigurationRepository configuration,
ILogger logger, ILogger logger,
IOperationSequence bootstrapSequence, IOperationSequence bootstrapSequence,
IOperationSequence sessionSequence, IOperationSequence sessionSequence,
IRuntimeHost runtimeHost,
IRuntimeInfo runtimeInfo, IRuntimeInfo runtimeInfo,
IServiceProxy serviceProxy, IServiceProxy serviceProxy,
ISettingsRepository settingsRepository, Action shutdown,
Action terminationCallback,
IText text, IText text,
IUserInterfaceFactory uiFactory) IUserInterfaceFactory uiFactory)
{ {
this.configuration = configuration;
this.logger = logger; this.logger = logger;
this.bootstrapSequence = bootstrapSequence; this.bootstrapSequence = bootstrapSequence;
this.sessionSequence = sessionSequence; this.sessionSequence = sessionSequence;
this.runtimeHost = runtimeHost;
this.runtimeInfo = runtimeInfo; this.runtimeInfo = runtimeInfo;
this.serviceProxy = serviceProxy; this.serviceProxy = serviceProxy;
this.settingsRepository = settingsRepository; this.shutdown = shutdown;
this.terminationCallback = terminationCallback;
this.text = text; this.text = text;
this.uiFactory = uiFactory; this.uiFactory = uiFactory;
} }
@ -58,19 +63,16 @@ namespace SafeExamBrowser.Runtime.Behaviour
{ {
logger.Info("--- Initiating startup procedure ---"); logger.Info("--- Initiating startup procedure ---");
runtimeWindow = uiFactory.CreateRuntimeWindow(runtimeInfo, text);
splashScreen = uiFactory.CreateSplashScreen(runtimeInfo, text); splashScreen = uiFactory.CreateSplashScreen(runtimeInfo, text);
splashScreen.Show(); splashScreen.Show();
bootstrapSequence.ProgressIndicator = splashScreen; bootstrapSequence.ProgressIndicator = splashScreen;
var success = bootstrapSequence.TryPerform(); initialized = bootstrapSequence.TryPerform();
System.Threading.Thread.Sleep(5000); if (initialized)
if (success)
{ {
runtimeWindow = uiFactory.CreateRuntimeWindow(runtimeInfo, text);
logger.Info("--- Application successfully initialized! ---"); logger.Info("--- Application successfully initialized! ---");
logger.Log(string.Empty); logger.Log(string.Empty);
logger.Subscribe(runtimeWindow); logger.Subscribe(runtimeWindow);
@ -85,17 +87,19 @@ namespace SafeExamBrowser.Runtime.Behaviour
logger.Log(string.Empty); logger.Log(string.Empty);
} }
return success; return initialized;
} }
public void Terminate() public void Terminate()
{ {
StopSession(); // TODO: Necessary here? Move to App.cs as private "started" flag if not...
if (!initialized)
{
return;
}
// TODO: // TODO: Only if session is running!
// - Disconnect from service StopSession();
// - Terminate runtime communication host
// - Revert kiosk mode (or do that when stopping session?)
logger.Unsubscribe(runtimeWindow); logger.Unsubscribe(runtimeWindow);
runtimeWindow?.Close(); runtimeWindow?.Close();
@ -104,6 +108,10 @@ namespace SafeExamBrowser.Runtime.Behaviour
logger.Log(string.Empty); logger.Log(string.Empty);
logger.Info("--- Initiating shutdown procedure ---"); logger.Info("--- Initiating shutdown procedure ---");
// TODO:
// - Disconnect from service
// - Terminate runtime communication host
// - Revert kiosk mode (or do that when stopping session?)
var success = bootstrapSequence.TryRevert(); var success = bootstrapSequence.TryRevert();
if (success) if (success)
@ -137,25 +145,28 @@ namespace SafeExamBrowser.Runtime.Behaviour
if (success) if (success)
{ {
// TODO
} }
else else
{ {
// TODO
} }
// TODO: Remove!
System.Threading.Thread.Sleep(5000); System.Threading.Thread.Sleep(5000);
runtimeWindow.HideProgressBar();
runtimeWindow.UpdateText(TextKey.RuntimeWindow_ApplicationRunning); runtimeWindow.UpdateText(TextKey.RuntimeWindow_ApplicationRunning);
if (settingsRepository.Current.KioskMode == KioskMode.DisableExplorerShell) if (configuration.CurrentSettings.KioskMode == KioskMode.DisableExplorerShell)
{ {
runtimeWindow.Hide(); runtimeWindow.Hide();
} }
// TODO: Remove!
System.Threading.Thread.Sleep(5000); System.Threading.Thread.Sleep(5000);
terminationCallback.Invoke(); shutdown.Invoke();
} }
private void StopSession() private void StopSession()
@ -163,12 +174,26 @@ namespace SafeExamBrowser.Runtime.Behaviour
logger.Info("Stopping current session..."); logger.Info("Stopping current session...");
runtimeWindow.Show(); runtimeWindow.Show();
runtimeWindow.BringToForeground(); runtimeWindow.BringToForeground();
runtimeWindow.ShowProgressBar();
runtimeWindow.UpdateText(TextKey.RuntimeWindow_StopSession, true); runtimeWindow.UpdateText(TextKey.RuntimeWindow_StopSession, true);
// TODO: // TODO:
// - Terminate client (or does it terminate itself?) // - Terminate client (or does it terminate itself?)
// - Finalize session with service // - Finalize session with service
// - Stop event handling and close session // - Stop event handling and close session
var success = sessionSequence.TryRevert();
// TODO: Remove!
System.Threading.Thread.Sleep(5000);
if (success)
{
// TODO
}
else
{
// TODO
}
} }
} }
} }

View file

@ -0,0 +1,42 @@
/*
* 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 SafeExamBrowser.Contracts.Communication;
using SafeExamBrowser.Contracts.Communication.Messages;
using SafeExamBrowser.Contracts.Communication.Responses;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Core.Communication;
namespace SafeExamBrowser.Runtime.Communication
{
internal class RuntimeHost : BaseHost, IRuntimeHost
{
public RuntimeHost(string address, ILogger logger) : base(address, logger)
{
}
public override IConnectResponse Connect(Guid? token = null)
{
// TODO
throw new NotImplementedException();
}
public override void Disconnect(IMessage message)
{
// TODO
throw new NotImplementedException();
}
public override IResponse Send(IMessage message)
{
// TODO
throw new NotImplementedException();
}
}
}

View file

@ -8,11 +8,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Windows; using System.Windows;
using SafeExamBrowser.Configuration; using SafeExamBrowser.Configuration;
using SafeExamBrowser.Configuration.Settings;
using SafeExamBrowser.Contracts.Behaviour; using SafeExamBrowser.Contracts.Behaviour;
using SafeExamBrowser.Contracts.Behaviour.Operations; using SafeExamBrowser.Contracts.Behaviour.Operations;
using SafeExamBrowser.Contracts.Configuration; using SafeExamBrowser.Contracts.Configuration;
@ -23,6 +20,7 @@ using SafeExamBrowser.Core.I18n;
using SafeExamBrowser.Core.Logging; using SafeExamBrowser.Core.Logging;
using SafeExamBrowser.Runtime.Behaviour; using SafeExamBrowser.Runtime.Behaviour;
using SafeExamBrowser.Runtime.Behaviour.Operations; using SafeExamBrowser.Runtime.Behaviour.Operations;
using SafeExamBrowser.Runtime.Communication;
using SafeExamBrowser.UserInterface.Classic; using SafeExamBrowser.UserInterface.Classic;
using SafeExamBrowser.WindowsApi; using SafeExamBrowser.WindowsApi;
@ -31,7 +29,7 @@ namespace SafeExamBrowser.Runtime
internal class CompositionRoot internal class CompositionRoot
{ {
private ILogger logger; private ILogger logger;
private RuntimeInfo runtimeInfo; private IRuntimeInfo runtimeInfo;
private ISystemInfo systemInfo; private ISystemInfo systemInfo;
internal IRuntimeController RuntimeController { get; private set; } internal IRuntimeController RuntimeController { get; private set; }
@ -42,30 +40,30 @@ namespace SafeExamBrowser.Runtime
var bootstrapOperations = new Queue<IOperation>(); var bootstrapOperations = new Queue<IOperation>();
var sessionOperations = new Queue<IOperation>(); var sessionOperations = new Queue<IOperation>();
var nativeMethods = new NativeMethods(); var nativeMethods = new NativeMethods();
var settingsRepository = new SettingsRepository(); var configuration = new ConfigurationRepository();
var uiFactory = new UserInterfaceFactory(); var uiFactory = new UserInterfaceFactory();
logger = new Logger(); logger = new Logger();
runtimeInfo = new RuntimeInfo(); runtimeInfo = configuration.RuntimeInfo;
systemInfo = new SystemInfo(); systemInfo = new SystemInfo();
InitializeRuntimeInfo();
InitializeLogging(); InitializeLogging();
var text = new Text(logger); var text = new Text(logger);
var serviceProxy = new ServiceProxy(new ModuleLogger(logger, typeof(ServiceProxy)), "net.pipe://localhost/safeexambrowser/service"); var runtimeHost = new RuntimeHost(runtimeInfo.RuntimeAddress, new ModuleLogger(logger, typeof(RuntimeHost)));
var serviceProxy = new ServiceProxy(runtimeInfo.ServiceAddress, new ModuleLogger(logger, typeof(ServiceProxy)));
bootstrapOperations.Enqueue(new I18nOperation(logger, text)); bootstrapOperations.Enqueue(new I18nOperation(logger, text));
// TODO: RuntimeHostOperation here (is IBootstrapOperation -> only performed once per runtime!) bootstrapOperations.Enqueue(new CommunicationOperation(runtimeHost, logger));
sessionOperations.Enqueue(new ConfigurationOperation(logger, runtimeInfo, settingsRepository, text, uiFactory, args)); sessionOperations.Enqueue(new ConfigurationOperation(configuration, logger, runtimeInfo, text, uiFactory, args));
sessionOperations.Enqueue(new ServiceOperation(logger, serviceProxy, settingsRepository, text)); sessionOperations.Enqueue(new ServiceOperation(configuration, logger, serviceProxy, text));
sessionOperations.Enqueue(new KioskModeOperation(logger, settingsRepository)); sessionOperations.Enqueue(new KioskModeOperation(logger, configuration));
var bootstrapSequence = new OperationSequence(logger, bootstrapOperations); var boostrapSequence = new OperationSequence(logger, bootstrapOperations);
var sessionSequence = new OperationSequence(logger, sessionOperations); var sessionSequence = new OperationSequence(logger, sessionOperations);
RuntimeController = new RuntimeController(logger, bootstrapSequence, sessionSequence, runtimeInfo, serviceProxy, settingsRepository, Application.Current.Shutdown, text, uiFactory); RuntimeController = new RuntimeController(configuration, logger, boostrapSequence, sessionSequence, runtimeHost, runtimeInfo, serviceProxy, Application.Current.Shutdown, text, uiFactory);
} }
internal void LogStartupInformation() internal void LogStartupInformation()
@ -84,28 +82,7 @@ namespace SafeExamBrowser.Runtime
internal void LogShutdownInformation() internal void LogShutdownInformation()
{ {
logger?.Log($"{Environment.NewLine}# Application terminated at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}"); logger?.Log($"# Application terminated at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
}
private void InitializeRuntimeInfo()
{
var appDataFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), nameof(SafeExamBrowser));
var executable = Assembly.GetEntryAssembly();
var startTime = DateTime.Now;
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<AssemblyCopyrightAttribute>().Copyright;
runtimeInfo.ProgramDataFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), nameof(SafeExamBrowser));
runtimeInfo.ProgramTitle = executable.GetCustomAttribute<AssemblyTitleAttribute>().Title;
runtimeInfo.ProgramVersion = executable.GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion;
runtimeInfo.RuntimeLogFile = Path.Combine(logFolder, $"{logFilePrefix}_Runtime.txt");
} }
private void InitializeLogging() private void InitializeLogging()

View file

@ -90,6 +90,7 @@
<Compile Include="Behaviour\Operations\ConfigurationOperation.cs" /> <Compile Include="Behaviour\Operations\ConfigurationOperation.cs" />
<Compile Include="Behaviour\Operations\KioskModeOperation.cs" /> <Compile Include="Behaviour\Operations\KioskModeOperation.cs" />
<Compile Include="Behaviour\Operations\ServiceOperation.cs" /> <Compile Include="Behaviour\Operations\ServiceOperation.cs" />
<Compile Include="Communication\RuntimeHost.cs" />
<Compile Include="CompositionRoot.cs" /> <Compile Include="CompositionRoot.cs" />
<Compile Include="Properties\AssemblyInfo.cs"> <Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType> <SubType>Code</SubType>