SEBWIN-219: Started implementing the configuration operation.

This commit is contained in:
dbuechel 2018-01-18 15:14:05 +01:00
parent 31d6d544d0
commit 39261f5222
13 changed files with 299 additions and 3 deletions

View file

@ -19,7 +19,9 @@ namespace SafeExamBrowser.Configuration
public string BrowserCachePath { get; set; }
public string BrowserLogFile { get; set; }
public string ClientLogFile { get; set; }
public string DefaultSettingsFileName { get; set; }
public string ProgramCopyright { get; set; }
public string ProgramDataFolder { get; set; }
public string ProgramTitle { get; set; }
public string ProgramVersion { get; set; }
public string RuntimeLogFile { get; set; }

View file

@ -37,11 +37,21 @@ namespace SafeExamBrowser.Contracts.Configuration
/// </summary>
string ClientLogFile { get; }
/// <summary>
/// The default file name for application settings.
/// </summary>
string DefaultSettingsFileName { get; }
/// <summary>
/// The copyright information for the application (i.e. the executing assembly).
/// </summary>
string ProgramCopyright { get; }
/// <summary>
/// The path of the program data folder.
/// </summary>
string ProgramDataFolder { get; }
/// <summary>
/// The program title of the application (i.e. the executing assembly).
/// </summary>

View file

@ -25,6 +25,7 @@ namespace SafeExamBrowser.Contracts.I18n
Notification_LogTooltip,
SplashScreen_EmptyClipboard,
SplashScreen_InitializeBrowser,
SplashScreen_InitializeConfiguration,
SplashScreen_InitializeProcessMonitoring,
SplashScreen_InitializeTaskbar,
SplashScreen_InitializeWindowMonitoring,

View file

@ -6,10 +6,17 @@
* 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>

View file

@ -10,6 +10,7 @@
<Notification_LogTooltip>Application Log</Notification_LogTooltip>
<SplashScreen_EmptyClipboard>Emptying clipboard</SplashScreen_EmptyClipboard>
<SplashScreen_InitializeBrowser>Initializing browser</SplashScreen_InitializeBrowser>
<SplashScreen_InitializeConfiguration>Initializing application configuration</SplashScreen_InitializeConfiguration>
<SplashScreen_InitializeProcessMonitoring>Initializing process monitoring</SplashScreen_InitializeProcessMonitoring>
<SplashScreen_InitializeTaskbar>Initializing taskbar</SplashScreen_InitializeTaskbar>
<SplashScreen_InitializeWindowMonitoring>Initializing window monitoring</SplashScreen_InitializeWindowMonitoring>

View file

