Changed logging system to be event based.
Implemented LRUCache for instantiating services without starting. Service state changed back to enumerator. Namespace refactoring.
This commit is contained in:
parent
cac5ca054c
commit
35a2765559
@ -6,10 +6,12 @@ namespace GameServiceWarden.API.Module
|
|||||||
{
|
{
|
||||||
public interface IService
|
public interface IService
|
||||||
{
|
{
|
||||||
event EventHandler<bool> StateChangeEvent;
|
event EventHandler<ServiceState> StateChangeEvent;
|
||||||
|
event EventHandler<string> UpdateLogEvent;
|
||||||
IReadOnlyCollection<IServiceConfigurable> Configurables{ get; }
|
IReadOnlyCollection<IServiceConfigurable> Configurables{ get; }
|
||||||
void InitializeService(Stream stream);
|
void InitializeService();
|
||||||
void ElegantShutdown();
|
void ElegantShutdown();
|
||||||
|
byte[] GetLogBuffer();
|
||||||
void ExecuteCommand(string command);
|
void ExecuteCommand(string command);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -23,8 +23,7 @@ namespace GameServiceWarden.API.Module
|
|||||||
/// Creates an instance of a the service to be used.
|
/// Creates an instance of a the service to be used.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="workspace">The workspace directory. All service required files should be stored here. Expect the directory to be created.</param>
|
/// <param name="workspace">The workspace directory. All service required files should be stored here. Expect the directory to be created.</param>
|
||||||
/// <param name="clean">Whether or not this game service is new. That is, the <code>workspace</code> can be assumed empty.</param>
|
|
||||||
/// <returns>The <see cref="IService"/> responsible for the instance of the game service.</returns>
|
/// <returns>The <see cref="IService"/> responsible for the instance of the game service.</returns>
|
||||||
IService InstantiateService(string workspace, bool clean);
|
IService InstantiateService(string workspace);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,5 +4,6 @@ namespace GameServiceWarden.API.Module
|
|||||||
{
|
{
|
||||||
Stopped,
|
Stopped,
|
||||||
Running,
|
Running,
|
||||||
|
RestartPending
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -9,7 +9,7 @@ namespace GameServiceWarden.API.Games
|
|||||||
public ICollection<string> services;
|
public ICollection<string> services;
|
||||||
public ICollection<string> running;
|
public ICollection<string> running;
|
||||||
public ICollection<string> modules;
|
public ICollection<string> modules;
|
||||||
public IReadOnlyDictionary<string, string> logs;
|
public IReadOnlyDictionary<string, byte[]> logs;
|
||||||
public IReadOnlyDictionary<string, IReadOnlyDictionary<string, string>> serviceOptions;
|
public IReadOnlyDictionary<string, IReadOnlyDictionary<string, string>> serviceOptions;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,264 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.Globalization;
|
|
||||||
using System.IO;
|
|
||||||
using System.IO.Pipes;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using GameServiceWarden.Core.Games.Modules.Exceptions;
|
|
||||||
using GameServiceWarden.Core.Logging;
|
|
||||||
using GameServiceWarden.API.Module;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
|
|
||||||
//TODO Update UML
|
|
||||||
namespace GameServiceWarden.Core.Games
|
|
||||||
{
|
|
||||||
public class ServiceDescriptor //entity
|
|
||||||
{
|
|
||||||
private const string LOG_DISTRIBUTOR_PREFIX = "log_dist_";
|
|
||||||
private const int TIMEOUT = 1000;
|
|
||||||
/// <summary>
|
|
||||||
/// The name of the service itself, independent of the name of the module this service is using.
|
|
||||||
/// </summary>
|
|
||||||
public string ServiceName { get { return serviceName; } }
|
|
||||||
private readonly string serviceName;
|
|
||||||
private readonly Guid runningUID;
|
|
||||||
private volatile bool running;
|
|
||||||
private readonly IService service;
|
|
||||||
/// <summary>
|
|
||||||
/// The services log output pipe name.
|
|
||||||
/// </summary>
|
|
||||||
public string ServiceLogPipeName { get { return (runningUID.ToString() + ".pipe"); } }
|
|
||||||
private string moduleName;
|
|
||||||
private readonly string assemblyName;
|
|
||||||
private volatile NamedPipeServerStream logReceiver;
|
|
||||||
private volatile NamedPipeClientStream logSender;
|
|
||||||
private ConcurrentStack<NamedPipeServerStream> logStreamListeners;
|
|
||||||
private Task logUpdateTask;
|
|
||||||
private Task listenTask;
|
|
||||||
private volatile CancellationTokenSource stopToken;
|
|
||||||
private NamedPipeServerStream acceptingPipe;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Name of module this service uses.
|
|
||||||
/// </summary>
|
|
||||||
private readonly IReadOnlyDictionary<string, IServiceConfigurable> configurables;
|
|
||||||
public event EventHandler<bool> ServiceStateChangeEvent;
|
|
||||||
public ServiceDescriptor(IService service, string serviceName, string moduleName, string assemblyName)
|
|
||||||
{
|
|
||||||
this.service = service ?? throw new ArgumentNullException("service");
|
|
||||||
this.moduleName = moduleName ?? throw new ArgumentNullException("moduleName");
|
|
||||||
this.assemblyName = assemblyName ?? throw new ArgumentNullException("assemblyName");
|
|
||||||
this.serviceName = serviceName ?? throw new ArgumentNullException("serviceName");
|
|
||||||
this.service.StateChangeEvent += OnServiceStateChange;
|
|
||||||
runningUID = Guid.NewGuid();
|
|
||||||
|
|
||||||
|
|
||||||
Dictionary<string, IServiceConfigurable> tempConfigurables = new Dictionary<string, IServiceConfigurable>();
|
|
||||||
foreach (IServiceConfigurable configurable in service.Configurables)
|
|
||||||
{
|
|
||||||
tempConfigurables.Add(configurable.OptionName, configurable);
|
|
||||||
}
|
|
||||||
this.configurables = new ReadOnlyDictionary<string, IServiceConfigurable>(tempConfigurables);
|
|
||||||
|
|
||||||
logStreamListeners = new ConcurrentStack<NamedPipeServerStream>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Starts this service.
|
|
||||||
/// </summary>
|
|
||||||
/// <exception cref="InvalidOperationException">Is thrown when the service is already running.</exception>
|
|
||||||
public void Start()
|
|
||||||
{
|
|
||||||
Logger.Log($"\"{ServiceName}\" is starting.");
|
|
||||||
if (running) throw new InvalidOperationException("Service instance already running.");
|
|
||||||
logReceiver = new NamedPipeServerStream(LOG_DISTRIBUTOR_PREFIX + ServiceLogPipeName, PipeDirection.In, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
|
|
||||||
Task waitForConnection = logReceiver.WaitForConnectionAsync();
|
|
||||||
logSender = new NamedPipeClientStream(".", LOG_DISTRIBUTOR_PREFIX + ServiceLogPipeName, PipeDirection.Out);
|
|
||||||
logSender.Connect();
|
|
||||||
waitForConnection.Wait();
|
|
||||||
byte[] idToken = Guid.NewGuid().ToByteArray();
|
|
||||||
ValueTask sendTokenTask = logSender.WriteAsync(idToken);
|
|
||||||
byte[] receivedToken = new byte[idToken.Length];
|
|
||||||
logReceiver.Read(receivedToken);
|
|
||||||
|
|
||||||
if (!sendTokenTask.AsTask().Wait(500) || !sendTokenTask.IsCompletedSuccessfully)
|
|
||||||
{
|
|
||||||
throw new ServiceInitializationException("Error while sending identification token.");
|
|
||||||
}
|
|
||||||
if (!idToken.SequenceEqual(receivedToken))
|
|
||||||
{
|
|
||||||
throw new ServiceInitializationException("Wrong distributor identification token.");
|
|
||||||
}
|
|
||||||
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(TIMEOUT);
|
|
||||||
Task initializationTask = Task.Run(() => service.InitializeService(logSender), cancellationTokenSource.Token);
|
|
||||||
initializationTask.Wait();
|
|
||||||
cancellationTokenSource.Dispose();
|
|
||||||
stopToken = new CancellationTokenSource();
|
|
||||||
listenTask = AcceptLogConnections();
|
|
||||||
logUpdateTask = BroadcastLog();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Stops the service.
|
|
||||||
/// </summary>
|
|
||||||
/// <exception cref="InvalidOperationException">Is thrown when the is not running.</exception>
|
|
||||||
public void Stop()
|
|
||||||
{
|
|
||||||
if (!running) throw new InvalidOperationException("Service instance not running.");
|
|
||||||
Logger.Log($"\"{ServiceName}\" is stopping.");
|
|
||||||
service.ElegantShutdown();
|
|
||||||
stopToken.Cancel(); // Doesn't work on Linux(?)
|
|
||||||
acceptingPipe.Dispose(); //Handles Linux case
|
|
||||||
logSender.Dispose(); //Makes sure logging client is disposed
|
|
||||||
logReceiver.Dispose(); //Closes receiver (Linux doesn't respond to cancellations, needed to dispose either way).
|
|
||||||
|
|
||||||
NamedPipeServerStream terminatingPipe;
|
|
||||||
while (logStreamListeners.TryPop(out terminatingPipe))
|
|
||||||
{
|
|
||||||
terminatingPipe.Dispose(); // Required before waiting since this is under listenTask.
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!listenTask.Wait(TIMEOUT)) {
|
|
||||||
throw new TimeoutException($"Could not stop \"{ServiceName}\" accepting task within {TIMEOUT}ms.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (AggregateException e)
|
|
||||||
{
|
|
||||||
e.Handle((exception) => exception is TaskCanceledException || (exception is SocketException && exception.Message.Equals("Operation canceled"))); //Task cancel for Windows, Socket for operation cancellation.
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!logUpdateTask.Wait(TIMEOUT)) {
|
|
||||||
throw new TimeoutException($"Could not stop \"{ServiceName}\" broadcast within{TIMEOUT}ms.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (AggregateException e)
|
|
||||||
{
|
|
||||||
e.Handle((exception) => exception is TaskCanceledException || (exception is SocketException && exception.Message.Equals("Operation canceled"))); //Same as above.
|
|
||||||
}
|
|
||||||
stopToken.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sends a command to this service to execute.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="command">The command to execute.</param>
|
|
||||||
/// <exception cref="InvalidOperationException">Is thrown when the service is not running.</exception>
|
|
||||||
public void ExecuteCommand(string command)
|
|
||||||
{
|
|
||||||
Logger.Log($"\"{ServiceName}\" is executing command \"{command}\".", LogLevel.DEBUG);
|
|
||||||
if (!running) throw new InvalidOperationException("Service instance not running.");
|
|
||||||
service.ExecuteCommand(command);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the possible <see cref="IServiceConfigurable"/>'s names for this service.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>A <see cref="ISet{string}"/> returned where the string is the option's name.</returns>
|
|
||||||
public ISet<string> GetConfigurableOptions()
|
|
||||||
{
|
|
||||||
return new HashSet<string>(this.configurables.Keys);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool SetConfigurableValue(string configurationName, string value)
|
|
||||||
{
|
|
||||||
if (!this.configurables.ContainsKey(configurationName)) throw new KeyNotFoundException($"Unable to find option with name \"{configurationName}\".");
|
|
||||||
return this.configurables[configurationName].SetValue(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetConfigurableValue(string configurationName)
|
|
||||||
{
|
|
||||||
if (!this.configurables.ContainsKey(configurationName)) throw new KeyNotFoundException($"Unable to find option with name \"{configurationName}\".");
|
|
||||||
return this.configurables[configurationName].GetValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool GetServiceState()
|
|
||||||
{
|
|
||||||
return running;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetServiceName()
|
|
||||||
{
|
|
||||||
return serviceName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetModuleName()
|
|
||||||
{
|
|
||||||
return moduleName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <returns>The name of assembly this module is contained in.</returns>
|
|
||||||
public string GetAssemblyName()
|
|
||||||
{
|
|
||||||
return assemblyName;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnServiceStateChange(object sender, bool running)
|
|
||||||
{
|
|
||||||
this.running = running;
|
|
||||||
Logger.Log($"The service \"{ServiceName}\" is changing states to {(running ? "running" : "stopped")}.", LogLevel.DEBUG);
|
|
||||||
ServiceStateChangeEvent?.Invoke(this, running);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task AcceptLogConnections()
|
|
||||||
{
|
|
||||||
Logger.Log($"\"{ServiceName}\" is now accepting log listeners.");
|
|
||||||
while (running)
|
|
||||||
{
|
|
||||||
NamedPipeServerStream pipe = new NamedPipeServerStream(ServiceLogPipeName, PipeDirection.Out, NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
|
|
||||||
acceptingPipe = pipe;
|
|
||||||
await pipe.WaitForConnectionAsync(stopToken.Token);
|
|
||||||
Logger.Log($"A log listener has connected. Currently broadcasting to {logStreamListeners.Count + 1} listener(s).", LogLevel.DEBUG);
|
|
||||||
logStreamListeners.Push(pipe);
|
|
||||||
}
|
|
||||||
Logger.Log($"\"{ServiceName}\" stopped accepting log listeners.");
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task BroadcastLog()
|
|
||||||
{
|
|
||||||
Stack<NamedPipeServerStream> completeStack = new Stack<NamedPipeServerStream>();
|
|
||||||
Stack<(Task, CancellationTokenSource)> writeTasks = new Stack<(Task, CancellationTokenSource)>();
|
|
||||||
byte[] buffer = new byte[1024 * 8];
|
|
||||||
int fill;
|
|
||||||
Logger.Log($"\"{ServiceName}\" is now listening to the service log and broadcasting.");
|
|
||||||
while (running && (fill = await logReceiver.ReadAsync(buffer, 0, buffer.Length, stopToken.Token)) > 0)
|
|
||||||
{
|
|
||||||
Logger.Log($"Broadcasting {fill} bytes.", LogLevel.DEBUG);
|
|
||||||
NamedPipeServerStream pipe;
|
|
||||||
while (logStreamListeners.TryPop(out pipe))
|
|
||||||
{
|
|
||||||
if (!pipe.IsConnected)
|
|
||||||
{
|
|
||||||
pipe.Dispose();
|
|
||||||
Logger.Log($"\"{ServiceName}\" detected a disconnected log listener. Removing from list of listener(s).", LogLevel.DEBUG);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
CancellationTokenSource cancelToken = new CancellationTokenSource(1000);
|
|
||||||
writeTasks.Push((pipe.WriteAsync(buffer, 0, fill, cancelToken.Token), cancelToken));
|
|
||||||
completeStack.Push(pipe);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
NamedPipeServerStream completePipe;
|
|
||||||
while (completeStack.TryPop(out completePipe))
|
|
||||||
{
|
|
||||||
logStreamListeners.Push(completePipe);
|
|
||||||
}
|
|
||||||
(Task, CancellationTokenSource) taskAndCancel;
|
|
||||||
while (writeTasks.TryPop(out taskAndCancel))
|
|
||||||
{
|
|
||||||
await taskAndCancel.Item1;
|
|
||||||
taskAndCancel.Item2.Dispose();
|
|
||||||
}
|
|
||||||
Logger.Log($"\"{ServiceName}\" broadcasted to {logStreamListeners.Count} listener(s).", LogLevel.DEBUG);
|
|
||||||
}
|
|
||||||
Logger.Log($"\"{ServiceName}\" stopped listening to service log.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Runtime.Serialization;
|
using System.Runtime.Serialization;
|
||||||
|
|
||||||
namespace GameServiceWarden.Core.Games.Modules.Exceptions
|
namespace GameServiceWarden.Core.Module.Exceptions
|
||||||
{
|
{
|
||||||
[System.Serializable]
|
[System.Serializable]
|
||||||
public class ModuleLoadException : Exception
|
public class ModuleLoadException : Exception
|
@ -1,4 +1,4 @@
|
|||||||
namespace GameServiceWarden.Core.Games.Modules.Exceptions
|
namespace GameServiceWarden.Core.Module.Exceptions
|
||||||
{
|
{
|
||||||
[System.Serializable]
|
[System.Serializable]
|
||||||
public class ServiceInitializationException : System.Exception
|
public class ServiceInitializationException : System.Exception
|
@ -1,6 +1,6 @@
|
|||||||
using GameServiceWarden.API.Games;
|
using GameServiceWarden.API.Games;
|
||||||
|
|
||||||
namespace GameServiceWarden.Core.Games
|
namespace GameServiceWarden.Core.Module
|
||||||
{
|
{
|
||||||
public interface IServiceManagerActionExecuter
|
public interface IServiceManagerActionExecuter
|
||||||
{
|
{
|
@ -1,6 +1,6 @@
|
|||||||
using GameServiceWarden.API.Games;
|
using GameServiceWarden.API.Games;
|
||||||
|
|
||||||
namespace GameServiceWarden.Core.Games
|
namespace GameServiceWarden.Core.Module
|
||||||
{
|
{
|
||||||
public interface IServiceManagerMonitor
|
public interface IServiceManagerMonitor
|
||||||
{
|
{
|
77
src/GameServiceWarden.Core/Module/LRUCache.cs
Normal file
77
src/GameServiceWarden.Core/Module/LRUCache.cs
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace GameServiceWarden.Core.Module
|
||||||
|
{
|
||||||
|
public class LRUCache<K, V>
|
||||||
|
{
|
||||||
|
private class Node
|
||||||
|
{
|
||||||
|
public K key;
|
||||||
|
public V value;
|
||||||
|
public Node front;
|
||||||
|
public Node back;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Size {get { return size; } }
|
||||||
|
public int Length {get { return valueDictionary.Count; } }
|
||||||
|
private readonly int size;
|
||||||
|
private Node top;
|
||||||
|
private Node bottom;
|
||||||
|
private Dictionary<K, Node> valueDictionary;
|
||||||
|
private Action<V> cleanupAction;
|
||||||
|
|
||||||
|
public LRUCache(int size = 100, Action<V> cleanup = null)
|
||||||
|
{
|
||||||
|
this.size = size;
|
||||||
|
valueDictionary = new Dictionary<K, Node>(size);
|
||||||
|
this.cleanupAction = cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MoveToTop(K key) {
|
||||||
|
Node node = valueDictionary[key];
|
||||||
|
if (node != null && top != node) {
|
||||||
|
node.front.back = node.back;
|
||||||
|
node.back = top;
|
||||||
|
node.front = null;
|
||||||
|
top = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Node AddToTop(K key, V value) {
|
||||||
|
Node node = new Node();
|
||||||
|
node.front = null;
|
||||||
|
node.back = top;
|
||||||
|
node.value = value;
|
||||||
|
node.key = key;
|
||||||
|
top = node;
|
||||||
|
if (bottom == null) {
|
||||||
|
bottom = node;
|
||||||
|
} else if (valueDictionary.Count == Size) {
|
||||||
|
valueDictionary.Remove(bottom.key);
|
||||||
|
cleanupAction(bottom.value);
|
||||||
|
bottom = bottom.front;
|
||||||
|
}
|
||||||
|
valueDictionary[key] = node;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
public V Use(K key, Func<V> generate) {
|
||||||
|
if (generate == null) throw new ArgumentNullException("generate");
|
||||||
|
Node value = null;
|
||||||
|
if (valueDictionary.ContainsKey(key)) {
|
||||||
|
value = valueDictionary[key];
|
||||||
|
MoveToTop(key);
|
||||||
|
} else {
|
||||||
|
value = AddToTop(key, generate());
|
||||||
|
}
|
||||||
|
return value.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear() {
|
||||||
|
top = null;
|
||||||
|
bottom = null;
|
||||||
|
valueDictionary.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,7 @@ using System;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.Loader;
|
using System.Runtime.Loader;
|
||||||
|
|
||||||
namespace GameServiceWarden.Core.Games.Modules
|
namespace GameServiceWarden.Core.Module
|
||||||
{
|
{
|
||||||
class ModuleLoadContext : AssemblyLoadContext
|
class ModuleLoadContext : AssemblyLoadContext
|
||||||
{
|
{
|
@ -1,12 +1,12 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using GameServiceWarden.Core.Games.Modules.Exceptions;
|
using GameServiceWarden.Core.Module.Exceptions;
|
||||||
using GameServiceWarden.API.Module;
|
using GameServiceWarden.API.Module;
|
||||||
|
|
||||||
namespace GameServiceWarden.Core.Games.Modules
|
namespace GameServiceWarden.Core.Module
|
||||||
{
|
{
|
||||||
public class ServiceModuleLoader //Gateway
|
public class ModuleLoader //Gateway
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads an extension module.
|
/// Loads an extension module.
|
156
src/GameServiceWarden.Core/Module/ServiceDescriptor.cs
Normal file
156
src/GameServiceWarden.Core/Module/ServiceDescriptor.cs
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Pipes;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using GameServiceWarden.Core.Module.Exceptions;
|
||||||
|
using GameServiceWarden.Core.Logging;
|
||||||
|
using GameServiceWarden.API.Module;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
|
||||||
|
//TODO Update UML
|
||||||
|
namespace GameServiceWarden.Core.Module
|
||||||
|
{
|
||||||
|
public class ServiceDescriptor //entity
|
||||||
|
{
|
||||||
|
public const string MODULE_PROPERTY = "SERVICE_MODULE";
|
||||||
|
public const string ASSEMBLY_PROPERTY = "SERVICE_ASSEMBLY";
|
||||||
|
private const int TIMEOUT = 1000;
|
||||||
|
/// <summary>
|
||||||
|
/// The name of the service itself, independent of the name of the module this service is using.
|
||||||
|
/// </summary>
|
||||||
|
public string ServiceName { get { return serviceName; } }
|
||||||
|
private readonly string serviceName;
|
||||||
|
private volatile ServiceState state;
|
||||||
|
private readonly IService service;
|
||||||
|
private string moduleName;
|
||||||
|
private readonly string assemblyName;
|
||||||
|
/// <summary>
|
||||||
|
/// Name of module this service uses.
|
||||||
|
/// </summary>
|
||||||
|
private readonly IReadOnlyDictionary<string, IServiceConfigurable> configurables;
|
||||||
|
public event EventHandler<ServiceState> ServiceStateChangeEvent;
|
||||||
|
public event EventHandler<string> LogUpdateEvent;
|
||||||
|
public ServiceDescriptor(IService service, string serviceName, string moduleName, string assemblyName)
|
||||||
|
{
|
||||||
|
this.service = service ?? throw new ArgumentNullException("service");
|
||||||
|
this.moduleName = moduleName ?? throw new ArgumentNullException("moduleName");
|
||||||
|
this.assemblyName = assemblyName ?? throw new ArgumentNullException("assemblyName");
|
||||||
|
this.serviceName = serviceName ?? throw new ArgumentNullException("serviceName");
|
||||||
|
this.service.StateChangeEvent += OnServiceStateChange;
|
||||||
|
this.service.UpdateLogEvent += OnLogUpdate;
|
||||||
|
|
||||||
|
Dictionary<string, IServiceConfigurable> tempConfigurables = new Dictionary<string, IServiceConfigurable>();
|
||||||
|
foreach (IServiceConfigurable configurable in service.Configurables)
|
||||||
|
{
|
||||||
|
tempConfigurables.Add(configurable.OptionName, configurable);
|
||||||
|
}
|
||||||
|
this.configurables = new ReadOnlyDictionary<string, IServiceConfigurable>(tempConfigurables);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Starts this service.
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="InvalidOperationException">Is thrown when the service is already running.</exception>
|
||||||
|
public void Start()
|
||||||
|
{
|
||||||
|
Logger.Log($"\"{ServiceName}\" is starting.");
|
||||||
|
if (state == ServiceState.Running) throw new InvalidOperationException("Service instance already running.");
|
||||||
|
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(TIMEOUT);
|
||||||
|
Task initializationTask = Task.Run(() => service.InitializeService(), cancellationTokenSource.Token);
|
||||||
|
try {
|
||||||
|
initializationTask.Wait();
|
||||||
|
} catch (AggregateException a) {
|
||||||
|
a.Handle((e) => e is TaskCanceledException);
|
||||||
|
}
|
||||||
|
cancellationTokenSource.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stops the service.
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="InvalidOperationException">Is thrown when the is not running.</exception>
|
||||||
|
public void Stop()
|
||||||
|
{
|
||||||
|
if (state != ServiceState.Running) throw new InvalidOperationException("Service instance not running.");
|
||||||
|
Logger.Log($"\"{ServiceName}\" is stopping.");
|
||||||
|
service.ElegantShutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sends a command to this service to execute.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="command">The command to execute.</param>
|
||||||
|
/// <exception cref="InvalidOperationException">Is thrown when the service is not running.</exception>
|
||||||
|
public void ExecuteCommand(string command)
|
||||||
|
{
|
||||||
|
Logger.Log($"\"{ServiceName}\" is executing command \"{command}\".", LogLevel.DEBUG);
|
||||||
|
if (state != ServiceState.Running) throw new InvalidOperationException("Service instance not running.");
|
||||||
|
service.ExecuteCommand(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the possible <see cref="IServiceConfigurable"/>'s names for this service.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A <see cref="ISet{string}"/> returned where the string is the option's name.</returns>
|
||||||
|
public ISet<string> GetConfigurableOptions()
|
||||||
|
{
|
||||||
|
return new HashSet<string>(this.configurables.Keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool SetConfigurableValue(string configurationName, string value)
|
||||||
|
{
|
||||||
|
if (!this.configurables.ContainsKey(configurationName)) throw new KeyNotFoundException($"Unable to find option with name \"{configurationName}\".");
|
||||||
|
return this.configurables[configurationName].SetValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetConfigurableValue(string configurationName)
|
||||||
|
{
|
||||||
|
if (!this.configurables.ContainsKey(configurationName)) throw new KeyNotFoundException($"Unable to find option with name \"{configurationName}\".");
|
||||||
|
return this.configurables[configurationName].GetValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceState GetServiceState()
|
||||||
|
{
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetServiceName()
|
||||||
|
{
|
||||||
|
return serviceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetModuleName()
|
||||||
|
{
|
||||||
|
return moduleName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <returns>The name of assembly this module is contained in.</returns>
|
||||||
|
public string GetAssemblyName()
|
||||||
|
{
|
||||||
|
return assemblyName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] GetLogBuffer() {
|
||||||
|
if (state == ServiceState.Stopped) throw new InvalidOperationException("Cannot get log of service that is not running.");
|
||||||
|
return service.GetLogBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnServiceStateChange(object sender, ServiceState state)
|
||||||
|
{
|
||||||
|
this.state = state;
|
||||||
|
Logger.Log($"The service \"{ServiceName}\" is changing states to {this.state}.", LogLevel.DEBUG);
|
||||||
|
ServiceStateChangeEvent?.Invoke(this, this.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnLogUpdate(object sender, string log) {
|
||||||
|
LogUpdateEvent?.Invoke(this, log);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -9,22 +9,25 @@ using System.Linq;
|
|||||||
using GameServiceWarden.API.Games;
|
using GameServiceWarden.API.Games;
|
||||||
using GameServiceWarden.Core.Persistence;
|
using GameServiceWarden.Core.Persistence;
|
||||||
using GameServiceWarden.API.Module;
|
using GameServiceWarden.API.Module;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace GameServiceWarden.Core.Games
|
namespace GameServiceWarden.Core.Module
|
||||||
{
|
{
|
||||||
public class ServiceManager : IServiceManagerActionExecuter
|
public class ServiceManager : IServiceManagerActionExecuter
|
||||||
{
|
{
|
||||||
private IServiceManagerMonitor managerMonitor;
|
private IServiceManagerMonitor managerMonitor;
|
||||||
private readonly ConcurrentDictionary<string, ServiceDescriptor> running;
|
private readonly ConcurrentDictionary<string, ServiceDescriptor> running;
|
||||||
private readonly IPersistent<ServiceDescriptor> services;
|
private readonly IPersistent<IReadOnlyDictionary<string, string>> services;
|
||||||
|
private readonly LRUCache<string, ServiceDescriptor> descriptorCache;
|
||||||
private readonly IReadOnlyPersistent<IReadOnlyDictionary<string, IServiceModule>> modules;
|
private readonly IReadOnlyPersistent<IReadOnlyDictionary<string, IServiceModule>> modules;
|
||||||
|
|
||||||
public ServiceManager(IServiceManagerMonitor actionMonitor, IPersistent<ServiceDescriptor> services, IReadOnlyPersistent<IReadOnlyDictionary<string, IServiceModule>> modules)
|
public ServiceManager(IServiceManagerMonitor actionMonitor, IPersistent<IReadOnlyDictionary<string, string>> services, IReadOnlyPersistent<IReadOnlyDictionary<string, IServiceModule>> modules)
|
||||||
{
|
{
|
||||||
this.services = services ?? throw new ArgumentNullException("services");
|
this.services = services ?? throw new ArgumentNullException("services");
|
||||||
this.modules = modules ?? throw new ArgumentNullException("modules");
|
this.modules = modules ?? throw new ArgumentNullException("modules");
|
||||||
this.managerMonitor = actionMonitor ?? throw new ArgumentNullException("actionMonitor");
|
this.managerMonitor = actionMonitor ?? throw new ArgumentNullException("actionMonitor");
|
||||||
this.running = new ConcurrentDictionary<string, ServiceDescriptor>();
|
this.running = new ConcurrentDictionary<string, ServiceDescriptor>();
|
||||||
|
this.descriptorCache = new LRUCache<string, ServiceDescriptor>(100);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CreateService(string serviceName, string assemblyName, string moduleName)
|
public void CreateService(string serviceName, string assemblyName, string moduleName)
|
||||||
@ -32,8 +35,10 @@ namespace GameServiceWarden.Core.Games
|
|||||||
if (!this.modules.ContainsKey(assemblyName)) throw new KeyNotFoundException($"No file \"{assemblyName}\" found.");
|
if (!this.modules.ContainsKey(assemblyName)) throw new KeyNotFoundException($"No file \"{assemblyName}\" found.");
|
||||||
IReadOnlyDictionary<string, IServiceModule> assemblyModules = this.modules[assemblyName];
|
IReadOnlyDictionary<string, IServiceModule> assemblyModules = this.modules[assemblyName];
|
||||||
if (services.ContainsKey(serviceName)) throw new ArgumentException($"Service of Name \"{serviceName}\" already exists.");
|
if (services.ContainsKey(serviceName)) throw new ArgumentException($"Service of Name \"{serviceName}\" already exists.");
|
||||||
|
Dictionary<string, string> data = new Dictionary<string, string>();
|
||||||
services.AddToPersistence(serviceName, new ServiceDescriptor(assemblyModules[moduleName].InstantiateService(services.GetPathForKey(serviceName), true), serviceName, moduleName, assemblyName));
|
data[ServiceDescriptor.ASSEMBLY_PROPERTY] = assemblyName;
|
||||||
|
data[ServiceDescriptor.MODULE_PROPERTY] = moduleName;
|
||||||
|
services.AddToPersistence(serviceName, data);
|
||||||
ServiceManagerState managerState = new ServiceManagerState();
|
ServiceManagerState managerState = new ServiceManagerState();
|
||||||
managerState.delta = true;
|
managerState.delta = true;
|
||||||
managerState.subtract = false;
|
managerState.subtract = false;
|
||||||
@ -45,7 +50,7 @@ namespace GameServiceWarden.Core.Games
|
|||||||
public void DeleteService(string serviceName)
|
public void DeleteService(string serviceName)
|
||||||
{
|
{
|
||||||
if (!services.ContainsKey(serviceName)) throw new KeyNotFoundException($"Service under name \"{serviceName}\" not found.");
|
if (!services.ContainsKey(serviceName)) throw new KeyNotFoundException($"Service under name \"{serviceName}\" not found.");
|
||||||
if (services[serviceName].GetServiceState()) services[serviceName].Stop();
|
if (running.ContainsKey(serviceName)) running[serviceName].Stop();
|
||||||
services.Delete(serviceName);
|
services.Delete(serviceName);
|
||||||
ServiceManagerState managerState = new ServiceManagerState();
|
ServiceManagerState managerState = new ServiceManagerState();
|
||||||
managerState.delta = true;
|
managerState.delta = true;
|
||||||
@ -81,15 +86,18 @@ namespace GameServiceWarden.Core.Games
|
|||||||
private IEnumerable<string> GetServiceOptions(string serviceName)
|
private IEnumerable<string> GetServiceOptions(string serviceName)
|
||||||
{
|
{
|
||||||
if (!services.ContainsKey(serviceName)) throw new KeyNotFoundException($"Service under name \"{serviceName}\" not found.");
|
if (!services.ContainsKey(serviceName)) throw new KeyNotFoundException($"Service under name \"{serviceName}\" not found.");
|
||||||
ServiceDescriptor serviceInfo = services[serviceName];
|
IReadOnlyDictionary<string, string> info = services[serviceName];
|
||||||
return serviceInfo.GetConfigurableOptions();
|
ServiceDescriptor service = descriptorCache.Use(serviceName, () => GenerateDescriptor(serviceName, info[ServiceDescriptor.ASSEMBLY_PROPERTY], info[ServiceDescriptor.MODULE_PROPERTY]));
|
||||||
|
return service.GetConfigurableOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetServiceOptionValue(string serviceName, string optionName)
|
private string GetServiceOptionValue(string serviceName, string optionName)
|
||||||
{
|
{
|
||||||
if (!services.ContainsKey(serviceName)) throw new KeyNotFoundException($"Service under name \"{serviceName}\" not found.");
|
if (!services.ContainsKey(serviceName)) throw new KeyNotFoundException($"Service under name \"{serviceName}\" not found.");
|
||||||
if (!services[serviceName].GetConfigurableOptions().Contains(optionName)) throw new KeyNotFoundException($"Option \"{optionName}\" for service \"{serviceName}\" not found.");
|
IReadOnlyDictionary<string, string> info = services[serviceName];
|
||||||
return services[serviceName].GetConfigurableValue(optionName);
|
ServiceDescriptor service = descriptorCache.Use(serviceName, () => GenerateDescriptor(serviceName, info[ServiceDescriptor.ASSEMBLY_PROPERTY], info[ServiceDescriptor.MODULE_PROPERTY]));
|
||||||
|
if (!service.GetConfigurableOptions().Contains(optionName)) throw new KeyNotFoundException($"Option \"{optionName}\" for service \"{serviceName}\" not found.");
|
||||||
|
return service.GetConfigurableValue(optionName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IReadOnlyDictionary<string, IReadOnlyDictionary<string, string>> GetOptions() {
|
public IReadOnlyDictionary<string, IReadOnlyDictionary<string, string>> GetOptions() {
|
||||||
@ -112,9 +120,11 @@ namespace GameServiceWarden.Core.Games
|
|||||||
public bool SetServiceOptionValue(string serviceName, string optionName, string value)
|
public bool SetServiceOptionValue(string serviceName, string optionName, string value)
|
||||||
{
|
{
|
||||||
if (!services.ContainsKey(serviceName)) throw new KeyNotFoundException($"Service under name \"{serviceName}\" not found.");
|
if (!services.ContainsKey(serviceName)) throw new KeyNotFoundException($"Service under name \"{serviceName}\" not found.");
|
||||||
if (!services[serviceName].GetConfigurableOptions().Contains(optionName)) throw new KeyNotFoundException($"Option \"{optionName}\" for service \"{serviceName}\" not found.");
|
IReadOnlyDictionary<string, string> info = services[serviceName];
|
||||||
|
ServiceDescriptor service = descriptorCache.Use(serviceName, () => GenerateDescriptor(serviceName, info[ServiceDescriptor.ASSEMBLY_PROPERTY], info[ServiceDescriptor.MODULE_PROPERTY]));
|
||||||
|
if (!service.GetConfigurableOptions().Contains(optionName)) throw new KeyNotFoundException($"Option \"{optionName}\" for service \"{serviceName}\" not found.");
|
||||||
ServiceManagerState managerState = new ServiceManagerState();
|
ServiceManagerState managerState = new ServiceManagerState();
|
||||||
if (services[serviceName].SetConfigurableValue(optionName, value)) {
|
if (service.SetConfigurableValue(optionName, value)) {
|
||||||
managerState.delta = true;
|
managerState.delta = true;
|
||||||
Dictionary<string, IReadOnlyDictionary<string, string>> changedOption = new Dictionary<string, IReadOnlyDictionary<string, string>>();
|
Dictionary<string, IReadOnlyDictionary<string, string>> changedOption = new Dictionary<string, IReadOnlyDictionary<string, string>>();
|
||||||
Dictionary<string, string> options = new Dictionary<string, string>();
|
Dictionary<string, string> options = new Dictionary<string, string>();
|
||||||
@ -130,9 +140,11 @@ namespace GameServiceWarden.Core.Games
|
|||||||
{
|
{
|
||||||
if (!services.ContainsKey(serviceName)) throw new KeyNotFoundException($"Service under name \"{serviceName}\" not found.");
|
if (!services.ContainsKey(serviceName)) throw new KeyNotFoundException($"Service under name \"{serviceName}\" not found.");
|
||||||
if (running.ContainsKey(serviceName)) throw new InvalidOperationException($"Service under name \"{serviceName}\" is already running.");
|
if (running.ContainsKey(serviceName)) throw new InvalidOperationException($"Service under name \"{serviceName}\" is already running.");
|
||||||
ServiceDescriptor info = services[serviceName];
|
IReadOnlyDictionary<string, string> info = services[serviceName];
|
||||||
info.ServiceStateChangeEvent += OnServiceStateChange;
|
ServiceDescriptor service = descriptorCache.Use(serviceName, () => GenerateDescriptor(serviceName, info[ServiceDescriptor.ASSEMBLY_PROPERTY], info[ServiceDescriptor.MODULE_PROPERTY]));
|
||||||
info.Start();
|
service.ServiceStateChangeEvent += OnServiceStateChange;
|
||||||
|
service.LogUpdateEvent += OnLogUpdated;
|
||||||
|
service.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void StopService(string serviceName)
|
public void StopService(string serviceName)
|
||||||
@ -147,52 +159,55 @@ namespace GameServiceWarden.Core.Games
|
|||||||
running[serviceName].ExecuteCommand(command);
|
running[serviceName].ExecuteCommand(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetServiceLogPipeName(string serviceName)
|
private byte[] GetServiceLogBuffer(string serviceName) {
|
||||||
{
|
if (!running.ContainsKey(serviceName)) throw new InvalidOperationException($"Service under name \"{serviceName}\" is not running and therefore, does not have a log.");
|
||||||
if (!running.ContainsKey(serviceName)) throw new InvalidOperationException($"Service under name \"{serviceName}\" is not running.");
|
IReadOnlyDictionary<string, string> info = services[serviceName];
|
||||||
return running[serviceName].ServiceLogPipeName;
|
ServiceDescriptor service = descriptorCache.Use(serviceName, () => GenerateDescriptor(serviceName, info[ServiceDescriptor.ASSEMBLY_PROPERTY], info[ServiceDescriptor.MODULE_PROPERTY]));
|
||||||
|
return service.GetLogBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IReadOnlyDictionary<string, string> GetLogPipeNames() {
|
public Dictionary<string, byte[]> GetLogBuffer() {
|
||||||
ServiceManagerState managerState = new ServiceManagerState();
|
Dictionary<string, byte[]> logs = new Dictionary<string, byte[]>();
|
||||||
Dictionary<string, string> logPipeNames = new Dictionary<string, string>();
|
foreach (string service in running.Keys)
|
||||||
foreach (string service in GetRunningServiceNames())
|
|
||||||
{
|
{
|
||||||
logPipeNames.Add(service, GetServiceLogPipeName(service));
|
logs.Add(service, running[service].GetLogBuffer());
|
||||||
}
|
}
|
||||||
managerState.logs = logPipeNames;
|
ServiceManagerState managerState = new ServiceManagerState();
|
||||||
|
managerState.logs = logs;
|
||||||
managerMonitor.Present(managerState);
|
managerMonitor.Present(managerState);
|
||||||
return logPipeNames;
|
return logs;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnServiceStateChange(object sender, bool state) {
|
private ServiceDescriptor GenerateDescriptor(string name, string assembly, string module) {
|
||||||
|
return new ServiceDescriptor(modules[assembly][module].InstantiateService(services.GetPathForKey(name)), name, module, assembly);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnServiceStateChange(object sender, ServiceState state) {
|
||||||
ServiceDescriptor serviceInfo = (ServiceDescriptor)sender;
|
ServiceDescriptor serviceInfo = (ServiceDescriptor)sender;
|
||||||
ServiceManagerState managerChange = new ServiceManagerState();
|
ServiceManagerState managerChange = new ServiceManagerState();
|
||||||
|
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case true:
|
case ServiceState.Running:
|
||||||
if (running.TryAdd(serviceInfo.ServiceName, serviceInfo)) {
|
if (running.TryAdd(serviceInfo.ServiceName, serviceInfo)) {
|
||||||
managerChange.delta = true;
|
managerChange.delta = true;
|
||||||
managerChange.running = new List<string>();
|
managerChange.running = new List<string>();
|
||||||
managerChange.running.Add(serviceInfo.ServiceName);
|
managerChange.running.Add(serviceInfo.ServiceName);
|
||||||
Dictionary<string, string> logAdded = new Dictionary<string, string>();
|
|
||||||
logAdded.Add(serviceInfo.ServiceName, GetServiceLogPipeName(serviceInfo.ServiceName));
|
|
||||||
managerChange.logs = logAdded;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case false:
|
case ServiceState.Stopped:
|
||||||
ServiceDescriptor removed;
|
ServiceDescriptor removed;
|
||||||
if (running.TryRemove(serviceInfo.ServiceName, out removed)) {
|
if (running.TryRemove(serviceInfo.ServiceName, out removed)) {
|
||||||
removed.ServiceStateChangeEvent -= OnServiceStateChange;
|
removed.ServiceStateChangeEvent -= OnServiceStateChange;
|
||||||
services[serviceInfo.ServiceName] = removed;
|
removed.LogUpdateEvent -= OnLogUpdated;
|
||||||
|
Dictionary<string, string> removedInfo = new Dictionary<string, string>();
|
||||||
|
removedInfo[ServiceDescriptor.ASSEMBLY_PROPERTY] = removed.GetAssemblyName();
|
||||||
|
removedInfo[ServiceDescriptor.MODULE_PROPERTY] = removed.GetModuleName();
|
||||||
|
services[serviceInfo.ServiceName] = removedInfo;
|
||||||
managerChange.delta = true;
|
managerChange.delta = true;
|
||||||
managerChange.subtract = true;
|
managerChange.subtract = true;
|
||||||
managerChange.running = new List<string>();
|
managerChange.running = new List<string>();
|
||||||
managerChange.running.Add(serviceInfo.ServiceName);
|
managerChange.running.Add(serviceInfo.ServiceName);
|
||||||
Dictionary<string, string> logRemoved = new Dictionary<string, string>();
|
|
||||||
logRemoved.Add(serviceInfo.ServiceName, null);
|
|
||||||
managerChange.logs = logRemoved;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -200,6 +215,15 @@ namespace GameServiceWarden.Core.Games
|
|||||||
managerMonitor.Present(managerChange);
|
managerMonitor.Present(managerChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OnLogUpdated(object sender, string update) {
|
||||||
|
ServiceDescriptor service = (ServiceDescriptor)sender;
|
||||||
|
ServiceManagerState state = new ServiceManagerState();
|
||||||
|
state.delta = true;
|
||||||
|
Dictionary<string, string> logUpdate = new Dictionary<string, string>();
|
||||||
|
logUpdate[service.ServiceName] = update;
|
||||||
|
managerMonitor.Present(state);
|
||||||
|
}
|
||||||
|
|
||||||
public void ExecuteAction(ServiceManagerAction action)
|
public void ExecuteAction(ServiceManagerAction action)
|
||||||
{
|
{
|
||||||
switch (action.action)
|
switch (action.action)
|
||||||
@ -208,7 +232,7 @@ namespace GameServiceWarden.Core.Games
|
|||||||
GetServiceNames();
|
GetServiceNames();
|
||||||
GetRunningServiceNames();
|
GetRunningServiceNames();
|
||||||
GetModuleNames();
|
GetModuleNames();
|
||||||
GetLogPipeNames();
|
GetLogBuffer();
|
||||||
GetOptions();
|
GetOptions();
|
||||||
break;
|
break;
|
||||||
|
|
@ -1,5 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using GameServiceWarden.Core.Games;
|
using GameServiceWarden.Core.Module;
|
||||||
|
|
||||||
namespace GameServiceWarden.Core.Persistence
|
namespace GameServiceWarden.Core.Persistence
|
||||||
{
|
{
|
||||||
|
@ -3,39 +3,36 @@ using System.Collections;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using GameServiceWarden.Core.Games;
|
using GameServiceWarden.Core.Module;
|
||||||
using GameServiceWarden.API.Module;
|
using GameServiceWarden.API.Module;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
namespace GameServiceWarden.Core.Persistence
|
namespace GameServiceWarden.Core.Persistence
|
||||||
{
|
{
|
||||||
public class ServiceDescriptorPersistence : IPersistent<ServiceDescriptor>
|
public class ServiceDescriptorPersistence : IPersistent<IReadOnlyDictionary<string, string>>
|
||||||
{
|
{
|
||||||
private readonly IReadOnlyPersistent<IReadOnlyDictionary<string, IServiceModule>> modules;
|
|
||||||
private readonly string mapDirectory;
|
private readonly string mapDirectory;
|
||||||
private const string ASSEMBLY_NAME = "Assembly Name";
|
|
||||||
private const string MODULE_NAME = "Module Name";
|
|
||||||
private const string EXTENSION = ".sin";
|
private const string EXTENSION = ".sin";
|
||||||
|
|
||||||
|
|
||||||
public ServiceDescriptorPersistence(string mapDirectory, IReadOnlyPersistent<IReadOnlyDictionary<string, IServiceModule>> modules)
|
public ServiceDescriptorPersistence(string mapDirectory)
|
||||||
{
|
{
|
||||||
this.mapDirectory = mapDirectory;
|
this.mapDirectory = mapDirectory;
|
||||||
this.modules = modules;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ServiceDescriptor this[string key]
|
public IReadOnlyDictionary<string, string> this[string key]
|
||||||
{
|
{
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
SaveService(key, value.GetAssemblyName(), value.GetModuleName());
|
SaveService(key, value[ServiceDescriptor.ASSEMBLY_PROPERTY], value[ServiceDescriptor.MODULE_PROPERTY]);
|
||||||
}
|
}
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (!ContainsKey(key)) throw new KeyNotFoundException();
|
if (!ContainsKey(key)) throw new KeyNotFoundException();
|
||||||
string assemblyName = GetServiceInfoValue(key, ASSEMBLY_NAME);
|
Dictionary<string, string> info = new Dictionary<string, string>();
|
||||||
string moduleName = GetServiceInfoValue(key, MODULE_NAME);
|
info[ServiceDescriptor.ASSEMBLY_PROPERTY] = GetServiceInfoValue(key, ServiceDescriptor.ASSEMBLY_PROPERTY);
|
||||||
IService service = modules[assemblyName][moduleName].InstantiateService(GetPathForKey(key), false);
|
info[ServiceDescriptor.MODULE_PROPERTY] = GetServiceInfoValue(key, ServiceDescriptor.MODULE_PROPERTY);
|
||||||
return new ServiceDescriptor(service, key, moduleName, assemblyName);
|
return new ReadOnlyDictionary<string, string>(info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,10 +48,10 @@ namespace GameServiceWarden.Core.Persistence
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<ServiceDescriptor> Values {
|
public IEnumerable<IReadOnlyDictionary<string,string>> Values {
|
||||||
get {
|
get {
|
||||||
IEnumerable<string> keys = Keys;
|
IEnumerable<string> keys = Keys;
|
||||||
List<ServiceDescriptor> res = new List<ServiceDescriptor>();
|
List<IReadOnlyDictionary<string,string>> res = new List<IReadOnlyDictionary<string,string>>();
|
||||||
foreach (string key in keys)
|
foreach (string key in keys)
|
||||||
{
|
{
|
||||||
res.Add(this[key]);
|
res.Add(this[key]);
|
||||||
@ -75,7 +72,7 @@ namespace GameServiceWarden.Core.Persistence
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddToPersistence(string key, ServiceDescriptor value)
|
public void AddToPersistence(string key, IReadOnlyDictionary<string,string> value)
|
||||||
{
|
{
|
||||||
if (key == null) throw new ArgumentNullException();
|
if (key == null) throw new ArgumentNullException();
|
||||||
if (ContainsKey(key)) throw new ArgumentException();
|
if (ContainsKey(key)) throw new ArgumentException();
|
||||||
@ -96,13 +93,13 @@ namespace GameServiceWarden.Core.Persistence
|
|||||||
return Directory.Exists(GetPathForKey(key));
|
return Directory.Exists(GetPathForKey(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerator<KeyValuePair<string, ServiceDescriptor>> GetEnumerator()
|
public IEnumerator<KeyValuePair<string, IReadOnlyDictionary<string,string>>> GetEnumerator()
|
||||||
{
|
{
|
||||||
IEnumerable<string> keys = Keys;
|
IEnumerable<string> keys = Keys;
|
||||||
List<KeyValuePair<string, ServiceDescriptor>> result = new List<KeyValuePair<string, ServiceDescriptor>>();
|
List<KeyValuePair<string, IReadOnlyDictionary<string,string>>> result = new List<KeyValuePair<string, IReadOnlyDictionary<string,string>>>();
|
||||||
foreach (string key in keys)
|
foreach (string key in keys)
|
||||||
{
|
{
|
||||||
result.Add(new KeyValuePair<string, ServiceDescriptor>(key, this[key]));
|
result.Add(new KeyValuePair<string, IReadOnlyDictionary<string,string>>(key, this[key]));
|
||||||
}
|
}
|
||||||
return result.GetEnumerator();
|
return result.GetEnumerator();
|
||||||
}
|
}
|
||||||
@ -127,7 +124,7 @@ namespace GameServiceWarden.Core.Persistence
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryLoadValue(string key, [MaybeNullWhen(false)] out ServiceDescriptor value)
|
public bool TryLoadValue(string key, [MaybeNullWhen(false)] out IReadOnlyDictionary<string,string> value)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
value = this[key];
|
value = this[key];
|
||||||
@ -152,8 +149,8 @@ namespace GameServiceWarden.Core.Persistence
|
|||||||
Directory.CreateDirectory(serviceInfoPath);
|
Directory.CreateDirectory(serviceInfoPath);
|
||||||
using (StreamWriter writer = File.CreateText(Path.Combine(serviceInfoPath, key + EXTENSION)))
|
using (StreamWriter writer = File.CreateText(Path.Combine(serviceInfoPath, key + EXTENSION)))
|
||||||
{
|
{
|
||||||
writer.WriteLine($"{ASSEMBLY_NAME}: {assemblyName}");
|
writer.WriteLine($"{ServiceDescriptor.ASSEMBLY_PROPERTY}: {assemblyName}");
|
||||||
writer.WriteLine($"{MODULE_NAME}: {moduleName}");
|
writer.WriteLine($"{ServiceDescriptor.MODULE_PROPERTY}: {moduleName}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using GameServiceWarden.Core.Games.Modules;
|
using GameServiceWarden.Core.Module;
|
||||||
using GameServiceWarden.API.Module;
|
using GameServiceWarden.API.Module;
|
||||||
|
|
||||||
namespace GameServiceWarden.Core.Persistence
|
namespace GameServiceWarden.Core.Persistence
|
||||||
@ -12,7 +12,7 @@ namespace GameServiceWarden.Core.Persistence
|
|||||||
{
|
{
|
||||||
private readonly string mapDirectory;
|
private readonly string mapDirectory;
|
||||||
|
|
||||||
private readonly ServiceModuleLoader loader = new ServiceModuleLoader();
|
private readonly ModuleLoader loader = new ModuleLoader();
|
||||||
|
|
||||||
public ServiceModules(string mapDirectory)
|
public ServiceModules(string mapDirectory)
|
||||||
{
|
{
|
||||||
|
@ -2,7 +2,7 @@ using System.Diagnostics;
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using GameServiceWarden.API.Communicable;
|
using GameServiceWarden.API.Communicable;
|
||||||
using GameServiceWarden.API.Communicable.Requests;
|
using GameServiceWarden.API.Communicable.Requests;
|
||||||
using GameServiceWarden.Core.Games;
|
using GameServiceWarden.Core.Module;
|
||||||
using GameServiceWarden.Core.Logging;
|
using GameServiceWarden.Core.Logging;
|
||||||
|
|
||||||
namespace GameServiceWarden.Core.UI
|
namespace GameServiceWarden.Core.UI
|
||||||
|
@ -2,7 +2,7 @@ using System.Text.Json;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using GameServiceWarden.API.Communicable;
|
using GameServiceWarden.API.Communicable;
|
||||||
using GameServiceWarden.API.Games;
|
using GameServiceWarden.API.Games;
|
||||||
using GameServiceWarden.Core.Games;
|
using GameServiceWarden.Core.Module;
|
||||||
|
|
||||||
namespace GameServiceWarden.Core.UI
|
namespace GameServiceWarden.Core.UI
|
||||||
{
|
{
|
||||||
|
@ -2,7 +2,7 @@ using System.Collections;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using GameServiceWarden.Core.Games;
|
using GameServiceWarden.Core.Module;
|
||||||
using GameServiceWarden.Core.Persistence;
|
using GameServiceWarden.Core.Persistence;
|
||||||
|
|
||||||
namespace GameServiceWarden.Core.Tests.Modules
|
namespace GameServiceWarden.Core.Tests.Modules
|
||||||
|
@ -10,9 +10,11 @@ namespace GameServiceWarden.Core.Tests.Modules.Games
|
|||||||
{
|
{
|
||||||
public IReadOnlyCollection<IServiceConfigurable> Configurables { get; set; }
|
public IReadOnlyCollection<IServiceConfigurable> Configurables { get; set; }
|
||||||
|
|
||||||
public event EventHandler<bool> StateChangeEvent;
|
public event EventHandler<ServiceState> StateChangeEvent;
|
||||||
public ServiceState CurrentState { get; private set; } = ServiceState.Stopped;
|
public event EventHandler<string> UpdateLogEvent;
|
||||||
|
|
||||||
|
public ServiceState CurrentState { get; private set; } = ServiceState.Stopped;
|
||||||
|
private MemoryStream memoryStream;
|
||||||
private StreamWriter consoleWriter;
|
private StreamWriter consoleWriter;
|
||||||
private Stack<Task> taskStack = new Stack<Task>();
|
private Stack<Task> taskStack = new Stack<Task>();
|
||||||
|
|
||||||
@ -29,7 +31,7 @@ namespace GameServiceWarden.Core.Tests.Modules.Games
|
|||||||
public void ElegantShutdown()
|
public void ElegantShutdown()
|
||||||
{
|
{
|
||||||
CurrentState = ServiceState.Stopped;
|
CurrentState = ServiceState.Stopped;
|
||||||
StateChangeEvent?.Invoke(this, false);
|
StateChangeEvent?.Invoke(this, ServiceState.Stopped);
|
||||||
Task task;
|
Task task;
|
||||||
while(taskStack.TryPop(out task)) {
|
while(taskStack.TryPop(out task)) {
|
||||||
if (task.IsCompleted) {
|
if (task.IsCompleted) {
|
||||||
@ -44,13 +46,20 @@ namespace GameServiceWarden.Core.Tests.Modules.Games
|
|||||||
{
|
{
|
||||||
taskStack.Push(consoleWriter.WriteLineAsync(command));
|
taskStack.Push(consoleWriter.WriteLineAsync(command));
|
||||||
taskStack.Push(consoleWriter.FlushAsync());
|
taskStack.Push(consoleWriter.FlushAsync());
|
||||||
|
UpdateLogEvent?.Invoke(this, command);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void InitializeService(Stream stream)
|
public void InitializeService()
|
||||||
{
|
{
|
||||||
CurrentState = ServiceState.Running;
|
CurrentState = ServiceState.Running;
|
||||||
this.consoleWriter = new StreamWriter(stream);
|
memoryStream = new MemoryStream();
|
||||||
StateChangeEvent?.Invoke(this, true);
|
this.consoleWriter = new StreamWriter(memoryStream);
|
||||||
|
StateChangeEvent?.Invoke(this, ServiceState.Running);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] GetLogBuffer()
|
||||||
|
{
|
||||||
|
return memoryStream.ToArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using GameServiceWarden.API.Games;
|
using GameServiceWarden.API.Games;
|
||||||
using GameServiceWarden.Core.Games;
|
using GameServiceWarden.Core.Module;
|
||||||
|
|
||||||
namespace GameServiceWarden.Core.Tests.Modules.Games
|
namespace GameServiceWarden.Core.Tests.Modules.Games
|
||||||
{
|
{
|
||||||
|
@ -17,7 +17,7 @@ namespace GameServiceWarden.Core.Tests.Modules.Games
|
|||||||
|
|
||||||
public IEnumerable<string> Authors { get; private set; } = new string[] { "FakeAuthor", "FakeAuthor2" };
|
public IEnumerable<string> Authors { get; private set; } = new string[] { "FakeAuthor", "FakeAuthor2" };
|
||||||
|
|
||||||
public IService InstantiateService(string workspace, bool clean)
|
public IService InstantiateService(string workspace)
|
||||||
{
|
{
|
||||||
return new FakeService(configurables);
|
return new FakeService(configurables);
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using GameServiceWarden.Core.Games;
|
using GameServiceWarden.Core.Module;
|
||||||
using GameServiceWarden.Core.Logging;
|
using GameServiceWarden.Core.Logging;
|
||||||
using GameServiceWarden.API.Module;
|
using GameServiceWarden.API.Module;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using Xunit.Abstractions;
|
using Xunit.Abstractions;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace GameServiceWarden.Core.Tests.Modules.Games
|
namespace GameServiceWarden.Core.Tests.Modules.Games
|
||||||
{
|
{
|
||||||
@ -34,7 +35,7 @@ namespace GameServiceWarden.Core.Tests.Modules.Games
|
|||||||
IService stubService = new FakeService();
|
IService stubService = new FakeService();
|
||||||
ServiceDescriptor serviceInfo = new ServiceDescriptor(stubService, SERVICE_NAME, "FakeModule", "FakeAssembly");
|
ServiceDescriptor serviceInfo = new ServiceDescriptor(stubService, SERVICE_NAME, "FakeModule", "FakeAssembly");
|
||||||
serviceInfo.Start();
|
serviceInfo.Start();
|
||||||
Assert.True(serviceInfo.GetServiceState());
|
Assert.Equal(ServiceState.Running, serviceInfo.GetServiceState());
|
||||||
serviceInfo.Stop();
|
serviceInfo.Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,7 +47,7 @@ namespace GameServiceWarden.Core.Tests.Modules.Games
|
|||||||
ServiceDescriptor serviceInfo = new ServiceDescriptor(stubService, SERVICE_NAME, "FakeModule", "FakeAssembly");
|
ServiceDescriptor serviceInfo = new ServiceDescriptor(stubService, SERVICE_NAME, "FakeModule", "FakeAssembly");
|
||||||
serviceInfo.Start();
|
serviceInfo.Start();
|
||||||
serviceInfo.Stop();
|
serviceInfo.Stop();
|
||||||
Assert.False(serviceInfo.GetServiceState());
|
Assert.Equal(ServiceState.Stopped, serviceInfo.GetServiceState());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@ -89,7 +90,7 @@ namespace GameServiceWarden.Core.Tests.Modules.Games
|
|||||||
IService stubService = new FakeService();
|
IService stubService = new FakeService();
|
||||||
ServiceDescriptor serviceInfo = new ServiceDescriptor(stubService, SERVICE_NAME, "FakeModule", "FakeAssembly");
|
ServiceDescriptor serviceInfo = new ServiceDescriptor(stubService, SERVICE_NAME, "FakeModule", "FakeAssembly");
|
||||||
//Then
|
//Then
|
||||||
Assert.False(serviceInfo.GetServiceState());
|
Assert.Equal(ServiceState.Stopped, serviceInfo.GetServiceState());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@ -102,7 +103,7 @@ namespace GameServiceWarden.Core.Tests.Modules.Games
|
|||||||
//When
|
//When
|
||||||
serviceInfo.Start();
|
serviceInfo.Start();
|
||||||
//Then
|
//Then
|
||||||
Assert.True(serviceInfo.GetServiceState());
|
Assert.Equal(ServiceState.Running, serviceInfo.GetServiceState());
|
||||||
serviceInfo.Stop();
|
serviceInfo.Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,28 +144,24 @@ namespace GameServiceWarden.Core.Tests.Modules.Games
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ServiceLogPipeName_ServiceNotStarted_PipeNameReturned()
|
public void GetLogBuffer_CommandWritten_CommandLogged()
|
||||||
{
|
{
|
||||||
//Given
|
//Given
|
||||||
const string SERVICE_NAME = "ServiceLogPipeName_ServiceNotStarted_PipeNameReturned";
|
const string CMD = "hello";
|
||||||
|
const string SERVICE_NAME = "GetLogBuffer_CommandWritten_CommandLogged";
|
||||||
IService stubService = new FakeService();
|
IService stubService = new FakeService();
|
||||||
ServiceDescriptor serviceInfo = new ServiceDescriptor(stubService, SERVICE_NAME, "FakeModule", "FakeAssembly");
|
ServiceDescriptor serviceInfo = new ServiceDescriptor(stubService, SERVICE_NAME, "FakeModule", "FakeAssembly");
|
||||||
//Then
|
|
||||||
Assert.NotNull(serviceInfo.ServiceLogPipeName);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void ServiceLogPipeName_ServiceStarted_StreamReturned()
|
|
||||||
{
|
|
||||||
//Given
|
|
||||||
const string SERVICE_NAME = "ServiceLogPipeName_ServiceStarted_StreamReturned";
|
|
||||||
IService stubService = new FakeService();
|
|
||||||
ServiceDescriptor serviceInfo = new ServiceDescriptor(stubService, SERVICE_NAME, "FakeModule", "FakeAssembly");
|
|
||||||
//When
|
|
||||||
serviceInfo.Start();
|
serviceInfo.Start();
|
||||||
|
//When
|
||||||
|
serviceInfo.ExecuteCommand(CMD);
|
||||||
//Then
|
//Then
|
||||||
Assert.NotNull(serviceInfo.ServiceLogPipeName);
|
using (MemoryStream mem = new MemoryStream(serviceInfo.GetLogBuffer()))
|
||||||
serviceInfo.Stop();
|
{
|
||||||
|
using (StreamReader reader = new StreamReader(mem))
|
||||||
|
{
|
||||||
|
Assert.Equal(CMD, reader.ReadLine());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,11 +4,12 @@ using System.IO;
|
|||||||
using System.IO.Pipes;
|
using System.IO.Pipes;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using GameServiceWarden.Core.Games;
|
using GameServiceWarden.Core.Module;
|
||||||
using GameServiceWarden.Core.Logging;
|
using GameServiceWarden.Core.Logging;
|
||||||
using GameServiceWarden.API.Module;
|
using GameServiceWarden.API.Module;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using Xunit.Abstractions;
|
using Xunit.Abstractions;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
[assembly: CollectionBehavior(DisableTestParallelization = true)]
|
[assembly: CollectionBehavior(DisableTestParallelization = true)]
|
||||||
namespace GameServiceWarden.Core.Tests.Modules.Games
|
namespace GameServiceWarden.Core.Tests.Modules.Games
|
||||||
@ -28,7 +29,7 @@ namespace GameServiceWarden.Core.Tests.Modules.Games
|
|||||||
const string ASSEMBLY_NAME = "FakeAssembly";
|
const string ASSEMBLY_NAME = "FakeAssembly";
|
||||||
const string FAKE_SERVICE_NAME = "CreateService_NewManager_NewServiceCreated";
|
const string FAKE_SERVICE_NAME = "CreateService_NewManager_NewServiceCreated";
|
||||||
FakePersistence<IReadOnlyDictionary<string, IServiceModule>> stubPersistentModuleDictionary = new FakePersistence<IReadOnlyDictionary<string, IServiceModule>>();
|
FakePersistence<IReadOnlyDictionary<string, IServiceModule>> stubPersistentModuleDictionary = new FakePersistence<IReadOnlyDictionary<string, IServiceModule>>();
|
||||||
FakePersistence<ServiceDescriptor> stubPersistentServiceDictionary = new FakePersistence<ServiceDescriptor>();
|
FakePersistence<IReadOnlyDictionary<string, string>> stubPersistentServiceDictionary = new FakePersistence<IReadOnlyDictionary<string, string>>();
|
||||||
FakeServiceManagerMonitor stubMonitor = new FakeServiceManagerMonitor();
|
FakeServiceManagerMonitor stubMonitor = new FakeServiceManagerMonitor();
|
||||||
ServiceManager serviceManager = new ServiceManager(stubMonitor, stubPersistentServiceDictionary, stubPersistentModuleDictionary);
|
ServiceManager serviceManager = new ServiceManager(stubMonitor, stubPersistentServiceDictionary, stubPersistentModuleDictionary);
|
||||||
Dictionary<string, IServiceModule> stubAssemblyModulesDictionary = new Dictionary<string, IServiceModule>();
|
Dictionary<string, IServiceModule> stubAssemblyModulesDictionary = new Dictionary<string, IServiceModule>();
|
||||||
@ -48,7 +49,7 @@ namespace GameServiceWarden.Core.Tests.Modules.Games
|
|||||||
const string ASSEMBLY_NAME = "FakeAssembly";
|
const string ASSEMBLY_NAME = "FakeAssembly";
|
||||||
const string FAKE_SERVICE_NAME = "CreateService_OneService_ServiceDeleted";
|
const string FAKE_SERVICE_NAME = "CreateService_OneService_ServiceDeleted";
|
||||||
FakePersistence<IReadOnlyDictionary<string, IServiceModule>> stubPersistentModuleDictionary = new FakePersistence<IReadOnlyDictionary<string, IServiceModule>>();
|
FakePersistence<IReadOnlyDictionary<string, IServiceModule>> stubPersistentModuleDictionary = new FakePersistence<IReadOnlyDictionary<string, IServiceModule>>();
|
||||||
FakePersistence<ServiceDescriptor> stubPersistentServiceDictionary = new FakePersistence<ServiceDescriptor>();
|
FakePersistence<IReadOnlyDictionary<string, string>> stubPersistentServiceDictionary = new FakePersistence<IReadOnlyDictionary<string, string>>();
|
||||||
FakeServiceManagerMonitor stubMonitor = new FakeServiceManagerMonitor();
|
FakeServiceManagerMonitor stubMonitor = new FakeServiceManagerMonitor();
|
||||||
ServiceManager serviceManager = new ServiceManager(stubMonitor, stubPersistentServiceDictionary, stubPersistentModuleDictionary);
|
ServiceManager serviceManager = new ServiceManager(stubMonitor, stubPersistentServiceDictionary, stubPersistentModuleDictionary);
|
||||||
Dictionary<string, IServiceModule> stubAssemblyModulesDictionary = new Dictionary<string, IServiceModule>();
|
Dictionary<string, IServiceModule> stubAssemblyModulesDictionary = new Dictionary<string, IServiceModule>();
|
||||||
@ -71,7 +72,7 @@ namespace GameServiceWarden.Core.Tests.Modules.Games
|
|||||||
const string ASSEMBLY_NAME = "FakeAssembly";
|
const string ASSEMBLY_NAME = "FakeAssembly";
|
||||||
const string FAKE_SERVICE_PREFIX = "GetServiceNames_MultipleServices_AllCorrectNames_";
|
const string FAKE_SERVICE_PREFIX = "GetServiceNames_MultipleServices_AllCorrectNames_";
|
||||||
FakePersistence<IReadOnlyDictionary<string, IServiceModule>> stubPersistentModuleDictionary = new FakePersistence<IReadOnlyDictionary<string, IServiceModule>>();
|
FakePersistence<IReadOnlyDictionary<string, IServiceModule>> stubPersistentModuleDictionary = new FakePersistence<IReadOnlyDictionary<string, IServiceModule>>();
|
||||||
FakePersistence<ServiceDescriptor> stubPersistentServiceDictionary = new FakePersistence<ServiceDescriptor>();
|
FakePersistence<IReadOnlyDictionary<string, string>> stubPersistentServiceDictionary = new FakePersistence<IReadOnlyDictionary<string, string>>();
|
||||||
FakeServiceManagerMonitor stubMonitor = new FakeServiceManagerMonitor();
|
FakeServiceManagerMonitor stubMonitor = new FakeServiceManagerMonitor();
|
||||||
ServiceManager serviceManager = new ServiceManager(stubMonitor, stubPersistentServiceDictionary, stubPersistentModuleDictionary);
|
ServiceManager serviceManager = new ServiceManager(stubMonitor, stubPersistentServiceDictionary, stubPersistentModuleDictionary);
|
||||||
Dictionary<string, IServiceModule> stubAssemblyModulesDictionary = new Dictionary<string, IServiceModule>();
|
Dictionary<string, IServiceModule> stubAssemblyModulesDictionary = new Dictionary<string, IServiceModule>();
|
||||||
@ -97,7 +98,7 @@ namespace GameServiceWarden.Core.Tests.Modules.Games
|
|||||||
const string ASSEMBLY_NAME = "FakeAssembly";
|
const string ASSEMBLY_NAME = "FakeAssembly";
|
||||||
const string FAKE_SERVICE_NAME = "GetServiceOptions_ThreeOptionService_CorrectOptions";
|
const string FAKE_SERVICE_NAME = "GetServiceOptions_ThreeOptionService_CorrectOptions";
|
||||||
FakePersistence<IReadOnlyDictionary<string, IServiceModule>> stubPersistentModuleDictionary = new FakePersistence<IReadOnlyDictionary<string, IServiceModule>>();
|
FakePersistence<IReadOnlyDictionary<string, IServiceModule>> stubPersistentModuleDictionary = new FakePersistence<IReadOnlyDictionary<string, IServiceModule>>();
|
||||||
FakePersistence<ServiceDescriptor> stubPersistentServiceDictionary = new FakePersistence<ServiceDescriptor>();
|
FakePersistence<IReadOnlyDictionary<string, string>> stubPersistentServiceDictionary = new FakePersistence<IReadOnlyDictionary<string, string>>();
|
||||||
FakeServiceManagerMonitor stubMonitor = new FakeServiceManagerMonitor();
|
FakeServiceManagerMonitor stubMonitor = new FakeServiceManagerMonitor();
|
||||||
ServiceManager serviceManager = new ServiceManager(stubMonitor, stubPersistentServiceDictionary, stubPersistentModuleDictionary);
|
ServiceManager serviceManager = new ServiceManager(stubMonitor, stubPersistentServiceDictionary, stubPersistentModuleDictionary);
|
||||||
Dictionary<string, IServiceModule> stubAssemblyModulesDictionary = new Dictionary<string, IServiceModule>();
|
Dictionary<string, IServiceModule> stubAssemblyModulesDictionary = new Dictionary<string, IServiceModule>();
|
||||||
@ -122,7 +123,7 @@ namespace GameServiceWarden.Core.Tests.Modules.Games
|
|||||||
const string ASSEMBLY_NAME = "FakeAssembly";
|
const string ASSEMBLY_NAME = "FakeAssembly";
|
||||||
const string FAKE_SERVICE_NAME = "SetandGetServiceOptionValue_OneOption_OptionChanged";
|
const string FAKE_SERVICE_NAME = "SetandGetServiceOptionValue_OneOption_OptionChanged";
|
||||||
FakePersistence<IReadOnlyDictionary<string, IServiceModule>> stubPersistentModuleDictionary = new FakePersistence<IReadOnlyDictionary<string, IServiceModule>>();
|
FakePersistence<IReadOnlyDictionary<string, IServiceModule>> stubPersistentModuleDictionary = new FakePersistence<IReadOnlyDictionary<string, IServiceModule>>();
|
||||||
FakePersistence<ServiceDescriptor> stubPersistentServiceDictionary = new FakePersistence<ServiceDescriptor>();
|
FakePersistence<IReadOnlyDictionary<string, string>> stubPersistentServiceDictionary = new FakePersistence<IReadOnlyDictionary<string, string>>();
|
||||||
FakeServiceManagerMonitor stubMonitor = new FakeServiceManagerMonitor();
|
FakeServiceManagerMonitor stubMonitor = new FakeServiceManagerMonitor();
|
||||||
ServiceManager serviceManager = new ServiceManager(stubMonitor, stubPersistentServiceDictionary, stubPersistentModuleDictionary);
|
ServiceManager serviceManager = new ServiceManager(stubMonitor, stubPersistentServiceDictionary, stubPersistentModuleDictionary);
|
||||||
Dictionary<string, IServiceModule> stubAssemblyModulesDictionary = new Dictionary<string, IServiceModule>();
|
Dictionary<string, IServiceModule> stubAssemblyModulesDictionary = new Dictionary<string, IServiceModule>();
|
||||||
@ -145,7 +146,7 @@ namespace GameServiceWarden.Core.Tests.Modules.Games
|
|||||||
const string ASSEMBLY_NAME = "FakeAssembly";
|
const string ASSEMBLY_NAME = "FakeAssembly";
|
||||||
const string FAKE_SERVICE_NAME = "GetServiceState_NotRunning_ReturnsNotRunningState";
|
const string FAKE_SERVICE_NAME = "GetServiceState_NotRunning_ReturnsNotRunningState";
|
||||||
FakePersistence<IReadOnlyDictionary<string, IServiceModule>> stubPersistentModuleDictionary = new FakePersistence<IReadOnlyDictionary<string, IServiceModule>>();
|
FakePersistence<IReadOnlyDictionary<string, IServiceModule>> stubPersistentModuleDictionary = new FakePersistence<IReadOnlyDictionary<string, IServiceModule>>();
|
||||||
FakePersistence<ServiceDescriptor> stubPersistentServiceDictionary = new FakePersistence<ServiceDescriptor>();
|
FakePersistence<IReadOnlyDictionary<string, string>> stubPersistentServiceDictionary = new FakePersistence<IReadOnlyDictionary<string, string>>();
|
||||||
FakeServiceManagerMonitor stubMonitor = new FakeServiceManagerMonitor();
|
FakeServiceManagerMonitor stubMonitor = new FakeServiceManagerMonitor();
|
||||||
ServiceManager serviceManager = new ServiceManager(stubMonitor, stubPersistentServiceDictionary, stubPersistentModuleDictionary);
|
ServiceManager serviceManager = new ServiceManager(stubMonitor, stubPersistentServiceDictionary, stubPersistentModuleDictionary);
|
||||||
Dictionary<string, IServiceModule> stubAssemblyModulesDictionary = new Dictionary<string, IServiceModule>();
|
Dictionary<string, IServiceModule> stubAssemblyModulesDictionary = new Dictionary<string, IServiceModule>();
|
||||||
@ -165,7 +166,7 @@ namespace GameServiceWarden.Core.Tests.Modules.Games
|
|||||||
const string ASSEMBLY_NAME = "FakeAssembly";
|
const string ASSEMBLY_NAME = "FakeAssembly";
|
||||||
const string FAKE_SERVICE_NAME = "StartService_NotStarted_SuccessfulStart";
|
const string FAKE_SERVICE_NAME = "StartService_NotStarted_SuccessfulStart";
|
||||||
FakePersistence<IReadOnlyDictionary<string, IServiceModule>> stubPersistentModuleDictionary = new FakePersistence<IReadOnlyDictionary<string, IServiceModule>>();
|
FakePersistence<IReadOnlyDictionary<string, IServiceModule>> stubPersistentModuleDictionary = new FakePersistence<IReadOnlyDictionary<string, IServiceModule>>();
|
||||||
FakePersistence<ServiceDescriptor> stubPersistentServiceDictionary = new FakePersistence<ServiceDescriptor>();
|
FakePersistence<IReadOnlyDictionary<string, string>> stubPersistentServiceDictionary = new FakePersistence<IReadOnlyDictionary<string, string>>();
|
||||||
FakeServiceManagerMonitor stubMonitor = new FakeServiceManagerMonitor();
|
FakeServiceManagerMonitor stubMonitor = new FakeServiceManagerMonitor();
|
||||||
ServiceManager serviceManager = new ServiceManager(stubMonitor, stubPersistentServiceDictionary, stubPersistentModuleDictionary);
|
ServiceManager serviceManager = new ServiceManager(stubMonitor, stubPersistentServiceDictionary, stubPersistentModuleDictionary);
|
||||||
Dictionary<string, IServiceModule> stubAssemblyModulesDictionary = new Dictionary<string, IServiceModule>();
|
Dictionary<string, IServiceModule> stubAssemblyModulesDictionary = new Dictionary<string, IServiceModule>();
|
||||||
@ -187,7 +188,7 @@ namespace GameServiceWarden.Core.Tests.Modules.Games
|
|||||||
const string ASSEMBLY_NAME = "FakeAssembly";
|
const string ASSEMBLY_NAME = "FakeAssembly";
|
||||||
const string FAKE_SERVICE_NAME = "StopService_ServiceStartedThenStopped_StateUpdated";
|
const string FAKE_SERVICE_NAME = "StopService_ServiceStartedThenStopped_StateUpdated";
|
||||||
FakePersistence<IReadOnlyDictionary<string, IServiceModule>> stubPersistentModuleDictionary = new FakePersistence<IReadOnlyDictionary<string, IServiceModule>>();
|
FakePersistence<IReadOnlyDictionary<string, IServiceModule>> stubPersistentModuleDictionary = new FakePersistence<IReadOnlyDictionary<string, IServiceModule>>();
|
||||||
FakePersistence<ServiceDescriptor> stubPersistentServiceDictionary = new FakePersistence<ServiceDescriptor>();
|
FakePersistence<IReadOnlyDictionary<string, string>> stubPersistentServiceDictionary = new FakePersistence<IReadOnlyDictionary<string, string>>();
|
||||||
FakeServiceManagerMonitor stubMonitor = new FakeServiceManagerMonitor();
|
FakeServiceManagerMonitor stubMonitor = new FakeServiceManagerMonitor();
|
||||||
ServiceManager serviceManager = new ServiceManager(stubMonitor, stubPersistentServiceDictionary, stubPersistentModuleDictionary);
|
ServiceManager serviceManager = new ServiceManager(stubMonitor, stubPersistentServiceDictionary, stubPersistentModuleDictionary);
|
||||||
Dictionary<string, IServiceModule> stubAssemblyModulesDictionary = new Dictionary<string, IServiceModule>();
|
Dictionary<string, IServiceModule> stubAssemblyModulesDictionary = new Dictionary<string, IServiceModule>();
|
||||||
@ -203,14 +204,14 @@ namespace GameServiceWarden.Core.Tests.Modules.Games
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ExecuteCommand_CommandExecutedBeforeConnected_CommandLogged()
|
public void ExecuteCommand_ServiceStarted_CommandLogged()
|
||||||
{
|
{
|
||||||
//Given
|
//Given
|
||||||
const string ASSEMBLY_NAME = "FakeAssembly";
|
const string ASSEMBLY_NAME = "FakeAssembly";
|
||||||
const string FAKE_SERVICE_NAME = "ExecuteCommand_CommandExecutedBeforeConnected_CommandLogged";
|
const string FAKE_SERVICE_NAME = "ExecuteCommand_ServiceStarted_CommandLogged";
|
||||||
const string COMMAND = "TEST";
|
const string COMMAND = "TEST";
|
||||||
FakePersistence<IReadOnlyDictionary<string, IServiceModule>> stubPersistentModuleDictionary = new FakePersistence<IReadOnlyDictionary<string, IServiceModule>>();
|
FakePersistence<IReadOnlyDictionary<string, IServiceModule>> stubPersistentModuleDictionary = new FakePersistence<IReadOnlyDictionary<string, IServiceModule>>();
|
||||||
FakePersistence<ServiceDescriptor> stubPersistentServiceDictionary = new FakePersistence<ServiceDescriptor>();
|
FakePersistence<IReadOnlyDictionary<string, string>> stubPersistentServiceDictionary = new FakePersistence<IReadOnlyDictionary<string, string>>();
|
||||||
FakeServiceManagerMonitor stubMonitor = new FakeServiceManagerMonitor();
|
FakeServiceManagerMonitor stubMonitor = new FakeServiceManagerMonitor();
|
||||||
ServiceManager serviceManager = new ServiceManager(stubMonitor, stubPersistentServiceDictionary, stubPersistentModuleDictionary);
|
ServiceManager serviceManager = new ServiceManager(stubMonitor, stubPersistentServiceDictionary, stubPersistentModuleDictionary);
|
||||||
Dictionary<string, IServiceModule> stubAssemblyModulesDictionary = new Dictionary<string, IServiceModule>();
|
Dictionary<string, IServiceModule> stubAssemblyModulesDictionary = new Dictionary<string, IServiceModule>();
|
||||||
@ -220,116 +221,16 @@ namespace GameServiceWarden.Core.Tests.Modules.Games
|
|||||||
//When
|
//When
|
||||||
serviceManager.CreateService(FAKE_SERVICE_NAME, ASSEMBLY_NAME, stubServiceModule.Name);
|
serviceManager.CreateService(FAKE_SERVICE_NAME, ASSEMBLY_NAME, stubServiceModule.Name);
|
||||||
serviceManager.StartService(FAKE_SERVICE_NAME);
|
serviceManager.StartService(FAKE_SERVICE_NAME);
|
||||||
string pipeName = serviceManager.GetLogPipeNames()[FAKE_SERVICE_NAME];
|
|
||||||
NamedPipeClientStream clientStream = new NamedPipeClientStream(".", pipeName, PipeDirection.In);
|
|
||||||
serviceManager.ExecuteCommand(FAKE_SERVICE_NAME, COMMAND);
|
|
||||||
clientStream.Connect(1000);
|
|
||||||
Thread.Sleep(1000);
|
|
||||||
//Then
|
|
||||||
byte[] buffer = new byte[1024 * 8];
|
|
||||||
CancellationTokenSource cancelToken = new CancellationTokenSource(2000);
|
|
||||||
ValueTask<int> task = clientStream.ReadAsync(buffer, cancelToken.Token);
|
|
||||||
Assert.False(task.AsTask().Wait(1000));
|
|
||||||
serviceManager.StopService(FAKE_SERVICE_NAME);
|
|
||||||
cancelToken.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void ExecuteCommand_CommandExecutedAfterConnected_CommandLogged()
|
|
||||||
{
|
|
||||||
//Given
|
|
||||||
const string ASSEMBLY_NAME = "FakeAssembly";
|
|
||||||
const string FAKE_SERVICE_NAME = "ExecuteCommand_CommandExecutedAfterConnected_CommandLogged";
|
|
||||||
const string COMMAND = "TEST";
|
|
||||||
FakePersistence<IReadOnlyDictionary<string, IServiceModule>> stubPersistentModuleDictionary = new FakePersistence<IReadOnlyDictionary<string, IServiceModule>>();
|
|
||||||
FakePersistence<ServiceDescriptor> stubPersistentServiceDictionary = new FakePersistence<ServiceDescriptor>();
|
|
||||||
FakeServiceManagerMonitor stubMonitor = new FakeServiceManagerMonitor();
|
|
||||||
ServiceManager serviceManager = new ServiceManager(stubMonitor, stubPersistentServiceDictionary, stubPersistentModuleDictionary);
|
|
||||||
Dictionary<string, IServiceModule> stubAssemblyModulesDictionary = new Dictionary<string, IServiceModule>();
|
|
||||||
IServiceModule stubServiceModule = new FakeServiceModule();
|
|
||||||
stubAssemblyModulesDictionary.Add(stubServiceModule.Name, stubServiceModule);
|
|
||||||
stubPersistentModuleDictionary.AddToPersistence(ASSEMBLY_NAME, stubAssemblyModulesDictionary);
|
|
||||||
//When
|
|
||||||
serviceManager.CreateService(FAKE_SERVICE_NAME, ASSEMBLY_NAME, stubServiceModule.Name);
|
|
||||||
serviceManager.StartService(FAKE_SERVICE_NAME);
|
|
||||||
string pipeName = serviceManager.GetLogPipeNames()[FAKE_SERVICE_NAME];
|
|
||||||
NamedPipeClientStream clientStream = new NamedPipeClientStream(".", pipeName, PipeDirection.In);
|
|
||||||
clientStream.Connect(1000);
|
|
||||||
Thread.Sleep(1000);
|
|
||||||
serviceManager.ExecuteCommand(FAKE_SERVICE_NAME, COMMAND);
|
serviceManager.ExecuteCommand(FAKE_SERVICE_NAME, COMMAND);
|
||||||
//Then
|
//Then
|
||||||
using (StreamReader reader = new StreamReader(clientStream))
|
using (MemoryStream mem = new MemoryStream(serviceManager.GetLogBuffer()[FAKE_SERVICE_NAME]))
|
||||||
{
|
{
|
||||||
CancellationTokenSource cancelToken = new CancellationTokenSource(2000);
|
using (StreamReader reader = new StreamReader(mem))
|
||||||
string message = null;
|
|
||||||
Task task = Task.Run(() => message = reader.ReadLine(), cancelToken.Token);
|
|
||||||
Assert.True(task.Wait(1000));
|
|
||||||
Assert.True(COMMAND.Equals(message), $"Received message \"{message}\" when expecting \"{COMMAND}\"");
|
|
||||||
cancelToken.Dispose();
|
|
||||||
}
|
|
||||||
serviceManager.StopService(FAKE_SERVICE_NAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void ExecuteCommand_CommandExecutedAfterMultipleLogListenersConnected_CommandLogged()
|
|
||||||
{
|
|
||||||
//Given
|
|
||||||
const string ASSEMBLY_NAME = "FakeAssembly";
|
|
||||||
const string FAKE_SERVICE_NAME = "ExecuteCommand_CommandExecutedAfterMultipleLogListenersConnected_CommandLogged";
|
|
||||||
const string COMMAND = "TEST";
|
|
||||||
FakePersistence<IReadOnlyDictionary<string, IServiceModule>> stubPersistentModuleDictionary = new FakePersistence<IReadOnlyDictionary<string, IServiceModule>>();
|
|
||||||
FakePersistence<ServiceDescriptor> stubPersistentServiceDictionary = new FakePersistence<ServiceDescriptor>();
|
|
||||||
FakeServiceManagerMonitor stubMonitor = new FakeServiceManagerMonitor();
|
|
||||||
ServiceManager serviceManager = new ServiceManager(stubMonitor, stubPersistentServiceDictionary, stubPersistentModuleDictionary);
|
|
||||||
Dictionary<string, IServiceModule> stubAssemblyModulesDictionary = new Dictionary<string, IServiceModule>();
|
|
||||||
IServiceModule stubServiceModule = new FakeServiceModule();
|
|
||||||
stubAssemblyModulesDictionary.Add(stubServiceModule.Name, stubServiceModule);
|
|
||||||
stubPersistentModuleDictionary.AddToPersistence(ASSEMBLY_NAME, stubAssemblyModulesDictionary);
|
|
||||||
//When
|
|
||||||
serviceManager.CreateService(FAKE_SERVICE_NAME, ASSEMBLY_NAME, stubServiceModule.Name);
|
|
||||||
serviceManager.StartService(FAKE_SERVICE_NAME);
|
|
||||||
string pipeName = serviceManager.GetLogPipeNames()[FAKE_SERVICE_NAME];
|
|
||||||
NamedPipeClientStream[] clientStreams = new NamedPipeClientStream[5];
|
|
||||||
for (int i = 0; i < clientStreams.Length; i++)
|
|
||||||
{
|
|
||||||
clientStreams[i] = new NamedPipeClientStream(".", pipeName, PipeDirection.In);
|
|
||||||
clientStreams[i].Connect(1000);
|
|
||||||
}
|
|
||||||
Thread.Sleep(1000);
|
|
||||||
serviceManager.ExecuteCommand(FAKE_SERVICE_NAME, COMMAND);
|
|
||||||
//Then
|
|
||||||
for (int i = 0; i < clientStreams.Length; i++)
|
|
||||||
{
|
|
||||||
using (StreamReader reader = new StreamReader(clientStreams[i]))
|
|
||||||
{
|
{
|
||||||
string message = null;
|
Assert.Equal(COMMAND, reader.ReadLine());
|
||||||
Task clientTask = Task.Run(() => message = reader.ReadLine());
|
|
||||||
Assert.True(clientTask.Wait(1000));
|
|
||||||
Assert.True(COMMAND.Equals(message), $"Received message \"{message}\" when expecting \"{COMMAND}\"");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Task task = Task.Run(() => serviceManager.StopService(FAKE_SERVICE_NAME));
|
serviceManager.StopService(FAKE_SERVICE_NAME);
|
||||||
Assert.True(task.Wait(5000)); //TODO FIX WHY THIS IS HAPPENING!!!!!
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetServiceConsoleStream_ServiceStopped_ExceptionThrown()
|
|
||||||
{
|
|
||||||
//Given
|
|
||||||
const string ASSEMBLY_NAME = "FakeAssembly";
|
|
||||||
const string FAKE_SERVICE_NAME = "GetServiceConsoleStream_ServiceStopped_ExceptionThrown";
|
|
||||||
FakePersistence<IReadOnlyDictionary<string, IServiceModule>> stubPersistentModuleDictionary = new FakePersistence<IReadOnlyDictionary<string, IServiceModule>>();
|
|
||||||
FakePersistence<ServiceDescriptor> stubPersistentServiceDictionary = new FakePersistence<ServiceDescriptor>();
|
|
||||||
FakeServiceManagerMonitor stubMonitor = new FakeServiceManagerMonitor();
|
|
||||||
ServiceManager serviceManager = new ServiceManager(stubMonitor, stubPersistentServiceDictionary, stubPersistentModuleDictionary);
|
|
||||||
Dictionary<string, IServiceModule> stubAssemblyModulesDictionary = new Dictionary<string, IServiceModule>();
|
|
||||||
IServiceModule stubServiceModule = new FakeServiceModule();
|
|
||||||
stubAssemblyModulesDictionary.Add(stubServiceModule.Name, stubServiceModule);
|
|
||||||
stubPersistentModuleDictionary.AddToPersistence(ASSEMBLY_NAME, stubAssemblyModulesDictionary);
|
|
||||||
//When
|
|
||||||
serviceManager.CreateService(FAKE_SERVICE_NAME, ASSEMBLY_NAME, stubServiceModule.Name);
|
|
||||||
//Then
|
|
||||||
Assert.Throws<KeyNotFoundException>(() => serviceManager.GetLogPipeNames()[FAKE_SERVICE_NAME]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,7 +1,7 @@
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using GameServiceWarden.Core.Games;
|
using GameServiceWarden.Core.Module;
|
||||||
using GameServiceWarden.Core.Persistence;
|
using GameServiceWarden.Core.Persistence;
|
||||||
using GameServiceWarden.Core.Tests.Modules;
|
using GameServiceWarden.Core.Tests.Modules;
|
||||||
using GameServiceWarden.Core.Tests.Modules.Games;
|
using GameServiceWarden.Core.Tests.Modules.Games;
|
||||||
@ -28,7 +28,7 @@ namespace GameServiceWarden.Core.Tests.Persistence
|
|||||||
stubAssemblyDict[MODULE_NAME] = stubServiceModule;
|
stubAssemblyDict[MODULE_NAME] = stubServiceModule;
|
||||||
stubModulesPersistence[ASSEMBLY_NAME] = stubAssemblyDict;
|
stubModulesPersistence[ASSEMBLY_NAME] = stubAssemblyDict;
|
||||||
|
|
||||||
ServiceDescriptorPersistence persistedServices = new ServiceDescriptorPersistence(TEST_DIR, stubModulesPersistence);
|
ServiceDescriptorPersistence persistedServices = new ServiceDescriptorPersistence(TEST_DIR);
|
||||||
|
|
||||||
//Then
|
//Then
|
||||||
Assert.True(persistedServices.GetPathForKey(SERVICE_NAME).Equals(Path.Combine(TEST_DIR, SERVICE_NAME)));
|
Assert.True(persistedServices.GetPathForKey(SERVICE_NAME).Equals(Path.Combine(TEST_DIR, SERVICE_NAME)));
|
||||||
@ -49,11 +49,14 @@ namespace GameServiceWarden.Core.Tests.Persistence
|
|||||||
stubAssemblyDict[MODULE_NAME] = stubServiceModule;
|
stubAssemblyDict[MODULE_NAME] = stubServiceModule;
|
||||||
stubModulesPersistence[ASSEMBLY_NAME] = stubAssemblyDict;
|
stubModulesPersistence[ASSEMBLY_NAME] = stubAssemblyDict;
|
||||||
|
|
||||||
ServiceDescriptorPersistence persistedServiceInfos = new ServiceDescriptorPersistence(TEST_DIR, stubModulesPersistence);
|
ServiceDescriptorPersistence persistedServiceInfos = new ServiceDescriptorPersistence(TEST_DIR);
|
||||||
|
|
||||||
ServiceDescriptor stubServiceInfo = new ServiceDescriptor(stubModulesPersistence[ASSEMBLY_NAME][MODULE_NAME].InstantiateService(persistedServiceInfos.GetPathForKey(SERVICE_NAME), true), SERVICE_NAME, MODULE_NAME, ASSEMBLY_NAME);
|
ServiceDescriptor stubServiceInfo = new ServiceDescriptor(stubModulesPersistence[ASSEMBLY_NAME][MODULE_NAME].InstantiateService(persistedServiceInfos.GetPathForKey(SERVICE_NAME)), SERVICE_NAME, MODULE_NAME, ASSEMBLY_NAME);
|
||||||
//When
|
//When
|
||||||
persistedServiceInfos[SERVICE_NAME] = stubServiceInfo;
|
Dictionary<string, string> info = new Dictionary<string, string>();
|
||||||
|
info[ServiceDescriptor.MODULE_PROPERTY] = MODULE_NAME;
|
||||||
|
info[ServiceDescriptor.ASSEMBLY_PROPERTY] = ASSEMBLY_NAME;
|
||||||
|
persistedServiceInfos[SERVICE_NAME] = info;
|
||||||
//Then
|
//Then
|
||||||
Assert.True(Directory.Exists(TEST_DIR));
|
Assert.True(Directory.Exists(TEST_DIR));
|
||||||
Assert.True(Directory.Exists(persistedServiceInfos.GetPathForKey(SERVICE_NAME)));
|
Assert.True(Directory.Exists(persistedServiceInfos.GetPathForKey(SERVICE_NAME)));
|
||||||
@ -79,15 +82,19 @@ namespace GameServiceWarden.Core.Tests.Persistence
|
|||||||
stubAssemblyDict[MODULE_NAME] = stubServiceModule;
|
stubAssemblyDict[MODULE_NAME] = stubServiceModule;
|
||||||
stubModulesPersistence[ASSEMBLY_NAME] = stubAssemblyDict;
|
stubModulesPersistence[ASSEMBLY_NAME] = stubAssemblyDict;
|
||||||
|
|
||||||
ServiceDescriptorPersistence persistedServices = new ServiceDescriptorPersistence(TEST_DIR, stubModulesPersistence);
|
ServiceDescriptorPersistence persistedServices = new ServiceDescriptorPersistence(TEST_DIR);
|
||||||
|
|
||||||
ServiceDescriptor stubServiceInfo = new ServiceDescriptor(stubModulesPersistence[ASSEMBLY_NAME][MODULE_NAME].InstantiateService(persistedServices.GetPathForKey(SERVICE_NAME), true), SERVICE_NAME, MODULE_NAME, ASSEMBLY_NAME);
|
Dictionary<string, string> info = new Dictionary<string, string>();
|
||||||
persistedServices[SERVICE_NAME] = stubServiceInfo;
|
info[ServiceDescriptor.MODULE_PROPERTY] = MODULE_NAME;
|
||||||
|
info[ServiceDescriptor.ASSEMBLY_PROPERTY] = ASSEMBLY_NAME;
|
||||||
|
persistedServices[SERVICE_NAME] = info;
|
||||||
//When
|
//When
|
||||||
ServiceDescriptor loadedService = persistedServices[SERVICE_NAME];
|
IReadOnlyDictionary<string, string> loadedServiceInfo = persistedServices[SERVICE_NAME];
|
||||||
|
string loadedAssemblyName = loadedServiceInfo[ServiceDescriptor.ASSEMBLY_PROPERTY];
|
||||||
|
string loadedModuleName = loadedServiceInfo[ServiceDescriptor.MODULE_PROPERTY];
|
||||||
//Then
|
//Then
|
||||||
Assert.True(loadedService.GetModuleName().Equals(MODULE_NAME));
|
Assert.True(loadedModuleName.Equals(MODULE_NAME));
|
||||||
Assert.True(loadedService.GetAssemblyName().Equals(ASSEMBLY_NAME));
|
Assert.True(loadedAssemblyName.Equals(ASSEMBLY_NAME));
|
||||||
|
|
||||||
Directory.Delete(TEST_DIR, true);
|
Directory.Delete(TEST_DIR, true);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user