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?
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();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With