249 lines
7.9 KiB
C#
249 lines
7.9 KiB
C#
|
using System;
|
||
|
using System.Collections.Generic;
|
||
|
using System.Linq;
|
||
|
using UnityEngine.TestTools.TestRunner;
|
||
|
|
||
|
namespace UnityEngine.TestTools.Logging
|
||
|
{
|
||
|
internal sealed class LogScope : ILogScope
|
||
|
{
|
||
|
private static List<LogScope> s_ActiveScopes = new List<LogScope>();
|
||
|
|
||
|
private readonly object m_Lock = new object();
|
||
|
private bool m_Disposed;
|
||
|
private bool m_NeedToProcessLogs;
|
||
|
|
||
|
public Queue<LogMatch> ExpectedLogs { get; set; }
|
||
|
public List<LogEvent> AllLogs { get; }
|
||
|
public List<LogEvent> FailingLogs { get; }
|
||
|
public bool IgnoreFailingMessages { get; set; }
|
||
|
public bool IsNUnitException { get; private set; }
|
||
|
public bool IsNUnitSuccessException { get; private set; }
|
||
|
public bool IsNUnitInconclusiveException { get; private set; }
|
||
|
public bool IsNUnitIgnoreException { get; private set; }
|
||
|
public string NUnitExceptionMessage { get; private set; }
|
||
|
|
||
|
public static LogScope Current
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
if (s_ActiveScopes.Count == 0)
|
||
|
throw new InvalidOperationException("No log scope is available");
|
||
|
return s_ActiveScopes[0];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static bool HasCurrentLogScope()
|
||
|
{
|
||
|
return s_ActiveScopes.Count > 0;
|
||
|
}
|
||
|
|
||
|
public LogScope()
|
||
|
{
|
||
|
AllLogs = new List<LogEvent>();
|
||
|
FailingLogs = new List<LogEvent>();
|
||
|
ExpectedLogs = new Queue<LogMatch>();
|
||
|
IgnoreFailingMessages = false;
|
||
|
Activate();
|
||
|
}
|
||
|
|
||
|
private void Activate()
|
||
|
{
|
||
|
s_ActiveScopes.Insert(0, this);
|
||
|
RegisterScope(this);
|
||
|
Application.logMessageReceivedThreaded -= AddLog;
|
||
|
Application.logMessageReceivedThreaded += AddLog;
|
||
|
}
|
||
|
|
||
|
private void Deactivate()
|
||
|
{
|
||
|
Application.logMessageReceivedThreaded -= AddLog;
|
||
|
s_ActiveScopes.Remove(this);
|
||
|
UnregisterScope(this);
|
||
|
}
|
||
|
|
||
|
private static void RegisterScope(LogScope logScope)
|
||
|
{
|
||
|
Application.logMessageReceivedThreaded += logScope.AddLog;
|
||
|
}
|
||
|
|
||
|
private static void UnregisterScope(LogScope logScope)
|
||
|
{
|
||
|
Application.logMessageReceivedThreaded -= logScope.AddLog;
|
||
|
}
|
||
|
|
||
|
public void AddLog(string message, string stacktrace, LogType type)
|
||
|
{
|
||
|
lock (m_Lock)
|
||
|
{
|
||
|
m_NeedToProcessLogs = true;
|
||
|
var log = new LogEvent
|
||
|
{
|
||
|
LogType = type,
|
||
|
Message = message,
|
||
|
StackTrace = stacktrace,
|
||
|
};
|
||
|
|
||
|
AllLogs.Add(log);
|
||
|
|
||
|
if (IsNUnitResultStateException(stacktrace, type))
|
||
|
{
|
||
|
if (message.StartsWith("SuccessException"))
|
||
|
{
|
||
|
IsNUnitException = true;
|
||
|
IsNUnitSuccessException = true;
|
||
|
if (message.StartsWith("SuccessException: "))
|
||
|
{
|
||
|
NUnitExceptionMessage = message.Substring("SuccessException: ".Length);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
else if (message.StartsWith("InconclusiveException"))
|
||
|
{
|
||
|
IsNUnitException = true;
|
||
|
IsNUnitInconclusiveException = true;
|
||
|
if (message.StartsWith("InconclusiveException: "))
|
||
|
{
|
||
|
NUnitExceptionMessage = message.Substring("InconclusiveException: ".Length);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
else if (message.StartsWith("IgnoreException"))
|
||
|
{
|
||
|
IsNUnitException = true;
|
||
|
IsNUnitIgnoreException = true;
|
||
|
if (message.StartsWith("IgnoreException: "))
|
||
|
{
|
||
|
NUnitExceptionMessage = message.Substring("IgnoreException: ".Length);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (IsFailingLog(type) && !IgnoreFailingMessages)
|
||
|
{
|
||
|
FailingLogs.Add(log);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static bool IsNUnitResultStateException(string stacktrace, LogType logType)
|
||
|
{
|
||
|
if (logType != LogType.Exception)
|
||
|
return false;
|
||
|
|
||
|
return string.IsNullOrEmpty(stacktrace) || stacktrace.StartsWith("NUnit.Framework.Assert.");
|
||
|
}
|
||
|
|
||
|
private static bool IsFailingLog(LogType type)
|
||
|
{
|
||
|
switch (type)
|
||
|
{
|
||
|
case LogType.Assert:
|
||
|
case LogType.Error:
|
||
|
case LogType.Exception:
|
||
|
return true;
|
||
|
default:
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void Dispose()
|
||
|
{
|
||
|
Dispose(true);
|
||
|
GC.SuppressFinalize(this);
|
||
|
}
|
||
|
|
||
|
private void Dispose(bool disposing)
|
||
|
{
|
||
|
if (m_Disposed)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
m_Disposed = true;
|
||
|
|
||
|
if (disposing)
|
||
|
{
|
||
|
Deactivate();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool AnyFailingLogs()
|
||
|
{
|
||
|
ProcessExpectedLogs();
|
||
|
return FailingLogs.Any();
|
||
|
}
|
||
|
|
||
|
public void EvaluateLogScope(bool endOfScopeCheck)
|
||
|
{
|
||
|
ProcessExpectedLogs();
|
||
|
if (FailingLogs.Any())
|
||
|
{
|
||
|
var failureInWrongOrder = FailingLogs.FirstOrDefault(log => ExpectedLogs.Any(expected => expected.Matches(log)));
|
||
|
if (failureInWrongOrder != null)
|
||
|
{
|
||
|
var nextExpected = ExpectedLogs.Peek();
|
||
|
throw new OutOfOrderExpectedLogMessageException(failureInWrongOrder, nextExpected);
|
||
|
}
|
||
|
|
||
|
var failingLog = FailingLogs.First();
|
||
|
throw new UnhandledLogMessageException(failingLog);
|
||
|
}
|
||
|
if (endOfScopeCheck && ExpectedLogs.Any())
|
||
|
{
|
||
|
throw new UnexpectedLogMessageException(ExpectedLogs.Peek());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void ProcessExpectedLogs()
|
||
|
{
|
||
|
lock (m_Lock)
|
||
|
{
|
||
|
if (!m_NeedToProcessLogs || !ExpectedLogs.Any())
|
||
|
return;
|
||
|
|
||
|
LogMatch expectedLog = null;
|
||
|
foreach (var logEvent in AllLogs)
|
||
|
{
|
||
|
if (!ExpectedLogs.Any())
|
||
|
break;
|
||
|
if (logEvent.IsHandled)
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
if (expectedLog == null && ExpectedLogs.Any())
|
||
|
expectedLog = ExpectedLogs.Peek();
|
||
|
|
||
|
if (expectedLog != null && expectedLog.Matches(logEvent))
|
||
|
{
|
||
|
ExpectedLogs.Dequeue();
|
||
|
logEvent.IsHandled = true;
|
||
|
if (FailingLogs.Any(expectedLog.Matches))
|
||
|
{
|
||
|
var failingLog = FailingLogs.First(expectedLog.Matches);
|
||
|
FailingLogs.Remove(failingLog);
|
||
|
}
|
||
|
expectedLog = null;
|
||
|
}
|
||
|
}
|
||
|
m_NeedToProcessLogs = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void NoUnexpectedReceived()
|
||
|
{
|
||
|
lock (m_Lock)
|
||
|
{
|
||
|
ProcessExpectedLogs();
|
||
|
|
||
|
var unhandledLog = AllLogs.FirstOrDefault(x => !x.IsHandled);
|
||
|
if (unhandledLog != null)
|
||
|
{
|
||
|
throw new UnhandledLogMessageException(unhandledLog);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|