LogScope.cs 6.9 KB

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