CppUnit代码理解(2)

看一个很重要的宏:

CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( MyTest, "alltest" );

我也不清楚,先看宏定义吧,在HelpMacros.h里面:

/** Adds the specified fixture suite to the specified registry suite.
* \ingroup CreatingTestSuite
*
* This macro declares a static variable whose construction
* causes a test suite factory to be inserted in the global registry
* suite of the specified name. The registry is available by calling
* the static function CppUnit::TestFactoryRegistry::getRegistry().
* 
* For the suite name, use a string returned by a static function rather
* than a hardcoded string. That way, you can know what are the name of
* named registry and you don't risk mistyping the registry name.
*
* \code
* // MySuites.h
* namespace MySuites {
*   std::string math() { 
*     return "Math";
*   }
* }
*
* // ComplexNumberTest.cpp
* #include "MySuites.h"
* 
* CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( ComplexNumberTest, MySuites::math() );
* \endcode
*
* \param ATestFixtureType Type of the test case class.
* \param suiteName Name of the global registry suite the test suite is 
*                  registered into.
* \warning This macro should be used only once per line of code (the line
*          number is used to name a hidden static variable).
* \see CPPUNIT_TEST_SUITE_REGISTRATION
* \see CPPUNIT_REGISTRY_ADD_TO_DEFAULT
* \see CPPUNIT_REGISTRY_ADD
* \see CPPUNIT_TEST_SUITE, CppUnit::AutoRegisterSuite, 
*      CppUnit::TestFactoryRegistry..
*/
#define CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( ATestFixtureType, suiteName ) \
  static CPPUNIT_NS::AutoRegisterSuite< ATestFixtureType >                   \
             CPPUNIT_MAKE_UNIQUE_NAME(autoRegisterRegistry__ )(suiteName) 

 从上面注释的参数解释我们知道要注册的是我们的测试类,被注册到一个全局的测试包中,该测试包的名字为宏里面的suiteName。宏里面有个模板类AutoRegisterSuite,看其定义:

*! \brief (Implementation) Automatically register the test suite of the specified type.
*
* You should not use this class directly. Instead, use the following macros:
* - CPPUNIT_TEST_SUITE_REGISTRATION()
* - CPPUNIT_TEST_SUITE_NAMED_REGISTRATION()
*
* This object will register the test returned by TestCaseType::suite()
* when constructed to the test registry.
*
* This object is intented to be used as a static variable.
*
*
* \param TestCaseType Type of the test case which suite is registered.
* \see CPPUNIT_TEST_SUITE_REGISTRATION, CPPUNIT_TEST_SUITE_NAMED_REGISTRATION
* \see CppUnit::TestFactoryRegistry.
*/
template<class TestCaseType>
class AutoRegisterSuite
{
public:
  /** Auto-register the suite factory in the global registry.
   */
  AutoRegisterSuite()
      : m_registry( &TestFactoryRegistry::getRegistry() )
  {
    m_registry->registerFactory( &m_factory );
  } 

  /** Auto-register the suite factory in the specified registry.
   * \param name Name of the registry.
   */
  AutoRegisterSuite( const std::string &name )
      : m_registry( &TestFactoryRegistry::getRegistry( name ) )
  {
    m_registry->registerFactory( &m_factory );
  } 

  ~AutoRegisterSuite()
  {
    if ( TestFactoryRegistry::isValid() )
      m_registry->unregisterFactory( &m_factory );
  } 

private:
  TestFactoryRegistry *m_registry;
  TestSuiteFactory<TestCaseType> m_factory;
}; 

 

看code我们可以猜测出注册是把我们的测试类的工厂类注册到一个TestFactoryRegistry对象上去了。注意到第二个构造函数的初始化列表中,调用了TestFactoryRegistry的静态函数getRegistry,看一下TestFactoryRegistry和TestSuiteFactory的代码我们看一下就更明白了:

