diff --git a/src/NUnitFramework/framework/Internal/Execution/EventPump.cs b/src/NUnitFramework/framework/Internal/Execution/EventPump.cs
index 02834aeb40..63c55b95e9 100644
--- a/src/NUnitFramework/framework/Internal/Execution/EventPump.cs
+++ b/src/NUnitFramework/framework/Internal/Execution/EventPump.cs
@@ -29,12 +29,32 @@ public enum EventPumpState
}
///
- /// EventPump pulls events out of an EventQueue and sends
- /// them to a listener. It is used to send events back to
+ /// EventPump pulls Event instances out of an EventQueue and sends
+ /// them to a ITestListener. It is used to send these events back to
/// the client without using the CallContext of the test
/// runner thread.
///
- public class EventPump : IDisposable
+ public sealed class EventPump : EventPump, IDisposable
+ {
+ ///
+ /// Constructor for standard EventPump
+ ///
+ /// The EventListener to receive events
+ /// The event queue to pull events from
+ public EventPump(ITestListener eventListener, EventQueue events)
+ : base(eventListener, events, "Standard")
+ {
+ }
+ }
+
+ ///
+ /// EventPump base class pulls events of any type out of an EventQueue and sends
+ /// them to any listener. It is used to send events back to
+ /// the client without using the CallContext of the test
+ /// runner thread.
+ ///
+ public abstract class EventPump : IDisposable
+ where TEvent : IEvent
{
private static readonly Logger Log = InternalTrace.GetLogger("EventPump");
@@ -43,12 +63,12 @@ public class EventPump : IDisposable
///
/// The downstream listener to which we send events
///
- private readonly ITestListener _eventListener;
+ private readonly TListener _eventListener;
///
/// The queue that holds our events
///
- private readonly EventQueue _events;
+ private readonly EventQueue _events;
///
/// Thread to do the pumping
@@ -63,15 +83,18 @@ public class EventPump : IDisposable
#endregion
#region Constructor
+
///
/// Constructor
///
/// The EventListener to receive events
/// The event queue to pull events from
- public EventPump(ITestListener eventListener, EventQueue events)
+ /// Name of the thread and pump
+ protected EventPump(TListener eventListener, EventQueue events, string name = "Standard")
{
_eventListener = eventListener;
_events = events;
+ Name = name;
}
#endregion
@@ -111,7 +134,7 @@ public void Start()
{
_pumpThread = new Thread(PumpThreadProc)
{
- Name = "EventPumpThread" + Name,
+ Name = $"{Name}EventPumpThread",
Priority = ThreadPriority.Highest
};
@@ -142,14 +165,14 @@ public void Stop()
///
private void PumpThreadProc()
{
- Log.Debug("Starting EventPump");
+ Log.Debug($"Starting {Name}");
//ITestListener hostListeners = CoreExtensions.Host.Listeners;
try
{
while (true)
{
- Event? e = _events.Dequeue(PumpState == EventPumpState.Pumping);
+ var e = _events.Dequeue(PumpState == EventPumpState.Pumping);
if (e is null)
break;
try
diff --git a/src/NUnitFramework/framework/Internal/Execution/EventQueue.cs b/src/NUnitFramework/framework/Internal/Execution/EventQueue.cs
index c9308fa9d4..595c707993 100644
--- a/src/NUnitFramework/framework/Internal/Execution/EventQueue.cs
+++ b/src/NUnitFramework/framework/Internal/Execution/EventQueue.cs
@@ -6,16 +6,29 @@
namespace NUnit.Framework.Internal.Execution
{
+ ///
+ /// Interface for ALL event types that can be queued for processing.
+ ///
+ ///
+ public interface IEvent
+ {
+ ///
+ /// The Send method is implemented by derived classes to send the event to the specified listener.
+ ///
+ /// The listener.
+ void Send(TListener listener);
+ }
+
#region Individual Event Classes
///
- /// NUnit.Core.Event is the abstract base for all stored events.
+ /// NUnit.Core.Event is the abstract base for all stored standard events.
/// An Event is the stored representation of a call to the
/// ITestListener interface and is used to record such calls
/// or to queue them for forwarding on another thread or at
/// a later time.
///
- public abstract class Event
+ public abstract class Event : IEvent
{
///
/// The Send method is implemented by derived classes to send the event to the specified listener.
@@ -134,16 +147,25 @@ public override void Send(ITestListener listener)
#endregion
///
- /// Implements a queue of work items each of which
+ /// Implements a queue of work items for the Event type each of which
+ /// is queued as a WaitCallback.
+ ///
+ public sealed class EventQueue : EventQueue
+ {
+ }
+
+ ///
+ /// Implements a template for a queue of work items each of which
/// is queued as a WaitCallback.
+ /// It can handle any event types.
///
- public class EventQueue
+ public abstract class EventQueue
{
private const int SpinCount = 5;
// static readonly Logger log = InternalTrace.GetLogger("EventQueue");
- private readonly ConcurrentQueue _queue = new();
+ private readonly ConcurrentQueue _queue = new();
/* This event is used solely for the purpose of having an optimized sleep cycle when
* we have to wait on an external event (Add or Remove for instance)
@@ -171,7 +193,7 @@ public class EventQueue
/// Enqueues the specified event
///
/// The event to enqueue.
- public void Enqueue(Event e)
+ public void Enqueue(T e)
{
do
{
@@ -216,7 +238,7 @@ public void Enqueue(Event e)
///
///
///
- public Event? Dequeue(bool blockWhenEmpty)
+ public T? Dequeue(bool blockWhenEmpty)
{
SpinWait sw = new SpinWait();
@@ -229,7 +251,7 @@ public void Enqueue(Event e)
if (cachedRemoveId == cachedAddId)
{
if (!blockWhenEmpty || _stopped != 0)
- return null;
+ return default(T);
// Spin a few times to see if something changes
if (sw.Count <= SpinCount)
@@ -261,11 +283,11 @@ public void Enqueue(Event e)
continue;
// Dequeue our work item
- Event? e;
+ T? e;
while (!_queue.TryDequeue(out e))
{
if (!blockWhenEmpty || _stopped != 0)
- return null;
+ return default(T);
}
return e;