SEBWIN-219: Started defining & implementing communication contracts and functionality.
This commit is contained in:
parent
f321496815
commit
c56b4a0251
23 changed files with 405 additions and 106 deletions
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
[OperationContract(IsInitiating = true)]
|
||||
IConnectResponse Connect(Guid? token = null);
|
||||
|
||||
/// <summary>
|
||||
/// Closes the connection to the host and instructs it to terminate the communication session.
|
||||
/// </summary>
|
||||
[OperationContract(IsInitiating = false, IsTerminating = true)]
|
||||
void Disconnect(IMessage message);
|
||||
|
||||
/// <summary>
|
||||
/// Sends a message to the host, optionally returning a response. If no response is expected, <c>null</c> will be returned.
|
||||
/// </summary>
|
||||
[OperationContract(IsInitiating = false)]
|
||||
IResponse Send(IMessage message);
|
||||
}
|
||||
}
|
21
SafeExamBrowser.Contracts/Communication/Messages/IMessage.cs
Normal file
21
SafeExamBrowser.Contracts/Communication/Messages/IMessage.cs
Normal file
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// The communication token needed for authentication with the host.
|
||||
/// </summary>
|
||||
Guid CommunicationToken { get; }
|
||||
}
|
||||
}
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// The communication token needed for authentication with the host. Is <c>null</c> if a connection was refused.
|
||||
/// </summary>
|
||||
Guid? CommunicationToken { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the host has accepted the connection request.
|
||||
/// </summary>
|
||||
bool ConnectionEstablished { get; }
|
||||
}
|
||||
}
|
|
@ -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
|
||||
{
|
||||
}
|
||||
}
|
|
@ -12,6 +12,12 @@ namespace SafeExamBrowser.Contracts.Configuration.Settings
|
|||
{
|
||||
public interface ISettingsRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// 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>.
|
||||
/// </summary>
|
||||
ISettings Current { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to load settings from the specified path.
|
||||
/// </summary>
|
||||
|
|
|
@ -13,6 +13,12 @@ namespace SafeExamBrowser.Contracts.Logging
|
|||
{
|
||||
public interface ILogger
|
||||
{
|
||||
/// <summary>
|
||||
/// Logs the given message with severity <b>DEBUG</b>.
|
||||
/// </summary>
|
||||
/// <exception cref="ArgumentNullException" />
|
||||
void Debug(string message);
|
||||
|
||||
/// <summary>
|
||||
/// Logs the given message with severity <b>INFO</b>.
|
||||
/// </summary>
|
||||
|
|
|
@ -13,8 +13,9 @@ namespace SafeExamBrowser.Contracts.Logging
|
|||
/// </summary>
|
||||
public enum LogLevel
|
||||
{
|
||||
Info = 1,
|
||||
Warning = 2,
|
||||
Error = 3
|
||||
Debug = 1,
|
||||
Info = 2,
|
||||
Warning = 3,
|
||||
Error = 4
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows to specify the application settings to be used during runtime.
|
||||
/// </summary>
|
||||
ISettings Settings { set; }
|
||||
|
||||
/// <summary>
|
||||
/// Wires up and starts the application event handling.
|
||||
/// </summary>
|
||||
|
|
|
@ -50,9 +50,14 @@
|
|||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.ServiceModel" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Behaviour\IApplicationController.cs" />
|
||||
<Compile Include="Communication\ICommunicationHost.cs" />
|
||||
<Compile Include="Communication\Messages\IMessage.cs" />
|
||||
<Compile Include="Communication\Responses\IResponse.cs" />
|
||||
<Compile Include="Communication\Responses\IConnectResponse.cs" />
|
||||
<Compile Include="Configuration\IRuntimeInfo.cs" />
|
||||
<Compile Include="Configuration\Settings\ConfigurationMode.cs" />
|
||||
<Compile Include="Runtime\IRuntimeController.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<ArgumentNullException>(() => sut.Debug(null));
|
||||
Assert.ThrowsException<ArgumentNullException>(() => sut.Info(null));
|
||||
Assert.ThrowsException<ArgumentNullException>(() => sut.Warn(null));
|
||||
Assert.ThrowsException<ArgumentNullException>(() => sut.Error(null));
|
||||
|
|
106
SafeExamBrowser.Core/Communication/CommunicationHostProxy.cs
Normal file
106
SafeExamBrowser.Core/Communication/CommunicationHostProxy.cs
Normal file
|
@ -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<ICommunicationHost>.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";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,6 +21,16 @@ namespace SafeExamBrowser.Core.Logging
|
|||
private readonly IList<ILogContent> log = new List<ILogContent>();
|
||||
private readonly IList<ILogObserver> observers = new List<ILogObserver>();
|
||||
|
||||
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)
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.ServiceModel" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
</ItemGroup>
|
||||
|
@ -57,6 +58,7 @@
|
|||
<Compile Include="Behaviour\Operations\I18nOperation.cs" />
|
||||
<Compile Include="Behaviour\ShutdownController.cs" />
|
||||
<Compile Include="Behaviour\StartupController.cs" />
|
||||
<Compile Include="Communication\CommunicationHostProxy.cs" />
|
||||
<Compile Include="Logging\DefaultLogFormatter.cs" />
|
||||
<Compile Include="Logging\LogFileWriter.cs" />
|
||||
<Compile Include="Logging\LogMessage.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<ILogger> logger;
|
||||
private Mock<IRuntimeController> controller;
|
||||
private Mock<IRuntimeInfo> info;
|
||||
private Mock<ISettingsRepository> repository;
|
||||
private Mock<ISettings> settings;
|
||||
|
@ -37,7 +35,6 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
|
|||
public void Initialize()
|
||||
{
|
||||
logger = new Mock<ILogger>();
|
||||
controller = new Mock<IRuntimeController>();
|
||||
info = new Mock<IRuntimeInfo>();
|
||||
repository = new Mock<ISettingsRepository>();
|
||||
settings = new Mock<ISettings>();
|
||||
|
@ -55,23 +52,23 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
|
|||
[TestMethod]
|
||||
public void MustNotFailWithoutCommandLineArgs()
|
||||
{
|
||||
controller.SetupSet(c => c.Settings = It.IsAny<ISettings>());
|
||||
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<ISettings>(), 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<ISettings>(), Times.Once);
|
||||
repository.Verify(r => r.Load(It.Is<Uri>(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<ISettings>(), Times.Once);
|
||||
repository.Verify(r => r.Load(It.Is<Uri>(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<ISettings>(), Times.Once);
|
||||
repository.Verify(r => r.Load(It.Is<Uri>(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<ISettings>(), 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<string>(), It.IsAny<string>(), It.IsAny<MessageBoxAction>(), It.IsAny<MessageBoxIcon>())).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<string>(), It.IsAny<string>(), It.IsAny<MessageBoxAction>(), It.IsAny<MessageBoxIcon>())).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
|
||||
};
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -82,6 +82,7 @@
|
|||
<ItemGroup>
|
||||
<Compile Include="Behaviour\Operations\ConfigurationOperationTests.cs" />
|
||||
<Compile Include="Behaviour\Operations\RuntimeControllerOperationTests.cs" />
|
||||
<Compile Include="Behaviour\Operations\ServiceOperationTests.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
|
|
@ -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<IOperation>(instances.StartupOperations.Reverse());
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<IOperation> 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<IOperation>();
|
||||
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<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");
|
||||
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()
|
||||
{
|
||||
var logFileWriter = new LogFileWriter(new DefaultLogFormatter(), RuntimeInfo.RuntimeLogFile);
|
||||
var logFileWriter = new LogFileWriter(new DefaultLogFormatter(), runtimeInfo.RuntimeLogFile);
|
||||
|
||||
logFileWriter.Initialize();
|
||||
Logger.Subscribe(logFileWriter);
|
||||
logger.Subscribe(logFileWriter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -89,6 +89,7 @@
|
|||
<Compile Include="App.cs" />
|
||||
<Compile Include="Behaviour\Operations\ConfigurationOperation.cs" />
|
||||
<Compile Include="Behaviour\Operations\RuntimeControllerOperation.cs" />
|
||||
<Compile Include="Behaviour\Operations\ServiceOperation.cs" />
|
||||
<Compile Include="CompositionRoot.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs">
|
||||
<SubType>Code</SubType>
|
||||
|
|
Loading…
Reference in a new issue