~charlie.poole/nunitv2/equality-handlers

« back to all changes in this revision

Viewing changes to src/NUnitCore/tests/EventQueueTests.cs

  • Committer: Charlie Poole
  • Date: 2011-03-27 06:12:08 UTC
  • mfrom: (3290.1.7 work)
  • Revision ID: charlie@nunit.org-20110327061208-268vaqov3fc1fy71
Merge changes from trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
5
5
// ****************************************************************
6
6
using System;
7
7
using System.Collections;
 
8
using System.Diagnostics;
 
9
using System.IO;
8
10
using System.Threading;
 
11
using NUnit.Core.Extensibility;
9
12
using NUnit.Framework;
10
13
 
 
14
using ThreadState = System.Threading.ThreadState;
 
15
 
11
16
namespace NUnit.Core.Tests
12
17
{
13
18
        /// <summary>
19
24
        static readonly Event[] events = {
20
25
                                new RunStartedEvent( string.Empty, 0 ),
21
26
                                new SuiteStartedEvent( null ),
 
27
                new OutputEvent( new TestOutput( string.Empty, TestOutputType.Log )),
22
28
                                new TestStartedEvent( null ),
 
29
                new OutputEvent( new TestOutput( string.Empty, TestOutputType.Out )),
23
30
                                new TestFinishedEvent( null ),
 
31
                new OutputEvent( new TestOutput( string.Empty, TestOutputType.Trace )),
24
32
                                new SuiteFinishedEvent( null ),
25
33
                                new RunFinishedEvent( (TestResult)null )
26
34
                        };
41
49
        {
42
50
            for (int index = 0; index < events.Length; index++)
43
51
            {
44
 
                Event e = q.Dequeue();
 
52
                Event e = q.Dequeue(false);
45
53
                Assert.AreEqual(events[index].GetType(), e.GetType(),
46
54
                    string.Format("Event {0}", index));
47
55
            }
77
85
            }
78
86
        }
79
87
 
 
88
        #region EventQueue tests
 
89
 
80
90
        [Test]
81
91
        public void QueueEvents()
82
92
        {
85
95
            VerifyQueue(q);
86
96
        }
87
97
 
 
98
         [Test]
 
99
        public void DequeueEmpty()
 
100
        {
 
101
            EventQueue q = new EventQueue();
 
102
            Assert.IsNull(q.Dequeue(false));
 
103
        }
 
104
 
 
105
        [TestFixture]
 
106
        public class DequeueBlocking_StopTest : ProducerConsumerTest
 
107
        {
 
108
            private EventQueue q;
 
109
            private volatile int receivedEvents;
 
110
 
 
111
            [Test]
 
112
            [Timeout(1000)]
 
113
            public void DequeueBlocking_Stop()
 
114
            {
 
115
                this.q = new EventQueue();
 
116
                this.receivedEvents = 0;
 
117
                this.RunProducerConsumer();
 
118
                Assert.AreEqual(events.Length + 1, this.receivedEvents);
 
119
            }
 
120
 
 
121
            protected override void Producer()
 
122
            {
 
123
                EnqueueEvents(this.q);
 
124
                while (this.receivedEvents < events.Length)
 
125
                {
 
126
                    Thread.Sleep(30);
 
127
                }
 
128
 
 
129
                this.q.Stop();
 
130
            }
 
131
 
 
132
            protected override void Consumer()
 
133
            {
 
134
                Event e;
 
135
                do
 
136
                {
 
137
                    e = this.q.Dequeue(true);
 
138
                    this.receivedEvents++;
 
139
                    Thread.MemoryBarrier();
 
140
                }
 
141
                while (e != null);
 
142
            }
 
143
        }
 
144
 
 
145
        [TestFixture]
 
146
        public class SetWaitHandle_Enqueue_SynchronousTest : ProducerConsumerTest
 
