CppUnit源码解读(3)

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

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;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值