Synchronization Facilities
Runner is responsible for providing synchronization mechanisms for the tests. Example 1 presents the methods in the IPNUnitServices interface. For now, it simply provides a barrier-based synchronization mechanism.
public interface IPNUnitServices { void NotifyResult(string TestName,PNUnitTestResult result); void InitBarrier(string TestName, string barrier); void InitBarrier(string TestName, string barrier, int Max); void EnterBarrier(string barrier); }
You can initialize a barrier using Test by defining the maximum number of elements that pass through the barrier, or Test can just let Runner handle it. In the latter case, Runner assumes that all tests pass through the barrier, so it only depends on the definition made in the XML file. However, the former approach is useful when you need to define synchronization between a few participants in the test.
As Figure 3 illustrates, the barrier mechanism is a basic synchronization primitive. All tests must pass through a barrier before any of them are allowed to pass.
Listing One, the barrier implementation, shows that there are two important methods plus a constructor for the Barrier class. The constructor initializes the number of elements that must pass through the barrier to release it. The Enter() method is invoked when a test enters a barrier. If it doesn't enter a barrier and all of the other involved tests have gone through it, then the thread goes to sleep using a Monitor. The second method, Abandon(), is used to release barriers under fail conditions. For instance, if a test fails before hitting a barrier, Abandon() is called, so that an erroneous test won't freeze the rest of the tests.
public class Barrier { private int mCount; private int mMaxCount; private Object mLock = new Object(); public Barrier(int maxCount) { mCount = 0; mMaxCount = maxCount; } public void Enter() { lock( mLock ) { ++mCount; if( mCount >= mMaxCount ) { mCount = 0; Monitor.PulseAll(mLock); } else Monitor.Wait(mLock); } } public void Abandon() { lock( mLock ) { mMaxCount; if( mCount >= mMaxCount ) { mCount = 0; Monitor.PulseAll(mLock); } } } }
With the Barrier method, we have been able to implement large test suites, covering all the basic core functionality of our system. Still, it would be easy to include some other primitivessemaphores, for instance.