147
        {
 
148
            private EventQueue q;
 
149
            private AutoResetEvent waitHandle;
 
150
            private volatile bool afterEnqueue;
 
151
 
 
152
            [Test]
 
153
            [Timeout(1000)]
 
154
            public void SetWaitHandle_Enqueue_Synchronous()
 
155
            {
 
156
                using (this.waitHandle = new AutoResetEvent(false))
 
157
                {
 
158
                    this.q = new EventQueue();
 
159
                    this.q.SetWaitHandleForSynchronizedEvents(this.waitHandle);
 
160
                    this.afterEnqueue = false;
 
161
                    this.RunProducerConsumer();
 
162
                }
 
163
            }
 
164
 
 
165
            protected override void Producer()
 
166
            {
 
167
                Event synchronousEvent = new RunStartedEvent(string.Empty, 0);
 
168
                Assert.IsTrue(synchronousEvent.IsSynchronous);
 
169
                this.q.Enqueue(synchronousEvent);
 
170
                this.afterEnqueue = true;
 
171
                Thread.MemoryBarrier();
 
172
            }
 
173
 
 
174
            protected override void Consumer()
 
175
            {
 
176
                this.q.Dequeue(true);
 
177
                Thread.Sleep(30);
 
178
                Assert.IsFalse(this.afterEnqueue);
 
179
                this.waitHandle.Set();
 
180
                Thread.Sleep(30);
 
181
                Assert.IsTrue(this.afterEnqueue);
 
182
            }
 
183
        }
 
184
 
 
185
        [TestFixture]
 
186
        public class SetWaitHandle_Enqueue_AsynchronousTest : ProducerConsumerTest
 
187
        {
 
188
            private EventQueue q;
 
189
            private volatile bool afterEnqueue;
 
190
 
 
191
            [Test]
 
192
            [Timeout(1000)]
 
193
            public void SetWaitHandle_Enqueue_Asynchronous()
 
194
            {
 
195
                using (AutoResetEvent waitHandle = new AutoResetEvent(false))
 
196
                {
 
197
                    this.q = new EventQueue();
 
198
                    this.q.SetWaitHandleForSynchronizedEvents(waitHandle);
 
199
                    this.afterEnqueue = false;
 
200
                    this.RunProducerConsumer();
 
201
                }
 
202
            }
 
203
 
 
204
            protected override void Producer()
 
205
            {
 
206
                Event asynchronousEvent = new OutputEvent(new TestOutput(string.Empty, TestOutputType.Trace));
 
207
                Assert.IsFalse(asynchronousEvent.IsSynchronous);
 
208
                this.q.Enqueue(asynchronousEvent);
 
209
                this.afterEnqueue = true;
 
210
                Thread.MemoryBarrier();
 
211
            }
 
212
 
 
213
            protected override void Consumer()
 
214
            {
 
215
                this.q.Dequeue(true);
 
216
                Thread.Sleep(30);
 
217
                Assert.IsTrue(this.afterEnqueue);
 
218
            }
 
219
        }
 
220
 
 
221
        #endregion EventQueue tests
 
222
 
 
223
        #region QueuingEventListener tests
 
224
 
88
225
        [Test]
89
226
        public void SendEvents()
90
227
        {
93
230
            VerifyQueue(el.Events);
94
231
        }
95
232
 
 
233
        #endregion
 
234
 
 
235
        #region EventPump tests
 
236
 
96
237
        [Test]
97
238
        public void StartAndStopPumpOnEmptyQueue()
98
239
        {
99
 
            EventPump pump = new EventPump(NullListener.NULL, new EventQueue(), false);
100
 
            StartPump(pump, 1000);
101
 
            Assert.That(pump.PumpState, Is.EqualTo(EventPumpState.Pumping));
102
 
            StopPump(pump, 1000);
103
 
            Assert.That(pump.PumpState, Is.EqualTo(EventPumpState.Stopped));
 
240
            EventQueue q = new EventQueue();
 
241
            using (EventPump pump = new EventPump(NullListener.NULL, q, false))
 
242
            {
 
243
                pump.Name = "StartAndStopPumpOnEmptyQueue";
 
244
                StartPump(pump, 1000);
 
245
                Assert.That(pump.PumpState, Is.EqualTo(EventPumpState.Pumping));
 
246
                StopPump(pump, 1000);
 
247
                Assert.That(pump.PumpState, Is.EqualTo(EventPumpState.Stopped));
 
248
            }
104
249
        }
105
250
 
106
251
        [Test]
107
252
        public void PumpAutoStopsOnRunFinished()