/*! \brief Registry for TestFactory.
* \ingroup CreatingTestSuite
*
* Notes that the registry \b DON'T assumes lifetime control for any registered tests
* anymore.
*
* The <em>default</em> registry is the registry returned by getRegistry() with the 
* default name parameter value.
*
* To register tests, use the macros:
* - CPPUNIT_TEST_SUITE_REGISTRATION(): to add tests in the default registry.
* - CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(): to add tests in a named registry.
*
* Example 1: retreiving a suite that contains all the test registered with
* CPPUNIT_TEST_SUITE_REGISTRATION().
* \code
* CppUnit::TestFactoryRegistry &registry = CppUnit::TestFactoryRegistry::getRegistry();
* CppUnit::TestSuite *suite = registry.makeTest();
* \endcode
*
* Example 2: retreiving a suite that contains all the test registered with
* \link CPPUNIT_TEST_SUITE_NAMED_REGISTRATION() CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( ..., "Math" )\endlink.
* \code
* CppUnit::TestFactoryRegistry &mathRegistry = CppUnit::TestFactoryRegistry::getRegistry( "Math" );
* CppUnit::TestSuite *mathSuite = mathRegistry.makeTest();
* \endcode
*
* Example 3: creating a test suite hierarchy composed of unnamed registration and
* named registration:
* - All Tests
*   - tests registered with CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( ..., "Graph" )
*   - tests registered with CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( ..., "Math" )
*   - tests registered with CPPUNIT_TEST_SUITE_REGISTRATION
*
* \code
* CppUnit::TestSuite *rootSuite = new CppUnit::TestSuite( "All tests" );
* rootSuite->addTest( CppUnit::TestFactoryRegistry::getRegistry( "Graph" ).makeTest() );
* rootSuite->addTest( CppUnit::TestFactoryRegistry::getRegistry( "Math" ).makeTest() );
* CppUnit::TestFactoryRegistry::getRegistry().addTestToSuite( rootSuite );
* \endcode
*
* The same result can be obtained with:
* \code
* CppUnit::TestFactoryRegistry &registry = CppUnit::TestFactoryRegistry::getRegistry();
* registry.addRegistry( "Graph" );
* registry.addRegistry( "Math" );
* CppUnit::TestSuite *suite = registry.makeTest();
* \endcode
*
* Since a TestFactoryRegistry is a TestFactory, the named registries can be 
* registered in the unnamed registry, creating the hierarchy links.
*
* \see TestSuiteFactory, AutoRegisterSuite
* \see CPPUNIT_TEST_SUITE_REGISTRATION, CPPUNIT_TEST_SUITE_NAMED_REGISTRATION
*/
class CPPUNIT_API TestFactoryRegistry : public TestFactory
{
public:
  /** Constructs the registry with the specified name.
   * \param name Name of the registry. It is the name of TestSuite returned by
   *             makeTest().
   */
  TestFactoryRegistry( std::string name ); 

/// Destructor.
  virtual ~TestFactoryRegistry(); 

  /** Returns a new TestSuite that contains the registered test.
   * \return A new TestSuite which contains all the test added using 
   * registerFactory(TestFactory *).
   */
  virtual Test *makeTest(); 

  /** Returns a named registry.
   *
   * If the \a name is left to its default value, then the registry that is returned is
   * the one used by CPPUNIT_TEST_SUITE_REGISTRATION(): the 'top' level registry.
   *
   * \param name Name of the registry to return.
   * \return Registry. If the registry does not exist, it is created with the
   *         specified name.
   */
  static TestFactoryRegistry &getRegistry( const std::string &name = "All Tests" ); 

  /** Adds the registered tests to the specified suite.
   * \param suite Suite the tests are added to.
   */
  void addTestToSuite( TestSuite *suite ); 

  /** Adds the specified TestFactory to the registry.
   *
   * \param factory Factory to register. 
   */
  void registerFactory( TestFactory *factory ); 

  /*! Removes the specified TestFactory from the registry.
   * 
   * The specified factory is not destroyed.
   * \param factory Factory to remove from the registry.
   * \todo Address case when trying to remove a TestRegistryFactory.
   */
  void unregisterFactory( TestFactory *factory ); 

  /*! Adds a registry to the registry.
   * 
   * Convenience method to help create test hierarchy. See TestFactoryRegistry detail
   * for examples of use. Calling this method is equivalent to:
   * \code
   * this->registerFactory( TestFactoryRegistry::getRegistry( name ) );
   * \endcode
   *
   * \param name Name of the registry to add.
   */
  void addRegistry( const std::string &name ); 

  /*! Tests if the registry is valid.
   *
   * This method should be used when unregistering test factory on static variable 
   * destruction to ensure that the registry has not been already destroyed (in 
   * that case there is no need to unregister the test factory).
   *
   * You should not concern yourself with this method unless you are writing a class
   * like AutoRegisterSuite.
   *
   * \return \c true if the specified registry has not been destroyed, 
   *         otherwise returns \c false.
   * \see AutoRegisterSuite.
   */
  static bool isValid(); 

  /** Adds the specified TestFactory with a specific name (DEPRECATED).
   * \param name Name associated to the factory.
   * \param factory Factory to register. 
   * \deprecated Use registerFactory( TestFactory *) instead.
   */
  void registerFactory( const std::string &name,
                        TestFactory *factory ); 

private:
  TestFactoryRegistry( const TestFactoryRegistry &copy );
  void operator =( const TestFactoryRegistry &copy ); 

private:
  typedef CppUnitSet<TestFactory *, std::less<TestFactory*> > Factories;
  Factories m_factories; 

  std::string m_name;
}; 

 

其中静态函数getRegistry的实现:

TestFactoryRegistry &
TestFactoryRegistry::getRegistry( const std::string &name )
{
  return *TestFactoryRegistryList::getRegistry( name );
} 

 

再看这个TestFactoryRegistryList::getRegistry的实现:

