diff --git a/src/Tgstation.Server.Host/Extensions/ServiceCollectionExtensions.cs b/src/Tgstation.Server.Host/Extensions/ServiceCollectionExtensions.cs
index 4fcbc5d32e..4980d732d8 100644
--- a/src/Tgstation.Server.Host/Extensions/ServiceCollectionExtensions.cs
+++ b/src/Tgstation.Server.Host/Extensions/ServiceCollectionExtensions.cs
@@ -5,6 +5,7 @@ using System.Globalization;
using Elastic.CommonSchema.Serilog;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using Serilog;
@@ -27,6 +28,11 @@ namespace Tgstation.Server.Host.Extensions
///
static Type chatProviderFactoryType = typeof(ProviderFactory);
+ ///
+ /// A for an additional to use.
+ ///
+ static ServiceDescriptor additionalLoggerProvider;
+
///
/// Change the used as an implementation for calls to .
///
@@ -36,6 +42,17 @@ namespace Tgstation.Server.Host.Extensions
chatProviderFactoryType = typeof(TProviderFactory);
}
+ ///
+ /// Add an additional to s that call .
+ ///
+ /// The of to add.
+ public static void UseAdditionalLoggerProvider() where TLoggerProvider : class, ILoggerProvider
+ {
+ if (additionalLoggerProvider != null)
+ throw new InvalidOperationException("Cannot have multiple additionalLoggerProviders!");
+ additionalLoggerProvider = ServiceDescriptor.Singleton();
+ }
+
///
/// Adds a implementation to the given .
///
@@ -133,6 +150,9 @@ namespace Tgstation.Server.Host.Extensions
if (Debugger.IsAttached)
builder.AddDebug();
+
+ if (additionalLoggerProvider != null)
+ builder.Services.TryAddEnumerable(additionalLoggerProvider);
});
}
}
diff --git a/tests/Tgstation.Server.Tests/Live/HardFailLogger.cs b/tests/Tgstation.Server.Tests/Live/HardFailLogger.cs
new file mode 100644
index 0000000000..22b46a5532
--- /dev/null
+++ b/tests/Tgstation.Server.Tests/Live/HardFailLogger.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Threading.Tasks;
+
+using Microsoft.Extensions.Logging;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace Tgstation.Server.Tests.Live
+{
+ sealed class HardFailLogger : ILogger
+ {
+ readonly Action failureSink;
+
+ public HardFailLogger(Action failureSink)
+ {
+ this.failureSink = failureSink ?? throw new ArgumentNullException(nameof(failureSink));
+ }
+
+ public IDisposable BeginScope(TState state) => Task.CompletedTask;
+
+ public bool IsEnabled(LogLevel logLevel) => true;
+
+ public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter)
+ {
+ var logMessage = formatter(state, exception);
+ if (logLevel == LogLevel.Error || (logLevel == LogLevel.Critical && logMessage != "DropDatabase configuration option set! Dropping any existing database..."))
+ failureSink(new AssertFailedException(logMessage));
+ }
+ }
+}
diff --git a/tests/Tgstation.Server.Tests/Live/HardFailLoggerProvider.cs b/tests/Tgstation.Server.Tests/Live/HardFailLoggerProvider.cs
new file mode 100644
index 0000000000..e72ea557db
--- /dev/null
+++ b/tests/Tgstation.Server.Tests/Live/HardFailLoggerProvider.cs
@@ -0,0 +1,23 @@
+using System.Threading.Tasks;
+
+using Microsoft.Extensions.Logging;
+
+namespace Tgstation.Server.Tests.Live
+{
+ sealed class HardFailLoggerProvider : ILoggerProvider
+ {
+ public static bool BlockFails { get; set; }
+
+ public static Task FailureSource => failureSink.Task;
+
+ static readonly TaskCompletionSource failureSink = new TaskCompletionSource();
+
+ public ILogger CreateLogger(string categoryName) => new HardFailLogger(ex =>
+ {
+ if (!BlockFails)
+ failureSink.TrySetException(ex);
+ });
+
+ public void Dispose() { }
+ }
+}
diff --git a/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs b/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs
index e39d36650e..535d4d124c 100644
--- a/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs
+++ b/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs
@@ -698,6 +698,26 @@ namespace Tgstation.Server.Tests.Live
[TestMethod]
public async Task TestStandardTgsOperation()
{
+ const int MaximumTestMinutes = 30;
+ using var hardCancellationTokenSource = new CancellationTokenSource(TimeSpan.FromMinutes(MaximumTestMinutes));
+ var hardCancellationToken = hardCancellationTokenSource.Token;
+
+ ServiceCollectionExtensions.UseAdditionalLoggerProvider();
+
+ var internalTask = TestTgsInternal(hardCancellationToken);
+ await Task.WhenAny(
+ internalTask,
+ HardFailLoggerProvider.FailureSource);
+
+ if (!internalTask.IsCompleted)
+ {
+ hardCancellationTokenSource.Cancel();
+ await Task.WhenAll(internalTask, HardFailLoggerProvider.FailureSource);
+ }
+ }
+
+ async Task TestTgsInternal(CancellationToken hardCancellationToken)
+ {
var discordConnectionString = Environment.GetEnvironmentVariable("TGS_TEST_DISCORD_TOKEN");
var ircConnectionString = Environment.GetEnvironmentVariable("TGS_TEST_IRC_CONNECTION_STRING");
var missingChatVarsCount = Convert.ToInt32(String.IsNullOrWhiteSpace(discordConnectionString))
@@ -734,9 +754,6 @@ namespace Tgstation.Server.Tests.Live
using var server = new LiveTestingServer(null, true);
- const int MaximumTestMinutes = 180;
- using var hardTimeoutCancellationTokenSource = new CancellationTokenSource(TimeSpan.FromMinutes(MaximumTestMinutes));
- var hardCancellationToken = hardTimeoutCancellationTokenSource.Token;
using var serverCts = CancellationTokenSource.CreateLinkedTokenSource(hardCancellationToken);
var cancellationToken = serverCts.Token;
@@ -816,12 +833,13 @@ namespace Tgstation.Server.Tests.Live
// http bind test https://github.com/tgstation/tgstation-server/issues/1065
if (new PlatformIdentifier().IsWindows)
{
- using var blockingSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
- blockingSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ExclusiveAddressUse, true);
- blockingSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, false);
- blockingSocket.Bind(new IPEndPoint(IPAddress.Any, server.Url.Port));
+ HardFailLoggerProvider.BlockFails = true;
try
{
+ using var blockingSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
+ blockingSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ExclusiveAddressUse, true);
+ blockingSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, false);
+ blockingSocket.Bind(new IPEndPoint(IPAddress.Any, server.Url.Port));
// bind test run
await server.Run(cancellationToken);
Assert.Fail("Expected server task to end with a SocketException");
@@ -830,6 +848,10 @@ namespace Tgstation.Server.Tests.Live
{
Assert.AreEqual(ex.SocketErrorCode, SocketError.AddressAlreadyInUse);
}
+ finally
+ {
+ HardFailLoggerProvider.BlockFails = false;
+ }
}
await Task.WhenAny(serverTask, Task.Delay(TimeSpan.FromMinutes(1), cancellationToken));