108
253
        {
109
254
            EventQueue q = new EventQueue();
110
 
            EventPump pump = new EventPump(NullListener.NULL, q, true);
111
 
            Assert.That(pump.PumpState, Is.EqualTo(EventPumpState.Stopped));
112
 
            StartPump(pump, 1000);
113
 
            Assert.That(pump.PumpState, Is.EqualTo(EventPumpState.Pumping));
114
 
            q.Enqueue(new RunFinishedEvent(new Exception()));
115
 
            WaitForPumpToStop(pump, 1000);
116
 
            Assert.That(pump.PumpState, Is.EqualTo(EventPumpState.Stopped));
 
255
            using (EventPump pump = new EventPump(NullListener.NULL, q, true))
 
256
            {
 
257
                pump.Name = "PumpAutoStopsOnRunFinished";
 
258
                Assert.That(pump.PumpState, Is.EqualTo(EventPumpState.Stopped));
 
259
                StartPump(pump, 1000);
 
260
                Assert.That(pump.PumpState, Is.EqualTo(EventPumpState.Pumping));
 
261
                q.Enqueue(new RunFinishedEvent(new Exception()));
 
262
                WaitForPumpToStop(pump, 1000);
 
263
                Assert.That(pump.PumpState, Is.EqualTo(EventPumpState.Stopped));
 
264
            }
117
265
        }
118
266
 
119
267
        [Test]
 
268
        [Timeout(3000)]
120
269
        public void PumpEvents()
121
270
        {
122
271
            EventQueue q = new EventQueue();
123
 
            EnqueueEvents(q);
124
272
            QueuingEventListener el = new QueuingEventListener();
125
 
            EventPump pump = new EventPump(el, q, false);
126
 
            Assert.That(pump.PumpState, Is.EqualTo(EventPumpState.Stopped));
127
 
            StartPump(pump, 1000);
128
 
            Assert.That(pump.PumpState, Is.EqualTo(EventPumpState.Pumping));
129
 
            StopPump(pump, 1000);
130
 
            Assert.That(pump.PumpState, Is.EqualTo(EventPumpState.Stopped));
 
273
            using (EventPump pump = new EventPump(el, q, false))
 
274
            {
 
275
                pump.Name = "PumpEvents";
 
276
                Assert.That(pump.PumpState, Is.EqualTo(EventPumpState.Stopped));
 
277
                StartPump(pump, 1000);
 
278
                Assert.That(pump.PumpState, Is.EqualTo(EventPumpState.Pumping));
 
279
                EnqueueEvents(q);
 
280
                StopPump(pump, 1000);
 
281
                Assert.That(pump.PumpState, Is.EqualTo(EventPumpState.Stopped));
 
282
            }
131
283
            VerifyQueue(el.Events);
132
284
        }
133
285
 
134
286
        [Test]
 
287
        [Timeout(2000)]
135
288
        public void PumpEventsWithAutoStop()
136
289
        {
 
290
             EventQueue q = new EventQueue();
 
291
            QueuingEventListener el = new QueuingEventListener();
 
292
            using (EventPump pump = new EventPump(el, q, true))
 
293
            {
 
294
                pump.Name = "PumpEventsWithAutoStop";
 
295
                pump.Start();
 
296
                EnqueueEvents(q);
 
297
                int tries = 10;
 
298
                while (--tries > 0 && q.Count > 0)
 
299
                {
 
300
                    Thread.Sleep(100);
 
301
                }
 
302
                Assert.That(pump.PumpState, Is.EqualTo(EventPumpState.Stopped));
 
303
            }
 
304
        }
 
305
 
 
306
        [Test]
 
307
        [Timeout(2000)]
 
308
        public void PumpPendingEventsAfterAutoStop()
 
