Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I test that implementations are synchronous or asynchronous where required to be so?

I'm trying to write unit tests around a custom SynchronizationContext implementation.

The two important operations on this class are Send and Post, where Send invokes a delegate synchronously and Post invokes a delegate asynchronously.

I would like to write unit tests to verify this behaviour, that the delegates were executed synchronously or asynchronously. I don't want the tests to rely on delays for success cases, because it artificially prolongs the tests running (but it's reasonable to have failures cause a delay) in execution.

Initially I have considered using Tasks to signal the execution of a delegate:

var tcs = new TaskCompletionSource<object>();

var context = new CustomSynchronizationContext();

context.Send((state) => tcs.SetResult(null), null);

// Task should already be completed!
Assert.IsTrue(this.tcs.Task.IsCompleted);

However, this does not ensure the delegate was not executed asynchronously very quickly before the test runner could continue.

How can I arrange a test around the context to ensure that Send blocks for the completion of the delegate and Post does not, but that the delegates are both invoked?

like image 446
Paul Turner Avatar asked Nov 02 '22 20:11

Paul Turner


1 Answers

I believe you can achieve this using a pair of ManualResetEvents. Using the code below, the slow down is only experienced if the tests failed (the numbers are pretty high and could probably be reduced safely). The idea here is that we assert the order in which things must occur that can only happen if we block or don't block.

For the synchronous test:

var incall = new ManualResetEvent(false);
var unblock = new ManualResetEvent(false);
var context = new CustomSynchronizationContext();
var t = Task.Run(() => context.Send(state =>
{
    incall.Set();
    unblock.WaitOne(5000);
}, null));
Assert.IsTrue(incall.WaitOne(1000));
Assert.IsFalse(t.Wait(10));
unblock.Set();
Assert.IsTrue(t.Wait(1000));

for the Async test:

var incall = new ManualResetEvent(false);
var unblock = new ManualResetEvent(false);
var context = new CustomSynchronizationContext();
var t = Task.Run(() =>context.Post(state =>
{
    incall.Set();
    unblock.WaitOne(5000);
}, null));
Assert.IsTrue(incall.WaitOne(1000));
Assert.IsTrue(t.Wait(1000)); //This will timeout if unblock is blocking completion of the task
unblock.Set();
like image 174
jageall Avatar answered Nov 14 '22 03:11

jageall