using System;
using System.Collections.Generic;
using System.IO;
using NUnit.Framework;
using Robust.Shared.Log;
using Robust.Shared.Timing;
using Serilog.Events;
namespace Robust.UnitTesting.Pool;
///
/// Log handler intended for pooled integration tests.
///
///
///
/// This class logs to one place: an NUnit (so it nicely gets attributed to a test in your IDE)
///
///
/// The active test context can be swapped out so pooled instances can correctly have their logs attributed.
///
///
public sealed class PoolTestLogHandler : ILogHandler
{
private readonly string? _prefix;
private RStopwatch _stopwatch;
public TextWriter? ActiveContext { get; private set; }
public LogLevel? FailureLevel { get; set; }
public IReadOnlyList FailingLogs => _failingLogs;
private readonly List _failingLogs = new();
public PoolTestLogHandler(string? prefix)
{
_prefix = prefix != null ? $"{prefix}: " : "";
}
public bool ShuttingDown;
///
///
/// Event handler that allows you to override a potential failing log.
/// Use this if you want to allow certain error logs to be considered passing.
///
///
/// Has the sawmill name and passed in, and should return a boolean
/// when the log message should not be logged as a failure.
///
///
public event Func? JudgeLog;
public void Log(string sawmillName, LogEvent message)
{
var level = message.Level.ToRobust();
if (ShuttingDown && (FailureLevel == null || level < FailureLevel))
return;
if (ActiveContext is not { } testContext)
{
// If this gets hit it means something is logging to this instance while it's "between" tests.
// This is a bug in either the game or the testing system, and must always be investigated.
throw new InvalidOperationException("Log to pool test log handler without active test context");
}
var name = LogMessage.LogLevelToName(level);
var seconds = _stopwatch.Elapsed.TotalSeconds;
var rendered = message.RenderMessage();
var line = $"{_prefix}{seconds:F3}s [{name}] {sawmillName}: {rendered}";
testContext.WriteLine(line);
if (FailureLevel == null || level < FailureLevel || (JudgeLog?.Invoke(sawmillName, message) ?? false))
return;
testContext.Flush();
_failingLogs.Add($"{line} Exception: {message.Exception}");
}
public void ClearContext()
{
ActiveContext = null;
_failingLogs.Clear();
}
public void ActivateContext(TextWriter context)
{
_stopwatch.Restart();
ActiveContext = context;
}
}