309
        {
137
310
            EventQueue q = new EventQueue();
138
311
            EnqueueEvents(q);
139
 
            Assert.AreEqual(6, q.Count);
 
312
            Event[] eventsAfterStop =
 
313
            {
 
314
                new OutputEvent(new TestOutput("foo", TestOutputType.Out)),
 
315
                new OutputEvent(new TestOutput("bar", TestOutputType.Trace)),
 
316
            };
 
317
            foreach (Event e in eventsAfterStop)
 
318
            {
 
319
                q.Enqueue(e);
 
320
            }
 
321
 
140
322
            QueuingEventListener el = new QueuingEventListener();
141
 
            EventPump pump = new EventPump(el, q, true);
142
 
            pump.Start();
143
 
            int tries = 10;
144
 
            while (--tries > 0 && q.Count > 0)
145
 
            {
146
 
                Thread.Sleep(100);
147
 
            }
148
 
            Assert.That(pump.PumpState, Is.EqualTo(EventPumpState.Stopped));
 
323
            using (EventPump pump = new EventPump(el, q, true))
 
324
            {
 
325
                pump.Name = "PumpPendingEventsAfterAutoStop";
 
326
                pump.Start();
 
327
                int tries = 10;
 
328
                while (--tries > 0 && q.Count > 0)
 
329
                {
 
330
                    Thread.Sleep(100);
 
331
                }
 
332
 
 
333
                Assert.That(pump.PumpState, Is.EqualTo(EventPumpState.Stopped));
 
334
            }
 
335
            Assert.That(el.Events.Count, Is.EqualTo(events.Length + eventsAfterStop.Length));
 
336
        }
 
337
 
 
338
        [Test]
 
339
        [Timeout(1000)]
 
340
        public void PumpSynchronousAndAsynchronousEvents()
 
341
        {
 
342
            EventQueue q = new EventQueue();
 
343
            using (EventPump pump = new EventPump(NullListener.NULL, q, false))
 
344
            {
 
345
                pump.Name = "PumpSynchronousAndAsynchronousEvents";
 
346
                pump.Start();
 
347
 
 
348
                int numberOfAsynchronousEvents = 0;
 
349
                int sumOfAsynchronousQueueLength = 0;
 
350
                const int Repetitions = 2;
 
351
                for (int i = 0; i < Repetitions; i++)
 
352
                {
 
353
                    foreach (Event e in events)
 
354
                    {
 
355
                        q.Enqueue(e);
 
356
                        if (e.IsSynchronous)
 
357
                        {
 
358
                            Assert.That(q.Count, Is.EqualTo(0));
 
359
                        }
 
360
                        else
 
361
                        {
 
362
                            sumOfAsynchronousQueueLength += q.Count;
 
363
                            numberOfAsynchronousEvents++;
 
364
                        }
 
365
                    }
 
366
                }
 
367
 
 
368
                Console.WriteLine("Average queue length: {0}", (float)sumOfAsynchronousQueueLength / numberOfAsynchronousEvents);
 
369
            }
 
370
        }
 
371
 
 
372
        /// <summary>
 
373
        /// Verifies that when
 
374
        /// (1) Traces are captured and fed into the EventListeners, and
 
375
        /// (2) an EventListener writes Traces,
 
376
        /// the Trace / EventPump / EventListener do not deadlock.
 
377
        /// </summary>
 
378
        /// <remarks>
 
379
        /// This mainly simulates the object structure created by RemoteTestRunner.Run.
 
380
        /// </remarks>
 
381
        [Test]
 
382
        [Timeout(1000)]
 
383
        public void TracingEventListenerDoesNotDeadlock()
 
384
        {
 
385
            QueuingEventListener upstreamListener = new QueuingEventListener();
 
386
            EventQueue upstreamListenerQueue = upstreamListener.Events;
 
387
 
 
388
            // Install a TraceListener sending TestOutput events to the upstreamListener.
 
389
            // This simulates RemoteTestRunner.StartTextCapture, where TestContext installs such a TraceListener.
 
390
            TextWriter traceWriter = new EventListenerTextWriter(upstreamListener, TestOutputType.Trace);
 
391
            const string TraceListenerName = "TracingEventListenerDoesNotDeadlock";
 
392
            TraceListener feedingTraceToUpstreamListener = new TextWriterTraceListener(traceWriter, TraceListenerName);
 
393
 
 
394
            try
 
395
            {
 
396
                Trace.Listeners.Add(feedingTraceToUpstreamListener);
 
397
 
 
398
                // downstreamListenerToTrace simulates an EventListener installed e.g. by an Addin, 
 
399
                // which may call Trace within the EventListener methods:
 
400
                TracingEventListener downstreamListenerToTrace = new TracingEventListener();
 
401
                using (EventPump pump = new EventPump(downstreamListenerToTrace, upstreamListenerQueue, false))
 
402
                {
 
403
                    pump.Name = "TracingEventListenerDoesNotDeadlock";
 
404
                    pump.Start();
 
405
 
 
406
                    const int Repetitions = 10;
 
407
                    for (int i = 0; i < Repetitions; i++)
 
408
                    {
 
409
                        foreach (Event e in events)
 
410
                        {
 
411
                            Trace.WriteLine("Before sending {0} event.", e.GetType().Name);
 
412
                            e.Send(upstreamListener);
 
413
                            Trace.WriteLine("After sending {0} event.", e.GetType().Name);
 
414
                        }
 
415
                    }
 
416
                }
 
417
            }
 
418
            finally
 
419
            {
 
420
                Trace.Listeners.Remove(TraceListenerName);
 
421
                feedingTraceToUpstreamListener.Dispose();
 
422
            }
 
423
        }
 
