A game about forced loneliness, made by TACStudios
1using System; 2using System.Collections.Generic; 3using System.Linq; 4using UnityEngine.TestTools.TestRunner; 5 6namespace UnityEngine.TestTools.Logging 7{ 8 internal sealed class LogScope : ILogScope 9 { 10 private static List<LogScope> s_ActiveScopes = new List<LogScope>(); 11 12 private readonly object m_Lock = new object(); 13 private bool m_Disposed; 14 private bool m_NeedToProcessLogs; 15 16 public Queue<LogMatch> ExpectedLogs { get; set; } 17 public List<LogEvent> AllLogs { get; } 18 public List<LogEvent> FailingLogs { get; } 19 public bool IgnoreFailingMessages { get; set; } 20 public bool IsNUnitException { get; private set; } 21 public bool IsNUnitSuccessException { get; private set; } 22 public bool IsNUnitInconclusiveException { get; private set; } 23 public bool IsNUnitIgnoreException { get; private set; } 24 public string NUnitExceptionMessage { get; private set; } 25 26 public static LogScope Current 27 { 28 get 29 { 30 if (s_ActiveScopes.Count == 0) 31 throw new InvalidOperationException("No log scope is available"); 32 return s_ActiveScopes[0]; 33 } 34 } 35 36 public static bool HasCurrentLogScope() 37 { 38 return s_ActiveScopes.Count > 0; 39 } 40 41 public LogScope() 42 { 43 AllLogs = new List<LogEvent>(); 44 FailingLogs = new List<LogEvent>(); 45 ExpectedLogs = new Queue<LogMatch>(); 46 IgnoreFailingMessages = false; 47 Activate(); 48 } 49 50 private void Activate() 51 { 52 s_ActiveScopes.Insert(0, this); 53 RegisterScope(this); 54 Application.logMessageReceivedThreaded -= AddLog; 55 Application.logMessageReceivedThreaded += AddLog; 56 } 57 58 private void Deactivate() 59 { 60 Application.logMessageReceivedThreaded -= AddLog; 61 s_ActiveScopes.Remove(this); 62 UnregisterScope(this); 63 } 64 65 private static void RegisterScope(LogScope logScope) 66 { 67 Application.logMessageReceivedThreaded += logScope.AddLog; 68 } 69 70 private static void UnregisterScope(LogScope logScope) 71 { 72 Application.logMessageReceivedThreaded -= logScope.AddLog; 73 } 74 75 public void AddLog(string message, string stacktrace, LogType type) 76 { 77 lock (m_Lock) 78 { 79 m_NeedToProcessLogs = true; 80 var log = new LogEvent 81 { 82 LogType = type, 83 Message = message, 84 StackTrace = stacktrace, 85 }; 86 87 AllLogs.Add(log); 88 89 if (IsNUnitResultStateException(stacktrace, type)) 90 { 91 if (message.StartsWith("SuccessException")) 92 { 93 IsNUnitException = true; 94 IsNUnitSuccessException = true; 95 if (message.StartsWith("SuccessException: ")) 96 { 97 NUnitExceptionMessage = message.Substring("SuccessException: ".Length); 98 return; 99 } 100 } 101 else if (message.StartsWith("InconclusiveException")) 102 { 103 IsNUnitException = true; 104 IsNUnitInconclusiveException = true; 105 if (message.StartsWith("InconclusiveException: ")) 106 { 107 NUnitExceptionMessage = message.Substring("InconclusiveException: ".Length); 108 return; 109 } 110 } 111 else if (message.StartsWith("IgnoreException")) 112 { 113 IsNUnitException = true; 114 IsNUnitIgnoreException = true; 115 if (message.StartsWith("IgnoreException: ")) 116 { 117 NUnitExceptionMessage = message.Substring("IgnoreException: ".Length); 118 return; 119 } 120 } 121 } 122 123 if (IsFailingLog(type) && !IgnoreFailingMessages) 124 { 125 FailingLogs.Add(log); 126 } 127 } 128 } 129 130 private static bool IsNUnitResultStateException(string stacktrace, LogType logType) 131 { 132 if (logType != LogType.Exception) 133 return false; 134 135 return string.IsNullOrEmpty(stacktrace) || stacktrace.StartsWith("NUnit.Framework.Assert."); 136 } 137 138 private static bool IsFailingLog(LogType type) 139 { 140 switch (type) 141 { 142 case LogType.Assert: 143 case LogType.Error: 144 case LogType.Exception: 145 return true; 146 default: 147 return false; 148 } 149 } 150 151 public void Dispose() 152 { 153 Dispose(true); 154 GC.SuppressFinalize(this); 155 } 156 157 private void Dispose(bool disposing) 158 { 159 if (m_Disposed) 160 { 161 return; 162 } 163 164 m_Disposed = true; 165 166 if (disposing) 167 { 168 Deactivate(); 169 } 170 } 171 172 public bool AnyFailingLogs() 173 { 174 ProcessExpectedLogs(); 175 return FailingLogs.Any(); 176 } 177 178 public void EvaluateLogScope(bool endOfScopeCheck) 179 { 180 ProcessExpectedLogs(); 181 if (FailingLogs.Any()) 182 { 183 var failureInWrongOrder = FailingLogs.FirstOrDefault(log => ExpectedLogs.Any(expected => expected.Matches(log))); 184 if (failureInWrongOrder != null) 185 { 186 var nextExpected = ExpectedLogs.Peek(); 187 throw new OutOfOrderExpectedLogMessageException(failureInWrongOrder, nextExpected); 188 } 189 190 var failingLog = FailingLogs.First(); 191 throw new UnhandledLogMessageException(failingLog); 192 } 193 if (endOfScopeCheck && ExpectedLogs.Any()) 194 { 195 throw new UnexpectedLogMessageException(ExpectedLogs.Peek()); 196 } 197 } 198 199 public void ProcessExpectedLogs() 200 { 201 lock (m_Lock) 202 { 203 if (!m_NeedToProcessLogs || !ExpectedLogs.Any()) 204 return; 205 206 LogMatch expectedLog = null; 207 foreach (var logEvent in AllLogs) 208 { 209 if (!ExpectedLogs.Any()) 210 break; 211 if (logEvent.IsHandled) 212 { 213 continue; 214 } 215 if (expectedLog == null && ExpectedLogs.Any()) 216 expectedLog = ExpectedLogs.Peek(); 217 218 if (expectedLog != null && expectedLog.Matches(logEvent)) 219 { 220 ExpectedLogs.Dequeue(); 221 logEvent.IsHandled = true; 222 if (FailingLogs.Any(expectedLog.Matches)) 223 { 224 var failingLog = FailingLogs.First(expectedLog.Matches); 225 FailingLogs.Remove(failingLog); 226 } 227 expectedLog = null; 228 } 229 } 230 m_NeedToProcessLogs = false; 231 } 232 } 233 234 public void NoUnexpectedReceived() 235 { 236 lock (m_Lock) 237 { 238 ProcessExpectedLogs(); 239 240 var unhandledLog = AllLogs.FirstOrDefault(x => !x.IsHandled); 241 if (unhandledLog != null) 242 { 243 throw new UnhandledLogMessageException(unhandledLog); 244 } 245 } 246 } 247 } 248}