views:

832

answers:

2

I’m writing a test that expects to receive an event from an object that it is calling. Specifically, I am calling out to an object that connects to an AIX machine via SSH (using the open source Granados project), then disconnecting, and I want to make sure I receive the OnConnectionClosed event that is being raised during the disconnect. It sounds simple enough, and I’ve written many tests like this in the past, but this time some strange behavior is occurring that I believe is related to threading.

Basically, the object I call is raising the ‘OnConnectionClosed’ event on a different thread than what I call it from. What I’m seeing is that when I run the test by selecting ‘Debug Test’ from the UI, it passes, but if I choose ‘Run Test’, it fails (even if there are no breakpoints set during the debug run). I’ve done some Googling and found this post that seems to indicate that by default the MSTest host runs in Single Thread mode but that a config change can make it run in Multi Thread mode. This sounded like it would logically fix my problem, but of course, it did not.

Some other posts I’ve come across also make me think that MSTest is simply not monitoring the background threads (so the events raised by them are not being ‘heard’). This would also make sense, and since it seems to work in debug mode, and it seems like the fix above should logically solve that problem, then I’m confused as to why it’s not working. It is possible that I’m simply not correctly dealing with the threads, although I would expect that to still be a problem in debug mode if it were the case.

Has anyone else tried to test something in a similar way? If so, did you encounter similar problems? And if so, how did you resolve them?

I’ve pasted the relevant unit test code below (I’ve removed the connection info for security reasons).

[TestClass]
public class SSHReaderTests
{
    private bool received = false;
    private delegate bool SimpleFunc();

    [TestInitialize]
    public void MyTestInitialize()
    {
        received = false;
    }

    [TestMethod]
    public void Should_raise_OnReaderConnectionClosed_event_after_successful_connection_is_disconnected()
    {
        IReader reader = new SSHReader();

        reader.OnReaderConnectionClosed += delegate
                                     {
                                         received = true;
                                     };

        reader.Connect("*****", "*****", "*****");

        //Assert.IsTrue(reader.IsConnected);

        reader.Disconnect();

        //Assert.IsFalse(reader.IsConnected);

        Assert.IsTrue(WaitUntilTrue(delegate {
            return received; }, 30000, 1000));
    }

    private static bool WaitUntilTrue(SimpleFunc func, int timeoutInMillis, int timeBetweenChecksInMillis)
    {
        Stopwatch stopwatch = new Stopwatch();
        stopwatch.Start();

        while(stopwatch.ElapsedMilliseconds < timeoutInMillis)
        {
            if (func())
                return true;

            Thread.Sleep(timeBetweenChecksInMillis);
        }
        return false;
    }
}
+1  A: 

Use the WaitHandle classes in the System.Threading namespace. Either, AutoResetEvent or ManualResetEvent. The difference between the two is that AutoResetEvent lets one thread proceed each time it is set, while ManualResetEvent releases all waiting threads on set.

The reason your example doesn't work has to do with compiler optimizations. The code does not actually get compiled to what you would think at first glance. Most likely, the compiler will do something like place the local variable in a register and never actually fetch it during your loop that checks. You can avoid this type of thing with the volatile keyword, but I would highly recommend reading up on threading and concurrency for more details. Joe Duffy's blog at http://www.bluebytesoftware.com is a great resource to get started, and I highly recommend his Concurrency Programming on Windows book that is coming out soon.

jezell
A: 

Not exactly what you arr asking about, but you may find some workable solutions or at least ideas by checking out the MS Research project called CHESS. It's for multithreaded concurrency testing in .net.

Kevin Dostalek