Wednesday, July 22, 2009

Locking Multithreaded Unit Tests for Sequential Access In Visual Studio

As I mentioned in a prior post getting Visual Studio Unit tests to execute single threaded is impossible unless you want to write a replacement to the unit test runner. I have several tests that use remoting and rely on a pre-defined channel. When executing things in parallel this causes socket already in use errors. Because each test is executed in an isolated app domain using a lock on a static object wont work either. I have heard you can lock during class setup and teardown this may work due to some locking semantics in the Runner library but for those of you wanting the benefits of setting up and tearing down using class inheritance from a different assembly good luck no dice. I have chosen a very old school solution simply write a file to the unit test directory and use a mutex as a lock this works across multiple processes. Don't forget to test for the unexpected by protecting against an infinite loop Use a timeout to ensure the mutex is not infinitely held waiting for a stalled process.

public int TimeoutInSeconds { get; set; }

public string Name { get; private set; }

private Mutex Mutex
{
get
{
if (_mutex == null)
{
_mutex = new Mutex(false, Name);
}
return _mutex;
}
}

private string LockFileName
{
get
{
return String.Concat(Name, ".lock");
}
}

public override void Initialize(UnitTestBase test)
{
Mutex.WaitOne(new TimeSpan(0, 0, 0, TimeoutInSeconds, 0));
}

public override void Cleanup(UnitTestBase test)
{
Mutex.ReleaseMutex();
}


//Test Setup
long ticks = DateTime.Now.Ticks;
while (File.Exists(LockFileName))
{
Thread.Sleep(100);
if (TimeoutInSeconds > 0 && new TimeSpan(ticks - DateTime.Now.Ticks).Seconds >= TimeoutInSeconds)
{
throw new Exception(String.Format("Unable to obtain lock {0}", Name));
}
}
using (StreamWriter writer = File.CreateText(LockFileName))
{
writer.Write("Lock held by ");
writer.Write(test.GetType().FullName);
}
//Teardown
File.Delete(LockFileName);

No comments:

Post a Comment