看一个很重要的宏:
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 ®istry = 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 ®istry = 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 © ); void operator =( const TestFactoryRegistry © ); 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; }
大致浏览完了,最好看一下一个类图关系: