Unit testing synchronization constructs...

146 views
Skip to first unread message

Gishu Pillai

unread,
Feb 10, 2014, 5:45:13 AM2/10/14
to growing-object-o...@googlegroups.com
I'm writing a class that supports 2 methods Start and Stop

The requirements

Start
1. Start needs to spawn a background task/thread that is running until Stop is called
2. Start needs to invoke a library function DeviceStart passing a bunch of callbacks from within the background task
3. Start should block until DeviceStart has completed.

Stop
1. Gracefully terminates the background task

Now the implementation is pretty straightforward however getting this under test.. I'm struggling a bit

* Spawning a new task can be tested via injecting a synchronous scheduler
* Bullet#3 is a bit of a problem. Start can pass in a synchronization construct like a ResetEvent on which Start can block, the background task can signal it at the right time. However how do I test this ?

Is this a solved problem ?

Gishu

Angel Java Lopez

unread,
Feb 10, 2014, 6:02:56 AM2/10/14
to growing-object-o...@googlegroups.com
Hi!

Gishu, maybe I misunderstood your problem, but there is a  possible solution.


In this test, I have an actor (a la Akka/Scala actor), that receive messages, in a parallel thread. The process of the message is written in a lambda C# expression: it increments a shared counter, and then, set on a wait handle.

The test body creates and launches the actor. Send it a message. Wait the shared handle. AND THEN, it tests the shared total.

In this simple I case, I could ignore the total. But I use the total trick in other test, like


to check the process of many messages.

I could add a TryWait, instead of Wait, in case I want to raise an Assert.Fail() if the try wait timeout

Angel "Java" Lopez
@ajlopez




--
 
---
You received this message because you are subscribed to the Google Groups "Growing Object-Oriented Software" group.
To unsubscribe from this group and stop receiving emails from it, send an email to growing-object-oriente...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Gishu Pillai

unread,
Feb 10, 2014, 6:18:29 AM2/10/14
to growing-object-o...@googlegroups.com
Hi Angel,

This involves spinning up multiple threads and involves real sync constructs in unit tests. Maybe it's unavoidable.. but even with this I can't test things like sync constructs timing out (see Start timed out below)

Maybe the following code snippet makes things clearer...

void Start()
{
   using (var blocker = new AutoResetEvent())
   {
      // _workerThread.Start(WorkerProc, start)
      if (!blocker.Wait(TimeSpan.FromMinutes(1)))
           // throw TimeoutException("Start timed out !");
   }
}

private void WorkerProc(object state)
{
   var blocker = state as AutoResetEvent;
   // call Device.Start(callbacks)
   blocker.Set();

   while (_stopRequested)
      // sit in a loop till Stop is called

   Device.Stop();
}

Angel Java Lopez

unread,
Feb 10, 2014, 6:29:41 AM2/10/14
to growing-object-o...@googlegroups.com
First idea:

I see
// call Device.Start(callbacks)
before
blocker.Set();

I don't know what is your Device, and what are your callbacks. Uncomment that line, and then, inject a Device with an Start method that Thread.Sleep for more than a minute

If the Device is "fixed", ummm,... I could change that design, to be more testable.

Suggestions? Alternatives?

Angel "Java" Lopez
@ajlopez



--

Gishu Pillai

unread,
Feb 10, 2014, 6:51:17 AM2/10/14
to growing-object-o...@googlegroups.com
Comments are there to indicate pseudocode - they are an essential part of the routine.
Device is a collaborator which is going to be pinging me with callbacks

* The timeout test would => Thread.Sleep() block my test for a minute.
* Also extracting part of the WorkerThreadProc into its own method (for subclass n override to timeout) or injecting a code block into the WorkerThreadProc... it seems readability is taking a hit for testability.

Angel Java Lopez

unread,
Feb 10, 2014, 6:53:52 AM2/10/14
to growing-object-o...@googlegroups.com
The "one minute" value for timeout could be injected in your object, and used in Start method, with a 1 minute as default.

So, your test don't need to block for 1 minute.


Steve Freeman

unread,
Feb 10, 2014, 7:51:59 AM2/10/14
to growing-object-o...@googlegroups.com
A common technique is to have an "executor" that does all the thread handling stuff. To test the core logic you can create a "synchronous" version that just calls through. You can test the async executor in isolation. Then just a couple of tests to exercise the whole thing together (and make the wait time configurable).

S

Gishu Pillai

unread,
Feb 10, 2014, 8:10:46 AM2/10/14
to growing-object-o...@googlegroups.com
@angel
So now the WorkerThreadProc needs 2 seams - the Dev Start Proc and the timeout.

@steve - Yes the 'executor' is what I termed as the scheduler - making async code blocks run synchronously for unit tests. That part is cool.

However in this case, the background task is a while loop which will hang the unit test when run on the test runner thread.
For testing the synchronization points, should I use the Async/production scheduler.

It seems I'm scooping out the guts of the method one by one to make it inject-able for testing. I fear the end result won't be as easy to read as the pseudo code. I'll try it out though...
 For single-threaded code, I've found that TDD improves the readability of the code.

I'm not purposely trying to be difficult here :) Just trying to learn...
Gishu

Steve Freeman

unread,
Feb 10, 2014, 9:11:53 AM2/10/14
to growing-object-o...@googlegroups.com
One of the problems with concurrency is that it's a global property, rather than a unit property. Our best chance is to localise anything to do with concurrency and then run single-threaded within that.

Unless this needs down-to-the-metal performance, it's possible that the while loop could be part of the concurrency part that accepts a call to make repeatedly.

S

Malte Finsterwalder

unread,
Feb 11, 2014, 5:07:28 AM2/11/14
to growing-object-o...@googlegroups.com
Testing concurrent code is hard and often tricky, in my experience.

And the best idea I have is, to separate as much as possible.
Completely separate the concurrency parts from the business logic, so the logic can be tested in isolation.
Then plug in dummy-implementations into the concurrency parts to be able to test those.
This can make understandability a little worse at times.
But sometimes it also helps to isolate things nicely.

Good luck,
   Malte
Reply all
Reply to author
Forward
0 new messages