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);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 |