PageRenderTime 45ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/src/NUnit/core/EventQueue.cs

#
C# | 350 lines | 245 code | 43 blank | 62 comment | 10 complexity | 2e98606eea2c65572930ad5b4fb3935c MD5 | raw file
Possible License(s): GPL-2.0
  1. // ****************************************************************
  2. // Copyright 2007, Charlie Poole
  3. // This is free software licensed under the NUnit license. You may
  4. // obtain a copy of the license at http://nunit.org.
  5. // ****************************************************************
  6. using System;
  7. using System.Collections;
  8. using System.Globalization;
  9. using System.Runtime.Serialization;
  10. using System.Threading;
  11. namespace NUnit.Core
  12. {
  13. #region Individual Event Classes
  14. /// <summary>
  15. /// NUnit.Core.Event is the abstract base for all stored events.
  16. /// An Event is the stored representation of a call to the
  17. /// EventListener interface and is used to record such calls
  18. /// or to queue them for forwarding on another thread or at
  19. /// a later time.
  20. /// </summary>
  21. public abstract class Event
  22. {
  23. abstract public void Send( EventListener listener );
  24. /// <summary>
  25. /// Gets a value indicating whether this event is delivered synchronously by the NUnit <see cref="EventPump"/>.
  26. /// <para>
  27. /// If <c>true</c>, and if <see cref="EventQueue.SetWaitHandleForSynchronizedEvents"/> has been used to
  28. /// set a WaitHandle, <see cref="EventQueue.Enqueue"/> blocks its calling thread until the <see cref="EventPump"/>
  29. /// thread has delivered the event and sets the WaitHandle.
  30. /// </para>
  31. /// </summary>
  32. public virtual bool IsSynchronous
  33. {
  34. get
  35. {
  36. return false;
  37. }
  38. }
  39. protected static Exception WrapUnserializableException(Exception ex)
  40. {
  41. string message = string.Format(
  42. CultureInfo.InvariantCulture,
  43. "(failed to serialize original Exception - original Exception follows){0}{1}",
  44. Environment.NewLine,
  45. ex);
  46. return new Exception(message);
  47. }
  48. }
  49. public class RunStartedEvent : Event
  50. {
  51. string name;
  52. int testCount;
  53. public RunStartedEvent( string name, int testCount )
  54. {
  55. this.name = name;
  56. this.testCount = testCount;
  57. }
  58. public override bool IsSynchronous
  59. {
  60. get
  61. {
  62. return true;
  63. }
  64. }
  65. public override void Send( EventListener listener )
  66. {
  67. listener.RunStarted(name, testCount);
  68. }
  69. }
  70. public class RunFinishedEvent : Event
  71. {
  72. TestResult result;
  73. Exception exception;
  74. public RunFinishedEvent( TestResult result )
  75. {
  76. this.result = result;
  77. }
  78. public RunFinishedEvent( Exception exception )
  79. {
  80. this.exception = exception;
  81. }
  82. public override void Send( EventListener listener )
  83. {
  84. if ( this.exception != null )
  85. {
  86. try
  87. {
  88. listener.RunFinished( this.exception );
  89. }
  90. catch (SerializationException)
  91. {
  92. Exception wrapped = WrapUnserializableException(this.exception);
  93. listener.RunFinished(wrapped);
  94. }
  95. }
  96. else
  97. listener.RunFinished( this.result );
  98. }
  99. }
  100. public class TestStartedEvent : Event
  101. {
  102. TestName testName;
  103. public TestStartedEvent( TestName testName )
  104. {
  105. this.testName = testName;
  106. }
  107. public override bool IsSynchronous
  108. {
  109. get
  110. {
  111. return true;
  112. }
  113. }
  114. public override void Send( EventListener listener )
  115. {
  116. listener.TestStarted( this.testName );
  117. }
  118. }
  119. public class TestFinishedEvent : Event
  120. {
  121. TestResult result;
  122. public TestFinishedEvent( TestResult result )
  123. {
  124. this.result = result;
  125. }
  126. public override void Send( EventListener listener )
  127. {
  128. listener.TestFinished( this.result );
  129. }
  130. }
  131. public class SuiteStartedEvent : Event
  132. {
  133. TestName suiteName;
  134. public SuiteStartedEvent( TestName suiteName )
  135. {
  136. this.suiteName = suiteName;
  137. }
  138. public override bool IsSynchronous
  139. {
  140. get
  141. {
  142. return true;
  143. }
  144. }
  145. public override void Send( EventListener listener )
  146. {
  147. listener.SuiteStarted( this.suiteName );
  148. }
  149. }
  150. public class SuiteFinishedEvent : Event
  151. {
  152. TestResult result;
  153. public SuiteFinishedEvent( TestResult result )
  154. {
  155. this.result = result;
  156. }
  157. public override void Send( EventListener listener )
  158. {
  159. listener.SuiteFinished( this.result );
  160. }
  161. }
  162. public class UnhandledExceptionEvent : Event
  163. {
  164. Exception exception;
  165. public UnhandledExceptionEvent( Exception exception )
  166. {
  167. this.exception = exception;
  168. }
  169. public override void Send( EventListener listener )
  170. {
  171. try
  172. {
  173. listener.UnhandledException( this.exception );
  174. }
  175. catch (SerializationException)
  176. {
  177. Exception wrapped = WrapUnserializableException(this.exception);
  178. listener.UnhandledException(wrapped);
  179. }
  180. }
  181. }
  182. public class OutputEvent : Event
  183. {
  184. TestOutput output;
  185. public OutputEvent( TestOutput output )
  186. {
  187. this.output = output;
  188. }
  189. public override void Send( EventListener listener )
  190. {
  191. listener.TestOutput( this.output );
  192. }
  193. }
  194. #endregion
  195. /// <summary>
  196. /// Implements a queue of work items each of which
  197. /// is queued as a WaitCallback.
  198. /// </summary>
  199. public class EventQueue
  200. {
  201. private readonly Queue queue = new Queue();
  202. private readonly object syncRoot;
  203. private bool stopped;
  204. /// <summary>
  205. /// WaitHandle for synchronous event delivery in <see cref="Enqueue"/>.
  206. /// <para>
  207. /// Having just one handle for the whole <see cref="EventQueue"/> implies that
  208. /// there may be only one producer (the test thread) for synchronous events.
  209. /// If there can be multiple producers for synchronous events, one would have
  210. /// to introduce one WaitHandle per event.
  211. /// </para>
  212. /// </summary>
  213. private AutoResetEvent synchronousEventSent;
  214. public int Count
  215. {
  216. get
  217. {
  218. lock( this.syncRoot )
  219. {
  220. return this.queue.Count;
  221. }
  222. }
  223. }
  224. public EventQueue()
  225. {
  226. this.syncRoot = queue.SyncRoot;
  227. }
  228. /// <summary>
  229. /// Sets a handle on which to wait, when <see cref="Enqueue"/> is called
  230. /// for an <see cref="Event"/> with <see cref="Event.IsSynchronous"/> == true.
  231. /// </summary>
  232. /// <param name="synchronousEventWaitHandle">
  233. /// The wait handle on which to wait, when <see cref="Enqueue"/> is called
  234. /// for an <see cref="Event"/> with <see cref="Event.IsSynchronous"/> == true.
  235. /// <para>The caller is responsible for disposing this wait handle.</para>
  236. /// </param>
  237. public void SetWaitHandleForSynchronizedEvents( AutoResetEvent synchronousEventWaitHandle )
  238. {
  239. this.synchronousEventSent = synchronousEventWaitHandle;
  240. }
  241. public void Enqueue( Event e )
  242. {
  243. lock( this.syncRoot )
  244. {
  245. this.queue.Enqueue( e );
  246. Monitor.Pulse( this.syncRoot );
  247. }
  248. if ( this.synchronousEventSent != null && e.IsSynchronous )
  249. {
  250. this.synchronousEventSent.WaitOne();
  251. }
  252. else
  253. {
  254. Thread.Sleep( 0 ); // give EventPump thread a chance to process the event
  255. }
  256. }
  257. /// <summary>
  258. /// Removes the first element from the queue and returns it (or <c>null</c>).
  259. /// </summary>
  260. /// <param name="blockWhenEmpty">
  261. /// If <c>true</c> and the queue is empty, the calling thread is blocked until
  262. /// either an element is enqueued, or <see cref="Stop"/> is called.
  263. /// </param>
  264. /// <returns>
  265. /// <list type="bullet">
  266. /// <item>
  267. /// <term>If the queue not empty</term>
  268. /// <description>the first element.</description>
  269. /// </item>
  270. /// <item>
  271. /// <term>otherwise, if <paramref name="blockWhenEmpty"/>==<c>false</c>
  272. /// or <see cref="Stop"/> has been called</term>
  273. /// <description><c>null</c>.</description>
  274. /// </item>
  275. /// </list>
  276. /// </returns>
  277. public Event Dequeue( bool blockWhenEmpty )
  278. {
  279. lock( this.syncRoot )
  280. {
  281. while ( this.queue.Count == 0 )
  282. {
  283. if ( blockWhenEmpty && !this.stopped )
  284. {
  285. Monitor.Wait( this.syncRoot );
  286. }
  287. else
  288. {
  289. return null;
  290. }
  291. }
  292. return (Event)this.queue.Dequeue();
  293. }
  294. }
  295. public void Stop()
  296. {
  297. lock( this.syncRoot )
  298. {
  299. if ( !this.stopped )
  300. {
  301. this.stopped = true;
  302. Monitor.Pulse( this.syncRoot );
  303. }
  304. }
  305. }
  306. }
  307. }