424
 
 
425
        /// <summary> 
 
426
        /// Floods the queue of an EventPump with multiple concurrent event producers.
 
427
        /// Prints the maximum queue length to Console, but does not implement an
 
428
        /// oracle on what the maximum queue length should be.
 
429
        /// </summary>
 
430
        /// <param name="numberOfProducers">The number of concurrent producer threads.</param>
 
431
        /// <param name="producerDelay">
 
432
        /// If <c>true</c>, the producer threads slow down by adding a short delay time.
 
433
        /// </param>
 
434
        [TestCase(1, false)]
 
435
        [TestCase(5, true)]
 
436
        [TestCase(5, false)]
 
437
        [Explicit("Takes several seconds. Just prints the queue length of the EventPump to Console, but has no oracle regarding this.")]
 
438
        public void EventPumpQueueLength(int numberOfProducers, bool producerDelay)
 
439
        {
 
440
            EventQueue q = new EventQueue();
 
441
            EventProducer[] producers = new EventProducer[numberOfProducers];
 
442
            for (int i = 0; i < numberOfProducers; i++)
 
443
            {
 
444
                producers[i] = new EventProducer(q, i, producerDelay);
 
445
            }
 
446
 
 
447
            using (EventPump pump = new EventPump(NullListener.NULL, q, false))
 
448
            {
 
449
                pump.Name = "EventPumpQueueLength";
 
450
                pump.Start();
 
451
 
 
452
                foreach (EventProducer p in producers)
 
453
                {
 
454
                    p.ProducerThread.Start();
 
455
                }
 
456
                foreach (EventProducer p in producers)
 
457
                {
 
458
                    p.ProducerThread.Join();
 
459
                }
 
460
                pump.Stop();
 
461
            }
 
462
            Assert.That(q.Count, Is.EqualTo(0));
 
463
 
 
464
            foreach (EventProducer p in producers)
 
465
            {
 
466
                Console.WriteLine(
 
467
                    "#Events: {0}, MaxQueueLength: {1}", p.SentEventsCount, p.MaxQueueLength);
 
468
                Assert.IsNull(p.Exception, "{0}", p.Exception);
 
469
            }
 
470
        }
 
471
 
 
472
        #endregion
 
473
    
 
474
        public abstract class ProducerConsumerTest
 
475
        {
 
476
            private volatile Exception myConsumerException;
 
477
 
 
478
            protected void RunProducerConsumer()
 
479
            {
 
480
                this.myConsumerException = null;
 
481
                Thread consumerThread = new Thread(new ThreadStart(this.ConsumerThreadWrapper));
 
482
                try
 
483
                {
 
484
                    consumerThread.Start();
 
485
                    this.Producer();
 
486
                    bool consumerStopped = consumerThread.Join(1000);
 
487
                    Assert.IsTrue(consumerStopped);
 
488
                }
 
489
                finally
 
490
                {
 
491
                    consumerThread.Abort();
 
492
                    if ((consumerThread.ThreadState & ThreadState.WaitSleepJoin) != 0)
 
493
                    {
 
494
                        consumerThread.Interrupt();
 
495
                    }
 
496
                }
 
497
 
 
498
                Assert.IsNull(this.myConsumerException);
 
499
            }
 
500
 
 
501
            protected abstract void Producer();
 
502
 
 
503
            protected abstract void Consumer();
 
504
 
 
505
            private void ConsumerThreadWrapper()
 
506
            {
 
507
                try
 
508
                {
 
509
                    this.Consumer();
 
510
                }
 
511
                catch (ThreadAbortException)
 
512
                {
 
513
                    Thread.ResetAbort();
 
514
                }
 
515
                catch (Exception ex)
 
516
                {
 
517
                    this.myConsumerException = ex;
 
518
                }
 
519
            }
 
520
        }
 
