PageRenderTime 24ms CodeModel.GetById 15ms app.highlight 6ms RepoModel.GetById 1ms app.codeStats 0ms

/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}