mirror of
https://github.com/tgstation/tgstation-server.git
synced 2026-08-22 20:47:28 +01:00
Time to go home
This commit is contained in:
@@ -80,6 +80,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Configuration.Install" />
|
||||
<Reference Include="System.ServiceProcess" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@@ -17,4 +17,8 @@
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.1.1" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
namespace Tgstation.Server.Host.Watchdog
|
||||
{
|
||||
/// <summary>
|
||||
/// For deleting <see cref="System.Reflection.Assembly"/>s used by the program
|
||||
/// </summary>
|
||||
interface IActiveAssemblyDeleter
|
||||
{
|
||||
/// <summary>
|
||||
/// Deletes an <see cref="System.Reflection.Assembly"/> that is in use by the runtime
|
||||
/// </summary>
|
||||
/// <param name="assemblyPath">The <see cref="System.Reflection.Assembly.Location"/> of the <see cref="System.Reflection.Assembly"/> to delete</param>
|
||||
void DeleteActiveAssembly(string assemblyPath);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace Tgstation.Server.Host.Watchdog
|
||||
{
|
||||
/// <summary>
|
||||
/// For deleting <see cref="Host"/> libraries used by the program
|
||||
/// </summary>
|
||||
interface IActiveLibraryDeleter
|
||||
{
|
||||
/// <summary>
|
||||
/// Deletes a <see cref="Host"/> library that is in use by the runtime
|
||||
/// </summary>
|
||||
/// <param name="assemblyPath">The path of the library to delete</param>
|
||||
void DeleteActiveLibrary(string assemblyPath);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
@@ -30,14 +32,26 @@ namespace Tgstation.Server.Host.Watchdog
|
||||
/// <returns>A new <see cref="IServer"/></returns>
|
||||
public IServer CreateServer(string[] args, string updatePath)
|
||||
{
|
||||
var assembly = LoadFromAssemblyPath(assemblyPath);
|
||||
//find the IServerFactory implementation
|
||||
//help here: https://stackoverflow.com/questions/40908568/assembly-loading-in-net-core
|
||||
|
||||
var serverFactoryInterfaceType = typeof(IServerFactory);
|
||||
var serverFactoryImplementationType = assembly.GetTypes().Where(x => serverFactoryInterfaceType.IsAssignableFrom(x)).First();
|
||||
var oldCd = Environment.CurrentDirectory;
|
||||
Directory.SetCurrentDirectory(Path.GetDirectoryName(assemblyPath));
|
||||
try
|
||||
{
|
||||
var assembly = LoadFromAssemblyPath(assemblyPath);
|
||||
|
||||
var serverFactory = (IServerFactory)Activator.CreateInstance(serverFactoryImplementationType);
|
||||
return serverFactory.CreateServer(args, updatePath);
|
||||
//find the IServerFactory implementation
|
||||
var serverFactoryInterfaceType = typeof(IServerFactory);
|
||||
var serverFactoryImplementationType = assembly.GetTypes().Where(x => serverFactoryInterfaceType.IsAssignableFrom(x)).First();
|
||||
|
||||
var serverFactory = (IServerFactory)Activator.CreateInstance(serverFactoryImplementationType);
|
||||
|
||||
return serverFactory.CreateServer(args, updatePath);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Directory.SetCurrentDirectory(oldCd);
|
||||
}
|
||||
}
|
||||
|
||||
//honestly have no idea what this is for, but the examples i see just return null and it seems to work just fine
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Tgstation.Server.Host.Watchdog
|
||||
{
|
||||
/// <summary>
|
||||
/// See <see cref="IActiveAssemblyDeleter"/> for POSIX systems
|
||||
/// </summary>
|
||||
sealed class PosixActiveAssemblyDeleter : IActiveAssemblyDeleter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public void DeleteActiveAssembly(string assemblyPath) => File.Delete(assemblyPath ?? throw new ArgumentNullException(nameof(assemblyPath))); //glory of inodes
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Tgstation.Server.Host.Watchdog
|
||||
{
|
||||
/// <summary>
|
||||
/// See <see cref="IActiveLibraryDeleter"/> for POSIX systems
|
||||
/// </summary>
|
||||
sealed class PosixActiveLibraryDeleter : IActiveLibraryDeleter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public void DeleteActiveLibrary(string assemblyPath) => Directory.Delete(assemblyPath ?? throw new ArgumentNullException(nameof(assemblyPath)), true); //glory of inodes
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,6 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Tgstation.Server.Host.Startup\Tgstation.Server.Host.Startup.csproj" />
|
||||
<ProjectReference Include="..\Tgstation.Server.Host\Tgstation.Server.Host.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Tgstation.Server.Host.Startup;
|
||||
@@ -10,15 +11,11 @@ namespace Tgstation.Server.Host.Watchdog
|
||||
/// <inheritdoc />
|
||||
sealed class Watchdog : IWatchdog
|
||||
{
|
||||
/// <summary>
|
||||
/// The initial <see cref="IServerFactory"/> for the <see cref="Watchdog"/>
|
||||
/// </summary>
|
||||
readonly IServerFactory initialServerFactory;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IActiveAssemblyDeleter"/> for the <see cref="Watchdog"/>
|
||||
/// The <see cref="IActiveLibraryDeleter"/> for the <see cref="Watchdog"/>
|
||||
/// </summary>
|
||||
readonly IActiveAssemblyDeleter activeAssemblyDeleter;
|
||||
readonly IActiveLibraryDeleter activeLibraryDeleter;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IIsolatedAssemblyContextFactory"/> for the <see cref="Watchdog"/>
|
||||
@@ -33,14 +30,12 @@ namespace Tgstation.Server.Host.Watchdog
|
||||
/// <summary>
|
||||
/// Construct a <see cref="Watchdog"/>
|
||||
/// </summary>
|
||||
/// <param name="initialServerFactory">The value of <see cref="initialServerFactory"/></param>
|
||||
/// <param name="activeAssemblyDeleter">The value of <see cref="activeAssemblyDeleter"/></param>
|
||||
/// <param name="activeLibraryDeleter">The value of <see cref="activeLibraryDeleter"/></param>
|
||||
/// <param name="isolatedAssemblyLoader">The value of <see cref="isolatedAssemblyLoader"/></param>
|
||||
/// <param name="logger">The value of <see cref="logger"/></param>
|
||||
public Watchdog(IServerFactory initialServerFactory, IActiveAssemblyDeleter activeAssemblyDeleter, IIsolatedAssemblyContextFactory isolatedAssemblyLoader, ILogger<Watchdog> logger)
|
||||
public Watchdog(IActiveLibraryDeleter activeLibraryDeleter, IIsolatedAssemblyContextFactory isolatedAssemblyLoader, ILogger<Watchdog> logger)
|
||||
{
|
||||
this.initialServerFactory = initialServerFactory ?? throw new ArgumentNullException(nameof(initialServerFactory));
|
||||
this.activeAssemblyDeleter = activeAssemblyDeleter ?? throw new ArgumentNullException(nameof(activeAssemblyDeleter));
|
||||
this.activeLibraryDeleter = activeLibraryDeleter ?? throw new ArgumentNullException(nameof(activeLibraryDeleter));
|
||||
this.isolatedAssemblyLoader = isolatedAssemblyLoader ?? throw new ArgumentNullException(nameof(isolatedAssemblyLoader));
|
||||
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
@@ -48,48 +43,43 @@ namespace Tgstation.Server.Host.Watchdog
|
||||
/// <inheritdoc />
|
||||
public async Task RunAsync(string[] args, CancellationToken cancellationToken)
|
||||
{
|
||||
const string DefaultAssemblyPath = "Default";
|
||||
|
||||
var assemblyStoragePath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "lib");
|
||||
var assemblyName = String.Join(".", nameof(Tgstation), nameof(Server), nameof(Host), "dll");
|
||||
|
||||
logger.LogInformation("Host watchdog starting...");
|
||||
|
||||
var nextAssemblyPath = Path.GetFullPath(Path.Combine(assemblyStoragePath, DefaultAssemblyPath));
|
||||
string lastAssemblyPath = null;
|
||||
try
|
||||
{
|
||||
//first run the host we started with
|
||||
logger.LogTrace("Running with initial server factory...");
|
||||
var serverFactory = initialServerFactory;
|
||||
logger.LogTrace("Determining location of host assembly...");
|
||||
var assemblyPath = serverFactory.GetType().Assembly.Location;
|
||||
logger.LogDebug("Path to initial host assembly: {0}", assemblyPath);
|
||||
var assemblyName = Path.GetFileName(assemblyPath);
|
||||
const string UpdatePath = "Updates";
|
||||
var newAssemblyDirectory = Path.Combine(Path.GetDirectoryName(assemblyPath), UpdatePath);
|
||||
|
||||
var firstIteration = true;
|
||||
do
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
using (logger.BeginScope("Host invocation"))
|
||||
{
|
||||
logger.LogTrace("Atttempting to create new server factory...");
|
||||
Guid updateGuid;
|
||||
using (var server = serverFactory.CreateServer(args, newAssemblyDirectory))
|
||||
{
|
||||
logger.LogTrace("Running server...");
|
||||
await server.RunAsync(cancellationToken).ConfigureAwait(false);
|
||||
logger.LogInformation("Active host exited.");
|
||||
{ //forces serverFactory out of the picture once the scope ends
|
||||
var serverFactory = isolatedAssemblyLoader.CreateIsolatedServerFactory(Path.Combine(nextAssemblyPath, assemblyName));
|
||||
using (var server = serverFactory.CreateServer(args, assemblyStoragePath))
|
||||
{
|
||||
logger.LogTrace("Running server...");
|
||||
await server.RunAsync(cancellationToken).ConfigureAwait(false);
|
||||
logger.LogInformation("Active host exited.");
|
||||
|
||||
if (!server.UpdateGuid.HasValue)
|
||||
break;
|
||||
updateGuid = server.UpdateGuid.Value;
|
||||
if (!server.UpdateGuid.HasValue)
|
||||
break;
|
||||
updateGuid = server.UpdateGuid.Value;
|
||||
}
|
||||
}
|
||||
|
||||
logger.LogInformation("Update path is set to \"{0}\", attempting host assembly hotswap...", updateGuid);
|
||||
GC.Collect(Int32.MaxValue, GCCollectionMode.Forced, true, true);
|
||||
if (!firstIteration)
|
||||
{
|
||||
logger.LogTrace("Deleting old host assembly");
|
||||
//TODO: make this use directories
|
||||
//activeAssemblyDeleter.DeleteActiveAssembly(newAssemblyDirectory);
|
||||
}
|
||||
logger.LogTrace("Atttempting to create new server factory...");
|
||||
serverFactory = isolatedAssemblyLoader.CreateIsolatedServerFactory(Path.Combine(newAssemblyDirectory, updateGuid.ToString(), assemblyName));
|
||||
firstIteration = false;
|
||||
|
||||
activeLibraryDeleter.DeleteActiveLibrary(nextAssemblyPath);
|
||||
|
||||
nextAssemblyPath = Path.Combine(assemblyStoragePath, updateGuid.ToString());
|
||||
}
|
||||
while (!cancellationToken.IsCancellationRequested);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
@@ -98,6 +88,11 @@ namespace Tgstation.Server.Host.Watchdog
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.LogCritical("Error running host assembly! Exception: {0}", e);
|
||||
nextAssemblyPath = lastAssemblyPath ?? DefaultAssemblyPath; //don't wanna save a critfailed assembly
|
||||
}
|
||||
if (nextAssemblyPath != DefaultAssemblyPath)
|
||||
{
|
||||
logger.LogInformation("Setting next default host assembly path to {0}...", nextAssemblyPath);
|
||||
}
|
||||
logger.LogInformation("Host watchdog exiting...");
|
||||
}
|
||||
|
||||
@@ -10,6 +10,6 @@ namespace Tgstation.Server.Host.Watchdog
|
||||
{
|
||||
/// <inheritdoc />
|
||||
[ExcludeFromCodeCoverage]
|
||||
public IWatchdog CreateWatchdog(ILoggerFactory loggerFactory) => new Watchdog(new ServerFactory(), RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? (IActiveAssemblyDeleter)new WindowsActiveAssemblyDeleter() : new PosixActiveAssemblyDeleter(), new IsolatedAssemblyContextFactory(), loggerFactory?.CreateLogger<Watchdog>() ?? throw new ArgumentNullException(nameof(loggerFactory)));
|
||||
public IWatchdog CreateWatchdog(ILoggerFactory loggerFactory) => new Watchdog(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? (IActiveLibraryDeleter)new WindowsActiveLibraryDeleter() : new PosixActiveLibraryDeleter(), new IsolatedAssemblyContextFactory(), loggerFactory?.CreateLogger<Watchdog>() ?? throw new ArgumentNullException(nameof(loggerFactory)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Tgstation.Server.Host.Watchdog
|
||||
{
|
||||
/// <summary>
|
||||
/// See <see cref="IActiveAssemblyDeleter"/> for Windows systems
|
||||
/// </summary>
|
||||
sealed class WindowsActiveAssemblyDeleter : IActiveAssemblyDeleter
|
||||
{
|
||||
/// <summary>
|
||||
/// Set a file located at <paramref name="path"/> to be deleted on reboot
|
||||
/// </summary>
|
||||
/// <param name="path">The file to delete on reboot</param>
|
||||
[ExcludeFromCodeCoverage]
|
||||
static void DeleteFileOnReboot(string path)
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && !NativeMethods.MoveFileEx(path, null, NativeMethods.MoveFileFlags.DelayUntilReboot))
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void DeleteActiveAssembly(string assemblyPath)
|
||||
{
|
||||
if (assemblyPath == null)
|
||||
throw new ArgumentNullException(nameof(assemblyPath));
|
||||
|
||||
//Can't use Path.GetTempFileName() because it may cross drives, which won't actually rename the file
|
||||
var tmpLocation = String.Concat(assemblyPath, Guid.NewGuid());
|
||||
File.Move(assemblyPath, tmpLocation);
|
||||
DeleteFileOnReboot(tmpLocation);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Tgstation.Server.Host.Watchdog
|
||||
{
|
||||
/// <summary>
|
||||
/// See <see cref="IActiveLibraryDeleter"/> for Windows systems
|
||||
/// </summary>
|
||||
sealed class WindowsActiveLibraryDeleter : IActiveLibraryDeleter
|
||||
{
|
||||
/// <summary>
|
||||
/// Set a directory located at <paramref name="path"/> to be deleted on reboot
|
||||
/// </summary>
|
||||
/// <param name="path">The file to delete on reboot</param>
|
||||
[ExcludeFromCodeCoverage]
|
||||
static void DeleteDirectoryOnReboot(string path)
|
||||
{
|
||||
if (!NativeMethods.MoveFileEx(path, null, NativeMethods.MoveFileFlags.DelayUntilReboot))
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void DeleteActiveLibrary(string assemblyPath)
|
||||
{
|
||||
if (assemblyPath == null)
|
||||
throw new ArgumentNullException(nameof(assemblyPath));
|
||||
|
||||
var tmpLocation = Path.Combine(Path.GetDirectoryName(assemblyPath), Guid.NewGuid().ToString());
|
||||
Directory.Move(assemblyPath, tmpLocation);
|
||||
DeleteDirectoryOnReboot(tmpLocation);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,6 +56,7 @@ namespace Tgstation.Server.Host
|
||||
[ExcludeFromCodeCoverage]
|
||||
public async Task RunAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
Console.WriteLine("Hello world!");
|
||||
using (cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken))
|
||||
using (var webHost = webHostBuilder
|
||||
.UseStartup<Application>()
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace Tgstation.Server.Host.Watchdog.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests for <see cref="IsolatedAssemblyContextFactory"/>
|
||||
/// </summary>
|
||||
[TestClass]
|
||||
public sealed class TestIsolatedAssemblyContextFactory
|
||||
{
|
||||
[TestMethod]
|
||||
public void TestServerFactoryCreation()
|
||||
{
|
||||
var contextFactory = new IsolatedAssemblyContextFactory();
|
||||
Assert.IsNotNull(contextFactory.CreateIsolatedServerFactory(typeof(ServerFactory).Assembly.Location));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using System;
|
||||
|
||||
namespace Tgstation.Server.Host.Watchdog.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests for <see cref="IsolatedServerFactory"/>
|
||||
/// </summary>
|
||||
[TestClass]
|
||||
public sealed class TestIsolatedServerFactory
|
||||
{
|
||||
[TestMethod]
|
||||
public void TestConstruction()
|
||||
{
|
||||
Assert.ThrowsException<ArgumentNullException>(() => new IsolatedServerFactory(null));
|
||||
var isf = new IsolatedServerFactory(typeof(ServerFactory).Assembly.Location);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestLoading()
|
||||
{
|
||||
var isf = new IsolatedServerFactory(typeof(ServerFactory).Assembly.Location);
|
||||
Assert.IsNotNull(isf.CreateServer(Array.Empty<string>(), String.Empty));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Tgstation.Server.Host.Watchdog.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests for <see cref="PosixActiveAssemblyDeleter"/>
|
||||
/// </summary>
|
||||
[TestClass]
|
||||
public sealed class TestPosixActiveAssemblyDeleter
|
||||
{
|
||||
[TestMethod]
|
||||
public void TestAssemblyDeletion()
|
||||
{
|
||||
var ourAssembly = GetType().Assembly;
|
||||
var fakeAssemblyPath = String.Concat(ourAssembly.Location, Guid.NewGuid());
|
||||
File.Copy(ourAssembly.Location, fakeAssemblyPath);
|
||||
|
||||
try
|
||||
{
|
||||
var deleter = new PosixActiveAssemblyDeleter();
|
||||
deleter.DeleteActiveAssembly(fakeAssemblyPath);
|
||||
Assert.IsFalse(File.Exists(fakeAssemblyPath));
|
||||
}
|
||||
catch
|
||||
{
|
||||
File.Delete(fakeAssemblyPath);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestNullInvoke()
|
||||
{
|
||||
var deleter = new PosixActiveAssemblyDeleter();
|
||||
Assert.ThrowsException<ArgumentNullException>(() => deleter.DeleteActiveAssembly(null));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,15 +14,13 @@ namespace Tgstation.Server.Host.Watchdog.Tests
|
||||
[TestMethod]
|
||||
public void TestConstruction()
|
||||
{
|
||||
Assert.ThrowsException<ArgumentNullException>(() => new Watchdog(null, null, null, null));
|
||||
var mockServerFactory = new Mock<IServerFactory>();
|
||||
Assert.ThrowsException<ArgumentNullException>(() => new Watchdog(mockServerFactory.Object, null, null, null));
|
||||
var mockActiveAssemblyDeleter = new Mock<IActiveAssemblyDeleter>();
|
||||
Assert.ThrowsException<ArgumentNullException>(() => new Watchdog(mockServerFactory.Object, mockActiveAssemblyDeleter.Object, null, null));
|
||||
Assert.ThrowsException<ArgumentNullException>(() => new Watchdog(null, null, null));
|
||||
var mockActiveAssemblyDeleter = new Mock<IActiveLibraryDeleter>();
|
||||
Assert.ThrowsException<ArgumentNullException>(() => new Watchdog(mockActiveAssemblyDeleter.Object, null, null));
|
||||
var mockIsolatedServerContextFactory = new Mock<IIsolatedAssemblyContextFactory>();
|
||||
Assert.ThrowsException<ArgumentNullException>(() => new Watchdog(mockServerFactory.Object, mockActiveAssemblyDeleter.Object, mockIsolatedServerContextFactory.Object, null));
|
||||
Assert.ThrowsException<ArgumentNullException>(() => new Watchdog(mockActiveAssemblyDeleter.Object, mockIsolatedServerContextFactory.Object, null));
|
||||
var mockLogger = new LoggerFactory().CreateLogger<Watchdog>();
|
||||
var wd = new Watchdog(mockServerFactory.Object, mockActiveAssemblyDeleter.Object, mockIsolatedServerContextFactory.Object, mockLogger);
|
||||
var wd = new Watchdog(mockActiveAssemblyDeleter.Object, mockIsolatedServerContextFactory.Object, mockLogger);
|
||||
}
|
||||
|
||||
class MockServerFactory : IServerFactory
|
||||
@@ -37,11 +35,11 @@ namespace Tgstation.Server.Host.Watchdog.Tests
|
||||
{
|
||||
var mockServer = new Mock<IServer>();
|
||||
var mockServerFactory = new MockServerFactory(mockServer.Object);
|
||||
var mockActiveAssemblyDeleter = new Mock<IActiveAssemblyDeleter>();
|
||||
var mockActiveAssemblyDeleter = new Mock<IActiveLibraryDeleter>();
|
||||
var mockIsolatedServerContextFactory = new Mock<IIsolatedAssemblyContextFactory>();
|
||||
var mockLogger = new LoggerFactory().CreateLogger<Watchdog>();
|
||||
|
||||
var wd = new Watchdog(mockServerFactory, mockActiveAssemblyDeleter.Object, mockIsolatedServerContextFactory.Object, mockLogger);
|
||||
var wd = new Watchdog(mockActiveAssemblyDeleter.Object, mockIsolatedServerContextFactory.Object, mockLogger);
|
||||
|
||||
using (var cts = new CancellationTokenSource())
|
||||
{
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
|
||||
namespace Tgstation.Server.Host.Watchdog.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests for <see cref="WindowsActiveAssemblyDeleter"/>
|
||||
/// </summary>
|
||||
[TestClass]
|
||||
public sealed class TestWindowsActiveAssemblyDeleter
|
||||
{
|
||||
[TestMethod]
|
||||
public void TestAssemblyDeletion()
|
||||
{
|
||||
var ourAssembly = GetType().Assembly;
|
||||
var fakeAssemblyPath = String.Concat(ourAssembly.Location, Guid.NewGuid());
|
||||
File.Copy(ourAssembly.Location, fakeAssemblyPath);
|
||||
|
||||
try
|
||||
{
|
||||
var deleter = new WindowsActiveAssemblyDeleter();
|
||||
try
|
||||
{
|
||||
deleter.DeleteActiveAssembly(fakeAssemblyPath);
|
||||
}
|
||||
catch (Win32Exception e)
|
||||
{
|
||||
Assert.AreEqual(e.NativeErrorCode, 5);
|
||||
}
|
||||
Assert.IsFalse(File.Exists(fakeAssemblyPath));
|
||||
}
|
||||
catch
|
||||
{
|
||||
File.Delete(fakeAssemblyPath);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestNullInvoke()
|
||||
{
|
||||
var deleter = new WindowsActiveAssemblyDeleter();
|
||||
Assert.ThrowsException<ArgumentNullException>(() => deleter.DeleteActiveAssembly(null));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -52,6 +52,9 @@ EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tgstation.Server.Host.Tests", "tests\Tgstation.Server.Host.Tests\Tgstation.Server.Host.Tests.csproj", "{A3362FF6-550F-480F-859E-8EC1EB6EAB31}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tgstation.Server.Host.Watchdog", "src\Tgstation.Server.Host.Watchdog\Tgstation.Server.Host.Watchdog.csproj", "{5D2D682C-6BF0-439C-850B-6AB945BBEAEA}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{2B69AD6D-2B5A-4023-8EAD-0BD1B18E028A} = {2B69AD6D-2B5A-4023-8EAD-0BD1B18E028A}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tgstation.Server.Host.Watchdog.Tests", "tests\Tgstation.Server.Host.Watchdog.Tests\Tgstation.Server.Host.Watchdog.Tests.csproj", "{7500F776-4384-4B5F-A8D8-22461CAD108B}"
|
||||
EndProject
|
||||
|
||||
Reference in New Issue
Block a user