521
 
 
522
        private class EventProducer
 
523
        {
 
524
            public readonly Thread ProducerThread;
 
525
            public int SentEventsCount;
 
526
            public int MaxQueueLength;
 
527
            public Exception Exception;
 
528
            private readonly EventQueue queue;
 
529
            private readonly bool delay;
 
530
 
 
531
            public EventProducer(EventQueue q, int id, bool delay)
 
532
            {
 
533
                this.queue = q;
 
534
                this.ProducerThread = new Thread(new ThreadStart(this.Produce));
 
535
                this.ProducerThread.Name = this.GetType().FullName + id;
 
536
                this.delay = delay;
 
537
            }
 
538
 
 
539
            private void Produce()
 
540
            {
 
541
                try
 
542
                {
 
543
                    Event e = new OutputEvent(new TestOutput(this.ProducerThread.Name, TestOutputType.Log));
 
544
                    DateTime start = DateTime.Now;
 
545
                    while (DateTime.Now - start <= TimeSpan.FromSeconds(3))
 
546
                    {
 
547
                        this.queue.Enqueue(e);
 
548
                        this.SentEventsCount++;
 
549
                        this.MaxQueueLength = Math.Max(this.queue.Count, this.MaxQueueLength);
 
550
 
 
551
                        // without Sleep or with just a Sleep(0), the EventPump thread does not keep up and the queue gets very long
 
552
                        if (this.delay)
 
553
                        {
 
554
                            Thread.Sleep(1);
 
555
                        }
 
556
                    }
 
557
                }
 
558
                catch (Exception ex)
 
559
                {
 
560
                    this.Exception = ex;
 
561
                }
 
562
            }
 
563
        }
 
564
 
 
565
        private class TracingEventListener : EventListener
 
566
        {
 
567
            #region EventListener Members
 
568
            public void RunStarted(string name, int testCount)
 
569
            {
 
570
                WriteTrace("RunStarted({0},{1})", name, testCount);
 
571
            }
 
572
 
 
573
            public void RunFinished(TestResult result)
 
574
            {
 
575
                WriteTrace("RunFinished({0})", result);
 
576
            }
 
577
 
 
578
            public void RunFinished(Exception exception)
 
579
            {
 
580
                WriteTrace("RunFinished({0})", exception);
 
581
            }
 
582
 
 
583
            public void TestStarted(TestName testName)
 
584
            {
 
585
                WriteTrace("TestStarted({0})", testName);
 
586
            }
 
587
 
 
588
            public void TestFinished(TestResult result)
 
589
            {
 
590
                WriteTrace("TestFinished({0})", result);
 
591
            }
 
592
 
 
593
            public void SuiteStarted(TestName testName)
 
594
            {
 
595
                WriteTrace("SuiteStarted({0})", testName);
 
596
            }
 
597
 
 
598
            public void SuiteFinished(TestResult result)
 
599
            {
 
600
                WriteTrace("SuiteFinished({0})", result);
 
601
            }
 
602
 
 
603
            public void UnhandledException(Exception exception)
 
604
            {
 
605
                WriteTrace("UnhandledException({0})", exception);
 
606
            }
 
607
 
 
608
            public void TestOutput(TestOutput testOutput)
 
609
            {
 
610
                if (testOutput.Type != TestOutputType.Trace)
 
611
                {
 
612
                    WriteTrace("TestOutput {0}: '{1}'", testOutput.Type, testOutput.Text);
 
613
                }
 
614
            }
 
615
            #endregion
 
616
 
 
617
#if NET_2_0
 
618
            private static void WriteTrace(string message, params object[] args)
 
619
            {
 
620
                Trace.TraceInformation(message, args);
 
621
            }
 
622
#else
 
623
            private static void WriteTrace(string message, params object[] args)
 
624
            {
 
625
                Trace.WriteLine(string.Format(message, args));
 
626
            }
 
627
#endif
149
628
        }
150
629
    }
151
630
}