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