CppUnit源码解读(3)

类别:编程语言 点击:0 评论:0 推荐:

【声明】如需复制、传播,请附上本声明,谢谢。原文出处:http://morningspace.51.net/,[email protected]

测试结果记录相关

从这里开始,将要讲述core中,测试结果记录的相关部分。

CppUnit是支持多线程的,你可以在一个线程中执行测试,在另一个线程中收集测试结果;或者在不同线程中并行执行多个测试,而用一个线程收集测试结果。framework中为此提供了简单而必要的支持。

[SynchronizedObject]

相关文件: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]

相关文件: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]

相关文件: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;

本文地址:http://com.8s8s.com/it/it28957.htm