static TestFactoryRegistry *getRegistry( const std::string &name )
  {
    // If the following assertion failed, then TestFactoryRegistry::getRegistry() 
    // was called during static variable destruction without checking the registry 
    // validity beforehand using TestFactoryRegistry::isValid() beforehand.
    assert( isValid() );
    if ( !isValid() )         // release mode
      return NULL;            // => force CRASH 

    return getInstance()->getInternalRegistry( name );
  } 

static TestFactoryRegistryList *getInstance()
  {
    static TestFactoryRegistryList list;
    return &list;
  } 

TestFactoryRegistry *getInternalRegistry( const std::string &name )
{
  Registries::const_iterator foundIt = m_registries.find( name );
  if ( foundIt == m_registries.end() )
  {
    TestFactoryRegistry *factory = new TestFactoryRegistry( name );
    m_registries.insert( std::pair<const std::string, TestFactoryRegistry*>( name, factory ) );
    return factory;
  }
  return (*foundIt).second;
} 

 

看明白了额,一个singleton模式又浮出水面。

然后看下面这两个函数的实现:

 

Test *
TestFactoryRegistry::makeTest()
{
  TestSuite *suite = new TestSuite( m_name );
  addTestToSuite( suite );
  return suite;
} 

void 
TestFactoryRegistry::addTestToSuite( TestSuite *suite )
{
  for ( Factories::iterator it = m_factories.begin(); 
        it != m_factories.end(); 
        ++it )
  {
    TestFactory *factory = *it;
    suite->addTest( factory->makeTest() );
  }
} 

 

结合着下面的main函数就很明白了,先创建一个作为root的TestSuite,然后各个TestFactory生成的TestSuite再统统加到这个作为root的TestSuite里面去了。

int main()
{
    CPPUNIT_NS::TestResult controller;
    CPPUNIT_NS::TestRunner runner;
    CPPUNIT_NS::TestResultCollector result;
    controller.addListener(&result);
    runner.addTest(CPPUNIT_NS::TestFactoryRegistry::getRegistry().makeTest());
    runner.run(controller, "");
} 

 

 

接着看这个controller和runner以及results直接的关系。看类TestResult类的addListener函数的定义:

/*! \brief Runs a test using the specified controller.
  * \param controller Event manager and controller used for testing
  * \param testPath Test path string. See Test::resolveTestPath() for detail.
  * \exception std::invalid_argument if no test matching \a testPath is found.
  *                                  see TestPath::TestPath( Test*, const std::string &)
  *                                  for detail.
  */
 virtual void run( TestResult &controller,
                   const std::string &testPath = "" )
{
  TestPath path = m_suite->resolveTestPath( testPath );
  Test *testToRun = path.getChildTest(); 

  controller.runTest( testToRun );
} 

 最后还是交给TestResult的runTest去指向:

 

void 
TestResult::runTest( Test *test )
{
  startTestRun( test );
  test->run( this );
  endTestRun( test );
} 

 Test case最终就这样一层层call下去了,但是前面TestResult的那个protect函数怎么起作用的呢,就是怎么监测测试的error、failure和success呢?具体函数定义如下:

*! \brief Protects a call to the specified functor.
   *
   * See Protector to understand how protector works. A default protector is
   * always present. It captures CppUnit::Exception, std::exception and
   * any other exceptions, retrieving as much as possible information about
   * the exception as possible.
   *
   * Additional Protector can be added to the chain to support other exception
   * types using pushProtector() and popProtector().
   *
   * \param functor Functor to call (typically a call to setUp(), runTest() or
   *                tearDown().
   * \param test Test the functor is associated to (used for failure reporting).
   * \param shortDescription Short description override for the failure message.
   */
  virtual bool protect( const Functor &functor,
                        Test *test,
                        const std::string &shortDescription = std::string("") ); 

 再看看下面这些代码我们就大彻大悟了!

TestResult::TestResult( SynchronizationObject *syncObject )
    : SynchronizedObject( syncObject )
    , m_protectorChain( new ProtectorChain() )
    , m_stop( false )
{ 
  m_protectorChain->push( new DefaultProtector() );
} 

bool 
DefaultProtector::protect( const Functor &functor,
                           const ProtectorContext &context )
{
  try
  {
    return functor();
  }
  catch ( Exception &failure )
  {
    reportFailure( context, failure );
  }
  catch ( std::exception &e )
  {
    std::string shortDescription( "uncaught exception of type " );
#if CPPUNIT_USE_TYPEINFO_NAME
    shortDescription += TypeInfoHelper::getClassName( typeid(e) );
#else
    shortDescription += "std::exception (or derived).";
#endif
    Message message( shortDescription, e.what() );
    reportError( context, message );
  }
  catch ( ... )
  {
    reportError( context,
                 Message( "uncaught exception of unknown type") );
  }
  return false;
}   

 

大致浏览完了,最好看一下一个类图关系:

你可能感兴趣的:(cpp)