Goal: Give a timeout to external code which is not tested. If my app would cause the other party to freeze, I want to know it immediately. Let's say it's very sophisticated unit test. If a test client used my app and returned in 500ms - it's passed, if not - it's failed.
So yes, I'm not going to be gentle on client, this is a crash test site. I wait 500ms, then I don't care what else client waits, I want to crash it with exception immediately, but THEN REPORT IT as a test result.
Possibly as one of many test results.
So, now, here, code:
Console.Write("Testing");
try {
for (int j = 0; j < 3; j++) {
Console.Write(".");
for (int i = 0; i < 256; i++) {
using (var cts = new CancellationTokenSource(500)) {
var token = cts.Token;
token.Register(() => throw new TimeoutException("FREEZE DETECTED"));
clients.Add(LDSCheckAsync().Result);
}
}
clients.ForEach(i => i.Dispose());
clients.Clear();
}
Console.WriteLine("OK.");
}
catch {
clients.ForEach(i => i?.Dispose());
Console.WriteLine("FAIL!");
}
I designed the test to fail. But I don't get "FAIL" message, I get UNHANDLED EXCEPTION.
Why, and how to do it correctly. Before you even try do write a comment to it, please really do consider I can't use IfCancellationRequested or anything similar here. The function under the test would normally exit immediately, but since some network errors it can enter a 30 seconds freeze. It's totally unknown to me when it happens and even more unknown why. The test I make should help me diagnose it. THE FREEZE CANNOT BE CANCELED. I know that. What I want is to convert freeze into a nice catchable exception, then catch it, then log it. But when try / catch doesn't work as expected (yes, even outside VS), I'm helpless.
Since it's answered...
I made a little handy tool to make my life easier next time I would need to test code for freezing:
using System.Threading;
using System.Threading.Tasks;
namespace Woof.DebugEx {
public class FreezeTest {
private readonly Action Subject;
private int Timeout;
private bool IsDone;
public FreezeTest(Action subject) => Subject = subject;
public void Test(int timeout) {
Timeout = timeout;
var tested = new Task(Tested, null, TaskCreationOptions.LongRunning);
var watchdog = new Task(WatchDog, null, TaskCreationOptions.LongRunning);
tested.Start();
watchdog.Start();
Task.WaitAny(tested, watchdog);
if (!IsDone) throw new TimeoutException("FREEZE DETECTED.");
}
private void Tested(object state) {
Subject();
IsDone = true;
}
private void WatchDog(object state) => Thread.Sleep(Timeout);
}
}
Now my test fragment looks like this:
Console.Write("Testing");
try {
for (int j = 0; j < 8; j++) {
Console.Write(".");
for (int i = 0; i < 16; i++) {
new FreezeTest(() => {
clients.Add(LDSCheckAsync().Result);
}).Test(100);
}
clients.ForEach(i => i.Close());
clients.Clear();
Thread.Sleep(1000);
}
Console.WriteLine("OK.");
}
catch {
clients.ForEach(i => i?.Dispose());
Console.WriteLine("FAIL!");
}