CppUnit是支持多线程的,你可以在一个线程中执行测试,在另一个线程中收集测试结果;或者在不同线程中并行执行多个测试,而用一个线程收集测试结果。framework中为此提供了简单而必要的支持。
[SynchronizedObject] [TestListener] [TestResult]
相关文件:SynchronizedObject.h,SynchronizedObject.cpp
SynchronizedObject用来管理一个被同步对象,前面提到的TestResult就是从该类派生的。所谓被同步对象,是指其成员会被多个线程并发使用。
SynchronizedObject定义了一个public属性的abstract inner class——SynchronizationObject,代表具备同步属性的对象:
class SynchronizationObject{ public: SynchronizationObject() {} virtual ~SynchronizationObject() {} virtual void lock() {} virtual void unlock() {}};
此类定义了互斥锁功能,但具体行为需在其派生类中实现。不同环境下的实现方式想必也不尽相同。随CppUnit源码所附的范例中有个MfcSynchronizationObject就是SynchronizationObject的子类,它使用了MFC的CCriticalSection:
class MfcSynchronizationObject : public CppUnit::SynchronizedObject::SynchronizationObject{ CCriticalSection m_syncObject;public: void lock() { m_syncObject.Lock(); } void unlock() { m_syncObject.Unlock(); }};
SynchronizedObject还定义了一个protected属性的inner class——ExclusiveZone,作为内部使用的辅助类。它用于在当前作用域内锁定一个SynchronizationObject的实例。其实现类似于std::auto_ptr,它持有一个指向SynchronizationObject对象的指针,ctor中调用lock,dtor中调用unlock:
class ExclusiveZone{ SynchronizationObject *m_syncObject; public: ExclusiveZone( SynchronizationObject *syncObject ) : m_syncObject( syncObject ) { m_syncObject->lock(); } ~ExclusiveZone() { m_syncObject->unlock (); }};
除去这些,SynchronizedObject就很简单了。它持有一个指向SynchronizationObject实例的指针:
SynchronizationObject *m_syncObject;
并管理其生命周期,在dtor中delete之。至于如何传入该指针,则提供了两种方法:
SynchronizedObject::SynchronizedObject( SynchronizationObject *syncObject ) : m_syncObject( syncObject == 0 ? new SynchronizationObject() : syncObject ){}void SynchronizedObject::setSynchronizationObject( SynchronizationObject *syncObject ){ delete m_syncObject; m_syncObject = syncObject;}
在讲述TestResult之前,还有一些障碍要扫清。
相关文件:TestListener.h
CppUnit的测试结果记录使用了Observer Pattern,在GoF中对该pattern有如下描述:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。在测试执行的过程中,当发生错误时,TestResult就会被告知,而后它会将该信息传递给TestListener。因此,TestResult对应了Observer Pattern中的Subject,而TestListener则对应了Observer。
不过TestListener只是一个什么事都没做的基类,像输出测试结果这样事情还得留待派生类来解决,比如outputter部分要提到的TextTestProgressListener。此外,还是尽量不要使用Listener进行测试结果的输出为好,应该使用outputter中所提供的专有工具。
从TestListener的定义可以看到,在如下三类事件发生时,TestListener将会被通知到
- 测试执行之前
- 测试失败
- 测试执行之后
来看一下代码:
// 在一个测试运行前被调用virtual void startTest( Test *test ) {}// 运行测试失败时被调用virtual void addFailure( const TestFailure &failure )// 在一个测试运行后被调用virtual void endTest( Test *test )
addFailure中的failure是临时对象,在该方法调用之后即被销毁,和Exception一样(参见TestCase的run函数),也需要使用其自身提供的clone方法来创建一个副本。有关TestFailure,稍后将会提到。
至于endTest,即便测试失败,该函数也会被调用,参见TestCase的run函数。
相关文件:TestResult.h,TestResult.cpp
真是千呼万唤始出来啊。TestResult用以收集测试过程中的相关信息,它派生自SynchronizedObject,从而支持多线程。有了前面的铺垫,对TestResult的理解就变得非常容易了。
TestResult维护了一个指向TestListener对象的指针队列:
protected: typedef std::deque<TestListener *> TestListeners; TestListeners m_listeners;
为获取到测试相关信息,TestListener需要注册到TestResult中,于是就有了addListener方法:
void TestResult::addListener( TestListener *listener ){ ExclusiveZone zone( m_syncObject ); // ExclusiveZone终于有用武之地了 m_listeners.push_back( listener );}
当然还少不了removeListener:
void TestResult::removeListener ( TestListener *listener ){ ExclusiveZone zone( m_syncObject ); m_listeners.erase( std::remove( m_listeners.begin(), m_listeners.end(), listener ), m_listeners.end());}
我们再来看看TestResult作为Subject的那些Notify方法:
void TestResult::addError( Test *test, Exception *e ){ addFailure( TestFailure( test, e, true ) );}void TestResult::addFailure( Test *test, Exception *e ){ addFailure( TestFailure( test, e, false ) );}void TestResult::addFailure( const TestFailure &failure ){ ExclusiveZone zone( m_syncObject ); // 遍历deque<TestListener *> for ( TestListeners::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it ) (*it)->addFailure( failure ); // 调用TestListener的addFailure}void TestResult::startTest( Test *test ){ ExclusiveZone zone( m_syncObject ); // 遍历deque<TestListener *> for ( TestListeners::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it ) (*it)->startTest( test ); // 调用TestListener的startTest}void TestResult::endTest( Test *test ){ ExclusiveZone zone( m_syncObject ); // 遍历deque<TestListener *> for ( TestListeners::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it ) (*it)->endTest( test ); // 调用TestListener的endTest}
此处的注释足以说明问题,至于error和failure的区别,在讲到TestFailure时自然会明了。
最后再来看看有关shouldStop的代码,该函数曾在TestSuite的run中出现过:
TestResult::TestResult( SynchronizationObject *syncObject ) : SynchronizedObject( syncObject ){ reset();}void TestResult::reset(){ ExclusiveZone zone( m_syncObject ); m_stop = false;}bool TestResult::shouldStop() const{ ExclusiveZone zone( m_syncObject ); return m_stop;}void TestResult::stop(){ ExclusiveZone zone( m_syncObject ); m_stop = true;}
没有什么特别的,只是一个m_stop在掌控着一切,而m_stop则是TestResult的一个protected属性的成员变量:
bool m_stop;