@ -0,0 +1,153 @@
/*
* 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 Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Configuration.Settings;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.Runtime;
using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Runtime.Behaviour.Operations;
namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
{
[TestClass]
public class ConfigurationOperationTests
{
private Mock<ILogger> logger;
private Mock<IRuntimeController> controller;
private Mock<IRuntimeInfo> info;
private Mock<ISettingsRepository> repository;
private Mock<ISplashScreen> splashScreen;
private ConfigurationOperation sut;
[TestInitialize]
public void Initialize()
{
logger = new Mock<ILogger>();
controller = new Mock<IRuntimeController>();
info = new Mock<IRuntimeInfo>();
repository = new Mock<ISettingsRepository>();
splashScreen = new Mock<ISplashScreen>();
info.SetupGet(r => r.AppDataFolder).Returns(@"C:\Not\Really\AppData");
info.SetupGet(r => r.DefaultSettingsFileName).Returns("SettingsDummy.txt");
info.SetupGet(r => r.ProgramDataFolder).Returns(@"C:\Not\Really\ProgramData");
}
[TestMethod]
public void MustNotFailWithoutCommandLineArgs()
{
controller.SetupSet(c => c.Settings = It.IsAny<ISettings>());
sut = new ConfigurationOperation(logger.Object, controller.Object, info.Object, repository.Object, null)
{
SplashScreen = splashScreen.Object
};
sut.Perform();
sut = new ConfigurationOperation(logger.Object, controller.Object, info.Object, repository.Object, new string[] { })
{
SplashScreen = splashScreen.Object
};
sut.Perform();
controller.VerifySet(c => c.Settings = It.IsAny<ISettings>(), Times.Exactly(2));
}
[TestMethod]
public void MustNotFailWithInvalidUri()
{
var path = @"an/invalid\path.'*%yolo/()";
sut = new ConfigurationOperation(logger.Object, controller.Object, info.Object, repository.Object, new [] { "blubb.exe", path })
{
SplashScreen = splashScreen.Object
};
sut.Perform();
}
[TestMethod]
public void MustUseCommandLineArgumentAs1stPrio()
{
var path = @"http://www.safeexambrowser.org/whatever.seb";
var location = Path.GetDirectoryName(GetType().Assembly.Location);
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, 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);
}
[TestMethod]
public void MustUseProgramDataAs2ndPrio()
{
var location = Path.GetDirectoryName(GetType().Assembly.Location);
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, 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 MustUseAppDataAs3rdPrio()
{
var location = Path.GetDirectoryName(GetType().Assembly.Location);
info.SetupGet(r => r.AppDataFolder).Returns(location);
sut = new ConfigurationOperation(logger.Object, controller.Object, info.Object, repository.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, null)
{
SplashScreen = splashScreen.Object
};
sut.Perform();
controller.VerifySet(c => c.Settings = It.IsAny<ISettings>(), Times.Once);
repository.Verify(r => r.LoadDefaults(), Times.Once);
}
}
}

View file

@ -80,6 +80,7 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Behaviour\Operations\ConfigurationOperationTests.cs" />
<Compile Include="Behaviour\Operations\RuntimeControllerOperationTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
@ -97,6 +98,14 @@
<Name>SafeExamBrowser.Runtime</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Content Include="SettingsDummy.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="WRONG\SettingsDummy.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">

View file

@ -0,0 +1 @@


View file

@ -0,0 +1 @@


View file

@ -0,0 +1,104 @@
/*
* 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 SafeExamBrowser.Contracts.Behaviour;
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
{
internal class ConfigurationOperation : IOperation
{
private ILogger logger;
private IRuntimeController controller;
private IRuntimeInfo runtimeInfo;
private ISettingsRepository repository;
private string[] commandLineArgs;
public ISplashScreen SplashScreen { private get; set; }
public ConfigurationOperation(
ILogger logger,
IRuntimeController controller,
IRuntimeInfo runtimeInfo,
ISettingsRepository repository,
string[] commandLineArgs)
{
this.logger = logger;
this.controller = controller;
this.commandLineArgs = commandLineArgs;
this.repository = repository;
this.runtimeInfo = runtimeInfo;
}
public void Perform()
{
logger.Info("Initializing application configuration...");
SplashScreen.UpdateText(TextKey.SplashScreen_InitializeConfiguration);
var isValidUri = TryGetSettingsUri(out Uri uri);
if (isValidUri)
{
logger.Info($"Loading configuration from '{uri.AbsolutePath}'...");
controller.Settings = repository.Load(uri);
}
else
{
logger.Info("No valid settings file specified nor found in PROGRAMDATA or APPDATA - loading default settings...");
controller.Settings = repository.LoadDefaults();
}
// TODO: Allow user to quit if in Configure Client mode - callback to terminate WPF application?
}
public void Revert()
{
// Nothing to do here...
}
private bool TryGetSettingsUri(out Uri uri)
{
var path = string.Empty;
var isValidUri = false;
var programDataSettings = Path.Combine(runtimeInfo.ProgramDataFolder, runtimeInfo.DefaultSettingsFileName);
var appDataSettings = Path.Combine(runtimeInfo.AppDataFolder, runtimeInfo.DefaultSettingsFileName);
uri = null;
if (commandLineArgs?.Length > 1)
{
path = commandLineArgs[1];
isValidUri = Uri.TryCreate(path, UriKind.Absolute, out uri);
logger.Info($"Found command-line argument for settings file: '{path}', the URI is {(isValidUri ? "valid" : "invalid")}.");
}
if (!isValidUri && File.Exists(programDataSettings))
{
path = programDataSettings;
isValidUri = Uri.TryCreate(path, UriKind.Absolute, out uri);
logger.Info($"Found settings file in PROGRAMDATA: '{path}', the URI is {(isValidUri ? "valid" : "invalid")}.");
}
if (!isValidUri && File.Exists(appDataSettings))
{
path = appDataSettings;
isValidUri = Uri.TryCreate(path, UriKind.Absolute, out uri);
logger.Info($"Found settings file in APPDATA: '{path}', the URI is {(isValidUri ? "valid" : "invalid")}.");
}
return isValidUri;
}
}
}

View file

@ -6,6 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using SafeExamBrowser.Contracts.Configuration.Settings;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.Runtime;
@ -15,6 +16,8 @@ namespace SafeExamBrowser.Runtime.Behaviour
{
private ILogger logger;
public ISettings Settings { private get; set; }
public RuntimeController(ILogger logger)
{
this.logger = logger;

View file

@ -6,12 +6,12 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using SafeExamBrowser.Configuration;
using SafeExamBrowser.Configuration.Settings;
using SafeExamBrowser.Contracts.Behaviour;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Logging;
@ -34,9 +34,11 @@ namespace SafeExamBrowser.Runtime
internal void BuildObjectGraph()
{
var args = Environment.GetCommandLineArgs();
var logger = new Logger();
var nativeMethods = new NativeMethods();
var runtimeInfo = new RuntimeInfo();
var settingsRepository = new SettingsRepository();
var systemInfo = new SystemInfo();
var uiFactory = new UserInterfaceFactory();
@ -51,8 +53,7 @@ namespace SafeExamBrowser.Runtime
StartupOperations = new Queue<IOperation>();
StartupOperations.Enqueue(new I18nOperation(logger, text));
// TODO
//StartupOperations.Enqueue(new ConfigurationOperation());
StartupOperations.Enqueue(new ConfigurationOperation(logger, runtimeController, runtimeInfo, settingsRepository, args));
//StartupOperations.Enqueue(new KioskModeOperation());
StartupOperations.Enqueue(new RuntimeControllerOperation(runtimeController, logger));
}
@ -70,7 +71,9 @@ namespace SafeExamBrowser.Runtime
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");

View file

@ -87,6 +87,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="App.cs" />
<Compile Include="Behaviour\Operations\ConfigurationOperation.cs" />
<Compile Include="Behaviour\Operations\RuntimeControllerOperation.cs" />
<Compile Include="CompositionRoot.cs" />
<Compile Include="Properties\AssemblyInfo.cs">