vendredi 27 février 2015

Unit Testing a Windows Service Event Handlers

I am writing a Windows Service and I want to write unit tests against the Event Handlers for checking how they manage the worker thread. I've managed the OnStart, OnStop and OnPause with no problem, but the OnContinue is causing me some angst.


Windows Service


The Program.Main()


This does nothing out of the ordinary - it just sets up the Service to run.


MyService.cs This has a Manual Reset Event:



private static ManualResetEvent _resetEvent = new ManualResetEvent(false);


MyService.OnStart()


This creates a Worker thread, so that the Main thread can continue listening to events from the Service Control Manager


MyService.OnStop()


This checks if the Workerthread.IsAlive is true, and if so, calls .Abort() on it.


MyService.OnPause()


This checks if the Worker's ThreadState is either Suspended | SuspendRequested, and if not then it calls .Reset() on the _resetEvent.


Note: currently unsure if these are the ideal states, or if I should be looking at the state 'WaitSleepJoin'.


MyService.OnContinue()


This checks if the Worker's ThreadState is either Suspended | SuspendRequested, and if so then it calls .Set() on the _resetEvent.


MyTests


OnStart() tests


This is easy, I just call the Event Handler and it sees there's no Worker thread, so creates one.


OnStop() tests


Slightly more complex. Here in the test I create a Worker thread that calls a delegate which sends it to sleep. I then use Reflection to set it as the Worker thread private property in the Service, and then call the OnStop() event handler. This finds the started Worker Thread which is sleeping, so aborts it.


OnPause() tests


Similar to the OnStop() tests, but instead of creating a sleeping thread, I create one that iterates through a small loop (that's big enough last a few seconds). When I call the OnPause() method it finds the Worker Thread which is working, and calls .Reset() on the _resetEvent.


OnContinue() tests


So my plan here was to pattern the OnPause and OnStop tests, but this time create a worker thread that was currently suspended. However, in .NET 4.5, Thread.Suspend() is obsolete.


I therefore created a local ManualResetEvent [one per unit test; I have multiple Unit Tests for the OnContinue() event handler] and in my delegate method that the Worker thread is running, I set it to call .WaitOne(). If I run one test, then that works fine. But if I run them all together then it appears that the Unique ManualResetEvent for each test somehow affects the other tests.


Example code from just one Unit Test where I create the Worker Thread and inject it into the Service's private thread method:



const BindingFlags InstanceFlags =
BindingFlags.Instance | BindingFlags.NonPublic;

PropertyInfo prop = t.GetProperty("MyWorkerThread", InstanceFlags);
EventWaitHandle wh2 = new ManualResetEvent(true);
var thread = new Thread(() =>
{
var sb = new Lazy<StringBuilder>();
for (int i = 0; i < 10000000; i++)
{
wh2.WaitOne();
sb.Value.Append(i.ToString());
}
});

thread.Start();
prop.SetValue(service, thread, null);


So when I duplicate this in another test, but have "wh3" rather than "wh2", then my tests all seem to trip over themselves.


Tools Using Visual Studio 2013 and the NUnit test runner.


Would appreciate it if anyone could point out where I'm going wrong, or better still, point me to a better way to achieve this.


Many thanks


Griff


Aucun commentaire:

Enregistrer un commentaire