1
0

PoolTestLogHandler.cs 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. using System.IO;
  2. using Robust.Shared.Log;
  3. using Robust.Shared.Timing;
  4. using Serilog.Events;
  5. namespace Content.IntegrationTests;
  6. #nullable enable
  7. /// <summary>
  8. /// Log handler intended for pooled integration tests.
  9. /// </summary>
  10. /// <remarks>
  11. /// <para>
  12. /// This class logs to two places: an NUnit <see cref="TestContext"/>
  13. /// (so it nicely gets attributed to a test in your IDE),
  14. /// and an in-memory ring buffer for diagnostic purposes.
  15. /// If test pooling breaks, the ring buffer can be used to see what the broken instance has gone through.
  16. /// </para>
  17. /// <para>
  18. /// The active test context can be swapped out so pooled instances can correctly have their logs attributed.
  19. /// </para>
  20. /// </remarks>
  21. public sealed class PoolTestLogHandler : ILogHandler
  22. {
  23. private readonly string? _prefix;
  24. private RStopwatch _stopwatch;
  25. public TextWriter? ActiveContext { get; private set; }
  26. public LogLevel? FailureLevel { get; set; }
  27. public PoolTestLogHandler(string? prefix)
  28. {
  29. _prefix = prefix != null ? $"{prefix}: " : "";
  30. }
  31. public bool ShuttingDown;
  32. public void Log(string sawmillName, LogEvent message)
  33. {
  34. var level = message.Level.ToRobust();
  35. if (ShuttingDown && (FailureLevel == null || level < FailureLevel))
  36. return;
  37. if (ActiveContext is not { } testContext)
  38. {
  39. // If this gets hit it means something is logging to this instance while it's "between" tests.
  40. // This is a bug in either the game or the testing system, and must always be investigated.
  41. throw new InvalidOperationException("Log to pool test log handler without active test context");
  42. }
  43. var name = LogMessage.LogLevelToName(level);
  44. var seconds = _stopwatch.Elapsed.TotalSeconds;
  45. var rendered = message.RenderMessage();
  46. var line = $"{_prefix}{seconds:F3}s [{name}] {sawmillName}: {rendered}";
  47. testContext.WriteLine(line);
  48. if (FailureLevel == null || level < FailureLevel)
  49. return;
  50. testContext.Flush();
  51. Assert.Fail($"{line} Exception: {message.Exception}");
  52. }
  53. public void ClearContext()
  54. {
  55. ActiveContext = null;
  56. }
  57. public void ActivateContext(TextWriter context)
  58. {
  59. _stopwatch.Restart();
  60. ActiveContext = context;
  61. }
  62. }